@routineless/nx-aws-cdk
Version:
Nx plugin for AWS CDK
208 lines • 10.7 kB
JavaScript
;
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