Skip to content

Commit

Permalink
fix(package): condition on presence of externals
Browse files Browse the repository at this point in the history
  • Loading branch information
olup committed Feb 26, 2021
1 parent bf795c9 commit d2454e4
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 31 deletions.
33 changes: 22 additions & 11 deletions src/pack-individually.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import * as archiver from 'archiver';
import * as fs from 'fs-extra';
import * as glob from 'glob';
import * as path from 'path';
import { intersection, isEmpty, path as get, uniq } from 'ramda';
import { intersection, isEmpty, path as get, without } from 'ramda';
import * as semver from 'semver';
import { SERVERLESS_FOLDER } from '.';
import { doSharePath, flatDep, getDepsFromBundle } from './helper';
import * as Packagers from './packagers';
import { findProjectRoot } from './utils';
import { humanSize } from './utils';

function setArtifactPath(func, artifactPath) {
const version = this.serverless.getVersion();
Expand All @@ -28,11 +28,11 @@ function setArtifactPath(func, artifactPath) {
const excludedFilesDefault = ['package-lock.json', 'yarn.lock', 'package.json'];

export async function packIndividually() {
const buildDir = this.serverless.config.servicePath;

// If individually is not set, ignore this part
if (!get(['service', 'package', 'individually'], this.serverless)) return null;
if (!this.serverless?.service?.package?.individually) return null;

const packager = await Packagers.get(this.buildOptions.packager);
const buildDir = this.serverless.config.servicePath;

// get a list of all path in build
const files = glob.sync('**', {
Expand All @@ -51,10 +51,11 @@ export async function packIndividually() {
const bundlePathList = buildResults.map(b => b.bundlePath);

// get a list of external dependencies already listed in package.json
const externals = Object.keys(require(path.join(findProjectRoot(), 'package.json')).dependencies);
const externals = without<string>(this.buildOptions.exclude, this.buildOptions.external);
const hasExternals = !!externals?.length;

// get a tree of all production dependencies
const { dependencies } = await packager.getProdDependencies(buildDir);
const packagerDependenciesList = hasExternals ? await packager.getProdDependencies(buildDir) : {};

// package each function
await Promise.all(
Expand All @@ -67,9 +68,14 @@ export async function packIndividually() {
...bundlePathList.filter(p => p !== bundlePath),
];

const bundleDeps = getDepsFromBundle(path.join(buildDir, bundlePath));
const bundleExternals = intersection(bundleDeps, externals);
const depWhiteList = flatDep(dependencies, bundleExternals);
// allowed external dependencies in the final zip
let depWhiteList = [];

if (hasExternals) {
const bundleDeps = getDepsFromBundle(path.join(buildDir, bundlePath));
const bundleExternals = intersection(bundleDeps, externals);
depWhiteList = flatDep(packagerDependenciesList.dependencies, bundleExternals);
}

// Create zip and open it
const zip = archiver.create('zip');
Expand All @@ -91,6 +97,7 @@ export async function packIndividually() {

// exclude non whitelisted dependencies
if (filePath.startsWith('node_modules')) {
if (!hasExternals) return;
if (
// this is needed for dependencies that maps to a path (like scopped ones)
!depWhiteList.find(dep => doSharePath(filePath, 'node_modules/' + dep))
Expand All @@ -115,7 +122,11 @@ export async function packIndividually() {

return new Promise((resolve, reject) => {
output.on('close', () => {
this.serverless.cli.log(`Zip function: ${func.name} [${Date.now() - startZip} ms]`);
// log zip results
const { size } = fs.statSync(artifactPath);
this.serverless.cli.log(
`Zip function: ${func.name} - ${humanSize(size)} [${Date.now() - startZip} ms]`
);

// defined present zip as output artifact
setArtifactPath.call(this, func, path.relative(this.originalServicePath, artifactPath));
Expand Down
31 changes: 18 additions & 13 deletions src/packagers/yarn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,24 @@ export class Yarn implements Packager {

const depJson = processOutput.stdout;
const parsedTree = JSON.parse(depJson);
const convertTrees = convertingTrees => reduce((__, tree: JSONObject) => {
const splitModule = split('@', tree.name);
// If we have a scoped module we have to re-add the @
if (startsWith('@', tree.name)) {
splitModule.splice(0, 1);
splitModule[0] = '@' + splitModule[0];
}
__[head(splitModule)] = {
version: join('@', tail(splitModule)),
dependencies: convertTrees(tree.children),
};
return __;
}, {}, convertingTrees);
const convertTrees = convertingTrees =>
reduce(
(__, tree: JSONObject) => {
const splitModule = split('@', tree.name);
// If we have a scoped module we have to re-add the @
if (startsWith('@', tree.name)) {
splitModule.splice(0, 1);
splitModule[0] = '@' + splitModule[0];
}
__[head(splitModule)] = {
version: join('@', tail(splitModule)),
dependencies: convertTrees(tree.children),
};
return __;
},
{},
convertingTrees
);

const trees = pathOr([], ['data', 'trees'], parsedTree);
const result = {
Expand Down
27 changes: 20 additions & 7 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ export class SpawnError extends Error {
* @param {string[]} [args] - Arguments
* @param {Object} [options] - Options for child_process.spawn
*/
export function spawnProcess(command: string, args: string[], options: childProcess.SpawnOptionsWithoutStdio) {
return new Promise<{stdout: string; stderr: string}>((resolve, reject) => {
export function spawnProcess(
command: string,
args: string[],
options: childProcess.SpawnOptionsWithoutStdio
) {
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
const child = childProcess.spawn(command, args, options);
let stdout = '';
let stderr = '';
Expand All @@ -41,7 +45,13 @@ export function spawnProcess(command: string, args: string[], options: childProc
});
child.on('close', exitCode => {
if (exitCode !== 0) {
reject(new SpawnError(`${command} ${join(' ', args)} failed with code ${exitCode}`, stdout, stderr));
reject(
new SpawnError(
`${command} ${join(' ', args)} failed with code ${exitCode}`,
stdout,
stderr
)
);
} else {
resolve({ stdout, stderr });
}
Expand Down Expand Up @@ -71,8 +81,11 @@ export function findUp(name: string, directory: string = process.cwd()): string
* Forwards `rootDir` or finds project root folder.
*/
export function findProjectRoot(rootDir?: string): string | undefined {
return rootDir
?? findUp('yarn.lock')
?? findUp('package-lock.json')
?? findUp('package.json');
return rootDir ?? findUp('yarn.lock') ?? findUp('package-lock.json') ?? findUp('package.json');
}

export const humanSize = (size: number) => {
const i = Math.floor(Math.log(size) / Math.log(1024));
const sanitized = (size / Math.pow(1024, i)).toFixed(2);
return `${sanitized} ${['B', 'KB', 'MB', 'GB', 'TB'][i]}`;
};

0 comments on commit d2454e4

Please sign in to comment.