diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index 619b1f2477c2..12d1bd85805d 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -163,27 +163,64 @@ class PluginManager { return require('@serverless/enterprise-plugin'); } - parsePluginsObject(servicePlugs) { - let localPath = + getModules(servicePlugs) { + if (Array.isArray(servicePlugs)) { + return servicePlugs; + } + if (servicePlugs && Array.isArray(servicePlugs.modules)) { + return servicePlugs.modules; + } + + return []; + } + + getLocalPath(servicePlugs) { + return this.getCustomLocalPath(servicePlugs) || this.getDefaultLocalPath(); + } + + getDefaultLocalPath() { + return ( this.serverless && this.serverless.config && this.serverless.config.servicePath && - path.join(this.serverless.config.servicePath, '.serverless_plugins'); - let modules = []; + path.join(this.serverless.config.servicePath, '.serverless_plugins') + ); + } - if (Array.isArray(servicePlugs)) { - modules = servicePlugs; - } else if (servicePlugs) { - localPath = - servicePlugs.localPath && typeof servicePlugs.localPath === 'string' - ? servicePlugs.localPath - : localPath; - if (Array.isArray(servicePlugs.modules)) { - modules = servicePlugs.modules; - } + getCustomLocalPath(servicePlugs) { + return servicePlugs && servicePlugs.localPath && typeof servicePlugs.localPath === 'string'; + } + + parsePluginsObject(servicePlugs) { + return { modules: this.getModules(servicePlugs), localPath: this.getLocalPath(servicePlugs) }; + } + + getLocalPluginsPaths(servicePlugs) { + const localPluginsPaths = []; + + // Adding custom local path if provided + // + // plugins: + // localPath: './myPluginPath' + // modules: ['Plugin1'] + const customLocalPath = this.getCustomLocalPath(servicePlugs); + if (customLocalPath) { + localPluginsPaths.push(customLocalPath); } - return { modules, localPath }; + // Adding plugins with relative path references + // + // plugins: ['./myPluginDir/Plugin1'] + // + // OR + // + // plugins: + // localPath: './myPluginPath' + // modules: ['Plugin1', './myPluginDir/Plugin2'] -> Plugin2 does not use localPath like Plugin1 + localPluginsPaths.push(...this.getModules(servicePlugs).filter(name => name.startsWith('./'))); + + // Plugins loaded from .serverless_plugins folder are not compiled within this method + return localPluginsPaths; } createCommandAlias(alias, command) { diff --git a/lib/classes/PluginManager.test.js b/lib/classes/PluginManager.test.js index a676d69f6226..7d3495e49281 100644 --- a/lib/classes/PluginManager.test.js +++ b/lib/classes/PluginManager.test.js @@ -839,6 +839,37 @@ describe('PluginManager', () => { }); }); + describe('#getLocalPluginsPaths()', () => { + it('should return empty array without any plugins', () => { + const servicePlugins = {}; + expect(pluginManager.getLocalPluginsPaths(servicePlugins)).to.deep.equal([]); + }); + + it('should return path of all plugins with relative paths', () => { + const servicePlugins = [ + './sub/directory/Plugin1', + './sub/other/directory/Plugin2', + 'Plugin3', + ]; + expect(pluginManager.getLocalPluginsPaths(servicePlugins)).to.deep.equal([ + './sub/directory/Plugin1', + './sub/other/directory/Plugin2', + ]); + }); + + it('should return path of all plugins with relative paths when localPath override is provided', () => { + const servicePlugins = { + localPath: './myPluginDirectory', + modules: ['./sub/directory/Plugin1', './sub/other/directory/Plugin2', 'Plugin3'], + }; + expect(pluginManager.getLocalPluginsPaths(servicePlugins)).to.deep.equal([ + './myPluginDirectory', + './sub/directory/Plugin1', + './sub/other/directory/Plugin2', + ]); + }); + }); + describe('command aliases', () => { describe('#getAliasCommandTarget', () => { it('should return an alias target', () => { diff --git a/lib/plugins/package/lib/packageService.js b/lib/plugins/package/lib/packageService.js index 22d50091963a..ec722065064f 100644 --- a/lib/plugins/package/lib/packageService.js +++ b/lib/plugins/package/lib/packageService.js @@ -33,11 +33,10 @@ module.exports = { .getServerlessConfigFilePath(this.serverless) .then(configFilePath => { const packageExcludes = this.serverless.service.package.exclude || []; - // add local service plugins Path - const pluginsLocalPath = this.serverless.pluginManager.parsePluginsObject( - this.serverless.service.plugins - ).localPath; - const localPathExcludes = pluginsLocalPath ? [pluginsLocalPath] : []; + // add local service plugins Paths + const pluginsLocalPaths = this.serverless.pluginManager + .getLocalPluginsPaths(this.serverless.service.plugins) + .map(pluginsLocalPath => `${pluginsLocalPath}/**`); // add layer paths const layerExcludes = excludeLayers ? this.serverless.service @@ -51,7 +50,7 @@ module.exports = { return _.union( this.defaultExcludes, serverlessConfigFileExclude, - localPathExcludes, + pluginsLocalPaths, packageExcludes, layerExcludes, exclude diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index d731cc6c0094..32af7ca0766b 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -90,7 +90,37 @@ describe('#packageService()', () => { return expect(packagePlugin.getExcludes()).to.be.fulfilled.then(exclude => expect(exclude).to.deep.equal( - _.union(packagePlugin.defaultExcludes, [serverlessConfigFileName], [localPath]) + _.union(packagePlugin.defaultExcludes, [serverlessConfigFileName], [`${localPath}/**`]) + ) + ); + }); + + it('should exclude plugins local paths', () => { + serverless.service.plugins = ['./sub/directory/myPlugin']; + + return expect(packagePlugin.getExcludes()).to.be.fulfilled.then(exclude => + expect(exclude).to.deep.equal( + _.union( + packagePlugin.defaultExcludes, + [serverlessConfigFileName], + ['./sub/directory/myPlugin/**'] + ) + ) + ); + }); + + it('should exclude lugins localPath defaults and plugins local paths', () => { + const localPath = './myplugins'; + serverless.service.plugins = { localPath, modules: ['Plugin', './sub/directory/myPlugin'] }; + + return expect(packagePlugin.getExcludes()).to.be.fulfilled.then(exclude => + expect(exclude).to.deep.equal( + _.union( + packagePlugin.defaultExcludes, + [serverlessConfigFileName], + [`${localPath}/**`], + ['./sub/directory/myPlugin/**'] + ) ) ); }); @@ -137,7 +167,7 @@ describe('#packageService()', () => { _.union( packagePlugin.defaultExcludes, [serverlessConfigFileName], - [localPath], + [`${localPath}/**`], packageExcludes ) ) @@ -158,7 +188,7 @@ describe('#packageService()', () => { _.union( packagePlugin.defaultExcludes, [serverlessConfigFileName], - [localPath], + [`${localPath}/**`], packageExcludes, funcExcludes )