@cplieger/deadset-ts 8.0.0-dev.2 → 8.0.0-dev.21
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 +3 -1
- package/THIRD_PARTY_NOTICES.md +27 -0
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/alias-chain.js +35 -9
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +274 -496
- package/dist/src/augmentations.js +64 -3
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/bindings.js +382 -0
- package/dist/src/commands/held.js +499 -0
- package/dist/src/commands/read.js +161 -0
- package/dist/src/commands/shell.js +169 -0
- package/dist/src/commands/tools.js +1847 -0
- package/dist/src/component-files.js +3 -2
- package/dist/src/component-layer.js +4 -2
- package/dist/src/config.js +6 -82
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/consumers.js +10 -38
- package/dist/src/contextual-uses.js +57 -26
- package/dist/src/contract/config.generated.js +561 -0
- package/dist/src/contract/contract.generated.js +1370 -0
- package/dist/src/contract/report.generated.js +866 -0
- package/dist/src/dependencies.js +165 -122
- package/dist/src/destructuring.js +1 -19
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +181 -168
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +52 -256
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +170 -30
- package/dist/src/entries/conventions.js +740 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +12 -42
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +5 -28
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +670 -0
- package/dist/src/entries/test-runners.js +650 -0
- package/dist/src/entries/workflow-steps.js +345 -0
- package/dist/src/exemptions/argument-places.js +131 -0
- package/dist/src/exemptions/calls.js +338 -0
- package/dist/src/exemptions/classes.js +13 -0
- package/dist/src/{decorator.js → exemptions/decorator.js} +19 -30
- package/dist/src/exemptions/detectors.js +48 -0
- package/dist/src/{enum-group.js → exemptions/enum-group.js} +9 -16
- package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +7 -11
- package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +76 -54
- package/dist/src/exemptions/generated-file.js +241 -0
- package/dist/src/{injection-container.js → exemptions/injection-container.js} +20 -26
- package/dist/src/exemptions/installed-globals.js +173 -0
- package/dist/src/{interface-satisfaction.js → exemptions/interface-satisfaction.js} +52 -121
- package/dist/src/exemptions/lifecycle.js +150 -0
- package/dist/src/{reflective-lookup.js → exemptions/reflective-lookup.js} +3 -3
- package/dist/src/exemptions/serialization-contract.js +1276 -0
- package/dist/src/exemptions/sites.js +182 -0
- package/dist/src/{template-field.js → exemptions/template-field.js} +13 -23
- package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
- package/dist/src/exposure.js +400 -0
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +3 -26
- package/dist/src/findings/completion.js +109 -56
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +27 -41
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +3 -2
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +16 -13
- package/dist/src/findings/non-code-artifacts.js +17 -12
- package/dist/src/findings/reads-and-writes.js +86 -27
- package/dist/src/findings/self-check.js +44 -30
- package/dist/src/findings/unused-declarations.js +29 -45
- package/dist/src/findings/visibility-narrowing.js +23 -31
- package/dist/src/findings-pass.js +13 -21
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +67 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/index/values.js +124 -0
- package/dist/src/index.js +2 -2
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory/commonjs.js +507 -0
- package/dist/src/inventory/enumerate.js +167 -0
- package/dist/src/inventory/syntax.js +236 -0
- package/dist/src/inventory.js +323 -724
- package/dist/src/javascript-file.js +48 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +126 -107
- package/dist/src/kinds.js +3 -366
- package/dist/src/{components.js → liveness/components.js} +2 -3
- package/dist/src/{graph.js → liveness/graph.js} +52 -28
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/load/classify.js +289 -0
- package/dist/src/load/diagnostics.js +87 -0
- package/dist/src/load/discover.js +285 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/package-scope.js +56 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +113 -0
- package/dist/src/load/synthesize.js +152 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +24 -66
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +82 -16
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +134 -0
- package/dist/src/position.js +43 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +95 -0
- package/dist/src/ref.js +12 -20
- package/dist/src/references/read-rule.js +1 -0
- package/dist/src/references/read-rules.js +48 -0
- package/dist/src/references/sink.js +237 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +117 -1074
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +246 -730
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +22 -24
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +6 -15
- package/dist/src/roots/manifest.js +216 -0
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +487 -0
- package/dist/src/roots/unseen-loads.js +236 -0
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/session.js +37 -29
- package/dist/src/source-files.js +6 -10
- package/dist/src/stores.js +4 -2
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +36 -83
- package/dist/src/suppress.js +50 -71
- package/dist/src/symbol-keys.js +92 -88
- package/dist/src/tests/file-class.js +76 -0
- package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
- package/dist/src/{test-support.js → tests/support.js} +5 -12
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +298 -0
- package/dist/src/text/yaml.js +684 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/vendor/unbash/ansi-c.js +132 -0
- package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
- package/dist/src/vendor/unbash/arithmetic.js +591 -0
- package/dist/src/vendor/unbash/assignment.js +131 -0
- package/dist/src/vendor/unbash/chars.js +48 -0
- package/dist/src/vendor/unbash/command.js +41 -0
- package/dist/src/vendor/unbash/expansion.js +80 -0
- package/dist/src/vendor/unbash/glob.js +268 -0
- package/dist/src/vendor/unbash/heredoc.js +27 -0
- package/dist/src/vendor/unbash/internal-types.js +1 -0
- package/dist/src/vendor/unbash/lexer.js +4274 -0
- package/dist/src/vendor/unbash/limits.js +3 -0
- package/dist/src/vendor/unbash/parser.js +1366 -0
- package/dist/src/vendor/unbash/parts.js +57 -0
- package/dist/src/vendor/unbash/substitution-context.js +8 -0
- package/dist/src/vendor/unbash/traversal.js +212 -0
- package/dist/src/vendor/unbash/types.js +1 -0
- package/dist/src/vendor/unbash/word-value.js +61 -0
- package/dist/src/vendor/unbash/word.js +40 -0
- package/dist/src/verbs/analyze.js +194 -127
- package/dist/src/verbs/describe.js +13 -18
- package/dist/src/verbs/explain.js +85 -139
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +30 -6
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/jsr.json +2 -1
- package/package.json +14 -8
- package/src/alias-chain.ts +40 -11
- package/src/amd.ts +130 -0
- package/src/analysis.ts +426 -733
- package/src/augmentations.ts +93 -4
- package/src/automatic-types.ts +12 -30
- package/src/bindings.ts +460 -0
- package/src/commands/held.ts +632 -0
- package/src/commands/read.ts +242 -0
- package/src/commands/shell.ts +228 -0
- package/src/commands/tools.ts +2382 -0
- package/src/component-files.ts +4 -2
- package/src/component-layer.ts +4 -2
- package/src/config.ts +31 -302
- package/src/conformance-record.ts +2 -2
- package/src/consumers.ts +16 -66
- package/src/contextual-uses.ts +66 -34
- package/src/contract/config.generated.ts +821 -0
- package/src/contract/contract.generated.ts +1453 -0
- package/src/contract/report.generated.ts +1241 -0
- package/src/dependencies.ts +223 -128
- package/src/destructuring.ts +1 -31
- package/src/edges.ts +41 -140
- package/src/element-access.ts +190 -187
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +68 -278
- package/src/{convention-rows.ts → entries/convention-rows.ts} +227 -31
- package/src/{conventions.ts → entries/conventions.ts} +367 -583
- package/src/{html-entries.ts → entries/html-entries.ts} +22 -48
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +7 -30
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +858 -0
- package/src/entries/test-runners.ts +835 -0
- package/src/entries/workflow-steps.ts +432 -0
- package/src/exemptions/argument-places.ts +198 -0
- package/src/exemptions/calls.ts +483 -0
- package/src/exemptions/classes.ts +49 -0
- package/src/exemptions/decorator.ts +56 -0
- package/src/exemptions/detectors.ts +75 -0
- package/src/{enum-group.ts → exemptions/enum-group.ts} +10 -19
- package/src/{erased-stores.ts → exemptions/erased-stores.ts} +7 -14
- package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +85 -66
- package/src/exemptions/generated-file.ts +318 -0
- package/src/{injection-container.ts → exemptions/injection-container.ts} +29 -34
- package/src/exemptions/installed-globals.ts +255 -0
- package/src/{interface-satisfaction.ts → exemptions/interface-satisfaction.ts} +69 -160
- package/src/exemptions/lifecycle.ts +286 -0
- package/src/{reflective-lookup.ts → exemptions/reflective-lookup.ts} +5 -5
- package/src/exemptions/serialization-contract.ts +1729 -0
- package/src/exemptions/sites.ts +293 -0
- package/src/{template-field.ts → exemptions/template-field.ts} +22 -32
- package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
- package/src/exposure.ts +492 -0
- package/src/extensions.ts +45 -0
- package/src/facts.ts +46 -0
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +15 -25
- package/src/finding.ts +10 -10
- package/src/findings/boundary.ts +11 -0
- package/src/findings/completion.ts +133 -64
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +9 -10
- package/src/findings/emitters.ts +38 -52
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +4 -3
- package/src/findings/interfaces.ts +7 -9
- package/src/findings/intra-function.ts +16 -14
- package/src/findings/non-code-artifacts.ts +24 -15
- package/src/findings/reads-and-writes.ts +118 -41
- package/src/findings/self-check.ts +51 -38
- package/src/findings/unused-declarations.ts +31 -49
- package/src/findings/visibility-narrowing.ts +36 -40
- package/src/findings-pass.ts +15 -31
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +96 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +50 -26
- package/src/host.ts +12 -5
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/index/values.ts +170 -0
- package/src/index.ts +2 -2
- package/src/indexed-access.ts +570 -527
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory/commonjs.ts +696 -0
- package/src/inventory/enumerate.ts +203 -0
- package/src/inventory/syntax.ts +311 -0
- package/src/inventory.ts +442 -895
- package/src/javascript-file.ts +54 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +133 -122
- package/src/kinds.ts +5 -413
- package/src/{components.ts → liveness/components.ts} +4 -6
- package/src/{graph.ts → liveness/graph.ts} +100 -35
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/load/classify.ts +433 -0
- package/src/load/diagnostics.ts +154 -0
- package/src/load/discover.ts +471 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +194 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/package-scope.ts +74 -0
- package/src/load/plan.ts +513 -0
- package/src/load/specifiers.ts +171 -0
- package/src/load/synthesize.ts +230 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +41 -77
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/partial.ts +3 -2
- package/src/paths.ts +92 -16
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +189 -0
- package/src/position.ts +44 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +201 -10
- package/src/ref.ts +12 -20
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +314 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +187 -1450
- package/src/report-schema.ts +68 -0
- package/src/report.ts +103 -226
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +284 -1115
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +26 -29
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +7 -16
- package/src/roots/manifest.ts +303 -0
- package/src/roots/published-reach.ts +486 -0
- package/src/{roots.ts → roots/root-set.ts} +286 -469
- package/src/roots/unseen-loads.ts +345 -0
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/session.ts +87 -55
- package/src/source-files.ts +6 -11
- package/src/stores.ts +15 -3
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +55 -91
- package/src/suppress.ts +78 -79
- package/src/symbol-keys.ts +104 -107
- package/src/tests/file-class.ts +135 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +15 -21
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.ts +388 -0
- package/src/text/yaml.ts +747 -0
- package/src/type-errors.ts +18 -288
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/vendor/unbash/LICENSE +15 -0
- package/src/vendor/unbash/VENDORED.md +16 -0
- package/src/vendor/unbash/ansi-c.ts +132 -0
- package/src/vendor/unbash/arithmetic-parts.ts +53 -0
- package/src/vendor/unbash/arithmetic.ts +641 -0
- package/src/vendor/unbash/assignment.ts +163 -0
- package/src/vendor/unbash/chars.ts +48 -0
- package/src/vendor/unbash/command.ts +47 -0
- package/src/vendor/unbash/expansion.ts +144 -0
- package/src/vendor/unbash/glob.ts +284 -0
- package/src/vendor/unbash/heredoc.ts +34 -0
- package/src/vendor/unbash/internal-types.ts +94 -0
- package/src/vendor/unbash/lexer.ts +4352 -0
- package/src/vendor/unbash/limits.ts +3 -0
- package/src/vendor/unbash/parser.ts +1612 -0
- package/src/vendor/unbash/parts.ts +96 -0
- package/src/vendor/unbash/substitution-context.ts +20 -0
- package/src/vendor/unbash/traversal.ts +182 -0
- package/src/vendor/unbash/types.ts +660 -0
- package/src/vendor/unbash/word-value.ts +64 -0
- package/src/vendor/unbash/word.ts +50 -0
- package/src/verbs/analyze.ts +255 -164
- package/src/verbs/describe.ts +19 -17
- package/src/verbs/explain.ts +93 -169
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +36 -6
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/dist/src/base-url.js +0 -18
- package/dist/src/calls.js +0 -155
- package/dist/src/conventions.js +0 -937
- package/dist/src/discover.js +0 -468
- package/dist/src/entry-points.js +0 -529
- package/dist/src/exempt-classes.js +0 -75
- package/dist/src/exempt.js +0 -209
- package/dist/src/generated-file.js +0 -95
- package/dist/src/glob.js +0 -65
- package/dist/src/manifest.js +0 -165
- package/dist/src/matrix.js +0 -246
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/roots.js +0 -672
- package/dist/src/serialization-contract.js +0 -611
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/sweep.js +0 -224
- package/dist/src/test-joins.js +0 -101
- package/dist/src/value-flow.js +0 -74
- package/dist/src/version.js +0 -7
- package/dist/src/workflow-steps.js +0 -185
- package/src/base-url.ts +0 -24
- package/src/calls.ts +0 -225
- package/src/decorator.ts +0 -78
- package/src/discover.ts +0 -653
- package/src/entry-points.ts +0 -687
- package/src/exempt-classes.ts +0 -123
- package/src/exempt.ts +0 -323
- package/src/generated-file.ts +0 -111
- package/src/glob.ts +0 -67
- package/src/manifest.ts +0 -234
- package/src/matrix.ts +0 -423
- package/src/pnpm-workspace.ts +0 -135
- package/src/serialization-contract.ts +0 -849
- package/src/setup-failure.ts +0 -39
- package/src/sweep.ts +0 -347
- package/src/test-joins.ts +0 -120
- package/src/value-flow.ts +0 -104
- package/src/version.ts +0 -7
- package/src/workflow-steps.ts +0 -216
|
@@ -6,46 +6,8 @@
|
|
|
6
6
|
* No configuration file is run.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
isAsExpression,
|
|
12
|
-
isBinaryExpression,
|
|
13
|
-
isCallExpression,
|
|
14
|
-
isClassDeclaration,
|
|
15
|
-
isComputedPropertyName,
|
|
16
|
-
isExportAssignment,
|
|
17
|
-
isExportDeclaration,
|
|
18
|
-
isExpressionStatement,
|
|
19
|
-
isFunctionDeclaration,
|
|
20
|
-
isIdentifier,
|
|
21
|
-
isImportDeclaration,
|
|
22
|
-
isNamedExports,
|
|
23
|
-
isNamespaceImport,
|
|
24
|
-
isNoSubstitutionTemplateLiteral,
|
|
25
|
-
isNonNullExpression,
|
|
26
|
-
isNumericLiteral,
|
|
27
|
-
isObjectBindingPattern,
|
|
28
|
-
isObjectLiteralExpression,
|
|
29
|
-
isParenthesizedExpression,
|
|
30
|
-
isPropertyAccessExpression,
|
|
31
|
-
isPropertyAssignment,
|
|
32
|
-
isSatisfiesExpression,
|
|
33
|
-
isShorthandPropertyAssignment,
|
|
34
|
-
isSpreadAssignment,
|
|
35
|
-
isStringLiteral,
|
|
36
|
-
isVariableStatement,
|
|
37
|
-
SyntaxKind,
|
|
38
|
-
type AsExpression,
|
|
39
|
-
type Identifier,
|
|
40
|
-
type Node,
|
|
41
|
-
type ObjectBindingPattern,
|
|
42
|
-
type NonNullExpression,
|
|
43
|
-
type ObjectLiteralExpression,
|
|
44
|
-
type ParenthesizedExpression,
|
|
45
|
-
type SatisfiesExpression,
|
|
46
|
-
type SourceFile,
|
|
47
|
-
} from "@typescript/native/ast";
|
|
48
|
-
import { lineOf } from "./component-files.ts";
|
|
9
|
+
import type { SourceFile } from "@typescript/native/ast";
|
|
10
|
+
import { lineOf } from "../component-files.ts";
|
|
49
11
|
import {
|
|
50
12
|
CONVENTION_ROWS,
|
|
51
13
|
type ConventionRow,
|
|
@@ -57,15 +19,41 @@ import {
|
|
|
57
19
|
type PackageKey,
|
|
58
20
|
type PrefixedNameReading,
|
|
59
21
|
type ShortNameReading,
|
|
22
|
+
type TestDirectoryReading,
|
|
60
23
|
type TestFileReading,
|
|
61
24
|
} from "./convention-rows.ts";
|
|
62
25
|
import { documentStrings } from "./configuration-files.ts";
|
|
63
|
-
import {
|
|
64
|
-
import type
|
|
65
|
-
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
26
|
+
import { readCommands, type CommandReading } from "../commands/read.ts";
|
|
27
|
+
import { nodeTestGlobs, type RunnerInput, type TestGlob } from "./test-runners.ts";
|
|
28
|
+
import {
|
|
29
|
+
documentModule,
|
|
30
|
+
membersNamed,
|
|
31
|
+
staticModule,
|
|
32
|
+
textsOf,
|
|
33
|
+
valuesAt,
|
|
34
|
+
type StaticModule,
|
|
35
|
+
type Value,
|
|
36
|
+
} from "./static-module.ts";
|
|
37
|
+
import type { Host } from "../host.ts";
|
|
38
|
+
import {
|
|
39
|
+
ancestors,
|
|
40
|
+
cannotStartPackageName,
|
|
41
|
+
dirnamePath,
|
|
42
|
+
joinPath,
|
|
43
|
+
normalizePath,
|
|
44
|
+
relativePath,
|
|
45
|
+
resolvePath,
|
|
46
|
+
} from "../paths.ts";
|
|
47
|
+
import type { Failure } from "../failure.ts";
|
|
48
|
+
import type { SourceFiles } from "../source-files.ts";
|
|
49
|
+
import { satisfies } from "../version-range.ts";
|
|
50
|
+
import { bytewise } from "../text/compare.ts";
|
|
51
|
+
import { isRecord } from "../json-document.ts";
|
|
52
|
+
import { filesIn } from "../fs/tree.ts";
|
|
53
|
+
import { DEPENDENCY_SECTIONS, INSTALLED_PACKAGES, MANIFEST, readJSON } from "../fs/manifest.ts";
|
|
54
|
+
import { globExpression } from "../text/glob.ts";
|
|
55
|
+
import { yamlDocument } from "../text/yaml.ts";
|
|
56
|
+
import { APPENDED_EXTENSIONS, braced } from "../extensions.ts";
|
|
69
57
|
|
|
70
58
|
/** One manifest the analysis reads. */
|
|
71
59
|
export interface ConventionManifest {
|
|
@@ -110,7 +98,7 @@ interface GeneratedDirectory {
|
|
|
110
98
|
/** A setup failure a row met, at the directory of the manifest that declared it. */
|
|
111
99
|
interface ConventionFailure {
|
|
112
100
|
readonly dir: string;
|
|
113
|
-
readonly failure:
|
|
101
|
+
readonly failure: Failure;
|
|
114
102
|
}
|
|
115
103
|
|
|
116
104
|
/** What the rows decided for one run. */
|
|
@@ -127,8 +115,10 @@ export interface Conventions {
|
|
|
127
115
|
readonly uses: readonly string[];
|
|
128
116
|
/** Per configuration file, absolute, the strings its applied rows' selection keys hold. */
|
|
129
117
|
readonly selected: ReadonlyMap<string, Selected>;
|
|
130
|
-
/** The files, absolute, an applied row's test-file properties name. */
|
|
131
|
-
readonly testFiles:
|
|
118
|
+
/** The files, absolute, an applied row's test-file properties name, each with the string naming it. */
|
|
119
|
+
readonly testFiles: ReadonlyMap<string, string>;
|
|
120
|
+
/** The test-file patterns of the applied rows' test directory readings. */
|
|
121
|
+
readonly testGlobs: readonly TestGlob[];
|
|
132
122
|
}
|
|
133
123
|
|
|
134
124
|
/** The strings one configuration file holds at selection keys, which root no file. */
|
|
@@ -149,32 +139,15 @@ interface ConventionEntry {
|
|
|
149
139
|
/** Parses one file's text with no program. */
|
|
150
140
|
type ParseSource = (fileName: string, text: string) => SourceFile;
|
|
151
141
|
|
|
152
|
-
|
|
142
|
+
/** A configuration file its tool reads as a YAML document. */
|
|
143
|
+
const YAML_NAME = /\.ya?ml$/u;
|
|
153
144
|
|
|
154
|
-
/** A configuration file
|
|
155
|
-
const
|
|
145
|
+
/** A configuration file its tool reads as a JSON document whose name does not end with `.json`. */
|
|
146
|
+
const JSON_NAME = /^\.[\w-]+rc$|\.jsonc$/u;
|
|
156
147
|
|
|
157
148
|
/** The sections an installed package is required by, so its absence fails the run. */
|
|
158
149
|
const REQUIRED_SECTIONS = ["dependencies", "devDependencies"] as const;
|
|
159
150
|
|
|
160
|
-
const DEPENDENCY_SECTIONS = [
|
|
161
|
-
...REQUIRED_SECTIONS,
|
|
162
|
-
"peerDependencies",
|
|
163
|
-
"optionalDependencies",
|
|
164
|
-
] as const;
|
|
165
|
-
|
|
166
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
167
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function readJSON(host: Host, path: string): unknown {
|
|
171
|
-
try {
|
|
172
|
-
return JSON.parse(host.readFile(path));
|
|
173
|
-
} catch {
|
|
174
|
-
return undefined;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
151
|
function declares(
|
|
179
152
|
manifest: unknown,
|
|
180
153
|
name: string,
|
|
@@ -195,353 +168,59 @@ function declares(
|
|
|
195
168
|
* none, or `undefined` where no `node_modules` holds it.
|
|
196
169
|
*/
|
|
197
170
|
function installedVersion(host: Host, dir: string, name: string): string | undefined {
|
|
198
|
-
for (
|
|
199
|
-
const path = joinPath(at,
|
|
171
|
+
for (const at of ancestors(dir)) {
|
|
172
|
+
const path = joinPath(at, INSTALLED_PACKAGES, name, MANIFEST);
|
|
200
173
|
if (host.kindOf(path) === "file") {
|
|
201
174
|
const manifest = readJSON(host, path);
|
|
202
175
|
const version = isRecord(manifest) ? manifest["version"] : undefined;
|
|
203
176
|
return typeof version === "string" ? version : "";
|
|
204
177
|
}
|
|
205
|
-
if (dirnamePath(at) === at) {
|
|
206
|
-
return undefined;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/** Whether one node wraps an expression without changing its value. */
|
|
212
|
-
function isWrapper(
|
|
213
|
-
node: Node,
|
|
214
|
-
): node is ParenthesizedExpression | AsExpression | SatisfiesExpression | NonNullExpression {
|
|
215
|
-
return (
|
|
216
|
-
isParenthesizedExpression(node) ||
|
|
217
|
-
isAsExpression(node) ||
|
|
218
|
-
isSatisfiesExpression(node) ||
|
|
219
|
-
isNonNullExpression(node)
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** The expression a wrapper that changes no value holds. */
|
|
224
|
-
function unwrapped(node: Node): Node {
|
|
225
|
-
let at = node;
|
|
226
|
-
while (isWrapper(at)) {
|
|
227
|
-
at = at.expression;
|
|
228
|
-
}
|
|
229
|
-
return at;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** The key one property is written with, where it is written as a name or as literal text. */
|
|
233
|
-
function keyOf(property: Node): string | undefined {
|
|
234
|
-
if (!isPropertyAssignment(property) && !isShorthandPropertyAssignment(property)) {
|
|
235
|
-
return undefined;
|
|
236
|
-
}
|
|
237
|
-
const { name } = property;
|
|
238
|
-
const key = isComputedPropertyName(name) ? name.expression : name;
|
|
239
|
-
if (
|
|
240
|
-
(isIdentifier(key) && key === name) ||
|
|
241
|
-
isStringLiteral(key) ||
|
|
242
|
-
isNoSubstitutionTemplateLiteral(key)
|
|
243
|
-
) {
|
|
244
|
-
return key.text;
|
|
245
178
|
}
|
|
246
179
|
return undefined;
|
|
247
180
|
}
|
|
248
181
|
|
|
249
|
-
/**
|
|
250
|
-
function
|
|
251
|
-
|
|
252
|
-
if (name === undefined || !isComputedPropertyName(name)) {
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
const key = name.expression;
|
|
256
|
-
return !(isStringLiteral(key) || isNumericLiteral(key) || isNoSubstitutionTemplateLiteral(key));
|
|
182
|
+
/** The objects a reading reads in one configuration file: each named call's options, or the default export. */
|
|
183
|
+
function readObjects(module: StaticModule, call: OptionsCall | undefined): readonly Value[] {
|
|
184
|
+
return call === undefined ? module.defaultExport : module.options(call.module, call.export);
|
|
257
185
|
}
|
|
258
186
|
|
|
259
|
-
/**
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
for (const statement of file.statements) {
|
|
263
|
-
if (isVariableStatement(statement)) {
|
|
264
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
265
|
-
if (isIdentifier(declaration.name) && declaration.initializer !== undefined) {
|
|
266
|
-
values.set(declaration.name.text, declaration.initializer);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return values;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function isModuleExports(node: Node): boolean {
|
|
275
|
-
return (
|
|
276
|
-
isPropertyAccessExpression(node) &&
|
|
277
|
-
isIdentifier(node.expression) &&
|
|
278
|
-
node.expression.text === "module" &&
|
|
279
|
-
node.name.text === "exports"
|
|
280
|
-
);
|
|
187
|
+
/** Where a value that a reading cannot take as a literal is written, undefined in a document with no positions. */
|
|
188
|
+
interface NotLiteral {
|
|
189
|
+
readonly at: number | undefined;
|
|
281
190
|
}
|
|
282
191
|
|
|
283
192
|
/**
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (file.fileName.endsWith(JSON_EXTENSION)) {
|
|
289
|
-
return file.statements.flatMap((statement) =>
|
|
290
|
-
isExpressionStatement(statement) ? [statement.expression] : [],
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
const found: Node[] = [];
|
|
294
|
-
for (const statement of file.statements) {
|
|
295
|
-
if (isExportAssignment(statement)) {
|
|
296
|
-
found.push(statement.expression);
|
|
297
|
-
} else if (
|
|
298
|
-
(isFunctionDeclaration(statement) || isClassDeclaration(statement)) &&
|
|
299
|
-
statement.modifiers?.some((one) => one.kind === SyntaxKind.DefaultKeyword) === true
|
|
300
|
-
) {
|
|
301
|
-
found.push(statement);
|
|
302
|
-
} else if (
|
|
303
|
-
isExportDeclaration(statement) &&
|
|
304
|
-
statement.moduleSpecifier === undefined &&
|
|
305
|
-
statement.exportClause !== undefined &&
|
|
306
|
-
isNamedExports(statement.exportClause)
|
|
307
|
-
) {
|
|
308
|
-
for (const element of statement.exportClause.elements) {
|
|
309
|
-
if (element.name.text === "default") {
|
|
310
|
-
found.push(element.propertyName ?? element.name);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
} else if (
|
|
314
|
-
isExpressionStatement(statement) &&
|
|
315
|
-
isBinaryExpression(statement.expression) &&
|
|
316
|
-
statement.expression.operatorToken.kind === SyntaxKind.EqualsToken &&
|
|
317
|
-
isModuleExports(statement.expression.left)
|
|
318
|
-
) {
|
|
319
|
-
found.push(statement.expression.right);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return found;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/** The declarations whose name is a binding of the scope they are written in. */
|
|
326
|
-
const BINDING_KINDS: ReadonlySet<SyntaxKind> = new Set([
|
|
327
|
-
SyntaxKind.VariableDeclaration,
|
|
328
|
-
SyntaxKind.Parameter,
|
|
329
|
-
SyntaxKind.BindingElement,
|
|
330
|
-
SyntaxKind.FunctionDeclaration,
|
|
331
|
-
SyntaxKind.ClassDeclaration,
|
|
332
|
-
SyntaxKind.ImportClause,
|
|
333
|
-
SyntaxKind.ImportSpecifier,
|
|
334
|
-
SyntaxKind.NamespaceImport,
|
|
335
|
-
SyntaxKind.ImportEqualsDeclaration,
|
|
336
|
-
]);
|
|
337
|
-
|
|
338
|
-
/** Whether one file declares a binding named `require` at any depth, which shadows the global. */
|
|
339
|
-
function declaresRequire(file: SourceFile): boolean {
|
|
340
|
-
let found = false;
|
|
341
|
-
const visit = (node: Node): void => {
|
|
342
|
-
const name = (node as { readonly name?: Node }).name;
|
|
343
|
-
if (
|
|
344
|
-
name !== undefined &&
|
|
345
|
-
isIdentifier(name) &&
|
|
346
|
-
name.text === "require" &&
|
|
347
|
-
BINDING_KINDS.has(node.kind)
|
|
348
|
-
) {
|
|
349
|
-
found = true;
|
|
350
|
-
}
|
|
351
|
-
if (!found) {
|
|
352
|
-
node.forEachChild(visit);
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
file.forEachChild(visit);
|
|
356
|
-
return found;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* The top-level `const`, `let` and `var` declarations that bind a `require` of one module,
|
|
361
|
-
* written with the module's name as its one string-literal argument, in a file that
|
|
362
|
-
* declares no binding named `require`: each binds as an import of the module does.
|
|
363
|
-
*/
|
|
364
|
-
function requireBindings(
|
|
365
|
-
file: SourceFile,
|
|
366
|
-
module: string,
|
|
367
|
-
): readonly (Identifier | ObjectBindingPattern)[] {
|
|
368
|
-
const found: (Identifier | ObjectBindingPattern)[] = [];
|
|
369
|
-
for (const statement of file.statements) {
|
|
370
|
-
if (!isVariableStatement(statement)) {
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
374
|
-
const value =
|
|
375
|
-
declaration.initializer === undefined ? undefined : unwrapped(declaration.initializer);
|
|
376
|
-
const [argument, ...rest] =
|
|
377
|
-
value !== undefined && isCallExpression(value) ? value.arguments : [];
|
|
378
|
-
if (
|
|
379
|
-
value === undefined ||
|
|
380
|
-
!isCallExpression(value) ||
|
|
381
|
-
!isIdentifier(value.expression) ||
|
|
382
|
-
value.expression.text !== "require" ||
|
|
383
|
-
argument === undefined ||
|
|
384
|
-
rest.length > 0 ||
|
|
385
|
-
!isStringLiteral(argument) ||
|
|
386
|
-
argument.text !== module
|
|
387
|
-
) {
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
if (isIdentifier(declaration.name) || isObjectBindingPattern(declaration.name)) {
|
|
391
|
-
found.push(declaration.name);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return found.length > 0 && declaresRequire(file) ? [] : found;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* The first argument of every call one file makes to one module's export, through a
|
|
400
|
-
* named or a namespace import; `undefined` for a call with no argument.
|
|
401
|
-
*/
|
|
402
|
-
function optionsArguments(file: SourceFile, call: OptionsCall): readonly (Node | undefined)[] {
|
|
403
|
-
const names = new Set<string>();
|
|
404
|
-
const namespaces = new Set<string>();
|
|
405
|
-
for (const name of requireBindings(file, call.module)) {
|
|
406
|
-
if (isIdentifier(name)) {
|
|
407
|
-
namespaces.add(name.text);
|
|
408
|
-
continue;
|
|
409
|
-
}
|
|
410
|
-
for (const element of name.elements) {
|
|
411
|
-
const local = element.name;
|
|
412
|
-
const exported = element.propertyName ?? local;
|
|
413
|
-
if (
|
|
414
|
-
element.dotDotDotToken === undefined &&
|
|
415
|
-
local !== undefined &&
|
|
416
|
-
isIdentifier(local) &&
|
|
417
|
-
exported !== undefined &&
|
|
418
|
-
(isIdentifier(exported) || isStringLiteral(exported)) &&
|
|
419
|
-
exported.text === call.export
|
|
420
|
-
) {
|
|
421
|
-
names.add(local.text);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
for (const statement of file.statements) {
|
|
426
|
-
const bindings = isImportDeclaration(statement)
|
|
427
|
-
? statement.importClause?.namedBindings
|
|
428
|
-
: undefined;
|
|
429
|
-
if (
|
|
430
|
-
bindings === undefined ||
|
|
431
|
-
!isImportDeclaration(statement) ||
|
|
432
|
-
!isStringLiteral(statement.moduleSpecifier) ||
|
|
433
|
-
statement.moduleSpecifier.text !== call.module
|
|
434
|
-
) {
|
|
435
|
-
continue;
|
|
436
|
-
}
|
|
437
|
-
if (isNamespaceImport(bindings)) {
|
|
438
|
-
namespaces.add(bindings.name.text);
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
for (const element of bindings.elements) {
|
|
442
|
-
if ((element.propertyName ?? element.name).text === call.export) {
|
|
443
|
-
names.add(element.name.text);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
const found: (Node | undefined)[] = [];
|
|
448
|
-
const visit = (node: Node): void => {
|
|
449
|
-
if (isCallExpression(node)) {
|
|
450
|
-
const callee = unwrapped(node.expression);
|
|
451
|
-
const named = isIdentifier(callee) && names.has(callee.text);
|
|
452
|
-
const throughNamespace =
|
|
453
|
-
isPropertyAccessExpression(callee) &&
|
|
454
|
-
isIdentifier(callee.expression) &&
|
|
455
|
-
namespaces.has(callee.expression.text) &&
|
|
456
|
-
callee.name.text === call.export;
|
|
457
|
-
if (named || throughNamespace) {
|
|
458
|
-
found.push(node.arguments[0]);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
node.forEachChild(visit);
|
|
462
|
-
};
|
|
463
|
-
file.forEachChild(visit);
|
|
464
|
-
return found;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* The object literal one expression stands for, read through the file's top-level
|
|
469
|
-
* declarations and, where `throughCalls`, through a call's first argument: `undefined`
|
|
470
|
-
* for a call with no argument, and any other node where the expression is not written
|
|
471
|
-
* out as an object literal.
|
|
472
|
-
*/
|
|
473
|
-
function objectOf(
|
|
474
|
-
node: Node,
|
|
475
|
-
declared: ReadonlyMap<string, Node>,
|
|
476
|
-
throughCalls: boolean,
|
|
477
|
-
seen: ReadonlySet<string> = new Set(),
|
|
478
|
-
): Node | undefined {
|
|
479
|
-
const at = unwrapped(node);
|
|
480
|
-
if (isIdentifier(at)) {
|
|
481
|
-
const value = declared.get(at.text);
|
|
482
|
-
if (value !== undefined && !seen.has(at.text)) {
|
|
483
|
-
return objectOf(value, declared, throughCalls, new Set([...seen, at.text]));
|
|
484
|
-
}
|
|
485
|
-
} else if (throughCalls && isCallExpression(at)) {
|
|
486
|
-
const [first] = at.arguments;
|
|
487
|
-
return first === undefined ? undefined : objectOf(first, declared, throughCalls, seen);
|
|
488
|
-
}
|
|
489
|
-
return at;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/** The text of a string literal or a template literal with no substitution, or undefined. */
|
|
493
|
-
function literalText(node: Node): string | undefined {
|
|
494
|
-
const at = unwrapped(node);
|
|
495
|
-
return isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at) ? at.text : undefined;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* The literal texts one value at a path's end writes: the literal itself, or, where
|
|
500
|
-
* `elements`, each element of an array literal of literals. Undefined for any other value.
|
|
501
|
-
*/
|
|
502
|
-
function literalTexts(held: Node, elements: boolean): readonly string[] | undefined {
|
|
503
|
-
const one = literalText(held);
|
|
504
|
-
if (one !== undefined) {
|
|
505
|
-
return [one];
|
|
506
|
-
}
|
|
507
|
-
if (!elements || !isArrayLiteralExpression(held)) {
|
|
508
|
-
return undefined;
|
|
509
|
-
}
|
|
510
|
-
const texts = held.elements.map(literalText);
|
|
511
|
-
return texts.every((text) => text !== undefined) ? texts : undefined;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* Reads the values at one path of keys below one object literal into `values`, and
|
|
516
|
-
* answers the first member that does not write its value out: a spread, a value that is
|
|
517
|
-
* not a literal at the path's end, or one that is not an object literal before it.
|
|
193
|
+
* Reads the values at one path of keys below one object into `values`, and answers the
|
|
194
|
+
* first place that does not write its value out: an object that is no object literal, a
|
|
195
|
+
* spread or a computed key, a value at the path's end that is no literal, or one before
|
|
196
|
+
* it that is no object literal.
|
|
518
197
|
*/
|
|
519
198
|
function readPath(
|
|
520
|
-
object:
|
|
199
|
+
object: Value,
|
|
521
200
|
keys: readonly string[],
|
|
522
201
|
values: string[],
|
|
523
202
|
elements: boolean,
|
|
524
|
-
):
|
|
203
|
+
): NotLiteral | undefined {
|
|
204
|
+
if (object.kind !== "object") {
|
|
205
|
+
return object;
|
|
206
|
+
}
|
|
525
207
|
const [key, ...rest] = keys;
|
|
526
|
-
for (const member of object.
|
|
527
|
-
if (
|
|
208
|
+
for (const member of object.members) {
|
|
209
|
+
if (member.key === undefined) {
|
|
528
210
|
return member;
|
|
529
211
|
}
|
|
530
|
-
if (
|
|
212
|
+
if (member.key !== key) {
|
|
531
213
|
continue;
|
|
532
214
|
}
|
|
533
|
-
const held = isPropertyAssignment(member) ? unwrapped(member.initializer) : undefined;
|
|
534
215
|
if (rest.length === 0) {
|
|
535
|
-
const texts =
|
|
216
|
+
const texts = textsOf(member.value, elements);
|
|
536
217
|
if (texts === undefined) {
|
|
537
218
|
return member;
|
|
538
219
|
}
|
|
539
220
|
values.push(...texts);
|
|
540
221
|
} else {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
const failed = readPath(held, rest, values, elements);
|
|
222
|
+
const failed =
|
|
223
|
+
member.value.kind === "object" ? readPath(member.value, rest, values, elements) : member;
|
|
545
224
|
if (failed !== undefined) {
|
|
546
225
|
return failed;
|
|
547
226
|
}
|
|
@@ -550,80 +229,45 @@ function readPath(
|
|
|
550
229
|
return undefined;
|
|
551
230
|
}
|
|
552
231
|
|
|
553
|
-
/**
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
one === undefined ? undefined : objectOf(one, declared, false),
|
|
564
|
-
);
|
|
565
|
-
const found: Node[] = [];
|
|
566
|
-
const visit = (object: Node, keys: readonly string[]): void => {
|
|
567
|
-
if (!isObjectLiteralExpression(object)) {
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
const [key, ...rest] = keys;
|
|
571
|
-
for (const member of object.properties) {
|
|
572
|
-
if (!isPropertyAssignment(member) || keyOf(member) !== key) {
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
const held = unwrapped(member.initializer);
|
|
576
|
-
if (rest.length > 0) {
|
|
577
|
-
visit(held, rest);
|
|
578
|
-
} else {
|
|
579
|
-
found.push(held);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
for (const object of objects) {
|
|
584
|
-
if (object !== undefined) {
|
|
585
|
-
visit(object, property.split("."));
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return found;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
/** The string literals among values: each value itself, or each element of an array value. */
|
|
592
|
-
function stringsAt(file: SourceFile, property: string, call?: OptionsCall): readonly Node[] {
|
|
593
|
-
return valuesAt(file, property, call).flatMap((held) => {
|
|
594
|
-
if (literalText(held) !== undefined) {
|
|
595
|
-
return [held];
|
|
596
|
-
}
|
|
597
|
-
return isArrayLiteralExpression(held)
|
|
598
|
-
? held.elements.map(unwrapped).filter((one) => literalText(one) !== undefined)
|
|
599
|
-
: [];
|
|
600
|
-
});
|
|
232
|
+
/** The text values among the values at one path: each value itself, or each element of an array value. */
|
|
233
|
+
function stringsAt(
|
|
234
|
+
objects: readonly Value[],
|
|
235
|
+
property: string,
|
|
236
|
+
): readonly { readonly text: string; readonly at: number | undefined }[] {
|
|
237
|
+
return valuesAt(objects, property.split(".")).flatMap((value) =>
|
|
238
|
+
(value.kind === "array" ? value.elements : [value]).flatMap((one) =>
|
|
239
|
+
one.kind === "text" ? [one] : [],
|
|
240
|
+
),
|
|
241
|
+
);
|
|
601
242
|
}
|
|
602
243
|
|
|
603
244
|
/** Whether one value holds a helper setting's value, as {@link HelperReading} states it. */
|
|
604
|
-
function holdsValue(held:
|
|
245
|
+
function holdsValue(held: Value, value: true | string): boolean {
|
|
605
246
|
if (value === true) {
|
|
606
|
-
return held.kind ===
|
|
247
|
+
return held.kind === "boolean" && held.value;
|
|
607
248
|
}
|
|
608
|
-
if (
|
|
609
|
-
return
|
|
249
|
+
if (held.kind === "text") {
|
|
250
|
+
return held.text === value;
|
|
610
251
|
}
|
|
611
252
|
return (
|
|
612
|
-
|
|
253
|
+
held.kind === "array" &&
|
|
613
254
|
held.elements.some((element) => {
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
return first !== undefined && literalText(first) === value;
|
|
255
|
+
const first = element.kind === "array" ? element.elements[0] : element;
|
|
256
|
+
return first?.kind === "text" && first.text === value;
|
|
617
257
|
})
|
|
618
258
|
);
|
|
619
259
|
}
|
|
620
260
|
|
|
621
|
-
/** The directory one
|
|
622
|
-
function testFileRoot(
|
|
623
|
-
|
|
624
|
-
|
|
261
|
+
/** The directory one group of objects' test-file strings are read against, as {@link TestFileReading} states it. */
|
|
262
|
+
function testFileRoot(
|
|
263
|
+
objects: readonly Value[],
|
|
264
|
+
dir: string,
|
|
265
|
+
roots: readonly string[],
|
|
266
|
+
): string | undefined {
|
|
267
|
+
for (const property of roots) {
|
|
268
|
+
const values = valuesAt(objects, property.split("."));
|
|
625
269
|
if (values.length > 0) {
|
|
626
|
-
const texts = new Set(values.map(
|
|
270
|
+
const texts = new Set(values.map((one) => (one.kind === "text" ? one.text : undefined)));
|
|
627
271
|
const [text] = texts;
|
|
628
272
|
return texts.size === 1 && text !== undefined ? resolvePath(dir, text) : undefined;
|
|
629
273
|
}
|
|
@@ -631,45 +275,146 @@ function testFileRoot(file: SourceFile, dir: string, reading: TestFileReading):
|
|
|
631
275
|
return dir;
|
|
632
276
|
}
|
|
633
277
|
|
|
634
|
-
/**
|
|
278
|
+
/**
|
|
279
|
+
* The files an applied row's test-file properties name, each string also added to
|
|
280
|
+
* `selected`, since a test file is rooted by no configuration string (conventions.11). A
|
|
281
|
+
* property's path up to its last `*` selects objects that each resolve their strings
|
|
282
|
+
* against their own roots.
|
|
283
|
+
*/
|
|
635
284
|
function testFilesNamed(
|
|
636
|
-
|
|
637
|
-
parse: ParseSource,
|
|
285
|
+
read: ReadFiles,
|
|
638
286
|
dir: string,
|
|
639
287
|
readings: readonly TestFileReading[],
|
|
640
|
-
into:
|
|
288
|
+
into: Map<string, string>,
|
|
289
|
+
selected: Map<string, { starts: Set<number>; texts: string[] }>,
|
|
641
290
|
): void {
|
|
642
291
|
for (const reading of readings) {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
292
|
+
const star = reading.property.lastIndexOf("*.");
|
|
293
|
+
const each = star < 0 ? undefined : reading.property.slice(0, star + 1).split(".");
|
|
294
|
+
const property = star < 0 ? reading.property : reading.property.slice(star + 2);
|
|
295
|
+
for (const { name, module } of read(dir, reading.files)) {
|
|
296
|
+
const path = joinPath(dir, name);
|
|
297
|
+
const held = selected.get(path) ?? { starts: new Set<number>(), texts: [] };
|
|
298
|
+
selected.set(path, held);
|
|
299
|
+
const objects = readObjects(module, reading.call);
|
|
300
|
+
const groups = each === undefined ? [objects] : valuesAt(objects, each).map((one) => [one]);
|
|
301
|
+
for (const group of groups) {
|
|
302
|
+
const at = testFileRoot(group, dir, reading.roots);
|
|
303
|
+
if (at === undefined) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
for (const { text, at: start } of stringsAt(group, property)) {
|
|
307
|
+
if (start !== undefined) {
|
|
308
|
+
held.starts.add(start);
|
|
309
|
+
}
|
|
310
|
+
held.texts.push(text);
|
|
311
|
+
if (text !== "") {
|
|
312
|
+
into.set(resolvePath(at, text), text);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
647
315
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** The directory and patterns one configuration object's tests are read from, undefined where its text does not state them. */
|
|
321
|
+
interface TestDirectory {
|
|
322
|
+
readonly directory: string;
|
|
323
|
+
readonly patterns: readonly string[];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** The one value an object's last write of a key gives, `null` where it writes none, undefined where it may hide one. */
|
|
327
|
+
function lastMember(object: Value, key: string): Value | null | undefined {
|
|
328
|
+
const values = membersNamed(object, key);
|
|
329
|
+
return values === undefined ? undefined : (values.at(-1) ?? null);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** The test directory one object states, each member it does not write taken from `inherited`. */
|
|
333
|
+
function testDirectoryOf(
|
|
334
|
+
object: Value,
|
|
335
|
+
reading: TestDirectoryReading,
|
|
336
|
+
inherited: TestDirectory,
|
|
337
|
+
): TestDirectory | undefined {
|
|
338
|
+
if (object.kind !== "object") {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
const directory = lastMember(object, reading.directory);
|
|
342
|
+
const match = lastMember(object, reading.match);
|
|
343
|
+
if (directory === undefined || match === undefined) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
const dirs = directory === null ? [inherited.directory] : textsOf(directory, false);
|
|
347
|
+
const patterns = match === null ? inherited.patterns : textsOf(match, true);
|
|
348
|
+
const [one] = dirs ?? [];
|
|
349
|
+
return one === undefined || patterns === undefined ? undefined : { directory: one, patterns };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The test-file patterns an applied row's test directory readings give: each configuration
|
|
354
|
+
* object's own, or, where it lists projects, each project's, a member a project does not
|
|
355
|
+
* write taken from the configuration. An object whose text does not state them gives none.
|
|
356
|
+
*/
|
|
357
|
+
function testDirectoryGlobs(
|
|
358
|
+
read: ReadFiles,
|
|
359
|
+
dir: string,
|
|
360
|
+
readings: readonly TestDirectoryReading[],
|
|
361
|
+
into: TestGlob[],
|
|
362
|
+
): void {
|
|
363
|
+
for (const reading of readings) {
|
|
364
|
+
const base = { directory: ".", patterns: [reading.defaultMatch] };
|
|
365
|
+
for (const { module } of read(dir, reading.files)) {
|
|
366
|
+
for (const config of module.defaultExport) {
|
|
367
|
+
const top = testDirectoryOf(config, reading, base);
|
|
368
|
+
const projects = lastMember(config, reading.projects);
|
|
369
|
+
if (top === undefined || projects === undefined) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const listed =
|
|
373
|
+
projects === null ? [] : projects.kind === "array" ? projects.elements : [projects];
|
|
374
|
+
const each =
|
|
375
|
+
listed.length === 0 ? [top] : listed.map((one) => testDirectoryOf(one, reading, top));
|
|
376
|
+
for (const found of each) {
|
|
377
|
+
for (const pattern of found?.patterns ?? []) {
|
|
378
|
+
const expression = anywhereBelow(pattern);
|
|
379
|
+
into.push({
|
|
380
|
+
dir: resolvePath(dir, found?.directory ?? "."),
|
|
381
|
+
pattern,
|
|
382
|
+
matches: (name) => expression.test(name.replace(/^\/+/u, "")),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
652
385
|
}
|
|
653
386
|
}
|
|
654
387
|
}
|
|
655
388
|
}
|
|
656
389
|
}
|
|
657
390
|
|
|
391
|
+
/**
|
|
392
|
+
* The expression a test-file pattern denotes over a file's whole path, as Playwright's
|
|
393
|
+
* matcher reads it: a pattern gains a leading `**` segment unless it starts with one, and
|
|
394
|
+
* case is not compared (https://github.com/microsoft/playwright/blob/main/packages/playwright/src/util.ts,
|
|
395
|
+
* `createFileMatcher`).
|
|
396
|
+
*/
|
|
397
|
+
function anywhereBelow(pattern: string): RegExp {
|
|
398
|
+
const expression = globExpression(pattern.startsWith("**/") ? pattern : `**/${pattern}`);
|
|
399
|
+
return new RegExp(expression.source, "iu");
|
|
400
|
+
}
|
|
401
|
+
|
|
658
402
|
/** The strings an applied row's selection keys hold, added per configuration file to `into`. */
|
|
659
403
|
function selectedStrings(
|
|
660
|
-
|
|
661
|
-
parse: ParseSource,
|
|
404
|
+
read: ReadFiles,
|
|
662
405
|
dir: string,
|
|
663
406
|
readings: readonly MoveReading[],
|
|
664
407
|
into: Map<string, { starts: Set<number>; texts: string[] }>,
|
|
665
408
|
): void {
|
|
666
409
|
for (const reading of readings) {
|
|
667
|
-
for (const { name,
|
|
410
|
+
for (const { name, module } of read(dir, reading.files)) {
|
|
668
411
|
const path = joinPath(dir, name);
|
|
669
412
|
const held = into.get(path) ?? { starts: new Set<number>(), texts: [] };
|
|
670
|
-
for (const
|
|
671
|
-
|
|
672
|
-
|
|
413
|
+
for (const { text, at } of stringsAt(readObjects(module, reading.call), reading.property)) {
|
|
414
|
+
if (at !== undefined) {
|
|
415
|
+
held.starts.add(at);
|
|
416
|
+
}
|
|
417
|
+
held.texts.push(text);
|
|
673
418
|
}
|
|
674
419
|
into.set(path, held);
|
|
675
420
|
}
|
|
@@ -695,7 +440,7 @@ function manifestKeyUses(manifest: unknown, keys: readonly string[]): readonly s
|
|
|
695
440
|
}
|
|
696
441
|
|
|
697
442
|
/** The extensions an entry a manifest entry key names without one may carry. */
|
|
698
|
-
const ENTRY_EXTENSIONS =
|
|
443
|
+
const ENTRY_EXTENSIONS = braced(APPENDED_EXTENSIONS);
|
|
699
444
|
|
|
700
445
|
/** The characters that make a manifest string a pattern rather than a directory's name. */
|
|
701
446
|
const PATTERN = /[*?{}[\]]/u;
|
|
@@ -739,12 +484,12 @@ function manifestEntryGlobs(manifest: unknown, keys: readonly ManifestEntryKey[]
|
|
|
739
484
|
interface PropertyReading {
|
|
740
485
|
/** The literal values the file sets the property to. */
|
|
741
486
|
readonly values: readonly string[];
|
|
742
|
-
/**
|
|
743
|
-
readonly notLiteral?:
|
|
487
|
+
/** Where the first value that is not a literal is written, where there is one. */
|
|
488
|
+
readonly notLiteral?: NotLiteral;
|
|
744
489
|
}
|
|
745
490
|
|
|
746
491
|
/**
|
|
747
|
-
* Every value one file writes at one property's path inside the
|
|
492
|
+
* Every value one file writes at one property's path inside the objects it is read from:
|
|
748
493
|
* the options argument of every call `call` names, or the file's default export. A string
|
|
749
494
|
* literal or a template literal with no substitution is the value, and so is each element
|
|
750
495
|
* of an array literal of them where `elements`. A read object, or an object along the
|
|
@@ -752,26 +497,14 @@ interface PropertyReading {
|
|
|
752
497
|
* itself, is not a literal, and neither is any other value at the path.
|
|
753
498
|
*/
|
|
754
499
|
export function readProperty(
|
|
755
|
-
|
|
500
|
+
module: StaticModule,
|
|
756
501
|
property: string,
|
|
757
502
|
call?: OptionsCall,
|
|
758
503
|
elements = false,
|
|
759
504
|
): PropertyReading {
|
|
760
|
-
const declared = declaredValues(file);
|
|
761
|
-
const objects =
|
|
762
|
-
call === undefined
|
|
763
|
-
? defaultExports(file).map((one) => objectOf(one, declared, true))
|
|
764
|
-
: optionsArguments(file, call).map((one) =>
|
|
765
|
-
one === undefined ? undefined : objectOf(one, declared, false),
|
|
766
|
-
);
|
|
767
505
|
const values: string[] = [];
|
|
768
|
-
for (const object of
|
|
769
|
-
|
|
770
|
-
continue;
|
|
771
|
-
}
|
|
772
|
-
const notLiteral = isObjectLiteralExpression(object)
|
|
773
|
-
? readPath(object, property.split("."), values, elements)
|
|
774
|
-
: object;
|
|
506
|
+
for (const object of readObjects(module, call)) {
|
|
507
|
+
const notLiteral = readPath(object, property.split("."), values, elements);
|
|
775
508
|
if (notLiteral !== undefined) {
|
|
776
509
|
return { values, notLiteral };
|
|
777
510
|
}
|
|
@@ -815,24 +548,14 @@ function expand(
|
|
|
815
548
|
/** The configuration files in one directory that one glob names, by name. */
|
|
816
549
|
function configurationFiles(host: Host, dir: string, glob: string): readonly string[] {
|
|
817
550
|
const expression = globExpression(glob);
|
|
818
|
-
|
|
819
|
-
try {
|
|
820
|
-
entries = host.readDirectory(dir);
|
|
821
|
-
} catch {
|
|
822
|
-
return [];
|
|
823
|
-
}
|
|
824
|
-
return entries
|
|
825
|
-
.filter((entry) => !entry.directory && expression.test(entry.name))
|
|
826
|
-
.map((entry) => entry.name)
|
|
827
|
-
.sort();
|
|
551
|
+
return filesIn(host, dir, (name) => expression.test(name));
|
|
828
552
|
}
|
|
829
553
|
|
|
830
554
|
/** What one move resolved to: its directories, or the failure its property met. */
|
|
831
555
|
type MoveResult = { readonly dirs: readonly (string | undefined)[] } | { readonly failure: string };
|
|
832
556
|
|
|
833
557
|
function resolveMove(
|
|
834
|
-
|
|
835
|
-
parse: ParseSource,
|
|
558
|
+
read: ReadFiles,
|
|
836
559
|
dir: string,
|
|
837
560
|
row: ConventionRow,
|
|
838
561
|
move: DirectoryMove,
|
|
@@ -840,13 +563,14 @@ function resolveMove(
|
|
|
840
563
|
): MoveResult {
|
|
841
564
|
const set: string[] = [];
|
|
842
565
|
for (const reading of move.readings) {
|
|
843
|
-
for (const { name, text,
|
|
844
|
-
const read = readProperty(
|
|
566
|
+
for (const { name, text, module } of read(dir, reading.files)) {
|
|
567
|
+
const read = readProperty(module, reading.property, reading.call);
|
|
845
568
|
if (read.notLiteral !== undefined) {
|
|
846
|
-
const
|
|
569
|
+
const { at } = read.notLiteral;
|
|
570
|
+
const where = at === undefined ? name : `${name}:${String(lineOf(text, at))}`;
|
|
847
571
|
return {
|
|
848
572
|
failure:
|
|
849
|
-
`${
|
|
573
|
+
`${where} sets ${reading.property}, which moves a directory ` +
|
|
850
574
|
`the convention row ${row.name} reads, to a value that is not a string literal: ` +
|
|
851
575
|
`write it as a literal, or name ${row.name} in ts.disabled_conventions and its ` +
|
|
852
576
|
`files in ts.entry_files`,
|
|
@@ -866,31 +590,44 @@ function resolveMove(
|
|
|
866
590
|
return { dirs: [...new Set(dirs)] };
|
|
867
591
|
}
|
|
868
592
|
|
|
869
|
-
/**
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
593
|
+
/** One configuration file a reading names, read as a static module. */
|
|
594
|
+
interface ReadFile {
|
|
595
|
+
readonly name: string;
|
|
596
|
+
readonly text: string;
|
|
597
|
+
readonly module: StaticModule;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** The configuration files in one directory a reading's globs name, in name order. */
|
|
601
|
+
type ReadFiles = (dir: string, files: readonly string[]) => readonly ReadFile[];
|
|
602
|
+
|
|
603
|
+
/** A reader that reads and parses each configuration file once for every row and reading of a run. */
|
|
604
|
+
function readFilesOnce(host: Host, parse: ParseSource): ReadFiles {
|
|
605
|
+
const read = new Map<string, ReadFile | undefined>();
|
|
606
|
+
const readOne = (dir: string, name: string): ReadFile | undefined => {
|
|
607
|
+
const path = joinPath(dir, name);
|
|
608
|
+
if (read.has(path)) {
|
|
609
|
+
return read.get(path);
|
|
610
|
+
}
|
|
611
|
+
let found: ReadFile | undefined;
|
|
612
|
+
try {
|
|
613
|
+
const text = host.readFile(path);
|
|
614
|
+
if (YAML_NAME.test(name)) {
|
|
615
|
+
const value = yamlDocument(text);
|
|
616
|
+
found = value === undefined ? undefined : { name, text, module: documentModule(value) };
|
|
617
|
+
} else {
|
|
618
|
+
const file = parse(JSON_NAME.test(name) ? `${path}.json` : path, text);
|
|
619
|
+
found = { name, text, module: staticModule(file) };
|
|
885
620
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
text,
|
|
889
|
-
file: parse(JSON_RC.test(name) ? `${path}${JSON_EXTENSION}` : path, text),
|
|
890
|
-
});
|
|
621
|
+
} catch {
|
|
622
|
+
found = undefined;
|
|
891
623
|
}
|
|
892
|
-
|
|
893
|
-
|
|
624
|
+
read.set(path, found);
|
|
625
|
+
return found;
|
|
626
|
+
};
|
|
627
|
+
return (dir, files) =>
|
|
628
|
+
files.flatMap((glob) =>
|
|
629
|
+
configurationFiles(host, dir, glob).flatMap((name) => readOne(dir, name) ?? []),
|
|
630
|
+
);
|
|
894
631
|
}
|
|
895
632
|
|
|
896
633
|
/**
|
|
@@ -899,8 +636,7 @@ function readingFiles(
|
|
|
899
636
|
* declared dependency the row's package name stands for with some value.
|
|
900
637
|
*/
|
|
901
638
|
function shortNameUses(
|
|
902
|
-
|
|
903
|
-
parse: ParseSource,
|
|
639
|
+
read: ReadFiles,
|
|
904
640
|
dir: string,
|
|
905
641
|
manifest: unknown,
|
|
906
642
|
readings: readonly ShortNameReading[],
|
|
@@ -914,8 +650,8 @@ function shortNameUses(
|
|
|
914
650
|
const used = new Set<string>();
|
|
915
651
|
for (const reading of readings) {
|
|
916
652
|
const [before = "", after = ""] = reading.package.split("{}");
|
|
917
|
-
for (const {
|
|
918
|
-
const read = readProperty(
|
|
653
|
+
for (const { module } of read(dir, reading.files)) {
|
|
654
|
+
const read = readProperty(module, reading.property, reading.call, true);
|
|
919
655
|
for (const value of read.values) {
|
|
920
656
|
used.add(`${before}${value}${after}`);
|
|
921
657
|
}
|
|
@@ -932,7 +668,7 @@ function shortNameUses(
|
|
|
932
668
|
}
|
|
933
669
|
}
|
|
934
670
|
}
|
|
935
|
-
return [...used].filter((name) => declared.has(name)).sort(
|
|
671
|
+
return [...used].filter((name) => declared.has(name)).sort(bytewise);
|
|
936
672
|
}
|
|
937
673
|
|
|
938
674
|
/**
|
|
@@ -943,7 +679,7 @@ function prefixedPackage(value: string, reading: PrefixedNameReading): string |
|
|
|
943
679
|
const { prefix, marker } = reading;
|
|
944
680
|
let name: string;
|
|
945
681
|
if (marker === undefined) {
|
|
946
|
-
if (value.includes(":") ||
|
|
682
|
+
if (value.includes(":") || cannotStartPackageName(value)) {
|
|
947
683
|
return undefined;
|
|
948
684
|
}
|
|
949
685
|
const parts = value.split("/");
|
|
@@ -969,8 +705,7 @@ function prefixedPackage(value: string, reading: PrefixedNameReading): string |
|
|
|
969
705
|
|
|
970
706
|
/** The declared dependencies an applied row's prefixed names use. */
|
|
971
707
|
function prefixedNameUses(
|
|
972
|
-
|
|
973
|
-
parse: ParseSource,
|
|
708
|
+
read: ReadFiles,
|
|
974
709
|
dir: string,
|
|
975
710
|
manifest: unknown,
|
|
976
711
|
readings: readonly PrefixedNameReading[],
|
|
@@ -983,8 +718,8 @@ function prefixedNameUses(
|
|
|
983
718
|
);
|
|
984
719
|
const used = new Set<string>();
|
|
985
720
|
for (const reading of readings) {
|
|
986
|
-
for (const {
|
|
987
|
-
for (const value of readProperty(
|
|
721
|
+
for (const { module } of read(dir, reading.files)) {
|
|
722
|
+
for (const value of readProperty(module, reading.property, reading.call, true).values) {
|
|
988
723
|
const named = prefixedPackage(value, reading);
|
|
989
724
|
if (named !== undefined && declared.has(named)) {
|
|
990
725
|
used.add(named);
|
|
@@ -992,13 +727,12 @@ function prefixedNameUses(
|
|
|
992
727
|
}
|
|
993
728
|
}
|
|
994
729
|
}
|
|
995
|
-
return [...used].sort(
|
|
730
|
+
return [...used].sort(bytewise);
|
|
996
731
|
}
|
|
997
732
|
|
|
998
733
|
/** The declared dependencies an applied row's helper settings use. */
|
|
999
734
|
function helperUses(
|
|
1000
|
-
|
|
1001
|
-
parse: ParseSource,
|
|
735
|
+
read: ReadFiles,
|
|
1002
736
|
dir: string,
|
|
1003
737
|
manifest: unknown,
|
|
1004
738
|
readings: readonly HelperReading[],
|
|
@@ -1013,8 +747,8 @@ function helperUses(
|
|
|
1013
747
|
.filter(
|
|
1014
748
|
(reading) =>
|
|
1015
749
|
declared.has(reading.package) &&
|
|
1016
|
-
|
|
1017
|
-
valuesAt(
|
|
750
|
+
read(dir, reading.files).some(({ module }) =>
|
|
751
|
+
valuesAt(readObjects(module, reading.call), reading.property.split(".")).some((held) =>
|
|
1018
752
|
holdsValue(held, reading.value),
|
|
1019
753
|
),
|
|
1020
754
|
),
|
|
@@ -1028,8 +762,7 @@ function helperUses(
|
|
|
1028
762
|
* by `:` and a name. A member's own name is no such string.
|
|
1029
763
|
*/
|
|
1030
764
|
function packageKeyUses(
|
|
1031
|
-
|
|
1032
|
-
parse: ParseSource,
|
|
765
|
+
read: ReadFiles,
|
|
1033
766
|
dir: string,
|
|
1034
767
|
manifest: unknown,
|
|
1035
768
|
keys: readonly PackageKey[],
|
|
@@ -1039,31 +772,31 @@ function packageKeyUses(
|
|
|
1039
772
|
return isRecord(held) ? Object.keys(held) : [];
|
|
1040
773
|
});
|
|
1041
774
|
const strings: string[] = [];
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
775
|
+
const children = (value: Value): readonly Value[] =>
|
|
776
|
+
value.kind === "object"
|
|
777
|
+
? value.members.map((member) => member.value)
|
|
778
|
+
: value.kind === "array"
|
|
779
|
+
? value.elements
|
|
780
|
+
: [];
|
|
781
|
+
const collect = (value: Value): void => {
|
|
782
|
+
if (value.kind === "text") {
|
|
783
|
+
strings.push(value.text);
|
|
1050
784
|
}
|
|
785
|
+
children(value).forEach(collect);
|
|
1051
786
|
};
|
|
1052
787
|
for (const { key, files } of keys) {
|
|
1053
|
-
const visit = (
|
|
1054
|
-
if (
|
|
1055
|
-
|
|
788
|
+
const visit = (value: Value): void => {
|
|
789
|
+
if (value.kind === "object") {
|
|
790
|
+
value.members
|
|
791
|
+
.filter((member) => member.key === key)
|
|
792
|
+
.forEach((member) => {
|
|
793
|
+
collect(member.value);
|
|
794
|
+
});
|
|
1056
795
|
}
|
|
1057
|
-
|
|
796
|
+
children(value).forEach(visit);
|
|
1058
797
|
};
|
|
1059
|
-
for (const {
|
|
1060
|
-
|
|
1061
|
-
for (const one of defaultExports(file)) {
|
|
1062
|
-
const object = objectOf(one, values, true);
|
|
1063
|
-
if (object !== undefined) {
|
|
1064
|
-
visit(object);
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
798
|
+
for (const { module } of read(dir, files)) {
|
|
799
|
+
module.defaultExport.forEach(visit);
|
|
1067
800
|
}
|
|
1068
801
|
}
|
|
1069
802
|
return declared.filter((name) =>
|
|
@@ -1076,12 +809,14 @@ function packageKeyUses(
|
|
|
1076
809
|
);
|
|
1077
810
|
}
|
|
1078
811
|
|
|
1079
|
-
/**
|
|
1080
|
-
function
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
812
|
+
/** What the command reader reads each of a manifest's `scripts` as, in the manifest's directory. */
|
|
813
|
+
function scriptReadings(manifest: unknown, dir: string): readonly CommandReading[] {
|
|
814
|
+
const scripts = isRecord(manifest) ? manifest["scripts"] : undefined;
|
|
815
|
+
return isRecord(scripts)
|
|
816
|
+
? Object.values(scripts).flatMap((text) =>
|
|
817
|
+
typeof text === "string" ? [readCommands(text, dir)] : [],
|
|
818
|
+
)
|
|
819
|
+
: [];
|
|
1085
820
|
}
|
|
1086
821
|
|
|
1087
822
|
/**
|
|
@@ -1101,9 +836,31 @@ export function readConventions(
|
|
|
1101
836
|
const failures: ConventionFailure[] = [];
|
|
1102
837
|
const uses = new Set<string>();
|
|
1103
838
|
const selected = new Map<string, { starts: Set<number>; texts: string[] }>();
|
|
1104
|
-
const testFiles = new
|
|
839
|
+
const testFiles = new Map<string, string>();
|
|
840
|
+
const testGlobs: TestGlob[] = [];
|
|
841
|
+
const read = readFilesOnce(host, parse);
|
|
1105
842
|
for (const manifest of manifests) {
|
|
1106
|
-
const declared = readJSON(host, joinPath(manifest.dir,
|
|
843
|
+
const declared = readJSON(host, joinPath(manifest.dir, MANIFEST));
|
|
844
|
+
const scripts = scriptReadings(declared, manifest.dir);
|
|
845
|
+
let input: RunnerInput | undefined;
|
|
846
|
+
const runnerInput = (): RunnerInput => {
|
|
847
|
+
input ??= {
|
|
848
|
+
host,
|
|
849
|
+
dir: manifest.dir,
|
|
850
|
+
manifest: declared,
|
|
851
|
+
configuration: (dir, names) => {
|
|
852
|
+
for (const name of names) {
|
|
853
|
+
const [found] = read(dir, [name]);
|
|
854
|
+
if (found !== undefined) {
|
|
855
|
+
return { dir, module: found.module };
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return undefined;
|
|
859
|
+
},
|
|
860
|
+
scripts: scripts.flatMap((one) => one.commands),
|
|
861
|
+
};
|
|
862
|
+
return input;
|
|
863
|
+
};
|
|
1107
864
|
const versions = new Map<string, string | undefined>();
|
|
1108
865
|
const missing = new Set<string>();
|
|
1109
866
|
for (const row of rows) {
|
|
@@ -1126,7 +883,7 @@ export function readConventions(
|
|
|
1126
883
|
failures.push({
|
|
1127
884
|
dir: manifest.dir,
|
|
1128
885
|
failure: {
|
|
1129
|
-
|
|
886
|
+
failureClass: "missing-module",
|
|
1130
887
|
detail:
|
|
1131
888
|
`${manifest.path} declares ${row.package}, which no node_modules at or ` +
|
|
1132
889
|
`above its directory holds: run the package manager's install`,
|
|
@@ -1140,7 +897,7 @@ export function readConventions(
|
|
|
1140
897
|
const resolved = new Map<string, readonly (string | undefined)[]>();
|
|
1141
898
|
let failed: string | undefined;
|
|
1142
899
|
for (const move of row.moves) {
|
|
1143
|
-
const result = resolveMove(
|
|
900
|
+
const result = resolveMove(read, manifest.dir, row, move, resolved);
|
|
1144
901
|
if ("failure" in result) {
|
|
1145
902
|
failed = result.failure;
|
|
1146
903
|
break;
|
|
@@ -1152,7 +909,7 @@ export function readConventions(
|
|
|
1152
909
|
failures.push({
|
|
1153
910
|
dir: manifest.dir,
|
|
1154
911
|
failure: {
|
|
1155
|
-
|
|
912
|
+
failureClass: "convention-not-literal",
|
|
1156
913
|
detail: where === "." ? failed : `${where}/${failed}`,
|
|
1157
914
|
},
|
|
1158
915
|
});
|
|
@@ -1164,25 +921,27 @@ export function readConventions(
|
|
|
1164
921
|
version,
|
|
1165
922
|
manifest: manifest.path,
|
|
1166
923
|
});
|
|
1167
|
-
for (const name of shortNameUses(
|
|
924
|
+
for (const name of shortNameUses(read, manifest.dir, declared, row.shortNames ?? [])) {
|
|
1168
925
|
uses.add(name);
|
|
1169
926
|
}
|
|
1170
927
|
const prefixed = row.prefixedNames ?? [];
|
|
1171
|
-
for (const name of prefixedNameUses(
|
|
928
|
+
for (const name of prefixedNameUses(read, manifest.dir, declared, prefixed)) {
|
|
1172
929
|
uses.add(name);
|
|
1173
930
|
}
|
|
1174
931
|
const keys = row.packageKeys ?? [];
|
|
1175
|
-
for (const name of packageKeyUses(
|
|
932
|
+
for (const name of packageKeyUses(read, manifest.dir, declared, keys)) {
|
|
1176
933
|
uses.add(name);
|
|
1177
934
|
}
|
|
1178
935
|
for (const name of manifestKeyUses(declared, row.manifestKeys ?? [])) {
|
|
1179
936
|
uses.add(name);
|
|
1180
937
|
}
|
|
1181
|
-
for (const name of helperUses(
|
|
938
|
+
for (const name of helperUses(read, manifest.dir, declared, row.helpers ?? [])) {
|
|
1182
939
|
uses.add(name);
|
|
1183
940
|
}
|
|
1184
|
-
selectedStrings(
|
|
1185
|
-
testFilesNamed(
|
|
941
|
+
selectedStrings(read, manifest.dir, row.selections ?? [], selected);
|
|
942
|
+
testFilesNamed(read, manifest.dir, row.testFiles ?? [], testFiles, selected);
|
|
943
|
+
testDirectoryGlobs(read, manifest.dir, row.testDirectories ?? [], testGlobs);
|
|
944
|
+
testGlobs.push(...(row.testRunner?.(runnerInput()) ?? []));
|
|
1186
945
|
const excludes = [
|
|
1187
946
|
...new Set((row.excludes ?? []).flatMap((one) => expand(one, resolved))),
|
|
1188
947
|
].map(globExpression);
|
|
@@ -1202,25 +961,50 @@ export function readConventions(
|
|
|
1202
961
|
});
|
|
1203
962
|
}
|
|
1204
963
|
}
|
|
964
|
+
testGlobs.push(...nodeTestGlobs(scripts.flatMap((one) => one.tests)));
|
|
1205
965
|
}
|
|
1206
966
|
const unique = [...new Map(applied.map((one) => [JSON.stringify(one), one])).values()];
|
|
1207
967
|
unique.sort(
|
|
1208
968
|
(a, b) =>
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
969
|
+
bytewise(a.name, b.name) ||
|
|
970
|
+
bytewise(a.manifest, b.manifest) ||
|
|
971
|
+
bytewise(JSON.stringify(a), JSON.stringify(b)),
|
|
1212
972
|
);
|
|
1213
973
|
return {
|
|
1214
974
|
applied: unique,
|
|
1215
975
|
globs,
|
|
1216
976
|
generated,
|
|
1217
977
|
failures,
|
|
1218
|
-
uses: [...uses].sort(
|
|
978
|
+
uses: [...uses].sort(bytewise),
|
|
1219
979
|
selected,
|
|
1220
980
|
testFiles,
|
|
981
|
+
testGlobs,
|
|
1221
982
|
};
|
|
1222
983
|
}
|
|
1223
984
|
|
|
985
|
+
/**
|
|
986
|
+
* The files, absolute, of one project an applied row names as test files, each with the
|
|
987
|
+
* string or the pattern that names it.
|
|
988
|
+
*/
|
|
989
|
+
export function conventionTestFiles(
|
|
990
|
+
conventions: Conventions,
|
|
991
|
+
files: SourceFiles,
|
|
992
|
+
): ReadonlyMap<string, string> {
|
|
993
|
+
const found = new Map(conventions.testFiles);
|
|
994
|
+
for (const { dir, pattern, matches } of conventions.testGlobs) {
|
|
995
|
+
for (const file of files.byName.values()) {
|
|
996
|
+
if (
|
|
997
|
+
relativePath(dir, file.fileName) !== undefined &&
|
|
998
|
+
matches(file.fileName) &&
|
|
999
|
+
!found.has(file.fileName)
|
|
1000
|
+
) {
|
|
1001
|
+
found.set(file.fileName, pattern);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return found;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1224
1008
|
/**
|
|
1225
1009
|
* Every own file of one project below a directory an applied row names as generated, by
|
|
1226
1010
|
* its path below the target root, with the name of the first such row.
|
|
@@ -1273,7 +1057,7 @@ export function conventionFailuresFor(
|
|
|
1273
1057
|
conventions: Conventions,
|
|
1274
1058
|
configFile: string,
|
|
1275
1059
|
ownFiles: readonly string[],
|
|
1276
|
-
): readonly
|
|
1060
|
+
): readonly Failure[] {
|
|
1277
1061
|
return conventions.failures
|
|
1278
1062
|
.filter(
|
|
1279
1063
|
({ dir }) =>
|