fyn 2.1.2 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/fyn.js +121 -2
  2. package/package.json +1 -1
package/dist/fyn.js CHANGED
@@ -3907,7 +3907,13 @@ class Fyn {
3907
3907
  } else {
3908
3908
  centralDir = this._fynpo.config.centralDir;
3909
3909
  if (!centralDir) {
3910
- centralDir = Path.join(this._fynpo.dir, ".fynpo", "_store");
3910
+ const storeBaseDir = await fynTil.resolveGitMainWorktreeDir(this._fynpo.dir);
3911
+ if (storeBaseDir !== this._fynpo.dir) {
3912
+ logger.info(
3913
+ `fynpo monorepo is a git worktree; sharing central store from main worktree ${storeBaseDir}`
3914
+ );
3915
+ }
3916
+ centralDir = Path.join(storeBaseDir, ".fynpo", "_store");
3911
3917
  }
3912
3918
  logger.info(`Enabling central store by fynpo monorepo using dir ${centralDir}`);
3913
3919
  }
@@ -5636,6 +5642,7 @@ const Fs = __webpack_require__("./lib/util/file-ops.ts");
5636
5642
  const _ = __webpack_require__("./node_modules/.f/_/lodash/4.17.21/lodash/lodash.min.js");
5637
5643
  const chalk = __webpack_require__("./node_modules/.f/_/chalk/4.1.2/chalk/source/index.js");
5638
5644
  const simpleSemverCompare = (__webpack_require__("./lib/util/semver.ts").simpleCompare);
5645
+ const Semver = __webpack_require__("./node_modules/.f/_/semver/7.8.0/semver/index.js");
5639
5646
  const Yaml = __webpack_require__("./node_modules/.f/_/yamljs/0.3.0/yamljs/lib/Yaml.js");
5640
5647
  const sortObjKeys = __webpack_require__("./lib/util/sort-obj-keys.ts");
