feat: 升级vite8 (#276)

Co-authored-by: qlin <qlin@webank.com>
This commit is contained in:
qlin 2026-04-28 20:45:13 +08:00 committed by GitHub
parent 6868e85a9b
commit b7308f445e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
74 changed files with 5520 additions and 5448 deletions

37
cliff.toml Normal file
View File

@ -0,0 +1,37 @@
[changelog]
header = """
# Changelog\n
"""
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}](https://github.com/WeBankFinTech/fes.js/compare/{{ previous.version }}...{{ version }}) ({{ timestamp | date(format="%Y-%m-%d") }})
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
* {{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}](https://github.com/WeBankFinTech/fes.js/commit/{{ commit.id }}))
{% endfor %}
{% endfor %}\n
"""
trim = true
[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
commit_parsers = [
{ message = "^feat", group = "Features" },
{ message = "^fix", group = "Bug Fixes" },
{ message = "^perf", group = "Performance Improvements" },
{ message = "^doc", group = "Documentation" },
{ message = "^style", group = "Styles" },
{ message = "^refactor", group = "Code Refactoring" },
{ message = "^test", group = "Tests" },
{ message = "^chore", group = "Chores" },
{ message = "^revert", group = "Reverts" },
]
filter_commits = false
tag_pattern = "v[0-9].*"
sort_tags = "newest"

View File

@ -28,30 +28,28 @@
"docs:build-pages": "BASE=fes.js vitepress build docs", "docs:build-pages": "BASE=fes.js vitepress build docs",
"test": "fes test", "test": "fes test",
"lint": "eslint --ignore-pattern='templates'", "lint": "eslint --ignore-pattern='templates'",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", "changelog": "git-cliff -o CHANGELOG.md",
"hooks:sync": "simple-git-hooks" "hooks:sync": "simple-git-hooks"
}, },
"dependencies": { "dependencies": {
"consola": "^3.4.2", "consola": "^3.4.2",
"conventional-changelog-cli": "^5.0.0", "execa": "^9.6.1",
"execa": "^6.1.0",
"minimist": "^1.2.6", "minimist": "^1.2.6",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"semver": "^7.3.6", "semver": "^7.3.6",
"tsup": "^8.5.0", "tsup": "^8.5.0",
"turbo": "^2.5.6" "turbo": "^2.9.6"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^5.2.2", "@antfu/eslint-config": "^8.2.0",
"@commitlint/cli": "^18.4.4", "@commitlint/cli": "^20.5.0",
"@commitlint/config-conventional": "^18.4.4", "@commitlint/config-conventional": "^20.5.0",
"chokidar": "^3.5.3", "chokidar": "^5.0.0",
"commitizen": "^4.3.1",
"cz-conventional-changelog": "^3.3.0",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"eslint": "^9.34.0", "eslint": "^9.34.0",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",
"lint-staged": "^15.2.0", "git-cliff": "^2.12.0",
"lint-staged": "^16.4.0",
"simple-git-hooks": "^2.9.0", "simple-git-hooks": "^2.9.0",
"typescript": "^5.9.2", "typescript": "^5.9.2",
"vitepress": "1.0.0-alpha.73", "vitepress": "1.0.0-alpha.73",
@ -65,10 +63,5 @@
"*.{js,jsx,vue,ts}": [ "*.{js,jsx,vue,ts}": [
"npm run lint" "npm run lint"
] ]
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
} }
} }

View File

@ -36,29 +36,30 @@
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"@rollup/pluginutils": "^5.1.0", "@vitejs/plugin-basic-ssl": "^2.3.0",
"@vitejs/plugin-basic-ssl": "^2.1.0", "@vitejs/plugin-legacy": "^8.0.1",
"@vitejs/plugin-legacy": "^7.2.1", "@vitejs/plugin-vue": "^6.0.6",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue-jsx": "^5.1.5",
"@vitejs/plugin-vue-jsx": "^5.1.1",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"colorette": "^2.0.16", "colorette": "^2.0.16",
"connect-history-api-fallback": "^2.0.0", "connect-history-api-fallback": "^2.0.0",
"consola": "^3.4.2", "consola": "^3.4.2",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"dotenv-expand": "^8.0.2", "dotenv-expand": "^8.0.2",
"ejs": "^3.1.6", "ejs": "^5.0.2",
"fast-glob": "^3.2.11", "fast-glob": "^3.2.11",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",
"html-minifier-terser": "^7.2.0", "html-minifier-terser": "^7.2.0",
"less": "^4.2.0", "less": "^4.2.0",
"minimatch": "^10.0.1",
"node-html-parser": "^5.3.3", "node-html-parser": "^5.3.3",
"pathe": "^0.2.0", "pathe": "^2.0.3",
"picocolors": "^1.1.1",
"postcss-flexbugs-fixes": "^5.0.2", "postcss-flexbugs-fixes": "^5.0.2",
"postcss-safe-parser": "^6.0.0", "postcss-safe-parser": "^6.0.0",
"rollup-plugin-visualizer": "^6.0.3",
"terser": "^5.24.0", "terser": "^5.24.0",
"vite": "^7.1.4" "vite": "^8.0.9",
"vite-bundle-analyzer": "^1.3.7"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"
} }

View File

@ -7,16 +7,6 @@ import postcssFlexbugsFixes from 'postcss-flexbugs-fixes';
import postcssSafeParser from 'postcss-safe-parser'; import postcssSafeParser from 'postcss-safe-parser';
import { getInnerCommonConfig } from '../../common/getConfig'; import { getInnerCommonConfig } from '../../common/getConfig';
function getEsbuildTarget(targets: any): string[] {
const result: string[] = [];
['chrome', 'edge', 'firefox', 'hermes', 'ios', 'node', 'opera', 'rhino', 'safari'].forEach((key) => {
if (targets[key]) {
result.push(`${key}${targets[key]}`);
}
});
return result;
}
export default async (api: IPluginAPI<ViteBuildConfig>): Promise<InlineConfig> => { export default async (api: IPluginAPI<ViteBuildConfig>): Promise<InlineConfig> => {
const { deepmerge, getTargetsAndBrowsersList } = api.utils; const { deepmerge, getTargetsAndBrowsersList } = api.utils;

View File

@ -2,6 +2,7 @@ import type { IPluginAPI } from '@fesjs/shared';
import type { ViteDevServer } from 'vite'; import type { ViteDevServer } from 'vite';
import process from 'node:process'; import process from 'node:process';
import { createServer } from 'vite'; import { createServer } from 'vite';
import pc from 'picocolors';
import getDevConfig from './getDevConfig'; import getDevConfig from './getDevConfig';
interface Args { interface Args {
@ -14,7 +15,7 @@ interface Args {
export default (api: IPluginAPI) => { export default (api: IPluginAPI) => {
const { const {
paths, paths,
utils: { chalk, rimraf }, utils: { rimraf },
} = api; } = api;
let server: ViteDevServer | undefined; let server: ViteDevServer | undefined;
@ -63,7 +64,7 @@ export default (api: IPluginAPI) => {
name: 'restartServer', name: 'restartServer',
fn() { fn() {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.gray('Try to restart dev server...')); console.log(pc.gray('Try to restart dev server...'));
destroy(); destroy();
if (typeof process !== 'undefined' && process.send) { if (typeof process !== 'undefined' && process.send) {
process.send({ process.send({

View File

@ -1,14 +1,14 @@
import type { ConfigEnv, Plugin, ResolvedConfig, ViteDevServer } from 'vite'; import type { ConfigEnv, Plugin, ResolvedConfig, ViteDevServer } from 'vite';
import process from 'node:process'; import process from 'node:process';
import { createFilter } from '@rollup/pluginutils';
import { dim } from 'colorette'; import { dim } from 'colorette';
import consola from 'consola'; import consola from 'consola';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import { expand } from 'dotenv-expand'; import { expand } from 'dotenv-expand';
import { render } from 'ejs'; import ejs from 'ejs';
import fg from 'fast-glob'; import fg from 'fast-glob';
import fse from 'fs-extra'; import fse from 'fs-extra';
import { minify } from 'html-minifier-terser'; import { minify } from 'html-minifier-terser';
import { minimatch } from 'minimatch';
import { parse } from 'node-html-parser'; import { parse } from 'node-html-parser';
import path, { dirname, join } from 'pathe'; import path, { dirname, join } from 'pathe';
import { normalizePath } from 'vite'; import { normalizePath } from 'vite';
@ -119,7 +119,7 @@ function createPlugin(userOptions: UserOptions = {}): Plugin {
if (input) { if (input) {
return { return {
build: { build: {
rollupOptions: { rolldownOptions: {
input, input,
}, },
}, },
@ -271,7 +271,7 @@ async function renderHtml(html: string, config: any): Promise<string> {
...(env || {}), ...(env || {}),
...data, ...data,
}; };
let result = await render(html, ejsData, ejsOptions); let result = await ejs.render(html, ejsData, ejsOptions);
if (entry) { if (entry) {
result = removeEntryScript(result, verbose); result = removeEntryScript(result, verbose);
result = result.replace(bodyInjectRE, `<script type="module" src="${normalizePath(`${entry}`)}"></script></body>`); result = result.replace(bodyInjectRE, `<script type="module" src="${normalizePath(`${entry}`)}"></script></body>`);
@ -292,7 +292,7 @@ function getPage(userOptions: UserOptions, name: string, viteConfig: ResolvedCon
} }
function isMpa(viteConfig: ResolvedConfig | undefined): boolean { function isMpa(viteConfig: ResolvedConfig | undefined): boolean {
const input = viteConfig?.build?.rollupOptions?.input ?? undefined; const input = viteConfig?.build?.rolldownOptions?.input ?? undefined;
return typeof input !== 'string' && Object.keys(input || {}).length > 1; return typeof input !== 'string' && Object.keys(input || {}).length > 1;
} }
@ -352,7 +352,7 @@ function createRewire(reg: string, page: Page, baseUrl: string, proxyUrlKeys: st
}; };
} }
const htmlFilter = createFilter(['**/*.html']); const htmlFilter = (id: string) => minimatch(id, '**/*.html');
function getOptions(_minify: boolean) { function getOptions(_minify: boolean) {
return { return {

View File

@ -1,6 +1,6 @@
import type { IPluginAPI } from '@fesjs/shared'; import type { IPluginAPI } from '@fesjs/shared';
import process from 'node:process'; import process from 'node:process';
import { visualizer } from 'rollup-plugin-visualizer'; import { analyzer } from 'vite-bundle-analyzer';
export default (api: IPluginAPI<{ viteAnalyze: Record<string, any> }>) => { export default (api: IPluginAPI<{ viteAnalyze: Record<string, any> }>) => {
api.describe({ api.describe({
@ -16,11 +16,10 @@ export default (api: IPluginAPI<{ viteAnalyze: Record<string, any> }>) => {
api.modifyBundleConfig((memo: any) => { api.modifyBundleConfig((memo: any) => {
memo.plugins.push( memo.plugins.push(
visualizer({ analyzer({
filename: './.cache/visualizer/stats.html', analyzerMode: 'static',
open: true, fileName: './.cache/visualizer/stats',
gzipSize: true, openAnalyzer: true,
brotliSize: true,
...api.config.viteAnalyze, ...api.config.viteAnalyze,
}), }),
); );

View File

@ -44,7 +44,7 @@
"@babel/preset-typescript": "^7.27.1", "@babel/preset-typescript": "^7.27.1",
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"@vue/babel-plugin-jsx": "^1.5.0", "@vue/babel-plugin-jsx": "^2.0.1",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"babel-loader": "^10.0.0", "babel-loader": "^10.0.0",
@ -54,6 +54,7 @@
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^5.0.0", "css-minimizer-webpack-plugin": "^5.0.0",
"es-toolkit": "^1.46.0",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",
"get-folder-size": "^5.0.0", "get-folder-size": "^5.0.0",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
@ -61,6 +62,7 @@
"less": "^4.1.3", "less": "^4.1.3",
"less-loader": "^11.1.0", "less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.9.4", "mini-css-extract-plugin": "^2.9.4",
"picocolors": "^1.1.1",
"postcss": "^8.4.33", "postcss": "^8.4.33",
"postcss-flexbugs-fixes": "^5.0.2", "postcss-flexbugs-fixes": "^5.0.2",
"postcss-loader": "^7.1.0", "postcss-loader": "^7.1.0",

View File

@ -1,5 +1,5 @@
import type { WebpackBuildConfig } from '../../../shared'; import type { WebpackBuildConfig } from '../../../shared';
import { chalk } from '@fesjs/utils'; import pc from 'picocolors';
import webpack from 'webpack'; import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server'; import WebpackDevServer from 'webpack-dev-server';
@ -68,13 +68,13 @@ export function startDevServer({ webpackConfig, host, port, proxy, https, before
const server = new WebpackDevServer(options, compiler); const server = new WebpackDevServer(options, compiler);
if (options.host === '0.0.0.0') { if (options.host === '0.0.0.0') {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.green(' ➜ Local: '), chalk.cyan(`${options.server}://127.0.0.1:${options.port}`)); console.log(pc.green(' ➜ Local: '), pc.cyan(`${options.server}://127.0.0.1:${options.port}`));
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.gray(' ➜ Network: '), chalk.gray(`${options.server}://${options.host}:${options.port}`)); console.log(pc.gray(' ➜ Network: '), pc.gray(`${options.server}://${options.host}:${options.port}`));
} }
else { else {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.green(' ➜ :Local: '), chalk.cyan(`${options.server}://${options.host}:${options.port}`)); console.log(pc.green(' ➜ :Local: '), pc.cyan(`${options.server}://${options.host}:${options.port}`));
} }
server.startCallback((err) => { server.startCallback((err) => {
if (err) { if (err) {

View File

@ -5,6 +5,7 @@ import path from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { removeSync } from 'fs-extra/esm'; import { removeSync } from 'fs-extra/esm';
import getFolderSize from 'get-folder-size'; import getFolderSize from 'get-folder-size';
import pc from 'picocolors';
import { cleanTmpPathExceptCache, getBundleAndConfigs } from '../../common/buildDevUtils'; import { cleanTmpPathExceptCache, getBundleAndConfigs } from '../../common/buildDevUtils';
import connectHistoryMiddleware from './connectHistoryMiddleware'; import connectHistoryMiddleware from './connectHistoryMiddleware';
import { startDevServer } from './devServer'; import { startDevServer } from './devServer';
@ -29,7 +30,7 @@ async function handleCacheClean(cwd: string) {
export default (api: IPluginAPI<WebpackBuildConfig>) => { export default (api: IPluginAPI<WebpackBuildConfig>) => {
const { const {
paths, paths,
utils: { chalk, getPort, getHostName, changePort, logger }, utils: { getPort, getHostName, changePort, logger },
} = api; } = api;
let port: number; let port: number;
@ -112,7 +113,7 @@ export default (api: IPluginAPI<WebpackBuildConfig>) => {
api.registerMethod({ api.registerMethod({
name: 'restartServer', name: 'restartServer',
fn() { fn() {
logger.info(chalk.gray('Try to restart dev server...')); logger.info(pc.gray('Try to restart dev server...'));
destroy(); destroy();
process.send?.({ process.send?.({
type: 'RESTART', type: 'RESTART',

View File

@ -4,7 +4,8 @@ import type { WebpackBuildConfig } from '../../shared';
import { existsSync, readFileSync } from 'node:fs'; import { existsSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path'; import { join, resolve } from 'node:path';
import zlib from 'node:zlib'; import zlib from 'node:zlib';
import { chalk, rimraf } from '@fesjs/utils'; import { rimraf } from '@fesjs/utils';
import pc from 'picocolors';
import UI from 'cliui'; import UI from 'cliui';
import getConfig from './webpackConfig'; import getConfig from './webpackConfig';
@ -168,14 +169,14 @@ export function printFileSizes({ stats, dir }: PrintFileSizesOptions) {
} }
ui.div( ui.div(
`${makeRow(chalk.cyan.bold('File'), chalk.cyan.bold('Size'), chalk.cyan.bold('Gzipped'))}\n\n${orderedAssets `${makeRow(pc.cyan(pc.bold('File')), pc.cyan(pc.bold('Size')), pc.cyan(pc.bold('Gzipped')))}\n\n${orderedAssets
.map((asset: any) => .map((asset: any) =>
makeRow( makeRow(
asset.name.endsWith('js') asset.name.endsWith('js')
? asset.suggested ? asset.suggested
? chalk.yellow(join(dir, asset.name)) ? pc.yellow(join(dir, asset.name))
: chalk.green(join(dir, asset.name)) : pc.green(join(dir, asset.name))
: chalk.blue(join(dir, asset.name)), : pc.blue(join(dir, asset.name)),
filesize(asset.size), filesize(asset.size),
getGzippedSize(asset), getGzippedSize(asset),
), ),
@ -184,17 +185,17 @@ export function printFileSizes({ stats, dir }: PrintFileSizesOptions) {
); );
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`${ui.toString()}\n\n ${chalk.gray('Images and other types of assets omitted.')}\n`); console.log(`${ui.toString()}\n\n ${pc.gray('Images and other types of assets omitted.')}\n`);
if (orderedAssets?.some((asset: any) => asset.suggested)) { if (orderedAssets?.some((asset: any) => asset.suggested)) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(); console.log();
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.yellow('The bundle size is significantly larger than recommended.')); console.log(pc.yellow('The bundle size is significantly larger than recommended.'));
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.yellow('Consider reducing it with code splitting')); console.log(pc.yellow('Consider reducing it with code splitting'));
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.yellow('You can also analyze the project dependencies using ANALYZE=1')); console.log(pc.yellow('You can also analyze the project dependencies using ANALYZE=1'));
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(); console.log();
} }

View File

@ -21,7 +21,7 @@ const DEFAULT_EXCLUDE_NODE_MODULES = [
'core-js', 'core-js',
'echarts', 'echarts',
'@babel/runtime', '@babel/runtime',
'lodash-es', 'es-toolkit',
'webpack-dev-server', 'webpack-dev-server',
'ansi-html', 'ansi-html',
'html-entities', 'html-entities',

View File

@ -1,6 +1,7 @@
import assert from 'node:assert'; import assert from 'node:assert';
import path from 'node:path'; import path from 'node:path';
import { lodash, winPath } from '@fesjs/utils'; import { winPath } from '@fesjs/utils';
import { isPlainObject } from 'es-toolkit/compat';
interface SpecifierObject { interface SpecifierObject {
local: string; local: string;
@ -40,7 +41,7 @@ export default function generateExports(basePath: string, { item, fesExportsHook
fesExportsHook[specifier] = true; fesExportsHook[specifier] = true;
return specifier; return specifier;
} }
assert(lodash.isPlainObject(specifier), `Configure item context should be Plain Object, but got ${specifier}.`); assert(isPlainObject(specifier), `Configure item context should be Plain Object, but got ${specifier}.`);
assert((specifier as SpecifierObject).local && (specifier as SpecifierObject).exported, 'local and exported should be supplied.'); assert((specifier as SpecifierObject).local && (specifier as SpecifierObject).exported, 'local and exported should be supplied.');
return `${(specifier as SpecifierObject).local} as ${(specifier as SpecifierObject).exported}`; return `${(specifier as SpecifierObject).local} as ${(specifier as SpecifierObject).exported}`;
}); });

View File

@ -33,11 +33,14 @@
"@babel/core": "^7.28.3", "@babel/core": "^7.28.3",
"@babel/preset-env": "^7.28.3", "@babel/preset-env": "^7.28.3",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"chokidar": "^5.0.0",
"commander": "^7.0.0", "commander": "^7.0.0",
"dotenv": "8.2.0", "dotenv": "8.2.0",
"es-toolkit": "^1.46.0",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",
"joi": "17.3.0", "joi": "17.3.0",
"package-up": "^5.0.0", "package-up": "^5.0.0",
"picocolors": "^1.1.1",
"tapable": "^2.2.0" "tapable": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -4,8 +4,11 @@ import { existsSync } from 'node:fs';
import { extname, join } from 'node:path'; import { extname, join } from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { pathToFileURL } from 'node:url'; import { pathToFileURL } from 'node:url';
import { chalk, chokidar, compatESModuleRequire, deepmerge, lodash, winPath } from '@fesjs/utils'; import { compatESModuleRequire, deepmerge, winPath } from '@fesjs/utils';
import * as chokidar from 'chokidar';
import { clone, difference } from 'es-toolkit/compat';
import joi from 'joi'; import joi from 'joi';
import pc from 'picocolors';
import { ServiceStage } from '../service/enums'; import { ServiceStage } from '../service/enums';
import { getUserConfigWithKey, updateUserConfigWithKey } from './utils/configUtils'; import { getUserConfigWithKey, updateUserConfigWithKey } from './utils/configUtils';
import isEqual from './utils/isEqual'; import isEqual from './utils/isEqual';
@ -185,7 +188,7 @@ export default class Config {
getWatchFilesAndDirectories(): string[] { getWatchFilesAndDirectories(): string[] {
const fesEnv = process.env.FES_ENV; const fesEnv = process.env.FES_ENV;
const configFiles = lodash.clone(CONFIG_FILES); const configFiles = clone(CONFIG_FILES);
CONFIG_FILES.forEach((f) => { CONFIG_FILES.forEach((f) => {
if (this.localConfig) { if (this.localConfig) {
configFiles.push(this.addAffix(f, 'local')); configFiles.push(this.addAffix(f, 'local'));
@ -217,9 +220,9 @@ export default class Config {
}); });
watcher.on('all', async (event, path) => { watcher.on('all', async (event, path) => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.green(`[${event}] ${path}`)); console.log(pc.green(`[${event}] ${path}`));
const newPaths = this.getWatchFilesAndDirectories(); const newPaths = this.getWatchFilesAndDirectories();
const diffs = lodash.difference(newPaths, paths); const diffs = difference(newPaths, paths);
if (diffs.length) { if (diffs.length) {
watcher.add(diffs); watcher.add(diffs);
paths = paths.concat(diffs); paths = paths.concat(diffs);

View File

@ -1,5 +1,5 @@
import type { UserConfig } from '../../types'; import type { UserConfig } from '../../types';
import { lodash } from '@fesjs/utils'; import { set, get } from 'es-toolkit/compat';
interface UpdateUserConfigWithKeyOptions { interface UpdateUserConfigWithKeyOptions {
key: string; key: string;
@ -17,12 +17,12 @@ export function updateUserConfigWithKey({
value, value,
userConfig, userConfig,
}: UpdateUserConfigWithKeyOptions): void { }: UpdateUserConfigWithKeyOptions): void {
lodash.set(userConfig, key, value); set(userConfig, key, value);
} }
export function getUserConfigWithKey({ export function getUserConfigWithKey({
key, key,
userConfig, userConfig,
}: GetUserConfigWithKeyOptions): any { }: GetUserConfigWithKeyOptions): any {
return lodash.get(userConfig, key); return get(userConfig, key);
} }

View File

@ -1,10 +1,10 @@
import { lodash } from '@fesjs/utils'; import { isPlainObject, isEqual as deepIsEqual } from 'es-toolkit/compat';
function funcToStr(obj: any): any { function funcToStr(obj: any): any {
if (typeof obj === 'function') { if (typeof obj === 'function') {
return obj.toString(); return obj.toString();
} }
if (lodash.isPlainObject(obj)) { if (isPlainObject(obj)) {
return Object.keys(obj).reduce((memo: Record<string, any>, key: string) => { return Object.keys(obj).reduce((memo: Record<string, any>, key: string) => {
memo[key] = funcToStr(obj[key]); memo[key] = funcToStr(obj[key]);
return memo; return memo;
@ -14,5 +14,5 @@ function funcToStr(obj: any): any {
} }
export default function isEqual(a: any, b: any): boolean { export default function isEqual(a: any, b: any): boolean {
return lodash.isEqual(funcToStr(a), funcToStr(b)); return deepIsEqual(funcToStr(a), funcToStr(b));
} }

View File

@ -1,4 +1,5 @@
import { deepmerge, lodash } from '@fesjs/utils'; import { deepmerge } from '@fesjs/utils';
import { isPlainObject } from 'es-toolkit/compat';
interface MergeDefaultOptions { interface MergeDefaultOptions {
defaultConfig: any; defaultConfig: any;
@ -6,7 +7,7 @@ interface MergeDefaultOptions {
} }
export default function mergeDefault({ defaultConfig, config }: MergeDefaultOptions): any { export default function mergeDefault({ defaultConfig, config }: MergeDefaultOptions): any {
if (lodash.isPlainObject(defaultConfig) && lodash.isPlainObject(config)) { if (isPlainObject(defaultConfig) && isPlainObject(config)) {
return deepmerge(defaultConfig, config); return deepmerge(defaultConfig, config);
} }
return typeof config !== 'undefined' ? config : defaultConfig; return typeof config !== 'undefined' ? config : defaultConfig;

View File

@ -1,7 +1,8 @@
import type { Paths, UserConfig } from '../types'; import type { Paths, UserConfig } from '../types';
import { existsSync, statSync } from 'node:fs'; import { existsSync, statSync } from 'node:fs';
import { join } from 'node:path'; import { join } from 'node:path';
import { lodash, winPath } from '@fesjs/utils'; import { winPath } from '@fesjs/utils';
import { mapValues } from 'es-toolkit/compat';
interface GetServicePathsOptions { interface GetServicePathsOptions {
cwd: string; cwd: string;
@ -14,7 +15,7 @@ function isDirectoryAndExist(path: string): boolean {
} }
function normalizeWithWinPath(obj: Record<string, string>): Record<string, string> { function normalizeWithWinPath(obj: Record<string, string>): Record<string, string> {
return lodash.mapValues(obj, value => winPath(value)); return mapValues(obj, value => winPath(value));
} }
export default function getServicePaths({ cwd, config, env }: GetServicePathsOptions): Paths { export default function getServicePaths({ cwd, config, env }: GetServicePathsOptions): Paths {

View File

@ -15,7 +15,8 @@ import { EventEmitter } from 'node:events';
import { existsSync } from 'node:fs'; import { existsSync } from 'node:fs';
import { join } from 'node:path'; import { join } from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { chalk, lodash } from '@fesjs/utils'; import pc from 'picocolors';
import { clone } from 'es-toolkit/compat';
import { Command, Option } from 'commander'; import { Command, Option } from 'commander';
import { readJSONSync } from 'fs-extra/esm'; import { readJSONSync } from 'fs-extra/esm';
import { AsyncSeriesWaterfallHook } from 'tapable'; import { AsyncSeriesWaterfallHook } from 'tapable';
@ -375,7 +376,7 @@ export default class Service extends EventEmitter {
} }
// 深度优先 // 深度优先
const extraPresets = lodash.clone(this._extraPresets); const extraPresets = clone(this._extraPresets);
this._extraPresets = []; this._extraPresets = [];
while (extraPresets.length) { while (extraPresets.length) {
await this.initPreset(extraPresets.shift()!); await this.initPreset(extraPresets.shift()!);
@ -542,7 +543,7 @@ export default class Service extends EventEmitter {
command command
.usage('<command> [options]') .usage('<command> [options]')
.version(`@fesjs/fes ${this.fesPkg.version || ''}`, '-v, --vers', 'output the current version') .version(`@fesjs/fes ${this.fesPkg.version || ''}`, '-v, --vers', 'output the current version')
.description(chalk.cyan('一个好用的前端应用解决方案')); .description(pc.cyan('一个好用的前端应用解决方案'));
return command; return command;
} }
@ -599,7 +600,7 @@ export default class Service extends EventEmitter {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(); console.log();
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(` Run ${chalk.cyan('fes <command> --help')} for detailed usage of given command.`); console.log(` Run ${pc.cyan('fes <command> --help')} for detailed usage of given command.`);
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(); console.log();
}); });

View File

@ -7,6 +7,7 @@ import type {
} from '../types'; } from '../types';
import assert from 'node:assert'; import assert from 'node:assert';
import * as utils from '@fesjs/utils'; import * as utils from '@fesjs/utils';
import { isPlainObject } from 'es-toolkit/compat';
import { EnableBy, PluginType, ServiceStage } from './enums'; import { EnableBy, PluginType, ServiceStage } from './enums';
import { isValidPlugin, pathToObj } from './utils/pluginUtils'; import { isValidPlugin, pathToObj } from './utils/pluginUtils';
@ -135,7 +136,7 @@ export default class PluginAPI {
|| function (hookFn: any) { || function (hookFn: any) {
const hook: Partial<Hook> = { const hook: Partial<Hook> = {
key: name, key: name,
...(utils.lodash.isPlainObject(hookFn) ? hookFn : { fn: hookFn }), ...(isPlainObject(hookFn) ? hookFn : { fn: hookFn }),
}; };
// @ts-expect-error this // @ts-expect-error this
this.register(hook as Hook); this.register(hook as Hook);

View File

@ -2,7 +2,9 @@ import type { Plugin } from '../../types';
import { basename, dirname, extname, join, relative } from 'node:path'; import { basename, dirname, extname, join, relative } from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { pathToFileURL } from 'node:url'; import { pathToFileURL } from 'node:url';
import { chalk, compatESModuleRequire, lodash, resolve, winPath } from '@fesjs/utils'; import { compatESModuleRequire, resolve, winPath } from '@fesjs/utils';
import pc from 'picocolors';
import { camelCase } from 'es-toolkit/compat';
import { readJSONSync } from 'fs-extra/esm'; import { readJSONSync } from 'fs-extra/esm';
import { packageUp } from 'package-up'; import { packageUp } from 'package-up';
import { OWNER_DIR } from '../../shared'; import { OWNER_DIR } from '../../shared';
@ -72,7 +74,7 @@ function filterBuilder(opts: FilterBuilderOptions): string[] {
.filter(builder => builder.includes(opts.builder || '')); .filter(builder => builder.includes(opts.builder || ''));
if (builders.length > 1) { if (builders.length > 1) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(chalk.yellow(`提示您使用了多个builder默认使用第一个${builders[0]}`)); console.log(pc.yellow(`提示您使用了多个builder默认使用第一个${builders[0]}`));
return [builders[0]]; return [builders[0]];
} }
return builders; return builders;
@ -115,7 +117,7 @@ export function getPluginsOrPresets(type: PluginType, opts: GetPluginsOrPresetsO
function nameToKey(name: string): string { function nameToKey(name: string): string {
return name return name
.split('.') .split('.')
.map(part => lodash.camelCase(part)) .map(part => camelCase(part))
.join('.'); .join('.');
} }

View File

@ -38,7 +38,7 @@
"citty": "^0.1.6", "citty": "^0.1.6",
"consola": "^3.4.2", "consola": "^3.4.2",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",
"glob": "^11.0.3", "glob": "^13.0.6",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"ora": "^8.2.0", "ora": "^8.2.0",
"semver": "^7.7.2", "semver": "^7.7.2",

View File

@ -17,7 +17,7 @@
"@fesjs/plugin-icon": "^5.0.0", "@fesjs/plugin-icon": "^5.0.0",
"@fesjs/plugin-request": "^5.0.0", "@fesjs/plugin-request": "^5.0.0",
"core-js": "^3.43.0", "core-js": "^3.43.0",
"lodash-es": "^4.17.21", "es-toolkit": "^1.46.0",
"vue": "^3.5.17" "vue": "^3.5.17"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,5 +1,5 @@
import { defineRuntimeConfig } from '@fesjs/fes' import { defineRuntimeConfig } from '@fesjs/fes'
import { isPlainObject } from 'lodash-es' import { isPlainObject } from 'es-toolkit/compat'
export default defineRuntimeConfig({ export default defineRuntimeConfig({
request: { request: {

View File

@ -22,7 +22,7 @@
"@fesjs/plugin-layout": "^6.0.0", "@fesjs/plugin-layout": "^6.0.0",
"@fesjs/plugin-model": "^4.0.0", "@fesjs/plugin-model": "^4.0.0",
"core-js": "^3.43.0", "core-js": "^3.43.0",
"lodash-es": "^4.17.21", "es-toolkit": "^1.46.0",
"vue": "^3.5.17" "vue": "^3.5.17"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,5 +1,5 @@
import { access, defineRuntimeConfig } from '@fesjs/fes' import { access, defineRuntimeConfig } from '@fesjs/fes'
import { isPlainObject } from 'lodash-es' import { isPlainObject } from 'es-toolkit/compat'
import PageLoading from '@/components/pageLoading.vue' import PageLoading from '@/components/pageLoading.vue'
import UserCenter from '@/components/userCenter.vue' import UserCenter from '@/components/userCenter.vue'

View File

@ -7,7 +7,7 @@ export default defineBuildConfig({
__DEV__: false, __DEV__: false,
}, },
publicPath: './', publicPath: './',
title: '海贼王', title: '歌者',
router: { router: {
mode: 'hash', mode: 'hash',
}, },

View File

@ -28,7 +28,7 @@
"@fesjs/plugin-monaco-editor": "workspace:*", "@fesjs/plugin-monaco-editor": "workspace:*",
"@fesjs/plugin-pinia": "workspace:*", "@fesjs/plugin-pinia": "workspace:*",
"@fesjs/plugin-request": "workspace:*", "@fesjs/plugin-request": "workspace:*",
"@tailwindcss/vite": "^4.1.12", "@tailwindcss/vite": "^4.2.4",
"core-js": "^3.45.1", "core-js": "^3.45.1",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"tailwindcss": "^4.1.12", "tailwindcss": "^4.1.12",

View File

@ -1,8 +1,7 @@
export default { export default {
home: '首页', home: '首页',
store: '状态管理', store: '状态管理',
editor: '编辑器', editor: '编辑器',
externalLink: '外部链接', externalLink: '外部链接',
mock: '代理' mock: '代理',
}; };

View File

@ -11,9 +11,6 @@ export default defineBuildConfig({
console: { console: {
version: true, version: true,
}, },
html: {
title: '海贼王',
},
router: { router: {
mode: 'hash', mode: 'hash',
}, },

View File

@ -54,6 +54,7 @@
"@fesjs/preset-built-in": "^4.0.0-beta.0", "@fesjs/preset-built-in": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.1",
"picocolors": "^1.1.1",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,7 +1,8 @@
import type { CliArgs } from './types'; import type { CliArgs } from './types';
import { join } from 'node:path'; import { join } from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { chalk, semver, yParser } from '@fesjs/utils'; import pc from 'picocolors';
import { semver, yParser } from '@fesjs/utils';
import fesPkg from '../package.json'; import fesPkg from '../package.json';
import { Service } from './serviceWithBuiltIn'; import { Service } from './serviceWithBuiltIn';
import fork from './utils/fork'; import fork from './utils/fork';
@ -13,7 +14,7 @@ const requiredVersion = fesPkg.engines.node;
function checkNodeVersion(wanted: string, id: string): void { function checkNodeVersion(wanted: string, id: string): void {
if (!semver.satisfies(process.version, wanted, { includePrerelease: true })) { if (!semver.satisfies(process.version, wanted, { includePrerelease: true })) {
console.log(chalk.red(`You are using Node ${process.version}, but this version of ${id} requires Node ${wanted}.\nPlease upgrade your Node version.`)); console.log(pc.red(`You are using Node ${process.version}, but this version of ${id} requires Node ${wanted}.\nPlease upgrade your Node version.`));
process.exit(1); process.exit(1);
} }
} }
@ -58,7 +59,7 @@ export async function main(): Promise<void> {
} }
} }
catch (e: any) { catch (e: any) {
console.error(chalk.red(e.message)); console.error(pc.red(e.message));
console.error(e.stack); console.error(e.stack);
process.exit(1); process.exit(1);
} }

View File

@ -1,7 +1,8 @@
import type { ServiceInstance } from '@fesjs/compiler'; import type { ServiceInstance } from '@fesjs/compiler';
import type { DevArgs } from './types'; import type { DevArgs } from './types';
import process from 'node:process'; import process from 'node:process';
import { chalk, yParser } from '@fesjs/utils'; import pc from 'picocolors';
import { yParser } from '@fesjs/utils';
import fesPkg from '../package.json'; import fesPkg from '../package.json';
import { Service } from './serviceWithBuiltIn'; import { Service } from './serviceWithBuiltIn';
import getCwd from './utils/getCwd'; import getCwd from './utils/getCwd';
@ -48,7 +49,7 @@ function onSignal(signal: string, service: ServiceInstance): void {
process.once('SIGTERM', () => onSignal('SIGTERM', service)); process.once('SIGTERM', () => onSignal('SIGTERM', service));
} }
catch (e: any) { catch (e: any) {
console.error(chalk.red(e.message)); console.error(pc.red(e.message));
console.error(e.stack); console.error(e.stack);
process.exit(1); process.exit(1);
} }

View File

@ -37,7 +37,7 @@
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"lodash-es": "^4.17.21" "es-toolkit": "^1.46.0"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"
} }

View File

@ -39,7 +39,7 @@ export default (api: IPluginAPI) => {
path: absoluteFilePath, path: absoluteFilePath,
content: Mustache.render(readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'), { content: Mustache.render(readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'), {
REPLACE_ROLES: JSON.stringify(roles), REPLACE_ROLES: JSON.stringify(roles),
lodashPath: 'lodash-es', lodashPath: 'es-toolkit/compat',
}), }),
}); });

View File

@ -35,6 +35,7 @@
}, },
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"glob": "^13.0.6",
"svgo": "^4.0.0" "svgo": "^4.0.0"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"

View File

@ -2,6 +2,7 @@ import type { IPluginAPI } from '@fesjs/shared';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { basename, dirname, join } from 'node:path'; import { basename, dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { globSync } from 'glob';
import optimizeSvg from './optimizeSvg'; import optimizeSvg from './optimizeSvg';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
@ -28,7 +29,8 @@ export default (api: IPluginAPI) => {
let generatedOnce = false; let generatedOnce = false;
api.onGenerateFiles(async () => { api.onGenerateFiles(async () => {
const base = join(api.paths.absSrcPath, 'icons'); const base = join(api.paths.absSrcPath, 'icons');
const iconFiles = api.utils.glob.sync('**/*', { const iconFiles = globSync('**/*', {
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)));

View File

@ -37,8 +37,9 @@
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"lodash-es": "^4.17.21", "glob": "^13.0.6",
"vue-i18n": "^9.0.0" "es-toolkit": "^1.46.0",
"vue-i18n": "^11.3.2"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"
} }

View File

@ -1,4 +1,4 @@
import { merge } from 'lodash-es' import { merge } from 'es-toolkit/compat'
{{#REPLACE_IMPORTS}} {{#REPLACE_IMPORTS}}
import {{importName}} from "{{{path}}}"; import {{importName}} from "{{{path}}}";
{{/REPLACE_IMPORTS}} {{/REPLACE_IMPORTS}}

View File

@ -1,5 +1,6 @@
import { basename, join } from 'node:path'; import { basename, join } from 'node:path';
import { glob, winPath } from '@fesjs/utils'; import { globSync } from 'glob';
import { winPath } from '@fesjs/utils';
const ignore = /\.(d\.ts|\.test\.(js|ts))$/; const ignore = /\.(d\.ts|\.test\.(js|ts))$/;
@ -19,7 +20,7 @@ export function getLocales(cwdArray) {
const map = {}; const map = {};
const files = []; const files = [];
cwdArray.forEach((cwd) => { cwdArray.forEach((cwd) => {
glob.sync('**/*.js', { globSync('**/*.js', {
cwd, cwd,
}) })
.filter(file => !ignore.test(file)) .filter(file => !ignore.test(file))

View File

@ -1,4 +1,4 @@
import type { VueI18n } from 'vue-i18n'; import type { Composer } from 'vue-i18n';
export { useI18n } from 'vue-i18n'; export { useI18n } from 'vue-i18n';
@ -7,7 +7,7 @@ export const locale: {
addLocale: ({ locale, messages }: { locale: string; messages: object }) => void; addLocale: ({ locale, messages }: { locale: string; messages: object }) => void;
getAllLocales: () => string[]; getAllLocales: () => string[];
messages: Record<string, object>; messages: Record<string, object>;
t: VueI18n['t']; t: Composer['t'];
}; };
declare module '@fesjs/fes' { declare module '@fesjs/fes' {
@ -22,6 +22,6 @@ declare module '@fesjs/fes' {
| false; | false;
} }
interface PluginRuntimeConfig { interface PluginRuntimeConfig {
onLocaleChange: (params: { t: VueI18n['t']; locale: string }) => void; onLocaleChange: (params: { t: Composer['t']; locale: string }) => void;
} }
} }

View File

@ -36,7 +36,9 @@
}, },
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0" "@fesjs/utils": "^4.0.0-beta.0",
"es-toolkit": "^1.46.0",
"glob": "^13.0.6"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"
} }

View File

@ -2,6 +2,7 @@ import type { IPluginAPI } from '@fesjs/shared';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path'; import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { uniq } from 'es-toolkit/compat';
import pkg from '../package.json' assert { type: 'json' }; import pkg from '../package.json' assert { type: 'json' };
import { getModels } from './utils/getModels'; import { getModels } from './utils/getModels';
@ -15,7 +16,7 @@ const __dirname = dirname(__filename);
export default (api: IPluginAPI) => { export default (api: IPluginAPI) => {
const { const {
paths, paths,
utils: { lodash, Mustache, winPath }, utils: { Mustache, winPath },
} = api; } = api;
function getModelDir() { function getModelDir() {
@ -28,7 +29,7 @@ export default (api: IPluginAPI) => {
function getAllModels() { function getAllModels() {
const srcModelsPath = getModelsPath(); const srcModelsPath = getModelsPath();
return lodash.uniq([...getModels(srcModelsPath)]); return uniq([...getModels(srcModelsPath)]);
} }
const absCoreFilePath = join(namespace, 'core.js'); const absCoreFilePath = join(namespace, 'core.js');

View File

@ -1,11 +1,10 @@
import { glob } from '@fesjs/utils'; import { globSync } from 'glob';
import { getValidFiles } from '.'; import { getValidFiles } from '.';
export function getModels(cwd: string, pattern?: string) { export function getModels(cwd: string, pattern?: string) {
const files = glob const files = globSync(pattern || '**/*.{js,jsx,ts,tsx}', {
.sync(pattern || '**/*.{js,jsx,ts,tsx}', { cwd,
cwd, })
})
.filter( .filter(
file => !file.endsWith('.d.ts') file => !file.endsWith('.d.ts')
&& !file.endsWith('.test.js') && !file.endsWith('.test.js')

View File

@ -36,10 +36,9 @@
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"lodash-es": "^4.17.21", "es-toolkit": "^1.46.0",
"monaco-editor": "^0.36.1", "monaco-editor": "^0.36.1",
"monaco-editor-webpack-plugin": "^7.1.0", "monaco-editor-webpack-plugin": "^7.1.0"
"vite-plugin-monaco-editor": "^1.1.0"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"
} }

View File

@ -39,7 +39,6 @@ export default (api: IPluginAPI) => {
const absEditorFilePath = join(namespace, 'editor.vue'); const absEditorFilePath = join(namespace, 'editor.vue');
api.onGenerateFiles(() => { api.onGenerateFiles(() => {
// 文件写出
api.writeTmpFile({ api.writeTmpFile({
path: absoluteFilePath, path: absoluteFilePath,
content: Mustache.render(readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'), {}), content: Mustache.render(readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'), {}),
@ -54,13 +53,14 @@ export default (api: IPluginAPI) => {
path: absLoaderFilePath, path: absLoaderFilePath,
content: Mustache.render(readFileSync(join(__dirname, 'runtime/loader.tpl'), 'utf-8'), { content: Mustache.render(readFileSync(join(__dirname, 'runtime/loader.tpl'), 'utf-8'), {
MONACO_EDITOR: 'monaco-editor', MONACO_EDITOR: 'monaco-editor',
IS_VITE: api.builder.name === 'vite',
}), }),
}); });
api.writeTmpFile({ api.writeTmpFile({
path: absEditorFilePath, path: absEditorFilePath,
content: Mustache.render(readFileSync(join(__dirname, 'runtime/editor.tpl'), 'utf-8'), { content: Mustache.render(readFileSync(join(__dirname, 'runtime/editor.tpl'), 'utf-8'), {
LODASH_ES: 'lodash-es', LODASH_ES: 'es-toolkit/compat',
}), }),
}); });
@ -83,11 +83,18 @@ export default (api: IPluginAPI) => {
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`); api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
if (api.builder.name === 'vite') { if (api.builder.name === 'vite') {
api.modifyBundleConfig((config) => { api.modifyBundleConfig((memo) => {
const monacoEditorPlugin = esmRequire('vite-plugin-monaco-editor').default; memo.plugins.push({
config?.plugins?.push(monacoEditorPlugin(api.config?.monacoEditor || {})); name: 'vite:monaco-editor-nls',
enforce: 'pre',
resolveId(id) {
if (id === 'monaco-editor/esm/vs/nls' || id.endsWith('/vs/nls')) {
return { id: 'monaco-editor/esm/vs/nls.js', moduleSideEffects: false };
}
},
});
return memo;
}); });
//
} }
else { else {
api.chainWebpack((webpackConfig) => { api.chainWebpack((webpackConfig) => {

View File

@ -5,7 +5,7 @@
import { import {
computed, ref, watch, onMounted, onBeforeUnmount computed, ref, watch, onMounted, onBeforeUnmount
} from 'vue'; } from 'vue';
import { merge, debounce } from '{{{ LODASH_ES }}}'; import { merge, debounce } from 'es-toolkit/compat';
// eslint-disable-next-line // eslint-disable-next-line
import monaco from './loader'; import monaco from './loader';

View File

@ -1,7 +1,24 @@
{{#IS_VITE}}
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') return new jsonWorker();
if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker();
if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker();
if (label === 'typescript' || label === 'javascript') return new tsWorker();
return new editorWorker();
}
};
{{/IS_VITE}}
import * as monaco from '{{{ MONACO_EDITOR }}}'; import * as monaco from '{{{ MONACO_EDITOR }}}';
import defaultTheme from './theme/default'; import defaultTheme from './theme/default';
// 默认主题
defaultTheme.register(monaco); defaultTheme.register(monaco);
export default monaco; export default monaco;

View File

@ -1,8 +1,7 @@
<template> <template>
<div>hello wrold</div>
<FTabs v-model="activeKey"> <FTabs v-model="activeKey">
<FTabPane name="webpack子应用首页" value="1"> <FTabPane name="webpack子应用首页" value="1">
<!-- <MicroAppWithMemoHistory key="1" name="webpack-micro" url="/webpack" /> --> <MicroAppWithMemoHistory key="1" name="webpack-micro" url="/webpack" />
</FTabPane> </FTabPane>
<FTabPane name="webpack子应用测试页" value="2"> <FTabPane name="webpack子应用测试页" value="2">
<MicroAppWithMemoHistory key="2" name="webpack-micro" url="/webpack/test" /> <MicroAppWithMemoHistory key="2" name="webpack-micro" url="/webpack/test" />

View File

@ -43,9 +43,9 @@
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"address": "^1.1.2", "address": "^1.1.2",
"lodash-es": "^4.17.21", "cheerio": "^1.0.0",
"qiankun": "^2.10.16", "es-toolkit": "^1.46.0",
"vite-plugin-qiankun": "^1.0.15" "qiankun": "^2.10.16"
}, },
"devDependencies": { "devDependencies": {
"npm-run-all": "^4.1.5" "npm-run-all": "^4.1.5"

View File

@ -45,7 +45,7 @@ export default function (api) {
qiankunStateForMicroModelNamespace, qiankunStateForMicroModelNamespace,
HAS_PLUGIN_MODEL: HAS_PLUGIN_MODEL && existsSync(winPath(join(api.paths.absSrcPath, 'models/qiankunStateForMicro.js'))), HAS_PLUGIN_MODEL: HAS_PLUGIN_MODEL && existsSync(winPath(join(api.paths.absSrcPath, 'models/qiankunStateForMicro.js'))),
QIANKUN: 'qiankun', QIANKUN: 'qiankun',
LODASH_ES: 'lodash-es', LODASH_ES: 'es-toolkit/compat',
}), }),
}); });

View File

@ -9,7 +9,7 @@ import {
shallowRef, shallowRef,
} from "vue"; } from "vue";
import { loadMicroApp } from "{{{QIANKUN}}}"; import { loadMicroApp } from "{{{QIANKUN}}}";
import { mergeWith, cloneDeep, isEqual, concat } from "{{{LODASH_ES}}}"; import { mergeWith, cloneDeep, isEqual, concat } from "es-toolkit/compat";
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import { getMasterOptions } from "./masterOptions"; import { getMasterOptions } from "./masterOptions";

View File

@ -3,9 +3,9 @@ import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path'; import { dirname, join } from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { lodash } from '@fesjs/utils'; import { isEqual } from 'es-toolkit/compat';
import vitePluginQiankun from 'vite-plugin-qiankun'; import qiankunPlugin from '../vite-plugin';
import { qiankunStateFromMainModelNamespace } from '../constants'; import { qiankunStateFromMainModelNamespace } from '../constants';
const namespace = 'plugin-qiankun/micro'; const namespace = 'plugin-qiankun/micro';
@ -14,7 +14,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
export function isSlaveEnable(api) { export function isSlaveEnable(api) {
return !!api.userConfig?.qiankun?.micro || lodash.isEqual(api.userConfig?.qiankun, {}) || !!process.env.INITIAL_QIANKUN_MIRCO_OPTIONS; return !!api.userConfig?.qiankun?.micro || isEqual(api.userConfig?.qiankun, {}) || !!process.env.INITIAL_QIANKUN_MIRCO_OPTIONS;
} }
export default function (api) { export default function (api) {
@ -52,6 +52,7 @@ export default function (api) {
const absLifecyclePath = join(namespace, 'lifecycle.js'); const absLifecyclePath = join(namespace, 'lifecycle.js');
const absMicroOptionsPath = join(namespace, 'slaveOptions.js'); const absMicroOptionsPath = join(namespace, 'slaveOptions.js');
const absModelPath = join(namespace, 'qiankunModel.js'); const absModelPath = join(namespace, 'qiankunModel.js');
const absViteHelperPath = join(namespace, 'viteHelper.js');
api.register({ api.register({
key: 'addExtraModels', key: 'addExtraModels',
@ -92,6 +93,26 @@ export default function (api) {
`, `,
}); });
if (api.builder.name === 'vite') {
api.writeTmpFile({
path: absViteHelperPath,
content: `
export const qiankunWindow = typeof window !== 'undefined' ? (window.proxy || window) : {};
export const renderWithQiankun = (qiankunLifeCycle) => {
if (qiankunWindow?.__POWERED_BY_QIANKUN__) {
if (!window.moudleQiankunAppLifeCycles) {
window.moudleQiankunAppLifeCycles = {};
}
if (qiankunWindow.qiankunName) {
window.moudleQiankunAppLifeCycles[qiankunWindow.qiankunName] = qiankunLifeCycle;
}
}
};
`,
});
}
if (HAS_PLUGIN_MODEL) { if (HAS_PLUGIN_MODEL) {
api.writeTmpFile({ api.writeTmpFile({
path: absModelPath, path: absModelPath,
@ -103,11 +124,10 @@ export default function (api) {
api.addRuntimePlugin(() => `@@/${absRuntimePath}`); api.addRuntimePlugin(() => `@@/${absRuntimePath}`);
if (api.builder.name === 'vite') { if (api.builder.name === 'vite') {
// 处理
api.modifyBundleConfig((memo) => { api.modifyBundleConfig((memo) => {
assert(api.pkg.name, 'You should have name in package.json'); assert(api.pkg.name, 'You should have name in package.json');
memo.plugins.push( memo.plugins.push(
vitePluginQiankun(api.pkg.name, { qiankunPlugin(api.pkg.name, {
useDevMode: api.config.qiankun?.micro?.useDevMode, useDevMode: api.config.qiankun?.micro?.useDevMode,
}), }),
); );
@ -115,8 +135,8 @@ export default function (api) {
}); });
api.addEntryImports(() => ({ api.addEntryImports(() => ({
source: `vite-plugin-qiankun/dist/helper`, source: `@@/${absViteHelperPath}`,
specifier: '{ renderWithQiankun, qiankunWindow }', specifier: '{ qiankunWindow, renderWithQiankun }',
})); }));
api.addEntryImports(() => ({ api.addEntryImports(() => ({
@ -137,8 +157,8 @@ export default function (api) {
mount, mount,
update, update,
unmount, unmount,
}) });
if (!qiankunWindow.__POWERED_BY_QIANKUN__) { if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
bootstrap().then(mount); bootstrap().then(mount);
} }
@ -181,7 +201,7 @@ export default function (api) {
export const mount = qiankun_genMount('#${api.config.mountElementId}'); export const mount = qiankun_genMount('#${api.config.mountElementId}');
export const unmount = qiankun_genUnmount(); export const unmount = qiankun_genUnmount();
export const update = qiankun_genUpdate(); export const update = qiankun_genUpdate();
if (!window.__POWERED_BY_QIANKUN__) { if (!window.__POWERED_BY_QIANKUN__) {
bootstrap().then(mount); bootstrap().then(mount);
} }

View File

@ -1,5 +1,5 @@
import { reactive } from 'vue'; import { reactive } from 'vue';
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'es-toolkit/compat'
let initState = reactive({}); let initState = reactive({});
const setModelState = (props) => { const setModelState = (props) => {
// 使用深拷贝去掉主应用数据和子应用数据的引用关系,避免出现副作用。 // 使用深拷贝去掉主应用数据和子应用数据的引用关系,避免出现副作用。

View File

@ -0,0 +1,31 @@
export interface QiankunProps {
container?: HTMLElement;
[x: string]: any;
}
export interface QiankunLifeCycle {
bootstrap: () => void | Promise<void>;
mount: (props: QiankunProps) => void | Promise<void>;
unmount: (props: QiankunProps) => void | Promise<void>;
update: (props: QiankunProps) => void | Promise<void>;
}
export interface QiankunWindow {
__POWERED_BY_QIANKUN__?: boolean;
[x: string]: any;
}
export const qiankunWindow: QiankunWindow = typeof window !== 'undefined' ? ((window as any).proxy || window) : {};
export function renderWithQiankun(qiankunLifeCycle: QiankunLifeCycle) {
if (qiankunWindow?.__POWERED_BY_QIANKUN__) {
if (!(window as any).moudleQiankunAppLifeCycles) {
(window as any).moudleQiankunAppLifeCycles = {};
}
if (qiankunWindow.qiankunName) {
(window as any).moudleQiankunAppLifeCycles[qiankunWindow.qiankunName] = qiankunLifeCycle;
}
}
}
export default renderWithQiankun;

View File

@ -0,0 +1,117 @@
import type { CheerioAPI } from 'cheerio';
import type { Element } from 'domhandler';
import type { PluginOption } from 'vite';
import { load } from 'cheerio';
function createQiankunHelper(qiankunName: string) {
return `
const createDeffer = (hookName) => {
const d = new Promise((resolve, reject) => {
window.proxy && (window.proxy[\`vite\${hookName}\`] = resolve)
})
return props => d.then(fn => fn(props));
}
const bootstrap = createDeffer('bootstrap');
const mount = createDeffer('mount');
const unmount = createDeffer('unmount');
const update = createDeffer('update');
;(global => {
global.qiankunName = '${qiankunName}';
global['${qiankunName}'] = {
bootstrap,
mount,
unmount,
update
};
})(window);
`;
}
function createImportFinallyResolve(qiankunName: string) {
return `
const qiankunLifeCycle = window.moudleQiankunAppLifeCycles && window.moudleQiankunAppLifeCycles['${qiankunName}'];
if (qiankunLifeCycle) {
window.proxy.vitemount((props) => qiankunLifeCycle.mount(props));
window.proxy.viteunmount((props) => qiankunLifeCycle.unmount(props));
window.proxy.vitebootstrap(() => qiankunLifeCycle.bootstrap());
window.proxy.viteupdate((props) => qiankunLifeCycle.update(props));
}
`;
}
export interface MicroOption {
useDevMode?: boolean;
}
function module2DynamicImport($: CheerioAPI, scriptTag: Element | undefined, isProduction: boolean, microOption: MicroOption) {
if (!scriptTag) {
return;
}
const script$ = $(scriptTag);
const moduleSrc = script$.attr('src');
let appendBase = '';
if (microOption.useDevMode && !isProduction) {
appendBase = '(window.proxy ? (window.proxy.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ + \'..\') : \'\') + ';
}
script$.removeAttr('src');
script$.removeAttr('type');
script$.html(`import(${appendBase}'${moduleSrc}')`);
return script$;
}
function qiankunPlugin(qiankunName: string, microOption: MicroOption = {}): PluginOption {
let isProduction = false;
let base = '';
return {
name: 'qiankun-html-transform',
configResolved(config) {
isProduction = config.command === 'build' || config.isProduction;
base = config.base;
},
configureServer(server) {
return () => {
server.middlewares.use((req, res, next) => {
if (isProduction || !microOption.useDevMode) {
next();
return;
}
const end = res.end.bind(res);
(res as any).end = (...args: any[]) => {
let [htmlStr, ...rest] = args;
if (typeof htmlStr === 'string') {
const $ = load(htmlStr);
module2DynamicImport($, $(`script[src="${base}@vite/client"]`).get(0), isProduction, microOption);
htmlStr = $.html();
}
end(htmlStr, ...rest);
};
next();
});
};
},
transformIndexHtml(html: string) {
const $ = load(html);
const moduleTags = $('body script[type=module], head script[crossorigin=""]');
if (!moduleTags || !moduleTags.length) {
return;
}
// Remove modulepreload links — they won't work correctly in qiankun
$('link[rel="modulepreload"]').remove();
const len = moduleTags.length;
moduleTags.each((i, moduleTag) => {
const script$ = module2DynamicImport($, moduleTag, isProduction, microOption);
if (len - 1 === i) {
script$?.html(`${script$.html()}.finally(() => {${createImportFinallyResolve(qiankunName)}})`);
}
});
$('body').append(`<script>${createQiankunHelper(qiankunName)}</script>`);
return $.html();
},
};
}
export default qiankunPlugin;

View File

@ -6,6 +6,7 @@ export default defineConfig({
'src/index.ts', 'src/index.ts',
'src/main/index.ts', 'src/main/index.ts',
'src/micro/index.ts', 'src/micro/index.ts',
'src/vite-plugin/index.ts',
], ],
splitting: false, splitting: false,
sourcemap: false, sourcemap: false,

View File

@ -36,11 +36,11 @@
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"@swc/core": "^1.13.5", "@swc/core": "1.13.5",
"@swc/css": "^0.0.28", "@swc/css": "^0.0.28",
"css-minimizer-webpack-plugin": "^7.0.2", "css-minimizer-webpack-plugin": "^8.0.0",
"swc-loader": "^0.2.6", "swc-loader": "0.2.7",
"swc-plugin-vue-jsx": "^0.4.0", "swc-plugin-vue-jsx": "0.4.0",
"terser-webpack-plugin": "^5.3.14" "terser-webpack-plugin": "^5.5.0"
} }
} }

View File

@ -19,7 +19,7 @@ const DEFAULT_EXCLUDE_NODE_MODULES = [
'core-js', 'core-js',
'echarts', 'echarts',
'@babel/runtime', '@babel/runtime',
'lodash-es', 'es-toolkit',
'webpack-dev-server', 'webpack-dev-server',
'ansi-html', 'ansi-html',
'html-entities', 'html-entities',

View File

@ -36,7 +36,7 @@
"dependencies": { "dependencies": {
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"lodash-es": "^4.17.21" "es-toolkit": "^1.46.0"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"
} }

View File

@ -38,10 +38,13 @@
"@fesjs/runtime": "^4.0.0-beta.0", "@fesjs/runtime": "^4.0.0-beta.0",
"@fesjs/shared": "^4.0.0-beta.0", "@fesjs/shared": "^4.0.0-beta.0",
"@fesjs/utils": "^4.0.0-beta.0", "@fesjs/utils": "^4.0.0-beta.0",
"@vue/compiler-sfc": "^3.3.4", "@vue/compiler-sfc": "^3.5.33",
"@wll8/better-mock": "0.3.3-alpha", "@wll8/better-mock": "0.3.3-alpha",
"chokidar": "^5.0.0",
"envinfo": "^7.7.3", "envinfo": "^7.7.3",
"express": "^4.17.3" "es-toolkit": "^1.46.0",
"express": "^4.17.3",
"glob": "^13.0.6"
}, },
"typings": "./types.d.ts" "typings": "./types.d.ts"
} }

View File

@ -1,5 +1,6 @@
import { defineComponent, onBeforeMount, ref, provide, } from 'vue' import { defineComponent, onBeforeMount, ref, provide, } from 'vue'
import { useRouter, RouterView } from 'vue-router' import { useRouter, RouterView } from 'vue-router'
import { getHistory } from './core/routes/routeExports';
import { plugin } from './core/plugin'; import { plugin } from './core/plugin';
import { updateInitialState } from './initialState'; import { updateInitialState } from './initialState';
import { ApplyPluginsType } from '{{{ runtimePath }}}'; import { ApplyPluginsType } from '{{{ runtimePath }}}';
@ -66,7 +67,7 @@ export default function getRootContainer(_routes, _plugin) {
plugin.applyPlugins({ plugin.applyPlugins({
key: 'onRouterCreated', key: 'onRouterCreated',
type: ApplyPluginsType.event, type: ApplyPluginsType.event,
args: { router }, args: { router, history: getHistory() },
}); });
}) })

View File

@ -1,9 +1,10 @@
import { readdirSync, readFileSync, statSync } from 'node:fs'; import { readdirSync, readFileSync, statSync } from 'node:fs';
import { basename, dirname, extname, join } from 'node:path'; import { basename, dirname, extname, join } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { generate, lodash, logger, parser, winPath } from '@fesjs/utils'; import { generate, logger, parser, winPath } from '@fesjs/utils';
import { parse } from '@vue/compiler-sfc'; import { parse } from '@vue/compiler-sfc';
import { camelCase, cloneDeep, isEmpty } from 'es-toolkit/compat';
import { runtimePath } from '../../../utils/constants'; import { runtimePath } from '../../../utils/constants';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
@ -152,7 +153,7 @@ function genRoutes(parentRoutes, path, parentRoutePath) {
routeMeta = getRouteMeta(descriptor.script.content) || routeMeta; routeMeta = getRouteMeta(descriptor.script.content) || routeMeta;
} }
// 优先使用 descriptor.script 兼容 script 和 script setup 同时存在的情况 // 优先使用 descriptor.script 兼容 script 和 script setup 同时存在的情况
if (descriptor.scriptSetup && lodash.isEmpty(routeMeta)) { if (descriptor.scriptSetup && isEmpty(routeMeta)) {
routeMeta = getRouteMeta(descriptor.scriptSetup.content) || routeMeta; routeMeta = getRouteMeta(descriptor.scriptSetup.content) || routeMeta;
} }
} }
@ -203,7 +204,7 @@ function genRoutes(parentRoutes, path, parentRoutePath) {
* 42 * 42
* 5*1 * 5*1
* 6/1 * 6/1
* @param {*} routes * @param {*} routes
*/ */
function rank(routes) { function rank(routes) {
@ -252,7 +253,7 @@ function getRoutes({ config, absPagesPath }) {
} }
function genComponentName(component, paths) { function genComponentName(component, paths) {
const componentName = lodash.camelCase(component.replace(paths.absPagesPath, '').replace('.vue', '')); const componentName = camelCase(component.replace(paths.absPagesPath, '').replace('.vue', ''));
if (/^\d+/.test(componentName)) { if (/^\d+/.test(componentName)) {
return `numPage${componentName}`; return `numPage${componentName}`;
} }
@ -268,7 +269,7 @@ function isFunctionComponent(component) {
function getRoutesJSON({ routes, config, paths }) { function getRoutesJSON({ routes, config, paths }) {
// 因为要往 routes 里加无用的信息,所以必须 deep clone 一下,避免污染 // 因为要往 routes 里加无用的信息,所以必须 deep clone 一下,避免污染
const clonedRoutes = lodash.cloneDeep(routes); const clonedRoutes = cloneDeep(routes);
const importList = []; const importList = [];

View File

@ -2,7 +2,8 @@ import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path'; import { resolve } from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { pathToFileURL } from 'node:url'; import { pathToFileURL } from 'node:url';
import { chokidar, lodash } from '@fesjs/utils'; import * as chokidar from 'chokidar';
import { isArray, isFunction, isPlainObject } from 'es-toolkit/compat';
export default (api) => { export default (api) => {
let mockFlag = false; // mock 开关flag let mockFlag = false; // mock 开关flag
@ -22,10 +23,10 @@ export default (api) => {
// 对 array、object 遍历处理 // 对 array、object 遍历处理
function traversalHandler(val, callback) { function traversalHandler(val, callback) {
if (lodash.isArray(val)) { if (isArray(val)) {
val.forEach(callback); val.forEach(callback);
} }
if (lodash.isPlainObject(val)) { if (isPlainObject(val)) {
Object.keys(val).forEach((key) => { Object.keys(val).forEach((key) => {
callback(val[key], key); callback(val[key], key);
}); });
@ -49,7 +50,7 @@ export default (api) => {
} }
if (len === 1) { if (len === 1) {
const newOption = arg[0]; const newOption = arg[0];
if (lodash.isPlainObject(newOption)) { if (isPlainObject(newOption)) {
traversalHandler(newOption, (value, key) => { traversalHandler(newOption, (value, key) => {
if (key === 'headers') { if (key === 'headers') {
traversalHandler(newOption.headers, (headervalue, headerkey) => { traversalHandler(newOption.headers, (headervalue, headerkey) => {
@ -103,7 +104,7 @@ export default (api) => {
// register babel // register babel
const _initFunction = await import(pathToFileURL(mockFile).href); const _initFunction = await import(pathToFileURL(mockFile).href);
const initFunction = _initFunction.default || _initFunction; const initFunction = _initFunction.default || _initFunction;
if (!lodash.isFunction(initFunction)) { if (!isFunction(initFunction)) {
api.logger.info('mock.js should export Function'); api.logger.info('mock.js should export Function');
return; return;
} }
@ -144,10 +145,10 @@ export default (api) => {
res.cookie(name, value, item); res.cookie(name, value, item);
}); });
// do result // do result
if (lodash.isFunction(matchRequet.result)) { if (isFunction(matchRequet.result)) {
matchRequet.result(req, res); matchRequet.result(req, res);
} }
else if (lodash.isArray(matchRequet.result) || lodash.isPlainObject(matchRequet.result)) { else if (isArray(matchRequet.result) || isPlainObject(matchRequet.result)) {
!matchRequet.type && res.type('json'); !matchRequet.type && res.type('json');
res.json(matchRequet.result); res.json(matchRequet.result);
} }
@ -165,7 +166,7 @@ export default (api) => {
api.onStart(async () => { api.onStart(async () => {
// 获取mock配置: 是否打开 // 获取mock配置: 是否打开
mockFlag = lodash.isPlainObject(api.config.mock) ? true : api.config.mock; mockFlag = isPlainObject(api.config.mock) ? true : api.config.mock;
if (!mockFlag) { if (!mockFlag) {
return; return;
} }

View File

@ -1,6 +1,7 @@
import assert from 'node:assert'; import assert from 'node:assert';
import { copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs'; import { copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path'; import { dirname, join } from 'node:path';
import { globSync } from 'glob';
import { startWatch } from './watch/watchMode'; import { startWatch } from './watch/watchMode';
export default function (api) { export default function (api) {
@ -48,7 +49,7 @@ export default function (api) {
assert(api.stage >= api.ServiceStage.pluginReady, 'api.copyTmpFiles() should not execute in register stage.'); assert(api.stage >= api.ServiceStage.pluginReady, 'api.copyTmpFiles() should not execute in register stage.');
assert(path, 'api.copyTmpFiles() should has param path'); assert(path, 'api.copyTmpFiles() should has param path');
assert(namespace, 'api.copyTmpFiles() should has param namespace'); assert(namespace, 'api.copyTmpFiles() should has param namespace');
const files = api.utils.glob.sync('**/*', { const files = globSync('**/*', {
cwd: path, cwd: path,
}); });
files.forEach((file) => { files.forEach((file) => {

View File

@ -1,5 +1,7 @@
import process from 'node:process'; import process from 'node:process';
import { chokidar, getAppPath, lodash, winPath } from '@fesjs/utils'; import { getAppPath, winPath } from '@fesjs/utils';
import * as chokidar from 'chokidar';
import { throttle, uniq } from 'es-toolkit/compat';
import { watchPkg } from './watchPkg'; import { watchPkg } from './watchPkg';
async function generateWhenFilesChange({ api }) { async function generateWhenFilesChange({ api }) {
@ -22,7 +24,7 @@ async function generateWhenFilesChange({ api }) {
}); });
watcher.on( watcher.on(
'all', 'all',
lodash.throttle(async () => { throttle(async () => {
await api.applyPlugins({ await api.applyPlugins({
key: 'onGenerateFiles', key: 'onGenerateFiles',
type: api.ApplyPluginsType.event, type: api.ApplyPluginsType.event,
@ -37,7 +39,7 @@ async function generateWhenFilesChange({ api }) {
type: api.ApplyPluginsType.add, type: api.ApplyPluginsType.add,
initialValue: [paths.absPagesPath, getAppPath(paths.absSrcPath)], initialValue: [paths.absPagesPath, getAppPath(paths.absSrcPath)],
}); });
lodash.uniq(watcherPaths.map(p => winPath(p))).forEach((p) => { uniq(watcherPaths.map(p => winPath(p))).forEach((p) => {
createWatcher(p); createWatcher(p);
}); });

View File

@ -1,7 +1,9 @@
import { existsSync, readFileSync } from 'node:fs'; import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path'; import { join } from 'node:path';
import { isPluginOrPreset, PluginType } from '@fesjs/compiler'; import { isPluginOrPreset, PluginType } from '@fesjs/compiler';
import { chokidar, lodash, winPath } from '@fesjs/utils'; import { winPath } from '@fesjs/utils';
import * as chokidar from 'chokidar';
import { isEqual } from 'es-toolkit/compat';
function getPlugins(opts) { function getPlugins(opts) {
return Object.keys({ return Object.keys({
@ -31,7 +33,7 @@ export function watchPkg(opts) {
}); });
watcher.on('all', () => { watcher.on('all', () => {
const newPlugins = getPluginsFromPkgPath({ pkgPath }); const newPlugins = getPluginsFromPkgPath({ pkgPath });
if (!lodash.isEqual(plugins, newPlugins)) { if (!isEqual(plugins, newPlugins)) {
// 已经重启了,只处理一次就够了 // 已经重启了,只处理一次就够了
opts.onChange(); opts.onChange();
} }

View File

@ -1,6 +1,7 @@
import assert from 'node:assert'; import assert from 'node:assert';
import path from 'node:path'; import path from 'node:path';
import { lodash, winPath } from '@fesjs/utils'; import { winPath } from '@fesjs/utils';
import { isPlainObject } from 'es-toolkit/compat';
const reserveLibrarys = ['fes']; // reserve library const reserveLibrarys = ['fes']; // reserve library
// todo 插件导出内容冲突问题待解决 // todo 插件导出内容冲突问题待解决
@ -22,7 +23,7 @@ export default function generateExports(basePath, { item, fesExportsHook }) {
fesExportsHook[specifier] = true; fesExportsHook[specifier] = true;
return specifier; return specifier;
} }
assert(lodash.isPlainObject(specifier), `Configure item context should be Plain Object, but got ${specifier}.`); assert(isPlainObject(specifier), `Configure item context should be Plain Object, but got ${specifier}.`);
assert(specifier.local && specifier.exported, 'local and exported should be supplied.'); assert(specifier.local && specifier.exported, 'local and exported should be supplied.');
return `${specifier.local} as ${specifier.exported}`; return `${specifier.local} as ${specifier.exported}`;
}); });

View File

@ -33,14 +33,12 @@
"@babel/generator": "^7.28.3", "@babel/generator": "^7.28.3",
"@babel/parser": "^7.28.4", "@babel/parser": "^7.28.4",
"@babel/traverse": "^7.28.4", "@babel/traverse": "^7.28.4",
"chalk": "^4.1.2",
"chokidar": "^3.5.2",
"debug": "^4.3.2", "debug": "^4.3.2",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"glob": "^9.3.2", "es-toolkit": "^1.46.0",
"lodash": "^4.17.21",
"mkdirp": "^2.1.6", "mkdirp": "^2.1.6",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"picocolors": "^1.1.1",
"portfinder": "^1.0.32", "portfinder": "^1.0.32",
"resolve": "^1.20.0", "resolve": "^1.20.0",
"rimraf": "^4.4.1", "rimraf": "^4.4.1",
@ -50,7 +48,6 @@
"devDependencies": { "devDependencies": {
"@types/babel__generator": "^7.27.0", "@types/babel__generator": "^7.27.0",
"@types/babel__traverse": "^7.28.0", "@types/babel__traverse": "^7.28.0",
"@types/lodash": "^4.17.20",
"@types/mustache": "^4.2.6", "@types/mustache": "^4.2.6",
"@types/resolve": "^1.20.6", "@types/resolve": "^1.20.6",
"@types/semver": "^7.7.1" "@types/semver": "^7.7.1"

View File

@ -1,12 +1,8 @@
import { generate } from '@babel/generator'; import { generate } from '@babel/generator';
import * as parser from '@babel/parser'; import * as parser from '@babel/parser';
import traverse from '@babel/traverse'; import traverse from '@babel/traverse';
import chalk from 'chalk';
import * as chokidar from 'chokidar';
import createDebug from 'debug'; import createDebug from 'debug';
import deepmerge from 'deepmerge'; import deepmerge from 'deepmerge';
import glob from 'glob';
import lodash from 'lodash';
import mkdirp from 'mkdirp'; import mkdirp from 'mkdirp';
import Mustache from 'mustache'; import Mustache from 'mustache';
import portfinder from 'portfinder'; import portfinder from 'portfinder';
@ -29,13 +25,9 @@ import stringifyObjValue from './stringifyObjValue';
import winPath from './winPath'; import winPath from './winPath';
export { export {
chalk,
chokidar,
createDebug, createDebug,
deepmerge, deepmerge,
generate, generate,
glob,
lodash,
logger, logger,
mkdirp, mkdirp,
Mustache, Mustache,

View File

@ -1,13 +1,13 @@
import chalk from 'chalk'; import pc from 'picocolors';
export const prefixes = { export const prefixes = {
wait: `${chalk.cyan('wait')} -`, wait: `${pc.cyan('wait')} -`,
error: `${chalk.red('error')} -`, error: `${pc.red('error')} -`,
warn: `${chalk.yellow('warn')} -`, warn: `${pc.yellow('warn')} -`,
ready: `${chalk.green('ready')} -`, ready: `${pc.green('ready')} -`,
info: `${chalk.cyan('info')} -`, info: `${pc.cyan('info')} -`,
event: `${chalk.magenta('event')} -`, event: `${pc.magenta('event')} -`,
debug: `${chalk.gray('debug')} -`, debug: `${pc.gray('debug')} -`,
}; };
export function wait(...message: any[]): void { export function wait(...message: any[]): void {

View File

@ -1,7 +1,7 @@
import lodash from 'lodash'; import { cloneDeep } from 'es-toolkit/compat';
export default (obj: Record<string, any>): Record<string, string> => { export default (obj: Record<string, any>): Record<string, string> => {
const newObj: Record<string, any> = lodash.cloneDeep(obj); const newObj: Record<string, any> = cloneDeep(obj);
for (const key in newObj) { for (const key in newObj) {
if (Object.prototype.hasOwnProperty.call(newObj, key)) { if (Object.prototype.hasOwnProperty.call(newObj, key)) {
newObj[key] = JSON.stringify(newObj[key]); newObj[key] = JSON.stringify(newObj[key]);

10310
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff