Skip to content

Dev deps fix + cleanup + refactor #159

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

Merged
merged 5 commits into from
Jul 13, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
refactor: improvements to documentation and type definitions
  • Loading branch information
JackCuthbert committed Jul 12, 2019
commit 4201aaa6ef2d15e641ff7708f17897f795ccfdf3
46 changes: 46 additions & 0 deletions src/Serverless.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
declare namespace Serverless {
interface Instance {
cli: {
log(str: string): void
}

config: {
servicePath: string
}

service: {
provider: {
name: string
}
functions: {
[key: string]: Serverless.Function
}
package: Serverless.Package
getAllFunctions(): string[]
}

pluginManager: PluginManager
}

interface Options {
function?: string
watch?: boolean
extraServicePath?: string
}

interface Function {
handler: string
package: Serverless.Package
}

interface Package {
include: string[]
exclude: string[]
artifact?: string
individually?: boolean
}

interface PluginManager {
spawn(command: string): Promise<void>
}
}
101 changes: 53 additions & 48 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
import * as path from 'path'
import * as fs from 'fs-extra'

import * as _ from 'lodash'
import * as globby from 'globby'

import { ServerlessOptions, ServerlessInstance, ServerlessFunction } from './types'
import * as typescript from './typescript'

import { watchFiles } from './watchFiles'

// Folders
const serverlessFolder = '.serverless'
const buildFolder = '.build'
const SERVERLESS_FOLDER = '.serverless'
const BUILD_FOLDER = '.build'

