1 | const { superstruct } = require('superstruct')
|
2 | const getFileNames = require('./getFileNames')
|
3 |
|
4 | module.exports = (api, config) => {
|
5 | api.logger.debug('Validating config', config)
|
6 | const struct = superstruct()
|
7 |
|
8 | const entry = struct.optional(struct.union(['string', 'array', 'object']))
|
9 |
|
10 | const output = struct(
|
11 | {
|
12 | dir: 'string',
|
13 | sourceMap: 'boolean',
|
14 | minimize: 'boolean',
|
15 | format: struct.enum(['iife', 'cjs', 'umd']),
|
16 | moduleName: struct.optional('string'),
|
17 | publicUrl: 'string',
|
18 | target: struct.enum([
|
19 | 'web',
|
20 | 'node',
|
21 | 'electron',
|
22 | 'electron-renderer',
|
23 | 'electron-main',
|
24 | 'node-webkit',
|
25 | 'webworker',
|
26 | 'async-node'
|
27 | ]),
|
28 | clean: 'boolean',
|
29 | fileNames: struct.optional(
|
30 | struct.object({
|
31 | js: struct.optional('string'),
|
32 | css: struct.optional('string'),
|
33 | font: struct.optional('string'),
|
34 | image: struct.optional('string')
|
35 | })
|
36 | ),
|
37 | html: struct.optional(struct.union(['boolean', 'object']))
|
38 | },
|
39 | {
|
40 | dir: 'dist',
|
41 | sourceMap: !api.isProd,
|
42 | minimize: api.isProd,
|
43 | format: 'iife',
|
44 | publicUrl: '/',
|
45 | target: 'web',
|
46 | default: true,
|
47 | clean: true
|
48 | }
|
49 | )
|
50 |
|
51 | const babel = struct(
|
52 | {
|
53 | jsx: 'string',
|
54 | transpileModules: struct.optional(struct.list(['string'])),
|
55 | namedImports: struct.optional('object')
|
56 | },
|
57 | {
|
58 | jsx: 'react'
|
59 | }
|
60 | )
|
61 |
|
62 | const css = struct(
|
63 | {
|
64 | extract: 'boolean',
|
65 | loaderOptions: struct.optional(
|
66 | struct.object({
|
67 | css: struct.optional('object'),
|
68 | less: struct.optional('object'),
|
69 | postcss: struct.optional('object'),
|
70 | sass: struct.optional('object'),
|
71 | stylus: struct.optional('object')
|
72 | })
|
73 | )
|
74 | },
|
75 | {
|
76 | extract: api.isProd
|
77 | }
|
78 | )
|
79 |
|
80 | const devServer = struct(
|
81 | {
|
82 | hot: 'boolean',
|
83 | hotOnly: 'boolean?',
|
84 | host: 'string',
|
85 | port: struct.union(['string', 'number']),
|
86 | hotEntries: struct(['string']),
|
87 | headers: struct.optional('object'),
|
88 | proxy: struct.optional(
|
89 | struct.union([
|
90 | 'string',
|
91 | 'object',
|
92 | 'function',
|
93 | struct([struct.union(['object', 'function'])])
|
94 | ])
|
95 | ),
|
96 | open: 'boolean',
|
97 | historyApiFallback: struct.optional(struct.union(['boolean', 'object'])),
|
98 | before: struct.optional('function'),
|
99 | after: struct.optional('function'),
|
100 | https: struct.optional(struct.union(['boolean', 'object']))
|
101 | },
|
102 | {
|
103 | hot: true,
|
104 |
|
105 | host: process.env.HOST || '0.0.0.0',
|
106 | port: process.env.PORT || 4000,
|
107 | hotEntries: ['index'],
|
108 | open: false
|
109 | }
|
110 | )
|
111 |
|
112 | const plugins = struct.optional([
|
113 | struct.union([
|
114 | 'string',
|
115 | struct({
|
116 | resolve: struct.union(['string', 'object']),
|
117 | options: struct.optional('object')
|
118 | })
|
119 | ])
|
120 | ])
|
121 |
|
122 | const assets = struct(
|
123 | {
|
124 | inlineImageMaxSize: struct.optional('number')
|
125 | },
|
126 | {
|
127 | inlineImageMaxSize: 5000
|
128 | }
|
129 | )
|
130 |
|
131 | const Struct = struct(
|
132 | {
|
133 | entry,
|
134 | output,
|
135 | plugins,
|
136 | parallel: 'boolean',
|
137 | cache: 'boolean',
|
138 | babel,
|
139 | css,
|
140 | devServer,
|
141 | envs: struct.optional('object'),
|
142 | constants: struct.optional('object'),
|
143 | chainWebpack: struct.optional('function'),
|
144 | configureWebpack: struct.optional(struct.union(['object', 'function'])),
|
145 | assets,
|
146 | publicFolder: struct.union(['string', 'boolean']),
|
147 | pages: struct.optional('object')
|
148 | },
|
149 | {
|
150 | cache: true,
|
151 | parallel: false,
|
152 | publicFolder: 'public'
|
153 | }
|
154 | )
|
155 |
|
156 | const [error, result] = Struct.validate(config)
|
157 |
|
158 | if (error) {
|
159 | throw error
|
160 | }
|
161 |
|
162 | result.output.fileNames = Object.assign(
|
163 | getFileNames({ useHash: api.isProd, format: result.output.format }),
|
164 | result.output.fileNames
|
165 | )
|
166 |
|
167 | if (!result.entry) {
|
168 | if (api.pkg.data.source) {
|
169 | api.logger.debug(
|
170 | 'Using the value of `source` field in `package.json` as app entry'
|
171 | )
|
172 | result.entry = api.pkg.data.source
|
173 | } else {
|
174 | api.logger.debug('Using `index` as app entry')
|
175 | result.entry = 'index'
|
176 | }
|
177 | }
|
178 |
|
179 |
|
180 | if (process.env.NODE_ENV === 'test') {
|
181 | result.cache = false
|
182 | }
|
183 |
|
184 |
|
185 | result.output.publicUrl = result.output.publicUrl
|
186 |
|
187 | .replace(/\/?$/, '/')
|
188 |
|
189 | .replace(/^\.\//, '')
|
190 |
|
191 | api.logger.debug('Validated config', result)
|
192 |
|
193 | return result
|
194 | }
|