fix: eslint babel parse

This commit is contained in:
bac-joker 2021-02-08 19:21:30 +08:00
commit cbbda6df42
98 changed files with 3963 additions and 714 deletions

View File

@ -17,5 +17,10 @@ module.exports = {
'no-restricted-syntax': 'off', 'no-restricted-syntax': 'off',
'no-undefined': 'off', 'no-undefined': 'off',
'vue/valid-template-root': 'off' 'vue/valid-template-root': 'off'
},
parserOptions: {
babelOptions: {
presets: ['@babel/preset-env']
}
} }
}; };

View File

@ -16,7 +16,9 @@ const headPkgs = [
"fes-plugin-icon", "fes-plugin-icon",
"fes-plugin-locale", "fes-plugin-locale",
"fes-plugin-enums", "fes-plugin-enums",
"create-fes-app" "fes-plugin-jest",
"fes-plugin-vuex",
"create-fes-app",
]; ];
const tailPkgs = []; const tailPkgs = [];
// const otherPkgs = readdirSync(join(__dirname, 'packages')).filter( // const otherPkgs = readdirSync(join(__dirname, 'packages')).filter(

View File

@ -1,5 +1,5 @@
{ {
"version": "2.0.0-alpha.5", "version": "2.0.0-alpha.8",
"changelog": { "changelog": {
"repo": "WeBankFinTech/fes.js", "repo": "WeBankFinTech/fes.js",
"cacheDir": ".changelog", "cacheDir": ".changelog",

View File

@ -34,7 +34,7 @@
"@vuepress/plugin-pwa": "^2.0.0-alpha.18", "@vuepress/plugin-pwa": "^2.0.0-alpha.18",
"@vuepress/plugin-pwa-popup": "^2.0.0-alpha.18", "@vuepress/plugin-pwa-popup": "^2.0.0-alpha.18",
"@vuepress/theme-vue": "^2.0.0-alpha.18", "@vuepress/theme-vue": "^2.0.0-alpha.18",
"@webank/eslint-config-webank": "^0.2.10", "@webank/eslint-config-webank": "0.2.10",
"commitizen": "^4.2.1", "commitizen": "^4.2.1",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"esbuild-loader": "^2.7.0", "esbuild-loader": "^2.7.0",

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/create-fes-app", "name": "@webank/create-fes-app",
"version": "2.0.0-alpha.3", "version": "2.0.0-alpha.8",
"description": "create a app base on fes.js", "description": "create a app base on fes.js",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-template-h5", "name": "@webank/fes-template-h5",
"version": "2.0.0-alpha.1", "version": "2.0.0-alpha",
"description": "fes 移动端项目模版", "description": "fes 移动端项目模版",
"scripts": { "scripts": {
"build": "fes build", "build": "fes build",
@ -13,26 +13,41 @@
"easy", "easy",
"strong" "strong"
], ],
"files": [
".eslintrc.js",
".gitignore",
".fes.js",
".fes.prod.js",
"mock.js",
"package.json",
"README.md",
"tsconfig.json",
"/src",
"/config"
],
"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/fes-template-h5" "directory": "packages/fes-template-h5"
}, },
"author": "harrywan qlin", "author": "qlin",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues" "url": "https://github.com/WeBankFinTech/fes.js/issues"
}, },
"homepage": "https://github.com/WeBankFinTech/fes.js#readme", "homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"publishConfig": {
"access": "public"
},
"devDependencies": { "devDependencies": {
"@vue/compiler-sfc": "^3.0.0", "@webank/eslint-config-webank": "0.2.10",
"@webank/eslint-config-webank": "0.2.7",
"postcss-px-to-viewport": "1.1.1" "postcss-px-to-viewport": "1.1.1"
}, },
"dependencies": { "dependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.6",
"@webank/fes-plugin-icon": "^2.0.0-alpha.0", "@webank/fes-plugin-icon": "^2.0.0-alpha.6",
"@webank/fes-plugin-request": "^2.0.0-alpha.0", "@webank/fes-plugin-request": "^2.0.0-alpha.6",
"vue": "^3.0.4" "vue": "3.0.5"
} },
"private": true
} }

View File

@ -9,6 +9,7 @@
<meta name="format-detection" content="email=no"/> <meta name="format-detection" content="email=no"/>
<meta name="viewport" content="viewport-fit=cover,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"> <meta name="viewport" content="viewport-fit=cover,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
<link rel="shortcut icon" type="image/x-icon" href="./logo.png">
</head> </head>
<body ontouchstart=""> <body ontouchstart="">
<div id="app"></div> <div id="app"></div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -1,19 +1,14 @@
module.exports = { module.exports = {
extends: [ extends: ['@webank/eslint-config-webank/vue.js'],
'@webank/eslint-config-webank/vue.js' overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
]
}
], ],
globals: { env: {
// 这里填入你的项目需要的全局变量 jest: true
// 这里值为 false 表示这个全局变量不允许被重新赋值,比如:
//
// Vue: false
__DEV__: false
},
rules: {
'vue/comment-directive': 'off',
'global-require': 'off',
'import/no-unresolved': 'off',
'no-restricted-syntax': 'off'
} }
}; };

View File

@ -14,7 +14,7 @@ export default {
}, },
layout: { layout: {
title: "Fes.js", title: "Fes.js",
logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg', footer: 'Created by MumbelFe',
multiTabs: false, multiTabs: false,
menus: [{ menus: [{
name: 'index' name: 'index'
@ -27,5 +27,8 @@ export default {
}, },
devServer: { devServer: {
port: 8080 port: 8080
},
enums: {
status: [['0', '无效的'], ['1', '有效的']]
} }
}; };

View File

@ -2,7 +2,7 @@
# dependencies # dependencies
/node_modules /node_modules
/coverage
# fes # fes
/src/.fes /src/.fes

View File

@ -0,0 +1,5 @@
import sum from '@/utils/sum';
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});

View File

@ -1,12 +1,13 @@
{ {
"name": "@webank/fes-template", "name": "@webank/fes-template",
"version": "2.0.0-alpha.1", "version": "2.0.0-alpha",
"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"
}, },
"keywords": [ "keywords": [
"管理端", "管理端",
@ -15,6 +16,18 @@
"easy", "easy",
"strong" "strong"
], ],
"files": [
".eslintrc.js",
".gitignore",
".fes.js",
".fes.prod.js",
"mock.js",
"package.json",
"README.md",
"tsconfig.json",
"/src",
"/config"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git", "url": "git+https://github.com/WeBankFinTech/fes.js.git",
@ -26,18 +39,25 @@
"url": "https://github.com/WeBankFinTech/fes.js/issues" "url": "https://github.com/WeBankFinTech/fes.js/issues"
}, },
"homepage": "https://github.com/WeBankFinTech/fes.js#readme", "homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"publishConfig": {
"access": "public"
},
"devDependencies": { "devDependencies": {
"@vue/compiler-sfc": "^3.0.0", "@webank/eslint-config-webank": "0.2.10"
"@webank/eslint-config-webank": "0.2.7"
}, },
"dependencies": { "dependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.6",
"@webank/fes-plugin-access": "^2.0.0-alpha.0", "@webank/fes-plugin-access": "^2.0.0-alpha.6",
"@webank/fes-plugin-layout": "^2.0.0-alpha.0", "@webank/fes-plugin-layout": "^2.0.0-alpha.6",
"@webank/fes-plugin-locale": "^2.0.0-alpha.0", "@webank/fes-plugin-locale": "^2.0.0-alpha.6",
"@webank/fes-plugin-model": "^2.0.0-alpha.0", "@webank/fes-plugin-model": "^2.0.0-alpha.6",
"@webank/fes-plugin-enums": "^2.0.0-alpha.6",
"@webank/fes-plugin-jest": "^2.0.0-alpha.6",
"@webank/fes-plugin-vuex": "^2.0.0-alpha.6",
"ant-design-vue": "2.0.0-rc.3", "ant-design-vue": "2.0.0-rc.3",
"vue": "3.0.4" "vue": "3.0.5",
} "vuex": "^4.0.0-rc.2"
},
"private": true
} }

View File

