Skip to content

Commit e5c3202

Browse files
committed
fix: resolve CDN metadata directory entries correctly
Only expose direct children from package directory metadata so nested files do not make Monaco probe nonexistent root entries like vue/index.d.ts or @babel/parser/index.js. Also ignore nested node_modules probes before they hit the CDN.
1 parent 82d5a98 commit e5c3202

File tree

2 files changed

+70
-30
lines changed

2 files changed

+70
-30
lines changed

src/monaco/resource.ts

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export function createNpmFileSystem(
5555

5656
return {
5757
async stat(uri) {
58-
const path = getCdnPath(uri)
58+
const path = normalizePath(getCdnPath(uri))
5959
if (path === undefined) {
6060
return
6161
}
@@ -70,14 +70,14 @@ export function createNpmFileSystem(
7070
return await _stat(path)
7171
},
7272
async readFile(uri) {
73-
const path = getCdnPath(uri)
73+
const path = normalizePath(getCdnPath(uri))
7474
if (path === undefined) {
7575
return
7676
}
7777
return await _readFile(path)
7878
},
7979
readDirectory(uri) {
80-
const path = getCdnPath(uri)
80+
const path = normalizePath(getCdnPath(uri))
8181
if (path === undefined) {
8282
return []
8383
}
@@ -86,6 +86,9 @@ export function createNpmFileSystem(
8686
}
8787

8888
async function _stat(path: string) {
89+
if (hasNodeModulesSegment(path)) {
90+
return
91+
}
8992
if (statCache.has(path)) {
9093
return {
9194
...statCache.get(path),
@@ -142,6 +145,9 @@ export function createNpmFileSystem(
142145
}
143146

144147
async function _readDirectory(path: string): Promise<[string, FileType][]> {
148+
if (hasNodeModulesSegment(path)) {
149+
return []
150+
}
145151
if (dirCache.has(path)) {
146152
return dirCache.get(path)!
147153
}
@@ -182,17 +188,7 @@ export function createNpmFileSystem(
182188
return []
183189
}
184190

185-
const result: [string, FileType][] = data.files.map((file) => {
186-
const type =
187-
file.type === 'directory'
188-
? (2 as FileType.Directory)
189-
: (1 as FileType.File)
190-
191-
const fullPath = file.path
192-
statCache.set(fullPath, { type })
193-
194-
return [_getNameFromPath(file.path), type]
195-
})
191+
const result = getDirectDirectoryEntries(path, pkgPath, data.files)
196192

197193
dirCache.set(path, result)
198194
return result
@@ -201,26 +197,53 @@ export function createNpmFileSystem(
201197
}
202198
}
203199

204-
function _getNameFromPath(path: string): string {
205-
if (!path) return ''
206-
207-
const trimmedPath = path.endsWith('/') ? path.slice(0, -1) : path
208-
209-
const lastSlashIndex = trimmedPath.lastIndexOf('/')
210-
211-
if (
212-
lastSlashIndex === -1 ||
213-
(lastSlashIndex === 0 && trimmedPath.length === 1)
214-
) {
215-
return trimmedPath
200+
function getDirectDirectoryEntries(
201+
path: string,
202+
pkgPath: string,
203+
files: {
204+
path: string
205+
type: 'file' | 'directory'
206+
size?: number
207+
}[],
208+
): [string, FileType][] {
209+
const entries = new Map<string, FileType>()
210+
const prefix = trimSlashes(pkgPath)
211+
212+
for (const file of files) {
213+
const isRootedPath = file.path.startsWith('/')
214+
const filePath = trimSlashes(file.path)
215+
if (!filePath) continue
216+
217+
const relativePath =
218+
prefix && filePath.startsWith(`${prefix}/`)
219+
? filePath.slice(prefix.length + 1)
220+
: prefix && isRootedPath
221+
? undefined
222+
: filePath
223+
224+
if (!relativePath) continue
225+
226+
const [name, ...rest] = relativePath.split('/')
227+
const type =
228+
rest.length > 0 || file.type === 'directory'
229+
? (2 as FileType.Directory)
230+
: (1 as FileType.File)
231+
232+
entries.set(name, type)
233+
statCache.set(joinPath(path, name), { type })
216234
}
217235

218-
return trimmedPath.slice(lastSlashIndex + 1)
236+
return [...entries]
219237
}
220238

221239
async function _readFile(path: string): Promise<string | undefined> {
222240
const [_modName, pkgName, _version, pkgFilePath] = resolvePackageName(path)
223-
if (!pkgName || !pkgFilePath || !(await isValidPackageName(pkgName))) {
241+
if (
242+
!pkgName ||
243+
!pkgFilePath ||
244+
hasNodeModulesSegment(pkgFilePath) ||
245+
!(await isValidPackageName(pkgName))
246+
) {
224247
return
225248
}
226249

@@ -245,9 +268,25 @@ export function createNpmFileSystem(
245268
return await fetchResults.get(path)!
246269
}
247270

271+
function hasNodeModulesSegment(path: string) {
272+
return path.split('/').includes('node_modules')
273+
}
274+
275+
function joinPath(base: string, path: string) {
276+
return [trimSlashes(base), trimSlashes(path)].filter(Boolean).join('/')
277+
}
278+
279+
function normalizePath(path: string | undefined) {
280+
return path === undefined ? undefined : trimSlashes(path)
281+
}
282+
283+
function trimSlashes(path: string) {
284+
return path.replace(/^\/+|\/+$/g, '')
285+
}
286+
248287
async function isValidPackageName(pkgName: string) {
249-
// ignore @aaa/node_modules
250-
if (pkgName.endsWith('/node_modules')) {
288+
// ignore nested node_modules probes like /node_modules/node_modules
289+
if (pkgName === 'node_modules' || pkgName.endsWith('/node_modules')) {
251290
return false
252291
}
253292
// hard code to skip known invalid package

src/transform.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ async function doCompileTemplate(
397397
`$1 ${fnName}`,
398398
)}` + `\n${COMP_IDENTIFIER}.${fnName} = ${fnName}`
399399

400+
// @ts-expect-error multiRoot in 3.6
400401
if(descriptor.vapor && !ssr) {
401402
code += `\n${COMP_IDENTIFIER}.__multiRoot = ${multiRoot}`
402403
}

0 commit comments

Comments
 (0)