generator-white-label 9.0.0 → 10.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.
Files changed (98) hide show
  1. package/CLI.md +13 -17
  2. package/EXISTING_APPLICATIONS.md +155 -0
  3. package/PACKAGE_MANAGERS.md +6 -4
  4. package/README.md +135 -153
  5. package/app/404.ts +28 -0
  6. package/app/README.md +19 -7
  7. package/{scaffold/no-jsx/app → app}/assets/script/index.ts +1 -0
  8. package/{scaffold/no-jsx/app → app}/assets/script/tasks/TaskView.ts +1 -1
  9. package/app/assets/view/CodeBlock.ts +15 -0
  10. package/app/assets/view/examples/tasks/TaskExample.ts +50 -0
  11. package/app/assets/view/examples/tasks/task-state.ts +1 -1
  12. package/app/assets/view/layout/SiteFooter.ts +11 -0
  13. package/app/assets/view/layout/SiteHeader.ts +17 -0
  14. package/app/assets/view/page-data.ts +2 -2
  15. package/app/assets/view/sections/GettingStartedSection.ts +26 -0
  16. package/app/assets/view/sections/HeroSection.ts +15 -0
  17. package/app/assets/view/sections/LiveExampleSection.ts +24 -0
  18. package/app/assets/view/sections/PackageDocsSection.ts +69 -0
  19. package/app/assets/view/sections/{SourceGuideSection.tsx → SourceGuideSection.ts} +11 -16
  20. package/app/index.ts +50 -0
  21. package/cli/index.ts +4 -41
  22. package/dist/app/404.d.ts +1 -1
  23. package/dist/app/404.js +18 -2
  24. package/dist/app/404.js.map +1 -1
  25. package/dist/app/assets/script/index.d.ts +1 -6
  26. package/dist/app/assets/script/index.js +1 -6
  27. package/dist/app/assets/script/index.js.map +1 -1
  28. package/dist/app/assets/script/tasks/TaskView.d.ts +1 -6
  29. package/dist/app/assets/script/tasks/TaskView.js +2 -8
  30. package/dist/app/assets/script/tasks/TaskView.js.map +1 -1
  31. package/dist/app/assets/view/CodeBlock.d.ts +3 -3
  32. package/dist/app/assets/view/CodeBlock.js +4 -2
  33. package/dist/app/assets/view/CodeBlock.js.map +1 -1
  34. package/dist/app/assets/view/examples/tasks/TaskExample.d.ts +2 -8
  35. package/dist/app/assets/view/examples/tasks/TaskExample.js +41 -9
  36. package/dist/app/assets/view/examples/tasks/TaskExample.js.map +1 -1
  37. package/dist/app/assets/view/examples/tasks/task-state.d.ts +1 -1
  38. package/dist/app/assets/view/examples/tasks/task-state.js.map +1 -1
  39. package/dist/app/assets/view/layout/SiteFooter.d.ts +2 -2
  40. package/dist/app/assets/view/layout/SiteFooter.js +8 -3
  41. package/dist/app/assets/view/layout/SiteFooter.js.map +1 -1
  42. package/dist/app/assets/view/layout/SiteHeader.d.ts +2 -8
  43. package/dist/app/assets/view/layout/SiteHeader.js +14 -9
  44. package/dist/app/assets/view/layout/SiteHeader.js.map +1 -1
  45. package/dist/app/assets/view/page-data.d.ts +2 -2
  46. package/dist/app/assets/view/page-data.js.map +1 -1
  47. package/dist/app/assets/view/sections/GettingStartedSection.d.ts +1 -1
  48. package/dist/app/assets/view/sections/GettingStartedSection.js +21 -6
  49. package/dist/app/assets/view/sections/GettingStartedSection.js.map +1 -1
  50. package/dist/app/assets/view/sections/HeroSection.d.ts +2 -8
  51. package/dist/app/assets/view/sections/HeroSection.js +12 -9
  52. package/dist/app/assets/view/sections/HeroSection.js.map +1 -1
  53. package/dist/app/assets/view/sections/LiveExampleSection.d.ts +2 -6
  54. package/dist/app/assets/view/sections/LiveExampleSection.js +17 -7
  55. package/dist/app/assets/view/sections/LiveExampleSection.js.map +1 -1
  56. package/dist/app/assets/view/sections/PackageDocsSection.d.ts +1 -1
  57. package/dist/app/assets/view/sections/PackageDocsSection.js +57 -20
  58. package/dist/app/assets/view/sections/PackageDocsSection.js.map +1 -1
  59. package/dist/app/assets/view/sections/SourceGuideSection.d.ts +2 -7
  60. package/dist/app/assets/view/sections/SourceGuideSection.js +20 -8
  61. package/dist/app/assets/view/sections/SourceGuideSection.js.map +1 -1
  62. package/dist/app/index.d.ts +2 -8
  63. package/dist/app/index.js +36 -11
  64. package/dist/app/index.js.map +1 -1
  65. package/dist/cli/index.d.ts +0 -3
  66. package/dist/cli/index.js +4 -36
  67. package/dist/cli/index.js.map +1 -1
  68. package/dist/scaffold/index.d.ts +2 -13
  69. package/dist/scaffold/index.js +17 -53
  70. package/dist/scaffold/index.js.map +1 -1
  71. package/dist/scripts/build.js +97 -28
  72. package/dist/scripts/build.js.map +1 -1
  73. package/dist/server/handler.js +2 -9
  74. package/dist/server/handler.js.map +1 -1
  75. package/package.json +22 -22
  76. package/pnpm-workspace.yaml +0 -1
  77. package/scaffold/AGENTS.md +13 -8
  78. package/scaffold/index.ts +17 -57
  79. package/scripts/build.ts +90 -30
  80. package/server/handler.ts +2 -10
  81. package/tsconfig.site.json +1 -4
  82. package/app/404.tsx +0 -28
  83. package/app/assets/script/index.tsx +0 -13
  84. package/app/assets/script/tasks/TaskView.tsx +0 -24
  85. package/app/assets/view/CodeBlock.tsx +0 -17
  86. package/app/assets/view/examples/tasks/TaskExample.tsx +0 -63
  87. package/app/assets/view/layout/SiteFooter.tsx +0 -11
  88. package/app/assets/view/layout/SiteHeader.tsx +0 -23
  89. package/app/assets/view/sections/GettingStartedSection.tsx +0 -27
  90. package/app/assets/view/sections/HeroSection.tsx +0 -21
  91. package/app/assets/view/sections/LiveExampleSection.tsx +0 -29
  92. package/app/assets/view/sections/PackageDocsSection.tsx +0 -69
  93. package/app/index.tsx +0 -54
  94. package/scaffold/no-jsx/README.md +0 -73
  95. package/scaffold/no-jsx/app/404.ts +0 -10
  96. package/scaffold/no-jsx/app/assets/view/examples/tasks/TaskExample.ts +0 -39
  97. package/scaffold/no-jsx/app/index.ts +0 -58
  98. package/tsconfig.site.no-jsx.json +0 -24
