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.
Files changed (44) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/LICENSE +16 -3
  3. package/README.md +2 -2
  4. package/THIRD-PARTY-NOTICES.md +130 -0
  5. package/dist/chunks/chunk-ESMZAOP2.js +283 -0
  6. package/dist/dev-ui/main.js +215 -77
  7. package/dist/index.js +1 -1
  8. package/dist/pd.js +5887 -1003
  9. package/dist/types/config/index.d.ts +98 -5
  10. package/dist/types/config/validate.d.ts +12 -0
  11. package/dist/types/contracts/dev-protocol.d.ts +106 -4
  12. package/dist/types/contracts/iframe-fit.d.ts +33 -0
  13. package/dist/types/contracts/index.d.ts +36 -0
  14. package/package.json +10 -3
  15. package/templates/base/README.md +1 -0
  16. package/templates/base/_gitignore +1 -0
  17. package/templates/base/pattern-dump.config.ts +1 -0
  18. package/templates/base/src/styles/base/document.scss +5 -1
  19. package/templates/storefront/.github/workflows/pages.yml +53 -0
  20. package/templates/storefront/assets/icon.svg +2 -0
  21. package/templates/storefront/pages/about.md +15 -0
  22. package/templates/storefront/pages/index.md +24 -0
  23. package/templates/storefront/src/patterns/components/note/note.hbs +4 -0
  24. package/templates/storefront/src/patterns/components/note/note.json +4 -0
  25. package/templates/storefront/src/patterns/components/note/note.md +11 -0
  26. package/templates/storefront/src/patterns/components/note/note.schema.json +13 -0
  27. package/templates/storefront/src/patterns/components/note/note~warning.json +4 -0
  28. package/templates/storefront/src/patterns/layouts/page/page.hbs +8 -0
  29. package/templates/storefront/src/patterns/layouts/page/page.json +8 -0
  30. package/templates/storefront/src/patterns/layouts/page/page.md +6 -0
  31. package/templates/storefront/src/patterns/layouts/page/page.schema.json +19 -0
  32. package/templates/storefront/src/patterns/partials/base/base.hbs +17 -0
  33. package/templates/storefront/src/patterns/partials/base/base.json +4 -0
  34. package/templates/storefront/src/patterns/partials/base/base.md +6 -0
  35. package/templates/storefront/src/patterns/partials/base/base.schema.json +18 -0
  36. package/templates/storefront/src/patterns/partials/head/head.hbs +1 -0
  37. package/templates/storefront/src/patterns/partials/head/head.md +6 -0
  38. package/templates/storefront/src/styles/components/note.scss +8 -0
  39. package/templates/storefront/src/styles/layouts/page.scss +6 -0
  40. package/templates/storefront/src/styles/partials/base.scss +13 -0
  41. package/templates/storefront-fills/config.txt +14 -0
  42. package/templates/storefront-fills/gitignore.txt +2 -0
  43. package/templates/storefront-fills/readme.md +60 -0
  44. package/dist/chunks/chunk-3WZHFYHZ.js +0 -160
