electron-webpack
Version:
> Because setting up `webpack` in the `electron` environment shouldn't be difficult.
554 lines (426 loc) • 16.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAppConfiguration = getAppConfiguration;
exports.getMainConfiguration = getMainConfiguration;
exports.getRendererConfiguration = getRendererConfiguration;
exports.getDllConfiguration = getDllConfiguration;
exports.getTestConfiguration = getTestConfiguration;
exports.createConfigurator = createConfigurator;
exports.configure = configure;
exports.WebpackConfigurator = void 0;
function BluebirdPromise() {
const data = _interopRequireWildcard(require("bluebird"));
BluebirdPromise = function () {
return data;
};
return data;
}
function _dotenv() {
const data = require("dotenv");
_dotenv = function () {
return data;
};
return data;
}
function _dotenvExpand() {
const data = _interopRequireDefault(require("dotenv-expand"));
_dotenvExpand = function () {
return data;
};
return data;
}
function _fsExtra() {
const data = require("fs-extra");
_fsExtra = function () {
return data;
};
return data;
}
function _lazyVal() {
const data = require("lazy-val");
_lazyVal = function () {
return data;
};
return data;
}
var path = _interopRequireWildcard(require("path"));
function _readConfigFile() {
const data = require("read-config-file");
_readConfigFile = function () {
return data;
};
return data;
}
function _deepAssign() {
const data = require("read-config-file/out/deepAssign");
_deepAssign = function () {
return data;
};
return data;
}
require("source-map-support/register");
function _webpackMerge() {
const data = _interopRequireDefault(require("webpack-merge"));
_webpackMerge = function () {
return data;
};
return data;
}
function _config() {
const data = require("./config");
_config = function () {
return data;
};
return data;
}
function _ts() {
const data = require("./configurators/ts");
_ts = function () {
return data;
};
return data;
}
function _vue() {
const data = require("./configurators/vue/vue");
_vue = function () {
return data;
};
return data;
}
function _BaseTarget() {
const data = require("./targets/BaseTarget");
_BaseTarget = function () {
return data;
};
return data;
}
function _MainTarget() {
const data = require("./targets/MainTarget");
_MainTarget = function () {
return data;
};
return data;
}
function _RendererTarget() {
const data = require("./targets/RendererTarget");
_RendererTarget = function () {
return data;
};
return data;
}
function _util() {
const data = require("./util");
_util = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const _debug = require("debug"); // noinspection JSUnusedGlobalSymbols
function getAppConfiguration(env) {
return BluebirdPromise().filter([configure("main", env), configure("renderer", env)], it => it != null);
} // noinspection JSUnusedGlobalSymbols
function getMainConfiguration(env) {
return configure("main", env);
} // noinspection JSUnusedGlobalSymbols
function getRendererConfiguration(env) {
return configure("renderer", env);
} // in the future, if need, isRenderer = true arg can be added
// noinspection JSUnusedGlobalSymbols
function getDllConfiguration(env) {
return configure("renderer-dll", env);
} // noinspection JSUnusedGlobalSymbols
async function getTestConfiguration(env) {
const configurator = await createConfigurator("test", env);
return await configurator.configure({
testComponents: path.join(process.cwd(), "src/renderer/components/testComponents.ts")
});
}
class WebpackConfigurator {
// electronWebpackConfiguration expected to be resolved (use getElectronWebpackConfiguration())
constructor(type, env, electronWebpackConfiguration, metadata) {
this.type = type;
this.env = env;
this.electronWebpackConfiguration = electronWebpackConfiguration;
this.metadata = metadata;
this.electronVersionPromise = new (_lazyVal().Lazy)(() => getInstalledElectronVersion(this.projectDir));
this.isTest = this.type === "test";
this.debug = _debug(`electron-webpack:${this.type}`);
this._configuration = null;
this.rules = [];
this.plugins = []; // js must be first - e.g. iView has two files loading-bar.js and loading-bar.vue - when we require "loading-bar", js file must be resolved and not vue
this.extensions = [".js", ".json", ".node"];
this._electronVersion = null;
this.entryFiles = [];
if (metadata.dependencies == null) {
metadata.dependencies = {};
}
if (metadata.devDependencies == null) {
metadata.devDependencies = {};
}
this.projectDir = electronWebpackConfiguration.projectDir;
this.isRenderer = type.startsWith("renderer");
process.env.BABEL_ENV = type;
this.isProduction = this.env.production == null ? process.env.NODE_ENV === "production" : this.env.production;
this.debug(`isProduction: ${this.isProduction}`);
this.sourceDir = this.getSourceDirectory(this.type);
this.staticSourceDirectory = this.electronWebpackConfiguration.staticSourceDirectory;
this.commonSourceDirectory = this.electronWebpackConfiguration.commonSourceDirectory;
this.commonDistDirectory = this.electronWebpackConfiguration.commonDistDirectory;
this.rendererTemplate = this.electronWebpackConfiguration.renderer && this.electronWebpackConfiguration.renderer.template || "src/index.ejs";
}
get config() {
return this._configuration;
}
get electronVersion() {
return this._electronVersion;
}
/**
* Returns null if code processing for type is disabled.
*/
getSourceDirectory(type) {
const part = this.getPartConfiguration(type);
if (part === null || part != null && part.sourceDirectory === null) {
// part or sourceDirectory is explicitly set to null
return null;
}
const result = part == null ? null : part.sourceDirectory;
if (result == null) {
return path.join(this.projectDir, "src", type.startsWith("renderer") || type === "test" ? "renderer" : type);
} else {
return path.resolve(this.projectDir, result);
}
}
getPartConfiguration(type) {
if (type === "main") {
return this.electronWebpackConfiguration.main;
} else {
return this.electronWebpackConfiguration.renderer;
}
}
hasDependency(name) {
return name in this.metadata.dependencies || this.hasDevDependency(name);
}
hasDevDependency(name) {
return name in this.metadata.devDependencies;
}
/**
* Returns the names of devDependencies that match a given string or regex.
* If no matching dependencies are found, an empty array is returned.
*
* @return list of matching dependency names, e.g. `["@babel/preset-react", "@babel/preset-stage-0"]`
*/
getMatchingDevDependencies(options = {}) {
const includes = options.includes || [];
const excludes = new Set(options.excludes || []);
return Object.keys(this.metadata.devDependencies).filter(name => !excludes.has(name) && includes.some(prefix => name.startsWith(prefix)));
}
async configure(entry) {
// noinspection SpellCheckingInspection
this._configuration = {
context: this.projectDir,
devtool: this.isProduction || this.isTest ? "nosources-source-map" : "eval-source-map",
externals: this.computeExternals(),
node: {
__dirname: !this.isProduction,
__filename: !this.isProduction
},
output: {
filename: "[name].js",
chunkFilename: "[name].bundle.js",
libraryTarget: "commonjs2",
path: path.join(this.commonDistDirectory, this.type)
},
target: this.isTest ? "node" : `electron-${this.type === "renderer-dll" ? "renderer" : this.type}`,
resolve: {
alias: {
"@": this.sourceDir,
common: this.commonSourceDirectory
},
extensions: this.extensions
},
module: {
rules: this.rules
},
plugins: this.plugins
};
if (entry != null) {
this._configuration.entry = entry;
} // if electronVersion not specified, use latest
this._electronVersion = this.electronWebpackConfiguration.electronVersion || (await this.electronVersionPromise.value) || "3.0.7";
const target = (() => {
switch (this.type) {
case "renderer":
return new (_RendererTarget().RendererTarget)();
case "renderer-dll":
return new (_RendererTarget().BaseRendererTarget)();
case "test":
return new (_RendererTarget().BaseRendererTarget)();
case "main":
return new (_MainTarget().MainTarget)();
default:
return new (_BaseTarget().BaseTarget)();
}
})();
this.debug(`Target class: ${target.constructor.name}`);
target.configureRules(this);
await Promise.all([target.configurePlugins(this), (0, _ts().configureTypescript)(this)]);
(0, _vue().configureVue)(this);
if (this.debug.enabled) {
this.debug(`\n\n${this.type} config:` + JSON.stringify(this._configuration, null, 2) + "\n\n");
}
if (this.config.entry == null) {
this.entryFiles.push((await computeEntryFile(this.sourceDir, this.projectDir)));
this.config.entry = {
[this.type]: this.entryFiles
};
const mainConfiguration = this.electronWebpackConfiguration.main || {};
let extraEntries = mainConfiguration.extraEntries;
if (this.type === "main" && extraEntries != null) {
if (typeof extraEntries === "string") {
extraEntries = [extraEntries];
}
if (Array.isArray(extraEntries)) {
for (const p of extraEntries) {
this.config.entry[path.basename(p, path.extname(p))] = p;
}
} else {
Object.assign(this.config.entry, extraEntries);
}
}
} // noinspection ES6RedundantAwait
this._configuration = await Promise.resolve(this.applyCustomModifications(this.config));
return this.config;
}
applyCustomModifications(config) {
const {
renderer,
main
} = this.electronWebpackConfiguration;
const applyCustom = configPath => {
const customModule = require(path.join(this.projectDir, configPath));
if (typeof customModule === "function") {
return customModule(config, this);
} else {
return _webpackMerge().default.smart(config, customModule);
}
};
if (this.type === "renderer" && renderer && renderer.webpackConfig) {
return applyCustom(renderer.webpackConfig);
} else if (this.type === "renderer-dll" && renderer && renderer.webpackDllConfig) {
return applyCustom(renderer.webpackDllConfig);
} else if (this.type === "main" && main && main.webpackConfig) {
return applyCustom(main.webpackConfig);
} else {
return config;
}
}
computeExternals() {
const whiteListedModules = new Set(this.electronWebpackConfiguration.whiteListedModules || []);
if (this.isRenderer) {
whiteListedModules.add("react");
whiteListedModules.add("react-dom");
whiteListedModules.add("vue");
}
const filter = name => !name.startsWith("@types/") && (whiteListedModules == null || !whiteListedModules.has(name));
const externals = Object.keys(this.metadata.dependencies).filter(filter);
externals.push("electron");
externals.push("webpack"); // because electron-devtools-installer specified in the devDependencies, but required in the index.dev
externals.push("electron-devtools-installer");
if (this.type === "main") {
externals.push("webpack/hot/log-apply-result");
externals.push("electron-webpack/out/electron-main-hmr/HmrClient");
externals.push("source-map-support/source-map-support.js");
}
if (this.electronWebpackConfiguration.externals != null) {
return externals.concat(this.electronWebpackConfiguration.externals);
}
return externals;
}
}
exports.WebpackConfigurator = WebpackConfigurator;
const schemeDataPromise = new (_lazyVal().Lazy)(() => (0, _fsExtra().readJson)(path.join(__dirname, "..", "scheme.json")));
async function createConfigurator(type, env) {
if (env != null) {
// allow to pass as `--env.autoClean=false` webpack arg
const _env = env;
for (const name of ["minify", "autoClean", "production"]) {
if (_env[name] === "true") {
_env[name] = true;
} else if (_env[name] === "false") {
_env[name] = false;
}
}
}
if (env == null) {
env = {};
}
const projectDir = (env.configuration || {}).projectDir || process.cwd();
const packageMetadata = (0, _config().getPackageMetadata)(projectDir);
const electronWebpackConfig = await (0, _config().getElectronWebpackConfiguration)({
packageMetadata,
projectDir
});
if (env.configuration != null) {
(0, _deepAssign().deepAssign)(electronWebpackConfig, env.configuration);
}
await (0, _readConfigFile().validateConfig)(electronWebpackConfig, schemeDataPromise, message => {
return `${message}
How to fix:
1. Open https://webpack.electron.build/configuration
2. Search the option name on the page.
* Not found? The option was deprecated or not exists (check spelling).
* Found? Check that the option in the appropriate place. e.g. "sourceDirectory" only in the "main" or "renderer", not in the root.
`;
});
return new WebpackConfigurator(type, env, electronWebpackConfig, (await packageMetadata.value));
}
async function configure(type, env) {
const configurator = await createConfigurator(type, env);
const sourceDir = configurator.sourceDir; // explicitly set to null - do not handle at all and do not show info message
if (sourceDir === null) {
return null;
}
const processEnv = configurator.isProduction ? "production" : "development";
const dotEnvPath = path.resolve(configurator.projectDir, ".env");
const dotenvFiles = [`${dotEnvPath}.${processEnv}.local`, `${dotEnvPath}.${processEnv}`, `${dotEnvPath}.local`, dotEnvPath];
for (const file of dotenvFiles) {
const exists = await (0, _fsExtra().pathExists)(file);
if (exists) {
(0, _dotenvExpand().default)((0, _dotenv().config)({
path: file
}));
}
}
return await configurator.configure();
}
async function computeEntryFile(srcDir, projectDir) {
const candidates = [];
for (const ext of ["ts", "js", "tsx", "jsx"]) {
for (const name of ["index", "main", "app"]) {
candidates.push(`${name}.${ext}`);
}
}
const file = await (0, _util().getFirstExistingFile)(candidates, srcDir);
if (file == null) {
throw new Error(`Cannot find entry file ${path.relative(projectDir, path.join(srcDir, "index.ts"))} (or main.ts, or app.ts, or index.js, or main.js, or app.js)`);
}
return file;
}
async function getInstalledElectronVersion(projectDir) {
for (const name of ["electron", "electron-prebuilt", "electron-prebuilt-compile"]) {
try {
return (await (0, _fsExtra().readJson)(path.join(projectDir, "node_modules", name, "package.json"))).version;
} catch (e) {
if (e.code !== "ENOENT") {
throw e;
}
}
}
}
// __ts-babel@6.0.4
//# sourceMappingURL=main.js.map