@tsslint/core 1.3.5 → 1.4.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.
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from './lib/build';
2
2
  export * from './lib/watch';
3
- import type { Config, ProjectContext } from '@tsslint/types';
3
+ import type { Config, ProjectContext, Rules } from '@tsslint/types';
4
4
  import type * as ts from 'typescript';
5
5
  export type FileLintCache = [
6
6
  mtime: number,
@@ -13,9 +13,17 @@ export type Linter = ReturnType<typeof createLinter>;
13
13
  export declare function createLinter(ctx: ProjectContext, config: Config | Config[], mode: 'cli' | 'typescript-plugin', logger?: typeof import('@clack/prompts')): {
14
14
  lint(fileName: string, cache?: FileLintCache): ts.DiagnosticWithLocation[];
15
15
  hasCodeFixes(fileName: string): boolean;
16
- getCodeFixes(fileName: string, start: number, end: number, diagnostics?: ts.Diagnostic[], cache?: FileLintCache): ts.CodeFixAction[];
16
+ getCodeFixes(fileName: string, start: number, end: number, diagnostics?: ts.Diagnostic[], minimatchCache?: FileLintCache[4]): ts.CodeFixAction[];
17
17
  getRefactors(fileName: string, start: number, end: number): ts.RefactorActionInfo[];
18
18
  getRefactorEdits(fileName: string, actionName: string): ts.FileTextChanges[] | undefined;
19
+ getRules: (fileName: string, minimatchCache: undefined | FileLintCache[4]) => Rules;
20
+ getConfigs: (fileName: string, minimatchCache: undefined | FileLintCache[4]) => {
21
+ include: string[];
22
+ exclude: string[];
23
+ rules: Rules;
24
+ plugins: import("@tsslint/types").PluginInstance[];
25
+ }[];
19
26
  };
27
+ export declare function createRelatedInformation(ts: typeof import('typescript'), err: Error, stackOffset: number): ts.DiagnosticRelatedInformation | undefined;
20
28
  export declare function combineCodeFixes(fileName: string, fixes: ts.CodeFixAction[]): ts.TextChange[];
21
29
  export declare function applyTextChanges(baseSnapshot: ts.IScriptSnapshot, textChanges: ts.TextChange[]): ts.IScriptSnapshot;
package/index.js CHANGED
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.createLinter = createLinter;
18
+ exports.createRelatedInformation = createRelatedInformation;
18
19
  exports.combineCodeFixes = combineCodeFixes;
19
20
  exports.applyTextChanges = applyTextChanges;
20
21
  __exportStar(require("./lib/build"), exports);
@@ -22,34 +23,18 @@ __exportStar(require("./lib/watch"), exports);
22
23
  const ErrorStackParser = require("error-stack-parser");
23
24
  const path = require("path");
24
25
  const minimatch = require("minimatch");
26
+ const typeAwareModeChange = new Error('enable type-aware mode');
25
27
  function createLinter(ctx, config, mode,
26
28
  // @ts-expect-error
27
29
  logger) {
28
- if (mode === 'typescript-plugin') {
29
- require('source-map-support').install({
30
- retrieveFile(path) {
31
- if (!path.endsWith('.js.map')) {
32
- return;
33
- }
34
- path = path.replace(/\\/g, '/');
35
- // monkey-fix, refs: https://github.com/typescript-eslint/typescript-eslint/issues/9352
36
- if (path.includes('/@typescript-eslint/eslint-plugin/dist/rules/')
37
- || path.includes('/eslint-plugin-expect-type/lib/rules/')) {
38
- return JSON.stringify({
39
- version: 3,
40
- sources: [],
41
- sourcesContent: [],
42
- mappings: '',
43
- names: [],
44
- });
45
- }
46
- },
47
- });
48
- }
49
- let languageServiceUsage = mode === 'typescript-plugin' ? 1 : 0;
30
+ let languageServiceUsage = 0;
50
31
  const ts = ctx.typescript;
51
32
  const languageService = new Proxy(ctx.languageService, {
52
33
  get(target, key, receiver) {
34
+ if (!languageServiceUsage) {
35
+ languageServiceUsage++;
36
+ throw typeAwareModeChange;
37
+ }
53
38
  languageServiceUsage++;
54
39
  return Reflect.get(target, key, receiver);
55
40
  },
@@ -58,7 +43,6 @@ logger) {
58
43
  const fileConfigs = new Map();
59
44
  const fileFixes = new Map();
60
45
  const fileRefactors = new Map();
61
- const sourceFiles = new Map();
62
46
  const snapshot2SourceFile = new WeakMap();
63
47
  const basePath = path.dirname(ctx.configFile);
64
48
  const configs = (Array.isArray(config) ? config : [config])
@@ -69,12 +53,10 @@ logger) {
69
53
  plugins: (config.plugins ?? []).map(plugin => plugin(ctx)),
70
54
  }));
71
55
  const normalizedPath = new Map();
72
- const debug = (Array.isArray(config) ? config : [config]).some(config => config.debug);
73
56
  return {
74
57
  lint(fileName, cache) {
75
58
  let cacheableDiagnostics = [];
76
59
  let uncacheableDiagnostics = [];
77
- let debugInfo;
78
60
  let currentRuleId;
79
61
  let currentIssues = 0;
80
62
  let currentFixes = 0;
@@ -82,26 +64,7 @@ logger) {
82
64
  let currentRuleLanguageServiceUsage = 0;
83
65
  let sourceFile;
84
66
  let hasUncacheResult = false;
85
- if (debug) {
86
- debugInfo = {
87
- category: ts.DiagnosticCategory.Message,
88
- code: 'debug',
89
- messageText: '- Config: ' + ctx.configFile + '\n',
90
- file: getSourceFile(fileName),
91
- start: 0,
92
- length: 0,
93
- source: 'tsslint',
94
- relatedInformation: [],
95
- };
96
- uncacheableDiagnostics.push(debugInfo);
97
- }
98
- const rules = getFileRules(fileName, cache);
99
- if (!rules || !Object.keys(rules).length) {
100
- if (debugInfo) {
101
- debugInfo.messageText += '- Rules: ❌ (no rules)\n';
102
- }
103
- }
104
- const prevLanguageServiceUsage = languageServiceUsage;
67
+ const rules = getFileRules(fileName, cache?.[4]);
105
68
  const rulesContext = {
106
69
  ...ctx,
107
70
  languageService,
@@ -123,14 +86,10 @@ logger) {
123
86
  }
124
87
  fixes.clear();
125
88
  refactors.length = 0;
126
- if (debugInfo) {
127
- debugInfo.messageText += '- Rules:\n';
128
- }
129
- runRules(rules);
130
- if (!!prevLanguageServiceUsage !== !!languageServiceUsage) {
89
+ if (!runRules(rules)) {
131
90
  return this.lint(fileName, cache);
132
91
  }
133
- const configs = getFileConfigs(fileName, cache);
92
+ const configs = getFileConfigs(fileName, cache?.[4]);
134
93
  if (cache) {
135
94
  for (const [ruleId, fixes] of cachedRules) {
136
95
  cache[1][ruleId] = fixes;
@@ -193,7 +152,9 @@ logger) {
193
152
  break;
194
153
  }
195
154
  if (typeof rule === 'object') {
196
- runRules(rule, [...paths, path]);
155
+ if (!runRules(rule, [...paths, path])) {
156
+ return false;
157
+ }
197
158
  continue;
198
159
  }
199
160
  currentRuleLanguageServiceUsage = languageServiceUsage;
@@ -205,56 +166,38 @@ logger) {
205
166
  continue;
206
167
  }
207
168
  hasUncacheResult = true;
208
- const start = Date.now();
209
169
  try {
210
170
  rule(rulesContext);
211
- if (debugInfo) {
212
- const time = Date.now() - start;
213
- debugInfo.messageText += ` - ${currentRuleId}`;
214
- const details = [];
215
- if (currentIssues) {
216
- details.push(`${currentIssues} issues`);
217
- }
218
- if (currentFixes) {
219
- details.push(`${currentFixes} fixes`);
220
- }
221
- if (currentRefactors) {
222
- details.push(`${currentRefactors} refactors`);
223
- }
224
- if (time) {
225
- details.push(`${time}ms`);
226
- }
227
- if (details.length) {
228
- debugInfo.messageText += ` (${details.join(', ')})`;
229
- }
230
- debugInfo.messageText += '\n';
231
- }
232
171
  }
233
172
  catch (err) {
234
- console.error(err);
235
- if (debugInfo) {
236
- debugInfo.messageText += ` - ${currentRuleId} (❌ ${err && typeof err === 'object' && 'stack' in err ? err.stack : String(err)}})\n`;
173
+ if (err === typeAwareModeChange) {
174
+ // logger?.log.message(`Type-aware mode enabled by ${currentRuleId} rule.`);
175
+ return false;
176
+ }
177
+ else if (err instanceof Error) {
178
+ report(ts.DiagnosticCategory.Error, err.stack ?? err.message, 0, 0, 0, err);
179
+ }
180
+ else {
181
+ report(ts.DiagnosticCategory.Error, String(err), 0, 0, false);
237
182
  }
238
- }
239
- if (debug && !!currentRuleLanguageServiceUsage !== !!languageServiceUsage) {
240
- logger?.log.message(`Type-aware mode enabled by ${currentRuleId} rule.`);
241
183
  }
242
184
  if (cache && currentRuleLanguageServiceUsage === languageServiceUsage) {
243
185
  cachedRules.set(currentRuleId, currentFixes);
244
186
  }
245
187
  }
188
+ return true;
246
189
  }
247
190
  ;
248
- function reportError(message, start, end, traceOffset = 0) {
249
- return report(ts.DiagnosticCategory.Error, message, start, end, traceOffset);
191
+ function reportError(message, start, end, stackOffset) {
192
+ return report(ts.DiagnosticCategory.Error, message, start, end, stackOffset);
250
193
  }
251
- function reportWarning(message, start, end, traceOffset = 0) {
252
- return report(ts.DiagnosticCategory.Warning, message, start, end, traceOffset);
194
+ function reportWarning(message, start, end, stackOffset) {
195
+ return report(ts.DiagnosticCategory.Warning, message, start, end, stackOffset);
253
196
  }
254
- function reportSuggestion(message, start, end, traceOffset = 0) {
255
- return report(ts.DiagnosticCategory.Suggestion, message, start, end, traceOffset);
197
+ function reportSuggestion(message, start, end, stackOffset) {
198
+ return report(ts.DiagnosticCategory.Suggestion, message, start, end, stackOffset);
256
199
  }
257
- function report(category, message, start, end, traceOffset) {
200
+ function report(category, message, start, end, stackOffset = 2, err) {
258
201
  const error = {
259
202
  category,
260
203
  code: currentRuleId,
@@ -276,15 +219,11 @@ logger) {
276
219
  })),
277
220
  });
278
221
  }
279
- if (mode === 'typescript-plugin') {
280
- const stacks = traceOffset === false
281
- ? []
282
- : ErrorStackParser.parse(new Error());
283
- if (typeof traceOffset === 'number') {
284
- const baseOffset = 2 + traceOffset;
285
- if (stacks.length > baseOffset) {
286
- pushRelatedInformation(error, stacks[baseOffset]);
287
- }
222
+ if (mode === 'typescript-plugin' && typeof stackOffset === 'number') {
223
+ err ??= new Error();
224
+ const relatedInfo = createRelatedInformation(ts, err, stackOffset);
225
+ if (relatedInfo) {
226
+ error.relatedInformation.push(relatedInfo);
288
227
  }
289
228
  }
290
229
  fixes.set(error, []);
@@ -315,43 +254,6 @@ logger) {
315
254
  },
316
255
  };
317
256
  }
318
- function pushRelatedInformation(error, stack) {
319
- if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
320
- let fileName = stack.fileName
321
- .replace(/\\/g, '/')
322
- .split('?time=')[0];
323
- if (fileName.startsWith('file://')) {
324
- fileName = fileName.substring('file://'.length);
325
- }
326
- if (fileName.includes('http-url:')) {
327
- fileName = fileName.split('http-url:')[1];
328
- }
329
- if (!sourceFiles.has(fileName)) {
330
- const text = ctx.languageServiceHost.readFile(fileName);
331
- sourceFiles.set(fileName, [
332
- text !== undefined,
333
- ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true)
334
- ]);
335
- }
336
- const [exist, stackFile] = sourceFiles.get(fileName);
337
- let pos = 0;
338
- if (exist) {
339
- try {
340
- pos = stackFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
341
- }
342
- catch { }
343
- }
344
- error.relatedInformation ??= [];
345
- error.relatedInformation.push({
346
- category: ts.DiagnosticCategory.Message,
347
- code: 0,
348
- file: stackFile,
349
- start: pos,
350
- length: 0,
351
- messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
352
- });
353
- }
354
- }
355
257
  },
