UNPKG

6.54 kBJavaScriptView Raw
1const path = require( 'path' );
2const chalk = require( 'chalk' );
3
4// Fix rollup watcher chokidar path issue.
5const _tmpCwd = process.cwd();
6process.chdir( __dirname );
7
8// Require rollup.
9const rollup = require( 'rollup' );
10
11// Revert rollup watcher chokidar path issue fix.
12process.chdir( _tmpCwd );
13
14// Rollup dependencies.
15const rollupNodeResolve = require( 'rollup-plugin-node-resolve' );
16const rollupCommonJS = require( 'rollup-plugin-commonjs' );
17const babel = require( 'rollup-plugin-babel' );
18const Observable = require( 'zen-observable' );
19const minify = require('./rollup-minify');
20
21/**
22 * Returns current local time string.
23 *
24 * @returns {String}
25 */
26const currentLocalTime = function() {
27 return ( new Date() ).toLocaleTimeString();
28};
29
30const logPrefix = function() {
31 return chalk`{gray ${ currentLocalTime() }} {bold [JS]}`;
32};
33
34const formatError = function( error ) {
35 if ( error.formatted ) {
36 return chalk`{red ${error.formatted}}`;
37 }
38
39 if ( error.message ) {
40 return chalk`{red ${error.message}}`;
41 }
42
43 return error.toString();
44};
45
46const validate = function( { source, destination, globals, external, mode = 'build' } ) {
47 if ( mode !== 'watch' && mode !== 'build' ) {
48 mode = 'build';
49 }
50
51 return new Promise( function( resolve, reject ) {
52 if ( ! source ) {
53 return reject( chalk`${ logPrefix() } {red Source is required!}` );
54 }
55
56 if ( ! destination ) {
57 return reject( chalk`${ logPrefix() } {red Destination is required!}` );
58 }
59
60 return resolve();
61 } )
62 .then( () => ( {
63 source,
64 destination,
65 globals,
66 external,
67 mode,
68 } ) );
69};
70
71const rollupPluginPrependBabelPolyfill = function(source) {
72 return {
73 name: 'prepend-babel-polyfill',
74 transform(code, id) {
75 if (!String.prototype.endsWith.call(id, source)) {
76 return;
77 }
78
79 return {
80 code: 'import \'@runforest/js/babel-polyfill.js\';\n' + code,
81 };
82 },
83 };
84};
85
86const getPluginsOption = function( source ) {
87 return [
88 // Use the Node.js resolution algorithm with Rollup
89 rollupNodeResolve( {
90 // some package.json files have a `browser` field which
91 // specifies alternative files to load for people bundling
92 // for the browser. If that's you, use this option, otherwise
93 // pkg.browser will be ignored
94 browser: true,
95 // Additional options
96 customResolveOptions: {
97 // Set node_modules as module directory
98 moduleDirectory: 'node_modules',
99 },
100 } ),
101 // Convert CommonJS modules to ES2015
102 rollupCommonJS( {
103 // Convert only imported npm modules
104 include: 'node_modules/**',
105 } ),
106 // Prepends import '@babel/polyfill';
107 rollupPluginPrependBabelPolyfill(source),
108 // Babel is a toolchain that is mainly used to convert ECMAScript 2015+
109 // code into a backwards compatible version of JavaScript in current
110 // and older browsers or environments
111 babel( {
112 // Do not transform npm modules
113 exclude: 'node_modules/**',
114 presets: [ [
115 // @babel/preset-env is a smart preset that allows you to use
116 // the latest JavaScript without needing to micromanage which
117 // syntax transforms (and optionally, browser polyfills) are
118 // needed by your target environment(s).
119 require( '@babel/preset-env' ),
120 {
121 // The starting point where the config search for
122 // browserslist will start, and ascend to the system root
123 // until found
124 configPath: source,
125 // Disable transformation of ES6 module syntax because that
126 // is what rollup do
127 modules: false,
128 // @babel/polyfill insertion is handled by the plugin above
129 useBuiltIns: 'entry',
130 },
131 ] ]
132 } ),
133 // ECMAScript 2015+ minifier
134 minify(),
135 ];
136};
137
138const getInputOptions = function( source, external ) {
139 return {
140 input: source,
141 external: external,
142 plugins: getPluginsOption( source ),
143 };
144};
145
146const getOutputOptions = function( source, destination, globals ) {
147 return {
148 format: 'iife',
149 file: path.resolve( destination, path.basename( source ) ),
150 globals: globals,
151 sourcemap: true,
152 };
153};
154
155module.exports = function( config ) {
156 return new Observable( function( observer ) {
157 validate( config )
158 .then( function( config ) {
159 return new Promise( function( resolve, reject ) {
160 const { mode } = config;
161
162 if ( 'watch' !== mode ) {
163 return resolve( config );
164 }
165
166 const { source, destination, globals, external } = config;
167
168 const watcher = rollup.watch( Object.assign(
169 getInputOptions( source, external ),
170 {
171 output: getOutputOptions( source, destination, globals ),
172 watch: {
173 chokidar: true,
174 exclude: 'node_modules/**',
175 },
176 }
177 ) );
178
179 let waitingMessageTimeout;
180
181 watcher.on( 'event', function( event ) {
182 clearTimeout( waitingMessageTimeout );
183
184 switch ( event.code ) {
185 case 'START':
186 observer.next( 'Start compiling...' );
187
188 break;
189
190 case 'BUNDLE_START':
191 observer.next(
192 chalk`Compiling {blue ${ event.input }}...`
193 );
194 break;
195
196 case 'BUNDLE_END':
197 const destinations = event.output
198 .map( ( dest ) => path.relative( process.cwd(), dest ) )
199 .join( ', ' );
200
201 observer.next(
202 chalk`{blue ${ event.input }} is compiled to ` +
203 chalk`{green ${ destinations }}.`
204 );
205 break;
206
207 case 'END':
208 waitingMessageTimeout = setTimeout( () => {
209 observer.next( 'Waiting...' );
210 }, 2000 );
211 break;
212
213 case 'ERROR':
214 if ( event.error ) {
215 observer.next( formatError( event.error ) );
216 } else {
217 observer.next(
218 chalk`{red Unknown error!}\n` +
219 formatError( event )
220 );
221 }
222 break;
223
224 default:
225 watcher.close();
226 if ( event.error ) {
227 reject( event.error );
228 } else {
229 reject(
230 chalk`{red Unknown unrecoverable error!}\n` +
231 formatError( event )
232 );
233 }
234
235 break;
236 }
237 } );
238 } );
239 } )
240 .then( function( config ) {
241 const { mode } = config;
242
243 if ( 'build' !== mode ) {
244 return Promise.resolve( config );
245 }
246
247 const { source, destination, globals, external } = config;
248
249 return rollup.rollup( getInputOptions( source, external ) )
250 .then(
251 ( bundle ) =>
252 bundle.write( getOutputOptions( source, destination, globals ) )
253 );
254 } )
255 .then( function() {
256 observer.complete();
257 } )
258 .catch( function( error ) {
259 observer.error( error );
260 } );
261 } );
262};