UNPKG

@sanpjs/bundler-webpack

Version:

@sanpjs/bundler-webpack

250 lines 9.13 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /** * @file Sanp Webpack Bundler * @author wangyongqing <wangyongqing01@baidu.com> */ const fs_extra_1 = __importDefault(require("fs-extra")); const webpack_1 = __importDefault(require("webpack")); const webpack_merge_1 = require("webpack-merge"); const tapable_1 = require("tapable"); const dotenv_1 = __importDefault(require("dotenv")); const server_1 = __importDefault(require("@sanpjs/server")); const rules = __importStar(require("./rules")); const partials = __importStar(require("./partials")); const utils_1 = require("./utils"); const inspect_1 = __importDefault(require("./inspect")); const async_mfs_1 = __importDefault(require("./utils/async-mfs")); class BundlerWebpack { context; hooks; logger; rawOptions; running; server; useMfs; mfs; constructor(context) { this.context = context; this.running = false; this.context.bundler = this; this.hooks = Object.freeze({ config: new tapable_1.AsyncSeriesHook(['config']), failed: new tapable_1.SyncHook(['error']), done: new tapable_1.SyncHook(['stats']) }); // 处理build options对应的finalize this.logger = context.logger; this.rawOptions = context.config; this.useMfs = this.context.dev && this.context.server?.devMiddleware?.mfs; if (this.useMfs) { this.mfs = new async_mfs_1.default(); } } run(args, callback) { if (this.running) { return; } this.running = true; const finalCallback = (err, stats) => { // watch 模式下不改变 running this.running = this.context.watch; if (err) { this.logger.error(err); this.hooks.failed.call(err); callback && callback(err); return; } this.hooks.done.callAsync(stats, (err) => { if (err) { return this.hooks.failed.call(err); } callback && callback(null, stats); }); }; this.toConfig(args, (err, config) => { if (err || !config) { return finalCallback(err); } // 最终调用 webpack 的地方 const compiler = (0, webpack_1.default)(config); if (this.context.dev && config[0].devServer) { if (this.useMfs) { compiler.outputFileSystem = this.mfs; } this.server = new server_1.default(config[0].devServer, compiler, this.context); return this.server.run(finalCallback); } compiler.run((err, stats) => { if (err) { return finalCallback(err); } if (stats && stats.hasErrors()) { return finalCallback(stats.toJson().errors); } finalCallback(null, stats); stats && (0, inspect_1.default)(stats, args, this); }); // fixme: 此处调用会导致build立即结束 // this.hooks.compiler.callAsync(compiler, err => { // if (err) { // return finalCallback(err); // } // }); }); } async close() { if (this.running) { this.server && (await this.server.close()); this.server = null; this.running = false; if (this.mfs) { delete this.mfs.data; delete this.mfs; } } } _loadPartial(name, args, callback) { try { const partial = partials[name]; callback(null, partial(this.context, args)); } catch (e) { // this.logger.error(`fail to load config partial ${name}`, e); callback(e); } } _loadEnv(mode) { const load = (envPath) => { let env = {}; try { const content = fs_extra_1.default.readFileSync(envPath); env = dotenv_1.default.parse(content) || {}; this.logger.debug('loadEnv envPath ', envPath); this.logger.debug('loadEnv env object ', env); } catch (err) { // 文件不存在 if (err.toString().indexOf('ENOENT') < 0) { this.logger.error(err); } } return env; }; let env = {}; ['.env', `.env.${mode}`, `.env.${mode}.local`].forEach(envName => { const envPath = this.context.resolver.resolveEntry(envName, false); if (fs_extra_1.default.existsSync(envPath)) { env = Object.assign(env, load(envPath)); } }); process.env.NODE_ENV = mode; Object.keys(env).forEach(key => { if (!process.env.hasOwnProperty(key)) { process.env[key] = env[key]; } }); } normalizeConfig(args, callback) { const { mode } = args; let webpackConfig = { // 此处注释后dev下不输出服务地址 // infrastructureLogging: { // level: 'none' // }, stats: 'errors-only' }; const loadPartialCallback = (err, partialConfig) => { if (err) { return callback(err); } webpackConfig = (0, webpack_merge_1.merge)(webpackConfig, partialConfig); }; // 遍历将 partials 中的配置合并到 webpackConfig 中 const partialTypes = ['base', mode]; for (let t of partialTypes) { this._loadPartial(t, args, loadPartialCallback); } return webpackConfig; } normalizeSsrConfig(args, callback) { const { mode } = args; let webpackConfig = { // 此处注释后dev下不输出服务地址 // infrastructureLogging: { // level: 'none' // }, stats: 'errors-only' }; // 注入环境变量 this._loadEnv(mode); const loadPartialCallback = (err, partialConfig) => { if (err) { return callback(err); } webpackConfig = (0, webpack_merge_1.merge)(webpackConfig, partialConfig); }; // 遍历将 partials 中的配置合并到 webpackConfig 中 const partialTypes = ['server', mode]; for (let t of partialTypes) { this._loadPartial(t, args, loadPartialCallback); } // TODO: 先跑通,后续独立搞一个 partials/server.ts 来处理 SSR 下的配置 webpackConfig.devtool = false; webpackConfig.devServer = undefined; return webpackConfig; } toConfig(args, callback) { const context = this.context; const { finalize } = context.config.build; let webpackConfigs = [this.normalizeConfig(args, callback)]; // ssr 的打包处理 if (context.isSsr) { webpackConfigs.push(this.normalizeSsrConfig(args, callback)); } const internals = { rules, loader: utils_1.getLoader, loaders: utils_1.getLoaders, replaceOption: utils_1.replaceOption, replaceRule: utils_1.replaceRule }; // 调用 hooks.config 相关的 tapAsync 注册的方法 this.hooks.config.callAsync(webpackConfigs, (err) => { if (err) { return callback(err); } let configs = webpackConfigs; // 通过 finalize 进行配置的终极处理 if (finalize) { configs = webpackConfigs.map(webpackConfig => finalize(webpackConfig, internals, context)); } callback(null, configs); }); } } exports.default = BundlerWebpack; ; //# sourceMappingURL=index.js.map