From b76b7d02986f1bcd4663feb45e97a49fdf6de4e1 Mon Sep 17 00:00:00 2001 From: Daniel Sellers Date: Fri, 4 Jan 2019 17:40:51 -0700 Subject: [PATCH 1/2] Closes #433 Fairly trivial, just used the ignore option in glob to exclude files matching the glob. 1. Setup a serverless project with an index.test.js and an index.js that handle a route 2. Start it up and see that you get a warning about duplicate files matching for index 3. Now pull in this PR 4. add the following to your serverless.yml file in the custom > webpack section `excludeFiles: **/*.test.js` 5. restart serverless 6. see that the warnings are now gone! - [x] Write tests - [ ] Write documentation - [x] Fix linting errors - [x] Make sure code coverage hasn't dropped - [x] Provide verification config / commands / resources - [x] Enable "Allow edits from maintainers" for this PR - [x] Update the messages below ***Is this ready for review?:*** YES ***Is it a breaking change?:*** NO --- README.md | 11 ++++++----- lib/Configuration.js | 6 +++++- lib/validate.js | 7 ++++--- tests/validate.test.js | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3904246f16..dd54c9c90c 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ custom: webpackConfig: 'webpack.config.js' # Name of webpack configuration file includeModules: false # Node modules configuration for packaging packager: 'npm' # Packager that will be used to package your external modules + excludeFiles: src/**/*.test.js # Provide a glob for files to ignore ``` ### Webpack configuration file @@ -302,17 +303,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 @@ -339,7 +340,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` @@ -372,7 +373,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`. diff --git a/lib/Configuration.js b/lib/Configuration.js index ba49252d65..fe8415fce3 100644 --- a/lib/Configuration.js +++ b/lib/Configuration.js @@ -5,7 +5,7 @@ const _ = require('lodash'); -/** +/** * Plugin defaults */ const DefaultConfig = { @@ -50,6 +50,10 @@ class Configuration { return this._config.includeModules; } + get excludeFiles() { + return this._config.excludeFiles; + } + get packager() { return this._config.packager; } diff --git a/lib/validate.js b/lib/validate.js index 8a68fbf443..a6ebc580fe 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -33,7 +33,8 @@ module.exports = { const getEntryExtension = fileName => { const files = glob.sync(`${fileName}.*`, { cwd: this.serverless.config.servicePath, - nodir: true + nodir: true, + ignore: this.configuration.excludeFiles ? this.configuration.excludeFiles : undefined }); if (_.isEmpty(files)) { @@ -117,7 +118,7 @@ module.exports = { return BbPromise.reject(err); } } - + // Intermediate function to handle async webpack config const processConfig = _config => { this.webpackConfig = _config; @@ -220,7 +221,7 @@ module.exports = { return BbPromise.resolve(); }; - + // Webpack config can be a Promise, If it's a Promise wait for resolved config object. if (this.webpackConfig && _.isFunction(this.webpackConfig.then)) { return BbPromise.resolve(this.webpackConfig.then(config => processConfig(config))); diff --git a/tests/validate.test.js b/tests/validate.test.js index 15faba934b..15e87923f8 100644 --- a/tests/validate.test.js +++ b/tests/validate.test.js @@ -745,6 +745,40 @@ describe('validate', () => { }); }); + it('should call glob with ignore parameter if there is an excludeFiles config', () => { + const testOutPath = 'test'; + const testFunction = 'func1'; + const testConfig = { + entry: 'test', + context: 'testcontext', + output: { + path: testOutPath, + }, + }; + _.set(module.serverless.service, 'custom.webpack.config', testConfig); + _.set(module.serverless.service, 'custom.webpack.excludeFiles', '**/*.ts'); + module.serverless.service.functions = testFunctionsConfig; + module.options.function = testFunction; + globSyncStub.returns(['module1.js']); + return expect(module.validate()).to.be.fulfilled + .then(() => { + const lib = require('../lib/index'); + const expectedLibEntries = { + 'module1': './module1.js' + }; + + expect(lib.entries).to.deep.equal(expectedLibEntries); + + expect(globSyncStub).to.have.been.calledOnceWith('module1.*', { + ignore: '**/*.ts', + cwd: null, + nodir: true + }); + expect(serverless.cli.log).not.to.have.been.called; + return null; + }); + }); + it('should throw an exception if no handler is found', () => { const testOutPath = 'test'; const testFunction = 'func1'; From 414068b08cb304cbd5b3e4a9f4c35a42eb88eff9 Mon Sep 17 00:00:00 2001 From: Daniel Sellers Date: Mon, 7 Jan 2019 09:43:47 -0700 Subject: [PATCH 2/2] Adds documentation in the readme --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index dd54c9c90c..6d792e515e 100644 --- a/README.md +++ b/README.md @@ -431,6 +431,25 @@ from a local folder (e.g. `"mymodule": "file:../../myOtherProject/mymodule"`). With that you can do test deployments from the local machine with different module versions or modules before they are published officially. +#### Exclude Files with similar names + +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 +resolution. Just add: `excludeFiles: **/*.test.js` (with whatever glob you want +to exclude). + +```yaml +# serverless.yml +custom: + webpack: + excludeFiles: **/*.test.js +``` + +This is also useful for projects that use TypeScript. + #### Examples You can find an example setups in the [`examples`][link-examples] folder.