@cplieger/deadset-ts 4.0.0 → 5.0.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 (117) hide show
  1. package/README.md +68 -50
  2. package/dist/bin/component-mapper.js +5 -0
  3. package/dist/bin/mapper-connection.js +101 -0
  4. package/dist/bin/node-host.js +24 -2
  5. package/dist/src/analysis.js +230 -37
  6. package/dist/src/component-files.js +612 -0
  7. package/dist/src/component-layer.js +132 -0
  8. package/dist/src/config.js +9 -2
  9. package/dist/src/configuration-files.js +186 -0
  10. package/dist/src/conformance-record.js +2 -2
  11. package/dist/src/convention-rows.js +383 -0
  12. package/dist/src/conventions.js +456 -0
  13. package/dist/src/dependencies.js +30 -3
  14. package/dist/src/discover.js +11 -4
  15. package/dist/src/emit-map.js +72 -0
  16. package/dist/src/entry-point-gaps.js +81 -35
  17. package/dist/src/entry-points.js +47 -32
  18. package/dist/src/exempt.js +11 -3
  19. package/dist/src/findings/completion.js +19 -29
  20. package/dist/src/findings/emitters.js +22 -1
  21. package/dist/src/findings/unused-declarations.js +7 -2
  22. package/dist/src/graph.js +13 -5
  23. package/dist/src/inventory.js +4 -2
  24. package/dist/src/kinds.js +2 -2
  25. package/dist/src/manifest.js +37 -11
  26. package/dist/src/matrix.js +21 -13
  27. package/dist/src/partial.js +3 -1
  28. package/dist/src/pnpm-workspace.js +120 -0
  29. package/dist/src/position.js +17 -2
  30. package/dist/src/print.js +2 -0
  31. package/dist/src/references.js +209 -30
  32. package/dist/src/report.js +26 -6
  33. package/dist/src/reporters/annotations.js +10 -3
  34. package/dist/src/reporters/template.js +6 -3
  35. package/dist/src/resolve.js +31 -6
  36. package/dist/src/roots.js +37 -8
  37. package/dist/src/run.js +13 -3
  38. package/dist/src/schema.js +4 -2
  39. package/dist/src/session.js +141 -34
  40. package/dist/src/setup-failure.js +23 -0
  41. package/dist/src/source-files.js +4 -61
  42. package/dist/src/sweep.js +4 -2
  43. package/dist/src/template-field.js +38 -6
  44. package/dist/src/test-support.js +59 -0
  45. package/dist/src/type-errors.js +252 -0
  46. package/dist/src/type-query-alias.js +92 -0
  47. package/dist/src/verbs/analyze.js +24 -4
  48. package/dist/src/verbs/explain.js +4 -1
  49. package/dist/src/verbs/print-projects.js +17 -56
  50. package/dist/src/verbs/print-retained.js +7 -2
  51. package/dist/src/verbs/print-roots.js +7 -2
  52. package/dist/src/version-range.js +255 -0
  53. package/dist/src/version.js +1 -1
  54. package/dist/src/workspace-manifests.js +68 -0
  55. package/dist/src/workspace-programs.js +342 -0
  56. package/dist/src/workspace-resolution.js +307 -0
  57. package/dist/src/workspace-run.js +75 -0
  58. package/dist/src/workspace.js +358 -0
  59. package/jsr.json +1 -1
  60. package/package.json +2 -1
  61. package/src/analysis.ts +395 -71
  62. package/src/component-files.ts +718 -0
  63. package/src/component-layer.ts +160 -0
  64. package/src/config.ts +11 -2
  65. package/src/configuration-files.ts +235 -0
  66. package/src/conformance-record.ts +2 -2
  67. package/src/convention-rows.ts +440 -0
  68. package/src/conventions.ts +640 -0
  69. package/src/dependencies.ts +36 -2
  70. package/src/discover.ts +11 -7
  71. package/src/emit-map.ts +93 -0
  72. package/src/entry-point-gaps.ts +92 -38
  73. package/src/entry-points.ts +66 -36
  74. package/src/exempt.ts +21 -3
  75. package/src/findings/completion.ts +24 -33
  76. package/src/findings/emitters.ts +32 -1
  77. package/src/findings/unused-declarations.ts +9 -2
  78. package/src/graph.ts +15 -4
  79. package/src/host.ts +23 -0
  80. package/src/index.ts +1 -1
  81. package/src/inventory.ts +13 -2
  82. package/src/kinds.ts +2 -2
  83. package/src/manifest.ts +47 -14
  84. package/src/matrix.ts +43 -15
  85. package/src/partial.ts +6 -1
  86. package/src/pnpm-workspace.ts +135 -0
  87. package/src/position.ts +23 -2
  88. package/src/print.ts +2 -0
  89. package/src/references.ts +227 -28
  90. package/src/report.ts +56 -6
  91. package/src/reporters/annotations.ts +12 -3
  92. package/src/reporters/template.ts +6 -3
  93. package/src/resolve.ts +59 -6
  94. package/src/roots.ts +60 -9
  95. package/src/run.ts +15 -5
  96. package/src/schema.ts +4 -2
  97. package/src/session.ts +218 -37
  98. package/src/setup-failure.ts +39 -0
  99. package/src/source-files.ts +4 -66
  100. package/src/sweep.ts +9 -2
  101. package/src/template-field.ts +42 -7
  102. package/src/test-support.ts +77 -0
  103. package/src/type-errors.ts +340 -0
  104. package/src/type-query-alias.ts +133 -0
  105. package/src/verbs/analyze.ts +38 -4
  106. package/src/verbs/explain.ts +4 -1
  107. package/src/verbs/print-projects.ts +17 -69
  108. package/src/verbs/print-retained.ts +7 -2
  109. package/src/verbs/print-roots.ts +7 -2
  110. package/src/verbs/verb.ts +6 -3
  111. package/src/version-range.ts +309 -0
  112. package/src/version.ts +1 -1
  113. package/src/workspace-manifests.ts +100 -0
  114. package/src/workspace-programs.ts +486 -0
  115. package/src/workspace-resolution.ts +393 -0
  116. package/src/workspace-run.ts +148 -0
  117. package/src/workspace.ts +463 -0
