foss-design 0.9.7 → 0.9.9
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 -136
- package/dist/viewer/assets/{index-Cg9mKtjz.js → index-6h0AwUgn.js} +49 -49
- package/dist/viewer/assets/{index-oN46u7re.css → index-DnxkAHbH.css} +1 -1
- package/dist/viewer/index.html +2 -2
- package/dist/viewer-lib/index.js +51 -2
- package/dist/viewer-lib/styles.css +11 -1
- package/dist/viewer-lib/types/viewer/src/ui/menu.d.ts +12 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
5
5
|
import fs37 from "fs";
|
|
6
|
-
import
|
|
6
|
+
import path36 from "path";
|
|
7
7
|
import { parseArgs } from "util";
|
|
8
8
|
|
|
9
9
|
// src/core/paths.ts
|
|
@@ -348,8 +348,8 @@ function routeError(pattern) {
|
|
|
348
348
|
}
|
|
349
349
|
return null;
|
|
350
350
|
}
|
|
351
|
-
function split(
|
|
352
|
-
return
|
|
351
|
+
function split(path37) {
|
|
352
|
+
return path37.split("/").filter(Boolean);
|
|
353
353
|
}
|
|
354
354
|
function compileRoute(pattern) {
|
|
355
355
|
return {
|
|
@@ -368,8 +368,8 @@ function decode2(part) {
|
|
|
368
368
|
return part;
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
|
-
function scoreRoute(route2,
|
|
372
|
-
const parts = split(
|
|
371
|
+
function scoreRoute(route2, path37) {
|
|
372
|
+
const parts = split(path37.split(/[?#]/)[0] ?? "").map(decode2);
|
|
373
373
|
const walk2 = (s, p) => {
|
|
374
374
|
if (s === route2.segments.length) return p === parts.length ? 0 : null;
|
|
375
375
|
const segment = route2.segments[s];
|
|
@@ -387,11 +387,11 @@ function scoreRoute(route2, path36) {
|
|
|
387
387
|
};
|
|
388
388
|
return walk2(0, 0);
|
|
389
389
|
}
|
|
390
|
-
function matchRoute(entries,
|
|
390
|
+
function matchRoute(entries, path37) {
|
|
391
391
|
let best = null;
|
|
392
392
|
for (const entry of entries) {
|
|
393
393
|
for (const route2 of entry.routes) {
|
|
394
|
-
const score = scoreRoute(route2,
|
|
394
|
+
const score = scoreRoute(route2, path37);
|
|
395
395
|
if (score !== null && (!best || score > best.score)) best = { score, value: entry.value };
|
|
396
396
|
}
|
|
397
397
|
}
|
|
@@ -518,9 +518,9 @@ var CanvasSchema = z.strictObject({
|
|
|
518
518
|
pages: z.array(z.unknown()).min(1, "add at least one page")
|
|
519
519
|
});
|
|
520
520
|
var ID_PATTERN = /^[a-z0-9][a-z0-9_-]*$/i;
|
|
521
|
-
function formatPath(base,
|
|
521
|
+
function formatPath(base, path37) {
|
|
522
522
|
let out = base;
|
|
523
|
-
for (const part of
|
|
523
|
+
for (const part of path37) out += typeof part === "number" ? `[${part}]` : out ? `.${String(part)}` : String(part);
|
|
524
524
|
return out;
|
|
525
525
|
}
|
|
526
526
|
function describeZodError(error, base = "") {
|
|
@@ -2150,10 +2150,23 @@ async function buildSite(paths, out, options = {}) {
|
|
|
2150
2150
|
inputs.push(file);
|
|
2151
2151
|
}
|
|
2152
2152
|
fs12.mkdirSync(out, { recursive: true });
|
|
2153
|
+
const moduleMap = {};
|
|
2153
2154
|
if (inputs.length) {
|
|
2154
2155
|
const base = baseConfig(project);
|
|
2155
2156
|
await build({
|
|
2156
2157
|
...base,
|
|
2158
|
+
plugins: [
|
|
2159
|
+
...base.plugins ?? [],
|
|
2160
|
+
options.moduleMap ? {
|
|
2161
|
+
name: "design-module-map",
|
|
2162
|
+
generateBundle(_, bundle) {
|
|
2163
|
+
for (const [file, chunk2] of Object.entries(bundle)) {
|
|
2164
|
+
if (chunk2.type !== "chunk") continue;
|
|
2165
|
+
moduleMap[file] = (chunk2.moduleIds ?? []).map((id2) => id2.split("?")[0]).filter((id2) => path12.isAbsolute(id2) && !id2.includes(`${path12.sep}node_modules${path12.sep}`)).map((id2) => toPosix(path12.relative(paths.root, id2))).filter((rel) => !rel.startsWith(".."));
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
} : null
|
|
2169
|
+
],
|
|
2157
2170
|
root: staging,
|
|
2158
2171
|
base: "./",
|
|
2159
2172
|
logLevel: "warn",
|
|
@@ -2173,6 +2186,7 @@ async function buildSite(paths, out, options = {}) {
|
|
|
2173
2186
|
fs12.rmSync(out, { recursive: true, force: true });
|
|
2174
2187
|
fs12.mkdirSync(out, { recursive: true });
|
|
2175
2188
|
}
|
|
2189
|
+
if (options.moduleMap) fs12.writeFileSync(path12.join(out, "modules.json"), JSON.stringify(moduleMap));
|
|
2176
2190
|
for (const { source, dir } of htmlScreens) {
|
|
2177
2191
|
copyTree(
|
|
2178
2192
|
path12.dirname(source.file),
|
|
@@ -2634,7 +2648,7 @@ var CloudClient = class {
|
|
|
2634
2648
|
}
|
|
2635
2649
|
host;
|
|
2636
2650
|
token;
|
|
2637
|
-
async send(method,
|
|
2651
|
+
async send(method, path37, init) {
|
|
2638
2652
|
const headers = {
|
|
2639
2653
|
"User-Agent": `foss-design/${packageVersion()} (${os.platform()})`,
|
|
2640
2654
|
Accept: "application/json"
|
|
@@ -2642,7 +2656,7 @@ var CloudClient = class {
|
|
|
2642
2656
|
if (this.token) headers.Authorization = `Bearer ${this.token}`;
|
|
2643
2657
|
if (init.type) headers["Content-Type"] = init.type;
|
|
2644
2658
|
try {
|
|
2645
|
-
return await fetch(`${this.host}/api${
|
|
2659
|
+
return await fetch(`${this.host}/api${path37}`, {
|
|
2646
2660
|
method,
|
|
2647
2661
|
headers,
|
|
2648
2662
|
body: init.body,
|
|
@@ -2663,8 +2677,8 @@ var CloudClient = class {
|
|
|
2663
2677
|
throw errorFor(this.host, response.status, body, response.headers.get("retry-after"));
|
|
2664
2678
|
}
|
|
2665
2679
|
/** JSON in, JSON out; any non-2xx answer throws a CloudError. */
|
|
2666
|
-
async request(method,
|
|
2667
|
-
const response = await this.send(method,
|
|
2680
|
+
async request(method, path37, body) {
|
|
2681
|
+
const response = await this.send(method, path37, {
|
|
2668
2682
|
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
2669
2683
|
type: body === void 0 ? void 0 : "application/json",
|
|
2670
2684
|
timeout: JSON_TIMEOUT
|
|
@@ -2676,7 +2690,7 @@ var CloudClient = class {
|
|
|
2676
2690
|
try {
|
|
2677
2691
|
return JSON.parse(text2);
|
|
2678
2692
|
} catch {
|
|
2679
|
-
throw new CloudError(`${this.host} answered ${method} ${
|
|
2693
|
+
throw new CloudError(`${this.host} answered ${method} ${path37} with something other than JSON`, 0, "bad_response");
|
|
2680
2694
|
}
|
|
2681
2695
|
}
|
|
2682
2696
|
/** POST /device/code. */
|
|
@@ -3031,10 +3045,32 @@ var plural = (count, word, many = `${word}s`) => `${count} ${count === 1 ? word
|
|
|
3031
3045
|
// src/capture/cover.ts
|
|
3032
3046
|
import fs20 from "fs";
|
|
3033
3047
|
import os3 from "os";
|
|
3048
|
+
import path19 from "path";
|
|
3034
3049
|
|
|
3035
3050
|
// src/server/snapshots.ts
|
|
3051
|
+
import { createHash as createHash5 } from "crypto";
|
|
3036
3052
|
import fs17 from "fs";
|
|
3037
3053
|
import path17 from "path";
|
|
3054
|
+
function depsHash(root, rels, cache = /* @__PURE__ */ new Map()) {
|
|
3055
|
+
const hash = createHash5("sha1");
|
|
3056
|
+
for (const rel of [...rels].sort()) {
|
|
3057
|
+
let one = cache.get(rel);
|
|
3058
|
+
if (one === void 0) {
|
|
3059
|
+
one = sourceHash(path17.join(root, rel)) ?? "missing";
|
|
3060
|
+
cache.set(rel, one);
|
|
3061
|
+
}
|
|
3062
|
+
hash.update(`${rel}\0${one}
|
|
3063
|
+
`);
|
|
3064
|
+
}
|
|
3065
|
+
return hash.digest("hex").slice(0, 16);
|
|
3066
|
+
}
|
|
3067
|
+
function sourceHash(file) {
|
|
3068
|
+
try {
|
|
3069
|
+
return createHash5("sha1").update(fs17.readFileSync(file)).digest("hex").slice(0, 16);
|
|
3070
|
+
} catch {
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3038
3074
|
var SAFE = /^@?[a-z0-9][a-z0-9_-]*$/i;
|
|
3039
3075
|
function isSafeSegment(value) {
|
|
3040
3076
|
return SAFE.test(value);
|
|
@@ -3050,6 +3086,12 @@ var SnapshotStore = class {
|
|
|
3050
3086
|
metaFile(canvas) {
|
|
3051
3087
|
return path17.join(this.dir, canvas, "meta.json");
|
|
3052
3088
|
}
|
|
3089
|
+
/** Written aside and renamed: a run stopped mid-write leaves the old file, not half of one. */
|
|
3090
|
+
writeMeta(canvas, data) {
|
|
3091
|
+
const temp = `${this.metaFile(canvas)}.${process.pid}.tmp`;
|
|
3092
|
+
fs17.writeFileSync(temp, JSON.stringify(data));
|
|
3093
|
+
fs17.renameSync(temp, this.metaFile(canvas));
|
|
3094
|
+
}
|
|
3053
3095
|
load(canvas) {
|
|
3054
3096
|
const cached = this.meta.get(canvas);
|
|
3055
3097
|
if (cached) return cached;
|
|
@@ -3084,7 +3126,26 @@ var SnapshotStore = class {
|
|
|
3084
3126
|
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) return null;
|
|
3085
3127
|
return this.load(canvas)[id2]?.[theme2] ?? null;
|
|
3086
3128
|
}
|
|
3087
|
-
|
|
3129
|
+
/** The source hash recorded with the snapshot; undefined for snapshots taken before hashes were kept. */
|
|
3130
|
+
source(canvas, id2, theme2) {
|
|
3131
|
+
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) return void 0;
|
|
3132
|
+
return this.load(canvas)[id2]?.sources?.[theme2];
|
|
3133
|
+
}
|
|
3134
|
+
/** The files the snapshot's frame loaded, when a build told (see `Meta.deps`). */
|
|
3135
|
+
deps(canvas, id2) {
|
|
3136
|
+
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) return void 0;
|
|
3137
|
+
return this.load(canvas)[id2]?.deps;
|
|
3138
|
+
}
|
|
3139
|
+
/** Record `hash` for a snapshot taken before hashes were kept: from now on it counts as that content's. */
|
|
3140
|
+
adopt(canvas, id2, theme2, hash) {
|
|
3141
|
+
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) return;
|
|
3142
|
+
const data = this.load(canvas);
|
|
3143
|
+
const entry = data[id2];
|
|
3144
|
+
if (!entry?.[theme2]) return;
|
|
3145
|
+
entry.sources = { ...entry.sources, [theme2]: hash };
|
|
3146
|
+
this.writeMeta(canvas, data);
|
|
3147
|
+
}
|
|
3148
|
+
save(canvas, id2, theme2, png, height, source, deps) {
|
|
3088
3149
|
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) throw new Error("bad snapshot key");
|
|
3089
3150
|
const dir = path17.join(this.dir, canvas);
|
|
3090
3151
|
fs17.mkdirSync(dir, { recursive: true });
|
|
@@ -3093,10 +3154,12 @@ var SnapshotStore = class {
|
|
|
3093
3154
|
fs17.renameSync(`${file}.${process.pid}.tmp`, file);
|
|
3094
3155
|
const data = this.load(canvas);
|
|
3095
3156
|
const version = Date.now();
|
|
3096
|
-
|
|
3097
|
-
const
|
|
3098
|
-
|
|
3099
|
-
|
|
3157
|
+
const previous = data[id2];
|
|
3158
|
+
const sources = { ...previous?.sources };
|
|
3159
|
+
if (source) sources[theme2] = source;
|
|
3160
|
+
else delete sources[theme2];
|
|
3161
|
+
data[id2] = { ...previous, [theme2]: version, ...height ? { height } : {}, sources, ...deps ? { deps } : {} };
|
|
3162
|
+
this.writeMeta(canvas, data);
|
|
3100
3163
|
return { url: this.url(canvas, id2, theme2, version) };
|
|
3101
3164
|
}
|
|
3102
3165
|
/** Absolute path of `<id>.<theme>.png`, or of its thumbnail for `<id>.<theme>.thumb.png` (made on first use); null when missing. */
|
|
@@ -3120,7 +3183,8 @@ async function serveBuild(paths, name, options) {
|
|
|
3120
3183
|
await buildSite(paths, out, {
|
|
3121
3184
|
canvases: options.canvases,
|
|
3122
3185
|
includeSystem: options.includeSystem,
|
|
3123
|
-
includeViewer: false
|
|
3186
|
+
includeViewer: false,
|
|
3187
|
+
moduleMap: options.moduleMap
|
|
3124
3188
|
});
|
|
3125
3189
|
const files = sirv(out, { dev: true });
|
|
3126
3190
|
const server = http.createServer((req, res) => {
|
|
@@ -3341,6 +3405,7 @@ function staleSnapshots(paths, canvasIds, filter = {}) {
|
|
|
3341
3405
|
const store = new SnapshotStore(paths.snapshots);
|
|
3342
3406
|
const project = new DesignProject(paths, DEV_URLS, store.lookup);
|
|
3343
3407
|
const stale = [];
|
|
3408
|
+
const hashes2 = /* @__PURE__ */ new Map();
|
|
3344
3409
|
for (const id2 of canvasIds) {
|
|
3345
3410
|
const canvas = project.canvas(id2);
|
|
3346
3411
|
if (!canvas) continue;
|
|
@@ -3353,13 +3418,19 @@ function staleSnapshots(paths, canvasIds, filter = {}) {
|
|
|
3353
3418
|
if (filter.screens && !filter.screens.has(job.id)) continue;
|
|
3354
3419
|
const file = files.get(job.id);
|
|
3355
3420
|
if (!file || !fs20.existsSync(file)) continue;
|
|
3356
|
-
const
|
|
3421
|
+
const deps = store.deps(id2, job.id);
|
|
3422
|
+
const hash = deps ? depsHash(paths.root, deps, hashes2) : sourceHash(file);
|
|
3357
3423
|
const pinned = inDark.get(job.id) === job.theme;
|
|
3358
3424
|
const themes = pinned ? [job.theme] : THEMES2;
|
|
3359
|
-
const screen = { id: job.id };
|
|
3360
3425
|
for (const theme2 of themes) {
|
|
3361
|
-
|
|
3362
|
-
|
|
3426
|
+
if (!store.version(id2, job.id, theme2)) {
|
|
3427
|
+
stale.push({ canvas: id2, screen: job.id, theme: theme2, source: hash });
|
|
3428
|
+
continue;
|
|
3429
|
+
}
|
|
3430
|
+
const recorded = store.source(id2, job.id, theme2);
|
|
3431
|
+
if (recorded === void 0) {
|
|
3432
|
+
if (hash) store.adopt(id2, job.id, theme2, hash);
|
|
3433
|
+
} else if (recorded !== hash) stale.push({ canvas: id2, screen: job.id, theme: theme2, source: hash });
|
|
3363
3434
|
}
|
|
3364
3435
|
}
|
|
3365
3436
|
}
|
|
@@ -3376,9 +3447,12 @@ async function takeSnapshots(paths, targets, preview, progress2) {
|
|
|
3376
3447
|
let docOf = (id2) => project.canvas(id2).doc;
|
|
3377
3448
|
let stop = async () => {
|
|
3378
3449
|
};
|
|
3450
|
+
let modules = null;
|
|
3451
|
+
const hashes2 = /* @__PURE__ */ new Map();
|
|
3379
3452
|
try {
|
|
3380
|
-
const build2 = await serveBuild(paths, "snapshot-build", { canvases, includeSystem: false });
|
|
3453
|
+
const build2 = await serveBuild(paths, "snapshot-build", { canvases, includeSystem: false, moduleMap: true });
|
|
3381
3454
|
url = build2.base;
|
|
3455
|
+
modules = build2.read("modules.json");
|
|
3382
3456
|
docOf = (id2) => build2.read(`api/canvas/${id2}.json`) ?? project.canvas(id2).doc;
|
|
3383
3457
|
stop = build2.close;
|
|
3384
3458
|
} catch {
|
|
@@ -3391,7 +3465,7 @@ async function takeSnapshots(paths, targets, preview, progress2) {
|
|
|
3391
3465
|
const line = () => `Snapshots ${done}/${targets.length} (both themes, as the viewer and the cloud show them)`;
|
|
3392
3466
|
progress2.update(line());
|
|
3393
3467
|
try {
|
|
3394
|
-
await mapLimit([...targets], TABS, async ({ canvas, screen, theme: theme2 }) => {
|
|
3468
|
+
await mapLimit([...targets], TABS, async ({ canvas, screen, theme: theme2, source }) => {
|
|
3395
3469
|
const job = canvasFrames(docOf(canvas), url, theme2, { id: screen })[0];
|
|
3396
3470
|
if (job) {
|
|
3397
3471
|
const page = await browser.newPage({
|
|
@@ -3399,12 +3473,25 @@ async function takeSnapshots(paths, targets, preview, progress2) {
|
|
|
3399
3473
|
deviceScaleFactor: Math.min(1, SNAPSHOT_WIDTH / job.width),
|
|
3400
3474
|
colorScheme: theme2
|
|
3401
3475
|
});
|
|
3476
|
+
const loaded = /* @__PURE__ */ new Set();
|
|
3477
|
+
page.on("request", (request) => {
|
|
3478
|
+
const at = request.url();
|
|
3479
|
+
if (at.startsWith(url)) loaded.add(decodeURIComponent(new URL(at).pathname.slice(1)));
|
|
3480
|
+
});
|
|
3402
3481
|
try {
|
|
3403
3482
|
const report = await openFrame(page, job.url, { waitForReady: job.waitForReady });
|
|
3404
3483
|
if (report.ready) {
|
|
3405
3484
|
const png = await page.screenshot({ fullPage: job.fullPage });
|
|
3406
3485
|
const height = job.fullPage ? await page.evaluate(() => document.documentElement.scrollHeight) : void 0;
|
|
3407
|
-
|
|
3486
|
+
const file = project.source(canvas, screen)?.file;
|
|
3487
|
+
const deps = modules ? [
|
|
3488
|
+
.../* @__PURE__ */ new Set([
|
|
3489
|
+
...[...loaded].flatMap((rel) => modules[rel] ?? []),
|
|
3490
|
+
...file ? [toPosix(path19.relative(paths.root, file))] : []
|
|
3491
|
+
])
|
|
3492
|
+
].sort() : [];
|
|
3493
|
+
const hash = deps.length ? depsHash(paths.root, deps, hashes2) : source;
|
|
3494
|
+
store.save(canvas, screen, theme2, png, height, hash, deps.length ? deps : void 0);
|
|
3408
3495
|
} else failed.push(`${canvas}/${screen} (${theme2})`);
|
|
3409
3496
|
} catch {
|
|
3410
3497
|
failed.push(`${canvas}/${screen} (${theme2})`);
|
|
@@ -3431,21 +3518,21 @@ async function takeSnapshots(paths, targets, preview, progress2) {
|
|
|
3431
3518
|
|
|
3432
3519
|
// src/cloud/pull.ts
|
|
3433
3520
|
import fs24 from "fs";
|
|
3434
|
-
import
|
|
3521
|
+
import path23 from "path";
|
|
3435
3522
|
|
|
3436
3523
|
// src/cloud/merge.ts
|
|
3437
3524
|
import fs22 from "fs";
|
|
3438
|
-
import
|
|
3525
|
+
import path21 from "path";
|
|
3439
3526
|
import { merge as diff3 } from "node-diff3";
|
|
3440
3527
|
|
|
3441
3528
|
// src/cloud/write.ts
|
|
3442
3529
|
import fs21 from "fs";
|
|
3443
|
-
import
|
|
3530
|
+
import path20 from "path";
|
|
3444
3531
|
function writeInside(root, rel, data) {
|
|
3445
|
-
const file =
|
|
3532
|
+
const file = path20.join(root, rel);
|
|
3446
3533
|
if (!isInside(root, file)) throw new CliError(`Refusing to write outside ${root}: ${rel}`);
|
|
3447
|
-
fs21.mkdirSync(
|
|
3448
|
-
if (!isInside(fs21.realpathSync(root), fs21.realpathSync(
|
|
3534
|
+
fs21.mkdirSync(path20.dirname(file), { recursive: true });
|
|
3535
|
+
if (!isInside(fs21.realpathSync(root), fs21.realpathSync(path20.dirname(file))))
|
|
3449
3536
|
throw new CliError(`Refusing to write through a link out of ${root}: ${rel}`);
|
|
3450
3537
|
if (fs21.existsSync(file) && fs21.lstatSync(file).isSymbolicLink()) fs21.unlinkSync(file);
|
|
3451
3538
|
fs21.writeFileSync(file, data);
|
|
@@ -3459,16 +3546,16 @@ function pruneEmpty(dir, stop) {
|
|
|
3459
3546
|
} catch {
|
|
3460
3547
|
return;
|
|
3461
3548
|
}
|
|
3462
|
-
current =
|
|
3549
|
+
current = path20.dirname(current);
|
|
3463
3550
|
}
|
|
3464
3551
|
}
|
|
3465
3552
|
|
|
3466
3553
|
// src/cloud/merge.ts
|
|
3467
3554
|
var TEXT = /\.(?:[cm]?[jt]sx?|json|css|scss|html?|mdx?|svg|txt|ya?ml|csv|xml|toml)$/i;
|
|
3468
3555
|
var isText = (rel, ...data) => TEXT.test(rel) && data.every((buffer) => !buffer || !buffer.subarray(0, 8192).includes(0));
|
|
3469
|
-
var mergeDir = (paths, key) =>
|
|
3470
|
-
var stateFile = (paths, key) =>
|
|
3471
|
-
var sideFile = (paths, key, rel, side) =>
|
|
3556
|
+
var mergeDir = (paths, key) => path21.join(cloudCache(paths), "merge", ...key.split("/"));
|
|
3557
|
+
var stateFile = (paths, key) => path21.join(mergeDir(paths, key), "state.json");
|
|
3558
|
+
var sideFile = (paths, key, rel, side) => path21.join(mergeDir(paths, key), side, rel);
|
|
3472
3559
|
function readMergeState(paths, key) {
|
|
3473
3560
|
try {
|
|
3474
3561
|
return JSON.parse(fs22.readFileSync(stateFile(paths, key), "utf8"));
|
|
@@ -3482,10 +3569,10 @@ function saveState(paths, state) {
|
|
|
3482
3569
|
`);
|
|
3483
3570
|
}
|
|
3484
3571
|
function mergingUnits(paths) {
|
|
3485
|
-
const root =
|
|
3572
|
+
const root = path21.join(cloudCache(paths), "merge");
|
|
3486
3573
|
const found = [];
|
|
3487
3574
|
const visit = (dir, prefix) => {
|
|
3488
|
-
if (fs22.existsSync(
|
|
3575
|
+
if (fs22.existsSync(path21.join(dir, "state.json"))) {
|
|
3489
3576
|
const state = readMergeState(paths, prefix);
|
|
3490
3577
|
if (state) found.push(state);
|
|
3491
3578
|
return;
|
|
@@ -3496,7 +3583,7 @@ function mergingUnits(paths) {
|
|
|
3496
3583
|
} catch {
|
|
3497
3584
|
}
|
|
3498
3585
|
for (const entry of entries)
|
|
3499
|
-
if (entry.isDirectory()) visit(
|
|
3586
|
+
if (entry.isDirectory()) visit(path21.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
3500
3587
|
};
|
|
3501
3588
|
visit(root, "");
|
|
3502
3589
|
return found;
|
|
@@ -3506,7 +3593,7 @@ function openConflicts(paths, state) {
|
|
|
3506
3593
|
return state.conflicts.filter((conflict) => {
|
|
3507
3594
|
if (conflict.kind !== "text") return true;
|
|
3508
3595
|
try {
|
|
3509
|
-
return MARKER.test(fs22.readFileSync(
|
|
3596
|
+
return MARKER.test(fs22.readFileSync(path21.join(paths.design, conflict.path), "utf8"));
|
|
3510
3597
|
} catch {
|
|
3511
3598
|
return false;
|
|
3512
3599
|
}
|
|
@@ -3515,19 +3602,19 @@ function openConflicts(paths, state) {
|
|
|
3515
3602
|
function keep(paths, key, rel, side, data) {
|
|
3516
3603
|
if (!data) return;
|
|
3517
3604
|
const file = sideFile(paths, key, rel, side);
|
|
3518
|
-
fs22.mkdirSync(
|
|
3605
|
+
fs22.mkdirSync(path21.dirname(file), { recursive: true });
|
|
3519
3606
|
fs22.writeFileSync(file, data);
|
|
3520
3607
|
}
|
|
3521
3608
|
function mergeUnit(paths, key, headRev, base, here, cloud2, blob) {
|
|
3522
3609
|
const files = { added: 0, changed: 0, removed: 0, merged: 0 };
|
|
3523
3610
|
const conflicts = [];
|
|
3524
|
-
const stop = key === SYSTEM_UNIT ? paths.design :
|
|
3611
|
+
const stop = key === SYSTEM_UNIT ? paths.design : path21.dirname(unitRoot(paths, key));
|
|
3525
3612
|
fs22.rmSync(mergeDir(paths, key), { recursive: true, force: true });
|
|
3526
|
-
const read = (rel) => fs22.readFileSync(
|
|
3613
|
+
const read = (rel) => fs22.readFileSync(path21.join(paths.design, rel));
|
|
3527
3614
|
const remove = (rel) => {
|
|
3528
|
-
const file =
|
|
3615
|
+
const file = path21.join(paths.design, rel);
|
|
3529
3616
|
fs22.rmSync(file, { force: true });
|
|
3530
|
-
pruneEmpty(
|
|
3617
|
+
pruneEmpty(path21.dirname(file), stop);
|
|
3531
3618
|
};
|
|
3532
3619
|
for (const rel of [.../* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(here), ...Object.keys(cloud2)])].sort()) {
|
|
3533
3620
|
const b = base[rel];
|
|
@@ -3583,11 +3670,11 @@ function resolveConflict(paths, state, rel, side) {
|
|
|
3583
3670
|
const conflict = state.conflicts.find((item) => item.path === rel);
|
|
3584
3671
|
if (!conflict) return;
|
|
3585
3672
|
const kept = sideFile(paths, state.unit, rel, side);
|
|
3586
|
-
const stop = state.unit === SYSTEM_UNIT ? paths.design :
|
|
3673
|
+
const stop = state.unit === SYSTEM_UNIT ? paths.design : path21.dirname(unitRoot(paths, state.unit));
|
|
3587
3674
|
if (fs22.existsSync(kept)) writeInside(paths.design, rel, fs22.readFileSync(kept));
|
|
3588
3675
|
else {
|
|
3589
|
-
fs22.rmSync(
|
|
3590
|
-
pruneEmpty(
|
|
3676
|
+
fs22.rmSync(path21.join(paths.design, rel), { force: true });
|
|
3677
|
+
pruneEmpty(path21.dirname(path21.join(paths.design, rel)), stop);
|
|
3591
3678
|
}
|
|
3592
3679
|
state.conflicts = state.conflicts.filter((item) => item.path !== rel);
|
|
3593
3680
|
saveState(paths, state);
|
|
@@ -3605,7 +3692,7 @@ function finishMerge(paths, link, key, accept = false) {
|
|
|
3605
3692
|
|
|
3606
3693
|
// src/cloud/meta.ts
|
|
3607
3694
|
import fs23 from "fs";
|
|
3608
|
-
import
|
|
3695
|
+
import path22 from "path";
|
|
3609
3696
|
async function syncProjectMeta(paths, client, link) {
|
|
3610
3697
|
const remote = await client.project(link.project);
|
|
3611
3698
|
if (!remote) return [];
|
|
@@ -3624,7 +3711,7 @@ async function syncProjectMeta(paths, client, link) {
|
|
|
3624
3711
|
if (localChanged && !cloudChanged) {
|
|
3625
3712
|
try {
|
|
3626
3713
|
if (file) {
|
|
3627
|
-
const ext =
|
|
3714
|
+
const ext = path22.extname(file).slice(1);
|
|
3628
3715
|
await client.setProjectIcon(link.project, { data: fs23.readFileSync(file), type: ICON_TYPES[ext] });
|
|
3629
3716
|
lines.push("The project icon went up to the cloud.");
|
|
3630
3717
|
} else {
|
|
@@ -3660,12 +3747,12 @@ function checkManifest(key, manifest) {
|
|
|
3660
3747
|
}
|
|
3661
3748
|
function applyUnit(paths, key, local, target, blobs) {
|
|
3662
3749
|
const counts = { added: 0, changed: 0, removed: 0 };
|
|
3663
|
-
const stop = key === SYSTEM_UNIT ? paths.design :
|
|
3750
|
+
const stop = key === SYSTEM_UNIT ? paths.design : path23.dirname(unitRoot(paths, key));
|
|
3664
3751
|
for (const rel of Object.keys(local)) {
|
|
3665
3752
|
if (rel in target) continue;
|
|
3666
|
-
const file =
|
|
3753
|
+
const file = path23.join(paths.design, rel);
|
|
3667
3754
|
fs24.rmSync(file, { force: true });
|
|
3668
|
-
pruneEmpty(
|
|
3755
|
+
pruneEmpty(path23.dirname(file), stop);
|
|
3669
3756
|
counts.removed++;
|
|
3670
3757
|
}
|
|
3671
3758
|
for (const [rel, entry] of Object.entries(target)) {
|
|
@@ -3764,7 +3851,7 @@ async function pull(ctx, options) {
|
|
|
3764
3851
|
}
|
|
3765
3852
|
const byHash = /* @__PURE__ */ new Map();
|
|
3766
3853
|
for (const unit of scan.units.values())
|
|
3767
|
-
for (const [rel, entry] of Object.entries(unit.manifest)) byHash.set(entry.hash,
|
|
3854
|
+
for (const [rel, entry] of Object.entries(unit.manifest)) byHash.set(entry.hash, path23.join(paths.design, rel));
|
|
3768
3855
|
const needed = /* @__PURE__ */ new Set();
|
|
3769
3856
|
const bases = /* @__PURE__ */ new Set();
|
|
3770
3857
|
for (const plan of plans) {
|
|
@@ -3863,7 +3950,7 @@ async function pull(ctx, options) {
|
|
|
3863
3950
|
|
|
3864
3951
|
// src/cloud/push.ts
|
|
3865
3952
|
import fs25 from "fs";
|
|
3866
|
-
import
|
|
3953
|
+
import path24 from "path";
|
|
3867
3954
|
function blockedReport(status, message) {
|
|
3868
3955
|
return {
|
|
3869
3956
|
unit: status.key,
|
|
@@ -3979,7 +4066,7 @@ async function push(ctx, options) {
|
|
|
3979
4066
|
for (const key of toPush) {
|
|
3980
4067
|
const s = status(key);
|
|
3981
4068
|
if (s.conflictRev !== void 0) {
|
|
3982
|
-
const dir =
|
|
4069
|
+
const dir = path24.relative(paths.root, incomingDir(paths, key));
|
|
3983
4070
|
blocked.push(
|
|
3984
4071
|
blockedReport(
|
|
3985
4072
|
s,
|
|
@@ -4028,7 +4115,7 @@ async function push(ctx, options) {
|
|
|
4028
4115
|
const files = /* @__PURE__ */ new Map();
|
|
4029
4116
|
const addFiles = (manifest, dir) => {
|
|
4030
4117
|
for (const [rel, entry] of Object.entries(manifest))
|
|
4031
|
-
if (!files.has(entry.hash)) files.set(entry.hash,
|
|
4118
|
+
if (!files.has(entry.hash)) files.set(entry.hash, path24.join(dir, rel));
|
|
4032
4119
|
};
|
|
4033
4120
|
for (const key of pushing) {
|
|
4034
4121
|
const id2 = unitCanvasId(key);
|
|
@@ -4074,7 +4161,7 @@ async function push(ctx, options) {
|
|
|
4074
4161
|
await client.putBlob(link.project, hash, data);
|
|
4075
4162
|
} catch (error) {
|
|
4076
4163
|
if (error instanceof CloudError && error.status === 413)
|
|
4077
|
-
throw new CliError(`${
|
|
4164
|
+
throw new CliError(`${path24.relative(process.cwd(), file)} (${megabytes(data.length)}): ${error.message}`);
|
|
4078
4165
|
throw error;
|
|
4079
4166
|
}
|
|
4080
4167
|
sentFiles++;
|
|
@@ -4118,7 +4205,7 @@ function refuseLegacyGo(paths, keys) {
|
|
|
4118
4205
|
for (const key of keys) {
|
|
4119
4206
|
if (!key.startsWith("canvas/")) continue;
|
|
4120
4207
|
const id2 = key.slice("canvas/".length);
|
|
4121
|
-
for (const file of canvasCodeFiles(
|
|
4208
|
+
for (const file of canvasCodeFiles(path24.join(paths.canvases, id2), project.canvas(id2)?.screens ?? [])) {
|
|
4122
4209
|
for (const line of legacyGoLines(file)) hits.add(`${relToRoot(paths, file)}:${line}`);
|
|
4123
4210
|
}
|
|
4124
4211
|
}
|
|
@@ -4139,13 +4226,13 @@ import fs29 from "fs";
|
|
|
4139
4226
|
// src/server/index.ts
|
|
4140
4227
|
import fs28 from "fs";
|
|
4141
4228
|
import http2 from "http";
|
|
4142
|
-
import
|
|
4229
|
+
import path27 from "path";
|
|
4143
4230
|
import sirv2 from "sirv";
|
|
4144
4231
|
import { createServer, searchForWorkspaceRoot } from "vite";
|
|
4145
4232
|
|
|
4146
4233
|
// src/server/api.ts
|
|
4147
4234
|
import fs26 from "fs";
|
|
4148
|
-
import
|
|
4235
|
+
import path25 from "path";
|
|
4149
4236
|
function sendJson(res, status, body) {
|
|
4150
4237
|
res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
4151
4238
|
res.end(JSON.stringify(body));
|
|
@@ -4196,7 +4283,7 @@ async function handleApi(ctx, req, res, url) {
|
|
|
4196
4283
|
}
|
|
4197
4284
|
if (route2 === "/source") {
|
|
4198
4285
|
const rel = url.searchParams.get("path") ?? "";
|
|
4199
|
-
const file =
|
|
4286
|
+
const file = path25.resolve(project.paths.root, rel);
|
|
4200
4287
|
if (!rel || !isInside(project.paths.root, file) || !fs26.existsSync(file) || fs26.statSync(file).isDirectory()) {
|
|
4201
4288
|
sendJson(res, 404, { error: "not found" });
|
|
4202
4289
|
return true;
|
|
@@ -4236,7 +4323,10 @@ async function handleApi(ctx, req, res, url) {
|
|
|
4236
4323
|
try {
|
|
4237
4324
|
const body = await readBody(req);
|
|
4238
4325
|
if (body.subarray(0, 8).toString("hex") !== "89504e470d0a1a0a") throw new Error("not a PNG");
|
|
4239
|
-
const
|
|
4326
|
+
const deps = snapshots.deps(canvas, id2);
|
|
4327
|
+
const file = project.source(canvas, id2)?.file;
|
|
4328
|
+
const source = deps ? depsHash(project.paths.root, deps) : file ? sourceHash(file) : null;
|
|
4329
|
+
const { url: snapshotUrl } = snapshots.save(canvas, id2, theme2, body, height, source);
|
|
4240
4330
|
project.invalidateCanvas(canvas);
|
|
4241
4331
|
events.send({ type: "snapshot", canvas, id: id2, theme: theme2, url: snapshotUrl, height });
|
|
4242
4332
|
sendJson(res, 200, { url: snapshotUrl });
|
|
@@ -4295,9 +4385,9 @@ var EventHub = class {
|
|
|
4295
4385
|
// src/server/state.ts
|
|
4296
4386
|
import fs27 from "fs";
|
|
4297
4387
|
import os4 from "os";
|
|
4298
|
-
import
|
|
4299
|
-
var stateFile2 = (paths) =>
|
|
4300
|
-
var logFile = (paths) =>
|
|
4388
|
+
import path26 from "path";
|
|
4389
|
+
var stateFile2 = (paths) => path26.join(paths.cache, "server.json");
|
|
4390
|
+
var logFile = (paths) => path26.join(paths.cache, "server.log");
|
|
4301
4391
|
function readState(paths) {
|
|
4302
4392
|
try {
|
|
4303
4393
|
return JSON.parse(fs27.readFileSync(stateFile2(paths), "utf8"));
|
|
@@ -4306,10 +4396,10 @@ function readState(paths) {
|
|
|
4306
4396
|
}
|
|
4307
4397
|
}
|
|
4308
4398
|
function registryDir() {
|
|
4309
|
-
const base = process.platform === "win32" ? process.env.LOCALAPPDATA || process.env.APPDATA ||
|
|
4310
|
-
return
|
|
4399
|
+
const base = process.platform === "win32" ? process.env.LOCALAPPDATA || process.env.APPDATA || path26.join(os4.homedir(), "AppData", "Local") : process.env.XDG_CACHE_HOME || path26.join(os4.homedir(), ".cache");
|
|
4400
|
+
return path26.join(base, "foss-design", "previews");
|
|
4311
4401
|
}
|
|
4312
|
-
var entryFile = (pid) =>
|
|
4402
|
+
var entryFile = (pid) => path26.join(registryDir(), `${pid}.json`);
|
|
4313
4403
|
function writeState(paths, state) {
|
|
4314
4404
|
fs27.mkdirSync(paths.cache, { recursive: true });
|
|
4315
4405
|
fs27.writeFileSync(stateFile2(paths), `${JSON.stringify(state, null, 2)}
|
|
@@ -4336,7 +4426,7 @@ function listPreviews() {
|
|
|
4336
4426
|
}
|
|
4337
4427
|
const found = [];
|
|
4338
4428
|
for (const name of files) {
|
|
4339
|
-
const file =
|
|
4429
|
+
const file = path26.join(registryDir(), name);
|
|
4340
4430
|
let state = null;
|
|
4341
4431
|
try {
|
|
4342
4432
|
state = JSON.parse(fs27.readFileSync(file, "utf8"));
|
|
@@ -4348,7 +4438,7 @@ function listPreviews() {
|
|
|
4348
4438
|
}
|
|
4349
4439
|
let recorded = null;
|
|
4350
4440
|
try {
|
|
4351
|
-
recorded = JSON.parse(fs27.readFileSync(
|
|
4441
|
+
recorded = JSON.parse(fs27.readFileSync(path26.join(state.root, ".design", ".cache", "server.json"), "utf8"));
|
|
4352
4442
|
} catch {
|
|
4353
4443
|
}
|
|
4354
4444
|
found.push({ ...state, tracked: recorded?.pid === state.pid });
|
|
@@ -4363,7 +4453,7 @@ async function stopPreview(entry) {
|
|
|
4363
4453
|
const until = Date.now() + 5e3;
|
|
4364
4454
|
while (Date.now() < until && pidAlive(entry.pid)) await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4365
4455
|
fs27.rmSync(entryFile(entry.pid), { force: true });
|
|
4366
|
-
const stateAt =
|
|
4456
|
+
const stateAt = path26.join(entry.root, ".design", ".cache", "server.json");
|
|
4367
4457
|
try {
|
|
4368
4458
|
if (JSON.parse(fs27.readFileSync(stateAt, "utf8")).pid === entry.pid)
|
|
4369
4459
|
fs27.rmSync(stateAt, { force: true });
|
|
@@ -4452,8 +4542,8 @@ async function startDevServer(options) {
|
|
|
4452
4542
|
// `.cache` is ours (server.log, snapshots, builds): a change there must not reload frames. The
|
|
4453
4543
|
// generated entries are modules the frames load, so those stay watched.
|
|
4454
4544
|
ignored: (file) => {
|
|
4455
|
-
const rel = toPosix(
|
|
4456
|
-
const inCache = rel !== "" && !rel.startsWith("..") && !
|
|
4545
|
+
const rel = toPosix(path27.relative(paths.cache, file));
|
|
4546
|
+
const inCache = rel !== "" && !rel.startsWith("..") && !path27.isAbsolute(rel);
|
|
4457
4547
|
return inCache && rel !== "entries" && !rel.startsWith("entries/");
|
|
4458
4548
|
}
|
|
4459
4549
|
},
|
|
@@ -4462,7 +4552,7 @@ async function startDevServer(options) {
|
|
|
4462
4552
|
forwardConsole: false
|
|
4463
4553
|
},
|
|
4464
4554
|
optimizeDeps: {
|
|
4465
|
-
entries: entries.files().map((file) => toPosix(
|
|
4555
|
+
entries: entries.files().map((file) => toPosix(path27.relative(paths.design, file)))
|
|
4466
4556
|
}
|
|
4467
4557
|
});
|
|
4468
4558
|
const publicDir = project.publicDir();
|
|
@@ -4475,7 +4565,7 @@ async function startDevServer(options) {
|
|
|
4475
4565
|
}
|
|
4476
4566
|
};
|
|
4477
4567
|
const viewer = fs28.existsSync(PKG.viewer) ? sirv2(PKG.viewer, { dev: true, etag: true }) : null;
|
|
4478
|
-
const viewerIndex =
|
|
4568
|
+
const viewerIndex = path27.join(PKG.viewer, "index.html");
|
|
4479
4569
|
const serveViewerIndex = (res) => {
|
|
4480
4570
|
const html = fs28.readFileSync(viewerIndex, "utf8").replace(/<head>/i, '<head>\n<base href="/">');
|
|
4481
4571
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
|
|
@@ -4483,7 +4573,7 @@ async function startDevServer(options) {
|
|
|
4483
4573
|
};
|
|
4484
4574
|
const ctx = { project, events, snapshots };
|
|
4485
4575
|
const systemCssUrl = `/${relToDesign(paths, entries.systemCssFile())}`;
|
|
4486
|
-
const bootUrl = `/${relToDesign(paths,
|
|
4576
|
+
const bootUrl = `/${relToDesign(paths, path27.join(entries.dir, HTML_BOOT))}`;
|
|
4487
4577
|
const frameHead = (source, extra = {}) => {
|
|
4488
4578
|
const canvasTheme = source.canvas === SYSTEM_CANVAS ? void 0 : project.canvas(source.canvas)?.doc.theme;
|
|
4489
4579
|
return {
|
|
@@ -4513,7 +4603,7 @@ async function startDevServer(options) {
|
|
|
4513
4603
|
};
|
|
4514
4604
|
const serveHtmlScreen = async (res, url2) => {
|
|
4515
4605
|
const rel = decodeURIComponent(url2.pathname.slice("/_fs/".length));
|
|
4516
|
-
const file =
|
|
4606
|
+
const file = path27.resolve(paths.design, rel);
|
|
4517
4607
|
if (!isInside(paths.design, file) || !fs28.existsSync(file)) {
|
|
4518
4608
|
notFoundPage(res, "Screen not found", `No file .design/${rel}`);
|
|
4519
4609
|
return;
|
|
@@ -4524,7 +4614,7 @@ async function startDevServer(options) {
|
|
|
4524
4614
|
key: `${canvas}/${id2}`,
|
|
4525
4615
|
canvas,
|
|
4526
4616
|
id: id2,
|
|
4527
|
-
title:
|
|
4617
|
+
title: path27.basename(file),
|
|
4528
4618
|
file,
|
|
4529
4619
|
format: "html",
|
|
4530
4620
|
props: {},
|
|
@@ -4563,7 +4653,7 @@ async function startDevServer(options) {
|
|
|
4563
4653
|
}
|
|
4564
4654
|
if (url2.pathname.startsWith("/_fs/")) {
|
|
4565
4655
|
const rel = publicRel(url2.pathname.slice("/_fs/".length));
|
|
4566
|
-
if (publicDir && rel !== null && !project.isPublicFile(rel) && fs28.existsSync(
|
|
4656
|
+
if (publicDir && rel !== null && !project.isPublicFile(rel) && fs28.existsSync(path27.join(publicDir, rel))) {
|
|
4567
4657
|
notFoundPage(res, "Not found", url2.pathname);
|
|
4568
4658
|
return;
|
|
4569
4659
|
}
|
|
@@ -4575,7 +4665,7 @@ async function startDevServer(options) {
|
|
|
4575
4665
|
const rel = publicRel(url2.pathname.slice(1));
|
|
4576
4666
|
if (servePublic && rel !== null && project.isPublicFile(rel))
|
|
4577
4667
|
servePublic(req, res, () => notFoundPage(res, "Not found", url2.pathname));
|
|
4578
|
-
else if (
|
|
4668
|
+
else if (path27.extname(url2.pathname)) notFoundPage(res, "Not found", url2.pathname);
|
|
4579
4669
|
else serveViewerIndex(res);
|
|
4580
4670
|
};
|
|
4581
4671
|
if (url2.pathname === "/" || url2.pathname === "/index.html") serveViewerIndex(res);
|
|
@@ -4603,7 +4693,7 @@ async function startDevServer(options) {
|
|
|
4603
4693
|
};
|
|
4604
4694
|
vite.watcher.on("all", (event, file) => {
|
|
4605
4695
|
if (publicDir && isInside(publicDir, file)) project.invalidatePublic();
|
|
4606
|
-
const rel = toPosix(
|
|
4696
|
+
const rel = toPosix(path27.relative(paths.design, file));
|
|
4607
4697
|
if (rel.startsWith("..") || rel.startsWith(".cache/") || rel.startsWith("node_modules/")) return;
|
|
4608
4698
|
if (rel === "design.json") {
|
|
4609
4699
|
queue("config", () => {
|
|
@@ -5328,7 +5418,7 @@ function printLinkIssues(issues, project) {
|
|
|
5328
5418
|
// src/cli/commands/cloud.ts
|
|
5329
5419
|
import fs30 from "fs";
|
|
5330
5420
|
import os5 from "os";
|
|
5331
|
-
import
|
|
5421
|
+
import path28 from "path";
|
|
5332
5422
|
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
5333
5423
|
async function runLogin() {
|
|
5334
5424
|
const host = cloudHost();
|
|
@@ -5429,7 +5519,7 @@ async function runLink(paths, wanted, options) {
|
|
|
5429
5519
|
print(`${dim("Already linked to")} ${project.name} ${dim(`(${project.id}, ${project.role})`)}`);
|
|
5430
5520
|
} else {
|
|
5431
5521
|
writeLink(paths, { host, project: project.id, units: {}, conflicts: {} });
|
|
5432
|
-
fs30.rmSync(
|
|
5522
|
+
fs30.rmSync(path28.join(cloudCache(paths), "incoming"), { recursive: true, force: true });
|
|
5433
5523
|
const replaced = current ? dim(` (replacing the link to ${current.project})`) : "";
|
|
5434
5524
|
print(`${green("linked")} .design \u2192 ${bold(project.name)} ${dim(`(${project.id}, ${project.role})`)}${replaced}`);
|
|
5435
5525
|
}
|
|
@@ -5457,7 +5547,7 @@ async function runLink(paths, wanted, options) {
|
|
|
5457
5547
|
|
|
5458
5548
|
// src/cli/commands/icon.ts
|
|
5459
5549
|
import fs31 from "fs";
|
|
5460
|
-
import
|
|
5550
|
+
import path29 from "path";
|
|
5461
5551
|
async function toCloud(paths, file) {
|
|
5462
5552
|
const link = readLink(paths);
|
|
5463
5553
|
if (!link) return;
|
|
@@ -5468,7 +5558,7 @@ async function toCloud(paths, file) {
|
|
|
5468
5558
|
}
|
|
5469
5559
|
const client = new CloudClient(link.host, credential.token);
|
|
5470
5560
|
try {
|
|
5471
|
-
const ext = file ?
|
|
5561
|
+
const ext = file ? path29.extname(file).slice(1) : null;
|
|
5472
5562
|
await client.setProjectIcon(
|
|
5473
5563
|
link.project,
|
|
5474
5564
|
file && ext ? { data: fs31.readFileSync(file), type: ICON_TYPES[ext] } : null
|
|
@@ -5484,14 +5574,14 @@ async function runIcon(paths, file, options) {
|
|
|
5484
5574
|
if (options.remove) {
|
|
5485
5575
|
const had = iconFile(paths);
|
|
5486
5576
|
writeIcon(paths, null);
|
|
5487
|
-
print(had ? `${green("Removed")} ${
|
|
5577
|
+
print(had ? `${green("Removed")} ${path29.relative(process.cwd(), had)}` : "The project has no icon.");
|
|
5488
5578
|
await toCloud(paths, null);
|
|
5489
5579
|
return;
|
|
5490
5580
|
}
|
|
5491
5581
|
if (!file) {
|
|
5492
5582
|
const current = iconFile(paths);
|
|
5493
5583
|
if (!current) return print("The project has no icon. Set one with `design icon <file.svg|png|webp>`.");
|
|
5494
|
-
return print(`${
|
|
5584
|
+
return print(`${path29.relative(process.cwd(), current)} ${dim(`${Math.ceil(fs31.statSync(current).size / 1024)} KB`)}`);
|
|
5495
5585
|
}
|
|
5496
5586
|
let data;
|
|
5497
5587
|
try {
|
|
@@ -5499,20 +5589,20 @@ async function runIcon(paths, file, options) {
|
|
|
5499
5589
|
} catch {
|
|
5500
5590
|
throw new CliError(`No file ${file}`);
|
|
5501
5591
|
}
|
|
5502
|
-
const problem = iconProblem(data,
|
|
5592
|
+
const problem = iconProblem(data, path29.extname(file).slice(1).toLowerCase() || void 0);
|
|
5503
5593
|
if (problem) throw new CliError(`${file}: ${problem}`);
|
|
5504
5594
|
const written = writeIcon(paths, data);
|
|
5505
|
-
print(`${green("Set")} the project icon: ${
|
|
5595
|
+
print(`${green("Set")} the project icon: ${path29.relative(process.cwd(), written)}`);
|
|
5506
5596
|
await toCloud(paths, written);
|
|
5507
5597
|
}
|
|
5508
5598
|
|
|
5509
5599
|
// src/cli/commands/import.ts
|
|
5510
5600
|
import fs33 from "fs";
|
|
5511
|
-
import
|
|
5601
|
+
import path31 from "path";
|
|
5512
5602
|
|
|
5513
5603
|
// src/import/claude-design.ts
|
|
5514
5604
|
import fs32 from "fs";
|
|
5515
|
-
import
|
|
5605
|
+
import path30 from "path";
|
|
5516
5606
|
|
|
5517
5607
|
// src/import/markup.ts
|
|
5518
5608
|
var VOID = /* @__PURE__ */ new Set([
|
|
@@ -6120,7 +6210,7 @@ function boardId(file, used) {
|
|
|
6120
6210
|
return id2;
|
|
6121
6211
|
}
|
|
6122
6212
|
function importClaudeDesign(dir, options = {}) {
|
|
6123
|
-
const canvasFile =
|
|
6213
|
+
const canvasFile = path30.join(dir, "canvas.json");
|
|
6124
6214
|
if (!fs32.existsSync(canvasFile))
|
|
6125
6215
|
throw new Error(`No canvas.json in ${dir}: point at the exported Claude Design project folder`);
|
|
6126
6216
|
const source = JSON.parse(fs32.readFileSync(canvasFile, "utf8"));
|
|
@@ -6151,7 +6241,7 @@ function importClaudeDesign(dir, options = {}) {
|
|
|
6151
6241
|
try {
|
|
6152
6242
|
files.set(
|
|
6153
6243
|
`${name}.jsx`,
|
|
6154
|
-
convertFile(name, fs32.readFileSync(
|
|
6244
|
+
convertFile(name, fs32.readFileSync(path30.join(dir, file), "utf8"), { known, routes, warn: warn2 })
|
|
6155
6245
|
);
|
|
6156
6246
|
} catch (error) {
|
|
6157
6247
|
warn2(`could not be converted: ${error.message}`);
|
|
@@ -6199,7 +6289,7 @@ function importClaudeDesign(dir, options = {}) {
|
|
|
6199
6289
|
screens++;
|
|
6200
6290
|
}
|
|
6201
6291
|
const canvas = {
|
|
6202
|
-
title: options.title ?? source.title ??
|
|
6292
|
+
title: options.title ?? source.title ?? path30.basename(path30.resolve(dir)),
|
|
6203
6293
|
description: "Imported from Claude Design.",
|
|
6204
6294
|
// The templates bring their own styles; the design system's reset would restyle them.
|
|
6205
6295
|
system: false,
|
|
@@ -6211,7 +6301,7 @@ function importClaudeDesign(dir, options = {}) {
|
|
|
6211
6301
|
// src/cli/commands/import.ts
|
|
6212
6302
|
var slug = (text2) => text2.normalize("NFKD").replace(/[^\w\s-]/g, "").trim().replace(/[\s_]+/g, "-").toLowerCase();
|
|
6213
6303
|
function runImport(paths, from, options) {
|
|
6214
|
-
const dir =
|
|
6304
|
+
const dir = path31.resolve(from);
|
|
6215
6305
|
if (!fs33.existsSync(dir) || !fs33.statSync(dir).isDirectory()) throw new CliError(`No folder ${from}`);
|
|
6216
6306
|
let result;
|
|
6217
6307
|
try {
|
|
@@ -6219,13 +6309,13 @@ function runImport(paths, from, options) {
|
|
|
6219
6309
|
} catch (error) {
|
|
6220
6310
|
throw new CliError(error.message);
|
|
6221
6311
|
}
|
|
6222
|
-
const id2 = options.canvas ?? (slug(String(result.canvas.title)) || slug(
|
|
6312
|
+
const id2 = options.canvas ?? (slug(String(result.canvas.title)) || slug(path31.basename(dir)) || "imported");
|
|
6223
6313
|
if (!ID_PATTERN.test(id2)) throw new CliError(`"${id2}" is no canvas id; pass one with --canvas <id>`);
|
|
6224
|
-
const out =
|
|
6314
|
+
const out = path31.join(paths.canvases, id2);
|
|
6225
6315
|
if (fs33.existsSync(out)) throw new CliError(`.design/canvas/${id2} exists; pick another id with --canvas <id>`);
|
|
6226
|
-
fs33.mkdirSync(
|
|
6227
|
-
for (const [name, content] of result.files) fs33.writeFileSync(
|
|
6228
|
-
fs33.writeFileSync(
|
|
6316
|
+
fs33.mkdirSync(path31.join(out, "screens"), { recursive: true });
|
|
6317
|
+
for (const [name, content] of result.files) fs33.writeFileSync(path31.join(out, "screens", name), content);
|
|
6318
|
+
fs33.writeFileSync(path31.join(out, "canvas.json"), `${JSON.stringify(result.canvas, null, 2)}
|
|
6229
6319
|
`);
|
|
6230
6320
|
print(`${green("Imported")} ${bold(String(result.canvas.title))} \u2192 .design/canvas/${id2}`);
|
|
6231
6321
|
print(
|
|
@@ -6245,7 +6335,7 @@ function runImport(paths, from, options) {
|
|
|
6245
6335
|
|
|
6246
6336
|
// src/cli/commands/merge.ts
|
|
6247
6337
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
6248
|
-
import
|
|
6338
|
+
import path32 from "path";
|
|
6249
6339
|
import { createInterface } from "readline/promises";
|
|
6250
6340
|
var KIND = {
|
|
6251
6341
|
text: "both sides changed lines, the markers show them",
|
|
@@ -6261,7 +6351,7 @@ function matchTargets(paths, states, targets) {
|
|
|
6261
6351
|
for (const conflict of openConflicts(paths, unit)) picked.push({ state: unit, conflict });
|
|
6262
6352
|
continue;
|
|
6263
6353
|
}
|
|
6264
|
-
const rel = [
|
|
6354
|
+
const rel = [path32.relative(paths.design, path32.resolve(target)), target.replace(/^\.design\//, "")];
|
|
6265
6355
|
const found = states.flatMap(
|
|
6266
6356
|
(state) => state.conflicts.filter((item) => rel.includes(item.path)).map((conflict) => ({ state, conflict }))
|
|
6267
6357
|
);
|
|
@@ -6277,7 +6367,7 @@ function printOpen(paths, states) {
|
|
|
6277
6367
|
print(`${bold(state.unit)} ${dim(`merged with cloud r${state.headRev}`)}`);
|
|
6278
6368
|
for (const conflict of open2) {
|
|
6279
6369
|
print(` ${red("\u2717")} ${conflict.path} ${dim(KIND[conflict.kind])}`);
|
|
6280
|
-
const sides = ["here", "cloud", "base"].map((side) => `${side}: ${
|
|
6370
|
+
const sides = ["here", "cloud", "base"].map((side) => `${side}: ${path32.relative(process.cwd(), sideFile(paths, state.unit, conflict.path, side))}`).join(" \xB7 ");
|
|
6281
6371
|
print(` ${dim(sides)}`);
|
|
6282
6372
|
}
|
|
6283
6373
|
}
|
|
@@ -6358,7 +6448,7 @@ async function runMerge(paths, targets, options) {
|
|
|
6358
6448
|
if (answer === "s") break;
|
|
6359
6449
|
if (answer === "e" && edit) {
|
|
6360
6450
|
const editor = process.env.VISUAL || process.env.EDITOR || "vi";
|
|
6361
|
-
spawnSync2(editor, [
|
|
6451
|
+
spawnSync2(editor, [path32.join(paths.design, conflict.path)], { stdio: "inherit", shell: true });
|
|
6362
6452
|
if (!openConflicts(paths, state).some((item) => item.path === conflict.path)) break;
|
|
6363
6453
|
print(yellow(" Conflict markers are still there."));
|
|
6364
6454
|
}
|
|
@@ -6402,7 +6492,7 @@ async function runRename(paths, name) {
|
|
|
6402
6492
|
|
|
6403
6493
|
// src/cli/commands/scaffold.ts
|
|
6404
6494
|
import fs35 from "fs";
|
|
6405
|
-
import
|
|
6495
|
+
import path33 from "path";
|
|
6406
6496
|
|
|
6407
6497
|
// src/cli/templates.ts
|
|
6408
6498
|
var TOKENS_CSS = `/*
|
|
@@ -6614,16 +6704,16 @@ function mainScreen(canvasId) {
|
|
|
6614
6704
|
// src/cli/commands/scaffold.ts
|
|
6615
6705
|
function write(file, content, root) {
|
|
6616
6706
|
if (fs35.existsSync(file)) {
|
|
6617
|
-
print(` ${dim("exists ")} ${
|
|
6707
|
+
print(` ${dim("exists ")} ${path33.relative(root, file)}`);
|
|
6618
6708
|
return false;
|
|
6619
6709
|
}
|
|
6620
|
-
fs35.mkdirSync(
|
|
6710
|
+
fs35.mkdirSync(path33.dirname(file), { recursive: true });
|
|
6621
6711
|
fs35.writeFileSync(file, content);
|
|
6622
|
-
print(` ${green("created")} ${
|
|
6712
|
+
print(` ${green("created")} ${path33.relative(root, file)}`);
|
|
6623
6713
|
return true;
|
|
6624
6714
|
}
|
|
6625
6715
|
function ensureGitignored(root) {
|
|
6626
|
-
const file =
|
|
6716
|
+
const file = path33.join(root, ".gitignore");
|
|
6627
6717
|
const current = fs35.existsSync(file) ? fs35.readFileSync(file, "utf8") : "";
|
|
6628
6718
|
const covered = current.split(/\r?\n/).map((line) => line.trim()).some((line) => [".design", ".design/", "/.design", "/.design/", ".design/*"].includes(line));
|
|
6629
6719
|
if (covered) return "present";
|
|
@@ -6635,11 +6725,11 @@ function ensureGitignored(root) {
|
|
|
6635
6725
|
}
|
|
6636
6726
|
function projectName(root) {
|
|
6637
6727
|
try {
|
|
6638
|
-
const manifest = JSON.parse(fs35.readFileSync(
|
|
6728
|
+
const manifest = JSON.parse(fs35.readFileSync(path33.join(root, "package.json"), "utf8"));
|
|
6639
6729
|
if (typeof manifest.name === "string" && manifest.name) return manifest.name;
|
|
6640
6730
|
} catch {
|
|
6641
6731
|
}
|
|
6642
|
-
return
|
|
6732
|
+
return path33.basename(root);
|
|
6643
6733
|
}
|
|
6644
6734
|
function runInit(root, options) {
|
|
6645
6735
|
const paths = designPaths(root);
|
|
@@ -6664,40 +6754,40 @@ function runSystemInit(root, options) {
|
|
|
6664
6754
|
} catch {
|
|
6665
6755
|
}
|
|
6666
6756
|
const name = options.name ?? `${base} design system`;
|
|
6667
|
-
print(`Scaffolding the design system in ${
|
|
6668
|
-
write(
|
|
6757
|
+
print(`Scaffolding the design system in ${path33.relative(root, paths.system)}`);
|
|
6758
|
+
write(path33.join(paths.system, "system.json"), `${JSON.stringify({ name }, null, 2)}
|
|
6669
6759
|
`, root);
|
|
6670
|
-
write(
|
|
6760
|
+
write(path33.join(paths.system, "tokens.css"), TOKENS_CSS, root);
|
|
6671
6761
|
if (!options.empty) {
|
|
6672
|
-
write(
|
|
6673
|
-
write(
|
|
6674
|
-
write(
|
|
6762
|
+
write(path33.join(paths.system, "guidelines", "01-overview.md"), OVERVIEW_MD, root);
|
|
6763
|
+
write(path33.join(paths.system, "components", "button.tsx"), BUTTON_TSX, root);
|
|
6764
|
+
write(path33.join(paths.system, "specimens", "button.tsx"), BUTTON_SPECIMEN_TSX, root);
|
|
6675
6765
|
}
|
|
6676
6766
|
}
|
|
6677
6767
|
function runNew(root, id2, options) {
|
|
6678
6768
|
if (!ID_PATTERN.test(id2)) throw new CliError(`"${id2}" is not a valid canvas id: use letters, digits, "-" and "_"`);
|
|
6679
6769
|
const paths = designPaths(root);
|
|
6680
|
-
const dir =
|
|
6681
|
-
if (fs35.existsSync(
|
|
6770
|
+
const dir = path33.join(paths.canvases, id2);
|
|
6771
|
+
if (fs35.existsSync(path33.join(dir, "canvas.json"))) throw new CliError(`Canvas "${id2}" already exists in ${dir}`);
|
|
6682
6772
|
print(`Creating canvas "${id2}"`);
|
|
6683
6773
|
const title = options.title ?? titleize(id2);
|
|
6684
6774
|
if (options.empty) {
|
|
6685
6775
|
write(
|
|
6686
|
-
|
|
6776
|
+
path33.join(dir, "canvas.json"),
|
|
6687
6777
|
`${JSON.stringify({ title, pages: [{ id: "main", title: "Main", sections: [] }] }, null, 2)}
|
|
6688
6778
|
`,
|
|
6689
6779
|
root
|
|
6690
6780
|
);
|
|
6691
|
-
fs35.mkdirSync(
|
|
6781
|
+
fs35.mkdirSync(path33.join(dir, "screens"), { recursive: true });
|
|
6692
6782
|
} else {
|
|
6693
|
-
write(
|
|
6694
|
-
write(
|
|
6783
|
+
write(path33.join(dir, "canvas.json"), canvasJson(title), root);
|
|
6784
|
+
write(path33.join(dir, "screens", "Main.tsx"), mainScreen(id2), root);
|
|
6695
6785
|
}
|
|
6696
6786
|
}
|
|
6697
6787
|
|
|
6698
6788
|
// src/cli/commands/shot.ts
|
|
6699
6789
|
import fs36 from "fs";
|
|
6700
|
-
import
|
|
6790
|
+
import path34 from "path";
|
|
6701
6791
|
var OVERVIEW_TIMEOUT = 9e4;
|
|
6702
6792
|
async function runShot(paths, target, options) {
|
|
6703
6793
|
const [canvasId = "", screenId] = target.split("/");
|
|
@@ -6713,8 +6803,8 @@ async function runShot(paths, target, options) {
|
|
|
6713
6803
|
throw new CliError(`No page "${options.page}" in "${canvasId}". Pages: ${doc.pages.map((p) => p.id).join(", ")}`);
|
|
6714
6804
|
}
|
|
6715
6805
|
const theme2 = options.theme ?? doc.theme ?? "light";
|
|
6716
|
-
const outBase =
|
|
6717
|
-
const outDir =
|
|
6806
|
+
const outBase = path34.resolve(options.out ?? paths.shots);
|
|
6807
|
+
const outDir = path34.join(outBase, canvasId);
|
|
6718
6808
|
fs36.mkdirSync(outDir, { recursive: true });
|
|
6719
6809
|
const base = await serverBase(paths);
|
|
6720
6810
|
const browser = await launchChrome();
|
|
@@ -6737,7 +6827,7 @@ async function runShot(paths, target, options) {
|
|
|
6737
6827
|
warn(`page "${pageId}": not every frame reported ready within ${OVERVIEW_TIMEOUT / 1e3}s; shooting anyway`);
|
|
6738
6828
|
}
|
|
6739
6829
|
await page.waitForTimeout(400);
|
|
6740
|
-
const file =
|
|
6830
|
+
const file = path34.join(outDir, `${pageId}.overview.png`);
|
|
6741
6831
|
await page.screenshot({ path: file });
|
|
6742
6832
|
await page.close();
|
|
6743
6833
|
print(file);
|
|
@@ -6766,7 +6856,7 @@ async function shootFrames(browser, jobs, outDir) {
|
|
|
6766
6856
|
let failed = false;
|
|
6767
6857
|
const results = await mapLimit(jobs, 4, async (job) => {
|
|
6768
6858
|
const page = await browser.newPage({ viewport: { width: job.width, height: job.height } });
|
|
6769
|
-
const file =
|
|
6859
|
+
const file = path34.join(outDir, `${job.id}.${job.theme}.png`);
|
|
6770
6860
|
try {
|
|
6771
6861
|
const report = await openFrame(page, job.url, { waitForReady: job.waitForReady });
|
|
6772
6862
|
await page.screenshot({ path: file, fullPage: job.fullPage });
|
|
@@ -6798,7 +6888,7 @@ async function shootSystem(paths, project, id2, options) {
|
|
|
6798
6888
|
if (id2 && !all.some((job) => job.id === id2)) {
|
|
6799
6889
|
throw new CliError(`No specimen "${id2}" in the design system. Specimens: ${all.map((job) => job.id).join(", ")}`);
|
|
6800
6890
|
}
|
|
6801
|
-
const outDir =
|
|
6891
|
+
const outDir = path34.join(path34.resolve(options.out ?? paths.shots), SYSTEM_CANVAS);
|
|
6802
6892
|
fs36.mkdirSync(outDir, { recursive: true });
|
|
6803
6893
|
const base = await serverBase(paths);
|
|
6804
6894
|
const jobs = systemFrames(system.doc, base, theme2).filter((job) => !id2 || job.id === id2);
|
|
@@ -6823,6 +6913,16 @@ async function runSnapshots(paths, targets, options) {
|
|
|
6823
6913
|
}
|
|
6824
6914
|
if (options.page && picked.size !== 1)
|
|
6825
6915
|
throw new CliError("--page needs exactly one canvas: `design snapshots <canvas> --page <id>`");
|
|
6916
|
+
const project = new DesignProject(paths, DEV_URLS);
|
|
6917
|
+
for (const [canvas, screens] of picked) {
|
|
6918
|
+
if (!screens) continue;
|
|
6919
|
+
const ids = new Set(project.canvas(canvas).screens.map((screen) => screen.id));
|
|
6920
|
+
const missing = [...screens].filter((id2) => !ids.has(id2));
|
|
6921
|
+
if (missing.length)
|
|
6922
|
+
throw new CliError(
|
|
6923
|
+
`No screen ${missing.map((id2) => `"${id2}"`).join(", ")} in "${canvas}". Screens: ${[...ids].slice(0, 30).join(", ")}${ids.size > 30 ? ", \u2026" : ""}`
|
|
6924
|
+
);
|
|
6925
|
+
}
|
|
6826
6926
|
const canvases = picked.size ? [...picked.keys()] : known;
|
|
6827
6927
|
const stale = canvases.flatMap(
|
|
6828
6928
|
(canvas) => staleSnapshots(paths, [canvas], { page: options.page, screens: picked.get(canvas) ?? void 0 })
|
|
@@ -6839,7 +6939,7 @@ async function runSnapshots(paths, targets, options) {
|
|
|
6839
6939
|
}
|
|
6840
6940
|
|
|
6841
6941
|
// src/cli/commands/cloud-status.ts
|
|
6842
|
-
import
|
|
6942
|
+
import path35 from "path";
|
|
6843
6943
|
var STATE_TEXT = {
|
|
6844
6944
|
in_sync: green,
|
|
6845
6945
|
local_changes: yellow,
|
|
@@ -6862,7 +6962,7 @@ function detail(paths, status) {
|
|
|
6862
6962
|
if (status.state === "remote_ahead" && !status.local && !status.baseRev) parts.push("not pulled yet");
|
|
6863
6963
|
if (status.state === "remote_ahead" && status.changed.length && !status.deletedRemotely)
|
|
6864
6964
|
parts.push(`${plural(status.changed.length, "file")} changed here; the cloud only rebuilt it`);
|
|
6865
|
-
if (status.conflictRev !== void 0) parts.push(`merge ${
|
|
6965
|
+
if (status.conflictRev !== void 0) parts.push(`merge ${path35.relative(paths.root, incomingDir(paths, status.key))}`);
|
|
6866
6966
|
return parts.join(" \xB7 ");
|
|
6867
6967
|
}
|
|
6868
6968
|
async function printCloudStatus(paths, label) {
|
|
@@ -7057,7 +7157,7 @@ ${bold("Environment")}
|
|
|
7057
7157
|
FOSS_DESIGN_CLOUD Cloud to sign in and link to (default https://app.fossdesign.dev)
|
|
7058
7158
|
`;
|
|
7059
7159
|
function projectPaths(rootOption, requireDesign = true) {
|
|
7060
|
-
const found = rootOption ?
|
|
7160
|
+
const found = rootOption ? path36.resolve(rootOption) : findProjectRoot();
|
|
7061
7161
|
if (!found) {
|
|
7062
7162
|
if (!requireDesign) return designPaths(process.cwd());
|
|
7063
7163
|
throw new CliError("No .design folder here or above. Run `design init` in the project root first.");
|
|
@@ -7110,7 +7210,7 @@ async function main(argv) {
|
|
|
7110
7210
|
if (values.theme && !theme2) throw new CliError("--theme is light or dark");
|
|
7111
7211
|
switch (command) {
|
|
7112
7212
|
case "init":
|
|
7113
|
-
return runInit(values.root ?
|
|
7213
|
+
return runInit(values.root ? path36.resolve(values.root) : process.cwd(), {
|
|
7114
7214
|
name: values.name,
|
|
7115
7215
|
gitignore: !values["no-gitignore"]
|
|
7116
7216
|
});
|
|
@@ -7222,7 +7322,7 @@ function relaunchFromRealPath() {
|
|
|
7222
7322
|
} catch {
|
|
7223
7323
|
return false;
|
|
7224
7324
|
}
|
|
7225
|
-
const same = (a, b) =>
|
|
7325
|
+
const same = (a, b) => path36.resolve(a).toLowerCase() === path36.resolve(b).toLowerCase();
|
|
7226
7326
|
if (same(realScript, script) && same(realCwd, process.cwd())) return false;
|
|
7227
7327
|
const result = spawnSync3(process.execPath, [...process.execArgv, realScript, ...process.argv.slice(2)], {
|
|
7228
7328
|
stdio: "inherit",
|