@ithinkdt/lint 4.0.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.
- package/README.md +71 -0
- package/package.json +82 -0
- package/pnpm-lock.yaml +2735 -0
- package/src/base.js +198 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +108 -0
- package/src/prettier.d.ts +4 -0
- package/src/prettier.js +13 -0
- package/src/stylelint.d.ts +4 -0
- package/src/stylelint.js +49 -0
- package/src/typescript-eslint-parser-for-extra-files/index.js +67 -0
- package/src/typescript-eslint-parser-for-extra-files/transform.js +28 -0
- package/src/typescript-eslint-parser-for-extra-files/ts.js +378 -0
- package/src/typescript-eslint-parser-for-extra-files/utils/get-project-config-files.js +25 -0
- package/src/typescript-eslint-parser-for-extra-files/utils/resolve-project-list.js +63 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import ts from 'typescript'
|
|
3
|
+
import { transformExtraFile } from './transform.js'
|
|
4
|
+
|
|
5
|
+
export class TSServiceManager {
|
|
6
|
+
tsServices = new Map()
|
|
7
|
+
|
|
8
|
+
getProgram(code, options) {
|
|
9
|
+
const tsconfigPath = options.project
|
|
10
|
+
const extraFileExtensions = [...new Set(options.extraFileExtensions)]
|
|
11
|
+
|
|
12
|
+
let serviceList = this.tsServices.get(tsconfigPath)
|
|
13
|
+
if (!serviceList) {
|
|
14
|
+
serviceList = []
|
|
15
|
+
this.tsServices.set(tsconfigPath, serviceList)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let service = serviceList.find((service) =>
|
|
19
|
+
extraFileExtensions.every((ext) => service.extraFileExtensions.includes(ext)),
|
|
20
|
+
)
|
|
21
|
+
if (!service) {
|
|
22
|
+
service = new TSService(tsconfigPath, extraFileExtensions)
|
|
23
|
+
serviceList.unshift(service)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return service.getProgram(code, options.filePath)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
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
|
+
constructor(tsconfigPath, extraFileExtensions) {
|
|
48
|
+
this.tsconfigPath = tsconfigPath
|
|
49
|
+
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
|
+
this.currTarget = {
|
|
64
|
+
code,
|
|
65
|
+
filePath: normalized,
|
|
66
|
+
dirMap,
|
|
67
|
+
}
|
|
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
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
createWatch(tsconfigPath, extraFileExtensions) {
|
|
86
|
+
const createAbstractBuilder = (...args) => {
|
|
87
|
+
const [rootNames, options, argHost, oldProgram, configFileParsingDiagnostics, projectReferences] = args
|
|
88
|
+
|
|
89
|
+
const host = argHost
|
|
90
|
+
if (!this.patchedHostSet.has(host)) {
|
|
91
|
+
this.patchedHostSet.add(host)
|
|
92
|
+
|
|
93
|
+
const getTargetSourceFile = (fileName, languageVersionOrOptions) => {
|
|
94
|
+
if (
|
|
95
|
+
this.currTarget.filePath === normalizeFileName(fileName) &&
|
|
96
|
+
isExtra(fileName, extraFileExtensions)
|
|
97
|
+
) {
|
|
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
|
+
))
|
|
106
|
+
}
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const original = {
|
|
111
|
+
getSourceFile: host.getSourceFile,
|
|
112
|
+
getSourceFileByPath: host.getSourceFileByPath,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
host.getSourceFile = (fileName, languageVersionOrOptions, ...args) => {
|
|
116
|
+
// Always call the original function, because it calls the file watcher.
|
|
117
|
+
const originalSourceFile = original.getSourceFile.call(
|
|
118
|
+
host,
|
|
119
|
+
fileName,
|
|
120
|
+
languageVersionOrOptions,
|
|
121
|
+
...args,
|
|
122
|
+
)
|
|
123
|
+
return getTargetSourceFile(fileName, languageVersionOrOptions) ?? originalSourceFile
|
|
124
|
+
}
|
|
125
|
+
host.getSourceFileByPath = (fileName, path, languageVersionOrOptions, ...args) => {
|
|
126
|
+
// Always call the original function, because it calls the file watcher.
|
|
127
|
+
const originalSourceFile = original.getSourceFileByPath.call(
|
|
128
|
+
host,
|
|
129
|
+
fileName,
|
|
130
|
+
path,
|
|
131
|
+
languageVersionOrOptions,
|
|
132
|
+
...args,
|
|
133
|
+
)
|
|
134
|
+
return getTargetSourceFile(fileName, languageVersionOrOptions) ?? originalSourceFile
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return ts.createAbstractBuilder(
|
|
138
|
+
rootNames,
|
|
139
|
+
options,
|
|
140
|
+
host,
|
|
141
|
+
oldProgram,
|
|
142
|
+
configFileParsingDiagnostics,
|
|
143
|
+
projectReferences,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const watchCompilerHost = ts.createWatchCompilerHost(
|
|
148
|
+
tsconfigPath,
|
|
149
|
+
{
|
|
150
|
+
noEmit: true,
|
|
151
|
+
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
|
+
allowNonTsExtensions: true,
|
|
157
|
+
},
|
|
158
|
+
ts.sys,
|
|
159
|
+
createAbstractBuilder,
|
|
160
|
+
(diagnostic) => {
|
|
161
|
+
throw new Error(formatDiagnostics([diagnostic]))
|
|
162
|
+
},
|
|
163
|
+
() => {
|
|
164
|
+
// Not reported in reportWatchStatus.
|
|
165
|
+
},
|
|
166
|
+
undefined,
|
|
167
|
+
extraFileExtensions.map((extension) => ({
|
|
168
|
+
extension,
|
|
169
|
+
isMixedContent: true,
|
|
170
|
+
scriptKind: ts.ScriptKind.Deferred,
|
|
171
|
+
})),
|
|
172
|
+
)
|
|
173
|
+
const original = {
|
|
174
|
+
readFile: watchCompilerHost.readFile,
|
|
175
|
+
fileExists: watchCompilerHost.fileExists,
|
|
176
|
+
readDirectory: watchCompilerHost.readDirectory,
|
|
177
|
+
directoryExists: watchCompilerHost.directoryExists,
|
|
178
|
+
getDirectories: watchCompilerHost.getDirectories,
|
|
179
|
+
}
|
|
180
|
+
watchCompilerHost.getDirectories = (dirName, ...args) => {
|
|
181
|
+
const result = distinctArray(
|
|
182
|
+
...original.getDirectories.call(watchCompilerHost, dirName, ...args),
|
|
183
|
+
// Include the path to the target file if the target file does not actually exist.
|
|
184
|
+
this.currTarget.dirMap.get(normalizeFileName(dirName))?.name,
|
|
185
|
+
)
|
|
186
|
+
return result
|
|
187
|
+
}
|
|
188
|
+
watchCompilerHost.directoryExists = (dirName, ...args) => {
|
|
189
|
+
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))
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
watchCompilerHost.readDirectory = (dirName, ...args) => {
|
|
196
|
+
let results = original.readDirectory.call(watchCompilerHost, dirName, ...args)
|
|
197
|
+
|
|
198
|
+
// Include the target file if the target file does not actually exist.
|
|
199
|
+
const file = this.currTarget.dirMap.get(normalizeFileName(dirName))
|
|
200
|
+
if (file) {
|
|
201
|
+
if (file.path === this.currTarget.filePath) {
|
|
202
|
+
results.push(file.path)
|
|
203
|
+
} else {
|
|
204
|
+
results = results.filter((f) => file.path !== f && file.name !== f)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return distinctArray(...results)
|
|
209
|
+
}
|
|
210
|
+
watchCompilerHost.readFile = (fileName, ...args) => {
|
|
211
|
+
const realFileName = getRealFileNameIfExist(fileName)
|
|
212
|
+
if (realFileName == undefined) {
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
if (this.currTarget.filePath === realFileName) {
|
|
216
|
+
// It is the file currently being parsed.
|
|
217
|
+
return transformExtraFile(this.currTarget.code, {
|
|
218
|
+
filePath: realFileName,
|
|
219
|
+
current: true,
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const code = original.readFile.call(watchCompilerHost, realFileName, ...args)
|
|
224
|
+
if (!code) {
|
|
225
|
+
return code
|
|
226
|
+
}
|
|
227
|
+
return transformExtraFile(code, {
|
|
228
|
+
filePath: realFileName,
|
|
229
|
+
current: false,
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
// Modify it so that it can be determined that the virtual file actually exists.
|
|
233
|
+
watchCompilerHost.fileExists = (fileName) => {
|
|
234
|
+
return getRealFileNameIfExist(fileName) != undefined
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const getRealFileNameIfExist = (fileName) => {
|
|
238
|
+
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
|
+
if (this.currTarget.dirMap.has(normalizedFileName)) {
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
if (this.currTarget.filePath === normalizedFileName) {
|
|
245
|
+
// It is the file currently being parsed.
|
|
246
|
+
return normalizedFileName
|
|
247
|
+
}
|
|
248
|
+
const exists = original.fileExists.call(watchCompilerHost, normalizedFileName)
|
|
249
|
+
if (exists) {
|
|
250
|
+
return normalizedFileName
|
|
251
|
+
}
|
|
252
|
+
if (isVirtualTSX(normalizedFileName, extraFileExtensions)) {
|
|
253
|
+
const real = normalizedFileName.slice(0, -4)
|
|
254
|
+
for (const dts of toExtraDtsFileNames(real, extraFileExtensions)) {
|
|
255
|
+
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
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (original.fileExists.call(watchCompilerHost, real)) {
|
|
261
|
+
return real
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// It keeps a callback to mark the parsed file as changed so that it can be re-parsed.
|
|
268
|
+
watchCompilerHost.watchFile = (fileName, callback) => {
|
|
269
|
+
const normalized = normalizeFileName(fileName)
|
|
270
|
+
this.fileWatchCallbacks.set(normalized, {
|
|
271
|
+
update: () => callback(fileName, ts.FileWatcherEventKind.Changed),
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
close: () => {
|
|
276
|
+
this.fileWatchCallbacks.delete(normalized)
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// Use watchCompilerHost but don't actually watch the files and directories.
|
|
281
|
+
watchCompilerHost.watchDirectory = () => {
|
|
282
|
+
return {
|
|
283
|
+
close: () => {
|
|
284
|
+
// noop
|
|
285
|
+
},
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
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
|
+
watchCompilerHost.afterProgramCreate = (program) => {
|
|
294
|
+
const originalDiagnostics = program.getConfigFileParsingDiagnostics()
|
|
295
|
+
const configFileDiagnostics = originalDiagnostics.filter(
|
|
296
|
+
(diag) => diag.category === ts.DiagnosticCategory.Error && diag.code !== 18_003,
|
|
297
|
+
)
|
|
298
|
+
if (configFileDiagnostics.length > 0) {
|
|
299
|
+
throw new Error(formatDiagnostics(configFileDiagnostics))
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const watch = ts.createWatchProgram(watchCompilerHost)
|
|
304
|
+
return watch
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** If the given filename has extra extensions, returns the d.ts filename. */
|
|
309
|
+
function toExtraDtsFileNames(fileName, extraFileExtensions) {
|
|
310
|
+
const ext = getExtIfExtra(fileName, extraFileExtensions)
|
|
311
|
+
if (ext != undefined) {
|
|
312
|
+
return [`${fileName}.d.ts`, `${fileName.slice(0, -ext.length)}.d${ext}.ts`]
|
|
313
|
+
}
|
|
314
|
+
return []
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Checks the given filename has extra extension or not. */
|
|
318
|
+
function isExtra(fileName, extraFileExtensions) {
|
|
319
|
+
return getExtIfExtra(fileName, extraFileExtensions) != undefined
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Gets the file extension if the given file is an extra extension file. */
|
|
323
|
+
function getExtIfExtra(fileName, extraFileExtensions) {
|
|
324
|
+
for (const extraFileExtension of extraFileExtensions) {
|
|
325
|
+
if (fileName.endsWith(extraFileExtension)) {
|
|
326
|
+
return extraFileExtension
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Checks the given filename is virtual file tsx or not. */
|
|
333
|
+
function isVirtualTSX(fileName, extraFileExtensions) {
|
|
334
|
+
for (const extraFileExtension of extraFileExtensions) {
|
|
335
|
+
if (fileName.endsWith(`${extraFileExtension}.tsx`)) {
|
|
336
|
+
return true
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return false
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function formatDiagnostics(diagnostics) {
|
|
343
|
+
return ts.formatDiagnostics(diagnostics, {
|
|
344
|
+
getCanonicalFileName: (f) => f,
|
|
345
|
+
getCurrentDirectory: () => process.cwd(),
|
|
346
|
+
getNewLine: () => '\n',
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function normalizeFileName(fileName) {
|
|
351
|
+
let normalized = path.normalize(fileName)
|
|
352
|
+
if (normalized.endsWith(path.sep)) {
|
|
353
|
+
normalized = normalized.slice(0, -1)
|
|
354
|
+
}
|
|
355
|
+
if (ts.sys.useCaseSensitiveFileNames) {
|
|
356
|
+
return toAbsolutePath(normalized)
|
|
357
|
+
}
|
|
358
|
+
return toAbsolutePath(normalized.toLowerCase())
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function toAbsolutePath(filePath, baseDir) {
|
|
362
|
+
return path.isAbsolute(filePath) ? filePath : path.join(baseDir || process.cwd(), filePath)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function* iterateDirs(filePath) {
|
|
366
|
+
let target = filePath
|
|
367
|
+
let parent
|
|
368
|
+
while ((parent = path.dirname(target)) !== target) {
|
|
369
|
+
yield parent
|
|
370
|
+
target = parent
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function distinctArray(...list) {
|
|
375
|
+
return [...new Set(ts.sys.useCaseSensitiveFileNames ? list : list.map((s) => s?.toLowerCase()))].filter(
|
|
376
|
+
(s) => s != undefined,
|
|
377
|
+
)
|
|
378
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
export function getProjectConfigFiles(options) {
|
|
5
|
+
if (options.project !== true) {
|
|
6
|
+
return Array.isArray(options.project) ? options.project : [options.project]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let directory = path.dirname(options.filePath)
|
|
10
|
+
const checkedDirectories = [directory]
|
|
11
|
+
|
|
12
|
+
do {
|
|
13
|
+
const tsconfigPath = path.join(directory, 'tsconfig.json')
|
|
14
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
15
|
+
return [tsconfigPath]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
directory = path.dirname(directory)
|
|
19
|
+
checkedDirectories.push(directory)
|
|
20
|
+
} while (directory.length > 1 && directory.length >= options.tsconfigRootDir.length)
|
|
21
|
+
|
|
22
|
+
throw new Error(
|
|
23
|
+
`project was set to \`true\` but couldn't find any tsconfig.json relative to '${options.filePath}' within '${options.tsconfigRootDir}'.`,
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import ts from 'typescript'
|
|
3
|
+
import { globbySync } from 'globby'
|
|
4
|
+
import isGlob from 'is-glob'
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes, sanitizes, resolves and filters the provided project paths
|
|
7
|
+
*/
|
|
8
|
+
export function resolveProjectList(options) {
|
|
9
|
+
const sanitizedProjects = []
|
|
10
|
+
|
|
11
|
+
// Normalize and sanitize the project paths
|
|
12
|
+
if (options.project) {
|
|
13
|
+
for (const project of options.project) {
|
|
14
|
+
if (typeof project === 'string') {
|
|
15
|
+
sanitizedProjects.push(project)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (sanitizedProjects.length === 0) {
|
|
21
|
+
return []
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const projectFolderIgnoreList = []
|
|
25
|
+
for (const folder of options.projectFolderIgnoreList ?? ['**/node_modules/**']) {
|
|
26
|
+
if (typeof folder === 'string') {
|
|
27
|
+
projectFolderIgnoreList.push(folder.startsWith('!') ? folder : `!${folder}`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Transform glob patterns into paths
|
|
32
|
+
const nonGlobProjects = sanitizedProjects.filter((project) => !isGlob(project))
|
|
33
|
+
const globProjects = sanitizedProjects.filter((project) => isGlob(project))
|
|
34
|
+
|
|
35
|
+
const uniqueCanonicalProjectPaths = new Set(
|
|
36
|
+
[
|
|
37
|
+
...nonGlobProjects,
|
|
38
|
+
...(globProjects.length === 0
|
|
39
|
+
? []
|
|
40
|
+
: globbySync([...globProjects, ...projectFolderIgnoreList], {
|
|
41
|
+
cwd: options.tsconfigRootDir,
|
|
42
|
+
})),
|
|
43
|
+
].map((project) => getCanonicalFileName(ensureAbsolutePath(project, options.tsconfigRootDir))),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return [...uniqueCanonicalProjectPaths]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// typescript doesn't provide a ts.sys implementation for browser environments
|
|
50
|
+
const useCaseSensitiveFileNames = ts.sys === undefined ? true : ts.sys.useCaseSensitiveFileNames
|
|
51
|
+
const correctPathCasing = useCaseSensitiveFileNames ? (filePath) => filePath : (filePath) => filePath.toLowerCase()
|
|
52
|
+
|
|
53
|
+
function getCanonicalFileName(filePath) {
|
|
54
|
+
let normalized = path.normalize(filePath)
|
|
55
|
+
if (normalized.endsWith(path.sep)) {
|
|
56
|
+
normalized = normalized.slice(0, -1)
|
|
57
|
+
}
|
|
58
|
+
return correctPathCasing(normalized)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function ensureAbsolutePath(p, tsconfigRootDir) {
|
|
62
|
+
return path.isAbsolute(p) ? p : path.join(tsconfigRootDir || process.cwd(), p)
|
|
63
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"exclude": ["node_modules", "dist"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"noImplicitOverride": true,
|
|
6
|
+
"target": "ESNext",
|
|
7
|
+
"module": "Preserve",
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"moduleResolution": "Bundler",
|
|
10
|
+
"lib": ["ESNext"],
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"strictNullChecks": true
|
|
13
|
+
}
|
|
14
|
+
}
|