package/src/report.ts CHANGED
@@ -13,18 +13,16 @@ import type {
13
13
  FindingPosition,
14
14
  PositionedSymbol,
15
15
  } from "./finding.ts";
16
+ import type { AppliedConvention } from "./conventions.ts";
16
17
  import type { PassResult } from "./findings-pass.ts";
17
18
  import type { StaleSuppression } from "./findings/self-check.ts";
18
19
  import type { TestFileRule } from "./references.ts";
19
20
 
20
21
  /** The version of the report schema this analyzer writes a report to. */
21
- const SCHEMA_VERSION = "6.1.0";
22
+ const SCHEMA_VERSION = "7.0.0";
22
23
 
23
- /**
24
- * Every report schema version this analyzer reads: the schema versions the Contract it
25
- * implements admits, each an instance of the one it writes.
26
- */
27
- export const SCHEMA_VERSIONS_ACCEPTED: readonly string[] = ["6.0.0", SCHEMA_VERSION];
24
+ /** Every report schema version this analyzer reads: the one the Contract it implements admits. */
25
+ export const SCHEMA_VERSIONS_ACCEPTED: readonly string[] = [SCHEMA_VERSION];
28
26
 
29
27
  /** The name this analyzer writes every document under and mints every identifier with. */
30
28
  export const ANALYZER_NAME = "deadset-ts";
@@ -151,6 +149,22 @@ export interface ReportNotBuilt {
151
149
  readonly error: string;
152
150
  }
153
151
 
