@timiagama/ecr 0.0.1 → 0.1.0

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 (138) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/README.md +411 -7
  4. package/dist/bin.d.ts +20 -0
  5. package/dist/bin.d.ts.map +1 -0
  6. package/dist/bin.js +26 -0
  7. package/dist/bin.js.map +1 -0
  8. package/dist/cli/corpus-loader.d.ts +198 -0
  9. package/dist/cli/corpus-loader.d.ts.map +1 -0
  10. package/dist/cli/corpus-loader.js +351 -0
  11. package/dist/cli/corpus-loader.js.map +1 -0
  12. package/dist/cli/corpus-statistics.d.ts +113 -0
  13. package/dist/cli/corpus-statistics.d.ts.map +1 -0
  14. package/dist/cli/corpus-statistics.js +119 -0
  15. package/dist/cli/corpus-statistics.js.map +1 -0
  16. package/dist/cli/diagnostic-reporter.d.ts +103 -0
  17. package/dist/cli/diagnostic-reporter.d.ts.map +1 -0
  18. package/dist/cli/diagnostic-reporter.js +254 -0
  19. package/dist/cli/diagnostic-reporter.js.map +1 -0
  20. package/dist/cli/project-ignore.d.ts +113 -0
  21. package/dist/cli/project-ignore.d.ts.map +1 -0
  22. package/dist/cli/project-ignore.js +199 -0
  23. package/dist/cli/project-ignore.js.map +1 -0
  24. package/dist/cli/safe-text.d.ts +25 -0
  25. package/dist/cli/safe-text.d.ts.map +1 -0
  26. package/dist/cli/safe-text.js +34 -0
  27. package/dist/cli/safe-text.js.map +1 -0
  28. package/dist/cli/supervisor.d.ts +108 -0
  29. package/dist/cli/supervisor.d.ts.map +1 -0
  30. package/dist/cli/supervisor.js +139 -0
  31. package/dist/cli/supervisor.js.map +1 -0
  32. package/dist/cli.d.ts +247 -0
  33. package/dist/cli.d.ts.map +1 -0
  34. package/dist/cli.js +591 -0
  35. package/dist/cli.js.map +1 -0
  36. package/dist/corpus-validator.d.ts +183 -0
  37. package/dist/corpus-validator.d.ts.map +1 -0
  38. package/dist/corpus-validator.js +479 -0
  39. package/dist/corpus-validator.js.map +1 -0
  40. package/dist/document-identity-rule.d.ts +198 -0
  41. package/dist/document-identity-rule.d.ts.map +1 -0
  42. package/dist/document-identity-rule.js +244 -0
  43. package/dist/document-identity-rule.js.map +1 -0
  44. package/dist/ecr.d.ts +103 -0
  45. package/dist/ecr.d.ts.map +1 -0
  46. package/dist/ecr.js +216 -0
  47. package/dist/ecr.js.map +1 -0
  48. package/dist/glob-pattern.d.ts +94 -0
  49. package/dist/glob-pattern.d.ts.map +1 -0
  50. package/dist/glob-pattern.js +188 -0
  51. package/dist/glob-pattern.js.map +1 -0
  52. package/dist/heading-source-form.d.ts +77 -0
  53. package/dist/heading-source-form.d.ts.map +1 -0
  54. package/dist/heading-source-form.js +129 -0
  55. package/dist/heading-source-form.js.map +1 -0
  56. package/dist/identifier-grammar.d.ts +212 -0
  57. package/dist/identifier-grammar.d.ts.map +1 -0
  58. package/dist/identifier-grammar.js +273 -0
  59. package/dist/identifier-grammar.js.map +1 -0
  60. package/dist/index.d.ts +19 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +11 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/inline-reference-rule.d.ts +557 -0
  65. package/dist/inline-reference-rule.d.ts.map +1 -0
  66. package/dist/inline-reference-rule.js +802 -0
  67. package/dist/inline-reference-rule.js.map +1 -0
  68. package/dist/meta-documents.d.ts +91 -0
  69. package/dist/meta-documents.d.ts.map +1 -0
  70. package/dist/meta-documents.js +133 -0
  71. package/dist/meta-documents.js.map +1 -0
  72. package/dist/per-document-visitor.d.ts +510 -0
  73. package/dist/per-document-visitor.d.ts.map +1 -0
  74. package/dist/per-document-visitor.js +1032 -0
  75. package/dist/per-document-visitor.js.map +1 -0
  76. package/dist/references-section-rule.d.ts +482 -0
  77. package/dist/references-section-rule.d.ts.map +1 -0
  78. package/dist/references-section-rule.js +777 -0
  79. package/dist/references-section-rule.js.map +1 -0
  80. package/dist/section-hierarchy-rule.d.ts +299 -0
  81. package/dist/section-hierarchy-rule.d.ts.map +1 -0
  82. package/dist/section-hierarchy-rule.js +482 -0
  83. package/dist/section-hierarchy-rule.js.map +1 -0
  84. package/dist/source-alignment.d.ts +41 -0
  85. package/dist/source-alignment.d.ts.map +1 -0
  86. package/dist/source-alignment.js +166 -0
  87. package/dist/source-alignment.js.map +1 -0
  88. package/dist/source-lines.d.ts +69 -0
  89. package/dist/source-lines.d.ts.map +1 -0
  90. package/dist/source-lines.js +104 -0
  91. package/dist/source-lines.js.map +1 -0
  92. package/dist/spec-version.d.ts +19 -0
  93. package/dist/spec-version.d.ts.map +1 -0
  94. package/dist/spec-version.js +19 -0
  95. package/dist/spec-version.js.map +1 -0
  96. package/dist/types.d.ts +219 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +11 -0
  99. package/dist/types.js.map +1 -0
  100. package/examples/docs/0. Orientation/0.0 - System Overview.md +34 -0
  101. package/examples/docs/0. Orientation/0.0.1 - Glossary.md +21 -0
  102. package/examples/docs/0. Orientation/0.0.2 - Quality Gates.md +22 -0
  103. package/examples/docs/3. Ingestion/3.1 - Ingestion - Validation Rules.md +33 -0
  104. package/examples/docs/4. Payments/4.10 - Settlement.md +22 -0
  105. package/examples/docs/4. Payments/4.2 - Payment Processing Contract.md +33 -0
  106. package/examples/docs/8. Orchestration/8.1 - Workflow Orchestration Contract.md +38 -0
  107. package/examples/docs/8. Orchestration/8.1.3 - Retry Policy.md +17 -0
  108. package/examples/docs/8. Orchestration/8.10 - Dead Letter Queue.md +20 -0
  109. package/examples/docs/README.md +7 -0
  110. package/package.json +65 -7
  111. package/protocol/navigation-protocol.md +223 -0
  112. package/spec/v2/1 - ECR - Structural Specification.md +1467 -0
  113. package/spec/v2/2 - ECR - User Guide.md +845 -0
  114. package/spec/v2/3 - Design Rationale - The Section Separator.md +243 -0
  115. package/src/bin.ts +30 -0
  116. package/src/cli/corpus-loader.ts +482 -0
  117. package/src/cli/corpus-statistics.ts +196 -0
  118. package/src/cli/diagnostic-reporter.ts +344 -0
  119. package/src/cli/project-ignore.ts +238 -0
  120. package/src/cli/safe-text.ts +39 -0
  121. package/src/cli/supervisor.ts +204 -0
  122. package/src/cli.ts +836 -0
  123. package/src/corpus-validator.ts +642 -0
  124. package/src/document-identity-rule.ts +422 -0
  125. package/src/ecr.ts +298 -0
  126. package/src/glob-pattern.ts +242 -0
  127. package/src/heading-source-form.ts +173 -0
  128. package/src/identifier-grammar.ts +380 -0
  129. package/src/index.ts +78 -0
  130. package/src/inline-reference-rule.ts +1235 -0
  131. package/src/meta-documents.ts +159 -0
  132. package/src/per-document-visitor.ts +1528 -0
  133. package/src/references-section-rule.ts +1182 -0
  134. package/src/section-hierarchy-rule.ts +705 -0
  135. package/src/source-alignment.ts +209 -0
  136. package/src/source-lines.ts +123 -0
  137. package/src/spec-version.ts +19 -0
  138. package/src/types.ts +267 -0
