diff --git a/src/Serverless.d.ts b/src/Serverless.d.ts index 1a47388d..0d8de942 100644 --- a/src/Serverless.d.ts +++ b/src/Serverless.d.ts @@ -1,4 +1,5 @@ declare namespace Serverless { + interface Instance { cli: { log(str: string): void @@ -8,18 +9,7 @@ declare namespace Serverless { servicePath: string } - service: { - provider: { - name: string - runtime?: string - } - functions: { - [key: string]: Serverless.Function - } - package: Serverless.Package - getAllFunctions(): string[] - } - + service: Service pluginManager: PluginManager } @@ -42,6 +32,18 @@ declare namespace Serverless { individually?: boolean } + type FunctionMap = Record + + interface Service { + provider: { + name: string + runtime?: string + } + functions: FunctionMap + package: Serverless.Package + getAllFunctions(): string[] + } + interface PluginManager { spawn(command: string): Promise } diff --git a/src/index.ts b/src/index.ts index d55c469e..509b866e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -79,19 +79,21 @@ export class TypeScriptPlugin { const { options } = this const { service } = this.serverless - if (options.function) { - return { - [options.function]: service.functions[this.options.function] - } - } - - return service.functions + const allFunctions = options.function ? { + [options.function]: service.functions[this.options.function] + } : service.functions + + // Ensure we only handle runtimes that support Typescript + return _.pickBy(allFunctions, ({runtime}) => { + const resolvedRuntime = runtime || service.provider.runtime + // If runtime is not specified on the function or provider, default to previous behaviour + return resolvedRuntime === undefined ? true : resolvedRuntime.match(/^node/) + }) } get rootFileNames() { return typescript.extractFileNames( this.originalServicePath, - this.serverless.service.provider.name, this.serverless.service.provider.runtime, this.functions ) @@ -235,7 +237,7 @@ export class TypeScriptPlugin { } if (service.package.individually) { - const functionNames = service.getAllFunctions() + const functionNames = Object.keys(this.functions) functionNames.forEach(name => { service.functions[name].package.artifact = path.join( this.originalServicePath, @@ -276,4 +278,4 @@ export class TypeScriptPlugin { } } -module.exports = TypeScriptPlugin +export default TypeScriptPlugin \ No newline at end of file diff --git a/src/typescript.ts b/src/typescript.ts index a13089bc..22f7354a 100644 --- a/src/typescript.ts +++ b/src/typescript.ts @@ -18,7 +18,7 @@ export function makeDefaultTypescriptConfig() { return defaultTypescriptConfig } -export function extractFileNames(cwd: string, provider: string, globalRuntime?: string, functions?: { [key: string]: Serverless.Function }): string[] { +export function extractFileNames(cwd: string, provider: string, functions?: { [key: string]: Serverless.Function }): string[] { // The Google provider will use the entrypoint not from the definition of the // handler function, but instead from the package.json:main field, or via a // index.js file. This check reads the current package.json in the same way @@ -46,13 +46,7 @@ export function extractFileNames(cwd: string, provider: string, globalRuntime?: } } - const runtimeIsNode = (runtime: string) => runtime.toLowerCase().startsWith('node') - const shouldProcessFunction = (fn: Serverless.Function) => - (fn.runtime !== undefined && runtimeIsNode(fn.runtime)) || - (fn.runtime === undefined && (globalRuntime === undefined || runtimeIsNode(globalRuntime))) - return _.values(functions) - .filter(shouldProcessFunction) .map(fn => fn.handler) .map(h => { const fnName = _.last(h.split('.')) diff --git a/tests/index.functions.test.ts b/tests/index.functions.test.ts new file mode 100644 index 00000000..46535612 --- /dev/null +++ b/tests/index.functions.test.ts @@ -0,0 +1,110 @@ +import TypeScriptPlugin from '../src' + +const createInstance = (functions: Serverless.FunctionMap, globalRuntime?: string): Serverless.Instance => ({ + cli: { + log: jest.fn() + }, + config: { + servicePath: 'servicePath' + }, + service: { + provider: { + name: 'aws', + runtime: globalRuntime + }, + package: { + individually: true, + include: [], + exclude: [] + }, + functions, + getAllFunctions: jest.fn() + }, + pluginManager: { + spawn: jest.fn() + } +}) + +describe('functions', () => { + const functions: Serverless.FunctionMap = { + hello: { + handler: 'tests/assets/hello.handler', + package: { + include: [], + exclude: [] + } + }, + world: { + handler: 'tests/assets/world.handler', + runtime: 'nodejs12.x', + package: { + include: [], + exclude: [] + }, + }, + js: { + handler: 'tests/assets/jsfile.create', + package: { + include: [], + exclude: [] + } + }, + notActuallyTypescript: { + handler: 'tests/assets/jsfile.create', + package: { + include: [], + exclude: [] + }, + runtime: 'go1.x' + }, + } + + describe('when the provider runtime is Node', () => { + it('can get filter out non node based functions', () => { + const slsInstance = createInstance(functions, 'nodejs10.x') + const plugin = new TypeScriptPlugin(slsInstance, {}) + + expect( + Object.values(plugin.functions).map(fn => fn.handler), + ).toEqual( + [ + 'tests/assets/hello.handler', + 'tests/assets/world.handler', + 'tests/assets/jsfile.create', + ], + ) + }) + }) + + describe('when the provider runtime is not Node', () => { + it('can get filter out non node based functions', () => { + const slsInstance = createInstance(functions, 'python2.7') + const plugin = new TypeScriptPlugin(slsInstance, {}) + + expect( + Object.values(plugin.functions).map(fn => fn.handler), + ).toEqual( + [ + 'tests/assets/world.handler', + ], + ) + }) + }) + + describe('when the provider runtime is undefined', () => { + it('can get filter out non node based functions', () => { + const slsInstance = createInstance(functions) + const plugin = new TypeScriptPlugin(slsInstance, {}) + + expect( + Object.values(plugin.functions).map(fn => fn.handler), + ).toEqual( + [ + 'tests/assets/hello.handler', + 'tests/assets/world.handler', + 'tests/assets/jsfile.create', + ], + ) + }) + }) +}) diff --git a/tests/typescript.extractFileName.test.ts b/tests/typescript.extractFileName.test.ts deleted file mode 100644 index 90046cfb..00000000 --- a/tests/typescript.extractFileName.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -import {extractFileNames} from '../src/typescript' -import * as path from 'path' - -const functions: { [key: string]: Serverless.Function } = { - hello: { - handler: 'tests/assets/hello.handler', - package: { - include: [], - exclude: [] - } - }, - world: { - handler: 'tests/assets/world.handler', - package: { - include: [], - exclude: [] - }, - runtime: 'nodejs12.x' - }, - js: { - handler: 'tests/assets/jsfile.create', - package: { - include: [], - exclude: [] - } - }, - notActuallyTypescript: { - handler: 'tests/assets/jsfile.create', - package: { - include: [], - exclude: [] - }, - runtime: 'go1.x' - }, -} - -describe('extractFileName', () => { - describe('when the provider runtime is Node', () => { - it('can get function filenames from serverless service for a non-google provider', () => { - expect( - extractFileNames(process.cwd(), 'aws', 'nodejs10.x', functions), - ).toEqual( - [ - 'tests/assets/hello.ts', - 'tests/assets/world.ts', - 'tests/assets/jsfile.js', - ], - ) - }) - - it('can get function filename from serverless service for a google provider', () => { - expect( - extractFileNames(path.join(process.cwd(), 'example'), 'google', 'nodejs') - ).toEqual( - [ - 'handler.ts' - ] - ) - }) - }) - describe('when the provider runtime is not node', () => { - it('can get function filenames from serverless service for a non-google provider', () => { - expect( - extractFileNames(process.cwd(), 'aws', 'python2.7', functions), - ).toEqual( - [ - 'tests/assets/world.ts', - ], - ) - }) - - it('can get function filename from serverless service for a google provider', () => { - expect( - extractFileNames(path.join(process.cwd(), 'example'), 'google', 'python37') - ).toEqual( - [ - 'handler.ts' - ] - ) - }) - }) - describe('when the provider runtime is undefined', () => { - it('can get function filenames from serverless service for a non-google provider', () => { - expect( - extractFileNames(process.cwd(), 'aws', undefined, functions), - ).toEqual( - [ - 'tests/assets/hello.ts', - 'tests/assets/world.ts', - 'tests/assets/jsfile.js', - ], - ) - }) - - it('can get function filename from serverless service for a google provider', () => { - expect( - extractFileNames(path.join(process.cwd(), 'example'), 'google', undefined) - ).toEqual( - [ - 'handler.ts' - ] - ) - }) - }) -}) diff --git a/yarn.lock b/yarn.lock index 735f988f..8b0ac5d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3456,9 +3456,9 @@ type-check@~0.3.2: prelude-ls "~1.1.2" typescript@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6" - integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q== + version "3.7.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" + integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== uglify-js@^3.1.4: version "3.5.2"