pattern-dump 0.2.0-alpha.0 → 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.
- package/CHANGELOG.md +22 -0
- package/README.md +6 -6
- package/dist/chunks/{chunk-ESMZAOP2.js → chunk-5WFBNPXB.js} +14 -3
- package/dist/index.js +1 -1
- package/dist/pd.js +272 -63
- package/dist/types/config/index.d.ts +8 -2
- package/dist/types/config/validate.d.ts +7 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
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
|
+
|
|
3
25
|
## 0.2.0-alpha.0
|
|
4
26
|
|
|
5
27
|
The storefront: a website written as markdown pages, dressed by a skin made of ordinary components, served by
|
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
|
-
> **
|
|
8
|
-
>
|
|
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
|
|
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
|
|
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
|
|
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
|
|
38
|
+
npm install --global pattern-dump
|
|
39
39
|
pd init my-design-system
|
|
40
40
|
```
|
|
41
41
|
|
|
@@ -59,7 +59,7 @@ var SHAPE = {
|
|
|
59
59
|
required: ["pagesDir", "outDir"]
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
|
-
var REQUIRED = ["
|
|
62
|
+
var REQUIRED = ["outDir"];
|
|
63
63
|
var WORKBENCH_PATH = /^\/(?:(?!\.{1,2}(?:\/|$))[a-z0-9._~-]+\/?)+$/i;
|
|
64
64
|
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
65
65
|
var isStrings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
@@ -242,13 +242,24 @@ function configProblems(raw) {
|
|
|
242
242
|
return problems;
|
|
243
243
|
}
|
|
244
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
|
+
}
|
|
245
256
|
function validateConfig(raw, source) {
|
|
246
257
|
const problems = configProblems(raw);
|
|
247
258
|
if (problems.length > 0) {
|
|
248
259
|
throw new ConfigError(`${source}: invalid config
|
|
249
260
|
${problems.map((p) => ` - ${p}`).join("\n")}`);
|
|
250
261
|
}
|
|
251
|
-
const config = raw;
|
|
262
|
+
const config = withSourceDefaults(raw);
|
|
252
263
|
const sf = config.storefront;
|
|
253
264
|
if (!sf) return config;
|
|
254
265
|
const workbenchUrl = sf.workbenchUrl !== void 0 ? withOneTrailingSlash(sf.workbenchUrl) : void 0;
|
|
@@ -271,7 +282,7 @@ function sameHostWorkbenchPath(storefront) {
|
|
|
271
282
|
|
|
272
283
|
// packages/config/src/index.ts
|
|
273
284
|
function defineConfig(config) {
|
|
274
|
-
return config;
|
|
285
|
+
return withSourceDefaults(config);
|
|
275
286
|
}
|
|
276
287
|
|
|
277
288
|
export {
|
package/dist/index.js
CHANGED
package/dist/pd.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
sameHostWorkbenchPath,
|
|
5
5
|
suggest,
|
|
6
6
|
validateConfig
|
|
7
|
-
} from "./chunks/chunk-
|
|
7
|
+
} from "./chunks/chunk-5WFBNPXB.js";
|
|
8
8
|
|
|
9
9
|
// packages/contracts/src/dev-protocol.ts
|
|
10
10
|
var AUDIT_SCOPE = {
|
|
@@ -335,9 +335,9 @@ async function buildDiscoveryMap(roots) {
|
|
|
335
335
|
const dir = dirname(absPath);
|
|
336
336
|
const stem = basename(absPath, ".hbs");
|
|
337
337
|
const fileSet = dirFileSets.get(dir) ?? /* @__PURE__ */ new Set();
|
|
338
|
-
const
|
|
338
|
+
const source2 = rootOf.get(absPath)?.root.source;
|
|
339
339
|
return {
|
|
340
|
-
...
|
|
340
|
+
...source2 ? { source: source2 } : {},
|
|
341
341
|
hbsPath: absPath,
|
|
342
342
|
jsonPath: fileSet.has(`${stem}.json`) ? join(dir, `${stem}.json`) : void 0,
|
|
343
343
|
mdPath: fileSet.has(`${stem}.md`) ? join(dir, `${stem}.md`) : void 0,
|
|
@@ -395,8 +395,8 @@ async function buildDiscoveryMap(roots) {
|
|
|
395
395
|
const baseName = basename(varPaths[0], ".hbs").split("~")[0];
|
|
396
396
|
const fileSet = dirFileSets.get(varDir) ?? /* @__PURE__ */ new Set();
|
|
397
397
|
const mdPath = fileSet.has(`${baseName}.md`) ? join(varDir, `${baseName}.md`) : void 0;
|
|
398
|
-
const
|
|
399
|
-
discoveryMap.set(baseId, { ...
|
|
398
|
+
const source2 = rootOf.get(varPaths[0])?.root.source;
|
|
399
|
+
discoveryMap.set(baseId, { ...source2 ? { source: source2 } : {}, hbsPath: "", ownMarkup: false, variants: variantTokens, mdPath });
|
|
400
400
|
handleMap.set(handle, baseId);
|
|
401
401
|
}
|
|
402
402
|
}
|
|
@@ -426,8 +426,8 @@ async function buildDiscoveryMap(roots) {
|
|
|
426
426
|
if (!baseEntry?.hbsPath) continue;
|
|
427
427
|
const variantHandle = `${deriveHandle(baseId)}~${variantToken}`;
|
|
428
428
|
if (!handleMap.has(variantHandle)) {
|
|
429
|
-
const
|
|
430
|
-
discoveryMap.set(variantId, { ...
|
|
429
|
+
const source2 = rootOf.get(jsonPath)?.root.source;
|
|
430
|
+
discoveryMap.set(variantId, { ...source2 ? { source: source2 } : {}, hbsPath: baseEntry.hbsPath, jsonPath, ownMarkup: false });
|
|
431
431
|
handleMap.set(variantHandle, variantId);
|
|
432
432
|
}
|
|
433
433
|
}
|
|
@@ -911,9 +911,9 @@ var htmlValidator = new HtmlValidate({
|
|
|
911
911
|
}
|
|
912
912
|
});
|
|
913
913
|
function deepMerge(target, ...sources) {
|
|
914
|
-
for (const
|
|
915
|
-
if (!
|
|
916
|
-
for (const [key, value] of Object.entries(
|
|
914
|
+
for (const source2 of sources) {
|
|
915
|
+
if (!source2) continue;
|
|
916
|
+
for (const [key, value] of Object.entries(source2)) {
|
|
917
917
|
const targetVal = target[key];
|
|
918
918
|
if (value !== null && typeof value === "object" && !Array.isArray(value) && targetVal !== null && typeof targetVal === "object" && !Array.isArray(targetVal)) {
|
|
919
919
|
deepMerge(targetVal, value);
|
|
@@ -955,16 +955,16 @@ async function readDataFile(path, display) {
|
|
|
955
955
|
};
|
|
956
956
|
}
|
|
957
957
|
}
|
|
958
|
-
function imageTag(
|
|
958
|
+
function imageTag(source2, o) {
|
|
959
959
|
const esc = Handlebars3.escapeExpression;
|
|
960
960
|
const rest = `alt="${esc(o.alt)}"${o.title !== void 0 ? ` title="${esc(o.title)}"` : ""} loading="${o.eager ? "eager" : "lazy"}" decoding="async"`;
|
|
961
|
-
if (
|
|
962
|
-
const widths = o.widths.filter((w) => w <=
|
|
963
|
-
if (widths.length === 0) widths.push(
|
|
964
|
-
const urls = widths.map((w) =>
|
|
961
|
+
if (source2.kind === "single") return `<img src="${esc(source2.url)}" width="${source2.width}" height="${source2.height}" ${rest}>`;
|
|
962
|
+
const widths = o.widths.filter((w) => w <= source2.width);
|
|
963
|
+
if (widths.length === 0) widths.push(source2.width);
|
|
964
|
+
const urls = widths.map((w) => source2.variantUrl(w, o.settings));
|
|
965
965
|
const top = widths[widths.length - 1];
|
|
966
966
|
const srcset = urls.map((u, i) => `${esc(u)} ${widths[i]}w`).join(", ");
|
|
967
|
-
return `<img src="${esc(urls[urls.length - 1])}" srcset="${srcset}" sizes="${esc(o.sizes)}" width="${top}" height="${Math.round(top *
|
|
967
|
+
return `<img src="${esc(urls[urls.length - 1])}" srcset="${srcset}" sizes="${esc(o.sizes)}" width="${top}" height="${Math.round(top * source2.height / source2.width)}" ${rest}>`;
|
|
968
968
|
}
|
|
969
969
|
function registerImageHelper(env, images) {
|
|
970
970
|
env.registerHelper("pd-image", function(value, options) {
|
|
@@ -979,11 +979,11 @@ function registerImageHelper(env, images) {
|
|
|
979
979
|
}
|
|
980
980
|
if (typeof value !== "string" || value === "") throw new Error(`{{pd-image}}: the image field is empty`);
|
|
981
981
|
if (!images) throw new Error(`{{pd-image}}: this renderer was given no image index`);
|
|
982
|
-
const
|
|
983
|
-
if (!
|
|
984
|
-
if (
|
|
982
|
+
const source2 = images(value);
|
|
983
|
+
if (!source2) throw new Error(`{{pd-image}}: "${value}" is not an image in the assets`);
|
|
984
|
+
if (source2.kind === "unavailable") throw new Error(source2.reason);
|
|
985
985
|
const settings = options.data?.pdImages;
|
|
986
|
-
return new env.SafeString(imageTag(
|
|
986
|
+
return new env.SafeString(imageTag(source2, {
|
|
987
987
|
widths: slot.widths,
|
|
988
988
|
sizes: slot.sizes,
|
|
989
989
|
alt: String(alt),
|
|
@@ -1373,21 +1373,21 @@ function loadSharp(importer) {
|
|
|
1373
1373
|
if (!importer) loaded = attempt;
|
|
1374
1374
|
return attempt;
|
|
1375
1375
|
}
|
|
1376
|
-
function imageVariantKey(
|
|
1377
|
-
return imageVariantKeyFromSourceHash(imageSourceHash(
|
|
1376
|
+
function imageVariantKey(source2, width, encoding) {
|
|
1377
|
+
return imageVariantKeyFromSourceHash(imageSourceHash(source2), width, encoding);
|
|
1378
1378
|
}
|
|
1379
|
-
function imageSourceHash(
|
|
1380
|
-
return createHash("sha256").update(
|
|
1379
|
+
function imageSourceHash(source2) {
|
|
1380
|
+
return createHash("sha256").update(source2).digest("hex");
|
|
1381
1381
|
}
|
|
1382
1382
|
function imageVariantKeyFromSourceHash(sourceHash, width, encoding) {
|
|
1383
1383
|
return createHash("sha256").update(`${sourceHash}|webp|orient=auto|w=${width}|q=${encoding.quality}|e=${encoding.effort}|sharp=${SHARP_VERSION}`).digest("hex").slice(0, 10);
|
|
1384
1384
|
}
|
|
1385
|
-
async function imageSize(sharp,
|
|
1386
|
-
const meta = await sharp(
|
|
1385
|
+
async function imageSize(sharp, source2) {
|
|
1386
|
+
const meta = await sharp(source2).metadata();
|
|
1387
1387
|
return { width: meta.autoOrient?.width ?? meta.width ?? 0, height: meta.autoOrient?.height ?? meta.height ?? 0 };
|
|
1388
1388
|
}
|
|
1389
|
-
function encodeWebp(sharp,
|
|
1390
|
-
return sharp(
|
|
1389
|
+
function encodeWebp(sharp, source2, width, encoding) {
|
|
1390
|
+
return sharp(source2, { autoOrient: true }).resize({ width, withoutEnlargement: true }).webp({ quality: encoding.quality, effort: encoding.effort }).toBuffer();
|
|
1391
1391
|
}
|
|
1392
1392
|
function createImageCache(dir) {
|
|
1393
1393
|
const file = (key) => join2(dir, `${key}.webp`);
|
|
@@ -2960,6 +2960,163 @@ async function runA11yProducer(html) {
|
|
|
2960
2960
|
return diagnostics;
|
|
2961
2961
|
}
|
|
2962
2962
|
|
|
2963
|
+
// packages/a11y/src/page.ts
|
|
2964
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
2965
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
2966
|
+
import { JSDOM as JSDOM2 } from "jsdom";
|
|
2967
|
+
|
|
2968
|
+
// packages/a11y/src/page-rule-tiers.generated.json
|
|
2969
|
+
var page_rule_tiers_generated_default = {
|
|
2970
|
+
_note: "Generated by packages/a11y/src/page-classifier.ts \u2014 do not hand-edit. Re-run: npm run classify-pages --workspace @pattern-dump/a11y",
|
|
2971
|
+
axeVersion: "4.11.4",
|
|
2972
|
+
tags: [
|
|
2973
|
+
"wcag2a",
|
|
2974
|
+
"wcag2aa",
|
|
2975
|
+
"wcag21a",
|
|
2976
|
+
"wcag21aa",
|
|
2977
|
+
"wcag22aa",
|
|
2978
|
+
"best-practice"
|
|
2979
|
+
],
|
|
2980
|
+
jsdom: [
|
|
2981
|
+
"accesskeys",
|
|
2982
|
+
"area-alt",
|
|
2983
|
+
"aria-allowed-attr",
|
|
2984
|
+
"aria-allowed-role",
|
|
2985
|
+
"aria-command-name",
|
|
2986
|
+
"aria-conditional-attr",
|
|
2987
|
+
"aria-deprecated-role",
|
|
2988
|
+
"aria-dialog-name",
|
|
2989
|
+
"aria-hidden-body",
|
|
2990
|
+
"aria-input-field-name",
|
|
2991
|
+
"aria-meter-name",
|
|
2992
|
+
"aria-progressbar-name",
|
|
2993
|
+
"aria-required-attr",
|
|
2994
|
+
"aria-required-children",
|
|
2995
|
+
"aria-required-parent",
|
|
2996
|
+
"aria-roledescription",
|
|
2997
|
+
"aria-roles",
|
|
2998
|
+
"aria-text",
|
|
2999
|
+
"aria-toggle-field-name",
|
|
3000
|
+
"aria-tooltip-name",
|
|
3001
|
+
"aria-treeitem-name",
|
|
3002
|
+
"aria-valid-attr",
|
|
3003
|
+
"aria-valid-attr-value",
|
|
3004
|
+
"autocomplete-valid",
|
|
3005
|
+
"blink",
|
|
3006
|
+
"button-name",
|
|
3007
|
+
"definition-list",
|
|
3008
|
+
"dlitem",
|
|
3009
|
+
"document-title",
|
|
3010
|
+
"empty-heading",
|
|
3011
|
+
"empty-table-header",
|
|
3012
|
+
"frame-title",
|
|
3013
|
+
"heading-order",
|
|
3014
|
+
"html-has-lang",
|
|
3015
|
+
"html-lang-valid",
|
|
3016
|
+
"html-xml-lang-mismatch",
|
|
3017
|
+
"image-alt",
|
|
3018
|
+
"image-redundant-alt",
|
|
3019
|
+
"input-button-name",
|
|
3020
|
+
"input-image-alt",
|
|
3021
|
+
"label",
|
|
3022
|
+
"label-title-only",
|
|
3023
|
+
"landmark-banner-is-top-level",
|
|
3024
|
+
"landmark-contentinfo-is-top-level",
|
|
3025
|
+
"landmark-main-is-top-level",
|
|
3026
|
+
"landmark-no-duplicate-banner",
|
|
3027
|
+
"landmark-no-duplicate-contentinfo",
|
|
3028
|
+
"landmark-no-duplicate-main",
|
|
3029
|
+
"landmark-unique",
|
|
3030
|
+
"link-name",
|
|
3031
|
+
"list",
|
|
3032
|
+
"listitem",
|
|
3033
|
+
"meta-refresh",
|
|
3034
|
+
"meta-viewport",
|
|
3035
|
+
"nested-interactive",
|
|
3036
|
+
"presentation-role-conflict",
|
|
3037
|
+
"region",
|
|
3038
|
+
"role-img-alt",
|
|
3039
|
+
"scope-attr-valid",
|
|
3040
|
+
"select-name",
|
|
3041
|
+
"summary-name",
|
|
3042
|
+
"svg-img-alt",
|
|
3043
|
+
"tabindex",
|
|
3044
|
+
"table-duplicate-name",
|
|
3045
|
+
"td-headers-attr",
|
|
3046
|
+
"valid-lang"
|
|
3047
|
+
],
|
|
3048
|
+
browser: [
|
|
3049
|
+
"aria-braille-equivalent",
|
|
3050
|
+
"aria-hidden-focus",
|
|
3051
|
+
"aria-prohibited-attr",
|
|
3052
|
+
"audio-caption",
|
|
3053
|
+
"avoid-inline-spacing",
|
|
3054
|
+
"bypass",
|
|
3055
|
+
"color-contrast",
|
|
3056
|
+
"css-orientation-lock",
|
|
3057
|
+
"duplicate-id-aria",
|
|
3058
|
+
"focus-order-semantics",
|
|
3059
|
+
"form-field-multiple-labels",
|
|
3060
|
+
"frame-focusable-content",
|
|
3061
|
+
"frame-tested",
|
|
3062
|
+
"frame-title-unique",
|
|
3063
|
+
"hidden-content",
|
|
3064
|
+
"label-content-name-mismatch",
|
|
3065
|
+
"landmark-complementary-is-top-level",
|
|
3066
|
+
"landmark-one-main",
|
|
3067
|
+
"link-in-text-block",
|
|
3068
|
+
"marquee",
|
|
3069
|
+
"meta-viewport-large",
|
|
3070
|
+
"no-autoplay-audio",
|
|
3071
|
+
"object-alt",
|
|
3072
|
+
"p-as-heading",
|
|
3073
|
+
"page-has-heading-one",
|
|
3074
|
+
"scrollable-region-focusable",
|
|
3075
|
+
"server-side-image-map",
|
|
3076
|
+
"skip-link",
|
|
3077
|
+
"table-fake-caption",
|
|
3078
|
+
"target-size",
|
|
3079
|
+
"td-has-header",
|
|
3080
|
+
"th-has-data-cells",
|
|
3081
|
+
"video-caption"
|
|
3082
|
+
]
|
|
3083
|
+
};
|
|
3084
|
+
|
|
3085
|
+
// packages/a11y/src/page.ts
|
|
3086
|
+
var require4 = createRequire3(import.meta.url);
|
|
3087
|
+
var axeSource;
|
|
3088
|
+
var source = () => axeSource ??= readFileSync2(require4.resolve("axe-core/axe.min.js"), "utf8");
|
|
3089
|
+
var PAGE_TAGS = ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa", "best-practice"];
|
|
3090
|
+
async function runAxeOnPage(html, rules) {
|
|
3091
|
+
const dom = new JSDOM2(html, { url: "http://site.invalid/", runScripts: "outside-only", pretendToBeVisual: true });
|
|
3092
|
+
dom.virtualConsole.removeAllListeners();
|
|
3093
|
+
try {
|
|
3094
|
+
dom.window.eval(source());
|
|
3095
|
+
const axe2 = dom.window.axe;
|
|
3096
|
+
const ruleIds = axe2.getRules(PAGE_TAGS).map((r2) => r2.ruleId);
|
|
3097
|
+
const options = rules.length ? { runOnly: { type: "rule", values: rules } } : { runOnly: { type: "tag", values: PAGE_TAGS } };
|
|
3098
|
+
const r = await axe2.run(dom.window.document, { ...options, resultTypes: ["violations", "incomplete"] });
|
|
3099
|
+
return { violations: r.violations, incomplete: r.incomplete, axeVersion: axe2.version, ruleIds };
|
|
3100
|
+
} finally {
|
|
3101
|
+
dom.window.close();
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
function severityOf(impact, tags) {
|
|
3105
|
+
const wcag = tags.some((t) => /^wcag\d/.test(t));
|
|
3106
|
+
return wcag && (impact === "critical" || impact === "serious") ? "error" : "warning";
|
|
3107
|
+
}
|
|
3108
|
+
async function runPageA11y(html) {
|
|
3109
|
+
const { violations } = await runAxeOnPage(html, page_rule_tiers_generated_default.jsdom);
|
|
3110
|
+
return violations.map((v) => ({
|
|
3111
|
+
ruleId: v.id,
|
|
3112
|
+
severity: severityOf(v.impact, v.tags),
|
|
3113
|
+
impact: v.impact ?? "minor",
|
|
3114
|
+
help: v.help,
|
|
3115
|
+
helpUrl: v.helpUrl,
|
|
3116
|
+
nodes: v.nodes.map((n) => ({ target: n.target.map(String).join(" "), html: n.html }))
|
|
3117
|
+
}));
|
|
3118
|
+
}
|
|
3119
|
+
|
|
2963
3120
|
// packages/dev-server/src/diagnostics.ts
|
|
2964
3121
|
async function checkComponent(componentId, variant, render, quarantineEntry) {
|
|
2965
3122
|
if (quarantineEntry) {
|
|
@@ -3119,7 +3276,7 @@ async function persistSlices(root, slices) {
|
|
|
3119
3276
|
}
|
|
3120
3277
|
|
|
3121
3278
|
// packages/dev-server/src/packages.ts
|
|
3122
|
-
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as
|
|
3279
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
|
|
3123
3280
|
import { dirname as dirname5, join as join13, sep as sep3 } from "node:path";
|
|
3124
3281
|
function findPackageDir(name, root) {
|
|
3125
3282
|
let dir = root;
|
|
@@ -3164,7 +3321,7 @@ function loadPatternPackages(references, root) {
|
|
|
3164
3321
|
throw new Error(`pattern package "${name}" is not installed \u2014 run: npm install --save-dev ${name}`);
|
|
3165
3322
|
}
|
|
3166
3323
|
const dir = realpathSync2(via);
|
|
3167
|
-
const pkg = JSON.parse(
|
|
3324
|
+
const pkg = JSON.parse(readFileSync3(join13(dir, "package.json"), "utf8"));
|
|
3168
3325
|
if (!pkg.patternDump) {
|
|
3169
3326
|
if (!direct) return;
|
|
3170
3327
|
throw new Error(
|
|
@@ -3441,8 +3598,8 @@ async function serveImageVariant(pathname, host, res, cfg) {
|
|
|
3441
3598
|
const variant = parseImageUrl(pathname);
|
|
3442
3599
|
if (!variant) return notFound();
|
|
3443
3600
|
const encoding = cfg.storefront && host === cfg.storefront.host ? cfg.storefront.images : cfg.images;
|
|
3444
|
-
const
|
|
3445
|
-
if (!
|
|
3601
|
+
const source2 = await variantSource(variant.name, variant.hash, variant.width, encoding, cfg);
|
|
3602
|
+
if (!source2) return notFound();
|
|
3446
3603
|
let sharp;
|
|
3447
3604
|
try {
|
|
3448
3605
|
sharp = await loadSharp();
|
|
@@ -3453,11 +3610,11 @@ async function serveImageVariant(pathname, host, res, cfg) {
|
|
|
3453
3610
|
res.end(e.message);
|
|
3454
3611
|
return;
|
|
3455
3612
|
}
|
|
3456
|
-
if (variant.width > (await imageSize(sharp,
|
|
3613
|
+
if (variant.width > (await imageSize(sharp, source2)).width) return notFound();
|
|
3457
3614
|
const cache = cacheFor(cfg);
|
|
3458
3615
|
let bytes = await cache.read(variant.hash);
|
|
3459
3616
|
if (!bytes) {
|
|
3460
|
-
bytes = await encodeWebp(sharp,
|
|
3617
|
+
bytes = await encodeWebp(sharp, source2, variant.width, encoding);
|
|
3461
3618
|
await cache.write(variant.hash, bytes);
|
|
3462
3619
|
}
|
|
3463
3620
|
res.writeHead(200, { "Content-Type": "image/webp", "Cache-Control": "public, max-age=31536000, immutable" });
|
|
@@ -3755,6 +3912,22 @@ function checkLinks(site) {
|
|
|
3755
3912
|
return { problems, external, references };
|
|
3756
3913
|
}
|
|
3757
3914
|
|
|
3915
|
+
// packages/dev-server/src/storefront/page-basics.ts
|
|
3916
|
+
function checkPageBasics(html) {
|
|
3917
|
+
let main = 0, h1 = 0;
|
|
3918
|
+
for (const tag of startTags(html)) {
|
|
3919
|
+
const name = tag.name.toLowerCase();
|
|
3920
|
+
if (name === "main" || attributesOf(tag).some((a) => a.name === "role" && a.value?.trim() === "main")) main++;
|
|
3921
|
+
if (name === "h1") h1++;
|
|
3922
|
+
}
|
|
3923
|
+
const problems = [];
|
|
3924
|
+
if (main === 0) problems.push("the page has no <main>: the page's own content goes in one, so assistive technology can go straight to it");
|
|
3925
|
+
else if (main > 1) problems.push(`the page has ${main} main landmarks (<main> or role="main"): it has exactly one, holding the page's own content`);
|
|
3926
|
+
if (h1 === 0) problems.push("the page has no <h1>: one heading names the page");
|
|
3927
|
+
else if (h1 > 1) problems.push(`the page has ${h1} <h1> headings: one names the page, the others are <h2> or lower`);
|
|
3928
|
+
return problems;
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3758
3931
|
// packages/dev-server/src/storefront/site-files.ts
|
|
3759
3932
|
var escapeXml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3760
3933
|
function sitemapXml(entries) {
|
|
@@ -3798,18 +3971,18 @@ async function buildStaticSite(config, root, opts) {
|
|
|
3798
3971
|
};
|
|
3799
3972
|
const project = resolve7(root);
|
|
3800
3973
|
if (inside2(project, outDir)) throw new Error(`pd build: --out ${outDir} would contain the project itself \u2014 choose a folder inside or beside it, like ./dist`);
|
|
3801
|
-
for (const
|
|
3802
|
-
if (inside2(outDir,
|
|
3803
|
-
throw new Error(`pd build: --out ${outDir} overlaps ${
|
|
3974
|
+
for (const source2 of [cfg.patternsDir, cfg.stylesDir, ...cfg.themeDirs, ...cfg.assetRoots]) {
|
|
3975
|
+
if (inside2(outDir, source2) || inside2(source2, outDir)) {
|
|
3976
|
+
throw new Error(`pd build: --out ${outDir} overlaps ${source2}, which Pattern Dump reads \u2014 choose another folder`);
|
|
3804
3977
|
}
|
|
3805
3978
|
}
|
|
3806
3979
|
const storefront = cfg.storefront;
|
|
3807
3980
|
if (storefront) {
|
|
3808
3981
|
const sfOut = storefront.outDir;
|
|
3809
3982
|
if (inside2(project, sfOut)) throw new Error(`pd build: storefront.outDir ${sfOut} would contain the project itself \u2014 choose a folder inside or beside it`);
|
|
3810
|
-
for (const
|
|
3811
|
-
if (inside2(sfOut,
|
|
3812
|
-
throw new Error(`pd build: storefront.outDir ${sfOut} overlaps ${
|
|
3983
|
+
for (const source2 of [cfg.patternsDir, cfg.stylesDir, storefront.pagesDir, ...cfg.themeDirs, ...cfg.assetRoots]) {
|
|
3984
|
+
if (inside2(sfOut, source2) || inside2(source2, sfOut)) {
|
|
3985
|
+
throw new Error(`pd build: storefront.outDir ${sfOut} overlaps ${source2}, which Pattern Dump reads \u2014 choose another folder`);
|
|
3813
3986
|
}
|
|
3814
3987
|
}
|
|
3815
3988
|
if (inside2(sfOut, outDir) || inside2(outDir, sfOut)) {
|
|
@@ -3847,6 +4020,7 @@ ${sfCrawl.problems.map((p) => ` - ${p}`).join("\n")}`);
|
|
|
3847
4020
|
if (sfCrawl) log(`\u2026 storefront checked: ${sfCrawl.urls.length} files`);
|
|
3848
4021
|
const origin = `http://127.0.0.1:${server.port}`;
|
|
3849
4022
|
let linkSummary;
|
|
4023
|
+
let a11ySummary;
|
|
3850
4024
|
const externalNotes = [];
|
|
3851
4025
|
if (storefront && sfGet && sfCrawl) {
|
|
3852
4026
|
const { pages: list } = await (await sfGet(PD_ROUTES.pages)).json();
|
|
@@ -3884,6 +4058,34 @@ ${links.problems.map((p) => ` - ${p}`).join("\n")}`);
|
|
|
3884
4058
|
for (const [url, on] of links.external) externalNotes.push(`an external link, not checked: ${url} (on ${on.join(", ")})`);
|
|
3885
4059
|
linkSummary = { pages: pages.length, references: links.references, external: links.external.size };
|
|
3886
4060
|
log(`\u2026 links checked: ${links.references} references on ${pages.length} pages`);
|
|
4061
|
+
const errors = [];
|
|
4062
|
+
const warned = /* @__PURE__ */ new Map();
|
|
4063
|
+
for (const page of pages) {
|
|
4064
|
+
const html = sfCrawl.documents.get(page);
|
|
4065
|
+
if (html === void 0) continue;
|
|
4066
|
+
for (const p of checkPageBasics(html)) errors.push(`${page}: ${p}`);
|
|
4067
|
+
for (const f of await runPageA11y(html)) {
|
|
4068
|
+
const where = f.nodes.map((n) => n.target).join(", ");
|
|
4069
|
+
if (f.severity === "error") {
|
|
4070
|
+
errors.push(`${page}: ${f.ruleId} (${f.impact}): ${f.help}, on ${where} (${f.helpUrl})`);
|
|
4071
|
+
continue;
|
|
4072
|
+
}
|
|
4073
|
+
const w = warned.get(f.ruleId) ?? { finding: f, pages: [], elements: 0 };
|
|
4074
|
+
w.pages.push(page);
|
|
4075
|
+
w.elements += f.nodes.length;
|
|
4076
|
+
warned.set(f.ruleId, w);
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
if (errors.length) {
|
|
4080
|
+
await server.close();
|
|
4081
|
+
throw new Error(`pd build: the storefront has accessibility problems, so nothing was written:
|
|
4082
|
+
${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
4083
|
+
}
|
|
4084
|
+
a11ySummary = {
|
|
4085
|
+
pages: pages.length,
|
|
4086
|
+
warnings: [...warned.values()].map((w) => `${w.finding.ruleId} (${w.finding.impact}): ${w.finding.help}, ${w.elements} element${w.elements === 1 ? "" : "s"} on ${w.pages.join(", ")} (${w.finding.helpUrl})`)
|
|
4087
|
+
};
|
|
4088
|
+
log(`\u2026 accessibility checked on ${pages.length} pages`);
|
|
3887
4089
|
}
|
|
3888
4090
|
await rm3(outDir, { recursive: true, force: true });
|
|
3889
4091
|
await mkdir5(outDir, { recursive: true });
|
|
@@ -4000,6 +4202,7 @@ ${sf.failures.map((f) => ` - ${f}`).join("\n")}`);
|
|
|
4000
4202
|
}
|
|
4001
4203
|
report.notes.push(...storefrontNotes, ...externalNotes);
|
|
4002
4204
|
if (linkSummary) report.links = linkSummary;
|
|
4205
|
+
if (a11ySummary) report.accessibility = a11ySummary;
|
|
4003
4206
|
log(`\u2026 storefront: ${pages} pages, ${counts.files} files`);
|
|
4004
4207
|
}
|
|
4005
4208
|
const used = /* @__PURE__ */ new Set();
|
|
@@ -5524,8 +5727,8 @@ async function unsafeOutputs(id, at, deps) {
|
|
|
5524
5727
|
for (const componentId of [...templates].sort()) {
|
|
5525
5728
|
const file = deps.discoveryMap.get(componentId)?.hbsPath;
|
|
5526
5729
|
if (!file) continue;
|
|
5527
|
-
const
|
|
5528
|
-
for (const o of unescapedOutputs(
|
|
5730
|
+
const source2 = await readFile18(file, "utf8").catch(() => "");
|
|
5731
|
+
for (const o of unescapedOutputs(source2)) {
|
|
5529
5732
|
if (!o.helper && allowed.has(o.name)) continue;
|
|
5530
5733
|
const what = o.helper ? `a helper's result unescaped ({{{${o.name}}}})` : `"${o.name}" unescaped ({{{${o.name}}}})`;
|
|
5531
5734
|
problems.push({ at, message: `${componentId} outputs ${what} at ${deps.shown(file)}:${o.line}:${o.column}, which only a text/markdown property may be` });
|
|
@@ -5751,16 +5954,16 @@ function proseImages(md, tokens, prose) {
|
|
|
5751
5954
|
continue;
|
|
5752
5955
|
}
|
|
5753
5956
|
if (!src.startsWith("/assets/")) continue;
|
|
5754
|
-
const
|
|
5755
|
-
if (!
|
|
5957
|
+
const source2 = prose.lookup(src);
|
|
5958
|
+
if (!source2) {
|
|
5756
5959
|
problem(` is not an image in the assets`);
|
|
5757
5960
|
continue;
|
|
5758
5961
|
}
|
|
5759
|
-
if (
|
|
5760
|
-
problem(`: ${
|
|
5962
|
+
if (source2.kind === "unavailable") {
|
|
5963
|
+
problem(`: ${source2.reason}`);
|
|
5761
5964
|
continue;
|
|
5762
5965
|
}
|
|
5763
|
-
if (
|
|
5966
|
+
if (source2.kind === "variants" && !prose.slot) {
|
|
5764
5967
|
problem(` needs its layout's slot for prose images: ${prose.layoutId} ` + (prose.slotProblem ? `has an invalid x-pd-prose-images (${prose.slotProblem})` : `declares no x-pd-prose-images`));
|
|
5765
5968
|
continue;
|
|
5766
5969
|
}
|
|
@@ -5768,7 +5971,7 @@ function proseImages(md, tokens, prose) {
|
|
|
5768
5971
|
const atOnce = eager > 0;
|
|
5769
5972
|
if (atOnce) eager--;
|
|
5770
5973
|
const slot = prose.slot;
|
|
5771
|
-
child.meta = { pdImage: imageTag(
|
|
5974
|
+
child.meta = { pdImage: imageTag(source2, {
|
|
5772
5975
|
widths: slot?.widths ?? [],
|
|
5773
5976
|
sizes: slot ? atOnce ? slot.sizes : `auto, ${slot.sizes}` : "",
|
|
5774
5977
|
alt,
|
|
@@ -6090,7 +6293,7 @@ async function buildComponentDetail(componentId, patternsDir, renderFn, tracker,
|
|
|
6090
6293
|
} catch {
|
|
6091
6294
|
}
|
|
6092
6295
|
}
|
|
6093
|
-
const
|
|
6296
|
+
const source2 = {
|
|
6094
6297
|
hbs,
|
|
6095
6298
|
hbsHighlighted,
|
|
6096
6299
|
...jsonStr !== void 0 ? { json: jsonStr, jsonHighlighted } : {},
|
|
@@ -6131,7 +6334,7 @@ async function buildComponentDetail(componentId, patternsDir, renderFn, tracker,
|
|
|
6131
6334
|
layout,
|
|
6132
6335
|
...deprecatedUse ? { deprecatedUse } : {},
|
|
6133
6336
|
variants,
|
|
6134
|
-
source,
|
|
6337
|
+
source: source2,
|
|
6135
6338
|
doc,
|
|
6136
6339
|
...requireScripts ? { requireScripts } : {},
|
|
6137
6340
|
...requireAssets ? { requireAssets } : {},
|
|
@@ -6402,7 +6605,7 @@ async function stampCss(css, hashOf) {
|
|
|
6402
6605
|
|
|
6403
6606
|
// packages/dev-server/src/assets.ts
|
|
6404
6607
|
import { existsSync as existsSync5 } from "node:fs";
|
|
6405
|
-
import { createRequire as
|
|
6608
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
6406
6609
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
6407
6610
|
function resolveDevUiDist(moduleUrl, exists = existsSync5) {
|
|
6408
6611
|
const packaged = fileURLToPath5(new URL("./dev-ui/", moduleUrl));
|
|
@@ -6410,7 +6613,7 @@ function resolveDevUiDist(moduleUrl, exists = existsSync5) {
|
|
|
6410
6613
|
return fileURLToPath5(new URL("../../../apps/dev-ui/dist/", moduleUrl));
|
|
6411
6614
|
}
|
|
6412
6615
|
function resolveAxeCore(moduleUrl) {
|
|
6413
|
-
return
|
|
6616
|
+
return createRequire4(moduleUrl).resolve("axe-core/axe.min.js");
|
|
6414
6617
|
}
|
|
6415
6618
|
|
|
6416
6619
|
// packages/dev-server/src/storefront/page-image.ts
|
|
@@ -6669,7 +6872,7 @@ async function assemblePage(route, model, a) {
|
|
|
6669
6872
|
const notFound = () => ({ status: 404, html: errorDocument(route, [`there is no page at ${route}`], a.live), problems: [`there is no page at ${route}`], notes: [] });
|
|
6670
6873
|
const isNotFound = route === PD_ROUTES.notFound;
|
|
6671
6874
|
let page = isNotFound ? model.notFound : model.files.get(route);
|
|
6672
|
-
let
|
|
6875
|
+
let source2;
|
|
6673
6876
|
if (isNotFound && !page) {
|
|
6674
6877
|
const layoutId = `layouts/${DEFAULT_LAYOUT}`;
|
|
6675
6878
|
if (!a.discoveryMap.has(layoutId)) {
|
|
@@ -6681,13 +6884,13 @@ async function assemblePage(route, model, a) {
|
|
|
6681
6884
|
};
|
|
6682
6885
|
}
|
|
6683
6886
|
const declared = (await readSchema(a.discoveryMap.get(layoutId)?.schemaPath)).schema?.properties ?? {};
|
|
6684
|
-
|
|
6685
|
-
page = { route, file: "", path: `${NOT_FOUND_FILE} (built in)`, frontmatter: readFrontmatterStrict(
|
|
6887
|
+
source2 = builtInNotFound(Object.hasOwn(declared, "title"));
|
|
6888
|
+
page = { route, file: "", path: `${NOT_FOUND_FILE} (built in)`, frontmatter: readFrontmatterStrict(source2) };
|
|
6686
6889
|
}
|
|
6687
6890
|
if (!page) return notFound();
|
|
6688
|
-
if (
|
|
6891
|
+
if (source2 === void 0) {
|
|
6689
6892
|
try {
|
|
6690
|
-
|
|
6893
|
+
source2 = await readFile23(page.file, "utf8");
|
|
6691
6894
|
} catch (e) {
|
|
6692
6895
|
if (e.code === "ENOENT") return notFound();
|
|
6693
6896
|
throw e;
|
|
@@ -6720,7 +6923,7 @@ async function assemblePage(route, model, a) {
|
|
|
6720
6923
|
const prose = imageLookup ? {
|
|
6721
6924
|
layoutId: checked.layoutId,
|
|
6722
6925
|
lookup: imageLookup,
|
|
6723
|
-
lineOffset: frontmatterBodyOffset(
|
|
6926
|
+
lineOffset: frontmatterBodyOffset(source2),
|
|
6724
6927
|
problems: [],
|
|
6725
6928
|
...proseSlot.slot ? { slot: proseSlot.slot } : {},
|
|
6726
6929
|
...proseSlot.problem ? { slotProblem: proseSlot.problem } : {}
|
|
@@ -6729,7 +6932,7 @@ async function assemblePage(route, model, a) {
|
|
|
6729
6932
|
const placedProblems = [];
|
|
6730
6933
|
const headings = [];
|
|
6731
6934
|
const body = await renderDoc(
|
|
6732
|
-
frontmatterBody(
|
|
6935
|
+
frontmatterBody(source2),
|
|
6733
6936
|
(id, variant) => a.render({ component: id, variant }).then((r) => r.html),
|
|
6734
6937
|
a.discoveryMap,
|
|
6735
6938
|
{
|
|
@@ -6741,7 +6944,7 @@ async function assemblePage(route, model, a) {
|
|
|
6741
6944
|
...prose ? { prose } : {},
|
|
6742
6945
|
// Components placed with values (pd- tags): checked against their schemas, rendered into the page.
|
|
6743
6946
|
placed: {
|
|
6744
|
-
lineOffset: frontmatterBodyOffset(
|
|
6947
|
+
lineOffset: frontmatterBodyOffset(source2),
|
|
6745
6948
|
problems: placedProblems,
|
|
6746
6949
|
deps: {
|
|
6747
6950
|
discoveryMap: a.discoveryMap,
|
|
@@ -6905,6 +7108,7 @@ ${html}`);
|
|
|
6905
7108
|
routes.add(self);
|
|
6906
7109
|
const links = checkLinks({ built: routes, documents: /* @__PURE__ */ new Map([[self, html]]), pages: [self], pagesOnly: true });
|
|
6907
7110
|
notes.push(...links.problems.map((p) => `warning: ${page.path}${p.slice(self.length)}`));
|
|
7111
|
+
notes.push(...checkPageBasics(html).map((p) => `warning: ${page.path}: ${p}`));
|
|
6908
7112
|
}
|
|
6909
7113
|
return { status: isNotFound ? 404 : 200, html: document(head, `${html}
|
|
6910
7114
|
${fitterScript()}`, a.live, a.lang), problems: [], notes };
|
|
@@ -7597,7 +7801,7 @@ function rootMatchers(watched) {
|
|
|
7597
7801
|
|
|
7598
7802
|
// packages/dev-server/src/watch.ts
|
|
7599
7803
|
import { createHash as createHash6 } from "node:crypto";
|
|
7600
|
-
import { existsSync as existsSync6, readFileSync as
|
|
7804
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, statSync as statSync4 } from "node:fs";
|
|
7601
7805
|
import { appendFile, readdir as readdir9 } from "node:fs/promises";
|
|
7602
7806
|
import { basename as basename10, extname as extname4, join as join26, relative as relative13, resolve as resolve10, sep as sep9 } from "node:path";
|
|
7603
7807
|
import chokidar from "chokidar";
|
|
@@ -7658,7 +7862,7 @@ function seenNow(path) {
|
|
|
7658
7862
|
const s = statSync4(path);
|
|
7659
7863
|
const stamp2 = `${s.ino}:${s.ctimeMs}:${s.mtimeMs}:${s.size}`;
|
|
7660
7864
|
const forced = globalThis.__testOnly_watchStamp;
|
|
7661
|
-
return { stamp: forced ? forced(path, stamp2) : stamp2, real: stamp2, hash: createHash6("sha1").update(
|
|
7865
|
+
return { stamp: forced ? forced(path, stamp2) : stamp2, real: stamp2, hash: createHash6("sha1").update(readFileSync4(path)).digest("hex") };
|
|
7662
7866
|
} catch {
|
|
7663
7867
|
return null;
|
|
7664
7868
|
}
|
|
@@ -9144,6 +9348,11 @@ async function runBuild(root, configPath, options) {
|
|
|
9144
9348
|
if (report.links) {
|
|
9145
9349
|
console.log(`[pd build] links: ${report.links.references} references on ${report.links.pages} pages, none broken` + (report.links.external ? `; ${report.links.external} external, not checked (notes)` : ""));
|
|
9146
9350
|
}
|
|
9351
|
+
if (report.accessibility) {
|
|
9352
|
+
const n = report.accessibility.warnings.length;
|
|
9353
|
+
console.log(`[pd build] accessibility: ${report.accessibility.pages} pages, no errors` + (n ? `; ${n} rule${n === 1 ? "" : "s"} with warnings:` : ""));
|
|
9354
|
+
for (const w of report.accessibility.warnings) console.log(`[pd build] warning: ${w}`);
|
|
9355
|
+
}
|
|
9147
9356
|
for (const w of report.warnings) console.log(`[pd build] not built: ${w}`);
|
|
9148
9357
|
for (const n of report.notes) console.log(`[pd build] note: ${n}`);
|
|
9149
9358
|
}
|
|
@@ -9485,7 +9694,7 @@ import { basename as basename12, dirname as dirname13, join as join31, relative
|
|
|
9485
9694
|
// packages/cli/package.json
|
|
9486
9695
|
var package_default = {
|
|
9487
9696
|
name: "@pattern-dump/cli",
|
|
9488
|
-
version: "0.
|
|
9697
|
+
version: "0.3.0",
|
|
9489
9698
|
private: true,
|
|
9490
9699
|
description: "The pd command and the published pattern-dump package.",
|
|
9491
9700
|
type: "module",
|
|
@@ -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, sameHostWorkbenchPath, suggest, validateConfig } from "./validate.js";
|
|
3
|
+
export { ConfigError, configProblems, sameHostWorkbenchPath, suggest, validateConfig, withSourceDefaults } 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. */
|
|
@@ -242,4 +242,10 @@ export type StorefrontNavEntry = {
|
|
|
242
242
|
label: string;
|
|
243
243
|
children: StorefrontNavEntry[];
|
|
244
244
|
};
|
|
245
|
-
|
|
245
|
+
/**
|
|
246
|
+
* What a project writes: the config, where `sourceDir`, `patternsDir` and `styles` may be left out (a site whose
|
|
247
|
+
* patterns and styles all come from pattern packages has none of its own). `defineConfig` and `validateConfig` fill
|
|
248
|
+
* them in (`withSourceDefaults`), so everything that reads a config gets all three.
|
|
249
|
+
*/
|
|
250
|
+
export type PatternDumpConfigInput = Omit<PatternDumpConfig, "sourceDir" | "patternsDir" | "styles"> & Partial<Pick<PatternDumpConfig, "sourceDir" | "patternsDir" | "styles">>;
|
|
251
|
+
export declare function defineConfig(config: PatternDumpConfigInput): PatternDumpConfig;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PatternDumpConfig } from "./index.js";
|
|
1
|
+
import type { PatternDumpConfig, PatternDumpConfigInput } from "./index.js";
|
|
2
2
|
/** The closest of `known` to `key` when it is near enough to be a typo of it, else undefined. Used for
|
|
3
3
|
* "did you mean" in config problems, and by other checks that name an unknown key. */
|
|
4
4
|
export declare function suggest(key: string, known: string[]): string | undefined;
|
|
@@ -10,6 +10,12 @@ export declare class ConfigError extends Error {
|
|
|
10
10
|
/** The problems with a loaded config, one line each; empty when it is valid. */
|
|
11
11
|
export declare function configProblems(raw: unknown): string[];
|
|
12
12
|
/** Check a loaded config and return it typed, or throw one error listing every problem. */
|
|
13
|
+
/**
|
|
14
|
+
* A config with its source folders filled in where it names none: `sourceDir` is "./src", and `patternsDir` and
|
|
15
|
+
* `styles.dir` are its `patterns` and `styles` folders. None of them has to exist: a site whose patterns and styles
|
|
16
|
+
* all come from pattern packages has none of its own. What the config names is kept as it is.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withSourceDefaults(config: PatternDumpConfigInput): PatternDumpConfig;
|
|
13
19
|
export declare function validateConfig(raw: unknown, source: string): PatternDumpConfig;
|
|
14
20
|
/**
|
|
15
21
|
* The path of a workbench published on the storefront's own host, or undefined when it has a host of its own.
|
package/package.json
CHANGED