fyn 2.1.2 → 2.1.4
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/README.md +161 -0
- package/dist/fyn.js +1163 -158
- package/package.json +1 -1
package/dist/fyn.js
CHANGED
|
@@ -143,6 +143,7 @@ const myPkg = __webpack_require__("./cli/mypkg.ts");
|
|
|
143
143
|
const { cleanErrorStack } = __webpack_require__("./node_modules/.f/_/@jchip/error/1.0.3/@jchip/error/dist/index.js");
|
|
144
144
|
const { setupNodeGypEnv } = __webpack_require__("./lib/util/setup-node-gyp.ts");
|
|
145
145
|
const hardLinkDir = __webpack_require__("./lib/util/hard-link-dir.ts");
|
|
146
|
+
const { syncLocalExports, localExportsScanIgnores } = __webpack_require__("./lib/local-exports.ts");
|
|
146
147
|
const xsh = __webpack_require__("./node_modules/.f/_/xsh/0.4.6/xsh/lib/index.js");
|
|
147
148
|
function checkNewVersion(npmConfig) {
|
|
148
149
|
checkPkgNewVersionEngine({
|
|
@@ -417,13 +418,21 @@ class FynCli {
|
|
|
417
418
|
}
|
|
418
419
|
async syncLocalLinks() {
|
|
419
420
|
await this.fyn._initializePkg();
|
|
420
|
-
const { localPkgLinks } = this.fyn._installConfig;
|
|
421
|
+
const { localPkgLinks, localExports } = this.fyn._installConfig;
|
|
422
|
+
let refreshed = false;
|
|
421
423
|
if (!_.isEmpty(localPkgLinks)) {
|
|
422
424
|
for (const vdir in localPkgLinks) {
|
|
423
425
|
const tgtDir = Path.join(this.fyn._cwd, vdir);
|
|
424
426
|
const srcDir = Path.join(this.fyn._cwd, localPkgLinks[vdir].srcDir);
|
|
425
427
|
await hardLinkDir.link(srcDir, tgtDir, { sourceMaps: localPkgLinks[vdir].sourceMaps });
|
|
426
428
|
}
|
|
429
|
+
refreshed = true;
|
|
430
|
+
}
|
|
431
|
+
if (localExports) {
|
|
432
|
+
await syncLocalExports({ cwd: this.fyn._cwd, manifest: localExports });
|
|
433
|
+
refreshed = true;
|
|
434
|
+
}
|
|
435
|
+
if (refreshed) {
|
|
427
436
|
logger.info(`refreshed linked files for local packages`);
|
|
428
437
|
} else {
|
|
429
438
|
logger.info(`There are no local packages`);
|
|
@@ -444,7 +453,9 @@ class FynCli {
|
|
|
444
453
|
return Promise.try(() => this.fyn._initializePkg()).then(async () => {
|
|
445
454
|
checkNewVersion(this.fyn._options);
|
|
446
455
|
if (!this.fyn._changeProdMode && !this.fyn._options.forceInstall && this.fyn._installConfig.time) {
|
|
447
|
-
const stats = await scanFileStats(this.fyn.cwd
|
|
456
|
+
const stats = await scanFileStats(this.fyn.cwd, {
|
|
457
|
+
moreIgnores: localExportsScanIgnores(this.fyn._pkg)
|
|
458
|
+
});
|
|
448
459
|
const { latestMtimeMs } = stats;
|
|
449
460
|
logger.debug(
|
|
450
461
|
"time check from install config - last install time",
|
|
@@ -616,16 +627,13 @@ class FynCli {
|
|
|
616
627
|
}
|
|
617
628
|
script = script || argv.args?.script;
|
|
618
629
|
if (argv.opts?.list || !script) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
console.log(`Lifecycle scripts included in ${this.fyn._pkg.name}:
|
|
630
|
+
await this.fyn.loadPkg();
|
|
631
|
+
if (!argv.opts?.list) {
|
|
632
|
+
console.log(`Lifecycle scripts included in ${this.fyn._pkg.name}:
|
|
623
633
|
`);
|
|
624
|
-
}
|
|
625
|
-
console.log(Object.keys(_.get(this.fyn._pkg, "scripts", {})).join("\n"));
|
|
626
|
-
} finally {
|
|
627
|
-
fyntil.exit(0);
|
|
628
634
|
}
|
|
635
|
+
console.log(Object.keys(_.get(this.fyn._pkg, "scripts", {})).join("\n"));
|
|
636
|
+
fyntil.exit(0);
|
|
629
637
|
}
|
|
630
638
|
await this.fyn.loadPkg();
|
|
631
639
|
if (!_.get(this.fyn._pkg, ["scripts", script])) {
|
|
@@ -786,8 +794,15 @@ const pickEnvOptions = () => {
|
|
|
786
794
|
cfg[m.optKey] = ev === m.checkValue;
|
|
787
795
|
logger.info(`setting option ${m.optKey} to ${cfg[m.optKey]} by env ${envKey} value ${ev}`);
|
|
788
796
|
}
|
|
797
|
+
return cfg;
|
|
789
798
|
}, {});
|
|
790
799
|
};
|
|
800
|
+
const getRunExitCode = (err) => err.code !== void 0 ? err.code : err.errno || 1;
|
|
801
|
+
const setLockfile = (config, lockfile) => {
|
|
802
|
+
const previous = config.opts.lockfile;
|
|
803
|
+
config.opts.lockfile = lockfile;
|
|
804
|
+
return previous;
|
|
805
|
+
};
|
|
791
806
|
const pickOptions = async (cmd, checkFynpo = true) => {
|
|
792
807
|
const meta = cmd.jsonMeta;
|
|
793
808
|
const rootOpts = cmd.rootCmd?.opts || {};
|
|
@@ -830,6 +845,11 @@ const pickOptions = async (cmd, checkFynpo = true) => {
|
|
|
830
845
|
if (allOpts.progress) logger.setItemType(allOpts.progress);
|
|
831
846
|
return { opts: allOpts, rcData, _cliSource: meta.source, _fynpo: fynpo };
|
|
832
847
|
};
|
|
848
|
+
const makeFynGlobal = async (cmd, extra = {}) => {
|
|
849
|
+
const { opts } = await pickOptions(cmd, false);
|
|
850
|
+
const tag = cmd.getParent()?.opts?.tag;
|
|
851
|
+
return new FynGlobal({ globalDir: cmd.opts?.dir, tag, fynOpts: opts, ...extra });
|
|
852
|
+
};
|
|
833
853
|
const options = {
|
|
834
854
|
fynlocal: {
|
|
835
855
|
args: "<flag boolean>",
|
|
@@ -930,6 +950,10 @@ const options = {
|
|
|
930
950
|
args: "<flag boolean>",
|
|
931
951
|
desc: "Ignore host in tarball URL from meta dist."
|
|
932
952
|
},
|
|
953
|
+
"enforce-registry-deps": {
|
|
954
|
+
args: "<flag boolean>",
|
|
955
|
+
desc: "Require transitive deps to be from a registry (default on). --no-enforce-registry-deps to disable."
|
|
956
|
+
},
|
|
933
957
|
"show-deprecated": {
|
|
934
958
|
alias: "s",
|
|
935
959
|
args: "<flag boolean>",
|
|
@@ -1030,13 +1054,12 @@ const commands = {
|
|
|
1030
1054
|
Object.assign(meta.opts, cmd.rootCmd.opts);
|
|
1031
1055
|
}
|
|
1032
1056
|
const config = await pickOptions(cmd);
|
|
1033
|
-
const lockFile = config
|
|
1034
|
-
config.lockfile = false;
|
|
1057
|
+
const lockFile = setLockfile(config, false);
|
|
1035
1058
|
const cli = new FynCli(config);
|
|
1036
1059
|
const opts = Object.assign({}, meta.opts, meta.args);
|
|
1037
1060
|
return cli.add(opts).then((added) => {
|
|
1038
1061
|
if (!added || !meta.opts.install) return;
|
|
1039
|
-
config
|
|
1062
|
+
setLockfile(config, lockFile);
|
|
1040
1063
|
config.noStartupInfo = true;
|
|
1041
1064
|
logger.info("installing...");
|
|
1042
1065
|
fynTil.resetFynpo();
|
|
@@ -1080,14 +1103,13 @@ const commands = {
|
|
|
1080
1103
|
Object.assign(meta.opts, cmd.rootCmd.opts);
|
|
1081
1104
|
}
|
|
1082
1105
|
const options2 = await pickOptions(cmd);
|
|
1083
|
-
const lockFile = options2
|
|
1084
|
-
options2.lockfile = false;
|
|
1106
|
+
const lockFile = setLockfile(options2, false);
|
|
1085
1107
|
const cli = new FynCli(options2);
|
|
1086
1108
|
const opts = Object.assign({}, meta.opts, meta.args);
|
|
1087
1109
|
const removed = await cli.remove(opts);
|
|
1088
1110
|
if (removed) {
|
|
1089
1111
|
if (!meta.opts.install) return;
|
|
1090
|
-
options2
|
|
1112
|
+
setLockfile(options2, lockFile);
|
|
1091
1113
|
options2.noStartupInfo = true;
|
|
1092
1114
|
fynTil.resetFynpo();
|
|
1093
1115
|
logger.info("installing...");
|
|
@@ -1121,7 +1143,7 @@ const commands = {
|
|
|
1121
1143
|
const options2 = await pickOptions(cmd, !meta.opts.list);
|
|
1122
1144
|
return await new FynCli(options2).run(meta, void 0, cmd, parsed);
|
|
1123
1145
|
} catch (err) {
|
|
1124
|
-
const exitCode = err
|
|
1146
|
+
const exitCode = getRunExitCode(err);
|
|
1125
1147
|
if (err.event && err.script) {
|
|
1126
1148
|
logger.error(chalk.red(`Script '${err.event}' failed${err.pkgid ? ` for ${err.pkgid}` : ""}`));
|
|
1127
1149
|
if (err.path) {
|
|
@@ -1202,9 +1224,7 @@ const commands = {
|
|
|
1202
1224
|
"new-tag": { args: "<flag boolean>", desc: "Install as new tag even if same version exists" }
|
|
1203
1225
|
},
|
|
1204
1226
|
async exec(cmd) {
|
|
1205
|
-
|
|
1206
|
-
const tag = cmd.getParent()?.opts?.tag;
|
|
1207
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir, yes: cmd.opts?.yes, tag });
|
|
1227
|
+
const fynGlobal = await makeFynGlobal(cmd, { yes: cmd.opts?.yes });
|
|
1208
1228
|
const packages = cmd.args?.packages || [];
|
|
1209
1229
|
if (packages.length === 0) {
|
|
1210
1230
|
logger.error("No packages specified");
|
|
@@ -1229,11 +1249,9 @@ const commands = {
|
|
|
1229
1249
|
"yes": { alias: "y", desc: "Auto-confirm all prompts" }
|
|
1230
1250
|
},
|
|
1231
1251
|
async exec(cmd) {
|
|
1232
|
-
|
|
1233
|
-
const tag = cmd.getParent()?.opts?.tag;
|
|
1234
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir, yes: cmd.opts?.yes, tag });
|
|
1252
|
+
const fynGlobal = await makeFynGlobal(cmd, { yes: cmd.opts?.yes });
|
|
1235
1253
|
const packageSpec = cmd.args?.package;
|
|
1236
|
-
if (!packageSpec && !tag) {
|
|
1254
|
+
if (!packageSpec && !fynGlobal.tag) {
|
|
1237
1255
|
logger.error("No package specified");
|
|
1238
1256
|
logger.info("Usage: fyn global remove <name>[@<version>]");
|
|
1239
1257
|
logger.info(" Or: fyn global --tag=<tag> remove");
|
|
@@ -1252,11 +1270,9 @@ const commands = {
|
|
|
1252
1270
|
"dir": { args: "<dir string>", desc: "Directory for global packages (default: ~/.fyn/global)" }
|
|
1253
1271
|
},
|
|
1254
1272
|
async exec(cmd) {
|
|
1255
|
-
|
|
1256
|
-
const tag = cmd.getParent()?.opts?.tag;
|
|
1257
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir, tag });
|
|
1273
|
+
const fynGlobal = await makeFynGlobal(cmd);
|
|
1258
1274
|
const packageSpec = cmd.args?.package;
|
|
1259
|
-
if (!packageSpec && !tag) {
|
|
1275
|
+
if (!packageSpec && !fynGlobal.tag) {
|
|
1260
1276
|
logger.error("No package specified");
|
|
1261
1277
|
logger.info("Usage: fyn global link <name>@<version>");
|
|
1262
1278
|
logger.info(" Or: fyn global --tag=<tag> link");
|
|
@@ -1276,9 +1292,7 @@ const commands = {
|
|
|
1276
1292
|
"dir": { args: "<dir string>", desc: "Directory for global packages (default: ~/.fyn/global)" }
|
|
1277
1293
|
},
|
|
1278
1294
|
async exec(cmd) {
|
|
1279
|
-
|
|
1280
|
-
const tag = cmd.getParent()?.opts?.tag;
|
|
1281
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir, tag });
|
|
1295
|
+
const fynGlobal = await makeFynGlobal(cmd);
|
|
1282
1296
|
await fynGlobal.listGlobalPackages(cmd.args?.name);
|
|
1283
1297
|
}
|
|
1284
1298
|
},
|
|
@@ -1289,9 +1303,7 @@ const commands = {
|
|
|
1289
1303
|
"dir": { args: "<dir string>", desc: "Directory for global packages (default: ~/.fyn/global)" }
|
|
1290
1304
|
},
|
|
1291
1305
|
async exec(cmd) {
|
|
1292
|
-
|
|
1293
|
-
const tag = cmd.getParent()?.opts?.tag;
|
|
1294
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir, tag });
|
|
1306
|
+
const fynGlobal = await makeFynGlobal(cmd);
|
|
1295
1307
|
const packageSpec = cmd.args?.package;
|
|
1296
1308
|
const updated = await fynGlobal.updateGlobalPackage(packageSpec);
|
|
1297
1309
|
if (!updated) {
|
|
@@ -1306,9 +1318,7 @@ const commands = {
|
|
|
1306
1318
|
"dir": { args: "<dir string>", desc: "Directory for global packages (default: ~/.fyn/global)" }
|
|
1307
1319
|
},
|
|
1308
1320
|
async exec(cmd) {
|
|
1309
|
-
|
|
1310
|
-
const tag = cmd.getParent()?.opts?.tag;
|
|
1311
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir, tag });
|
|
1321
|
+
const fynGlobal = await makeFynGlobal(cmd);
|
|
1312
1322
|
await fynGlobal.useNodeVersion(cmd.args?.version);
|
|
1313
1323
|
}
|
|
1314
1324
|
},
|
|
@@ -1319,8 +1329,7 @@ const commands = {
|
|
|
1319
1329
|
"dir": { args: "<dir string>", desc: "Directory for global packages (default: ~/.fyn/global)" }
|
|
1320
1330
|
},
|
|
1321
1331
|
async exec(cmd) {
|
|
1322
|
-
|
|
1323
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir });
|
|
1332
|
+
const fynGlobal = await makeFynGlobal(cmd);
|
|
1324
1333
|
const packageName = cmd.args?.package;
|
|
1325
1334
|
const removed = await fynGlobal.cleanupPackage(packageName);
|
|
1326
1335
|
if (removed === 0) {
|
|
@@ -1336,9 +1345,7 @@ const commands = {
|
|
|
1336
1345
|
"dir": { args: "<dir string>", desc: "Directory for global packages (default: ~/.fyn/global)" }
|
|
1337
1346
|
},
|
|
1338
1347
|
async exec(cmd) {
|
|
1339
|
-
|
|
1340
|
-
const tag = cmd.getParent()?.opts?.tag;
|
|
1341
|
-
const fynGlobal = new FynGlobal({ globalDir: cmd.opts?.dir, tag });
|
|
1348
|
+
const fynGlobal = await makeFynGlobal(cmd);
|
|
1342
1349
|
fynGlobal.showPathSetup();
|
|
1343
1350
|
}
|
|
1344
1351
|
}
|
|
@@ -1420,6 +1427,9 @@ module.exports = {
|
|
|
1420
1427
|
run,
|
|
1421
1428
|
fun,
|
|
1422
1429
|
nodeGyp,
|
|
1430
|
+
getRunExitCode,
|
|
1431
|
+
pickEnvOptions,
|
|
1432
|
+
setLockfile,
|
|
1423
1433
|
hardLinkDir
|
|
1424
1434
|
};
|
|
1425
1435
|
|
|
@@ -1686,6 +1696,7 @@ class ShowStat {
|
|
|
1686
1696
|
return this._show(pkgIds);
|
|
1687
1697
|
}).catch((err) => {
|
|
1688
1698
|
logger.error(err);
|
|
1699
|
+
throw err;
|
|
1689
1700
|
}).finally(() => {
|
|
1690
1701
|
logger.removeItem(FETCH_META);
|
|
1691
1702
|
});
|
|
@@ -1977,10 +1988,26 @@ module.exports.__TEST__ = {
|
|
|
1977
1988
|
/***/ "./lib/cacache-util.ts":
|
|
1978
1989
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
1979
1990
|
|
|
1991
|
+
"use strict";
|
|
1992
|
+
|
|
1980
1993
|
const cacache = __webpack_require__("./node_modules/.f/_/cacache/20.0.1/cacache/lib/index.js");
|
|
1981
1994
|
const fs = (__webpack_require__("fs").promises);
|
|
1982
1995
|
const path = __webpack_require__("path");
|
|
1983
1996
|
const crypto = __webpack_require__("crypto");
|
|
1997
|
+
function hashKey(key) {
|
|
1998
|
+
return crypto.createHash("sha256").update(key).digest("hex");
|
|
1999
|
+
}
|
|
2000
|
+
function getBucketPath(cache, key) {
|
|
2001
|
+
const hashed = hashKey(key);
|
|
2002
|
+
const indexV = (__webpack_require__("./node_modules/.f/_/cacache/20.0.1/cacache/package.json")["cache-version"].index);
|
|
2003
|
+
return path.join(
|
|
2004
|
+
cache,
|
|
2005
|
+
`index-v${indexV}`,
|
|
2006
|
+
hashed.slice(0, 2),
|
|
2007
|
+
hashed.slice(2, 4),
|
|
2008
|
+
hashed.slice(4)
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
1984
2011
|
async function refreshCacheEntry(cache, key) {
|
|
1985
2012
|
const bucket = getBucketPath(cache, key);
|
|
1986
2013
|
const time = /* @__PURE__ */ new Date();
|
|
@@ -2007,20 +2034,6 @@ async function getCacheInfoWithRefreshTime(cache, key) {
|
|
|
2007
2034
|
throw err;
|
|
2008
2035
|
}
|
|
2009
2036
|
}
|
|
2010
|
-
function getBucketPath(cache, key) {
|
|
2011
|
-
const hashed = hashKey(key);
|
|
2012
|
-
const indexV = (__webpack_require__("./node_modules/.f/_/cacache/20.0.1/cacache/package.json")["cache-version"].index);
|
|
2013
|
-
return path.join(
|
|
2014
|
-
cache,
|
|
2015
|
-
`index-v${indexV}`,
|
|
2016
|
-
hashed.slice(0, 2),
|
|
2017
|
-
hashed.slice(2, 4),
|
|
2018
|
-
hashed.slice(4)
|
|
2019
|
-
);
|
|
2020
|
-
}
|
|
2021
|
-
function hashKey(key) {
|
|
2022
|
-
return crypto.createHash("sha256").update(key).digest("hex");
|
|
2023
|
-
}
|
|
2024
2037
|
module.exports = {
|
|
2025
2038
|
refreshCacheEntry,
|
|
2026
2039
|
getCacheInfoWithRefreshTime,
|
|
@@ -2105,9 +2118,12 @@ class DepData {
|
|
|
2105
2118
|
return this.getPkgsData(item.optFailed)[item.name];
|
|
2106
2119
|
}
|
|
2107
2120
|
getPkgById(id) {
|
|
2108
|
-
const
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2121
|
+
const lastAt = id.lastIndexOf("@");
|
|
2122
|
+
const sep = lastAt > 0 ? lastAt : -1;
|
|
2123
|
+
const name = sep > 0 ? id.slice(0, sep) : id;
|
|
2124
|
+
const version = sep > 0 ? id.slice(sep + 1) : void 0;
|
|
2125
|
+
const x = this.getPkgsData()[name];
|
|
2126
|
+
return version ? x[version] : x;
|
|
2111
2127
|
}
|
|
2112
2128
|
eachVersion(cb) {
|
|
2113
2129
|
const pkgs = this.pkgs;
|
|
@@ -2740,6 +2756,7 @@ const unlock = util.promisify(lockfile.unlock);
|
|
|
2740
2756
|
class FynGlobal {
|
|
2741
2757
|
/**
|
|
2742
2758
|
* Create a FynGlobal instance
|
|
2759
|
+
*
|
|
2743
2760
|
* @param {Object} options - Configuration options
|
|
2744
2761
|
* @param {string} [options.globalDir] - Root directory for global packages (default: ~/.fyn/global)
|
|
2745
2762
|
* @param {string} [options.registry] - NPM registry URL (default: https://registry.npmjs.org)
|
|
@@ -2764,21 +2781,24 @@ class FynGlobal {
|
|
|
2764
2781
|
}
|
|
2765
2782
|
/**
|
|
2766
2783
|
* Create a Fyn instance for global package installation
|
|
2784
|
+
*
|
|
2767
2785
|
* @param {string} cwd - Working directory for the install
|
|
2768
2786
|
* @param {boolean} fynlocal - Whether this is a local package
|
|
2769
2787
|
* @returns {Fyn} Configured Fyn instance
|
|
2770
2788
|
*/
|
|
2771
2789
|
_createFyn(cwd, fynlocal) {
|
|
2772
2790
|
process.env.FYN_CENTRAL_DIR = Path.join(this.globalRoot, "_central-storage");
|
|
2791
|
+
const fynOpts = this.options.fynOpts || {};
|
|
2773
2792
|
return new Fyn({
|
|
2774
2793
|
opts: {
|
|
2794
|
+
...fynOpts,
|
|
2775
2795
|
cwd,
|
|
2776
2796
|
targetDir: "node_modules",
|
|
2777
2797
|
centralStore: true,
|
|
2778
2798
|
lockfile: true,
|
|
2779
2799
|
fynlocal,
|
|
2780
2800
|
sourceMaps: false,
|
|
2781
|
-
registry: this.options.registry || "https://registry.npmjs.org",
|
|
2801
|
+
registry: this.options.registry || fynOpts.registry || "https://registry.npmjs.org",
|
|
2782
2802
|
layout: "normal",
|
|
2783
2803
|
flattenTop: true
|
|
2784
2804
|
},
|
|
@@ -2801,6 +2821,7 @@ class FynGlobal {
|
|
|
2801
2821
|
}
|
|
2802
2822
|
/**
|
|
2803
2823
|
* Read the installed.json registry
|
|
2824
|
+
*
|
|
2804
2825
|
* @returns {Object} Registry object with packages info
|
|
2805
2826
|
*/
|
|
2806
2827
|
async readInstalledJson() {
|
|
@@ -2820,6 +2841,7 @@ class FynGlobal {
|
|
|
2820
2841
|
}
|
|
2821
2842
|
/**
|
|
2822
2843
|
* Get all versions of a package from the registry
|
|
2844
|
+
*
|
|
2823
2845
|
* @param {string} packageName
|
|
2824
2846
|
* @returns {Array} Array of version info objects
|
|
2825
2847
|
*/
|
|
@@ -2829,6 +2851,7 @@ class FynGlobal {
|
|
|
2829
2851
|
}
|
|
2830
2852
|
/**
|
|
2831
2853
|
* Get the linked version of a package
|
|
2854
|
+
*
|
|
2832
2855
|
* @param {string} packageName
|
|
2833
2856
|
* @returns {Object|null} Version info or null
|
|
2834
2857
|
*/
|
|
@@ -2838,6 +2861,7 @@ class FynGlobal {
|
|
|
2838
2861
|
}
|
|
2839
2862
|
/**
|
|
2840
2863
|
* Find package info by tag (e.g., g1, g2)
|
|
2864
|
+
*
|
|
2841
2865
|
* @param {string} tag - The tag to find
|
|
2842
2866
|
* @returns {Object|null} Object with packageName and versionInfo, or null if not found
|
|
2843
2867
|
*/
|
|
@@ -2854,6 +2878,7 @@ class FynGlobal {
|
|
|
2854
2878
|
}
|
|
2855
2879
|
/**
|
|
2856
2880
|
* Validate that the tag option points to an existing installation
|
|
2881
|
+
*
|
|
2857
2882
|
* @returns {Object} Object with packageName and versionInfo
|
|
2858
2883
|
* @throws {Error} If tag is set but doesn't exist
|
|
2859
2884
|
*/
|
|
@@ -2884,14 +2909,19 @@ class FynGlobal {
|
|
|
2884
2909
|
}
|
|
2885
2910
|
/**
|
|
2886
2911
|
* Remove a version from the registry
|
|
2912
|
+
*
|
|
2913
|
+
* @param {string} packageName - package name
|
|
2914
|
+
* @param {string} dir - the tag dir (e.g. "g1"); the unique key of an entry.
|
|
2915
|
+
* The same version can exist under multiple tag dirs (--new-tag), so entries
|
|
2916
|
+
* must be identified by dir, not by version string.
|
|
2887
2917
|
*/
|
|
2888
|
-
async removeFromRegistry(packageName,
|
|
2918
|
+
async removeFromRegistry(packageName, dir) {
|
|
2889
2919
|
const registry = await this.readInstalledJson();
|
|
2890
2920
|
if (!registry.packages[packageName]) {
|
|
2891
2921
|
return;
|
|
2892
2922
|
}
|
|
2893
2923
|
const versions = registry.packages[packageName].versions;
|
|
2894
|
-
const idx = versions.findIndex((v) => v.
|
|
2924
|
+
const idx = versions.findIndex((v) => v.dir === dir);
|
|
2895
2925
|
if (idx >= 0) {
|
|
2896
2926
|
versions.splice(idx, 1);
|
|
2897
2927
|
if (versions.length === 0) {
|
|
@@ -3125,6 +3155,7 @@ class FynGlobal {
|
|
|
3125
3155
|
}
|
|
3126
3156
|
/**
|
|
3127
3157
|
* Link bin executables to global bin directory
|
|
3158
|
+
*
|
|
3128
3159
|
* @param {string} gId - Package directory ID (e.g., "g1")
|
|
3129
3160
|
* @param {Object} bins - Map of binName -> binPath
|
|
3130
3161
|
* @param {boolean} force - If true, overwrite existing bins
|
|
@@ -3158,6 +3189,7 @@ class FynGlobal {
|
|
|
3158
3189
|
* - Latest version wins (becomes linked)
|
|
3159
3190
|
* - Prompts if newer version available when installing older
|
|
3160
3191
|
* - Prompts to remove old versions after installing new
|
|
3192
|
+
*
|
|
3161
3193
|
* @param {string} packageSpec - Package spec to install
|
|
3162
3194
|
* @param {Object} [options] - Install options
|
|
3163
3195
|
* @param {boolean} [options.newTag] - Install as new tag even if same version exists
|
|
@@ -3209,7 +3241,7 @@ class FynGlobal {
|
|
|
3209
3241
|
if (!existingExact.linked) {
|
|
3210
3242
|
const linkIt = await this.promptYesNo(`Link this version to make it active?`);
|
|
3211
3243
|
if (linkIt) {
|
|
3212
|
-
await this.linkPackageVersion(packageName
|
|
3244
|
+
await this.linkPackageVersion(`${packageName}@${existingExact.version}`);
|
|
3213
3245
|
}
|
|
3214
3246
|
}
|
|
3215
3247
|
return false;
|
|
@@ -3256,7 +3288,7 @@ class FynGlobal {
|
|
|
3256
3288
|
if (shouldLink) {
|
|
3257
3289
|
for (const existing of existingVersions) {
|
|
3258
3290
|
if (existing.linked) {
|
|
3259
|
-
await this.unlinkBinsForVersion(packageName, existing.
|
|
3291
|
+
await this.unlinkBinsForVersion(packageName, existing.dir);
|
|
3260
3292
|
existing.linked = false;
|
|
3261
3293
|
await this.addToRegistry(packageName, existing);
|
|
3262
3294
|
}
|
|
@@ -3291,7 +3323,7 @@ class FynGlobal {
|
|
|
3291
3323
|
if (removeOld) {
|
|
3292
3324
|
for (const existing of existingVersions) {
|
|
3293
3325
|
if (existing.version !== installedVersion) {
|
|
3294
|
-
await this.removeVersion(packageName, existing.
|
|
3326
|
+
await this.removeVersion(packageName, existing.dir);
|
|
3295
3327
|
logger.info(`Removed ${packageName}@${existing.version}`);
|
|
3296
3328
|
}
|
|
3297
3329
|
}
|
|
@@ -3315,9 +3347,9 @@ class FynGlobal {
|
|
|
3315
3347
|
/**
|
|
3316
3348
|
* Unlink bins for a specific version
|
|
3317
3349
|
*/
|
|
3318
|
-
async unlinkBinsForVersion(packageName,
|
|
3350
|
+
async unlinkBinsForVersion(packageName, dir) {
|
|
3319
3351
|
const versions = await this.getPackageVersions(packageName);
|
|
3320
|
-
const versionInfo = versions.find((v) => v.
|
|
3352
|
+
const versionInfo = versions.find((v) => v.dir === dir);
|
|
3321
3353
|
if (!versionInfo) return;
|
|
3322
3354
|
const binLinker = this._getBinLinker();
|
|
3323
3355
|
const bins = this._getVersionBinTargets(versionInfo);
|
|
@@ -3331,24 +3363,30 @@ class FynGlobal {
|
|
|
3331
3363
|
}
|
|
3332
3364
|
}
|
|
3333
3365
|
/**
|
|
3334
|
-
* Remove a specific
|
|
3366
|
+
* Remove a specific installed entry of a package, identified by its tag dir
|
|
3367
|
+
* (the unique key — the same version may exist under several tag dirs).
|
|
3368
|
+
*
|
|
3369
|
+
* @param {string} packageName - package name
|
|
3370
|
+
* @param {string} dir - the entry's tag dir (e.g. "g1")
|
|
3371
|
+
* @returns {boolean} true if an entry was removed
|
|
3335
3372
|
*/
|
|
3336
|
-
async removeVersion(packageName,
|
|
3373
|
+
async removeVersion(packageName, dir) {
|
|
3337
3374
|
const versions = await this.getPackageVersions(packageName);
|
|
3338
|
-
const versionInfo = versions.find((v) => v.
|
|
3375
|
+
const versionInfo = versions.find((v) => v.dir === dir);
|
|
3339
3376
|
if (!versionInfo) {
|
|
3340
3377
|
return false;
|
|
3341
3378
|
}
|
|
3342
3379
|
if (versionInfo.linked) {
|
|
3343
|
-
await this.unlinkBinsForVersion(packageName,
|
|
3380
|
+
await this.unlinkBinsForVersion(packageName, dir);
|
|
3344
3381
|
}
|
|
3345
3382
|
const pkgDir = Path.join(this.packagesDir, versionInfo.dir);
|
|
3346
3383
|
await Fs.$.rimraf(pkgDir);
|
|
3347
|
-
await this.removeFromRegistry(packageName,
|
|
3384
|
+
await this.removeFromRegistry(packageName, dir);
|
|
3348
3385
|
return true;
|
|
3349
3386
|
}
|
|
3350
3387
|
/**
|
|
3351
3388
|
* Remove globally installed package version(s)
|
|
3389
|
+
*
|
|
3352
3390
|
* @param {string} packageSpec - Package name or name@semver pattern
|
|
3353
3391
|
* - name: remove all versions (warns if multiple, won't remove linked unless only one)
|
|
3354
3392
|
* - name@version: remove exact version
|
|
@@ -3359,11 +3397,12 @@ class FynGlobal {
|
|
|
3359
3397
|
if (this.tag) {
|
|
3360
3398
|
const found = await this.validateTag();
|
|
3361
3399
|
logger.info(`Removing ${found.packageName}@${found.versionInfo.version} (${this.tag})`);
|
|
3362
|
-
await this.removeVersion(found.packageName, found.versionInfo.
|
|
3400
|
+
await this.removeVersion(found.packageName, found.versionInfo.dir);
|
|
3363
3401
|
logger.info(`Removed ${found.packageName}@${found.versionInfo.version}`);
|
|
3364
3402
|
return true;
|
|
3365
3403
|
}
|
|
3366
|
-
let packageName
|
|
3404
|
+
let packageName;
|
|
3405
|
+
let versionSpec;
|
|
3367
3406
|
if (packageSpec.startsWith("@")) {
|
|
3368
3407
|
const lastAt = packageSpec.lastIndexOf("@");
|
|
3369
3408
|
if (lastAt > 0 && lastAt !== packageSpec.indexOf("@")) {
|
|
@@ -3418,13 +3457,14 @@ class FynGlobal {
|
|
|
3418
3457
|
}
|
|
3419
3458
|
}
|
|
3420
3459
|
for (const versionInfo of toRemove) {
|
|
3421
|
-
await this.removeVersion(packageName, versionInfo.
|
|
3460
|
+
await this.removeVersion(packageName, versionInfo.dir);
|
|
3422
3461
|
logger.info(`Removed ${packageName}@${versionInfo.version}`);
|
|
3423
3462
|
}
|
|
3424
3463
|
return true;
|
|
3425
3464
|
}
|
|
3426
3465
|
/**
|
|
3427
3466
|
* List globally installed packages
|
|
3467
|
+
*
|
|
3428
3468
|
* @param {string} [filterName] - Optional package name to filter by
|
|
3429
3469
|
*/
|
|
3430
3470
|
async listGlobalPackages(filterName) {
|
|
@@ -3482,10 +3522,13 @@ ${packageName}:`);
|
|
|
3482
3522
|
}
|
|
3483
3523
|
/**
|
|
3484
3524
|
* Link (activate) a specific version of a package
|
|
3525
|
+
*
|
|
3485
3526
|
* @param {string} packageSpec - Package name@version to link, or ignored if --tag is specified
|
|
3486
3527
|
*/
|
|
3487
3528
|
async linkPackageVersion(packageSpec) {
|
|
3488
|
-
let packageName
|
|
3529
|
+
let packageName;
|
|
3530
|
+
let version;
|
|
3531
|
+
let targetVersion;
|
|
3489
3532
|
if (this.tag) {
|
|
3490
3533
|
const found = await this.validateTag();
|
|
3491
3534
|
packageName = found.packageName;
|
|
@@ -3539,7 +3582,7 @@ Use: fyn global link ${packageName}@<version>`);
|
|
|
3539
3582
|
const versions = await this.getPackageVersions(packageName);
|
|
3540
3583
|
const currentLinked = versions.find((v) => v.linked);
|
|
3541
3584
|
if (currentLinked) {
|
|
3542
|
-
await this.unlinkBinsForVersion(packageName, currentLinked.
|
|
3585
|
+
await this.unlinkBinsForVersion(packageName, currentLinked.dir);
|
|
3543
3586
|
}
|
|
3544
3587
|
const pkgDir = Path.join(this.packagesDir, targetVersion.dir);
|
|
3545
3588
|
const bins = await this.discoverBins(pkgDir, packageName);
|
|
@@ -3553,6 +3596,7 @@ Use: fyn global link ${packageName}@<version>`);
|
|
|
3553
3596
|
}
|
|
3554
3597
|
/**
|
|
3555
3598
|
* Find a package by its local path
|
|
3599
|
+
*
|
|
3556
3600
|
* @param {string} localPath - The local path to search for
|
|
3557
3601
|
* @returns {Object|null} Object with packageName and versions, or null
|
|
3558
3602
|
*/
|
|
@@ -3572,6 +3616,7 @@ Use: fyn global link ${packageName}@<version>`);
|
|
|
3572
3616
|
}
|
|
3573
3617
|
/**
|
|
3574
3618
|
* Update a globally installed package (linked version or specific tag)
|
|
3619
|
+
*
|
|
3575
3620
|
* @param {string} packageSpec - Package name or local path
|
|
3576
3621
|
*/
|
|
3577
3622
|
async updateGlobalPackage(packageSpec) {
|
|
@@ -3697,6 +3742,7 @@ Use: fyn global link ${packageName}@<version>`);
|
|
|
3697
3742
|
}
|
|
3698
3743
|
/**
|
|
3699
3744
|
* Cleanup non-linked versions of a package
|
|
3745
|
+
*
|
|
3700
3746
|
* @param {string} [packageName] - Package name to cleanup, or all packages if not specified
|
|
3701
3747
|
* @returns {number} Number of versions removed
|
|
3702
3748
|
*/
|
|
@@ -3726,7 +3772,7 @@ Use: fyn global link ${packageName}@<version>`);
|
|
|
3726
3772
|
continue;
|
|
3727
3773
|
}
|
|
3728
3774
|
for (const v of nonLinked) {
|
|
3729
|
-
await this.removeVersion(pkgName, v.
|
|
3775
|
+
await this.removeVersion(pkgName, v.dir);
|
|
3730
3776
|
logger.info(`Removed ${pkgName}@${v.version} (${v.dir})`);
|
|
3731
3777
|
totalRemoved++;
|
|
3732
3778
|
}
|
|
@@ -3782,6 +3828,7 @@ const fynTil = __webpack_require__("./lib/util/fyntil.ts");
|
|
|
3782
3828
|
const FynCentral = __webpack_require__("./lib/fyn-central.ts");
|
|
3783
3829
|
const xaa = __webpack_require__("./lib/util/xaa.ts");
|
|
3784
3830
|
const { checkPkgNeedInstall } = __webpack_require__("./lib/util/check-pkg-need-install.ts");
|
|
3831
|
+
const { localExportsNeedInstall } = __webpack_require__("./lib/local-exports.ts");
|
|
3785
3832
|
const lockfile = __webpack_require__("./node_modules/.f/_/lockfile/1.0.4/lockfile/lockfile.js");
|
|
3786
3833
|
const createLock = util.promisify(lockfile.lock);
|
|
3787
3834
|
const unlock = util.promisify(lockfile.unlock);
|
|
@@ -3907,7 +3954,13 @@ class Fyn {
|
|
|
3907
3954
|
} else {
|
|
3908
3955
|
centralDir = this._fynpo.config.centralDir;
|
|
3909
3956
|
if (!centralDir) {
|
|
3910
|
-
|
|
3957
|
+
const storeBaseDir = await fynTil.resolveGitMainWorktreeDir(this._fynpo.dir);
|
|
3958
|
+
if (storeBaseDir !== this._fynpo.dir) {
|
|
3959
|
+
logger.info(
|
|
3960
|
+
`fynpo monorepo is a git worktree; sharing central store from main worktree ${storeBaseDir}`
|
|
3961
|
+
);
|
|
3962
|
+
}
|
|
3963
|
+
centralDir = Path.join(storeBaseDir, ".fynpo", "_store");
|
|
3911
3964
|
}
|
|
3912
3965
|
logger.info(`Enabling central store by fynpo monorepo using dir ${centralDir}`);
|
|
3913
3966
|
}
|
|
@@ -3922,6 +3975,7 @@ class Fyn {
|
|
|
3922
3975
|
}
|
|
3923
3976
|
/**
|
|
3924
3977
|
* Check user production mode option against saved install config in node_modules
|
|
3978
|
+
*
|
|
3925
3979
|
* @remarks - this._installConfig must've been initialized
|
|
3926
3980
|
* @returns nothing
|
|
3927
3981
|
*/
|
|
@@ -3979,13 +4033,13 @@ class Fyn {
|
|
|
3979
4033
|
const fynInstallConfig = JSON.parse(await Fs.readFile(filename));
|
|
3980
4034
|
logger.debug("loaded fynInstallConfig", fynInstallConfig);
|
|
3981
4035
|
const { layout } = fynInstallConfig;
|
|
3982
|
-
if (layout && layout !== this.
|
|
4036
|
+
if (layout && layout !== this._options.layout) {
|
|
3983
4037
|
if (this._cliSource.layout !== "default") {
|
|
3984
4038
|
logger.warn(
|
|
3985
|
-
`Forcing layout to ${layout} from ${this.
|
|
4039
|
+
`Forcing layout to ${layout} from ${this._options.layout} because your existing node_modules uses that. To change it, please remove node_modules first.`
|
|
3986
4040
|
);
|
|
3987
4041
|
}
|
|
3988
|
-
this.
|
|
4042
|
+
this._options.layout = layout;
|
|
3989
4043
|
}
|
|
3990
4044
|
const recordedShort = fynInstallConfig.shortPkgDir === void 0 ? true : fynInstallConfig.shortPkgDir;
|
|
3991
4045
|
if (recordedShort !== this._shortPkgDir) {
|
|
@@ -4106,6 +4160,7 @@ class Fyn {
|
|
|
4106
4160
|
}
|
|
4107
4161
|
/**
|
|
4108
4162
|
* Get the version of a direct dependency from package.json
|
|
4163
|
+
*
|
|
4109
4164
|
* @param {string} pkgName - Package name to look up
|
|
4110
4165
|
* @returns {string|null} The version or null if not found
|
|
4111
4166
|
*/
|
|
@@ -4180,6 +4235,12 @@ class Fyn {
|
|
|
4180
4235
|
return true;
|
|
4181
4236
|
}
|
|
4182
4237
|
}
|
|
4238
|
+
if (await localExportsNeedInstall({
|
|
4239
|
+
cwd: this._cwd,
|
|
4240
|
+
manifest: this._installConfig.localExports
|
|
4241
|
+
})) {
|
|
4242
|
+
return true;
|
|
4243
|
+
}
|
|
4183
4244
|
return false;
|
|
4184
4245
|
}
|
|
4185
4246
|
setLocalDeps(localsByDepth) {
|
|
@@ -4194,6 +4255,9 @@ class Fyn {
|
|
|
4194
4255
|
setLocalPkgLinks(localLinks) {
|
|
4195
4256
|
this._installConfig.localPkgLinks = localLinks;
|
|
4196
4257
|
}
|
|
4258
|
+
setLocalExports(manifest) {
|
|
4259
|
+
this._installConfig.localExports = manifest;
|
|
4260
|
+
}
|
|
4197
4261
|
// save the config to outputDir
|
|
4198
4262
|
async saveInstallConfig() {
|
|
4199
4263
|
const outputDir = this.getOutputDir();
|
|
@@ -4210,7 +4274,7 @@ class Fyn {
|
|
|
4210
4274
|
time: Date.now() + 5,
|
|
4211
4275
|
centralDir,
|
|
4212
4276
|
production: this.production,
|
|
4213
|
-
layout: this.
|
|
4277
|
+
layout: this._options.layout,
|
|
4214
4278
|
shortPkgDir: this._shortPkgDir
|
|
4215
4279
|
// not a good idea to save --run-npm options to install config because
|
|
4216
4280
|
// future fyn install will automatically run them and would be unexpected.
|
|
@@ -4247,7 +4311,7 @@ class Fyn {
|
|
|
4247
4311
|
let pkgFile;
|
|
4248
4312
|
if (options.pkgFile === "package.json") {
|
|
4249
4313
|
let foundDir = null;
|
|
4250
|
-
|
|
4314
|
+
pathUpEach(this._cwd, (path) => {
|
|
4251
4315
|
const testPath = Path.join(path, "package.json");
|
|
4252
4316
|
if (Fs.existsSync(testPath)) {
|
|
4253
4317
|
foundDir = path;
|
|
@@ -4331,6 +4395,46 @@ class Fyn {
|
|
|
4331
4395
|
get showDeprecated() {
|
|
4332
4396
|
return this._options.showDeprecated && "show-deprecated";
|
|
4333
4397
|
}
|
|
4398
|
+
// package.json `fyn.allowScripts` whitelist - maps `name@spec` or
|
|
4399
|
+
// `name@version` to the lifecycle scripts allowed for packages that did not
|
|
4400
|
+
// come from a configured registry (github/git/url tarball deps).
|
|
4401
|
+
get allowScripts() {
|
|
4402
|
+
if (this._allowScripts === void 0 && this._pkg) {
|
|
4403
|
+
this._allowScripts = _.get(this._pkg, ["fyn", "allowScripts"]) || {};
|
|
4404
|
+
}
|
|
4405
|
+
return this._allowScripts || {};
|
|
4406
|
+
}
|
|
4407
|
+
// package.json `fyn.allowTopLevelScripts` - opt-in (default off) to trust the
|
|
4408
|
+
// lifecycle scripts of non-registry packages (github/git/url tarball) that are
|
|
4409
|
+
// declared directly in the top-level package.json, without per-package
|
|
4410
|
+
// `fyn.allowScripts` entries. `true`/`"*"` allows all lifecycle scripts; an
|
|
4411
|
+
// array allows only those script names. Transitive deps stay blocked.
|
|
4412
|
+
get allowTopLevelScripts() {
|
|
4413
|
+
if (this._allowTopLevelScripts === void 0 && this._pkg) {
|
|
4414
|
+
this._allowTopLevelScripts = _.get(this._pkg, ["fyn", "allowTopLevelScripts"]) || false;
|
|
4415
|
+
}
|
|
4416
|
+
return this._allowTopLevelScripts || false;
|
|
4417
|
+
}
|
|
4418
|
+
// Security policy: transitive (non-top-level) dependencies must resolve from
|
|
4419
|
+
// a published registry. git/github/url-tarball sources and unparseable semver
|
|
4420
|
+
// are rejected (hard error). Local (file:/link:/symlink, incl. fynpo
|
|
4421
|
+
// siblings) and `npm:` aliases are accepted. Only the top-level package.json
|
|
4422
|
+
// may declare non-registry deps.
|
|
4423
|
+
//
|
|
4424
|
+
// Default ON. Disable explicitly with the CLI flag
|
|
4425
|
+
// `--no-enforce-registry-deps` or package.json `fyn.enforceRegistryDeps:false`.
|
|
4426
|
+
// Precedence: CLI option (when given) > package.json fyn flag > default (on).
|
|
4427
|
+
get enforceRegistryDeps() {
|
|
4428
|
+
const cliOpt = this._options.enforceRegistryDeps;
|
|
4429
|
+
if (cliOpt !== void 0) {
|
|
4430
|
+
return Boolean(cliOpt);
|
|
4431
|
+
}
|
|
4432
|
+
if (this._enforceRegistryDeps === void 0 && this._pkg) {
|
|
4433
|
+
const pkgOpt = _.get(this._pkg, ["fyn", "enforceRegistryDeps"]);
|
|
4434
|
+
this._enforceRegistryDeps = pkgOpt === void 0 ? true : Boolean(pkgOpt);
|
|
4435
|
+
}
|
|
4436
|
+
return this._enforceRegistryDeps === void 0 ? true : this._enforceRegistryDeps;
|
|
4437
|
+
}
|
|
4334
4438
|
get refreshOptionals() {
|
|
4335
4439
|
return this._options.refreshOptionals;
|
|
4336
4440
|
}
|
|
@@ -4522,6 +4626,7 @@ class Fyn {
|
|
|
4522
4626
|
}
|
|
4523
4627
|
/**
|
|
4524
4628
|
* Scan FV_DIR for modules saved in the ${name}/${version} format
|
|
4629
|
+
*
|
|
4525
4630
|
* @returns {*} pkgs under fv dir with their versions
|
|
4526
4631
|
*/
|
|
4527
4632
|
async loadFvVersions() {
|
|
@@ -4559,7 +4664,7 @@ class Fyn {
|
|
|
4559
4664
|
async createPkgOutDir(dir, keep) {
|
|
4560
4665
|
try {
|
|
4561
4666
|
const r = await Fs.$.mkdirp(dir);
|
|
4562
|
-
if (r ===
|
|
4667
|
+
if (r === void 0 && !keep && dir !== this.getOutputDir()) {
|
|
4563
4668
|
await this.clearPkgOutDir(dir);
|
|
4564
4669
|
}
|
|
4565
4670
|
} catch (err) {
|
|
@@ -4823,6 +4928,373 @@ class LifecycleScripts {
|
|
|
4823
4928
|
module.exports = LifecycleScripts;
|
|
4824
4929
|
|
|
4825
4930
|
|
|
4931
|
+
/***/ }),
|
|
4932
|
+
|
|
4933
|
+
/***/ "./lib/local-exports.ts":
|
|
4934
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
4935
|
+
|
|
4936
|
+
"use strict";
|
|
4937
|
+
|
|
4938
|
+
const Path = __webpack_require__("path");
|
|
4939
|
+
const Fs = __webpack_require__("./lib/util/file-ops.ts");
|
|
4940
|
+
const fynTil = __webpack_require__("./lib/util/fyntil.ts");
|
|
4941
|
+
const { getUrlType } = __webpack_require__("./lib/util/lifecycle-script-policy.ts");
|
|
4942
|
+
const DEFAULT_ROOT_DIR = "_fyn";
|
|
4943
|
+
const MANIFEST_FILE = ".fyn-local-exports.json";
|
|
4944
|
+
const MANIFEST_VERSION = 1;
|
|
4945
|
+
const SAFE_NAME = /^[A-Za-z0-9_][A-Za-z0-9._-]*$/;
|
|
4946
|
+
const posixify = (value) => value.split(Path.sep).join("/");
|
|
4947
|
+
const rootOfEntry = (entry) => entry.root || DEFAULT_ROOT_DIR;
|
|
4948
|
+
const emptyManifest = () => ({ version: MANIFEST_VERSION, exports: {} });
|
|
4949
|
+
const packagePathParts = (name) => {
|
|
4950
|
+
const parts = typeof name === "string" ? name.split("/") : [];
|
|
4951
|
+
const scoped = parts.length === 2 && parts[0][0] === "@";
|
|
4952
|
+
const safe = scoped ? SAFE_NAME.test(parts[0].slice(1)) && SAFE_NAME.test(parts[1]) : parts.length === 1 && SAFE_NAME.test(parts[0]);
|
|
4953
|
+
if (!safe) {
|
|
4954
|
+
throw new Error(`Invalid package name for fyn.localExports: ${name}`);
|
|
4955
|
+
}
|
|
4956
|
+
return parts;
|
|
4957
|
+
};
|
|
4958
|
+
const checkExportName = (packageName, exportName) => {
|
|
4959
|
+
if (!SAFE_NAME.test(exportName) || exportName === "." || exportName === "..") {
|
|
4960
|
+
throw new Error(
|
|
4961
|
+
`Invalid fyn.localExports export name ${JSON.stringify(exportName)} in ${packageName}`
|
|
4962
|
+
);
|
|
4963
|
+
}
|
|
4964
|
+
};
|
|
4965
|
+
const normalizeRootDir = (value, ctx) => {
|
|
4966
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
4967
|
+
throw new Error(`${ctx} must be a non-empty relative directory`);
|
|
4968
|
+
}
|
|
4969
|
+
if (Path.isAbsolute(value) || Path.posix.isAbsolute(value) || Path.win32.isAbsolute(value)) {
|
|
4970
|
+
throw new Error(`${ctx} must not be absolute: ${value}`);
|
|
4971
|
+
}
|
|
4972
|
+
const parts = value.split(/[\\/]+/).filter((part) => part && part !== ".");
|
|
4973
|
+
if (parts.length === 0 || parts.includes("..") || parts.includes("node_modules") || parts.includes(".git")) {
|
|
4974
|
+
throw new Error(`Unsafe ${ctx}: ${value}`);
|
|
4975
|
+
}
|
|
4976
|
+
return parts.join("/");
|
|
4977
|
+
};
|
|
4978
|
+
const nestsWithin = (parent, child) => {
|
|
4979
|
+
const relative = Path.posix.relative(parent, child);
|
|
4980
|
+
return relative !== "" && !relative.startsWith("..");
|
|
4981
|
+
};
|
|
4982
|
+
const assertNoNestedRoots = (roots) => {
|
|
4983
|
+
const uniq = [...new Set(roots)];
|
|
4984
|
+
for (const a of uniq) {
|
|
4985
|
+
for (const b of uniq) {
|
|
4986
|
+
if (a !== b && nestsWithin(a, b)) {
|
|
4987
|
+
throw new Error(`fyn local export directories must not nest: ${a} contains ${b}`);
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
}
|
|
4991
|
+
};
|
|
4992
|
+
const resolveLocalExportsConfig = (pkg) => {
|
|
4993
|
+
const fyn = pkg && pkg.fyn || {};
|
|
4994
|
+
const defaultDir = fyn.localExportsDir === void 0 ? DEFAULT_ROOT_DIR : normalizeRootDir(fyn.localExportsDir, "fyn.localExportsDir");
|
|
4995
|
+
const byPackage = {};
|
|
4996
|
+
const dirs = fyn.localExportsDirs;
|
|
4997
|
+
if (dirs !== void 0 && dirs !== null) {
|
|
4998
|
+
if (typeof dirs !== "object" || Array.isArray(dirs)) {
|
|
4999
|
+
throw new Error("fyn.localExportsDirs must be an object of package name to directory");
|
|
5000
|
+
}
|
|
5001
|
+
for (const name of Object.keys(dirs)) {
|
|
5002
|
+
packagePathParts(name);
|
|
5003
|
+
byPackage[name] = normalizeRootDir(dirs[name], `fyn.localExportsDirs[${JSON.stringify(name)}]`);
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
assertNoNestedRoots([defaultDir, ...Object.values(byPackage)]);
|
|
5007
|
+
return { defaultDir, byPackage };
|
|
5008
|
+
};
|
|
5009
|
+
const rootDirForPackage = (config, name) => config.byPackage && config.byPackage[name] || config.defaultDir;
|
|
5010
|
+
const localExportsScanIgnores = (pkg) => {
|
|
5011
|
+
let config;
|
|
5012
|
+
try {
|
|
5013
|
+
config = resolveLocalExportsConfig(pkg);
|
|
5014
|
+
} catch (err) {
|
|
5015
|
+
return [];
|
|
5016
|
+
}
|
|
5017
|
+
const roots = [config.defaultDir, ...Object.values(config.byPackage)];
|
|
5018
|
+
return [...new Set(roots)].map((root) => `**/${root}`);
|
|
5019
|
+
};
|
|
5020
|
+
const groupByRoot = (exportsMap) => {
|
|
5021
|
+
const byRoot = /* @__PURE__ */ new Map();
|
|
5022
|
+
for (const target of Object.keys(exportsMap)) {
|
|
5023
|
+
const root = rootOfEntry(exportsMap[target]);
|
|
5024
|
+
if (!byRoot.has(root)) {
|
|
5025
|
+
byRoot.set(root, {});
|
|
5026
|
+
}
|
|
5027
|
+
byRoot.get(root)[target] = exportsMap[target];
|
|
5028
|
+
}
|
|
5029
|
+
return byRoot;
|
|
5030
|
+
};
|
|
5031
|
+
const normalizeManifest = (manifest) => {
|
|
5032
|
+
if (!manifest) {
|
|
5033
|
+
return emptyManifest();
|
|
5034
|
+
}
|
|
5035
|
+
if (manifest.version !== MANIFEST_VERSION || !manifest.exports || Array.isArray(manifest.exports) || typeof manifest.exports !== "object") {
|
|
5036
|
+
throw new Error("Invalid fyn local exports manifest");
|
|
5037
|
+
}
|
|
5038
|
+
for (const target of Object.keys(manifest.exports)) {
|
|
5039
|
+
const entry = manifest.exports[target];
|
|
5040
|
+
if (!entry || typeof entry !== "object" || typeof entry.source !== "string" || !entry.source) {
|
|
5041
|
+
throw new Error("Invalid fyn local exports manifest entry");
|
|
5042
|
+
}
|
|
5043
|
+
const root = normalizeRootDir(rootOfEntry(entry), "fyn local exports manifest root");
|
|
5044
|
+
const expectedTarget = posixify(
|
|
5045
|
+
Path.join(root, ...packagePathParts(entry.package), entry.export)
|
|
5046
|
+
);
|
|
5047
|
+
checkExportName(entry.package, entry.export);
|
|
5048
|
+
if (target !== expectedTarget || entry.target !== expectedTarget) {
|
|
5049
|
+
throw new Error(`Invalid fyn local exports manifest target: ${target}`);
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
return manifest;
|
|
5053
|
+
};
|
|
5054
|
+
const checkSourcePath = (packageName, sourcePath) => {
|
|
5055
|
+
if (typeof sourcePath !== "string" || !sourcePath.trim()) {
|
|
5056
|
+
throw new Error(`fyn.localExports source for ${packageName} must be a relative directory`);
|
|
5057
|
+
}
|
|
5058
|
+
if (Path.isAbsolute(sourcePath) || Path.posix.isAbsolute(sourcePath) || Path.win32.isAbsolute(sourcePath)) {
|
|
5059
|
+
throw new Error(`fyn.localExports source for ${packageName} must not be absolute`);
|
|
5060
|
+
}
|
|
5061
|
+
const parts = sourcePath.split(/[\\/]+/);
|
|
5062
|
+
if (parts.includes("..") || parts.includes("node_modules") || parts.includes(".git")) {
|
|
5063
|
+
throw new Error(`Unsafe fyn.localExports source ${sourcePath} in ${packageName}`);
|
|
5064
|
+
}
|
|
5065
|
+
};
|
|
5066
|
+
const isInside = (root, child) => {
|
|
5067
|
+
const relative = Path.relative(root, child);
|
|
5068
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${Path.sep}`);
|
|
5069
|
+
};
|
|
5070
|
+
const readOwnedManifest = async (cwd, root) => {
|
|
5071
|
+
const marker = Path.join(cwd, root, MANIFEST_FILE);
|
|
5072
|
+
try {
|
|
5073
|
+
const value = JSON.parse(await Fs.readFile(marker, "utf8"));
|
|
5074
|
+
return normalizeManifest(value);
|
|
5075
|
+
} catch (err) {
|
|
5076
|
+
if (err.code === "ENOENT" || err.code === "ENOTDIR") {
|
|
5077
|
+
return null;
|
|
5078
|
+
}
|
|
5079
|
+
throw new Error(`Invalid ${root} ownership manifest: ${err.message}`);
|
|
5080
|
+
}
|
|
5081
|
+
};
|
|
5082
|
+
const sourcePathFor = (cwd, entry) => Path.resolve(cwd, entry.source);
|
|
5083
|
+
const linkMatches = async (cwd, entry) => {
|
|
5084
|
+
try {
|
|
5085
|
+
const target = await Fs.realpath(Path.resolve(cwd, entry.target));
|
|
5086
|
+
const source = await Fs.realpath(sourcePathFor(cwd, entry));
|
|
5087
|
+
return target === source;
|
|
5088
|
+
} catch (err) {
|
|
5089
|
+
return false;
|
|
5090
|
+
}
|
|
5091
|
+
};
|
|
5092
|
+
async function makeLocalExportsManifest({ cwd, depInfos, config: exportsConfig }) {
|
|
5093
|
+
const resolved = exportsConfig || { defaultDir: DEFAULT_ROOT_DIR, byPackage: {} };
|
|
5094
|
+
const entries = {};
|
|
5095
|
+
const consumerRoot = await Fs.realpath(cwd);
|
|
5096
|
+
for (const depInfo of depInfos) {
|
|
5097
|
+
if (depInfo.local !== "hard" || getUrlType(depInfo) || depInfo.optFailed || depInfo._removed) {
|
|
5098
|
+
continue;
|
|
5099
|
+
}
|
|
5100
|
+
const config = depInfo.json && depInfo.json.fyn && depInfo.json.fyn.localExports;
|
|
5101
|
+
if (config === void 0 || config === false) {
|
|
5102
|
+
continue;
|
|
5103
|
+
}
|
|
5104
|
+
if (!config || Array.isArray(config) || typeof config !== "object") {
|
|
5105
|
+
throw new Error(`fyn.localExports for ${depInfo.name} must be an object or false`);
|
|
5106
|
+
}
|
|
5107
|
+
const packageParts = packagePathParts(depInfo.name);
|
|
5108
|
+
const packageRoot = await Fs.realpath(depInfo.dir);
|
|
5109
|
+
for (const exportName of Object.keys(config).sort()) {
|
|
5110
|
+
const configuredSource = config[exportName];
|
|
5111
|
+
if (configuredSource === false) {
|
|
5112
|
+
continue;
|
|
5113
|
+
}
|
|
5114
|
+
checkExportName(depInfo.name, exportName);
|
|
5115
|
+
checkSourcePath(depInfo.name, configuredSource);
|
|
5116
|
+
const unresolvedSource = Path.resolve(packageRoot, configuredSource);
|
|
5117
|
+
let source;
|
|
5118
|
+
let sourceStat;
|
|
5119
|
+
try {
|
|
5120
|
+
source = await Fs.realpath(unresolvedSource);
|
|
5121
|
+
sourceStat = await Fs.stat(source);
|
|
5122
|
+
} catch (err) {
|
|
5123
|
+
throw new Error(
|
|
5124
|
+
`fyn.localExports source ${configuredSource} in ${depInfo.name} does not exist`
|
|
5125
|
+
);
|
|
5126
|
+
}
|
|
5127
|
+
if (!isInside(packageRoot, source)) {
|
|
5128
|
+
throw new Error(
|
|
5129
|
+
`fyn.localExports source ${configuredSource} in ${depInfo.name} escapes the package`
|
|
5130
|
+
);
|
|
5131
|
+
}
|
|
5132
|
+
if (!sourceStat.isDirectory()) {
|
|
5133
|
+
throw new Error(
|
|
5134
|
+
`fyn.localExports source ${configuredSource} in ${depInfo.name} is not a directory`
|
|
5135
|
+
);
|
|
5136
|
+
}
|
|
5137
|
+
const root = rootDirForPackage(resolved, depInfo.name);
|
|
5138
|
+
const target = posixify(Path.join(root, ...packageParts, exportName));
|
|
5139
|
+
const relativeSource = posixify(Path.relative(consumerRoot, source));
|
|
5140
|
+
const prior = entries[target];
|
|
5141
|
+
if (prior && (prior.source !== relativeSource || prior.version !== depInfo.version)) {
|
|
5142
|
+
throw new Error(
|
|
5143
|
+
`Local export destination collision for ${depInfo.name}@${depInfo.version}: ${target}`
|
|
5144
|
+
);
|
|
5145
|
+
}
|
|
5146
|
+
const targetPath = Path.resolve(consumerRoot, target);
|
|
5147
|
+
entries[target] = {
|
|
5148
|
+
package: depInfo.name,
|
|
5149
|
+
version: depInfo.version,
|
|
5150
|
+
export: exportName,
|
|
5151
|
+
source: relativeSource,
|
|
5152
|
+
target,
|
|
5153
|
+
root,
|
|
5154
|
+
linkTarget: fynTil.isWin32 ? source : posixify(Path.relative(Path.dirname(targetPath), source))
|
|
5155
|
+
};
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
const sortedEntries = {};
|
|
5159
|
+
for (const target of Object.keys(entries).sort()) {
|
|
5160
|
+
sortedEntries[target] = entries[target];
|
|
5161
|
+
}
|
|
5162
|
+
return { version: MANIFEST_VERSION, exports: sortedEntries };
|
|
5163
|
+
}
|
|
5164
|
+
async function rootNeedsInstall(cwd, root, exportsForRoot) {
|
|
5165
|
+
const rootManifest = { version: MANIFEST_VERSION, exports: exportsForRoot };
|
|
5166
|
+
let realized;
|
|
5167
|
+
try {
|
|
5168
|
+
realized = await readOwnedManifest(cwd, root);
|
|
5169
|
+
} catch (err) {
|
|
5170
|
+
return true;
|
|
5171
|
+
}
|
|
5172
|
+
if (!realized || JSON.stringify(realized) !== JSON.stringify(rootManifest)) {
|
|
5173
|
+
return true;
|
|
5174
|
+
}
|
|
5175
|
+
for (const target of Object.keys(exportsForRoot)) {
|
|
5176
|
+
if (!await linkMatches(cwd, exportsForRoot[target])) {
|
|
5177
|
+
return true;
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
return false;
|
|
5181
|
+
}
|
|
5182
|
+
async function localExportsNeedInstall({ cwd, manifest }) {
|
|
5183
|
+
const desired = normalizeManifest(manifest);
|
|
5184
|
+
const byRoot = groupByRoot(desired.exports);
|
|
5185
|
+
if (byRoot.size === 0) {
|
|
5186
|
+
try {
|
|
5187
|
+
return Boolean(await readOwnedManifest(cwd, DEFAULT_ROOT_DIR));
|
|
5188
|
+
} catch (err) {
|
|
5189
|
+
return false;
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
for (const [root, exportsForRoot] of byRoot) {
|
|
5193
|
+
if (await rootNeedsInstall(cwd, root, exportsForRoot)) {
|
|
5194
|
+
return true;
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
return false;
|
|
5198
|
+
}
|
|
5199
|
+
async function reconcileOneRoot(cwd, root, exportsForRoot) {
|
|
5200
|
+
const targets = Object.keys(exportsForRoot);
|
|
5201
|
+
const rootPath = Path.join(cwd, root);
|
|
5202
|
+
let owned;
|
|
5203
|
+
try {
|
|
5204
|
+
owned = await readOwnedManifest(cwd, root);
|
|
5205
|
+
} catch (err) {
|
|
5206
|
+
if (targets.length === 0) {
|
|
5207
|
+
return;
|
|
5208
|
+
}
|
|
5209
|
+
throw err;
|
|
5210
|
+
}
|
|
5211
|
+
if (targets.length === 0) {
|
|
5212
|
+
if (owned) {
|
|
5213
|
+
await Fs.$.rimraf(rootPath);
|
|
5214
|
+
}
|
|
5215
|
+
return;
|
|
5216
|
+
}
|
|
5217
|
+
if (await Fs.exists(rootPath) && !owned) {
|
|
5218
|
+
throw new Error(`Refusing to modify ${rootPath} without a fyn ownership manifest`);
|
|
5219
|
+
}
|
|
5220
|
+
if (owned && !await rootNeedsInstall(cwd, root, exportsForRoot)) {
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5223
|
+
const desiredRootManifest = { version: MANIFEST_VERSION, exports: exportsForRoot };
|
|
5224
|
+
const suffix = `${process.pid}-${Date.now()}`;
|
|
5225
|
+
const staging = `${rootPath}.fyn-tmp-${suffix}`;
|
|
5226
|
+
const backup = `${rootPath}.fyn-old-${suffix}`;
|
|
5227
|
+
let movedExisting = false;
|
|
5228
|
+
try {
|
|
5229
|
+
await Fs.$.rimraf(staging);
|
|
5230
|
+
await Fs.$.mkdirp(staging);
|
|
5231
|
+
for (const target of targets) {
|
|
5232
|
+
const entry = exportsForRoot[target];
|
|
5233
|
+
const stagedTarget = Path.join(staging, Path.relative(root, target));
|
|
5234
|
+
const source = sourcePathFor(cwd, entry);
|
|
5235
|
+
const stat = await Fs.stat(source);
|
|
5236
|
+
if (!stat.isDirectory()) {
|
|
5237
|
+
throw new Error(`Local export source is not a directory: ${source}`);
|
|
5238
|
+
}
|
|
5239
|
+
await Fs.$.mkdirp(Path.dirname(stagedTarget));
|
|
5240
|
+
await fynTil.symlinkDir(stagedTarget, source, !fynTil.isWin32);
|
|
5241
|
+
}
|
|
5242
|
+
await Fs.writeFile(
|
|
5243
|
+
Path.join(staging, MANIFEST_FILE),
|
|
5244
|
+
`${JSON.stringify(desiredRootManifest, null, 2)}
|
|
5245
|
+
`
|
|
5246
|
+
);
|
|
5247
|
+
if (owned) {
|
|
5248
|
+
await Fs.$.rimraf(backup);
|
|
5249
|
+
await Fs.rename(rootPath, backup);
|
|
5250
|
+
movedExisting = true;
|
|
5251
|
+
}
|
|
5252
|
+
await Fs.$.mkdirp(Path.dirname(rootPath));
|
|
5253
|
+
await Fs.rename(staging, rootPath);
|
|
5254
|
+
if (movedExisting) {
|
|
5255
|
+
movedExisting = false;
|
|
5256
|
+
await Fs.$.rimraf(backup);
|
|
5257
|
+
}
|
|
5258
|
+
} catch (err) {
|
|
5259
|
+
await Fs.$.rimraf(staging);
|
|
5260
|
+
if (movedExisting) {
|
|
5261
|
+
await Fs.$.rimraf(rootPath);
|
|
5262
|
+
await Fs.rename(backup, rootPath);
|
|
5263
|
+
}
|
|
5264
|
+
throw err;
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
async function reconcileLocalExports({ cwd, manifest, previous }) {
|
|
5268
|
+
const desired = normalizeManifest(manifest);
|
|
5269
|
+
const byRoot = groupByRoot(desired.exports);
|
|
5270
|
+
const allRoots = /* @__PURE__ */ new Set([DEFAULT_ROOT_DIR, ...byRoot.keys()]);
|
|
5271
|
+
if (previous) {
|
|
5272
|
+
for (const root of groupByRoot(normalizeManifest(previous).exports).keys()) {
|
|
5273
|
+
allRoots.add(root);
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5276
|
+
for (const root of [...allRoots].sort()) {
|
|
5277
|
+
if (!byRoot.has(root)) {
|
|
5278
|
+
await reconcileOneRoot(cwd, root, {});
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
for (const root of [...byRoot.keys()].sort()) {
|
|
5282
|
+
await reconcileOneRoot(cwd, root, byRoot.get(root));
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
async function syncLocalExports(options) {
|
|
5286
|
+
return reconcileLocalExports(options);
|
|
5287
|
+
}
|
|
5288
|
+
module.exports = {
|
|
5289
|
+
makeLocalExportsManifest,
|
|
5290
|
+
reconcileLocalExports,
|
|
5291
|
+
syncLocalExports,
|
|
5292
|
+
localExportsNeedInstall,
|
|
5293
|
+
resolveLocalExportsConfig,
|
|
5294
|
+
localExportsScanIgnores
|
|
5295
|
+
};
|
|
5296
|
+
|
|
5297
|
+
|
|
4826
5298
|
/***/ }),
|
|
4827
5299
|
|
|
4828
5300
|
/***/ "./lib/local-pkg-builder.ts":
|
|
@@ -4891,7 +5363,12 @@ class LocalPkgBuilder {
|
|
|
4891
5363
|
}, {});
|
|
4892
5364
|
logger.debug("local pkgs for build all paths", allPaths, "uniq paths", uniqPaths);
|
|
4893
5365
|
for (const path of uniqPaths) {
|
|
4894
|
-
|
|
5366
|
+
try {
|
|
5367
|
+
await this.addItem(byPathLookup[path]);
|
|
5368
|
+
} catch (error) {
|
|
5369
|
+
this._startError = error;
|
|
5370
|
+
break;
|
|
5371
|
+
}
|
|
4895
5372
|
}
|
|
4896
5373
|
logger.debug("resolving build local _started promise");
|
|
4897
5374
|
this._started.resolve();
|
|
@@ -4930,6 +5407,9 @@ class LocalPkgBuilder {
|
|
|
4930
5407
|
logger.debug("waiting for local build item start, fullPath:", fullPath);
|
|
4931
5408
|
await this._started.promise;
|
|
4932
5409
|
}
|
|
5410
|
+
if (this._startError) {
|
|
5411
|
+
return { error: this._startError };
|
|
5412
|
+
}
|
|
4933
5413
|
const x = this._waitItems[fullPath];
|
|
4934
5414
|
if (x && x.promise) {
|
|
4935
5415
|
logger.debug("waiting for build local item", fullPath, x);
|
|
@@ -5386,6 +5866,7 @@ module.exports = PkgBinLinker;
|
|
|
5386
5866
|
"use strict";
|
|
5387
5867
|
|
|
5388
5868
|
const Fs = __webpack_require__("./lib/util/file-ops.ts");
|
|
5869
|
+
const Path = __webpack_require__("path");
|
|
5389
5870
|
const PkgBinLinkerBase = __webpack_require__("./lib/pkg-bin-linker-base.ts");
|
|
5390
5871
|
const CYGWIN_LINK = `#!/bin/sh
|
|
5391
5872
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
@@ -5441,6 +5922,31 @@ class PkgBinLinkerWin32 extends PkgBinLinkerBase {
|
|
|
5441
5922
|
await this._unlinkFile(symlink);
|
|
5442
5923
|
await this._unlinkFile(symlink + ".cmd");
|
|
5443
5924
|
}
|
|
5925
|
+
// Extract the {{TARGET}} path baked into a generated .cmd wrapper (the path
|
|
5926
|
+
// after `%~dp0\`, ignoring the node.exe reference).
|
|
5927
|
+
async _readBinLinkTarget(symlink) {
|
|
5928
|
+
const content = (await Fs.readFile(symlink + ".cmd")).toString();
|
|
5929
|
+
const matches = [...content.matchAll(/%~dp0[\\/]+([^"\r\n]+)/g)].map((m) => m[1]);
|
|
5930
|
+
return matches.find((m) => m !== "node.exe");
|
|
5931
|
+
}
|
|
5932
|
+
//
|
|
5933
|
+
// Platform specific: the "bin" is a pair of regular script files (cygwin +
|
|
5934
|
+
// .cmd), not a symlink, so the base _cleanLink's Fs.access on the wrapper
|
|
5935
|
+
// always succeeds and never cleans a stale bin. Instead, read the wrapper and
|
|
5936
|
+
// remove it only if the target it points to no longer exists.
|
|
5937
|
+
//
|
|
5938
|
+
async _cleanLink(sym) {
|
|
5939
|
+
const symlink = Path.join(this._binDir, sym);
|
|
5940
|
+
try {
|
|
5941
|
+
const target = await this._readBinLinkTarget(symlink);
|
|
5942
|
+
if (target && await Fs.exists(Path.join(this._binDir, target))) {
|
|
5943
|
+
return false;
|
|
5944
|
+
}
|
|
5945
|
+
} catch (e) {
|
|
5946
|
+
}
|
|
5947
|
+
await this._rmBinLink(symlink);
|
|
5948
|
+
return true;
|
|
5949
|
+
}
|
|
5444
5950
|
async _readBinLinks() {
|
|
5445
5951
|
return (await Fs.readdir(this._binDir)).filter((x) => !x.endsWith(".cmd"));
|
|
5446
5952
|
}
|
|
@@ -5636,6 +6142,7 @@ const Fs = __webpack_require__("./lib/util/file-ops.ts");
|
|
|
5636
6142
|
const _ = __webpack_require__("./node_modules/.f/_/lodash/4.17.21/lodash/lodash.min.js");
|
|
5637
6143
|
const chalk = __webpack_require__("./node_modules/.f/_/chalk/4.1.2/chalk/source/index.js");
|
|
5638
6144
|
const simpleSemverCompare = (__webpack_require__("./lib/util/semver.ts").simpleCompare);
|
|
6145
|
+
const Semver = __webpack_require__("./node_modules/.f/_/semver/7.8.0/semver/index.js");
|
|
5639
6146
|
const Yaml = __webpack_require__("./node_modules/.f/_/yamljs/0.3.0/yamljs/lib/Yaml.js");
|
|
5640
6147
|
const sortObjKeys = __webpack_require__("./lib/util/sort-obj-keys.ts");
|
|
5641
6148
|
const {
|
|
@@ -5830,7 +6337,10 @@ class PkgDepLocker {
|
|
|
5830
6337
|
const versions = {};
|
|
5831
6338
|
_.each(sorted, (version) => {
|
|
5832
6339
|
const vpkg = locked[version];
|
|
5833
|
-
|
|
6340
|
+
const isLocalVpkg = vpkg && vpkg.$ === "local";
|
|
6341
|
+
const badVersionKey = !isLocalVpkg && !Semver.valid(version);
|
|
6342
|
+
const badDeps = vpkg && vpkg.dependencies && !this._depsResolvable(vpkg.dependencies);
|
|
6343
|
+
if (!_.isEmpty(vpkg) && vpkg._valid !== false && !badVersionKey && !badDeps) {
|
|
5834
6344
|
if (vpkg.$ === "local") {
|
|
5835
6345
|
vpkg.local = true;
|
|
5836
6346
|
vpkg.dist = {
|
|
@@ -5853,6 +6363,15 @@ class PkgDepLocker {
|
|
|
5853
6363
|
}
|
|
5854
6364
|
versions[version] = vpkg;
|
|
5855
6365
|
} else {
|
|
6366
|
+
if (badVersionKey) {
|
|
6367
|
+
logger.error(
|
|
6368
|
+
`lockfile entry for ${item.name} has invalid version key "${version}" - ignoring and re-resolving from registry`
|
|
6369
|
+
);
|
|
6370
|
+
} else if (badDeps) {
|
|
6371
|
+
logger.error(
|
|
6372
|
+
`lockfile entry ${item.name}@${version} has dependencies not satisfiable within the lock (corrupt lock) - ignoring and re-resolving from registry`
|
|
6373
|
+
);
|
|
6374
|
+
}
|
|
5856
6375
|
valid = false;
|
|
5857
6376
|
}
|
|
5858
6377
|
});
|
|
@@ -5874,6 +6393,37 @@ class PkgDepLocker {
|
|
|
5874
6393
|
}
|
|
5875
6394
|
return valid && locked;
|
|
5876
6395
|
}
|
|
6396
|
+
//
|
|
6397
|
+
// Check that every recorded dependency pin can be resolved within the lock data.
|
|
6398
|
+
// A healthy fyn lock is self-contained: every locked package's dependency specs are
|
|
6399
|
+
// satisfied by some version that also has a lock entry. A pin that points at a version
|
|
6400
|
+
// absent from the lock signals corruption (e.g. a version block's deps got swapped
|
|
6401
|
+
// during a rebase/merge), so the entry must be dropped and re-resolved.
|
|
6402
|
+
//
|
|
6403
|
+
_depsResolvable(deps) {
|
|
6404
|
+
for (const depName in deps) {
|
|
6405
|
+
if (!this._isDepResolvable(depName, deps[depName])) {
|
|
6406
|
+
return false;
|
|
6407
|
+
}
|
|
6408
|
+
}
|
|
6409
|
+
return true;
|
|
6410
|
+
}
|
|
6411
|
+
_isDepResolvable(depName, spec) {
|
|
6412
|
+
const depLock = this._lockData[depName];
|
|
6413
|
+
if (!depLock) return true;
|
|
6414
|
+
let versions;
|
|
6415
|
+
let rangeMap;
|
|
6416
|
+
if (depLock.versions) {
|
|
6417
|
+
versions = Object.keys(depLock.versions);
|
|
6418
|
+
rangeMap = depLock[LOCK_RSEMVERS] || {};
|
|
6419
|
+
} else {
|
|
6420
|
+
versions = Object.keys(depLock).filter((k) => !k.startsWith("_"));
|
|
6421
|
+
rangeMap = depLock._ || {};
|
|
6422
|
+
}
|
|
6423
|
+
if (Object.prototype.hasOwnProperty.call(rangeMap, spec)) return true;
|
|
6424
|
+
if (!Semver.validRange(spec)) return true;
|
|
6425
|
+
return versions.some((v) => Semver.valid(v) && Semver.satisfies(v, spec));
|
|
6426
|
+
}
|
|
5877
6427
|
/**
|
|
5878
6428
|
* Set the package.json's dependencies items and check if they changed from
|
|
5879
6429
|
* lock data.
|
|
@@ -5881,6 +6431,7 @@ class PkgDepLocker {
|
|
|
5881
6431
|
* - dependencies
|
|
5882
6432
|
* - optionalDependencies
|
|
5883
6433
|
* - devDependencies
|
|
6434
|
+
*
|
|
5884
6435
|
* @param {*} pkgDepItems - dep items generated by makePkgDepItems in pkg-dep-resolver.js
|
|
5885
6436
|
*
|
|
5886
6437
|
* @returns {*} none
|
|
@@ -6044,6 +6595,14 @@ class PkgDepLocker {
|
|
|
6044
6595
|
if (!Path.isAbsolute(filename)) filename = Path.resolve(filename);
|
|
6045
6596
|
const data = (await Fs.readFile(filename)).toString();
|
|
6046
6597
|
this._shaSum = this.shasum(data);
|
|
6598
|
+
if (/^(<<<<<<<|=======|>>>>>>>)/m.test(data)) {
|
|
6599
|
+
logger.error(
|
|
6600
|
+
`lockfile ${filename} has git conflict markers - ignoring it and re-resolving from registry`
|
|
6601
|
+
);
|
|
6602
|
+
this._shaSum = Date.now();
|
|
6603
|
+
this._lockData = {};
|
|
6604
|
+
return false;
|
|
6605
|
+
}
|
|
6047
6606
|
this._lockData = Yaml.parse(data);
|
|
6048
6607
|
const basedir = Path.dirname(filename);
|
|
6049
6608
|
this._fullLocalPath(basedir);
|
|
@@ -6102,6 +6661,7 @@ const logFormat = __webpack_require__("./lib/util/log-format.ts");
|
|
|
6102
6661
|
const { LONG_WAIT_META } = __webpack_require__("./lib/log-items.ts");
|
|
6103
6662
|
const { checkPkgOsCpu, relativePath, unSlashNpmScope } = __webpack_require__("./lib/util/fyntil.ts");
|
|
6104
6663
|
const { getDepSection, makeDepStep } = __webpack_require__("./node_modules/.f/_/@fynpo/base/1.1.22-fynlocal_h/@fynpo/base/dist/index.js");
|
|
6664
|
+
const { violatesRegistryPolicy } = __webpack_require__("./lib/util/registry-dep-policy.ts");
|
|
6105
6665
|
const xaa = __webpack_require__("./lib/util/xaa.ts");
|
|
6106
6666
|
const { AggregateError } = __webpack_require__("./node_modules/.f/_/@jchip/error/1.0.3/@jchip/error/dist/index.js");
|
|
6107
6667
|
const Promise = __webpack_require__("./node_modules/.f/_/aveazul/1.1.0/aveazul/cjs-entry.cjs");
|
|
@@ -6368,6 +6928,31 @@ class PkgDepResolver {
|
|
|
6368
6928
|
}
|
|
6369
6929
|
return semver;
|
|
6370
6930
|
}
|
|
6931
|
+
/**
|
|
6932
|
+
* Enforce the `fyn.enforceRegistryDeps` policy on a transitive dep item.
|
|
6933
|
+
* Throws (aborting the install) when the item is a non-registry source or has
|
|
6934
|
+
* an unparseable semver.
|
|
6935
|
+
*
|
|
6936
|
+
* @param {object} item - the (transitive) dep item being added
|
|
6937
|
+
* @param {object} parent - the dep item that requires `item`
|
|
6938
|
+
* @returns {void}
|
|
6939
|
+
*/
|
|
6940
|
+
_enforceRegistryDep(item, parent) {
|
|
6941
|
+
const violation = violatesRegistryPolicy(item);
|
|
6942
|
+
if (!violation) {
|
|
6943
|
+
return;
|
|
6944
|
+
}
|
|
6945
|
+
const depId = `${item.name}@${item.semver}`;
|
|
6946
|
+
let reason = `has an invalid/unparseable version "${violation.semver}"`;
|
|
6947
|
+
if (violation.kind === "url") {
|
|
6948
|
+
reason = `is from a non-registry source (${violation.urlType})`;
|
|
6949
|
+
} else if (violation.kind === "local") {
|
|
6950
|
+
reason = `is a local dependency declared below a non-registry source (${violation.urlType})`;
|
|
6951
|
+
}
|
|
6952
|
+
throw new Error(
|
|
6953
|
+
`fyn.enforceRegistryDeps: transitive dependency "${depId}" required by "${parent.id}" ${reason}. Only the top-level package.json may use git/github/URL dependencies - transitive dependencies must come from a registry.`
|
|
6954
|
+
);
|
|
6955
|
+
}
|
|
6371
6956
|
/**
|
|
6372
6957
|
* create the dep relation items for a package
|
|
6373
6958
|
*
|
|
@@ -6403,6 +6988,9 @@ class PkgDepResolver {
|
|
|
6403
6988
|
deepResolve
|
|
6404
6989
|
};
|
|
6405
6990
|
const newItem = new DepItem(opt, depItem);
|
|
6991
|
+
if (this._fyn.enforceRegistryDeps && depItem.depth >= 1) {
|
|
6992
|
+
this._enforceRegistryDep(newItem, depItem);
|
|
6993
|
+
}
|
|
6406
6994
|
if (noPrefetch !== true) this.prefetchMeta(newItem);
|
|
6407
6995
|
items.push(newItem);
|
|
6408
6996
|
}
|
|
@@ -6574,6 +7162,15 @@ class PkgDepResolver {
|
|
|
6574
7162
|
}
|
|
6575
7163
|
}
|
|
6576
7164
|
const metaJson = meta.versions[resolved];
|
|
7165
|
+
const localFromMeta = meta.local || metaJson.local;
|
|
7166
|
+
if (localFromMeta) {
|
|
7167
|
+
if (!item.localType) {
|
|
7168
|
+
item.localType = localFromMeta;
|
|
7169
|
+
}
|
|
7170
|
+
if (this._fyn.enforceRegistryDeps && item.parent.depth >= 1) {
|
|
7171
|
+
this._enforceRegistryDep(item, item.parent);
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
6577
7174
|
const platformCheck = () => {
|
|
6578
7175
|
const sysCheck2 = checkPkgOsCpu(metaJson);
|
|
6579
7176
|
if (sysCheck2 !== true) {
|
|
@@ -6630,11 +7227,7 @@ class PkgDepResolver {
|
|
|
6630
7227
|
pkgV.hasI = 1;
|
|
6631
7228
|
}
|
|
6632
7229
|
}
|
|
6633
|
-
const localFromMeta = meta.local || metaJson.local;
|
|
6634
7230
|
if (localFromMeta) {
|
|
6635
|
-
if (!item.localType) {
|
|
6636
|
-
item.localType = localFromMeta;
|
|
6637
|
-
}
|
|
6638
7231
|
pkgV.local = item.localType;
|
|
6639
7232
|
item.fullPath = pkgV.dir = pkgV.dist.fullPath;
|
|
6640
7233
|
pkgV.str = meta.jsonStr;
|
|
@@ -7108,9 +7701,9 @@ ${item.depPath.join(" > ")}`
|
|
|
7108
7701
|
throw new Error(failMetaMsg(item.name));
|
|
7109
7702
|
}
|
|
7110
7703
|
const updated = this._fyn.depLocker.update(item, meta);
|
|
7111
|
-
const
|
|
7112
|
-
if (
|
|
7113
|
-
return
|
|
7704
|
+
const resolved = this._resolveWithMeta({ item, meta: updated, force: false, noLocal: true });
|
|
7705
|
+
if (resolved) {
|
|
7706
|
+
return resolved;
|
|
7114
7707
|
}
|
|
7115
7708
|
logger.debug(
|
|
7116
7709
|
`cached meta for ${item.name} has no version satisfying ${item.semver}; refetching from registry`
|
|
@@ -7128,7 +7721,7 @@ ${item.depPath.join(" > ")}`
|
|
|
7128
7721
|
});
|
|
7129
7722
|
});
|
|
7130
7723
|
}).catch((err) => {
|
|
7131
|
-
if (item.dsrc
|
|
7724
|
+
if (!item.dsrc || !item.dsrc.includes("opt")) {
|
|
7132
7725
|
if (err.message.includes("Unable to retrieve meta")) {
|
|
7133
7726
|
throw err;
|
|
7134
7727
|
} else {
|
|
@@ -7186,7 +7779,13 @@ class PkgDistExtractor {
|
|
|
7186
7779
|
});
|
|
7187
7780
|
this._fyn = options.fyn;
|
|
7188
7781
|
this._promiseQ.on("done", (x) => this.done(x));
|
|
7189
|
-
this._promiseQ.on("failItem", (x) =>
|
|
7782
|
+
this._promiseQ.on("failItem", (x) => {
|
|
7783
|
+
logger.error("dist extractor failed item", x.error);
|
|
7784
|
+
const listener = _.get(x, "item.listener");
|
|
7785
|
+
if (listener) {
|
|
7786
|
+
setTimeout(() => listener.emit("fail", x.error), 0);
|
|
7787
|
+
}
|
|
7788
|
+
});
|
|
7190
7789
|
}
|
|
7191
7790
|
addPkgDist(data) {
|
|
7192
7791
|
this._promiseQ.addItem(data);
|
|
@@ -7244,6 +7843,10 @@ class PkgDistExtractor {
|
|
|
7244
7843
|
} else {
|
|
7245
7844
|
const json = await this._fyn.ensureProperPkgDir(pkg, fullOutDir);
|
|
7246
7845
|
if (json) {
|
|
7846
|
+
if (data.listener) {
|
|
7847
|
+
const listener = data.listener;
|
|
7848
|
+
setTimeout(() => listener.emit("done", json), 0);
|
|
7849
|
+
}
|
|
7247
7850
|
return json;
|
|
7248
7851
|
}
|
|
7249
7852
|
await this._fyn.createPkgOutDir(fullOutDir);
|
|
@@ -7451,6 +8054,7 @@ class PkgDistFetcher {
|
|
|
7451
8054
|
}
|
|
7452
8055
|
/**
|
|
7453
8056
|
* Check if pkg already has a copy extracted to node_modules
|
|
8057
|
+
*
|
|
7454
8058
|
* @param {*} pkg - package info
|
|
7455
8059
|
* @returns {*} pkg in FV_DIR and its package.json
|
|
7456
8060
|
*/
|
|
@@ -7533,8 +8137,14 @@ const logger = __webpack_require__("./lib/logger.ts");
|
|
|
7533
8137
|
const logFormat = __webpack_require__("./lib/util/log-format.ts");
|
|
7534
8138
|
const fynTil = __webpack_require__("./lib/util/fyntil.ts");
|
|
7535
8139
|
const hardLinkDir = __webpack_require__("./lib/util/hard-link-dir.ts");
|
|
8140
|
+
const {
|
|
8141
|
+
makeLocalExportsManifest,
|
|
8142
|
+
reconcileLocalExports,
|
|
8143
|
+
resolveLocalExportsConfig
|
|
8144
|
+
} = __webpack_require__("./lib/local-exports.ts");
|
|
7536
8145
|
const { INSTALL_PACKAGE } = __webpack_require__("./lib/log-items.ts");
|
|
7537
8146
|
const { runNpmScript } = __webpack_require__("./lib/util/run-npm-script.ts");
|
|
8147
|
+
const { evaluateScriptPolicy, isScriptAllowed } = __webpack_require__("./lib/util/lifecycle-script-policy.ts");
|
|
7538
8148
|
const xaa = __webpack_require__("./lib/util/xaa.ts");
|
|
7539
8149
|
const { AggregateError } = __webpack_require__("./node_modules/.f/_/@jchip/error/1.0.3/@jchip/error/dist/index.js");
|
|
7540
8150
|
const { RESOLVE_ORDER, RSEMVERS, LOCK_RSEMVERS, SEMVER } = __webpack_require__("./lib/symbols.ts");
|
|
@@ -7616,7 +8226,7 @@ class PkgInstaller {
|
|
|
7616
8226
|
} catch (err) {
|
|
7617
8227
|
if (err.code === "EPERM") {
|
|
7618
8228
|
const st = await Fs.stat(pkgJsonFp);
|
|
7619
|
-
await Fs.chmod(pkgJsonFp, st.mode
|
|
8229
|
+
await Fs.chmod(pkgJsonFp, st.mode | 384);
|
|
7620
8230
|
await Fs.writeFile(pkgJsonFp, `${outputStr}
|
|
7621
8231
|
`);
|
|
7622
8232
|
}
|
|
@@ -7688,7 +8298,7 @@ class PkgInstaller {
|
|
|
7688
8298
|
if (depInfo._removing) return;
|
|
7689
8299
|
depInfo._removing = true;
|
|
7690
8300
|
const optReqs = depInfo.requests.map((req) => {
|
|
7691
|
-
return req.reverse().find((r) => r.startsWith("opt"));
|
|
8301
|
+
return req.slice().reverse().find((r) => r.startsWith("opt"));
|
|
7692
8302
|
});
|
|
7693
8303
|
const failedId = `${depInfo.name}@${depInfo.version}`;
|
|
7694
8304
|
for (const r of optReqs) {
|
|
@@ -7811,12 +8421,27 @@ class PkgInstaller {
|
|
|
7811
8421
|
if (this._fyn.showDeprecated && _.isEmpty(warned)) {
|
|
7812
8422
|
logger.info(chalk.green("HOORAY!!! None of your dependencies are marked deprecated."));
|
|
7813
8423
|
}
|
|
7814
|
-
}).then(() => this._saveLockData()).then(() => {
|
|
8424
|
+
}).then(() => this._installLocalExports()).then(() => this._saveLockData()).then(() => {
|
|
7815
8425
|
logger.info(`${chalk.green("done install")} ${logFormat.time(Date.now() - start)}`);
|
|
7816
8426
|
}).finally(() => {
|
|
7817
8427
|
logger.removeItem(INSTALL_PACKAGE);
|
|
7818
8428
|
});
|
|
7819
8429
|
}
|
|
8430
|
+
async _installLocalExports() {
|
|
8431
|
+
const pkgsData = this._data.getPkgsData();
|
|
8432
|
+
const config = resolveLocalExportsConfig(this._fyn._pkg);
|
|
8433
|
+
const manifest = await makeLocalExportsManifest({
|
|
8434
|
+
cwd: this._fyn._cwd,
|
|
8435
|
+
config,
|
|
8436
|
+
depInfos: this.toLink.filter((depInfo) => {
|
|
8437
|
+
const versions = pkgsData[depInfo.name];
|
|
8438
|
+
return versions && versions[depInfo.version] === depInfo;
|
|
8439
|
+
})
|
|
8440
|
+
});
|
|
8441
|
+
const previous = this._fyn._installConfig.localExports;
|
|
8442
|
+
await reconcileLocalExports({ cwd: this._fyn._cwd, manifest, previous });
|
|
8443
|
+
this._fyn.setLocalExports(manifest);
|
|
8444
|
+
}
|
|
7820
8445
|
async _buildLocalPkg(depInfo) {
|
|
7821
8446
|
if (this._fyn._options.buildLocal && this._fyn._localPkgBuilder) {
|
|
7822
8447
|
const itemRes = await this._fyn._localPkgBuilder.waitForItem(depInfo.dir);
|
|
@@ -7848,25 +8473,60 @@ class PkgInstaller {
|
|
|
7848
8473
|
}
|
|
7849
8474
|
json._fyn = {};
|
|
7850
8475
|
const scripts = json.scripts || {};
|
|
8476
|
+
const scriptPolicy = evaluateScriptPolicy(depInfo, this._fyn.allowScripts, {
|
|
8477
|
+
allowTopLevel: this._fyn.allowTopLevelScripts
|
|
8478
|
+
});
|
|
8479
|
+
const blockedScripts = [];
|
|
8480
|
+
const isAllowed = (scriptName) => {
|
|
8481
|
+
if (isScriptAllowed(scriptPolicy, scriptName)) {
|
|
8482
|
+
return true;
|
|
8483
|
+
}
|
|
8484
|
+
blockedScripts.push(scriptName);
|
|
8485
|
+
return false;
|
|
8486
|
+
};
|
|
7851
8487
|
const hasPI = json.hasPI || Boolean(scripts.preinstall);
|
|
7852
8488
|
const piExed = Boolean(depInfo.preinstall);
|
|
7853
8489
|
if (!piExed && hasPI) {
|
|
7854
8490
|
if (depInfo.preInstalled) {
|
|
7855
8491
|
json._fyn.preinstall = true;
|
|
7856
|
-
} else {
|
|
8492
|
+
} else if (isAllowed("preinstall")) {
|
|
7857
8493
|
logger.debug("adding preinstall step for", depInfo.dir);
|
|
7858
8494
|
this.preInstall.push(depInfo);
|
|
7859
8495
|
}
|
|
7860
8496
|
}
|
|
7861
8497
|
this.toLink.push(depInfo);
|
|
7862
8498
|
const install = ["install", "postinstall"].filter((x) => {
|
|
7863
|
-
return Boolean(scripts[x]) && !json._fyn[x];
|
|
8499
|
+
return Boolean(scripts[x]) && !json._fyn[x] && isAllowed(x);
|
|
7864
8500
|
});
|
|
7865
8501
|
if (install.length > 0) {
|
|
7866
8502
|
logger.debug("adding install step for", depInfo.dir, install);
|
|
7867
8503
|
depInfo.install = install;
|
|
7868
8504
|
this.postInstall.push(depInfo);
|
|
7869
8505
|
}
|
|
8506
|
+
if (blockedScripts.length > 0) {
|
|
8507
|
+
this._warnBlockedScripts(depInfo, scriptPolicy, blockedScripts);
|
|
8508
|
+
}
|
|
8509
|
+
}
|
|
8510
|
+
_warnBlockedScripts(depInfo, policy, blocked) {
|
|
8511
|
+
const id = logFormat.pkgId(depInfo);
|
|
8512
|
+
logger.warn(
|
|
8513
|
+
`${chalk.black.bgYellow("WARN")} ${chalk.magenta("scripts blocked")} ${id}`,
|
|
8514
|
+
chalk.yellow(
|
|
8515
|
+
`skipped lifecycle [${blocked.join(", ")}] for non-registry (${policy.urlType}) package - not in fyn.allowScripts`
|
|
8516
|
+
)
|
|
8517
|
+
);
|
|
8518
|
+
logger.verbose(
|
|
8519
|
+
chalk.blue(" To allow, add to package.json:"),
|
|
8520
|
+
chalk.cyan(
|
|
8521
|
+
`"fyn": { "allowScripts": { "${policy.key}": [${blocked.map((s) => `"${s}"`).join(", ")}] } }`
|
|
8522
|
+
)
|
|
8523
|
+
);
|
|
8524
|
+
if (policy.topLevel) {
|
|
8525
|
+
logger.verbose(
|
|
8526
|
+
chalk.blue(" Or trust all direct deps' scripts with:"),
|
|
8527
|
+
chalk.cyan(`"fyn": { "allowTopLevelScripts": true }`)
|
|
8528
|
+
);
|
|
8529
|
+
}
|
|
7870
8530
|
}
|
|
7871
8531
|
_cleanBin() {
|
|
7872
8532
|
logger.updateItem(INSTALL_PACKAGE, "cleaning node_modules/.bin");
|
|
@@ -8068,6 +8728,8 @@ const PkgDepLinker = __webpack_require__("./lib/pkg-dep-linker.ts");
|
|
|
8068
8728
|
const semverUtil = __webpack_require__("./lib/util/semver.ts");
|
|
8069
8729
|
const { readPkgJson } = __webpack_require__("./lib/util/fyntil.ts");
|
|
8070
8730
|
const { OPTIONAL_RESOLVER } = __webpack_require__("./lib/log-items.ts");
|
|
8731
|
+
const { DEP_ITEM, SEMVER } = __webpack_require__("./lib/symbols.ts");
|
|
8732
|
+
const { evaluateScriptPolicy, isScriptAllowed } = __webpack_require__("./lib/util/lifecycle-script-policy.ts");
|
|
8071
8733
|
xsh.Promise = Promise;
|
|
8072
8734
|
class PkgOptResolver {
|
|
8073
8735
|
constructor(options) {
|
|
@@ -8130,6 +8792,32 @@ class PkgOptResolver {
|
|
|
8130
8792
|
// - 0: add item back to resolve
|
|
8131
8793
|
// - not: add item to queue for logging at end
|
|
8132
8794
|
//
|
|
8795
|
+
/**
|
|
8796
|
+
* Evaluate the lifecycle-script policy for an optional dep's preinstall.
|
|
8797
|
+
*
|
|
8798
|
+
* Non-registry (github/git/url) optional deps do not run preinstall unless
|
|
8799
|
+
* whitelisted via `fyn.allowScripts` / `fyn.allowTopLevelScripts` - the same
|
|
8800
|
+
* deny-by-default policy the regular installer enforces (FPM-41). Registry
|
|
8801
|
+
* and local deps are trusted and always allowed.
|
|
8802
|
+
*
|
|
8803
|
+
* @param {object} item the optional dep's DepItem
|
|
8804
|
+
* @param {string} name package name
|
|
8805
|
+
* @param {string} version resolved version
|
|
8806
|
+
* @returns {{allowed:boolean, policy:object}} the policy decision
|
|
8807
|
+
*/
|
|
8808
|
+
checkPreinstallPolicy(item, name, version) {
|
|
8809
|
+
const optDepInfo = {
|
|
8810
|
+
[DEP_ITEM]: item,
|
|
8811
|
+
[SEMVER]: item.semver,
|
|
8812
|
+
name,
|
|
8813
|
+
version,
|
|
8814
|
+
top: !_.get(item, ["parent", "depth"])
|
|
8815
|
+
};
|
|
8816
|
+
const policy = evaluateScriptPolicy(optDepInfo, this._fyn.allowScripts, {
|
|
8817
|
+
allowTopLevel: this._fyn.allowTopLevelScripts
|
|
8818
|
+
});
|
|
8819
|
+
return { allowed: isScriptAllowed(policy, "preinstall"), policy };
|
|
8820
|
+
}
|
|
8133
8821
|
/* eslint-disable max-statements */
|
|
8134
8822
|
optCheck(data) {
|
|
8135
8823
|
const name = data.item.name;
|
|
@@ -8248,6 +8936,17 @@ class PkgOptResolver {
|
|
|
8248
8936
|
);
|
|
8249
8937
|
return { passed: true };
|
|
8250
8938
|
} else if (_.get(res, "pkg.scripts.preinstall")) {
|
|
8939
|
+
const { allowed, policy } = this.checkPreinstallPolicy(data.item, name, version);
|
|
8940
|
+
if (!allowed) {
|
|
8941
|
+
logger.warn(
|
|
8942
|
+
`${chalk.black.bgYellow("WARN")} ${chalk.magenta("scripts blocked")} ${displayId}`,
|
|
8943
|
+
chalk.yellow(
|
|
8944
|
+
`skipped optional preinstall for non-registry (${policy.urlType}) package - not in fyn.allowScripts`
|
|
8945
|
+
)
|
|
8946
|
+
);
|
|
8947
|
+
logPass("preinstall blocked by script policy - keeping optional package");
|
|
8948
|
+
return { passed: true };
|
|
8949
|
+
}
|
|
8251
8950
|
data.runningScript = true;
|
|
8252
8951
|
logger.updateItem(OPTIONAL_RESOLVER, `running preinstall for ${displayId}`);
|
|
8253
8952
|
const ls = new LifecycleScripts({
|
|
@@ -8346,12 +9045,19 @@ const { PackageRef } = __webpack_require__("./node_modules/.f/_/@fynpo/base/1.1.
|
|
|
8346
9045
|
const Arborist = __webpack_require__("./node_modules/.f/_/@npmcli/arborist/9.1.6/@npmcli/arborist/lib/index.js");
|
|
8347
9046
|
const WATCH_TIME = 5e3;
|
|
8348
9047
|
const META_CACHE_STALE_TIME = 24 * 60 * 60 * 1e3;
|
|
9048
|
+
const SAFE_GIT_TOKEN = /^[A-Za-z0-9._/~^-]+$/;
|
|
9049
|
+
function isSafeGitToken(s) {
|
|
9050
|
+
return typeof s === "string" && s.length > 0 && s.length < 256 && SAFE_GIT_TOKEN.test(s) && !s.startsWith("-");
|
|
9051
|
+
}
|
|
9052
|
+
function isPinnedGitCommit(semver) {
|
|
9053
|
+
if (!semver) return false;
|
|
9054
|
+
const committish = semver.includes("#") ? semver.split("#")[1] : semver;
|
|
9055
|
+
return /^[a-f0-9]{40}$/.test(committish);
|
|
9056
|
+
}
|
|
8349
9057
|
async function checkGitRepoHasNewCommits(gitUrl, ref, cachedCommitHash) {
|
|
8350
9058
|
if (!cachedCommitHash) return true;
|
|
8351
9059
|
try {
|
|
8352
|
-
const {
|
|
8353
|
-
const Path2 = __webpack_require__("path");
|
|
8354
|
-
const fs2 = __webpack_require__("fs");
|
|
9060
|
+
const { execFileSync } = __webpack_require__("child_process");
|
|
8355
9061
|
let actualGitUrl = gitUrl;
|
|
8356
9062
|
let isLocalRepo = false;
|
|
8357
9063
|
let localRepoPath = null;
|
|
@@ -8363,10 +9069,15 @@ async function checkGitRepoHasNewCommits(gitUrl, ref, cachedCommitHash) {
|
|
|
8363
9069
|
}
|
|
8364
9070
|
} else if (!gitUrl.includes("://")) {
|
|
8365
9071
|
isLocalRepo = true;
|
|
8366
|
-
localRepoPath =
|
|
9072
|
+
localRepoPath = Path.resolve(gitUrl);
|
|
8367
9073
|
}
|
|
8368
9074
|
if (isLocalRepo && localRepoPath) {
|
|
8369
|
-
const
|
|
9075
|
+
const safeRef = ref || "HEAD";
|
|
9076
|
+
if (!isSafeGitToken(safeRef)) {
|
|
9077
|
+
logger.debug(`refusing potentially unsafe git ref: ${safeRef}`);
|
|
9078
|
+
return null;
|
|
9079
|
+
}
|
|
9080
|
+
const output = execFileSync("git", ["rev-parse", safeRef], {
|
|
8370
9081
|
cwd: localRepoPath,
|
|
8371
9082
|
stdio: "pipe",
|
|
8372
9083
|
encoding: "utf8",
|
|
@@ -8389,7 +9100,12 @@ async function checkGitRepoHasNewCommits(gitUrl, ref, cachedCommitHash) {
|
|
|
8389
9100
|
} else {
|
|
8390
9101
|
actualGitUrl = gitUrl;
|
|
8391
9102
|
}
|
|
8392
|
-
const
|
|
9103
|
+
const safeRef = ref || "HEAD";
|
|
9104
|
+
if (!isSafeGitToken(safeRef) || actualGitUrl.startsWith("-")) {
|
|
9105
|
+
logger.debug(`refusing potentially unsafe git url/ref: ${actualGitUrl}#${safeRef}`);
|
|
9106
|
+
return null;
|
|
9107
|
+
}
|
|
9108
|
+
const output = execFileSync("git", ["ls-remote", actualGitUrl, safeRef], {
|
|
8393
9109
|
stdio: "pipe",
|
|
8394
9110
|
encoding: "utf8",
|
|
8395
9111
|
timeout: 1e4
|
|
@@ -8626,16 +9342,17 @@ class PkgSrcManager {
|
|
|
8626
9342
|
logger.debug(`pacote.packument ${qItem.packumentUrl}`);
|
|
8627
9343
|
const promise2 = pacote.packument(
|
|
8628
9344
|
pkgName,
|
|
9345
|
+
// pacote 21 / npm-registry-fetch 19 read camelCase options; the old
|
|
9346
|
+
// kebab-case names were silently ignored. preferOnline forces a server
|
|
9347
|
+
// revalidation (cache mode "no-cache") for this refresh path.
|
|
8629
9348
|
this.getPacoteOpts({
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
"cache-key": qItem.cacheKey,
|
|
9349
|
+
fullMetadata: true,
|
|
9350
|
+
fetchRetries: 3,
|
|
9351
|
+
preferOnline: true,
|
|
8634
9352
|
memoize: false
|
|
8635
9353
|
})
|
|
8636
9354
|
);
|
|
8637
9355
|
return promise2.then((x) => {
|
|
8638
|
-
this._metaStat.inTx--;
|
|
8639
9356
|
if (!x) {
|
|
8640
9357
|
const msg = `pacote returned null/undefined for packument of ${pkgName}`;
|
|
8641
9358
|
logger.error(chalk.yellow(msg));
|
|
@@ -8656,6 +9373,7 @@ class PkgSrcManager {
|
|
|
8656
9373
|
this.updateFetchMetaStatus(false);
|
|
8657
9374
|
const promise = qItem.item.urlType ? this.fetchUrlSemverMeta(qItem.item) : pacoteRequest();
|
|
8658
9375
|
return promise.then((x) => {
|
|
9376
|
+
this._metaStat.inTx--;
|
|
8659
9377
|
const time = Date.now() - startTime;
|
|
8660
9378
|
if (time > 20 * 1e3) {
|
|
8661
9379
|
logger.info(
|
|
@@ -8670,10 +9388,11 @@ class PkgSrcManager {
|
|
|
8670
9388
|
qItem.defer.reject(new AggregateError([new Error(msg)], msg));
|
|
8671
9389
|
return;
|
|
8672
9390
|
}
|
|
8673
|
-
refreshCacheEntry(this.
|
|
9391
|
+
refreshCacheEntry(this._cacheDir, qItem.cacheKey).catch(() => {
|
|
8674
9392
|
});
|
|
8675
9393
|
qItem.defer.resolve(x);
|
|
8676
9394
|
}).catch((err) => {
|
|
9395
|
+
this._metaStat.inTx--;
|
|
8677
9396
|
qItem.defer.reject(err);
|
|
8678
9397
|
});
|
|
8679
9398
|
}
|
|
@@ -8715,19 +9434,7 @@ class PkgSrcManager {
|
|
|
8715
9434
|
} else {
|
|
8716
9435
|
dirPacker = this._getPacoteDirPacker();
|
|
8717
9436
|
}
|
|
8718
|
-
|
|
8719
|
-
if (item.urlType.startsWith("git") && item.semver && !item.semver.match(/^[a-f0-9]{40}$/)) {
|
|
8720
|
-
const potentialCacheKeys = [
|
|
8721
|
-
// Try common cache key patterns based on semver
|
|
8722
|
-
`fyn-tarball-for-git+https://github.com/${item.semver.replace(/^github:/, "").split("#")[0]}.git#`,
|
|
8723
|
-
`fyn-tarball-for-git+ssh://git@github.com/${item.semver.replace(/^github:/, "").split("#")[0]}.git#`
|
|
8724
|
-
];
|
|
8725
|
-
for (const baseKey of potentialCacheKeys) {
|
|
8726
|
-
try {
|
|
8727
|
-
} catch (e) {
|
|
8728
|
-
}
|
|
8729
|
-
}
|
|
8730
|
-
}
|
|
9437
|
+
const pacoteOpts = { dirPacker };
|
|
8731
9438
|
return pacote.manifest(`${item.name}@${item.semver}`, this.getPacoteOpts(pacoteOpts)).then((manifest) => {
|
|
8732
9439
|
manifest = Object.assign({}, manifest);
|
|
8733
9440
|
return {
|
|
@@ -8751,9 +9458,10 @@ class PkgSrcManager {
|
|
|
8751
9458
|
let integrity;
|
|
8752
9459
|
let shouldRefresh = false;
|
|
8753
9460
|
if (tgzCacheInfo) {
|
|
8754
|
-
const
|
|
9461
|
+
const cachedTarball = tgzCacheInfo.metadata?.dist?.tarball;
|
|
9462
|
+
const cachedResolved = tgzCacheInfo.metadata?._resolved || (cachedTarball?.startsWith(MARK_URL_SPEC) ? JSON.parse(cachedTarball.slice(MARK_URL_SPEC.length))._resolved : null);
|
|
8755
9463
|
const cachedCommitHash = cachedResolved?.match(/#([a-f0-9]{40})$/)?.[1];
|
|
8756
|
-
if (!shouldRefresh && cachedCommitHash && item.semver && !item.semver
|
|
9464
|
+
if (!shouldRefresh && cachedCommitHash && item.semver && !isPinnedGitCommit(item.semver)) {
|
|
8757
9465
|
let gitUrl = item.semver;
|
|
8758
9466
|
let ref = "HEAD";
|
|
8759
9467
|
if (gitUrl.includes("#")) {
|
|
@@ -8776,17 +9484,15 @@ class PkgSrcManager {
|
|
|
8776
9484
|
logger.debug(
|
|
8777
9485
|
`git cache for '${item.name}' has new commits (cached: ${cachedCommitHash.substring(0, 8)}, checking ${ref}), forcing refresh`
|
|
8778
9486
|
);
|
|
8779
|
-
} else {
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
);
|
|
8789
|
-
}
|
|
9487
|
+
} else if (tgzCacheInfo.refreshTime) {
|
|
9488
|
+
const stale = Date.now() - tgzCacheInfo.refreshTime;
|
|
9489
|
+
const staleByTime = stale >= META_CACHE_STALE_TIME;
|
|
9490
|
+
if (staleByTime) {
|
|
9491
|
+
shouldRefresh = true;
|
|
9492
|
+
const reason = hasNewCommits === null ? "ls-remote failed" : "no new commits";
|
|
9493
|
+
logger.debug(
|
|
9494
|
+
`git cache for '${item.name}' (${reason}), cache is stale by time (${(stale / 1e3 / 60 / 60).toFixed(1)}h old), forcing refresh`
|
|
9495
|
+
);
|
|
8790
9496
|
}
|
|
8791
9497
|
}
|
|
8792
9498
|
} else if (!shouldRefresh && tgzCacheInfo.refreshTime) {
|
|
@@ -8860,6 +9566,7 @@ class PkgSrcManager {
|
|
|
8860
9566
|
const cacheKey = `make-fetch-happen:request-cache:${packumentUrl}`;
|
|
8861
9567
|
const legacyCacheKey = `make-fetch-happen:request-cache:full:${packumentUrl}`;
|
|
8862
9568
|
const cacheKeys = [cacheKey, legacyCacheKey];
|
|
9569
|
+
let cacheMemoized = false;
|
|
8863
9570
|
const loadCachedPackument = async (key, memoize = true) => {
|
|
8864
9571
|
try {
|
|
8865
9572
|
const cached = await cacache.get(this._cacheDir, key, { memoize });
|
|
@@ -8882,6 +9589,33 @@ class PkgSrcManager {
|
|
|
8882
9589
|
}
|
|
8883
9590
|
return _.maxBy(cachedEntries, (cached) => cached.refreshTime || 0) || cachedEntries[0];
|
|
8884
9591
|
};
|
|
9592
|
+
const loadPreparedUrlMeta = async () => {
|
|
9593
|
+
const info = await getCacheInfoWithRefreshTime(
|
|
9594
|
+
this._cacheDir,
|
|
9595
|
+
`fyn-tarball-for-${item.semver}`
|
|
9596
|
+
);
|
|
9597
|
+
const cachedManifest = info && info.metadata;
|
|
9598
|
+
if (!(cachedManifest && cachedManifest.version)) {
|
|
9599
|
+
return void 0;
|
|
9600
|
+
}
|
|
9601
|
+
const tarball = JSON.stringify(
|
|
9602
|
+
Object.assign(
|
|
9603
|
+
_.pick(item, ["urlType", "semver"]),
|
|
9604
|
+
_.pick(cachedManifest, ["_resolved", "_id"])
|
|
9605
|
+
)
|
|
9606
|
+
);
|
|
9607
|
+
const manifest = Object.assign({}, cachedManifest, {
|
|
9608
|
+
dist: {
|
|
9609
|
+
integrity: info.integrity,
|
|
9610
|
+
tarball: `${MARK_URL_SPEC}${tarball}`
|
|
9611
|
+
}
|
|
9612
|
+
});
|
|
9613
|
+
return {
|
|
9614
|
+
name: item.name,
|
|
9615
|
+
versions: { [manifest.version]: manifest },
|
|
9616
|
+
urlVersions: { [item.semver]: manifest }
|
|
9617
|
+
};
|
|
9618
|
+
};
|
|
8885
9619
|
const readMemoizedPackument = async () => {
|
|
8886
9620
|
const memoized = await loadBestCachedPackument(false);
|
|
8887
9621
|
if (!(memoized && memoized.data)) {
|
|
@@ -8892,7 +9626,11 @@ class PkgSrcManager {
|
|
|
8892
9626
|
this._metaStat.wait--;
|
|
8893
9627
|
return JSON.parse(memoized.data.toString());
|
|
8894
9628
|
};
|
|
9629
|
+
let fetchAttempted = false;
|
|
9630
|
+
let foundCache;
|
|
9631
|
+
let foundPackument;
|
|
8895
9632
|
const queueMetaFetchRequest = (cached) => {
|
|
9633
|
+
fetchAttempted = true;
|
|
8896
9634
|
const offline = this._fyn.remoteMetaDisabled;
|
|
8897
9635
|
if (cached && this._fyn.forceCache) {
|
|
8898
9636
|
this._metaStat.wait--;
|
|
@@ -8917,18 +9655,24 @@ class PkgSrcManager {
|
|
|
8917
9655
|
return netQItem.defer.promise;
|
|
8918
9656
|
};
|
|
8919
9657
|
this._metaStat.wait++;
|
|
8920
|
-
let foundCache;
|
|
8921
|
-
let cacheMemoized = false;
|
|
8922
9658
|
const metaMemoizeUrl = this._fyn._options.metaMemoize;
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
9659
|
+
let cacheLookup;
|
|
9660
|
+
if (item.urlType) {
|
|
9661
|
+
cacheLookup = this._fyn.remoteMetaDisabled ? loadPreparedUrlMeta().then((preparedUrlMeta) => ({ preparedUrlMeta })) : Promise.resolve();
|
|
9662
|
+
} else if (forceRefresh) {
|
|
9663
|
+
cacheLookup = Promise.resolve();
|
|
9664
|
+
} else {
|
|
9665
|
+
cacheLookup = loadBestCachedPackument(true);
|
|
9666
|
+
}
|
|
9667
|
+
const promise = cacheLookup.then((cached) => {
|
|
9668
|
+
if (cached && cached.preparedUrlMeta) {
|
|
9669
|
+
cacheMemoized = true;
|
|
9670
|
+
this._metaStat.wait--;
|
|
9671
|
+
return cached.preparedUrlMeta;
|
|
9672
|
+
}
|
|
8931
9673
|
foundCache = cached;
|
|
9674
|
+
const packument = cached && cached.data && JSON.parse(cached.data);
|
|
9675
|
+
foundPackument = packument;
|
|
8932
9676
|
if (cached && cached.refreshTime) {
|
|
8933
9677
|
const stale = Date.now() - cached.refreshTime;
|
|
8934
9678
|
const since = (stale / 1e3).toFixed(2);
|
|
@@ -8971,10 +9715,19 @@ class PkgSrcManager {
|
|
|
8971
9715
|
return queueMetaFetchRequest(packument);
|
|
8972
9716
|
}
|
|
8973
9717
|
}).catch((err) => {
|
|
9718
|
+
if (fetchAttempted) {
|
|
9719
|
+
if (foundPackument) {
|
|
9720
|
+
cacheMemoized = true;
|
|
9721
|
+
logger.warn(
|
|
9722
|
+
`failed to refresh packument for '${pkgName}', using stale cached metadata: ${err.message}`
|
|
9723
|
+
);
|
|
9724
|
+
return foundPackument;
|
|
9725
|
+
}
|
|
9726
|
+
throw err;
|
|
9727
|
+
}
|
|
8974
9728
|
if (foundCache) {
|
|
8975
9729
|
const data = foundCache.data && foundCache.data.toString();
|
|
8976
9730
|
logger.debug(`fail to process packument cache - ${err.message}; data ${data}`);
|
|
8977
|
-
throw err;
|
|
8978
9731
|
}
|
|
8979
9732
|
return queueMetaFetchRequest();
|
|
8980
9733
|
}).then((meta) => {
|
|
@@ -9038,7 +9791,7 @@ class PkgSrcManager {
|
|
|
9038
9791
|
return passthrough2;
|
|
9039
9792
|
}
|
|
9040
9793
|
const opts = this.getPacoteOpts({
|
|
9041
|
-
|
|
9794
|
+
fullMetadata: true,
|
|
9042
9795
|
integrity,
|
|
9043
9796
|
resolved: tarballUrl
|
|
9044
9797
|
});
|
|
@@ -9155,6 +9908,7 @@ class PkgSrcManager {
|
|
|
9155
9908
|
}
|
|
9156
9909
|
module.exports = PkgSrcManager;
|
|
9157
9910
|
module.exports.META_CACHE_STALE_TIME = META_CACHE_STALE_TIME;
|
|
9911
|
+
module.exports.isPinnedGitCommit = isPinnedGitCommit;
|
|
9158
9912
|
|
|
9159
9913
|
|
|
9160
9914
|
/***/ }),
|
|
@@ -9267,7 +10021,8 @@ async function checkPkgNeedInstall(dir, checkCtime = 0) {
|
|
|
9267
10021
|
hasScript
|
|
9268
10022
|
};
|
|
9269
10023
|
} catch (error) {
|
|
9270
|
-
|
|
10024
|
+
logger.warn(`unable to determine if local package at ${dir} needs install: ${error.message}`);
|
|
10025
|
+
throw error;
|
|
9271
10026
|
}
|
|
9272
10027
|
}
|
|
9273
10028
|
exports.checkPkgNeedInstall = checkPkgNeedInstall;
|
|
@@ -9402,6 +10157,67 @@ const fyntil = {
|
|
|
9402
10157
|
return fyntil.fynpoConfig = {};
|
|
9403
10158
|
}
|
|
9404
10159
|
},
|
|
10160
|
+
/**
|
|
10161
|
+
* Detect if `dir` lives inside a git linked worktree and, if so, resolve the
|
|
10162
|
+
* equivalent directory in the repo's main worktree.
|
|
10163
|
+
*
|
|
10164
|
+
* fynpo uses `<monorepo>/.fynpo/_store` as the central package store. When the
|
|
10165
|
+
* monorepo is checked out as a git linked worktree, each worktree would
|
|
10166
|
+
* otherwise get its own `.fynpo` store - wasteful and slow. Pointing them at
|
|
10167
|
+
* the main worktree's store lets all worktrees share one central store.
|
|
10168
|
+
*
|
|
10169
|
+
* @param dir directory to resolve (typically the fynpo monorepo top dir)
|
|
10170
|
+
* @returns the equivalent directory in the main worktree, or `dir` unchanged
|
|
10171
|
+
* when it's not in a git repo or already in the main worktree.
|
|
10172
|
+
*/
|
|
10173
|
+
async resolveGitMainWorktreeDir(dir) {
|
|
10174
|
+
const startDir = Path.resolve(dir);
|
|
10175
|
+
let treeTop = startDir;
|
|
10176
|
+
let gitPath;
|
|
10177
|
+
let gitStat;
|
|
10178
|
+
for (; ; ) {
|
|
10179
|
+
try {
|
|
10180
|
+
const p = Path.join(treeTop, ".git");
|
|
10181
|
+
gitStat = await Fs.stat(p);
|
|
10182
|
+
gitPath = p;
|
|
10183
|
+
break;
|
|
10184
|
+
} catch (err) {
|
|
10185
|
+
if (err.code !== "ENOENT") {
|
|
10186
|
+
throw err;
|
|
10187
|
+
}
|
|
10188
|
+
}
|
|
10189
|
+
const parent = Path.dirname(treeTop);
|
|
10190
|
+
if (parent === treeTop) {
|
|
10191
|
+
break;
|
|
10192
|
+
}
|
|
10193
|
+
treeTop = parent;
|
|
10194
|
+
}
|
|
10195
|
+
if (!gitPath || gitStat.isDirectory()) {
|
|
10196
|
+
return dir;
|
|
10197
|
+
}
|
|
10198
|
+
try {
|
|
10199
|
+
const gitFile = await Fs.readFile(gitPath, "utf8");
|
|
10200
|
+
const m = gitFile.match(/^gitdir:\s*(.+)$/m);
|
|
10201
|
+
if (!m) {
|
|
10202
|
+
return dir;
|
|
10203
|
+
}
|
|
10204
|
+
let worktreeGitDir = m[1].trim();
|
|
10205
|
+
if (!Path.isAbsolute(worktreeGitDir)) {
|
|
10206
|
+
worktreeGitDir = Path.resolve(treeTop, worktreeGitDir);
|
|
10207
|
+
}
|
|
10208
|
+
const commondir = (await Fs.readFile(Path.join(worktreeGitDir, "commondir"), "utf8")).trim();
|
|
10209
|
+
const commonGitDir = Path.isAbsolute(commondir) ? commondir : Path.resolve(worktreeGitDir, commondir);
|
|
10210
|
+
if (Path.basename(commonGitDir) !== ".git") {
|
|
10211
|
+
return dir;
|
|
10212
|
+
}
|
|
10213
|
+
const mainTreeTop = Path.dirname(commonGitDir);
|
|
10214
|
+
const rel = Path.relative(treeTop, startDir);
|
|
10215
|
+
return Path.join(mainTreeTop, rel);
|
|
10216
|
+
} catch (err) {
|
|
10217
|
+
logger.debug(`resolveGitMainWorktreeDir failed for ${dir}: ${err.message}`);
|
|
10218
|
+
return dir;
|
|
10219
|
+
}
|
|
10220
|
+
},
|
|
9405
10221
|
removeAuthInfo(rcObj) {
|
|
9406
10222
|
const rmObj = {};
|
|
9407
10223
|
for (const key in rcObj) {
|
|
@@ -9413,7 +10229,10 @@ const fyntil = {
|
|
|
9413
10229
|
return rmObj;
|
|
9414
10230
|
},
|
|
9415
10231
|
exit(err) {
|
|
9416
|
-
|
|
10232
|
+
if (typeof err === "number") {
|
|
10233
|
+
return process.exit(err);
|
|
10234
|
+
}
|
|
10235
|
+
return process.exit(err ? 1 : 0);
|
|
9417
10236
|
},
|
|
9418
10237
|
async readJson(file, defaultData) {
|
|
9419
10238
|
try {
|
|
@@ -9807,7 +10626,7 @@ async function linkPackTree({ tree, src, dest, sym1, sourceMaps }) {
|
|
|
9807
10626
|
const files = tree[SYM_FILES];
|
|
9808
10627
|
const destFiles = await prepDestDir(dest);
|
|
9809
10628
|
for (const file of files) {
|
|
9810
|
-
if (!ci.isCI && file.match(
|
|
10629
|
+
if (!ci.isCI && file.match(/\.(js|mjs)\.map$/)) {
|
|
9811
10630
|
continue;
|
|
9812
10631
|
}
|
|
9813
10632
|
destFiles[file] = true;
|
|
@@ -9868,6 +10687,133 @@ module.exports = {
|
|
|
9868
10687
|
module.exports = __webpack_require__("./node_modules/.f/_/item-queue/1.1.2/item-queue/lib/inflight.js");
|
|
9869
10688
|
|
|
9870
10689
|
|
|
10690
|
+
/***/ }),
|
|
10691
|
+
|
|
10692
|
+
/***/ "./lib/util/lifecycle-script-policy.ts":
|
|
10693
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
10694
|
+
|
|
10695
|
+
"use strict";
|
|
10696
|
+
|
|
10697
|
+
const semverUtil = __webpack_require__("./lib/util/semver.ts");
|
|
10698
|
+
const { DEP_ITEM, SEMVER } = __webpack_require__("./lib/symbols.ts");
|
|
10699
|
+
const TRUSTED_URL_TYPES = /* @__PURE__ */ new Set(["npm"]);
|
|
10700
|
+
function getSourceUrlType(depItem) {
|
|
10701
|
+
let item = depItem;
|
|
10702
|
+
while (item) {
|
|
10703
|
+
const analyzed = item.semver ? semverUtil.analyze(item.semver) : {};
|
|
10704
|
+
const urlType = item.urlType || analyzed.urlType;
|
|
10705
|
+
if (urlType) {
|
|
10706
|
+
return urlType;
|
|
10707
|
+
}
|
|
10708
|
+
if (!(item.localType || analyzed.localType)) {
|
|
10709
|
+
return void 0;
|
|
10710
|
+
}
|
|
10711
|
+
item = item.parent;
|
|
10712
|
+
}
|
|
10713
|
+
return void 0;
|
|
10714
|
+
}
|
|
10715
|
+
function getUrlType(depInfo) {
|
|
10716
|
+
const depItem = depInfo[DEP_ITEM];
|
|
10717
|
+
if (depItem) {
|
|
10718
|
+
return getSourceUrlType(depItem);
|
|
10719
|
+
}
|
|
10720
|
+
const spec = depInfo[SEMVER];
|
|
10721
|
+
if (spec) {
|
|
10722
|
+
return semverUtil.analyze(spec).urlType;
|
|
10723
|
+
}
|
|
10724
|
+
return void 0;
|
|
10725
|
+
}
|
|
10726
|
+
function isTrustedScriptSource(depInfo) {
|
|
10727
|
+
const urlType = getUrlType(depInfo);
|
|
10728
|
+
return !urlType || TRUSTED_URL_TYPES.has(urlType);
|
|
10729
|
+
}
|
|
10730
|
+
function makeAllowKeys(depInfo) {
|
|
10731
|
+
const depItem = depInfo[DEP_ITEM];
|
|
10732
|
+
const spec = depItem && depItem.semver || depInfo[SEMVER];
|
|
10733
|
+
const keys = [];
|
|
10734
|
+
if (spec) {
|
|
10735
|
+
keys.push(`${depInfo.name}@${spec}`);
|
|
10736
|
+
}
|
|
10737
|
+
if (depInfo.version && depInfo.version !== spec) {
|
|
10738
|
+
keys.push(`${depInfo.name}@${depInfo.version}`);
|
|
10739
|
+
}
|
|
10740
|
+
return keys;
|
|
10741
|
+
}
|
|
10742
|
+
function normalizeAllowEntry(value, acc) {
|
|
10743
|
+
if (value === true || value === "*") {
|
|
10744
|
+
acc.allowAll = true;
|
|
10745
|
+
return acc;
|
|
10746
|
+
}
|
|
10747
|
+
let list = [];
|
|
10748
|
+
if (Array.isArray(value)) {
|
|
10749
|
+
list = value;
|
|
10750
|
+
} else if (value !== void 0) {
|
|
10751
|
+
list = [value];
|
|
10752
|
+
}
|
|
10753
|
+
for (const s of list) {
|
|
10754
|
+
if (s === true || s === "*") {
|
|
10755
|
+
acc.allowAll = true;
|
|
10756
|
+
} else if (typeof s === "string") {
|
|
10757
|
+
acc.scripts.add(s.toLowerCase());
|
|
10758
|
+
}
|
|
10759
|
+
}
|
|
10760
|
+
return acc;
|
|
10761
|
+
}
|
|
10762
|
+
function foldAllowScripts(keys, allowScripts, acc) {
|
|
10763
|
+
let matchedKey;
|
|
10764
|
+
for (const key of keys) {
|
|
10765
|
+
const value = allowScripts && allowScripts[key];
|
|
10766
|
+
if (value !== void 0) {
|
|
10767
|
+
if (!matchedKey) matchedKey = key;
|
|
10768
|
+
normalizeAllowEntry(value, acc);
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10771
|
+
return matchedKey;
|
|
10772
|
+
}
|
|
10773
|
+
function isTopLevelDep(depInfo) {
|
|
10774
|
+
return Boolean(depInfo && depInfo.top);
|
|
10775
|
+
}
|
|
10776
|
+
function evaluateScriptPolicy(depInfo, allowScripts, options = {}) {
|
|
10777
|
+
const urlType = getUrlType(depInfo);
|
|
10778
|
+
const keys = makeAllowKeys(depInfo);
|
|
10779
|
+
const topLevel = isTopLevelDep(depInfo);
|
|
10780
|
+
if (!urlType || TRUSTED_URL_TYPES.has(urlType)) {
|
|
10781
|
+
return { trusted: true, urlType, allowAll: true, allowed: /* @__PURE__ */ new Set(), key: keys[0], topLevel };
|
|
10782
|
+
}
|
|
10783
|
+
const acc = { allowAll: false, scripts: /* @__PURE__ */ new Set() };
|
|
10784
|
+
const matchedKey = foldAllowScripts(keys, allowScripts, acc);
|
|
10785
|
+
const { allowTopLevel } = options;
|
|
10786
|
+
if (topLevel && allowTopLevel !== void 0 && allowTopLevel !== false) {
|
|
10787
|
+
normalizeAllowEntry(allowTopLevel, acc);
|
|
10788
|
+
}
|
|
10789
|
+
return {
|
|
10790
|
+
trusted: false,
|
|
10791
|
+
urlType,
|
|
10792
|
+
allowAll: acc.allowAll,
|
|
10793
|
+
allowed: acc.scripts,
|
|
10794
|
+
// key to suggest when warning - prefer a matched key, else the spec form
|
|
10795
|
+
key: matchedKey || keys[0],
|
|
10796
|
+
topLevel
|
|
10797
|
+
};
|
|
10798
|
+
}
|
|
10799
|
+
function isScriptAllowed(policy, scriptName) {
|
|
10800
|
+
if (policy.trusted || policy.allowAll) {
|
|
10801
|
+
return true;
|
|
10802
|
+
}
|
|
10803
|
+
return policy.allowed.has(String(scriptName).toLowerCase());
|
|
10804
|
+
}
|
|
10805
|
+
module.exports = {
|
|
10806
|
+
TRUSTED_URL_TYPES,
|
|
10807
|
+
getSourceUrlType,
|
|
10808
|
+
getUrlType,
|
|
10809
|
+
isTrustedScriptSource,
|
|
10810
|
+
isTopLevelDep,
|
|
10811
|
+
makeAllowKeys,
|
|
10812
|
+
evaluateScriptPolicy,
|
|
10813
|
+
isScriptAllowed
|
|
10814
|
+
};
|
|
10815
|
+
|
|
10816
|
+
|
|
9871
10817
|
/***/ }),
|
|
9872
10818
|
|
|
9873
10819
|
/***/ "./lib/util/log-format.ts":
|
|
@@ -9888,6 +10834,7 @@ module.exports = {
|
|
|
9888
10834
|
* - the part before package name blue
|
|
9889
10835
|
* - the package name part magenta
|
|
9890
10836
|
* - remaining as is
|
|
10837
|
+
*
|
|
9891
10838
|
* @param {*} name - package name
|
|
9892
10839
|
* @param {*} path - path to highlight
|
|
9893
10840
|
* @returns
|
|
@@ -9987,7 +10934,7 @@ function makeNpmEnv(data, opts, prefix, env) {
|
|
|
9987
10934
|
if (minimalConfigKeys.includes(normalizedKey) || minimalConfigKeys.includes(key)) {
|
|
9988
10935
|
const envKey = `npm_config_${normalizedKey}`;
|
|
9989
10936
|
const val = opts.config[key];
|
|
9990
|
-
if (val
|
|
10937
|
+
if (val !== null && val !== void 0 && typeof val !== "function") {
|
|
9991
10938
|
env[envKey] = String(val);
|
|
9992
10939
|
}
|
|
9993
10940
|
}
|
|
@@ -10054,6 +11001,61 @@ ItemQueue.Promise = __webpack_require__("./lib/util/aveazul.ts");
|
|
|
10054
11001
|
module.exports = ItemQueue;
|
|
10055
11002
|
|
|
10056
11003
|
|
|
11004
|
+
/***/ }),
|
|
11005
|
+
|
|
11006
|
+
/***/ "./lib/util/registry-dep-policy.ts":
|
|
11007
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
11008
|
+
|
|
11009
|
+
"use strict";
|
|
11010
|
+
|
|
11011
|
+
const Semver = __webpack_require__("./node_modules/.f/_/semver/7.8.0/semver/index.js");
|
|
11012
|
+
const { TRUSTED_URL_TYPES, getSourceUrlType } = __webpack_require__("./lib/util/lifecycle-script-policy.ts");
|
|
11013
|
+
function isNonRegistryUrlType(urlType) {
|
|
11014
|
+
return Boolean(urlType) && !TRUSTED_URL_TYPES.has(urlType);
|
|
11015
|
+
}
|
|
11016
|
+
function isValidRegistrySpec(spec) {
|
|
11017
|
+
if (spec === void 0 || spec === null) {
|
|
11018
|
+
return false;
|
|
11019
|
+
}
|
|
11020
|
+
const s = String(spec).trim();
|
|
11021
|
+
if (s === "" || s === "*" || s === "x") {
|
|
11022
|
+
return true;
|
|
11023
|
+
}
|
|
11024
|
+
if (Semver.validRange(s) !== null) {
|
|
11025
|
+
return true;
|
|
11026
|
+
}
|
|
11027
|
+
if (Semver.coerce(s)) {
|
|
11028
|
+
return true;
|
|
11029
|
+
}
|
|
11030
|
+
return /^[a-zA-Z][\w.-]*$/.test(s);
|
|
11031
|
+
}
|
|
11032
|
+
function violatesRegistryPolicy(dep) {
|
|
11033
|
+
const urlType = dep.urlType;
|
|
11034
|
+
if (urlType) {
|
|
11035
|
+
if (TRUSTED_URL_TYPES.has(urlType)) {
|
|
11036
|
+
return null;
|
|
11037
|
+
}
|
|
11038
|
+
return { kind: "url", urlType };
|
|
11039
|
+
}
|
|
11040
|
+
if (dep.localType) {
|
|
11041
|
+
const sourceUrlType = getSourceUrlType(dep);
|
|
11042
|
+
if (isNonRegistryUrlType(sourceUrlType)) {
|
|
11043
|
+
return { kind: "local", urlType: sourceUrlType };
|
|
11044
|
+
}
|
|
11045
|
+
return null;
|
|
11046
|
+
}
|
|
11047
|
+
if (!isValidRegistrySpec(dep.semver)) {
|
|
11048
|
+
return { kind: "semver", semver: dep.semver };
|
|
11049
|
+
}
|
|
11050
|
+
return null;
|
|
11051
|
+
}
|
|
11052
|
+
module.exports = {
|
|
11053
|
+
isNonRegistryUrlType,
|
|
11054
|
+
isValidRegistrySpec,
|
|
11055
|
+
violatesRegistryPolicy
|
|
11056
|
+
};
|
|
11057
|
+
|
|
11058
|
+
|
|
10057
11059
|
/***/ }),
|
|
10058
11060
|
|
|
10059
11061
|
/***/ "./lib/util/run-npm-script.ts":
|
|
@@ -10160,11 +11162,14 @@ function simpleCompare(a, b) {
|
|
|
10160
11162
|
}
|
|
10161
11163
|
if (partsA[1]) {
|
|
10162
11164
|
if (partsB[1]) {
|
|
11165
|
+
if (Semver.valid(a) && Semver.valid(b)) {
|
|
11166
|
+
return Semver.rcompare(a, b);
|
|
11167
|
+
}
|
|
10163
11168
|
return partsA[1] > partsB[1] ? -1 : 1;
|
|
10164
11169
|
}
|
|
10165
|
-
return -1;
|
|
10166
|
-
} else if (partsB[1]) {
|
|
10167
11170
|
return 1;
|
|
11171
|
+
} else if (partsB[1]) {
|
|
11172
|
+
return -1;
|
|
10168
11173
|
} else {
|
|
10169
11174
|
return 0;
|
|
10170
11175
|
}
|
|
@@ -10448,7 +11453,7 @@ async function _scanFileStats(dir, ignores, baseDir = "") {
|
|
|
10448
11453
|
}
|
|
10449
11454
|
function scanFileStats(dir, options = {}) {
|
|
10450
11455
|
const ignores = [
|
|
10451
|
-
`**/?(node_modules|.vscode|.DS_Store|coverage|.nyc_output|.fynpo|.git|.github|.gitignore)`,
|
|
11456
|
+
`**/?(node_modules|_fyn|.vscode|.DS_Store|coverage|.nyc_output|.fynpo|.git|.github|.gitignore)`,
|
|
10452
11457
|
"**/*.?(log|md)"
|
|
10453
11458
|
].concat(options.ignores || `**/?(docs|docusaurus|packages|tmp|.etmp|samples|dist)`).concat(options.moreIgnores).filter((x) => x);
|
|
10454
11459
|
return _scanFileStats(dir, ignores, "");
|