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
|
@@ -165,6 +165,7 @@ const buildComponentCss = (data, handoff) => __awaiter(void 0, void 0, void 0, f
|
|
|
165
165
|
const buildMainCss = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
166
166
|
var _a, _b, _c;
|
|
167
167
|
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
168
|
+
const mainCssPath = path_1.default.resolve(outputPath, exports.MAIN_COMPONENT_CSS_FILE);
|
|
168
169
|
const runtimeConfig = (0, config_1.initRuntimeConfig)(handoff)[0];
|
|
169
170
|
if (((_a = runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.entries) === null || _a === void 0 ? void 0 : _a.scss) && fs_extra_1.default.existsSync(runtimeConfig.entries.scss)) {
|
|
170
171
|
const stat = yield fs_extra_1.default.stat(runtimeConfig.entries.scss);
|
|
@@ -172,6 +173,9 @@ const buildMainCss = (handoff) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
172
173
|
if (entryPath === runtimeConfig.entries.scss || fs_extra_1.default.existsSync(entryPath)) {
|
|
173
174
|
logger_1.Logger.info(`Building styles for global entry (${exports.MAIN_COMPONENT_CSS_FILE})…`);
|
|
174
175
|
const startedAt = Date.now();
|
|
176
|
+
// Drop the previous artifact so a failed/empty rebuild cannot preserve stale global CSS that
|
|
177
|
+
// would still be referenced as current.
|
|
178
|
+
yield fs_extra_1.default.remove(mainCssPath);
|
|
175
179
|
try {
|
|
176
180
|
const loadPaths = [
|
|
177
181
|
path_1.default.resolve(handoff.workingPath),
|
|
@@ -191,10 +195,14 @@ const buildMainCss = (handoff) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
191
195
|
logger_1.Logger.info(`Finished building styles for global entry (${exports.MAIN_COMPONENT_CSS_FILE}) in ${(0, duration_1.formatDurationMs)(Date.now() - startedAt)}`);
|
|
192
196
|
}
|
|
193
197
|
catch (_d) {
|
|
194
|
-
// buildCssBundle already logs failure for the entry path
|
|
198
|
+
// buildCssBundle already logs failure for the entry path; ensure no stale artifact remains.
|
|
199
|
+
yield fs_extra_1.default.remove(mainCssPath);
|
|
195
200
|
}
|
|
196
201
|
}
|
|
202
|
+
return;
|
|
197
203
|
}
|
|
204
|
+
// No usable global SCSS entry: drop any stale artifact so it is not referenced as current.
|
|
205
|
+
yield fs_extra_1.default.remove(mainCssPath);
|
|
198
206
|
});
|
|
199
207
|
exports.buildMainCss = buildMainCss;
|
|
200
208
|
exports.default = buildComponentCss;
|
|
@@ -12,10 +12,16 @@ export declare const MAIN_COMPONENT_JS_FILE = "main.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export declare const buildComponentJs: (data: TransformComponentTokensResult, handoff: Handoff) => Promise<TransformComponentTokensResult>;
|
|
14
14
|
/**
|
|
15
|
-
* Builds the
|
|
15
|
+
* Builds the global preview script artifact (`component/main.js`) from the configured workspace JS
|
|
16
|
+
* entry.
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* The artifact is optional: when no global JS entry is configured, or the configured entry no longer
|
|
19
|
+
* exists, or the build fails/produces no output, any stale `main.js` is removed so a failed rebuild
|
|
20
|
+
* never leaves global JS that appears current. Generated HTML references `main.js` only when this
|
|
21
|
+
* function leaves a fresh artifact on disk, so an omitted artifact yields no dangling reference.
|
|
22
|
+
*
|
|
23
|
+
* Output uses the browser-compatible IIFE format so the artifact can load as a classic script ahead
|
|
24
|
+
* of dependent component scripts while preserving intentional top-level side effects.
|
|
19
25
|
*
|
|
20
26
|
* @param handoff - The Handoff configuration object containing build settings
|
|
21
27
|
* @returns A Promise that resolves when the build process is complete
|
|
@@ -30,7 +30,7 @@ exports.MAIN_COMPONENT_JS_FILE = 'main.js';
|
|
|
30
30
|
* @param options.outputPath - The directory where the bundle will be output
|
|
31
31
|
* @param options.outputFilename - The name of the output file
|
|
32
32
|
*/
|
|
33
|
-
const buildJsBundle = (_a, handoff_1) => __awaiter(void 0, [_a, handoff_1], void 0, function* ({ entry, outputPath, outputFilename }, handoff) {
|
|
33
|
+
const buildJsBundle = (_a, handoff_1) => __awaiter(void 0, [_a, handoff_1], void 0, function* ({ entry, outputPath, outputFilename, format = 'cjs', }, handoff) {
|
|
34
34
|
var _b, _c, _d;
|
|
35
35
|
const absEntryPath = path_1.default.resolve(entry);
|
|
36
36
|
// Store the current NODE_ENV value before vite build
|
|
@@ -42,10 +42,12 @@ const buildJsBundle = (_a, handoff_1) => __awaiter(void 0, [_a, handoff_1], void
|
|
|
42
42
|
let viteConfig = Object.assign(Object.assign({}, vite_config_1.default), { build: Object.assign(Object.assign({}, vite_config_1.default.build), { lib: {
|
|
43
43
|
entry: absEntryPath,
|
|
44
44
|
name: path_1.default.basename(outputFilename, '.js'),
|
|
45
|
-
formats: [
|
|
45
|
+
formats: [format],
|
|
46
46
|
fileName: () => outputFilename,
|
|
47
47
|
}, rollupOptions: Object.assign(Object.assign({}, (_b = vite_config_1.default.build) === null || _b === void 0 ? void 0 : _b.rollupOptions), { output: {
|
|
48
|
-
|
|
48
|
+
// The global IIFE bundle is frequently side-effect-only; `auto` lets a bundle with no
|
|
49
|
+
// exports emit cleanly while `named` preserves the existing component-JS (cjs) behavior.
|
|
50
|
+
exports: format === 'iife' ? 'auto' : 'named',
|
|
49
51
|
} }), outDir: outputPath }) });
|
|
50
52
|
if ((_d = (_c = handoff === null || handoff === void 0 ? void 0 : handoff.config) === null || _c === void 0 ? void 0 : _c.hooks) === null || _d === void 0 ? void 0 : _d.jsBuildConfig) {
|
|
51
53
|
viteConfig = handoff.config.hooks.jsBuildConfig(viteConfig);
|
|
@@ -118,32 +120,58 @@ const buildComponentJs = (data, handoff) => __awaiter(void 0, void 0, void 0, fu
|
|
|
118
120
|
});
|
|
119
121
|
exports.buildComponentJs = buildComponentJs;
|
|
120
122
|
/**
|
|
121
|
-
* Builds the
|
|
123
|
+
* Builds the global preview script artifact (`component/main.js`) from the configured workspace JS
|
|
124
|
+
* entry.
|
|
122
125
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
126
|
+
* The artifact is optional: when no global JS entry is configured, or the configured entry no longer
|
|
127
|
+
* exists, or the build fails/produces no output, any stale `main.js` is removed so a failed rebuild
|
|
128
|
+
* never leaves global JS that appears current. Generated HTML references `main.js` only when this
|
|
129
|
+
* function leaves a fresh artifact on disk, so an omitted artifact yields no dangling reference.
|
|
130
|
+
*
|
|
131
|
+
* Output uses the browser-compatible IIFE format so the artifact can load as a classic script ahead
|
|
132
|
+
* of dependent component scripts while preserving intentional top-level side effects.
|
|
125
133
|
*
|
|
126
134
|
* @param handoff - The Handoff configuration object containing build settings
|
|
127
135
|
* @returns A Promise that resolves when the build process is complete
|
|
128
136
|
*/
|
|
129
137
|
const buildMainJS = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
138
|
+
var _a;
|
|
130
139
|
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
140
|
+
const mainJsPath = path_1.default.resolve(outputPath, exports.MAIN_COMPONENT_JS_FILE);
|
|
131
141
|
const runtimeConfig = (0, config_1.initRuntimeConfig)(handoff)[0];
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
const entry = (_a = runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.entries) === null || _a === void 0 ? void 0 : _a.js;
|
|
143
|
+
// No usable global JS entry: drop any stale artifact and emit nothing to reference.
|
|
144
|
+
if (!entry || !fs_extra_1.default.existsSync(path_1.default.resolve(entry))) {
|
|
145
|
+
if (entry) {
|
|
146
|
+
logger_1.Logger.warn(`Global JS entry "${entry}" was not found; removing stale ${exports.MAIN_COMPONENT_JS_FILE}.`);
|
|
147
|
+
}
|
|
148
|
+
yield fs_extra_1.default.remove(mainJsPath);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
logger_1.Logger.info(`Building script for global entry (${exports.MAIN_COMPONENT_JS_FILE})…`);
|
|
152
|
+
const startedAt = Date.now();
|
|
153
|
+
// Remove the previous artifact before rebuilding so a failed or empty build cannot silently
|
|
154
|
+
// preserve stale global JS that would still be referenced as current.
|
|
155
|
+
yield fs_extra_1.default.remove(mainJsPath);
|
|
156
|
+
try {
|
|
157
|
+
yield buildJsBundle({
|
|
158
|
+
entry,
|
|
159
|
+
outputPath,
|
|
160
|
+
outputFilename: exports.MAIN_COMPONENT_JS_FILE,
|
|
161
|
+
format: 'iife',
|
|
162
|
+
}, handoff);
|
|
163
|
+
if (yield fs_extra_1.default.pathExists(mainJsPath)) {
|
|
141
164
|
logger_1.Logger.info(`Finished building script for global entry (${exports.MAIN_COMPONENT_JS_FILE}) in ${(0, duration_1.formatDurationMs)(Date.now() - startedAt)}`);
|
|
142
165
|
}
|
|
143
|
-
|
|
144
|
-
logger_1.Logger.error(`
|
|
166
|
+
else {
|
|
167
|
+
logger_1.Logger.error(`Global script build produced no ${exports.MAIN_COMPONENT_JS_FILE} from "${entry}".`);
|
|
145
168
|
}
|
|
146
169
|
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
logger_1.Logger.error(`Failed to build global script (${exports.MAIN_COMPONENT_JS_FILE}) from "${entry}":`, e);
|
|
172
|
+
// Ensure a partial/failed build leaves no stale or broken artifact behind.
|
|
173
|
+
yield fs_extra_1.default.remove(mainJsPath);
|
|
174
|
+
}
|
|
147
175
|
});
|
|
148
176
|
exports.buildMainJS = buildMainJS;
|
|
149
177
|
exports.default = exports.buildComponentJs;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ArtifactReference } from '../../../artifacts';
|
|
2
|
+
import Handoff from '../../../index';
|
|
3
|
+
/**
|
|
4
|
+
* Centralized shared/global artifact model for generated preview HTML.
|
|
5
|
+
*
|
|
6
|
+
* The shared artifacts (`component/main.css`, `component/main.js`, `component/shared.css`) are
|
|
7
|
+
* optional: each is emitted only when a corresponding source entry is configured and builds
|
|
8
|
+
* successfully. Generated HTML must reference a shared artifact only when it actually exists, so a
|
|
9
|
+
* failed or absent global build never leaves a dangling reference the browser would request.
|
|
10
|
+
*
|
|
11
|
+
* This module is the single place that decides which shared artifacts are present and turns that
|
|
12
|
+
* decision into structured references and the `<link>`/`<script>` tags every renderer emits — so
|
|
13
|
+
* dependency resolution is driven by the artifact model, never by parsing HTML.
|
|
14
|
+
*/
|
|
15
|
+
/** Which shared/global artifacts currently exist in the component output directory. */
|
|
16
|
+
export interface SharedArtifactPresence {
|
|
17
|
+
/** `component/main.js` — the global preview script built from the configured JS entry. */
|
|
18
|
+
mainJs: boolean;
|
|
19
|
+
/** `component/main.css` — the global stylesheet built from the configured SCSS entry. */
|
|
20
|
+
mainCss: boolean;
|
|
21
|
+
/** `component/shared.css` — shared component styles extracted by the SCSS split marker. */
|
|
22
|
+
sharedCss: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve which shared/global artifacts are present on disk. Global artifacts are built before
|
|
26
|
+
* component/pattern HTML is generated, so an existence check here reflects whether the artifact can
|
|
27
|
+
* be referenced without producing a missing-file browser request.
|
|
28
|
+
*/
|
|
29
|
+
export declare const resolveSharedArtifactPresence: (handoff: Handoff) => SharedArtifactPresence;
|
|
30
|
+
/** Options controlling which shared style references a renderer participates in. */
|
|
31
|
+
export interface SharedStyleOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Whether the rendered component opts into the shared-styles split (`component/shared.css`).
|
|
34
|
+
* Only renderers that consume shared styles (Handlebars) set this; it is still gated on the
|
|
35
|
+
* artifact actually existing.
|
|
36
|
+
*/
|
|
37
|
+
includeSharedStyles?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build the structured shared-artifact references a preview/inspect document depends on, filtered to
|
|
41
|
+
* the artifacts that actually exist. Shared/global artifacts are optional references (`required:
|
|
42
|
+
* false`) owned by no entity (`ownerKind: 'asset'`, `ownerId: null`). Stylesheet references precede
|
|
43
|
+
* the script reference so consumers preserve the documented load order.
|
|
44
|
+
*/
|
|
45
|
+
export declare const getSharedArtifactReferences: (presence: SharedArtifactPresence, options?: SharedStyleOptions) => ArtifactReference[];
|
|
46
|
+
/**
|
|
47
|
+
* Render the `<link rel="stylesheet">` tags for the present shared stylesheet artifacts
|
|
48
|
+
* (`component/shared.css` then `component/main.css`), in load order. Returns the tags joined by a
|
|
49
|
+
* newline, or an empty string when no shared stylesheet exists.
|
|
50
|
+
*/
|
|
51
|
+
export declare const renderSharedStyleLinks: (presence: SharedArtifactPresence, basePath: string, options?: SharedStyleOptions) => string;
|
|
52
|
+
/**
|
|
53
|
+
* Render the global preview script tag for `component/main.js` when present, or an empty string when
|
|
54
|
+
* absent. The global script is a classic (non-deferred) tag so it executes before dependent
|
|
55
|
+
* component-specific scripts and preserves intentional top-level side effects.
|
|
56
|
+
*/
|
|
57
|
+
export declare const renderGlobalScriptTag: (presence: SharedArtifactPresence, basePath: string) => string;
|
|
58
|
+
/** Which of a component's own optional artifacts exist in the component output directory. */
|
|
59
|
+
export interface ComponentArtifactPresence {
|
|
60
|
+
/** `component/<id>.css` — built only when the component declares an SCSS/CSS entry. */
|
|
61
|
+
css: boolean;
|
|
62
|
+
/** `component/<id>.js` — built only when the component declares a JS entry. */
|
|
63
|
+
js: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve which of a component's own css/js artifacts exist on disk. A component's styles and
|
|
67
|
+
* scripts are built (or removed when their entry is absent) before its preview HTML is generated, so
|
|
68
|
+
* an existence check here reflects whether the artifact can be referenced without producing a
|
|
69
|
+
* missing-file browser request. Like the shared artifacts, these references are optional.
|
|
70
|
+
*/
|
|
71
|
+
export declare const resolveComponentArtifactPresence: (handoff: Handoff, componentId: string) => ComponentArtifactPresence;
|
|
72
|
+
/**
|
|
73
|
+
* Render the `<link rel="stylesheet">` tag for a component's own stylesheet (`component/<id>.css`)
|
|
74
|
+
* when it exists, or an empty string when the component declares no styles — so generated HTML never
|
|
75
|
+
* references an absent optional artifact.
|
|
76
|
+
*/
|
|
77
|
+
export declare const renderComponentStyleLink: (presence: ComponentArtifactPresence, componentId: string, basePath: string) => string;
|
|
78
|
+
/**
|
|
79
|
+
* Render the `<script>` tag for a component's own script (`component/<id>.js`) when it exists, or an
|
|
80
|
+
* empty string when the component declares no script. The component script is a classic tag loaded
|
|
81
|
+
* after the global script so global side effects run first.
|
|
82
|
+
*/
|
|
83
|
+
export declare const renderComponentScriptTag: (presence: ComponentArtifactPresence, componentId: string, basePath: string) => string;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderComponentScriptTag = exports.renderComponentStyleLink = exports.resolveComponentArtifactPresence = exports.renderGlobalScriptTag = exports.renderSharedStyleLinks = exports.getSharedArtifactReferences = exports.resolveSharedArtifactPresence = void 0;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const artifacts_1 = require("../../../artifacts");
|
|
10
|
+
const url_1 = require("../../../artifacts/url");
|
|
11
|
+
const component_1 = require("../component");
|
|
12
|
+
const css_1 = require("./css");
|
|
13
|
+
const javascript_1 = require("./javascript");
|
|
14
|
+
/**
|
|
15
|
+
* Resolve which shared/global artifacts are present on disk. Global artifacts are built before
|
|
16
|
+
* component/pattern HTML is generated, so an existence check here reflects whether the artifact can
|
|
17
|
+
* be referenced without producing a missing-file browser request.
|
|
18
|
+
*/
|
|
19
|
+
const resolveSharedArtifactPresence = (handoff) => {
|
|
20
|
+
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
21
|
+
return {
|
|
22
|
+
mainJs: fs_extra_1.default.existsSync(path_1.default.join(outputPath, javascript_1.MAIN_COMPONENT_JS_FILE)),
|
|
23
|
+
mainCss: fs_extra_1.default.existsSync(path_1.default.join(outputPath, css_1.MAIN_COMPONENT_CSS_FILE)),
|
|
24
|
+
sharedCss: fs_extra_1.default.existsSync(path_1.default.join(outputPath, css_1.SHARED_COMPONENT_CSS_FILE)),
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.resolveSharedArtifactPresence = resolveSharedArtifactPresence;
|
|
28
|
+
/**
|
|
29
|
+
* Build the structured shared-artifact references a preview/inspect document depends on, filtered to
|
|
30
|
+
* the artifacts that actually exist. Shared/global artifacts are optional references (`required:
|
|
31
|
+
* false`) owned by no entity (`ownerKind: 'asset'`, `ownerId: null`). Stylesheet references precede
|
|
32
|
+
* the script reference so consumers preserve the documented load order.
|
|
33
|
+
*/
|
|
34
|
+
const getSharedArtifactReferences = (presence, options) => {
|
|
35
|
+
const references = [];
|
|
36
|
+
if ((options === null || options === void 0 ? void 0 : options.includeSharedStyles) && presence.sharedCss) {
|
|
37
|
+
references.push({ path: artifacts_1.SHARED_STYLES_CSS_ARTIFACT_PATH, kind: 'shared', required: false, ownerKind: 'asset', ownerId: null });
|
|
38
|
+
}
|
|
39
|
+
if (presence.mainCss) {
|
|
40
|
+
references.push({ path: artifacts_1.SHARED_MAIN_CSS_ARTIFACT_PATH, kind: 'shared', required: false, ownerKind: 'asset', ownerId: null });
|
|
41
|
+
}
|
|
42
|
+
if (presence.mainJs) {
|
|
43
|
+
references.push({ path: artifacts_1.SHARED_MAIN_JS_ARTIFACT_PATH, kind: 'shared', required: false, ownerKind: 'asset', ownerId: null });
|
|
44
|
+
}
|
|
45
|
+
return references;
|
|
46
|
+
};
|
|
47
|
+
exports.getSharedArtifactReferences = getSharedArtifactReferences;
|
|
48
|
+
/**
|
|
49
|
+
* Render the `<link rel="stylesheet">` tags for the present shared stylesheet artifacts
|
|
50
|
+
* (`component/shared.css` then `component/main.css`), in load order. Returns the tags joined by a
|
|
51
|
+
* newline, or an empty string when no shared stylesheet exists.
|
|
52
|
+
*/
|
|
53
|
+
const renderSharedStyleLinks = (presence, basePath, options) => (0, exports.getSharedArtifactReferences)(presence, options)
|
|
54
|
+
.filter((ref) => ref.path.endsWith('.css'))
|
|
55
|
+
.map((ref) => `<link rel="stylesheet" href="${(0, url_1.buildArtifactUrl)(ref.path, basePath)}">`)
|
|
56
|
+
.join('\n');
|
|
57
|
+
exports.renderSharedStyleLinks = renderSharedStyleLinks;
|
|
58
|
+
/**
|
|
59
|
+
* Render the global preview script tag for `component/main.js` when present, or an empty string when
|
|
60
|
+
* absent. The global script is a classic (non-deferred) tag so it executes before dependent
|
|
61
|
+
* component-specific scripts and preserves intentional top-level side effects.
|
|
62
|
+
*/
|
|
63
|
+
const renderGlobalScriptTag = (presence, basePath) => presence.mainJs ? `<script src="${(0, url_1.buildArtifactUrl)(artifacts_1.SHARED_MAIN_JS_ARTIFACT_PATH, basePath)}"></script>` : '';
|
|
64
|
+
exports.renderGlobalScriptTag = renderGlobalScriptTag;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve which of a component's own css/js artifacts exist on disk. A component's styles and
|
|
67
|
+
* scripts are built (or removed when their entry is absent) before its preview HTML is generated, so
|
|
68
|
+
* an existence check here reflects whether the artifact can be referenced without producing a
|
|
69
|
+
* missing-file browser request. Like the shared artifacts, these references are optional.
|
|
70
|
+
*/
|
|
71
|
+
const resolveComponentArtifactPresence = (handoff, componentId) => {
|
|
72
|
+
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
73
|
+
return {
|
|
74
|
+
css: fs_extra_1.default.existsSync(path_1.default.join(outputPath, `${componentId}.css`)),
|
|
75
|
+
js: fs_extra_1.default.existsSync(path_1.default.join(outputPath, `${componentId}.js`)),
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
exports.resolveComponentArtifactPresence = resolveComponentArtifactPresence;
|
|
79
|
+
/**
|
|
80
|
+
* Render the `<link rel="stylesheet">` tag for a component's own stylesheet (`component/<id>.css`)
|
|
81
|
+
* when it exists, or an empty string when the component declares no styles — so generated HTML never
|
|
82
|
+
* references an absent optional artifact.
|
|
83
|
+
*/
|
|
84
|
+
const renderComponentStyleLink = (presence, componentId, basePath) => presence.css ? `<link rel="stylesheet" href="${(0, url_1.buildArtifactUrl)(`component/${componentId}.css`, basePath)}">` : '';
|
|
85
|
+
exports.renderComponentStyleLink = renderComponentStyleLink;
|
|
86
|
+
/**
|
|
87
|
+
* Render the `<script>` tag for a component's own script (`component/<id>.js`) when it exists, or an
|
|
88
|
+
* empty string when the component declares no script. The component script is a classic tag loaded
|
|
89
|
+
* after the global script so global side effects run first.
|
|
90
|
+
*/
|
|
91
|
+
const renderComponentScriptTag = (presence, componentId, basePath) => presence.js ? `<script src="${(0, url_1.buildArtifactUrl)(`component/${componentId}.js`, basePath)}"></script>` : '';
|
|
92
|
+
exports.renderComponentScriptTag = renderComponentScriptTag;
|
|
@@ -43,9 +43,12 @@ exports.getComponentOutputPath = getComponentOutputPath;
|
|
|
43
43
|
*/
|
|
44
44
|
function componentTransformer(handoff) {
|
|
45
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// Build the shared/global artifacts first so component/pattern preview HTML can reference them
|
|
47
|
+
// only when they actually exist. A failed/absent global build therefore never produces a dangling
|
|
48
|
+
// reference in generated HTML.
|
|
48
49
|
yield (0, javascript_1.buildMainJS)(handoff);
|
|
49
50
|
yield (0, css_1.buildMainCss)(handoff);
|
|
51
|
+
const componentData = yield (0, builder_1.default)(handoff);
|
|
52
|
+
yield (0, summary_1.default)(handoff, componentData);
|
|
50
53
|
});
|
|
51
54
|
}
|
|
@@ -16,7 +16,9 @@ exports.getPatternIdsReferencingComponents = void 0;
|
|
|
16
16
|
exports.processPatterns = processPatterns;
|
|
17
17
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
18
|
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const url_1 = require("../../../artifacts/url");
|
|
19
20
|
const logger_1 = require("../../../utils/logger");
|
|
21
|
+
const shared_artifacts_1 = require("../component/shared-artifacts");
|
|
20
22
|
const api_1 = require("./api");
|
|
21
23
|
const html_1 = require("./html");
|
|
22
24
|
/**
|
|
@@ -39,7 +41,7 @@ const getPatternIdsReferencingComponents = (handoff, componentIds) => {
|
|
|
39
41
|
return result;
|
|
40
42
|
};
|
|
41
43
|
exports.getPatternIdsReferencingComponents = getPatternIdsReferencingComponents;
|
|
42
|
-
function buildPattern(handoff, patternId, pattern, componentOutputDir, basePath) {
|
|
44
|
+
function buildPattern(handoff, patternId, pattern, componentOutputDir, basePath, sharedArtifacts) {
|
|
43
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
44
46
|
const fragments = [];
|
|
45
47
|
let hasErrors = false;
|
|
@@ -71,6 +73,14 @@ function buildPattern(handoff, patternId, pattern, componentOutputDir, basePath)
|
|
|
71
73
|
const html = yield fs_extra_1.default.readFile(htmlFilePath, 'utf8');
|
|
72
74
|
fragments.push({ componentId: ref.id, html });
|
|
73
75
|
}
|
|
76
|
+
// Resolve component-owned artifact presence for each composed component so the pattern references
|
|
77
|
+
// only the stylesheets that actually exist (no dangling references for style-less components).
|
|
78
|
+
const componentArtifacts = new Map();
|
|
79
|
+
for (const fragment of fragments) {
|
|
80
|
+
if (!componentArtifacts.has(fragment.componentId)) {
|
|
81
|
+
componentArtifacts.set(fragment.componentId, (0, shared_artifacts_1.resolveComponentArtifactPresence)(handoff, fragment.componentId));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
74
84
|
if (fragments.length === 0) {
|
|
75
85
|
logger_1.Logger.warn(`Pattern "${patternId}" produced no fragments. Skipping.`);
|
|
76
86
|
return null;
|
|
@@ -78,9 +88,9 @@ function buildPattern(handoff, patternId, pattern, componentOutputDir, basePath)
|
|
|
78
88
|
if (hasErrors) {
|
|
79
89
|
logger_1.Logger.warn(`Pattern "${patternId}" has missing components but will be composed from available fragments.`);
|
|
80
90
|
}
|
|
81
|
-
const composedHtml = (0, html_1.composePatternHtml)(patternId, pattern.title, fragments, basePath);
|
|
91
|
+
const composedHtml = (0, html_1.composePatternHtml)(patternId, pattern.title, fragments, basePath, sharedArtifacts, componentArtifacts);
|
|
82
92
|
const patternUrl = `${patternId}.html`;
|
|
83
|
-
const patternData = Object.assign(Object.assign({}, pattern), { id: patternId, url: patternUrl, path:
|
|
93
|
+
const patternData = Object.assign(Object.assign({}, pattern), { id: patternId, url: patternUrl, path: (0, url_1.buildPatternDetailUrl)(patternId, basePath) });
|
|
84
94
|
yield (0, api_1.writePatternHtml)(patternId, composedHtml, handoff);
|
|
85
95
|
yield (0, api_1.writePatternApi)(patternId, patternData, handoff);
|
|
86
96
|
logger_1.Logger.debug(`Built pattern: ${patternId} (${fragments.length} component(s))`);
|
|
@@ -94,8 +104,14 @@ function buildPattern(handoff, patternId, pattern, componentOutputDir, basePath)
|
|
|
94
104
|
*/
|
|
95
105
|
function processPatterns(handoff, options) {
|
|
96
106
|
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
-
var _a
|
|
98
|
-
|
|
107
|
+
var _a;
|
|
108
|
+
// Resolve the pattern set through the storage-agnostic store (v2). The filesystem store is a read
|
|
109
|
+
// view over `runtimeConfig.entries.patterns`, so these are the same records the build has always
|
|
110
|
+
// used — rebuilt into the id-keyed map the build logic below expects.
|
|
111
|
+
const runtimePatterns = {};
|
|
112
|
+
for (const pattern of yield handoff.store.patterns.list()) {
|
|
113
|
+
runtimePatterns[pattern.id] = pattern;
|
|
114
|
+
}
|
|
99
115
|
const patternIds = Object.keys(runtimePatterns);
|
|
100
116
|
const onlyPatternIds = options === null || options === void 0 ? void 0 : options.onlyPatternIds;
|
|
101
117
|
const isPartial = onlyPatternIds !== undefined;
|
|
@@ -105,13 +121,16 @@ function processPatterns(handoff, options) {
|
|
|
105
121
|
return [];
|
|
106
122
|
}
|
|
107
123
|
const componentOutputDir = path_1.default.resolve(handoff.workingPath, 'public/api/component');
|
|
108
|
-
const basePath = (
|
|
124
|
+
const basePath = (_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : '';
|
|
125
|
+
// Resolve the shared/global artifacts once so composed patterns reference `component/main.css` and
|
|
126
|
+
// `component/main.js` only when present (deduplicated to a single global script per page).
|
|
127
|
+
const sharedArtifacts = (0, shared_artifacts_1.resolveSharedArtifactPresence)(handoff);
|
|
109
128
|
if (!isPartial) {
|
|
110
129
|
logger_1.Logger.info(`Building ${patternIds.length} pattern(s)...`);
|
|
111
130
|
const result = [];
|
|
112
131
|
for (const patternId of patternIds) {
|
|
113
132
|
const pattern = runtimePatterns[patternId];
|
|
114
|
-
const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath);
|
|
133
|
+
const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath, sharedArtifacts);
|
|
115
134
|
if (built) {
|
|
116
135
|
result.push(built);
|
|
117
136
|
}
|
|
@@ -135,7 +154,7 @@ function processPatterns(handoff, options) {
|
|
|
135
154
|
for (const patternId of patternIds) {
|
|
136
155
|
if (onlyPatternIds.has(patternId)) {
|
|
137
156
|
const pattern = runtimePatterns[patternId];
|
|
138
|
-
const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath);
|
|
157
|
+
const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath, sharedArtifacts);
|
|
139
158
|
if (built) {
|
|
140
159
|
existingById.set(patternId, built);
|
|
141
160
|
}
|
|
@@ -145,7 +164,7 @@ function processPatterns(handoff, options) {
|
|
|
145
164
|
}
|
|
146
165
|
else if (!existingById.has(patternId)) {
|
|
147
166
|
const pattern = runtimePatterns[patternId];
|
|
148
|
-
const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath);
|
|
167
|
+
const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath, sharedArtifacts);
|
|
149
168
|
if (built) {
|
|
150
169
|
existingById.set(patternId, built);
|
|
151
170
|
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
import type { ComponentArtifactPresence, SharedArtifactPresence } from '../component/shared-artifacts';
|
|
1
2
|
/**
|
|
2
3
|
* Composes multiple component preview HTML documents into a single-page HTML.
|
|
3
4
|
*
|
|
4
|
-
* Each component preview may be a React SSR document (
|
|
5
|
-
*
|
|
5
|
+
* Each component preview may be a React SSR document (server-rendered markup plus a `#root` /
|
|
6
|
+
* `#__APP_PROPS__` pair) or a static Handlebars document.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* React client/hydration bundles are component-owned artifacts (`component/<id>.client.js`). This
|
|
9
|
+
* composer copies no per-fragment bundle: it namespaces each fragment's root/props so multiple
|
|
10
|
+
* instances coexist, marks each root with the `data-handoff-component`/`data-handoff-props`
|
|
11
|
+
* attributes the component bundle hydrates by, and references each unique component's client artifact
|
|
12
|
+
* exactly once.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* to overwrite earlier React instances before hydration completes.
|
|
14
|
+
* Shared/global artifacts follow the shared-artifact model: `component/main.css` and
|
|
15
|
+
* `component/main.js` are referenced only when present, and the global script is emitted exactly once
|
|
16
|
+
* (deduplicated across fragments) so its top-level side effects run a single time, ahead of the
|
|
17
|
+
* per-component client bundles.
|
|
15
18
|
*/
|
|
16
19
|
export declare const composePatternHtml: (_patternId: string, patternTitle: string, fragments: {
|
|
17
20
|
componentId: string;
|
|
18
21
|
html: string;
|
|
19
|
-
}[], basePath: string) => string;
|
|
22
|
+
}[], basePath: string, sharedArtifacts: SharedArtifactPresence, componentArtifacts: Map<string, ComponentArtifactPresence>) => string;
|
|
@@ -6,33 +6,47 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.composePatternHtml = void 0;
|
|
7
7
|
const escape_1 = __importDefault(require("lodash/escape"));
|
|
8
8
|
const node_html_parser_1 = require("node-html-parser");
|
|
9
|
+
const artifacts_1 = require("../../../artifacts");
|
|
10
|
+
const url_1 = require("../../../artifacts/url");
|
|
9
11
|
/**
|
|
10
12
|
* Composes multiple component preview HTML documents into a single-page HTML.
|
|
11
13
|
*
|
|
12
|
-
* Each component preview may be a React SSR document (
|
|
13
|
-
*
|
|
14
|
+
* Each component preview may be a React SSR document (server-rendered markup plus a `#root` /
|
|
15
|
+
* `#__APP_PROPS__` pair) or a static Handlebars document.
|
|
14
16
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
+
* React client/hydration bundles are component-owned artifacts (`component/<id>.client.js`). This
|
|
18
|
+
* composer copies no per-fragment bundle: it namespaces each fragment's root/props so multiple
|
|
19
|
+
* instances coexist, marks each root with the `data-handoff-component`/`data-handoff-props`
|
|
20
|
+
* attributes the component bundle hydrates by, and references each unique component's client artifact
|
|
21
|
+
* exactly once.
|
|
17
22
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* to overwrite earlier React instances before hydration completes.
|
|
23
|
+
* Shared/global artifacts follow the shared-artifact model: `component/main.css` and
|
|
24
|
+
* `component/main.js` are referenced only when present, and the global script is emitted exactly once
|
|
25
|
+
* (deduplicated across fragments) so its top-level side effects run a single time, ahead of the
|
|
26
|
+
* per-component client bundles.
|
|
23
27
|
*/
|
|
24
|
-
const composePatternHtml = (_patternId, patternTitle, fragments, basePath) => {
|
|
28
|
+
const composePatternHtml = (_patternId, patternTitle, fragments, basePath, sharedArtifacts, componentArtifacts) => {
|
|
29
|
+
var _a;
|
|
25
30
|
const cssHrefs = new Set();
|
|
26
31
|
const bodyParts = [];
|
|
27
|
-
const
|
|
28
|
-
|
|
32
|
+
const clientArtifactHrefs = new Set();
|
|
33
|
+
if (sharedArtifacts.mainCss) {
|
|
34
|
+
cssHrefs.add((0, url_1.buildArtifactUrl)(artifacts_1.SHARED_MAIN_CSS_ARTIFACT_PATH, basePath));
|
|
35
|
+
}
|
|
29
36
|
cssHrefs.add(`${basePath}/assets/css/preview.css`);
|
|
37
|
+
// Canonical URL of the shared global script; used to strip any per-fragment reference so the
|
|
38
|
+
// global script is emitted (and executed) exactly once for the whole composed page.
|
|
39
|
+
const globalScriptUrl = (0, url_1.buildArtifactUrl)(artifacts_1.SHARED_MAIN_JS_ARTIFACT_PATH, basePath);
|
|
30
40
|
const seenComponents = new Set();
|
|
31
41
|
for (let i = 0; i < fragments.length; i++) {
|
|
32
42
|
const { componentId, html } = fragments[i];
|
|
33
43
|
if (!seenComponents.has(componentId)) {
|
|
34
44
|
seenComponents.add(componentId);
|
|
35
|
-
|
|
45
|
+
// Reference a component's stylesheet only when it actually exists, so a pattern never requests
|
|
46
|
+
// an absent optional artifact for a component that declares no styles.
|
|
47
|
+
if ((_a = componentArtifacts.get(componentId)) === null || _a === void 0 ? void 0 : _a.css) {
|
|
48
|
+
cssHrefs.add((0, url_1.buildArtifactUrl)(`component/${componentId}.css`, basePath));
|
|
49
|
+
}
|
|
36
50
|
}
|
|
37
51
|
const doc = (0, node_html_parser_1.parse)(html);
|
|
38
52
|
const suffix = `_p${i}`;
|
|
@@ -40,28 +54,37 @@ const composePatternHtml = (_patternId, patternTitle, fragments, basePath) => {
|
|
|
40
54
|
const namespacedPropsId = `__APP_PROPS__${suffix}`;
|
|
41
55
|
// -- Extract props JSON (React SSR) ------------------------------------
|
|
42
56
|
const propsScript = doc.querySelector('#__APP_PROPS__');
|
|
43
|
-
let propsContent =
|
|
57
|
+
let propsContent = null;
|
|
44
58
|
if (propsScript) {
|
|
45
59
|
propsContent = propsScript.textContent || '{}';
|
|
46
60
|
propsScript.remove();
|
|
47
61
|
}
|
|
48
|
-
// --
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
for (const script of
|
|
52
|
-
let code = script.textContent || '';
|
|
53
|
-
if (code.trim()) {
|
|
54
|
-
code = code
|
|
55
|
-
.replace(/__APP_PROPS__/g, namespacedPropsId)
|
|
56
|
-
.replace(/getElementById\(\s*["']root["']\s*\)/g, `getElementById("${namespacedRootId}")`);
|
|
57
|
-
scriptTags.push(` <script type="module">\n${code}\n </script>`);
|
|
58
|
-
}
|
|
62
|
+
// -- Drop the fragment's own client-artifact reference -----------------
|
|
63
|
+
// The bundle is referenced once per component for the whole page (below); a fragment must not
|
|
64
|
+
// carry its standalone-preview script tag into the composed document.
|
|
65
|
+
for (const script of doc.querySelectorAll('script[type="module"]')) {
|
|
59
66
|
script.remove();
|
|
60
67
|
}
|
|
68
|
+
// -- Drop the fragment's own global-script reference -------------------
|
|
69
|
+
// Static (Handlebars) fragments render the global `component/main.js` tag inside their body; left
|
|
70
|
+
// in place it would be inlined per fragment and run the global side effects multiple times. The
|
|
71
|
+
// page references the global script exactly once (below).
|
|
72
|
+
for (const script of doc.querySelectorAll('script')) {
|
|
73
|
+
if (script.getAttribute('src') === globalScriptUrl) {
|
|
74
|
+
script.remove();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
61
77
|
// -- Namespace the root element (React SSR) ----------------------------
|
|
78
|
+
// A React fragment exposes its mount point to the component bundle via stable data attributes.
|
|
62
79
|
const rootEl = doc.querySelector('#root');
|
|
80
|
+
const isReactFragment = !!rootEl && propsContent !== null;
|
|
63
81
|
if (rootEl) {
|
|
64
82
|
rootEl.setAttribute('id', namespacedRootId);
|
|
83
|
+
if (isReactFragment) {
|
|
84
|
+
rootEl.setAttribute('data-handoff-component', componentId);
|
|
85
|
+
rootEl.setAttribute('data-handoff-props', namespacedPropsId);
|
|
86
|
+
clientArtifactHrefs.add((0, url_1.buildArtifactUrl)(`component/${componentId}.client.js`, basePath));
|
|
87
|
+
}
|
|
65
88
|
}
|
|
66
89
|
// -- Extract body content ----------------------------------------------
|
|
67
90
|
const bodyEl = doc.querySelector('body');
|
|
@@ -70,14 +93,26 @@ const composePatternHtml = (_patternId, patternTitle, fragments, basePath) => {
|
|
|
70
93
|
: rootEl
|
|
71
94
|
? rootEl.outerHTML
|
|
72
95
|
: doc.innerHTML || '';
|
|
96
|
+
const propsScriptTag = propsContent !== null
|
|
97
|
+
? ` <script id="${namespacedPropsId}" type="application/json">${propsContent}</script>\n`
|
|
98
|
+
: '';
|
|
73
99
|
bodyParts.push(` <div class="handoff-pattern-block" data-component="${componentId}" data-fragment="${i}">\n` +
|
|
74
|
-
|
|
100
|
+
propsScriptTag +
|
|
75
101
|
` ${bodyContent}\n` +
|
|
76
102
|
` </div>`);
|
|
77
103
|
}
|
|
78
104
|
const linkTags = Array.from(cssHrefs)
|
|
79
105
|
.map((href) => ` <link rel="stylesheet" href="${href}" />`)
|
|
80
106
|
.join('\n');
|
|
107
|
+
// The global script is referenced once for the whole page (deduplicated across fragments) and
|
|
108
|
+
// placed before the per-component client bundles so its side effects run first and exactly once.
|
|
109
|
+
const globalScriptTag = sharedArtifacts.mainJs
|
|
110
|
+
? ` <script src="${(0, url_1.buildArtifactUrl)(artifacts_1.SHARED_MAIN_JS_ARTIFACT_PATH, basePath)}"></script>`
|
|
111
|
+
: '';
|
|
112
|
+
const clientScriptTags = Array.from(clientArtifactHrefs)
|
|
113
|
+
.map((href) => ` <script type="module" src="${href}"></script>`)
|
|
114
|
+
.join('\n');
|
|
115
|
+
const scriptTags = [globalScriptTag, clientScriptTags].filter(Boolean).join('\n');
|
|
81
116
|
return `<!DOCTYPE html>
|
|
82
117
|
<html lang="en">
|
|
83
118
|
<head>
|
|
@@ -87,7 +122,7 @@ ${linkTags}
|
|
|
87
122
|
</head>
|
|
88
123
|
<body>
|
|
89
124
|
${bodyParts.join('\n')}
|
|
90
|
-
${scriptTags
|
|
125
|
+
${scriptTags}
|
|
91
126
|
</body>
|
|
92
127
|
</html>`;
|
|
93
128
|
};
|