@liuli-util/cli 3.10.1 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +3 -3
- package/dist/PathUtil.d.ts +6 -6
- package/dist/bin.d.ts +1 -1
- package/dist/bin.js +102 -86
- package/dist/bin.js.map +3 -3
- package/dist/commands/esbuild/ESBuildProgram.d.ts +70 -10
- package/dist/commands/esbuild/ESBuildProgram.d.ts.map +1 -1
- package/dist/commands/esbuild/index.d.ts +2 -2
- package/dist/commands/esbuild/index.d.ts.map +1 -1
- package/dist/commands/esbuild/util/debounce.d.ts +15 -0
- package/dist/commands/esbuild/util/debounce.d.ts.map +1 -0
- package/dist/commands/esbuild/util/esbuildPlugins.d.ts +16 -0
- package/dist/commands/esbuild/util/esbuildPlugins.d.ts.map +1 -0
- package/dist/commands/generate/GenerateProgram.d.ts +17 -17
- package/dist/commands/generate/GenerateProgram.d.ts.map +1 -1
- package/dist/commands/generate/index.d.ts +2 -2
- package/dist/commands/sync/SyncProgram.d.ts +14 -15
- package/dist/commands/sync/SyncProgram.d.ts.map +1 -1
- package/dist/commands/sync/index.d.ts +2 -2
- package/dist/commands/sync/when.d.ts +20 -20
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +5 -0
- package/dist/index.esm.js.map +3 -3
- package/dist/index.js +5 -5
- package/dist/index.js.map +3 -3
- package/dist/utils/AsyncArray.d.ts +20 -20
- package/dist/utils/appendScript.d.ts +6 -6
- package/dist/utils/arrayToMap.d.ts +2 -2
- package/dist/utils/findParent.d.ts +7 -7
- package/dist/utils/index.d.ts +4 -4
- package/package.json +19 -7
- package/src/@types/global.d.ts +8 -0
- package/src/bin.ts +2 -2
- package/src/commands/esbuild/ESBuildProgram.ts +263 -63
- package/src/commands/esbuild/__tests__/ESBuildProgram.test.ts +125 -0
- package/src/commands/esbuild/__tests__/spinnies.test.ts +11 -0
- package/src/commands/esbuild/index.ts +14 -6
- package/src/commands/esbuild/util/debounce.ts +29 -0
- package/src/commands/esbuild/util/esbuildPlugins.ts +82 -0
- package/src/commands/generate/GenerateProgram.ts +7 -5
- package/src/commands/sync/SyncProgram.ts +6 -12
- package/src/commands/sync/__tests__/SyncProgram.test.ts +2 -2
- package/src/index.ts +3 -1
- package/src/utils/__tests__/appendScript.test.ts +1 -1
- package/templates/cli/package.json +1 -1
- package/templates/lib/package.json +1 -1
- package/dist/commands/generate/__tests__/GenerateProgram.test.d.ts +0 -2
- package/dist/commands/generate/__tests__/GenerateProgram.test.d.ts.map +0 -1
- package/dist/commands/sync/__tests__/SyncProgram.test.d.ts +0 -2
- package/dist/commands/sync/__tests__/SyncProgram.test.d.ts.map +0 -1
- package/dist/commands/sync/__tests__/when.test.d.ts +0 -2
- package/dist/commands/sync/__tests__/when.test.d.ts.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/utils/__tests__/appendScript.test.d.ts +0 -2
- package/dist/utils/__tests__/appendScript.test.d.ts.map +0 -1
- package/dist/utils/__tests__/arrayToMap.test.d.ts +0 -2
- package/dist/utils/__tests__/arrayToMap.test.d.ts.map +0 -1
- package/dist/utils/__tests__/findParent.test.d.ts +0 -2
- package/dist/utils/__tests__/findParent.test.d.ts.map +0 -1
- package/dist/utils/execPromise.d.ts +0 -4
- package/dist/utils/execPromise.d.ts.map +0 -1
- package/src/utils/execPromise.ts +0 -16
@@ -0,0 +1,125 @@
|
|
1
|
+
import { ESBuildProgram } from '../ESBuildProgram'
|
2
|
+
import * as path from 'path'
|
3
|
+
import { mkdirp, pathExists, remove, writeJson } from 'fs-extra'
|
4
|
+
import { PackageJson } from 'type-fest'
|
5
|
+
import { build, Platform, Plugin } from 'esbuild'
|
6
|
+
import { nativeNodeModules, nodeExternals } from '../util/esbuildPlugins'
|
7
|
+
|
8
|
+
describe('测试 ESBuildProgram', () => {
|
9
|
+
describe('测试 getPlatform', () => {
|
10
|
+
const base: string = path.resolve(__dirname, '.temp/getPlatform')
|
11
|
+
beforeEach(async () => {
|
12
|
+
await remove(base)
|
13
|
+
await mkdirp(base)
|
14
|
+
})
|
15
|
+
it('测试 node 类型', async () => {
|
16
|
+
await writeJson(path.resolve(base, 'package.json'), {
|
17
|
+
devDependencies: {
|
18
|
+
'@types/node': '16',
|
19
|
+
},
|
20
|
+
} as PackageJson)
|
21
|
+
expect(await ESBuildProgram.getPlatform(base)).toBe('node' as Platform)
|
22
|
+
})
|
23
|
+
it('测试浏览器类型', async () => {
|
24
|
+
await writeJson(path.resolve(base, 'tsconfig.json'), {
|
25
|
+
compilerOptions: {
|
26
|
+
lib: ['DOM'],
|
27
|
+
},
|
28
|
+
})
|
29
|
+
expect(await ESBuildProgram.getPlatform(base)).toBe('browser' as Platform)
|
30
|
+
})
|
31
|
+
it('测试通用类型', async () => {
|
32
|
+
expect(await ESBuildProgram.getPlatform(base)).toBe('neutral' as Platform)
|
33
|
+
})
|
34
|
+
})
|
35
|
+
describe('测试 getDeps', () => {
|
36
|
+
const base: string = path.resolve(__dirname, '.temp/getDeps')
|
37
|
+
beforeEach(async () => {
|
38
|
+
await remove(base)
|
39
|
+
await mkdirp(base)
|
40
|
+
})
|
41
|
+
it('基本示例', async () => {
|
42
|
+
await writeJson(path.resolve(base, 'package.json'), {
|
43
|
+
devDependencies: {
|
44
|
+
'@types/node': '16',
|
45
|
+
},
|
46
|
+
dependencies: {
|
47
|
+
ora: '^6',
|
48
|
+
},
|
49
|
+
peerDependencies: {
|
50
|
+
typescript: '^4',
|
51
|
+
},
|
52
|
+
} as PackageJson)
|
53
|
+
const res = (await ESBuildProgram.getDeps(base)).sort()
|
54
|
+
console.log(res)
|
55
|
+
expect(res).toEqual(['@types/node', 'ora', 'typescript'].sort())
|
56
|
+
})
|
57
|
+
})
|
58
|
+
describe('测试构建相关', () => {
|
59
|
+
const base: string = path.resolve()
|
60
|
+
const program = new ESBuildProgram({
|
61
|
+
base,
|
62
|
+
isWatch: false,
|
63
|
+
})
|
64
|
+
let deps: string[]
|
65
|
+
let platform: Platform
|
66
|
+
let plugins: Plugin[]
|
67
|
+
beforeEach(async () => {
|
68
|
+
await remove(path.resolve(base, 'dist'))
|
69
|
+
deps = await ESBuildProgram.getDeps(base)
|
70
|
+
platform = await ESBuildProgram.getPlatform(base)
|
71
|
+
plugins = ESBuildProgram.getPlugins(platform)
|
72
|
+
})
|
73
|
+
it('测试 genDTS', async () => {
|
74
|
+
await program.genDTS()
|
75
|
+
expect(
|
76
|
+
await pathExists(path.resolve(base, 'dist/index.d.ts')),
|
77
|
+
).toBeTruthy()
|
78
|
+
})
|
79
|
+
it('测试 getBuildCjsOption', async () => {
|
80
|
+
await build(
|
81
|
+
program.getBuildCjsOption({
|
82
|
+
deps: deps,
|
83
|
+
platform: platform,
|
84
|
+
}),
|
85
|
+
)
|
86
|
+
expect(await pathExists(path.resolve(base, 'dist/index.js'))).toBeTruthy()
|
87
|
+
})
|
88
|
+
it('测试 getBuildESMOption', async () => {
|
89
|
+
const option = program.getBuildESMOption({
|
90
|
+
deps: deps,
|
91
|
+
platform: platform,
|
92
|
+
})
|
93
|
+
console.log('option: ', deps, option)
|
94
|
+
await build(option)
|
95
|
+
expect(
|
96
|
+
await pathExists(path.resolve(base, 'dist/index.esm.js')),
|
97
|
+
).toBeTruthy()
|
98
|
+
})
|
99
|
+
it('测试 getBuildCliOption', async () => {
|
100
|
+
await build(
|
101
|
+
program.getBuildCliOption({
|
102
|
+
deps: deps,
|
103
|
+
platform: platform,
|
104
|
+
}),
|
105
|
+
)
|
106
|
+
expect(await pathExists(path.resolve(base, 'dist/bin.js'))).toBeTruthy()
|
107
|
+
})
|
108
|
+
})
|
109
|
+
})
|
110
|
+
it('测试 esbuild', async () => {
|
111
|
+
await build({
|
112
|
+
outfile: './dist/bin.js',
|
113
|
+
format: 'cjs',
|
114
|
+
sourcemap: true,
|
115
|
+
entryPoints: ['./src/bin.ts'],
|
116
|
+
bundle: true,
|
117
|
+
external: [
|
118
|
+
...ESBuildProgram.globalExternal,
|
119
|
+
// ...(await ESBuildProgram.getDeps(path.resolve())),
|
120
|
+
],
|
121
|
+
platform: 'node',
|
122
|
+
plugins: [nativeNodeModules(), nodeExternals()],
|
123
|
+
treeShaking: true,
|
124
|
+
})
|
125
|
+
})
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import Spinnies from 'spinnies'
|
2
|
+
|
3
|
+
it('测试 spinnies', async () => {
|
4
|
+
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
5
|
+
const spinnies = new Spinnies()
|
6
|
+
spinnies.add('a', { text: '开始任务 a' })
|
7
|
+
spinnies.add('b', { text: '开始任务 b' })
|
8
|
+
await wait(3000)
|
9
|
+
spinnies.succeed('a', { text: '成功运行任务 a' })
|
10
|
+
spinnies.fail('b', { text: '成功运行任务 b' })
|
11
|
+
})
|
@@ -2,15 +2,19 @@ import { Command } from 'commander'
|
|
2
2
|
import { ESBuildProgram } from './ESBuildProgram'
|
3
3
|
import path from 'path'
|
4
4
|
|
5
|
-
const
|
6
|
-
export const buildCommand = new Command('build')
|
5
|
+
export const esbuildCommand = new Command('build')
|
7
6
|
.addCommand(
|
8
|
-
new Command('
|
7
|
+
new Command('lib')
|
9
8
|
.description('使用 esbulid 将 ts lib 打包到 dist 目录,格式为 esm/cjs')
|
10
9
|
.option('-w --watch', '监视模式')
|
11
10
|
.action(async (option: { watch?: boolean }) => {
|
12
|
-
|
13
|
-
|
11
|
+
const buildProgram = new ESBuildProgram({
|
12
|
+
base: path.resolve(),
|
13
|
+
isWatch: !!option.watch,
|
14
|
+
})
|
15
|
+
await buildProgram.buildLib()
|
16
|
+
})
|
17
|
+
.alias('pkg'),
|
14
18
|
)
|
15
19
|
.addCommand(
|
16
20
|
new Command('cli')
|
@@ -19,6 +23,10 @@ export const buildCommand = new Command('build')
|
|
19
23
|
)
|
20
24
|
.option('-w, --watch', '监视模式')
|
21
25
|
.action(async (option: { watch?: boolean }) => {
|
22
|
-
|
26
|
+
const buildProgram = new ESBuildProgram({
|
27
|
+
base: path.resolve(),
|
28
|
+
isWatch: !!option.watch,
|
29
|
+
})
|
30
|
+
await buildProgram.buildCli()
|
23
31
|
}),
|
24
32
|
)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/**
|
2
|
+
* 函数去抖
|
3
|
+
* 去抖 (debounce) 去抖就是对于一定时间段的连续的函数调用,只让其执行一次
|
4
|
+
* 注: 包装后的函数如果两次操作间隔小于 delay 则不会被执行, 如果一直在操作就会一直不执行, 直到操作停止的时间大于 delay 最小间隔时间才会执行一次, 不管任何时间调用都需要停止操作等待最小延迟时间
|
5
|
+
* 应用场景主要在那些连续的操作, 例如页面滚动监听, 包装后的函数只会执行最后一次
|
6
|
+
* 注: 该函数第一次调用一定不会执行,第一次一定拿不到缓存值,后面的连续调用都会拿到上一次的缓存值。如果需要在第一次调用获取到的缓存值,则需要传入第三个参数 {@param init},默认为 {@code undefined} 的可选参数
|
7
|
+
* 注: 返回函数结果的高阶函数需要使用 {@see Proxy} 实现,以避免原函数原型链上的信息丢失
|
8
|
+
*
|
9
|
+
* @param fn 真正需要执行的操作
|
10
|
+
* @param delay 最小延迟时间,单位为 ms
|
11
|
+
* @param init 初始的缓存值,不填默认为 {@see undefined}
|
12
|
+
* @return 包装后有去抖功能的函数。该函数是异步的,与需要包装的函数 {@param fn} 是否异步没有太大关联
|
13
|
+
*/
|
14
|
+
export function debounce<
|
15
|
+
T extends (...args: any[]) => any,
|
16
|
+
R extends (...args: Parameters<T>) => Promise<ReturnType<T>>,
|
17
|
+
>(fn: T, delay: number, init: any = null): R {
|
18
|
+
let flag: number
|
19
|
+
let result = init
|
20
|
+
return async function (...args) {
|
21
|
+
return new Promise((resolve) => {
|
22
|
+
if (flag) {
|
23
|
+
clearTimeout(flag as any)
|
24
|
+
}
|
25
|
+
flag = setTimeout(() => resolve((result = fn(...args))), delay) as any
|
26
|
+
setTimeout(() => resolve(result), delay)
|
27
|
+
})
|
28
|
+
} as R
|
29
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import { Plugin } from 'esbuild'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 处理 nodejs 原生模块
|
5
|
+
* @link https://github.com/evanw/esbuild/issues/1051#issuecomment-806325487
|
6
|
+
*/
|
7
|
+
export function nativeNodeModules(): Plugin {
|
8
|
+
return {
|
9
|
+
name: 'native-node-modules',
|
10
|
+
setup(build) {
|
11
|
+
// If a ".node" file is imported within a module in the "file" namespace, resolve
|
12
|
+
// it to an absolute path and put it into the "node-file" virtual namespace.
|
13
|
+
build.onResolve({ filter: /\.node$/, namespace: 'file' }, (args) => ({
|
14
|
+
path: require.resolve(args.path, { paths: [args.resolveDir] }),
|
15
|
+
namespace: 'node-file',
|
16
|
+
}))
|
17
|
+
|
18
|
+
// Files in the "node-file" virtual namespace call "require()" on the
|
19
|
+
// path from esbuild of the ".node" file in the output directory.
|
20
|
+
build.onLoad({ filter: /.*/, namespace: 'node-file' }, (args) => ({
|
21
|
+
contents: `
|
22
|
+
import path from ${JSON.stringify(args.path)}
|
23
|
+
try { module.exports = require(path) }
|
24
|
+
catch {}
|
25
|
+
`,
|
26
|
+
}))
|
27
|
+
|
28
|
+
// If a ".node" file is imported within a module in the "node-file" namespace, put
|
29
|
+
// it in the "file" namespace where esbuild's default loading behavior will handle
|
30
|
+
// it. It is already an absolute path since we resolved it to one above.
|
31
|
+
build.onResolve(
|
32
|
+
{ filter: /\.node$/, namespace: 'node-file' },
|
33
|
+
(args) => ({
|
34
|
+
path: args.path,
|
35
|
+
namespace: 'file',
|
36
|
+
}),
|
37
|
+
)
|
38
|
+
|
39
|
+
// Tell esbuild's default loading behavior to use the "file" loader for
|
40
|
+
// these ".node" files.
|
41
|
+
const opts = build.initialOptions
|
42
|
+
opts.loader = opts.loader || {}
|
43
|
+
opts.loader['.node'] = 'file'
|
44
|
+
},
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* 排除和替换 node 内置模块
|
50
|
+
*/
|
51
|
+
export function nodeExternals(): Plugin {
|
52
|
+
return {
|
53
|
+
name: 'esbuild-plugin-node-externals',
|
54
|
+
setup(build) {
|
55
|
+
build.onResolve({ filter: /(^node:)/ }, (args) => ({
|
56
|
+
path: args.path.slice(5),
|
57
|
+
external: true,
|
58
|
+
}))
|
59
|
+
},
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
/**
|
64
|
+
* 自动排除所有依赖项
|
65
|
+
* golang 不支持 js 的一些语法,参考 https://github.com/evanw/esbuild/issues/1634
|
66
|
+
*/
|
67
|
+
export function autoExternal(): Plugin {
|
68
|
+
return {
|
69
|
+
name: 'esbuild-plugin-auto-external',
|
70
|
+
setup(build) {
|
71
|
+
build.onResolve({ filter: /.*/ }, (args) => {
|
72
|
+
if (/^\.{1,2}\//.test(args.path)) {
|
73
|
+
return
|
74
|
+
}
|
75
|
+
return {
|
76
|
+
path: args.path,
|
77
|
+
external: true,
|
78
|
+
}
|
79
|
+
})
|
80
|
+
},
|
81
|
+
}
|
82
|
+
}
|
@@ -9,7 +9,7 @@ import {
|
|
9
9
|
writeFile,
|
10
10
|
writeJSON,
|
11
11
|
} from 'fs-extra'
|
12
|
-
import
|
12
|
+
import { prompt } from 'enquirer'
|
13
13
|
import { SyncProgram } from '../sync/SyncProgram'
|
14
14
|
import { PathUtil } from '../../PathUtil'
|
15
15
|
|
@@ -30,7 +30,7 @@ export class GenerateProgram {
|
|
30
30
|
*/
|
31
31
|
async generate(config: GenerateConfig): Promise<void> {
|
32
32
|
if (!config.dest) {
|
33
|
-
const { dest } = await
|
33
|
+
const { dest } = await prompt<{ dest: string }>({
|
34
34
|
name: 'dest',
|
35
35
|
type: 'input',
|
36
36
|
message: '请输入项目名',
|
@@ -41,7 +41,7 @@ export class GenerateProgram {
|
|
41
41
|
config.dest = path.resolve(dest)
|
42
42
|
}
|
43
43
|
if (!config.template) {
|
44
|
-
const { template } = await
|
44
|
+
const { template } = await prompt<{ template: TemplateTypeEnum }>({
|
45
45
|
name: 'template',
|
46
46
|
type: 'list',
|
47
47
|
message: '请选择模板',
|
@@ -69,10 +69,12 @@ export class GenerateProgram {
|
|
69
69
|
(await pathExists(destFile)) &&
|
70
70
|
(await readdir(destFile)).some((file) => pathExists(file))
|
71
71
|
) {
|
72
|
-
const { override } = await
|
72
|
+
const { override } = await prompt<{
|
73
|
+
override: boolean
|
74
|
+
}>({
|
73
75
|
name: 'override',
|
74
76
|
type: 'confirm',
|
75
|
-
|
77
|
+
initial: true,
|
76
78
|
message: '目标位置不是一个空目录,确认要覆盖么?',
|
77
79
|
})
|
78
80
|
if (!override) {
|
@@ -1,24 +1,16 @@
|
|
1
1
|
import { readFile, readJson, writeFile, writeJson } from 'fs-extra'
|
2
2
|
import path from 'path'
|
3
|
-
import {
|
4
|
-
import { merge } from 'lodash'
|
3
|
+
import { merge } from 'lodash-es'
|
5
4
|
import { PackageJson } from 'type-fest'
|
6
5
|
import prettier from '@liuli-util/prettier-standard-config/package.json'
|
7
6
|
import eslintTs from '@liuli-util/eslint-config-ts/package.json'
|
8
7
|
import eslintReactTs from '@liuli-util/eslint-config-react-ts/package.json'
|
9
8
|
import commitlint from '@liuli-util/commitlint-standard-config/package.json'
|
10
|
-
import { prompt } from '
|
9
|
+
import { prompt } from 'enquirer'
|
11
10
|
import { isIncludeDep, isNpmPackage, isYarnRoot, isYarnSubModule } from './when'
|
12
11
|
import { appendScript, arrayToMap, AsyncArray } from '../../utils'
|
13
12
|
import { PathUtil } from '../../PathUtil'
|
14
13
|
|
15
|
-
export function installDeps(base: string, deps: string[]): void {
|
16
|
-
execSync('yarn add -D -W ' + deps.join(' '), {
|
17
|
-
stdio: 'inherit',
|
18
|
-
cwd: base,
|
19
|
-
})
|
20
|
-
}
|
21
|
-
|
22
14
|
export async function mergeJson(base: string, json: object): Promise<void> {
|
23
15
|
const pkgJsonFilePath = path.resolve(base, './package.json')
|
24
16
|
await writeJson(
|
@@ -240,8 +232,10 @@ export class SyncProgram {
|
|
240
232
|
}),
|
241
233
|
(item) => item.type,
|
242
234
|
)
|
243
|
-
const res = await prompt
|
244
|
-
|
235
|
+
const res = await prompt<{
|
236
|
+
sync: string[]
|
237
|
+
}>({
|
238
|
+
type: 'multiselect',
|
245
239
|
message: '请选择需要同步的配置项',
|
246
240
|
name: 'sync',
|
247
241
|
choices: [...configMap.keys()],
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { mkdir, readJson, remove, writeJson } from 'fs-extra'
|
2
2
|
import path from 'path'
|
3
3
|
import { SyncConfigType, SyncProgram } from '../SyncProgram'
|
4
|
-
import { merge } from 'lodash'
|
4
|
+
import { merge } from 'lodash-es'
|
5
5
|
import { PackageJson } from 'type-fest'
|
6
6
|
|
7
7
|
describe('测试 SyncProgram', () => {
|
@@ -44,7 +44,7 @@ describe('测试 SyncProgram', () => {
|
|
44
44
|
}, 100_000)
|
45
45
|
})
|
46
46
|
|
47
|
-
it('测试 lodash.merge', () => {
|
47
|
+
it('测试 lodash-es.merge', () => {
|
48
48
|
const res = merge({ arr: ['a'] }, { arr: ['b'] })
|
49
49
|
expect(res).toEqual({ arr: ['b'] })
|
50
50
|
})
|
package/src/index.ts
CHANGED
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"GenerateProgram.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/generate/__tests__/GenerateProgram.test.ts"],"names":[],"mappings":""}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"SyncProgram.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/sync/__tests__/SyncProgram.test.ts"],"names":[],"mappings":""}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"when.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/sync/__tests__/when.test.ts"],"names":[],"mappings":""}
|