package/dist/app/index.js CHANGED
@@ -1,5 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "white-label-view/jsx-runtime";
2
- import { raw } from 'white-label-view/jsx-runtime';
1
+ import { html, unsafeHTML } from 'white-label-view/html';
3
2
  import SiteFooter from './assets/view/layout/SiteFooter.js';
4
3
  import SiteHeader from './assets/view/layout/SiteHeader.js';
5
4
  import GettingStartedSection from './assets/view/sections/GettingStartedSection.js';
@@ -7,16 +6,42 @@ import HeroSection from './assets/view/sections/HeroSection.js';
7
6
  import LiveExampleSection from './assets/view/sections/LiveExampleSection.js';
8
7
  import PackageDocsSection from './assets/view/sections/PackageDocsSection.js';
9
8
  import SourceGuideSection from './assets/view/sections/SourceGuideSection.js';
10
- /**
11
- * The page template is intentionally thin.
12
- *
13
- * Developers learning White Label can start here to see the complete page at
14
- * a glance, then follow each import into a focused example. Build-time data is
15
- * kept at this boundary while individual views stay reusable and easy to read.
16
- */
9
+ function structuredDataScript(value) {
10
+ const json = JSON.stringify(value).replaceAll('<', '\\u003c');
11
+ return unsafeHTML(`<script type="application/ld+json">${json}</script>`);
12
+ }
13
+ /** Compose the static page from small tagged-template functions. */
17
14
  export default function IndexPage(data) {
18
15
  const page = data;
19
- const structuredData = JSON.stringify(page.structuredData).replaceAll('<', '\\u003c');
20
- return (_jsxs("html", { id: "index", lang: "en-US", dir: "ltr", children: [_jsxs("head", { children: [_jsx("meta", { charset: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: page.title }), _jsx("meta", { name: "description", content: page.meta.description }), _jsx("meta", { name: "robots", content: "index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1" }), _jsx("link", { rel: "canonical", href: `${page.siteUrl}/` }), _jsx("meta", { property: "og:title", content: page.title }), _jsx("meta", { property: "og:description", content: page.meta.description }), _jsx("meta", { property: "og:type", content: "website" }), _jsx("meta", { property: "og:url", content: `${page.siteUrl}/` }), _jsx("link", { rel: "stylesheet", href: `${page.cdn}release/${page.version}/assets/style/global.css`, media: "all" }), _jsx("link", { rel: "stylesheet", href: `${page.cdn}release/${page.version}/assets/style/print.css`, media: "print" }), _jsx("script", { type: "application/ld+json", children: raw(structuredData) })] }), _jsxs("body", { itemscope: true, itemtype: "https://schema.org/WebPage", children: [_jsx("a", { className: "skip-link", href: "#main", children: "Skip to content" }), _jsx(SiteHeader, {}), _jsxs("main", { id: "main", tabindex: "-1", children: [_jsx(HeroSection, {}), _jsx(LiveExampleSection, {}), _jsx(PackageDocsSection, {}), _jsx(SourceGuideSection, {}), _jsx(GettingStartedSection, {})] }), _jsx(SiteFooter, {}), _jsx("script", { src: `${page.cdn}release/${page.version}/assets/script/index.compiled.js`, defer: true })] })] }));
16
+ return html `<html id="index" lang="en-US" dir="ltr">
17
+ <head>
18
+ <meta charset="utf-8">
19
+ <meta name="viewport" content="width=device-width, initial-scale=1">
20
+ <title>${page.title}</title>
21
+ <meta name="description" content="${page.meta.description}">
22
+ <meta name="robots" content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1">
23
+ <link rel="canonical" href="${page.siteUrl}/">
24
+ <meta property="og:title" content="${page.title}">
25
+ <meta property="og:description" content="${page.meta.description}">
26
+ <meta property="og:type" content="website">
27
+ <meta property="og:url" content="${page.siteUrl}/">
28
+ <link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/global.css" media="all">
29
+ <link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/print.css" media="print">
30
+ ${structuredDataScript(page.structuredData)}
31
+ </head>
32
+ <body itemscope itemtype="https://schema.org/WebPage">
33
+ <a class="skip-link" href="#main">Skip to content</a>
34
+ ${SiteHeader()}
35
+ <main id="main" tabindex="-1">
36
+ ${HeroSection()}
37
+ ${LiveExampleSection()}
38
+ ${PackageDocsSection()}
39
+ ${SourceGuideSection()}
40
+ ${GettingStartedSection()}
41
+ </main>
42
+ ${SiteFooter()}
43
+ <script src="${page.cdn}release/${page.version}/assets/script/index.compiled.js" defer></script>
44
+ </body>
45
+ </html>`;
21
46
  }
