diff --git a/package.json b/package.json index 72e0f390..3e394292 100644 --- a/package.json +++ b/package.json @@ -62,12 +62,12 @@ "typescript": "^3.9.10" }, "dependencies": { + "@serverless/utils": "pre-6", "fs-extra": "^7.0.1", "globby": "^10.0.2", "lodash": "^4.17.21" }, "peerDependencies": { - "serverless": "2", "typescript": ">=2.2.2" }, "jest": { diff --git a/src/Serverless.d.ts b/src/Serverless.d.ts index 7c3e9f2d..085d3911 100644 --- a/src/Serverless.d.ts +++ b/src/Serverless.d.ts @@ -63,6 +63,20 @@ declare namespace Serverless { } > + interface Progress { + update(message: string): void + remove(): void + } + + interface Utils { + log: ((message: string) => void) & { + verbose(message: string): void + } + progress: { + create(opts?: { message?: string }): Progress; + } + } + interface PluginManager { spawn(command: string): Promise } diff --git a/src/index.ts b/src/index.ts index adafe7a3..d43643e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,11 +16,14 @@ export class TypeScriptPlugin { serverless: Serverless.Instance options: Serverless.Options hooks: { [key: string]: Function } + utils: Serverless.Utils commands: Serverless.CommandsDefinition + watchProgress?: Serverless.Progress - constructor(serverless: Serverless.Instance, options: Serverless.Options) { + constructor(serverless: Serverless.Instance, options: Serverless.Options, utils: Serverless.Utils) { this.serverless = serverless this.options = options + this.utils = utils this.commands = { invoke: { @@ -81,9 +84,13 @@ export class TypeScriptPlugin { delete require.cache[module] }) } + this.watchProgress = this.watchProgress ?? this.utils.progress.create() }, 'after:invoke:local:invoke': async () => { if (this.options.watch) { + if (this.watchProgress) { + this.watchProgress.update('Watching for TypeScript changes') + } await this.watchFunction() } } @@ -131,8 +138,7 @@ export class TypeScriptPlugin { return } - this.serverless.cli.log(`Watch function ${this.options.function}...`) - this.serverless.cli.log('Waiting for changes...') + this.utils.log.verbose(`Watching function ${this.options.function}`) this.isWatching = true await new Promise((resolve, reject) => { @@ -147,7 +153,7 @@ export class TypeScriptPlugin { return } - this.serverless.cli.log(`Watching typescript files...`) + this.utils.log.verbose(`Watching typescript files`) this.isWatching = true watchFiles(this.rootFileNames, this.originalServicePath, this.compileTs.bind(this)) @@ -155,7 +161,9 @@ export class TypeScriptPlugin { async compileTs(): Promise { this.prepare() - this.serverless.cli.log('Compiling with Typescript...') + const progress = this.utils.progress.create({ + message: 'Compiling TypeScript code' + }) if (!this.originalServicePath) { // Save original service path and functions @@ -173,13 +181,13 @@ export class TypeScriptPlugin { const tsconfig = typescript.getTypescriptConfig( this.originalServicePath, tsConfigFileLocation, - this.isWatching ? null : this.serverless.cli + !this.isWatching ) tsconfig.outDir = BUILD_FOLDER const emitedFiles = await typescript.run(this.rootFileNames, tsconfig) - this.serverless.cli.log('Typescript compiled.') + progress.remove() return emitedFiles } diff --git a/src/serverless-logs.d.ts b/src/serverless-logs.d.ts new file mode 100644 index 00000000..4ac73263 --- /dev/null +++ b/src/serverless-logs.d.ts @@ -0,0 +1,6 @@ +declare module '@serverless/utils/log' { + export const log: ((message: string) => void) & { + verbose(message: string): void + warning(message: string): void + } +} diff --git a/src/typescript.ts b/src/typescript.ts index b3e37d25..1cdb1077 100644 --- a/src/typescript.ts +++ b/src/typescript.ts @@ -2,6 +2,7 @@ import * as ts from 'typescript' import * as fs from 'fs-extra' import * as _ from 'lodash' import * as path from 'path' +import {log} from '@serverless/utils/log' export function makeDefaultTypescriptConfig() { const defaultTypescriptConfig: ts.CompilerOptions = { @@ -38,8 +39,7 @@ export function extractFileNames(cwd: string, provider: string, functions?: { [k // Check that the file indeed exists. if (!fs.existsSync(path.join(cwd, main))) { - console.log(`Cannot locate entrypoint, ${main} not found`) - throw new Error('Typescript compilation failed') + throw new Error(`Typescript compilation failed: Cannot locate entrypoint, ${main} not found`) } return [main] @@ -65,8 +65,7 @@ export function extractFileNames(cwd: string, provider: string, functions?: { [k } // Can't find the files. Watch will have an exception anyway. So throw one with error. - console.log(`Cannot locate handler - ${fileName} not found`) - throw new Error('Typescript compilation failed. Please ensure handlers exists with ext .ts or .js') + throw new Error(`Typescript compilation failed. Please ensure handlers exists with ext .ts or .js.\nCannot locate handler: ${fileName} not found.`) }) } @@ -80,15 +79,15 @@ export async function run(fileNames: string[], options: ts.CompilerOptions): Pro allDiagnostics.forEach(diagnostic => { if (!diagnostic.file) { - console.log(diagnostic) + log.verbose(JSON.stringify(diagnostic)) } const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start) const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n') - console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`) + log.verbose(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`) }) if (emitResult.emitSkipped) { - throw new Error('Typescript compilation failed') + throw new Error('TypeScript compilation failed') } return emitResult.emittedFiles.filter(filename => filename.endsWith('.js')) @@ -113,7 +112,7 @@ export function getSourceFiles( export function getTypescriptConfig( cwd: string, tsConfigFileLocation: string = 'tsconfig.json', - logger?: { log: (str: string) => void } + shouldLog?: boolean ): ts.CompilerOptions { const configFilePath = path.join(cwd, tsConfigFileLocation) @@ -133,13 +132,13 @@ export function getTypescriptConfig( throw new Error(JSON.stringify(configParseResult.errors)) } - if (logger) { - logger.log(`Using local tsconfig.json - ${tsConfigFileLocation}`) + if (shouldLog) { + log.verbose(`Using local tsconfig.json - ${tsConfigFileLocation}`) } // disallow overrriding rootDir - if (configParseResult.options.rootDir && path.resolve(configParseResult.options.rootDir) !== path.resolve(cwd) && logger) { - logger.log('Warning: "rootDir" from local tsconfig.json is overriden') + if (configParseResult.options.rootDir && path.resolve(configParseResult.options.rootDir) !== path.resolve(cwd) && log) { + log.warning('Typescript: "rootDir" from local tsconfig.json is overridden') } configParseResult.options.rootDir = cwd