handoff-app 1.2.2-7 → 2.0.0-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 +37 -15
- package/README.md +372 -118
- package/dist/app-builder/build.d.ts +41 -2
- package/dist/app-builder/build.js +455 -18
- package/dist/app-builder/client-config.d.ts +15 -2
- package/dist/app-builder/client-config.js +43 -4
- package/dist/app-builder/config-diff/snapshot.d.ts +1 -6
- package/dist/app-builder/config-diff/snapshot.js +2 -9
- package/dist/app-builder/index.d.ts +1 -1
- package/dist/app-builder/index.js +3 -1
- package/dist/app-builder/nav-shell.d.ts +23 -0
- package/dist/app-builder/nav-shell.js +80 -0
- package/dist/app-builder/static-export.d.ts +17 -0
- package/dist/app-builder/static-export.js +181 -0
- package/dist/app-builder/vercel-output.d.ts +39 -0
- package/dist/app-builder/vercel-output.js +259 -0
- package/dist/app-builder/watchers/utils.d.ts +2 -0
- package/dist/app-builder/watchers.d.ts +14 -4
- package/dist/app-builder/watchers.js +123 -28
- package/dist/app-builder/websocket.js +1 -1
- package/dist/artifacts/index.d.ts +2 -0
- package/dist/artifacts/index.js +13 -0
- package/dist/artifacts/types.d.ts +136 -0
- package/dist/artifacts/types.js +24 -0
- package/dist/artifacts/url.d.ts +49 -0
- package/dist/artifacts/url.js +86 -0
- package/dist/cache/build-cache.js +48 -4
- package/dist/cli.js +0 -0
- package/dist/commands/build/app.js +1 -2
- package/dist/commands/build/index.d.ts +17 -0
- package/dist/commands/build/index.js +66 -0
- package/dist/commands/checkout/index.d.ts +24 -0
- package/dist/commands/checkout/index.js +71 -0
- package/dist/commands/db/migrate.d.ts +11 -0
- package/dist/commands/db/migrate.js +33 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/publish/index.d.ts +21 -0
- package/dist/commands/publish/index.js +68 -0
- package/dist/config/defaults.js +15 -1
- package/dist/config/loader.js +6 -1
- package/dist/config/normalizers/declaration.js +4 -1
- package/dist/config/normalizers/page.d.ts +18 -0
- package/dist/config/normalizers/page.js +34 -0
- package/dist/config/normalizers/pattern.js +7 -1
- package/dist/config/runtime.d.ts +4 -0
- package/dist/config/runtime.js +53 -5
- package/dist/index.d.ts +65 -1
- package/dist/index.js +123 -3
- package/dist/nav/index.d.ts +83 -0
- package/dist/nav/index.js +9 -0
- package/dist/nav/registry.d.ts +15 -0
- package/dist/nav/registry.js +42 -0
- package/dist/nav/resolver.d.ts +8 -0
- package/dist/nav/resolver.js +192 -0
- package/dist/nav/workspace.d.ts +18 -0
- package/dist/nav/workspace.js +103 -0
- package/dist/pipeline/styles.js +7 -35
- package/dist/pipeline/token-transformers.d.ts +45 -0
- package/dist/pipeline/token-transformers.js +71 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +17 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.js +87 -0
- package/dist/registry/asset-storage/define.d.ts +10 -0
- package/dist/registry/asset-storage/define.js +13 -0
- package/dist/registry/asset-storage/resolve.d.ts +45 -0
- package/dist/registry/asset-storage/resolve.js +36 -0
- package/dist/registry/asset-storage/types.d.ts +51 -0
- package/dist/registry/asset-storage/types.js +9 -0
- package/dist/registry/assets/layout.d.ts +22 -0
- package/dist/registry/assets/layout.js +30 -0
- package/dist/registry/assets/sets.d.ts +18 -0
- package/dist/registry/assets/sets.js +44 -0
- package/dist/registry/assets/transfer.d.ts +45 -0
- package/dist/registry/assets/transfer.js +11 -0
- package/dist/registry/checkout/assets.d.ts +16 -0
- package/dist/registry/checkout/assets.js +150 -0
- package/dist/registry/checkout/index.d.ts +33 -0
- package/dist/registry/checkout/index.js +610 -0
- package/dist/registry/checkout/tokens.d.ts +15 -0
- package/dist/registry/checkout/tokens.js +171 -0
- package/dist/registry/client.d.ts +61 -0
- package/dist/registry/client.js +211 -0
- package/dist/registry/connection.d.ts +30 -0
- package/dist/registry/connection.js +36 -0
- package/dist/registry/db/adapter.d.ts +43 -0
- package/dist/registry/db/adapter.js +59 -0
- package/dist/registry/db/client.d.ts +35 -0
- package/dist/registry/db/client.js +94 -0
- package/dist/registry/db/index.d.ts +5 -0
- package/dist/registry/db/index.js +24 -0
- package/dist/registry/db/migrate.d.ts +23 -0
- package/dist/registry/db/migrate.js +64 -0
- package/dist/registry/db/schema.d.ts +4869 -0
- package/dist/registry/db/schema.js +347 -0
- package/dist/registry/path.d.ts +4 -0
- package/dist/registry/path.js +31 -0
- package/dist/registry/publish/artifacts.d.ts +16 -0
- package/dist/registry/publish/artifacts.js +132 -0
- package/dist/registry/publish/assets.d.ts +16 -0
- package/dist/registry/publish/assets.js +129 -0
- package/dist/registry/publish/errors.d.ts +5 -0
- package/dist/registry/publish/errors.js +26 -0
- package/dist/registry/publish/index.d.ts +27 -0
- package/dist/registry/publish/index.js +126 -0
- package/dist/registry/publish/package.d.ts +27 -0
- package/dist/registry/publish/package.js +257 -0
- package/dist/registry/publish/publish-build.d.ts +12 -0
- package/dist/registry/publish/publish-build.js +43 -0
- package/dist/registry/publish/tokens.d.ts +18 -0
- package/dist/registry/publish/tokens.js +112 -0
- package/dist/registry/tokens/sets.d.ts +59 -0
- package/dist/registry/tokens/sets.js +109 -0
- package/dist/registry/tokens/transfer.d.ts +48 -0
- package/dist/registry/tokens/transfer.js +10 -0
- package/dist/registry/transfer.d.ts +75 -0
- package/dist/registry/transfer.js +11 -0
- package/dist/store/filesystem-assets.d.ts +17 -0
- package/dist/store/filesystem-assets.js +147 -0
- package/dist/store/filesystem.d.ts +76 -0
- package/dist/store/filesystem.js +208 -0
- package/dist/store/index.d.ts +20 -0
- package/dist/store/index.js +38 -0
- package/dist/store/registry.d.ts +77 -0
- package/dist/store/registry.js +350 -0
- package/dist/store/source-files.d.ts +24 -0
- package/dist/store/source-files.js +147 -0
- package/dist/store/types.d.ts +185 -0
- package/dist/store/types.js +12 -0
- package/dist/transformers/plugins/csf-render.js +17 -8
- package/dist/transformers/plugins/handlebars-previews.js +10 -3
- package/dist/transformers/plugins/ssr-render.js +116 -49
- package/dist/transformers/preview/component/api.js +3 -1
- package/dist/transformers/preview/component/artifacts.js +8 -1
- package/dist/transformers/preview/component/builder.js +13 -5
- package/dist/transformers/preview/component/css.js +9 -1
- package/dist/transformers/preview/component/javascript.d.ts +9 -3
- package/dist/transformers/preview/component/javascript.js +45 -17
- package/dist/transformers/preview/component/shared-artifacts.d.ts +83 -0
- package/dist/transformers/preview/component/shared-artifacts.js +92 -0
- package/dist/transformers/preview/component.js +5 -2
- package/dist/transformers/preview/pattern/builder.js +28 -9
- package/dist/transformers/preview/pattern/html.d.ts +13 -10
- package/dist/transformers/preview/pattern/html.js +62 -27
- package/dist/transformers/preview/types.d.ts +38 -0
- package/dist/transformers/utils/handlebars.d.ts +3 -0
- package/dist/transformers/utils/handlebars.js +19 -5
- package/dist/types/config.d.ts +118 -25
- package/dist/utils/menu-shell.d.ts +60 -0
- package/dist/utils/menu-shell.js +221 -0
- package/dist/utils/stable-stringify.d.ts +2 -0
- package/dist/utils/stable-stringify.js +8 -0
- package/docs/cli.md +4 -2
- package/drizzle/0000_init_registry_storage.sql +95 -0
- package/drizzle/0001_add_review_metadata.sql +2 -0
- package/drizzle/0002_add_pages.sql +28 -0
- package/drizzle/0003_add_tokens.sql +29 -0
- package/drizzle/0004_add_assets.sql +37 -0
- package/drizzle/meta/0000_snapshot.json +642 -0
- package/drizzle/meta/0001_snapshot.json +654 -0
- package/drizzle/meta/0002_snapshot.json +835 -0
- package/drizzle/meta/0003_snapshot.json +1023 -0
- package/drizzle/meta/0004_snapshot.json +1252 -0
- package/drizzle/meta/_journal.json +41 -0
- package/drizzle.config.ts +16 -0
- package/package.json +10 -1
- package/src/app/components/Component/Preview.tsx +3 -2
- package/src/app/components/Component/RegistryActions.tsx +150 -0
- package/src/app/components/Foundations/DisplayLogo.tsx +5 -3
- package/src/app/components/Layout/Header.tsx +21 -3
- package/src/app/components/Layout/Main.tsx +11 -3
- package/src/app/components/Navigation/MainNav.tsx +4 -2
- package/src/app/components/Navigation/MobileNav.tsx +4 -2
- package/src/app/components/Navigation/SideNav.tsx +46 -11
- package/src/app/components/NotFound.tsx +24 -0
- package/src/app/components/context/NavProvider.tsx +71 -0
- package/src/app/components/old/Header.tsx +3 -3
- package/src/app/components/util/index.ts +203 -380
- package/src/app/components/util/useCollectionAssets.ts +125 -0
- package/src/app/components/util/useFoundationTokens.ts +67 -0
- package/src/app/generated/default-pages.json +1 -0
- package/src/app/generated/nav-shell.json +1 -0
- package/src/app/lib/api/query.ts +6 -0
- package/src/app/lib/api/redact.ts +46 -0
- package/src/app/lib/asset-storage.ts +81 -0
- package/src/app/lib/docs-api/artifacts.ts +116 -0
- package/src/app/lib/docs-api/backend.ts +151 -0
- package/src/app/lib/docs-api/errors.ts +52 -0
- package/src/app/lib/docs-api/handler.ts +34 -0
- package/src/app/lib/docs-api/index.ts +7 -0
- package/src/app/lib/docs-api/records.ts +189 -0
- package/src/app/lib/docs-api/registry.ts +130 -0
- package/src/app/lib/docs-api/runtime-config.ts +173 -0
- package/src/app/lib/docs-api/serve.ts +37 -0
- package/src/app/lib/docs-api/token-detail.ts +19 -0
- package/src/app/lib/registry-api/allowlist.ts +163 -0
- package/src/app/lib/registry-api/asset-transfer.ts +363 -0
- package/src/app/lib/registry-api/auth.ts +68 -0
- package/src/app/lib/registry-api/entity-routes.ts +197 -0
- package/src/app/lib/registry-api/errors.ts +51 -0
- package/src/app/lib/registry-api/files.ts +135 -0
- package/src/app/lib/registry-api/handler.ts +96 -0
- package/src/app/lib/registry-api/index.ts +4 -0
- package/src/app/lib/registry-api/meta.ts +87 -0
- package/src/app/lib/registry-api/revalidate.ts +36 -0
- package/src/app/lib/registry-api/store.ts +348 -0
- package/src/app/lib/registry-api/token-transfer.ts +231 -0
- package/src/app/lib/registry-api/transfer.ts +513 -0
- package/src/app/lib/registry-api/validation.ts +74 -0
- package/src/app/lib/registry-connection.ts +58 -0
- package/src/app/lib/utils.ts +14 -0
- package/src/app/next.config.mjs +72 -13
- package/src/app/pages/[...slug]/index.tsx +74 -20
- package/src/app/pages/_app.tsx +5 -2
- package/src/app/pages/api/docs/artifacts/[...artifactPath].ts +16 -0
- package/src/app/pages/api/docs/assets/[collection]/[...path].ts +50 -0
- package/src/app/pages/api/docs/assets/[collection]/index.ts +22 -0
- package/src/app/pages/api/docs/components/[id]/assets/[asset].ts +33 -0
- package/src/app/pages/api/docs/components/[id]/inspect/[previewId].ts +20 -0
- package/src/app/pages/api/docs/components/[id]/previews/[previewId].ts +21 -0
- package/src/app/pages/api/docs/components/[id].ts +24 -0
- package/src/app/pages/api/docs/components.json.ts +12 -0
- package/src/app/pages/api/docs/nav.json.ts +48 -0
- package/src/app/pages/api/docs/patterns/[id]/preview.ts +19 -0
- package/src/app/pages/api/docs/patterns/[id].ts +24 -0
- package/src/app/pages/api/docs/patterns.json.ts +12 -0
- package/src/app/pages/api/docs/tokens/components/[id].ts +30 -0
- package/src/app/pages/api/docs/tokens/foundations/[type].ts +32 -0
- package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/components/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/components/[id].ts +20 -0
- package/src/app/pages/api/registry/components/index.ts +19 -0
- package/src/app/pages/api/registry/health.ts +20 -0
- package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/patterns/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/patterns/[id].ts +20 -0
- package/src/app/pages/api/registry/patterns/index.ts +19 -0
- package/src/app/pages/api/registry/transfer/[kind]/[id].ts +39 -0
- package/src/app/pages/api/registry/transfer/assets/[collection].ts +24 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +23 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +11 -0
- package/src/app/pages/api/registry/transfer/assets/index.ts +10 -0
- package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +26 -0
- package/src/app/pages/api/registry/transfer/tokens/index.ts +11 -0
- package/src/app/pages/assets/fonts.tsx +41 -17
- package/src/app/pages/assets/index.tsx +2 -2
- package/src/app/pages/assets/logos.tsx +2 -2
- package/src/app/pages/foundations/colors.tsx +18 -8
- package/src/app/pages/foundations/effects.tsx +18 -8
- package/src/app/pages/foundations/icons/[name]/index.tsx +26 -14
- package/src/app/pages/foundations/icons/index.tsx +13 -9
- package/src/app/pages/foundations/index.tsx +1 -1
- package/src/app/pages/foundations/logo.tsx +7 -4
- package/src/app/pages/foundations/typography.tsx +19 -9
- package/src/app/pages/index.tsx +1 -1
- package/src/app/pages/system/component/[component]/index.tsx +87 -17
- package/src/app/pages/system/index.tsx +13 -4
- package/src/app/pages/system/pattern/[pattern]/index.tsx +70 -14
- package/src/app/pages/system/tokens/components/[component]/index.tsx +42 -14
- package/src/app/pages/system/tokens/foundations/colors.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/effects.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/typography.tsx +8 -4
- package/src/app/tsconfig.json +2 -3
- package/src/app-builder/build.ts +555 -11
- package/src/app-builder/client-config.ts +61 -4
- package/src/app-builder/config-diff/snapshot.ts +1 -11
- package/src/app-builder/index.ts +1 -1
- package/src/app-builder/nav-shell.ts +74 -0
- package/src/app-builder/static-export.ts +215 -0
- package/src/app-builder/vercel-output.ts +297 -0
- package/src/app-builder/watchers/utils.ts +2 -0
- package/src/app-builder/watchers.ts +149 -25
- package/src/app-builder/websocket.ts +2 -2
- package/src/artifacts/README.md +14 -0
- package/src/artifacts/index.ts +17 -0
- package/src/artifacts/types.ts +146 -0
- package/src/artifacts/url.ts +90 -0
- package/src/cache/build-cache.ts +47 -2
- package/src/commands/build/app.ts +1 -2
- package/src/commands/build/index.ts +61 -0
- package/src/commands/checkout/index.ts +66 -0
- package/src/commands/db/migrate.ts +25 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/publish/index.ts +63 -0
- package/src/config/defaults.ts +15 -0
- package/src/config/loader.ts +10 -1
- package/src/config/normalizers/declaration.ts +3 -0
- package/src/config/normalizers/page.ts +45 -0
- package/src/config/normalizers/pattern.ts +4 -1
- package/src/config/runtime.ts +48 -2
- package/src/index.ts +126 -3
- package/src/nav/index.ts +94 -0
- package/src/nav/registry.ts +54 -0
- package/src/nav/resolver.ts +217 -0
- package/src/nav/workspace.ts +87 -0
- package/src/pipeline/styles.ts +4 -35
- package/src/pipeline/token-transformers.ts +97 -0
- package/src/registry/asset-storage/adapters/vercel-blob.ts +51 -0
- package/src/registry/asset-storage/define.ts +15 -0
- package/src/registry/asset-storage/resolve.ts +72 -0
- package/src/registry/asset-storage/types.ts +46 -0
- package/src/registry/assets/layout.ts +38 -0
- package/src/registry/assets/sets.ts +48 -0
- package/src/registry/assets/transfer.ts +50 -0
- package/src/registry/checkout/assets.ts +126 -0
- package/src/registry/checkout/index.ts +596 -0
- package/src/registry/checkout/tokens.ts +152 -0
- package/src/registry/client.ts +234 -0
- package/src/registry/connection.ts +50 -0
- package/src/registry/db/adapter.ts +74 -0
- package/src/registry/db/client.ts +88 -0
- package/src/registry/db/index.ts +35 -0
- package/src/registry/db/migrate.ts +56 -0
- package/src/registry/db/schema.ts +444 -0
- package/src/registry/path.ts +26 -0
- package/src/registry/publish/artifacts.ts +147 -0
- package/src/registry/publish/assets.ts +129 -0
- package/src/registry/publish/errors.ts +34 -0
- package/src/registry/publish/index.ts +128 -0
- package/src/registry/publish/package.ts +272 -0
- package/src/registry/publish/publish-build.ts +51 -0
- package/src/registry/publish/tokens.ts +119 -0
- package/src/registry/tokens/sets.ts +132 -0
- package/src/registry/tokens/transfer.ts +53 -0
- package/src/registry/transfer.ts +82 -0
- package/src/store/filesystem-assets.ts +152 -0
- package/src/store/filesystem.ts +237 -0
- package/src/store/index.ts +62 -0
- package/src/store/registry.ts +334 -0
- package/src/store/source-files.ts +155 -0
- package/src/store/types.ts +210 -0
- package/src/transformers/plugins/csf-render.ts +31 -7
- package/src/transformers/plugins/handlebars-previews.ts +22 -3
- package/src/transformers/plugins/ssr-render.ts +145 -57
- package/src/transformers/preview/component/api.ts +2 -1
- package/src/transformers/preview/component/artifacts.ts +10 -1
- package/src/transformers/preview/component/builder.ts +9 -2
- package/src/transformers/preview/component/css.ts +10 -1
- package/src/transformers/preview/component/javascript.ts +55 -20
- package/src/transformers/preview/component/shared-artifacts.ts +146 -0
- package/src/transformers/preview/component.ts +5 -2
- package/src/transformers/preview/pattern/builder.ts +33 -7
- package/src/transformers/preview/pattern/html.ts +71 -27
- package/src/transformers/preview/types.ts +42 -0
- package/src/transformers/utils/handlebars.ts +31 -5
- package/src/types/config.ts +123 -25
- package/src/utils/menu-shell.ts +241 -0
- package/src/utils/stable-stringify.ts +7 -0
- package/templates/default/package.json.tpl +1 -1
- package/handoff.config.js +0 -90
- package/src/app/components/ComponentSearch.tsx +0 -93
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized store abstraction.
|
|
3
|
+
*
|
|
4
|
+
* Both the workspace (filesystem) store and the future registry (database) store implement one
|
|
5
|
+
* read contract, so the docs read API and other consumers are storage-agnostic.
|
|
6
|
+
* Read methods are typed `Awaitable` so the filesystem implementation can stay synchronous
|
|
7
|
+
* while a database implementation is async behind the same interface.
|
|
8
|
+
*
|
|
9
|
+
* This module is types-only and provider-agnostic.
|
|
10
|
+
*/
|
|
11
|
+
import type { TokenSetKind } from '../registry/tokens/sets';
|
|
12
|
+
import type { ComponentListObject, PageListObject, PatternListObject } from '../transformers/preview/types';
|
|
13
|
+
/** A value that may be returned directly or as a promise. */
|
|
14
|
+
export type Awaitable<T> = T | Promise<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Kind of a text source file tracked by the store. Mirrors the registry text-file model.
|
|
17
|
+
* `declaration` is **workspace-only** — registry stores never return it
|
|
18
|
+
* and declarations are synthesized on checkout.
|
|
19
|
+
*/
|
|
20
|
+
export type TextFileKind = 'declaration' | 'component' | 'template' | 'style' | 'script' | 'story' | 'docs' | 'markdown' | 'schema' | 'other';
|
|
21
|
+
export type RegistryTextFileKind = Exclude<TextFileKind, 'declaration'>;
|
|
22
|
+
/**
|
|
23
|
+
* A normalized text source file belonging to a component or pattern. Used for checkout,
|
|
24
|
+
* inspection, and publish — never to rebuild previews during docs serving.
|
|
25
|
+
*/
|
|
26
|
+
export interface TextFileResource {
|
|
27
|
+
/** Registry-safe relative path of the file within the entity (e.g. `Badge.tsx`). */
|
|
28
|
+
path: string;
|
|
29
|
+
/** Absolute filesystem path the content was read from (workspace store only). */
|
|
30
|
+
absolutePath: string;
|
|
31
|
+
/** Classified file kind. */
|
|
32
|
+
kind: TextFileKind;
|
|
33
|
+
/** File content. */
|
|
34
|
+
content: string;
|
|
35
|
+
/** Content type to serve/transfer the file with. */
|
|
36
|
+
contentType: string;
|
|
37
|
+
}
|
|
38
|
+
export type RegistryTextFileResource = TextFileResource & {
|
|
39
|
+
kind: RegistryTextFileKind;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* A reference to a source file resolvable by a store. For the filesystem store this is an
|
|
43
|
+
* absolute path; the contract keeps it opaque so a database store can use its own reference form.
|
|
44
|
+
*/
|
|
45
|
+
export interface SourceReference {
|
|
46
|
+
/** Absolute filesystem path (workspace store). */
|
|
47
|
+
absolutePath: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read contract implemented by every component store backing. Consumers depend only on this so
|
|
51
|
+
* they never need to know whether the filesystem or a database is active.
|
|
52
|
+
*/
|
|
53
|
+
export interface ComponentStore {
|
|
54
|
+
/** All normalized component records, identical in shape to today's runtime records. */
|
|
55
|
+
list(): Awaitable<ComponentListObject[]>;
|
|
56
|
+
/** A single normalized component record by stable id, or `null` when absent. */
|
|
57
|
+
get(id: string): Awaitable<ComponentListObject | null>;
|
|
58
|
+
/** Read a source file by reference, or `null` when it cannot be resolved. */
|
|
59
|
+
getSource(ref: SourceReference): Awaitable<TextFileResource | null>;
|
|
60
|
+
/**
|
|
61
|
+
* The entity's declaration plus every `entries`-referenced source file, for checkout/publish.
|
|
62
|
+
* Returns an empty array when the entity is unknown.
|
|
63
|
+
*/
|
|
64
|
+
getRelatedSourceFiles(id: string): Awaitable<TextFileResource[]>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read contract implemented by every pattern store backing. Same shape as {@link ComponentStore}
|
|
68
|
+
* over pattern records.
|
|
69
|
+
*/
|
|
70
|
+
export interface PatternStore {
|
|
71
|
+
/** All normalized pattern records, identical in shape to today's runtime records. */
|
|
72
|
+
list(): Awaitable<PatternListObject[]>;
|
|
73
|
+
/** A single normalized pattern record by stable id, or `null` when absent. */
|
|
74
|
+
get(id: string): Awaitable<PatternListObject | null>;
|
|
75
|
+
/** Read a source file by reference, or `null` when it cannot be resolved. */
|
|
76
|
+
getSource(ref: SourceReference): Awaitable<TextFileResource | null>;
|
|
77
|
+
/**
|
|
78
|
+
* The pattern's declaration plus every `entries`-referenced source file, for checkout/publish.
|
|
79
|
+
* Returns an empty array when the pattern is unknown.
|
|
80
|
+
*/
|
|
81
|
+
getRelatedSourceFiles(id: string): Awaitable<TextFileResource[]>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read contract implemented by every page store backing. Same shape as {@link PatternStore} over
|
|
85
|
+
* page records. A page's single related source file is its verbatim `.md` (kind `markdown`).
|
|
86
|
+
*/
|
|
87
|
+
export interface PageStore {
|
|
88
|
+
/** All normalized page records. */
|
|
89
|
+
list(): Awaitable<PageListObject[]>;
|
|
90
|
+
/** A single normalized page record by stable id, or `null` when absent. */
|
|
91
|
+
get(id: string): Awaitable<PageListObject | null>;
|
|
92
|
+
/** Read a source file by reference, or `null` when it cannot be resolved. */
|
|
93
|
+
getSource(ref: SourceReference): Awaitable<TextFileResource | null>;
|
|
94
|
+
/** The page's verbatim `.md` source file, for checkout/publish. Empty when the page is unknown. */
|
|
95
|
+
getRelatedSourceFiles(id: string): Awaitable<TextFileResource[]>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A logical token set as the store exposes it: its stable id, kind, and the exact extracted token
|
|
99
|
+
* slice (a `record` of `IColorObject[]`/…/`IFileComponentObject`). Consumers read the whole set and
|
|
100
|
+
* group/sort/look up in memory, so there is no individual-token accessor.
|
|
101
|
+
*/
|
|
102
|
+
export interface TokenSetRecord {
|
|
103
|
+
id: string;
|
|
104
|
+
kind: TokenSetKind;
|
|
105
|
+
record: unknown;
|
|
106
|
+
}
|
|
107
|
+
/** A generated token artifact (CSS/SCSS/Style Dictionary/types/custom output) as the store returns it. */
|
|
108
|
+
export interface TokenArtifactResource {
|
|
109
|
+
/** Registry-safe relative output path under the tokens dir (e.g. `css/colors.css`). */
|
|
110
|
+
path: string;
|
|
111
|
+
/** Logical format label (`css`|`scss`|`types`|`styleDictionary`|custom). */
|
|
112
|
+
format: string;
|
|
113
|
+
content: string;
|
|
114
|
+
contentType: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Read contract implemented by every token store backing. The filesystem backing derives sets from
|
|
118
|
+
* the local `tokens.json` + generated files; the registry backing reads the `token_sets` /
|
|
119
|
+
* `token_artifacts` tables. Consumers depend only on this so token behavior is consistent across
|
|
120
|
+
* modes even though the storage differs.
|
|
121
|
+
*/
|
|
122
|
+
export interface TokenStore {
|
|
123
|
+
/** All logical token sets (three foundation sets + one per component). */
|
|
124
|
+
listSets(): Awaitable<TokenSetRecord[]>;
|
|
125
|
+
/** A single set by stable id, or `null` when absent. */
|
|
126
|
+
getSet(id: string): Awaitable<TokenSetRecord | null>;
|
|
127
|
+
/** Every generated artifact owned by a set. Empty when the set is unknown or has no output. */
|
|
128
|
+
getArtifacts(id: string): Awaitable<TokenArtifactResource[]>;
|
|
129
|
+
/** One generated artifact for a set by logical format, or `null` when absent. */
|
|
130
|
+
getArtifact(id: string, format: string): Awaitable<TokenArtifactResource | null>;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Lightweight metadata for one published asset. Collection/list reads return only this shape - never
|
|
134
|
+
* the binary body - so large collections do not produce oversized responses.
|
|
135
|
+
*/
|
|
136
|
+
export interface AssetMetadata {
|
|
137
|
+
/** Collection the asset belongs to (`icons`|`logos`|`fonts`). */
|
|
138
|
+
collection: string;
|
|
139
|
+
/** Registry-safe logical path within the collection (e.g. `assets/icons/add.svg`, `icons.zip`). */
|
|
140
|
+
path: string;
|
|
141
|
+
/** Human-facing asset name. */
|
|
142
|
+
name: string;
|
|
143
|
+
/** Content type to serve the asset with. */
|
|
144
|
+
contentType: string;
|
|
145
|
+
/** Byte length. */
|
|
146
|
+
size: number;
|
|
147
|
+
/** SHA-256 of the bytes (hex) - content identity + ETag. */
|
|
148
|
+
contentHash: string;
|
|
149
|
+
/** Free-form asset metadata carried from extraction (icon index, description, …). */
|
|
150
|
+
metadata?: Record<string, unknown> | null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* An asset's resolved content: inline bytes (filesystem store, or a DB-backed blob), or a redirect
|
|
154
|
+
* URL when an object-storage provider serves the content directly.
|
|
155
|
+
*/
|
|
156
|
+
export interface AssetContentResource extends AssetMetadata {
|
|
157
|
+
/** Inline bytes, when the content is served from the filesystem or an inline DB blob. */
|
|
158
|
+
body?: Buffer;
|
|
159
|
+
/** Signed/public provider URL to redirect to, when an object-storage provider serves the content. */
|
|
160
|
+
redirectUrl?: string;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Read contract implemented by every asset store backing. The filesystem backing derives collections
|
|
164
|
+
* from the generated `public/api` tree + archives; the registry backing reads the `assets` /
|
|
165
|
+
* `asset_collections` tables and resolves content through the configured storage provider. List
|
|
166
|
+
* methods return metadata only; content is fetched one asset at a time.
|
|
167
|
+
*/
|
|
168
|
+
export interface AssetStore {
|
|
169
|
+
/** All collections that have any published assets. */
|
|
170
|
+
listCollections(): Awaitable<string[]>;
|
|
171
|
+
/** Lightweight metadata for every asset in a collection (no bodies). Empty when unknown. */
|
|
172
|
+
listAssets(collection: string): Awaitable<AssetMetadata[]>;
|
|
173
|
+
/** Metadata for one asset by collection + logical path, or `null` when absent. */
|
|
174
|
+
getAsset(collection: string, path: string): Awaitable<AssetMetadata | null>;
|
|
175
|
+
/** One asset's resolved content (bytes or redirect), or `null` when absent. */
|
|
176
|
+
getAssetContent(collection: string, path: string): Awaitable<AssetContentResource | null>;
|
|
177
|
+
}
|
|
178
|
+
/** Convenience pairing of the stores backing one runtime. */
|
|
179
|
+
export interface HandoffStore {
|
|
180
|
+
components: ComponentStore;
|
|
181
|
+
patterns: PatternStore;
|
|
182
|
+
pages: PageStore;
|
|
183
|
+
tokens: TokenStore;
|
|
184
|
+
assets: AssetStore;
|
|
185
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Normalized store abstraction.
|
|
4
|
+
*
|
|
5
|
+
* Both the workspace (filesystem) store and the future registry (database) store implement one
|
|
6
|
+
* read contract, so the docs read API and other consumers are storage-agnostic.
|
|
7
|
+
* Read methods are typed `Awaitable` so the filesystem implementation can stay synchronous
|
|
8
|
+
* while a database implementation is async behind the same interface.
|
|
9
|
+
*
|
|
10
|
+
* This module is types-only and provider-agnostic.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -22,7 +22,7 @@ const react_element_to_jsx_string_1 = __importDefault(require("react-element-to-
|
|
|
22
22
|
const logger_1 = require("../../utils/logger");
|
|
23
23
|
const docgen_1 = require("../docgen");
|
|
24
24
|
const component_1 = require("../preview/component");
|
|
25
|
-
const
|
|
25
|
+
const shared_artifacts_1 = require("../preview/component/shared-artifacts");
|
|
26
26
|
const html_1 = require("../utils/html");
|
|
27
27
|
const module_1 = require("../utils/module");
|
|
28
28
|
const schema_1 = require("../utils/schema");
|
|
@@ -235,15 +235,20 @@ function buildUsageSnippet(componentName, importStatement, element, properties,
|
|
|
235
235
|
importStatement,
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
|
-
function createHtmlDocument(componentId, previewTitle, renderedHtml) {
|
|
239
|
-
var _a
|
|
238
|
+
function createHtmlDocument(componentId, previewTitle, renderedHtml, sharedArtifacts, componentArtifacts) {
|
|
239
|
+
var _a;
|
|
240
|
+
const basePath = (_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : '';
|
|
241
|
+
// Shared/global and component-owned artifacts are referenced only when present, so a CSF preview
|
|
242
|
+
// never requests an absent optional artifact. CSF previews stay static; the global script runs
|
|
243
|
+
// only for its top-level side effects when a global JS entry exists.
|
|
244
|
+
const sharedStyleLinks = (0, shared_artifacts_1.renderSharedStyleLinks)(sharedArtifacts, basePath);
|
|
245
|
+
const componentStyleLink = (0, shared_artifacts_1.renderComponentStyleLink)(componentArtifacts, componentId, basePath);
|
|
246
|
+
const globalScriptTag = (0, shared_artifacts_1.renderGlobalScriptTag)(sharedArtifacts, basePath);
|
|
240
247
|
return `<!DOCTYPE html>
|
|
241
248
|
<html lang="en">
|
|
242
249
|
<head>
|
|
243
|
-
<meta charset="UTF-8"
|
|
244
|
-
<link rel="stylesheet" href="${
|
|
245
|
-
<link rel="stylesheet" href="${(_b = process.env.HANDOFF_APP_BASE_PATH) !== null && _b !== void 0 ? _b : ''}/api/component/${componentId}.css" />
|
|
246
|
-
<link rel="stylesheet" href="${(_c = process.env.HANDOFF_APP_BASE_PATH) !== null && _c !== void 0 ? _c : ''}/assets/css/preview.css" />
|
|
250
|
+
<meta charset="UTF-8" />${sharedStyleLinks ? `\n ${sharedStyleLinks}` : ''}${componentStyleLink ? `\n ${componentStyleLink}` : ''}
|
|
251
|
+
<link rel="stylesheet" href="${basePath}/assets/css/preview.css" />${globalScriptTag ? `\n ${globalScriptTag}` : ''}
|
|
247
252
|
<title>${previewTitle}</title>
|
|
248
253
|
</head>
|
|
249
254
|
<body>
|
|
@@ -359,6 +364,10 @@ function csfRenderPlugin(componentData, _documentationComponents, handoff) {
|
|
|
359
364
|
};
|
|
360
365
|
}
|
|
361
366
|
componentData.properties = (0, schema_1.ensureIds)(generatedProperties);
|
|
367
|
+
// Resolve which shared/global and component-owned artifacts exist so generated HTML references
|
|
368
|
+
// them only when present. These are built before component HTML, so this reflects final state.
|
|
369
|
+
const sharedArtifacts = (0, shared_artifacts_1.resolveSharedArtifactPresence)(handoff);
|
|
370
|
+
const componentArtifacts = (0, shared_artifacts_1.resolveComponentArtifactPresence)(handoff, componentId);
|
|
362
371
|
let lastHtml = '';
|
|
363
372
|
for (const [previewKey, preview] of Object.entries(componentData.previews)) {
|
|
364
373
|
const storyKey = preview.sourcePreview && storyMap[preview.sourcePreview]
|
|
@@ -367,7 +376,7 @@ function csfRenderPlugin(componentData, _documentationComponents, handoff) {
|
|
|
367
376
|
const storyValue = storyKey ? storyMap[storyKey] : undefined;
|
|
368
377
|
const storyArgs = preview.values || {};
|
|
369
378
|
const rendered = safeRenderToHtml(meta, storyValue, storyArgs);
|
|
370
|
-
const html = yield (0, html_1.formatHtml)(createHtmlDocument(componentId, preview.title, rendered));
|
|
379
|
+
const html = yield (0, html_1.formatHtml)(createHtmlDocument(componentId, preview.title, rendered, sharedArtifacts, componentArtifacts));
|
|
371
380
|
const fileName = `${componentId}-${previewKey}.html`;
|
|
372
381
|
this.emitFile({
|
|
373
382
|
type: 'asset',
|
|
@@ -17,6 +17,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
17
17
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
18
18
|
const path_1 = __importDefault(require("path"));
|
|
19
19
|
const logger_1 = require("../../utils/logger");
|
|
20
|
+
const shared_artifacts_1 = require("../preview/component/shared-artifacts");
|
|
20
21
|
const handlebars_2 = require("../utils/handlebars");
|
|
21
22
|
const html_1 = require("../utils/html");
|
|
22
23
|
const string_1 = require("../utils/string");
|
|
@@ -61,7 +62,7 @@ function processComponentInstances(componentData, documentationComponents) {
|
|
|
61
62
|
* @param injectFieldWrappers - Whether to inject field wrappers for inspection
|
|
62
63
|
* @returns Rendered HTML string
|
|
63
64
|
*/
|
|
64
|
-
function renderHandlebarsTemplate(template, componentData, previewData, injectFieldWrappers, handoff) {
|
|
65
|
+
function renderHandlebarsTemplate(template, componentData, previewData, injectFieldWrappers, handoff, sharedArtifacts, componentArtifacts) {
|
|
65
66
|
return __awaiter(this, void 0, void 0, function* () {
|
|
66
67
|
var _a, _b;
|
|
67
68
|
// Register Handlebars helpers with current injection state
|
|
@@ -72,6 +73,8 @@ function renderHandlebarsTemplate(template, componentData, previewData, injectFi
|
|
|
72
73
|
title: componentData.title
|
|
73
74
|
}, previewData, {
|
|
74
75
|
includeSharedStyles: Boolean(componentData.sharedStyles),
|
|
76
|
+
sharedArtifacts,
|
|
77
|
+
componentArtifacts,
|
|
75
78
|
});
|
|
76
79
|
const compiled = handlebars_1.default.compile(template)(context);
|
|
77
80
|
return yield (0, html_1.formatHtmlWithWrapper)(compiled);
|
|
@@ -143,13 +146,17 @@ function handlebarsPreviewsPlugin(componentData, documentationComponents, handof
|
|
|
143
146
|
};
|
|
144
147
|
}
|
|
145
148
|
const generatedPreviews = {};
|
|
149
|
+
// Resolve which shared/global and component-owned artifacts exist so generated HTML references
|
|
150
|
+
// them only when present. These are built before component HTML, so this reflects final state.
|
|
151
|
+
const sharedArtifacts = (0, shared_artifacts_1.resolveSharedArtifactPresence)(handoff);
|
|
152
|
+
const componentArtifacts = (0, shared_artifacts_1.resolveComponentArtifactPresence)(handoff, componentId);
|
|
146
153
|
// Generate previews for each variation
|
|
147
154
|
for (const previewKey in componentData.previews) {
|
|
148
155
|
try {
|
|
149
156
|
const previewData = componentData.previews[previewKey];
|
|
150
157
|
// Render both normal and inspect modes
|
|
151
|
-
const normalModeHtml = yield renderHandlebarsTemplate(templateContent, componentData, previewData, false, handoff);
|
|
152
|
-
const inspectModeHtml = yield renderHandlebarsTemplate(templateContent, componentData, previewData, true, handoff);
|
|
158
|
+
const normalModeHtml = yield renderHandlebarsTemplate(templateContent, componentData, previewData, false, handoff, sharedArtifacts, componentArtifacts);
|
|
159
|
+
const inspectModeHtml = yield renderHandlebarsTemplate(templateContent, componentData, previewData, true, handoff, sharedArtifacts, componentArtifacts);
|
|
153
160
|
// Emit preview files
|
|
154
161
|
emitPreviewFiles(componentId, previewKey, normalModeHtml, inspectModeHtml, (file) => this.emitFile(file));
|
|
155
162
|
generatedPreviews[previewKey] = normalModeHtml;
|
|
@@ -19,9 +19,10 @@ const path_1 = __importDefault(require("path"));
|
|
|
19
19
|
const react_1 = __importDefault(require("react"));
|
|
20
20
|
const server_1 = __importDefault(require("react-dom/server"));
|
|
21
21
|
const vite_1 = require("vite");
|
|
22
|
+
const url_1 = require("../../artifacts/url");
|
|
22
23
|
const logger_1 = require("../../utils/logger");
|
|
23
24
|
const docgen_1 = require("../docgen");
|
|
24
|
-
const
|
|
25
|
+
const shared_artifacts_1 = require("../preview/component/shared-artifacts");
|
|
25
26
|
const build_1 = require("../utils/build");
|
|
26
27
|
const html_1 = require("../utils/html");
|
|
27
28
|
const module_1 = require("../utils/module");
|
|
@@ -40,6 +41,14 @@ const PLUGIN_CONSTANTS = {
|
|
|
40
41
|
PROPS_SCRIPT_ID: '__APP_PROPS__',
|
|
41
42
|
INSPECT_SUFFIX: '-inspect',
|
|
42
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Suffix of the component-owned client/hydration artifact.
|
|
46
|
+
* One artifact per component (`component/<id>.client.js`) drives hydration for every preview of
|
|
47
|
+
* that component; it is referenced via the canonical artifact URL.
|
|
48
|
+
*/
|
|
49
|
+
const CLIENT_ARTIFACT_SUFFIX = 'client.js';
|
|
50
|
+
/** Logical artifact path of a component's client/hydration bundle. */
|
|
51
|
+
const clientArtifactPath = (componentId) => `component/${componentId}.${CLIENT_ARTIFACT_SUFFIX}`;
|
|
43
52
|
/**
|
|
44
53
|
* Loads and processes component schema using hierarchical approach
|
|
45
54
|
* @param componentData - Component transformation data
|
|
@@ -88,43 +97,90 @@ function loadComponentSchemaAndModule(componentData, componentPath, handoff) {
|
|
|
88
97
|
});
|
|
89
98
|
}
|
|
90
99
|
/**
|
|
91
|
-
* Generates client-side hydration source code
|
|
100
|
+
* Generates client-side hydration source code for the component-owned client artifact.
|
|
101
|
+
*
|
|
102
|
+
* The bundle is shared by every preview of a component and is emitted once as
|
|
103
|
+
* `component/<id>.client.js`. It hydrates two shapes of mount point so the same artifact serves both
|
|
104
|
+
* a standalone preview document and a composed pattern document without per-fragment inline code:
|
|
105
|
+
*
|
|
106
|
+
* - The standalone preview's `#${ROOT_ELEMENT_ID}` element paired with the `#${PROPS_SCRIPT_ID}`
|
|
107
|
+
* props script (one mount per document).
|
|
108
|
+
* - Any number of pattern mount points marked `[data-handoff-component="<id>"]`, each carrying a
|
|
109
|
+
* `data-handoff-props` attribute naming its (namespaced) props script id. A pattern document
|
|
110
|
+
* references this artifact once per component and the bundle hydrates every matching mount.
|
|
111
|
+
*
|
|
112
|
+
* @param componentId - Component identifier used to match pattern mount points to this bundle
|
|
92
113
|
* @param componentPath - Path to the component file
|
|
93
114
|
* @returns Client-side hydration source code
|
|
94
115
|
*/
|
|
95
|
-
function generateClientHydrationSource(componentPath) {
|
|
116
|
+
function generateClientHydrationSource(componentId, componentPath) {
|
|
96
117
|
return `
|
|
97
118
|
import React from 'react';
|
|
98
119
|
import { hydrateRoot } from 'react-dom/client';
|
|
99
120
|
import Component from '${(0, vite_1.normalizePath)(componentPath)}';
|
|
100
121
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
const parseProps = (propsId) => {
|
|
123
|
+
const raw = propsId ? document.getElementById(propsId)?.textContent : '{}';
|
|
124
|
+
try {
|
|
125
|
+
return JSON.parse(raw || '{}');
|
|
126
|
+
} catch (_) {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const standaloneRoot = document.getElementById('${PLUGIN_CONSTANTS.ROOT_ELEMENT_ID}');
|
|
132
|
+
if (standaloneRoot && !standaloneRoot.hasAttribute('data-handoff-component')) {
|
|
133
|
+
hydrateRoot(standaloneRoot, <Component {...parseProps('${PLUGIN_CONSTANTS.PROPS_SCRIPT_ID}')} />);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const patternMounts = document.querySelectorAll('[data-handoff-component=' + JSON.stringify('${componentId}') + ']');
|
|
137
|
+
patternMounts.forEach((mount) => {
|
|
138
|
+
hydrateRoot(mount, <Component {...parseProps(mount.getAttribute('data-handoff-props'))} />);
|
|
139
|
+
});
|
|
104
140
|
`;
|
|
105
141
|
}
|
|
106
142
|
/**
|
|
107
|
-
* Generates complete HTML document with SSR content and
|
|
143
|
+
* Generates complete HTML document with SSR content and a reference to the component-owned client
|
|
144
|
+
* artifact. The preview HTML carries only document structure, server-rendered markup, preview data,
|
|
145
|
+
* stylesheets, and standard artifact references. Interactive React previews treat the client
|
|
146
|
+
* artifact as a required reference, so the script tag surfaces a clear, visible failure if the
|
|
147
|
+
* artifact cannot be loaded.
|
|
108
148
|
* @param componentId - Component identifier
|
|
109
149
|
* @param previewTitle - Title for the preview
|
|
110
150
|
* @param renderedHtml - Server-rendered HTML content
|
|
111
|
-
* @param clientJs - Client-side JavaScript bundle
|
|
112
151
|
* @param props - Component props as JSON
|
|
113
152
|
* @returns Complete HTML document
|
|
114
153
|
*/
|
|
115
|
-
function generateHtmlDocument(componentId, previewTitle, renderedHtml,
|
|
116
|
-
var _a
|
|
154
|
+
function generateHtmlDocument(componentId, previewTitle, renderedHtml, props, sharedArtifacts, componentArtifacts) {
|
|
155
|
+
var _a;
|
|
156
|
+
const basePath = (_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : '';
|
|
157
|
+
const clientArtifactUrl = (0, url_1.buildArtifactUrl)(clientArtifactPath(componentId), basePath);
|
|
158
|
+
const clientLoadErrorMessage = `Failed to load the React client artifact (${clientArtifactPath(componentId)}). This preview cannot hydrate.`;
|
|
159
|
+
// The handler is JS embedded in a double-quoted HTML attribute, so any `"` (e.g. from
|
|
160
|
+
// JSON.stringify) and `&` must be entity-encoded or the attribute terminates early and
|
|
161
|
+
// breaks HTML parsing. Browsers decode the entities before the JS engine runs the handler.
|
|
162
|
+
const clientLoadErrorHandler = `document.getElementById('${PLUGIN_CONSTANTS.ROOT_ELEMENT_ID}').setAttribute('data-handoff-client-error', '1');console.error(${JSON.stringify(clientLoadErrorMessage)});`
|
|
163
|
+
.replace(/&/g, '&')
|
|
164
|
+
.replace(/"/g, '"');
|
|
165
|
+
// Shared/global stylesheets (`component/main.css`) and the component's own stylesheet
|
|
166
|
+
// (`component/<id>.css`) are emitted only when present so absent optional artifacts never produce a
|
|
167
|
+
// missing-file browser request.
|
|
168
|
+
const sharedStyleLinks = (0, shared_artifacts_1.renderSharedStyleLinks)(sharedArtifacts, basePath);
|
|
169
|
+
const componentStyleLink = (0, shared_artifacts_1.renderComponentStyleLink)(componentArtifacts, componentId, basePath);
|
|
170
|
+
// The global script (`component/main.js`) is a classic tag placed before the client module so it
|
|
171
|
+
// runs before dependent component-specific scripts when present.
|
|
172
|
+
const globalScriptTag = (0, shared_artifacts_1.renderGlobalScriptTag)(sharedArtifacts, basePath);
|
|
117
173
|
return `<!DOCTYPE html>
|
|
118
174
|
<html lang="en">
|
|
119
175
|
<head>
|
|
120
|
-
<meta charset="UTF-8"
|
|
121
|
-
<link rel="stylesheet" href="${
|
|
122
|
-
<
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
${
|
|
127
|
-
|
|
176
|
+
<meta charset="UTF-8" />${sharedStyleLinks ? `\n ${sharedStyleLinks}` : ''}${componentStyleLink ? `\n ${componentStyleLink}` : ''}
|
|
177
|
+
<link rel="stylesheet" href="${basePath}/assets/css/preview.css" />
|
|
178
|
+
<script id="${PLUGIN_CONSTANTS.PROPS_SCRIPT_ID}" type="application/json">${JSON.stringify(props)}</script>${globalScriptTag ? `\n ${globalScriptTag}` : ''}
|
|
179
|
+
<script
|
|
180
|
+
type="module"
|
|
181
|
+
src="${clientArtifactUrl}"
|
|
182
|
+
onerror="${clientLoadErrorHandler}"
|
|
183
|
+
></script>
|
|
128
184
|
<title>${previewTitle}</title>
|
|
129
185
|
</head>
|
|
130
186
|
<body>
|
|
@@ -206,44 +262,55 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
|
|
|
206
262
|
}
|
|
207
263
|
}
|
|
208
264
|
}
|
|
265
|
+
// Build the component-owned client/hydration bundle once and emit it as a single
|
|
266
|
+
// `component/<id>.client.js` artifact. The hydration source only imports the component and
|
|
267
|
+
// reads props from the in-document `__APP_PROPS__` element, so it is identical across every
|
|
268
|
+
// preview of this component.
|
|
269
|
+
const clientHydrationSource = generateClientHydrationSource(componentId, componentPath);
|
|
270
|
+
const clientBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_CLIENT_BUILD_CONFIG), { logLevel: 'silent', stdin: {
|
|
271
|
+
contents: clientHydrationSource,
|
|
272
|
+
resolveDir: process.cwd(),
|
|
273
|
+
loader: 'tsx',
|
|
274
|
+
}, plugins: [(0, build_1.createReactResolvePlugin)(handoff.workingPath, handoff.modulePath)] });
|
|
275
|
+
const finalClientBuildConfig = ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.clientBuildConfig)
|
|
276
|
+
? handoff.config.hooks.clientBuildConfig(clientBuildConfig)
|
|
277
|
+
: clientBuildConfig;
|
|
278
|
+
let clientBundleJs;
|
|
279
|
+
try {
|
|
280
|
+
const bundledClient = yield esbuild_1.default.build(finalClientBuildConfig);
|
|
281
|
+
if (bundledClient.warnings.length > 0) {
|
|
282
|
+
const messages = yield esbuild_1.default.formatMessages(bundledClient.warnings, { kind: 'warning', color: true });
|
|
283
|
+
messages.forEach((msg) => logger_1.Logger.warn(msg));
|
|
284
|
+
}
|
|
285
|
+
clientBundleJs = bundledClient.outputFiles[0].text;
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
// The client artifact is a required reference for interactive React previews. If it cannot be
|
|
289
|
+
// built, do not emit preview HTML that points at a missing artifact — fail clearly instead.
|
|
290
|
+
logger_1.Logger.error(`Failed to build client bundle for ${componentId}`);
|
|
291
|
+
if (error.errors) {
|
|
292
|
+
const messages = yield esbuild_1.default.formatMessages(error.errors, { kind: 'error', color: true });
|
|
293
|
+
messages.forEach((msg) => logger_1.Logger.error(msg));
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
this.emitFile({
|
|
298
|
+
type: 'asset',
|
|
299
|
+
fileName: `${componentId}.${CLIENT_ARTIFACT_SUFFIX}`,
|
|
300
|
+
source: clientBundleJs,
|
|
301
|
+
});
|
|
209
302
|
let finalHtml = '';
|
|
303
|
+
// Resolve which shared/global and component-owned artifacts exist so generated HTML references
|
|
304
|
+
// them only when present. These are built before component HTML, so this reflects final state.
|
|
305
|
+
const sharedArtifacts = (0, shared_artifacts_1.resolveSharedArtifactPresence)(handoff);
|
|
306
|
+
const componentArtifacts = (0, shared_artifacts_1.resolveComponentArtifactPresence)(handoff, componentId);
|
|
210
307
|
// Generate previews for each variation
|
|
211
308
|
for (const previewKey in componentData.previews) {
|
|
212
309
|
const previewProps = componentData.previews[previewKey].values;
|
|
213
310
|
// Server-side render the component
|
|
214
311
|
const serverRenderedHtml = server_1.default.renderToString(react_1.default.createElement(ReactComponent, previewProps));
|
|
215
312
|
const formattedHtml = yield (0, html_1.formatHtml)(serverRenderedHtml);
|
|
216
|
-
|
|
217
|
-
const clientHydrationSource = generateClientHydrationSource(componentPath);
|
|
218
|
-
// Build client-side bundle
|
|
219
|
-
const clientBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_CLIENT_BUILD_CONFIG), { logLevel: 'silent', stdin: {
|
|
220
|
-
contents: clientHydrationSource,
|
|
221
|
-
resolveDir: process.cwd(),
|
|
222
|
-
loader: 'tsx',
|
|
223
|
-
}, plugins: [(0, build_1.createReactResolvePlugin)(handoff.workingPath, handoff.modulePath)] });
|
|
224
|
-
// Apply user's client build config hook if provided
|
|
225
|
-
const finalClientBuildConfig = ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.clientBuildConfig)
|
|
226
|
-
? handoff.config.hooks.clientBuildConfig(clientBuildConfig)
|
|
227
|
-
: clientBuildConfig;
|
|
228
|
-
let clientBundleJs;
|
|
229
|
-
try {
|
|
230
|
-
const bundledClient = yield esbuild_1.default.build(finalClientBuildConfig);
|
|
231
|
-
if (bundledClient.warnings.length > 0) {
|
|
232
|
-
const messages = yield esbuild_1.default.formatMessages(bundledClient.warnings, { kind: 'warning', color: true });
|
|
233
|
-
messages.forEach((msg) => logger_1.Logger.warn(msg));
|
|
234
|
-
}
|
|
235
|
-
clientBundleJs = bundledClient.outputFiles[0].text;
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
logger_1.Logger.error(`Failed to build client bundle for ${componentId}`);
|
|
239
|
-
if (error.errors) {
|
|
240
|
-
const messages = yield esbuild_1.default.formatMessages(error.errors, { kind: 'error', color: true });
|
|
241
|
-
messages.forEach((msg) => logger_1.Logger.error(msg));
|
|
242
|
-
}
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
// Generate complete HTML document
|
|
246
|
-
finalHtml = generateHtmlDocument(componentId, componentData.previews[previewKey].title, formattedHtml, clientBundleJs, previewProps);
|
|
313
|
+
finalHtml = generateHtmlDocument(componentId, componentData.previews[previewKey].title, formattedHtml, previewProps, sharedArtifacts, componentArtifacts);
|
|
247
314
|
// Emit preview files
|
|
248
315
|
this.emitFile({
|
|
249
316
|
type: 'asset',
|
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.readComponentMetadataApi = exports.readComponentApi = exports.writeComponentApi = exports.getAPIPath = exports.sanitizeComponentApiData = void 0;
|
|
16
16
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const url_1 = require("../../../artifacts/url");
|
|
18
19
|
const previews_1 = require("./previews");
|
|
19
20
|
const sanitizeComponentApiData = (component) => {
|
|
20
21
|
if (!component)
|
|
@@ -119,6 +120,7 @@ exports.readComponentApi = readComponentApi;
|
|
|
119
120
|
* @returns The component summary or null if not found
|
|
120
121
|
*/
|
|
121
122
|
const readComponentMetadataApi = (handoff, id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
var _a;
|
|
122
124
|
const componentData = yield (0, exports.readComponentApi)(handoff, id);
|
|
123
125
|
if (!componentData) {
|
|
124
126
|
return null;
|
|
@@ -137,7 +139,7 @@ const readComponentMetadataApi = (handoff, id) => __awaiter(void 0, void 0, void
|
|
|
137
139
|
tags: componentData.tags ? componentData.tags : [],
|
|
138
140
|
properties: componentData.properties,
|
|
139
141
|
previews: (0, previews_1.getDocumentedPreviews)(componentData.previews),
|
|
140
|
-
path:
|
|
142
|
+
path: (0, url_1.buildComponentDetailUrl)(id, (_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : ''),
|
|
141
143
|
};
|
|
142
144
|
});
|
|
143
145
|
exports.readComponentMetadataApi = readComponentMetadataApi;
|
|
@@ -21,6 +21,8 @@ const css_1 = require("./css");
|
|
|
21
21
|
const javascript_1 = require("./javascript");
|
|
22
22
|
const COMPONENT_JS_RESERVED_FILES = new Set([javascript_1.MAIN_COMPONENT_JS_FILE]);
|
|
23
23
|
const COMPONENT_CSS_RESERVED_FILES = new Set([css_1.MAIN_COMPONENT_CSS_FILE, css_1.SHARED_COMPONENT_CSS_FILE]);
|
|
24
|
+
/** Suffix of a component-owned client/hydration artifact (`<id>.client.js`), see ssr-render plugin. */
|
|
25
|
+
const CLIENT_ARTIFACT_SUFFIX = '.client.js';
|
|
24
26
|
const getComponentApiPath = (handoff) => path_1.default.resolve((0, api_1.getAPIPath)(handoff), 'component');
|
|
25
27
|
exports.getComponentApiPath = getComponentApiPath;
|
|
26
28
|
const getComponentPreviewKeys = (handoff, componentId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -81,7 +83,9 @@ const syncComponentArtifacts = (handoff) => __awaiter(void 0, void 0, void 0, fu
|
|
|
81
83
|
const parsed = path_1.default.parse(entry);
|
|
82
84
|
if (parsed.ext === '.json' || parsed.ext === '.js' || parsed.ext === '.css') {
|
|
83
85
|
if (!COMPONENT_JS_RESERVED_FILES.has(entry) && !COMPONENT_CSS_RESERVED_FILES.has(entry)) {
|
|
84
|
-
|
|
86
|
+
// `<id>.client.js` is owned by `<id>`, not by a phantom `<id>.client` component — strip the
|
|
87
|
+
// compound suffix so the owner id matches the real component (path.parse would yield `<id>.client`).
|
|
88
|
+
discoveredArtifactIds.add(entry.endsWith(CLIENT_ARTIFACT_SUFFIX) ? entry.slice(0, -CLIENT_ARTIFACT_SUFFIX.length) : parsed.name);
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
91
|
}
|
|
@@ -91,6 +95,9 @@ const syncComponentArtifacts = (handoff) => __awaiter(void 0, void 0, void 0, fu
|
|
|
91
95
|
if (yield fs_extra_1.default.pathExists(path_1.default.resolve(componentPath, `${componentId}.js`))) {
|
|
92
96
|
validFiles.add(`${componentId}.js`);
|
|
93
97
|
}
|
|
98
|
+
if (yield fs_extra_1.default.pathExists(path_1.default.resolve(componentPath, `${componentId}${CLIENT_ARTIFACT_SUFFIX}`))) {
|
|
99
|
+
validFiles.add(`${componentId}${CLIENT_ARTIFACT_SUFFIX}`);
|
|
100
|
+
}
|
|
94
101
|
if (yield fs_extra_1.default.pathExists(path_1.default.resolve(componentPath, `${componentId}.css`))) {
|
|
95
102
|
validFiles.add(`${componentId}.css`);
|
|
96
103
|
}
|
|
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
26
26
|
exports.ComponentSegment = void 0;
|
|
27
27
|
exports.processComponents = processComponents;
|
|
28
28
|
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
|
|
29
|
+
const url_1 = require("../../../artifacts/url");
|
|
29
30
|
const cache_1 = require("../../../cache");
|
|
30
31
|
const duration_1 = require("../../../utils/duration");
|
|
31
32
|
const logger_1 = require("../../../utils/logger");
|
|
@@ -149,11 +150,18 @@ const createComponentBuildPlan = (segmentToProcess, existingData) => {
|
|
|
149
150
|
*/
|
|
150
151
|
function processComponents(handoff, id, segmentToProcess, options) {
|
|
151
152
|
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
-
var _a, _b, _c, _d
|
|
153
|
+
var _a, _b, _c, _d;
|
|
153
154
|
const result = [];
|
|
154
155
|
const documentationObject = yield handoff.getDocumentationObject();
|
|
155
156
|
const components = (_a = documentationObject === null || documentationObject === void 0 ? void 0 : documentationObject.components) !== null && _a !== void 0 ? _a : {};
|
|
156
|
-
|
|
157
|
+
// Resolve the component set through the storage-agnostic store (v2). The filesystem store is a
|
|
158
|
+
// read view over `runtimeConfig.entries.components`, so these are the same records the build has
|
|
159
|
+
// always used — rebuilt into the id-keyed map the build logic below expects.
|
|
160
|
+
const runtimeComponents = {};
|
|
161
|
+
for (const component of yield handoff.store.components.list()) {
|
|
162
|
+
if (component)
|
|
163
|
+
runtimeComponents[component.id] = component;
|
|
164
|
+
}
|
|
157
165
|
const allComponentIds = Object.keys(runtimeComponents);
|
|
158
166
|
if (id && !runtimeComponents[id]) {
|
|
159
167
|
yield (0, artifacts_1.removeComponentApi)(handoff, id);
|
|
@@ -188,7 +196,7 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
188
196
|
for (const componentId of allComponentIds) {
|
|
189
197
|
const currentFileStates = yield (0, cache_1.computeComponentFileStates)(handoff, componentId);
|
|
190
198
|
componentFileStatesMap.set(componentId, currentFileStates);
|
|
191
|
-
const cachedEntry = (
|
|
199
|
+
const cachedEntry = (_b = cache === null || cache === void 0 ? void 0 : cache.components) === null || _b === void 0 ? void 0 : _b[componentId];
|
|
192
200
|
if (!cachedEntry) {
|
|
193
201
|
logger_1.Logger.info(`Component '${componentId}': new component, will build`);
|
|
194
202
|
componentsToBuild.add(componentId);
|
|
@@ -337,7 +345,7 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
337
345
|
* Run validation if explicitly requested and a hook is configured.
|
|
338
346
|
* This allows custom logic to assess the validity of the generated component data.
|
|
339
347
|
*/
|
|
340
|
-
if (buildPlan.validationMode && ((
|
|
348
|
+
if (buildPlan.validationMode && ((_d = (_c = handoff.config) === null || _c === void 0 ? void 0 : _c.hooks) === null || _d === void 0 ? void 0 : _d.validateComponent)) {
|
|
341
349
|
const validationResults = yield handoff.config.hooks.validateComponent(data);
|
|
342
350
|
data.validations = validationResults;
|
|
343
351
|
}
|
|
@@ -400,7 +408,7 @@ const buildComponentSummary = (id, data) => {
|
|
|
400
408
|
tags: data.tags ? data.tags : [],
|
|
401
409
|
properties: data.properties,
|
|
402
410
|
previews: (0, previews_1.getDocumentedPreviews)(data.previews),
|
|
403
|
-
path:
|
|
411
|
+
path: (0, url_1.buildComponentDetailUrl)(id, (_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : ''),
|
|
404
412
|
};
|
|
405
413
|
};
|
|
406
414
|
exports.default = processComponents;
|