@adobe/helix-cli
Version:
Project Helix CLI
156 lines (136 loc) • 5.13 kB
JavaScript
/*
* Copyright 2018 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
const Packager = require('parcel-bundler/src/packagers/Packager');
const objectHash = require('parcel-bundler/src//utils/objectHash');
const path = require('path');
const fs = require('fs-extra');
/**
* Parcel packager that writes the source code as-is to the target without adding any
* transformation. It also creates a `.info.json` file that contains the dependencies of the
* processed asset.
*
* For example:
*
* ```
* {
* main: "html.js",
* requires: [
* "html.pre.js"
* ]
* }
* ```
*/
class RawJSPackager extends Packager {
async setup() {
await fs.ensureDir(path.dirname(this.bundle.name));
}
getSize() {
return this._size || 0;
}
async start() {
this.first = true;
this.dedupe = new Map();
this.bundleLoaders = new Set();
this.externalModules = new Set();
}
async addAsset(asset) {
// If this module is referenced by another JS bundle, it needs to be exposed externally.
// In that case, don't dedupe the asset as it would affect the module ids that are referenced
// by other bundles.
const isExposed = !Array.from(asset.parentDeps).every((dep) => {
const depAsset = this.bundler.loadedAssets.get(dep.parent);
return this.bundle.assets.has(depAsset) || depAsset.type !== 'js';
});
if (!isExposed) {
const key = this.dedupeKey(asset);
if (this.dedupe.has(key)) {
return;
}
// Don't dedupe when HMR is turned on since it messes with the asset ids
if (!this.options.hmr) {
this.dedupe.set(key, asset.id);
}
}
const deps = {};
// eslint-disable-next-line no-restricted-syntax
for (const [dep, mod] of asset.depAssets) {
// For dynamic dependencies, list the child bundles to load along with the module id
if (dep.dynamic) {
const bundles = [this.getBundleSpecifier(mod.parentBundle)];
// eslint-disable-next-line no-restricted-syntax
for (const child of mod.parentBundle.siblingBundles) {
if (!child.isEmpty) {
bundles.push(this.getBundleSpecifier(child));
this.bundleLoaders.add(child.type);
}
}
bundles.push(mod.id);
deps[dep.name] = bundles;
this.bundleLoaders.add(mod.type);
} else {
deps[dep.name] = this.dedupe.get(this.dedupeKey(mod)) || mod.id;
// If the dep isn't in this bundle, add it to the list of external modules to preload.
// Only do this if this is the root JS bundle, otherwise they will have already been
// loaded in parallel with this bundle as part of a dynamic import.
if (!this.bundle.assets.has(mod)) {
this.externalModules.add(mod);
if (
!this.bundle.parentBundle
|| this.bundle.isolated
|| this.bundle.parentBundle.type !== 'js'
) {
this.bundleLoaders.add(mod.type);
}
}
}
}
// handle source map
const mapBundle = this.bundle.siblingBundlesMap.get('map');
let code = asset.generated.js;
if (mapBundle) {
const mapName = path.basename(mapBundle.name);
code = `${code}\n//# sourceMappingURL=${mapName}`;
}
// write file
await fs.writeFile(this.bundle.name, code, 'utf-8');
this._size = code.length;
// write info
const bundleDir = path.dirname(this.bundle.name);
const info = {
main: path.basename(this.bundle.name),
requires: Object.keys(deps)
.map(dep => path.relative(bundleDir, path.resolve(bundleDir, dep))),
};
const infoName = path.resolve(bundleDir, `${path.basename(this.bundle.name, '.js')}.info.json`);
await fs.writeJSON(infoName, info, { spaces: 2 });
}
// eslint-disable-next-line class-methods-use-this
getBundleSpecifier(bundle) {
const name = path.basename(bundle.name);
if (bundle.entryAsset) {
return [name, bundle.entryAsset.id];
}
return name;
}
// eslint-disable-next-line class-methods-use-this
dedupeKey(asset) {
// cannot rely *only* on generated JS for deduplication because paths like
// `../` can cause 2 identical JS files to behave differently depending on
// where they are located on the filesystem
const deps = Array.from(asset.depAssets.values(), dep => dep.name).sort();
return objectHash([asset.generated.js, deps]);
}
// eslint-disable-next-line class-methods-use-this,no-empty-function
async end() {
}
}
module.exports = RawJSPackager;