workspace-conformance 2.0.1 → 2.0.2

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/dist/index.cjs CHANGED
@@ -4142,7 +4142,9 @@ var ExhaustivenessError = class extends Error {
4142
4142
  }
4143
4143
  };
4144
4144
  /**
4145
- * Compiler options for the in-memory programs: strict, ESM, no libraries beyond the language's own, and no ambient types. Only what a self-contained snippet needs.
4145
+ * Compiler options for the in-memory programs: strict, ESM, the target's default library (for ES2022 that is `lib.es2022.full.d.ts`, which includes the DOM), and no ambient types. Only what a self-contained snippet needs.
4146
+ *
4147
+ * `skipDefaultLibCheck` leaves TypeScript's own library files unchecked, so diagnostics cover the snippet and not the library's declarations, which otherwise dominate the cost of every program. Errors in the snippet's own files, declaration files included, are still reported.
4146
4148
  */
4147
4149
  const SNIPPET_COMPILER_OPTIONS = {
4148
4150
  strict: true,
@@ -4150,7 +4152,8 @@ const SNIPPET_COMPILER_OPTIONS = {
4150
4152
  target: ts_morph.ScriptTarget.ES2022,
4151
4153
  module: ts_morph.ModuleKind.ESNext,
4152
4154
  moduleResolution: ts_morph.ModuleResolutionKind.Bundler,
4153
- types: []
4155
+ types: [],
4156
+ skipDefaultLibCheck: true
4154
4157
  };
4155
4158
  function inMemoryProject(files, options) {
4156
4159
  const project = new ts_morph.Project({
@@ -4164,15 +4167,41 @@ function inMemoryProject(files, options) {
4164
4167
  return project;
4165
4168
  }
4166
4169
  /**
4170
+ * One project per distinct set of compiler options, kept for the life of the module so that the default library is parsed and bound once instead of once per program. {@link typeErrors} adds its files to the project and removes them before it returns, so a call never sees another call's files.
4171
+ */
4172
+ const projectsByOptions = /* @__PURE__ */ new Map();
4173
+ function sharedProject(options) {
4174
+ const compilerOptions = {
4175
+ ...SNIPPET_COMPILER_OPTIONS,
4176
+ ...options
4177
+ };
4178
+ const key = JSON.stringify(compilerOptions);
4179
+ const existing = projectsByOptions.get(key);
4180
+ if (existing !== void 0) return existing;
4181
+ const project = new ts_morph.Project({
4182
+ useInMemoryFileSystem: true,
4183
+ compilerOptions
4184
+ });
4185
+ projectsByOptions.set(key, project);
4186
+ return project;
4187
+ }
4188
+ /**
4167
4189
  * Compile the files in memory and return every error the type checker reports. Names are paths in the in-memory file system, so one file can import another with a relative path.
4168
4190
  */
4169
4191
  function typeErrors(files, options = {}) {
4170
- return inMemoryProject(files, options).getPreEmitDiagnostics().map((diagnostic) => ({
4171
- file: diagnostic.getSourceFile()?.getBaseName() ?? "",
4172
- line: diagnostic.getLineNumber() ?? 0,
4173
- code: diagnostic.getCode(),
4174
- message: ts_morph.ts.flattenDiagnosticMessageText(diagnostic.compilerObject.messageText, "\n")
4175
- }));
4192
+ const project = sharedProject(options);
4193
+ const added = [];
4194
+ try {
4195
+ for (const [name, source] of Object.entries(files)) added.push(project.createSourceFile(name, source));
4196
+ return project.getPreEmitDiagnostics().map((diagnostic) => ({
4197
+ file: diagnostic.getSourceFile()?.getBaseName() ?? "",
4198
+ line: diagnostic.getLineNumber() ?? 0,
4199
+ code: diagnostic.getCode(),
4200
+ message: ts_morph.ts.flattenDiagnosticMessageText(diagnostic.compilerObject.messageText, "\n")
4201
+ }));
4202
+ } finally {
4203
+ for (const sourceFile of added) project.removeSourceFile(sourceFile);
4204
+ }
4176
4205
  }
4177
4206
  function handlerMap(source, map) {
4178
4207
  let initializer = source.getVariableDeclaration(map)?.getInitializer();
package/dist/index.js CHANGED
@@ -4141,7 +4141,9 @@ var ExhaustivenessError = class extends Error {
4141
4141
  }
4142
4142
  };
4143
4143
  /**
4144
- * Compiler options for the in-memory programs: strict, ESM, no libraries beyond the language's own, and no ambient types. Only what a self-contained snippet needs.
4144
+ * Compiler options for the in-memory programs: strict, ESM, the target's default library (for ES2022 that is `lib.es2022.full.d.ts`, which includes the DOM), and no ambient types. Only what a self-contained snippet needs.
4145
+ *
4146
+ * `skipDefaultLibCheck` leaves TypeScript's own library files unchecked, so diagnostics cover the snippet and not the library's declarations, which otherwise dominate the cost of every program. Errors in the snippet's own files, declaration files included, are still reported.
4145
4147
  */
4146
4148
  const SNIPPET_COMPILER_OPTIONS = {
4147
4149
  strict: true,
@@ -4149,7 +4151,8 @@ const SNIPPET_COMPILER_OPTIONS = {
4149
4151
  target: ScriptTarget.ES2022,
4150
4152
  module: ModuleKind.ESNext,
4151
4153
  moduleResolution: ModuleResolutionKind.Bundler,
4152
- types: []
4154
+ types: [],
4155
+ skipDefaultLibCheck: true
4153
4156
  };
4154
4157
  function inMemoryProject(files, options) {
4155
4158
  const project = new Project({
@@ -4163,15 +4166,41 @@ function inMemoryProject(files, options) {
4163
4166
  return project;
4164
4167
  }
4165
4168
  /**
4169
+ * One project per distinct set of compiler options, kept for the life of the module so that the default library is parsed and bound once instead of once per program. {@link typeErrors} adds its files to the project and removes them before it returns, so a call never sees another call's files.
4170
+ */
4171
+ const projectsByOptions = /* @__PURE__ */ new Map();
4172
+ function sharedProject(options) {
4173
+ const compilerOptions = {
4174
+ ...SNIPPET_COMPILER_OPTIONS,
4175
+ ...options
4176
+ };
4177
+ const key = JSON.stringify(compilerOptions);
4178
+ const existing = projectsByOptions.get(key);
4179
+ if (existing !== void 0) return existing;
4180
+ const project = new Project({
4181
+ useInMemoryFileSystem: true,
4182
+ compilerOptions
4183
+ });
4184
+ projectsByOptions.set(key, project);
4185
+ return project;
4186
+ }
4187
+ /**
4166
4188
  * Compile the files in memory and return every error the type checker reports. Names are paths in the in-memory file system, so one file can import another with a relative path.
4167
4189
  */
4168
4190
  function typeErrors(files, options = {}) {
4169
- return inMemoryProject(files, options).getPreEmitDiagnostics().map((diagnostic) => ({
4170
- file: diagnostic.getSourceFile()?.getBaseName() ?? "",
4171
- line: diagnostic.getLineNumber() ?? 0,
4172
- code: diagnostic.getCode(),
4173
- message: ts.flattenDiagnosticMessageText(diagnostic.compilerObject.messageText, "\n")
4174
- }));
4191
+ const project = sharedProject(options);
4192
+ const added = [];
4193
+ try {
4194
+ for (const [name, source] of Object.entries(files)) added.push(project.createSourceFile(name, source));
4195
+ return project.getPreEmitDiagnostics().map((diagnostic) => ({
4196
+ file: diagnostic.getSourceFile()?.getBaseName() ?? "",
4197
+ line: diagnostic.getLineNumber() ?? 0,
4198
+ code: diagnostic.getCode(),
4199
+ message: ts.flattenDiagnosticMessageText(diagnostic.compilerObject.messageText, "\n")
4200
+ }));
4201
+ } finally {
4202
+ for (const sourceFile of added) project.removeSourceFile(sourceFile);
4203
+ }
4175
4204
  }
4176
4205
  function handlerMap(source, map) {
4177
4206
  let initializer = source.getVariableDeclaration(map)?.getInitializer();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "workspace-conformance",
3
3
  "description": "Conformance checks for a pnpm workspace that ESLint cannot express: type-graph, import-graph and cross-file checks driven by the shared layout section of @exadev/config",
4
- "version": "2.0.1",
4
+ "version": "2.0.2",
5
5
  "bin": {
6
6
  "workspace-conformance": "./dist/cli.js"
7
7
  },