-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
/
Copy pathGeneratorAPI.js
122 lines (111 loc) · 3.73 KB
/
GeneratorAPI.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
const fs = require('fs')
const ejs = require('ejs')
const path = require('path')
const walk = require('klaw-sync')
const isBinary = require('isbinaryfile')
const mergeDeps = require('./util/mergeDeps')
const errorParser = require('error-stack-parser')
const isString = val => typeof val === 'string'
const isFunction = val => typeof val === 'function'
const isObject = val => val && typeof val === 'object'
module.exports = class GeneratorAPI {
constructor (id, generator, options, rootOptions) {
this.id = id
this.generator = generator
this.options = options
this.rootOptions = rootOptions
}
injectFileMiddleware (middleware) {
this.generator.fileMiddlewares.push(middleware)
}
extendPackage (fields, options = { merge: true }) {
const pkg = this.generator.pkg
const toMerge = isFunction(fields) ? fields(pkg) : fields
for (const key in toMerge) {
const value = toMerge[key]
const existing = pkg[key]
if (isObject(value) && (key === 'dependencies' || key === 'devDependencies')) {
// use special version resolution merge
pkg[key] = mergeDeps(
this.id,
existing || {},
value,
this.generator.depSources
)
} else if (!options.merge || !(key in pkg)) {
pkg[key] = value
} else if (Array.isArray(value) && Array.isArray(existing)) {
pkg[key] = existing.concat(value)
} else if (isObject(value) && isObject(existing)) {
pkg[key] = Object.assign({}, existing, value)
} else {
pkg[key] = value
}
}
}
render (fileDir, additionalData = {}, ejsOptions = {}) {
const baseDir = extractCallDir()
if (isString(fileDir)) {
fileDir = path.resolve(baseDir, fileDir)
this.injectFileMiddleware(files => {
const data = Object.assign({
options: this.options,
rootOptions: this.rootOptions
}, additionalData)
const _files = walk(fileDir, {
nodir: true,
filter: file => path.basename(file.path) !== '.DS_Store'
})
for (const file of _files) {
const relativePath = path.relative(fileDir, file.path)
const content = renderFile(file.path, data, ejsOptions)
// only set file if it's not all whitespace, or is a Buffer (binary files)
if (Buffer.isBuffer(content) || /[^\s]/.test(content)) {
files[relativePath] = content
}
}
})
} else if (isObject(fileDir)) {
this.injectFileMiddleware(files => {
const data = Object.assign({
options: this.options,
rootOptions: this.rootOptions
}, additionalData)
for (const targetPath in fileDir) {
const sourcePath = path.resolve(baseDir, fileDir[targetPath])
const content = renderFile(sourcePath, data, ejsOptions)
if (Buffer.isBuffer(content) || content.trim()) {
files[targetPath] = content
}
}
})
} else if (isFunction(fileDir)) {
this.injectFileMiddleware(fileDir)
}
}
postProcessFiles (cb) {
this.generator.postProcessFilesCbs.push(cb)
}
onCreateComplete (cb) {
this.generator.completeCbs.push(cb)
}
resolve (_path) {
return path.resolve(this.generator.context, _path)
}
hasPlugin (id) {
return this.generator.hasPlugin(id)
}
}
function extractCallDir () {
// extract api.render() callsite file location using error stack
const obj = {}
Error.captureStackTrace(obj)
const stack = errorParser.parse(obj)
return path.dirname(stack[2].fileName)
}
function renderFile (name, data, ejsOptions) {
if (isBinary.sync(name)) {
return fs.readFileSync(name) // return buffer
}
return ejs.render(fs.readFileSync(name, 'utf-8'), data, ejsOptions)
}