package/CHANGELOG.md CHANGED
@@ -1,5 +1,125 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0-alpha.0
4
+
5
+ The storefront: a website written as markdown pages, dressed by a skin made of ordinary components, served by
6
+ `pd dev` on a host of its own and built by `pd build` next to the workbench. With it come responsive images, link
7
+ and image checks, and live examples of your components on the site's pages.
8
+
9
+ Pattern Dump now needs Node.js 22.22 or later (it was 20.10).
10
+
11
+ ### Workbench
12
+
13
+ - Pattern packages linked into a project (a `file:` dependency or a workspace) are watched like the project's own
14
+ files, and each is read from its real folder. Global data, assets and helper modules reload while `pd dev` runs.
15
+ - A pattern package's TypeScript helpers are refused with a message naming them, and diagnostics name files by
16
+ project or package path, never by an absolute path.
17
+ - Live reload is dependable: changes are handled one at a time, in the order they happen; a page or preview that
18
+ missed a change fetches it again and never shows an older one; a save the file watcher drops is still handled; a
19
+ folder added while `pd dev` runs (as `git checkout` or `git pull` does) is seen with every file in it.
20
+ - A component added while `pd dev` runs gets its own stylesheet even when the stylesheet is saved before the
21
+ template.
22
+ - A variant reached by its own id (`/preview/atoms/toggle-demo~alt`) is the same document as `?variant=alt`, with
23
+ its base's behaviour scripts, layout and CSS.
24
+ - An overview tile picks up a stylesheet that was still empty when it rendered.
25
+ - Behaviour scripts are stamped with their content hash, so a browser fetches a script again exactly when it
26
+ changed.
27
+ - Every base-layer file whose rules no component owns and no `documentBase` entry lists is named in
28
+ `BASE-LAYER.generated.md`, with its rule count.
29
+
30
+ ### Storefront
31
+
32
+ - Pages: every markdown file in `storefront.pagesDir` is a page, rendered with a layout from your patterns. Its
33
+ frontmatter is checked against the layout's schema, `storefront.site` against `partials/base`'s, and every
34
+ problem names its file and line. `pd dev` serves the site at `http://live.localhost:4000/`, `pd build` writes it
35
+ to `storefront.outDir`.
36
+ - Live examples: a `pattern` fence shows a component's own preview in a frame, with links to its variants. A
37
+ component can declare the height its example starts at (`x-pd-example-height`), and `pd dev` warns when that
38
+ height is too tall.
39
+ - Placing components with values: `<pd-…>` tags in a page's markdown, checked against the component's schema at
40
+ every level (`x-pd-component`, `x-pd-content`, `text/markdown` properties), with the file, line and column of any
41
+ problem.
42
+ - What a page knows about itself, for layouts that ask for it: its headings (with GitHub's anchors), section,
43
+ breadcrumbs, previous and next pages, dates from git, and declared navigations.
44
+ - Search engines and link previews: canonical URLs, Open Graph, the site's language, JSON-LD, `sitemap.xml`,
45
+ `robots.txt`, a 404 page, `redirectFrom` for old routes, `index: false`, and a link-preview image per page or site.
46
+ - A skin can add to every page's `<head>` through a `partials/head` component, and can be a pattern package.
47
+ - `pd init --storefront` starts a project with a website: pages, a plain skin, the workbench under `/workbench/`
48
+ on the same host, and a GitHub Pages workflow.
49
+ - Every file a page references (stylesheets, scripts, fonts, images) carries the content hash of the file as
50
+ served, so a visitor's browser fetches it again exactly when it changed.
51
+
52
+ ### Images
53
+
54
+ - Every raster image is served as WebP variants of the widths it is shown at, encoded with sharp on first use and
55
+ cached in `node_modules/.cache/pattern-dump/images/`. `images` (quality, effort) sets the encoding, and
56
+ `storefront.images` overrides it for the site.
57
+ - Image slots: a component declares where an image goes and how wide it is shown (`x-pd-image`), and its template
58
+ writes it with `{{pd-image field alt=…}}`. Alt text is required; `alt=""` marks a decorative image.
59
+ - Images in a page's markdown get their variants in the slot the page's layout declares (`x-pd-prose-images`),
60
+ with `sizes="auto"`, and the first ones can load at once (`eager`).
61
+ - Every `<img>` carries its width and height, so the page doesn't shift as images load. Photos are turned upright
62
+ by their EXIF orientation.
63
+
64
+ ### Checks
65
+
66
+ - `pd build` checks every link on every page of the site before it writes anything: pages, files, `#` anchors (a
67
+ heading's too), the site's own address and links into the workbench. A broken link stops the build; links to other
68
+ sites are listed in the build's notes. `pd dev` warns about a page's links to pages that don't exist.
69
+ - `pd build` refuses a page with an `<img>` that has no width and height, or a raster image served without its WebP
70
+ variants, and names the fix; `pd dev` shows the same as warnings.
71
+ - A schema field named like a registered helper is an error, and so is an unknown `x-pd-` key in a schema.
72
+ - The shape check tells a list from an object (an array property was reported as a mismatch).
73
+
74
+ ### Fixes
75
+
76
+ - `pd dev` answers only requests addressed to its own host names, accepts live-reload connections only from its own
77
+ pages, binds its live-reload socket to 127.0.0.1 like its HTTP server, and serves files only from inside each
78
+ route's folder.
79
+ - The starter's body defaults no longer override a pattern package's document styles (hTWOo's dark themes showed
80
+ dark text on a white page).
81
+ - Two config loads in the same millisecond no longer share a temporary file.
82
+ - The audit reuses a cached result only when it was made by the same version of the check.
83
+ - The live-reload socket reports the port it is actually bound to.
84
+ - A code example on a page that quotes `<img src="/assets/…">` is shown as written, not with a cache stamp added.
85
+
86
+ ## 0.1.0-alpha.4
87
+
88
+ Fixes found while moving a third PatternLab styleguide to Pattern Dump with hTWOo consumed only as
89
+ pattern packages: its templates, data, scripts and CSS all come from `@n8d/htwoo-patterns` and
90
+ `@n8d/htwoo-core`, and the project compiles only its own SCSS.
91
+
92
+ ### Added
93
+
94
+ - `styles: false` on a package reference leaves that package's stylesheets out, for a project that
95
+ compiles them itself; its templates, data, themes, scripts and assets are still used. It also works
96
+ for a package that another brings in as a peer: name the peer with the option.
97
+ - `themes.dir` is optional: `themes: { default: "<name>" }` alone makes one of your packages' themes the
98
+ one previews start with. `pd dev` warns when the default matches no theme.
99
+ - A component or variant `.json` that does not parse is reported as a `DATA_INVALID` diagnostic, with
100
+ the file and, where the parser gives one, the line and column, instead of being dropped silently. An
101
+ invalid variant file no longer fails the whole render.
102
+
103
+ ### Fixed
104
+
105
+ - A class belongs to the rule that defines it (the class on its own, such as `.card` or `.card:hover`),
106
+ not to every rule that mentions it. A project rule that restyles a package class in context
107
+ (`.tile .hoo-radio`) took the class over, so the package component's CSS was never linked where the
108
+ class was used.
109
+ - A rule repeated after a different one (A, B, A) keeps its last position in the base layer and in
110
+ component CSS, as the browser applies it. The first copy was kept, so B won.
111
+ - Included partials keep the caller's data frame, as in stock Handlebars: `@root` is the page's data,
112
+ and `{{#if @partial-block}}` sees the block of a block call.
113
+ - A configured `styles.entry` list compiles in the order given. It was sorted, which could change the
114
+ cascade.
115
+ - SCSS can load an installed package by its bare name (`@use '@scope/pkg/…'`) or a `pkg:` URL, without
116
+ `SASS_PATH`.
117
+ - A data variant reached by its own id (`/preview/atoms/chip~danger`) or included by its handle
118
+ (`{{> "atoms-chip~danger"}}`) renders on top of its base component's data, as `?variant=` already did.
119
+ - `pd build` copies assets as `pd dev` serves them: where two asset folders share a name, the first one
120
+ (the project's) wins and the other is left out and reported. A package's `images/` used to overwrite
121
+ the project's in the snapshot. `pd dev` warns about such folders at startup.
122
+
3
123
  ## 0.1.0-alpha.3
4
124
 
5
125
  Fixes found while moving a second PatternLab styleguide (n8d) to Pattern Dump.
package/LICENSE CHANGED
@@ -1,8 +1,21 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 n8d and contributors
3
+ Copyright (c) 2026 Stefan Bauer (N8D e.U.)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, subject to the conditions of the MIT License.
8
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -4,7 +4,7 @@ A workbench for HTML and CSS design systems, and a successor to PatternLab. Writ
4
4
  Handlebars templates with SCSS, data and Markdown docs; Pattern Dump shows them live, checks them, maps
5
5
  how they relate, and publishes the result as a static site.
6
6
 
7
- > **Alpha (0.1.0-alpha.3).** Published under the `alpha` tag. Options and file conventions can still change
7
+ > **Alpha (0.2.0-alpha.0).** Published under the `alpha` tag. Options and file conventions can still change
8
8
  > between releases.
9
9
 
10
10
  ## Requirements
@@ -102,4 +102,4 @@ export default defineConfig({
102
102
 
103
103
  ## License
104
104
 
105
- [MIT](https://github.com/n8design/pattern-dump/blob/main/LICENSE) © 2026 [n8d](https://n8d.at) and contributors
105
+ [MIT](https://github.com/n8design/pattern-dump/blob/main/LICENSE) © 2026 Stefan Bauer (N8D e.U.)
@@ -0,0 +1,130 @@
1
+ # Third-party notices
2
+
3
+ Pattern Dump is MIT-licensed (see `LICENSE`). It includes code from the projects below, under their own
4
+ licences.
5
+
6
+ ## github-slugger 2.0.0
7
+
8
+ - **Source:** https://github.com/Flet/github-slugger
9
+ - **Used in:** `packages/dev-server/src/storefront/slug.ts` (the slugger and its generated regex, ported
10
+ unchanged) and `packages/dev-server/test/fixtures/github-slugger-2.0.0.json` (its test fixtures, copied
11
+ unchanged).
12
+ - **Licence:** ISC
13
+
14
+ ```
15
+ Copyright (c) 2015, Dan Flettre <fletd01@yahoo.com>
16
+
17
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is
18
+ hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
21
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
22
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
23
+ USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
24
+ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25
+ ```
26
+
27
+ ## Fonts in the repository
28
+
29
+ - **Mr Bedfort**, Alejandro Paul (Sudtipos), version 1.000, with Reserved Font Name "Mr Bedfort".
30
+ - **Used in:** `test-bench/assets/fonts/pd-test.woff2`, unmodified; its licence text is
31
+ `test-bench/licenses/pd-test-OFL.txt`, and `test-bench/README.md` says what the file is. Not part of the npm package.
32
+ - **Licence:** SIL Open Font License 1.1.
33
+
34
+ The experimental skin's fonts (Montserrat, Source Serif 4, IBM Plex Mono) are listed, with their sources and
35
+ licences, in `skins/experimental/assets/skin-experimental/fonts/README.md`.
36
+
37
+ ## Images in the repository
38
+
39
+ These files are in the git repository, in test fixtures and benches. They are not part of the npm package.
40
+
41
+ ### Photographs from Unsplash
42
+
43
+ - **Space Shuttle Challenger**, NASA: https://unsplash.com/photos/dCgbRAQmTQA
44
+ - **Used in:** `packages/build/test/fixtures/images/` (`photo.jpg`, `portrait-exif6.jpg` and `alpha.png`, cut from
45
+ it; how, in the README beside them).
46
+ - **Licence:** [Unsplash License](https://unsplash.com/license). This is not the MIT licence and not an open-source
47
+ licence: it covers these images only, and the repository's MIT licence does not cover them.
48
+
49
+ - **Cable cars**, Cody Hiscox: https://unsplash.com/photos/kzb0Mo0YQDw
50
+ - **Used in:** `storefront-bench/src/assets/images/cable-cars.jpg` (cropped and scaled; how, in the README beside it).
51
+ - **Licence:** [Unsplash License](https://unsplash.com/license), as above: not the MIT licence.
52
+
53
+ ## Phosphor Icons
54
+
55
+ - **Phosphor Icons**: https://github.com/phosphor-icons/core
56
+ - **Used in:** `apps/dev-ui/src/icons/index.ts`: twenty icons of the workbench (regular and fill weights, the
57
+ SVG paths unchanged), which are in the npm package as part of `dist/dev-ui/main.js`. The registry's two other
58
+ icons (the disclosure chevron and the open-in-new-window mark) are not Phosphor's.
59
+ - **Also used in:** `test-bench/assets/images/symbols.svg`: the `star` and `check` icons (regular weight, paths
60
+ unchanged), for the sprite and `<use>` specs. Not part of the npm package.
61
+ - **Licence:** MIT
62
+
63
+ ```
64
+ MIT License
65
+
66
+ Copyright (c) 2023 Phosphor Icons
67
+
68
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
69
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
70
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
71
+ to permit persons to whom the Software is furnished to do so, subject to the following conditions:
72
+
73
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of
74
+ the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
77
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
78
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
79
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
80
+ IN THE SOFTWARE.
81
+ ```
82
+
83
+ ## Monokai Light
84
+
85
+ - **Monokai Light**, Velichko Konstantin: https://github.com/zoxon/vscode-theme-monokai-light
86
+ - **Used in:** `packages/renderer/src/themes/monokai-light.ts`: the syntax theme `pd-monokai-light` takes its
87
+ token colours from the theme's JSON, with two deliberate changes (listed in the file). It is part of the npm
88
+ package (`dist/pd.js`).
89
+ - **Licence:** MIT
90
+
91
+ ```
92
+ MIT License
93
+
94
+ Copyright (c) 2017 Velichko Konstantin
95
+
96
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
97
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
98
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
99
+ to permit persons to whom the Software is furnished to do so, subject to the following conditions:
100
+
101
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of
102
+ the Software.
103
+
104
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
105
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
106
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
107
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
108
+ IN THE SOFTWARE.
109
+ ```
110
+
111
+ ## Colour vision deficiency matrices
112
+
113
+ - **Machado, G. M., Oliveira, M. M., Fernandes, L. (2009).** "A Physiologically-based Model for Simulation of Color
114
+ Vision Deficiency". *IEEE Transactions on Visualization and Computer Graphics* 15(6), 1291-1298.
115
+ https://doi.org/10.1109/TVCG.2009.113
116
+ - **Used in:** `apps/dev-ui/src/core/cvd-math.ts`: the matrices of the workbench's colour-vision simulation (protanopia,
117
+ deuteranopia, tritanopia, eleven severities each). They are numbers published in that paper: data, not code. The
118
+ achromatopsia table is not from the paper.
119
+ - **Licence:** none applies to published data; the paper is cited as its source.
120
+
121
+ ## Dependencies with other licences
122
+
123
+ The npm package declares its dependencies, and npm installs them with their own licence files; none is bundled into
124
+ `dist/` or modified. Almost all are MIT, ISC, BSD or Apache-2.0. Two have terms worth naming:
125
+
126
+ - **axe-core** (Deque Systems), **MPL-2.0**: https://github.com/dequelabs/axe-core. Used by the accessibility checks
127
+ (`packages/a11y`), as installed.
128
+ - **libvips** binaries (`@img/sharp-libvips-*`), **LGPL-3.0-or-later**: https://github.com/lovell/sharp-libvips. Optional
129
+ dependencies of **sharp** (Apache-2.0), which encodes the storefront's images; installed as prebuilt binaries, not
130
+ linked into or copied into this package. An install with `--omit=optional` leaves them out.
@@ -0,0 +1,283 @@
1
+ // packages/config/src/validate.ts
2
+ import { relative, resolve, sep } from "node:path";
3
+ var IMAGES = { keys: { quality: "quality", effort: "effort" } };
4
+ var SHAPE = {
5
+ $schema: "string",
6
+ sourceDir: "string",
7
+ patternsDir: "string",
8
+ outDir: "string",
9
+ styles: {
10
+ keys: {
11
+ strategy: { values: ["parallel"], unsupported: ["co-located"] },
12
+ dir: "string",
13
+ entry: "string-or-strings"
14
+ },
15
+ required: ["dir"]
16
+ },
17
+ themes: {
18
+ keys: {
19
+ dir: "string",
20
+ default: "string",
21
+ switch: { values: ["attribute", "stylesheet"] },
22
+ swatch: { keys: { slots: "strings" }, required: ["slots"] }
23
+ },
24
+ required: ["default"]
25
+ },
26
+ assets: {
27
+ keys: {
28
+ strategy: { values: ["central"], unsupported: ["component-relative"] },
29
+ dir: "string-or-strings",
30
+ css: "strings"
31
+ }
32
+ },
33
+ scriptsDir: "string",
34
+ globalScripts: "strings",
35
+ dataDir: "string",
36
+ helpers: "string",
37
+ documentBase: "strings",
38
+ overviews: "boolean",
39
+ shell: {
40
+ keys: { brand: { keys: { title: "string", logo: "logo", logoAlt: "string", href: "string" } } }
41
+ },
42
+ packages: "packages",
43
+ exclude: "strings",
44
+ build: { keys: { outDir: "string", basePath: "string", audit: "boolean" } },
45
+ images: IMAGES,
46
+ storefront: {
47
+ keys: {
48
+ pagesDir: "string",
49
+ outDir: "string",
50
+ host: "host",
51
+ workbenchUrl: "workbench-url",
52
+ site: "plain-object",
53
+ url: "site-url",
54
+ lang: "lang",
55
+ index: "boolean",
56
+ image: "asset-path",
57
+ images: IMAGES
58
+ },
59
+ required: ["pagesDir", "outDir"]
60
+ }
61
+ };
62
+ var REQUIRED = ["sourceDir", "patternsDir", "outDir", "styles"];
63
+ var WORKBENCH_PATH = /^\/(?:(?!\.{1,2}(?:\/|$))[a-z0-9._~-]+\/?)+$/i;
64
+ var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
65
+ var isStrings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
66
+ function distance(a, b) {
67
+ const row = Array.from({ length: b.length + 1 }, (_, i) => i);
68
+ for (let i = 1; i <= a.length; i++) {
69
+ let prev = row[0];
70
+ row[0] = i;
71
+ for (let j = 1; j <= b.length; j++) {
72
+ const cur = row[j];
73
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1].toLowerCase() === b[j - 1].toLowerCase() ? 0 : 1));
74
+ prev = cur;
75
+ }
76
+ }
77
+ return row[b.length];
78
+ }
79
+ function suggest(key, known) {
80
+ let best;
81
+ let bestD = Infinity;
82
+ for (const k of known) {
83
+ const d = distance(key, k);
84
+ if (d < bestD) {
85
+ best = k;
86
+ bestD = d;
87
+ }
88
+ }
89
+ if (best === void 0) return void 0;
90
+ const sameStart = key.slice(0, 3).toLowerCase() === best.slice(0, 3).toLowerCase();
91
+ return bestD <= 2 || bestD <= 3 && sameStart ? best : void 0;
92
+ }
93
+ function checkObject(value, shape, required, at, problems) {
94
+ const known = Object.keys(shape);
95
+ const unknown = Object.keys(value).filter((k) => !(k in shape));
96
+ for (const key of required) {
97
+ if (value[key] !== void 0) continue;
98
+ const near = unknown.find((u) => suggest(u, [key]) === key);
99
+ problems.push(near ? `missing required "${at}${key}" (found "${at}${near}" \u2014 did you mean "${at}${key}"?)` : `missing required "${at}${key}"`);
100
+ }
101
+ for (const key of unknown) {
102
+ const near = suggest(key, known);
103
+ if (near && required.includes(near) && value[near] === void 0) continue;
104
+ problems.push(`unknown key "${at}${key}"${near ? ` \u2014 did you mean "${at}${near}"?` : ""}`);
105
+ }
106
+ for (const key of known) {
107
+ if (value[key] !== void 0) checkValue(value[key], shape[key], `${at}${key}`, problems);
108
+ }
109
+ }
110
+ function checkValue(value, rule, at, problems) {
111
+ const fail = (expected) => problems.push(`"${at}" must be ${expected}`);
112
+ if (rule === "string") {
113
+ if (typeof value !== "string") fail("a string");
114
+ return;
115
+ }
116
+ if (rule === "boolean") {
117
+ if (typeof value !== "boolean") fail("true or false");
118
+ return;
119
+ }
120
+ if (rule === "strings") {
121
+ if (!isStrings(value)) fail("a list of strings");
122
+ return;
123
+ }
124
+ if (rule === "string-or-strings") {
125
+ if (typeof value !== "string" && !isStrings(value)) fail("a string or a list of strings");
126
+ return;
127
+ }
128
+ if (rule === "packages") {
129
+ const ok = Array.isArray(value) && value.every((p) => typeof p === "string" || isObject(p) && typeof p.name === "string" && (p.exclude === void 0 || isStrings(p.exclude)) && (p.styles === void 0 || typeof p.styles === "boolean"));
130
+ if (!ok) fail(`a list of package names or { name, exclude, styles } objects`);
131
+ return;
132
+ }
133
+ if (rule === "logo") {
134
+ const ok = typeof value === "string" || isObject(value) && typeof value.light === "string" && typeof value.dark === "string";
135
+ if (!ok) fail(`a path or { light, dark }`);
136
+ return;
137
+ }
138
+ if (rule === "workbench-url") {
139
+ let ok = false;
140
+ if (typeof value === "string" && WORKBENCH_PATH.test(value)) ok = true;
141
+ else if (typeof value === "string") {
142
+ try {
143
+ const url = new URL(value);
144
+ ok = /^https?:$/.test(url.protocol) && url.search === "" && url.hash === "" && !/[?#]/.test(value);
145
+ } catch {
146
+ ok = false;
147
+ }
148
+ }
149
+ if (!ok) fail(`an absolute http(s) URL for a workbench on its own host, such as "https://workbench.example.com/", or a path for one on the storefront's host, such as "/workbench/"`);
150
+ return;
151
+ }
152
+ if (rule === "site-url") {
153
+ let ok = false;
154
+ if (typeof value === "string") {
155
+ try {
156
+ const url = new URL(value);
157
+ ok = /^https?:$/.test(url.protocol) && url.pathname === "/" && url.search === "" && url.hash === "" && !/[?#]/.test(value);
158
+ } catch {
159
+ ok = false;
160
+ }
161
+ }
162
+ if (!ok) fail(`the site's absolute http(s) address, with no path, query or fragment, such as "https://example.com/"`);
163
+ return;
164
+ }
165
+ if (rule === "lang") {
166
+ if (typeof value !== "string" || !/^[a-z]{2,3}(?:-[a-z]{4})?(?:-(?:[a-z]{2}|\d{3}))?(?:-(?:[a-z\d]{5,8}|\d[a-z\d]{3}))*$/i.test(value)) {
167
+ fail(`a language tag, such as "en", "de-AT" or "zh-Hant-TW"`);
168
+ }
169
+ return;
170
+ }
171
+ if (rule === "asset-path") {
172
+ if (typeof value !== "string" || !/^\/assets\/[^?#]+[^/?#]$/.test(value)) {
173
+ fail(`a path to a file in the assets, such as "/assets/images/share.png"`);
174
+ }
175
+ return;
176
+ }
177
+ if (rule === "quality") {
178
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 100) fail("a whole number from 1 to 100 (WebP quality)");
179
+ return;
180
+ }
181
+ if (rule === "effort") {
182
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 6) fail("a whole number from 0 to 6 (WebP effort: 0 fastest, 6 smallest)");
183
+ return;
184
+ }
185
+ if (rule === "plain-object") {
186
+ if (!isObject(value)) fail(`an object of the site's values, such as { title: "My site" }`);
187
+ return;
188
+ }
189
+ if (rule === "host") {
190
+ if (typeof value !== "string" || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*$/i.test(value)) {
191
+ fail(`a host name with no scheme, port or path, such as "live.localhost"`);
192
+ }
193
+ return;
194
+ }
195
+ if ("values" in rule) {
196
+ if (typeof value === "string" && rule.unsupported?.includes(value)) {
197
+ problems.push(`"${at}": "${value}" is not supported yet \u2014 use ${rule.values.map((v) => `"${v}"`).join(" or ")}, or leave it out`);
198
+ } else if (typeof value !== "string" || !rule.values.includes(value)) {
199
+ fail(rule.values.map((v) => `"${v}"`).join(" or "));
200
+ }
201
+ return;
202
+ }
203
+ if (!isObject(value)) {
204
+ fail("an object");
205
+ return;
206
+ }
207
+ checkObject(value, rule.keys, rule.required ?? [], `${at}.`, problems);
208
+ }
209
+ var ConfigError = class extends Error {
210
+ name = "ConfigError";
211
+ };
212
+ function checkRelations(raw, problems) {
213
+ const sf = raw.storefront;
214
+ if (!isObject(sf)) return;
215
+ const folders = [
216
+ ["storefront.pagesDir", sf.pagesDir],
217
+ ["patternsDir", raw.patternsDir],
218
+ ["storefront.outDir", sf.outDir]
219
+ ];
220
+ const root = resolve(sep, "pd-root");
221
+ const inside = (child, parent) => {
222
+ const rel = relative(parent, child);
223
+ return rel !== "" && !rel.startsWith("..") && !rel.startsWith(sep) && !/^[a-zA-Z]:/.test(rel);
224
+ };
225
+ for (let i = 0; i < folders.length; i++) {
226
+ for (let j = i + 1; j < folders.length; j++) {
227
+ const [a, va] = folders[i], [b, vb] = folders[j];
228
+ if (typeof va !== "string" || typeof vb !== "string") continue;
229
+ const pa = resolve(root, va), pb = resolve(root, vb);
230
+ const label = (name, value) => `"${name}" ("${value}")`;
231
+ if (pa === pb) problems.push(`${label(a, va)} and ${label(b, vb)} are the same folder \u2014 each needs a folder of its own`);
232
+ else if (inside(pa, pb)) problems.push(`${label(a, va)} is inside ${label(b, vb)} \u2014 each needs a folder of its own, outside the other`);
233
+ else if (inside(pb, pa)) problems.push(`${label(b, vb)} is inside ${label(a, va)} \u2014 each needs a folder of its own, outside the other`);
234
+ }
235
+ }
236
+ }
237
+ function configProblems(raw) {
238
+ if (!isObject(raw)) return ["the config must be an object (a .ts config must `export default defineConfig({ \u2026 })`)"];
239
+ const problems = [];
240
+ checkObject(raw, SHAPE, REQUIRED, "", problems);
241
+ if (problems.length === 0) checkRelations(raw, problems);
242
+ return problems;
243
+ }
244
+ var withOneTrailingSlash = (url) => `${url.replace(/\/+$/, "")}/`;
245
+ function validateConfig(raw, source) {
246
+ const problems = configProblems(raw);
247
+ if (problems.length > 0) {
248
+ throw new ConfigError(`${source}: invalid config
249
+ ${problems.map((p) => ` - ${p}`).join("\n")}`);
250
+ }
251
+ const config = raw;
252
+ const sf = config.storefront;
253
+ if (!sf) return config;
254
+ const workbenchUrl = sf.workbenchUrl !== void 0 ? withOneTrailingSlash(sf.workbenchUrl) : void 0;
255
+ const url = sf.url !== void 0 ? `${new URL(sf.url).origin}/` : void 0;
256
+ if (workbenchUrl === sf.workbenchUrl && url === sf.url) return config;
257
+ return { ...config, storefront: {
258
+ ...sf,
259
+ ...workbenchUrl !== void 0 ? { workbenchUrl } : {},
260
+ ...url !== void 0 ? { url } : {}
261
+ } };
262
+ }
263
+ function sameHostWorkbenchPath(storefront) {
264
+ const w = storefront?.workbenchUrl;
265
+ if (w === void 0) return void 0;
266
+ if (w.startsWith("/")) return w;
267
+ if (storefront?.url === void 0) return void 0;
268
+ const u = new URL(w);
269
+ return `${u.origin}/` === storefront.url ? u.pathname : void 0;
270
+ }
271
+
272
+ // packages/config/src/index.ts
273
+ function defineConfig(config) {
274
+ return config;
275
+ }
276
+
277
+ export {
278
+ suggest,
279
+ ConfigError,
280
+ validateConfig,
281
+ sameHostWorkbenchPath,
282
+ defineConfig
283
+ };