Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to exclude files using regular expression #612

Merged
merged 2 commits into from
Jul 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,17 +305,17 @@ custom:
#### Runtime dependencies

If a runtime dependency is detected that is found in the `devDependencies` section and
so would not be packaged, the plugin will error until you explicitly exclude it (see `forceExclude` below)
so would not be packaged, the plugin will error until you explicitly exclude it (see `forceExclude` below)
or move it to the `dependencies` section.

#### AWS-SDK

An exception for the runtime dependency error is the AWS-SDK. All projects using the AWS-SDK normally
have it listed in `devDependencies` because AWS provides it already in their Lambda environment. In this case
have it listed in `devDependencies` because AWS provides it already in their Lambda environment. In this case
the aws-sdk is automatically excluded and only an informational message is printed (in `--verbose` mode).

The main reason for the warning is, that silently ignoring anything contradicts the declarative nature
of Serverless' service definition. So the correct way to define the handling for the aws-sdk is, as
of Serverless' service definition. So the correct way to define the handling for the aws-sdk is, as
you would do for all other excluded modules (see `forceExclude` below).

```yaml
Expand All @@ -342,7 +342,7 @@ custom:
```

You should select the packager, that you use to develop your projects, because only
then locked versions will be handled correctly, i.e. the plugin uses the generated
then locked versions will be handled correctly, i.e. the plugin uses the generated
(and usually committed) package lock file that is created by your favorite packager.

Each packager might support specific options that can be set in the `packagerOptions`
Expand Down Expand Up @@ -375,7 +375,7 @@ You can specify custom scripts that are executed after the installation of the f
has been finished. These are standard packager scripts as they can be used in any `package.json`.

Warning: The use cases for them are very rare and specific and you should investigate first,
if your use case can be covered with webpack plugins first. They should never access files
if your use case can be covered with webpack plugins first. They should never access files
outside of their current working directory which is the compiled function folder, if any.
A valid use case would be to start anything available as binary from `node_modules`.

Expand Down Expand Up @@ -439,7 +439,7 @@ If you have a project structure that uses something like `index.js` and a
co-located `index.test.js` then you have likely seen an error like:
`WARNING: More than one matching handlers found for index. Using index.js`

This config option allows you to exlcude files that match a glob from function
This config option allows you to exclude files that match a glob from function
resolution. Just add: `excludeFiles: **/*.test.js` (with whatever glob you want
to exclude).

Expand All @@ -452,6 +452,19 @@ custom:

This is also useful for projects that use TypeScript.

#### Exclude Files with Regular Expression

This config option allows you to filter files that match a regex pattern before
adding to the zip file. Just add: `excludeRegex: \.ts|test|\.map` (with whatever
regex you want to exclude).

```yaml
# serverless.yml
custom:
webpack:
excludeRegex: /\.ts|test|\.map/
```

#### Keep output directory after packaging

You can keep the output directory (defaults to `.webpack`) from being removed
Expand Down
4 changes: 4 additions & 0 deletions lib/Configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class Configuration {
get excludeFiles() {
return this._config.excludeFiles;
}

get excludeRegex() {
return this._config.excludeRegex;
}

get packager() {
return this._config.packager;
Expand Down
6 changes: 5 additions & 1 deletion lib/packageModules.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@ function zip(directory, name) {

const output = fs.createWriteStream(artifactFilePath);

const files = glob.sync('**', {
let files = glob.sync('**', {
cwd: directory,
dot: true,
silent: true,
follow: true
});

if (this.configuration.excludeRegex) {
files = _.filter(files, f => f.match(this.configuration.excludeRegex) === null);
}

if (_.isEmpty(files)) {
const error = new this.serverless.classes.Error('Packaging: No files found');
return BbPromise.reject(error);
Expand Down
52 changes: 51 additions & 1 deletion tests/packageModules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const path = require('path');
const sinon = require('sinon');
const mockery = require('mockery');
const Serverless = require('serverless');
const Configuration = require('../lib/Configuration');

// Mocks
const fsMockFactory = require('./mocks/fs.mock');
Expand Down Expand Up @@ -73,7 +74,8 @@ describe('packageModules', () => {
{
serverless,
options: {},
webpackOutputPath: '.webpack'
webpackOutputPath: '.webpack',
configuration: new Configuration()
},
baseModule
);
Expand Down Expand Up @@ -308,6 +310,54 @@ describe('packageModules', () => {
module.compileStats = stats;
return expect(module.packageModules()).to.be.rejectedWith('Packaging: No files found');
});

it('should reject if no files are found because all files are excluded using regex', () => {
module.configuration = new Configuration({
webpack: {
excludeRegex: /.*/
}
});

// Test data
const stats = {
stats: [
{
compilation: {
compiler: {
outputPath: '/my/Service/Path/.webpack/service'
}
}
}
]
};
const files = [ 'README.md', 'src/handler1.js', 'src/handler1.js.map', 'src/handler2.js', 'src/handler2.js.map' ];
const allFunctions = [ 'func1', 'func2' ];
const func1 = {
handler: 'src/handler1',
events: []
};
const func2 = {
handler: 'src/handler2',
events: []
};
// Serverless behavior
sandbox.stub(serverless.config, 'servicePath').value('/my/Service/Path');
getVersionStub.returns('1.18.0');
getServiceObjectStub.returns({
name: 'test-service'
});
getAllFunctionsStub.returns(allFunctions);
getFunctionStub.withArgs('func1').returns(func1);
getFunctionStub.withArgs('func2').returns(func2);
// Mock behavior
globMock.sync.returns(files);
fsMock._streamMock.on.withArgs('open').yields();
fsMock._streamMock.on.withArgs('close').yields();
fsMock._statMock.isDirectory.returns(false);

module.compileStats = stats;
return expect(module.packageModules()).to.be.rejectedWith('Packaging: No files found');
});
});

describe('with individual packaging', () => {
Expand Down