handoff-app 1.2.2-7 → 2.0.0-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Changelog.md +37 -15
- package/README.md +372 -118
- package/dist/app-builder/build.d.ts +41 -2
- package/dist/app-builder/build.js +455 -18
- package/dist/app-builder/client-config.d.ts +15 -2
- package/dist/app-builder/client-config.js +43 -4
- package/dist/app-builder/config-diff/snapshot.d.ts +1 -6
- package/dist/app-builder/config-diff/snapshot.js +2 -9
- package/dist/app-builder/index.d.ts +1 -1
- package/dist/app-builder/index.js +3 -1
- package/dist/app-builder/nav-shell.d.ts +23 -0
- package/dist/app-builder/nav-shell.js +80 -0
- package/dist/app-builder/static-export.d.ts +17 -0
- package/dist/app-builder/static-export.js +181 -0
- package/dist/app-builder/vercel-output.d.ts +39 -0
- package/dist/app-builder/vercel-output.js +259 -0
- package/dist/app-builder/watchers/utils.d.ts +2 -0
- package/dist/app-builder/watchers.d.ts +14 -4
- package/dist/app-builder/watchers.js +123 -28
- package/dist/app-builder/websocket.js +1 -1
- package/dist/artifacts/index.d.ts +2 -0
- package/dist/artifacts/index.js +13 -0
- package/dist/artifacts/types.d.ts +136 -0
- package/dist/artifacts/types.js +24 -0
- package/dist/artifacts/url.d.ts +49 -0
- package/dist/artifacts/url.js +86 -0
- package/dist/cache/build-cache.js +48 -4
- package/dist/cli.js +0 -0
- package/dist/commands/build/app.js +1 -2
- package/dist/commands/build/index.d.ts +17 -0
- package/dist/commands/build/index.js +66 -0
- package/dist/commands/checkout/index.d.ts +24 -0
- package/dist/commands/checkout/index.js +71 -0
- package/dist/commands/db/migrate.d.ts +11 -0
- package/dist/commands/db/migrate.js +33 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/publish/index.d.ts +21 -0
- package/dist/commands/publish/index.js +68 -0
- package/dist/config/defaults.js +15 -1
- package/dist/config/loader.js +6 -1
- package/dist/config/normalizers/declaration.js +4 -1
- package/dist/config/normalizers/page.d.ts +18 -0
- package/dist/config/normalizers/page.js +34 -0
- package/dist/config/normalizers/pattern.js +7 -1
- package/dist/config/runtime.d.ts +4 -0
- package/dist/config/runtime.js +53 -5
- package/dist/index.d.ts +65 -1
- package/dist/index.js +123 -3
- package/dist/nav/index.d.ts +83 -0
- package/dist/nav/index.js +9 -0
- package/dist/nav/registry.d.ts +15 -0
- package/dist/nav/registry.js +42 -0
- package/dist/nav/resolver.d.ts +8 -0
- package/dist/nav/resolver.js +192 -0
- package/dist/nav/workspace.d.ts +18 -0
- package/dist/nav/workspace.js +103 -0
- package/dist/pipeline/styles.js +7 -35
- package/dist/pipeline/token-transformers.d.ts +45 -0
- package/dist/pipeline/token-transformers.js +71 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +17 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.js +87 -0
- package/dist/registry/asset-storage/define.d.ts +10 -0
- package/dist/registry/asset-storage/define.js +13 -0
- package/dist/registry/asset-storage/resolve.d.ts +45 -0
- package/dist/registry/asset-storage/resolve.js +36 -0
- package/dist/registry/asset-storage/types.d.ts +51 -0
- package/dist/registry/asset-storage/types.js +9 -0
- package/dist/registry/assets/layout.d.ts +22 -0
- package/dist/registry/assets/layout.js +30 -0
- package/dist/registry/assets/sets.d.ts +18 -0
- package/dist/registry/assets/sets.js +44 -0
- package/dist/registry/assets/transfer.d.ts +45 -0
- package/dist/registry/assets/transfer.js +11 -0
- package/dist/registry/checkout/assets.d.ts +16 -0
- package/dist/registry/checkout/assets.js +150 -0
- package/dist/registry/checkout/index.d.ts +33 -0
- package/dist/registry/checkout/index.js +610 -0
- package/dist/registry/checkout/tokens.d.ts +15 -0
- package/dist/registry/checkout/tokens.js +171 -0
- package/dist/registry/client.d.ts +61 -0
- package/dist/registry/client.js +211 -0
- package/dist/registry/connection.d.ts +30 -0
- package/dist/registry/connection.js +36 -0
- package/dist/registry/db/adapter.d.ts +43 -0
- package/dist/registry/db/adapter.js +59 -0
- package/dist/registry/db/client.d.ts +35 -0
- package/dist/registry/db/client.js +94 -0
- package/dist/registry/db/index.d.ts +5 -0
- package/dist/registry/db/index.js +24 -0
- package/dist/registry/db/migrate.d.ts +23 -0
- package/dist/registry/db/migrate.js +64 -0
- package/dist/registry/db/schema.d.ts +4869 -0
- package/dist/registry/db/schema.js +347 -0
- package/dist/registry/path.d.ts +4 -0
- package/dist/registry/path.js +31 -0
- package/dist/registry/publish/artifacts.d.ts +16 -0
- package/dist/registry/publish/artifacts.js +132 -0
- package/dist/registry/publish/assets.d.ts +16 -0
- package/dist/registry/publish/assets.js +129 -0
- package/dist/registry/publish/errors.d.ts +5 -0
- package/dist/registry/publish/errors.js +26 -0
- package/dist/registry/publish/index.d.ts +27 -0
- package/dist/registry/publish/index.js +126 -0
- package/dist/registry/publish/package.d.ts +27 -0
- package/dist/registry/publish/package.js +257 -0
- package/dist/registry/publish/publish-build.d.ts +12 -0
- package/dist/registry/publish/publish-build.js +43 -0
- package/dist/registry/publish/tokens.d.ts +18 -0
- package/dist/registry/publish/tokens.js +112 -0
- package/dist/registry/tokens/sets.d.ts +59 -0
- package/dist/registry/tokens/sets.js +109 -0
- package/dist/registry/tokens/transfer.d.ts +48 -0
- package/dist/registry/tokens/transfer.js +10 -0
- package/dist/registry/transfer.d.ts +75 -0
- package/dist/registry/transfer.js +11 -0
- package/dist/store/filesystem-assets.d.ts +17 -0
- package/dist/store/filesystem-assets.js +147 -0
- package/dist/store/filesystem.d.ts +76 -0
- package/dist/store/filesystem.js +208 -0
- package/dist/store/index.d.ts +20 -0
- package/dist/store/index.js +38 -0
- package/dist/store/registry.d.ts +77 -0
- package/dist/store/registry.js +350 -0
- package/dist/store/source-files.d.ts +24 -0
- package/dist/store/source-files.js +147 -0
- package/dist/store/types.d.ts +185 -0
- package/dist/store/types.js +12 -0
- package/dist/transformers/plugins/csf-render.js +17 -8
- package/dist/transformers/plugins/handlebars-previews.js +10 -3
- package/dist/transformers/plugins/ssr-render.js +116 -49
- package/dist/transformers/preview/component/api.js +3 -1
- package/dist/transformers/preview/component/artifacts.js +8 -1
- package/dist/transformers/preview/component/builder.js +13 -5
- package/dist/transformers/preview/component/css.js +9 -1
- package/dist/transformers/preview/component/javascript.d.ts +9 -3
- package/dist/transformers/preview/component/javascript.js +45 -17
- package/dist/transformers/preview/component/shared-artifacts.d.ts +83 -0
- package/dist/transformers/preview/component/shared-artifacts.js +92 -0
- package/dist/transformers/preview/component.js +5 -2
- package/dist/transformers/preview/pattern/builder.js +28 -9
- package/dist/transformers/preview/pattern/html.d.ts +13 -10
- package/dist/transformers/preview/pattern/html.js +62 -27
- package/dist/transformers/preview/types.d.ts +38 -0
- package/dist/transformers/utils/handlebars.d.ts +3 -0
- package/dist/transformers/utils/handlebars.js +19 -5
- package/dist/types/config.d.ts +118 -25
- package/dist/utils/menu-shell.d.ts +60 -0
- package/dist/utils/menu-shell.js +221 -0
- package/dist/utils/stable-stringify.d.ts +2 -0
- package/dist/utils/stable-stringify.js +8 -0
- package/docs/cli.md +4 -2
- package/drizzle/0000_init_registry_storage.sql +95 -0
- package/drizzle/0001_add_review_metadata.sql +2 -0
- package/drizzle/0002_add_pages.sql +28 -0
- package/drizzle/0003_add_tokens.sql +29 -0
- package/drizzle/0004_add_assets.sql +37 -0
- package/drizzle/meta/0000_snapshot.json +642 -0
- package/drizzle/meta/0001_snapshot.json +654 -0
- package/drizzle/meta/0002_snapshot.json +835 -0
- package/drizzle/meta/0003_snapshot.json +1023 -0
- package/drizzle/meta/0004_snapshot.json +1252 -0
- package/drizzle/meta/_journal.json +41 -0
- package/drizzle.config.ts +16 -0
- package/package.json +10 -1
- package/src/app/components/Component/Preview.tsx +3 -2
- package/src/app/components/Component/RegistryActions.tsx +150 -0
- package/src/app/components/Foundations/DisplayLogo.tsx +5 -3
- package/src/app/components/Layout/Header.tsx +21 -3
- package/src/app/components/Layout/Main.tsx +11 -3
- package/src/app/components/Navigation/MainNav.tsx +4 -2
- package/src/app/components/Navigation/MobileNav.tsx +4 -2
- package/src/app/components/Navigation/SideNav.tsx +46 -11
- package/src/app/components/NotFound.tsx +24 -0
- package/src/app/components/context/NavProvider.tsx +71 -0
- package/src/app/components/old/Header.tsx +3 -3
- package/src/app/components/util/index.ts +203 -380
- package/src/app/components/util/useCollectionAssets.ts +125 -0
- package/src/app/components/util/useFoundationTokens.ts +67 -0
- package/src/app/generated/default-pages.json +1 -0
- package/src/app/generated/nav-shell.json +1 -0
- package/src/app/lib/api/query.ts +6 -0
- package/src/app/lib/api/redact.ts +46 -0
- package/src/app/lib/asset-storage.ts +81 -0
- package/src/app/lib/docs-api/artifacts.ts +116 -0
- package/src/app/lib/docs-api/backend.ts +151 -0
- package/src/app/lib/docs-api/errors.ts +52 -0
- package/src/app/lib/docs-api/handler.ts +34 -0
- package/src/app/lib/docs-api/index.ts +7 -0
- package/src/app/lib/docs-api/records.ts +189 -0
- package/src/app/lib/docs-api/registry.ts +130 -0
- package/src/app/lib/docs-api/runtime-config.ts +173 -0
- package/src/app/lib/docs-api/serve.ts +37 -0
- package/src/app/lib/docs-api/token-detail.ts +19 -0
- package/src/app/lib/registry-api/allowlist.ts +163 -0
- package/src/app/lib/registry-api/asset-transfer.ts +363 -0
- package/src/app/lib/registry-api/auth.ts +68 -0
- package/src/app/lib/registry-api/entity-routes.ts +197 -0
- package/src/app/lib/registry-api/errors.ts +51 -0
- package/src/app/lib/registry-api/files.ts +135 -0
- package/src/app/lib/registry-api/handler.ts +96 -0
- package/src/app/lib/registry-api/index.ts +4 -0
- package/src/app/lib/registry-api/meta.ts +87 -0
- package/src/app/lib/registry-api/revalidate.ts +36 -0
- package/src/app/lib/registry-api/store.ts +348 -0
- package/src/app/lib/registry-api/token-transfer.ts +231 -0
- package/src/app/lib/registry-api/transfer.ts +513 -0
- package/src/app/lib/registry-api/validation.ts +74 -0
- package/src/app/lib/registry-connection.ts +58 -0
- package/src/app/lib/utils.ts +14 -0
- package/src/app/next.config.mjs +72 -13
- package/src/app/pages/[...slug]/index.tsx +74 -20
- package/src/app/pages/_app.tsx +5 -2
- package/src/app/pages/api/docs/artifacts/[...artifactPath].ts +16 -0
- package/src/app/pages/api/docs/assets/[collection]/[...path].ts +50 -0
- package/src/app/pages/api/docs/assets/[collection]/index.ts +22 -0
- package/src/app/pages/api/docs/components/[id]/assets/[asset].ts +33 -0
- package/src/app/pages/api/docs/components/[id]/inspect/[previewId].ts +20 -0
- package/src/app/pages/api/docs/components/[id]/previews/[previewId].ts +21 -0
- package/src/app/pages/api/docs/components/[id].ts +24 -0
- package/src/app/pages/api/docs/components.json.ts +12 -0
- package/src/app/pages/api/docs/nav.json.ts +48 -0
- package/src/app/pages/api/docs/patterns/[id]/preview.ts +19 -0
- package/src/app/pages/api/docs/patterns/[id].ts +24 -0
- package/src/app/pages/api/docs/patterns.json.ts +12 -0
- package/src/app/pages/api/docs/tokens/components/[id].ts +30 -0
- package/src/app/pages/api/docs/tokens/foundations/[type].ts +32 -0
- package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/components/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/components/[id].ts +20 -0
- package/src/app/pages/api/registry/components/index.ts +19 -0
- package/src/app/pages/api/registry/health.ts +20 -0
- package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/patterns/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/patterns/[id].ts +20 -0
- package/src/app/pages/api/registry/patterns/index.ts +19 -0
- package/src/app/pages/api/registry/transfer/[kind]/[id].ts +39 -0
- package/src/app/pages/api/registry/transfer/assets/[collection].ts +24 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +23 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +11 -0
- package/src/app/pages/api/registry/transfer/assets/index.ts +10 -0
- package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +26 -0
- package/src/app/pages/api/registry/transfer/tokens/index.ts +11 -0
- package/src/app/pages/assets/fonts.tsx +41 -17
- package/src/app/pages/assets/index.tsx +2 -2
- package/src/app/pages/assets/logos.tsx +2 -2
- package/src/app/pages/foundations/colors.tsx +18 -8
- package/src/app/pages/foundations/effects.tsx +18 -8
- package/src/app/pages/foundations/icons/[name]/index.tsx +26 -14
- package/src/app/pages/foundations/icons/index.tsx +13 -9
- package/src/app/pages/foundations/index.tsx +1 -1
- package/src/app/pages/foundations/logo.tsx +7 -4
- package/src/app/pages/foundations/typography.tsx +19 -9
- package/src/app/pages/index.tsx +1 -1
- package/src/app/pages/system/component/[component]/index.tsx +87 -17
- package/src/app/pages/system/index.tsx +13 -4
- package/src/app/pages/system/pattern/[pattern]/index.tsx +70 -14
- package/src/app/pages/system/tokens/components/[component]/index.tsx +42 -14
- package/src/app/pages/system/tokens/foundations/colors.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/effects.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/typography.tsx +8 -4
- package/src/app/tsconfig.json +2 -3
- package/src/app-builder/build.ts +555 -11
- package/src/app-builder/client-config.ts +61 -4
- package/src/app-builder/config-diff/snapshot.ts +1 -11
- package/src/app-builder/index.ts +1 -1
- package/src/app-builder/nav-shell.ts +74 -0
- package/src/app-builder/static-export.ts +215 -0
- package/src/app-builder/vercel-output.ts +297 -0
- package/src/app-builder/watchers/utils.ts +2 -0
- package/src/app-builder/watchers.ts +149 -25
- package/src/app-builder/websocket.ts +2 -2
- package/src/artifacts/README.md +14 -0
- package/src/artifacts/index.ts +17 -0
- package/src/artifacts/types.ts +146 -0
- package/src/artifacts/url.ts +90 -0
- package/src/cache/build-cache.ts +47 -2
- package/src/commands/build/app.ts +1 -2
- package/src/commands/build/index.ts +61 -0
- package/src/commands/checkout/index.ts +66 -0
- package/src/commands/db/migrate.ts +25 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/publish/index.ts +63 -0
- package/src/config/defaults.ts +15 -0
- package/src/config/loader.ts +10 -1
- package/src/config/normalizers/declaration.ts +3 -0
- package/src/config/normalizers/page.ts +45 -0
- package/src/config/normalizers/pattern.ts +4 -1
- package/src/config/runtime.ts +48 -2
- package/src/index.ts +126 -3
- package/src/nav/index.ts +94 -0
- package/src/nav/registry.ts +54 -0
- package/src/nav/resolver.ts +217 -0
- package/src/nav/workspace.ts +87 -0
- package/src/pipeline/styles.ts +4 -35
- package/src/pipeline/token-transformers.ts +97 -0
- package/src/registry/asset-storage/adapters/vercel-blob.ts +51 -0
- package/src/registry/asset-storage/define.ts +15 -0
- package/src/registry/asset-storage/resolve.ts +72 -0
- package/src/registry/asset-storage/types.ts +46 -0
- package/src/registry/assets/layout.ts +38 -0
- package/src/registry/assets/sets.ts +48 -0
- package/src/registry/assets/transfer.ts +50 -0
- package/src/registry/checkout/assets.ts +126 -0
- package/src/registry/checkout/index.ts +596 -0
- package/src/registry/checkout/tokens.ts +152 -0
- package/src/registry/client.ts +234 -0
- package/src/registry/connection.ts +50 -0
- package/src/registry/db/adapter.ts +74 -0
- package/src/registry/db/client.ts +88 -0
- package/src/registry/db/index.ts +35 -0
- package/src/registry/db/migrate.ts +56 -0
- package/src/registry/db/schema.ts +444 -0
- package/src/registry/path.ts +26 -0
- package/src/registry/publish/artifacts.ts +147 -0
- package/src/registry/publish/assets.ts +129 -0
- package/src/registry/publish/errors.ts +34 -0
- package/src/registry/publish/index.ts +128 -0
- package/src/registry/publish/package.ts +272 -0
- package/src/registry/publish/publish-build.ts +51 -0
- package/src/registry/publish/tokens.ts +119 -0
- package/src/registry/tokens/sets.ts +132 -0
- package/src/registry/tokens/transfer.ts +53 -0
- package/src/registry/transfer.ts +82 -0
- package/src/store/filesystem-assets.ts +152 -0
- package/src/store/filesystem.ts +237 -0
- package/src/store/index.ts +62 -0
- package/src/store/registry.ts +334 -0
- package/src/store/source-files.ts +155 -0
- package/src/store/types.ts +210 -0
- package/src/transformers/plugins/csf-render.ts +31 -7
- package/src/transformers/plugins/handlebars-previews.ts +22 -3
- package/src/transformers/plugins/ssr-render.ts +145 -57
- package/src/transformers/preview/component/api.ts +2 -1
- package/src/transformers/preview/component/artifacts.ts +10 -1
- package/src/transformers/preview/component/builder.ts +9 -2
- package/src/transformers/preview/component/css.ts +10 -1
- package/src/transformers/preview/component/javascript.ts +55 -20
- package/src/transformers/preview/component/shared-artifacts.ts +146 -0
- package/src/transformers/preview/component.ts +5 -2
- package/src/transformers/preview/pattern/builder.ts +33 -7
- package/src/transformers/preview/pattern/html.ts +71 -27
- package/src/transformers/preview/types.ts +42 -0
- package/src/transformers/utils/handlebars.ts +31 -5
- package/src/types/config.ts +123 -25
- package/src/utils/menu-shell.ts +241 -0
- package/src/utils/stable-stringify.ts +7 -0
- package/templates/default/package.json.tpl +1 -1
- package/handoff.config.js +0 -90
- package/src/app/components/ComponentSearch.tsx +0 -93
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
import { startCase } from 'lodash';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import type { NavMenuItem, NavSubSection, SectionLink } from '../nav';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Build-time menu *shell* builder.
|
|
9
|
+
*
|
|
10
|
+
* The docs navigation is two things: a SHELL (which sections exist, their order/titles, and which
|
|
11
|
+
* submenu slots are filled at runtime from the registry) and the runtime ENTITY lists
|
|
12
|
+
* (components/patterns/token sets) that the resolver splices into those slots. The shell is
|
|
13
|
+
* markdown-driven and therefore static per build; the entities are mutable at runtime and the
|
|
14
|
+
* resolved tree is served by `/api/docs/nav.json`.
|
|
15
|
+
*
|
|
16
|
+
* On Vercel the registry function cannot read `config/docs` markdown at request time — those reads
|
|
17
|
+
* use absolute build-machine paths (`HANDOFF_MODULE_PATH/...`) that do not exist on the deploy host
|
|
18
|
+
* and that nft cannot trace. So the shell is frozen here at build time (where the markdown is
|
|
19
|
+
* present) into `generated/nav-shell.json`, which the API route imports statically (bundled, path
|
|
20
|
+
* independent). The resolver combines it with runtime-variable records before the client renders it.
|
|
21
|
+
*
|
|
22
|
+
* This module is intentionally dependency-light and PURE (fs + gray-matter, no DB, no React, no
|
|
23
|
+
* `@handoff` aliases) so the compiled build pipeline (`dist/`) can import it — `src/app` is excluded
|
|
24
|
+
* from `tsc` and is not importable from the builder.
|
|
25
|
+
*
|
|
26
|
+
* It emits the canonical section structure with empty entity lists and `dynamic` splice markers.
|
|
27
|
+
* `src/nav/resolver.ts` is the sole place that fills those markers in every runtime mode.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** Back-compatible shell-builder names reconciled to the canonical navigation contract. */
|
|
31
|
+
export type MenuShellSubItem = NavMenuItem;
|
|
32
|
+
export type MenuShellSubSection = NavSubSection;
|
|
33
|
+
export type MenuShellSection = SectionLink;
|
|
34
|
+
|
|
35
|
+
export interface BuildMenuShellOptions {
|
|
36
|
+
/** Absolute path to the package docs root (`<modulePath>/config/docs`). */
|
|
37
|
+
docRoot: string;
|
|
38
|
+
/** Absolute path to the working-project page overrides (`<workingPath>/pages`), optional. */
|
|
39
|
+
workingPagesDir?: string;
|
|
40
|
+
/** Resolved app base path (e.g. `"foundations"` → no leading/trailing slash), optional. */
|
|
41
|
+
basePath?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Paths that have dedicated route files and are therefore excluded from auto-scanned submenus.
|
|
46
|
+
* Single source of truth — the docs app imports this (re-exported there as `knownPaths`).
|
|
47
|
+
*/
|
|
48
|
+
export const KNOWN_PATHS = [
|
|
49
|
+
'assets',
|
|
50
|
+
'assets/fonts',
|
|
51
|
+
'assets/icons',
|
|
52
|
+
'assets/logos',
|
|
53
|
+
'foundations',
|
|
54
|
+
'foundations/colors',
|
|
55
|
+
'foundations/icons',
|
|
56
|
+
'foundations/effects',
|
|
57
|
+
'foundations/logos',
|
|
58
|
+
'foundations/logo',
|
|
59
|
+
'foundations/typography',
|
|
60
|
+
'system',
|
|
61
|
+
'system/component',
|
|
62
|
+
'system/tokens',
|
|
63
|
+
'system/tokens/foundations',
|
|
64
|
+
'system/tokens/foundations/colors',
|
|
65
|
+
'system/tokens/foundations/effects',
|
|
66
|
+
'system/tokens/foundations/typography',
|
|
67
|
+
'system/tokens/components',
|
|
68
|
+
'system/pattern',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/** Normalize the base path to the resolver's `"prefix/"` form (or `""`). */
|
|
72
|
+
const normalizeBasePath = (basePath?: string): string => {
|
|
73
|
+
if (!basePath) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
return basePath.replace(/^\/+|\/+$/g, '') + '/';
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The static Tokens submenu (Foundations only). Component token sets are mode-resolved runtime
|
|
81
|
+
* records, so the shell bakes only the deterministic Foundations entries. The resolver appends the
|
|
82
|
+
* component token-set records when it fills the dynamic slot. `basePath` must already be
|
|
83
|
+
* `"prefix/"`-normalized.
|
|
84
|
+
*/
|
|
85
|
+
export const buildTokensFoundationsMenu = (basePath: string): MenuShellSubItem[] => [
|
|
86
|
+
{
|
|
87
|
+
title: 'Foundations',
|
|
88
|
+
path: `${basePath}system/tokens/foundations`,
|
|
89
|
+
menu: [
|
|
90
|
+
{ title: 'Colors', path: `${basePath}system/tokens/foundations/colors` },
|
|
91
|
+
{ title: 'Effects', path: `${basePath}system/tokens/foundations/effects` },
|
|
92
|
+
{ title: 'Typography', path: `${basePath}system/tokens/foundations/typography` },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Recursively build menu entries from .md files in a directory (auto-scan for sections that do not
|
|
99
|
+
* declare a `menu` in frontmatter). Shared with the app — single source of truth for the auto-scan.
|
|
100
|
+
*/
|
|
101
|
+
export const buildMenuFromDirectory = (dirPath: string, urlPrefix: string): MenuShellSubItem[] => {
|
|
102
|
+
if (!fs.existsSync(dirPath)) return [];
|
|
103
|
+
const entries = fs.readdirSync(dirPath);
|
|
104
|
+
const items: (MenuShellSubItem & { weight?: number })[] = [];
|
|
105
|
+
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
const fullPath = path.join(dirPath, entry);
|
|
108
|
+
if (fs.lstatSync(fullPath).isDirectory()) {
|
|
109
|
+
const nestedItems = buildMenuFromDirectory(fullPath, `${urlPrefix}/${entry}`);
|
|
110
|
+
if (nestedItems.length > 0) {
|
|
111
|
+
items.push({ title: startCase(entry), path: `${urlPrefix}/${entry}`, menu: nestedItems });
|
|
112
|
+
}
|
|
113
|
+
} else if (entry.endsWith('.md') && entry !== 'index.md') {
|
|
114
|
+
const slug = entry.replace('.md', '');
|
|
115
|
+
const fullSlugPath = `${urlPrefix}/${slug}`.replace(/^\/+/, '');
|
|
116
|
+
if (KNOWN_PATHS.indexOf(fullSlugPath) >= 0) continue;
|
|
117
|
+
|
|
118
|
+
const contents = fs.readFileSync(fullPath, 'utf-8');
|
|
119
|
+
const { data: metadata } = matter(contents);
|
|
120
|
+
if (metadata.enabled === false) continue;
|
|
121
|
+
|
|
122
|
+
items.push({
|
|
123
|
+
title: metadata.menuTitle ?? metadata.title ?? startCase(slug),
|
|
124
|
+
path: `/${fullSlugPath}`,
|
|
125
|
+
weight: metadata.weight ?? 0,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return items.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0));
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Build the full navigation shell from the docs markdown, with all registry entity slots
|
|
135
|
+
* (components/patterns/token sets) emitted empty and tagged `dynamic` for the resolver to fill.
|
|
136
|
+
*
|
|
137
|
+
* Returns the top-level `MenuShellSection[]` sorted by weight — the same shape the docs app's
|
|
138
|
+
* `SectionLink[]` uses, so it serializes directly into `generated/nav-shell.json`.
|
|
139
|
+
*/
|
|
140
|
+
export const buildMenuShell = (options: BuildMenuShellOptions): MenuShellSection[] => {
|
|
141
|
+
const { docRoot, workingPagesDir } = options;
|
|
142
|
+
const basePath = normalizeBasePath(options.basePath);
|
|
143
|
+
|
|
144
|
+
if (!fs.existsSync(docRoot)) {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const docFiles = fs.readdirSync(docRoot);
|
|
149
|
+
let pageFiles: string[] = [];
|
|
150
|
+
if (workingPagesDir && fs.existsSync(workingPagesDir)) {
|
|
151
|
+
pageFiles = fs.readdirSync(workingPagesDir);
|
|
152
|
+
}
|
|
153
|
+
// De-duplicate; working-project pages win over package docs (same as the app).
|
|
154
|
+
const list = Array.from(new Set([...docFiles, ...pageFiles]));
|
|
155
|
+
|
|
156
|
+
const sections: MenuShellSection[] = [];
|
|
157
|
+
for (const fileName of list) {
|
|
158
|
+
const fromPages = pageFiles.includes(fileName);
|
|
159
|
+
const search = fromPages ? path.resolve(workingPagesDir as string, fileName) : path.resolve(docRoot, fileName);
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
fs.lstatSync(search).isDirectory() ||
|
|
163
|
+
search === path.resolve(docRoot, 'index.md') ||
|
|
164
|
+
(workingPagesDir && search === path.resolve(workingPagesDir, 'index.md')) ||
|
|
165
|
+
!fileName.endsWith('md')
|
|
166
|
+
) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const contents = fs.readFileSync(search, 'utf-8');
|
|
171
|
+
const { data: metadata } = matter(contents);
|
|
172
|
+
if (metadata.enabled === false) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const filepath = `/${fileName.replace('.md', '')}`;
|
|
177
|
+
const subSections: MenuShellSubSection[] = [];
|
|
178
|
+
|
|
179
|
+
if (metadata.menu) {
|
|
180
|
+
for (const key of Object.keys(metadata.menu)) {
|
|
181
|
+
const sub = metadata.menu[key];
|
|
182
|
+
if (sub.components) {
|
|
183
|
+
// Omit `type` entirely when there is no filter (`undefined` is not JSON-serializable).
|
|
184
|
+
const dynamic =
|
|
185
|
+
typeof sub.components === 'string'
|
|
186
|
+
? { kind: 'components' as const, type: sub.components }
|
|
187
|
+
: { kind: 'components' as const };
|
|
188
|
+
subSections.push({ title: sub.title, menu: [], dynamic });
|
|
189
|
+
} else if (sub.tokens) {
|
|
190
|
+
// Foundations are baked (deterministic); the resolver appends published component token
|
|
191
|
+
// sets into this slot when it builds the refreshed registry tree.
|
|
192
|
+
subSections.push({ title: 'Tokens', menu: buildTokensFoundationsMenu(basePath), dynamic: { kind: 'token-components' } });
|
|
193
|
+
} else if (sub.patterns) {
|
|
194
|
+
// Always keep the empty patterns slot in the shell for the resolver to fill on refresh.
|
|
195
|
+
subSections.push({ title: sub.title || 'Patterns', menu: [], dynamic: { kind: 'patterns' } });
|
|
196
|
+
} else if (sub.enabled !== false) {
|
|
197
|
+
subSections.push(sub as MenuShellSubSection);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
// No frontmatter menu: auto-scan the matching directory (working pages win over docs).
|
|
202
|
+
const dirName = fileName.replace('.md', '');
|
|
203
|
+
const nestedFromDocs = buildMenuFromDirectory(path.resolve(docRoot, dirName), `/${dirName}`);
|
|
204
|
+
const nestedFromPages = workingPagesDir
|
|
205
|
+
? buildMenuFromDirectory(path.resolve(workingPagesDir, dirName), `/${dirName}`)
|
|
206
|
+
: [];
|
|
207
|
+
const seenPaths = new Set<string>();
|
|
208
|
+
const children: MenuShellSubItem[] = [];
|
|
209
|
+
for (const item of [...nestedFromPages, ...nestedFromDocs]) {
|
|
210
|
+
if (item.path && !seenPaths.has(item.path)) {
|
|
211
|
+
seenPaths.add(item.path);
|
|
212
|
+
children.push(item);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Wrap the scanned children under one labeled group (no `path`) so the side nav renders them as
|
|
216
|
+
// links — a flat subsection carrying a `path` but no `menu` renders nothing. This matches the
|
|
217
|
+
// record-backed nesting produced by the resolver so all modes remain structurally identical.
|
|
218
|
+
if (children.length > 0) {
|
|
219
|
+
subSections.push({ title: metadata.menuTitle ?? metadata.title, menu: children });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let external: string | boolean = false;
|
|
224
|
+
if (
|
|
225
|
+
typeof metadata.external === 'string' &&
|
|
226
|
+
(metadata.external.startsWith('http://') || metadata.external.startsWith('https://') || metadata.external.startsWith('/'))
|
|
227
|
+
) {
|
|
228
|
+
external = metadata.external;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
sections.push({
|
|
232
|
+
title: metadata.menuTitle ?? metadata.title,
|
|
233
|
+
external,
|
|
234
|
+
weight: metadata.weight ?? 0,
|
|
235
|
+
path: filepath,
|
|
236
|
+
subSections,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return sections.sort((a, b) => a.weight - b.weight);
|
|
241
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Serialize a value with object keys sorted at every nesting level. */
|
|
2
|
+
export const stableStringify = (value: unknown): string =>
|
|
3
|
+
JSON.stringify(value, (_, nestedValue) =>
|
|
4
|
+
nestedValue && typeof nestedValue === 'object' && !Array.isArray(nestedValue)
|
|
5
|
+
? Object.fromEntries(Object.entries(nestedValue).sort(([left], [right]) => left.localeCompare(right)))
|
|
6
|
+
: nestedValue
|
|
7
|
+
);
|
package/handoff.config.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/** @type {import('handoff-app').Config} */
|
|
2
|
-
const path = require('path');
|
|
3
|
-
module.exports = {
|
|
4
|
-
// Set your Figma project ID here or in the environment variable HANDOFF_FIGMA_PROJECT_ID
|
|
5
|
-
figma_project_id: null,
|
|
6
|
-
// Set the output directory for the exported components
|
|
7
|
-
exportsOutputDirectory: 'exported',
|
|
8
|
-
// Set the output directory for the sites
|
|
9
|
-
sitesOutputDirectory: 'out',
|
|
10
|
-
// These settings control the behavior of the application
|
|
11
|
-
app: {
|
|
12
|
-
// Set the theme for the application
|
|
13
|
-
theme: 'default',
|
|
14
|
-
// Set the title for the application
|
|
15
|
-
title: 'Handoff Design System',
|
|
16
|
-
// Set the client for the application, used in the footer
|
|
17
|
-
client: 'Handoff Technologies',
|
|
18
|
-
// Set the Google Tag Manager ID to add GTM scripts
|
|
19
|
-
google_tag_manager: null,
|
|
20
|
-
// Set the attribution for the application
|
|
21
|
-
attribution: true, // Should we remove this?
|
|
22
|
-
// Set the type copy for the typeography page
|
|
23
|
-
type_copy: 'Almost before we knew it, we had left the ground.',
|
|
24
|
-
// Set the type sort for the typeography page
|
|
25
|
-
type_sort: [
|
|
26
|
-
'Heading 1',
|
|
27
|
-
'Heading 2',
|
|
28
|
-
'Heading 3',
|
|
29
|
-
'Heading 4',
|
|
30
|
-
'Heading 5',
|
|
31
|
-
'Heading 6',
|
|
32
|
-
'Paragraph',
|
|
33
|
-
'Subheading',
|
|
34
|
-
'Blockquote',
|
|
35
|
-
'Input Labels',
|
|
36
|
-
'Link',
|
|
37
|
-
],
|
|
38
|
-
// Set the color sort for the color page
|
|
39
|
-
color_sort: ['primary', 'secondary', 'extra', 'system'],
|
|
40
|
-
// Set the component sort for the component page
|
|
41
|
-
component_sort: ['primary', 'secondary', 'transparent'],
|
|
42
|
-
// Set the base path for the application
|
|
43
|
-
base_path: '',
|
|
44
|
-
// Set the breakpoints for the application
|
|
45
|
-
breakpoints: {
|
|
46
|
-
sm: { size: 576, name: 'Small' },
|
|
47
|
-
md: { size: 768, name: 'Medium' },
|
|
48
|
-
lg: { size: 992, name: 'Large' },
|
|
49
|
-
xl: { size: 1200, name: 'Extra Large' },
|
|
50
|
-
},
|
|
51
|
-
// Set the ports for the application
|
|
52
|
-
ports: { app: 3000, websocket: 3001 },
|
|
53
|
-
},
|
|
54
|
-
// These are the entry points used to compile the target application
|
|
55
|
-
entries: {
|
|
56
|
-
scss: './sass/main.scss',
|
|
57
|
-
js: './js/main.js',
|
|
58
|
-
bundle: './js/main.js',
|
|
59
|
-
//components: ['./test_components'],
|
|
60
|
-
//patterns: ['./test_patterns'],
|
|
61
|
-
},
|
|
62
|
-
// These are the asset zip file download links
|
|
63
|
-
assets_zip_links: {
|
|
64
|
-
icons: null,
|
|
65
|
-
logos: null,
|
|
66
|
-
},
|
|
67
|
-
// Hooks allow you to override the default behavior of the application
|
|
68
|
-
hooks: {
|
|
69
|
-
// Alter the vite css build config
|
|
70
|
-
cssBuildConfig: (config) => {
|
|
71
|
-
return config;
|
|
72
|
-
},
|
|
73
|
-
// Validate each component before it is exported
|
|
74
|
-
validateComponent: (component) => {
|
|
75
|
-
return {};
|
|
76
|
-
},
|
|
77
|
-
// Alter the vite client build config
|
|
78
|
-
clientBuildConfig: (config) => {
|
|
79
|
-
return config;
|
|
80
|
-
},
|
|
81
|
-
// Alter the vite html build config
|
|
82
|
-
htmlBuildConfig: (config) => {
|
|
83
|
-
return config;
|
|
84
|
-
},
|
|
85
|
-
// Alter the vite js build config
|
|
86
|
-
jsBuildConfig: (config) => {
|
|
87
|
-
return config;
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { ComponentListObject } from '@handoff/transformers/preview/types';
|
|
2
|
-
import { Laptop, Search } from 'lucide-react';
|
|
3
|
-
import Link from 'next/link';
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Get the Component List
|
|
8
|
-
* @returns Promise<ComponentListObject[]>
|
|
9
|
-
*/
|
|
10
|
-
export const getComponentList = async (): Promise<ComponentListObject[]> => {
|
|
11
|
-
// Try to load the component from the public json
|
|
12
|
-
let data = await fetch(`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/components.json`).then((res) => res.json());
|
|
13
|
-
return data as ComponentListObject[];
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const ComponentSearch = ({ }) => {
|
|
17
|
-
const [search, setSearch] = React.useState('');
|
|
18
|
-
const [loading, setLoading] = React.useState(true);
|
|
19
|
-
const [list, setList] = React.useState<ComponentListObject[]>([]);
|
|
20
|
-
|
|
21
|
-
const filterList = React.useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => {
|
|
22
|
-
setSearch(event.currentTarget.value.toLowerCase().replace(/[\W_]+/g, ' '));
|
|
23
|
-
// filter list by search
|
|
24
|
-
const filteredList = list.filter((component) => {
|
|
25
|
-
return component.title.toLowerCase().includes(search) || component.description.toLowerCase().includes(search);
|
|
26
|
-
});
|
|
27
|
-
setList(filteredList);
|
|
28
|
-
}, []);
|
|
29
|
-
|
|
30
|
-
React.useEffect(() => {
|
|
31
|
-
getComponentList().then((data) => {
|
|
32
|
-
setList(data);
|
|
33
|
-
setLoading(false);
|
|
34
|
-
});
|
|
35
|
-
}, []);
|
|
36
|
-
|
|
37
|
-
if (loading) {
|
|
38
|
-
return <p>Loading...</p>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!list) {
|
|
42
|
-
return <p>{JSON.stringify(list)}No icons found</p>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<>
|
|
47
|
-
<div className="c-form-element c-form-element--fullwidth c-form-element--big">
|
|
48
|
-
<div className="c-form-element__field">
|
|
49
|
-
<div className="c-form-element__icon">
|
|
50
|
-
<Search />
|
|
51
|
-
</div>
|
|
52
|
-
<input type="text" className="c-form-element__text" placeholder="Search icons..." onChange={filterList} />
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
<p>
|
|
56
|
-
<strong>{list.length}</strong> found
|
|
57
|
-
</p>
|
|
58
|
-
<div className="o-row">
|
|
59
|
-
<div className="o-col-12@md">
|
|
60
|
-
<div className="o-stack-1@md o-stack-1@lg">
|
|
61
|
-
{list.length > 0 ? (
|
|
62
|
-
list.map((component) => <DisplayComponent key={component.id} component={component} />)
|
|
63
|
-
) : (
|
|
64
|
-
<div className="c-search-results">
|
|
65
|
-
<Laptop />
|
|
66
|
-
<h4>No icons found.</h4>
|
|
67
|
-
</div>
|
|
68
|
-
)}
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
</>
|
|
73
|
-
);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const DisplayComponent: React.FC<{ component: ComponentListObject }> = ({ component }) => {
|
|
77
|
-
return (
|
|
78
|
-
<div>
|
|
79
|
-
<Link href={`/components/single/${component.id}`}>
|
|
80
|
-
<div className="c-card c-card--component-preview">
|
|
81
|
-
<div className="o-row">
|
|
82
|
-
<div className="o-col-12@md">
|
|
83
|
-
<h4>{component.title}</h4>
|
|
84
|
-
<p>{component.description}</p>
|
|
85
|
-
</div>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
</Link>
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export default ComponentSearch;
|