Skip to content

Commit ddfe7f4

Browse files
authored
feat: Node.js Middleware support (#3018)
1 parent 4585d6b commit ddfe7f4

File tree

112 files changed

+3167
-1395
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+3167
-1395
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
node_modules/
22
dist/
33
.next
4+
.next-node-middleware
45
edge-runtime/vendor
56
# deno.json is ephemeral and generated for the purpose of vendoring remote modules in CI
67
tools/deno/deno.json

edge-runtime/lib/cjs.test.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { createRequire } from 'node:module'
2+
import { join, dirname, relative } from 'node:path'
3+
import { fileURLToPath } from 'node:url'
4+
5+
import { assertEquals } from 'https://deno.land/[email protected]/testing/asserts.ts'
6+
7+
import { registerCJSModules } from './cjs.ts'
8+
9+
type RequireResult = Record<string, string>
10+
11+
const localRequire = createRequire(import.meta.url)
12+
const realRequireResult = localRequire('./fixture/cjs/entry.js') as RequireResult
13+
14+
const fixtureRoot = new URL('./fixture/cjs/', import.meta.url)
15+
const virtualRoot = new URL('file:///virtual-root/index.mjs')
16+
17+
const fixtureRootPath = fileURLToPath(fixtureRoot)
18+
const virtualRootPath = dirname(fileURLToPath(virtualRoot))
19+
20+
// load fixture into virtual CJS
21+
const virtualModules = new Map<string, string>()
22+
const decoder = new TextDecoder('utf-8')
23+
async function addVirtualModulesFromDir(dir: string) {
24+
const dirUrl = new URL('./' + dir, fixtureRoot)
25+
26+
for await (const dirEntry of Deno.readDir(dirUrl)) {
27+
const relPath = join(dir, dirEntry.name)
28+
if (dirEntry.isDirectory) {
29+
await addVirtualModulesFromDir(relPath + '/')
30+
} else if (dirEntry.isFile) {
31+
const fileURL = new URL('./' + dirEntry.name, dirUrl)
32+
virtualModules.set(relPath, decoder.decode(await Deno.readFile(fileURL)))
33+
}
34+
}
35+
}
36+
37+
await addVirtualModulesFromDir('')
38+
registerCJSModules(virtualRoot, virtualModules)
39+
40+
const virtualRequire = createRequire(virtualRoot)
41+
const virtualRequireResult = virtualRequire('./entry.js') as RequireResult
42+
43+
const expectedVirtualRequireResult = {
44+
entry: '/virtual-root/entry.js',
45+
46+
packageExportsConditionsExportedModule:
47+
'/virtual-root/node_modules/package-exports-conditions/dist/exported-module.js',
48+
packageExportsConditionsRoot:
49+
'/virtual-root/node_modules/package-exports-conditions/root-export.js',
50+
packageExportsConditionsWildcardModuleNoExt:
51+
'/virtual-root/node_modules/package-exports-conditions/dist/wildcard/module.js',
52+
packageExportsConditionsWildcardModuleWithExt:
53+
'/virtual-root/node_modules/package-exports-conditions/dist/wildcard/module.js',
54+
packageExportsExportedModule:
55+
'/virtual-root/node_modules/package-exports/dist/exported-module.js',
56+
packageExportsMainRoot: '/virtual-root/node_modules/package-exports-main/root-export.js',
57+
packageExportsNotAllowedBecauseNotInExportMap: 'ERROR',
58+
packageExportsRoot: '/virtual-root/node_modules/package-exports/root-export.js',
59+
packageExportsSugarRoot: '/virtual-root/node_modules/package-exports-sugar/root-export.js',
60+
packageExportsWildcardModuleNoExt:
61+
'/virtual-root/node_modules/package-exports/dist/wildcard/module.js',
62+
packageExportsWildcardModuleWithExt:
63+
'/virtual-root/node_modules/package-exports/dist/wildcard/module.js',
64+
packageRoot: '/virtual-root/node_modules/package/index.js',
65+
packageInternalModule: '/virtual-root/node_modules/package/internal-module.js',
66+
packageMainRoot: '/virtual-root/node_modules/package-main/main.js',
67+
packageMainInternalModule: '/virtual-root/node_modules/package-main/internal-module.js',
68+
} as RequireResult
69+
70+
Deno.test('Virtual CJS Module loader matches real CJS Module loader', async (t) => {
71+
// make sure we collect all the possible keys to spot any cases of potentially missing keys in one of the objects
72+
const allTheKeys = [
73+
...new Set([
74+
...Object.keys(expectedVirtualRequireResult),
75+
...Object.keys(realRequireResult),
76+
...Object.keys(virtualRequireResult),
77+
]),
78+
]
79+
80+
function normalizeValue(value: string, basePath: string) {
81+
if (value === 'ERROR') {
82+
return value
83+
}
84+
85+
return relative(basePath, value)
86+
}
87+
88+
for (const key of allTheKeys) {
89+
const virtualValue = virtualRequireResult[key]
90+
const realValue = realRequireResult[key]
91+
92+
// values are filepaths or "ERROR" strings, "real" require has actual file system paths, virtual ones has virtual paths starting with file:///virtual-root/
93+
// we compare remaining paths to ensure same relative paths are reported indicating that resolution works the same in
94+
// in real CommonJS and simulated one
95+
assertEquals(
96+
normalizeValue(realValue, fixtureRootPath),
97+
normalizeValue(virtualValue, virtualRootPath),
98+
)
99+
}
100+
})
101+
102+
Deno.test('Virtual CJS Module loader matches expected results', async (t) => {
103+
// the main portion of testing functionality is in above assertions that compare real require and virtual one
104+
// below is additional explicit assertion mostly to make sure that test setup is correct
105+
assertEquals(virtualRequireResult, expectedVirtualRequireResult)
106+
})

0 commit comments

Comments
 (0)