export class TypeScriptPlugin {

private originalServicePath: string
private isWatching: boolean

serverless: ServerlessInstance
options: ServerlessOptions
commands: { [key: string]: any }
serverless: Serverless.Instance
options: Serverless.Options
hooks: { [key: string]: Function }

constructor(serverless: ServerlessInstance, options: ServerlessOptions) {
constructor(serverless: Serverless.Instance, options: Serverless.Options) {
this.serverless = serverless
this.options = options

Expand Down Expand Up @@ -55,31 +49,42 @@ export class TypeScriptPlugin {
'after:invoke:local:invoke': () => {
if (this.options.watch) {
this.watchFunction()
this.serverless.cli.log('Waiting for changes ...')
this.serverless.cli.log('Waiting for changes...')
}
}
}
}

get functions() {
return this.options.function
? { [this.options.function] : this.serverless.service.functions[this.options.function] }
: this.serverless.service.functions
const { options } = this
const { service } = this.serverless

if (options.function) {
return {
[options.function]: service.functions[this.options.function]
}
}

return service.functions
}

get rootFileNames() {
return typescript.extractFileNames(this.originalServicePath, this.serverless.service.provider.name, this.functions)
return typescript.extractFileNames(
this.originalServicePath,
this.serverless.service.provider.name,
this.functions
)
}

prepare() {
// exclude serverless-plugin-typescript
const functions = this.functions
for (const fnName in functions) {
const fn = functions[fnName]
for (const fnName in this.functions) {
const fn = this.functions[fnName]
fn.package = fn.package || {
exclude: [],
include: [],
}

// Add plugin to excluded packages or an empty array if exclude is undefined
fn.package.exclude = _.uniq([...fn.package.exclude || [], 'node_modules/serverless-plugin-typescript'])
}
Expand All @@ -106,9 +111,7 @@ export class TypeScriptPlugin {
this.serverless.cli.log(`Watching typescript files...`)

this.isWatching = true
watchFiles(this.rootFileNames, this.originalServicePath, () => {
this.compileTs()
})
watchFiles(this.rootFileNames, this.originalServicePath, this.compileTs)
}

async compileTs(): Promise<string[]> {
Expand All @@ -119,25 +122,26 @@ export class TypeScriptPlugin {
// Save original service path and functions
this.originalServicePath = this.serverless.config.servicePath
// Fake service path so that serverless will know what to zip
this.serverless.config.servicePath = path.join(this.originalServicePath, buildFolder)
this.serverless.config.servicePath = path.join(this.originalServicePath, BUILD_FOLDER)
}

const tsconfig = typescript.getTypescriptConfig(
this.originalServicePath,
this.isWatching ? null : this.serverless.cli
)

tsconfig.outDir = buildFolder
tsconfig.outDir = BUILD_FOLDER

const emitedFiles = await typescript.run(this.rootFileNames, tsconfig)
await this.copyExtras()
this.serverless.cli.log('Typescript compiled.')
return emitedFiles
}

/** Link or copy extras such as node_modules or package.include definitions */
async copyExtras() {
const outPkgPath = path.resolve(path.join(buildFolder, 'package.json'))
const outModulesPath = path.resolve(path.join(buildFolder, 'node_modules'))
const outPkgPath = path.resolve(path.join(BUILD_FOLDER, 'package.json'))
const outModulesPath = path.resolve(path.join(BUILD_FOLDER, 'node_modules'))

// Link or copy node_modules and package.json to .build so Serverless can
// exlcude devDeps during packaging
Expand All @@ -154,53 +158,58 @@ export class TypeScriptPlugin {
const files = await globby(this.serverless.service.package.include)

for (const filename of files) {
const destFileName = path.resolve(path.join(buildFolder, filename))
const destFileName = path.resolve(path.join(BUILD_FOLDER, filename))
const dirname = path.dirname(destFileName)

if (!fs.existsSync(dirname)) {
fs.mkdirpSync(dirname)
}

if (!fs.existsSync(destFileName)) {
fs.copySync(path.resolve(filename), path.resolve(path.join(buildFolder, filename)))
fs.copySync(path.resolve(filename), path.resolve(path.join(BUILD_FOLDER, filename)))
}
}
}
}

/**
* Move built code to the serverless folder, taking into account individual
* packaging preferences.
*/
async moveArtifacts(): Promise<void> {
const { service } = this.serverless

await fs.copy(
path.join(this.originalServicePath, buildFolder, serverlessFolder),
path.join(this.originalServicePath, serverlessFolder)
path.join(this.originalServicePath, BUILD_FOLDER, SERVERLESS_FOLDER),
path.join(this.originalServicePath, SERVERLESS_FOLDER)
)

if (this.options.function) {
const fn = this.serverless.service.functions[this.options.function]
const basename = path.basename(fn.package.artifact)
fn.package.artifact = path.join(
const fn = service.functions[this.options.function]
fn.package.artifact = path.join(
this.originalServicePath,
serverlessFolder,
SERVERLESS_FOLDER,
path.basename(fn.package.artifact)
)
return
}

if (this.serverless.service.package.individually) {
const functionNames = this.serverless.service.getAllFunctions()
if (service.package.individually) {
const functionNames = service.getAllFunctions()
functionNames.forEach(name => {
this.serverless.service.functions[name].package.artifact = path.join(
service.functions[name].package.artifact = path.join(
this.originalServicePath,
serverlessFolder,
path.basename(this.serverless.service.functions[name].package.artifact)
SERVERLESS_FOLDER,
path.basename(service.functions[name].package.artifact)
)
})
return
}

this.serverless.service.package.artifact = path.join(
service.package.artifact = path.join(
this.originalServicePath,
serverlessFolder,
path.basename(this.serverless.service.package.artifact)
SERVERLESS_FOLDER,
path.basename(service.package.artifact)
)
}

Expand All @@ -209,18 +218,14 @@ export class TypeScriptPlugin {
// Restore service path
this.serverless.config.servicePath = this.originalServicePath
// Remove temp build folder
fs.removeSync(path.join(this.originalServicePath, buildFolder))
fs.removeSync(path.join(this.originalServicePath, BUILD_FOLDER))
}

/**
* Attempt to symlink a given path or directory and copy if it fails with an
* `EPERM` error.
*/
private async linkOrCopy(
srcPath: string,
dstPath: string,
type?: 'dir' | 'junction' | 'file'
): Promise<void> {
private async linkOrCopy(srcPath: string, dstPath: string, type?: fs.FsSymlinkType): Promise<void> {
return fs.symlink(srcPath, dstPath, type)
.catch(error => {
if (error.code === 'EPERM' && error.errno === -4048) {
Expand Down
39 changes: 0 additions & 39 deletions src/types.ts

This file was deleted.

4 changes: 1 addition & 3 deletions src/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as ts from 'typescript'
import * as fs from 'fs-extra'
import * as _ from 'lodash'
import { ServerlessFunction } from './types'
import * as path from 'path'

export function makeDefaultTypescriptConfig() {
Expand All @@ -19,8 +18,7 @@ export function makeDefaultTypescriptConfig() {
return defaultTypescriptConfig
}

export function extractFileNames(cwd: string, provider: string, functions?: { [key: string]: ServerlessFunction }): 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
Expand Down
10 changes: 2 additions & 8 deletions src/watchFiles.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import * as typescript from './typescript'
import * as ts from 'typescript'
import { watchFile, unwatchFile, Stats} from 'fs'
import { ServerlessOptions, ServerlessInstance, ServerlessFunction } from './types'

export function watchFiles(
rootFileNames: string[],
originalServicePath: string,
cb: () => void
) {
export function watchFiles(rootFileNames: string[], originalServicePath: string, cb: () => void) {
const tsConfig = typescript.getTypescriptConfig(originalServicePath)
let watchedFiles = typescript.getSourceFiles(rootFileNames, tsConfig)

Expand All @@ -18,7 +12,7 @@ export function watchFiles(
function watchCallback(curr: Stats, prev: Stats) {
// Check timestamp
if (+curr.mtime <= +prev.mtime) {
return
return
}

cb()
Expand Down
3 changes: 1 addition & 2 deletions tests/typescript.extractFileName.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {extractFileNames} from '../src/typescript'
import {ServerlessFunction} from '../src/types'
import * as path from 'path'

const functions: { [key: string]: ServerlessFunction } = {
const functions: { [key: string]: Serverless.Function } = {
hello: {
handler: 'tests/assets/hello.handler',
package: {
Expand Down