feat: 优化构建

This commit is contained in:
winixt 2022-03-25 20:31:30 +08:00
parent 7cc8ea51eb
commit 242787c358
22 changed files with 5027 additions and 4661 deletions

View File

@ -40,7 +40,7 @@
"@vuepress/plugin-docsearch": "2.0.0-beta.28",
"@vuepress/plugin-pwa": "2.0.0-beta.28",
"@vuepress/plugin-pwa-popup": "2.0.0-beta.28",
"@webank/eslint-config-webank": "0.3.1",
"@webank/eslint-config-webank": "^1.2.0",
"chalk": "^4.1.2",
"chokidar": "^3.5.2",
"commitizen": "^4.2.1",
@ -56,7 +56,7 @@
},
"lint-staged": {
"*.{js,jsx,vue,ts}": [
"eslint --format=codeframe"
"eslint"
]
},
"husky": {

View File

@ -10,8 +10,8 @@ export default (api) => {
config: {
schema(joi) {
return joi.object();
}
}
},
},
});
const namespace = 'plugin-icon';
@ -24,34 +24,29 @@ export default (api) => {
api.onGenerateFiles(async () => {
const base = join(api.paths.absSrcPath, 'icons');
const iconFiles = api.utils.glob.sync('**/*', {
cwd: join(api.paths.absSrcPath, 'icons')
cwd: join(api.paths.absSrcPath, 'icons'),
});
const svgDatas = await optimizeSvg(iconFiles.map(item => join(base, item)));
const svgDatas = await optimizeSvg(iconFiles.map((item) => join(base, item)));
const iconNames = [];
const SVG_COMPONENT_TMPLATE = 'export default () => (SVG)';
for (const { fileName, data } of svgDatas) {
iconNames.push(basename(fileName, '.svg'));
api.writeTmpFile({
path: `${namespace}/icons/${basename(fileName, '.svg')}.js`,
content: SVG_COMPONENT_TMPLATE
.replace('SVG', data)
path: `${namespace}/icons/${basename(fileName, '.svg')}.jsx`,
content: SVG_COMPONENT_TMPLATE.replace('SVG', data),
});
}
api.writeTmpFile({
path: `${namespace}/icons.js`,
content: api.utils.Mustache.render(
readFileSync(join(__dirname, 'runtime/icons.tpl'), 'utf-8'),
{
ICON_NAMES: iconNames
}
)
content: api.utils.Mustache.render(readFileSync(join(__dirname, 'runtime/icons.tpl'), 'utf-8'), {
ICON_NAMES: iconNames,
}),
});
api.writeTmpFile({
path: absRuntimeFilePath,
content: api.utils.Mustache.render(readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8'), {
})
content: api.utils.Mustache.render(readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8'), {}),
});
if (!generatedOnce) {
@ -59,7 +54,7 @@ export default (api) => {
api.copyTmpFiles({
namespace,
path: join(__dirname, 'runtime'),
ignore: ['.tpl']
ignore: ['.tpl'],
});
}
});

View File

@ -1,9 +1,8 @@
<script>
import { computed } from 'vue';
// eslint-disable-next-line
import icons from '../icons';
const noop = () => { };
const noop = () => {};
export default {
name: 'FesIcon',
@ -17,25 +16,21 @@ export default {
}
return tabIndex;
});
const svgStyle = computed(() => (props.rotate
? {
msTransform: `rotate(${props.rotate}deg)`,
transform: `rotate(${props.rotate}deg)`
}
: null));
const svgStyle = computed(() =>
props.rotate
? {
msTransform: `rotate(${props.rotate}deg)`,
transform: `rotate(${props.rotate}deg)`,
}
: null,
);
const svgCls = computed(() => ({
'inner-icon--spin': !!props.spin || props.type === 'loading'
'inner-icon--spin': !!props.spin || props.type === 'loading',
}));
return () => (
<span
tabIndex={iconTabIndex.value}
role="img"
class="inner-icon"
onClick={attrs.onClick || noop}
>
<span tabIndex={iconTabIndex.value} role="img" class="inner-icon" onClick={attrs.onClick || noop}>
<CurrentIcon.value class={svgCls.value} style={svgStyle.value} />
</span>
);
}
},
};
</script>

View File

@ -42,13 +42,10 @@
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@webank/eslint-config-webank": "0.3.1"
},
"dependencies": {
"@fesjs/fes": "^2.0.0",
"vue": "^3.0.5",
"@fesjs/fes-design": "^0.1.10"
},
"private": true
}
}

