jiek 1.0.3 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,94 @@
1
+ import fs from 'node:fs'
2
+ import { dirname, resolve } from 'node:path'
3
+
4
+ import { parse } from 'jsonc-parser'
5
+ import { isMatch } from 'micromatch'
6
+
7
+ type TSConfig = {
8
+ extends?: string | string[]
9
+ compilerOptions?: Record<string, unknown>
10
+ references?: { path: string }[]
11
+ files?: string[]
12
+ include?: string[]
13
+ exclude?: string[]
14
+ }
15
+
16
+ const getTSConfig = (p: string): TSConfig =>
17
+ !fs.existsSync(p) || !fs.statSync(p).isFile()
18
+ ? {}
19
+ : parse(fs.readFileSync(p, 'utf-8'), [], { allowTrailingComma: true, allowEmptyContent: true })
20
+
21
+ const getExtendTSConfig = (tsconfigPath: string): TSConfig => {
22
+ tsconfigPath = resolve(tsconfigPath)
23
+ const tsconfigPathDirname = dirname(tsconfigPath)
24
+ const { extends: exts, ...tsconfig } = getTSConfig(tsconfigPath)
25
+ const resolvePaths = (paths: string[] | undefined) => paths?.map(p => resolve(tsconfigPathDirname, p)) ?? []
26
+
27
+ const extendsPaths = resolvePaths(
28
+ exts ? Array.isArray(exts) ? exts : [exts] : []
29
+ )
30
+ if (extendsPaths.length === 0) return tsconfig
31
+ return extendsPaths
32
+ .map(getExtendTSConfig)
33
+ .concat(tsconfig)
34
+ // https://www.typescriptlang.org/tsconfig/#files:~:text=Currently%2C%20the%20only%20top%2Dlevel%20property%20that%20is%20excluded%20from%20inheritance%20is%20references.
35
+ // Currently, the only top-level property that is excluded from inheritance is references.
36
+ .reduce((acc, { compilerOptions = {}, references: _, ...curr }) => ({
37
+ ...acc,
38
+ ...curr,
39
+ compilerOptions: {
40
+ ...acc.compilerOptions,
41
+ ...compilerOptions
42
+ }
43
+ }), {})
44
+ }
45
+
46
+ export const getCompilerOptionsByFilePath = (
47
+ tsconfigPath: string,
48
+ filePath: string
49
+ ): Record<string, unknown> | undefined => {
50
+ tsconfigPath = resolve(tsconfigPath)
51
+ filePath = resolve(filePath)
52
+ const tsconfigPathDirname = dirname(tsconfigPath)
53
+ // https://www.typescriptlang.org/tsconfig/#files:~:text=It%E2%80%99s%20worth%20noting%20that%20files%2C%20include%2C%20and%20exclude%20from%20the%20inheriting%20config%20file%20overwrite%20those%20from%20the%20base%20config%20file%2C%20and%20that%20circularity%20between%20configuration%20files%20is%20not%20allowed.
54
+ // It’s worth noting that files, include, and exclude from the inheriting config file overwrite
55
+ // those from the base config file, and that circularity between configuration files is not allowed.
56
+ const tsconfig = getExtendTSConfig(tsconfigPath)
57
+
58
+ const resolvePaths = (paths: string[] | undefined) => paths?.map(p => resolve(tsconfigPathDirname, p)) ?? []
59
+
60
+ const [
61
+ references,
62
+ files,
63
+ include,
64
+ exclude
65
+ ] = [
66
+ tsconfig.references?.map(({ path }) => path),
67
+ tsconfig.files,
68
+ tsconfig.include,
69
+ tsconfig.exclude
70
+ ].map(resolvePaths)
71
+ if (exclude.length > 0 && exclude.some(i => isMatch(filePath, i))) return
72
+
73
+ // when files or include is not empty, the tsconfig should be ignored
74
+ if (tsconfig.files?.length === 0 && tsconfig.include?.length === 0) return
75
+ let isInclude = false
76
+ isInclude ||= files.length > 0 && files.includes(filePath)
77
+ isInclude ||= include.length > 0 && include.some(i => isMatch(filePath, i))
78
+ if (isInclude) {
79
+ return tsconfig.compilerOptions ?? {}
80
+ } else {
81
+ // when files or include is not empty, but the file is not matched, the tsconfig should be ignored
82
+ if (
83
+ (tsconfig.files && tsconfig.files.length > 0)
84
+ || (tsconfig.include && tsconfig.include.length > 0)
85
+ ) return
86
+ }
87
+
88
+ references.reverse()
89
+ for (const ref of references) {
90
+ const compilerOptions = getCompilerOptionsByFilePath(ref, filePath)
91
+ if (compilerOptions) return compilerOptions
92
+ }
93
+ return tsconfig.compilerOptions
94
+ }