@fusion.dev/helix-docs 0.186.1 → 0.188.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-docs",
3
3
  "description": "Versioned documentation content and registry for Helix.",
4
- "version": "0.186.1",
4
+ "version": "0.188.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -36,14 +36,14 @@
36
36
  },
37
37
  "bugs": "https://github.com/project-fusion/fusion/issues",
38
38
  "devDependencies": {
39
- "@fusion.dev/helix": "0.186.1",
40
- "@fusion.dev/helix-cem": "0.186.1",
41
- "@fusion.dev/helix-headless": "0.186.1",
42
- "@fusion.dev/helix-icons": "0.186.1",
43
- "@fusion.dev/helix-map": "0.186.1",
44
- "@fusion.dev/helix-styles": "0.186.1",
45
- "@fusion.dev/helix-testing": "0.186.1",
46
- "@fusion.dev/helix-theme": "0.186.1",
39
+ "@fusion.dev/helix": "0.188.0",
40
+ "@fusion.dev/helix-cem": "0.188.0",
41
+ "@fusion.dev/helix-headless": "0.188.0",
42
+ "@fusion.dev/helix-icons": "0.188.0",
43
+ "@fusion.dev/helix-map": "0.188.0",
44
+ "@fusion.dev/helix-styles": "0.188.0",
45
+ "@fusion.dev/helix-testing": "0.188.0",
46
+ "@fusion.dev/helix-theme": "0.188.0",
47
47
  "@fusion.dev/scripts": "0.0.0-dev",
48
48
  "lit": "^3.2.1",
49
49
  "playwright": "^1.63.0"
package/dist/catalog.d.ts DELETED
@@ -1,194 +0,0 @@
1
- /**
2
- * The component catalog: manifests and example markup, as one shape a reader
3
- * can render.
4
- *
5
- * **Pure, and it lives here for the same reason `reference()` does.** Reading
6
- * the manifests and the `.examples/*.html` files off disk is the caller's job,
7
- * because a content package that touched the filesystem would trace itself
8
- * into every consumer's server bundle.
9
- *
10
- * Two sources and no third: the manifests describe the API, and the example
11
- * files hold the markup. Nothing about a component is written down here, which
12
- * is what keeps a site from drifting away from the library it documents.
13
- */
14
- import type { HelixManifest } from '@fusion.dev/helix-cem';
15
- export interface ApiMember {
16
- description: string;
17
- name: string;
18
- /**
19
- * Where the platform feature this is built on is specified, if it is one.
20
- *
21
- * Optional here and required on `PropertyDoc`, because a property always has
22
- * the field even when it is empty and a slot or a part is built on nothing.
23
- * It is on the member rather than only on the property so the Read table can
24
- * carry a mark: `form` and `labels` are read-only and are the two members in
25
- * the library that most need one.
26
- */
27
- references?: {
28
- label: string;
29
- url: string;
30
- }[];
31
- type: string;
32
- }
33
- /**
34
- * A property, which carries a default where an attribute does not.
35
- *
36
- * Separate from `ApiMember` rather than an optional field on it, because a slot
37
- * and a CSS part have no default and a column of empty cells is a table saying
38
- * the data is missing rather than that the concept does not apply.
39
- */
40
- export interface PropertyDoc {
41
- default: string;
42
- description: string;
43
- name: string;
44
- /** Whether it is a getter, so a reader is not invited to set it. */
45
- readonly: boolean;
46
- /** Where the platform feature this is built on is specified. */
47
- references: {
48
- label: string;
49
- url: string;
50
- }[];
51
- type: string;
52
- }
53
- export interface EventDoc {
54
- description: string;
55
- name: string;
56
- reactProp: string;
57
- type: string;
58
- }
59
- export interface Element {
60
- attributes: ApiMember[];
61
- cssParts: ApiMember[];
62
- cssProperties: ApiMember[];
63
- cssStates: ApiMember[];
64
- description: string;
65
- /**
66
- * The entry that registers the element, under the tier's package: the
67
- * family's own, such as `filter-input`, or one of its own where the family's
68
- * leaves it out, such as `filter-input/menu`.
69
- */
70
- entry: string;
71
- events: EventDoc[];
72
- label: string;
73
- methods: ApiMember[];
74
- /** Role within the family. `Root` for its primary element. */
75
- part: string;
76
- properties: PropertyDoc[];
77
- slots: ApiMember[];
78
- summary: string;
79
- tagName: string;
80
- tier: 'headless' | 'styled';
81
- }
82
- export interface Example {
83
- /** The example's own stylesheet, or empty where it needs none. */
84
- css: string;
85
- /** One line from the example's `@summary` comment, or empty where unwritten. */
86
- description: string;
87
- html: string;
88
- label: string;
89
- /**
90
- * The frame layout this example asks for, where it asks for one.
91
- *
92
- * Absent on almost every example, which is the design rather than an
93
- * oversight: the reference picks `centered` and only the exceptions say
94
- * otherwise, so what is written down is the handful of facts that differ
95
- * rather than a table of defaults kept by hand. A consumer that has no
96
- * layouts can ignore it.
97
- */
98
- layout?: string;
99
- /**
100
- * The least height the preview pane should give this example, in pixels.
101
- *
102
- * Present on the six overlay families and nowhere else. Height is derived
103
- * from what the frame drew, so a declared one is a fact that can go stale;
104
- * these are the examples where there is nothing to derive, because what they
105
- * open is `position: fixed` and excluded from measurement by design.
106
- */
107
- min?: number;
108
- slug: string;
109
- /**
110
- * The same example authored in TSX, which is a file and never a conversion.
111
- *
112
- * **Both syntaxes are authored, neither derived.** A derived example is a
113
- * function of its twin rather than a thing anybody wrote, and converting the
114
- * HTML mechanically does not work: an example carrying a `<script>` becomes
115
- * a JSX fragment with a `<script>` in it, which is a syntax error rather
116
- * than a bad example.
117
- *
118
- * `helix component`'s `examples-paired` holds the two files to each other and
119
- * `check:spec:visual-parity` holds the two pictures to each other.
120
- */
121
- tsx: string;
122
- /**
123
- * Which tier the example shows, read from what it renders.
124
- *
125
- * Off the markup rather than off the filename, the same test `helix component`'s `tokens`
126
- * uses to know a headless example's stylesheet may write raw values: an
127
- * example is a headless one when it renders a `helix-headless-` element.
128
- * Both tiers call theirs `preview.html`, the package directory carrying the
129
- * tier, so the name cannot answer and this is the only fact available.
130
- */
131
- tier: 'headless' | 'styled';
132
- }
133
- export interface Family {
134
- aliases: string[];
135
- /** What the component is for, from `@category`. The gallery groups on it. */
136
- category: string;
137
- documentation: string;
138
- elements: Element[];
139
- examples: Example[];
140
- /**
141
- * The entries of other families whose elements a page writes inside this
142
- * one, such as `option` for Select: the family does not register them, so
143
- * the page imports them beside it. The reference's usage lists them under
144
- * the family's own entries.
145
- *
146
- * Read from the import lines the family's descriptions show, where each
147
- * container says what to import beside it. See "A container does not
148
- * register another family's children" in `helix/CONVENTIONS.md`.
149
- */
150
- imports: string[];
151
- label: string;
152
- since: string;
153
- slug: string;
154
- /** The family's source directory, relative to the repository root. */
155
- source: string;
156
- status: string;
157
- summary: string;
158
- }
159
- /**
160
- * One example as it sits on disk, before this decides what to call it.
161
- *
162
- * An example is up to two files, `<slug>.html` and an optional `<slug>.css`.
163
- * The HTML is the list: a stylesheet on its own is not an example, and
164
- * `helix component`'s `examples` fails on one whose markup is missing rather than showing a
165
- * sandbox with a stylesheet and nothing to style.
166
- *
167
- * The pair is what the sandbox already shows, `index.html` beside `styles.css`,
168
- * so the second file is the one the reader is looking at rather than a build
169
- * step. Never an inline `style` attribute instead: it is unreachable from a
170
- * stylesheet, cannot express a hover or a media query, and teaches a reader to
171
- * write the one thing the library exists to avoid.
172
- */
173
- export interface ExampleSource {
174
- css: string;
175
- /** Family folder the example was found in. */
176
- family: string;
177
- html: string;
178
- /** File name without its extension: `preview`, `headless`, `cards.headless`. */
179
- slug: string;
180
- /** The authored TSX beside the HTML, never a conversion of it. */
181
- tsx: string;
182
- }
183
- /**
184
- * Every family across the given manifests, alphabetical, each with its elements
185
- * and examples.
186
- *
187
- * The family a declaration belongs to comes from where its file is rather than
188
- * from its tag name, which is the same reasoning `place()` carries for the
189
- * reference: `helix-button-group` is one family and `helix-tab` is a part
190
- * of another, and nothing in the name says which.
191
- */
192
- export declare const catalog: (manifests: HelixManifest[], examples?: ExampleSource[]) => Family[];
193
- /** The family of a given slug, from an already-built catalog. */
194
- export declare const findInCatalog: (families: Family[], slug: string) => Family | undefined;
package/dist/catalog.js DELETED
@@ -1,163 +0,0 @@
1
- import { displayName, startCase, tierOf } from './naming.js';
2
- import { place } from './reference.js';
3
- const CANONICAL = 'preview';
4
- const HEADLESS = 'headless';
5
- const HEADLESS_SUFFIX = /\.headless$/;
6
- const withoutUndefined = (text) => text
7
- .split('|')
8
- .map((part) => part.trim())
9
- .filter((part) => part !== 'undefined')
10
- .join(' | ');
11
- const PLATFORM_CALLBACK = /Callback$/;
12
- const isPublic = (row) => row.privacy === undefined || row.privacy === 'public';
13
- const properties = (rows) => (rows ?? [])
14
- .filter((row) => row.kind === 'field' && isPublic(row) && row.static !== true)
15
- .map((row) => ({
16
- default: row.default === 'undefined' ? '' : (row.default ?? ''),
17
- description: row.description ?? '',
18
- name: row.name ?? '',
19
- readonly: row.readonly === true,
20
- references: row.helix?.references ?? [],
21
- type: withoutUndefined(row.type?.text ?? ''),
22
- }));
23
- const methods = (rows) => (rows ?? [])
24
- .filter((row) => row.kind === 'method' && isPublic(row) && !PLATFORM_CALLBACK.test(row.name ?? ''))
25
- .map((row) => {
26
- const args = (row.parameters ?? [])
27
- .map((arg) => `${arg.name ?? ''}: ${arg.type?.text ?? 'unknown'}`)
28
- .join(', ');
29
- return {
30
- description: row.description ?? '',
31
- name: row.name ?? '',
32
- type: `(${args}) => ${row.return?.type?.text ?? 'void'}`,
33
- };
34
- });
35
- const members = (rows) => (rows ?? []).map((row) => ({
36
- description: row.description ?? '',
37
- name: row.name ?? '',
38
- type: withoutUndefined(row.type?.text ?? ''),
39
- }));
40
- const ANNOTATION = /^[^\S\n]*<!--\s*@[\s\S]*?-->[^\S\n]*\n?/gm;
41
- const withoutAnnotations = (html) => html.replace(ANNOTATION, '').trimStart();
42
- const examplesOf = (slug, declared, sources) => {
43
- const described = new Map((declared?.examples ?? []).map((entry) => [entry.slug ?? '', entry.description ?? '']));
44
- const layouts = new Map((declared?.examples ?? []).map((entry) => [entry.slug ?? '', entry.layout ?? '']));
45
- const minimums = new Map((declared?.examples ?? []).map((entry) => [entry.slug ?? '', entry.min ?? 0]));
46
- return (sources
47
- .filter((source) => source.family === slug)
48
- .sort((a, b) => {
49
- const pairOf = (slug) => slug === HEADLESS ? CANONICAL : slug.replace(HEADLESS_SUFFIX, '');
50
- const twin = (slug) => (slug === HEADLESS || HEADLESS_SUFFIX.test(slug) ? 1 : 0);
51
- const left = pairOf(a.slug);
52
- const right = pairOf(b.slug);
53
- if (left !== right) {
54
- return left === CANONICAL ? -1 : right === CANONICAL ? 1 : left.localeCompare(right);
55
- }
56
- return twin(a.slug) - twin(b.slug);
57
- })
58
- .map((source) => {
59
- const layout = layouts.get(source.slug);
60
- const min = minimums.get(source.slug);
61
- const mirrored = source.slug === HEADLESS ? CANONICAL : source.slug.replace(HEADLESS_SUFFIX, '');
62
- return {
63
- css: source.css,
64
- description: described.get(source.slug) ?? '',
65
- html: withoutAnnotations(source.html),
66
- label: startCase(mirrored),
67
- ...(layout ? { layout } : {}),
68
- ...(min ? { min } : {}),
69
- slug: source.slug,
70
- tier: (source.html.includes('<helix-headless-') ? 'headless' : 'styled'),
71
- tsx: source.tsx,
72
- };
73
- }));
74
- };
75
- const entriesOf = (manifest) => {
76
- const entries = new Map();
77
- for (const module of manifest.modules ?? []) {
78
- const entry = /^src\/(.+)\/index\.ts$/.exec(module.path ?? '')?.[1];
79
- if (!entry) {
80
- continue;
81
- }
82
- for (const exported of module.exports ?? []) {
83
- const from = exported.declaration?.module?.replace(/\.[jt]s$/, '');
84
- const held = from === undefined ? undefined : entries.get(from);
85
- if (from !== undefined && (held === undefined || entry.length < held.length)) {
86
- entries.set(from, entry);
87
- }
88
- }
89
- }
90
- return entries;
91
- };
92
- export const catalog = (manifests, examples = []) => {
93
- const byFamily = new Map();
94
- const entryOf = new Map();
95
- for (const manifest of manifests) {
96
- const entries = entriesOf(manifest);
97
- for (const module of manifest.modules ?? []) {
98
- for (const declaration of module.declarations ?? []) {
99
- if (!declaration.tagName) {
100
- continue;
101
- }
102
- const { family } = place(module.path);
103
- if (!family) {
104
- continue;
105
- }
106
- byFamily.set(family, [...(byFamily.get(family) ?? []), declaration]);
107
- entryOf.set(declaration, entries.get((module.path ?? '').replace(/\.[jt]s$/, '')) ?? family);
108
- }
109
- }
110
- }
111
- return [...byFamily.entries()]
112
- .map(([slug, entries]) => {
113
- const root = entries.find((entry) => entry.tagName === `helix-${slug}`) ??
114
- entries[0];
115
- const named = (tag) => entries.find((entry) => entry.tagName === tag);
116
- const roots = [named(`helix-${slug}`), named(`helix-${HEADLESS}-${slug}`)].filter((entry) => entry !== undefined);
117
- return {
118
- aliases: [...new Set(roots.flatMap((entry) => entry.helix?.aliases ?? []))],
119
- category: root.helix?.category ?? '',
120
- documentation: root.helix?.documentation ?? '',
121
- elements: entries
122
- .map((entry) => ({
123
- attributes: members(entry.attributes),
124
- cssParts: members(entry.cssParts),
125
- cssProperties: members(entry.cssProperties),
126
- cssStates: members(entry.cssStates),
127
- description: entry.description ?? '',
128
- entry: entryOf.get(entry) ?? slug,
129
- events: (entry.events ?? []).map((event) => ({
130
- description: event.description ?? '',
131
- name: event.name ?? '',
132
- reactProp: event.reactProp ?? `on${event.name ?? ''}`,
133
- type: event.type?.text ?? '',
134
- })),
135
- label: displayName(entry.tagName),
136
- methods: methods(entry.members),
137
- part: entry.helix?.part ?? '',
138
- properties: properties(entry.members),
139
- slots: members(entry.slots),
140
- summary: entry.summary ?? '',
141
- tagName: entry.tagName,
142
- tier: tierOf(entry.tagName),
143
- }))
144
- .sort((a, b) => Number(a.tier === 'headless') - Number(b.tier === 'headless') ||
145
- a.tagName.localeCompare(b.tagName)),
146
- examples: examplesOf(slug, root.helix, examples),
147
- imports: importsOf(slug, entries),
148
- label: startCase(slug),
149
- since: root.helix?.since ?? '',
150
- slug,
151
- source: root.helix?.source ?? '',
152
- status: root.helix?.status ?? '',
153
- summary: root.summary ?? '',
154
- };
155
- })
156
- .sort((a, b) => a.label.localeCompare(b.label));
157
- };
158
- const importsOf = (slug, declarations) => [
159
- ...new Set(declarations.flatMap((declaration) => [...(declaration.description ?? '').matchAll(/import '@fusion\.dev\/helix\/([a-z-]+)'/g)]
160
- .map((match) => match[1] ?? '')
161
- .filter((entry) => entry !== '' && entry !== slug))),
162
- ].sort();
163
- export const findInCatalog = (families, slug) => families.find((family) => family.slug === slug);
package/dist/naming.d.ts DELETED
@@ -1,14 +0,0 @@
1
- /**
2
- * Display names for components.
3
- *
4
- * The product is Helix, so every component being called "HelixCard" inside the
5
- * Helix site is noise: it repeats the one thing the reader already knows on
6
- * every row of the sidebar. The tag keeps its prefix, because that is a real
7
- * identifier a consumer types.
8
- */
9
- /** `app-header` becomes `App Header`, and `qr-code` becomes `QR Code`. */
10
- export declare const startCase: (slug: string) => string;
11
- /** `helix-app-header` becomes `App Header`, `helix-headless-card` becomes `Card`. */
12
- export declare const displayName: (tagName: string) => string;
13
- /** Which tier a tag belongs to, read from the prefix rather than the manifest. */
14
- export declare const tierOf: (tagName: string) => 'headless' | 'styled';
package/dist/naming.js DELETED
@@ -1,8 +0,0 @@
1
- const INITIALISMS = new Set(['api', 'css', 'html', 'id', 'qr', 'ui', 'url']);
2
- export const startCase = (slug) => slug
3
- .split('-')
4
- .filter(Boolean)
5
- .map((word) => INITIALISMS.has(word) ? word.toUpperCase() : word.charAt(0).toUpperCase() + word.slice(1))
6
- .join(' ');
7
- export const displayName = (tagName) => startCase(tagName.replace(/^helix-(?:headless-)?/, ''));
8
- export const tierOf = (tagName) => tagName.startsWith('helix-headless-') ? 'headless' : 'styled';