├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .nycrc
├── .travis.yml
├── LICENSE
├── README.md
├── bin
├── omni
└── omni-door.js
├── commitlint.config.js
├── docs
├── CHANGELOG.md
├── CHANGELOG.zh-CN.md
├── DEV.md
├── DEV.zh-CN.md
├── OMNI.md
├── OMNI.zh-CN.md
├── README.zh-CN.md
└── omni-init.gif
├── mocha.opts
├── package.json
├── scripts
├── branch.sh
├── copy.sh
├── publish.sh
└── version.sh
├── src
├── @types
│ └── rollup.d.ts
├── commands
│ ├── build
│ │ ├── __test__
│ │ │ └── index.test.ts
│ │ ├── dependencies_build.ts
│ │ ├── gulp.ts
│ │ ├── index.ts
│ │ ├── rollup.ts
│ │ └── webpack.ts
│ ├── commands.ts
│ ├── dev
│ │ ├── __test__
│ │ │ └── index.test.ts
│ │ ├── index.ts
│ │ ├── open.chrome.applescript
│ │ ├── open.ts
│ │ ├── run.ts
│ │ └── server.ts
│ ├── index.ts
│ ├── initial
│ │ ├── __test__
│ │ │ └── index.test.ts
│ │ ├── index.ts
│ │ └── initial_preset.ts
│ ├── new
│ │ ├── __test__
│ │ │ └── index.test.ts
│ │ └── index.ts
│ ├── release
│ │ ├── __test__
│ │ │ └── index.test.ts
│ │ ├── branch.sh
│ │ ├── index.ts
│ │ ├── publish.sh
│ │ └── version.sh
│ ├── servers
│ │ ├── __test__
│ │ │ └── index.test.ts
│ │ ├── express-webpack.ts
│ │ ├── favicon.ico
│ │ ├── index.ts
│ │ └── koa-next.ts
│ └── start
│ │ ├── __test__
│ │ └── index.test.ts
│ │ └── index.ts
├── index.d.ts
└── utils
│ ├── __test__
│ └── index.test.ts
│ ├── index.ts
│ ├── logo.ts
│ ├── signal.ts
│ └── tackle_plugins.ts
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | bin/
2 | lib/
3 | node_modules/
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | node: true,
4 | es6: true,
5 | },
6 | extends: [
7 | 'eslint:recommended',
8 | 'plugin:@typescript-eslint/eslint-recommended',
9 | ],
10 | globals: {
11 | Atomics: 'readonly',
12 | SharedArrayBuffer: 'readonly',
13 | },
14 | parser: '@typescript-eslint/parser',
15 | parserOptions: {
16 | ecmaFeatures: {
17 | module: true,
18 | ts: true,
19 | tsx: true,
20 | },
21 | ecmaVersion: 2018,
22 | sourceType: 'module',
23 | },
24 | plugins: ['@typescript-eslint'],
25 | rules: {
26 | 'no-console': [
27 | 'error',
28 | {
29 | allow: ['warn', 'error', 'info'],
30 | },
31 | ],
32 | '@typescript-eslint/indent': ['warn', 2],
33 | quotes: ['error', 'single'],
34 | semi: ['error', 'always'],
35 | 'no-unused-vars': ['off'],
36 | 'no-useless-escape': ['off']
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .nyc_output
4 | .omni_cache
5 | coverage
6 | lib
7 |
8 | omni.config.js
9 | package-lock.json
10 | *.log
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | scripts
3 | src
4 | docs
5 |
6 | .editorconfig
7 | .eslintignore
8 | .eslintrc.js
9 | tsconfig.json
10 | *.config.js
11 | *.conf.js
12 | .gitignore
13 |
14 | test
15 | __test__
16 |
17 | _config.yml
18 | .nyc_output
19 | .travis.yml
20 | coverage
21 | .nycrc
22 | mocha.opts
23 |
24 | yarn.lock
25 | package-lock.json
26 | *.log
--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
1 | {
2 | "extension": [
3 | ".ts",
4 | ".tsx"
5 | ],
6 | "include": [
7 | "src/**/*.ts"
8 | ],
9 | "exclude": [
10 | "**/*.d.ts",
11 | "src/**/*.test.ts",
12 | "src/commands/index.ts",
13 | "src/commands/commands.ts",
14 | "lib/*"
15 | ],
16 | "reporter": [
17 | "lcovonly"
18 | ],
19 | "all": true
20 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "10"
4 | dist: trusty
5 | sudo: required
6 | addons:
7 | - chrome: stable
8 | before_install:
9 | - npm i -g codecov
10 | install:
11 | - yarn
12 | script:
13 | - npm run lint
14 | - npm run test
15 | - codecov
16 | cache:
17 | yarn: true
18 | directories:
19 | - node_modules
20 | after_success:
21 | - export CODECOV_TOKEN="00ef439d-5d49-4470-970b-ff6a47daea9b"
22 | - bash <(curl -s https://codecov.io/bash) -s coverage/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Bobby Li
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🐸 @omni-door/cli
2 |
3 | https://www.omnidoor.org
4 |
5 | The CLI Tool for set up standard frontend project.
6 |
7 | [](https://www.npmjs.com/package/@omni-door/cli)
8 | [](https://badge.fury.io/js/%40omni-door%2Fcli)
9 | [](http://nodejs.org/download/)
10 | [](https://travis-ci.com/omni-door/cli)
11 | [](https://codecov.io/gh/omni-door/cli)
12 | [](http://commitizen.github.io/cz-cli/)
13 | [](https://packagephobia.now.sh/result?p=%40omni-door%2Fcli)
14 | [](https://github.com/omni-door/cli/blob/master/LICENSE)
15 |
16 |
17 |
18 |
19 |
20 | English | [简体中文](./docs/README.zh-CN.md)
21 |
22 | [ DETAILS](./docs/OMNI.md)
23 |
24 | [CHANGELOG](./docs/CHANGELOG.md)
25 |
26 | ## install
27 | The latest LTS version of Node.js is recommended, or at least ensure node >= 10.13.0
28 |
29 | Several options to get up and running:
30 |
31 | * Clone the repo: `git@github.com:omni-door/cli.git`
32 |
33 | * Install with [npm](https://www.npmjs.com/package/@omni-door/cli): `npm install @omni-door/cli -g`
34 |
35 | * Install with [Yarn](https://yarnpkg.com/en/package/@omni-door/cli): `yarn global add @omni-door/cli`
36 |
37 | * Initial project with [npx](https://www.npmjs.com/package/@omni-door/cli): `npx @omni-door/cli init`
38 |
39 | ## omni --help
40 | ```shell
41 | Usage: index [command] [options]
42 |
43 | Options:
44 |
45 | -v, --version output the version number
46 | -h, --help output usage information
47 |
48 | Commands:
49 |
50 | init [options] [strategy] initialize your project, [strategy] could be stable(default) or latest
51 | dev [options] omni dev [-p ] [-H ] [-P ]
52 | start [options] omni start [-p ] [-H ] [-P ]
53 | new [options] [name] omni new [name] [-f | -c] [-P ]
54 | build [options] build your project according to the [omni.config.js]'s build field
55 | release [options] publish your project according to the [omni.config.js]'s release field
56 |
57 | ```
58 |
59 | ## omni init
60 |
61 | ### Initial your project by answer several questions
62 | ```shell
63 | omni init
64 | ```
65 |
66 | ### Initial your project with lastest denpendencies
67 | ```shell
68 | omni init lastest
69 | ```
70 |
71 | ### Initial your project without install dependencies
72 | ```shell
73 | omni init -n
74 | ```
75 |
76 | ### Initial your project according to some template
77 | ```shell
78 | omni init -t [projectName]
79 | ```
80 | or
81 | ```shell
82 | omni init --react_entire [projectName]
83 | ```
84 |
85 | ### options
86 | ```shell
87 | Usage: omni init [strategy] [options]
88 |
89 | initialize your project, [strategy] could be stable(default) or latest
90 |
91 | Arguments:
92 |
93 | strategy stable or latest
94 |
95 | Options:
96 | -rb, --react_basic [name] create a basic React SPA project
97 | -rs, --react_standard [name] create a standard React SPA project
98 | -re, --react_entire [name] create a most versatile React SPA project
99 | -rp, --react_pc [name] create a React SPA project based on Antd
100 | -vb, --vue_basic [name] create a basic Vue SPA project
101 | -vs, --vue_standard [name] create a standard Vue SPA project
102 | -ve, --vue_entire [name] create a most versatile Vue SPA project
103 | -rS, --react_ssr [name] create a React component library
104 | -rc, --react_components [name] create a React component library
105 | -vc, --vue_components [name] create a Vue component library
106 | -t, --toolkit [name] create a toolkit project
107 | -n, --no-install init project without install dependencies
108 | -P, --path the workpath for init the project
109 | -h, --help output usage information
110 | ```
111 |
112 | ---
113 |
114 | ## omni dev
115 |
116 | ### options
117 | ```shell
118 | Usage: omni dev [options]
119 |
120 | omni dev [-p ] [-H ] [-P ]
121 |
122 | Options:
123 | -p, --port start the dev-server according to the specified port
124 | -H, --hostname start the dev-server according to the specified hostname
125 | -P, --path the workpath for start the dev-server
126 | -h, --help output usage information
127 | ```
128 |
129 | ---
130 |
131 | ## omni start
132 |
133 | ### options
134 | ```shell
135 | Usage: omni start [options]
136 |
137 | omni start [-p ] [-H ] [-P ]
138 |
139 | Options:
140 | -p, --port start the prod-server according to the specified port
141 | -H, --hostname start the prod-server according to the specified hostname
142 | -P, --path the workpath for start the prod-server
143 | -h, --help output usage information
144 | ```
145 |
146 | ---
147 |
148 | ## omni new
149 |
150 | ### options
151 | ```shell
152 | Usage: omni new [name] [options]
153 |
154 | omni new [name] [-f | -c] [-P ]
155 |
156 | Arguments:
157 |
158 | name optional! The name of component.
159 |
160 | Options:
161 | -f, --function create a React-Function-Component
162 | -c, --class create a React-Class-Component
163 | -r, --render create a Vue-Render-Function
164 | -s, --single create a Vue-Single-File-Component
165 | -P, --path the workpath for create component
166 | -h, --help display help for command
167 | ```
168 |
169 | ---
170 |
171 | ## omni build
172 |
173 | ### options
174 | ```shell
175 | Usage: omni build [options]
176 |
177 | build your project according to the [omni.config.js]'s build field
178 |
179 | Options:
180 | -c, --config specify the path of config file
181 | -n, --no-verify bypass all pre-check before building
182 | -P, --path the workpath for build project
183 | -h, --help output usage information
184 | ```
185 |
186 | ---
187 |
188 | ## omni release
189 |
190 | ### options
191 | ```shell
192 | Usage: omni release [options]
193 |
194 | publish your project according to the [omni.config.js]'s release field
195 |
196 | Options:
197 | -a, --automatic automatic iteration version
198 | -i, --ignore ignore automatic iteration version
199 | -m, --manual manual iteration version
200 | -t, --tag the tag will add to npm-package
201 | -n, --no-verify bypass unit-test eslint and stylelint check
202 | -P, --path the workpath for release project
203 | -h, --help output usage information
204 | ```
205 |
206 | ---
207 |
208 | ## API Docs
209 | click [here](./docs/DEV.md)
210 |
211 | ## License
212 |
213 | Copyright (c) 2019 [Bobby.li](https://github.com/BobbyLH)
214 |
215 | Released under the MIT License
--------------------------------------------------------------------------------
/bin/omni:
--------------------------------------------------------------------------------
1 | ../lib/node_modules/omni-door/bin/omni-door.js
--------------------------------------------------------------------------------
/bin/omni-door.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | require('../lib/commands/index');
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | const Configuration = {
2 | formatter: '@commitlint/format',
3 | /*
4 | * Any rules defined here will override rules from @commitlint/config-conventional
5 | */
6 | rules: {
7 | 'type-enum': [2, 'always', [
8 | '[OMNI-DOOR]',
9 | 'feat',
10 | 'feature',
11 | 'fix',
12 | 'hotfix',
13 | 'docs',
14 | 'style',
15 | 'refactor',
16 | 'test',
17 | 'revert',
18 | 'update',
19 | 'upgrade',
20 | 'modify',
21 | 'merge',
22 | 'chore',
23 | 'optimization',
24 | 'build',
25 | 'perf'
26 | ]]
27 | },
28 | /*
29 | * Functions that return true if commitlint should ignore the given message.
30 | */
31 | ignores: [
32 | commit => {
33 | const regExp = /^Merge branch.+/;
34 | return regExp.test(commit);
35 | }
36 | ],
37 | /*
38 | * Whether commitlint uses the default ignore rules.
39 | */
40 | defaultIgnores: true
41 | };
42 |
43 | module.exports = Configuration;
--------------------------------------------------------------------------------
/docs/CHANGELOG.zh-CN.md:
--------------------------------------------------------------------------------
1 | # 变更日志
2 |
3 | [English](./CHANGELOG.md) | 简体中文
4 |
5 | # v3
6 | ## v3.1.x
7 | ### v3.1.1
8 | 1. 「fix」`omni build` rollup 配置
9 |
10 | ### v3.1.0
11 | 1. 「update」`omni build` rollup 移除旧的插件支持,并升级配置
12 |
13 | 2. 「update」`omni build` rollup 的 configuration 使用函数作为参数
14 |
15 | ## v3.0.x
16 | ### v3.0.3
17 | 1. 「update」`omni init` husky 初始化
18 |
19 | ### v3.0.2
20 | 1. 「update」`omni build` tsc esm 构建 module 设置为 esnext
21 |
22 | ### v3.0.1
23 | 1. 「update」`omni init` 在 husky init 之前添加 `git init` 命令
24 |
25 | ### v3.0.0
26 | 1. 「update」`omni init` react 组件库移除 docz, styleguidist, bisheng 等demo文档的支持
27 |
28 | 2. 「update」`omni init` react-ssr 移除 koa+next 作为SSR服务的支持
29 |
30 | 3. 「update」支持 react@19, next@15 以及其他依赖的升级
31 |
32 | ---
33 |
34 | # v2
35 | ## v2.9.x
36 | ### v2.9.17
37 | 1. 「update」`omni release` 没有设置 npm 仓库地址也能进行 tag 选择
38 |
39 | ### v2.9.16
40 | 1. 「chore」`omni release` rc 标签名称
41 |
42 | ### v2.9.15
43 | 1. 「fix」`omni build` rollup ESM模块构建参数
44 |
45 | ### v2.9.14
46 | 1. 「fix」`omni build` rollup 多入口文件构建问题
47 |
48 | ### v2.9.13
49 | 1. 「update」`omni dev` webpack 配置支持函数的形式
50 |
51 | ### v2.9.12
52 | 1. 「update」`omni release` 当 `npm` 字段未设置的时候,无法更改版本号
53 |
54 | ### v2.9.11
55 | 1. 「update」`omni build` rollup 配置
56 |
57 | ### v2.9.10
58 | 1. 「fix」`omni dev` dumi 开发服务命令
59 |
60 | ### v2.9.9
61 | 1. 「fix」`omni build` vue 文件没有生成声明文件
62 | 2. 「fix」`omni build` `.vue` 后缀转换成 `.js`
63 |
64 | ### v2.9.8
65 | 1. 「update」`omni build` 用 ts-patch 替代 ttypescript
66 |
67 | ### v2.9.7
68 | 1. 「update」`omni build` gulp 排除 `*.{demo,test,stories}.{vue,ts,tsx,js,jsx}` 文件构建
69 |
70 | ### v2.9.6
71 | 1. 「feat」`omni new` 指定模版的版本
72 |
73 | ### v2.9.5
74 | 1. 「feat」`omni init` 指定模版的版本
75 |
76 | ### v2.9.4
77 | 1. 「chore」`omni init` 添加 init 命令日志
78 |
79 | ### v2.9.3
80 | 1. 「update」`omni dev` storybook 开发命令变更
81 |
82 | ### v2.9.2
83 | 1. 「update」`omni build` gulp-sass 手动引入 sass
84 |
85 | ### v2.9.1
86 | 1. 「update」`omni init` 添加 deprecated 和 recommend 提示
87 |
88 |
89 | ### v2.9.0
90 | 1. 「chore」升级 express 和 node-sass
91 |
92 | ## v2.8.x
93 | ### v2.8.2
94 | 1. 「fix」`omni start` 移除最新版本提示避免网络阻塞
95 |
96 | ### v2.8.1
97 | 1. 「optimization」node 进程异常信号监听
98 |
99 | ### v2.8.0
100 | 1. 「feat」中间件支持 http 方法
101 |
102 | 2. 「chore」升级 @omni-door/utils
103 |
104 | ## v2.7.x
105 | ### v2.7.5
106 | 1. 「fix」`koa-next` 服务中间件
107 |
108 | ### v2.7.4
109 | 1. 「update」移除 `handleKoaApp` 的支持
110 |
111 | 2. 「update」新增 `cors` 的支持
112 |
113 | ### v2.7.3
114 | 1. 「update」`ssr-react` 下的 `koa-next` 服务,支持通过 `handleKoaApp` 回调操作KoaApp
115 |
116 | ### v2.7.2
117 | 1. 「fix」`omni start` 缺失 middleware 和 https 参数
118 |
119 | ### v2.7.1
120 | 1. 「update」`omni init` ssr-react 移除样式选择
121 |
122 | ### v2.7.0
123 | 1. 「optimization」`omni init` 和 `omni release` REPL 交互提升
124 |
125 | 2. 「upgrade」升级 @omni-door/utils
126 |
127 | 3. 「feat」支持 React@18
128 |
129 | ## v2.6.x
130 | ### v2.6.4
131 | 1. 「upgrade」inquirer | mkcert
132 |
133 | ### v2.6.2
134 | 1. 「fix」`omni build` 未能支持 `spa-react-pc` 项目
135 |
136 | ### v2.6.1
137 | 1. 「fix」`omni new` 和 `omni build` 未能支持 `spa-react-pc` 项目
138 |
139 | ### v2.6.0
140 | 1. 「feat」支持 `spa-react-pc` 项目
141 |
142 | ## v2.5.x
143 | ### v2.5.8
144 | 1. 「optimization」`omni build` 和 `omni release` log 输出优化
145 |
146 | ### v2.5.7
147 | 1. 「update」升级 *shelljs* 解决循环依赖的警告
148 |
149 | ### v2.5.6
150 | 1. 「fix」 `omni build` gulp 打包 *.vue* 文件后缀名转换
151 |
152 | 2. 「update」 `omni build` dependencies_build 依赖版本更新
153 |
154 | 3. 「optimization」 `omni release` 从版本中自动获取tag时,`rc` 会强制转换成 `latest`
155 |
156 | ### v2.5.5
157 | 1. 「optimization」 `omni build` gulp 打包自定义暴露完整配置项
158 |
159 | ### v2.5.4
160 | 1. 「optimization」 `omni build` gulp 打包支持自定义配置
161 |
162 | ### v2.5.3
163 | 1. 「fix」 `omni build` gulp 打包替换 vue-SFC 文件路径
164 |
165 | ### v2.5.2
166 | 1. 「optimization」 `omni build` gulp 打包编译 vue-SFC 文件
167 |
168 | ### v2.5.1
169 | 1. 「optimization」 `omni build` gulp 打包支持 vue SFC
170 |
171 | 2. 「feat」 `omni new` *component-vue* 项目支持 SFC 和 Render-Function
172 |
173 | ### v2.5.0
174 | 1. 「feat」 `omni build` gulp 打包支持 css 路径拼接并替换 css-minifier
175 |
176 | ## v2.4.x
177 | ### v2.4.9
178 | 1. 「update」 `omni release` 将插件的处理程序置于 git 和 npm 的操作之前
179 |
180 | ### v2.4.8
181 | 1. 「optimization」 `omni release` tag 和 plugin handler
182 |
183 | 2. 「upgrade」升级 @omni-door/utils
184 |
185 | ### v2.4.7
186 | 1. 「optimization」 `omni init` 在 *component-react* 项目中,选择 *docz* 作为 demo 框架时,检查 node 版本是否 `>= 12`
187 |
188 | ### v2.4.6
189 | 1. 「chore」 `omni new` 名称提示
190 |
191 | 2. 「optimization」 `omni dev` 打开浏览器之前先确保端口已经被占用
192 |
193 | ### v2.4.5
194 | 1. 「optimization」 `omni release` 操作git前先检查状态
195 |
196 | ### v2.4.4
197 | 1. 「update」 `omni dev` 打开浏览器延时
198 |
199 | ### v2.4.3
200 | 1. 「optimization」 `omni release` npm publish 支持两步验证(OTP)
201 |
202 | ### v2.4.2
203 | 1. 「optimization」 `omni build` rollup 自定义配置文件的参数传递
204 |
205 | ### v2.4.1
206 | 1. 「fix」 `omni init` 覆写的工作路径
207 |
208 | ### v2.4.0
209 | 1. 「optimization」 更新 cli 的提示
210 |
211 | 2. 「feat」支持 `component-vue` 项目
212 |
213 | ## v2.3.x
214 |
215 | ### v2.3.11
216 | 1. 「fix」 匹配版本号的正则表达式
217 |
218 | ### v2.3.10
219 | 1. 「optimization」 日志优化
220 |
221 | 2. 「fix」 `omni build` 无法删除在工作路径之外的文件或文件夹
222 |
223 | ### v2.3.9
224 | 1. 「optimization」 日志优化
225 |
226 | ### v2.3.8
227 | 1. 「optimization」 日志优化
228 |
229 | ### v2.3.7
230 | 1. 「optimization」 `omni release` 迭代优化
231 |
232 | ### v2.3.6
233 | 1. 「fix」 `omni build` rollup 中的 typescript插件,影响输出结果的问题
234 |
235 | ### v2.3.5
236 | 1. 「fix」 `omni release` 自动迭代版本号的 tag 不正确
237 |
238 | ### v2.3.4
239 | 1. 「optimization」 `omni release` 自动迭代版本号策略顺序优化
240 |
241 | ### v2.3.3
242 | 1. 「optimization」 `omni init` 单元测试 `spa-vue` 项目默认不选
243 |
244 | 2. 「optimization」 `omni release` 自动迭代版本号优化
245 |
246 | ### v2.3.2
247 | 1. 「optimization」 `omni init` 固定 `@omni-door/cli` 的中版本号
248 |
249 | ### v2.3.1
250 | 1. 「feat」支持 `spa-vue` 项目
251 |
252 | ### v2.3.0
253 | 1. 「optimization」`omni init`、`omni new` 新增脚手架 latest 版本的更新提示
254 |
255 | 2. 「upgrade」升级 @omni-door/utils,并替换 API
256 |
257 | ## v2.2.x
258 | ### v2.2.13
259 | 1. 「fix」`omni dev` favicon.icon 不存在的导致开发服务崩溃
260 |
261 | ### v2.2.12
262 | 1. 「upgrade」升级 @omni-door/utils
263 |
264 | ### v2.2.11
265 | 1. 「fix」`omni dev` 更改 `http-proxy-middleware` 的 API
266 |
267 | ### v2.2.10
268 | 1. 「fix」`omni init` toolkit 项目无法创建
269 |
270 | ### v2.2.9
271 | 1. 「fix」`omni dev` spa 项目 dev-server 通配符路由缺失
272 |
273 | ### v2.2.8
274 | 1. 「optimization」`omni init`、`omni new` 脚手架版本号和模板版本号同步
275 |
276 | 2. 「fix」`omni init` 包管理工具安装时的异常处理
277 |
278 | ### v2.2.7
279 | 1. 「optimization」`omni init` 新增对 REPL(命令行运行的交互式界面) 方式交互的包管理器校验
280 |
281 | 2. 「fix」`omni init` 当未选择样式文件时,正确的展示后续内容
282 |
283 | ### v2.2.6
284 | 1. 「optimization」npm-package latest 版本校验不阻塞程序运行
285 |
286 | ### v2.2.5
287 | 1. 「fix」`omni init` 对 `layout` 的值做转换
288 |
289 | ### v2.2.4
290 | 1. 「feat」`omni init` spa-react 项目支持 `layout` 选项
291 |
292 | ### v2.2.3
293 | 1. 「feat」spa-react 项目支持 webpack5
294 |
295 | 2. 「feat」spa-react 项目开发服务支持自定义 favicon
296 |
297 | ### v2.2.2
298 | 1. 「feat」`omni init` 模板的 pkj-tool 默认使用 pnpm
299 |
300 | ### v2.2.1
301 | 1. 「fix」`omni init` 项目名校验的问题
302 |
303 | ### v2.2.0
304 | 1. 「feat」`omni init` 安装工具新增 pnpm 选项,并移除 cnpm
305 |
306 | 2. 「feat」`omni init` 新增项目名规范校验
307 |
308 | 3. 「feat」新增 *最新版本 cli 安装提示*
309 |
310 | 4. 「feat」新增 *错误命令意图推测*
311 |
312 | ## v2.1.x
313 | ### v2.1.6
314 | 1. 「fix」`omni release` 自动构建参数缺失的问题.
315 |
316 | ### v2.1.5
317 | 1. 「feat」`omni release` 支持 自动构建(autoBuild).
318 |
319 | ### v2.1.4
320 | 1. 「fix」`omni build` toolkit 项目错误的从 'undefined' 或 'null' 中进行解构.
321 |
322 | ### v2.1.3
323 | 1. 「update」`omni build` toolkit 项目构建产物过滤掉忽略的文件夹
324 |
325 | ### v2.1.2
326 | 1. 「update」`omni build` 升级 toolkit 项目的 rollup 配置
327 |
328 | ### v2.1.1
329 | 1. 「fix」`omni release` 手动迭代版本号无法正确匹配
330 |
331 | ### v2.1.0
332 | 1. 「optimization」`omni build` 用 tsc 或 gulp 编译的项目,默认支持 alias
333 |
334 | 2. 「fix」`omni build` 自动安装缺少的构建依赖不全的问题
335 |
336 | ## v2.0.x
337 | ### v2.0.17
338 | 1. 「fix」`omni build` 组件项目gulp配置文件bug
339 |
340 | ### v2.0.16
341 | 1. 「fix」`omni build` 组件项目不兼容同时存在 scss less 文件的问题
342 |
343 | ### v2.0.15
344 | 1. 「optimization」`omni init` 模板的版本自动对齐脚手架的版本
345 |
346 | ### v2.0.14
347 | 1. 「optimization」优化 `omni init/release` 的日志输出
348 |
349 | ### v2.0.13
350 | 1. 「optimization」调用`omni *(commands)` 的日志输出
351 |
352 | ### v2.0.12
353 | 1. 「fix」`omni release` 因为 cache 导致获取当前版本号不正确的问题
354 |
355 | ### v2.0.11
356 | 1. 「fix」`omni release` 在命令行中自定义版本号,自动设置 `tag` 的优先级问题,如在 package.json 中原来的版本号为 `0.0.19`,而后用命令行迭代 `omni release -m 0.0.20-alpha.1`,此时自动判断 `tag` 应为 `alpha` 而非 `latest`
357 |
358 | 2. 「optimization」`omni release` publish 到 npm 仓库的日志优化
359 |
360 | ### v2.0.10
361 | 1. 「optimization」 `release` 新增 `autoTag` 字段,设置为 `true` 时,发布到npm仓库时会自动根据当前版本号设定tag
362 |
363 | ### v2.0.9
364 | 1. 「optimization」`omni release` 自定义版本号会自动根据含带的字母确定默认的 tag
365 |
366 | ### v2.0.8
367 | 1. 「fix」`omni release` 自定义版本号因正则匹配失效的问题
368 |
369 | 2. 「optimization」`omni release` 默认 tag 取自现版本号的字母后缀
370 |
371 | ### v2.0.7
372 | 1. 「fix」`omni build` rollup.config.js namedExports 补全 react 和 react-dom 的 API
373 |
374 | ### v2.0.6
375 | 1. 「fix」`omni start` 依赖引用的问题
376 |
377 | ### v2.0.5
378 | 1. 「fix」`omni dev` 对于 react-ssr 项目的错误判断
379 |
380 | ### v2.0.4
381 | 1. 「feat」`omni *` 所有命令均支持 `-P ` 选项用于自定义工作路径
382 |
383 | ### v2.0.3
384 | 1. 「update」[newTpl、initial] 支持 `tplPkjTag` 选项
385 |
386 | ### v2.0.2
387 | 1. 「fix」修复 express typescript 的问题,[点击详见issue](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/47339#issuecomment-691800846)
388 |
389 | 2. 「docs」变更日志优化
390 |
391 | ### v2.0.1
392 | 1. 「fix」`omni dev` 修复 ssr-react 项目无法启动的问题
393 |
394 | ### v2.0.0
395 | 1. 「feat」`omni init` 支持 ssr-react 项目类型
396 |
397 | 2. 「optimization」`omni init` 优化
398 |
399 | 3. 「feat」新增 `omni start` 命令
400 |
401 | ---
402 |
403 | # v1
404 | ## v1.4.x
405 | ### v1.4.4
406 | 1. 「optimization」 `omni init` 固定 template 的版本号
407 |
408 | ### v1.4.3
409 | 1. 「feat」 `omni init` 支持 `tplPkjTag` 选项
410 |
411 | ### v1.4.2
412 | 1. 「perf」`omni release` 优化了 require package.json 的过程
413 |
414 | 2. 「chore」升级 @omni-door/utils
415 |
416 | ### v1.4.1
417 | 1. 「chore」固定依赖的版本号
418 |
419 | 2. 「fix」`omni build` typescript 被禁用的情况处理
420 |
421 | ### v1.4.0
422 | 1. 「feat」`omni dev` 开发服务支持 `https` 的协议
423 |
424 | ## v1.3.x
425 | ### v1.3.9
426 | 1. 「update」`omni dev` 开发服务 host 默认更变为 `0.0.0.0`
427 |
428 | ### v1.3.8
429 | 1. 「update」`omni build` 的 reserve.assets 字段的类型更变为: `(string | { srcPath: string; relativePath?: string; })[]`
430 |
431 | ### v1.3.7
432 | 1. 「docs」`omni release` 文本调整
433 |
434 | 2. 「optimization」`omni build` gulp 打包优化
435 |
436 | ### v1.3.6
437 | 1. 「fix」升级 @omni-door/utils,解决 logTime 前缀不正确的问题
438 |
439 | ### v1.3.5
440 | 1. 「feat」`omni release` 新增 -a / --automatic 选项,并支持REPL(read-eval-print loop)
441 |
442 | ### v1.3.4
443 | 1. 「feat」`omni dev` storybook 启动开发服务添加 --quiet 选项
444 |
445 | 2. 「feat」`omni dev` 支持 host 配置
446 |
447 | ### v1.3.3
448 | 1. 「chore」`omni dev` 整合 toolkit 和 component-library 项目的开发服务
449 |
450 | ### v1.3.2
451 | 1. 「chore」`omni build` 固定 gulp 构建的 cwd 路径 同时支持 component-libaray 输出全量的css
452 |
453 | ### v1.3.1
454 | 1. 「feat」`omni build` 支持使用 gulp 来打包组件库项目
455 |
456 | 2. 「chore」升级 @omni-door/utils
457 |
458 | 3. 「fix」`omni new [name]` 的 name 可能是 `undefined` 的问题
459 |
460 | ### v1.3.0
461 | 1. 「update」`omni dev -p - port` 改为必填
462 |
463 | 2. 「update」`omni new [name] [option]` 的 name 改为选填,并支持REPL(read-eval-print loop)
464 |
465 | ## v1.2.x
466 | ### v1.2.38
467 | 1. 「fix」修复项目 package.json 可能不存在的问题
468 |
469 | 2. 「feat」`omni init` 新增支持 `pkjFieldName` 字段
470 |
471 | ### v1.2.37
472 | 1. 「feat」`omni build` 新增支持 `pkjFieldName` 字段
473 |
474 | ### v1.2.36
475 | 1. 「chore」升级 @omni-door/utils
476 |
477 | 2. 「feat」在 package.json 中支持 omni 字段,使之能够自定义配置文件(omni.config.js)的路径
478 |
479 | ### v1.2.35
480 | 1. 「feat」`omni new` 新增对传入的模块名做校验:
481 |
482 | - 模块名大于等于2个字符;
483 |
484 | - 第一个字符只能由 下划线_ 或 大小写字母 组成;
485 |
486 | - 后续字符只能由 数字、下划线_、大小写字母 组成!
487 |
488 | ### v1.2.34
489 | 1. 「feat」`omni initial` 支持 custom initPath
490 |
491 | ### v1.2.33
492 | 1. 「fix」`omni release` 删除会导致版本号不正确的缓存
493 |
494 | ### v1.2.32
495 | 1. 「fix」修复构建/发布完成后未退出程序的问题
496 |
497 | ### v1.2.31
498 | 1. 「fix」修复 `omni release` linux环境下版本无法迭代的问题
499 |
500 | ### v1.2.30
501 | 1. 「feat」`omni dev` proxy 支持传入 `function` 类型
502 |
503 | ### v1.2.29
504 | 1. 「feat」`omni dev` middleware 支持传入 `function` 类型
505 |
506 | 2. 「chore」升级 @omni-door/utils
507 |
508 | ### v1.2.28
509 | 1. 「update」 `dev` 字段新增 `devMiddlewareOptions` 属性,对应 `webpack-dev-middleware` [Options](https://github.com/webpack/webpack-dev-middleware#options)
510 |
511 | ### v1.2.27
512 | 1. 「chore」依赖升级 commander@5.1.0
513 |
514 | ### v1.2.26
515 | 1. 「fix」commandar 自动输出 help 信息的逻辑
516 |
517 | ### v1.2.25
518 | 1. 「chore」依赖升级 commander@4.1.0
519 |
520 | 2. 「chore」`omni init` 提升参数 `tplPkjParams` 的权重
521 |
522 | ### v1.2.24
523 | 1. 「fix」依赖固定版本号
524 |
525 | ### v1.2.23
526 | 1. 「fix」修复重名文件夹的检测的问题
527 |
528 | 2. 「feat」`omni init` 项目重名支持重新输入,最大10次
529 |
530 | ### v1.2.22
531 | 1. 「feat」`omni init` 初始化时新增对重名文件夹的检测和覆盖确认提示
532 |
533 | ### v1.2.21
534 | 1. 「fix」初始化时选择 no-install 不会出现安装工具的选择
535 |
536 | ### v1.2.20
537 | 1. 「feat」`omni init` 新增初始化时不安装依赖的选项
538 |
539 | ### v1.2.19
540 | 1. 「chore」升级 @omni-door/utils
541 |
542 | 2. 「feat」plugin-new 回调参数新增 `模板来源(tplSource)` 字段
543 |
544 | 3. 「feat」plugin-build 回调参数新增 `自定义的构建配置(buildConfig)` 字段
545 |
546 | 4. 「feat」plugin-release 回调参数新增 `版本迭代策略(versionIterTactic)` 字段
547 |
548 | ### v1.2.18
549 | 1. 「fix」修复 logo 未被正确替换的问题
550 |
551 | ### v1.2.17
552 | 1. 「feat」`omni new` 移除组件名自动大写第一个字母的逻辑
553 |
554 | 2. 「chore」`omni build` toolkit 打包模板修改
555 |
556 | 3. 「fix」`omni release` 修复commitlint遗漏verify参数的问题
557 |
558 | ### v1.2.16
559 | 1. 「chore」升级 @omni-door/utils
560 |
561 | 2. 「chore」移除错误输出信息中多余的 `JSON.stringify`
562 |
563 | 3. 「update」为 `process` 绑定 `'SIGINT', 'SIGQUIT', 'SIGTERM'` 事件,监听退出程序
564 |
565 | 4. 「fix」`omni dev/build` 修复 `require` 无法获取工作路径的依赖的问题
566 |
567 | ### v1.2.15
568 | 1. 「update」优化异常操作处理
569 |
570 | 2. 「chore」@omni-door/utils 替换 @omni-door/tpl-utils
571 |
572 | 3. 「feat」`omni dev` 采用 create-react-app [复用已打开浏览器的tab策略](https://github.com/facebook/create-react-app/blob/32eebfeb7f5cdf93a790318eb76b81bb2927458e/packages/react-dev-utils/openChrome.applescript)
573 |
574 | ### v1.2.14
575 | 1. 「update」`omni build/release` 支持 prettier 飞行检查
576 |
577 | 2. 「update」`omni build` 新增对于自定义配置文件是否存在的校验
578 |
579 | 3. 「feat」`omni init` component-react项目新增 styleguidist Demo框架的选择
580 |
581 | ### v1.2.13
582 | 1. 「fix」`omni build` 当 catch[try/catch] 到错误时,停止 loading 状态
583 |
584 | 2. 「update」`omni init` 集成 prettier
585 |
586 | ### v1.2.12
587 | 1. 「feat」`omni build` 支持自定义配置文件路径
588 |
589 | 2. 「fix」`omni build` 修复输出资源是否存在的校验的bug
590 |
591 | ### v1.2.11
592 | 1. 「feat」`omni build` 支持 "hash"、"chunkhash"、"contenthash"
593 |
594 | 2. 「update」升级依赖 @omni-door/tpl-utils
595 |
596 | ### v1.2.10
597 | 1. 「feat」`omni initial` before 和 after 支持异步执行
598 |
599 | 2. 「feat」`omni new` 支持 before 和 after 回调
600 |
601 | ### v1.2.9
602 | 1. 「feat」`omni dev` 新增 Signals listener
603 |
604 | 2. 「update」升级依赖 @omni-door/tpl-utils
605 |
606 | 3. 「feat」`omni dev` 修复 history API 路由不正确的问题
607 |
608 | ### v1.2.8
609 | 1. 「update」插件新增 `options` 参数,并更新 type 定义
610 |
611 | ### v1.2.7
612 | 1. 「update」`omni release` 发布过程的异常处理逻辑更改
613 |
614 | 2. 「update」`omni release` 发布到npm仓库显示指定registry
615 |
616 | 3. 「update」`omni release` 发布到git仓库避免修改origin
617 |
618 | 4. 「feat」`omni release` 新增 tag 选项
619 |
620 | 5. 「update」插件运行新增try/catch异常处理
621 |
622 | ### v1.2.6
623 | 1. 「feat」[initial] 支持 `tplPkjParams` 选项
624 |
625 | 2. 「update」`omni build` 移除自动发布的时间日志
626 |
627 | ### v1.2.5
628 | 1. 「fix」`omni release` 修复版本检测的问题
629 |
630 | ### v1.2.4
631 | 1. 「fix」`omni release` 修复 分支检测和npm发布log前缀不正确的问题
632 |
633 | 2. 「update」升级 @omni-door/tpl-utils 依赖
634 |
635 | ### v1.2.3
636 | 1. 「docs」`omni release` 汉化翻译
637 |
638 | 2. 「update」升级 @omni-door/tpl-utils 依赖
639 |
640 | 3. 「feat」`omni initial/build/release)` 新增耗时日志输出
641 |
642 | ### v1.2.2
643 | 1. 「fix」`omni build` rollup 打包新增未正确发现入口文件的提示
644 |
645 | 2. 「feat」[newTpl、initial] 支持 `tplPkj` 选项
646 |
647 | 3. 「update」`omni new` 恢复 plugin 的支持
648 |
649 | ### v1.2.1
650 | 1. 「update」拆分模板
651 | - @omni-door/tpl-spa-react
652 |
653 | - @omni-door/tpl-toolkit
654 |
655 | - @omni-door/tpl-component-react
656 |
657 | ### v1.2.0
658 | 1. 「update」命名规范
659 |
660 | ## v1.1.x
661 | ### v1.1.3
662 | 1. 「update」 修复无法由于循环引用导致webpack配置文件无法获取正确值的问题
663 |
664 | 2. 「update」 新增 html-webpack-plugin
665 |
666 | 3. 「update」 将css、less、scss等样式文件的处理迁移至此
667 |
668 | ### v1.1.2
669 | 1. 「update」 build 新增 hash 字段控制打包资源名是否添加哈希
670 |
671 | 2. 「docs」新增 omni.config.js 详解文档
672 |
673 | ### v1.1.1
674 | 1. 「update」 修复函数的调用栈在转化成字符串后丢失,无法正确获取到变量值的问题
675 |
676 | ### v1.1.0
677 | 1. 「update」 新增 resolutions 字段,解决 (依赖重复导致TS报错)[https://stackoverflow.com/questions/52399839/duplicate-identifier-librarymanagedattributes] 的问题
678 |
679 | 2. 「update」`onmi init` stable 策略版本更新
680 |
681 | 3. 「update」`onmi init` component-react 和 toolkit 默认开启单元测试
682 |
683 | ## v1.0.x
684 | ### v1.0.10
685 | 1. 「fix」[node-version-check] 修复node版本检测问题
686 |
687 | 2. 「feat」`onmi init` 动态更新初始化总步数
688 |
689 | 3. 「fix」`onmi build` 修复错误日志丢失的问题
690 |
691 | 4. 「fix」`onmi release` 修复错误日志丢失的问题
692 |
693 | ### v1.0.9
694 | 1. 「update」 新增 stylelint 检测启动参数 {--allow-empty-input}
695 |
696 | 2. 「update」 新增 jest 单元测试启动参数 {--passWithNoTests}
697 |
698 | 3. 「update」 移除 allowJs、experimentalDecorators 的注释,更新 {exclude} 字段
699 |
700 | 4. 「update」 新增 webpack-bundle-analyzer 插件
701 |
702 | 5. 「update」<.npmignore> 生成基于项目类型
703 |
704 | 6. 「update」`onmi init` 支持样式多选
705 |
706 | ### v1.0.8
707 | 1. 「fix」`omni init` 修复自定义Logo显示不正确问题
708 |
709 | 2. 「chore」`omni init` 支持移除内置的依赖项
710 |
711 | ### v1.0.7
712 | 1. 「update」 模板新增对项目类型判断
713 |
714 | 2. 「update」 注释变更
715 |
716 | 3. 「update」 优化生产环境打包
717 |
718 | 4. 「update」[dev-server] 新增运行时错误捕获
719 |
720 | 5. 「fix」`omni new` 修复无法识别参数的问题
721 |
722 | ### v1.0.6
723 | 1. 「feat」新增对 node 版本做检测,要求 node >= 10.13.0
724 |
725 | ### v1.0.5
726 | 1. 「update」, 新增 `dev.middleware` 字段
727 |
728 | 2. 「update」, 新增 `dev.logLevel` 字段
729 |
730 | 3. 「update」, `dev.webpack_config` 更变为 `dev.webpack`
731 |
732 | ### v1.0.4
733 | 1. 「feat」[dev-server] 启动进程优化
734 |
735 | 2. 「feat」`omni init` 更改 `omni init -s` 和 `omni init --simple` 为 `omni init -b` 和 `omni init -basic`
736 |
737 | ### v1.0.3
738 | 1. 「optimization」`omni build` 自动发布优化,摆脱依赖npm script,直接调用release方法
739 |
740 | ### v1.0.2
741 | 1. 「feat」toolkit 项目支持使用 tsc 打包
742 |
743 | ### v1.0.1
744 | 1. 「chore」优化了log日志的输出
745 |
746 | ### v1.0.0
747 | 1. 「feat」新增 `omni dev` 命令以支持基于 Express + webpack-dev-server 的开发服务
748 |
749 | ---
750 |
751 | # v0
752 | ## v0.2.x
753 | 1. 可用版本,请使用最新版本
754 |
755 | ## v0.1.x
756 | 1. 可用版本,请使用最新版本
757 |
758 | ## v0.0.x
759 | 1. 非正式版本,请使用最新版本
760 |
761 | ---
762 |
763 | **标签的含义**:
764 | - 「xxx」 - 类型
765 |
766 | - \ - 模板
767 |
768 | - [xxx] - 行为、特性、功能
--------------------------------------------------------------------------------
/docs/DEV.md:
--------------------------------------------------------------------------------
1 | # Docs
2 |
3 | English | [简体中文](./DEV.zh-CN.md)
4 |
5 | **@omni-door/cli** provides the ability of secondary development, which is implemented through plug-in or import form。
6 |
7 | ---
8 |
9 | ## Plugin
10 | The Plugin provides the third-party developers with the ability to perform multiple tasks in each lifecycle of the project. Please make sure that the Plugin writing meets the type definition of `type OmniPlugin`.
11 |
12 | ### Write a plugin for gzip when execution release stage
13 |
14 | ```js
15 | import pack from 'pack'; // pseudo code for gzip
16 |
17 | export default function (config, options) {
18 | return {
19 | name: '@scope/my-release-plugin',
20 | stage: 'release',
21 | handler: config => new Promise((resolve, reject) => {
22 | const { build } = config;
23 | const srcPath = build.outDir;
24 | const destPath = path.resolve(process.cwd(), 'dist.zip');
25 | // gzip
26 | pack(srcPath, destPath, function (res) {
27 | if (res === 'success') {
28 | return resolve();
29 | }
30 | return reject();
31 | });
32 | })
33 | });
34 | }
35 | ```
36 |
37 | ### Type of plugin
38 | ```ts
39 | type PLUGIN_STAGE = 'new' | 'build' | 'release';
40 |
41 | interface OmniPlugin {
42 | name: string;
43 | stage: T;
44 | handler: PluginHandler;
45 | }
46 |
47 | interface PluginHandler {
48 | (
49 | config: config: Omit,
50 | options?: T extends 'new' ? OptionTemplate : T extends 'build' ? OptionBuild : OptionRelease
51 | ): Promise;
52 | }
53 |
54 | // stage of "new"
55 | type OptionTemplate = {
56 | componentName: string;
57 | componentType: 'function' | 'class';
58 | tplSource: string;
59 | };
60 |
61 | // stage of "build"
62 | type OptionBuild = {
63 | verify?: boolean;
64 | buildConfig?: string;
65 | };
66 |
67 | // stage of "release"
68 | type OptionRelease = {
69 | version: string;
70 | versionIterTactic: 'ignore' | 'manual' | 'auto';
71 | verify?: boolean;
72 | tag?: string;
73 | };
74 | ```
75 |
76 | ### Type of OmniConfig
77 | ```ts
78 | import type { Configuration } from 'webpack';
79 | import type { Config } from 'http-proxy-middleware';
80 | import type { Options as DevMiddlewareOptions } from 'webpack-dev-middleware';
81 | import type { Request, Response, NextFunction } from 'express';
82 | import type * as KoaApp from 'koa';
83 |
84 | type ANY_OBJECT = { [propName: string]: any };
85 | type ServerType = 'storybook' | 'default';
86 | type BUILD = 'webpack' | 'rollup' | 'gulp' | 'tsc' | 'next' | '';
87 | type NPM = 'npm' | 'yarn' | 'pnpm';
88 | type PROJECT_TYPE = 'spa-react' | 'spa-react-pc' | 'spa-vue' | 'ssr-react' | 'component-react' | 'component-vue' | 'toolkit';
89 | type STYLE = 'less' | 'scss' | 'css' | 'all' | '';
90 | type SSR_SERVER = 'next-app' | 'next-pages' | 'nuxt' | '';
91 | type Method = 'get' | 'GET' | 'post' | 'POST' | 'put' | 'PUT' | 'del' | 'DEL';
92 |
93 | type OmniServer = {
94 | port?: number;
95 | host?: string;
96 | https?: boolean | { key: string; cert: string; };
97 | CA?: {
98 | organization?: string;
99 | countryCode?: string;
100 | state?: string;
101 | locality?: string;
102 | validityDays?: number;
103 | };
104 | proxy?: {
105 | route: PathParams;
106 | config: Config;
107 | }[];
108 | middleware?: {
109 | route: PathParams;
110 | callback: MiddleWareCallback;
111 | method?: Method;
112 | }[];
113 | cors?: {
114 | origin?: string | ((ctx: KoaCtx) => string);
115 | allowMethods?: string | string[];
116 | exposeHeaders?: string | string[];
117 | allowHeaders?: string | string[];
118 | maxAge?: string | number;
119 | credentials?: boolean | ((ctx: KoaCtx) => string);
120 | keepHeadersOnError?: boolean;
121 | secureContext?: boolean;
122 | privateNetworkAccess?: boolean;
123 | };
124 | nextRouter?: NextRouter;
125 | };
126 |
127 | interface OmniBaseConfig {
128 | type: PROJECT_TYPE;
129 | dev?: OmniServer & {
130 | devMiddlewareOptions?: Partial;
131 | webpack?: Configuration | (() => Configuration);
132 | configuration?: (config: ANY_OBJECT) => ANY_OBJECT;
133 | serverType?: ServerType;
134 | favicon?: string;
135 | };
136 | server?: OmniServer & { serverType?: SSR_SERVER; };
137 | build: {
138 | autoRelease?: boolean;
139 | srcDir: string;
140 | outDir: string;
141 | esmDir?: string;
142 | hash?: boolean | HASH;
143 | configuration?: (config: ANY_OBJECT) => ANY_OBJECT;
144 | tool?: Exclude;
145 | preflight?: {
146 | typescript?: boolean;
147 | test?: boolean;
148 | eslint?: boolean;
149 | prettier?: boolean;
150 | stylelint?: boolean;
151 | };
152 | reserve?: {
153 | style?: boolean;
154 | assets?: (string | { srcPath: string; relativePath?: string; })[];
155 | };
156 | };
157 | release: {
158 | git?: string;
159 | npm?: string | boolean;
160 | autoBuild?: boolean;
161 | autoTag?: boolean;
162 | preflight?: {
163 | test?: boolean;
164 | eslint?: boolean;
165 | prettier?: boolean;
166 | stylelint?: boolean;
167 | commitlint?: boolean;
168 | branch?: string;
169 | };
170 | };
171 | template: {
172 | root: string;
173 | test?: boolean;
174 | typescript?: boolean;
175 | stylesheet?: STYLE;
176 | readme?: MARKDOWN | boolean;
177 | };
178 | plugins?: OmniPlugin[];
179 | }
180 |
181 | interface OmniRollupConfig extends OmniBaseConfig {
182 | build: OmniBaseConfig['build'] & {
183 | tool: Extract;
184 | configuration?: (getConfig: (bundle: boolean) => ANY_OBJECT) => ANY_OBJECT;
185 | };
186 | }
187 |
188 | type OmniConfig = OmniBaseConfig | OmniRollupConfig;
189 | ```
190 |
191 | - `name`: the name of plugin
192 |
193 | - `stage`: the stage of plugin execution
194 |
195 | - `handler`: executed callback function, returned in the form of `promise`
196 |
197 | - through `import { PluginHandler_Release } from '@omni-door/cli/lib/index.d';` to get the type that *handler* should satisfy
198 | - support: `PluginHandler_Dev`, `PluginHandler_Build`, `PluginHandler_Release`, `PluginHandler_New`
199 | ---
200 |
201 | ## The commands by import
202 | - `import { initial } from '@omni-door/cli';`: get the initial instruction, then call with paramter directly:
203 |
204 | ```ts
205 | initial({
206 | standard: true // initial a standard project
207 | }, {
208 | // before the project initial
209 | before: dir_name => ({
210 | create_dir: false // avoid create new dir
211 | }),
212 | // after finish the project initial
213 | after: () => {
214 | return {
215 | success: true,
216 | msg: 'build success!'
217 | };
218 | },
219 | // custom the installing template
220 | tplPkj: '@omni-door/tpl-toolkit',
221 | // custom the template parameters
222 | tplPkjParams: ['bid=55232', 'test=false'],
223 | // custom the name of omni.config.js file
224 | configFileName: 'custom.config.js'
225 | });
226 | ```
227 |
228 | - Other phases commands: `import { dev, new as newTpl, build, release } from '@omni-door/cli';`
229 |
230 | - Support custom logo and brand:
231 | ```ts
232 | import { setLogo, setBrand } from '@omni-door/cli';
233 |
234 | setLogo('😄');
235 | setBrand('some_prefix:');
236 | ```
--------------------------------------------------------------------------------
/docs/DEV.zh-CN.md:
--------------------------------------------------------------------------------
1 | # 接入文档
2 |
3 | [English](./DEV.md) | 简体中文
4 |
5 | @omni-door/cli 提供了二次开发的能力,通过 plugin 或者 import 到项目中实现。
6 |
7 | ---
8 |
9 | ## Plugin
10 | 插件向第三方开发者提供了脚手架在项目各个周期的执行多元化任务的能力,插件的编写请务必满足 `type OmniPlugin` 的类型定义。
11 |
12 | ### 编写一个 release 阶段做压缩打包的插件
13 |
14 | ```js
15 | import pack from 'pack'; // 压缩的伪代码
16 |
17 | export default function (config, options) {
18 | return {
19 | name: '@scope/my-release-plugin',
20 | stage: 'release',
21 | handler: config => new Promise((resolve, reject) => {
22 | const { build } = config;
23 | const srcPath = build.outDir;
24 | const destPath = path.resolve(process.cwd(), 'dist.zip');
25 | // 压缩打包
26 | pack(srcPath, destPath, function (res) {
27 | if (res === 'success') {
28 | return resolve();
29 | }
30 | return reject();
31 | });
32 | })
33 | });
34 | }
35 | ```
36 |
37 | ### plugin 的类型
38 | ```ts
39 | type PLUGIN_STAGE = 'new' | 'build' | 'release';
40 |
41 | interface OmniPlugin {
42 | name: string;
43 | stage: T;
44 | handler: PluginHandler;
45 | }
46 |
47 | interface PluginHandler {
48 | (
49 | config: config: Omit,
50 | options?: T extends 'new' ? OptionTemplate : T extends 'build' ? OptionBuild : OptionRelease
51 | ): Promise;
52 | }
53 |
54 | // "new" 阶段
55 | type OptionTemplate = {
56 | componentName: string;
57 | componentType: 'function' | 'class';
58 | tplSource: string;
59 | };
60 |
61 | // "build" 阶段
62 | type OptionBuild = {
63 | verify?: boolean;
64 | buildConfig?: string;
65 | };
66 |
67 | // "release" 阶段
68 | type OptionRelease = {
69 | version: string;
70 | versionIterTactic: 'ignore' | 'manual' | 'auto';
71 | verify?: boolean;
72 | tag?: string;
73 | };
74 | ```
75 |
76 | ### OmniConfig 的类型
77 | ```ts
78 | import type { Configuration } from 'webpack';
79 | import type { Config } from 'http-proxy-middleware';
80 | import type { Options as DevMiddlewareOptions } from 'webpack-dev-middleware';
81 | import type { Request, Response, NextFunction } from 'express';
82 | import type * as KoaApp from 'koa';
83 |
84 | type ANY_OBJECT = { [propName: string]: any };
85 | type ServerType = 'storybook' | 'dumi' | 'default';
86 | type BUILD = 'webpack' | 'rollup' | 'gulp' | 'tsc' | 'next' | '';
87 | type NPM = 'npm' | 'yarn' | 'pnpm';
88 | type PROJECT_TYPE = 'spa-react' | 'spa-react-pc' | 'spa-vue' | 'ssr-react' | 'component-react' | 'component-vue' | 'toolkit';
89 | type STYLE = 'less' | 'scss' | 'css' | 'all' | '';
90 | type SSR_SERVER = 'next-app' | 'next-pages' | 'nuxt' | '';
91 | type Method = 'get' | 'GET' | 'post' | 'POST' | 'put' | 'PUT' | 'del' | 'DEL';
92 |
93 | type OmniServer = {
94 | port?: number;
95 | host?: string;
96 | https?: boolean | { key: string; cert: string; };
97 | CA?: {
98 | organization?: string;
99 | countryCode?: string;
100 | state?: string;
101 | locality?: string;
102 | validityDays?: number;
103 | };
104 | proxy?: {
105 | route: PathParams;
106 | config: Config;
107 | }[];
108 | middleware?: {
109 | route: PathParams;
110 | callback: MiddleWareCallback;
111 | method?: Method;
112 | }[];
113 | cors?: {
114 | origin?: string | ((ctx: KoaCtx) => string);
115 | allowMethods?: string | string[];
116 | exposeHeaders?: string | string[];
117 | allowHeaders?: string | string[];
118 | maxAge?: string | number;
119 | credentials?: boolean | ((ctx: KoaCtx) => string);
120 | keepHeadersOnError?: boolean;
121 | secureContext?: boolean;
122 | privateNetworkAccess?: boolean;
123 | };
124 | nextRouter?: NextRouter;
125 | };
126 |
127 | interface OmniBaseConfig {
128 | type: PROJECT_TYPE;
129 | dev?: OmniServer & {
130 | devMiddlewareOptions?: Partial;
131 | webpack?: Configuration | (() => Configuration);
132 | configuration?: (config: ANY_OBJECT) => ANY_OBJECT;
133 | serverType?: ServerType;
134 | favicon?: string;
135 | };
136 | server?: OmniServer & { serverType?: SSR_SERVER; };
137 | build: {
138 | autoRelease?: boolean;
139 | srcDir: string;
140 | outDir: string;
141 | esmDir?: string;
142 | hash?: boolean | HASH;
143 | configuration?: (config: ANY_OBJECT) => ANY_OBJECT;
144 | tool?: Exclude;
145 | preflight?: {
146 | typescript?: boolean;
147 | test?: boolean;
148 | eslint?: boolean;
149 | prettier?: boolean;
150 | stylelint?: boolean;
151 | };
152 | reserve?: {
153 | style?: boolean;
154 | assets?: (string | { srcPath: string; relativePath?: string; })[];
155 | };
156 | };
157 | release: {
158 | git?: string;
159 | npm?: string | boolean;
160 | autoBuild?: boolean;
161 | autoTag?: boolean;
162 | preflight?: {
163 | test?: boolean;
164 | eslint?: boolean;
165 | prettier?: boolean;
166 | stylelint?: boolean;
167 | commitlint?: boolean;
168 | branch?: string;
169 | };
170 | };
171 | template: {
172 | root: string;
173 | test?: boolean;
174 | typescript?: boolean;
175 | stylesheet?: STYLE;
176 | readme?: MARKDOWN | boolean;
177 | };
178 | plugins?: OmniPlugin[];
179 | }
180 |
181 | interface OmniRollupConfig extends OmniBaseConfig {
182 | build: OmniBaseConfig['build'] & {
183 | tool: Extract;
184 | configuration?: (getConfig: (bundle: boolean) => ANY_OBJECT) => ANY_OBJECT;
185 | };
186 | }
187 |
188 | type OmniConfig = OmniBaseConfig | OmniRollupConfig;
189 | ```
190 |
191 | - `name`:插件的名称
192 |
193 | - `stage`:插件执行的阶段
194 |
195 | - `handler`:执行的回调函数,以 `promise` 的形式返回
196 |
197 | - 通过 `import { PluginHandler_Release } from '@omni-door/cli/lib/index.d';` 获取 handle 应满足的类型
198 | - 支持: `PluginHandler_Dev`、`PluginHandler_Build`、`PluginHandler_Release`、`PluginHandler_New`
199 | ---
200 |
201 | ## import 引入 command 命令
202 | - `import { initial } from '@omni-door/cli';`:获取 initial 指令,传入参数直接调用:
203 |
204 | ```ts
205 | initial({
206 | standard: true // 构建一个标准项目
207 | }, {
208 | // 项目初始化开始前
209 | before: dir_name => ({
210 | create_dir: false // 避免新创建文件夹
211 | }),
212 | // 项目初始化完成后
213 | after: () => {
214 | return {
215 | success: true,
216 | msg: '完成项目初始化构建'
217 | };
218 | },
219 | // 自定义安装的模板
220 | tplPkj: '@omni-door/tpl-toolkit',
221 | // 自定义模板需要传入的参数
222 | tplPkjParams: ['bid=55232', 'test=false'],
223 | // 自定义 omni.config.js 文件名称
224 | configFileName: 'custom.config.js'
225 | });
226 | ```
227 |
228 | - 其他阶段的命令同样支持:`import { dev, new as newTpl, build, release } from '@omni-door/cli';`
229 |
230 | - 支持自定义 logo、brand 前缀:
231 | ```ts
232 | import { setLogo, setBrand } from '@omni-door/cli';
233 |
234 | setLogo('😄');
235 | setBrand('自定义的前缀:');
236 | ```
--------------------------------------------------------------------------------
/docs/OMNI.md:
--------------------------------------------------------------------------------
1 | # omni.config.js Detail
2 |
3 | English | [简体中文](./OMNI.zh-CN.md)
4 |
5 | ## type
6 | OMNI will process of initialization, construction and template creation according to different project types
7 |
8 | The project types:
9 |
10 | - spa-react - React single-page-application
11 |
12 | - spa-react-pc - React single-page-application based on [Antd](https://ant.design/)
13 |
14 | - spa-vue - Vue single-page-application
15 |
16 | - ssr-react - React sever-side-render application
17 |
18 | - component-react - React Component Library
19 |
20 | - component-vue - Vue Component Library
21 |
22 | - toolkit - SDK Library
23 |
24 | ## dev
25 | The dev-server based on express, realizing hot-update, api-proxy and other common functions. Provide personalized customization schemes such as middleware customization, port number, log output level and webpack configuration.
26 |
27 | - middleware - middleware configuration:
28 |
29 | ```ts
30 | {
31 | route: string;
32 | callback: (req: any, res: any) => Promise;
33 | }
34 | ```
35 |
36 | or
37 |
38 | ```ts
39 | (params: {
40 | ip: string;
41 | port: number;
42 | host?: string;
43 | proxyConfig?: (ProxyItem | ProxyFn)[];
44 | }) => {
45 | route: string;
46 | callback: (req: any, res: any) => Promise;
47 | }
48 | ```
49 |
50 | - webpack - dev-server webpack configuration
51 |
52 | - proxy - dev-server proxy configuration
53 |
54 | ```ts
55 | {
56 | route: '/api', // Address of the local service for the proxy API
57 | config: {
58 | target: 'http://www.api.com/api', // The actual address of the proxy API
59 | changeOrigin: true // whether change the host
60 | }
61 | }
62 | ```
63 |
64 | or
65 |
66 | ```ts
67 | (params: {
68 | ip: string;
69 | port: number;
70 | host?: string;
71 | middlewareConfig?: (MiddlewareItem | MiddlewareFn)[];
72 | }) => {
73 | route: string;
74 | config: Config;
75 | }
76 | ```
77 |
78 | For more configuration, see [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware)
79 |
80 | - port - dev-server port
81 |
82 | - host - dev-server host
83 |
84 | - https - start dev-server with https protocol which could custom `key` and `cert`
85 |
86 | - serverType - dev-server type
87 |
88 | - favicon - favicon path for dev-server
89 |
90 | ## build
91 |
92 | - autoRelease - auto release project after build success
93 |
94 | - srcDir - the build source directory
95 |
96 | - outDir - the directory for compiled project
97 |
98 | - esmDir - es6 module compiled directory
99 |
100 | - hash - whether the hash tag add to building result, optional 'contenthash', 'chunkhash' and 'hash'(true equal 'contenthash')
101 |
102 | - configuration - The callback will be call in the build-process, you can return your custom build configuration
103 |
104 | - reserve - Configure resources that are not packaged but need to be kept in the build result
105 | - style - whether or not reserve the stylesheet files
106 |
107 | - assets - reserve other asset paths
108 |
109 | - preflight - the flight check before build
110 | - typescript - whether or not process the ts or tsx files
111 |
112 | - test - whether or not process unit-test
113 |
114 | - eslint - whether or not process eslint check
115 |
116 | - prettier - whether or not process prettier check
117 |
118 | - stylelint - whether or not process stylelint check
119 |
120 | ## release
121 | - autoBuild - auto build project before release process
122 |
123 | - autoTag - npm publish will auto set tag according to the current version
124 |
125 | - git - project git repo url
126 |
127 | - npm - npm depository url
128 |
129 | - preflight - the flight check before release
130 | - test - whether or not process unit-test
131 |
132 | - eslint - whether or not process eslint check
133 |
134 | - prettier - whether or not process prettier check
135 |
136 | - stylelint - whether or not process stylelint check
137 |
138 | - commitlint - whether or not process commitlint check
139 |
140 | - branch - only can release in this branch, set empty string to ignore this check
141 |
142 | ## template
143 | - root - the root directory for generate template
144 |
145 | - typescript - whether or not apply typescript
146 |
147 | - test - whether or not generate unit-test file
148 |
149 | - stylesheet - stylesheet type
150 |
151 | - readme - [true, 'mdx'] ([whether or not README.md, generate mdx or md file])
152 |
153 | ## plugins
154 | plugin must meet following types:
155 |
156 | ```ts
157 | type OmniPlugin = {
158 | name: string;
159 | stage: PLUGIN_STAGE;
160 | handler: PluginHandler;
161 | };
162 |
163 | type PLUGIN_STAGE = 'new' | 'build' | 'release';
164 | interface PluginHandler {
165 | (config: Omit): Promise;
166 | }
167 | ```
--------------------------------------------------------------------------------
/docs/OMNI.zh-CN.md:
--------------------------------------------------------------------------------
1 | # omni.config.js 详解
2 |
3 | [English](./OMNI.md) | 简体中文
4 |
5 | ## type 项目类型
6 | OMNI 会根据不同的项目类型决定整个初始化、构建、创建模板的过程
7 |
8 | 目前支持的项目类型有:
9 |
10 | - spa-react - React单页应用
11 |
12 | - spa-react-pc - 基于 [Antd](https://ant.design/) 的React中后台单页应用
13 |
14 | - spa-vue - Vue单页应用
15 |
16 | - ssr-react - React服务端渲染应用
17 |
18 | - component-react - React组件库
19 |
20 | - component-vue - Vue组件库
21 |
22 | - toolkit - SDK工具包
23 |
24 | ## dev 开发服务
25 | 开发服务基于express,搭配 webpack-dev-middleware、webpack-hot-middleware、http-proxy-middleware 等中间件,实现了热更新、接口代理等常用功能,并提供了中间件的自定义、端口号、log日志输出级别、webpack配置等个性化定制方案。
26 |
27 | - middleware - 中间件配置,参考下面👇的类型:
28 |
29 | ```ts
30 | {
31 | route: string;
32 | callback: (req: any, res: any) => Promise;
33 | }
34 | ```
35 |
36 | or
37 |
38 | ```ts
39 | (params: {
40 | ip: string;
41 | port: number;
42 | host?: string;
43 | proxyConfig?: (ProxyItem | ProxyFn)[];
44 | }) => {
45 | route: string;
46 | callback: (req: any, res: any) => Promise;
47 | }
48 | ```
49 |
50 | - webpack - 开发服务端webpack配置
51 |
52 | - proxy - 开发服务代理配置
53 |
54 | ```ts
55 | {
56 | route: '/api', // 代理API的本地服务的地址
57 | config: {
58 | target: 'http://www.api.com/api', // 代理API的实际地址
59 | changeOrigin: true // 是否改变host
60 | }
61 | }
62 | ```
63 |
64 | or
65 |
66 | ```ts
67 | (params: {
68 | ip: string;
69 | port: number;
70 | host?: string;
71 | middlewareConfig?: (MiddlewareItem | MiddlewareFn)[];
72 | }) => {
73 | route: string;
74 | config: Config;
75 | }
76 | ```
77 |
78 | 更多配置详见 [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware)
79 |
80 | - port - 开发服务启动的端口号
81 |
82 | - host - 开发服务启动的host
83 |
84 | - https - 开发服务以https协议启动,可自定义 `key` 和 `cert`
85 |
86 | - serverType - 开发服务的类型
87 |
88 | - favicon - 开发服务的 favicon 路径
89 |
90 | ## build 构建配置
91 |
92 | - autoRelease - 构建完成后是否自动发布
93 |
94 | - srcDir - 构建资源输入路径
95 |
96 | - outDir - 构建结果输出路径
97 |
98 | - esmDir - 构建结果输出路径(符合es6 module规范)
99 |
100 | - hash - 构建的资源是否加上hash,可选 'contenthash'、'chunkhash'、'hash'(传入true则是contenthash)
101 |
102 | - configuration - 构建阶段的自定义配置回调,返回自定义的配置
103 |
104 | - reserve - 配置未经过打包,但需要保留进构建结果的资源
105 | - style - 构建结果是否保留样式文件
106 |
107 | - assets - 构建结果保留其他资源的路径
108 |
109 | - preflight - 构建前的飞行检查
110 | - typescript - 是否处理ts或tsx文件
111 |
112 | - test - 是否进行单元测试
113 |
114 | - eslint - 是否进行eslint检测
115 |
116 | - prettier - 是否进行prettier检测
117 |
118 | - stylelint - 是否进行stylelint检测
119 |
120 | ## release
121 | - autoBuild - 发布之前是否自动构建项目
122 |
123 | - autoTag - 发布到npm仓库时会自动根据当前版本号设定tag
124 |
125 | - git - 发布的git仓库地址
126 |
127 | - npm - 发布的npm仓库地址
128 |
129 | - preflight - 发布前的飞行检查
130 | - test - 发布前是否进行单元测试
131 |
132 | - eslint - 发布前是否进行eslint检测
133 |
134 | - prettier - 发布前是否进行prettier检测
135 |
136 | - stylelint - 发布前是否进行stylelint检测
137 |
138 | - commitlint - 发布前是否进行commitlint检测
139 |
140 | - branch - 发布前进行分支检测,设置为空字符串则不会检测
141 |
142 | ## template 新建模板配置
143 | - root - 生成模板的根路径
144 |
145 | - typescript - 是否创建ts文件
146 |
147 | - test - 是否创建单元测试文件
148 |
149 | - stylesheet - 样式文件类型
150 |
151 | - readme - [true, 'mdx'] ([是否生成ReadMe文件, 创建md 或 mdx文件])
152 |
153 | ## plugins
154 | 插件集合,插件需满足下面的类型:
155 |
156 | ```ts
157 | type OmniPlugin = {
158 | name: string;
159 | stage: PLUGIN_STAGE;
160 | handler: PluginHandler;
161 | };
162 |
163 | type PLUGIN_STAGE = 'new' | 'build' | 'release';
164 | interface PluginHandler {
165 | (config: Omit): Promise;
166 | }
167 | ```
--------------------------------------------------------------------------------
/docs/README.zh-CN.md:
--------------------------------------------------------------------------------
1 | # 🐸 @omni-door/cli
2 |
3 | https://www.omnidoor.org
4 |
5 | 一个能创建标准的前端项目的脚手架。
6 |
7 | [](https://www.npmjs.com/package/@omni-door/cli)
8 | [](https://badge.fury.io/js/%40omni-door%2Fcli)
9 | [](http://nodejs.org/download/)
10 | [](https://travis-ci.com/omni-door/cli)
11 | [](https://codecov.io/gh/omni-door/cli)
12 | [](https://packagephobia.now.sh/result?p=%40omni-door%2Fcli)
13 | [](https://github.com/omni-door/cli/blob/master/LICENSE)
14 |
15 | [English](../README.md) | 简体中文
16 |
17 | [ 详解](./OMNI.zh-CN.md)
18 |
19 | [变更日志](./CHANGELOG.zh-CN.md)
20 |
21 | ## 安装
22 | 推荐使用 node 最新的 LTS 版本,或至少保证 node >= 10.13.0
23 |
24 | 几个选项:
25 |
26 | * 克隆仓库: `git@github.com:omni-door/cli.git`
27 |
28 | * 用 [npm](https://www.npmjs.com/package/@omni-door/cli) 安装:`npm install @omni-door/cli -g`
29 |
30 | * 用 [Yarn](https://yarnpkg.com/en/package/@omni-door/cli) 安装:`yarn global add @omni-door/cli`
31 |
32 | * 直接用 [npx](https://www.npmjs.com/package/@omni-door/cli) 初始化你的项目:`npx @omni-door/cli init`
33 |
34 | ## omni --help
35 | ```shell
36 | 使用: omni [command] [options]
37 |
38 | Options:
39 |
40 | -v, --version 输出版本号
41 | -h, --help 输出使用帮助
42 |
43 | Commands:
44 |
45 | init [strategy] [options] 初始化你的项目,[strategy(策略)] 可用是stable(默认) 或 lastst
46 | dev [options] omni dev -p [port]
47 | new [options] omni new [module] [-f | -c]
48 | build 根据 [omni.config.js] 打包构建你的项目
49 | release [options] 根据 [omni.config.js] 发布你的项目
50 |
51 | ```
52 |
53 | ## omni init
54 |
55 | ### 初始化一个项目
56 | ```shell
57 | omni init
58 | ```
59 |
60 | ### 用最新的依赖@lastest初始化项目
61 | ```shell
62 | omni init lastest
63 | ```
64 |
65 | ### 初始化项目但不安装依赖
66 | ```shell
67 | omni init -n
68 | ```
69 |
70 | ### 套用模板一键初始化项目
71 | ```shell
72 | omni init -t [projectName]
73 | ```
74 | or
75 | ```shell
76 | omni init --entire [projectName]
77 | ```
78 |
79 | ### 选项
80 | ```shell
81 | 使用: omni init [strategy] [options]
82 |
83 | initialize your project, [strategy] could be stable(default) or latest
84 |
85 | Arguments:
86 |
87 | strategy stable or latest
88 |
89 | Options:
90 | -rb, --react_basic [name] 创建一个最基本的 React 单页应用
91 | -rs, --react_standard [name] 创建一个标准的 React 单页应用
92 | -re, --react_entire [name] 创建一个全量的 React 单页应用
93 | -rp, --react_pc [name] 创建一个基于 Antd 的 React 中后台单页应用
94 | -vb, --vue_basic [name] 创建一个最基本的 Vue 单页应用
95 | -vs, --vue_standard [name] 创建一个标准的 Vue 单页应用
96 | -ve, --vue_entire [name] 创建一个全量的 Vue 单页应用
97 | -rS, --react_ssr [name] 创建一个 React SSR 应用
98 | -rc, --react_components [name] 创建一个 React 组件库
99 | -vc, --vue_components [name] 创建一个 Vue 组件库
100 | -t, --toolkit [name] 创建一个工具库
101 | -n, --no-install 初始化项目不安装任何依赖
102 | -P, --path 创建项目的工作路径
103 | -h, --help 输出帮助信息
104 | ```
105 |
106 | ---
107 |
108 | ## omni dev
109 |
110 | ### 选项
111 | ```shell
112 | 使用: omni dev [options]
113 |
114 | omni dev [-p ] [-H ] [-P ]
115 |
116 | Options:
117 | -p, --port 根据指定的端口号启动开发服务
118 | -H, --hostname 根据指定的hostname启动开发服务
119 | -P, --path 启动开发服务的工作路径
120 | -h, --help 输出帮助信息
121 | ```
122 |
123 | ---
124 |
125 | ## omni start
126 |
127 | ### 选项
128 | ```shell
129 | 使用: omni start [options]
130 |
131 | omni start [-p ] [-H ] [-P ]
132 |
133 | Options:
134 | -p, --port 根据指定的端口号启动生产服务
135 | -H, --hostname 根据指定的hostname启动生产服务
136 | -P, --path 启动生产服务的工作路径
137 | -h, --help 输出帮助信息
138 | ```
139 |
140 | ---
141 |
142 | ## omni new
143 |
144 | ### 选项
145 | ```shell
146 | 使用: omni new [name] [options]
147 |
148 | omni new [name] [-f | -c] [-P ]
149 |
150 | Arguments:
151 |
152 | module 可选!组件名称。
153 |
154 | Options:
155 | -f, --function 创建一个React函数组件
156 | -c, --class 创建一个React类组件
157 | -r, --render 创建一个Vue渲染函数组件
158 | -s, --single 创建一个Vue模板组件
159 | -P, --path 创建组件的工作路径
160 | -h, --help 输出帮助信息
161 | ```
162 |
163 | ---
164 |
165 | ## omni build
166 |
167 | ### 选项
168 | ```shell
169 | 使用: omni build [options]
170 |
171 | 根据 [omni.config.js] 的 build 字段构建项目
172 |
173 | Options:
174 | -c, --config 指定构建的配置文件路径
175 | -n, --no-verify 绕过所有预检直接构建
176 | -P, --path 构建的工作路径
177 | -h, --help 输出帮助信息
178 | ```
179 |
180 | ---
181 |
182 | ## omni release
183 |
184 | ### 选项
185 | ```shell
186 | 使用: omni release [options]
187 |
188 | 根据 [omni.config.js] 的 release 字段发布项目
189 |
190 | Options:
191 | -a, --automatic 发布并自动迭代版本号
192 | -i, --ignore 发布并忽视版本号的迭代
193 | -m, --manual 发布并手动指定版本号
194 | -t, --tag 发布时指定tag
195 | -n, --no-verify 绕过所有的预检直接发布
196 | -P, --path 发布的工作路径
197 | -h, --help 输出帮助信息
198 | ```
199 |
200 | ---
201 |
202 | ## API文档
203 | 点 [这里](./DEV.zh-CN.md)
204 |
205 | ## License
206 |
207 | Copyright (c) 2019 [Bobby.li](https://github.com/BobbyLH)
208 |
209 | Released under the MIT License
--------------------------------------------------------------------------------
/docs/omni-init.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni-door/cli/7349af22a058d59d20b4194609dc2e9d4ae35010/docs/omni-init.gif
--------------------------------------------------------------------------------
/mocha.opts:
--------------------------------------------------------------------------------
1 | # mocha.opts
2 | --require ts-node/register src/**/__test__/*.ts
3 | --reporter spec
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@omni-door/cli",
3 | "version": "3.1.1",
4 | "description": "A tool set for set up the standard JS project",
5 | "bin": {
6 | "omni": "./bin/omni-door.js"
7 | },
8 | "main": "lib/commands/commands.js",
9 | "typings": "lib/commands/commands.d.ts",
10 | "scripts": {
11 | "test": "nyc mocha --opts mocha.opts",
12 | "lint": "eslint src/ --ext .ts --ext .tsx",
13 | "lint:fix": "eslint src/ --ext .ts --ext .tsx --fix",
14 | "build": "npm run build:rm && npm run build:tsc && npm run build:copy",
15 | "build:rm": "rm -rf lib/*",
16 | "build:tsc": "tsc --build",
17 | "build:branch": "./scripts/branch.sh",
18 | "build:version": "./scripts/version.sh",
19 | "build:copy": "./scripts/copy.sh",
20 | "release": "npm run build:branch master && npm run build && npm run build:version"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/omni-door/cli.git"
25 | },
26 | "keywords": [
27 | "omni",
28 | "omni-door"
29 | ],
30 | "author": "bobby.li",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/omni-door/cli/issues",
34 | "email": "omni.door.official@gmail.com"
35 | },
36 | "husky": {
37 | "hooks": {
38 | "pre-commit": "lint-staged",
39 | "pre-push": "npm run lint && npm run test",
40 | "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS"
41 | }
42 | },
43 | "lint-staged": {
44 | "src/**/*.{ts,tsx}": [
45 | "npm run lint:fix",
46 | "git add"
47 | ]
48 | },
49 | "homepage": "https://github.com/omni-door/cli#readme",
50 | "devDependencies": {
51 | "@babel/core": "^7.6.4",
52 | "@types/chai": "^4.2.3",
53 | "@types/clui": "^0.3.0",
54 | "@types/detect-port": "^1.1.0",
55 | "@types/figlet": "^1.2.0",
56 | "@types/fs-extra": "^8.0.1",
57 | "@types/inquirer": "^6.5.0",
58 | "@types/ip": "^1.1.0",
59 | "@types/koa-router": "^7.4.1",
60 | "@types/mkcert": "^1.2.0",
61 | "@types/mocha": "^5.2.7",
62 | "@types/next-server": "^9.0.0",
63 | "@types/node": "^12.11.1",
64 | "@types/rollup": "^0.54.0",
65 | "@types/semver": "^7.3.4",
66 | "@types/shelljs": "^0.8.5",
67 | "@types/webpack-env": "^1.15.0",
68 | "@types/webpack-hot-middleware": "^2.25.0",
69 | "@typescript-eslint/eslint-plugin": "^3.8.0",
70 | "@typescript-eslint/parser": "^3.8.0",
71 | "babel-loader": "^8.0.6",
72 | "chai": "^4.2.0",
73 | "commitizen": "^4.1.2",
74 | "commitlint": "^8.2.0",
75 | "css-loader": "^3.2.0",
76 | "cz-conventional-changelog": "^3.2.0",
77 | "detect-port": "^1.3.0",
78 | "eslint": "^7.6.0",
79 | "express": "4.17.3",
80 | "http-proxy-middleware": "^0.20.0",
81 | "husky": "^3.0.9",
82 | "ip": "^1.1.5",
83 | "less": "^3.10.3",
84 | "less-loader": "^5.0.0",
85 | "lint-staged": "^9.4.2",
86 | "mocha": "^6.2.2",
87 | "node-sass": "9.0.0",
88 | "nyc": "^14.1.1",
89 | "open": "^7.0.0",
90 | "rollup": "^1.26.0",
91 | "rollup-plugin-babel": "^4.3.3",
92 | "rollup-plugin-commonjs": "^10.1.0",
93 | "rollup-plugin-node-resolve": "^5.2.0",
94 | "rollup-plugin-typescript": "^1.0.1",
95 | "rollup-plugin-typescript2": "^0.24.3",
96 | "sass-loader": "^8.0.0",
97 | "style-loader": "^1.0.0",
98 | "ts-loader": "^6.2.1",
99 | "ts-node": "^8.4.1",
100 | "typescript": "3.9.7",
101 | "webpack": "^4.41.5",
102 | "webpack-cli": "^3.3.9",
103 | "webpack-dev-middleware": "^3.7.2",
104 | "webpack-hot-middleware": "^2.25.0",
105 | "webpack-merge": "^4.2.2"
106 | },
107 | "dependencies": {
108 | "@omni-door/utils": "~1.4.0",
109 | "@types/express": "4.17.8",
110 | "@types/http-proxy-middleware": "0.19.3",
111 | "@types/koa": "2.11.3",
112 | "@types/webpack": "4.41.26",
113 | "@types/webpack-dev-middleware": "4.0.0",
114 | "chalk": "3.0.0",
115 | "commander": "5.1.0",
116 | "del": "5.1.0",
117 | "figlet": "1.2.4",
118 | "fs-extra": "8.1.0",
119 | "inquirer": "8.2.4",
120 | "leven": "3.1.0",
121 | "mkcert": "1.5.0",
122 | "shelljs": "0.8.5"
123 | },
124 | "resolutions": {
125 | "@types/express/@types/express-serve-static-core": "4.17.12"
126 | },
127 | "config": {
128 | "commitizen": {
129 | "path": "./node_modules/cz-conventional-changelog"
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/scripts/branch.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | branch=$1
4 | name="🐸 [OMNI-DOOR/CLI]"
5 |
6 | checkBranch () {
7 | if [ -z "$branch" ]; then
8 | echo -e "\033[31m \n ${name}: The branch parameter cannot be empty\n \033[0m"
9 | exit 1
10 | fi
11 |
12 | currentBranch=$(git branch | grep \* | cut -d " " -f2)
13 |
14 | if [ "$currentBranch" != "$branch" ]
15 | then
16 | echo -e "\033[31m \n ${name}: Please switch to \033[43;30m ${branch} \033[0m \033[31mbranch first\n \033[0m"
17 | exit 1
18 | fi
19 |
20 | echo -e "\033[36m \n ${name}: The current branch is ${branch}\n \033[0m"
21 | }
22 |
23 | checkBranch
--------------------------------------------------------------------------------
/scripts/copy.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | cp -rf src/index.d.ts lib/index.d.ts
4 | cp -rf src/commands/release/branch.sh lib/commands/release/branch.sh
5 | cp -rf src/commands/release/version.sh lib/commands/release/version.sh
6 | cp -rf src/commands/release/publish.sh lib/commands/release/publish.sh
7 | cp -rf src/commands/dev/open.chrome.applescript lib/commands/dev/open.chrome.applescript
8 | cp -rf src/commands/servers/favicon.ico lib/commands/servers/favicon.ico
--------------------------------------------------------------------------------
/scripts/publish.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | name="🐸 [OMNI-DOOR/CLI]"
4 |
5 | if [ $? -eq 0 ]
6 | then
7 | pkjV=$(grep \"version\" package.json)
8 | version=$(echo ${pkjV} | tr -cd "[0-9].")
9 | git add -A
10 | git commit -m "${name}: ${version}"
11 | git push
12 | npm publish --registry='https://registry.npmjs.org'
13 | echo -e "\033[32m \n${name}: The npm-package publish success - ${version}\n \033[0m"
14 | else
15 | echo -e "\033[31m \n${name}: The npm-package publish failed!\n \033[0m"
16 | fi
--------------------------------------------------------------------------------
/scripts/version.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | iterate=$1
4 | name="🐸 [OMNI-DOOR/CLI]"
5 | dot="."
6 | OS=`uname`
7 |
8 | replaceVersion () {
9 | if [ "$OS" = "Darwin" ]; then
10 | sed -i "" "s/$1/$2/g" "package.json"
11 | else
12 | sed -i"" "s/$1/$2/g" "package.json"
13 | fi
14 | }
15 |
16 | updateVersion () {
17 | versionLine=$(grep \"version\" package.json)
18 | version=$(echo ${versionLine} | tr -cd "[0-9-a-zA-Z]." | sed -ne "s/[^0-9]*\(\([0-9a-zA-Z]\.\)\)/\1/p")
19 | prevSubVersion=$(echo ${version#*.})
20 | subVersion=$(echo ${prevSubVersion%.*})
21 | subSubVersion=$(echo ${version##*.})
22 | manualVersion=$(echo "$iterate" | grep "[0-9].[0-9]*.[0-9]")
23 | if [ "$iterate" = "i" -o "$iterate" = "ignore" ]
24 | then
25 | echo -e "\033[33m${name}: Ignoring the version of iteration\033[0m"
26 | elif [ -z "$iterate" ]
27 | then
28 | newSubSubVersion=`expr $subSubVersion + 1`
29 | newVersion=$(echo ${version/${dot}${subVersion}${dot}${subSubVersion}/${dot}${subVersion}${dot}${newSubSubVersion}})
30 | newVersionLine=$(echo "${versionLine/${version}/${newVersion}}")
31 | echo -e "\033[36m${name}: Auto-increase the version of iteration to ${newVersion}\033[0m"
32 | replaceVersion "$versionLine" "$newVersionLine"
33 | elif [ -n "$manualVersion" ]
34 | then
35 | newVersion=$(echo ${version/${version}/${manualVersion}})
36 | newVersionLine=$(echo "${versionLine/${version}/${newVersion}}")
37 | echo -e "\033[35m${name}: Manual specify the version of iteration to ${manualVersion}\033[0m"
38 | replaceVersion "$versionLine" "$newVersionLine"
39 | else
40 | echo -e "\033[41;37m${name}: Please input correct version number\033[0m"
41 | exit 1
42 | fi
43 | }
44 |
45 | updateVersion
46 |
47 | exec "./scripts/publish.sh"
--------------------------------------------------------------------------------
/src/@types/rollup.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'rollup-plugin-babel';
2 | declare module 'rollup-plugin-typescript';
--------------------------------------------------------------------------------
/src/commands/build/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import build from '../index';
4 | import rollupConfig from '../rollup';
5 | import webpackConfig from '../webpack';
6 | import dependencies from '../dependencies_build';
7 |
8 | describe('build command test', function () {
9 | it('type checking', function () {
10 | expect(build).to.be.a('function');
11 | expect(rollupConfig).to.be.a('function');
12 | expect(webpackConfig).to.be.a('function');
13 | expect(dependencies).to.be.a('function');
14 | });
15 |
16 | it('call rollupConfig', function () {
17 | const config1 = rollupConfig({
18 | ts: true,
19 | multiOutput: false,
20 | srcDir: 'src',
21 | outDir: 'lib',
22 | esmDir: 'es'
23 | });
24 | expect(config1).to.be.a('string');
25 |
26 | const config2 = rollupConfig({
27 | ts: false,
28 | multiOutput: true,
29 | srcDir: 'src',
30 | outDir: 'dist',
31 | esmDir: 'es',
32 | configFileName: 'custom.config.js'
33 | });
34 | expect(config2).to.be.a('string');
35 | });
36 |
37 | it('call webpackConfig', function () {
38 | const config1 = webpackConfig({
39 | ts: true,
40 | multiOutput: false,
41 | srcDir: 'src',
42 | outDir: 'lib',
43 | configFileName: 'custom.config.js'
44 | });
45 | expect(config1).to.be.a('string');
46 |
47 | const config2 = webpackConfig({
48 | ts: false,
49 | multiOutput: true,
50 | srcDir: 'src',
51 | outDir: 'dist',
52 | hash: 'chunkhash'
53 | });
54 | expect(config2).to.be.a('string');
55 | });
56 |
57 | it('call dependencies', function (done) {
58 | dependencies({
59 | build: 'webpack',
60 | project_type: 'spa-react'
61 | }).then(webpackDependencies => {
62 | expect(webpackDependencies).to.be.a('string');
63 | expect(!!~webpackDependencies.indexOf('webpack')).to.be.true;
64 | dependencies({
65 | build: 'rollup',
66 | project_type: 'toolkit'
67 | }).then(rollupDependencies => {
68 | expect(!!~rollupDependencies.indexOf('rollup')).to.be.true;
69 | done();
70 | });
71 | });
72 | });
73 | });
--------------------------------------------------------------------------------
/src/commands/build/dependencies_build.ts:
--------------------------------------------------------------------------------
1 | import { getDependency, arr2str } from '@omni-door/utils';
2 | /* import types */
3 | import type { BUILD, PROJECT_TYPE } from '@omni-door/utils';
4 |
5 | export default async function (config: {
6 | build: BUILD;
7 | project_type: PROJECT_TYPE;
8 | }) {
9 | const dependency = await getDependency('stable', {
10 | '@babel/core': '~7.10.0',
11 | '@babel/preset-env': '~7.10.0',
12 | '@babel/preset-react': '~7.10.0',
13 | '@babel/preset-typescript': '~7.10.0',
14 | '@babel/plugin-transform-runtime': '~7.10.0',
15 | '@babel/plugin-proposal-class-properties': '~7.10.0',
16 | '@omni-door/gulp-plugin-vue-sfc': '~0.1.0',
17 | 'babel-loader': '~8.1.0',
18 | 'cache-loader': '~4.1.0',
19 | 'css-loader': '~3.4.2',
20 | 'cssnano': '4.1.10',
21 | 'file-loader': '~5.0.2',
22 | 'html-webpack-plugin': '3.2.0',
23 | 'less': '~3.11.1',
24 | 'less-loader': '~5.0.0',
25 | 'mini-css-extract-plugin': '0.9.0',
26 | 'optimize-css-assets-webpack-plugin': '5.0.3',
27 | 'sass': '~1.77.6',
28 | 'sass-loader': '~10.2.1',
29 | 'style-loader': '~1.1.3',
30 | 'terser-webpack-plugin': '2.3.4',
31 | 'url-loader': '~3.0.0',
32 | 'vue-loader': 'next',
33 | 'vue-tsc': '~2.0.29',
34 | 'webpack': '~4.41.6',
35 | 'webpack-bundle-analyzer': '3.6.0',
36 | 'webpack-cli': '~3.3.11',
37 | 'webpack-dev-middleware': '3.7.2',
38 | 'webpack-hot-middleware': '2.25.0',
39 | 'webpack-merge': '4.2.2',
40 | 'webpackbar': '4.0.0',
41 | 'rollup': '1.31.1',
42 | 'rollup-plugin-babel': '4.3.3',
43 | 'rollup-plugin-commonjs': '10.1.0',
44 | 'rollup-plugin-json': '4.0.0',
45 | 'rollup-plugin-node-resolve': '5.2.0',
46 | 'rollup-plugin-typescript': '1.0.1',
47 | 'rollup-plugin-typescript2': '0.26.0',
48 | 'ts-patch': '~3.2.1',
49 | 'typescript': '~5.5.4',
50 | 'typescript-transform-paths': '~3.4.7',
51 | 'gulp': '4.0.2',
52 | 'gulp-autoprefixer': '7.0.1',
53 | 'gulp-babel': '8.0.0',
54 | 'gulp-clean-css': '4.3.0',
55 | 'gulp-concat': '2.6.1',
56 | 'gulp-concat-css': '3.1.0',
57 | 'gulp-cssnano': '2.1.3',
58 | 'gulp-less': '4.0.1',
59 | 'gulp-sass': '5.1.0',
60 | 'gulp-if': '3.0.0',
61 | 'gulp-sourcemaps': '3.0.0',
62 | 'gulp-ts-alias': '1.3.0',
63 | 'gulp-typescript': '5.0.1',
64 | 'through2': '4.0.1',
65 | 'next': '~10.0.1',
66 | 'next-url-prettifier': '1.4.0',
67 | '@next/bundle-analyzer': '~10.0.1',
68 | '@zeit/next-css': '~1.0.1',
69 | '@zeit/next-less': '~1.0.1',
70 | '@zeit/next-sass': '~1.0.1',
71 | 'next-compose-plugins': '~2.2.0',
72 | 'next-transpile-modules': '~4.0.2',
73 | });
74 | const {
75 | build,
76 | project_type
77 | } = config;
78 |
79 | const buildDependencies = build === 'webpack' ? [
80 | dependency('webpack'),
81 | dependency('webpack-cli'),
82 | dependency('webpack-merge'),
83 | project_type === 'spa-vue' ? dependency('vue-loader') : '',
84 | dependency('babel-loader'),
85 | dependency('cache-loader'),
86 | dependency('style-loader'),
87 | dependency('css-loader'),
88 | dependency('less'),
89 | dependency('less-loader'),
90 | dependency('sass-loader'),
91 | dependency('sass'),
92 | dependency('url-loader'),
93 | dependency('file-loader'),
94 | dependency('html-webpack-plugin'),
95 | dependency('terser-webpack-plugin'),
96 | dependency('optimize-css-assets-webpack-plugin'),
97 | dependency('mini-css-extract-plugin'),
98 | dependency('cssnano'),
99 | dependency('webpackbar'),
100 | dependency('webpack-bundle-analyzer'),
101 | dependency('@babel/core'),
102 | dependency('@babel/preset-env'),
103 | dependency('@babel/preset-react'),
104 | dependency('@babel/preset-typescript')
105 | ] : build === 'rollup' ? [
106 | dependency('rollup'),
107 | dependency('rollup-plugin-node-resolve'),
108 | dependency('rollup-plugin-babel'),
109 | dependency('rollup-plugin-commonjs'),
110 | dependency('rollup-plugin-node-resolve'),
111 | dependency('rollup-plugin-typescript'),
112 | dependency('rollup-plugin-typescript2'),
113 | dependency('rollup-plugin-json'),
114 | dependency('@babel/core'),
115 | dependency('@babel/preset-env'),
116 | dependency('@babel/preset-typescript')
117 | ] : build === 'gulp' ? [
118 | dependency('gulp'),
119 | dependency('gulp-autoprefixer'),
120 | dependency('gulp-babel'),
121 | dependency('gulp-concat'),
122 | dependency('gulp-concat-css'),
123 | dependency('gulp-cssnano'),
124 | dependency('gulp-less'),
125 | dependency('gulp-clean-css'),
126 | dependency('sass'),
127 | dependency('gulp-sass'),
128 | dependency('gulp-if'),
129 | dependency('gulp-sourcemaps'),
130 | dependency('gulp-ts-alias'),
131 | dependency('gulp-typescript'),
132 | dependency('through2'),
133 | dependency('@babel/core'),
134 | dependency('@babel/preset-env'),
135 | dependency('@babel/preset-react'),
136 | dependency('@babel/preset-typescript'),
137 | dependency('@babel/plugin-transform-runtime'),
138 | dependency('@babel/plugin-proposal-class-properties'),
139 | project_type === 'component-vue' ? dependency('@omni-door/gulp-plugin-vue-sfc') : ''
140 | ] : build === 'tsc' ? [
141 | dependency('ts-patch'),
142 | dependency('typescript'),
143 | dependency('typescript-transform-paths'),
144 | project_type === 'component-vue'? dependency('vue-tsc') : ''
145 | ] : build === 'next' ? [
146 | dependency('next'),
147 | dependency('next-url-prettifier'),
148 | dependency('@zeit/next-css'),
149 | dependency('@zeit/next-less'),
150 | dependency('@zeit/next-sass'),
151 | dependency('next-compose-plugins'),
152 | dependency('next-transpile-modules')
153 | ]
154 | : [];
155 |
156 | return arr2str(buildDependencies);
157 | }
--------------------------------------------------------------------------------
/src/commands/build/gulp.ts:
--------------------------------------------------------------------------------
1 | export default function (config: {
2 | srcDir: string;
3 | outDir: string;
4 | esmDir: string;
5 | configurationPath?: string;
6 | configFileName?: string;
7 | pkjFieldName?: string;
8 | }) {
9 | const { srcDir = 'src', outDir = 'lib', esmDir = 'es', configurationPath, pkjFieldName = 'omni', configFileName = 'omni.config.js' } = config;
10 |
11 | return `'use strict';
12 |
13 | const path = require('path');
14 | const { requireCwd } = require('@omni-door/utils');
15 | const gulp = requireCwd('gulp');
16 | const babel = requireCwd('gulp-babel');
17 | const less = requireCwd('gulp-less', true);
18 | const sass = requireCwd('gulp-sass', true)(requireCwd('sass', true));
19 | const gulpif = requireCwd('gulp-if');
20 | const alias = requireCwd('gulp-ts-alias');
21 | const typescript = requireCwd('gulp-typescript');
22 | const sourcemaps = requireCwd('gulp-sourcemaps');
23 | const autoprefixer = requireCwd('gulp-autoprefixer');
24 | const cssnano = requireCwd('gulp-cssnano');
25 | const concat = requireCwd('gulp-concat');
26 | const concatCss = requireCwd('gulp-concat-css');
27 | const cleanCSS = requireCwd('gulp-clean-css');
28 | const through2 = requireCwd('through2');
29 | const replace = requireCwd('gulp-replace-path', true);
30 | const vueSFC = requireCwd('@omni-door/gulp-plugin-vue-sfc', true);
31 |
32 | const ppkj = requireCwd('./package.json');
33 | const configFilePath = (ppkj && ppkj.${pkjFieldName} && ppkj.${pkjFieldName}.filePath) || './${configFileName}';
34 | const configs = requireCwd(configFilePath);
35 | ${configurationPath ? `const customConfig = require('${configurationPath}')
36 | ` : ''}
37 | const { build } = configs || {};
38 | const { configuration = ({ task }) => { const [compileCJS, compileES, compileSFC, ...rest] = task; return [gulp.series(compileCJS, compileES, compileSFC), ...rest]; } } = build || {};
39 | const project = typescript && typescript.createProject('tsconfig.json');
40 |
41 | const params = {
42 | dest: {
43 | lib: '${outDir}',
44 | es: '${esmDir}'
45 | },
46 | styles: [
47 | '${srcDir}/**/*.{css,less,scss,sass}',
48 | '!${srcDir}/**/{demo,__demo__,test,__test__,stories,__stories__}/*.{css,less,scss,sass}'
49 | ],
50 | scripts: [
51 | '${srcDir}/**/*.{ts,tsx,js,jsx}',
52 | '!${srcDir}/**/{demo,__demo__,test,__test__,stories,__stories__}/*.{ts,tsx,js,jsx}',
53 | '!${srcDir}/**/*.{demo,test,stories}.{ts,tsx,js,jsx}'
54 | ],
55 | vue: [
56 | '${srcDir}/**/*.vue',
57 | '!${srcDir}/**/{demo,__demo__,test,__test__,stories,__stories__}/*.{vue,ts,tsx,js,jsx}',
58 | '!${srcDir}/**/*.{demo,test,stories}.{vue,ts,tsx,js,jsx}'
59 | ]
60 | };
61 |
62 | function cssInjection (content) {
63 | return content
64 | .replace(/\\\/style\\\/?'/g, "/style/css\'")
65 | .replace(/\\\/style\\\/?"/g, '/style/css\"')
66 | .replace(/\\\.(less|scss|sass)/g, '.css');
67 | }
68 |
69 | function compileScripts (babelEnv, destDir) {
70 | const { scripts } = params;
71 | process.env.BABEL_ENV = babelEnv;
72 | return gulp
73 | .src(scripts)
74 | .pipe((alias && project) ? alias({ configuration: project.config }) : through2.obj())
75 | .pipe(sourcemaps ? sourcemaps.init() : through2.obj())
76 | .pipe(project ? project() : through2.obj())
77 | .pipe(babel({ root: process.cwd() }))
78 | .pipe(replace ? replace(/\\.vue("|'){1}/g, '.js$1') : through2.obj())
79 | .pipe(
80 | through2.obj(function (file, encoding, next) {
81 | this.push(file.clone());
82 | if (file.path.match(/(\\\/|\\\\\)style(\\\/|\\\\\)index\\\.js/)) {
83 | const content = file.contents.toString(encoding);
84 | file.contents = Buffer.from(cssInjection(content));
85 | file.path = file.path.replace(/index\\\.js/, 'css.js');
86 | this.push(file);
87 | next();
88 | } else {
89 | next();
90 | }
91 | })
92 | )
93 | .pipe(sourcemaps ? sourcemaps.write({ sourceRoot: file => path.relative(path.join(file.cwd, file.path), file.base) }) : through2.obj())
94 | .pipe(gulp.dest(destDir));
95 | }
96 |
97 | function compileCJS () {
98 | const { dest } = params;
99 | return compileScripts('cjs', dest.lib);
100 | }
101 |
102 | function compileES () {
103 | const { dest } = params;
104 | return compileScripts('es', dest.es);
105 | }
106 |
107 | function compileSFC () {
108 | const { dest, vue } = params;
109 | return gulp
110 | .src(vue)
111 | .pipe(sourcemaps ? sourcemaps.init() : through2.obj())
112 | .pipe(vueSFC ? vueSFC.default({ ext: '.ts' }) : through2.obj())
113 | .pipe((alias && project) ? alias({ configuration: project.config }) : through2.obj())
114 | .pipe(project ? project() : through2.obj())
115 | .pipe(babel({ root: process.cwd() }))
116 | .pipe(replace ? replace(/\\.vue("|'){1}/g, '.js$1') : through2.obj())
117 | .pipe(sourcemaps ? sourcemaps.write({ sourceRoot: file => path.relative(path.join(file.cwd, file.path), file.base) }) : through2.obj())
118 | .pipe(gulp.dest(dest.lib))
119 | .pipe(gulp.dest(dest.es));
120 | }
121 |
122 | function copyStylesheet () {
123 | const { dest, styles } = params;
124 | return gulp
125 | .src(styles)
126 | .pipe(gulp.dest(dest.lib))
127 | .pipe(gulp.dest(dest.es));
128 | }
129 |
130 | function handleLess (file) {
131 | let result = false;
132 | if (!less) return result;
133 | if (file.path.match(/.less$/)) {
134 | result = true;
135 | }
136 |
137 | return result;
138 | }
139 |
140 | function handleSass (file) {
141 | let result = false;
142 | if (!sass) return result;
143 | if (file.path.match(/.(scss|sass)$/)) {
144 | result = true;
145 | }
146 |
147 | return result;
148 | }
149 |
150 | function trans2css() {
151 | const { dest, styles } = params;
152 | return gulp
153 | .src(styles)
154 | .pipe(gulpif(handleLess, less ? less() : through2.obj()))
155 | .pipe(gulpif(handleSass, sass ? sass() : through2.obj()))
156 | .pipe(autoprefixer())
157 | .pipe(cssnano({ zindex: false, reduceIdents: false }))
158 | .pipe(gulp.dest(dest.lib))
159 | .pipe(gulp.dest(dest.es))
160 | .pipe(concatCss('index.css'))
161 | .pipe(concat('index.css'))
162 | .pipe(cleanCSS())
163 | .pipe(gulp.dest(dest.lib))
164 | .pipe(gulp.dest(dest.es));
165 | }
166 |
167 | const builds = ${
168 | configurationPath
169 | ? 'customConfig'
170 | : 'gulp.parallel.apply(gulp, configuration({ task: [compileCJS, compileES, compileSFC, copyStylesheet, trans2css], params }));'
171 | }
172 |
173 | exports.build = builds;
174 |
175 | exports.default = builds;
176 | `;
177 | }
--------------------------------------------------------------------------------
/src/commands/build/index.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { execSync } from'child_process';
4 | import fsExtra from 'fs-extra';
5 | import inquirer from 'inquirer';
6 | import del from 'del';
7 | import rollupConfig from './rollup';
8 | import webpackConfig from './webpack';
9 | import gulpConfig from './gulp';
10 | import {
11 | spinner,
12 | exec,
13 | logErr,
14 | logInfo,
15 | logWarn,
16 | logSuc,
17 | logCongrat,
18 | logEmph,
19 | logTime,
20 | underline,
21 | italic,
22 | outputFile,
23 | nodeVersionCheck
24 | } from '@omni-door/utils';
25 | import { getHandlers, logo, signal } from '../../utils';
26 | import dependencies_build from './dependencies_build';
27 | import release from '../release';
28 | /* import types */
29 | import type { BUILD } from '@omni-door/utils';
30 | import type { OmniConfig, OmniPlugin } from '../../index.d';
31 |
32 | export default async function (
33 | config: OmniConfig | null,
34 | buildTactic?: {
35 | config?: string;
36 | verify?: boolean;
37 | buildConfig?: string;
38 | pkjFieldName?: string;
39 | configFileName?: string;
40 | },
41 | autoBuild?: boolean
42 | ) {
43 | try {
44 | // node version pre-check
45 | await nodeVersionCheck('10.13.0');
46 | } catch (e) {
47 | logWarn(e as string);
48 | }
49 |
50 | if (!config || JSON.stringify(config) === '{}') {
51 | logWarn('Please initialize project first');
52 | logWarn('请先初始化项目');
53 | return process.exit(0);
54 | }
55 |
56 | // bind exit signals
57 | signal();
58 |
59 | const message = 'Building(开始构建)';
60 | logTime('BUILD(项目构建)');
61 | logInfo(message);
62 |
63 | const { type, template, build, release: configRelease, plugins } = config;
64 |
65 | const {
66 | autoRelease,
67 | srcDir,
68 | outDir,
69 | esmDir = '',
70 | hash,
71 | tool,
72 | reserve = {},
73 | preflight
74 | } = build;
75 |
76 | const {
77 | typescript = false,
78 | test = false,
79 | eslint = false,
80 | prettier = false,
81 | stylelint = false
82 | } = preflight || {};
83 |
84 | const CWD = process.cwd();
85 | const { config: configPath, verify, buildConfig, pkjFieldName, configFileName } = buildTactic || {};
86 | let configurationPath = configPath && path.resolve(CWD, configPath);
87 | if (configurationPath && !fs.existsSync(configurationPath)) configurationPath = void(0);
88 |
89 | if (!outDir || !srcDir) {
90 | handleBuildErr('The "srcDir" or "outDir" were missed in configuration file(配置文件中未定义 "srcDir" 或 "outDir")')();
91 | }
92 |
93 | function handleBuildSuc (msg?: string) {
94 | msg = msg || 'Building completed(构建成功)!';
95 |
96 | return function (isExit?: boolean) {
97 | logCongrat(msg!);
98 | isExit && process.exit(0);
99 | };
100 | }
101 |
102 | function handleBuildErr (msg?: string) {
103 | msg = msg || 'Building failed(构建失败)!';
104 |
105 | return function (err?: string) {
106 | err && logErr(err);
107 | msg && logErr(msg);
108 | type !== 'toolkit' && spinner.state('fail');
109 | process.exit(1);
110 | };
111 | }
112 |
113 | function installDenpendencies (build: BUILD) {
114 | return inquirer.prompt([
115 | {
116 | name: 'install',
117 | type: 'confirm',
118 | message: `${logo()} Automatic install dependencies(自动安装所需要的依赖)?`,
119 | default: true
120 | }
121 | ]).then(async answers => {
122 | const { install } = answers;
123 | if (install) {
124 | const dependencies = await dependencies_build({ build, project_type: type });
125 |
126 | // install tool pre-check
127 | let iTool = 'pnpm i --save-dev';
128 | try {
129 | execSync('pnpm -v', { stdio: 'ignore' });
130 | } catch (e) {
131 | iTool = 'yarn add -D';
132 | try {
133 | execSync('yarn -v', { stdio: 'ignore' });
134 | } catch (e) {
135 | iTool = 'npm i --save-dev';
136 | }
137 | }
138 |
139 | return exec([
140 | `${iTool} ${dependencies}`
141 | ],
142 | () => {
143 | logSuc('The dependencies install completed');
144 | logSuc('构建依赖安装完毕');
145 | return true;
146 | },
147 | err => {
148 | logWarn(err);
149 | logWarn('The dependencies install occured some accidents');
150 | logWarn('依赖安装发生了错误');
151 | return false;
152 | });
153 | } else {
154 | return false;
155 | }
156 | });
157 | }
158 |
159 | function copyStylesheet (dir: string, originDir?: string) {
160 | const list = fs.readdirSync(dir);
161 | list.map((v, k) => {
162 | const filePath = path.resolve(dir, v);
163 | const stats = fs.statSync(filePath);
164 | if (stats.isDirectory()) {
165 | if (!/^.*(test|stories).*$/.test(v)) {
166 | copyStylesheet(filePath, originDir || dir);
167 | }
168 | } else if (/.(css|scss|sass|less)$/.test(v)) {
169 | const relativePath = path.relative(originDir || dir, filePath);
170 | const destPath = path.resolve(outDir, relativePath);
171 | const emsPath = esmDir && path.resolve(esmDir, relativePath);
172 | fsExtra.ensureDirSync(path.resolve(destPath, '..'));
173 | fsExtra.copySync(filePath, destPath);
174 | if (emsPath) {
175 | fsExtra.ensureDirSync(path.resolve(emsPath, '..'));
176 | fsExtra.copySync(filePath, emsPath);
177 | }
178 | }
179 | });
180 | }
181 |
182 | function copyReserves (reserves: (string | { srcPath: string; relativePath?: string; })[]) {
183 | for (let i = 0, len = reserves.length; i < len; i++) {
184 | const reserveItem = reserves[i];
185 | let srcPath = '';
186 | let relativePath = '';
187 | if (typeof reserveItem === 'string') {
188 | srcPath = reserveItem;
189 | } else {
190 | srcPath = reserveItem.srcPath;
191 | relativePath = reserveItem.relativePath || '';
192 | }
193 |
194 | let stats;
195 | try {
196 | stats = fs.statSync(srcPath);
197 | } catch (error) {
198 | logWarn(`The path of "${srcPath}" is invaild`);
199 | logWarn(`"${srcPath}" 是一个无效的路径`);
200 | continue;
201 | }
202 |
203 | relativePath = relativePath || path.relative(srcDir, srcPath);
204 | const destPath = path.resolve(outDir, relativePath);
205 | const emsPath = esmDir && path.resolve(esmDir, relativePath);
206 | if (stats.isDirectory()) {
207 | fsExtra.ensureDirSync(destPath);
208 | emsPath && fsExtra.ensureDirSync(emsPath);
209 | } else if (stats.isFile()) {
210 | fsExtra.ensureDirSync(path.resolve(destPath, '..'));
211 | emsPath && fsExtra.ensureDirSync(path.resolve(emsPath, '..'));
212 | } else {
213 | logWarn(`The file or directory path which is "${srcPath}" cannot be found`);
214 | logWarn(`"${srcPath}" 不是有效的文件或文件夹路径`);
215 | continue;
216 | }
217 | fsExtra.copySync(srcPath, destPath);
218 | emsPath && fsExtra.copySync(srcPath, emsPath);
219 | }
220 | }
221 |
222 | try {
223 | if (verify && test) {
224 | await exec(['npm test'], () => logSuc('Unit Test!'), handleBuildErr('The unit test not pass(单元测试失败)'));
225 | }
226 |
227 | if (verify && eslint) {
228 | await exec(['npm run lint:es'], () => logSuc('Eslint!'), handleBuildErr(`The eslint not pass(eslint校验失败) \n try to exec(尝试执行): ${underline('npm run lint:es_fix')}`));
229 | }
230 |
231 | if (verify && prettier) {
232 | await exec(['npm run lint:prettier'], () => logSuc('Prettier!'), handleBuildErr(`The prettier not pass(prettier校验失败) \n try to exec(尝试执行): ${underline('npm run lint:prettier_fix')}`));
233 | }
234 |
235 | if (verify && stylelint) {
236 | await exec(['npm run lint:style'], () => logSuc('Stylelint!'), handleBuildErr(`The stylelint not pass(stylelint校验失败) \n try to exec(尝试执行): ${underline('npm run lint:style_fix')}`));
237 | }
238 |
239 | let realOutDir: string = '';
240 | const buildCliArr = [];
241 | const buildCliPath = {
242 | tsc: path.resolve(CWD, 'node_modules/typescript/bin/tsc'),
243 | tspc: path.resolve(CWD, 'node_modules/ts-patch/bin/tspc.js'),
244 | vuetsc: path.resolve(CWD, 'node_modules/vue-tsc/bin/vue-tsc.js'),
245 | rollup: path.resolve(CWD, 'node_modules/rollup/dist/bin/rollup'),
246 | webpack: path.resolve(CWD, 'node_modules/webpack-cli/bin/cli.js'),
247 | gulp: path.resolve(CWD, 'node_modules/gulp/bin/gulp.js'),
248 | next: path.resolve(CWD, 'node_modules/next/dist/bin/next')
249 | };
250 | if (tool === 'tsc') {
251 | if (!typescript) {
252 | handleBuildErr('The typescript had been forbidden(已禁用 typescript,无法完成构建)')();
253 | }
254 |
255 | let tscPath = buildCliPath.tsc;
256 | // ts-patch is preferred
257 | if (fs.existsSync(buildCliPath.tspc)) tscPath = buildCliPath.tspc;
258 |
259 | if (!fs.existsSync(tscPath)) {
260 | logWarn('Please install typescript first');
261 | logWarn('请先安装 typescript 相关依赖');
262 | const is_go_on = await installDenpendencies('tsc');
263 | if (!is_go_on) return process.exit(0);
264 | tscPath = buildCliPath.tspc;
265 | }
266 |
267 | buildCliArr.push(`${tscPath} --outDir ${outDir} --project ${configurationPath || path.resolve(CWD, 'tsconfig.json')} --rootDir ${srcDir}`);
268 | esmDir && buildCliArr.push(`${tscPath} --module ESNext --target ES6 --outDir ${esmDir} --project ${configurationPath || path.resolve(CWD, 'tsconfig.json')} --rootDir ${srcDir}`);
269 | realOutDir = outDir;
270 | } else if (type === 'ssr-react') {
271 | const nextPath = buildCliPath.next;
272 |
273 | if (!fs.existsSync(nextPath)) {
274 | logWarn('Please install webpack first');
275 | logWarn('请先安装 webpack 相关依赖');
276 | const is_go_on = await installDenpendencies('next');
277 | if (!is_go_on) return process.exit(0);
278 | }
279 |
280 | buildCliArr.push(`${nextPath} build`);
281 | } else {
282 | const content_rollup = !buildConfig && type === 'toolkit' && rollupConfig({ ts: typescript, multiOutput: true, srcDir, outDir, esmDir, configurationPath, pkjFieldName, configFileName });
283 | const content_webpack = !buildConfig && (type === 'spa-react' || type === 'spa-react-pc' || type === 'spa-vue') && webpackConfig({ ts: typescript, multiOutput: false, srcDir, outDir, configurationPath, pkjFieldName, configFileName, hash });
284 | const content_gulp = !buildConfig && (type === 'component-react' || type === 'component-vue') && gulpConfig({ srcDir, outDir, esmDir, configurationPath, pkjFieldName, configFileName });
285 | const content_config = buildConfig || content_rollup || content_webpack || content_gulp;
286 |
287 | // put temporary file for build process
288 | if (content_config) {
289 | const buildConfigPath = path.resolve(__dirname, '../../../', '.omni_cache/build.config.js');
290 |
291 | let is_go_on = true;
292 | if (type === 'toolkit') {
293 | const rollupPath = buildCliPath.rollup;
294 |
295 | if (!fs.existsSync(rollupPath)) {
296 | logWarn('Please install rollup first');
297 | logWarn('请先安装 rollup 相关依赖');
298 | is_go_on = await installDenpendencies('rollup');
299 | }
300 |
301 | buildCliArr.push(`${rollupPath} -c ${buildConfigPath}`);
302 | } else if (type === 'spa-react' || type === 'spa-react-pc' || type === 'spa-vue') {
303 | const webpackPath = buildCliPath.webpack;
304 |
305 | if (!fs.existsSync(webpackPath)) {
306 | logWarn('Please install webpack first');
307 | logWarn('请先安装 webpack 相关依赖');
308 | is_go_on = await installDenpendencies('webpack');
309 | }
310 |
311 | buildCliArr.push(`${webpackPath} --config ${buildConfigPath}`);
312 | } else if (type === 'component-react' || type === 'component-vue') {
313 | let tscPath = buildCliPath.tsc;
314 | const vTscPath = buildCliPath.vuetsc;
315 | const gulpPath = buildCliPath.gulp;
316 | // ts-patch is preferred
317 | if (fs.existsSync(buildCliPath.tspc)) tscPath = buildCliPath.tspc;
318 |
319 | if (typescript && (!fs.existsSync(tscPath) || (type === 'component-vue' && !fs.existsSync(vTscPath)))) {
320 | logWarn('Please install typescript first');
321 | logWarn('请先安装 typescript 相关依赖');
322 | is_go_on = await installDenpendencies('tsc');
323 | if (is_go_on) tscPath = buildCliPath.tspc;
324 | }
325 | if (is_go_on && !fs.existsSync(gulpPath)) {
326 | logWarn('Please install gulp first');
327 | logWarn('请先安装 gulp 相关依赖');
328 | is_go_on = await installDenpendencies('gulp');
329 | }
330 |
331 | buildCliArr.push(
332 | typescript ? `${tscPath} --outDir ${outDir} --project ${configurationPath || path.resolve(CWD, 'tsconfig.json')} --emitDeclarationOnly --rootDir ${srcDir}` : '',
333 | typescript && esmDir ? `${tscPath} --module ESNext --target ES6 --outDir ${esmDir} --project ${configurationPath || path.resolve(CWD, 'tsconfig.json')} --emitDeclarationOnly --rootDir ${srcDir}` : '',
334 | typescript && type === 'component-vue' ? `${vTscPath} --outDir ${outDir} --project ${configurationPath || path.resolve(CWD, 'tsconfig.json')} --emitDeclarationOnly --rootDir ${srcDir}` : '',
335 | typescript && esmDir && type === 'component-vue' ? `${vTscPath} --module ESNext --target ES6 --outDir ${esmDir} --project ${configurationPath || path.resolve(CWD, 'tsconfig.json')} --emitDeclarationOnly --rootDir ${srcDir}` : '',
336 | `${gulpPath} --gulpfile ${buildConfigPath} --cwd ${CWD}`
337 | );
338 | }
339 |
340 | if (!is_go_on) return process.exit(0);
341 |
342 | outputFile({
343 | file_path: buildConfigPath,
344 | file_content: content_config
345 | });
346 |
347 | if (type === 'spa-react' || type === 'spa-react-pc' || type === 'spa-vue') {
348 | const bConfig = require(buildConfigPath);
349 | realOutDir = (bConfig && bConfig.output && bConfig.output.path) || outDir;
350 | }
351 | }
352 | }
353 |
354 | // loading
355 | if (type !== 'toolkit') {
356 | spinner.color('green');
357 | spinner.prefix('moon');
358 | spinner.state('start', 'Building, please wait patiently(项目构建中)');
359 | }
360 |
361 | try {
362 | del.sync(realOutDir || outDir, {
363 | force: true
364 | });
365 | esmDir && del.sync(esmDir, {
366 | force: true
367 | });
368 | } catch (err) {
369 | logWarn(err as string);
370 | }
371 |
372 | await exec(buildCliArr, async function () {
373 | const { style, assets = [] } = reserve;
374 | if (type !== 'component-react' && type !== 'component-vue' && style) copyStylesheet(srcDir);
375 | copyReserves(assets);
376 |
377 | // handle build plugins
378 | const plugin_handles = plugins && plugins.length > 0 && getHandlers<'build'>(plugins as OmniPlugin<'build'>[], 'build');
379 | if (plugin_handles) {
380 | for (const name in plugin_handles) {
381 | const handler = plugin_handles[name];
382 | await handler({
383 | type,
384 | template,
385 | build,
386 | release: configRelease
387 | }, {
388 | verify,
389 | buildConfig
390 | });
391 | }
392 | }
393 |
394 | if (realOutDir && !fs.existsSync(realOutDir)) {
395 | handleBuildErr(`The output file ${realOutDir} doesn't exist(输出的 ${realOutDir} 文件不存在,构建失败)`)();
396 | } else {
397 | type !== 'toolkit' && spinner.state('stop');
398 | logTime('BUILD(项目构建)', true);
399 | const shouldExit = !(autoRelease || autoBuild);
400 | handleBuildSuc()(shouldExit);
401 | }
402 | }, handleBuildErr());
403 |
404 | // auto release
405 | if (!autoBuild && autoRelease) {
406 | logEmph(italic('Start auto release'));
407 | logEmph(italic('开始自动发布'));
408 | try {
409 | await release(config, { verify: false }, autoRelease);
410 | handleBuildSuc('Auto release success(自动发布成功)!')(true);
411 | } catch (err) {
412 | handleBuildErr('Auto release failed(自动发布失败)!')();
413 | }
414 | }
415 | } catch (err) {
416 | logErr(err as string);
417 | handleBuildErr('👆 Oops! Building process occured some accidents(糟糕!构建过程发生了点意外)!')();
418 | }
419 | }
--------------------------------------------------------------------------------
/src/commands/build/rollup.ts:
--------------------------------------------------------------------------------
1 | export default function (config: {
2 | ts: boolean;
3 | multiOutput: boolean;
4 | srcDir: string;
5 | outDir: string;
6 | esmDir: string;
7 | configurationPath?: string;
8 | configFileName?: string;
9 | pkjFieldName?: string;
10 | }) {
11 | const { ts, multiOutput, srcDir = 'src', outDir = 'lib', esmDir, configurationPath, pkjFieldName = 'omni', configFileName = 'omni.config.js' } = config;
12 |
13 | return `'use strict';
14 |
15 | const fs = require('fs');
16 | const path = require('path');
17 | const { requireCwd, logErr } = require('@omni-door/utils');
18 | const { nodeResolve: resolve } = requireCwd('@rollup/plugin-node-resolve', true) || {};
19 | const commonjs = requireCwd('@rollup/plugin-commonjs', true);
20 | const { babel } = requireCwd('@rollup/plugin-babel', true) || {};
21 | const json = requireCwd('@rollup/plugin-json', true);
22 | ${ts ? `const typescript = requireCwd('@rollup/plugin-typescript', true);
23 | ` : ''}
24 | const pkj = requireCwd('./package.json');
25 | const configFilePath = (pkj && pkj.${pkjFieldName} && pkj.${pkjFieldName}.filePath) || './${configFileName}';
26 | const configs = requireCwd(configFilePath);
27 | ${configurationPath ? `const customConfig = require('${configurationPath}')
28 | ` : ''}
29 | const { build } = configs || {};
30 | const { configuration = getConfig => getConfig(true) } = build || {};
31 |
32 | const extensions = ['.ts', '.js'];
33 | const tsExcludes = ['node_modules/**', '**/__test__/*'];
34 | const babelConfig = {
35 | exclude: 'node_modules/**',
36 | plugins: [['@babel/plugin-transform-runtime', { useESModules: false, corejs: 3 }]],
37 | babelHelpers: 'runtime',
38 | extensions
39 | };
40 | const resolveConfig = {
41 | extensions,
42 | preferBuiltins: true,
43 | browser: true
44 | };
45 | const tsconfig = {
46 | cjs: (dir = '', emit = false) => ({
47 | compilerOptions: {
48 | target: 'es5',
49 | module: 'esnext',
50 | declaration: emit,
51 | outDir: path.resolve('${outDir}', dir),
52 | emitDeclarationOnly: emit
53 | },
54 | exclude: tsExcludes
55 | }),
56 | esm: (dir = '', emit = false) => ({
57 | compilerOptions: {
58 | target: 'esnext',
59 | module: 'esnext',
60 | declaration: emit,
61 | outDir: path.resolve('${esmDir}', dir),
62 | emitDeclarationOnly: emit
63 | },
64 | exclude: tsExcludes
65 | })
66 | };
67 |
68 | let indexPath = '';
69 | const exts = ['ts', 'tsx', 'jsx', 'js'];
70 | for (let i = 0, len = exts.length; i < len; i++) {
71 | indexPath = path.resolve('${srcDir}', \`index.\${exts[i]}\`);
72 | if (fs.existsSync(indexPath)) break;
73 | if (i === len - 1) {
74 | logErr('请以 index 为名称指定正确的入口文件!(Please specify the correct entry file with name of index)');
75 | process.exit(1);
76 | }
77 | }
78 |
79 | function flatten (arr) {
80 | return arr.reduce(function(prev, next){
81 | return prev.concat(Array.isArray(next) ? flatten(next) : next);
82 | }, []);
83 | }
84 |
85 | function createConfig (bundle = true) {
86 | const filesPaths = [];
87 | ${multiOutput ? `const getEntryPath = (files, preDir) => {
88 | const len = files.length;
89 | for (let i = 0; i < len; i++) {
90 | const file = files[i];
91 | if (file.includes('test')) continue;
92 | const preDirPath = path.join(preDir, file);
93 | const filePath = path.resolve('${srcDir}', preDirPath);
94 | const stats = fs.statSync(filePath);
95 | if (stats.isDirectory()) {
96 | getEntryPath(fs.readdirSync(filePath), preDirPath);
97 | } else {
98 | let entryPath = '';
99 | const extArr = path.extname(filePath).split('.');
100 | entryPath = (extArr.length && !!~exts.indexOf(extArr[1])) ? filePath : '';
101 | if (!entryPath || !fs.existsSync(entryPath) || fs.existsSync(path.resolve(filePath, '.buildignore'))) {
102 | tsExcludes.push(\`\${filePath}/*\`);
103 | continue;
104 | }
105 | filesPaths.push({
106 | entry: entryPath,
107 | file: path.join(file, 'index.js'),
108 | dir: preDir
109 | });
110 | }
111 | }
112 | };
113 | getEntryPath(fs.readdirSync('${srcDir}'), '');
114 | ` : ''}
115 | return bundle ? [
116 | {
117 | input: indexPath,
118 | output: {
119 | file: '${outDir}/index.js',
120 | format: 'cjs',
121 | exports: 'named',
122 | compact: true
123 | },
124 | plugins: [
125 | resolve(resolveConfig),
126 | commonjs(),
127 | ${ts ? 'typescript(tsconfig.cjs(\'\', true)),' : ''}
128 | babel(babelConfig),
129 | json()
130 | ]
131 | },
132 | ${esmDir
133 | ? `
134 | {
135 | input: indexPath,
136 | output: {
137 | file: '${esmDir}/index.js',
138 | format: 'esm',
139 | compact: true
140 | },
141 | plugins: [
142 | resolve(resolveConfig),
143 | commonjs({ transformMixedEsModules: true, esmExternals: true }),
144 | ${ts ? 'typescript(tsconfig.esm(\'\', true)),' : ''}
145 | json()
146 | ]
147 | },`
148 | : ''}
149 |
150 | ...flatten(filesPaths.map(fileParams => {
151 | const { entry, file, dir } = fileParams;
152 | return [
153 | {
154 | input: entry,
155 | output: {
156 | dir: path.resolve('${outDir}', dir),
157 | format: 'cjs',
158 | exports: 'named',
159 | compact: true
160 | },
161 | plugins: [
162 | resolve(resolveConfig),
163 | commonjs(),
164 | ${ts ? 'typescript(tsconfig.cjs(dir)),' : ''}
165 | json()
166 | ]
167 | },
168 | ${esmDir
169 | ? `
170 | {
171 | input: entry,
172 | output: {
173 | dir: path.resolve('${esmDir}', dir),
174 | format: 'esm',
175 | compact: true
176 | },
177 | plugins: [
178 | resolve(resolveConfig),
179 | commonjs({ transformMixedEsModules: true, esmExternals: true }),
180 | ${ts ? 'typescript(tsconfig.esm(dir)),' : ''}
181 | json()
182 | ]
183 | }`
184 | : ''}
185 | ];
186 | }))
187 | ] : [
188 | {
189 | input: indexPath,
190 | output: {
191 | dir: '${outDir}',
192 | format: 'cjs',
193 | exports: 'named',
194 | compact: true,
195 | preserveModules: true,
196 | preserveModulesRoot: '${srcDir}'
197 | },
198 | plugins: [
199 | resolve(resolveConfig),
200 | commonjs(),
201 | ${ts ? 'typescript(tsconfig.cjs(\'\', true)),' : ''}
202 | babel(babelConfig),
203 | json()
204 | ]
205 | },
206 | ${esmDir
207 | ? `
208 | {
209 | input: indexPath,
210 | output: {
211 | dir: '${esmDir}',
212 | format: 'esm',
213 | compact: true,
214 | preserveModules: true,
215 | preserveModulesRoot: '${srcDir}'
216 | },
217 | plugins: [
218 | resolve(resolveConfig),
219 | commonjs({ transformMixedEsModules: true, esmExternals: true }),
220 | ${ts ? 'typescript(tsconfig.esm(\'\', true)),' : ''}
221 | json()
222 | ]
223 | },`
224 | : ''}
225 | ]
226 | };
227 |
228 | module.exports = ${
229 | configurationPath
230 | ? 'typeof customConfig === \'function\' ? customConfig((bundle = true) => createConfig(bundle)) : customConfig'
231 | : 'configuration((bundle = true) => createConfig(bundle));'
232 | }
233 | `;
234 | }
--------------------------------------------------------------------------------
/src/commands/build/webpack.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | /* import types */
3 | import type { HASH } from '@omni-door/utils';
4 |
5 | export default function (config: {
6 | ts: boolean;
7 | multiOutput: boolean;
8 | srcDir: string;
9 | outDir: string;
10 | configurationPath?: string;
11 | pkjFieldName?: string;
12 | configFileName?: string;
13 | hash?: boolean | HASH;
14 | }) {
15 | const { multiOutput, srcDir = path.resolve(__dirname, '../src/'), outDir = path.resolve(__dirname, '../lib/'), configurationPath, pkjFieldName = 'omni', configFileName = 'omni.config.js', hash } = config;
16 | const hashType =
17 | typeof hash === 'string'
18 | ? hash
19 | : hash
20 | ? 'contenthash'
21 | : hash;
22 |
23 | return `'use strict';
24 |
25 | const fs = require('fs');
26 | const path = require('path');
27 | const { requireCwd } = require('@omni-door/utils');
28 | const merge = requireCwd('webpack-merge');
29 |
30 | const ppkj = requireCwd('./package.json');
31 | const configFilePath = (ppkj && ppkj.${pkjFieldName} && ppkj.${pkjFieldName}.filePath) || './${configFileName}';
32 | const configs = requireCwd(configFilePath);
33 | ${configurationPath ? `const customConfig = require('${configurationPath}')` : ''}
34 |
35 | const { build } = configs || {};
36 | const { configuration = config => config } = build || {};
37 |
38 | ${
39 | multiOutput
40 | ? `const entriesPath = '${srcDir}';
41 | const entriesList = getFolders(entriesPath);
42 |
43 | function getFolders (folderPath) {
44 | const list = fs.readdirSync(folderPath)
45 | const folderList = list.filter((v, k) => {
46 | const stats = fs.statSync(\`\${folderPath}/\${v}\`);
47 | return stats.isDirectory();
48 | })
49 | return folderList;
50 | }`
51 | : ''
52 | }
53 |
54 | let indexPath = '';
55 | const exts = ['ts', 'tsx', 'js', 'jsx'];
56 | for (let i = 0, len = exts.length; i < len; i++) {
57 | indexPath = path.resolve('${srcDir}', \`index.\${exts[i]}\`);
58 | if (fs.existsSync(indexPath)) break;
59 | }
60 | const entry = {
61 | index: indexPath
62 | };
63 |
64 | ${
65 | multiOutput
66 | ? `entriesList.forEach(v => {
67 | if (v !== 'style' && v !== 'styles') {
68 | let entryPath = '';
69 | for (let i = 0, len = exts.length; i < len; i++) {
70 | entryPath = path.resolve('${srcDir}', \`\${v}/index.\${exts[i]}\`);
71 | if (fs.existsSync(entryPath)) break;
72 | }
73 | entry[v] = entryPath;
74 | }
75 | })`
76 | : ''
77 | }
78 |
79 | const basicConfig = {
80 | entry,
81 | output: {
82 | filename: '${hashType ? `[name].[${hashType}:8].js` : '[name].js'}',
83 | path: '${outDir}'
84 | },
85 | mode: 'production'
86 | };
87 |
88 | module.exports = ${
89 | configurationPath
90 | ? 'merge(basicConfig, customConfig);'
91 | : 'configuration(basicConfig);'
92 | }`;
93 | }
--------------------------------------------------------------------------------
/src/commands/commands.ts:
--------------------------------------------------------------------------------
1 | import initial from './initial';
2 | import dev from './dev';
3 | import start from './start';
4 | import newTpl from './new';
5 | import build from './build';
6 | import release from './release';
7 | import { setLogo, setBrand } from '@omni-door/utils';
8 |
9 | export { default as initial } from './initial';
10 | export { default as dev } from './dev';
11 | export { default as start } from './start';
12 | export { default as newTpl } from './new';
13 | export { default as build } from './build';
14 | export { default as release } from './release';
15 | export { setLogo, setBrand } from '@omni-door/utils';
16 |
17 | export default {
18 | initial,
19 | dev,
20 | start,
21 | new: newTpl,
22 | build,
23 | release,
24 | setLogo,
25 | setBrand
26 | };
--------------------------------------------------------------------------------
/src/commands/dev/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import dev from '../index';
4 | import run from '../run';
5 | import server from '../server';
6 |
7 | describe('dev command test', function () {
8 | it('type checking', function () {
9 | expect(dev).to.be.a('function');
10 | expect(run).to.be.a('function');
11 | expect(server).to.be.a('function');
12 | });
13 | });
--------------------------------------------------------------------------------
/src/commands/dev/index.ts:
--------------------------------------------------------------------------------
1 | import run from './run';
2 | import { logWarn, nodeVersionCheck } from '@omni-door/utils';
3 | import { signal } from '../../utils';
4 | /* import types */
5 | import type { OmniConfig } from '../../index.d';
6 |
7 | function handleException (msg?: string) {
8 | logWarn(msg || 'Oops! Some unknown errors have occurred(发生了一些未知错误)!');
9 | process.exit(0);
10 | }
11 |
12 | export default async function (config: OmniConfig | null, options: {
13 | port?: number | string;
14 | hostname?: string;
15 | }) {
16 | try {
17 | // node version pre-check
18 | await nodeVersionCheck('8');
19 | } catch (e) {
20 | logWarn(e as string);
21 | }
22 |
23 | if (!config || JSON.stringify(config) === '{}') {
24 | handleException('Please initialize project first(请先初始化项目)!');
25 | }
26 |
27 | const p = options.port;
28 | const h = options.hostname;
29 | const { type, dev, server } = config!;
30 |
31 | if (!dev || JSON.stringify(dev) === '{}') {
32 | handleException('The dev field is missing in config file(配置文件 dev 字段缺失)!');
33 | }
34 |
35 | const { serverType: server_type, ...serverOptions } = server || {};
36 | const {
37 | port,
38 | host,
39 | devMiddlewareOptions,
40 | webpack,
41 | proxy,
42 | middleware,
43 | serverType = 'default',
44 | ...rest
45 | } = { ...serverOptions, ...dev };
46 |
47 | // bind exit signals
48 | signal();
49 |
50 | const EWServerList = [ 'spa-react', 'spa-react-pc', 'spa-vue' ];
51 | if (~EWServerList.indexOf(type) && !webpack) {
52 | handleException(`The ${type}-app missing the dev-server webpack-config(${type}应用 缺少开发服务webpack配置文件)!`);
53 | }
54 |
55 | const _port = !isNaN(+p!)
56 | ? +p!
57 | : !isNaN(+port!)
58 | ? +port!
59 | : 6200;
60 |
61 | run({
62 | ...rest,
63 | p: _port,
64 | host: h || host,
65 | webpackConfig: webpack!,
66 | devMiddlewareOptions,
67 | proxyConfig: proxy,
68 | middlewareConfig: middleware,
69 | serverType,
70 | projectType: type
71 | });
72 | }
--------------------------------------------------------------------------------
/src/commands/dev/open.chrome.applescript:
--------------------------------------------------------------------------------
1 | property targetTab: null
2 | property targetTabIndex: -1
3 | property targetWindow: null
4 | property theProgram: "Google Chrome"
5 |
6 | on run argv
7 | set theURL to item 1 of argv
8 |
9 | -- Allow requested program to be optional,
10 | -- default to Google Chrome
11 | if (count of argv) > 1 then
12 | set theProgram to item 2 of argv
13 | end if
14 |
15 | using terms from application "Google Chrome"
16 | tell application theProgram
17 |
18 | if (count every window) = 0 then
19 | make new window
20 | end if
21 |
22 | -- 1: Looking for tab running debugger
23 | -- then, Reload debugging tab if found
24 | -- then return
25 | set found to my lookupTabWithUrl(theURL)
26 | if found then
27 | set targetWindow's active tab index to targetTabIndex
28 | tell targetTab to reload
29 | tell targetWindow to activate
30 | set index of targetWindow to 1
31 | return
32 | end if
33 |
34 | -- 2: Looking for Empty tab
35 | -- In case debugging tab was not found
36 | -- We try to find an empty tab instead
37 | set found to my lookupTabWithUrl("chrome://newtab/")
38 | if found then
39 | set targetWindow's active tab index to targetTabIndex
40 | set URL of targetTab to theURL
41 | tell targetWindow to activate
42 | return
43 | end if
44 |
45 | -- 3: Create new tab
46 | -- both debugging and empty tab were not found
47 | -- make a new tab with url
48 | tell window 1
49 | activate
50 | make new tab with properties {URL:theURL}
51 | end tell
52 | end tell
53 | end using terms from
54 | end run
55 |
56 | -- Function:
57 | -- Lookup tab with given url
58 | -- if found, store tab, index, and window in properties
59 | -- (properties were declared on top of file)
60 | on lookupTabWithUrl(lookupUrl)
61 | using terms from application "Google Chrome"
62 | tell application theProgram
63 | -- Find a tab with the given url
64 | set found to false
65 | set theTabIndex to -1
66 | repeat with theWindow in every window
67 | set theTabIndex to 0
68 | repeat with theTab in every tab of theWindow
69 | set theTabIndex to theTabIndex + 1
70 | if (theTab's URL as string) contains lookupUrl then
71 | -- assign tab, tab index, and window to properties
72 | set targetTab to theTab
73 | set targetTabIndex to theTabIndex
74 | set targetWindow to theWindow
75 | set found to true
76 | exit repeat
77 | end if
78 | end repeat
79 |
80 | if found then
81 | exit repeat
82 | end if
83 | end repeat
84 | end tell
85 | end using terms from
86 | return found
87 | end lookupTabWithUrl
88 |
--------------------------------------------------------------------------------
/src/commands/dev/open.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import { requireCwd } from '@omni-door/utils';
3 |
4 | export default async function (url: string) {
5 | if (process.platform === 'darwin') {
6 | // Will use the first open browser found from list
7 | const supportedChromiumBrowsers = [
8 | 'Google Chrome Canary',
9 | 'Google Chrome',
10 | 'Microsoft Edge',
11 | 'Brave Browser',
12 | 'Vivaldi',
13 | 'Chromium',
14 | ];
15 |
16 | for (const chromiumBrowser of supportedChromiumBrowsers) {
17 | try {
18 | // Try our best to reuse existing tab
19 | // on OSX Chromium-based browser with AppleScript
20 | execSync('ps cax | grep "' + chromiumBrowser + '"');
21 | execSync(
22 | 'osascript open.chrome.applescript "' +
23 | encodeURI(url) +
24 | '" "' +
25 | chromiumBrowser +
26 | '"',
27 | {
28 | cwd: __dirname,
29 | stdio: 'ignore',
30 | }
31 | );
32 | return true;
33 | } catch (err) {
34 | // Ignore errors.
35 | }
36 | }
37 | }
38 |
39 | // Fallback to open
40 | // (It will always open new tab)
41 | try {
42 | const open = requireCwd('open');
43 | await open(url, { wait: false, url: true });
44 | return true;
45 | } catch (err) {
46 | return false;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/commands/dev/run.ts:
--------------------------------------------------------------------------------
1 | import inquirer from 'inquirer';
2 | import { logErr, requireCwd } from '@omni-door/utils';
3 | import server from './server';
4 | import { logo } from '../../utils';
5 | /* import types */
6 | import type { ServerOptions } from './server';
7 |
8 | export default async function ({
9 | p,
10 | ...rest
11 | }: ServerOptions): Promise {
12 | try {
13 | const detectPort = requireCwd('detect-port');
14 |
15 | const _p = await detectPort(p).catch((err: any) => {
16 | logErr(err);
17 | return process.exit(1);
18 | });
19 |
20 | if (p !== _p) {
21 | inquirer.prompt([
22 | {
23 | name: 'changePort',
24 | type: 'confirm',
25 | message: `The port ${p} is not available, would you like to run on ${_p}(${logo()} ${p} 端口被占用了,切换到 ${_p})?`,
26 | default: true
27 | }
28 | ]).then(answer => {
29 | if (answer.changePort) {
30 | server(Object.assign(rest, { p: _p }));
31 | } else {
32 | process.exit(0);
33 | }
34 | });
35 | } else {
36 | server(Object.assign(rest, { p }));
37 | }
38 | } catch (err) {
39 | logErr(err as string);
40 | process.exit(1);
41 | }
42 | }
--------------------------------------------------------------------------------
/src/commands/dev/server.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import * as mkcert from 'mkcert';
4 | import {
5 | logWarn,
6 | logErr,
7 | requireCwd,
8 | exec,
9 | outputFile
10 | } from '@omni-door/utils';
11 | import { EWServer, KNServer } from '../servers';
12 | import open from './open';
13 | /* import types */
14 | import type { Config } from 'http-proxy-middleware';
15 | import type { EWServerParams } from '../servers';
16 | import type { PROJECT_TYPE } from '@omni-door/utils';
17 | import type { KoaApp, NextRouter, ServerType, PathParams, MiddleWareCallback, Method } from '../../index.d';
18 |
19 | export type KoaCtx = KoaApp.ParameterizedContext;
20 |
21 | // types-proxy
22 | export type ProxyItem = { route: PathParams; config: Config; };
23 | export type ProxyConfig = (ProxyItem | ProxyFn)[];
24 | export type ProxyFn = (params: {
25 | ip: string;
26 | port: number;
27 | host?: string;
28 | middlewareConfig?: MiddlewareConfig;
29 | }) => ProxyItem;
30 |
31 | // types-middleware
32 | export type MiddlewareItem = { route: PathParams; callback: MiddleWareCallback; method?: Method };
33 | export type MiddlewareConfig = (MiddlewareItem | MiddlewareFn)[];
34 | export type MiddlewareFn = (params: {
35 | ip: string;
36 | port: number;
37 | host?: string;
38 | proxyConfig?: ProxyConfig;
39 | }) => MiddlewareItem;
40 |
41 | // types-cors
42 | export type CorsConfig = {
43 | origin?: string | ((ctx: KoaCtx) => string);
44 | allowMethods?: string | string[];
45 | exposeHeaders?: string | string[];
46 | allowHeaders?: string | string[];
47 | maxAge?: string | number;
48 | credentials?: boolean | ((ctx: KoaCtx) => string);
49 | keepHeadersOnError?: boolean;
50 | secureContext?: boolean;
51 | privateNetworkAccess?: boolean;
52 | }
53 |
54 | // types-server
55 | type EWServerOptions = Pick
56 | export type ServerOptions = {
57 | p: number;
58 | host?: string;
59 | https?: boolean | { key: string; cert: string; };
60 | CA?: {
61 | organization?: string;
62 | countryCode?: string;
63 | state?: string;
64 | locality?: string;
65 | validityDays?: number;
66 | };
67 | proxyConfig?: ProxyConfig;
68 | middlewareConfig?: MiddlewareConfig;
69 | corsConfig?: CorsConfig;
70 | serverType: ServerType;
71 | projectType: PROJECT_TYPE;
72 | nextRouter?: NextRouter;
73 | } & EWServerOptions;
74 |
75 | async function server ({
76 | p,
77 | host,
78 | https: httpsConfig,
79 | CA,
80 | serverType,
81 | projectType,
82 | devMiddlewareOptions = {},
83 | webpackConfig,
84 | proxyConfig = [],
85 | middlewareConfig = [],
86 | nextRouter,
87 | corsConfig,
88 | favicon
89 | }: ServerOptions): Promise {
90 | try {
91 | const CWD = process.cwd();
92 | const ip = requireCwd('ip');
93 | const ipAddress: string = ip.address();
94 | const serverHost = host || '0.0.0.0';
95 | const openHost = host || ipAddress || '0.0.0.0';
96 | let serverUrl = openHost + ':' + p;
97 |
98 | const nextDevCli = `${path.resolve(CWD, 'node_modules/.bin/next')} dev --port ${p} --hostname ${serverHost}`;
99 | const ServerDevCli = {
100 | storybook: `${path.resolve(CWD, 'node_modules/.bin/storybook')} dev -p ${p} -h ${serverHost} --disable-telemetry --quiet --ci`,
101 | dumi: `PORT=${p} ${path.resolve(CWD, 'node_modules/.bin/dumi')} dev`,
102 | 'next-app': nextDevCli,
103 | 'next-pages': nextDevCli,
104 | nuxt: `${path.resolve(CWD, 'node_modules/.bin/nuxt')} dev --port ${p} --hostname ${serverHost}`
105 | };
106 | const autoOpenServer = [
107 | 'storybook',
108 | 'next-app',
109 | 'next-pages',
110 | 'dumi'
111 | ];
112 |
113 | const needCustomServer = !serverType || serverType === 'default' || serverType === 'express-webpack';
114 | if (needCustomServer) {
115 | let isHttps = false;
116 | let key, cert;
117 | if (httpsConfig) {
118 | if (typeof httpsConfig === 'boolean') {
119 | try {
120 | const cacheDirPath = path.resolve(__dirname, '../../../.omni_cache');
121 | const keyPath = path.resolve(cacheDirPath, `${openHost}-key.pem`);
122 | const certPath = path.resolve(cacheDirPath, `${openHost}-cert.pem`);
123 |
124 | if (fs.existsSync(keyPath)) {
125 | key = fs.readFileSync(keyPath);
126 | cert = fs.readFileSync(certPath);
127 | } else {
128 | const ca = await mkcert.createCA({
129 | organization: 'OMNI-DOOR',
130 | countryCode: 'CN',
131 | state: 'SHANGHAI',
132 | locality: 'SONGJIANG',
133 | validityDays: 365,
134 | ...CA
135 | });
136 |
137 | const certificate = await mkcert.createCert({
138 | domains: [openHost, '127.0.0.1', 'localhost'],
139 | validityDays: 365,
140 | caKey: ca.key,
141 | caCert: ca.cert
142 | });
143 | key = certificate.key;
144 | cert = certificate.cert;
145 | outputFile({
146 | file_path: keyPath,
147 | file_content: key
148 | });
149 | outputFile({
150 | file_path: certPath,
151 | file_content: cert
152 | });
153 | }
154 |
155 | isHttps = true;
156 | } catch (err) {
157 | logWarn(err as string);
158 | logWarn(`Failing to generate the certificate (生成证书失败)!\nYou can specify the certificate manually (可通过以下方式手动指定证书):
159 |
160 | https: {
161 | key: fs.readFileSync(path.resolve(\${your_path_to_key})),
162 | cert: fs.readFileSync(path.resolve(\${your_path_to_cert}))
163 | }`);
164 | isHttps = false;
165 | }
166 | } else {
167 | key = httpsConfig.key;
168 | cert = httpsConfig.cert;
169 | }
170 | }
171 |
172 | if (isHttps && (!key || !cert)) {
173 | logWarn('Missing the certificate, start the dev-server with http');
174 | logWarn('证书缺失,将以http启动开发服务');
175 | isHttps = false;
176 | }
177 |
178 | const serverBasicOptions = {
179 | middlewareConfig,
180 | proxyConfig,
181 | ipAddress,
182 | host: openHost,
183 | listenHost: serverHost,
184 | port: p,
185 | httpsConfig: isHttps ? { key, cert } : void 0
186 | };
187 |
188 | switch (projectType) {
189 | case 'ssr-react':
190 | KNServer({
191 | dev: process.env.NODE_ENV === 'production' ? false : true,
192 | nextRouter,
193 | corsConfig,
194 | ...serverBasicOptions
195 | });
196 | break;
197 | case 'ssr-vue':
198 | logWarn('Not support ssr-vue yet');
199 | logWarn('暂不支持 ssr-vue 项目');
200 | break;
201 | case 'spa-react':
202 | case 'spa-react-pc':
203 | case 'spa-vue':
204 | EWServer({
205 | webpackConfig,
206 | devMiddlewareOptions,
207 | favicon,
208 | ...serverBasicOptions
209 | });
210 | break;
211 | }
212 | } else {
213 | serverUrl = 'http://' + serverUrl;
214 | exec([ServerDevCli[serverType as keyof typeof ServerDevCli]]);
215 | if (~autoOpenServer.indexOf(serverType)) {
216 | const detectPort = requireCwd('detect-port');
217 | const openAfterPortAvailable = () => {
218 | setTimeout(() => {
219 | detectPort(p)
220 | .then((_p : number) => {
221 | if (_p === p) {
222 | openAfterPortAvailable();
223 | } else {
224 | open(serverUrl);
225 | }
226 | })
227 | .catch((err: any) => {
228 | logWarn(err);
229 | open(serverUrl);
230 | });
231 | }, 1000);
232 | };
233 | openAfterPortAvailable();
234 | }
235 | }
236 |
237 | } catch (err) {
238 | logErr(err as string);
239 | process.exit(1);
240 | }
241 | }
242 |
243 | export default server;
--------------------------------------------------------------------------------
/src/commands/index.ts:
--------------------------------------------------------------------------------
1 | import program from 'commander';
2 | import leven from 'leven';
3 | import chalk from 'chalk';
4 | import { nodeVersionCheck, npmVersionCheck, updateNotifier, logErr, logWarn, requireCwd, logInfo } from '@omni-door/utils';
5 | /* import types */
6 | import type { OmniConfig } from '../index.d';
7 |
8 | const commandDicts = {
9 | init: 'init',
10 | dev: 'dev',
11 | start: 'start',
12 | new: 'new',
13 | build: 'build',
14 | release: 'release'
15 | };
16 |
17 | (async function () {
18 | try {
19 | await nodeVersionCheck('10.13.0');
20 | } catch (e) {
21 | logWarn(e as string);
22 | }
23 |
24 | const { initial, dev, start, newTpl, build, release } = require('./commands');
25 | const pkj = require('../../package.json');
26 | let config: OmniConfig | null = null;
27 | let configFilePath = './omni.config.js';
28 |
29 | function getConfig (silent?: boolean) {
30 | try {
31 | const ppkj = requireCwd('./package.json', true);
32 | configFilePath = ppkj?.omni?.filePath || configFilePath;
33 | config = requireCwd(configFilePath, silent);
34 | } catch (e) {
35 | logWarn(e as string);
36 | }
37 | }
38 |
39 | function checkConfig () {
40 | if (!config) {
41 | logWarn(`Please initialize project first or checking the "${configFilePath}" configuration file`);
42 | logWarn(`请先初始化项目或检查 "${configFilePath}" 配置文件是否存在问题`);
43 | process.exit(0);
44 | }
45 | }
46 |
47 | function changeCWD (workPath: string) {
48 | try {
49 | process.chdir(workPath);
50 | const cwd = process.cwd();
51 | logInfo(`The work path change to "${cwd}"`);
52 | logInfo(`工作路径变更为 "${cwd}"`);
53 | } catch (err) {
54 | logWarn(`Please checking the "${workPath}" had existed`);
55 | logWarn(`工作路径变更失败,请检查 "${workPath}" 是否存在`);
56 | process.exit(0);
57 | }
58 | }
59 |
60 | program
61 | .version(pkj.version, '-v, --version')
62 | .name('omni')
63 | .usage('[command] [options]');
64 |
65 | program
66 | .command(`${commandDicts.init} [strategy]`)
67 | .option('-rb, --react_basic [name]', 'create a basic React SPA project')
68 | .option('-rs, --react_standard [name]', 'create a standard React SPA project')
69 | .option('-re, --react_entire [name]', 'create a most versatile React SPA project')
70 | .option('-rp, --react_pc [name]', 'create a React SPA project based on antd')
71 | .option('-vb, --vue_basic [name]', 'create a basic Vue SPA project')
72 | .option('-vs, --vue_standard [name]', 'create a standard Vue SPA project')
73 | .option('-ve, --vue_entire [name]', 'create a most versatile Vue SPA project')
74 | .option('-rS, --react_ssr [name]', 'create a React component library')
75 | .option('-rc, --react_components [name]', 'create a React component library')
76 | .option('-vc, --vue_components [name]', 'create a Vue component library')
77 | .option('-t, --toolkit [name]', 'create a toolkit project')
78 | .option('-n, --no-install', 'init project without install dependencies')
79 | .option('-P, --path ', 'the workpath for init the project')
80 | .description('initialize your project, [strategy] could be stable(default) or latest', {
81 | strategy: 'stable or latest',
82 | })
83 | .usage('[strategy] [options]')
84 | .action((strategy, options) => {
85 | updateNotifier(pkj);
86 |
87 | const workPath = options.path;
88 | if (workPath) changeCWD(workPath);
89 |
90 | const CLITAG = pkj?.version?.match?.(/[a-zA-Z]+/g)?.[0];
91 | const TPLTAG = pkj?.version?.match?.(/[0-9]+\.[0-9]+/g)?.[0];
92 | const CLICURRENTVERSION = pkj?.version?.match?.(/[0-9]+\.[0-9]+\.[0-9]+/g)?.[0];
93 |
94 | initial(strategy, options, { tplPkjTag: TPLTAG ? `~${TPLTAG}` : 'latest', tplPkjParams: [ `tag=${CLITAG || (CLICURRENTVERSION ? `~${CLICURRENTVERSION}` : 'latest')}` ] });
95 | });
96 |
97 | program
98 | .command(commandDicts.dev)
99 | .option('-p, --port ', 'start the dev-server according to the specified port')
100 | .option('-H, --hostname ', 'start the dev-server according to the specified hostname')
101 | .option('-P, --path ', 'the workpath for start the dev-server')
102 | .description('omni dev [-p ] [-H ] [-P ]', {
103 | port: 'The dev-server listen port.',
104 | host: 'The dev-server running hostname.',
105 | path: 'The cli workpath for running dev-server.'
106 | })
107 | .action((options) => {
108 | const workPath = options.path;
109 | if (workPath) changeCWD(workPath);
110 |
111 | getConfig(!!workPath);
112 | checkConfig();
113 | npmVersionCheck(pkj.name, pkj.version);
114 |
115 | dev(config, options);
116 | });
117 |
118 | program
119 | .command(commandDicts.start)
120 | .option('-p, --port ', 'start the prod-server according to the specified port')
121 | .option('-H, --hostname ', 'start the prod-server according to the specified hostname')
122 | .option('-P, --path ', 'the workpath for start the prod-server')
123 | .description('omni start [-p ] [-H ] [-P ]', {
124 | port: 'The prod-server listen port.',
125 | host: 'The prod-server running hostname.',
126 | path: 'The cli workpath for running prod-server.'
127 | })
128 | .action((options) => {
129 | const workPath = options.path;
130 | if (workPath) changeCWD(workPath);
131 |
132 | getConfig(!!workPath);
133 | checkConfig();
134 |
135 | start(config, options);
136 | });
137 |
138 | program
139 | .command(`${commandDicts.new} [name]`)
140 | .option('-f, --function', 'create a React-Function-Component')
141 | .option('-c, --class', 'create a React-Class-Component')
142 | .option('-r, --render', 'create a Vue-Render-Function')
143 | .option('-s, --single', 'create a Vue-Single-File-Component')
144 | .option('-P, --path ', 'the workpath for create component')
145 | .description('omni new [name] [-f | -c] [-P ]', {
146 | name: 'The name of component.',
147 | })
148 | .usage('[name] [options]')
149 | .action((componentName, options) => {
150 | const workPath = options.path;
151 | if (workPath) changeCWD(workPath);
152 |
153 | getConfig(!!workPath);
154 | checkConfig();
155 | updateNotifier(pkj);
156 | const TPLTAG = pkj?.version?.match?.(/[0-9]\.[0-9]/g)?.[0];
157 | newTpl(config, componentName, { ...options, tplPkjTag: TPLTAG });
158 | });
159 |
160 | program
161 | .command(commandDicts.build)
162 | .option('-c, --config ', 'specify the path of config file')
163 | .option('-n, --no-verify', 'bypass all pre-check before building')
164 | .option('-P, --path ', 'the workpath for build project')
165 | .description('build your project according to the [omni.config.js]\'s build field')
166 | .action((buildTactic) => {
167 | const workPath = buildTactic.path;
168 | if (workPath) changeCWD(workPath);
169 |
170 | getConfig(!!workPath);
171 | checkConfig();
172 | npmVersionCheck(pkj.name, pkj.version);
173 |
174 | build(config, buildTactic);
175 | });
176 |
177 | program
178 | .command(commandDicts.release)
179 | .option('-a, --automatic', 'auto-increase the version of iteration')
180 | .option('-i, --ignore', 'ignoring the version of iteration')
181 | .option('-m, --manual ', 'manual specify the version of iteration')
182 | .option('-t, --tag ', 'the tag will add to npm-package')
183 | .option('-n, --no-verify', 'bypass all pre-check before release')
184 | .option('-P, --path ', 'the workpath for release project')
185 | .description('publish your project according to the [omni.config.js]\'s release field')
186 | .action((iterTactic) => {
187 | const workPath = iterTactic.path;
188 | if (workPath) changeCWD(workPath);
189 |
190 | getConfig(!!workPath);
191 | checkConfig();
192 | updateNotifier(pkj);
193 |
194 | release(config, iterTactic);
195 | });
196 |
197 | program.arguments('')
198 | .action(unknownCommand => {
199 | const availableCommands = program.commands.map(cmd => cmd._name);
200 |
201 | let suggestion: any;
202 |
203 | availableCommands.forEach(cmd => {
204 | const isBestMatch =
205 | leven(cmd, unknownCommand) < leven(suggestion || '', unknownCommand);
206 | if (leven(cmd, unknownCommand) < 3 && isBestMatch) {
207 | suggestion = cmd;
208 | }
209 | });
210 |
211 | logErr(`Unknown command ${chalk.bold(`omni ${unknownCommand}`)}`);
212 | if (suggestion) {
213 | logWarn(`Try to ${chalk.underline(chalk.green(`omni ${suggestion}`))}`);
214 | }
215 | });
216 |
217 | program.parse(process.argv);
218 | if (!program.args.length) {
219 | program.help();
220 | }
221 | })();
222 |
--------------------------------------------------------------------------------
/src/commands/initial/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import initial from '../index';
4 | import {
5 | cli_basic_react,
6 | cli_standard_react,
7 | cli_entire_react,
8 | cli_pc_react,
9 | cli_basic_vue,
10 | cli_standard_vue,
11 | cli_entire_vue,
12 | cli_ssr_react,
13 | cli_components_react,
14 | cli_components_vue,
15 | cli_toolkit
16 | } from '../initial_preset';
17 |
18 |
19 | describe('initial command test', function () {
20 | it('type checking', function () {
21 | expect(initial).to.be.a('function');
22 | });
23 | });
24 |
25 | describe('initial preset test', function () {
26 | it('cli_basic_react checking', function () {
27 | expect(cli_basic_react).to.be.an('object');
28 |
29 | expect(cli_basic_react.build).to.be.a('string');
30 | expect(cli_basic_react.build).to.be.equal('webpack');
31 |
32 | expect(cli_basic_react.pkgtool).to.be.a('string');
33 | expect(cli_basic_react.pkgtool).to.be.equal('pnpm');
34 |
35 | expect(cli_basic_react['project_type']).to.be.a('string');
36 | expect(cli_basic_react['project_type']).to.be.equal('spa-react');
37 |
38 | expect(cli_basic_react.ts).to.be.a('boolean');
39 | expect(cli_basic_react.ts).to.be.false;
40 |
41 | expect(cli_basic_react.testFrame).to.be.a('string');
42 | expect(cli_basic_react.testFrame).to.be.equal('');
43 |
44 | expect(cli_basic_react.eslint).to.be.a('boolean');
45 | expect(cli_basic_react.eslint).to.be.false;
46 |
47 | expect(cli_basic_react.commitlint).to.be.a('boolean');
48 | expect(cli_basic_react.commitlint).to.be.false;
49 |
50 | expect(cli_basic_react.style).to.be.a('string');
51 | expect(cli_basic_react.style).to.be.equal('css');
52 |
53 | expect(cli_basic_react.stylelint).to.be.a('boolean');
54 | expect(cli_basic_react.stylelint).to.be.false;
55 | });
56 |
57 | it('cli_standard_react checking', function () {
58 | expect(cli_standard_react).to.be.an('object');
59 |
60 | expect(cli_standard_react.build).to.be.a('string');
61 | expect(cli_standard_react.build).to.be.equal('webpack');
62 |
63 | expect(cli_standard_react.pkgtool).to.be.a('string');
64 | expect(cli_standard_react.pkgtool).to.be.equal('pnpm');
65 |
66 | expect(cli_standard_react['project_type']).to.be.a('string');
67 | expect(cli_standard_react['project_type']).to.be.equal('spa-react');
68 |
69 | expect(cli_standard_react.ts).to.be.a('boolean');
70 | expect(cli_standard_react.ts).to.be.true;
71 |
72 | expect(cli_standard_react.testFrame).to.be.a('string');
73 | expect(cli_standard_react.testFrame).to.be.equal('');
74 |
75 | expect(cli_standard_react.eslint).to.be.a('boolean');
76 | expect(cli_standard_react.eslint).to.be.true;
77 |
78 | expect(cli_standard_react.commitlint).to.be.a('boolean');
79 | expect(cli_standard_react.commitlint).to.be.false;
80 |
81 | expect(cli_standard_react.style).to.be.a('string');
82 | expect(cli_standard_react.style).to.be.equal('less');
83 |
84 | expect(cli_standard_react.stylelint).to.be.a('boolean');
85 | expect(cli_standard_react.stylelint).to.be.true;
86 | });
87 |
88 | it('cli_entire_react checking', function () {
89 | expect(cli_entire_react).to.be.an('object');
90 |
91 | expect(cli_entire_react.build).to.be.a('string');
92 | expect(cli_entire_react.build).to.be.equal('webpack');
93 |
94 | expect(cli_entire_react.pkgtool).to.be.a('string');
95 | expect(cli_entire_react.pkgtool).to.be.equal('pnpm');
96 |
97 | expect(cli_entire_react['project_type']).to.be.a('string');
98 | expect(cli_entire_react['project_type']).to.be.equal('spa-react');
99 |
100 | expect(cli_entire_react.ts).to.be.a('boolean');
101 | expect(cli_entire_react.ts).to.be.true;
102 |
103 | expect(cli_entire_react.testFrame).to.be.a('string');
104 | expect(cli_entire_react.testFrame).to.be.equal('jest');
105 |
106 | expect(cli_entire_react.eslint).to.be.a('boolean');
107 | expect(cli_entire_react.eslint).to.be.true;
108 |
109 | expect(cli_entire_react.commitlint).to.be.a('boolean');
110 | expect(cli_entire_react.commitlint).to.be.true;
111 |
112 | expect(cli_entire_react.style).to.be.a('string');
113 | expect(cli_entire_react.style).to.be.equal('all');
114 |
115 | expect(cli_entire_react.stylelint).to.be.a('boolean');
116 | expect(cli_entire_react.stylelint).to.be.true;
117 | });
118 |
119 | it('cli_entire_react checking', function () {
120 | expect(cli_entire_react).to.be.an('object');
121 |
122 | expect(cli_pc_react.build).to.be.a('string');
123 | expect(cli_pc_react.build).to.be.equal('webpack');
124 |
125 | expect(cli_pc_react.pkgtool).to.be.a('string');
126 | expect(cli_pc_react.pkgtool).to.be.equal('pnpm');
127 |
128 | expect(cli_pc_react['project_type']).to.be.a('string');
129 | expect(cli_pc_react['project_type']).to.be.equal('spa-react-pc');
130 |
131 | expect(cli_pc_react.ts).to.be.a('boolean');
132 | expect(cli_pc_react.ts).to.be.true;
133 |
134 | expect(cli_pc_react.testFrame).to.be.a('string');
135 | expect(cli_pc_react.testFrame).to.be.equal('');
136 |
137 | expect(cli_pc_react.eslint).to.be.a('boolean');
138 | expect(cli_pc_react.eslint).to.be.true;
139 |
140 | expect(cli_pc_react.commitlint).to.be.a('boolean');
141 | expect(cli_pc_react.commitlint).to.be.true;
142 |
143 | expect(cli_pc_react.style).to.be.a('string');
144 | expect(cli_pc_react.style).to.be.equal('less');
145 |
146 | expect(cli_pc_react.stylelint).to.be.a('boolean');
147 | expect(cli_pc_react.stylelint).to.be.true;
148 | });
149 |
150 | it('cli_basic_vue checking', function () {
151 | expect(cli_basic_vue).to.be.an('object');
152 |
153 | expect(cli_basic_vue.build).to.be.a('string');
154 | expect(cli_basic_vue.build).to.be.equal('webpack');
155 |
156 | expect(cli_basic_vue.pkgtool).to.be.a('string');
157 | expect(cli_basic_vue.pkgtool).to.be.equal('pnpm');
158 |
159 | expect(cli_basic_vue['project_type']).to.be.a('string');
160 | expect(cli_basic_vue['project_type']).to.be.equal('spa-vue');
161 |
162 | expect(cli_basic_vue.ts).to.be.a('boolean');
163 | expect(cli_basic_vue.ts).to.be.false;
164 |
165 | expect(cli_basic_vue.testFrame).to.be.a('string');
166 | expect(cli_basic_vue.testFrame).to.be.equal('');
167 |
168 | expect(cli_basic_vue.eslint).to.be.a('boolean');
169 | expect(cli_basic_vue.eslint).to.be.false;
170 |
171 | expect(cli_basic_vue.commitlint).to.be.a('boolean');
172 | expect(cli_basic_vue.commitlint).to.be.false;
173 |
174 | expect(cli_basic_vue.style).to.be.a('string');
175 | expect(cli_basic_vue.style).to.be.equal('css');
176 |
177 | expect(cli_basic_vue.stylelint).to.be.a('boolean');
178 | expect(cli_basic_vue.stylelint).to.be.false;
179 | });
180 |
181 | it('cli_standard_vue checking', function () {
182 | expect(cli_standard_vue).to.be.an('object');
183 |
184 | expect(cli_standard_vue.build).to.be.a('string');
185 | expect(cli_standard_vue.build).to.be.equal('webpack');
186 |
187 | expect(cli_standard_vue.pkgtool).to.be.a('string');
188 | expect(cli_standard_vue.pkgtool).to.be.equal('pnpm');
189 |
190 | expect(cli_standard_vue['project_type']).to.be.a('string');
191 | expect(cli_standard_vue['project_type']).to.be.equal('spa-vue');
192 |
193 | expect(cli_standard_vue.ts).to.be.a('boolean');
194 | expect(cli_standard_vue.ts).to.be.true;
195 |
196 | expect(cli_standard_vue.testFrame).to.be.a('string');
197 | expect(cli_standard_vue.testFrame).to.be.equal('');
198 |
199 | expect(cli_standard_vue.eslint).to.be.a('boolean');
200 | expect(cli_standard_vue.eslint).to.be.true;
201 |
202 | expect(cli_standard_vue.commitlint).to.be.a('boolean');
203 | expect(cli_standard_vue.commitlint).to.be.false;
204 |
205 | expect(cli_standard_vue.style).to.be.a('string');
206 | expect(cli_standard_vue.style).to.be.equal('less');
207 |
208 | expect(cli_standard_vue.stylelint).to.be.a('boolean');
209 | expect(cli_standard_vue.stylelint).to.be.true;
210 | });
211 |
212 | it('cli_entire_vue checking', function () {
213 | expect(cli_entire_vue).to.be.an('object');
214 |
215 | expect(cli_entire_vue.build).to.be.a('string');
216 | expect(cli_entire_vue.build).to.be.equal('webpack');
217 |
218 | expect(cli_entire_vue.pkgtool).to.be.a('string');
219 | expect(cli_entire_vue.pkgtool).to.be.equal('pnpm');
220 |
221 | expect(cli_entire_vue['project_type']).to.be.a('string');
222 | expect(cli_entire_vue['project_type']).to.be.equal('spa-vue');
223 |
224 | expect(cli_entire_vue.ts).to.be.a('boolean');
225 | expect(cli_entire_vue.ts).to.be.true;
226 |
227 | expect(cli_entire_vue.testFrame).to.be.a('string');
228 | expect(cli_entire_vue.testFrame).to.be.equal('jest');
229 |
230 | expect(cli_entire_vue.eslint).to.be.a('boolean');
231 | expect(cli_entire_vue.eslint).to.be.true;
232 |
233 | expect(cli_entire_vue.commitlint).to.be.a('boolean');
234 | expect(cli_entire_vue.commitlint).to.be.true;
235 |
236 | expect(cli_entire_vue.style).to.be.a('string');
237 | expect(cli_entire_vue.style).to.be.equal('all');
238 |
239 | expect(cli_entire_vue.stylelint).to.be.a('boolean');
240 | expect(cli_entire_vue.stylelint).to.be.true;
241 | });
242 |
243 | it('cli_ssr_react checking', function () {
244 | expect(cli_ssr_react).to.be.an('object');
245 |
246 | expect(cli_ssr_react.build).to.be.a('string');
247 | expect(cli_ssr_react.build).to.be.equal('next');
248 |
249 | expect(cli_ssr_react.pkgtool).to.be.a('string');
250 | expect(cli_ssr_react.pkgtool).to.be.equal('pnpm');
251 |
252 | expect(cli_ssr_react['project_type']).to.be.a('string');
253 | expect(cli_ssr_react['project_type']).to.be.equal('ssr-react');
254 |
255 | expect(cli_ssr_react.ts).to.be.a('boolean');
256 | expect(cli_ssr_react.ts).to.be.true;
257 |
258 | expect(cli_ssr_react.testFrame).to.be.a('string');
259 | expect(cli_ssr_react.testFrame).to.be.equal('jest');
260 |
261 | expect(cli_ssr_react.eslint).to.be.a('boolean');
262 | expect(cli_ssr_react.eslint).to.be.true;
263 |
264 | expect(cli_ssr_react.prettier).to.be.a('boolean');
265 | expect(cli_ssr_react.prettier).to.be.true;
266 |
267 | expect(cli_ssr_react.commitlint).to.be.a('boolean');
268 | expect(cli_ssr_react.commitlint).to.be.true;
269 |
270 | expect(cli_ssr_react.style).to.be.a('string');
271 | expect(cli_ssr_react.style).to.be.equal('all');
272 |
273 | expect(cli_ssr_react.stylelint).to.be.a('boolean');
274 | expect(cli_ssr_react.stylelint).to.be.true;
275 |
276 | expect(cli_ssr_react.serverType).to.be.a('string');
277 | expect(cli_ssr_react.serverType).to.be.equal('next-app');
278 | });
279 |
280 | it('cli_components_react checking', function () {
281 | expect(cli_components_react).to.be.an('object');
282 |
283 | expect(cli_components_react.build).to.be.a('string');
284 | expect(cli_components_react.build).to.be.equal('tsc');
285 |
286 | expect(cli_components_react.pkgtool).to.be.a('string');
287 | expect(cli_components_react.pkgtool).to.be.equal('yarn');
288 |
289 | expect(cli_components_react['project_type']).to.be.a('string');
290 | expect(cli_components_react['project_type']).to.be.equal('component-react');
291 |
292 | expect(cli_components_react.ts).to.be.a('boolean');
293 | expect(cli_components_react.ts).to.be.true;
294 |
295 | expect(cli_components_react.testFrame).to.be.a('string');
296 | expect(cli_components_react.testFrame).to.be.equal('jest');
297 |
298 | expect(cli_components_react.eslint).to.be.a('boolean');
299 | expect(cli_components_react.eslint).to.be.true;
300 |
301 | expect(cli_components_react.commitlint).to.be.a('boolean');
302 | expect(cli_components_react.commitlint).to.be.true;
303 |
304 | expect(cli_components_react.style).to.be.a('string');
305 | expect(cli_components_react.style).to.be.equal('less');
306 |
307 | expect(cli_components_react.stylelint).to.be.a('boolean');
308 | expect(cli_components_react.stylelint).to.be.true;
309 |
310 | expect(cli_components_react.devServer).to.be.a('string');
311 | expect(cli_components_react.devServer).to.be.equal('storybook');
312 | });
313 |
314 | it('cli_components_vue checking', function () {
315 | expect(cli_components_vue).to.be.an('object');
316 |
317 | expect(cli_components_vue.build).to.be.a('string');
318 | expect(cli_components_vue.build).to.be.equal('tsc');
319 |
320 | expect(cli_components_vue.pkgtool).to.be.a('string');
321 | expect(cli_components_vue.pkgtool).to.be.equal('yarn');
322 |
323 | expect(cli_components_vue['project_type']).to.be.a('string');
324 | expect(cli_components_vue['project_type']).to.be.equal('component-vue');
325 |
326 | expect(cli_components_vue.ts).to.be.a('boolean');
327 | expect(cli_components_vue.ts).to.be.true;
328 |
329 | expect(cli_components_vue.testFrame).to.be.a('string');
330 | expect(cli_components_vue.testFrame).to.be.equal('jest');
331 |
332 | expect(cli_components_vue.eslint).to.be.a('boolean');
333 | expect(cli_components_vue.eslint).to.be.true;
334 |
335 | expect(cli_components_vue.commitlint).to.be.a('boolean');
336 | expect(cli_components_vue.commitlint).to.be.true;
337 |
338 | expect(cli_components_vue.style).to.be.a('string');
339 | expect(cli_components_vue.style).to.be.equal('less');
340 |
341 | expect(cli_components_vue.stylelint).to.be.a('boolean');
342 | expect(cli_components_vue.stylelint).to.be.true;
343 |
344 | expect(cli_components_vue.devServer).to.be.a('string');
345 | expect(cli_components_vue.devServer).to.be.equal('storybook');
346 | });
347 |
348 | it('cli_toolkit checking', function () {
349 | expect(cli_toolkit).to.be.an('object');
350 |
351 | expect(cli_toolkit.build).to.be.a('string');
352 | expect(cli_toolkit.build).to.be.equal('rollup');
353 |
354 | expect(cli_toolkit.pkgtool).to.be.a('string');
355 | expect(cli_toolkit.pkgtool).to.be.equal('yarn');
356 |
357 | expect(cli_toolkit['project_type']).to.be.a('string');
358 | expect(cli_toolkit['project_type']).to.be.equal('toolkit');
359 |
360 | expect(cli_toolkit.ts).to.be.a('boolean');
361 | expect(cli_toolkit.ts).to.be.true;
362 |
363 | expect(cli_toolkit.testFrame).to.be.a('string');
364 | expect(cli_toolkit.testFrame).to.be.equal('mocha');
365 |
366 | expect(cli_toolkit.eslint).to.be.a('boolean');
367 | expect(cli_toolkit.eslint).to.be.true;
368 |
369 | expect(cli_toolkit.commitlint).to.be.a('boolean');
370 | expect(cli_toolkit.commitlint).to.be.true;
371 |
372 | expect(cli_toolkit.style).to.be.a('string');
373 | expect(cli_toolkit.style).to.be.equal('');
374 |
375 | expect(cli_toolkit.stylelint).to.be.a('boolean');
376 | expect(cli_toolkit.stylelint).to.be.false;
377 | });
378 | });
379 |
--------------------------------------------------------------------------------
/src/commands/initial/initial_preset.ts:
--------------------------------------------------------------------------------
1 |
2 | import type {
3 | BUILD,
4 | TEST_FRAME,
5 | PKJ_TOOL,
6 | STYLE,
7 | LAYOUT,
8 | SPA_SERVER,
9 | COMPONENT_SERVER,
10 | SSR_SERVER,
11 | PROJECT_TYPE
12 | } from '@omni-door/utils';
13 |
14 | type DEV_SERVER = SPA_SERVER | COMPONENT_SERVER;
15 |
16 | export type GInstallCli = {
17 | project_type: PROJECT_TYPE;
18 | pkgtool: PKJ_TOOL;
19 | build: BUILD;
20 | ts: boolean;
21 | testFrame: TEST_FRAME;
22 | eslint: boolean;
23 | prettier: boolean;
24 | commitlint: boolean;
25 | style: STYLE;
26 | layout?: LAYOUT;
27 | stylelint: boolean;
28 | devServer?: DEV_SERVER;
29 | serverType?: SSR_SERVER;
30 | };
31 |
32 | export const cli_basic_react: GInstallCli = {
33 | project_type: 'spa-react',
34 | pkgtool: 'pnpm',
35 | build: 'webpack',
36 | ts: false,
37 | testFrame: '',
38 | eslint: false,
39 | prettier: false,
40 | commitlint: false,
41 | style: 'css',
42 | layout: 'px',
43 | stylelint: false
44 | };
45 |
46 | export const cli_standard_react: GInstallCli = {
47 | project_type: 'spa-react',
48 | pkgtool: 'pnpm',
49 | build: 'webpack',
50 | ts: true,
51 | testFrame: '',
52 | eslint: true,
53 | prettier: true,
54 | commitlint: false,
55 | style: 'less',
56 | layout: 'px',
57 | stylelint: true
58 | };
59 |
60 | export const cli_entire_react: GInstallCli = {
61 | project_type: 'spa-react',
62 | pkgtool: 'pnpm',
63 | build: 'webpack',
64 | ts: true,
65 | testFrame: 'jest',
66 | eslint: true,
67 | prettier: true,
68 | commitlint: true,
69 | style: 'all',
70 | layout: 'px',
71 | stylelint: true
72 | };
73 |
74 | export const cli_pc_react: GInstallCli = {
75 | project_type: 'spa-react-pc',
76 | pkgtool: 'pnpm',
77 | build: 'webpack',
78 | ts: true,
79 | testFrame: '',
80 | eslint: true,
81 | prettier: true,
82 | commitlint: true,
83 | style: 'less',
84 | stylelint: true
85 | };
86 |
87 | export const cli_basic_vue: GInstallCli = {
88 | project_type: 'spa-vue',
89 | pkgtool: 'pnpm',
90 | build: 'webpack',
91 | ts: false,
92 | testFrame: '',
93 | eslint: false,
94 | prettier: false,
95 | commitlint: false,
96 | style: 'css',
97 | layout: 'px',
98 | stylelint: false
99 | };
100 |
101 | export const cli_standard_vue: GInstallCli = {
102 | project_type: 'spa-vue',
103 | pkgtool: 'pnpm',
104 | build: 'webpack',
105 | ts: true,
106 | testFrame: '',
107 | eslint: true,
108 | prettier: true,
109 | commitlint: false,
110 | style: 'less',
111 | layout: 'px',
112 | stylelint: true
113 | };
114 |
115 | export const cli_entire_vue: GInstallCli = {
116 | project_type: 'spa-vue',
117 | pkgtool: 'pnpm',
118 | build: 'webpack',
119 | ts: true,
120 | testFrame: 'jest',
121 | eslint: true,
122 | prettier: true,
123 | commitlint: true,
124 | style: 'all',
125 | layout: 'px',
126 | stylelint: true
127 | };
128 |
129 | export const cli_ssr_react: GInstallCli = {
130 | project_type: 'ssr-react',
131 | pkgtool: 'pnpm',
132 | build: 'next',
133 | ts: true,
134 | testFrame: 'jest',
135 | eslint: true,
136 | prettier: true,
137 | commitlint: true,
138 | style: 'all',
139 | stylelint: true,
140 | serverType: 'next-app'
141 | };
142 |
143 | export const cli_components_react: GInstallCli = {
144 | project_type: 'component-react',
145 | pkgtool: 'yarn',
146 | build: 'tsc',
147 | ts: true,
148 | testFrame: 'jest',
149 | eslint: true,
150 | prettier: true,
151 | commitlint: true,
152 | style: 'less',
153 | stylelint: true,
154 | devServer: 'storybook'
155 | };
156 |
157 | export const cli_components_vue: GInstallCli = {
158 | project_type: 'component-vue',
159 | pkgtool: 'yarn',
160 | build: 'tsc',
161 | ts: true,
162 | testFrame: 'jest',
163 | eslint: true,
164 | prettier: true,
165 | commitlint: true,
166 | style: 'less',
167 | stylelint: true,
168 | devServer: 'storybook'
169 | };
170 |
171 | export const cli_toolkit: GInstallCli = {
172 | project_type: 'toolkit',
173 | pkgtool: 'yarn',
174 | build: 'rollup',
175 | ts: true,
176 | testFrame: 'mocha',
177 | eslint: true,
178 | prettier: true,
179 | commitlint: true,
180 | style: '',
181 | stylelint: false
182 | };
183 |
184 | export default {
185 | cli_basic_react,
186 | cli_standard_react,
187 | cli_entire_react,
188 | cli_pc_react,
189 | cli_basic_vue,
190 | cli_standard_vue,
191 | cli_entire_vue,
192 | cli_ssr_react,
193 | cli_components_react,
194 | cli_components_vue,
195 | cli_toolkit
196 | };
--------------------------------------------------------------------------------
/src/commands/new/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import newTpl from '../index';
4 |
5 | describe('new command test', function () {
6 | it('type checking', function () {
7 | expect(newTpl).to.be.a('function');
8 | });
9 | });
--------------------------------------------------------------------------------
/src/commands/new/index.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import inquirer from 'inquirer';
4 | import {
5 | exec,
6 | arr2str,
7 | logErr,
8 | logInfo,
9 | logWarn,
10 | logSuc,
11 | getNpmVersions,
12 | nodeVersionCheck
13 | } from '@omni-door/utils';
14 | import { getHandlers, signal, logo } from '../../utils';
15 | /* import types */
16 | import type { OmniConfig, OmniPlugin } from '../../index.d';
17 |
18 | function handleException (msg?: string) {
19 | logWarn(msg || 'Oops! Some unknown errors have occurred(发生了一些未知错误)!');
20 | process.exit(0);
21 | }
22 |
23 | export default async function (config: OmniConfig | null, componentName: string, options?: {
24 | function?: boolean;
25 | class?: boolean;
26 | render?: boolean;
27 | single?: boolean;
28 | tplPkj?: string;
29 | tplPkjTag?: string;
30 | before?: (params: {
31 | root: string;
32 | componentName: string;
33 | }) => (void | Promise);
34 | after?: (params: {
35 | root: string;
36 | componentName: string;
37 | }) => (void | Promise);
38 | }) {
39 | try {
40 | // node version pre-check
41 | await nodeVersionCheck('8');
42 | } catch (e) {
43 | logWarn(e as string);
44 | }
45 |
46 | if (!config || JSON.stringify(config) === '{}') {
47 | handleException('Please initialize first(请先初始化项目)!');
48 | }
49 |
50 | const {
51 | type,
52 | template,
53 | build,
54 | release,
55 | plugins
56 | } = config!;
57 |
58 | if (!type) {
59 | handleException('Cannot find the project type(项目类型缺失)!');
60 | }
61 |
62 | const {
63 | root,
64 | test,
65 | typescript = false,
66 | stylesheet = '',
67 | readme = false
68 | } = template!;
69 |
70 | let module_cn = '组件';
71 | let module_en = 'component';
72 | if (type === 'toolkit') {
73 | module_cn = '模块';
74 | module_en = 'module';
75 | }
76 |
77 | // eslint-disable-next-line prefer-const
78 | let { function: fc, class: cc, render: h, single: sfc, tplPkj, tplPkjTag, before, after } = options || {};
79 |
80 | if (!root) {
81 | handleException(`Missing the path for generate ${module_en}(生成${module_cn}的路径缺失)!`);
82 | }
83 |
84 | if (!componentName || (!fc && !cc && !h && !sfc)) {
85 | const moduleType = {
86 | fc: 'Function-Component(函数组件)',
87 | cc: 'Class-Component(类组件)',
88 | h: 'Render-Function(渲染函数组件)',
89 | sfc: 'Single-File-Component(模板组件)',
90 | };
91 | const questions = [
92 | {
93 | name: 'name',
94 | type: 'input',
95 | when: (answer: any) => {
96 | if (componentName) {
97 | return false;
98 | }
99 | return true;
100 | },
101 | message: `${logo()} Please enter ${module_en} name(请输入${module_cn}名称):`
102 | },
103 | {
104 | name: 'type',
105 | type: 'list',
106 | when: (answer: any) => {
107 | if (!answer.name && !componentName) {
108 | handleException(`Please input the ${module_en} name(请输入创建的${module_cn}名称)!`);
109 | }
110 | if (type === 'spa-vue' || type === 'toolkit' || fc || cc) {
111 | return false;
112 | }
113 | return true;
114 | },
115 | choices: type === 'component-vue' ? [ moduleType.h, moduleType.sfc ] : [ moduleType.fc, moduleType.cc ],
116 | message: `${logo()} Select the type of ${module_en}(选择${module_cn}类型):`
117 | }
118 | ];
119 | await new Promise((resolve) => {
120 | inquirer.prompt(questions)
121 | .then(answers => {
122 | const { name, type } = answers;
123 | componentName = name || componentName;
124 | switch (type) {
125 | case moduleType.fc:
126 | fc = true;
127 | break;
128 | case moduleType.cc:
129 | cc = true;
130 | break;
131 | case moduleType.sfc:
132 | sfc = true;
133 | break;
134 | case moduleType.h:
135 | h = true;
136 | break;
137 | }
138 | resolve(void 0);
139 | });
140 | }).catch(err => {
141 | handleException(err);
142 | });
143 | }
144 |
145 | if (!/^[a-zA-Z\_]\w+$/g.test(componentName)) {
146 | handleException(
147 | `Please input a valid module name(请输入合法的${module_cn}名称)!\n
148 | Rules(规则):\n
149 | 1. The ${module_cn} name must greater-or-equal 2(${module_cn}名大于等于2个字符)\n
150 | 2. The first character can only be underscore or upper/lower case letter(第一个字符只能由 下划线_ 或 大小写字母 组成)\n
151 | 3. The subsequent characters can only be numberm, underscore, upper and lower case letter(后续字符只能由 数字、下划线_、大小写字母 组成)\n
152 | `
153 | );
154 | }
155 |
156 | // bind exit signals
157 | signal();
158 |
159 | const mdx = readme === 'mdx';
160 | const path_cp = path.resolve(root, componentName);
161 | const path_cp_rel = path.relative(process.cwd(), path_cp);
162 |
163 | if (fs.existsSync(path_cp)) {
164 | handleException(`The ${componentName} ${module_en} had been existed(${module_cn} ${componentName} 已存在)!`);
165 | }
166 |
167 | const hasStorybook = fs.existsSync(path.resolve(process.cwd(), '.storybook'));
168 | const params = [
169 | `componentName=${componentName}`,
170 | `newPath=${path_cp}`,
171 | `stylesheet=${stylesheet}`,
172 | `ts=${typescript}`,
173 | `type=${cc
174 | ? 'cc'
175 | : fc
176 | ? 'fc'
177 | : h
178 | ? 'h'
179 | : sfc
180 | ? 'sfc'
181 | : ''
182 | }`,
183 | `test=${!!test}`,
184 | `hasStorybook=${hasStorybook}`,
185 | readme ? `md=${mdx ? 'mdx' : 'md'}` : ''
186 | ];
187 |
188 | let newTplPkj = tplPkj;
189 | if (!newTplPkj) {
190 | switch (type) {
191 | case 'spa-react':
192 | newTplPkj = '@omni-door/tpl-spa-react';
193 | break;
194 | case 'spa-react-pc':
195 | newTplPkj = '@omni-door/tpl-spa-react-pc';
196 | break;
197 | case 'spa-vue':
198 | newTplPkj = '@omni-door/tpl-spa-vue';
199 | break;
200 | case 'ssr-react':
201 | newTplPkj = '@omni-door/tpl-ssr-react';
202 | break;
203 | case 'component-react':
204 | newTplPkj = '@omni-door/tpl-component-react';
205 | break;
206 | case 'component-vue':
207 | newTplPkj = '@omni-door/tpl-component-vue';
208 | break;
209 | case 'toolkit':
210 | default:
211 | newTplPkj = '@omni-door/tpl-toolkit';
212 | break;
213 | }
214 | }
215 |
216 | let templatePackageTag = tplPkjTag || 'latest';
217 | if (tplPkjTag) {
218 | const matchVer = tplPkjTag.match(/\d+.\d+/)?.[0];
219 | if (matchVer) {
220 | const versions = await getNpmVersions(newTplPkj);
221 | const [firstNum, secondNum] = matchVer.split('.');
222 | const regexp = new RegExp(`^${firstNum}{1}.${secondNum}{1}.\\d+$`);
223 | const thirdNum = Math.max(...versions.filter(v => regexp.test(v)).map(v => +(v.split('.')?.[2] ?? 0)));
224 | templatePackageTag = `${firstNum}.${secondNum}.${thirdNum}`;
225 | }
226 | }
227 |
228 | typeof before === 'function' && await before({
229 | componentName,
230 | root
231 | });
232 |
233 | const newTpl = `${newTplPkj}@${templatePackageTag}`;
234 | logInfo(`Downloading the ${newTpl}, please wait patiently(正在下载 ${newTpl},请稍后)…`);
235 | exec(
236 | [
237 | `npx ${newTpl} new ${arr2str(params)}`
238 | ],
239 | async function () {
240 | // handle new plugins
241 | const plugin_handles = plugins && plugins.length > 0 && getHandlers<'new'>(plugins as OmniPlugin<'new'>[], 'new');
242 | if (plugin_handles) {
243 | for (const name in plugin_handles) {
244 | const handler = plugin_handles[name];
245 | await handler({
246 | type,
247 | template,
248 | build,
249 | release
250 | }, {
251 | componentName,
252 | componentType: cc ? 'class' : 'function',
253 | tplSource: newTplPkj!
254 | });
255 | }
256 | }
257 | typeof after === 'function' && await after({
258 | componentName,
259 | root
260 | });
261 | // success logger
262 | logSuc(`The ${componentName} local at ${path_cp_rel}, construction completed(${componentName} 位于 ${path_cp_rel},创建完成)!`);
263 | process.exit(0);
264 | },
265 | function (err: any) {
266 | logErr(err);
267 | logErr('👆 Oops! Some error occured(完蛋!好像有错误)\n');
268 | process.exit(1);
269 | });
270 | }
--------------------------------------------------------------------------------
/src/commands/release/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import release from '../index';
4 |
5 | describe('release command test', function () {
6 | it('type checking', function () {
7 | expect(release).to.be.a('function');
8 | });
9 | });
--------------------------------------------------------------------------------
/src/commands/release/branch.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | branch=$1
4 | name=$2
5 |
6 | if [ "$name" == "" ]
7 | then
8 | name="🐸 [OMNI-DOOR]"
9 | fi
10 |
11 | checkBranch () {
12 | if [ -z "$branch" ]; then
13 | echo -e "\033[31m \n ${name} The branch cannot be empty \033[0m"
14 | echo -e "\033[31m \n ${name} 分支不能为空\n \033[0m"
15 | exit 1
16 | fi
17 |
18 | currentBranch=$(git branch | grep \* | cut -d " " -f2)
19 |
20 | if [ "$currentBranch" != "$branch" ]
21 | then
22 | if [ "$currentBranch" == "" ]
23 | then
24 | echo -e "\033[31m \n ${name} Please initialize git repository and finishing the first push operation by yourself \033[0m"
25 | echo -e "\033[31m \n ${name} 请先初始化 git 仓库并手动完成第一次推送\n \033[0m"
26 | else
27 | echo -e "\033[31m \n ${name} Please switch to \033[43;30m ${branch} \033[0m \033[31mbranch first \033[0m"
28 | echo -e "\033[31m \n ${name} 请切换到 \033[43;30m ${branch} \033[0m \033[31m分支进行发布\n \033[0m"
29 | fi
30 | exit 1
31 | fi
32 |
33 | echo -e "\033[36m \n ${name} The current branch is ${branch} \033[0m"
34 | echo -e "\033[36m \n ${name} 当前分支为 ${branch}\n \033[0m"
35 | }
36 |
37 | checkBranch
--------------------------------------------------------------------------------
/src/commands/release/index.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import inquirer from 'inquirer';
4 | import semver from 'semver';
5 | import chalk from 'chalk';
6 | import {
7 | exec,
8 | logErr,
9 | logInfo,
10 | logWarn,
11 | logSuc,
12 | logCongrat,
13 | logEmph,
14 | logTime,
15 | italic,
16 | underline,
17 | nodeVersionCheck,
18 | getNpmVersions,
19 | logPrefix
20 | } from '@omni-door/utils';
21 | import { spawn, execSync } from 'child_process';
22 | import { getHandlers, signal, logo } from '../../utils';
23 | import buildCommands from '../build';
24 | /* import types */
25 | import type { OmniConfig, OmniPlugin } from '../../index.d';
26 |
27 | const tagCustom = '$omni_custom$';
28 |
29 | const tagDict = {
30 | '1. alpha (内测版)': 'alpha',
31 | '2. beta (公测版)': 'beta',
32 | '3. rc (候选版)': 'rc',
33 | '4. latest (正式版)': 'latest',
34 | '5. custom (自定义)': tagCustom
35 | };
36 |
37 | const tagDictWithExtraWords = {
38 | '1. alpha (内测版 - 当前标签)': 'alpha',
39 | '2. beta (公测版 - 当前标签)': 'beta',
40 | '3. rc (候选版 - 当前标签)': 'rc',
41 | '4. latest (正式版 - 当前标签)': 'latest'
42 | };
43 |
44 | const iterDict = {
45 | automatic: '1. automatic(自动)',
46 | manual: '2. manual(手动)',
47 | ignore: '3. ignore(忽略)'
48 | };
49 |
50 | function getAutoIterDict (version: string, tag: string) {
51 | if (tag === 'latest') {
52 | return {
53 | [`1. patch (${version} -> ${semver.inc(version, 'patch')})`]: ['latest', semver.inc(version, 'patch')],
54 | [`2. minor (${version} -> ${semver.inc(version, 'minor')})`]: ['latest', semver.inc(version, 'minor')],
55 | [`3. major (${version} -> ${semver.inc(version, 'major')})`]: ['latest', semver.inc(version, 'major')]
56 | };
57 | }
58 |
59 | if (tag === 'rc') {
60 | return {
61 | [`1. pre-release (${version} -> ${semver.inc(version, 'prerelease', tag)})`]: [tag || 'prerelease', semver.inc(version, 'prerelease', tag)],
62 | [`2. patch (${version} -> ${semver.inc(version, 'patch')})`]: ['latest', semver.inc(version, 'patch')],
63 | [`3. minor (${version} -> ${semver.inc(version, 'minor')})`]: ['latest', semver.inc(version, 'minor')],
64 | [`4. major (${version} -> ${semver.inc(version, 'major')})`]: ['latest', semver.inc(version, 'major')]
65 | };
66 | }
67 |
68 | return {
69 | [`1. pre-release (${version} -> ${semver.inc(version, 'prerelease', tag)})`]: [tag || 'prerelease', semver.inc(version, 'prerelease', tag)],
70 | [`2. pre-patch (${version} -> ${semver.inc(version, 'prepatch', tag)})`]: [tag || 'prepatch', semver.inc(version, 'prepatch', tag)],
71 | [`3. pre-minor (${version} -> ${semver.inc(version, 'preminor', tag)})`]: [tag || 'preminor', semver.inc(version, 'preminor', tag)],
72 | [`4. pre-major (${version} -> ${semver.inc(version, 'premajor', tag)})`]: [tag || 'premajor', semver.inc(version, 'premajor', tag)],
73 | };
74 | }
75 |
76 | export default async function (
77 | config: OmniConfig | null,
78 | iterTactic?: {
79 | automatic?: boolean | string;
80 | ignore?: boolean;
81 | manual?: string;
82 | verify?: boolean;
83 | tag?: string;
84 | config?: string;
85 | buildConfig?: string;
86 | pkjFieldName?: string;
87 | configFileName?: string;
88 | },
89 | autoRelease?: boolean
90 | ) {
91 | try {
92 | // node version pre-check
93 | await nodeVersionCheck('8');
94 | } catch (e) {
95 | logWarn(e as string);
96 | }
97 |
98 | if (!config || JSON.stringify(config) === '{}') {
99 | logWarn('Please initialize project first');
100 | logWarn('请先初始化项目');
101 | process.exit(0);
102 | }
103 |
104 | // bind exit signals
105 | signal();
106 |
107 | const { type, template, build, release = {}, plugins } = config;
108 | const {
109 | git,
110 | npm,
111 | autoBuild,
112 | autoTag,
113 | preflight
114 | } = release;
115 | const {
116 | test = false,
117 | eslint = false,
118 | prettier = false,
119 | stylelint = false,
120 | commitlint = false,
121 | branch
122 | } = preflight || {};
123 |
124 | if (branch) {
125 | // branch check
126 | let branchInfo = '';
127 | await exec(
128 | [`${path.resolve(__dirname, 'branch.sh')} ${branch} "${logPrefix()}"`],
129 | function (results) { branchInfo = results[0]; },
130 | function () { process.exit(1); }
131 | );
132 | if (!~branchInfo.indexOf('current branch is')) {
133 | // branch check failed!
134 | return;
135 | }
136 | }
137 |
138 | function handleReleaseSuc (msg?: string) {
139 | msg = msg || 'Release completed (发布完成)!';
140 |
141 | return function (isExit?: boolean) {
142 | logCongrat(msg!);
143 | isExit && process.exit(0);
144 | };
145 | }
146 |
147 | function handleReleaseErr (msg?: string) {
148 | msg = msg || 'Release failed (发布失败)!';
149 |
150 | return function (err?: string) {
151 | err && logErr(err);
152 | msg && logErr(msg);
153 | process.exit(1);
154 | };
155 | }
156 |
157 | function getPkjData (pkjPath: string) {
158 | let pkj = {
159 | name: 'OMNI-PROJECT',
160 | version: '0.0.1'
161 | };
162 | if (fs.existsSync(pkjPath)) {
163 | delete require.cache[pkjPath]; // delete cache in order to avoid version may not correct
164 | pkj = require(pkjPath);
165 | }
166 | return pkj;
167 | }
168 |
169 | try {
170 | // eslint-disable-next-line prefer-const
171 | let { automatic, ignore, manual, tag, verify, ...rest } = iterTactic || {};
172 |
173 | // package.json data
174 | const pkjPath = path.resolve(process.cwd(), 'package.json');
175 | let pkj = getPkjData(pkjPath);
176 |
177 | // the version for iteration
178 | let iterVersion = manual || (typeof automatic === 'string' ? automatic : '') || pkj.version;
179 | // whether or not need iteration
180 | const needIteration = ignore === void 0 && manual === void 0 && automatic === void 0;
181 |
182 | const versionErrMsg = `Please input valid version (请输入有效的版本号)\n
183 | Reference to (版本号规则可参考): https://semver.org/`;
184 | const tagErrMsg = 'The tag can only contain letters (标签只能包含字母)';
185 | const versionRepeatMsg = (ver: string) => `The ${ver} is not available (${ver} 不可用)`;
186 |
187 | const existedVersions = [] as string[];
188 | let versionsPromise = Promise.resolve();
189 | if (npm) {
190 | versionsPromise = getNpmVersions(pkj.name, { registry: typeof npm === 'string' ? npm : void 0 })
191 | .then(res => { existedVersions.push(...res); });
192 | }
193 |
194 | // infer the tag which according to the version
195 | const defaultTag = manual
196 | ? manual.match(/[a-zA-Z]+/g)?.[0] ?? 'latest'
197 | : pkj?.version?.match(/[a-zA-Z]+/g)?.[0] ?? 'latest';
198 |
199 | // automatic interation dictionary
200 | const autoIterDict = {} as Record;
201 |
202 | if (needIteration || (npm && !tag)) {
203 | await new Promise((resolve, reject) => {
204 | inquirer.prompt([
205 | {
206 | name: 'presetTag',
207 | type: 'list',
208 | when: () => !tag && !autoTag,
209 | choices: () => {
210 | const result = Object.keys(tagDict);
211 | const presetTags = Object.values(tagDict);
212 | if (!presetTags.some(v => v === defaultTag)) {
213 | const key = `0. ${defaultTag} (当前标签)`;
214 | result.unshift(key);
215 | tagDictWithExtraWords[key as keyof typeof tagDictWithExtraWords] = defaultTag;
216 | } else {
217 | const ind = presetTags.indexOf(defaultTag);
218 | const preset = result[ind];
219 | result.splice(ind, 1, preset.replace(')', ' - 当前标签)'));
220 | }
221 | return result;
222 | },
223 | default: () => {
224 | const result = Object.keys(tagDict);
225 | const presetTags = Object.values(tagDict);
226 | if (presetTags.some(v => v === defaultTag)) {
227 | return result[presetTags.indexOf(defaultTag)].replace(')', ' - 当前标签)');
228 | }
229 | },
230 | message: 'Choose the tag (选择标签):'
231 | },
232 | {
233 | name: 'label',
234 | type: 'input',
235 | when: answer => tagDict[answer.presetTag as keyof typeof tagDict] === tagCustom,
236 | default: () => {
237 | if (defaultTag === 'rc') return 'latest';
238 | return defaultTag;
239 | },
240 | validate: val => {
241 | if (/^[a-zA-Z]+$/g.test(val)) {
242 | return true;
243 | }
244 | return tagErrMsg;
245 | },
246 | message: `${logo()}Input the tag (输入标签):`
247 | },
248 | {
249 | name: 'iter',
250 | type: 'list',
251 | when: () => needIteration,
252 | choices: [ iterDict.automatic, iterDict.manual, iterDict.ignore ],
253 | message: `${logo()}Select the way of iteration (选择迭代方式):`
254 | },
255 | {
256 | name: 'version_semantic',
257 | type: 'list',
258 | when: answer => answer.iter === iterDict.automatic,
259 | choices: (answer) => {
260 | Object.assign(autoIterDict, getAutoIterDict(
261 | pkj.version,
262 | answer.label ||
263 | tagDict[answer.presetTag as keyof typeof tagDict] ||
264 | tagDictWithExtraWords[answer.presetTag as keyof typeof tagDictWithExtraWords] ||
265 | defaultTag
266 | ));
267 | return [ ...Object.keys(autoIterDict) ];
268 | },
269 | message: `${logo()}Select the version (选择版本):`
270 | },
271 | {
272 | name: 'version_manual',
273 | type: 'input',
274 | when: answer => answer.iter === iterDict.manual,
275 | validate: val => {
276 | if (!semver.valid(val)) {
277 | console.info('\n');
278 | logWarn(versionErrMsg);
279 | return false;
280 | }
281 | return true;
282 | },
283 | message: `${logo()}Input the version (输入版本号):`
284 | },
285 | {
286 | name: 'changeVersion',
287 | type: 'confirm',
288 | message: () => {
289 | const currentVer = iterVersion;
290 | const type = tag === 'latest' ? 'patch' : 'prerelease';
291 | while(~existedVersions.indexOf(iterVersion)) {
292 | iterVersion = semver.inc(iterVersion, type, tag)!;
293 | }
294 |
295 | return `The ${chalk.strikethrough.red(currentVer)} had been occupied, would you like change to ${chalk.bold.underline.green(iterVersion)}?`;
296 | },
297 | when: async (answer) => {
298 | const { version_manual, version_semantic, presetTag, label } = answer;
299 | iterVersion = version_manual || autoIterDict[version_semantic]?.[1] || iterVersion;
300 | const versionTag = iterVersion?.match(/[a-zA-Z]+/g)?.[0];
301 | tag = label
302 | || tagDict[presetTag as keyof typeof tagDict]
303 | || tagDictWithExtraWords[presetTag as keyof typeof tagDictWithExtraWords]
304 | || (versionTag === 'rc' ? 'latest' : versionTag)
305 | || autoIterDict[version_semantic]?.[0]
306 | || defaultTag;
307 | if (!npm) return false;
308 | await versionsPromise;
309 | return existedVersions.some(v => iterVersion === v);
310 | }
311 | }
312 | ])
313 | .then(answers => {
314 | const { iter, version_semantic, version_manual, changeVersion } = answers;
315 | if (changeVersion === false) {
316 | const currentVer = version_manual ?? autoIterDict[version_semantic]?.[1] ?? '';
317 | logWarn(versionRepeatMsg(currentVer));
318 | process.exit(1);
319 | }
320 |
321 | switch (iter) {
322 | case iterDict.automatic:
323 | // eslint-disable-next-line no-case-declarations
324 | automatic = iterVersion ?? true;
325 | break;
326 | case iterDict.manual:
327 | manual = iterVersion;
328 | break;
329 | case iterDict.ignore:
330 | ignore = true;
331 | break;
332 | }
333 |
334 | resolve(void 0);
335 | })
336 | .catch(handleReleaseErr());
337 | });
338 | } else if (npm) {
339 | await versionsPromise;
340 | if (~existedVersions.indexOf(iterVersion)) {
341 | logWarn(versionRepeatMsg(iterVersion));
342 | process.exit(0);
343 | }
344 | }
345 |
346 | if (manual && !semver.valid(manual)) {
347 | logWarn(versionErrMsg);
348 | process.exit(0);
349 | }
350 |
351 | // auto build
352 | if (autoBuild && !autoRelease) {
353 | logEmph(italic('Start building the project automatically'));
354 | logEmph(italic('开始自动构建项目'));
355 | try {
356 | await buildCommands(
357 | config,
358 | {
359 | ...rest,
360 | verify
361 | },
362 | true
363 | );
364 | } catch (err) {
365 | handleReleaseErr('Auto building the project failed(自动构建项目失败)!')();
366 | }
367 | }
368 |
369 | logTime('RELEASE(发布)');
370 | logInfo('Starting release process(开始发布)!');
371 | if (!autoBuild && verify && test) {
372 | await exec(['npm test'], () => logSuc('Unit Test!'), handleReleaseErr('The unit test not pass(单元测试失败)'));
373 | }
374 |
375 | if (!autoBuild && verify && eslint) {
376 | await exec(['npm run lint:es'], () => logSuc('Eslint!'), handleReleaseErr(`The eslint not pass(eslint校验失败) \n try to exec(尝试执行): ${underline('npm run lint:es_fix')}`));
377 | }
378 |
379 | if (!autoBuild && verify && prettier) {
380 | await exec(['npm run lint:prettier'], () => logSuc('Prettier!'), handleReleaseErr(`The prettier not pass(prettier校验失败) \n try to exec(尝试执行): ${underline('npm run lint:prettier_fix')}`));
381 | }
382 |
383 | if (!autoBuild && verify && stylelint) {
384 | await exec(['npm run lint:style'], () => logSuc('Stylelint!'), handleReleaseErr(`The stylelint not pass(stylelint校验失败) \n try to exec(尝试执行): ${underline('npm run lint:style_fix')}`));
385 | }
386 |
387 | const versionShellSuffix = ignore
388 | ? 'i'
389 | : manual
390 | ? `m ${manual}`
391 | : typeof automatic === 'string'
392 | ? `a ${automatic}`
393 | : '';
394 | await exec(
395 | [`${path.resolve(__dirname, 'version.sh')} "${logPrefix()}" ${versionShellSuffix}`],
396 | () => {
397 | // re-require to get correct version
398 | pkj = getPkjData(pkjPath);
399 | logEmph(`The current version is ${pkj.version}`);
400 | logEmph(`当前版本号为 ${pkj.version}`);
401 | },
402 | handleReleaseErr('The version iteration failed(版本迭代失败)!')
403 | );
404 |
405 | // handle release plugins
406 | const plugin_handles = plugins && plugins.length > 0 && getHandlers<'release'>(plugins as OmniPlugin<'release'>[], 'release');
407 | if (plugin_handles) {
408 | const version = pkj ? pkj.version : 'unknown';
409 | const versionIterTactic = ignore ? 'ignore' : manual ? 'manual' : 'auto';
410 | for (const name in plugin_handles) {
411 | const handler = plugin_handles[name];
412 | await handler({
413 | type,
414 | template,
415 | build,
416 | release
417 | }, {
418 | version,
419 | versionIterTactic,
420 | verify,
421 | tag
422 | });
423 | }
424 | }
425 |
426 | const hasChange = !!execSync('git status -s').toString();
427 | if (git && hasChange) {
428 | const gitUrl = git.trim();
429 | let gitOriginUrl = '';
430 | let gitOmniUrl = '';
431 | await exec([
432 | 'git remote get-url origin'
433 | ], function (results) {
434 | gitOriginUrl = results[0] && results[0].trim();
435 | }, () => {}, true);
436 | await exec([
437 | 'git remote get-url omni'
438 | ], function (results) {
439 | gitOmniUrl = results[0] && results[0].trim();
440 | }, () => {}, true);
441 |
442 | let canPush = true;
443 | let remote = gitUrl === gitOmniUrl ? 'omni' : 'origin';
444 | if (gitUrl !== gitOriginUrl && gitUrl !== gitOmniUrl) {
445 | !gitOmniUrl && logInfo(`Adding remote omni ${git}(新增远程地址omni ${git})`);
446 | const execArr = ['git remote remove omni', `git remote add omni ${git}`];
447 | !gitOmniUrl && execArr.shift(); // remote没有omni,移除remove操作
448 |
449 | await exec(
450 | execArr,
451 | () => {
452 | logEmph(`git remote omni: ${git}`);
453 | remote = 'omni';
454 | },
455 | () => {
456 | logWarn('setting git remote failed');
457 | logWarn('git remote 设置失败');
458 | canPush = false;
459 | }
460 | );
461 | }
462 |
463 | const commit = commitlint && !verify
464 | ? `git commit -m'[${pkj.name.toUpperCase()}]: ${pkj.version}' --no-verify`
465 | : `git commit -m'[${pkj.name.toUpperCase()}]: ${pkj.version}'`;
466 |
467 | const push = commitlint && !verify
468 | ? `git push ${remote} ${branch || 'master'} --no-verify`
469 | : `git push ${remote} ${branch || 'master'}`;
470 |
471 | canPush && await exec(
472 | [
473 | 'git add -A',
474 | `${commit}`,
475 | `${push}`
476 | ],
477 | () => {
478 | logSuc('Pushing to git-repo successfully!');
479 | logSuc('git仓库推送成功!');
480 | },
481 | handleReleaseErr('Pushing to git-repo failed(git仓库推送失败)!')
482 | );
483 | }
484 |
485 | if (npm) {
486 | let npmUrl = '';
487 | await exec(
488 | ['npm get registry'],
489 | function (results) {
490 | npmUrl = results[0] && results[0].trim();
491 | }, () => {}, true
492 | );
493 |
494 | await new Promise((resolve, reject) => {
495 | const npm_publish = spawn(
496 | 'npm',
497 | [
498 | 'publish',
499 | `--registry=${(npm && typeof npm === 'string') ? npm : npmUrl}`,
500 | `--tag=${tag}`,
501 | '--access public'
502 | ],
503 | {
504 | detached: true,
505 | stdio: 'inherit'
506 | }
507 | );
508 |
509 | if (npm_publish.stdout) {
510 | npm_publish.stdout.on('data', data => {
511 | console.info(data.toString());
512 | });
513 | }
514 |
515 | if (npm_publish.stderr) {
516 | npm_publish.stderr.on('data', data => {
517 | console.info(data.toString());
518 | });
519 | }
520 |
521 | npm_publish.on('error', handleReleaseErr('The npm-package publish failed(npm包发布失败)!'));
522 |
523 | npm_publish.on('close', code => {
524 | if (code === 0) {
525 | logSuc(`The npm-package publish success with version ${pkj.version}@${tag}!`);
526 | logSuc(`npm包发布成功, 版本号为 ${pkj.version}@${tag}!`);
527 | resolve(null);
528 | } else {
529 | reject();
530 | }
531 | });
532 | });
533 | }
534 |
535 | logTime('RELEASE(发布)', true);
536 | const shouldExit = !autoRelease;
537 | handleReleaseSuc()(shouldExit);
538 | } catch (err) {
539 | logErr(err as string);
540 | handleReleaseErr('👆 Oops! release process occured some accidents(糟糕!发布过程发生了一点意外)')();
541 | }
542 | }
--------------------------------------------------------------------------------
/src/commands/release/publish.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | name=$1
3 |
4 | if [ "$name" == "" ]
5 | then
6 | name="🐸 [OMNI-DOOR]"
7 | fi
8 |
9 | npm publish --registry='https://registry.npmjs.org/'
10 |
11 | echo -e "\033[35m${name} The npm-package publish success!\033[0m"
12 | echo -e "\033[35m${name} npm包发布成功!\033[0m"
--------------------------------------------------------------------------------
/src/commands/release/version.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | name=$1
4 | if [ "$name" == "" ]
5 | then
6 | name="🐸 [OMNI-DOOR]"
7 | fi
8 | iterate=$2
9 | inputVersion=$3
10 | dot="."
11 | OS=`uname`
12 |
13 | replaceVersion () {
14 | if [ "$OS" = "Darwin" ]; then
15 | sed -i "" "s/$1/$2/g" "package.json"
16 | else
17 | sed -i"" "s/$1/$2/g" "package.json"
18 | fi
19 | }
20 |
21 | updateVersion () {
22 | versionLine=$(grep \"version\" package.json)
23 | version=$(echo ${versionLine} | tr -cd "[0-9-a-zA-Z]." | sed -ne "s/[^0-9]*\(\([0-9a-zA-Z]\.\)\)/\1/p")
24 | prevSubVersion=$(echo ${version#*.})
25 | subVersion=$(echo ${prevSubVersion%.*})
26 | subSubVersion=$(echo ${version##*.})
27 | if [ "$iterate" = "i" -o "$iterate" = "ignore" ]
28 | then
29 | echo -e "\n\033[33m${name} Ignoring the version of iteration \033[0m\n"
30 | echo -e "\033[33m${name} 忽略版本号迭代\033[0m\n"
31 | elif [ "$iterate" = "m" -o "$iterate" = "manual" ]
32 | then
33 | newVersion=$(echo ${version/${version}/${inputVersion}})
34 | newVersionLine=$(echo "${versionLine/${version}/${newVersion}}")
35 | echo -e "\n\033[35m${name} Manual specify the version of iteration to ${manualVersion} \033[0m\n"
36 | echo -e "\033[35m${name} 版本号手动迭代至 ${inputVersion}\033[0m\n"
37 | replaceVersion "$versionLine" "$newVersionLine"
38 | elif [ "$iterate" = "a" -o "$iterate" = "auto" ]
39 | then
40 | newVersion=$(echo ${version/${version}/${inputVersion}})
41 | newVersionLine=$(echo "${versionLine/${version}/${newVersion}}")
42 | echo -e "\n\033[36m${name} Auto-increase the version of iteration to ${newVersion} \033[0m\n"
43 | echo -e "\033[36m${name} 版本号自动迭代至 ${newVersion}\033[0m\n"
44 | replaceVersion "$versionLine" "$newVersionLine"
45 | elif [ -z "$iterate" ]
46 | then
47 | newSubSubVersion=`expr $subSubVersion + 1`
48 | newVersion=$(echo ${version/${dot}${subVersion}${dot}${subSubVersion}/${dot}${subVersion}${dot}${newSubSubVersion}})
49 | newVersionLine=$(echo "${versionLine/${version}/${newVersion}}")
50 | echo -e "\n\033[36m${name} Auto-increase the version of iteration to ${newVersion} \033[0m\n"
51 | echo -e "\033[36m${name} 版本号自动迭代至 ${newVersion}\033[0m\n"
52 | replaceVersion "$versionLine" "$newVersionLine"
53 | else
54 | echo -e "\n\033[31m${name} Version iteration failed \033[0m\n"
55 | echo -e "\033[31m${name} 版本迭代失败\033[0m\n"
56 | exit 1
57 | fi
58 | }
59 |
60 | updateVersion
--------------------------------------------------------------------------------
/src/commands/servers/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import {
4 | EWServer,
5 | KNServer
6 | } from '../index';
7 |
8 | describe('express-webpack server test', function () {
9 | it('type checking', function () {
10 | expect(EWServer).to.be.a('function');
11 | });
12 | });
13 |
14 | describe('koa-next server test', function () {
15 | it('type checking', function () {
16 | expect(KNServer).to.be.a('function');
17 | });
18 | });
--------------------------------------------------------------------------------
/src/commands/servers/express-webpack.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import http from 'http';
4 | import https from 'https';
5 | import { logInfo, requireCwd } from '@omni-door/utils';
6 | import open from '../dev/open';
7 | /* import types */
8 | import type { Express } from 'express';
9 | import type { Configuration, Compiler } from 'webpack';
10 | import type { WebpackDevMiddleware, Options } from 'webpack-dev-middleware';
11 | import type { NextHandleFunction } from 'connect';
12 | import type { ProxyConfig, MiddlewareConfig } from '../dev/server';
13 | import type { EWMiddleWareCallback } from '../../index.d';
14 |
15 | export interface EWServerParams {
16 | webpackConfig: Configuration | (() => Configuration);
17 | devMiddlewareOptions?: Partial;
18 | proxyConfig?: ProxyConfig;
19 | middlewareConfig?: MiddlewareConfig;
20 | ipAddress: string;
21 | host: string;
22 | listenHost?: string;
23 | port: number;
24 | httpsConfig?: {
25 | key?: string | Buffer;
26 | cert?: string | Buffer;
27 | };
28 | favicon?: string;
29 | }
30 |
31 | export default function ({
32 | webpackConfig,
33 | devMiddlewareOptions,
34 | proxyConfig = [],
35 | middlewareConfig = [],
36 | ipAddress,
37 | host,
38 | listenHost,
39 | port,
40 | httpsConfig,
41 | favicon: faviconPath
42 | }: EWServerParams) {
43 | const express = requireCwd('express');
44 | const favicon = requireCwd('serve-favicon');
45 | const { createProxyMiddleware } = requireCwd('http-proxy-middleware');
46 | const webpack = requireCwd('webpack');
47 | const compiler: Compiler = webpack(typeof webpackConfig === 'function' ? webpackConfig() : webpackConfig);
48 | const devMiddleware: WebpackDevMiddleware & NextHandleFunction = requireCwd('webpack-dev-middleware')(compiler, {
49 | publicPath: '/',
50 | ...devMiddlewareOptions
51 | });
52 | const hotMiddleware= requireCwd('webpack-hot-middleware');
53 |
54 | const app: Express = express();
55 |
56 | // dev server middleware
57 | app.use(devMiddleware);
58 |
59 | // hot refresh middleware
60 | app.use(hotMiddleware(compiler, {
61 | log: logInfo,
62 | path: '/__webpack_hmr',
63 | heartbeat: 10 * 1000
64 | }));
65 |
66 | // http proxy middleware
67 | for (let i = 0; i < proxyConfig.length; i++) {
68 | const item = proxyConfig[i];
69 | const { route, config } = typeof item === 'function' ? item({
70 | ip: ipAddress,
71 | port,
72 | host,
73 | middlewareConfig
74 | }) : item;
75 |
76 | app.use(
77 | route,
78 | createProxyMiddleware(config)
79 | );
80 | }
81 |
82 | // custom middleware
83 | for (let i = 0; i < middlewareConfig.length; i++) {
84 | const item = middlewareConfig[i];
85 | const { route, callback, method } = typeof item === 'function' ? item({
86 | ip: ipAddress,
87 | port,
88 | host,
89 | proxyConfig
90 | }) : item;
91 |
92 | let _method = (method?.toLowerCase() ?? 'use') as 'get' | 'post' | 'delete' | 'del' | 'put' | 'use';
93 | if (_method === 'del') _method = 'delete';
94 | app[_method](
95 | route,
96 | callback as EWMiddleWareCallback
97 | );
98 | }
99 |
100 | // favicon.ico
101 | const icoPath = faviconPath && fs.existsSync(faviconPath) ? faviconPath : path.resolve(__dirname, 'favicon.ico');
102 | fs.existsSync(icoPath) && app.use(favicon(icoPath));
103 |
104 | // index.html for SPA browser router
105 | app.use('*', devMiddleware);
106 |
107 | let server;
108 | let serverUrl = `${host}:${port}`;
109 | if (httpsConfig) {
110 | server = https.createServer({
111 | key: httpsConfig.key,
112 | cert: httpsConfig.cert
113 | }, app);
114 | serverUrl = 'https://' + serverUrl;
115 | } else {
116 | server = http.createServer(app);
117 | serverUrl = 'http://' + serverUrl;
118 | }
119 |
120 | server.listen(port, listenHost || host, async () => {
121 | await open(serverUrl);
122 | logInfo('> Ready on: ' + serverUrl);
123 | });
124 | }
--------------------------------------------------------------------------------
/src/commands/servers/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omni-door/cli/7349af22a058d59d20b4194609dc2e9d4ae35010/src/commands/servers/favicon.ico
--------------------------------------------------------------------------------
/src/commands/servers/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | default as EWServer,
3 | EWServerParams
4 | } from './express-webpack';
5 | export {
6 | default as KNServer,
7 | KNServerParams
8 | } from './koa-next';
--------------------------------------------------------------------------------
/src/commands/servers/koa-next.ts:
--------------------------------------------------------------------------------
1 | // !deprecated
2 | import path from 'path';
3 | import http from 'http';
4 | import https from 'https';
5 | import { logInfo, logWarn, logErr, requireCwd, _typeof } from '@omni-door/utils';
6 | import open from '../dev/open';
7 | /* import types */
8 | import type NextServer from 'next-server/dist/server/next-server';
9 | import type * as KoaRouter from 'koa-router';
10 | import type { ProxyConfig, MiddlewareConfig, CorsConfig } from '../dev/server';
11 | import type { NextRouter, KNMiddleWareCallback, KoaApp, ANY_OBJECT } from '../../index.d';
12 |
13 | export interface KNServerParams {
14 | dev: boolean;
15 | proxyConfig?: ProxyConfig;
16 | middlewareConfig?: MiddlewareConfig;
17 | corsConfig?: CorsConfig;
18 | ipAddress: string;
19 | host: string;
20 | listenHost?: string;
21 | port: number;
22 | httpsConfig?: {
23 | key?: string | Buffer;
24 | cert?: string | Buffer;
25 | };
26 | nextRouter?: NextRouter;
27 | }
28 |
29 | export default function ({
30 | dev,
31 | ipAddress,
32 | proxyConfig = [],
33 | middlewareConfig = [],
34 | corsConfig,
35 | host,
36 | listenHost,
37 | port,
38 | httpsConfig,
39 | nextRouter
40 | }: KNServerParams) {
41 | const Koa = requireCwd('koa');
42 | const next = requireCwd('next');
43 | const Router = requireCwd('koa-router');
44 | const bodyParser = requireCwd('koa-bodyparser');
45 | const k2c = requireCwd('koa2-connect');
46 | const statics = requireCwd('koa-static');
47 | const cors = requireCwd('@koa/cors');
48 | const proxy = requireCwd('http-proxy-middleware');
49 | const { pathToRegexp } = requireCwd('path-to-regexp');
50 | const publicPath = path.resolve(process.cwd(), 'public');
51 |
52 | const nextApp: NextServer = next({ dev });
53 | nextApp
54 | .prepare()
55 | .then(() => {
56 | const app: KoaApp = new Koa();
57 | const router: KoaRouter = new Router();
58 |
59 | // cors
60 | app.use(cors(corsConfig));
61 |
62 | // middleware: http-proxy
63 | app.use(async (ctx, next) => {
64 | const { path } = ctx;
65 | let needProxy = false;
66 |
67 | for (let i = 0; i < proxyConfig.length; i++) {
68 | const item = proxyConfig[i];
69 | const { route, config } = typeof item === 'function' ? item({
70 | ip: ipAddress,
71 | port,
72 | host,
73 | middlewareConfig
74 | }) : item;
75 |
76 | try {
77 | if (
78 | pathToRegexp(route).test(path) ||
79 | new RegExp(`^${route}`).test(path)
80 | ) {
81 | needProxy = true;
82 | await k2c(proxy(path, config))(ctx, next);
83 | break;
84 | }
85 | } catch (err) {
86 | logWarn(err as any);
87 | logWarn(`The http-proxy「${route})」match occur error`);
88 | logWarn(`http-proxy「${route}」匹配异常`);
89 | }
90 | }
91 |
92 | if (!needProxy) {
93 | await next();
94 | } else {
95 | return;
96 | }
97 | });
98 |
99 | // middleware: custom
100 | const middlewares = [...middlewareConfig];
101 | for (let i = 0; i < middlewares.length; i++) {
102 | const item = middlewares[i];
103 | const { route, callback, method } = typeof item === 'function' ? item({
104 | ip: ipAddress,
105 | port,
106 | host,
107 | proxyConfig
108 | }) : item;
109 | const _method = (method?.toLowerCase() ?? 'get') as 'get' | 'post' | 'put' | 'del';
110 | const anyStr = '@#$%^#*(&^!~)::;;".._--';
111 | const wildcardRoute = !route || pathToRegexp(route).test(anyStr) || new RegExp(`^${route}`).test(anyStr);
112 | if (wildcardRoute) {
113 | router.use(callback);
114 | } else {
115 | router[_method](route, callback);
116 | }
117 | }
118 |
119 | // inject routes
120 | // based on next-url-prettifier
121 | // https://github.com/BDav24/next-url-prettifier
122 | nextRouter && nextRouter?.forEachPattern(({ page, pattern, defaultParams, beforeRender }) => router.get(pattern, async (ctx, next) => {
123 | let shouldRender: boolean | ANY_OBJECT = true;
124 |
125 | try {
126 | const { req, res, query, params } = ctx;
127 |
128 | if (typeof beforeRender === 'function') {
129 | try {
130 | shouldRender = await beforeRender(ctx, next);
131 | } catch (err) {
132 | logWarn(err as any);
133 | logWarn(`The ${page}'s beforeRender error!`);
134 | logWarn(`${page} 页面 beforeRender 执行异常`);
135 | }
136 | }
137 |
138 | shouldRender && nextApp.render(req, res, `/${page}`, Object.assign(Object.create(null), defaultParams, query, params, _typeof(shouldRender) === 'object' ? shouldRender : null));
139 | } catch (err) {
140 | shouldRender = false;
141 | logWarn(JSON.stringify(err));
142 | logWarn(`The ${page} router error`);
143 | logWarn(`${page} 路由出错`);
144 | }
145 |
146 | if (shouldRender) {
147 | ctx.status = 200;
148 | ctx.respond = false;
149 | }
150 | }));
151 |
152 | // other source redirect to '/'
153 | router.get('(.*)', async ctx => {
154 | await nextApp.render(ctx.req, ctx.res, '/', ctx.query);
155 | ctx.status = 200;
156 | ctx.respond = false;
157 | });
158 |
159 | // middleware: static-server, body-parser and router
160 | app.use(statics(publicPath));
161 | app.use(bodyParser());
162 | app.use(router.routes()).use(router.allowedMethods());
163 |
164 | let server;
165 | let serverUrl = `${host}:${port}`;
166 | if (httpsConfig) {
167 | server = https.createServer({
168 | key: httpsConfig.key,
169 | cert: httpsConfig.cert
170 | }, app.callback());
171 | serverUrl = 'https://' + serverUrl;
172 | } else {
173 | server = http.createServer(app.callback());
174 | serverUrl = 'http://' + serverUrl;
175 | }
176 |
177 | server.listen(port, listenHost || host, async () => {
178 | dev && await open(serverUrl);
179 | logInfo(`The server running with ${dev ? 'DEV' : 'PROD'}-MODE!`);
180 | logInfo('> Ready on: ' + serverUrl);
181 | });
182 | })
183 | .catch(err => {
184 | const error = new Error(err);
185 | logErr(`${error}\nThe Error stack: ${error.stack}`);
186 | });
187 | }
--------------------------------------------------------------------------------
/src/commands/start/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import start from '../index';
4 |
5 | describe('start command test', function () {
6 | it('type checking', function () {
7 | expect(start).to.be.a('function');
8 | });
9 | });
--------------------------------------------------------------------------------
/src/commands/start/index.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { logWarn, nodeVersionCheck, requireCwd, exec, _typeof } from '@omni-door/utils';
3 | import { KNServer } from '../servers';
4 | import { signal } from '../../utils';
5 | /* import types */
6 | import type { OmniConfig } from '../../index.d';
7 |
8 | function handleException (msg?: string) {
9 | logWarn(msg || '发生了一些未知错误!(Ops! Some unknown errors have occurred!)');
10 | process.exit(0);
11 | }
12 |
13 | export default async function (config: OmniConfig | null, options: {
14 | port?: number | string;
15 | hostname?: string;
16 | }) {
17 | try {
18 | // node version pre-check
19 | await nodeVersionCheck('8');
20 | } catch (e) {
21 | logWarn(e as string);
22 | }
23 |
24 | if (!config || JSON.stringify(config) === '{}') {
25 | handleException('Please initialize project first(请先初始化项目)!');
26 | }
27 | const { server } = config!;
28 |
29 | if (!server || JSON.stringify(server) === '{}') {
30 | handleException('The start field is missing in config file(配置文件 start 字段缺失)!');
31 | }
32 | const {
33 | port,
34 | host,
35 | serverType,
36 | middleware,
37 | https,
38 | ...rest
39 | } = server || {};
40 | if (!serverType) {
41 | handleException('Please specify server-type(请指定 server 类型)!');
42 | }
43 |
44 | // bind exit signals
45 | signal();
46 |
47 | const p = options.port;
48 | const h = options.hostname;
49 | const ip = requireCwd('ip');
50 | const ipAddress: string = ip.address();
51 | const CWD = process.cwd();
52 | const _port = (p ? +p : port) || 6200;
53 | const _host = h || host || '0.0.0.0';
54 |
55 | if (_typeof(https) === 'boolean') {
56 | logWarn(`The https must specify path when start server at production environment (开发环境中 https 必须指定路径): \n
57 |
58 | https: {
59 | key: fs.readFileSync(path.resolve(\${your_path_to_key})),
60 | cert: fs.readFileSync(path.resolve(\${your_path_to_cert}))
61 | }`);
62 | }
63 |
64 | switch (serverType) {
65 | case 'next-app':
66 | case 'next-pages':
67 | exec([`${path.resolve(CWD, 'node_modules/.bin/next')} start --port ${_port} --hostname ${_host}`]);
68 | break;
69 | case 'nuxt':
70 | default:
71 | logWarn('Not support ssr-vue yet');
72 | logWarn('暂不支持 ssr-vue 项目');
73 | }
74 | }
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'webpack';
2 | export type { Configuration } from 'webpack';
3 | import type { Config } from 'http-proxy-middleware';
4 | export type { Config } from 'http-proxy-middleware';
5 | import type { Options as DevMiddlewareOptions } from 'webpack-dev-middleware';
6 | export type { Options as DevMiddlewareOptions } from 'webpack-dev-middleware';
7 | import type { Request, Response, NextFunction } from 'express';
8 | export type { Request, Response, NextFunction } from 'express';
9 | import type * as KoaApp from 'koa';
10 | export type { default as KoaApp } from 'koa';
11 | import type { BUILD, PROJECT_TYPE, STYLE, PLUGIN_STAGE, HASH, SPA_SERVER, COMPONENT_SERVER, SSR_SERVER, MARKDOWN } from '@omni-door/utils';
12 |
13 | export type ANY_OBJECT = { [propName: string]: any };
14 |
15 | export type Method = 'get' | 'GET' | 'post' | 'POST' | 'put' | 'PUT' | 'del' | 'DEL';
16 |
17 | export type PathParams = string | RegExp | (string | RegExp)[];
18 |
19 | export type KoaCtx = KoaApp.ParameterizedContext;
20 |
21 | export type OptionTemplate = {
22 | componentName: string;
23 | componentType: 'function' | 'class';
24 | tplSource: string;
25 | };
26 |
27 | export type OptionBuild = {
28 | verify?: boolean;
29 | buildConfig?: string;
30 | };
31 |
32 | export type OptionRelease = {
33 | version: string;
34 | versionIterTactic: 'ignore' | 'manual' | 'auto';
35 | verify?: boolean;
36 | tag?: string;
37 | };
38 |
39 | export interface PluginHandler {
40 | (
41 | config: Omit,
42 | options?: T extends 'new' ? OptionTemplate : T extends 'build' ? OptionBuild : OptionRelease
43 | ): Promise;
44 | }
45 | export type HandlerFactory = (handler: PluginHandler, errMsg?: string) => PluginHandler;
46 |
47 | export interface OmniPlugin {
48 | name: string;
49 | stage: T;
50 | handler: PluginHandler;
51 | }
52 |
53 | export type EWMiddleWareCallback = (req: Request, res: Response, next: NextFunction) => void;
54 | export type KNMiddleWareCallback = KoaApp.Middleware;
55 | export type MiddleWareCallback = EWMiddleWareCallback | KNMiddleWareCallback;
56 |
57 | export type ServerType = SPA_SERVER | COMPONENT_SERVER | SSR_SERVER | 'default';
58 |
59 | export interface NextRouter {
60 | forEachPattern: (apply: (params: {
61 | page: string;
62 | pattern: string;
63 | defaultParams?: ANY_OBJECT;
64 | beforeRender?: (ctx: KoaApp.ParameterizedContext, next: KoaApp.Next) => boolean | ANY_OBJECT;
65 | }) => any) => void;
66 | }
67 |
68 | export type OmniServer = {
69 | port?: number;
70 | host?: string;
71 | https?: boolean | { key: string; cert: string; };
72 | CA?: {
73 | organization?: string;
74 | countryCode?: string;
75 | state?: string;
76 | locality?: string;
77 | validityDays?: number;
78 | };
79 | proxy?: {
80 | route: PathParams;
81 | config: Config;
82 | }[];
83 | middleware?: {
84 | route: PathParams;
85 | callback: MiddleWareCallback;
86 | method?: Method;
87 | }[];
88 | cors?: {
89 | origin?: string | ((ctx: KoaCtx) => string);
90 | allowMethods?: string | string[];
91 | exposeHeaders?: string | string[];
92 | allowHeaders?: string | string[];
93 | maxAge?: string | number;
94 | credentials?: boolean | ((ctx: KoaCtx) => string);
95 | keepHeadersOnError?: boolean;
96 | secureContext?: boolean;
97 | privateNetworkAccess?: boolean;
98 | };
99 | nextRouter?: NextRouter;
100 | };
101 |
102 | export interface OmniBaseConfig {
103 | type: PROJECT_TYPE;
104 | dev?: OmniServer & {
105 | devMiddlewareOptions?: Partial;
106 | webpack?: Configuration | (() => Configuration);
107 | configuration?: (config: ANY_OBJECT) => ANY_OBJECT;
108 | serverType?: ServerType;
109 | favicon?: string;
110 | };
111 | server?: OmniServer & { serverType?: SSR_SERVER; };
112 | build: {
113 | autoRelease?: boolean;
114 | srcDir: string;
115 | outDir: string;
116 | esmDir?: string;
117 | hash?: boolean | HASH;
118 | configuration?: (config: ANY_OBJECT) => ANY_OBJECT;
119 | tool?: Exclude;
120 | preflight?: {
121 | typescript?: boolean;
122 | test?: boolean;
123 | eslint?: boolean;
124 | prettier?: boolean;
125 | stylelint?: boolean;
126 | };
127 | reserve?: {
128 | style?: boolean;
129 | assets?: (string | { srcPath: string; relativePath?: string; })[];
130 | };
131 | };
132 | release: {
133 | git?: string;
134 | npm?: string | boolean;
135 | autoBuild?: boolean;
136 | autoTag?: boolean;
137 | preflight?: {
138 | test?: boolean;
139 | eslint?: boolean;
140 | prettier?: boolean;
141 | stylelint?: boolean;
142 | commitlint?: boolean;
143 | branch?: string;
144 | };
145 | };
146 | template: {
147 | root: string;
148 | test?: boolean;
149 | typescript?: boolean;
150 | stylesheet?: STYLE;
151 | readme?: MARKDOWN | boolean;
152 | };
153 | plugins?: OmniPlugin[];
154 | }
155 |
156 | export interface OmniRollupConfig extends OmniBaseConfig {
157 | build: OmniBaseConfig['build'] & {
158 | tool: Extract;
159 | configuration?: (getConfig: (bundle: boolean) => ANY_OBJECT) => ANY_OBJECT;
160 | };
161 | }
162 |
163 | export type OmniConfig = OmniBaseConfig | OmniRollupConfig;
--------------------------------------------------------------------------------
/src/utils/__test__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha';
2 | import { expect } from 'chai';
3 | import { getHandlers, handlerFactory, logo, signal } from '../';
4 |
5 |
6 | describe('tackle_plugins test', function () {
7 | it('type checking', function () {
8 | expect(getHandlers).to.be.a('function');
9 | expect(handlerFactory).to.be.a('function');
10 | });
11 |
12 | it('call getHandlers', function () {
13 | getHandlers([
14 | {
15 | stage: 'build',
16 | name: 'test-build',
17 | handler: function () {
18 | console.info('test handle build plugins');
19 | return Promise.resolve();
20 | }
21 | }
22 | ], 'build');
23 | getHandlers([
24 | {
25 | stage: 'new',
26 | name: 'test-new',
27 | handler: function () {
28 | console.info('test handle new plugins');
29 | return Promise.resolve();
30 | }
31 | }
32 | ], 'new');
33 | getHandlers([
34 | {
35 | stage: 'release',
36 | name: 'test-release',
37 | handler: function () {
38 | console.info('test handle release plugins');
39 | return Promise.resolve();
40 | }
41 | }
42 | ], 'release');
43 | });
44 |
45 | it('call handlerFactory', function () {
46 | const buildFn = handlerFactory(function () {
47 | console.info('test handle build plugins');
48 | return Promise.resolve();
49 | });
50 | const newFn = handlerFactory(function () {
51 | console.info('test handle new plugins');
52 | return Promise.resolve();
53 | });
54 | const releaseFn = handlerFactory(function () {
55 | console.info('test handle release plugins');
56 | return Promise.resolve();
57 | });
58 | });
59 | });
60 |
61 | describe('logo test', function () {
62 | it('type checking', function () {
63 | expect(logo).to.be.a('function');
64 | expect(logo()).to.be.a('string');
65 | });
66 |
67 | it('value checking', function () {
68 | expect(logo()).to.be.equal('🐸 ');
69 | });
70 | });
71 |
72 | describe('signal test', function () {
73 | it('type checking', function () {
74 | expect(signal).to.be.a('function');
75 | });
76 | });
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export { default as logo } from './logo';
2 | export { default as signal } from './signal';
3 | export { getHandlers, handlerFactory } from './tackle_plugins';
--------------------------------------------------------------------------------
/src/utils/logo.ts:
--------------------------------------------------------------------------------
1 | import { getLogo } from '@omni-door/utils';
2 |
3 | export const logo = () => getLogo() + ' ';
4 |
5 | export default logo;
--------------------------------------------------------------------------------
/src/utils/signal.ts:
--------------------------------------------------------------------------------
1 | import { logInfo, logErr } from '@omni-door/utils';
2 |
3 | export default function () {
4 | (['SIGINT', 'SIGQUIT', 'SIGTERM'] as NodeJS.Signals[]).forEach((sig) => {
5 | process.on(sig, () => {
6 | logInfo(`process exit by ${sig}`);
7 | process.exit(0);
8 | });
9 | });
10 | process.on('uncaughtException', e => {
11 | logErr(`uncaughtException - ${e.name}:${e.message}`);
12 | });
13 | process.on('unhandledRejection', reason => {
14 | logErr(`unhandledRejection - ${JSON.stringify(reason)}`);
15 | });
16 | process.on('exit', code => {
17 | logInfo(`exit with code ${code}`);
18 | });
19 | }
--------------------------------------------------------------------------------
/src/utils/tackle_plugins.ts:
--------------------------------------------------------------------------------
1 | import { logWarn } from '@omni-door/utils';
2 | /* import types */
3 | import type { PLUGIN_STAGE } from '@omni-door/utils';
4 | import type { HandlerFactory, PluginHandler, OmniPlugin } from '../index.d';
5 |
6 | export function getHandlers (plugins: OmniPlugin[], stage: T) {
7 | const handlers: { [pluginName: string]: PluginHandler } = {};
8 | for (let i = 0; i < plugins.length; i++) {
9 | const plugin = plugins[i];
10 | plugin.stage === stage && (handlers[plugin.name] = handlerFactory(plugin.handler, `The "${plugin.name}" execution error, will skip to continue the rest of the operaions(插件 "${plugin.name}" 执行发生错误,将跳过继续执行剩余操作)`));
11 | }
12 |
13 | return handlers;
14 | }
15 |
16 | export const handlerFactory: HandlerFactory = (handler, errMsg) => (config, options) => {
17 | try {
18 | return Promise.resolve(handler(config, options));
19 | } catch (err) {
20 | logWarn(err as any);
21 | logWarn(errMsg || 'The plugin execution error, will skip to continue the rest of the operaions(插件执行发生错误,将跳过继续执行剩余操作)');
22 | }
23 | return Promise.resolve({});
24 | };
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "compilerOptions": {
4 | /* Basic Options */
5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
7 | "lib": ["dom", "es5", "es6", "es7", "es2017", "es2018", "esnext"], /* Specify library files to be included in the compilation. */
8 | // "allowJs": true, /* Allow javascript files to be compiled. */
9 | // "checkJs": true, /* Report errors in .js files. */
10 | "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
11 | "declaration": true, /* Generates corresponding '.d.ts' file. */
12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
13 | // "sourceMap": true, /* Generates corresponding '.map' file. */
14 | // "outFile": "./", /* Concatenate and emit output to single file. */
15 | "outDir": "./lib", /* Redirect output structure to the directory. */
16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
17 | // "composite": true, /* Enable project compilation */
18 | // "removeComments": true, /* Do not emit comments to output. */
19 | // "noEmit": true, /* Do not emit outputs. */
20 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
21 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
22 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
23 |
24 | /* Strict Type-Checking Options */
25 | "strict": true, /* Enable all strict type-checking options. */
26 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
27 | // "strictNullChecks": true, /* Enable strict null checks. */
28 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
29 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
30 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
31 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
32 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
33 |
34 | /* Additional Checks */
35 | // "noUnusedLocals": true, /* Report errors on unused locals. */
36 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
37 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
38 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
39 |
40 | /* Module Resolution Options */
41 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
42 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
43 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
44 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
45 | "typeRoots": ["node_modules/@types", "./src/@types"], /* List of folders to include type definitions from. */
46 | // "types": [], /* Type declaration files to be included in compilation. */
47 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
48 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
49 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
50 |
51 | /* Source Map Options */
52 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
53 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
54 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
55 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
56 |
57 | /* Experimental Options */
58 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
59 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
60 | },
61 | "exclude": [
62 | "node_modules",
63 | "src/**/__test__/*",
64 | "lib/*",
65 | "es/*",
66 | "build/*"
67 | ]
68 | }
69 |
70 |
--------------------------------------------------------------------------------