aws-lambda-nodejs-esbuild
Version:
λ💨 AWS CDK Construct to bundle JavaScript and TypeScript AWS lambdas using extremely fast esbuild
152 lines (151 loc) • 7.62 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.packExternalModules = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const ramda_1 = require("ramda");
const Packagers = __importStar(require("./packagers"));
function rebaseFileReferences(pathToPackageRoot, moduleVersion) {
if (/^(?:file:[^/]{2}|\.\/|\.\.\/)/.test(moduleVersion)) {
const filePath = ramda_1.replace(/^file:/, '', moduleVersion);
return ramda_1.replace(/\\/g, '/', `${moduleVersion.startsWith('file:') ? 'file:' : ''}${pathToPackageRoot}/${filePath}`);
}
return moduleVersion;
}
/**
* Add the given modules to a package json's dependencies.
*/
function addModulesToPackageJson(externalModules, packageJson, pathToPackageRoot) {
ramda_1.forEach(externalModule => {
var _a;
const splitModule = externalModule.split('@');
// If we have a scoped module we have to re-add the @
if (externalModule.startsWith('@')) {
splitModule.splice(0, 1);
splitModule[0] = '@' + splitModule[0];
}
let moduleVersion = ramda_1.join('@', ramda_1.tail(splitModule));
// We have to rebase file references to the target package.json
moduleVersion = rebaseFileReferences(pathToPackageRoot, moduleVersion);
packageJson.dependencies = packageJson.dependencies || {};
packageJson.dependencies[(_a = ramda_1.head(splitModule)) !== null && _a !== void 0 ? _a : ''] = moduleVersion;
}, externalModules);
}
/**
* Resolve the needed versions of production dependencies for external modules.
*/
function getProdModules(externalModules, packageJsonPath) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require(packageJsonPath);
const prodModules = [];
// only process the module stated in dependencies section
if (!packageJson.dependencies) {
return [];
}
// Get versions of all transient modules
ramda_1.forEach(externalModule => {
const moduleVersion = packageJson.dependencies[externalModule.external];
if (moduleVersion) {
prodModules.push(`${externalModule.external}@${moduleVersion}`);
// Check if the module has any peer dependencies and include them too
try {
const modulePackagePath = path.join(path.dirname(packageJsonPath), 'node_modules', externalModule.external, 'package.json');
const peerDependencies = require(modulePackagePath).peerDependencies;
if (!ramda_1.isEmpty(peerDependencies)) {
const peerModules = getProdModules(ramda_1.compose(ramda_1.map(([external]) => ({ external })), ramda_1.toPairs)(peerDependencies), packageJsonPath);
Array.prototype.push.apply(prodModules, peerModules);
}
}
catch (e) {
console.log(`WARNING: Could not check for peer dependencies of ${externalModule.external}`);
}
}
else {
if (!packageJson.devDependencies || !packageJson.devDependencies[externalModule.external]) {
prodModules.push(externalModule.external);
}
else {
// To minimize the chance of breaking setups we whitelist packages available on AWS here. These are due to the previously missing check
// most likely set in devDependencies and should not lead to an error now.
const ignoredDevDependencies = ['aws-sdk'];
if (!ramda_1.includes(externalModule.external, ignoredDevDependencies)) {
// Runtime dependency found in devDependencies but not forcefully excluded
throw new Error(`dependency error: ${externalModule.external}.`);
}
}
}
}, externalModules);
return prodModules;
}
/**
* We need a performant algorithm to install the packages for each single function.
* (1) We fetch ALL packages needed by ALL functions in a first step
* and use this as a base npm checkout. The checkout will be done to a
* separate temporary directory with a package.json that contains everything.
* (2) For each single compile we copy the whole node_modules to the compile
* directory and create a (function) compile specific package.json and store
* it in the compile directory. Now we start npm again there, and npm will just
* remove the superfluous packages and optimize the remaining dependencies.
* This will utilize the npm cache at its best and give us the needed results
* and performance.
*/
function packExternalModules(externals, cwd, compositeModulePath, pkger) {
if (!externals || !externals.length) {
return;
}
// Read function package.json
const packageJsonPath = path.join(cwd, 'package.json');
// Determine and create packager
const packager = Packagers.get(cwd, pkger);
// Fetch needed original package.json sections
const packageJson = fs.readJsonSync(packageJsonPath);
const packageSections = ramda_1.pick(packager.copyPackageSectionNames, packageJson);
// (1) Generate dependency composition
const externalModules = externals.map(external => ({ external }));
const compositeModules = ramda_1.uniq(getProdModules(externalModules, packageJsonPath));
if (ramda_1.isEmpty(compositeModules)) {
// The compiled code does not reference any external modules at all
return;
}
// (1.a) Install all needed modules
const compositePackageJsonPath = path.join(compositeModulePath, 'package.json');
// (1.a.1) Create a package.json
const compositePackageJson = ramda_1.mergeRight({
name: 'externals',
version: '1.0.0',
private: true,
}, packageSections);
const relativePath = path.relative(compositeModulePath, path.dirname(packageJsonPath));
addModulesToPackageJson(compositeModules, compositePackageJson, relativePath);
fs.writeJsonSync(compositePackageJsonPath, compositePackageJson);
// (1.a.2) Copy package-lock.json if it exists, to prevent unwanted upgrades
const packageLockPath = path.join(path.dirname(packageJsonPath), packager.lockfileName);
if (fs.existsSync(packageLockPath)) {
let packageLockFile = fs.readJsonSync(packageLockPath);
packageLockFile = packager.rebaseLockfile(relativePath, packageLockFile);
fs.writeJsonSync(path.join(compositeModulePath, packager.lockfileName), packageLockFile);
}
packager.install(compositeModulePath);
// Prune extraneous packages - removes not needed ones
packager.prune(compositeModulePath);
}
exports.packExternalModules = packExternalModules;