@format.dev/cli 0.1.1 → 0.1.2
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/index.mjs +157 -7
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -250,7 +250,7 @@ var cli_spec_default = { commands: [
|
|
|
250
250
|
|
|
251
251
|
//#endregion
|
|
252
252
|
//#region src/versioning/js-version.ts
|
|
253
|
-
const jsVersion = "0.1.
|
|
253
|
+
const jsVersion = "0.1.2";
|
|
254
254
|
|
|
255
255
|
//#endregion
|
|
256
256
|
//#region src/versioning/registry.ts
|
|
@@ -272,11 +272,43 @@ async function fetchLatestVersion() {
|
|
|
272
272
|
return data.version;
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/versioning/changelog.ts
|
|
277
|
+
const CHANGELOG_URL = "https://format.dev/changelog";
|
|
278
|
+
const CHANGELOG_INDEX_URL = process.env.FMT_CHANGELOG_INDEX_URL ?? "https://format.dev/changelog.json";
|
|
279
|
+
const FETCH_TIMEOUT_MS = 3e3;
|
|
280
|
+
function changelogEntriesBetween(args) {
|
|
281
|
+
const { entries, fromVersion, toVersion } = args;
|
|
282
|
+
return entries.filter((entry) => isNewer(entry.version, fromVersion) && !isNewer(entry.version, toVersion)).sort((a, b) => isNewer(a.version, b.version) ? 1 : -1);
|
|
283
|
+
}
|
|
284
|
+
async function fetchChangelogIndex() {
|
|
285
|
+
const controller = new AbortController();
|
|
286
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
287
|
+
try {
|
|
288
|
+
const response = await fetch(CHANGELOG_INDEX_URL, { signal: controller.signal });
|
|
289
|
+
if (!response.ok) throw new Error(`Changelog index returned ${response.status}`);
|
|
290
|
+
return (await response.json()).entries;
|
|
291
|
+
} finally {
|
|
292
|
+
clearTimeout(timeout);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async function changelogLines(args) {
|
|
296
|
+
const { fromVersion, toVersion, fetchEntries = fetchChangelogIndex } = args;
|
|
297
|
+
const entries = await fetchEntries().catch(() => null);
|
|
298
|
+
if (!entries) return [`See what's changed: ${CHANGELOG_URL}`];
|
|
299
|
+
const inRange = changelogEntriesBetween({
|
|
300
|
+
entries,
|
|
301
|
+
fromVersion,
|
|
302
|
+
toVersion
|
|
303
|
+
});
|
|
304
|
+
if (inRange.length === 0) return [];
|
|
305
|
+
return [`What's changed since ${fromVersion}:`, ...inRange.map((entry) => ` ${entry.version} ${entry.url}`)];
|
|
306
|
+
}
|
|
307
|
+
|
|
275
308
|
//#endregion
|
|
276
309
|
//#region src/versioning/update-check.ts
|
|
277
310
|
const CACHE_FILE_NAME = "update-check.json";
|
|
278
311
|
const CACHE_MAX_AGE_MS = 1440 * 60 * 1e3;
|
|
279
|
-
const CHANGELOG_URL = "https://format.dev/changelog";
|
|
280
312
|
function cacheDir() {
|
|
281
313
|
const home = os.homedir();
|
|
282
314
|
if (process.env.XDG_CACHE_HOME) return join(process.env.XDG_CACHE_HOME, "format");
|
|
@@ -599,11 +631,19 @@ function installArgs(packageManager, packages, dev) {
|
|
|
599
631
|
switch (packageManager) {
|
|
600
632
|
case "npm": return [
|
|
601
633
|
"install",
|
|
634
|
+
"--save-exact",
|
|
602
635
|
...dev ? ["--save-dev"] : [],
|
|
603
636
|
...packages
|
|
604
637
|
];
|
|
638
|
+
case "pnpm": return [
|
|
639
|
+
"add",
|
|
640
|
+
"--save-exact",
|
|
641
|
+
...saveFlag ? [saveFlag] : [],
|
|
642
|
+
...packages
|
|
643
|
+
];
|
|
605
644
|
default: return [
|
|
606
645
|
"add",
|
|
646
|
+
"--exact",
|
|
607
647
|
...saveFlag ? [saveFlag] : [],
|
|
608
648
|
...packages
|
|
609
649
|
];
|
|
@@ -1360,6 +1400,94 @@ async function newProjectCommand(cwd, providedDirectory, options) {
|
|
|
1360
1400
|
}
|
|
1361
1401
|
}
|
|
1362
1402
|
|
|
1403
|
+
//#endregion
|
|
1404
|
+
//#region src/versioning/cli-package.ts
|
|
1405
|
+
const CLI_PACKAGE_NAME$1 = "@format.dev/cli";
|
|
1406
|
+
async function readCliPackageVersion() {
|
|
1407
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
1408
|
+
for (;;) {
|
|
1409
|
+
const candidate = join(dir, "package.json");
|
|
1410
|
+
if (existsSync(candidate)) {
|
|
1411
|
+
const manifest = JSON.parse(await fs.readFile(candidate, "utf8"));
|
|
1412
|
+
if (manifest.name === CLI_PACKAGE_NAME$1) return manifest.version;
|
|
1413
|
+
}
|
|
1414
|
+
const parent = dirname(dir);
|
|
1415
|
+
if (parent === dir) throw new Error(`Could not find the ${CLI_PACKAGE_NAME$1} package.json above ${fileURLToPath(import.meta.url)}`);
|
|
1416
|
+
dir = parent;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
//#endregion
|
|
1421
|
+
//#region src/versioning/handoff.ts
|
|
1422
|
+
const HANDOFF_ENV = "FMT_UPDATE_HANDOFF";
|
|
1423
|
+
const CLI_PACKAGE_NAME = "@format.dev/cli";
|
|
1424
|
+
function readHandoffFromVersion() {
|
|
1425
|
+
return process.env[HANDOFF_ENV] ?? null;
|
|
1426
|
+
}
|
|
1427
|
+
function environmentWithoutHandoff() {
|
|
1428
|
+
const { [HANDOFF_ENV]: _handoff, ...environment } = process.env;
|
|
1429
|
+
return environment;
|
|
1430
|
+
}
|
|
1431
|
+
async function handOffUpdate(args) {
|
|
1432
|
+
const { targetVersion, fromVersion, cwd } = args;
|
|
1433
|
+
const commandArgs = [
|
|
1434
|
+
"exec",
|
|
1435
|
+
"--yes",
|
|
1436
|
+
`--package=${CLI_PACKAGE_NAME}@${targetVersion}`,
|
|
1437
|
+
"--",
|
|
1438
|
+
"format",
|
|
1439
|
+
"update",
|
|
1440
|
+
targetVersion
|
|
1441
|
+
];
|
|
1442
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
1443
|
+
const child = spawn("npm", commandArgs, {
|
|
1444
|
+
cwd,
|
|
1445
|
+
stdio: "inherit",
|
|
1446
|
+
shell: process.platform === "win32",
|
|
1447
|
+
env: {
|
|
1448
|
+
...process.env,
|
|
1449
|
+
[HANDOFF_ENV]: fromVersion ?? "none"
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
child.on("error", (error) => {
|
|
1453
|
+
rejectPromise(new CliError(`Failed to run the Format ${targetVersion} CLI: ${error.message}`));
|
|
1454
|
+
});
|
|
1455
|
+
child.on("exit", (code) => {
|
|
1456
|
+
resolvePromise(code ?? 1);
|
|
1457
|
+
});
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
//#endregion
|
|
1462
|
+
//#region src/versioning/fresh-install.ts
|
|
1463
|
+
const FORMAT_SCOPE = "@format.dev/";
|
|
1464
|
+
const DEPENDENCY_SECTIONS = [
|
|
1465
|
+
"dependencies",
|
|
1466
|
+
"devDependencies",
|
|
1467
|
+
"optionalDependencies"
|
|
1468
|
+
];
|
|
1469
|
+
function withoutFormatDependencies(manifest) {
|
|
1470
|
+
const copy = { ...manifest };
|
|
1471
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
1472
|
+
const entries = manifest[section];
|
|
1473
|
+
if (!entries) continue;
|
|
1474
|
+
copy[section] = Object.fromEntries(Object.entries(entries).filter(([name, version]) => !name.startsWith(FORMAT_SCOPE) || version.startsWith("workspace:")));
|
|
1475
|
+
}
|
|
1476
|
+
return copy;
|
|
1477
|
+
}
|
|
1478
|
+
async function installWithFreshFormatTree(args) {
|
|
1479
|
+
const { packageJsonPath, install } = args;
|
|
1480
|
+
const targetSource = await fs.readFile(packageJsonPath, "utf8");
|
|
1481
|
+
const withoutFormat = withoutFormatDependencies(JSON.parse(targetSource));
|
|
1482
|
+
try {
|
|
1483
|
+
await fs.writeFile(packageJsonPath, `${JSON.stringify(withoutFormat, null, " ")}\n`, "utf8");
|
|
1484
|
+
await install();
|
|
1485
|
+
} finally {
|
|
1486
|
+
await fs.writeFile(packageJsonPath, targetSource, "utf8");
|
|
1487
|
+
}
|
|
1488
|
+
await install();
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1363
1491
|
//#endregion
|
|
1364
1492
|
//#region src/cli/commands/update.ts
|
|
1365
1493
|
async function updateCommand(cwd, target) {
|
|
@@ -1368,6 +1496,13 @@ async function updateCommand(cwd, target) {
|
|
|
1368
1496
|
target,
|
|
1369
1497
|
pinnedVersion: state.pinnedVersion
|
|
1370
1498
|
});
|
|
1499
|
+
const cliVersion = await readCliPackageVersion();
|
|
1500
|
+
const handoffFromVersion = readHandoffFromVersion();
|
|
1501
|
+
if (handoffFromVersion === null && cliVersion !== targetVersion) return handOffUpdate({
|
|
1502
|
+
targetVersion,
|
|
1503
|
+
fromVersion: state.pinnedVersion,
|
|
1504
|
+
cwd: state.projectDir
|
|
1505
|
+
});
|
|
1371
1506
|
if (targetVersion !== state.pinnedVersion) {
|
|
1372
1507
|
await writeConfigVersion({
|
|
1373
1508
|
filepath: state.configFile.filepath,
|
|
@@ -1392,13 +1527,27 @@ async function updateCommand(cwd, target) {
|
|
|
1392
1527
|
const { command, args } = installCommand(packageManager);
|
|
1393
1528
|
console.log("");
|
|
1394
1529
|
console.log(`Installing with ${packageManager}...`);
|
|
1395
|
-
await
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1530
|
+
await installWithFreshFormatTree({
|
|
1531
|
+
packageJsonPath: state.packageJsonPath,
|
|
1532
|
+
install: () => runInstall({
|
|
1533
|
+
command,
|
|
1534
|
+
args,
|
|
1535
|
+
cwd: state.projectDir
|
|
1536
|
+
})
|
|
1399
1537
|
});
|
|
1400
1538
|
console.log("");
|
|
1401
1539
|
console.log(`Done. Your project is on Format ${targetVersion}.`);
|
|
1540
|
+
const fromVersion = handoffFromVersion ?? state.pinnedVersion;
|
|
1541
|
+
if (fromVersion !== null && fromVersion !== "none" && fromVersion !== targetVersion) {
|
|
1542
|
+
const lines = await changelogLines({
|
|
1543
|
+
fromVersion,
|
|
1544
|
+
toVersion: targetVersion
|
|
1545
|
+
});
|
|
1546
|
+
if (lines.length > 0) {
|
|
1547
|
+
console.log("");
|
|
1548
|
+
lines.forEach((line) => console.log(line));
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1402
1551
|
return 0;
|
|
1403
1552
|
}
|
|
1404
1553
|
async function resolveTargetVersion(args) {
|
|
@@ -1417,7 +1566,8 @@ async function runInstall(args) {
|
|
|
1417
1566
|
const child = spawn(command, commandArgs, {
|
|
1418
1567
|
cwd,
|
|
1419
1568
|
stdio: "inherit",
|
|
1420
|
-
shell: process.platform === "win32"
|
|
1569
|
+
shell: process.platform === "win32",
|
|
1570
|
+
env: environmentWithoutHandoff()
|
|
1421
1571
|
});
|
|
1422
1572
|
child.on("error", (error) => {
|
|
1423
1573
|
rejectPromise(new CliError(`Failed to run ${command} install: ${error.message}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@format.dev/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "The Format CLI: run Studio, compile documents, and keep Format packages in lockstep",
|
|
6
6
|
"author": "the format.dev developers",
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"verdaccio": "^6.8.0",
|
|
73
73
|
"vitest": "^4.0.18",
|
|
74
74
|
"vue": "^3.5.25",
|
|
75
|
-
"@format.dev/vue": "0.1.
|
|
76
|
-
"@format.dev/react": "0.1.
|
|
75
|
+
"@format.dev/vue": "0.1.2",
|
|
76
|
+
"@format.dev/react": "0.1.2"
|
|
77
77
|
},
|
|
78
78
|
"engines": {
|
|
79
79
|
"node": ">=22.12.0"
|