mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-06 03:59:53 +08:00
feat: 重写构建 & 升级到 webpack5
This commit is contained in:
parent
d7a7e1748a
commit
f44acf5c08
package.jsonyarn.lock
packages
fes-compiler
fes-preset-built-in
package.json
src
index.js
plugins
commands
features
alias.jsanalyze.jschainWebpack.jschunks.jscopy.jscssLoader.jscssnano.jsdefine.jsdevScripts.jsextraBabelPlugins.jsextraBabelPresets.jshardSource.jshash.jshtml.jsimageMinimizer.jsnodeModulesTransform.jsstyleLoader.jstargets.jsvueLoader.js
registerMethods.jsutils
fes-template-h5
fes-template
fes
@ -25,7 +25,7 @@
|
|||||||
"strong"
|
"strong"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lerna": "^3.18.4"
|
"lerna": "^3.22.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^11.0.0",
|
"@commitlint/cli": "^11.0.0",
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"commitizen": "^4.2.1",
|
"commitizen": "^4.2.1",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"esbuild-loader": "^2.7.0",
|
"esbuild-loader": "^2.7.0",
|
||||||
"father-build": "^1.18.5",
|
"father-build": "^1.19.1",
|
||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"lint-staged": "^10.4.0",
|
"lint-staged": "^10.4.0",
|
||||||
"vuepress": "^2.0.0-alpha.18"
|
"vuepress": "^2.0.0-alpha.18"
|
||||||
|
@ -24,13 +24,13 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/register": "^7.12.1",
|
"@babel/register": "^7.12.13",
|
||||||
"@umijs/babel-preset-umi": "3.3.3",
|
"@babel/preset-env": "^7.12.13",
|
||||||
"@umijs/utils": "3.3.3",
|
"@umijs/utils": "3.3.3",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"joi": "17.3.0",
|
"joi": "17.3.0",
|
||||||
"readline": "^1.3.0",
|
"readline": "^1.3.0",
|
||||||
"set-value": "3.0.2",
|
"set-value": "3.0.2",
|
||||||
"tapable": "2.0.0"
|
"tapable": "^2.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
packages/fes-compiler/src/service/babelRegister.js
Normal file
43
packages/fes-compiler/src/service/babelRegister.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
lodash,
|
||||||
|
winPath
|
||||||
|
} from '@umijs/utils';
|
||||||
|
|
||||||
|
|
||||||
|
export default class BabelRegister {
|
||||||
|
only = {};
|
||||||
|
|
||||||
|
setOnlyMap({
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
}) {
|
||||||
|
this.only[key] = value;
|
||||||
|
this.register();
|
||||||
|
}
|
||||||
|
|
||||||
|
register() {
|
||||||
|
const only = lodash.uniq(
|
||||||
|
Object.keys(this.only)
|
||||||
|
.reduce((memo, key) => memo.concat(this.only[key]), [])
|
||||||
|
.map(winPath)
|
||||||
|
);
|
||||||
|
require('@babel/register')({
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
require.resolve('@babel/preset-env'),
|
||||||
|
{
|
||||||
|
targets: {
|
||||||
|
node: 'current'
|
||||||
|
},
|
||||||
|
modules: 'commonjs'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
ignore: [/node_modules/],
|
||||||
|
only,
|
||||||
|
extensions: ['.jsx', '.js', '.ts', '.tsx'],
|
||||||
|
babelrc: false,
|
||||||
|
cache: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,8 @@ import { EventEmitter } from 'events';
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { AsyncSeriesWaterfallHook } from 'tapable';
|
import { AsyncSeriesWaterfallHook } from 'tapable';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import { BabelRegister, lodash } from '@umijs/utils';
|
import { lodash } from '@umijs/utils';
|
||||||
|
import BabelRegister from './babelRegister';
|
||||||
import { resolvePresets, pathToObj, resolvePlugins } from './utils/pluginUtils';
|
import { resolvePresets, pathToObj, resolvePlugins } from './utils/pluginUtils';
|
||||||
import loadDotEnv from './utils/loadDotEnv';
|
import loadDotEnv from './utils/loadDotEnv';
|
||||||
import isPromise from './utils/isPromise';
|
import isPromise from './utils/isPromise';
|
||||||
@ -146,6 +147,9 @@ export default class Service extends EventEmitter {
|
|||||||
const localPath = `${basePath}.local`;
|
const localPath = `${basePath}.local`;
|
||||||
loadDotEnv(basePath);
|
loadDotEnv(basePath);
|
||||||
loadDotEnv(localPath);
|
loadDotEnv(localPath);
|
||||||
|
if (process.env.FES_ENV) {
|
||||||
|
loadDotEnv(`${basePath}.${process.env.FES_ENV}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
@ -25,19 +25,46 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@umijs/bundler-webpack": "3.3.3",
|
"@babel/core": "^7.12.13",
|
||||||
"@umijs/server": "3.3.3",
|
"@babel/preset-env": "^7.12.13",
|
||||||
|
"@babel/plugin-proposal-export-default-from": "^7.12.13",
|
||||||
|
"@babel/plugin-proposal-pipeline-operator": "^7.12.13",
|
||||||
|
"@babel/plugin-proposal-do-expressions": "^7.12.13",
|
||||||
|
"@babel/plugin-proposal-function-bind": "^7.12.13",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.12.13",
|
||||||
"@umijs/utils": "3.3.3",
|
"@umijs/utils": "3.3.3",
|
||||||
"@vue/babel-plugin-jsx": "^1.0.0-rc.5",
|
"@vue/babel-plugin-jsx": "^1.0.2",
|
||||||
"@vue/compiler-sfc": "^3.0.4",
|
"@vue/compiler-sfc": "^3.0.4",
|
||||||
"@vue/preload-webpack-plugin": "1.1.2",
|
|
||||||
"@webank/fes-compiler": "^2.0.0-alpha.2",
|
"@webank/fes-compiler": "^2.0.0-alpha.2",
|
||||||
"cliui": "6.0.0",
|
"babel-loader": "^8.2.2",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"core-js": "^3.8.3",
|
||||||
"html-webpack-tags-plugin": "2.0.17",
|
"cliui": "7.0.4",
|
||||||
|
"html-webpack-plugin": "^5.0.0",
|
||||||
|
"html-webpack-tags-plugin": "^3.0.0",
|
||||||
"vue-loader": "^16.1.2",
|
"vue-loader": "^16.1.2",
|
||||||
"webpack-bundle-analyzer": "4.3.0",
|
"webpackbar": "^5.0.0-3",
|
||||||
|
"@soda/friendly-errors-webpack-plugin": "^1.8.0",
|
||||||
|
"webpack-bundle-analyzer": "^4.4.0",
|
||||||
|
"webpack-dev-server": "^3.11.2",
|
||||||
"babel-plugin-import": "1.13.3",
|
"babel-plugin-import": "1.13.3",
|
||||||
"hard-source-webpack-plugin": "0.13.1"
|
"hard-source-webpack-plugin": "0.13.1",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"url-loader": "^4.1.1",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
|
"css-loader": "^5.0.1",
|
||||||
|
"style-loader": "^2.0.0",
|
||||||
|
"mini-css-extract-plugin": "^1.3.5",
|
||||||
|
"css-minimizer-webpack-plugin": "^1.2.0",
|
||||||
|
"less": "3.9.0",
|
||||||
|
"less-loader": "^8.0.0",
|
||||||
|
"autoprefixer": "^10.2.4",
|
||||||
|
"postcss-loader": "^4.2.0",
|
||||||
|
"postcss": "^8.2.4",
|
||||||
|
"postcss-flexbugs-fixes": "^5.0.2",
|
||||||
|
"postcss-safe-parser": "^5.0.2",
|
||||||
|
"copy-webpack-plugin": "^7.0.0",
|
||||||
|
"webpack-chain": "^6.5.1",
|
||||||
|
"webpack": "^5.21.0",
|
||||||
|
"deepmerge": "^4.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,7 @@ export default function () {
|
|||||||
require.resolve('./plugins/features/base'),
|
require.resolve('./plugins/features/base'),
|
||||||
require.resolve('./plugins/features/babelPluginImport'),
|
require.resolve('./plugins/features/babelPluginImport'),
|
||||||
require.resolve('./plugins/features/chainWebpack'),
|
require.resolve('./plugins/features/chainWebpack'),
|
||||||
require.resolve('./plugins/features/chunks'),
|
|
||||||
require.resolve('./plugins/features/cssLoader'),
|
require.resolve('./plugins/features/cssLoader'),
|
||||||
require.resolve('./plugins/features/cssnano'),
|
|
||||||
require.resolve('./plugins/features/copy'),
|
require.resolve('./plugins/features/copy'),
|
||||||
require.resolve('./plugins/features/define'),
|
require.resolve('./plugins/features/define'),
|
||||||
require.resolve('./plugins/features/devScripts'),
|
require.resolve('./plugins/features/devScripts'),
|
||||||
@ -29,22 +27,19 @@ export default function () {
|
|||||||
require.resolve('./plugins/features/extraBabelPlugins'),
|
require.resolve('./plugins/features/extraBabelPlugins'),
|
||||||
require.resolve('./plugins/features/extraBabelPresets'),
|
require.resolve('./plugins/features/extraBabelPresets'),
|
||||||
require.resolve('./plugins/features/extraPostCSSPlugins'),
|
require.resolve('./plugins/features/extraPostCSSPlugins'),
|
||||||
require.resolve('./plugins/features/hash'),
|
|
||||||
require.resolve('./plugins/features/html'),
|
require.resolve('./plugins/features/html'),
|
||||||
require.resolve('./plugins/features/inlineLimit'),
|
require.resolve('./plugins/features/inlineLimit'),
|
||||||
require.resolve('./plugins/features/imageMinimizer'),
|
|
||||||
require.resolve('./plugins/features/lessLoader'),
|
require.resolve('./plugins/features/lessLoader'),
|
||||||
require.resolve('./plugins/features/mountElementId'),
|
require.resolve('./plugins/features/mountElementId'),
|
||||||
require.resolve('./plugins/features/nodeModulesTransform'),
|
|
||||||
require.resolve('./plugins/features/outputPath'),
|
require.resolve('./plugins/features/outputPath'),
|
||||||
require.resolve('./plugins/features/plugins'),
|
require.resolve('./plugins/features/plugins'),
|
||||||
require.resolve('./plugins/features/postcssLoader'),
|
require.resolve('./plugins/features/postcssLoader'),
|
||||||
require.resolve('./plugins/features/proxy'),
|
require.resolve('./plugins/features/proxy'),
|
||||||
require.resolve('./plugins/features/publicPath'),
|
require.resolve('./plugins/features/publicPath'),
|
||||||
require.resolve('./plugins/features/styleLoader'),
|
|
||||||
require.resolve('./plugins/features/singular'),
|
require.resolve('./plugins/features/singular'),
|
||||||
require.resolve('./plugins/features/targets'),
|
require.resolve('./plugins/features/targets'),
|
||||||
require.resolve('./plugins/features/terserOptions'),
|
require.resolve('./plugins/features/terserOptions'),
|
||||||
|
require.resolve('./plugins/features/nodeModulesTransform'),
|
||||||
require.resolve('./plugins/features/vueLoader'),
|
require.resolve('./plugins/features/vueLoader'),
|
||||||
require.resolve('./plugins/features/hardSource'),
|
require.resolve('./plugins/features/hardSource'),
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import webpack from 'webpack';
|
||||||
|
|
||||||
|
export async function build({
|
||||||
|
bundleConfig
|
||||||
|
}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const compiler = webpack(bundleConfig);
|
||||||
|
compiler.run((err, stats) => {
|
||||||
|
if (err || stats.hasErrors()) {
|
||||||
|
try {
|
||||||
|
console.log(stats.toString('errors-only'));
|
||||||
|
} catch (e) {}
|
||||||
|
console.error(err);
|
||||||
|
return reject(new Error('build failed'));
|
||||||
|
}
|
||||||
|
resolve({ stats });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -5,8 +5,9 @@ import {
|
|||||||
cleanTmpPathExceptCache,
|
cleanTmpPathExceptCache,
|
||||||
getBundleAndConfigs,
|
getBundleAndConfigs,
|
||||||
printFileSizes
|
printFileSizes
|
||||||
} from '../../../utils/buildDevUtils';
|
} from '../buildDevUtils';
|
||||||
import generateFiles from '../../../utils/generateFiles';
|
import generateFiles from '../../../utils/generateFiles';
|
||||||
|
import { build } from './build';
|
||||||
|
|
||||||
const logger = new Logger('fes:plugin-built-in');
|
const logger = new Logger('fes:plugin-built-in');
|
||||||
|
|
||||||
@ -28,11 +29,7 @@ export default function (api) {
|
|||||||
await generateFiles({ api, watch: false });
|
await generateFiles({ api, watch: false });
|
||||||
|
|
||||||
// build
|
// build
|
||||||
const {
|
const { bundleConfig } = await getBundleAndConfigs({ api });
|
||||||
bundler,
|
|
||||||
bundleConfigs,
|
|
||||||
bundleImplementor
|
|
||||||
} = await getBundleAndConfigs({ api });
|
|
||||||
try {
|
try {
|
||||||
// clear output path before exec build
|
// clear output path before exec build
|
||||||
if (process.env.CLEAR_OUTPUT !== 'none') {
|
if (process.env.CLEAR_OUTPUT !== 'none') {
|
||||||
@ -42,10 +39,7 @@ export default function (api) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { stats } = await bundler.build({
|
const { stats } = await build({ bundleConfig });
|
||||||
bundleConfigs,
|
|
||||||
bundleImplementor
|
|
||||||
});
|
|
||||||
if (process.env.RM_TMPDIR !== 'none') {
|
if (process.env.RM_TMPDIR !== 'none') {
|
||||||
rimraf.sync(paths.absTmpPath);
|
rimraf.sync(paths.absTmpPath);
|
||||||
}
|
}
|
||||||
|
@ -1,109 +1,70 @@
|
|||||||
import { Bundler as DefaultBundler } from '@umijs/bundler-webpack';
|
|
||||||
import { join, resolve } from 'path';
|
import { join, resolve } from 'path';
|
||||||
import { existsSync, readdirSync, readFileSync } from 'fs';
|
import { existsSync, readdirSync, readFileSync } from 'fs';
|
||||||
import { rimraf, chalk } from '@umijs/utils';
|
import { rimraf, chalk } from '@umijs/utils';
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
|
import getConfig from './webpackConfig/getConfig';
|
||||||
|
|
||||||
export async function getBundleAndConfigs({
|
export async function getBundleAndConfigs({
|
||||||
api,
|
api
|
||||||
port
|
|
||||||
}) {
|
}) {
|
||||||
// bundler
|
|
||||||
const Bundler = await api.applyPlugins({
|
|
||||||
type: api.ApplyPluginsType.modify,
|
|
||||||
key: 'modifyBundler',
|
|
||||||
initialValue: DefaultBundler
|
|
||||||
});
|
|
||||||
|
|
||||||
const bundleImplementor = await api.applyPlugins({
|
|
||||||
key: 'modifyBundleImplementor',
|
|
||||||
type: api.ApplyPluginsType.modify,
|
|
||||||
initialValue: undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
const bundler = new Bundler({
|
|
||||||
cwd: api.cwd,
|
|
||||||
config: api.config
|
|
||||||
});
|
|
||||||
const bundlerArgs = {
|
|
||||||
env: api.env,
|
|
||||||
bundler: { id: Bundler.id, version: Bundler.version }
|
|
||||||
};
|
|
||||||
|
|
||||||
// get config
|
// get config
|
||||||
async function getConfig({ type }) {
|
const env = api.env === 'production' ? 'production' : 'development';
|
||||||
const env = api.env === 'production' ? 'production' : 'development';
|
const getConfigOpts = await api.applyPlugins({
|
||||||
const getConfigOpts = await api.applyPlugins({
|
|
||||||
type: api.ApplyPluginsType.modify,
|
|
||||||
key: 'modifyBundleConfigOpts',
|
|
||||||
initialValue: {
|
|
||||||
env,
|
|
||||||
type,
|
|
||||||
port,
|
|
||||||
hot: process.env.HMR !== 'none',
|
|
||||||
entry: {
|
|
||||||
fes: join(api.paths.absTmpPath, 'fes.js')
|
|
||||||
},
|
|
||||||
// @ts-ignore
|
|
||||||
bundleImplementor,
|
|
||||||
async modifyBabelOpts(opts) {
|
|
||||||
return api.applyPlugins({
|
|
||||||
type: api.ApplyPluginsType.modify,
|
|
||||||
key: 'modifyBabelOpts',
|
|
||||||
initialValue: opts
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async modifyBabelPresetOpts(opts) {
|
|
||||||
return api.applyPlugins({
|
|
||||||
type: api.ApplyPluginsType.modify,
|
|
||||||
key: 'modifyBabelPresetOpts',
|
|
||||||
initialValue: opts
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async chainWebpack(webpackConfig, opts) {
|
|
||||||
return api.applyPlugins({
|
|
||||||
type: api.ApplyPluginsType.modify,
|
|
||||||
key: 'chainWebpack',
|
|
||||||
initialValue: webpackConfig,
|
|
||||||
args: {
|
|
||||||
...opts
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
args: {
|
|
||||||
...bundlerArgs,
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return api.applyPlugins({
|
|
||||||
type: api.ApplyPluginsType.modify,
|
|
||||||
key: 'modifyBundleConfig',
|
|
||||||
initialValue: await bundler.getConfig(getConfigOpts),
|
|
||||||
args: {
|
|
||||||
...bundlerArgs,
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const bundleConfigs = await api.applyPlugins({
|
|
||||||
type: api.ApplyPluginsType.modify,
|
type: api.ApplyPluginsType.modify,
|
||||||
key: 'modifyBundleConfigs',
|
key: 'modifyBundleConfigOpts',
|
||||||
initialValue: [await getConfig({ type: 'csr' })].filter(
|
initialValue: {
|
||||||
Boolean
|
cwd: api.paths.cwd,
|
||||||
),
|
config: api.config,
|
||||||
|
env,
|
||||||
|
entry: {
|
||||||
|
index: join(api.paths.absTmpPath, 'fes.js')
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
async modifyBabelOpts(opts) {
|
||||||
|
return api.applyPlugins({
|
||||||
|
type: api.ApplyPluginsType.modify,
|
||||||
|
key: 'modifyBabelOpts',
|
||||||
|
initialValue: opts
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async modifyBabelPresetOpts(opts) {
|
||||||
|
return api.applyPlugins({
|
||||||
|
type: api.ApplyPluginsType.modify,
|
||||||
|
key: 'modifyBabelPresetOpts',
|
||||||
|
initialValue: opts
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async chainWebpack(webpackConfig, opts) {
|
||||||
|
return api.applyPlugins({
|
||||||
|
type: api.ApplyPluginsType.modify,
|
||||||
|
key: 'chainWebpack',
|
||||||
|
initialValue: webpackConfig,
|
||||||
|
args: {
|
||||||
|
...opts
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async headScripts() {
|
||||||
|
return api.applyPlugins({
|
||||||
|
key: 'addHTMLHeadScripts',
|
||||||
|
type: api.ApplyPluginsType.add,
|
||||||
|
initialState: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
args: {
|
args: {
|
||||||
...bundlerArgs,
|
|
||||||
getConfig
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
const bundleConfig = await api.applyPlugins({
|
||||||
bundleImplementor,
|
type: api.ApplyPluginsType.modify,
|
||||||
bundler,
|
key: 'modifyBundleConfig',
|
||||||
bundleConfigs
|
initialValue: await getConfig(getConfigOpts),
|
||||||
};
|
args: {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { bundleConfig };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cleanTmpPathExceptCache({
|
export function cleanTmpPathExceptCache({
|
||||||
@ -219,7 +180,7 @@ export function printFileSizes(stats, dir) {
|
|||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
chalk.yellow(
|
chalk.yellow(
|
||||||
'Consider reducing it with code splitting: https://umijs.org/docs/load-on-demand'
|
'Consider reducing it with code splitting'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
@ -0,0 +1,46 @@
|
|||||||
|
import WebpackDevServer from 'webpack-dev-server';
|
||||||
|
import webpack from 'webpack';
|
||||||
|
|
||||||
|
|
||||||
|
export function startDevServer({
|
||||||
|
webpackConfig,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
beforeMiddlewares,
|
||||||
|
afterMiddlewares,
|
||||||
|
customerDevServerConfig
|
||||||
|
}) {
|
||||||
|
const options = {
|
||||||
|
contentBase: webpackConfig.output.path,
|
||||||
|
hot: true,
|
||||||
|
host,
|
||||||
|
compress: true,
|
||||||
|
noInfo: true,
|
||||||
|
clientLogLevel: 'silent',
|
||||||
|
stats: 'errors-only',
|
||||||
|
before: (app) => {
|
||||||
|
beforeMiddlewares.forEach((middleware) => {
|
||||||
|
app.use(middleware);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
after: (app) => {
|
||||||
|
afterMiddlewares.forEach((middleware) => {
|
||||||
|
app.use(middleware);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'access-control-allow-origin': '*'
|
||||||
|
},
|
||||||
|
...(customerDevServerConfig || {})
|
||||||
|
};
|
||||||
|
WebpackDevServer.addDevServerEntrypoints(webpackConfig, options);
|
||||||
|
const compiler = webpack(webpackConfig);
|
||||||
|
const server = new WebpackDevServer(compiler, options);
|
||||||
|
|
||||||
|
server.listen(port, host, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return server;
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
import { Server } from '@umijs/server';
|
|
||||||
import { delay } from '@umijs/utils';
|
import { delay } from '@umijs/utils';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import {
|
import {
|
||||||
cleanTmpPathExceptCache,
|
cleanTmpPathExceptCache,
|
||||||
getBundleAndConfigs
|
getBundleAndConfigs
|
||||||
} from '../../../utils/buildDevUtils';
|
} from '../buildDevUtils';
|
||||||
import generateFiles from '../../../utils/generateFiles';
|
import generateFiles from '../../../utils/generateFiles';
|
||||||
import { watchPkg } from './watchPkg';
|
import { watchPkg } from './watchPkg';
|
||||||
|
import { startDevServer } from './devServer';
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
const {
|
const {
|
||||||
@ -24,8 +24,7 @@ export default (api) => {
|
|||||||
for (const unwatch of unwatchs) {
|
for (const unwatch of unwatchs) {
|
||||||
unwatch();
|
unwatch();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
server?.close();
|
||||||
server?.listeningApp?.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
api.registerCommand({
|
api.registerCommand({
|
||||||
@ -37,15 +36,12 @@ export default (api) => {
|
|||||||
port: defaultPort ? parseInt(String(defaultPort), 10) : 8000
|
port: defaultPort ? parseInt(String(defaultPort), 10) : 8000
|
||||||
});
|
});
|
||||||
hostname = process.env.HOST || api.config.devServer?.host || '0.0.0.0';
|
hostname = process.env.HOST || api.config.devServer?.host || '0.0.0.0';
|
||||||
console.log(chalk.cyan('Starting the development server...'));
|
console.log(chalk.cyan(`Starting the development server http://${hostname}:${port} ...`));
|
||||||
process.send({
|
process.send({
|
||||||
type: 'UPDATE_PORT',
|
type: 'UPDATE_PORT',
|
||||||
port
|
port
|
||||||
});
|
});
|
||||||
|
|
||||||
// enable https, HTTP/2 by default when using --https
|
|
||||||
const isHTTPS = process.env.HTTPS || args.https;
|
|
||||||
|
|
||||||
cleanTmpPathExceptCache({
|
cleanTmpPathExceptCache({
|
||||||
absTmpPath: paths.absTmpPath
|
absTmpPath: paths.absTmpPath
|
||||||
});
|
});
|
||||||
@ -141,18 +137,7 @@ export default (api) => {
|
|||||||
await delay(500);
|
await delay(500);
|
||||||
|
|
||||||
// dev
|
// dev
|
||||||
const {
|
const { bundleConfig } = await getBundleAndConfigs({ api });
|
||||||
bundler,
|
|
||||||
bundleConfigs,
|
|
||||||
bundleImplementor
|
|
||||||
} = await getBundleAndConfigs({
|
|
||||||
api,
|
|
||||||
port
|
|
||||||
});
|
|
||||||
const opts = bundler.setupDevServerOpts({
|
|
||||||
bundleConfigs,
|
|
||||||
bundleImplementor
|
|
||||||
});
|
|
||||||
|
|
||||||
const beforeMiddlewares = await api.applyPlugins({
|
const beforeMiddlewares = await api.applyPlugins({
|
||||||
key: 'addBeforeMiddlewares',
|
key: 'addBeforeMiddlewares',
|
||||||
@ -166,25 +151,16 @@ export default (api) => {
|
|||||||
initialValue: [],
|
initialValue: [],
|
||||||
args: {}
|
args: {}
|
||||||
});
|
});
|
||||||
|
server = startDevServer({
|
||||||
server = new Server({
|
webpackConfig: bundleConfig,
|
||||||
...opts,
|
host: hostname,
|
||||||
compress: true,
|
port,
|
||||||
https: !!isHTTPS,
|
|
||||||
headers: {
|
|
||||||
'access-control-allow-origin': '*'
|
|
||||||
},
|
|
||||||
proxy: api.config.proxy,
|
proxy: api.config.proxy,
|
||||||
beforeMiddlewares,
|
beforeMiddlewares,
|
||||||
afterMiddlewares: [...middlewares],
|
afterMiddlewares: [...middlewares],
|
||||||
...(api.config.devServer || {})
|
customerDevServerConfig: api.config.devServer
|
||||||
});
|
|
||||||
const listenRet = await server.listen({
|
|
||||||
port,
|
|
||||||
hostname
|
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
...listenRet,
|
|
||||||
destroy
|
destroy
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
// css less post-css mini-css css 压缩
|
||||||
|
// extraPostCSSPlugins
|
||||||
|
// postcssLoader
|
||||||
|
// lessLoader
|
||||||
|
// css-loader
|
||||||
|
// 支持 热加载
|
||||||
|
// 性能优化
|
||||||
|
// css 压缩 https://github.com/webpack-contrib/css-minimizer-webpack-plugin
|
||||||
|
// 根据 entry 进行代码块拆分
|
||||||
|
// 根据 entry 将文件输出到不同的文件夹
|
||||||
|
|
||||||
|
import deepmerge from 'deepmerge';
|
||||||
|
|
||||||
|
function createRules({
|
||||||
|
isDev,
|
||||||
|
webpackConfig,
|
||||||
|
config,
|
||||||
|
lang,
|
||||||
|
test,
|
||||||
|
loader,
|
||||||
|
options,
|
||||||
|
browserslist
|
||||||
|
}) {
|
||||||
|
const rule = webpackConfig.module.rule(lang).test(test);
|
||||||
|
|
||||||
|
if (isDev) {
|
||||||
|
rule.use('extra-css-loader')
|
||||||
|
.loader(require.resolve('style-loader'))
|
||||||
|
.options({
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rule.use('extra-css-loader')
|
||||||
|
.loader(require('mini-css-extract-plugin').loader)
|
||||||
|
.options({
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rule.use('css-loader')
|
||||||
|
.loader(require.resolve('css-loader'))
|
||||||
|
.options({
|
||||||
|
...config.cssLoader
|
||||||
|
});
|
||||||
|
|
||||||
|
rule.use('postcss-loader')
|
||||||
|
.loader(require.resolve('postcss-loader'))
|
||||||
|
.options(deepmerge({
|
||||||
|
postcssOptions: () => ({
|
||||||
|
plugins: [
|
||||||
|
// https://github.com/luisrudge/postcss-flexbugs-fixes
|
||||||
|
require('postcss-flexbugs-fixes'),
|
||||||
|
require('postcss-safe-parser'),
|
||||||
|
[require('autoprefixer'), { ...config.autoprefixer, overrideBrowserslist: browserslist }],
|
||||||
|
...(config.extraPostCSSPlugins ? config.extraPostCSSPlugins : [])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}, config.postcssLoader || {}));
|
||||||
|
|
||||||
|
if (loader) {
|
||||||
|
rule.use(loader)
|
||||||
|
.loader(require.resolve(loader))
|
||||||
|
.options(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function createCssWebpackConfig({
|
||||||
|
isDev,
|
||||||
|
config,
|
||||||
|
webpackConfig,
|
||||||
|
browserslist
|
||||||
|
}) {
|
||||||
|
createRules({
|
||||||
|
isDev,
|
||||||
|
webpackConfig,
|
||||||
|
config,
|
||||||
|
lang: 'css',
|
||||||
|
test: /\.css$/,
|
||||||
|
browserslist
|
||||||
|
});
|
||||||
|
|
||||||
|
createRules({
|
||||||
|
isDev,
|
||||||
|
webpackConfig,
|
||||||
|
config,
|
||||||
|
lang: 'less',
|
||||||
|
test: /\.less$/,
|
||||||
|
loader: 'less-loader',
|
||||||
|
options: config.lessLoader || {},
|
||||||
|
browserslist
|
||||||
|
});
|
||||||
|
|
||||||
|
webpackConfig.plugin('extra-css')
|
||||||
|
.use(require.resolve('mini-css-extract-plugin'), [{
|
||||||
|
filename: isDev ? '[name].css' : '[name].[contenthash:8].css',
|
||||||
|
chunkFilename: isDev ? '[id].css' : '[id].[contenthash:8].css'
|
||||||
|
}]);
|
||||||
|
|
||||||
|
if (!isDev) {
|
||||||
|
webpackConfig.optimization
|
||||||
|
.minimizer('css')
|
||||||
|
.use(require.resolve('css-minimizer-webpack-plugin'), [{
|
||||||
|
sourceMap: config.devtool !== false
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import webpack from 'webpack';
|
||||||
|
|
||||||
|
const prefixRE = /^FES_APP_/;
|
||||||
|
|
||||||
|
const ENV_SHOULD_PASS = ['NODE_ENV', 'HMR', 'SOCKET_SERVER', 'ERROR_OVERLAY'];
|
||||||
|
|
||||||
|
function resolveDefine(opts = {}) {
|
||||||
|
const env = {};
|
||||||
|
Object.keys(process.env).forEach((key) => {
|
||||||
|
if (prefixRE.test(key) || ENV_SHOULD_PASS.includes(key)) {
|
||||||
|
env[key] = process.env[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const key in env) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(env, key)) {
|
||||||
|
env[key] = JSON.stringify(env[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const define = {
|
||||||
|
__VUE_OPTIONS_API__: true,
|
||||||
|
__VUE_PROD_DEVTOOLS__: false,
|
||||||
|
...opts.define
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const key in define) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(define, key)) {
|
||||||
|
define[key] = JSON.stringify(opts.define[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'process.env': env,
|
||||||
|
...define
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function createDefineWebpackConfig({
|
||||||
|
config,
|
||||||
|
webpackConfig
|
||||||
|
}) {
|
||||||
|
webpackConfig.plugin('define')
|
||||||
|
.use(webpack.DefinePlugin, [
|
||||||
|
resolveDefine({ define: config.define })
|
||||||
|
]);
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
import {
|
||||||
|
winPath
|
||||||
|
} from '@umijs/utils';
|
||||||
|
|
||||||
|
function getBabelOpts({
|
||||||
|
cwd,
|
||||||
|
targets,
|
||||||
|
presetOpts
|
||||||
|
}) {
|
||||||
|
const presets = [
|
||||||
|
[
|
||||||
|
require.resolve('@babel/preset-env'),
|
||||||
|
{
|
||||||
|
targets,
|
||||||
|
useBuiltIns: 'usage',
|
||||||
|
corejs: {
|
||||||
|
version: 3,
|
||||||
|
proposals: true
|
||||||
|
},
|
||||||
|
modules: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
];
|
||||||
|
const plugins = [
|
||||||
|
require('@babel/plugin-proposal-export-default-from').default,
|
||||||
|
[
|
||||||
|
require('@babel/plugin-proposal-pipeline-operator').default,
|
||||||
|
{
|
||||||
|
proposal: 'minimal'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
require('@babel/plugin-proposal-do-expressions').default,
|
||||||
|
require('@babel/plugin-proposal-function-bind').default,
|
||||||
|
[
|
||||||
|
require.resolve('@babel/plugin-transform-runtime'),
|
||||||
|
{
|
||||||
|
useESModules: true,
|
||||||
|
...presetOpts.transformRuntime
|
||||||
|
}
|
||||||
|
],
|
||||||
|
...(presetOpts.import
|
||||||
|
? presetOpts.import.map(importOpts => [
|
||||||
|
require.resolve('babel-plugin-import'),
|
||||||
|
importOpts,
|
||||||
|
importOpts.libraryName
|
||||||
|
])
|
||||||
|
: []),
|
||||||
|
require.resolve('@vue/babel-plugin-jsx')
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
babelrc: false,
|
||||||
|
cacheDirectory: process.env.BABEL_CACHE !== 'none' ? winPath(`${cwd}/.cache/babel-loader`) : false,
|
||||||
|
presets,
|
||||||
|
plugins,
|
||||||
|
overrides: [{
|
||||||
|
test: [/[\\/]node_modules[\\/]/, /\.fes/],
|
||||||
|
sourceType: 'unambiguous'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
cwd,
|
||||||
|
modifyBabelOpts,
|
||||||
|
modifyBabelPresetOpts,
|
||||||
|
targets
|
||||||
|
}) => {
|
||||||
|
let presetOpts = {
|
||||||
|
transformRuntime: {}
|
||||||
|
};
|
||||||
|
if (modifyBabelPresetOpts) {
|
||||||
|
presetOpts = await modifyBabelPresetOpts(presetOpts);
|
||||||
|
}
|
||||||
|
let babelOpts = getBabelOpts({
|
||||||
|
cwd,
|
||||||
|
presetOpts,
|
||||||
|
targets
|
||||||
|
});
|
||||||
|
if (modifyBabelOpts) {
|
||||||
|
babelOpts = await modifyBabelOpts(babelOpts);
|
||||||
|
}
|
||||||
|
return babelOpts;
|
||||||
|
};
|
@ -0,0 +1,305 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import Config from 'webpack-chain';
|
||||||
|
import webpack from 'webpack';
|
||||||
|
import createCssWebpackConfig from './css';
|
||||||
|
import getBableOpts from './getBableOpts';
|
||||||
|
import createVueWebpackConfig from './vue';
|
||||||
|
import createDefineWebpackConfig from './define';
|
||||||
|
import createMinimizerWebpackConfig from './minimizer';
|
||||||
|
import createHtmlWebpackConfig from './html';
|
||||||
|
|
||||||
|
function getTargetsAndBrowsersList({ config }) {
|
||||||
|
let targets = config.targets || {};
|
||||||
|
|
||||||
|
targets = Object.keys(targets)
|
||||||
|
.filter(key => targets[key] !== false)
|
||||||
|
.reduce((memo, key) => {
|
||||||
|
memo[key] = targets[key];
|
||||||
|
return memo;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const browserslist = targets.browsers
|
||||||
|
|| Object.keys(targets).map(key => `${key} >= ${targets[key] === true ? '0' : targets[key]}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
targets,
|
||||||
|
browserslist
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_EXCLUDE_NODE_MODULES = [
|
||||||
|
'vue',
|
||||||
|
'vuex',
|
||||||
|
'vue-router',
|
||||||
|
'ant-design-vue',
|
||||||
|
'core-js',
|
||||||
|
'echarts',
|
||||||
|
'@babel/runtime',
|
||||||
|
'lodash',
|
||||||
|
'webpack-dev-server',
|
||||||
|
'ansi-html',
|
||||||
|
'html-entities'
|
||||||
|
];
|
||||||
|
|
||||||
|
function genTranspileDepRegex(exclude) {
|
||||||
|
exclude = exclude.concat(DEFAULT_EXCLUDE_NODE_MODULES);
|
||||||
|
const deps = exclude.map((dep) => {
|
||||||
|
if (typeof dep === 'string') {
|
||||||
|
const depPath = join('node_modules', dep, '/');
|
||||||
|
return /^win/.test(require('os').platform()) ? depPath.replace(/\\/g, '\\\\') : depPath;
|
||||||
|
} if (dep instanceof RegExp) {
|
||||||
|
return dep.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('exclude only accepts an array of string or regular expressions');
|
||||||
|
});
|
||||||
|
return deps.length ? new RegExp(deps.join('|')) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default async function getConfig({
|
||||||
|
cwd,
|
||||||
|
config,
|
||||||
|
env,
|
||||||
|
entry = {},
|
||||||
|
modifyBabelOpts,
|
||||||
|
modifyBabelPresetOpts,
|
||||||
|
chainWebpack,
|
||||||
|
headScripts
|
||||||
|
}) {
|
||||||
|
const isDev = env === 'development';
|
||||||
|
const isProd = env === 'production';
|
||||||
|
const webpackConfig = new Config();
|
||||||
|
const absoluteOutput = join(cwd, config.outputPath || 'dist');
|
||||||
|
|
||||||
|
webpackConfig.mode(env);
|
||||||
|
webpackConfig.stats('verbose');
|
||||||
|
webpackConfig.externals(config.externals || {});
|
||||||
|
webpackConfig.devtool(isDev ? (config.devtool || 'cheap-module-source-map') : config.devtool);
|
||||||
|
|
||||||
|
// --------------- entry -----------
|
||||||
|
// Feature 公共模块 vue vue-router 处理 dependOn ?
|
||||||
|
Object.keys(entry).forEach((key) => {
|
||||||
|
webpackConfig.entry(key).add(entry[key]).end();
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------- output -----------
|
||||||
|
webpackConfig.output
|
||||||
|
.path(absoluteOutput)
|
||||||
|
.publicPath(config.publicPath || '')
|
||||||
|
.filename('[name].[contenthash:8].js')
|
||||||
|
.chunkFilename('[name].[contenthash:8].chunk.js');
|
||||||
|
|
||||||
|
// --------------- resolve -----------
|
||||||
|
webpackConfig.resolve.extensions.merge(['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm']);
|
||||||
|
|
||||||
|
if (config.alias) {
|
||||||
|
Object.keys(config.alias).forEach((key) => {
|
||||||
|
webpackConfig.resolve.alias
|
||||||
|
.set(key, config.alias[key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------- module -----------
|
||||||
|
webpackConfig.module
|
||||||
|
.rule('image')
|
||||||
|
.test(/\.(png|jpe?g|gif|webp|ico)(\?.*)?$/)
|
||||||
|
.use('url-loader')
|
||||||
|
.loader(require.resolve('url-loader'))
|
||||||
|
.options({
|
||||||
|
limit: config.inlineLimit || 8192,
|
||||||
|
esModule: false,
|
||||||
|
fallback: {
|
||||||
|
loader: require.resolve('file-loader'),
|
||||||
|
options: {
|
||||||
|
name: 'static/[name].[hash:8].[ext]',
|
||||||
|
esModule: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
webpackConfig.module
|
||||||
|
.rule('svg')
|
||||||
|
.test(/\.(svg)(\?.*)?$/)
|
||||||
|
.use('file-loader')
|
||||||
|
.loader(require.resolve('file-loader'))
|
||||||
|
.options({
|
||||||
|
name: 'static/[name].[hash:8].[ext]',
|
||||||
|
esModule: false
|
||||||
|
});
|
||||||
|
|
||||||
|
webpackConfig.module
|
||||||
|
.rule('fonts')
|
||||||
|
.test(/\.(eot|woff|woff2|ttf)(\?.*)?$/)
|
||||||
|
.use('file-loader')
|
||||||
|
.loader(require.resolve('file-loader'))
|
||||||
|
.options({
|
||||||
|
name: 'static/[name].[hash:8].[ext]',
|
||||||
|
esModule: false
|
||||||
|
});
|
||||||
|
|
||||||
|
webpackConfig.module
|
||||||
|
.rule('raw')
|
||||||
|
.test(/\.(txt|text|md)$/)
|
||||||
|
.use('raw-loader')
|
||||||
|
.loader(require.resolve('raw-loader'))
|
||||||
|
.options({
|
||||||
|
esModule: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { targets, browserslist } = getTargetsAndBrowsersList({ config });
|
||||||
|
const babelOpts = await getBableOpts({
|
||||||
|
cwd,
|
||||||
|
modifyBabelOpts,
|
||||||
|
modifyBabelPresetOpts,
|
||||||
|
targets
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------- js -----------
|
||||||
|
webpackConfig.module
|
||||||
|
.rule('js')
|
||||||
|
.test(/\.(js|mjs|jsx)$/)
|
||||||
|
.exclude.add((filepath) => {
|
||||||
|
// always transpile js in vue files
|
||||||
|
if (/\.vue\.jsx?$/.test(filepath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Don't transpile node_modules
|
||||||
|
return /node_modules/.test(filepath);
|
||||||
|
}).end()
|
||||||
|
.use('babel-loader')
|
||||||
|
.loader(require.resolve('babel-loader'))
|
||||||
|
.options(babelOpts);
|
||||||
|
|
||||||
|
// 为了避免第三方依赖包编译不充分导致线上问题,默认对 node_modules 也进行全编译
|
||||||
|
const transpileDepRegex = genTranspileDepRegex(config.nodeModulesTransform.exclude);
|
||||||
|
webpackConfig.module
|
||||||
|
.rule('js-in-node_modules')
|
||||||
|
.test(/\.(js|mjs)$/)
|
||||||
|
.include.add(/node_modules/).end()
|
||||||
|
.exclude.add((filepath) => {
|
||||||
|
if (transpileDepRegex && transpileDepRegex.test(filepath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}).end()
|
||||||
|
.use('babel-loader')
|
||||||
|
.loader(require.resolve('babel-loader'))
|
||||||
|
.options(babelOpts);
|
||||||
|
|
||||||
|
// --------------- css -----------
|
||||||
|
createCssWebpackConfig({
|
||||||
|
isDev,
|
||||||
|
config,
|
||||||
|
webpackConfig,
|
||||||
|
browserslist
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------- vue -----------
|
||||||
|
createVueWebpackConfig({
|
||||||
|
config,
|
||||||
|
webpackConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------- html -----------
|
||||||
|
const { publicCopyIgnore } = await createHtmlWebpackConfig({
|
||||||
|
cwd,
|
||||||
|
config,
|
||||||
|
webpackConfig,
|
||||||
|
headScripts,
|
||||||
|
isProd
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------- copy -----------
|
||||||
|
const copyPatterns = [existsSync(join(cwd, 'public')) && {
|
||||||
|
from: join(cwd, 'public'),
|
||||||
|
filter: (resourcePath) => {
|
||||||
|
if (resourcePath.indexOf('.DS_Store') !== -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (publicCopyIgnore.includes(resourcePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
to: absoluteOutput
|
||||||
|
}, ...((config.copy || []).map((item) => {
|
||||||
|
if (typeof item === 'string') {
|
||||||
|
return {
|
||||||
|
from: join(cwd, item.from),
|
||||||
|
to: absoluteOutput
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
from: join(cwd, item.from),
|
||||||
|
to: join(absoluteOutput, item.to)
|
||||||
|
};
|
||||||
|
}))].filter(Boolean);
|
||||||
|
// const publicCopyIgnore = ['.DS_Store'];
|
||||||
|
webpackConfig
|
||||||
|
.plugin('copy')
|
||||||
|
.use(require.resolve('copy-webpack-plugin'), [{
|
||||||
|
patterns: copyPatterns
|
||||||
|
}]);
|
||||||
|
|
||||||
|
// --------------- define -----------
|
||||||
|
createDefineWebpackConfig({
|
||||||
|
config,
|
||||||
|
webpackConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------- 分包 -----------
|
||||||
|
if (isProd) {
|
||||||
|
webpackConfig.optimization.splitChunks({
|
||||||
|
cacheGroups: {
|
||||||
|
defaultVendors: {
|
||||||
|
name: 'chunk-vendors',
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
priority: -10,
|
||||||
|
chunks: 'initial'
|
||||||
|
},
|
||||||
|
common: {
|
||||||
|
name: 'chunk-common',
|
||||||
|
minChunks: 2,
|
||||||
|
priority: -20,
|
||||||
|
chunks: 'initial',
|
||||||
|
reuseExistingChunk: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------- 压缩 -----------
|
||||||
|
createMinimizerWebpackConfig({
|
||||||
|
isProd,
|
||||||
|
config,
|
||||||
|
webpackConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------- 构建输出 ----------
|
||||||
|
webpackConfig
|
||||||
|
.plugin('progress')
|
||||||
|
.use(require.resolve('webpackbar'));
|
||||||
|
|
||||||
|
webpackConfig
|
||||||
|
.plugin('friendly-errors')
|
||||||
|
.use(require('@soda/friendly-errors-webpack-plugin'));
|
||||||
|
|
||||||
|
// --------------- chainwebpack -----------
|
||||||
|
if (chainWebpack) {
|
||||||
|
await chainWebpack(webpackConfig, {
|
||||||
|
webpack
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 用户配置的 chainWebpack 优先级最高
|
||||||
|
if (config.chainWebpack) {
|
||||||
|
await config.chainWebpack(webpackConfig, {
|
||||||
|
env,
|
||||||
|
webpack
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return webpackConfig.toConfig();
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import { join, resolve } from 'path';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
|
||||||
|
export default async function createHtmlWebpackConfig({
|
||||||
|
cwd,
|
||||||
|
config,
|
||||||
|
webpackConfig,
|
||||||
|
headScripts,
|
||||||
|
isProd
|
||||||
|
}) {
|
||||||
|
const htmlOptions = {
|
||||||
|
filename: '[name].html',
|
||||||
|
...config.html.options
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (isProd) {
|
||||||
|
Object.assign(htmlOptions, {
|
||||||
|
minify: {
|
||||||
|
removeComments: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
removeScriptTypeAttributes: true
|
||||||
|
// more options:
|
||||||
|
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const multiPageConfig = config.html.pages;
|
||||||
|
const htmlPath = join(cwd, 'public/index.html');
|
||||||
|
const defaultHtmlPath = resolve(__dirname, 'index-default.html');
|
||||||
|
const publicCopyIgnore = [];
|
||||||
|
|
||||||
|
if (!multiPageConfig) {
|
||||||
|
// default, single page setup.
|
||||||
|
htmlOptions.template = existsSync(htmlPath)
|
||||||
|
? htmlPath
|
||||||
|
: defaultHtmlPath;
|
||||||
|
|
||||||
|
publicCopyIgnore.push(htmlOptions.template);
|
||||||
|
webpackConfig
|
||||||
|
.plugin('html')
|
||||||
|
.use(require.resolve('html-webpack-plugin'), [htmlOptions]);
|
||||||
|
} else {
|
||||||
|
// TODO 支持多页
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headScripts) {
|
||||||
|
const headScriptsMap = await headScripts();
|
||||||
|
webpackConfig
|
||||||
|
.plugin('html-tags')
|
||||||
|
.use(require.resolve('html-webpack-tags-plugin'), [{
|
||||||
|
append: false,
|
||||||
|
scripts: headScriptsMap.map(script => ({
|
||||||
|
path: script.src
|
||||||
|
}))
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
publicCopyIgnore
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import deepmerge from 'deepmerge';
|
||||||
|
|
||||||
|
const defaultTerserOptions = {
|
||||||
|
compress: {
|
||||||
|
// turn off flags with small gains to speed up minification
|
||||||
|
arrows: false,
|
||||||
|
collapse_vars: false, // 0.3kb
|
||||||
|
comparisons: false,
|
||||||
|
computed_props: false,
|
||||||
|
hoist_funs: false,
|
||||||
|
hoist_props: false,
|
||||||
|
hoist_vars: false,
|
||||||
|
inline: false,
|
||||||
|
loops: false,
|
||||||
|
negate_iife: false,
|
||||||
|
properties: false,
|
||||||
|
reduce_funcs: false,
|
||||||
|
reduce_vars: false,
|
||||||
|
switches: false,
|
||||||
|
toplevel: false,
|
||||||
|
typeofs: false,
|
||||||
|
|
||||||
|
// a few flags with noticeable gains/speed ratio
|
||||||
|
// numbers based on out of the box vendor bundle
|
||||||
|
booleans: true, // 0.7kb
|
||||||
|
if_return: true, // 0.4kb
|
||||||
|
sequences: true, // 0.7kb
|
||||||
|
unused: true, // 2.3kb
|
||||||
|
|
||||||
|
// required features to drop conditional branches
|
||||||
|
conditionals: true,
|
||||||
|
dead_code: true,
|
||||||
|
evaluate: true
|
||||||
|
},
|
||||||
|
mangle: {
|
||||||
|
safari10: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const terserOptions = config => ({
|
||||||
|
terserOptions: deepmerge(
|
||||||
|
defaultTerserOptions,
|
||||||
|
config.terserOptions || {}
|
||||||
|
),
|
||||||
|
extractComments: false
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default function createMinimizerWebpackConfig({
|
||||||
|
isProd,
|
||||||
|
config,
|
||||||
|
webpackConfig
|
||||||
|
}) {
|
||||||
|
if (isProd) {
|
||||||
|
webpackConfig.optimization
|
||||||
|
.minimizer('terser')
|
||||||
|
.use(require.resolve('terser-webpack-plugin'), [terserOptions(config)]);
|
||||||
|
}
|
||||||
|
if (process.env.FES_ENV === 'test') {
|
||||||
|
webpackConfig.optimization.minimize(false);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
// import webpack from 'webpack';
|
||||||
|
|
||||||
|
export default function createVueWebpackConfig({
|
||||||
|
config,
|
||||||
|
webpackConfig
|
||||||
|
}) {
|
||||||
|
webpackConfig.module
|
||||||
|
.rule('vue')
|
||||||
|
.test(/\.vue$/)
|
||||||
|
.use('vue-loader')
|
||||||
|
.loader(require.resolve('vue-loader'))
|
||||||
|
.options({
|
||||||
|
babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy'],
|
||||||
|
...(config.vueLoader || {})
|
||||||
|
})
|
||||||
|
.end();
|
||||||
|
|
||||||
|
webpackConfig
|
||||||
|
.plugin('vue-loader')
|
||||||
|
.use(require('vue-loader').VueLoaderPlugin);
|
||||||
|
|
||||||
|
// webpackConfig
|
||||||
|
// .plugin('feature-flags')
|
||||||
|
// .use(webpack.DefinePlugin, [{
|
||||||
|
// __VUE_OPTIONS_API__: 'true',
|
||||||
|
// __VUE_PROD_DEVTOOLS__: 'false'
|
||||||
|
// }]);
|
||||||
|
}
|
@ -1,8 +1,5 @@
|
|||||||
import { dirname } from 'path';
|
|
||||||
import { winPath, resolve } from '@umijs/utils';
|
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
const { paths, pkg, cwd } = api;
|
const { paths } = api;
|
||||||
|
|
||||||
api.describe({
|
api.describe({
|
||||||
key: 'alias',
|
key: 'alias',
|
||||||
@ -15,50 +12,11 @@ export default (api) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function getUserLibDir({ library }) {
|
|
||||||
if (
|
|
||||||
(pkg.dependencies && pkg.dependencies[library])
|
|
||||||
|| (pkg.devDependencies && pkg.devDependencies[library])
|
|
||||||
// egg project using `clientDependencies` in ali tnpm
|
|
||||||
|| (pkg.clientDependencies && pkg.clientDependencies[library])
|
|
||||||
) {
|
|
||||||
return winPath(
|
|
||||||
dirname(
|
|
||||||
// 通过 resolve 往上找,可支持 lerna 仓库
|
|
||||||
// lerna 仓库如果用 yarn workspace 的依赖不一定在 node_modules,可能被提到根目录,并且没有 link
|
|
||||||
resolve.sync(`${library}/package.json`, {
|
|
||||||
basedir: cwd
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 另一种实现方式:
|
|
||||||
// 提供 projectFirstLibraries 的配置方式,但是不通用,先放插件层实现
|
|
||||||
api.chainWebpack(async (memo) => {
|
api.chainWebpack(async (memo) => {
|
||||||
const libraries = await api.applyPlugins({
|
|
||||||
key: 'addProjectFirstLibraries',
|
|
||||||
type: api.ApplyPluginsType.add,
|
|
||||||
initialValue: [
|
|
||||||
]
|
|
||||||
});
|
|
||||||
libraries.forEach((library) => {
|
|
||||||
memo.resolve.alias.set(
|
|
||||||
library.name,
|
|
||||||
getUserLibDir({ library: library.name }) || library.path
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 选择在 chainWebpack 中进行以上 alias 的初始化,是为了支持用户使用 modifyPaths API 对 paths 进行改写
|
// 选择在 chainWebpack 中进行以上 alias 的初始化,是为了支持用户使用 modifyPaths API 对 paths 进行改写
|
||||||
memo.resolve.alias.set('@', paths.absSrcPath);
|
memo.resolve.alias.set('@', paths.absSrcPath);
|
||||||
memo.resolve.alias.set('@@', paths.absTmpPath);
|
memo.resolve.alias.set('@@', paths.absTmpPath);
|
||||||
|
|
||||||
Object.keys(api.config.alias).forEach((key) => {
|
|
||||||
memo.resolve.alias.set(key, api.config.alias[key]);
|
|
||||||
});
|
|
||||||
|
|
||||||
return memo;
|
return memo;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -28,15 +28,15 @@ export default (api) => {
|
|||||||
defaultSizes: 'parsed' // stat // gzip
|
defaultSizes: 'parsed' // stat // gzip
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enableBy: () => !!(process.env.ANALYZE || process.env.ANALYZE_SSR)
|
enableBy: () => !!process.env.ANALYZE
|
||||||
});
|
});
|
||||||
api.chainWebpack((webpackConfig, opts) => {
|
api.chainWebpack((webpackConfig, opts) => {
|
||||||
const { type } = opts;
|
const { type } = opts;
|
||||||
if (type === 'csr' && !process.env.ANALYZE_SSR) {
|
if (type === 'csr') {
|
||||||
webpackConfig
|
webpackConfig
|
||||||
.plugin('bundle-analyzer')
|
.plugin('bundle-analyzer')
|
||||||
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [
|
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [
|
||||||
api.config?.analyze || {}
|
api.config?.analyze || {}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return webpackConfig;
|
return webpackConfig;
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import {
|
|
||||||
winPath
|
|
||||||
} from '@umijs/utils';
|
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
api.describe({
|
api.describe({
|
||||||
key: 'chainWebpack',
|
key: 'chainWebpack',
|
||||||
@ -11,39 +7,4 @@ export default (api) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
api.chainWebpack((webpackConfig) => {
|
|
||||||
const cwd = api.cwd;
|
|
||||||
// 添加 .vue 后缀
|
|
||||||
webpackConfig.resolve.extensions.merge([
|
|
||||||
'.vue'
|
|
||||||
]);
|
|
||||||
webpackConfig.module
|
|
||||||
.rule('js-in-node_modules').use('babel-loader').tap((options) => {
|
|
||||||
options.cacheDirectory = winPath(`${cwd}/.cache/babel-loader`);
|
|
||||||
return options;
|
|
||||||
});
|
|
||||||
if (api.env !== 'development') {
|
|
||||||
webpackConfig
|
|
||||||
.optimization.splitChunks({
|
|
||||||
cacheGroups: {
|
|
||||||
vendors: {
|
|
||||||
name: 'chunk-vendors',
|
|
||||||
test: /[\\/]node_modules[\\/]/,
|
|
||||||
priority: -10,
|
|
||||||
chunks: 'initial'
|
|
||||||
},
|
|
||||||
common: {
|
|
||||||
name: 'chunk-common',
|
|
||||||
minChunks: 2,
|
|
||||||
priority: -20,
|
|
||||||
chunks: 'initial',
|
|
||||||
reuseExistingChunk: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return webpackConfig;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
export default (api) => {
|
|
||||||
api.describe({
|
|
||||||
key: 'chunks',
|
|
||||||
config: {
|
|
||||||
schema(joi) {
|
|
||||||
return joi.array().items(joi.string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -10,8 +10,8 @@ export default (api) => {
|
|||||||
from: joi.string(),
|
from: joi.string(),
|
||||||
to: joi.string()
|
to: joi.string()
|
||||||
}),
|
}),
|
||||||
joi.string(),
|
joi.string()
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ export default (api) => {
|
|||||||
api.describe({
|
api.describe({
|
||||||
key: 'cssLoader',
|
key: 'cssLoader',
|
||||||
config: {
|
config: {
|
||||||
|
default: {},
|
||||||
schema(joi) {
|
schema(joi) {
|
||||||
return joi
|
return joi
|
||||||
.object({
|
.object({
|
||||||
@ -11,7 +12,7 @@ export default (api) => {
|
|||||||
modules: joi.alternatives(
|
modules: joi.alternatives(
|
||||||
joi.boolean(),
|
joi.boolean(),
|
||||||
joi.string(),
|
joi.string(),
|
||||||
joi.object(),
|
joi.object()
|
||||||
),
|
),
|
||||||
sourceMap: joi.boolean(),
|
sourceMap: joi.boolean(),
|
||||||
importLoaders: joi.number(),
|
importLoaders: joi.number(),
|
||||||
@ -24,11 +25,11 @@ export default (api) => {
|
|||||||
'camelCase',
|
'camelCase',
|
||||||
'camelCaseOnly',
|
'camelCaseOnly',
|
||||||
'dashes',
|
'dashes',
|
||||||
'dashesOnly',
|
'dashesOnly'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.description(
|
.description(
|
||||||
'more css-loader options see https://webpack.js.org/loaders/css-loader/#options',
|
'more css-loader options see https://webpack.js.org/loaders/css-loader/#options'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
export default (api) => {
|
|
||||||
api.describe({
|
|
||||||
// https://cssnano.co/optimisations/
|
|
||||||
key: 'cssnano',
|
|
||||||
config: {
|
|
||||||
default: {
|
|
||||||
mergeRules: false,
|
|
||||||
minifyFontValues: { removeQuotes: false }
|
|
||||||
},
|
|
||||||
schema(joi) {
|
|
||||||
return joi.object();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -7,8 +7,6 @@ export default (api) => {
|
|||||||
return joi.object();
|
return joi.object();
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
__VUE_OPTIONS_API__: true,
|
|
||||||
__VUE_PROD_DEVTOOLS__: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
import {
|
|
||||||
readFileSync
|
|
||||||
} from 'fs';
|
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
api.describe({
|
api.describe({
|
||||||
@ -14,100 +11,4 @@ export default (api) => {
|
|||||||
return api.env === 'development';
|
return api.env === 'development';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
api.addBeforeMiddlewares(() => (req, res, next) => {
|
|
||||||
if (req.path.includes('@@/devScripts.js')) {
|
|
||||||
api.applyPlugins({
|
|
||||||
key: 'addDevScripts',
|
|
||||||
type: api.ApplyPluginsType.add,
|
|
||||||
initialValue: process.env.HMR !== 'none'
|
|
||||||
? [
|
|
||||||
readFileSync(
|
|
||||||
require.resolve(
|
|
||||||
'@umijs/bundler-webpack/bundled/webpackHotDevClient'
|
|
||||||
),
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
: []
|
|
||||||
}).then((scripts) => {
|
|
||||||
res.end(
|
|
||||||
scripts
|
|
||||||
.join('\r\n\r\n')
|
|
||||||
.replace(
|
|
||||||
/{}.SOCKET_SERVER/g,
|
|
||||||
JSON.stringify(process.env.SOCKET_SERVER || '')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
api.addHTMLHeadScripts(() => [{
|
|
||||||
src: '@@/devScripts.js'
|
|
||||||
}]);
|
|
||||||
|
|
||||||
api.onGenerateFiles(() => {
|
|
||||||
api.writeTmpFile({
|
|
||||||
path: './core/devScripts.js',
|
|
||||||
content: process.env.HMR !== 'none'
|
|
||||||
? `
|
|
||||||
if (window.g_initWebpackHotDevClient) {
|
|
||||||
function tryApplyUpdates(onHotUpdateSuccess) {
|
|
||||||
// @ts-ignore
|
|
||||||
if (!module.hot) {
|
|
||||||
window.location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUpdateAvailable() {
|
|
||||||
// @ts-ignore
|
|
||||||
return window.g_getMostRecentCompilationHash() !== __webpack_hash__;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is update available?
|
|
||||||
// @ts-ignore
|
|
||||||
if (!isUpdateAvailable() || module.hot.status() !== 'idle') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleApplyUpdates(err, updatedModules) {
|
|
||||||
if (err || !updatedModules || window.g_getHadRuntimeError()) {
|
|
||||||
window.location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onHotUpdateSuccess && onHotUpdateSuccess();
|
|
||||||
|
|
||||||
if (isUpdateAvailable()) {
|
|
||||||
// While we were updating, there was a new update! Do it again.
|
|
||||||
tryApplyUpdates();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
module.hot.check(true).then(
|
|
||||||
function (updatedModules) {
|
|
||||||
handleApplyUpdates(null, updatedModules);
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
handleApplyUpdates(err, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.g_initWebpackHotDevClient({
|
|
||||||
tryApplyUpdates,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
`
|
|
||||||
: ''
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
api.addEntryImportsAhead(() => [{
|
|
||||||
source: '@@/core/devScripts'
|
|
||||||
}]);
|
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { winPath } from '@umijs/utils';
|
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
api.describe({
|
api.describe({
|
||||||
@ -9,13 +8,4 @@ export default (api) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
api.modifyBabelOpts((babelOpts) => {
|
|
||||||
babelOpts.cacheDirectory = process.env.BABEL_CACHE !== 'none'
|
|
||||||
? winPath(`${api.cwd}/.cache/babel-loader`)
|
|
||||||
: false;
|
|
||||||
babelOpts.plugins.push(require.resolve('@vue/babel-plugin-jsx'));
|
|
||||||
|
|
||||||
return babelOpts;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -8,16 +8,4 @@ export default (api) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
api.modifyBabelPresetOpts(opts => Object.assign({}, opts, {
|
|
||||||
typescript: false,
|
|
||||||
env: {
|
|
||||||
useBuiltIns: 'entry',
|
|
||||||
corejs: 3,
|
|
||||||
modules: false
|
|
||||||
},
|
|
||||||
react: false,
|
|
||||||
reactRemovePropTypes: false,
|
|
||||||
reactRequire: false,
|
|
||||||
svgr: false
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {
|
// import {
|
||||||
winPath
|
// winPath
|
||||||
} from '@umijs/utils';
|
// } from '@umijs/utils';
|
||||||
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
|
// import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
api.describe({
|
api.describe({
|
||||||
@ -13,31 +13,31 @@ export default (api) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
api.chainWebpack((webpackConfig) => {
|
// api.chainWebpack((webpackConfig) => {
|
||||||
const cwd = api.cwd;
|
// const cwd = api.cwd;
|
||||||
if (api.env === 'development') {
|
// if (api.env === 'development') {
|
||||||
webpackConfig
|
// webpackConfig
|
||||||
.plugin('hardSource')
|
// .plugin('hardSource')
|
||||||
.use(HardSourceWebpackPlugin, [{
|
// .use(HardSourceWebpackPlugin, [{
|
||||||
cacheDirectory: winPath(`${cwd}/.cache/hard-source/[confighash]`),
|
// cacheDirectory: winPath(`${cwd}/.cache/hard-source/[confighash]`),
|
||||||
...api.config.hardSource || {}
|
// ...api.config.hardSource || {}
|
||||||
}]);
|
// }]);
|
||||||
webpackConfig
|
// webpackConfig
|
||||||
.plugin('hardSourceExclude')
|
// .plugin('hardSourceExclude')
|
||||||
.use(HardSourceWebpackPlugin.ExcludeModulePlugin, [
|
// .use(HardSourceWebpackPlugin.ExcludeModulePlugin, [
|
||||||
[
|
// [
|
||||||
{
|
// {
|
||||||
// HardSource works with mini-css-extract-plugin but due to how
|
// // HardSource works with mini-css-extract-plugin but due to how
|
||||||
// mini-css emits assets, assets are not emitted on repeated builds with
|
// // mini-css emits assets, assets are not emitted on repeated builds with
|
||||||
// mini-css and hard-source together. Ignoring the mini-css loader
|
// // mini-css and hard-source together. Ignoring the mini-css loader
|
||||||
// modules, but not the other css loader modules, excludes the modules
|
// // modules, but not the other css loader modules, excludes the modules
|
||||||
// that mini-css needs rebuilt to output assets every time.
|
// // that mini-css needs rebuilt to output assets every time.
|
||||||
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
|
// test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return webpackConfig;
|
// return webpackConfig;
|
||||||
});
|
// });
|
||||||
};
|
};
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
export default (api) => {
|
|
||||||
api.describe({
|
|
||||||
key: 'hash',
|
|
||||||
config: {
|
|
||||||
default: true,
|
|
||||||
schema(joi) {
|
|
||||||
return joi.boolean();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,6 +1,3 @@
|
|||||||
import { resolve, join } from 'path';
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
|
|
||||||
export default (api) => {
|
export default (api) => {
|
||||||
api.describe({
|
api.describe({
|
||||||
key: 'html',
|
key: 'html',
|
||||||
@ -20,103 +17,4 @@ export default (api) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
api.chainWebpack(async (webpackConfig) => {
|
|
||||||
const isProd = api.env === 'production';
|
|
||||||
const htmlOptions = {
|
|
||||||
templateParameters: (compilation, assets, pluginOptions) => {
|
|
||||||
// enhance html-webpack-plugin's built in template params
|
|
||||||
let stats;
|
|
||||||
return {
|
|
||||||
// make stats lazy as it is expensive
|
|
||||||
get webpack() {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
return stats || (stats = compilation.getStats().toJson());
|
|
||||||
},
|
|
||||||
compilation,
|
|
||||||
webpackConfig: compilation.options,
|
|
||||||
htmlWebpackPlugin: {
|
|
||||||
files: assets,
|
|
||||||
options: pluginOptions
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
...api.config.html.options
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (isProd) {
|
|
||||||
Object.assign(htmlOptions, {
|
|
||||||
minify: {
|
|
||||||
removeComments: true,
|
|
||||||
collapseWhitespace: true,
|
|
||||||
collapseBooleanAttributes: true,
|
|
||||||
removeScriptTypeAttributes: true
|
|
||||||
// more options:
|
|
||||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const HTMLPlugin = require('html-webpack-plugin');
|
|
||||||
const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin');
|
|
||||||
const PreloadPlugin = require('@vue/preload-webpack-plugin');
|
|
||||||
const multiPageConfig = api.config.html.pages;
|
|
||||||
const htmlPath = join(api.paths.cwd, 'public/index.html');
|
|
||||||
const defaultHtmlPath = resolve(__dirname, 'index-default.html');
|
|
||||||
const publicCopyIgnore = ['.DS_Store'];
|
|
||||||
|
|
||||||
if (!multiPageConfig) {
|
|
||||||
// default, single page setup.
|
|
||||||
htmlOptions.template = existsSync(htmlPath)
|
|
||||||
? htmlPath
|
|
||||||
: defaultHtmlPath;
|
|
||||||
|
|
||||||
publicCopyIgnore.push({
|
|
||||||
glob: htmlOptions.template,
|
|
||||||
matchBase: false
|
|
||||||
});
|
|
||||||
|
|
||||||
webpackConfig
|
|
||||||
.plugin('html')
|
|
||||||
.use(HTMLPlugin, [htmlOptions]);
|
|
||||||
|
|
||||||
// TODO onlyHtml 将资源注入 html 中的逻辑
|
|
||||||
if (!htmlOptions.onlyHtml || htmlOptions.preload !== false) {
|
|
||||||
// inject preload/prefetch to HTML
|
|
||||||
webpackConfig
|
|
||||||
.plugin('preload')
|
|
||||||
.use(PreloadPlugin, [{
|
|
||||||
rel: 'preload',
|
|
||||||
include: 'initial',
|
|
||||||
fileBlacklist: [/\.map$/, /hot-update\.js$/]
|
|
||||||
}]);
|
|
||||||
|
|
||||||
webpackConfig
|
|
||||||
.plugin('prefetch')
|
|
||||||
.use(PreloadPlugin, [{
|
|
||||||
rel: 'prefetch',
|
|
||||||
include: 'asyncChunks'
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO 支持多页
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isProd) {
|
|
||||||
const headScripts = await api.applyPlugins({
|
|
||||||
key: 'addHTMLHeadScripts',
|
|
||||||
type: api.ApplyPluginsType.add,
|
|
||||||
initialState: []
|
|
||||||
});
|
|
||||||
webpackConfig
|
|
||||||
.plugin('html-tags')
|
|
||||||
.use(HtmlWebpackTagsPlugin, [{
|
|
||||||
append: false,
|
|
||||||
scripts: headScripts.map(script => ({
|
|
||||||
path: script.src
|
|
||||||
}))
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
// import ImageMinimizerPlugin from 'image-minimizer-webpack-plugin';
|
|
||||||
|
|
||||||
export default (api) => {
|
|
||||||
api.describe({
|
|
||||||
key: 'imageMinimizer',
|
|
||||||
config: {
|
|
||||||
schema(joi) {
|
|
||||||
return joi.object();
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
disable: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
api.chainWebpack((webpackConfig) => {
|
|
||||||
if (!api.config.imageMinimizer.disable && api.env === 'production') {
|
|
||||||
// TODO 图片压缩
|
|
||||||
}
|
|
||||||
|
|
||||||
return webpackConfig;
|
|
||||||
});
|
|
||||||
};
|
|
@ -4,12 +4,10 @@ export default (api) => {
|
|||||||
key: 'nodeModulesTransform',
|
key: 'nodeModulesTransform',
|
||||||
config: {
|
config: {
|
||||||
default: {
|
default: {
|
||||||
type: 'all',
|
|
||||||
exclude: []
|
exclude: []
|
||||||
},
|
},
|
||||||
schema(joi) {
|
schema(joi) {
|
||||||
return joi.object({
|
return joi.object({
|
||||||
type: joi.string().valid('all', 'none'),
|
|
||||||
exclude: joi.array().items(joi.string())
|
exclude: joi.array().items(joi.string())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
export default (api) => {
|
|
||||||
api.describe({
|
|
||||||
key: 'styleLoader',
|
|
||||||
config: {
|
|
||||||
schema(joi) {
|
|
||||||
return joi.object();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -4,7 +4,6 @@ export default (api) => {
|
|||||||
key: 'targets',
|
key: 'targets',
|
||||||
config: {
|
config: {
|
||||||
default: {
|
default: {
|
||||||
node: true,
|
|
||||||
chrome: 49,
|
chrome: 49,
|
||||||
firefox: 64,
|
firefox: 64,
|
||||||
safari: 10,
|
safari: 10,
|
||||||
|
@ -8,28 +8,9 @@ export default (api) => {
|
|||||||
return joi
|
return joi
|
||||||
.object({})
|
.object({})
|
||||||
.description(
|
.description(
|
||||||
'more vue-loader options see https://vue-loader.vuejs.org/',
|
'more vue-loader options see https://vue-loader.vuejs.org/'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
api.chainWebpack((webpackConfig) => {
|
|
||||||
// 添加 .vue 后缀
|
|
||||||
webpackConfig.module
|
|
||||||
.rule('vue')
|
|
||||||
.test(/\.vue$/)
|
|
||||||
.use('vue-loader')
|
|
||||||
.loader(require.resolve('vue-loader'))
|
|
||||||
.options({
|
|
||||||
babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy']
|
|
||||||
})
|
|
||||||
.end()
|
|
||||||
.end();
|
|
||||||
|
|
||||||
webpackConfig
|
|
||||||
.plugin('vue-loader')
|
|
||||||
.use(require('vue-loader').VueLoaderPlugin);
|
|
||||||
|
|
||||||
return webpackConfig;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,6 @@ export default function (api) {
|
|||||||
'modifyBundleImplementor',
|
'modifyBundleImplementor',
|
||||||
'modifyBundleConfigOpts',
|
'modifyBundleConfigOpts',
|
||||||
'modifyBundleConfig',
|
'modifyBundleConfig',
|
||||||
'modifyBundleConfigs',
|
|
||||||
'modifyBabelOpts',
|
'modifyBabelOpts',
|
||||||
'modifyBabelPresetOpts',
|
'modifyBabelPresetOpts',
|
||||||
'chainWebpack',
|
'chainWebpack',
|
||||||
|
@ -2,5 +2,5 @@ import { winPath } from '@umijs/utils';
|
|||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
|
|
||||||
export const runtimePath = winPath(
|
export const runtimePath = winPath(
|
||||||
dirname(require.resolve('@webank/fes-runtime/package.json')),
|
dirname(require.resolve('@webank/fes-runtime/package.json'))
|
||||||
);
|
);
|
||||||
|
@ -15,9 +15,6 @@ export default {
|
|||||||
title: '海贼王'
|
title: '海贼王'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
imageMinimizer: {
|
|
||||||
disable: false
|
|
||||||
},
|
|
||||||
extraPostCSSPlugins: [
|
extraPostCSSPlugins: [
|
||||||
pxtoviewport({
|
pxtoviewport({
|
||||||
unitToConvert: 'px',
|
unitToConvert: 'px',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="onepiece">
|
<div class="onepiece">
|
||||||
fes & 拉夫德鲁 <br />
|
fes & 拉夫德鲁<br />
|
||||||
<fes-icon @click="clickIcon" :spin="true" class="one-icon" type="smile" />
|
<fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" />
|
||||||
<div v-if="loading" class="loading">loading</div>
|
<div v-if="loading" class="loading">loading</div>
|
||||||
<div v-else class="data">{{data}}</div>
|
<div v-else class="data">{{data}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
# fes 模版
|
# fes 模版
|
||||||
|
|
||||||
内部测试用,不对外发布
|
内部测试用,不对外发布
|
||||||
|
|
||||||
|
|
||||||
|
## 环境变量
|
||||||
|
|
||||||
|
* 业务代码使用的全局变量,使用 webpack define 定义
|
||||||
|
* 针对不同的环境构建的变量
|
||||||
|
* 开发环境 .evn.local
|
||||||
|
* .env 定义环境变量
|
||||||
|
* .env.xxx 定义特定的环境变
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="haizekuo">
|
<div class="haizekuo">
|
||||||
<div>国际化: {{t("test")}}</div>
|
<div>国际化 {{t("test")}}</div>
|
||||||
fes & 拉夫德鲁 <br />
|
fes & 拉夫德鲁 <br />
|
||||||
<access :id="accessId"> accessOnepicess1 <input /> </access>
|
<access :id="accessId"> accessOnepicess1 <input /> </access>
|
||||||
<div v-access="accessId"> accessOnepicess2 <input /> </div>
|
<div v-access="accessId"> accessOnepicess2 <input /> </div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
cjs: { type: 'babel', lazy: true },
|
cjs: { type: 'babel', lazy: false },
|
||||||
esm: { type: 'rollup' },
|
esm: { type: 'rollup' },
|
||||||
disableTypeCheck: false,
|
disableTypeCheck: false,
|
||||||
extraExternals: ['@@/core/exports'],
|
extraExternals: ['@@/core/exports'],
|
||||||
|
@ -80,6 +80,7 @@ program
|
|||||||
.action(async () => {
|
.action(async () => {
|
||||||
try {
|
try {
|
||||||
process.env.NODE_ENV = 'production';
|
process.env.NODE_ENV = 'production';
|
||||||
|
process.env.FES_ENV = args.mode || '';
|
||||||
await new Service({
|
await new Service({
|
||||||
cwd: getCwd(),
|
cwd: getCwd(),
|
||||||
pkg: getPkg(process.cwd())
|
pkg: getPkg(process.cwd())
|
||||||
|
@ -24,6 +24,7 @@ function onSignal(signal, service) {
|
|||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
process.env.NODE_ENV = 'development';
|
process.env.NODE_ENV = 'development';
|
||||||
|
process.env.FES_ENV = args.mode || '';
|
||||||
const service = new Service({
|
const service = new Service({
|
||||||
cwd: getCwd(),
|
cwd: getCwd(),
|
||||||
pkg: getPkg(process.cwd())
|
pkg: getPkg(process.cwd())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user