@agent-scope/cli 1.16.0 → 1.17.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/cli.js +236 -120
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +191 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +193 -83
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync, writeFileSync, readFileSync, mkdirSync, appendFileSync, readdirSync, rmSync } from 'fs';
|
|
2
|
-
import { resolve, join, dirname } from 'path';
|
|
1
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync, appendFileSync, readdirSync, rmSync, statSync, createReadStream } from 'fs';
|
|
2
|
+
import { resolve, join, extname, dirname } from 'path';
|
|
3
3
|
import { generateManifest } from '@agent-scope/manifest';
|
|
4
4
|
import { SpriteSheetGenerator, safeRender, BrowserPool, ALL_CONTEXT_IDS, contextAxis, stressAxis, ALL_STRESS_IDS, RenderMatrix, SatoriRenderer } from '@agent-scope/render';
|
|
5
5
|
import { TokenResolver, ComplianceEngine, parseTokenFileSync, ThemeResolver, exportTokens, validateTokenFile, TokenValidationError, TokenParseError, ImpactAnalyzer } from '@agent-scope/tokens';
|
|
@@ -7,8 +7,10 @@ import { Command } from 'commander';
|
|
|
7
7
|
import * as esbuild from 'esbuild';
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
import * as readline from 'readline';
|
|
10
|
-
import { chromium } from 'playwright';
|
|
11
10
|
import { loadTrace, generateTest, getBrowserEntryScript } from '@agent-scope/playwright';
|
|
11
|
+
import { chromium } from 'playwright';
|
|
12
|
+
import { createServer } from 'http';
|
|
13
|
+
import { buildSite } from '@agent-scope/site';
|
|
12
14
|
|
|
13
15
|
// src/ci/commands.ts
|
|
14
16
|
async function buildComponentHarness(filePath, componentName, props, viewportWidth, projectCss) {
|
|
@@ -1107,9 +1109,9 @@ function createRL() {
|
|
|
1107
1109
|
});
|
|
1108
1110
|
}
|
|
1109
1111
|
async function ask(rl, question) {
|
|
1110
|
-
return new Promise((
|
|
1112
|
+
return new Promise((resolve17) => {
|
|
1111
1113
|
rl.question(question, (answer) => {
|
|
1112
|
-
|
|
1114
|
+
resolve17(answer.trim());
|
|
1113
1115
|
});
|
|
1114
1116
|
});
|
|
1115
1117
|
}
|
|
@@ -1681,6 +1683,7 @@ async function runHooksProfiling(componentName, filePath, props) {
|
|
|
1681
1683
|
try {
|
|
1682
1684
|
const context = await browser.newContext();
|
|
1683
1685
|
const page = await context.newPage();
|
|
1686
|
+
await page.addInitScript({ content: getBrowserEntryScript() });
|
|
1684
1687
|
const htmlHarness = await buildComponentHarness(filePath, componentName, props, 1280);
|
|
1685
1688
|
await page.setContent(htmlHarness, { waitUntil: "load" });
|
|
1686
1689
|
await page.waitForFunction(
|
|
@@ -2064,24 +2067,6 @@ Available: ${available}`
|
|
|
2064
2067
|
var MANIFEST_PATH4 = ".reactscope/manifest.json";
|
|
2065
2068
|
var DEFAULT_VIEWPORT_WIDTH = 375;
|
|
2066
2069
|
var DEFAULT_VIEWPORT_HEIGHT = 812;
|
|
2067
|
-
var _pool2 = null;
|
|
2068
|
-
async function getPool2() {
|
|
2069
|
-
if (_pool2 === null) {
|
|
2070
|
-
_pool2 = new BrowserPool({
|
|
2071
|
-
size: { browsers: 1, pagesPerBrowser: 1 },
|
|
2072
|
-
viewportWidth: DEFAULT_VIEWPORT_WIDTH,
|
|
2073
|
-
viewportHeight: DEFAULT_VIEWPORT_HEIGHT
|
|
2074
|
-
});
|
|
2075
|
-
await _pool2.init();
|
|
2076
|
-
}
|
|
2077
|
-
return _pool2;
|
|
2078
|
-
}
|
|
2079
|
-
async function shutdownPool2() {
|
|
2080
|
-
if (_pool2 !== null) {
|
|
2081
|
-
await _pool2.close();
|
|
2082
|
-
_pool2 = null;
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
2070
|
function mapNodeType(node) {
|
|
2086
2071
|
if (node.type === "forward_ref") return "forwardRef";
|
|
2087
2072
|
if (node.type === "host") return "host";
|
|
@@ -2291,10 +2276,12 @@ function formatInstrumentTree(root, showProviderDepth = false) {
|
|
|
2291
2276
|
}
|
|
2292
2277
|
async function runInstrumentTree(options) {
|
|
2293
2278
|
const { componentName, filePath } = options;
|
|
2294
|
-
const
|
|
2295
|
-
const slot = await pool.acquire();
|
|
2296
|
-
const { page } = slot;
|
|
2279
|
+
const browser = await chromium.launch({ headless: true });
|
|
2297
2280
|
try {
|
|
2281
|
+
const context = await browser.newContext({
|
|
2282
|
+
viewport: { width: DEFAULT_VIEWPORT_WIDTH, height: DEFAULT_VIEWPORT_HEIGHT }
|
|
2283
|
+
});
|
|
2284
|
+
const page = await context.newPage();
|
|
2298
2285
|
await page.addInitScript({ content: getBrowserEntryScript() });
|
|
2299
2286
|
const htmlHarness = await buildComponentHarness(
|
|
2300
2287
|
filePath,
|
|
@@ -2348,7 +2335,7 @@ async function runInstrumentTree(options) {
|
|
|
2348
2335
|
}
|
|
2349
2336
|
return instrumentRoot;
|
|
2350
2337
|
} finally {
|
|
2351
|
-
|
|
2338
|
+
await browser.close();
|
|
2352
2339
|
}
|
|
2353
2340
|
}
|
|
2354
2341
|
function createInstrumentTreeCommand() {
|
|
@@ -2388,7 +2375,6 @@ Available: ${available}`
|
|
|
2388
2375
|
providerDepth: opts.providerDepth,
|
|
2389
2376
|
wastedRenders: opts.wastedRenders
|
|
2390
2377
|
});
|
|
2391
|
-
await shutdownPool2();
|
|
2392
2378
|
const fmt2 = resolveFormat2(opts.format);
|
|
2393
2379
|
if (fmt2 === "json") {
|
|
2394
2380
|
process.stdout.write(`${JSON.stringify(instrumentRoot, null, 2)}
|
|
@@ -2399,7 +2385,6 @@ Available: ${available}`
|
|
|
2399
2385
|
`);
|
|
2400
2386
|
}
|
|
2401
2387
|
} catch (err) {
|
|
2402
|
-
await shutdownPool2();
|
|
2403
2388
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2404
2389
|
`);
|
|
2405
2390
|
process.exit(1);
|
|
@@ -2717,22 +2702,22 @@ async function replayInteraction2(page, steps) {
|
|
|
2717
2702
|
}
|
|
2718
2703
|
}
|
|
2719
2704
|
}
|
|
2720
|
-
var
|
|
2721
|
-
async function
|
|
2722
|
-
if (
|
|
2723
|
-
|
|
2705
|
+
var _pool2 = null;
|
|
2706
|
+
async function getPool2() {
|
|
2707
|
+
if (_pool2 === null) {
|
|
2708
|
+
_pool2 = new BrowserPool({
|
|
2724
2709
|
size: { browsers: 1, pagesPerBrowser: 2 },
|
|
2725
2710
|
viewportWidth: 1280,
|
|
2726
2711
|
viewportHeight: 800
|
|
2727
2712
|
});
|
|
2728
|
-
await
|
|
2713
|
+
await _pool2.init();
|
|
2729
2714
|
}
|
|
2730
|
-
return
|
|
2715
|
+
return _pool2;
|
|
2731
2716
|
}
|
|
2732
|
-
async function
|
|
2733
|
-
if (
|
|
2734
|
-
await
|
|
2735
|
-
|
|
2717
|
+
async function shutdownPool2() {
|
|
2718
|
+
if (_pool2 !== null) {
|
|
2719
|
+
await _pool2.close();
|
|
2720
|
+
_pool2 = null;
|
|
2736
2721
|
}
|
|
2737
2722
|
}
|
|
2738
2723
|
async function analyzeRenders(options) {
|
|
@@ -2749,7 +2734,7 @@ Available: ${available}`
|
|
|
2749
2734
|
const rootDir = process.cwd();
|
|
2750
2735
|
const filePath = resolve(rootDir, descriptor.filePath);
|
|
2751
2736
|
const htmlHarness = await buildComponentHarness(filePath, options.componentName, {}, 1280);
|
|
2752
|
-
const pool = await
|
|
2737
|
+
const pool = await getPool2();
|
|
2753
2738
|
const slot = await pool.acquire();
|
|
2754
2739
|
const { page } = slot;
|
|
2755
2740
|
const startMs = performance.now();
|
|
@@ -2857,7 +2842,7 @@ function createInstrumentRendersCommand() {
|
|
|
2857
2842
|
interaction,
|
|
2858
2843
|
manifestPath: opts.manifest
|
|
2859
2844
|
});
|
|
2860
|
-
await
|
|
2845
|
+
await shutdownPool2();
|
|
2861
2846
|
if (opts.json || !isTTY()) {
|
|
2862
2847
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
2863
2848
|
`);
|
|
@@ -2866,7 +2851,7 @@ function createInstrumentRendersCommand() {
|
|
|
2866
2851
|
`);
|
|
2867
2852
|
}
|
|
2868
2853
|
} catch (err) {
|
|
2869
|
-
await
|
|
2854
|
+
await shutdownPool2();
|
|
2870
2855
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2871
2856
|
`);
|
|
2872
2857
|
process.exit(1);
|
|
@@ -2926,22 +2911,22 @@ function writeReportToFile(report, outputPath, pretty) {
|
|
|
2926
2911
|
}
|
|
2927
2912
|
var MANIFEST_PATH6 = ".reactscope/manifest.json";
|
|
2928
2913
|
var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
|
|
2929
|
-
var
|
|
2930
|
-
async function
|
|
2931
|
-
if (
|
|
2932
|
-
|
|
2914
|
+
var _pool3 = null;
|
|
2915
|
+
async function getPool3(viewportWidth, viewportHeight) {
|
|
2916
|
+
if (_pool3 === null) {
|
|
2917
|
+
_pool3 = new BrowserPool({
|
|
2933
2918
|
size: { browsers: 1, pagesPerBrowser: 4 },
|
|
2934
2919
|
viewportWidth,
|
|
2935
2920
|
viewportHeight
|
|
2936
2921
|
});
|
|
2937
|
-
await
|
|
2922
|
+
await _pool3.init();
|
|
2938
2923
|
}
|
|
2939
|
-
return
|
|
2924
|
+
return _pool3;
|
|
2940
2925
|
}
|
|
2941
|
-
async function
|
|
2942
|
-
if (
|
|
2943
|
-
await
|
|
2944
|
-
|
|
2926
|
+
async function shutdownPool3() {
|
|
2927
|
+
if (_pool3 !== null) {
|
|
2928
|
+
await _pool3.close();
|
|
2929
|
+
_pool3 = null;
|
|
2945
2930
|
}
|
|
2946
2931
|
}
|
|
2947
2932
|
function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
@@ -2952,7 +2937,7 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
|
|
|
2952
2937
|
_satori: satori,
|
|
2953
2938
|
async renderCell(props, _complexityClass) {
|
|
2954
2939
|
const startMs = performance.now();
|
|
2955
|
-
const pool = await
|
|
2940
|
+
const pool = await getPool3(viewportWidth, viewportHeight);
|
|
2956
2941
|
const htmlHarness = await buildComponentHarness(
|
|
2957
2942
|
filePath,
|
|
2958
2943
|
componentName,
|
|
@@ -3088,7 +3073,7 @@ Available: ${available}`
|
|
|
3088
3073
|
}
|
|
3089
3074
|
}
|
|
3090
3075
|
);
|
|
3091
|
-
await
|
|
3076
|
+
await shutdownPool3();
|
|
3092
3077
|
if (outcome.crashed) {
|
|
3093
3078
|
process.stderr.write(`\u2717 Render failed: ${outcome.error.message}
|
|
3094
3079
|
`);
|
|
@@ -3136,7 +3121,7 @@ Available: ${available}`
|
|
|
3136
3121
|
);
|
|
3137
3122
|
}
|
|
3138
3123
|
} catch (err) {
|
|
3139
|
-
await
|
|
3124
|
+
await shutdownPool3();
|
|
3140
3125
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
3141
3126
|
`);
|
|
3142
3127
|
process.exit(1);
|
|
@@ -3221,7 +3206,7 @@ Available: ${available}`
|
|
|
3221
3206
|
concurrency
|
|
3222
3207
|
});
|
|
3223
3208
|
const result = await matrix.render();
|
|
3224
|
-
await
|
|
3209
|
+
await shutdownPool3();
|
|
3225
3210
|
process.stderr.write(
|
|
3226
3211
|
`Done. ${result.stats.totalCells} cells, avg ${result.stats.avgRenderTimeMs.toFixed(1)}ms
|
|
3227
3212
|
`
|
|
@@ -3266,7 +3251,7 @@ Available: ${available}`
|
|
|
3266
3251
|
process.stdout.write(formatMatrixCsv(componentName, result));
|
|
3267
3252
|
}
|
|
3268
3253
|
} catch (err) {
|
|
3269
|
-
await
|
|
3254
|
+
await shutdownPool3();
|
|
3270
3255
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
3271
3256
|
`);
|
|
3272
3257
|
process.exit(1);
|
|
@@ -3363,13 +3348,13 @@ function registerRenderAll(renderCmd) {
|
|
|
3363
3348
|
workers.push(worker());
|
|
3364
3349
|
}
|
|
3365
3350
|
await Promise.all(workers);
|
|
3366
|
-
await
|
|
3351
|
+
await shutdownPool3();
|
|
3367
3352
|
process.stderr.write("\n");
|
|
3368
3353
|
const summary = formatSummaryText(results, outputDir);
|
|
3369
3354
|
process.stderr.write(`${summary}
|
|
3370
3355
|
`);
|
|
3371
3356
|
} catch (err) {
|
|
3372
|
-
await
|
|
3357
|
+
await shutdownPool3();
|
|
3373
3358
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
3374
3359
|
`);
|
|
3375
3360
|
process.exit(1);
|
|
@@ -3411,26 +3396,26 @@ function createRenderCommand() {
|
|
|
3411
3396
|
return renderCmd;
|
|
3412
3397
|
}
|
|
3413
3398
|
var DEFAULT_BASELINE_DIR = ".reactscope/baseline";
|
|
3414
|
-
var
|
|
3415
|
-
async function
|
|
3416
|
-
if (
|
|
3417
|
-
|
|
3399
|
+
var _pool4 = null;
|
|
3400
|
+
async function getPool4(viewportWidth, viewportHeight) {
|
|
3401
|
+
if (_pool4 === null) {
|
|
3402
|
+
_pool4 = new BrowserPool({
|
|
3418
3403
|
size: { browsers: 1, pagesPerBrowser: 4 },
|
|
3419
3404
|
viewportWidth,
|
|
3420
3405
|
viewportHeight
|
|
3421
3406
|
});
|
|
3422
|
-
await
|
|
3407
|
+
await _pool4.init();
|
|
3423
3408
|
}
|
|
3424
|
-
return
|
|
3409
|
+
return _pool4;
|
|
3425
3410
|
}
|
|
3426
|
-
async function
|
|
3427
|
-
if (
|
|
3428
|
-
await
|
|
3429
|
-
|
|
3411
|
+
async function shutdownPool4() {
|
|
3412
|
+
if (_pool4 !== null) {
|
|
3413
|
+
await _pool4.close();
|
|
3414
|
+
_pool4 = null;
|
|
3430
3415
|
}
|
|
3431
3416
|
}
|
|
3432
3417
|
async function renderComponent2(filePath, componentName, props, viewportWidth, viewportHeight) {
|
|
3433
|
-
const pool = await
|
|
3418
|
+
const pool = await getPool4(viewportWidth, viewportHeight);
|
|
3434
3419
|
const htmlHarness = await buildComponentHarness(filePath, componentName, props, viewportWidth);
|
|
3435
3420
|
const slot = await pool.acquire();
|
|
3436
3421
|
const { page } = slot;
|
|
@@ -3680,7 +3665,7 @@ async function runBaseline(options = {}) {
|
|
|
3680
3665
|
workers.push(worker());
|
|
3681
3666
|
}
|
|
3682
3667
|
await Promise.all(workers);
|
|
3683
|
-
await
|
|
3668
|
+
await shutdownPool4();
|
|
3684
3669
|
if (isTTY()) {
|
|
3685
3670
|
process.stderr.write("\n");
|
|
3686
3671
|
}
|
|
@@ -3741,26 +3726,26 @@ function loadBaselineRenderJson2(baselineDir, componentName) {
|
|
|
3741
3726
|
if (!existsSync(jsonPath)) return null;
|
|
3742
3727
|
return JSON.parse(readFileSync(jsonPath, "utf-8"));
|
|
3743
3728
|
}
|
|
3744
|
-
var
|
|
3745
|
-
async function
|
|
3746
|
-
if (
|
|
3747
|
-
|
|
3729
|
+
var _pool5 = null;
|
|
3730
|
+
async function getPool5(viewportWidth, viewportHeight) {
|
|
3731
|
+
if (_pool5 === null) {
|
|
3732
|
+
_pool5 = new BrowserPool({
|
|
3748
3733
|
size: { browsers: 1, pagesPerBrowser: 4 },
|
|
3749
3734
|
viewportWidth,
|
|
3750
3735
|
viewportHeight
|
|
3751
3736
|
});
|
|
3752
|
-
await
|
|
3737
|
+
await _pool5.init();
|
|
3753
3738
|
}
|
|
3754
|
-
return
|
|
3739
|
+
return _pool5;
|
|
3755
3740
|
}
|
|
3756
|
-
async function
|
|
3757
|
-
if (
|
|
3758
|
-
await
|
|
3759
|
-
|
|
3741
|
+
async function shutdownPool5() {
|
|
3742
|
+
if (_pool5 !== null) {
|
|
3743
|
+
await _pool5.close();
|
|
3744
|
+
_pool5 = null;
|
|
3760
3745
|
}
|
|
3761
3746
|
}
|
|
3762
3747
|
async function renderComponent3(filePath, componentName, props, viewportWidth, viewportHeight) {
|
|
3763
|
-
const pool = await
|
|
3748
|
+
const pool = await getPool5(viewportWidth, viewportHeight);
|
|
3764
3749
|
const htmlHarness = await buildComponentHarness(filePath, componentName, props, viewportWidth);
|
|
3765
3750
|
const slot = await pool.acquire();
|
|
3766
3751
|
const { page } = slot;
|
|
@@ -4009,7 +3994,7 @@ async function runDiff(options = {}) {
|
|
|
4009
3994
|
}
|
|
4010
3995
|
await Promise.all(workers);
|
|
4011
3996
|
}
|
|
4012
|
-
await
|
|
3997
|
+
await shutdownPool5();
|
|
4013
3998
|
if (isTTY() && total > 0) {
|
|
4014
3999
|
process.stderr.write("\n");
|
|
4015
4000
|
}
|
|
@@ -4609,6 +4594,130 @@ function buildStructuredReport(report) {
|
|
|
4609
4594
|
route: report.route?.pattern ?? null
|
|
4610
4595
|
};
|
|
4611
4596
|
}
|
|
4597
|
+
var MIME_TYPES = {
|
|
4598
|
+
".html": "text/html; charset=utf-8",
|
|
4599
|
+
".css": "text/css; charset=utf-8",
|
|
4600
|
+
".js": "application/javascript; charset=utf-8",
|
|
4601
|
+
".json": "application/json; charset=utf-8",
|
|
4602
|
+
".png": "image/png",
|
|
4603
|
+
".jpg": "image/jpeg",
|
|
4604
|
+
".jpeg": "image/jpeg",
|
|
4605
|
+
".svg": "image/svg+xml",
|
|
4606
|
+
".ico": "image/x-icon"
|
|
4607
|
+
};
|
|
4608
|
+
function registerBuild(siteCmd) {
|
|
4609
|
+
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(
|
|
4610
|
+
async (opts) => {
|
|
4611
|
+
try {
|
|
4612
|
+
const inputDir = resolve(process.cwd(), opts.input);
|
|
4613
|
+
const outputDir = resolve(process.cwd(), opts.output);
|
|
4614
|
+
if (!existsSync(inputDir)) {
|
|
4615
|
+
throw new Error(
|
|
4616
|
+
`Input directory not found: ${inputDir}
|
|
4617
|
+
Run \`scope manifest generate\` and \`scope render\` first.`
|
|
4618
|
+
);
|
|
4619
|
+
}
|
|
4620
|
+
const manifestPath = join(inputDir, "manifest.json");
|
|
4621
|
+
if (!existsSync(manifestPath)) {
|
|
4622
|
+
throw new Error(
|
|
4623
|
+
`Manifest not found at ${manifestPath}
|
|
4624
|
+
Run \`scope manifest generate\` first.`
|
|
4625
|
+
);
|
|
4626
|
+
}
|
|
4627
|
+
process.stderr.write(`Building site from ${inputDir}\u2026
|
|
4628
|
+
`);
|
|
4629
|
+
await buildSite({
|
|
4630
|
+
inputDir,
|
|
4631
|
+
outputDir,
|
|
4632
|
+
basePath: opts.basePath,
|
|
4633
|
+
...opts.compliance !== void 0 && {
|
|
4634
|
+
compliancePath: resolve(process.cwd(), opts.compliance)
|
|
4635
|
+
},
|
|
4636
|
+
title: opts.title
|
|
4637
|
+
});
|
|
4638
|
+
process.stderr.write(`Site written to ${outputDir}
|
|
4639
|
+
`);
|
|
4640
|
+
process.stdout.write(`${outputDir}
|
|
4641
|
+
`);
|
|
4642
|
+
} catch (err) {
|
|
4643
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
4644
|
+
`);
|
|
4645
|
+
process.exit(1);
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
);
|
|
4649
|
+
}
|
|
4650
|
+
function registerServe(siteCmd) {
|
|
4651
|
+
siteCmd.command("serve").description("Serve the built static site locally").option("-p, --port <number>", "Port to listen on", "3000").option("-d, --dir <path>", "Directory to serve", ".reactscope/site").action((opts) => {
|
|
4652
|
+
try {
|
|
4653
|
+
const port = Number.parseInt(opts.port, 10);
|
|
4654
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
4655
|
+
throw new Error(`Invalid port: ${opts.port}`);
|
|
4656
|
+
}
|
|
4657
|
+
const serveDir = resolve(process.cwd(), opts.dir);
|
|
4658
|
+
if (!existsSync(serveDir)) {
|
|
4659
|
+
throw new Error(
|
|
4660
|
+
`Serve directory not found: ${serveDir}
|
|
4661
|
+
Run \`scope site build\` first.`
|
|
4662
|
+
);
|
|
4663
|
+
}
|
|
4664
|
+
const server = createServer((req, res) => {
|
|
4665
|
+
const rawUrl = req.url ?? "/";
|
|
4666
|
+
const urlPath = decodeURIComponent(rawUrl.split("?")[0] ?? "/");
|
|
4667
|
+
const filePath = join(serveDir, urlPath.endsWith("/") ? `${urlPath}index.html` : urlPath);
|
|
4668
|
+
if (!filePath.startsWith(serveDir)) {
|
|
4669
|
+
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
4670
|
+
res.end("Forbidden");
|
|
4671
|
+
return;
|
|
4672
|
+
}
|
|
4673
|
+
if (existsSync(filePath) && statSync(filePath).isFile()) {
|
|
4674
|
+
const ext = extname(filePath).toLowerCase();
|
|
4675
|
+
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
4676
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
4677
|
+
createReadStream(filePath).pipe(res);
|
|
4678
|
+
return;
|
|
4679
|
+
}
|
|
4680
|
+
const htmlPath = `${filePath}.html`;
|
|
4681
|
+
if (existsSync(htmlPath) && statSync(htmlPath).isFile()) {
|
|
4682
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
4683
|
+
createReadStream(htmlPath).pipe(res);
|
|
4684
|
+
return;
|
|
4685
|
+
}
|
|
4686
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
4687
|
+
res.end(`Not found: ${urlPath}`);
|
|
4688
|
+
});
|
|
4689
|
+
server.listen(port, () => {
|
|
4690
|
+
process.stderr.write(`Scope site running at http://localhost:${port}
|
|
4691
|
+
`);
|
|
4692
|
+
process.stderr.write(`Serving ${serveDir}
|
|
4693
|
+
`);
|
|
4694
|
+
process.stderr.write("Press Ctrl+C to stop.\n");
|
|
4695
|
+
});
|
|
4696
|
+
server.on("error", (err) => {
|
|
4697
|
+
if (err.code === "EADDRINUSE") {
|
|
4698
|
+
process.stderr.write(`Error: Port ${port} is already in use.
|
|
4699
|
+
`);
|
|
4700
|
+
} else {
|
|
4701
|
+
process.stderr.write(`Server error: ${err.message}
|
|
4702
|
+
`);
|
|
4703
|
+
}
|
|
4704
|
+
process.exit(1);
|
|
4705
|
+
});
|
|
4706
|
+
} catch (err) {
|
|
4707
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
4708
|
+
`);
|
|
4709
|
+
process.exit(1);
|
|
4710
|
+
}
|
|
4711
|
+
});
|
|
4712
|
+
}
|
|
4713
|
+
function createSiteCommand() {
|
|
4714
|
+
const siteCmd = new Command("site").description(
|
|
4715
|
+
"Build and serve the static component gallery site"
|
|
4716
|
+
);
|
|
4717
|
+
registerBuild(siteCmd);
|
|
4718
|
+
registerServe(siteCmd);
|
|
4719
|
+
return siteCmd;
|
|
4720
|
+
}
|
|
4612
4721
|
var DEFAULT_STYLES_PATH = ".reactscope/compliance-styles.json";
|
|
4613
4722
|
function loadStylesFile(stylesPath) {
|
|
4614
4723
|
const absPath = resolve(process.cwd(), stylesPath);
|
|
@@ -5628,6 +5737,7 @@ function createProgram(options = {}) {
|
|
|
5628
5737
|
registerDiffSubCommand(existingReportCmd);
|
|
5629
5738
|
registerPrCommentSubCommand(existingReportCmd);
|
|
5630
5739
|
}
|
|
5740
|
+
program.addCommand(createSiteCommand());
|
|
5631
5741
|
return program;
|
|
5632
5742
|
}
|
|
5633
5743
|
|