fyn 3.1.7 → 3.1.8

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 (3) hide show
  1. package/README.md +6 -0
  2. package/dist/fyn.mjs +129 -27
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,6 +16,12 @@ cd <your-project>
16
16
  fyn
17
17
  ```
18
18
 
19
+ On an existing install, fyn checks file modification times before reinstalling. This scan
20
+ prunes directories and files matched by repository and nested `.gitignore` rules, alongside
21
+ its built-in exclusions. The rules apply even to tracked source files; manifests, lockfiles,
22
+ install configuration and changes to the consulted ignore rules are still checked.
23
+ Use `fyn --fi` to bypass this freshness check and force installation.
24
+
19
25
  Want to add a package on your local disk as a dependency to your project? Do this:
20
26
 
21
27
  ```sh
package/dist/fyn.mjs CHANGED
@@ -26965,6 +26965,65 @@ const join2 = (sep, a, b) => {
26965
26965
  else return a + sep + b;
26966
26966
  };
26967
26967
 
26968
+ //#endregion
26969
+ //#region node_modules/filter-scan-dir/dist/gitignore.js
26970
+ function ancestorDirs(dir) {
26971
+ const dirs = [dir];
26972
+ for (let current = dir;;) {
26973
+ if (fs.existsSync(Path.join(current, ".git"))) return dirs.reverse();
26974
+ const parent = Path.dirname(current);
26975
+ if (parent === current) return [dir];
26976
+ dirs.push(parent);
26977
+ current = parent;
26978
+ }
26979
+ }
26980
+ var GitignoreRules = class GitignoreRules {
26981
+ constructor(rules = []) {
26982
+ this.rules = rules;
26983
+ }
26984
+ ignores(file, directory) {
26985
+ let ignored = false;
26986
+ for (const { dir, matcher } of this.rules) {
26987
+ const relative = Path.relative(dir, file).split(Path.sep).join("/");
26988
+ if (!relative) continue;
26989
+ const result = matcher.test(relative + (directory ? "/" : ""));
26990
+ if (result.ignored) ignored = true;
26991
+ else if (result.unignored) ignored = false;
26992
+ }
26993
+ return ignored;
26994
+ }
26995
+ add(dir, source, parse) {
26996
+ return new GitignoreRules([...this.rules, {
26997
+ dir,
26998
+ matcher: parse(source)
26999
+ }]);
27000
+ }
27001
+ static loadSync(dir, parse, parent) {
27002
+ let rules = parent || new GitignoreRules();
27003
+ for (const base of parent ? [dir] : ancestorDirs(dir)) {
27004
+ if (!parent && rules.ignores(base, true)) rules = new GitignoreRules();
27005
+ try {
27006
+ rules = rules.add(base, fs.readFileSync(Path.join(base, ".gitignore"), "utf8"), parse);
27007
+ } catch (error) {
27008
+ if (error.code !== "ENOENT" && error.code !== "ENOTDIR") throw error;
27009
+ }
27010
+ }
27011
+ return rules;
27012
+ }
27013
+ static async load(dir, parse, parent) {
27014
+ let rules = parent || new GitignoreRules();
27015
+ for (const base of parent ? [dir] : ancestorDirs(dir)) {
27016
+ if (!parent && rules.ignores(base, true)) rules = new GitignoreRules();
27017
+ try {
27018
+ rules = rules.add(base, await fs.promises.readFile(Path.join(base, ".gitignore"), "utf8"), parse);
27019
+ } catch (error) {
27020
+ if (error.code !== "ENOENT" && error.code !== "ENOTDIR") throw error;
27021
+ }
27022
+ }
27023
+ return rules;
27024
+ }
27025
+ };
27026
+
26968
27027
  //#endregion
26969
27028
  //#region node_modules/filter-scan-dir/dist/index.js
