handoff-app 1.2.0-0 → 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/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/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/preview/component/builder.js +52 -5
- package/dist/transformers/preview/component/css.js +6 -4
- package/dist/transformers/preview/component/javascript.d.ts +0 -1
- package/dist/transformers/preview/component/javascript.js +25 -14
- 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/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/preview/component/builder.ts +61 -5
- package/src/transformers/preview/component/css.ts +6 -5
- package/src/transformers/preview/component/javascript.ts +29 -19
- 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
|
}
|
|
@@ -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;
|
|
@@ -27,6 +27,7 @@ exports.ComponentSegment = void 0;
|
|
|
27
27
|
exports.processComponents = processComponents;
|
|
28
28
|
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
|
|
29
29
|
const cache_1 = require("../../../cache");
|
|
30
|
+
const duration_1 = require("../../../utils/duration");
|
|
30
31
|
const logger_1 = require("../../../utils/logger");
|
|
31
32
|
const schema_1 = require("../../utils/schema");
|
|
32
33
|
const types_1 = require("../types");
|
|
@@ -83,6 +84,37 @@ const ensureDefaultPreview = (data) => {
|
|
|
83
84
|
const getBuildPreviews = (data) => {
|
|
84
85
|
return Object.assign(Object.assign({}, ((data === null || data === void 0 ? void 0 : data.previews) || {})), ((data === null || data === void 0 ? void 0 : data.internalPatternPreviews) || {}));
|
|
85
86
|
};
|
|
87
|
+
/** Vite-backed segments in execution order; extend here if new steps are added. */
|
|
88
|
+
const VITE_BUILD_SEGMENTS = [
|
|
89
|
+
{ key: 'js', label: 'script' },
|
|
90
|
+
{ key: 'css', label: 'styles' },
|
|
91
|
+
{ key: 'previews', label: 'previews' },
|
|
92
|
+
];
|
|
93
|
+
const isFullViteComponentBuild = (plan) => plan.js && plan.css && plan.previews;
|
|
94
|
+
const getActiveViteBuildLabels = (plan) => VITE_BUILD_SEGMENTS.filter(({ key }) => plan[key]).map(({ label }) => label);
|
|
95
|
+
/** e.g. "a", "a and b", "a, b, and c" — common CLI copy style. */
|
|
96
|
+
const formatEnglishList = (items) => {
|
|
97
|
+
if (items.length === 0)
|
|
98
|
+
return '';
|
|
99
|
+
if (items.length === 1)
|
|
100
|
+
return items[0];
|
|
101
|
+
if (items.length === 2)
|
|
102
|
+
return `${items[0]} and ${items[1]}`;
|
|
103
|
+
return `${items.slice(0, -1).join(', ')}, and ${items[items.length - 1]}`;
|
|
104
|
+
};
|
|
105
|
+
const viteBuildStartMessage = (componentId, plan) => {
|
|
106
|
+
if (isFullViteComponentBuild(plan)) {
|
|
107
|
+
return `Building component "${componentId}"…`;
|
|
108
|
+
}
|
|
109
|
+
return `Building ${formatEnglishList(getActiveViteBuildLabels(plan))} for component "${componentId}"…`;
|
|
110
|
+
};
|
|
111
|
+
const viteBuildFinishMessage = (componentId, plan, elapsedMs) => {
|
|
112
|
+
const duration = (0, duration_1.formatDurationMs)(elapsedMs);
|
|
113
|
+
if (isFullViteComponentBuild(plan)) {
|
|
114
|
+
return `Finished building component "${componentId}" in ${duration}`;
|
|
115
|
+
}
|
|
116
|
+
return `Finished building ${formatEnglishList(getActiveViteBuildLabels(plan))} for component "${componentId}" in ${duration}`;
|
|
117
|
+
};
|
|
86
118
|
/**
|
|
87
119
|
* Returns a normalized build plan describing which component segments need rebuilding.
|
|
88
120
|
*
|
|
@@ -170,12 +202,17 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
170
202
|
if (cache) {
|
|
171
203
|
(0, cache_1.pruneRemovedComponents)(cache, allComponentIds);
|
|
172
204
|
}
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
205
|
+
const total = allComponentIds.length;
|
|
206
|
+
const toBuildCount = componentsToBuild.size;
|
|
207
|
+
const skippedCount = total - toBuildCount;
|
|
208
|
+
if (total > 0 && toBuildCount === 0) {
|
|
209
|
+
logger_1.Logger.info(`All ${total} components unchanged; skipping build`);
|
|
210
|
+
}
|
|
211
|
+
else if (skippedCount > 0 && toBuildCount > 0) {
|
|
212
|
+
logger_1.Logger.info(`Building ${toBuildCount} of ${total} components (${skippedCount} unchanged)`);
|
|
176
213
|
}
|
|
177
|
-
else if (
|
|
178
|
-
logger_1.Logger.info(`Building all ${
|
|
214
|
+
else if (toBuildCount > 0) {
|
|
215
|
+
logger_1.Logger.info(`Building all ${toBuildCount} components`);
|
|
179
216
|
}
|
|
180
217
|
else {
|
|
181
218
|
logger_1.Logger.info('All components up to date, nothing to build');
|
|
@@ -266,6 +303,12 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
266
303
|
}
|
|
267
304
|
// Components should always have at least one preview variation.
|
|
268
305
|
ensureDefaultPreview(data);
|
|
306
|
+
const runsViteBuildSteps = buildPlan.js || buildPlan.css || buildPlan.previews;
|
|
307
|
+
let viteBuildStartedAtMs = 0;
|
|
308
|
+
if (runsViteBuildSteps) {
|
|
309
|
+
viteBuildStartedAtMs = Date.now();
|
|
310
|
+
logger_1.Logger.info(viteBuildStartMessage(runtimeComponentId, buildPlan));
|
|
311
|
+
}
|
|
269
312
|
// Build JS if needed (new build, validation missing, or explicit segment request).
|
|
270
313
|
if (buildPlan.js) {
|
|
271
314
|
data = yield (0, javascript_1.default)(data, handoff);
|
|
@@ -278,6 +321,10 @@ function processComponents(handoff, id, segmentToProcess, options) {
|
|
|
278
321
|
if (buildPlan.previews) {
|
|
279
322
|
data = yield (0, html_1.default)(Object.assign(Object.assign({}, data), { previews: getBuildPreviews(data) }), handoff, components);
|
|
280
323
|
}
|
|
324
|
+
if (runsViteBuildSteps) {
|
|
325
|
+
const elapsedMs = Date.now() - viteBuildStartedAtMs;
|
|
326
|
+
logger_1.Logger.info(viteBuildFinishMessage(runtimeComponentId, buildPlan, elapsedMs));
|
|
327
|
+
}
|
|
281
328
|
/**
|
|
282
329
|
* Run validation if explicitly requested and a hook is configured.
|
|
283
330
|
* This allows custom logic to assess the validity of the generated component data.
|
|
@@ -17,6 +17,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const vite_1 = require("vite");
|
|
19
19
|
const config_1 = require("../../../config");
|
|
20
|
+
const duration_1 = require("../../../utils/duration");
|
|
20
21
|
const logger_1 = require("../../../utils/logger");
|
|
21
22
|
const vite_config_1 = __importDefault(require("../../vite-config"));
|
|
22
23
|
const component_1 = require("../component");
|
|
@@ -155,9 +156,9 @@ const buildMainCss = (handoff) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
155
156
|
const stat = yield fs_extra_1.default.stat(runtimeConfig.entries.scss);
|
|
156
157
|
const entryPath = stat.isDirectory() ? path_1.default.resolve(runtimeConfig.entries.scss, 'main.scss') : runtimeConfig.entries.scss;
|
|
157
158
|
if (entryPath === runtimeConfig.entries.scss || fs_extra_1.default.existsSync(entryPath)) {
|
|
158
|
-
logger_1.Logger.
|
|
159
|
+
logger_1.Logger.info(`Building styles for global entry (main.css)…`);
|
|
160
|
+
const startedAt = Date.now();
|
|
159
161
|
try {
|
|
160
|
-
// Setup SASS load paths
|
|
161
162
|
const loadPaths = [
|
|
162
163
|
path_1.default.resolve(handoff.workingPath),
|
|
163
164
|
path_1.default.resolve(handoff.workingPath, handoff.exportsDirectory, handoff.getProjectId()),
|
|
@@ -173,9 +174,10 @@ const buildMainCss = (handoff) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
173
174
|
loadPaths,
|
|
174
175
|
handoff,
|
|
175
176
|
});
|
|
177
|
+
logger_1.Logger.info(`Finished building styles for global entry (main.css) in ${(0, duration_1.formatDurationMs)(Date.now() - startedAt)}`);
|
|
176
178
|
}
|
|
177
|
-
catch (
|
|
178
|
-
|
|
179
|
+
catch (_d) {
|
|
180
|
+
// buildCssBundle already logs failure for the entry path
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
}
|
|
@@ -18,7 +18,6 @@ export declare const buildComponentJs: (data: TransformComponentTokensResult, ha
|
|
|
18
18
|
*
|
|
19
19
|
* @param handoff - The Handoff configuration object containing build settings
|
|
20
20
|
* @returns A Promise that resolves when the build process is complete
|
|
21
|
-
* @throws May throw an error if the build process fails
|
|
22
21
|
*/
|
|
23
22
|
export declare const buildMainJS: (handoff: Handoff) => Promise<void>;
|
|
24
23
|
export default buildComponentJs;
|
|
@@ -17,6 +17,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const vite_1 = require("vite");
|
|
19
19
|
const config_1 = require("../../../config");
|
|
20
|
+
const duration_1 = require("../../../utils/duration");
|
|
20
21
|
const logger_1 = require("../../../utils/logger");
|
|
21
22
|
const vite_config_1 = __importDefault(require("../../vite-config"));
|
|
22
23
|
const component_1 = require("../component");
|
|
@@ -50,9 +51,6 @@ const buildJsBundle = (_a, handoff_1) => __awaiter(void 0, [_a, handoff_1], void
|
|
|
50
51
|
}
|
|
51
52
|
yield (0, vite_1.build)(viteConfig);
|
|
52
53
|
}
|
|
53
|
-
catch (e) {
|
|
54
|
-
logger_1.Logger.error(`Failed to build JS for "${outputFilename}":`, e);
|
|
55
|
-
}
|
|
56
54
|
finally {
|
|
57
55
|
// Restore the original NODE_ENV value after vite build completes
|
|
58
56
|
// This prevents interference with Next.js app building/running processes
|
|
@@ -83,11 +81,17 @@ const buildComponentJs = (data, handoff) => __awaiter(void 0, void 0, void 0, fu
|
|
|
83
81
|
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
84
82
|
try {
|
|
85
83
|
const js = yield fs_extra_1.default.readFile(path_1.default.resolve(entry), 'utf8');
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
try {
|
|
85
|
+
yield buildJsBundle({
|
|
86
|
+
entry,
|
|
87
|
+
outputPath,
|
|
88
|
+
outputFilename: `${id}.js`,
|
|
89
|
+
}, handoff);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
logger_1.Logger.error(`Failed to bundle JS for component "${id}" (${id}.js):`, e);
|
|
93
|
+
return data;
|
|
94
|
+
}
|
|
91
95
|
data.js = js;
|
|
92
96
|
const compiled = yield fs_extra_1.default.readFile(path_1.default.resolve(outputPath, `${id}.js`), 'utf8');
|
|
93
97
|
data['jsCompiled'] = compiled;
|
|
@@ -106,17 +110,24 @@ exports.buildComponentJs = buildComponentJs;
|
|
|
106
110
|
*
|
|
107
111
|
* @param handoff - The Handoff configuration object containing build settings
|
|
108
112
|
* @returns A Promise that resolves when the build process is complete
|
|
109
|
-
* @throws May throw an error if the build process fails
|
|
110
113
|
*/
|
|
111
114
|
const buildMainJS = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
115
|
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
113
116
|
const runtimeConfig = (0, config_1.initRuntimeConfig)(handoff)[0];
|
|
114
117
|
if (runtimeConfig && runtimeConfig.entries.js && fs_extra_1.default.existsSync(path_1.default.resolve(runtimeConfig.entries.js))) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
logger_1.Logger.info(`Building script for global entry (main.js)…`);
|
|
119
|
+
const startedAt = Date.now();
|
|
120
|
+
try {
|
|
121
|
+
yield buildJsBundle({
|
|
122
|
+
entry: runtimeConfig.entries.js,
|
|
123
|
+
outputPath,
|
|
124
|
+
outputFilename: 'main.js',
|
|
125
|
+
}, handoff);
|
|
126
|
+
logger_1.Logger.info(`Finished building script for global entry (main.js) in ${(0, duration_1.formatDurationMs)(Date.now() - startedAt)}`);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
logger_1.Logger.error(`Failed to build global script (main.js):`, e);
|
|
130
|
+
}
|
|
120
131
|
}
|
|
121
132
|
});
|
|
122
133
|
exports.buildMainJS = buildMainJS;
|
|
@@ -4,4 +4,8 @@ export declare const getPatternAPIPath: (handoff: Handoff) => string;
|
|
|
4
4
|
export declare const writePatternApi: (id: string, pattern: PatternObject, handoff: Handoff) => Promise<void>;
|
|
5
5
|
export declare const writePatternHtml: (id: string, html: string, handoff: Handoff) => Promise<void>;
|
|
6
6
|
export declare const writePatternSummaryApi: (handoff: Handoff, patterns: PatternListObject[]) => Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Reads the pattern list summary when present (used for partial pattern rebuilds).
|
|
9
|
+
*/
|
|
10
|
+
export declare const readPatternSummaryApi: (handoff: Handoff) => Promise<PatternListObject[] | null>;
|
|
7
11
|
export declare const syncPatternArtifacts: (handoff: Handoff, validIds: Iterable<string>) => Promise<void>;
|
|
@@ -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.syncPatternArtifacts = exports.writePatternSummaryApi = exports.writePatternHtml = exports.writePatternApi = exports.getPatternAPIPath = void 0;
|
|
15
|
+
exports.syncPatternArtifacts = exports.readPatternSummaryApi = exports.writePatternSummaryApi = exports.writePatternHtml = exports.writePatternApi = exports.getPatternAPIPath = void 0;
|
|
16
16
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const getPatternAPIPath = (handoff) => {
|
|
@@ -44,6 +44,24 @@ const writePatternSummaryApi = (handoff, patterns) => __awaiter(void 0, void 0,
|
|
|
44
44
|
yield fs_extra_1.default.writeFile(path_1.default.resolve(apiPath, 'patterns.json'), JSON.stringify(patterns, null, 2));
|
|
45
45
|
});
|
|
46
46
|
exports.writePatternSummaryApi = writePatternSummaryApi;
|
|
47
|
+
/**
|
|
48
|
+
* Reads the pattern list summary when present (used for partial pattern rebuilds).
|
|
49
|
+
*/
|
|
50
|
+
const readPatternSummaryApi = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
51
|
+
const summaryPath = path_1.default.resolve(handoff.workingPath, 'public/api/patterns.json');
|
|
52
|
+
if (!fs_extra_1.default.existsSync(summaryPath)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const raw = yield fs_extra_1.default.readFile(summaryPath, 'utf8');
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
59
|
+
}
|
|
60
|
+
catch (_a) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
exports.readPatternSummaryApi = readPatternSummaryApi;
|
|
47
65
|
const syncPatternArtifacts = (handoff, validIds) => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
66
|
const validIdSet = new Set(validIds);
|
|
49
67
|
const patternPath = (0, exports.getPatternAPIPath)(handoff);
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import Handoff from '../../../index';
|
|
2
2
|
import { PatternListObject } from '../types';
|
|
3
|
+
export type ProcessPatternsOptions = {
|
|
4
|
+
/** When set, only these pattern ids are recomposed; others keep existing artifacts. */
|
|
5
|
+
onlyPatternIds?: Set<string>;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Returns pattern ids whose declarations reference any of the given component ids.
|
|
9
|
+
*/
|
|
10
|
+
export declare const getPatternIdsReferencingComponents: (handoff: Handoff, componentIds: string[]) => string[];
|
|
3
11
|
/**
|
|
4
12
|
* Process all pattern declarations and compose their HTML from pre-built
|
|
5
13
|
* component preview files. This function must run AFTER buildComponents
|
|
6
14
|
* so that all component preview HTML files already exist on disk.
|
|
7
15
|
*/
|
|
8
|
-
export declare function processPatterns(handoff: Handoff): Promise<PatternListObject[]>;
|
|
16
|
+
export declare function processPatterns(handoff: Handoff, options?: ProcessPatternsOptions): Promise<PatternListObject[]>;
|
|
9
17
|
export default processPatterns;
|