152
+ /** One type error that skipped the function or statement holding it. */
153
+ export interface TypeErrorSkip {
154
+ /** The file holding the error, below the target root. */
155
+ readonly path: string;
156
+ readonly line: number;
157
+ /** The compiler's message, on one line. */
158
+ readonly message: string;
159
+ }
160
+
161
+ /** One configuration in which the checker left questions unanswered, with what they held. */
162
+ export interface UnansweredCount {
163
+ readonly configuration: string;
164
+ readonly questions: number;
165
+ readonly declarations: number;
166
+ }
167
+
154
168
  /** One consumer the run loaded, at the directory a report names it by. */
155
169
  interface ReportConsumer {
156
170
  readonly id: string;
@@ -201,6 +215,10 @@ export interface Report {
201
215
  readonly declared_gaps: readonly WireDeclaredGap[];
202
216
  readonly excluded_by_cgo: readonly never[];
203
217
  readonly test_file_rules: readonly TestFileRule[];
218
+ readonly type_error_skips: readonly TypeErrorSkip[];
219
+ readonly notes: readonly never[];
220
+ readonly unanswered_questions: readonly UnansweredCount[];
221
+ readonly conventions_applied: readonly AppliedConvention[];
204
222
  readonly totals: WireTotals;
205
223
  }
206
224
 
@@ -217,6 +235,10 @@ export interface ReportInput {
217
235
  readonly unavailable: readonly UnavailableConsumer[];
218
236
  readonly result: PassResult;
219
237
  readonly testFileRules: readonly TestFileRule[];
238
+ readonly typeErrorSkips: readonly TypeErrorSkip[];
239
+ readonly unanswered: readonly UnansweredCount[];
240
+ /** The convention rows the run applied, in the report's order. */
241
+ readonly conventionsApplied: readonly AppliedConvention[];
220
242
  }
221
243
 
222
244
  /** Two strings ordered bytewise. */
@@ -462,6 +484,34 @@ export function buildReport(input: ReportInput): Report {
462
484
  test_file_rules: [...input.testFileRules]
463
485
  .sort((a, b) => compare(a.rule, b.rule) || a.matched - b.matched)
464
486
  .map((one) => ({ rule: one.rule, matched: one.matched })),
487
+ type_error_skips: [
488
+ ...new Map(
489
+ input.typeErrorSkips.map((one) => {
490
+ const skip = { path: one.path, line: one.line, message: one.message };
491
+ return [JSON.stringify(skip), skip] as const;
492
+ }),
493
+ ),
494
+ ]
495
+ .sort(([a, x], [b, y]) => compare(x.path, y.path) || x.line - y.line || compare(a, b))
496
+ .map(([, skip]) => skip),
497
+ notes: [],
498
+ unanswered_questions: [...input.unanswered]
499
+ .sort(
500
+ (a, b) =>
501
+ compare(a.configuration, b.configuration) ||
502
+ compare(JSON.stringify(a), JSON.stringify(b)),
503
+ )
504
+ .map((one) => ({
505
+ configuration: one.configuration,
506
+ questions: one.questions,
507
+ declarations: one.declarations,
508
+ })),
509
+ conventions_applied: input.conventionsApplied.map((one) => ({
510
+ name: one.name,
511
+ package: one.package,
512
+ version: one.version,
513
+ manifest: one.manifest,
514
+ })),
465
515
  totals: totalsOf(findings, input.result),
466
516
  };
467
517
  }
@@ -37,11 +37,17 @@ function command(
37
37
  return `::${level} file=${escapeProperty(at.path)},line=${String(at.line)},col=${String(at.column)},endLine=${String(endLine)},title=${escapeProperty(title)}::${escapeData(message)}`;
38
38
  }
39
39
 
