pattern-dump 0.1.0-alpha.3 → 0.2.0-alpha.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/CHANGELOG.md +120 -0
- package/LICENSE +16 -3
- package/README.md +2 -2
- package/THIRD-PARTY-NOTICES.md +130 -0
- package/dist/chunks/chunk-ESMZAOP2.js +283 -0
- package/dist/dev-ui/main.js +215 -77
- package/dist/index.js +1 -1
- package/dist/pd.js +5887 -1003
- package/dist/types/config/index.d.ts +98 -5
- package/dist/types/config/validate.d.ts +12 -0
- package/dist/types/contracts/dev-protocol.d.ts +106 -4
- package/dist/types/contracts/iframe-fit.d.ts +33 -0
- package/dist/types/contracts/index.d.ts +36 -0
- package/package.json +10 -3
- package/templates/base/README.md +1 -0
- package/templates/base/_gitignore +1 -0
- package/templates/base/pattern-dump.config.ts +1 -0
- package/templates/base/src/styles/base/document.scss +5 -1
- package/templates/storefront/.github/workflows/pages.yml +53 -0
- package/templates/storefront/assets/icon.svg +2 -0
- package/templates/storefront/pages/about.md +15 -0
- package/templates/storefront/pages/index.md +24 -0
- package/templates/storefront/src/patterns/components/note/note.hbs +4 -0
- package/templates/storefront/src/patterns/components/note/note.json +4 -0
- package/templates/storefront/src/patterns/components/note/note.md +11 -0
- package/templates/storefront/src/patterns/components/note/note.schema.json +13 -0
- package/templates/storefront/src/patterns/components/note/note~warning.json +4 -0
- package/templates/storefront/src/patterns/layouts/page/page.hbs +8 -0
- package/templates/storefront/src/patterns/layouts/page/page.json +8 -0
- package/templates/storefront/src/patterns/layouts/page/page.md +6 -0
- package/templates/storefront/src/patterns/layouts/page/page.schema.json +19 -0
- package/templates/storefront/src/patterns/partials/base/base.hbs +17 -0
- package/templates/storefront/src/patterns/partials/base/base.json +4 -0
- package/templates/storefront/src/patterns/partials/base/base.md +6 -0
- package/templates/storefront/src/patterns/partials/base/base.schema.json +18 -0
- package/templates/storefront/src/patterns/partials/head/head.hbs +1 -0
- package/templates/storefront/src/patterns/partials/head/head.md +6 -0
- package/templates/storefront/src/styles/components/note.scss +8 -0
- package/templates/storefront/src/styles/layouts/page.scss +6 -0
- package/templates/storefront/src/styles/partials/base.scss +13 -0
- package/templates/storefront-fills/config.txt +14 -0
- package/templates/storefront-fills/gitignore.txt +2 -0
- package/templates/storefront-fills/readme.md +60 -0
- package/dist/chunks/chunk-3WZHFYHZ.js +0 -160
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ShellBrandConfig, SwatchSlots, ThemeSwitch } from "../contracts/index.js";
|
|
2
2
|
export type { ShellBrandConfig, SwatchSlots, ThemeSwitch };
|
|
3
|
-
export { ConfigError, configProblems, validateConfig } from "./validate.js";
|
|
3
|
+
export { ConfigError, configProblems, sameHostWorkbenchPath, suggest, validateConfig } from "./validate.js";
|
|
4
4
|
export interface AssetsConfig {
|
|
5
5
|
/** Where assets live. Only 'central' (the default: one assets dir, served at /assets/) is supported
|
|
6
6
|
* so far; 'component-relative' is reserved, and the config check rejects it as not supported yet. */
|
|
@@ -37,9 +37,10 @@ export interface StylesConfig {
|
|
|
37
37
|
entry?: string | string[];
|
|
38
38
|
}
|
|
39
39
|
export interface ThemesConfig {
|
|
40
|
-
/** Directory of theme files (custom-property value-sets).
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
/** Directory of the project's own theme files (custom-property value-sets). Leave it out to use
|
|
41
|
+
* only the themes pattern packages provide, with `default` naming one of them. */
|
|
42
|
+
dir?: string;
|
|
43
|
+
/** Default theme name applied via data-theme on the preview body: a project theme or a package's. */
|
|
43
44
|
default: string;
|
|
44
45
|
/** Switch wiring: attribute toggle (default) or per-theme stylesheet swap. */
|
|
45
46
|
switch?: ThemeSwitch;
|
|
@@ -66,10 +67,17 @@ export interface ShellConfig {
|
|
|
66
67
|
* invoking `cwd`. This holds even when the file is supplied via `--config` from outside the
|
|
67
68
|
* root — which is what lets PD open a read-only project it does not own.
|
|
68
69
|
*/
|
|
69
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* A pattern package named in the config: the npm name, or the name plus options.
|
|
72
|
+
* - `exclude`: extra globs of the package's templates to hide.
|
|
73
|
+
* - `styles: false`: leave the package's stylesheets out (its templates, data, themes, scripts and
|
|
74
|
+
* assets are still used), e.g. when the project compiles that CSS itself. A package reached only
|
|
75
|
+
* as another package's peer can be named here just to carry this option.
|
|
76
|
+
*/
|
|
70
77
|
export type PackageReference = string | {
|
|
71
78
|
name: string;
|
|
72
79
|
exclude?: string[];
|
|
80
|
+
styles?: boolean;
|
|
73
81
|
};
|
|
74
82
|
export interface PatternDumpConfig {
|
|
75
83
|
sourceDir: string;
|
|
@@ -148,5 +156,90 @@ export interface PatternDumpConfig {
|
|
|
148
156
|
basePath?: string;
|
|
149
157
|
audit?: boolean;
|
|
150
158
|
};
|
|
159
|
+
/** How responsive image variants are encoded (WebP). Defaults: quality 75, effort 4. A site can override
|
|
160
|
+
* either in `storefront.images`. */
|
|
161
|
+
images?: ImageSettings;
|
|
162
|
+
/** The storefront: a website of real HTML pages, written from markdown, beside the workbench. */
|
|
163
|
+
storefront?: StorefrontConfig;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The storefront. `pd dev` serves it on its own host (`live.localhost` unless `host` says otherwise),
|
|
167
|
+
* the workbench on `pd.localhost`, both from `/`. `pd build` writes it to its own folder, apart from
|
|
168
|
+
* the workbench snapshot. A page renders with the layout `layouts/<layout>` from the project's patterns.
|
|
169
|
+
*/
|
|
170
|
+
/** How responsive image variants are encoded. */
|
|
171
|
+
export interface ImageSettings {
|
|
172
|
+
/** WebP quality, a whole number from 1 to 100. Default: 75. */
|
|
173
|
+
quality?: number;
|
|
174
|
+
/** WebP effort, 0 (fastest) to 6 (smallest files). Default: 4. */
|
|
175
|
+
effort?: number;
|
|
151
176
|
}
|
|
177
|
+
export interface StorefrontConfig {
|
|
178
|
+
/** Folder of the pages, relative to the root. `index.md` is `/`, `guides/setup.md` is `/guides/setup/`.
|
|
179
|
+
* `pagesDir`, `patternsDir` and `outDir` are three separate folders: none is another, and none is inside
|
|
180
|
+
* another. */
|
|
181
|
+
pagesDir: string;
|
|
182
|
+
/** Where `pd build` writes the storefront, relative to the root. Its own folder, apart from the workbench
|
|
183
|
+
* snapshot, and separate from `pagesDir` and `patternsDir` (see `pagesDir`). */
|
|
184
|
+
outDir: string;
|
|
185
|
+
/** The host name `pd dev` serves the storefront on. Default: `live.localhost`. */
|
|
186
|
+
host?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Where the built workbench will be hosted: an absolute http(s) URL with no query or fragment, e.g.
|
|
189
|
+
* `https://workbench.example.com/`, or, for a workbench on the storefront's own host, a path such as
|
|
190
|
+
* `/workbench/` (then built with that base path, and its previews kept out of the storefront's
|
|
191
|
+
* `robots.txt`; an absolute URL on `url`'s origin counts as the same host too). Loaded with exactly
|
|
192
|
+
* one trailing slash, whether written with one or not; links from storefront pages to the workbench are
|
|
193
|
+
* this value followed by a path. Used by `pd build` only: in `pd dev` those links always go to
|
|
194
|
+
* `http://pd.localhost:<port>`, the workbench of the same process.
|
|
195
|
+
*/
|
|
196
|
+
workbenchUrl?: string;
|
|
197
|
+
/**
|
|
198
|
+
* The site's address, such as `https://example.com/`: an absolute http(s) URL with no path (the storefront
|
|
199
|
+
* is served from the root of its host), no query or fragment; loaded as its origin followed by `/`. With it,
|
|
200
|
+
* pages get a canonical URL, Open Graph and JSON-LD, and `pd build` writes a sitemap; without it they are
|
|
201
|
+
* left out, and `pd build` says so once.
|
|
202
|
+
*/
|
|
203
|
+
url?: string;
|
|
204
|
+
/** The language of every page, as `<html lang>` takes it (a BCP 47 tag such as `de-AT`). Default: `en`. */
|
|
205
|
+
lang?: string;
|
|
206
|
+
/**
|
|
207
|
+
* `false` keeps the whole site out of search engines (a staging site): every page gets `noindex`,
|
|
208
|
+
* `robots.txt` disallows everything, and there is no sitemap. Default: `true`.
|
|
209
|
+
*/
|
|
210
|
+
index?: boolean;
|
|
211
|
+
/** The image a page shows in link previews when it names none of its own: a raster image in the assets
|
|
212
|
+
* (PNG, JPEG, GIF or WebP), such as `/assets/images/share.png`. */
|
|
213
|
+
image?: string;
|
|
214
|
+
/** This site's image encoding, over the project's `images`. */
|
|
215
|
+
images?: ImageSettings;
|
|
216
|
+
/**
|
|
217
|
+
* Values every page of the site shares, such as its title or the line in its footer. Every layout gets
|
|
218
|
+
* them as `site`; a page cannot set them. Which keys there are, and their types, is what the `site`
|
|
219
|
+
* property of `partials/base`'s schema declares: they are checked against it, not inferred.
|
|
220
|
+
*
|
|
221
|
+
* One key is Pattern Dump's: `navigations`, named lists of links (a footer's, a sidebar's), each entry a
|
|
222
|
+
* page, an external link or a group, nested to any depth. Their shape is fixed (StorefrontNavEntry) and
|
|
223
|
+
* checked by Pattern Dump; the skin's schema only lists the names it uses.
|
|
224
|
+
*/
|
|
225
|
+
site?: Record<string, unknown> & {
|
|
226
|
+
navigations?: Record<string, StorefrontNavEntry[]>;
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* One entry of a declared navigation (`storefront.site.navigations.<name>`):
|
|
231
|
+
* - a page, by its path in the pages folder (`"guides/setup.md"`), labelled with its title unless `label`;
|
|
232
|
+
* - an external link: `href` is `https:`, `http:` or `mailto:`, or a path from the site's root (`/feed.xml`);
|
|
233
|
+
* - a group: a `label` and its `children`, entries of any of the three kinds.
|
|
234
|
+
*/
|
|
235
|
+
export type StorefrontNavEntry = {
|
|
236
|
+
page: string;
|
|
237
|
+
label?: string;
|
|
238
|
+
} | {
|
|
239
|
+
href: string;
|
|
240
|
+
label: string;
|
|
241
|
+
} | {
|
|
242
|
+
label: string;
|
|
243
|
+
children: StorefrontNavEntry[];
|
|
244
|
+
};
|
|
152
245
|
export declare function defineConfig(config: PatternDumpConfig): PatternDumpConfig;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { PatternDumpConfig } from "./index.js";
|
|
2
|
+
/** The closest of `known` to `key` when it is near enough to be a typo of it, else undefined. Used for
|
|
3
|
+
* "did you mean" in config problems, and by other checks that name an unknown key. */
|
|
4
|
+
export declare function suggest(key: string, known: string[]): string | undefined;
|
|
2
5
|
/** A config that cannot be used, with a message written for the person editing it. The CLI prints
|
|
3
6
|
* its message alone, without a stack trace. */
|
|
4
7
|
export declare class ConfigError extends Error {
|
|
@@ -8,3 +11,12 @@ export declare class ConfigError extends Error {
|
|
|
8
11
|
export declare function configProblems(raw: unknown): string[];
|
|
9
12
|
/** Check a loaded config and return it typed, or throw one error listing every problem. */
|
|
10
13
|
export declare function validateConfig(raw: unknown, source: string): PatternDumpConfig;
|
|
14
|
+
/**
|
|
15
|
+
* The path of a workbench published on the storefront's own host, or undefined when it has a host of its own.
|
|
16
|
+
* Same-host means a root-relative `workbenchUrl` ("/workbench/"), or an absolute one on the origin of
|
|
17
|
+
* `storefront.url`. Reads a validated config, so both are already normalised (one trailing slash).
|
|
18
|
+
*/
|
|
19
|
+
export declare function sameHostWorkbenchPath(storefront: {
|
|
20
|
+
workbenchUrl?: string;
|
|
21
|
+
url?: string;
|
|
22
|
+
} | undefined): string | undefined;
|
|
@@ -70,7 +70,7 @@ export interface TreeNode {
|
|
|
70
70
|
children?: TreeNode[];
|
|
71
71
|
}
|
|
72
72
|
export type DiagnosticSeverity = "error" | "warning" | "info";
|
|
73
|
-
export type DiagnosticKind = "RENDER_ERROR" | "UNFILLED_REQUIRED" | "SHAPE_MISMATCH" | "HTML_INVALID" | "CASCADE_WARNING" | "A11Y" | "HIGHLIGHT_FALLBACK" | "HANDLE_COLLISION";
|
|
73
|
+
export type DiagnosticKind = "RENDER_ERROR" | "UNFILLED_REQUIRED" | "SHAPE_MISMATCH" | "HTML_INVALID" | "CASCADE_WARNING" | "A11Y" | "HIGHLIGHT_FALLBACK" | "HANDLE_COLLISION" | "DATA_INVALID" | "SCHEMA_INVALID";
|
|
74
74
|
/** Fields every diagnostic carries, whichever engine produced it. */
|
|
75
75
|
interface DiagnosticBase {
|
|
76
76
|
severity: DiagnosticSeverity;
|
|
@@ -81,7 +81,10 @@ interface DiagnosticBase {
|
|
|
81
81
|
ruleId?: string;
|
|
82
82
|
/** JSON Schema / data pointer, e.g. "#/properties/label" — schema kinds only. */
|
|
83
83
|
path?: string;
|
|
84
|
-
/** Source-file position — RENDER_ERROR, best-effort.
|
|
84
|
+
/** Source-file position — RENDER_ERROR and DATA_INVALID, best-effort. `file` is never an absolute
|
|
85
|
+
* path: relative to the project root for the project's own files (`src/patterns/atoms/x/x.json`),
|
|
86
|
+
* `<package name>/<path in the package>` for a pattern package's (`@acme/ds/patterns/atoms/x/x.json`).
|
|
87
|
+
* Output built anywhere names a file the same way, and names no folder of the machine it ran on. */
|
|
85
88
|
location?: {
|
|
86
89
|
file?: string;
|
|
87
90
|
line?: number;
|
|
@@ -464,6 +467,14 @@ export declare const PD_ROUTES: {
|
|
|
464
467
|
readonly version: "/api/version";
|
|
465
468
|
/** GET → ShellClientConfig */
|
|
466
469
|
readonly config: "/api/config";
|
|
470
|
+
/** GET → StorefrontPagesResponse, on the storefront host. What `pd build` crawls for storefront pages. */
|
|
471
|
+
readonly pages: "/api/pages";
|
|
472
|
+
/** GET → the storefront's sitemap (XML), on the storefront host; only when `storefront.url` is set. */
|
|
473
|
+
readonly sitemap: "/sitemap.xml";
|
|
474
|
+
/** GET → robots.txt (text): the storefront's on the storefront host, the workbench's on every other host. */
|
|
475
|
+
readonly robots: "/robots.txt";
|
|
476
|
+
/** GET → the storefront's 404 page (HTML, status 404), on the storefront host. `pd build` writes it as is. */
|
|
477
|
+
readonly notFound: "/404.html";
|
|
467
478
|
/** The shell bundle, stylesheet and vendored assets. */
|
|
468
479
|
readonly mainJs: "/_pd/main.js";
|
|
469
480
|
readonly mainCss: "/_pd/main.css";
|
|
@@ -474,6 +485,16 @@ export declare const PD_ROUTES: {
|
|
|
474
485
|
/** Document-base layer (reset + font-face). */
|
|
475
486
|
readonly baseCss: "/_pd/base.css";
|
|
476
487
|
};
|
|
488
|
+
/**
|
|
489
|
+
* The two front ends of one `pd dev` process, told apart by the request's Host header. Both serve
|
|
490
|
+
* from `/`. Plain `localhost` and `127.0.0.1` also get the workbench. `storefront.host` in the config
|
|
491
|
+
* replaces the storefront default, which is a host of pd dev's only when there is a storefront. A request
|
|
492
|
+
* for any other host is refused (403).
|
|
493
|
+
*/
|
|
494
|
+
export declare const PD_HOSTS: {
|
|
495
|
+
readonly storefront: "live.localhost";
|
|
496
|
+
readonly workbench: "pd.localhost";
|
|
497
|
+
};
|
|
477
498
|
/** Prefixes of the parameterised routes. routes.ts matches with these; the builders emit them. */
|
|
478
499
|
export declare const PD_ROUTE_PREFIX: {
|
|
479
500
|
readonly component: "/api/component/";
|
|
@@ -484,6 +505,7 @@ export declare const PD_ROUTE_PREFIX: {
|
|
|
484
505
|
readonly themeCss: "/_pd/themes/";
|
|
485
506
|
readonly script: "/_pd/scripts/";
|
|
486
507
|
readonly assets: "/assets/";
|
|
508
|
+
readonly images: "/_pd/images/";
|
|
487
509
|
};
|
|
488
510
|
/**
|
|
489
511
|
* GET /api/component/:id[?variant=x] → ComponentDetailResponse (404 → ApiError).
|
|
@@ -496,6 +518,21 @@ export declare function componentCssUrl(id: ComponentId, version?: number | stri
|
|
|
496
518
|
export declare function themeCssUrl(name: string): string;
|
|
497
519
|
/** A compiled behaviour script (esbuild, from scriptsDir). `version` is a cache-buster. */
|
|
498
520
|
export declare function scriptUrl(id: string, version?: number | string): string;
|
|
521
|
+
/**
|
|
522
|
+
* A responsive image variant, served immutable: WebP, `width` pixels wide, of the asset `name` (its path under the
|
|
523
|
+
* assets folder without its extension, e.g. "images/harbour"). `hash` is the variant's key, the first 10 hex
|
|
524
|
+
* digits of a SHA-256 over the source's bytes and what the variant is encoded with, so the URL changes whenever
|
|
525
|
+
* its bytes would. Each segment of `name` is encoded.
|
|
526
|
+
*/
|
|
527
|
+
export declare function imageUrl(name: string, hash: string, width: number): string;
|
|
528
|
+
/** An image variant URL's parts (`name` decoded), or null when the path is not one: an empty, "." or ".."
|
|
529
|
+
* segment, a segment that doesn't decode, a hash that isn't 10 hex digits, a width that isn't a whole number
|
|
530
|
+
* from 1, written without leading zeros. */
|
|
531
|
+
export declare function parseImageUrl(pathname: string): {
|
|
532
|
+
name: string;
|
|
533
|
+
hash: string;
|
|
534
|
+
width: number;
|
|
535
|
+
} | null;
|
|
499
536
|
/** How the shell switches component themes in the preview: toggle `data-theme` on one set of
|
|
500
537
|
* linked theme stylesheets, or swap a single theme stylesheet (for bare-`:root` theme sets). */
|
|
501
538
|
export type ThemeSwitch = "attribute" | "stylesheet";
|
|
@@ -509,6 +546,31 @@ export interface ShellClientConfig {
|
|
|
509
546
|
/** The configured default theme — what an un-themed preview actually shows. "" when none. */
|
|
510
547
|
defaultTheme: string;
|
|
511
548
|
}
|
|
549
|
+
/** One storefront page: a markdown file in `storefront.pagesDir`, at the route its path defines. */
|
|
550
|
+
export interface StorefrontPage {
|
|
551
|
+
/** Root-relative and slash-wrapped: "/" for pages/index.md, "/guides/setup/" for pages/guides/setup.md. */
|
|
552
|
+
route: string;
|
|
553
|
+
/** The page's `title`, or its route when it has none. */
|
|
554
|
+
title: string;
|
|
555
|
+
/** The layout it renders with: the component `layouts/<layout>`. */
|
|
556
|
+
layout: string;
|
|
557
|
+
/** Old routes that forward to this page (frontmatter `redirectFrom`), each root-relative and slash-wrapped.
|
|
558
|
+
* `pd build` writes a forwarding page at each. Absent when the page has none. */
|
|
559
|
+
redirectFrom?: string[];
|
|
560
|
+
}
|
|
561
|
+
/** A nav entry. Top-level pages and folders; a folder's pages are its children (one level only). */
|
|
562
|
+
export interface StorefrontNavItem {
|
|
563
|
+
route: string;
|
|
564
|
+
title: string;
|
|
565
|
+
/** The page's `description` from its frontmatter; absent when it has none. */
|
|
566
|
+
description?: string;
|
|
567
|
+
children?: StorefrontNavItem[];
|
|
568
|
+
}
|
|
569
|
+
/** GET /api/pages — every storefront page and the nav, in nav order. */
|
|
570
|
+
export interface StorefrontPagesResponse {
|
|
571
|
+
pages: StorefrontPage[];
|
|
572
|
+
nav: StorefrontNavItem[];
|
|
573
|
+
}
|
|
512
574
|
/** The body of every JSON error response. */
|
|
513
575
|
export interface ApiError {
|
|
514
576
|
error: string;
|
|
@@ -563,7 +625,9 @@ export declare function staticPathFor(liveUrl: string): string;
|
|
|
563
625
|
* Protocol version of this file's wire shapes — HTTP responses AND socket messages.
|
|
564
626
|
*
|
|
565
627
|
* Bump it whenever a shape changes incompatibly (a field removed, renamed or retyped; an HMR arm
|
|
566
|
-
* removed). Adding an optional field does not need a bump
|
|
628
|
+
* removed). Adding an optional field does not need a bump, and neither does adding an HMR arm: a shell
|
|
629
|
+
* bundled before the arm existed receives it, matches none of its handlers and does nothing. The
|
|
630
|
+
* server announces it three ways —
|
|
567
631
|
* the `hello` socket message, the `X-PD-Protocol` response header, and `VersionInfo.protocol` —
|
|
568
632
|
* and the shell compares it against the value it was bundled with, so a stale shell talking to a
|
|
569
633
|
* newer server says so instead of misbehaving silently. Same idea as GRAPH_ARTIFACT_VERSION.
|
|
@@ -571,6 +635,20 @@ export declare function staticPathFor(liveUrl: string): string;
|
|
|
571
635
|
export declare const PD_PROTOCOL_VERSION = 1;
|
|
572
636
|
/** Response header carrying PD_PROTOCOL_VERSION on every dev-server response. */
|
|
573
637
|
export declare const PD_PROTOCOL_HEADER = "X-PD-Protocol";
|
|
638
|
+
/**
|
|
639
|
+
* The live-reload generation a `pd dev` response was rendered from: `<boot>.<n>`, where `<boot>` names the
|
|
640
|
+
* server process and `<n>` counts the reload messages it has sent. Sent by a live server on every response
|
|
641
|
+
* (this header), in `hello`, and on every reload message (content-swap, css-link-swap, tree-changed,
|
|
642
|
+
* page-changed), bumped when such a message is sent, after the change's state is in place. A request takes
|
|
643
|
+
* it before anything else, so a document rendered while a change landed carries the generation from before
|
|
644
|
+
* it. A client that later sees a newer one knows its document may predate a change it never heard about
|
|
645
|
+
* (sent while its socket was not open, or while its fetch was in flight) and fetches it again. Absent when
|
|
646
|
+
* the server has no live reload (`pd build`). Additive: no protocol bump.
|
|
647
|
+
*/
|
|
648
|
+
export declare const PD_GENERATION_HEADER = "X-PD-Generation";
|
|
649
|
+
/** True when generation `a` is older than `b`: from an earlier server process, or a lower count. Either
|
|
650
|
+
* absent compares as not behind (a server without live reload never makes a client refetch). */
|
|
651
|
+
export declare function generationBehind(a: string | undefined | null, b: string | undefined | null): boolean;
|
|
574
652
|
/**
|
|
575
653
|
* HMR socket messages, discriminated on `type`:
|
|
576
654
|
* - hello : sent once per connection — carries PD_PROTOCOL_VERSION.
|
|
@@ -579,6 +657,8 @@ export declare const PD_PROTOCOL_HEADER = "X-PD-Protocol";
|
|
|
579
657
|
* - error : a render or build failure, surfaced on the Console.
|
|
580
658
|
* - tree-changed : a component was added or removed → re-fetch the tree.
|
|
581
659
|
* - collision : a handle collision quarantined a component → surfaced on the Console.
|
|
660
|
+
* - page-changed : a storefront page (or the nav) changed → storefront pages reload. The
|
|
661
|
+
* workbench shell receives it too and ignores it.
|
|
582
662
|
*
|
|
583
663
|
* NOTE: A11yResult / meta.a11y were removed in M4.11. Rendered a11y findings are produced
|
|
584
664
|
* client-side (axe in the live iframe) and do not flow through the HMR channel.
|
|
@@ -602,10 +682,20 @@ export interface CssLinkSwapMessage {
|
|
|
602
682
|
* Only components with non-empty CSS output are included.
|
|
603
683
|
*/
|
|
604
684
|
orderedHrefs?: string[];
|
|
685
|
+
/**
|
|
686
|
+
* Present (true) when the component's CSS was empty before this change and is not now. A component with
|
|
687
|
+
* no CSS gets no link (the link set holds linkable components only), so a document rendered while it
|
|
688
|
+
* was empty has no link for it to update.
|
|
689
|
+
*/
|
|
690
|
+
cssBecameNonEmpty?: true;
|
|
691
|
+
/** The generation this message bumped the server to (PD_GENERATION_HEADER); set by the server. */
|
|
692
|
+
generation?: string;
|
|
605
693
|
}
|
|
606
694
|
/** Fired when a .hbs is added or removed — client re-fetches /api/tree to rebuild the nav. */
|
|
607
695
|
export interface TreeChangedMessage {
|
|
608
696
|
type: "tree-changed";
|
|
697
|
+
/** The generation this message bumped the server to (PD_GENERATION_HEADER); set by the server. */
|
|
698
|
+
generation?: string;
|
|
609
699
|
}
|
|
610
700
|
/** M6: a handle collision was detected at discovery time. Both paths are named so the author
|
|
611
701
|
* can resolve the conflict (rename or move a file). The tree remains operational; the
|
|
@@ -616,15 +706,27 @@ export interface CollisionMessage {
|
|
|
616
706
|
incumbentPath: string;
|
|
617
707
|
quarantinedPath: string;
|
|
618
708
|
}
|
|
619
|
-
|
|
709
|
+
/** A storefront page changed. `route` names the page ("/guides/setup/"); null means a page was added
|
|
710
|
+
* or removed, so the nav — and with it every page — changed. */
|
|
711
|
+
export interface PageChangedMessage {
|
|
712
|
+
type: "page-changed";
|
|
713
|
+
route: string | null;
|
|
714
|
+
/** The generation this message bumped the server to (PD_GENERATION_HEADER); set by the server. */
|
|
715
|
+
generation?: string;
|
|
716
|
+
}
|
|
717
|
+
export type HmrMessage = HelloMessage | ContentSwapMessage | ErrorMessage | CssLinkSwapMessage | TreeChangedMessage | CollisionMessage | PageChangedMessage;
|
|
620
718
|
/** First frame on every connection. Lets the shell detect a protocol mismatch with the server. */
|
|
621
719
|
export interface HelloMessage {
|
|
622
720
|
type: "hello";
|
|
623
721
|
protocol: number;
|
|
722
|
+
/** The current live-reload generation (PD_GENERATION_HEADER). */
|
|
723
|
+
generation?: string;
|
|
624
724
|
}
|
|
625
725
|
export interface ContentSwapMessage {
|
|
626
726
|
type: "content-swap";
|
|
627
727
|
componentId: ComponentId;
|
|
728
|
+
/** The generation this message bumped the server to (PD_GENERATION_HEADER); set by the server. */
|
|
729
|
+
generation?: string;
|
|
628
730
|
}
|
|
629
731
|
export interface ErrorMessage {
|
|
630
732
|
type: "error";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** What the embedding page does with a decision. Only `write` is required. */
|
|
2
|
+
export interface HeightFitAdapter {
|
|
3
|
+
/** Set the frame's height, in CSS px. */
|
|
4
|
+
write(frame: HTMLIFrameElement, height: number): void;
|
|
5
|
+
/** Whether this frame content-fits its height at all (a full-page preview does not). Default: yes. */
|
|
6
|
+
fits?(frame: HTMLIFrameElement): boolean;
|
|
7
|
+
/** Called once when a frame's height turns out to be circular, after it was set back to its steady height. */
|
|
8
|
+
onCircular?(frame: HTMLIFrameElement): void;
|
|
9
|
+
/** Called when a frame's history is forgotten (its document was replaced or restyled). */
|
|
10
|
+
onReset?(frame: HTMLIFrameElement): void;
|
|
11
|
+
}
|
|
12
|
+
/** Per frame: the height last applied, the last height that held still, and how many growing ticks followed in a row. */
|
|
13
|
+
export interface HeightState {
|
|
14
|
+
last: number;
|
|
15
|
+
steady: number;
|
|
16
|
+
run: number;
|
|
17
|
+
}
|
|
18
|
+
export interface HeightFitter {
|
|
19
|
+
/** Apply a pd-height measured in `frame`'s document. Returns true when the frame's height was written. */
|
|
20
|
+
accept(frame: HTMLIFrameElement, height: number): boolean;
|
|
21
|
+
/** Forget a frame's height history: growth detection starts over. */
|
|
22
|
+
reset(frame: HTMLIFrameElement): void;
|
|
23
|
+
/** If `event` is a pd-height message from one of `frames`, that frame and its height; otherwise null. */
|
|
24
|
+
fromMessage(event: MessageEvent<unknown>, frames: Iterable<HTMLIFrameElement>): {
|
|
25
|
+
frame: HTMLIFrameElement;
|
|
26
|
+
height: number;
|
|
27
|
+
} | null;
|
|
28
|
+
/** Whether `measured` continues a run of growth against the heights we imposed — the loop's signature. */
|
|
29
|
+
isCircularGrowth(previous: HeightState | undefined, measured: number): boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function createHeightFitter(adapter: HeightFitAdapter): HeightFitter;
|
|
32
|
+
/** The circular-growth rule, for callers without a fitter. The same function every fitter uses. */
|
|
33
|
+
export declare function isCircularGrowth(previous: HeightState | undefined, measured: number): boolean;
|
|
@@ -16,7 +16,33 @@ export interface RenderInput {
|
|
|
16
16
|
variant?: string;
|
|
17
17
|
/** Live-control overrides; highest precedence in the cascade. */
|
|
18
18
|
data?: Record<string, unknown>;
|
|
19
|
+
/** Which image settings the variant URLs this render writes are keyed with: "site" for a storefront page and the
|
|
20
|
+
* previews its host serves (`storefront.images` over `images`), else the project's (`images`). */
|
|
21
|
+
images?: "site";
|
|
19
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* What an image in the assets is, for `{{pd-image}}`: looked up by its URL (`/assets/…`), synchronously, from an
|
|
25
|
+
* index built before rendering. Sizes are as the image is displayed (EXIF orientation applied).
|
|
26
|
+
* - `variants`: a raster image, served as WebP variants; `variantUrl(width, settings)` is a variant's URL.
|
|
27
|
+
* - `single`: one file, as it is (an SVG, or a raster image pd dev shows as it is when it can't encode).
|
|
28
|
+
* - `unavailable`: a raster image that can't be served as variants; `reason` says why (the build fails the page).
|
|
29
|
+
*/
|
|
30
|
+
export type ImageSource = {
|
|
31
|
+
kind: "variants";
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
variantUrl(width: number, settings?: "site"): string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "single";
|
|
37
|
+
url: string;
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "unavailable";
|
|
42
|
+
reason: string;
|
|
43
|
+
};
|
|
44
|
+
/** Looks an asset up by its URL; undefined when the assets have no image there. */
|
|
45
|
+
export type ImageLookup = (src: string) => ImageSource | undefined;
|
|
20
46
|
export interface RenderMeta {
|
|
21
47
|
componentId: string;
|
|
22
48
|
variant?: string;
|
|
@@ -117,6 +143,8 @@ export type Render = (input: RenderInput) => Promise<RenderOutput>;
|
|
|
117
143
|
export interface RendererOptions {
|
|
118
144
|
/** Root containing the atomic pattern folders (atoms/, molecules/, ...). */
|
|
119
145
|
patternsDir: string;
|
|
146
|
+
/** The images in the assets, for `{{pd-image}}`. Without it, `{{pd-image}}` fails the render. */
|
|
147
|
+
images?: ImageLookup;
|
|
120
148
|
/** Every patterns folder, project first, when pattern packages are in use. Takes precedence over
|
|
121
149
|
* `patternsDir` for discovery. */
|
|
122
150
|
patternRoots?: PatternRoot[];
|
|
@@ -158,6 +186,13 @@ export interface RendererOptions {
|
|
|
158
186
|
* the real M6 file-as-component walk, never the old collectComponents folder-per-component walk.
|
|
159
187
|
*/
|
|
160
188
|
discoveryMap?: DiscoveryMap;
|
|
189
|
+
/**
|
|
190
|
+
* How a diagnostic names a source file (`location.file`): project-relative, or prefixed with its
|
|
191
|
+
* package's name (see `Diagnostic` in dev-protocol). The dev-server supplies it, since only it knows
|
|
192
|
+
* the project root and each package's folder. Without it, or when it returns an absolute path, the
|
|
193
|
+
* renderer names the file relative to its pattern root. A diagnostic never carries an absolute path.
|
|
194
|
+
*/
|
|
195
|
+
displayPath?: (absPath: string) => string;
|
|
161
196
|
}
|
|
162
197
|
/** Factory: a configured renderer closes over its source root, keeping `render()` pure. */
|
|
163
198
|
export type CreateRenderer = (options: RendererOptions) => Render;
|
|
@@ -292,3 +327,4 @@ export interface ShellBrandConfig {
|
|
|
292
327
|
/** Ordered list of CSS custom-property names used as swatch slots. */
|
|
293
328
|
export type SwatchSlots = string[];
|
|
294
329
|
export * from "./dev-protocol.js";
|
|
330
|
+
export * from "./iframe-fit.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pattern-dump",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-alpha.0",
|
|
4
4
|
"description": "A workbench for HTML and CSS design systems: live component previews, docs, checks and static publishing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
"static-site",
|
|
17
17
|
"htwoo"
|
|
18
18
|
],
|
|
19
|
-
"author":
|
|
19
|
+
"author": {
|
|
20
|
+
"name": "Stefan Bauer (N8D e.U.)",
|
|
21
|
+
"email": "stefan.bauer@n8d.at",
|
|
22
|
+
"url": "https://n8d.at"
|
|
23
|
+
},
|
|
20
24
|
"license": "MIT",
|
|
21
25
|
"homepage": "https://github.com/n8design/pattern-dump#readme",
|
|
22
26
|
"repository": {
|
|
@@ -51,10 +55,11 @@
|
|
|
51
55
|
"templates",
|
|
52
56
|
"README.md",
|
|
53
57
|
"LICENSE",
|
|
58
|
+
"THIRD-PARTY-NOTICES.md",
|
|
54
59
|
"CHANGELOG.md"
|
|
55
60
|
],
|
|
56
61
|
"engines": {
|
|
57
|
-
"node": ">=
|
|
62
|
+
"node": ">=22.22"
|
|
58
63
|
},
|
|
59
64
|
"dependencies": {
|
|
60
65
|
"@shikijs/transformers": "^4.1.0",
|
|
@@ -69,7 +74,9 @@
|
|
|
69
74
|
"markdown-it": "^14.2.0",
|
|
70
75
|
"node-html-parser": "^7.1.0",
|
|
71
76
|
"postcss": "^8.5.15",
|
|
77
|
+
"postcss-selector-parser": "^7.1.1",
|
|
72
78
|
"sass": "^1.100.0",
|
|
79
|
+
"sharp": "0.35.5",
|
|
73
80
|
"shiki": "^4.1.0",
|
|
74
81
|
"source-map": "^0.6.1",
|
|
75
82
|
"ws": "^8.21.0"
|
package/templates/base/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
// The document defaults every preview sits on — the base layer named by `documentBase` in the config.
|
|
4
4
|
// Fallbacks keep a preview readable before a theme is chosen.
|
|
5
|
+
//
|
|
6
|
+
// `:where(body)` has no specificity, so these are defaults only: a pattern package's own document
|
|
7
|
+
// rules (hTWOo's body background and text colour, which follow its themes) override them even
|
|
8
|
+
// though the project's base rules come later in the cascade.
|
|
5
9
|
|
|
6
10
|
*,
|
|
7
11
|
*::before,
|
|
@@ -9,7 +13,7 @@
|
|
|
9
13
|
box-sizing: border-box;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
body {
|
|
16
|
+
:where(body) {
|
|
13
17
|
margin: 0;
|
|
14
18
|
font-family: tokens.$font-family;
|
|
15
19
|
line-height: 1.5;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Builds the website and the workbench, and publishes both to GitHub Pages:
|
|
2
|
+
# the website at the root of your domain, the workbench under /workbench/.
|
|
3
|
+
# Before the first run: Settings → Pages → Source: "GitHub Actions", and your custom domain: see "Publish it" in the README.
|
|
4
|
+
name: Pages
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
pages: write
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
# One deploy at a time; a deploy that has started is never cancelled halfway.
|
|
17
|
+
concurrency:
|
|
18
|
+
group: pages
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
runs-on: ubuntu-24.04
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
with:
|
|
27
|
+
# The full history: a layout that shows page dates needs it, and pd build refuses a shallow clone then.
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
- uses: actions/setup-node@v7
|
|
30
|
+
with:
|
|
31
|
+
node-version: 24
|
|
32
|
+
cache: npm
|
|
33
|
+
- run: npm ci
|
|
34
|
+
- run: npm run build
|
|
35
|
+
# pd build writes the website (site/) and the workbench (dist/) side by side; Pages takes one folder.
|
|
36
|
+
- run: |
|
|
37
|
+
mkdir _site
|
|
38
|
+
cp -R site/. _site/
|
|
39
|
+
cp -R dist _site/workbench
|
|
40
|
+
- uses: actions/configure-pages@v6
|
|
41
|
+
- uses: actions/upload-pages-artifact@v5
|
|
42
|
+
with:
|
|
43
|
+
path: _site
|
|
44
|
+
|
|
45
|
+
deploy:
|
|
46
|
+
needs: build
|
|
47
|
+
runs-on: ubuntu-24.04
|
|
48
|
+
environment:
|
|
49
|
+
name: github-pages
|
|
50
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
51
|
+
steps:
|
|
52
|
+
- id: deployment
|
|
53
|
+
uses: actions/deploy-pages@v5
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: About
|
|
3
|
+
description: What is in this starter, and where it lives.
|
|
4
|
+
order: 1
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The pages are in `pages/`. What they look like is the skin, written as ordinary components so the
|
|
8
|
+
workbench shows each part of it:
|
|
9
|
+
|
|
10
|
+
- `src/patterns/layouts/page`: the layout every page renders with
|
|
11
|
+
- `src/patterns/partials/base`: the frame around it, with the header, navigation and footer
|
|
12
|
+
- `src/patterns/partials/head`: what the skin adds to every page's `<head>`
|
|
13
|
+
- `src/patterns/components/note`: a component the pages show
|
|
14
|
+
|
|
15
|
+
The site's name and footer are `storefront.site` in `pattern-dump.config.ts`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Home
|
|
3
|
+
description: A website written in markdown, next to the design system it shows.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
This page is `pages/index.md`. Every markdown file in `pages/` is a page, at the address its path
|
|
7
|
+
names: `pages/about.md` is [/about/](/about/).
|
|
8
|
+
|
|
9
|
+
## A live example
|
|
10
|
+
|
|
11
|
+
A `pattern` fence shows a component from the workbench, in a frame of its own, so its own styles
|
|
12
|
+
render it and nothing on this page can restyle it:
|
|
13
|
+
|
|
14
|
+
```pattern components/note
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## A component with your values
|
|
18
|
+
|
|
19
|
+
A `pd-` tag places a component with the values you give it. They are checked against the component's
|
|
20
|
+
schema, `src/patterns/components/note/note.schema.json`, before the page is shown:
|
|
21
|
+
|
|
22
|
+
<pd-components-note tone="tip" heading="Try breaking it">
|
|
23
|
+
Change `tone="tip"` to `tone="tips"` and save. The page shows what is wrong, and where.
|
|
24
|
+
</pd-components-note>
|