@enact/dev-utils
Version:
A collection of development utilities for Enact apps.
153 lines (138 loc) • 4.59 kB
JavaScript
const fs = require('fs');
const path = require('path');
const DllEntryPlugin = require('webpack/lib/DllEntryPlugin');
const DllModule = require('webpack/lib/DllModule');
const RuntimeGlobals = require('webpack/lib/RuntimeGlobals');
const {RawSource} = require('webpack-sources');
const app = require('../../option-parser');
const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.require, RuntimeGlobals.module]);
const pkgCache = {};
const checkPkgMain = function (dir) {
if (pkgCache[dir]) {
return pkgCache[dir].main;
} else {
try {
const text = fs.readFileSync(path.join(dir, 'package.json'), {encoding: 'utf8'});
pkgCache[dir] = JSON.parse(text);
return pkgCache[dir].main;
} catch (e) {
return undefined;
}
}
};
const parentCache = {};
const findParent = function (dir) {
if (parentCache[dir]) {
return parentCache[dir];
} else {
const currPkg = path.join(dir, 'package.json');
if (fs.existsSync(currPkg)) {
return dir;
} else if (dir === '/' || dir === '' || dir === '.') {
return null;
} else {
return findParent(path.dirname(dir));
}
}
};
function normalizeModuleID(id, polyfill) {
const dir = fs.existsSync(id) && fs.statSync(id).isDirectory() ? id : path.dirname(id);
parentCache[dir] = findParent(dir);
if (parentCache[dir]) {
const main = checkPkgMain(parentCache[dir]);
if (main && path.resolve(id) === path.resolve(path.join(parentCache[dir], main))) {
id = parentCache[dir];
}
}
id = id.replace(/\\/g, '/');
// Remove any leading ./node_modules prefix
const nodeModulesPrefix = './node_modules/';
if (id.startsWith(nodeModulesPrefix)) {
id = id.substring(nodeModulesPrefix.length);
}
// Reduce core-js long paths to shorthand
if (id.includes('node_modules/core-js/')) {
id = id.substring(id.indexOf('node_modules/core-js/') + 13);
}
// Transform IDs as needed
if (path.resolve(id) === polyfill) {
// shorthand to load core-js
id = '@enact/polyfills';
} else if (!id.includes('node_modules')) {
// Remove any js file extension
if (id.endsWith('.js')) {
id = id.substring(0, id.length - 3);
}
// Remove any /index suffix as we want the user-accessible ID
if (id.endsWith('/index') && id.length > 6 && !id.startsWith('core-js')) {
id = id.substring(0, id.length - 6);
}
// Add package name prefix for local files
if (id.startsWith('.') && !id.startsWith('..')) {
id = id.replace('.', app.name);
}
}
return id;
}
DllModule.prototype.codeGeneration = function () {
const sources = new Map();
let header = '';
if (DllModule.entries[this.name]) {
header += '__webpack_require__.load = function(loader) {\n';
header += '\tloader = loader || __webpack_require__;';
for (let i = 0; i < DllModule.entries[this.name].length; i++) {
header += "\tloader('" + DllModule.entries[this.name][i] + "');\n";
}
header += '};\n';
}
sources.set('javascript', new RawSource(header + 'module.exports = __webpack_require__;'));
return {
sources,
runtimeRequirements: RUNTIME_REQUIREMENTS
};
};
class EnactFrameworkPlugin {
constructor(options = {}) {
this.options = options;
}
apply(compiler) {
const poly = this.options.polyfill;
// Map entries to the DLLEntryPlugin
DllModule.entries = {};
compiler.hooks.entryOption.tap('EnactFrameworkPlugin', (context, entry) => {
function itemToPlugin(item, name) {
if (Array.isArray(item)) {
DllModule.entries[name] = [];
for (let i = 0; i < item.length; i++) {
const prefix = item[i].startsWith('.') ? '' : './node_modules/';
DllModule.entries[name].push(normalizeModuleID(prefix + item[i], poly));
}
return new DllEntryPlugin(context, item, {name});
} else {
throw new Error('EnactFrameworkPlugin: supply an Array as entry');
}
}
if (typeof entry === 'object') {
Object.keys(entry).forEach(name => itemToPlugin(entry[name].import, name).apply(compiler));
} else {
itemToPlugin(entry, 'main').apply(compiler);
}
return true;
});
// Format the internal module ID to a usable named descriptor
compiler.hooks.compilation.tap('EnactFrameworkPlugin', compilation => {
compilation.hooks.beforeModuleIds.tap('EnactFrameworkPlugin', modules => {
const chunkGraph = compilation.chunkGraph;
modules.forEach(m => {
const ident = m.libIdent({
context: this.options.context || compiler.options.context
});
if (ident && chunkGraph.getModuleId(m) === null) {
chunkGraph.setModuleId(m, normalizeModuleID(ident, poly));
}
}, this);
});
});
}
}
module.exports = EnactFrameworkPlugin;