@agent-scope/cli 1.17.3 → 1.18.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/dist/cli.js +316 -165
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +226 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +224 -79
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -6,14 +6,16 @@ var manifest = require('@agent-scope/manifest');
|
|
|
6
6
|
var render = require('@agent-scope/render');
|
|
7
7
|
var tokens = require('@agent-scope/tokens');
|
|
8
8
|
var commander = require('commander');
|
|
9
|
-
var
|
|
9
|
+
var esbuild2 = require('esbuild');
|
|
10
10
|
var module$1 = require('module');
|
|
11
11
|
var readline = require('readline');
|
|
12
12
|
var playwright = require('@agent-scope/playwright');
|
|
13
13
|
var playwright$1 = require('playwright');
|
|
14
|
+
var os = require('os');
|
|
14
15
|
var http = require('http');
|
|
15
16
|
var site = require('@agent-scope/site');
|
|
16
17
|
|
|
18
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
17
19
|
function _interopNamespace(e) {
|
|
18
20
|
if (e && e.__esModule) return e;
|
|
19
21
|
var n = Object.create(null);
|
|
@@ -32,13 +34,13 @@ function _interopNamespace(e) {
|
|
|
32
34
|
return Object.freeze(n);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
var
|
|
37
|
+
var esbuild2__namespace = /*#__PURE__*/_interopNamespace(esbuild2);
|
|
36
38
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
37
39
|
|
|
38
40
|
// src/ci/commands.ts
|
|
39
|
-
async function buildComponentHarness(filePath, componentName, props, viewportWidth, projectCss,
|
|
41
|
+
async function buildComponentHarness(filePath, componentName, props, viewportWidth, projectCss, wrapperScript) {
|
|
40
42
|
const bundledScript = await bundleComponentToIIFE(filePath, componentName, props);
|
|
41
|
-
return wrapInHtml(bundledScript, viewportWidth, projectCss,
|
|
43
|
+
return wrapInHtml(bundledScript, viewportWidth, projectCss, wrapperScript);
|
|
42
44
|
}
|
|
43
45
|
async function bundleComponentToIIFE(filePath, componentName, props) {
|
|
44
46
|
const propsJson = JSON.stringify(props).replace(/<\/script>/gi, "<\\/script>");
|
|
@@ -74,7 +76,12 @@ import { createElement } from "react";
|
|
|
74
76
|
window.__SCOPE_RENDER_COMPLETE__ = true;
|
|
75
77
|
return;
|
|
76
78
|
}
|
|
77
|
-
|
|
79
|
+
// If a scope file wrapper was injected, use it to wrap the component
|
|
80
|
+
var Wrapper = (window).__SCOPE_WRAPPER__;
|
|
81
|
+
var element = Wrapper
|
|
82
|
+
? createElement(Wrapper, null, createElement(Component, props))
|
|
83
|
+
: createElement(Component, props);
|
|
84
|
+
createRoot(rootEl).render(element);
|
|
78
85
|
// Use requestAnimationFrame to let React flush the render
|
|
79
86
|
requestAnimationFrame(function() {
|
|
80
87
|
window.__SCOPE_RENDER_COMPLETE__ = true;
|
|
@@ -86,7 +93,7 @@ import { createElement } from "react";
|
|
|
86
93
|
})();
|
|
87
94
|
`
|
|
88
95
|
);
|
|
89
|
-
const result = await
|
|
96
|
+
const result = await esbuild2__namespace.build({
|
|
90
97
|
stdin: {
|
|
91
98
|
contents: wrapperCode,
|
|
92
99
|
// Resolve relative imports (within the component's dir)
|
|
@@ -124,12 +131,11 @@ ${msg}`);
|
|
|
124
131
|
}
|
|
125
132
|
return outputFile.text;
|
|
126
133
|
}
|
|
127
|
-
function wrapInHtml(bundledScript, viewportWidth, projectCss,
|
|
134
|
+
function wrapInHtml(bundledScript, viewportWidth, projectCss, wrapperScript) {
|
|
128
135
|
const projectStyleBlock = projectCss != null && projectCss.length > 0 ? `<style id="scope-project-css">
|
|
129
136
|
${projectCss.replace(/<\/style>/gi, "<\\/style>")}
|
|
130
137
|
</style>` : "";
|
|
131
|
-
const
|
|
132
|
-
` : "";
|
|
138
|
+
const wrapperScriptBlock = wrapperScript != null && wrapperScript.length > 0 ? `<script id="scope-wrapper-script">${wrapperScript}</script>` : "";
|
|
133
139
|
return `<!DOCTYPE html>
|
|
134
140
|
<html lang="en">
|
|
135
141
|
<head>
|
|
@@ -144,7 +150,8 @@ ${projectCss.replace(/<\/style>/gi, "<\\/style>")}
|
|
|
144
150
|
</head>
|
|
145
151
|
<body>
|
|
146
152
|
<div id="scope-root" data-reactscope-root></div>
|
|
147
|
-
${
|
|
153
|
+
${wrapperScriptBlock}
|
|
154
|
+
<script>${bundledScript}</script>
|
|
148
155
|
</body>
|
|
149
156
|
</html>`;
|
|
150
157
|
}
|
|
@@ -507,16 +514,16 @@ async function getTailwindCompiler(cwd) {
|
|
|
507
514
|
from: entryPath,
|
|
508
515
|
loadStylesheet
|
|
509
516
|
});
|
|
510
|
-
const
|
|
511
|
-
compilerCache = { cwd, build:
|
|
512
|
-
return
|
|
517
|
+
const build3 = result.build.bind(result);
|
|
518
|
+
compilerCache = { cwd, build: build3 };
|
|
519
|
+
return build3;
|
|
513
520
|
}
|
|
514
521
|
async function getCompiledCssForClasses(cwd, classes) {
|
|
515
|
-
const
|
|
516
|
-
if (
|
|
522
|
+
const build3 = await getTailwindCompiler(cwd);
|
|
523
|
+
if (build3 === null) return null;
|
|
517
524
|
const deduped = [...new Set(classes)].filter(Boolean);
|
|
518
525
|
if (deduped.length === 0) return null;
|
|
519
|
-
return
|
|
526
|
+
return build3(deduped);
|
|
520
527
|
}
|
|
521
528
|
|
|
522
529
|
// src/ci/commands.ts
|
|
@@ -1134,9 +1141,9 @@ function createRL() {
|
|
|
1134
1141
|
});
|
|
1135
1142
|
}
|
|
1136
1143
|
async function ask(rl, question) {
|
|
1137
|
-
return new Promise((
|
|
1144
|
+
return new Promise((resolve18) => {
|
|
1138
1145
|
rl.question(question, (answer) => {
|
|
1139
|
-
|
|
1146
|
+
resolve18(answer.trim());
|
|
1140
1147
|
});
|
|
1141
1148
|
});
|
|
1142
1149
|
}
|
|
@@ -2958,6 +2965,122 @@ function writeReportToFile(report, outputPath, pretty) {
|
|
|
2958
2965
|
const json = pretty ? JSON.stringify(report, null, 2) : JSON.stringify(report);
|
|
2959
2966
|
fs.writeFileSync(outputPath, json, "utf-8");
|
|
2960
2967
|
}
|
|
2968
|
+
var SCOPE_EXTENSIONS = [".scope.tsx", ".scope.ts", ".scope.jsx", ".scope.js"];
|
|
2969
|
+
function findScopeFile(componentFilePath) {
|
|
2970
|
+
const dir = path.dirname(componentFilePath);
|
|
2971
|
+
const stem = componentFilePath.replace(/\.(tsx?|jsx?)$/, "");
|
|
2972
|
+
const baseName = stem.slice(dir.length + 1);
|
|
2973
|
+
for (const ext of SCOPE_EXTENSIONS) {
|
|
2974
|
+
const candidate = path.join(dir, `${baseName}${ext}`);
|
|
2975
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
2976
|
+
}
|
|
2977
|
+
return null;
|
|
2978
|
+
}
|
|
2979
|
+
async function loadScopeFile(scopeFilePath) {
|
|
2980
|
+
const tmpDir = path.join(os.tmpdir(), `scope-file-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
2981
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
2982
|
+
const outFile = path.join(tmpDir, "scope-file.cjs");
|
|
2983
|
+
try {
|
|
2984
|
+
const result = await esbuild2__namespace.build({
|
|
2985
|
+
entryPoints: [scopeFilePath],
|
|
2986
|
+
bundle: true,
|
|
2987
|
+
format: "cjs",
|
|
2988
|
+
platform: "node",
|
|
2989
|
+
target: "node18",
|
|
2990
|
+
outfile: outFile,
|
|
2991
|
+
write: true,
|
|
2992
|
+
jsx: "automatic",
|
|
2993
|
+
jsxImportSource: "react",
|
|
2994
|
+
// Externalize React — we don't need to execute JSX, just extract plain data
|
|
2995
|
+
external: ["react", "react-dom", "react/jsx-runtime"],
|
|
2996
|
+
define: {
|
|
2997
|
+
"process.env.NODE_ENV": '"development"'
|
|
2998
|
+
},
|
|
2999
|
+
logLevel: "silent"
|
|
3000
|
+
});
|
|
3001
|
+
if (result.errors.length > 0) {
|
|
3002
|
+
const msg = result.errors.map((e) => `${e.text}${e.location ? ` (${e.location.file}:${e.location.line})` : ""}`).join("\n");
|
|
3003
|
+
throw new Error(`Failed to bundle scope file ${scopeFilePath}:
|
|
3004
|
+
${msg}`);
|
|
3005
|
+
}
|
|
3006
|
+
const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
3007
|
+
delete req.cache[path.resolve(outFile)];
|
|
3008
|
+
const mod = req(outFile);
|
|
3009
|
+
const scenarios = extractScenarios(mod, scopeFilePath);
|
|
3010
|
+
const hasWrapper = typeof mod.wrapper === "function" || typeof mod.default?.wrapper === "function";
|
|
3011
|
+
return { filePath: scopeFilePath, scenarios, hasWrapper };
|
|
3012
|
+
} finally {
|
|
3013
|
+
try {
|
|
3014
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
3015
|
+
} catch {
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
async function loadScopeFileForComponent(componentFilePath) {
|
|
3020
|
+
const scopeFilePath = findScopeFile(componentFilePath);
|
|
3021
|
+
if (scopeFilePath === null) return null;
|
|
3022
|
+
return loadScopeFile(scopeFilePath);
|
|
3023
|
+
}
|
|
3024
|
+
function extractScenarios(mod, filePath) {
|
|
3025
|
+
const raw = mod.scenarios ?? mod.default?.scenarios;
|
|
3026
|
+
if (raw === void 0) return {};
|
|
3027
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
3028
|
+
console.warn(`[scope] ${filePath}: "scenarios" export is not a plain object \u2014 ignoring.`);
|
|
3029
|
+
return {};
|
|
3030
|
+
}
|
|
3031
|
+
const result = {};
|
|
3032
|
+
for (const [name, props] of Object.entries(raw)) {
|
|
3033
|
+
if (typeof props !== "object" || props === null || Array.isArray(props)) {
|
|
3034
|
+
console.warn(`[scope] ${filePath}: scenario "${name}" is not a plain object \u2014 skipping.`);
|
|
3035
|
+
continue;
|
|
3036
|
+
}
|
|
3037
|
+
result[name] = props;
|
|
3038
|
+
}
|
|
3039
|
+
return result;
|
|
3040
|
+
}
|
|
3041
|
+
async function buildWrapperScript(scopeFilePath) {
|
|
3042
|
+
const wrapperEntry = (
|
|
3043
|
+
/* ts */
|
|
3044
|
+
`
|
|
3045
|
+
import * as __scopeMod from ${JSON.stringify(scopeFilePath)};
|
|
3046
|
+
// Expose the wrapper on window so the harness can access it
|
|
3047
|
+
var wrapper =
|
|
3048
|
+
__scopeMod.wrapper ??
|
|
3049
|
+
(__scopeMod.default && __scopeMod.default.wrapper) ??
|
|
3050
|
+
null;
|
|
3051
|
+
window.__SCOPE_WRAPPER__ = wrapper;
|
|
3052
|
+
`
|
|
3053
|
+
);
|
|
3054
|
+
const result = await esbuild2__namespace.build({
|
|
3055
|
+
stdin: {
|
|
3056
|
+
contents: wrapperEntry,
|
|
3057
|
+
resolveDir: path.dirname(scopeFilePath),
|
|
3058
|
+
loader: "tsx",
|
|
3059
|
+
sourcefile: "__scope_wrapper_entry__.tsx"
|
|
3060
|
+
},
|
|
3061
|
+
bundle: true,
|
|
3062
|
+
format: "iife",
|
|
3063
|
+
platform: "browser",
|
|
3064
|
+
target: "es2020",
|
|
3065
|
+
write: false,
|
|
3066
|
+
jsx: "automatic",
|
|
3067
|
+
jsxImportSource: "react",
|
|
3068
|
+
external: [],
|
|
3069
|
+
define: {
|
|
3070
|
+
"process.env.NODE_ENV": '"development"',
|
|
3071
|
+
global: "globalThis"
|
|
3072
|
+
},
|
|
3073
|
+
logLevel: "silent"
|
|
3074
|
+
});
|
|
3075
|
+
if (result.errors.length > 0) {
|
|
3076
|
+
const msg = result.errors.map((e) => `${e.text}${e.location ? ` (${e.location.file}:${e.location.line})` : ""}`).join("\n");
|
|
3077
|
+
throw new Error(`Failed to build wrapper script from ${scopeFilePath}:
|
|
3078
|
+
${msg}`);
|
|
3079
|
+
}
|
|
3080
|
+
return result.outputFiles?.[0]?.text ?? "";
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
// src/render-commands.ts
|
|
2961
3084
|
var MANIFEST_PATH6 = ".reactscope/manifest.json";
|
|
2962
3085
|
var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
|
|
2963
3086
|
var _pool3 = null;
|
|
@@ -2978,7 +3101,7 @@ async function shutdownPool3() {
|
|
|
2978
3101
|
_pool3 = null;
|
|
2979
3102
|
}
|
|
2980
3103
|
}
|
|
2981
|
-
function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
3104
|
+
function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, wrapperScript) {
|
|
2982
3105
|
const satori = new render.SatoriRenderer({
|
|
2983
3106
|
defaultViewport: { width: viewportWidth, height: viewportHeight }
|
|
2984
3107
|
});
|
|
@@ -2991,7 +3114,10 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
|
2991
3114
|
filePath,
|
|
2992
3115
|
componentName,
|
|
2993
3116
|
props,
|
|
2994
|
-
viewportWidth
|
|
3117
|
+
viewportWidth,
|
|
3118
|
+
void 0,
|
|
3119
|
+
// projectCss (handled separately)
|
|
3120
|
+
wrapperScript
|
|
2995
3121
|
);
|
|
2996
3122
|
const slot = await pool.acquire();
|
|
2997
3123
|
const { page } = slot;
|
|
@@ -3082,8 +3208,37 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
|
3082
3208
|
}
|
|
3083
3209
|
};
|
|
3084
3210
|
}
|
|
3211
|
+
function buildScenarioMap(opts, scopeData) {
|
|
3212
|
+
if (opts.scenario !== void 0) {
|
|
3213
|
+
if (scopeData === null) {
|
|
3214
|
+
throw new Error(`--scenario "${opts.scenario}" requires a .scope file next to the component`);
|
|
3215
|
+
}
|
|
3216
|
+
const props = scopeData.scenarios[opts.scenario];
|
|
3217
|
+
if (props === void 0) {
|
|
3218
|
+
const available = Object.keys(scopeData.scenarios).join(", ") || "(none)";
|
|
3219
|
+
throw new Error(
|
|
3220
|
+
`Scenario "${opts.scenario}" not found in scope file.
|
|
3221
|
+
Available: ${available}`
|
|
3222
|
+
);
|
|
3223
|
+
}
|
|
3224
|
+
return { [opts.scenario]: props };
|
|
3225
|
+
}
|
|
3226
|
+
if (opts.props !== void 0) {
|
|
3227
|
+
let parsed;
|
|
3228
|
+
try {
|
|
3229
|
+
parsed = JSON.parse(opts.props);
|
|
3230
|
+
} catch {
|
|
3231
|
+
throw new Error(`Invalid props JSON: ${opts.props}`);
|
|
3232
|
+
}
|
|
3233
|
+
return { __default__: parsed };
|
|
3234
|
+
}
|
|
3235
|
+
if (scopeData !== null && Object.keys(scopeData.scenarios).length > 0) {
|
|
3236
|
+
return scopeData.scenarios;
|
|
3237
|
+
}
|
|
3238
|
+
return { __default__: {} };
|
|
3239
|
+
}
|
|
3085
3240
|
function registerRenderSingle(renderCmd) {
|
|
3086
|
-
renderCmd.command("component <component>", { isDefault: true }).description("Render a single component to PNG or JSON").option("--props <json>", `Inline props JSON, e.g. '{"variant":"primary"}'`).option("--viewport <WxH>", "Viewport size e.g. 1280x720", "375x812").option("--theme <name>", "Theme name from the token system").option("-o, --output <path>", "Write PNG to file instead of stdout").option("--format <fmt>", "Output format: png or json (default: auto)").option("--manifest <path>", "Path to manifest.json", MANIFEST_PATH6).action(
|
|
3241
|
+
renderCmd.command("component <component>", { isDefault: true }).description("Render a single component to PNG or JSON").option("--props <json>", `Inline props JSON, e.g. '{"variant":"primary"}'`).option("--scenario <name>", "Run a named scenario from the component's .scope file").option("--viewport <WxH>", "Viewport size e.g. 1280x720", "375x812").option("--theme <name>", "Theme name from the token system").option("-o, --output <path>", "Write PNG to file instead of stdout").option("--format <fmt>", "Output format: png or json (default: auto)").option("--manifest <path>", "Path to manifest.json", MANIFEST_PATH6).action(
|
|
3087
3242
|
async (componentName, opts) => {
|
|
3088
3243
|
try {
|
|
3089
3244
|
const manifest = loadManifest(opts.manifest);
|
|
@@ -3095,80 +3250,71 @@ function registerRenderSingle(renderCmd) {
|
|
|
3095
3250
|
Available: ${available}`
|
|
3096
3251
|
);
|
|
3097
3252
|
}
|
|
3098
|
-
let props = {};
|
|
3099
|
-
if (opts.props !== void 0) {
|
|
3100
|
-
try {
|
|
3101
|
-
props = JSON.parse(opts.props);
|
|
3102
|
-
} catch {
|
|
3103
|
-
throw new Error(`Invalid props JSON: ${opts.props}`);
|
|
3104
|
-
}
|
|
3105
|
-
}
|
|
3106
3253
|
const { width, height } = parseViewport(opts.viewport);
|
|
3107
3254
|
const rootDir = process.cwd();
|
|
3108
3255
|
const filePath = path.resolve(rootDir, descriptor.filePath);
|
|
3109
|
-
const
|
|
3256
|
+
const scopeData = await loadScopeFileForComponent(filePath);
|
|
3257
|
+
const wrapperScript = scopeData?.hasWrapper === true ? await buildWrapperScript(scopeData.filePath) : void 0;
|
|
3258
|
+
const scenarios = buildScenarioMap(opts, scopeData);
|
|
3259
|
+
const renderer = buildRenderer(filePath, componentName, width, height, wrapperScript);
|
|
3110
3260
|
process.stderr.write(
|
|
3111
3261
|
`Rendering ${componentName} [${descriptor.complexityClass}] at ${width}\xD7${height}\u2026
|
|
3112
3262
|
`
|
|
3113
3263
|
);
|
|
3114
|
-
const
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3264
|
+
const fmt2 = resolveSingleFormat(opts.format);
|
|
3265
|
+
let anyFailed = false;
|
|
3266
|
+
for (const [scenarioName, props] of Object.entries(scenarios)) {
|
|
3267
|
+
const isNamed = scenarioName !== "__default__";
|
|
3268
|
+
const label = isNamed ? `${componentName}:${scenarioName}` : componentName;
|
|
3269
|
+
const outcome = await render.safeRender(
|
|
3270
|
+
() => renderer.renderCell(props, descriptor.complexityClass),
|
|
3271
|
+
{
|
|
3272
|
+
props,
|
|
3273
|
+
sourceLocation: {
|
|
3274
|
+
file: descriptor.filePath,
|
|
3275
|
+
line: descriptor.loc.start,
|
|
3276
|
+
column: 0
|
|
3277
|
+
}
|
|
3122
3278
|
}
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
if (outcome.crashed) {
|
|
3127
|
-
process.stderr.write(`\u2717 Render failed: ${outcome.error.message}
|
|
3279
|
+
);
|
|
3280
|
+
if (outcome.crashed) {
|
|
3281
|
+
process.stderr.write(`\u2717 ${label} render failed: ${outcome.error.message}
|
|
3128
3282
|
`);
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3283
|
+
const hintList = outcome.error.heuristicFlags.join(", ");
|
|
3284
|
+
if (hintList.length > 0) {
|
|
3285
|
+
process.stderr.write(` Hints: ${hintList}
|
|
3132
3286
|
`);
|
|
3287
|
+
}
|
|
3288
|
+
anyFailed = true;
|
|
3289
|
+
continue;
|
|
3133
3290
|
}
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
`\u2713 ${componentName} \u2192 ${opts.output} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3291
|
+
const result = outcome.result;
|
|
3292
|
+
const outFileName = isNamed ? `${componentName}-${scenarioName}.png` : `${componentName}.png`;
|
|
3293
|
+
if (opts.output !== void 0 && !isNamed) {
|
|
3294
|
+
const outPath = path.resolve(process.cwd(), opts.output);
|
|
3295
|
+
fs.writeFileSync(outPath, result.screenshot);
|
|
3296
|
+
process.stdout.write(
|
|
3297
|
+
`\u2713 ${label} \u2192 ${opts.output} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3142
3298
|
`
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
if (fmt2 === "json") {
|
|
3148
|
-
const json = formatRenderJson(componentName, props, result);
|
|
3149
|
-
process.stdout.write(`${JSON.stringify(json, null, 2)}
|
|
3299
|
+
);
|
|
3300
|
+
} else if (fmt2 === "json") {
|
|
3301
|
+
const json = formatRenderJson(label, props, result);
|
|
3302
|
+
process.stdout.write(`${JSON.stringify(json, null, 2)}
|
|
3150
3303
|
`);
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
`
|
|
3160
|
-
);
|
|
3161
|
-
} else {
|
|
3162
|
-
const dir = path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
3163
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
3164
|
-
const outPath = path.resolve(dir, `${componentName}.png`);
|
|
3165
|
-
fs.writeFileSync(outPath, result.screenshot);
|
|
3166
|
-
const relPath = `${DEFAULT_OUTPUT_DIR}/${componentName}.png`;
|
|
3167
|
-
process.stdout.write(
|
|
3168
|
-
`\u2713 ${componentName} \u2192 ${relPath} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3304
|
+
} else {
|
|
3305
|
+
const dir = path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
3306
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
3307
|
+
const outPath = path.resolve(dir, outFileName);
|
|
3308
|
+
fs.writeFileSync(outPath, result.screenshot);
|
|
3309
|
+
const relPath = `${DEFAULT_OUTPUT_DIR}/${outFileName}`;
|
|
3310
|
+
process.stdout.write(
|
|
3311
|
+
`\u2713 ${label} \u2192 ${relPath} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3169
3312
|
`
|
|
3170
|
-
|
|
3313
|
+
);
|
|
3314
|
+
}
|
|
3171
3315
|
}
|
|
3316
|
+
await shutdownPool3();
|
|
3317
|
+
if (anyFailed) process.exit(1);
|
|
3172
3318
|
} catch (err) {
|
|
3173
3319
|
await shutdownPool3();
|
|
3174
3320
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|