handoff-app 1.1.3 → 1.2.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/config/config.template.js +21 -14
- package/config/config.template.ts +54 -0
- package/config/docs/system.md +2 -0
- package/dist/app-builder/build.js +31 -12
- package/dist/app-builder/config-diff/index.d.ts +4 -0
- package/dist/app-builder/config-diff/index.js +12 -0
- package/dist/app-builder/config-diff/registry.d.ts +9 -0
- package/dist/app-builder/config-diff/registry.js +30 -0
- package/dist/app-builder/config-diff/snapshot.d.ts +13 -0
- package/dist/app-builder/config-diff/snapshot.js +43 -0
- package/dist/app-builder/config-diff/strategies/pattern.d.ts +2 -0
- package/dist/app-builder/config-diff/strategies/pattern.js +106 -0
- package/dist/app-builder/config-diff/types.d.ts +33 -0
- package/dist/app-builder/config-diff/types.js +2 -0
- package/dist/app-builder/watchers/component.d.ts +15 -0
- package/dist/app-builder/watchers/component.js +69 -0
- package/dist/app-builder/watchers/utils.d.ts +14 -0
- package/dist/app-builder/watchers/utils.js +44 -0
- package/dist/app-builder/watchers.d.ts +10 -19
- package/dist/app-builder/watchers.js +65 -79
- package/dist/app-builder/websocket.js +10 -4
- package/dist/cache/build-cache.js +14 -5
- package/dist/cli/create.js +67 -23
- package/dist/cli/eject.js +48 -7
- package/dist/cli/make.js +29 -4
- package/dist/cli/scaffold.js +60 -19
- package/dist/cli.js +1 -1
- package/dist/config/helpers.d.ts +7 -0
- package/dist/config/helpers.js +31 -0
- package/dist/config/index.d.ts +2 -1
- package/dist/config/index.js +5 -1
- package/dist/config/loader.d.ts +10 -1
- package/dist/config/loader.js +92 -28
- package/dist/config/normalizers/declaration.d.ts +11 -0
- package/dist/config/normalizers/declaration.js +119 -0
- package/dist/config/normalizers/pattern.d.ts +8 -0
- package/dist/config/normalizers/pattern.js +35 -0
- package/dist/config/runtime.d.ts +9 -3
- package/dist/config/runtime.js +260 -33
- package/dist/declarations/helpers.d.ts +6 -0
- package/dist/declarations/helpers.js +19 -0
- package/dist/declarations/index.d.ts +2 -0
- package/dist/declarations/index.js +9 -0
- package/dist/declarations/types.d.ts +60 -0
- package/dist/declarations/types.js +2 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +34 -5
- package/dist/pipeline/index.d.ts +1 -0
- package/dist/pipeline/index.js +3 -1
- package/dist/pipeline/patterns.d.ts +8 -0
- package/dist/pipeline/patterns.js +21 -0
- package/dist/transformers/docgen/index.js +12 -2
- package/dist/transformers/plugins/csf-render.js +170 -34
- package/dist/transformers/plugins/ssr-render.js +18 -2
- package/dist/transformers/preview/component/api.js +16 -7
- package/dist/transformers/preview/component/builder.js +61 -8
- package/dist/transformers/preview/component/css.d.ts +8 -0
- package/dist/transformers/preview/component/css.js +6 -4
- package/dist/transformers/preview/component/html.js +14 -5
- package/dist/transformers/preview/component/javascript.d.ts +0 -1
- package/dist/transformers/preview/component/javascript.js +25 -14
- package/dist/transformers/preview/component/previews.d.ts +9 -0
- package/dist/transformers/preview/component/previews.js +10 -0
- package/dist/transformers/preview/pattern/api.d.ts +11 -0
- package/dist/transformers/preview/pattern/api.js +79 -0
- package/dist/transformers/preview/pattern/builder.d.ts +17 -0
- package/dist/transformers/preview/pattern/builder.js +167 -0
- package/dist/transformers/preview/pattern/html.d.ts +19 -0
- package/dist/transformers/preview/pattern/html.js +94 -0
- package/dist/transformers/preview/types.d.ts +54 -0
- package/dist/transformers/utils/csf.d.ts +16 -0
- package/dist/transformers/utils/csf.js +30 -0
- package/dist/transformers/utils/index.d.ts +1 -0
- package/dist/transformers/utils/index.js +2 -0
- package/dist/transformers/utils/module.d.ts +5 -0
- package/dist/transformers/utils/module.js +26 -13
- package/dist/transformers/utils/usage.d.ts +23 -0
- package/dist/transformers/utils/usage.js +294 -0
- package/dist/transformers/utils/vite-logger.js +4 -4
- package/dist/transformers/vite-config.js +2 -0
- package/dist/types/config.d.ts +32 -7
- package/dist/types/preview.d.ts +2 -0
- package/dist/utils/duration.d.ts +2 -0
- package/dist/utils/duration.js +6 -0
- package/dist/utils/logger.d.ts +42 -5
- package/dist/utils/logger.js +135 -15
- package/docs/cli.md +3 -3
- package/package.json +3 -1
- package/src/app/components/Component/PageSliceResolver.tsx +6 -6
- package/src/app/components/Component/Preview.tsx +17 -12
- package/src/app/components/Component/slices/ComponentDisplaySlice.tsx +16 -6
- package/src/app/components/Markdown/CodeHighlight.tsx +60 -11
- package/src/app/components/util/index.ts +71 -2
- package/src/app/pages/system/pattern/[pattern]/index.tsx +346 -0
- package/src/app/pages/system/pattern/index.tsx +14 -0
- package/src/app-builder/README.md +37 -9
- package/src/app-builder/build.ts +34 -12
- package/src/app-builder/config-diff/index.ts +4 -0
- package/src/app-builder/config-diff/registry.ts +23 -0
- package/src/app-builder/config-diff/snapshot.ts +51 -0
- package/src/app-builder/config-diff/strategies/pattern.ts +80 -0
- package/src/app-builder/config-diff/types.ts +36 -0
- package/src/app-builder/watchers/component.ts +72 -0
- package/src/app-builder/watchers/utils.ts +44 -0
- package/src/app-builder/watchers.ts +74 -80
- package/src/app-builder/websocket.ts +16 -5
- package/src/cache/build-cache.ts +14 -5
- package/src/cli/create.ts +86 -25
- package/src/cli/eject.ts +29 -7
- package/src/cli/make.ts +32 -5
- package/src/cli/scaffold.ts +68 -21
- package/src/cli.ts +1 -1
- package/src/config/README.md +10 -1
- package/src/config/helpers.ts +43 -0
- package/src/config/index.ts +4 -1
- package/src/config/loader.ts +110 -31
- package/src/config/normalizers/declaration.ts +176 -0
- package/src/config/normalizers/pattern.ts +56 -0
- package/src/config/runtime.ts +317 -35
- package/src/declarations/helpers.ts +36 -0
- package/src/declarations/index.ts +19 -0
- package/src/declarations/types.ts +70 -0
- package/src/index.ts +47 -6
- package/src/pipeline/index.ts +1 -0
- package/src/pipeline/patterns.ts +12 -0
- package/src/transformers/README.md +2 -2
- package/src/transformers/docgen/index.ts +13 -2
- package/src/transformers/plugins/csf-render.ts +216 -49
- package/src/transformers/plugins/ssr-render.ts +23 -7
- package/src/transformers/preview/component/api.ts +27 -7
- package/src/transformers/preview/component/builder.ts +82 -9
- package/src/transformers/preview/component/css.ts +6 -5
- package/src/transformers/preview/component/html.ts +18 -5
- package/src/transformers/preview/component/javascript.ts +29 -19
- package/src/transformers/preview/component/previews.ts +12 -0
- package/src/transformers/preview/pattern/api.ts +83 -0
- package/src/transformers/preview/pattern/builder.ts +192 -0
- package/src/transformers/preview/pattern/html.ts +107 -0
- package/src/transformers/preview/types.ts +58 -1
- package/src/transformers/utils/csf.ts +48 -0
- package/src/transformers/utils/index.ts +3 -0
- package/src/transformers/utils/module.ts +33 -18
- package/src/transformers/utils/usage.ts +319 -0
- package/src/transformers/utils/vite-logger.ts +4 -4
- package/src/transformers/vite-config.ts +2 -0
- package/src/types/README.md +1 -1
- package/src/types/config.ts +33 -7
- package/src/types/preview.ts +2 -0
- package/src/utils/duration.ts +3 -0
- package/src/utils/logger.ts +143 -16
- package/templates/default/components/button/Button.jsx.tpl +64 -0
- package/templates/default/components/button/button.react.handoff.js.tpl +31 -0
- package/templates/default/components/button/button.react.handoff.ts.tpl +31 -0
- package/templates/default/handoff.config.js.tpl +29 -16
- package/templates/default/handoff.config.ts.tpl +196 -0
- package/templates/default/handoff.config.with-examples.js.tpl +28 -17
- package/templates/default/handoff.config.with-examples.ts.tpl +195 -0
- package/templates/default/package.json.tpl +1 -3
- package/handoff.config.js +0 -90
- package/templates/default/components/button/button.js.tpl +0 -62
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCsfStoryEntries = getCsfStoryEntries;
|
|
4
|
+
exports.createCsfStoryPreviews = createCsfStoryPreviews;
|
|
5
|
+
const lodash_1 = require("lodash");
|
|
6
|
+
function getCsfStoryEntries(moduleExports) {
|
|
7
|
+
return Object.entries(moduleExports).filter(([key, value]) => {
|
|
8
|
+
if (key === 'default' || key === '__esModule')
|
|
9
|
+
return false;
|
|
10
|
+
if (typeof value === 'function')
|
|
11
|
+
return true;
|
|
12
|
+
if (value && typeof value === 'object')
|
|
13
|
+
return true;
|
|
14
|
+
return false;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function createCsfStoryPreviews(moduleExports) {
|
|
18
|
+
const meta = (moduleExports.default || {});
|
|
19
|
+
const stories = getCsfStoryEntries(moduleExports);
|
|
20
|
+
const previews = {};
|
|
21
|
+
for (const [storyKey, storyValue] of stories) {
|
|
22
|
+
const storyObj = (typeof storyValue === 'function' ? {} : storyValue);
|
|
23
|
+
previews[storyKey] = {
|
|
24
|
+
title: storyObj.name || (0, lodash_1.startCase)(storyKey),
|
|
25
|
+
values: Object.assign(Object.assign({}, (meta.args || {})), (storyObj.args || {})),
|
|
26
|
+
url: '',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return previews;
|
|
30
|
+
}
|
|
@@ -28,6 +28,8 @@ __exportStar(require("./html"), exports);
|
|
|
28
28
|
__exportStar(require("./build"), exports);
|
|
29
29
|
// Module utilities
|
|
30
30
|
__exportStar(require("./module"), exports);
|
|
31
|
+
// CSF utilities
|
|
32
|
+
__exportStar(require("./csf"), exports);
|
|
31
33
|
// Handlebars utilities
|
|
32
34
|
__exportStar(require("./handlebars"), exports);
|
|
33
35
|
// Schema loader utilities
|
|
@@ -6,3 +6,8 @@ import { ModuleEvaluationResult } from '../types';
|
|
|
6
6
|
* @returns Module evaluation result with exports
|
|
7
7
|
*/
|
|
8
8
|
export declare function buildAndEvaluateModule(entryPath: string, handoff: any): Promise<ModuleEvaluationResult>;
|
|
9
|
+
/**
|
|
10
|
+
* Builds and evaluates a module synchronously using esbuild.
|
|
11
|
+
* Used by config/runtime code paths that must remain synchronous.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildAndEvaluateModuleSync(entryPath: string, handoff: any): ModuleEvaluationResult;
|
|
@@ -13,8 +13,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.buildAndEvaluateModule = buildAndEvaluateModule;
|
|
16
|
+
exports.buildAndEvaluateModuleSync = buildAndEvaluateModuleSync;
|
|
16
17
|
const esbuild_1 = __importDefault(require("esbuild"));
|
|
17
18
|
const build_1 = require("./build");
|
|
19
|
+
function createModuleBuildConfig(entryPath, handoff) {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
const defaultBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_SSR_BUILD_CONFIG), { entryPoints: [entryPath] });
|
|
22
|
+
return ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.ssrBuildConfig)
|
|
23
|
+
? handoff.config.hooks.ssrBuildConfig(defaultBuildConfig)
|
|
24
|
+
: defaultBuildConfig;
|
|
25
|
+
}
|
|
26
|
+
function evaluateBuiltModule(code) {
|
|
27
|
+
const mod = { exports: {} };
|
|
28
|
+
const func = new Function('require', 'module', 'exports', code);
|
|
29
|
+
func(require, mod, mod.exports);
|
|
30
|
+
return mod;
|
|
31
|
+
}
|
|
18
32
|
/**
|
|
19
33
|
* Builds and evaluates a module using esbuild
|
|
20
34
|
* @param entryPath - Path to the module entry point
|
|
@@ -23,20 +37,19 @@ const build_1 = require("./build");
|
|
|
23
37
|
*/
|
|
24
38
|
function buildAndEvaluateModule(entryPath, handoff) {
|
|
25
39
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
-
|
|
27
|
-
// Default esbuild configuration
|
|
28
|
-
const defaultBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_SSR_BUILD_CONFIG), { entryPoints: [entryPath] });
|
|
29
|
-
// Apply user's SSR build config hook if provided
|
|
30
|
-
const buildConfig = ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.ssrBuildConfig)
|
|
31
|
-
? handoff.config.hooks.ssrBuildConfig(defaultBuildConfig)
|
|
32
|
-
: defaultBuildConfig;
|
|
33
|
-
// Compile the module
|
|
40
|
+
const buildConfig = createModuleBuildConfig(entryPath, handoff);
|
|
34
41
|
const build = yield esbuild_1.default.build(buildConfig);
|
|
35
42
|
const { text: code } = build.outputFiles[0];
|
|
36
|
-
|
|
37
|
-
const mod = { exports: {} };
|
|
38
|
-
const func = new Function('require', 'module', 'exports', code);
|
|
39
|
-
func(require, mod, mod.exports);
|
|
40
|
-
return mod;
|
|
43
|
+
return evaluateBuiltModule(code);
|
|
41
44
|
});
|
|
42
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Builds and evaluates a module synchronously using esbuild.
|
|
48
|
+
* Used by config/runtime code paths that must remain synchronous.
|
|
49
|
+
*/
|
|
50
|
+
function buildAndEvaluateModuleSync(entryPath, handoff) {
|
|
51
|
+
const buildConfig = createModuleBuildConfig(entryPath, handoff);
|
|
52
|
+
const build = esbuild_1.default.buildSync(buildConfig);
|
|
53
|
+
const { text: code } = build.outputFiles[0];
|
|
54
|
+
return evaluateBuiltModule(code);
|
|
55
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SlotMetadata } from '../preview/component';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts the PascalCase component name from a file path.
|
|
4
|
+
* e.g. `/path/to/Button.tsx` -> `Button`
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractComponentName(filePath: string): string;
|
|
7
|
+
export interface GenerateUsageSnippetOptions {
|
|
8
|
+
componentName: string;
|
|
9
|
+
properties: Record<string, SlotMetadata>;
|
|
10
|
+
previewValues: Record<string, any>;
|
|
11
|
+
/** Template filename for the import path (e.g. "Button.tsx") */
|
|
12
|
+
templateFileName?: string;
|
|
13
|
+
importStatement?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generates a JSX usage snippet showing how to consume a React component
|
|
17
|
+
* with the given preview values.
|
|
18
|
+
*
|
|
19
|
+
* Iterates over the union of preview values and schema properties so that
|
|
20
|
+
* props not present in the preview are still represented when they carry
|
|
21
|
+
* a meaningful default or are required.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateUsageSnippet(options: GenerateUsageSnippetOptions): string;
|
|
@@ -0,0 +1,294 @@
|
|
|
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.extractComponentName = extractComponentName;
|
|
7
|
+
exports.generateUsageSnippet = generateUsageSnippet;
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const component_1 = require("../preview/component");
|
|
10
|
+
const CHILDREN_PROP = 'children';
|
|
11
|
+
const INDENT = ' ';
|
|
12
|
+
function isValidIdentifier(key) {
|
|
13
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Formats a prop value as a JSX attribute string.
|
|
17
|
+
* Returns the right-hand side only (e.g. `"hello"`, `{42}`, `{true}`).
|
|
18
|
+
*/
|
|
19
|
+
function formatPropValue(value, type) {
|
|
20
|
+
if (value === undefined || value === null) {
|
|
21
|
+
return '{undefined}';
|
|
22
|
+
}
|
|
23
|
+
switch (type) {
|
|
24
|
+
case component_1.SlotType.BOOLEAN:
|
|
25
|
+
return typeof value === 'boolean' ? `{${value}}` : `{${Boolean(value)}}`;
|
|
26
|
+
case component_1.SlotType.NUMBER:
|
|
27
|
+
return `{${Number(value)}}`;
|
|
28
|
+
case component_1.SlotType.FUNCTION:
|
|
29
|
+
return '{() => { /* handler */ }}';
|
|
30
|
+
case component_1.SlotType.OBJECT:
|
|
31
|
+
return `{${formatObjectLiteral(value)}}`;
|
|
32
|
+
case component_1.SlotType.ARRAY:
|
|
33
|
+
return `{${formatArrayLiteral(value)}}`;
|
|
34
|
+
case component_1.SlotType.TEXT:
|
|
35
|
+
case component_1.SlotType.IMAGE:
|
|
36
|
+
case component_1.SlotType.BUTTON:
|
|
37
|
+
case component_1.SlotType.ENUM:
|
|
38
|
+
default:
|
|
39
|
+
if (typeof value === 'string') {
|
|
40
|
+
return `"${escapeJsxString(value)}"`;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === 'boolean') {
|
|
43
|
+
return `{${value}}`;
|
|
44
|
+
}
|
|
45
|
+
if (typeof value === 'number') {
|
|
46
|
+
return `{${value}}`;
|
|
47
|
+
}
|
|
48
|
+
if (typeof value === 'object') {
|
|
49
|
+
return `{${JSON.stringify(value, null, 2)}}`;
|
|
50
|
+
}
|
|
51
|
+
return `"${escapeJsxString(String(value))}"`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function escapeJsxString(str) {
|
|
55
|
+
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
56
|
+
}
|
|
57
|
+
function formatObjectLiteral(value) {
|
|
58
|
+
if (typeof value !== 'object' || value === null) {
|
|
59
|
+
return JSON.stringify(value);
|
|
60
|
+
}
|
|
61
|
+
const entries = Object.entries(value);
|
|
62
|
+
if (entries.length === 0)
|
|
63
|
+
return '{}';
|
|
64
|
+
if (entries.length <= 3) {
|
|
65
|
+
const inline = entries
|
|
66
|
+
.map(([k, v]) => `${isValidIdentifier(k) ? k : JSON.stringify(k)}: ${JSON.stringify(v)}`)
|
|
67
|
+
.join(', ');
|
|
68
|
+
if (inline.length <= 60)
|
|
69
|
+
return `{ ${inline} }`;
|
|
70
|
+
}
|
|
71
|
+
return JSON.stringify(value, null, 2);
|
|
72
|
+
}
|
|
73
|
+
function formatArrayLiteral(value) {
|
|
74
|
+
if (!Array.isArray(value))
|
|
75
|
+
return JSON.stringify(value);
|
|
76
|
+
if (value.length === 0)
|
|
77
|
+
return '[]';
|
|
78
|
+
const inline = JSON.stringify(value);
|
|
79
|
+
if (inline.length <= 60)
|
|
80
|
+
return inline;
|
|
81
|
+
return JSON.stringify(value, null, 2);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Determines whether a prop value matches its declared default and can be omitted.
|
|
85
|
+
*/
|
|
86
|
+
function matchesDefault(value, slotDefault) {
|
|
87
|
+
if (slotDefault === undefined || slotDefault === null)
|
|
88
|
+
return false;
|
|
89
|
+
if (typeof value === typeof slotDefault && value === slotDefault)
|
|
90
|
+
return true;
|
|
91
|
+
// SlotMetadata.default is typed as `string | undefined`, but runtime values
|
|
92
|
+
// may be numbers or booleans. Do a loose string comparison as a fallback.
|
|
93
|
+
if (String(value) === String(slotDefault))
|
|
94
|
+
return true;
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Extracts the PascalCase component name from a file path.
|
|
99
|
+
* e.g. `/path/to/Button.tsx` -> `Button`
|
|
100
|
+
*/
|
|
101
|
+
function extractComponentName(filePath) {
|
|
102
|
+
const basename = path_1.default.basename(filePath);
|
|
103
|
+
const ext = path_1.default.extname(basename);
|
|
104
|
+
return basename.slice(0, basename.length - ext.length);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Generates a JSX usage snippet showing how to consume a React component
|
|
108
|
+
* with the given preview values.
|
|
109
|
+
*
|
|
110
|
+
* Iterates over the union of preview values and schema properties so that
|
|
111
|
+
* props not present in the preview are still represented when they carry
|
|
112
|
+
* a meaningful default or are required.
|
|
113
|
+
*/
|
|
114
|
+
function generateUsageSnippet(options) {
|
|
115
|
+
var _a;
|
|
116
|
+
const { componentName, properties, previewValues, templateFileName, importStatement } = options;
|
|
117
|
+
const importPath = `./${templateFileName ? path_1.default.basename(templateFileName, path_1.default.extname(templateFileName)) : componentName}`;
|
|
118
|
+
const attrs = [];
|
|
119
|
+
let childrenValue = undefined;
|
|
120
|
+
let hasChildren = false;
|
|
121
|
+
// Merge all known prop keys: preview values first (preserve order), then
|
|
122
|
+
// any additional keys from the property schema that weren't in the preview.
|
|
123
|
+
const allKeys = new Set([...Object.keys(previewValues), ...Object.keys(properties)]);
|
|
124
|
+
for (const key of allKeys) {
|
|
125
|
+
const slot = properties[key];
|
|
126
|
+
const inPreview = key in previewValues;
|
|
127
|
+
const value = inPreview ? previewValues[key] : resolveSchemaValue(slot);
|
|
128
|
+
// Nothing to show for this prop
|
|
129
|
+
if (value === undefined || value === null) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (key === CHILDREN_PROP) {
|
|
133
|
+
hasChildren = true;
|
|
134
|
+
childrenValue = value;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// Skip function props that weren't explicitly provided in preview values
|
|
138
|
+
if (!inPreview && (slot === null || slot === void 0 ? void 0 : slot.type) === component_1.SlotType.FUNCTION) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// Omit props that match their declared default
|
|
142
|
+
if (slot && matchesDefault(value, slot.default)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const type = (_a = slot === null || slot === void 0 ? void 0 : slot.type) !== null && _a !== void 0 ? _a : inferSlotType(value);
|
|
146
|
+
// Boolean true → JSX shorthand: <Comp disabled />
|
|
147
|
+
if (type === component_1.SlotType.BOOLEAN && value === true) {
|
|
148
|
+
attrs.push(key);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
// Boolean false → omit (implicit JSX default)
|
|
152
|
+
if (type === component_1.SlotType.BOOLEAN && value === false) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
attrs.push(`${key}=${formatPropValue(value, type)}`);
|
|
156
|
+
}
|
|
157
|
+
const lines = [];
|
|
158
|
+
lines.push(importStatement || `import ${componentName} from '${importPath}';`);
|
|
159
|
+
lines.push('');
|
|
160
|
+
if (hasChildren && childrenValue !== undefined && childrenValue !== null && childrenValue !== '') {
|
|
161
|
+
const openTag = attrs.length > 0 ? `<${componentName} ${attrs.join(' ')}>` : `<${componentName}>`;
|
|
162
|
+
const childStr = typeof childrenValue === 'string'
|
|
163
|
+
? childrenValue
|
|
164
|
+
: typeof childrenValue === 'number' || typeof childrenValue === 'boolean'
|
|
165
|
+
? String(childrenValue)
|
|
166
|
+
: '{/* children */}';
|
|
167
|
+
if (childStr.length <= 40 && !childStr.includes('\n')) {
|
|
168
|
+
lines.push(`${openTag}${childStr}</${componentName}>`);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
lines.push(openTag);
|
|
172
|
+
for (const childLine of childStr.split('\n')) {
|
|
173
|
+
lines.push(`${INDENT}${childLine}`);
|
|
174
|
+
}
|
|
175
|
+
lines.push(`</${componentName}>`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
if (attrs.length === 0) {
|
|
180
|
+
lines.push(`<${componentName} />`);
|
|
181
|
+
}
|
|
182
|
+
else if (attrs.length <= 3 && attrs.join(' ').length <= 60) {
|
|
183
|
+
lines.push(`<${componentName} ${attrs.join(' ')} />`);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
lines.push(`<${componentName}`);
|
|
187
|
+
for (const attr of attrs) {
|
|
188
|
+
lines.push(`${INDENT}${attr}`);
|
|
189
|
+
}
|
|
190
|
+
lines.push('/>');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return lines.join('\n');
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Resolves a display value for a schema-only property (not present in preview values).
|
|
197
|
+
*
|
|
198
|
+
* - Uses the declared default when available and non-trivial.
|
|
199
|
+
* - For required props without a default, generates a type-appropriate placeholder.
|
|
200
|
+
* - Returns `undefined` for optional props with no useful default (they are skipped).
|
|
201
|
+
*/
|
|
202
|
+
function resolveSchemaValue(slot) {
|
|
203
|
+
var _a;
|
|
204
|
+
if (!slot)
|
|
205
|
+
return undefined;
|
|
206
|
+
// Use explicit default if it's non-trivial
|
|
207
|
+
if (slot.default !== undefined && slot.default !== null && slot.default !== '') {
|
|
208
|
+
return coerceDefault(slot.default, slot.type);
|
|
209
|
+
}
|
|
210
|
+
// Required props must appear even without a default
|
|
211
|
+
if ((_a = slot.rules) === null || _a === void 0 ? void 0 : _a.required) {
|
|
212
|
+
return getPlaceholder(slot);
|
|
213
|
+
}
|
|
214
|
+
// Optional with no default → omit
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Coerces a string default from SlotMetadata into the appropriate JS type
|
|
219
|
+
* for code-generation purposes.
|
|
220
|
+
*/
|
|
221
|
+
function coerceDefault(value, type) {
|
|
222
|
+
if (typeof value !== 'string')
|
|
223
|
+
return value;
|
|
224
|
+
switch (type) {
|
|
225
|
+
case component_1.SlotType.BOOLEAN:
|
|
226
|
+
return value === 'true';
|
|
227
|
+
case component_1.SlotType.NUMBER: {
|
|
228
|
+
const n = Number(value);
|
|
229
|
+
return Number.isNaN(n) ? value : n;
|
|
230
|
+
}
|
|
231
|
+
default:
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Generates a type-appropriate placeholder value for required props
|
|
237
|
+
* that have no default and no preview value.
|
|
238
|
+
*/
|
|
239
|
+
function getPlaceholder(slot) {
|
|
240
|
+
switch (slot.type) {
|
|
241
|
+
case component_1.SlotType.ENUM: {
|
|
242
|
+
const member = extractFirstEnumMember(slot);
|
|
243
|
+
return member !== null && member !== void 0 ? member : 'value';
|
|
244
|
+
}
|
|
245
|
+
case component_1.SlotType.TEXT:
|
|
246
|
+
case component_1.SlotType.IMAGE:
|
|
247
|
+
return '...';
|
|
248
|
+
case component_1.SlotType.NUMBER:
|
|
249
|
+
return 0;
|
|
250
|
+
case component_1.SlotType.BOOLEAN:
|
|
251
|
+
return true;
|
|
252
|
+
case component_1.SlotType.ARRAY:
|
|
253
|
+
return [];
|
|
254
|
+
case component_1.SlotType.OBJECT:
|
|
255
|
+
return {};
|
|
256
|
+
case component_1.SlotType.FUNCTION:
|
|
257
|
+
return undefined; // handled separately
|
|
258
|
+
default:
|
|
259
|
+
return '...';
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Attempts to extract the first enum member from a SlotMetadata's deepType
|
|
264
|
+
* (e.g. `"primary"` from `'primary' | 'secondary' | 'danger'`).
|
|
265
|
+
*/
|
|
266
|
+
function extractFirstEnumMember(slot) {
|
|
267
|
+
var _a, _b;
|
|
268
|
+
// deepType.members contains the enum values when available
|
|
269
|
+
if (((_a = slot.deepType) === null || _a === void 0 ? void 0 : _a.kind) === 'enum' && ((_b = slot.deepType.members) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
270
|
+
const raw = slot.deepType.members[0];
|
|
271
|
+
// Members are stored as quoted strings like `"primary"` — strip quotes
|
|
272
|
+
return typeof raw === 'string' ? raw.replace(/^["']|["']$/g, '') : undefined;
|
|
273
|
+
}
|
|
274
|
+
// Fall back to parsing the generic display string (e.g. `"primary" | "secondary"`)
|
|
275
|
+
if (slot.generic) {
|
|
276
|
+
const match = slot.generic.match(/["']([^"']+)["']/);
|
|
277
|
+
if (match)
|
|
278
|
+
return match[1];
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
function inferSlotType(value) {
|
|
283
|
+
if (typeof value === 'boolean')
|
|
284
|
+
return component_1.SlotType.BOOLEAN;
|
|
285
|
+
if (typeof value === 'number')
|
|
286
|
+
return component_1.SlotType.NUMBER;
|
|
287
|
+
if (typeof value === 'function')
|
|
288
|
+
return component_1.SlotType.FUNCTION;
|
|
289
|
+
if (Array.isArray(value))
|
|
290
|
+
return component_1.SlotType.ARRAY;
|
|
291
|
+
if (typeof value === 'object' && value !== null)
|
|
292
|
+
return component_1.SlotType.OBJECT;
|
|
293
|
+
return component_1.SlotType.TEXT;
|
|
294
|
+
}
|
|
@@ -7,21 +7,21 @@ const createViteLogger = () => {
|
|
|
7
7
|
return {
|
|
8
8
|
hasWarned: false,
|
|
9
9
|
info(msg, options) {
|
|
10
|
-
logger_1.Logger.info(msg);
|
|
10
|
+
logger_1.Logger.info(msg, 'vite');
|
|
11
11
|
},
|
|
12
12
|
warn(msg, options) {
|
|
13
13
|
this.hasWarned = true;
|
|
14
|
-
logger_1.Logger.warn(msg);
|
|
14
|
+
logger_1.Logger.warn(msg, 'vite');
|
|
15
15
|
},
|
|
16
16
|
warnOnce(msg, options) {
|
|
17
17
|
if (warnedMessages.has(msg))
|
|
18
18
|
return;
|
|
19
19
|
warnedMessages.add(msg);
|
|
20
20
|
this.hasWarned = true;
|
|
21
|
-
logger_1.Logger.warn(msg);
|
|
21
|
+
logger_1.Logger.warn(msg, 'vite');
|
|
22
22
|
},
|
|
23
23
|
error(msg, options) {
|
|
24
|
-
logger_1.Logger.error(msg);
|
|
24
|
+
logger_1.Logger.error(msg, undefined, 'vite');
|
|
25
25
|
},
|
|
26
26
|
clearScreen(type) {
|
|
27
27
|
// No-op to preserve terminal history
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
// vite.config.ts
|
|
4
4
|
const vite_1 = require("vite");
|
|
5
|
+
const vite_logger_1 = require("./utils/vite-logger");
|
|
5
6
|
exports.default = (0, vite_1.defineConfig)({
|
|
7
|
+
customLogger: (0, vite_logger_1.createViteLogger)(),
|
|
6
8
|
publicDir: false,
|
|
7
9
|
build: {
|
|
8
10
|
lib: {
|
package/dist/types/config.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type Handlebars from 'handlebars';
|
|
|
3
3
|
import { Types as HandoffTypes } from 'handoff-core';
|
|
4
4
|
import { InlineConfig } from 'vite';
|
|
5
5
|
import { SlotMetadata } from '../transformers/preview/component';
|
|
6
|
-
import { ComponentListObject, TransformComponentTokensResult } from '../transformers/preview/types';
|
|
6
|
+
import { ComponentListObject, PatternListObject, TransformComponentTokensResult } from '../transformers/preview/types';
|
|
7
7
|
import { ValidationResult } from './preview';
|
|
8
8
|
export interface ImageStyle {
|
|
9
9
|
name: string;
|
|
@@ -65,13 +65,19 @@ export interface NextAppConfig {
|
|
|
65
65
|
theme?: string;
|
|
66
66
|
title: string;
|
|
67
67
|
client: string;
|
|
68
|
-
google_tag_manager
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
google_tag_manager?: string | null | undefined;
|
|
69
|
+
googleTagManager?: string | null | undefined;
|
|
70
|
+
type_copy?: string;
|
|
71
|
+
typeCopy?: string;
|
|
72
|
+
type_sort?: string[];
|
|
73
|
+
typeSort?: string[];
|
|
74
|
+
color_sort?: string[];
|
|
75
|
+
colorSort?: string[];
|
|
72
76
|
breakpoints: Breakpoints;
|
|
73
|
-
component_sort
|
|
74
|
-
|
|
77
|
+
component_sort?: string[];
|
|
78
|
+
componentSort?: string[];
|
|
79
|
+
base_path?: string;
|
|
80
|
+
basePath?: string;
|
|
75
81
|
attribution: boolean;
|
|
76
82
|
ports?: {
|
|
77
83
|
app: number;
|
|
@@ -80,7 +86,9 @@ export interface NextAppConfig {
|
|
|
80
86
|
}
|
|
81
87
|
export interface Config {
|
|
82
88
|
dev_access_token?: string | null | undefined;
|
|
89
|
+
devAccessToken?: string | null | undefined;
|
|
83
90
|
figma_project_id?: string | null | undefined;
|
|
91
|
+
figmaProjectId?: string | null | undefined;
|
|
84
92
|
exportsOutputDirectory?: string;
|
|
85
93
|
sitesOutputDirectory?: string;
|
|
86
94
|
useVariables?: boolean;
|
|
@@ -123,6 +131,12 @@ export interface Config {
|
|
|
123
131
|
* @example ["components/button", "components/input"]
|
|
124
132
|
*/
|
|
125
133
|
components?: string[];
|
|
134
|
+
/**
|
|
135
|
+
* Array of pattern paths to be included in the build.
|
|
136
|
+
* Patterns compose multiple component previews into single-page views.
|
|
137
|
+
* @example ["patterns/hero-section", "patterns"]
|
|
138
|
+
*/
|
|
139
|
+
patterns?: string[];
|
|
126
140
|
};
|
|
127
141
|
/**
|
|
128
142
|
* Configuration for asset zip file download links
|
|
@@ -140,6 +154,10 @@ export interface Config {
|
|
|
140
154
|
*/
|
|
141
155
|
logos?: string;
|
|
142
156
|
};
|
|
157
|
+
assetsZipLinks?: {
|
|
158
|
+
icons?: string;
|
|
159
|
+
logos?: string;
|
|
160
|
+
};
|
|
143
161
|
/**
|
|
144
162
|
* Configuration hooks for extending functionality
|
|
145
163
|
*/
|
|
@@ -278,6 +296,10 @@ export interface RuntimeConfigComponentOptions {
|
|
|
278
296
|
};
|
|
279
297
|
};
|
|
280
298
|
}
|
|
299
|
+
export interface ConfigFileEntry {
|
|
300
|
+
kind: string;
|
|
301
|
+
entityId: string;
|
|
302
|
+
}
|
|
281
303
|
export interface RuntimeConfig {
|
|
282
304
|
entries?: {
|
|
283
305
|
scss?: string;
|
|
@@ -286,6 +308,9 @@ export interface RuntimeConfig {
|
|
|
286
308
|
components: {
|
|
287
309
|
[id: string]: ComponentListObject;
|
|
288
310
|
};
|
|
311
|
+
patterns: {
|
|
312
|
+
[id: string]: PatternListObject;
|
|
313
|
+
};
|
|
289
314
|
};
|
|
290
315
|
options: {
|
|
291
316
|
[key: string]: RuntimeConfigComponentOptions;
|
package/dist/types/preview.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface PreviewObject {
|
|
|
54
54
|
[key: string]: string;
|
|
55
55
|
};
|
|
56
56
|
url: string;
|
|
57
|
+
usage?: string;
|
|
57
58
|
};
|
|
58
59
|
};
|
|
59
60
|
properties?: {
|
|
@@ -61,6 +62,7 @@ export interface PreviewObject {
|
|
|
61
62
|
};
|
|
62
63
|
code: string;
|
|
63
64
|
html?: string;
|
|
65
|
+
usage?: string;
|
|
64
66
|
format: string;
|
|
65
67
|
variant?: Record<string, string>;
|
|
66
68
|
options?: {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatDurationMs = void 0;
|
|
4
|
+
/** Human-readable duration for CLI build logs (ms under 1s, seconds with 2 decimals otherwise). */
|
|
5
|
+
const formatDurationMs = (elapsedMs) => elapsedMs < 1000 ? `${Math.round(elapsedMs)}ms` : `${(elapsedMs / 1000).toFixed(2)}s`;
|
|
6
|
+
exports.formatDurationMs = formatDurationMs;
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -1,13 +1,50 @@
|
|
|
1
|
+
/** Logical source of a log line — used for a fixed-width–style colored tag after the timestamp. */
|
|
2
|
+
export type LogScope = 'handoff' | 'vite' | 'next';
|
|
3
|
+
/**
|
|
4
|
+
* Env (e.g. project `.env` via `dotenv/config`):
|
|
5
|
+
* - `HANDOFF_LOG_LEVEL` — `debug` | `info` | `warn` | `error` | `silent` (default: `info`).
|
|
6
|
+
* Controls minimum severity: at `warn`, info/success/debug lines are hidden; at `error`, only errors.
|
|
7
|
+
* - `HANDOFF_LOG_SCOPES` — comma-separated allowlist: `handoff`, `vite`, `next`. Defaults to all three if unset or empty.
|
|
8
|
+
*
|
|
9
|
+
* Programmatic `Logger.init({ debug: true })` (Handoff `debug` flag) forces debug-level verbosity unless overridden.
|
|
10
|
+
*/
|
|
1
11
|
export declare class Logger {
|
|
12
|
+
/** From Handoff constructor; enables debug lines together with env. */
|
|
2
13
|
private static debugMode;
|
|
14
|
+
/** Minimum message severity to print: 0=debug, 1=info, 2=warn, 3=error. `silent` uses 99. */
|
|
15
|
+
private static severityThreshold;
|
|
16
|
+
/** Enabled scopes; defaults to all of `handoff`, `vite`, `next`. */
|
|
17
|
+
private static scopesFilter;
|
|
3
18
|
static init(options?: {
|
|
4
19
|
debug?: boolean;
|
|
5
20
|
}): void;
|
|
21
|
+
/** Re-read `process.env` (useful if env is loaded after first `init`). */
|
|
22
|
+
static applyEnvConfig(): void;
|
|
23
|
+
private static scopeEnabled;
|
|
24
|
+
/** Message severities: debug=0, info-tier=1, warn=2, error=3 */
|
|
25
|
+
private static shouldEmit;
|
|
6
26
|
private static getTimestamp;
|
|
7
|
-
|
|
8
|
-
static
|
|
9
|
-
static
|
|
10
|
-
static
|
|
11
|
-
|
|
27
|
+
/** Short label shown after the timestamp so you can scan for handoff vs vite vs next. */
|
|
28
|
+
private static scopeTag;
|
|
29
|
+
private static basePrefix;
|
|
30
|
+
static log(message: string, scope?: LogScope): void;
|
|
31
|
+
/**
|
|
32
|
+
* Vite often passes strings that already contain ANSI; avoid wrapping those in chalk.cyan so colors stay correct.
|
|
33
|
+
*/
|
|
34
|
+
static info(message: string, scope?: LogScope): void;
|
|
35
|
+
static success(message: string, scope?: LogScope): void;
|
|
36
|
+
static warn(message: string, scope?: LogScope): void;
|
|
37
|
+
static error(message: string, error?: any, scope?: LogScope): void;
|
|
12
38
|
static debug(message: string, data?: any): void;
|
|
39
|
+
/**
|
|
40
|
+
* Log one line from a child process with timestamp + scope tag. Child ANSI is preserved (no extra chalk on the line).
|
|
41
|
+
*/
|
|
42
|
+
static childProcessLine(line: string, scope?: LogScope): void;
|
|
43
|
+
/**
|
|
44
|
+
* Pipe stdout/stderr from a spawned child through {@link childProcessLine}. Use with
|
|
45
|
+
* `stdio: ['inherit' | 'ignore', 'pipe', 'pipe']`.
|
|
46
|
+
*/
|
|
47
|
+
static pipeChildStreams(stdout: NodeJS.ReadableStream | null, stderr: NodeJS.ReadableStream | null, scope?: LogScope): void;
|
|
48
|
+
/** Forward a completed child-process buffer (e.g. from `spawn.sync` with stdio pipe) line-by-line. */
|
|
49
|
+
static childProcessBuffer(buffer: Buffer | null | undefined, scope?: LogScope): void;
|
|
13
50
|
}
|