22
47
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../app/index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,8BAA8B,CAAC;AACjD,OAAO,UAAU,MAAM,oCAAoC,CAAC;AAC5D,OAAO,UAAU,MAAM,oCAAoC,CAAC;AAE5D,OAAO,qBAAqB,MAAM,iDAAiD,CAAC;AACpF,OAAO,WAAW,MAAM,uCAAuC,CAAC;AAChE,OAAO,kBAAkB,MAAM,8CAA8C,CAAC;AAC9E,OAAO,kBAAkB,MAAM,8CAA8C,CAAC;AAC9E,OAAO,kBAAkB,MAAM,8CAA8C,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,IAA6B;IAC3D,MAAM,IAAI,GAAG,IAA2B,CAAC;IACzC,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAEtF,OAAO,CACH,gBAAM,EAAE,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,GAAG,EAAC,KAAK,aACnC,2BACI,eAAM,OAAO,EAAC,OAAO,GAAG,EACxB,eAAM,IAAI,EAAC,UAAU,EAAC,OAAO,EAAC,qCAAqC,GAAG,EACtE,0BAAQ,IAAI,CAAC,KAAK,GAAS,EAC3B,eAAM,IAAI,EAAC,aAAa,EAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,GAAI,EAC3D,eAAM,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAC,0EAA0E,GAAG,EACzG,eAAM,GAAG,EAAC,WAAW,EAAC,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,GAAI,EAClD,eAAM,QAAQ,EAAC,UAAU,EAAC,OAAO,EAAE,IAAI,CAAC,KAAK,GAAI,EACjD,eAAM,QAAQ,EAAC,gBAAgB,EAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,GAAI,EAClE,eAAM,QAAQ,EAAC,SAAS,EAAC,OAAO,EAAC,SAAS,GAAG,EAC7C,eAAM,QAAQ,EAAC,QAAQ,EAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,GAAI,EACvD,eAAM,GAAG,EAAC,YAAY,EAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO,0BAA0B,EAAE,KAAK,EAAC,KAAK,GAAG,EACzG,eAAM,GAAG,EAAC,YAAY,EAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO,yBAAyB,EAAE,KAAK,EAAC,OAAO,GAAG,EAC1G,iBAAQ,IAAI,EAAC,qBAAqB,YAAE,GAAG,CAAC,cAAc,CAAC,GAAU,IAC9D,EACP,gBAAM,SAAS,QAAC,QAAQ,EAAC,4BAA4B,aACjD,YAAG,SAAS,EAAC,WAAW,EAAC,IAAI,EAAC,OAAO,gCAAoB,EACzD,KAAC,UAAU,KAAG,EACd,gBAAM,EAAE,EAAC,MAAM,EAAC,QAAQ,EAAC,IAAI,aACzB,KAAC,WAAW,KAAG,EACf,KAAC,kBAAkB,KAAG,EACtB,KAAC,kBAAkB,KAAG,EACtB,KAAC,kBAAkB,KAAG,EACtB,KAAC,qBAAqB,KAAG,IACtB,EACP,KAAC,UAAU,KAAG,EACd,iBAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO,kCAAkC,EAAE,KAAK,SAAG,IACxF,IACJ,CACV,CAAC;AACN,CAAC","sourcesContent":["import {raw} from 'white-label-view/jsx-runtime';\nimport SiteFooter from './assets/view/layout/SiteFooter.js';\nimport SiteHeader from './assets/view/layout/SiteHeader.js';\nimport type {PageData} from './assets/view/page-data.js';\nimport GettingStartedSection from './assets/view/sections/GettingStartedSection.js';\nimport HeroSection from './assets/view/sections/HeroSection.js';\nimport LiveExampleSection from './assets/view/sections/LiveExampleSection.js';\nimport PackageDocsSection from './assets/view/sections/PackageDocsSection.js';\nimport SourceGuideSection from './assets/view/sections/SourceGuideSection.js';\n\n/**\n * The page template is intentionally thin.\n *\n * Developers learning White Label can start here to see the complete page at\n * a glance, then follow each import into a focused example. Build-time data is\n * kept at this boundary while individual views stay reusable and easy to read.\n */\nexport default function IndexPage(data: Record<string, unknown>) {\n const page = data as unknown as PageData;\n const structuredData = JSON.stringify(page.structuredData).replaceAll('<', '\\\\u003c');\n\n return (\n <html id=\"index\" lang=\"en-US\" dir=\"ltr\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>{page.title}</title>\n <meta name=\"description\" content={page.meta.description} />\n <meta name=\"robots\" content=\"index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1\" />\n <link rel=\"canonical\" href={`${page.siteUrl}/`} />\n <meta property=\"og:title\" content={page.title} />\n <meta property=\"og:description\" content={page.meta.description} />\n <meta property=\"og:type\" content=\"website\" />\n <meta property=\"og:url\" content={`${page.siteUrl}/`} />\n <link rel=\"stylesheet\" href={`${page.cdn}release/${page.version}/assets/style/global.css`} media=\"all\" />\n <link rel=\"stylesheet\" href={`${page.cdn}release/${page.version}/assets/style/print.css`} media=\"print\" />\n <script type=\"application/ld+json\">{raw(structuredData)}</script>\n </head>\n <body itemscope itemtype=\"https://schema.org/WebPage\">\n <a className=\"skip-link\" href=\"#main\">Skip to content</a>\n <SiteHeader />\n <main id=\"main\" tabindex=\"-1\">\n <HeroSection />\n <LiveExampleSection />\n <PackageDocsSection />\n <SourceGuideSection />\n <GettingStartedSection />\n </main>\n <SiteFooter />\n <script src={`${page.cdn}release/${page.version}/assets/script/index.compiled.js`} defer />\n </body>\n </html>\n );\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../app/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAE,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACvD,OAAO,UAAU,MAAM,oCAAoC,CAAC;AAC5D,OAAO,UAAU,MAAM,oCAAoC,CAAC;AAE5D,OAAO,qBAAqB,MAAM,iDAAiD,CAAC;AACpF,OAAO,WAAW,MAAM,uCAAuC,CAAC;AAChE,OAAO,kBAAkB,MAAM,8CAA8C,CAAC;AAC9E,OAAO,kBAAkB,MAAM,8CAA8C,CAAC;AAC9E,OAAO,kBAAkB,MAAM,8CAA8C,CAAC;AAE9E,SAAS,oBAAoB,CAAC,KAAc;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC9D,OAAO,UAAU,CAAC,sCAAsC,IAAI,WAAW,CAAC,CAAC;AAC7E,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,IAA6B;IAC3D,MAAM,IAAI,GAAG,IAA2B,CAAC;IAEzC,OAAO,IAAI,CAAA;;;;yBAIU,IAAI,CAAC,KAAK;oDACiB,IAAI,CAAC,IAAI,CAAC,WAAW;;8CAE3B,IAAI,CAAC,OAAO;qDACL,IAAI,CAAC,KAAK;2DACJ,IAAI,CAAC,IAAI,CAAC,WAAW;;mDAE7B,IAAI,CAAC,OAAO;+CAChB,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO;+CAC/B,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO;kBAC5D,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC;;;;kBAIzC,UAAU,EAAE;;sBAER,WAAW,EAAE;sBACb,kBAAkB,EAAE;sBACpB,kBAAkB,EAAE;sBACpB,kBAAkB,EAAE;sBACpB,qBAAqB,EAAE;;kBAE3B,UAAU,EAAE;+BACC,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO;;gBAE9C,CAAC;AACjB,CAAC","sourcesContent":["import {html, unsafeHTML} from 'white-label-view/html';\nimport SiteFooter from './assets/view/layout/SiteFooter.js';\nimport SiteHeader from './assets/view/layout/SiteHeader.js';\nimport type {PageData} from './assets/view/page-data.js';\nimport GettingStartedSection from './assets/view/sections/GettingStartedSection.js';\nimport HeroSection from './assets/view/sections/HeroSection.js';\nimport LiveExampleSection from './assets/view/sections/LiveExampleSection.js';\nimport PackageDocsSection from './assets/view/sections/PackageDocsSection.js';\nimport SourceGuideSection from './assets/view/sections/SourceGuideSection.js';\n\nfunction structuredDataScript(value: unknown) {\n const json = JSON.stringify(value).replaceAll('<', '\\\\u003c');\n return unsafeHTML(`<script type=\"application/ld+json\">${json}</script>`);\n}\n\n/** Compose the static page from small tagged-template functions. */\nexport default function IndexPage(data: Record<string, unknown>) {\n const page = data as unknown as PageData;\n\n return html`<html id=\"index\" lang=\"en-US\" dir=\"ltr\">\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${page.title}</title>\n <meta name=\"description\" content=\"${page.meta.description}\">\n <meta name=\"robots\" content=\"index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1\">\n <link rel=\"canonical\" href=\"${page.siteUrl}/\">\n <meta property=\"og:title\" content=\"${page.title}\">\n <meta property=\"og:description\" content=\"${page.meta.description}\">\n <meta property=\"og:type\" content=\"website\">\n <meta property=\"og:url\" content=\"${page.siteUrl}/\">\n <link rel=\"stylesheet\" href=\"${page.cdn}release/${page.version}/assets/style/global.css\" media=\"all\">\n <link rel=\"stylesheet\" href=\"${page.cdn}release/${page.version}/assets/style/print.css\" media=\"print\">\n ${structuredDataScript(page.structuredData)}\n </head>\n <body itemscope itemtype=\"https://schema.org/WebPage\">\n <a class=\"skip-link\" href=\"#main\">Skip to content</a>\n ${SiteHeader()}\n <main id=\"main\" tabindex=\"-1\">\n ${HeroSection()}\n ${LiveExampleSection()}\n ${PackageDocsSection()}\n ${SourceGuideSection()}\n ${GettingStartedSection()}\n </main>\n ${SiteFooter()}\n <script src=\"${page.cdn}release/${page.version}/assets/script/index.compiled.js\" defer></script>\n </body>\n </html>`;\n}\n"]}
@@ -5,9 +5,6 @@ export interface CliDependencies {
5
5
  createProject?: typeof createProject;
6
6
  stdout?: Pick<NodeJS.WriteStream, 'write'>;
7
7
  stderr?: Pick<NodeJS.WriteStream, 'write'>;
8
- input?: NodeJS.ReadableStream;
9
- output?: NodeJS.WritableStream;
10
- isInteractive?: boolean;
11
8
  }
12
9
  export declare function formatCliError(error: unknown): string;
13
10
  export declare function runCli(args: readonly string[], dependencies?: CliDependencies): Promise<0 | 1>;
package/dist/cli/index.js CHANGED
@@ -3,36 +3,23 @@ import { realpathSync } from 'node:fs';
3
3
  import { readdir } from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import process from 'node:process';
6
- import { createInterface } from 'node:readline/promises';
7
6
  import { fileURLToPath } from 'node:url';
8
7
  import { createProject } from '../scaffold/index.js';
9
8
  function usage() {
10
9
  return [
11
10
  'Usage:',
12
- ' white-label create <directory> [--jsx | --no-jsx]',
11
+ ' white-label create <directory>',
13
12
  '',
14
13
  'Commands:',
15
- ' create <directory> Create a White Label project.',
14
+ ' create <directory> Create a tagged-template White Label project.',
16
15
  '',
17
16
  'Options:',
18
- ' --jsx Generate TypeScript with JSX/TSX templates.',
19
- ' --no-jsx Generate plain TypeScript with HTML string templates.',
20
17
  ' -h, --help Show this help message.'
21
18
  ].join('\n');
22
19
  }
23
20
  export function formatCliError(error) {
24
21
  return error instanceof Error ? error.message : String(error);
25
22
  }
26
- async function askForJsx(input, output) {
27
- const readline = createInterface({ input, output });
28
- try {
29
- 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) ');
30
- return !/^n(?:o)?$/i.test(answer.trim());
31
- }
32
- finally {
33
- readline.close();
34
- }
35
- }
36
23
  /** Reject an existing non-empty target so a mistyped CLI destination cannot overwrite project files. */
