-
-
Notifications
You must be signed in to change notification settings - Fork 29
/
helpers.ts
144 lines (131 loc) · 4.2 KB
/
helpers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import fs from 'node:fs'
import path from 'node:path'
import type { Position } from 'acorn'
import type { Point } from 'unist'
import type { NormalPosition } from './types'
export const last = <T>(items: T[] | readonly T[]) =>
// eslint-disable-next-line unicorn/prefer-at -- FIXME: Node 16.6+ required
items && items[items.length - 1]
export const arrayify = <T, R = T extends Array<infer S> ? S : T>(
...args: T[]
) =>
args.reduce<R[]>((arr, curr) => {
arr.push(...(Array.isArray(curr) ? curr : curr == null ? [] : [curr]))
return arr
}, [])
/**
* Given a filepath, get the nearest path that is a regular file.
* The filepath provided by eslint may be a virtual filepath rather than a file
* on disk. This attempts to transform a virtual path into an on-disk path
*/
export const getPhysicalFilename = (
filename: string,
child?: string,
): string => {
try {
if (fs.statSync(filename).isDirectory()) {
return child || filename
}
} catch (err) {
const { code } = err as { code: string }
// https://github.com/eslint/eslint/issues/11989
// Additionally, it seems there is no `ENOTDIR` code on Windows...
if (code === 'ENOTDIR' || code === 'ENOENT') {
return getPhysicalFilename(path.dirname(filename), filename)
}
}
return filename
}
/**
* ! copied from https://github.com/just-jeb/angular-builders/blob/master/packages/custom-webpack/src/utils.ts#L53-L67
*
* This uses a dynamic import to load a module which may be ESM.
* CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript
* will currently, unconditionally downlevel dynamic import into a require call.
* require calls cannot load ESM code and will result in a runtime error. To workaround
* this, a Function constructor is used to prevent TypeScript from changing the dynamic import.
* Once TypeScript provides support for keeping the dynamic import this workaround can
* be dropped.
*
* @param modulePath The path of the module to load.
* @returns A Promise that resolves to the dynamically imported module.
*/
/* istanbul ignore next */
export const loadEsmModule = <T>(modulePath: URL | string): Promise<T> =>
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
new Function('modulePath', `return import(modulePath);`)(
modulePath,
) as Promise<T>
/* istanbul ignore next -- used in worker */
export const getPositionAtFactory = (text: string) => {
const lines = text.split('\n')
return (offset: number): Position => {
let currOffset = 0
for (const [index, line_] of lines.entries()) {
const line = index + 1
const nextOffset = currOffset + line_.length
if (nextOffset >= offset) {
return {
line,
column: offset - currOffset,
}
}
currOffset = nextOffset + 1 // add a line break `\n` offset
}
}
}
export const normalizePosition = ({
start,
end,
text,
}: {
start: Point | { offset: number }
end: Point | { offset: number }
text?: string
}): NormalPosition => {
const startOffset = start.offset
const endOffset = end.offset
const range: [number, number] = [startOffset, endOffset]
const getPositionAt =
text == null
? null
: /* istanbul ignore next -- used in worker */ getPositionAtFactory(text)
return {
start: startOffset,
end: endOffset,
loc: {
start:
/* istanbul ignore next -- used in worker */ 'line' in start
? (start as Position)
: getPositionAt(startOffset),
end:
/* istanbul ignore next -- used in worker */ 'line' in end
? (end as Position)
: getPositionAt(endOffset),
},
range,
}
}
/* istanbul ignore next -- used in worker */
export const prevCharOffsetFactory =
(text: string) =>
(offset: number): number => {
for (let i = offset; i >= 0; i--) {
const char = text[i]
if (/^\S$/.test(char)) {
return i
}
}
}
/* istanbul ignore next -- used in worker */
export const nextCharOffsetFactory = (text: string) => {
const total = text.length
return (offset: number): number => {
for (let i = offset; i <= total; i++) {
const char = text[i]
if (/^\S$/.test(char)) {
return i
}
}
}
}