40
+ /** The title and the message prefix of a type-error skip's annotation. */
41
+ const SKIP_TITLE = "type error skipped";
42
+ const SKIP_MESSAGE =
43
+ "the analysis did not evaluate the function or statement holding this type error: ";
44
+
40
45
  /**
41
46
  * One workflow annotation per finding, then one per stale suppression, each naming its
42
- * file, line, column and last line, titled by its code and kind. A finding at or above the
43
- * failing severity is an error and one below it a warning; a stale suppression is an error,
44
- * because its kind is fixed on at the failing severity.
47
+ * file, line, column and last line, titled by its code and kind, then one warning per
48
+ * type-error skip naming its file and line. A finding at or above the failing severity is
49
+ * an error and one below it a warning; a stale suppression is an error, because its kind
50
+ * is fixed on at the failing severity.
45
51
  */
46
52
  export function annotations(report: Report, options: RenderOptions): Iterable<string> {
47
53
  return coalesced(
@@ -64,6 +70,9 @@ export function annotations(report: Report, options: RenderOptions): Iterable<st
64
70
  stale.message,
65
71
  )}\n`;
66
72
  }
73
+ for (const skip of report.type_error_skips) {
74
+ yield `::warning file=${escapeProperty(skip.path)},line=${String(skip.line)},title=${escapeProperty(SKIP_TITLE)}::${escapeData(SKIP_MESSAGE + skip.message)}\n`;
75
+ }
67
76
  })(),
68
77
  );
69
78
  }
@@ -893,7 +893,7 @@ class Execution {
893
893
  let held = value;
894
894
  for (const name of names) {
895
895
  if (!isObject(held)) {
896
- throw new RenderError(`can't evaluate field ${name} in a ${kindOf(held)}`);
896
+ throw new RenderError(`cannot evaluate field ${name} in a ${kindOf(held)}`);
897
897
  }