37
24
  async function assertDestinationAvailable(destination) {
38
25
  try {
@@ -53,37 +40,18 @@ export async function runCli(args, dependencies = {}) {
53
40
  const scaffold = dependencies.createProject ?? createProject;
54
41
  const stdout = dependencies.stdout ?? process.stdout;
55
42
  const stderr = dependencies.stderr ?? process.stderr;
56
- const input = dependencies.input ?? process.stdin;
57
- const output = dependencies.output ?? process.stdout;
58
43
  const [command, destination, ...options] = args;
59
44
  if (command === '--help' || command === '-h' || args.length === 0) {
60
45
  stdout.write(`${usage()}\n`);
61
46
  return 0;
62
47
  }
63
- const allowedOptions = new Set(['--jsx', '--no-jsx']);
64
- if (command !== 'create' ||
65
- !destination ||
66
- options.some(option => !allowedOptions.has(option)) ||
67
- (options.includes('--jsx') && options.includes('--no-jsx'))) {
48
+ if (command !== 'create' || !destination || options.length) {
68
49
  stderr.write(`${usage()}\n`);
69
50
  return 1;
70
51
  }
71
- let jsx;
72
- if (options.includes('--jsx')) {
73
- jsx = true;
74
- }
75
- else if (options.includes('--no-jsx')) {
76
- jsx = false;
77
- }
78
- else if (dependencies.isInteractive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
79
- jsx = await askForJsx(input, output);
80
- }
81
- else {
82
- jsx = true;
83
- }
84
52
  const resolvedDestination = path.resolve(cwd(), destination);
85
53
  await assertDestinationAvailable(resolvedDestination);
86
- await scaffold({ destination: resolvedDestination, jsx });
54
+ await scaffold({ destination: resolvedDestination });
87
55
  stdout.write(`Created White Label project at ${resolvedDestination}\n`);
88
56
  return 0;
89
57
  }
@@ -1 +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 {realpathSync} from 'node:fs';\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 (realpathSync(invokedPath) === realpathSync(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"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport {realpathSync} from 'node:fs';\nimport {readdir} from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\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}\n\nfunction usage() {\n return [\n 'Usage:',\n ' white-label create <directory>',\n '',\n 'Commands:',\n ' create <directory> Create a tagged-template White Label project.',\n '',\n 'Options:',\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\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 [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 if (command !== 'create' || !destination || options.length) {\n stderr.write(`${usage()}\\n`);\n return 1;\n }\n\n const resolvedDestination = path.resolve(cwd(), destination);\n await assertDestinationAvailable(resolvedDestination);\n await scaffold({destination: resolvedDestination});\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 (realpathSync(invokedPath) === realpathSync(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"]}
@@ -5,7 +5,6 @@ export interface ScaffoldFileSystem {
5
5
  export interface CreateProjectOptions {
6
6
  destination: string;
7
7
  fileSystem?: ScaffoldFileSystem;
8
- jsx?: boolean;
9
8
  }
10
9
  /** Return the package manifest used by generated White Label projects. */
11
10
  export declare function createSiteManifest(): {
@@ -24,7 +23,6 @@ export declare function createSiteManifest(): {
24
23
  allowScripts: {
25
24
  '@parcel/watcher@2.5.1': boolean;
26
25
  'esbuild@0.28.2': boolean;
27
- 'white-label-view@6.0.0': boolean;
28
26
  };
29
27
  dependenciesMeta: {
30
28
  '@parcel/watcher@2.5.1': {
@@ -33,9 +31,6 @@ export declare function createSiteManifest(): {
33
31
  'esbuild@0.28.2': {
34
32
  built: boolean;
35
33
  };
36
- 'white-label-view@6.0.0': {
37
- built: boolean;
38
- };
39
34
  };
40
35
  devDependencies: {
41
36
  '@tailwindcss/cli': string;
@@ -54,11 +49,5 @@ export declare function createSiteManifest(): {
54
49
  'white-label-view': string;
55
50
  };
56
51
  };
57
- /**
58
- * Create a White Label project without overwriting a non-empty destination.
59
- *
60
- * This is the shared implementation behind every creation interface. Adapters
61
- * translate their environment into these options instead of owning templates,
62
- * overwrite policy, or project-generation behavior themselves.
63
- */
64
- export declare function createProject({ destination, fileSystem, jsx }: CreateProjectOptions): Promise<void>;
52
+ /** Create the canonical tagged-template White Label starter without overwriting user files. */
53
+ export declare function createProject({ destination, fileSystem }: CreateProjectOptions): Promise<void>;
@@ -43,13 +43,11 @@ export function createSiteManifest() {
43
43
  },
44
44
  allowScripts: {
45
45
  '@parcel/watcher@2.5.1': true,
46
- 'esbuild@0.28.2': true,
47
- 'white-label-view@6.0.0': true
46
+ 'esbuild@0.28.2': true
48
47
  },
49
48
  dependenciesMeta: {
50
49
  '@parcel/watcher@2.5.1': { built: true },
51
- 'esbuild@0.28.2': { built: true },
52
- 'white-label-view@6.0.0': { built: true }
50
+ 'esbuild@0.28.2': { built: true }
53
51
  },
54
52
  devDependencies: {
55
53
  '@tailwindcss/cli': '4.3.3',
@@ -64,60 +62,26 @@ export function createSiteManifest() {
64
62
  dependencies: {
65
63
  'white-label-mediator': '5.0.0',
66
64
  'white-label-model': '7.0.1',
67
- 'white-label-router': '6.0.0',
68
- 'white-label-view': '6.0.0'
65
+ 'white-label-router': '6.1.0',
66
+ 'white-label-view': '7.0.0'
69
67
  }
70
68
  };
71
69
  }
72
- const commonNoJsxCopies = [
73
- ['app/assets/data', 'app/assets/data'],
74
- ['app/assets/style', 'app/assets/style'],
75
- ['app/assets/script/tasks/TaskApplication.ts', 'app/assets/script/tasks/TaskApplication.ts'],
76
- ['app/assets/script/tasks/TaskMediator.ts', 'app/assets/script/tasks/TaskMediator.ts'],
77
- ['app/assets/script/tasks/TaskModel.ts', 'app/assets/script/tasks/TaskModel.ts'],
78
- ['app/assets/script/tasks/TaskRouter.ts', 'app/assets/script/tasks/TaskRouter.ts'],
79
- ['app/assets/view/examples/tasks/task-state.ts', 'app/assets/view/examples/tasks/task-state.ts'],
80
- ['app/package.json', 'app/package.json']
70
+ const scaffoldCopies = [
71
+ ['.editorconfig', '.editorconfig'],
72
+ ['.yarnrc.yml', '.yarnrc.yml'],
73
+ ['pnpm-workspace.yaml', 'pnpm-workspace.yaml'],
74
+ ['app', 'app'],
75
+ ['app/README.md', 'README.md'],
76
+ ['server', 'server'],
77
+ ['scripts', 'scripts'],
78
+ ['template-test', 'test'],
79
+ ['tsconfig.site.json', 'tsconfig.json']
81
80
  ];
82
- const noJsxTemplateCopies = [
83
- ['scaffold/no-jsx/app/index.ts', 'app/index.ts'],
84
- ['scaffold/no-jsx/app/404.ts', 'app/404.ts'],
85
- ['scaffold/no-jsx/app/assets/script/index.ts', 'app/assets/script/index.ts'],
86
- ['scaffold/no-jsx/app/assets/script/tasks/TaskView.ts', 'app/assets/script/tasks/TaskView.ts'],
87
- ['scaffold/no-jsx/app/assets/view/examples/tasks/TaskExample.ts', 'app/assets/view/examples/tasks/TaskExample.ts'],
88
- ['scaffold/no-jsx/README.md', 'README.md'],
89
- ['tsconfig.site.no-jsx.json', 'tsconfig.json']
90
- ];
91
- /**
92
- * Create a White Label project without overwriting a non-empty destination.
93
- *
94
- * This is the shared implementation behind every creation interface. Adapters
95
- * translate their environment into these options instead of owning templates,
96
- * overwrite policy, or project-generation behavior themselves.
97
- */
98
- export async function createProject({ destination, fileSystem = nodeFileSystem, jsx = true }) {
81
+ /** Create the canonical tagged-template White Label starter without overwriting user files. */
82
+ export async function createProject({ destination, fileSystem = nodeFileSystem }) {
99
83
  await assertDestinationAvailable(destination);
100
- const copies = jsx ? [
101
- ['.editorconfig', '.editorconfig'],
102
- ['.yarnrc.yml', '.yarnrc.yml'],
103
- ['pnpm-workspace.yaml', 'pnpm-workspace.yaml'],
104
- ['app', 'app'],
105
- ['app/README.md', 'README.md'],
106
- ['server', 'server'],
107
- ['scripts', 'scripts'],
108
- ['template-test', 'test'],
109
- ['tsconfig.site.json', 'tsconfig.json']
110
- ] : [
111
- ['.editorconfig', '.editorconfig'],
112
- ['.yarnrc.yml', '.yarnrc.yml'],
113
- ['pnpm-workspace.yaml', 'pnpm-workspace.yaml'],
114
- ...commonNoJsxCopies,
115
- ...noJsxTemplateCopies,
116
- ['server', 'server'],
117
- ['scripts', 'scripts'],
118
- ['template-test', 'test']
119
- ];
120
- for (const [source, target] of copies) {
84
+ for (const [source, target] of scaffoldCopies) {
121
85
  await fileSystem.copy(path.join(packageRoot, source), path.join(destination, target));
122
86
  }
123
87
  await fileSystem.writeJSON(path.join(destination, 'package.json'), createSiteManifest());
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../scaffold/index.ts"],"names":[],"mappings":"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","sourcesContent":["/** @module scaffold */\nimport {cp, mkdir, readdir, 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/** Refuse to layer a generated project over existing user files. */\nasync function assertDestinationAvailable(destination: string) {\n try {\n const entries = await readdir(destination);\n if (entries.length) {throw new Error(`Destination directory must be empty: ${destination}`);}\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {return;}\n throw error;\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@6.0.0': true\n },\n dependenciesMeta: {\n '@parcel/watcher@2.5.1': {built: true},\n 'esbuild@0.28.2': {built: true},\n 'white-label-view@6.0.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': '5.0.0',\n 'white-label-model': '7.0.1',\n 'white-label-router': '6.0.0',\n 'white-label-view': '6.0.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 without overwriting a non-empty destination.\n *\n * This is the shared implementation behind every creation interface. Adapters\n * translate their environment into these options instead of owning templates,\n * overwrite policy, or project-generation behavior themselves.\n */\nexport async function createProject({destination, fileSystem = nodeFileSystem, jsx = true}: CreateProjectOptions) {\n await assertDestinationAvailable(destination);\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"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../scaffold/index.ts"],"names":[],"mappings":"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","sourcesContent":["/** @module scaffold */\nimport {cp, mkdir, readdir, 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}\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/** Refuse to layer a generated project over existing user files. */\nasync function assertDestinationAvailable(destination: string) {\n try {\n const entries = await readdir(destination);\n if (entries.length) {throw new Error(`Destination directory must be empty: ${destination}`);}\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {return;}\n throw error;\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 },\n dependenciesMeta: {\n '@parcel/watcher@2.5.1': {built: true},\n 'esbuild@0.28.2': {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': '5.0.0',\n 'white-label-model': '7.0.1',\n 'white-label-router': '6.1.0',\n 'white-label-view': '7.0.0'\n }\n };\n}\n\nconst scaffoldCopies = [\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\n/** Create the canonical tagged-template White Label starter without overwriting user files. */\nexport async function createProject({destination, fileSystem = nodeFileSystem}: CreateProjectOptions) {\n await assertDestinationAvailable(destination);\n for (const [source, target] of scaffoldCopies) {\n await fileSystem.copy(path.join(packageRoot, source), path.join(destination, target));\n }\n await fileSystem.writeJSON(path.join(destination, 'package.json'), createSiteManifest());\n}\n"]}
@@ -6,27 +6,87 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
6
6
  import { build as buildJavaScript } from 'esbuild';
7
7
  const execFileAsync = promisify(execFile);
8
8
  const defaultProjectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
9
+ const relativePublicPrefix = /^\/(?!\/)[A-Za-z0-9._~!$&'()*+,;=:@%/-]*$/;
10
+ function hasControlCharacters(value) {
11
+ return [...value].some(character => {
12
+ const code = character.charCodeAt(0);
13
+ return code <= 0x1f || code === 0x7f;
14
+ });
15
+ }
16
+ function normalizeVersion(value) {
17
+ if (typeof value !== 'string' || !/^[A-Za-z0-9._-]+$/.test(value) || value === '.' || value === '..') {
18
+ throw new Error('Version may contain only letters, numbers, dots, underscores, and hyphens and must name one release directory');
19
+ }
20
+ return value;
21
+ }
22
+ function normalizeProduction(value) {
23
+ if (typeof value !== 'boolean') {
24
+ throw new Error('Production must be a boolean');
25
+ }
26
+ return value;
27
+ }
28
+ function normalizeSiteUrl(value, production) {
29
+ if (typeof value !== 'string') {
30
+ throw new Error('Site URL must be an absolute HTTP(S) URL; production builds require HTTPS');
31
+ }
32
+ let url;
33
+ try {
34
+ url = new URL(value);
35
+ }
36
+ catch {
37
+ throw new Error('Site URL must be an absolute HTTP(S) URL; production builds require HTTPS');
38
+ }
39
+ if (hasControlCharacters(value) || (url.protocol !== 'http:' && url.protocol !== 'https:') ||
40
+ url.username || url.password || url.search || url.hash || (production && url.protocol !== 'https:')) {
41
+ throw new Error('Site URL must be an absolute HTTP(S) URL without credentials, query, or fragment; production builds require HTTPS');
42
+ }
43
+ return url.toString().replace(/\/$/, '');
44
+ }
45
+ function normalizePublicPrefix(value, name, production) {
46
+ if (typeof value !== 'string' || hasControlCharacters(value)) {
47
+ throw new Error(`${name} must be a root-relative or absolute HTTP(S) URL prefix`);
48
+ }
49
+ if (relativePublicPrefix.test(value)) {
50
+ return value.endsWith('/') ? value : `${value}/`;
51
+ }
52
+ let url;
53
+ try {
54
+ url = new URL(value);
55
+ }
56
+ catch {
57
+ throw new Error(`${name} must be a root-relative or absolute HTTP(S) URL prefix`);
58
+ }
59
+ if ((url.protocol !== 'http:' && url.protocol !== 'https:') || url.username || url.password ||
60
+ url.search || url.hash || (production && url.protocol !== 'https:')) {
61
+ throw new Error(`${name} must be a root-relative or absolute HTTP(S) URL prefix${production ? ' using HTTPS in production' : ''}`);
62
+ }
63
+ const normalized = url.toString();
64
+ return normalized.endsWith('/') ? normalized : `${normalized}/`;
65
+ }
66
+ function normalizeBuildConfig(config) {
67
+ const production = normalizeProduction(config.production);
68
+ return {
69
+ cdn: normalizePublicPrefix(config.cdn, 'cdn', production),
70
+ production,
71
+ siteUrl: normalizeSiteUrl(config.siteUrl, production),
72
+ version: normalizeVersion(config.version),
73
+ www: normalizePublicPrefix(config.www, 'www', production)
74
+ };
75
+ }
9
76
  /** Read deployment flags and reject unsafe or invalid deployment values. */
10
77
  export function parseArguments(argumentsList) {
11
78
  const values = Object.fromEntries(argumentsList.map((argument) => {
12
79
  const [key, ...value] = argument.replace(/^--/, '').split('=');
13
80
  return [key, value.join('=')];
14
81
  }));
15
- const version = values.version || String(Math.floor(Date.now() / 1000));
16
- if (!/^[A-Za-z0-9._-]+$/.test(version)) {
17
- throw new Error('Version may contain only letters, numbers, dots, underscores, and hyphens');
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
- return {
82
+ const production = values.production === 'true';
83
+ return normalizeBuildConfig({
24
84
  cdn: values.cdn || '/',
25
- production: values.production === 'true',
26
- siteUrl,
27
- version,
85
+ production,
86
+ siteUrl: values['site-url'] || 'http://localhost:8080',
87
+ version: values.version || String(Math.floor(Date.now() / 1000)),
28
88
  www: values.www || '/'
29
- };
89
+ });
30
90
  }
31
91
  /** Return the local Tailwind executable name for the current platform. */
32
92
  export function tailwindExecutable(platform = process.platform) {
@@ -41,15 +101,15 @@ async function filesUnder(directory) {
41
101
  }));
42
102
  return files.flat();
43
103
  }
44
- /** Render compiled TypeScript or TSX pages using global, page-specific, and deployment data. */
104
+ /** Render compiled TypeScript pages using global, page-specific, and deployment data. */
45
105
  async function renderMarkup(config, outputRoot, projectRoot) {
46
106
  const globalData = JSON.parse(await fs.readFile(path.join(projectRoot, 'app/assets/data/view/global.json'), 'utf8'));
47
107
  const appRoot = path.join(projectRoot, 'app');
48
108
  const pages = (await filesUnder(appRoot))
49
- .filter((file) => /\.tsx?$/.test(file) && !file.includes(`${path.sep}assets${path.sep}`));
109
+ .filter((file) => /\.ts$/.test(file) && !file.endsWith('.d.ts') && !file.includes(`${path.sep}assets${path.sep}`));
50
110
  await Promise.all(pages.map(async (page) => {
51
111
  const relative = path.relative(appRoot, page);
52
- const pageDataPath = path.join(projectRoot, 'app/assets/data/view', relative.replace(/\.tsx?$/, '.json'));
112
+ const pageDataPath = path.join(projectRoot, 'app/assets/data/view', relative.replace(/\.ts$/, '.json'));
53
113
  let pageData = {};
54
114
  try {
55
115
  pageData = JSON.parse(await fs.readFile(pageDataPath, 'utf8'));
@@ -59,13 +119,13 @@ async function renderMarkup(config, outputRoot, projectRoot) {
59
119
  throw error;
60
120
  }
61
121
  }
62
- const compiledPage = path.join(projectRoot, 'dist/app', relative.replace(/\.tsx?$/, '.js'));
122
+ const compiledPage = path.join(projectRoot, 'dist/app', relative.replace(/\.ts$/, '.js'));
63
123
  const module = await import(pathToFileURL(compiledPage).href);
64
124
  if (typeof module.default !== 'function') {
65
125
  throw new TypeError(`Page module ${relative} must export a default render function`);
66
126
  }
67
127
  const markup = module.default({ ...globalData, ...pageData, ...config });
68
- const destination = path.join(outputRoot, relative.replace(/\.tsx?$/, '.html'));
128
+ const destination = path.join(outputRoot, relative.replace(/\.ts$/, '.html'));
69
129
  await fs.mkdir(path.dirname(destination), { recursive: true });
70
130
  await fs.writeFile(destination, `<!DOCTYPE html>\n${String(markup)}\n`);
71
131
  }));
@@ -85,11 +145,11 @@ async function compileStyles(outputAssets, production, projectRoot) {
85
145
  await execFileAsync(tailwindExecutable(), argumentsList, { cwd: projectRoot });
86
146
  await fs.copyFile(path.join(styleRoot, 'print.css'), path.join(outputAssets, 'style/print.css'));
87
147
  }
88
- /** Bundle TypeScript entry points for browsers, excluding declaration-only files. */
148
+ /** Bundle plain TypeScript browser entry points, excluding declaration-only files. */
89
149
  async function compileScripts(outputAssets, production, projectRoot) {
90
150
  const scriptRoot = path.join(projectRoot, 'app/assets/script');
91
151
  const entries = (await fs.readdir(scriptRoot, { withFileTypes: true }))
92
- .filter((entry) => entry.isFile() && !entry.name.endsWith('.d.ts') && /\.tsx?$/.test(entry.name))
152
+ .filter((entry) => entry.isFile() && !entry.name.endsWith('.d.ts') && entry.name.endsWith('.ts'))
93
153
  .map((entry) => path.join(scriptRoot, entry.name));
94
154
  await fs.mkdir(path.join(outputAssets, 'script'), { recursive: true });
95
155
  await buildJavaScript({
@@ -97,7 +157,7 @@ async function compileScripts(outputAssets, production, projectRoot) {
97
157
  entryNames: '[name].compiled',
98
158
  entryPoints: entries,
99
159
  format: 'iife',
100
- loader: { '.ts': 'ts', '.tsx': 'tsx' },
160
+ loader: { '.ts': 'ts' },
101
161
  minify: production,
102
162
  outdir: path.join(outputAssets, 'script'),
103
163
  sourcemap: !production,
@@ -105,20 +165,29 @@ async function compileScripts(outputAssets, production, projectRoot) {
105
165
  });
106
166
  await fs.writeFile(path.join(outputAssets, 'script/global.compiled.js'), '');
107
167
  }
168
+ function escapeXml(value) {
169
+ return value
170
+ .replaceAll('&', '&amp;')
171
+ .replaceAll('<', '&lt;')
172
+ .replaceAll('>', '&gt;')
173
+ .replaceAll('"', '&quot;')
174
+ .replaceAll("'", '&apos;');
175
+ }
108
176
  /** Replace the deployment directory with compiled pages, scripts, styles, and static assets. */
109
177
  export async function build(config = parseArguments(process.argv.slice(2)), projectRoot = defaultProjectRoot) {
178
+ const safeConfig = normalizeBuildConfig(config);
110
179
  const outputRoot = path.join(projectRoot, '_deploy');
111
- const outputAssets = path.join(outputRoot, 'release', config.version, 'assets');
180
+ const outputAssets = path.join(outputRoot, 'release', safeConfig.version, 'assets');
112
181
  await fs.rm(outputRoot, { recursive: true, force: true });
113
182
  await fs.mkdir(outputAssets, { recursive: true });
114
183
  await fs.cp(path.join(projectRoot, 'app/assets/data'), path.join(outputAssets, 'data'), { recursive: true });
115
- await fs.writeFile(path.join(outputAssets, 'data/config.json'), JSON.stringify(config));
116
- await renderMarkup(config, outputRoot, projectRoot);
117
- await compileStyles(outputAssets, config.production, projectRoot);
118
- await compileScripts(outputAssets, config.production, projectRoot);
119
- const robots = `User-agent: *\nAllow: /\nSitemap: ${config.siteUrl}/sitemap.xml\n`;
184
+ await fs.writeFile(path.join(outputAssets, 'data/config.json'), JSON.stringify(safeConfig));
185
+ await renderMarkup(safeConfig, outputRoot, projectRoot);
186
+ await compileStyles(outputAssets, safeConfig.production, projectRoot);
187
+ await compileScripts(outputAssets, safeConfig.production, projectRoot);
188
+ const robots = `User-agent: *\nAllow: /\nSitemap: ${safeConfig.siteUrl}/sitemap.xml\n`;
120
189
  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`;
190
+ const sitemap = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>${escapeXml(`${safeConfig.siteUrl}/`)}</loc></url></urlset>\n`;
122
191
  await fs.writeFile(path.join(outputRoot, 'sitemap.xml'), sitemap);
123
192
  }
124
193
  if (path.resolve(process.argv[1] || '') === fileURLToPath(import.meta.url)) {