diff --git a/package-lock.json b/package-lock.json index 271b433583..4e05e67f26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22350,6 +22350,19 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/parse-imports": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", + "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==", + "license": "Apache-2.0 AND MIT", + "dependencies": { + "es-module-lexer": "^1.5.3", + "slashes": "^3.0.12" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/parse-json": { "version": "5.2.0", "license": "MIT", @@ -24201,6 +24214,12 @@ "node": ">=8" } }, + "node_modules/slashes": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", + "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", + "license": "ISC" + }, "node_modules/slice-ansi": { "version": "5.0.0", "dev": true, @@ -28312,7 +28331,6 @@ "license": "MIT", "dependencies": { "@import-maps/resolve": "^1.0.1", - "@vercel/nft": "0.27.7", "ajv": "^8.11.2", "ajv-errors": "^3.0.0", "better-ajv-errors": "^1.2.0", @@ -28328,6 +28346,7 @@ "node-stream-zip": "^1.15.0", "p-retry": "^5.1.1", "p-wait-for": "^5.0.0", + "parse-imports": "^2.2.1", "path-key": "^4.0.0", "semver": "^7.3.8", "tmp-promise": "^3.0.3", diff --git a/packages/build/tests/monitor/snapshots/tests.js.md b/packages/build/tests/monitor/snapshots/tests.js.md index 3830a8a710..061d23b038 100644 --- a/packages/build/tests/monitor/snapshots/tests.js.md +++ b/packages/build/tests/monitor/snapshots/tests.js.md @@ -1588,7 +1588,7 @@ Generated by [AVA](https://avajs.dev). ────────────────────────────────────────────────────────────────␊ ␊ Error message␊ - Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {"basePath":"packages/build/tests/monitor/fixtures/edge_function_error","destPath":"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip","externals":[],"functions":[{"name":"trouble","path":"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],"importMapData":"{/"imports/":{/"builtins/":/"node:builtins/",/"@netlify/edge-functions/":/"https://edge.netlify.com/v1.0.0/index.ts/",/"netlify:edge/":/"https://edge.netlify.com/v1.0.0/index.ts?v=legacy/"},/"scopes/":{}}","vendorDirectory":"/external/path"}␊ + Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {"basePath":"packages/build/tests/monitor/fixtures/edge_function_error","destPath":"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip","externals":[],"functions":[{"name":"trouble","path":"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],"importMapData":"{/"imports/":{/"@netlify/edge-functions/":/"https://edge.netlify.com/v1.0.0/index.ts/",/"netlify:edge/":/"https://edge.netlify.com/v1.0.0/index.ts?v=legacy/"},/"scopes/":{}}"}␊ error: Uncaught (in promise) Error: Error: Could not find file: packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/file.ts␊ const ret = new Error(getStringFromWasm0(arg0, arg1));␊ ^␊ @@ -1606,9 +1606,9 @@ Generated by [AVA](https://avajs.dev). Error monitoring payload:␊ {␊ "errorClass": "functionsBundling",␊ - "errorMessage": "Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {/"basePath/":/"packages/build/tests/monitor/fixtures/edge_function_error",/"destPath/":/"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip",/"externals/":[],/"functions/":[{/"name/":/"trouble/",/"path/":/"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],/"importMapData/":/"{//"imports//":{//"builtins//"://"node:builtins//",//"@netlify/edge-functions//"://"https://edge.netlify.com/v1.0.0/index.ts//",//"netlify:edge//"://"https://edge.netlify.com/v1.0.0/index.ts?v=legacy//"},//"scopes//":{}}/",/"vendorDirectory/":/"/external/path"}/nerror: Uncaught (in promise) Error: Error: Could not find file: packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/file.ts/n const ret = new Error(getStringFromWasm0(arg0, arg1));/n ^/n at __wbg_new_HEXADECIMAL_ID (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at (wasm://wasm/HEXADECIMAL_ID:1:80)/n at (wasm://wasm/HEXADECIMAL_ID:1:80)/n at (wasm://wasm/HEXADECIMAL_ID:1:80)/n at __wbg_adapter_40 (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:8)/n at real (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at eventLoopTick (ext:core/01_core.js:80:7)",␊ + "errorMessage": "Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {/"basePath/":/"packages/build/tests/monitor/fixtures/edge_function_error",/"destPath/":/"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip",/"externals/":[],/"functions/":[{/"name/":/"trouble/",/"path/":/"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],/"importMapData/":/"{//"imports//":{//"@netlify/edge-functions//"://"https://edge.netlify.com/v1.0.0/index.ts//",//"netlify:edge//"://"https://edge.netlify.com/v1.0.0/index.ts?v=legacy//"},//"scopes//":{}}/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/file.ts/n const ret = new Error(getStringFromWasm0(arg0, arg1));/n ^/n at __wbg_new_HEXADECIMAL_ID (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at (wasm://wasm/HEXADECIMAL_ID:1:80)/n at (wasm://wasm/HEXADECIMAL_ID:1:80)/n at (wasm://wasm/HEXADECIMAL_ID:1:80)/n at __wbg_adapter_40 (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:8)/n at real (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at eventLoopTick (ext:core/01_core.js:80:7)",␊ "context": "Bundling of edge function failed",␊ - "groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/",/"/":/"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at /external/path at /external/path at /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path",␊ + "groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at /external/path at /external/path at /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path",␊ "severity": "info",␊ "unhandled": false,␊ "location": {␊ @@ -1619,7 +1619,7 @@ Generated by [AVA](https://avajs.dev). "pluginPackageJson": false,␊ "BUILD_ID": "0",␊ "other": {␊ - "groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/",/"/":/"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at /external/path at /external/path at /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path"␊ + "groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at /external/path at /external/path at /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path"␊ }␊ }` diff --git a/packages/build/tests/monitor/snapshots/tests.js.snap b/packages/build/tests/monitor/snapshots/tests.js.snap index c1aee46f95..00538540fa 100644 Binary files a/packages/build/tests/monitor/snapshots/tests.js.snap and b/packages/build/tests/monitor/snapshots/tests.js.snap differ diff --git a/packages/edge-bundler/node/bundler.test.ts b/packages/edge-bundler/node/bundler.test.ts index 4358bd7277..0883ac6396 100644 --- a/packages/edge-bundler/node/bundler.test.ts +++ b/packages/edge-bundler/node/bundler.test.ts @@ -506,6 +506,33 @@ test('Loads npm modules from bare specifiers', async () => { await rm(vendorDirectory.path, { force: true, recursive: true }) }) +test('Loads npm modules which use package.json.exports', async () => { + const { basePath, cleanup, distPath } = await useFixture('imports_npm_module_exports') + const sourceDirectory = join(basePath, 'functions') + const declarations: Declaration[] = [ + { + function: 'func1', + path: '/func1', + }, + ] + const vendorDirectory = await tmp.dir() + + await bundle([sourceDirectory], distPath, declarations, { + basePath, + vendorDirectory: vendorDirectory.path, + }) + + const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8') + const manifest = JSON.parse(manifestFile) + const bundlePath = join(distPath, manifest.bundles[0].asset) + const { func1 } = await runESZIP(bundlePath, vendorDirectory.path) + + expect(func1).toBe('hello') + + await cleanup() + await rm(vendorDirectory.path, { force: true, recursive: true }) +}) + test('Loads npm modules in a monorepo setup', async () => { const systemLogger = vi.fn() const { basePath: rootPath, cleanup, distPath } = await useFixture('monorepo_npm_module') diff --git a/packages/edge-bundler/node/npm_dependencies.ts b/packages/edge-bundler/node/npm_dependencies.ts index f3e4aa6cbc..e57af5fd14 100644 --- a/packages/edge-bundler/node/npm_dependencies.ts +++ b/packages/edge-bundler/node/npm_dependencies.ts @@ -4,10 +4,9 @@ import path from 'path' import { fileURLToPath, pathToFileURL } from 'url' import { resolve, ParsedImportMap } from '@import-maps/resolve' -import { nodeFileTrace, resolve as nftResolve } from '@vercel/nft' import { build } from 'esbuild' import { findUp } from 'find-up' -import getPackageName from 'get-package-name' +import { parseImports } from 'parse-imports' import tmp from 'tmp-promise' import { ImportMap } from './import_map.js' @@ -16,6 +15,10 @@ import { pathsBetween } from './utils/fs.js' const TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.tsx', '.cts', '.ctsx', '.mts', '.mtsx']) +const slugifyFileName = (specifier: string) => { + return specifier.replace(/\//g, '_') +} + const slugifyPackageName = (specifier: string) => { if (!specifier.startsWith('@')) return specifier const [scope, pkg] = specifier.split('/') @@ -70,9 +73,20 @@ const getTypesPath = async (packageJsonPath: string): Promise => { +function packageName(specifier: string) { + if (!specifier.startsWith('@')) return specifier.split('/')[0] + const [scope, pkg] = specifier.split('/') + return `${scope}/${pkg}` +} + +const safelyDetectTypes = async (pkg: string, basePath: string): Promise => { try { - return await getTypesPath(packageJsonPath) + const json = await findUp(`node_modules/${packageName(pkg)}/package.json`, { + cwd: basePath, + }) + if (json) { + return await getTypesPath(json) + } } catch { return undefined } @@ -105,101 +119,91 @@ interface GetNPMSpecifiersOptions { rootPath: string } +async function compileTypeScript(file: string): Promise { + const compiled = await build({ + bundle: false, + entryPoints: [file], + logLevel: 'silent', + platform: 'node', + write: false, + }) + + return compiled.outputFiles[0].text +} + +async function parseImportsForFile(file: string, rootPath: string) { + const source = TYPESCRIPT_EXTENSIONS.has(path.extname(file)) + ? await compileTypeScript(file) + : await fs.readFile(file, 'utf-8') + + return await parseImports(source, { + resolveFrom: rootPath, + }) +} + /** * Parses a set of functions and returns a list of specifiers that correspond * to npm modules. */ const getNPMSpecifiers = async ({ basePath, functions, importMap, environment, rootPath }: GetNPMSpecifiersOptions) => { const baseURL = pathToFileURL(basePath) - const { reasons } = await nodeFileTrace(functions, { - base: rootPath, - processCwd: basePath, - readFile: async (filePath: string) => { - // If this is a TypeScript file, we need to compile in before we can - // parse it. - if (TYPESCRIPT_EXTENSIONS.has(path.extname(filePath))) { - const compiled = await build({ - bundle: false, - entryPoints: [filePath], - logLevel: 'silent', - platform: 'node', - write: false, - }) - - return compiled.outputFiles[0].text - } - - return fs.readFile(filePath, 'utf8') - }, - resolve: async (specifier, ...args) => { - // Start by checking whether the specifier matches any import map defined - // by the user. - const { matched, resolvedImport } = resolve(specifier, importMap, baseURL) - - // If it does, the resolved import is the specifier we'll evaluate going - // forward. - if (matched && resolvedImport.protocol === 'file:') { - const newSpecifier = fileURLToPath(resolvedImport).replace(/\\/g, '/') - - return nftResolve(newSpecifier, ...args) - } - - return nftResolve(specifier, ...args) - }, - }) const npmSpecifiers: { specifier: string; types?: string }[] = [] - const npmSpecifiersWithExtraneousFiles = new Set() - - for (const [filePath, reason] of reasons.entries()) { - const parents = [...reason.parents] - const isExtraneousFile = reason.type.every((type) => type === 'asset') - - // An extraneous file is a dependency that was traced by NFT and marked - // as not being statically imported. We can't process dynamic importing - // at runtime, so we gather the list of modules that may use these files - // so that we can warn users about this caveat. - if (isExtraneousFile) { - parents.forEach((path) => { - const specifier = getPackageName(path) - - if (specifier) { - npmSpecifiersWithExtraneousFiles.add(specifier) + for (const func of functions) { + const imports = await parseImportsForFile(func, rootPath) + + for (const i of imports) { + // The non-null assertion is required because typescript can not infer that `moduleSpecifier.value` can be narrowed to a string. + // The narrowing is possible because `moduleSpecifier.value` will always be a string when `moduleSpecifier.isConstant` is true. + const specifier = i.moduleSpecifier.isConstant ? i.moduleSpecifier.value! : i.moduleSpecifier.code + switch (i.moduleSpecifier.type) { + case 'absolute': { + npmSpecifiers.push( + ...(await getNPMSpecifiers({ basePath, functions: [specifier], importMap, environment, rootPath })), + ) + break } - }) - } - - // every dependency will have its `package.json` in `reasons` exactly once. - // by only looking at this file, we save us from doing duplicate work. - const isPackageJson = filePath.endsWith('package.json') - if (!isPackageJson) continue - - const packageName = getPackageName(filePath) - if (packageName === undefined) continue - - const isDirectDependency = parents.some((parentPath) => { - if (!parentPath.startsWith(`node_modules${path.sep}`)) return true - // typically, edge functions have no direct dependency on the `package.json` of a module. - // it's the impl files that depend on `package.json`, so we need to check the parents of - // the `package.json` file as well to see if the module is a direct dependency. - const parents = [...(reasons.get(parentPath)?.parents ?? [])] - return parents.some((parentPath) => !parentPath.startsWith(`node_modules${path.sep}`)) - }) - - // We're only interested in capturing the specifiers that are first-level - // dependencies. Because we'll bundle all modules in a subsequent step, - // any transitive dependencies will be handled then. - if (isDirectDependency) { - npmSpecifiers.push({ - specifier: packageName, - types: environment === 'development' ? await safelyDetectTypes(path.join(basePath, filePath)) : undefined, - }) + case 'relative': { + const filePath = path.join(path.dirname(func), specifier) + npmSpecifiers.push( + ...(await getNPMSpecifiers({ basePath, functions: [filePath], importMap, environment, rootPath })), + ) + break + } + case 'package': { + // node: prefixed imports are detected as packages instead of as builtins + // we don't want to try and bundle builtins so we ignore node: prefixed imports + if (specifier.startsWith('node:')) { + break + } + + const { matched, resolvedImport } = resolve(specifier, importMap, baseURL) + if (matched) { + if (resolvedImport.protocol === 'file:') { + const newSpecifier = fileURLToPath(resolvedImport).replace(/\\/g, '/') + npmSpecifiers.push( + ...(await getNPMSpecifiers({ basePath, functions: [newSpecifier], importMap, environment, rootPath })), + ) + } + } else if (!resolvedImport?.protocol?.startsWith('http')) { + const t = await safelyDetectTypes(specifier, basePath) + npmSpecifiers.push({ + specifier: specifier, + types: t, + }) + } + break + } + case 'builtin': + case 'invalid': + case 'unknown': { + // We don't bundle these types of modules + break + } + } } } - return { - npmSpecifiers, - npmSpecifiersWithExtraneousFiles: [...npmSpecifiersWithExtraneousFiles], - } + return npmSpecifiers } interface VendorNPMSpecifiersOptions { @@ -231,7 +235,7 @@ export const vendorNPMSpecifiers = async ({ // Otherwise, create a random temporary directory. const temporaryDirectory = directory ? { path: directory } : await tmp.dir() - const { npmSpecifiers, npmSpecifiersWithExtraneousFiles } = await getNPMSpecifiers({ + const npmSpecifiers = await getNPMSpecifiers({ basePath, functions, importMap: importMap.getContentsWithURLObjects(), @@ -244,8 +248,8 @@ export const vendorNPMSpecifiers = async ({ // specifier, and each of these files will become entry points to esbuild. const ops = await Promise.all( npmSpecifiers.map(async ({ specifier, types }) => { - const code = `import * as mod from "${specifier}"; export default mod.default; export * from "${specifier}";` - const filePath = path.join(temporaryDirectory.path, `bundled-${slugifyPackageName(specifier)}.js`) + const code = `import * as mod from "${specifier}";\nexport default mod.default;\nexport * from "${specifier}";` + const filePath = path.join(temporaryDirectory.path, `bundled-${slugifyFileName(specifier)}.js`) await fs.writeFile(filePath, code) @@ -341,7 +345,6 @@ export const vendorNPMSpecifiers = async ({ cleanup, directory: temporaryDirectory.path, importMap: newImportMap, - npmSpecifiersWithExtraneousFiles, outputFiles, } } diff --git a/packages/edge-bundler/node/server/server.test.ts b/packages/edge-bundler/node/server/server.test.ts index 3ce5816bc3..53d892b86a 100644 --- a/packages/edge-bundler/node/server/server.test.ts +++ b/packages/edge-bundler/node/server/server.test.ts @@ -47,7 +47,7 @@ test('Starts a server and serves requests for edge functions', async () => { importMapPaths, } - const { features, functionsConfig, graph, success, npmSpecifiersWithExtraneousFiles } = await server( + const { features, functionsConfig, graph, success } = await server( functions, { very_secret_secret: 'i love netlify', @@ -57,7 +57,6 @@ test('Starts a server and serves requests for edge functions', async () => { expect(features).toEqual({ npmModules: true }) expect(success).toBe(true) expect(functionsConfig).toEqual([{ path: '/my-function' }, {}, { path: '/global-netlify' }]) - expect(npmSpecifiersWithExtraneousFiles).toEqual(['dictionary']) const modules = graph?.modules.filter(({ kind, mediaType }) => kind === 'esm' && mediaType === 'TypeScript') for (const key in functions) { @@ -101,7 +100,7 @@ test('Starts a server and serves requests for edge functions', async () => { const idBarrelFile = await readFile(join(servePath, 'bundled-id.js'), 'utf-8') expect(idBarrelFile).toContain(`/// `) - const identidadeBarrelFile = await readFile(join(servePath, 'bundled-pt-committee__identidade.js'), 'utf-8') + const identidadeBarrelFile = await readFile(join(servePath, 'bundled-@pt-committee_identidade.js'), 'utf-8') expect(identidadeBarrelFile).toContain( `/// `, ) @@ -139,7 +138,7 @@ test('Serves edge functions in a monorepo setup', async () => { importMapPaths, } - const { features, functionsConfig, graph, success, npmSpecifiersWithExtraneousFiles } = await server( + const { features, functionsConfig, graph, success } = await server( functions, { very_secret_secret: 'i love netlify', @@ -150,7 +149,6 @@ test('Serves edge functions in a monorepo setup', async () => { expect(features).toEqual({ npmModules: true }) expect(success).toBe(true) expect(functionsConfig).toEqual([{ path: '/func1' }]) - expect(npmSpecifiersWithExtraneousFiles).toEqual(['child-1']) for (const key in functions) { const graphEntry = graph?.modules.some( diff --git a/packages/edge-bundler/node/server/server.ts b/packages/edge-bundler/node/server/server.ts index 0ec1a402e2..43f420bcc8 100644 --- a/packages/edge-bundler/node/server/server.ts +++ b/packages/edge-bundler/node/server/server.ts @@ -97,8 +97,6 @@ const prepareServer = ({ const importMap = new ImportMap() await importMap.addFiles(options.importMapPaths ?? [], logger) - const npmSpecifiersWithExtraneousFiles: string[] = [] - // we keep track of the files that are relevant to the user's code, so we can clean up leftovers from past executions later const relevantFiles = [stage2Path] @@ -115,7 +113,6 @@ const prepareServer = ({ if (vendor) { features.npmModules = true importMap.add(vendor.importMap) - npmSpecifiersWithExtraneousFiles.push(...vendor.npmSpecifiersWithExtraneousFiles) relevantFiles.push(...vendor.outputFiles) } @@ -165,7 +162,6 @@ const prepareServer = ({ features, functionsConfig, graph, - npmSpecifiersWithExtraneousFiles, success, } } diff --git a/packages/edge-bundler/package.json b/packages/edge-bundler/package.json index 515b29c3de..56920acfb7 100644 --- a/packages/edge-bundler/package.json +++ b/packages/edge-bundler/package.json @@ -58,7 +58,6 @@ }, "dependencies": { "@import-maps/resolve": "^1.0.1", - "@vercel/nft": "0.27.7", "ajv": "^8.11.2", "ajv-errors": "^3.0.0", "better-ajv-errors": "^1.2.0", @@ -74,6 +73,7 @@ "node-stream-zip": "^1.15.0", "p-retry": "^5.1.1", "p-wait-for": "^5.0.0", + "parse-imports": "^2.2.1", "path-key": "^4.0.0", "semver": "^7.3.8", "tmp-promise": "^3.0.3", diff --git a/packages/edge-bundler/test/fixtures/imports_npm_module_exports/functions/func1.ts b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/functions/func1.ts new file mode 100644 index 0000000000..c25fc996c3 --- /dev/null +++ b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/functions/func1.ts @@ -0,0 +1,5 @@ +import hello from '@secret/magic/sub-path' + +export default async () => { + return new Response(hello()) +} diff --git a/packages/edge-bundler/test/fixtures/imports_npm_module_exports/node_modules/@secret/magic/dist/hello.js b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/node_modules/@secret/magic/dist/hello.js new file mode 100644 index 0000000000..85e31430e8 --- /dev/null +++ b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/node_modules/@secret/magic/dist/hello.js @@ -0,0 +1,3 @@ +export default function hello() { + return 'hello' +} \ No newline at end of file diff --git a/packages/edge-bundler/test/fixtures/imports_npm_module_exports/node_modules/@secret/magic/package.json b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/node_modules/@secret/magic/package.json new file mode 100644 index 0000000000..d68422a1bc --- /dev/null +++ b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/node_modules/@secret/magic/package.json @@ -0,0 +1,6 @@ +{ + "name": "@secret/magic", + "exports": { + "./sub-path": "./dist/hello.js" + } +} \ No newline at end of file diff --git a/packages/edge-bundler/test/fixtures/imports_npm_module_exports/package.json b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/package.json new file mode 100644 index 0000000000..3dbc1ca591 --- /dev/null +++ b/packages/edge-bundler/test/fixtures/imports_npm_module_exports/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +}