@tsslint/core 1.0.16 → 1.0.18

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
@@ -3,7 +3,7 @@ export * from './lib/watch';
3
3
  import type { Config, ProjectContext } from '@tsslint/types';
4
4
  import type * as ts from 'typescript';
5
5
  export type Linter = ReturnType<typeof createLinter>;
6
- export declare function createLinter(ctx: ProjectContext, config: Config, withStack: boolean): {
6
+ export declare function createLinter(ctx: ProjectContext, config: Config | Config[], withStack: boolean): {
7
7
  lint(fileName: string): ts.DiagnosticWithLocation[];
8
8
  hasCodeFixes(fileName: string): boolean;
9
9
  getCodeFixes(fileName: string, start: number, end: number, diagnostics?: ts.Diagnostic[]): ts.CodeFixAction[];
package/index.js CHANGED
@@ -44,24 +44,26 @@ function createLinter(ctx, config, withStack) {
44
44
  const fileFixes = new Map();
45
45
  const fileRefactors = new Map();
46
46
  const sourceFiles = new Map();
47
- const plugins = (config.plugins ?? []).map(plugin => plugin(ctx));
48
- const includes = [];
49
- const excludes = [];
50
- for (const include of config.include ?? []) {
51
- const basePath = path.dirname(ctx.configFile);
52
- includes.push([include, path.resolve(basePath, include)]);
53
- }
54
- for (const exclude of config.exclude ?? []) {
55
- const basePath = path.dirname(ctx.configFile);
56
- excludes.push([exclude, path.resolve(basePath, exclude)]);
57
- }
47
+ const basePath = path.dirname(ctx.configFile);
48
+ const configs = (Array.isArray(config) ? config : [config])
49
+ .map(config => ({
50
+ config,
51
+ includes: (config.include ?? []).map(include => {
52
+ return path.resolve(basePath, include);
53
+ }),
54
+ excludes: (config.exclude ?? []).map(exclude => {
55
+ return path.resolve(basePath, exclude);
56
+ }),
57
+ }));
58
+ const plugins = configs.map(({ config }) => config.plugins ?? []).flat().map(plugin => plugin(ctx));
59
+ const debug = configs.some(({ config }) => config.debug);
58
60
  return {
59
61
  lint(fileName) {
60
62
  let diagnostics = [];
61
63
  let debugInfo;
62
- if (config.debug) {
64
+ if (debug) {
63
65
  debugInfo = {
64
- category: ts.DiagnosticCategory.Message,
66
+ category: ts.DiagnosticCategory.Suggestion,
65
67
  code: 'debug',
66
68
  messageText: '- Config: ' + ctx.configFile + '\n',
67
69
  file: ctx.languageService.getProgram().getSourceFile(fileName),
@@ -72,36 +74,12 @@ function createLinter(ctx, config, withStack) {
72
74
  };
73
75
  diagnostics.push(debugInfo);
74
76
  }
75
- for (const [exclude, pattern] of excludes) {
76
- if (minimatch.minimatch(fileName, pattern)) {
77
- if (debugInfo) {
78
- debugInfo.messageText += '- Included: ❌ (via ' + JSON.stringify({ exclude: [exclude] }) + ')\n';
79
- }
80
- return diagnostics;
81
- }
82
- }
83
- if (includes.length) {
84
- let included = false;
85
- for (const [include, pattern] of includes) {
86
- if (minimatch.minimatch(fileName, pattern)) {
87
- included = true;
88
- if (debugInfo) {
89
- debugInfo.messageText += '- Included: ✅ (via ' + JSON.stringify({ include: [include] }) + ')\n';
90
- }
91
- break;
92
- }
93
- }
94
- if (!included) {
95
- if (debugInfo) {
96
- debugInfo.messageText += '- Included: ❌ (not included in any include patterns)\n';
97
- }
98
- return diagnostics;
99
- }
100
- }
101
- else {
77
+ const rules = getFileRules(fileName);
78
+ if (!rules || !Object.keys(rules).length) {
102
79
  if (debugInfo) {
103
- debugInfo.messageText += '- Included: (no include patterns)\n';
80
+ debugInfo.messageText += '- Rules: (no rules)\n';
104
81
  }
82
+ return diagnostics;
105
83
  }
106
84
  const sourceFile = ctx.languageService.getProgram()?.getSourceFile(fileName);
107
85
  if (!sourceFile) {
@@ -115,7 +93,6 @@ function createLinter(ctx, config, withStack) {
115
93
  reportSuggestion,
116
94
  };
117
95
  const token = ctx.languageServiceHost.getCancellationToken?.();
118
- const rules = getFileRules(sourceFile.fileName);
119
96
  const fixes = getFileFixes(sourceFile.fileName);
120
97
  const refactors = getFileRefactors(sourceFile.fileName);
121
98
  let currentRuleId;
@@ -265,7 +242,9 @@ function createLinter(ctx, config, withStack) {
265
242
  }
266
243
  function pushRelatedInformation(error, stack) {
267
244
  if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
268
- let fileName = stack.fileName.replace(/\\/g, '/');
245
+ let fileName = stack.fileName
246
+ .replace(/\\/g, '/')
247
+ .split('?time=')[0];
269
248
  if (fileName.startsWith('file://')) {
270
249
  fileName = fileName.substring('file://'.length);
271
250
  }
@@ -366,7 +345,19 @@ function createLinter(ctx, config, withStack) {
366
345
  function getFileRules(fileName) {
367
346
  let rules = fileRules.get(fileName);
368
347
  if (!rules) {
369
- rules = { ...config.rules };
348
+ rules = {};
349
+ for (const { config, includes, excludes } of configs) {
350
+ if (!config.rules) {
351
+ continue;
352
+ }
353
+ if (excludes.some(pattern => minimatch.minimatch(fileName, pattern))) {
354
+ continue;
355
+ }
356
+ if (includes.length && !includes.some(pattern => minimatch.minimatch(fileName, pattern))) {
357
+ continue;
358
+ }
359
+ rules = { ...rules, ...config.rules };
360
+ }
370
361
  for (const plugin of plugins) {
371
362
  if (plugin.resolveRules) {
372
363
  rules = plugin.resolveRules(fileName, rules);
package/lib/build.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import type { Config } from '@tsslint/config';
2
- export declare function buildConfigFile(configFilePath: string, createHash?: (path: string) => string, logger?: Pick<typeof console, 'log' | 'warn' | 'error'>): Promise<Config>;
2
+ export declare function buildConfigFile(configFilePath: string, createHash?: (path: string) => string, logger?: Pick<typeof console, 'log' | 'warn' | 'error'>): Promise<Config | Config[]>;
package/lib/watch.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import esbuild = require('esbuild');
2
2
  import type { Config } from '@tsslint/config';
3
- export declare function watchConfigFile(configFilePath: string, onBuild: (config: Config | undefined, result: esbuild.BuildResult) => void, watch?: boolean, createHash?: (path: string) => string, logger?: Pick<typeof console, 'log' | 'warn' | 'error'>): Promise<esbuild.BuildContext<{
3
+ export declare function watchConfigFile(configFilePath: string, onBuild: (config: Config | Config[] | undefined, result: esbuild.BuildResult) => void, watch?: boolean, createHash?: (path: string) => string, logger?: Pick<typeof console, 'log' | 'warn' | 'error'>): Promise<esbuild.BuildContext<{
4
4
  entryPoints: string[];
5
5
  bundle: true;
6
6
  sourcemap: true;
package/lib/watch.js CHANGED
@@ -46,9 +46,9 @@ async function watchConfigFile(configFilePath, onBuild, watch = true, createHash
46
46
  if (!path.endsWith('.ts')) {
47
47
  try {
48
48
  const maybeJsPath = require.resolve(path, { paths: [resolveDir] });
49
- if (!maybeJsPath.endsWith('.ts')) {
49
+ if (maybeJsPath !== path && !maybeJsPath.endsWith('.ts')) {
50
50
  return {
51
- path: maybeJsPath,
51
+ path: 'file://' + maybeJsPath,
52
52
  external: true,
53
53
  };
54
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsslint/core",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "**/*.js",
@@ -12,11 +12,11 @@
12
12
  "directory": "packages/core"
13
13
  },
14
14
  "dependencies": {
15
- "@tsslint/types": "1.0.16",
15
+ "@tsslint/types": "1.0.18",
16
16
  "error-stack-parser": "^2.1.4",
17
17
  "esbuild": "^0.23.0",
18
18
  "minimatch": "^10.0.1",
19
19
  "source-map-support": "^0.5.21"
20
20
  },
21
- "gitHead": "e430814fd394da5afbcd3157d02c246592162f75"
21
+ "gitHead": "95545c63940a9b57651c785cf4d730d6c3d22b99"
22
22
  }
package/lib/linter.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import type { Config, ProjectContext } from '@tsslint/types';
2
- import type * as ts from 'typescript';
3
- export type Linter = ReturnType<typeof createLinter>;
4
- export declare function createLinter(ctx: ProjectContext, config: Config, withStack: boolean): {
5
- lint(fileName: string): ts.DiagnosticWithLocation[];
6
- hasCodeFixes(fileName: string): boolean;
7
- getCodeFixes(fileName: string, start: number, end: number, diagnostics?: ts.Diagnostic[]): ts.CodeFixAction[];
8
- getRefactors(fileName: string, start: number, end: number): ts.RefactorActionInfo[];
9
- getRefactorEdits(fileName: string, name: string): ts.FileTextChanges[] | undefined;
10
- };
11
- export declare function combineCodeFixes(fileName: string, fixes: ts.CodeFixAction[]): ts.TextChange[];
12
- export declare function applyTextChanges(baseSnapshot: ts.IScriptSnapshot, textChanges: ts.TextChange[]): ts.IScriptSnapshot;
package/lib/linter.js DELETED
@@ -1,342 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createLinter = createLinter;
4
- exports.combineCodeFixes = combineCodeFixes;
5
- exports.applyTextChanges = applyTextChanges;
6
- const ErrorStackParser = require("error-stack-parser");
7
- const path = require("path");
8
- const minimatch = require("minimatch");
9
- function createLinter(ctx, config, withStack) {
10
- if (withStack) {
11
- require('source-map-support').install({
12
- retrieveFile(path) {
13
- // monkey-fix, refs: https://github.com/typescript-eslint/typescript-eslint/issues/9352
14
- if (path.replace(/\\/g, '/').includes('/@typescript-eslint/eslint-plugin/dist/rules/') && path.endsWith('.js.map')) {
15
- return JSON.stringify({
16
- version: 3,
17
- sources: [],
18
- sourcesContent: [],
19
- mappings: '',
20
- names: [],
21
- });
22
- }
23
- },
24
- });
25
- }
26
- const ts = ctx.typescript;
27
- const fileRules = new Map();
28
- const fileFixes = new Map();
29
- const fileRefactors = new Map();
30
- const sourceFiles = new Map();
31
- const plugins = (config.plugins ?? []).map(plugin => plugin(ctx));
32
- const excludes = [];
33
- for (let exclude of config.exclude ?? []) {
34
- const basePath = path.dirname(ctx.configFile);
35
- excludes.push(path.resolve(basePath, exclude));
36
- }
37
- return {
38
- lint(fileName) {
39
- if (excludes.some(pattern => minimatch.minimatch(fileName, pattern))) {
40
- return [];
41
- }
42
- const sourceFile = ctx.languageService.getProgram()?.getSourceFile(fileName);
43
- if (!sourceFile) {
44
- throw new Error(`No source file found for ${fileName}`);
45
- }
46
- const rulesContext = {
47
- ...ctx,
48
- sourceFile,
49
- reportError,
50
- reportWarning,
51
- reportSuggestion,
52
- };
53
- const token = ctx.languageServiceHost.getCancellationToken?.();
54
- const rules = getFileRules(sourceFile.fileName);
55
- const fixes = getFileFixes(sourceFile.fileName);
56
- const refactors = getFileRefactors(sourceFile.fileName);
57
- let diagnostics = [];
58
- let currentRuleId;
59
- fixes.clear();
60
- refactors.clear();
61
- for (const [id, rule] of Object.entries(rules)) {
62
- if (token?.isCancellationRequested()) {
63
- break;
64
- }
65
- currentRuleId = id;
66
- try {
67
- rule(rulesContext);
68
- }
69
- catch (err) {
70
- diagnostics.push({
71
- category: ts.DiagnosticCategory.Error,
72
- code: id,
73
- messageText: String(err),
74
- file: sourceFile,
75
- start: 0,
76
- length: 0,
77
- source: 'tsslint',
78
- relatedInformation: [],
79
- });
80
- }
81
- }
82
- for (const plugin of plugins) {
83
- if (plugin.resolveDiagnostics) {
84
- diagnostics = plugin.resolveDiagnostics(sourceFile.fileName, diagnostics);
85
- }
86
- }
87
- const diagnosticSet = new Set(diagnostics);
88
- for (const [ruleId, fix] of [...fixes]) {
89
- const final = fix.filter(fix => diagnosticSet.has(fix.diagnostic));
90
- if (final.length) {
91
- fixes.set(ruleId, final);
92
- }
93
- else {
94
- fixes.delete(ruleId);
95
- }
96
- }
97
- for (const [ruleId, refactor] of [...refactors]) {
98
- const final = refactor.filter(fix => diagnosticSet.has(fix.diagnostic));
99
- if (final.length) {
100
- refactors.set(ruleId, final);
101
- }
102
- else {
103
- refactors.delete(ruleId);
104
- }
105
- }
106
- return diagnostics;
107
- function reportError(message, start, end, traceOffset = 0) {
108
- return report(ts.DiagnosticCategory.Error, message, start, end, traceOffset);
109
- }
110
- function reportWarning(message, start, end, traceOffset = 0) {
111
- return report(ts.DiagnosticCategory.Warning, message, start, end, traceOffset);
112
- }
113
- function reportSuggestion(message, start, end, traceOffset = 0) {
114
- return report(ts.DiagnosticCategory.Suggestion, message, start, end, traceOffset);
115
- }
116
- function report(category, message, start, end, traceOffset) {
117
- const error = {
118
- category,
119
- code: currentRuleId,
120
- messageText: message,
121
- file: sourceFile,
122
- start,
123
- length: end - start,
124
- source: 'tsslint',
125
- relatedInformation: [],
126
- };
127
- if (withStack) {
128
- const stacks = traceOffset === false
129
- ? []
130
- : ErrorStackParser.parse(new Error());
131
- if (typeof traceOffset === 'number') {
132
- const baseOffset = 2 + traceOffset;
133
- if (stacks.length > baseOffset) {
134
- pushRelatedInformation(error, stacks[baseOffset]);
135
- }
136
- }
137
- }
138
- diagnostics.push(error);
139
- return {
140
- withDeprecated() {
141
- error.reportsDeprecated = true;
142
- return this;
143
- },
144
- withUnnecessary() {
145
- error.reportsUnnecessary = true;
146
- return this;
147
- },
148
- withFix(title, getEdits) {
149
- if (!fixes.has(currentRuleId)) {
150
- fixes.set(currentRuleId, []);
151
- }
152
- fixes.get(currentRuleId).push(({
153
- diagnostic: error,
154
- title,
155
- start,
156
- end,
157
- getEdits,
158
- }));
159
- return this;
160
- },
161
- withRefactor(title, getEdits) {
162
- if (!refactors.has(currentRuleId)) {
163
- refactors.set(currentRuleId, []);
164
- }
165
- refactors.get(currentRuleId).push(({
166
- diagnostic: error,
167
- title,
168
- start,
169
- end,
170
- getEdits,
171
- }));
172
- return this;
173
- },
174
- };
175
- }
176
- function pushRelatedInformation(error, stack) {
177
- if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
178
- let fileName = stack.fileName.replace(/\\/g, '/');
179
- if (fileName.startsWith('file://')) {
180
- fileName = fileName.substring('file://'.length);
181
- }
182
- if (fileName.includes('http-url:')) {
183
- fileName = fileName.split('http-url:')[1];
184
- }
185
- if (!sourceFiles.has(fileName)) {
186
- const text = ctx.languageServiceHost.readFile(fileName) ?? '';
187
- sourceFiles.set(fileName, ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true));
188
- }
189
- const stackFile = sourceFiles.get(fileName);
190
- let pos = 0;
191
- try {
192
- pos = stackFile?.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
193
- }
194
- catch { }
195
- error.relatedInformation?.push({
196
- category: ts.DiagnosticCategory.Message,
197
- code: 0,
198
- file: stackFile,
199
- start: pos,
200
- length: 0,
201
- messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
202
- });
203
- }
204
- }
205
- },
206
- hasCodeFixes(fileName) {
207
- const fixesMap = getFileFixes(fileName);
208
- for (const [_ruleId, fixes] of fixesMap) {
209
- if (fixes.length) {
210
- return true;
211
- }
212
- }
213
- return false;
214
- },
215
- getCodeFixes(fileName, start, end, diagnostics) {
216
- const fixesMap = getFileFixes(fileName);
217
- let result = [];
218
- for (const [ruleId, fixes] of fixesMap) {
219
- for (let i = 0; i < fixes.length; i++) {
220
- const fix = fixes[i];
221
- if (diagnostics?.length && !diagnostics.includes(fix.diagnostic)) {
222
- continue;
223
- }
224
- if ((fix.start >= start && fix.start <= end) ||
225
- (fix.end >= start && fix.end <= end) ||
226
- (start >= fix.start && start <= fix.end) ||
227
- (end >= fix.start && end <= fix.end)) {
228
- result.push({
229
- fixName: `tsslint:${ruleId}`,
230
- description: fix.title,
231
- changes: fix.getEdits(),
232
- fixId: 'tsslint',
233
- fixAllDescription: 'Fix all TSSLint errors'
234
- });
235
- }
236
- }
237
- }
238
- for (const plugin of plugins) {
239
- if (plugin.resolveCodeFixes) {
240
- result = plugin.resolveCodeFixes(fileName, result);
241
- }
242
- }
243
- return result;
244
- },
245
- getRefactors(fileName, start, end) {
246
- const refactorsMap = getFileRefactors(fileName);
247
- let result = [];
248
- for (const [ruleId, refactors] of refactorsMap) {
249
- for (let i = 0; i < refactors.length; i++) {
250
- const refactor = refactors[i];
251
- if ((refactor.start >= start && refactor.start <= end) ||
252
- (refactor.end >= start && refactor.end <= end) ||
253
- (start >= refactor.start && start <= refactor.end) ||
254
- (end >= refactor.start && end <= refactor.end)) {
255
- result.push({
256
- name: `tsslint:${ruleId}:${i}`,
257
- description: refactor.title + ' (' + ruleId + ')',
258
- kind: 'quickfix',
259
- });
260
- }
261
- }
262
- }
263
- return result;
264
- },
265
- getRefactorEdits(fileName, name) {
266
- if (name.startsWith('tsslint:')) {
267
- const [ruleId, index] = name.slice('tsslint:'.length).split(':');
268
- const refactorsMap = getFileRefactors(fileName);
269
- const refactor = refactorsMap.get(ruleId)?.[Number(index)];
270
- if (refactor) {
271
- return refactor.getEdits();
272
- }
273
- }
274
- },
275
- };
276
- function getFileRules(fileName) {
277
- let rules = fileRules.get(fileName);
278
- if (!rules) {
279
- rules = { ...config.rules };
280
- for (const plugin of plugins) {
281
- if (plugin.resolveRules) {
282
- rules = plugin.resolveRules(fileName, rules);
283
- }
284
- }
285
- fileRules.set(fileName, rules);
286
- }
287
- return rules;
288
- }
289
- function getFileFixes(fileName) {
290
- if (!fileFixes.has(fileName)) {
291
- fileFixes.set(fileName, new Map());
292
- }
293
- return fileFixes.get(fileName);
294
- }
295
- function getFileRefactors(fileName) {
296
- if (!fileRefactors.has(fileName)) {
297
- fileRefactors.set(fileName, new Map());
298
- }
299
- return fileRefactors.get(fileName);
300
- }
301
- }
302
- function combineCodeFixes(fileName, fixes) {
303
- const changes = fixes
304
- .map(fix => fix.changes)
305
- .flat()
306
- .filter(change => change.fileName === fileName && change.textChanges.length)
307
- .sort((a, b) => b.textChanges[0].span.start - a.textChanges[0].span.start);
308
- let lastChangeAt = Number.MAX_VALUE;
309
- let finalTextChanges = [];
310
- for (const change of changes) {
311
- const textChanges = [...change.textChanges].sort((a, b) => a.span.start - b.span.start);
312
- const firstChange = textChanges[0];
313
- const lastChange = textChanges[textChanges.length - 1];
314
- if (lastChangeAt >= lastChange.span.start + lastChange.span.length) {
315
- lastChangeAt = firstChange.span.start;
316
- finalTextChanges = finalTextChanges.concat(textChanges);
317
- }
318
- }
319
- return finalTextChanges;
320
- }
321
- function applyTextChanges(baseSnapshot, textChanges) {
322
- textChanges = [...textChanges].sort((a, b) => b.span.start - a.span.start);
323
- let text = baseSnapshot.getText(0, baseSnapshot.getLength());
324
- for (const change of textChanges) {
325
- text = text.slice(0, change.span.start) + change.newText + text.slice(change.span.start + change.span.length);
326
- }
327
- return {
328
- getText(start, end) {
329
- return text.substring(start, end);
330
- },
331
- getLength() {
332
- return text.length;
333
- },
334
- getChangeRange(oldSnapshot) {
335
- if (oldSnapshot === baseSnapshot) {
336
- // TODO
337
- }
338
- return undefined;
339
- },
340
- };
341
- }
342
- //# sourceMappingURL=linter.js.map