@ -3,7 +3,8 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>fes Vue3</title> <title>fes.js</title>
<link rel="shortcut icon" type="image/x-icon" href="./logo.png">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -5,6 +5,11 @@
<access :id="accessId"> accessOnepicess1 <input /> </access> <access :id="accessId"> accessOnepicess1 <input /> </access>
<div v-access="accessId"> accessOnepicess2 <input /> </div> <div v-access="accessId"> accessOnepicess2 <input /> </div>
<input /> <input />
<h4>数据字典</h4>
<div v-for="item in enumsGet('status')" :key="item.key">{{item.value}}{{item.key}}</div>
<div v-for="item in roles" :key="item.key">{{item.name}}{{item.disabled}}</div>
<div>{{enumsGet('roles', '2', { dir: 'eName' })}}</div>
<h4>Vuex <button @click="increment">click me{{count}}</button></h4>
</div> </div>
</template> </template>
<config> <config>
@ -14,9 +19,10 @@
} }
</config> </config>
<script> <script>
import { ref, onMounted } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useStore } from 'vuex';
import { import {
access, useAccess, useRouter, useI18n, locale access, useAccess, useRouter, useI18n, locale, enums
} from '@webank/fes'; } from '@webank/fes';
export default { export default {
@ -26,6 +32,41 @@ export default {
const localI18n = useI18n(); const localI18n = useI18n();
const router = useRouter(); const router = useRouter();
const accessId = ref('/onepiece1'); const accessId = ref('/onepiece1');
enums.push('roles', [
{
id: '1',
cName: '系统管理员',
eName: 'System',
perm: ['1', '2', '3']
},
{
id: '2',
cName: '业务管理员',
eName: 'Business',
perm: ['1', '2']
},
{
id: '3',
cName: '普通用户',
eName: 'User',
perm: ['1']
}
], { keyName: 'id' });
const roles = enums.get('roles', {
extend: [
{
key: 'name',
dir: 'cName'
},
{
key: 'disabled',
transfer: item => item.value.perm.some(i => i >= 2)
}
]
});
console.log(roles);
const store = useStore();
console.log('store==>', store);
onMounted(() => { onMounted(() => {
console.log(router); console.log(router);
setTimeout(() => { setTimeout(() => {
@ -43,7 +84,11 @@ export default {
accessId, accessId,
fes, fes,
accessOnepicess, accessOnepicess,
t: localI18n.t t: localI18n.t,
enumsGet: enums.get,
roles,
count: computed(() => store.state.counter.count),
increment: () => store.commit('counter/increment')
}; };
} }
}; };

View File

@ -0,0 +1,10 @@
<template>
<div>test</div>
</template>
<script>
import { } from '@webank/fes';
export default {
};
</script>

View File

@ -0,0 +1,23 @@
export default {
namespaced: true,
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -0,0 +1,3 @@
import { createLogger } from 'vuex';
export default createLogger();

View File

@ -0,0 +1,25 @@
export default {
namespaced: true,
state: () => ({
name: 'aring',
age: 20,
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -0,0 +1,3 @@
export default function sum(a, b) {
return a + b;
}

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-compiler", "name": "@webank/fes-compiler",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-compiler", "description": "@webank/fes-compiler",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -27,6 +27,7 @@
"@babel/register": "^7.12.13", "@babel/register": "^7.12.13",
"@babel/preset-env": "^7.12.13", "@babel/preset-env": "^7.12.13",
"@umijs/utils": "3.3.3", "@umijs/utils": "3.3.3",
"commander": "^7.0.0",
"dotenv": "8.2.0", "dotenv": "8.2.0",
"joi": "17.3.0", "joi": "17.3.0",
"readline": "^1.3.0", "readline": "^1.3.0",

View File

@ -130,41 +130,9 @@ export default class Config {
getUserConfig() { getUserConfig() {
const configFile = this.getConfigFile(); const configFile = this.getConfigFile();
this.configFile = configFile; this.configFile = configFile;
// 潜在问题: if (configFile.length > 0) {
// .local 和 .env 的配置必须有 configFile 才有效
if (configFile) {
let envConfigFile;
if (process.env.FES_ENV) {
const envConfigFileName = this.addAffix(
configFile,
process.env.FES_ENV
);
const fileNameWithoutExt = envConfigFileName.replace(
extname(envConfigFileName),
''
);
envConfigFile = getFile({
base: this.cwd,
fileNameWithoutExt,
type: 'javascript'
}).filename;
if (!envConfigFile) {
throw new Error(
`get user config failed, ${envConfigFile} does not exist, but process.env.FES_ENV is set to ${process.env.FES_ENV}.`
);
}
}
const files = [
configFile,
envConfigFile,
this.localConfig && this.addAffix(configFile, 'local')
]
.filter(f => !!f)
.map(f => join(this.cwd, f))
.filter(f => existsSync(f));
// clear require cache and set babel register // clear require cache and set babel register
const requireDeps = files.reduce((memo, file) => { const requireDeps = configFile.reduce((memo, file) => {
memo = memo.concat(parseRequireDeps(file)); memo = memo.concat(parseRequireDeps(file));
return memo; return memo;
}, []); }, []);
@ -175,7 +143,7 @@ export default class Config {
}); });
// require config and merge // require config and merge
return this.mergeConfig(...this.requireConfigs(files)); return this.mergeConfig(...this.requireConfigs(configFile));
} }
return {}; return {};
} }
@ -201,8 +169,41 @@ export default class Config {
getConfigFile() { getConfigFile() {
// TODO: support custom config file // TODO: support custom config file
const configFile = CONFIG_FILES.find(f => existsSync(join(this.cwd, f))); let configFile = CONFIG_FILES.find(f => existsSync(join(this.cwd, f)));
return configFile ? winPath(configFile) : null; if (!configFile) return [];
configFile = winPath(configFile);
let envConfigFile;
// 潜在问题:
// .local 和 .env 的配置必须有 configFile 才有效
if (process.env.FES_ENV) {
const envConfigFileName = this.addAffix(
configFile,
process.env.FES_ENV
);
const fileNameWithoutExt = envConfigFileName.replace(
extname(envConfigFileName),
''
);
envConfigFile = getFile({
base: this.cwd,
fileNameWithoutExt,
type: 'javascript'
}).filename;
if (!envConfigFile) {
throw new Error(
`get user config failed, ${envConfigFile} does not exist, but process.env.FES_ENV is set to ${process.env.FES_ENV}.`
);
}
}
const files = [
configFile,
envConfigFile,
this.localConfig && this.addAffix(configFile, 'local')
]
.filter(f => !!f)
.map(f => join(this.cwd, f))
.filter(f => existsSync(f));
return files;
} }
getWatchFilesAndDirectories() { getWatchFilesAndDirectories() {

View File

@ -3,11 +3,12 @@ import { EventEmitter } from 'events';
import assert from 'assert'; import assert from 'assert';
import { AsyncSeriesWaterfallHook } from 'tapable'; import { AsyncSeriesWaterfallHook } from 'tapable';
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { lodash } from '@umijs/utils'; import { lodash, chalk } from '@umijs/utils';
import BabelRegister from './babelRegister'; import { Command, Option } from 'commander';
import { resolvePresets, pathToObj, resolvePlugins } from './utils/pluginUtils'; import { resolvePresets, pathToObj, resolvePlugins } from './utils/pluginUtils';
import loadDotEnv from './utils/loadDotEnv'; import loadDotEnv from './utils/loadDotEnv';
import isPromise from './utils/isPromise'; import isPromise from './utils/isPromise';
import BabelRegister from './babelRegister';
import PluginAPI from './pluginAPI'; import PluginAPI from './pluginAPI';
import { import {
ApplyPluginsType, ApplyPluginsType,
@ -88,6 +89,8 @@ export default class Service extends EventEmitter {
// repoDir should be the root dir of repo // repoDir should be the root dir of repo
this.pkg = opts.pkg || this.resolvePackage(); this.pkg = opts.pkg || this.resolvePackage();
this.env = opts.env || process.env.NODE_ENV; this.env = opts.env || process.env.NODE_ENV;
this.fesPkg = opts.fesPkg || {};
assert(existsSync(this.cwd), `cwd ${this.cwd} does not exist.`); assert(existsSync(this.cwd), `cwd ${this.cwd} does not exist.`);
@ -112,6 +115,10 @@ export default class Service extends EventEmitter {
env: this.env env: this.env
}); });
this.program = new Command();
this.initCommand();
// setup initial plugins // setup initial plugins
const baseOpts = { const baseOpts = {
pkg: this.pkg, pkg: this.pkg,
@ -262,6 +269,7 @@ export default class Service extends EventEmitter {
'paths', 'paths',
'cwd', 'cwd',
'pkg', 'pkg',
'configInstance',
'userConfig', 'userConfig',
'config', 'config',
'env', 'env',
@ -483,12 +491,28 @@ export default class Service extends EventEmitter {
} }
} }
async run({ name, args = {} }) { initCommand() {
args._ = args._ || []; this.program
// shift the command itself .usage('<command> [options]')
if (args._[0] === name) args._.shift(); .version(`@webank/fes ${this.fesPkg.version}`, '-v, --vers', 'output the current version')
.description(chalk.cyan('一个好用的前端应用解决方案'));
}
this.args = args; parseCommand() {
this.program.on('--help', () => {
console.log();
console.log(
` Run ${chalk.cyan(
'fes <command> --help'
)} for detailed usage of given command.`
);
console.log();
});
this.program.commands.forEach(c => c.on('--help', () => console.log()));
this.program.parse(process.argv);
}
async run({ rawArgv = {}, args = {} }) {
await this.init(); await this.init();
this.setStage(ServiceStage.run); this.setStage(ServiceStage.run);
@ -500,27 +524,37 @@ export default class Service extends EventEmitter {
} }
}); });
return this.runCommand({ this.runCommand({ rawArgv, args });
name,
args
});
} }
async runCommand({ name, args = {} }) { async runCommand({ rawArgv = {}, args = {} }) {
assert(this.stage >= ServiceStage.init, 'service is not initialized.'); assert(this.stage >= ServiceStage.init, 'service is not initialized.');
args._ = args._ || []; Object.keys(this.commands).forEach((command) => {
// shift the command itself const commandOptionConfig = this.commands[command];
if (args._[0] === name) args._.shift(); const program = this.program;
let c = program.command(command).description(commandOptionConfig.description);
const command = typeof this.commands[name] === 'string' if (Array.isArray(commandOptionConfig.options)) {
? this.commands[this.commands[name]] commandOptionConfig.options.forEach((config) => {
: this.commands[name]; const option = new Option(config.name, config.description);
assert(command, `run command failed, command ${name} does not exists.`); if (config.default) {
option.default(config.default);
const { fn } = command; }
return fn({ if (config.choices) {
args option.choices(config.choices);
}
c = c.addOption(option);
});
}
if (commandOptionConfig.fn) {
c.action(async () => {
await commandOptionConfig.fn({
rawArgv, args, options: c.opts(), program
});
});
}
}); });
this.parseCommand();
} }
} }

View File

@ -3,6 +3,7 @@ import * as utils from '@umijs/utils';
import { isValidPlugin, pathToObj } from './utils/pluginUtils'; import { isValidPlugin, pathToObj } from './utils/pluginUtils';
import { EnableBy, PluginType, ServiceStage } from './enums'; import { EnableBy, PluginType, ServiceStage } from './enums';
import Logger from '../logger'; import Logger from '../logger';
// TODO // TODO
// 标准化 logger // 标准化 logger
export default class PluginAPI { export default class PluginAPI {
@ -62,16 +63,21 @@ export default class PluginAPI {
).concat(hook); ).concat(hook);
} }
registerCommand(command) { registerCommand(commandOption) {
const { name, alias } = command; const { command, fn } = commandOption;
assert( assert(
!this.service.commands[name], !this.service.commands[command],
`api.registerCommand() failed, the command ${name} is exists.` `api.registerCommand() failed, the command ${command} is exists.`
); );
this.service.commands[name] = command; assert(
if (alias) { typeof command === 'string',
this.service.commands[alias] = name; 'api.registerCommand() failed, the command must be String.'
} );
assert(
typeof fn === 'function',
'api.registerCommand() failed, the fn must be function.'
);
this.service.commands[command] = commandOption;
} }
// 在 preset 初始化阶段放后面,在插件注册阶段放前面 // 在 preset 初始化阶段放后面,在插件注册阶段放前面

View File

@ -1,2 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-plugin-access", "name": "@webank/fes-plugin-access",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-access", "description": "@webank/fes-plugin-access",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -28,6 +28,6 @@
}, },
"peerDependencies": { "peerDependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
"vue": "^3.0.4" "vue": "3.0.5"
} }
} }