View File

@ -42,13 +42,10 @@
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@webank/eslint-config-webank": "0.3.1"
},
"dependencies": {
"@fesjs/fes": "^2.0.0",
"vue": "^3.0.5",
"@fesjs/fes-design": "^0.1.10"
},
"private": true
}
}

View File

@ -1,4 +1,3 @@
import WindiCSSWebpackPlugin from 'windicss-webpack-plugin';
export default (api) => {
@ -8,8 +7,8 @@ export default (api) => {
schema(joi) {
return joi.object();
},
default: {}
}
default: {},
},
});
api.addEntryImportsAhead(() => [{ source: 'windi-base.css' }, { source: 'windi-components.css' }, { source: 'windi-utilities.css' }]);
@ -23,12 +22,12 @@ export default (api) => {
// A common use case is scanning files from the root directory
include: ['**/*.{vue,jsx,js,ts,tsx}'],
// if you are excluding files, make sure you always include node_modules and .git
exclude: ['node_modules', '.git', 'dist']
exclude: ['node_modules', '.git', 'dist', '.fes'],
},
...config
...config,
},
...otherOption
}
...otherOption,
},
]);
if (api.env === 'development') {
memo.module.rule('css').test((path) => {
@ -41,8 +40,8 @@ export default (api) => {
lang: 'windicss',
test: /windi-utilities.css$/,
styleLoaderOption: {
insert: 'body'
}
insert: 'body',
},
});
}

View File

@ -35,7 +35,10 @@
"@babel/preset-typescript": "^7.15.0",
"@fesjs/compiler": "^2.0.5",
"@fesjs/utils": "^2.0.4",
"@originjs/vite-plugin-commonjs": "^1.0.3",
"@soda/friendly-errors-webpack-plugin": "^1.8.0",
"@vitejs/plugin-vue": "^2.2.4",
"@vitejs/plugin-vue-jsx": "^1.3.8",
"@vue/babel-plugin-jsx": "^1.0.2",
"autoprefixer": "^10.2.4",
"babel-loader": "^8.2.2",
@ -65,6 +68,7 @@
"raw-loader": "^4.0.2",
"style-loader": "^2.0.0",
"url-loader": "^4.1.1",
"vite": "^2.8.6",
"vue-loader": "^16.1.2",
"webpack": "^5.24.2",
"webpack-bundle-analyzer": "^4.4.0",
@ -75,5 +79,6 @@
"peerDependencies": {
"@vue/compiler-sfc": "^3.0.5",
"core-js": "^3.8.3"
}
},
"devDependencies": {}
}

View File

@ -0,0 +1,8 @@
export default {
name: 'sfc-config',
transform(code, id) {
if (/vue&type=config/.test(id)) {
return `export default ''`;
}
},
};

View File

@ -0,0 +1,209 @@
/**
* @copy 该文件代码大部分出自 umi有需要请参考
* https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/commands/dev/dev.ts
*/
const assert = require('assert');
export default (api) => {
const {
env,
paths,
utils: { chalk, portfinder },
} = api;
const unwatchs = [];
let port;
let hostname;
let server;
function destroy() {
for (const unwatch of unwatchs) {
unwatch();
}
server?.close();
}
api.registerCommand({
command: 'dev',
description: 'start a local http service for development',
options: [
{
name: '--port',
description: 'http service port, like 8080',
},
{
name: '--https',
description: 'whether to turn on the https service',
},
],
async fn({ args = {} }) {
const { cleanTmpPathExceptCache, getBundleAndConfigs } = require('../buildDevUtils');
const { delay } = require('@fesjs/utils');
const createRouteMiddleware = require('./createRouteMiddleware').default;
const generateFiles = require('../../../utils/generateFiles').default;
const { watchPkg } = require('./watchPkg');
const defaultPort = process.env.PORT || args.port || api.config.devServer?.port;
port = await portfinder.getPortPromise({
port: defaultPort ? parseInt(String(defaultPort), 10) : 8000,
});
hostname = process.env.HOST || api.config.devServer?.host || 'localhost';
process.send({
type: 'UPDATE_PORT',
port,
});
// enable https
const isHTTPS = process.env.HTTPS || args.https;
console.log(chalk.cyan(`Starting the development server ${isHTTPS ? 'https' : 'http'}://${hostname}:${port} ...`));
cleanTmpPathExceptCache({
absTmpPath: paths.absTmpPath,
});
const watch = process.env.WATCH !== 'none';
// generate files
const unwatchGenerateFiles = await generateFiles({
api,
watch,
});
if (unwatchGenerateFiles) unwatchs.push(unwatchGenerateFiles);
if (watch) {
// watch pkg changes
const unwatchPkg = watchPkg({
cwd: api.cwd,
onChange() {
console.log();
api.logger.info('Plugins in package.json changed.');
api.restartServer();
},
});
unwatchs.push(unwatchPkg);
// watch config change
const unwatchConfig = api.service.configInstance.watch({
userConfig: api.service.userConfig,
onChange: async ({ pluginChanged, valueChanged }) => {
if (pluginChanged.length) {
console.log();
api.logger.info(`Plugins of ${pluginChanged.map((p) => p.key).join(', ')} changed.`);
api.restartServer();
}
if (valueChanged.length) {
let reload = false;
let regenerateTmpFiles = false;
const fns = [];
const reloadConfigs = [];
valueChanged.forEach(({ key, pluginId }) => {
const { onChange } = api.service.plugins[pluginId].config || {};
if (onChange === api.ConfigChangeType.regenerateTmpFiles) {
regenerateTmpFiles = true;
}
if (!onChange || onChange === api.ConfigChangeType.reload) {
reload = true;
reloadConfigs.push(key);
}
if (typeof onChange === 'function') {
fns.push(onChange);
}
});
if (reload) {
console.log();
api.logger.info(`Config ${reloadConfigs.join(', ')} changed.`);
api.restartServer();
} else {
api.service.userConfig = api.service.configInstance.getUserConfig();
await api.setConfig();
if (regenerateTmpFiles) {
await generateFiles({
api,
});
} else {
fns.forEach((fn) => fn());
}
}
}
},
});
unwatchs.push(unwatchConfig);
}
// delay dev server 启动,避免重复 compile
// https://github.com/webpack/watchpack/issues/25
// https://github.com/yessky/webpack-mild-compile
await delay(500);
// dev
const { bundleConfig } = await getBundleAndConfigs({ api });
const beforeMiddlewares = await api.applyPlugins({
key: 'addBeforeMiddlewares',
type: api.ApplyPluginsType.add,
initialValue: [],
args: {},
});
const middlewares = await api.applyPlugins({
key: 'addMiddlewares',
type: api.ApplyPluginsType.add,
initialValue: [],
args: {},
});
const { startDevServer } = require('./devServer');
server = startDevServer({
webpackConfig: bundleConfig,
host: hostname,
port,
proxy: api.config.proxy,
https: isHTTPS,
beforeMiddlewares: [...beforeMiddlewares, createRouteMiddleware(api)],
afterMiddlewares: [...middlewares],
customerDevServerConfig: api.config.devServer,
});
return {
destroy,
};
},
});
api.registerMethod({
name: 'getPort',
fn() {
assert(env === 'development', 'api.getPort() is only valid in development.');
return port;
},
});
api.registerMethod({
name: 'getHostname',
fn() {
assert(env === 'development', 'api.getHostname() is only valid in development.');
return hostname;
},
});
api.registerMethod({
name: 'getServer',
fn() {
assert(env === 'development', 'api.getServer() is only valid in development.');
return server;
},
});
api.registerMethod({
name: 'restartServer',
fn() {
console.log(chalk.gray('Try to restart dev server...'));
destroy();
process.send({
type: 'RESTART',
});
},
});
};

View File

