@agent-scope/cli 1.17.2 → 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 +349 -170
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +256 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +254 -83
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -61,9 +61,9 @@ import { Command } from "commander";
|
|
|
61
61
|
// src/component-bundler.ts
|
|
62
62
|
import { dirname } from "path";
|
|
63
63
|
import * as esbuild from "esbuild";
|
|
64
|
-
async function buildComponentHarness(filePath, componentName, props, viewportWidth, projectCss) {
|
|
64
|
+
async function buildComponentHarness(filePath, componentName, props, viewportWidth, projectCss, wrapperScript) {
|
|
65
65
|
const bundledScript = await bundleComponentToIIFE(filePath, componentName, props);
|
|
66
|
-
return wrapInHtml(bundledScript, viewportWidth, projectCss);
|
|
66
|
+
return wrapInHtml(bundledScript, viewportWidth, projectCss, wrapperScript);
|
|
67
67
|
}
|
|
68
68
|
async function bundleComponentToIIFE(filePath, componentName, props) {
|
|
69
69
|
const propsJson = JSON.stringify(props).replace(/<\/script>/gi, "<\\/script>");
|
|
@@ -99,7 +99,12 @@ import { createElement } from "react";
|
|
|
99
99
|
window.__SCOPE_RENDER_COMPLETE__ = true;
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
// If a scope file wrapper was injected, use it to wrap the component
|
|
103
|
+
var Wrapper = (window).__SCOPE_WRAPPER__;
|
|
104
|
+
var element = Wrapper
|
|
105
|
+
? createElement(Wrapper, null, createElement(Component, props))
|
|
106
|
+
: createElement(Component, props);
|
|
107
|
+
createRoot(rootEl).render(element);
|
|
103
108
|
// Use requestAnimationFrame to let React flush the render
|
|
104
109
|
requestAnimationFrame(function() {
|
|
105
110
|
window.__SCOPE_RENDER_COMPLETE__ = true;
|
|
@@ -149,10 +154,11 @@ ${msg}`);
|
|
|
149
154
|
}
|
|
150
155
|
return outputFile.text;
|
|
151
156
|
}
|
|
152
|
-
function wrapInHtml(bundledScript, viewportWidth, projectCss) {
|
|
157
|
+
function wrapInHtml(bundledScript, viewportWidth, projectCss, wrapperScript) {
|
|
153
158
|
const projectStyleBlock = projectCss != null && projectCss.length > 0 ? `<style id="scope-project-css">
|
|
154
159
|
${projectCss.replace(/<\/style>/gi, "<\\/style>")}
|
|
155
160
|
</style>` : "";
|
|
161
|
+
const wrapperScriptBlock = wrapperScript != null && wrapperScript.length > 0 ? `<script id="scope-wrapper-script">${wrapperScript}</script>` : "";
|
|
156
162
|
return `<!DOCTYPE html>
|
|
157
163
|
<html lang="en">
|
|
158
164
|
<head>
|
|
@@ -167,6 +173,7 @@ ${projectCss.replace(/<\/style>/gi, "<\\/style>")}
|
|
|
167
173
|
</head>
|
|
168
174
|
<body>
|
|
169
175
|
<div id="scope-root" data-reactscope-root></div>
|
|
176
|
+
${wrapperScriptBlock}
|
|
170
177
|
<script>${bundledScript}</script>
|
|
171
178
|
</body>
|
|
172
179
|
</html>`;
|
|
@@ -535,16 +542,16 @@ async function getTailwindCompiler(cwd) {
|
|
|
535
542
|
from: entryPath,
|
|
536
543
|
loadStylesheet
|
|
537
544
|
});
|
|
538
|
-
const
|
|
539
|
-
compilerCache = { cwd, build:
|
|
540
|
-
return
|
|
545
|
+
const build3 = result.build.bind(result);
|
|
546
|
+
compilerCache = { cwd, build: build3 };
|
|
547
|
+
return build3;
|
|
541
548
|
}
|
|
542
549
|
async function getCompiledCssForClasses(cwd, classes) {
|
|
543
|
-
const
|
|
544
|
-
if (
|
|
550
|
+
const build3 = await getTailwindCompiler(cwd);
|
|
551
|
+
if (build3 === null) return null;
|
|
545
552
|
const deduped = [...new Set(classes)].filter(Boolean);
|
|
546
553
|
if (deduped.length === 0) return null;
|
|
547
|
-
return
|
|
554
|
+
return build3(deduped);
|
|
548
555
|
}
|
|
549
556
|
|
|
550
557
|
// src/ci/commands.ts
|
|
@@ -1174,9 +1181,9 @@ function createRL() {
|
|
|
1174
1181
|
});
|
|
1175
1182
|
}
|
|
1176
1183
|
async function ask(rl, question) {
|
|
1177
|
-
return new Promise((
|
|
1184
|
+
return new Promise((resolve18) => {
|
|
1178
1185
|
rl.question(question, (answer) => {
|
|
1179
|
-
|
|
1186
|
+
resolve18(answer.trim());
|
|
1180
1187
|
});
|
|
1181
1188
|
});
|
|
1182
1189
|
}
|
|
@@ -1326,6 +1333,7 @@ function createInitCommand() {
|
|
|
1326
1333
|
|
|
1327
1334
|
// src/instrument/renders.ts
|
|
1328
1335
|
import { resolve as resolve7 } from "path";
|
|
1336
|
+
import { getBrowserEntryScript as getBrowserEntryScript5 } from "@agent-scope/playwright";
|
|
1329
1337
|
import { BrowserPool as BrowserPool2 } from "@agent-scope/render";
|
|
1330
1338
|
import { Command as Command5 } from "commander";
|
|
1331
1339
|
|
|
@@ -1765,8 +1773,15 @@ async function runHooksProfiling(componentName, filePath, props) {
|
|
|
1765
1773
|
try {
|
|
1766
1774
|
const context = await browser.newContext();
|
|
1767
1775
|
const page = await context.newPage();
|
|
1768
|
-
|
|
1769
|
-
const htmlHarness = await buildComponentHarness(
|
|
1776
|
+
const scopeRuntime = getBrowserEntryScript2();
|
|
1777
|
+
const htmlHarness = await buildComponentHarness(
|
|
1778
|
+
filePath,
|
|
1779
|
+
componentName,
|
|
1780
|
+
props,
|
|
1781
|
+
1280,
|
|
1782
|
+
void 0,
|
|
1783
|
+
scopeRuntime
|
|
1784
|
+
);
|
|
1770
1785
|
await page.setContent(htmlHarness, { waitUntil: "load" });
|
|
1771
1786
|
await page.waitForFunction(
|
|
1772
1787
|
() => {
|
|
@@ -1860,6 +1875,7 @@ Available: ${available}`
|
|
|
1860
1875
|
|
|
1861
1876
|
// src/instrument/profile.ts
|
|
1862
1877
|
import { resolve as resolve5 } from "path";
|
|
1878
|
+
import { getBrowserEntryScript as getBrowserEntryScript3 } from "@agent-scope/playwright";
|
|
1863
1879
|
import { Command as Cmd2 } from "commander";
|
|
1864
1880
|
import { chromium as chromium3 } from "playwright";
|
|
1865
1881
|
var MANIFEST_PATH3 = ".reactscope/manifest.json";
|
|
@@ -2030,7 +2046,15 @@ async function runInteractionProfile(componentName, filePath, props, interaction
|
|
|
2030
2046
|
try {
|
|
2031
2047
|
const context = await browser.newContext();
|
|
2032
2048
|
const page = await context.newPage();
|
|
2033
|
-
const
|
|
2049
|
+
const scopeRuntime = getBrowserEntryScript3();
|
|
2050
|
+
const htmlHarness = await buildComponentHarness(
|
|
2051
|
+
filePath,
|
|
2052
|
+
componentName,
|
|
2053
|
+
props,
|
|
2054
|
+
1280,
|
|
2055
|
+
void 0,
|
|
2056
|
+
scopeRuntime
|
|
2057
|
+
);
|
|
2034
2058
|
await page.setContent(htmlHarness, { waitUntil: "load" });
|
|
2035
2059
|
await page.waitForFunction(
|
|
2036
2060
|
() => {
|
|
@@ -2154,7 +2178,7 @@ Available: ${available}`
|
|
|
2154
2178
|
|
|
2155
2179
|
// src/instrument/tree.ts
|
|
2156
2180
|
import { resolve as resolve6 } from "path";
|
|
2157
|
-
import { getBrowserEntryScript as
|
|
2181
|
+
import { getBrowserEntryScript as getBrowserEntryScript4 } from "@agent-scope/playwright";
|
|
2158
2182
|
import { Command as Command4 } from "commander";
|
|
2159
2183
|
import { chromium as chromium4 } from "playwright";
|
|
2160
2184
|
var MANIFEST_PATH4 = ".reactscope/manifest.json";
|
|
@@ -2377,12 +2401,14 @@ async function runInstrumentTree(options) {
|
|
|
2377
2401
|
viewport: { width: DEFAULT_VIEWPORT_WIDTH, height: DEFAULT_VIEWPORT_HEIGHT }
|
|
2378
2402
|
});
|
|
2379
2403
|
const page = await context.newPage();
|
|
2380
|
-
|
|
2404
|
+
const scopeRuntime = getBrowserEntryScript4();
|
|
2381
2405
|
const htmlHarness = await buildComponentHarness(
|
|
2382
2406
|
filePath,
|
|
2383
2407
|
componentName,
|
|
2384
2408
|
{},
|
|
2385
|
-
DEFAULT_VIEWPORT_WIDTH
|
|
2409
|
+
DEFAULT_VIEWPORT_WIDTH,
|
|
2410
|
+
void 0,
|
|
2411
|
+
scopeRuntime
|
|
2386
2412
|
);
|
|
2387
2413
|
await page.setContent(htmlHarness, { waitUntil: "load" });
|
|
2388
2414
|
await page.waitForFunction(
|
|
@@ -2830,13 +2856,20 @@ Available: ${available}`
|
|
|
2830
2856
|
}
|
|
2831
2857
|
const rootDir = process.cwd();
|
|
2832
2858
|
const filePath = resolve7(rootDir, descriptor.filePath);
|
|
2833
|
-
const
|
|
2859
|
+
const preScript = getBrowserEntryScript5() + "\n" + buildInstrumentationScript();
|
|
2860
|
+
const htmlHarness = await buildComponentHarness(
|
|
2861
|
+
filePath,
|
|
2862
|
+
options.componentName,
|
|
2863
|
+
{},
|
|
2864
|
+
1280,
|
|
2865
|
+
void 0,
|
|
2866
|
+
preScript
|
|
2867
|
+
);
|
|
2834
2868
|
const pool = await getPool2();
|
|
2835
2869
|
const slot = await pool.acquire();
|
|
2836
2870
|
const { page } = slot;
|
|
2837
2871
|
const startMs = performance.now();
|
|
2838
2872
|
try {
|
|
2839
|
-
await page.addInitScript(buildInstrumentationScript());
|
|
2840
2873
|
await page.setContent(htmlHarness, { waitUntil: "load" });
|
|
2841
2874
|
await page.waitForFunction(
|
|
2842
2875
|
() => window.__SCOPE_RENDER_COMPLETE__ === true,
|
|
@@ -2968,8 +3001,8 @@ function createInstrumentCommand() {
|
|
|
2968
3001
|
}
|
|
2969
3002
|
|
|
2970
3003
|
// src/render-commands.ts
|
|
2971
|
-
import { mkdirSync as
|
|
2972
|
-
import { resolve as
|
|
3004
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
3005
|
+
import { resolve as resolve9 } from "path";
|
|
2973
3006
|
import {
|
|
2974
3007
|
ALL_CONTEXT_IDS,
|
|
2975
3008
|
ALL_STRESS_IDS,
|
|
@@ -2981,6 +3014,129 @@ import {
|
|
|
2981
3014
|
stressAxis
|
|
2982
3015
|
} from "@agent-scope/render";
|
|
2983
3016
|
import { Command as Command6 } from "commander";
|
|
3017
|
+
|
|
3018
|
+
// src/scope-file.ts
|
|
3019
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, rmSync } from "fs";
|
|
3020
|
+
import { createRequire as createRequire2 } from "module";
|
|
3021
|
+
import { tmpdir } from "os";
|
|
3022
|
+
import { dirname as dirname2, join as join3, resolve as resolve8 } from "path";
|
|
3023
|
+
import * as esbuild2 from "esbuild";
|
|
3024
|
+
var SCOPE_EXTENSIONS = [".scope.tsx", ".scope.ts", ".scope.jsx", ".scope.js"];
|
|
3025
|
+
function findScopeFile(componentFilePath) {
|
|
3026
|
+
const dir = dirname2(componentFilePath);
|
|
3027
|
+
const stem = componentFilePath.replace(/\.(tsx?|jsx?)$/, "");
|
|
3028
|
+
const baseName = stem.slice(dir.length + 1);
|
|
3029
|
+
for (const ext of SCOPE_EXTENSIONS) {
|
|
3030
|
+
const candidate = join3(dir, `${baseName}${ext}`);
|
|
3031
|
+
if (existsSync6(candidate)) return candidate;
|
|
3032
|
+
}
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
async function loadScopeFile(scopeFilePath) {
|
|
3036
|
+
const tmpDir = join3(tmpdir(), `scope-file-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
3037
|
+
mkdirSync3(tmpDir, { recursive: true });
|
|
3038
|
+
const outFile = join3(tmpDir, "scope-file.cjs");
|
|
3039
|
+
try {
|
|
3040
|
+
const result = await esbuild2.build({
|
|
3041
|
+
entryPoints: [scopeFilePath],
|
|
3042
|
+
bundle: true,
|
|
3043
|
+
format: "cjs",
|
|
3044
|
+
platform: "node",
|
|
3045
|
+
target: "node18",
|
|
3046
|
+
outfile: outFile,
|
|
3047
|
+
write: true,
|
|
3048
|
+
jsx: "automatic",
|
|
3049
|
+
jsxImportSource: "react",
|
|
3050
|
+
// Externalize React — we don't need to execute JSX, just extract plain data
|
|
3051
|
+
external: ["react", "react-dom", "react/jsx-runtime"],
|
|
3052
|
+
define: {
|
|
3053
|
+
"process.env.NODE_ENV": '"development"'
|
|
3054
|
+
},
|
|
3055
|
+
logLevel: "silent"
|
|
3056
|
+
});
|
|
3057
|
+
if (result.errors.length > 0) {
|
|
3058
|
+
const msg = result.errors.map((e) => `${e.text}${e.location ? ` (${e.location.file}:${e.location.line})` : ""}`).join("\n");
|
|
3059
|
+
throw new Error(`Failed to bundle scope file ${scopeFilePath}:
|
|
3060
|
+
${msg}`);
|
|
3061
|
+
}
|
|
3062
|
+
const req = createRequire2(import.meta.url);
|
|
3063
|
+
delete req.cache[resolve8(outFile)];
|
|
3064
|
+
const mod = req(outFile);
|
|
3065
|
+
const scenarios = extractScenarios(mod, scopeFilePath);
|
|
3066
|
+
const hasWrapper = typeof mod.wrapper === "function" || typeof mod.default?.wrapper === "function";
|
|
3067
|
+
return { filePath: scopeFilePath, scenarios, hasWrapper };
|
|
3068
|
+
} finally {
|
|
3069
|
+
try {
|
|
3070
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
3071
|
+
} catch {
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
async function loadScopeFileForComponent(componentFilePath) {
|
|
3076
|
+
const scopeFilePath = findScopeFile(componentFilePath);
|
|
3077
|
+
if (scopeFilePath === null) return null;
|
|
3078
|
+
return loadScopeFile(scopeFilePath);
|
|
3079
|
+
}
|
|
3080
|
+
function extractScenarios(mod, filePath) {
|
|
3081
|
+
const raw = mod.scenarios ?? mod.default?.scenarios;
|
|
3082
|
+
if (raw === void 0) return {};
|
|
3083
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
3084
|
+
console.warn(`[scope] ${filePath}: "scenarios" export is not a plain object \u2014 ignoring.`);
|
|
3085
|
+
return {};
|
|
3086
|
+
}
|
|
3087
|
+
const result = {};
|
|
3088
|
+
for (const [name, props] of Object.entries(raw)) {
|
|
3089
|
+
if (typeof props !== "object" || props === null || Array.isArray(props)) {
|
|
3090
|
+
console.warn(`[scope] ${filePath}: scenario "${name}" is not a plain object \u2014 skipping.`);
|
|
3091
|
+
continue;
|
|
3092
|
+
}
|
|
3093
|
+
result[name] = props;
|
|
3094
|
+
}
|
|
3095
|
+
return result;
|
|
3096
|
+
}
|
|
3097
|
+
async function buildWrapperScript(scopeFilePath) {
|
|
3098
|
+
const wrapperEntry = (
|
|
3099
|
+
/* ts */
|
|
3100
|
+
`
|
|
3101
|
+
import * as __scopeMod from ${JSON.stringify(scopeFilePath)};
|
|
3102
|
+
// Expose the wrapper on window so the harness can access it
|
|
3103
|
+
var wrapper =
|
|
3104
|
+
__scopeMod.wrapper ??
|
|
3105
|
+
(__scopeMod.default && __scopeMod.default.wrapper) ??
|
|
3106
|
+
null;
|
|
3107
|
+
window.__SCOPE_WRAPPER__ = wrapper;
|
|
3108
|
+
`
|
|
3109
|
+
);
|
|
3110
|
+
const result = await esbuild2.build({
|
|
3111
|
+
stdin: {
|
|
3112
|
+
contents: wrapperEntry,
|
|
3113
|
+
resolveDir: dirname2(scopeFilePath),
|
|
3114
|
+
loader: "tsx",
|
|
3115
|
+
sourcefile: "__scope_wrapper_entry__.tsx"
|
|
3116
|
+
},
|
|
3117
|
+
bundle: true,
|
|
3118
|
+
format: "iife",
|
|
3119
|
+
platform: "browser",
|
|
3120
|
+
target: "es2020",
|
|
3121
|
+
write: false,
|
|
3122
|
+
jsx: "automatic",
|
|
3123
|
+
jsxImportSource: "react",
|
|
3124
|
+
external: [],
|
|
3125
|
+
define: {
|
|
3126
|
+
"process.env.NODE_ENV": '"development"',
|
|
3127
|
+
global: "globalThis"
|
|
3128
|
+
},
|
|
3129
|
+
logLevel: "silent"
|
|
3130
|
+
});
|
|
3131
|
+
if (result.errors.length > 0) {
|
|
3132
|
+
const msg = result.errors.map((e) => `${e.text}${e.location ? ` (${e.location.file}:${e.location.line})` : ""}`).join("\n");
|
|
3133
|
+
throw new Error(`Failed to build wrapper script from ${scopeFilePath}:
|
|
3134
|
+
${msg}`);
|
|
3135
|
+
}
|
|
3136
|
+
return result.outputFiles?.[0]?.text ?? "";
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
// src/render-commands.ts
|
|
2984
3140
|
var MANIFEST_PATH6 = ".reactscope/manifest.json";
|
|
2985
3141
|
var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
|
|
2986
3142
|
var _pool3 = null;
|
|
@@ -3001,7 +3157,7 @@ async function shutdownPool3() {
|
|
|
3001
3157
|
_pool3 = null;
|
|
3002
3158
|
}
|
|
3003
3159
|
}
|
|
3004
|
-
function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
3160
|
+
function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, wrapperScript) {
|
|
3005
3161
|
const satori = new SatoriRenderer({
|
|
3006
3162
|
defaultViewport: { width: viewportWidth, height: viewportHeight }
|
|
3007
3163
|
});
|
|
@@ -3014,7 +3170,10 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
|
3014
3170
|
filePath,
|
|
3015
3171
|
componentName,
|
|
3016
3172
|
props,
|
|
3017
|
-
viewportWidth
|
|
3173
|
+
viewportWidth,
|
|
3174
|
+
void 0,
|
|
3175
|
+
// projectCss (handled separately)
|
|
3176
|
+
wrapperScript
|
|
3018
3177
|
);
|
|
3019
3178
|
const slot = await pool.acquire();
|
|
3020
3179
|
const { page } = slot;
|
|
@@ -3105,8 +3264,37 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
|
3105
3264
|
}
|
|
3106
3265
|
};
|
|
3107
3266
|
}
|
|
3267
|
+
function buildScenarioMap(opts, scopeData) {
|
|
3268
|
+
if (opts.scenario !== void 0) {
|
|
3269
|
+
if (scopeData === null) {
|
|
3270
|
+
throw new Error(`--scenario "${opts.scenario}" requires a .scope file next to the component`);
|
|
3271
|
+
}
|
|
3272
|
+
const props = scopeData.scenarios[opts.scenario];
|
|
3273
|
+
if (props === void 0) {
|
|
3274
|
+
const available = Object.keys(scopeData.scenarios).join(", ") || "(none)";
|
|
3275
|
+
throw new Error(
|
|
3276
|
+
`Scenario "${opts.scenario}" not found in scope file.
|
|
3277
|
+
Available: ${available}`
|
|
3278
|
+
);
|
|
3279
|
+
}
|
|
3280
|
+
return { [opts.scenario]: props };
|
|
3281
|
+
}
|
|
3282
|
+
if (opts.props !== void 0) {
|
|
3283
|
+
let parsed;
|
|
3284
|
+
try {
|
|
3285
|
+
parsed = JSON.parse(opts.props);
|
|
3286
|
+
} catch {
|
|
3287
|
+
throw new Error(`Invalid props JSON: ${opts.props}`);
|
|
3288
|
+
}
|
|
3289
|
+
return { __default__: parsed };
|
|
3290
|
+
}
|
|
3291
|
+
if (scopeData !== null && Object.keys(scopeData.scenarios).length > 0) {
|
|
3292
|
+
return scopeData.scenarios;
|
|
3293
|
+
}
|
|
3294
|
+
return { __default__: {} };
|
|
3295
|
+
}
|
|
3108
3296
|
function registerRenderSingle(renderCmd) {
|
|
3109
|
-
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(
|
|
3297
|
+
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(
|
|
3110
3298
|
async (componentName, opts) => {
|
|
3111
3299
|
try {
|
|
3112
3300
|
const manifest = loadManifest(opts.manifest);
|
|
@@ -3118,80 +3306,71 @@ function registerRenderSingle(renderCmd) {
|
|
|
3118
3306
|
Available: ${available}`
|
|
3119
3307
|
);
|
|
3120
3308
|
}
|
|
3121
|
-
let props = {};
|
|
3122
|
-
if (opts.props !== void 0) {
|
|
3123
|
-
try {
|
|
3124
|
-
props = JSON.parse(opts.props);
|
|
3125
|
-
} catch {
|
|
3126
|
-
throw new Error(`Invalid props JSON: ${opts.props}`);
|
|
3127
|
-
}
|
|
3128
|
-
}
|
|
3129
3309
|
const { width, height } = parseViewport(opts.viewport);
|
|
3130
3310
|
const rootDir = process.cwd();
|
|
3131
|
-
const filePath =
|
|
3132
|
-
const
|
|
3311
|
+
const filePath = resolve9(rootDir, descriptor.filePath);
|
|
3312
|
+
const scopeData = await loadScopeFileForComponent(filePath);
|
|
3313
|
+
const wrapperScript = scopeData?.hasWrapper === true ? await buildWrapperScript(scopeData.filePath) : void 0;
|
|
3314
|
+
const scenarios = buildScenarioMap(opts, scopeData);
|
|
3315
|
+
const renderer = buildRenderer(filePath, componentName, width, height, wrapperScript);
|
|
3133
3316
|
process.stderr.write(
|
|
3134
3317
|
`Rendering ${componentName} [${descriptor.complexityClass}] at ${width}\xD7${height}\u2026
|
|
3135
3318
|
`
|
|
3136
3319
|
);
|
|
3137
|
-
const
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3320
|
+
const fmt2 = resolveSingleFormat(opts.format);
|
|
3321
|
+
let anyFailed = false;
|
|
3322
|
+
for (const [scenarioName, props] of Object.entries(scenarios)) {
|
|
3323
|
+
const isNamed = scenarioName !== "__default__";
|
|
3324
|
+
const label = isNamed ? `${componentName}:${scenarioName}` : componentName;
|
|
3325
|
+
const outcome = await safeRender2(
|
|
3326
|
+
() => renderer.renderCell(props, descriptor.complexityClass),
|
|
3327
|
+
{
|
|
3328
|
+
props,
|
|
3329
|
+
sourceLocation: {
|
|
3330
|
+
file: descriptor.filePath,
|
|
3331
|
+
line: descriptor.loc.start,
|
|
3332
|
+
column: 0
|
|
3333
|
+
}
|
|
3145
3334
|
}
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
if (outcome.crashed) {
|
|
3150
|
-
process.stderr.write(`\u2717 Render failed: ${outcome.error.message}
|
|
3335
|
+
);
|
|
3336
|
+
if (outcome.crashed) {
|
|
3337
|
+
process.stderr.write(`\u2717 ${label} render failed: ${outcome.error.message}
|
|
3151
3338
|
`);
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3339
|
+
const hintList = outcome.error.heuristicFlags.join(", ");
|
|
3340
|
+
if (hintList.length > 0) {
|
|
3341
|
+
process.stderr.write(` Hints: ${hintList}
|
|
3155
3342
|
`);
|
|
3343
|
+
}
|
|
3344
|
+
anyFailed = true;
|
|
3345
|
+
continue;
|
|
3156
3346
|
}
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
`\u2713 ${componentName} \u2192 ${opts.output} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3347
|
+
const result = outcome.result;
|
|
3348
|
+
const outFileName = isNamed ? `${componentName}-${scenarioName}.png` : `${componentName}.png`;
|
|
3349
|
+
if (opts.output !== void 0 && !isNamed) {
|
|
3350
|
+
const outPath = resolve9(process.cwd(), opts.output);
|
|
3351
|
+
writeFileSync5(outPath, result.screenshot);
|
|
3352
|
+
process.stdout.write(
|
|
3353
|
+
`\u2713 ${label} \u2192 ${opts.output} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3165
3354
|
`
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
if (fmt2 === "json") {
|
|
3171
|
-
const json = formatRenderJson(componentName, props, result);
|
|
3172
|
-
process.stdout.write(`${JSON.stringify(json, null, 2)}
|
|
3355
|
+
);
|
|
3356
|
+
} else if (fmt2 === "json") {
|
|
3357
|
+
const json = formatRenderJson(label, props, result);
|
|
3358
|
+
process.stdout.write(`${JSON.stringify(json, null, 2)}
|
|
3173
3359
|
`);
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
`
|
|
3183
|
-
);
|
|
3184
|
-
} else {
|
|
3185
|
-
const dir = resolve8(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
3186
|
-
mkdirSync3(dir, { recursive: true });
|
|
3187
|
-
const outPath = resolve8(dir, `${componentName}.png`);
|
|
3188
|
-
writeFileSync5(outPath, result.screenshot);
|
|
3189
|
-
const relPath = `${DEFAULT_OUTPUT_DIR}/${componentName}.png`;
|
|
3190
|
-
process.stdout.write(
|
|
3191
|
-
`\u2713 ${componentName} \u2192 ${relPath} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3360
|
+
} else {
|
|
3361
|
+
const dir = resolve9(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
3362
|
+
mkdirSync4(dir, { recursive: true });
|
|
3363
|
+
const outPath = resolve9(dir, outFileName);
|
|
3364
|
+
writeFileSync5(outPath, result.screenshot);
|
|
3365
|
+
const relPath = `${DEFAULT_OUTPUT_DIR}/${outFileName}`;
|
|
3366
|
+
process.stdout.write(
|
|
3367
|
+
`\u2713 ${label} \u2192 ${relPath} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
|
|
3192
3368
|
`
|
|
3193
|
-
|
|
3369
|
+
);
|
|
3370
|
+
}
|
|
3194
3371
|
}
|
|
3372
|
+
await shutdownPool3();
|
|
3373
|
+
if (anyFailed) process.exit(1);
|
|
3195
3374
|
} catch (err) {
|
|
3196
3375
|
await shutdownPool3();
|
|
3197
3376
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3220,7 +3399,7 @@ Available: ${available}`
|
|
|
3220
3399
|
const concurrency = Math.max(1, parseInt(opts.concurrency, 10) || 8);
|
|
3221
3400
|
const { width, height } = { width: 375, height: 812 };
|
|
3222
3401
|
const rootDir = process.cwd();
|
|
3223
|
-
const filePath =
|
|
3402
|
+
const filePath = resolve9(rootDir, descriptor.filePath);
|
|
3224
3403
|
const renderer = buildRenderer(filePath, componentName, width, height);
|
|
3225
3404
|
const axes = [];
|
|
3226
3405
|
if (opts.axes !== void 0) {
|
|
@@ -3287,7 +3466,7 @@ Available: ${available}`
|
|
|
3287
3466
|
const { SpriteSheetGenerator: SpriteSheetGenerator2 } = await import("@agent-scope/render");
|
|
3288
3467
|
const gen = new SpriteSheetGenerator2();
|
|
3289
3468
|
const sheet = await gen.generate(result);
|
|
3290
|
-
const spritePath =
|
|
3469
|
+
const spritePath = resolve9(process.cwd(), opts.sprite);
|
|
3291
3470
|
writeFileSync5(spritePath, sheet.png);
|
|
3292
3471
|
process.stderr.write(`Sprite sheet saved to ${spritePath}
|
|
3293
3472
|
`);
|
|
@@ -3297,9 +3476,9 @@ Available: ${available}`
|
|
|
3297
3476
|
const { SpriteSheetGenerator: SpriteSheetGenerator2 } = await import("@agent-scope/render");
|
|
3298
3477
|
const gen = new SpriteSheetGenerator2();
|
|
3299
3478
|
const sheet = await gen.generate(result);
|
|
3300
|
-
const dir =
|
|
3301
|
-
|
|
3302
|
-
const outPath =
|
|
3479
|
+
const dir = resolve9(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
3480
|
+
mkdirSync4(dir, { recursive: true });
|
|
3481
|
+
const outPath = resolve9(dir, `${componentName}-matrix.png`);
|
|
3303
3482
|
writeFileSync5(outPath, sheet.png);
|
|
3304
3483
|
const relPath = `${DEFAULT_OUTPUT_DIR}/${componentName}-matrix.png`;
|
|
3305
3484
|
process.stdout.write(
|
|
@@ -3343,8 +3522,8 @@ function registerRenderAll(renderCmd) {
|
|
|
3343
3522
|
return;
|
|
3344
3523
|
}
|
|
3345
3524
|
const concurrency = Math.max(1, parseInt(opts.concurrency, 10) || 4);
|
|
3346
|
-
const outputDir =
|
|
3347
|
-
|
|
3525
|
+
const outputDir = resolve9(process.cwd(), opts.outputDir);
|
|
3526
|
+
mkdirSync4(outputDir, { recursive: true });
|
|
3348
3527
|
const rootDir = process.cwd();
|
|
3349
3528
|
process.stderr.write(`Rendering ${total} components (concurrency: ${concurrency})\u2026
|
|
3350
3529
|
`);
|
|
@@ -3353,7 +3532,7 @@ function registerRenderAll(renderCmd) {
|
|
|
3353
3532
|
const renderOne = async (name) => {
|
|
3354
3533
|
const descriptor = manifest.components[name];
|
|
3355
3534
|
if (descriptor === void 0) return;
|
|
3356
|
-
const filePath =
|
|
3535
|
+
const filePath = resolve9(rootDir, descriptor.filePath);
|
|
3357
3536
|
const renderer = buildRenderer(filePath, name, 375, 812);
|
|
3358
3537
|
const outcome = await safeRender2(
|
|
3359
3538
|
() => renderer.renderCell({}, descriptor.complexityClass),
|
|
@@ -3376,7 +3555,7 @@ function registerRenderAll(renderCmd) {
|
|
|
3376
3555
|
success: false,
|
|
3377
3556
|
errorMessage: outcome.error.message
|
|
3378
3557
|
});
|
|
3379
|
-
const errPath =
|
|
3558
|
+
const errPath = resolve9(outputDir, `${name}.error.json`);
|
|
3380
3559
|
writeFileSync5(
|
|
3381
3560
|
errPath,
|
|
3382
3561
|
JSON.stringify(
|
|
@@ -3394,9 +3573,9 @@ function registerRenderAll(renderCmd) {
|
|
|
3394
3573
|
}
|
|
3395
3574
|
const result = outcome.result;
|
|
3396
3575
|
results.push({ name, renderTimeMs: result.renderTimeMs, success: true });
|
|
3397
|
-
const pngPath =
|
|
3576
|
+
const pngPath = resolve9(outputDir, `${name}.png`);
|
|
3398
3577
|
writeFileSync5(pngPath, result.screenshot);
|
|
3399
|
-
const jsonPath =
|
|
3578
|
+
const jsonPath = resolve9(outputDir, `${name}.json`);
|
|
3400
3579
|
writeFileSync5(jsonPath, JSON.stringify(formatRenderJson(name, {}, result), null, 2));
|
|
3401
3580
|
if (isTTY()) {
|
|
3402
3581
|
process.stdout.write(
|
|
@@ -3469,8 +3648,8 @@ function createRenderCommand() {
|
|
|
3469
3648
|
}
|
|
3470
3649
|
|
|
3471
3650
|
// src/report/baseline.ts
|
|
3472
|
-
import { existsSync as
|
|
3473
|
-
import { resolve as
|
|
3651
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
3652
|
+
import { resolve as resolve10 } from "path";
|
|
3474
3653
|
import { generateManifest as generateManifest3 } from "@agent-scope/manifest";
|
|
3475
3654
|
import { BrowserPool as BrowserPool4, safeRender as safeRender3 } from "@agent-scope/render";
|
|
3476
3655
|
import { ComplianceEngine as ComplianceEngine2, TokenResolver as TokenResolver2 } from "@agent-scope/tokens";
|
|
@@ -3619,17 +3798,17 @@ async function runBaseline(options = {}) {
|
|
|
3619
3798
|
} = options;
|
|
3620
3799
|
const startTime = performance.now();
|
|
3621
3800
|
const rootDir = process.cwd();
|
|
3622
|
-
const baselineDir =
|
|
3623
|
-
const rendersDir =
|
|
3624
|
-
if (
|
|
3625
|
-
|
|
3801
|
+
const baselineDir = resolve10(rootDir, outputDir);
|
|
3802
|
+
const rendersDir = resolve10(baselineDir, "renders");
|
|
3803
|
+
if (existsSync7(baselineDir)) {
|
|
3804
|
+
rmSync2(baselineDir, { recursive: true, force: true });
|
|
3626
3805
|
}
|
|
3627
|
-
|
|
3806
|
+
mkdirSync5(rendersDir, { recursive: true });
|
|
3628
3807
|
let manifest;
|
|
3629
3808
|
if (manifestPath !== void 0) {
|
|
3630
3809
|
const { readFileSync: readFileSync12 } = await import("fs");
|
|
3631
|
-
const absPath =
|
|
3632
|
-
if (!
|
|
3810
|
+
const absPath = resolve10(rootDir, manifestPath);
|
|
3811
|
+
if (!existsSync7(absPath)) {
|
|
3633
3812
|
throw new Error(`Manifest not found at ${absPath}.`);
|
|
3634
3813
|
}
|
|
3635
3814
|
manifest = JSON.parse(readFileSync12(absPath, "utf-8"));
|
|
@@ -3642,7 +3821,7 @@ async function runBaseline(options = {}) {
|
|
|
3642
3821
|
process.stderr.write(`Found ${count} components.
|
|
3643
3822
|
`);
|
|
3644
3823
|
}
|
|
3645
|
-
writeFileSync6(
|
|
3824
|
+
writeFileSync6(resolve10(baselineDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
3646
3825
|
let componentNames = Object.keys(manifest.components);
|
|
3647
3826
|
if (componentsGlob !== void 0) {
|
|
3648
3827
|
componentNames = componentNames.filter((name) => matchGlob(componentsGlob, name));
|
|
@@ -3663,7 +3842,7 @@ async function runBaseline(options = {}) {
|
|
|
3663
3842
|
auditedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3664
3843
|
};
|
|
3665
3844
|
writeFileSync6(
|
|
3666
|
-
|
|
3845
|
+
resolve10(baselineDir, "compliance.json"),
|
|
3667
3846
|
JSON.stringify(emptyReport, null, 2),
|
|
3668
3847
|
"utf-8"
|
|
3669
3848
|
);
|
|
@@ -3684,7 +3863,7 @@ async function runBaseline(options = {}) {
|
|
|
3684
3863
|
const renderOne = async (name) => {
|
|
3685
3864
|
const descriptor = manifest.components[name];
|
|
3686
3865
|
if (descriptor === void 0) return;
|
|
3687
|
-
const filePath =
|
|
3866
|
+
const filePath = resolve10(rootDir, descriptor.filePath);
|
|
3688
3867
|
const outcome = await safeRender3(
|
|
3689
3868
|
() => renderComponent2(filePath, name, {}, viewportWidth, viewportHeight),
|
|
3690
3869
|
{
|
|
@@ -3703,7 +3882,7 @@ async function runBaseline(options = {}) {
|
|
|
3703
3882
|
}
|
|
3704
3883
|
if (outcome.crashed) {
|
|
3705
3884
|
failureCount++;
|
|
3706
|
-
const errPath =
|
|
3885
|
+
const errPath = resolve10(rendersDir, `${name}.error.json`);
|
|
3707
3886
|
writeFileSync6(
|
|
3708
3887
|
errPath,
|
|
3709
3888
|
JSON.stringify(
|
|
@@ -3721,10 +3900,10 @@ async function runBaseline(options = {}) {
|
|
|
3721
3900
|
return;
|
|
3722
3901
|
}
|
|
3723
3902
|
const result = outcome.result;
|
|
3724
|
-
writeFileSync6(
|
|
3903
|
+
writeFileSync6(resolve10(rendersDir, `${name}.png`), result.screenshot);
|
|
3725
3904
|
const jsonOutput = formatRenderJson(name, {}, result);
|
|
3726
3905
|
writeFileSync6(
|
|
3727
|
-
|
|
3906
|
+
resolve10(rendersDir, `${name}.json`),
|
|
3728
3907
|
JSON.stringify(jsonOutput, null, 2),
|
|
3729
3908
|
"utf-8"
|
|
3730
3909
|
);
|
|
@@ -3752,7 +3931,7 @@ async function runBaseline(options = {}) {
|
|
|
3752
3931
|
const engine = new ComplianceEngine2(resolver);
|
|
3753
3932
|
const batchReport = engine.auditBatch(computedStylesMap);
|
|
3754
3933
|
writeFileSync6(
|
|
3755
|
-
|
|
3934
|
+
resolve10(baselineDir, "compliance.json"),
|
|
3756
3935
|
JSON.stringify(batchReport, null, 2),
|
|
3757
3936
|
"utf-8"
|
|
3758
3937
|
);
|
|
@@ -3795,21 +3974,21 @@ function registerBaselineSubCommand(reportCmd) {
|
|
|
3795
3974
|
}
|
|
3796
3975
|
|
|
3797
3976
|
// src/report/diff.ts
|
|
3798
|
-
import { existsSync as
|
|
3799
|
-
import { resolve as
|
|
3977
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
3978
|
+
import { resolve as resolve11 } from "path";
|
|
3800
3979
|
import { generateManifest as generateManifest4 } from "@agent-scope/manifest";
|
|
3801
3980
|
import { BrowserPool as BrowserPool5, safeRender as safeRender4 } from "@agent-scope/render";
|
|
3802
3981
|
import { ComplianceEngine as ComplianceEngine3, TokenResolver as TokenResolver3 } from "@agent-scope/tokens";
|
|
3803
3982
|
var DEFAULT_BASELINE_DIR2 = ".reactscope/baseline";
|
|
3804
3983
|
function loadBaselineCompliance(baselineDir) {
|
|
3805
|
-
const compliancePath =
|
|
3806
|
-
if (!
|
|
3984
|
+
const compliancePath = resolve11(baselineDir, "compliance.json");
|
|
3985
|
+
if (!existsSync8(compliancePath)) return null;
|
|
3807
3986
|
const raw = JSON.parse(readFileSync6(compliancePath, "utf-8"));
|
|
3808
3987
|
return raw;
|
|
3809
3988
|
}
|
|
3810
3989
|
function loadBaselineRenderJson2(baselineDir, componentName) {
|
|
3811
|
-
const jsonPath =
|
|
3812
|
-
if (!
|
|
3990
|
+
const jsonPath = resolve11(baselineDir, "renders", `${componentName}.json`);
|
|
3991
|
+
if (!existsSync8(jsonPath)) return null;
|
|
3813
3992
|
return JSON.parse(readFileSync6(jsonPath, "utf-8"));
|
|
3814
3993
|
}
|
|
3815
3994
|
var _pool5 = null;
|
|
@@ -3977,14 +4156,14 @@ async function runDiff(options = {}) {
|
|
|
3977
4156
|
} = options;
|
|
3978
4157
|
const startTime = performance.now();
|
|
3979
4158
|
const rootDir = process.cwd();
|
|
3980
|
-
const baselineDir =
|
|
3981
|
-
if (!
|
|
4159
|
+
const baselineDir = resolve11(rootDir, baselineDirRaw);
|
|
4160
|
+
if (!existsSync8(baselineDir)) {
|
|
3982
4161
|
throw new Error(
|
|
3983
4162
|
`Baseline directory not found at "${baselineDir}". Run \`scope report baseline\` first to create a baseline snapshot.`
|
|
3984
4163
|
);
|
|
3985
4164
|
}
|
|
3986
|
-
const baselineManifestPath =
|
|
3987
|
-
if (!
|
|
4165
|
+
const baselineManifestPath = resolve11(baselineDir, "manifest.json");
|
|
4166
|
+
if (!existsSync8(baselineManifestPath)) {
|
|
3988
4167
|
throw new Error(
|
|
3989
4168
|
`Baseline manifest.json not found at "${baselineManifestPath}". The baseline directory may be incomplete \u2014 re-run \`scope report baseline\`.`
|
|
3990
4169
|
);
|
|
@@ -3998,8 +4177,8 @@ async function runDiff(options = {}) {
|
|
|
3998
4177
|
);
|
|
3999
4178
|
let currentManifest;
|
|
4000
4179
|
if (manifestPath !== void 0) {
|
|
4001
|
-
const absPath =
|
|
4002
|
-
if (!
|
|
4180
|
+
const absPath = resolve11(rootDir, manifestPath);
|
|
4181
|
+
if (!existsSync8(absPath)) {
|
|
4003
4182
|
throw new Error(`Manifest not found at "${absPath}".`);
|
|
4004
4183
|
}
|
|
4005
4184
|
currentManifest = JSON.parse(readFileSync6(absPath, "utf-8"));
|
|
@@ -4035,7 +4214,7 @@ async function runDiff(options = {}) {
|
|
|
4035
4214
|
const renderOne = async (name) => {
|
|
4036
4215
|
const descriptor = currentManifest.components[name];
|
|
4037
4216
|
if (descriptor === void 0) return;
|
|
4038
|
-
const filePath =
|
|
4217
|
+
const filePath = resolve11(rootDir, descriptor.filePath);
|
|
4039
4218
|
const outcome = await safeRender4(
|
|
4040
4219
|
() => renderComponent3(filePath, name, {}, viewportWidth, viewportHeight),
|
|
4041
4220
|
{
|
|
@@ -4275,8 +4454,8 @@ function registerDiffSubCommand(reportCmd) {
|
|
|
4275
4454
|
}
|
|
4276
4455
|
|
|
4277
4456
|
// src/report/pr-comment.ts
|
|
4278
|
-
import { existsSync as
|
|
4279
|
-
import { resolve as
|
|
4457
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
4458
|
+
import { resolve as resolve12 } from "path";
|
|
4280
4459
|
var STATUS_BADGE = {
|
|
4281
4460
|
added: "\u2705 added",
|
|
4282
4461
|
removed: "\u{1F5D1}\uFE0F removed",
|
|
@@ -4359,8 +4538,8 @@ function formatPrComment(diff) {
|
|
|
4359
4538
|
return lines.join("\n");
|
|
4360
4539
|
}
|
|
4361
4540
|
function loadDiffResult(filePath) {
|
|
4362
|
-
const abs =
|
|
4363
|
-
if (!
|
|
4541
|
+
const abs = resolve12(filePath);
|
|
4542
|
+
if (!existsSync9(abs)) {
|
|
4364
4543
|
throw new Error(`DiffResult file not found: ${abs}`);
|
|
4365
4544
|
}
|
|
4366
4545
|
let raw;
|
|
@@ -4392,7 +4571,7 @@ function registerPrCommentSubCommand(reportCmd) {
|
|
|
4392
4571
|
const diff = loadDiffResult(opts.input);
|
|
4393
4572
|
const comment = formatPrComment(diff);
|
|
4394
4573
|
if (opts.output !== void 0) {
|
|
4395
|
-
writeFileSync8(
|
|
4574
|
+
writeFileSync8(resolve12(opts.output), comment, "utf-8");
|
|
4396
4575
|
process.stderr.write(`PR comment written to ${opts.output}
|
|
4397
4576
|
`);
|
|
4398
4577
|
} else {
|
|
@@ -4687,9 +4866,9 @@ function buildStructuredReport(report) {
|
|
|
4687
4866
|
}
|
|
4688
4867
|
|
|
4689
4868
|
// src/site-commands.ts
|
|
4690
|
-
import { createReadStream, existsSync as
|
|
4869
|
+
import { createReadStream, existsSync as existsSync10, statSync } from "fs";
|
|
4691
4870
|
import { createServer } from "http";
|
|
4692
|
-
import { extname, join as
|
|
4871
|
+
import { extname, join as join4, resolve as resolve13 } from "path";
|
|
4693
4872
|
import { buildSite } from "@agent-scope/site";
|
|
4694
4873
|
import { Command as Command7 } from "commander";
|
|
4695
4874
|
var MIME_TYPES = {
|
|
@@ -4707,16 +4886,16 @@ function registerBuild(siteCmd) {
|
|
|
4707
4886
|
siteCmd.command("build").description("Build a static HTML gallery from .reactscope/ output").option("-i, --input <path>", "Path to .reactscope input directory", ".reactscope").option("-o, --output <path>", "Output directory for generated site", ".reactscope/site").option("--base-path <path>", "Base URL path prefix for subdirectory deployment", "/").option("--compliance <path>", "Path to compliance batch report JSON").option("--title <text>", "Site title", "Scope \u2014 Component Gallery").action(
|
|
4708
4887
|
async (opts) => {
|
|
4709
4888
|
try {
|
|
4710
|
-
const inputDir =
|
|
4711
|
-
const outputDir =
|
|
4712
|
-
if (!
|
|
4889
|
+
const inputDir = resolve13(process.cwd(), opts.input);
|
|
4890
|
+
const outputDir = resolve13(process.cwd(), opts.output);
|
|
4891
|
+
if (!existsSync10(inputDir)) {
|
|
4713
4892
|
throw new Error(
|
|
4714
4893
|
`Input directory not found: ${inputDir}
|
|
4715
4894
|
Run \`scope manifest generate\` and \`scope render\` first.`
|
|
4716
4895
|
);
|
|
4717
4896
|
}
|
|
4718
|
-
const manifestPath =
|
|
4719
|
-
if (!
|
|
4897
|
+
const manifestPath = join4(inputDir, "manifest.json");
|
|
4898
|
+
if (!existsSync10(manifestPath)) {
|
|
4720
4899
|
throw new Error(
|
|
4721
4900
|
`Manifest not found at ${manifestPath}
|
|
4722
4901
|
Run \`scope manifest generate\` first.`
|
|
@@ -4729,7 +4908,7 @@ Run \`scope manifest generate\` first.`
|
|
|
4729
4908
|
outputDir,
|
|
4730
4909
|
basePath: opts.basePath,
|
|
4731
4910
|
...opts.compliance !== void 0 && {
|
|
4732
|
-
compliancePath:
|
|
4911
|
+
compliancePath: resolve13(process.cwd(), opts.compliance)
|
|
4733
4912
|
},
|
|
4734
4913
|
title: opts.title
|
|
4735
4914
|
});
|
|
@@ -4752,8 +4931,8 @@ function registerServe(siteCmd) {
|
|
|
4752
4931
|
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
4753
4932
|
throw new Error(`Invalid port: ${opts.port}`);
|
|
4754
4933
|
}
|
|
4755
|
-
const serveDir =
|
|
4756
|
-
if (!
|
|
4934
|
+
const serveDir = resolve13(process.cwd(), opts.dir);
|
|
4935
|
+
if (!existsSync10(serveDir)) {
|
|
4757
4936
|
throw new Error(
|
|
4758
4937
|
`Serve directory not found: ${serveDir}
|
|
4759
4938
|
Run \`scope site build\` first.`
|
|
@@ -4762,13 +4941,13 @@ Run \`scope site build\` first.`
|
|
|
4762
4941
|
const server = createServer((req, res) => {
|
|
4763
4942
|
const rawUrl = req.url ?? "/";
|
|
4764
4943
|
const urlPath = decodeURIComponent(rawUrl.split("?")[0] ?? "/");
|
|
4765
|
-
const filePath =
|
|
4944
|
+
const filePath = join4(serveDir, urlPath.endsWith("/") ? `${urlPath}index.html` : urlPath);
|
|
4766
4945
|
if (!filePath.startsWith(serveDir)) {
|
|
4767
4946
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
4768
4947
|
res.end("Forbidden");
|
|
4769
4948
|
return;
|
|
4770
4949
|
}
|
|
4771
|
-
if (
|
|
4950
|
+
if (existsSync10(filePath) && statSync(filePath).isFile()) {
|
|
4772
4951
|
const ext = extname(filePath).toLowerCase();
|
|
4773
4952
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
4774
4953
|
res.writeHead(200, { "Content-Type": contentType });
|
|
@@ -4776,7 +4955,7 @@ Run \`scope site build\` first.`
|
|
|
4776
4955
|
return;
|
|
4777
4956
|
}
|
|
4778
4957
|
const htmlPath = `${filePath}.html`;
|
|
4779
|
-
if (
|
|
4958
|
+
if (existsSync10(htmlPath) && statSync(htmlPath).isFile()) {
|
|
4780
4959
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
4781
4960
|
createReadStream(htmlPath).pipe(res);
|
|
4782
4961
|
return;
|
|
@@ -4818,8 +4997,8 @@ function createSiteCommand() {
|
|
|
4818
4997
|
}
|
|
4819
4998
|
|
|
4820
4999
|
// src/tokens/commands.ts
|
|
4821
|
-
import { existsSync as
|
|
4822
|
-
import { resolve as
|
|
5000
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
5001
|
+
import { resolve as resolve17 } from "path";
|
|
4823
5002
|
import {
|
|
4824
5003
|
parseTokenFileSync as parseTokenFileSync2,
|
|
4825
5004
|
TokenParseError,
|
|
@@ -4830,16 +5009,16 @@ import {
|
|
|
4830
5009
|
import { Command as Command9 } from "commander";
|
|
4831
5010
|
|
|
4832
5011
|
// src/tokens/compliance.ts
|
|
4833
|
-
import { existsSync as
|
|
4834
|
-
import { resolve as
|
|
5012
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
5013
|
+
import { resolve as resolve14 } from "path";
|
|
4835
5014
|
import {
|
|
4836
5015
|
ComplianceEngine as ComplianceEngine4,
|
|
4837
5016
|
TokenResolver as TokenResolver4
|
|
4838
5017
|
} from "@agent-scope/tokens";
|
|
4839
5018
|
var DEFAULT_STYLES_PATH = ".reactscope/compliance-styles.json";
|
|
4840
5019
|
function loadStylesFile(stylesPath) {
|
|
4841
|
-
const absPath =
|
|
4842
|
-
if (!
|
|
5020
|
+
const absPath = resolve14(process.cwd(), stylesPath);
|
|
5021
|
+
if (!existsSync11(absPath)) {
|
|
4843
5022
|
throw new Error(
|
|
4844
5023
|
`Compliance styles file not found at ${absPath}.
|
|
4845
5024
|
Run \`scope render all\` first to generate component styles, or use --styles to specify a path.
|
|
@@ -5006,8 +5185,8 @@ function registerCompliance(tokensCmd) {
|
|
|
5006
5185
|
}
|
|
5007
5186
|
|
|
5008
5187
|
// src/tokens/export.ts
|
|
5009
|
-
import { existsSync as
|
|
5010
|
-
import { resolve as
|
|
5188
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
5189
|
+
import { resolve as resolve15 } from "path";
|
|
5011
5190
|
import {
|
|
5012
5191
|
exportTokens,
|
|
5013
5192
|
parseTokenFileSync,
|
|
@@ -5020,21 +5199,21 @@ var CONFIG_FILE = "reactscope.config.json";
|
|
|
5020
5199
|
var SUPPORTED_FORMATS = ["css", "ts", "scss", "tailwind", "flat-json", "figma"];
|
|
5021
5200
|
function resolveTokenFilePath2(fileFlag) {
|
|
5022
5201
|
if (fileFlag !== void 0) {
|
|
5023
|
-
return
|
|
5202
|
+
return resolve15(process.cwd(), fileFlag);
|
|
5024
5203
|
}
|
|
5025
|
-
const configPath =
|
|
5026
|
-
if (
|
|
5204
|
+
const configPath = resolve15(process.cwd(), CONFIG_FILE);
|
|
5205
|
+
if (existsSync12(configPath)) {
|
|
5027
5206
|
try {
|
|
5028
5207
|
const raw = readFileSync9(configPath, "utf-8");
|
|
5029
5208
|
const config = JSON.parse(raw);
|
|
5030
5209
|
if (typeof config === "object" && config !== null && "tokens" in config && typeof config.tokens === "object" && config.tokens !== null && typeof config.tokens?.file === "string") {
|
|
5031
5210
|
const file = config.tokens.file;
|
|
5032
|
-
return
|
|
5211
|
+
return resolve15(process.cwd(), file);
|
|
5033
5212
|
}
|
|
5034
5213
|
} catch {
|
|
5035
5214
|
}
|
|
5036
5215
|
}
|
|
5037
|
-
return
|
|
5216
|
+
return resolve15(process.cwd(), DEFAULT_TOKEN_FILE);
|
|
5038
5217
|
}
|
|
5039
5218
|
function createTokensExportCommand() {
|
|
5040
5219
|
return new Command8("export").description("Export design tokens to a downstream format").requiredOption("--format <fmt>", `Output format: ${SUPPORTED_FORMATS.join(", ")}`).option("--file <path>", "Path to token file (overrides config)").option("--out <path>", "Write output to file instead of stdout").option("--prefix <prefix>", "CSS/SCSS: prefix for variable names (e.g. 'scope')").option("--selector <selector>", "CSS: custom root selector (default: ':root')").option(
|
|
@@ -5053,7 +5232,7 @@ Supported formats: ${SUPPORTED_FORMATS.join(", ")}
|
|
|
5053
5232
|
const format = opts.format;
|
|
5054
5233
|
try {
|
|
5055
5234
|
const filePath = resolveTokenFilePath2(opts.file);
|
|
5056
|
-
if (!
|
|
5235
|
+
if (!existsSync12(filePath)) {
|
|
5057
5236
|
throw new Error(
|
|
5058
5237
|
`Token file not found at ${filePath}.
|
|
5059
5238
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|
|
@@ -5098,7 +5277,7 @@ Available themes: ${themeNames.join(", ")}`
|
|
|
5098
5277
|
themes: themesMap
|
|
5099
5278
|
});
|
|
5100
5279
|
if (opts.out !== void 0) {
|
|
5101
|
-
const outPath =
|
|
5280
|
+
const outPath = resolve15(process.cwd(), opts.out);
|
|
5102
5281
|
writeFileSync9(outPath, output, "utf-8");
|
|
5103
5282
|
process.stderr.write(`Exported ${tokens.length} tokens to ${outPath}
|
|
5104
5283
|
`);
|
|
@@ -5213,8 +5392,8 @@ ${formatImpactSummary(report)}
|
|
|
5213
5392
|
}
|
|
5214
5393
|
|
|
5215
5394
|
// src/tokens/preview.ts
|
|
5216
|
-
import { mkdirSync as
|
|
5217
|
-
import { resolve as
|
|
5395
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
5396
|
+
import { resolve as resolve16 } from "path";
|
|
5218
5397
|
import { BrowserPool as BrowserPool6, SpriteSheetGenerator } from "@agent-scope/render";
|
|
5219
5398
|
import { ComplianceEngine as ComplianceEngine6, ImpactAnalyzer as ImpactAnalyzer2, TokenResolver as TokenResolver7 } from "@agent-scope/tokens";
|
|
5220
5399
|
var DEFAULT_STYLES_PATH3 = ".reactscope/compliance-styles.json";
|
|
@@ -5395,9 +5574,9 @@ function registerPreview(tokensCmd) {
|
|
|
5395
5574
|
});
|
|
5396
5575
|
const spriteResult = await generator.generate(matrixResult);
|
|
5397
5576
|
const tokenLabel = tokenPath.replace(/\./g, "-");
|
|
5398
|
-
const outputPath = opts.output ??
|
|
5399
|
-
const outputDir =
|
|
5400
|
-
|
|
5577
|
+
const outputPath = opts.output ?? resolve16(process.cwd(), DEFAULT_OUTPUT_DIR2, `preview-${tokenLabel}.png`);
|
|
5578
|
+
const outputDir = resolve16(outputPath, "..");
|
|
5579
|
+
mkdirSync6(outputDir, { recursive: true });
|
|
5401
5580
|
writeFileSync10(outputPath, spriteResult.png);
|
|
5402
5581
|
const useJson = opts.format === "json" || opts.format !== "text" && !isTTY();
|
|
5403
5582
|
if (useJson) {
|
|
@@ -5457,24 +5636,24 @@ function buildTable2(headers, rows) {
|
|
|
5457
5636
|
}
|
|
5458
5637
|
function resolveTokenFilePath(fileFlag) {
|
|
5459
5638
|
if (fileFlag !== void 0) {
|
|
5460
|
-
return
|
|
5639
|
+
return resolve17(process.cwd(), fileFlag);
|
|
5461
5640
|
}
|
|
5462
|
-
const configPath =
|
|
5463
|
-
if (
|
|
5641
|
+
const configPath = resolve17(process.cwd(), CONFIG_FILE2);
|
|
5642
|
+
if (existsSync13(configPath)) {
|
|
5464
5643
|
try {
|
|
5465
5644
|
const raw = readFileSync10(configPath, "utf-8");
|
|
5466
5645
|
const config = JSON.parse(raw);
|
|
5467
5646
|
if (typeof config === "object" && config !== null && "tokens" in config && typeof config.tokens === "object" && config.tokens !== null && typeof config.tokens?.file === "string") {
|
|
5468
5647
|
const file = config.tokens.file;
|
|
5469
|
-
return
|
|
5648
|
+
return resolve17(process.cwd(), file);
|
|
5470
5649
|
}
|
|
5471
5650
|
} catch {
|
|
5472
5651
|
}
|
|
5473
5652
|
}
|
|
5474
|
-
return
|
|
5653
|
+
return resolve17(process.cwd(), DEFAULT_TOKEN_FILE2);
|
|
5475
5654
|
}
|
|
5476
5655
|
function loadTokens(absPath) {
|
|
5477
|
-
if (!
|
|
5656
|
+
if (!existsSync13(absPath)) {
|
|
5478
5657
|
throw new Error(
|
|
5479
5658
|
`Token file not found at ${absPath}.
|
|
5480
5659
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|
|
@@ -5694,7 +5873,7 @@ function registerValidate(tokensCmd) {
|
|
|
5694
5873
|
).option("--file <path>", "Path to token file (overrides config)").option("--format <fmt>", "Output format: json or text (default: auto-detect)").action((opts) => {
|
|
5695
5874
|
try {
|
|
5696
5875
|
const filePath = resolveTokenFilePath(opts.file);
|
|
5697
|
-
if (!
|
|
5876
|
+
if (!existsSync13(filePath)) {
|
|
5698
5877
|
throw new Error(
|
|
5699
5878
|
`Token file not found at ${filePath}.
|
|
5700
5879
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|