1 | const path = require('path')
|
2 | const majo = require('majo')
|
3 | const matcher = require('micromatch')
|
4 | const { glob, fs } = require('majo')
|
5 | const isBinaryPath = require('is-binary-path')
|
6 | const logger = require('./logger')
|
7 | const getGlobPatterns = require('./utils/getGlobPatterns')
|
8 |
|
9 | module.exports = async (config, context) => {
|
10 | const actions =
|
11 | typeof config.actions === 'function' ?
|
12 | await config.actions.call(context, context) :
|
13 | config.actions
|
14 |
|
15 | for (const action of actions) {
|
16 | logger.debug('Running action:', action)
|
17 | if (action.type === 'add' && action.files) {
|
18 | const stream = majo()
|
19 | stream.source(['!**/node_modules/**'].concat(action.files), {
|
20 | baseDir: path.resolve(
|
21 | context.generator.path,
|
22 | config.templateDir || 'template'
|
23 | )
|
24 | })
|
25 |
|
26 | if (action.filters) {
|
27 | const excludedPatterns = getGlobPatterns(
|
28 | action.filters,
|
29 | context.answers,
|
30 | true
|
31 | )
|
32 |
|
33 | if (excludedPatterns.length > 0) {
|
34 | stream.use(() => {
|
35 | const excludedFiles = matcher(stream.fileList, excludedPatterns)
|
36 | for (const file of excludedFiles) {
|
37 | stream.deleteFile(file)
|
38 | }
|
39 | })
|
40 | }
|
41 | }
|
42 |
|
43 | const shouldTransform =
|
44 | typeof action.transform === 'boolean' ?
|
45 | action.transform :
|
46 | config.transform !== false
|
47 |
|
48 | if (shouldTransform) {
|
49 | stream.use(({ files }) => {
|
50 | let fileList = Object.keys(stream.files)
|
51 |
|
52 |
|
53 | fileList = fileList.filter(fp => !isBinaryPath(fp))
|
54 |
|
55 | if (action.transformInclude) {
|
56 | fileList = matcher(fileList, action.transformInclude)
|
57 | }
|
58 | if (action.transformExclude) {
|
59 | fileList = matcher.not(fileList, action.transformExclude)
|
60 | }
|
61 |
|
62 | const modulePaths = [context.generator.path, ...module.paths]
|
63 | const transformerName = config.transformer || 'ejs'
|
64 | let transformerPath
|
65 | try {
|
66 | transformerPath = require.resolve(
|
67 | `jstransformer-${transformerName}`,
|
68 | {
|
69 | paths: modulePaths
|
70 | }
|
71 | )
|
72 | } catch (err) {
|
73 | throw new Error(`Cannot resolve transfomer: ${transformerName}`)
|
74 | }
|
75 |
|
76 | const transformer = require('jstransformer')(
|
77 | require(transformerPath)
|
78 | )
|
79 |
|
80 | fileList.forEach(relativePath => {
|
81 | const contents = files[relativePath].contents.toString()
|
82 |
|
83 | logger.debug('context.data', context.data)
|
84 | stream.writeContents(
|
85 | relativePath,
|
86 | transformer.render(
|
87 | contents,
|
88 | config.transformerOptions,
|
89 | Object.assign({}, context.answers, {
|
90 | context
|
91 | }, context.data)
|
92 | ).body
|
93 | )
|
94 | })
|
95 | })
|
96 | }
|
97 | stream.on('write', (_, targetPath) => {
|
98 | logger.fileAction('magenta', 'Created', targetPath)
|
99 | })
|
100 |
|
101 | const target = action.target || config.target || '.'
|
102 | const outDir = path.resolve(context.outDir, target)
|
103 | logger.debug('add_action_outDir', outDir)
|
104 | await stream.dest(outDir)
|
105 | } else if (action.type === 'modify' && action.handler) {
|
106 | const stream = majo()
|
107 | stream.source(action.files, { baseDir: context.outDir })
|
108 | stream.use(async ({ files }) => {
|
109 | await Promise.all(
|
110 |
|
111 | Object.keys(files).map(async relativePath => {
|
112 | const isJson = relativePath.endsWith('.json')
|
113 | let contents = stream.fileContents(relativePath)
|
114 | if (isJson) {
|
115 | contents = JSON.parse(contents)
|
116 | }
|
117 | let result = await action.handler.call(context, contents, relativePath)
|
118 | if (isJson) {
|
119 | result = JSON.stringify(result, null, 2)
|
120 | }
|
121 | stream.writeContents(relativePath, result)
|
122 | logger.fileAction(
|
123 | 'yellow',
|
124 | 'Modified',
|
125 | path.join(context.outDir, relativePath)
|
126 | )
|
127 | })
|
128 | )
|
129 | })
|
130 | await stream.dest(context.outDir)
|
131 | } else if (action.type === 'move' && action.patterns) {
|
132 | await Promise.all(
|
133 | Object.keys(action.patterns).map(async pattern => {
|
134 | const files = await glob(pattern, {
|
135 | cwd: context.outDir,
|
136 | absolute: true,
|
137 | onlyFiles: false
|
138 | })
|
139 | if (files.length > 1) {
|
140 | throw new Error('"move" pattern can only match one file!')
|
141 | } else if (files.length === 1) {
|
142 | const from = files[0]
|
143 | const to = path.join(context.outDir, action.patterns[pattern])
|
144 | await fs.move(from, to, {
|
145 | overwrite: true
|
146 | })
|
147 | logger.fileMoveAction(from, to)
|
148 | }
|
149 | })
|
150 | )
|
151 | } else if (action.type === 'remove' && action.files) {
|
152 | let patterns
|
153 | if (typeof action.files === 'function') {
|
154 | action.files = action.files(context.answers)
|
155 | }
|
156 | if (typeof action.files === 'string' || Array.isArray(action.files)) {
|
157 | patterns = [].concat(action.files)
|
158 | } else if (typeof action.files === 'object') {
|
159 | patterns = getGlobPatterns(action.files, context.answers)
|
160 | }
|
161 | const files = await glob(patterns, {
|
162 | cwd: context.outDir,
|
163 | absolute: true
|
164 | })
|
165 | await Promise.all(
|
166 | files.map(file => {
|
167 | logger.fileAction('red', 'Removed', file)
|
168 | return fs.remove(file)
|
169 | })
|
170 | )
|
171 | }
|
172 | }
|
173 | }
|