@venn-lang/cli 0.1.2 → 0.1.3
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.mjs +56 -39
- package/package.json +12 -12
- package/src/commands/upgrade.ts +5 -1
- package/src/upgrade/install-site.ts +38 -15
package/dist/cli.mjs
CHANGED
|
@@ -1455,33 +1455,26 @@ function readBare(cur) {
|
|
|
1455
1455
|
return raw !== "" && !Number.isNaN(num) ? num : raw;
|
|
1456
1456
|
}
|
|
1457
1457
|
/**
|
|
1458
|
-
*
|
|
1458
|
+
* An empty table for a manifest to fill.
|
|
1459
1459
|
*
|
|
1460
|
-
*
|
|
1461
|
-
*
|
|
1462
|
-
*
|
|
1460
|
+
* Made without a prototype, so a key read from the file is a key and nothing
|
|
1461
|
+
* else. Assigning `__proto__` onto an ordinary object changes how every object
|
|
1462
|
+
* in the process behaves, and a manifest arrives with somebody else's project:
|
|
1463
|
+
* a `venn.toml` holding `[package.__proto__]` was enough to put a property on
|
|
1463
1464
|
* `Object.prototype`.
|
|
1464
|
-
*/
|
|
1465
|
-
const RESERVED$1 = /* @__PURE__ */ new Set([
|
|
1466
|
-
"__proto__",
|
|
1467
|
-
"constructor",
|
|
1468
|
-
"prototype"
|
|
1469
|
-
]);
|
|
1470
|
-
/**
|
|
1471
|
-
* Whether a key read from a manifest may be assigned.
|
|
1472
1465
|
*
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1475
|
-
*
|
|
1466
|
+
* Refusing that one name would have closed that one door. Having no prototype
|
|
1467
|
+
* to reach removes the question, and the table is read with `Object.keys` and
|
|
1468
|
+
* `Object.entries`, which do not care that it has none.
|
|
1476
1469
|
*/
|
|
1477
|
-
function
|
|
1478
|
-
return
|
|
1470
|
+
function emptyTable() {
|
|
1471
|
+
return Object.create(null);
|
|
1479
1472
|
}
|
|
1480
1473
|
/** The table `[path]` names, created along the way if it is not there yet. */
|
|
1481
1474
|
function enterSection(root, path) {
|
|
1482
1475
|
let node = root;
|
|
1483
1476
|
for (const key of keysOf$1(path)) {
|
|
1484
|
-
node[key] ??=
|
|
1477
|
+
node[key] ??= emptyTable();
|
|
1485
1478
|
node = node[key];
|
|
1486
1479
|
}
|
|
1487
1480
|
return node;
|
|
@@ -1499,12 +1492,12 @@ function enterTableArray(root, path) {
|
|
|
1499
1492
|
const parent = enterSection(root, keys.join("."));
|
|
1500
1493
|
const found = Array.isArray(parent[last]) ? parent[last] : [];
|
|
1501
1494
|
parent[last] = found;
|
|
1502
|
-
const table =
|
|
1495
|
+
const table = emptyTable();
|
|
1503
1496
|
found.push(table);
|
|
1504
1497
|
return table;
|
|
1505
1498
|
}
|
|
1506
1499
|
function keysOf$1(path) {
|
|
1507
|
-
return path.split(".").map((part) => part.trim().replace(/^["']|["']$/g, "")).filter((part) => part !== ""
|
|
1500
|
+
return path.split(".").map((part) => part.trim().replace(/^["']|["']$/g, "")).filter((part) => part !== "");
|
|
1508
1501
|
}
|
|
1509
1502
|
/**
|
|
1510
1503
|
* A TOML reader for `venn.toml`: sections, nested `[a.b]`, arrays of tables
|
|
@@ -1517,7 +1510,7 @@ function keysOf$1(path) {
|
|
|
1517
1510
|
* @returns the root table. Malformed lines are skipped, never thrown on.
|
|
1518
1511
|
*/
|
|
1519
1512
|
function parseToml(content) {
|
|
1520
|
-
const root =
|
|
1513
|
+
const root = emptyTable();
|
|
1521
1514
|
let section = root;
|
|
1522
1515
|
for (const raw of content.split(/\r?\n/)) {
|
|
1523
1516
|
const line = stripComment(raw).trim();
|
|
@@ -1545,7 +1538,6 @@ function assign(section, line) {
|
|
|
1545
1538
|
const eq = line.indexOf("=");
|
|
1546
1539
|
if (eq < 0) return;
|
|
1547
1540
|
const key = line.slice(0, eq).trim().replace(/^["']|["']$/g, "");
|
|
1548
|
-
if (!isSafeKey(key)) return;
|
|
1549
1541
|
section[key] = readValue(cursor(line.slice(eq + 1)));
|
|
1550
1542
|
}
|
|
1551
1543
|
/**
|
|
@@ -1651,7 +1643,7 @@ function join$2(...parts) {
|
|
|
1651
1643
|
* directory it was written against; stopping short of it would leave a walk up
|
|
1652
1644
|
* from `packages/api` never reaching the workspace root beside it.
|
|
1653
1645
|
*/
|
|
1654
|
-
function parentOf(path) {
|
|
1646
|
+
function parentOf$1(path) {
|
|
1655
1647
|
const flat = normalise$1(path);
|
|
1656
1648
|
if (flat === "" || flat === "/") return void 0;
|
|
1657
1649
|
const slash = flat.lastIndexOf("/");
|
|
@@ -1666,7 +1658,7 @@ function parentOf(path) {
|
|
|
1666
1658
|
*/
|
|
1667
1659
|
function ancestors(path) {
|
|
1668
1660
|
const found = [];
|
|
1669
|
-
for (let at = normalise$1(path); at !== void 0; at = parentOf(at)) found.push(at);
|
|
1661
|
+
for (let at = normalise$1(path); at !== void 0; at = parentOf$1(at)) found.push(at);
|
|
1670
1662
|
return found;
|
|
1671
1663
|
}
|
|
1672
1664
|
/** Whether `path` is `base` or sits inside it. */
|
|
@@ -55693,9 +55685,12 @@ function crashed(error) {
|
|
|
55693
55685
|
//#region src/upgrade/install-site.ts
|
|
55694
55686
|
/**
|
|
55695
55687
|
* Where each manager keeps what it installed for the user rather than for a
|
|
55696
|
-
* project. A copy
|
|
55697
|
-
*
|
|
55698
|
-
*
|
|
55688
|
+
* project. A copy under one of these is global wherever it was invoked from,
|
|
55689
|
+
* which matters because several live under the home directory: running from `~`
|
|
55690
|
+
* would otherwise make a global install look like a project's.
|
|
55691
|
+
*
|
|
55692
|
+
* The list cannot be complete on its own, since npm follows wherever a version
|
|
55693
|
+
* manager puts node. {@link isBesideNode} covers what it cannot name.
|
|
55699
55694
|
*/
|
|
55700
55695
|
const GLOBAL_ROOTS = [
|
|
55701
55696
|
"/pnpm/global/",
|
|
@@ -55713,13 +55708,11 @@ const GLOBAL_ROOTS = [
|
|
|
55713
55708
|
* running, and is empty when the binary is invoked directly, which is every
|
|
55714
55709
|
* time anyone actually uses it.
|
|
55715
55710
|
*
|
|
55716
|
-
* Each manager keeps its global root somewhere unmistakable, so the path is
|
|
55717
|
-
* enough to tell them apart on all three operating systems.
|
|
55718
|
-
*
|
|
55719
55711
|
* @param path Where the running CLI lives. Must be a plain path: pass
|
|
55720
55712
|
* `fileURLToPath(import.meta.url)`, since a `file:///` URL would be read as
|
|
55721
55713
|
* living outside every project.
|
|
55722
55714
|
* @param cwd The directory the user invoked it from.
|
|
55715
|
+
* @param nodePath The node binary running this, from `process.execPath`.
|
|
55723
55716
|
* @returns The manager and whether the install is global, or `unknown` when the
|
|
55724
55717
|
* path matches nothing recognisable.
|
|
55725
55718
|
*/
|
|
@@ -55732,7 +55725,11 @@ function installSiteOf(args) {
|
|
|
55732
55725
|
};
|
|
55733
55726
|
return {
|
|
55734
55727
|
manager,
|
|
55735
|
-
global: isGlobal(
|
|
55728
|
+
global: isGlobal({
|
|
55729
|
+
path,
|
|
55730
|
+
cwd: normalise(args.cwd),
|
|
55731
|
+
node: normalise(args.nodePath)
|
|
55732
|
+
})
|
|
55736
55733
|
};
|
|
55737
55734
|
}
|
|
55738
55735
|
function normalise(path) {
|
|
@@ -55746,19 +55743,38 @@ function managerOf(path) {
|
|
|
55746
55743
|
}
|
|
55747
55744
|
/**
|
|
55748
55745
|
* A copy under the working directory belongs to that project, not to the user,
|
|
55749
|
-
* unless it sits
|
|
55746
|
+
* unless it sits somewhere only a global install can be.
|
|
55750
55747
|
*
|
|
55751
55748
|
* Upgrading a copy the project owns would move a version its manifest still
|
|
55752
55749
|
* pins, so the next install would put the old one back and the user would be
|
|
55753
55750
|
* left wondering which of the two is running.
|
|
55754
55751
|
*/
|
|
55755
|
-
function isGlobal(
|
|
55756
|
-
if (
|
|
55757
|
-
|
|
55752
|
+
function isGlobal(args) {
|
|
55753
|
+
if (GLOBAL_ROOTS.some((root) => args.path.includes(root))) return true;
|
|
55754
|
+
if (args.path.includes("/yarn/") && args.path.includes("/global/")) return true;
|
|
55755
|
+
if (isBesideNode(args.path, args.node)) return true;
|
|
55756
|
+
return !args.path.startsWith(`${args.cwd}/`);
|
|
55758
55757
|
}
|
|
55759
|
-
|
|
55760
|
-
|
|
55761
|
-
|
|
55758
|
+
/**
|
|
55759
|
+
* npm keeps its global packages beside the node binary: alongside it on
|
|
55760
|
+
* Windows, under a sibling `lib` elsewhere.
|
|
55761
|
+
*
|
|
55762
|
+
* Asked of node rather than matched against a list of directory names, because
|
|
55763
|
+
* a version manager puts node wherever it likes and the global root follows it
|
|
55764
|
+
* there. `C:\nvm4w\nodejs` is a link to `AppData\Local\nvm\v24.17.0`, which
|
|
55765
|
+
* node resolves before anything here sees it, and no list would have had that
|
|
55766
|
+
* name in it. The upgrade then read every global install as a project's and
|
|
55767
|
+
* refused to touch it.
|
|
55768
|
+
*/
|
|
55769
|
+
function isBesideNode(path, node) {
|
|
55770
|
+
const directory = parentOf(node);
|
|
55771
|
+
if (directory === "") return false;
|
|
55772
|
+
if (path.startsWith(`${directory}/node_modules/`)) return true;
|
|
55773
|
+
return path.startsWith(`${parentOf(directory)}/lib/node_modules/`);
|
|
55774
|
+
}
|
|
55775
|
+
function parentOf(path) {
|
|
55776
|
+
const at = path.lastIndexOf("/");
|
|
55777
|
+
return at === -1 ? "" : path.slice(0, at);
|
|
55762
55778
|
}
|
|
55763
55779
|
//#endregion
|
|
55764
55780
|
//#region src/upgrade/upgrade-plan.ts
|
|
@@ -55919,7 +55935,8 @@ async function upgradeCommand(args) {
|
|
|
55919
55935
|
function currentSite() {
|
|
55920
55936
|
return installSiteOf({
|
|
55921
55937
|
path: fileURLToPath(import.meta.url),
|
|
55922
|
-
cwd: process.cwd()
|
|
55938
|
+
cwd: process.cwd(),
|
|
55939
|
+
nodePath: process.execPath
|
|
55923
55940
|
});
|
|
55924
55941
|
}
|
|
55925
55942
|
function fail(message) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@venn-lang/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The venn binary: one command for starting, checking, running and testing a Venn project.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"venn",
|
|
@@ -41,17 +41,17 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"citty": "^0.2.2",
|
|
44
|
-
"@venn-lang/assert": "0.1.
|
|
45
|
-
"@venn-lang/contracts": "0.1.
|
|
46
|
-
"@venn-lang/
|
|
47
|
-
"@venn-lang/
|
|
48
|
-
"@venn-lang/http": "0.1.
|
|
49
|
-
"@venn-lang/
|
|
50
|
-
"@venn-lang/
|
|
51
|
-
"@venn-lang/
|
|
52
|
-
"@venn-lang/
|
|
53
|
-
"@venn-lang/
|
|
54
|
-
"@venn-lang/
|
|
44
|
+
"@venn-lang/assert": "0.1.3",
|
|
45
|
+
"@venn-lang/contracts": "0.1.3",
|
|
46
|
+
"@venn-lang/dts": "0.1.3",
|
|
47
|
+
"@venn-lang/core": "0.1.3",
|
|
48
|
+
"@venn-lang/http": "0.1.3",
|
|
49
|
+
"@venn-lang/project": "0.1.3",
|
|
50
|
+
"@venn-lang/runtime": "0.1.3",
|
|
51
|
+
"@venn-lang/sdk": "0.1.3",
|
|
52
|
+
"@venn-lang/stdlib": "0.1.3",
|
|
53
|
+
"@venn-lang/types": "0.1.3",
|
|
54
|
+
"@venn-lang/io": "0.1.3"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^24",
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -40,7 +40,11 @@ export async function upgradeCommand(args: {
|
|
|
40
40
|
* every install inside a project look global.
|
|
41
41
|
*/
|
|
42
42
|
function currentSite(): InstallSite {
|
|
43
|
-
return installSiteOf({
|
|
43
|
+
return installSiteOf({
|
|
44
|
+
path: fileURLToPath(import.meta.url),
|
|
45
|
+
cwd: process.cwd(),
|
|
46
|
+
nodePath: process.execPath,
|
|
47
|
+
});
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
function fail(message: string): number {
|
|
@@ -2,9 +2,12 @@ import type { InstallSite, PackageManager } from "./upgrade.types.js";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Where each manager keeps what it installed for the user rather than for a
|
|
5
|
-
* project. A copy
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* project. A copy under one of these is global wherever it was invoked from,
|
|
6
|
+
* which matters because several live under the home directory: running from `~`
|
|
7
|
+
* would otherwise make a global install look like a project's.
|
|
8
|
+
*
|
|
9
|
+
* The list cannot be complete on its own, since npm follows wherever a version
|
|
10
|
+
* manager puts node. {@link isBesideNode} covers what it cannot name.
|
|
8
11
|
*/
|
|
9
12
|
const GLOBAL_ROOTS: readonly string[] = [
|
|
10
13
|
"/pnpm/global/",
|
|
@@ -23,21 +26,21 @@ const GLOBAL_ROOTS: readonly string[] = [
|
|
|
23
26
|
* running, and is empty when the binary is invoked directly, which is every
|
|
24
27
|
* time anyone actually uses it.
|
|
25
28
|
*
|
|
26
|
-
* Each manager keeps its global root somewhere unmistakable, so the path is
|
|
27
|
-
* enough to tell them apart on all three operating systems.
|
|
28
|
-
*
|
|
29
29
|
* @param path Where the running CLI lives. Must be a plain path: pass
|
|
30
30
|
* `fileURLToPath(import.meta.url)`, since a `file:///` URL would be read as
|
|
31
31
|
* living outside every project.
|
|
32
32
|
* @param cwd The directory the user invoked it from.
|
|
33
|
+
* @param nodePath The node binary running this, from `process.execPath`.
|
|
33
34
|
* @returns The manager and whether the install is global, or `unknown` when the
|
|
34
35
|
* path matches nothing recognisable.
|
|
35
36
|
*/
|
|
36
|
-
export function installSiteOf(args: { path: string; cwd: string }): InstallSite {
|
|
37
|
+
export function installSiteOf(args: { path: string; cwd: string; nodePath: string }): InstallSite {
|
|
37
38
|
const path = normalise(args.path);
|
|
38
39
|
const manager = managerOf(path);
|
|
39
40
|
if (!manager) return { manager: "unknown", global: false };
|
|
40
|
-
|
|
41
|
+
const cwd = normalise(args.cwd);
|
|
42
|
+
const node = normalise(args.nodePath);
|
|
43
|
+
return { manager, global: isGlobal({ path, cwd, node }) };
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
function normalise(path: string): string {
|
|
@@ -53,18 +56,38 @@ function managerOf(path: string): PackageManager | undefined {
|
|
|
53
56
|
|
|
54
57
|
/**
|
|
55
58
|
* A copy under the working directory belongs to that project, not to the user,
|
|
56
|
-
* unless it sits
|
|
59
|
+
* unless it sits somewhere only a global install can be.
|
|
57
60
|
*
|
|
58
61
|
* Upgrading a copy the project owns would move a version its manifest still
|
|
59
62
|
* pins, so the next install would put the old one back and the user would be
|
|
60
63
|
* left wondering which of the two is running.
|
|
61
64
|
*/
|
|
62
|
-
function isGlobal(path: string
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
+
function isGlobal(args: { path: string; cwd: string; node: string }): boolean {
|
|
66
|
+
if (GLOBAL_ROOTS.some((root) => args.path.includes(root))) return true;
|
|
67
|
+
if (args.path.includes("/yarn/") && args.path.includes("/global/")) return true;
|
|
68
|
+
if (isBesideNode(args.path, args.node)) return true;
|
|
69
|
+
return !args.path.startsWith(`${args.cwd}/`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* npm keeps its global packages beside the node binary: alongside it on
|
|
74
|
+
* Windows, under a sibling `lib` elsewhere.
|
|
75
|
+
*
|
|
76
|
+
* Asked of node rather than matched against a list of directory names, because
|
|
77
|
+
* a version manager puts node wherever it likes and the global root follows it
|
|
78
|
+
* there. `C:\nvm4w\nodejs` is a link to `AppData\Local\nvm\v24.17.0`, which
|
|
79
|
+
* node resolves before anything here sees it, and no list would have had that
|
|
80
|
+
* name in it. The upgrade then read every global install as a project's and
|
|
81
|
+
* refused to touch it.
|
|
82
|
+
*/
|
|
83
|
+
function isBesideNode(path: string, node: string): boolean {
|
|
84
|
+
const directory = parentOf(node);
|
|
85
|
+
if (directory === "") return false;
|
|
86
|
+
if (path.startsWith(`${directory}/node_modules/`)) return true;
|
|
87
|
+
return path.startsWith(`${parentOf(directory)}/lib/node_modules/`);
|
|
65
88
|
}
|
|
66
89
|
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
return
|
|
90
|
+
function parentOf(path: string): string {
|
|
91
|
+
const at = path.lastIndexOf("/");
|
|
92
|
+
return at === -1 ? "" : path.slice(0, at);
|
|
70
93
|
}
|