1 | const fs = require('@parcel/fs');
|
2 | const Resolver = require('./Resolver');
|
3 | const Parser = require('./Parser');
|
4 | const WorkerFarm = require('@parcel/workers');
|
5 | const Path = require('path');
|
6 | const Bundle = require('./Bundle');
|
7 | const Watcher = require('@parcel/watcher');
|
8 | const FSCache = require('./FSCache');
|
9 | const HMRServer = require('./HMRServer');
|
10 | const Server = require('./Server');
|
11 | const {EventEmitter} = require('events');
|
12 | const logger = require('@parcel/logger');
|
13 | const PackagerRegistry = require('./packagers');
|
14 | const localRequire = require('./utils/localRequire');
|
15 | const config = require('./utils/config');
|
16 | const loadEnv = require('./utils/env');
|
17 | const PromiseQueue = require('./utils/PromiseQueue');
|
18 | const installPackage = require('./utils/installPackage');
|
19 | const bundleReport = require('./utils/bundleReport');
|
20 | const prettifyTime = require('./utils/prettifyTime');
|
21 | const getRootDir = require('./utils/getRootDir');
|
22 | const {glob, isGlob} = require('./utils/glob');
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | class Bundler extends EventEmitter {
|
29 | constructor(entryFiles, options = {}) {
|
30 | super();
|
31 |
|
32 | entryFiles = this.normalizeEntries(entryFiles);
|
33 | this.watchedGlobs = entryFiles.filter(entry => isGlob(entry));
|
34 | this.entryFiles = this.findEntryFiles(entryFiles);
|
35 | this.options = this.normalizeOptions(options);
|
36 |
|
37 | this.resolver = new Resolver(this.options);
|
38 | this.parser = new Parser(this.options);
|
39 | this.packagers = new PackagerRegistry(this.options);
|
40 | this.cache = this.options.cache ? new FSCache(this.options) : null;
|
41 | this.delegate = options.delegate || {};
|
42 | this.bundleLoaders = {};
|
43 |
|
44 | this.addBundleLoader('wasm', {
|
45 | browser: require.resolve('./builtins/loaders/browser/wasm-loader'),
|
46 | node: require.resolve('./builtins/loaders/node/wasm-loader')
|
47 | });
|
48 | this.addBundleLoader('css', {
|
49 | browser: require.resolve('./builtins/loaders/browser/css-loader'),
|
50 | node: require.resolve('./builtins/loaders/node/css-loader')
|
51 | });
|
52 | this.addBundleLoader('js', {
|
53 | browser: require.resolve('./builtins/loaders/browser/js-loader'),
|
54 | node: require.resolve('./builtins/loaders/node/js-loader')
|
55 | });
|
56 | this.addBundleLoader('html', {
|
57 | browser: require.resolve('./builtins/loaders/browser/html-loader'),
|
58 | node: require.resolve('./builtins/loaders/node/html-loader')
|
59 | });
|
60 |
|
61 | this.pending = false;
|
62 | this.loadedAssets = new Map();
|
63 | this.watchedAssets = new Map();
|
64 |
|
65 | this.farm = null;
|
66 | this.watcher = null;
|
67 | this.hmr = null;
|
68 | this.bundleHashes = null;
|
69 | this.error = null;
|
70 | this.buildQueue = new PromiseQueue(this.processAsset.bind(this));
|
71 | this.rebuildTimeout = null;
|
72 |
|
73 | logger.setOptions(this.options);
|
74 | }
|
75 |
|
76 | normalizeEntries(entryFiles) {
|
77 |
|
78 | if (entryFiles && !Array.isArray(entryFiles)) {
|
79 | entryFiles = [entryFiles];
|
80 | }
|
81 |
|
82 |
|
83 | if (!entryFiles || entryFiles.length === 0) {
|
84 | entryFiles = [process.cwd()];
|
85 | }
|
86 |
|
87 | return entryFiles;
|
88 | }
|
89 |
|
90 | findEntryFiles(entryFiles) {
|
91 |
|
92 | return entryFiles
|
93 | .reduce((p, m) => p.concat(glob.sync(m)), [])
|
94 | .map(f => Path.resolve(f));
|
95 | }
|
96 |
|
97 | normalizeOptions(options) {
|
98 | const isProduction =
|
99 | options.production || process.env.NODE_ENV === 'production';
|
100 | const publicURL = options.publicUrl || options.publicURL || '/';
|
101 | const watch =
|
102 | typeof options.watch === 'boolean' ? options.watch : !isProduction;
|
103 | const target = options.target || 'browser';
|
104 | const hmr =
|
105 | target === 'node'
|
106 | ? false
|
107 | : typeof options.hmr === 'boolean'
|
108 | ? options.hmr
|
109 | : watch;
|
110 | const scopeHoist =
|
111 | options.scopeHoist !== undefined ? options.scopeHoist : false;
|
112 | return {
|
113 | production: isProduction,
|
114 | outDir: Path.resolve(options.outDir || 'dist'),
|
115 | outFile: options.outFile || '',
|
116 | publicURL: publicURL,
|
117 | watch: watch,
|
118 | cache: typeof options.cache === 'boolean' ? options.cache : true,
|
119 | cacheDir: Path.resolve(options.cacheDir || '.cache'),
|
120 | killWorkers:
|
121 | typeof options.killWorkers === 'boolean' ? options.killWorkers : true,
|
122 | minify:
|
123 | typeof options.minify === 'boolean' ? options.minify : isProduction,
|
124 | target: target,
|
125 | bundleNodeModules:
|
126 | typeof options.bundleNodeModules === 'boolean'
|
127 | ? options.bundleNodeModules
|
128 | : target === 'browser',
|
129 | hmr: hmr,
|
130 | https: options.https || false,
|
131 | logLevel: isNaN(options.logLevel) ? 3 : options.logLevel,
|
132 | entryFiles: this.entryFiles,
|
133 | hmrPort: options.hmrPort || 0,
|
134 | rootDir: getRootDir(this.entryFiles),
|
135 | sourceMaps:
|
136 | (typeof options.sourceMaps === 'boolean' ? options.sourceMaps : true) &&
|
137 | !scopeHoist,
|
138 | hmrHostname:
|
139 | options.hmrHostname ||
|
140 | options.host ||
|
141 | (options.target === 'electron' ? 'localhost' : ''),
|
142 | detailedReport: options.detailedReport || false,
|
143 | global: options.global,
|
144 | autoinstall:
|
145 | typeof options.autoInstall === 'boolean'
|
146 | ? options.autoInstall
|
147 | : process.env.PARCEL_AUTOINSTALL === 'false'
|
148 | ? false
|
149 | : !isProduction,
|
150 | scopeHoist: scopeHoist,
|
151 | contentHash:
|
152 | typeof options.contentHash === 'boolean'
|
153 | ? options.contentHash
|
154 | : isProduction,
|
155 | throwErrors:
|
156 | typeof options.throwErrors === 'boolean' ? options.throwErrors : true
|
157 | };
|
158 | }
|
159 |
|
160 | addAssetType(extension, path) {
|
161 | if (typeof path !== 'string') {
|
162 | throw new Error('Asset type should be a module path.');
|
163 | }
|
164 |
|
165 | if (this.farm) {
|
166 | throw new Error('Asset types must be added before bundling.');
|
167 | }
|
168 |
|
169 | this.parser.registerExtension(extension, path);
|
170 | }
|
171 |
|
172 | addPackager(type, packager) {
|
173 | if (this.farm) {
|
174 | throw new Error('Packagers must be added before bundling.');
|
175 | }
|
176 |
|
177 | this.packagers.add(type, packager);
|
178 | }
|
179 |
|
180 | addBundleLoader(type, paths) {
|
181 | if (typeof paths === 'string') {
|
182 | paths = {node: paths, browser: paths};
|
183 | } else if (typeof paths !== 'object') {
|
184 | throw new Error('Bundle loaders should be an object.');
|
185 | }
|
186 |
|
187 | for (const target in paths) {
|
188 | if (target !== 'node' && target !== 'browser') {
|
189 | throw new Error(`Unknown bundle loader target "${target}".`);
|
190 | }
|
191 |
|
192 | if (typeof paths[target] !== 'string') {
|
193 | throw new Error('Bundle loader should be a string.');
|
194 | }
|
195 | }
|
196 |
|
197 | if (this.farm) {
|
198 | throw new Error('Bundle loaders must be added before bundling.');
|
199 | }
|
200 |
|
201 | this.bundleLoaders[type] = paths;
|
202 | }
|
203 |
|
204 | async loadPlugins() {
|
205 | let relative = Path.join(this.options.rootDir, 'index');
|
206 | let pkg = await config.load(relative, ['package.json']);
|
207 | if (!pkg) {
|
208 | return;
|
209 | }
|
210 |
|
211 | let lastDep;
|
212 | try {
|
213 | let deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
|
214 | for (let dep in deps) {
|
215 | lastDep = dep;
|
216 | const pattern = /^(@.*\/)?parcel-plugin-.+/;
|
217 | if (pattern.test(dep)) {
|
218 | let plugin = await localRequire(dep, relative);
|
219 | await plugin(this);
|
220 | }
|
221 | }
|
222 | } catch (err) {
|
223 | logger.warn(
|
224 | `Plugin ${lastDep} failed to initialize: ${err.stack ||
|
225 | err.message ||
|
226 | err}`
|
227 | );
|
228 | }
|
229 | }
|
230 |
|
231 | async bundle() {
|
232 |
|
233 | if (this.pending) {
|
234 | return new Promise((resolve, reject) => {
|
235 | this.once('buildEnd', () => {
|
236 | this.bundle().then(resolve, reject);
|
237 | });
|
238 | });
|
239 | }
|
240 |
|
241 | let isInitialBundle = !this.entryAssets;
|
242 | let startTime = Date.now();
|
243 | let initialised = !isInitialBundle;
|
244 | this.pending = true;
|
245 | this.error = null;
|
246 |
|
247 | logger.clear();
|
248 | logger.progress('Building...');
|
249 |
|
250 | try {
|
251 |
|
252 | await this.start();
|
253 |
|
254 |
|
255 | this.emit('buildStart', this.entryFiles);
|
256 |
|
257 |
|
258 | if (isInitialBundle) {
|
259 | await fs.mkdirp(this.options.outDir);
|
260 |
|
261 | this.entryAssets = new Set();
|
262 | for (let entry of this.entryFiles) {
|
263 | try {
|
264 | let asset = await this.resolveAsset(entry);
|
265 | this.buildQueue.add(asset);
|
266 | this.entryAssets.add(asset);
|
267 | } catch (err) {
|
268 | throw new Error(
|
269 | `Cannot resolve entry "${entry}" from "${this.options.rootDir}"`
|
270 | );
|
271 | }
|
272 | }
|
273 |
|
274 | if (this.entryAssets.size === 0) {
|
275 | throw new Error('No entries found.');
|
276 | }
|
277 |
|
278 | initialised = true;
|
279 | }
|
280 |
|
281 |
|
282 | let loadedAssets = await this.buildQueue.run();
|
283 |
|
284 |
|
285 |
|
286 | let changedAssets = [...this.findOrphanAssets(), ...loadedAssets];
|
287 |
|
288 |
|
289 | for (let asset of this.loadedAssets.values()) {
|
290 | asset.invalidateBundle();
|
291 | }
|
292 |
|
293 | logger.progress(`Producing bundles...`);
|
294 |
|
295 |
|
296 | this.mainBundle = new Bundle();
|
297 | for (let asset of this.entryAssets) {
|
298 | this.createBundleTree(asset, this.mainBundle);
|
299 | }
|
300 |
|
301 |
|
302 | if (this.mainBundle.childBundles.size === 1) {
|
303 | this.mainBundle = Array.from(this.mainBundle.childBundles)[0];
|
304 | }
|
305 |
|
306 |
|
307 | let numBundles = this.bundleNameMap ? this.bundleNameMap.size : 0;
|
308 | this.bundleNameMap = this.mainBundle.getBundleNameMap(
|
309 | this.options.contentHash
|
310 | );
|
311 |
|
312 | for (let asset of changedAssets) {
|
313 | asset.replaceBundleNames(this.bundleNameMap);
|
314 | }
|
315 |
|
316 |
|
317 | let bundlesChanged = numBundles !== this.bundleNameMap.size;
|
318 | if (this.hmr && !isInitialBundle) {
|
319 | this.hmr.emitUpdate(changedAssets, bundlesChanged);
|
320 | }
|
321 |
|
322 | logger.progress(`Packaging...`);
|
323 |
|
324 |
|
325 | this.bundleHashes = await this.mainBundle.package(
|
326 | this,
|
327 | bundlesChanged ? null : this.bundleHashes
|
328 | );
|
329 |
|
330 |
|
331 | this.unloadOrphanedAssets();
|
332 |
|
333 | let buildTime = Date.now() - startTime;
|
334 | let time = prettifyTime(buildTime);
|
335 | logger.success(`Built in ${time}.`);
|
336 | if (!this.watcher) {
|
337 | bundleReport(this.mainBundle, this.options.detailedReport);
|
338 | }
|
339 |
|
340 | this.emit('bundled', this.mainBundle);
|
341 | return this.mainBundle;
|
342 | } catch (err) {
|
343 | this.error = err;
|
344 |
|
345 | logger.error(err);
|
346 |
|
347 | this.emit('buildError', err);
|
348 |
|
349 | if (this.hmr) {
|
350 | this.hmr.emitError(err);
|
351 | }
|
352 |
|
353 | if (this.options.throwErrors && !this.hmr) {
|
354 | throw err;
|
355 | } else if (!this.options.watch || !initialised) {
|
356 | await this.stop();
|
357 | process.exit(1);
|
358 | }
|
359 | } finally {
|
360 | this.pending = false;
|
361 | this.emit('buildEnd');
|
362 |
|
363 |
|
364 | if (!this.watcher && this.options.killWorkers) {
|
365 | await this.stop();
|
366 | }
|
367 | }
|
368 | }
|
369 |
|
370 | async start() {
|
371 | if (this.farm) {
|
372 | return;
|
373 | }
|
374 |
|
375 | await this.loadPlugins();
|
376 |
|
377 | if (!this.options.env) {
|
378 | await loadEnv(Path.join(this.options.rootDir, 'index'));
|
379 | this.options.env = process.env;
|
380 | }
|
381 |
|
382 | this.options.extensions = Object.assign({}, this.parser.extensions);
|
383 | this.options.bundleLoaders = this.bundleLoaders;
|
384 |
|
385 | if (this.options.watch) {
|
386 | this.watcher = new Watcher();
|
387 |
|
388 | if (process.env.NODE_ENV === 'test' && !this.watcher.ready) {
|
389 | await new Promise(resolve => this.watcher.once('ready', resolve));
|
390 | }
|
391 | this.watchedGlobs.forEach(glob => {
|
392 | this.watcher.add(glob);
|
393 | });
|
394 | this.watcher.on('add', this.onAdd.bind(this));
|
395 | this.watcher.on('change', this.onChange.bind(this));
|
396 | this.watcher.on('unlink', this.onUnlink.bind(this));
|
397 | }
|
398 |
|
399 | if (this.options.hmr) {
|
400 | this.hmr = new HMRServer();
|
401 | this.options.hmrPort = await this.hmr.start(this.options);
|
402 | }
|
403 |
|
404 | this.farm = await WorkerFarm.getShared(this.options, {
|
405 | workerPath: require.resolve('./worker.js')
|
406 | });
|
407 | }
|
408 |
|
409 | async stop() {
|
410 | if (this.watcher) {
|
411 | await this.watcher.stop();
|
412 | }
|
413 |
|
414 | if (this.hmr) {
|
415 | this.hmr.stop();
|
416 | }
|
417 |
|
418 |
|
419 |
|
420 | if (this.farm) {
|
421 | await this.farm.end();
|
422 | }
|
423 | }
|
424 |
|
425 | async getAsset(name, parent) {
|
426 | let asset = await this.resolveAsset(name, parent);
|
427 | this.buildQueue.add(asset);
|
428 | await this.buildQueue.run();
|
429 | return asset;
|
430 | }
|
431 |
|
432 | async resolveAsset(name, parent) {
|
433 | let {path} = await this.resolver.resolve(name, parent);
|
434 | return this.getLoadedAsset(path);
|
435 | }
|
436 |
|
437 | getLoadedAsset(path) {
|
438 | if (this.loadedAssets.has(path)) {
|
439 | return this.loadedAssets.get(path);
|
440 | }
|
441 |
|
442 | let asset = this.parser.getAsset(path, this.options);
|
443 | this.loadedAssets.set(path, asset);
|
444 |
|
445 | this.watch(path, asset);
|
446 | return asset;
|
447 | }
|
448 |
|
449 | async watch(path, asset) {
|
450 | if (!this.watcher) {
|
451 | return;
|
452 | }
|
453 |
|
454 | path = await fs.realpath(path);
|
455 |
|
456 | if (!this.watchedAssets.has(path)) {
|
457 | this.watcher.watch(path);
|
458 | this.watchedAssets.set(path, new Set());
|
459 | }
|
460 | this.watchedAssets.get(path).add(asset);
|
461 | }
|
462 |
|
463 | async unwatch(path, asset) {
|
464 | path = await fs.realpath(path);
|
465 | if (!this.watchedAssets.has(path)) {
|
466 | return;
|
467 | }
|
468 |
|
469 | let watched = this.watchedAssets.get(path);
|
470 | watched.delete(asset);
|
471 |
|
472 | if (watched.size === 0) {
|
473 | this.watchedAssets.delete(path);
|
474 | this.watcher.unwatch(path);
|
475 | }
|
476 | }
|
477 |
|
478 | async resolveDep(asset, dep, install = true) {
|
479 | try {
|
480 | if (dep.resolved) {
|
481 | return this.getLoadedAsset(dep.resolved);
|
482 | }
|
483 |
|
484 | return await this.resolveAsset(dep.name, asset.name);
|
485 | } catch (err) {
|
486 |
|
487 | if (dep.optional) {
|
488 | return;
|
489 | }
|
490 |
|
491 | if (err.code === 'MODULE_NOT_FOUND') {
|
492 | let isLocalFile = /^[/~.]/.test(dep.name);
|
493 | let fromNodeModules = asset.name.includes(
|
494 | `${Path.sep}node_modules${Path.sep}`
|
495 | );
|
496 |
|
497 | if (
|
498 | !isLocalFile &&
|
499 | !fromNodeModules &&
|
500 | this.options.autoinstall &&
|
501 | install
|
502 | ) {
|
503 | return this.installDep(asset, dep);
|
504 | }
|
505 |
|
506 | err.message = `Cannot resolve dependency '${dep.name}'`;
|
507 | if (isLocalFile) {
|
508 | const absPath = Path.resolve(Path.dirname(asset.name), dep.name);
|
509 | err.message += ` at '${absPath}'`;
|
510 | }
|
511 |
|
512 | await this.throwDepError(asset, dep, err);
|
513 | }
|
514 |
|
515 | throw err;
|
516 | }
|
517 | }
|
518 |
|
519 | async installDep(asset, dep) {
|
520 |
|
521 | let resolved = await this.resolver.resolveModule(dep.name, asset.name);
|
522 |
|
523 |
|
524 | if (resolved.moduleName && !resolved.moduleDir) {
|
525 | try {
|
526 | await installPackage(resolved.moduleName, asset.name, {
|
527 | saveDev: false
|
528 | });
|
529 | } catch (err) {
|
530 | await this.throwDepError(asset, dep, err);
|
531 | }
|
532 | }
|
533 |
|
534 | return this.resolveDep(asset, dep, false);
|
535 | }
|
536 |
|
537 | async throwDepError(asset, dep, err) {
|
538 |
|
539 | if (dep.loc) {
|
540 | await asset.loadIfNeeded();
|
541 | err.loc = dep.loc;
|
542 | err = asset.generateErrorMessage(err);
|
543 | }
|
544 |
|
545 | err.fileName = asset.name;
|
546 | throw err;
|
547 | }
|
548 |
|
549 | async processAsset(asset, isRebuild) {
|
550 | if (isRebuild) {
|
551 | asset.invalidate();
|
552 | if (this.cache) {
|
553 | this.cache.invalidate(asset.name);
|
554 | }
|
555 | }
|
556 |
|
557 | await this.loadAsset(asset);
|
558 | }
|
559 |
|
560 | async loadAsset(asset) {
|
561 | if (asset.processed) {
|
562 | return;
|
563 | }
|
564 |
|
565 | if (!this.error) {
|
566 | logger.progress(`Building ${asset.basename}...`);
|
567 | }
|
568 |
|
569 |
|
570 | asset.processed = true;
|
571 |
|
572 |
|
573 | asset.startTime = Date.now();
|
574 | let processed = this.cache && (await this.cache.read(asset.name));
|
575 | let cacheMiss = false;
|
576 | if (!processed || asset.shouldInvalidate(processed.cacheData)) {
|
577 | processed = await this.farm.run(asset.name);
|
578 | cacheMiss = true;
|
579 | }
|
580 |
|
581 | asset.endTime = Date.now();
|
582 | asset.buildTime = asset.endTime - asset.startTime;
|
583 | asset.id = processed.id;
|
584 | asset.generated = processed.generated;
|
585 | asset.sourceMaps = processed.sourceMaps;
|
586 | asset.hash = processed.hash;
|
587 | asset.cacheData = processed.cacheData;
|
588 |
|
589 |
|
590 | let dependencies = processed.dependencies;
|
591 | if (this.delegate.getImplicitDependencies) {
|
592 | let implicitDeps = await this.delegate.getImplicitDependencies(asset);
|
593 | if (implicitDeps) {
|
594 | dependencies = dependencies.concat(implicitDeps);
|
595 | }
|
596 | }
|
597 |
|
598 |
|
599 | let assetDeps = await Promise.all(
|
600 | dependencies.map(async dep => {
|
601 | if (dep.includedInParent) {
|
602 |
|
603 |
|
604 |
|
605 | this.watch(dep.name, asset);
|
606 | } else {
|
607 | dep.parent = asset.name;
|
608 | let assetDep = await this.resolveDep(asset, dep);
|
609 | if (assetDep) {
|
610 | await this.loadAsset(assetDep);
|
611 | }
|
612 |
|
613 | return assetDep;
|
614 | }
|
615 | })
|
616 | );
|
617 |
|
618 |
|
619 |
|
620 |
|
621 | if (processed.error !== null) {
|
622 | throw processed.error;
|
623 | }
|
624 |
|
625 |
|
626 | dependencies.forEach((dep, i) => {
|
627 | asset.dependencies.set(dep.name, dep);
|
628 | let assetDep = assetDeps[i];
|
629 | if (assetDep) {
|
630 | asset.depAssets.set(dep, assetDep);
|
631 | dep.resolved = assetDep.name;
|
632 | }
|
633 | });
|
634 |
|
635 | logger.verbose(`Built ${asset.relativeName}...`);
|
636 |
|
637 | if (this.cache && cacheMiss) {
|
638 | this.cache.write(asset.name, processed);
|
639 | }
|
640 | }
|
641 |
|
642 | createBundleTree(asset, bundle, dep, parentBundles = new Set()) {
|
643 | if (dep) {
|
644 | asset.parentDeps.add(dep);
|
645 | }
|
646 |
|
647 | if (asset.parentBundle && !bundle.isolated) {
|
648 |
|
649 | if (asset.parentBundle !== bundle) {
|
650 | let commonBundle = bundle.findCommonAncestor(asset.parentBundle);
|
651 |
|
652 |
|
653 |
|
654 | if (asset.parentBundle.type === commonBundle.type) {
|
655 | this.moveAssetToBundle(asset, commonBundle);
|
656 | return;
|
657 | }
|
658 | } else {
|
659 | return;
|
660 | }
|
661 |
|
662 |
|
663 | if (parentBundles.has(asset.parentBundle)) {
|
664 | return;
|
665 | }
|
666 | }
|
667 |
|
668 |
|
669 |
|
670 | if (bundle.isolated && bundle.assets.has(asset)) {
|
671 | return;
|
672 | }
|
673 |
|
674 | let isEntryAsset =
|
675 | asset.parentBundle && asset.parentBundle.entryAsset === asset;
|
676 |
|
677 |
|
678 |
|
679 | if (bundle.type && asset.generated[bundle.type] != null && !dep.dynamic) {
|
680 | bundle.addAsset(asset);
|
681 | }
|
682 |
|
683 | if ((dep && dep.dynamic) || !bundle.type) {
|
684 |
|
685 | if (isEntryAsset) {
|
686 | return;
|
687 | }
|
688 |
|
689 |
|
690 | bundle = bundle.createChildBundle(asset, dep);
|
691 | } else if (
|
692 | asset.type &&
|
693 | !this.packagers.get(asset.type).shouldAddAsset(bundle, asset)
|
694 | ) {
|
695 |
|
696 | if (isEntryAsset) {
|
697 | return;
|
698 | }
|
699 |
|
700 |
|
701 |
|
702 | bundle = bundle.createSiblingBundle(asset, dep);
|
703 | } else {
|
704 |
|
705 | bundle.getSiblingBundle(asset.type).addAsset(asset);
|
706 | }
|
707 |
|
708 |
|
709 | if (asset.type && asset.generated[asset.type]) {
|
710 | for (let t in asset.generated) {
|
711 | if (asset.generated[t]) {
|
712 | bundle.getSiblingBundle(t).addAsset(asset);
|
713 | }
|
714 | }
|
715 | }
|
716 |
|
717 | asset.parentBundle = bundle;
|
718 | parentBundles.add(bundle);
|
719 |
|
720 | for (let [dep, assetDep] of asset.depAssets) {
|
721 | this.createBundleTree(assetDep, bundle, dep, parentBundles);
|
722 | }
|
723 |
|
724 | parentBundles.delete(bundle);
|
725 | return bundle;
|
726 | }
|
727 |
|
728 | moveAssetToBundle(asset, commonBundle) {
|
729 |
|
730 | if (
|
731 | asset.parentBundle.entryAsset === asset ||
|
732 | asset.parentBundle === commonBundle
|
733 | ) {
|
734 | return;
|
735 | }
|
736 |
|
737 | for (let bundle of Array.from(asset.bundles)) {
|
738 | if (!bundle.isolated) {
|
739 | bundle.removeAsset(asset);
|
740 | }
|
741 | commonBundle.getSiblingBundle(bundle.type).addAsset(asset);
|
742 | }
|
743 |
|
744 | let oldBundle = asset.parentBundle;
|
745 | asset.parentBundle = commonBundle;
|
746 |
|
747 |
|
748 | for (let child of asset.depAssets.values()) {
|
749 | if (child.parentBundle === oldBundle) {
|
750 | this.moveAssetToBundle(child, commonBundle);
|
751 | }
|
752 | }
|
753 | }
|
754 |
|
755 | *findOrphanAssets() {
|
756 | for (let asset of this.loadedAssets.values()) {
|
757 | if (!asset.parentBundle) {
|
758 | yield asset;
|
759 | }
|
760 | }
|
761 | }
|
762 |
|
763 | unloadOrphanedAssets() {
|
764 | for (let asset of this.findOrphanAssets()) {
|
765 | this.unloadAsset(asset);
|
766 | }
|
767 | }
|
768 |
|
769 | unloadAsset(asset) {
|
770 | this.loadedAssets.delete(asset.name);
|
771 | if (this.watcher) {
|
772 | this.unwatch(asset.name, asset);
|
773 |
|
774 |
|
775 | for (let dep of asset.dependencies.values()) {
|
776 | if (dep.includedInParent) {
|
777 | this.unwatch(dep.name, asset);
|
778 | }
|
779 | }
|
780 | }
|
781 | }
|
782 |
|
783 | async onAdd(path) {
|
784 | path = Path.join(process.cwd(), path);
|
785 |
|
786 | let asset = this.parser.getAsset(path, this.options);
|
787 | this.loadedAssets.set(path, asset);
|
788 |
|
789 | this.entryAssets.add(asset);
|
790 |
|
791 | await this.watch(path, asset);
|
792 | this.onChange(path);
|
793 | }
|
794 |
|
795 | async onChange(path) {
|
796 |
|
797 | if (!Path.isAbsolute(path)) {
|
798 | path = Path.resolve(process.cwd(), path);
|
799 | }
|
800 |
|
801 | let assets = this.watchedAssets.get(path);
|
802 | if (!assets || !assets.size) {
|
803 | return;
|
804 | }
|
805 |
|
806 | logger.clear();
|
807 | logger.progress(`Building ${Path.basename(path)}...`);
|
808 |
|
809 |
|
810 | for (let asset of assets) {
|
811 | this.buildQueue.add(asset, true);
|
812 | }
|
813 |
|
814 | clearTimeout(this.rebuildTimeout);
|
815 |
|
816 | this.rebuildTimeout = setTimeout(async () => {
|
817 | await this.bundle();
|
818 | }, 100);
|
819 | }
|
820 |
|
821 | async onUnlink(path) {
|
822 |
|
823 | if (!Path.isAbsolute(path)) {
|
824 | path = Path.resolve(process.cwd(), path);
|
825 | }
|
826 |
|
827 | let asset = this.getLoadedAsset(path);
|
828 | this.entryAssets.delete(asset);
|
829 | this.unloadAsset(asset);
|
830 |
|
831 | this.bundle();
|
832 | }
|
833 |
|
834 | middleware() {
|
835 | this.bundle();
|
836 | return Server.middleware(this);
|
837 | }
|
838 |
|
839 | async serve(port = 1234, https = false, host) {
|
840 | this.server = await Server.serve(this, port, host, https);
|
841 | try {
|
842 | await this.bundle();
|
843 | } catch (e) {
|
844 |
|
845 | }
|
846 | return this.server;
|
847 | }
|
848 | }
|
849 |
|
850 | module.exports = Bundler;
|
851 | Bundler.Asset = require('./Asset');
|
852 | Bundler.Packager = require('./packagers/Packager');
|