package/dist/bin.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ECR Executable Entry Point
4
+ *
5
+ * This file runs the CLI unconditionally and does nothing else. It must not
6
+ * guard itself with a "was I invoked directly?" check that compares
7
+ * `process.argv[1]` with this module's path: package managers install the
8
+ * binary as a symlink (`node_modules/.bin/ecr`), Node resolves the module to
9
+ * its real path, and the two never match, so a guarded entry point silently
10
+ * exits 0 without running. Keeping the commands in `cli.ts` and the side
11
+ * effect here removes the need for any such check.
12
+ *
13
+ * It is also where supervision is asked for, because this file knows the one
14
+ * thing a supervised run needs: the script a child process would have to run,
15
+ * which is this file itself, at its real path rather than through whatever
16
+ * symlink invoked it. A host embedding the library instead of the command
17
+ * gets no supervision, and bounds its own work.
18
+ */
19
+ import { fileURLToPath } from 'node:url';
20
+ import { EcrCommandLine } from './cli.js';
21
+ const result = new EcrCommandLine({
22
+ supervision: { entryPoint: fileURLToPath(import.meta.url) },
23
+ }).run(process.argv.slice(2));
24
+ process[result.stream].write(result.output);
25
+ process.exitCode = result.exitCode;
26
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C,MAAM,MAAM,GAAmB,IAAI,cAAc,CAAC;IAChD,WAAW,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;CAC5D,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC5C,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Corpus Loading
3
+ *
4
+ * File discovery is the host's responsibility, not the linter's. This module
5
+ * is the CLI's implementation of that responsibility: it walks a directory,
6
+ * excludes meta-documents, and produces the inputs the linter consumes.
7
+ */
8
+ import type { CorpusDocumentInput } from '../ecr.js';
9
+ /**
10
+ * A path the walk reached but could not read.
11
+ */
12
+ export interface UnreadablePath {
13
+ /** Corpus-relative path, with forward slashes. */
14
+ readonly path: string;
15
+ /** Why it could not be read: the file system's error code, or its message. */
16
+ readonly reason: string;
17
+ }
18
+ /**
19
+ * The outcome of walking a corpus directory.
20
+ */
21
+ export interface LoadedCorpus {
22
+ /** Documents to validate, in path order. */
23
+ readonly documents: readonly CorpusDocumentInput[];
24
+ /**
25
+ * Corpus-relative paths excluded as meta-documents or by ignore pattern. A
26
+ * directory excluded whole by a pattern is listed once, with a trailing
27
+ * slash, and is not walked.
28
+ */
29
+ readonly excludedPaths: readonly string[];
30
+ /**
31
+ * Links beneath the corpus root that were not followed: every link to a
32
+ * directory, every link named like a document, and every link whose target
33
+ * is missing.
34
+ */
35
+ readonly notFollowedPaths: readonly string[];
36
+ /**
37
+ * Paths that could not be read. A corpus with any of these was not fully
38
+ * read, so its validation result cannot stand for the whole corpus.
39
+ */
40
+ readonly unreadablePaths: readonly UnreadablePath[];
41
+ /**
42
+ * Whether the project's `.ecrignore` excludes the corpus root itself, in
43
+ * which case nothing beneath it was walked.
44
+ */
45
+ readonly rootExcluded: boolean;
46
+ }
47
+ /**
48
+ * A project's own ignore patterns, from its `.ecrignore`, and the directory
49
+ * they are relative to.
50
+ */
51
+ export interface ProjectIgnore {
52
+ /** The project root: the directory the command runs from. */
53
+ readonly workingDirectory: string;
54
+ /** Patterns relative to the working directory. */
55
+ readonly patterns: readonly string[];
56
+ }
57
+ /**
58
+ * Discovers and reads the Markdown documents of a corpus.
59
+ *
60
+ * Paths are reported relative to the corpus root and normalised to forward
61
+ * slashes, so that ignore patterns and diagnostics read the same on every
62
+ * platform.
63
+ *
64
+ * Symbolic links and junctions beneath the root are not followed, because the
65
+ * published recursive searches (`rg`, `grep -r`) do not follow them: a
66
+ * document reached only through a link would be validated but could not be
67
+ * found (1#9.11). A root that is itself a link is followed, as both search
68
+ * engines follow a link named on their command line.
69
+ */
70
+ export declare class CorpusLoader {
71
+ /** Absolute or relative path to the corpus root directory. */
72
+ private readonly corpusRoot;
73
+ /** Decides which discovered documents are excluded from validation. */
74
+ private readonly metaDocumentFilter;
75
+ /**
76
+ * Applies the project's `.ecrignore` patterns, which are relative to the
77
+ * working directory rather than the corpus root; absent when there are none.
78
+ */
79
+ private readonly projectFilter;
80
+ /** The directory the project's patterns are relative to. */
81
+ private readonly workingDirectory;
82
+ /**
83
+ * Creates a loader for one corpus directory.
84
+ *
85
+ * @param corpusRoot - Path to the directory to walk
86
+ * @param ignorePatterns - Glob patterns relative to the corpus root, from `--ignore`
87
+ * @param projectIgnore - The project's `.ecrignore` patterns, relative to the working directory
88
+ */
89
+ constructor(corpusRoot: string, ignorePatterns?: readonly string[], projectIgnore?: ProjectIgnore);
90
+ /**
91
+ * Walks the corpus root and reads every Markdown document that is not
92
+ * excluded.
93
+ *
94
+ * A path that cannot be read is recorded and the walk continues, so that
95
+ * every such path is reported at once.
96
+ *
97
+ * @returns The documents to validate, and the paths excluded, not followed, or not readable
98
+ */
99
+ load(): LoadedCorpus;
100
+ /**
101
+ * Orders two corpus-relative paths by their UTF-16 code units, as
102
+ * `Array.prototype.sort` does by default.
103
+ *
104
+ * @param left - A path
105
+ * @param right - Another path
106
+ * @returns Negative, zero or positive, as for a sort comparator
107
+ */
108
+ private static comparePaths;
109
+ /**
110
+ * Walks one directory, in name order.
111
+ *
112
+ * @param directory - Directory to walk
113
+ * @param walk - What the walk has found so far
114
+ */
115
+ private walkDirectory;
116
+ /**
117
+ * Walks one directory entry: a link is recorded and not followed, a
118
+ * directory is descended into unless an ignore pattern excludes it whole,
119
+ * and a Markdown file is read unless it is excluded.
120
+ *
121
+ * @param entryPath - Path of the entry
122
+ * @param entryName - The entry's own name
123
+ * @param walk - What the walk has found so far
124
+ */
125
+ private walkEntry;
126
+ /**
127
+ * Records a link without following it. An ignore pattern covering it
128
+ * excludes it instead; a link to a file that is not Markdown is of no
129
+ * interest, as it could never be a document.
130
+ *
131
+ * @param entryPath - Path of the link
132
+ * @param entryName - The link's own name
133
+ * @param relativePath - The link's corpus-relative path
134
+ * @param walk - What the walk has found so far
135
+ */
136
+ private recordLink;
137
+ /**
138
+ * Determines whether an ignore pattern of either kind covers a path whose
139
+ * kind cannot or need not be known, such as a link or a vanished entry.
140
+ *
141
+ * @param relativePath - Corpus-relative path
142
+ * @param entryPath - Path of the entry, for the project's patterns
143
+ * @returns `true` when a file pattern or a whole-directory pattern covers it
144
+ */
145
+ private tellExcludedWhateverItIs;
146
+ /**
147
+ * Determines whether a `.ecrignore` pattern excludes the corpus root, either
148
+ * itself or through a directory above it: with `ecr/**` ignored, a root of
149
+ * `ecr/sub` is excluded as surely as `ecr`.
150
+ *
151
+ * @returns `true` when the root, or any directory between it and the project root, is excluded whole
152
+ */
153
+ private tellProjectExcludesRoot;
154
+ /**
155
+ * Determines whether a `.ecrignore` pattern excludes a whole directory.
156
+ *
157
+ * @param directoryPath - Path of the directory
158
+ * @returns `true` when the project's patterns cover everything beneath it
159
+ */
160
+ private tellProjectExcludesDirectory;
161
+ /**
162
+ * Determines whether a `.ecrignore` pattern excludes a file.
163
+ *
164
+ * @param filePath - Path of the file
165
+ * @returns `true` when one of the project's patterns matches it
166
+ */
167
+ private tellProjectExcludesFile;
168
+ /**
169
+ * Converts a path into one relative to the working directory, with forward
170
+ * slashes, which is what `.ecrignore` patterns are matched against.
171
+ *
172
+ * @param path - A path
173
+ * @returns The path relative to the working directory, or `undefined` when there are no project patterns or the path lies outside the project
174
+ */
175
+ private toProjectRelativePath;
176
+ /**
177
+ * Determines whether a name is a Markdown document's.
178
+ *
179
+ * @param entryName - A file or link name
180
+ * @returns `true` for a `.md` name, in any case
181
+ */
182
+ private static tellMarkdownName;
183
+ /**
184
+ * Determines whether a link resolves to something other than a directory.
185
+ *
186
+ * @param linkPath - Path of the link
187
+ * @returns `true` when the target exists and is not a directory
188
+ */
189
+ private static tellLinksToFile;
190
+ /**
191
+ * Converts an absolute path into a corpus-relative path with forward slashes.
192
+ *
193
+ * @param absolutePath - A path beneath the corpus root
194
+ * @returns The path relative to the corpus root
195
+ */
196
+ private toCorpusRelativePath;
197
+ }
198
+ //# sourceMappingURL=corpus-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus-loader.d.ts","sourceRoot":"","sources":["../../src/cli/corpus-loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IACpD;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAuBD;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY;IACvB,8DAA8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IAExD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAE/D,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IAEtD;;;;;;OAMG;gBAED,UAAU,EAAE,MAAM,EAClB,cAAc,GAAE,SAAS,MAAM,EAAO,EACtC,aAAa,CAAC,EAAE,aAAa;IAU/B;;;;;;;;OAQG;IACI,IAAI,IAAI,YAAY;IAgC3B;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAQ3B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAqBrB;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IAyDjB;;;;;;;;;OASG;IACH,OAAO,CAAC,UAAU;IAkBlB;;;;;;;OAOG;IACH,OAAO,CAAC,wBAAwB;IAShC;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAc/B;;;;;OAKG;IACH,OAAO,CAAC,4BAA4B;IAOpC;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;IAM/B;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAI/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAQ9B;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;CAU7B"}
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Corpus Loading
3
+ *
4
+ * File discovery is the host's responsibility, not the linter's. This module
5
+ * is the CLI's implementation of that responsibility: it walks a directory,
6
+ * excludes meta-documents, and produces the inputs the linter consumes.
7
+ */
8
+ import { lstatSync, readFileSync, readdirSync, statSync } from 'node:fs';
9
+ import { isAbsolute, join, relative } from 'node:path';
10
+ import { MetaDocumentFilter } from '../meta-documents.js';
11
+ /**
12
+ * Directory-walking characters that mark a path as hidden on every platform
13
+ * this runs on. Hidden directories are skipped so that `.git`, `.obsidian` and
14
+ * similar never reach the linter.
15
+ */
16
+ const HIDDEN_PREFIX = '.';
17
+ /**
18
+ * The directory package managers install dependencies into. It is skipped
19
+ * wherever the walk meets it: an installed copy of this package carries the
20
+ * specification, whose DocIDs would collide with a project's own, and any
21
+ * other package's Markdown is not the project's documentation. A corpus root
22
+ * named explicitly is still walked, even inside one.
23
+ */
24
+ const DEPENDENCY_DIRECTORY = 'node_modules';
25
+ /**
26
+ * Extension identifying a Markdown document.
27
+ */
28
+ const MARKDOWN_EXTENSION = '.md';
29
+ /**
30
+ * Discovers and reads the Markdown documents of a corpus.
31
+ *
32
+ * Paths are reported relative to the corpus root and normalised to forward
33
+ * slashes, so that ignore patterns and diagnostics read the same on every
34
+ * platform.
35
+ *
36
+ * Symbolic links and junctions beneath the root are not followed, because the
37
+ * published recursive searches (`rg`, `grep -r`) do not follow them: a
38
+ * document reached only through a link would be validated but could not be
39
+ * found (1#9.11). A root that is itself a link is followed, as both search
40
+ * engines follow a link named on their command line.
41
+ */
42
+ export class CorpusLoader {
43
+ /** Absolute or relative path to the corpus root directory. */
44
+ corpusRoot;
45
+ /** Decides which discovered documents are excluded from validation. */
46
+ metaDocumentFilter;
47
+ /**
48
+ * Applies the project's `.ecrignore` patterns, which are relative to the
49
+ * working directory rather than the corpus root; absent when there are none.
50
+ */
51
+ projectFilter;
52
+ /** The directory the project's patterns are relative to. */
53
+ workingDirectory;
54
+ /**
55
+ * Creates a loader for one corpus directory.
56
+ *
57
+ * @param corpusRoot - Path to the directory to walk
58
+ * @param ignorePatterns - Glob patterns relative to the corpus root, from `--ignore`
59
+ * @param projectIgnore - The project's `.ecrignore` patterns, relative to the working directory
60
+ */
61
+ constructor(corpusRoot, ignorePatterns = [], projectIgnore) {
62
+ this.corpusRoot = corpusRoot;
63
+ this.metaDocumentFilter = new MetaDocumentFilter(ignorePatterns);
64
+ // No meta-document names: those are already applied by the corpus filter.
65
+ this.projectFilter =
66
+ projectIgnore === undefined ? undefined : new MetaDocumentFilter(projectIgnore.patterns, []);
67
+ this.workingDirectory = projectIgnore?.workingDirectory;
68
+ }
69
+ /**
70
+ * Walks the corpus root and reads every Markdown document that is not
71
+ * excluded.
72
+ *
73
+ * A path that cannot be read is recorded and the walk continues, so that
74
+ * every such path is reported at once.
75
+ *
76
+ * @returns The documents to validate, and the paths excluded, not followed, or not readable
77
+ */
78
+ load() {
79
+ const walk = new CorpusWalk();
80
+ const rootExcluded = this.tellProjectExcludesRoot();
81
+ if (!rootExcluded) {
82
+ this.walkDirectory(this.corpusRoot, walk);
83
+ }
84
+ // Sorted by corpus-relative path, so the order is the same on every
85
+ // platform whatever the path separator.
86
+ walk.documents.sort((left, right) => CorpusLoader.comparePaths(left.uri, right.uri));
87
+ walk.excludedPaths.sort((left, right) => CorpusLoader.comparePaths(left, right));
88
+ walk.notFollowedPaths.sort((left, right) => CorpusLoader.comparePaths(left, right));
89
+ walk.unreadablePaths.sort((left, right) => CorpusLoader.comparePaths(left.path, right.path));
90
+ return {
91
+ documents: walk.documents,
92
+ excludedPaths: walk.excludedPaths,
93
+ notFollowedPaths: walk.notFollowedPaths,
94
+ unreadablePaths: walk.unreadablePaths,
95
+ rootExcluded,
96
+ };
97
+ }
98
+ /**
99
+ * Orders two corpus-relative paths by their UTF-16 code units, as
100
+ * `Array.prototype.sort` does by default.
101
+ *
102
+ * @param left - A path
103
+ * @param right - Another path
104
+ * @returns Negative, zero or positive, as for a sort comparator
105
+ */
106
+ static comparePaths(left, right) {
107
+ if (left === right) {
108
+ return 0;
109
+ }
110
+ return left < right ? -1 : 1;
111
+ }
112
+ /**
113
+ * Walks one directory, in name order.
114
+ *
115
+ * @param directory - Directory to walk
116
+ * @param walk - What the walk has found so far
117
+ */
118
+ walkDirectory(directory, walk) {
119
+ let entryNames;
120
+ try {
121
+ entryNames = readdirSync(directory).sort((left, right) => CorpusLoader.comparePaths(left, right));
122
+ }
123
+ catch (error) {
124
+ walk.recordUnreadable(this.toCorpusRelativePath(directory), error);
125
+ return;
126
+ }
127
+ for (const entryName of entryNames) {
128
+ if (entryName.startsWith(HIDDEN_PREFIX)) {
129
+ continue;
130
+ }
131
+ this.walkEntry(join(directory, entryName), entryName, walk);
132
+ }
133
+ }
134
+ /**
135
+ * Walks one directory entry: a link is recorded and not followed, a
136
+ * directory is descended into unless an ignore pattern excludes it whole,
137
+ * and a Markdown file is read unless it is excluded.
138
+ *
139
+ * @param entryPath - Path of the entry
140
+ * @param entryName - The entry's own name
141
+ * @param walk - What the walk has found so far
142
+ */
143
+ walkEntry(entryPath, entryName, walk) {
144
+ const relativePath = this.toCorpusRelativePath(entryPath);
145
+ let entryStats;
146
+ try {
147
+ entryStats = lstatSync(entryPath);
148
+ }
149
+ catch (error) {
150
+ if (this.tellExcludedWhateverItIs(relativePath, entryPath)) {
151
+ walk.excludedPaths.push(relativePath);
152
+ return;
153
+ }
154
+ walk.recordUnreadable(relativePath, error);
155
+ return;
156
+ }
157
+ if (entryStats.isSymbolicLink()) {
158
+ this.recordLink(entryPath, entryName, relativePath, walk);
159
+ return;
160
+ }
161
+ if (entryStats.isDirectory()) {
162
+ if (entryName === DEPENDENCY_DIRECTORY ||
163
+ this.metaDocumentFilter.excludesDirectory(relativePath) ||
164
+ this.tellProjectExcludesDirectory(entryPath)) {
165
+ walk.excludedPaths.push(`${relativePath}/`);
166
+ return;
167
+ }
168
+ this.walkDirectory(entryPath, walk);
169
+ return;
170
+ }
171
+ if (!CorpusLoader.tellMarkdownName(entryName)) {
172
+ return;
173
+ }
174
+ if (this.metaDocumentFilter.shouldExclude(relativePath) ||
175
+ this.tellProjectExcludesFile(entryPath)) {
176
+ walk.excludedPaths.push(relativePath);
177
+ return;
178
+ }
179
+ try {
180
+ walk.documents.push({
181
+ uri: relativePath,
182
+ markdownText: readFileSync(entryPath, 'utf8'),
183
+ });
184
+ }
185
+ catch (error) {
186
+ walk.recordUnreadable(relativePath, error);
187
+ }
188
+ }
189
+ /**
190
+ * Records a link without following it. An ignore pattern covering it
191
+ * excludes it instead; a link to a file that is not Markdown is of no
192
+ * interest, as it could never be a document.
193
+ *
194
+ * @param entryPath - Path of the link
195
+ * @param entryName - The link's own name
196
+ * @param relativePath - The link's corpus-relative path
197
+ * @param walk - What the walk has found so far
198
+ */
199
+ recordLink(entryPath, entryName, relativePath, walk) {
200
+ if (this.tellExcludedWhateverItIs(relativePath, entryPath)) {
201
+ walk.excludedPaths.push(relativePath);
202
+ return;
203
+ }
204
+ if (!CorpusLoader.tellMarkdownName(entryName) && CorpusLoader.tellLinksToFile(entryPath)) {
205
+ return;
206
+ }
207
+ walk.notFollowedPaths.push(relativePath);
208
+ }
209
+ /**
210
+ * Determines whether an ignore pattern of either kind covers a path whose
211
+ * kind cannot or need not be known, such as a link or a vanished entry.
212
+ *
213
+ * @param relativePath - Corpus-relative path
214
+ * @param entryPath - Path of the entry, for the project's patterns
215
+ * @returns `true` when a file pattern or a whole-directory pattern covers it
216
+ */
217
+ tellExcludedWhateverItIs(relativePath, entryPath) {
218
+ return (this.metaDocumentFilter.excludesDirectory(relativePath) ||
219
+ this.metaDocumentFilter.matchesIgnorePattern(relativePath) ||
220
+ this.tellProjectExcludesDirectory(entryPath) ||
221
+ this.tellProjectExcludesFile(entryPath));
222
+ }
223
+ /**
224
+ * Determines whether a `.ecrignore` pattern excludes the corpus root, either
225
+ * itself or through a directory above it: with `ecr/**` ignored, a root of
226
+ * `ecr/sub` is excluded as surely as `ecr`.
227
+ *
228
+ * @returns `true` when the root, or any directory between it and the project root, is excluded whole
229
+ */
230
+ tellProjectExcludesRoot() {
231
+ const projectPath = this.toProjectRelativePath(this.corpusRoot);
232
+ if (projectPath === undefined || projectPath === '') {
233
+ return false;
234
+ }
235
+ const segments = projectPath.split('/');
236
+ return segments.some((_segment, index) => this.projectFilter?.excludesDirectory(segments.slice(0, index + 1).join('/')) === true);
237
+ }
238
+ /**
239
+ * Determines whether a `.ecrignore` pattern excludes a whole directory.
240
+ *
241
+ * @param directoryPath - Path of the directory
242
+ * @returns `true` when the project's patterns cover everything beneath it
243
+ */
244
+ tellProjectExcludesDirectory(directoryPath) {
245
+ const projectPath = this.toProjectRelativePath(directoryPath);
246
+ return projectPath !== undefined && projectPath !== '' &&
247
+ this.projectFilter?.excludesDirectory(projectPath) === true;
248
+ }
249
+ /**
250
+ * Determines whether a `.ecrignore` pattern excludes a file.
251
+ *
252
+ * @param filePath - Path of the file
253
+ * @returns `true` when one of the project's patterns matches it
254
+ */
255
+ tellProjectExcludesFile(filePath) {
256
+ const projectPath = this.toProjectRelativePath(filePath);
257
+ return projectPath !== undefined && this.projectFilter?.matchesIgnorePattern(projectPath) === true;
258
+ }
259
+ /**
260
+ * Converts a path into one relative to the working directory, with forward
261
+ * slashes, which is what `.ecrignore` patterns are matched against.
262
+ *
263
+ * @param path - A path
264
+ * @returns The path relative to the working directory, or `undefined` when there are no project patterns or the path lies outside the project
265
+ */
266
+ toProjectRelativePath(path) {
267
+ if (this.workingDirectory === undefined) {
268
+ return undefined;
269
+ }
270
+ const projectPath = relative(this.workingDirectory, path);
271
+ // The project's patterns say nothing about paths outside the project.
272
+ if (isAbsolute(projectPath) || /^\.\.(?:[\\/]|$)/.test(projectPath)) {
273
+ return undefined;
274
+ }
275
+ return projectPath.replaceAll('\\', '/');
276
+ }
277
+ /**
278
+ * Determines whether a name is a Markdown document's.
279
+ *
280
+ * @param entryName - A file or link name
281
+ * @returns `true` for a `.md` name, in any case
282
+ */
283
+ static tellMarkdownName(entryName) {
284
+ return entryName.toLowerCase().endsWith(MARKDOWN_EXTENSION);
285
+ }
286
+ /**
287
+ * Determines whether a link resolves to something other than a directory.
288
+ *
289
+ * @param linkPath - Path of the link
290
+ * @returns `true` when the target exists and is not a directory
291
+ */
292
+ static tellLinksToFile(linkPath) {
293
+ try {
294
+ return !statSync(linkPath).isDirectory();
295
+ }
296
+ catch {
297
+ return false;
298
+ }
299
+ }
300
+ /**
301
+ * Converts an absolute path into a corpus-relative path with forward slashes.
302
+ *
303
+ * @param absolutePath - A path beneath the corpus root
304
+ * @returns The path relative to the corpus root
305
+ */
306
+ toCorpusRelativePath(absolutePath) {
307
+ const withoutRoot = absolutePath.startsWith(this.corpusRoot)
308
+ ? absolutePath.slice(this.corpusRoot.length)
309
+ : absolutePath;
310
+ return withoutRoot
311
+ .split('\\')
312
+ .join('/')
313
+ .replace(/^\/+/, '');
314
+ }
315
+ }
316
+ /**
317
+ * What one walk of a corpus has found so far.
318
+ */
319
+ class CorpusWalk {
320
+ /** Documents read, in the order reached. */
321
+ documents = [];
322
+ /** Paths excluded as meta-documents or by ignore pattern. */
323
+ excludedPaths = [];
324
+ /** Links recorded and not followed. */
325
+ notFollowedPaths = [];
326
+ /** Paths reached but not readable. */
327
+ unreadablePaths = [];
328
+ /**
329
+ * Records a path that could not be read.
330
+ *
331
+ * @param path - The corpus-relative path; empty for the corpus root itself
332
+ * @param error - What reading it threw
333
+ */
334
+ recordUnreadable(path, error) {
335
+ this.unreadablePaths.push({ path: path === '' ? '.' : path, reason: CorpusWalk.showReason(error) });
336
+ }
337
+ /**
338
+ * Describes a file-system error briefly.
339
+ *
340
+ * @param error - What was thrown
341
+ * @returns The error's code, such as `ENOENT`, or its message
342
+ */
343
+ static showReason(error) {
344
+ if (error instanceof Error) {
345
+ const code = error.code;
346
+ return typeof code === 'string' ? code : error.message;
347
+ }
348
+ return String(error);
349
+ }
350
+ }
351
+ //# sourceMappingURL=corpus-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus-loader.js","sourceRoot":"","sources":["../../src/cli/corpus-loader.ts"],"names":[],"mappings":"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"}