Skip to content

Commit 9cabcc2

Browse files
committed
fix: support package.json#main in CJS simulation
1 parent b217456 commit 9cabcc2

File tree

1 file changed

+62
-18
lines changed

1 file changed

+62
-18
lines changed

edge-runtime/lib/cjs.ts

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,66 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
66
type RegisteredModule = {
77
source: string
88
loaded: boolean
9-
filename: string
9+
filepath: string
10+
// lazily parsed json string
11+
parsedJson?: any
1012
}
1113
const registeredModules = new Map<string, RegisteredModule>()
1214

1315
const require = createRequire(import.meta.url)
1416

1517
let hookedIn = false
1618

19+
function parseJson(matchedModule: RegisteredModule) {
20+
if (matchedModule.parsedJson) {
21+
return matchedModule.parsedJson
22+
}
23+
24+
try {
25+
const jsonContent = JSON.parse(matchedModule.source)
26+
matchedModule.parsedJson = jsonContent
27+
return jsonContent
28+
} catch (error) {
29+
throw new Error(`Failed to parse JSON module: ${matchedModule.filepath}`, { cause: error })
30+
}
31+
}
32+
1733
function seedCJSModuleCacheAndReturnTarget(matchedModule: RegisteredModule, parent: Module) {
1834
if (matchedModule.loaded) {
19-
return matchedModule.filename
35+
return matchedModule.filepath
2036
}
21-
const { source, filename } = matchedModule
37+
const { source, filepath } = matchedModule
2238

23-
const mod = new Module(filename)
39+
const mod = new Module(filepath)
2440
mod.parent = parent
25-
mod.filename = filename
26-
mod.path = dirname(filename)
41+
mod.filename = filepath
42+
mod.path = dirname(filepath)
2743
// @ts-expect-error - private untyped API
2844
mod.paths = Module._nodeModulePaths(mod.path)
29-
require.cache[filename] = mod
45+
require.cache[filepath] = mod
3046

31-
const wrappedSource = `(function (exports, require, module, __filename, __dirname) { ${source}\n});`
3247
try {
33-
const compiled = vm.runInThisContext(wrappedSource, {
34-
filename,
35-
lineOffset: 0,
36-
displayErrors: true,
37-
})
38-
compiled(mod.exports, createRequire(pathToFileURL(filename)), mod, filename, dirname(filename))
48+
if (filepath.endsWith('.json')) {
49+
Object.assign(mod.exports, parseJson(matchedModule))
50+
} else {
51+
const wrappedSource = `(function (exports, require, module, __filename, __dirname) { ${source}\n});`
52+
const compiled = vm.runInThisContext(wrappedSource, {
53+
filename: filepath,
54+
lineOffset: 0,
55+
displayErrors: true,
56+
})
57+
const modRequire = createRequire(pathToFileURL(filepath))
58+
compiled(mod.exports, modRequire, mod, filepath, dirname(filepath))
59+
}
3960
mod.loaded = matchedModule.loaded = true
4061
} catch (error) {
41-
throw new Error(`Failed to compile CJS module: ${filename}`, { cause: error })
62+
throw new Error(`Failed to compile CJS module: ${filepath}`, { cause: error })
4263
}
4364

44-
return filename
65+
return filepath
4566
}
4667

68+
// ideally require.extensions could be used, but it does NOT include '.cjs', so hardcoding instead
4769
const exts = ['.js', '.cjs', '.json']
4870

4971
function tryWithExtensions(filename: string) {
@@ -80,7 +102,7 @@ export function registerCJSModules(baseUrl: URL, modules: Map<string, string>) {
80102
for (const [filename, source] of modules.entries()) {
81103
const target = join(basePath, filename)
82104

83-
registeredModules.set(target, { source, loaded: false, filename: target })
105+
registeredModules.set(target, { source, loaded: false, filepath: target })
84106
}
85107

86108
if (!hookedIn) {
@@ -101,8 +123,30 @@ export function registerCJSModules(baseUrl: URL, modules: Map<string, string>) {
101123
let matchedModule = tryMatchingWithIndex(target)
102124

103125
if (!isRelative && !target.startsWith('/')) {
126+
const packageName = target.startsWith('@')
127+
? target.split('/').slice(0, 2).join('/')
128+
: target.split('/')[0]
129+
const moduleInPackagePath = target.slice(packageName.length + 1)
130+
104131
for (const nodeModulePaths of args[1].paths) {
105-
const potentialPath = join(nodeModulePaths, target)
132+
const potentialPackageJson = join(nodeModulePaths, packageName, 'package.json')
133+
134+
const maybePackageJson = registeredModules.get(potentialPackageJson)
135+
136+
let relativeTarget = moduleInPackagePath
137+
138+
let pkgJson: any = null
139+
if (maybePackageJson) {
140+
pkgJson = parseJson(maybePackageJson)
141+
142+
// TODO: exports and anything else like that
143+
if (moduleInPackagePath.length === 0 && pkgJson.main) {
144+
relativeTarget = pkgJson.main
145+
}
146+
}
147+
148+
const potentialPath = join(nodeModulePaths, packageName, relativeTarget)
149+
106150
matchedModule = tryMatchingWithIndex(potentialPath)
107151
if (matchedModule) {
108152
break

0 commit comments

Comments
 (0)