pattern-dump 0.1.0-alpha.4 → 0.3.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 +106 -1
  2. package/LICENSE +16 -3
  3. package/README.md +7 -7
  4. package/THIRD-PARTY-NOTICES.md +130 -0
  5. package/dist/chunks/chunk-5WFBNPXB.js +294 -0
  6. package/dist/dev-ui/main.js +215 -77
  7. package/dist/index.js +1 -1
  8. package/dist/pd.js +5833 -881
  9. package/dist/types/config/index.d.ts +93 -2
  10. package/dist/types/config/validate.d.ts +19 -1
  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 +9 -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-NXGPZL5L.js +0 -160
package/CHANGELOG.md CHANGED
@@ -1,8 +1,113 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ From this release Pattern Dump is published as plain 0.x versions under npm's `latest` tag: install it with
6
+ `npx pattern-dump init`, without `@alpha`. Being at 0.x, breaking changes are possible between minor versions, and
7
+ this changelog names them.
8
+
9
+ ### Added
10
+
11
+ - `sourceDir`, `patternsDir` and `styles` are optional. A site whose components, skin and styles all come from
12
+ pattern packages names none of them; they default to `./src`, `./src/patterns` and `./src/styles`, and the folders
13
+ don't have to exist.
14
+
15
+ ### Changed
16
+
17
+ - `pd build` now checks every page of a storefront with axe before it writes anything, and **stops on an
18
+ accessibility error**: a WCAG rule rated critical or serious (an image without alt text, a button with no name, a
19
+ page without a language), or a page without exactly one `<main>` and one `<h1>`. Each error is printed with its
20
+ page, the rule, the elements it was found on and a link to the rule's explanation; moderate and minor findings,
21
+ and axe's best practices, are listed as warnings and don't stop the build. To see them before a build, run
22
+ `pd build` locally: nothing is written while there are errors. In `pd dev`, a page without one `<main>` and one
23
+ `<h1>` is a warning in the terminal.
24
+
25
+ ## 0.2.0-alpha.0
26
+
27
+ The storefront: a website written as markdown pages, dressed by a skin made of ordinary components, served by
28
+ `pd dev` on a host of its own and built by `pd build` next to the workbench. With it come responsive images, link
29
+ and image checks, and live examples of your components on the site's pages.
30
+
31
+ Pattern Dump now needs Node.js 22.22 or later (it was 20.10).
32
+
33
+ ### Workbench
34
+
35
+ - Pattern packages linked into a project (a `file:` dependency or a workspace) are watched like the project's own
36
+ files, and each is read from its real folder. Global data, assets and helper modules reload while `pd dev` runs.
37
+ - A pattern package's TypeScript helpers are refused with a message naming them, and diagnostics name files by
38
+ project or package path, never by an absolute path.
39
+ - Live reload is dependable: changes are handled one at a time, in the order they happen; a page or preview that
40
+ missed a change fetches it again and never shows an older one; a save the file watcher drops is still handled; a
41
+ folder added while `pd dev` runs (as `git checkout` or `git pull` does) is seen with every file in it.
42
+ - A component added while `pd dev` runs gets its own stylesheet even when the stylesheet is saved before the
43
+ template.
44
+ - A variant reached by its own id (`/preview/atoms/toggle-demo~alt`) is the same document as `?variant=alt`, with
45
+ its base's behaviour scripts, layout and CSS.
46
+ - An overview tile picks up a stylesheet that was still empty when it rendered.
47
+ - Behaviour scripts are stamped with their content hash, so a browser fetches a script again exactly when it
48
+ changed.
49
+ - Every base-layer file whose rules no component owns and no `documentBase` entry lists is named in
50
+ `BASE-LAYER.generated.md`, with its rule count.
51
+
52
+ ### Storefront
53
+
54
+ - Pages: every markdown file in `storefront.pagesDir` is a page, rendered with a layout from your patterns. Its
55
+ frontmatter is checked against the layout's schema, `storefront.site` against `partials/base`'s, and every
56
+ problem names its file and line. `pd dev` serves the site at `http://live.localhost:4000/`, `pd build` writes it
57
+ to `storefront.outDir`.
58
+ - Live examples: a `pattern` fence shows a component's own preview in a frame, with links to its variants. A
59
+ component can declare the height its example starts at (`x-pd-example-height`), and `pd dev` warns when that
60
+ height is too tall.
61
+ - Placing components with values: `<pd-…>` tags in a page's markdown, checked against the component's schema at
62
+ every level (`x-pd-component`, `x-pd-content`, `text/markdown` properties), with the file, line and column of any
63
+ problem.
64
+ - What a page knows about itself, for layouts that ask for it: its headings (with GitHub's anchors), section,
65
+ breadcrumbs, previous and next pages, dates from git, and declared navigations.
66
+ - Search engines and link previews: canonical URLs, Open Graph, the site's language, JSON-LD, `sitemap.xml`,
67
+ `robots.txt`, a 404 page, `redirectFrom` for old routes, `index: false`, and a link-preview image per page or site.
68
+ - A skin can add to every page's `<head>` through a `partials/head` component, and can be a pattern package.
69
+ - `pd init --storefront` starts a project with a website: pages, a plain skin, the workbench under `/workbench/`
70
+ on the same host, and a GitHub Pages workflow.
71
+ - Every file a page references (stylesheets, scripts, fonts, images) carries the content hash of the file as
72
+ served, so a visitor's browser fetches it again exactly when it changed.
73
+
74
+ ### Images
75
+
76
+ - Every raster image is served as WebP variants of the widths it is shown at, encoded with sharp on first use and
77
+ cached in `node_modules/.cache/pattern-dump/images/`. `images` (quality, effort) sets the encoding, and
78
+ `storefront.images` overrides it for the site.
79
+ - Image slots: a component declares where an image goes and how wide it is shown (`x-pd-image`), and its template
80
+ writes it with `{{pd-image field alt=…}}`. Alt text is required; `alt=""` marks a decorative image.
81
+ - Images in a page's markdown get their variants in the slot the page's layout declares (`x-pd-prose-images`),
82
+ with `sizes="auto"`, and the first ones can load at once (`eager`).
83
+ - Every `<img>` carries its width and height, so the page doesn't shift as images load. Photos are turned upright
84
+ by their EXIF orientation.
85
+
86
+ ### Checks
87
+
88
+ - `pd build` checks every link on every page of the site before it writes anything: pages, files, `#` anchors (a
89
+ heading's too), the site's own address and links into the workbench. A broken link stops the build; links to other
90
+ sites are listed in the build's notes. `pd dev` warns about a page's links to pages that don't exist.
91
+ - `pd build` refuses a page with an `<img>` that has no width and height, or a raster image served without its WebP
92
+ variants, and names the fix; `pd dev` shows the same as warnings.
93
+ - A schema field named like a registered helper is an error, and so is an unknown `x-pd-` key in a schema.
94
+ - The shape check tells a list from an object (an array property was reported as a mismatch).
95
+
96
+ ### Fixes
97
+
98
+ - `pd dev` answers only requests addressed to its own host names, accepts live-reload connections only from its own
99
+ pages, binds its live-reload socket to 127.0.0.1 like its HTTP server, and serves files only from inside each
100
+ route's folder.
101
+ - The starter's body defaults no longer override a pattern package's document styles (hTWOo's dark themes showed
102
+ dark text on a white page).
103
+ - Two config loads in the same millisecond no longer share a temporary file.
104
+ - The audit reuses a cached result only when it was made by the same version of the check.
105
+ - The live-reload socket reports the port it is actually bound to.
106
+ - A code example on a page that quotes `<img src="/assets/…">` is shown as written, not with a cache stamp added.
107
+
3
108
  ## 0.1.0-alpha.4
4
109
 
5
- Fixes found while moving a third PatternLab styleguide (CTS) to Pattern Dump with hTWOo consumed only as
110
+ Fixes found while moving a third PatternLab styleguide to Pattern Dump with hTWOo consumed only as
6
111
  pattern packages: its templates, data, scripts and CSS all come from `@n8d/htwoo-patterns` and
7
112
  `@n8d/htwoo-core`, and the project compiles only its own SCSS.
8
113
 
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,19 +4,19 @@ 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.4).** Published under the `alpha` tag. Options and file conventions can still change
8
- > between releases.
7
+ > **0.3.0.** Pattern Dump is at 0.x, so breaking changes are possible between minor versions (0.3 to 0.4);
8
+ > the [changelog](https://github.com/n8design/pattern-dump/blob/main/CHANGELOG.md) names them.
9
9
 
10
10
  ## Requirements
11
11
 
12
- Node.js 20.10 or later, and npm.
12
+ Node.js 22.22 or later, and npm.
13
13
 
14
14
  ## Quick start
15
15
 
16
16
  Create a new project:
17
17
 
18
18
  ```bash
19
- npx pattern-dump@alpha init my-design-system
19
+ npx pattern-dump init my-design-system
20
20
  cd my-design-system
21
21
  npm run dev # → http://localhost:4000
22
22
  ```
@@ -24,7 +24,7 @@ npm run dev # → http://localhost:4000
24
24
  Or add Pattern Dump to an existing project:
25
25
 
26
26
  ```bash
27
- npm install --save-dev pattern-dump@alpha
27
+ npm install --save-dev pattern-dump
28
28
  npx pd init
29
29
  ```
30
30
 
@@ -35,7 +35,7 @@ overwrites an existing file.
35
35
  ### Installing `pd` globally
36
36
 
37
37
  ```bash
38
- npm install --global pattern-dump@alpha
38
+ npm install --global pattern-dump
39
39
  pd init my-design-system
40
40
  ```
41
41
 
@@ -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,294 @@
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 = ["outDir"];
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 withSourceDefaults(config) {
246
+ if (config.sourceDir !== void 0 && config.patternsDir !== void 0 && config.styles !== void 0) return config;
247
+ const sourceDir = config.sourceDir ?? "./src";
248
+ const base = sourceDir.replace(/\/+$/, "");
249
+ return {
250
+ ...config,
251
+ sourceDir,
252
+ patternsDir: config.patternsDir ?? `${base}/patterns`,
253
+ styles: config.styles ?? { strategy: "parallel", dir: `${base}/styles` }
254
+ };
255
+ }
256
+ function validateConfig(raw, source) {
257
+ const problems = configProblems(raw);
258
+ if (problems.length > 0) {
259
+ throw new ConfigError(`${source}: invalid config
260
+ ${problems.map((p) => ` - ${p}`).join("\n")}`);
261
+ }
262
+ const config = withSourceDefaults(raw);
263
+ const sf = config.storefront;
264
+ if (!sf) return config;
265
+ const workbenchUrl = sf.workbenchUrl !== void 0 ? withOneTrailingSlash(sf.workbenchUrl) : void 0;
266
+ const url = sf.url !== void 0 ? `${new URL(sf.url).origin}/` : void 0;
267
+ if (workbenchUrl === sf.workbenchUrl && url === sf.url) return config;
268
+ return { ...config, storefront: {
269
+ ...sf,
270
+ ...workbenchUrl !== void 0 ? { workbenchUrl } : {},
271
+ ...url !== void 0 ? { url } : {}
272
+ } };
273
+ }
274
+ function sameHostWorkbenchPath(storefront) {
275
+ const w = storefront?.workbenchUrl;
276
+ if (w === void 0) return void 0;
277
+ if (w.startsWith("/")) return w;
278
+ if (storefront?.url === void 0) return void 0;
279
+ const u = new URL(w);
280
+ return `${u.origin}/` === storefront.url ? u.pathname : void 0;
281
+ }
282
+
283
+ // packages/config/src/index.ts
284
+ function defineConfig(config) {
285
+ return withSourceDefaults(config);
286
+ }
287
+
288
+ export {
289
+ suggest,
290
+ ConfigError,
291
+ validateConfig,
292
+ sameHostWorkbenchPath,
293
+ defineConfig
294
+ };