26970
27029
  /**
@@ -27051,9 +27110,10 @@ function processFile(options, extras) {
27051
27110
  }
27052
27111
  return false;
27053
27112
  }
27054
- function acceptEntry(options, entry, path) {
27113
+ function acceptEntry(options, entry, path, gitignore) {
27055
27114
  const isDirectory = entry.isDirectory();
27056
27115
  if (isDirectory && options._ignoreDirs.has(entry.name)) return false;
27116
+ if (gitignore === null || gitignore === void 0 ? void 0 : gitignore.ignores(Path.resolve(options.dir, path, entry.name), isDirectory)) return false;
27057
27117
  if (options.prefilter && !options.prefilter(entry.name, path, entry)) return false;
27058
27118
  if (isDirectory) return true;
27059
27119
  if (options.ignoreExt.length || options.filterExt.length) {
@@ -27096,7 +27156,7 @@ function releaseDirectory(options) {
27096
27156
  * @param level
27097
27157
  * @returns
27098
27158
  */
27099
- async function walk(path, options, level = 0) {
27159
+ async function walk(path, options, level = 0, parentRules) {
27100
27160
  let promises = [];
27101
27161
  let hasSlot = false;
27102
27162
  try {
@@ -27107,6 +27167,7 @@ async function walk(path, options, level = 0) {
27107
27167
  }
27108
27168
  if (options._stopped) return void 0;
27109
27169
  const dir = Path.join(options.dir, path);
27170
+ const gitignore = options.gitignore ? await GitignoreRules.load(Path.resolve(dir), options.gitignore, parentRules) : void 0;
27110
27171
  let files = await asyncReaddir(dir, options.readdirOpts);
27111
27172
  if (options.sortFiles) {
27112
27173
  if (!options.readdirOpts) files = files.sort();
@@ -27116,7 +27177,7 @@ async function walk(path, options, level = 0) {
27116
27177
  const extrasFiles = options.fullStat && options._earlyFilter && (options.filter || options.filterDir) ? files.map((entry) => entry.name) : files;
27117
27178
  for (let ix = 0; !options._stopped && ix < files.length; ix++) {
27118
27179
  const file = files[ix];
27119
- if (options._earlyFilter && !acceptEntry(options, file, path)) continue;
27180
+ if (options._earlyFilter && !acceptEntry(options, file, path, gitignore)) continue;
27120
27181
  let extras;
27121
27182
  if (options.fullStat) {
27122
27183
  const name = options._earlyFilter ? file.name : file;
@@ -27143,7 +27204,7 @@ async function walk(path, options, level = 0) {
27143
27204
  break;
27144
27205
  }
27145
27206
  if (!flags.skip && level < options.maxLevel) {
27146
- const walkP = walk(extras.dirFile, options, level + 1);
27207
+ const walkP = walk(extras.dirFile, options, level + 1, gitignore);
27147
27208
  if (options.concurrency > 1) {
27148
27209
  promises.push(walkP);
27149
27210
  if (promises.length >= options.concurrency) {
@@ -27207,7 +27268,7 @@ function makeOptions(opts) {
27207
27268
  _earlyFilter: false,
27208
27269
  _ignoreDirs: new Set([].concat(options.ignoreDirs || []))
27209
27270
  });
27210
- opts2._earlyFilter = !!opts2.prefilter || opts2._ignoreDirs.size > 0 || opts2.ignoreExt.length > 0 || opts2.filterExt.length > 0;
27271
+ opts2._earlyFilter = !!opts2.gitignore || !!opts2.prefilter || opts2._ignoreDirs.size > 0 || opts2.ignoreExt.length > 0 || opts2.filterExt.length > 0;
27211
27272
  if (!opts2.fullStat || opts2._earlyFilter) opts2.readdirOpts = { withFileTypes: true };
27212
27273
  return opts2;
27213
27274
  }
@@ -28836,20 +28897,20 @@ const resolveAutoSearch = (val) => {
28836
28897
  if (val === false) return {
28837
28898
  enable: false,
28838
28899
  respectGitignore: false,
28839
- stopOnPackageJsonFound: false
28900
+ stopOnPackageJsonFound: true
28840
28901
  };
28841
28902
  if (val && typeof val === "object") {
28842
28903
  const obj = val;
28843
28904
  return {
28844
28905
  enable: obj.enable !== false,
28845
28906
  respectGitignore: obj.respectGitignore === true,
28846
- stopOnPackageJsonFound: obj.stopOnPackageJsonFound === true
28907
+ stopOnPackageJsonFound: obj.stopOnPackageJsonFound !== false
28847
28908
  };
28848
28909
  }
28849
28910
  return {
28850
28911
  enable: true,
28851
28912
  respectGitignore: false,
28852
- stopOnPackageJsonFound: false
28913
+ stopOnPackageJsonFound: true
28853
28914
  };
28854
28915
  };
28855
28916
  /**
@@ -28857,12 +28918,12 @@ const resolveAutoSearch = (val) => {
28857
28918
  *
28858
28919
  * Accepts both shapes:
28859
28920
  *
28860
- * - **array** - the historical form. Treated as `publishInclude`, with auto-search on and
28861
- * `respectGitignore` off. It no longer narrows discovery.
28921
+ * - **array** - the historical form. Treated as additive `include` and `publishInclude`,
28922
+ * with auto-search on and `respectGitignore` off. It no longer narrows discovery.
28862
28923
  * - **object** - `{ autoSearch, include, exclude, publishInclude, publishExclude }`.
28863
28924
  *
28864
- * Defaults: `autoSearch` on, `respectGitignore` off. With auto-search off and no `include`,
28865
- * `include` falls back to `["packages/*"]`.
28925
+ * Defaults: `autoSearch` on, `respectGitignore` off, stop at package.json. With auto-search
28926
+ * off and no `include`, `include` falls back to `["packages/*"]`.
28866
28927
  *
28867
28928
  * @param packages - the raw `packages` value from fynpo.json / fynpo.config.js
28868
28929
  * @returns the resolved config, every field populated
@@ -28874,7 +28935,7 @@ function resolvePackagesConfig(packages) {
28874
28935
  autoSearch: {
28875
28936
  enable: true,
28876
28937
  respectGitignore: false,
28877
- stopOnPackageJsonFound: false
28938
+ stopOnPackageJsonFound: true
28878
28939
  },
28879
28940
  include: list,
28880
28941
  exclude: [],
@@ -28898,9 +28959,9 @@ function resolvePackagesConfig(packages) {
28898
28959
  * Decide how to scan for packages.
28899
28960
  *
28900
28961
  * `include` does NOT turn auto-search off - auto-search is on by default and stays on, so it
28901
- * still decides *how the tree is walked*. `include` then filters what the walk found, via
28902
- * {@link includeFilter}. Only with auto-search off does `include` become the scan patterns
28903
- * themselves, falling back to `packages/*`.
28962
+ * still decides *how the tree is walked*. `include` adds explicit paths below package
28963
+ * boundaries. Only with auto-search off does `include` restrict the scan paths,
28964
+ * falling back to `packages/*`.
28904
28965
  *
28905
28966
  * @param config - resolved packages config
28906
28967
  * @returns `null` to auto-search the whole repo, otherwise the patterns to scan
@@ -28910,9 +28971,8 @@ function scanPatterns(config) {
28910
28971
  return config.include.length > 0 ? config.include : [...DEFAULT_INCLUDE];
28911
28972
  }
28912
28973
  /**
28913
- * The patterns a discovered package must match to be kept.
28914
- *
28915
- * Empty means keep everything the scan found.
28974
+ * Explicit inclusion patterns, used to add nested paths and promote them to managed packages.
28975
+ * With auto-search disabled, they restrict discovery to the declared paths.
28916
28976
  *
28917
28977
  * @param config - resolved packages config
28918
28978
  * @returns patterns to match a package path against
@@ -29735,9 +29795,9 @@ var FynpoDepGraph = class {
29735
29795
  const mms = resolved.map((p) => new Minimatch(p));
29736
29796
  groups = groupMM(mms, {});
29737
29797
  }
29738
- const foundInAutoSearch = (path) => {
29798
+ const foundInAutoSearch = (path, partial = false) => {
29739
29799
  if (!autoSearch || !pkgConfig.autoSearch.stopOnPackageJsonFound) return false;
29740
- for (const e of autoSearchFound) if (isPathInside$1(path, e)) return true;
29800
+ for (const e of autoSearchFound) if (isPathInside$1(path, e)) return !includeMms.some((m) => m.match(posixify$2(path), partial));
29741
29801
  return false;
29742
29802
  };
29743
29803
  const files = [];
@@ -29757,7 +29817,7 @@ var FynpoDepGraph = class {
29757
29817
  },
29758
29818
  filterDir: (dir, path, extras) => {
29759
29819
  if (dir !== "node_modules") {
29760
- if (foundInAutoSearch(path) || isExcluded(path) || skipForAutoSearch(path)) return false;
29820
+ if (foundInAutoSearch(extras.dirFile, true) || isExcluded(path) || skipForAutoSearch(path)) return false;
29761
29821
  return prefix === "." && groups[prefix] === null ? !dir.startsWith(".") : Boolean(groups[prefix].find((save) => save.mm.match(extras.dirFile)));
29762
29822
  }
29763
29823
  return false;
@@ -29775,7 +29835,7 @@ var FynpoDepGraph = class {
29775
29835
  const pkgPath = posixify$2(Path.dirname(pkgFile));
29776
29836
  const nested = autoSearch && packageRoots.some((root) => isPathInside$1(pkgPath, root));
29777
29837
  const included = isIncluded(pkgPath);
29778
- if (!included && !nested) continue;
29838
+ if (!autoSearch && !included) continue;
29779
29839
  const managed = !autoSearch || !nested || includeMms.length > 0 && included;
29780
29840
  const pkgStr = await promises.readFile(Path.join(cwd, pkgFile), "utf-8");
29781
29841
  const pkgJson = JSON.parse(pkgStr);
@@ -30667,7 +30727,7 @@ const fyntil = {
30667
30727
  }
30668
30728
  const graph = new FynpoDepGraph({
30669
30729
  cwd: fcm.topDir,
30670
- patterns: config.packages,
30730
+ packages: config.packages,
30671
30731
  noFynLocal: config.noFynLocal
30672
30732
  });
30673
30733
  await graph.resolve();
@@ -106667,7 +106727,8 @@ var FynCentral = class {
106667
106727
  //#endregion
106668
106728
  //#region lib/util/stat-dir.ts
106669
106729
  async function _scanFileStats(dir, ignores, baseDir = "") {
106670
- const ignore = (fullPath) => ignores.find((pattern) => minimatch(fullPath, pattern, { dot: true }));
106730
+ const patterns = ignores.map((pattern) => new Minimatch(pattern, { dot: true }));
106731
+ const ignore = (fullPath) => patterns.find((pattern) => pattern.match(fullPath));
106671
106732
  let latestMtimeMs = 0;
106672
106733
  let latestFile = "";
106673
106734
  const updateLatest = (mtimeMs, file) => {
@@ -106676,6 +106737,13 @@ async function _scanFileStats(dir, ignores, baseDir = "") {
106676
106737
  latestFile = file;
106677
106738
  }
106678
106739
  };
106740
+ const inspectFile = (file) => {
106741
+ try {
106742
+ updateLatest(file_ops_default.statSync(file).mtimeMs, file);
106743
+ } catch (err) {
106744
+ if (err.code !== "ENOENT" && err.code !== "ENOTDIR") throw err;
106745
+ }
106746
+ };
106679
106747
  const filter = (file, path, extras) => {
106680
106748
  if (ignore(extras.fullFile)) return false;
106681
106749
  updateLatest(extras.stat.mtimeMs, extras.fullFile);
@@ -106683,11 +106751,45 @@ async function _scanFileStats(dir, ignores, baseDir = "") {
106683
106751
  };
106684
106752
  const fullDir = Path.join(baseDir, dir);
106685
106753
  updateLatest((await file_ops_default.stat(fullDir)).mtimeMs, fullDir);
106754
+ for (const file of [
106755
+ "package.json",
106756
+ "package-fyn.json",
106757
+ "fyn-lock.yaml",
106758
+ "package-lock.json",
106759
+ "npm-shrinkwrap.json",
106760
+ "yarn.lock",
106761
+ ".npmrc",
106762
+ ".fynrc",
106763
+ "fynpo.json",
106764
+ "fynpo.config.js",
106765
+ "fynpo.config.json"
106766
+ ]) inspectFile(Path.join(fullDir, file));
106767
+ const ruleDirs = [];
106768
+ let ancestor = Path.resolve(fullDir);
106769
+ while (true) {
106770
+ ruleDirs.push(ancestor);
106771
+ if (file_ops_default.existsSync(Path.join(ancestor, ".git"))) break;
106772
+ const parent = Path.dirname(ancestor);
106773
+ if (parent === ancestor) {
106774
+ ruleDirs.length = 1;
106775
+ break;
106776
+ }
106777
+ ancestor = parent;
106778
+ }
106779
+ for (const ruleDir of ruleDirs) {
106780
+ inspectFile(ruleDir);
106781
+ inspectFile(Path.join(ruleDir, ".gitignore"));
106782
+ }
106686
106783
  await filterScanDir({
106687
106784
  cwd: fullDir,
106688
106785
  prependCwd: false,
106786
+ gitignore: (rules) => (0, import_ignore.default)().add(rules),
106689
106787
  filter,
106690
- filterDir: filter,
106788
+ filterDir: (file, path, extras) => {
106789
+ if (!filter(file, path, extras)) return false;
106790
+ inspectFile(Path.join(extras.fullFile, ".gitignore"));
106791
+ return true;
106792
+ },
106691
106793
  concurrency: 500,
106692
106794
  fullStat: true
106693
106795
  });
@@ -106697,7 +106799,7 @@ async function _scanFileStats(dir, ignores, baseDir = "") {
106697
106799
  };
106698
106800
  }
106699
106801
  function scanFileStats(dir, options = {}) {
106700
- return _scanFileStats(dir, [`**/?(node_modules|_fyn|.vscode|.DS_Store|coverage|.nyc_output|.fynpo|.git|.github|.gitignore)`, "**/*.?(log|md)"].concat(options.ignores || `**/?(docs|docusaurus|packages|tmp|.etmp|samples|dist)`).concat(options.moreIgnores).filter((x) => x), "");
106802
+ return _scanFileStats(dir, [`**/?(node_modules|_fyn|.vscode|.DS_Store|coverage|.nyc_output|.fynpo|.git|.github|.gitignore)`, "**/*.?(log|md)"].concat(options.ignores || `**/?(docs|docusaurus|packages|tmp|.temp|.etmp|samples|dist)`).concat(options.moreIgnores).filter((x) => x), "");
106701
106803
  }
106702
106804
 
106703
106805
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fyn",
3
3
  "type": "module",
4
- "version": "3.1.7",
4
+ "version": "3.1.8",
5
5
  "description": "fyn is a node.js package manager, and supports fynpo -- a zero setup monorepo tool",
6
6
  "main": "./bin/index.mjs",
7
7
  "homepage": "https://github.com/jchip/fynjs/tree/main/packages/fyn",