@liuli-util/cli 3.15.0 → 3.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. package/CHANGELOG.md +96 -89
  2. package/dist/bin.js +96 -77
  3. package/dist/bin.js.map +3 -3
  4. package/dist/commands/deploy/DeployService.d.ts +51 -0
  5. package/dist/commands/deploy/DeployService.d.ts.map +1 -0
  6. package/dist/commands/deploy/deploy.d.ts +3 -0
  7. package/dist/commands/deploy/deploy.d.ts.map +1 -0
  8. package/dist/commands/deploy/index.d.ts +3 -0
  9. package/dist/commands/deploy/index.d.ts.map +1 -0
  10. package/dist/commands/deploy/util/FileLock.d.ts +14 -0
  11. package/dist/commands/deploy/util/FileLock.d.ts.map +1 -0
  12. package/dist/commands/deploy/util/PromiseUtil.d.ts +14 -0
  13. package/dist/commands/deploy/util/PromiseUtil.d.ts.map +1 -0
  14. package/dist/commands/deploy/util/createArchive.d.ts +10 -0
  15. package/dist/commands/deploy/util/createArchive.d.ts.map +1 -0
  16. package/dist/commands/deploy/util/execPromise.d.ts +4 -0
  17. package/dist/commands/deploy/util/execPromise.d.ts.map +1 -0
  18. package/dist/commands/deploy/util/wait.d.ts +9 -0
  19. package/dist/commands/deploy/util/wait.d.ts.map +1 -0
  20. package/dist/commands/esbuild/ESBuildProgram.d.ts.map +1 -1
  21. package/dist/index.esm.js +1 -1
  22. package/dist/index.esm.js.map +2 -2
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +2 -2
  25. package/dist/utils/nodeCacheDir.d.ts +2 -0
  26. package/dist/utils/nodeCacheDir.d.ts.map +1 -0
  27. package/package.json +13 -7
  28. package/src/bin.ts +3 -2
  29. package/src/commands/deploy/DeployService.ts +157 -0
  30. package/src/commands/deploy/__tests__/DeployService.test.ts +75 -0
  31. package/src/commands/deploy/__tests__/FileLock.test.ts +27 -0
  32. package/src/commands/deploy/__tests__/conf.test.ts +29 -0
  33. package/src/commands/deploy/__tests__/simpleGit.test.ts +29 -0
  34. package/src/commands/deploy/__tests__/util/deployGhPageWorker.ts +16 -0
  35. package/src/commands/deploy/deploy.ts +48 -0
  36. package/src/commands/deploy/index.ts +7 -0
  37. package/src/commands/deploy/util/FileLock.ts +31 -0
  38. package/src/commands/deploy/util/PromiseUtil.ts +34 -0
  39. package/src/commands/deploy/util/createArchive.ts +30 -0
  40. package/src/commands/deploy/util/execPromise.ts +13 -0
  41. package/src/commands/deploy/util/wait.ts +23 -0
  42. package/src/commands/esbuild/ESBuildProgram.ts +1 -1
  43. package/src/commands/esbuild/__tests__/.temp/getDeps/package.json +1 -0
  44. package/src/commands/generate/__tests__/.temp/test-cli/CHANGELOG.md +1 -0
  45. package/src/commands/generate/__tests__/.temp/test-cli/README.md +1 -0
  46. package/src/commands/generate/__tests__/.temp/test-cli/bin.js +3 -0
  47. package/src/commands/generate/__tests__/.temp/test-cli/package.json +44 -0
  48. package/src/commands/generate/__tests__/.temp/test-cli/src/bin.ts +13 -0
  49. package/src/commands/generate/__tests__/.temp/test-cli/src/index.ts +1 -0
  50. package/src/commands/generate/__tests__/.temp/test-cli/tsconfig.json +28 -0
  51. package/src/utils/__tests__/nodeCacheDir.test.ts +6 -0
  52. package/src/utils/nodeCacheDir.ts +54 -0
  53. package/templates/cli/tsconfig.json +28 -28
  54. package/templates/lib/tsconfig.json +28 -28
  55. package/tsconfig.json +34 -34
  56. package/src/commands/sync/__tests__/.temp/package.json +0 -16
