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",
"test": "fes test",
"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"
},
"dependencies": {
"consola": "^3.4.2",
"conventional-changelog-cli": "^5.0.0",
"execa": "^6.1.0",
"execa": "^9.6.1",
"minimist": "^1.2.6",
"picocolors": "^1.1.1",
"semver": "^7.3.6",
"tsup": "^8.5.0",
"turbo": "^2.5.6"
"turbo": "^2.9.6"
},
"devDependencies": {
"@antfu/eslint-config": "^5.2.2",
"@commitlint/cli": "^18.4.4",
"@commitlint/config-conventional": "^18.4.4",
"chokidar": "^3.5.3",
"commitizen": "^4.3.1",
"cz-conventional-changelog": "^3.3.0",
"@antfu/eslint-config": "^8.2.0",
"@commitlint/cli": "^20.5.0",
"@commitlint/config-conventional": "^20.5.0",
"chokidar": "^5.0.0",
"deepmerge": "^4.2.2",
"eslint": "^9.34.0",
"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",
"typescript": "^5.9.2",
"vitepress": "1.0.0-alpha.73",
@ -65,10 +63,5 @@
"*.{js,jsx,vue,ts}": [
"npm run lint"
]
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}

View File

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

View File

@ -7,16 +7,6 @@ import postcssFlexbugsFixes from 'postcss-flexbugs-fixes';
import postcssSafeParser from 'postcss-safe-parser';
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> => {
const { deepmerge, getTargetsAndBrowsersList } = api.utils;

View File

@ -2,6 +2,7 @@ import type { IPluginAPI } from '@fesjs/shared';
import type { ViteDevServer } from 'vite';
import process from 'node:process';
import { createServer } from 'vite';
import pc from 'picocolors';
import getDevConfig from './getDevConfig';
interface Args {
@ -14,7 +15,7 @@ interface Args {
export default (api: IPluginAPI) => {
const {
paths,
utils: { chalk, rimraf },
utils: { rimraf },
} = api;
let server: ViteDevServer | undefined;
@ -63,7 +64,7 @@ export default (api: IPluginAPI) => {
name: 'restartServer',
fn() {
// 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();
if (typeof process !== 'undefined' && process.send) {
process.send({

View File

@ -1,14 +1,14 @@
import type { ConfigEnv, Plugin, ResolvedConfig, ViteDevServer } from 'vite';
import process from 'node:process';
import { createFilter } from '@rollup/pluginutils';
import { dim } from 'colorette';
import consola from 'consola';
import dotenv from 'dotenv';
import { expand } from 'dotenv-expand';
import { render } from 'ejs';
import ejs from 'ejs';
import fg from 'fast-glob';
import fse from 'fs-extra';
import { minify } from 'html-minifier-terser';
import { minimatch } from 'minimatch';
import { parse } from 'node-html-parser';
import path, { dirname, join } from 'pathe';
import { normalizePath } from 'vite';
@ -119,7 +119,7 @@ function createPlugin(userOptions: UserOptions = {}): Plugin {
if (input) {
return {
build: {
rollupOptions: {
rolldownOptions: {
input,
},
},
@ -271,7 +271,7 @@ async function renderHtml(html: string, config: any): Promise<string> {
...(env || {}),
...data,
};
let result = await render(html, ejsData, ejsOptions);
let result = await ejs.render(html, ejsData, ejsOptions);
if (entry) {
result = removeEntryScript(result, verbose);
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 {
const input = viteConfig?.build?.rollupOptions?.input ?? undefined;
const input = viteConfig?.build?.rolldownOptions?.input ?? undefined;
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) {
return {

View File

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

View File

@ -44,7 +44,7 @@
"@babel/preset-typescript": "^7.27.1",
"@fesjs/shared": "^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",
"autoprefixer": "^10.4.14",
"babel-loader": "^10.0.0",
@ -54,6 +54,7 @@
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^5.0.0",
"es-toolkit": "^1.46.0",
"fs-extra": "^11.3.1",
"get-folder-size": "^5.0.0",
"html-webpack-plugin": "^5.5.0",
@ -61,6 +62,7 @@
"less": "^4.1.3",
"less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.9.4",
"picocolors": "^1.1.1",
"postcss": "^8.4.33",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-loader": "^7.1.0",

View File

@ -1,5 +1,5 @@
import type { WebpackBuildConfig } from '../../../shared';
import { chalk } from '@fesjs/utils';
import pc from 'picocolors';
import webpack from 'webpack';
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);
if (options.host === '0.0.0.0') {
// 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
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 {
// 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) => {
if (err) {

View File

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

View File

@ -4,7 +4,8 @@ import type { WebpackBuildConfig } from '../../shared';
import { existsSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
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 getConfig from './webpackConfig';
@ -168,14 +169,14 @@ export function printFileSizes({ stats, dir }: PrintFileSizesOptions) {
}
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) =>
makeRow(
asset.name.endsWith('js')
? asset.suggested
? chalk.yellow(join(dir, asset.name))
: chalk.green(join(dir, asset.name))
: chalk.blue(join(dir, asset.name)),
? pc.yellow(join(dir, asset.name))
: pc.green(join(dir, asset.name))
: pc.blue(join(dir, asset.name)),
filesize(asset.size),
getGzippedSize(asset),
),
@ -184,17 +185,17 @@ export function printFileSizes({ stats, dir }: PrintFileSizesOptions) {
);
// 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)) {
// eslint-disable-next-line no-console
console.log();
// 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
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
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
console.log();
}

View File

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

View File

@ -1,6 +1,7 @@
import assert from 'node:assert';
import path from 'node:path';
import { lodash, winPath } from '@fesjs/utils';
import { winPath } from '@fesjs/utils';
import { isPlainObject } from 'es-toolkit/compat';
interface SpecifierObject {
local: string;
@ -40,7 +41,7 @@ export default function generateExports(basePath: string, { item, fesExportsHook
fesExportsHook[specifier] = true;
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.');
return `${(specifier as SpecifierObject).local} as ${(specifier as SpecifierObject).exported}`;
});

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import type { UserConfig } from '../../types';
import { lodash } from '@fesjs/utils';
import { set, get } from 'es-toolkit/compat';
interface UpdateUserConfigWithKeyOptions {
key: string;
@ -17,12 +17,12 @@ export function updateUserConfigWithKey({
value,
userConfig,
}: UpdateUserConfigWithKeyOptions): void {
lodash.set(userConfig, key, value);
set(userConfig, key, value);
}
export function getUserConfigWithKey({
key,
userConfig,
}: 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 {
if (typeof obj === 'function') {
return obj.toString();
}
if (lodash.isPlainObject(obj)) {
if (isPlainObject(obj)) {
return Object.keys(obj).reduce((memo: Record<string, any>, key: string) => {
memo[key] = funcToStr(obj[key]);
return memo;
@ -14,5 +14,5 @@ function funcToStr(obj: any): any {
}
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 {
defaultConfig: any;
@ -6,7 +7,7 @@ interface MergeDefaultOptions {
}
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 typeof config !== 'undefined' ? config : defaultConfig;

View File

@ -1,7 +1,8 @@
import type { Paths, UserConfig } from '../types';
import { existsSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { lodash, winPath } from '@fesjs/utils';
import { winPath } from '@fesjs/utils';
import { mapValues } from 'es-toolkit/compat';
interface GetServicePathsOptions {
cwd: string;
@ -14,7 +15,7 @@ function isDirectoryAndExist(path: string): boolean {
}
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 {

View File

@ -15,7 +15,8 @@ import { EventEmitter } from 'node:events';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
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 { readJSONSync } from 'fs-extra/esm';
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 = [];
while (extraPresets.length) {
await this.initPreset(extraPresets.shift()!);
@ -542,7 +543,7 @@ export default class Service extends EventEmitter {
command
.usage('<command> [options]')
.version(`@fesjs/fes ${this.fesPkg.version || ''}`, '-v, --vers', 'output the current version')
.description(chalk.cyan('一个好用的前端应用解决方案'));
.description(pc.cyan('一个好用的前端应用解决方案'));
return command;
}
@ -599,7 +600,7 @@ export default class Service extends EventEmitter {
// eslint-disable-next-line no-console
console.log();
// 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
console.log();
});

View File

@ -7,6 +7,7 @@ import type {
} from '../types';
import assert from 'node:assert';
import * as utils from '@fesjs/utils';
import { isPlainObject } from 'es-toolkit/compat';
import { EnableBy, PluginType, ServiceStage } from './enums';
import { isValidPlugin, pathToObj } from './utils/pluginUtils';
@ -135,7 +136,7 @@ export default class PluginAPI {
|| function (hookFn: any) {
const hook: Partial<Hook> = {
key: name,
...(utils.lodash.isPlainObject(hookFn) ? hookFn : { fn: hookFn }),
...(isPlainObject(hookFn) ? hookFn : { fn: hookFn }),
};
// @ts-expect-error this
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 process from 'node:process';
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 { packageUp } from 'package-up';
import { OWNER_DIR } from '../../shared';
@ -72,7 +74,7 @@ function filterBuilder(opts: FilterBuilderOptions): string[] {
.filter(builder => builder.includes(opts.builder || ''));
if (builders.length > 1) {
// 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;
@ -115,7 +117,7 @@ export function getPluginsOrPresets(type: PluginType, opts: GetPluginsOrPresetsO
function nameToKey(name: string): string {
return name
.split('.')
.map(part => lodash.camelCase(part))
.map(part => camelCase(part))
.join('.');
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,8 @@
import type { CliArgs } from './types';
import { join } from 'node:path';
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 { Service } from './serviceWithBuiltIn';
import fork from './utils/fork';
@ -13,7 +14,7 @@ const requiredVersion = fesPkg.engines.node;
function checkNodeVersion(wanted: string, id: string): void {
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);
}
}
@ -58,7 +59,7 @@ export async function main(): Promise<void> {
}
}
catch (e: any) {
console.error(chalk.red(e.message));
console.error(pc.red(e.message));
console.error(e.stack);
process.exit(1);
}

View File

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

View File

@ -37,7 +37,7 @@
"dependencies": {
"@fesjs/shared": "^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"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
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))$/;
@ -19,7 +20,7 @@ export function getLocales(cwdArray) {
const map = {};
const files = [];
cwdArray.forEach((cwd) => {
glob.sync('**/*.js', {
globSync('**/*.js', {
cwd,
})
.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';
@ -7,7 +7,7 @@ export const locale: {
addLocale: ({ locale, messages }: { locale: string; messages: object }) => void;
getAllLocales: () => string[];
messages: Record<string, object>;
t: VueI18n['t'];
t: Composer['t'];
};
declare module '@fesjs/fes' {
@ -22,6 +22,6 @@ declare module '@fesjs/fes' {
| false;
}
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": {
"@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"
}

View File

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

View File

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

View File

@ -36,10 +36,9 @@
"dependencies": {
"@fesjs/shared": "^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-webpack-plugin": "^7.1.0",
"vite-plugin-monaco-editor": "^1.1.0"
"monaco-editor-webpack-plugin": "^7.1.0"
},
"typings": "./types.d.ts"
}

View File

@ -39,7 +39,6 @@ export default (api: IPluginAPI) => {
const absEditorFilePath = join(namespace, 'editor.vue');
api.onGenerateFiles(() => {
// 文件写出
api.writeTmpFile({
path: absoluteFilePath,
content: Mustache.render(readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'), {}),
@ -54,13 +53,14 @@ export default (api: IPluginAPI) => {
path: absLoaderFilePath,
content: Mustache.render(readFileSync(join(__dirname, 'runtime/loader.tpl'), 'utf-8'), {
MONACO_EDITOR: 'monaco-editor',
IS_VITE: api.builder.name === 'vite',
}),
});
api.writeTmpFile({
path: absEditorFilePath,
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}`);
if (api.builder.name === 'vite') {
api.modifyBundleConfig((config) => {
const monacoEditorPlugin = esmRequire('vite-plugin-monaco-editor').default;
config?.plugins?.push(monacoEditorPlugin(api.config?.monacoEditor || {}));
api.modifyBundleConfig((memo) => {
memo.plugins.push({
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 {
api.chainWebpack((webpackConfig) => {

View File

@ -5,7 +5,7 @@
import {
computed, ref, watch, onMounted, onBeforeUnmount
} from 'vue';
import { merge, debounce } from '{{{ LODASH_ES }}}';
import { merge, debounce } from 'es-toolkit/compat';
// eslint-disable-next-line
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 defaultTheme from './theme/default';
// 默认主题
defaultTheme.register(monaco);
export default monaco;
export default monaco;

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import {
shallowRef,
} from "vue";
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
import { getMasterOptions } from "./masterOptions";

View File

@ -3,9 +3,9 @@ import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import process from 'node:process';
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';
const namespace = 'plugin-qiankun/micro';
@ -14,7 +14,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
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) {
@ -52,6 +52,7 @@ export default function (api) {
const absLifecyclePath = join(namespace, 'lifecycle.js');
const absMicroOptionsPath = join(namespace, 'slaveOptions.js');
const absModelPath = join(namespace, 'qiankunModel.js');
const absViteHelperPath = join(namespace, 'viteHelper.js');
api.register({
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) {
api.writeTmpFile({
path: absModelPath,
@ -103,11 +124,10 @@ export default function (api) {
api.addRuntimePlugin(() => `@@/${absRuntimePath}`);
if (api.builder.name === 'vite') {
// 处理
api.modifyBundleConfig((memo) => {
assert(api.pkg.name, 'You should have name in package.json');
memo.plugins.push(
vitePluginQiankun(api.pkg.name, {
qiankunPlugin(api.pkg.name, {
useDevMode: api.config.qiankun?.micro?.useDevMode,
}),
);
@ -115,8 +135,8 @@ export default function (api) {
});
api.addEntryImports(() => ({
source: `vite-plugin-qiankun/dist/helper`,
specifier: '{ renderWithQiankun, qiankunWindow }',
source: `@@/${absViteHelperPath}`,
specifier: '{ qiankunWindow, renderWithQiankun }',
}));
api.addEntryImports(() => ({
@ -137,8 +157,8 @@ export default function (api) {
mount,
update,
unmount,
})
});
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
bootstrap().then(mount);
}
@ -181,7 +201,7 @@ export default function (api) {
export const mount = qiankun_genMount('#${api.config.mountElementId}');
export const unmount = qiankun_genUnmount();
export const update = qiankun_genUpdate();
if (!window.__POWERED_BY_QIANKUN__) {
bootstrap().then(mount);
}

View File

@ -1,5 +1,5 @@
import { reactive } from 'vue';
import { cloneDeep } from 'lodash-es'
import { cloneDeep } from 'es-toolkit/compat'
let initState = reactive({});
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/main/index.ts',
'src/micro/index.ts',
'src/vite-plugin/index.ts',
],
splitting: false,
sourcemap: false,

View File

@ -36,11 +36,11 @@
"dependencies": {
"@fesjs/shared": "^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",
"css-minimizer-webpack-plugin": "^7.0.2",
"swc-loader": "^0.2.6",
"swc-plugin-vue-jsx": "^0.4.0",
"terser-webpack-plugin": "^5.3.14"
"css-minimizer-webpack-plugin": "^8.0.0",
"swc-loader": "0.2.7",
"swc-plugin-vue-jsx": "0.4.0",
"terser-webpack-plugin": "^5.5.0"
}
}

View File

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

View File

@ -36,7 +36,7 @@
"dependencies": {
"@fesjs/shared": "^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"
}

View File

@ -38,10 +38,13 @@
"@fesjs/runtime": "^4.0.0-beta.0",
"@fesjs/shared": "^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",
"chokidar": "^5.0.0",
"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"
}

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import assert from 'node:assert';
import { copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { globSync } from 'glob';
import { startWatch } from './watch/watchMode';
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(path, 'api.copyTmpFiles() should has param path');
assert(namespace, 'api.copyTmpFiles() should has param namespace');
const files = api.utils.glob.sync('**/*', {
const files = globSync('**/*', {
cwd: path,
});
files.forEach((file) => {

View File

@ -1,5 +1,7 @@
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';
async function generateWhenFilesChange({ api }) {
@ -22,7 +24,7 @@ async function generateWhenFilesChange({ api }) {
});
watcher.on(
'all',
lodash.throttle(async () => {
throttle(async () => {
await api.applyPlugins({
key: 'onGenerateFiles',
type: api.ApplyPluginsType.event,
@ -37,7 +39,7 @@ async function generateWhenFilesChange({ api }) {
type: api.ApplyPluginsType.add,
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);
});

View File

@ -1,7 +1,9 @@
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
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) {
return Object.keys({
@ -31,7 +33,7 @@ export function watchPkg(opts) {
});
watcher.on('all', () => {
const newPlugins = getPluginsFromPkgPath({ pkgPath });
if (!lodash.isEqual(plugins, newPlugins)) {
if (!isEqual(plugins, newPlugins)) {
// 已经重启了,只处理一次就够了
opts.onChange();
}

View File

@ -1,6 +1,7 @@
import assert from 'node:assert';
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
// todo 插件导出内容冲突问题待解决
@ -22,7 +23,7 @@ export default function generateExports(basePath, { item, fesExportsHook }) {
fesExportsHook[specifier] = true;
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.');
return `${specifier.local} as ${specifier.exported}`;
});

View File

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

View File

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

View File

@ -1,13 +1,13 @@
import chalk from 'chalk';
import pc from 'picocolors';
export const prefixes = {
wait: `${chalk.cyan('wait')} -`,
error: `${chalk.red('error')} -`,
warn: `${chalk.yellow('warn')} -`,
ready: `${chalk.green('ready')} -`,
info: `${chalk.cyan('info')} -`,
event: `${chalk.magenta('event')} -`,
debug: `${chalk.gray('debug')} -`,
wait: `${pc.cyan('wait')} -`,
error: `${pc.red('error')} -`,
warn: `${pc.yellow('warn')} -`,
ready: `${pc.green('ready')} -`,
info: `${pc.cyan('info')} -`,
event: `${pc.magenta('event')} -`,
debug: `${pc.gray('debug')} -`,
};
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> => {
const newObj: Record<string, any> = lodash.cloneDeep(obj);
const newObj: Record<string, any> = cloneDeep(obj);
for (const key in newObj) {
if (Object.prototype.hasOwnProperty.call(newObj, key)) {
newObj[key] = JSON.stringify(newObj[key]);

10310
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff