mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-10-14 02:48:06 +08:00
Compare commits
No commits in common. "v4.0.0-beta.0" and "master" have entirely different histories.
v4.0.0-bet
...
master
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,10 +1,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
.history
|
.history
|
||||||
|
|
||||||
.cache
|
.cache
|
||||||
.turbo
|
|
||||||
|
|
||||||
.temp
|
.temp
|
||||||
.hound
|
.hound
|
||||||
.fes
|
.fes
|
||||||
|
3
.npmrc
3
.npmrc
@ -1,4 +1,3 @@
|
|||||||
registry=https://registry.npmmirror.com
|
registry=https://registry.npmmirror.com
|
||||||
link-workspace-packages=true
|
auto-install-peers=true
|
||||||
prefer-workspace-packages=true
|
|
||||||
shamefully-hoist=true
|
shamefully-hoist=true
|
||||||
|
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,18 +1,3 @@
|
|||||||
# [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)
|
## [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-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-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-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-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-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-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-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-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-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) |
|
| [@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';
|
import antfu from '@antfu/eslint-config';
|
||||||
|
|
||||||
export default await antfu({
|
export default await antfu({
|
||||||
|
// TODO: 使用 ignore 代替 cli 命令中的配置
|
||||||
stylistic: {
|
stylistic: {
|
||||||
indent: 4,
|
indent: 4,
|
||||||
quotes: 'single',
|
quotes: 'single',
|
||||||
semi: 'always',
|
semi: 'always',
|
||||||
ignores: ['*.yaml'],
|
|
||||||
},
|
},
|
||||||
typescript: true,
|
typescript: true,
|
||||||
vue: 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",
|
"name": "fes.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.0.0-beta.0",
|
"version": "3.4.12",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.14.0",
|
"packageManager": "pnpm@8.6.6",
|
||||||
"description": "一个好用的前端管理台快速开发框架",
|
"description": "一个好用的前端管理台快速开发框架",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
@ -19,8 +19,8 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bootstrap": "pnpm i",
|
"bootstrap": "pnpm i",
|
||||||
"dev": "turbo run watch",
|
"dev": "node scripts/build.mjs --watch",
|
||||||
"build": "turbo run build",
|
"build": "node scripts/build.mjs",
|
||||||
"release": "node scripts/release.mjs",
|
"release": "node scripts/release.mjs",
|
||||||
"clean": "rm -rf ./node_modules & rm -rf packages/**/node_modules & rm -rf packages/**/.cache",
|
"clean": "rm -rf ./node_modules & rm -rf packages/**/node_modules & rm -rf packages/**/.cache",
|
||||||
"docs:dev": "vitepress dev docs",
|
"docs:dev": "vitepress dev docs",
|
||||||
@ -32,30 +32,29 @@
|
|||||||
"hooks:sync": "simple-git-hooks"
|
"hooks:sync": "simple-git-hooks"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"consola": "^3.4.2",
|
"chalk": "^5.0.1",
|
||||||
"conventional-changelog-cli": "^5.0.0",
|
"conventional-changelog-cli": "^4.1.0",
|
||||||
|
"enquirer": "^2.3.6",
|
||||||
"execa": "^6.1.0",
|
"execa": "^6.1.0",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"picocolors": "^1.1.1",
|
"semver": "^7.3.6"
|
||||||
"semver": "^7.3.6",
|
|
||||||
"tsup": "^8.5.0",
|
|
||||||
"turbo": "^2.5.6"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^5.2.2",
|
"@antfu/eslint-config": "^3.8.0",
|
||||||
"@commitlint/cli": "^18.4.4",
|
"@commitlint/cli": "^18.4.4",
|
||||||
"@commitlint/config-conventional": "^18.4.4",
|
"@commitlint/config-conventional": "^18.4.4",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"commitizen": "^4.3.1",
|
"commitizen": "^4.3.1",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
"eslint": "^9.34.0",
|
"eslint": "^9.13.0",
|
||||||
"fs-extra": "^11.3.1",
|
"fs-extra": "^11.1.1",
|
||||||
"lint-staged": "^15.2.0",
|
"lint-staged": "^15.2.0",
|
||||||
"simple-git-hooks": "^2.9.0",
|
"simple-git-hooks": "^2.9.0",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.6.3",
|
||||||
"vitepress": "1.0.0-alpha.73",
|
"vitepress": "1.0.0-alpha.73",
|
||||||
"vue": "^3.5.21"
|
"vue": "^3.3.4",
|
||||||
|
"yargs-parser": "^21.1.1"
|
||||||
},
|
},
|
||||||
"simple-git-hooks": {
|
"simple-git-hooks": {
|
||||||
"pre-commit": "npx lint-staged",
|
"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-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-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-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-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-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-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-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-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-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-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-pinia](http://fesjs.mumblefe.cn/reference/plugin/plugins/pinia.html) | pinia,状态处理 |
|
||||||
| [@fesjs/plugin-watermark](http://fesjs.mumblefe.cn/reference/plugin/plugins/watermark.html) | 水印 |
|
| [@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",
|
"name": "@fesjs/create-fes-app",
|
||||||
"version": "4.0.0-beta.0",
|
"version": "3.0.7",
|
||||||
"description": "create a app base on fes.js",
|
"description": "create a app base on fes.js",
|
||||||
"author": "qlin",
|
"main": "lib/index.js",
|
||||||
"license": "MIT",
|
"files": [
|
||||||
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
|
"lib",
|
||||||
|
"bin",
|
||||||
|
"templates/**/*"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"create-fes-app": "bin/create-fes-app.js"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
||||||
"directory": "packages/create-fes-app"
|
"directory": "packages/create-fes-app"
|
||||||
},
|
},
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
|
||||||
},
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"fes"
|
"fes"
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"main": "dist/index.mjs",
|
"author": "qlin",
|
||||||
"module": "dist/index.mjs",
|
"license": "MIT",
|
||||||
"bin": {
|
"bugs": {
|
||||||
"create-fes-app": "bin/create-fes-app.mjs"
|
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"bin",
|
|
||||||
"dist",
|
|
||||||
"templates/**/*"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"watch": "tsup --watch",
|
|
||||||
"build": "tsup"
|
|
||||||
},
|
},
|
||||||
|
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"citty": "^0.1.6",
|
"@fesjs/utils": "^3.0.3",
|
||||||
"consola": "^3.4.2",
|
"fs-extra": "^11.1.1",
|
||||||
"fs-extra": "^11.3.1",
|
"inquirer": "^7.3.3",
|
||||||
"glob": "^11.0.3",
|
"readline": "^1.3.0",
|
||||||
"mustache": "^4.2.0",
|
"validate-npm-package-name": "^3.0.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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fesjs/builder-webpack": "^4.0.0",
|
"@fesjs/builder-webpack": "^3.1.0",
|
||||||
"@fesjs/fes": "^4.0.0",
|
"@fesjs/fes": "^3.1.17",
|
||||||
"@fesjs/plugin-icon": "^5.0.0",
|
"@fesjs/plugin-icon": "^4.0.0",
|
||||||
"@fesjs/plugin-request": "^5.0.0",
|
"@fesjs/plugin-request": "^4.0.1",
|
||||||
"core-js": "^3.43.0",
|
"core-js": "^3.43.0",
|
||||||
"lodash-es": "^4.17.21",
|
|
||||||
"vue": "^3.5.17"
|
"vue": "^3.5.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^5.2.2",
|
"@antfu/eslint-config": "4.16.1",
|
||||||
"eslint": "^9.35.0",
|
"eslint": "9.29.0",
|
||||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
"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">
|
<template>
|
||||||
import { defineRouteMeta, useRequest, useRouter } from '@fesjs/fes'
|
<div class="onepiece">
|
||||||
import { onMounted, ref } from 'vue'
|
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({
|
defineRouteMeta({
|
||||||
title: '首页',
|
title: '首页',
|
||||||
})
|
});
|
||||||
|
|
||||||
const fes = ref('fes upgrade to vue3')
|
export default {
|
||||||
const rotate = ref(90)
|
setup() {
|
||||||
const router = useRouter()
|
const fes = ref('fes upgrade to vue3');
|
||||||
onMounted(() => {
|
const rotate = ref(90);
|
||||||
console.log(router)
|
const router = useRouter();
|
||||||
console.log('mounted1!!')
|
onMounted(() => {
|
||||||
})
|
console.log(router);
|
||||||
function clickIcon() {
|
console.log('mounted1!!');
|
||||||
console.log('click Icon')
|
});
|
||||||
}
|
const clickIcon = () => {
|
||||||
const { loading, data } = useRequest('api')
|
console.log('click Icon');
|
||||||
|
};
|
||||||
|
const { loading, data } = useRequest('api');
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
fes,
|
||||||
|
rotate,
|
||||||
|
clickIcon,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</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>
|
<style lang="less" scoped>
|
||||||
@import '~@/styles/mixins/hairline';
|
@import '~@/styles/mixins/hairline';
|
||||||
@import '~@/styles/mixins/hover';
|
@import '~@/styles/mixins/hover';
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<template>
|
||||||
import { defineRouteMeta } from '@fesjs/fes'
|
<div>{{ fes }}</div>
|
||||||
import { ref } from 'vue'
|
</template>
|
||||||
|
<script>
|
||||||
|
import { defineRouteMeta } from '@fesjs/fes';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
defineRouteMeta({
|
defineRouteMeta({
|
||||||
title: 'one piece',
|
title: 'one piece',
|
||||||
})
|
});
|
||||||
|
|
||||||
const fes = ref('fes upgrade to vue3')
|
export default {
|
||||||
|
setup() {
|
||||||
|
const fes = ref('fes upgrade to vue3');
|
||||||
|
return {
|
||||||
|
fes,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>{{ fes }}</div>
|
|
||||||
</template>
|
|
||||||
|
@ -1,34 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "@fesjs/template",
|
"name": "@fesjs/template",
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "fes项目模版",
|
"description": "fes项目模版",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "fes build",
|
"build": "fes build",
|
||||||
"prod": "FES_ENV=prod fes build",
|
"prod": "FES_ENV=prod fes build",
|
||||||
"analyze": "ANALYZE=1 fes build",
|
"analyze": "ANALYZE=1 fes build",
|
||||||
"dev": "fes dev",
|
"dev": "fes dev",
|
||||||
"test:unit": "fes test:unit"
|
"test:unit": "fes test:unit"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fesjs/builder-vite": "^5.0.0",
|
"@fesjs/builder-webpack": "^3.1.0",
|
||||||
"@fesjs/fes": "^4.0.0",
|
"@fesjs/fes": "^3.1.17",
|
||||||
"@fesjs/fes-design": "^0.8.82",
|
"@fesjs/fes-design": "^0.8.82",
|
||||||
"@fesjs/plugin-access": "^4.0.0",
|
"@fesjs/plugin-access": "^3.1.9",
|
||||||
"@fesjs/plugin-enums": "^4.0.0",
|
"@fesjs/plugin-enums": "^3.0.1",
|
||||||
"@fesjs/plugin-layout": "^6.0.0",
|
"@fesjs/plugin-layout": "^5.4.6",
|
||||||
"@fesjs/plugin-model": "^4.0.0",
|
"@fesjs/plugin-model": "^3.0.3",
|
||||||
"core-js": "^3.43.0",
|
"core-js": "^3.43.0",
|
||||||
"lodash-es": "^4.17.21",
|
"vue": "^3.5.17"
|
||||||
"vue": "^3.5.17"
|
},
|
||||||
},
|
"devDependencies": {
|
||||||
"devDependencies": {
|
"@antfu/eslint-config": "4.16.1",
|
||||||
"@antfu/eslint-config": "^5.2.2",
|
"eslint": "9.29.0",
|
||||||
"eslint": "^9.35.0",
|
"typescript": "5.8.3"
|
||||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
}
|
||||||
"typescript": "^5.9.2"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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>
|
<template>
|
||||||
<div class="page-loading">
|
<div class="page-loading">
|
||||||
<FSpin size="large" stroke="#5384ff" />
|
<f-spin size="large" stroke="#5384ff" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
|
import { FSpin } from '@fesjs/fes-design';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FSpin,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.page-loading {
|
.page-loading {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useModel } from '@fesjs/fes'
|
|
||||||
|
|
||||||
const initialState = useModel('@@initialState')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="right">
|
<div class="right">{{ initialState.userName }}</div>
|
||||||
{{ initialState.userName }}
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
<script>
|
||||||
|
import { useModel } from '@fesjs/fes';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const initialState = useModel('@@initialState');
|
||||||
|
return {
|
||||||
|
initialState,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<style scope>
|
<style scope>
|
||||||
.right {
|
.right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<template>
|
||||||
import { defineRouteMeta } from '@fesjs/fes'
|
<div style="padding: 32px">hello world</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineRouteMeta } from '@fesjs/fes';
|
||||||
|
|
||||||
defineRouteMeta({
|
defineRouteMeta({
|
||||||
name: 'index',
|
name: 'index',
|
||||||
title: '首页',
|
title: '首页',
|
||||||
})
|
});
|
||||||
</script>
|
</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
|
node_modules
|
||||||
lib
|
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}}}",
|
"name": "fes-plugin-{{{name}}}",
|
||||||
"version": "1.0.0",
|
"version": "3.0.0",
|
||||||
"description": "一个fes.js插件",
|
"description": "一个fes.js插件",
|
||||||
"main": "dist/index.mjs",
|
"main": "lib/index.js",
|
||||||
"module": "dist/index.mjs",
|
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"lib",
|
||||||
"README.md",
|
"README.md",
|
||||||
"types.d.ts"
|
"types.d.ts"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "tsup --watch",
|
"dev": "node scripts/build.js --watch",
|
||||||
"build": "tsup"
|
"build": "node scripts/build.js",
|
||||||
|
"lint": "eslint -c ./.eslintrc.js --ext .js,.jsx,.vue,.ts"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -19,14 +19,31 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^5.2.2",
|
"@babel/core": "^7.23.2",
|
||||||
"tsup": "^8.5.0",
|
"@babel/preset-env": "^7.23.2",
|
||||||
"fs-extra": "^11.3.1",
|
"@webank/eslint-config-webank": "1.2.7",
|
||||||
"eslint": "^9.34.0",
|
"chalk": "^4.1.2",
|
||||||
"typescript": "^5.9.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": {
|
"peerDependencies": {
|
||||||
"@fesjs/fes": "^4.0.0",
|
"@fesjs/fes": "^3.0.0",
|
||||||
"vue": "^3.5.20",
|
"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 { join } from 'path';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'fs';
|
||||||
import { name } from '../package.json';
|
import { name } from '../package.json';
|
||||||
|
|
||||||
const namespace = 'plugin-{{{name}}}';
|
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",
|
"name": "@fesjs/builder-vite",
|
||||||
"version": "5.0.0-beta.0",
|
"version": "4.0.5",
|
||||||
"description": "@fesjs/builder-vite",
|
"description": "@fesjs/builder-vite",
|
||||||
"author": "qlin",
|
"author": "qlin",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
||||||
"directory": "packages/builder-vite"
|
"directory": "packages/fes-builder-vite"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||||
@ -16,49 +16,44 @@
|
|||||||
"keywords": [
|
"keywords": [
|
||||||
"fes"
|
"fes"
|
||||||
],
|
],
|
||||||
"main": "dist/index.mjs",
|
"main": "lib/index.js",
|
||||||
"module": "dist/index.mjs",
|
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"lib",
|
||||||
"types.d.ts"
|
"types.d.ts"
|
||||||
],
|
],
|
||||||
"scripts": {
|
|
||||||
"watch": "tsup --watch",
|
|
||||||
"build": "tsup"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@fesjs/fes": "^4.0.0-beta.0",
|
"@fesjs/fes": "^3.1.12",
|
||||||
"core-js": "^3.45.1"
|
"core-js": "^3.29.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fesjs/shared": "^4.0.0-beta.0",
|
"@babel/core": "^7.23.3",
|
||||||
"@fesjs/utils": "^4.0.0-beta.0",
|
"@fesjs/utils": "^3.0.3",
|
||||||
"@rollup/pluginutils": "^5.1.0",
|
"@rollup/pluginutils": "^5.1.0",
|
||||||
"@vitejs/plugin-basic-ssl": "^2.1.0",
|
"@vitejs/plugin-basic-ssl": "^1.0.2",
|
||||||
"@vitejs/plugin-legacy": "^7.2.1",
|
"@vitejs/plugin-legacy": "^5.2.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^4.5.0",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.4",
|
||||||
"colorette": "^2.0.16",
|
"colorette": "^2.0.16",
|
||||||
"connect-history-api-fallback": "^2.0.0",
|
"connect-history-api-fallback": "^2.0.0",
|
||||||
"consola": "^3.4.2",
|
"consola": "^2.15.3",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"dotenv-expand": "^8.0.2",
|
"dotenv-expand": "^8.0.2",
|
||||||
"ejs": "^3.1.6",
|
"ejs": "^3.1.6",
|
||||||
"fast-glob": "^3.2.11",
|
"fast-glob": "^3.2.11",
|
||||||
"fs-extra": "^11.3.1",
|
"fs-extra": "^10.0.1",
|
||||||
"html-minifier-terser": "^7.2.0",
|
"html-minifier-terser": "^6.1.0",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"node-html-parser": "^5.3.3",
|
"node-html-parser": "^5.3.3",
|
||||||
"pathe": "^0.2.0",
|
"pathe": "^0.2.0",
|
||||||
"postcss-flexbugs-fixes": "^5.0.2",
|
"postcss-flexbugs-fixes": "^5.0.2",
|
||||||
"postcss-safe-parser": "^6.0.0",
|
"postcss-safe-parser": "^6.0.0",
|
||||||
"rollup-plugin-visualizer": "^6.0.3",
|
"rollup-plugin-visualizer": "^5.9.3",
|
||||||
"terser": "^5.24.0",
|
"terser": "^5.24.0",
|
||||||
"vite": "^7.1.4"
|
"vite": "^5.0.3"
|
||||||
},
|
},
|
||||||
"typings": "./types.d.ts"
|
"typings": "./types.d.ts"
|
||||||
}
|
}
|
@ -1,14 +1,9 @@
|
|||||||
import type { IPluginAPI } from '@fesjs/shared';
|
import { splitVendorChunkPlugin } from 'vite';
|
||||||
import type { InlineConfig, UserConfig } from 'vite';
|
|
||||||
import type { ViteBuildConfig } from '../../shared';
|
|
||||||
import legacy from '@vitejs/plugin-legacy';
|
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';
|
import { getInnerCommonConfig } from '../../common/getConfig';
|
||||||
|
|
||||||
function getEsbuildTarget(targets: any): string[] {
|
function getEsbuildTarget(targets) {
|
||||||
const result: string[] = [];
|
const result = [];
|
||||||
['chrome', 'edge', 'firefox', 'hermes', 'ios', 'node', 'opera', 'rhino', 'safari'].forEach((key) => {
|
['chrome', 'edge', 'firefox', 'hermes', 'ios', 'node', 'opera', 'rhino', 'safari'].forEach((key) => {
|
||||||
if (targets[key]) {
|
if (targets[key]) {
|
||||||
result.push(`${key}${targets[key]}`);
|
result.push(`${key}${targets[key]}`);
|
||||||
@ -17,20 +12,20 @@ function getEsbuildTarget(targets: any): string[] {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async (api: IPluginAPI<ViteBuildConfig>): Promise<InlineConfig> => {
|
export default async (api) => {
|
||||||
const { deepmerge, getTargetsAndBrowsersList } = api.utils;
|
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 { targets, browserslist } = getTargetsAndBrowsersList({ config: api.config });
|
||||||
|
|
||||||
const bundleConfig: InlineConfig = deepmerge(getInnerCommonConfig(api), {
|
const bundleConfig = deepmerge(getInnerCommonConfig(api), {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
css: {
|
css: {
|
||||||
postcss: {
|
postcss: {
|
||||||
plugins: [
|
plugins: [
|
||||||
postcssFlexbugsFixes,
|
require('postcss-flexbugs-fixes'),
|
||||||
postcssSafeParser,
|
require('postcss-safe-parser'),
|
||||||
autoprefixer({
|
require('autoprefixer')({
|
||||||
...api.config.autoprefixer,
|
...api.config.autoprefixer,
|
||||||
overrideBrowserslist: browserslist,
|
overrideBrowserslist: browserslist,
|
||||||
}),
|
}),
|
||||||
@ -44,10 +39,12 @@ export default async (api: IPluginAPI<ViteBuildConfig>): Promise<InlineConfig> =
|
|||||||
targets,
|
targets,
|
||||||
...api.config.viteLegacy,
|
...api.config.viteLegacy,
|
||||||
}),
|
}),
|
||||||
|
splitVendorChunkPlugin(),
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
...build,
|
...build,
|
||||||
terserOptions: build.terserOptions || api.config.terserOptions,
|
terserOptions: build.terserOptions || api.config.terserOptions,
|
||||||
|
target: build.target || getEsbuildTarget(targets),
|
||||||
outDir: build.outDir || api.config.outputPath || 'dist',
|
outDir: build.outDir || api.config.outputPath || 'dist',
|
||||||
assetsDir: build.assetsDir || 'static',
|
assetsDir: build.assetsDir || 'static',
|
||||||
assetsInlineLimit: build.assetsInlineLimit || api.config.inlineLimit || 8192,
|
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 basicSsl from '@vitejs/plugin-basic-ssl';
|
||||||
import { getInnerCommonConfig } from '../../common/getConfig';
|
import { getInnerCommonConfig } from '../../common/getConfig';
|
||||||
import viteMiddlewarePlugin from './viteMiddlewarePlugin';
|
import viteMiddlewarePlugin from './viteMiddlewarePlugin';
|
||||||
|
|
||||||
interface Args {
|
export default async (api, args) => {
|
||||||
port?: string | number;
|
|
||||||
https?: boolean;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async (api: IPluginAPI, args: Args): Promise<InlineConfig> => {
|
|
||||||
const { deepmerge, getPort, changePort, getHostName } = api.utils;
|
const { deepmerge, getPort, changePort, getHostName } = api.utils;
|
||||||
|
|
||||||
const viteOption = api.config.vite || api.config.viteOption || {};
|
const port = await getPort(process.env.PORT || args.port || api.config.viteOption.server?.port);
|
||||||
|
|
||||||
const port = await getPort(process.env.PORT || args.port || viteOption.server?.port);
|
|
||||||
changePort(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({
|
const beforeMiddlewares = await api.applyPlugins({
|
||||||
key: 'addBeforeMiddlewares',
|
key: 'addBeforeMiddlewares',
|
||||||
@ -36,9 +25,9 @@ export default async (api: IPluginAPI, args: Args): Promise<InlineConfig> => {
|
|||||||
args: {},
|
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',
|
mode: 'development',
|
||||||
plugins: [viteMiddlewarePlugin(beforeMiddlewares, middlewares), isHTTPS && basicSsl()].filter(Boolean),
|
plugins: [viteMiddlewarePlugin(beforeMiddlewares, middlewares), isHTTPS && basicSsl()].filter(Boolean),
|
||||||
server: {
|
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 { createServer } from 'vite';
|
||||||
import getDevConfig from './getDevConfig';
|
import getDevConfig from './getDevConfig';
|
||||||
|
|
||||||
interface Args {
|
export default (api) => {
|
||||||
args?: Record<string, any>;
|
|
||||||
rawArgv?: Record<string, any>;
|
|
||||||
options?: Record<string, any>;
|
|
||||||
program?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (api: IPluginAPI) => {
|
|
||||||
const {
|
const {
|
||||||
paths,
|
paths,
|
||||||
utils: { chalk, rimraf },
|
utils: { chalk, rimraf },
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
let server: ViteDevServer | undefined;
|
let server;
|
||||||
|
|
||||||
function destroy() {
|
function destroy() {
|
||||||
if (server) {
|
server?.close();
|
||||||
server.close().catch(() => {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
api.registerCommand({
|
api.registerCommand({
|
||||||
@ -38,7 +26,7 @@ export default (api: IPluginAPI) => {
|
|||||||
description: 'whether to turn on the https service',
|
description: 'whether to turn on the https service',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
async fn({ args = {} }: Args) {
|
async fn({ args = {} }) {
|
||||||
rimraf.sync(paths.absTmpPath);
|
rimraf.sync(paths.absTmpPath);
|
||||||
|
|
||||||
await api.applyPlugins({
|
await api.applyPlugins({
|
||||||
@ -62,14 +50,11 @@ export default (api: IPluginAPI) => {
|
|||||||
api.registerMethod({
|
api.registerMethod({
|
||||||
name: 'restartServer',
|
name: 'restartServer',
|
||||||
fn() {
|
fn() {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(chalk.gray('Try to restart dev server...'));
|
console.log(chalk.gray('Try to restart dev server...'));
|
||||||
destroy();
|
destroy();
|
||||||
if (typeof process !== 'undefined' && process.send) {
|
process.send({
|
||||||
process.send({
|
type: 'RESTART',
|
||||||
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