View File

@ -1,2 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-plugin-enums", "name": "@webank/fes-plugin-enums",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-enums", "description": "@webank/fes-plugin-enums",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -28,6 +28,6 @@
}, },
"peerDependencies": { "peerDependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
"vue": "^3.0.4" "vue": "3.0.5"
} }
} }

View File

@ -25,13 +25,15 @@ function get(name, key, opt = { dir: 'value', extend: []}) {
key = '' key = ''
} }
let list = ENUMS[name] || [] let list = ENUMS[name] || []
let value
if (key) { if (key) {
let res = list.filter(item => item.key === key)[0] let res = list.filter(item => item.key === key)[0]
if (!res) return key if (!res) return key
return readonly(parseValueDir(res.value, opt.dir) || key) value = parseValueDir(res.value, opt.dir) || key
} else { } else {
return readonly(format(list, opt.extend)) value = format(list, opt.extend)
} }
return typeof value === 'object' ? readonly(value) : value
} }
/** /**

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-plugin-icon", "name": "@webank/fes-plugin-icon",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-icon", "description": "@webank/fes-plugin-icon",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -27,7 +27,7 @@
"access": "public" "access": "public"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.0.4" "vue": "3.0.5"
}, },
"dependencies": { "dependencies": {
"svgo": "1.3.2" "svgo": "1.3.2"

View File

@ -0,0 +1,3 @@
export default {
disableTypeCheck: false,
};

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-present webank
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -0,0 +1 @@
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; // eslint-disable-line

View File

@ -0,0 +1,6 @@
require('core-js/stable');
require('regenerator-runtime/runtime');
if (typeof window !== 'undefined') {
require('whatwg-fetch');
}

View File

@ -0,0 +1,9 @@
module.exports = {
process() {
return 'module.exports = {};';
},
getCacheKey() {
// The output is always the same.
return 'css';
}
};

View File

@ -0,0 +1,7 @@
const babelJest = require('babel-jest');
module.exports = babelJest.createTransformer({
presets: [require.resolve('@umijs/babel-preset-umi/node')],
babelrc: false,
configFile: false
});

View File

@ -0,0 +1,50 @@
{
"name": "@webank/fes-plugin-jest",
"version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-jest",
"main": "lib/index.js",
"files": [
"lib",
"helpers"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
"directory": "packages/fes-plugin-jest"
},
"keywords": [
"fes",
"unit",
"jest"
],
"author": "harrywan",
"license": "MIT",
"bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues"
},
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@babel/core": "7.11.6",
"@umijs/babel-preset-umi": "3.2.24",
"@webank/fes-compiler": "^2.0.0-alpha.8",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^26.6.3",
"core-js": "3.6.5",
"jest": "^26.6.3",
"jest-cli": "^26.6.3",
"jest-serializer-vue": "^2.0.2",
"jest-transform-stub": "^2.0.0",
"jest-watch-typeahead": "^0.6.1",
"regenerator-runtime": "^0.13.7",
"ts-jest": "^26.5.0",
"typescript": "~4.1.2",
"vue-jest": "^5.0.0-0",
"whatwg-fetch": "^3.4.1"
}
}

View File

@ -0,0 +1,60 @@
import { existsSync } from 'fs';
import { join } from 'path';
export default (cwd, args) => {
const testMatchTypes = ['spec', 'test'];
if (args.e2e) {
testMatchTypes.push('e2e');
}
const hasSrc = existsSync(join(cwd, 'src'));
return {
collectCoverageFrom: [
'index.{js,jsx,ts,tsx,vue}',
hasSrc && 'src/**/*.{js,jsx,ts,tsx,vue}',
'!**/.fes/**',
'!**/typings/**',
'!**/types/**',
'!**/fixtures/**',
'!**/examples/**',
'!**/*.d.ts'
].filter(Boolean),
moduleFileExtensions: [
'js',
'jsx',
'json',
// tell Jest to handle *.vue files
'vue'
],
transform: {
// process *.vue files with vue-jest
'^.+\\.vue$': require.resolve('vue-jest'),
'.+\\.(css|styl|less|sass|scss|jpg|jpeg|png|svg|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
require.resolve('jest-transform-stub'),
'^.+\\.jsx?$': require.resolve(
'../helpers/transformers/javascript'
)
},
setupFiles: [require.resolve('../helpers/setupFiles/shim')],
setupFilesAfterEnv: [require.resolve('../helpers/setupFiles/jasmine')],
transformIgnorePatterns: ['/node_modules/'],
// support the same @ -> src alias mapping in source code
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
// serializer for snapshots
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
`**/tests/**/*.(${testMatchTypes.join('|')}).[jt]s?(x)`,
'**/__tests__/**/*.[jt]s?(x)'
],
// https://github.com/facebook/jest/issues/6766
testURL: 'http://localhost/',
watchPlugins: [
require.resolve('jest-watch-typeahead/filename'),
require.resolve('jest-watch-typeahead/testname')
],
verbose: true
};
};

View File