@ -2,6 +2,11 @@
* @copy 该文件代码大部分出自 umi有需要请参考
* https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/commands/dev/dev.ts
*/
import { createServer } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import { viteCommonjs } from '@originjs/vite-plugin-commonjs';
import SFCConfigBlockPlugin from './SFCConfigBlockPlugin';
const assert = require('assert');
@ -9,7 +14,7 @@ export default (api) => {
const {
env,
paths,
utils: { chalk, portfinder }
utils: { chalk, portfinder },
} = api;
const unwatchs = [];
@ -27,48 +32,44 @@ export default (api) => {
api.registerCommand({
command: 'dev',
description: 'start a local http service for development',
options: [{
name: '--port',
description: 'http service port, like 8080'
}, {
name: '--https',
description: 'whether to turn on the https service'
}],
options: [
{
name: '--port',
description: 'http service port, like 8080',
},
{
name: '--https',
description: 'whether to turn on the https service',
},
],
async fn({ args = {} }) {
const {
cleanTmpPathExceptCache,
getBundleAndConfigs
} = require('../buildDevUtils');
const { delay } = require('@fesjs/utils');
const createRouteMiddleware = require('./createRouteMiddleware').default;
const { cleanTmpPathExceptCache } = require('../buildDevUtils');
const generateFiles = require('../../../utils/generateFiles').default;
const { watchPkg } = require('./watchPkg');
const defaultPort = process.env.PORT || args.port || api.config.devServer?.port;
port = await portfinder.getPortPromise({
port: defaultPort ? parseInt(String(defaultPort), 10) : 8000
port: defaultPort ? parseInt(String(defaultPort), 10) : 8000,
});
hostname = process.env.HOST || api.config.devServer?.host || 'localhost';
process.send({
type: 'UPDATE_PORT',
port
port,
});
// enable https
const isHTTPS = process.env.HTTPS || args.https;
console.log(chalk.cyan(`Starting the development server ${isHTTPS ? 'https' : 'http'}://${hostname}:${port} ...`));
// const isHTTPS = process.env.HTTPS || args.https;
cleanTmpPathExceptCache({
absTmpPath: paths.absTmpPath
absTmpPath: paths.absTmpPath,
});
const watch = process.env.WATCH !== 'none';
// generate files
const unwatchGenerateFiles = await generateFiles({
api,
watch
watch,
});
if (unwatchGenerateFiles) unwatchs.push(unwatchGenerateFiles);
@ -80,7 +81,7 @@ export default (api) => {
console.log();
api.logger.info('Plugins in package.json changed.');
api.restartServer();
}
},
});
unwatchs.push(unwatchPkg);
@ -90,11 +91,7 @@ export default (api) => {
onChange: async ({ pluginChanged, valueChanged }) => {
if (pluginChanged.length) {
console.log();
api.logger.info(
`Plugins of ${pluginChanged
.map(p => p.key)
.join(', ')} changed.`
);
api.logger.info(`Plugins of ${pluginChanged.map((p) => p.key).join(', ')} changed.`);
api.restartServer();
}
if (valueChanged.length) {
@ -104,16 +101,10 @@ export default (api) => {
const reloadConfigs = [];
valueChanged.forEach(({ key, pluginId }) => {
const { onChange } = api.service.plugins[pluginId].config || {};
if (
onChange
=== api.ConfigChangeType.regenerateTmpFiles
) {
if (onChange === api.ConfigChangeType.regenerateTmpFiles) {
regenerateTmpFiles = true;
}
if (
!onChange
|| onChange === api.ConfigChangeType.reload
) {
if (!onChange || onChange === api.ConfigChangeType.reload) {
reload = true;
reloadConfigs.push(key);
}
@ -124,11 +115,7 @@ export default (api) => {
if (reload) {
console.log();
api.logger.info(
`Config ${reloadConfigs.join(
', '
)} changed.`
);
api.logger.info(`Config ${reloadConfigs.join(', ')} changed.`);
api.restartServer();
} else {
api.service.userConfig = api.service.configInstance.getUserConfig();
@ -137,86 +124,91 @@ export default (api) => {
if (regenerateTmpFiles) {
await generateFiles({
api
api,
});
} else {
fns.forEach(fn => fn());
fns.forEach((fn) => fn());
}
}
}
}
},
});
unwatchs.push(unwatchConfig);
}
// delay dev server 启动,避免重复 compile
// https://github.com/webpack/watchpack/issues/25
// https://github.com/yessky/webpack-mild-compile
await delay(500);
server = await createServer({
mode: 'development',
plugins: [vue(), SFCConfigBlockPlugin, vueJsx(), viteCommonjs()],
configFile: false,
resolve: {
alias: {
'@': paths.absSrcPath,
'@@': paths.absTmpPath,
},
},
server: {
port: 8000,
},
});
await server.listen();
server.printUrls();
// dev
const { bundleConfig } = await getBundleAndConfigs({ api });
// const { bundleConfig } = await getBundleAndConfigs({ api });
// const beforeMiddlewares = await api.applyPlugins({
// key: 'addBeforeMiddlewares',
// type: api.ApplyPluginsType.add,
// initialValue: [],
// args: {}
// });
// const middlewares = await api.applyPlugins({
// key: 'addMiddlewares',
// type: api.ApplyPluginsType.add,
// initialValue: [],
// args: {}
// });
// const { startDevServer } = require('./devServer');
// server = startDevServer({
// webpackConfig: bundleConfig,
// host: hostname,
// port,
// proxy: api.config.proxy,
// https: isHTTPS,
// beforeMiddlewares: [...beforeMiddlewares, createRouteMiddleware(api)],
// afterMiddlewares: [...middlewares],
// customerDevServerConfig: api.config.devServer
// });
const beforeMiddlewares = await api.applyPlugins({
key: 'addBeforeMiddlewares',
type: api.ApplyPluginsType.add,
initialValue: [],
args: {}
});
const middlewares = await api.applyPlugins({
key: 'addMiddlewares',
type: api.ApplyPluginsType.add,
initialValue: [],
args: {}
});
const { startDevServer } = require('./devServer');
server = startDevServer({
webpackConfig: bundleConfig,
host: hostname,
port,
proxy: api.config.proxy,
https: isHTTPS,
beforeMiddlewares: [...beforeMiddlewares, createRouteMiddleware(api)],
afterMiddlewares: [...middlewares],
customerDevServerConfig: api.config.devServer
});
return {
destroy
destroy,
};
}
},
});
api.registerMethod({
name: 'getPort',
fn() {
assert(
env === 'development',
'api.getPort() is only valid in development.'
);
assert(env === 'development', 'api.getPort() is only valid in development.');
return port;
}
},
});
api.registerMethod({
name: 'getHostname',
fn() {
assert(
env === 'development',
'api.getHostname() is only valid in development.'
);
assert(env === 'development', 'api.getHostname() is only valid in development.');
return hostname;
}
},
});
api.registerMethod({
name: 'getServer',
fn() {
assert(
env === 'development',
'api.getServer() is only valid in development.'
);
assert(env === 'development', 'api.getServer() is only valid in development.');
return server;
}
},
});
api.registerMethod({
@ -225,8 +217,8 @@ export default (api) => {
console.log(chalk.gray('Try to restart dev server...'));
destroy();
process.send({
type: 'RESTART'
type: 'RESTART',
});
}
},
});
};

