pattern-dump 0.2.0-alpha.0 → 0.4.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 +83 -0
- package/README.md +12 -7
- package/dist/chunks/{chunk-ESMZAOP2.js → chunk-DIDJPCLS.js} +177 -18
- package/dist/index.js +1 -1
- package/dist/pd.js +1689 -766
- package/dist/types/config/index.d.ts +21 -6
- package/dist/types/config/own-manifest.d.ts +9 -0
- package/dist/types/config/validate.d.ts +15 -2
- package/dist/types/contracts/dev-protocol.d.ts +1 -1
- package/package.json +1 -1
- package/templates/storefront/.github/workflows/pages.yml +3 -2
- package/templates/storefront/pages/articles/hello.md +10 -0
- package/templates/storefront/pages/articles/index.md +10 -0
- package/templates/storefront/src/patterns/layouts/listing/listing.hbs +16 -0
- package/templates/storefront/src/patterns/layouts/listing/listing.json +13 -0
- package/templates/storefront/src/patterns/layouts/listing/listing.md +7 -0
- package/templates/storefront/src/patterns/layouts/listing/listing.schema.json +18 -0
- package/templates/storefront/src/patterns/layouts/page/page.hbs +1 -0
- package/templates/storefront/src/patterns/layouts/page/page.json +1 -1
- package/templates/storefront/src/patterns/layouts/page/page.md +2 -1
- package/templates/storefront/src/patterns/layouts/page/page.schema.json +3 -1
- package/templates/storefront/src/styles/layouts/listing.scss +15 -0
- package/templates/storefront/src/styles/layouts/page.scss +3 -0
- package/templates/storefront-fills/config.txt +4 -1
- package/templates/storefront-fills/readme.md +11 -3
|
@@ -1,6 +1,7 @@
|
|
|
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, storefrontBasePath, suggest, validateConfig, withSourceDefaults } from "./validate.js";
|
|
4
|
+
export { withOwnManifest } from "./own-manifest.js";
|
|
4
5
|
export interface AssetsConfig {
|
|
5
6
|
/** Where assets live. Only 'central' (the default: one assets dir, served at /assets/) is supported
|
|
6
7
|
* so far; 'component-relative' is reserved, and the config check rejects it as not supported yet. */
|
|
@@ -195,10 +196,11 @@ export interface StorefrontConfig {
|
|
|
195
196
|
*/
|
|
196
197
|
workbenchUrl?: string;
|
|
197
198
|
/**
|
|
198
|
-
* The site's address, such as `https://example.com/`: an absolute http(s) URL with no
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
199
|
+
* The site's address, such as `https://example.com/`: an absolute http(s) URL with no query or fragment; loaded
|
|
200
|
+
* with exactly one trailing slash. It may have a path, such as `https://example.github.io/project/`: the site is
|
|
201
|
+
* then published under that path (its base path), and every page, stylesheet, script and image it links is
|
|
202
|
+
* served under it, in `pd dev` too. With it, pages get a canonical URL, Open Graph and JSON-LD, and `pd build`
|
|
203
|
+
* writes a sitemap; without it they are left out, `pd build` says so once, and the site is served from `/`.
|
|
202
204
|
*/
|
|
203
205
|
url?: string;
|
|
204
206
|
/** The language of every page, as `<html lang>` takes it (a BCP 47 tag such as `de-AT`). Default: `en`. */
|
|
@@ -213,6 +215,13 @@ export interface StorefrontConfig {
|
|
|
213
215
|
image?: string;
|
|
214
216
|
/** This site's image encoding, over the project's `images`. */
|
|
215
217
|
images?: ImageSettings;
|
|
218
|
+
/**
|
|
219
|
+
* The site's own additions to every page's `<head>`, such as an analytics script or a search engine's verification
|
|
220
|
+
* tag: a Handlebars file relative to the root, such as `./head.hbs`, rendered with `site` (plain Handlebars, without
|
|
221
|
+
* Pattern Dump's helpers) and placed after the skin's `partials/head`. It is checked as `partials/head` is: only
|
|
222
|
+
* `<meta>`, `<link>`, `<script>`, `<style>` and `<noscript>`, and none of the tags Pattern Dump writes itself.
|
|
223
|
+
*/
|
|
224
|
+
head?: string;
|
|
216
225
|
/**
|
|
217
226
|
* Values every page of the site shares, such as its title or the line in its footer. Every layout gets
|
|
218
227
|
* them as `site`; a page cannot set them. Which keys there are, and their types, is what the `site`
|
|
@@ -242,4 +251,10 @@ export type StorefrontNavEntry = {
|
|
|
242
251
|
label: string;
|
|
243
252
|
children: StorefrontNavEntry[];
|
|
244
253
|
};
|
|
245
|
-
|
|
254
|
+
/**
|
|
255
|
+
* What a project writes: the config, where `sourceDir`, `patternsDir` and `styles` may be left out (a site whose
|
|
256
|
+
* patterns and styles all come from pattern packages has none of its own). `defineConfig` and `validateConfig` fill
|
|
257
|
+
* them in (`withSourceDefaults`), so everything that reads a config gets all three.
|
|
258
|
+
*/
|
|
259
|
+
export type PatternDumpConfigInput = Omit<PatternDumpConfig, "sourceDir" | "patternsDir" | "styles"> & Partial<Pick<PatternDumpConfig, "sourceDir" | "patternsDir" | "styles">>;
|
|
260
|
+
export declare function defineConfig(config: PatternDumpConfigInput): PatternDumpConfig;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The config with each folder its project's own manifest names and the config doesn't, or the config as it is when
|
|
3
|
+
* there is no manifest. `where` names the two files in the error. A config that sets a field the manifest sets too,
|
|
4
|
+
* to another value, throws a ConfigError listing every such field. The raw config is never changed.
|
|
5
|
+
*/
|
|
6
|
+
export declare function withOwnManifest(raw: unknown, manifest: unknown, where: {
|
|
7
|
+
config: string;
|
|
8
|
+
manifest: string;
|
|
9
|
+
}): unknown;
|
|
@@ -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,11 +10,24 @@ 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;
|
|
20
|
+
/**
|
|
21
|
+
* The storefront's base path: the path of `storefront.url` ("/" at the root of a host, "/project/" under a path), or
|
|
22
|
+
* "/" for a site with no address. Reads a validated config, so the URL is already in its one form.
|
|
23
|
+
*/
|
|
24
|
+
export declare function storefrontBasePath(storefront: {
|
|
25
|
+
url?: string;
|
|
26
|
+
} | undefined): string;
|
|
14
27
|
/**
|
|
15
28
|
* The path of a workbench published on the storefront's own host, or undefined when it has a host of its own.
|
|
16
29
|
* Same-host means a root-relative `workbenchUrl` ("/workbench/"), or an absolute one on the origin of
|
|
17
|
-
* `storefront.url
|
|
30
|
+
* `storefront.url` (whatever the site's own path). The path is the workbench's full path on the host. Reads a validated config, so both are already normalised (one trailing slash).
|
|
18
31
|
*/
|
|
19
32
|
export declare function sameHostWorkbenchPath(storefront: {
|
|
20
33
|
workbenchUrl?: string;
|
|
@@ -70,7 +70,7 @@ export interface TreeNode {
|
|
|
70
70
|
children?: TreeNode[];
|
|
71
71
|
}
|
|
72
72
|
export type DiagnosticSeverity = "error" | "warning" | "info";
|
|
73
|
-
export type DiagnosticKind = "RENDER_ERROR" | "UNFILLED_REQUIRED" | "SHAPE_MISMATCH" | "HTML_INVALID" | "CASCADE_WARNING" | "A11Y" | "HIGHLIGHT_FALLBACK" | "HANDLE_COLLISION" | "DATA_INVALID" | "SCHEMA_INVALID";
|
|
73
|
+
export type DiagnosticKind = "RENDER_ERROR" | "UNFILLED_REQUIRED" | "SHAPE_MISMATCH" | "HTML_INVALID" | "CASCADE_WARNING" | "A11Y" | "HIGHLIGHT_FALLBACK" | "HANDLE_COLLISION" | "DATA_INVALID" | "SCHEMA_INVALID" | "HELPER_SHADOW";
|
|
74
74
|
/** Fields every diagnostic carries, whichever engine produced it. */
|
|
75
75
|
interface DiagnosticBase {
|
|
76
76
|
severity: DiagnosticSeverity;
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
# Builds the website and the workbench, and publishes both to GitHub Pages:
|
|
2
|
-
#
|
|
1
|
+
# Builds the website and the workbench, and publishes both to GitHub Pages: the website at the address in
|
|
2
|
+
# storefront.url (your domain, or a project page's address with its path), the workbench inside it at
|
|
3
|
+
# workbench/ (build.basePath gives its full path).
|
|
3
4
|
# Before the first run: Settings → Pages → Source: "GitHub Actions", and your custom domain: see "Publish it" in the README.
|
|
4
5
|
name: Pages
|
|
5
6
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hello, articles
|
|
3
|
+
description: The example article. Copy it to start a new one.
|
|
4
|
+
date: 2026-11-14
|
|
5
|
+
tags: [getting-started, notes]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
An article is a page in `pages/articles/` with a `date` (`YYYY-MM-DD`), which orders the listing and is the page's
|
|
9
|
+
publication date for search engines. `tags` are lowercase words for grouping. A page with `draft: true` shows while
|
|
10
|
+
you work and is left out of `pd build`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Articles
|
|
3
|
+
description: Everything written here, newest first.
|
|
4
|
+
layout: listing
|
|
5
|
+
sort: date
|
|
6
|
+
order: 2
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
This page is `pages/articles/index.md`. Every page in this folder is listed below, newest first. Add a markdown
|
|
10
|
+
file with a `date` and it shows up here, with no link to write.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{{#> partials-base }}
|
|
2
|
+
<article class="page listing">
|
|
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
|
+
<ul class="listing-entries" role="list">
|
|
7
|
+
{{#each page.children}}
|
|
8
|
+
<li class="listing-entry">
|
|
9
|
+
<a class="listing-title" href="{{route}}">{{title}}</a>
|
|
10
|
+
{{#if date}}<time class="listing-date" datetime="{{date}}">{{date}}</time>{{/if}}
|
|
11
|
+
{{#if description}}<p class="listing-teaser">{{description}}</p>{{/if}}
|
|
12
|
+
</li>
|
|
13
|
+
{{/each}}
|
|
14
|
+
</ul>
|
|
15
|
+
</article>
|
|
16
|
+
{{/partials-base}}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Articles",
|
|
3
|
+
"description": "Everything written here, newest first.",
|
|
4
|
+
"body": "<p>The folder's index.md, rendered.</p>",
|
|
5
|
+
"site": { "title": __PD_BRAND_JSON__, "footer": "Made with Pattern Dump." },
|
|
6
|
+
"nav": [{ "route": "/", "title": "Home" }, { "route": "/articles/", "title": "Articles", "current": true }],
|
|
7
|
+
"page": {
|
|
8
|
+
"children": [
|
|
9
|
+
{ "route": "/articles/second/", "title": "A second article", "description": "Its teaser: the page's description, or its first paragraph.", "date": "2026-11-14", "image": null, "tags": ["notes"], "draft": false },
|
|
10
|
+
{ "route": "/articles/first/", "title": "A first article", "description": "Listed below the newer one.", "date": "2026-11-01", "image": null, "tags": [], "draft": false }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Listing
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The layout of a folder's `index.md` that lists the folder's pages, such as `pages/articles/index.md`: set
|
|
6
|
+
`layout: listing`, and `sort: date` to list the newest first. It declares `page.children`, so the storefront fills
|
|
7
|
+
it with the folder's pages, each with its route, title, description (or its first paragraph), date, image and tags.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Listing",
|
|
4
|
+
"description": "A folder's index.md that lists the folder's pages: a collection, such as articles.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["title"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"title": { "type": "string", "description": "The listing's heading, and its title in the navigation" },
|
|
9
|
+
"description": { "type": "string", "description": "One sentence under the heading, and the page's description for search results" },
|
|
10
|
+
"page": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"description": "What the storefront works out for the page",
|
|
13
|
+
"properties": {
|
|
14
|
+
"children": { "description": "The folder's pages, newest first with sort: date" }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<article class="page">
|
|
3
3
|
<h1 class="page-title">{{title}}</h1>
|
|
4
4
|
{{#if description}}<p class="page-description">{{description}}</p>{{/if}}
|
|
5
|
+
{{#if page.date}}<div class="page-meta"><time datetime="{{page.date}}">{{page.date}}</time>{{#if page.tags.length}}<ul class="page-tags" aria-label="Tags">{{#each page.tags}}<li>{{this}}</li>{{/each}}</ul>{{/if}}</div>{{/if}}
|
|
5
6
|
<div class="page-body">{{{body}}}</div>
|
|
6
7
|
{{#if page.next}}<p class="page-next"><a rel="next" href="{{page.next.route}}">Next: {{page.next.title}}</a></p>{{/if}}
|
|
7
8
|
</article>
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
"body": "<p>The page's markdown, rendered.</p>",
|
|
5
5
|
"site": { "title": __PD_BRAND_JSON__, "footer": "Made with Pattern Dump." },
|
|
6
6
|
"nav": [{ "route": "/", "title": "Home", "current": true }, { "route": "/about/", "title": "About" }],
|
|
7
|
-
"page": { "next": { "route": "/about/", "title": "About" } }
|
|
7
|
+
"page": { "next": { "route": "/about/", "title": "About" }, "date": "2026-11-14", "tags": ["notes"] }
|
|
8
8
|
}
|
|
@@ -3,4 +3,5 @@ title: Page
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
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`.
|
|
6
|
+
Its schema is the frontmatter contract: a page may set `title` (required) and `description`. A page with a `date`
|
|
7
|
+
(an article) shows it under the description, with its `tags`.
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"type": "object",
|
|
13
13
|
"description": "What the storefront works out for the page",
|
|
14
14
|
"properties": {
|
|
15
|
-
"next": { "description": "The page after this one in the navigation" }
|
|
15
|
+
"next": { "description": "The page after this one in the navigation" },
|
|
16
|
+
"date": { "description": "The page's date, from its frontmatter, for an article" },
|
|
17
|
+
"tags": { "description": "The page's tags, from its frontmatter" }
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@use "../base/tokens";
|
|
2
|
+
|
|
3
|
+
.listing-entries { margin: tokens.$space-lg 0 0; padding: 0; list-style: none; }
|
|
4
|
+
.listing-entry { margin-block-end: tokens.$space-lg; }
|
|
5
|
+
.listing-title { font-weight: 600; }
|
|
6
|
+
.listing-date { margin-inline-start: tokens.$space-sm; color: var(--color-text-muted, #4b5563); }
|
|
7
|
+
.listing-teaser {
|
|
8
|
+
margin: 0;
|
|
9
|
+
// The teaser is never shortened in the data: three lines at most here.
|
|
10
|
+
display: -webkit-box;
|
|
11
|
+
-webkit-box-orient: vertical;
|
|
12
|
+
-webkit-line-clamp: 3;
|
|
13
|
+
line-clamp: 3;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
@@ -4,3 +4,6 @@
|
|
|
4
4
|
.page-description { margin-block: 0 tokens.$space-lg; color: var(--color-text-muted, #4b5563); }
|
|
5
5
|
.page-body iframe { width: 100%; border: 0; }
|
|
6
6
|
.page-next { margin-top: tokens.$space-lg; }
|
|
7
|
+
.page-meta { margin-block: 0 tokens.$space-lg; color: var(--color-text-muted, #4b5563); }
|
|
8
|
+
.page-tags { display: inline; margin: 0; padding: 0; list-style: none; }
|
|
9
|
+
.page-tags li { display: inline; margin-inline-start: tokens.$space-sm; }
|
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
// The workbench is published on the website's own host, under /workbench/ (and built for that path,
|
|
7
7
|
// build.basePath below).
|
|
8
8
|
workbenchUrl: "/workbench/",
|
|
9
|
-
//
|
|
9
|
+
// The site's address, once you know it: canonical URLs, link previews and the sitemap are built from it.
|
|
10
10
|
// url: "https://www.example.com/",
|
|
11
|
+
// Or a subfolder, such as a GitHub project page: the site is then served under the path, in `npm run dev` too,
|
|
12
|
+
// and the workbench goes under it as well (workbenchUrl and build.basePath "/<repository>/workbench/").
|
|
13
|
+
// url: "https://<owner>.github.io/<repository>/",
|
|
11
14
|
site: { title: __PD_BRAND_JSON__, footer: "Made with Pattern Dump." },
|
|
12
15
|
},
|
|
13
16
|
build: { basePath: "/workbench/" },
|
|
@@ -30,6 +30,9 @@ returns by itself. How `pd-` tags work:
|
|
|
30
30
|
| `src/patterns/components/note/` | the component the home page shows |
|
|
31
31
|
| `pattern-dump.config.ts` | `storefront`: the website's name and footer (`site`), and where it is published |
|
|
32
32
|
|
|
33
|
+
`pages/articles/` is a collection: its `index.md` (layout `listing`, `sort: date`) lists every page in the folder,
|
|
34
|
+
newest first, by its `date`. Copy `hello.md` to write another; `draft: true` keeps one out of the build.
|
|
35
|
+
|
|
33
36
|
A page that doesn't exist shows a built-in 404 page; write `pages/404.md` to change it. A page that moved
|
|
34
37
|
can forward from its old address (`redirectFrom: [/old/]`), and `index: false` keeps a page out of search
|
|
35
38
|
engines. All of it is in the [storefront guide](https://github.com/n8design/pattern-dump/blob/main/docs/guide/storefront.md).
|
|
@@ -42,9 +45,14 @@ what the GitHub Pages workflow does.
|
|
|
42
45
|
|
|
43
46
|
### Publish it
|
|
44
47
|
|
|
45
|
-
`.github/workflows/pages.yml` publishes both to GitHub Pages on every push to `main
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
`.github/workflows/pages.yml` publishes both to GitHub Pages on every push to `main`, at a custom domain
|
|
49
|
+
or as a project page.
|
|
50
|
+
|
|
51
|
+
**As a project page** (`https://<owner>.github.io/<repository>/`, no domain needed): in
|
|
52
|
+
`pattern-dump.config.ts`, set `storefront.url` to that address, and `storefront.workbenchUrl` and
|
|
53
|
+
`build.basePath` to `/<repository>/workbench/`. Then **Settings → Pages → Source:** "GitHub Actions".
|
|
54
|
+
|
|
55
|
+
**At a custom domain**, once, in the repository on GitHub:
|
|
48
56
|
|
|
49
57
|
1. **Settings → Pages → Source:** "GitHub Actions".
|
|
50
58
|
2. **Settings → Pages → Custom domain:** your domain, then Save. GitHub recommends verifying the domain in
|