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