Skip to content

Commit

Permalink
Merge pull request #93 from floydspace/fix/fix-#90-refacto-directory
Browse files Browse the repository at this point in the history
Fix/fix #90 refacto directory and bundling
  • Loading branch information
floydspace authored Mar 5, 2021
2 parents bebc691 + 66d856d commit 4fe6328
Show file tree
Hide file tree
Showing 8 changed files with 292 additions and 204 deletions.
44 changes: 23 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
💨 serverless-esbuild
==============
# 💨 serverless-esbuild

Serverless plugin for zero-config JavaScript and TypeScript code bundling using promising fast & furious [`esbuild`](https://github.com/evanw/esbuild) bundler and minifier

Expand All @@ -9,16 +8,15 @@ Serverless plugin for zero-config JavaScript and TypeScript code bundling using
[![build status](https://img.shields.io/github/workflow/status/floydspace/serverless-esbuild/release)](https://github.com/floydspace/serverless-esbuild/actions)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)


## Features

* Zero-config: Works out of the box without the need to install any other compiler or plugins
* Supports ESNext syntax with transforming limitations (See *Note*)
* Supports `sls package`, `sls deploy` and `sls deploy function`
* Supports `sls invoke local`
* Integrates nicely with [`serverless-offline`](https://github.com/dherault/serverless-offline)
- Zero-config: Works out of the box without the need to install any other compiler or plugins
- Supports ESNext syntax with transforming limitations (See _Note_)
- Supports `sls package`, `sls deploy` and `sls deploy function`
- Supports `sls invoke local`
- Integrates nicely with [`serverless-offline`](https://github.com/dherault/serverless-offline)

*Note*: The default JavaScript syntax target is set to [`ES2017`](https://node.green/#ES2017), so the final bundle will be supported by all [AWS Lambda Node.js runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). If you still using an old lambda runtime and have to respect it you can play with esbuild `target` option, see [JavaScript syntax support](https://github.com/evanw/esbuild#javascript-syntax-support) for more details about syntax transform limitations.
_Note_: The default JavaScript syntax target is set to [`ES2017`](https://node.green/#ES2017), so the final bundle will be supported by all [AWS Lambda Node.js runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). If you still using an old lambda runtime and have to respect it you can play with esbuild `target` option, see [JavaScript syntax support](https://github.com/evanw/esbuild#javascript-syntax-support) for more details about syntax transform limitations.

## Install

Expand Down Expand Up @@ -55,7 +53,6 @@ See [example folder](example) for a minimal example.

All files from `package/include` will be included in the final build file. See [Exclude/Include](https://serverless.com/framework/docs/providers/aws/guide/packaging#exclude--include)


## Usage

### Automatic compilation
Expand All @@ -73,13 +70,13 @@ simulate AWS Lambda and AWS API Gateway locally.

Add the plugins to your `serverless.yml` file and make sure that `serverless-esbuild`
precedes `serverless-offline` as the order is important:

```yaml
plugins:
...
- serverless-esbuild
...
- serverless-offline
...
plugins: ...
- serverless-esbuild
...
- serverless-offline
...
```

Run `serverless offline` or `serverless offline start` to start the Lambda/API simulation.
Expand Down Expand Up @@ -109,11 +106,12 @@ Note: When overriding ignore pattern, remember to ignore `.build` directory to a
Configure your service the same as mentioned above, but additionally add the `serverless-dynamodb-local`
plugin as follows:
```yaml
plugins:
- serverless-esbuild
- serverless-dynamodb-local
- serverless-offline
plugins:
- serverless-esbuild
- serverless-dynamodb-local
- serverless-offline
```

Run `serverless offline start`.
Expand All @@ -136,4 +134,8 @@ Options are:

[Victor Korzunin](https://floydspace.github.io/)

Inspired by [serverless-plugin-typescript](https://github.com/prisma-labs/serverless-plugin-typescript)
## Contributors

[Loup Topalian](https://github.com/olup)

Inspired by [serverless-plugin-typescript](https://github.com/prisma-labs/serverless-plugin-typescript) and [serverless-webpack](https://github.com/serverless-heaven/serverless-webpack)
59 changes: 33 additions & 26 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import * as chokidar from 'chokidar';

import { extractFileNames } from './helper';
import { packExternalModules } from './pack-externals';
import { packIndividually } from './pack-individually';
import { pack } from './pack';
import { preOffline } from './pre-offline';
import { preLocal } from './pre-local';

export const SERVERLESS_FOLDER = '.serverless';
export const BUILD_FOLDER = '.build';
export const WORK_FOLDER = '.esbuild';

interface OptionsExtended extends Serverless.Options {
verbose?: boolean;
Expand All @@ -38,12 +41,13 @@ const DEFAULT_BUILD_OPTIONS: Partial<Configuration> = {
packager: 'npm',
watch: {
pattern: './**/*.(js|ts)',
ignore: [BUILD_FOLDER, 'dist', 'node_modules', SERVERLESS_FOLDER],
ignore: [WORK_FOLDER, 'dist', 'node_modules', SERVERLESS_FOLDER],
},
};

export class EsbuildPlugin implements Plugin {
private originalServicePath: string;
workDirPath: string;
buildDirPath: string;

serverless: Serverless;
options: OptionsExtended;
Expand All @@ -55,13 +59,20 @@ export class EsbuildPlugin implements Plugin {
func: any;
}[];
packExternalModules: () => Promise<void>;
packIndividually: () => Promise<void>;
pack: () => Promise<void>;
preOffline: () => Promise<void>;
preLocal: () => void;

constructor(serverless: Serverless, options: OptionsExtended) {
this.serverless = serverless;
this.options = options;
this.packExternalModules = packExternalModules.bind(this);
this.packIndividually = packIndividually.bind(this);
this.pack = pack.bind(this);
this.preOffline = preOffline.bind(this);
this.preLocal = preLocal.bind(this);

this.workDirPath = path.join(this.serverless.config.servicePath, WORK_FOLDER);
this.buildDirPath = path.join(this.workDirPath, BUILD_FOLDER);

const withDefaultOptions = mergeRight(DEFAULT_BUILD_OPTIONS);
this.buildOptions = withDefaultOptions<Configuration>(
Expand All @@ -78,19 +89,21 @@ export class EsbuildPlugin implements Plugin {
await this.bundle();
await this.packExternalModules();
await this.copyExtras();
await this.preOffline();
this.watch();
},
'before:offline:start:init': async () => {
await this.bundle();
await this.packExternalModules();
await this.copyExtras();
await this.preOffline();
this.watch();
},
'before:package:createDeploymentArtifacts': async () => {
await this.bundle();
await this.packExternalModules();
await this.copyExtras();
await this.packIndividually();
await this.pack();
},
'after:package:createDeploymentArtifacts': async () => {
await this.cleanup();
Expand All @@ -99,7 +112,7 @@ export class EsbuildPlugin implements Plugin {
await this.bundle();
await this.packExternalModules();
await this.copyExtras();
await this.packIndividually();
await this.pack();
},
'after:deploy:function:packageFunction': async () => {
await this.cleanup();
Expand All @@ -108,6 +121,7 @@ export class EsbuildPlugin implements Plugin {
await this.bundle();
await this.packExternalModules();
await this.copyExtras();
await this.preLocal();
},
};
}
Expand All @@ -129,7 +143,7 @@ export class EsbuildPlugin implements Plugin {

get rootFileNames() {
return extractFileNames(
this.originalServicePath,
this.serverless.config.servicePath,
this.serverless.service.provider.name,
this.functions
);
Expand All @@ -152,6 +166,8 @@ export class EsbuildPlugin implements Plugin {
}

prepare() {
fs.mkdirpSync(this.buildDirPath);
fs.mkdirpSync(path.join(this.workDirPath, SERVERLESS_FOLDER));
// exclude serverless-esbuild
for (const fnName in this.functions) {
const fn = this.serverless.service.getFunction(fnName);
Expand All @@ -171,20 +187,13 @@ export class EsbuildPlugin implements Plugin {
this.prepare();
this.serverless.cli.log('Compiling with esbuild...');

if (!this.originalServicePath) {
// Save original service path and functions
this.originalServicePath = this.serverless.config.servicePath;
// Fake service path so that serverless will know what to zip
this.serverless.config.servicePath = path.join(this.originalServicePath, BUILD_FOLDER);
}

return Promise.all(
this.rootFileNames.map(async ({ entry, func }) => {
const config: Omit<BuildOptions, 'watch'> = {
...this.buildOptions,
external: [...this.buildOptions.external, ...this.buildOptions.exclude],
entryPoints: [entry],
outdir: path.join(this.originalServicePath, BUILD_FOLDER, path.dirname(entry)),
outdir: path.join(this.buildDirPath, path.dirname(entry)),
platform: 'node',
incremental,
};
Expand Down Expand Up @@ -216,15 +225,15 @@ export class EsbuildPlugin implements Plugin {
const files = await globby(service.package.include);

for (const filename of files) {
const destFileName = path.resolve(path.join(BUILD_FOLDER, filename));
const destFileName = path.resolve(path.join(this.buildDirPath, filename));
const dirname = path.dirname(destFileName);

if (!fs.existsSync(dirname)) {
fs.mkdirpSync(dirname);
}

if (!fs.existsSync(destFileName)) {
fs.copySync(path.resolve(filename), path.resolve(path.join(BUILD_FOLDER, filename)));
fs.copySync(path.resolve(filename), path.resolve(path.join(this.buildDirPath, filename)));
}
}
}
Expand All @@ -238,14 +247,14 @@ export class EsbuildPlugin implements Plugin {
const { service } = this.serverless;

await fs.copy(
path.join(this.originalServicePath, BUILD_FOLDER, SERVERLESS_FOLDER),
path.join(this.originalServicePath, SERVERLESS_FOLDER)
path.join(this.workDirPath, SERVERLESS_FOLDER),
path.join(this.serverless.config.servicePath, SERVERLESS_FOLDER)
);

if (this.options.function) {
const fn = service.getFunction(this.options.function);
fn.package.artifact = path.join(
this.originalServicePath,
this.serverless.config.servicePath,
SERVERLESS_FOLDER,
path.basename(fn.package.artifact)
);
Expand All @@ -256,7 +265,7 @@ export class EsbuildPlugin implements Plugin {
const functionNames = service.getAllFunctions();
functionNames.forEach(name => {
service.getFunction(name).package.artifact = path.join(
this.originalServicePath,
this.serverless.config.servicePath,
SERVERLESS_FOLDER,
path.basename(service.getFunction(name).package.artifact)
);
Expand All @@ -265,18 +274,16 @@ export class EsbuildPlugin implements Plugin {
}

service.package.artifact = path.join(
this.originalServicePath,
this.serverless.config.servicePath,
SERVERLESS_FOLDER,
path.basename(service.package.artifact)
);
}

async cleanup(): Promise<void> {
await this.moveArtifacts();
// Restore service path
this.serverless.config.servicePath = this.originalServicePath;
// Remove temp build folder
fs.removeSync(path.join(this.originalServicePath, BUILD_FOLDER));
fs.removeSync(path.join(this.workDirPath));
}
}

Expand Down
Loading

0 comments on commit 4fe6328

Please sign in to comment.