View File

@ -0,0 +1,3 @@
import { defineComponent } from 'vue';
export default defineComponent(() => () => (<RouterView></RouterView>));

View File

@ -2,13 +2,14 @@
import {
createApp,
reactive,
defineComponent
} from 'vue';
import { plugin } from './core/plugin';
import './core/pluginRegister';
import { ApplyPluginsType } from '{{{ runtimePath }}}';
import { getRoutes } from './core/routes/routes';
import DefaultContainer from './defaultContainer';
{{{ imports }}}
{{{ entryCodeAhead }}}
@ -18,7 +19,7 @@ const renderClient = (opts = {}) => {
const rootContainer = plugin.applyPlugins({
type: ApplyPluginsType.modify,
key: 'rootContainer',
initialValue: defineComponent(() => () => (<RouterView></RouterView>)),
initialValue: DefaultContainer,
args: {
routes: routes,
plugin: plugin

View File

@ -15,7 +15,7 @@ export function importsToStr(imports) {
export default function (api) {
const {
utils: { Mustache }
utils: { Mustache },
} = api;
api.onGenerateFiles(async () => {
@ -31,31 +31,37 @@ export default function (api) {
await api.applyPlugins({
key: 'addEntryCode',
type: api.ApplyPluginsType.add,
initialValue: []
initialValue: [],
})
).join('\r\n'),
entryCodeAhead: (
await api.applyPlugins({
key: 'addEntryCodeAhead',
type: api.ApplyPluginsType.add,
initialValue: []
initialValue: [],
})
).join('\r\n'),
importsAhead: importsToStr(
await api.applyPlugins({
key: 'addEntryImportsAhead',
type: api.ApplyPluginsType.add,
initialValue: []
})
initialValue: [],
}),
).join('\r\n'),
imports: importsToStr(
await api.applyPlugins({
key: 'addEntryImports',
type: api.ApplyPluginsType.add,
initialValue: []
})
).join('\r\n')
})
initialValue: [],
}),
).join('\r\n'),
}),
});
const defaultContainerName = 'defaultContainer';
api.writeTmpFile({
path: `${defaultContainerName}.jsx`,
content: readFileSync(join(__dirname, `./${defaultContainerName}.tpl`), 'utf-8'),
});
});
}

