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.
- package/CLI.md +13 -17
- package/EXISTING_APPLICATIONS.md +155 -0
- package/PACKAGE_MANAGERS.md +6 -4
- package/README.md +135 -153
- package/app/404.ts +28 -0
- package/app/README.md +19 -7
- package/{scaffold/no-jsx/app → app}/assets/script/index.ts +1 -0
- 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 +4 -41
- 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/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 +4 -36
- package/dist/cli/index.js.map +1 -1
- package/dist/scaffold/index.d.ts +2 -13
- package/dist/scaffold/index.js +17 -53
- 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.js +2 -9
- package/dist/server/handler.js.map +1 -1
- package/package.json +22 -22
- package/pnpm-workspace.yaml +0 -1
- package/scaffold/AGENTS.md +13 -8
- package/scaffold/index.ts +17 -57
- package/scripts/build.ts +90 -30
- package/server/handler.ts +2 -10
- 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 -73
- 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, including destination safety. 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
|
|
|
@@ -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,13 +60,10 @@ 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
|
-
Set `jsx: true` for JSX/TSX templates or `jsx: false` for plain TypeScript and HTML strings. Use `jsx: false` as the starting point when another template engine should own rendering. Omitting `jsx` defaults to `true`.
|
|
71
|
-
|
|
72
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
|
|
@@ -77,6 +72,7 @@ The source is intentionally small enough to teach the design:
|
|
|
77
72
|
|
|
78
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.
|