mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-10-13 18:22:13 +08:00
Compare commits
No commits in common. "v4.0.0-beta.1" and "master" have entirely different histories.
v4.0.0-bet
...
master
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,10 +1,7 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
.history
|
||||
|
||||
.cache
|
||||
.turbo
|
||||
|
||||
.temp
|
||||
.hound
|
||||
.fes
|
||||
|
3
.npmrc
3
.npmrc
@ -1,4 +1,3 @@
|
||||
registry=https://registry.npmmirror.com
|
||||
link-workspace-packages=true
|
||||
prefer-workspace-packages=true
|
||||
auto-install-peers=true
|
||||
shamefully-hoist=true
|
||||
|
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,22 +1,3 @@
|
||||
# [4.0.0-beta.1](https://github.com/WeBankFinTech/fes.js/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2025-09-22)
|
||||
|
||||
|
||||
|
||||
# [4.0.0-beta.0](https://github.com/WeBankFinTech/fes.js/compare/v3.4.12...v4.0.0-beta.0) (2025-09-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 优化模版样式 ([effd137](https://github.com/WeBankFinTech/fes.js/commit/effd1378b44dfdfab47ff46ccf1b7e6e0d4d7e66))
|
||||
* 使用 pathToFileURL 确保 Windows 下路径导入正确 ([ed26dfb](https://github.com/WeBankFinTech/fes.js/commit/ed26dfb39b753f7d28ab1e3d6bd43fb90c972427))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* esm ([c4c081a](https://github.com/WeBankFinTech/fes.js/commit/c4c081ae3a279ec891236460386b48cf7128d737))
|
||||
|
||||
|
||||
|
||||
## [3.4.12](https://github.com/WeBankFinTech/fes.js/compare/v3.4.11...v3.4.12) (2025-06-24)
|
||||
|
||||
|
||||
|
@ -56,10 +56,12 @@ It mainly has the following functions:
|
||||
| [@fesjs/plugin-access](http://fesjs.mumblefe.cn/reference/plugin/plugins/access.html) | Provides the ability to control the permissions of page resources |
|
||||
| [@fesjs/plugin-enums](http://fesjs.mumblefe.cn/reference/plugin/plugins/enums.html#%E4%BB%8B%E7%BB%8D) | Provide unified enumeration access and rich functions to handle enumeration |
|
||||
| [@fesjs/plugin-icon](http://fesjs.mumblefe.cn/reference/plugin/plugins/icon.html#%E4%BB%8B%E7%BB%8D) | svg file is automatically registered as a component |
|
||||
| [@fesjs/plugin-jest](http://fesjs.mumblefe.cn/reference/plugin/plugins/jest.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | Based on `Jest`, provide unit testing and coverage testing capabilities |
|
||||
| [@fesjs/plugin-layout](http://fesjs.mumblefe.cn/reference/plugin/plugins/layout.html) | Simple configuration to have a layout, including navigation and sidebar |
|
||||
| [@fesjs/plugin-locale](http://fesjs.mumblefe.cn/reference/plugin/plugins/locale.html#%E4%BB%8B%E7%BB%8D) | Based on `Vue I18n`, providing internationalization capabilities |
|
||||
| [@fesjs/plugin-model](http://fesjs.mumblefe.cn/reference/plugin/plugins/model.html#%E4%BB%8B%E7%BB%8D) | Simple data management solution |
|
||||
| [@fesjs/plugin-request](http://fesjs.mumblefe.cn/reference/plugin/plugins/request.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | Based on the request encapsulated by `Axios`, built-in functions such as preventing repeated requests, request throttling, and error handling |
|
||||
| [@fesjs/plugin-vuex](http://fesjs.mumblefe.cn/reference/plugin/plugins/vuex.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | Based on `Vuex`, provide state management capabilities |
|
||||
| [@fesjs/plugin-qiankun](http://fesjs.mumblefe.cn/reference/plugin/plugins/qiankun.html#%E4%BB%8B%E7%BB%8D) | Based on `qiankun`, provide microservice capabilities |
|
||||
| [@fesjs/plugin-sass](http://fesjs.mumblefe.cn/reference/plugin/plugins/sass.html#%E4%BB%8B%E7%BB%8D) | Style support sass |
|
||||
| [@fesjs/plugin-monaco-editor](http://fesjs.mumblefe.cn/reference/plugin/plugins/editor.html#%E4%BB%8B%E7%BB%8D) | Provide code editor capability, based on `monaco-editor` (code editor used by VS Code) |
|
||||
|
30
build.config.js
Normal file
30
build.config.js
Normal file
@ -0,0 +1,30 @@
|
||||
export default {
|
||||
pkgs: [
|
||||
'create-fes-app',
|
||||
'fes',
|
||||
'fes-compiler',
|
||||
'fes-preset-built-in',
|
||||
'fes-builder-vite',
|
||||
'fes-builder-webpack',
|
||||
'fes-runtime',
|
||||
'fes-utils',
|
||||
'fes-plugin-access',
|
||||
'fes-plugin-enums',
|
||||
'fes-plugin-icon',
|
||||
'fes-plugin-jest',
|
||||
'fes-plugin-layout',
|
||||
'fes-plugin-locale',
|
||||
'fes-plugin-model',
|
||||
'fes-plugin-monaco-editor',
|
||||
'fes-plugin-qiankun',
|
||||
'fes-plugin-request',
|
||||
'fes-plugin-sass',
|
||||
'fes-plugin-vuex',
|
||||
'fes-plugin-pinia',
|
||||
'fes-plugin-windicss',
|
||||
'fes-plugin-watermark',
|
||||
'fes-plugin-login',
|
||||
'fes-plugin-swc',
|
||||
],
|
||||
copy: [],
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
# 从 3.x 迁移到 4.x
|
||||
|
||||
构建模块从 commonjs 切换到 esm
|
||||
|
||||
## 需调整内容
|
||||
|
||||
- package.json 添加 `"type": "module"`
|
||||
|
||||
## 版本 4.x 的 break
|
||||
|
||||
1. [@fesjs/builder-vite]: vite5 升级到 [vite7](https://cn.vitejs.dev/guide/migration.html)
|
||||
2. [@fesjs/plugin-pinia]: pinia 2.x > [3.x](https://github.com/vuejs/pinia/releases/tag/v3.0.0)
|
||||
|
||||
## 插件
|
||||
|
||||
- 移除插件[@fesjs/plugin-vuex]
|
||||
- 移除插件[@fesjs/plugin-windicss]
|
||||
- 移除插件[@fesjs/plugin-jest]
|
@ -2,11 +2,11 @@
|
||||
import antfu from '@antfu/eslint-config';
|
||||
|
||||
export default await antfu({
|
||||
// TODO: 使用 ignore 代替 cli 命令中的配置
|
||||
stylistic: {
|
||||
indent: 4,
|
||||
quotes: 'single',
|
||||
semi: 'always',
|
||||
ignores: ['*.yaml'],
|
||||
},
|
||||
typescript: true,
|
||||
vue: true,
|
||||
|
8
jest.config.js
Normal file
8
jest.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
module.exports = {
|
||||
testPathIgnorePatterns: [
|
||||
'/node_modules/',
|
||||
'fes-template',
|
||||
'fes-template-h5'
|
||||
]
|
||||
};
|
29
package.json
29
package.json
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "fes.js",
|
||||
"type": "module",
|
||||
"version": "4.0.0-beta.1",
|
||||
"version": "3.4.12",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.14.0",
|
||||
"packageManager": "pnpm@8.6.6",
|
||||
"description": "一个好用的前端管理台快速开发框架",
|
||||
"preferGlobal": true,
|
||||
"workspaces": [
|
||||
@ -19,8 +19,8 @@
|
||||
],
|
||||
"scripts": {
|
||||
"bootstrap": "pnpm i",
|
||||
"dev": "turbo run watch",
|
||||
"build": "turbo run build",
|
||||
"dev": "node scripts/build.mjs --watch",
|
||||
"build": "node scripts/build.mjs",
|
||||
"release": "node scripts/release.mjs",
|
||||
"clean": "rm -rf ./node_modules & rm -rf packages/**/node_modules & rm -rf packages/**/.cache",
|
||||
"docs:dev": "vitepress dev docs",
|
||||
@ -32,30 +32,29 @@
|
||||
"hooks:sync": "simple-git-hooks"
|
||||
},
|
||||
"dependencies": {
|
||||
"consola": "^3.4.2",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"chalk": "^5.0.1",
|
||||
"conventional-changelog-cli": "^4.1.0",
|
||||
"enquirer": "^2.3.6",
|
||||
"execa": "^6.1.0",
|
||||
"minimist": "^1.2.6",
|
||||
"picocolors": "^1.1.1",
|
||||
"semver": "^7.3.6",
|
||||
"tsup": "^8.5.0",
|
||||
"turbo": "^2.5.6"
|
||||
"semver": "^7.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.2.2",
|
||||
"@antfu/eslint-config": "^3.8.0",
|
||||
"@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",
|
||||
"deepmerge": "^4.2.2",
|
||||
"eslint": "^9.34.0",
|
||||
"fs-extra": "^11.3.1",
|
||||
"eslint": "^9.13.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lint-staged": "^15.2.0",
|
||||
"simple-git-hooks": "^2.9.0",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript": "^5.6.3",
|
||||
"vitepress": "1.0.0-alpha.73",
|
||||
"vue": "^3.5.21"
|
||||
"vue": "^3.3.4",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "npx lint-staged",
|
||||
|
@ -1,40 +0,0 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
import type { InlineConfig } from 'vite';
|
||||
import { existsSync } from 'node:fs';
|
||||
import process from 'node:process';
|
||||
import { build } from 'vite';
|
||||
import getBuildConfig from './getBuildConfig';
|
||||
|
||||
export default function (api: IPluginAPI) {
|
||||
const {
|
||||
paths,
|
||||
utils: { rimraf },
|
||||
} = api;
|
||||
|
||||
api.registerCommand({
|
||||
command: 'build',
|
||||
description: 'build application for production',
|
||||
async fn() {
|
||||
rimraf.sync(paths.absTmpPath);
|
||||
|
||||
// generate files
|
||||
await api.applyPlugins({
|
||||
key: 'onGenerateFiles',
|
||||
type: api.ApplyPluginsType.event,
|
||||
});
|
||||
|
||||
const bundleConfig: InlineConfig = await getBuildConfig(api);
|
||||
// clear output path before exec build
|
||||
if (process.env.CLEAR_OUTPUT !== 'none') {
|
||||
if (paths.absOutputPath && existsSync(paths.absOutputPath)) {
|
||||
rimraf.sync(paths.absOutputPath);
|
||||
}
|
||||
}
|
||||
|
||||
await build(bundleConfig);
|
||||
if (process.env.RM_TMPDIR !== 'none') {
|
||||
rimraf.sync(paths.absTmpPath);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import type { Connect } from 'vite';
|
||||
import { join } from 'node:path';
|
||||
import historyFallback from 'connect-history-api-fallback';
|
||||
import { pathExistsSync } from 'fs-extra/esm';
|
||||
|
||||
interface ViteConfig {
|
||||
publicDir: string;
|
||||
}
|
||||
|
||||
interface HistoryParams {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
function proxyMiddleware(viteConfig: ViteConfig, params: HistoryParams): Connect.NextHandleFunction {
|
||||
return (req: any, res: any, next: any) => {
|
||||
const fileName = join(viteConfig.publicDir, req.url);
|
||||
if (req.url.length > 1 && req.url.startsWith('/') && pathExistsSync(fileName)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const history = historyFallback(params);
|
||||
history(req, res, next);
|
||||
};
|
||||
}
|
||||
|
||||
export default proxyMiddleware;
|
@ -1,22 +0,0 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
|
||||
export default (api: IPluginAPI) => {
|
||||
api.describe({
|
||||
key: 'viteOption',
|
||||
config: {
|
||||
schema(joi: any) {
|
||||
return joi.object();
|
||||
},
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
api.describe({
|
||||
key: 'vite',
|
||||
config: {
|
||||
schema(joi: any) {
|
||||
return joi.object();
|
||||
},
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
};
|
@ -1,30 +0,0 @@
|
||||
import { join } from 'node:path';
|
||||
import { OWNER_DIR, ViteBuildConfig } from './shared';
|
||||
|
||||
interface BuilderPlugin {
|
||||
plugins: string[];
|
||||
}
|
||||
|
||||
export { ViteBuildConfig };
|
||||
|
||||
export default function (): BuilderPlugin {
|
||||
return {
|
||||
plugins: [
|
||||
join(OWNER_DIR, 'dist/registerBuilder.mjs'),
|
||||
join(OWNER_DIR, 'dist/registerMethods.mjs'),
|
||||
join(OWNER_DIR, 'dist/registerType.mjs'),
|
||||
|
||||
// bundle configs
|
||||
join(OWNER_DIR, 'dist/features/viteHtml.mjs'),
|
||||
join(OWNER_DIR, 'dist/features/viteOption.mjs'),
|
||||
join(OWNER_DIR, 'dist/features/viteVueJsx.mjs'),
|
||||
join(OWNER_DIR, 'dist/features/viteVuePlugin.mjs'),
|
||||
join(OWNER_DIR, 'dist/features/viteAnalyze.mjs'),
|
||||
join(OWNER_DIR, 'dist/features/viteLegacy.mjs'),
|
||||
|
||||
// commands
|
||||
join(OWNER_DIR, 'dist/commands/build/index.mjs'),
|
||||
join(OWNER_DIR, 'dist/commands/dev/index.mjs'),
|
||||
],
|
||||
};
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
|
||||
export default function (api: IPluginAPI) {
|
||||
api.registerBuilder({
|
||||
name: 'vite',
|
||||
});
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import type { Options as PolyfillOptions } from '@vitejs/plugin-legacy';
|
||||
import type { Options } from '@vitejs/plugin-vue';
|
||||
import type createPlugin from '@vitejs/plugin-vue-jsx';
|
||||
import type { HTMLOptions, UserConfig } from 'vite';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export const OWNER_DIR: string = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
export interface ViteBuildConfig {
|
||||
viteOption?: UserConfig;
|
||||
vite?: UserConfig;
|
||||
viteVuePlugin?: Options;
|
||||
viteVueJsx?: Parameters<typeof createPlugin>[0];
|
||||
viteLegacy?: PolyfillOptions;
|
||||
viteHtml?: HTMLOptions;
|
||||
viteAnalyze?: any;
|
||||
viteOptionConfig?: UserConfig;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": ["@fesjs/typescript-config/base.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,24 +0,0 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: [
|
||||
'src/index.ts',
|
||||
'src/registerBuilder.ts',
|
||||
'src/registerMethods.ts',
|
||||
'src/registerType.ts',
|
||||
'src/features/viteHtml.ts',
|
||||
'src/features/viteOption.ts',
|
||||
'src/features/viteVueJsx.ts',
|
||||
'src/features/viteVuePlugin.ts',
|
||||
'src/features/viteAnalyze.ts',
|
||||
'src/features/viteLegacy.ts',
|
||||
'src/commands/build/index.ts',
|
||||
'src/commands/dev/index.ts',
|
||||
],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
clean: true,
|
||||
dts: true,
|
||||
shims: true,
|
||||
format: ['esm'],
|
||||
});
|
21
packages/builder-vite/types.d.ts
vendored
21
packages/builder-vite/types.d.ts
vendored
@ -1,21 +0,0 @@
|
||||
import type { ServerOptions } from 'vite';
|
||||
// eslint-disable-next-line antfu/no-import-dist
|
||||
import type { ViteBuildConfig } from './dist/index.d.mjs';
|
||||
|
||||
declare module '@fesjs/fes' {
|
||||
interface PluginBuildConfig extends ViteBuildConfig {
|
||||
|
||||
}
|
||||
|
||||
interface FesConfig {
|
||||
terserOptions?: any;
|
||||
inlineLimit?: number;
|
||||
outputPath?: string;
|
||||
proxy?: ServerOptions['proxy'];
|
||||
title?: string;
|
||||
mountElementId?: string;
|
||||
publicPath?: string;
|
||||
alias?: Record<string, string>;
|
||||
autoprefixer?: any;
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { WebpackBuildConfig } from './shared';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export {
|
||||
WebpackBuildConfig,
|
||||
};
|
||||
|
||||
export default function () {
|
||||
return {
|
||||
plugins: [
|
||||
join(__dirname, './plugins/registerBuilder.mjs'),
|
||||
|
||||
// register methods
|
||||
join(__dirname, './plugins/registerMethods.mjs'),
|
||||
join(__dirname, './plugins/registerType.mjs'),
|
||||
|
||||
// bundle configs
|
||||
join(__dirname, './plugins/features/analyze.mjs'),
|
||||
join(__dirname, './plugins/features/chainWebpack.mjs'),
|
||||
join(__dirname, './plugins/features/cssLoader.mjs'),
|
||||
join(__dirname, './plugins/features/copy.mjs'),
|
||||
join(__dirname, './plugins/features/devServer.mjs'),
|
||||
join(__dirname, './plugins/features/devtool.mjs'),
|
||||
join(__dirname, './plugins/features/externals.mjs'),
|
||||
join(__dirname, './plugins/features/exportStatic.mjs'),
|
||||
join(__dirname, './plugins/features/extraBabelPlugins.mjs'),
|
||||
join(__dirname, './plugins/features/extraBabelPresets.mjs'),
|
||||
join(__dirname, './plugins/features/extraPostCSSPlugins.mjs'),
|
||||
join(__dirname, './plugins/features/html.mjs'),
|
||||
join(__dirname, './plugins/features/lessLoader.mjs'),
|
||||
join(__dirname, './plugins/features/postcssLoader.mjs'),
|
||||
join(__dirname, './plugins/features/nodeModulesTransform.mjs'),
|
||||
join(__dirname, './plugins/features/vueLoader.mjs'),
|
||||
join(__dirname, './plugins/features/extraCSS.mjs'),
|
||||
|
||||
// commands
|
||||
join(__dirname, './plugins/commands/build/index.mjs'),
|
||||
join(__dirname, './plugins/commands/dev/index.mjs'),
|
||||
join(__dirname, './plugins/commands/webpack/index.mjs'),
|
||||
],
|
||||
};
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
const pitcher = (code: string): string => code;
|
||||
|
||||
export function pitch(this: any) {
|
||||
if (/&blockType=config/.test(this.resourceQuery)) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export default pitcher;
|
@ -1,28 +0,0 @@
|
||||
import type Config from 'webpack-5-chain';
|
||||
import type { WebpackBuildConfig } from '../../../shared';
|
||||
import { resolveRuntimeEnv, stringifyObjValue } from '@fesjs/utils';
|
||||
import webpack from 'webpack';
|
||||
|
||||
interface CreateDefineWebpackConfigOptions {
|
||||
config: WebpackBuildConfig;
|
||||
publicPath?: string;
|
||||
webpackConfig: Config;
|
||||
}
|
||||
|
||||
export default function createDefineWebpackConfig({ config, publicPath, webpackConfig }: CreateDefineWebpackConfigOptions) {
|
||||
const env = stringifyObjValue(resolveRuntimeEnv(publicPath || ''));
|
||||
|
||||
const define = stringifyObjValue({
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: false,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
||||
...(config as any).define,
|
||||
});
|
||||
|
||||
webpackConfig.plugin('define').use(webpack.DefinePlugin, [
|
||||
{
|
||||
'process.env': env,
|
||||
...define,
|
||||
},
|
||||
]);
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
import type { WebpackBuildConfig } from '../../../shared';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { resolveRuntimeEnv, winPath } from '@fesjs/utils';
|
||||
import { esmResolve } from '../../../shared';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
interface CreateHtmlWebpackConfigOptions {
|
||||
api: IPluginAPI<WebpackBuildConfig>;
|
||||
cwd: string;
|
||||
config: WebpackBuildConfig;
|
||||
webpackConfig: any;
|
||||
headScripts?: () => Promise<Array<{ src: string }>>;
|
||||
isProd: boolean;
|
||||
publicPath?: string;
|
||||
}
|
||||
|
||||
interface Route {
|
||||
path: string;
|
||||
meta?: {
|
||||
title?: string;
|
||||
};
|
||||
children?: Route[];
|
||||
}
|
||||
|
||||
export default async function createHtmlWebpackConfig({ api, cwd, config, webpackConfig, headScripts, isProd, publicPath }: CreateHtmlWebpackConfigOptions) {
|
||||
const htmlOptions: any = {
|
||||
filename: '[name].html',
|
||||
...config.html,
|
||||
templateParameters: {
|
||||
title: (api.config as any).title || config.html?.title || 'fes.js',
|
||||
...resolveRuntimeEnv(publicPath || ''),
|
||||
mountElementId: (config as any).mountElementId,
|
||||
},
|
||||
};
|
||||
|
||||
if (isProd) {
|
||||
Object.assign(htmlOptions, {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
collapseBooleanAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const htmlPath = join(cwd, 'index.html');
|
||||
const defaultHtmlPath = join(__dirname, '../index-default.html');
|
||||
const publicCopyIgnore: string[] = [];
|
||||
|
||||
// default, single page setup.
|
||||
htmlOptions.template = existsSync(htmlPath) ? htmlPath : defaultHtmlPath;
|
||||
|
||||
publicCopyIgnore.push(winPath(htmlOptions.template));
|
||||
|
||||
webpackConfig.plugin('html').use(esmResolve('html-webpack-plugin'), [htmlOptions]);
|
||||
|
||||
// 如果需要导出html,则根据路由生成对应的html文件
|
||||
if ((config as any).exportStatic) {
|
||||
const routes: Route[] = await (api as any).getRoutes();
|
||||
const addHtml = (_routes: Route[] | undefined) => {
|
||||
if (Array.isArray(_routes)) {
|
||||
_routes.forEach((route) => {
|
||||
const _fileName = `${route.path.slice(1) || 'index'}.html`;
|
||||
if (_fileName !== 'index.html') {
|
||||
const _htmlOptions = {
|
||||
...config.html,
|
||||
filename: _fileName,
|
||||
templateParameters: {
|
||||
title: route?.meta?.title || config.html?.title || (api.config as any).title || 'fes.js',
|
||||
...resolveRuntimeEnv(publicPath!),
|
||||
mountElementId: (config as any).mountElementId,
|
||||
},
|
||||
};
|
||||
webpackConfig.plugin(_fileName).use(esmResolve('html-webpack-plugin'), [_htmlOptions]);
|
||||
}
|
||||
if (route.children && route.children.length) {
|
||||
addHtml(route.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
addHtml(routes);
|
||||
}
|
||||
|
||||
if (headScripts) {
|
||||
const headScriptsMap = await headScripts();
|
||||
webpackConfig.plugin('html-tags').use(esmResolve('html-webpack-tags-plugin'), [
|
||||
{
|
||||
append: false,
|
||||
scripts: headScriptsMap.map(script => ({
|
||||
path: script.src,
|
||||
})),
|
||||
},
|
||||
]);
|
||||
}
|
||||
return {
|
||||
publicCopyIgnore,
|
||||
};
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import type Config from 'webpack-5-chain';
|
||||
import type { WebpackBuildConfig } from '../../../shared';
|
||||
import { join } from 'node:path';
|
||||
import { esmRequire, esmResolve, OWNER_DIR } from '../../../shared';
|
||||
|
||||
interface CreateVueWebpackConfigOptions {
|
||||
config: WebpackBuildConfig;
|
||||
webpackConfig: Config;
|
||||
}
|
||||
|
||||
export default function createVueWebpackConfig({ config, webpackConfig }: CreateVueWebpackConfigOptions) {
|
||||
webpackConfig.module
|
||||
.rule('vue')
|
||||
.test(/\.vue$/)
|
||||
.use('vue-loader')
|
||||
.loader(esmResolve('vue-loader'))
|
||||
.options({
|
||||
babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy'],
|
||||
...(config as any).vueLoader || {},
|
||||
})
|
||||
.end();
|
||||
|
||||
webpackConfig.module
|
||||
.rule('vue-custom')
|
||||
.resourceQuery((query: string) => {
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
return query.startsWith('?vue&type=custom');
|
||||
})
|
||||
.use('vue-custom-loader')
|
||||
.loader(join(OWNER_DIR, './pitcher.mjs'));
|
||||
|
||||
webpackConfig.plugin('vue-loader-plugin').use(esmRequire('vue-loader').VueLoaderPlugin);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
|
||||
export default function (api: IPluginAPI) {
|
||||
api.registerBuilder({
|
||||
name: 'webpack',
|
||||
});
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import type HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import type { LoaderOptions, PluginOptions } from 'mini-css-extract-plugin';
|
||||
import type Config from 'webpack-5-chain';
|
||||
import { createRequire } from 'node:module';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export interface CopyFileType {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export const OWNER_DIR: string = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
export const esmRequire = createRequire(import.meta.url);
|
||||
|
||||
export function esmResolve(specifier: string) {
|
||||
const esmRequire = createRequire(import.meta.url);
|
||||
return esmRequire.resolve(specifier);
|
||||
}
|
||||
|
||||
export interface WebpackBuildConfig {
|
||||
analyze?: {
|
||||
analyzerMode?: 'server' | 'static' | 'disabled';
|
||||
analyzerHost?: string;
|
||||
analyzerPort?: number | 'auto';
|
||||
openAnalyzer?: boolean;
|
||||
generateStatsFile?: boolean;
|
||||
statsFilename?: string;
|
||||
logLevel?: 'info' | 'warn' | 'error' | 'silent';
|
||||
defaultSizes?: 'stat' | 'parsed' | 'gzip';
|
||||
};
|
||||
chainWebpack?: (memo: Config, args: any) => void;
|
||||
copy?: CopyFileType | CopyFileType[];
|
||||
cssLoader?: {
|
||||
url?: boolean | ((url: string, resourcePath: string) => boolean);
|
||||
import?: boolean | { filter: (url: string, media: string, resourcePath: string) => boolean };
|
||||
modules?: boolean | string | object;
|
||||
sourceMap?: boolean;
|
||||
importLoaders?: number;
|
||||
onlyLocals?: boolean;
|
||||
esModule?: boolean;
|
||||
localsConvention?: 'asIs' | 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly';
|
||||
};
|
||||
devServer?: {
|
||||
port?: number;
|
||||
host?: string;
|
||||
https?: boolean;
|
||||
headers?: object;
|
||||
[key: string]: any;
|
||||
};
|
||||
devtool?: Config.DevTool;
|
||||
exportStatic?: {
|
||||
htmlSuffix?: boolean;
|
||||
dynamicRoot?: boolean;
|
||||
};
|
||||
externals?: string | ((data: any) => any);
|
||||
extraBabelPlugins?: [];
|
||||
extraBabelPresets?: [];
|
||||
extraPostCSSPlugins?: [];
|
||||
html?: HtmlWebpackPlugin.Options;
|
||||
lessLoader?: Record<string, any>;
|
||||
nodeModulesTransform?: {
|
||||
exclude: string[];
|
||||
};
|
||||
postcssLoader?: Record<string, any>;
|
||||
vueLoader?: object;
|
||||
extraCSS?: {
|
||||
loader?: LoaderOptions;
|
||||
plugin?: PluginOptions;
|
||||
};
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": ["@fesjs/typescript-config/base.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,41 +0,0 @@
|
||||
import { copySync } from 'fs-extra/esm';
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: [
|
||||
'src/index.ts',
|
||||
'src/plugins/commands/pitcher.ts',
|
||||
'src/plugins/registerBuilder.ts',
|
||||
'src/plugins/registerMethods.ts',
|
||||
'src/plugins/registerType.ts',
|
||||
'src/plugins/features/analyze.ts',
|
||||
'src/plugins/features/chainWebpack.ts',
|
||||
'src/plugins/features/cssLoader.ts',
|
||||
'src/plugins/features/copy.ts',
|
||||
'src/plugins/features/devServer.ts',
|
||||
'src/plugins/features/devtool.ts',
|
||||
'src/plugins/features/externals.ts',
|
||||
'src/plugins/features/exportStatic.ts',
|
||||
'src/plugins/features/extraBabelPlugins.ts',
|
||||
'src/plugins/features/extraBabelPresets.ts',
|
||||
'src/plugins/features/extraPostCSSPlugins.ts',
|
||||
'src/plugins/features/html.ts',
|
||||
'src/plugins/features/lessLoader.ts',
|
||||
'src/plugins/features/postcssLoader.ts',
|
||||
'src/plugins/features/nodeModulesTransform.ts',
|
||||
'src/plugins/features/vueLoader.ts',
|
||||
'src/plugins/features/extraCSS.ts',
|
||||
'src/plugins/commands/build/index.ts',
|
||||
'src/plugins/commands/dev/index.ts',
|
||||
'src/plugins/commands/webpack/index.ts',
|
||||
],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
clean: true,
|
||||
dts: true,
|
||||
shims: true,
|
||||
format: ['esm'],
|
||||
onSuccess() {
|
||||
copySync('src/plugins/commands/index-default.html', 'dist/plugins/commands/index-default.html');
|
||||
},
|
||||
});
|
6
packages/builder-webpack/types.d.ts
vendored
6
packages/builder-webpack/types.d.ts
vendored
@ -1,6 +0,0 @@
|
||||
// eslint-disable-next-line antfu/no-import-dist
|
||||
import type { WebpackBuildConfig } from './dist/index.d.mts';
|
||||
|
||||
declare module '@fesjs/fes' {
|
||||
interface PluginBuildConfig extends WebpackBuildConfig {}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import type { UserConfig } from '../../types';
|
||||
import { lodash } from '@fesjs/utils';
|
||||
|
||||
interface UpdateUserConfigWithKeyOptions {
|
||||
key: string;
|
||||
value: any;
|
||||
userConfig: UserConfig;
|
||||
}
|
||||
|
||||
interface GetUserConfigWithKeyOptions {
|
||||
key: string;
|
||||
userConfig: UserConfig;
|
||||
}
|
||||
|
||||
export function updateUserConfigWithKey({
|
||||
key,
|
||||
value,
|
||||
userConfig,
|
||||
}: UpdateUserConfigWithKeyOptions): void {
|
||||
lodash.set(userConfig, key, value);
|
||||
}
|
||||
|
||||
export function getUserConfigWithKey({
|
||||
key,
|
||||
userConfig,
|
||||
}: GetUserConfigWithKeyOptions): any {
|
||||
return lodash.get(userConfig, key);
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { lodash } from '@fesjs/utils';
|
||||
|
||||
function funcToStr(obj: any): any {
|
||||
if (typeof obj === 'function') {
|
||||
return obj.toString();
|
||||
}
|
||||
if (lodash.isPlainObject(obj)) {
|
||||
return Object.keys(obj).reduce((memo: Record<string, any>, key: string) => {
|
||||
memo[key] = funcToStr(obj[key]);
|
||||
return memo;
|
||||
}, {});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export default function isEqual(a: any, b: any): boolean {
|
||||
return lodash.isEqual(funcToStr(a), funcToStr(b));
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import Config from './config';
|
||||
import Service from './service';
|
||||
import { PluginType } from './service/enums';
|
||||
import { isPluginOrPreset } from './service/utils/pluginUtils';
|
||||
|
||||
export { Config, isPluginOrPreset, PluginType, Service };
|
||||
|
||||
export type {
|
||||
ConfigInstance,
|
||||
PluginAPIInstance,
|
||||
ServiceInstance,
|
||||
} from './types';
|
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* 插件类型枚举
|
||||
*/
|
||||
export enum PluginType {
|
||||
preset = 'preset',
|
||||
plugin = 'plugin',
|
||||
builder = 'builder',
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务阶段枚举
|
||||
*/
|
||||
export enum ServiceStage {
|
||||
uninitialized = 0,
|
||||
constructor = 1,
|
||||
init = 2,
|
||||
initPresets = 3,
|
||||
initPlugins = 4,
|
||||
initHooks = 5,
|
||||
pluginReady = 6,
|
||||
getConfig = 7,
|
||||
getPaths = 8,
|
||||
run = 9,
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置变更类型枚举
|
||||
*/
|
||||
export enum ConfigChangeType {
|
||||
reload = 'reload',
|
||||
regenerateTmpFiles = 'regenerateTmpFiles',
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用插件类型枚举
|
||||
*/
|
||||
export enum ApplyPluginsType {
|
||||
add = 'add',
|
||||
modify = 'modify',
|
||||
event = 'event',
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用方式枚举
|
||||
*/
|
||||
export enum EnableBy {
|
||||
register = 'register',
|
||||
config = 'config',
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
import type { Paths, UserConfig } from '../types';
|
||||
import { existsSync, statSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { lodash, winPath } from '@fesjs/utils';
|
||||
|
||||
interface GetServicePathsOptions {
|
||||
cwd: string;
|
||||
config: UserConfig;
|
||||
env: string;
|
||||
}
|
||||
|
||||
function isDirectoryAndExist(path: string): boolean {
|
||||
return existsSync(path) && statSync(path).isDirectory();
|
||||
}
|
||||
|
||||
function normalizeWithWinPath(obj: Record<string, string>): Record<string, string> {
|
||||
return lodash.mapValues(obj, value => winPath(value));
|
||||
}
|
||||
|
||||
export default function getServicePaths({ cwd, config, env }: GetServicePathsOptions): Paths {
|
||||
let absSrcPath = cwd;
|
||||
if (isDirectoryAndExist(join(cwd, 'src'))) {
|
||||
absSrcPath = join(cwd, 'src');
|
||||
}
|
||||
|
||||
const absPagesPath = config.singular ? join(absSrcPath, 'page') : join(absSrcPath, 'pages');
|
||||
|
||||
const tmpDir = ['.fes', env !== 'development' && env].filter(Boolean).join('-');
|
||||
const paths = {
|
||||
tmpDir,
|
||||
cwd,
|
||||
absNodeModulesPath: join(cwd, 'node_modules'),
|
||||
absOutputPath: join(cwd, (config.outputPath as string) || './dist'),
|
||||
absSrcPath,
|
||||
absPagesPath,
|
||||
absTmpPath: join(absSrcPath, tmpDir),
|
||||
};
|
||||
return normalizeWithWinPath(paths) as unknown as Paths;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* 判断对象是否为 Promise
|
||||
* @param obj 待判断的对象
|
||||
* @returns 如果是 Promise 返回 true,否则返回 false
|
||||
*/
|
||||
export default function isPromise(obj: any): obj is Promise<any> {
|
||||
return (
|
||||
!!obj
|
||||
&& (typeof obj === 'object' || typeof obj === 'function')
|
||||
&& typeof obj.then === 'function'
|
||||
);
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
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 { readJSONSync } from 'fs-extra/esm';
|
||||
import { packageUp } from 'package-up';
|
||||
import { OWNER_DIR } from '../../shared';
|
||||
import { PluginType } from '../enums';
|
||||
|
||||
interface FilterBuilderOptions {
|
||||
pkg: Record<string, any>;
|
||||
builder?: string;
|
||||
}
|
||||
|
||||
interface FilterPluginAndPresetOptions {
|
||||
pkg: Record<string, any>;
|
||||
builder?: string;
|
||||
}
|
||||
|
||||
interface GetPluginsOrPresetsOptions {
|
||||
presets?: string[];
|
||||
plugins?: string[];
|
||||
userConfigPresets?: string[];
|
||||
userConfigPlugins?: string[];
|
||||
pkg: Record<string, any>;
|
||||
cwd: string;
|
||||
builder?: string;
|
||||
}
|
||||
|
||||
interface PathToObjOptions {
|
||||
path: string;
|
||||
type: PluginType;
|
||||
cwd: string;
|
||||
}
|
||||
|
||||
interface ResolvePresetsOptions {
|
||||
presets?: string[];
|
||||
userConfigPresets?: string[];
|
||||
builder?: string;
|
||||
pkg: Record<string, any>;
|
||||
cwd: string;
|
||||
}
|
||||
|
||||
interface ResolvePluginsOptions {
|
||||
plugins?: string[];
|
||||
userConfigPlugins?: string[];
|
||||
builder?: string;
|
||||
pkg: Record<string, any>;
|
||||
cwd: string;
|
||||
}
|
||||
|
||||
const RE: Record<PluginType, RegExp> = {
|
||||
[PluginType.plugin]: /^(@fesjs\/|@webank\/fes-|fes-)plugin-(.+)$/,
|
||||
[PluginType.preset]: /^(@fesjs\/|@webank\/fes-|fes-)preset-(.+)$/,
|
||||
[PluginType.builder]: /^(@fesjs\/|@webank\/fes-|fes-)builder-(.+)$/,
|
||||
};
|
||||
|
||||
export function isPluginOrPreset(type: PluginType, name: string): boolean {
|
||||
const hasScope = name.charAt(0) === '@';
|
||||
const re = RE[type];
|
||||
if (hasScope) {
|
||||
return re.test(name.split('/')[1]) || re.test(name);
|
||||
}
|
||||
return re.test(name);
|
||||
}
|
||||
|
||||
function filterBuilder(opts: FilterBuilderOptions): string[] {
|
||||
const builders = Object.keys(opts.pkg.devDependencies || {})
|
||||
.concat(Object.keys(opts.pkg.dependencies || {}))
|
||||
.filter(isPluginOrPreset.bind(null, PluginType.builder))
|
||||
.filter(builder => builder.includes(opts.builder || ''));
|
||||
if (builders.length > 1) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.yellow(`提示:您使用了多个builder,默认使用第一个${builders[0]}`));
|
||||
return [builders[0]];
|
||||
}
|
||||
return builders;
|
||||
}
|
||||
|
||||
function filterPluginAndPreset(type: PluginType, opts: FilterPluginAndPresetOptions): string[] {
|
||||
const base = Object.keys(opts.pkg.devDependencies || {})
|
||||
.concat(Object.keys(opts.pkg.dependencies || {}))
|
||||
.filter(isPluginOrPreset.bind(null, type));
|
||||
if (type === PluginType.preset) {
|
||||
return base.concat(filterBuilder(opts));
|
||||
}
|
||||
if (type === PluginType.plugin) {
|
||||
return base.concat(join(OWNER_DIR, './dist/service/plugins/builder.mjs'));
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
export function getPluginsOrPresets(type: PluginType, opts: GetPluginsOrPresetsOptions): string[] {
|
||||
const upperCaseType = type.toUpperCase();
|
||||
return [
|
||||
// opts
|
||||
...(opts[type === PluginType.preset ? 'presets' : 'plugins'] || []),
|
||||
// env
|
||||
...(process.env[`FES_${upperCaseType}S`] || '').split(',').filter(Boolean),
|
||||
...filterPluginAndPreset(type, opts),
|
||||
// user config
|
||||
...(opts[type === PluginType.preset ? 'userConfigPresets' : 'userConfigPlugins'] || []),
|
||||
].map(path =>
|
||||
resolve.sync(path, {
|
||||
basedir: opts.cwd,
|
||||
extensions: ['.js', '.ts'],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// e.g.
|
||||
// initial-state -> initialState
|
||||
// webpack.css-loader -> webpack.cssLoader
|
||||
function nameToKey(name: string): string {
|
||||
return name
|
||||
.split('.')
|
||||
.map(part => lodash.camelCase(part))
|
||||
.join('.');
|
||||
}
|
||||
|
||||
function pkgNameToKey(pkgName: string, type: PluginType): string {
|
||||
if (pkgName.charAt(0) === '@' && !pkgName.startsWith('@fesjs/')) {
|
||||
pkgName = pkgName.split('/')[1];
|
||||
}
|
||||
return nameToKey(pkgName.replace(RE[type], ''));
|
||||
}
|
||||
|
||||
export async function pathToObj({ path, type, cwd }: PathToObjOptions): Promise<Plugin> {
|
||||
let pkg: Record<string, any>;
|
||||
let isPkgPlugin = false;
|
||||
const pkgJSONPath = await packageUp({ cwd: path });
|
||||
if (pkgJSONPath) {
|
||||
pkg = readJSONSync(pkgJSONPath);
|
||||
isPkgPlugin = winPath(join(dirname(pkgJSONPath), pkg.main || 'index.js')) === winPath(path);
|
||||
}
|
||||
|
||||
let id: string;
|
||||
if (isPkgPlugin) {
|
||||
id = pkg!.name;
|
||||
}
|
||||
else if (winPath(path).startsWith(winPath(cwd))) {
|
||||
id = `./${winPath(relative(cwd, path))}`;
|
||||
}
|
||||
else if (pkgJSONPath) {
|
||||
id = winPath(join(pkg!.name, relative(dirname(pkgJSONPath!), path)));
|
||||
}
|
||||
else {
|
||||
id = winPath(path);
|
||||
}
|
||||
id = id.replace('@fesjs/preset-built-in/dist/plugins', '@@');
|
||||
id = id.replace(/\.js$/, '');
|
||||
|
||||
const key = isPkgPlugin ? pkgNameToKey(pkg!.name, type) : nameToKey(basename(path, extname(path)));
|
||||
|
||||
return {
|
||||
id,
|
||||
key,
|
||||
path: winPath(path),
|
||||
async apply() {
|
||||
try {
|
||||
// 使用 pathToFileURL 确保在 Windows 下路径格式正确
|
||||
const fileUrl = pathToFileURL(path).href;
|
||||
const ret = await import(fileUrl);
|
||||
// use the default member for es modules
|
||||
return compatESModuleRequire(ret);
|
||||
}
|
||||
catch (e: any) {
|
||||
throw new Error(`Register ${path} failed, since ${e.message}`);
|
||||
}
|
||||
},
|
||||
defaultConfig: null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolvePresets(opts: ResolvePresetsOptions): Promise<Plugin[]> {
|
||||
const type = PluginType.preset;
|
||||
const presets = await Promise.all([...getPluginsOrPresets(type, opts)].map(path =>
|
||||
pathToObj({
|
||||
type,
|
||||
path,
|
||||
cwd: opts.cwd,
|
||||
}),
|
||||
));
|
||||
return presets
|
||||
.sort((a, b) => {
|
||||
if (a.id === '@fesjs/preset-built-in') {
|
||||
return -1;
|
||||
}
|
||||
if (b.id === '@fesjs/preset-built-in') {
|
||||
return 1;
|
||||
}
|
||||
if (/^(?:@fesjs\/|@webank\/fes-|fes-)builder-/.test(a.id)) {
|
||||
return -1;
|
||||
}
|
||||
if (/^(?:@fesjs\/|@webank\/fes-|fes-)builder-/.test(b.id)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
export async function resolvePlugins(opts: ResolvePluginsOptions): Promise<Plugin[]> {
|
||||
const type = PluginType.plugin;
|
||||
const plugins = await Promise.all([...getPluginsOrPresets(type, opts)].map(path =>
|
||||
pathToObj({
|
||||
type,
|
||||
path,
|
||||
cwd: opts.cwd,
|
||||
}),
|
||||
));
|
||||
return plugins;
|
||||
}
|
||||
|
||||
export function isValidPlugin(plugin: any): plugin is Plugin {
|
||||
return plugin && plugin.id && plugin.key && typeof plugin.apply === 'function';
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
/**
|
||||
* 获取编译器所有者的目录路径
|
||||
*/
|
||||
export const OWNER_DIR: string = join(dirname(fileURLToPath(import.meta.url)), '..');
|
@ -1,94 +0,0 @@
|
||||
import type Config from './config';
|
||||
import type Service from './service';
|
||||
import type { ApplyPluginsType, EnableBy } from './service/enums';
|
||||
import type PluginAPI from './service/pluginAPI';
|
||||
|
||||
export type ConfigInstance = InstanceType<typeof Config>;
|
||||
export type ServiceInstance = InstanceType<typeof Service>;
|
||||
export type PluginAPIInstance = InstanceType<typeof PluginAPI>;
|
||||
|
||||
// Enums
|
||||
export type { ApplyPluginsType, ConfigChangeType, EnableBy, PluginType, ServiceStage } from './service/enums';
|
||||
|
||||
// Utility types
|
||||
export interface UserConfig {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface Paths {
|
||||
tmpDir: string;
|
||||
cwd: string;
|
||||
absNodeModulesPath: string;
|
||||
absOutputPath: string;
|
||||
absSrcPath: string;
|
||||
absPagesPath: string;
|
||||
absTmpPath: string;
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
id: string;
|
||||
key: string;
|
||||
path: string;
|
||||
apply: () => Promise<any> | any;
|
||||
config?: PluginConfig;
|
||||
enableBy?: EnableBy | (() => boolean);
|
||||
isPreset?: boolean;
|
||||
defaultConfig: any;
|
||||
}
|
||||
|
||||
export interface PluginConfig {
|
||||
schema?: (joi: any) => any;
|
||||
default?: any;
|
||||
}
|
||||
|
||||
export interface Hook {
|
||||
key: string;
|
||||
fn: (...args: any[]) => any;
|
||||
pluginId?: string;
|
||||
stage?: number;
|
||||
before?: string;
|
||||
}
|
||||
|
||||
export interface CommandOption {
|
||||
command: string;
|
||||
description: string;
|
||||
options?: CommandOptionConfig[];
|
||||
fn?: (args: CommandArgs) => Promise<void> | void;
|
||||
}
|
||||
|
||||
export interface CommandOptionConfig {
|
||||
name: string;
|
||||
description: string;
|
||||
default?: any;
|
||||
choices?: string[];
|
||||
}
|
||||
|
||||
export interface CommandArgs {
|
||||
rawArgv: Record<string, any>;
|
||||
args: Record<string, any>;
|
||||
options: Record<string, any>;
|
||||
program: any;
|
||||
}
|
||||
|
||||
export interface ApplyPluginsOptions {
|
||||
key: string;
|
||||
type: ApplyPluginsType;
|
||||
initialValue?: any;
|
||||
args?: any;
|
||||
}
|
||||
|
||||
export interface ResolvePresetsOptions {
|
||||
presets?: string[];
|
||||
userConfigPresets?: string[];
|
||||
builder?: string;
|
||||
pkg: Record<string, any>;
|
||||
cwd: string;
|
||||
}
|
||||
|
||||
export interface ResolvePluginsOptions {
|
||||
plugins?: string[];
|
||||
userConfigPlugins?: string[];
|
||||
builder?: string;
|
||||
pkg: Record<string, any>;
|
||||
cwd: string;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": ["@fesjs/typescript-config/base.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts', 'src/service/plugins/builder.ts'],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
clean: true,
|
||||
dts: true,
|
||||
shims: true,
|
||||
format: ['esm'],
|
||||
});
|
@ -1 +1 @@
|
||||
Used in bin/create-fes-app.mjs to determine if it is in the local debug state.
|
||||
Used in bin/create-fes-app.js to determine if it is in the local debug state.
|
||||
|
@ -35,13 +35,16 @@ Fes.js 是一个好用的前端应用解决方案。提供覆盖编译构建到
|
||||
| [@fesjs/plugin-access](http://fesjs.mumblefe.cn/reference/plugin/plugins/access.html) | 提供对页面资源的权限控制能力 |
|
||||
| [@fesjs/plugin-enums](http://fesjs.mumblefe.cn/reference/plugin/plugins/enums.html#%E4%BB%8B%E7%BB%8D) | 提供统一的枚举存取及丰富的函数来处理枚举 |
|
||||
| [@fesjs/plugin-icon](http://fesjs.mumblefe.cn/reference/plugin/plugins/icon.html#%E4%BB%8B%E7%BB%8D) | svg 文件自动注册为组件 |
|
||||
| [@fesjs/plugin-jest](http://fesjs.mumblefe.cn/reference/plugin/plugins/jest.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | 基于 `Jest`,提供单元测试、覆盖测试能力 |
|
||||
| [ @fesjs/plugin-layout](http://fesjs.mumblefe.cn/reference/plugin/plugins/layout.html) | 简单的配置即可拥有布局,包括导航以及侧边栏 |
|
||||
| [@fesjs/plugin-locale](http://fesjs.mumblefe.cn/reference/plugin/plugins/locale.html#%E4%BB%8B%E7%BB%8D) | 基于 `Vue I18n`,提供国际化能力 |
|
||||
| [@fesjs/plugin-model](http://fesjs.mumblefe.cn/reference/plugin/plugins/model.html#%E4%BB%8B%E7%BB%8D) | 简易的数据管理方案 |
|
||||
| [@fesjs/plugin-request](http://fesjs.mumblefe.cn/reference/plugin/plugins/request.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | 基于 `Axios` 封装的 request,内置防止重复请求、请求节流、错误处理等功能 |
|
||||
| [@fesjs/plugin-vuex](http://fesjs.mumblefe.cn/reference/plugin/plugins/vuex.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | 基于 `Vuex`, 提供状态管理能力 |
|
||||
| [@fesjs/plugin-qiankun](http://fesjs.mumblefe.cn/reference/plugin/plugins/qiankun.html#%E4%BB%8B%E7%BB%8D) | 基于 `qiankun`,提供微服务能力 |
|
||||
| [@fesjs/plugin-sass](http://fesjs.mumblefe.cn/reference/plugin/plugins/sass.html#%E4%BB%8B%E7%BB%8D) | 样式支持 sass |
|
||||
| [@fesjs/plugin-monaco-editor](http://fesjs.mumblefe.cn/reference/plugin/plugins/editor.html#%E4%BB%8B%E7%BB%8D) | 提供代码编辑器能力, 基于`monaco-editor`(VS Code 使用的代码编辑器) |
|
||||
| [@fesjs/plugin-windicss](http://fesjs.mumblefe.cn/reference/plugin/plugins/windicss.html) | 基于 `windicss`,提供原子化 CSS 能力 |
|
||||
| [@fesjs/plugin-pinia](http://fesjs.mumblefe.cn/reference/plugin/plugins/pinia.html) | pinia,状态处理 |
|
||||
| [@fesjs/plugin-watermark](http://fesjs.mumblefe.cn/reference/plugin/plugins/watermark.html) | 水印 |
|
||||
|
||||
|
3
packages/create-fes-app/bin/create-fes-app.js
Executable file
3
packages/create-fes-app/bin/create-fes-app.js
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('../lib/cli');
|
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { runMain } from '../dist/index.mjs';
|
||||
|
||||
runMain();
|
@ -1,50 +1,39 @@
|
||||
{
|
||||
"name": "@fesjs/create-fes-app",
|
||||
"version": "4.0.0-beta.0",
|
||||
"version": "3.0.7",
|
||||
"description": "create a app base on fes.js",
|
||||
"author": "qlin",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"bin",
|
||||
"templates/**/*"
|
||||
],
|
||||
"bin": {
|
||||
"create-fes-app": "bin/create-fes-app.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
||||
"directory": "packages/create-fes-app"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"fes"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"main": "dist/index.mjs",
|
||||
"module": "dist/index.mjs",
|
||||
"bin": {
|
||||
"create-fes-app": "bin/create-fes-app.mjs"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"dist",
|
||||
"templates/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"watch": "tsup --watch",
|
||||
"build": "tsup"
|
||||
"author": "qlin",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||
},
|
||||
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"citty": "^0.1.6",
|
||||
"consola": "^3.4.2",
|
||||
"fs-extra": "^11.3.1",
|
||||
"glob": "^11.0.3",
|
||||
"mustache": "^4.2.0",
|
||||
"ora": "^8.2.0",
|
||||
"semver": "^7.7.2",
|
||||
"validate-npm-package-name": "^6.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/validate-npm-package-name": "^4.0.2"
|
||||
"@fesjs/utils": "^3.0.3",
|
||||
"fs-extra": "^11.1.1",
|
||||
"inquirer": "^7.3.3",
|
||||
"readline": "^1.3.0",
|
||||
"validate-npm-package-name": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
49
packages/create-fes-app/src/cli.js
Normal file
49
packages/create-fes-app/src/cli.js
Normal file
@ -0,0 +1,49 @@
|
||||
import { chalk, yParser } from '@fesjs/utils';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
|
||||
const args = yParser(process.argv.slice(2), {
|
||||
alias: {
|
||||
version: ['v'],
|
||||
help: ['h'],
|
||||
force: ['f'],
|
||||
merge: ['m'],
|
||||
proxy: ['x']
|
||||
},
|
||||
boolean: ['version', 'help', 'merge', 'force']
|
||||
});
|
||||
|
||||
if (args._.length > 1) {
|
||||
console.log(chalk.yellow('\n Warning: You provided more than one argument. The first one will be used as the app\'s name, the rest are ignored.'));
|
||||
}
|
||||
|
||||
if (args.version && !args._[0]) {
|
||||
args._[0] = 'version';
|
||||
const local = existsSync(join(__dirname, '../.local'))
|
||||
? chalk.cyan('@local')
|
||||
: '';
|
||||
const { name, version } = require('../package.json');
|
||||
console.log(`${name}@${version}${local}`);
|
||||
} else if (args.help && !args._[0]) {
|
||||
console.log(`
|
||||
Usage: create-fes-app <name>
|
||||
|
||||
Options:
|
||||
-v, --version Output the current version
|
||||
-h, --help Display help for command
|
||||
-f, --force Overwrite target directory if it exists
|
||||
-m, --merge Merge target directory if it exists
|
||||
-x, --proxy <proxyUrl> Use specified proxy when creating project
|
||||
`);
|
||||
} else {
|
||||
require('.')
|
||||
.default({
|
||||
cwd: process.cwd(),
|
||||
args
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Create failed, ${err.message}`);
|
||||
console.error(err);
|
||||
});
|
||||
}
|
22
packages/create-fes-app/src/generator/App.js
Normal file
22
packages/create-fes-app/src/generator/App.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { Generator } from '@fesjs/utils';
|
||||
|
||||
export default class AppGenerator extends Generator {
|
||||
constructor({ cwd, args, path, targetDir }) {
|
||||
super({
|
||||
cwd,
|
||||
args,
|
||||
});
|
||||
this.path = path;
|
||||
this.targetDir = targetDir;
|
||||
}
|
||||
|
||||
async writing() {
|
||||
this.copyDirectory({
|
||||
context: {
|
||||
version: require('../../package.json').version,
|
||||
},
|
||||
path: this.path,
|
||||
target: this.targetDir,
|
||||
});
|
||||
}
|
||||
}
|
24
packages/create-fes-app/src/generator/Plugin.js
Normal file
24
packages/create-fes-app/src/generator/Plugin.js
Normal file
@ -0,0 +1,24 @@
|
||||
import { Generator } from '@fesjs/utils';
|
||||
|
||||
export default class AppGenerator extends Generator {
|
||||
constructor({ cwd, args, path, targetDir, name }) {
|
||||
super({
|
||||
cwd,
|
||||
args,
|
||||
});
|
||||
this.path = path;
|
||||
this.targetDir = targetDir;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
async writing() {
|
||||
this.copyDirectory({
|
||||
context: {
|
||||
version: require('../../package.json').version,
|
||||
name: this.name,
|
||||
},
|
||||
path: this.path,
|
||||
target: this.targetDir,
|
||||
});
|
||||
}
|
||||
}
|
117
packages/create-fes-app/src/index.js
Normal file
117
packages/create-fes-app/src/index.js
Normal file
@ -0,0 +1,117 @@
|
||||
import path from 'path';
|
||||
import { chalk } from '@fesjs/utils';
|
||||
import validateProjectName from 'validate-npm-package-name';
|
||||
import fs from 'fs-extra';
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
import { clearConsole } from './utils';
|
||||
import AppGenerator from './generator/App';
|
||||
import PluginGenerator from './generator/Plugin';
|
||||
|
||||
export default async ({ cwd, args }) => {
|
||||
if (args.proxy) {
|
||||
process.env.HTTP_PROXY = args.proxy;
|
||||
}
|
||||
const projectName = args._[0];
|
||||
const inCurrent = projectName === '.';
|
||||
const name = inCurrent ? path.relative('../', cwd) : projectName;
|
||||
const targetDir = path.resolve(cwd, projectName || '.');
|
||||
|
||||
const result = validateProjectName(name);
|
||||
if (!result.validForNewPackages) {
|
||||
console.error(chalk.red(`Invalid project name: "${name}"`));
|
||||
result.errors &&
|
||||
result.errors.forEach((err) => {
|
||||
console.error(chalk.red.dim(`Error: ${err}`));
|
||||
});
|
||||
result.warnings &&
|
||||
result.warnings.forEach((warn) => {
|
||||
console.error(chalk.red.dim(`Warning: ${warn}`));
|
||||
});
|
||||
throw new Error('Process exited');
|
||||
}
|
||||
if (fs.pathExistsSync(targetDir) && !args.merge) {
|
||||
if (args.force) {
|
||||
await fs.remove(targetDir);
|
||||
} else if (inCurrent) {
|
||||
clearConsole();
|
||||
const { ok } = await inquirer.prompt([
|
||||
{
|
||||
name: 'ok',
|
||||
type: 'confirm',
|
||||
message: 'Generate project in current directory?',
|
||||
},
|
||||
]);
|
||||
if (!ok) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
clearConsole();
|
||||
const { action } = await inquirer.prompt([
|
||||
{
|
||||
name: 'action',
|
||||
type: 'list',
|
||||
message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
|
||||
choices: [
|
||||
{ name: 'Overwrite', value: 'overwrite' },
|
||||
{ name: 'Merge', value: 'merge' },
|
||||
{ name: 'Cancel', value: false },
|
||||
],
|
||||
},
|
||||
]);
|
||||
if (!action) {
|
||||
return null;
|
||||
}
|
||||
if (action === 'overwrite') {
|
||||
console.log(`\nRemoving ${chalk.cyan(targetDir)}...`);
|
||||
await fs.remove(targetDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearConsole();
|
||||
const { template } = await inquirer.prompt([
|
||||
{
|
||||
name: 'template',
|
||||
type: 'list',
|
||||
message: 'Pick an template:',
|
||||
choices: [
|
||||
{ name: 'PC, suitable for management desk front-end applications', value: 'pc' },
|
||||
{ name: 'H5, suitable for mobile applications', value: 'h5' },
|
||||
{ name: 'Plugin, suitable for fes plugin', value: 'plugin' },
|
||||
{ name: 'Cancel', value: false },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
if (template === 'pc' || template === 'h5') {
|
||||
const generator = new AppGenerator({
|
||||
cwd,
|
||||
args,
|
||||
targetDir,
|
||||
path: path.join(__dirname, `../templates/app/${template}`),
|
||||
});
|
||||
await generator.run();
|
||||
console.log();
|
||||
console.log(chalk.green(`project ${projectName} created successfully, please execute the following command to use:`));
|
||||
console.log(`$ cd ${projectName}`);
|
||||
console.log('$ pnpm i');
|
||||
console.log('$ pnpm dev');
|
||||
console.log();
|
||||
} else if (template === 'plugin') {
|
||||
const generator = new PluginGenerator({
|
||||
cwd,
|
||||
args,
|
||||
targetDir,
|
||||
path: path.join(__dirname, '../templates/plugin'),
|
||||
name,
|
||||
});
|
||||
await generator.run();
|
||||
console.log();
|
||||
console.log(chalk.green(`plugin ${projectName} created successfully, please execute the following command to use:`));
|
||||
console.log(`$ cd ${projectName}`);
|
||||
console.log('$ pnpm i');
|
||||
console.log('$ pnpm dev');
|
||||
console.log();
|
||||
}
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
export { main } from './main';
|
||||
export { runMain } from './run';
|
@ -1,116 +0,0 @@
|
||||
import { join, relative, resolve } from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { defineCommand } from 'citty';
|
||||
import consola from 'consola';
|
||||
import ora from 'ora';
|
||||
|
||||
import validate from 'validate-npm-package-name';
|
||||
import pkg from '../package.json' assert { type: 'json' };
|
||||
import { getWorkPath } from './shared';
|
||||
import { setupGlobalConsole } from './utils/console';
|
||||
import { checkEngines } from './utils/engines';
|
||||
import { copyDirectory } from './utils/gen';
|
||||
|
||||
export const main = defineCommand({
|
||||
meta: {
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
description: pkg.description,
|
||||
},
|
||||
args: {
|
||||
name: {
|
||||
type: 'positional',
|
||||
description: '项目名称',
|
||||
required: true,
|
||||
},
|
||||
proxy: {
|
||||
type: 'string',
|
||||
description: '代理地址',
|
||||
alias: ['-p'],
|
||||
},
|
||||
merge: {
|
||||
type: 'boolean',
|
||||
description: '是否合并',
|
||||
alias: ['m'],
|
||||
},
|
||||
force: {
|
||||
type: 'boolean',
|
||||
description: '是否强制覆盖',
|
||||
alias: ['f'],
|
||||
},
|
||||
},
|
||||
async setup() {
|
||||
setupGlobalConsole();
|
||||
await checkEngines();
|
||||
},
|
||||
async run({ args }) {
|
||||
const inCurrent = args.name === '.';
|
||||
const cwd = getWorkPath();
|
||||
const projectName = inCurrent ? relative('../', cwd) : args.name;
|
||||
|
||||
const result = validate(projectName);
|
||||
if (!result.validForNewPackages) {
|
||||
consola.error(`Invalid project name: "${projectName}"`);
|
||||
result.errors
|
||||
&& result.errors.forEach((err) => {
|
||||
consola.error(`Error: ${err}`);
|
||||
});
|
||||
result.warnings
|
||||
&& result.warnings.forEach((warn) => {
|
||||
consola.warn(`Warning: ${warn}`);
|
||||
});
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const template = await consola.prompt('Pick an template:', {
|
||||
type: 'select',
|
||||
options: [{
|
||||
label: 'PC, suitable for management desk front-end applications',
|
||||
value: 'pc',
|
||||
}, {
|
||||
label: 'H5, suitable for mobile applications',
|
||||
value: 'h5',
|
||||
}, {
|
||||
label: 'Plugin, suitable for fes plugin',
|
||||
value: 'plugin',
|
||||
}, {
|
||||
label: 'Cancel',
|
||||
value: 'cancel',
|
||||
}],
|
||||
});
|
||||
|
||||
const targetDir = resolve(cwd, projectName || '.');
|
||||
if (template === 'pc' || template === 'h5') {
|
||||
const spinner = ora('项目生成中加载中...').start();
|
||||
copyDirectory({
|
||||
context: {
|
||||
version: pkg.version,
|
||||
},
|
||||
path: join(__dirname, `../templates/app/${template}`),
|
||||
target: targetDir,
|
||||
});
|
||||
spinner.succeed('项目创建成功');
|
||||
consola.box([
|
||||
`cd ${projectName}`,
|
||||
'pnpm i',
|
||||
'pnpm dev',
|
||||
].join('\n'));
|
||||
}
|
||||
else if (template === 'plugin') {
|
||||
copyDirectory({
|
||||
context: {
|
||||
version: pkg.version,
|
||||
name: projectName,
|
||||
},
|
||||
path: join(__dirname, '../templates/plugin'),
|
||||
target: targetDir,
|
||||
});
|
||||
consola.success(`plugin ${projectName} created successfully, please execute the following command to use:`);
|
||||
consola.box([
|
||||
`cd ${projectName}`,
|
||||
'pnpm i',
|
||||
'pnpm dev',
|
||||
].join('\n'));
|
||||
}
|
||||
},
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
import { runMain as _runMain } from 'citty';
|
||||
|
||||
import { main } from './main';
|
||||
|
||||
export const runMain = () => _runMain(main);
|
@ -1,27 +0,0 @@
|
||||
import { readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export const OWNER_DIR = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
export function getWorkPath() {
|
||||
return process.env.PWD || process.cwd();
|
||||
}
|
||||
|
||||
export function removeSync(dir: string) {
|
||||
rmSync(dir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function readJsonSync(filePath: string) {
|
||||
const content = readFileSync(filePath, 'utf-8');
|
||||
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
export function writeJSONSync(filePath: string, data: Record<string, any>) {
|
||||
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
||||
}
|
13
packages/create-fes-app/src/utils.js
Normal file
13
packages/create-fes-app/src/utils.js
Normal file
@ -0,0 +1,13 @@
|
||||
import readline from 'readline';
|
||||
|
||||
export const clearConsole = (title) => {
|
||||
if (process.stdout.isTTY) {
|
||||
const blank = '\n'.repeat(process.stdout.rows);
|
||||
console.log(blank);
|
||||
readline.cursorTo(process.stdout, 0, 0);
|
||||
readline.clearScreenDown(process.stdout);
|
||||
if (title) {
|
||||
console.log(title);
|
||||
}
|
||||
}
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
import process from 'node:process';
|
||||
|
||||
import { consola } from 'consola';
|
||||
|
||||
export function setupGlobalConsole(opts: { dev?: boolean } = {}) {
|
||||
// Wrap all console logs with consola for better DX
|
||||
if (opts.dev) {
|
||||
consola.wrapAll();
|
||||
}
|
||||
else {
|
||||
consola.wrapConsole();
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', err => consola.error('[unhandledRejection]', err));
|
||||
|
||||
process.on('uncaughtException', err => consola.error('[uncaughtException]', err));
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import process from 'node:process';
|
||||
|
||||
import { logger } from './logger';
|
||||
|
||||
export async function checkEngines() {
|
||||
const satisfies = await import('semver/functions/satisfies.js').then(
|
||||
r => r.default || (r as any as typeof import('semver/functions/satisfies.js')),
|
||||
); // npm/node-semver#381
|
||||
const currentNode = process.versions.node;
|
||||
const nodeRange = '>= 18.0.0';
|
||||
|
||||
if (!satisfies(currentNode, nodeRange)) {
|
||||
logger.warn(
|
||||
`Current version of Node.js (\`${currentNode}\`) is unsupported and might cause issues.\n Please upgrade to a compatible version \`${nodeRange}\`.`,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import { readFileSync, statSync } from 'node:fs';
|
||||
import { join, relative } from 'node:path';
|
||||
import { copySync, outputFileSync } from 'fs-extra/esm';
|
||||
import { globSync } from 'glob';
|
||||
import Mustache from 'mustache';
|
||||
|
||||
import { getWorkPath } from '../shared';
|
||||
|
||||
function copyTpl(opts: {
|
||||
templatePath: string;
|
||||
target: string;
|
||||
context: Record<string, any>;
|
||||
}): void {
|
||||
const tpl = readFileSync(opts.templatePath, 'utf-8');
|
||||
const content = Mustache.render(tpl, opts.context);
|
||||
|
||||
outputFileSync(opts.target, content, 'utf-8');
|
||||
}
|
||||
|
||||
export function copyDirectory(opts: {
|
||||
path: string;
|
||||
target: string;
|
||||
context: Record<string, any>;
|
||||
}): void {
|
||||
const files = globSync('**/*', {
|
||||
cwd: opts.path,
|
||||
dot: true,
|
||||
ignore: ['**/node_modules/**'],
|
||||
});
|
||||
files.forEach((file) => {
|
||||
const absFile = join(opts.path, file);
|
||||
if (statSync(absFile).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
if (file.endsWith('.tpl')) {
|
||||
return copyTpl({
|
||||
templatePath: absFile,
|
||||
target: join(opts.target, file.replace(/\.tpl$/, '')),
|
||||
context: opts.context,
|
||||
});
|
||||
}
|
||||
|
||||
const absTarget = join(opts.target, file);
|
||||
copySync(absFile, absTarget);
|
||||
});
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import { consola } from 'consola';
|
||||
|
||||
export const logger = consola.withTag('fes');
|
@ -12,18 +12,17 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-webpack": "^4.0.0",
|
||||
"@fesjs/fes": "^4.0.0",
|
||||
"@fesjs/plugin-icon": "^5.0.0",
|
||||
"@fesjs/plugin-request": "^5.0.0",
|
||||
"@fesjs/builder-webpack": "^3.1.0",
|
||||
"@fesjs/fes": "^3.1.17",
|
||||
"@fesjs/plugin-icon": "^4.0.0",
|
||||
"@fesjs/plugin-request": "^4.0.1",
|
||||
"core-js": "^3.43.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"vue": "^3.5.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.2.2",
|
||||
"eslint": "^9.35.0",
|
||||
"@antfu/eslint-config": "4.16.1",
|
||||
"eslint": "9.29.0",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
"typescript": "^5.9.2"
|
||||
"typescript": "5.8.3"
|
||||
}
|
||||
}
|
||||
|
44
packages/create-fes-app/templates/app/h5/src/app.js
Normal file
44
packages/create-fes-app/templates/app/h5/src/app.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { defineRuntimeConfig } from '@fesjs/fes';
|
||||
|
||||
export default defineRuntimeConfig({
|
||||
request: {
|
||||
// API 前缀
|
||||
baseURL: '',
|
||||
dataHandler(data, response) {
|
||||
// 处理响应内容异常
|
||||
if (data.code !== '0') {
|
||||
if (data.code === '20000') {
|
||||
console.log('hello world');
|
||||
}
|
||||
throw new Error(response);
|
||||
}
|
||||
// 响应数据格式化
|
||||
return data?.result ? data.result : data;
|
||||
},
|
||||
// http 异常,和插件异常
|
||||
errorHandler(error) {
|
||||
if (error.response) {
|
||||
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
|
||||
console.log(error.response.data);
|
||||
console.log(error.response.status);
|
||||
console.log(error.response.headers);
|
||||
} else if (error.request) {
|
||||
// 请求已经成功发起,但没有收到响应
|
||||
// `error.request` 在浏览器中是 XMLHttpRequest 的实例,
|
||||
// 而在node.js中是 http.ClientRequest 的实例
|
||||
console.log(error.request);
|
||||
} else if (error.type) {
|
||||
// 插件异常
|
||||
console.log(error.msg);
|
||||
} else {
|
||||
// 发送请求时出了点问题
|
||||
console.log('Error', error.message);
|
||||
}
|
||||
console.log(error.config);
|
||||
},
|
||||
// 请求拦截器
|
||||
requestInterceptors: [],
|
||||
// 响应拦截器
|
||||
responseInterceptors: [],
|
||||
},
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
import { defineRuntimeConfig } from '@fesjs/fes'
|
||||
import { isPlainObject } from 'lodash-es'
|
||||
|
||||
export default defineRuntimeConfig({
|
||||
request: {
|
||||
baseURL: '',
|
||||
timeout: 10000, // 默认 10s
|
||||
method: 'POST', // 默认 post
|
||||
mergeRequest: false, // 是否合并请求
|
||||
responseType: null, // 可选 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData',默认根据 content-type 处理
|
||||
credentials: 'include', // 默认 include, 'include' | 'same-origin' | 'omit'
|
||||
headers: {}, // 传给服务器的 header
|
||||
cacheData: false, // 是否缓存
|
||||
requestInterceptor: (config: Config) => Config,
|
||||
responseInterceptor: (response: RequestResponse) => RequestResponse,
|
||||
transformData(data, response) {
|
||||
// 处理响应内容异常
|
||||
if (isPlainObject(data)) {
|
||||
if (data.code === '10000') {
|
||||
return Promise.reject(data)
|
||||
}
|
||||
return data?.result ? data.result : data
|
||||
}
|
||||
return data
|
||||
},
|
||||
// http 异常,和插件异常
|
||||
errorHandler(error) {
|
||||
// 处理业务异常,例如上述 transformData 抛出的异常
|
||||
if (error.code) {
|
||||
console.log(error.msg)
|
||||
}
|
||||
else if (error.response) {
|
||||
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
|
||||
console.log(`服务异常:${error.response.status}`)
|
||||
}
|
||||
else {
|
||||
// 请求异常
|
||||
console.log(error.msg || error.message || `请求失败`)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
64
packages/create-fes-app/templates/app/h5/src/common/utils.js
Normal file
64
packages/create-fes-app/templates/app/h5/src/common/utils.js
Normal file
@ -0,0 +1,64 @@
|
||||
// TODO
|
||||
// 时间格式化
|
||||
// js 数字精度计算
|
||||
// 手机号、身份证号 等的校验
|
||||
// 数字分割
|
||||
|
||||
export function resetContainerHeight(dom) {
|
||||
const originalHeight = document.body.clientHeight || document.documentElement.clientHeight;
|
||||
|
||||
window.onresize = function () {
|
||||
const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
|
||||
if (resizeHeight < originalHeight) {
|
||||
// 恢复内容区域高度
|
||||
const container = document.querySelector(dom);
|
||||
container.style.height = originalHeight;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function resetInputBlur() {
|
||||
const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d.]+)/i);
|
||||
if (!isWechat) return;
|
||||
const wechatVersion = isWechat[1];
|
||||
const version = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
|
||||
|
||||
// 如果设备类型为iOS 12+ 和wechat 6.7.4+,恢复成原来的视口
|
||||
if (+wechatVersion.replace(/\./g, '') >= 674 && +version[1] >= 12) {
|
||||
window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));
|
||||
}
|
||||
}
|
||||
|
||||
export function getQueryString(name) {
|
||||
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
|
||||
const r = window.location.search.substr(1).match(reg);
|
||||
if (r != null) {
|
||||
return decodeURIComponent(r[2]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function simpleRequest(options) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.timeout = 3000;
|
||||
if (options.type === 'GET') {
|
||||
xhr.open(options.type, options.url, options.async || true);
|
||||
xhr.send(null);
|
||||
} else if (options.type === 'POST') {
|
||||
xhr.open(options.type, options.url, options.async || true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.send(JSON.stringify(options.data || {}));
|
||||
}
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
options.successed(xhr.responseText);
|
||||
} else {
|
||||
options.failed && options.failed(xhr);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.ontimeout = function () {
|
||||
options.failed && options.failed(xhr);
|
||||
};
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
export function resetContainerHeight(dom) {
|
||||
const originalHeight = document.body.clientHeight || document.documentElement.clientHeight
|
||||
|
||||
window.onresize = function () {
|
||||
const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight
|
||||
if (resizeHeight < originalHeight) {
|
||||
// 恢复内容区域高度
|
||||
const container = document.querySelector(dom)
|
||||
container.style.height = originalHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function resetInputBlur() {
|
||||
const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d.]+)/i)
|
||||
if (!isWechat)
|
||||
return
|
||||
const wechatVersion = isWechat[1]
|
||||
const version = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/)
|
||||
|
||||
// 如果设备类型为iOS 12+ 和wechat 6.7.4+,恢复成原来的视口
|
||||
if (+wechatVersion.replace(/\./g, '') >= 674 && +version[1] >= 12) {
|
||||
window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight))
|
||||
}
|
||||
}
|
||||
|
||||
export function getQueryString(name) {
|
||||
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')
|
||||
const r = window.location.search.substr(1).match(reg)
|
||||
if (r != null) {
|
||||
return decodeURIComponent(r[2])
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function simpleRequest(options) {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.timeout = 3000
|
||||
if (options.type === 'GET') {
|
||||
xhr.open(options.type, options.url, options.async || true)
|
||||
xhr.send(null)
|
||||
}
|
||||
else if (options.type === 'POST') {
|
||||
xhr.open(options.type, options.url, options.async || true)
|
||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||
xhr.send(JSON.stringify(options.data || {}))
|
||||
}
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
options.successed(xhr.responseText)
|
||||
}
|
||||
else {
|
||||
options.failed && options.failed(xhr)
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.ontimeout = function () {
|
||||
options.failed && options.failed(xhr)
|
||||
}
|
||||
}
|
@ -1,37 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import { defineRouteMeta, useRequest, useRouter } from '@fesjs/fes'
|
||||
import { onMounted, ref } from 'vue'
|
||||
<template>
|
||||
<div class="onepiece">
|
||||
fes & 拉夫德鲁 <br />
|
||||
<fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" />
|
||||
<div v-if="loading" class="loading">loading</div>
|
||||
<div v-else class="data">{{ data }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRouter, useRequest, defineRouteMeta } from '@fesjs/fes';
|
||||
|
||||
defineRouteMeta({
|
||||
title: '首页',
|
||||
})
|
||||
});
|
||||
|
||||
const fes = ref('fes upgrade to vue3')
|
||||
const rotate = ref(90)
|
||||
const router = useRouter()
|
||||
export default {
|
||||
setup() {
|
||||
const fes = ref('fes upgrade to vue3');
|
||||
const rotate = ref(90);
|
||||
const router = useRouter();
|
||||
onMounted(() => {
|
||||
console.log(router)
|
||||
console.log('mounted1!!')
|
||||
})
|
||||
function clickIcon() {
|
||||
console.log('click Icon')
|
||||
}
|
||||
const { loading, data } = useRequest('api')
|
||||
console.log(router);
|
||||
console.log('mounted1!!');
|
||||
});
|
||||
const clickIcon = () => {
|
||||
console.log('click Icon');
|
||||
};
|
||||
const { loading, data } = useRequest('api');
|
||||
return {
|
||||
loading,
|
||||
data,
|
||||
fes,
|
||||
rotate,
|
||||
clickIcon,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="onepiece">
|
||||
fes & 拉夫德鲁 <br>
|
||||
<fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" />
|
||||
<div v-if="loading" class="loading">
|
||||
loading
|
||||
</div>
|
||||
<div v-else class="data">
|
||||
{{ data }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '~@/styles/mixins/hairline';
|
||||
@import '~@/styles/mixins/hover';
|
||||
|
@ -1,14 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { defineRouteMeta } from '@fesjs/fes'
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineRouteMeta({
|
||||
title: 'one piece',
|
||||
})
|
||||
|
||||
const fes = ref('fes upgrade to vue3')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>{{ fes }}</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineRouteMeta } from '@fesjs/fes';
|
||||
import { ref } from 'vue';
|
||||
|
||||
defineRouteMeta({
|
||||
title: 'one piece',
|
||||
});
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const fes = ref('fes upgrade to vue3');
|
||||
return {
|
||||
fes,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -14,21 +14,19 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-vite": "^5.0.0",
|
||||
"@fesjs/fes": "^4.0.0",
|
||||
"@fesjs/builder-webpack": "^3.1.0",
|
||||
"@fesjs/fes": "^3.1.17",
|
||||
"@fesjs/fes-design": "^0.8.82",
|
||||
"@fesjs/plugin-access": "^4.0.0",
|
||||
"@fesjs/plugin-enums": "^4.0.0",
|
||||
"@fesjs/plugin-layout": "^6.0.0",
|
||||
"@fesjs/plugin-model": "^4.0.0",
|
||||
"@fesjs/plugin-access": "^3.1.9",
|
||||
"@fesjs/plugin-enums": "^3.0.1",
|
||||
"@fesjs/plugin-layout": "^5.4.6",
|
||||
"@fesjs/plugin-model": "^3.0.3",
|
||||
"core-js": "^3.43.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"vue": "^3.5.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.2.2",
|
||||
"eslint": "^9.35.0",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
"typescript": "^5.9.2"
|
||||
"@antfu/eslint-config": "4.16.1",
|
||||
"eslint": "9.29.0",
|
||||
"typescript": "5.8.3"
|
||||
}
|
||||
}
|
||||
|
25
packages/create-fes-app/templates/app/pc/src/app.jsx
Normal file
25
packages/create-fes-app/templates/app/pc/src/app.jsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { access, defineRuntimeConfig } from '@fesjs/fes';
|
||||
|
||||
import PageLoading from '@/components/pageLoading.vue';
|
||||
import UserCenter from '@/components/userCenter.vue';
|
||||
|
||||
export default defineRuntimeConfig({
|
||||
beforeRender: {
|
||||
loading: <PageLoading />,
|
||||
action() {
|
||||
const { setRole } = access;
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
setRole('admin');
|
||||
// 初始化应用的全局状态,可以通过 useModel('@@initialState') 获取,具体用法看@/components/UserCenter 文件
|
||||
resolve({
|
||||
userName: '李雷',
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
renderCustom: () => <UserCenter />,
|
||||
},
|
||||
});
|
@ -1,61 +0,0 @@
|
||||
import { access, defineRuntimeConfig } from '@fesjs/fes'
|
||||
import { isPlainObject } from 'lodash-es'
|
||||
import PageLoading from '@/components/pageLoading.vue'
|
||||
import UserCenter from '@/components/userCenter.vue'
|
||||
|
||||
export default defineRuntimeConfig({
|
||||
beforeRender: {
|
||||
loading: <PageLoading />,
|
||||
action() {
|
||||
const { setRole } = access
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
setRole('admin')
|
||||
// 初始化应用的全局状态,可以通过 useModel('@@initialState') 获取,具体用法看@/components/UserCenter 文件
|
||||
resolve({
|
||||
userName: '李雷',
|
||||
})
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
},
|
||||
request: {
|
||||
baseURL: '',
|
||||
timeout: 10000, // 默认 10s
|
||||
method: 'POST', // 默认 post
|
||||
mergeRequest: false, // 是否合并请求
|
||||
responseType: null, // 可选 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData',默认根据 content-type 处理
|
||||
credentials: 'include', // 默认 include, 'include' | 'same-origin' | 'omit'
|
||||
headers: {}, // 传给服务器的 header
|
||||
cacheData: false, // 是否缓存
|
||||
transformData(data, response) {
|
||||
// 处理响应内容异常
|
||||
if (isPlainObject(data)) {
|
||||
if (data.code === '10000') {
|
||||
return Promise.reject(data)
|
||||
}
|
||||
return data?.result ? data.result : data
|
||||
}
|
||||
return data
|
||||
},
|
||||
// http 异常,和插件异常
|
||||
errorHandler(error) {
|
||||
// 处理业务异常,例如上述 transformData 抛出的异常
|
||||
if (error.code) {
|
||||
console.log(error.msg)
|
||||
}
|
||||
else if (error.response) {
|
||||
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
|
||||
console.log(`服务异常:${error.response.status}`)
|
||||
}
|
||||
else {
|
||||
// 请求异常
|
||||
console.log(error.msg || error.message || `请求失败`)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
layout: {
|
||||
renderCustom: () => <UserCenter />,
|
||||
},
|
||||
})
|
@ -1,13 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { FSpin } from '@fesjs/fes-design'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-loading">
|
||||
<FSpin size="large" stroke="#5384ff" />
|
||||
<f-spin size="large" stroke="#5384ff" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { FSpin } from '@fesjs/fes-design';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FSpin,
|
||||
},
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.page-loading {
|
||||
position: fixed;
|
||||
|
@ -1,15 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useModel } from '@fesjs/fes'
|
||||
|
||||
const initialState = useModel('@@initialState')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="right">
|
||||
{{ initialState.userName }}
|
||||
</div>
|
||||
<div class="right">{{ initialState.userName }}</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useModel } from '@fesjs/fes';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const initialState = useModel('@@initialState');
|
||||
return {
|
||||
initialState,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scope>
|
||||
.right {
|
||||
text-align: right;
|
||||
|
@ -1,14 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { defineRouteMeta } from '@fesjs/fes'
|
||||
<template>
|
||||
<div style="padding: 32px">hello world</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineRouteMeta } from '@fesjs/fes';
|
||||
|
||||
defineRouteMeta({
|
||||
name: 'index',
|
||||
title: '首页',
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 32px">
|
||||
hello world
|
||||
</div>
|
||||
</template>
|
||||
|
16
packages/create-fes-app/templates/plugin/.editorconfig
Normal file
16
packages/create-fes-app/templates/plugin/.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
lib
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
21
packages/create-fes-app/templates/plugin/.eslintrc.js
Normal file
21
packages/create-fes-app/templates/plugin/.eslintrc.js
Normal file
@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
extends: ['@webank/eslint-config-webank/vue.js'],
|
||||
globals: {
|
||||
// 这里填入你的项目需要的全局变量
|
||||
// 这里值为 false 表示这个全局变量不允许被重新赋值,比如:
|
||||
//
|
||||
// Vue: false
|
||||
__DEV__: false,
|
||||
},
|
||||
rules: {
|
||||
'vue/comment-directive': 'off',
|
||||
'global-require': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-undefined': 'off',
|
||||
'vue/valid-template-root': 'off',
|
||||
},
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
};
|
@ -1,8 +1,2 @@
|
||||
.DS_Store
|
||||
|
||||
|
||||
node_modules
|
||||
lib
|
||||
dist
|
||||
npm-debug.log
|
||||
|
||||
|
4
packages/create-fes-app/templates/plugin/.prettierrc
Normal file
4
packages/create-fes-app/templates/plugin/.prettierrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none"
|
||||
}
|
@ -1 +0,0 @@
|
||||
# Fes.js 插件
|
3
packages/create-fes-app/templates/plugin/build.config.js
Normal file
3
packages/create-fes-app/templates/plugin/build.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
copy: ['runtime'],
|
||||
};
|
@ -1,36 +0,0 @@
|
||||
// eslint.config.js
|
||||
import antfu from '@antfu/eslint-config';
|
||||
|
||||
export default await antfu({
|
||||
stylistic: {
|
||||
indent: 4,
|
||||
quotes: 'single',
|
||||
semi: 'always',
|
||||
ignores: ['*.yaml'],
|
||||
},
|
||||
typescript: true,
|
||||
vue: true,
|
||||
rules: {
|
||||
'curly': ['error', 'multi-line'],
|
||||
'vue/block-order': [
|
||||
'error',
|
||||
{
|
||||
order: ['template', 'script', 'style'],
|
||||
},
|
||||
],
|
||||
'style/member-delimiter-style': [
|
||||
'error',
|
||||
{
|
||||
multiline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: true,
|
||||
},
|
||||
singleline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: false,
|
||||
},
|
||||
multilineDetection: 'brackets',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "fes-plugin-{{{name}}}",
|
||||
"version": "1.0.0",
|
||||
"version": "3.0.0",
|
||||
"description": "一个fes.js插件",
|
||||
"main": "dist/index.mjs",
|
||||
"module": "dist/index.mjs",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"lib",
|
||||
"README.md",
|
||||
"types.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"watch": "tsup --watch",
|
||||
"build": "tsup"
|
||||
"dev": "node scripts/build.js --watch",
|
||||
"build": "node scripts/build.js",
|
||||
"lint": "eslint -c ./.eslintrc.js --ext .js,.jsx,.vue,.ts"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -19,14 +19,31 @@
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.2.2",
|
||||
"tsup": "^8.5.0",
|
||||
"fs-extra": "^11.3.1",
|
||||
"eslint": "^9.34.0",
|
||||
"typescript": "^5.9.2"
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/preset-env": "^7.23.2",
|
||||
"@webank/eslint-config-webank": "1.2.7",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.2",
|
||||
"deepmerge": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"husky": "^4.3.0",
|
||||
"lint-staged": "^10.4.0",
|
||||
"yargs-parser": "^20.2.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fesjs/fes": "^4.0.0",
|
||||
"vue": "^3.5.20",
|
||||
"@fesjs/fes": "^3.0.0",
|
||||
"vue": "^3.2.47"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,vue,ts}": [
|
||||
"eslint --format=codeframe"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged",
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"typings": "./types.d.ts"
|
||||
}
|
144
packages/create-fes-app/templates/plugin/scripts/build.js
Normal file
144
packages/create-fes-app/templates/plugin/scripts/build.js
Normal file
@ -0,0 +1,144 @@
|
||||
// 关闭 import 规则
|
||||
/* eslint import/no-extraneous-dependencies: 0 */
|
||||
|
||||
const fs = require('fs');
|
||||
const fse = require('fs-extra');
|
||||
const path = require('path');
|
||||
const merge = require('deepmerge');
|
||||
const chokidar = require('chokidar');
|
||||
const chalk = require('chalk');
|
||||
const argv = require('yargs-parser')(process.argv.slice(2));
|
||||
|
||||
const compiler = require('./compiler');
|
||||
const randomColor = require('./randomColor');
|
||||
const pkg = require('../package.json');
|
||||
|
||||
const ESM_OUTPUT_DIR = 'es';
|
||||
const NODE_CJS_OUTPUT_DIR = 'lib';
|
||||
const SOURCE_DIR = 'src';
|
||||
const CONFIG_FILE_NAME = 'build.config.js';
|
||||
const GLOBAL_CONFIG_PATH = path.join(process.cwd(), CONFIG_FILE_NAME);
|
||||
const DEFAULT_CONFIG = {
|
||||
target: 'node',
|
||||
};
|
||||
|
||||
function genLog(pkgName) {
|
||||
return (msg) => {
|
||||
console.log(`${randomColor(pkgName)}: ${msg}`);
|
||||
};
|
||||
}
|
||||
|
||||
function genShortPath(filePath) {
|
||||
const codePath = filePath.split(`/${SOURCE_DIR}/`)[1];
|
||||
return `${SOURCE_DIR}/${codePath}`;
|
||||
}
|
||||
|
||||
function getPkgSourcePath() {
|
||||
return path.join(process.cwd(), SOURCE_DIR);
|
||||
}
|
||||
|
||||
function getOutputPath(config) {
|
||||
if (config.target === 'browser') {
|
||||
return path.join(process.cwd(), ESM_OUTPUT_DIR);
|
||||
}
|
||||
|
||||
return path.join(process.cwd(), NODE_CJS_OUTPUT_DIR);
|
||||
}
|
||||
|
||||
function getGlobalConfig() {
|
||||
if (fs.existsSync(GLOBAL_CONFIG_PATH)) {
|
||||
const userConfig = require(GLOBAL_CONFIG_PATH);
|
||||
return merge(DEFAULT_CONFIG, userConfig);
|
||||
}
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
function cleanBeforeCompilerResult(log) {
|
||||
const esmOutputDir = path.join(process.cwd(), ESM_OUTPUT_DIR);
|
||||
const cjsOutputDir = path.join(process.cwd(), NODE_CJS_OUTPUT_DIR);
|
||||
if (fs.existsSync(esmOutputDir)) {
|
||||
log(chalk.gray(`Clean ${ESM_OUTPUT_DIR} directory`));
|
||||
fse.removeSync(esmOutputDir);
|
||||
}
|
||||
if (fs.existsSync(cjsOutputDir)) {
|
||||
log(chalk.gray(`Clean ${NODE_CJS_OUTPUT_DIR} directory`));
|
||||
fse.removeSync(cjsOutputDir);
|
||||
}
|
||||
}
|
||||
|
||||
function transformFile(filePath, outputPath, config, log) {
|
||||
if (/\.[jt]sx?$/.test(path.extname(filePath))) {
|
||||
try {
|
||||
const code = fs.readFileSync(filePath, 'utf-8');
|
||||
const shortFilePath = genShortPath(filePath);
|
||||
const transformedCode = compiler(code, config);
|
||||
|
||||
const type = config.target === 'browser' ? ESM_OUTPUT_DIR : NODE_CJS_OUTPUT_DIR;
|
||||
log(`Transform to ${type} for ${config.target === 'browser' ? chalk.yellow(shortFilePath) : chalk.blue(shortFilePath)}`);
|
||||
fse.outputFileSync(outputPath, transformedCode);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
} else {
|
||||
fse.copySync(filePath, outputPath);
|
||||
}
|
||||
}
|
||||
|
||||
function compilerPkg(codeDir, outputDir, config, log) {
|
||||
const files = fs.readdirSync(codeDir);
|
||||
files.forEach((file) => {
|
||||
const filePath = path.join(codeDir, file);
|
||||
const outputFilePath = path.join(outputDir, file);
|
||||
const fileStats = fs.lstatSync(filePath);
|
||||
if (config.copy.includes(file)) {
|
||||
fse.copySync(filePath, outputFilePath);
|
||||
} else if (fileStats.isDirectory(filePath) && !/__tests__/.test(file)) {
|
||||
fse.ensureDirSync(outputFilePath);
|
||||
compilerPkg(filePath, outputFilePath, config, log);
|
||||
} else if (fileStats.isFile(filePath)) {
|
||||
transformFile(filePath, outputFilePath, config, log);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function watchFile(dir, outputDir, config, log) {
|
||||
chokidar
|
||||
.watch(dir, {
|
||||
ignoreInitial: true,
|
||||
})
|
||||
.on('all', (event, changeFile) => {
|
||||
// 修改的可能是一个目录,一个文件,一个需要 copy 的文件 or 目录
|
||||
const shortChangeFile = genShortPath(changeFile);
|
||||
const outputPath = changeFile.replace(dir, outputDir);
|
||||
const stat = fs.lstatSync(changeFile);
|
||||
log(`[${event}] ${shortChangeFile}`);
|
||||
if (config.resolveCopy.some((item) => changeFile.startsWith(item))) {
|
||||
fse.copySync(changeFile, outputPath);
|
||||
} else if (stat.isFile()) {
|
||||
transformFile(changeFile, outputPath, config, log);
|
||||
} else if (stat.isDirectory()) {
|
||||
compilerPkg(changeFile, outputPath, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
const sourceCodeDir = getPkgSourcePath();
|
||||
const pkgName = pkg.name;
|
||||
if (fs.existsSync(sourceCodeDir)) {
|
||||
const log = genLog(pkgName);
|
||||
const config = getGlobalConfig();
|
||||
const outputDir = getOutputPath(config);
|
||||
|
||||
cleanBeforeCompilerResult(log);
|
||||
const type = config.target === 'browser' ? ESM_OUTPUT_DIR : NODE_CJS_OUTPUT_DIR;
|
||||
log(chalk.white(`Build ${type} with babel`));
|
||||
compilerPkg(sourceCodeDir, outputDir, config, log);
|
||||
if (argv.watch) {
|
||||
log(chalk.magenta(`Start watch ${SOURCE_DIR} directory...`));
|
||||
watchFile(sourceCodeDir, outputDir, config, log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
52
packages/create-fes-app/templates/plugin/scripts/compiler.js
Normal file
52
packages/create-fes-app/templates/plugin/scripts/compiler.js
Normal file
@ -0,0 +1,52 @@
|
||||
// 关闭 import 规则
|
||||
/* eslint import/no-extraneous-dependencies: 0 */
|
||||
|
||||
const babel = require('@babel/core');
|
||||
|
||||
function transform(code, options) {
|
||||
const result = babel.transformSync(code, options);
|
||||
return result.code;
|
||||
}
|
||||
|
||||
function transformNodeCode(code) {
|
||||
return transform(code, {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: 'cjs',
|
||||
targets: { node: '12' },
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function transformBrowserCode(code) {
|
||||
// 因为 fes.js 在生产打包的时候,会处理所有的 node_modules 下的文件,确保不会丢失必要 polyfill
|
||||
// 因此这里不对 polyfill 进行处理,避免全局污染
|
||||
return transform(code, {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: false,
|
||||
useBuiltIns: false,
|
||||
targets: { chrome: '64' },
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function compiler(code, config) {
|
||||
if (!config.target || config.target === 'node') {
|
||||
return transformNodeCode(code);
|
||||
}
|
||||
if (config.target === 'browser') {
|
||||
return transformBrowserCode(code);
|
||||
}
|
||||
throw new Error(`config target error: ${config.target}, only can use 'node' and 'browser'`);
|
||||
}
|
||||
|
||||
module.exports = compiler;
|
@ -0,0 +1,35 @@
|
||||
/* eslint import/no-extraneous-dependencies: 0 */
|
||||
const chalk = require('chalk');
|
||||
|
||||
const colors = [
|
||||
'red',
|
||||
'green',
|
||||
'yellow',
|
||||
'blue',
|
||||
'magenta',
|
||||
'cyan',
|
||||
'gray',
|
||||
'redBright',
|
||||
'greenBright',
|
||||
'yellowBright',
|
||||
'blueBright',
|
||||
'magentaBright',
|
||||
'cyanBright',
|
||||
];
|
||||
|
||||
let index = 0;
|
||||
const cache = {};
|
||||
|
||||
module.exports = function (pkg) {
|
||||
if (!cache[pkg]) {
|
||||
const color = colors[index];
|
||||
const str = chalk[color].bold(pkg);
|
||||
cache[pkg] = str;
|
||||
if (index === colors.length - 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
return cache[pkg];
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { join } from 'node:path';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
import { name } from '../package.json';
|
||||
|
||||
const namespace = 'plugin-{{{name}}}';
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"rootDir": "./src",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { copySync } from 'fs-extra/esm';
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
clean: true,
|
||||
dts: false,
|
||||
shims: true,
|
||||
format: ['esm'],
|
||||
onSuccess() {
|
||||
copySync('src/runtime', 'dist/runtime');
|
||||
},
|
||||
});
|
10
packages/create-fes-app/templates/plugin/types.d.ts
vendored
Normal file
10
packages/create-fes-app/templates/plugin/types.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
import {} from '@fesjs/fes';
|
||||
declare module "@fesjs/fes" {
|
||||
interface PluginBuildConfig {
|
||||
|
||||
}
|
||||
|
||||
interface PluginRuntimeConfig {
|
||||
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": ["@fesjs/typescript-config/base.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
clean: true,
|
||||
dts: false,
|
||||
shims: true,
|
||||
format: ['esm'],
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fesjs/builder-vite",
|
||||
"version": "5.0.0-beta.0",
|
||||
"version": "4.0.5",
|
||||
"description": "@fesjs/builder-vite",
|
||||
"author": "qlin",
|
||||
"license": "MIT",
|
||||
@ -8,7 +8,7 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
||||
"directory": "packages/builder-vite"
|
||||
"directory": "packages/fes-builder-vite"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||
@ -16,49 +16,44 @@
|
||||
"keywords": [
|
||||
"fes"
|
||||
],
|
||||
"main": "dist/index.mjs",
|
||||
"module": "dist/index.mjs",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"lib",
|
||||
"types.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"watch": "tsup --watch",
|
||||
"build": "tsup"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fesjs/fes": "^4.0.0-beta.0",
|
||||
"core-js": "^3.45.1"
|
||||
"@fesjs/fes": "^3.1.12",
|
||||
"core-js": "^3.29.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/shared": "^4.0.0-beta.0",
|
||||
"@fesjs/utils": "^4.0.0-beta.0",
|
||||
"@babel/core": "^7.23.3",
|
||||
"@fesjs/utils": "^3.0.3",
|
||||
"@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",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"@vitejs/plugin-basic-ssl": "^1.0.2",
|
||||
"@vitejs/plugin-legacy": "^5.2.0",
|
||||
"@vitejs/plugin-vue": "^4.5.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"colorette": "^2.0.16",
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
"consola": "^3.4.2",
|
||||
"consola": "^2.15.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"dotenv-expand": "^8.0.2",
|
||||
"ejs": "^3.1.6",
|
||||
"fast-glob": "^3.2.11",
|
||||
"fs-extra": "^11.3.1",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"html-minifier-terser": "^6.1.0",
|
||||
"less": "^4.2.0",
|
||||
"node-html-parser": "^5.3.3",
|
||||
"pathe": "^0.2.0",
|
||||
"postcss-flexbugs-fixes": "^5.0.2",
|
||||
"postcss-safe-parser": "^6.0.0",
|
||||
"rollup-plugin-visualizer": "^6.0.3",
|
||||
"rollup-plugin-visualizer": "^5.9.3",
|
||||
"terser": "^5.24.0",
|
||||
"vite": "^7.1.4"
|
||||
"vite": "^5.0.3"
|
||||
},
|
||||
"typings": "./types.d.ts"
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
import type { InlineConfig, UserConfig } from 'vite';
|
||||
import type { ViteBuildConfig } from '../../shared';
|
||||
import { splitVendorChunkPlugin } from 'vite';
|
||||
import legacy from '@vitejs/plugin-legacy';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
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[] = [];
|
||||
function getEsbuildTarget(targets) {
|
||||
const result = [];
|
||||
['chrome', 'edge', 'firefox', 'hermes', 'ios', 'node', 'opera', 'rhino', 'safari'].forEach((key) => {
|
||||
if (targets[key]) {
|
||||
result.push(`${key}${targets[key]}`);
|
||||
@ -17,20 +12,20 @@ function getEsbuildTarget(targets: any): string[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
export default async (api: IPluginAPI<ViteBuildConfig>): Promise<InlineConfig> => {
|
||||
export default async (api) => {
|
||||
const { deepmerge, getTargetsAndBrowsersList } = api.utils;
|
||||
|
||||
const { build = {} } = api.config.vite || api.config.viteOption as UserConfig;
|
||||
const { build = {} } = api.config.viteOption;
|
||||
const { targets, browserslist } = getTargetsAndBrowsersList({ config: api.config });
|
||||
|
||||
const bundleConfig: InlineConfig = deepmerge(getInnerCommonConfig(api), {
|
||||
const bundleConfig = deepmerge(getInnerCommonConfig(api), {
|
||||
mode: 'production',
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [
|
||||
postcssFlexbugsFixes,
|
||||
postcssSafeParser,
|
||||
autoprefixer({
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-safe-parser'),
|
||||
require('autoprefixer')({
|
||||
...api.config.autoprefixer,
|
||||
overrideBrowserslist: browserslist,
|
||||
}),
|
||||
@ -44,10 +39,12 @@ export default async (api: IPluginAPI<ViteBuildConfig>): Promise<InlineConfig> =
|
||||
targets,
|
||||
...api.config.viteLegacy,
|
||||
}),
|
||||
splitVendorChunkPlugin(),
|
||||
],
|
||||
build: {
|
||||
...build,
|
||||
terserOptions: build.terserOptions || api.config.terserOptions,
|
||||
target: build.target || getEsbuildTarget(targets),
|
||||
outDir: build.outDir || api.config.outputPath || 'dist',
|
||||
assetsDir: build.assetsDir || 'static',
|
||||
assetsInlineLimit: build.assetsInlineLimit || api.config.inlineLimit || 8192,
|
42
packages/fes-builder-vite/src/commands/build/index.js
Normal file
42
packages/fes-builder-vite/src/commands/build/index.js
Normal file
@ -0,0 +1,42 @@
|
||||
import { build } from 'vite';
|
||||
import { existsSync } from 'fs';
|
||||
import getBuildConfig from './getBuildConfig';
|
||||
|
||||
export default function (api) {
|
||||
const {
|
||||
paths,
|
||||
utils: { rimraf },
|
||||
} = api;
|
||||
|
||||
api.registerCommand({
|
||||
command: 'build',
|
||||
description: 'build application for production',
|
||||
async fn() {
|
||||
rimraf.sync(paths.absTmpPath);
|
||||
|
||||
// generate files
|
||||
await api.applyPlugins({
|
||||
key: 'onGenerateFiles',
|
||||
type: api.ApplyPluginsType.event,
|
||||
});
|
||||
|
||||
const bundleConfig = await getBuildConfig(api);
|
||||
try {
|
||||
// clear output path before exec build
|
||||
if (process.env.CLEAR_OUTPUT !== 'none') {
|
||||
if (paths.absOutputPath && existsSync(paths.absOutputPath)) {
|
||||
rimraf.sync(paths.absOutputPath);
|
||||
}
|
||||
}
|
||||
|
||||
await build(bundleConfig);
|
||||
if (process.env.RM_TMPDIR !== 'none') {
|
||||
rimraf.sync(paths.absTmpPath);
|
||||
}
|
||||
} catch (err) {
|
||||
// throw build error
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
@ -1,27 +1,16 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
import type { InlineConfig } from 'vite';
|
||||
import process from 'node:process';
|
||||
import basicSsl from '@vitejs/plugin-basic-ssl';
|
||||
import { getInnerCommonConfig } from '../../common/getConfig';
|
||||
import viteMiddlewarePlugin from './viteMiddlewarePlugin';
|
||||
|
||||
interface Args {
|
||||
port?: string | number;
|
||||
https?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export default async (api: IPluginAPI, args: Args): Promise<InlineConfig> => {
|
||||
export default async (api, args) => {
|
||||
const { deepmerge, getPort, changePort, getHostName } = api.utils;
|
||||
|
||||
const viteOption = api.config.vite || api.config.viteOption || {};
|
||||
|
||||
const port = await getPort(process.env.PORT || args.port || viteOption.server?.port);
|
||||
const port = await getPort(process.env.PORT || args.port || api.config.viteOption.server?.port);
|
||||
changePort(port);
|
||||
|
||||
const hostname = getHostName(viteOption.server?.host);
|
||||
const hostname = getHostName(api.config.viteOption.server?.host);
|
||||
|
||||
const { server } = viteOption;
|
||||
const { server } = api.config.viteOption;
|
||||
|
||||
const beforeMiddlewares = await api.applyPlugins({
|
||||
key: 'addBeforeMiddlewares',
|
||||
@ -36,9 +25,9 @@ export default async (api: IPluginAPI, args: Args): Promise<InlineConfig> => {
|
||||
args: {},
|
||||
});
|
||||
|
||||
const isHTTPS = !!(process.env.HTTPS || args.https || viteOption.server?.https);
|
||||
const isHTTPS = !!(process.env.HTTPS || args.https || api.config.viteOption.server?.https);
|
||||
|
||||
const bundleConfig: InlineConfig = deepmerge(getInnerCommonConfig(api), {
|
||||
const bundleConfig = deepmerge(getInnerCommonConfig(api), {
|
||||
mode: 'development',
|
||||
plugins: [viteMiddlewarePlugin(beforeMiddlewares, middlewares), isHTTPS && basicSsl()].filter(Boolean),
|
||||
server: {
|
@ -1,28 +1,16 @@
|
||||
import type { IPluginAPI } from '@fesjs/shared';
|
||||
import type { ViteDevServer } from 'vite';
|
||||
import process from 'node:process';
|
||||
import { createServer } from 'vite';
|
||||
import getDevConfig from './getDevConfig';
|
||||
|
||||
interface Args {
|
||||
args?: Record<string, any>;
|
||||
rawArgv?: Record<string, any>;
|
||||
options?: Record<string, any>;
|
||||
program?: any;
|
||||
}
|
||||
|
||||
export default (api: IPluginAPI) => {
|
||||
export default (api) => {
|
||||
const {
|
||||
paths,
|
||||
utils: { chalk, rimraf },
|
||||
} = api;
|
||||
|
||||
let server: ViteDevServer | undefined;
|
||||
let server;
|
||||
|
||||
function destroy() {
|
||||
if (server) {
|
||||
server.close().catch(() => {});
|
||||
}
|
||||
server?.close();
|
||||
}
|
||||
|
||||
api.registerCommand({
|
||||
@ -38,7 +26,7 @@ export default (api: IPluginAPI) => {
|
||||
description: 'whether to turn on the https service',
|
||||
},
|
||||
],
|
||||
async fn({ args = {} }: Args) {
|
||||
async fn({ args = {} }) {
|
||||
rimraf.sync(paths.absTmpPath);
|
||||
|
||||
await api.applyPlugins({
|
||||
@ -62,14 +50,11 @@ export default (api: IPluginAPI) => {
|
||||
api.registerMethod({
|
||||
name: 'restartServer',
|
||||
fn() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.gray('Try to restart dev server...'));
|
||||
destroy();
|
||||
if (typeof process !== 'undefined' && process.send) {
|
||||
process.send({
|
||||
type: 'RESTART',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user