handoff-app 1.2.2-6 → 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/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/ComponentLists.tsx +102 -102
- 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,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured artifact and build-metadata model for handoff-app v2.
|
|
3
|
+
*
|
|
4
|
+
* These types are the contract the rest of v2 reads from instead of parsing generated HTML.
|
|
5
|
+
* The workspace (filesystem) and registry (database) tracks both produce and consume this
|
|
6
|
+
* shape, so dependency validation, serving, publish, ingestion, and pattern composition all
|
|
7
|
+
* rely on structured data rather than HTML inspection.
|
|
8
|
+
*
|
|
9
|
+
* This module is a pure prefactor: types and constants only. No artifact is read or written
|
|
10
|
+
* here — consumers (the store, docs read API, build, publish/ingest) come in later issues.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Logical artifact kind. Mirrors the docs read-model artifact record's `artifactKind` — the
|
|
15
|
+
* broad class an artifact belongs to, independent of how it is stored or served.
|
|
16
|
+
*/
|
|
17
|
+
export type ArtifactKind = 'json' | 'html' | 'css' | 'javascript' | 'other';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* What an artifact (or reference) logically belongs to. Shared/global artifacts are owned by
|
|
21
|
+
* `asset` with a `null` owner id; entity-owned artifacts name their component or pattern.
|
|
22
|
+
*/
|
|
23
|
+
export type ArtifactOwnerKind = 'component' | 'pattern' | 'asset';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Kind of a structured artifact reference carried by an HTML artifact. Drives load order,
|
|
27
|
+
* validation, and pattern dedup without ever parsing the HTML.
|
|
28
|
+
*/
|
|
29
|
+
export type ArtifactReferenceKind = 'client' | 'style' | 'script' | 'shared' | 'other';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build status for an artifact/entity. A successful publish sets `current` with `builtAt`,
|
|
33
|
+
* `builderVersion`, and `artifactHash` all present; metadata-only records report `missing`.
|
|
34
|
+
*/
|
|
35
|
+
export type ArtifactBuildStatus = 'current' | 'stale' | 'missing' | 'error';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A structured reference from one artifact (typically preview/inspect HTML) to another
|
|
39
|
+
* artifact it depends on. References are the single source of truth for dependency
|
|
40
|
+
* resolution — HTML is never parsed for dependencies.
|
|
41
|
+
*/
|
|
42
|
+
export interface ArtifactReference {
|
|
43
|
+
/** Logical path of the referenced artifact (e.g. `component/main.css`). */
|
|
44
|
+
path: string;
|
|
45
|
+
/** Reference kind, used for load ordering and dedup. */
|
|
46
|
+
kind: ArtifactReferenceKind;
|
|
47
|
+
/** Whether the referencing artifact cannot render correctly without this reference. */
|
|
48
|
+
required: boolean;
|
|
49
|
+
/** What the referenced artifact belongs to, when known. */
|
|
50
|
+
ownerKind?: ArtifactOwnerKind;
|
|
51
|
+
/** Owner entity id; `null` for shared/global artifacts. */
|
|
52
|
+
ownerId?: string | null;
|
|
53
|
+
/** Optional content type of the referenced artifact. */
|
|
54
|
+
contentType?: string;
|
|
55
|
+
/** Optional format/version tag of the referenced artifact. */
|
|
56
|
+
formatVersion?: string;
|
|
57
|
+
/** Optional build identity the reference was resolved against. */
|
|
58
|
+
buildId?: string;
|
|
59
|
+
/** Optional content hash of the referenced artifact. */
|
|
60
|
+
hash?: string;
|
|
61
|
+
/** Optional byte size of the referenced artifact. */
|
|
62
|
+
size?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build metadata for an artifact or entity. Captures enough to validate, serve, and debug docs
|
|
67
|
+
* output.
|
|
68
|
+
*/
|
|
69
|
+
export interface ArtifactBuildMetadata {
|
|
70
|
+
/** Current build state for the artifact/entity. */
|
|
71
|
+
status: ArtifactBuildStatus;
|
|
72
|
+
/** ISO timestamp of the build that produced the current artifact, when built. */
|
|
73
|
+
builtAt?: string;
|
|
74
|
+
/** Version of the builder that produced the artifact. */
|
|
75
|
+
builderVersion?: string;
|
|
76
|
+
/** Content hash of the produced artifact. */
|
|
77
|
+
artifactHash?: string;
|
|
78
|
+
/** Optional hash of the source that produced the artifact, for staleness detection. */
|
|
79
|
+
sourceHash?: string;
|
|
80
|
+
/** Optional non-fatal build warnings. */
|
|
81
|
+
warnings?: string[];
|
|
82
|
+
/** Optional fatal build error message. */
|
|
83
|
+
error?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Size diagnostics for an artifact, used for publish/build reporting.
|
|
88
|
+
*/
|
|
89
|
+
export interface ArtifactSizeDiagnostics {
|
|
90
|
+
/** Raw byte size of the artifact content. */
|
|
91
|
+
bytes: number;
|
|
92
|
+
/** Optional gzipped byte size, when measured. */
|
|
93
|
+
gzipBytes?: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Structured description of a single docs read-model artifact. This is the shape the docs read
|
|
98
|
+
* API serves, the static build materializes, and registry publish/ingest moves over the wire —
|
|
99
|
+
* carrying enough to validate, serve, and debug an artifact.
|
|
100
|
+
*
|
|
101
|
+
* `content` and `storageRef` are intentionally both optional so the model leaves room for
|
|
102
|
+
* future object storage of large/binary assets rather than assuming inline content for every
|
|
103
|
+
* kind.
|
|
104
|
+
*/
|
|
105
|
+
export interface ArtifactDescriptor {
|
|
106
|
+
/** Logical artifact path (e.g. `component/badge-primary.html`). Stable key for serving. */
|
|
107
|
+
path: string;
|
|
108
|
+
/** Broad artifact kind. */
|
|
109
|
+
artifactKind: ArtifactKind;
|
|
110
|
+
/** Content type to serve the artifact with (e.g. `text/html; charset=utf-8`). */
|
|
111
|
+
contentType: string;
|
|
112
|
+
/** Inline artifact content, when stored inline. */
|
|
113
|
+
content?: string;
|
|
114
|
+
/** Reference to externally stored content (future object storage), when not inline. */
|
|
115
|
+
storageRef?: string;
|
|
116
|
+
/** What the artifact belongs to. */
|
|
117
|
+
ownerKind: ArtifactOwnerKind;
|
|
118
|
+
/** Owner entity id; `null` for shared/global artifacts. */
|
|
119
|
+
ownerId: string | null;
|
|
120
|
+
/** Structured references this artifact depends on (required and optional). */
|
|
121
|
+
references: ArtifactReference[];
|
|
122
|
+
/** Optional format/version tag (e.g. preview/inspect HTML format version). */
|
|
123
|
+
formatVersion?: string;
|
|
124
|
+
/** Build identity that produced this artifact. */
|
|
125
|
+
buildId?: string;
|
|
126
|
+
/** Content hash of the artifact. */
|
|
127
|
+
hash?: string;
|
|
128
|
+
/** Byte size of the artifact. */
|
|
129
|
+
size?: number;
|
|
130
|
+
/** Build metadata for the artifact. */
|
|
131
|
+
build?: ArtifactBuildMetadata;
|
|
132
|
+
/** Optional size diagnostics. */
|
|
133
|
+
diagnostics?: ArtifactSizeDiagnostics;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Logical path of the shared global stylesheet artifact. */
|
|
137
|
+
export const SHARED_MAIN_CSS_ARTIFACT_PATH = 'component/main.css';
|
|
138
|
+
/** Logical path of the shared global script artifact. */
|
|
139
|
+
export const SHARED_MAIN_JS_ARTIFACT_PATH = 'component/main.js';
|
|
140
|
+
/** Logical path of the shared component styles artifact. */
|
|
141
|
+
export const SHARED_STYLES_CSS_ARTIFACT_PATH = 'component/shared.css';
|
|
142
|
+
|
|
143
|
+
/** Logical path of the per-build artifact metadata manifest. */
|
|
144
|
+
export const ARTIFACT_METADATA_PATH = 'artifact-metadata.json';
|
|
145
|
+
/** Format version tag carried by generated React preview/inspect HTML. */
|
|
146
|
+
export const REACT_PREVIEW_FORMAT_VERSION = 'handoff-react-preview-v1';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized, basePath-aware canonical artifact URL builder.
|
|
3
|
+
*
|
|
4
|
+
* Every artifact reference emitted in generated HTML and docs read data flows through this one
|
|
5
|
+
* builder. URLs are always of the canonical form:
|
|
6
|
+
*
|
|
7
|
+
* {basePath}/api/docs/artifacts/{artifactPath}
|
|
8
|
+
*
|
|
9
|
+
* intended for use directly in native `href`/`src` attributes. By contract these URLs never carry
|
|
10
|
+
* a query string and never depend on artifact-specific data attributes — artifact references come
|
|
11
|
+
* from structured metadata, not the URL or the HTML.
|
|
12
|
+
*
|
|
13
|
+
* This module is pure and runtime-agnostic (no Node or browser globals) so the build and the
|
|
14
|
+
* Next app can both import it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Canonical route prefix all artifact URLs are served under. */
|
|
18
|
+
export const ARTIFACTS_ROUTE_SEGMENT = 'api/docs/artifacts';
|
|
19
|
+
|
|
20
|
+
/** Route prefix for docs read API metadata (`.json`) reads. */
|
|
21
|
+
export const DOCS_ROUTE_SEGMENT = 'api/docs';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Normalize a configured basePath to either an empty string or a single-leading-slash,
|
|
25
|
+
* no-trailing-slash form. Mirrors the Next app's `resolveBasePath` so builder output composes
|
|
26
|
+
* correctly with the configured Next `basePath`.
|
|
27
|
+
*/
|
|
28
|
+
export const normalizeBasePath = (basePath?: string | null): string => {
|
|
29
|
+
if (!basePath) {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
const trimmed = basePath.replace(/^\/+|\/+$/g, '');
|
|
33
|
+
return trimmed ? `/${trimmed}` : '';
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Split a logical artifact path into its non-empty segments. Leading/trailing/duplicate slashes
|
|
38
|
+
* are ignored; backslashes are treated as separators so Windows-style inputs normalize too.
|
|
39
|
+
*/
|
|
40
|
+
const toArtifactSegments = (artifactPath: string): string[] =>
|
|
41
|
+
artifactPath
|
|
42
|
+
.split(/[\\/]+/)
|
|
43
|
+
.filter((segment) => segment.length > 0);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether a logical path segment is a relative-traversal marker (`.` or `..`). These survive
|
|
47
|
+
* `encodeURIComponent` (dots are unreserved) and so must be rejected explicitly to keep emitted
|
|
48
|
+
* URLs canonical and contained — mirrors the server resolver's segment validation as defense in
|
|
49
|
+
* depth.
|
|
50
|
+
*/
|
|
51
|
+
const isTraversalSegment = (segment: string): boolean => segment === '.' || segment === '..';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Build a canonical artifact URL for a logical artifact path.
|
|
55
|
+
*
|
|
56
|
+
* Each path segment is individually `encodeURIComponent`-encoded (so a slash separating logical
|
|
57
|
+
* segments is preserved while reserved characters within a segment are escaped), and the
|
|
58
|
+
* configured `basePath` is preserved ahead of the canonical route.
|
|
59
|
+
*
|
|
60
|
+
* @param artifactPath Logical artifact path, e.g. `component/badge-primary.html`.
|
|
61
|
+
* @param basePath Optional configured base path; normalized like the Next app's basePath.
|
|
62
|
+
* @returns A canonical `{basePath}/api/docs/artifacts/{encodedPath}` URL.
|
|
63
|
+
* @throws If `artifactPath` has no usable segments, or contains a `.`/`..` traversal segment.
|
|
64
|
+
*/
|
|
65
|
+
export const buildArtifactUrl = (artifactPath: string, basePath?: string | null): string => {
|
|
66
|
+
const segments = toArtifactSegments(artifactPath);
|
|
67
|
+
if (segments.length === 0) {
|
|
68
|
+
throw new Error(`Cannot build artifact URL: empty artifact path "${artifactPath}".`);
|
|
69
|
+
}
|
|
70
|
+
if (segments.some(isTraversalSegment)) {
|
|
71
|
+
throw new Error(`Cannot build artifact URL: path traversal is not allowed in "${artifactPath}".`);
|
|
72
|
+
}
|
|
73
|
+
const encoded = segments.map((segment) => encodeURIComponent(segment)).join('/');
|
|
74
|
+
return `${normalizeBasePath(basePath)}/${ARTIFACTS_ROUTE_SEGMENT}/${encoded}`;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build the canonical docs read API URL for a component's detail metadata
|
|
79
|
+
* (`{basePath}/api/docs/components/{id}.json`). The id is URL-encoded; `basePath` is preserved.
|
|
80
|
+
* This is the single source of the metadata-read URL that the build records as an entity's `path`.
|
|
81
|
+
*/
|
|
82
|
+
export const buildComponentDetailUrl = (id: string, basePath?: string | null): string =>
|
|
83
|
+
`${normalizeBasePath(basePath)}/${DOCS_ROUTE_SEGMENT}/components/${encodeURIComponent(id)}.json`;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Build the canonical docs read API URL for a pattern's detail metadata
|
|
87
|
+
* (`{basePath}/api/docs/patterns/{id}.json`). The id is URL-encoded; `basePath` is preserved.
|
|
88
|
+
*/
|
|
89
|
+
export const buildPatternDetailUrl = (id: string, basePath?: string | null): string =>
|
|
90
|
+
`${normalizeBasePath(basePath)}/${DOCS_ROUTE_SEGMENT}/patterns/${encodeURIComponent(id)}.json`;
|
package/src/cache/build-cache.ts
CHANGED
|
@@ -226,6 +226,23 @@ export function getComponentFilePaths(handoff: Handoff, componentId: string): {
|
|
|
226
226
|
files.push(matchingConfigPath);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
// Include every pattern config file whose declaration references this component.
|
|
230
|
+
// When a pattern is added or modified, injectPatternPreviews injects new
|
|
231
|
+
// __pattern_* synthetic preview keys onto the referenced component, changing
|
|
232
|
+
// what processComponents must render. Without tracking these files here, the
|
|
233
|
+
// cache would incorrectly consider the component unchanged and skip the preview
|
|
234
|
+
// rebuild, leaving buildPatterns unable to find the required HTML fragments.
|
|
235
|
+
const runtimePatterns = handoff.runtimeConfig?.entries?.patterns ?? {};
|
|
236
|
+
for (const configPath of configPaths) {
|
|
237
|
+
const entry = handoff.getConfigFileEntry(configPath);
|
|
238
|
+
if (entry?.kind !== 'pattern') continue;
|
|
239
|
+
const pattern = runtimePatterns[entry.entityId];
|
|
240
|
+
if (!pattern) continue;
|
|
241
|
+
if (pattern.components?.some((ref) => ref.id === componentId)) {
|
|
242
|
+
files.push(configPath);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
229
246
|
return { files, templateDir };
|
|
230
247
|
}
|
|
231
248
|
|
|
@@ -302,8 +319,36 @@ export function hasComponentChanged(
|
|
|
302
319
|
* Checks if the component output files exist
|
|
303
320
|
*/
|
|
304
321
|
export async function checkOutputExists(handoff: Handoff, componentId: string): Promise<boolean> {
|
|
305
|
-
const
|
|
306
|
-
|
|
322
|
+
const outputDirectory = path.resolve(handoff.workingPath, 'public/api/component');
|
|
323
|
+
const componentPath = path.resolve(outputDirectory, `${componentId}.json`);
|
|
324
|
+
if (!(await fs.pathExists(componentPath))) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const runtimeComponent = handoff.runtimeConfig?.entries?.components?.[componentId];
|
|
329
|
+
const previewIds = new Set([
|
|
330
|
+
...Object.keys(runtimeComponent?.previews ?? {}),
|
|
331
|
+
...Object.keys(runtimeComponent?.internalPatternPreviews ?? {}),
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const component = await fs.readJson(componentPath);
|
|
336
|
+
for (const previewId of Object.keys(component?.previews ?? {})) {
|
|
337
|
+
previewIds.add(previewId);
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
for (const previewId of previewIds) {
|
|
344
|
+
const previewPath = path.resolve(outputDirectory, `${componentId}-${previewId}.html`);
|
|
345
|
+
const inspectPath = path.resolve(outputDirectory, `${componentId}-${previewId}-inspect.html`);
|
|
346
|
+
if (!(await fs.pathExists(previewPath)) || !(await fs.pathExists(inspectPath))) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return true;
|
|
307
352
|
}
|
|
308
353
|
|
|
309
354
|
/**
|
|
@@ -16,11 +16,10 @@ const command: CommandModule<{}, BuildAppArgs> = {
|
|
|
16
16
|
type: 'boolean',
|
|
17
17
|
default: false,
|
|
18
18
|
});
|
|
19
|
-
return getSharedOptions(yargs);
|
|
20
19
|
},
|
|
21
20
|
handler: async (args: BuildAppArgs) => {
|
|
22
21
|
const handoff = new Handoff(args.debug, args.force);
|
|
23
|
-
await handoff.build(args.skipComponents ?? false);
|
|
22
|
+
await handoff.build('static', args.skipComponents ?? false);
|
|
24
23
|
},
|
|
25
24
|
};
|
|
26
25
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { CommandModule } from 'yargs';
|
|
2
|
+
import Handoff from '../../';
|
|
3
|
+
import { DEFAULT_BUILD_TARGET, HandoffBuildError, type BuildPackage, type BuildTarget } from '../../app-builder';
|
|
4
|
+
import { Logger } from '../../utils/logger';
|
|
5
|
+
import { SharedArgs } from '../types';
|
|
6
|
+
import { getSharedOptions } from '../utils';
|
|
7
|
+
|
|
8
|
+
const BUILD_TARGETS: BuildTarget[] = ['static', 'registry'];
|
|
9
|
+
const BUILD_PACKAGES: BuildPackage[] = ['standalone', 'vercel'];
|
|
10
|
+
|
|
11
|
+
export interface BuildArgs extends SharedArgs {
|
|
12
|
+
target?: BuildTarget;
|
|
13
|
+
package?: BuildPackage;
|
|
14
|
+
skipComponents?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `handoff-app build [--target static|registry] [--package standalone|vercel]` — builds the
|
|
19
|
+
* documentation site for the resolved target and packages it for the resolved deliverable.
|
|
20
|
+
* `--target` selects *what* is built; the optional, additive `--package` selects *how* it is
|
|
21
|
+
* packaged. Bare `build` resolves to the static target with the `out/<projectId>` export;
|
|
22
|
+
* `--package` never implies a `--target`.
|
|
23
|
+
*/
|
|
24
|
+
const command: CommandModule<{}, BuildArgs> = {
|
|
25
|
+
command: 'build',
|
|
26
|
+
describe: 'Build the design system for the given target (default: static export)',
|
|
27
|
+
builder: (yargs) => {
|
|
28
|
+
return getSharedOptions(yargs)
|
|
29
|
+
.option('target', {
|
|
30
|
+
describe: 'Build target (what is built)',
|
|
31
|
+
choices: BUILD_TARGETS,
|
|
32
|
+
default: DEFAULT_BUILD_TARGET,
|
|
33
|
+
})
|
|
34
|
+
.option('package', {
|
|
35
|
+
describe: 'Packaging format (how it is delivered). Omit for the default per-target deliverable.',
|
|
36
|
+
choices: BUILD_PACKAGES,
|
|
37
|
+
})
|
|
38
|
+
.option('skip-components', {
|
|
39
|
+
describe: 'Skip building components before building the app',
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
default: false,
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
handler: async (args: BuildArgs) => {
|
|
45
|
+
const handoff = new Handoff(args.debug, args.force);
|
|
46
|
+
try {
|
|
47
|
+
await handoff.build(args.target ?? DEFAULT_BUILD_TARGET, args.skipComponents ?? false, args.package);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// Expected business/flow failures (invalid combinations, unsupported runtime modes) print as a
|
|
50
|
+
// single actionable line; unexpected errors re-throw so their stack trace is preserved.
|
|
51
|
+
if (error instanceof HandoffBuildError) {
|
|
52
|
+
Logger.error(error.message);
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default command;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CommandModule } from 'yargs';
|
|
2
|
+
import Handoff from '../../';
|
|
3
|
+
import { Logger } from '../../utils/logger';
|
|
4
|
+
import { SharedArgs } from '../types';
|
|
5
|
+
import { getSharedOptions } from '../utils';
|
|
6
|
+
|
|
7
|
+
/** Entity kinds checkout-able from a connected workspace. */
|
|
8
|
+
const ENTITY_KINDS = ['component', 'pattern', 'page', 'tokens', 'assets'] as const;
|
|
9
|
+
|
|
10
|
+
export interface CheckoutArgs extends SharedArgs {
|
|
11
|
+
type: (typeof ENTITY_KINDS)[number];
|
|
12
|
+
id?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `handoff-app checkout <component|pattern|page> <id>` — pull an entity from the connected registry
|
|
17
|
+
* into this workspace: writes its source files in standard authoring form and synthesizes a local
|
|
18
|
+
* declaration.
|
|
19
|
+
*
|
|
20
|
+
* `handoff-app checkout tokens [setId]` — pull every published token set (or only the named set) into
|
|
21
|
+
* this workspace: reconstruct `tokens.json` and restore the generated token files.
|
|
22
|
+
*
|
|
23
|
+
* `handoff-app checkout assets [collection]` pulls every published asset collection (or only the
|
|
24
|
+
* named one) into this workspace, recreating the standard asset files, sprite/manifest, and archives.
|
|
25
|
+
* Available only from a connected workspace (`runtime.mode: workspace` + a configured
|
|
26
|
+
* `registryConnection`). Overwriting existing local files requires `--force` or an interactive
|
|
27
|
+
* confirmation.
|
|
28
|
+
*/
|
|
29
|
+
const command: CommandModule<{}, CheckoutArgs> = {
|
|
30
|
+
command: 'checkout <type> [id]',
|
|
31
|
+
describe: 'Pull a component, pattern, page, design tokens, or assets from the connected registry into this workspace',
|
|
32
|
+
builder: (yargs) => {
|
|
33
|
+
return getSharedOptions(yargs)
|
|
34
|
+
.positional('type', {
|
|
35
|
+
describe: 'The kind of entity to checkout',
|
|
36
|
+
choices: ENTITY_KINDS,
|
|
37
|
+
type: 'string',
|
|
38
|
+
})
|
|
39
|
+
.positional('id', {
|
|
40
|
+
describe: 'The stable id of the entity (component/pattern/page id, token set id, or asset collection); optional for tokens/assets',
|
|
41
|
+
type: 'string',
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
handler: async (args: CheckoutArgs) => {
|
|
45
|
+
const handoff = new Handoff(args.debug, args.force);
|
|
46
|
+
try {
|
|
47
|
+
if (args.type === 'tokens') {
|
|
48
|
+
await handoff.checkoutTokens(args.id);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (args.type === 'assets') {
|
|
52
|
+
await handoff.checkoutAssets(args.id);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!args.id) {
|
|
56
|
+
throw new Error(`An id is required to checkout a ${args.type} (e.g. "handoff-app checkout ${args.type} <id>").`);
|
|
57
|
+
}
|
|
58
|
+
await handoff.checkout(args.type, args.id);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
Logger.error(error instanceof Error ? error.message : String(error));
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default command;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CommandModule } from 'yargs';
|
|
2
|
+
import Handoff from '../../';
|
|
3
|
+
import { SharedArgs } from '../types';
|
|
4
|
+
import { getSharedOptions } from '../utils';
|
|
5
|
+
|
|
6
|
+
export interface DbMigrateArgs extends SharedArgs {}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `handoff-app db:migrate` — runs the package-owned registry database migrations against the
|
|
10
|
+
* configured PostgreSQL/Neon database. Reads the consuming project's config + DB env vars and runs
|
|
11
|
+
* independently of `build`.
|
|
12
|
+
*/
|
|
13
|
+
const command: CommandModule<{}, DbMigrateArgs> = {
|
|
14
|
+
command: 'db:migrate',
|
|
15
|
+
describe: 'Run registry database migrations (Drizzle / PostgreSQL)',
|
|
16
|
+
builder: (yargs) => {
|
|
17
|
+
return getSharedOptions(yargs);
|
|
18
|
+
},
|
|
19
|
+
handler: async (args: DbMigrateArgs) => {
|
|
20
|
+
const handoff = new Handoff(args.debug, args.force);
|
|
21
|
+
await handoff.dbMigrate();
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default command;
|
package/src/commands/index.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import buildCommand from './build';
|
|
1
2
|
import buildAppCommand from './build/app';
|
|
2
3
|
import buildComponentsCommand from './build/components';
|
|
4
|
+
import checkoutCommand from './checkout';
|
|
5
|
+
import dbMigrateCommand from './db/migrate';
|
|
3
6
|
import devCommand from './dev';
|
|
4
7
|
import ejectConfigCommand from './eject/config';
|
|
5
8
|
import ejectPagesCommand from './eject/pages';
|
|
@@ -9,13 +12,17 @@ import initCommand from './init';
|
|
|
9
12
|
import makeComponentCommand from './make/component';
|
|
10
13
|
import makePageCommand from './make/page';
|
|
11
14
|
import makeTemplateCommand from './make/template';
|
|
15
|
+
import publishCommand from './publish';
|
|
12
16
|
import scaffoldCommand from './scaffold';
|
|
13
17
|
import startCommand from './start';
|
|
14
18
|
import validateComponentsCommand from './validate/components';
|
|
15
19
|
|
|
16
20
|
export const commands = [
|
|
21
|
+
buildCommand,
|
|
17
22
|
buildAppCommand,
|
|
18
23
|
buildComponentsCommand,
|
|
24
|
+
checkoutCommand,
|
|
25
|
+
dbMigrateCommand,
|
|
19
26
|
devCommand,
|
|
20
27
|
ejectConfigCommand,
|
|
21
28
|
ejectPagesCommand,
|
|
@@ -25,6 +32,7 @@ export const commands = [
|
|
|
25
32
|
makePageCommand,
|
|
26
33
|
makeComponentCommand,
|
|
27
34
|
makeTemplateCommand,
|
|
35
|
+
publishCommand,
|
|
28
36
|
scaffoldCommand,
|
|
29
37
|
startCommand,
|
|
30
38
|
validateComponentsCommand,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { CommandModule } from 'yargs';
|
|
2
|
+
import Handoff from '../../';
|
|
3
|
+
import { Logger } from '../../utils/logger';
|
|
4
|
+
import { SharedArgs } from '../types';
|
|
5
|
+
import { getSharedOptions } from '../utils';
|
|
6
|
+
|
|
7
|
+
/** Entity kinds publishable from a connected workspace. */
|
|
8
|
+
const ENTITY_KINDS = ['component', 'pattern', 'page', 'tokens', 'assets'] as const;
|
|
9
|
+
|
|
10
|
+
export interface PublishArgs extends SharedArgs {
|
|
11
|
+
type: (typeof ENTITY_KINDS)[number];
|
|
12
|
+
id?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `handoff-app publish <component|pattern|page> <id>` — fresh targeted local build + upload of the
|
|
17
|
+
* selected entity to the connected registry.
|
|
18
|
+
*
|
|
19
|
+
* `handoff-app publish tokens [setId]` — fresh token build + upload of every logical token set, or
|
|
20
|
+
* only the named set (`foundation/colors`, `component/button`).
|
|
21
|
+
*
|
|
22
|
+
* `handoff-app publish assets [collection]` runs a fresh build and uploads every asset collection, or
|
|
23
|
+
* only the named one (`icons`, `logos`, `fonts`). Available only from a connected workspace
|
|
24
|
+
* (`runtime.mode: workspace` + a configured `registryConnection`).
|
|
25
|
+
*/
|
|
26
|
+
const command: CommandModule<{}, PublishArgs> = {
|
|
27
|
+
command: 'publish <type> [id]',
|
|
28
|
+
describe: 'Build and publish a component, pattern, page, design tokens, or assets to the connected registry',
|
|
29
|
+
builder: (yargs) => {
|
|
30
|
+
return getSharedOptions(yargs)
|
|
31
|
+
.positional('type', {
|
|
32
|
+
describe: 'The kind of entity to publish',
|
|
33
|
+
choices: ENTITY_KINDS,
|
|
34
|
+
type: 'string',
|
|
35
|
+
})
|
|
36
|
+
.positional('id', {
|
|
37
|
+
describe: 'The stable id of the entity (component/pattern/page id, token set id, or asset collection); optional for tokens/assets',
|
|
38
|
+
type: 'string',
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
handler: async (args: PublishArgs) => {
|
|
42
|
+
const handoff = new Handoff(args.debug, args.force);
|
|
43
|
+
try {
|
|
44
|
+
if (args.type === 'tokens') {
|
|
45
|
+
await handoff.publishTokens(args.id);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (args.type === 'assets') {
|
|
49
|
+
await handoff.publishAssets(args.id);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!args.id) {
|
|
53
|
+
throw new Error(`An id is required to publish a ${args.type} (e.g. "handoff-app publish ${args.type} <id>").`);
|
|
54
|
+
}
|
|
55
|
+
await handoff.publish(args.type, args.id);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
Logger.error(error instanceof Error ? error.message : String(error));
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default command;
|
package/src/config/defaults.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveRegistryConnection } from '../registry/connection';
|
|
1
2
|
import { ClientConfig, Config } from '../types/config';
|
|
2
3
|
|
|
3
4
|
export interface ImageStyle {
|
|
@@ -11,6 +12,9 @@ export interface ImageStyle {
|
|
|
11
12
|
export const defaultConfig = (): Config => ({
|
|
12
13
|
dev_access_token: process.env.HANDOFF_DEV_ACCESS_TOKEN ?? null,
|
|
13
14
|
figma_project_id: process.env.HANDOFF_FIGMA_PROJECT_ID ?? null,
|
|
15
|
+
runtime: {
|
|
16
|
+
mode: 'workspace',
|
|
17
|
+
},
|
|
14
18
|
exportsOutputDirectory: process.env.HANDOFF_OUTPUT_DIR ?? 'exported',
|
|
15
19
|
sitesOutputDirectory: process.env.HANDOFF_SITES_DIR ?? 'out',
|
|
16
20
|
useVariables: process.env.HANDOFF_USE_VARIABLES === 'true',
|
|
@@ -66,13 +70,24 @@ export const getClientConfig = (config: Config): ClientConfig => {
|
|
|
66
70
|
sitesOutputDirectory,
|
|
67
71
|
assets_zip_links = { icons: null, logos: null },
|
|
68
72
|
useVariables,
|
|
73
|
+
runtime,
|
|
69
74
|
} = { ...defaultConfig(), ...config };
|
|
70
75
|
|
|
76
|
+
const mode = runtime?.mode ?? 'workspace';
|
|
77
|
+
// Connected-workspace affordance: only a workspace with a resolvable registry URL is "connected".
|
|
78
|
+
// URL-only threshold (no token required) so the publish hint surfaces before the access token is
|
|
79
|
+
// set. Never project the URL/token value itself — only this boolean crosses to the browser.
|
|
80
|
+
const connected = mode === 'workspace' && Boolean(resolveRegistryConnection(config).url);
|
|
81
|
+
|
|
71
82
|
return {
|
|
72
83
|
app,
|
|
73
84
|
exportsOutputDirectory,
|
|
74
85
|
sitesOutputDirectory,
|
|
75
86
|
assets_zip_links,
|
|
76
87
|
useVariables,
|
|
88
|
+
runtime: {
|
|
89
|
+
mode,
|
|
90
|
+
connected,
|
|
91
|
+
},
|
|
77
92
|
};
|
|
78
93
|
};
|
package/src/config/loader.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import esbuild from 'esbuild';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
+
import merge from 'lodash/merge';
|
|
3
4
|
import { createRequire } from 'module';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
import { Config } from '../types/config';
|
|
@@ -109,7 +110,15 @@ export const initConfigWithMetadata = (configOverride?: Partial<Config>): Config
|
|
|
109
110
|
});
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
const
|
|
113
|
+
const defaults = defaultConfig();
|
|
114
|
+
// Top-level merge stays shallow to preserve 1.x semantics, except for the `runtime`
|
|
115
|
+
// block which is deep-merged so a partial user `runtime` (e.g. only `mode`) does not
|
|
116
|
+
// wipe the defaults supplied for the rest of the block.
|
|
117
|
+
const resolvedConfig = {
|
|
118
|
+
...defaults,
|
|
119
|
+
...config,
|
|
120
|
+
runtime: merge({}, defaults.runtime, config.runtime),
|
|
121
|
+
} as Config;
|
|
113
122
|
return { config: resolvedConfig, configPath };
|
|
114
123
|
};
|
|
115
124
|
|
|
@@ -172,5 +172,8 @@ export const normalizeComponentDeclaration = (raw: RawDeclaration, options: Norm
|
|
|
172
172
|
renderer: resolvedRenderer,
|
|
173
173
|
entries,
|
|
174
174
|
previews: normalizedPreviews,
|
|
175
|
+
// Source location of the entity (declaration directory), retained so the store can expose a
|
|
176
|
+
// stable source reference for checkout/publish.
|
|
177
|
+
path: path.dirname(options.declarationPath),
|
|
175
178
|
};
|
|
176
179
|
};
|