Skip to content

Commit 6c15f35

Browse files
author
John Reeves
committed
moved TypeScriptPlugin class into its own file.
This fixes issues with the mixed types of exports so other code (tests) can import TypeScriptPlugin.
1 parent 424fe49 commit 6c15f35

File tree

2 files changed

+277
-276
lines changed

2 files changed

+277
-276
lines changed

src/index.ts

+1-276
Original file line numberDiff line numberDiff line change
@@ -1,278 +1,3 @@
1-
import * as path from 'path'
2-
import * as fs from 'fs-extra'
3-
import * as _ from 'lodash'
4-
import * as globby from 'globby'
5-
6-
import * as typescript from './typescript'
7-
import { watchFiles } from './watchFiles'
8-
9-
const SERVERLESS_FOLDER = '.serverless'
10-
const BUILD_FOLDER = '.build'
11-
12-
export class TypeScriptPlugin {
13-
private originalServicePath: string
14-
private isWatching: boolean
15-
16-
serverless: Serverless.Instance
17-
options: Serverless.Options
18-
hooks: { [key: string]: Function }
19-
20-
constructor(serverless: Serverless.Instance, options: Serverless.Options) {
21-
this.serverless = serverless
22-
this.options = options
23-
24-
this.hooks = {
25-
'before:run:run': async () => {
26-
await this.compileTs()
27-
await this.copyExtras()
28-
await this.copyDependencies()
29-
},
30-
'before:offline:start': async () => {
31-
await this.compileTs()
32-
await this.copyExtras()
33-
await this.copyDependencies()
34-
this.watchAll()
35-
},
36-
'before:offline:start:init': async () => {
37-
await this.compileTs()
38-
await this.copyExtras()
39-
await this.copyDependencies()
40-
this.watchAll()
41-
},
42-
'before:package:createDeploymentArtifacts': async () => {
43-
await this.compileTs()
44-
await this.copyExtras()
45-
await this.copyDependencies(true)
46-
},
47-
'after:package:createDeploymentArtifacts': async () => {
48-
await this.cleanup()
49-
},
50-
'before:deploy:function:packageFunction': async () => {
51-
await this.compileTs()
52-
await this.copyExtras()
53-
await this.copyDependencies(true)
54-
},
55-
'after:deploy:function:packageFunction': async () => {
56-
await this.cleanup()
57-
},
58-
'before:invoke:local:invoke': async () => {
59-
const emitedFiles = await this.compileTs()
60-
await this.copyExtras()
61-
await this.copyDependencies()
62-
if (this.isWatching) {
63-
emitedFiles.forEach(filename => {
64-
const module = require.resolve(path.resolve(this.originalServicePath, filename))
65-
delete require.cache[module]
66-
})
67-
}
68-
},
69-
'after:invoke:local:invoke': () => {
70-
if (this.options.watch) {
71-
this.watchFunction()
72-
this.serverless.cli.log('Waiting for changes...')
73-
}
74-
}
75-
}
76-
}
77-
78-
get functions() {
79-
const { options } = this
80-
const { service } = this.serverless
81-
82-
if (options.function) {
83-
return {
84-
[options.function]: service.functions[this.options.function]
85-
}
86-
}
87-
88-
return service.functions
89-
}
90-
91-
get rootFileNames() {
92-
return typescript.extractFileNames(
93-
this.originalServicePath,
94-
this.serverless.service.provider.name,
95-
this.functions
96-
)
97-
}
98-
99-
prepare() {
100-
// exclude serverless-plugin-typescript
101-
for (const fnName in this.functions) {
102-
const fn = this.functions[fnName]
103-
fn.package = fn.package || {
104-
exclude: [],
105-
include: [],
106-
}
107-
108-
// Add plugin to excluded packages or an empty array if exclude is undefined
109-
fn.package.exclude = _.uniq([...fn.package.exclude || [], 'node_modules/serverless-plugin-typescript'])
110-
}
111-
}
112-
113-
async watchFunction(): Promise<void> {
114-
if (this.isWatching) {
115-
return
116-
}
117-
118-
this.serverless.cli.log(`Watch function ${this.options.function}...`)
119-
120-
this.isWatching = true
121-
watchFiles(this.rootFileNames, this.originalServicePath, () => {
122-
this.serverless.pluginManager.spawn('invoke:local')
123-
})
124-
}
125-
126-
async watchAll(): Promise<void> {
127-
if (this.isWatching) {
128-
return
129-
}
130-
131-
this.serverless.cli.log(`Watching typescript files...`)
132-
133-
this.isWatching = true
134-
watchFiles(this.rootFileNames, this.originalServicePath, this.compileTs.bind(this))
135-
}
136-
137-
async compileTs(): Promise<string[]> {
138-
this.prepare()
139-
this.serverless.cli.log('Compiling with Typescript...')
140-
141-
if (!this.originalServicePath) {
142-
// Save original service path and functions
143-
this.originalServicePath = this.serverless.config.servicePath
144-
// Fake service path so that serverless will know what to zip
145-
this.serverless.config.servicePath = path.join(this.originalServicePath, BUILD_FOLDER)
146-
}
147-
148-
const tsconfig = typescript.getTypescriptConfig(
149-
this.originalServicePath,
150-
this.isWatching ? null : this.serverless.cli
151-
)
152-
153-
tsconfig.outDir = BUILD_FOLDER
154-
155-
const emitedFiles = await typescript.run(this.rootFileNames, tsconfig)
156-
this.serverless.cli.log('Typescript compiled.')
157-
return emitedFiles
158-
}
159-
160-
/** Link or copy extras such as node_modules or package.include definitions */
161-
async copyExtras() {
162-
const { service } = this.serverless
163-
164-
// include any "extras" from the "include" section
165-
if (service.package.include && service.package.include.length > 0) {
166-
const files = await globby(service.package.include)
167-
168-
for (const filename of files) {
169-
const destFileName = path.resolve(path.join(BUILD_FOLDER, filename))
170-
const dirname = path.dirname(destFileName)
171-
172-
if (!fs.existsSync(dirname)) {
173-
fs.mkdirpSync(dirname)
174-
}
175-
176-
if (!fs.existsSync(destFileName)) {
177-
fs.copySync(path.resolve(filename), path.resolve(path.join(BUILD_FOLDER, filename)))
178-
}
179-
}
180-
}
181-
}
182-
183-
/**
184-
* Copy the `node_modules` folder and `package.json` files to the output
185-
* directory.
186-
* @param isPackaging Provided if serverless is packaging the service for deployment
187-
*/
188-
async copyDependencies(isPackaging = false) {
189-
const outPkgPath = path.resolve(path.join(BUILD_FOLDER, 'package.json'))
190-
const outModulesPath = path.resolve(path.join(BUILD_FOLDER, 'node_modules'))
191-
192-
// copy development dependencies during packaging
193-
if (isPackaging) {
194-
if (fs.existsSync(outModulesPath)) {
195-
fs.unlinkSync(outModulesPath)
196-
}
197-
198-
fs.copySync(
199-
path.resolve('node_modules'),
200-
path.resolve(path.join(BUILD_FOLDER, 'node_modules'))
201-
)
202-
} else {
203-
if (!fs.existsSync(outModulesPath)) {
204-
await this.linkOrCopy(path.resolve('node_modules'), outModulesPath, 'junction')
205-
}
206-
}
207-
208-
// copy/link package.json
209-
if (!fs.existsSync(outPkgPath)) {
210-
await this.linkOrCopy(path.resolve('package.json'), outPkgPath, 'file')
211-
}
212-
}
213-
214-
/**
215-
* Move built code to the serverless folder, taking into account individual
216-
* packaging preferences.
217-
*/
218-
async moveArtifacts(): Promise<void> {
219-
const { service } = this.serverless
220-
221-
await fs.copy(
222-
path.join(this.originalServicePath, BUILD_FOLDER, SERVERLESS_FOLDER),
223-
path.join(this.originalServicePath, SERVERLESS_FOLDER)
224-
)
225-
226-
if (this.options.function) {
227-
const fn = service.functions[this.options.function]
228-
fn.package.artifact = path.join(
229-
this.originalServicePath,
230-
SERVERLESS_FOLDER,
231-
path.basename(fn.package.artifact)
232-
)
233-
return
234-
}
235-
236-
if (service.package.individually) {
237-
const functionNames = service.getAllFunctions()
238-
functionNames.forEach(name => {
239-
service.functions[name].package.artifact = path.join(
240-
this.originalServicePath,
241-
SERVERLESS_FOLDER,
242-
path.basename(service.functions[name].package.artifact)
243-
)
244-
})
245-
return
246-
}
247-
248-
service.package.artifact = path.join(
249-
this.originalServicePath,
250-
SERVERLESS_FOLDER,
251-
path.basename(service.package.artifact)
252-
)
253-
}
254-
255-
async cleanup(): Promise<void> {
256-
await this.moveArtifacts()
257-
// Restore service path
258-
this.serverless.config.servicePath = this.originalServicePath
259-
// Remove temp build folder
260-
fs.removeSync(path.join(this.originalServicePath, BUILD_FOLDER))
261-
}
262-
263-
/**
264-
* Attempt to symlink a given path or directory and copy if it fails with an
265-
* `EPERM` error.
266-
*/
267-
private async linkOrCopy(srcPath: string, dstPath: string, type?: fs.FsSymlinkType): Promise<void> {
268-
return fs.symlink(srcPath, dstPath, type)
269-
.catch(error => {
270-
if (error.code === 'EPERM' && error.errno === -4048) {
271-
return fs.copy(srcPath, dstPath)
272-
}
273-
throw error
274-
})
275-
}
276-
}
1+
import {TypeScriptPlugin} from './typeScriptPlugin'
2772

2783
module.exports = TypeScriptPlugin

0 commit comments

Comments
 (0)