@ -0,0 +1,97 @@
import assert from 'assert';
import { join } from 'path';
import { existsSync } from 'fs';
import { Logger } from '@webank/fes-compiler';
import { options as CliOptions } from 'jest-cli/build/cli/args';
import createDefaultConfig from './createDefaultConfig';
const logger = new Logger('fes:plugin-unit-jest');
function getCommandOptiton() {
const opts = [];
Object.keys(CliOptions).forEach((key) => {
const option = CliOptions[key];
const opt = {};
if (key !== 'version') {
if (option.alias) {
opt.name = `-${option.alias} --${key}`;
} else {
opt.name = `--${key}`;
}
opt.description = option.description;
opts.push(opt);
}
});
return opts;
}
export default function (api) {
const { utils: { mergeConfig }, cwd } = api;
api.registerCommand({
command: 'test',
description: 'run unit tests with jest',
options: getCommandOptiton(),
async fn({ args }) {
process.env.NODE_ENV = 'test';
if (args._[0] === 'test') {
args._.shift();
}
args.debug && logger.log(`args: ${JSON.stringify(args)}`);
// Read config from cwd/jest.config.js
const userJestConfigFile = join(cwd, 'jest.config.js');
const userJestConfig = existsSync(userJestConfigFile) && require(userJestConfigFile);
args.debug && logger.log(`config from jest.config.js: ${JSON.stringify(userJestConfig)}`);
// Read jest config from package.json
const packageJSONPath = join(cwd, 'package.json');
const packageJestConfig = existsSync(packageJSONPath) && require(packageJSONPath).jest;
args.debug && logger.log(`jest config from package.json: ${JSON.stringify(packageJestConfig)}`);
// Merge configs
// user config and args config could have value function for modification
const config = mergeConfig(
createDefaultConfig(cwd, args),
packageJestConfig,
userJestConfig
);
args.debug && logger.log(`final config: ${JSON.stringify(config)}`);
// Generate jest options
const argsConfig = Object.keys(CliOptions).reduce((prev, name) => {
if (args[name]) prev[name] = args[name];
// Convert alias args into real one
const { alias } = CliOptions[name];
if (alias && args[alias]) prev[name] = args[alias];
return prev;
}, {});
args.debug && logger.log(`config from args: ${JSON.stringify(argsConfig)}`);
// 比较大的库建议使用require使用时才加载提升fes命令的效率
const { runCLI } = require('jest');
// Run jest
const result = await runCLI(
{
// @ts-ignore
_: args._ || [],
// @ts-ignore
$0: args.$0 || '',
// 必须是单独的 config 配置,值为 string否则不生效
// @ts-ignore
config: JSON.stringify(config),
...argsConfig
},
[cwd]
);
args.debug && logger.log(result);
// Throw error when run failed
assert(result.results.success, 'Test with jest failed');
}
});
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-plugin-layout", "name": "@webank/fes-plugin-layout",
"version": "2.0.0-alpha.5", "version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-layout", "description": "@webank/fes-plugin-layout",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -33,6 +33,6 @@
"@ant-design/icons-vue": "^5.1.6", "@ant-design/icons-vue": "^5.1.6",
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
"ant-design-vue": "2.0.0-rc.3", "ant-design-vue": "2.0.0-rc.3",
"vue": "^3.0.4" "vue": "3.0.5"
} }
} }

View File

@ -28,11 +28,10 @@ export default (api) => {
const HAS_LOCALE = api.hasPlugins(['@webank/fes-plugin-locale']); const HAS_LOCALE = api.hasPlugins(['@webank/fes-plugin-locale']);
const HAS_ACCESS = api.hasPlugins(['@webank/fes-plugin-access']);
// .fes配置 // .fes配置
const userConfig = { const userConfig = {
title: name, title: name,
footer: 'Created by Fes.js',
...(api.config.layout || {}) ...(api.config.layout || {})
}; };

View File

@ -56,8 +56,8 @@
<MultiTabProvider v-if="multiTabs" /> <MultiTabProvider v-if="multiTabs" />
<router-view v-else></router-view> <router-view v-else></router-view>
</a-layout-content> </a-layout-content>
<a-layout-footer class="layout-footer"> <a-layout-footer v-if="footer" class="layout-footer">
Fes.js ©2020 Created by MumbleFe {{footer}}
</a-layout-footer> </a-layout-footer>
</a-layout> </a-layout>
</a-layout> </a-layout>
@ -128,7 +128,8 @@ export default {
sideWidth: { sideWidth: {
type: Number, type: Number,
default: 200 default: 200
} },
footer: String
}, },
setup(props) { setup(props) {
const collapsed = ref(false); const collapsed = ref(false);

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-plugin-locale", "name": "@webank/fes-plugin-locale",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-locale", "description": "@webank/fes-plugin-locale",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -28,12 +28,12 @@
}, },
"dependencies": { "dependencies": {
"@umijs/utils": "3.3.3", "@umijs/utils": "3.3.3",
"vue-i18n": "^9.0.0-beta.15" "vue-i18n": "^9.0.0-rc.5"
}, },
"peerDependencies": { "peerDependencies": {
"@ant-design/icons-vue": "^5.1.6", "@ant-design/icons-vue": "^5.1.6",
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
"ant-design-vue": "2.0.0-rc.3", "ant-design-vue": "2.0.0-rc.3",
"vue": "^3.0.4" "vue": "3.0.5"
} }
} }

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-plugin-model", "name": "@webank/fes-plugin-model",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-model", "description": "@webank/fes-plugin-model",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -31,6 +31,6 @@
}, },
"peerDependencies": { "peerDependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
"vue": "^3.0.4" "vue": "3.0.5"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-plugin-request", "name": "@webank/fes-plugin-request",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-request", "description": "@webank/fes-plugin-request",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -28,7 +28,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
"vue": "^3.0.4" "vue": "3.0.5"
}, },
"dependencies": { "dependencies": {
"axios": "0.21.1" "axios": "0.21.1"

View File

@ -0,0 +1,3 @@
export default {
disableTypeCheck: false,
};

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-present webank
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -0,0 +1,37 @@
{
"name": "@webank/fes-plugin-vuex",
"version": "2.0.0-alpha.8",
"description": "@webank/fes-plugin-vuex",
"main": "lib/index.js",
"files": [
"lib"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
"directory": "packages/fes-plugin-vuex"
},
"keywords": [
"fes"
],
"author": "aringlai",
"license": "MIT",
"bugs": {
"url": "https://github.com/WeBankFinTech/fes.js/issues"
},
"homepage": "https://github.com/WeBankFinTech/fes.js#readme",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@umijs/utils": "3.3.3"
},
"peerDependencies": {
"@webank/fes": "^2.0.0-alpha.0",
"vue": "3.0.5",
"vuex": "^4.0.0-rc.2"
}
}

View File

@ -0,0 +1,160 @@
import { parser } from '@umijs/utils';
import { readdirSync, readFileSync, statSync } from 'fs';
import { join } from 'path';
/**
* 获取文件夹所有JS文件路径
* @param {string} dir
*/
function getDirFilePaths(dir) {
const dirs = readdirSync(dir);
let pathList = [];
for (const name of dirs) {
const path = join(dir, name);
const info = statSync(path);
if (info.isDirectory()) {
pathList = pathList.concat(getDirFilePaths(path));
} else if (path.endsWith('.js')) {
pathList.push(path);
}
}
return pathList;
}
/**
* 路径转驼峰
* @param {*} path
*/
function pathToHump(path, root) {
return path.replace(root, '')
.replace('.js', '')
.replace(/(\/|\.|-|_)\S/g, text => text[1].toUpperCase())
.replace(/\S/, text => text.toLowerCase());
}
/**
* 获取vuex模块的mutationsactionsgetters类型
* @param {*} ast
* @param {*} name
*/
function getModelTypes(ast, name, namespace = '') {
const types = {
mutations: {},
actions: {},
getters: {}
};
let namespaced = false;
if (ast.type !== 'ObjectExpression') return types;
ast.properties.forEach((node) => {
if (node.key.name === 'namespaced' && node.value.value) {
namespaced = true;
return;
}
if (Object.keys(types).includes(node.key.name)) {
let type = types[node.key.name];
if (namespaced) {
type = types[node.key.name][name];
if (!type) {
// eslint-disable-next-line no-multi-assign
type = types[node.key.name][name] = {};
}
}
node.value.properties.forEach((prop) => {
const key = prop.key && prop.key.name;
if (key) {
type[key] = `${namespace}${namespaced ? `${name}/` : ''}${key}`;
}
});
return;
}
if (node.key.name === 'modules') {
node.value.properties.forEach((prop) => {
const subTypes = getModelTypes(prop.value, prop.key.name, `${namespace}${namespaced ? `${name}/` : ''}`);
Object.keys(types).forEach((key) => {
if (namespaced) {
types[key][name] = {
...subTypes[key],
...types[key][name]
};
} else {
types[key] = {
...subTypes[key],
...types[key]
};
}
});
});
}
});
return types;
}
/**
* 解析模块
* @param {*} paths
* @param {*} root
*/
function parseModel(paths = [], root) {
const modules = [];
const importModules = [];
let MUTATION_TYPES = {};
let ACTION_TYPES = {};
let GETTER_TYPES = {};
paths.forEach((path) => {
const moduleName = pathToHump(path, root);
importModules.push(`import ${moduleName} from '${path}'`);
modules.push(moduleName);
const content = readFileSync(path).toString('utf-8');
let ast = parser.parse(content, {
sourceType: 'module',
plugins: ['jsx', 'typescript']
});
ast = ast.program.body.filter(body => body.type === 'ExportDefaultDeclaration')[0];
if (ast) {
const { mutations, actions, getters } = getModelTypes(ast.declaration, moduleName);
MUTATION_TYPES = {
...mutations,
...MUTATION_TYPES
};
ACTION_TYPES = {
...actions,
...ACTION_TYPES
};
GETTER_TYPES = {
...getters,
...GETTER_TYPES
};
}
});
return {
modules, importModules, MUTATION_TYPES, ACTION_TYPES, GETTER_TYPES
};
}
function parsePlugin(paths = [], root) {
const plugins = [];
const importPlugins = [];
paths.forEach((path) => {
const moduleName = pathToHump(path, root);
importPlugins.push(`import ${moduleName} from '${path}'`);
plugins.push(moduleName);
});
return { plugins, importPlugins };
}
export function parseStore(root) {
const paths = getDirFilePaths(root);
const modelPaths = [];
const pluginPaths = [];
paths.forEach((path) => {
if (path.indexOf('plugin') > -1) {
pluginPaths.push(path);
} else {
modelPaths.push(path);
}
});
return {
...parsePlugin(pluginPaths, root),
...parseModel(modelPaths, root)
};
}

View File

@ -0,0 +1,62 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { parseStore } from './helper';
const namespace = 'plugin-vuex';
export default (api) => {
const {
paths,
utils: { Mustache }
} = api;
api.describe({
key: 'vuex',
config: {
schema(joi) {
return joi.object();
},
onChange: api.ConfigChangeType.regenerateTmpFiles
}
});
const absCoreFilePath = join(namespace, 'core.js');
const absRuntimeFilePath = join(namespace, 'runtime.js');
api.onGenerateFiles(() => {
const root = join(paths.absSrcPath, api.config.singular ? 'store' : 'stores');
const store = parseStore(root);
const vuexConfig = api.config.vuex || {};
// 文件写出
api.writeTmpFile({
path: absCoreFilePath,
content: Mustache.render(
readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'),
{
IMPORT_MODULES: store.importModules.join('\n'),
IMPORT_PLUGINS: store.importPlugins.join('\n'),
MODULES: `{ ${store.modules.join(', ')} }`,
PLUGINS: `[${store.plugins.join(', ')}]`,
MUTATION_TYPES: JSON.stringify(store.MUTATION_TYPES),
ACTION_TYPES: JSON.stringify(store.ACTION_TYPES),
GETTER_TYPES: JSON.stringify(store.GETTER_TYPES),
VUEX_CONFIG: JSON.stringify(vuexConfig)
}
)
});
api.copyTmpFiles({
namespace,
path: join(__dirname, 'runtime'),
ignore: ['.tpl']
});
});
api.addPluginExports(() => [
{
specifiers: ['MUTATION_TYPES', 'ACTION_TYPES', 'GETTER_TYPES'],
source: absCoreFilePath
}
]);
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
};

View File

@ -0,0 +1,26 @@
import { createStore } from 'vuex';
{{{IMPORT_MODULES}}};
{{{IMPORT_PLUGINS}}};
const modules = {{{MODULES}}};
const MUTATION_TYPES = {{{MUTATION_TYPES}}};
const ACTION_TYPES = {{{ACTION_TYPES}}};
const GETTER_TYPES = {{{GETTER_TYPES}}};
const conifg = {{{VUEX_CONFIG}}};
const install = function (app) {
app.use(createStore({
modules: modules,
plugins: {{{PLUGINS}}},
strict: conifg.strict,
devtools: conifg.devtools
}));
}
export {
install,
MUTATION_TYPES,
ACTION_TYPES,
GETTER_TYPES
};

View File

@ -0,0 +1,6 @@
// eslint-disable-next-line import/extensions
import { install } from './core';
export function onAppCreated({ app }) {
install(app);
}

View File

@ -1,3 +1,7 @@
# fes
一个好用的前端应用解决方案
# 内置插件 # 内置插件
## TODO ## TODO

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-preset-built-in", "name": "@webank/fes-preset-built-in",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-preset-built-in", "description": "@webank/fes-preset-built-in",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -35,19 +35,16 @@
"@umijs/utils": "3.3.3", "@umijs/utils": "3.3.3",
"@vue/babel-plugin-jsx": "^1.0.2", "@vue/babel-plugin-jsx": "^1.0.2",
"@vue/compiler-sfc": "^3.0.4", "@vue/compiler-sfc": "^3.0.4",
"@webank/fes-compiler": "^2.0.0-alpha.2",
"babel-loader": "^8.2.2", "babel-loader": "^8.2.2",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"cliui": "7.0.4", "cliui": "7.0.4",
"html-webpack-plugin": "^5.0.0", "html-webpack-plugin": "^5.0.0",
"html-webpack-tags-plugin": "^3.0.0", "html-webpack-tags-plugin": "^3.0.0",
"vue-loader": "^16.1.2",
"webpackbar": "^5.0.0-3", "webpackbar": "^5.0.0-3",
"@soda/friendly-errors-webpack-plugin": "^1.8.0", "@soda/friendly-errors-webpack-plugin": "^1.8.0",
"webpack-bundle-analyzer": "^4.4.0", "webpack-bundle-analyzer": "^4.4.0",
"webpack-dev-server": "^3.11.2", "webpack-dev-server": "^3.11.2",
"babel-plugin-import": "1.13.3", "babel-plugin-import": "1.13.3",
"hard-source-webpack-plugin": "0.13.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
@ -63,8 +60,15 @@
"postcss-flexbugs-fixes": "^5.0.2", "postcss-flexbugs-fixes": "^5.0.2",
"postcss-safe-parser": "^5.0.2", "postcss-safe-parser": "^5.0.2",
"copy-webpack-plugin": "^7.0.0", "copy-webpack-plugin": "^7.0.0",
"webpack-chain": "^6.5.1",
"webpack": "^5.21.0", "webpack": "^5.21.0",
"deepmerge": "^4.2.2" "deepmerge": "^4.2.2",
"@webank/fes-compiler": "^2.0.0-alpha.8",
"envinfo": "^7.7.3",
"vue-loader": "^16.1.2",
"cli-highlight": "^2.1.4",
"webpack-chain": "^6.5.1",
"body-parser": "^1.19.0",
"cookie-parser": "^1.4.5",
"mockjs": "^1.1.0"
} }
} }

