1
1
import path from "path" ;
2
2
import webpack , { Configuration } from "webpack" ;
3
+ import fs from "fs-extra" ;
3
4
4
5
type PluginName = "react" ;
5
6
const PLUGIN_NAME : PluginName = "react" ;
@@ -154,35 +155,51 @@ class ServerlessReact {
154
155
this . hooks = {
155
156
initialize : async ( ) => { } ,
156
157
"react:validate" : async ( ) => {
157
- console . log ( "!!!! react:validate") ;
158
+ this . log . verbose ( " react:validate") ;
158
159
} ,
159
160
"react:build" : async ( ) => {
160
- console . log ( "!!!! react:build") ;
161
+ this . log . verbose ( " react:build") ;
161
162
} ,
162
163
"before:offline:start" : async ( ) => {
163
- this . log . verbose ( "before:offline:start" ) ;
164
- await this . build ( ) ;
164
+ const { config , compiler } = await this . build ( ) ;
165
+ await this . watch ( config , compiler ) ;
165
166
} ,
166
167
"before:offline:start:init" : async ( ) => {
167
- this . log . verbose ( "before:offline:start:init" ) ;
168
- await this . build ( ) ;
168
+ const { config , compiler } = await this . build ( ) ;
169
+ await this . watch ( config , compiler ) ;
169
170
} ,
170
171
} ;
171
172
}
172
173
173
- build = async ( ) : Promise < void > => {
174
+ build = async ( ) : Promise < {
175
+ config : webpack . Configuration ;
176
+ compiler : webpack . Compiler ;
177
+ } > => {
174
178
// TODO Check if react-scripts exists
179
+ process . env . BABEL_ENV = "production" ;
180
+ process . env . NODE_ENV = "production" ;
175
181
176
- const webpackConfig = path . join (
182
+ require ( path . join (
183
+ this . serverlessConfig . servicePath ,
184
+ this . pluginConfig . webpackConfig || "node_modules/react-scripts/config/env"
185
+ ) ) ;
186
+
187
+ const paths = require ( path . join (
188
+ this . serverlessConfig . servicePath ,
189
+ this . pluginConfig . webpackConfig ||
190
+ "node_modules/react-scripts/config/paths"
191
+ ) ) ;
192
+
193
+ const { checkBrowsers } = require ( "react-dev-utils/browsersHelper" ) ;
194
+ await checkBrowsers ( paths . appPath , false ) ;
195
+
196
+ const configFactory = require ( path . join (
177
197
this . serverlessConfig . servicePath ,
178
198
this . pluginConfig . webpackConfig ||
179
199
"node_modules/react-scripts/config/webpack.config.js"
180
- ) ;
200
+ ) ) ;
181
201
182
- const configFactory = require ( webpackConfig ) ;
183
- const config : Configuration = configFactory (
184
- process . env . NODE_ENV === "development" ? "development" : "production"
185
- ) ;
202
+ const config : Configuration = configFactory ( "production" ) ;
186
203
187
204
if ( ! config . output ) {
188
205
throw new Error ( "No output config in webpack config" ) ;
@@ -193,40 +210,83 @@ class ServerlessReact {
193
210
`.${ PLUGIN_NAME } `
194
211
) ;
195
212
196
- // TODO use config.entry as a fallback
197
- // config.entry = path.join(
198
- // this.serverlessConfig.servicePath,
199
- // this.pluginConfig.entryPoint || "src/index.js"
200
- // );
201
-
202
- // TODO appSrc
203
- // TODO publicUrlOrPath
204
-
205
- // TODO Copy public dir
213
+ fs . emptyDirSync ( config . output . path ) ;
214
+ fs . copySync ( paths . appPublic , config . output . path , {
215
+ dereference : true ,
216
+ filter : ( file ) => file !== paths . appHtml ,
217
+ } ) ;
206
218
207
219
const compiler = webpack ( config ) ;
208
220
209
221
return new Promise ( ( resolve , reject ) => {
222
+ this . log . verbose ( `[${ config . entry } ] Starting webpack build...` ) ;
223
+
210
224
compiler . run ( ( err , stats ) => {
211
- if ( err ) {
212
- return reject ( err ) ;
213
- }
214
- if ( ! stats ) {
215
- return reject ( new Error ( "No stats from webpack" ) ) ;
216
- }
217
- if ( stats . hasErrors ( ) ) {
218
- // TODO Formatting like build.js
219
- return reject ( new Error ( stats . toJson ( ) . errors ?. join ( "\n\n" ) ) ) ;
225
+ try {
226
+ this . handleWebpackError ( config , err ) ;
227
+ } catch ( error : any ) {
228
+ this . log . error ( `[${ config . entry } ] ${ error . message } ` ) ;
229
+ return reject ( ) ;
220
230
}
221
231
222
- // TODO fail on process.env.CI + warnings
223
- console . log ( "Compiled with warnings.\n" ) ;
224
- console . log ( stats . toJson ( ) . warnings ?. join ( "\n\n" ) ) ;
232
+ try {
233
+ this . handleWebpackStats ( config , stats ) ;
234
+ } catch ( error : any ) {
235
+ this . log . error ( `[${ config . entry } ] ${ error . message } ` ) ;
236
+ return reject ( ) ;
237
+ }
225
238
226
- resolve ( ) ;
239
+ this . log . verbose ( `[${ config . entry } ] Webpack build complete.` ) ;
240
+ resolve ( { config, compiler } ) ;
227
241
} ) ;
228
242
} ) ;
229
243
} ;
244
+
245
+ watch = async (
246
+ config : webpack . Configuration ,
247
+ _compiler : webpack . Compiler
248
+ ) => {
249
+ this . log . verbose ( `[${ config . entry } ] TODO: Watching for changes...` ) ;
250
+ } ;
251
+
252
+ handleWebpackError = (
253
+ _config : webpack . Configuration ,
254
+ error : Error | null | undefined
255
+ ) => {
256
+ if ( ! error ) {
257
+ return ;
258
+ }
259
+
260
+ throw new Error ( error . message ) ;
261
+ } ;
262
+
263
+ handleWebpackStats = (
264
+ _config : webpack . Configuration ,
265
+ stats ?: webpack . Stats
266
+ ) => {
267
+ if ( ! stats ) {
268
+ throw new Error ( `Webpack did not emit stats.` ) ;
269
+ }
270
+
271
+ const statsJson = stats . toJson ( ) ;
272
+
273
+ const { errors, warnings } = statsJson ;
274
+
275
+ if ( errors && errors . length > 0 ) {
276
+ throw new Error ( `Webpack failed to compile:\n${ errors . join ( "\n\n" ) } ` ) ;
277
+ }
278
+
279
+ if ( warnings && warnings . length > 0 ) {
280
+ const message = `Webpack compiled with warnings:\n${ warnings . join (
281
+ "\n\n"
282
+ ) } `;
283
+ if ( process . env . CI ) {
284
+ throw new Error ( message ) ;
285
+ } else {
286
+ this . log . warning ( message ) ;
287
+ }
288
+ }
289
+ } ;
230
290
}
231
291
232
292
module . exports = ServerlessReact ;
0 commit comments