handoff-app 1.2.2-7 → 2.0.0-1
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/paths.js +25 -1
- package/dist/app-builder/static-export.d.ts +17 -0
- package/dist/app-builder/static-export.js +206 -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 +16 -0
- package/dist/artifacts/types.d.ts +136 -0
- package/dist/artifacts/types.js +24 -0
- package/dist/artifacts/url.d.ts +67 -0
- package/dist/artifacts/url.js +116 -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 +124 -31
- 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 +11 -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 +6 -3
- 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 +28 -16
- package/src/app/pages/foundations/icons/index.tsx +18 -12
- package/src/app/pages/foundations/index.tsx +1 -1
- package/src/app/pages/foundations/logo.tsx +12 -8
- 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/paths.ts +27 -1
- package/src/app-builder/static-export.ts +242 -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 +24 -0
- package/src/artifacts/types.ts +146 -0
- package/src/artifacts/url.ts +122 -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 +129 -31
- 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
|
@@ -4,15 +4,21 @@ import { ComponentDocumentationOptions, PreviewObject } from '@handoff/types/pre
|
|
|
4
4
|
import * as fs from 'fs-extra';
|
|
5
5
|
import matter from 'gray-matter';
|
|
6
6
|
import { Types as CoreTypes } from 'handoff-core';
|
|
7
|
-
import { groupBy, startCase, uniq } from 'lodash';
|
|
8
7
|
import path from 'path';
|
|
9
8
|
import { ParsedUrlQuery } from 'querystring';
|
|
9
|
+
import { KNOWN_PATHS } from '@handoff/utils/menu-shell';
|
|
10
|
+
import { getRegistryNavData, getWorkspaceNavData, type NavData, type NavTokenSet, type SectionLink } from '@handoff/nav';
|
|
11
|
+
import { resolveDocsBackend } from '../../lib/docs-api/backend';
|
|
12
|
+
import { tokenFormatStrings } from '../../lib/docs-api/token-detail';
|
|
13
|
+
// Build-time-baked navigation shell. Imported statically (same as `pages/api/docs/nav.json.ts`) so
|
|
14
|
+
// it is bundled into the route chunk and readable at request time in the Vercel registry lambda,
|
|
15
|
+
// where the markdown it is derived from is not traceable. See src/utils/menu-shell.ts.
|
|
16
|
+
import navShell from '@/generated/nav-shell.json';
|
|
10
17
|
// Get the parsed url string type
|
|
11
18
|
export interface IParams extends ParsedUrlQuery {
|
|
12
19
|
slug: string | string[];
|
|
13
20
|
}
|
|
14
21
|
|
|
15
|
-
|
|
16
22
|
// Type for the metadata from frontmatter
|
|
17
23
|
export interface Metadata {
|
|
18
24
|
title: string;
|
|
@@ -21,23 +27,7 @@ export interface Metadata {
|
|
|
21
27
|
metaDescription: string;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
export interface SectionLink {
|
|
26
|
-
title: string;
|
|
27
|
-
weight: number;
|
|
28
|
-
external?: string | boolean;
|
|
29
|
-
path: string;
|
|
30
|
-
subSections: {
|
|
31
|
-
title: string;
|
|
32
|
-
path: string;
|
|
33
|
-
image: string;
|
|
34
|
-
menu?: {
|
|
35
|
-
title: string;
|
|
36
|
-
path: string;
|
|
37
|
-
image: string;
|
|
38
|
-
}[];
|
|
39
|
-
}[];
|
|
40
|
-
}
|
|
30
|
+
export type { SectionLink } from '@handoff/nav';
|
|
41
31
|
// Documentation Page Properties
|
|
42
32
|
export interface DocumentationProps {
|
|
43
33
|
metadata: Metadata;
|
|
@@ -45,6 +35,8 @@ export interface DocumentationProps {
|
|
|
45
35
|
options?: ComponentDocumentationOptions;
|
|
46
36
|
menu: SectionLink[];
|
|
47
37
|
current: SectionLink;
|
|
38
|
+
navData: NavData;
|
|
39
|
+
currentSectionId: string;
|
|
48
40
|
config: ClientConfig;
|
|
49
41
|
}
|
|
50
42
|
|
|
@@ -76,30 +68,10 @@ export interface FoundationDocumentationProps extends DocumentationWithTokensPro
|
|
|
76
68
|
design: CoreTypes.IDocumentationObject['localStyles'];
|
|
77
69
|
}
|
|
78
70
|
/**
|
|
79
|
-
*
|
|
71
|
+
* Default paths that have dedicated route files (excluded from auto-scanned submenus). Single source
|
|
72
|
+
* of truth lives in `@handoff/utils/menu-shell` (shared with the build-time shell builder).
|
|
80
73
|
*/
|
|
81
|
-
export const knownPaths =
|
|
82
|
-
'assets',
|
|
83
|
-
'assets/fonts',
|
|
84
|
-
'assets/icons',
|
|
85
|
-
'assets/logos',
|
|
86
|
-
'foundations',
|
|
87
|
-
'foundations/colors',
|
|
88
|
-
'foundations/icons',
|
|
89
|
-
'foundations/effects',
|
|
90
|
-
'foundations/logos',
|
|
91
|
-
'foundations/logo',
|
|
92
|
-
'foundations/typography',
|
|
93
|
-
'system',
|
|
94
|
-
'system/component',
|
|
95
|
-
'system/tokens',
|
|
96
|
-
'system/tokens/foundations',
|
|
97
|
-
'system/tokens/foundations/colors',
|
|
98
|
-
'system/tokens/foundations/effects',
|
|
99
|
-
'system/tokens/foundations/typography',
|
|
100
|
-
'system/tokens/components',
|
|
101
|
-
'system/pattern',
|
|
102
|
-
];
|
|
74
|
+
export const knownPaths = KNOWN_PATHS;
|
|
103
75
|
|
|
104
76
|
/**
|
|
105
77
|
* Get the plural name of a component
|
|
@@ -146,12 +118,12 @@ const collectMarkdownPaths = (rootDir: string, relativeParts: string[] = []): st
|
|
|
146
118
|
* Build catch-all static paths for all markdown pages at any depth.
|
|
147
119
|
* Excludes paths in knownPaths (those have dedicated route files).
|
|
148
120
|
*/
|
|
149
|
-
export const buildCatchAllStaticPaths = () => {
|
|
121
|
+
export const buildCatchAllStaticPaths = (includeWorkspacePages = true) => {
|
|
150
122
|
const docRoot = path.resolve(process.env.HANDOFF_MODULE_PATH ?? '', 'config/docs');
|
|
151
123
|
const pageRoot = path.resolve(process.env.HANDOFF_WORKING_PATH ?? '', 'pages');
|
|
152
124
|
|
|
153
125
|
const docPaths = collectMarkdownPaths(docRoot);
|
|
154
|
-
const pagePaths = collectMarkdownPaths(pageRoot);
|
|
126
|
+
const pagePaths = includeWorkspacePages ? collectMarkdownPaths(pageRoot) : [];
|
|
155
127
|
|
|
156
128
|
const seen = new Set<string>();
|
|
157
129
|
const allPaths: string[][] = [];
|
|
@@ -165,323 +137,24 @@ export const buildCatchAllStaticPaths = () => {
|
|
|
165
137
|
}
|
|
166
138
|
}
|
|
167
139
|
|
|
168
|
-
return allPaths
|
|
169
|
-
.filter((segments) => knownPaths.indexOf(segments.join('/')) < 0)
|
|
170
|
-
.map((segments) => ({ params: { slug: segments } }));
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Recursively build menu entries from .md files in a directory.
|
|
175
|
-
* Returns sub-section items with nested menu items for subdirectories.
|
|
176
|
-
*/
|
|
177
|
-
const buildMenuFromDirectory = (dirPath: string, urlPrefix: string): any[] => {
|
|
178
|
-
if (!fs.existsSync(dirPath)) return [];
|
|
179
|
-
const entries = fs.readdirSync(dirPath);
|
|
180
|
-
const items: any[] = [];
|
|
181
|
-
|
|
182
|
-
for (const entry of entries) {
|
|
183
|
-
const fullPath = path.join(dirPath, entry);
|
|
184
|
-
if (fs.lstatSync(fullPath).isDirectory()) {
|
|
185
|
-
const nestedItems = buildMenuFromDirectory(fullPath, `${urlPrefix}/${entry}`);
|
|
186
|
-
if (nestedItems.length > 0) {
|
|
187
|
-
items.push({
|
|
188
|
-
title: startCase(entry),
|
|
189
|
-
path: `${urlPrefix}/${entry}`,
|
|
190
|
-
menu: nestedItems,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
} else if (entry.endsWith('.md') && entry !== 'index.md') {
|
|
194
|
-
const slug = entry.replace('.md', '');
|
|
195
|
-
const fullSlugPath = `${urlPrefix}/${slug}`.replace(/^\/+/, '');
|
|
196
|
-
if (knownPaths.indexOf(fullSlugPath) >= 0) continue;
|
|
197
|
-
|
|
198
|
-
const contents = fs.readFileSync(fullPath, 'utf-8');
|
|
199
|
-
const { data: metadata } = matter(contents);
|
|
200
|
-
if (metadata.enabled === false) continue;
|
|
201
|
-
|
|
202
|
-
items.push({
|
|
203
|
-
title: metadata.menuTitle ?? metadata.title ?? startCase(slug),
|
|
204
|
-
path: `/${fullSlugPath}`,
|
|
205
|
-
weight: metadata.weight ?? 0,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return items.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0));
|
|
140
|
+
return allPaths.filter((segments) => knownPaths.indexOf(segments.join('/')) < 0).map((segments) => ({ params: { slug: segments } }));
|
|
211
141
|
};
|
|
212
142
|
|
|
213
143
|
/**
|
|
214
|
-
*
|
|
215
|
-
*
|
|
144
|
+
* Fetch patterns from the mode-aware docs read API.
|
|
145
|
+
*
|
|
146
|
+
* Resolves through {@link resolveDocsBackend}, so the active `runtime.mode` decides the source:
|
|
147
|
+
* generated workspace artifacts in workspace mode, the registry database in registry mode.
|
|
216
148
|
*/
|
|
217
|
-
export const
|
|
218
|
-
const docRoot = path.join(process.env.HANDOFF_MODULE_PATH ?? '', 'config/docs');
|
|
219
|
-
if (!fs.existsSync(docRoot)) {
|
|
220
|
-
return [];
|
|
221
|
-
}
|
|
222
|
-
const files = fs.readdirSync(docRoot);
|
|
223
|
-
let list = files;
|
|
224
|
-
const workingPages = path.resolve(process.env.HANDOFF_WORKING_PATH ?? '', 'pages');
|
|
225
|
-
let pages: string[] = [];
|
|
226
|
-
if (fs.existsSync(workingPages)) {
|
|
227
|
-
pages = fs.readdirSync(workingPages);
|
|
228
|
-
list = list.concat(pages);
|
|
229
|
-
}
|
|
230
|
-
const sections: SectionLink[] = [];
|
|
231
|
-
const custom = uniq(list)
|
|
232
|
-
.map((fileName: string) => {
|
|
233
|
-
let search = '';
|
|
234
|
-
if (pages.includes(fileName)) {
|
|
235
|
-
search = path.resolve(workingPages, fileName);
|
|
236
|
-
} else {
|
|
237
|
-
search = path.resolve(docRoot, fileName);
|
|
238
|
-
}
|
|
239
|
-
if (
|
|
240
|
-
!fs.lstatSync(search).isDirectory() &&
|
|
241
|
-
search !== path.resolve(docRoot, 'index.md') &&
|
|
242
|
-
search !== path.resolve(workingPages, 'index.md') &&
|
|
243
|
-
fileName.endsWith('md')
|
|
244
|
-
) {
|
|
245
|
-
const contents = fs.readFileSync(search, 'utf-8');
|
|
246
|
-
const { data: metadata } = matter(contents);
|
|
247
|
-
if (metadata.enabled === false) {
|
|
248
|
-
return undefined;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const filepath = `/${fileName.replace('.md', '')}`;
|
|
252
|
-
let subSections = [];
|
|
253
|
-
|
|
254
|
-
if (metadata.menu) {
|
|
255
|
-
subSections = Object.keys(metadata.menu)
|
|
256
|
-
.map((key) => {
|
|
257
|
-
const sub = metadata.menu[key];
|
|
258
|
-
if (sub.components) {
|
|
259
|
-
return {
|
|
260
|
-
title: sub.title,
|
|
261
|
-
menu: staticBuildComponentMenu(sub.components),
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
if (sub.tokens) {
|
|
265
|
-
return {
|
|
266
|
-
title: 'Tokens',
|
|
267
|
-
menu: staticBuildTokensMenu(),
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
if (sub.patterns) {
|
|
271
|
-
const patternMenu = staticBuildPatternMenu();
|
|
272
|
-
if (patternMenu.length > 0) {
|
|
273
|
-
return {
|
|
274
|
-
title: sub.title || 'Patterns',
|
|
275
|
-
menu: patternMenu,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
280
|
-
if (sub.enabled !== false) {
|
|
281
|
-
return sub;
|
|
282
|
-
}
|
|
283
|
-
})
|
|
284
|
-
.filter(filterOutUndefined);
|
|
285
|
-
} else {
|
|
286
|
-
// Only auto-scan directories when no frontmatter menu is defined
|
|
287
|
-
const dirName = fileName.replace('.md', '');
|
|
288
|
-
const docDir = path.resolve(docRoot, dirName);
|
|
289
|
-
const pagesDir = path.resolve(workingPages, dirName);
|
|
290
|
-
const nestedFromDocs = buildMenuFromDirectory(docDir, `/${dirName}`);
|
|
291
|
-
const nestedFromPages = buildMenuFromDirectory(pagesDir, `/${dirName}`);
|
|
292
|
-
|
|
293
|
-
const seenPaths = new Set<string>();
|
|
294
|
-
for (const item of [...nestedFromPages, ...nestedFromDocs]) {
|
|
295
|
-
if (item.path && !seenPaths.has(item.path)) {
|
|
296
|
-
seenPaths.add(item.path);
|
|
297
|
-
subSections.push(item);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
let external: string | boolean = false;
|
|
303
|
-
if (
|
|
304
|
-
typeof metadata.external === 'string' &&
|
|
305
|
-
(metadata.external.startsWith('http://') || metadata.external.startsWith('https://') || metadata.external.startsWith('/'))
|
|
306
|
-
) {
|
|
307
|
-
external = metadata.external;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return {
|
|
311
|
-
title: metadata.menuTitle ?? metadata.title,
|
|
312
|
-
external,
|
|
313
|
-
weight: metadata.weight ?? 0,
|
|
314
|
-
path: `${filepath}`,
|
|
315
|
-
subSections,
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
})
|
|
319
|
-
.filter(filterOutUndefined);
|
|
320
|
-
return sections.concat(custom).sort((a: SectionLink, b: SectionLink) => a.weight - b.weight);
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const buildBasePath = () => {
|
|
324
|
-
if (!process.env.HANDOFF_APP_BASE_PATH) {
|
|
325
|
-
return '';
|
|
326
|
-
}
|
|
327
|
-
return (process.env.HANDOFF_APP_BASE_PATH ?? '').replace(/^\/+|\/+$/g, '') + '/';
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const staticBuildComponentMenu = (type?: boolean | string) => {
|
|
331
|
-
const basePath = buildBasePath();
|
|
332
|
-
let menu = [];
|
|
333
|
-
let components = fetchComponents({ includeTokens: false });
|
|
334
|
-
if (typeof type === 'string' && type !== '') {
|
|
335
|
-
components = components.filter((component) => component.type == type);
|
|
336
|
-
}
|
|
337
|
-
// Build the submenu of exportables (components)
|
|
338
|
-
const groupedComponents = groupBy(components, (e) => e.group ?? '');
|
|
339
|
-
Object.keys(groupedComponents).forEach((group) => {
|
|
340
|
-
const menuGroup = { title: group || 'Uncategorized', menu: [] };
|
|
341
|
-
groupedComponents[group].forEach((component) => {
|
|
342
|
-
const docs = fetchDocPageMetadataAndContent('docs/system/', component.id);
|
|
343
|
-
let title = startCase(component.id);
|
|
344
|
-
if (docs.metadata.title) {
|
|
345
|
-
title = docs.metadata.title;
|
|
346
|
-
}
|
|
347
|
-
if (component.name) {
|
|
348
|
-
title = component.name;
|
|
349
|
-
}
|
|
350
|
-
menuGroup.menu.push({ path: `${basePath}system/component/${component.id}`, title });
|
|
351
|
-
});
|
|
352
|
-
// sort the menu group by name alphabetical
|
|
353
|
-
menuGroup.menu = menuGroup.menu.sort((a, b) => a.title.localeCompare(b.title));
|
|
354
|
-
menu.push(menuGroup);
|
|
355
|
-
});
|
|
356
|
-
// sort the menu by name alphabetical
|
|
357
|
-
menu = menu.sort((a, b) => a.title.localeCompare(b.title));
|
|
358
|
-
return menu;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const staticBuildTokensMenu = () => {
|
|
362
|
-
const basePath = buildBasePath();
|
|
363
|
-
|
|
364
|
-
const menu = [
|
|
365
|
-
{
|
|
366
|
-
title: `Foundations`,
|
|
367
|
-
path: `${basePath}system/tokens/foundations`,
|
|
368
|
-
menu: [
|
|
369
|
-
{
|
|
370
|
-
title: `Colors`,
|
|
371
|
-
path: `${basePath}system/tokens/foundations/colors`,
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
title: `Effects`,
|
|
375
|
-
path: `${basePath}system/tokens/foundations/effects`,
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
title: `Typography`,
|
|
379
|
-
path: `${basePath}system/tokens/foundations/typography`,
|
|
380
|
-
},
|
|
381
|
-
],
|
|
382
|
-
},
|
|
383
|
-
];
|
|
384
|
-
|
|
385
|
-
const componentMenuItems = [];
|
|
386
|
-
const components = fetchComponents({ includeApi: false });
|
|
387
|
-
// Build the submenu of exportables (components)
|
|
388
|
-
const groupedComponents = groupBy(components, (e) => e.group ?? '');
|
|
389
|
-
Object.keys(groupedComponents).forEach((group) => {
|
|
390
|
-
groupedComponents[group].forEach((component) => {
|
|
391
|
-
const docs = fetchDocPageMetadataAndContent('docs/system/', component.id);
|
|
392
|
-
let title = startCase(component.id);
|
|
393
|
-
if (docs.metadata.title) {
|
|
394
|
-
title = docs.metadata.title;
|
|
395
|
-
}
|
|
396
|
-
if (component.name) {
|
|
397
|
-
title = component.name;
|
|
398
|
-
}
|
|
399
|
-
componentMenuItems.push({ path: `${basePath}system/tokens/components/${component.id}`, title });
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
if (componentMenuItems.length > 0) {
|
|
404
|
-
menu.push({
|
|
405
|
-
title: `Components`,
|
|
406
|
-
path: `${basePath}system/tokens/components`,
|
|
407
|
-
menu: componentMenuItems,
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return menu;
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
const staticBuildTokenMenu = () => {
|
|
415
|
-
const basePath = buildBasePath();
|
|
416
|
-
|
|
417
|
-
let subSections = {
|
|
418
|
-
title: 'Tokens',
|
|
419
|
-
path: `${basePath}system/tokens`,
|
|
420
|
-
menu: [],
|
|
421
|
-
};
|
|
422
|
-
const tokens = getTokens();
|
|
423
|
-
|
|
424
|
-
return subSections;
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Fetch patterns from the patterns.json API file
|
|
429
|
-
*/
|
|
430
|
-
export const fetchPatterns = (): { id: string; title: string; description: string; group: string }[] => {
|
|
431
|
-
const patternsFilePath = path.resolve(
|
|
432
|
-
process.env.HANDOFF_MODULE_PATH ?? '',
|
|
433
|
-
'.handoff',
|
|
434
|
-
`${process.env.HANDOFF_PROJECT_ID}`,
|
|
435
|
-
'public',
|
|
436
|
-
'api',
|
|
437
|
-
'patterns.json'
|
|
438
|
-
);
|
|
439
|
-
|
|
440
|
-
if (!fs.existsSync(patternsFilePath)) {
|
|
441
|
-
return [];
|
|
442
|
-
}
|
|
443
|
-
|
|
149
|
+
export const fetchPatterns = async (): Promise<PatternListObject[]> => {
|
|
444
150
|
try {
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
) as PatternListObject[];
|
|
448
|
-
|
|
449
|
-
return patternList.map((p) => ({
|
|
450
|
-
id: p.id,
|
|
451
|
-
title: p.title || '',
|
|
452
|
-
description: p.description || '',
|
|
453
|
-
group: p.group || '',
|
|
454
|
-
}));
|
|
151
|
+
const backend = await resolveDocsBackend();
|
|
152
|
+
return (await backend.listPatterns()) ?? [];
|
|
455
153
|
} catch {
|
|
456
154
|
return [];
|
|
457
155
|
}
|
|
458
156
|
};
|
|
459
157
|
|
|
460
|
-
const staticBuildPatternMenu = () => {
|
|
461
|
-
const basePath = buildBasePath();
|
|
462
|
-
const patterns = fetchPatterns();
|
|
463
|
-
|
|
464
|
-
if (patterns.length === 0) return [];
|
|
465
|
-
|
|
466
|
-
const grouped = groupBy(patterns, (p) => p.group ?? '');
|
|
467
|
-
let menu: { title: string; menu: { path: string; title: string }[] }[] = [];
|
|
468
|
-
|
|
469
|
-
Object.keys(grouped).forEach((group) => {
|
|
470
|
-
const menuGroup = { title: group || 'Uncategorized', menu: [] as { path: string; title: string }[] };
|
|
471
|
-
grouped[group].forEach((pattern) => {
|
|
472
|
-
menuGroup.menu.push({
|
|
473
|
-
path: `${basePath}system/pattern/${pattern.id}`,
|
|
474
|
-
title: pattern.title || startCase(pattern.id),
|
|
475
|
-
});
|
|
476
|
-
});
|
|
477
|
-
menuGroup.menu.sort((a, b) => a.title.localeCompare(b.title));
|
|
478
|
-
menu.push(menuGroup);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
menu.sort((a, b) => a.title.localeCompare(b.title));
|
|
482
|
-
return menu;
|
|
483
|
-
};
|
|
484
|
-
|
|
485
158
|
/**
|
|
486
159
|
* Filter the menus by the current path
|
|
487
160
|
* @param menu
|
|
@@ -491,14 +164,54 @@ const staticBuildPatternMenu = () => {
|
|
|
491
164
|
export const getCurrentSection = (menu: SectionLink[], path: string): SectionLink | null =>
|
|
492
165
|
menu.filter((section) => section.path === path)[0];
|
|
493
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Resolve the render-ready first-paint navigation for every page. Registry initial delivery is
|
|
169
|
+
* deliberately shell-only; workspace delivery fills the same canonical tree from local records.
|
|
170
|
+
* The legacy `menu`/`current` aliases remain page-layout props while all navigation consumers read
|
|
171
|
+
* `navData` through `NavProvider`.
|
|
172
|
+
*/
|
|
173
|
+
export const getNavProps = async (
|
|
174
|
+
currentSectionId: string
|
|
175
|
+
): Promise<{ navData: NavData; currentSectionId: string; menu: SectionLink[]; current: SectionLink | null }> => {
|
|
176
|
+
let navData: NavData;
|
|
177
|
+
if (isRegistryRuntime()) {
|
|
178
|
+
navData = await getRegistryNavData({
|
|
179
|
+
shell: navShell as unknown as SectionLink[],
|
|
180
|
+
load: 'initial',
|
|
181
|
+
basePath: process.env.HANDOFF_APP_BASE_PATH,
|
|
182
|
+
fetchRecords: async () => ({ components: [], patterns: [], pages: [], tokenSets: [] }),
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
const backend = await resolveDocsBackend();
|
|
186
|
+
navData = await getWorkspaceNavData({
|
|
187
|
+
docRoot: path.resolve(process.env.HANDOFF_MODULE_PATH ?? '', 'config/docs'),
|
|
188
|
+
workingPagesDir: path.resolve(process.env.HANDOFF_WORKING_PATH ?? '', 'pages'),
|
|
189
|
+
basePath: process.env.HANDOFF_APP_BASE_PATH,
|
|
190
|
+
load: 'initial',
|
|
191
|
+
loaders: {
|
|
192
|
+
components: () => backend.listComponents(),
|
|
193
|
+
patterns: () => backend.listPatterns(),
|
|
194
|
+
pages: () => backend.listPages(),
|
|
195
|
+
tokenSets: async () => (await backend.listTokenSets()) as NavTokenSet[],
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
navData,
|
|
201
|
+
currentSectionId,
|
|
202
|
+
menu: navData.shell,
|
|
203
|
+
current: getCurrentSection(navData.shell, currentSectionId) ?? null,
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
|
|
494
207
|
/**
|
|
495
208
|
* Build a static object for rending markdown pages
|
|
496
209
|
* @param path
|
|
497
210
|
* @param slug
|
|
498
211
|
* @returns
|
|
499
212
|
*/
|
|
500
|
-
export const fetchDocPageMarkdown = (path: string, slug: string | undefined, id: string, runtimeConfig?: RuntimeConfig) => {
|
|
501
|
-
const
|
|
213
|
+
export const fetchDocPageMarkdown = async (path: string, slug: string | undefined, id: string, runtimeConfig?: RuntimeConfig) => {
|
|
214
|
+
const nav = await getNavProps(id);
|
|
502
215
|
const { metadata, content, options } = fetchDocPageMetadataAndContent(path, slug, runtimeConfig);
|
|
503
216
|
// Return props
|
|
504
217
|
return {
|
|
@@ -506,8 +219,10 @@ export const fetchDocPageMarkdown = (path: string, slug: string | undefined, id:
|
|
|
506
219
|
metadata,
|
|
507
220
|
content,
|
|
508
221
|
options,
|
|
509
|
-
menu,
|
|
510
|
-
current:
|
|
222
|
+
menu: nav.menu,
|
|
223
|
+
current: nav.current,
|
|
224
|
+
navData: nav.navData,
|
|
225
|
+
currentSectionId: nav.currentSectionId,
|
|
511
226
|
},
|
|
512
227
|
};
|
|
513
228
|
};
|
|
@@ -519,10 +234,10 @@ export const fetchDocPageMarkdown = (path: string, slug: string | undefined, id:
|
|
|
519
234
|
* @param id
|
|
520
235
|
* @returns
|
|
521
236
|
*/
|
|
522
|
-
export const fetchCompDocPageMarkdown = (path: string, slug: string | undefined, id: string, runtimeConfig?: RuntimeConfig) => {
|
|
237
|
+
export const fetchCompDocPageMarkdown = async (path: string, slug: string | undefined, id: string, runtimeConfig?: RuntimeConfig) => {
|
|
523
238
|
return {
|
|
524
239
|
props: {
|
|
525
|
-
...fetchDocPageMarkdown(path, slug, id, runtimeConfig).props,
|
|
240
|
+
...(await fetchDocPageMarkdown(path, slug, id, runtimeConfig)).props,
|
|
526
241
|
scss: slug ? fetchTokensString(slug, 'scss') : '',
|
|
527
242
|
css: slug ? fetchTokensString(slug, 'css') : '',
|
|
528
243
|
styleDictionary: slug ? fetchTokensString(slug, 'styleDictionary') : '',
|
|
@@ -531,19 +246,68 @@ export const fetchCompDocPageMarkdown = (path: string, slug: string | undefined,
|
|
|
531
246
|
};
|
|
532
247
|
};
|
|
533
248
|
|
|
534
|
-
|
|
249
|
+
/**
|
|
250
|
+
* Fetch components from the mode-aware docs read API.
|
|
251
|
+
*
|
|
252
|
+
* Resolves through {@link resolveDocsBackend}, so the active `runtime.mode` decides the source:
|
|
253
|
+
* generated workspace artifacts in workspace mode, the registry database in registry mode. This is
|
|
254
|
+
* the entity list the navigation, detail, and preview views all share, so they agree on the same
|
|
255
|
+
* set for the active mode.
|
|
256
|
+
*/
|
|
257
|
+
export const fetchComponents = async (): Promise<ComponentListObject[]> => {
|
|
258
|
+
try {
|
|
259
|
+
const backend = await resolveDocsBackend();
|
|
260
|
+
return (await backend.listComponents()) ?? [];
|
|
261
|
+
} catch {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/** A component token set resolved for a docs page: the token object + the four download strings. */
|
|
267
|
+
export interface ComponentTokensResult {
|
|
268
|
+
component: CoreTypes.IFileComponentObject;
|
|
269
|
+
css: string;
|
|
270
|
+
scss: string;
|
|
271
|
+
styleDictionary: string;
|
|
272
|
+
types: string;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Fetch a component's token set through the mode-aware docs read API. Resolves through
|
|
277
|
+
* {@link resolveDocsBackend}, so registry mode reads the DB-backed `component/<id>` set (mutable,
|
|
278
|
+
* publishable after deploy) while workspace mode reads the generated files. Returns `null` when the
|
|
279
|
+
* set does not exist.
|
|
280
|
+
*/
|
|
281
|
+
export const fetchComponentTokens = async (componentId: string): Promise<ComponentTokensResult | null> => {
|
|
282
|
+
try {
|
|
283
|
+
const backend = await resolveDocsBackend();
|
|
284
|
+
const detail = await backend.getTokenSetDetail(`component/${componentId}`);
|
|
285
|
+
if (!detail) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
return { component: detail.record as CoreTypes.IFileComponentObject, ...tokenFormatStrings(detail.artifacts) };
|
|
289
|
+
} catch {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
type FetchLocalComponentsOptions = {
|
|
535
295
|
includeTokens?: boolean;
|
|
536
296
|
includeApi?: boolean;
|
|
537
297
|
};
|
|
538
298
|
|
|
539
299
|
/**
|
|
540
|
-
* Fetch
|
|
300
|
+
* Fetch components from the local, build-time workspace artifacts (`tokens.json` and the generated
|
|
301
|
+
* `components.json`). This is the legacy, mode-independent local read used **only** by the tokens
|
|
302
|
+
* views, which are not yet covered by the registry read path (out of scope); it intentionally stays
|
|
303
|
+
* local so token behavior is preserved exactly until tokens are migrated.
|
|
304
|
+
*
|
|
541
305
|
* @param options - Configuration object to specify which component sources to include
|
|
542
306
|
* @param options.includeTokens - Include components from tokens.json (default: true)
|
|
543
307
|
* @param options.includeApi - Include components from components.json API (default: true)
|
|
544
|
-
* @returns
|
|
308
|
+
* @returns Array of component objects with id, type, group, name, and description
|
|
545
309
|
*/
|
|
546
|
-
export const
|
|
310
|
+
export const fetchLocalComponents = (options?: FetchLocalComponentsOptions) => {
|
|
547
311
|
const includeTokens = options?.includeTokens ?? true;
|
|
548
312
|
const includeApi = options?.includeApi ?? true;
|
|
549
313
|
|
|
@@ -619,36 +383,61 @@ let cachedClientConfig: ClientConfigCache | null = null;
|
|
|
619
383
|
|
|
620
384
|
const getDefaultClientConfig = (): ClientConfigCache => {
|
|
621
385
|
return {
|
|
622
|
-
config: {} as ClientConfig,
|
|
386
|
+
config: { runtime: { mode: 'workspace', connected: false } } as ClientConfig,
|
|
623
387
|
};
|
|
624
388
|
};
|
|
625
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Resolve the persisted client config path. Workspace dev/static reads it from the build-machine
|
|
392
|
+
* `.handoff/<projectId>` staging dir; the packaged registry app reads it from the entry
|
|
393
|
+
* dir the build copies it into (the standalone server's cwd), since the build-machine path does not
|
|
394
|
+
* exist on the deploy host.
|
|
395
|
+
*/
|
|
396
|
+
const resolveClientConfigPath = (): string | null => {
|
|
397
|
+
const candidates = [
|
|
398
|
+
path.resolve(process.env.HANDOFF_MODULE_PATH ?? '', '.handoff', process.env.HANDOFF_PROJECT_ID ?? '', 'client.config.json'),
|
|
399
|
+
path.resolve(process.cwd(), 'client.config.json'),
|
|
400
|
+
];
|
|
401
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Apply the build-time-baked runtime mode over a loaded client config. Mode is
|
|
406
|
+
* config-only and baked into the bundle, so the packaged registry app reports `registry` even when
|
|
407
|
+
* its client.config.json cannot be resolved on the deploy host.
|
|
408
|
+
*/
|
|
409
|
+
const applyRuntimeModeOverride = (cache: ClientConfigCache): ClientConfigCache => {
|
|
410
|
+
const bakedMode = process.env.HANDOFF_RUNTIME_MODE?.trim();
|
|
411
|
+
if (bakedMode === 'workspace' || bakedMode === 'registry') {
|
|
412
|
+
// A baked registry mode is never a connected workspace, so clear `connected` to keep the
|
|
413
|
+
// projected state internally consistent regardless of the source project's config.
|
|
414
|
+
const connected = bakedMode === 'registry' ? false : (cache.config.runtime?.connected ?? false);
|
|
415
|
+
return { config: { ...cache.config, runtime: { ...cache.config.runtime, mode: bakedMode, connected } } };
|
|
416
|
+
}
|
|
417
|
+
return cache;
|
|
418
|
+
};
|
|
419
|
+
|
|
626
420
|
const loadClientConfig = (): ClientConfigCache => {
|
|
627
421
|
if (cachedClientConfig) {
|
|
628
422
|
return cachedClientConfig;
|
|
629
423
|
}
|
|
630
424
|
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
if (!fs.existsSync(clientConfigPath)) {
|
|
636
|
-
// Return empty default instead of throwing to support running without fetch
|
|
637
|
-
return getDefaultClientConfig();
|
|
425
|
+
const clientConfigPath = resolveClientConfigPath();
|
|
426
|
+
if (!clientConfigPath) {
|
|
427
|
+
// No persisted config: still honor a baked runtime mode so the packaged app reports correctly.
|
|
428
|
+
return applyRuntimeModeOverride(getDefaultClientConfig());
|
|
638
429
|
}
|
|
639
430
|
|
|
640
431
|
try {
|
|
641
432
|
const cacheContent = fs.readFileSync(clientConfigPath, 'utf-8');
|
|
642
|
-
cachedClientConfig = JSON.parse(cacheContent) as ClientConfigCache;
|
|
433
|
+
cachedClientConfig = applyRuntimeModeOverride(JSON.parse(cacheContent) as ClientConfigCache);
|
|
643
434
|
return cachedClientConfig;
|
|
644
435
|
} catch (e) {
|
|
645
436
|
// Return empty default on error instead of throwing
|
|
646
|
-
return getDefaultClientConfig();
|
|
437
|
+
return applyRuntimeModeOverride(getDefaultClientConfig());
|
|
647
438
|
}
|
|
648
439
|
};
|
|
649
440
|
|
|
650
|
-
|
|
651
|
-
|
|
652
441
|
/**
|
|
653
442
|
* Fetch Component Doc Page Markdown
|
|
654
443
|
* @param path
|
|
@@ -656,14 +445,17 @@ const loadClientConfig = (): ClientConfigCache => {
|
|
|
656
445
|
* @param id
|
|
657
446
|
* @returns
|
|
658
447
|
*/
|
|
659
|
-
export const fetchFoundationDocPageMarkdown = (path: string, slug: string | undefined, id: string) => {
|
|
448
|
+
export const fetchFoundationDocPageMarkdown = async (path: string, slug: string | undefined, id: string) => {
|
|
449
|
+
// In a registry build the download strings must not be frozen from the build machine's generated
|
|
450
|
+
// files; they hydrate at request time from the DB-backed docs read API (`useFoundationTokens`).
|
|
451
|
+
const readTokens = !isRegistryRuntime() && slug;
|
|
660
452
|
return {
|
|
661
453
|
props: {
|
|
662
|
-
...fetchDocPageMarkdown(path, slug, id).props,
|
|
663
|
-
scss:
|
|
664
|
-
css:
|
|
665
|
-
styleDictionary:
|
|
666
|
-
types:
|
|
454
|
+
...(await fetchDocPageMarkdown(path, slug, id)).props,
|
|
455
|
+
scss: readTokens ? fetchTokensString(pluralizeComponent(slug), 'scss') : '',
|
|
456
|
+
css: readTokens ? fetchTokensString(pluralizeComponent(slug), 'css') : '',
|
|
457
|
+
styleDictionary: readTokens ? fetchTokensString(pluralizeComponent(slug), 'styleDictionary') : '',
|
|
458
|
+
types: readTokens ? fetchTokensString(pluralizeComponent(slug), 'types') : '',
|
|
667
459
|
},
|
|
668
460
|
};
|
|
669
461
|
};
|
|
@@ -673,6 +465,35 @@ export const getClientRuntimeConfig = (): ClientConfig => {
|
|
|
673
465
|
return clientConfig.config;
|
|
674
466
|
};
|
|
675
467
|
|
|
468
|
+
/**
|
|
469
|
+
* Whether the docs app is running in registry mode. Drives the request-time behaviors (on-demand
|
|
470
|
+
* detail rendering and the live nav fetch) that must not affect the workspace dev or static-export
|
|
471
|
+
* builds. Resolves from the same baked/persisted runtime mode the docs read API uses.
|
|
472
|
+
*/
|
|
473
|
+
export const isRegistryRuntime = (): boolean => getClientRuntimeConfig().runtime?.mode === 'registry';
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Build-time foundation `localStyles` for a foundation page's `getStaticProps`. In workspace/static
|
|
477
|
+
* this is the local `tokens.json` snapshot (correct: static exports are immutable). In a **registry**
|
|
478
|
+
* build it is empty — foundation token data must not be frozen into build-time page props from the
|
|
479
|
+
* build machine's `tokens.json`; the page hydrates it at request time from the DB-backed docs read
|
|
480
|
+
* API (`useFoundationTokens`). Without this gate a registry build bakes the builder's local tokens,
|
|
481
|
+
* so foundations would appear before anything is published.
|
|
482
|
+
*/
|
|
483
|
+
export const buildTimeFoundationDesign = (): CoreTypes.IDocumentationObject['localStyles'] =>
|
|
484
|
+
isRegistryRuntime()
|
|
485
|
+
? ({ color: [], typography: [], effect: [] } as CoreTypes.IDocumentationObject['localStyles'])
|
|
486
|
+
: getTokens().localStyles;
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Build-time asset collections for an asset page's `getStaticProps`. Workspace/static bakes the local
|
|
490
|
+
* `tokens.json` assets (correct: static exports are immutable snapshots). A **registry** build returns
|
|
491
|
+
* empty: asset bodies must not be frozen into build-time page props from the build machine; the page
|
|
492
|
+
* hydrates them at request time from the DB-backed docs read API (`useCollectionAssets`). Without this
|
|
493
|
+
* gate a registry build bakes the builder's local assets, so they would appear before any publish.
|
|
494
|
+
*/
|
|
495
|
+
export const buildTimeAssets = (): CoreTypes.IDocumentationObject['assets'] => (isRegistryRuntime() ? {} : getTokens().assets);
|
|
496
|
+
|
|
676
497
|
export const getTokens = (): CoreTypes.IDocumentationObject => {
|
|
677
498
|
const exportedFilePath = process.env.HANDOFF_EXPORT_PATH
|
|
678
499
|
? path.resolve(process.env.HANDOFF_EXPORT_PATH, 'tokens.json')
|
|
@@ -731,7 +552,9 @@ export const fetchDocPageMetadataAndContent = (localPath: string, slug: string |
|
|
|
731
552
|
const contentModuleFilePath = path.resolve(handoffModulePath, 'config', `${localPath}${slug}.md`);
|
|
732
553
|
const contentWorkingFilePath = path.resolve(handoffWorkingPath, `${pagePath}${slug}.md`);
|
|
733
554
|
|
|
734
|
-
|
|
555
|
+
// Registry serves package `config/docs` only; workspace overrides are invisible to it (custom
|
|
556
|
+
// content comes from the DB), so the working-path read is skipped in registry mode.
|
|
557
|
+
if (!isRegistryRuntime() && fs.existsSync(contentWorkingFilePath)) {
|
|
735
558
|
currentContents = fs.readFileSync(contentWorkingFilePath, 'utf-8');
|
|
736
559
|
} else if (!fs.existsSync(contentModuleFilePath)) {
|
|
737
560
|
return { metadata: {}, content: currentContents, options: {} };
|