foss-design 0.9.6 → 0.9.7
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 +232 -171
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3029,7 +3029,8 @@ var byUnit = (a, b) => a === SYSTEM_UNIT ? -1 : b === SYSTEM_UNIT ? 1 : a < b ?
|
|
|
3029
3029
|
var plural = (count, word, many = `${word}s`) => `${count} ${count === 1 ? word : many}`;
|
|
3030
3030
|
|
|
3031
3031
|
// src/capture/cover.ts
|
|
3032
|
-
import
|
|
3032
|
+
import fs20 from "fs";
|
|
3033
|
+
import os3 from "os";
|
|
3033
3034
|
|
|
3034
3035
|
// src/server/snapshots.ts
|
|
3035
3036
|
import fs17 from "fs";
|
|
@@ -3087,11 +3088,15 @@ var SnapshotStore = class {
|
|
|
3087
3088
|
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) throw new Error("bad snapshot key");
|
|
3088
3089
|
const dir = path17.join(this.dir, canvas);
|
|
3089
3090
|
fs17.mkdirSync(dir, { recursive: true });
|
|
3090
|
-
|
|
3091
|
+
const file = path17.join(dir, `${id2}.${theme2}.png`);
|
|
3092
|
+
fs17.writeFileSync(`${file}.${process.pid}.tmp`, png);
|
|
3093
|
+
fs17.renameSync(`${file}.${process.pid}.tmp`, file);
|
|
3091
3094
|
const data = this.load(canvas);
|
|
3092
3095
|
const version = Date.now();
|
|
3093
3096
|
data[id2] = { ...data[id2], [theme2]: version, ...height ? { height } : {} };
|
|
3094
|
-
|
|
3097
|
+
const temp = `${this.metaFile(canvas)}.${process.pid}.tmp`;
|
|
3098
|
+
fs17.writeFileSync(temp, JSON.stringify(data));
|
|
3099
|
+
fs17.renameSync(temp, this.metaFile(canvas));
|
|
3095
3100
|
return { url: this.url(canvas, id2, theme2, version) };
|
|
3096
3101
|
}
|
|
3097
3102
|
/** Absolute path of `<id>.<theme>.png`, or of its thumbnail for `<id>.<theme>.thumb.png` (made on first use); null when missing. */
|
|
@@ -3104,8 +3109,54 @@ var SnapshotStore = class {
|
|
|
3104
3109
|
}
|
|
3105
3110
|
};
|
|
3106
3111
|
|
|
3107
|
-
// src/capture/
|
|
3112
|
+
// src/capture/build-server.ts
|
|
3108
3113
|
import fs18 from "fs";
|
|
3114
|
+
import http from "http";
|
|
3115
|
+
import path18 from "path";
|
|
3116
|
+
import sirv from "sirv";
|
|
3117
|
+
async function serveBuild(paths, name, options) {
|
|
3118
|
+
const out = path18.join(paths.cache, name);
|
|
3119
|
+
fs18.rmSync(out, { recursive: true, force: true });
|
|
3120
|
+
await buildSite(paths, out, {
|
|
3121
|
+
canvases: options.canvases,
|
|
3122
|
+
includeSystem: options.includeSystem,
|
|
3123
|
+
includeViewer: false
|
|
3124
|
+
});
|
|
3125
|
+
const files = sirv(out, { dev: true });
|
|
3126
|
+
const server = http.createServer((req, res) => {
|
|
3127
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
3128
|
+
const segments = url.pathname.split("/").map((segment) => {
|
|
3129
|
+
try {
|
|
3130
|
+
return decodeURIComponent(segment);
|
|
3131
|
+
} catch {
|
|
3132
|
+
return segment;
|
|
3133
|
+
}
|
|
3134
|
+
});
|
|
3135
|
+
req.url = segments.join("/") + url.search;
|
|
3136
|
+
files(req, res, () => {
|
|
3137
|
+
res.statusCode = 404;
|
|
3138
|
+
res.end("Not found");
|
|
3139
|
+
});
|
|
3140
|
+
});
|
|
3141
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
3142
|
+
return {
|
|
3143
|
+
base: `http://127.0.0.1:${server.address().port}`,
|
|
3144
|
+
read(rel) {
|
|
3145
|
+
try {
|
|
3146
|
+
return JSON.parse(fs18.readFileSync(path18.join(out, rel), "utf8"));
|
|
3147
|
+
} catch {
|
|
3148
|
+
return null;
|
|
3149
|
+
}
|
|
3150
|
+
},
|
|
3151
|
+
close: () => new Promise((resolve) => {
|
|
3152
|
+
server.closeAllConnections();
|
|
3153
|
+
server.close(() => resolve());
|
|
3154
|
+
})
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
// src/capture/chrome.ts
|
|
3159
|
+
import fs19 from "fs";
|
|
3109
3160
|
var CANDIDATES = {
|
|
3110
3161
|
darwin: [
|
|
3111
3162
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
@@ -3129,8 +3180,8 @@ var CANDIDATES = {
|
|
|
3129
3180
|
};
|
|
3130
3181
|
function findChrome() {
|
|
3131
3182
|
const configured = process.env.DESIGN_CHROME;
|
|
3132
|
-
if (configured &&
|
|
3133
|
-
for (const candidate of CANDIDATES[process.platform] ?? []) if (
|
|
3183
|
+
if (configured && fs19.existsSync(configured)) return configured;
|
|
3184
|
+
for (const candidate of CANDIDATES[process.platform] ?? []) if (fs19.existsSync(candidate)) return candidate;
|
|
3134
3185
|
return null;
|
|
3135
3186
|
}
|
|
3136
3187
|
async function launchChrome() {
|
|
@@ -3161,6 +3212,8 @@ async function openFrame(page, url, options = {}) {
|
|
|
3161
3212
|
record2(text2);
|
|
3162
3213
|
});
|
|
3163
3214
|
let ready = false;
|
|
3215
|
+
let loadedAt = 0;
|
|
3216
|
+
let readyMs;
|
|
3164
3217
|
let reloads = 0;
|
|
3165
3218
|
const onNavigate = () => {
|
|
3166
3219
|
reloads++;
|
|
@@ -3173,6 +3226,7 @@ async function openFrame(page, url, options = {}) {
|
|
|
3173
3226
|
outdated = () => resolve("outdated");
|
|
3174
3227
|
});
|
|
3175
3228
|
await page.goto(url, { waitUntil: "load", timeout: options.timeoutMs ?? 3e4 });
|
|
3229
|
+
loadedAt = Date.now();
|
|
3176
3230
|
page.on("load", onNavigate);
|
|
3177
3231
|
if (!ready) {
|
|
3178
3232
|
const signal = page.waitForFunction(() => window.__DESIGN_READY__ === true, null, {
|
|
@@ -3181,6 +3235,7 @@ async function openFrame(page, url, options = {}) {
|
|
|
3181
3235
|
signal.catch(() => {
|
|
3182
3236
|
});
|
|
3183
3237
|
ready = await Promise.race([signal, turnedAway]).catch(() => null) === "ready";
|
|
3238
|
+
if (ready) readyMs = Date.now() - loadedAt;
|
|
3184
3239
|
}
|
|
3185
3240
|
if (ready || options.waitForReady === false) await page.waitForTimeout(options.settleMs ?? 700);
|
|
3186
3241
|
page.off("load", onNavigate);
|
|
@@ -3198,7 +3253,12 @@ async function openFrame(page, url, options = {}) {
|
|
|
3198
3253
|
const unique2 = errors.filter(
|
|
3199
3254
|
(error, index) => error && !errors.some((other, j) => j !== index && other && other.length < error.length && error.includes(other))
|
|
3200
3255
|
);
|
|
3201
|
-
return {
|
|
3256
|
+
return {
|
|
3257
|
+
errors: [...new Set(unique2)],
|
|
3258
|
+
ready,
|
|
3259
|
+
...held ? { held } : {},
|
|
3260
|
+
...readyMs !== void 0 ? { readyMs } : {}
|
|
3261
|
+
};
|
|
3202
3262
|
}
|
|
3203
3263
|
|
|
3204
3264
|
// src/capture/frames.ts
|
|
@@ -3275,7 +3335,7 @@ async function mapLimit(items, limit, fn) {
|
|
|
3275
3335
|
|
|
3276
3336
|
// src/capture/cover.ts
|
|
3277
3337
|
var SNAPSHOT_WIDTH = 900;
|
|
3278
|
-
var TABS =
|
|
3338
|
+
var TABS = Math.min(8, Math.max(2, os3.availableParallelism() - 1));
|
|
3279
3339
|
var THEMES2 = ["light", "dark"];
|
|
3280
3340
|
function staleSnapshots(paths, canvasIds, filter = {}) {
|
|
3281
3341
|
const store = new SnapshotStore(paths.snapshots);
|
|
@@ -3292,8 +3352,8 @@ function staleSnapshots(paths, canvasIds, filter = {}) {
|
|
|
3292
3352
|
for (const job of inLight) {
|
|
3293
3353
|
if (filter.screens && !filter.screens.has(job.id)) continue;
|
|
3294
3354
|
const file = files.get(job.id);
|
|
3295
|
-
if (!file || !
|
|
3296
|
-
const changed =
|
|
3355
|
+
if (!file || !fs20.existsSync(file)) continue;
|
|
3356
|
+
const changed = fs20.statSync(file).mtimeMs;
|
|
3297
3357
|
const pinned = inDark.get(job.id) === job.theme;
|
|
3298
3358
|
const themes = pinned ? [job.theme] : THEMES2;
|
|
3299
3359
|
const screen = { id: job.id };
|
|
@@ -3305,12 +3365,25 @@ function staleSnapshots(paths, canvasIds, filter = {}) {
|
|
|
3305
3365
|
}
|
|
3306
3366
|
return stale;
|
|
3307
3367
|
}
|
|
3308
|
-
async function takeSnapshots(paths, targets,
|
|
3368
|
+
async function takeSnapshots(paths, targets, preview, progress2) {
|
|
3309
3369
|
if (!targets.length) return [];
|
|
3310
3370
|
if (!findChrome()) return ["No Chrome here, so screens keep the snapshots they have."];
|
|
3311
3371
|
const store = new SnapshotStore(paths.snapshots);
|
|
3312
3372
|
const project = new DesignProject(paths, DEV_URLS, store.lookup);
|
|
3313
|
-
const
|
|
3373
|
+
const canvases = [...new Set(targets.map((target) => target.canvas))];
|
|
3374
|
+
progress2.update(`Building ${canvases.join(", ")} to take snapshots from\u2026`);
|
|
3375
|
+
let url;
|
|
3376
|
+
let docOf = (id2) => project.canvas(id2).doc;
|
|
3377
|
+
let stop = async () => {
|
|
3378
|
+
};
|
|
3379
|
+
try {
|
|
3380
|
+
const build2 = await serveBuild(paths, "snapshot-build", { canvases, includeSystem: false });
|
|
3381
|
+
url = build2.base;
|
|
3382
|
+
docOf = (id2) => build2.read(`api/canvas/${id2}.json`) ?? project.canvas(id2).doc;
|
|
3383
|
+
stop = build2.close;
|
|
3384
|
+
} catch {
|
|
3385
|
+
url = await preview();
|
|
3386
|
+
}
|
|
3314
3387
|
const browser = await launchChrome();
|
|
3315
3388
|
const failed = [];
|
|
3316
3389
|
const started = Date.now();
|
|
@@ -3319,7 +3392,7 @@ async function takeSnapshots(paths, targets, base, progress2) {
|
|
|
3319
3392
|
progress2.update(line());
|
|
3320
3393
|
try {
|
|
3321
3394
|
await mapLimit([...targets], TABS, async ({ canvas, screen, theme: theme2 }) => {
|
|
3322
|
-
const job = canvasFrames(
|
|
3395
|
+
const job = canvasFrames(docOf(canvas), url, theme2, { id: screen })[0];
|
|
3323
3396
|
if (job) {
|
|
3324
3397
|
const page = await browser.newPage({
|
|
3325
3398
|
viewport: { width: job.width, height: job.height },
|
|
@@ -3345,6 +3418,7 @@ async function takeSnapshots(paths, targets, base, progress2) {
|
|
|
3345
3418
|
});
|
|
3346
3419
|
} finally {
|
|
3347
3420
|
await browser.close();
|
|
3421
|
+
await stop();
|
|
3348
3422
|
}
|
|
3349
3423
|
const seconds2 = Math.round((Date.now() - started) / 1e3);
|
|
3350
3424
|
progress2.done(`Took ${targets.length - failed.length} snapshot${targets.length === 1 ? "" : "s"} in ${seconds2}s`);
|
|
@@ -3356,73 +3430,73 @@ async function takeSnapshots(paths, targets, base, progress2) {
|
|
|
3356
3430
|
}
|
|
3357
3431
|
|
|
3358
3432
|
// src/cloud/pull.ts
|
|
3359
|
-
import
|
|
3360
|
-
import
|
|
3433
|
+
import fs24 from "fs";
|
|
3434
|
+
import path22 from "path";
|
|
3361
3435
|
|
|
3362
3436
|
// src/cloud/merge.ts
|
|
3363
|
-
import
|
|
3364
|
-
import
|
|
3437
|
+
import fs22 from "fs";
|
|
3438
|
+
import path20 from "path";
|
|
3365
3439
|
import { merge as diff3 } from "node-diff3";
|
|
3366
3440
|
|
|
3367
3441
|
// src/cloud/write.ts
|
|
3368
|
-
import
|
|
3369
|
-
import
|
|
3442
|
+
import fs21 from "fs";
|
|
3443
|
+
import path19 from "path";
|
|
3370
3444
|
function writeInside(root, rel, data) {
|
|
3371
|
-
const file =
|
|
3445
|
+
const file = path19.join(root, rel);
|
|
3372
3446
|
if (!isInside(root, file)) throw new CliError(`Refusing to write outside ${root}: ${rel}`);
|
|
3373
|
-
|
|
3374
|
-
if (!isInside(
|
|
3447
|
+
fs21.mkdirSync(path19.dirname(file), { recursive: true });
|
|
3448
|
+
if (!isInside(fs21.realpathSync(root), fs21.realpathSync(path19.dirname(file))))
|
|
3375
3449
|
throw new CliError(`Refusing to write through a link out of ${root}: ${rel}`);
|
|
3376
|
-
if (
|
|
3377
|
-
|
|
3450
|
+
if (fs21.existsSync(file) && fs21.lstatSync(file).isSymbolicLink()) fs21.unlinkSync(file);
|
|
3451
|
+
fs21.writeFileSync(file, data);
|
|
3378
3452
|
}
|
|
3379
3453
|
function pruneEmpty(dir, stop) {
|
|
3380
3454
|
let current = dir;
|
|
3381
3455
|
while (isInside(stop, current) && current !== stop) {
|
|
3382
3456
|
try {
|
|
3383
|
-
if (
|
|
3384
|
-
|
|
3457
|
+
if (fs21.readdirSync(current).length) return;
|
|
3458
|
+
fs21.rmdirSync(current);
|
|
3385
3459
|
} catch {
|
|
3386
3460
|
return;
|
|
3387
3461
|
}
|
|
3388
|
-
current =
|
|
3462
|
+
current = path19.dirname(current);
|
|
3389
3463
|
}
|
|
3390
3464
|
}
|
|
3391
3465
|
|
|
3392
3466
|
// src/cloud/merge.ts
|
|
3393
3467
|
var TEXT = /\.(?:[cm]?[jt]sx?|json|css|scss|html?|mdx?|svg|txt|ya?ml|csv|xml|toml)$/i;
|
|
3394
3468
|
var isText = (rel, ...data) => TEXT.test(rel) && data.every((buffer) => !buffer || !buffer.subarray(0, 8192).includes(0));
|
|
3395
|
-
var mergeDir = (paths, key) =>
|
|
3396
|
-
var stateFile = (paths, key) =>
|
|
3397
|
-
var sideFile = (paths, key, rel, side) =>
|
|
3469
|
+
var mergeDir = (paths, key) => path20.join(cloudCache(paths), "merge", ...key.split("/"));
|
|
3470
|
+
var stateFile = (paths, key) => path20.join(mergeDir(paths, key), "state.json");
|
|
3471
|
+
var sideFile = (paths, key, rel, side) => path20.join(mergeDir(paths, key), side, rel);
|
|
3398
3472
|
function readMergeState(paths, key) {
|
|
3399
3473
|
try {
|
|
3400
|
-
return JSON.parse(
|
|
3474
|
+
return JSON.parse(fs22.readFileSync(stateFile(paths, key), "utf8"));
|
|
3401
3475
|
} catch {
|
|
3402
3476
|
return null;
|
|
3403
3477
|
}
|
|
3404
3478
|
}
|
|
3405
3479
|
function saveState(paths, state) {
|
|
3406
|
-
|
|
3407
|
-
|
|
3480
|
+
fs22.mkdirSync(mergeDir(paths, state.unit), { recursive: true });
|
|
3481
|
+
fs22.writeFileSync(stateFile(paths, state.unit), `${JSON.stringify(state, null, 2)}
|
|
3408
3482
|
`);
|
|
3409
3483
|
}
|
|
3410
3484
|
function mergingUnits(paths) {
|
|
3411
|
-
const root =
|
|
3485
|
+
const root = path20.join(cloudCache(paths), "merge");
|
|
3412
3486
|
const found = [];
|
|
3413
3487
|
const visit = (dir, prefix) => {
|
|
3414
|
-
if (
|
|
3488
|
+
if (fs22.existsSync(path20.join(dir, "state.json"))) {
|
|
3415
3489
|
const state = readMergeState(paths, prefix);
|
|
3416
3490
|
if (state) found.push(state);
|
|
3417
3491
|
return;
|
|
3418
3492
|
}
|
|
3419
3493
|
let entries = [];
|
|
3420
3494
|
try {
|
|
3421
|
-
entries =
|
|
3495
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
3422
3496
|
} catch {
|
|
3423
3497
|
}
|
|
3424
3498
|
for (const entry of entries)
|
|
3425
|
-
if (entry.isDirectory()) visit(
|
|
3499
|
+
if (entry.isDirectory()) visit(path20.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
3426
3500
|
};
|
|
3427
3501
|
visit(root, "");
|
|
3428
3502
|
return found;
|
|
@@ -3432,7 +3506,7 @@ function openConflicts(paths, state) {
|
|
|
3432
3506
|
return state.conflicts.filter((conflict) => {
|
|
3433
3507
|
if (conflict.kind !== "text") return true;
|
|
3434
3508
|
try {
|
|
3435
|
-
return MARKER.test(
|
|
3509
|
+
return MARKER.test(fs22.readFileSync(path20.join(paths.design, conflict.path), "utf8"));
|
|
3436
3510
|
} catch {
|
|
3437
3511
|
return false;
|
|
3438
3512
|
}
|
|
@@ -3441,19 +3515,19 @@ function openConflicts(paths, state) {
|
|
|
3441
3515
|
function keep(paths, key, rel, side, data) {
|
|
3442
3516
|
if (!data) return;
|
|
3443
3517
|
const file = sideFile(paths, key, rel, side);
|
|
3444
|
-
|
|
3445
|
-
|
|
3518
|
+
fs22.mkdirSync(path20.dirname(file), { recursive: true });
|
|
3519
|
+
fs22.writeFileSync(file, data);
|
|
3446
3520
|
}
|
|
3447
3521
|
function mergeUnit(paths, key, headRev, base, here, cloud2, blob) {
|
|
3448
3522
|
const files = { added: 0, changed: 0, removed: 0, merged: 0 };
|
|
3449
3523
|
const conflicts = [];
|
|
3450
|
-
const stop = key === SYSTEM_UNIT ? paths.design :
|
|
3451
|
-
|
|
3452
|
-
const read = (rel) =>
|
|
3524
|
+
const stop = key === SYSTEM_UNIT ? paths.design : path20.dirname(unitRoot(paths, key));
|
|
3525
|
+
fs22.rmSync(mergeDir(paths, key), { recursive: true, force: true });
|
|
3526
|
+
const read = (rel) => fs22.readFileSync(path20.join(paths.design, rel));
|
|
3453
3527
|
const remove = (rel) => {
|
|
3454
|
-
const file =
|
|
3455
|
-
|
|
3456
|
-
pruneEmpty(
|
|
3528
|
+
const file = path20.join(paths.design, rel);
|
|
3529
|
+
fs22.rmSync(file, { force: true });
|
|
3530
|
+
pruneEmpty(path20.dirname(file), stop);
|
|
3457
3531
|
};
|
|
3458
3532
|
for (const rel of [.../* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(here), ...Object.keys(cloud2)])].sort()) {
|
|
3459
3533
|
const b = base[rel];
|
|
@@ -3509,11 +3583,11 @@ function resolveConflict(paths, state, rel, side) {
|
|
|
3509
3583
|
const conflict = state.conflicts.find((item) => item.path === rel);
|
|
3510
3584
|
if (!conflict) return;
|
|
3511
3585
|
const kept = sideFile(paths, state.unit, rel, side);
|
|
3512
|
-
const stop = state.unit === SYSTEM_UNIT ? paths.design :
|
|
3513
|
-
if (
|
|
3586
|
+
const stop = state.unit === SYSTEM_UNIT ? paths.design : path20.dirname(unitRoot(paths, state.unit));
|
|
3587
|
+
if (fs22.existsSync(kept)) writeInside(paths.design, rel, fs22.readFileSync(kept));
|
|
3514
3588
|
else {
|
|
3515
|
-
|
|
3516
|
-
pruneEmpty(
|
|
3589
|
+
fs22.rmSync(path20.join(paths.design, rel), { force: true });
|
|
3590
|
+
pruneEmpty(path20.dirname(path20.join(paths.design, rel)), stop);
|
|
3517
3591
|
}
|
|
3518
3592
|
state.conflicts = state.conflicts.filter((item) => item.path !== rel);
|
|
3519
3593
|
saveState(paths, state);
|
|
@@ -3523,15 +3597,15 @@ function finishMerge(paths, link, key, accept = false) {
|
|
|
3523
3597
|
if (state) {
|
|
3524
3598
|
const open2 = openConflicts(paths, state).filter((conflict) => !(accept && conflict.kind !== "text"));
|
|
3525
3599
|
if (open2.length) return open2;
|
|
3526
|
-
|
|
3600
|
+
fs22.rmSync(mergeDir(paths, key), { recursive: true, force: true });
|
|
3527
3601
|
}
|
|
3528
3602
|
delete link.conflicts[key];
|
|
3529
3603
|
return [];
|
|
3530
3604
|
}
|
|
3531
3605
|
|
|
3532
3606
|
// src/cloud/meta.ts
|
|
3533
|
-
import
|
|
3534
|
-
import
|
|
3607
|
+
import fs23 from "fs";
|
|
3608
|
+
import path21 from "path";
|
|
3535
3609
|
async function syncProjectMeta(paths, client, link) {
|
|
3536
3610
|
const remote = await client.project(link.project);
|
|
3537
3611
|
if (!remote) return [];
|
|
@@ -3550,8 +3624,8 @@ async function syncProjectMeta(paths, client, link) {
|
|
|
3550
3624
|
if (localChanged && !cloudChanged) {
|
|
3551
3625
|
try {
|
|
3552
3626
|
if (file) {
|
|
3553
|
-
const ext =
|
|
3554
|
-
await client.setProjectIcon(link.project, { data:
|
|
3627
|
+
const ext = path21.extname(file).slice(1);
|
|
3628
|
+
await client.setProjectIcon(link.project, { data: fs23.readFileSync(file), type: ICON_TYPES[ext] });
|
|
3555
3629
|
lines.push("The project icon went up to the cloud.");
|
|
3556
3630
|
} else {
|
|
3557
3631
|
await client.setProjectIcon(link.project, null);
|
|
@@ -3586,12 +3660,12 @@ function checkManifest(key, manifest) {
|
|
|
3586
3660
|
}
|
|
3587
3661
|
function applyUnit(paths, key, local, target, blobs) {
|
|
3588
3662
|
const counts = { added: 0, changed: 0, removed: 0 };
|
|
3589
|
-
const stop = key === SYSTEM_UNIT ? paths.design :
|
|
3663
|
+
const stop = key === SYSTEM_UNIT ? paths.design : path22.dirname(unitRoot(paths, key));
|
|
3590
3664
|
for (const rel of Object.keys(local)) {
|
|
3591
3665
|
if (rel in target) continue;
|
|
3592
|
-
const file =
|
|
3593
|
-
|
|
3594
|
-
pruneEmpty(
|
|
3666
|
+
const file = path22.join(paths.design, rel);
|
|
3667
|
+
fs24.rmSync(file, { force: true });
|
|
3668
|
+
pruneEmpty(path22.dirname(file), stop);
|
|
3595
3669
|
counts.removed++;
|
|
3596
3670
|
}
|
|
3597
3671
|
for (const [rel, entry] of Object.entries(target)) {
|
|
@@ -3661,7 +3735,7 @@ async function pull(ctx, options) {
|
|
|
3661
3735
|
}
|
|
3662
3736
|
if (s.conflictRev !== void 0) {
|
|
3663
3737
|
finishMerge(paths, link, key, true);
|
|
3664
|
-
|
|
3738
|
+
fs24.rmSync(incomingDir(paths, key), { recursive: true, force: true });
|
|
3665
3739
|
}
|
|
3666
3740
|
report.units.push({ ...reportFor("up_to_date"), rev: s.baseRev });
|
|
3667
3741
|
} else if (!s.changed.length) {
|
|
@@ -3690,7 +3764,7 @@ async function pull(ctx, options) {
|
|
|
3690
3764
|
}
|
|
3691
3765
|
const byHash = /* @__PURE__ */ new Map();
|
|
3692
3766
|
for (const unit of scan.units.values())
|
|
3693
|
-
for (const [rel, entry] of Object.entries(unit.manifest)) byHash.set(entry.hash,
|
|
3767
|
+
for (const [rel, entry] of Object.entries(unit.manifest)) byHash.set(entry.hash, path22.join(paths.design, rel));
|
|
3694
3768
|
const needed = /* @__PURE__ */ new Set();
|
|
3695
3769
|
const bases = /* @__PURE__ */ new Set();
|
|
3696
3770
|
for (const plan of plans) {
|
|
@@ -3711,7 +3785,7 @@ async function pull(ctx, options) {
|
|
|
3711
3785
|
const download = [];
|
|
3712
3786
|
for (const hash of needed) {
|
|
3713
3787
|
const file = byHash.get(hash);
|
|
3714
|
-
if (file) blobs.set(hash,
|
|
3788
|
+
if (file) blobs.set(hash, fs24.readFileSync(file));
|
|
3715
3789
|
else download.push(hash);
|
|
3716
3790
|
}
|
|
3717
3791
|
if (download.length) {
|
|
@@ -3747,7 +3821,7 @@ async function pull(ctx, options) {
|
|
|
3747
3821
|
if (plan.remote) link.units[key] = { rev: plan.remote.headRev, files: hashes(plan.remote.manifest) };
|
|
3748
3822
|
else delete link.units[key];
|
|
3749
3823
|
delete link.conflicts[key];
|
|
3750
|
-
|
|
3824
|
+
fs24.rmSync(incomingDir(paths, key), { recursive: true, force: true });
|
|
3751
3825
|
} else {
|
|
3752
3826
|
const result = mergeUnit(
|
|
3753
3827
|
paths,
|
|
@@ -3758,7 +3832,7 @@ async function pull(ctx, options) {
|
|
|
3758
3832
|
plan.remote.manifest,
|
|
3759
3833
|
(hash) => blobs.get(hash)
|
|
3760
3834
|
);
|
|
3761
|
-
|
|
3835
|
+
fs24.rmSync(incomingDir(paths, key), { recursive: true, force: true });
|
|
3762
3836
|
link.units[key] = { rev: plan.remote.headRev, files: hashes(plan.remote.manifest) };
|
|
3763
3837
|
entry.rev = plan.remote.headRev;
|
|
3764
3838
|
entry.files = result.files;
|
|
@@ -3788,8 +3862,8 @@ async function pull(ctx, options) {
|
|
|
3788
3862
|
}
|
|
3789
3863
|
|
|
3790
3864
|
// src/cloud/push.ts
|
|
3791
|
-
import
|
|
3792
|
-
import
|
|
3865
|
+
import fs25 from "fs";
|
|
3866
|
+
import path23 from "path";
|
|
3793
3867
|
function blockedReport(status, message) {
|
|
3794
3868
|
return {
|
|
3795
3869
|
unit: status.key,
|
|
@@ -3905,7 +3979,7 @@ async function push(ctx, options) {
|
|
|
3905
3979
|
for (const key of toPush) {
|
|
3906
3980
|
const s = status(key);
|
|
3907
3981
|
if (s.conflictRev !== void 0) {
|
|
3908
|
-
const dir =
|
|
3982
|
+
const dir = path23.relative(paths.root, incomingDir(paths, key));
|
|
3909
3983
|
blocked.push(
|
|
3910
3984
|
blockedReport(
|
|
3911
3985
|
s,
|
|
@@ -3954,7 +4028,7 @@ async function push(ctx, options) {
|
|
|
3954
4028
|
const files = /* @__PURE__ */ new Map();
|
|
3955
4029
|
const addFiles = (manifest, dir) => {
|
|
3956
4030
|
for (const [rel, entry] of Object.entries(manifest))
|
|
3957
|
-
if (!files.has(entry.hash)) files.set(entry.hash,
|
|
4031
|
+
if (!files.has(entry.hash)) files.set(entry.hash, path23.join(dir, rel));
|
|
3958
4032
|
};
|
|
3959
4033
|
for (const key of pushing) {
|
|
3960
4034
|
const id2 = unitCanvasId(key);
|
|
@@ -3986,7 +4060,7 @@ async function push(ctx, options) {
|
|
|
3986
4060
|
if (missing.length) {
|
|
3987
4061
|
const unknown = missing.find((hash) => !files.has(hash));
|
|
3988
4062
|
if (unknown) throw new CliError(`The cloud asked for ${unknown.slice(0, 12)}, which is not part of this push`);
|
|
3989
|
-
const bytes = missing.reduce((sum, hash) => sum +
|
|
4063
|
+
const bytes = missing.reduce((sum, hash) => sum + fs25.statSync(files.get(hash)).size, 0);
|
|
3990
4064
|
const bar = ctx.progress();
|
|
3991
4065
|
const started = Date.now();
|
|
3992
4066
|
let sentFiles = 0;
|
|
@@ -3995,12 +4069,12 @@ async function push(ctx, options) {
|
|
|
3995
4069
|
bar.update(line());
|
|
3996
4070
|
await mapLimit(missing, TRANSFERS, async (hash) => {
|
|
3997
4071
|
const file = files.get(hash);
|
|
3998
|
-
const data =
|
|
4072
|
+
const data = fs25.readFileSync(file);
|
|
3999
4073
|
try {
|
|
4000
4074
|
await client.putBlob(link.project, hash, data);
|
|
4001
4075
|
} catch (error) {
|
|
4002
4076
|
if (error instanceof CloudError && error.status === 413)
|
|
4003
|
-
throw new CliError(`${
|
|
4077
|
+
throw new CliError(`${path23.relative(process.cwd(), file)} (${megabytes(data.length)}): ${error.message}`);
|
|
4004
4078
|
throw error;
|
|
4005
4079
|
}
|
|
4006
4080
|
sentFiles++;
|
|
@@ -4033,7 +4107,7 @@ async function push(ctx, options) {
|
|
|
4033
4107
|
url: webUrl(link, unit.key)
|
|
4034
4108
|
});
|
|
4035
4109
|
}
|
|
4036
|
-
for (const key of resolved)
|
|
4110
|
+
for (const key of resolved) fs25.rmSync(incomingDir(paths, key), { recursive: true, force: true });
|
|
4037
4111
|
writeLink(paths, link);
|
|
4038
4112
|
report.units.sort((a, b) => byUnit(a.unit, b.unit));
|
|
4039
4113
|
return report;
|
|
@@ -4044,7 +4118,7 @@ function refuseLegacyGo(paths, keys) {
|
|
|
4044
4118
|
for (const key of keys) {
|
|
4045
4119
|
if (!key.startsWith("canvas/")) continue;
|
|
4046
4120
|
const id2 = key.slice("canvas/".length);
|
|
4047
|
-
for (const file of canvasCodeFiles(
|
|
4121
|
+
for (const file of canvasCodeFiles(path23.join(paths.canvases, id2), project.canvas(id2)?.screens ?? [])) {
|
|
4048
4122
|
for (const line of legacyGoLines(file)) hits.add(`${relToRoot(paths, file)}:${line}`);
|
|
4049
4123
|
}
|
|
4050
4124
|
}
|
|
@@ -4060,18 +4134,18 @@ Still using go():
|
|
|
4060
4134
|
|
|
4061
4135
|
// src/cli/commands/preview.ts
|
|
4062
4136
|
import { spawn } from "child_process";
|
|
4063
|
-
import
|
|
4137
|
+
import fs29 from "fs";
|
|
4064
4138
|
|
|
4065
4139
|
// src/server/index.ts
|
|
4066
|
-
import
|
|
4067
|
-
import
|
|
4068
|
-
import
|
|
4069
|
-
import
|
|
4140
|
+
import fs28 from "fs";
|
|
4141
|
+
import http2 from "http";
|
|
4142
|
+
import path26 from "path";
|
|
4143
|
+
import sirv2 from "sirv";
|
|
4070
4144
|
import { createServer, searchForWorkspaceRoot } from "vite";
|
|
4071
4145
|
|
|
4072
4146
|
// src/server/api.ts
|
|
4073
|
-
import
|
|
4074
|
-
import
|
|
4147
|
+
import fs26 from "fs";
|
|
4148
|
+
import path24 from "path";
|
|
4075
4149
|
function sendJson(res, status, body) {
|
|
4076
4150
|
res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
4077
4151
|
res.end(JSON.stringify(body));
|
|
@@ -4122,17 +4196,17 @@ async function handleApi(ctx, req, res, url) {
|
|
|
4122
4196
|
}
|
|
4123
4197
|
if (route2 === "/source") {
|
|
4124
4198
|
const rel = url.searchParams.get("path") ?? "";
|
|
4125
|
-
const file =
|
|
4126
|
-
if (!rel || !isInside(project.paths.root, file) || !
|
|
4199
|
+
const file = path24.resolve(project.paths.root, rel);
|
|
4200
|
+
if (!rel || !isInside(project.paths.root, file) || !fs26.existsSync(file) || fs26.statSync(file).isDirectory()) {
|
|
4127
4201
|
sendJson(res, 404, { error: "not found" });
|
|
4128
4202
|
return true;
|
|
4129
4203
|
}
|
|
4130
|
-
if (
|
|
4204
|
+
if (fs26.statSync(file).size > 2 * 1024 * 1024) {
|
|
4131
4205
|
sendJson(res, 413, { error: "file too large to show" });
|
|
4132
4206
|
return true;
|
|
4133
4207
|
}
|
|
4134
4208
|
res.writeHead(200, { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" });
|
|
4135
|
-
res.end(
|
|
4209
|
+
res.end(fs26.readFileSync(file, "utf8"));
|
|
4136
4210
|
return true;
|
|
4137
4211
|
}
|
|
4138
4212
|
if (route2 === "/icon" && (method === "PUT" || method === "DELETE")) {
|
|
@@ -4179,7 +4253,7 @@ async function handleApi(ctx, req, res, url) {
|
|
|
4179
4253
|
return true;
|
|
4180
4254
|
}
|
|
4181
4255
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "public, max-age=31536000, immutable" });
|
|
4182
|
-
|
|
4256
|
+
fs26.createReadStream(file).pipe(res);
|
|
4183
4257
|
return true;
|
|
4184
4258
|
}
|
|
4185
4259
|
return false;
|
|
@@ -4219,62 +4293,62 @@ var EventHub = class {
|
|
|
4219
4293
|
};
|
|
4220
4294
|
|
|
4221
4295
|
// src/server/state.ts
|
|
4222
|
-
import
|
|
4223
|
-
import
|
|
4224
|
-
import
|
|
4225
|
-
var stateFile2 = (paths) =>
|
|
4226
|
-
var logFile = (paths) =>
|
|
4296
|
+
import fs27 from "fs";
|
|
4297
|
+
import os4 from "os";
|
|
4298
|
+
import path25 from "path";
|
|
4299
|
+
var stateFile2 = (paths) => path25.join(paths.cache, "server.json");
|
|
4300
|
+
var logFile = (paths) => path25.join(paths.cache, "server.log");
|
|
4227
4301
|
function readState(paths) {
|
|
4228
4302
|
try {
|
|
4229
|
-
return JSON.parse(
|
|
4303
|
+
return JSON.parse(fs27.readFileSync(stateFile2(paths), "utf8"));
|
|
4230
4304
|
} catch {
|
|
4231
4305
|
return null;
|
|
4232
4306
|
}
|
|
4233
4307
|
}
|
|
4234
4308
|
function registryDir() {
|
|
4235
|
-
const base = process.platform === "win32" ? process.env.LOCALAPPDATA || process.env.APPDATA ||
|
|
4236
|
-
return
|
|
4309
|
+
const base = process.platform === "win32" ? process.env.LOCALAPPDATA || process.env.APPDATA || path25.join(os4.homedir(), "AppData", "Local") : process.env.XDG_CACHE_HOME || path25.join(os4.homedir(), ".cache");
|
|
4310
|
+
return path25.join(base, "foss-design", "previews");
|
|
4237
4311
|
}
|
|
4238
|
-
var entryFile = (pid) =>
|
|
4312
|
+
var entryFile = (pid) => path25.join(registryDir(), `${pid}.json`);
|
|
4239
4313
|
function writeState(paths, state) {
|
|
4240
|
-
|
|
4241
|
-
|
|
4314
|
+
fs27.mkdirSync(paths.cache, { recursive: true });
|
|
4315
|
+
fs27.writeFileSync(stateFile2(paths), `${JSON.stringify(state, null, 2)}
|
|
4242
4316
|
`);
|
|
4243
4317
|
try {
|
|
4244
|
-
|
|
4245
|
-
|
|
4318
|
+
fs27.mkdirSync(registryDir(), { recursive: true });
|
|
4319
|
+
fs27.writeFileSync(entryFile(state.pid), `${JSON.stringify(state, null, 2)}
|
|
4246
4320
|
`);
|
|
4247
4321
|
} catch {
|
|
4248
4322
|
}
|
|
4249
4323
|
}
|
|
4250
4324
|
function clearState(paths, pid) {
|
|
4251
4325
|
const state = readState(paths);
|
|
4252
|
-
|
|
4326
|
+
fs27.rmSync(entryFile(pid ?? state?.pid ?? -1), { force: true });
|
|
4253
4327
|
if (pid !== void 0 && state && state.pid !== pid) return;
|
|
4254
|
-
|
|
4328
|
+
fs27.rmSync(stateFile2(paths), { force: true });
|
|
4255
4329
|
}
|
|
4256
4330
|
function listPreviews() {
|
|
4257
4331
|
let files = [];
|
|
4258
4332
|
try {
|
|
4259
|
-
files =
|
|
4333
|
+
files = fs27.readdirSync(registryDir()).filter((name) => name.endsWith(".json"));
|
|
4260
4334
|
} catch {
|
|
4261
4335
|
return [];
|
|
4262
4336
|
}
|
|
4263
4337
|
const found = [];
|
|
4264
4338
|
for (const name of files) {
|
|
4265
|
-
const file =
|
|
4339
|
+
const file = path25.join(registryDir(), name);
|
|
4266
4340
|
let state = null;
|
|
4267
4341
|
try {
|
|
4268
|
-
state = JSON.parse(
|
|
4342
|
+
state = JSON.parse(fs27.readFileSync(file, "utf8"));
|
|
4269
4343
|
} catch {
|
|
4270
4344
|
}
|
|
4271
4345
|
if (!state?.pid || !pidAlive(state.pid)) {
|
|
4272
|
-
|
|
4346
|
+
fs27.rmSync(file, { force: true });
|
|
4273
4347
|
continue;
|
|
4274
4348
|
}
|
|
4275
4349
|
let recorded = null;
|
|
4276
4350
|
try {
|
|
4277
|
-
recorded = JSON.parse(
|
|
4351
|
+
recorded = JSON.parse(fs27.readFileSync(path25.join(state.root, ".design", ".cache", "server.json"), "utf8"));
|
|
4278
4352
|
} catch {
|
|
4279
4353
|
}
|
|
4280
4354
|
found.push({ ...state, tracked: recorded?.pid === state.pid });
|
|
@@ -4288,11 +4362,11 @@ async function stopPreview(entry) {
|
|
|
4288
4362
|
}
|
|
4289
4363
|
const until = Date.now() + 5e3;
|
|
4290
4364
|
while (Date.now() < until && pidAlive(entry.pid)) await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4291
|
-
|
|
4292
|
-
const stateAt =
|
|
4365
|
+
fs27.rmSync(entryFile(entry.pid), { force: true });
|
|
4366
|
+
const stateAt = path25.join(entry.root, ".design", ".cache", "server.json");
|
|
4293
4367
|
try {
|
|
4294
|
-
if (JSON.parse(
|
|
4295
|
-
|
|
4368
|
+
if (JSON.parse(fs27.readFileSync(stateAt, "utf8")).pid === entry.pid)
|
|
4369
|
+
fs27.rmSync(stateAt, { force: true });
|
|
4296
4370
|
} catch {
|
|
4297
4371
|
}
|
|
4298
4372
|
}
|
|
@@ -4351,15 +4425,15 @@ function notFoundPage(res, title, text2) {
|
|
|
4351
4425
|
}
|
|
4352
4426
|
async function startDevServer(options) {
|
|
4353
4427
|
const paths = designPaths(options.root);
|
|
4354
|
-
if (!
|
|
4355
|
-
|
|
4428
|
+
if (!fs28.existsSync(paths.design)) throw new Error(`No .design folder in ${options.root}; run \`design init\``);
|
|
4429
|
+
fs28.mkdirSync(paths.cache, { recursive: true });
|
|
4356
4430
|
const snapshots = new SnapshotStore(paths.snapshots);
|
|
4357
4431
|
const project = new DesignProject(paths, DEV_URLS, snapshots.lookup);
|
|
4358
4432
|
const entries = new Entries(project);
|
|
4359
4433
|
entries.sync();
|
|
4360
4434
|
const links = linkShippedPackages(paths, project.appDir());
|
|
4361
4435
|
const events = new EventHub();
|
|
4362
|
-
const httpServer =
|
|
4436
|
+
const httpServer = http2.createServer();
|
|
4363
4437
|
dropStaleDepCache(paths, project.appDir());
|
|
4364
4438
|
const base = baseConfig(project);
|
|
4365
4439
|
const vite = await createServer({
|
|
@@ -4378,8 +4452,8 @@ async function startDevServer(options) {
|
|
|
4378
4452
|
// `.cache` is ours (server.log, snapshots, builds): a change there must not reload frames. The
|
|
4379
4453
|
// generated entries are modules the frames load, so those stay watched.
|
|
4380
4454
|
ignored: (file) => {
|
|
4381
|
-
const rel = toPosix(
|
|
4382
|
-
const inCache = rel !== "" && !rel.startsWith("..") && !
|
|
4455
|
+
const rel = toPosix(path26.relative(paths.cache, file));
|
|
4456
|
+
const inCache = rel !== "" && !rel.startsWith("..") && !path26.isAbsolute(rel);
|
|
4383
4457
|
return inCache && rel !== "entries" && !rel.startsWith("entries/");
|
|
4384
4458
|
}
|
|
4385
4459
|
},
|
|
@@ -4388,11 +4462,11 @@ async function startDevServer(options) {
|
|
|
4388
4462
|
forwardConsole: false
|
|
4389
4463
|
},
|
|
4390
4464
|
optimizeDeps: {
|
|
4391
|
-
entries: entries.files().map((file) => toPosix(
|
|
4465
|
+
entries: entries.files().map((file) => toPosix(path26.relative(paths.design, file)))
|
|
4392
4466
|
}
|
|
4393
4467
|
});
|
|
4394
4468
|
const publicDir = project.publicDir();
|
|
4395
|
-
const servePublic = publicDir ?
|
|
4469
|
+
const servePublic = publicDir ? sirv2(publicDir, { dev: true }) : null;
|
|
4396
4470
|
const publicRel = (encoded) => {
|
|
4397
4471
|
try {
|
|
4398
4472
|
return decodeURIComponent(encoded);
|
|
@@ -4400,16 +4474,16 @@ async function startDevServer(options) {
|
|
|
4400
4474
|
return null;
|
|
4401
4475
|
}
|
|
4402
4476
|
};
|
|
4403
|
-
const viewer =
|
|
4404
|
-
const viewerIndex =
|
|
4477
|
+
const viewer = fs28.existsSync(PKG.viewer) ? sirv2(PKG.viewer, { dev: true, etag: true }) : null;
|
|
4478
|
+
const viewerIndex = path26.join(PKG.viewer, "index.html");
|
|
4405
4479
|
const serveViewerIndex = (res) => {
|
|
4406
|
-
const html =
|
|
4480
|
+
const html = fs28.readFileSync(viewerIndex, "utf8").replace(/<head>/i, '<head>\n<base href="/">');
|
|
4407
4481
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
|
|
4408
4482
|
res.end(html);
|
|
4409
4483
|
};
|
|
4410
4484
|
const ctx = { project, events, snapshots };
|
|
4411
4485
|
const systemCssUrl = `/${relToDesign(paths, entries.systemCssFile())}`;
|
|
4412
|
-
const bootUrl = `/${relToDesign(paths,
|
|
4486
|
+
const bootUrl = `/${relToDesign(paths, path26.join(entries.dir, HTML_BOOT))}`;
|
|
4413
4487
|
const frameHead = (source, extra = {}) => {
|
|
4414
4488
|
const canvasTheme = source.canvas === SYSTEM_CANVAS ? void 0 : project.canvas(source.canvas)?.doc.theme;
|
|
4415
4489
|
return {
|
|
@@ -4430,7 +4504,7 @@ async function startDevServer(options) {
|
|
|
4430
4504
|
notFoundPage(res, "Screen not found", `No screen "${id2}" in "${canvas}". Check canvas.json.`);
|
|
4431
4505
|
return;
|
|
4432
4506
|
}
|
|
4433
|
-
if (!
|
|
4507
|
+
if (!fs28.existsSync(entries.entryFile(source))) entries.sync();
|
|
4434
4508
|
const entryUrl = `/${relToDesign(paths, entries.entryFile(source))}`;
|
|
4435
4509
|
const html = await vite.transformIndexHtml(url2.pathname + url2.search, moduleShell(frameHead(source), entryUrl));
|
|
4436
4510
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
|
|
@@ -4439,8 +4513,8 @@ async function startDevServer(options) {
|
|
|
4439
4513
|
};
|
|
4440
4514
|
const serveHtmlScreen = async (res, url2) => {
|
|
4441
4515
|
const rel = decodeURIComponent(url2.pathname.slice("/_fs/".length));
|
|
4442
|
-
const file =
|
|
4443
|
-
if (!isInside(paths.design, file) || !
|
|
4516
|
+
const file = path26.resolve(paths.design, rel);
|
|
4517
|
+
if (!isInside(paths.design, file) || !fs28.existsSync(file)) {
|
|
4444
4518
|
notFoundPage(res, "Screen not found", `No file .design/${rel}`);
|
|
4445
4519
|
return;
|
|
4446
4520
|
}
|
|
@@ -4450,13 +4524,13 @@ async function startDevServer(options) {
|
|
|
4450
4524
|
key: `${canvas}/${id2}`,
|
|
4451
4525
|
canvas,
|
|
4452
4526
|
id: id2,
|
|
4453
|
-
title:
|
|
4527
|
+
title: path26.basename(file),
|
|
4454
4528
|
file,
|
|
4455
4529
|
format: "html",
|
|
4456
4530
|
props: {},
|
|
4457
4531
|
system: true
|
|
4458
4532
|
};
|
|
4459
|
-
const raw =
|
|
4533
|
+
const raw = fs28.readFileSync(file, "utf8");
|
|
4460
4534
|
const withSystem = source.system && !optsOutOfSystem(raw);
|
|
4461
4535
|
const html = injectIntoHtml(
|
|
4462
4536
|
raw,
|
|
@@ -4489,7 +4563,7 @@ async function startDevServer(options) {
|
|
|
4489
4563
|
}
|
|
4490
4564
|
if (url2.pathname.startsWith("/_fs/")) {
|
|
4491
4565
|
const rel = publicRel(url2.pathname.slice("/_fs/".length));
|
|
4492
|
-
if (publicDir && rel !== null && !project.isPublicFile(rel) &&
|
|
4566
|
+
if (publicDir && rel !== null && !project.isPublicFile(rel) && fs28.existsSync(path26.join(publicDir, rel))) {
|
|
4493
4567
|
notFoundPage(res, "Not found", url2.pathname);
|
|
4494
4568
|
return;
|
|
4495
4569
|
}
|
|
@@ -4501,7 +4575,7 @@ async function startDevServer(options) {
|
|
|
4501
4575
|
const rel = publicRel(url2.pathname.slice(1));
|
|
4502
4576
|
if (servePublic && rel !== null && project.isPublicFile(rel))
|
|
4503
4577
|
servePublic(req, res, () => notFoundPage(res, "Not found", url2.pathname));
|
|
4504
|
-
else if (
|
|
4578
|
+
else if (path26.extname(url2.pathname)) notFoundPage(res, "Not found", url2.pathname);
|
|
4505
4579
|
else serveViewerIndex(res);
|
|
4506
4580
|
};
|
|
4507
4581
|
if (url2.pathname === "/" || url2.pathname === "/index.html") serveViewerIndex(res);
|
|
@@ -4529,7 +4603,7 @@ async function startDevServer(options) {
|
|
|
4529
4603
|
};
|
|
4530
4604
|
vite.watcher.on("all", (event, file) => {
|
|
4531
4605
|
if (publicDir && isInside(publicDir, file)) project.invalidatePublic();
|
|
4532
|
-
const rel = toPosix(
|
|
4606
|
+
const rel = toPosix(path26.relative(paths.design, file));
|
|
4533
4607
|
if (rel.startsWith("..") || rel.startsWith(".cache/") || rel.startsWith("node_modules/")) return;
|
|
4534
4608
|
if (rel === "design.json") {
|
|
4535
4609
|
queue("config", () => {
|
|
@@ -4640,8 +4714,8 @@ async function ensureServer(paths, options) {
|
|
|
4640
4714
|
const recorded = readState(paths);
|
|
4641
4715
|
if (recorded && pidAlive(recorded.pid)) await stopPreview(recorded);
|
|
4642
4716
|
for (const other of listPreviews()) if (other.root === paths.root) await stopPreview(other);
|
|
4643
|
-
|
|
4644
|
-
const log =
|
|
4717
|
+
fs29.mkdirSync(paths.cache, { recursive: true });
|
|
4718
|
+
const log = fs29.openSync(logFile(paths), "w");
|
|
4645
4719
|
const args = [PKG.cli, "preview", "--foreground", "--root", paths.root];
|
|
4646
4720
|
if (options.port) args.push("--port", String(options.port));
|
|
4647
4721
|
const child = spawn(process.execPath, args, {
|
|
@@ -4653,7 +4727,7 @@ async function ensureServer(paths, options) {
|
|
|
4653
4727
|
child.unref();
|
|
4654
4728
|
const state = await waitForServer(paths, 4e4);
|
|
4655
4729
|
if (!state) {
|
|
4656
|
-
const tail =
|
|
4730
|
+
const tail = fs29.existsSync(logFile(paths)) ? fs29.readFileSync(logFile(paths), "utf8").split("\n").slice(-25) : [];
|
|
4657
4731
|
throw new CliError(`The preview server did not start.
|
|
4658
4732
|
${tail.join("\n")}`);
|
|
4659
4733
|
}
|
|
@@ -5035,11 +5109,8 @@ async function runMe(paths, options) {
|
|
|
5035
5109
|
}
|
|
5036
5110
|
|
|
5037
5111
|
// src/cli/commands/check.ts
|
|
5038
|
-
import fs29 from "fs";
|
|
5039
|
-
import http2 from "http";
|
|
5040
|
-
import path26 from "path";
|
|
5041
|
-
import sirv2 from "sirv";
|
|
5042
5112
|
var plural2 = (count, word, many = `${word}s`) => `${count} ${count === 1 ? word : many}`;
|
|
5113
|
+
var SLOW_MS = 3e3;
|
|
5043
5114
|
function printIssues(issues) {
|
|
5044
5115
|
const byFile = /* @__PURE__ */ new Map();
|
|
5045
5116
|
for (const issue of issues) byFile.set(issue.file, [...byFile.get(issue.file) ?? [], issue]);
|
|
@@ -5072,7 +5143,15 @@ async function renderFrames(jobs) {
|
|
|
5072
5143
|
await page.close().catch(() => {
|
|
5073
5144
|
});
|
|
5074
5145
|
}
|
|
5075
|
-
return {
|
|
5146
|
+
return {
|
|
5147
|
+
key: job.key,
|
|
5148
|
+
url: job.url,
|
|
5149
|
+
ready: report.ready,
|
|
5150
|
+
held: report.held,
|
|
5151
|
+
readyMs: report.readyMs,
|
|
5152
|
+
errors: report.errors,
|
|
5153
|
+
links
|
|
5154
|
+
};
|
|
5076
5155
|
});
|
|
5077
5156
|
} finally {
|
|
5078
5157
|
await browser.close();
|
|
@@ -5117,42 +5196,15 @@ async function runCheck(paths, targets, options) {
|
|
|
5117
5196
|
let close = async () => {
|
|
5118
5197
|
};
|
|
5119
5198
|
if (options.built) {
|
|
5120
|
-
const out = path26.join(paths.cache, "check-build");
|
|
5121
|
-
fs29.rmSync(out, { recursive: true, force: true });
|
|
5122
5199
|
if (!options.json) print(dim("Building the screens as design push does\u2026"));
|
|
5123
|
-
await
|
|
5200
|
+
const build2 = await serveBuild(paths, "check-build", {
|
|
5124
5201
|
canvases: canvases.length ? canvases : void 0,
|
|
5125
|
-
includeSystem: !canvases.length
|
|
5126
|
-
includeViewer: false
|
|
5127
|
-
});
|
|
5128
|
-
const read = (rel) => {
|
|
5129
|
-
try {
|
|
5130
|
-
return JSON.parse(fs29.readFileSync(path26.join(out, rel), "utf8"));
|
|
5131
|
-
} catch {
|
|
5132
|
-
return null;
|
|
5133
|
-
}
|
|
5134
|
-
};
|
|
5135
|
-
docOf = (id2) => read(`api/canvas/${id2}.json`) ?? project.canvas(id2).doc;
|
|
5136
|
-
systemDoc = canvases.length ? null : read("api/system.json");
|
|
5137
|
-
const files = sirv2(out, { dev: true });
|
|
5138
|
-
const server = http2.createServer((req, res) => {
|
|
5139
|
-
const url = new URL(req.url ?? "/", "http://localhost");
|
|
5140
|
-
const segments = url.pathname.split("/").map((segment) => {
|
|
5141
|
-
try {
|
|
5142
|
-
return decodeURIComponent(segment);
|
|
5143
|
-
} catch {
|
|
5144
|
-
return segment;
|
|
5145
|
-
}
|
|
5146
|
-
});
|
|
5147
|
-
req.url = segments.join("/") + url.search;
|
|
5148
|
-
files(req, res, () => {
|
|
5149
|
-
res.statusCode = 404;
|
|
5150
|
-
res.end("Not found");
|
|
5151
|
-
});
|
|
5202
|
+
includeSystem: !canvases.length
|
|
5152
5203
|
});
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5204
|
+
docOf = (id2) => build2.read(`api/canvas/${id2}.json`) ?? project.canvas(id2).doc;
|
|
5205
|
+
systemDoc = canvases.length ? null : build2.read("api/system.json");
|
|
5206
|
+
base = build2.base;
|
|
5207
|
+
close = build2.close;
|
|
5156
5208
|
if (!options.json) print(dim(`Serving the build at ${base}`));
|
|
5157
5209
|
} else {
|
|
5158
5210
|
const wasRunning = await liveServer(paths);
|
|
@@ -5192,7 +5244,16 @@ async function runCheck(paths, targets, options) {
|
|
|
5192
5244
|
if (frame.held) print(` ${red(`holdReady() was not released within 20s (${frame.held} still held)`)}`);
|
|
5193
5245
|
else if (!frame.ready) print(` ${red("did not finish rendering (no ready signal within 20s)")}`);
|
|
5194
5246
|
for (const error of frame.errors) print(` ${red(error.split("\n")[0])}`);
|
|
5247
|
+
if (frame.readyMs !== void 0 && frame.readyMs > SLOW_MS)
|
|
5248
|
+
print(` ${yellow(`slow: ${(frame.readyMs / 1e3).toFixed(1)}s to become ready`)}`);
|
|
5195
5249
|
}
|
|
5250
|
+
const slow = frames.filter((frame) => (frame.readyMs ?? 0) > SLOW_MS);
|
|
5251
|
+
if (slow.length)
|
|
5252
|
+
print(
|
|
5253
|
+
dim(
|
|
5254
|
+
`${plural2(slow.length, "screen")} took over ${SLOW_MS / 1e3}s to become ready; every frame of them costs that in the viewer, check and push snapshots. See what they wait for (retries, timers, polling, entrance gates).`
|
|
5255
|
+
)
|
|
5256
|
+
);
|
|
5196
5257
|
}
|
|
5197
5258
|
if (linkIssues.length) printLinkIssues(linkIssues, project);
|
|
5198
5259
|
const screens = [...picked.values()].reduce((sum, set) => sum + (set?.size ?? 0), 0);
|
|
@@ -5266,13 +5327,13 @@ function printLinkIssues(issues, project) {
|
|
|
5266
5327
|
|
|
5267
5328
|
// src/cli/commands/cloud.ts
|
|
5268
5329
|
import fs30 from "fs";
|
|
5269
|
-
import
|
|
5330
|
+
import os5 from "os";
|
|
5270
5331
|
import path27 from "path";
|
|
5271
5332
|
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
5272
5333
|
async function runLogin() {
|
|
5273
5334
|
const host = cloudHost();
|
|
5274
5335
|
const client = new CloudClient(host, null);
|
|
5275
|
-
const code = await client.deviceCode(
|
|
5336
|
+
const code = await client.deviceCode(os5.hostname());
|
|
5276
5337
|
const url = new URL(code.verification_uri_complete || code.verification_uri, host).toString();
|
|
5277
5338
|
const expiresIn = code.expires_in ?? 600;
|
|
5278
5339
|
print(`${bold("Sign in to foss-design Cloud")} ${dim(host)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foss-design",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"description": "Design canvases and design systems for coding agents: live, animated, clickable screens on an infinite canvas, served locally from a .design folder.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|