handoff-app 1.1.3 → 1.2.0-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +7 -2
- 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 +93 -0
- package/dist/app-builder/config-diff/types.d.ts +19 -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 +56 -102
- 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 +9 -0
- package/dist/config/normalizers/pattern.js +38 -0
- package/dist/config/runtime.d.ts +9 -3
- package/dist/config/runtime.js +254 -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 +6 -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 +9 -3
- package/dist/transformers/preview/component/css.d.ts +8 -0
- package/dist/transformers/preview/component/html.js +14 -5
- 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 +7 -0
- package/dist/transformers/preview/pattern/api.js +61 -0
- package/dist/transformers/preview/pattern/builder.d.ts +9 -0
- package/dist/transformers/preview/pattern/builder.js +86 -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/types/config.d.ts +32 -7
- package/dist/types/preview.d.ts +2 -0
- 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 +223 -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 +8 -2
- 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 +65 -0
- package/src/app-builder/config-diff/types.ts +21 -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 +62 -80
- 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 +63 -0
- package/src/config/runtime.ts +310 -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 +10 -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 +21 -4
- package/src/transformers/preview/component/html.ts +18 -5
- package/src/transformers/preview/component/previews.ts +12 -0
- package/src/transformers/preview/pattern/api.ts +66 -0
- package/src/transformers/preview/pattern/builder.ts +94 -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/types/README.md +1 -1
- package/src/types/config.ts +33 -7
- package/src/types/preview.ts +2 -0
- 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,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.syncPatternArtifacts = exports.writePatternSummaryApi = exports.writePatternHtml = exports.writePatternApi = exports.getPatternAPIPath = void 0;
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const getPatternAPIPath = (handoff) => {
|
|
19
|
+
const patternPath = path_1.default.resolve(handoff.workingPath, 'public/api/pattern');
|
|
20
|
+
if (!fs_extra_1.default.existsSync(patternPath)) {
|
|
21
|
+
fs_extra_1.default.mkdirSync(patternPath, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
return patternPath;
|
|
24
|
+
};
|
|
25
|
+
exports.getPatternAPIPath = getPatternAPIPath;
|
|
26
|
+
const writePatternApi = (id, pattern, handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
+
const outputDir = (0, exports.getPatternAPIPath)(handoff);
|
|
28
|
+
const outputFilePath = path_1.default.resolve(outputDir, `${id}.json`);
|
|
29
|
+
yield fs_extra_1.default.writeFile(outputFilePath, JSON.stringify(pattern, null, 2));
|
|
30
|
+
});
|
|
31
|
+
exports.writePatternApi = writePatternApi;
|
|
32
|
+
const writePatternHtml = (id, html, handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
const outputDir = (0, exports.getPatternAPIPath)(handoff);
|
|
34
|
+
const outputFilePath = path_1.default.resolve(outputDir, `${id}.html`);
|
|
35
|
+
yield fs_extra_1.default.writeFile(outputFilePath, html);
|
|
36
|
+
});
|
|
37
|
+
exports.writePatternHtml = writePatternHtml;
|
|
38
|
+
const writePatternSummaryApi = (handoff, patterns) => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
const apiPath = path_1.default.resolve(handoff.workingPath, 'public/api');
|
|
40
|
+
if (!fs_extra_1.default.existsSync(apiPath)) {
|
|
41
|
+
fs_extra_1.default.mkdirSync(apiPath, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
patterns.sort((a, b) => a.title.localeCompare(b.title));
|
|
44
|
+
yield fs_extra_1.default.writeFile(path_1.default.resolve(apiPath, 'patterns.json'), JSON.stringify(patterns, null, 2));
|
|
45
|
+
});
|
|
46
|
+
exports.writePatternSummaryApi = writePatternSummaryApi;
|
|
47
|
+
const syncPatternArtifacts = (handoff, validIds) => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
+
const validIdSet = new Set(validIds);
|
|
49
|
+
const patternPath = (0, exports.getPatternAPIPath)(handoff);
|
|
50
|
+
const entries = yield fs_extra_1.default.readdir(patternPath);
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const parsed = path_1.default.parse(entry);
|
|
53
|
+
const isPatternArtifact = parsed.ext === '.json' || parsed.ext === '.html';
|
|
54
|
+
if (!isPatternArtifact)
|
|
55
|
+
continue;
|
|
56
|
+
if (!validIdSet.has(parsed.name)) {
|
|
57
|
+
yield fs_extra_1.default.remove(path_1.default.resolve(patternPath, entry));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
exports.syncPatternArtifacts = syncPatternArtifacts;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Handoff from '../../../index';
|
|
2
|
+
import { PatternListObject } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Process all pattern declarations and compose their HTML from pre-built
|
|
5
|
+
* component preview files. This function must run AFTER buildComponents
|
|
6
|
+
* so that all component preview HTML files already exist on disk.
|
|
7
|
+
*/
|
|
8
|
+
export declare function processPatterns(handoff: Handoff): Promise<PatternListObject[]>;
|
|
9
|
+
export default processPatterns;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.processPatterns = processPatterns;
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const logger_1 = require("../../../utils/logger");
|
|
19
|
+
const api_1 = require("./api");
|
|
20
|
+
const html_1 = require("./html");
|
|
21
|
+
/**
|
|
22
|
+
* Process all pattern declarations and compose their HTML from pre-built
|
|
23
|
+
* component preview files. This function must run AFTER buildComponents
|
|
24
|
+
* so that all component preview HTML files already exist on disk.
|
|
25
|
+
*/
|
|
26
|
+
function processPatterns(handoff) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
var _a, _b, _c, _d;
|
|
29
|
+
const runtimePatterns = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.patterns) !== null && _c !== void 0 ? _c : {};
|
|
30
|
+
const patternIds = Object.keys(runtimePatterns);
|
|
31
|
+
const result = [];
|
|
32
|
+
if (patternIds.length === 0) {
|
|
33
|
+
yield (0, api_1.syncPatternArtifacts)(handoff, []);
|
|
34
|
+
yield (0, api_1.writePatternSummaryApi)(handoff, result);
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
logger_1.Logger.info(`Building ${patternIds.length} pattern(s)...`);
|
|
38
|
+
const componentOutputDir = path_1.default.resolve(handoff.workingPath, 'public/api/component');
|
|
39
|
+
const basePath = (_d = process.env.HANDOFF_APP_BASE_PATH) !== null && _d !== void 0 ? _d : '';
|
|
40
|
+
for (const patternId of patternIds) {
|
|
41
|
+
const pattern = runtimePatterns[patternId];
|
|
42
|
+
const fragments = [];
|
|
43
|
+
let hasErrors = false;
|
|
44
|
+
for (let i = 0; i < pattern.components.length; i++) {
|
|
45
|
+
const ref = pattern.components[i];
|
|
46
|
+
if (ref.resolved === false && !ref.resolvedPreview) {
|
|
47
|
+
hasErrors = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const previewKey = ref.resolvedPreview || ref.preview || 'default';
|
|
51
|
+
const htmlFileName = `${ref.id}-${previewKey}.html`;
|
|
52
|
+
const htmlFilePath = path_1.default.resolve(componentOutputDir, htmlFileName);
|
|
53
|
+
if (!fs_extra_1.default.existsSync(htmlFilePath)) {
|
|
54
|
+
const error = `Pattern "${patternId}" component[${i}] ("${ref.id}") preview file not found: ${htmlFileName}. ` +
|
|
55
|
+
`Ensure the component has a preview named "${previewKey}".`;
|
|
56
|
+
logger_1.Logger.warn(error);
|
|
57
|
+
ref.resolved = false;
|
|
58
|
+
hasErrors = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
ref.resolved = true;
|
|
62
|
+
const html = yield fs_extra_1.default.readFile(htmlFilePath, 'utf8');
|
|
63
|
+
fragments.push({ componentId: ref.id, html });
|
|
64
|
+
}
|
|
65
|
+
if (fragments.length === 0) {
|
|
66
|
+
logger_1.Logger.warn(`Pattern "${patternId}" produced no fragments. Skipping.`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (hasErrors) {
|
|
70
|
+
logger_1.Logger.warn(`Pattern "${patternId}" has missing components but will be composed from available fragments.`);
|
|
71
|
+
}
|
|
72
|
+
const composedHtml = (0, html_1.composePatternHtml)(patternId, pattern.title, fragments, basePath);
|
|
73
|
+
const patternUrl = `${patternId}.html`;
|
|
74
|
+
const patternData = Object.assign(Object.assign({}, pattern), { url: patternUrl, path: `${basePath}/api/pattern/${patternId}.json` });
|
|
75
|
+
yield (0, api_1.writePatternHtml)(patternId, composedHtml, handoff);
|
|
76
|
+
yield (0, api_1.writePatternApi)(patternId, patternData, handoff);
|
|
77
|
+
result.push(patternData);
|
|
78
|
+
logger_1.Logger.debug(`Built pattern: ${patternId} (${fragments.length} component(s))`);
|
|
79
|
+
}
|
|
80
|
+
yield (0, api_1.syncPatternArtifacts)(handoff, result.map((pattern) => pattern.id));
|
|
81
|
+
yield (0, api_1.writePatternSummaryApi)(handoff, result);
|
|
82
|
+
logger_1.Logger.info(`Finished building ${result.length} pattern(s).`);
|
|
83
|
+
return result;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
exports.default = processPatterns;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composes multiple component preview HTML documents into a single-page HTML.
|
|
3
|
+
*
|
|
4
|
+
* Each component preview may be a React SSR document (with hydration scripts
|
|
5
|
+
* and a `#root` / `#__APP_PROPS__` pair) or a static Handlebars document.
|
|
6
|
+
*
|
|
7
|
+
* This function namespaces the element ids per-fragment so multiple React
|
|
8
|
+
* components can coexist and hydrate independently on the same page.
|
|
9
|
+
*
|
|
10
|
+
* Each hydration bundle gets its own `<script type="module">` tag for full
|
|
11
|
+
* module-level isolation. This is critical because esbuild bundles React
|
|
12
|
+
* into each script using module-scoped `var` declarations, and `hydrateRoot`
|
|
13
|
+
* is asynchronous — sharing a single module scope would cause later bundles
|
|
14
|
+
* to overwrite earlier React instances before hydration completes.
|
|
15
|
+
*/
|
|
16
|
+
export declare const composePatternHtml: (_patternId: string, patternTitle: string, fragments: {
|
|
17
|
+
componentId: string;
|
|
18
|
+
html: string;
|
|
19
|
+
}[], basePath: string) => string;
|
|
@@ -0,0 +1,94 @@
|
|
|
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.composePatternHtml = void 0;
|
|
7
|
+
const escape_1 = __importDefault(require("lodash/escape"));
|
|
8
|
+
const node_html_parser_1 = require("node-html-parser");
|
|
9
|
+
/**
|
|
10
|
+
* Composes multiple component preview HTML documents into a single-page HTML.
|
|
11
|
+
*
|
|
12
|
+
* Each component preview may be a React SSR document (with hydration scripts
|
|
13
|
+
* and a `#root` / `#__APP_PROPS__` pair) or a static Handlebars document.
|
|
14
|
+
*
|
|
15
|
+
* This function namespaces the element ids per-fragment so multiple React
|
|
16
|
+
* components can coexist and hydrate independently on the same page.
|
|
17
|
+
*
|
|
18
|
+
* Each hydration bundle gets its own `<script type="module">` tag for full
|
|
19
|
+
* module-level isolation. This is critical because esbuild bundles React
|
|
20
|
+
* into each script using module-scoped `var` declarations, and `hydrateRoot`
|
|
21
|
+
* is asynchronous — sharing a single module scope would cause later bundles
|
|
22
|
+
* to overwrite earlier React instances before hydration completes.
|
|
23
|
+
*/
|
|
24
|
+
const composePatternHtml = (_patternId, patternTitle, fragments, basePath) => {
|
|
25
|
+
const cssHrefs = new Set();
|
|
26
|
+
const bodyParts = [];
|
|
27
|
+
const scriptTags = [];
|
|
28
|
+
cssHrefs.add(`${basePath}/api/component/main.css`);
|
|
29
|
+
cssHrefs.add(`${basePath}/assets/css/preview.css`);
|
|
30
|
+
const seenComponents = new Set();
|
|
31
|
+
for (let i = 0; i < fragments.length; i++) {
|
|
32
|
+
const { componentId, html } = fragments[i];
|
|
33
|
+
if (!seenComponents.has(componentId)) {
|
|
34
|
+
seenComponents.add(componentId);
|
|
35
|
+
cssHrefs.add(`${basePath}/api/component/${componentId}.css`);
|
|
36
|
+
}
|
|
37
|
+
const doc = (0, node_html_parser_1.parse)(html);
|
|
38
|
+
const suffix = `_p${i}`;
|
|
39
|
+
const namespacedRootId = `root${suffix}`;
|
|
40
|
+
const namespacedPropsId = `__APP_PROPS__${suffix}`;
|
|
41
|
+
// -- Extract props JSON (React SSR) ------------------------------------
|
|
42
|
+
const propsScript = doc.querySelector('#__APP_PROPS__');
|
|
43
|
+
let propsContent = '{}';
|
|
44
|
+
if (propsScript) {
|
|
45
|
+
propsContent = propsScript.textContent || '{}';
|
|
46
|
+
propsScript.remove();
|
|
47
|
+
}
|
|
48
|
+
// -- Extract and rewrite hydration scripts (React SSR) -----------------
|
|
49
|
+
// Each script becomes its own <script type="module"> for full isolation.
|
|
50
|
+
const moduleScripts = doc.querySelectorAll('script[type="module"]');
|
|
51
|
+
for (const script of moduleScripts) {
|
|
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
|
+
}
|
|
59
|
+
script.remove();
|
|
60
|
+
}
|
|
61
|
+
// -- Namespace the root element (React SSR) ----------------------------
|
|
62
|
+
const rootEl = doc.querySelector('#root');
|
|
63
|
+
if (rootEl) {
|
|
64
|
+
rootEl.setAttribute('id', namespacedRootId);
|
|
65
|
+
}
|
|
66
|
+
// -- Extract body content ----------------------------------------------
|
|
67
|
+
const bodyEl = doc.querySelector('body');
|
|
68
|
+
const bodyContent = bodyEl
|
|
69
|
+
? bodyEl.innerHTML.trim()
|
|
70
|
+
: rootEl
|
|
71
|
+
? rootEl.outerHTML
|
|
72
|
+
: doc.innerHTML || '';
|
|
73
|
+
bodyParts.push(` <div class="handoff-pattern-block" data-component="${componentId}" data-fragment="${i}">\n` +
|
|
74
|
+
` <script id="${namespacedPropsId}" type="application/json">${propsContent}</script>\n` +
|
|
75
|
+
` ${bodyContent}\n` +
|
|
76
|
+
` </div>`);
|
|
77
|
+
}
|
|
78
|
+
const linkTags = Array.from(cssHrefs)
|
|
79
|
+
.map((href) => ` <link rel="stylesheet" href="${href}" />`)
|
|
80
|
+
.join('\n');
|
|
81
|
+
return `<!DOCTYPE html>
|
|
82
|
+
<html lang="en">
|
|
83
|
+
<head>
|
|
84
|
+
<meta charset="UTF-8" />
|
|
85
|
+
${linkTags}
|
|
86
|
+
<title>${(0, escape_1.default)(patternTitle)}</title>
|
|
87
|
+
</head>
|
|
88
|
+
<body>
|
|
89
|
+
${bodyParts.join('\n')}
|
|
90
|
+
${scriptTags.join('\n')}
|
|
91
|
+
</body>
|
|
92
|
+
</html>`;
|
|
93
|
+
};
|
|
94
|
+
exports.composePatternHtml = composePatternHtml;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GeneratedDocs } from 'handoff-docgen';
|
|
2
2
|
import { Card } from '../../app/components/Component/Cards';
|
|
3
|
+
import type { RendererKind } from '../../declarations/types';
|
|
3
4
|
import { ValidationResult } from '../../types/preview';
|
|
4
5
|
import { Filter } from '../../utils/filter';
|
|
5
6
|
import { SlotMetadata } from './component';
|
|
@@ -93,7 +94,17 @@ export type ComponentObject = {
|
|
|
93
94
|
scss?: string;
|
|
94
95
|
/** Optional path to component template file (if available) */
|
|
95
96
|
template?: string;
|
|
97
|
+
/** Optional path to React component source for preview rendering */
|
|
98
|
+
component?: string;
|
|
99
|
+
/** Optional path to CSF story file */
|
|
100
|
+
story?: string;
|
|
101
|
+
/** Optional path to schema file (if available) */
|
|
102
|
+
schema?: string;
|
|
103
|
+
/** Optional path to templates directory */
|
|
104
|
+
templates?: string;
|
|
96
105
|
};
|
|
106
|
+
/** Optional explicit renderer id */
|
|
107
|
+
renderer?: RendererKind;
|
|
97
108
|
/** Schema describing the expected properties (props/slots) for the component */
|
|
98
109
|
properties: {
|
|
99
110
|
[key: string]: SlotMetadata;
|
|
@@ -102,10 +113,18 @@ export type ComponentObject = {
|
|
|
102
113
|
previews: {
|
|
103
114
|
[key: string]: OptionalPreviewRender;
|
|
104
115
|
};
|
|
116
|
+
/** Internal previews generated only for pattern composition, not public docs */
|
|
117
|
+
internalPatternPreviews?: {
|
|
118
|
+
[key: string]: OptionalPreviewRender;
|
|
119
|
+
};
|
|
105
120
|
/** Optional array of high-level categories for search/filtering */
|
|
106
121
|
categories?: string[];
|
|
107
122
|
/** Optional array of tags for search/filtering (e.g. "primary", "interactive") */
|
|
108
123
|
tags?: string[];
|
|
124
|
+
/** Optional markdown strings describing recommended usage patterns */
|
|
125
|
+
should_do?: string[];
|
|
126
|
+
/** Optional markdown strings describing anti-patterns to avoid */
|
|
127
|
+
should_not_do?: string[];
|
|
109
128
|
/** Optional source Figma file or node URL for the component */
|
|
110
129
|
figma?: string;
|
|
111
130
|
/** Optional canonical Figma component name or ID (used for matching back to design tokens) */
|
|
@@ -138,11 +157,15 @@ export type TransformComponentTokensResult = {
|
|
|
138
157
|
css?: string;
|
|
139
158
|
sass?: string;
|
|
140
159
|
sharedStyles?: string;
|
|
160
|
+
usage?: string;
|
|
141
161
|
title?: string;
|
|
142
162
|
description?: string;
|
|
143
163
|
previews?: {
|
|
144
164
|
[key: string]: OptionalPreviewRender;
|
|
145
165
|
};
|
|
166
|
+
internalPatternPreviews?: {
|
|
167
|
+
[key: string]: OptionalPreviewRender;
|
|
168
|
+
};
|
|
146
169
|
properties?: {
|
|
147
170
|
[key: string]: SlotMetadata;
|
|
148
171
|
};
|
|
@@ -151,8 +174,12 @@ export type TransformComponentTokensResult = {
|
|
|
151
174
|
js?: string;
|
|
152
175
|
scss?: string;
|
|
153
176
|
template?: string;
|
|
177
|
+
component?: string;
|
|
178
|
+
story?: string;
|
|
154
179
|
schema?: string;
|
|
180
|
+
templates?: string;
|
|
155
181
|
};
|
|
182
|
+
renderer?: RendererKind;
|
|
156
183
|
options?: {
|
|
157
184
|
preview?: {
|
|
158
185
|
groupBy?: string;
|
|
@@ -168,7 +195,34 @@ export type OptionalPreviewRender = {
|
|
|
168
195
|
[key: string]: string | string[] | any;
|
|
169
196
|
};
|
|
170
197
|
url: string;
|
|
198
|
+
usage?: string;
|
|
199
|
+
/** Optional source preview/story key used to derive this preview */
|
|
200
|
+
sourcePreview?: string;
|
|
171
201
|
};
|
|
172
202
|
export interface TransformedPreviewComponents {
|
|
173
203
|
[key: string]: TransformComponentTokensResult[];
|
|
174
204
|
}
|
|
205
|
+
export type PatternComponentEntry = {
|
|
206
|
+
/** Component id reference */
|
|
207
|
+
id: string;
|
|
208
|
+
/** Named preview variant to use (optional if args provided) */
|
|
209
|
+
preview?: string;
|
|
210
|
+
/** Arg overrides (optional if preview provided) */
|
|
211
|
+
args?: Record<string, any>;
|
|
212
|
+
/** Preview key resolved during injectPatternPreviews (set at build time) */
|
|
213
|
+
resolvedPreview?: string;
|
|
214
|
+
/** Whether the pattern entry resolved to a usable component preview */
|
|
215
|
+
resolved?: boolean;
|
|
216
|
+
};
|
|
217
|
+
export type PatternObject = {
|
|
218
|
+
title: string;
|
|
219
|
+
description?: string;
|
|
220
|
+
group?: string;
|
|
221
|
+
tags?: string[];
|
|
222
|
+
components: PatternComponentEntry[];
|
|
223
|
+
url?: string;
|
|
224
|
+
};
|
|
225
|
+
export type PatternListObject = {
|
|
226
|
+
id: string;
|
|
227
|
+
path: string;
|
|
228
|
+
} & PatternObject;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { OptionalPreviewRender } from '../preview/types';
|
|
2
|
+
export type StoryObject = {
|
|
3
|
+
name?: string;
|
|
4
|
+
args?: Record<string, any>;
|
|
5
|
+
argTypes?: Record<string, any>;
|
|
6
|
+
render?: (args: Record<string, any>) => any;
|
|
7
|
+
};
|
|
8
|
+
export type CsfMeta = {
|
|
9
|
+
title?: string;
|
|
10
|
+
component?: any;
|
|
11
|
+
args?: Record<string, any>;
|
|
12
|
+
argTypes?: Record<string, any>;
|
|
13
|
+
render?: (args: Record<string, any>) => any;
|
|
14
|
+
};
|
|
15
|
+
export declare function getCsfStoryEntries(moduleExports: Record<string, any>): Array<[string, StoryObject]>;
|
|
16
|
+
export declare function createCsfStoryPreviews(moduleExports: Record<string, any>): Record<string, OptionalPreviewRender>;
|
|
@@ -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;
|