generator-white-label 5.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +15 -0
- package/.yarnrc.yml +6 -0
- package/CLI.md +82 -0
- package/LICENSE +9 -0
- package/PACKAGE_MANAGERS.md +35 -0
- package/README.md +204 -124
- package/app/404.tsx +28 -0
- package/app/README.md +66 -8
- package/app/assets/data/view/global.json +7 -47
- package/app/assets/data/view/index.json +9 -1
- package/app/assets/script/index.tsx +13 -0
- package/app/assets/script/tasks/TaskApplication.ts +77 -0
- package/app/assets/script/tasks/TaskMediator.ts +20 -0
- package/app/assets/script/tasks/TaskModel.ts +32 -0
- package/app/assets/script/tasks/TaskRouter.ts +30 -0
- package/app/assets/script/tasks/TaskView.tsx +24 -0
- package/app/assets/style/global.css +111 -0
- package/app/assets/style/print.css +8 -0
- package/app/assets/view/CodeBlock.tsx +17 -0
- package/app/assets/view/examples/tasks/TaskExample.tsx +63 -0
- package/app/assets/view/examples/tasks/task-state.ts +46 -0
- package/app/assets/view/layout/SiteFooter.tsx +11 -0
- package/app/assets/view/layout/SiteHeader.tsx +23 -0
- package/app/assets/view/page-data.ts +14 -0
- package/app/assets/view/sections/GettingStartedSection.tsx +27 -0
- package/app/assets/view/sections/HeroSection.tsx +21 -0
- package/app/assets/view/sections/LiveExampleSection.tsx +29 -0
- package/app/assets/view/sections/PackageDocsSection.tsx +69 -0
- package/app/assets/view/sections/SourceGuideSection.tsx +28 -0
- package/app/index.tsx +54 -0
- package/cli/index.ts +117 -0
- package/dist/app/404.d.ts +1 -0
- package/dist/app/404.js +6 -0
- package/dist/app/404.js.map +1 -0
- package/dist/app/assets/script/index.d.ts +8 -2
- package/dist/app/assets/script/index.js +11 -1
- package/dist/app/assets/script/index.js.map +1 -1
- package/dist/app/assets/script/tasks/TaskApplication.d.ts +20 -0
- package/dist/app/assets/script/tasks/TaskApplication.js +63 -0
- package/dist/app/assets/script/tasks/TaskApplication.js.map +1 -0
- package/dist/app/assets/script/tasks/TaskMediator.d.ts +15 -0
- package/dist/app/assets/script/tasks/TaskMediator.js +5 -0
- package/dist/app/assets/script/tasks/TaskMediator.js.map +1 -0
- package/dist/app/assets/script/tasks/TaskModel.d.ts +13 -0
- package/dist/app/assets/script/tasks/TaskModel.js +33 -0
- package/dist/app/assets/script/tasks/TaskModel.js.map +1 -0
- package/dist/app/assets/script/tasks/TaskRouter.d.ts +12 -0
- package/dist/app/assets/script/tasks/TaskRouter.js +28 -0
- package/dist/app/assets/script/tasks/TaskRouter.js.map +1 -0
- package/dist/app/assets/script/tasks/TaskView.d.ts +9 -0
- package/dist/app/assets/script/tasks/TaskView.js +24 -0
- package/dist/app/assets/script/tasks/TaskView.js.map +1 -0
- package/dist/app/assets/view/CodeBlock.d.ts +11 -0
- package/dist/app/assets/view/CodeBlock.js +12 -0
- package/dist/app/assets/view/CodeBlock.js.map +1 -0
- package/dist/app/assets/view/examples/tasks/TaskExample.d.ts +11 -0
- package/dist/app/assets/view/examples/tasks/TaskExample.js +16 -0
- package/dist/app/assets/view/examples/tasks/TaskExample.js.map +1 -0
- package/dist/app/assets/view/examples/tasks/task-state.d.ts +22 -0
- package/dist/app/assets/view/examples/tasks/task-state.js +30 -0
- package/dist/app/assets/view/examples/tasks/task-state.js.map +1 -0
- package/dist/app/assets/view/layout/SiteFooter.d.ts +2 -0
- package/dist/app/assets/view/layout/SiteFooter.js +6 -0
- package/dist/app/assets/view/layout/SiteFooter.js.map +1 -0
- package/dist/app/assets/view/layout/SiteHeader.d.ts +8 -0
- package/dist/app/assets/view/layout/SiteHeader.js +12 -0
- package/dist/app/assets/view/layout/SiteHeader.js.map +1 -0
- package/dist/app/assets/view/page-data.d.ts +16 -0
- package/dist/app/assets/view/page-data.js +2 -0
- package/dist/app/assets/view/page-data.js.map +1 -0
- package/dist/app/assets/view/sections/GettingStartedSection.d.ts +2 -0
- package/dist/app/assets/view/sections/GettingStartedSection.js +11 -0
- package/dist/app/assets/view/sections/GettingStartedSection.js.map +1 -0
- package/dist/app/assets/view/sections/HeroSection.d.ts +8 -0
- package/dist/app/assets/view/sections/HeroSection.js +12 -0
- package/dist/app/assets/view/sections/HeroSection.js.map +1 -0
- package/dist/app/assets/view/sections/LiveExampleSection.d.ts +6 -0
- package/dist/app/assets/view/sections/LiveExampleSection.js +13 -0
- package/dist/app/assets/view/sections/LiveExampleSection.js.map +1 -0
- package/dist/app/assets/view/sections/PackageDocsSection.d.ts +2 -0
- package/dist/app/assets/view/sections/PackageDocsSection.js +31 -0
- package/dist/app/assets/view/sections/PackageDocsSection.js.map +1 -0
- package/dist/app/assets/view/sections/SourceGuideSection.d.ts +7 -0
- package/dist/app/assets/view/sections/SourceGuideSection.js +11 -0
- package/dist/app/assets/view/sections/SourceGuideSection.js.map +1 -0
- package/dist/app/index.d.ts +8 -0
- package/dist/app/index.js +22 -0
- package/dist/app/index.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.js +97 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/scaffold/index.d.ts +64 -0
- package/dist/scaffold/index.js +109 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scripts/build.d.ts +5 -13
- package/dist/scripts/build.js +48 -79
- package/dist/scripts/build.js.map +1 -1
- package/package.json +77 -40
- package/pnpm-workspace.yaml +4 -0
- package/scaffold/index.ts +127 -0
- package/scaffold/no-jsx/README.md +47 -0
- package/scaffold/no-jsx/app/404.ts +10 -0
- package/scaffold/no-jsx/app/assets/script/index.ts +7 -0
- package/scaffold/no-jsx/app/assets/script/tasks/TaskView.ts +19 -0
- package/scaffold/no-jsx/app/assets/view/examples/tasks/TaskExample.ts +39 -0
- package/scaffold/no-jsx/app/index.ts +58 -0
- package/scripts/build.ts +55 -86
- package/server/handler.ts +75 -0
- package/template-test/site.test.js +233 -0
- package/tsconfig.site.json +6 -5
- package/tsconfig.site.no-jsx.json +24 -0
- package/app/404.hbs +0 -20
- package/app/assets/data/coutries.json +0 -992
- package/app/assets/data/states.json +0 -204
- package/app/assets/data/view/toolkit/colors.json +0 -17
- package/app/assets/data/view/toolkit/components.json +0 -39
- package/app/assets/data/view/toolkit/elements.json +0 -119
- package/app/assets/data/view/toolkit/grids.json +0 -17
- package/app/assets/data/view/toolkit/icons.json +0 -17
- package/app/assets/data/view/toolkit/index.json +0 -17
- package/app/assets/data/view/toolkit/type.json +0 -17
- package/app/assets/font/opensans/LICENSE.txt +0 -202
- package/app/assets/font/opensans/opensans-bold-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-bolditalic-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-extrabold-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-extrabolditalic-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-italic-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-light-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-lightitalic-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-regular-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-semibold-webfont.woff2 +0 -0
- package/app/assets/font/opensans/opensans-semibolditalic-webfont.woff2 +0 -0
- package/app/assets/font/opensans/stylesheet.css +0 -120
- package/app/assets/image/apple-touch-icon.png +0 -0
- package/app/assets/image/demo/fallback.avif +0 -0
- package/app/assets/image/demo/fallback.jpg +0 -0
- package/app/assets/image/demo/fallback.webp +0 -0
- package/app/assets/image/demo/large.avif +0 -0
- package/app/assets/image/demo/large.webp +0 -0
- package/app/assets/image/demo/small.avif +0 -0
- package/app/assets/image/demo/small.webp +0 -0
- package/app/assets/image/favicon.png +0 -0
- package/app/assets/image/graphic-css3.svg +0 -30
- package/app/assets/image/graphic-html5.svg +0 -29
- package/app/assets/markup/element/a.hbs +0 -29
- package/app/assets/markup/element/button.hbs +0 -30
- package/app/assets/markup/element/div.hbs +0 -11
- package/app/assets/markup/element/head.hbs +0 -78
- package/app/assets/markup/element/iframe.hbs +0 -20
- package/app/assets/markup/element/img.hbs +0 -30
- package/app/assets/markup/element/input.hbs +0 -43
- package/app/assets/markup/element/label.hbs +0 -14
- package/app/assets/markup/element/legend.hbs +0 -5
- package/app/assets/markup/element/meta.hbs +0 -11
- package/app/assets/markup/element/nav.hbs +0 -13
- package/app/assets/markup/element/p.hbs +0 -8
- package/app/assets/markup/element/picture.hbs +0 -58
- package/app/assets/markup/element/script.hbs +0 -3
- package/app/assets/markup/element/select.hbs +0 -35
- package/app/assets/markup/element/span.hbs +0 -8
- package/app/assets/markup/element/svg.hbs +0 -11
- package/app/assets/markup/element/table.hbs +0 -50
- package/app/assets/markup/element/textarea.hbs +0 -24
- package/app/assets/markup/element/ul.hbs +0 -13
- package/app/assets/markup/modal/modal-example-1.handlebars +0 -8
- package/app/assets/markup/toolkit/accessibility-skip-to-element-1.handlebars +0 -1
- package/app/assets/markup/toolkit/accessibility-speaker-1.handlebars +0 -1
- package/app/assets/markup/toolkit/anchor-1.handlebars +0 -1
- package/app/assets/markup/toolkit/button-1.handlebars +0 -1
- package/app/assets/markup/toolkit/footer-1.handlebars +0 -3
- package/app/assets/markup/toolkit/header-1.handlebars +0 -1
- package/app/assets/markup/toolkit/heading-1.handlebars +0 -1
- package/app/assets/markup/toolkit/heading-2.handlebars +0 -1
- package/app/assets/markup/toolkit/heading-3.handlebars +0 -1
- package/app/assets/markup/toolkit/heading-4.handlebars +0 -1
- package/app/assets/markup/toolkit/image-1.handlebars +0 -1
- package/app/assets/markup/toolkit/input-checkbox-1.handlebars +0 -1
- package/app/assets/markup/toolkit/input-error-1.handlebars +0 -8
- package/app/assets/markup/toolkit/input-radio-1.handlebars +0 -1
- package/app/assets/markup/toolkit/input-text-1.handlebars +0 -1
- package/app/assets/markup/toolkit/label-1.handlebars +0 -1
- package/app/assets/markup/toolkit/legend-1.handlebars +0 -1
- package/app/assets/markup/toolkit/list-1.handlebars +0 -1
- package/app/assets/markup/toolkit/modal-1.handlebars +0 -4
- package/app/assets/markup/toolkit/nav-1.handlebars +0 -1
- package/app/assets/markup/toolkit/picture-1.handlebars +0 -1
- package/app/assets/markup/toolkit/select-1.handlebars +0 -1
- package/app/assets/markup/toolkit/table-1.handlebars +0 -1
- package/app/assets/markup/toolkit/text-1.handlebars +0 -1
- package/app/assets/markup/toolkit/textarea-1.handlebars +0 -1
- package/app/assets/markup/toolkit/youtube-player-1.handlebars +0 -1
- package/app/assets/script/api/facebook.ts +0 -41
- package/app/assets/script/api/youtube.ts +0 -22
- package/app/assets/script/index.ts +0 -2
- package/app/assets/script/mediator/global.ts +0 -4
- package/app/assets/script/model/singletons/countries.ts +0 -5
- package/app/assets/script/model/singletons/global.ts +0 -5
- package/app/assets/script/model/singletons/states.ts +0 -5
- package/app/assets/script/model/user.ts +0 -4
- package/app/assets/script/toolkit.ts +0 -11
- package/app/assets/script/types.d.ts +0 -16
- package/app/assets/script/utility/ajax.ts +0 -15
- package/app/assets/script/utility/form.ts +0 -119
- package/app/assets/script/utility/regex.ts +0 -26
- package/app/assets/script/utility/share.ts +0 -77
- package/app/assets/script/utility/string.ts +0 -148
- package/app/assets/script/view/default.tsx +0 -14
- package/app/assets/script/view/toolkit/youtube-player-1.ts +0 -31
- package/app/assets/style/base/_toolkit.scss +0 -39
- package/app/assets/style/base/colors/_colors.scss +0 -18
- package/app/assets/style/base/components/_modal-1.scss +0 -80
- package/app/assets/style/base/components/_sticky-1.scss +0 -19
- package/app/assets/style/base/components/_tabpanel-1.scss +0 -115
- package/app/assets/style/base/components/_tabpanel-2.scss +0 -64
- package/app/assets/style/base/components/_tabpanel-base-1.scss +0 -13
- package/app/assets/style/base/components/_youtube-player-1.scss +0 -16
- package/app/assets/style/base/elements/_anchors.scss +0 -27
- package/app/assets/style/base/elements/_buttons.scss +0 -25
- package/app/assets/style/base/elements/_fieldsets.scss +0 -9
- package/app/assets/style/base/elements/_images.scss +0 -8
- package/app/assets/style/base/elements/_inputs.scss +0 -78
- package/app/assets/style/base/elements/_labels.scss +0 -16
- package/app/assets/style/base/elements/_landmarks.scss +0 -3
- package/app/assets/style/base/elements/_legends.scss +0 -11
- package/app/assets/style/base/elements/_lists.scss +0 -15
- package/app/assets/style/base/elements/_pictures.scss +0 -4
- package/app/assets/style/base/elements/_tables.scss +0 -39
- package/app/assets/style/base/layouts/_staggered-1.scss +0 -29
- package/app/assets/style/base/type/_fonts.scss +0 -125
- package/app/assets/style/base/type/_headings.scss +0 -36
- package/app/assets/style/base/type/_text.scss +0 -9
- package/app/assets/style/base/utilities/_accessibility.scss +0 -55
- package/app/assets/style/base/utilities/_functions.scss +0 -98
- package/app/assets/style/base/utilities/_scroll.scss +0 -11
- package/app/assets/style/base/utilities/_type.scss +0 -13
- package/app/assets/style/global.scss +0 -123
- package/app/assets/style/print.scss +0 -6
- package/app/assets/style/toolkit.scss +0 -387
- package/app/index.hbs +0 -21
- package/app/robots.txt +0 -1
- package/app/toolkit/colors.hbs +0 -102
- package/app/toolkit/components.hbs +0 -261
- package/app/toolkit/elements.hbs +0 -497
- package/app/toolkit/grids.hbs +0 -413
- package/app/toolkit/icons.hbs +0 -87
- package/app/toolkit/index.hbs +0 -144
- package/app/toolkit/type.hbs +0 -120
- package/dist/app/assets/data/coutries.json +0 -992
- package/dist/app/assets/data/states.json +0 -204
- package/dist/app/assets/script/api/facebook.d.ts +0 -8
- package/dist/app/assets/script/api/facebook.js +0 -42
- package/dist/app/assets/script/api/facebook.js.map +0 -1
- package/dist/app/assets/script/api/youtube.d.ts +0 -8
- package/dist/app/assets/script/api/youtube.js +0 -22
- package/dist/app/assets/script/api/youtube.js.map +0 -1
- package/dist/app/assets/script/mediator/global.d.ts +0 -4
- package/dist/app/assets/script/mediator/global.js +0 -4
- package/dist/app/assets/script/mediator/global.js.map +0 -1
- package/dist/app/assets/script/model/singletons/countries.d.ts +0 -20
- package/dist/app/assets/script/model/singletons/countries.js +0 -5
- package/dist/app/assets/script/model/singletons/countries.js.map +0 -1
- package/dist/app/assets/script/model/singletons/global.d.ts +0 -20
- package/dist/app/assets/script/model/singletons/global.js +0 -5
- package/dist/app/assets/script/model/singletons/global.js.map +0 -1
- package/dist/app/assets/script/model/singletons/states.d.ts +0 -20
- package/dist/app/assets/script/model/singletons/states.js +0 -5
- package/dist/app/assets/script/model/singletons/states.js.map +0 -1
- package/dist/app/assets/script/model/user.d.ts +0 -4
- package/dist/app/assets/script/model/user.js +0 -6
- package/dist/app/assets/script/model/user.js.map +0 -1
- package/dist/app/assets/script/toolkit.d.ts +0 -1
- package/dist/app/assets/script/toolkit.js +0 -7
- package/dist/app/assets/script/toolkit.js.map +0 -1
- package/dist/app/assets/script/utility/ajax.d.ts +0 -8
- package/dist/app/assets/script/utility/ajax.js +0 -17
- package/dist/app/assets/script/utility/ajax.js.map +0 -1
- package/dist/app/assets/script/utility/form.d.ts +0 -64
- package/dist/app/assets/script/utility/form.js +0 -111
- package/dist/app/assets/script/utility/form.js.map +0 -1
- package/dist/app/assets/script/utility/regex.d.ts +0 -14
- package/dist/app/assets/script/utility/regex.js +0 -27
- package/dist/app/assets/script/utility/regex.js.map +0 -1
- package/dist/app/assets/script/utility/share.d.ts +0 -68
- package/dist/app/assets/script/utility/share.js +0 -81
- package/dist/app/assets/script/utility/share.js.map +0 -1
- package/dist/app/assets/script/utility/string.d.ts +0 -64
- package/dist/app/assets/script/utility/string.js +0 -135
- package/dist/app/assets/script/utility/string.js.map +0 -1
- package/dist/app/assets/script/view/default.d.ts +0 -11
- package/dist/app/assets/script/view/default.js +0 -15
- package/dist/app/assets/script/view/default.js.map +0 -1
- package/dist/app/assets/script/view/toolkit/youtube-player-1.d.ts +0 -23
- package/dist/app/assets/script/view/toolkit/youtube-player-1.js +0 -32
- package/dist/app/assets/script/view/toolkit/youtube-player-1.js.map +0 -1
- package/dist/generators/app/index.d.ts +0 -18
- package/dist/generators/app/index.js +0 -59
- package/dist/generators/app/index.js.map +0 -1
- package/generators/app/index.js +0 -2
- package/generators/app/index.ts +0 -61
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { createInterface } from 'node:readline/promises';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { createProject } from '../scaffold/index.js';
|
|
8
|
+
function usage() {
|
|
9
|
+
return [
|
|
10
|
+
'Usage:',
|
|
11
|
+
' white-label create <directory> [--jsx | --no-jsx]',
|
|
12
|
+
'',
|
|
13
|
+
'Commands:',
|
|
14
|
+
' create <directory> Create a White Label project.',
|
|
15
|
+
'',
|
|
16
|
+
'Options:',
|
|
17
|
+
' --jsx Generate TypeScript with JSX/TSX templates.',
|
|
18
|
+
' --no-jsx Generate plain TypeScript with HTML string templates.',
|
|
19
|
+
' -h, --help Show this help message.'
|
|
20
|
+
].join('\n');
|
|
21
|
+
}
|
|
22
|
+
export function formatCliError(error) {
|
|
23
|
+
return error instanceof Error ? error.message : String(error);
|
|
24
|
+
}
|
|
25
|
+
async function askForJsx(input, output) {
|
|
26
|
+
const readline = createInterface({ input, output });
|
|
27
|
+
try {
|
|
28
|
+
const answer = await readline.question('Use JSX/TSX for page and view templates? Choose Yes for JSX syntax like <section>...</section>, or No for plain TypeScript that returns HTML strings. (Y/n) ');
|
|
29
|
+
return !/^n(?:o)?$/i.test(answer.trim());
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
readline.close();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Reject an existing non-empty target so a mistyped CLI destination cannot overwrite project files. */
|
|
36
|
+
async function assertDestinationAvailable(destination) {
|
|
37
|
+
try {
|
|
38
|
+
const entries = await readdir(destination);
|
|
39
|
+
if (entries.length) {
|
|
40
|
+
throw new Error(`Destination directory must be empty: ${destination}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function runCli(args, dependencies = {}) {
|
|
51
|
+
const cwd = dependencies.cwd ?? process.cwd;
|
|
52
|
+
const scaffold = dependencies.createProject ?? createProject;
|
|
53
|
+
const stdout = dependencies.stdout ?? process.stdout;
|
|
54
|
+
const stderr = dependencies.stderr ?? process.stderr;
|
|
55
|
+
const input = dependencies.input ?? process.stdin;
|
|
56
|
+
const output = dependencies.output ?? process.stdout;
|
|
57
|
+
const [command, destination, ...options] = args;
|
|
58
|
+
if (command === '--help' || command === '-h' || args.length === 0) {
|
|
59
|
+
stdout.write(`${usage()}\n`);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
const allowedOptions = new Set(['--jsx', '--no-jsx']);
|
|
63
|
+
if (command !== 'create' ||
|
|
64
|
+
!destination ||
|
|
65
|
+
options.some(option => !allowedOptions.has(option)) ||
|
|
66
|
+
(options.includes('--jsx') && options.includes('--no-jsx'))) {
|
|
67
|
+
stderr.write(`${usage()}\n`);
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
let jsx;
|
|
71
|
+
if (options.includes('--jsx')) {
|
|
72
|
+
jsx = true;
|
|
73
|
+
}
|
|
74
|
+
else if (options.includes('--no-jsx')) {
|
|
75
|
+
jsx = false;
|
|
76
|
+
}
|
|
77
|
+
else if (dependencies.isInteractive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
78
|
+
jsx = await askForJsx(input, output);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
jsx = true;
|
|
82
|
+
}
|
|
83
|
+
const resolvedDestination = path.resolve(cwd(), destination);
|
|
84
|
+
await assertDestinationAvailable(resolvedDestination);
|
|
85
|
+
await scaffold({ destination: resolvedDestination, jsx });
|
|
86
|
+
stdout.write(`Created White Label project at ${resolvedDestination}\n`);
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
const invokedPath = path.resolve(process.argv[1]);
|
|
90
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
91
|
+
if (invokedPath === modulePath) {
|
|
92
|
+
runCli(process.argv.slice(2)).then((exitCode) => { process.exitCode = exitCode; }, (error) => {
|
|
93
|
+
process.stderr.write(`Unable to create White Label project: ${formatCliError(error)}\n`);
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport {readdir} from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport {createInterface} from 'node:readline/promises';\nimport {fileURLToPath} from 'node:url';\nimport {createProject} from '../scaffold/index.js';\n\nexport interface CliDependencies {\n cwd?: () => string;\n createProject?: typeof createProject;\n stdout?: Pick<NodeJS.WriteStream, 'write'>;\n stderr?: Pick<NodeJS.WriteStream, 'write'>;\n input?: NodeJS.ReadableStream;\n output?: NodeJS.WritableStream;\n isInteractive?: boolean;\n}\n\nfunction usage() {\n return [\n 'Usage:',\n ' white-label create <directory> [--jsx | --no-jsx]',\n '',\n 'Commands:',\n ' create <directory> Create a White Label project.',\n '',\n 'Options:',\n ' --jsx Generate TypeScript with JSX/TSX templates.',\n ' --no-jsx Generate plain TypeScript with HTML string templates.',\n ' -h, --help Show this help message.'\n ].join('\\n');\n}\n\nexport function formatCliError(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function askForJsx(input: NodeJS.ReadableStream, output: NodeJS.WritableStream): Promise<boolean> {\n const readline = createInterface({input, output});\n try {\n const answer = await readline.question(\n 'Use JSX/TSX for page and view templates? Choose Yes for JSX syntax like <section>...</section>, or No for plain TypeScript that returns HTML strings. (Y/n) '\n );\n return !/^n(?:o)?$/i.test(answer.trim());\n } finally {\n readline.close();\n }\n}\n\n/** Reject an existing non-empty target so a mistyped CLI destination cannot overwrite project files. */\nasync function assertDestinationAvailable(destination: string) {\n try {\n const entries = await readdir(destination);\n if (entries.length) {\n throw new Error(`Destination directory must be empty: ${destination}`);\n }\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {return;}\n throw error;\n }\n}\n\nexport async function runCli(args: readonly string[], dependencies: CliDependencies = {}) {\n const cwd = dependencies.cwd ?? process.cwd;\n const scaffold = dependencies.createProject ?? createProject;\n const stdout = dependencies.stdout ?? process.stdout;\n const stderr = dependencies.stderr ?? process.stderr;\n const input = dependencies.input ?? process.stdin;\n const output = dependencies.output ?? process.stdout;\n const [command, destination, ...options] = args;\n\n if (command === '--help' || command === '-h' || args.length === 0) {\n stdout.write(`${usage()}\\n`);\n return 0;\n }\n\n const allowedOptions = new Set(['--jsx', '--no-jsx']);\n if (\n command !== 'create' ||\n !destination ||\n options.some(option => !allowedOptions.has(option)) ||\n (options.includes('--jsx') && options.includes('--no-jsx'))\n ) {\n stderr.write(`${usage()}\\n`);\n return 1;\n }\n\n let jsx: boolean;\n if (options.includes('--jsx')) {\n jsx = true;\n } else if (options.includes('--no-jsx')) {\n jsx = false;\n } else if (dependencies.isInteractive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY)) {\n jsx = await askForJsx(input, output);\n } else {\n jsx = true;\n }\n\n const resolvedDestination = path.resolve(cwd(), destination);\n await assertDestinationAvailable(resolvedDestination);\n await scaffold({destination: resolvedDestination, jsx});\n stdout.write(`Created White Label project at ${resolvedDestination}\\n`);\n return 0;\n}\n\nconst invokedPath = path.resolve(process.argv[1] as string);\nconst modulePath = fileURLToPath(import.meta.url);\nif (invokedPath === modulePath) {\n runCli(process.argv.slice(2)).then(\n (exitCode) => {process.exitCode = exitCode;},\n (error: unknown) => {\n process.stderr.write(`Unable to create White Label project: ${formatCliError(error)}\\n`);\n process.exitCode = 1;\n }\n );\n}\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export interface ScaffoldFileSystem {
|
|
2
|
+
copy(source: string, destination: string): void | Promise<void>;
|
|
3
|
+
writeJSON(destination: string, value: unknown): void | Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export interface CreateProjectOptions {
|
|
6
|
+
destination: string;
|
|
7
|
+
fileSystem?: ScaffoldFileSystem;
|
|
8
|
+
jsx?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Return the package manifest used by generated White Label projects. */
|
|
11
|
+
export declare function createSiteManifest(): {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
private: boolean;
|
|
15
|
+
type: string;
|
|
16
|
+
engines: {
|
|
17
|
+
node: string;
|
|
18
|
+
};
|
|
19
|
+
scripts: {
|
|
20
|
+
build: string;
|
|
21
|
+
typecheck: string;
|
|
22
|
+
test: string;
|
|
23
|
+
};
|
|
24
|
+
allowScripts: {
|
|
25
|
+
'@parcel/watcher@2.5.1': boolean;
|
|
26
|
+
'esbuild@0.28.2': boolean;
|
|
27
|
+
'white-label-view@5.1.0': boolean;
|
|
28
|
+
};
|
|
29
|
+
dependenciesMeta: {
|
|
30
|
+
'@parcel/watcher@2.5.1': {
|
|
31
|
+
built: boolean;
|
|
32
|
+
};
|
|
33
|
+
'esbuild@0.28.2': {
|
|
34
|
+
built: boolean;
|
|
35
|
+
};
|
|
36
|
+
'white-label-view@5.1.0': {
|
|
37
|
+
built: boolean;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
devDependencies: {
|
|
41
|
+
'@tailwindcss/cli': string;
|
|
42
|
+
'@types/node': string;
|
|
43
|
+
'axe-core': string;
|
|
44
|
+
esbuild: string;
|
|
45
|
+
'html-validate': string;
|
|
46
|
+
jsdom: string;
|
|
47
|
+
tailwindcss: string;
|
|
48
|
+
typescript: string;
|
|
49
|
+
};
|
|
50
|
+
dependencies: {
|
|
51
|
+
'white-label-mediator': string;
|
|
52
|
+
'white-label-model': string;
|
|
53
|
+
'white-label-router': string;
|
|
54
|
+
'white-label-view': string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Create a White Label project.
|
|
59
|
+
*
|
|
60
|
+
* This is the shared implementation behind every creation interface. Adapters
|
|
61
|
+
* translate their environment into these options instead of owning templates or
|
|
62
|
+
* project-generation behavior themselves.
|
|
63
|
+
*/
|
|
64
|
+
export declare function createProject({ destination, fileSystem, jsx }: CreateProjectOptions): Promise<void>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** @module scaffold */
|
|
2
|
+
import { cp, mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
6
|
+
const nodeFileSystem = {
|
|
7
|
+
async copy(source, destination) {
|
|
8
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
9
|
+
await cp(source, destination, { recursive: true });
|
|
10
|
+
},
|
|
11
|
+
async writeJSON(destination, value) {
|
|
12
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
13
|
+
await writeFile(destination, `${JSON.stringify(value, null, 2)}\n`);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/** Return the package manifest used by generated White Label projects. */
|
|
17
|
+
export function createSiteManifest() {
|
|
18
|
+
return {
|
|
19
|
+
name: 'white-label-site',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
private: true,
|
|
22
|
+
type: 'module',
|
|
23
|
+
engines: { node: '^22.18.0 || >=24.11.0' },
|
|
24
|
+
scripts: {
|
|
25
|
+
build: 'tsc -p tsconfig.json && node dist/scripts/build.js',
|
|
26
|
+
typecheck: 'tsc -p tsconfig.json --noEmit',
|
|
27
|
+
test: 'node --run typecheck && node --run build -- --version=test --production=true --site-url=https://example.com && node --test --experimental-test-coverage --test-coverage-include=dist/app/assets/script/index.js --test-coverage-lines=100 --test-coverage-functions=100 --test-coverage-branches=100 test/*.test.js'
|
|
28
|
+
},
|
|
29
|
+
allowScripts: {
|
|
30
|
+
'@parcel/watcher@2.5.1': true,
|
|
31
|
+
'esbuild@0.28.2': true,
|
|
32
|
+
'white-label-view@5.1.0': true
|
|
33
|
+
},
|
|
34
|
+
dependenciesMeta: {
|
|
35
|
+
'@parcel/watcher@2.5.1': { built: true },
|
|
36
|
+
'esbuild@0.28.2': { built: true },
|
|
37
|
+
'white-label-view@5.1.0': { built: true }
|
|
38
|
+
},
|
|
39
|
+
devDependencies: {
|
|
40
|
+
'@tailwindcss/cli': '4.3.3',
|
|
41
|
+
'@types/node': '24.13.3',
|
|
42
|
+
'axe-core': '4.13.0',
|
|
43
|
+
'esbuild': '0.28.2',
|
|
44
|
+
'html-validate': '11.15.0',
|
|
45
|
+
'jsdom': '30.0.1',
|
|
46
|
+
'tailwindcss': '4.3.3',
|
|
47
|
+
'typescript': '7.0.2'
|
|
48
|
+
},
|
|
49
|
+
dependencies: {
|
|
50
|
+
'white-label-mediator': '4.0.0',
|
|
51
|
+
'white-label-model': '6.0.0',
|
|
52
|
+
'white-label-router': '5.0.0',
|
|
53
|
+
'white-label-view': '5.1.0'
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const commonNoJsxCopies = [
|
|
58
|
+
['app/assets/data', 'app/assets/data'],
|
|
59
|
+
['app/assets/style', 'app/assets/style'],
|
|
60
|
+
['app/assets/script/tasks/TaskApplication.ts', 'app/assets/script/tasks/TaskApplication.ts'],
|
|
61
|
+
['app/assets/script/tasks/TaskMediator.ts', 'app/assets/script/tasks/TaskMediator.ts'],
|
|
62
|
+
['app/assets/script/tasks/TaskModel.ts', 'app/assets/script/tasks/TaskModel.ts'],
|
|
63
|
+
['app/assets/script/tasks/TaskRouter.ts', 'app/assets/script/tasks/TaskRouter.ts'],
|
|
64
|
+
['app/assets/view/examples/tasks/task-state.ts', 'app/assets/view/examples/tasks/task-state.ts'],
|
|
65
|
+
['app/package.json', 'app/package.json']
|
|
66
|
+
];
|
|
67
|
+
const noJsxTemplateCopies = [
|
|
68
|
+
['scaffold/no-jsx/app/index.ts', 'app/index.ts'],
|
|
69
|
+
['scaffold/no-jsx/app/404.ts', 'app/404.ts'],
|
|
70
|
+
['scaffold/no-jsx/app/assets/script/index.ts', 'app/assets/script/index.ts'],
|
|
71
|
+
['scaffold/no-jsx/app/assets/script/tasks/TaskView.ts', 'app/assets/script/tasks/TaskView.ts'],
|
|
72
|
+
['scaffold/no-jsx/app/assets/view/examples/tasks/TaskExample.ts', 'app/assets/view/examples/tasks/TaskExample.ts'],
|
|
73
|
+
['scaffold/no-jsx/README.md', 'README.md'],
|
|
74
|
+
['tsconfig.site.no-jsx.json', 'tsconfig.json']
|
|
75
|
+
];
|
|
76
|
+
/**
|
|
77
|
+
* Create a White Label project.
|
|
78
|
+
*
|
|
79
|
+
* This is the shared implementation behind every creation interface. Adapters
|
|
80
|
+
* translate their environment into these options instead of owning templates or
|
|
81
|
+
* project-generation behavior themselves.
|
|
82
|
+
*/
|
|
83
|
+
export async function createProject({ destination, fileSystem = nodeFileSystem, jsx = true }) {
|
|
84
|
+
const copies = jsx ? [
|
|
85
|
+
['.editorconfig', '.editorconfig'],
|
|
86
|
+
['.yarnrc.yml', '.yarnrc.yml'],
|
|
87
|
+
['pnpm-workspace.yaml', 'pnpm-workspace.yaml'],
|
|
88
|
+
['app', 'app'],
|
|
89
|
+
['app/README.md', 'README.md'],
|
|
90
|
+
['server', 'server'],
|
|
91
|
+
['scripts', 'scripts'],
|
|
92
|
+
['template-test', 'test'],
|
|
93
|
+
['tsconfig.site.json', 'tsconfig.json']
|
|
94
|
+
] : [
|
|
95
|
+
['.editorconfig', '.editorconfig'],
|
|
96
|
+
['.yarnrc.yml', '.yarnrc.yml'],
|
|
97
|
+
['pnpm-workspace.yaml', 'pnpm-workspace.yaml'],
|
|
98
|
+
...commonNoJsxCopies,
|
|
99
|
+
...noJsxTemplateCopies,
|
|
100
|
+
['server', 'server'],
|
|
101
|
+
['scripts', 'scripts'],
|
|
102
|
+
['template-test', 'test']
|
|
103
|
+
];
|
|
104
|
+
for (const [source, target] of copies) {
|
|
105
|
+
await fileSystem.copy(path.join(packageRoot, source), path.join(destination, target));
|
|
106
|
+
}
|
|
107
|
+
await fileSystem.writeJSON(path.join(destination, 'package.json'), createSiteManifest());
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../scaffold/index.ts"],"names":[],"mappings":"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","sourcesContent":["/** @module scaffold */\nimport {cp, mkdir, writeFile} from 'node:fs/promises';\nimport path from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nconst packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');\n\nexport interface ScaffoldFileSystem {\n copy(source: string, destination: string): void | Promise<void>;\n writeJSON(destination: string, value: unknown): void | Promise<void>;\n}\n\nexport interface CreateProjectOptions {\n destination: string;\n fileSystem?: ScaffoldFileSystem;\n jsx?: boolean;\n}\n\nconst nodeFileSystem: ScaffoldFileSystem = {\n async copy(source, destination) {\n await mkdir(path.dirname(destination), {recursive: true});\n await cp(source, destination, {recursive: true});\n },\n async writeJSON(destination, value) {\n await mkdir(path.dirname(destination), {recursive: true});\n await writeFile(destination, `${JSON.stringify(value, null, 2)}\\n`);\n }\n};\n\n/** Return the package manifest used by generated White Label projects. */\nexport function createSiteManifest() {\n return {\n name: 'white-label-site',\n version: '1.0.0',\n private: true,\n type: 'module',\n engines: {node: '^22.18.0 || >=24.11.0'},\n scripts: {\n build: 'tsc -p tsconfig.json && node dist/scripts/build.js',\n typecheck: 'tsc -p tsconfig.json --noEmit',\n test: 'node --run typecheck && node --run build -- --version=test --production=true --site-url=https://example.com && node --test --experimental-test-coverage --test-coverage-include=dist/app/assets/script/index.js --test-coverage-lines=100 --test-coverage-functions=100 --test-coverage-branches=100 test/*.test.js'\n },\n allowScripts: {\n '@parcel/watcher@2.5.1': true,\n 'esbuild@0.28.2': true,\n 'white-label-view@5.1.0': true\n },\n dependenciesMeta: {\n '@parcel/watcher@2.5.1': {built: true},\n 'esbuild@0.28.2': {built: true},\n 'white-label-view@5.1.0': {built: true}\n },\n devDependencies: {\n '@tailwindcss/cli': '4.3.3',\n '@types/node': '24.13.3',\n 'axe-core': '4.13.0',\n 'esbuild': '0.28.2',\n 'html-validate': '11.15.0',\n 'jsdom': '30.0.1',\n 'tailwindcss': '4.3.3',\n 'typescript': '7.0.2'\n },\n dependencies: {\n 'white-label-mediator': '4.0.0',\n 'white-label-model': '6.0.0',\n 'white-label-router': '5.0.0',\n 'white-label-view': '5.1.0'\n }\n };\n}\n\nconst commonNoJsxCopies = [\n ['app/assets/data', 'app/assets/data'],\n ['app/assets/style', 'app/assets/style'],\n ['app/assets/script/tasks/TaskApplication.ts', 'app/assets/script/tasks/TaskApplication.ts'],\n ['app/assets/script/tasks/TaskMediator.ts', 'app/assets/script/tasks/TaskMediator.ts'],\n ['app/assets/script/tasks/TaskModel.ts', 'app/assets/script/tasks/TaskModel.ts'],\n ['app/assets/script/tasks/TaskRouter.ts', 'app/assets/script/tasks/TaskRouter.ts'],\n ['app/assets/view/examples/tasks/task-state.ts', 'app/assets/view/examples/tasks/task-state.ts'],\n ['app/package.json', 'app/package.json']\n] as const;\n\nconst noJsxTemplateCopies = [\n ['scaffold/no-jsx/app/index.ts', 'app/index.ts'],\n ['scaffold/no-jsx/app/404.ts', 'app/404.ts'],\n ['scaffold/no-jsx/app/assets/script/index.ts', 'app/assets/script/index.ts'],\n ['scaffold/no-jsx/app/assets/script/tasks/TaskView.ts', 'app/assets/script/tasks/TaskView.ts'],\n ['scaffold/no-jsx/app/assets/view/examples/tasks/TaskExample.ts', 'app/assets/view/examples/tasks/TaskExample.ts'],\n ['scaffold/no-jsx/README.md', 'README.md'],\n ['tsconfig.site.no-jsx.json', 'tsconfig.json']\n] as const;\n\n/**\n * Create a White Label project.\n *\n * This is the shared implementation behind every creation interface. Adapters\n * translate their environment into these options instead of owning templates or\n * project-generation behavior themselves.\n */\nexport async function createProject({destination, fileSystem = nodeFileSystem, jsx = true}: CreateProjectOptions) {\n const copies = jsx ? [\n ['.editorconfig', '.editorconfig'],\n ['.yarnrc.yml', '.yarnrc.yml'],\n ['pnpm-workspace.yaml', 'pnpm-workspace.yaml'],\n ['app', 'app'],\n ['app/README.md', 'README.md'],\n ['server', 'server'],\n ['scripts', 'scripts'],\n ['template-test', 'test'],\n ['tsconfig.site.json', 'tsconfig.json']\n ] as const : [\n ['.editorconfig', '.editorconfig'],\n ['.yarnrc.yml', '.yarnrc.yml'],\n ['pnpm-workspace.yaml', 'pnpm-workspace.yaml'],\n ...commonNoJsxCopies,\n ...noJsxTemplateCopies,\n ['server', 'server'],\n ['scripts', 'scripts'],\n ['template-test', 'test']\n ] as const;\n\n for (const [source, target] of copies) {\n await fileSystem.copy(path.join(packageRoot, source), path.join(destination, target));\n }\n\n await fileSystem.writeJSON(path.join(destination, 'package.json'), createSiteManifest());\n}\n"]}
|
package/dist/scripts/build.d.ts
CHANGED
|
@@ -2,21 +2,13 @@
|
|
|
2
2
|
export interface BuildConfig {
|
|
3
3
|
cdn: string;
|
|
4
4
|
production: boolean;
|
|
5
|
-
|
|
5
|
+
siteUrl: string;
|
|
6
6
|
version: string;
|
|
7
7
|
www: string;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Read deployment flags and reject version strings that contain path separators.
|
|
11
|
-
* @param argumentsList - Command-line arguments excluding the Node executable and script.
|
|
12
|
-
* @returns Deployment configuration with defaults for omitted flags.
|
|
13
|
-
* @throws When input does not satisfy the documented contract.
|
|
14
|
-
*/
|
|
9
|
+
/** Read deployment flags and reject unsafe or invalid deployment values. */
|
|
15
10
|
export declare function parseArguments(argumentsList: string[]): BuildConfig;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* @param projectRoot - Project containing app sources and installed dependencies.
|
|
20
|
-
* @returns A promise resolving after the deployment directory has been rebuilt.
|
|
21
|
-
*/
|
|
11
|
+
/** Return the local Tailwind executable name for the current platform. */
|
|
12
|
+
export declare function tailwindExecutable(platform?: NodeJS.Platform): "tailwindcss" | "tailwindcss.cmd";
|
|
13
|
+
/** Replace the deployment directory with compiled pages, scripts, styles, and static assets. */
|
|
22
14
|
export declare function build(config?: BuildConfig, projectRoot?: string): Promise<void>;
|
package/dist/scripts/build.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
1
2
|
import fs from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
6
|
import { build as buildJavaScript } from 'esbuild';
|
|
5
|
-
|
|
6
|
-
import * as sass from 'sass';
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
7
8
|
const defaultProjectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
8
|
-
/**
|
|
9
|
-
* Read deployment flags and reject version strings that contain path separators.
|
|
10
|
-
* @param argumentsList - Command-line arguments excluding the Node executable and script.
|
|
11
|
-
* @returns Deployment configuration with defaults for omitted flags.
|
|
12
|
-
* @throws When input does not satisfy the documented contract.
|
|
13
|
-
*/
|
|
9
|
+
/** Read deployment flags and reject unsafe or invalid deployment values. */
|
|
14
10
|
export function parseArguments(argumentsList) {
|
|
15
11
|
const values = Object.fromEntries(argumentsList.map((argument) => {
|
|
16
12
|
const [key, ...value] = argument.replace(/^--/, '').split('=');
|
|
@@ -20,19 +16,23 @@ export function parseArguments(argumentsList) {
|
|
|
20
16
|
if (!/^[A-Za-z0-9._-]+$/.test(version)) {
|
|
21
17
|
throw new Error('Version may contain only letters, numbers, dots, underscores, and hyphens');
|
|
22
18
|
}
|
|
19
|
+
const siteUrl = (values['site-url'] || 'http://localhost:8080').replace(/\/$/, '');
|
|
20
|
+
if (!URL.canParse(siteUrl) || (values.production === 'true' && !siteUrl.startsWith('https://'))) {
|
|
21
|
+
throw new Error('Production builds require an absolute HTTPS --site-url');
|
|
22
|
+
}
|
|
23
23
|
return {
|
|
24
24
|
cdn: values.cdn || '/',
|
|
25
25
|
production: values.production === 'true',
|
|
26
|
-
|
|
26
|
+
siteUrl,
|
|
27
27
|
version,
|
|
28
28
|
www: values.www || '/'
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*/
|
|
31
|
+
/** Return the local Tailwind executable name for the current platform. */
|
|
32
|
+
export function tailwindExecutable(platform = process.platform) {
|
|
33
|
+
return platform === 'win32' ? 'tailwindcss.cmd' : 'tailwindcss';
|
|
34
|
+
}
|
|
35
|
+
/** Recursively collect files below a directory, propagating filesystem failures. */
|
|
36
36
|
async function filesUnder(directory) {
|
|
37
37
|
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
38
38
|
const files = await Promise.all(entries.map(async (entry) => {
|
|
@@ -41,27 +41,15 @@ async function filesUnder(directory) {
|
|
|
41
41
|
}));
|
|
42
42
|
return files.flat();
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Register partials and render pages using global, page-specific, and deployment data.
|
|
46
|
-
* @param config - Validated deployment or database configuration.
|
|
47
|
-
* @param outputRoot - Destination for rendered pages.
|
|
48
|
-
* @param projectRoot - Project containing app sources and installed dependencies.
|
|
49
|
-
* @returns A promise resolving after all pages have been written.
|
|
50
|
-
*/
|
|
44
|
+
/** Render compiled TypeScript or TSX pages using global, page-specific, and deployment data. */
|
|
51
45
|
async function renderMarkup(config, outputRoot, projectRoot) {
|
|
52
|
-
const markupRoot = path.join(projectRoot, 'app/assets/markup');
|
|
53
|
-
for (const partialPath of await filesUnder(markupRoot)) {
|
|
54
|
-
if (/\.(hbs|handlebars)$/.test(partialPath)) {
|
|
55
|
-
const name = path.relative(markupRoot, partialPath).replace(/\\/g, '/').replace(/\.(hbs|handlebars)$/, '');
|
|
56
|
-
Handlebars.registerPartial(name, await fs.readFile(partialPath, 'utf8'));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
46
|
const globalData = JSON.parse(await fs.readFile(path.join(projectRoot, 'app/assets/data/view/global.json'), 'utf8'));
|
|
60
|
-
const
|
|
61
|
-
|
|
47
|
+
const appRoot = path.join(projectRoot, 'app');
|
|
48
|
+
const pages = (await filesUnder(appRoot))
|
|
49
|
+
.filter((file) => /\.tsx?$/.test(file) && !file.includes(`${path.sep}assets${path.sep}`));
|
|
62
50
|
await Promise.all(pages.map(async (page) => {
|
|
63
|
-
const relative = path.relative(
|
|
64
|
-
const pageDataPath = path.join(projectRoot, 'app/assets/data/view', relative.replace(/\.
|
|
51
|
+
const relative = path.relative(appRoot, page);
|
|
52
|
+
const pageDataPath = path.join(projectRoot, 'app/assets/data/view', relative.replace(/\.tsx?$/, '.json'));
|
|
65
53
|
let pageData = {};
|
|
66
54
|
try {
|
|
67
55
|
pageData = JSON.parse(await fs.readFile(pageDataPath, 'utf8'));
|
|
@@ -71,46 +59,33 @@ async function renderMarkup(config, outputRoot, projectRoot) {
|
|
|
71
59
|
throw error;
|
|
72
60
|
}
|
|
73
61
|
}
|
|
74
|
-
const
|
|
75
|
-
const
|
|
62
|
+
const compiledPage = path.join(projectRoot, 'dist/app', relative.replace(/\.tsx?$/, '.js'));
|
|
63
|
+
const module = await import(pathToFileURL(compiledPage).href);
|
|
64
|
+
if (typeof module.default !== 'function') {
|
|
65
|
+
throw new TypeError(`Page module ${relative} must export a default render function`);
|
|
66
|
+
}
|
|
67
|
+
const markup = module.default({ ...globalData, ...pageData, ...config });
|
|
68
|
+
const destination = path.join(outputRoot, relative.replace(/\.tsx?$/, '.html'));
|
|
76
69
|
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
77
|
-
await fs.writeFile(destination,
|
|
70
|
+
await fs.writeFile(destination, `<!DOCTYPE html>\n${String(markup)}\n`);
|
|
78
71
|
}));
|
|
79
72
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Compile project Sass and include the full published Bootstrap stylesheet.
|
|
82
|
-
* @param outputAssets - Destination for compiled asset directories.
|
|
83
|
-
* @param production - Enable compressed output and omit development source maps.
|
|
84
|
-
* @param projectRoot - Project containing app sources and installed dependencies.
|
|
85
|
-
* @returns A promise resolving after all stylesheets have been written.
|
|
86
|
-
*/
|
|
73
|
+
/** Compile Tailwind and project CSS into the deploy directory. */
|
|
87
74
|
async function compileStyles(outputAssets, production, projectRoot) {
|
|
88
75
|
const styleRoot = path.join(projectRoot, 'app/assets/style');
|
|
89
|
-
await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
: '';
|
|
101
|
-
const bootstrapCss = production && bootstrapSource
|
|
102
|
-
? sass.compileString(bootstrapSource, { style: 'compressed', syntax: 'css' }).css
|
|
103
|
-
: bootstrapSource;
|
|
104
|
-
await fs.writeFile(destination, `${bootstrapCss}${bootstrapCss ? '\n' : ''}${result.css}`);
|
|
105
|
-
}));
|
|
76
|
+
await fs.mkdir(path.join(outputAssets, 'style'), { recursive: true });
|
|
77
|
+
const destination = path.join(outputAssets, 'style/global.css');
|
|
78
|
+
const argumentsList = [
|
|
79
|
+
'-i', path.join(styleRoot, 'global.css'),
|
|
80
|
+
'-o', destination
|
|
81
|
+
];
|
|
82
|
+
if (production) {
|
|
83
|
+
argumentsList.push('--minify');
|
|
84
|
+
}
|
|
85
|
+
await execFileAsync(tailwindExecutable(), argumentsList, { cwd: projectRoot });
|
|
86
|
+
await fs.copyFile(path.join(styleRoot, 'print.css'), path.join(outputAssets, 'style/print.css'));
|
|
106
87
|
}
|
|
107
|
-
/**
|
|
108
|
-
* Bundle TypeScript entry points for browsers, excluding declaration-only files.
|
|
109
|
-
* @param outputAssets - Destination for compiled asset directories.
|
|
110
|
-
* @param production - Enable compressed output and omit development source maps.
|
|
111
|
-
* @param projectRoot - Project containing app sources and installed dependencies.
|
|
112
|
-
* @returns A promise resolving after all script bundles have been written.
|
|
113
|
-
*/
|
|
88
|
+
/** Bundle TypeScript entry points for browsers, excluding declaration-only files. */
|
|
114
89
|
async function compileScripts(outputAssets, production, projectRoot) {
|
|
115
90
|
const scriptRoot = path.join(projectRoot, 'app/assets/script');
|
|
116
91
|
const entries = (await fs.readdir(scriptRoot, { withFileTypes: true }))
|
|
@@ -130,27 +105,21 @@ async function compileScripts(outputAssets, production, projectRoot) {
|
|
|
130
105
|
});
|
|
131
106
|
await fs.writeFile(path.join(outputAssets, 'script/global.compiled.js'), '');
|
|
132
107
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Replace the project deployment directory with compiled pages, scripts, styles, and static assets.
|
|
135
|
-
* @param config - Validated deployment or database configuration.
|
|
136
|
-
* @param projectRoot - Project containing app sources and installed dependencies.
|
|
137
|
-
* @returns A promise resolving after the deployment directory has been rebuilt.
|
|
138
|
-
*/
|
|
108
|
+
/** Replace the deployment directory with compiled pages, scripts, styles, and static assets. */
|
|
139
109
|
export async function build(config = parseArguments(process.argv.slice(2)), projectRoot = defaultProjectRoot) {
|
|
140
110
|
const outputRoot = path.join(projectRoot, '_deploy');
|
|
141
111
|
const outputAssets = path.join(outputRoot, 'release', config.version, 'assets');
|
|
142
112
|
await fs.rm(outputRoot, { recursive: true, force: true });
|
|
143
113
|
await fs.mkdir(outputAssets, { recursive: true });
|
|
144
|
-
await
|
|
145
|
-
fs.cp(path.join(projectRoot, 'app/assets/data'), path.join(outputAssets, 'data'), { recursive: true }),
|
|
146
|
-
fs.cp(path.join(projectRoot, 'app/assets/font'), path.join(outputAssets, 'font'), { recursive: true }),
|
|
147
|
-
fs.cp(path.join(projectRoot, 'app/assets/image'), path.join(outputAssets, 'image'), { recursive: true })
|
|
148
|
-
]);
|
|
114
|
+
await fs.cp(path.join(projectRoot, 'app/assets/data'), path.join(outputAssets, 'data'), { recursive: true });
|
|
149
115
|
await fs.writeFile(path.join(outputAssets, 'data/config.json'), JSON.stringify(config));
|
|
150
116
|
await renderMarkup(config, outputRoot, projectRoot);
|
|
151
117
|
await compileStyles(outputAssets, config.production, projectRoot);
|
|
152
118
|
await compileScripts(outputAssets, config.production, projectRoot);
|
|
153
|
-
|
|
119
|
+
const robots = `User-agent: *\nAllow: /\nSitemap: ${config.siteUrl}/sitemap.xml\n`;
|
|
120
|
+
await fs.writeFile(path.join(outputRoot, 'robots.txt'), robots);
|
|
121
|
+
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>${config.siteUrl}/</loc></url></urlset>\n`;
|
|
122
|
+
await fs.writeFile(path.join(outputRoot, 'sitemap.xml'), sitemap);
|
|
154
123
|
}
|
|
155
124
|
if (path.resolve(process.argv[1] || '') === fileURLToPath(import.meta.url)) {
|
|
156
125
|
await build();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../scripts/build.ts"],"names":[],"mappings":"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","sourcesContent":["/** Values injected into page templates and the generated config file. */\nexport interface BuildConfig {cdn: string; production: boolean; service: string; version: string; www: string}\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {fileURLToPath} from 'node:url';\nimport {build as buildJavaScript} from 'esbuild';\nimport Handlebars from 'handlebars';\nimport * as sass from 'sass';\n\nconst defaultProjectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');\n\n/**\n * Read deployment flags and reject version strings that contain path separators.\n * @param argumentsList - Command-line arguments excluding the Node executable and script.\n * @returns Deployment configuration with defaults for omitted flags.\n * @throws When input does not satisfy the documented contract.\n */\nexport function parseArguments(argumentsList: string[]): BuildConfig {\n const values = Object.fromEntries(argumentsList.map((argument) => {\n const [key, ...value] = argument.replace(/^--/, '').split('=');\n return [key, value.join('=')];\n }));\n const version = values.version || String(Math.floor(Date.now() / 1000));\n if (!/^[A-Za-z0-9._-]+$/.test(version)) {\n throw new Error('Version may contain only letters, numbers, dots, underscores, and hyphens');\n }\n return {\n cdn: values.cdn || '/',\n production: values.production === 'true',\n service: values.service || '/service-endpoint',\n version,\n www: values.www || '/'\n };\n}\n\n/**\n * Recursively collect files below a directory, propagating filesystem failures.\n * @param directory - Directory to traverse recursively.\n * @returns A promise resolving to all descendant file paths.\n */\nasync function filesUnder(directory: string): Promise<string[]> {\n const entries = await fs.readdir(directory, {withFileTypes: true});\n const files = await Promise.all(entries.map(async (entry) => {\n const location = path.join(directory, entry.name);\n return entry.isDirectory() ? filesUnder(location) : [location];\n }));\n return files.flat();\n}\n\n/**\n * Register partials and render pages using global, page-specific, and deployment data.\n * @param config - Validated deployment or database configuration.\n * @param outputRoot - Destination for rendered pages.\n * @param projectRoot - Project containing app sources and installed dependencies.\n * @returns A promise resolving after all pages have been written.\n */\nasync function renderMarkup(config: BuildConfig, outputRoot: string, projectRoot: string) {\n const markupRoot = path.join(projectRoot, 'app/assets/markup');\n for (const partialPath of await filesUnder(markupRoot)) {\n if (/\\.(hbs|handlebars)$/.test(partialPath)) {\n const name = path.relative(markupRoot, partialPath).replace(/\\\\/g, '/').replace(/\\.(hbs|handlebars)$/, '');\n Handlebars.registerPartial(name, await fs.readFile(partialPath, 'utf8'));\n }\n }\n\n const globalData = JSON.parse(await fs.readFile(\n path.join(projectRoot, 'app/assets/data/view/global.json'), 'utf8'\n ));\n const pages = (await filesUnder(path.join(projectRoot, 'app')))\n .filter((file) => file.endsWith('.hbs') && !file.includes(`${path.sep}assets${path.sep}`));\n\n await Promise.all(pages.map(async (page) => {\n const relative = path.relative(path.join(projectRoot, 'app'), page);\n const pageDataPath = path.join(\n projectRoot, 'app/assets/data/view', relative.replace(/\\.hbs$/, '.json')\n );\n let pageData = {};\n try {\n pageData = JSON.parse(await fs.readFile(pageDataPath, 'utf8'));\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) {\n throw error;\n }\n }\n const template = Handlebars.compile(await fs.readFile(page, 'utf8'));\n const destination = path.join(outputRoot, relative.replace(/\\.hbs$/, '.html'));\n await fs.mkdir(path.dirname(destination), {recursive: true});\n await fs.writeFile(destination, template({...globalData, ...pageData, ...config}));\n }));\n}\n\n/**\n * Compile project Sass and include the full published Bootstrap stylesheet.\n * @param outputAssets - Destination for compiled asset directories.\n * @param production - Enable compressed output and omit development source maps.\n * @param projectRoot - Project containing app sources and installed dependencies.\n * @returns A promise resolving after all stylesheets have been written.\n */\nasync function compileStyles(outputAssets: string, production: boolean, projectRoot: string) {\n const styleRoot = path.join(projectRoot, 'app/assets/style');\n await Promise.all(['global', 'print', 'toolkit'].map(async (name) => {\n const result = sass.compile(path.join(styleRoot, `${name}.scss`), {\n loadPaths: [path.join(projectRoot, 'node_modules')],\n style: production ? 'compressed' : 'expanded'\n });\n const destination = path.join(outputAssets, 'style', `${name}.css`);\n await fs.mkdir(path.dirname(destination), {recursive: true});\n // Bootstrap publishes compiled CSS, so consuming it avoids recompiling third-party\n // Sass and keeps dependency deprecation warnings out of project builds.\n const bootstrapSource = name === 'global'\n ? (await fs.readFile(\n path.join(projectRoot, 'node_modules/bootstrap/dist/css/bootstrap.css'), 'utf8'\n )).replace(/\\/\\*# sourceMappingURL=bootstrap\\.css\\.map \\*\\//, '')\n : '';\n const bootstrapCss = production && bootstrapSource\n ? sass.compileString(bootstrapSource, {style: 'compressed', syntax: 'css'}).css\n : bootstrapSource;\n await fs.writeFile(destination, `${bootstrapCss}${bootstrapCss ? '\\n' : ''}${result.css}`);\n }));\n}\n\n/**\n * Bundle TypeScript entry points for browsers, excluding declaration-only files.\n * @param outputAssets - Destination for compiled asset directories.\n * @param production - Enable compressed output and omit development source maps.\n * @param projectRoot - Project containing app sources and installed dependencies.\n * @returns A promise resolving after all script bundles have been written.\n */\nasync function compileScripts(outputAssets: string, production: boolean, projectRoot: string) {\n const scriptRoot = path.join(projectRoot, 'app/assets/script');\n const entries = (await fs.readdir(scriptRoot, {withFileTypes: true}))\n .filter((entry) => entry.isFile() && !entry.name.endsWith('.d.ts') && /\\.tsx?$/.test(entry.name))\n .map((entry) => path.join(scriptRoot, entry.name));\n await fs.mkdir(path.join(outputAssets, 'script'), {recursive: true});\n await buildJavaScript({\n bundle: true,\n entryNames: '[name].compiled',\n entryPoints: entries,\n format: 'iife',\n loader: {'.ts': 'ts', '.tsx': 'tsx'},\n minify: production,\n outdir: path.join(outputAssets, 'script'),\n sourcemap: !production,\n target: ['es2018']\n });\n await fs.writeFile(path.join(outputAssets, 'script/global.compiled.js'), '');\n}\n\n/**\n * Replace the project deployment directory with compiled pages, scripts, styles, and static assets.\n * @param config - Validated deployment or database configuration.\n * @param projectRoot - Project containing app sources and installed dependencies.\n * @returns A promise resolving after the deployment directory has been rebuilt.\n */\nexport async function build(config = parseArguments(process.argv.slice(2)), projectRoot = defaultProjectRoot) {\n const outputRoot = path.join(projectRoot, '_deploy');\n const outputAssets = path.join(outputRoot, 'release', config.version, 'assets');\n await fs.rm(outputRoot, {recursive: true, force: true});\n await fs.mkdir(outputAssets, {recursive: true});\n\n await Promise.all([\n fs.cp(path.join(projectRoot, 'app/assets/data'), path.join(outputAssets, 'data'), {recursive: true}),\n fs.cp(path.join(projectRoot, 'app/assets/font'), path.join(outputAssets, 'font'), {recursive: true}),\n fs.cp(path.join(projectRoot, 'app/assets/image'), path.join(outputAssets, 'image'), {recursive: true})\n ]);\n await fs.writeFile(path.join(outputAssets, 'data/config.json'), JSON.stringify(config));\n await renderMarkup(config, outputRoot, projectRoot);\n await compileStyles(outputAssets, config.production, projectRoot);\n await compileScripts(outputAssets, config.production, projectRoot);\n await fs.copyFile(path.join(projectRoot, 'app/robots.txt'), path.join(outputRoot, 'robots.txt'));\n}\n\nif (path.resolve(process.argv[1] || '') === fileURLToPath(import.meta.url)) {\n await build();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../scripts/build.ts"],"names":[],"mappings":"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","sourcesContent":["/** Values injected into page templates and the generated config file. */\nexport interface BuildConfig {cdn: string; production: boolean; siteUrl: string; version: string; www: string}\nimport {execFile} from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {promisify} from 'node:util';\nimport {fileURLToPath, pathToFileURL} from 'node:url';\nimport {build as buildJavaScript} from 'esbuild';\n\nconst execFileAsync = promisify(execFile);\nconst defaultProjectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');\n\n/** Read deployment flags and reject unsafe or invalid deployment values. */\nexport function parseArguments(argumentsList: string[]): BuildConfig {\n const values = Object.fromEntries(argumentsList.map((argument) => {\n const [key, ...value] = argument.replace(/^--/, '').split('=');\n return [key, value.join('=')];\n }));\n const version = values.version || String(Math.floor(Date.now() / 1000));\n if (!/^[A-Za-z0-9._-]+$/.test(version)) {\n throw new Error('Version may contain only letters, numbers, dots, underscores, and hyphens');\n }\n const siteUrl = (values['site-url'] || 'http://localhost:8080').replace(/\\/$/, '');\n if (!URL.canParse(siteUrl) || (values.production === 'true' && !siteUrl.startsWith('https://'))) {\n throw new Error('Production builds require an absolute HTTPS --site-url');\n }\n return {\n cdn: values.cdn || '/',\n production: values.production === 'true',\n siteUrl,\n version,\n www: values.www || '/'\n };\n}\n\n/** Return the local Tailwind executable name for the current platform. */\nexport function tailwindExecutable(platform: NodeJS.Platform = process.platform) {\n return platform === 'win32' ? 'tailwindcss.cmd' : 'tailwindcss';\n}\n\n/** Recursively collect files below a directory, propagating filesystem failures. */\nasync function filesUnder(directory: string): Promise<string[]> {\n const entries = await fs.readdir(directory, {withFileTypes: true});\n const files = await Promise.all(entries.map(async (entry) => {\n const location = path.join(directory, entry.name);\n return entry.isDirectory() ? filesUnder(location) : [location];\n }));\n return files.flat();\n}\n\ninterface PageModule {\n default(data: Record<string, unknown>): {toString(): string} | string;\n}\n\n/** Render compiled TypeScript or TSX pages using global, page-specific, and deployment data. */\nasync function renderMarkup(config: BuildConfig, outputRoot: string, projectRoot: string) {\n const globalData = JSON.parse(await fs.readFile(\n path.join(projectRoot, 'app/assets/data/view/global.json'), 'utf8'\n )) as Record<string, unknown>;\n const appRoot = path.join(projectRoot, 'app');\n const pages = (await filesUnder(appRoot))\n .filter((file) => /\\.tsx?$/.test(file) && !file.includes(`${path.sep}assets${path.sep}`));\n\n await Promise.all(pages.map(async (page) => {\n const relative = path.relative(appRoot, page);\n const pageDataPath = path.join(\n projectRoot, 'app/assets/data/view', relative.replace(/\\.tsx?$/, '.json')\n );\n let pageData: Record<string, unknown> = {};\n try {\n pageData = JSON.parse(await fs.readFile(pageDataPath, 'utf8')) as Record<string, unknown>;\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) {\n throw error;\n }\n }\n const compiledPage = path.join(projectRoot, 'dist/app', relative.replace(/\\.tsx?$/, '.js'));\n const module = await import(pathToFileURL(compiledPage).href) as PageModule;\n if (typeof module.default !== 'function') {\n throw new TypeError(`Page module ${relative} must export a default render function`);\n }\n const markup = module.default({...globalData, ...pageData, ...config});\n const destination = path.join(outputRoot, relative.replace(/\\.tsx?$/, '.html'));\n await fs.mkdir(path.dirname(destination), {recursive: true});\n await fs.writeFile(destination, `<!DOCTYPE html>\\n${String(markup)}\\n`);\n }));\n}\n\n/** Compile Tailwind and project CSS into the deploy directory. */\nasync function compileStyles(outputAssets: string, production: boolean, projectRoot: string) {\n const styleRoot = path.join(projectRoot, 'app/assets/style');\n await fs.mkdir(path.join(outputAssets, 'style'), {recursive: true});\n const destination = path.join(outputAssets, 'style/global.css');\n const argumentsList = [\n '-i', path.join(styleRoot, 'global.css'),\n '-o', destination\n ];\n if (production) {argumentsList.push('--minify');}\n await execFileAsync(tailwindExecutable(), argumentsList, {cwd: projectRoot});\n await fs.copyFile(path.join(styleRoot, 'print.css'), path.join(outputAssets, 'style/print.css'));\n}\n\n/** Bundle TypeScript entry points for browsers, excluding declaration-only files. */\nasync function compileScripts(outputAssets: string, production: boolean, projectRoot: string) {\n const scriptRoot = path.join(projectRoot, 'app/assets/script');\n const entries = (await fs.readdir(scriptRoot, {withFileTypes: true}))\n .filter((entry) => entry.isFile() && !entry.name.endsWith('.d.ts') && /\\.tsx?$/.test(entry.name))\n .map((entry) => path.join(scriptRoot, entry.name));\n await fs.mkdir(path.join(outputAssets, 'script'), {recursive: true});\n await buildJavaScript({\n bundle: true,\n entryNames: '[name].compiled',\n entryPoints: entries,\n format: 'iife',\n loader: {'.ts': 'ts', '.tsx': 'tsx'},\n minify: production,\n outdir: path.join(outputAssets, 'script'),\n sourcemap: !production,\n target: ['es2018']\n });\n await fs.writeFile(path.join(outputAssets, 'script/global.compiled.js'), '');\n}\n\n/** Replace the deployment directory with compiled pages, scripts, styles, and static assets. */\nexport async function build(config = parseArguments(process.argv.slice(2)), projectRoot = defaultProjectRoot) {\n const outputRoot = path.join(projectRoot, '_deploy');\n const outputAssets = path.join(outputRoot, 'release', config.version, 'assets');\n await fs.rm(outputRoot, {recursive: true, force: true});\n await fs.mkdir(outputAssets, {recursive: true});\n\n await fs.cp(path.join(projectRoot, 'app/assets/data'), path.join(outputAssets, 'data'), {recursive: true});\n await fs.writeFile(path.join(outputAssets, 'data/config.json'), JSON.stringify(config));\n await renderMarkup(config, outputRoot, projectRoot);\n await compileStyles(outputAssets, config.production, projectRoot);\n await compileScripts(outputAssets, config.production, projectRoot);\n const robots = `User-agent: *\\nAllow: /\\nSitemap: ${config.siteUrl}/sitemap.xml\\n`;\n await fs.writeFile(path.join(outputRoot, 'robots.txt'), robots);\n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"><url><loc>${config.siteUrl}/</loc></url></urlset>\\n`;\n await fs.writeFile(path.join(outputRoot, 'sitemap.xml'), sitemap);\n}\n\nif (path.resolve(process.argv[1] || '') === fileURLToPath(import.meta.url)) {\n await build();\n}\n"]}
|