@@ -0,0 +1,16 @@
1
+ import { GhPagesDeployService } from '../../DeployService'
2
+ import * as path from 'path'
3
+
4
+ async function deployGhPages() {
5
+ const tempPath = path.resolve(__dirname, '../.temp/')
6
+ const ghPagesDeployService = new GhPagesDeployService({
7
+ cwd: tempPath,
8
+ dest: 'dist',
9
+ remote: 'examples/test-app',
10
+ })
11
+ const now = Date.now()
12
+ await ghPagesDeployService.deploy().on('process', (title) => console.log(`[${now}] ${title}`))
13
+ }
14
+
15
+ // noinspection JSIgnoredPromiseFromCall
16
+ deployGhPages()
@@ -0,0 +1,48 @@
1
+ import {
2
+ BaseDeployOptions,
3
+ DeployTypeEnum,
4
+ GhPagesDeployService,
5
+ IDeployService,
6
+ SftpDeployOptions,
7
+ SftpDeployService,
8
+ } from './DeployService'
9
+ import * as path from 'path'
10
+ import { pathExists, readJson } from 'fs-extra'
11
+
12
+ async function getOptions(cwd: string): Promise<BaseDeployOptions> {
13
+ const pkgJsonPath = path.resolve(cwd, 'package.json')
14
+ if (await pathExists(pkgJsonPath)) {
15
+ const config = (await readJson(pkgJsonPath)).deploy
16
+ if (!config) {
17
+ throw new Error('找不到配置')
18
+ }
19
+ return config
20
+ }
21
+ throw new Error('找不到配置')
22
+ }
23
+
24
+ export async function deploy(options: Omit<BaseDeployOptions, 'type'>): Promise<void> {
25
+ const deployOptions = await getOptions(options.cwd)
26
+ let service: IDeployService
27
+ const _options = {
28
+ ...options,
29
+ ...deployOptions,
30
+ } as unknown as SftpDeployOptions
31
+ switch (deployOptions.type) {
32
+ case DeployTypeEnum.Sftp:
33
+ service = new SftpDeployService(_options)
34
+ break
35
+ case DeployTypeEnum.GhPages:
36
+ service = new GhPagesDeployService(_options)
37
+ break
38
+ default:
39
+ throw new Error('未知的部署预设类型 ' + deployOptions.type)
40
+ }
41
+ const [isValidate, errorText] = service.validate()
42
+ if (!isValidate) {
43
+ throw new Error(errorText)
44
+ }
45
+ await service.deploy().on('process', (title) => {
46
+ console.info(title)
47
+ })
48
+ }
@@ -0,0 +1,7 @@
1
+ import { Command } from 'commander'
2
+ import { deploy } from './deploy'
3
+ import * as path from 'path'
4
+
5
+ export const deployCommand = new Command('deploy')
6
+ .description('部署项目到远端')
7
+ .action(() => deploy({ cwd: path.resolve() }))
@@ -0,0 +1,31 @@
1
+ import { close, open, remove } from 'fs-extra'
2
+ import path from 'path'
3
+
4
+ export class FileLock {
5
+ constructor(private readonly lockFilePath: string) {}
6
+
7
+ private lockId?: number
8
+
9
+ /**
10
+ * 加锁
11
+ */
12
+ async lock(): Promise<boolean> {
13
+ try {
14
+ this.lockId = await open(path.resolve(this.lockFilePath), 'wx')
15
+ return true
16
+ } catch (e) {
17
+ return false
18
+ }
19
+ }
20
+
21
+ /**
22
+ * 解锁
23
+ */
24
+ async unlock(): Promise<void> {
25
+ if (!this.lockId) {
26
+ throw new Error('未加锁')
27
+ }
28
+ await remove(path.resolve(this.lockFilePath))
29
+ close(this.lockId)
30
+ }
31
+ }
@@ -0,0 +1,34 @@
1
+ import { ConditionalKeys, PromiseValue } from 'type-fest'
2
+
3
+ type VoidFunc = ((...args: any[]) => void) | undefined
4
+
5
+ export type EventExtPromise<T, E> = Promise<T> & {
6
+ on<K extends ConditionalKeys<E, VoidFunc>>(type: K, callback: E[K]): EventExtPromise<T, E>
7
+ }
8
+
9
+ export class PromiseUtil {
10
+ /**
11
+ * 创建一个支持 on* 事件的 Promise 实例
12
+ * @param executor
13
+ */
14
+ static wrapOnEvent<
15
+ F extends (events: any) => Promise<any>,
16
+ E extends Parameters<F>[0],
17
+ K extends ConditionalKeys<E, VoidFunc>,
18
+ >(executor: F): EventExtPromise<PromiseValue<ReturnType<F>>, E> {
19
+ const events: Partial<Pick<E, K>> = {}
20
+ const res = new Promise(async (resolve, reject) => {
21
+ await new Promise((resolve) => setTimeout(resolve, 0))
22
+ try {
23
+ resolve(await executor(events))
24
+ } catch (e) {
25
+ reject(e)
26
+ }
27
+ })
28
+ Reflect.set(res, 'on', (type: K, callback: E[K]) => {
29
+ events[type] = callback
30
+ return res
31
+ })
32
+ return res as any
33
+ }
34
+ }
@@ -0,0 +1,30 @@
1
+ import { create, CreateOptions } from 'tar'
2
+ import { promise } from 'glob-promise'
3
+ import * as path from 'path'
4
+
5
+ export type ArchiveOptions = {
6
+ // sourceDir 源目录,一般设置为 dist
7
+ sourceDir: string
8
+ // destPath 目标位置,可能是 <packageName>.jpl
9
+ destPath: string
10
+ }
11
+
12
+ /**
13
+ * 创建 jpl 压缩文件
14
+ * @param options
15
+ */
16
+ export async function createArchive(options: ArchiveOptions): Promise<void> {
17
+ const sourceDir = path.resolve(options.sourceDir)
18
+ const destPath = path.resolve(options.destPath)
19
+ const distFiles = (await promise(`${sourceDir}/**/*`, { nodir: true })).map((f) => f.substr(sourceDir.length + 1))
20
+ await create(
21
+ {
22
+ strict: true,
23
+ portable: true,
24
+ file: destPath,
25
+ cwd: sourceDir,
26
+ sync: true,
27
+ } as Partial<CreateOptions>,
28
+ distFiles,
29
+ )
30
+ }
@@ -0,0 +1,13 @@
1
+ import { exec, ExecOptions } from 'child_process'
2
+
3
+ export function execPromise(command: string, options?: ExecOptions): Promise<string | Buffer> {
4
+ return new Promise((resolve, reject) => {
5
+ exec(command, options, (error, stdout) => {
6
+ if (error) {
7
+ reject(error)
8
+ return
9
+ }
10
+ resolve(stdout)
11
+ })
12
+ })
13
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 等待指定的时间/等待指定表达式成立
3
+ * 如果未指定等待条件则立刻执行
4
+ * 注: 此实现在 nodejs 10- 会存在宏任务与微任务的问题,切记 async-await 本质上还是 Promise 的语法糖,实际上并非真正的同步函数!!!即便在浏览器,也不要依赖于这种特性。
5
+ * @param param 等待时间/等待条件
6
+ * @returns Promise 对象
7
+ */
8
+ export function wait(param?: number | (() => boolean | Promise<boolean>)): Promise<void> {
9
+ return new Promise((resolve) => {
10
+ if (typeof param === 'number') {
11
+ setTimeout(resolve, param)
12
+ } else if (typeof param === 'function') {
13
+ const timer = setInterval(async () => {
14
+ if (await param()) {
15
+ clearInterval(timer)
16
+ resolve()
17
+ }
18
+ }, 100)
19
+ } else {
20
+ resolve()
21
+ }
22
+ })
23
+ }
@@ -31,7 +31,7 @@ export class ESBuildProgram {
31
31
  this.options.isWatch = isWatch
32
32
  }
33
33
 
34
- static readonly globalExternal = ['esbuild', 'pnpapi', 'ts-morph']
34
+ static readonly globalExternal = ['esbuild', 'pnpapi', 'ts-morph', 'ssh2']
35
35
 
36
36
  /**
37
37
  * 获取所有依赖
@@ -0,0 +1 @@
1
+ {"devDependencies":{"@types/node":"16"},"dependencies":{"ora":"^6"},"peerDependencies":{"typescript":"^4"}}
@@ -0,0 +1 @@
1
+ # @liuli-util/cli-test-cli
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ // eslint-disable-next-line no-undef
3
+ require('./dist/bin.js')
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "test-cli",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.esm.js",
6
+ "types": "dist/index.d.ts",
7
+ "license": "MIT",
8
+ "scripts": {
9
+ "build": "rimraf dist && liuli-cli build cli",
10
+ "dev": "liuli-cli build cli -w",
11
+ "start": "esno src/bin.ts",
12
+ "docs:server": "live-server docs",
13
+ "docs:dev": "typedoc --watch",
14
+ "docs:build": "rimraf docs && typedoc",
15
+ "docs:deploy": "yarn docs:build && gh-pages -d docs/ -e / -a"
16
+ },
17
+ "bin": {
18
+ "cli-name": "./bin.js"
19
+ },
20
+ "jest": {
21
+ "preset": "ts-jest"
22
+ },
23
+ "dependencies": {
24
+ "commander": "^8.2.0",
25
+ "fs-extra": "^10.0.0",
26
+ "inquirer": "^8.1.5"
27
+ },
28
+ "devDependencies": {
29
+ "@liuli-util/cli": "^3.11.1",
30
+ "@types/fs-extra": "^9.0.13",
31
+ "@types/inquirer": "^8.1.2",
32
+ "@types/jest": "^27.0.2",
33
+ "@types/lodash": "^4.14.173",
34
+ "@types/node": "^16.9.6",
35
+ "esno": "^0.9.1",
36
+ "gh-pages": "^3.2.3",
37
+ "jest": "^27.2.1",
38
+ "rimraf": "^3.0.2",
39
+ "ts-jest": "^27.0.5",
40
+ "type-fest": "^2.3.4",
41
+ "typedoc": "^0.22.4",
42
+ "typescript": "^4.4.3"
43
+ }
44
+ }
@@ -0,0 +1,13 @@
1
+ import { Command } from 'commander'
2
+ import { prompt } from 'inquirer'
3
+
4
+ new Command()
5
+ .action(async () => {
6
+ const { name } = await prompt<{ name: string }>({
7
+ type: 'input',
8
+ name: 'name',
9
+ message: '请输入名字',
10
+ })
11
+ console.log(`hello ${name}`)
12
+ })
13
+ .parse()
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "lib": [
5
+ "ESNext"
6
+ ],
7
+ "outDir": "./dist",
8
+ "skipLibCheck": true,
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "module": "ESNext",
12
+ "moduleResolution": "node",
13
+ "sourceMap": true,
14
+ "declaration": true,
15
+ "declarationMap": true
16
+ },
17
+ "include": [
18
+ "src"
19
+ ],
20
+ "typedocOptions": {
21
+ "entryPoints": [
22
+ "src/index.ts"
23
+ ],
24
+ "out": "docs",
25
+ "readme": "README.md",
26
+ "gitRemote": "origin"
27
+ }
28
+ }
@@ -0,0 +1,6 @@
1
+ import { nodeCacheDir } from '../nodeCacheDir'
2
+
3
+ it('测试 nodeCacheDir', () => {
4
+ const res = nodeCacheDir('liuli-cli')
5
+ console.log(res)
6
+ })
@@ -0,0 +1,54 @@
1
+ /*
2
+ copy by https://github.com/LinusU/node-cachedir
3
+ */
4
+ import * as os from 'os'
5
+ import * as path from 'path'
6
+
7
+ function posix(id: string) {
8
+ const cacheHome = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache')
9
+ return path.join(cacheHome, id)
10
+ }
11
+
12
+ function darwin(id: string) {
13
+ return path.join(os.homedir(), 'Library', 'Caches', id)
14
+ }
15
+
16
+ function win32(id: string) {
17
+ const appData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')
18
+ return path.join(appData, id, 'Cache')
19
+ }
20
+
21
+ const implementation = (function () {
22
+ switch (os.platform()) {
23
+ case 'darwin':
24
+ return darwin
25
+ case 'win32':
26
+ return win32
27
+ case 'aix':
28
+ case 'android':
29
+ case 'freebsd':
30
+ case 'linux':
31
+ case 'netbsd':
32
+ case 'openbsd':
33
+ case 'sunos':
34
+ return posix
35
+ default:
36
+ console.error(
37
+ `(node:${
38
+ process.pid
39
+ }) [cachedir] Warning: the platform "${os.platform()}" is not currently supported by node-cachedir, falling back to "posix". Please file an issue with your platform here: https://github.com/LinusU/node-cachedir/issues/new`,
40
+ )
41
+ return posix
42
+ }
43
+ })()
44
+
45
+ export function nodeCacheDir(id: string): string {
46
+ if (id.length === 0) {
47
+ throw new Error('id cannot be empty')
48
+ }
49
+ if (/[^0-9a-zA-Z-]/.test(id)) {
50
+ throw new Error('id cannot contain special characters')
51
+ }
52
+
53
+ return implementation(id)
54
+ }
@@ -1,28 +1,28 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "lib": [
5
- "ESNext"
6
- ],
7
- "outDir": "./dist",
8
- "skipLibCheck": true,
9
- "esModuleInterop": true,
10
- "strict": true,
11
- "module": "ESNext",
12
- "moduleResolution": "node",
13
- "sourceMap": true,
14
- "declaration": true,
15
- "declarationMap": true
16
- },
17
- "include": [
18
- "src"
19
- ],
20
- "typedocOptions": {
21
- "entryPoints": [
22
- "src/index.ts"
23
- ],
24
- "out": "docs",
25
- "readme": "README.md",
26
- "gitRemote": "origin"
27
- }
28
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "lib": [
5
+ "ESNext"
6
+ ],
7
+ "outDir": "./dist",
8
+ "skipLibCheck": true,
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "module": "ESNext",
12
+ "moduleResolution": "node",
13
+ "sourceMap": true,
14
+ "declaration": true,
15
+ "declarationMap": true
16
+ },
17
+ "include": [
18
+ "src"
19
+ ],
20
+ "typedocOptions": {
21
+ "entryPoints": [
22
+ "src/index.ts"
23
+ ],
24
+ "out": "docs",
25
+ "readme": "README.md",
26
+ "gitRemote": "origin"
27
+ }
28
+ }
@@ -1,28 +1,28 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "lib": [
5
- "ESNext"
6
- ],
7
- "outDir": "./dist",
8
- "skipLibCheck": true,
9
- "esModuleInterop": true,
10
- "strict": true,
11
- "module": "ESNext",
12
- "moduleResolution": "node",
13
- "sourceMap": true,
14
- "declaration": true,
15
- "declarationMap": true
16
- },
17
- "include": [
18
- "src"
19
- ],
20
- "typedocOptions": {
21
- "entryPoints": [
22
- "src/index.ts"
23
- ],
24
- "out": "docs",
25
- "readme": "README.md",
26
- "gitRemote": "origin"
27
- }
28
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "lib": [
5
+ "ESNext"
6
+ ],
7
+ "outDir": "./dist",
8
+ "skipLibCheck": true,
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "module": "ESNext",
12
+ "moduleResolution": "node",
13
+ "sourceMap": true,
14
+ "declaration": true,
15
+ "declarationMap": true
16
+ },
17
+ "include": [
18
+ "src"
19
+ ],
20
+ "typedocOptions": {
21
+ "entryPoints": [
22
+ "src/index.ts"
23
+ ],
24
+ "out": "docs",
25
+ "readme": "README.md",
26
+ "gitRemote": "origin"
27
+ }
28
+ }
package/tsconfig.json CHANGED
@@ -1,34 +1,34 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "lib": [
5
- "ESNext"
6
- ],
7
- "outDir": "./dist",
8
- "skipLibCheck": true,
9
- "esModuleInterop": true,
10
- "strict": true,
11
- "module": "ESNext",
12
- "moduleResolution": "node",
13
- "sourceMap": true,
14
- "declaration": true,
15
- "declarationMap": true,
16
- "resolveJsonModule": true
17
- },
18
- "include": [
19
- "src"
20
- ],
21
- "typedocOptions": {
22
- "entryPoints": [
23
- "src/index.ts"
24
- ],
25
- "out": "docs",
26
- "readme": "README.md",
27
- "gitRemote": "origin"
28
- },
29
- "ts-node": {
30
- "compilerOptions": {
31
- "module": "CommonJS"
32
- }
33
- }
34
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "lib": [
5
+ "ESNext"
6
+ ],
7
+ "outDir": "./dist",
8
+ "skipLibCheck": true,
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "module": "ESNext",
12
+ "moduleResolution": "node",
13
+ "sourceMap": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "resolveJsonModule": true
17
+ },
18
+ "include": [
19
+ "src"
20
+ ],
21
+ "typedocOptions": {
22
+ "entryPoints": [
23
+ "src/index.ts"
24
+ ],
25
+ "out": "docs",
26
+ "readme": "README.md",
27
+ "gitRemote": "origin"
28
+ },
29
+ "ts-node": {
30
+ "compilerOptions": {
31
+ "module": "CommonJS"
32
+ }
33
+ }
34
+ }
@@ -1,16 +0,0 @@
1
- {
2
- "name": "temp",
3
- "sync": [
4
- "jest"
5
- ],
6
- "jest": {
7
- "preset": "ts-jest",
8
- "testMatch": [
9
- "<rootDir>/src/**/__tests__/*.test.ts"
10
- ]
11
- },
12
- "devDependencies": {
13
- "jest": "^27.4.3",
14
- "ts-jest": "^27.0.7"
15
- }
16
- }