5641
5648
  const {
@@ -5830,7 +5837,10 @@ class PkgDepLocker {
5830
5837
  const versions = {};
5831
5838
  _.each(sorted, (version) => {
5832
5839
  const vpkg = locked[version];
5833
- if (!_.isEmpty(vpkg) && vpkg._valid !== false) {
5840
+ const isLocalVpkg = vpkg && vpkg.$ === "local";
5841
+ const badVersionKey = !isLocalVpkg && !Semver.valid(version);
5842
+ const badDeps = vpkg && vpkg.dependencies && !this._depsResolvable(vpkg.dependencies);
5843
+ if (!_.isEmpty(vpkg) && vpkg._valid !== false && !badVersionKey && !badDeps) {
5834
5844
  if (vpkg.$ === "local") {
5835
5845
  vpkg.local = true;
5836
5846
  vpkg.dist = {
@@ -5853,6 +5863,15 @@ class PkgDepLocker {
5853
5863
  }
5854
5864
  versions[version] = vpkg;
5855
5865
  } else {
5866
+ if (badVersionKey) {
5867
+ logger.error(
5868
+ `lockfile entry for ${item.name} has invalid version key "${version}" - ignoring and re-resolving from registry`
5869
+ );
5870
+ } else if (badDeps) {
5871
+ logger.error(
5872
+ `lockfile entry ${item.name}@${version} has dependencies not satisfiable within the lock (corrupt lock) - ignoring and re-resolving from registry`
5873
+ );
5874
+ }
5856
5875
  valid = false;
5857
5876
  }
5858
5877
  });
@@ -5874,6 +5893,37 @@ class PkgDepLocker {
5874
5893
  }
5875
5894
  return valid && locked;
5876
5895
  }
5896
+ //
5897
+ // Check that every recorded dependency pin can be resolved within the lock data.
5898
+ // A healthy fyn lock is self-contained: every locked package's dependency specs are
5899
+ // satisfied by some version that also has a lock entry. A pin that points at a version
5900
+ // absent from the lock signals corruption (e.g. a version block's deps got swapped
5901
+ // during a rebase/merge), so the entry must be dropped and re-resolved.
5902
+ //
5903
+ _depsResolvable(deps) {
5904
+ for (const depName in deps) {
5905
+ if (!this._isDepResolvable(depName, deps[depName])) {
5906
+ return false;
5907
+ }
5908
+ }
5909
+ return true;
5910
+ }
5911
+ _isDepResolvable(depName, spec) {
5912
+ const depLock = this._lockData[depName];
5913
+ if (!depLock) return true;
5914
+ let versions;
5915
+ let rangeMap;
5916
+ if (depLock.versions) {
5917
+ versions = Object.keys(depLock.versions);
5918
+ rangeMap = depLock[LOCK_RSEMVERS] || {};
5919
+ } else {
5920
+ versions = Object.keys(depLock).filter((k) => !k.startsWith("_"));
5921
+ rangeMap = depLock._ || {};
5922
+ }
5923
+ if (Object.prototype.hasOwnProperty.call(rangeMap, spec)) return true;
5924
+ if (!Semver.validRange(spec)) return true;
5925
+ return versions.some((v) => Semver.valid(v) && Semver.satisfies(v, spec));
5926
+ }
5877
5927
  /**
5878
5928
  * Set the package.json's dependencies items and check if they changed from
5879
5929
  * lock data.
@@ -6044,6 +6094,14 @@ class PkgDepLocker {
6044
6094
  if (!Path.isAbsolute(filename)) filename = Path.resolve(filename);
6045
6095
  const data = (await Fs.readFile(filename)).toString();
6046
6096
  this._shaSum = this.shasum(data);
6097
+ if (/^(<<<<<<<|=======|>>>>>>>)/m.test(data)) {
6098
+ logger.error(
6099
+ `lockfile ${filename} has git conflict markers - ignoring it and re-resolving from registry`
6100
+ );
6101
+ this._shaSum = Date.now();
6102
+ this._lockData = {};
6103
+ return false;
6104
+ }
6047
6105
  this._lockData = Yaml.parse(data);
6048
6106
  const basedir = Path.dirname(filename);
6049
6107
  this._fullLocalPath(basedir);
@@ -9402,6 +9460,67 @@ const fyntil = {
9402
9460
  return fyntil.fynpoConfig = {};
9403
9461
  }
9404
9462
  },
9463
+ /**
9464
+ * Detect if `dir` lives inside a git linked worktree and, if so, resolve the
9465
+ * equivalent directory in the repo's main worktree.
9466
+ *
9467
+ * fynpo uses `<monorepo>/.fynpo/_store` as the central package store. When the
9468
+ * monorepo is checked out as a git linked worktree, each worktree would
9469
+ * otherwise get its own `.fynpo` store - wasteful and slow. Pointing them at
9470
+ * the main worktree's store lets all worktrees share one central store.
9471
+ *
9472
+ * @param dir directory to resolve (typically the fynpo monorepo top dir)
9473
+ * @returns the equivalent directory in the main worktree, or `dir` unchanged
9474
+ * when it's not in a git repo or already in the main worktree.
9475
+ */
9476
+ async resolveGitMainWorktreeDir(dir) {
9477
+ const startDir = Path.resolve(dir);
9478
+ let treeTop = startDir;
9479
+ let gitPath;
9480
+ let gitStat;
9481
+ for (; ; ) {
9482
+ try {
9483
+ const p = Path.join(treeTop, ".git");
9484
+ gitStat = await Fs.stat(p);
9485
+ gitPath = p;
9486
+ break;
9487
+ } catch (err) {
9488
+ if (err.code !== "ENOENT") {
9489
+ throw err;
9490
+ }
9491
+ }
9492
+ const parent = Path.dirname(treeTop);
9493
+ if (parent === treeTop) {
9494
+ break;
9495
+ }
9496
+ treeTop = parent;
9497
+ }
9498
+ if (!gitPath || gitStat.isDirectory()) {
9499
+ return dir;
9500
+ }
9501
+ try {
9502
+ const gitFile = await Fs.readFile(gitPath, "utf8");
9503
+ const m = gitFile.match(/^gitdir:\s*(.+)$/m);
9504
+ if (!m) {
9505
+ return dir;
9506
+ }
9507
+ let worktreeGitDir = m[1].trim();
9508
+ if (!Path.isAbsolute(worktreeGitDir)) {
9509
+ worktreeGitDir = Path.resolve(treeTop, worktreeGitDir);
9510
+ }
9511
+ const commondir = (await Fs.readFile(Path.join(worktreeGitDir, "commondir"), "utf8")).trim();
9512
+ const commonGitDir = Path.isAbsolute(commondir) ? commondir : Path.resolve(worktreeGitDir, commondir);
9513
+ if (Path.basename(commonGitDir) !== ".git") {
9514
+ return dir;
9515
+ }
9516
+ const mainTreeTop = Path.dirname(commonGitDir);
9517
+ const rel = Path.relative(treeTop, startDir);
9518
+ return Path.join(mainTreeTop, rel);
9519
+ } catch (err) {
9520
+ logger.debug(`resolveGitMainWorktreeDir failed for ${dir}: ${err.message}`);
9521
+ return dir;
9522
+ }
9523
+ },
9405
9524
  removeAuthInfo(rcObj) {
9406
9525
  const rmObj = {};
9407
9526
  for (const key in rcObj) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fyn",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "fyn is the NPM for fynpo -- a zero setup monorepo tool",
5
5
  "main": "./bin/fyn.js",
6
6
  "homepage": "https://jchip.github.io/fynpo/",