handoff-app 1.2.0-0 → 1.2.0-2
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/dist/app-builder/build.js +24 -10
- package/dist/app-builder/config-diff/index.d.ts +1 -1
- package/dist/app-builder/config-diff/registry.d.ts +2 -2
- package/dist/app-builder/config-diff/registry.js +2 -2
- package/dist/app-builder/config-diff/strategies/pattern.js +14 -1
- package/dist/app-builder/config-diff/types.d.ts +15 -1
- package/dist/app-builder/paths.js +18 -1
- package/dist/app-builder/watchers.js +35 -3
- package/dist/app-builder/websocket.js +10 -4
- package/dist/cli.js +1 -1
- package/dist/config/normalizers/pattern.d.ts +0 -1
- package/dist/config/normalizers/pattern.js +0 -3
- package/dist/config/runtime.js +10 -4
- package/dist/pipeline/patterns.d.ts +3 -1
- package/dist/pipeline/patterns.js +2 -2
- package/dist/transformers/plugins/csf-render.js +2 -1
- package/dist/transformers/plugins/ssr-render.js +2 -1
- package/dist/transformers/preview/component/api.d.ts +2 -13
- package/dist/transformers/preview/component/api.js +7 -46
- package/dist/transformers/preview/component/artifacts.d.ts +4 -0
- package/dist/transformers/preview/component/artifacts.js +126 -0
- package/dist/transformers/preview/component/builder.js +62 -6
- package/dist/transformers/preview/component/css.d.ts +2 -0
- package/dist/transformers/preview/component/css.js +25 -9
- package/dist/transformers/preview/component/javascript.d.ts +1 -1
- package/dist/transformers/preview/component/javascript.js +45 -19
- package/dist/transformers/preview/component/summary.d.ts +6 -0
- package/dist/transformers/preview/component/summary.js +55 -0
- package/dist/transformers/preview/component.js +3 -25
- package/dist/transformers/preview/pattern/api.d.ts +4 -0
- package/dist/transformers/preview/pattern/api.js +19 -1
- package/dist/transformers/preview/pattern/builder.d.ts +9 -1
- package/dist/transformers/preview/pattern/builder.js +123 -42
- package/dist/transformers/utils/vite-logger.js +4 -4
- package/dist/transformers/vite-config.js +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/package.json +1 -1
- package/src/app/pages/system/pattern/[pattern]/index.tsx +178 -55
- package/src/app-builder/build.ts +26 -10
- package/src/app-builder/config-diff/index.ts +1 -1
- package/src/app-builder/config-diff/registry.ts +3 -3
- package/src/app-builder/config-diff/strategies/pattern.ts +17 -2
- package/src/app-builder/config-diff/types.ts +16 -1
- package/src/app-builder/paths.ts +22 -1
- package/src/app-builder/watchers.ts +15 -3
- package/src/app-builder/websocket.ts +16 -5
- package/src/cli.ts +1 -1
- package/src/config/normalizers/pattern.ts +0 -7
- package/src/config/runtime.ts +11 -4
- package/src/pipeline/patterns.ts +5 -3
- package/src/transformers/plugins/csf-render.ts +2 -1
- package/src/transformers/plugins/ssr-render.ts +2 -1
- package/src/transformers/preview/component/api.ts +2 -50
- package/src/transformers/preview/component/artifacts.ts +131 -0
- package/src/transformers/preview/component/builder.ts +72 -6
- package/src/transformers/preview/component/css.ts +26 -10
- package/src/transformers/preview/component/javascript.ts +50 -23
- package/src/transformers/preview/component/summary.ts +54 -0
- package/src/transformers/preview/component.ts +2 -1
- package/src/transformers/preview/pattern/api.ts +17 -0
- package/src/transformers/preview/pattern/builder.ts +156 -58
- package/src/transformers/utils/vite-logger.ts +4 -4
- package/src/transformers/vite-config.ts +2 -0
- package/src/utils/duration.ts +3 -0
- package/src/utils/logger.ts +143 -16
|
@@ -107,9 +107,11 @@ const buildApp = (handoff, skipComponents) => __awaiter(void 0, void 0, void 0,
|
|
|
107
107
|
// Build app
|
|
108
108
|
const buildResult = cross_spawn_1.default.sync('npx', ['next', 'build'], {
|
|
109
109
|
cwd: appPath,
|
|
110
|
-
stdio: 'inherit',
|
|
110
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
111
111
|
env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production' }),
|
|
112
112
|
});
|
|
113
|
+
logger_1.Logger.childProcessBuffer(buildResult.stdout);
|
|
114
|
+
logger_1.Logger.childProcessBuffer(buildResult.stderr);
|
|
113
115
|
if (buildResult.status !== 0) {
|
|
114
116
|
let errorMsg = `Next.js build failed with exit code ${buildResult.status}`;
|
|
115
117
|
if (buildResult.error) {
|
|
@@ -155,19 +157,28 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
155
157
|
// create empty directory
|
|
156
158
|
yield fs_extra_1.default.ensureDir(moduleOutput);
|
|
157
159
|
}
|
|
160
|
+
logger_1.Logger.info(`Starting Next.js dev server (Turbopack) at http://${hostname}:${port}…`);
|
|
158
161
|
const nextProcess = (0, cross_spawn_1.default)('npx', ['next', 'dev', '--turbopack', '--port', String(port)], {
|
|
159
162
|
cwd: appPath,
|
|
160
|
-
stdio: 'inherit',
|
|
163
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
161
164
|
env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'development' }),
|
|
162
165
|
});
|
|
163
|
-
logger_1.Logger.
|
|
166
|
+
logger_1.Logger.pipeChildStreams(nextProcess.stdout, nextProcess.stderr);
|
|
164
167
|
nextProcess.on('error', (error) => {
|
|
165
|
-
logger_1.Logger.error(`Next.js dev process
|
|
168
|
+
logger_1.Logger.error(`Next.js dev process failed to start: ${error}`);
|
|
166
169
|
process.exit(1);
|
|
167
170
|
});
|
|
168
|
-
nextProcess.on('close', (code) => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
nextProcess.on('close', (code, signal) => {
|
|
172
|
+
if (code === 0) {
|
|
173
|
+
logger_1.Logger.success(`Next.js dev process exited normally`);
|
|
174
|
+
}
|
|
175
|
+
else if (signal) {
|
|
176
|
+
logger_1.Logger.warn(`Next.js dev process stopped (${signal})`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
logger_1.Logger.error(`Next.js dev process exited with code ${code}`);
|
|
180
|
+
}
|
|
181
|
+
process.exit(code !== null && code !== void 0 ? code : 1);
|
|
171
182
|
});
|
|
172
183
|
const wss = yield (0, websocket_1.createWebSocketServer)((_d = (_c = handoff.config.app.ports) === null || _c === void 0 ? void 0 : _c.websocket) !== null && _d !== void 0 ? _d : 3001);
|
|
173
184
|
const chokidarConfig = {
|
|
@@ -205,12 +216,15 @@ const devApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
205
216
|
}
|
|
206
217
|
// Persist client configuration
|
|
207
218
|
yield (0, client_config_1.persistClientConfig)(handoff);
|
|
208
|
-
|
|
209
|
-
|
|
219
|
+
const devPort = (_b = (_a = handoff.config.app.ports) === null || _a === void 0 ? void 0 : _a.app) !== null && _b !== void 0 ? _b : 3000;
|
|
220
|
+
logger_1.Logger.info(`Starting Next.js dev server (Turbopack) on port ${devPort}…`);
|
|
221
|
+
const devResult = cross_spawn_1.default.sync('npx', ['next', 'dev', '--turbopack', '--port', String(devPort)], {
|
|
210
222
|
cwd: appPath,
|
|
211
|
-
stdio: 'inherit',
|
|
223
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
212
224
|
env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'development' }),
|
|
213
225
|
});
|
|
226
|
+
logger_1.Logger.childProcessBuffer(devResult.stdout);
|
|
227
|
+
logger_1.Logger.childProcessBuffer(devResult.stderr);
|
|
214
228
|
if (devResult.status !== 0) {
|
|
215
229
|
let errorMsg = `Next.js dev failed with exit code ${devResult.status}`;
|
|
216
230
|
if (devResult.error) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ConfigDiffStrategy, RebuildHandle } from './types';
|
|
1
|
+
export type { ConfigDiffStrategy, FinalizeContext, RebuildHandle } from './types';
|
|
2
2
|
export { type MapSnapshot, diffMapSnapshots, stableStringify } from './snapshot';
|
|
3
3
|
export { patternDiffStrategy } from './strategies/pattern';
|
|
4
4
|
export { getAllStrategies, getStrategy, runAllFinalizers } from './registry';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Handoff from '../..';
|
|
2
|
-
import { ConfigDiffStrategy } from './types';
|
|
2
|
+
import { ConfigDiffStrategy, FinalizeContext } from './types';
|
|
3
3
|
export declare const getStrategy: (kind: string) => ConfigDiffStrategy | undefined;
|
|
4
4
|
export declare const getAllStrategies: () => ConfigDiffStrategy[];
|
|
5
5
|
/**
|
|
6
6
|
* Runs every registered strategy finalizer.
|
|
7
7
|
* Called after component rebuilds so derived artifacts stay up-to-date.
|
|
8
8
|
*/
|
|
9
|
-
export declare const runAllFinalizers: (handoff: Handoff) => Promise<void>;
|
|
9
|
+
export declare const runAllFinalizers: (handoff: Handoff, context?: FinalizeContext) => Promise<void>;
|
|
@@ -22,9 +22,9 @@ exports.getAllStrategies = getAllStrategies;
|
|
|
22
22
|
* Runs every registered strategy finalizer.
|
|
23
23
|
* Called after component rebuilds so derived artifacts stay up-to-date.
|
|
24
24
|
*/
|
|
25
|
-
const runAllFinalizers = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const runAllFinalizers = (handoff, context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
26
|
for (const strategy of strategies.values()) {
|
|
27
|
-
yield strategy.finalize(handoff);
|
|
27
|
+
yield strategy.finalize(handoff, context);
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
exports.runAllFinalizers = runAllFinalizers;
|
|
@@ -35,6 +35,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
35
35
|
exports.patternDiffStrategy = void 0;
|
|
36
36
|
const patterns_1 = require("../../../pipeline/patterns");
|
|
37
37
|
const builder_1 = __importStar(require("../../../transformers/preview/component/builder"));
|
|
38
|
+
const builder_2 = require("../../../transformers/preview/pattern/builder");
|
|
38
39
|
const logger_1 = require("../../../utils/logger");
|
|
39
40
|
const snapshot_1 = require("../snapshot");
|
|
40
41
|
/**
|
|
@@ -85,8 +86,20 @@ exports.patternDiffStrategy = {
|
|
|
85
86
|
},
|
|
86
87
|
};
|
|
87
88
|
},
|
|
88
|
-
finalize(handoff) {
|
|
89
|
+
finalize(handoff, context) {
|
|
89
90
|
return __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
if (context === null || context === void 0 ? void 0 : context.skipPatternFinalizer) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const componentIds = context === null || context === void 0 ? void 0 : context.patternRebuildComponentIds;
|
|
95
|
+
if (componentIds === null || componentIds === void 0 ? void 0 : componentIds.length) {
|
|
96
|
+
const patternIds = (0, builder_2.getPatternIdsReferencingComponents)(handoff, componentIds);
|
|
97
|
+
if (patternIds.length === 0) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
yield (0, patterns_1.buildPatterns)(handoff, { onlyPatternIds: new Set(patternIds) });
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
90
103
|
yield (0, patterns_1.buildPatterns)(handoff);
|
|
91
104
|
});
|
|
92
105
|
},
|
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import Handoff from '../..';
|
|
2
|
+
/**
|
|
3
|
+
* Optional hints for post-build finalizers (e.g. selective pattern rebuild).
|
|
4
|
+
*/
|
|
5
|
+
export type FinalizeContext = {
|
|
6
|
+
/**
|
|
7
|
+
* Only patterns that reference at least one of these component ids are rebuilt.
|
|
8
|
+
* When omitted, all patterns are rebuilt.
|
|
9
|
+
*/
|
|
10
|
+
patternRebuildComponentIds?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* When true, skip pattern artifact generation (no component preview HTML changed).
|
|
13
|
+
*/
|
|
14
|
+
skipPatternFinalizer?: boolean;
|
|
15
|
+
};
|
|
2
16
|
/**
|
|
3
17
|
* Handle returned by ConfigDiffStrategy.capture().
|
|
4
18
|
*
|
|
@@ -15,5 +29,5 @@ export interface RebuildHandle {
|
|
|
15
29
|
export interface ConfigDiffStrategy {
|
|
16
30
|
readonly kind: string;
|
|
17
31
|
capture(handoff: Handoff, entityId: string): RebuildHandle;
|
|
18
|
-
finalize(handoff: Handoff): Promise<void>;
|
|
32
|
+
finalize(handoff: Handoff, context?: FinalizeContext): Promise<void>;
|
|
19
33
|
}
|
|
@@ -41,6 +41,15 @@ const getAppPath = (handoff) => {
|
|
|
41
41
|
return path_1.default.resolve(handoff.modulePath, '.handoff', `${handoff.getProjectId()}`);
|
|
42
42
|
};
|
|
43
43
|
exports.getAppPath = getAppPath;
|
|
44
|
+
const mirrorDirectory = (sourcePath, destinationPath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
|
+
if (!(yield fs_extra_1.default.pathExists(sourcePath))) {
|
|
46
|
+
yield fs_extra_1.default.remove(destinationPath);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
yield fs_extra_1.default.remove(destinationPath);
|
|
50
|
+
yield fs_extra_1.default.ensureDir(path_1.default.dirname(destinationPath));
|
|
51
|
+
yield fs_extra_1.default.copy(sourcePath, destinationPath, { overwrite: true });
|
|
52
|
+
});
|
|
44
53
|
/**
|
|
45
54
|
* Copy the public dir from the working dir to the module dir.
|
|
46
55
|
*/
|
|
@@ -48,9 +57,17 @@ const syncPublicFiles = (handoff) => __awaiter(void 0, void 0, void 0, function*
|
|
|
48
57
|
const appPath = (0, exports.getAppPath)(handoff);
|
|
49
58
|
const workingPublicPath = (0, exports.getWorkingPublicPath)(handoff);
|
|
50
59
|
if (workingPublicPath) {
|
|
51
|
-
|
|
60
|
+
const destinationPublicPath = path_1.default.resolve(appPath, 'public');
|
|
61
|
+
const sourceApiPath = path_1.default.resolve(workingPublicPath, 'api');
|
|
62
|
+
const destinationApiPath = path_1.default.resolve(destinationPublicPath, 'api');
|
|
63
|
+
yield fs_extra_1.default.copy(workingPublicPath, destinationPublicPath, {
|
|
52
64
|
overwrite: true,
|
|
65
|
+
filter: (file) => {
|
|
66
|
+
const relativePath = path_1.default.relative(workingPublicPath, file);
|
|
67
|
+
return relativePath !== 'api' && !relativePath.startsWith(`api${path_1.default.sep}`);
|
|
68
|
+
},
|
|
53
69
|
});
|
|
70
|
+
yield mirrorDirectory(sourceApiPath, destinationApiPath);
|
|
54
71
|
}
|
|
55
72
|
});
|
|
56
73
|
exports.syncPublicFiles = syncPublicFiles;
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -16,7 +39,7 @@ exports.watchComponentDirectories = exports.watchRuntimeConfiguration = exports.
|
|
|
16
39
|
const chokidar_1 = __importDefault(require("chokidar"));
|
|
17
40
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
41
|
const path_1 = __importDefault(require("path"));
|
|
19
|
-
const builder_1 =
|
|
42
|
+
const builder_1 = __importStar(require("../transformers/preview/component/builder"));
|
|
20
43
|
const css_1 = require("../transformers/preview/component/css");
|
|
21
44
|
const javascript_1 = require("../transformers/preview/component/javascript");
|
|
22
45
|
const logger_1 = require("../utils/logger");
|
|
@@ -181,8 +204,17 @@ const watchRuntimeComponents = (handoff, state, runtimeComponentPathsToWatch) =>
|
|
|
181
204
|
const entryType = runtimeComponentEntryTypeByPath.get(normalizedFile);
|
|
182
205
|
const segmentToUpdate = entryType ? (0, component_1.mapEntryTypeToSegment)(entryType) : undefined;
|
|
183
206
|
const componentId = (0, component_1.resolveComponentIdForChangedFile)(handoff, file);
|
|
207
|
+
const skipPatterns = segmentToUpdate === builder_1.ComponentSegment.JavaScript ||
|
|
208
|
+
segmentToUpdate === builder_1.ComponentSegment.Style;
|
|
209
|
+
let finalizeContext;
|
|
210
|
+
if (skipPatterns) {
|
|
211
|
+
finalizeContext = { skipPatternFinalizer: true };
|
|
212
|
+
}
|
|
213
|
+
else if (componentId) {
|
|
214
|
+
finalizeContext = { patternRebuildComponentIds: [componentId] };
|
|
215
|
+
}
|
|
184
216
|
yield (0, builder_1.default)(handoff, componentId, segmentToUpdate);
|
|
185
|
-
yield (0, config_diff_1.runAllFinalizers)(handoff);
|
|
217
|
+
yield (0, config_diff_1.runAllFinalizers)(handoff, finalizeContext);
|
|
186
218
|
}
|
|
187
219
|
catch (e) {
|
|
188
220
|
logger_1.Logger.error('Error processing component:', e);
|
|
@@ -298,7 +330,7 @@ const watchComponentDirectories = (handoff, state, chokidarConfig) => {
|
|
|
298
330
|
(0, exports.watchRuntimeComponents)(handoff, state, (0, component_1.getRuntimeComponentsPathsToWatch)(handoff));
|
|
299
331
|
(0, exports.watchRuntimeConfiguration)(handoff, state);
|
|
300
332
|
yield (0, builder_1.default)(handoff, dirName);
|
|
301
|
-
yield (0, config_diff_1.runAllFinalizers)(handoff);
|
|
333
|
+
yield (0, config_diff_1.runAllFinalizers)(handoff, { patternRebuildComponentIds: [dirName] });
|
|
302
334
|
}
|
|
303
335
|
catch (e) {
|
|
304
336
|
logger_1.Logger.error('Error processing new component:', e);
|
|
@@ -20,7 +20,14 @@ const logger_1 = require("../utils/logger");
|
|
|
20
20
|
* @returns A function that accepts a message string and broadcasts it to all connected clients.
|
|
21
21
|
*/
|
|
22
22
|
const createWebSocketServer = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (port = 3001) {
|
|
23
|
+
logger_1.Logger.info(`Starting WebSocket server for live reload on port ${port}…`);
|
|
23
24
|
const wss = new ws_1.WebSocket.Server({ port });
|
|
25
|
+
wss.on('listening', () => {
|
|
26
|
+
logger_1.Logger.info(`WebSocket server listening on ws://localhost:${port} (live reload)`);
|
|
27
|
+
});
|
|
28
|
+
wss.on('error', (err) => {
|
|
29
|
+
logger_1.Logger.error(`WebSocket server failed${err instanceof Error ? `: ${err.message}` : ''}`, err instanceof Error ? err : undefined);
|
|
30
|
+
});
|
|
24
31
|
// Heartbeat function to mark a connection as alive.
|
|
25
32
|
const heartbeat = function () {
|
|
26
33
|
this.isAlive = true;
|
|
@@ -30,7 +37,7 @@ const createWebSocketServer = (...args_1) => __awaiter(void 0, [...args_1], void
|
|
|
30
37
|
const extWs = ws;
|
|
31
38
|
extWs.isAlive = true;
|
|
32
39
|
extWs.send(JSON.stringify({ type: 'WELCOME' }));
|
|
33
|
-
extWs.on('error', (error) => logger_1.Logger.error('WebSocket error:', error));
|
|
40
|
+
extWs.on('error', (error) => logger_1.Logger.error('WebSocket client error:', error));
|
|
34
41
|
extWs.on('pong', heartbeat);
|
|
35
42
|
});
|
|
36
43
|
// Periodically ping clients to ensure they are still connected
|
|
@@ -38,7 +45,7 @@ const createWebSocketServer = (...args_1) => __awaiter(void 0, [...args_1], void
|
|
|
38
45
|
wss.clients.forEach((client) => {
|
|
39
46
|
const extWs = client;
|
|
40
47
|
if (!extWs.isAlive) {
|
|
41
|
-
logger_1.Logger.
|
|
48
|
+
logger_1.Logger.debug('Terminating inactive WebSocket client');
|
|
42
49
|
return client.terminate();
|
|
43
50
|
}
|
|
44
51
|
extWs.isAlive = false;
|
|
@@ -49,10 +56,9 @@ const createWebSocketServer = (...args_1) => __awaiter(void 0, [...args_1], void
|
|
|
49
56
|
wss.on('close', () => {
|
|
50
57
|
clearInterval(pingInterval);
|
|
51
58
|
});
|
|
52
|
-
logger_1.Logger.success(`WebSocket server listening on ws://localhost:${port}`);
|
|
53
59
|
// Return a function to broadcast a message to all connected clients
|
|
54
60
|
return (message) => {
|
|
55
|
-
logger_1.Logger.
|
|
61
|
+
logger_1.Logger.debug(`Broadcasting reload to ${wss.clients.size} WebSocket client(s)`);
|
|
56
62
|
wss.clients.forEach((client) => {
|
|
57
63
|
if (client.readyState === ws_1.WebSocket.OPEN) {
|
|
58
64
|
client.send(message);
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,6 @@ type RawPatternDeclaration = Record<string, any>;
|
|
|
3
3
|
type NormalizePatternOptions = {
|
|
4
4
|
declarationPath: string;
|
|
5
5
|
fallbackId: string;
|
|
6
|
-
warn: (message: string) => void;
|
|
7
6
|
};
|
|
8
7
|
export declare const normalizePatternDeclaration: (raw: RawPatternDeclaration, options: NormalizePatternOptions) => PatternListObject;
|
|
9
8
|
export {};
|
|
@@ -13,9 +13,6 @@ const normalizePatternDeclaration = (raw, options) => {
|
|
|
13
13
|
throw new Error(`Pattern "${options.fallbackId}" in "${options.declarationPath}" has an invalid component reference at index ${index}: missing "id".`);
|
|
14
14
|
}
|
|
15
15
|
const preview = typeof ref.preview === 'string' ? ref.preview.trim() : undefined;
|
|
16
|
-
if (!preview && !ref.args) {
|
|
17
|
-
options.warn(`Pattern "${options.fallbackId}" component ref "${ref.id}" at index ${index} has neither "preview" nor "args". The component's default preview will be used.`);
|
|
18
|
-
}
|
|
19
16
|
return {
|
|
20
17
|
id: ref.id.trim(),
|
|
21
18
|
preview,
|
package/dist/config/runtime.js
CHANGED
|
@@ -217,7 +217,6 @@ const initRuntimeConfig = (handoff) => {
|
|
|
217
217
|
pattern = (0, pattern_1.normalizePatternDeclaration)(rawPattern, {
|
|
218
218
|
declarationPath: resolvedPatternConfigPath,
|
|
219
219
|
fallbackId: patternBaseName,
|
|
220
|
-
warn: (message) => logger_1.Logger.warn(message),
|
|
221
220
|
});
|
|
222
221
|
}
|
|
223
222
|
catch (err) {
|
|
@@ -313,11 +312,18 @@ const injectPatternPreviews = (result) => {
|
|
|
313
312
|
ref.resolvedPreview = ref.preview;
|
|
314
313
|
continue;
|
|
315
314
|
}
|
|
316
|
-
// Case 2: no preview AND no args -> use the component's first existing preview
|
|
315
|
+
// Case 2: no preview AND no args -> use the component's first existing preview (never assume "default")
|
|
317
316
|
if (!ref.preview && !ref.args) {
|
|
318
317
|
const previewKeys = Object.keys(component.previews || {});
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
const firstPreview = previewKeys[0];
|
|
319
|
+
if (firstPreview) {
|
|
320
|
+
ref.resolvedPreview = firstPreview;
|
|
321
|
+
ref.resolved = true;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
logger_1.Logger.warn(`Pattern "${patternId}" component ref "${ref.id}" has neither "preview" nor "args", and the component has no previews. This fragment will be skipped.`);
|
|
325
|
+
ref.resolved = false;
|
|
326
|
+
}
|
|
321
327
|
continue;
|
|
322
328
|
}
|
|
323
329
|
// Case 3: args present (with or without preview base) -> create synthetic preview
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Handoff from '..';
|
|
2
|
+
import { ProcessPatternsOptions } from '../transformers/preview/pattern/builder';
|
|
3
|
+
export type { ProcessPatternsOptions };
|
|
2
4
|
/**
|
|
3
5
|
* Builds pattern previews by composing pre-built component preview HTML.
|
|
4
6
|
* Must be called AFTER buildComponents.
|
|
5
7
|
*/
|
|
6
|
-
export declare const buildPatterns: (handoff: Handoff) => Promise<void>;
|
|
8
|
+
export declare const buildPatterns: (handoff: Handoff, options?: ProcessPatternsOptions) => Promise<void>;
|
|
@@ -15,7 +15,7 @@ const builder_1 = require("../transformers/preview/pattern/builder");
|
|
|
15
15
|
* Builds pattern previews by composing pre-built component preview HTML.
|
|
16
16
|
* Must be called AFTER buildComponents.
|
|
17
17
|
*/
|
|
18
|
-
const buildPatterns = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
-
yield (0, builder_1.processPatterns)(handoff);
|
|
18
|
+
const buildPatterns = (handoff, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
yield (0, builder_1.processPatterns)(handoff, options);
|
|
20
20
|
});
|
|
21
21
|
exports.buildPatterns = buildPatterns;
|
|
@@ -22,6 +22,7 @@ const react_element_to_jsx_string_1 = __importDefault(require("react-element-to-
|
|
|
22
22
|
const logger_1 = require("../../utils/logger");
|
|
23
23
|
const docgen_1 = require("../docgen");
|
|
24
24
|
const component_1 = require("../preview/component");
|
|
25
|
+
const css_1 = require("../preview/component/css");
|
|
25
26
|
const html_1 = require("../utils/html");
|
|
26
27
|
const module_1 = require("../utils/module");
|
|
27
28
|
const schema_1 = require("../utils/schema");
|
|
@@ -240,7 +241,7 @@ function createHtmlDocument(componentId, previewTitle, renderedHtml) {
|
|
|
240
241
|
<html lang="en">
|
|
241
242
|
<head>
|
|
242
243
|
<meta charset="UTF-8" />
|
|
243
|
-
<link rel="stylesheet" href="${(_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : ''}/api/component
|
|
244
|
+
<link rel="stylesheet" href="${(_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : ''}/api/component/${css_1.MAIN_COMPONENT_CSS_FILE}" />
|
|
244
245
|
<link rel="stylesheet" href="${(_b = process.env.HANDOFF_APP_BASE_PATH) !== null && _b !== void 0 ? _b : ''}/api/component/${componentId}.css" />
|
|
245
246
|
<link rel="stylesheet" href="${(_c = process.env.HANDOFF_APP_BASE_PATH) !== null && _c !== void 0 ? _c : ''}/assets/css/preview.css" />
|
|
246
247
|
<title>${previewTitle}</title>
|
|
@@ -21,6 +21,7 @@ const server_1 = __importDefault(require("react-dom/server"));
|
|
|
21
21
|
const vite_1 = require("vite");
|
|
22
22
|
const logger_1 = require("../../utils/logger");
|
|
23
23
|
const docgen_1 = require("../docgen");
|
|
24
|
+
const css_1 = require("../preview/component/css");
|
|
24
25
|
const build_1 = require("../utils/build");
|
|
25
26
|
const html_1 = require("../utils/html");
|
|
26
27
|
const module_1 = require("../utils/module");
|
|
@@ -117,7 +118,7 @@ function generateHtmlDocument(componentId, previewTitle, renderedHtml, clientJs,
|
|
|
117
118
|
<html lang="en">
|
|
118
119
|
<head>
|
|
119
120
|
<meta charset="UTF-8" />
|
|
120
|
-
<link rel="stylesheet" href="${(_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : ''}/api/component
|
|
121
|
+
<link rel="stylesheet" href="${(_a = process.env.HANDOFF_APP_BASE_PATH) !== null && _a !== void 0 ? _a : ''}/api/component/${css_1.MAIN_COMPONENT_CSS_FILE}" />
|
|
121
122
|
<link rel="stylesheet" href="${(_b = process.env.HANDOFF_APP_BASE_PATH) !== null && _b !== void 0 ? _b : ''}/api/component/${componentId}.css" />
|
|
122
123
|
<link rel="stylesheet" href="${(_c = process.env.HANDOFF_APP_BASE_PATH) !== null && _c !== void 0 ? _c : ''}/assets/css/preview.css" />
|
|
123
124
|
<script id="${PLUGIN_CONSTANTS.PROPS_SCRIPT_ID}" type="application/json">${JSON.stringify(props)}</script>
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import Handoff from '../../../index';
|
|
2
2
|
import { ComponentListObject, TransformComponentTokensResult } from '../types';
|
|
3
|
+
export declare const sanitizeComponentApiData: (component: TransformComponentTokensResult) => TransformComponentTokensResult;
|
|
3
4
|
export declare const getAPIPath: (handoff: Handoff) => string;
|
|
4
|
-
/**
|
|
5
|
-
* Build the preview API from the component data
|
|
6
|
-
* @param handoff
|
|
7
|
-
* @param componentData
|
|
8
|
-
*/
|
|
9
|
-
declare const writeComponentSummaryAPI: (handoff: Handoff, componentData: ComponentListObject[]) => Promise<void>;
|
|
10
5
|
export declare const writeComponentApi: (id: string, component: TransformComponentTokensResult, handoff: Handoff, preserveKeys?: string[]) => Promise<void>;
|
|
11
|
-
/**
|
|
12
|
-
* Update the main component summary API with the new component data
|
|
13
|
-
* @param handoff
|
|
14
|
-
* @param componentData
|
|
15
|
-
*/
|
|
16
|
-
export declare const updateComponentSummaryApi: (handoff: Handoff, componentData: ComponentListObject[], isFullRebuild?: boolean) => Promise<void>;
|
|
17
6
|
/**
|
|
18
7
|
* Read the component API data
|
|
19
8
|
* @param handoff
|
|
@@ -28,4 +17,4 @@ export declare const readComponentApi: (handoff: Handoff, id: string) => Promise
|
|
|
28
17
|
* @returns The component summary or null if not found
|
|
29
18
|
*/
|
|
30
19
|
export declare const readComponentMetadataApi: (handoff: Handoff, id: string) => Promise<ComponentListObject | null>;
|
|
31
|
-
export default
|
|
20
|
+
export default writeComponentApi;
|
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.readComponentMetadataApi = exports.readComponentApi = exports.
|
|
15
|
+
exports.readComponentMetadataApi = exports.readComponentApi = exports.writeComponentApi = exports.getAPIPath = exports.sanitizeComponentApiData = void 0;
|
|
16
16
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const previews_1 = require("./previews");
|
|
@@ -23,6 +23,7 @@ const sanitizeComponentApiData = (component) => {
|
|
|
23
23
|
delete sanitized.internalPatternPreviews;
|
|
24
24
|
return sanitized;
|
|
25
25
|
};
|
|
26
|
+
exports.sanitizeComponentApiData = sanitizeComponentApiData;
|
|
26
27
|
/**
|
|
27
28
|
* Merges values from a source object into a target object, returning a new object.
|
|
28
29
|
* For each key present in either object:
|
|
@@ -62,28 +63,19 @@ const getAPIPath = (handoff) => {
|
|
|
62
63
|
return apiPath;
|
|
63
64
|
};
|
|
64
65
|
exports.getAPIPath = getAPIPath;
|
|
65
|
-
/**
|
|
66
|
-
* Build the preview API from the component data
|
|
67
|
-
* @param handoff
|
|
68
|
-
* @param componentData
|
|
69
|
-
*/
|
|
70
|
-
const writeComponentSummaryAPI = (handoff, componentData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
-
componentData.sort((a, b) => a.title.localeCompare(b.title));
|
|
72
|
-
yield fs_extra_1.default.writeFile(path_1.default.resolve((0, exports.getAPIPath)(handoff), 'components.json'), JSON.stringify(componentData, null, 2));
|
|
73
|
-
});
|
|
74
66
|
const writeComponentApi = (id_1, component_1, handoff_1, ...args_1) => __awaiter(void 0, [id_1, component_1, handoff_1, ...args_1], void 0, function* (id, component, handoff, preserveKeys = []) {
|
|
75
67
|
const outputDirPath = path_1.default.resolve((0, exports.getAPIPath)(handoff), 'component');
|
|
76
68
|
const outputFilePath = path_1.default.resolve(outputDirPath, `${id}.json`);
|
|
77
|
-
const sanitizedComponent = sanitizeComponentApiData(component);
|
|
69
|
+
const sanitizedComponent = (0, exports.sanitizeComponentApiData)(component);
|
|
78
70
|
if (fs_extra_1.default.existsSync(outputFilePath)) {
|
|
79
71
|
const existingJson = yield fs_extra_1.default.readFile(outputFilePath, 'utf8');
|
|
80
72
|
if (existingJson) {
|
|
81
73
|
try {
|
|
82
|
-
const existingData = sanitizeComponentApiData(JSON.parse(existingJson));
|
|
74
|
+
const existingData = (0, exports.sanitizeComponentApiData)(JSON.parse(existingJson));
|
|
83
75
|
// Special case: always allow page to be cleared when undefined
|
|
84
76
|
// This handles the case where page slices are removed
|
|
85
77
|
const finalPreserveKeys = sanitizedComponent.page === undefined ? preserveKeys.filter((key) => key !== 'page') : preserveKeys;
|
|
86
|
-
const mergedData = sanitizeComponentApiData(updateObject(existingData, sanitizedComponent, finalPreserveKeys));
|
|
78
|
+
const mergedData = (0, exports.sanitizeComponentApiData)(updateObject(existingData, sanitizedComponent, finalPreserveKeys));
|
|
87
79
|
yield fs_extra_1.default.writeFile(outputFilePath, JSON.stringify(mergedData, null, 2));
|
|
88
80
|
return;
|
|
89
81
|
}
|
|
@@ -98,37 +90,6 @@ const writeComponentApi = (id_1, component_1, handoff_1, ...args_1) => __awaiter
|
|
|
98
90
|
yield fs_extra_1.default.writeFile(outputFilePath, JSON.stringify(sanitizedComponent, null, 2));
|
|
99
91
|
});
|
|
100
92
|
exports.writeComponentApi = writeComponentApi;
|
|
101
|
-
/**
|
|
102
|
-
* Update the main component summary API with the new component data
|
|
103
|
-
* @param handoff
|
|
104
|
-
* @param componentData
|
|
105
|
-
*/
|
|
106
|
-
const updateComponentSummaryApi = (handoff_1, componentData_1, ...args_1) => __awaiter(void 0, [handoff_1, componentData_1, ...args_1], void 0, function* (handoff, componentData, isFullRebuild = false) {
|
|
107
|
-
if (isFullRebuild) {
|
|
108
|
-
// Full rebuild: replace the entire file
|
|
109
|
-
yield writeComponentSummaryAPI(handoff, componentData);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
// Partial update: merge with existing data
|
|
113
|
-
const apiPath = path_1.default.resolve((0, exports.getAPIPath)(handoff), 'components.json');
|
|
114
|
-
let existingData = [];
|
|
115
|
-
if (fs_extra_1.default.existsSync(apiPath)) {
|
|
116
|
-
try {
|
|
117
|
-
const existing = yield fs_extra_1.default.readFile(apiPath, 'utf8');
|
|
118
|
-
existingData = JSON.parse(existing).map((component) => (Object.assign(Object.assign({}, component), { previews: (0, previews_1.getDocumentedPreviews)(component.previews) })));
|
|
119
|
-
}
|
|
120
|
-
catch (_a) {
|
|
121
|
-
// Corrupt or missing JSON — treat as empty
|
|
122
|
-
existingData = [];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Replace existing entries with same ID
|
|
126
|
-
const incomingIds = new Set(componentData.map((c) => c.id));
|
|
127
|
-
const merged = [...componentData, ...existingData.filter((c) => !incomingIds.has(c.id))];
|
|
128
|
-
// Always write the file (even if merged is empty)
|
|
129
|
-
yield writeComponentSummaryAPI(handoff, merged);
|
|
130
|
-
});
|
|
131
|
-
exports.updateComponentSummaryApi = updateComponentSummaryApi;
|
|
132
93
|
/**
|
|
133
94
|
* Read the component API data
|
|
134
95
|
* @param handoff
|
|
@@ -141,7 +102,7 @@ const readComponentApi = (handoff, id) => __awaiter(void 0, void 0, void 0, func
|
|
|
141
102
|
try {
|
|
142
103
|
const existingJson = yield fs_extra_1.default.readFile(outputFilePath, 'utf8');
|
|
143
104
|
if (existingJson) {
|
|
144
|
-
return sanitizeComponentApiData(JSON.parse(existingJson));
|
|
105
|
+
return (0, exports.sanitizeComponentApiData)(JSON.parse(existingJson));
|
|
145
106
|
}
|
|
146
107
|
}
|
|
147
108
|
catch (_) {
|
|
@@ -180,4 +141,4 @@ const readComponentMetadataApi = (handoff, id) => __awaiter(void 0, void 0, void
|
|
|
180
141
|
};
|
|
181
142
|
});
|
|
182
143
|
exports.readComponentMetadataApi = readComponentMetadataApi;
|
|
183
|
-
exports.default =
|
|
144
|
+
exports.default = exports.writeComponentApi;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import Handoff from '../../../index';
|
|
2
|
+
export declare const getComponentApiPath: (handoff: Handoff) => string;
|
|
3
|
+
export declare const removeComponentApi: (handoff: Handoff, id: string) => Promise<void>;
|
|
4
|
+
export declare const syncComponentArtifacts: (handoff: Handoff) => Promise<void>;
|