mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2026-07-13 05:01:10 +08:00
Compare commits
9 Commits
ed26dfb39b
...
c1fa59fcf8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1fa59fcf8 | ||
|
|
720296fc82 | ||
|
|
4158a1aee2 | ||
|
|
8297d91004 | ||
|
|
77b651c50b | ||
|
|
ad860c8f20 | ||
|
|
fd811c5269 | ||
|
|
f29492ebe0 | ||
|
|
3c77c9a2b6 |
2
.npmrc
2
.npmrc
@ -1,2 +1,4 @@
|
||||
registry=https://registry.npmmirror.com
|
||||
link-workspace-packages=true
|
||||
prefer-workspace-packages=true
|
||||
shamefully-hoist=true
|
||||
|
||||
@ -56,12 +56,10 @@ It mainly has the following functions:
|
||||
| [@fesjs/plugin-access](http://fesjs.mumblefe.cn/reference/plugin/plugins/access.html) | Provides the ability to control the permissions of page resources |
|
||||
| [@fesjs/plugin-enums](http://fesjs.mumblefe.cn/reference/plugin/plugins/enums.html#%E4%BB%8B%E7%BB%8D) | Provide unified enumeration access and rich functions to handle enumeration |
|
||||
| [@fesjs/plugin-icon](http://fesjs.mumblefe.cn/reference/plugin/plugins/icon.html#%E4%BB%8B%E7%BB%8D) | svg file is automatically registered as a component |
|
||||
| [@fesjs/plugin-jest](http://fesjs.mumblefe.cn/reference/plugin/plugins/jest.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | Based on `Jest`, provide unit testing and coverage testing capabilities |
|
||||
| [@fesjs/plugin-layout](http://fesjs.mumblefe.cn/reference/plugin/plugins/layout.html) | Simple configuration to have a layout, including navigation and sidebar |
|
||||
| [@fesjs/plugin-locale](http://fesjs.mumblefe.cn/reference/plugin/plugins/locale.html#%E4%BB%8B%E7%BB%8D) | Based on `Vue I18n`, providing internationalization capabilities |
|
||||
| [@fesjs/plugin-model](http://fesjs.mumblefe.cn/reference/plugin/plugins/model.html#%E4%BB%8B%E7%BB%8D) | Simple data management solution |
|
||||
| [@fesjs/plugin-request](http://fesjs.mumblefe.cn/reference/plugin/plugins/request.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | Based on the request encapsulated by `Axios`, built-in functions such as preventing repeated requests, request throttling, and error handling |
|
||||
| [@fesjs/plugin-vuex](http://fesjs.mumblefe.cn/reference/plugin/plugins/vuex.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | Based on `Vuex`, provide state management capabilities |
|
||||
| [@fesjs/plugin-qiankun](http://fesjs.mumblefe.cn/reference/plugin/plugins/qiankun.html#%E4%BB%8B%E7%BB%8D) | Based on `qiankun`, provide microservice capabilities |
|
||||
| [@fesjs/plugin-sass](http://fesjs.mumblefe.cn/reference/plugin/plugins/sass.html#%E4%BB%8B%E7%BB%8D) | Style support sass |
|
||||
| [@fesjs/plugin-monaco-editor](http://fesjs.mumblefe.cn/reference/plugin/plugins/editor.html#%E4%BB%8B%E7%BB%8D) | Provide code editor capability, based on `monaco-editor` (code editor used by VS Code) |
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
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: [],
|
||||
};
|
||||
18
docs/guide/upgrade4.md
Normal file
18
docs/guide/upgrade4.md
Normal file
@ -0,0 +1,18 @@
|
||||
# 从 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]
|
||||
@ -32,11 +32,11 @@
|
||||
"hooks:sync": "simple-git-hooks"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^5.0.1",
|
||||
"conventional-changelog-cli": "^4.1.0",
|
||||
"enquirer": "^2.3.6",
|
||||
"consola": "^3.4.2",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"execa": "^6.1.0",
|
||||
"minimist": "^1.2.6",
|
||||
"picocolors": "^1.1.1",
|
||||
"semver": "^7.3.6",
|
||||
"tsup": "^8.5.0",
|
||||
"turbo": "^2.5.6"
|
||||
@ -55,8 +55,7 @@
|
||||
"simple-git-hooks": "^2.9.0",
|
||||
"typescript": "^5.9.2",
|
||||
"vitepress": "1.0.0-alpha.73",
|
||||
"vue": "^3.5.21",
|
||||
"yargs-parser": "^21.1.1"
|
||||
"vue": "^3.5.21"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "npx lint-staged",
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
"@vue/babel-plugin-jsx": "^1.5.0",
|
||||
"ajv": "^8.12.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"babel-loader": "^9.1.2",
|
||||
"babel-loader": "^10.0.0",
|
||||
"cli-highlight": "^2.1.11",
|
||||
"cliui": "^8.0.1",
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
|
||||
@ -142,10 +142,11 @@ export default class Config {
|
||||
}
|
||||
|
||||
async requireConfigs(configFiles: string[]): Promise<any[]> {
|
||||
const models = await Promise.all(configFiles.map(f => {
|
||||
const models = await Promise.all(configFiles.map((f) => {
|
||||
// 使用 pathToFileURL 确保在 Windows 下路径格式正确
|
||||
const fileUrl = pathToFileURL(f).href;
|
||||
return import(fileUrl);
|
||||
// 避免命中模块缓存
|
||||
return import(`${fileUrl}?t=${Date.now()}`);
|
||||
}));
|
||||
return models.map(m => compatESModuleRequire(m));
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
Used in bin/create-fes-app.js to determine if it is in the local debug state.
|
||||
Used in bin/create-fes-app.mjs to determine if it is in the local debug state.
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('../lib/cli');
|
||||
5
packages/create-fes-app/bin/create-fes-app.mjs
Executable file
5
packages/create-fes-app/bin/create-fes-app.mjs
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { runMain } from '../dist/index.mjs';
|
||||
|
||||
runMain();
|
||||
@ -17,23 +17,34 @@
|
||||
"fes"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"main": "lib/index.js",
|
||||
"main": "dist/index.mjs",
|
||||
"module": "dist/index.mjs",
|
||||
"bin": {
|
||||
"create-fes-app": "bin/create-fes-app.js"
|
||||
"create-fes-app": "bin/create-fes-app.mjs"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"dist",
|
||||
"templates/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "tsup --watch --sourcemap",
|
||||
"build": "tsup"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/utils": "^3.0.3",
|
||||
"fs-extra": "^11.1.1",
|
||||
"inquirer": "^7.3.3",
|
||||
"readline": "^1.3.0",
|
||||
"validate-npm-package-name": "^3.0.0"
|
||||
"citty": "^0.1.6",
|
||||
"consola": "^3.4.2",
|
||||
"fs-extra": "^11.3.1",
|
||||
"glob": "^11.0.3",
|
||||
"mustache": "^4.2.0",
|
||||
"ora": "^8.2.0",
|
||||
"semver": "^7.7.2",
|
||||
"validate-npm-package-name": "^6.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/validate-npm-package-name": "^4.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { chalk, yParser } from '@fesjs/utils';
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { chalk } from '@fesjs/utils';
|
||||
import fs from 'fs-extra';
|
||||
import inquirer from 'inquirer';
|
||||
import validateProjectName from 'validate-npm-package-name';
|
||||
|
||||
import AppGenerator from './generator/App';
|
||||
import PluginGenerator from './generator/Plugin';
|
||||
import { clearConsole } from './utils';
|
||||
|
||||
v;
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
2
packages/create-fes-app/src/index.ts
Normal file
2
packages/create-fes-app/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { main } from './main';
|
||||
export { runMain } from './run';
|
||||
116
packages/create-fes-app/src/main.ts
Normal file
116
packages/create-fes-app/src/main.ts
Normal file
@ -0,0 +1,116 @@
|
||||
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'));
|
||||
}
|
||||
},
|
||||
});
|
||||
5
packages/create-fes-app/src/run.ts
Normal file
5
packages/create-fes-app/src/run.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { runMain as _runMain } from 'citty';
|
||||
|
||||
import { main } from './main';
|
||||
|
||||
export const runMain = () => _runMain(main);
|
||||
27
packages/create-fes-app/src/shared.ts
Normal file
27
packages/create-fes-app/src/shared.ts
Normal file
@ -0,0 +1,27 @@
|
||||
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));
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import process from 'node:process';
|
||||
import readline from 'node:readline';
|
||||
|
||||
export function 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
packages/create-fes-app/src/utils/console.ts
Normal file
17
packages/create-fes-app/src/utils/console.ts
Normal file
@ -0,0 +1,17 @@
|
||||
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));
|
||||
}
|
||||
17
packages/create-fes-app/src/utils/engines.ts
Normal file
17
packages/create-fes-app/src/utils/engines.ts
Normal file
@ -0,0 +1,17 @@
|
||||
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}\`.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
46
packages/create-fes-app/src/utils/gen.ts
Normal file
46
packages/create-fes-app/src/utils/gen.ts
Normal file
@ -0,0 +1,46 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
3
packages/create-fes-app/src/utils/logger.ts
Normal file
3
packages/create-fes-app/src/utils/logger.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { consola } from 'consola';
|
||||
|
||||
export const logger = consola.withTag('fes');
|
||||
@ -12,17 +12,18 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-webpack": "^3.1.0",
|
||||
"@fesjs/fes": "^3.1.17",
|
||||
"@fesjs/plugin-icon": "^4.0.0",
|
||||
"@fesjs/plugin-request": "^4.0.1",
|
||||
"@fesjs/builder-webpack": "^4.0.0",
|
||||
"@fesjs/fes": "^4.0.0",
|
||||
"@fesjs/plugin-icon": "^5.0.0",
|
||||
"@fesjs/plugin-request": "^5.0.0",
|
||||
"core-js": "^3.43.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"vue": "^3.5.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "4.16.1",
|
||||
"eslint": "9.29.0",
|
||||
"@antfu/eslint-config": "^5.2.2",
|
||||
"eslint": "^9.35.0",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
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: [],
|
||||
},
|
||||
});
|
||||
42
packages/create-fes-app/templates/app/h5/src/app.ts
Normal file
42
packages/create-fes-app/templates/app/h5/src/app.ts
Normal file
@ -0,0 +1,42 @@
|
||||
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 || `请求失败`)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -1,64 +0,0 @@
|
||||
// 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);
|
||||
};
|
||||
}
|
||||
61
packages/create-fes-app/templates/app/h5/src/common/utils.ts
Normal file
61
packages/create-fes-app/templates/app/h5/src/common/utils.ts
Normal file
@ -0,0 +1,61 @@
|
||||
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,43 +1,37 @@
|
||||
<template>
|
||||
<div class="onepiece">
|
||||
fes & 拉夫德鲁 <br />
|
||||
<fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" />
|
||||
<div v-if="loading" class="loading">loading</div>
|
||||
<div v-else class="data">{{ data }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRouter, useRequest, defineRouteMeta } from '@fesjs/fes';
|
||||
<script setup lang="ts">
|
||||
import { defineRouteMeta, useRequest, useRouter } from '@fesjs/fes'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
defineRouteMeta({
|
||||
title: '首页',
|
||||
});
|
||||
title: '首页',
|
||||
})
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const fes = ref('fes upgrade to vue3');
|
||||
const rotate = ref(90);
|
||||
const router = useRouter();
|
||||
onMounted(() => {
|
||||
console.log(router);
|
||||
console.log('mounted1!!');
|
||||
});
|
||||
const clickIcon = () => {
|
||||
console.log('click Icon');
|
||||
};
|
||||
const { loading, data } = useRequest('api');
|
||||
return {
|
||||
loading,
|
||||
data,
|
||||
fes,
|
||||
rotate,
|
||||
clickIcon,
|
||||
};
|
||||
},
|
||||
};
|
||||
const fes = ref('fes upgrade to vue3')
|
||||
const rotate = ref(90)
|
||||
const router = useRouter()
|
||||
onMounted(() => {
|
||||
console.log(router)
|
||||
console.log('mounted1!!')
|
||||
})
|
||||
function clickIcon() {
|
||||
console.log('click Icon')
|
||||
}
|
||||
const { loading, data } = useRequest('api')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="onepiece">
|
||||
fes & 拉夫德鲁 <br>
|
||||
<fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" />
|
||||
<div v-if="loading" class="loading">
|
||||
loading
|
||||
</div>
|
||||
<div v-else class="data">
|
||||
{{ data }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '~@/styles/mixins/hairline';
|
||||
@import '~@/styles/mixins/hover';
|
||||
|
||||
@ -1,20 +1,14 @@
|
||||
<template>
|
||||
<div>{{ fes }}</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineRouteMeta } from '@fesjs/fes';
|
||||
import { ref } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { defineRouteMeta } from '@fesjs/fes'
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineRouteMeta({
|
||||
title: 'one piece',
|
||||
});
|
||||
title: 'one piece',
|
||||
})
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const fes = ref('fes upgrade to vue3');
|
||||
return {
|
||||
fes,
|
||||
};
|
||||
},
|
||||
};
|
||||
const fes = ref('fes upgrade to vue3')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>{{ fes }}</div>
|
||||
</template>
|
||||
|
||||
@ -1,32 +1,34 @@
|
||||
{
|
||||
"name": "@fesjs/template",
|
||||
"version": "3.0.0",
|
||||
"private": true,
|
||||
"description": "fes项目模版",
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
"test:unit": "fes test:unit"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-webpack": "^3.1.0",
|
||||
"@fesjs/fes": "^3.1.17",
|
||||
"@fesjs/fes-design": "^0.8.82",
|
||||
"@fesjs/plugin-access": "^3.1.9",
|
||||
"@fesjs/plugin-enums": "^3.0.1",
|
||||
"@fesjs/plugin-layout": "^5.4.6",
|
||||
"@fesjs/plugin-model": "^3.0.3",
|
||||
"core-js": "^3.43.0",
|
||||
"vue": "^3.5.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "4.16.1",
|
||||
"eslint": "9.29.0",
|
||||
"typescript": "5.8.3"
|
||||
}
|
||||
"name": "@fesjs/template",
|
||||
"version": "3.0.0",
|
||||
"private": true,
|
||||
"description": "fes项目模版",
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
"test:unit": "fes test:unit"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-vite": "^5.0.0",
|
||||
"@fesjs/fes": "^4.0.0",
|
||||
"@fesjs/fes-design": "^0.8.82",
|
||||
"@fesjs/plugin-access": "^4.0.0",
|
||||
"@fesjs/plugin-enums": "^4.0.0",
|
||||
"@fesjs/plugin-layout": "^6.0.0",
|
||||
"@fesjs/plugin-model": "^4.0.0",
|
||||
"core-js": "^3.43.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"vue": "^3.5.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.2.2",
|
||||
"eslint": "^9.35.0",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
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 />,
|
||||
},
|
||||
});
|
||||
61
packages/create-fes-app/templates/app/pc/src/app.tsx
Normal file
61
packages/create-fes-app/templates/app/pc/src/app.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
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,20 +1,13 @@
|
||||
<template>
|
||||
<div class="page-loading">
|
||||
<f-spin size="large" stroke="#5384ff" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { FSpin } from '@fesjs/fes-design';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FSpin,
|
||||
},
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
<script setup lang="ts">
|
||||
import { FSpin } from '@fesjs/fes-design'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-loading">
|
||||
<FSpin size="large" stroke="#5384ff" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.page-loading {
|
||||
position: fixed;
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
<template>
|
||||
<div class="right">{{ initialState.userName }}</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useModel } from '@fesjs/fes';
|
||||
<script setup lang="ts">
|
||||
import { useModel } from '@fesjs/fes'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const initialState = useModel('@@initialState');
|
||||
return {
|
||||
initialState,
|
||||
};
|
||||
},
|
||||
};
|
||||
const initialState = useModel('@@initialState')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="right">
|
||||
{{ initialState.userName }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scope>
|
||||
.right {
|
||||
text-align: right;
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div style="padding: 32px">hello world</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineRouteMeta } from '@fesjs/fes';
|
||||
<script setup lang="ts">
|
||||
import { defineRouteMeta } from '@fesjs/fes'
|
||||
|
||||
defineRouteMeta({
|
||||
name: 'index',
|
||||
title: '首页',
|
||||
});
|
||||
name: 'index',
|
||||
title: '首页',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 32px">
|
||||
hello world
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
# 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
|
||||
@ -1,21 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['@webank/eslint-config-webank/vue.js'],
|
||||
globals: {
|
||||
// 这里填入你的项目需要的全局变量
|
||||
// 这里值为 false 表示这个全局变量不允许被重新赋值,比如:
|
||||
//
|
||||
// Vue: false
|
||||
__DEV__: false,
|
||||
},
|
||||
rules: {
|
||||
'vue/comment-directive': 'off',
|
||||
'global-require': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-undefined': 'off',
|
||||
'vue/valid-template-root': 'off',
|
||||
},
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
};
|
||||
@ -1,2 +1,8 @@
|
||||
.DS_Store
|
||||
|
||||
|
||||
node_modules
|
||||
lib
|
||||
lib
|
||||
dist
|
||||
npm-debug.log
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
1
packages/create-fes-app/templates/plugin/README.md
Normal file
1
packages/create-fes-app/templates/plugin/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Fes.js 插件
|
||||
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
copy: ['runtime'],
|
||||
};
|
||||
36
packages/create-fes-app/templates/plugin/eslint.config.js
Normal file
36
packages/create-fes-app/templates/plugin/eslint.config.js
Normal file
@ -0,0 +1,36 @@
|
||||
// eslint.config.js
|
||||
import antfu from '@antfu/eslint-config';
|
||||
|
||||
export default await antfu({
|
||||
stylistic: {
|
||||
indent: 4,
|
||||
quotes: 'single',
|
||||
semi: 'always',
|
||||
ignores: ['*.yaml'],
|
||||
},
|
||||
typescript: true,
|
||||
vue: true,
|
||||
rules: {
|
||||
'curly': ['error', 'multi-line'],
|
||||
'vue/block-order': [
|
||||
'error',
|
||||
{
|
||||
order: ['template', 'script', 'style'],
|
||||
},
|
||||
],
|
||||
'style/member-delimiter-style': [
|
||||
'error',
|
||||
{
|
||||
multiline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: true,
|
||||
},
|
||||
singleline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: false,
|
||||
},
|
||||
multilineDetection: 'brackets',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "fes-plugin-{{{name}}}",
|
||||
"version": "3.0.0",
|
||||
"version": "1.0.0",
|
||||
"description": "一个fes.js插件",
|
||||
"main": "lib/index.js",
|
||||
"main": "dist/index.mjs",
|
||||
"module": "dist/index.mjs",
|
||||
"files": [
|
||||
"lib",
|
||||
"dist",
|
||||
"README.md",
|
||||
"types.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "node scripts/build.js --watch",
|
||||
"build": "node scripts/build.js",
|
||||
"lint": "eslint -c ./.eslintrc.js --ext .js,.jsx,.vue,.ts"
|
||||
"dev": "tsup --watch --sourcemap",
|
||||
"build": "tsup"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -19,31 +19,14 @@
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/preset-env": "^7.23.2",
|
||||
"@webank/eslint-config-webank": "1.2.7",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.2",
|
||||
"deepmerge": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"husky": "^4.3.0",
|
||||
"lint-staged": "^10.4.0",
|
||||
"yargs-parser": "^20.2.9"
|
||||
"@antfu/eslint-config": "^5.2.2",
|
||||
"tsup": "^8.5.0",
|
||||
"fs-extra": "^11.3.1",
|
||||
"eslint": "^9.34.0",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fesjs/fes": "^3.0.0",
|
||||
"vue": "^3.2.47"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,vue,ts}": [
|
||||
"eslint --format=codeframe"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged",
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"typings": "./types.d.ts"
|
||||
"@fesjs/fes": "^4.0.0",
|
||||
"vue": "^3.5.20",
|
||||
}
|
||||
}
|
||||
@ -1,149 +0,0 @@
|
||||
// 关闭 import 规则
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const chalk = require('chalk');
|
||||
const chokidar = require('chokidar');
|
||||
const merge = require('deepmerge');
|
||||
const fse = require('fs-extra');
|
||||
const argv = require('yargs-parser')(process.argv.slice(2));
|
||||
|
||||
const pkg = require('../package.json');
|
||||
const compiler = require('./compiler');
|
||||
const randomColor = require('./randomColor');
|
||||
|
||||
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();
|
||||
@ -1,52 +0,0 @@
|
||||
// 关闭 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;
|
||||
@ -1,35 +0,0 @@
|
||||
/* 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 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'node:path';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { name } from '../package.json';
|
||||
|
||||
const namespace = 'plugin-{{{name}}}';
|
||||
15
packages/create-fes-app/templates/plugin/tsconfig.json
Normal file
15
packages/create-fes-app/templates/plugin/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"rootDir": "./src",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
15
packages/create-fes-app/templates/plugin/tsup.config.ts
Normal file
15
packages/create-fes-app/templates/plugin/tsup.config.ts
Normal file
@ -0,0 +1,15 @@
|
||||
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');
|
||||
},
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
import {} from '@fesjs/fes';
|
||||
declare module "@fesjs/fes" {
|
||||
interface PluginBuildConfig {
|
||||
|
||||
}
|
||||
|
||||
interface PluginRuntimeConfig {
|
||||
|
||||
}
|
||||
}
|
||||
9
packages/create-fes-app/tsconfig.json
Normal file
9
packages/create-fes-app/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": ["@fesjs/typescript-config/base.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
11
packages/create-fes-app/tsup.config.ts
Normal file
11
packages/create-fes-app/tsup.config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
clean: true,
|
||||
dts: false,
|
||||
shims: true,
|
||||
format: ['esm'],
|
||||
});
|
||||
@ -7,7 +7,7 @@
|
||||
"author": "qlin",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "FES_ENV=prod fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"dev": "fes dev"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@ -1,13 +1,19 @@
|
||||
<template>
|
||||
<div class="onepiece m-10px text-yellow-700">fes h5 & 拉夫德鲁<br /></div>
|
||||
<div class="onepiece m-10px text-yellow-700">
|
||||
fes h5 & 拉夫德鲁<br>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineRouteMeta } from '@fesjs/fes';
|
||||
import { defineRouteMeta, request } from '@fesjs/fes';
|
||||
|
||||
defineRouteMeta({
|
||||
title: '首页',
|
||||
name: 'testIndex',
|
||||
layout: false,
|
||||
});
|
||||
|
||||
request('/ras-mas/a/b').then((res) => {
|
||||
console.log('res', res);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -6,6 +6,7 @@ export default defineBuildConfig({
|
||||
define: {
|
||||
__DEV__: false,
|
||||
},
|
||||
publicPath: './',
|
||||
title: '海贼王',
|
||||
router: {
|
||||
mode: 'hash',
|
||||
@ -71,6 +72,8 @@ export default defineBuildConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
],
|
||||
server: {
|
||||
},
|
||||
},
|
||||
// enums: {
|
||||
// status: [
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
"author": "harrywan",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import { resolveConfig } from 'vite';
|
||||
|
||||
console.log(await resolveConfig({
|
||||
|
||||
}));
|
||||
@ -7,7 +7,6 @@
|
||||
"author": "harrywan",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
|
||||
@ -8,9 +8,13 @@ export default defineConfig({
|
||||
clean: true,
|
||||
dts: false,
|
||||
shims: true,
|
||||
outExtension: () => ({ js: '.mjs' }),
|
||||
onSuccess() {
|
||||
copySync('public', 'dist');
|
||||
},
|
||||
outExtension({ format }) {
|
||||
return {
|
||||
js: format === 'esm' ? '.mjs' : '.cjs',
|
||||
};
|
||||
},
|
||||
format: ['esm'],
|
||||
});
|
||||
|
||||
@ -6,48 +6,23 @@
|
||||
"description": "fes项目模版",
|
||||
"author": "harrywan",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"管理端",
|
||||
"fes",
|
||||
"fast",
|
||||
"easy",
|
||||
"strong"
|
||||
],
|
||||
"files": [
|
||||
".eslintrc.js",
|
||||
".fes.js",
|
||||
".fes.prod.js",
|
||||
".gitignore",
|
||||
"/config",
|
||||
"/src",
|
||||
"README.md",
|
||||
"mock.js",
|
||||
"package.json",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
"test": "fes test"
|
||||
"dev": "fes dev"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-vite": "file:../../../builder-vite",
|
||||
"@fesjs/fes": "file:../../../fes",
|
||||
"@fesjs/builder-vite": "workspace:*",
|
||||
"@fesjs/fes": "workspace:*",
|
||||
"@fesjs/fes-design": "^0.8.0",
|
||||
"@fesjs/plugin-access": "file:../../../plugin-access",
|
||||
"@fesjs/plugin-layout": "file:../../../plugin-layout",
|
||||
"@fesjs/plugin-model": "file:../../../plugin-model",
|
||||
"@fesjs/plugin-qiankun": "file:../../../plugin-qiankun",
|
||||
"@fesjs/plugin-watermark": "file:../../../plugin-watermark",
|
||||
"@fesjs/plugin-access": "workspace:*",
|
||||
"@fesjs/plugin-layout": "workspace:*",
|
||||
"@fesjs/plugin-model": "workspace:*",
|
||||
"@fesjs/plugin-qiankun": "workspace:*",
|
||||
"@fesjs/plugin-watermark": "workspace:*",
|
||||
"vue": "^3.5.21"
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,50 +5,20 @@
|
||||
"private": true,
|
||||
"description": "fes项目模版",
|
||||
"author": "harrywan",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
||||
"directory": "packages/plugin-qiankun"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"管理端",
|
||||
"fes",
|
||||
"fast",
|
||||
"easy",
|
||||
"strong"
|
||||
],
|
||||
"files": [
|
||||
".eslintrc.js",
|
||||
".fes.js",
|
||||
".fes.prod.js",
|
||||
".gitignore",
|
||||
"/config",
|
||||
"README.md",
|
||||
"mock.js",
|
||||
"package.json",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
"test": "fes test"
|
||||
"dev": "fes dev"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-vite": "file:../../../builder-vite",
|
||||
"@fesjs/fes": "file:../../../fes",
|
||||
"@fesjs/builder-vite": "workspace:*",
|
||||
"@fesjs/fes": "workspace:*",
|
||||
"@fesjs/fes-design": "^0.8.0",
|
||||
"@fesjs/plugin-qiankun": "file:../../../plugin-qiankun",
|
||||
"@fesjs/plugin-watermark": "file:../../../plugin-watermark",
|
||||
"@fesjs/plugin-qiankun": "workspace:*",
|
||||
"@fesjs/plugin-watermark": "workspace:*",
|
||||
"vue": "^3.5.21"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,3 @@
|
||||
// .fes.js 只负责管理编译时配置,只能使用plain Object
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
export function esmResolve(specifier) {
|
||||
const esmRequire = createRequire(import.meta.url);
|
||||
return esmRequire.resolve(specifier);
|
||||
}
|
||||
|
||||
export default {
|
||||
access: {
|
||||
roles: {
|
||||
|
||||
@ -6,31 +6,7 @@
|
||||
"description": "fes项目模版",
|
||||
"author": "harrywan",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"管理端",
|
||||
"fes",
|
||||
"fast",
|
||||
"easy",
|
||||
"strong"
|
||||
],
|
||||
"files": [
|
||||
".eslintrc.js",
|
||||
".fes.js",
|
||||
".fes.prod.js",
|
||||
".gitignore",
|
||||
"/config",
|
||||
"/src",
|
||||
"README.md",
|
||||
"mock.js",
|
||||
"package.json",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
@ -40,14 +16,14 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-webpack": "file:../../../builder-webpack",
|
||||
"@fesjs/fes": "file:../../../fes",
|
||||
"@fesjs/builder-webpack": "workspace:*",
|
||||
"@fesjs/fes": "workspace:*",
|
||||
"@fesjs/fes-design": "^0.8.0",
|
||||
"@fesjs/plugin-access": "file:../../../plugin-access",
|
||||
"@fesjs/plugin-layout": "file:../../../plugin-layout",
|
||||
"@fesjs/plugin-model": "file:../../../plugin-model",
|
||||
"@fesjs/plugin-qiankun": "file:../../../plugin-qiankun",
|
||||
"@fesjs/plugin-watermark": "file:../../../plugin-watermark",
|
||||
"@fesjs/plugin-access": "workspace:*",
|
||||
"@fesjs/plugin-layout": "workspace:*",
|
||||
"@fesjs/plugin-model": "workspace:*",
|
||||
"@fesjs/plugin-qiankun": "workspace:*",
|
||||
"@fesjs/plugin-watermark": "workspace:*",
|
||||
"vue": "^3.5.21"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,37 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "build/dist",
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"lib": ["esnext", "dom"],
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"jsx": "preserve",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"moduleResolution": "node",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"noUnusedLocals": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"experimentalDecorators": true,
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@@/*": ["./src/.fes/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"tests/**/*",
|
||||
"test/**/*",
|
||||
"__test__/**/*",
|
||||
"typings/**/*",
|
||||
"config/**/*",
|
||||
".eslintrc.js",
|
||||
".stylelintrc.js",
|
||||
".prettierrc.js"
|
||||
],
|
||||
"exclude": ["node_modules", "build", "dist", "scripts", "src/.fes/*", "webpack", "jest"]
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"jsx": "preserve",
|
||||
"lib": ["esnext", "dom"],
|
||||
"experimentalDecorators": true,
|
||||
"baseUrl": ".",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@@/*": ["./src/.fes/*"]
|
||||
},
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "build/dist",
|
||||
"sourceMap": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@ -11,21 +11,19 @@
|
||||
"url": "https://github.com/WeBankFinTech/fes.js/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "fes build",
|
||||
"prod": "FES_ENV=prod fes build",
|
||||
"analyze": "ANALYZE=1 fes build",
|
||||
"dev": "fes dev",
|
||||
"test": "fes test"
|
||||
"dev": "fes dev"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fesjs/builder-webpack": "file:../../../builder-webpack",
|
||||
"@fesjs/fes": "file:../../../fes",
|
||||
"@fesjs/builder-webpack": "workspace:*",
|
||||
"@fesjs/fes": "workspace:*",
|
||||
"@fesjs/fes-design": "^0.8.0",
|
||||
"@fesjs/plugin-qiankun": "file:../../../plugin-qiankun",
|
||||
"@fesjs/plugin-watermark": "file:../../../plugin-watermark",
|
||||
"@fesjs/plugin-qiankun": "workspace:*",
|
||||
"@fesjs/plugin-watermark": "workspace:*",
|
||||
"vue": "^3.5.21"
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
"@fesjs/utils": "workspace:*",
|
||||
"address": "^1.1.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"qiankun": "^2.7.0",
|
||||
"qiankun": "^2.10.16",
|
||||
"vite-plugin-qiankun": "^1.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import assert from 'assert';
|
||||
import { dirname, join } from 'path';
|
||||
import { existsSync, statSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
|
||||
import assert from 'node:assert';
|
||||
import { copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { startWatch } from './watch/watchMode';
|
||||
|
||||
export default function (api) {
|
||||
@ -43,7 +43,7 @@ export default function (api) {
|
||||
fn({ namespace, path, ignore }) {
|
||||
const base = join(api.paths.absTmpPath, namespace);
|
||||
// copy 行为只需要执行一次
|
||||
if (cacheCopyPath[base]) return;
|
||||
if (cacheCopyPath[base]) { return; }
|
||||
cacheCopyPath[base] = true;
|
||||
assert(api.stage >= api.ServiceStage.pluginReady, 'api.copyTmpFiles() should not execute in register stage.');
|
||||
assert(path, 'api.copyTmpFiles() should has param path');
|
||||
@ -59,11 +59,13 @@ export default function (api) {
|
||||
}
|
||||
if (statSync(source).isDirectory()) {
|
||||
api.utils.mkdirp.sync(target);
|
||||
} else if (Array.isArray(ignore)) {
|
||||
if (!ignore.some((pattern) => new RegExp(pattern).test(file))) {
|
||||
}
|
||||
else if (Array.isArray(ignore)) {
|
||||
if (!ignore.some(pattern => new RegExp(pattern).test(file))) {
|
||||
copyFileSync(source, target);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
copyFileSync(source, target);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { chokidar, winPath, lodash, getAppPath } from '@fesjs/utils';
|
||||
import process from 'node:process';
|
||||
import { chokidar, getAppPath, lodash, winPath } from '@fesjs/utils';
|
||||
import { watchPkg } from './watchPkg';
|
||||
|
||||
async function generateWhenFilesChange({ api }) {
|
||||
@ -36,7 +37,7 @@ async function generateWhenFilesChange({ api }) {
|
||||
type: api.ApplyPluginsType.add,
|
||||
initialValue: [paths.absPagesPath, getAppPath(paths.absSrcPath)],
|
||||
});
|
||||
lodash.uniq(watcherPaths.map((p) => winPath(p))).forEach((p) => {
|
||||
lodash.uniq(watcherPaths.map(p => winPath(p))).forEach((p) => {
|
||||
createWatcher(p);
|
||||
});
|
||||
|
||||
@ -44,7 +45,9 @@ async function generateWhenFilesChange({ api }) {
|
||||
}
|
||||
|
||||
export async function startWatch(api) {
|
||||
if (process.env.WATCH === 'none') return;
|
||||
if (process.env.WATCH === 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
let unwatchs = [];
|
||||
const restartServer = () => {
|
||||
@ -76,7 +79,7 @@ export async function startWatch(api) {
|
||||
onChange: async ({ pluginChanged, valueChanged }) => {
|
||||
if (pluginChanged.length) {
|
||||
console.log();
|
||||
api.logger.info(`Plugins of ${pluginChanged.map((p) => p.key).join(', ')} changed.`);
|
||||
api.logger.info(`Plugins of ${pluginChanged.map(p => p.key).join(', ')} changed.`);
|
||||
restartServer();
|
||||
}
|
||||
if (valueChanged.length) {
|
||||
@ -102,7 +105,8 @@ export async function startWatch(api) {
|
||||
console.log();
|
||||
api.logger.info(`Config ${reloadConfigs.join(', ')} changed.`);
|
||||
restartServer();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
api.service.userConfig = api.service.configInstance.getUserConfig();
|
||||
|
||||
await api.setConfig();
|
||||
@ -112,8 +116,9 @@ export async function startWatch(api) {
|
||||
key: 'onGenerateFiles',
|
||||
type: api.ApplyPluginsType.event,
|
||||
});
|
||||
} else {
|
||||
fns.forEach((fn) => fn());
|
||||
}
|
||||
else {
|
||||
fns.forEach(fn => fn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,13 @@
|
||||
/**
|
||||
* @copy 该文件代码大部分出自 umi,有需要请参考:
|
||||
* https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/commands/dev/watchPkg.ts
|
||||
*/
|
||||
|
||||
import { join } from 'path';
|
||||
import { chokidar, winPath, lodash } from '@fesjs/utils';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { isPluginOrPreset, PluginType } from '@fesjs/compiler';
|
||||
import { chokidar, lodash, winPath } from '@fesjs/utils';
|
||||
|
||||
function getPlugins(opts) {
|
||||
return Object.keys({
|
||||
...opts.pkg.dependencies,
|
||||
...opts.pkg.devDependencies,
|
||||
}).filter((name) => isPluginOrPreset(PluginType.plugin, name) || isPluginOrPreset(PluginType.preset, name));
|
||||
}).filter(name => isPluginOrPreset(PluginType.plugin, name) || isPluginOrPreset(PluginType.preset, name));
|
||||
}
|
||||
|
||||
function getPluginsFromPkgPath(opts) {
|
||||
@ -20,7 +15,8 @@ function getPluginsFromPkgPath(opts) {
|
||||
if (existsSync(opts.pkgPath)) {
|
||||
try {
|
||||
pkg = JSON.parse(readFileSync(opts.pkgPath, 'utf-8'));
|
||||
} catch (e) {
|
||||
}
|
||||
catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,77 +0,0 @@
|
||||
import { copyFileSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import glob from 'glob';
|
||||
import mkdirp from 'mkdirp';
|
||||
import Mustache from 'mustache';
|
||||
|
||||
interface GeneratorOptions {
|
||||
cwd: string;
|
||||
args: Record<string, any>;
|
||||
}
|
||||
|
||||
interface CopyTplOptions {
|
||||
templatePath: string;
|
||||
target: string;
|
||||
context: Record<string, any>;
|
||||
}
|
||||
|
||||
interface CopyDirectoryOptions {
|
||||
path: string;
|
||||
target: string;
|
||||
context: Record<string, any>;
|
||||
}
|
||||
|
||||
class Generator {
|
||||
cwd: string;
|
||||
args: Record<string, any>;
|
||||
|
||||
constructor({ cwd, args }: GeneratorOptions) {
|
||||
this.cwd = cwd;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
await this.writing();
|
||||
}
|
||||
|
||||
async writing(): Promise<void> { }
|
||||
|
||||
copyTpl(opts: CopyTplOptions): void {
|
||||
const tpl = readFileSync(opts.templatePath, 'utf-8');
|
||||
const content = Mustache.render(tpl, opts.context);
|
||||
mkdirp.sync(dirname(opts.target));
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`${chalk.green('Write:')} ${relative(this.cwd, opts.target)}`);
|
||||
writeFileSync(opts.target, content, 'utf-8');
|
||||
}
|
||||
|
||||
copyDirectory(opts: CopyDirectoryOptions): void {
|
||||
const files = glob.sync('**/*', {
|
||||
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 this.copyTpl({
|
||||
templatePath: absFile,
|
||||
target: join(opts.target, file.replace(/\.tpl$/, '')),
|
||||
context: opts.context,
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`${chalk.green('Copy: ')} ${file}`);
|
||||
const absTarget = join(opts.target, file);
|
||||
mkdirp.sync(dirname(absTarget));
|
||||
copyFileSync(absFile, absTarget);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Generator;
|
||||
@ -1,3 +1,6 @@
|
||||
export default function compatESModuleRequire(m: any): any {
|
||||
if (m.__esModule && m.default?.default) {
|
||||
return m.default.default;
|
||||
}
|
||||
return m.default ?? m;
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ import yParser from 'yargs-parser';
|
||||
import changePort from './changePort';
|
||||
import compatESModuleRequire from './compatESModuleRequire';
|
||||
import delay from './delay';
|
||||
import Generator from './Generator';
|
||||
import getAppPath from './getAppPath';
|
||||
import getHostName from './getHostName';
|
||||
import getPort from './getPort';
|
||||
@ -53,7 +52,6 @@ export {
|
||||
changePort,
|
||||
compatESModuleRequire,
|
||||
delay,
|
||||
Generator,
|
||||
getAppPath,
|
||||
getHostName,
|
||||
getPort,
|
||||
|
||||
1405
pnpm-lock.yaml
generated
1405
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,3 @@
|
||||
packages:
|
||||
- 'packages/**'
|
||||
- '!packages/create-fes-app/templates/**'
|
||||
- '!packages/plugin-qiankun/examples/**'
|
||||
- '!packages/fes-template/**'
|
||||
- '!packages/fes-template-vite/**'
|
||||
- '!packages/fes-template-h5/**'
|
||||
- 'packages/**'
|
||||
- '!packages/create-fes-app/templates/**'
|
||||
|
||||
@ -1,198 +0,0 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import chalk from 'chalk';
|
||||
import chokidar from 'chokidar';
|
||||
import merge from 'deepmerge';
|
||||
import fse from 'fs-extra';
|
||||
import yargsParser from 'yargs-parser';
|
||||
import buildConfig from '../build.config.js';
|
||||
import compiler from './compiler.mjs';
|
||||
import randomColor from './randomColor.mjs';
|
||||
|
||||
const argv = yargsParser(process.argv.slice(2));
|
||||
|
||||
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',
|
||||
pkgs: [],
|
||||
copy: [],
|
||||
};
|
||||
|
||||
const PACKAGE_PATH = path.join(process.cwd(), './packages');
|
||||
|
||||
function genLog(pkgName) {
|
||||
return (msg) => {
|
||||
console.log(`${randomColor(pkgName)}: ${msg}`);
|
||||
};
|
||||
}
|
||||
|
||||
function getPkgPath(pkgName) {
|
||||
return path.join(PACKAGE_PATH, pkgName);
|
||||
}
|
||||
|
||||
function genShortPath(filePath) {
|
||||
// 处理 windows 的路径
|
||||
filePath = filePath.replace(/\\/g, '/');
|
||||
const codePath = filePath.split(`/${SOURCE_DIR}/`)[1];
|
||||
return `${SOURCE_DIR}/${codePath}`;
|
||||
}
|
||||
|
||||
function getPkgSourcePath(pkgName) {
|
||||
return path.join(getPkgPath(pkgName), SOURCE_DIR);
|
||||
}
|
||||
|
||||
function getOutputPath(config, pkgName) {
|
||||
if (config.target === 'browser') {
|
||||
return path.join(getPkgPath(pkgName), ESM_OUTPUT_DIR);
|
||||
}
|
||||
|
||||
return path.join(getPkgPath(pkgName), NODE_CJS_OUTPUT_DIR);
|
||||
}
|
||||
|
||||
function getGlobalConfig() {
|
||||
if (fs.existsSync(GLOBAL_CONFIG_PATH)) {
|
||||
return merge(DEFAULT_CONFIG, buildConfig);
|
||||
}
|
||||
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
async function getPkgConfig(config, pkgName) {
|
||||
const pkgConfigPath = path.join(getPkgPath(pkgName), CONFIG_FILE_NAME);
|
||||
if (argv.watch) {
|
||||
config.sourceMap = true;
|
||||
}
|
||||
|
||||
if (fs.existsSync(pkgConfigPath)) {
|
||||
const content = await import(process.platform === 'win32' ? `file://${pkgConfigPath}` : pkgConfigPath);
|
||||
const result = merge(config, content.default);
|
||||
result.resolveCopy = result.copy.map(item => path.join(getPkgPath(pkgName), 'src', item));
|
||||
return result;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
function getNeedCompilerPkg(config) {
|
||||
// 用户通过 cli 指定的包,优先级最高
|
||||
if (argv.pkg) {
|
||||
return Array.isArray(argv.pkg) ? argv.pkg : argv.pkg;
|
||||
}
|
||||
|
||||
// 默认编译所有 packages
|
||||
if (!config.pkgs?.length) {
|
||||
const pkgs = fs.readdirSync(PACKAGE_PATH);
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
return config.pkgs;
|
||||
}
|
||||
|
||||
function cleanBeforeCompilerResult(pkgName, log) {
|
||||
const esmOutputDir = path.join(getPkgPath(pkgName), ESM_OUTPUT_DIR);
|
||||
const cjsOutputDir = path.join(getPkgPath(pkgName), 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);
|
||||
if (config.sourceMap) {
|
||||
config.sourceFileName = filePath;
|
||||
}
|
||||
|
||||
const transformedCode = compiler(code, config);
|
||||
|
||||
const type = config.target === 'browser' ? ESM_OUTPUT_DIR : NODE_CJS_OUTPUT_DIR;
|
||||
log(`Transform to ${type} for ${config.target === 'browser' ? chalk.yellow(shortFilePath) : chalk.blue(shortFilePath)}`);
|
||||
fse.outputFileSync(outputPath, transformedCode);
|
||||
}
|
||||
catch (error) {
|
||||
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 compilerPkgs(pkgs, globalConfig) {
|
||||
pkgs.forEach(async (pkgName) => {
|
||||
const sourceCodeDir = getPkgSourcePath(pkgName);
|
||||
if (fs.existsSync(sourceCodeDir)) {
|
||||
const log = genLog(pkgName);
|
||||
const config = await getPkgConfig(globalConfig, pkgName);
|
||||
const outputDir = getOutputPath(config, pkgName);
|
||||
|
||||
cleanBeforeCompilerResult(pkgName, 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
const globalConfig = getGlobalConfig();
|
||||
const pkgs = getNeedCompilerPkg(globalConfig);
|
||||
|
||||
compilerPkgs(pkgs, globalConfig);
|
||||
}
|
||||
|
||||
main();
|
||||
@ -1,51 +0,0 @@
|
||||
import { transformSync } from '@babel/core';
|
||||
|
||||
function transform(code, options) {
|
||||
const result = transformSync(code, options);
|
||||
return result.code;
|
||||
}
|
||||
|
||||
function transformNodeCode(code, config) {
|
||||
return transform(code, {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: false,
|
||||
targets: { node: '20' },
|
||||
},
|
||||
],
|
||||
],
|
||||
sourceFileName: config.sourceMap ? config.sourceFileName : undefined,
|
||||
sourceMaps: config.sourceMap ? 'inline' : false,
|
||||
});
|
||||
}
|
||||
|
||||
function transformBrowserCode(code) {
|
||||
// 因为 fes.js 在生产打包的时候,会处理所有的 node_modules 下的文件,确保不会丢失必要 polyfill
|
||||
// 因此这里不对 polyfill 进行处理,避免全局污染
|
||||
return transform(code, {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: false,
|
||||
useBuiltIns: false,
|
||||
targets: { chrome: '51' },
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export default function compiler(code, config) {
|
||||
if (!config.target || config.target === 'node') {
|
||||
return transformNodeCode(code, config);
|
||||
}
|
||||
|
||||
if (config.target === 'browser') {
|
||||
return transformBrowserCode(code);
|
||||
}
|
||||
|
||||
throw new Error(`config target error: ${config.target}, only can use 'node' and 'browser'`);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
# fes.js 源码编译
|
||||
|
||||
优雅的编译和日志输出,约定源码放在 `src` 目录。支持 node 端 cjs,browser:esm 编译。
|
||||
|
||||
不支持 browser 端的 cjs 编译,有两个理由:
|
||||
|
||||
1. 我们的内部包,目前来看只会在我们内部使用,没必再编译一份 cjs。
|
||||
2. 即使后来有其他包使用,也不大可能不支持 esm,即便不支持,到时候再加也没问题。
|
||||
|
||||
## 使用方式
|
||||
|
||||
- 在项目根目录下添加 `build.config.js` 指定需要编译的 `packages` 包
|
||||
- 可以通过 `--watch` cli 参数开启 `watch` 模式
|
||||
- 如果需要只编译某个包,通过 `--pkg pkgName` 参数指定
|
||||
|
||||
## 配置
|
||||
|
||||
```
|
||||
const config = {
|
||||
target: "node", // 编译目标 "node" | "browser", "node" 输出目录 lib, "browser" 输出目录 lib。默认编译目标 "node“
|
||||
pkgs: [], // 需要编译的 packages 包,默认编译根目录下所有的 packages 包,pkgs 参数只在根目录下的配置有效
|
||||
copy: [] // 直接拷贝,不进行编译
|
||||
}
|
||||
```
|
||||
@ -1,35 +0,0 @@
|
||||
/* eslint import/no-extraneous-dependencies: 0 */
|
||||
import chalk from 'chalk';
|
||||
|
||||
const colors = [
|
||||
'red',
|
||||
'green',
|
||||
'yellow',
|
||||
'blue',
|
||||
'magenta',
|
||||
'cyan',
|
||||
'gray',
|
||||
'redBright',
|
||||
'greenBright',
|
||||
'yellowBright',
|
||||
'blueBright',
|
||||
'magentaBright',
|
||||
'cyanBright',
|
||||
];
|
||||
|
||||
let index = 0;
|
||||
const cache = {};
|
||||
|
||||
export default 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,42 +1,43 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import * as url from 'node:url';
|
||||
import path from 'node:path';
|
||||
import minimist from 'minimist';
|
||||
import chalk from 'chalk';
|
||||
import semver from 'semver';
|
||||
import enquirer from 'enquirer';
|
||||
import consola from 'consola';
|
||||
import { execa } from 'execa';
|
||||
import minimist from 'minimist';
|
||||
import pc from 'picocolors';
|
||||
import semver from 'semver';
|
||||
import { getPublicPkgs } from './shared.mjs';
|
||||
|
||||
import buildConfig from '../build.config.js';
|
||||
|
||||
const { prompt } = enquirer;
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
const { preid: preId, dry: isDryRun } = minimist(process.argv.slice(2));
|
||||
const packages = buildConfig.pkgs;
|
||||
|
||||
const packages = getPublicPkgs();
|
||||
|
||||
const versionIncrements = ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease'];
|
||||
|
||||
function incVersion(version, i) {
|
||||
let _preId = preId || semver.prerelease(version)?.[0];
|
||||
if (!_preId && /pre/.test(i))
|
||||
if (!_preId && /pre/.test(i)) {
|
||||
_preId = 'beta';
|
||||
}
|
||||
|
||||
return semver.inc(version, i, _preId);
|
||||
}
|
||||
function autoIncVersion(version) {
|
||||
if (version.includes('-'))
|
||||
if (version.includes('-')) {
|
||||
return semver.inc(version, 'prerelease');
|
||||
}
|
||||
|
||||
return semver.inc(version, 'patch');
|
||||
}
|
||||
|
||||
const run = (bin, args, opts = {}) => execa(bin, args, { stdio: 'inherit', ...opts });
|
||||
const dryRun = (bin, args, opts = {}) => console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts);
|
||||
const dryRun = (bin, args, opts = {}) => consola.info(`[dryrun] ${bin} ${args.join(' ')}`, opts);
|
||||
const runIfNotDry = isDryRun ? dryRun : run;
|
||||
const getPkgRoot = pkg => path.resolve(__dirname, `../packages/${pkg}`);
|
||||
const step = msg => console.log(chalk.cyan(msg));
|
||||
const step = msg => consola.log(msg);
|
||||
function arrToObj(arr, key) {
|
||||
return arr.reduce((acc, cur) => {
|
||||
acc[cur[key]] = cur;
|
||||
@ -48,12 +49,11 @@ async function publishPackage(pkg, runIfNotDry) {
|
||||
step(`Publishing ${pkg.name}...`);
|
||||
try {
|
||||
let _releaseTag;
|
||||
if (pkg.newVersion.includes('-'))
|
||||
if (pkg.newVersion.includes('-')) {
|
||||
_releaseTag = 'next';
|
||||
}
|
||||
|
||||
await runIfNotDry(
|
||||
// note: use of pnpm is intentional here as we rely on its publishing
|
||||
// behavior.
|
||||
'npm',
|
||||
['publish', ...(_releaseTag ? ['--tag', _releaseTag] : []), '--access', 'public', '--registry', 'https://registry.npmjs.org'],
|
||||
{
|
||||
@ -61,14 +61,14 @@ async function publishPackage(pkg, runIfNotDry) {
|
||||
stdio: 'pipe',
|
||||
},
|
||||
);
|
||||
console.log('Successfully published :', chalk.green(`${pkg.name}@${pkg.newVersion}`));
|
||||
console.log('Successfully published :', pc.green(`${pkg.name}@${pkg.newVersion}`));
|
||||
}
|
||||
catch (e) {
|
||||
if (e.stderr.match(/previously published/))
|
||||
console.log(chalk.red(`Skipping already published: ${pkg.name}`));
|
||||
if (e.stderr.match(/previously published/)) {
|
||||
console.log(pc.red(`Skipping already published: ${pkg.name}`));
|
||||
}
|
||||
|
||||
else
|
||||
throw e;
|
||||
else { throw e; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,13 +95,15 @@ function updatePackage(pkgName, version, pkgs) {
|
||||
pkgJson.version = version;
|
||||
pkgJson.dependencies
|
||||
&& Object.keys(pkgJson.dependencies).forEach((npmName) => {
|
||||
if (pkgs[npmName])
|
||||
if (pkgs[npmName]) {
|
||||
pkgJson.dependencies[npmName] = `^${pkgs[npmName].newVersion}`;
|
||||
}
|
||||
});
|
||||
pkgJson.peerDependencies
|
||||
&& Object.keys(pkgJson.peerDependencies).forEach((npmName) => {
|
||||
if (pkgs[npmName])
|
||||
if (pkgs[npmName]) {
|
||||
pkgJson.peerDependencies[npmName] = `^${pkgs[npmName].newVersion}`;
|
||||
}
|
||||
});
|
||||
writePackageJson(pkgName, pkgJson);
|
||||
}
|
||||
@ -138,26 +140,25 @@ async function filterChangedPackages() {
|
||||
|
||||
async function createPackageNewVersion(name, version) {
|
||||
// no explicit version, offer suggestions
|
||||
const { release } = await prompt({
|
||||
const release = await consola.prompt(`Select release type: ${name}`, {
|
||||
type: 'select',
|
||||
name: 'release',
|
||||
message: `Select release type: ${name}`,
|
||||
choices: versionIncrements.map(i => `${i} (${incVersion(version, i)})`).concat(['custom']),
|
||||
options: versionIncrements.map((i) => {
|
||||
return {
|
||||
value: incVersion(version, i),
|
||||
label: `${i} (${incVersion(version, i)})`,
|
||||
};
|
||||
}).concat({
|
||||
value: 'custom',
|
||||
label: 'custom',
|
||||
}),
|
||||
});
|
||||
|
||||
let newVersion;
|
||||
let newVersion = release;
|
||||
if (release === 'custom') {
|
||||
newVersion = (
|
||||
await prompt({
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: `Input custom version: ${name}`,
|
||||
initial: version,
|
||||
})
|
||||
).version;
|
||||
}
|
||||
else {
|
||||
newVersion = release.match(/\((.*)\)/)[1];
|
||||
newVersion = await consola.prompt(`Input custom version: ${name}`, {
|
||||
type: 'text',
|
||||
default: version,
|
||||
});
|
||||
}
|
||||
|
||||
if (!semver.valid(newVersion)) {
|
||||
@ -212,7 +213,7 @@ async function main() {
|
||||
const changedPackages = await filterChangedPackages();
|
||||
|
||||
if (!changedPackages.length) {
|
||||
console.log(chalk.yellow(`No changes to commit.`));
|
||||
consola.warn(`No changes to commit.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -230,16 +231,15 @@ async function main() {
|
||||
const passiveUpdatePkgs = genOtherPkgsVersion(updatedPkgs);
|
||||
const packagesVersion = passiveUpdatePkgs.concat(updatedPkgs);
|
||||
|
||||
const { yes } = await prompt({
|
||||
const yes = await consola.prompt(`These packages will be released: \n${packagesVersion
|
||||
.map(pkg => `${pc.magenta(pkg.name)}: v${pkg.version} > ${pc.green(`v${pkg.newVersion}`)}`)
|
||||
.join('\n')}\nConfirm?`, {
|
||||
type: 'confirm',
|
||||
name: 'yes',
|
||||
message: `These packages will be released: \n${packagesVersion
|
||||
.map(pkg => `${chalk.magenta(pkg.name)}: v${pkg.version} > ${chalk.green(`v${pkg.newVersion}`)}`)
|
||||
.join('\n')}\nConfirm?`,
|
||||
});
|
||||
|
||||
if (!yes)
|
||||
if (!yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newRootVersion = await genRootPackageVersion();
|
||||
|
||||
@ -252,11 +252,11 @@ async function main() {
|
||||
await run('pnpm', ['i']);
|
||||
// // build all packages with types
|
||||
step('\nBuilding all packages...');
|
||||
if (!isDryRun)
|
||||
if (!isDryRun) {
|
||||
await run('pnpm', ['build']);
|
||||
}
|
||||
|
||||
else
|
||||
console.log(`(skipped build)`);
|
||||
else { console.log(`(skipped build)`); }
|
||||
|
||||
// generate changelog
|
||||
step('\nGenerating changelog...');
|
||||
@ -274,8 +274,9 @@ async function main() {
|
||||
|
||||
// publish packages
|
||||
step('\nPublishing packages...');
|
||||
for (const pkg of packagesVersion)
|
||||
for (const pkg of packagesVersion) {
|
||||
await publishPackage(pkg, runIfNotDry);
|
||||
}
|
||||
|
||||
// push to GitHub
|
||||
step('\nPushing to GitHub...');
|
||||
@ -283,8 +284,9 @@ async function main() {
|
||||
await runIfNotDry('git', ['push', 'origin', `refs/tags/v${newRootVersion}`]);
|
||||
await runIfNotDry('git', ['push']);
|
||||
|
||||
if (isDryRun)
|
||||
if (isDryRun) {
|
||||
console.log(`\nDry run finished - run git diff to see package changes.`);
|
||||
}
|
||||
|
||||
console.log();
|
||||
}
|
||||
|
||||
@ -1,5 +1,31 @@
|
||||
import { copyFileSync, mkdirSync, readdirSync } from 'node:fs';
|
||||
import { copyFileSync, mkdirSync, readdirSync, readFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import process from 'node:process';
|
||||
|
||||
export function getPublicPkgs() {
|
||||
const packagesDir = join(process.cwd(), 'packages');
|
||||
const dirs = readdirSync(packagesDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
const publicPkgs = [];
|
||||
for (const dir of dirs) {
|
||||
const pkgJsonPath = join(packagesDir, dir, 'package.json');
|
||||
try {
|
||||
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
|
||||
// 如果没有 private 字段或者 private 为 false,则是需要发布的包
|
||||
if (!pkgJson.private) {
|
||||
publicPkgs.push(dir);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// 如果读取或解析 package.json 失败,跳过该目录
|
||||
console.warn(`Warning: Could not read package.json for ${dir}`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
return publicPkgs;
|
||||
}
|
||||
|
||||
export function copyTplFiles(srcDir, dstDir) {
|
||||
function walk(currentSrc, currentDst) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user