1 | const path = require( 'path' );
|
2 | const chalk = require( 'chalk' );
|
3 |
|
4 |
|
5 | const _tmpCwd = process.cwd();
|
6 | process.chdir( __dirname );
|
7 |
|
8 |
|
9 | const rollup = require( 'rollup' );
|
10 |
|
11 |
|
12 | process.chdir( _tmpCwd );
|
13 |
|
14 |
|
15 | const rollupNodeResolve = require( 'rollup-plugin-node-resolve' );
|
16 | const rollupCommonJS = require( 'rollup-plugin-commonjs' );
|
17 | const babel = require( 'rollup-plugin-babel' );
|
18 | const Observable = require( 'zen-observable' );
|
19 | const minify = require('./rollup-minify');
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | const currentLocalTime = function() {
|
27 | return ( new Date() ).toLocaleTimeString();
|
28 | };
|
29 |
|
30 | const logPrefix = function() {
|
31 | return chalk`{gray ${ currentLocalTime() }} {bold [JS]}`;
|
32 | };
|
33 |
|
34 | const 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 |
|
46 | const validate = function( {
|
47 | source,
|
48 | destination,
|
49 | globals,
|
50 | external,
|
51 | mode = 'build',
|
52 | includeBabelPolyfill = true,
|
53 | } ) {
|
54 | if ( mode !== 'watch' && mode !== 'build' ) {
|
55 | mode = 'build';
|
56 | }
|
57 |
|
58 | if ( includeBabelPolyfill === false || includeBabelPolyfill === 'false' ) {
|
59 | includeBabelPolyfill = false;
|
60 | } else {
|
61 | includeBabelPolyfill = true;
|
62 | }
|
63 |
|
64 | return new Promise( function( resolve, reject ) {
|
65 | if ( ! source ) {
|
66 | return reject( chalk`${ logPrefix() } {red Source is required!}` );
|
67 | }
|
68 |
|
69 | if ( ! destination ) {
|
70 | return reject( chalk`${ logPrefix() } {red Destination is required!}` );
|
71 | }
|
72 |
|
73 | return resolve();
|
74 | } )
|
75 | .then( () => ( {
|
76 | source,
|
77 | destination,
|
78 | globals,
|
79 | external,
|
80 | mode,
|
81 | includeBabelPolyfill,
|
82 | } ) );
|
83 | };
|
84 |
|
85 | const rollupPluginPrependBabelPolyfill = function(source) {
|
86 | return {
|
87 | name: 'prepend-babel-polyfill',
|
88 | transform(code, id) {
|
89 | if (!String.prototype.endsWith.call(id, source)) {
|
90 | return;
|
91 | }
|
92 |
|
93 | return {
|
94 | code: 'import \'@runforest/js/babel-polyfill.js\';\n' + code,
|
95 | };
|
96 | },
|
97 | };
|
98 | };
|
99 |
|
100 | const getPluginsOption = function( { source, includeBabelPolyfill } ) {
|
101 | const plugins = [];
|
102 |
|
103 |
|
104 | plugins.push(rollupNodeResolve({
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | browser: true,
|
110 |
|
111 | customResolveOptions: {
|
112 |
|
113 | moduleDirectory: 'node_modules',
|
114 | },
|
115 | }));
|
116 |
|
117 |
|
118 | plugins.push(rollupCommonJS({
|
119 |
|
120 | include: 'node_modules/**',
|
121 | }));
|
122 |
|
123 | if (includeBabelPolyfill) {
|
124 |
|
125 | plugins.push(rollupPluginPrependBabelPolyfill(source));
|
126 | }
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | plugins.push(babel({
|
132 |
|
133 | exclude: 'node_modules/**',
|
134 | presets: [[
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | require( '@babel/preset-env' ),
|
140 | {
|
141 |
|
142 |
|
143 |
|
144 | configPath: source,
|
145 |
|
146 |
|
147 | modules: false,
|
148 |
|
149 | useBuiltIns: 'entry',
|
150 | },
|
151 | ]],
|
152 | }));
|
153 |
|
154 |
|
155 | plugins.push(minify());
|
156 |
|
157 | return plugins;
|
158 | };
|
159 |
|
160 | const getInputOptions = function( config ) {
|
161 | const { source, external } = config;
|
162 |
|
163 | return {
|
164 | input: source,
|
165 | external: external,
|
166 | plugins: getPluginsOption( config ),
|
167 | };
|
168 | };
|
169 |
|
170 | const getOutputOptions = function( source, destination, globals ) {
|
171 | return {
|
172 | format: 'iife',
|
173 | file: path.resolve( destination, path.basename( source ) ),
|
174 | globals: globals,
|
175 | sourcemap: true,
|
176 | };
|
177 | };
|
178 |
|
179 | module.exports = function( config ) {
|
180 | return new Observable( function( observer ) {
|
181 | validate( config )
|
182 | .then( function( config ) {
|
183 | return new Promise( function( resolve, reject ) {
|
184 | const { mode } = config;
|
185 |
|
186 | if ( 'watch' !== mode ) {
|
187 | return resolve( config );
|
188 | }
|
189 |
|
190 | const { source, destination, globals } = config;
|
191 |
|
192 | const watcher = rollup.watch( Object.assign(
|
193 | getInputOptions( config ),
|
194 | {
|
195 | output: getOutputOptions( source, destination, globals ),
|
196 | watch: {
|
197 | chokidar: true,
|
198 | exclude: 'node_modules/**',
|
199 | },
|
200 | }
|
201 | ) );
|
202 |
|
203 | let waitingMessageTimeout;
|
204 |
|
205 | watcher.on( 'event', function( event ) {
|
206 | clearTimeout( waitingMessageTimeout );
|
207 |
|
208 | switch ( event.code ) {
|
209 | case 'START':
|
210 | observer.next( 'Start compiling...' );
|
211 |
|
212 | break;
|
213 |
|
214 | case 'BUNDLE_START':
|
215 | observer.next(
|
216 | chalk`Compiling {blue ${ event.input }}...`
|
217 | );
|
218 | break;
|
219 |
|
220 | case 'BUNDLE_END':
|
221 | const destinations = event.output
|
222 | .map( ( dest ) => path.relative( process.cwd(), dest ) )
|
223 | .join( ', ' );
|
224 |
|
225 | observer.next(
|
226 | chalk`{blue ${ event.input }} is compiled to ` +
|
227 | chalk`{green ${ destinations }}.`
|
228 | );
|
229 | break;
|
230 |
|
231 | case 'END':
|
232 | waitingMessageTimeout = setTimeout( () => {
|
233 | observer.next( 'Waiting...' );
|
234 | }, 2000 );
|
235 | break;
|
236 |
|
237 | case 'ERROR':
|
238 | if ( event.error ) {
|
239 | observer.next( formatError( event.error ) );
|
240 | } else {
|
241 | observer.next(
|
242 | chalk`{red Unknown error!}\n` +
|
243 | formatError( event )
|
244 | );
|
245 | }
|
246 | break;
|
247 |
|
248 | default:
|
249 | watcher.close();
|
250 | if ( event.error ) {
|
251 | reject( event.error );
|
252 | } else {
|
253 | reject(
|
254 | chalk`{red Unknown unrecoverable error!}\n` +
|
255 | formatError( event )
|
256 | );
|
257 | }
|
258 |
|
259 | break;
|
260 | }
|
261 | } );
|
262 | } );
|
263 | } )
|
264 | .then( function( config ) {
|
265 | const { mode } = config;
|
266 |
|
267 | if ( 'build' !== mode ) {
|
268 | return Promise.resolve( config );
|
269 | }
|
270 |
|
271 | const { source, destination, globals } = config;
|
272 |
|
273 | return rollup.rollup( getInputOptions( config ) )
|
274 | .then(
|
275 | ( bundle ) =>
|
276 | bundle.write( getOutputOptions( source, destination, globals ) )
|
277 | );
|
278 | } )
|
279 | .then( function() {
|
280 | observer.complete();
|
281 | } )
|
282 | .catch( function( error ) {
|
283 | observer.error( error );
|
284 | } );
|
285 | } );
|
286 | };
|