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
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Note
3
+ ---
4
+
5
+ A short aside. A page places one with values of its own:
6
+
7
+ ```md
8
+ <pd-components-note tone="tip" heading="Try breaking it">
9
+ Text, in **markdown**.
10
+ </pd-components-note>
11
+ ```
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Note",
4
+ "description": "A short aside. On a page: <pd-components-note heading=\"…\">text</pd-components-note>.",
5
+ "type": "object",
6
+ "required": ["heading", "text"],
7
+ "x-pd-content": "text",
8
+ "properties": {
9
+ "heading": { "type": "string", "description": "One line, in bold" },
10
+ "text": { "type": "string", "contentMediaType": "text/markdown", "description": "The note itself: the tag's content, as markdown" },
11
+ "tone": { "type": "string", "enum": ["tip", "warning"], "description": "Its colour; a plain note without one" }
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "heading": "Careful",
3
+ "tone": "warning"
4
+ }
@@ -0,0 +1,8 @@
1
+ {{#> partials-base }}
2
+ <article class="page">
3
+ <h1 class="page-title">{{title}}</h1>
4
+ {{#if description}}<p class="page-description">{{description}}</p>{{/if}}
5
+ <div class="page-body">{{{body}}}</div>
6
+ {{#if page.next}}<p class="page-next"><a rel="next" href="{{page.next.route}}">Next: {{page.next.title}}</a></p>{{/if}}
7
+ </article>
8
+ {{/partials-base}}
@@ -0,0 +1,8 @@
1
+ {
2
+ "title": "A page",
3
+ "description": "Its one-sentence description.",
4
+ "body": "<p>The page's markdown, rendered.</p>",
5
+ "site": { "title": __PD_BRAND_JSON__, "footer": "Made with Pattern Dump." },
6
+ "nav": [{ "route": "/", "title": "Home", "current": true }, { "route": "/about/", "title": "About" }],
7
+ "page": { "next": { "route": "/about/", "title": "About" } }
8
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Page
3
+ ---
4
+
5
+ The layout every page renders with, unless its frontmatter names another with `layout: <name>`.
6
+ Its schema is the frontmatter contract: a page may set `title` (required) and `description`.
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Page",
4
+ "description": "The layout every page renders with. Its properties are what a page's frontmatter may set.",
5
+ "type": "object",
6
+ "x-pd-prose-images": { "widths": [480, 672, 1344], "sizes": "(min-width: 44rem) 42rem, calc(100vw - 2rem)" },
7
+ "required": ["title"],
8
+ "properties": {
9
+ "title": { "type": "string", "description": "The page's heading, its title in the navigation and in search results" },
10
+ "description": { "type": "string", "description": "One sentence under the heading, and the page's description for search results" },
11
+ "page": {
12
+ "type": "object",
13
+ "description": "What the storefront works out for the page",
14
+ "properties": {
15
+ "next": { "description": "The page after this one in the navigation" }
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,17 @@
1
+ <div class="site">
2
+ <a class="site-skip" href="#main">Skip to the content</a>
3
+ <header class="site-header">
4
+ <a class="site-title" href="/">{{site.title}}</a>
5
+ <nav class="site-nav" aria-label="Site">
6
+ <ul>
7
+ {{#each nav}}<li><a href="{{route}}"{{#if current}} aria-current="page"{{/if}}>{{title}}</a></li>{{/each}}
8
+ </ul>
9
+ </nav>
10
+ </header>
11
+ <main class="site-main" id="main">
12
+ {{> @partial-block }}
13
+ </main>
14
+ <footer class="site-footer">
15
+ <p>{{site.footer}}</p>
16
+ </footer>
17
+ </div>
@@ -0,0 +1,4 @@
1
+ {
2
+ "site": { "title": __PD_BRAND_JSON__, "footer": "Made with Pattern Dump." },
3
+ "nav": [{ "route": "/", "title": "Home", "current": true }, { "route": "/about/", "title": "About" }]
4
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Base
3
+ ---
4
+
5
+ The frame every layout fills: the header with the site's name and navigation, the main content, and
6
+ the footer. A layout fills it with `{{#> partials-base }} … {{/partials-base}}`.
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Base",
4
+ "description": "The frame every layout is built on. Its site property declares what storefront.site may hold.",
5
+ "type": "object",
6
+ "required": ["site"],
7
+ "properties": {
8
+ "site": {
9
+ "type": "object",
10
+ "description": "Values every page shares: storefront.site in the config",
11
+ "required": ["title"],
12
+ "properties": {
13
+ "title": { "type": "string", "description": "The site's name, in the header" },
14
+ "footer": { "type": "string", "description": "The line at the bottom of every page" }
15
+ }
16
+ }
17
+ }
18
+ }
@@ -0,0 +1 @@
1
+ <link rel="icon" href="/assets/icon.svg">
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Head
3
+ ---
4
+
5
+ What the skin adds to every page's `<head>`: here, the icon. Pattern Dump writes the title, the
6
+ description, the search and link-preview tags and the stylesheets itself, so they don't belong here.
@@ -0,0 +1,8 @@
1
+ @use "../base/tokens";
2
+
3
+ .note { margin-block: tokens.$space-md; padding: tokens.$space-md; border-left: 4px solid var(--color-border, #6b7280); border-radius: tokens.$radius; background: #f3f4f6; }
4
+ .note-tip { border-left-color: #0b7a3e; background: #ecf8f1; }
5
+ .note-warning { border-left-color: #a15c00; background: #fdf4e7; }
6
+ .note-heading { margin: 0 0 tokens.$space-xs; font-weight: 700; }
7
+ .note-text > :first-child { margin-top: 0; }
8
+ .note-text > :last-child { margin-bottom: 0; }
@@ -0,0 +1,6 @@
1
+ @use "../base/tokens";
2
+
3
+ .page-title { margin-block: 0 tokens.$space-sm; line-height: 1.2; }
4
+ .page-description { margin-block: 0 tokens.$space-lg; color: var(--color-text-muted, #4b5563); }
5
+ .page-body iframe { width: 100%; border: 0; }
6
+ .page-next { margin-top: tokens.$space-lg; }
@@ -0,0 +1,13 @@
1
+ @use "../base/tokens";
2
+
3
+ .site { max-width: 44rem; margin: 0 auto; padding: tokens.$space-lg tokens.$space-md; }
4
+ .site-skip { position: absolute; left: -100vw; }
5
+ .site-skip:focus { left: tokens.$space-md; }
6
+ .site-header { display: flex; flex-wrap: wrap; gap: tokens.$space-md; align-items: baseline; justify-content: space-between; }
7
+ .site-title { font-weight: 700; color: inherit; text-decoration: none; }
8
+ .site-nav ul { display: flex; gap: tokens.$space-md; margin: 0; padding: 0; list-style: none; }
9
+ .site-nav a[aria-current="page"] { font-weight: 700; }
10
+ .site-main { margin-block: tokens.$space-lg; }
11
+ // An image never overflows the column: with its width and height it keeps its aspect ratio as it scales down.
12
+ .site img { max-inline-size: 100%; block-size: auto; }
13
+ .site-footer { border-top: 1px solid var(--color-border, #6b7280); color: var(--color-text-muted, #4b5563); }
@@ -0,0 +1,14 @@
1
+ // The website: every markdown file in pages/ is a page, rendered with the skin in src/patterns/layouts
2
+ // and src/patterns/partials. `npm run dev` prints its address, http://live.localhost:4000/.
3
+ storefront: {
4
+ pagesDir: "./pages",
5
+ outDir: "./site",
6
+ // The workbench is published on the website's own host, under /workbench/ (and built for that path,
7
+ // build.basePath below).
8
+ workbenchUrl: "/workbench/",
9
+ // Your domain, once you have one: canonical URLs, link previews and the sitemap are built from it.
10
+ // url: "https://www.example.com/",
11
+ site: { title: __PD_BRAND_JSON__, footer: "Made with Pattern Dump." },
12
+ },
13
+ build: { basePath: "/workbench/" },
14
+
@@ -0,0 +1,2 @@
1
+ site
2
+ _site
@@ -0,0 +1,60 @@
1
+
2
+ ## The website
3
+
4
+ `pages/` is a website: every markdown file in it is a page, at the address its path names
5
+ (`pages/about.md` is `/about/`). `npm run dev` serves it next to the workbench:
6
+
7
+ | Address | Serves |
8
+ |---|---|
9
+ | http://live.localhost:4000/ | the website |
10
+ | http://pd.localhost:4000/ | the workbench |
11
+
12
+ If Safari can't open them, add this line to `/etc/hosts`: `127.0.0.1 live.localhost pd.localhost`.
13
+
14
+ ### Try breaking it
15
+
16
+ `pages/index.md` places the note component with a `pd-` tag, and its values are checked against the
17
+ component's schema before the page is shown. With the website open, change `tone="tip"` to `tone="tips"`
18
+ and save: the page shows what is wrong, with the file, line and column. Change it back and the page
19
+ returns by itself. How `pd-` tags work:
20
+ [placing components](https://github.com/n8design/pattern-dump/blob/main/docs/guide/storefront.md#placing-components).
21
+
22
+ ### Where it lives
23
+
24
+ | Path | What lives there |
25
+ |---|---|
26
+ | `pages/` | the pages, in markdown (`pages/` is the website; `src/patterns/pages/`, if you have it, holds page-sized components) |
27
+ | `src/patterns/layouts/page/` | the layout every page renders with; its schema is what a page's frontmatter may set |
28
+ | `src/patterns/partials/base/` | the frame around every page: header, navigation, footer |
29
+ | `src/patterns/partials/head/` | what the skin adds to every page's `<head>` (the icon) |
30
+ | `src/patterns/components/note/` | the component the home page shows |
31
+ | `pattern-dump.config.ts` | `storefront`: the website's name and footer (`site`), and where it is published |
32
+
33
+ A page that doesn't exist shows a built-in 404 page; write `pages/404.md` to change it. A page that moved
34
+ can forward from its old address (`redirectFrom: [/old/]`), and `index: false` keeps a page out of search
35
+ engines. All of it is in the [storefront guide](https://github.com/n8design/pattern-dump/blob/main/docs/guide/storefront.md).
36
+
37
+ ### Build it
38
+
39
+ `npm run build` writes the website to `site/` and the workbench to `dist/`, built for `/workbench/`. The
40
+ website is served from the root of its host, with the workbench copied into it at `/workbench/`, which is
41
+ what the GitHub Pages workflow does.
42
+
43
+ ### Publish it
44
+
45
+ `.github/workflows/pages.yml` publishes both to GitHub Pages on every push to `main`. The website lives at
46
+ the root of its host, so it needs a custom domain (a project page under `/<repository>/` can't host it).
47
+ Once, in the repository on GitHub:
48
+
49
+ 1. **Settings → Pages → Source:** "GitHub Actions".
50
+ 2. **Settings → Pages → Custom domain:** your domain, then Save. GitHub recommends verifying the domain in
51
+ your account settings first.
52
+ 3. **DNS:** for an apex domain (`example.com`), `A` records to `185.199.108.153`, `185.199.109.153`,
53
+ `185.199.110.153` and `185.199.111.153`; for a subdomain (`www.example.com`), a `CNAME` record to
54
+ `<your-user>.github.io`, without the repository name.
55
+ 4. **Enforce HTTPS** in the same settings, once GitHub offers it (it can take up to a day).
56
+ 5. **`storefront.url`** in `pattern-dump.config.ts`: the same address, for canonical URLs, link previews and
57
+ the sitemap.
58
+
59
+ The workflow writes no `CNAME` file: with a GitHub Actions deploy, the custom domain is the setting in
60
+ step 2. It installs with `npm ci`, so `pattern-dump` must come from the npm registry, not a local file.
@@ -1,160 +0,0 @@
1
- // packages/config/src/validate.ts
2
- var SHAPE = {
3
- $schema: "string",
4
- sourceDir: "string",
5
- patternsDir: "string",
6
- outDir: "string",
7
- styles: {
8
- keys: {
9
- strategy: { values: ["parallel"], unsupported: ["co-located"] },
10
- dir: "string",
11
- entry: "string-or-strings"
12
- },
13
- required: ["dir"]
14
- },
15
- themes: {
16
- keys: {
17
- dir: "string",
18
- default: "string",
19
- switch: { values: ["attribute", "stylesheet"] },
20
- swatch: { keys: { slots: "strings" }, required: ["slots"] }
21
- },
22
- required: ["dir", "default"]
23
- },
24
- assets: {
25
- keys: {
26
- strategy: { values: ["central"], unsupported: ["component-relative"] },
27
- dir: "string-or-strings",
28
- css: "strings"
29
- }
30
- },
31
- scriptsDir: "string",
32
- globalScripts: "strings",
33
- dataDir: "string",
34
- helpers: "string",
35
- documentBase: "strings",
36
- overviews: "boolean",
37
- shell: {
38
- keys: { brand: { keys: { title: "string", logo: "logo", logoAlt: "string", href: "string" } } }
39
- },
40
- packages: "packages",
41
- exclude: "strings",
42
- build: { keys: { outDir: "string", basePath: "string", audit: "boolean" } }
43
- };
44
- var REQUIRED = ["sourceDir", "patternsDir", "outDir", "styles"];
45
- var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
46
- var isStrings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
47
- function distance(a, b) {
48
- const row = Array.from({ length: b.length + 1 }, (_, i) => i);
49
- for (let i = 1; i <= a.length; i++) {
50
- let prev = row[0];
51
- row[0] = i;
52
- for (let j = 1; j <= b.length; j++) {
53
- const cur = row[j];
54
- row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1].toLowerCase() === b[j - 1].toLowerCase() ? 0 : 1));
55
- prev = cur;
56
- }
57
- }
58
- return row[b.length];
59
- }
60
- function suggest(key, known) {
61
- let best;
62
- let bestD = Infinity;
63
- for (const k of known) {
64
- const d = distance(key, k);
65
- if (d < bestD) {
66
- best = k;
67
- bestD = d;
68
- }
69
- }
70
- if (best === void 0) return void 0;
71
- const sameStart = key.slice(0, 3).toLowerCase() === best.slice(0, 3).toLowerCase();
72
- return bestD <= 2 || bestD <= 3 && sameStart ? best : void 0;
73
- }
74
- function checkObject(value, shape, required, at, problems) {
75
- const known = Object.keys(shape);
76
- const unknown = Object.keys(value).filter((k) => !(k in shape));
77
- for (const key of required) {
78
- if (value[key] !== void 0) continue;
79
- const near = unknown.find((u) => suggest(u, [key]) === key);
80
- problems.push(near ? `missing required "${at}${key}" (found "${at}${near}" \u2014 did you mean "${at}${key}"?)` : `missing required "${at}${key}"`);
81
- }
82
- for (const key of unknown) {
83
- const near = suggest(key, known);
84
- if (near && required.includes(near) && value[near] === void 0) continue;
85
- problems.push(`unknown key "${at}${key}"${near ? ` \u2014 did you mean "${at}${near}"?` : ""}`);
86
- }
87
- for (const key of known) {
88
- if (value[key] !== void 0) checkValue(value[key], shape[key], `${at}${key}`, problems);
89
- }
90
- }
91
- function checkValue(value, rule, at, problems) {
92
- const fail = (expected) => problems.push(`"${at}" must be ${expected}`);
93
- if (rule === "string") {
94
- if (typeof value !== "string") fail("a string");
95
- return;
96
- }
97
- if (rule === "boolean") {
98
- if (typeof value !== "boolean") fail("true or false");
99
- return;
100
- }
101
- if (rule === "strings") {
102
- if (!isStrings(value)) fail("a list of strings");
103
- return;
104
- }
105
- if (rule === "string-or-strings") {
106
- if (typeof value !== "string" && !isStrings(value)) fail("a string or a list of strings");
107
- return;
108
- }
109
- if (rule === "packages") {
110
- const ok = Array.isArray(value) && value.every((p) => typeof p === "string" || isObject(p) && typeof p.name === "string" && (p.exclude === void 0 || isStrings(p.exclude)));
111
- if (!ok) fail(`a list of package names or { name, exclude } objects`);
112
- return;
113
- }
114
- if (rule === "logo") {
115
- const ok = typeof value === "string" || isObject(value) && typeof value.light === "string" && typeof value.dark === "string";
116
- if (!ok) fail(`a path or { light, dark }`);
117
- return;
118
- }
119
- if ("values" in rule) {
120
- if (typeof value === "string" && rule.unsupported?.includes(value)) {
121
- problems.push(`"${at}": "${value}" is not supported yet \u2014 use ${rule.values.map((v) => `"${v}"`).join(" or ")}, or leave it out`);
122
- } else if (typeof value !== "string" || !rule.values.includes(value)) {
123
- fail(rule.values.map((v) => `"${v}"`).join(" or "));
124
- }
125
- return;
126
- }
127
- if (!isObject(value)) {
128
- fail("an object");
129
- return;
130
- }
131
- checkObject(value, rule.keys, rule.required ?? [], `${at}.`, problems);
132
- }
133
- var ConfigError = class extends Error {
134
- name = "ConfigError";
135
- };
136
- function configProblems(raw) {
137
- if (!isObject(raw)) return ["the config must be an object (a .ts config must `export default defineConfig({ \u2026 })`)"];
138
- const problems = [];
139
- checkObject(raw, SHAPE, REQUIRED, "", problems);
140
- return problems;
141
- }
142
- function validateConfig(raw, source) {
143
- const problems = configProblems(raw);
144
- if (problems.length > 0) {
145
- throw new ConfigError(`${source}: invalid config
146
- ${problems.map((p) => ` - ${p}`).join("\n")}`);
147
- }
148
- return raw;
149
- }
150
-
151
- // packages/config/src/index.ts
152
- function defineConfig(config) {
153
- return config;
154
- }
155
-
156
- export {
157
- ConfigError,
158
- validateConfig,
159
- defineConfig
160
- };