898
898
  if (!Object.hasOwn(held, name)) {
899
899
  throw new RenderError(`the document has no member ${JSON.stringify(name)} here`);
@@ -925,9 +925,12 @@ class Execution {
925
925
  if (head === undefined) {
926
926
  throw new RenderError("empty command");
927
927
  }
928
+ if (head.k === "lit" && head.value === null) {
929
+ throw new RenderError("nil is not a command");
930
+ }
928
931
  if (head.k !== "func") {
929
932
  if (rest.length > 0 || piped !== undefined) {
930
- throw new RenderError("can't give an argument to a non-function");
933
+ throw new RenderError("cannot give an argument to a non-function");
931
934
  }
932
935
  return this.arg(head, dot);
933
936
  }
@@ -1021,7 +1024,7 @@ class Execution {
1021
1024
  .sort()
1022
1025
  .map((key) => [key, value[key] ?? null]);
1023
1026
  } else {
1024
- throw new RenderError(`range can't iterate over a ${kindOf(value)}`);
1027
+ throw new RenderError(`range cannot iterate over a ${kindOf(value)}`);
1025
1028
  }
1026
1029
  if (entries.length === 0) {
1027
1030
  this.run(node.otherwise, dot);
package/src/resolve.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  type TargetKind,
22
22
  type TemplateDelimiters,
23
23
  } from "./config.ts";
24
+ import { CONVENTION_ROWS } from "./convention-rows.ts";
24
25
  import { FIXED_SEVERITY_CODES, LIVE_CODES } from "./kinds.ts";
25
26
  import {
26
27
  checkDocument,
@@ -105,6 +106,8 @@ interface Doc {
105
106
  readonly providers: readonly Provider[] | undefined;
106
107
  readonly testFiles: readonly string[] | undefined;
107
108
  readonly entryFiles: readonly string[] | undefined;
109
+ readonly componentExtensions: readonly string[] | undefined;
110
+ readonly disabledConventions: readonly string[] | undefined;
108
111
  readonly injectionRegistrations: readonly DeclarationEntry[] | undefined;
109
112
  readonly lifecycleContracts: readonly LifecycleContract[] | undefined;
110
113
  readonly serializers: readonly DeclarationEntry[] | undefined;
@@ -277,6 +280,44 @@ function readStrings<T extends string>(
277
280
  return value as readonly T[];
278
281
  }
279
282
 
283
+ /** A component file's extension: a full stop followed by letters and digits. */
284
+ const COMPONENT_EXTENSION = /^\.[A-Za-z0-9]+$/u;
285
+
286
+ function readComponentExtensions(
287
+ ts: Record<string, unknown> | undefined,
288
+ label: string,
289
+ ): readonly string[] | undefined {
290
+ const extensions = readStrings(ts, "component_extensions", "ts.component_extensions", label, 0);
291
+ const wrong = extensions?.find((one) => !COMPONENT_EXTENSION.test(one));
292
+ if (wrong !== undefined) {
293
+ throw malformed(
294
+ label,
295
+ "ts.component_extensions",
296
+ `${JSON.stringify(wrong)} is not a full stop followed by letters and digits`,
297
+ );
298
+ }
299
+ return extensions;
300
+ }
301
+
302
+ /** The names of the convention rows this analyzer's table carries. */
303
+ const CONVENTION_NAMES: ReadonlySet<string> = new Set(CONVENTION_ROWS.map((row) => row.name));
304
+
305
+ function readDisabledConventions(
306
+ ts: Record<string, unknown> | undefined,
307
+ label: string,
308
+ ): readonly string[] | undefined {
309
+ const names = readStrings(ts, "disabled_conventions", "ts.disabled_conventions", label, 0);
310
+ const unknown = names?.find((one) => !CONVENTION_NAMES.has(one));
311
+ if (unknown !== undefined) {
312
+ throw malformed(
313
+ label,
314
+ "ts.disabled_conventions",
315
+ `${JSON.stringify(unknown)} names no convention row this analyzer carries`,
316
+ );
317
+ }
318
+ return names;
319
+ }
320
+
280
321
  function requireMember(value: string | undefined, path: string, label: string): string {
281
322
  if (value === undefined || value === "") {
282
323
  throw malformed(label, path, "is required and names nothing");
@@ -792,6 +833,8 @@ function readDoc(value: unknown, label: string): Doc {
792
833
  providers: readProviders(providers, label),
793
834
  testFiles: readStrings(ts, "test_files", "ts.test_files", label, 1),
794
835
  entryFiles: readStrings(ts, "entry_files", "ts.entry_files", label, 0),
836
+ componentExtensions: readComponentExtensions(ts, label),
837
+ disabledConventions: readDisabledConventions(ts, label),
795
838
  injectionRegistrations: readDeclarations(
796
839
  ts,
797
840
  "injection_registrations",
@@ -986,13 +1029,13 @@ export function resolve(inputs: Inputs): { config: Config; provenance: Provenanc
986
1029
  }
987
1030
 
988
1031
  /**
989
- * The build matrix the configuration documents resolve to, every document read and
990
- * refused as {@link resolve} refuses it, and nothing else required of them: which
991
- * projects a run analyzes depends on the matrix alone, so a question about the
992
- * projects asks no target kind.
1032
+ * The configuration the documents resolve to, every document read and refused as
1033
+ * {@link resolve} refuses it, with no target kind required of them: which projects a
1034
+ * run analyzes does not depend on the kind, so a question about the projects asks
1035
+ * none. The kind is empty where no document names one.
993
1036
  */
994
- export function resolveMatrix(inputs: Inputs): readonly BuildConfiguration[] {
995
- return settleAll(inputs).config.analysis.configurations;
1037
+ export function resolveUnkinded(inputs: Inputs): Config {
1038
+ return settleAll(inputs).config;
996
1039
  }
997
1040
 
998
1041
  /**
@@ -1076,6 +1119,16 @@ function settleAll(inputs: Inputs): { config: Config; provenance: Map<string, Or
1076
1119
  ts: {
1077
1120
  testFiles: at("ts.test_files", defaults.ts.testFiles, (doc) => doc.testFiles),
1078
1121
  entryFiles: at("ts.entry_files", defaults.ts.entryFiles, (doc) => doc.entryFiles),
1122
+ componentExtensions: at(
1123
+ "ts.component_extensions",
1124
+ defaults.ts.componentExtensions,
1125
+ (doc) => doc.componentExtensions,
1126
+ ),
1127
+ disabledConventions: at(
1128
+ "ts.disabled_conventions",
1129
+ defaults.ts.disabledConventions,
1130
+ (doc) => doc.disabledConventions,
1131
+ ),
1079
1132
  injectionRegistrations: at(
1080
1133
  "ts.injection_registrations",
1081
1134
  defaults.ts.injectionRegistrations,
package/src/roots.ts CHANGED
@@ -10,29 +10,43 @@
10
10
 
11
11
  import type { SourceFile } from "@typescript/native/unstable/ast";
12
12
  import { aliasChains } from "./alias-chain.ts";
13
+ import { conventionEntries, type Conventions } from "./conventions.ts";
13
14
  import { entryPoints, type EntryRule } from "./entry-points.ts";
14
15
  import { globExpression } from "./glob.ts";
16
+ import type { Host } from "./host.ts";
15
17
  import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
16
18
  import type { Manifest, ManifestEntry } from "./manifest.ts";
17
19
  import { byPosition } from "./position.ts";
18
20
  import { UNANSWERED } from "./query.ts";
19
21
  import type { ProjectView } from "./session.ts";
20
22
  import { sourceFilesOf } from "./source-files.ts";
23
+ import { typeQueryAliases } from "./type-query-alias.ts";
21
24
 
22
25
  /** Why one declaration is a root. */
23
26
  export type RootKind =
24
27
  /** A file `ts.entry_files` names, and what it exports. */
25
28
  | "entry-file"
29
+ /** A file an applied convention row's globs match, and what it exports. */
30
+ | "convention"
26
31
  /** A file the manifest names as an importable entry point, and what it exports outside the published API. */
27
32
  | "manifest-entry"
28
33
  /** A file the manifest names as a command, and what it exports outside the published API. */
29
34
  | "manifest-binary"
35
+ /** A file a token of a manifest's script names, and what it exports. */
36
+ | "script"
37
+ /**
38
+ * A global a declaration file declares as an alias of a module's export, and the
39
+ * module declarations around it: a use of the global is a use of the export.
40
+ */
41
+ | "type-query-alias"
30
42
  /** A declaration a consumer of a library target can name. */
31
43
  | "published-api"
32
44
  /** A declaration an exact reference from the configuration names. */
33
45
  | "configured"
34
46
  /** A declaration a pattern from the configuration names. */
35
47
  | "pattern"
48
+ /** A declaration of a function or statement a type error skipped, and what it may have selected. */
49
+ | "type-error"
36
50
  /** A file a tool or the platform enters by its own convention, and what it exports where the tool reads it. */
37
51
  | EntryRule;
38
52
 
@@ -81,18 +95,34 @@ export interface Roots {
81
95
 
82
96
  /** What decides the roots beside the declarations themselves. */
83
97
  export interface RootOptions {
98
+ /** The platform the configuration files beside the program are read from. */
99
+ readonly host: Host;
84
100
  /** The target's own manifest. */
85
101
  readonly manifest: Manifest;
102
+ /**
103
+ * The entry points the other packages of the target's workspace name, each already
104
+ * marked published or not by its own manifest's rule.
105
+ */
106
+ readonly workspaceEntries?: readonly ManifestEntry[] | undefined;
86
107
  /** `roots.patterns` from the resolved configuration. */
87
108
  readonly patterns: readonly string[];
88
109
  /** `ts.entry_files` from the resolved configuration. */
89
110
  readonly entryFiles: readonly string[];
111
+ /** The convention rows the run applied. */
112
+ readonly conventions?: Conventions | undefined;
90
113
  /** `ts.test_files` from the resolved configuration. */
91
114
  readonly testFiles: readonly string[];
92
115
  /** Whether the target is a library, so a consumer outside it reaches its published API. */
93
116
  readonly publishedAPI: boolean;
94
117
  }
95
118
 
119
+ /** The kind of root each role of a manifest entry makes. */
120
+ const ENTRY_KINDS = {
121
+ import: "manifest-entry",
122
+ run: "manifest-binary",
123
+ script: "script",
124
+ } as const satisfies Record<ManifestEntry["role"], RootKind>;
125
+
96
126
  /** Whether a configured string carries either special character. */
97
127
  function isPattern(text: string): boolean {
98
128
  return text.includes("*") || text.includes("?");
@@ -204,19 +234,24 @@ export function roots<Brand>(
204
234
  };
205
235
 
206
236
  const published: SourceFile[] = [];
207
- const publishes = (entry: ManifestEntry): boolean =>
208
- options.publishedAPI &&
209
- entry.published &&
210
- (options.manifest.declaresExports ? entry.member.startsWith("exports") : true);
211
- for (const entry of options.manifest.entries) {
212
- if (publishes(entry)) {
237
+ const entries = [
238
+ ...options.manifest.entries.map((entry) => ({
239
+ entry,
240
+ publishes:
241
+ entry.published &&
242
+ (options.manifest.declaresExports ? entry.member.startsWith("exports") : true),
243
+ })),
244
+ ...(options.workspaceEntries ?? []).map((entry) => ({ entry, publishes: entry.published })),
245
+ ];
246
+ for (const { entry, publishes } of entries) {
247
+ if (options.publishedAPI && publishes) {
213
248
  published.push(...files.named(entry.path));
214
249
  }
215
250
  }
216
251
  const publishedNames = new Set(published.map((file) => file.fileName));
217
- for (const entry of options.manifest.entries) {
252
+ for (const { entry } of entries) {
218
253
  for (const file of files.named(entry.path)) {
219
- const kind = entry.role === "run" ? "manifest-binary" : "manifest-entry";
254
+ const kind = ENTRY_KINDS[entry.role];
220
255
  enter(file, kind, entry.member, !publishedNames.has(file.fileName));
221
256
  }
222
257
  }
@@ -228,9 +263,25 @@ export function roots<Brand>(
228
263
  }
229
264
  }
230
265
  }
231
- for (const point of entryPoints(project, files, options.testFiles)) {
266
+ for (const { file, row } of options.conventions === undefined
267
+ ? []
268
+ : conventionEntries(options.conventions, files)) {
269
+ enter(file, "convention", row, true);
270
+ }
271
+ for (const point of entryPoints(project, files, options.testFiles, options.host, targetRoot)) {
232
272
  enter(point.file, point.rule, point.source, point.exports);
233
273
  }
274
+ for (const file of files.byPath.values()) {
275
+ for (const alias of typeQueryAliases(file)) {
276
+ const source = alias.type.getText().replace(/\s+/gu, " ");
277
+ for (const node of [alias.declaration, ...alias.containers]) {
278
+ const id = declared.get(nodeKey(file, node));
279
+ if (id !== undefined) {
280
+ add(id, "type-query-alias", source);
281
+ }
282
+ }
283
+ }
284
+ }
234
285
 
235
286
  const { tables: exportsOf, answered } = exportTables(project, held, [
236
287
  ...entered.filter((entry) => entry.exports).map((entry) => entry.file),
package/src/run.ts CHANGED
@@ -4,7 +4,8 @@ import { EMITTERS } from "./findings/emitters.ts";
4
4
  import type { Host } from "./host.ts";
5
5
  import { joinPath, resolvePath } from "./paths.ts";
6
6
  import { readScope, ScopeError, scopeForDir, type Scope } from "./scope.ts";
7
- import { openEngine, type Engine } from "./session.ts";
7
+ import { SetupError, setupLine } from "./setup-failure.ts";
8
+ import { openEngine, type Engine, type EngineOptions } from "./session.ts";
8
9
  import { printConfigVerb } from "./verbs/print-config.ts";
9
10
  import { printProjectsVerb } from "./verbs/print-projects.ts";
10
11
  import { printRetainedVerb } from "./verbs/print-retained.ts";
@@ -204,7 +205,7 @@ export function run(
204
205
  out: Writer,
205
206
  err: Writer,
206
207
  host: Host,
207
- openClient: (collectTiming: boolean) => Engine = (collectTiming) => openEngine({ collectTiming }),
208
+ openClient: (options: EngineOptions) => Engine = openEngine,
208
209
  ): number {
209
210
  const editing = sourceEditOption(args);
210
211
  if (editing !== undefined) {
@@ -243,7 +244,7 @@ function invoke(
243
244
  own: VerbOptions,
244
245
  args: readonly string[],
245
246
  streams: { readonly out: Writer; readonly err: Writer; readonly host: Host },
246
- openClient: (collectTiming: boolean) => Engine,
247
+ openClient: (options: EngineOptions) => Engine,
247
248
  ): number {
248
249
  let options: Options | undefined;
249
250
  const read = (): Options => (options ??= readOptions(args, own));
@@ -256,8 +257,11 @@ function invoke(
256
257
  scope: () => scopeOf(streams.host, read()),
257
258
  option: (name) => read().named.get(name),
258
259
  repeated: (name) => read().repeated.get(name) ?? [],
259
- openClient: (collectTiming) => {
260
- opened = openClient(collectTiming);
260
+ openClient: (config) => {
261
+ opened = openClient({
262
+ collectTiming: false,
263
+ components: { extensions: config.ts.componentExtensions, host: streams.host },
264
+ });
261
265
  return opened;
262
266
  },
263
267
  emitters: EMITTERS,
@@ -282,6 +286,12 @@ function refuse(error: unknown, err: Writer): number {
282
286
  err.write(`deadset-ts: ${error.message}\n`);
283
287
  return EXIT_FAILURE;
284
288
  }
289
+ if (error instanceof SetupError) {
290
+ for (const failure of error.failures) {
291
+ err.write(`${setupLine(failure)}\n`);
292
+ }
293
+ return EXIT_FAILURE;
294
+ }
285
295
  if (error instanceof ScopeError) {
286
296
  err.write(`deadset-ts: ${error.message}\n`);
287
297
  return EXIT_FAILURE;
package/src/schema.ts CHANGED
@@ -102,6 +102,8 @@ export const SCHEMA_ROOT: KeyNode = {
102
102
  members: {
103
103
  test_files: leaf,
104
104
  entry_files: leaf,
105
+ component_extensions: leaf,
106
+ disabled_conventions: leaf,
105
107
  injection_registrations: declarations,
106
108
  lifecycle_contracts: {
107
109
  kind: "list",
@@ -261,9 +263,9 @@ export function unimplementedKey(label: string, path: string): ConfigError {
261
263
  const nearest = nearestKey(path);
262
264
  let hint = "";
263
265
  if (nearest === path) {
264
- hint = `; ${JSON.stringify(path)} is one setting written as nested objects, not as one key`;
266
+ hint = `. ${JSON.stringify(path)} is one setting written as nested objects, not as one key`;
265
267
  } else if (nearest !== "") {
266
- hint = `; the nearest implemented key is ${JSON.stringify(nearest)}`;
268
+ hint = `. The nearest implemented key is ${JSON.stringify(nearest)}`;
267
269
  }
268
270
  return new ConfigError(
269
271
  "unimplemented-key",