feat: 升级 vite5 (#222)

* feat: 升级 vite5

* fix: 优化 webpack publicPath

* fix: 优化文档

* chore: remove log

* chore: remove 无效代码
This commit is contained in:
qlin 2023-12-14 14:28:02 +08:00 committed by GitHub
parent fb54913109
commit fec7a8f1ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 3304 additions and 1241 deletions

View File

@ -1,22 +0,0 @@
module.exports = {
extends: ['@webank/eslint-config-webank/vue.js'],
globals: {
// 这里填入你的项目需要的全局变量
// 这里值为 false 表示这个全局变量不允许被重新赋值,比如:
//
// Vue: false
__DEV__: false,
},
rules: {
'vue/comment-directive': 'off',
'global-require': 'off',
'import/no-unresolved': 'off',
'no-restricted-syntax': 'off',
'no-undefined': 'off',
'vue/valid-template-root': 'off',
'no-use-before-define': 'off',
},
env: {
jest: true,
},
};

2
.gitignore vendored
View File

@ -1,7 +1,5 @@
.DS_Store
.idea
.git
.vscode
.history
.cache
.temp

View File

@ -1,4 +0,0 @@
module.exports = {
// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions
...require('@webank/eslint-config-webank/.prettierrc.js'),
};

48
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,48 @@
{
// Enable the ESlint flat config support
"eslint.experimental.useFlatConfig": true,
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
],
"pair-diff.patterns": [
{
"source": "./fixtures/output/**/*.*",
"target": "./fixtures/input/<base>"
}
]
}

View File

