@ithinkdt/lint 4.0.0-2 → 4.0.0-201

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.
@@ -1,9 +1,13 @@
1
1
  import path from 'node:path'
2
+
2
3
  import ts from 'typescript'
4
+
3
5
  import { transformExtraFile } from './transform.js'
4
6
 
5
7
  export class TSServiceManager {
6
- tsServices = new Map()
8
+ constructor() {
9
+ this.tsServices = new Map()
10
+ }
7
11
 
8
12
  getProgram(code, options) {
9
13
  const tsconfigPath = options.project
@@ -15,8 +19,8 @@ export class TSServiceManager {
15
19
  this.tsServices.set(tsconfigPath, serviceList)
16
20
  }
17
21
 
18
- let service = serviceList.find((service) =>
19
- extraFileExtensions.every((ext) => service.extraFileExtensions.includes(ext)),
22
+ let service = serviceList.find(service =>
23
+ extraFileExtensions.every(ext => service.extraFileExtensions.includes(ext)),
20
24
  )
21
25
  if (!service) {
22
26
  service = new TSService(tsconfigPath, extraFileExtensions)
@@ -28,58 +32,18 @@ export class TSServiceManager {
28
32
  }
29
33
 
30
34
  export class TSService {
31
- watch
32
-
33
- patchedHostSet = new WeakSet()
34
-
35
- tsconfigPath
36
-
37
- extraFileExtensions
38
-
39
- currTarget = {
40
- code: '',
41
- filePath: '',
42
- dirMap: new Map(),
43
- }
44
-
45
- fileWatchCallbacks = new Map()
46
-
47
35
  constructor(tsconfigPath, extraFileExtensions) {
36
+ this.patchedHostSet = new WeakSet()
48
37
  this.tsconfigPath = tsconfigPath
49
38
  this.extraFileExtensions = extraFileExtensions
50
- this.watch = this.createWatch(tsconfigPath, extraFileExtensions)
51
- }
52
-
53
- getProgram(code, filePath) {
54
- const normalized = normalizeFileName(filePath)
55
- const lastTarget = this.currTarget
56
-
57
- const dirMap = new Map()
58
- let childPath = normalized
59
- for (const dirName of iterateDirs(normalized)) {
60
- dirMap.set(dirName, { path: childPath, name: path.basename(childPath) })
61
- childPath = dirName
62
- }
63
39
  this.currTarget = {
64
- code,
65
- filePath: normalized,
66
- dirMap,
40
+ code: '',
41
+ filePath: '',
42
+ sourceFile: undefined,
43
+ dirMap: new Map(),
67
44
  }
68
- for (const { filePath: targetPath } of [this.currTarget, lastTarget]) {
69
- if (!targetPath) continue
70
- if (!ts.sys.fileExists(targetPath)) {
71
- // Signal a directory change to request a re-scan of the directory
72
- // because it targets a file that does not actually exist.
73
- this.fileWatchCallbacks.get(normalizeFileName(this.tsconfigPath))?.update()
74
- }
75
- this.fileWatchCallbacks.get(normalizeFileName(targetPath))?.update()
76
- }
77
-
78
- const program = this.watch.getProgram().getProgram()
79
- // sets parent pointers in source files
80
- program.getTypeChecker()
81
-
82
- return program
45
+ this.fileWatchCallbacks = new Map()
46
+ this.watch = this.createWatch(tsconfigPath, extraFileExtensions)
83
47
  }
84
48
 
85
49
  createWatch(tsconfigPath, extraFileExtensions) {
@@ -92,28 +56,28 @@ export class TSService {
92
56
 
93
57
  const getTargetSourceFile = (fileName, languageVersionOrOptions) => {
94
58
  if (
95
- this.currTarget.filePath === normalizeFileName(fileName) &&
96
- isExtra(fileName, extraFileExtensions)
59
+ this.currTarget.filePath === normalizeFileName(fileName)
60
+ && isExtra(fileName, extraFileExtensions)
97
61
  ) {
98
- // Parse the target file as TSX.
99
- return (this.currTarget.sourceFile ??= ts.createSourceFile(
100
- this.currTarget.filePath,
101
- this.currTarget.code,
102
- languageVersionOrOptions,
103
- true,
104
- ts.ScriptKind.TSX,
105
- ))
62
+ return (
63
+ this.currTarget.sourceFile
64
+ ?? ts.createSourceFile(
65
+ this.currTarget.filePath,
66
+ this.currTarget.code,
67
+ languageVersionOrOptions,
68
+ true,
69
+ ts.ScriptKind.TSX,
70
+ )
71
+ )
106
72
  }
107
- return
73
+ return null
108
74
  }
109
75
 
110
76
  const original = {
111
77
  getSourceFile: host.getSourceFile,
112
78
  getSourceFileByPath: host.getSourceFileByPath,
113
79
  }
114
-
115
80
  host.getSourceFile = (fileName, languageVersionOrOptions, ...args) => {
116
- // Always call the original function, because it calls the file watcher.
117
81
  const originalSourceFile = original.getSourceFile.call(
118
82
  host,
119
83
  fileName,
@@ -123,7 +87,6 @@ export class TSService {
123
87
  return getTargetSourceFile(fileName, languageVersionOrOptions) ?? originalSourceFile
124
88
  }
125
89
  host.getSourceFileByPath = (fileName, path, languageVersionOrOptions, ...args) => {
126
- // Always call the original function, because it calls the file watcher.
127
90
  const originalSourceFile = original.getSourceFileByPath.call(
128
91
  host,
129
92
  fileName,
@@ -149,10 +112,6 @@ export class TSService {
149
112
  {
150
113
  noEmit: true,
151
114
  jsx: ts.JsxEmit.Preserve,
152
-
153
- // This option is required if `includes` only includes `*.vue` files.
154
- // However, the option is not in the documentation.
155
- // https://github.com/microsoft/TypeScript/issues/28447
156
115
  allowNonTsExtensions: true,
157
116
  },
158
117
  ts.sys,
@@ -160,11 +119,9 @@ export class TSService {
160
119
  (diagnostic) => {
161
120
  throw new Error(formatDiagnostics([diagnostic]))
162
121
  },
163
- () => {
164
- // Not reported in reportWatchStatus.
165
- },
122
+ () => {},
166
123
  undefined,
167
- extraFileExtensions.map((extension) => ({
124
+ extraFileExtensions.map(extension => ({
168
125
  extension,
169
126
  isMixedContent: true,
170
127
  scriptKind: ts.ScriptKind.Deferred,
@@ -180,28 +137,25 @@ export class TSService {
180
137
  watchCompilerHost.getDirectories = (dirName, ...args) => {
181
138
  const result = distinctArray(
182
139
  ...original.getDirectories.call(watchCompilerHost, dirName, ...args),
183
- // Include the path to the target file if the target file does not actually exist.
184
140
  this.currTarget.dirMap.get(normalizeFileName(dirName))?.name,
185
141
  )
186
142
  return result
187
143
  }
188
144
  watchCompilerHost.directoryExists = (dirName, ...args) => {
189
145
  return (
190
- original.directoryExists.call(watchCompilerHost, dirName, ...args) ||
191
- // Include the path to the target file if the target file does not actually exist.
192
- this.currTarget.dirMap.has(normalizeFileName(dirName))
146
+ original.directoryExists.call(watchCompilerHost, dirName, ...args)
147
+ || this.currTarget.dirMap.has(normalizeFileName(dirName))
193
148
  )
194
149
  }
195
150
  watchCompilerHost.readDirectory = (dirName, ...args) => {
196
151
  let results = original.readDirectory.call(watchCompilerHost, dirName, ...args)
197
152
 
198
- // Include the target file if the target file does not actually exist.
199
153
  const file = this.currTarget.dirMap.get(normalizeFileName(dirName))
200
154
  if (file) {
201
155
  if (file.path === this.currTarget.filePath) {
202
156
  results.push(file.path)
203
157
  } else {
204
- results = results.filter((f) => file.path !== f && file.name !== f)
158
+ results = results.filter(f => file.path !== f && file.name !== f)
205
159
  }
206
160
  }
207
161
 
@@ -209,11 +163,10 @@ export class TSService {
209
163
  }
210
164
  watchCompilerHost.readFile = (fileName, ...args) => {
211
165
  const realFileName = getRealFileNameIfExist(fileName)
212
- if (realFileName == undefined) {
166
+ if (realFileName == null) {
213
167
  return
214
168
  }
215
169
  if (this.currTarget.filePath === realFileName) {
216
- // It is the file currently being parsed.
217
170
  return transformExtraFile(this.currTarget.code, {
218
171
  filePath: realFileName,
219
172
  current: true,
@@ -229,20 +182,16 @@ export class TSService {
229
182
  current: false,
230
183
  })
231
184
  }
232
- // Modify it so that it can be determined that the virtual file actually exists.
233
185
  watchCompilerHost.fileExists = (fileName) => {
234
- return getRealFileNameIfExist(fileName) != undefined
186
+ return getRealFileNameIfExist(fileName) != null
235
187
  }
236
188
 
237
189
  const getRealFileNameIfExist = (fileName) => {
238
190
  const normalizedFileName = normalizeFileName(fileName)
239
- // Even if it is actually a file, if it is specified as a directory to the target file,
240
- // it is assumed that it does not exist as a file.
241
191
  if (this.currTarget.dirMap.has(normalizedFileName)) {
242
- return
192
+ return null
243
193
  }
244
194
  if (this.currTarget.filePath === normalizedFileName) {
245
- // It is the file currently being parsed.
246
195
  return normalizedFileName
247
196
  }
248
197
  const exists = original.fileExists.call(watchCompilerHost, normalizedFileName)
@@ -253,18 +202,16 @@ export class TSService {
253
202
  const real = normalizedFileName.slice(0, -4)
254
203
  for (const dts of toExtraDtsFileNames(real, extraFileExtensions)) {
255
204
  if (original.fileExists.call(watchCompilerHost, dts)) {
256
- // If the d.ts file exists, respect it and consider the virtual file not to exist.
257
- return
205
+ return null
258
206
  }
259
207
  }
260
208
  if (original.fileExists.call(watchCompilerHost, real)) {
261
209
  return real
262
210
  }
263
211
  }
264
- return
212
+ return null
265
213
  }
266
214
 
267
- // It keeps a callback to mark the parsed file as changed so that it can be re-parsed.
268
215
  watchCompilerHost.watchFile = (fileName, callback) => {
269
216
  const normalized = normalizeFileName(fileName)
270
217
  this.fileWatchCallbacks.set(normalized, {
@@ -277,23 +224,16 @@ export class TSService {
277
224
  },
278
225
  }
279
226
  }
280
- // Use watchCompilerHost but don't actually watch the files and directories.
281
227
  watchCompilerHost.watchDirectory = () => {
282
228
  return {
283
- close: () => {
284
- // noop
285
- },
229
+ close: () => {},
286
230
  }
287
231
  }
288
232
 
289
- /**
290
- * It heavily references typescript-eslint.
291
- * @see https://github.com/typescript-eslint/typescript-eslint/blob/84e316be33dac5302bd0367c4d1960bef40c484d/packages/typescript-estree/src/create-program/createWatchProgram.ts#L297-L309
292
- */
293
233
  watchCompilerHost.afterProgramCreate = (program) => {
294
234
  const originalDiagnostics = program.getConfigFileParsingDiagnostics()
295
235
  const configFileDiagnostics = originalDiagnostics.filter(
296
- (diag) => diag.category === ts.DiagnosticCategory.Error && diag.code !== 18_003,
236
+ diag => diag.category === ts.DiagnosticCategory.Error && diag.code !== 18_003,
297
237
  )
298
238
  if (configFileDiagnostics.length > 0) {
299
239
  throw new Error(formatDiagnostics(configFileDiagnostics))
@@ -303,33 +243,60 @@ export class TSService {
303
243
  const watch = ts.createWatchProgram(watchCompilerHost)
304
244
  return watch
305
245
  }
246
+
247
+ getProgram(code, filePath) {
248
+ const normalized = normalizeFileName(filePath)
249
+ const lastTarget = this.currTarget
250
+
251
+ const dirMap = new Map()
252
+ let childPath = normalized
253
+ for (const dirName of iterateDirs(normalized)) {
254
+ dirMap.set(dirName, { path: childPath, name: path.basename(childPath) })
255
+ childPath = dirName
256
+ }
257
+ this.currTarget = {
258
+ code,
259
+ filePath: normalized,
260
+ dirMap,
261
+ }
262
+ for (const { filePath: targetPath } of [this.currTarget, lastTarget]) {
263
+ if (!targetPath) continue
264
+ if (!ts.sys.fileExists(targetPath)) {
265
+ this.fileWatchCallbacks.get(normalizeFileName(this.tsconfigPath))?.update()
266
+ }
267
+ this.fileWatchCallbacks.get(normalizeFileName(targetPath))?.update()
268
+ }
269
+
270
+ const program = this.watch.getProgram().getProgram()
271
+ program.getTypeChecker()
272
+
273
+ return program
274
+ }
275
+
276
+ watch = null
306
277
  }
307
278
 
308
- /** If the given filename has extra extensions, returns the d.ts filename. */
309
279
  function toExtraDtsFileNames(fileName, extraFileExtensions) {
310
280
  const ext = getExtIfExtra(fileName, extraFileExtensions)
311
- if (ext != undefined) {
281
+ if (ext != null) {
312
282
  return [`${fileName}.d.ts`, `${fileName.slice(0, -ext.length)}.d${ext}.ts`]
313
283
  }
314
284
  return []
315
285
  }
316
286
 
317
- /** Checks the given filename has extra extension or not. */
318
287
  function isExtra(fileName, extraFileExtensions) {
319
- return getExtIfExtra(fileName, extraFileExtensions) != undefined
288
+ return getExtIfExtra(fileName, extraFileExtensions) != null
320
289
  }
321
290
 
322
- /** Gets the file extension if the given file is an extra extension file. */
323
291
  function getExtIfExtra(fileName, extraFileExtensions) {
324
292
  for (const extraFileExtension of extraFileExtensions) {
325
293
  if (fileName.endsWith(extraFileExtension)) {
326
294
  return extraFileExtension
327
295
  }
328
296
  }
329
- return
297
+ return null
330
298
  }
331
299
 
332
- /** Checks the given filename is virtual file tsx or not. */
333
300
  function isVirtualTSX(fileName, extraFileExtensions) {
334
301
  for (const extraFileExtension of extraFileExtensions) {
335
302
  if (fileName.endsWith(`${extraFileExtension}.tsx`)) {
@@ -341,7 +308,7 @@ function isVirtualTSX(fileName, extraFileExtensions) {
341
308
 
342
309
  function formatDiagnostics(diagnostics) {
343
310
  return ts.formatDiagnostics(diagnostics, {
344
- getCanonicalFileName: (f) => f,
311
+ getCanonicalFileName: f => f,
345
312
  getCurrentDirectory: () => process.cwd(),
346
313
  getNewLine: () => '\n',
347
314
  })
@@ -353,9 +320,9 @@ function normalizeFileName(fileName) {
353
320
  normalized = normalized.slice(0, -1)
354
321
  }
355
322
  if (ts.sys.useCaseSensitiveFileNames) {
356
- return toAbsolutePath(normalized)
323
+ return toAbsolutePath(normalized, null)
357
324
  }
358
- return toAbsolutePath(normalized.toLowerCase())
325
+ return toAbsolutePath(normalized.toLowerCase(), null)
359
326
  }
360
327
 
361
328
  function toAbsolutePath(filePath, baseDir) {
@@ -372,7 +339,7 @@ function* iterateDirs(filePath) {
372
339
  }
373
340
 
374
341
  function distinctArray(...list) {
375
- return [...new Set(ts.sys.useCaseSensitiveFileNames ? list : list.map((s) => s?.toLowerCase()))].filter(
376
- (s) => s != undefined,
342
+ return [...new Set(ts.sys.useCaseSensitiveFileNames ? list : list.map(s => s?.toLowerCase()))].filter(
343
+ s => s != null,
377
344
  )
378
345
  }
@@ -1,7 +1,9 @@
1
1
  import path from 'node:path'
2
- import ts from 'typescript'
2
+
3
3
  import { globbySync } from 'globby'
4
4
  import isGlob from 'is-glob'
5
+ import ts from 'typescript'
6
+
5
7
  /**
6
8
  * Normalizes, sanitizes, resolves and filters the provided project paths
7
9
  */
@@ -9,7 +11,7 @@ export function resolveProjectList(options) {
9
11
  const sanitizedProjects = []
10
12
 
11
13
  // Normalize and sanitize the project paths
12
- if (options.project) {
14
+ if (options.project != null) {
13
15
  for (const project of options.project) {
14
16
  if (typeof project === 'string') {
15
17
  sanitizedProjects.push(project)
@@ -22,15 +24,18 @@ export function resolveProjectList(options) {
22
24
  }
23
25
 
24
26
  const projectFolderIgnoreList = []
27
+
25
28
  for (const folder of options.projectFolderIgnoreList ?? ['**/node_modules/**']) {
26
29
  if (typeof folder === 'string') {
27
- projectFolderIgnoreList.push(folder.startsWith('!') ? folder : `!${folder}`)
30
+ projectFolderIgnoreList.push(folder)
28
31
  }
29
32
  }
33
+ // prefix with a ! for not match glob
34
+ projectFolderIgnoreList.map(folder => (folder.startsWith('!') ? folder : `!${folder}`))
30
35
 
31
36
  // Transform glob patterns into paths
32
- const nonGlobProjects = sanitizedProjects.filter((project) => !isGlob(project))
33
- const globProjects = sanitizedProjects.filter((project) => isGlob(project))
37
+ const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project))
38
+ const globProjects = sanitizedProjects.filter(project => isGlob(project))
34
39
 
35
40
  const uniqueCanonicalProjectPaths = new Set(
36
41
  [
@@ -38,9 +43,9 @@ export function resolveProjectList(options) {
38
43
  ...(globProjects.length === 0
39
44
  ? []
40
45
  : globbySync([...globProjects, ...projectFolderIgnoreList], {
41
- cwd: options.tsconfigRootDir,
42
- })),
43
- ].map((project) => getCanonicalFileName(ensureAbsolutePath(project, options.tsconfigRootDir))),
46
+ cwd: options.tsconfigRootDir,
47
+ })),
48
+ ].map(project => _getCanonicalFileName(_ensureAbsolutePath(project, options.tsconfigRootDir))),
44
49
  )
45
50
 
46
51
  return [...uniqueCanonicalProjectPaths]
@@ -48,9 +53,9 @@ export function resolveProjectList(options) {
48
53
 
49
54
  // typescript doesn't provide a ts.sys implementation for browser environments
50
55
  const useCaseSensitiveFileNames = ts.sys === undefined ? true : ts.sys.useCaseSensitiveFileNames
51
- const correctPathCasing = useCaseSensitiveFileNames ? (filePath) => filePath : (filePath) => filePath.toLowerCase()
56
+ const correctPathCasing = useCaseSensitiveFileNames ? filePath => filePath : filePath => filePath.toLowerCase()
52
57
 
53
- function getCanonicalFileName(filePath) {
58
+ function _getCanonicalFileName(filePath) {
54
59
  let normalized = path.normalize(filePath)
55
60
  if (normalized.endsWith(path.sep)) {
56
61
  normalized = normalized.slice(0, -1)
@@ -58,6 +63,6 @@ function getCanonicalFileName(filePath) {
58
63
  return correctPathCasing(normalized)
59
64
  }
60
65
 
61
- function ensureAbsolutePath(p, tsconfigRootDir) {
66
+ function _ensureAbsolutePath(p, tsconfigRootDir) {
62
67
  return path.isAbsolute(p) ? p : path.join(tsconfigRootDir || process.cwd(), p)
63
68
  }