View File

@ -41,14 +41,17 @@ export default function () {
require.resolve('./plugins/features/terserOptions'), require.resolve('./plugins/features/terserOptions'),
require.resolve('./plugins/features/nodeModulesTransform'), require.resolve('./plugins/features/nodeModulesTransform'),
require.resolve('./plugins/features/vueLoader'), require.resolve('./plugins/features/vueLoader'),
require.resolve('./plugins/features/hardSource'), require.resolve('./plugins/features/mock'),
// misc // misc
require.resolve('./plugins/misc/route'), require.resolve('./plugins/misc/route'),
// commands // commands
require.resolve('./plugins/commands/build'), require.resolve('./plugins/commands/build'),
require.resolve('./plugins/commands/dev') require.resolve('./plugins/commands/dev'),
require.resolve('./plugins/commands/help'),
require.resolve('./plugins/commands/info'),
require.resolve('./plugins/commands/webpack')
] ]
}; };
} }

View File

@ -18,7 +18,7 @@ export default function (api) {
} = api; } = api;
api.registerCommand({ api.registerCommand({
name: 'build', command: 'build',
description: 'build application for production', description: 'build application for production',
async fn() { async fn() {
cleanTmpPathExceptCache({ cleanTmpPathExceptCache({

View File

@ -1,5 +1,5 @@
import { delay } from '@umijs/utils';
import assert from 'assert'; import assert from 'assert';
import { delay } from '@umijs/utils';
import { import {
cleanTmpPathExceptCache, cleanTmpPathExceptCache,
getBundleAndConfigs getBundleAndConfigs
@ -28,8 +28,15 @@ export default (api) => {
} }
api.registerCommand({ api.registerCommand({
name: 'dev', command: 'dev',
description: 'start a dev server for development', description: 'start a local http service for development',
options: [{
name: '--port',
description: 'http service port, like 8080'
}, {
name: '--https',
description: 'whether to turn on the https service'
}],
async fn({ args = {} }) { async fn({ args = {} }) {
const defaultPort = process.env.PORT || args.port || api.config.devServer?.port; const defaultPort = process.env.PORT || args.port || api.config.devServer?.port;
port = await portfinder.getPortPromise({ port = await portfinder.getPortPromise({

View File

@ -0,0 +1,11 @@
export default function (api) {
api.registerCommand({
command: 'help',
description: 'show command helps',
async fn({ program }) {
program.outputHelp();
}
});
}

View File

@ -0,0 +1,24 @@
export default function (api) {
api.registerCommand({
command: 'info',
description: 'print debugging information about your environment',
async fn() {
require('envinfo').run(
{
System: ['OS', 'CPU'],
Binaries: ['Node', 'Yarn', 'npm'],
Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
npmPackages: ['@webank/fes', 'vue', 'vue-router'],
npmGlobalPackages: ['@webank/fes']
},
{
showNotFound: true,
duplicates: true,
fullTree: true
}
)
.then(console.log);
}
});
}

View File

@ -0,0 +1,52 @@
import assert from 'assert';
import { getBundleAndConfigs } from '../buildDevUtils';
export default function (api) {
api.registerCommand({
command: 'webpack',
description: 'inspect webpack configurations',
options: [{
name: '--rule <ruleName>',
description: 'inspect a specific module rule'
}, {
name: '--plugin <pluginName>',
description: 'inspect a specific plugin'
}, {
name: '--rules',
description: 'list all module rule names'
}, {
name: '--plugins',
description: 'list all plugin names'
}, {
name: '--verbose',
description: 'show full function definitions in output'
}],
async fn({ options }) {
const { toString } = require('webpack-chain');
const { highlight } = require('cli-highlight');
const { bundleConfig } = await getBundleAndConfigs({ api });
let config = bundleConfig.filter(bc => bc.entry?.index)[0];
assert(config, 'No valid config found with fes entry.');
if (options.rule) {
config = config.module.rules.find(
r => r.__ruleNames[0] === options.rule
);
} else if (options.plugin) {
config = config.plugins.find(
p => p.__pluginName === options.plugin
);
} else if (options.rules) {
config = config.module.rules.map(r => r.__ruleNames[0]);
} else if (options.plugins) {
config = config.plugins.map(
p => p.__pluginName || p.constructor.name
);
}
console.log(highlight(toString(config, { verbose: options.verbose }), { language: 'js' }));
}
});
}

View File

@ -84,7 +84,12 @@ export default function createCssWebpackConfig({
lang: 'less', lang: 'less',
test: /\.less$/, test: /\.less$/,
loader: 'less-loader', loader: 'less-loader',
options: config.lessLoader || {}, options: {
lessOptions: {
javascriptEnabled: true,
...config.lessLoader
}
},
browserslist browserslist
}); });

View File

@ -26,7 +26,7 @@ function resolveDefine(opts = {}) {
for (const key in define) { for (const key in define) {
if (Object.prototype.hasOwnProperty.call(define, key)) { if (Object.prototype.hasOwnProperty.call(define, key)) {
define[key] = JSON.stringify(opts.define[key]); define[key] = JSON.stringify(define[key]);
} }
} }

View File

@ -78,6 +78,12 @@ export default async function getConfig({
webpackConfig.externals(config.externals || {}); webpackConfig.externals(config.externals || {});
webpackConfig.devtool(isDev ? (config.devtool || 'cheap-module-source-map') : config.devtool); webpackConfig.devtool(isDev ? (config.devtool || 'cheap-module-source-map') : config.devtool);
// --------------- cache -----------
webpackConfig.cache({
type: 'filesystem',
cacheDirectory: join(cwd, '.cache/webpack')
});
// --------------- entry ----------- // --------------- entry -----------
// Feature 公共模块 vue vue-router 处理 dependOn ? // Feature 公共模块 vue vue-router 处理 dependOn ?
Object.keys(entry).forEach((key) => { Object.keys(entry).forEach((key) => {

View File

@ -18,11 +18,4 @@ export default function createVueWebpackConfig({
webpackConfig webpackConfig
.plugin('vue-loader') .plugin('vue-loader')
.use(require('vue-loader').VueLoaderPlugin); .use(require('vue-loader').VueLoaderPlugin);
// webpackConfig
// .plugin('feature-flags')
// .use(webpack.DefinePlugin, [{
// __VUE_OPTIONS_API__: 'true',
// __VUE_PROD_DEVTOOLS__: 'false'
// }]);
} }

View File

@ -1,43 +0,0 @@
// import {
// winPath
// } from '@umijs/utils';
// import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
export default (api) => {
api.describe({
key: 'hardSource',
config: {
schema(joi) {
return joi.object();
}
}
});
// api.chainWebpack((webpackConfig) => {
// const cwd = api.cwd;
// if (api.env === 'development') {
// webpackConfig
// .plugin('hardSource')
// .use(HardSourceWebpackPlugin, [{
// cacheDirectory: winPath(`${cwd}/.cache/hard-source/[confighash]`),
// ...api.config.hardSource || {}
// }]);
// webpackConfig
// .plugin('hardSourceExclude')
// .use(HardSourceWebpackPlugin.ExcludeModulePlugin, [
// [
// {
// // HardSource works with mini-css-extract-plugin but due to how
// // mini-css emits assets, assets are not emitted on repeated builds with
// // mini-css and hard-source together. Ignoring the mini-css loader
// // modules, but not the other css loader modules, excludes the modules
// // that mini-css needs rebuilt to output assets every time.
// test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
// }
// ]
// ]);
// }
// return webpackConfig;
// });
};

View File

@ -3,6 +3,7 @@ export default (api) => {
api.describe({ api.describe({
key: 'lessLoader', key: 'lessLoader',
config: { config: {
default: {},
schema(joi) { schema(joi) {
return joi.object(); return joi.object();
} }

View File

@ -0,0 +1,174 @@
import { existsSync, readFileSync } from 'fs';
import { resolve } from 'path';
import { chokidar, lodash } from '@umijs/utils';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import Mock from 'mockjs';
export default (api) => {
let mockFlag = false; // mock 开关flag
let mockPrefix = '/'; // mock 过滤前缀
let mockFile = ''; // mock 文件
let loadMock = ''; // mock 对象
api.describe({
key: 'mock',
config: {
schema(joi) {
return joi.alternatives(joi.boolean(), joi.object());
}
}
});
// 对 array、object 遍历处理
function traversalHandler(val, callback) {
if (lodash.isArray(val)) {
val.forEach(callback);
}
if (lodash.isPlainObject(val)) {
Object.keys(val).forEach((key) => { callback(val[key], key); });
}
}
// 根据参数个数获取配置
function getOption(arg) {
const len = arg.length;
// 默认配置
const option = {
headers: {
'Cache-Control': 'no-cache'
},
statusCode: 200,
cookies: [],
timeout: 0
};
if (len === 0) return option;
if (len === 1) {
const newOption = arg[0];
if (lodash.isPlainObject(newOption)) {
traversalHandler(newOption, (value, key) => {
if (key === 'headers') {
traversalHandler(newOption.headers, (headervalue, headerkey) => {
option.headers[headerkey] = newOption.headers[headerkey];
});
} else {
option[key] = newOption[key];
}
});
}
} else {
option.url = arg[0];
option.result = arg[1];
}
return option;
}
// 把基于 cgiMockfile 的相对绝对转成绝对路径
function parsePath(value) {
return resolve(api.cwd, value);
}
const createMock = () => {
const requestList = [];
const cgiMock = (...arg) => {
const option = getOption(arg);
if (!option.url || !option.result) return;
requestList.push(option);
};
cgiMock.file = function (file) {
return readFileSync(parsePath(file));
};
// mock打开情况下配置的过滤前缀
const mockPrefixTemp = api.config.mock.prefix || mockPrefix;
mockPrefix = mockPrefixTemp === mockPrefix ? mockPrefixTemp : `${mockPrefixTemp}/`;
// mock文件处理
mockFile = parsePath('./mock.js');
if (!existsSync(mockFile)) {
api.logger.info('mock.js File does not exist, please check'); return;
}
// 清除require的缓存保证 mock 文件修改后拿到最新的 mock.js
if (require.cache[mockFile]) {
delete require.cache[mockFile];
}
// require最新的 mock.js 文件
try {
const projectMock = require(mockFile);
if (!lodash.isFunction(projectMock)) {
api.logger.info('mock.js should export Function'); return;
}
projectMock({ cgiMock, Mock });
} catch (err) {
api.logger.info('mock.js run fail!');
}
return (req, res, next) => {
// 如果请求不是以 cgiMock.prefix 开头,直接 next
if (!req.path.startsWith(mockPrefix)) {
return next();
}
// 请求以 cgiMock.prefix 开头,匹配处理
const matchRequet = requestList.find(item => req.path.search(item.url) !== -1);
if (!matchRequet) {
return next();
}
// set header
res.set(matchRequet.headers);
// set Content-Type
matchRequet.type && res.type(matchRequet.type);
// set status code
res.status(matchRequet.statusCode);
// set cookie
traversalHandler(matchRequet.cookies, (item) => {
const name = item.name;
const value = item.value;
delete item.name;
delete item.value;
res.cookie(name, value, item);
});
// do result
if (lodash.isFunction(matchRequet.result)) {
matchRequet.result(req, res);
} else if (
lodash.isArray(matchRequet.result) || lodash.isPlainObject(matchRequet.result)
) {
!matchRequet.type && res.type('json');
res.json(matchRequet.result);
} else {
!matchRequet.type && res.type('text');
res.send(matchRequet.result.toString());
}
};
};
api.onStart(() => {
// 获取mock配置: 是否打开
mockFlag = lodash.isPlainObject(api.config.mock) ? true : api.config.mock;
if (!mockFlag) return;
loadMock = createMock();
chokidar.watch(mockFile, {
ignoreInitial: true
}).on('change', () => {
api.logger.info('mock.js changedreload');
loadMock = createMock();
});
});
api.addBeforeMiddlewares(() => {
if (!mockFlag) return [];
return [
bodyParser.json(),
bodyParser.urlencoded({
extended: false
}),
cookieParser()
];
});
api.addBeforeMiddlewares(() => (req, res, next) => {
if (!mockFlag) return next();
loadMock(req, res, next);
});
};

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes-runtime", "name": "@webank/fes-runtime",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "@webank/fes-runtime", "description": "@webank/fes-runtime",
"main": "dist/index.js", "main": "dist/index.js",
"files": [ "files": [
@ -25,7 +25,7 @@
"access": "public" "access": "public"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.0.4" "vue": "3.0.5"
}, },
"dependencies": { "dependencies": {
"vue-router": "^4.0.1" "vue-router": "^4.0.1"

View File

@ -40,15 +40,14 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@vue/compiler-sfc": "^3.0.0", "@webank/eslint-config-webank": "0.2.10",
"@webank/eslint-config-webank": "^0.2.10",
"postcss-px-to-viewport": "1.1.1" "postcss-px-to-viewport": "1.1.1"
}, },
"dependencies": { "dependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
"@webank/fes-plugin-icon": "^2.0.0-alpha.0", "@webank/fes-plugin-icon": "^2.0.0-alpha.0",
"@webank/fes-plugin-request": "^2.0.0-alpha.0", "@webank/fes-plugin-request": "^2.0.0-alpha.0",
"vue": "^3.0.4" "vue": "3.0.5"
}, },
"private": true "private": true
} }

View File

@ -0,0 +1,14 @@
module.exports = {
extends: ['@webank/eslint-config-webank/vue.js'],
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
]
}
],
env: {
jest: true
}
};

View File

@ -9,16 +9,28 @@ export default {
publicPath: '/', publicPath: '/',
access: { access: {
roles: { roles: {
admin: ["/", "/onepiece"] admin: ["/", "/onepiece", '/store']
} }
}, },
mock: {
prefix: '/v2'
},
proxy: {
'/v2': {
'target': 'https://api.douban.com/',
'changeOrigin': true,
},
},
layout: { layout: {
title: "Fes.js", title: "Fes.js",
footer: 'Created by MumbelFe',
multiTabs: false, multiTabs: false,
menus: [{ menus: [{
name: 'index' name: 'index'
}, { }, {
name: 'onepiece' name: 'onepiece'
}, {
name: 'store'
}] }]
}, },
locale: { locale: {
@ -29,5 +41,8 @@ export default {
}, },
enums: { enums: {
status: [['0', '无效的'], ['1', '有效的']] status: [['0', '无效的'], ['1', '有效的']]
},
vuex: {
strict: true
} }
}; };

View File

@ -0,0 +1,5 @@
export default {
// define: {
// __DEV__: true
// },
}

View File

@ -2,7 +2,7 @@
# dependencies # dependencies
/node_modules /node_modules
/coverage
# fes # fes
/src/.fes /src/.fes

View File

@ -0,0 +1,5 @@
import sum from '@/utils/sum';
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});

View File

@ -0,0 +1,130 @@
module.exports = ({ cgiMock, Mock }) => {
const { Random } = Mock;
// 测试 proxy 与 mock 用例集合
cgiMock('/movie/in_theaters_mock', (req, res) => {
res.send(JSON.stringify({
code: '0',
msg: '',
result: {
text: 'movie: movie/in_theaters_mock ~~~~~'
}
}));
});
cgiMock('/movie/test_mock', (req, res) => {
res.send(JSON.stringify({
code: '0',
msg: '',
result: {
text: 'mock: movie/test_mock'
}
}));
});
// 测试用例: mock.js change重现请求需要能拉最新的数据
cgiMock('/watchtest', (req, res) => {
res.send(JSON.stringify({
code: '0',
msg: '',
result: {
text: '通过 register 测试 mock watch: 初始状态'
}
}));
});
// 返回一个数字
// cgiMock('/number', 666);
cgiMock('/number', 999);
// 返回一个json
cgiMock({
url: '/json',
result: {
code: '400101', msg: "不合法的请求:Missing cookie 'wb_app_id' for method parameter of type String", transactionTime: '20170309171146', success: false
}
});
// 利用 mock.js 产生随机文本
cgiMock('/text', Random.cparagraph());
// 返回一个字符串 利用 mock.js 产生随机字符
cgiMock('/random', Mock.mock({
'string|1-10': '★'
}));
// 正则匹配url, 返回一个字符串
cgiMock(/\/abc|\/xyz/, 'regexp test!');
// option.result 参数如果是一个函数, 可以实现自定义返回内容, 接收的参数是是经过 express 封装的 req 和 res 对象.
cgiMock(/\/function$/, (req, res) => {
res.send('function test');
});
// 返回文本 readFileSync
cgiMock('/file', cgiMock.file('./package.json'));
// 更复杂的规则配置
cgiMock({
url: /\/who/,
method: 'GET',
result(req, res) {
if (req.query.name === 'kwan') {
res.json({ kwan: '孤独患者' });
} else {
res.send('Nooooooooooo');
}
},
headers: {
'Content-Type': 'text/plain',
'Content-Length': '123',
ETag: '12345'
},
cookies: [
{
name: 'myname', value: 'kwan', maxAge: 900000, httpOnly: true
}
]
});
// 携带参数的请求
cgiMock('/v2/audit/list', (req, res) => {
const {
currentPage, pageSize, isAudited
} = req.body;
res.send({
code: '0',
msg: '',
data: {
currentPage,
pageSize,
totalPage: 2,
totalCount: 12,
pageData: Array.from({ length: pageSize }, () => ({
title: Random.title(),
authorName: Random.cname(),
authorId: Random.name(),
createTime: Date.now(),
updateTime: Date.now(),
readCount: Random.integer(60, 1000),
favoriteCount: Random.integer(1, 50),
postId: '12323',
serviceTag: '业务类型',
productTag: '产品类型',
requestTag: '需求类型',
handleTag: '已采纳',
postType: 'voice',
postStatus: isAudited ? 'pass' : 'auditing',
auditStatus: 'audit1'
}))
}
});
});
// multipart/form-data 类型
cgiMock('/v2/upload', (req, res) => {
res.send({
code: '0',
msg: '文件上传成功'
});
});
};

View File

@ -6,7 +6,8 @@
"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"
}, },
"keywords": [ "keywords": [
"管理端", "管理端",
@ -42,8 +43,7 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@vue/compiler-sfc": "^3.0.0", "@webank/eslint-config-webank": "0.2.10"
"@webank/eslint-config-webank": "^0.2.10"
}, },
"dependencies": { "dependencies": {
"@webank/fes": "^2.0.0-alpha.0", "@webank/fes": "^2.0.0-alpha.0",
@ -52,8 +52,12 @@
"@webank/fes-plugin-locale": "^2.0.0-alpha.0", "@webank/fes-plugin-locale": "^2.0.0-alpha.0",
"@webank/fes-plugin-model": "^2.0.0-alpha.0", "@webank/fes-plugin-model": "^2.0.0-alpha.0",
"@webank/fes-plugin-enums": "^2.0.0-alpha.0", "@webank/fes-plugin-enums": "^2.0.0-alpha.0",
"ant-design-vue": "2.0.0-rc.3", "@webank/fes-plugin-jest": "^2.0.0-alpha.8",
"vue": "3.0.4" "@webank/fes-plugin-vuex": "^2.0.0-alpha.8",
"@webank/fes-plugin-request": "2.0.0-alpha.1",
"ant-design-vue": "2.0.0",
"vue": "3.0.5",
"vuex": "^4.0.0-rc.2"
}, },
"private": true "private": true
} }

View File

@ -20,7 +20,7 @@
<script> <script>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { import {
access, useAccess, useRouter, useI18n, locale, enums access, useAccess, useRouter, useI18n, locale, enums, request
} from '@webank/fes'; } from '@webank/fes';
export default { export default {
@ -75,6 +75,25 @@ export default {
accessId.value = '11'; accessId.value = '11';
}, 4000); }, 4000);
// router.push('/onepiece'); // router.push('/onepiece');
console.log('测试 mock!!');
request('/v2/file').then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
request('/v2/movie/in_theaters_mock').then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
console.log('测试 proxy!!');
request('/v2/movie/in_theaters_proxy').then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
}); });
return { return {
accessId, accessId,

View File

@ -0,0 +1,50 @@
<template>
<div class="haizekuo">
<h4>Vuex</h4>
<div><button @click="increment">click me{{doubleCount}}</button></div>
<div><button :disabled="disabled" @click="login">async login</button></div>
<div><button @click="fooBarIncrement">foo/bar{{fooBarDoubleCount}}</button></div>
<div>{{address}}</div>
</div>
</template>
<config>
{
"name": "store",
"title": "vuex测试"
}
</config>
<script>
import { computed, ref } from 'vue';
import { useStore } from 'vuex';
import { MUTATION_TYPES, GETTER_TYPES, ACTION_TYPES } from '@webank/fes';
export default {
setup() {
const store = useStore();
console.log('store==>', store);
const disabled = ref(false);
return {
address: computed(() => store.getters[GETTER_TYPES.user.address]),
doubleCount: computed(() => store.getters[GETTER_TYPES.counter.doubleCount]),
disabled,
increment: () => store.commit(MUTATION_TYPES.counter.increment),
login: () => {
disabled.value = true;
store.dispatch(ACTION_TYPES.user.login).then((res) => {
// eslint-disable-next-line no-alert
window.alert(res);
disabled.value = false;
});
},
fooBarIncrement: () => store.commit(MUTATION_TYPES.fooBar.increment),
fooBarDoubleCount: computed(() => store.getters[GETTER_TYPES.fooBar.doubleCount])
};
}
};
</script>
<style scoped>
.haizekuo {
/* background: url('../images/icon.png'); */
}
</style>

View File

@ -0,0 +1,23 @@
export default {
namespaced: true,
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -0,0 +1,23 @@
export default {
namespaced: true,
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -0,0 +1,3 @@
import { createLogger } from 'vuex';
export default createLogger();

View File

@ -0,0 +1,54 @@
export default {
namespaced: true,
state: () => ({
name: 'aring',
age: 20,
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
},
login() {
return new Promise((reslove) => {
setTimeout(() => {
console.log('login');
reslove('OK');
}, 1000);
});
}
},
modules: {
address: {
state: () => ({
province: '广东省',
city: '深圳市',
zone: '南山区'
}),
getters: {
address(state) {
return state.province + state.city + state.zone;
}
}
},
posts: {
namespaced: true,
state: () => ({}),
mutations: {
doSomething() {}
}
}
}
};

View File

@ -0,0 +1,3 @@
export default function sum(a, b) {
return a + b;
}

View File

@ -0,0 +1,3 @@
# fes
一个好用的前端应用解决方案

View File

@ -1,31 +1,3 @@
# fes-cli # fes
`fes-cli`是命令行工具,解决创建工程、开发调试、打包发布。 一个好用的前端应用解决方案
## TODO
* 以插件化的形式重写 fes-cli
## 安装
npm install -g @webank/fes-cli
## 使用
### 创建项目
fes init [project]
### 开发调试
fes dev
启动http服务默认监听localhost:5000
### 编译打包
fes build
## 文档
详细使用请查看[文档](https://webankfintech.github.io/fes.js/)

View File

@ -1,6 +1,6 @@
{ {
"name": "@webank/fes", "name": "@webank/fes",
"version": "2.0.0-alpha.2", "version": "2.0.0-alpha.8",
"description": "一个好用的前端管理台快速开发框架", "description": "一个好用的前端管理台快速开发框架",
"preferGlobal": true, "preferGlobal": true,
"scripts": { "scripts": {
@ -40,12 +40,9 @@
], ],
"dependencies": { "dependencies": {
"@umijs/utils": "3.3.3", "@umijs/utils": "3.3.3",
"@webank/fes-compiler": "^2.0.0-alpha.2", "@webank/fes-compiler": "^2.0.0-alpha.8",
"@webank/fes-preset-built-in": "^2.0.0-alpha.2", "@webank/fes-preset-built-in": "^2.0.0-alpha.8",
"@webank/fes-runtime": "^2.0.0-alpha.2", "@webank/fes-runtime": "^2.0.0-alpha.8",
"commander": "^6.2.1",
"envinfo": "^7.7.3",
"leven": "^3.1.0",
"resolve-cwd": "^3.0.0" "resolve-cwd": "^3.0.0"
}, },
"engines": { "engines": {

View File

@ -1,6 +1,4 @@
import { chalk, yParser, semver } from '@umijs/utils'; import { chalk, yParser, semver } from '@umijs/utils';
import program from 'commander';
import leven from 'leven';
import { Service } from './serviceWithBuiltIn'; import { Service } from './serviceWithBuiltIn';
import fork from './utils/fork'; import fork from './utils/fork';
import getCwd from './utils/getCwd'; import getCwd from './utils/getCwd';
@ -24,36 +22,15 @@ function checkNodeVersion(wanted, id) {
checkNodeVersion(requiredVersion, '@webank/fes'); checkNodeVersion(requiredVersion, '@webank/fes');
function suggestCommands(unknownCommand) {
const availableCommands = program.commands.map(cmd => cmd._name);
let suggestion;
availableCommands.forEach((cmd) => {
const isBestMatch = leven(cmd, unknownCommand) < leven(suggestion || '', unknownCommand);
if (leven(cmd, unknownCommand) < 3 && isBestMatch) {
suggestion = cmd;
}
});
if (suggestion) {
console.log(` ${chalk.red(`Did you mean ${chalk.yellow(suggestion)}?`)}`);
}
}
// process.argv: [node, fes.js, command, args] // process.argv: [node, fes.js, command, args]
const args = yParser(process.argv.slice(2)); const rawArgv = process.argv.slice(2);
const args = yParser(rawArgv);
program (async () => {
.version(`@webank/fes ${fesPkg.version}`, '-v, --vers', 'output the current version') try {
.usage('<command> [options]') const command = args._[0];
.description(fesPkg.description); process.env.FES_ENV = args.mode || '';
if (command === 'dev') {
program
.command('dev')
.description('run local http service for development')
.action(() => {
try {
const child = fork({ const child = fork({
scriptPath: require.resolve('./forkedDev') scriptPath: require.resolve('./forkedDev')
}); });
@ -67,84 +44,22 @@ program
child.kill('SIGTERM'); child.kill('SIGTERM');
process.exit(1); process.exit(1);
}); });
} catch (e) { } else {
console.error(chalk.red(e.message)); if (command === 'build') {
console.error(e.stack); process.env.NODE_ENV = 'production';
process.exit(1); }
}
});
program
.command('build')
.description('compile and package code')
.action(async () => {
try {
process.env.NODE_ENV = 'production';
process.env.FES_ENV = args.mode || '';
await new Service({ await new Service({
cwd: getCwd(), cwd: getCwd(),
pkg: getPkg(process.cwd()) pkg: getPkg(process.cwd()),
fesPkg
}).run({ }).run({
name: 'build', args,
args rawArgv
}); });
} catch (e) {
console.error(chalk.red(e.message));
console.error(e.stack);
process.exit(1);
} }
}); } catch (e) {
console.error(chalk.red(e.message));
program console.error(e.stack);
.command('info') process.exit(1);
.description('print debugging information about your environment') }
.action(() => { })();
console.log(chalk.bold('\nEnvironment Info:'));
require('envinfo')
.run(
{
System: ['OS', 'CPU'],
Binaries: ['Node', 'Yarn', 'npm'],
Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
npmPackages: '/**/{typescript,*vue*,@webank/*/}',
npmGlobalPackages: ['@webank/fes']
},
{
showNotFound: true,
duplicates: true,
fullTree: true
}
)
.then(console.log);
});
// output help information on unknown commands
program
.arguments('[command]')
.action((cmd) => {
if (cmd) {
program.outputHelp();
console.log(` ${chalk.red(`Unknown command ${chalk.yellow(cmd)}.`)}`);
console.log();
suggestCommands(cmd);
process.exitCode = 1;
}
});
program.on('--help', () => {
console.log();
console.log(
` Run ${chalk.cyan(
'fes <command> --help'
)} for detailed usage of given command.`
);
console.log();
});
program.commands.forEach(c => c.on('--help', () => console.log()));
program.parse(process.argv);
if (!process.argv.slice(2).length) {
program.outputHelp();
}

View File

@ -2,6 +2,7 @@ import { chalk, yParser } from '@umijs/utils';
import { Service } from './serviceWithBuiltIn'; import { Service } from './serviceWithBuiltIn';
import getCwd from './utils/getCwd'; import getCwd from './utils/getCwd';
import getPkg from './utils/getPkg'; import getPkg from './utils/getPkg';
import fesPkg from '../package.json';
const args = yParser(process.argv.slice(2)); const args = yParser(process.argv.slice(2));
@ -27,7 +28,8 @@ function onSignal(signal, service) {
process.env.FES_ENV = args.mode || ''; process.env.FES_ENV = args.mode || '';
const service = new Service({ const service = new Service({
cwd: getCwd(), cwd: getCwd(),
pkg: getPkg(process.cwd()) pkg: getPkg(process.cwd()),
fesPkg
}); });
await service.run({ await service.run({
name: 'dev', name: 'dev',

2685
yarn.lock

File diff suppressed because it is too large Load Diff