Skip to content

Commit

Permalink
Merge pull request #4 from beforeyoubid/feature/final-touches
Browse files Browse the repository at this point in the history
Feature/final touches
  • Loading branch information
alice-byb authored Apr 18, 2023
2 parents 0797cd4 + 5ac7551 commit 932590a
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 24 deletions.
1 change: 1 addition & 0 deletions examples/example-layer-service/plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = [];
2 changes: 2 additions & 0 deletions examples/example-layer-service/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ functions:

custom:
foo: FOO
esbuild:
plugins: plugins.js
3 changes: 2 additions & 1 deletion module_test/esbuild-plugins.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const { nodeExternalsPlugin } = require('esbuild-node-externals');
const { default: graphqlLoaderPlugin } = require('@luckycatfactory/esbuild-graphql-loader');

module.exports = [nodeExternalsPlugin()];
module.exports = [nodeExternalsPlugin(), graphqlLoaderPlugin()];
4 changes: 4 additions & 0 deletions module_test/global.types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.graphql' {
const content: string;
export default content;
}
3 changes: 3 additions & 0 deletions module_test/handler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import _ from 'lodash';
import { APIGatewayProxyEvent, Context } from 'aws-lambda';

import schema from './schema.graphql';

const handler = (event: APIGatewayProxyEvent, context: Context) => {
console.log('Event: %j', event);
console.log('Event: %j', context);

console.log(`Using lodash: ${_.range(10)}`);
console.log(`GQL schema: ${schema}`);
};

export default handler;
3 changes: 3 additions & 0 deletions module_test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
"test": "cd .serverless && cat ./nodejs/package.json | grep lodash"
},
"devDependencies": {
"@luckycatfactory/esbuild-graphql-loader": "^3.8.1",
"@swc/core": "^1.2.223",
"@types/aws-lambda": "^8.10.101",
"@types/node": "^18.6.4",
"aws-lambda": "^1.0.7",
"esbuild-node-externals": "^1.7.0",
"graphql": "^16.6.0",
"graphql-tag": "^2.12.6",
"serverless": "^3.21.0",
"serverless-esbuild": "^1.43.1",
"swc-loader": "^0.2.3",
Expand Down
3 changes: 3 additions & 0 deletions module_test/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Query {
someQuery: String
}
4 changes: 4 additions & 0 deletions module_test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"files": ["*.ts"]
}
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This helps to keep the overall function size down.

This library is designed to be used in conjuction with [serverless-esbuild][serverless-esbuild] to build your code.

If you are using [serverless-webpack][serverless-webpack], you can try [serverless-webpack-layers][serverless-webpack-layers] to achieve this functionality.

![Diagram](./docs/diagram.png)

## Installation
Expand Down Expand Up @@ -80,5 +82,7 @@ custom:

[serverless]: https://www.serverless.com/
[serverless-esbuild]: https://github.com/floydspace/serverless-esbuild
[serverless-webpack]: https://github.com/serverless-heaven/serverless-webpack
[serverless-webpack-layers]: https://github.com/beforeyoubid/serverless-webpack-layers
[esbuild-plugins]: https://github.com/floydspace/serverless-esbuild#esbuild-plugins
[esbuild-node-externals]: https://github.com/pradel/esbuild-node-externals
3 changes: 3 additions & 0 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ function createSls(layerConfig = {}) {
},
custom: {
'esbuild-layers': layerConfig,
esbuild: {
plugins: 'examples/example-layer-service/plugins.js',
},
},
functions: {
hello: {
Expand Down
96 changes: 96 additions & 0 deletions src/__tests__/logger.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { log, error, warn, verbose, info, debug } from '../logger';

describe('logger', () => {
beforeEach(() => {
jest.spyOn(global.console, 'log').mockImplementation();
jest.spyOn(global.console, 'warn').mockImplementation();
jest.spyOn(global.console, 'error').mockImplementation();
});
afterEach(() => {
jest.resetAllMocks();
});
describe('log', () => {
it('should call console.log', () => {
const code = Math.random().toString();
log(code);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('[esbuild-layers]', code);
});
});
describe('error', () => {
it('should call console.error', () => {
const code = Math.random().toString();
error(code);
expect(console.error).toBeCalledTimes(1);
expect(console.error).toBeCalledWith('[esbuild-layers]', code);
});
});
describe('warn', () => {
it('should call console.warn', () => {
const code = Math.random().toString();
warn(code);
expect(console.warn).toBeCalledTimes(1);
expect(console.warn).toBeCalledWith('[esbuild-layers]', code);
});
});
describe('debug', () => {
it('should call console.log if log level = debug', () => {
const code = Math.random().toString();
debug({ level: 'debug' }, code);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('[esbuild-layers]', code);
});
it('should not call console.log if log level != verbose', () => {
const code = Math.random().toString();
debug({ level: 'verbose' }, code);
expect(console.log).toBeCalledTimes(0);
expect(console.log).not.toBeCalledWith('[esbuild-layers]', code);
});
it('should not call console.log if log level != debug', () => {
const code = Math.random().toString();
debug({ level: 'info' }, code);
expect(console.log).toBeCalledTimes(0);
expect(console.log).not.toBeCalledWith('[esbuild-layers]', code);
});
});
describe('verbose', () => {
it('should call console.log if log level = debug', () => {
const code = Math.random().toString();
verbose({ level: 'debug' }, code);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('[esbuild-layers]', code);
});
it('should call console.log if log level = verbose', () => {
const code = Math.random().toString();
verbose({ level: 'verbose' }, code);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('[esbuild-layers]', code);
});
it('should not call console.log if log level != verbose', () => {
const code = Math.random().toString();
verbose({ level: 'info' }, code);
expect(console.log).toBeCalledTimes(0);
expect(console.log).not.toBeCalledWith('[esbuild-layers]', code);
});
});
describe('info', () => {
it('should call console.log if log level = verbose', () => {
const code = Math.random().toString();
info({ level: 'verbose' }, code);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('[esbuild-layers]', code);
});
it('should call console.log if log level = debug', () => {
const code = Math.random().toString();
info({ level: 'debug' }, code);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('[esbuild-layers]', code);
});
it('should call console.log if log level = info', () => {
const code = Math.random().toString();
info({ level: 'info' }, code);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('[esbuild-layers]', code);
});
});
});
4 changes: 2 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Packager, type Config, Level } from './types';
import { type Packager, type Config } from './types';

export const PACKAGER_INSTALL_COMMAND: Record<Packager, string> = {
npm: 'npm install',
Expand All @@ -20,7 +20,7 @@ export const PACKAGER_LOCK_FILE_NAMES: Record<Packager, string> = {

export const DEFAULT_CONFIG: Config = {
packager: 'auto',
level: Level.info,
level: 'info',
clean: true,
minify: false,
};
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Layer,
Packager,
Config,
Level,
LevelName,
TransformedLayerResources,
Maybe,
FunctionLayerReference,
Expand All @@ -35,7 +35,7 @@ class EsbuildLayersPlugin implements Plugin {
region: string;
packager: Packager;
config: Config;
level: Level;
level: LevelName;
log: Plugin.Logging['log'];
installedLayerNames: Set<string>;

Expand All @@ -53,8 +53,8 @@ class EsbuildLayersPlugin implements Plugin {
this.serverless = serverless;
this.region = serverless.service.provider.region;
this.config = compileConfig(serverless.service.custom['esbuild-layers'] ?? {});
this.level = options.verbose ? Level.verbose : this.config.level;
this.log = logging?.log ?? Log;
this.level = options.verbose ? 'verbose' : this.config.level;
this.log = logging?.log ?? Log(this.level);

const packager = this.config.packager;
if (packager === 'auto') {
Expand Down
18 changes: 9 additions & 9 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Level } from './types';
import { Level, LevelName } from './types';
import type Plugin from 'serverless/classes/Plugin';

export function log(...s: unknown[]) {
Expand All @@ -11,24 +11,24 @@ export function warn(...s: unknown[]) {
console.warn('[esbuild-layers]', ...s);
}

export function verbose({ level }: { level: Level }, ...s: unknown[]) {
export function verbose({ level }: { level: LevelName }, ...s: unknown[]) {
Number(Level[level]) >= Level.verbose && log(...s);
}

export function info({ level }: { level: Level }, ...s: unknown[]) {
export function info({ level }: { level: LevelName }, ...s: unknown[]) {
Number(Level[level]) >= Level.info && log(...s);
}

export function debug({ level }: { level: Level }, ...s: unknown[]) {
export function debug({ level }: { level: LevelName }, ...s: unknown[]) {
Number(Level[level]) >= Level.debug && log(...s);
}

export const Log: Plugin.Logging['log'] = {
info,
debug,
verbose,
export const Log = (level: LevelName): Plugin.Logging['log'] => ({
info: info.bind(level),
debug: debug.bind(level),
verbose: verbose.bind(level),
error,
warning: warn,
notice: info,
success: info,
};
});
4 changes: 3 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export type Packager = 'npm' | 'yarn' | 'pnpm';

export type Config = {
packager: Packager | 'auto';
level: Level;
level: LevelName;
clean: boolean;
minify: boolean;
};
Expand All @@ -49,3 +49,5 @@ export enum Level {
verbose = 2,
debug = 3,
}

export type LevelName = keyof typeof Level;
29 changes: 22 additions & 7 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DEFAULT_CONFIG } from './constants';

import { exec as execNonPromise, ExecOptions, ExecException } from 'child_process';
import util from 'util';
import { error } from './logger';

/**
* TypeScript typeguard to ensure the type is not null or undefined
Expand Down Expand Up @@ -124,9 +125,21 @@ export function getLayers(serverless: Serverless): { [key: string]: Layer } {
export async function getExternalModules(serverless: Serverless, layerRefName: string): Promise<string[]> {
const entries = await resolvedEntries(serverless, layerRefName);
if (entries.length === 0) return [];
let modules: esbuild.Plugin[] = [];
const pluginFile = serverless.service.custom?.esbuild?.plugins;
if (pluginFile) {
try {
const resolvedPath = path.resolve(process.cwd(), pluginFile);
modules = await import(resolvedPath).then(i => i.default);
modules = modules.filter(m => m.name !== 'node-externals');
} catch (err) {
error(`Unable to import your plugin file (${pluginFile}) due to error:`);
console.error(err);
}
}
const result = await esbuild.build({
entryPoints: entries,
plugins: [nodeExternalsPlugin()],
plugins: [nodeExternalsPlugin(), ...modules],
metafile: true,
bundle: true,
platform: 'node',
Expand All @@ -135,12 +148,14 @@ export async function getExternalModules(serverless: Serverless, layerRefName: s
});

const importedModules = Object.values(result.metafile.outputs).map(({ imports }) => imports.map(i => i.path));
const requiredModules = Object.values(result.metafile.inputs).map(({ imports }) =>
imports
.filter(i => i.path.startsWith('node_modules'))
.map(i => i.original)
.filter(notEmpty)
);
const requiredModules = Object.entries(result.metafile.inputs)
.filter(([key]) => !key.startsWith('node_modules'))
.map(([, { imports }]) =>
imports
.filter(i => i.path.startsWith('node_modules'))
.map(i => i.original)
.filter(notEmpty)
);

const imports = new Set(
[...importedModules, ...requiredModules]
Expand Down

0 comments on commit 932590a

Please sign in to comment.