@ -1,4 +1,4 @@
module.exports = {
export default {
pkgs: [
'create-fes-app',
'fes',

View File

@ -4,11 +4,14 @@
1. 编译时的 [base](../reference/config/index.md/#base) 配置,移到了 [router.base](../reference/config/index.md/#router) 下。
2. [webpack-dev-server](https://github.com/webpack/webpack-dev-server) 从 `v3.x` 升级到了 `v4.x`,如果遇到配置不兼容,可以查看[webpack-dev-server 3.x 升级 4.x](https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md)。
3. [layout 插件](../reference/plugin/plugins/layout.md#_4-x-升级到-5-x) 有一些属性变更
3. [request 插件](../reference/plugin/plugins/request.md#_2-x-升级到-3-x) 有一些参数变更
## 继续使用 Webpack
1. 添加 Webpack 构建依赖包: `npm i @fesjs/builder-webpack -D`
2. 如果有,将 `public/index.html` 文件挪到项目根目录,移除 [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) 相关配置,具体模版变量使用请查看[HTML 模版](../guide/template.html)。
2. 如果设置了 `publicPath: './'`,请更改为 `publicPath: ''`
3. 如果有,将 `public/index.html` 文件挪到项目根目录,移除 [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) 相关配置,具体模版变量使用请查看[HTML 模版](../guide/template.html)。
## 换成 Vite

View File

@ -31,12 +31,12 @@ export default defineRuntimeConfig({
dataHandler(data, response) {
// 处理响应内容异常
if (data.code !== '0') {
if (data.code === '10000') {
if (data.code === '10000')
FMesseage.error('hello world');
}
if (data.code === '20000') {
if (data.code === '20000')
FMesseage.error('hello world');
}
throw new Error(response);
}
// 响应数据格式化
@ -49,15 +49,18 @@ export default defineRuntimeConfig({
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
}
else if (error.request) {
// 请求已经成功发起,但没有收到响应
// `error.request` 在浏览器中是 XMLHttpRequest 的实例,
// 而在node.js中是 http.ClientRequest 的实例
console.log(error.request);
} else if (error.type) {
}
else if (error.type) {
// 插件异常
console.log(error.msg);
} else {
}
else {
// 发送请求时出了点问题
console.log('Error', error.message);
}
@ -192,4 +195,4 @@ export default {
1. 删除 dataField 配置,通过 dataHandler 实现类似功能,详情看上文案例
2. errorHandler 改成了函数了,异常处理逻辑,查看上文案例
3. 废弃 base 参数,用 baseURL
4. 移除 skipErrorHandler 参数,直接传 errorHandler 可覆盖默认 errorHandler
4. 移除 skipErrorHandler 参数,目前还做了兼容,最好用 dataHandler 和 errorHandler 代替

20
eslint.config.js Normal file
View File

@ -0,0 +1,20 @@
// eslint.config.js
import antfu from '@antfu/eslint-config';
export default await antfu({
stylistic: {
indent: 4, // 4, or 'tab'
quotes: 'single', // or 'double'
semi: 'always',
},
typescript: true,
vue: true,
rules: {
'vue/block-order': [
'error',
{
order: ['template', 'script', 'style'],
},
],
},
});

View File

@ -7,6 +7,7 @@
"workspaces": [
"packages/*"
],
"type": "module",
"scripts": {
"bootstrap": "pnpm i",
"dev": "node scripts/build.mjs --watch",
@ -37,16 +38,17 @@
"semver": "^7.3.6"
},
"devDependencies": {
"@antfu/eslint-config": "^2.1.1",
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@webank/eslint-config-webank": "1.2.7",
"chokidar": "^3.5.3",
"commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0",
"deepmerge": "^4.2.2",
"eslint": "^8.54.0",
"fs-extra": "^11.1.1",
"husky": "^8.0.3",
"lint-staged": "^13.2.0",
"simple-git-hooks": "^2.9.0",
"typescript": "^5.0.4",
"vitepress": "1.0.0-alpha.73",
"vue": "^3.3.4",
@ -57,11 +59,9 @@
"npm run lint"
]
},
"husky": {
"hooks": {
"simple-git-hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"config": {
"commitizen": {

View File

@ -2,44 +2,41 @@
"name": "@fesjs/builder-vite",
"version": "3.0.4",
"description": "@fesjs/builder-vite",
"main": "lib/index.js",
"files": [
"lib",
"types.d.ts"
],
"author": "qlin",
"license": "MIT",
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
"directory": "packages/fes-builder-vite"
},
"keywords": [
"fes"
],
"author": "qlin",
"license": "MIT",
"bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues"
},
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"keywords": [
"fes"
],
"main": "lib/index.js",
"files": [
"lib",
"types.d.ts"
],
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"@fesjs/fes": "^3.1.4",
"core-js": "^3.29.1"
},
"dependencies": {
"@babel/core": "^7.21.3",
"@babel/core": "^7.23.3",
"@fesjs/utils": "^3.0.1",
"@vitejs/plugin-basic-ssl": "^1.0.1",
"@vitejs/plugin-legacy": "^3.0.1",
"@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"@rollup/pluginutils": "^5.1.0",
"@vitejs/plugin-basic-ssl": "^1.0.2",
"@vitejs/plugin-legacy": "^5.2.0",
"@vitejs/plugin-vue": "^4.5.0",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.4",
"babel-plugin-polyfill-corejs3": "^0.7.1",
"less": "^4.1.2",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-safe-parser": "^6.0.0",
"rollup-plugin-visualizer": "^5.6.0",
"terser": "^5.16.8",
"vite": "^4.2.1",
"@rollup/pluginutils": "^4.2.0",
"colorette": "^2.0.16",
"connect-history-api-fallback": "^2.0.0",
"consola": "^2.15.3",
@ -49,12 +46,14 @@
"fast-glob": "^3.2.11",
"fs-extra": "^10.0.1",
"html-minifier-terser": "^6.1.0",
"less": "^4.2.0",
"node-html-parser": "^5.3.3",
"pathe": "^0.2.0"
},
"peerDependencies": {
"@fesjs/fes": "^3.1.4",
"core-js": "^3.29.1"
"pathe": "^0.2.0",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-safe-parser": "^6.0.0",
"rollup-plugin-visualizer": "^5.9.3",
"terser": "^5.24.0",
"vite": "^5.0.3"
},
"typings": "./types.d.ts"
}

View File

@ -1,5 +1,5 @@
import { join } from 'path';
import { existsSync } from 'fs';
import { join } from 'node:path';
import { existsSync } from 'node:fs';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import { createHtmlPlugin } from './vite-plugin-html';
@ -9,15 +9,15 @@ import getDefine from './getDefine';
function getPostcssConfig(api) {
// TODO 支持其他 postcss 配置文件类型
const configPath = `${api.paths.cwd}/postcss.config.js`;
if (existsSync(configPath)) {
if (existsSync(configPath))
return require(`${api.paths.cwd}/postcss.config.js`);
}
return {};
}
export function getInnerCommonConfig(api) {
const { deepmerge, resolveRuntimeEnv } = api.utils;
const { server, build, define, base, ...otherViteOption } = api.config.viteOption;
const { base, ...otherViteOption } = api.config.viteOption;
const publicPath = base || api.config.publicPath || '/';

View File

@ -8,9 +8,13 @@ export default (api, publicPath) => {
...api.config.define,
...viteOption.define,
});
const formatEnv = Object.keys(env).reduce((acc, cur) => {
acc[`process.env.${cur}`] = JSON.stringify(env[cur]);
return acc;
}, {});
return {
'process.env': env,
...formatEnv,
...define,
};
};

View File

@ -1,7 +1,8 @@
import process from 'node:process';
import { render } from 'ejs';
import { expand } from 'dotenv-expand';
import dotenv from 'dotenv';
import path, { join, dirname } from 'pathe';
import path, { dirname, join } from 'pathe';
import fse from 'fs-extra';
import { normalizePath } from 'vite';
import { parse } from 'node-html-parser';
@ -15,20 +16,18 @@ import history from './connectHistoryMiddleware';
function lookupFile(dir, formats, pathOnly = false) {
for (const format of formats) {
const fullPath = join(dir, format);
if (fse.pathExistsSync(fullPath) && fse.statSync(fullPath).isFile()) {
if (fse.pathExistsSync(fullPath) && fse.statSync(fullPath).isFile())
return pathOnly ? fullPath : fse.readFileSync(fullPath, 'utf-8');
}
}
const parentDir = dirname(dir);
if (parentDir !== dir) {
if (parentDir !== dir)
return lookupFile(parentDir, formats, pathOnly);
}
}
function loadEnv(mode, envDir, prefix = '') {
if (mode === 'local') {
if (mode === 'local')
throw new Error(`"local" cannot be used as a mode name because it conflicts with the .local postfix for .env files.`);
}
const env = {};
const envFiles = [`.env.${mode}.local`, `.env.${mode}`, `.env.local`, `.env`];
for (const file of envFiles) {
@ -40,19 +39,19 @@ function loadEnv(mode, envDir, prefix = '') {
ignoreProcessEnv: true,
});
for (const [key, value] of Object.entries(parsed)) {
if (key.startsWith(prefix) && env[key] === undefined) {
if (key.startsWith(prefix) && env[key] === undefined)
env[key] = value;
} else if (key === 'NODE_ENV') {
else if (key === 'NODE_ENV')
process.env.VITE_USER_NODE_ENV = value;
}
}
}
}
return env;
}
async function isDirEmpty(dir) {
return fse.readdir(dir).then((files) => files.length === 0);
return fse.readdir(dir).then(files => files.length === 0);
}
const DEFAULT_TEMPLATE = 'index.html';
@ -65,7 +64,7 @@ function createPlugin(userOptions = {}) {
let env = {};
return {
name: 'vite:html',
enforce: 'pre',
order: 'pre',
configResolved(resolvedConfig) {
viteConfig = resolvedConfig;
env = loadEnv(viteConfig.mode, viteConfig.root, '');
@ -92,8 +91,9 @@ function createPlugin(userOptions = {}) {
filename,
template: template2,
});
} else {
_pages = pages.map((page) => ({
}
else {
_pages = pages.map(page => ({
filename: page.filename || DEFAULT_TEMPLATE,
template: page.template || DEFAULT_TEMPLATE,
}));
@ -103,15 +103,15 @@ function createPlugin(userOptions = {}) {
const keys = Object.keys(proxy);
let indexPage = null;
for (const page of _pages) {
if (page.filename !== 'index.html') {
if (page.filename !== 'index.html')
rewrites.push(createRewire(page.template, page, baseUrl, keys));
} else {
else
indexPage = page;
}
}
if (indexPage) {
if (indexPage)
rewrites.push(createRewire('', indexPage, baseUrl, keys));
}
server.middlewares.use(
history(viteConfig, {
disableDotRule: undefined,
@ -121,8 +121,8 @@ function createPlugin(userOptions = {}) {
);
},
transformIndexHtml: {
enforce: 'pre',
async transform(html, ctx) {
order: 'pre',
async handler(html, ctx) {
const url = ctx.filename;
const base = viteConfig.base;
const excludeBaseUrl = url.replace(base, '/');
@ -148,38 +148,36 @@ function createPlugin(userOptions = {}) {
if (isMpa(viteConfig) || pages.length) {
for (const page of pages) {
const dir = path.dirname(page.template);
if (!ignoreDirs.includes(dir)) {
if (!ignoreDirs.includes(dir))
outputDirs.push(dir);
}
}
} else {
else {
const dir = path.dirname(template);
if (!ignoreDirs.includes(dir)) {
if (!ignoreDirs.includes(dir))
outputDirs.push(dir);
}
}
const cwd = path.resolve(viteConfig.root, viteConfig.build.outDir);
const htmlFiles = await fg(
outputDirs.map((dir) => `${dir}/*.html`),
outputDirs.map(dir => `${dir}/*.html`),
{ cwd: path.resolve(cwd), absolute: true },
);
await Promise.all(
htmlFiles.map((file) =>
htmlFiles.map(file =>
fse.move(file, path.resolve(cwd, path.basename(file)), {
overwrite: true,
}),
),
);
const htmlDirs = await fg(
outputDirs.map((dir) => dir),
outputDirs.map(dir => dir),
{ cwd: path.resolve(cwd), onlyDirectories: true, absolute: true },
);
await Promise.all(
htmlDirs.map(async (item) => {
const isEmpty = await isDirEmpty(item);
if (isEmpty) {
if (isEmpty)
return fse.remove(item);
}
}),
);
},
@ -189,7 +187,7 @@ function createPlugin(userOptions = {}) {
function createInput({ pages = [], template = DEFAULT_TEMPLATE }, viteConfig) {
const input = {};
if (isMpa(viteConfig) || pages?.length) {
const templates = pages.map((page) => page.template);
const templates = pages.map(page => page.template);
templates.forEach((temp) => {
let dirName = path.dirname(temp);
const file = path.basename(temp);
@ -200,9 +198,9 @@ function createInput({ pages = [], template = DEFAULT_TEMPLATE }, viteConfig) {
return input;
}
const dir = path.dirname(template);
if (ignoreDirs.includes(dir)) {
if (ignoreDirs.includes(dir))
return undefined;
}
const file = path.basename(template);
const key = file.replace(/\.html/, '');
return {
@ -229,11 +227,12 @@ async function renderHtml(html, config) {
function getPage({ pages = [], entry, template = DEFAULT_TEMPLATE, inject = {} }, name, viteConfig) {
let page;
if (isMpa(viteConfig) || pages?.length) {
if (isMpa(viteConfig) || pages?.length)
page = getPageConfig(name, pages, DEFAULT_TEMPLATE);
} else {
else
page = createSpaPage(entry, template, inject);
}
return page;
}
@ -243,9 +242,9 @@ function isMpa(viteConfig) {
}
function removeEntryScript(html, verbose = false) {
if (!html) {
if (!html)
return html;
}
const root = parse(html);
const scriptNodes = root.querySelectorAll('script[type=module]') || [];
const removedNode = [];
@ -253,9 +252,9 @@ function removeEntryScript(html, verbose = false) {
removedNode.push(item.toString());
item.parentNode.removeChild(item);
});
verbose &&
removedNode.length &&
consola.warn(`vite-plugin-html: Since you have already configured entry, ${dim(
verbose
&& removedNode.length
&& consola.warn(`vite-plugin-html: Since you have already configured entry, ${dim(
removedNode.toString(),
)} is deleted. You may also delete it from the index.html.
`);
@ -276,7 +275,7 @@ function getPageConfig(htmlName, pages, defaultPage) {
filename: defaultPage,
template: `./${defaultPage}`,
};
const page = pages.filter((page2) => path.resolve(`/${page2.template}`) === path.resolve(`/${htmlName}`))?.[0];
const page = pages.filter(page2 => path.resolve(`/${page2.template}`) === path.resolve(`/${htmlName}`))?.[0];
return page ?? defaultPageOption ?? undefined;
}
@ -287,10 +286,10 @@ function createRewire(reg, page, baseUrl, proxyUrlKeys) {
const pathname = parsedUrl.pathname;
const excludeBaseUrl = pathname.replace(baseUrl, '/');
const template = path.resolve(baseUrl, page.template);
if (excludeBaseUrl === '/') {
if (excludeBaseUrl === '/')
return template;
}
const isApiUrl = proxyUrlKeys.some((item) => pathname.startsWith(path.resolve(baseUrl, item)));
const isApiUrl = proxyUrlKeys.some(item => pathname.startsWith(path.resolve(baseUrl, item)));
return isApiUrl ? parsedUrl.path : template;
},
};
@ -312,13 +311,13 @@ function getOptions(_minify) {
}
async function minifyHtml(html, minify$1) {
if (typeof minify$1 === 'boolean' && !minify$1) {
if (typeof minify$1 === 'boolean' && !minify$1)
return html;
}
let minifyOptions = minify$1;
if (typeof minify$1 === 'boolean' && minify$1) {
if (typeof minify$1 === 'boolean' && minify$1)
minifyOptions = getOptions(minify$1);
}
const res = await minify(html, minifyOptions);
return res;
}
@ -326,15 +325,14 @@ async function minifyHtml(html, minify$1) {
function createMinifyHtmlPlugin({ _minify = true } = {}) {
return {
name: 'vite:minify-html',
enforce: 'post',
order: 'post',
async generateBundle(_, outBundle) {
if (_minify) {
for (const bundle of Object.values(outBundle)) {
if (bundle.type === 'asset' && htmlFilter(bundle.fileName) && typeof bundle.source === 'string') {
if (bundle.type === 'asset' && htmlFilter(bundle.fileName) && typeof bundle.source === 'string')
bundle.source = await minifyHtml(bundle.source, _minify);
}
}
}
},
};
}

View File

@ -62,7 +62,7 @@
"webpack": "^5.87.0",
"webpack-5-chain": "^8.0.1",
"webpack-bundle-analyzer": "^4.4.0",
"webpack-dev-server": "^4.8.1",
"webpack-dev-server": "^4.15.1",
"webpackbar": "^5.0.2"
},
"peerDependencies": {

View File

@ -1,5 +1,5 @@
import { join } from 'path';
import { existsSync } from 'fs';
import { join } from 'node:path';
import { existsSync } from 'node:fs';
import Config from 'webpack-5-chain';
import webpack from 'webpack';
import createCssWebpackConfig from './css';
@ -27,11 +27,10 @@ function genTranspileDepRegex(exclude) {
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;
return require('node:os').platform().startsWith('win') ? depPath.replace(/\\/g, '\\\\') : depPath;
}
if (dep instanceof RegExp) {
if (dep instanceof RegExp)
return dep.source;
}
throw new Error('exclude only accepts an array of string or regular expressions');
});
@ -76,7 +75,6 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
// --------------- output -----------
webpackConfig.output
.path(absoluteOutput)
.publicPath(publicPath || '/')
.filename('static/[name].[contenthash:8].js')
.chunkFilename('static/[name].[contenthash:8].chunk.js')
.assetModuleFilename('static/[name][hash:8][ext]');
@ -133,9 +131,9 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
.test(/\.(js|mjs|jsx|ts|tsx)$/)
.exclude.add((filepath) => {
// always transpile js in vue files
if (/(\.vue|\.jsx)$/.test(filepath)) {
if (/(\.vue|\.jsx)$/.test(filepath))
return false;
}
// Don't transpile node_modules
return /node_modules/.test(filepath);
})
@ -153,9 +151,8 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
.include.add(/node_modules/)
.end()
.exclude.add((filepath) => {
if (transpileDepRegex && transpileDepRegex.test(filepath)) {
if (transpileDepRegex && transpileDepRegex.test(filepath))
return true;
}
return false;
})
@ -195,12 +192,12 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
existsSync(join(cwd, 'public')) && {
from: join(cwd, 'public'),
filter: (resourcePath) => {
if (resourcePath.indexOf('.DS_Store') !== -1) {
if (resourcePath.includes('.DS_Store'))
return false;
}
if (publicCopyIgnore.includes(resourcePath)) {
if (publicCopyIgnore.includes(resourcePath))
return false;
}
return true;
},
to: absoluteOutput,

View File

@ -4,7 +4,7 @@ export default (api) => {
config: {
default: '/',
schema(joi) {
return joi.string().regex(/\/$/).error(new Error('config.publicPath must end with /.'));
return joi.string().allow('');
},
},
});

View File

@ -1,10 +1,18 @@
{
"name": "@fesjs/template-h5",
"version": "2.0.0",
"private": true,
"description": "fes 移动端项目模版",
"scripts": {
"prod": "FES_ENV=prod fes build",
"dev": "fes dev"
"author": "qlin",
"license": "MIT",
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
"directory": "packages/fes-template-h5"
},
"bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues"
},
"keywords": [
"管理端",
@ -15,38 +23,30 @@
],
"files": [
".eslintrc.js",
".gitignore",
".fes.js",
".fes.prod.js",
".gitignore",
"/config",
"/src",
"README.md",
"mock.js",
"package.json",
"README.md",
"tsconfig.json",
"/src",
"/config"
"tsconfig.json"
],
"repository": {
"type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
"directory": "packages/fes-template-h5"
"scripts": {
"prod": "FES_ENV=prod fes build",
"dev": "fes dev"
},
"author": "qlin",
"license": "MIT",
"bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues"
},
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"publishConfig": {
"access": "public"
},
"dependencies": {
"postcss-px-to-viewport-8-plugin": "^1.2.3",
"@fesjs/builder-vite": "workspace:*",
"@fesjs/fes": "workspace:*",
"@fesjs/plugin-icon": "workspace:*",
"@fesjs/plugin-request": "workspace:*",
"@fesjs/builder-vite": "workspace:*",
"core-js": "^3.29.1",
"postcss-px-to-viewport-8-plugin": "^1.2.3",
"vue": "^3.2.47"
},
"private": true
}
}

View File

@ -1,11 +0,0 @@
module.exports = {
extends: ['@webank/eslint-config-webank/vue.js'],
overrides: [
{
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
},
],
env: {
jest: true,
},
};

View File

@ -1,6 +1,6 @@
import { defineBuildConfig } from '@fesjs/fes';
export default defineBuildConfig({
builder: 'webpack',
builder: 'vite',
publicPath: './',
});

View File

@ -1,7 +1,10 @@
{
"name": "@fesjs/template-vite",
"version": "2.0.0",
"private": true,
"description": "fes项目模版",
"author": "harrywan",
"license": "MIT",
"scripts": {
"build": "fes build",
"prod": "FES_ENV=prod fes build",
@ -9,36 +12,6 @@
"dev": "fes dev",
"test": "fes test"
},
"keywords": [
"管理端",
"fes",
"fast",
"easy",
"strong"
],
"files": [
".eslintrc.js",
".gitignore",
".fes.js",
".fes.prod.js",
"mock.js",
"package.json",
"README.md",
"tsconfig.json",
"/src",
"/config"
],
"repository": {
"type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
"directory": "packages/fes-template"
},
"author": "harrywan",
"license": "MIT",
"bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues"
},
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"publishConfig": {
"access": "public"
},
@ -61,6 +34,5 @@
"core-js": "^3.29.1",
"pinia": "^2.0.11",
"vue": "^3.2.47"
},
"private": true
}
}

View File

@ -25,7 +25,6 @@ export const beforeRender = {
export const layout = {
logo: `${process.env.BASE_URL}wine-outline.svg`,
renderCustom: (props) => {
console.log(props);
return <UserCenter />;
},
};

View File

@ -1,13 +1,15 @@
<template>
<div class="page">
menuTest: {{route.params}}
menuTest: {{ route.params }}
</div>
</template>
<config>
{
"title": "menuTest-详情"
}
</config>
<script>
import { useRoute } from '@fesjs/fes';
@ -17,9 +19,9 @@ export default {
setup() {
const route = useRoute();
return {
route
route,
};
}
},
};
</script>

View File

@ -1,14 +1,9 @@
<template>
<div>
{{params.id}}
{{ params.id }}
</div>
</template>
<config>
{
"name": "activeRoute",
"title": "动态路由"
}
</config>
<script>
import { useRoute } from '@fesjs/fes';
@ -16,8 +11,15 @@ export default {
setup() {
const { params } = useRoute();
return {
params
params,
};
}
},
};
</script>
<config>
{
"name": "activeRoute",
"title": "动态路由"
}
</config>

View File

@ -1,11 +0,0 @@
module.exports = {
extends: ['@webank/eslint-config-webank/vue.js'],
overrides: [
{
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
},
],
env: {
jest: true,
},
};

View File

@ -1,6 +1,7 @@
import { defineBuildConfig } from '@fesjs/fes';
export default defineBuildConfig({
publicPath: '',
targets: {
chrome: '78',
},

View File

@ -1,7 +1,10 @@
{
"name": "@fesjs/template",
"version": "2.0.0",
"private": true,
"description": "fes项目模版",
"author": "harrywan",
"license": "MIT",
"scripts": {
"build": "fes build",
"prod": "FES_ENV=prod fes build",
@ -10,61 +13,30 @@
"test": "fes test",
"prev": "fes preview"
},
"keywords": [
"管理端",
"fes",
"fast",
"easy",
"strong"
],
"files": [
".eslintrc.js",
".gitignore",
".fes.js",
".fes.prod.js",
"mock.js",
"package.json",
"README.md",
"tsconfig.json",
"/src",
"/config"
],
"repository": {
"type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
"directory": "packages/fes-template"
},
"author": "harrywan",
"license": "MIT",
"bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues"
},
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@fesjs/fes": "workspace:*",
"@fesjs/builder-webpack": "workspace:*",
"@fesjs/plugin-login": "workspace:*",
"@fesjs/fes": "workspace:*",
"@fesjs/fes-design": "^0.8.0",
"@fesjs/plugin-access": "workspace:*",
"@fesjs/plugin-enums": "workspace:*",
"@fesjs/plugin-jest": "workspace:*",
"@fesjs/plugin-layout": "workspace:*",
"@fesjs/plugin-locale": "workspace:*",
"@fesjs/plugin-login": "workspace:*",
"@fesjs/plugin-model": "workspace:*",
"@fesjs/plugin-monaco-editor": "workspace:*",
"@fesjs/plugin-pinia": "workspace:*",
"@fesjs/plugin-qiankun": "workspace:*",
"@fesjs/plugin-request": "workspace:*",
"@fesjs/plugin-sass": "workspace:*",
"@fesjs/plugin-swc": "workspace:*",
"@fesjs/plugin-watermark": "workspace:*",
"@fesjs/plugin-windicss": "workspace:*",
"@fesjs/plugin-swc": "workspace:*",
"@fesjs/fes-design": "^0.8.0",
"core-js": "3.29.1",
"pinia": "^2.0.33",
"vue": "^3.2.47"
},
"private": true
}
}

View File

@ -1,4 +1,5 @@
import { chalk, yParser, semver } from '@fesjs/utils';
import process from 'node:process';
import { chalk, semver, yParser } from '@fesjs/utils';
import fesPkg from '../package.json';
import { Service } from './serviceWithBuiltIn';
import fork from './utils/fork';
@ -10,6 +11,7 @@ const requiredVersion = fesPkg.engines.node;
function checkNodeVersion(wanted, id) {
if (!semver.satisfies(process.version, wanted, { includePrerelease: true })) {
// eslint-disable-next-line no-console
console.log(chalk.red(`You are using Node ${process.version}, but this version of ${id} requires Node ${wanted}.\nPlease upgrade your Node version.`));
process.exit(1);
}
@ -37,11 +39,12 @@ const args = yParser(rawArgv);
child.kill('SIGTERM');
process.exit(1);
});
} else {
hackFesInBuild();
if (command === 'build') {
process.env.NODE_ENV = 'production';
}
else {
hackFesInBuild();
if (command === 'build')
process.env.NODE_ENV = 'production';
await new Service({
cwd: getCwd(),
pkg: getPkg(process.cwd()),
@ -51,7 +54,8 @@ const args = yParser(rawArgv);
rawArgv,
});
}
} catch (e) {
}
catch (e) {
console.error(chalk.red(e.message));
console.error(e.stack);
process.exit(1);

View File

@ -1,11 +1,12 @@
import { fork } from 'child_process';
import { fork } from 'node:child_process';
import process from 'node:process';
const usedPorts = [];
let CURRENT_PORT;
export default function start({ scriptPath }) {
const execArgv = process.execArgv.slice(0);
const inspectArgvIndex = execArgv.findIndex((argv) => argv.includes('--inspect-brk'));
const inspectArgvIndex = execArgv.findIndex(argv => argv.includes('--inspect-brk'));
if (inspectArgvIndex > -1) {
const inspectArgv = execArgv[inspectArgvIndex];
@ -15,13 +16,14 @@ export default function start({ scriptPath }) {
inspectArgv.replace(/--inspect-brk=(.*)/, (match, s1) => {
let port;
try {
port = parseInt(s1, 10) + 1;
} catch (e) {
port = Number.parseInt(s1, 10) + 1;
}
catch (e) {
port = 9230; // node default inspect port plus 1.
}
if (usedPorts.includes(port)) {
if (usedPorts.includes(port))
port += 1;
}
usedPorts.push(port);
return `--inspect-brk=${port}`;
}),
@ -29,10 +31,8 @@ export default function start({ scriptPath }) {
}
// set port to env when current port has value
if (CURRENT_PORT) {
// @ts-ignore
if (CURRENT_PORT)
process.env.PORT = CURRENT_PORT;
}
const child = fork(scriptPath, process.argv.slice(2), {
execArgv,
@ -45,7 +45,8 @@ export default function start({ scriptPath }) {
start({
scriptPath,
});
} else if (type === 'UPDATE_PORT') {
}
else if (type === 'UPDATE_PORT') {
// set current used port
CURRENT_PORT = data.port;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,6 @@
/* eslint-disable import/extensions */
// 关闭 import 规则
/* eslint import/no-extraneous-dependencies: 0 */
import path from 'path';
import fs from 'fs';
import path from 'node:path';
import process from 'node:process';
import fs from 'node:fs';
import fse from 'fs-extra';
import chalk from 'chalk';
import merge from 'deepmerge';
@ -50,29 +47,28 @@ function getPkgSourcePath(pkgName) {
}
function getOutputPath(config, pkgName) {
if (config.target === 'browser') {
if (config.target === 'browser')
return path.join(getPkgPath(pkgName), ESM_OUTPUT_DIR);
}
return path.join(getPkgPath(pkgName), NODE_CJS_OUTPUT_DIR);
}
function getGlobalConfig() {
if (fs.existsSync(GLOBAL_CONFIG_PATH)) {
if (fs.existsSync(GLOBAL_CONFIG_PATH))
return merge(DEFAULT_CONFIG, buildConfig);
}
return DEFAULT_CONFIG;
}
async function getPkgConfig(config, pkgName) {
const pkgConfigPath = path.join(getPkgPath(pkgName), CONFIG_FILE_NAME);
if (argv.watch) {
if (argv.watch)
config.sourceMap = true;
}
if (fs.existsSync(pkgConfigPath)) {
const content = await import(process.platform === 'win32' ? `file://${pkgConfigPath}` : pkgConfigPath);
const result = merge(config, content.default);
result.resolveCopy = result.copy.map((item) => path.join(getPkgPath(pkgName), 'src', item));
result.resolveCopy = result.copy.map(item => path.join(getPkgPath(pkgName), 'src', item));
return result;
}
@ -81,9 +77,9 @@ async function getPkgConfig(config, pkgName) {
function getNeedCompilerPkg(config) {
// 用户通过 cli 指定的包,优先级最高
if (argv.pkg) {
if (argv.pkg)
return Array.isArray(argv.pkg) ? argv.pkg : argv.pkg;
}
// 默认编译所有 packages
if (!config.pkgs?.length) {
const pkgs = fs.readdirSync(PACKAGE_PATH);
@ -111,18 +107,20 @@ function transformFile(filePath, outputPath, config, log) {
try {
const code = fs.readFileSync(filePath, 'utf-8');
const shortFilePath = genShortPath(filePath);
if (config.sourceMap) {
if (config.sourceMap)
config.sourceFileName = filePath;
}
const transformedCode = compiler(code, config);
const type = config.target === 'browser' ? ESM_OUTPUT_DIR : NODE_CJS_OUTPUT_DIR;
log(`Transform to ${type} for ${config.target === 'browser' ? chalk.yellow(shortFilePath) : chalk.blue(shortFilePath)}`);
fse.outputFileSync(outputPath, transformedCode);
} catch (error) {
}
catch (error) {
console.error(error);
}
} else {
}
else {
fse.copySync(filePath, outputPath);
}
}
@ -135,10 +133,12 @@ function compilerPkg(codeDir, outputDir, config, log) {
const fileStats = fs.lstatSync(filePath);
if (config.copy.includes(file)) {
fse.copySync(filePath, outputFilePath);
} else if (fileStats.isDirectory(filePath) && !/__tests__/.test(file)) {
}
else if (fileStats.isDirectory(filePath) && !/__tests__/.test(file)) {
fse.ensureDirSync(outputFilePath);
compilerPkg(filePath, outputFilePath, config, log);
} else if (fileStats.isFile(filePath)) {
}
else if (fileStats.isFile(filePath)) {
transformFile(filePath, outputFilePath, config, log);
}
});
@ -155,13 +155,14 @@ function watchFile(dir, outputDir, config, log) {
const outputPath = changeFile.replace(dir, outputDir);
const stat = fs.lstatSync(changeFile);
log(`[${event}] ${shortChangeFile}`);
if (config.resolveCopy?.some((item) => changeFile.startsWith(item))) {
if (config.resolveCopy?.some(item => changeFile.startsWith(item)))
fse.copySync(changeFile, outputPath);
} else if (stat.isFile()) {
else if (stat.isFile())
transformFile(changeFile, outputPath, config, log);
} else if (stat.isDirectory()) {
else if (stat.isDirectory())
compilerPkg(changeFile, outputPath, config);
}
});
}

View File

@ -15,7 +15,7 @@ function transformNodeCode(code, config) {
'@babel/preset-env',
{
modules: 'cjs',
targets: { node: '12' },
targets: { node: '16' },
},
],
],
@ -42,11 +42,11 @@ function transformBrowserCode(code) {
}
export default function compiler(code, config) {
if (!config.target || config.target === 'node') {
if (!config.target || config.target === 'node')
return transformNodeCode(code, config);
}
if (config.target === 'browser') {
if (config.target === 'browser')
return transformBrowserCode(code);
}
throw new Error(`config target error: ${config.target}, only can use 'node' and 'browser'`);
}

View File

@ -25,11 +25,11 @@ export default function (pkg) {
const color = colors[index];
const str = chalk[color].bold(pkg);
cache[pkg] = str;
if (index === colors.length - 1) {
if (index === colors.length - 1)
index = 0;
} else {
else
index += 1;
}
}
return cache[pkg];
}

View File

@ -1,13 +1,13 @@
import fs from 'fs';
import * as url from 'url';
import path from 'path';
import fs from 'node:fs';
import process from 'node:process';
import * as url from 'node:url';
import path from 'node:path';
import minimist from 'minimist';
import chalk from 'chalk';
import semver from 'semver';
import enquirer from 'enquirer';
import { execa } from 'execa';
// eslint-disable-next-line import/extensions
import buildConfig from '../build.config.js';
const { prompt } = enquirer;
@ -18,39 +18,39 @@ const packages = buildConfig.pkgs;
const versionIncrements = ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease'];
const incVersion = (version, i) => {
function incVersion(version, i) {
let _preId = preId || semver.prerelease(version)?.[0];
if (!_preId && /pre/.test(i)) {
if (!_preId && /pre/.test(i))
_preId = 'beta';
}
return semver.inc(version, i, _preId);
};
const autoIncVersion = (version) => {
if (version.includes('-')) {
}
function autoIncVersion(version) {
if (version.includes('-'))
return semver.inc(version, 'prerelease');
}
return semver.inc(version, 'patch');
};
}
const run = (bin, args, opts = {}) => execa(bin, args, { stdio: 'inherit', ...opts });
const dryRun = (bin, args, opts = {}) => console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts);
const runIfNotDry = isDryRun ? dryRun : run;
const getPkgRoot = (pkg) => path.resolve(__dirname, `../packages/${pkg}`);
const step = (msg) => console.log(chalk.cyan(msg));
const arrToObj = (arr, key) =>
arr.reduce((acc, cur) => {
const getPkgRoot = pkg => path.resolve(__dirname, `../packages/${pkg}`);
const step = msg => console.log(chalk.cyan(msg));
function arrToObj(arr, key) {
return arr.reduce((acc, cur) => {
acc[cur[key]] = cur;
return acc;
}, {});
}
// eslint-disable-next-line no-shadow
async function publishPackage(pkg, runIfNotDry) {
step(`Publishing ${pkg.name}...`);
try {
let _releaseTag;
if (pkg.newVersion.includes('-')) {
if (pkg.newVersion.includes('-'))
_releaseTag = 'next';
}
await runIfNotDry(
// note: use of pnpm is intentional here as we rely on its publishing
// behavior.
@ -62,12 +62,13 @@ async function publishPackage(pkg, runIfNotDry) {
},
);
console.log('Successfully published :', chalk.green(`${pkg.name}@${pkg.newVersion}`));
} catch (e) {
if (e.stderr.match(/previously published/)) {
console.log(chalk.red(`Skipping already published: ${pkg.name}`));
} else {
throw e;
}
catch (e) {
if (e.stderr.match(/previously published/))
console.log(chalk.red(`Skipping already published: ${pkg.name}`));
else
throw e;
}
}
@ -78,7 +79,7 @@ function readPackageJson(pkg) {
function writePackageJson(pkg, content) {
const pkgPath = getPkgRoot(pkg);
fs.writeFileSync(path.join(pkgPath, 'package.json'), `${JSON.stringify(content, null, 2)}\n`);
fs.writeFileSync(path.join(pkgPath, 'package.json'), `${JSON.stringify(content, null, 4)}\n`);
}
function readPackageVersionAndName(pkg) {
@ -92,17 +93,15 @@ function readPackageVersionAndName(pkg) {
function updatePackage(pkgName, version, pkgs) {
const pkgJson = readPackageJson(pkgName);
pkgJson.version = version;
pkgJson.dependencies &&
Object.keys(pkgJson.dependencies).forEach((npmName) => {
if (pkgs[npmName]) {
pkgJson.dependencies
&& Object.keys(pkgJson.dependencies).forEach((npmName) => {
if (pkgs[npmName])
pkgJson.dependencies[npmName] = `^${pkgs[npmName].newVersion}`;
}
});
pkgJson.peerDependencies &&
Object.keys(pkgJson.peerDependencies).forEach((npmName) => {
if (pkgs[npmName]) {
pkgJson.peerDependencies
&& Object.keys(pkgJson.peerDependencies).forEach((npmName) => {
if (pkgs[npmName])
pkgJson.peerDependencies[npmName] = `^${pkgs[npmName].newVersion}`;
}
});
writePackageJson(pkgName, pkgJson);
}
@ -111,20 +110,20 @@ function updateRootVersion(newRootVersion) {
const pkgPath = path.resolve(path.resolve(__dirname, '..'), 'package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
pkg.version = newRootVersion;
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 4)}\n`);
}
function updateVersions(packagesVersion) {
const pkgs = arrToObj(packagesVersion, 'name');
packagesVersion.forEach((p) => updatePackage(p.dirName, p.newVersion, pkgs));
packagesVersion.forEach(p => updatePackage(p.dirName, p.newVersion, pkgs));
}
const isChangeInCurrentTag = async (pkg, newestTag) => {
async function isChangeInCurrentTag(pkg, newestTag) {
const { stdout: pkgDiffContent } = await run('git', ['diff', newestTag, `packages/${pkg}`], { stdio: 'pipe' });
return !!pkgDiffContent;
};
}
const filterChangedPackages = async () => {
async function filterChangedPackages() {
const { stdout: newestTag } = await run('git', ['describe', '--abbrev=0', '--tags'], { stdio: 'pipe' });
const results = await Promise.all(
@ -135,7 +134,7 @@ const filterChangedPackages = async () => {
);
return packages.filter((_v, index) => results[index]);
};
}
async function createPackageNewVersion(name, version) {
// no explicit version, offer suggestions
@ -143,7 +142,7 @@ async function createPackageNewVersion(name, version) {
type: 'select',
name: 'release',
message: `Select release type: ${name}`,
choices: versionIncrements.map((i) => `${i} (${incVersion(version, i)})`).concat(['custom']),
choices: versionIncrements.map(i => `${i} (${incVersion(version, i)})`).concat(['custom']),
});
let newVersion;
@ -156,7 +155,8 @@ async function createPackageNewVersion(name, version) {
initial: version,
})
).version;
} else {
}
else {
newVersion = release.match(/\((.*)\)/)[1];
}
@ -176,7 +176,7 @@ async function genRootPackageVersion() {
}
function genOtherPkgsVersion(packagesVersion) {
const noChangedPkgs = packages.filter((name) => !packagesVersion.find((item) => item.dirName === name));
const noChangedPkgs = packages.filter(name => !packagesVersion.find(item => item.dirName === name));
const pkgs = arrToObj(packagesVersion, 'name');
const result = [];
noChangedPkgs.forEach((currentPkg) => {
@ -234,13 +234,12 @@ async function main() {
type: 'confirm',
name: 'yes',
message: `These packages will be released: \n${packagesVersion
.map((pkg) => `${chalk.magenta(pkg.name)}: v${pkg.version} > ${chalk.green(`v${pkg.newVersion}`)}`)
.map(pkg => `${chalk.magenta(pkg.name)}: v${pkg.version} > ${chalk.green(`v${pkg.newVersion}`)}`)
.join('\n')}\nConfirm?`,
});
if (!yes) {
if (!yes)
return;
}
const newRootVersion = await genRootPackageVersion();
@ -253,11 +252,11 @@ async function main() {
await run('pnpm', ['i']);
// // build all packages with types
step('\nBuilding all packages...');
if (!isDryRun) {
if (!isDryRun)
await run('pnpm', ['build']);
} else {
else
console.log(`(skipped build)`);
}
// generate changelog
step('\nGenerating changelog...');
@ -268,15 +267,15 @@ async function main() {
step('\nCommitting changes...');
await runIfNotDry('git', ['add', '-A']);
await runIfNotDry('git', ['commit', '-m', `chore: v${newRootVersion}`]);
} else {
}
else {
console.log('No changes to commit.');
}
// publish packages
step('\nPublishing packages...');
for (const pkg of packagesVersion) {
for (const pkg of packagesVersion)
await publishPackage(pkg, runIfNotDry);
}
// push to GitHub
step('\nPushing to GitHub...');
@ -284,9 +283,9 @@ async function main() {
await runIfNotDry('git', ['push', 'origin', `refs/tags/v${newRootVersion}`]);
await runIfNotDry('git', ['push']);
if (isDryRun) {
if (isDryRun)
console.log(`\nDry run finished - run git diff to see package changes.`);
}
console.log();
}