UNPKG

serverless-esbuild

Version:

Serverless plugin for zero-config JavaScript and TypeScript code bundling using extremely fast esbuild

209 lines 12 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.filterFilesForZipPackage = void 0; exports.pack = pack; exports.copyPreBuiltResources = copyPreBuiltResources; const assert_1 = __importDefault(require("assert")); const path_1 = __importDefault(require("path")); const p_map_1 = __importDefault(require("p-map")); const fs_extra_1 = __importDefault(require("fs-extra")); const globby_1 = __importDefault(require("globby")); const ramda_1 = require("ramda"); const semver_1 = __importDefault(require("semver")); const constants_1 = require("./constants"); const helper_1 = require("./helper"); const packagers_1 = require("./packagers"); const utils_1 = require("./utils"); function setFunctionArtifactPath(func, artifactPath) { const version = this.serverless.getVersion(); // Serverless changed the artifact path location in version 1.18 if (semver_1.default.lt(version, '1.18.0')) { // eslint-disable-next-line no-param-reassign func.artifact = artifactPath; // eslint-disable-next-line no-param-reassign, prefer-object-spread func.package = Object.assign({}, func.package, { disable: true }); this.log.verbose(`${func.name} is packaged by the esbuild plugin. Ignore messages from SLS.`); } else { // eslint-disable-next-line no-param-reassign func.package = { artifact: artifactPath, }; } } const excludedFilesDefault = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'package.json']; const filterFilesForZipPackage = ({ files, functionAlias, includedFiles, excludedFiles, hasExternals, isGoogleProvider, depWhiteList, }) => { return files.filter(({ localPath }) => { // if file is present in patterns it must be included if (includedFiles.find((file) => file === localPath)) { return true; } // exclude non individual files based on file path (and things that look derived, e.g. foo.js => foo.js.map) if (excludedFiles.find((file) => localPath.startsWith(`${file}.`))) { return false; } // exclude files that belong to individual functions if (localPath.startsWith(constants_1.ONLY_PREFIX) && !localPath.startsWith(`${constants_1.ONLY_PREFIX}${functionAlias}/`)) return false; // exclude non whitelisted dependencies if (localPath.startsWith('node_modules')) { // if no externals is set or if the provider is google, we do not need any files from node_modules if (!hasExternals || isGoogleProvider) return false; if ( // this is needed for dependencies that maps to a path (like scoped ones) !depWhiteList.find((dep) => (0, helper_1.doSharePath)(localPath, `node_modules/${dep}`))) return false; } return true; }); }; exports.filterFilesForZipPackage = filterFilesForZipPackage; // eslint-disable-next-line max-statements async function pack() { // GOOGLE Provider requires a package.json and NO node_modules const providerName = this.serverless?.service?.provider?.name; const isGoogleProvider = this.serverless?.service?.provider?.name === 'google'; const isScalewayProvider = this.serverless?.service?.provider?.name === 'scaleway'; // Scaleway can not have package: individually const excludedFiles = isGoogleProvider ? [] : excludedFilesDefault; // Google and Scaleway providers cannot use individual packaging for now - this could be built in a future release const isPackageIndividuallyNotSupported = isGoogleProvider || isScalewayProvider || false; if (isPackageIndividuallyNotSupported && this.serverless?.service?.package?.individually) { throw new Error(`Packaging failed: cannot package function individually when using ${providerName} provider`); } const { buildDirPath, workDirPath } = this; (0, helper_1.assertIsString)(buildDirPath, 'buildDirPath is not a string'); (0, helper_1.assertIsString)(workDirPath, 'workDirPath is not a string'); // get a list of all path in build const files = globby_1.default .sync('**', { cwd: buildDirPath, dot: true, onlyFiles: true, }) .filter((file) => !excludedFiles.includes(file)) .map((localPath) => ({ localPath, rootPath: path_1.default.join(buildDirPath, localPath) })) .map((file) => { if (this.buildOptions?.resolveExtensions && this.buildOptions.resolveExtensions.length > 0) { if (this.buildOptions.stripEntryResolveExtensions) { return (0, helper_1.stripEntryResolveExtensions)(file, this.buildOptions.resolveExtensions); } } return file; }); if ((0, ramda_1.isEmpty)(files)) { this.log.verbose('Packaging: No files found. Skipping esbuild.'); return; } // 1) If individually is not set, just zip the all build dir and return if (!this.serverless?.service?.package?.individually) { const zipName = `${this.serverless.service.service}.zip`; const artifactPath = path_1.default.join(workDirPath, constants_1.SERVERLESS_FOLDER, zipName); // remove prefixes from individual extra files const filesPathList = (0, ramda_1.pipe)((0, ramda_1.reject)((0, ramda_1.test)(/^__only_[^/]+$/)), (0, ramda_1.map)((0, ramda_1.over)((0, ramda_1.lensProp)('localPath'), (0, ramda_1.replace)(/^__only_[^/]+\//, ''))))(files); const startZip = Date.now(); await (0, utils_1.zip)(artifactPath, filesPathList, this.buildOptions?.nativeZip); const { size } = fs_extra_1.default.statSync(artifactPath); this.log.verbose(`Zip service ${this.serverless.service.service} - ${(0, utils_1.humanSize)(size)} [${Date.now() - startZip} ms]`); // defined present zip as output artifact this.serverless.service.package.artifact = artifactPath; return; } (0, helper_1.assertIsString)(this.buildOptions?.packager, 'packager is not a string'); // 2) If individually is set, we'll optimize files and zip per-function const packager = await packagers_1.getPackager.call(this, this.buildOptions.packager, this.buildOptions.packagerOptions); // get a list of every function bundle const { buildResults } = this; (0, assert_1.default)(buildResults, 'buildResults is not an array'); const bundlePathList = buildResults.map((results) => results.bundlePath); let externals = []; // get the list of externals to include only if exclude is not set to * if (this.buildOptions.exclude !== '*' && !this.buildOptions.exclude.includes('*')) { externals = (0, ramda_1.without)(this.buildOptions.exclude, this.buildOptions.external ?? []); } const hasExternals = !!externals?.length; const { buildOptions } = this; // get a tree of all production dependencies const packagerDependenciesList = hasExternals ? await packager.getProdDependencies(buildDirPath) : {}; const packageFiles = await (0, globby_1.default)(this.serverless.service.package.patterns); const zipMapper = async (buildResult) => { const { func, functionAlias, bundlePath } = buildResult; const bundleExcludedFiles = bundlePathList.filter((item) => !bundlePath.startsWith(item)).map(utils_1.trimExtension); const functionPackagePatterns = func.package?.patterns || []; const functionExclusionPatterns = functionPackagePatterns .filter((pattern) => pattern.charAt(0) === '!') .map((pattern) => pattern.slice(1)); const functionFiles = await (0, globby_1.default)(functionPackagePatterns, { cwd: buildDirPath }); const functionExcludedFiles = (await (0, globby_1.default)(functionExclusionPatterns, { cwd: buildDirPath })).map(utils_1.trimExtension); const includedFiles = [...packageFiles, ...functionFiles]; const excludedPackageFiles = [...bundleExcludedFiles, ...functionExcludedFiles]; // allowed external dependencies in the final zip let depWhiteList = []; if (hasExternals && packagerDependenciesList.dependencies) { const bundleDeps = (0, helper_1.getDepsFromBundle)(path_1.default.join(buildDirPath, bundlePath), (0, helper_1.isESM)(buildOptions)); const bundleExternals = (0, ramda_1.intersection)(bundleDeps, externals); depWhiteList = (0, helper_1.flatDep)(packagerDependenciesList.dependencies, bundleExternals); } const zipName = `${functionAlias}.zip`; const artifactPath = path_1.default.join(workDirPath, constants_1.SERVERLESS_FOLDER, zipName); // filter files const filesPathList = (0, exports.filterFilesForZipPackage)({ files, functionAlias, includedFiles, hasExternals, isGoogleProvider, depWhiteList, excludedFiles: excludedPackageFiles, }) // remove prefix from individual function extra files .map(({ localPath, ...rest }) => ({ localPath: localPath.replace(`${constants_1.ONLY_PREFIX}${functionAlias}/`, ''), ...rest, })); const startZip = Date.now(); await (0, utils_1.zip)(artifactPath, filesPathList, buildOptions.nativeZip); const { size } = fs_extra_1.default.statSync(artifactPath); this.log.verbose(`Function zipped: ${functionAlias} - ${(0, utils_1.humanSize)(size)} [${Date.now() - startZip} ms]`); // defined present zip as output artifact setFunctionArtifactPath.call(this, func, path_1.default.relative(this.serviceDirPath, artifactPath)); }; this.log.verbose(`Zipping with concurrency: ${buildOptions.zipConcurrency}`); await (0, p_map_1.default)(buildResults, zipMapper, { concurrency: buildOptions.zipConcurrency }); this.log.verbose('All functions zipped.'); } async function copyPreBuiltResources() { this.log.verbose('Copying Prebuilt resources'); const { workDirPath, packageOutputPath } = this; (0, helper_1.assertIsString)(workDirPath, 'workDirPath is not a string'); (0, helper_1.assertIsString)(packageOutputPath, 'packageOutputPath is not a string'); // 1) If individually is not set, just zip the all build dir and return if (!this.serverless?.service?.package?.individually) { const zipName = `${this.serverless.service.service}.zip`; await fs_extra_1.default.copy(path_1.default.join(packageOutputPath, zipName), path_1.default.join(workDirPath, constants_1.SERVERLESS_FOLDER, zipName)); // defined present zip as output artifact this.serverless.service.package.artifact = path_1.default.join(workDirPath, constants_1.SERVERLESS_FOLDER, zipName); return; } // get a list of every function bundle const buildResults = Object.entries(this.functions) .filter(([functionAlias, func]) => func && functionAlias) .map(([functionAlias, func]) => ({ func, functionAlias })); (0, assert_1.default)(buildResults, 'buildResults is not an array'); const zipMapper = async (buildResult) => { const { func, functionAlias } = buildResult; if (func.skipEsbuild) { const zipName = `${functionAlias}.zip`; const artifactPath = path_1.default.join(workDirPath, constants_1.SERVERLESS_FOLDER, zipName); // defined present zip as output artifact await fs_extra_1.default.copy(path_1.default.join(packageOutputPath, zipName), artifactPath); setFunctionArtifactPath.call(this, func, path_1.default.relative(this.serviceDirPath, artifactPath)); } }; await (0, p_map_1.default)(buildResults, zipMapper, {}); this.log.verbose('All functions copied.'); } //# sourceMappingURL=pack.js.map