Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,20 @@ jobs:
with:
node-version: "20.5.0"
cache: 'pnpm'
- name: "ViteJS Project: Install dependencies"
- name: "NextJS Project: Install dependencies"
run: pnpm install
- name: "ViteJS Project: Test"
- name: "NextJS Project: Test"
run: pnpm run test
- name: "ViteJS Project: Build"
- name: "NextJS Project: Build"
run: pnpm run build
# Deploy GitHub Pages
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload dist repository
path: './apps/webtools/dist'
# Upload NextJS static export
path: './apps/webtools/out'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
6 changes: 5 additions & 1 deletion apps/webtools/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ lerna-debug.log*
node_modules
dist
dist-ssr
.next
out
*.local

# Editor directories and files
Expand All @@ -27,4 +29,6 @@ dist-ssr
downloads
public/nars
nars
public/nars.txt
public/nars.txt
public/buildinfo.json
public/nars.json
19 changes: 19 additions & 0 deletions apps/webtools/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Metadata } from 'next';
import type { ReactNode } from 'react';

export const metadata: Metadata = {
title: 'Nifi FlowFile Tools',
description: 'Client-side tools for Apache Nifi FlowFiles',
};

export default function RootLayout({
children,
}: Readonly<{
children: ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
7 changes: 7 additions & 0 deletions apps/webtools/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import dynamic from 'next/dynamic';

const Nf2tApp = dynamic(() => import('../src/Nf2tApp'), { ssr: false });

export default function Home() {
return <Nf2tApp />;
}
5 changes: 5 additions & 0 deletions apps/webtools/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
15 changes: 15 additions & 0 deletions apps/webtools/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const repository = process.env.GITHUB_REPOSITORY?.split('/')[1];
const basePath = process.env.GITHUB_ACTIONS === 'true' && repository ? `/${repository}` : '';

/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
trailingSlash: true,
basePath,
assetPrefix: basePath || undefined,
images: {
unoptimized: true,
},
};

export default nextConfig;
17 changes: 8 additions & 9 deletions apps/webtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@
"type": "module",
"homepage": "https://github.com/jgwoolley/nf2t-web",
"files": [
"./dist",
"./out",
"./public",
"./scripts",
"./src",
"./index.html",
"./tsconfig.json",
"./tsconfig.node.json",
"./vite.config.ts"
"./app",
"./next.config.mjs",
"./tsconfig.json"
],
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"fetch": "node fetch.js"
"dev": "next dev",
"build": "node ./scripts/generateStaticFiles.mjs && next build",
"start": "next start"
},
"dependencies": {
"@emotion/react": "^11.11.1",
Expand Down Expand Up @@ -52,6 +50,7 @@
"@types/wicg-file-system-access": "^2023.10.5",
"@vite-pwa/assets-generator": "^0.2.4",
"@vitejs/plugin-react": "^4.3.4",
"next": "^14.2.31",
"idb": "^8.0.0",
"jsdom": "^24.1.1",
"sharp": "^0.33.2",
Expand Down
124 changes: 124 additions & 0 deletions apps/webtools/scripts/generateStaticFiles.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { JSDOM } from 'jsdom';
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs';
import { open } from 'node:fs/promises';
import { execSync } from 'child_process';
import { readNars, WriteNarsSchema } from '@nf2t/nifitools-js';

const DOWNLOADS_PATH = './downloads';
const ZIP_PATH = `${DOWNLOADS_PATH}/nifi.zip`;
const CACHE_PATH = `${DOWNLOADS_PATH}/cache`;
const NARS_PATH = './nars';
const OUTPUT_PATH = './public';
const BUILD_INFO_OUTPUT = `${OUTPUT_PATH}/buildinfo.json`;
const NARS_OUTPUT = `${OUTPUT_PATH}/nars.json`;

function generateBuildinfo() {
const gitSha = execSync('git rev-parse HEAD').toString().trim();
const gitShortSha = execSync('git rev-parse --short HEAD').toString().trim();
const gitMessage = execSync('git log -1 --pretty=%B').toString().trim();
const gitDate = execSync('git log -1 --date=iso-strict --pretty=%cd').toString().trim();
const repository = process.env.GITHUB_REPOSITORY;
const base = repository ? repository.split('/')[1] : undefined;

return {
base,
git: {
SHA: gitSha,
shortSHA: gitShortSha,
date: gitDate,
message: gitMessage,
},
github: {
base,
GITHUB_JOB: process.env.GITHUB_JOB,
GITHUB_REPOSITORY: repository,
GITHUB_SHA: process.env.GITHUB_SHA,
GITHUB_REF: process.env.GITHUB_REF,
GITHUB_WORKFLOW: process.env.GITHUB_WORKFLOW,
GITHUB_ACTOR: process.env.GITHUB_ACTOR,
GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE,
},
node: process.versions,
};
}

async function generateNarsNew() {
const DOMParser = new JSDOM().window.DOMParser;

const narInfo = {
nars: [],
extensions: [],
attributes: [],
};

const files = readdirSync(NARS_PATH).map(async (x) => {
const fileHandle = await open(`${NARS_PATH}/${x}`);
const buffer = await fileHandle.readFile();
await fileHandle.close();
return new File([buffer], x);
});

await readNars({
files: await Promise.all(files),
setCurrentProgress: () => null,
parseNar: async (nar) => {
narInfo.nars.push(nar);
},
parseExtension: async (extension) => {
narInfo.extensions.push(extension);
},
parseAttribute: async (attribute) => {
narInfo.attributes.push(attribute);
},
DOMParser: new DOMParser(),
});

return narInfo;
}

async function generateNars() {
if (existsSync(CACHE_PATH)) {
const content = readFileSync(CACHE_PATH, { encoding: 'utf8' });
return WriteNarsSchema.parseAsync(JSON.parse(content));
}

try {
if (!existsSync(NARS_PATH)) {
mkdirSync(NARS_PATH);
if (!existsSync(DOWNLOADS_PATH)) {
mkdirSync(DOWNLOADS_PATH);
}

if (!existsSync(ZIP_PATH)) {
execSync(`curl https://dlcdn.apache.org/nifi/1.28.1/nifi-1.28.1-bin.zip -o ${ZIP_PATH}`, {
stdio: 'inherit',
});
}

execSync(`unzip ${ZIP_PATH} '*/*.nar' -d ${DOWNLOADS_PATH}/`, { stdio: 'inherit' });
execSync(`cp ${DOWNLOADS_PATH}/nifi-*.*.*/lib/*.nar ${NARS_PATH}`, { stdio: 'inherit' });
}

const narInfo = await generateNarsNew();
writeFileSync(CACHE_PATH, JSON.stringify(narInfo));
return narInfo;
} catch (error) {
console.warn('Unable to generate NAR metadata, continuing with empty values.', error);
return {
nars: [],
extensions: [],
attributes: [],
};
}
}

async function run() {
if (!existsSync(OUTPUT_PATH)) {
mkdirSync(OUTPUT_PATH);
}

writeFileSync(BUILD_INFO_OUTPUT, JSON.stringify(generateBuildinfo()));
writeFileSync(NARS_OUTPUT, JSON.stringify(await generateNars()));
}

run();
30 changes: 30 additions & 0 deletions apps/webtools/src/Nf2tApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';

import {
RouterProvider,
createHashHistory,
createRouter,
} from '@tanstack/react-router';
import rootRoute from './routes/rootRoute';
import { routeChildren } from './routes/routeDescriptions';
import { notFoundRoute } from './routes/notFoundRoute';

const routeTree = rootRoute.addChildren(routeChildren);

const history = createHashHistory();

const router = createRouter({
history: history,
routeTree: routeTree,
notFoundRoute: notFoundRoute,
});

declare module '@tanstack/react-router' {
interface Register {
router: typeof router;
}
}

export default function Nf2tApp() {
return <RouterProvider router={router} />;
}
21 changes: 10 additions & 11 deletions apps/webtools/src/routes/info/buildInfo.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,21 @@ export default function BuildProcess() {
)
},
{
alt: "Run the ViteJS development server.",
alt: "Run the NextJS development server.",
child: (
<>
<p>Run the ViteJS development server.</p>
<p>Run the NextJS development server.</p>
<ol>
<li><CodeSnippet submitSnackbarMessage={snackbarProps.submitSnackbarMessage} code="npm run dev" /></li>
<li>This command does the following:</li>
<ol>
<li>Looks in "scripts" for "dev" <ExternalLink href="https://github.com/jgwoolley/Nifi-Flow-File-Helper/blob/main/package.json">package.json</ExternalLink> and runs the <code>vite</code> command as a child process.</li>
<li>The <code>vite</code> plugin looks for <ExternalLink href="https://github.com/jgwoolley/Nifi-Flow-File-Helper/blob/main/vite.config.ts">vite.config.ts</ExternalLink>, and runs that script.</li>
<li>Looks in "scripts" for "dev" <ExternalLink href="https://github.com/jgwoolley/nf3t-web/blob/main/apps/webtools/package.json">package.json</ExternalLink> and runs the <code>next dev</code> command as a child process.</li>
<li>NextJS looks for <ExternalLink href="https://github.com/jgwoolley/nf3t-web/blob/main/apps/webtools/next.config.mjs">next.config.mjs</ExternalLink> and applies the static export configuration.</li>
<ol>
<li>Runs <CodeSnippet submitSnackbarMessage={snackbarProps.submitSnackbarMessage} code="git log" /> to get some build information, which will be writen to <code>buildinfo.json</code>, and read by the Web Site.</li>
<li>Runs the <ExternalLink href="https://vite-pwa-org.netlify.app/">VitePWA Plugin</ExternalLink> which creates files needed for the Web Site to be run as a PWA.</li>
<li>Runs the <code>react</code> ViteJS plugin to create a ReactJS website.</li>
<li>Runs <CodeSnippet submitSnackbarMessage={snackbarProps.submitSnackbarMessage} code="npm run build" /> to generate <code>buildinfo.json</code> and <code>nars.json</code> before the static export.</li>
<li>Loads the NextJS app entrypoint from <ExternalLink href="https://github.com/jgwoolley/nf3t-web/blob/main/apps/webtools/app/page.tsx">app/page.tsx</ExternalLink> and mounts the existing client-side router.</li>
<ol>
<li>Locates the <ExternalLink href="https://github.com/jgwoolley/Nifi-Flow-File-Helper/blob/main/index.html">index.html</ExternalLink> file, and utilizes the referenced <ExternalLink href="https://github.com/jgwoolley/Nifi-Flow-File-Helper/blob/main/src/main.tsx">/src/main.tsx</ExternalLink> to create the website.</li>
<li>Uses a client-only component to keep all route processing in the browser.</li>
</ol>
</ol>
<li>Runs as a website on your local computer. It will run in hot module replace mode, so any changes you make will immediately be deployed.</li>
Expand All @@ -93,13 +92,13 @@ export default function BuildProcess() {
)
},
{
alt: "Build the ViteJS SPA.",
alt: "Build the NextJS static export.",
child: (
<>
<p>Build the ViteJS SPA.</p>
<p>Build the NextJS static export.</p>
<ol>
<li><CodeSnippet submitSnackbarMessage={snackbarProps.submitSnackbarMessage} code="npm run build" /></li>
<li>Does everything in the "development server step", except it builds a SPA. This will not create a developer server, just the files needed to deploy the site.</li>
<li>Does everything in the "development server step", except it creates a static export in the <code>out</code> directory for deployment.</li>
</ol>
</>
)
Expand Down
31 changes: 24 additions & 7 deletions apps/webtools/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,41 @@
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",

"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"allowJs": true,
"esModuleInterop": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
"include": [
"next-env.d.ts",
"src",
"app",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
Loading