1 | const fs = require('fs-extra')
|
2 | const path = require('path')
|
3 |
|
4 | const validatePathname = require('./validate-pathname')
|
5 | const validateConfigDist = require('./validate-config-dist')
|
6 |
|
7 | const getCwd = require('../utils/get-cwd')
|
8 | const readBuildConfigFile = require('../utils/read-build-config-file')
|
9 |
|
10 | const {
|
11 | keyFileProjectConfigTempFull,
|
12 | keyFileProjectConfigTempPortion,
|
13 | filenameProjectConfigTempFull,
|
14 | filenameProjectConfigTempPortion,
|
15 | propertiesToExtract: _propertiesToExtract,
|
16 | dirConfigTemp: _dirConfigTemp,
|
17 | typesSPA,
|
18 | } = require('../defaults/before-build')
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | module.exports = async (projectDir = getCwd()) => {
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | let fileFullConfig = typeof process.env.KOOT_BUILD_CONFIG_PATHNAME === 'string'
|
39 | ? process.env.KOOT_BUILD_CONFIG_PATHNAME
|
40 | : path.resolve(projectDir, 'koot.config.js')
|
41 |
|
42 |
|
43 | if (!fs.existsSync(fileFullConfig)) {
|
44 | const buildConfig = await readBuildConfigFile()
|
45 | return validateBuildConfig(buildConfig)
|
46 | }
|
47 |
|
48 |
|
49 | const fullConfig = { ...require(fileFullConfig) }
|
50 |
|
51 |
|
52 | const isCustomProjectConfig = typeof process.env.KOOT_PROJECT_CONFIG_FULL_PATHNAME === 'string'
|
53 |
|
54 |
|
55 | const propertiesToExtract = [..._propertiesToExtract]
|
56 |
|
57 |
|
58 | const isFullConfig = propertiesToExtract.some(([key]) => typeof fullConfig[key] !== 'undefined')
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | if (isFullConfig) {
|
67 |
|
68 | const dirConfigTemp = path.resolve(projectDir, _dirConfigTemp)
|
69 | await fs.ensureDir(dirConfigTemp)
|
70 |
|
71 |
|
72 | const isSPA = typesSPA.includes(fullConfig.type)
|
73 |
|
74 | if (isSPA) {
|
75 |
|
76 | propertiesToExtract.push(['inject', undefined])
|
77 |
|
78 |
|
79 | if (!fullConfig.inject &&
|
80 | typeof fullConfig.server === 'object' &&
|
81 | typeof fullConfig.server.inject !== 'undefined'
|
82 | ) {
|
83 | fullConfig.inject = fullConfig.server.inject
|
84 | }
|
85 | } else {
|
86 |
|
87 | if (!!fullConfig.inject &&
|
88 | typeof fullConfig.server === 'object' &&
|
89 | !fullConfig.server.inject
|
90 | ) {
|
91 | fullConfig.inject.server = fullConfig.inject
|
92 | delete fullConfig.inject
|
93 | }
|
94 | }
|
95 |
|
96 |
|
97 | const projectConfigFull = {}
|
98 | const projectConfigPortion = {}
|
99 | const propertiesPortion = [
|
100 | 'redux',
|
101 | 'server'
|
102 | ]
|
103 |
|
104 |
|
105 | const buildConfig = propertiesToExtract.reduce((configRemains, curr) => {
|
106 |
|
107 | const [key, defaultValue] = curr
|
108 | const value = configRemains[key] || defaultValue
|
109 | projectConfigFull[key] = typeof value === 'object' ? { ...value } : value
|
110 | if (propertiesPortion.includes(key))
|
111 | projectConfigPortion[key] = typeof value === 'object' ? { ...value } : value
|
112 | delete configRemains[key]
|
113 | return configRemains
|
114 | }, fullConfig)
|
115 |
|
116 | delete projectConfigPortion.server.onRender
|
117 |
|
118 |
|
119 | if (isCustomProjectConfig) {
|
120 | return {
|
121 | ...validateBuildConfig(buildConfig),
|
122 | [keyFileProjectConfigTempFull]: process.env.KOOT_PROJECT_CONFIG_FULL_PATHNAME,
|
123 | [keyFileProjectConfigTempPortion]: process.env.KOOT_PROJECT_CONFIG_PORTION_PATHNAME
|
124 | }
|
125 | }
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | const evalValue = (objectName, key) => {
|
140 | try {
|
141 | if (eval(`typeof ${objectName}.${key} === 'string'`)) {
|
142 | const value = eval(`${objectName}.${key}`)
|
143 | const pathname = path.isAbsolute(value)
|
144 | ? value
|
145 | : validatePathname(value, projectDir).replace(/\\/g, '\\\\')
|
146 | const result = path.isAbsolute(pathname)
|
147 | ? pathname
|
148 | : ('../../../' + pathname.replace(/^\.\//, ''))
|
149 | eval(`${objectName}.${key} = \`require('${result}').default\``)
|
150 | }
|
151 | } catch (e) { }
|
152 | }
|
153 | const validateProjectConfig = (keys) => {
|
154 | keys.forEach(key => {
|
155 | evalValue('projectConfigFull', key)
|
156 | evalValue('projectConfigPortion', key)
|
157 | })
|
158 | }
|
159 | validateProjectConfig([
|
160 | 'router',
|
161 | 'redux.combineReducers',
|
162 | 'redux.store',
|
163 | 'client.before',
|
164 | 'client.after',
|
165 | 'client.onRouterUpdate',
|
166 | 'client.onHistoryUpdate',
|
167 | 'server.reducers',
|
168 | 'server.inject',
|
169 | 'server.before',
|
170 | 'server.after',
|
171 | 'server.onRender',
|
172 | 'server.onRender.beforeDataToStore',
|
173 | 'server.onRender.afterDataToStore',
|
174 | 'inject',
|
175 | ])
|
176 |
|
177 |
|
178 |
|
179 | const tempFull = propertiesToExtract.map(([key]) => {
|
180 | let result = ''
|
181 | if (key === 'server') {
|
182 | if (isSPA) return ''
|
183 | result = `export const ${key} = __SERVER__ ? ${JSON.stringify(projectConfigFull[key])} : {};`
|
184 | } else {
|
185 | result = `export const ${key} = ${JSON.stringify(projectConfigFull[key])};`
|
186 | }
|
187 | return result
|
188 | })
|
189 | .join('\n')
|
190 | .replace(/"require\((.+?)\).default"/g, `require($1).default`)
|
191 |
|
192 | const tempPortion = propertiesPortion.map((key) => {
|
193 | let result = ''
|
194 | if (key === 'server') {
|
195 | if (isSPA) return ''
|
196 | result = `export const ${key} = __SERVER__ ? ${JSON.stringify(projectConfigPortion[key])} : {};`
|
197 | } else {
|
198 | result = `export const ${key} = ${JSON.stringify(projectConfigPortion[key])};`
|
199 | }
|
200 | return result
|
201 | })
|
202 | .join('\n')
|
203 | .replace(/"require\((.+?)\).default"/g, `require($1).default`)
|
204 |
|
205 |
|
206 |
|
207 |
|
208 | const pathFull = path.resolve(dirConfigTemp, filenameProjectConfigTempFull.replace(/\*/g, Date.now()))
|
209 | process.env.KOOT_PROJECT_CONFIG_FULL_PATHNAME = pathFull
|
210 | await fs.writeFile(pathFull, tempFull, 'utf-8')
|
211 |
|
212 | const pathPortion = path.resolve(dirConfigTemp, filenameProjectConfigTempPortion.replace(/\*/g, Date.now()))
|
213 | process.env.KOOT_PROJECT_CONFIG_PORTION_PATHNAME = pathPortion
|
214 | await fs.writeFile(pathPortion, tempPortion, 'utf-8')
|
215 |
|
216 | return {
|
217 | ...validateBuildConfig(buildConfig),
|
218 | [keyFileProjectConfigTempFull]: pathFull,
|
219 | [keyFileProjectConfigTempPortion]: pathPortion
|
220 | }
|
221 |
|
222 | } else {
|
223 |
|
224 | const buildConfig = await readBuildConfigFile()
|
225 | return validateBuildConfig(buildConfig)
|
226 | }
|
227 | }
|
228 |
|
229 |
|
230 | const validateBuildConfig = (config = {}) => {
|
231 |
|
232 |
|
233 | if (typeof config.dest !== 'undefined') {
|
234 | config.dist = config.dest
|
235 | delete config.dest
|
236 | }
|
237 | if (typeof config.dist !== 'undefined') {
|
238 | validateConfigDist(config.dist)
|
239 | }
|
240 |
|
241 |
|
242 | if (typeof config.webpack === 'object') {
|
243 | |
244 |
|
245 |
|
246 |
|
247 |
|
248 | const applyWebpackConfig = (nameInObject, nameAfter) => {
|
249 | if (typeof config.webpack[nameInObject] !== 'undefined') {
|
250 | config[nameAfter] = config.webpack[nameInObject]
|
251 | delete config.webpack[nameInObject]
|
252 | }
|
253 | }
|
254 | applyWebpackConfig('config', 'config')
|
255 | applyWebpackConfig('beforeBuild', 'beforeBuild')
|
256 | applyWebpackConfig('afterBuild', 'afterBuild')
|
257 | applyWebpackConfig('defines', 'defines')
|
258 | applyWebpackConfig('dll', 'webpackDll')
|
259 | applyWebpackConfig('hmr', 'webpackHmr')
|
260 | applyWebpackConfig('compilerHook', 'webpackCompilerHook')
|
261 | Object.keys(config.webpack).forEach(key => {
|
262 | applyWebpackConfig(key, 'webpack' + key.substr(0, 1).toUpperCase() + key.substr(1))
|
263 | })
|
264 | delete config.webpack
|
265 | }
|
266 |
|
267 | return config
|
268 | }
|