-
Notifications
You must be signed in to change notification settings - Fork 35
feat: add pure ESM support to esbuild and default bundlers #1018
Changes from all commits
a468380
8e0c725
12df5fb
419c6c0
0e95b0f
2245a95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,12 @@ import { build, Metafile } from '@netlify/esbuild' | |
import { tmpName } from 'tmp-promise' | ||
|
||
import type { FunctionConfig } from '../../../../config.js' | ||
import { FeatureFlags } from '../../../../feature_flags.js' | ||
import { getPathWithExtension, safeUnlink } from '../../../../utils/fs.js' | ||
import type { RuntimeName } from '../../../runtime.js' | ||
import type { NodeBundlerName } from '../index.js' | ||
|
||
import { getBundlerTarget } from './bundler_target.js' | ||
import { getBundlerTarget, getModuleFormat } from './bundler_target.js' | ||
import { getDynamicImportsPlugin } from './plugin_dynamic_imports.js' | ||
import { getNativeModulesPlugin } from './plugin_native_modules.js' | ||
import { getNodeBuiltinPlugin } from './plugin_node_builtin.js' | ||
|
@@ -28,6 +29,7 @@ export const bundleJsFile = async function ({ | |
basePath, | ||
config, | ||
externalModules = [], | ||
featureFlags, | ||
ignoredModules = [], | ||
name, | ||
srcDir, | ||
|
@@ -37,6 +39,7 @@ export const bundleJsFile = async function ({ | |
basePath?: string | ||
config: FunctionConfig | ||
externalModules: string[] | ||
featureFlags: FeatureFlags | ||
ignoredModules: string[] | ||
name: string | ||
srcDir: string | ||
|
@@ -84,11 +87,21 @@ export const bundleJsFile = async function ({ | |
// URLs, not paths, so even on Windows they should use forward slashes. | ||
const sourceRoot = targetDirectory.replace(/\\/g, '/') | ||
|
||
// Configuring the output format of esbuild. The `includedFiles` array we get | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We were not including a |
||
// here contains additional paths to include with the bundle, like the path | ||
// to a `package.json` with {"type": "module"} in case of an ESM function. | ||
const { includedFiles: includedFilesFromModuleDetection, moduleFormat } = await getModuleFormat( | ||
srcDir, | ||
featureFlags, | ||
config.nodeVersion, | ||
) | ||
|
||
try { | ||
const { metafile = { inputs: {}, outputs: {} }, warnings } = await build({ | ||
bundle: true, | ||
entryPoints: [srcFile], | ||
external, | ||
format: moduleFormat, | ||
logLevel: 'warning', | ||
logLimit: ESBUILD_LOG_LIMIT, | ||
metafile: true, | ||
|
@@ -108,12 +121,14 @@ export const bundleJsFile = async function ({ | |
}) | ||
const inputs = Object.keys(metafile.inputs).map((path) => resolve(path)) | ||
const cleanTempFiles = getCleanupFunction([...bundlePaths.keys()]) | ||
const additionalPaths = [...dynamicImportsIncludedPaths, ...includedFilesFromModuleDetection] | ||
|
||
return { | ||
additionalPaths: [...dynamicImportsIncludedPaths], | ||
additionalPaths, | ||
bundlePaths, | ||
cleanTempFiles, | ||
inputs, | ||
moduleFormat, | ||
nativeNodeModules, | ||
nodeModulesWithDynamicImports: [...nodeModulesWithDynamicImports], | ||
warnings, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
const DEFAULT_VERSION = 'node12' | ||
import { FeatureFlags } from '../../../../feature_flags' | ||
import { ModuleFormat } from '../../utils/module_format' | ||
import { DEFAULT_NODE_VERSION, getNodeSupportMatrix } from '../../utils/node_version' | ||
import { getClosestPackageJson } from '../../utils/package_json' | ||
|
||
const versionMap = { | ||
'8.x': 'node8', | ||
|
@@ -10,18 +13,41 @@ const versionMap = { | |
type VersionKeys = keyof typeof versionMap | ||
type VersionValues = typeof versionMap[VersionKeys] | ||
|
||
export const getBundlerTarget = (suppliedVersion?: string): VersionValues => { | ||
const getBundlerTarget = (suppliedVersion?: string): VersionValues => { | ||
const version = normalizeVersion(suppliedVersion) | ||
|
||
if (version && version in versionMap) { | ||
return versionMap[version as VersionKeys] | ||
} | ||
|
||
return DEFAULT_VERSION | ||
return versionMap[`${DEFAULT_NODE_VERSION}.x`] | ||
} | ||
|
||
const getModuleFormat = async ( | ||
srcDir: string, | ||
featureFlags: FeatureFlags, | ||
configVersion?: string, | ||
): Promise<{ includedFiles: string[]; moduleFormat: ModuleFormat }> => { | ||
const packageJsonFile = await getClosestPackageJson(srcDir) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why we introduce + use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, somehow missed this reply. agree! I'll work on that. |
||
const nodeSupport = getNodeSupportMatrix(configVersion) | ||
|
||
if (featureFlags.zisi_pure_esm && packageJsonFile?.contents.type === 'module' && nodeSupport.esm) { | ||
return { | ||
includedFiles: [packageJsonFile.path], | ||
moduleFormat: 'esm', | ||
} | ||
} | ||
|
||
return { | ||
includedFiles: [], | ||
moduleFormat: 'cjs', | ||
} | ||
} | ||
|
||
const normalizeVersion = (version?: string) => { | ||
const match = version && version.match(/^nodejs(.*)$/) | ||
|
||
return match ? match[1] : version | ||
} | ||
|
||
export { getBundlerTarget, getModuleFormat } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This determines whether we'll look at a
.js
file and figure out whether it's CJS or ESM.