@cplieger/deadset-ts 8.0.0-dev.11 → 8.0.0-dev.13

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 (73) hide show
  1. package/README.md +2 -0
  2. package/THIRD_PARTY_NOTICES.md +27 -0
  3. package/dist/src/build-scripts.js +6 -13
  4. package/dist/src/commands/read.js +156 -0
  5. package/dist/src/commands/shell.js +75 -0
  6. package/dist/src/commands/tools.js +460 -0
  7. package/dist/src/configuration-files.js +11 -25
  8. package/dist/src/conformance-record.js +1 -1
  9. package/dist/src/dependencies.js +67 -115
  10. package/dist/src/load/synthesize.js +12 -5
  11. package/dist/src/manifest.js +27 -14
  12. package/dist/src/roots.js +18 -11
  13. package/dist/src/text/named-files.js +24 -3
  14. package/dist/src/text/yaml.js +464 -173
  15. package/dist/src/vendor/unbash/ansi-c.js +132 -0
  16. package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
  17. package/dist/src/vendor/unbash/arithmetic.js +591 -0
  18. package/dist/src/vendor/unbash/assignment.js +131 -0
  19. package/dist/src/vendor/unbash/chars.js +48 -0
  20. package/dist/src/vendor/unbash/command.js +41 -0
  21. package/dist/src/vendor/unbash/expansion.js +80 -0
  22. package/dist/src/vendor/unbash/glob.js +268 -0
  23. package/dist/src/vendor/unbash/heredoc.js +27 -0
  24. package/dist/src/vendor/unbash/internal-types.js +1 -0
  25. package/dist/src/vendor/unbash/lexer.js +4274 -0
  26. package/dist/src/vendor/unbash/limits.js +3 -0
  27. package/dist/src/vendor/unbash/parser.js +1366 -0
  28. package/dist/src/vendor/unbash/parts.js +57 -0
  29. package/dist/src/vendor/unbash/substitution-context.js +8 -0
  30. package/dist/src/vendor/unbash/traversal.js +212 -0
  31. package/dist/src/vendor/unbash/types.js +1 -0
  32. package/dist/src/vendor/unbash/word-value.js +61 -0
  33. package/dist/src/vendor/unbash/word.js +40 -0
  34. package/dist/src/workflow-steps.js +111 -133
  35. package/jsr.json +2 -1
  36. package/package.json +9 -7
  37. package/src/build-scripts.ts +6 -13
  38. package/src/commands/read.ts +230 -0
  39. package/src/commands/shell.ts +126 -0
  40. package/src/commands/tools.ts +618 -0
  41. package/src/configuration-files.ts +10 -26
  42. package/src/conformance-record.ts +1 -1
  43. package/src/dependencies.ts +99 -123
  44. package/src/load/synthesize.ts +18 -5
  45. package/src/manifest.ts +43 -25
  46. package/src/roots.ts +20 -13
  47. package/src/text/named-files.ts +26 -3
  48. package/src/text/yaml.ts +503 -179
  49. package/src/vendor/unbash/LICENSE +15 -0
  50. package/src/vendor/unbash/VENDORED.md +16 -0
  51. package/src/vendor/unbash/ansi-c.ts +132 -0
  52. package/src/vendor/unbash/arithmetic-parts.ts +53 -0
  53. package/src/vendor/unbash/arithmetic.ts +641 -0
  54. package/src/vendor/unbash/assignment.ts +163 -0
  55. package/src/vendor/unbash/chars.ts +48 -0
  56. package/src/vendor/unbash/command.ts +47 -0
  57. package/src/vendor/unbash/expansion.ts +144 -0
  58. package/src/vendor/unbash/glob.ts +284 -0
  59. package/src/vendor/unbash/heredoc.ts +34 -0
  60. package/src/vendor/unbash/internal-types.ts +94 -0
  61. package/src/vendor/unbash/lexer.ts +4352 -0
  62. package/src/vendor/unbash/limits.ts +3 -0
  63. package/src/vendor/unbash/parser.ts +1612 -0
  64. package/src/vendor/unbash/parts.ts +96 -0
  65. package/src/vendor/unbash/substitution-context.ts +20 -0
  66. package/src/vendor/unbash/traversal.ts +182 -0
  67. package/src/vendor/unbash/types.ts +660 -0
  68. package/src/vendor/unbash/word-value.ts +64 -0
  69. package/src/vendor/unbash/word.ts +50 -0
  70. package/src/workflow-steps.ts +153 -154
  71. package/dist/src/text/shell.js +0 -159
  72. package/src/text/named-files.inputs.json +0 -1522
  73. package/src/text/shell.ts +0 -161
@@ -13,16 +13,17 @@ import { nodeKey, packageScope } from "./inventory.js";
13
13
  import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, } from "./paths.js";
14
14
  import { UNANSWERED } from "./query.js";
15
15
  import { sourceFilesOf } from "./source-files.js";
16
- import { manifestCommands } from "./manifest.js";
17
- import { workflowCommands, workflowTokens } from "./workflow-steps.js";
16
+ import { manifestCommands, nodemonTexts } from "./manifest.js";
17
+ import { commandFileTexts, workflowCommands } from "./workflow-steps.js";
18
+ import { pathsNamed, preloadedPackages, readCommands, } from "./commands/read.js";
19
+ import { heldNames } from "./text/named-files.js";
18
20
  import { bytewise } from "./text/compare.js";
19
21
  import { isRecord } from "./json-document.js";
20
22
  import { inInstalledPackages, INSTALLED_PACKAGES, MANIFEST, readDocument } from "./fs/manifest.js";
21
- import { commandsOf, scriptTokens } from "./text/shell.js";
22
23
  const MODULES_SEGMENT = `/${INSTALLED_PACKAGES}/`;
23
24
  const TYPES_SCOPE = "@types/";
24
- /** A script token naming a JavaScript file by a path no expansion changes. */
25
- const SCRIPT_FILE = /^[^$`{*?[~]+\.(?:js|mjs|cjs)$/u;
25
+ /** A path a command names that is a JavaScript file's. */
26
+ const SCRIPT_FILE = /\.(?:js|mjs|cjs)$/u;
26
27
  /** The manifest sections a dependency is declared in, each by the selector its reference spells. */
27
28
  const SECTIONS = [
28
29
  ["dependencies", "dependency"],
@@ -281,10 +282,10 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
281
282
  return { uses, at, unanswered };
282
283
  }
283
284
  /**
284
- * The JavaScript files no program holds that a script of a manifest at or above one of the
285
- * project's files runs, each with the directory of that manifest, and those a workflow
286
- * step or git hook runs, with the target root's: a token naming such a file below the
287
- * target root and in no `node_modules`, read against the manifest's directory.
285
+ * The JavaScript files no program holds that a command string of a manifest at or above one of
286
+ * the project's files runs or names, each with the directory of that manifest, and those a
287
+ * workflow step or git hook names, with the target root's: a path `pathsNamed` reads, below the
288
+ * target root and in no `node_modules`.
288
289
  */
289
290
  function scriptRunFiles(host, targetRoot, files) {
290
291
  const root = resolvePath(host.workingDirectory(), targetRoot);
@@ -297,23 +298,28 @@ function scriptRunFiles(host, targetRoot, files) {
297
298
  dirs.add(dir);
298
299
  }
299
300
  }
300
- const tokens = new Map([
301
- [root, new Set(workflowTokens(host, root))],
301
+ const named = new Map([
302
+ [
303
+ root,
304
+ workflowReadings(host, root)
305
+ .flatMap(pathsNamed)
306
+ .map((one) => one.path),
307
+ ],
302
308
  ]);
303
309
  for (const dir of dirs) {
304
310
  const read = readDocument(host, joinPath(dir, MANIFEST));
305
311
  if (read !== undefined && isRecord(read.value)) {
306
- const own = scriptTokensOf(read.value);
307
- tokens.set(dir, dir === root ? new Set([...own, ...(tokens.get(root) ?? [])]) : own);
312
+ const own = manifestReadings(host, dir, read.value)
313
+ .flatMap(pathsNamed)
314
+ .map((one) => one.path);
315
+ named.set(dir, [...own, ...(named.get(dir) ?? [])]);
308
316
  }
309
317
  }
310
318
  const found = [];
311
- for (const [manifestDir, held] of tokens) {
312
- for (const token of held) {
313
- const path = SCRIPT_FILE.test(token) ? resolvePath(manifestDir, token) : undefined;
314
- const below = path === undefined ? undefined : relativePath(root, path);
315
- if (path !== undefined &&
316
- below !== undefined &&
319
+ for (const [manifestDir, paths] of named) {
320
+ for (const path of new Set(paths)) {
321
+ const below = SCRIPT_FILE.test(path) ? relativePath(root, path) : undefined;
322
+ if (below !== undefined &&
317
323
  !inInstalledPackages(below) &&
318
324
  !files.byName.has(path) &&
319
325
  host.kindOf(path) === "file") {
@@ -323,6 +329,40 @@ function scriptRunFiles(host, targetRoot, files) {
323
329
  }
324
330
  return found;
325
331
  }
332
+ /** What the command strings of one manifest, its scripts and nodemon's command, run. */
333
+ function manifestReadings(host, dir, manifest) {
334
+ return manifestCommands(host, dir, manifest).map(({ command }) => readCommands(command, dir));
335
+ }
336
+ /** What one manifest's command strings use, held with the whole text of nodemon's configuration. */
337
+ function manifestUses(host, dir, manifest) {
338
+ const uses = usesOf(manifestReadings(host, dir, manifest));
339
+ const texts = nodemonTexts(host, dir, manifest).map(({ text }) => text);
340
+ return { ...uses, held: [uses.held, ...texts].join("\n") };
341
+ }
342
+ /** What the target root's workflow steps, git hooks and recipes use, held with their files' whole text. */
343
+ function workflowUses(host, targetRoot) {
344
+ const uses = usesOf(workflowReadings(host, targetRoot));
345
+ return { ...uses, held: [uses.held, ...commandFileTexts(host, targetRoot)].join("\n") };
346
+ }
347
+ /**
348
+ * What the target root's workflow steps, git hooks, hook managers and recipes run, each held
349
+ * with the directory the file writes for it.
350
+ */
351
+ function workflowReadings(host, targetRoot) {
352
+ return workflowCommands(host, targetRoot).map(({ text, dir, writtenDirectory }) => {
353
+ const reading = readCommands(text, dir);
354
+ return { ...reading, held: [reading.held, writtenDirectory ?? ""].join("\n") };
355
+ });
356
+ }
357
+ function usesOf(readings) {
358
+ return {
359
+ words: new Set(readings.flatMap((one) => one.commands.flatMap((command) => command.words.flatMap((word) => word ?? [])))),
360
+ preloads: new Set(readings
361
+ .flatMap((one) => one.runs.flatMap(preloadedPackages))
362
+ .flatMap((specifier) => packageOfSpecifier(specifier) ?? [])),
363
+ held: readings.map((one) => one.held).join("\n"),
364
+ };
365
+ }
326
366
  /** The declarations of the inventory a use written at one node belongs to. */
327
367
  function ownersOf(file, held, node) {
328
368
  const parent = node.parent;
@@ -633,95 +673,6 @@ function declaredIn(text, manifest, path, scope) {
633
673
  export function declarationKey(dependency) {
634
674
  return `${dependency.position.path}\u0000${dependency.name}`;
635
675
  }
636
- /** Every token of every value of one manifest's `scripts` member. */
637
- function scriptTokensOf(manifest) {
638
- const scripts = manifest["scripts"];
639
- const found = new Set();
640
- if (isRecord(scripts)) {
641
- for (const command of Object.values(scripts)) {
642
- if (typeof command === "string") {
643
- scriptTokens(command).forEach((token) => found.add(token));
644
- }
645
- }
646
- }
647
- return found;
648
- }
649
- /**
650
- * The runtime flags whose value is a module loaded before the entry, as `--flag value` or
651
- * `--flag=value`: https://nodejs.org/api/cli.html#--importmodule,
652
- * https://nodejs.org/api/cli.html#--experimental-loadermodule,
653
- * https://nodejs.org/api/cli.html#-r---require-module
654
- */
655
- const PRELOAD_FLAGS = new Set([
656
- "--experimental-loader",
657
- "--import",
658
- "--loader",
659
- "--require",
660
- "-r",
661
- ]);
662
- /**
663
- * The programs that pass a preload flag to the runtime they start: Node itself, and the
664
- * runners that forward Node's flags or read the same `--require`:
665
- * https://mochajs.org/#-require-module-r-module, https://tsx.is/node-enhancement,
666
- * https://typestrong.org/ts-node/docs/options#require,
667
- * https://github.com/remy/nodemon#running-non-node-scripts
668
- */
669
- const PRELOADING_PROGRAMS = new Set([
670
- "mocha",
671
- "node",
672
- "nodemon",
673
- "ts-node",
674
- "tsx",
675
- ]);
676
- /** The programs that run the command after them, and an environment assignment before it. */
677
- const LAUNCHERS = new Set(["cross-env", "env", "npx"]);
678
- const ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/u;
679
- /**
680
- * The packages `scripts` preload by a runtime flag's bare module specifier, a flag
681
- * counting only in a command whose program passes it to the runtime, and for Node only
682
- * before the script it runs, whose own arguments come after it.
683
- */
684
- function preloadsOf(scripts) {
685
- const found = new Set();
686
- for (const script of scripts) {
687
- for (const command of commandsOf(script)) {
688
- const tokens = scriptTokens(command);
689
- let at = 0;
690
- while (at < tokens.length &&
691
- (ASSIGNMENT.test(tokens[at] ?? "") || LAUNCHERS.has(tokens[at] ?? ""))) {
692
- at += 1;
693
- }
694
- const path = tokens[at] ?? "";
695
- const program = path.slice(path.lastIndexOf("/") + 1);
696
- if (!PRELOADING_PROGRAMS.has(program)) {
697
- continue;
698
- }
699
- for (at += 1; at < tokens.length; at += 1) {
700
- const token = tokens[at] ?? "";
701
- if (!token.startsWith("-")) {
702
- if (program === "node") {
703
- break;
704
- }
705
- continue;
706
- }
707
- const equals = token.indexOf("=");
708
- const flag = equals < 0 ? token : token.slice(0, equals);
709
- if (!PRELOAD_FLAGS.has(flag)) {
710
- continue;
711
- }
712
- if (equals < 0) {
713
- at += 1;
714
- }
715
- const value = equals < 0 ? tokens[at] : token.slice(equals + 1);
716
- const name = value === undefined ? undefined : packageOfSpecifier(value);
717
- if (name !== undefined) {
718
- found.add(name);
719
- }
720
- }
721
- }
722
- }
723
- return found;
724
- }
725
676
  /** Whether a path below the target root lies in the directory `dir` below it, `""` being the root. */
726
677
  function isInside(path, dir) {
727
678
  return dir === "" || path.startsWith(`${dir}/`);
@@ -792,8 +743,7 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
792
743
  unbuilt.add(path);
793
744
  }
794
745
  }
795
- const workflow = new Set(workflowTokens(host, targetRoot));
796
- const workflowPreloads = preloadsOf(workflowCommands(host, targetRoot));
746
+ const workflow = workflowUses(host, targetRoot);
797
747
  const declared = [];
798
748
  const ran = new Set();
799
749
  const peers = new Map();
@@ -803,23 +753,25 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
803
753
  if (read === undefined || !isRecord(read.value)) {
804
754
  continue;
805
755
  }
806
- const tokens = scriptTokensOf(read.value);
807
- const preloads = preloadsOf(manifestCommands(host, dir, read.value).map(({ command }) => command));
756
+ const own = manifestUses(host, dir, read.value);
757
+ const held = `${own.held}\n${workflow.held}`;
808
758
  for (const dependency of declaredIn(read.text, read.value, path, scopeOf(path))) {
809
759
  const found = installedOf(host, dir, dependency.name);
810
760
  if (found === undefined && uninstalledHere.has(dir)) {
811
761
  continue;
812
762
  }
813
763
  declared.push(dependency);
814
- if (preloads.has(dependency.name) || workflowPreloads.has(dependency.name)) {
764
+ if (own.preloads.has(dependency.name) ||
765
+ workflow.preloads.has(dependency.name) ||
766
+ heldNames(held, [dependency.name]).size > 0) {
815
767
  ran.add(declarationKey(dependency));
816
768
  }
817
769
  if (found === undefined) {
818
770
  continue;
819
771
  }
820
772
  peers.set(declarationKey(dependency), found.peers);
821
- const runs = (command) => tokens.has(command) || workflow.has(command);
822
- if (found.commands.some(runs)) {
773
+ const runs = (command) => own.words.has(command) || workflow.words.has(command);
774
+ if (found.commands.some(runs) || heldNames(held, found.commands).size > 0) {
823
775
  ran.add(declarationKey(dependency));
824
776
  }
825
777
  }
@@ -4,11 +4,13 @@
4
4
  * configuration only the snapshot holds, with the module system of the runtime its package
5
5
  * is written for.
6
6
  */
7
+ import { readCommands } from "../commands/read.js";
7
8
  import { CONVENTION_ROWS } from "../convention-rows.js";
8
9
  import { holdsManifest, MANIFEST, readManifestAt } from "../fs/manifest.js";
9
10
  import { filesBelow } from "../fs/tree.js";
10
11
  import { JAVASCRIPT_FILE, minified, minifiedByName } from "../javascript-file.js";
11
12
  import { isRecord } from "../json-document.js";
13
+ import { manifestCommands } from "../manifest.js";
12
14
  import { ancestors, dirnamePath, joinPath, relativePath } from "../paths.js";
13
15
  import { bytewise } from "../text/compare.js";
14
16
  import { writtenPath } from "./workspace-programs.js";
@@ -89,11 +91,15 @@ const RUNTIME_OPTIONS = {
89
91
  };
90
92
  /** The manifest members that say a package is written for Node. */
91
93
  const NODE_MEMBERS = ["main", "exports", "bin", "type"];
92
- /** Whether one manifest says its package runs under Node, `engines.node` included; its scripts are not read. */
93
- function writtenForNode(manifest) {
94
+ /**
95
+ * Whether one manifest says its package runs under Node: a Node member, `engines.node`
96
+ * included, or a command string of it that runs a file with `node`, as `commands/read.ts` reads it.
97
+ */
98
+ function writtenForNode(host, dir, manifest) {
94
99
  const { engines } = manifest;
95
100
  return (NODE_MEMBERS.some((member) => manifest[member] !== undefined) ||
96
- (isRecord(engines) && engines["node"] !== undefined));
101
+ (isRecord(engines) && engines["node"] !== undefined) ||
102
+ manifestCommands(host, dir, manifest).some(({ command }) => readCommands(command, dir).runs.some((run) => run.runtime === "node")));
97
103
  }
98
104
  /**
99
105
  * The manifests, by path as `conventions` names them, an applied row of a tool that bundles
@@ -107,7 +113,8 @@ export function bundlingManifests(conventions) {
107
113
  /**
108
114
  * Each group's program, given the module system of the runtime its package is written for: a
109
115
  * bundler's where a row of a tool that bundles applies through the group's manifest or the
110
- * root's, else Node's where its manifest says the package runs under Node, under which a file's
116
+ * root's, else Node's where its manifest says the package runs under Node or a command string of
117
+ * it runs a file with `node`, under which a file's
111
118
  * extension and its package's `type` decide its module format, else a bundler's, which reads a
112
119
  * file with no module syntax as a classic script, as a browser loads it.
113
120
  */
@@ -118,7 +125,7 @@ export function formedPrograms(engine, host, groups, { root, origin, bundling, i
118
125
  const runtime = !bundling.has(MANIFEST) &&
119
126
  (below === undefined || !bundling.has(below)) &&
120
127
  group.manifest.kind === "read" &&
121
- writtenForNode(group.manifest.manifest)
128
+ writtenForNode(host, group.dir, group.manifest.manifest)
122
129
  ? "node"
123
130
  : "bundler";
124
131
  const configFile = writtenPath(host, joinPath(group.dir, GROUP_CONFIGURATION));
@@ -7,8 +7,7 @@
7
7
  import { isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
8
8
  import { isRecord } from "./json-document.js";
9
9
  import { MANIFEST, manifestAt, readJSON } from "./fs/manifest.js";
10
- import { scriptTokens } from "./text/shell.js";
11
- /** The manifest's own name. */
10
+ import { pathsNamed, readCommands } from "./commands/read.js";
12
11
  /** The three members that name an entry point beside `exports` and `bin`. */
13
12
  const LEGACY_MEMBERS = ["main", "module", "types"];
14
13
  /**
@@ -26,6 +25,25 @@ function isTarget(value, requireDot) {
26
25
  function below(member, key) {
27
26
  return `${member}[${JSON.stringify(key)}]`;
28
27
  }
28
+ /**
29
+ * The whole text of nodemon's configuration, the manifest's `nodemonConfig` member and the
30
+ * `nodemon.json` beside it, by the member each is. Layer 3 holds it whole, because nodemon runs
31
+ * commands from keys besides `exec`, `events` among them: https://github.com/remy/nodemon#config-files
32
+ */
33
+ export function nodemonTexts(host, dir, manifest) {
34
+ const member = manifest["nodemonConfig"];
35
+ let file;
36
+ try {
37
+ file = host.readFile(joinPath(dir, "nodemon.json"));
38
+ }
39
+ catch {
40
+ file = undefined;
41
+ }
42
+ return [
43
+ ...(member === undefined ? [] : [{ member: "nodemonConfig", text: JSON.stringify(member) }]),
44
+ ...(file === undefined ? [] : [{ member: "nodemon.json", text: file }]),
45
+ ];
46
+ }
29
47
  /**
30
48
  * The command strings one manifest runs, each at the member it is written under: each of its
31
49
  * `scripts`, and the `exec` command of nodemon's configuration, in the manifest's `nodemonConfig`
@@ -118,20 +136,15 @@ export function readManifest(host, targetRoot) {
118
136
  }
119
137
  }
120
138
  exportTargets(manifest["exports"], "exports", found);
121
- const scripts = manifest["scripts"];
122
- if (isRecord(scripts)) {
123
- for (const [name, command] of Object.entries(scripts)) {
124
- if (typeof command !== "string") {
125
- continue;
126
- }
127
- const member = below("scripts", name);
128
- for (const path of new Set(scriptTokens(command))) {
129
- found.push({ member, path, role: "script", published: false });
130
- }
139
+ for (const { member, command } of manifestCommands(host, targetRoot, manifest)) {
140
+ // Read against ".", so each path stays relative until the entries are resolved below.
141
+ for (const path of new Set(pathsNamed(readCommands(command, ".")).map((one) => one.path))) {
142
+ found.push({ member, path, role: "script", published: false });
131
143
  }
144
+ found.push({ member, path: ".", role: "script", published: false, held: command });
132
145
  }
133
- for (const { member, command } of manifestCommands(host, targetRoot, manifest)) {
134
- found.push({ member, path: ".", role: "script", published: false, command });
146
+ for (const { member, text } of nodemonTexts(host, targetRoot, manifest)) {
147
+ found.push({ member, path: ".", role: "script", published: false, held: text });
135
148
  }
136
149
  return {
137
150
  entries: found.map((entry) => ({
package/dist/src/roots.js CHANGED
@@ -20,7 +20,8 @@ import { bytewise } from "./text/compare.js";
20
20
  import { holdsManifest } from "./fs/manifest.js";
21
21
  import { globExpression } from "./text/glob.js";
22
22
  import { spelledFiles } from "./text/named-files.js";
23
- import { commandDirectories, workflowCommands } from "./workflow-steps.js";
23
+ import { commandFileTexts, workflowCommands } from "./workflow-steps.js";
24
+ import { readCommands } from "./commands/read.js";
24
25
  /** Whether one declaration is written below the directory of a published workspace member. */
25
26
  function inPublishedMember(held, id, root, members) {
26
27
  const path = held.symbols.find((symbol) => symbol.id === id)?.position.path;
@@ -174,11 +175,11 @@ function childrenOf(held) {
174
175
  return byParent;
175
176
  }
176
177
  /**
177
- * Per package, the own files its command strings name as plain text, but those a root enters
178
- * with all they export: a command can use no more of such a file than the root keeps live. A
179
- * manifest's are its entries' whole command strings, and the target root's package has the
180
- * workflow steps, git hooks and makefile recipes beside its own, with the directories its
181
- * workflow steps and lefthook commands run in, where a `.` names that directory's index file.
178
+ * Per package, the own files its held texts name, as `spelledFiles` reads them, but those a
179
+ * root enters with all they export: a command can use no more of such a file than the root
180
+ * keeps live. A manifest's are its entries' held texts, and the target root's package has the
181
+ * workflow steps, git hooks and makefile recipes beside its own, with the whole text of each
182
+ * file `commandFileTexts` reads.
182
183
  */
183
184
  function scriptHolds(entries, files, entered, host, targetRoot) {
184
185
  const rooted = new Set(entered.filter((one) => one.exports && one.names === undefined).map((one) => one.file.fileName));
@@ -189,13 +190,19 @@ function scriptHolds(entries, files, entered, host, targetRoot) {
189
190
  const commands = new Map([
190
191
  [
191
192
  resolvePath(cwd, targetRoot),
192
- [...workflowCommands(host, targetRoot), ...commandDirectories(host, targetRoot)],
193
+ [
194
+ ...workflowCommands(host, targetRoot).flatMap(({ text, dir, writtenDirectory }) => [
195
+ readCommands(text, dir).held,
196
+ ...(writtenDirectory === undefined ? [] : [writtenDirectory]),
197
+ ]),
198
+ ...commandFileTexts(host, targetRoot),
199
+ ],
193
200
  ],
194
201
  ]);
195
202
  for (const entry of entries) {
196
- if (entry.command !== undefined) {
203
+ if (entry.held !== undefined) {
197
204
  const dir = resolvePath(cwd, entry.dir);
198
- commands.set(dir, [...(commands.get(dir) ?? []), entry.command]);
205
+ commands.set(dir, [...(commands.get(dir) ?? []), entry.held]);
199
206
  }
200
207
  }
201
208
  const held = [];
@@ -249,7 +256,7 @@ export function roots(project, held, targetRoot, options) {
249
256
  (options.publishedAPI || options.publishedMembers?.has(entry.dir) === true),
250
257
  })),
251
258
  ];
252
- const scripted = entries.flatMap(({ entry }) => entry.role === "script" && entry.command === undefined ? files.named(entry.path) : []);
259
+ const scripted = entries.flatMap(({ entry }) => entry.role === "script" && entry.held === undefined ? files.named(entry.path) : []);
253
260
  const readBackTo = [
254
261
  ...scripted,
255
262
  ...points.filter((point) => READ_BACK_RULES.has(point.rule)).map((point) => point.file),
@@ -299,7 +306,7 @@ export function roots(project, held, targetRoot, options) {
299
306
  const unmapped = [];
300
307
  const mapped = patterned.map(({ file }) => file);
301
308
  for (const { entry } of entries) {
302
- if (entry.command !== undefined) {
309
+ if (entry.held !== undefined) {
303
310
  continue;
304
311
  }
305
312
  const named = entryFiles(entry);
@@ -1,7 +1,7 @@
1
1
  /**
2
- * The files a command's text names, read as plain text: the text is searched for each file's
3
- * names, which every spelling of its path contains, so no part of it is split, unquoted or
4
- * skipped first.
2
+ * Layer 3 of the command reader: the files and the package names a command's text names, read
3
+ * as plain text. The text is searched for each name, which every spelling of what it names
4
+ * contains, so no part of it is split, unquoted or skipped first.
5
5
  */
6
6
  import { dirnamePath, normalizePath, relativePath } from "../paths.js";
7
7
  /** The extension a source or an emitted file carries: a JavaScript or TypeScript one, or a declaration's. */
@@ -52,3 +52,24 @@ export function spelledFiles(text, dir, files) {
52
52
  }
53
53
  return [...found];
54
54
  }
55
+ /** A character a package's or a command's name may hold, so one beside a name makes it part of a longer one. */
56
+ const NAME_CHARACTER = /[\w.@-]/u;
57
+ /**
58
+ * Each of `names`, a package's or a command's name, that `text` holds with no name character
59
+ * before it and no character after it but one a path or a version may go on with, `/`, `.` or
60
+ * `@` among them: `module-alias/register` holds `module-alias`, and `tsxx` holds no `tsx`.
61
+ */
62
+ export function heldNames(text, names) {
63
+ const found = new Set();
64
+ for (const name of names) {
65
+ for (let at = text.indexOf(name); at >= 0 && name !== ""; at = text.indexOf(name, at + 1)) {
66
+ const before = text.charAt(at - 1);
67
+ const after = text.charAt(at + name.length);
68
+ if (!NAME_CHARACTER.test(before) && !/[\w-]/u.test(after)) {
69
+ found.add(name);
70
+ break;
71
+ }
72
+ }
73
+ }
74
+ return found;
75
+ }