1
1
#!/usr/bin/env node
2
2
/***********************************************************************************************************************
3
3
4
- build.js (v1.6.0 , 2021-12-19 )
4
+ build.js (v1.7.1 , 2021-12-21 )
5
5
A Node.js-hosted build script for SugarCube.
6
6
7
7
Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
8
8
Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.
9
9
10
10
***********************************************************************************************************************/
11
- /* eslint-env node, es6 */
11
+ /* eslint-env node, es2021 */
12
12
'use strict' ;
13
13
14
+
14
15
/*******************************************************************************
15
16
Configuration
16
17
*******************************************************************************/
18
+
17
19
const CONFIG = {
18
20
js : {
19
- main : [
21
+ files : [
20
22
// The ordering herein is significant.
21
23
'src/lib/alert.js' ,
22
24
'src/lib/patterns.js' ,
@@ -67,20 +69,23 @@ const CONFIG = {
67
69
outro : 'src/templates/outro.js'
68
70
}
69
71
} ,
70
- css : [
71
- // The ordering herein is significant.
72
- 'src/vendor/normalize.css' ,
73
- 'src/css/init-screen.css' ,
74
- 'src/css/font.css' ,
75
- 'src/css/core.css' ,
76
- 'src/css/core-display.css' ,
77
- 'src/css/core-passage.css' ,
78
- 'src/css/core-macro.css' ,
79
- 'src/css/ui-dialog.css' ,
80
- 'src/css/ui.css' ,
81
- 'src/css/ui-bar.css' ,
82
- 'src/css/ui-debug.css'
83
- ] ,
72
+ css : {
73
+ mixins : 'src/css/_mixins.css' ,
74
+ files : [
75
+ // The ordering herein is significant.
76
+ 'src/vendor/normalize.css' ,
77
+ 'src/css/init-screen.css' ,
78
+ 'src/css/font.css' ,
79
+ 'src/css/core.css' ,
80
+ 'src/css/core-display.css' ,
81
+ 'src/css/core-passage.css' ,
82
+ 'src/css/core-macro.css' ,
83
+ 'src/css/ui-dialog.css' ,
84
+ 'src/css/ui.css' ,
85
+ 'src/css/ui-bar.css' ,
86
+ 'src/css/ui-debug.css'
87
+ ]
88
+ } ,
84
89
libs : [
85
90
// The ordering herein is significant.
86
91
'src/vendor/classList.min.js' ,
@@ -133,13 +138,12 @@ const CONFIG = {
133
138
/*******************************************************************************
134
139
Main Script
135
140
*******************************************************************************/
136
- /*
137
- NOTICE!
138
141
139
- Where string replacements are done, we use the replacement function style here to
140
- disable all special replacement patterns, since some of them likely exist within
141
- the replacement strings (e.g. '$&' within the application source).
142
- */
142
+ // NOTICE!
143
+ //
144
+ // Where string replacements are done, we use the replacement function style to
145
+ // disable all special replacement patterns, since some of them may exist within
146
+ // the replacement strings—e.g., `$&` within the HTML or JavaScript sources.
143
147
144
148
const {
145
149
log,
@@ -156,21 +160,15 @@ const _opt = require('node-getopt').create([
156
160
[ 'b' , 'build=VERSION' , 'Build only for Twine major version: 1 or 2; default: build for all.' ] ,
157
161
[ 'd' , 'debug' , 'Keep debugging code; gated by DEBUG symbol.' ] ,
158
162
[ 'u' , 'unminified' , 'Suppress minification stages.' ] ,
159
- [ '6 ' , 'es6' , 'Suppress JavaScript transpilation stages.' ] ,
163
+ [ 'n ' , 'no-transpile' , 'Suppress JavaScript transpilation stages.' ] ,
160
164
[ 'h' , 'help' , 'Print this help, then exit.' ]
161
165
] )
162
- . bindHelp ( ) // bind option 'help' to default action
163
- . parseSystem ( ) ; // parse command line
164
-
165
- // uglify-js (v2) does not currently support ES6, so force `unminified` when `es6` is enabled.
166
- if ( _opt . options . es6 && ! _opt . options . unminified ) {
167
- _opt . options . unminified = true ;
168
- }
166
+ . bindHelp ( )
167
+ . parseSystem ( ) ;
169
168
170
169
let _buildForTwine1 = true ;
171
170
let _buildForTwine2 = true ;
172
171
173
- // build selection
174
172
if ( _opt . options . build ) {
175
173
switch ( _opt . options . build ) {
176
174
case '1' :
@@ -187,8 +185,8 @@ if (_opt.options.build) {
187
185
}
188
186
}
189
187
190
- // build the project
191
- ( ( ) => {
188
+ // Build the project.
189
+ ( async ( ) => {
192
190
console . log ( 'Starting builds...' ) ;
193
191
194
192
// Create the build ID file, if nonexistent.
@@ -226,9 +224,9 @@ if (_opt.options.build) {
226
224
projectBuild ( {
227
225
build : CONFIG . twine1 . build ,
228
226
version : version , // eslint-disable-line object-shorthand
229
- libSource : assembleLibraries ( CONFIG . libs ) , // combine the libraries
230
- appSource : compileJavaScript ( CONFIG . js , { twine1 : true } ) , // combine and minify the app JS
231
- cssSource : compileStyles ( CONFIG . css ) // combine and minify the app CSS
227
+ libSource : assembleLibraries ( CONFIG . libs ) , // combine the libraries
228
+ appSource : await compileJavaScript ( CONFIG . js , { twine1 : true } ) , // combine and minify the app JS
229
+ cssSource : compileStyles ( CONFIG . css ) // combine and minify the app CSS
232
230
} ) ;
233
231
234
232
// Process the files that simply need copied into the build.
@@ -243,9 +241,9 @@ if (_opt.options.build) {
243
241
projectBuild ( {
244
242
build : CONFIG . twine2 . build ,
245
243
version : version , // eslint-disable-line object-shorthand
246
- libSource : assembleLibraries ( CONFIG . libs ) , // combine the libraries
247
- appSource : compileJavaScript ( CONFIG . js , { twine1 : false } ) , // combine and minify the app JS
248
- cssSource : compileStyles ( CONFIG . css ) , // combine and minify the app CSS
244
+ libSource : assembleLibraries ( CONFIG . libs ) , // combine the libraries
245
+ appSource : await compileJavaScript ( CONFIG . js , { twine1 : false } ) , // combine and minify the app JS
246
+ cssSource : compileStyles ( CONFIG . css ) , // combine and minify the app CSS
249
247
250
248
postProcess ( sourceString ) {
251
249
// Load the output format.
@@ -274,10 +272,9 @@ if (_opt.options.build) {
274
272
275
273
// Update the build ID.
276
274
writeFileContents ( '.build' , String ( version . build ) ) ;
277
- } ) ( ) ;
278
-
279
- // That's all folks!
280
- console . log ( '\nBuilds complete! (check the "build" directory)' ) ;
275
+ } ) ( )
276
+ . then ( ( ) => console . log ( '\nBuilds complete! (check the "build" directory)' ) )
277
+ . catch ( reason => console . log ( '\nERROR:' , reason ) ) ;
281
278
282
279
283
280
/*******************************************************************************
@@ -290,86 +287,92 @@ function assembleLibraries(filenames) {
290
287
}
291
288
292
289
function compileJavaScript ( filenameObj , options ) {
293
- /* eslint-disable camelcase, prefer-template */
294
290
log ( 'compiling JavaScript...' ) ;
295
291
296
- const babelCore = require ( 'babel-core' ) ;
297
- const babelOpts = {
298
- code : true ,
299
- compact : false ,
300
- presets : [ 'env' ] ,
301
- filename : 'sugarcube.js'
302
- } ;
303
-
304
- // Join the files and transpile (ES6 → ES5) with Babel.
305
- let jsSource = concatFiles ( filenameObj . main ) ;
306
- jsSource = readFileContents ( filenameObj . wrap . intro )
307
- + '\n'
308
- + ( _opt . options . es6 ? jsSource : babelCore . transform ( jsSource , babelOpts ) . code )
309
- + '\n'
310
- + readFileContents ( filenameObj . wrap . outro ) ;
311
-
312
- if ( _opt . options . unminified ) {
313
- return [
314
- 'window.TWINE1=' + String ( ! ! options . twine1 ) ,
315
- 'window.DEBUG=' + String ( _opt . options . debug || false )
316
- ] . join ( ';\n' ) + ';\n' + jsSource ;
292
+ // Join the files.
293
+ let bundle = concatFiles ( filenameObj . files ) ;
294
+
295
+ // Transpile to ES5 with Babel.
296
+ if ( ! _opt . options . noTranspile ) {
297
+ const { transform } = require ( '@babel/core' ) ;
298
+ bundle = transform ( bundle , {
299
+ // babelHelpers : 'bundled',
300
+ code : true ,
301
+ compact : false ,
302
+ presets : [ [ '@babel/preset-env' ] ] ,
303
+ filename : 'sugarcube.bundle.js'
304
+ } ) . code ;
317
305
}
318
306
319
- const uglifyjs = require ( 'uglify-js' ) ;
320
- const minified = uglifyjs . minify ( jsSource , {
321
- compress : {
322
- global_defs : {
323
- TWINE1 : ! ! options . twine1 ,
324
- DEBUG : _opt . options . debug || false
307
+ bundle = `${ readFileContents ( filenameObj . wrap . intro ) } \n${ bundle } \n${ readFileContents ( filenameObj . wrap . outro ) } ` ;
308
+
309
+ return ( async source => {
310
+ if ( _opt . options . unminified ) {
311
+ return [
312
+ `window.TWINE1=${ Boolean ( options . twine1 ) } ` ,
313
+ `window.DEBUG=${ _opt . options . debug || false } ` ,
314
+ source
315
+ ] . join ( ';\n' ) ;
316
+ }
317
+
318
+ // Minify the code with Terser.
319
+ const { minify } = require ( 'terser' ) ;
320
+ const minified = await minify ( source , {
321
+ compress : {
322
+ global_defs : { // eslint-disable-line camelcase
323
+ TWINE1 : ! ! options . twine1 ,
324
+ DEBUG : _opt . options . debug || false
325
+ }
325
326
} ,
326
- keep_infinity : true
327
- } ,
328
- mangle : false
329
- } ) ;
327
+ mangle : false
328
+ } ) ;
330
329
331
- if ( minified . error ) {
332
- const { message, line, col, pos } = minified . error ;
333
- die ( `JavaScript minification error: ${ message } \n[@: ${ line } /${ col } /${ pos } ]` ) ;
334
- }
330
+ if ( minified . error ) {
331
+ const { message, line, col, pos } = minified . error ;
332
+ die ( `JavaScript minification error: ${ message } \n[@: ${ line } /${ col } /${ pos } ]` ) ;
333
+ }
335
334
336
- return minified . code ;
337
- /* eslint-enable camelcase, prefer-template */
335
+ return minified . code ;
336
+ } ) ( bundle ) ;
338
337
}
339
338
340
- function compileStyles ( filenames ) {
341
- /* eslint-disable prefer-template */
339
+ function compileStyles ( config ) {
342
340
log ( 'compiling CSS...' ) ;
343
341
344
- const postcss = require ( 'postcss' ) ;
345
- const CleanCss = require ( 'clean-css' ) ;
346
- const normalizeRegExp = / n o r m a l i z e \. c s s $ / ;
342
+ const autoprefixer = require ( 'autoprefixer' ) ;
343
+ const mixins = require ( 'postcss-mixins' ) ;
344
+ const postcss = require ( 'postcss' ) ;
345
+ const CleanCSS = require ( 'clean-css' ) ;
346
+ const excludeRE = / (?: n o r m a l i z e ) \. c s s $ / ;
347
+ const mixinContent = readFileContents ( config . mixins ) ;
347
348
348
- return concatFiles ( filenames , ( contents , filename ) => {
349
+ return concatFiles ( config . files , ( contents , filename ) => {
349
350
let css = contents ;
350
351
351
- // Don't run autoprefixer on 'normalize.css'.
352
- if ( ! normalizeRegExp . test ( filename ) ) {
353
- const processed = postcss ( [ require ( 'autoprefixer' ) ] ) . process ( css , { from : filename } ) ;
352
+ // Do not run the postcss plugins on files that match the exclusion regexp.
353
+ if ( ! excludeRE . test ( filename ) ) {
354
+ css = `${ mixinContent } \n${ css } ` ;
355
+
356
+ const processed = postcss ( [ mixins , autoprefixer ] ) . process ( css , { from : filename } ) ;
354
357
355
358
css = processed . css ;
356
359
357
360
processed . warnings ( ) . forEach ( mesg => console . warn ( mesg . text ) ) ;
358
361
}
359
362
360
363
if ( ! _opt . options . unminified ) {
361
- css = new CleanCss ( {
362
- level : 1 , // [clean-css v4] `1` is the default, but let's be specific
363
- compatibility : 'ie9' // [clean-css v4] 'ie10' is the default, so restore IE9 support
364
+ css = new CleanCSS ( {
365
+ level : 1 ,
366
+ compatibility : 'ie9'
364
367
} )
365
368
. minify ( css )
366
369
. styles ;
367
370
}
368
371
369
- return '<style id="style-' + _path . basename ( filename , '.css' ) . toLowerCase ( ) . replace ( / [ ^ 0 - 9 a - z ] + / g, '-' )
370
- + '" type="text/css">' + css + '</style>' ;
372
+ const fileSlug = _path . basename ( filename , '.css' ) . toLowerCase ( ) . replace ( / [ ^ 0 - 9 a - z ] + / g, '-' ) ;
373
+
374
+ return `<style id="style-${ fileSlug } " type="text/css">${ css } </style>` ;
371
375
} ) ;
372
- /* eslint-enable prefer-template */
373
376
}
374
377
375
378
function projectBuild ( project ) {
@@ -378,7 +381,8 @@ function projectBuild(project) {
378
381
379
382
log ( `building: "${ outfile } "` ) ;
380
383
381
- let output = readFileContents ( infile ) ; // load the story format template
384
+ // Load the story format template.
385
+ let output = readFileContents ( infile ) ;
382
386
383
387
// Process the source replacement tokens. (First!)
384
388
output = output . replace ( / ( [ ' " ` ] ) \{ \{ B U I L D _ L I B _ S O U R C E \} \} \1/ , ( ) => project . libSource ) ;
0 commit comments