handoff-app 2.0.0-8 → 2.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 → CHANGELOG.md} +85 -1
- package/{License.md → LICENSE.md} +1 -1
- package/README.md +816 -393
- package/RECIPES.md +975 -0
- package/UPGRADE.md +57 -9
- package/components.json +20 -20
- package/config/docs/foundations/colors/guidelines.md +0 -1
- package/config/docs/foundations/colors.md +0 -1
- package/config/docs/foundations/effects/guidelines.md +0 -1
- package/config/docs/foundations/effects/resources.md +0 -1
- package/config/docs/foundations/effects.md +0 -1
- package/config/docs/foundations/fonts.md +0 -1
- package/config/docs/foundations/grid.md +0 -1
- package/config/docs/foundations/icon.md +0 -1
- package/config/docs/foundations/icons/guidelines.md +0 -1
- package/config/docs/foundations/icons.md +0 -1
- package/config/docs/foundations/images/guidelines.md +0 -1
- package/config/docs/foundations/images/resources.md +0 -1
- package/config/docs/foundations/images.md +0 -1
- package/config/docs/foundations/logo/resources.md +0 -1
- package/config/docs/foundations/logo.md +0 -1
- package/config/docs/foundations/typography/guidelines.md +0 -1
- package/config/docs/foundations/typography/resources.md +0 -1
- package/config/docs/foundations/typography.md +0 -1
- package/config/docs/foundations.md +0 -1
- package/config/docs/guidelines.md +0 -1
- package/config/docs/index.md +0 -1
- package/config/docs/system/tokens/foundations/colors.md +0 -4
- package/config/docs/system/tokens/foundations/effects.md +0 -4
- package/config/docs/system/tokens/foundations/typography.md +0 -2
- package/config/docs/system.md +0 -2
- package/config/templates/page.md +2 -1
- package/config/templates/page.mdx +2 -1
- package/dist/ai/connections.d.ts +66 -0
- package/dist/ai/connections.js +149 -0
- package/dist/ai/define.d.ts +10 -0
- package/dist/ai/define.js +13 -0
- package/dist/ai/types.d.ts +26 -0
- package/dist/ai/types.js +10 -0
- package/dist/app-builder/build.d.ts +14 -7
- package/dist/app-builder/build.js +195 -66
- package/dist/app-builder/client-config.js +3 -2
- package/dist/app-builder/paths.d.ts +0 -12
- package/dist/app-builder/paths.js +3 -20
- package/dist/app-builder/static-export.js +1 -1
- package/dist/app-builder/vercel-output.d.ts +1 -1
- package/dist/app-builder/vercel-output.js +5 -4
- package/dist/cache/build-cache.d.ts +27 -4
- package/dist/cache/build-cache.js +114 -47
- package/dist/cache/build-inputs.d.ts +13 -0
- package/dist/cache/build-inputs.js +19 -0
- package/dist/cache/index.d.ts +2 -1
- package/dist/cache/index.js +5 -1
- package/dist/catalog/normalize.js +3 -0
- package/dist/catalog/previews.js +5 -4
- package/dist/catalog/types.d.ts +8 -0
- package/dist/cli/create.js +1 -2
- package/dist/commands/build/index.d.ts +1 -1
- package/dist/commands/build/index.js +1 -1
- package/dist/commands/utils.d.ts +3 -3
- package/dist/config/defaults.js +7 -4
- package/dist/config/from-env.d.ts +4 -0
- package/dist/config/from-env.js +38 -1
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +1 -4
- package/dist/config/loader.js +28 -2
- package/dist/config/resolve-env.d.ts +2 -1
- package/dist/config/resolve-env.js +28 -12
- package/dist/config/runtime.js +9 -11
- package/dist/handlebars.d.ts +10 -2
- package/dist/index.d.ts +12 -7
- package/dist/index.js +18 -22
- package/dist/nav/index.d.ts +2 -2
- package/dist/nav/registry.d.ts +3 -3
- package/dist/nav/registry.js +1 -1
- package/dist/nav/workspace.d.ts +3 -3
- package/dist/nav/workspace.js +1 -1
- package/dist/pipeline/figma.d.ts +13 -2
- package/dist/pipeline/figma.js +86 -46
- package/dist/pipeline/styles.js +1 -1
- package/dist/pipeline/validation.d.ts +1 -1
- package/dist/pipeline/validation.js +3 -3
- package/dist/providers.d.ts +10 -0
- package/dist/providers.js +13 -0
- package/dist/registry/asset-storage/define.d.ts +5 -5
- package/dist/registry/asset-storage/define.js +4 -4
- package/dist/registry/asset-storage/resolve.d.ts +9 -19
- package/dist/registry/asset-storage/resolve.js +33 -21
- package/dist/registry/asset-storage/types.d.ts +37 -17
- package/dist/registry/asset-storage/types.js +5 -5
- package/dist/registry/auth/ai-keys.d.ts +32 -0
- package/dist/registry/auth/ai-keys.js +69 -0
- package/dist/registry/auth/crypto.d.ts +17 -0
- package/dist/registry/auth/crypto.js +61 -1
- package/dist/registry/auth/index.d.ts +2 -1
- package/dist/registry/auth/index.js +11 -2
- package/dist/registry/checkout/index.d.ts +2 -1
- package/dist/registry/checkout/index.js +57 -127
- package/dist/registry/client.js +53 -17
- package/dist/registry/db/driver.js +3 -3
- package/dist/registry/db/schema.d.ts +261 -3
- package/dist/registry/db/schema.js +29 -4
- package/dist/registry/email/define.d.ts +10 -0
- package/dist/registry/email/define.js +13 -0
- package/dist/registry/email/providers/smtp.d.ts +10 -0
- package/dist/registry/email/providers/smtp.js +73 -0
- package/dist/registry/email/resolve.d.ts +28 -0
- package/dist/registry/email/resolve.js +74 -0
- package/dist/registry/email/types.d.ts +24 -0
- package/dist/registry/email/types.js +6 -0
- package/dist/registry/path.d.ts +2 -0
- package/dist/registry/path.js +11 -1
- package/dist/registry/publish/package.js +8 -0
- package/dist/registry/transfer.d.ts +4 -0
- package/dist/store/registry.d.ts +3 -3
- package/dist/store/registry.js +4 -4
- package/dist/transformers/plugins/ssr-render.js +40 -15
- package/dist/transformers/preview/component/builder.d.ts +1 -9
- package/dist/transformers/preview/component/builder.js +17 -17
- package/dist/transformers/preview/component/css.d.ts +3 -1
- package/dist/transformers/preview/component/css.js +92 -51
- package/dist/transformers/preview/component/html.d.ts +3 -1
- package/dist/transformers/preview/component/html.js +6 -2
- package/dist/transformers/preview/component/javascript.d.ts +2 -1
- package/dist/transformers/preview/component/javascript.js +10 -4
- package/dist/transformers/preview/pattern/html.js +3 -1
- package/dist/transformers/preview/types.d.ts +13 -0
- package/dist/transformers/utils/build.d.ts +24 -0
- package/dist/transformers/utils/build.js +100 -1
- package/dist/transformers/utils/module.js +2 -1
- package/dist/types/config.d.ts +135 -18
- package/dist/utils/path.d.ts +1 -5
- package/dist/utils/path.js +6 -23
- package/docs/api.md +6 -4
- package/docs/cli.md +275 -54
- package/drizzle/0007_add_ai_keys.sql +11 -0
- package/drizzle/0008_add_source_dir.sql +2 -0
- package/drizzle/meta/0007_snapshot.json +2013 -0
- package/drizzle/meta/0008_snapshot.json +2025 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +19 -8
- package/src/ai/README.md +42 -0
- package/src/ai/connections.ts +189 -0
- package/src/ai/define.ts +15 -0
- package/src/ai/types.ts +30 -0
- package/src/app/components/AiAssistant/AiAssistantPanel.tsx +144 -0
- package/src/app/components/AiAssistant/AiAssistantProvider.tsx +215 -0
- package/src/app/components/AiAssistant/AiConversation.tsx +737 -0
- package/src/app/components/AiAssistant/AiToolResult.tsx +99 -0
- package/src/app/components/AiAssistant/dockState.ts +86 -0
- package/src/app/components/AiAssistant/toolLabel.ts +24 -0
- package/src/app/components/AiAssistant/useAiChat.ts +147 -0
- package/src/app/components/AiAssistant/useAiConnections.ts +86 -0
- package/src/app/components/AiAssistant/usePageContext.ts +63 -0
- package/src/app/components/Auth/AccountLayout.tsx +58 -19
- package/src/app/components/Auth/AuthShell.tsx +27 -33
- package/src/app/components/Auth/RegistryInstallGate.tsx +4 -8
- package/src/app/components/Auth/installStatus.ts +29 -0
- package/src/app/components/Component/PageSliceResolver.tsx +1 -0
- package/src/app/components/Component/Preview.tsx +383 -189
- package/src/app/components/Component/slices/ComponentDisplaySlice.tsx +4 -0
- package/src/app/components/Foundations/ColorGrid.tsx +13 -14
- package/src/app/components/Layout/Header.tsx +21 -15
- package/src/app/components/Layout/Main.tsx +2 -2
- package/src/app/components/Markdown/MarkdownComponents.tsx +30 -32
- package/src/app/components/McpIntegration/McpConfigDialog.tsx +12 -51
- package/src/app/components/Navigation/MobileNav.tsx +2 -2
- package/src/app/components/Navigation/SideNav.tsx +1 -15
- package/src/app/components/context/NavProvider.tsx +7 -3
- package/src/app/components/ui/attachment.tsx +50 -0
- package/src/app/components/ui/bubble.tsx +57 -0
- package/src/app/components/ui/button.tsx +3 -1
- package/src/app/components/ui/confirm-dialog.tsx +87 -0
- package/src/app/components/ui/dialog.tsx +7 -2
- package/src/app/components/ui/marker.tsx +56 -0
- package/src/app/components/ui/sidebar.tsx +3 -1
- package/src/app/components/util/index.ts +3 -12
- package/src/app/css/index.css +64 -0
- package/src/app/lib/ai/auth.ts +33 -0
- package/src/app/lib/ai/context.ts +99 -0
- package/src/app/lib/ai/model.ts +79 -0
- package/src/app/lib/ai/prompt.ts +36 -0
- package/src/app/lib/ai/resolve.ts +97 -0
- package/src/app/lib/ai/tools.ts +120 -0
- package/src/app/lib/api/methods.ts +13 -0
- package/src/app/lib/asset-storage.ts +35 -36
- package/src/app/lib/auth/api.ts +4 -12
- package/src/app/lib/auth/email.ts +96 -23
- package/src/app/lib/docs-api/page-rendering.ts +1 -1
- package/src/app/lib/docs-api/registry.ts +2 -3
- package/src/app/lib/docs-api/runtime-config.ts +47 -21
- package/src/app/lib/mcp/components.ts +523 -0
- package/src/app/lib/mcp/page-url.ts +24 -0
- package/src/app/lib/mcp/pages.ts +84 -0
- package/src/app/lib/mcp/result.ts +33 -0
- package/src/app/lib/mcp/server.ts +39 -318
- package/src/app/lib/mcp/tokens.ts +334 -0
- package/src/app/lib/mcp/tool.ts +19 -0
- package/src/app/lib/registry-api/asset-transfer.ts +119 -11
- package/src/app/lib/registry-api/errors.ts +2 -0
- package/src/app/lib/registry-api/handler.ts +7 -1
- package/src/app/lib/registry-api/transfer.ts +28 -6
- package/src/app/lib/server-module.ts +34 -0
- package/src/app/lib/utils.ts +20 -0
- package/src/app/next.config.mjs +26 -24
- package/src/app/pages/[...slug]/index.tsx +2 -1
- package/src/app/pages/_app.tsx +6 -2
- package/src/app/pages/_document.tsx +6 -0
- package/src/app/pages/account/ai.tsx +183 -0
- package/src/app/pages/account/index.tsx +68 -68
- package/src/app/pages/account/tokens.tsx +37 -7
- package/src/app/pages/account/users.tsx +75 -15
- package/src/app/pages/api/account/ai/keys/[connectionId].ts +60 -0
- package/src/app/pages/api/account/ai/keys/index.ts +20 -0
- package/src/app/pages/api/admin/users/[id]/[action].ts +80 -11
- package/src/app/pages/api/admin/users/index.ts +2 -2
- package/src/app/pages/api/ai/chat.ts +115 -0
- package/src/app/pages/api/ai/connections.ts +23 -0
- package/src/app/pages/api/auth/request-reset.ts +14 -16
- package/src/app/pages/api/docs/nav.json.ts +1 -1
- package/src/app/pages/api/install.ts +3 -3
- package/src/app/pages/api/mcp.ts +6 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash]/complete.ts +11 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash]/upload.ts +12 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +1 -1
- package/src/app/pages/cli/device.tsx +10 -5
- package/src/app/pages/foundations/colors.tsx +9 -8
- package/src/app/pages/foundations/index.tsx +3 -3
- package/src/app/pages/index.tsx +2 -2
- package/src/app/pages/install.tsx +7 -2
- package/src/app/pages/login.tsx +32 -27
- package/src/app/pages/reset-password.tsx +21 -20
- package/src/app/pages/system/component/[component]/index.tsx +2 -5
- package/src/app/pages/system/index.tsx +11 -11
- package/src/app/pages/system/pattern/[pattern]/index.tsx +2 -2
- package/src/app/pages/system/tokens/components/[component]/index.tsx +0 -4
- package/src/app-builder/README.md +2 -1
- package/src/app-builder/build.ts +218 -73
- package/src/app-builder/client-config.ts +3 -2
- package/src/app-builder/paths.ts +3 -22
- package/src/app-builder/static-export.ts +1 -1
- package/src/app-builder/vercel-output.ts +5 -4
- package/src/cache/README.md +4 -1
- package/src/cache/build-cache.ts +128 -43
- package/src/cache/build-inputs.ts +28 -0
- package/src/cache/index.ts +4 -0
- package/src/catalog/README.md +3 -0
- package/src/catalog/normalize.ts +2 -0
- package/src/catalog/previews.ts +6 -3
- package/src/catalog/types.ts +8 -0
- package/src/cli/create.ts +1 -2
- package/src/commands/build/index.ts +1 -1
- package/src/config/README.md +10 -9
- package/src/config/defaults.ts +7 -4
- package/src/config/from-env.ts +33 -0
- package/src/config/helpers.ts +1 -3
- package/src/config/index.ts +0 -3
- package/src/config/loader.ts +27 -3
- package/src/config/resolve-env.ts +28 -15
- package/src/config/runtime.ts +10 -5
- package/src/handlebars.ts +8 -2
- package/src/index.ts +33 -29
- package/src/nav/index.ts +2 -2
- package/src/nav/registry.ts +3 -3
- package/src/nav/workspace.ts +3 -3
- package/src/pipeline/README.md +1 -1
- package/src/pipeline/figma.ts +98 -49
- package/src/pipeline/styles.ts +1 -1
- package/src/pipeline/validation.ts +3 -3
- package/src/providers.ts +18 -0
- package/src/registry/asset-storage/define.ts +6 -6
- package/src/registry/asset-storage/resolve.ts +38 -37
- package/src/registry/asset-storage/types.ts +39 -17
- package/src/registry/auth/ai-keys.ts +62 -0
- package/src/registry/auth/crypto.ts +63 -1
- package/src/registry/auth/index.ts +11 -1
- package/src/registry/checkout/index.ts +60 -138
- package/src/registry/client.ts +60 -20
- package/src/registry/db/driver.ts +2 -2
- package/src/registry/db/schema.ts +49 -22
- package/src/registry/email/define.ts +15 -0
- package/src/registry/email/providers/smtp.ts +39 -0
- package/src/registry/email/resolve.ts +94 -0
- package/src/registry/email/types.ts +28 -0
- package/src/registry/path.ts +11 -0
- package/src/registry/publish/package.ts +9 -0
- package/src/registry/transfer.ts +4 -0
- package/src/store/registry.ts +7 -7
- package/src/transformers/plugins/ssr-render.ts +53 -16
- package/src/transformers/preview/component/builder.ts +19 -25
- package/src/transformers/preview/component/css.ts +101 -53
- package/src/transformers/preview/component/html.ts +6 -2
- package/src/transformers/preview/component/javascript.ts +14 -4
- package/src/transformers/preview/pattern/html.ts +3 -1
- package/src/transformers/preview/types.ts +14 -0
- package/src/transformers/utils/build.ts +93 -0
- package/src/transformers/utils/module.ts +4 -2
- package/src/types/config.ts +148 -20
- package/src/utils/README.md +1 -1
- package/src/utils/path.ts +6 -29
- package/templates/default/handoff.config.js.tpl +10 -2
- package/templates/default/handoff.config.ts.tpl +10 -2
- package/templates/default/handoff.config.with-examples.js.tpl +10 -2
- package/templates/default/handoff.config.with-examples.ts.tpl +10 -2
- package/templates/default/package.json.tpl +2 -2
- package/.editorconfig +0 -19
- package/.eslintrc.json +0 -4
- package/.prettierignore +0 -0
- package/.prettierrc.json +0 -8
- package/.vscode/settings.json +0 -17
- package/dist/config/validator.d.ts +0 -9
- package/dist/config/validator.js +0 -24
- package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +0 -17
- package/dist/registry/asset-storage/adapters/vercel-blob.js +0 -87
- package/src/app/components/old/Header.tsx +0 -89
- package/src/app/lib/mcp/shape.ts +0 -596
- package/src/app/pages/assets/fonts.tsx +0 -129
- package/src/app/pages/assets/index.tsx +0 -91
- package/src/app/pages/assets/logos.tsx +0 -75
- package/src/config/validator.ts +0 -22
- package/src/registry/asset-storage/adapters/vercel-blob.ts +0 -51
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Server-only email provider contract. Handoff renders every message, and a provider only delivers it.
|
|
4
|
+
* SMTP is built in. Anything else is a {@link defineEmailProvider} module.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
package/dist/registry/path.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare const isSafeRelativePath: (value: string) => boolean;
|
|
2
2
|
export declare const normalizeRelativePath: (value: string) => string;
|
|
3
|
+
/** Unlike {@link isSafeRelativePath}, it can start with `../`. */
|
|
4
|
+
export declare const isWorkspaceRelativePath: (value: unknown) => value is string;
|
|
3
5
|
export declare const isSafePathSegment: (value: string) => boolean;
|
|
4
6
|
export declare const resolvePathWithin: (root: string, value: string) => string | null;
|
package/dist/registry/path.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolvePathWithin = exports.isSafePathSegment = exports.normalizeRelativePath = exports.isSafeRelativePath = void 0;
|
|
6
|
+
exports.resolvePathWithin = exports.isSafePathSegment = exports.isWorkspaceRelativePath = exports.normalizeRelativePath = exports.isSafeRelativePath = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const isSafeRelativePath = (value) => {
|
|
9
9
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
@@ -18,6 +18,16 @@ const isSafeRelativePath = (value) => {
|
|
|
18
18
|
exports.isSafeRelativePath = isSafeRelativePath;
|
|
19
19
|
const normalizeRelativePath = (value) => value.replace(/\\/g, '/');
|
|
20
20
|
exports.normalizeRelativePath = normalizeRelativePath;
|
|
21
|
+
/** Unlike {@link isSafeRelativePath}, it can start with `../`. */
|
|
22
|
+
const isWorkspaceRelativePath = (value) => typeof value === 'string' &&
|
|
23
|
+
value !== '' &&
|
|
24
|
+
value !== '.' &&
|
|
25
|
+
!value.includes('\0') &&
|
|
26
|
+
!value.includes('\\') &&
|
|
27
|
+
!value.startsWith('/') &&
|
|
28
|
+
!/^[a-zA-Z]:/.test(value) &&
|
|
29
|
+
path_1.default.posix.normalize(value) === value;
|
|
30
|
+
exports.isWorkspaceRelativePath = isWorkspaceRelativePath;
|
|
21
31
|
const isSafePathSegment = (value) => typeof value === 'string' && value === value.trim() && (0, exports.isSafeRelativePath)(value) && !(0, exports.normalizeRelativePath)(value).includes('/');
|
|
22
32
|
exports.isSafePathSegment = isSafePathSegment;
|
|
23
33
|
const resolvePathWithin = (root, value) => {
|
|
@@ -97,6 +97,12 @@ const enrichItem = (handoff, kind, id, summary) => __awaiter(void 0, void 0, voi
|
|
|
97
97
|
}
|
|
98
98
|
return item;
|
|
99
99
|
});
|
|
100
|
+
const findSourceDir = (handoff, kind, id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
const runtime = kind === 'component' ? yield handoff.store.components.get(id) : yield handoff.store.patterns.get(id);
|
|
102
|
+
const sourceDir = runtime === null || runtime === void 0 ? void 0 : runtime.path;
|
|
103
|
+
const relativeDir = sourceDir ? path_1.default.relative(handoff.workingPath, sourceDir) : '';
|
|
104
|
+
return relativeDir && !path_1.default.isAbsolute(relativeDir) ? relativeDir.split(path_1.default.sep).join('/') : undefined;
|
|
105
|
+
});
|
|
100
106
|
/** Map an entity's related source files to transfer files, dropping workspace-only declarations. */
|
|
101
107
|
const collectSourceFiles = (handoff, kind, id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
108
|
const store = kind === 'component' ? handoff.store.components : kind === 'pattern' ? handoff.store.patterns : handoff.store.pages;
|
|
@@ -164,6 +170,7 @@ const buildComponentPackage = (handoff, id) => __awaiter(void 0, void 0, void 0,
|
|
|
164
170
|
const artifacts = Array.from(byPath.values());
|
|
165
171
|
return {
|
|
166
172
|
item: yield enrichItem(handoff, 'component', id, summary),
|
|
173
|
+
sourceDir: yield findSourceDir(handoff, 'component', id),
|
|
167
174
|
files: yield collectSourceFiles(handoff, 'component', id),
|
|
168
175
|
artifacts,
|
|
169
176
|
build: createArtifactBuild(artifacts),
|
|
@@ -206,6 +213,7 @@ const buildPatternPackage = (handoff, id) => __awaiter(void 0, void 0, void 0, f
|
|
|
206
213
|
const artifacts = Array.from(byPath.values());
|
|
207
214
|
return {
|
|
208
215
|
item: yield enrichItem(handoff, 'pattern', id, summary),
|
|
216
|
+
sourceDir: yield findSourceDir(handoff, 'pattern', id),
|
|
209
217
|
files: yield collectSourceFiles(handoff, 'pattern', id),
|
|
210
218
|
artifacts,
|
|
211
219
|
build: createArtifactBuild(artifacts),
|
|
@@ -59,6 +59,8 @@ export interface TransferBuild {
|
|
|
59
59
|
export interface TransferPackage {
|
|
60
60
|
/** Normalized component, pattern, or page record stored as the served record. */
|
|
61
61
|
item: Record<string, unknown>;
|
|
62
|
+
/** Workspace-relative entity directory, for components and patterns. It can start with `../`. */
|
|
63
|
+
sourceDir?: string;
|
|
62
64
|
files: TransferFile[];
|
|
63
65
|
artifacts: TransferArtifact[];
|
|
64
66
|
build: TransferBuild;
|
|
@@ -71,6 +73,8 @@ export interface CheckoutPayload {
|
|
|
71
73
|
kind: TransferEntityKind;
|
|
72
74
|
/** Normalized record used to reconstruct the entity. */
|
|
73
75
|
item: Record<string, unknown>;
|
|
76
|
+
/** The published {@link TransferPackage.sourceDir}. */
|
|
77
|
+
sourceDir?: string;
|
|
74
78
|
files: TransferFile[];
|
|
75
79
|
}
|
|
76
80
|
/**
|
package/dist/store/registry.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - Reads never materialize source files or run the build pipeline — every record and file is
|
|
14
14
|
* returned exactly as it was published.
|
|
15
15
|
*/
|
|
16
|
-
import type {
|
|
16
|
+
import type { StorageProvider } from '../registry/asset-storage/types';
|
|
17
17
|
import type { RegistryDatabase } from '../registry/db/client';
|
|
18
18
|
import { type AssetStorageProvider } from '../registry/db/schema';
|
|
19
19
|
import type { ComponentListObject, PageListObject, PatternListObject } from '../transformers/preview/types';
|
|
@@ -22,11 +22,11 @@ import type { AssetContentResource, AssetMetadata, AssetStore, ComponentStore, H
|
|
|
22
22
|
export interface RegistryStoreContext {
|
|
23
23
|
db: RegistryDatabase;
|
|
24
24
|
/**
|
|
25
|
-
* Resolve the {@link
|
|
25
|
+
* Resolve the {@link StorageProvider} for a blob's recorded provider (or `null` for the inline
|
|
26
26
|
* `database` provider). Injected by the app runtime so `src/store` stays free of app-side imports;
|
|
27
27
|
* when omitted, only inline DB-backed asset content is resolvable.
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
resolveStorageProvider?: (provider: AssetStorageProvider) => Promise<StorageProvider | null>;
|
|
30
30
|
}
|
|
31
31
|
export declare class RegistryComponentStore implements ComponentStore {
|
|
32
32
|
private readonly context;
|
package/dist/store/registry.js
CHANGED
|
@@ -404,14 +404,14 @@ class RegistryAssetStore {
|
|
|
404
404
|
return Object.assign(Object.assign({}, base), { body: Buffer.from(row.blobContent) });
|
|
405
405
|
}
|
|
406
406
|
// Object-backed - resolve through the provider that stored it.
|
|
407
|
-
if (!row.storageRef || !this.context.
|
|
407
|
+
if (!row.storageRef || !this.context.resolveStorageProvider) {
|
|
408
408
|
return null;
|
|
409
409
|
}
|
|
410
|
-
const
|
|
411
|
-
if (!
|
|
410
|
+
const storage = yield this.context.resolveStorageProvider(row.storageProvider);
|
|
411
|
+
if (!storage) {
|
|
412
412
|
return null;
|
|
413
413
|
}
|
|
414
|
-
const result = yield
|
|
414
|
+
const result = yield storage.get(row.storageRef);
|
|
415
415
|
if (result.kind === 'redirect') {
|
|
416
416
|
return Object.assign(Object.assign({}, base), { redirectUrl: result.url });
|
|
417
417
|
}
|
|
@@ -121,12 +121,25 @@ function loadComponentSchemaAndModule(componentData, componentPath, handoff) {
|
|
|
121
121
|
* @param componentExport - Export holding the implementation; the default export when absent
|
|
122
122
|
* @returns Client-side hydration source code
|
|
123
123
|
*/
|
|
124
|
-
function generateClientHydrationSource(componentId, componentPath, componentExport) {
|
|
124
|
+
function generateClientHydrationSource(componentId, componentPath, componentExport, declarationPath) {
|
|
125
125
|
const importClause = componentExport && componentExport !== 'default' ? `{ ${componentExport} as Component }` : 'Component';
|
|
126
|
+
// A function cannot travel in the JSON props, so the client re-reads `render` from the module that
|
|
127
|
+
// defines it. Conditional, so an unaffected component does not ship its declaration's imports.
|
|
128
|
+
const declarationImport = declarationPath ? `import * as previewDeclaration from '${(0, vite_1.normalizePath)(declarationPath)}';` : '';
|
|
129
|
+
const resolveComponent = declarationPath
|
|
130
|
+
? `(mount) => {
|
|
131
|
+
const previewKey = mount.getAttribute('data-handoff-preview');
|
|
132
|
+
const render = previewKey ? previewDeclaration[previewKey]?.render : undefined;
|
|
133
|
+
return typeof render === 'function' ? render : Component;
|
|
134
|
+
}`
|
|
135
|
+
: '() => Component';
|
|
126
136
|
return `
|
|
127
137
|
import React from 'react';
|
|
128
138
|
import { hydrateRoot } from 'react-dom/client';
|
|
129
139
|
import ${importClause} from '${(0, vite_1.normalizePath)(componentPath)}';
|
|
140
|
+
${declarationImport}
|
|
141
|
+
|
|
142
|
+
const resolveComponent = ${resolveComponent};
|
|
130
143
|
|
|
131
144
|
const parseProps = (propsId) => {
|
|
132
145
|
const raw = propsId ? document.getElementById(propsId)?.textContent : '{}';
|
|
@@ -139,12 +152,14 @@ function generateClientHydrationSource(componentId, componentPath, componentExpo
|
|
|
139
152
|
|
|
140
153
|
const standaloneRoot = document.getElementById('${PLUGIN_CONSTANTS.ROOT_ELEMENT_ID}');
|
|
141
154
|
if (standaloneRoot && !standaloneRoot.hasAttribute('data-handoff-component')) {
|
|
142
|
-
|
|
155
|
+
const StandaloneComponent = resolveComponent(standaloneRoot);
|
|
156
|
+
hydrateRoot(standaloneRoot, <StandaloneComponent {...parseProps('${PLUGIN_CONSTANTS.PROPS_SCRIPT_ID}')} />);
|
|
143
157
|
}
|
|
144
158
|
|
|
145
159
|
const patternMounts = document.querySelectorAll('[data-handoff-component=' + JSON.stringify('${componentId}') + ']');
|
|
146
160
|
patternMounts.forEach((mount) => {
|
|
147
|
-
|
|
161
|
+
const MountComponent = resolveComponent(mount);
|
|
162
|
+
hydrateRoot(mount, <MountComponent {...parseProps(mount.getAttribute('data-handoff-props'))} />);
|
|
148
163
|
});
|
|
149
164
|
`;
|
|
150
165
|
}
|
|
@@ -156,11 +171,11 @@ function generateClientHydrationSource(componentId, componentPath, componentExpo
|
|
|
156
171
|
* artifact cannot be loaded.
|
|
157
172
|
* @param componentId - Component identifier
|
|
158
173
|
* @param previewTitle - Title for the preview
|
|
159
|
-
* @param renderedHtml - Server-rendered HTML
|
|
174
|
+
* @param renderedHtml - Server-rendered HTML, embedded verbatim so the client can hydrate it
|
|
160
175
|
* @param props - Component props as JSON
|
|
161
176
|
* @returns Complete HTML document
|
|
162
177
|
*/
|
|
163
|
-
function generateHtmlDocument(componentId, previewTitle, renderedHtml, props, sharedArtifacts, componentArtifacts) {
|
|
178
|
+
function generateHtmlDocument(componentId, previewKey, previewTitle, renderedHtml, props, sharedArtifacts, componentArtifacts) {
|
|
164
179
|
var _a;
|
|
165
180
|
const basePath = (_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : '';
|
|
166
181
|
const clientArtifactUrl = (0, url_1.buildArtifactUrl)(clientArtifactPath(componentId), basePath);
|
|
@@ -193,7 +208,7 @@ function generateHtmlDocument(componentId, previewTitle, renderedHtml, props, sh
|
|
|
193
208
|
<title>${previewTitle}</title>
|
|
194
209
|
</head>
|
|
195
210
|
<body>
|
|
196
|
-
<div id="${PLUGIN_CONSTANTS.ROOT_ELEMENT_ID}">${renderedHtml}</div>
|
|
211
|
+
<div id="${PLUGIN_CONSTANTS.ROOT_ELEMENT_ID}" data-handoff-preview="${previewKey}">${renderedHtml}</div>
|
|
197
212
|
</body>
|
|
198
213
|
</html>`;
|
|
199
214
|
}
|
|
@@ -224,7 +239,7 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
|
|
|
224
239
|
},
|
|
225
240
|
generateBundle(_, bundle) {
|
|
226
241
|
return __awaiter(this, void 0, void 0, function* () {
|
|
227
|
-
var _a, _b;
|
|
242
|
+
var _a, _b, _c, _d, _e;
|
|
228
243
|
// Remove all JS chunks to prevent conflicts
|
|
229
244
|
for (const [fileName, chunkInfo] of Object.entries(bundle)) {
|
|
230
245
|
if (chunkInfo.type === 'chunk' && fileName.includes(PLUGIN_CONSTANTS.SCRIPT_ID)) {
|
|
@@ -275,15 +290,19 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
|
|
|
275
290
|
// `component/<id>.client.js` artifact. The hydration source only imports the component and
|
|
276
291
|
// reads props from the in-document `__APP_PROPS__` element, so it is identical across every
|
|
277
292
|
// preview of this component.
|
|
278
|
-
const
|
|
293
|
+
const declarationPath = Object.values((_a = componentData.previews) !== null && _a !== void 0 ? _a : {}).some((preview) => typeof preview.render === 'function')
|
|
294
|
+
? (_b = componentData.entries) === null || _b === void 0 ? void 0 : _b.declaration
|
|
295
|
+
: undefined;
|
|
296
|
+
const clientHydrationSource = generateClientHydrationSource(componentId, componentPath, componentData.componentExport, declarationPath);
|
|
279
297
|
const clientBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_CLIENT_BUILD_CONFIG), { logLevel: 'silent', stdin: {
|
|
280
298
|
contents: clientHydrationSource,
|
|
281
299
|
resolveDir: process.cwd(),
|
|
282
300
|
loader: 'tsx',
|
|
283
301
|
}, plugins: [(0, build_1.createReactResolvePlugin)(handoff.workingPath, handoff.modulePath)] });
|
|
284
|
-
const
|
|
302
|
+
const hookedClientBuildConfig = ((_d = (_c = handoff.config) === null || _c === void 0 ? void 0 : _c.hooks) === null || _d === void 0 ? void 0 : _d.clientBuildConfig)
|
|
285
303
|
? handoff.config.hooks.clientBuildConfig(clientBuildConfig)
|
|
286
304
|
: clientBuildConfig;
|
|
305
|
+
const finalClientBuildConfig = (0, build_1.withStyleLoaders)(hookedClientBuildConfig);
|
|
287
306
|
let clientBundleJs;
|
|
288
307
|
try {
|
|
289
308
|
const bundledClient = yield esbuild_1.default.build(finalClientBuildConfig);
|
|
@@ -315,11 +334,17 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
|
|
|
315
334
|
const componentArtifacts = (0, shared_artifacts_1.resolveComponentArtifactPresence)(handoff, componentId);
|
|
316
335
|
// Generate previews for each variation
|
|
317
336
|
for (const previewKey in componentData.previews) {
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
337
|
+
const preview = componentData.previews[previewKey];
|
|
338
|
+
const previewProps = preview.values;
|
|
339
|
+
// A synthetic pattern preview is not a named export, so the client looks up its
|
|
340
|
+
// `sourcePreview` instead. A preview without one has no `render` either.
|
|
341
|
+
const hydrationPreviewKey = (_e = preview.sourcePreview) !== null && _e !== void 0 ? _e : previewKey;
|
|
342
|
+
// Rendered as a component rather than called, so a preview can use hooks and own state.
|
|
343
|
+
const PreviewComponent = (typeof preview.render === 'function' ? preview.render : ReactComponent);
|
|
344
|
+
// The markup must reach the document exactly as rendered. Formatting it adds text nodes React
|
|
345
|
+
// does not expect, so it discards the server markup and re-renders the preview on the client.
|
|
346
|
+
const serverRenderedHtml = server_1.default.renderToString(react_1.default.createElement(PreviewComponent, previewProps));
|
|
347
|
+
finalHtml = generateHtmlDocument(componentId, hydrationPreviewKey, preview.title, serverRenderedHtml, previewProps, sharedArtifacts, componentArtifacts);
|
|
323
348
|
// Emit preview files
|
|
324
349
|
this.emitFile({
|
|
325
350
|
type: 'asset',
|
|
@@ -340,7 +365,7 @@ function ssrRenderPlugin(componentData, documentationComponents, handoff) {
|
|
|
340
365
|
templateFileName: path_1.default.basename(componentPath),
|
|
341
366
|
});
|
|
342
367
|
}
|
|
343
|
-
//
|
|
368
|
+
// The previews are already emitted, so this formats only the docs code view.
|
|
344
369
|
finalHtml = yield (0, html_1.formatHtml)(finalHtml);
|
|
345
370
|
componentData.format = 'react';
|
|
346
371
|
componentData.preview = '';
|
|
@@ -9,20 +9,12 @@ export declare enum ComponentSegment {
|
|
|
9
9
|
Previews = "previews",
|
|
10
10
|
Validation = "validation"
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Options for processing components
|
|
14
|
-
*/
|
|
15
|
-
export interface ProcessComponentsOptions {
|
|
16
|
-
/** Enable caching to skip unchanged components */
|
|
17
|
-
useCache?: boolean;
|
|
18
|
-
}
|
|
19
12
|
/**
|
|
20
13
|
* Process components and generate their code, styles, and previews
|
|
21
14
|
* @param handoff - The Handoff instance containing configuration and state
|
|
22
15
|
* @param id - Optional component ID to process a specific component
|
|
23
16
|
* @param segmentToProcess - Optional segment to update
|
|
24
|
-
* @param options - Optional processing options including cache settings
|
|
25
17
|
* @returns Promise resolving to an array of processed components
|
|
26
18
|
*/
|
|
27
|
-
export declare function processComponents(handoff: Handoff, id?: string, segmentToProcess?: ComponentSegment
|
|
19
|
+
export declare function processComponents(handoff: Handoff, id?: string, segmentToProcess?: ComponentSegment): Promise<ComponentListObject[]>;
|
|
28
20
|
export default processComponents;
|
|
@@ -153,12 +153,11 @@ const createComponentBuildPlan = (segmentToProcess, existingData) => {
|
|
|
153
153
|
* @param handoff - The Handoff instance containing configuration and state
|
|
154
154
|
* @param id - Optional component ID to process a specific component
|
|
155
155
|
* @param segmentToProcess - Optional segment to update
|
|
156
|
-
* @param options - Optional processing options including cache settings
|
|
157
156
|
* @returns Promise resolving to an array of processed components
|
|
158
157
|
*/
|
|
159
|
-
function processComponents(handoff, id, segmentToProcess
|
|
158
|
+
function processComponents(handoff, id, segmentToProcess) {
|
|
160
159
|
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
-
var _a, _b, _c, _d;
|
|
160
|
+
var _a, _b, _c, _d, _e;
|
|
162
161
|
const result = [];
|
|
163
162
|
const documentationObject = yield handoff.getDocumentationObject();
|
|
164
163
|
const components = (_a = documentationObject === null || documentationObject === void 0 ? void 0 : documentationObject.components) !== null && _a !== void 0 ? _a : {};
|
|
@@ -177,17 +176,16 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
177
176
|
yield (0, artifacts_1.syncComponentArtifacts)(handoff, [id]);
|
|
178
177
|
return [];
|
|
179
178
|
}
|
|
180
|
-
// Determine which components need building based on cache
|
|
179
|
+
// Determine which components need building based on cache
|
|
181
180
|
let componentsToBuild;
|
|
182
181
|
let cache = null;
|
|
183
182
|
let currentGlobalDeps = {};
|
|
184
183
|
const componentFileStatesMap = new Map();
|
|
185
184
|
// Only use caching when:
|
|
186
|
-
// - useCache option is enabled
|
|
187
185
|
// - No specific component ID is requested (full build scenario)
|
|
188
186
|
// - No specific segment is requested (full build scenario)
|
|
189
187
|
// - Force flag is not set
|
|
190
|
-
const shouldUseCache =
|
|
188
|
+
const shouldUseCache = !id && !segmentToProcess && !handoff.force;
|
|
191
189
|
if (shouldUseCache) {
|
|
192
190
|
logger_1.Logger.debug('Loading build cache...');
|
|
193
191
|
cache = yield (0, cache_1.loadBuildCache)(handoff);
|
|
@@ -209,7 +207,7 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
209
207
|
logger_1.Logger.info(`Component '${componentId}': new component, will build`);
|
|
210
208
|
componentsToBuild.add(componentId);
|
|
211
209
|
}
|
|
212
|
-
else if ((0, cache_1.hasComponentChanged)(cachedEntry, currentFileStates)) {
|
|
210
|
+
else if (yield (0, cache_1.hasComponentChanged)(cachedEntry, currentFileStates)) {
|
|
213
211
|
logger_1.Logger.info(`Component '${componentId}': source files changed, will rebuild`);
|
|
214
212
|
componentsToBuild.add(componentId);
|
|
215
213
|
}
|
|
@@ -329,6 +327,7 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
329
327
|
}
|
|
330
328
|
// Components should always have at least one preview variation.
|
|
331
329
|
ensureDefaultPreview(data);
|
|
330
|
+
const inputs = shouldUseCache ? (0, cache_1.createBuildInputs)() : undefined;
|
|
332
331
|
const runsViteBuildSteps = buildPlan.js || buildPlan.css || buildPlan.previews;
|
|
333
332
|
let viteBuildStartedAtMs = 0;
|
|
334
333
|
if (runsViteBuildSteps) {
|
|
@@ -337,15 +336,15 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
337
336
|
}
|
|
338
337
|
// Build JS if needed (new build, validation missing, or explicit segment request).
|
|
339
338
|
if (buildPlan.js) {
|
|
340
|
-
data = yield (0, javascript_1.default)(data, handoff);
|
|
339
|
+
data = yield (0, javascript_1.default)(data, handoff, inputs);
|
|
341
340
|
}
|
|
342
341
|
// Build CSS if needed.
|
|
343
342
|
if (buildPlan.css) {
|
|
344
|
-
data = yield (0, css_1.default)(data, handoff);
|
|
343
|
+
data = yield (0, css_1.default)(data, handoff, inputs);
|
|
345
344
|
}
|
|
346
345
|
// Build previews (HTML, snapshots, etc) if needed.
|
|
347
346
|
if (buildPlan.previews) {
|
|
348
|
-
data = yield (0, html_1.default)(Object.assign(Object.assign({}, data), { previews: getBuildPreviews(data) }), handoff, components);
|
|
347
|
+
data = yield (0, html_1.default)(Object.assign(Object.assign({}, data), { previews: getBuildPreviews(data) }), handoff, components, inputs);
|
|
349
348
|
}
|
|
350
349
|
if (runsViteBuildSteps) {
|
|
351
350
|
const elapsedMs = Date.now() - viteBuildStartedAtMs;
|
|
@@ -371,18 +370,19 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
371
370
|
// Add to the cumulative results, to later update the global components summary file.
|
|
372
371
|
result.push(summary);
|
|
373
372
|
// Update cache entry for this component after successful build
|
|
374
|
-
if (shouldUseCache) {
|
|
373
|
+
if (shouldUseCache && inputs) {
|
|
375
374
|
if (!cache) {
|
|
376
375
|
cache = (0, cache_1.createEmptyCache)();
|
|
377
376
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
377
|
+
// Compute file states if not already computed (e.g., when global deps changed)
|
|
378
|
+
const fileStates = (_e = componentFileStatesMap.get(runtimeComponentId)) !== null && _e !== void 0 ? _e : (yield (0, cache_1.computeComponentFileStates)(handoff, runtimeComponentId));
|
|
379
|
+
const inputStates = yield (0, cache_1.computeBuildInputStates)(handoff, runtimeComponentId, inputs);
|
|
380
|
+
if (inputStates) {
|
|
381
|
+
(0, cache_1.updateComponentCacheEntry)(cache, runtimeComponentId, fileStates, inputStates);
|
|
381
382
|
}
|
|
382
383
|
else {
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
(0, cache_1.updateComponentCacheEntry)(cache, runtimeComponentId, computedFileStates);
|
|
384
|
+
// An incomplete list can miss a changed file, so the next run builds this component again.
|
|
385
|
+
delete cache.components[runtimeComponentId];
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { BuildInputs } from '../../../cache/build-inputs';
|
|
1
2
|
import Handoff from '../../../index';
|
|
2
3
|
import { TransformComponentTokensResult } from '../types';
|
|
3
4
|
export declare const MAIN_COMPONENT_CSS_FILE = "main.css";
|
|
4
5
|
export declare const SHARED_COMPONENT_CSS_FILE = "shared.css";
|
|
5
|
-
declare const buildComponentCss: (data: TransformComponentTokensResult, handoff: Handoff) => Promise<{
|
|
6
|
+
declare const buildComponentCss: (data: TransformComponentTokensResult, handoff: Handoff, inputs?: BuildInputs) => Promise<{
|
|
6
7
|
id: string;
|
|
7
8
|
type?: import("../types").ComponentType;
|
|
8
9
|
image?: string;
|
|
@@ -42,6 +43,7 @@ declare const buildComponentCss: (data: TransformComponentTokensResult, handoff:
|
|
|
42
43
|
story?: string;
|
|
43
44
|
schema?: string;
|
|
44
45
|
templates?: string;
|
|
46
|
+
declaration?: string;
|
|
45
47
|
};
|
|
46
48
|
renderer?: import("../../../index").RendererKind;
|
|
47
49
|
sourceFormat?: import("../../../index").SourceFormat;
|
|
@@ -16,8 +16,10 @@ exports.buildMainCss = exports.SHARED_COMPONENT_CSS_FILE = exports.MAIN_COMPONEN
|
|
|
16
16
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const vite_1 = require("vite");
|
|
19
|
+
const build_inputs_1 = require("../../../cache/build-inputs");
|
|
19
20
|
const duration_1 = require("../../../utils/duration");
|
|
20
21
|
const logger_1 = require("../../../utils/logger");
|
|
22
|
+
const build_1 = require("../../utils/build");
|
|
21
23
|
const vite_config_1 = __importDefault(require("../../vite-config"));
|
|
22
24
|
const component_1 = require("../component");
|
|
23
25
|
const { pathToFileURL } = require('url');
|
|
@@ -32,8 +34,9 @@ exports.SHARED_COMPONENT_CSS_FILE = 'shared.css';
|
|
|
32
34
|
* @param options.outputFilename - The name of the output file
|
|
33
35
|
* @param options.loadPaths - Array of paths for SASS to look for imports
|
|
34
36
|
* @param options.handoff - The Handoff configuration object
|
|
37
|
+
* @param options.inputs - Collects the files that the build reads, for the build cache
|
|
35
38
|
*/
|
|
36
|
-
const buildCssBundle = (_a) => __awaiter(void 0, [_a], void 0, function* ({ entry, outputPath, outputFilename, loadPaths, handoff, }) {
|
|
39
|
+
const buildCssBundle = (_a) => __awaiter(void 0, [_a], void 0, function* ({ entry, outputPath, outputFilename, loadPaths, handoff, inputs, }) {
|
|
37
40
|
var _b, _c;
|
|
38
41
|
// Store the current NODE_ENV value
|
|
39
42
|
const oldNodeEnv = process.env.NODE_ENV;
|
|
@@ -72,7 +75,7 @@ const buildCssBundle = (_a) => __awaiter(void 0, [_a], void 0, function* ({ entr
|
|
|
72
75
|
if ((_c = (_b = handoff === null || handoff === void 0 ? void 0 : handoff.config) === null || _b === void 0 ? void 0 : _b.hooks) === null || _c === void 0 ? void 0 : _c.cssBuildConfig) {
|
|
73
76
|
viteConfig = handoff.config.hooks.cssBuildConfig(viteConfig);
|
|
74
77
|
}
|
|
75
|
-
yield (0, vite_1.build)(viteConfig);
|
|
78
|
+
yield (0, vite_1.build)((0, build_inputs_1.withBuildInputs)(viteConfig, inputs));
|
|
76
79
|
}
|
|
77
80
|
catch (e) {
|
|
78
81
|
logger_1.Logger.error(`Failed to build CSS for "${entry}"`);
|
|
@@ -88,14 +91,49 @@ const buildCssBundle = (_a) => __awaiter(void 0, [_a], void 0, function* ({ entr
|
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
});
|
|
91
|
-
const
|
|
94
|
+
const STYLE_SOURCE_EXTENSIONS = new Set(['.scss', '.sass', '.css']);
|
|
95
|
+
/**
|
|
96
|
+
* The stylesheets of one item, in cascade order and deduplicated by resolved path.
|
|
97
|
+
*
|
|
98
|
+
* Discovered imports come first and the declared entry last, so a declared entry can override what
|
|
99
|
+
* the component imports. A project that both declares and imports one file must build it once.
|
|
100
|
+
*/
|
|
101
|
+
const resolveStyleSources = (data, handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
|
+
var _a, _b;
|
|
103
|
+
const template = (_a = data.entries) === null || _a === void 0 ? void 0 : _a.template;
|
|
104
|
+
const discovered = template ? yield (0, build_1.collectStyleImports)(path_1.default.resolve(template), handoff) : [];
|
|
105
|
+
const declared = ((_b = data.entries) === null || _b === void 0 ? void 0 : _b.scss) ? [path_1.default.resolve(data.entries.scss)] : [];
|
|
106
|
+
const ordered = [];
|
|
107
|
+
const seen = new Set();
|
|
108
|
+
for (const source of [...discovered, ...declared]) {
|
|
109
|
+
if (!seen.has(source) && STYLE_SOURCE_EXTENSIONS.has(path_1.default.extname(source)) && fs_extra_1.default.existsSync(source)) {
|
|
110
|
+
seen.add(source);
|
|
111
|
+
ordered.push(source);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return ordered;
|
|
115
|
+
});
|
|
116
|
+
/**
|
|
117
|
+
* A single Sass entry that `@use`s every source in order.
|
|
118
|
+
*
|
|
119
|
+
* `@use` emits a repeated file once and inlines a plain `.css` source, which `@import` would leave
|
|
120
|
+
* as an unresolvable URL. Namespaces are explicit because two sources sharing a basename would
|
|
121
|
+
* claim the same default namespace and fail the compile.
|
|
122
|
+
*/
|
|
123
|
+
const writeCombinedStyleEntry = (sources, outputPath, id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
|
+
const entryPath = path_1.default.resolve(outputPath, `.${id}.entry.scss`);
|
|
125
|
+
const rules = sources.map((source, index) => `@use '${source.split(path_1.default.sep).join('/')}' as ns${index};`);
|
|
126
|
+
yield fs_extra_1.default.outputFile(entryPath, `${rules.join('\n')}\n`);
|
|
127
|
+
return entryPath;
|
|
128
|
+
});
|
|
129
|
+
const buildComponentCss = (data, handoff, inputs) => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
130
|
var _a, _b, _c;
|
|
93
131
|
const id = data.id;
|
|
94
132
|
logger_1.Logger.debug(`buildComponentCss`, id);
|
|
95
133
|
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
96
134
|
const builtCssPath = path_1.default.resolve(outputPath, `${id}.css`);
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
135
|
+
const sources = yield resolveStyleSources(data, handoff);
|
|
136
|
+
if (sources.length === 0) {
|
|
99
137
|
// Keep generated output aligned with the current component declaration.
|
|
100
138
|
yield fs_extra_1.default.remove(builtCssPath);
|
|
101
139
|
delete data['css'];
|
|
@@ -103,59 +141,62 @@ const buildComponentCss = (data, handoff) => __awaiter(void 0, void 0, void 0, f
|
|
|
103
141
|
delete data['sharedStyles'];
|
|
104
142
|
return data;
|
|
105
143
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
144
|
+
// A single source compiles directly, so its relative asset URLs and documented Sass stay as authored.
|
|
145
|
+
const combinedEntry = sources.length > 1 ? yield writeCombinedStyleEntry(sources, outputPath, id) : undefined;
|
|
146
|
+
const entry = combinedEntry !== null && combinedEntry !== void 0 ? combinedEntry : sources[0];
|
|
110
147
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
delete data['css'];
|
|
151
|
-
delete data['sharedStyles'];
|
|
148
|
+
// Remove the previous artifact before rebuilding so "no emitted CSS"
|
|
149
|
+
// doesn't silently preserve stale output from an earlier build.
|
|
150
|
+
yield fs_extra_1.default.remove(builtCssPath);
|
|
151
|
+
// Read the original source
|
|
152
|
+
const primarySource = ((_a = data.entries) === null || _a === void 0 ? void 0 : _a.scss) ? path_1.default.resolve(data.entries.scss) : sources[0];
|
|
153
|
+
if (path_1.default.extname(primarySource) === '.scss') {
|
|
154
|
+
data['sass'] = yield fs_extra_1.default.readFile(primarySource, 'utf8');
|
|
155
|
+
}
|
|
156
|
+
// Setup SASS load paths
|
|
157
|
+
const loadPaths = [
|
|
158
|
+
path_1.default.resolve(handoff.workingPath),
|
|
159
|
+
handoff.getOutputPath(),
|
|
160
|
+
path_1.default.resolve(handoff.workingPath, 'node_modules'),
|
|
161
|
+
];
|
|
162
|
+
if ((_c = (_b = handoff.runtimeConfig) === null || _b === void 0 ? void 0 : _b.entries) === null || _c === void 0 ? void 0 : _c.scss) {
|
|
163
|
+
loadPaths.unshift(path_1.default.dirname(handoff.runtimeConfig.entries.scss));
|
|
164
|
+
}
|
|
165
|
+
yield buildCssBundle({
|
|
166
|
+
entry,
|
|
167
|
+
outputPath,
|
|
168
|
+
outputFilename: `${id}.css`,
|
|
169
|
+
loadPaths,
|
|
170
|
+
handoff,
|
|
171
|
+
inputs,
|
|
172
|
+
});
|
|
173
|
+
// Read the built CSS
|
|
174
|
+
if (fs_extra_1.default.existsSync(builtCssPath)) {
|
|
175
|
+
const builtCss = yield fs_extra_1.default.readFile(builtCssPath, 'utf8');
|
|
176
|
+
data['css'] = builtCss;
|
|
177
|
+
// Handle shared styles — if the component SCSS uses the split marker,
|
|
178
|
+
// extract shared styles from the portion above it
|
|
179
|
+
const splitCSS = builtCss.split('/* COMPONENT STYLES*/');
|
|
180
|
+
if (splitCSS && splitCSS.length > 1) {
|
|
181
|
+
data['css'] = splitCSS[1];
|
|
182
|
+
data['sharedStyles'] = splitCSS[0];
|
|
183
|
+
yield fs_extra_1.default.writeFile(path_1.default.resolve(outputPath, exports.SHARED_COMPONENT_CSS_FILE), data['sharedStyles']);
|
|
152
184
|
}
|
|
153
185
|
}
|
|
186
|
+
else {
|
|
187
|
+
delete data['css'];
|
|
188
|
+
delete data['sharedStyles'];
|
|
189
|
+
}
|
|
154
190
|
}
|
|
155
191
|
catch (e) {
|
|
156
192
|
logger_1.Logger.error(`Failed to build CSS for "${id}"`);
|
|
157
193
|
throw e;
|
|
158
194
|
}
|
|
195
|
+
finally {
|
|
196
|
+
if (combinedEntry) {
|
|
197
|
+
yield fs_extra_1.default.remove(combinedEntry);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
159
200
|
return data;
|
|
160
201
|
});
|
|
161
202
|
/**
|
|
@@ -179,7 +220,7 @@ const buildMainCss = (handoff) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
179
220
|
const loadPaths = [
|
|
180
221
|
path_1.default.dirname(scssEntry),
|
|
181
222
|
path_1.default.resolve(handoff.workingPath),
|
|
182
|
-
|
|
223
|
+
handoff.getOutputPath(),
|
|
183
224
|
path_1.default.resolve(handoff.workingPath, 'node_modules'),
|
|
184
225
|
];
|
|
185
226
|
yield buildCssBundle({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Types as CoreTypes } from 'handoff-core';
|
|
2
|
+
import { BuildInputs } from '../../../cache/build-inputs';
|
|
2
3
|
import Handoff from '../../../index';
|
|
3
4
|
import { TransformComponentTokensResult } from '../types';
|
|
4
5
|
/**
|
|
@@ -7,6 +8,7 @@ import { TransformComponentTokensResult } from '../types';
|
|
|
7
8
|
* @param data - The result of transforming component tokens.
|
|
8
9
|
* @param handoff - The Handoff configuration object.
|
|
9
10
|
* @param components - Optional file components object.
|
|
11
|
+
* @param inputs - Collects the files that the build reads, for the build cache.
|
|
10
12
|
* @returns A promise that resolves to the transformed component tokens result.
|
|
11
13
|
* @throws Will throw an error if the Vite build process fails.
|
|
12
14
|
*
|
|
@@ -15,5 +17,5 @@ import { TransformComponentTokensResult } from '../types';
|
|
|
15
17
|
* const result = await buildPreviews(transformedData, handoffConfig, fileComponents);
|
|
16
18
|
* ```
|
|
17
19
|
*/
|
|
18
|
-
export declare const buildPreviews: (data: TransformComponentTokensResult, handoff: Handoff, components?: CoreTypes.IDocumentationObject["components"]) => Promise<TransformComponentTokensResult>;
|
|
20
|
+
export declare const buildPreviews: (data: TransformComponentTokensResult, handoff: Handoff, components?: CoreTypes.IDocumentationObject["components"], inputs?: BuildInputs) => Promise<TransformComponentTokensResult>;
|
|
19
21
|
export default buildPreviews;
|