@remix-run/assets 0.4.4 → 0.5.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 +322 -56
- package/dist/assets.d.ts +2 -1
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +2 -2
- package/dist/lib/access.d.ts +6 -2
- package/dist/lib/access.d.ts.map +1 -1
- package/dist/lib/access.js +300 -5
- package/dist/lib/asset-server.d.ts +118 -5
- package/dist/lib/asset-server.d.ts.map +1 -1
- package/dist/lib/asset-server.js +260 -24
- package/dist/lib/compilation-error.d.ts +1 -1
- package/dist/lib/compilation-error.d.ts.map +1 -1
- package/dist/lib/file-matcher.js +1 -1
- package/dist/lib/files/compiler.d.ts.map +1 -1
- package/dist/lib/files/compiler.js +7 -6
- package/dist/lib/files/config.js +1 -1
- package/dist/lib/hmr.d.ts +37 -0
- package/dist/lib/hmr.d.ts.map +1 -0
- package/dist/lib/hmr.js +357 -0
- package/dist/lib/injected-packages.d.ts.map +1 -1
- package/dist/lib/injected-packages.js +36 -13
- package/dist/lib/loaders.d.ts +57 -0
- package/dist/lib/loaders.d.ts.map +1 -0
- package/dist/lib/loaders.js +1 -0
- package/dist/lib/module-store.d.ts +24 -0
- package/dist/lib/module-store.d.ts.map +1 -1
- package/dist/lib/module-store.js +136 -11
- package/dist/lib/routes.js +1 -1
- package/dist/lib/scripts/compiler.d.ts +24 -1
- package/dist/lib/scripts/compiler.d.ts.map +1 -1
- package/dist/lib/scripts/compiler.js +183 -29
- package/dist/lib/scripts/conditions.d.ts +2 -0
- package/dist/lib/scripts/conditions.d.ts.map +1 -0
- package/dist/lib/scripts/conditions.js +1 -0
- package/dist/lib/scripts/emit.d.ts +3 -0
- package/dist/lib/scripts/emit.d.ts.map +1 -1
- package/dist/lib/scripts/emit.js +24 -5
- package/dist/lib/scripts/resolve.d.ts +4 -0
- package/dist/lib/scripts/resolve.d.ts.map +1 -1
- package/dist/lib/scripts/resolve.js +70 -5
- package/dist/lib/scripts/transform.d.ts +9 -0
- package/dist/lib/scripts/transform.d.ts.map +1 -1
- package/dist/lib/scripts/transform.js +222 -18
- package/dist/lib/source-maps.js +1 -1
- package/dist/lib/styles/compiler.d.ts +14 -1
- package/dist/lib/styles/compiler.d.ts.map +1 -1
- package/dist/lib/styles/compiler.js +78 -14
- package/dist/lib/styles/emit.js +4 -4
- package/dist/lib/styles/resolve.d.ts.map +1 -1
- package/dist/lib/styles/resolve.js +8 -7
- package/dist/lib/styles/transform.js +3 -3
- package/dist/lib/target.d.ts +1 -1
- package/dist/lib/target.d.ts.map +1 -1
- package/dist/lib/watch.js +1 -1
- package/dist/types/hmr.d.ts +36 -0
- package/package.json +17 -11
- package/src/assets.ts +2 -1
- package/src/lib/access.ts +386 -5
- package/src/lib/asset-server.ts +429 -18
- package/src/lib/compilation-error.ts +1 -0
- package/src/lib/files/compiler.ts +7 -3
- package/src/lib/hmr.ts +397 -0
- package/src/lib/injected-packages.ts +41 -11
- package/src/lib/loaders.ts +80 -0
- package/src/lib/module-store.ts +190 -9
- package/src/lib/scripts/compiler.ts +248 -24
- package/src/lib/scripts/conditions.ts +1 -0
- package/src/lib/scripts/emit.ts +50 -5
- package/src/lib/scripts/resolve.ts +124 -2
- package/src/lib/scripts/transform.ts +290 -19
- package/src/lib/styles/compiler.ts +108 -8
- package/src/lib/styles/emit.ts +1 -1
- package/src/lib/styles/resolve.ts +11 -7
- package/src/types/hmr.d.ts +36 -0
package/src/lib/access.ts
CHANGED
|
@@ -1,26 +1,407 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import * as path from 'node:path'
|
|
1
3
|
import { createFileMatcher } from './file-matcher.ts'
|
|
2
4
|
import { isInjectedPackageFilePath } from './injected-packages.ts'
|
|
5
|
+
import { normalizeFilePath } from './paths.ts'
|
|
3
6
|
|
|
4
7
|
type AccessPolicy = {
|
|
8
|
+
getPackageWatchDirectories(): readonly string[]
|
|
9
|
+
handleFileEvent(filePath: string): void
|
|
5
10
|
isAllowed(filePath: string): boolean
|
|
6
11
|
}
|
|
7
12
|
|
|
13
|
+
const packageStateFileNames = new Set([
|
|
14
|
+
'bun.lock',
|
|
15
|
+
'bun.lockb',
|
|
16
|
+
'npm-shrinkwrap.json',
|
|
17
|
+
'package-lock.json',
|
|
18
|
+
'pnpm-lock.yaml',
|
|
19
|
+
'pnpm-workspace.yaml',
|
|
20
|
+
'yarn.lock',
|
|
21
|
+
])
|
|
22
|
+
const packageManagerRootFileNames = packageStateFileNames
|
|
23
|
+
const packageNamePartPattern = /^[A-Za-z0-9._~-]+$/
|
|
24
|
+
|
|
8
25
|
export function createAccessPolicy(options: {
|
|
9
|
-
|
|
10
|
-
|
|
26
|
+
allowFiles: readonly string[]
|
|
27
|
+
allowPackages?: readonly string[]
|
|
28
|
+
denyFiles?: readonly string[]
|
|
29
|
+
packageSearchRoots?: readonly string[]
|
|
11
30
|
rootDir: string
|
|
12
31
|
}): AccessPolicy {
|
|
13
|
-
let allowMatchers = options.
|
|
14
|
-
|
|
32
|
+
let allowMatchers = options.allowFiles.map((pattern) =>
|
|
33
|
+
createFileMatcher(pattern, options.rootDir),
|
|
34
|
+
)
|
|
35
|
+
let allowPackageNames = normalizePackageNames(options.allowPackages, 'allowPackages')
|
|
36
|
+
let denyMatchers = (options.denyFiles ?? []).map((pattern) =>
|
|
15
37
|
createFileMatcher(pattern, options.rootDir),
|
|
16
38
|
)
|
|
39
|
+
let packageSearchRoots = [options.rootDir, ...(options.packageSearchRoots ?? [])]
|
|
40
|
+
let packageRootPaths = createPackageRootPaths({
|
|
41
|
+
allowPackageNames,
|
|
42
|
+
searchRoots: packageSearchRoots,
|
|
43
|
+
})
|
|
44
|
+
let allowPackageRootPathTrie = createPackageRootPathTrie(packageRootPaths)
|
|
45
|
+
let packageStateDirectories =
|
|
46
|
+
allowPackageNames.size === 0 ? [] : getPackageStateDirectories(packageSearchRoots)
|
|
47
|
+
let packageRootsDirty = false
|
|
48
|
+
|
|
49
|
+
function refreshPackageRootPathTries(): void {
|
|
50
|
+
if (!packageRootsDirty) return
|
|
51
|
+
|
|
52
|
+
packageRootPaths = createPackageRootPaths({
|
|
53
|
+
allowPackageNames,
|
|
54
|
+
searchRoots: packageSearchRoots,
|
|
55
|
+
})
|
|
56
|
+
allowPackageRootPathTrie = createPackageRootPathTrie(packageRootPaths)
|
|
57
|
+
packageRootsDirty = false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isAllowedPackage(filePath: string): boolean {
|
|
61
|
+
if (allowPackageNames.size === 0) return false
|
|
62
|
+
refreshPackageRootPathTries()
|
|
63
|
+
|
|
64
|
+
return isPathInPackageRootPathTrie(filePath, allowPackageRootPathTrie)
|
|
65
|
+
}
|
|
17
66
|
|
|
18
67
|
return {
|
|
68
|
+
getPackageWatchDirectories() {
|
|
69
|
+
if (allowPackageNames.size === 0) return []
|
|
70
|
+
return packageStateDirectories
|
|
71
|
+
},
|
|
72
|
+
handleFileEvent(filePath) {
|
|
73
|
+
if (allowPackageNames.size === 0) return
|
|
74
|
+
if (!isPackageStateFileEvent(filePath, packageStateDirectories)) return
|
|
75
|
+
|
|
76
|
+
packageRootsDirty = true
|
|
77
|
+
},
|
|
19
78
|
isAllowed(filePath) {
|
|
20
79
|
if (isInjectedPackageFilePath(filePath)) return true
|
|
21
|
-
if (!allowMatchers.some((matcher) => matcher(filePath)))
|
|
80
|
+
if (!allowMatchers.some((matcher) => matcher(filePath)) && !isAllowedPackage(filePath)) {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
22
83
|
if (denyMatchers.length > 0 && denyMatchers.some((matcher) => matcher(filePath))) return false
|
|
23
84
|
return true
|
|
24
85
|
},
|
|
25
86
|
}
|
|
26
87
|
}
|
|
88
|
+
|
|
89
|
+
function normalizePackageNames(
|
|
90
|
+
packageOption: readonly string[] | undefined,
|
|
91
|
+
optionName: 'allowPackages',
|
|
92
|
+
): Set<string> {
|
|
93
|
+
let packageNames = new Set<string>()
|
|
94
|
+
|
|
95
|
+
for (let packageName of packageOption ?? []) {
|
|
96
|
+
if (typeof packageName !== 'string') {
|
|
97
|
+
throw new TypeError(`${optionName} values must be strings`)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let normalizedPackageName = packageName.trim()
|
|
101
|
+
if (!isValidPackageName(normalizedPackageName)) {
|
|
102
|
+
throw new TypeError(`${optionName} values must be package names. Received "${packageName}".`)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
packageNames.add(normalizedPackageName)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return packageNames
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function validatePackageName(packageName: string, message: string): void {
|
|
112
|
+
if (!isValidPackageName(packageName)) {
|
|
113
|
+
throw new TypeError(message)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
type PackageJson = {
|
|
118
|
+
dependencies?: Record<string, string>
|
|
119
|
+
optionalDependencies?: Record<string, string>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
type PackageRootPathTrie = {
|
|
123
|
+
children: Map<string, PackageRootPathTrie>
|
|
124
|
+
packageRoot: boolean
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
type PackageRootQueueItem = {
|
|
128
|
+
packageJsonPath: string
|
|
129
|
+
packageName: string
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function createPackageRootPaths(options: {
|
|
133
|
+
allowPackageNames: ReadonlySet<string>
|
|
134
|
+
searchRoots: readonly string[]
|
|
135
|
+
}): Set<string> {
|
|
136
|
+
let allowPackageRootPaths = new Set<string>()
|
|
137
|
+
let allowQueue: PackageRootQueueItem[] = []
|
|
138
|
+
let seenAllowedPackageRoots = new Set<string>()
|
|
139
|
+
let searchRoots = normalizePackageSearchRoots(options.searchRoots)
|
|
140
|
+
|
|
141
|
+
for (let packageName of options.allowPackageNames) {
|
|
142
|
+
let foundPackage = false
|
|
143
|
+
|
|
144
|
+
for (let searchRoot of searchRoots) {
|
|
145
|
+
let packageJsonPath = findPackageJsonPath(packageName, searchRoot)
|
|
146
|
+
if (packageJsonPath === null) continue
|
|
147
|
+
|
|
148
|
+
foundPackage = true
|
|
149
|
+
allowQueue.push({ packageJsonPath, packageName })
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!foundPackage) {
|
|
153
|
+
throw new TypeError(`Could not resolve allowed package "${packageName}".`)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
while (allowQueue.length > 0) {
|
|
158
|
+
let { packageJsonPath } = allowQueue.shift()!
|
|
159
|
+
let packageRootPath = normalizeFilePath(path.dirname(packageJsonPath))
|
|
160
|
+
if (seenAllowedPackageRoots.has(packageRootPath)) continue
|
|
161
|
+
seenAllowedPackageRoots.add(packageRootPath)
|
|
162
|
+
|
|
163
|
+
let packageJson = readPackageJson(packageJsonPath)
|
|
164
|
+
allowPackageRootPaths.add(packageRootPath)
|
|
165
|
+
|
|
166
|
+
for (let dependencyName of Object.keys(packageJson.dependencies ?? {})) {
|
|
167
|
+
validatePackageName(
|
|
168
|
+
dependencyName,
|
|
169
|
+
`Dependency "${dependencyName}" from ${packageJsonPath} must be a package name.`,
|
|
170
|
+
)
|
|
171
|
+
let dependencyPackageJsonPath = findPackageJsonPath(dependencyName, packageRootPath)
|
|
172
|
+
if (dependencyPackageJsonPath === null) {
|
|
173
|
+
throw new TypeError(
|
|
174
|
+
`Could not resolve dependency "${dependencyName}" from ${packageJsonPath}.`,
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
allowQueue.push({
|
|
178
|
+
packageJsonPath: dependencyPackageJsonPath,
|
|
179
|
+
packageName: dependencyName,
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
for (let dependencyName of Object.keys(packageJson.optionalDependencies ?? {})) {
|
|
183
|
+
validatePackageName(
|
|
184
|
+
dependencyName,
|
|
185
|
+
`Optional dependency "${dependencyName}" from ${packageJsonPath} must be a package name.`,
|
|
186
|
+
)
|
|
187
|
+
let dependencyPackageJsonPath = findPackageJsonPath(dependencyName, packageRootPath)
|
|
188
|
+
if (dependencyPackageJsonPath !== null) {
|
|
189
|
+
allowQueue.push({
|
|
190
|
+
packageJsonPath: dependencyPackageJsonPath,
|
|
191
|
+
packageName: dependencyName,
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return allowPackageRootPaths
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function createPackageRootPathTrie(packageRootPaths: ReadonlySet<string>): PackageRootPathTrie {
|
|
201
|
+
let rootNode = createPackageRootPathTrieNode()
|
|
202
|
+
|
|
203
|
+
for (let packageRootPath of packageRootPaths) {
|
|
204
|
+
let node = rootNode
|
|
205
|
+
for (let segment of getFilePathSegments(packageRootPath)) {
|
|
206
|
+
let childNode = node.children.get(segment)
|
|
207
|
+
if (!childNode) {
|
|
208
|
+
childNode = createPackageRootPathTrieNode()
|
|
209
|
+
node.children.set(segment, childNode)
|
|
210
|
+
}
|
|
211
|
+
node = childNode
|
|
212
|
+
}
|
|
213
|
+
node.packageRoot = true
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return rootNode
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function createPackageRootPathTrieNode(): PackageRootPathTrie {
|
|
220
|
+
return {
|
|
221
|
+
children: new Map(),
|
|
222
|
+
packageRoot: false,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function isPathInPackageRootPathTrie(filePath: string, trie: PackageRootPathTrie): boolean {
|
|
227
|
+
let node = trie
|
|
228
|
+
if (node.packageRoot) return true
|
|
229
|
+
|
|
230
|
+
for (let segment of getFilePathSegments(filePath)) {
|
|
231
|
+
let childNode = node.children.get(segment)
|
|
232
|
+
if (!childNode) return false
|
|
233
|
+
if (childNode.packageRoot) return true
|
|
234
|
+
node = childNode
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return false
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function getFilePathSegments(filePath: string): string[] {
|
|
241
|
+
return normalizeFilePath(filePath).split('/')
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function normalizePackageSearchRoots(searchRoots: readonly string[]): string[] {
|
|
245
|
+
let normalizedSearchRoots = new Set<string>()
|
|
246
|
+
|
|
247
|
+
for (let searchRoot of searchRoots) {
|
|
248
|
+
let normalizedSearchRoot = normalizeFilePath(searchRoot)
|
|
249
|
+
normalizedSearchRoots.add(normalizedSearchRoot)
|
|
250
|
+
|
|
251
|
+
if (path.basename(normalizedSearchRoot) === 'node_modules') {
|
|
252
|
+
normalizedSearchRoots.add(path.dirname(normalizedSearchRoot))
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return [...normalizedSearchRoots]
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function getPackageStateDirectories(searchRoots: readonly string[]): string[] {
|
|
260
|
+
let packageStateDirectories = new Set<string>()
|
|
261
|
+
|
|
262
|
+
for (let searchRoot of searchRoots) {
|
|
263
|
+
let packageManagerRoot = findPackageManagerRoot(searchRoot)
|
|
264
|
+
if (packageManagerRoot !== null) {
|
|
265
|
+
packageStateDirectories.add(packageManagerRoot)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return [...packageStateDirectories]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function isPackageStateFileEvent(
|
|
273
|
+
filePath: string,
|
|
274
|
+
packageStateDirectories: readonly string[],
|
|
275
|
+
): boolean {
|
|
276
|
+
let normalizedFilePath = normalizeFilePath(filePath)
|
|
277
|
+
let fileName = path.basename(normalizedFilePath)
|
|
278
|
+
if (!packageStateFileNames.has(fileName)) return false
|
|
279
|
+
|
|
280
|
+
let directory = path.dirname(normalizedFilePath)
|
|
281
|
+
return packageStateDirectories.some(
|
|
282
|
+
(packageStateDirectory) => directory === packageStateDirectory,
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function findPackageManagerRoot(startDirectory: string): string | null {
|
|
287
|
+
let directory = normalizePackageStateSearchRoot(startDirectory)
|
|
288
|
+
|
|
289
|
+
while (true) {
|
|
290
|
+
if (hasPackageManagerRootFile(directory)) return directory
|
|
291
|
+
|
|
292
|
+
let parentDirectory = path.dirname(directory)
|
|
293
|
+
if (parentDirectory === directory) return null
|
|
294
|
+
directory = parentDirectory
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function normalizePackageStateSearchRoot(searchRoot: string): string {
|
|
299
|
+
let normalizedSearchRoot = normalizeFilePath(searchRoot)
|
|
300
|
+
return path.basename(normalizedSearchRoot) === 'node_modules'
|
|
301
|
+
? path.dirname(normalizedSearchRoot)
|
|
302
|
+
: normalizedSearchRoot
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function hasPackageManagerRootFile(directory: string): boolean {
|
|
306
|
+
for (let fileName of packageManagerRootFileNames) {
|
|
307
|
+
try {
|
|
308
|
+
let stat = fs.statSync(path.join(directory, fileName))
|
|
309
|
+
if (stat.isFile()) return true
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (isPathNotFoundError(error)) continue
|
|
312
|
+
throw error
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return false
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function findPackageJsonPath(packageName: string, startDirectory: string): string | null {
|
|
320
|
+
let directory = normalizeFilePath(startDirectory)
|
|
321
|
+
|
|
322
|
+
while (true) {
|
|
323
|
+
let packageJsonPath = resolvePackageJsonPath(directory, packageName)
|
|
324
|
+
if (packageJsonPath !== null) return packageJsonPath
|
|
325
|
+
|
|
326
|
+
let parentDirectory = path.dirname(directory)
|
|
327
|
+
if (parentDirectory === directory) return null
|
|
328
|
+
directory = parentDirectory
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function resolvePackageJsonPath(directory: string, packageName: string): string | null {
|
|
333
|
+
let packagePath =
|
|
334
|
+
path.basename(directory) === 'node_modules'
|
|
335
|
+
? path.join(directory, packageName, 'package.json')
|
|
336
|
+
: path.join(directory, 'node_modules', packageName, 'package.json')
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
return normalizeFilePath(fs.realpathSync(packagePath))
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (isPathNotFoundError(error)) return null
|
|
342
|
+
throw error
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function readPackageJson(packageJsonPath: string): PackageJson {
|
|
347
|
+
let packageJson: unknown
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) as unknown
|
|
351
|
+
} catch (error) {
|
|
352
|
+
if (isPathNotFoundError(error)) return {}
|
|
353
|
+
throw error
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (packageJson === null || typeof packageJson !== 'object') {
|
|
357
|
+
return {}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
dependencies: readDependencyMap(packageJson, 'dependencies'),
|
|
362
|
+
optionalDependencies: readDependencyMap(packageJson, 'optionalDependencies'),
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function readDependencyMap(packageJson: object, key: string): Record<string, string> | undefined {
|
|
367
|
+
if (!(key in packageJson)) return undefined
|
|
368
|
+
|
|
369
|
+
let value = packageJson[key as keyof typeof packageJson]
|
|
370
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
371
|
+
return undefined
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
let dependencies: Record<string, string> = {}
|
|
375
|
+
for (let [dependencyName, dependencyVersion] of Object.entries(value)) {
|
|
376
|
+
if (typeof dependencyVersion === 'string') {
|
|
377
|
+
dependencies[dependencyName] = dependencyVersion
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return dependencies
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function isValidPackageName(packageName: string): boolean {
|
|
385
|
+
if (packageName.length === 0) return false
|
|
386
|
+
|
|
387
|
+
let packageNameParts = packageName.startsWith('@')
|
|
388
|
+
? packageName.slice(1).split('/')
|
|
389
|
+
: packageName.split('/')
|
|
390
|
+
|
|
391
|
+
if (packageNameParts.length !== (packageName.startsWith('@') ? 2 : 1)) return false
|
|
392
|
+
|
|
393
|
+
return packageNameParts.every(
|
|
394
|
+
(part) => part.length > 0 && part !== '.' && part !== '..' && packageNamePartPattern.test(part),
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function isPathNotFoundError(
|
|
399
|
+
error: unknown,
|
|
400
|
+
): error is NodeJS.ErrnoException & { code: 'ENOENT' | 'ENOTDIR' } {
|
|
401
|
+
return (
|
|
402
|
+
error instanceof Error &&
|
|
403
|
+
'code' in error &&
|
|
404
|
+
((error as NodeJS.ErrnoException).code === 'ENOENT' ||
|
|
405
|
+
(error as NodeJS.ErrnoException).code === 'ENOTDIR')
|
|
406
|
+
)
|
|
407
|
+
}
|