UNPKG

@routineless/nx-aws-cdk

Version:
208 lines 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.build = exports.getOutExtension = void 0; const tslib_1 = require("tslib"); const devkit_1 = require("@nx/devkit"); const esbuild = tslib_1.__importStar(require("esbuild")); const fs_extra_1 = require("fs-extra"); const path = tslib_1.__importStar(require("path")); const esbuild_1 = require("../../../utils/esbuild"); const ast_1 = require("./ast"); const dependencies_1 = require("./dependencies"); const ESM_FILE_EXTENSION = '.mjs'; const CJS_FILE_EXTENSION = '.cjs'; const externalBundleName = 'external'; // workaround for esm bundling issue https://github.com/evanw/esbuild/pull/2067 const shimBanner = { js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);", }; const getOutExtension = (options) => { const userDefinedExt = options.userDefinedBuildOptions?.outExtension?.['.js']; // Allow users to change the output extensions from default CJS and ESM extensions. // CJS -> .js // ESM -> .js return userDefinedExt === '.js' ? '.js' : options.format === 'esm' ? ESM_FILE_EXTENSION : CJS_FILE_EXTENSION; }; exports.getOutExtension = getOutExtension; const build = async (options, context, dependencies = []) => { if (!options.bundle) { if (options.format === 'esm') { devkit_1.logger.warn(`The project ${context.projectName} is using bundle:false with esm format. This feature is experimental, fallback to bundle:true if you encountered any problems building or deploying your code.`); } else { throw new Error('Unbundled mode is only supported with esm format. Use bundle:true or format:esm.'); } } const { internal, external } = dependencies.reduce(dependencies_1.dependenciesReducer, { internal: [], external: [], }); const mainAppEsbuildOptions = buildMainAppEsbuildOptions(options, context, internal); const buildResult = await esbuild.build(mainAppEsbuildOptions); if (!options.bundle) { if (external.length > 0) { const depBuildResult = await buildDependencies(options, context, mainAppEsbuildOptions, internal); await Promise.all([ generateInternalPackageJson(options, mainAppEsbuildOptions, internal), applyGeneratedCodeModifications(options, depBuildResult.reexportingResult), ]); } else { await generateInternalPackageJson(options, mainAppEsbuildOptions, internal); } } if (options.metafile) { const filename = 'meta.json'; await (0, fs_extra_1.writeJson)((0, devkit_1.joinPathFragments)(options.outputPath, filename), buildResult.metafile); } return buildResult; }; exports.build = build; const buildBaseEsbuildOptions = (options) => { const esbuildOptions = { ...options.userDefinedBuildOptions, entryNames: options.outputHashing === 'all' ? '[dir]/[name].[hash]' : '[dir]/[name]', bundle: !!options.bundle, // Cannot use external without bundle option external: options.bundle ? [...(options.userDefinedBuildOptions?.external ?? []), ...options.external] : [], minify: !!options.minify, platform: options.platform, target: options.target, metafile: !!options.metafile, tsconfig: options.tsConfig, sourcemap: (options.sourcemap ?? options.userDefinedBuildOptions?.sourcemap) || false, format: options.format, }; return esbuildOptions; }; const buildMainAppEsbuildOptions = (options, context, internalDeps = []) => { const esbuildOptions = buildBaseEsbuildOptions(options); esbuildOptions.banner = options.bundle && options.format === 'esm' ? shimBanner : {}; const outExtension = (0, exports.getOutExtension)(options); esbuildOptions.outExtension = { '.js': outExtension, }; if (!esbuildOptions.outfile && !esbuildOptions.outdir) { if (options.singleEntry && options.bundle && !esbuildOptions.splitting) { esbuildOptions.outfile = getOutfile(options); } else { esbuildOptions.outdir = options.outputPath; } } const entryPoints = options.additionalEntryPoints ? [options.main, ...options.additionalEntryPoints] : [options.main]; if (options.bundle) { esbuildOptions.entryPoints = entryPoints; } else { const projectSourceRoot = context.projectsConfigurations.projects[context.projectName].sourceRoot; // gets all files used by initial entry points and defines them as entry points as well const mainAppEntryPoints = (0, esbuild_1.getEntryPoints)(context.projectName, context, { initialTsConfigFileName: options.tsConfig, recursive: false, }).map((entryPoint) => ({ in: entryPoint, out: entryPoint.replace(`${projectSourceRoot}/`, '').replace(path.extname(entryPoint), ''), })); const internalProjectsEntryPoints = internalDeps .flatMap((dep) => (0, esbuild_1.getEntryPoints)(dep.node.name, context).map((entryPoint) => ({ entryPoint, dep }))) .map((depEntryPoint) => ({ in: depEntryPoint.entryPoint, out: path.join('node_modules', depEntryPoint.dep.name, depEntryPoint.entryPoint .replace(depEntryPoint.dep.node.data.sourceRoot, '') .replace(path.extname(depEntryPoint.entryPoint), '')), })); esbuildOptions.entryPoints = [...mainAppEntryPoints, ...internalProjectsEntryPoints]; } return esbuildOptions; }; const buildDependencies = async (_options, context, mainAppEsbuildOptions, internal) => { const options = { ..._options, bundle: true }; const esbuildOptions = buildBaseEsbuildOptions(options); esbuildOptions.outdir = `${options.outputPath}/node_modules/${externalBundleName}`; esbuildOptions.banner = options.format === 'esm' ? shimBanner : {}; const reexportingResult = await generateReexportingEntryPoint(context, options, mainAppEsbuildOptions, internal); esbuildOptions.entryPoints = [reexportingResult.reexportingEntryPoint]; const buildResult = await esbuild.build(esbuildOptions); await generatePackageJson(externalBundleName, 'index.js', { ...esbuildOptions, outdir: options.outputPath }); return { buildResult, reexportingResult }; }; const generateInternalPackageJson = async (options, esbuildOptions, internal) => { const writePromises = []; for (const dep of internal) { writePromises.push(generatePackageJson(dep.name, `index${(0, exports.getOutExtension)(options)}`, esbuildOptions)); } return Promise.all(writePromises); }; const applyGeneratedCodeModifications = async (options, reexportingResult) => { const modifications = options.format === 'esm' ? { format: 'esm', importDeclaration: [ ast_1.modificationFactory.localImportsExtensionModification, ast_1.modificationFactory.createImportSpecifiersModification({ reexportedImports: reexportingResult.reexportedImports, reexportingModule: externalBundleName, }), ], exportAllDeclaration: [ast_1.modificationFactory.localImportsExtensionModification], exportNamedDeclaration: [ast_1.modificationFactory.localImportsExtensionModification], } : { format: 'cjs', callExpression: [ast_1.modificationFactory.localRequireExtensionModification] }; return (0, ast_1.applyModifications)(options.outputPath, modifications, `**/node_modules/${externalBundleName}/**`); }; const generatePackageJson = async (name, main, esbuildOptions) => { const packageJsonContent = { name: name, version: '1.0.0', type: esbuildOptions.format === 'esm' ? 'module' : 'commonjs', main: main, }; return (0, fs_extra_1.writeJson)((0, devkit_1.joinPathFragments)(esbuildOptions.outdir, 'node_modules', name, 'package.json'), packageJsonContent); }; const generateReexportingEntryPoint = async (context, options, mainAppEsbuildOptions, internal) => { const mainAppEntryPoints = mainAppEsbuildOptions.entryPoints.map((entryPoint) => entryPoint.in); const internalPackageNames = internal.map((dep) => dep.name); const collectedImports = await (0, ast_1.collectAllImports)(mainAppEntryPoints, [ '.', '.*', ...options.external, ...internalPackageNames, ]); const tmpPath = path.join(devkit_1.workspaceRoot, 'tmp', context.projectName, externalBundleName); const reexportingFile = path.join(tmpPath, `index.js`); await (0, fs_extra_1.mkdir)(tmpPath, { recursive: true }); const reexportingFileContent = getReexportingFileContent(collectedImports); await (0, fs_extra_1.writeFile)(reexportingFile, reexportingFileContent); return { reexportingEntryPoint: reexportingFile, reexportedImports: collectedImports }; }; const getReexportingFileContent = (imports) => { const exportStatements = []; for (const [importModule, importsAggregate] of imports) { if (importsAggregate.namespaceImport) { exportStatements.push(`export * as ${(0, ast_1.resolveReexportingName)(importModule, { namespace: true })} from '${importModule}'`); } if (importsAggregate.namedImports.size || importsAggregate.defaultImport) { const namedImports = Array.from(importsAggregate.namedImports) .map((namedImport) => `${namedImport} as ${(0, ast_1.resolveReexportingName)(importModule, { named: namedImport })}`) .join(', '); const defaultExport = importsAggregate.defaultImport ? `default as ${(0, ast_1.resolveReexportingName)(importModule, { default: true })}` : ''; const hasBoth = importsAggregate.namedImports.size && importsAggregate.defaultImport; const namedExports = `${hasBoth ? ', ' : ''}${namedImports}`; exportStatements.push(`export { ${defaultExport}${namedExports} } from '${importModule}'`); } else if (!importsAggregate.namespaceImport && importsAggregate.sideEffectImport) { exportStatements.push(`import '${importModule}'`); } } return exportStatements.join('\n'); }; const getOutfile = (options) => { const ext = (0, exports.getOutExtension)(options); const candidate = (0, devkit_1.joinPathFragments)(options.outputPath, options.outputFileName); const { dir, name } = path.parse(candidate); return `${dir}/${name}${ext}`; }; //# sourceMappingURL=esbuild-helper.js.map