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
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {html} from 'white-label-view/html';
|
|
2
|
+
import CodeBlock, {syntax} from '../CodeBlock.js';
|
|
3
|
+
|
|
4
|
+
/** Keep the shortest path from generator install to a verified local build visible. */
|
|
5
|
+
export default function GettingStartedSection() {
|
|
6
|
+
return html`<section class="section" id="start" aria-labelledby="start-title">
|
|
7
|
+
<div class="container start-grid">
|
|
8
|
+
<div class="section-intro">
|
|
9
|
+
<p class="eyebrow">Get started</p>
|
|
10
|
+
<h2 id="start-title">Create a project with the generator.</h2>
|
|
11
|
+
<p>The generated project is static-first, TypeScript-first, accessible by default, and ready for application-specific code.</p>
|
|
12
|
+
</div>
|
|
13
|
+
<div>
|
|
14
|
+
<p class="code-label">Create a project</p>
|
|
15
|
+
${CodeBlock({lines: [html`<span class="${syntax.type}">npx</span> generator-white-label`]})}
|
|
16
|
+
<p class="code-label">Then develop</p>
|
|
17
|
+
${CodeBlock({lines: [
|
|
18
|
+
html`<span class="${syntax.type}">npm</span> ci`,
|
|
19
|
+
html`<span class="${syntax.type}">npm</span> test`,
|
|
20
|
+
html`<span class="${syntax.type}">npm</span> run build -- --version=<span class="${syntax.value}">local</span>`
|
|
21
|
+
]})}
|
|
22
|
+
<p><a href="https://github.com/bshack/white-label#readme">Read the complete generator documentation</a></p>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</section>`;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {html} from 'white-label-view/html';
|
|
2
|
+
|
|
3
|
+
/** Static presentation stays a plain tagged-template function; View is only needed for lifecycle/state. */
|
|
4
|
+
export default function HeroSection() {
|
|
5
|
+
return html`<section class="hero" id="top" aria-labelledby="hero-title">
|
|
6
|
+
<div class="container hero__inner">
|
|
7
|
+
<h1 id="hero-title">Small pieces.<br>Complete applications.</h1>
|
|
8
|
+
<p class="hero__lede">Framework-independent TypeScript packages for state, rendering, coordination, routing, and project generation.</p>
|
|
9
|
+
<div class="hero__links">
|
|
10
|
+
<a class="primary-link" href="#example">See the example</a>
|
|
11
|
+
<a href="#start">Install the generator</a>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</section>`;
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {html} from 'white-label-view/html';
|
|
2
|
+
import TaskExample from '../examples/tasks/TaskExample.js';
|
|
3
|
+
import {createInitialTaskState, describeTaskStatus} from '../examples/tasks/task-state.js';
|
|
4
|
+
|
|
5
|
+
/** Render useful initial HTML with the same tagged template reused by Browser View. */
|
|
6
|
+
export default function LiveExampleSection() {
|
|
7
|
+
const state = createInitialTaskState();
|
|
8
|
+
|
|
9
|
+
return html`<section class="section" id="example" aria-labelledby="example-title">
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="section-intro">
|
|
12
|
+
<p class="eyebrow">Live example</p>
|
|
13
|
+
<h2 id="example-title">Four packages. One small application.</h2>
|
|
14
|
+
<p>Add a task, toggle its state, or change the filter. Router translates the URL into intent, Mediator coordinates actions, Model owns state, and View keeps the interface in sync.</p>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="demo" data-task-example>
|
|
17
|
+
${TaskExample({state})}
|
|
18
|
+
<p class="visually-hidden" role="status" aria-live="polite" aria-atomic="true" data-task-status>
|
|
19
|
+
${describeTaskStatus(state)}
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</section>`;
|
|
24
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {html} from 'white-label-view/html';
|
|
2
|
+
import CodeBlock, {syntax} from '../CodeBlock.js';
|
|
3
|
+
|
|
4
|
+
const repositories = {
|
|
5
|
+
mediator: 'https://github.com/bshack/white-label-mediator',
|
|
6
|
+
model: 'https://github.com/bshack/white-label-model',
|
|
7
|
+
router: 'https://github.com/bshack/white-label-router',
|
|
8
|
+
view: 'https://github.com/bshack/white-label-view'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** Package documentation stays close to the example it describes. */
|
|
12
|
+
export default function PackageDocsSection() {
|
|
13
|
+
return html`<section class="section" id="packages" aria-labelledby="packages-title">
|
|
14
|
+
<div class="container">
|
|
15
|
+
<div class="section-intro">
|
|
16
|
+
<p class="eyebrow">Documentation</p>
|
|
17
|
+
<h2 id="packages-title">The core flow stays explicit.</h2>
|
|
18
|
+
<p>Each package has one job, can be used independently, and stays easy to replace or test because application concerns are not hidden behind a framework.</p>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="architecture"><ol>
|
|
21
|
+
<li><strong>Router</strong><span>turns a URL into application intent</span></li>
|
|
22
|
+
<li><strong>Mediator</strong><span>coordinates that intent between modules</span></li>
|
|
23
|
+
<li><strong>Model</strong><span>stores and publishes application state</span></li>
|
|
24
|
+
<li><strong>View</strong><span>renders the resulting interface</span></li>
|
|
25
|
+
</ol></div>
|
|
26
|
+
<div class="docs-grid">
|
|
27
|
+
<article>
|
|
28
|
+
<p class="eyebrow">Model</p><h3>State is observable, not magical.</h3>
|
|
29
|
+
${CodeBlock({lines: [
|
|
30
|
+
html`<span class="${syntax.keyword}">const</span> model = <span class="${syntax.keyword}">new</span> <span class="${syntax.type}">Model</span>({count: 0});`,
|
|
31
|
+
html`model.addEventListener(<span class="${syntax.value}">'change'</span>, event => render(event.detail));`,
|
|
32
|
+
html`model.update({count: 1});`
|
|
33
|
+
]})}
|
|
34
|
+
<p><a href="${repositories.model}">Model documentation</a></p>
|
|
35
|
+
</article>
|
|
36
|
+
<article>
|
|
37
|
+
<p class="eyebrow">View</p><h3>Tagged HTML is the first-party template syntax.</h3>
|
|
38
|
+
${CodeBlock({lines: [
|
|
39
|
+
html`<span class="${syntax.keyword}">import</span> {html} from <span class="${syntax.value}">'white-label-view/html'</span>;`,
|
|
40
|
+
html`<span class="${syntax.keyword}">const</span> view = <span class="${syntax.keyword}">new</span> <span class="${syntax.type}">View</span>({`,
|
|
41
|
+
html` model,`,
|
|
42
|
+
html` template: state => html\`${'<p>'}\${state.count}${'</p>'}\``,
|
|
43
|
+
html`}).initialize();`
|
|
44
|
+
]})}
|
|
45
|
+
<p><a href="${repositories.view}">View documentation</a></p>
|
|
46
|
+
</article>
|
|
47
|
+
<article>
|
|
48
|
+
<p class="eyebrow">Mediator</p><h3>Modules communicate through events.</h3>
|
|
49
|
+
${CodeBlock({lines: [
|
|
50
|
+
html`mediator.addEventListener(<span class="${syntax.value}">'counter:increment'</span>, increment);`,
|
|
51
|
+
html`mediator.dispatchEvent(<span class="${syntax.keyword}">new</span> <span class="${syntax.type}">CustomEvent</span>(<span class="${syntax.value}">'counter:increment'</span>));`
|
|
52
|
+
]})}
|
|
53
|
+
<p><a href="${repositories.mediator}">Mediator documentation</a></p>
|
|
54
|
+
</article>
|
|
55
|
+
<article>
|
|
56
|
+
<p class="eyebrow">Router</p><h3>Routes describe intent.</h3>
|
|
57
|
+
${CodeBlock({lines: [
|
|
58
|
+
html`router.routes = {`,
|
|
59
|
+
html` <span class="${syntax.value}">'/'</span>: (_scope, location) => {`,
|
|
60
|
+
html` mediator.dispatchEvent(<span class="${syntax.keyword}">new</span> <span class="${syntax.type}">CustomEvent</span>(<span class="${syntax.value}">'filter:set'</span>, {detail: location.data.query.filter}));`,
|
|
61
|
+
html` }`,
|
|
62
|
+
html`};`
|
|
63
|
+
]})}
|
|
64
|
+
<p><a href="${repositories.router}">Router documentation</a></p>
|
|
65
|
+
</article>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</section>`;
|
|
69
|
+
}
|
|
@@ -1,28 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* The landing page is intentionally organized so each path answers a different
|
|
5
|
-
* question: composition, rendering, state, coordination, routing, or testing.
|
|
6
|
-
*/
|
|
1
|
+
import {html} from 'white-label-view/html';
|
|
2
|
+
|
|
3
|
+
/** Point learners at the implementation paths that demonstrate each responsibility. */
|
|
7
4
|
export default function SourceGuideSection() {
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<p className="eyebrow">Read the source</p>
|
|
5
|
+
return html`<section class="section" id="source" aria-labelledby="source-title">
|
|
6
|
+
<div class="container start-grid">
|
|
7
|
+
<div class="section-intro">
|
|
8
|
+
<p class="eyebrow">Read the source</p>
|
|
13
9
|
<h2 id="source-title">This site is part of the documentation.</h2>
|
|
14
10
|
<p>The landing page is built with the same White Label packages it teaches. Start at the page composition, then follow the imports into the interactive example.</p>
|
|
15
11
|
</div>
|
|
16
|
-
<div
|
|
12
|
+
<div class="source-paths">
|
|
17
13
|
<dl>
|
|
18
|
-
<div><dt>Page composition</dt><dd><code>app/index.
|
|
19
|
-
<div><dt>
|
|
14
|
+
<div><dt>Page composition</dt><dd><code>app/index.ts</code></dd></div>
|
|
15
|
+
<div><dt>Tagged HTML views</dt><dd><code>app/assets/view/</code></dd></div>
|
|
20
16
|
<div><dt>Task application</dt><dd><code>app/assets/script/tasks/</code></dd></div>
|
|
21
17
|
<div><dt>Integration tests</dt><dd><code>test/app.test.js</code></dd></div>
|
|
22
18
|
</dl>
|
|
23
19
|
<p><a href="https://github.com/bshack/white-label/tree/master/app">Browse the application source</a></p>
|
|
24
20
|
</div>
|
|
25
21
|
</div>
|
|
26
|
-
</section
|
|
27
|
-
);
|
|
22
|
+
</section>`;
|
|
28
23
|
}
|
package/app/index.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {html, unsafeHTML} from 'white-label-view/html';
|
|
2
|
+
import SiteFooter from './assets/view/layout/SiteFooter.js';
|
|
3
|
+
import SiteHeader from './assets/view/layout/SiteHeader.js';
|
|
4
|
+
import type {PageData} from './assets/view/page-data.js';
|
|
5
|
+
import GettingStartedSection from './assets/view/sections/GettingStartedSection.js';
|
|
6
|
+
import HeroSection from './assets/view/sections/HeroSection.js';
|
|
7
|
+
import LiveExampleSection from './assets/view/sections/LiveExampleSection.js';
|
|
8
|
+
import PackageDocsSection from './assets/view/sections/PackageDocsSection.js';
|
|
9
|
+
import SourceGuideSection from './assets/view/sections/SourceGuideSection.js';
|
|
10
|
+
|
|
11
|
+
function structuredDataScript(value: unknown) {
|
|
12
|
+
const json = JSON.stringify(value).replaceAll('<', '\\u003c');
|
|
13
|
+
return unsafeHTML(`<script type="application/ld+json">${json}</script>`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Compose the static page from small tagged-template functions. */
|
|
17
|
+
export default function IndexPage(data: Record<string, unknown>) {
|
|
18
|
+
const page = data as unknown as PageData;
|
|
19
|
+
|
|
20
|
+
return html`<html id="index" lang="en-US" dir="ltr">
|
|
21
|
+
<head>
|
|
22
|
+
<meta charset="utf-8">
|
|
23
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
24
|
+
<title>${page.title}</title>
|
|
25
|
+
<meta name="description" content="${page.meta.description}">
|
|
26
|
+
<meta name="robots" content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1">
|
|
27
|
+
<link rel="canonical" href="${page.siteUrl}/">
|
|
28
|
+
<meta property="og:title" content="${page.title}">
|
|
29
|
+
<meta property="og:description" content="${page.meta.description}">
|
|
30
|
+
<meta property="og:type" content="website">
|
|
31
|
+
<meta property="og:url" content="${page.siteUrl}/">
|
|
32
|
+
<link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/global.css" media="all">
|
|
33
|
+
<link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/print.css" media="print">
|
|
34
|
+
${structuredDataScript(page.structuredData)}
|
|
35
|
+
</head>
|
|
36
|
+
<body itemscope itemtype="https://schema.org/WebPage">
|
|
37
|
+
<a class="skip-link" href="#main">Skip to content</a>
|
|
38
|
+
${SiteHeader()}
|
|
39
|
+
<main id="main" tabindex="-1">
|
|
40
|
+
${HeroSection()}
|
|
41
|
+
${LiveExampleSection()}
|
|
42
|
+
${PackageDocsSection()}
|
|
43
|
+
${SourceGuideSection()}
|
|
44
|
+
${GettingStartedSection()}
|
|
45
|
+
</main>
|
|
46
|
+
${SiteFooter()}
|
|
47
|
+
<script src="${page.cdn}release/${page.version}/assets/script/index.compiled.js" defer></script>
|
|
48
|
+
</body>
|
|
49
|
+
</html>`;
|
|
50
|
+
}
|
package/cli/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import {realpathSync} from 'node:fs';
|
|
|
4
4
|
import {readdir} from 'node:fs/promises';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import process from 'node:process';
|
|
7
|
-
import {createInterface} from 'node:readline/promises';
|
|
8
7
|
import {fileURLToPath} from 'node:url';
|
|
9
8
|
import {createProject} from '../scaffold/index.js';
|
|
10
9
|
|
|
@@ -13,22 +12,17 @@ export interface CliDependencies {
|
|
|
13
12
|
createProject?: typeof createProject;
|
|
14
13
|
stdout?: Pick<NodeJS.WriteStream, 'write'>;
|
|
15
14
|
stderr?: Pick<NodeJS.WriteStream, 'write'>;
|
|
16
|
-
input?: NodeJS.ReadableStream;
|
|
17
|
-
output?: NodeJS.WritableStream;
|
|
18
|
-
isInteractive?: boolean;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
17
|
function usage() {
|
|
22
18
|
return [
|
|
23
19
|
'Usage:',
|
|
24
|
-
' white-label create <directory>
|
|
20
|
+
' white-label create <directory>',
|
|
25
21
|
'',
|
|
26
22
|
'Commands:',
|
|
27
|
-
' create <directory> Create a White Label project.',
|
|
23
|
+
' create <directory> Create a tagged-template White Label project.',
|
|
28
24
|
'',
|
|
29
25
|
'Options:',
|
|
30
|
-
' --jsx Generate TypeScript with JSX/TSX templates.',
|
|
31
|
-
' --no-jsx Generate plain TypeScript with HTML string templates.',
|
|
32
26
|
' -h, --help Show this help message.'
|
|
33
27
|
].join('\n');
|
|
34
28
|
}
|
|
@@ -37,18 +31,6 @@ export function formatCliError(error: unknown) {
|
|
|
37
31
|
return error instanceof Error ? error.message : String(error);
|
|
38
32
|
}
|
|
39
33
|
|
|
40
|
-
async function askForJsx(input: NodeJS.ReadableStream, output: NodeJS.WritableStream): Promise<boolean> {
|
|
41
|
-
const readline = createInterface({input, output});
|
|
42
|
-
try {
|
|
43
|
-
const answer = await readline.question(
|
|
44
|
-
'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) '
|
|
45
|
-
);
|
|
46
|
-
return !/^n(?:o)?$/i.test(answer.trim());
|
|
47
|
-
} finally {
|
|
48
|
-
readline.close();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
34
|
/** Reject an existing non-empty target so a mistyped CLI destination cannot overwrite project files. */
|
|
53
35
|
async function assertDestinationAvailable(destination: string) {
|
|
54
36
|
try {
|
|
@@ -67,8 +49,6 @@ export async function runCli(args: readonly string[], dependencies: CliDependenc
|
|
|
67
49
|
const scaffold = dependencies.createProject ?? createProject;
|
|
68
50
|
const stdout = dependencies.stdout ?? process.stdout;
|
|
69
51
|
const stderr = dependencies.stderr ?? process.stderr;
|
|
70
|
-
const input = dependencies.input ?? process.stdin;
|
|
71
|
-
const output = dependencies.output ?? process.stdout;
|
|
72
52
|
const [command, destination, ...options] = args;
|
|
73
53
|
|
|
74
54
|
if (command === '--help' || command === '-h' || args.length === 0) {
|
|
@@ -76,31 +56,14 @@ export async function runCli(args: readonly string[], dependencies: CliDependenc
|
|
|
76
56
|
return 0;
|
|
77
57
|
}
|
|
78
58
|
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
command !== 'create' ||
|
|
82
|
-
!destination ||
|
|
83
|
-
options.some(option => !allowedOptions.has(option)) ||
|
|
84
|
-
(options.includes('--jsx') && options.includes('--no-jsx'))
|
|
85
|
-
) {
|
|
59
|
+
if (command !== 'create' || !destination || options.length) {
|
|
86
60
|
stderr.write(`${usage()}\n`);
|
|
87
61
|
return 1;
|
|
88
62
|
}
|
|
89
63
|
|
|
90
|
-
let jsx: boolean;
|
|
91
|
-
if (options.includes('--jsx')) {
|
|
92
|
-
jsx = true;
|
|
93
|
-
} else if (options.includes('--no-jsx')) {
|
|
94
|
-
jsx = false;
|
|
95
|
-
} else if (dependencies.isInteractive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
96
|
-
jsx = await askForJsx(input, output);
|
|
97
|
-
} else {
|
|
98
|
-
jsx = true;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
64
|
const resolvedDestination = path.resolve(cwd(), destination);
|
|
102
65
|
await assertDestinationAvailable(resolvedDestination);
|
|
103
|
-
await scaffold({destination: resolvedDestination
|
|
66
|
+
await scaffold({destination: resolvedDestination});
|
|
104
67
|
stdout.write(`Created White Label project at ${resolvedDestination}\n`);
|
|
105
68
|
return 0;
|
|
106
69
|
}
|
package/dist/app/404.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function NotFoundPage(data: Record<string, unknown>): import("white-label-view/
|
|
1
|
+
export default function NotFoundPage(data: Record<string, unknown>): import("white-label-view/html").HTMLMarkup;
|
package/dist/app/404.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { html } from 'white-label-view/html';
|
|
2
2
|
export default function NotFoundPage(data) {
|
|
3
3
|
const page = data;
|
|
4
|
-
return
|
|
4
|
+
return html `<html lang="en-US" dir="ltr">
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="utf-8">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
<meta name="robots" content="noindex,follow">
|
|
9
|
+
<title>Page not found | White Label</title>
|
|
10
|
+
<link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/global.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<a class="skip-link" href="#main">Skip to the message</a>
|
|
14
|
+
<main id="main" tabindex="-1" class="mx-auto max-w-5xl px-4 py-24 sm:px-6 lg:px-8">
|
|
15
|
+
<h1 class="text-5xl font-extrabold tracking-tight">Page not found</h1>
|
|
16
|
+
<p class="mt-6 text-lg">The requested page does not exist.</p>
|
|
17
|
+
<p class="mt-6"><a class="font-bold underline" href="${page.www}">Return to the White Label starter</a></p>
|
|
18
|
+
</main>
|
|
19
|
+
</body>
|
|
20
|
+
</html>`;
|
|
5
21
|
}
|
|
6
22
|
//# sourceMappingURL=404.js.map
|
package/dist/app/404.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"404.js","sourceRoot":"","sources":["../../app/404.
|
|
1
|
+
{"version":3,"file":"404.js","sourceRoot":"","sources":["../../app/404.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,uBAAuB,CAAC;AAQ3C,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,IAA6B;IAC9D,MAAM,IAAI,GAAG,IAA2B,CAAC;IACzC,OAAO,IAAI,CAAA;;;;;;+CAMgC,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO;;;;;;;2EAOH,IAAI,CAAC,GAAG;;;gBAGnE,CAAC;AACjB,CAAC","sourcesContent":["import {html} from 'white-label-view/html';\n\ninterface PageData {\n cdn: string;\n version: string;\n www: string;\n}\n\nexport default function NotFoundPage(data: Record<string, unknown>) {\n const page = data as unknown as PageData;\n return html`<html lang=\"en-US\" dir=\"ltr\">\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"robots\" content=\"noindex,follow\">\n <title>Page not found | White Label</title>\n <link rel=\"stylesheet\" href=\"${page.cdn}release/${page.version}/assets/style/global.css\">\n </head>\n <body>\n <a class=\"skip-link\" href=\"#main\">Skip to the message</a>\n <main id=\"main\" tabindex=\"-1\" class=\"mx-auto max-w-5xl px-4 py-24 sm:px-6 lg:px-8\">\n <h1 class=\"text-5xl font-extrabold tracking-tight\">Page not found</h1>\n <p class=\"mt-6 text-lg\">The requested page does not exist.</p>\n <p class=\"mt-6\"><a class=\"font-bold underline\" href=\"${page.www}\">Return to the White Label starter</a></p>\n </main>\n </body>\n </html>`;\n}\n"]}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser entry points should stay small.
|
|
3
|
-
*
|
|
4
|
-
* The task application owns its own composition and lifecycle, so the entry
|
|
5
|
-
* module has one obvious responsibility: start the feature for this document.
|
|
6
|
-
*/
|
|
1
|
+
/** Start the feature composition for this document. */
|
|
7
2
|
export { initializeTaskApplication } from './tasks/TaskApplication.js';
|
|
8
3
|
export { normalizeTaskFilter } from './tasks/TaskRouter.js';
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/** @module app/assets/script/index */
|
|
2
2
|
import { initializeTaskApplication } from './tasks/TaskApplication.js';
|
|
3
|
-
/**
|
|
4
|
-
* Browser entry points should stay small.
|
|
5
|
-
*
|
|
6
|
-
* The task application owns its own composition and lifecycle, so the entry
|
|
7
|
-
* module has one obvious responsibility: start the feature for this document.
|
|
8
|
-
*/
|
|
3
|
+
/** Start the feature composition for this document. */
|
|
9
4
|
export { initializeTaskApplication } from './tasks/TaskApplication.js';
|
|
10
5
|
export { normalizeTaskFilter } from './tasks/TaskRouter.js';
|
|
11
6
|
initializeTaskApplication(document);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../app/assets/script/index.
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../app/assets/script/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AAErE,uDAAuD;AACvD,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAE1D,yBAAyB,CAAC,QAAQ,CAAC,CAAC","sourcesContent":["/** @module app/assets/script/index */\nimport {initializeTaskApplication} from './tasks/TaskApplication.js';\n\n/** Start the feature composition for this document. */\nexport {initializeTaskApplication} from './tasks/TaskApplication.js';\nexport {normalizeTaskFilter} from './tasks/TaskRouter.js';\n\ninitializeTaskApplication(document);\n"]}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import View from 'white-label-view';
|
|
2
2
|
import type TaskModel from './TaskModel.js';
|
|
3
|
-
/**
|
|
4
|
-
* View owns rendering, but not application decisions.
|
|
5
|
-
*
|
|
6
|
-
* Passing the already-rendered task root lets View adopt useful static HTML
|
|
7
|
-
* instead of replacing the document merely because JavaScript has started.
|
|
8
|
-
*/
|
|
3
|
+
/** Adopt the build-time task root and reuse the same tagged template for model updates. */
|
|
9
4
|
export declare function createTaskView(parentElement: HTMLElement, model: TaskModel): View;
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx } from "white-label-view/jsx-runtime";
|
|
2
1
|
import View from 'white-label-view';
|
|
3
2
|
import TaskExample from '../../view/examples/tasks/TaskExample.js';
|
|
4
|
-
/**
|
|
5
|
-
* View owns rendering, but not application decisions.
|
|
6
|
-
*
|
|
7
|
-
* Passing the already-rendered task root lets View adopt useful static HTML
|
|
8
|
-
* instead of replacing the document merely because JavaScript has started.
|
|
9
|
-
*/
|
|
3
|
+
/** Adopt the build-time task root and reuse the same tagged template for model updates. */
|
|
10
4
|
export function createTaskView(parentElement, model) {
|
|
11
5
|
const element = parentElement.querySelector('[data-task-app]');
|
|
12
6
|
if (!element) {
|
|
@@ -17,7 +11,7 @@ export function createTaskView(parentElement, model) {
|
|
|
17
11
|
model,
|
|
18
12
|
parentElement,
|
|
19
13
|
template(data) {
|
|
20
|
-
return
|
|
14
|
+
return TaskExample({ state: data });
|
|
21
15
|
}
|
|
22
16
|
}).initialize();
|
|
23
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskView.js","sourceRoot":"","sources":["../../../../../app/assets/script/tasks/TaskView.
|
|
1
|
+
{"version":3,"file":"TaskView.js","sourceRoot":"","sources":["../../../../../app/assets/script/tasks/TaskView.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,kBAAkB,CAAC;AACpC,OAAO,WAAW,MAAM,0CAA0C,CAAC;AAInE,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,aAA0B,EAAE,KAAgB;IACvE,MAAM,OAAO,GAAG,aAAa,CAAC,aAAa,CAAc,iBAAiB,CAAC,CAAC;IAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;QAAA,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IAAA,CAAC;IAEtF,OAAO,IAAI,IAAI,CAAC;QACZ,OAAO;QACP,KAAK;QACL,aAAa;QACb,QAAQ,CAAC,IAAI;YACT,OAAO,WAAW,CAAC,EAAC,KAAK,EAAE,IAAiB,EAAC,CAAC,CAAC;QACnD,CAAC;KACJ,CAAC,CAAC,UAAU,EAAE,CAAC;AACpB,CAAC","sourcesContent":["import View from 'white-label-view';\nimport TaskExample from '../../view/examples/tasks/TaskExample.js';\nimport type {TaskState} from '../../view/examples/tasks/task-state.js';\nimport type TaskModel from './TaskModel.js';\n\n/** Adopt the build-time task root and reuse the same tagged template for model updates. */\nexport function createTaskView(parentElement: HTMLElement, model: TaskModel): View {\n const element = parentElement.querySelector<HTMLElement>('[data-task-app]');\n if (!element) {throw new TypeError('Task example requires its static task-app root');}\n\n return new View({\n element,\n model,\n parentElement,\n template(data) {\n return TaskExample({state: data as TaskState});\n }\n }).initialize();\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type HTMLMarkup } from 'white-label-view/html';
|
|
2
2
|
export declare const syntax: {
|
|
3
3
|
keyword: string;
|
|
4
4
|
type: string;
|
|
@@ -7,5 +7,5 @@ export declare const syntax: {
|
|
|
7
7
|
};
|
|
8
8
|
/** Render a readable code example with stable line numbers and caller-supplied syntax spans. */
|
|
9
9
|
export default function CodeBlock({ lines }: {
|
|
10
|
-
lines:
|
|
11
|
-
}):
|
|
10
|
+
lines: HTMLMarkup[];
|
|
11
|
+
}): HTMLMarkup;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { html } from 'white-label-view/html';
|
|
2
2
|
export const syntax = {
|
|
3
3
|
keyword: 'code-syntax-keyword',
|
|
4
4
|
type: 'code-syntax-type',
|
|
@@ -7,6 +7,8 @@ export const syntax = {
|
|
|
7
7
|
};
|
|
8
8
|
/** Render a readable code example with stable line numbers and caller-supplied syntax spans. */
|
|
9
9
|
export default function CodeBlock({ lines }) {
|
|
10
|
-
return
|
|
10
|
+
return html `<pre class="code-block"><code>${lines.map((line, index) => html `<span class="code-block__line">
|
|
11
|
+
<span aria-hidden="true" class="code-block__number">${index + 1}</span><span>${line}</span>
|
|
12
|
+
</span>`)}</code></pre>`;
|
|
11
13
|
}
|
|
12
14
|
//# sourceMappingURL=CodeBlock.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.js","sourceRoot":"","sources":["../../../../app/assets/view/CodeBlock.
|
|
1
|
+
{"version":3,"file":"CodeBlock.js","sourceRoot":"","sources":["../../../../app/assets/view/CodeBlock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAkB,MAAM,uBAAuB,CAAC;AAE5D,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,OAAO,EAAE,qBAAqB;IAC9B,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,mBAAmB;IAC1B,KAAK,EAAE,mBAAmB;CAC7B,CAAC;AAEF,gGAAgG;AAChG,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAC,KAAK,EAAwB;IAC5D,OAAO,IAAI,CAAA,iCAAiC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAA;sEACT,KAAK,GAAG,CAAC,gBAAgB,IAAI;oBAC/E,CAAC,eAAe,CAAC;AACrC,CAAC","sourcesContent":["import {html, type HTMLMarkup} from 'white-label-view/html';\n\nexport const syntax = {\n keyword: 'code-syntax-keyword',\n type: 'code-syntax-type',\n value: 'code-syntax-value',\n muted: 'code-syntax-muted'\n};\n\n/** Render a readable code example with stable line numbers and caller-supplied syntax spans. */\nexport default function CodeBlock({lines}: {lines: HTMLMarkup[]}) {\n return html`<pre class=\"code-block\"><code>${lines.map((line, index) => html`<span class=\"code-block__line\">\n <span aria-hidden=\"true\" class=\"code-block__number\">${index + 1}</span><span>${line}</span>\n </span>`)}</code></pre>`;\n}\n"]}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { type TaskState } from './task-state.js';
|
|
2
|
-
/**
|
|
3
|
-
* Render the task example from state only.
|
|
4
|
-
*
|
|
5
|
-
* The same JSX function is used twice: once while generating the initial HTML
|
|
6
|
-
* and again by white-label-view in the browser. Sharing one renderer keeps the
|
|
7
|
-
* static and interactive versions of the example from drifting apart.
|
|
8
|
-
*/
|
|
2
|
+
/** Render the task example from state for both build-time HTML and browser updates. */
|
|
9
3
|
export default function TaskExample({ state }: {
|
|
10
4
|
state: TaskState;
|
|
11
|
-
}): import("white-label-view/
|
|
5
|
+
}): import("white-label-view/html").HTMLMarkup;
|
|
@@ -1,16 +1,48 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { attributes, html } from 'white-label-view/html';
|
|
2
2
|
import { getVisibleTasks } from './task-state.js';
|
|
3
3
|
const filters = ['all', 'active', 'completed'];
|
|
4
|
-
/**
|
|
5
|
-
* Render the task example from state only.
|
|
6
|
-
*
|
|
7
|
-
* The same JSX function is used twice: once while generating the initial HTML
|
|
8
|
-
* and again by white-label-view in the browser. Sharing one renderer keeps the
|
|
9
|
-
* static and interactive versions of the example from drifting apart.
|
|
10
|
-
*/
|
|
4
|
+
/** Render the task example from state for both build-time HTML and browser updates. */
|
|
11
5
|
export default function TaskExample({ state }) {
|
|
12
6
|
const visibleTasks = getVisibleTasks(state);
|
|
13
7
|
const completed = state.tasks.filter(task => task.complete).length;
|
|
14
|
-
return
|
|
8
|
+
return html `<section class="task-app" data-task-app aria-label="Interactive task example">
|
|
9
|
+
<form class="task-form" data-task-form action="#example" method="get">
|
|
10
|
+
<label for="task-title">New task</label>
|
|
11
|
+
<div class="task-form__controls">
|
|
12
|
+
<input id="task-title" name="task" type="text" autocomplete="off" required>
|
|
13
|
+
<button type="submit">Add task</button>
|
|
14
|
+
</div>
|
|
15
|
+
</form>
|
|
16
|
+
|
|
17
|
+
<nav class="task-filters" aria-label="Filter tasks">
|
|
18
|
+
${filters.map(filter => html `<a ${attributes({
|
|
19
|
+
href: `/?tasks=${filter}#example`,
|
|
20
|
+
'data-task-filter': filter,
|
|
21
|
+
'data-pushstate': true,
|
|
22
|
+
'aria-current': state.filter === filter ? 'page' : 'false'
|
|
23
|
+
})}>${filter.charAt(0).toUpperCase()}${filter.slice(1)}</a>`)}
|
|
24
|
+
</nav>
|
|
25
|
+
|
|
26
|
+
<ul class="task-list">
|
|
27
|
+
${visibleTasks.map(task => html `<li>
|
|
28
|
+
<label>
|
|
29
|
+
<input ${attributes({
|
|
30
|
+
type: 'checkbox',
|
|
31
|
+
checked: task.complete,
|
|
32
|
+
'data-task-toggle': true,
|
|
33
|
+
'data-task-id': task.id
|
|
34
|
+
})}>
|
|
35
|
+
<span>${task.title}</span>
|
|
36
|
+
</label>
|
|
37
|
+
</li>`)}
|
|
38
|
+
</ul>
|
|
39
|
+
|
|
40
|
+
<dl class="demo__trace">
|
|
41
|
+
<div><dt>Router</dt><dd>/?tasks=${state.filter}</dd></div>
|
|
42
|
+
<div><dt>Mediator</dt><dd>task:* application events</dd></div>
|
|
43
|
+
<div><dt>Model</dt><dd>${state.tasks.length} tasks · ${completed} complete</dd></div>
|
|
44
|
+
<div><dt>View</dt><dd>${visibleTasks.length} tasks rendered</dd></div>
|
|
45
|
+
</dl>
|
|
46
|
+
</section>`;
|
|
15
47
|
}
|
|
16
48
|
//# sourceMappingURL=TaskExample.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskExample.js","sourceRoot":"","sources":["../../../../../../app/assets/view/examples/tasks/TaskExample.
|
|
1
|
+
{"version":3,"file":"TaskExample.js","sourceRoot":"","sources":["../../../../../../app/assets/view/examples/tasks/TaskExample.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,IAAI,EAAC,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAC,eAAe,EAAkC,MAAM,iBAAiB,CAAC;AAEjF,MAAM,OAAO,GAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAE7D,uFAAuF;AACvF,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAC,KAAK,EAAqB;IAC3D,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;IAEnE,OAAO,IAAI,CAAA;;;;;;;;;;kBAUG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAA,MAAM,UAAU,CAAC;QACzC,IAAI,EAAE,WAAW,MAAM,UAAU;QACjC,kBAAkB,EAAE,MAAM;QAC1B,gBAAgB,EAAE,IAAI;QACtB,cAAc,EAAE,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;KAC7D,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;;;;kBAI3D,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA;;qCAEV,UAAU,CAAC;QAChB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,IAAI,CAAC,QAAQ;QACtB,kBAAkB,EAAE,IAAI;QACxB,cAAc,EAAE,IAAI,CAAC,EAAE;KAC1B,CAAC;oCACM,IAAI,CAAC,KAAK;;0BAEpB,CAAC;;;;kDAIuB,KAAK,CAAC,MAAM;;yCAErB,KAAK,CAAC,KAAK,CAAC,MAAM,YAAY,SAAS;wCACxC,YAAY,CAAC,MAAM;;mBAExC,CAAC;AACpB,CAAC","sourcesContent":["import {attributes, html} from 'white-label-view/html';\nimport {getVisibleTasks, type TaskFilter, type TaskState} from './task-state.js';\n\nconst filters: TaskFilter[] = ['all', 'active', 'completed'];\n\n/** Render the task example from state for both build-time HTML and browser updates. */\nexport default function TaskExample({state}: {state: TaskState}) {\n const visibleTasks = getVisibleTasks(state);\n const completed = state.tasks.filter(task => task.complete).length;\n\n return html`<section class=\"task-app\" data-task-app aria-label=\"Interactive task example\">\n <form class=\"task-form\" data-task-form action=\"#example\" method=\"get\">\n <label for=\"task-title\">New task</label>\n <div class=\"task-form__controls\">\n <input id=\"task-title\" name=\"task\" type=\"text\" autocomplete=\"off\" required>\n <button type=\"submit\">Add task</button>\n </div>\n </form>\n\n <nav class=\"task-filters\" aria-label=\"Filter tasks\">\n ${filters.map(filter => html`<a ${attributes({\n href: `/?tasks=${filter}#example`,\n 'data-task-filter': filter,\n 'data-pushstate': true,\n 'aria-current': state.filter === filter ? 'page' : 'false'\n })}>${filter.charAt(0).toUpperCase()}${filter.slice(1)}</a>`)}\n </nav>\n\n <ul class=\"task-list\">\n ${visibleTasks.map(task => html`<li>\n <label>\n <input ${attributes({\n type: 'checkbox',\n checked: task.complete,\n 'data-task-toggle': true,\n 'data-task-id': task.id\n })}>\n <span>${task.title}</span>\n </label>\n </li>`)}\n </ul>\n\n <dl class=\"demo__trace\">\n <div><dt>Router</dt><dd>/?tasks=${state.filter}</dd></div>\n <div><dt>Mediator</dt><dd>task:* application events</dd></div>\n <div><dt>Model</dt><dd>${state.tasks.length} tasks · ${completed} complete</dd></div>\n <div><dt>View</dt><dd>${visibleTasks.length} tasks rendered</dd></div>\n </dl>\n </section>`;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Shared state shape used by build-time
|
|
1
|
+
/** Shared state shape used by build-time tagged HTML and the browser application. */
|
|
2
2
|
export type TaskFilter = 'all' | 'active' | 'completed';
|
|
3
3
|
export interface TaskItem {
|
|
4
4
|
complete: boolean;
|