View File

@ -1,7 +1,5 @@
import { readdirSync, statSync, readFileSync } from 'fs';
import {
join, extname, posix, basename
} from 'path';
import { join, extname, posix, basename } from 'path';
import { lodash, parser, generator } from '@fesjs/utils';
import { parse } from '@vue/compiler-sfc';
import { Logger } from '@fesjs/compiler';
@ -43,16 +41,13 @@ const checkHasLayout = function (path) {
const getRouteName = function (parentRoutePath, fileName) {
const routeName = posix.join(parentRoutePath, fileName);
return routeName
.slice(1)
.replace(/\//g, '_')
.replace(/@/g, '_')
.replace(/\*/g, 'FUZZYMATCH');
return routeName.slice(1).replace(/\//g, '_').replace(/@/g, '_').replace(/\*/g, 'FUZZYMATCH');
};
const getPagePathPrefix = (config) => `@/${config.singular ? 'page' : 'pages'}`;
const getComponentPath = function (parentRoutePath, fileName, config) {
const pagesName = config.singular ? 'page' : 'pages';
return posix.join(`@/${pagesName}/`, parentRoutePath, fileName);
return posix.join(`${getPagePathPrefix(config)}/`, parentRoutePath, fileName);
};
const getRoutePath = function (parentRoutePath, fileName) {
@ -74,9 +69,14 @@ const getRoutePath = function (parentRoutePath, fileName) {
function getRouteMeta(content) {
const ast = parser.parse(content, {
sourceType: 'module',
plugins: ['jsx', 'typescript']
plugins: ['jsx', 'typescript'],
});
const defineRouteExpression = ast.program.body.filter(expression => expression.type === 'ExpressionStatement' && expression.expression.type === 'CallExpression' && expression.expression.callee.name === 'defineRouteMeta')[0];
const defineRouteExpression = ast.program.body.filter(
(expression) =>
expression.type === 'ExpressionStatement' &&
expression.expression.type === 'CallExpression' &&
expression.expression.callee.name === 'defineRouteMeta',
)[0];
if (defineRouteExpression) {
const argument = generator(defineRouteExpression.expression.arguments[0]);
return JSON.parse(argument.code.replace(/'/g, '"').replace(/(\S+):/g, (global, m1) => `"${m1}":`));
@ -91,7 +91,7 @@ const genRoutes = function (parentRoutes, path, parentRoutePath, config) {
const dirList = readdirSync(path);
const hasLayout = checkHasLayout(path);
const layoutRoute = {
children: []
children: [],
};
if (hasLayout) {
layoutRoute.path = parentRoutePath;
@ -106,22 +106,22 @@ const genRoutes = function (parentRoutes, path, parentRoutePath, config) {
// 路由的path
const routePath = getRoutePath(parentRoutePath, fileName);
if (cacheGenRoutes[routePath]) {
logger.warn(`[WARNING]: The file path: ${routePath}(.jsx/.tsx/.vue) conflict in routerwill only use ${routePath}.tsx or ${routePath}.jsxplease remove one of.`);
logger.warn(
`[WARNING]: The file path: ${routePath}(.jsx/.tsx/.vue) conflict in routerwill only use ${routePath}.tsx or ${routePath}.jsxplease remove one of.`,
);
return;
}
cacheGenRoutes[routePath] = true;
// 路由名称
const routeName = getRouteName(parentRoutePath, fileName);
const componentPath = getComponentPath(parentRoutePath, fileName, config);
const componentPath = getComponentPath(parentRoutePath, ext === '.vue' ? `${fileName}${ext}` : fileName, config);
let content = readFileSync(component, 'utf-8');
let routeMeta = {};
if (ext === '.vue') {
const { descriptor } = parse(content);
const routeMetaBlock = descriptor.customBlocks.find(
b => b.type === 'config'
);
const routeMetaBlock = descriptor.customBlocks.find((b) => b.type === 'config');
routeMeta = routeMetaBlock?.content ? JSON.parse(routeMetaBlock.content) : {};
if (descriptor.script) {
content = descriptor.script.content;
@ -136,7 +136,7 @@ const genRoutes = function (parentRoutes, path, parentRoutePath, config) {
path: routePath,
component: componentPath,
name: routeMeta.name || routeName,
meta: routeMeta
meta: routeMeta,
};
if (hasLayout) {
if (fileName === 'layout') {
@ -215,18 +215,22 @@ const getRoutes = function ({ config, absPagesPath }) {
return routes;
};
function genComponentName(component, config) {
return lodash.camelCase(component.replace(getPagePathPrefix(config), '').replace('.vue', ''));
}
function isFunctionComponent(component) {
// eslint-disable-next-line
return (
/^\((.+)?\)(\s+)?=>/.test(component)
|| /^function([^(]+)?\(([^)]+)?\)([^{]+)?{/.test(component)
);
}
const getRoutesJSON = function ({ routes, config }) {
// 因为要往 routes 里加无用的信息,所以必须 deep clone 一下,避免污染
const clonedRoutes = lodash.cloneDeep(routes);
function isFunctionComponent(component) {
// eslint-disable-next-line
return (
/^\((.+)?\)(\s+)?=>/.test(component)
|| /^function([^(]+)?\(([^)]+)?\)([^{]+)?{/.test(component)
);
}
function replacer(key, value) {
switch (key) {
case 'component':
@ -235,36 +239,51 @@ const getRoutesJSON = function ({ routes, config }) {
// TODO 针对目录进行 chunk 划分import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
return `() => import('${value}')`;
}
return `require('${value}').default`;
return genComponentName(value, config);
default:
return value;
}
}
return JSON.stringify(clonedRoutes, replacer, 2)
.replace(
/"component": ("(.+?)")/g,
(global, m1, m2) => `"component": ${m2.replace(/\^/g, '"')}`
)
.replace(/"component": ("(.+?)")/g, (global, m1, m2) => `"component": ${m2.replace(/\^/g, '"')}`)
.replace(/\\r\\n/g, '\r\n')
.replace(/\\n/g, '\r\n');
};
function genComponentImportExpression(routes, config) {
if (config.dynamicImport) {
return [];
}
const result = [];
for (const routeConfig of routes) {
if (routeConfig.children) {
result.push(...genComponentImportExpression(routeConfig.children, config));
}
if (routeConfig.component && !isFunctionComponent(routeConfig.component)) {
result.push(`import ${genComponentName(routeConfig.component, config)} from '${routeConfig.component}';`);
}
}
return result;
}
export default function (api) {
api.describe({
key: 'router',
config: {
schema(joi) {
return joi
.object({
routes: joi.array(),
mode: joi.string()
});
return joi.object({
routes: joi.array(),
mode: joi.string(),
});
},
default: {
mode: 'hash'
}
}
mode: 'hash',
},
},
});
api.registerMethod({
@ -275,22 +294,21 @@ export default function (api) {
type: api.ApplyPluginsType.modify,
initialValue: getRoutes({
config: api.config,
absPagesPath: api.paths.absPagesPath
})
absPagesPath: api.paths.absPagesPath,
}),
});
}
},
});
api.registerMethod({
name: 'getRoutesJSON',
async fn() {
const routes = await api.getRoutes();
return getRoutesJSON({ routes, config: api.config });
}
async fn(routes) {
return getRoutesJSON({ routes: await (routes || api.getRoutes()), config: api.config });
},
});
const {
utils: { Mustache }
utils: { Mustache },
} = api;
const namespace = 'core/routes';
@ -302,35 +320,36 @@ export default function (api) {
const historyType = {
history: 'createWebHistory',
hash: 'createWebHashHistory',
memory: 'createMemoryHistory'
memory: 'createMemoryHistory',
};
api.onGenerateFiles(async () => {
const routesTpl = readFileSync(join(__dirname, 'template/routes.tpl'), 'utf-8');
const routes = await api.getRoutesJSON();
const routes = await api.getRoutes();
api.writeTmpFile({
path: absCoreFilePath,
content: Mustache.render(routesTpl, {
runtimePath,
routes,
COMPONENTS_IMPORT: genComponentImportExpression(routes, api.config).join('\n'),
routes: await api.getRoutesJSON(),
config: api.config,
routerBase: api.config.base,
CREATE_HISTORY: historyType[api.config.router.mode] || 'createWebHashHistory'
})
CREATE_HISTORY: historyType[api.config.router.mode] || 'createWebHashHistory',
}),
});
api.writeTmpFile({
path: absRuntimeFilePath,
content: readFileSync(join(__dirname, 'template/runtime.tpl'), 'utf-8')
content: readFileSync(join(__dirname, 'template/runtime.tpl'), 'utf-8'),
});
});
api.addCoreExports(() => [
{
specifiers: ['getRoutes', 'getRouter', 'getHistory', 'destroyRouter', 'defineRouteMeta'],
source: absCoreFilePath
}
source: absCoreFilePath,
},
]);
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);

View File

@ -1,6 +1,8 @@
import { createRouter as createVueRouter, {{{ CREATE_HISTORY }}}, ApplyPluginsType } from '{{{ runtimePath }}}';
import { plugin } from '@@/core/coreExports';
{{{ COMPONENTS_IMPORT }}}
export function getRoutes() {
const routes = {{{ routes }}};
return routes;

View File

@ -9,7 +9,6 @@ export default {
},
publicPath: '/',
request: {
base: '/ras-mas',
dataField: 'result'
},
html: {
@ -35,15 +34,15 @@ export default {
devServer: {
port: 8000
},
windicss: {
config: {
theme: {
extend: {
colors: {
green: '#7cb305'
}
}
}
}
}
// windicss: {
// config: {
// theme: {
// extend: {
// colors: {
// green: '#7cb305'
// }
// }
// }
// }
// }
};

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection" content="telephone=no" />
<meta name="format-detection" content="email=no" />
<meta name="viewport"
content="viewport-fit=cover,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<link rel="shortcut icon" type="image/x-icon" href="./logo.png">
</head>
<body ontouchstart="">
<div id="app"></div>
<script type="module" src="/src/.fes/fes.js"></script>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -40,7 +40,6 @@
"access": "public"
},
"devDependencies": {
"@webank/eslint-config-webank": "0.3.1",
"@ttou/postcss-px-to-viewport": "1.1.4",
"@vue/compiler-sfc": "^3.2.2"
},
@ -48,7 +47,6 @@
"@fesjs/fes": "^2.0.0",
"@fesjs/plugin-icon": "^2.0.0",
"@fesjs/plugin-request": "^2.0.0",
"@fesjs/plugin-windicss": "^2.0.0",
"vue": "^3.2.2"
},
"private": true

View File

@ -1,3 +0,0 @@
<template>
<div>hello vue</div>
</template>

View File

@ -1,12 +1,7 @@
<template>
<div class="onepiece m-10px text-green">
fes h5 & 拉夫德鲁<br />
<fes-icon
:spin="true"
class="one-icon"
type="smile"
@click="clickIcon"
/>
<fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" />
<HelloWorld />
<HelloTSX />
<helloTS />
@ -15,21 +10,21 @@
<script>
import { ref } from 'vue';
import { request, defineRouteMeta, useRoute } from '@fesjs/fes';
import HelloWorld from '@/components/helloWorld';
import HelloTSX from '@/components/helloTSX';
import helloTS from '@/components/helloTS';
import HelloWorld from '@/components/helloWorld.vue';
import HelloTSX from '@/components/helloTSX.vue';
import helloTS from '@/components/helloTS.vue';
defineRouteMeta({
title: '首页',
name: 'testIndex',
layout: false
layout: false,
});
export default {
components: {
HelloWorld,
HelloTSX,
helloTS
helloTS,
},
setup() {
const fes = ref('fes upgrade to vue3');
@ -68,8 +63,8 @@ export default {
'/get/api',
{ id },
{
method: 'get'
}
method: 'get',
},
);
};
@ -78,8 +73,8 @@ export default {
'/api',
{ id },
{
responseType: 'blob'
}
responseType: 'blob',
},
).then((data) => {
console.log(data);
});
@ -143,15 +138,15 @@ export default {
return {
fes,
rotate,
clickIcon
clickIcon,
};
}
},
};
</script>
<style lang="less" scoped>
@import '~@/styles/mixins/hairline';
@import '~@/styles/mixins/hover';
@import '@/styles/mixins/hairline';
@import '@/styles/mixins/hover';
div {
padding: 20px;

View File

@ -42,9 +42,6 @@
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@webank/eslint-config-webank": "0.3.1"
},
"dependencies": {
"@fesjs/fes": "^2.0.0",
"@fesjs/plugin-access": "^2.0.0",
@ -66,4 +63,4 @@
"pinia": "^2.0.11"
},
"private": true
}
}

8949
yarn.lock

File diff suppressed because it is too large Load Diff