generator-white-label 8.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.
- package/CLI.md +16 -20
- package/EXISTING_APPLICATIONS.md +155 -0
- package/PACKAGE_MANAGERS.md +6 -4
- package/README.md +136 -126
- package/app/404.ts +28 -0
- package/app/README.md +45 -7
- package/{scaffold/no-jsx/app → app}/assets/script/index.ts +1 -0
- package/app/assets/script/tasks/TaskApplication.ts +11 -11
- package/app/assets/script/tasks/TaskMediator.ts +6 -11
- package/app/assets/script/tasks/TaskRouter.ts +1 -1
- package/{scaffold/no-jsx/app → app}/assets/script/tasks/TaskView.ts +1 -1
- package/app/assets/view/CodeBlock.ts +15 -0
- package/app/assets/view/examples/tasks/TaskExample.ts +50 -0
- package/app/assets/view/examples/tasks/task-state.ts +1 -1
- package/app/assets/view/layout/SiteFooter.ts +11 -0
- package/app/assets/view/layout/SiteHeader.ts +17 -0
- package/app/assets/view/page-data.ts +2 -2
- package/app/assets/view/sections/GettingStartedSection.ts +26 -0
- package/app/assets/view/sections/HeroSection.ts +15 -0
- package/app/assets/view/sections/LiveExampleSection.ts +24 -0
- package/app/assets/view/sections/PackageDocsSection.ts +69 -0
- package/app/assets/view/sections/{SourceGuideSection.tsx → SourceGuideSection.ts} +11 -16
- package/app/index.ts +50 -0
- package/cli/index.ts +6 -42
- package/dist/app/404.d.ts +1 -1
- package/dist/app/404.js +18 -2
- package/dist/app/404.js.map +1 -1
- package/dist/app/assets/script/index.d.ts +1 -6
- package/dist/app/assets/script/index.js +1 -6
- package/dist/app/assets/script/index.js.map +1 -1
- package/dist/app/assets/script/tasks/TaskApplication.js +11 -11
- package/dist/app/assets/script/tasks/TaskApplication.js.map +1 -1
- package/dist/app/assets/script/tasks/TaskMediator.d.ts +5 -10
- package/dist/app/assets/script/tasks/TaskMediator.js.map +1 -1
- package/dist/app/assets/script/tasks/TaskRouter.js +1 -1
- package/dist/app/assets/script/tasks/TaskRouter.js.map +1 -1
- package/dist/app/assets/script/tasks/TaskView.d.ts +1 -6
- package/dist/app/assets/script/tasks/TaskView.js +2 -8
- package/dist/app/assets/script/tasks/TaskView.js.map +1 -1
- package/dist/app/assets/view/CodeBlock.d.ts +3 -3
- package/dist/app/assets/view/CodeBlock.js +4 -2
- package/dist/app/assets/view/CodeBlock.js.map +1 -1
- package/dist/app/assets/view/examples/tasks/TaskExample.d.ts +2 -8
- package/dist/app/assets/view/examples/tasks/TaskExample.js +41 -9
- package/dist/app/assets/view/examples/tasks/TaskExample.js.map +1 -1
- package/dist/app/assets/view/examples/tasks/task-state.d.ts +1 -1
- package/dist/app/assets/view/examples/tasks/task-state.js.map +1 -1
- package/dist/app/assets/view/layout/SiteFooter.d.ts +2 -2
- package/dist/app/assets/view/layout/SiteFooter.js +8 -3
- package/dist/app/assets/view/layout/SiteFooter.js.map +1 -1
- package/dist/app/assets/view/layout/SiteHeader.d.ts +2 -8
- package/dist/app/assets/view/layout/SiteHeader.js +14 -9
- package/dist/app/assets/view/layout/SiteHeader.js.map +1 -1
- package/dist/app/assets/view/page-data.d.ts +2 -2
- package/dist/app/assets/view/page-data.js.map +1 -1
- package/dist/app/assets/view/sections/GettingStartedSection.d.ts +1 -1
- package/dist/app/assets/view/sections/GettingStartedSection.js +21 -6
- package/dist/app/assets/view/sections/GettingStartedSection.js.map +1 -1
- package/dist/app/assets/view/sections/HeroSection.d.ts +2 -8
- package/dist/app/assets/view/sections/HeroSection.js +12 -9
- package/dist/app/assets/view/sections/HeroSection.js.map +1 -1
- package/dist/app/assets/view/sections/LiveExampleSection.d.ts +2 -6
- package/dist/app/assets/view/sections/LiveExampleSection.js +17 -7
- package/dist/app/assets/view/sections/LiveExampleSection.js.map +1 -1
- package/dist/app/assets/view/sections/PackageDocsSection.d.ts +1 -1
- package/dist/app/assets/view/sections/PackageDocsSection.js +57 -20
- package/dist/app/assets/view/sections/PackageDocsSection.js.map +1 -1
- package/dist/app/assets/view/sections/SourceGuideSection.d.ts +2 -7
- package/dist/app/assets/view/sections/SourceGuideSection.js +20 -8
- package/dist/app/assets/view/sections/SourceGuideSection.js.map +1 -1
- package/dist/app/index.d.ts +2 -8
- package/dist/app/index.js +36 -11
- package/dist/app/index.js.map +1 -1
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/index.js +6 -37
- package/dist/cli/index.js.map +1 -1
- package/dist/scaffold/index.d.ts +2 -13
- package/dist/scaffold/index.js +36 -56
- package/dist/scaffold/index.js.map +1 -1
- package/dist/scripts/build.js +97 -28
- package/dist/scripts/build.js.map +1 -1
- package/dist/server/handler.d.ts +7 -0
- package/dist/server/handler.js +60 -0
- package/dist/server/handler.js.map +1 -0
- package/package.json +25 -24
- package/pnpm-workspace.yaml +0 -1
- package/scaffold/AGENTS.md +23 -0
- package/scaffold/index.ts +32 -60
- package/scripts/build.ts +90 -30
- package/server/handler.ts +2 -10
- package/template-test/site.test.js +3 -1
- package/tsconfig.site.json +1 -4
- package/app/404.tsx +0 -28
- package/app/assets/script/index.tsx +0 -13
- package/app/assets/script/tasks/TaskView.tsx +0 -24
- package/app/assets/view/CodeBlock.tsx +0 -17
- package/app/assets/view/examples/tasks/TaskExample.tsx +0 -63
- package/app/assets/view/layout/SiteFooter.tsx +0 -11
- package/app/assets/view/layout/SiteHeader.tsx +0 -23
- package/app/assets/view/sections/GettingStartedSection.tsx +0 -27
- package/app/assets/view/sections/HeroSection.tsx +0 -21
- package/app/assets/view/sections/LiveExampleSection.tsx +0 -29
- package/app/assets/view/sections/PackageDocsSection.tsx +0 -69
- package/app/index.tsx +0 -54
- package/scaffold/no-jsx/README.md +0 -47
- package/scaffold/no-jsx/app/404.ts +0 -10
- package/scaffold/no-jsx/app/assets/view/examples/tasks/TaskExample.ts +0 -39
- package/scaffold/no-jsx/app/index.ts +0 -58
- package/tsconfig.site.no-jsx.json +0 -24
package/CLI.md
CHANGED
|
@@ -7,7 +7,7 @@ CLI ──────> createProject() ──────> project
|
|
|
7
7
|
Node API ─┘
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
`createProject()` owns the scaffold. The CLI translates command-line input into that API.
|
|
10
|
+
`createProject()` owns the canonical tagged-template scaffold, including destination safety. The CLI translates command-line input into that API; it does not maintain a second generation path.
|
|
11
11
|
|
|
12
12
|
## Create a project
|
|
13
13
|
|
|
@@ -25,7 +25,7 @@ yarn dlx generator-white-label create my-project
|
|
|
25
25
|
pnpm dlx generator-white-label create my-project
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Project creation accepts a missing or existing **empty** directory. A non-empty destination is rejected before scaffold files are copied or written.
|
|
29
29
|
|
|
30
30
|
Then install and test the generated project with npm, Yarn, or pnpm:
|
|
31
31
|
|
|
@@ -36,23 +36,21 @@ npm install && npm test
|
|
|
36
36
|
# or: pnpm install && pnpm test
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
The generated project uses ordinary `.ts` files and White Label View's first-party tagged HTML templates:
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
```ts
|
|
42
|
+
import {html} from 'white-label-view/html';
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
white-label create my-project --jsx
|
|
47
|
-
white-label create my-project --no-jsx
|
|
48
|
-
|
|
49
|
-
npx generator-white-label create my-project --jsx
|
|
50
|
-
npx generator-white-label create my-project --no-jsx
|
|
44
|
+
export default function Page(data: {title: string}) {
|
|
45
|
+
return html`<main><h1>${data.title}</h1></main>`;
|
|
46
|
+
}
|
|
51
47
|
```
|
|
52
48
|
|
|
53
|
-
|
|
49
|
+
Dynamic text and quoted-attribute values are escaped by the tagged-template runtime. Use `attributes()` for conditional/opening-tag attributes and reserve `unsafeHTML()` for application-owned content that is already trusted or sanitized.
|
|
54
50
|
|
|
55
|
-
|
|
51
|
+
There is no generator renderer prompt and no `--jsx` or `--no-jsx` flag. If a project prefers JSX, Handlebars, Eta, EJS, Mustache, Nunjucks, Pug, or another renderer, add it as an application dependency and return its rendered output from View's `template` function. JSX remains a tested third-party option rather than a generator-owned runtime.
|
|
52
|
+
|
|
53
|
+
See [Template engines](https://whitelabeljs.org/docs/view/#template-engines) for tested integrations and trust boundaries.
|
|
56
54
|
|
|
57
55
|
Run `white-label --help` for usage. See [`PACKAGE_MANAGERS.md`](PACKAGE_MANAGERS.md) for package-manager compatibility details.
|
|
58
56
|
|
|
@@ -62,21 +60,19 @@ Run `white-label --help` for usage. See [`PACKAGE_MANAGERS.md`](PACKAGE_MANAGERS
|
|
|
62
60
|
import {createProject} from 'generator-white-label';
|
|
63
61
|
|
|
64
62
|
await createProject({
|
|
65
|
-
destination: '/absolute/path/to/my-project'
|
|
66
|
-
jsx: false
|
|
63
|
+
destination: '/absolute/path/to/my-project'
|
|
67
64
|
});
|
|
68
65
|
```
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`createProject()` is the lower-level programmatic API and does not apply the CLI's non-empty-directory guard. Applications using it directly own destination-policy decisions.
|
|
67
|
+
`createProject()` uses the same destination-safety rule as the CLI: missing and empty directories are allowed; non-empty directories are rejected before project content is written.
|
|
73
68
|
|
|
74
69
|
## Read the implementation
|
|
75
70
|
|
|
76
71
|
The source is intentionally small enough to teach the design:
|
|
77
72
|
|
|
78
|
-
- `scaffold/index.ts` — project creation
|
|
73
|
+
- `scaffold/index.ts` — project creation and destination-safety boundary
|
|
79
74
|
- `cli/index.ts` — command-line adapter
|
|
75
|
+
- `app/` — canonical TypeScript/tagged-template starter
|
|
80
76
|
- `test/` — executable contracts
|
|
81
77
|
|
|
82
78
|
Creation behavior belongs in one place. Future integrations should call `createProject()` rather than copy templates or reimplement generation logic.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Adopt White Label in an existing server-rendered application
|
|
2
|
+
|
|
3
|
+
White Label does not require a rewrite. Existing applications should install only the runtime packages a feature needs and integrate them at explicit boundaries.
|
|
4
|
+
|
|
5
|
+
The **generator is intentionally for new projects** and refuses to scaffold over a non-empty destination. If an application already exists—SFCC/SFRA, a CMS, Rails, PHP, Java, .NET, another server-rendered stack, or a long-lived frontend—do not regenerate the host application. Add White Label to one feature or DOM region at a time.
|
|
6
|
+
|
|
7
|
+
This is one of the strongest White Label use cases: modern TypeScript structure and lifecycle where it helps, while the existing platform keeps ownership of routing, business rules, initial HTML, and the rest of the page.
|
|
8
|
+
|
|
9
|
+
## When this pattern fits
|
|
10
|
+
|
|
11
|
+
Incremental adoption is a good fit when:
|
|
12
|
+
|
|
13
|
+
- the server or CMS already renders meaningful HTML;
|
|
14
|
+
- public URLs, SEO, accessibility, or no-JavaScript behavior matter;
|
|
15
|
+
- a rewrite would be disproportionate to the feature being added;
|
|
16
|
+
- teams want observable state, lifecycle, application events, or URL-backed interaction without introducing a full frontend framework;
|
|
17
|
+
- multiple teams/features need clear ownership boundaries on the same page;
|
|
18
|
+
- commerce/account/content flows must keep authoritative business logic on the server.
|
|
19
|
+
|
|
20
|
+
It is less compelling for experiences that are intentionally fully client-owned and deeply stateful across most of the viewport, such as editors, design tools, complex collaborative workspaces, or other applications where a component framework already provides valuable shared conventions.
|
|
21
|
+
|
|
22
|
+
## Install only the needed primitives
|
|
23
|
+
|
|
24
|
+
Start with the actual responsibility the feature needs:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm install white-label-view white-label-model
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add Router or Mediator only when the feature genuinely needs URL state or cross-module events:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install white-label-router white-label-mediator
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
White Label packages do not require the generator at runtime and do not require one another unless the application chooses to compose them.
|
|
37
|
+
|
|
38
|
+
A useful rule is:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
existing platform owns the page
|
|
42
|
+
+
|
|
43
|
+
White Label owns a deliberate feature boundary
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Adopt existing server-rendered markup
|
|
47
|
+
|
|
48
|
+
`white-label-view` can own lifecycle around an element that already exists instead of replacing the host application's rendering system.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import {Model} from 'white-label-model';
|
|
52
|
+
import View from 'white-label-view';
|
|
53
|
+
|
|
54
|
+
const status = document.querySelector<HTMLElement>('[data-filter-status]')!;
|
|
55
|
+
const filters = new Model({color: ''});
|
|
56
|
+
|
|
57
|
+
const statusView = new View({
|
|
58
|
+
parentElement: status.parentElement!,
|
|
59
|
+
element: status,
|
|
60
|
+
model: filters,
|
|
61
|
+
update(element, data) {
|
|
62
|
+
const state = data as {color: string};
|
|
63
|
+
element.textContent = state.color
|
|
64
|
+
? `Color filter: ${state.color}`
|
|
65
|
+
: 'No color filter selected';
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}).initialize();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
No client template is required when an attached existing element can be updated in place. The host remains responsible for the initial HTML; View owns only the adopted root and its lifecycle.
|
|
72
|
+
|
|
73
|
+
Keep ownership narrow. Unrelated server-rendered siblings, headers, forms, navigation, and page chrome should remain outside the View unless the feature genuinely owns them.
|
|
74
|
+
|
|
75
|
+
`destroy()` removes the root the View owns. If the host application expects that element to remain after enhancement teardown, choose an ownership boundary that can be removed safely rather than treating host-owned markup as disposable.
|
|
76
|
+
|
|
77
|
+
## Scope progressive routing to one enhanced region
|
|
78
|
+
|
|
79
|
+
By default, Router listens for opted-in `data-pushstate` links across the document. An embedded or incrementally adopted feature can limit that interception to its own DOM region:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import Router from 'white-label-router';
|
|
83
|
+
|
|
84
|
+
const refinementArea = document.querySelector<HTMLElement>('[data-refinements]')!;
|
|
85
|
+
const router = new Router();
|
|
86
|
+
|
|
87
|
+
router.navigationRoot = refinementArea;
|
|
88
|
+
router.routes = {
|
|
89
|
+
'/search': (_scope, location) => {
|
|
90
|
+
filters.update({color: location.data.query.prefv1 || ''});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
router.initialize();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Only eligible links whose click event reaches that navigation root are enhanced by that Router. Other page links retain the host application's normal behavior. Multiple independently owned regions can therefore coexist without every Router claiming document-wide link handling.
|
|
97
|
+
|
|
98
|
+
`navigationRoot` controls click-listener ownership. It is deliberately separate from `router.scope`, which remains the value passed to route handlers.
|
|
99
|
+
|
|
100
|
+
## Add state and events only where needed
|
|
101
|
+
|
|
102
|
+
A feature does not need the full White Label stack.
|
|
103
|
+
|
|
104
|
+
Use `white-label-model` when the feature needs observable state. Load data through the application's existing fetch/client/service layer, validate it at that boundary when appropriate, then apply it to Model. Model should not replace a backend API, persistence layer, or framework store solely for architectural symmetry.
|
|
105
|
+
|
|
106
|
+
Use `white-label-mediator` when separate modules need to exchange intent without importing one another. Direct function calls are clearer when there is already an ownership relationship. Avoid turning a mediator into an unstructured global event namespace.
|
|
107
|
+
|
|
108
|
+
## Preserve the host application's server contract
|
|
109
|
+
|
|
110
|
+
White Label should not duplicate or bypass server-owned responsibilities. Keep these in the existing platform when it already owns them:
|
|
111
|
+
|
|
112
|
+
- authentication and authorization;
|
|
113
|
+
- pricing, inventory, tax, promotions, checkout, and other authoritative business rules;
|
|
114
|
+
- canonical URLs and directly requestable public routes;
|
|
115
|
+
- initial semantic content and crawlable navigation;
|
|
116
|
+
- CSRF/session protections and trusted API boundaries;
|
|
117
|
+
- CMS/content governance and server-side personalization rules;
|
|
118
|
+
- persistence, queues, retries, and cross-process workflows.
|
|
119
|
+
|
|
120
|
+
Use White Label where a client-side state, lifecycle, event, or URL boundary adds value. Normal server navigation is still the right choice when enhancement does not materially improve the interaction.
|
|
121
|
+
|
|
122
|
+
## SEO and accessibility boundary
|
|
123
|
+
|
|
124
|
+
For public content, keep the important information in the initial response. Progressive enhancement should improve interaction rather than become a prerequisite for discovery or comprehension.
|
|
125
|
+
|
|
126
|
+
Prefer:
|
|
127
|
+
|
|
128
|
+
- real `href` values and directly requestable routes;
|
|
129
|
+
- semantic headings, forms, controls, landmarks, and status regions in initial HTML;
|
|
130
|
+
- server/static ownership of canonical URLs, titles, descriptions, robots directives, and structured data;
|
|
131
|
+
- deliberate focus handling after meaningful client-side navigation or content replacement;
|
|
132
|
+
- client behavior that preserves keyboard, context-menu, modified-click, and no-JavaScript behavior.
|
|
133
|
+
|
|
134
|
+
Do not treat source-level metadata as proof of search-engine indexing. Validate deployed behavior and indexing with actual production/search-console evidence.
|
|
135
|
+
|
|
136
|
+
## Teardown
|
|
137
|
+
|
|
138
|
+
Long-lived shells and dynamically mounted regions should release the objects they own:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
statusView.destroy();
|
|
142
|
+
router.destroy();
|
|
143
|
+
filters.destroy();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Also destroy a feature-owned Mediator when that event boundary leaves the application lifecycle.
|
|
147
|
+
|
|
148
|
+
## Commerce and SFCC example
|
|
149
|
+
|
|
150
|
+
The White Label demo repository contains a concrete Salesforce B2C Commerce / SFRA example using the same incremental boundary:
|
|
151
|
+
|
|
152
|
+
- [SFCC / SFRA progressive enhancement recipe](https://github.com/bshack/white-label-demo-site/blob/main/docs/recipes/sfcc-sfra-progressive-enhancement.md)
|
|
153
|
+
- [Incremental JavaScript for server-rendered applications](https://whitelabeljs.org/guides/incremental-javascript-for-server-rendered-apps/)
|
|
154
|
+
|
|
155
|
+
The same pattern applies to other server-rendered stacks: keep the server as the source of truth, then adopt only the client-side regions that need richer behavior.
|
package/PACKAGE_MANAGERS.md
CHANGED
|
@@ -10,9 +10,9 @@ yarn dlx generator-white-label create my-project
|
|
|
10
10
|
pnpm dlx generator-white-label create my-project
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Every invocation creates the same canonical TypeScript/tagged-template starter. There are no renderer-selection flags.
|
|
14
14
|
|
|
15
|
-
See [Template engines
|
|
15
|
+
White Label View remains template-engine agnostic. Projects that prefer JSX or a third-party renderer can install it after generation and return its rendered output from View's `template` function. See [Template engines](https://whitelabeljs.org/docs/view/#template-engines) for tested integrations and trust boundaries.
|
|
16
16
|
|
|
17
17
|
## Work with a generated project
|
|
18
18
|
|
|
@@ -26,10 +26,12 @@ pnpm install && pnpm test
|
|
|
26
26
|
|
|
27
27
|
Generated manifests require the supported Node.js runtime but do not require a specific package manager. Nested scripts use Node's `--run` support instead of invoking npm internally.
|
|
28
28
|
|
|
29
|
-
Generated projects use exact npm versions for the four first-party White Label packages.
|
|
29
|
+
Generated projects use exact npm versions for the four first-party White Label packages. Install/build lifecycle approvals are limited to dependencies that need native or generated artifacts during installation: `esbuild` and `@parcel/watcher`. npm uses pinned `allowScripts` entries, Yarn uses pinned `dependenciesMeta` build permissions, and pnpm uses `onlyBuiltDependencies`. Published White Label runtime packages include their built output and do not receive lifecycle-script approval in generated projects.
|
|
30
30
|
|
|
31
|
-
Yarn's package-age security gate is left enabled for ordinary dependencies. The generated `.yarnrc.yml` preapproves only `white-label-mediator`, `white-label-model`, `white-label-router`, and `white-label-view` so a newly published White Label release can be installed immediately without disabling Yarn's protection for the rest of the dependency graph.
|
|
31
|
+
Yarn's package-age security gate is left enabled for ordinary dependencies. The generated `.yarnrc.yml` preapproves only `white-label-mediator`, `white-label-model`, `white-label-router`, and `white-label-view` so a newly published coordinated White Label release can be installed immediately without disabling Yarn's protection for the rest of the dependency graph. Package-age preapproval does not grant lifecycle-script execution.
|
|
32
32
|
|
|
33
33
|
## Repository development
|
|
34
34
|
|
|
35
35
|
The committed `package-lock.json` remains the generator repository's canonical dependency lockfile and npm remains the maintenance/audit path used by primary CI. Compatibility CI packs the real generator artifact and verifies its API and CLI under npm, Yarn, and pnpm. Alternate lockfiles are not committed merely for compatibility testing.
|
|
36
|
+
|
|
37
|
+
Generator 10 depends on `white-label-view@7`. View 7's implementation is merged but not yet published, so coordinated CI temporarily builds and injects the exact merged View 7 revision for integration testing. The release lockfile remains registry-based; do not replace it with a Git dependency or treat the temporary packed artifact as a published dependency. Normal registry installation remains a release gate until View 7 is published.
|
package/README.md
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
# generator-white-label
|
|
2
2
|
|
|
3
|
-
`generator-white-label` is a framework-independent TypeScript project generator for small, accessible, SEO-friendly sites with progressive enhancement, composable White Label primitives, and a provider-neutral Node.js function example.
|
|
3
|
+
`generator-white-label` is a framework-independent TypeScript project generator for small, accessible, SEO-friendly, HTML-first sites with progressive enhancement, composable White Label primitives, first-party tagged HTML templates, and a provider-neutral Node.js function example.
|
|
4
4
|
|
|
5
5
|
[Documentation](https://whitelabeljs.org/docs/generator/) · [API reference](https://whitelabeljs.org/api/#generator) · [Demo site](https://whitelabeljs.org/)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
White Label does not prescribe a framework. It provides focused pieces that can be composed where useful and omitted where they are not.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
understand → see → build → verify
|
|
11
|
-
```
|
|
9
|
+
## Where the generator fits
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Use the generator when starting a **new** site or small web application and you want:
|
|
12
|
+
|
|
13
|
+
- meaningful static HTML before client JavaScript runs;
|
|
14
|
+
- progressive enhancement instead of an SPA requirement;
|
|
15
|
+
- explicit Model/View/Mediator/Router boundaries;
|
|
16
|
+
- ordinary TypeScript with readable HTML-shaped templates;
|
|
17
|
+
- accessibility and search-oriented defaults;
|
|
18
|
+
- a small codebase that remains easy for humans and coding agents to inspect;
|
|
19
|
+
- provider-neutral Web `Request`/`Response` serverless composition.
|
|
20
|
+
|
|
21
|
+
The generated project is especially well suited to public sites, documentation/content experiences, agency or multi-client work, small product/account surfaces, and teams that want application structure without handing architecture to a full framework.
|
|
22
|
+
|
|
23
|
+
### Already have an application?
|
|
24
|
+
|
|
25
|
+
Do **not** use the generator to layer a scaffold over it. The generator intentionally rejects a non-empty destination.
|
|
26
|
+
|
|
27
|
+
Existing server-rendered, CMS, commerce, Rails/PHP/Java/.NET, or long-lived frontend applications should install the individual White Label runtime packages they need and adopt them incrementally. See [`EXISTING_APPLICATIONS.md`](EXISTING_APPLICATIONS.md) and the public [incremental server-rendered application guide](https://whitelabeljs.org/guides/incremental-javascript-for-server-rendered-apps/).
|
|
14
28
|
|
|
15
29
|
## The idea
|
|
16
30
|
|
|
@@ -30,21 +44,61 @@ URL / user action
|
|
|
30
44
|
DOM
|
|
31
45
|
```
|
|
32
46
|
|
|
33
|
-
These are responsibilities, not mandatory layers. Static content can stay plain
|
|
47
|
+
These are responsibilities, not mandatory layers. Static content can stay a plain TypeScript function that returns tagged HTML. A feature that does not need routing does not need a router; a feature that only needs local state can use Model by itself.
|
|
34
48
|
|
|
35
49
|
- [`white-label-router`](https://github.com/bshack/white-label-router) turns location into application intent.
|
|
36
50
|
- [`white-label-mediator`](https://github.com/bshack/white-label-mediator) coordinates application events.
|
|
37
51
|
- [`white-label-model`](https://github.com/bshack/white-label-model) owns observable state.
|
|
38
52
|
- [`white-label-view`](https://github.com/bshack/white-label-view) owns rendering and DOM lifecycle.
|
|
39
|
-
-
|
|
53
|
+
- `white-label-view/html` provides the first-party tagged-template helpers used by the generator.
|
|
40
54
|
|
|
41
55
|
The goal is simple boundaries with explicit composition.
|
|
42
56
|
|
|
57
|
+
## Tagged HTML templates
|
|
58
|
+
|
|
59
|
+
Generated pages and View templates are ordinary `.ts` functions:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import {html} from 'white-label-view/html';
|
|
63
|
+
|
|
64
|
+
export default function Page(data: {title: string}) {
|
|
65
|
+
return html`
|
|
66
|
+
<main>
|
|
67
|
+
<h1>${data.title}</h1>
|
|
68
|
+
</main>
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Normal text and quoted-attribute interpolations are escaped. Tagged markup composes without double escaping, so small view functions can remain reusable without introducing a component runtime.
|
|
74
|
+
|
|
75
|
+
Conditional or grouped opening-tag attributes use `attributes()`:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import {attributes, html} from 'white-label-view/html';
|
|
79
|
+
|
|
80
|
+
const input = html`
|
|
81
|
+
<input ${attributes({
|
|
82
|
+
type: 'checkbox',
|
|
83
|
+
checked: complete,
|
|
84
|
+
'data-task-id': taskId
|
|
85
|
+
})}>
|
|
86
|
+
`;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`unsafeHTML()` is deliberately explicit. Use it only for application-owned content that is already trusted or sanitized, such as controlled JSON-LD after applying the application's serialization policy. It is not a sanitizer and it is not a replacement for contextual URL/CSS validation.
|
|
90
|
+
|
|
91
|
+
The tagged-template runtime rejects interpolation in ambiguous or dangerous contexts such as script/style bodies, comments, tag names, and unquoted attribute positions rather than pretending generic escaping makes those contexts safe.
|
|
92
|
+
|
|
93
|
+
White Label View remains template-engine agnostic. JSX, Handlebars, Eta, EJS, Mustache, Nunjucks, Pug, and other renderers can remain application dependencies and return their rendered output from View's `template` function. JSX is a tested third-party option rather than a White Label-owned runtime.
|
|
94
|
+
|
|
95
|
+
See [Template engines](https://whitelabeljs.org/docs/view/#template-engines) for tested integrations and trust boundaries.
|
|
96
|
+
|
|
43
97
|
## Learn from the generated application
|
|
44
98
|
|
|
45
|
-
The generated landing page demonstrates the same architecture
|
|
99
|
+
The project is also a teaching tool. The generated landing page demonstrates the same architecture its source and tests document.
|
|
46
100
|
|
|
47
|
-
|
|
101
|
+
The task example shows all four runtime packages working together:
|
|
48
102
|
|
|
49
103
|
```text
|
|
50
104
|
TaskRouter
|
|
@@ -54,33 +108,19 @@ TaskMediator
|
|
|
54
108
|
TaskModel
|
|
55
109
|
↓ observable state
|
|
56
110
|
TaskView
|
|
57
|
-
↓ render
|
|
111
|
+
↓ tagged HTML render
|
|
58
112
|
DOM
|
|
59
113
|
```
|
|
60
114
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
## Read the source
|
|
64
|
-
|
|
65
|
-
For the default JSX scaffold, a useful reading order is:
|
|
115
|
+
A useful reading order is:
|
|
66
116
|
|
|
67
|
-
1. [`app/index.
|
|
68
|
-
2. [`app/assets/view/sections/LiveExampleSection.
|
|
69
|
-
3. [`app/assets/view/examples/tasks/TaskExample.
|
|
117
|
+
1. [`app/index.ts`](app/index.ts) — page composition.
|
|
118
|
+
2. [`app/assets/view/sections/LiveExampleSection.ts`](app/assets/view/sections/LiveExampleSection.ts) — static composition around an interactive feature.
|
|
119
|
+
3. [`app/assets/view/examples/tasks/TaskExample.ts`](app/assets/view/examples/tasks/TaskExample.ts) — shared tagged-template rendering.
|
|
70
120
|
4. [`app/assets/script/tasks/TaskApplication.ts`](app/assets/script/tasks/TaskApplication.ts) — explicit dependency wiring.
|
|
71
121
|
5. `TaskRouter`, `TaskMediator`, `TaskModel`, and `TaskView` — one responsibility at a time.
|
|
72
|
-
6. [`server/handler.ts`](server/handler.ts) — provider-neutral `Request`/`Response`
|
|
73
|
-
7.
|
|
74
|
-
|
|
75
|
-
The `--no-jsx` scaffold mirrors the same structure with `.ts` files and HTML-string render functions.
|
|
76
|
-
|
|
77
|
-
Comments focus on why boundaries exist instead of narrating obvious TypeScript.
|
|
78
|
-
|
|
79
|
-
When documenting public APIs, examples use comments where they clarify intent, lifecycle, side effects, or non-obvious behavior. Public method documentation should also state the return value—including meaningful boolean/status values, chaining returns, `undefined`, promises, and relevant thrown/rejected errors.
|
|
80
|
-
|
|
81
|
-
A useful rule when extending the project is:
|
|
82
|
-
|
|
83
|
-
> Introduce an abstraction when it gives a concern a clear home, not merely to create another layer.
|
|
122
|
+
6. [`server/handler.ts`](server/handler.ts) — provider-neutral `Request`/`Response` composition.
|
|
123
|
+
7. Tests — executable contracts for browser and generated-project behavior.
|
|
84
124
|
|
|
85
125
|
## Create a project
|
|
86
126
|
|
|
@@ -88,23 +128,13 @@ Requirement:
|
|
|
88
128
|
|
|
89
129
|
- Node.js `^22.18.0` or `>=24.11.0`
|
|
90
130
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
With npm:
|
|
131
|
+
Run the package directly:
|
|
94
132
|
|
|
95
133
|
```sh
|
|
96
134
|
npx generator-white-label create my-project
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
With Yarn:
|
|
100
|
-
|
|
101
|
-
```sh
|
|
135
|
+
# or
|
|
102
136
|
yarn dlx generator-white-label create my-project
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
With pnpm:
|
|
106
|
-
|
|
107
|
-
```sh
|
|
137
|
+
# or
|
|
108
138
|
pnpm dlx generator-white-label create my-project
|
|
109
139
|
```
|
|
110
140
|
|
|
@@ -115,7 +145,7 @@ npm install --global generator-white-label
|
|
|
115
145
|
white-label create my-project
|
|
116
146
|
```
|
|
117
147
|
|
|
118
|
-
After generation, use
|
|
148
|
+
After generation, use one package manager consistently:
|
|
119
149
|
|
|
120
150
|
```sh
|
|
121
151
|
cd my-project
|
|
@@ -124,25 +154,48 @@ npm install && npm test
|
|
|
124
154
|
# or: pnpm install && pnpm test
|
|
125
155
|
```
|
|
126
156
|
|
|
127
|
-
|
|
157
|
+
There is one canonical scaffold. The CLI has no renderer prompt and no `--jsx`/`--no-jsx` mode switch.
|
|
128
158
|
|
|
129
|
-
|
|
159
|
+
The generator refuses to layer a scaffold over an existing non-empty destination. This protection is enforced by the shared `createProject()` engine for both CLI and programmatic use. An existing empty directory is allowed; a missing directory is created.
|
|
130
160
|
|
|
131
|
-
|
|
161
|
+
See [`CLI.md`](CLI.md), [`PACKAGE_MANAGERS.md`](PACKAGE_MANAGERS.md), and [`EXISTING_APPLICATIONS.md`](EXISTING_APPLICATIONS.md).
|
|
132
162
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
163
|
+
## Progressive enhancement and SEO
|
|
164
|
+
|
|
165
|
+
The generated project renders meaningful HTML during the build. Interactive task filters are real links first and are enhanced with History API navigation after initialization.
|
|
136
166
|
|
|
137
|
-
|
|
138
|
-
|
|
167
|
+
```text
|
|
168
|
+
HTML owns semantics.
|
|
169
|
+
JavaScript enhances behavior.
|
|
139
170
|
```
|
|
140
171
|
|
|
141
|
-
|
|
172
|
+
Public content should remain readable, navigable, and crawlable without executing the enhancement bundle. Generated pages include canonical URLs, page titles/descriptions, robots metadata, semantic navigation, JSON-LD, and sitemap-oriented output; deployment still needs to serve the generated files correctly.
|
|
173
|
+
|
|
174
|
+
Do not treat metadata in source code as proof of search-engine indexing. Verify deployed behavior and indexing with actual production/search-console evidence.
|
|
175
|
+
|
|
176
|
+
## A simple View click event
|
|
177
|
+
|
|
178
|
+
Use View lifecycle hooks to add and remove browser listeners with the same callback reference:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import View from 'white-label-view';
|
|
182
|
+
|
|
183
|
+
class ButtonView extends View {
|
|
184
|
+
handleClick = () => console.log('Clicked');
|
|
142
185
|
|
|
143
|
-
|
|
186
|
+
addListeners() {
|
|
187
|
+
this.element.addEventListener('click', this.handleClick);
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
144
190
|
|
|
145
|
-
|
|
191
|
+
removeListeners() {
|
|
192
|
+
this.element.removeEventListener('click', this.handleClick);
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`addListeners()` runs when the View mounts. `removeListeners()` runs before replacement or destruction, so listener ownership remains explicit.
|
|
146
199
|
|
|
147
200
|
## Programmatic API
|
|
148
201
|
|
|
@@ -151,92 +204,45 @@ Project creation has one implementation:
|
|
|
151
204
|
```js
|
|
152
205
|
import {createProject} from 'generator-white-label';
|
|
153
206
|
|
|
154
|
-
// Resolves after the scaffold files and package manifest have been written.
|
|
155
207
|
await createProject({
|
|
156
|
-
destination: new URL('./my-project', import.meta.url).pathname
|
|
157
|
-
jsx: false
|
|
208
|
+
destination: new URL('./my-project', import.meta.url).pathname
|
|
158
209
|
});
|
|
159
210
|
```
|
|
160
211
|
|
|
161
|
-
|
|
212
|
+
`createProject(options)` returns `Promise<void>`. A successful call resolves with `undefined`; file-system failures reject. If the destination exists and contains files, it rejects before copying or writing project content.
|
|
213
|
+
|
|
214
|
+
`createProject()` is the canonical creation API. The CLI and future integrations are adapters around it rather than separate generation systems. Keeping overwrite policy here prevents adapters from bypassing the same safety boundary.
|
|
215
|
+
|
|
216
|
+
## Serverless and function runtimes
|
|
162
217
|
|
|
163
|
-
|
|
218
|
+
Every generated project includes `server/handler.ts`, a provider-neutral example built around Web `Request` and `Response`. It composes Router, Mediator, Model, and `white-label-view/server` without Express or a provider SDK.
|
|
164
219
|
|
|
165
|
-
`
|
|
220
|
+
Mutable White Label instances are created inside `handleRequest()`. Serverless hosts can reuse a warm process for many requests, so mutable module-level state can leak request data or listeners. Immutable configuration can still live at module scope when its lifetime is intentionally process-wide.
|
|
166
221
|
|
|
167
|
-
|
|
222
|
+
Cloud-specific adapters should stay at the boundary: translate a provider request into a Web `Request` when needed, call `handleRequest()`, then translate the returned `Response` back only if required.
|
|
223
|
+
|
|
224
|
+
Generated-project tests exercise sequential warm invocations, concurrent requests, request-data escaping, execution without browser globals, and a browser/Web-target bundle smoke test. They also enforce serverless composition size/import regression budgets. These are regression guards, not universal latency guarantees.
|
|
225
|
+
|
|
226
|
+
The Web-target smoke test does **not** claim blanket Cloudflare/Deno/edge-provider compatibility. Published runtime packages document Node as their supported server runtime; verify a specific target before deployment.
|
|
168
227
|
|
|
169
228
|
## Source layout
|
|
170
229
|
|
|
171
230
|
| Path | Purpose |
|
|
172
231
|
| --- | --- |
|
|
173
|
-
| `scaffold/index.ts` | Canonical `createProject()` implementation |
|
|
174
|
-
| `scaffold/no-jsx/` | Plain-TypeScript template equivalents |
|
|
232
|
+
| `scaffold/index.ts` | Canonical `createProject()` implementation and destination-safety boundary |
|
|
175
233
|
| `cli/index.ts` | First-party command-line adapter |
|
|
176
|
-
| `app/*.
|
|
177
|
-
| `app/assets/view/` |
|
|
234
|
+
| `app/*.ts` | Static pages using tagged HTML templates |
|
|
235
|
+
| `app/assets/view/` | Reusable tagged-template views and sections |
|
|
178
236
|
| `app/assets/script/tasks/` | Model/View/Mediator/Router example |
|
|
179
|
-
| `server/handler.ts` | Provider-neutral serverless
|
|
180
|
-
| `scripts/build.ts` | Static rendering and asset build
|
|
237
|
+
| `server/handler.ts` | Provider-neutral serverless Request → Response example |
|
|
238
|
+
| `scripts/build.ts` | Static rendering and asset build |
|
|
181
239
|
| `test/` | Executable contracts and integration tests |
|
|
182
|
-
| `template-test/` | Generated-project validation
|
|
183
|
-
|
|
184
|
-
No React, Bootstrap, Sass, Eta, Handlebars, Mustache, Nunjucks, Pug, web fonts, cloud SDK, or browser-side template framework is required. Third-party template engines and provider adapters remain optional application dependencies.
|
|
240
|
+
| `template-test/` | Generated-project validation |
|
|
185
241
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
JSX is optional. When enabled, TypeScript uses the White Label JSX runtime:
|
|
189
|
-
|
|
190
|
-
```json
|
|
191
|
-
{
|
|
192
|
-
"compilerOptions": {
|
|
193
|
-
"jsx": "react-jsx",
|
|
194
|
-
"jsxImportSource": "white-label-view"
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
A page remains an ordinary function:
|
|
200
|
-
|
|
201
|
-
```tsx
|
|
202
|
-
export default function Page(data: Record<string, unknown>) {
|
|
203
|
-
return <main><h1>{String(data.title)}</h1></main>;
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
With `--no-jsx`, the equivalent page is ordinary TypeScript returning an HTML string instead. The application architecture and generated features remain the same. That no-JSX scaffold is also the intended starting point when Handlebars, Eta, EJS, Mustache, Nunjucks, Pug, or another renderer should remain the project's template convention.
|
|
208
|
-
|
|
209
|
-
For larger pages, compose focused views instead of growing one renderer indefinitely.
|
|
210
|
-
|
|
211
|
-
JSX expressions are escaped by default. In plain-TypeScript templates or third-party engines, applications own the engine's escaping and raw-output configuration. See [Template engines and JSX options](https://whitelabeljs.org/docs/view/#template-engines) for the tested matrix and trust boundaries.
|
|
212
|
-
|
|
213
|
-
## Progressive enhancement
|
|
214
|
-
|
|
215
|
-
The generated project renders meaningful HTML during the build. Interactive task filters are real links first and are enhanced with History API navigation after initialization.
|
|
216
|
-
|
|
217
|
-
The principle is intentional:
|
|
218
|
-
|
|
219
|
-
```text
|
|
220
|
-
HTML owns semantics.
|
|
221
|
-
JavaScript enhances behavior.
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## Serverless and function runtimes
|
|
225
|
-
|
|
226
|
-
Every generated project includes `server/handler.ts`, a provider-neutral example built around the Web `Request` and `Response` APIs. It composes Router, Mediator, Model, and `white-label-view/server` without Express or a provider SDK.
|
|
227
|
-
|
|
228
|
-
Mutable White Label instances are created inside `handleRequest()`. This is intentional: serverless hosts can reuse a warm process for many requests, so mutable module-level Model/View/Router/Mediator instances can leak request data or listeners. Immutable configuration can still be shared at module scope when its lifetime is intentionally process-wide.
|
|
229
|
-
|
|
230
|
-
Cloud-specific adapters should stay at the boundary. Translate an AWS/Vercel/Netlify/Azure/etc. request into a Web `Request` when necessary, call `handleRequest()`, and translate the returned `Response` back only if the provider requires it.
|
|
231
|
-
|
|
232
|
-
Generated-project tests exercise sequential warm invocations, concurrent requests, request-data escaping, execution without browser globals, and a browser/Web-target bundle smoke test. They also enforce a 100 kB minified serverless-composition bundle budget and a 750 ms fresh-process handler-import budget. These are regression guards, not universal latency guarantees.
|
|
233
|
-
|
|
234
|
-
The Web-target bundle smoke test catches unresolved Node built-ins, but it does **not** claim blanket Cloudflare/Deno/edge-provider compatibility. The published runtime packages still document Node as their supported server runtime; verify a specific edge provider before deployment.
|
|
242
|
+
No React, Bootstrap, Sass, Eta, Handlebars, Mustache, Nunjucks, Pug, web font, cloud SDK, or browser-side template framework is required. Optional renderers/providers remain application dependencies.
|
|
235
243
|
|
|
236
244
|
## Build and verify
|
|
237
245
|
|
|
238
|
-
The repository keeps npm as its canonical maintenance/audit path and committed lockfile. Generated projects support npm, Yarn, and pnpm.
|
|
239
|
-
|
|
240
246
|
Development build:
|
|
241
247
|
|
|
242
248
|
```sh
|
|
@@ -263,13 +269,17 @@ npm run audit
|
|
|
263
269
|
npm pack --dry-run
|
|
264
270
|
```
|
|
265
271
|
|
|
266
|
-
Tests are part of the documentation.
|
|
272
|
+
Tests are part of the documentation. Executable project source is held to 100% statement, branch, function, and line coverage. CI checks supported Node versions, packed CLI/programmatic installation, production builds, and generated-project compatibility across npm, Yarn, and pnpm.
|
|
273
|
+
|
|
274
|
+
## Coordinated runtime release
|
|
275
|
+
|
|
276
|
+
Generator 10 targets the published White Label runtime line: `white-label-mediator@5.0.0`, `white-label-model@7.0.1`, `white-label-router@6.1.0`, and `white-label-view@7.0.0`. Release and CI verification use those registry packages directly.
|
|
267
277
|
|
|
268
278
|
## White Label ecosystem
|
|
269
279
|
|
|
270
280
|
- [`white-label-model`](https://github.com/bshack/white-label-model) — observable state.
|
|
271
|
-
- [`white-label-view`](https://github.com/bshack/white-label-view) — rendering, DOM lifecycle,
|
|
281
|
+
- [`white-label-view`](https://github.com/bshack/white-label-view) — rendering, existing-DOM lifecycle, tagged HTML templates, server rendering, and a template-engine-agnostic contract.
|
|
272
282
|
- [`white-label-mediator`](https://github.com/bshack/white-label-mediator) — application events.
|
|
273
|
-
- [`white-label-router`](https://github.com/bshack/white-label-router) — routing and URL state.
|
|
283
|
+
- [`white-label-router`](https://github.com/bshack/white-label-router) — progressive routing and URL state.
|
|
274
284
|
|
|
275
285
|
The generated project imports the real packages rather than reproducing their behavior locally. That makes it both an example and an ecosystem integration test.
|