356
258
  hasCodeFixes(fileName) {
357
259
  const fixesMap = getFileFixes(fileName);
@@ -362,8 +264,8 @@ logger) {
362
264
  }
363
265
  return false;
364
266
  },
365
- getCodeFixes(fileName, start, end, diagnostics, cache) {
366
- const configs = getFileConfigs(fileName, cache);
267
+ getCodeFixes(fileName, start, end, diagnostics, minimatchCache) {
268
+ const configs = getFileConfigs(fileName, minimatchCache);
367
269
  const fixesMap = getFileFixes(fileName);
368
270
  const result = [];
369
271
  for (const [diagnostic, actions] of fixesMap) {
@@ -428,6 +330,8 @@ logger) {
428
330
  }
429
331
  }
430
332
  },
333
+ getRules: getFileRules,
334
+ getConfigs: getFileConfigs,
431
335
  };
432
336
  function getSourceFile(fileName) {
433
337
  if (languageServiceUsage) {
@@ -446,11 +350,11 @@ logger) {
446
350
  }
447
351
  throw new Error('No source file');
448
352
  }
449
- function getFileRules(fileName, cache) {
353
+ function getFileRules(fileName, minimatchCache) {
450
354
  let result = fileRules.get(fileName);
451
355
  if (!result) {
452
356
  result = {};
453
- const configs = getFileConfigs(fileName, cache);
357
+ const configs = getFileConfigs(fileName, minimatchCache);
454
358
  for (const { rules } of configs) {
455
359
  result = {
456
360
  ...result,
@@ -468,7 +372,7 @@ logger) {
468
372
  }
469
373
  return result;
470
374
  }
471
- function getFileConfigs(fileName, cache) {
375
+ function getFileConfigs(fileName, minimatchCache) {
472
376
  let result = fileConfigs.get(fileName);
473
377
  if (!result) {
474
378
  result = configs.filter(({ include, exclude }) => {
@@ -482,9 +386,9 @@ logger) {
482
386
  });
483
387
  fileConfigs.set(fileName, result);
484
388
  function _minimatch(pattern) {
485
- if (cache) {
486
- if (pattern in cache[4]) {
487
- return cache[4][pattern];
389
+ if (minimatchCache) {
390
+ if (pattern in minimatchCache) {
391
+ return minimatchCache[pattern];
488
392
  }
489
393
  }
490
394
  let normalized = normalizedPath.get(pattern);
@@ -493,8 +397,8 @@ logger) {
493
397
  normalizedPath.set(pattern, normalized);
494
398
  }
495
399
  const res = minimatch.minimatch(fileName, normalized);
496
- if (cache) {
497
- cache[4][pattern] = res;
400
+ if (minimatchCache) {
401
+ minimatchCache[pattern] = res;
498
402
  }
499
403
  return res;
500
404
  }
@@ -514,6 +418,49 @@ logger) {
514
418
  return fileRefactors.get(fileName);
515
419
  }
516
420
  }
421
+ const fsFiles = new Map();
422
+ function createRelatedInformation(ts, err, stackOffset) {
423
+ const stacks = ErrorStackParser.parse(err);
424
+ if (stacks.length <= stackOffset) {
425
+ return;
426
+ }
427
+ const stack = stacks[stackOffset];
428
+ if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
429
+ let fileName = stack.fileName.replace(/\\/g, '/');
430
+ if (fileName.startsWith('file://')) {
431
+ fileName = fileName.substring('file://'.length);
432
+ }
433
+ if (fileName.includes('http-url:')) {
434
+ fileName = fileName.split('http-url:')[1];
435
+ }
436
+ const mtime = ts.sys.getModifiedTime?.(fileName)?.getTime() ?? 0;
437
+ const lastMtime = fsFiles.get(fileName)?.[1];
438
+ if (mtime !== lastMtime) {
439
+ const text = ts.sys.readFile(fileName);
440
+ fsFiles.set(fileName, [
441
+ text !== undefined,
442
+ mtime,
443
+ ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true)
444
+ ]);
445
+ }
446
+ const [exist, _mtime, relatedFile] = fsFiles.get(fileName);
447
+ let pos = 0;
448
+ if (exist) {
449
+ try {
450
+ pos = relatedFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
451
+ }
452
+ catch { }
453
+ }
454
+ return {
455
+ category: ts.DiagnosticCategory.Message,
456
+ code: 0,
457
+ file: relatedFile,
458
+ start: pos,
459
+ length: 0,
460
+ messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
461
+ };
462
+ }
463
+ }
517
464
  function combineCodeFixes(fileName, fixes) {
518
465
  const changes = fixes
519
466
  .map(fix => fix.changes)
@@ -0,0 +1 @@
1
+ export declare function buildConfig(configFilePath: string, createHash?: (path: string) => string, logger?: typeof import('@clack/prompts')): Promise<string>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildConfig = buildConfig;
4
+ const watch_1 = require("./watch");
5
+ function buildConfig(configFilePath, createHash,
6
+ // @ts-expect-error
7
+ logger) {
8
+ return new Promise((resolve, reject) => {
9
+ (0, watch_1.watchConfig)(configFilePath, (config, result) => {
10
+ if (config) {
11
+ resolve(config);
12
+ }
13
+ else {
14
+ reject(result);
15
+ }
16
+ }, false, createHash, logger);
17
+ });
18
+ }
19
+ //# sourceMappingURL=build%20copy.js.map
package/lib/build.d.ts CHANGED
@@ -1,2 +1 @@
1
- import type { Config } from '@tsslint/config';
2
- export declare function buildConfigFile(configFilePath: string, createHash?: (path: string) => string, logger?: typeof import('@clack/prompts')): Promise<Config | Config[]>;
1
+ export declare function buildConfig(configFilePath: string, createHash?: (path: string) => string, spinner?: ReturnType<typeof import('@clack/prompts').spinner>, stopSnipper?: (message: string, code?: number) => void): Promise<string | undefined>;
package/lib/build.js CHANGED
@@ -1,19 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildConfigFile = buildConfigFile;
3
+ exports.buildConfig = buildConfig;
4
4
  const watch_1 = require("./watch");
5
- function buildConfigFile(configFilePath, createHash,
5
+ const _path = require("path");
6
+ function buildConfig(configFilePath, createHash,
6
7
  // @ts-expect-error
7
- logger) {
8
- return new Promise((resolve, reject) => {
9
- (0, watch_1.watchConfigFile)(configFilePath, (config, result) => {
10
- if (config) {
11
- resolve(config);
12
- }
13
- else {
14
- reject(result);
15
- }
16
- }, false, createHash, logger);
8
+ spinner, stopSnipper) {
9
+ const buildStart = Date.now();
10
+ const configFileDisplayPath = _path.relative(process.cwd(), configFilePath);
11
+ spinner?.message('Building ' + configFileDisplayPath);
12
+ return new Promise(async (resolve) => {
13
+ try {
14
+ await (0, watch_1.watchConfig)(configFilePath, builtConfig => {
15
+ if (builtConfig) {
16
+ stopSnipper?.('Built ' + configFileDisplayPath + ' in ' + (Date.now() - buildStart) + 'ms');
17
+ }
18
+ else {
19
+ stopSnipper?.('Failed to build ' + configFileDisplayPath + ' in ' + (Date.now() - buildStart) + 'ms', 1);
20
+ }
21
+ resolve(builtConfig);
22
+ }, false, createHash, spinner, stopSnipper);
23
+ }
24
+ catch (e) {
25
+ stopSnipper?.('Failed to build ' + configFileDisplayPath + ' in ' + (Date.now() - buildStart) + 'ms', 1);
26
+ resolve(undefined);
27
+ }
17
28
  });
18
29
  }
19
30
  //# sourceMappingURL=build.js.map
package/lib/load.js ADDED
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // import type { Config } from '@tsslint/config';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ // import url = require('url');
5
+ // // import ErrorStackParser = require('error-stack-parser');
6
+ // export async function loadConfig(outFile: string): Promise<Config | Config[]> {
7
+ // // try {
8
+ // return (await import(url.pathToFileURL(outFile).toString() + '?time=' + Date.now())).default;
9
+ // // } catch (e: any) {
10
+ // // if (e.stack) {
11
+ // // const stack = ErrorStackParser.parse(e)[0];
12
+ // // if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
13
+ // // let fileName = stack.fileName
14
+ // // .replace(/\\/g, '/')
15
+ // // .split('?time=')[0];
16
+ // // if (fileName.startsWith('file://')) {
17
+ // // fileName = fileName.substring('file://'.length);
18
+ // // }
19
+ // // result.errors.push({
20
+ // // id: 'config-import-error',
21
+ // // text: String(e),
22
+ // // location: {
23
+ // // file: fileName,
24
+ // // line: stack.lineNumber,
25
+ // // column: stack.columnNumber - 1,
26
+ // // lineText: '',
27
+ // // },
28
+ // // } as any);
29
+ // // } else {
30
+ // // result.errors.push({
31
+ // // id: 'config-import-error',
32
+ // // text: String(e),
33
+ // // } as any);
34
+ // // }
35
+ // // } else {
36
+ // // result.errors.push({
37
+ // // id: 'config-import-error',
38
+ // // text: String(e),
39
+ // // } as any);
40
+ // // }
41
+ // // }
42
+ // }
43
+ //# sourceMappingURL=load.js.map
package/lib/watch.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import esbuild = require('esbuild');
2
- import type { Config } from '@tsslint/config';
3
- export declare function watchConfigFile(configFilePath: string, onBuild: (config: Config | Config[] | undefined, result: esbuild.BuildResult) => void, watch?: boolean, createHash?: (path: string) => string, logger?: typeof import('@clack/prompts')): Promise<esbuild.BuildContext<{
2
+ export declare function watchConfig(configFilePath: string, onBuild: (config: string | undefined, result: esbuild.BuildResult) => void, watch?: boolean, createHash?: (path: string) => string, spinner?: ReturnType<typeof import('@clack/prompts').spinner>, stopSnipper?: (message: string, code?: number) => void): Promise<esbuild.BuildContext<{
4
3
  entryPoints: string[];
5
4
  bundle: true;
6
5
  sourcemap: true;
package/lib/watch.js CHANGED
@@ -1,83 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.watchConfigFile = watchConfigFile;
3
+ exports.watchConfig = watchConfig;
4
4
  exports.getDotTsslintPath = getDotTsslintPath;
5
5
  const esbuild = require("esbuild");
6
6
  const _path = require("path");
7
7
  const fs = require("fs");
8
8
  const url = require("url");
9
- const ErrorStackParser = require("error-stack-parser");
10
- async function watchConfigFile(configFilePath, onBuild, watch = true, createHash = btoa,
9
+ async function watchConfig(configFilePath, onBuild, watch = true, createHash = btoa,
11
10
  // @ts-expect-error
12
- logger) {
11
+ spinner, stopSnipper) {
13
12
  const outDir = getDotTsslintPath(configFilePath);
14
13
  const outFileName = createHash(_path.relative(outDir, configFilePath)) + '.mjs';
15
14
  const outFile = _path.join(outDir, outFileName);
16
- const configFileDisplayPath = _path.relative(process.cwd(), configFilePath);
17
- const resultHandler = async (result) => {
18
- let config;
19
- for (const error of [
20
- ...result.errors,
21
- ...result.warnings,
22
- ]) {
23
- if (error.id) {
24
- error.id = 'esbuild:' + error.id;
25
- }
26
- else {
27
- error.id = 'config-build-error';
28
- }
29
- }
30
- const buildResultText = 'Built ' + configFileDisplayPath + ' in ' + (Date.now() - buildStart) + 'ms';
31
- configBuildingSpinner?.message(buildResultText);
15
+ const resultHandler = (result) => {
32
16
  if (!result.errors.length) {
33
- const loadStart = Date.now();
34
- configBuildingSpinner?.message(buildResultText + ', importing...');
35
- try {
36
- config = (await import(url.pathToFileURL(outFile).toString() + '?time=' + Date.now())).default;
37
- configBuildingSpinner?.stop(buildResultText + ', imported in ' + (Date.now() - loadStart) + 'ms.');
38
- }
39
- catch (e) {
40
- configBuildingSpinner?.stop(buildResultText + ', failed to import.');
41
- if (e.stack) {
42
- const stack = ErrorStackParser.parse(e)[0];
43
- if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
44
- let fileName = stack.fileName
45
- .replace(/\\/g, '/')
46
- .split('?time=')[0];
47
- if (fileName.startsWith('file://')) {
48
- fileName = fileName.substring('file://'.length);
49
- }
50
- result.errors.push({
51
- id: 'config-import-error',
52
- text: String(e),
53
- location: {
54
- file: fileName,
55
- line: stack.lineNumber,
56
- column: stack.columnNumber - 1,
57
- lineText: '',
58
- },
59
- });
60
- }
61
- else {
62
- result.errors.push({
63
- id: 'config-import-error',
64
- text: String(e),
65
- });
66
- }
67
- }
68
- else {
69
- result.errors.push({
70
- id: 'config-import-error',
71
- text: String(e),
72
- });
73
- }
74
- }
17
+ onBuild(outFile, result);
18
+ }
19
+ else {
20
+ onBuild(undefined, result);
75
21
  }
76
- onBuild(config, result);
77
22
  };
78
- let buildStart;
79
- const configBuildingSpinner = logger?.spinner();
80
- configBuildingSpinner?.start('Building ' + configFileDisplayPath);
81
23
  const ctx = await esbuild.context({
82
24
  entryPoints: [configFilePath],
83
25
  bundle: true,
@@ -88,18 +30,16 @@ logger) {
88
30
  plugins: [{
89
31
  name: 'tsslint',
90
32
  setup(build) {
91
- build.onStart(() => {
92
- buildStart = Date.now();
93
- });
94
33
  build.onResolve({ filter: /^https?:\/\// }, async ({ path: importUrl }) => {
95
34
  const cachePath = _path.join(outDir, importUrl.split('://')[0], ...importUrl.split('://')[1].split('/'));
96
35
  if (!fs.existsSync(cachePath)) {
97
- configBuildingSpinner?.message('Downloading ' + importUrl);
36
+ const start = Date.now();
37
+ spinner?.message('Downloading ' + importUrl);
98
38
  const response = await fetch(importUrl);
99
- configBuildingSpinner?.message('Building ' + configFileDisplayPath);
100
39
  if (!response.ok) {
101
40
  throw new Error(`Failed to load ${importUrl}`);
102
41
  }
42
+ stopSnipper?.('Downloaded ' + importUrl + ' in ' + (Date.now() - start) + 'ms');
103
43
  const text = await response.text();
104
44
  fs.mkdirSync(_path.dirname(cachePath), { recursive: true });
105
45
  fs.writeFileSync(cachePath, text, 'utf8');
@@ -142,9 +82,14 @@ logger) {
142
82
  await ctx.watch();
143
83
  }
144
84
  else {
145
- const result = await ctx.rebuild();
146
- await ctx.dispose();
147
- resultHandler(result);
85
+ try {
86
+ const result = await ctx.rebuild(); // could throw
87
+ await ctx.dispose();
88
+ resultHandler(result);
89
+ }
90
+ catch (e) {
91
+ throw e;
92
+ }
148
93
  }
149
94
  return ctx;
150
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsslint/core",
3
- "version": "1.3.5",
3
+ "version": "1.4.0",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "**/*.js",
@@ -12,11 +12,10 @@
12
12
  "directory": "packages/core"
13
13
  },
14
14
  "dependencies": {
15
- "@tsslint/types": "1.3.5",
15
+ "@tsslint/types": "1.4.0",
16
16
  "error-stack-parser": "^2.1.4",
17
17
  "esbuild": ">=0.17.0",
18
- "minimatch": "^10.0.1",
19
- "source-map-support": "^0.5.21"
18
+ "minimatch": "^10.0.1"
20
19
  },
21
20
  "devDependencies": {
22
21
  "@clack/prompts": "^0.8.2"
@@ -24,5 +23,5 @@
24
23
  "scripts": {
25
24
  "postinstall": "node scripts/cleanCache.js"
26
25
  },
27
- "gitHead": "6fd4e516735cdf1205a3fa0df91933de72f62476"
26
+ "gitHead": "9a3f7ce55f079eaaedfb61af9b72d8ba736f0123"
28
27
  }