build-plugin-sto-public-path
Version:
解决React.lazy模式下申坊无法注入publicPath的问题
283 lines (235 loc) • 8.45 kB
JavaScript
/*!
* build-plugin-sto-public-path | 解决React.lazy模式下申坊无法注入publicPath的问题
* @version 1.0.0
* @author Frank Fu <fz1986856560@gmail.com>
* Released under MIT license
* Copyright (c) 2021 Frank Fu.
*/
;
var path = require('path');
var fs = require('fs');
var yargsParser = require('yargs-parser');
var detectPort = require('detect-port');
var terser = require('terser');
var webpack = require('webpack');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var yargsParser__default = /*#__PURE__*/_interopDefaultLegacy(yargsParser);
var detectPort__default = /*#__PURE__*/_interopDefaultLegacy(detectPort);
var terser__default = /*#__PURE__*/_interopDefaultLegacy(terser);
var webpack__default = /*#__PURE__*/_interopDefaultLegacy(webpack);
/**
* getEnv 与 getAssetsUrl 两个方法会以字符串的形式注入到其他方法中
* 故不要引用外部变量
*/
function getEnv$1() {
var daily = 'DAILY';
var pre = 'PRE';
var prod = 'PROD';
var host = location.host;
if (/localhost|local|\d+\.\d+\.\d+\.\d+|test/.test(host)) {return daily;}
if (/daily\./.test(host)) {return daily;}
if (/pre-/.test(host)) {return pre;}
return prod;
}
function getAssetsBuildPath$3(path) {
if (!path || typeof path !== 'string') {
return '/';
}
const cwd = process.cwd();
const rest = path.replace(cwd, '').split(path__default["default"].sep);
const dirPath = rest.slice(2);
return dirPath.length ? `/${dirPath.join('/')}/` : '/';
}
function getAssetsUrl$1(env, projectPath) {
var urlMaps = {
DAILY: '//assets-test.sto.cn',
PRE: '//pre-assets.sto.cn',
PROD: '//assets.sto.cn',
};
var shimPath = projectPath.indexOf('/') === 0 ? '' : '/';
return urlMaps[env] + shimPath + projectPath;
}
function getAssetName(chunks, chunkName, fileType) {
return (chunks.filter(function (chunk) {
return chunk.name === chunkName
})[0] || {files: []}).files.filter(el => {
if (fileType) {
if (Array.isArray(fileType)) {
return fileType.some(element => {
const reg = new RegExp('\\\.' + element + '$');
return reg.test(el)
})
} else {
return new RegExp('\\\.' + fileType + '$').test(el);
}
} else {
return true;
}
});}
/**
* 生成唯一 uuid
*/
function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : r & 0x3 | 0x8;
return v.toString(16).toUpperCase();
});
}
var utils = {
getEnv: getEnv$1,
uuid,
getAssetName,
getAssetsUrl: getAssetsUrl$1,
getAssetsBuildPath: getAssetsBuildPath$3,
};
const { getAssetsBuildPath: getAssetsBuildPath$2 } = utils;
function getDefaultOption() {
const rawArgv = yargsParser__default["default"](process.argv.slice(2), {
configuration: { 'strip-dashed': true },
});
const DEFAULT_PORT = rawArgv.port || process.env.PORT;
const DISABLE_ASK = !!rawArgv.disableAsk || !!process.env.DISABLE_ASK || false;
return {
defaultPort: parseInt(DEFAULT_PORT || 3333, 10),
disabledAsk: DISABLE_ASK,
};
}
var dev = async function(buildPath) {
const { defaultPort, disabledAsk } = getDefaultOption();
const newPort = await detectPort__default["default"](defaultPort);
const port = newPort !== defaultPort && !disabledAsk ? parseInt(newPort, 10) : defaultPort;
const path = getAssetsBuildPath$2(buildPath).slice(1);
return `http://localhost:${port}/${path}`;
};
const { minify } = terser__default["default"];
const { getEnv, getAssetsUrl, getAssetsBuildPath: getAssetsBuildPath$1 } = utils;
var prod = async function getRuntimePublicPath({ projectPath, buildPath }) {
let version = process.env.npm_package_version;
// 如果是在devops部署平台
if (process.env.BUILD_PLATFORM === 'devops') {
const buildArgv = JSON.parse(process.env.BUILD_ARGV_STR);
version = buildArgv['def_publish_version'];
}
const { code } = await minify(
`function _p() {
var getEnv = ${getEnv.toString()}
var getAssetsUrl = ${getAssetsUrl.toString()}
var version='${version}';
var projectPath='${projectPath}';
var buildPath='${getAssetsBuildPath$1(buildPath)}';
var currentEnv = getEnv();
var url = getAssetsUrl(currentEnv, projectPath);
return url + version + buildPath;
}`
);
return `(${code})()`;
};
class AddPublicPathEntry {
constructor(opts) {
this.options = Object.assign(
{},
{
additionalEntries: [],
},
opts,
);
}
apply(compiler) {
compiler.hooks.afterEnvironment.tap('AddPublicPathEntry', () => {
const additionalEntries = this.options.additionalEntries;
if (typeof webpack__default["default"].EntryPlugin !== "undefined") {
for (const additionalEntry of additionalEntries) {
new webpack__default["default"].EntryPlugin(compiler.context, additionalEntry, {
// eslint-disable-next-line no-undefined
name: undefined,
}).apply(compiler);
}
} else {
const prependEntry = (originalEntry, newAdditionalEntries) => {
if (typeof originalEntry === "function") {
return () =>
Promise.resolve(originalEntry()).then((entry) =>
prependEntry(entry)
);
}
if (
typeof originalEntry === "object" &&
!Array.isArray(originalEntry)
) {
/** @type {Object<string,string>} */
const clone = {};
Object.keys(originalEntry).forEach((key) => {
// entry[key] should be a string here
const entryDescription = originalEntry[key];
clone[key] = prependEntry(entryDescription);
});
return clone;
}
// in this case, entry is a string or an array.
// make sure that we do not add duplicates.
/** @type {Entry} */
const entriesClone = additionalEntries.slice(0);
[].concat(originalEntry).forEach((newEntry) => {
if (!entriesClone.includes(newEntry)) {
entriesClone.push(newEntry);
}
});
return entriesClone;
};
compiler.options.entry = prependEntry(
compiler.options.entry || "./src");
}
});
}
}
var AddPublicPathEntry_1 = AddPublicPathEntry;
const { getAssetsBuildPath } = utils;
var src = ({ onGetWebpackConfig }, userOptions = {}) => {
onGetWebpackConfig(async config => {
const { enableRuntimePath, projectPath, appType = 'assets', publicPath = {} } = userOptions;
const env = process.env.NODE_ENV;
const isDev = env === 'development';
const isProd = env === 'production';
if (enableRuntimePath) {
if (isDev) {
const buildPath = config.output.store.get('path');
const path = await dev(buildPath);
config.output.publicPath(path);
} else if (isProd) {
const buildPath = config.output.store.get('path');
let code;
if (appType === 'site') {
const realBuildPath = getAssetsBuildPath(buildPath);
code = `
var isMobile = 'ontouchend' in window.document;
var __build_path__ = isMobile ? '${realBuildPath}' : '';
__webpack_public_path__ = window.publicPath + __build_path__;
`;
} else {
const runtimeCode = await prod({
projectPath,
buildPath,
});
code = `__webpack_public_path__ = ${runtimeCode};`;
}
fs__default["default"].writeFileSync(path__default["default"].resolve(__dirname, './wpc.js'), code);
const additionalEntries = [];
const publicPathEntry = path__default["default"].join(__dirname, './wpc.js');
additionalEntries.push(publicPathEntry);
config
.plugin('AddPublicPathEntry')
.use(AddPublicPathEntry_1, [{
additionalEntries,
}]);
}
} else {
if (publicPath[env]) {
config.output.publicPath(publicPath[env]);
}
}
});
};
module.exports = src;