@build-script/package-tools 0.0.1
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/LICENSE +21 -0
- package/README.md +136 -0
- package/config/rig.json +6 -0
- package/lib/command-file-map.generated.d.ts +23 -0
- package/lib/command-file-map.generated.d.ts.map +1 -0
- package/lib/command-file-map.generated.js +27 -0
- package/lib/command-file-map.generated.js.map +1 -0
- package/lib/commands/detect-package-change.d.ts +14 -0
- package/lib/commands/detect-package-change.d.ts.map +1 -0
- package/lib/commands/detect-package-change.js +55 -0
- package/lib/commands/detect-package-change.js.map +1 -0
- package/lib/commands/monorepo-bump-version.d.ts +22 -0
- package/lib/commands/monorepo-bump-version.d.ts.map +1 -0
- package/lib/commands/monorepo-bump-version.js +121 -0
- package/lib/commands/monorepo-bump-version.js.map +1 -0
- package/lib/commands/monorepo-cnpm-sync.d.ts +8 -0
- package/lib/commands/monorepo-cnpm-sync.d.ts.map +1 -0
- package/lib/commands/monorepo-cnpm-sync.js +16 -0
- package/lib/commands/monorepo-cnpm-sync.js.map +1 -0
- package/lib/commands/monorepo-invalid.d.ts +8 -0
- package/lib/commands/monorepo-invalid.d.ts.map +1 -0
- package/lib/commands/monorepo-invalid.js +19 -0
- package/lib/commands/monorepo-invalid.js.map +1 -0
- package/lib/commands/monorepo-list.d.ts +22 -0
- package/lib/commands/monorepo-list.d.ts.map +1 -0
- package/lib/commands/monorepo-list.js +40 -0
- package/lib/commands/monorepo-list.js.map +1 -0
- package/lib/commands/monorepo-publish.d.ts +22 -0
- package/lib/commands/monorepo-publish.d.ts.map +1 -0
- package/lib/commands/monorepo-publish.js +178 -0
- package/lib/commands/monorepo-publish.js.map +1 -0
- package/lib/commands/monorepo-tsconfig.d.ts +14 -0
- package/lib/commands/monorepo-tsconfig.d.ts.map +1 -0
- package/lib/commands/monorepo-tsconfig.js +131 -0
- package/lib/commands/monorepo-tsconfig.js.map +1 -0
- package/lib/commands/monorepo-upgrade.d.ts +8 -0
- package/lib/commands/monorepo-upgrade.d.ts.map +1 -0
- package/lib/commands/monorepo-upgrade.js +110 -0
- package/lib/commands/monorepo-upgrade.js.map +1 -0
- package/lib/commands/run-if-version-mismatch.d.ts +23 -0
- package/lib/commands/run-if-version-mismatch.d.ts.map +1 -0
- package/lib/commands/run-if-version-mismatch.js +64 -0
- package/lib/commands/run-if-version-mismatch.js.map +1 -0
- package/lib/commands/sync-my-readme.d.ts +9 -0
- package/lib/commands/sync-my-readme.d.ts.map +1 -0
- package/lib/commands/sync-my-readme.js +51 -0
- package/lib/commands/sync-my-readme.js.map +1 -0
- package/lib/commands/test.d.ts +9 -0
- package/lib/commands/test.d.ts.map +1 -0
- package/lib/commands/test.js +16 -0
- package/lib/commands/test.js.map +1 -0
- package/lib/common/cache/escape-package-path.d.ts +2 -0
- package/lib/common/cache/escape-package-path.d.ts.map +1 -0
- package/lib/common/cache/escape-package-path.js +7 -0
- package/lib/common/cache/escape-package-path.js.map +1 -0
- package/lib/common/cache/native.npm.d.ts +50 -0
- package/lib/common/cache/native.npm.d.ts.map +1 -0
- package/lib/common/cache/native.npm.js +162 -0
- package/lib/common/cache/native.npm.js.map +1 -0
- package/lib/common/functions/cli.d.ts +85 -0
- package/lib/common/functions/cli.d.ts.map +1 -0
- package/lib/common/functions/cli.js +110 -0
- package/lib/common/functions/cli.js.map +1 -0
- package/lib/common/functions/global-lifecycle.d.ts +5 -0
- package/lib/common/functions/global-lifecycle.d.ts.map +1 -0
- package/lib/common/functions/global-lifecycle.js +34 -0
- package/lib/common/functions/global-lifecycle.js.map +1 -0
- package/lib/common/git/git.d.ts +12 -0
- package/lib/common/git/git.d.ts.map +1 -0
- package/lib/common/git/git.js +65 -0
- package/lib/common/git/git.js.map +1 -0
- package/lib/common/package-manager/constant.d.ts +2 -0
- package/lib/common/package-manager/constant.d.ts.map +1 -0
- package/lib/common/package-manager/constant.js +2 -0
- package/lib/common/package-manager/constant.js.map +1 -0
- package/lib/common/package-manager/driver.abstract.d.ts +41 -0
- package/lib/common/package-manager/driver.abstract.d.ts.map +1 -0
- package/lib/common/package-manager/driver.abstract.js +138 -0
- package/lib/common/package-manager/driver.abstract.js.map +1 -0
- package/lib/common/package-manager/driver.npm.d.ts +7 -0
- package/lib/common/package-manager/driver.npm.d.ts.map +1 -0
- package/lib/common/package-manager/driver.npm.js +11 -0
- package/lib/common/package-manager/driver.npm.js.map +1 -0
- package/lib/common/package-manager/driver.pnpm.d.ts +7 -0
- package/lib/common/package-manager/driver.pnpm.d.ts.map +1 -0
- package/lib/common/package-manager/driver.pnpm.js +83 -0
- package/lib/common/package-manager/driver.pnpm.js.map +1 -0
- package/lib/common/package-manager/functions.d.ts +13 -0
- package/lib/common/package-manager/functions.d.ts.map +1 -0
- package/lib/common/package-manager/functions.js +95 -0
- package/lib/common/package-manager/functions.js.map +1 -0
- package/lib/common/package-manager/package-json.d.ts +5 -0
- package/lib/common/package-manager/package-json.d.ts.map +1 -0
- package/lib/common/package-manager/package-json.js +50 -0
- package/lib/common/package-manager/package-json.js.map +1 -0
- package/lib/common/package-manager/package-manager.d.ts +5 -0
- package/lib/common/package-manager/package-manager.d.ts.map +1 -0
- package/lib/common/package-manager/package-manager.js +27 -0
- package/lib/common/package-manager/package-manager.js.map +1 -0
- package/lib/common/package-manager/proxy.d.ts +5 -0
- package/lib/common/package-manager/proxy.d.ts.map +1 -0
- package/lib/common/package-manager/proxy.js +116 -0
- package/lib/common/package-manager/proxy.js.map +1 -0
- package/lib/common/shared-jobs/cnpm-sync.d.ts +4 -0
- package/lib/common/shared-jobs/cnpm-sync.d.ts.map +1 -0
- package/lib/common/shared-jobs/cnpm-sync.js +52 -0
- package/lib/common/shared-jobs/cnpm-sync.js.map +1 -0
- package/lib/common/shared-jobs/detect-change-job.d.ts +12 -0
- package/lib/common/shared-jobs/detect-change-job.d.ts.map +1 -0
- package/lib/common/shared-jobs/detect-change-job.js +52 -0
- package/lib/common/shared-jobs/detect-change-job.js.map +1 -0
- package/lib/common/shared-jobs/publish-package-version-job.d.ts +4 -0
- package/lib/common/shared-jobs/publish-package-version-job.d.ts.map +1 -0
- package/lib/common/shared-jobs/publish-package-version-job.js +40 -0
- package/lib/common/shared-jobs/publish-package-version-job.js.map +1 -0
- package/lib/common/taball/decompress.d.ts +2 -0
- package/lib/common/taball/decompress.d.ts.map +1 -0
- package/lib/common/taball/decompress.js +39 -0
- package/lib/common/taball/decompress.js.map +1 -0
- package/lib/common/taball/file-download.d.ts +15 -0
- package/lib/common/taball/file-download.d.ts.map +1 -0
- package/lib/common/taball/file-download.js +137 -0
- package/lib/common/taball/file-download.js.map +1 -0
- package/lib/common/temp-work-folder.d.ts +20 -0
- package/lib/common/temp-work-folder.d.ts.map +1 -0
- package/lib/common/temp-work-folder.js +68 -0
- package/lib/common/temp-work-folder.js.map +1 -0
- package/lib/common/version.generated.d.ts +12 -0
- package/lib/common/version.generated.d.ts.map +1 -0
- package/lib/common/version.generated.js +17 -0
- package/lib/common/version.generated.js.map +1 -0
- package/lib/main.d.ts +2 -0
- package/lib/main.d.ts.map +1 -0
- package/lib/main.js +115 -0
- package/lib/main.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/load.js +12 -0
- package/package.json +59 -0
- package/src/command-file-map.generated.ts +28 -0
- package/src/command-file-map.generator.ts +23 -0
- package/src/commands/detect-package-change.ts +57 -0
- package/src/commands/monorepo-bump-version.ts +152 -0
- package/src/commands/monorepo-cnpm-sync.ts +19 -0
- package/src/commands/monorepo-invalid.ts +22 -0
- package/src/commands/monorepo-list.ts +46 -0
- package/src/commands/monorepo-publish.ts +227 -0
- package/src/commands/monorepo-tsconfig.ts +156 -0
- package/src/commands/monorepo-upgrade.ts +131 -0
- package/src/commands/run-if-version-mismatch.ts +78 -0
- package/src/commands/sync-my-readme.ts +56 -0
- package/src/commands/test.ts +18 -0
- package/src/common/cache/escape-package-path.ts +6 -0
- package/src/common/cache/native.npm.ts +206 -0
- package/src/common/functions/cli.ts +145 -0
- package/src/common/functions/global-lifecycle.ts +40 -0
- package/src/common/git/git.ts +75 -0
- package/src/common/package-manager/constant.ts +1 -0
- package/src/common/package-manager/driver.abstract.ts +162 -0
- package/src/common/package-manager/driver.npm.ts +13 -0
- package/src/common/package-manager/driver.pnpm.ts +113 -0
- package/src/common/package-manager/functions.ts +117 -0
- package/src/common/package-manager/package-json.ts +56 -0
- package/src/common/package-manager/package-manager.ts +46 -0
- package/src/common/package-manager/proxy.ts +129 -0
- package/src/common/shared-jobs/cnpm-sync.ts +57 -0
- package/src/common/shared-jobs/detect-change-job.ts +76 -0
- package/src/common/shared-jobs/publish-package-version-job.ts +46 -0
- package/src/common/taball/decompress.ts +41 -0
- package/src/common/taball/file-download.ts +166 -0
- package/src/common/temp-work-folder.ts +80 -0
- package/src/common/version.generated.ts +18 -0
- package/src/common/version.generator.ts +15 -0
- package/src/global.d.ts +17 -0
- package/src/main.ts +144 -0
- package/src/tsconfig.json +11 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { logger } from '@idlebox/logger';
|
|
2
|
+
import { commandInPath, execLazyError, exists } from '@idlebox/node';
|
|
3
|
+
import { rm } from 'node:fs/promises';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { isVerbose } from '../functions/cli.js';
|
|
6
|
+
|
|
7
|
+
export async function requireGitInPath() {
|
|
8
|
+
if (await commandInPath('git')) {
|
|
9
|
+
} else {
|
|
10
|
+
throw new Error('git必须在PATH中');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class GitWorkingTree {
|
|
15
|
+
constructor(public readonly path: string) {}
|
|
16
|
+
|
|
17
|
+
protected _exec(cmds: string[]) {
|
|
18
|
+
return execLazyError('git', cmds, { cwd: this.path, verbose: isVerbose });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async init() {
|
|
22
|
+
logger.debug('初始化git工作区: %s', this.path);
|
|
23
|
+
const gitDir = resolve(this.path, '.git');
|
|
24
|
+
if (await exists(gitDir)) {
|
|
25
|
+
logger.debug(' - 删除已有.git文件夹');
|
|
26
|
+
await rm(gitDir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
await this._exec(['init']);
|
|
29
|
+
await this._exec(['add', '.']);
|
|
30
|
+
await this._exec(['commit', '-m', 'Init']);
|
|
31
|
+
logger.debug('(初始化完成)');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async commitChanges() {
|
|
35
|
+
logger.debug('检测文件更改:');
|
|
36
|
+
|
|
37
|
+
const { stdout: testOut } = await this._exec(['status']);
|
|
38
|
+
const statusOut = testOut.toString().trim();
|
|
39
|
+
if (statusOut.includes('nothing to commit, working tree clean')) {
|
|
40
|
+
logger.debug(' git工作区状态: 干净');
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
// if (isDebugMode) {
|
|
44
|
+
// await execa('git', ['diff'], { cwd: this.path, stdio: ['ignore', 2, 2] });
|
|
45
|
+
// }
|
|
46
|
+
logger.debug(' git工作区状态: 有修改');
|
|
47
|
+
|
|
48
|
+
await this._exec(['add', '.']);
|
|
49
|
+
await this._exec(['commit', '-m', 'DetectChangedFiles']);
|
|
50
|
+
|
|
51
|
+
const { stdout } = await this._exec(['log', '--name-only', '-1']);
|
|
52
|
+
const lines = stdout
|
|
53
|
+
.toString()
|
|
54
|
+
.trim()
|
|
55
|
+
.split(/\n/g)
|
|
56
|
+
.map((i) => i.trim())
|
|
57
|
+
.filter((i) => i.length > 0);
|
|
58
|
+
const titleLine = lines.indexOf('DetectChangedFiles');
|
|
59
|
+
if (titleLine === -1) {
|
|
60
|
+
throw new Error('git commit - 未知错误');
|
|
61
|
+
}
|
|
62
|
+
const files = lines.slice(titleLine + 1);
|
|
63
|
+
|
|
64
|
+
logger.debug(
|
|
65
|
+
' 文件更改: %d 个 (%s%s)',
|
|
66
|
+
files.length,
|
|
67
|
+
files.slice(0, 5).join(', '),
|
|
68
|
+
files.length > 5 ? ' ...' : '',
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return files.map((item) => {
|
|
72
|
+
return item.replace('Would remove ', '');
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org/';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { MonorepoWorkspace } from '@build-script/monorepo-lib';
|
|
2
|
+
import { ensureLinkTarget } from '@idlebox/ensure-symlink';
|
|
3
|
+
import { logger } from '@idlebox/logger';
|
|
4
|
+
import { exists, writeFileIfChange } from '@idlebox/node';
|
|
5
|
+
import { execa } from 'execa';
|
|
6
|
+
import { dirname, resolve } from 'node:path';
|
|
7
|
+
import { NpmCacheHandler } from '../cache/native.npm.js';
|
|
8
|
+
import { registryInput } from '../functions/cli.js';
|
|
9
|
+
import { TempWorkingFolder } from '../temp-work-folder.js';
|
|
10
|
+
import { DEFAULT_NPM_REGISTRY } from './constant.js';
|
|
11
|
+
import { cachedPackageJson } from './package-json.js';
|
|
12
|
+
|
|
13
|
+
export interface IUploadResult {
|
|
14
|
+
name: string;
|
|
15
|
+
version: string;
|
|
16
|
+
published: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export enum PackageManagerUsageKind {
|
|
20
|
+
Read = 0,
|
|
21
|
+
Write = 1,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export abstract class PackageManager {
|
|
25
|
+
abstract readonly binary: string;
|
|
26
|
+
public readonly projectPath: string;
|
|
27
|
+
private readonly configTemp;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
public readonly usageKind: PackageManagerUsageKind,
|
|
31
|
+
public readonly workspace: MonorepoWorkspace,
|
|
32
|
+
subdir = process.cwd(),
|
|
33
|
+
) {
|
|
34
|
+
this.configTemp = new TempWorkingFolder(this.workspace, 'package-manager', true);
|
|
35
|
+
this.projectPath = resolve(workspace.root, subdir);
|
|
36
|
+
if (!this.projectPath.startsWith(workspace.root)) {
|
|
37
|
+
throw new Error(`project "${this.projectPath}" is outside the workspace root`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public install() {
|
|
42
|
+
return execa(this.binary, ['install'], { cwd: this.projectPath, stdio: 'inherit' });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public pack(saveAs: string, packagePath = this.projectPath) {
|
|
46
|
+
logger.verbose(`打包项目: ${packagePath}`);
|
|
47
|
+
return this._pack(saveAs, packagePath);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected abstract _pack(saveAs: string, packagePath: string): Promise<string>;
|
|
51
|
+
|
|
52
|
+
async loadPackageJson() {
|
|
53
|
+
return cachedPackageJson(resolve(this.projectPath, 'package.json'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getScope() {
|
|
57
|
+
const pkg = await this.loadPackageJson();
|
|
58
|
+
if (pkg.name?.startsWith('@')) {
|
|
59
|
+
const name = pkg.name.split('/')[0];
|
|
60
|
+
return name;
|
|
61
|
+
} else {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getConfig(key: string): Promise<any> {
|
|
67
|
+
const pkgPublishConfig = this.workspace.getNpmRCPath(true);
|
|
68
|
+
if (this.usageKind === PackageManagerUsageKind.Read || !(await exists(pkgPublishConfig))) {
|
|
69
|
+
return this._get_config(dirname(this.workspace.getNpmRCPath(false)), key);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!this.configTemp.exists) {
|
|
73
|
+
await this.configTemp.mkdir();
|
|
74
|
+
await ensureLinkTarget(pkgPublishConfig, `${this.configTemp.path}/.npmrc`);
|
|
75
|
+
await writeFileIfChange(`${this.configTemp.path}/package.json`, '{}');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return this._get_config(this.configTemp.path, key);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private async _get_config(cwd: string, key: string) {
|
|
82
|
+
const scope = await this.getScope();
|
|
83
|
+
if (scope) {
|
|
84
|
+
const { stdout } = await this._execGetOut(cwd, ['config', 'get', `${scope}:${key}`]);
|
|
85
|
+
logger.debug('$ npm config get %s:%s -> %s', scope, key, stdout);
|
|
86
|
+
if (`${stdout}` !== 'undefined') {
|
|
87
|
+
return stdout;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const { stdout } = await this._execGetOut(cwd, ['config', 'get', key]);
|
|
91
|
+
logger.debug('$ npm config get %s -> %s', key, stdout);
|
|
92
|
+
return stdout === 'undefined' ? undefined : stdout;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected abstract _uploadTarball(pack: string, cwd: string): Promise<IUploadResult>;
|
|
96
|
+
public async uploadTarball(pack: string, cwd: string = this.projectPath) {
|
|
97
|
+
logger.debug(`上传压缩包: ${pack}`);
|
|
98
|
+
try {
|
|
99
|
+
const r = await this._uploadTarball(pack, cwd);
|
|
100
|
+
logger.debug(' 发布成功: %s @ %s [%s]', r.name, r.version, r.published);
|
|
101
|
+
return r;
|
|
102
|
+
} catch (e: any) {
|
|
103
|
+
logger.error` tarball发布失败! ${e}`;
|
|
104
|
+
throw e;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
protected async _execGetOut(cwd: string, cmds: string[], reject = true) {
|
|
109
|
+
const result = await execa(this.binary, cmds, {
|
|
110
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
111
|
+
cwd: cwd,
|
|
112
|
+
reject: reject,
|
|
113
|
+
stripFinalNewline: true,
|
|
114
|
+
encoding: 'utf8',
|
|
115
|
+
all: true,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
get stdout() {
|
|
120
|
+
return result.stdout.trim();
|
|
121
|
+
},
|
|
122
|
+
get stderr() {
|
|
123
|
+
return result.stderr.trim();
|
|
124
|
+
},
|
|
125
|
+
get all() {
|
|
126
|
+
return result.all.trim();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private _cachedReg?: string;
|
|
132
|
+
public async getNpmRegistry() {
|
|
133
|
+
if (!this._cachedReg) {
|
|
134
|
+
switch (registryInput) {
|
|
135
|
+
case 'detect':
|
|
136
|
+
logger.debug(`检测registry地址: ${registryInput}`);
|
|
137
|
+
this._cachedReg = await this.getConfig('registry');
|
|
138
|
+
break;
|
|
139
|
+
default:
|
|
140
|
+
if (!registryInput.startsWith('https://')) {
|
|
141
|
+
throw new Error(`不支持的--registry协议: ${registryInput}`);
|
|
142
|
+
}
|
|
143
|
+
logger.debug('使用命令行提供的registry地址 (%s)', registryInput);
|
|
144
|
+
this._cachedReg = registryInput;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return this._cachedReg || DEFAULT_NPM_REGISTRY;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private _cache_handler?: NpmCacheHandler;
|
|
151
|
+
async createCacheHandler() {
|
|
152
|
+
if (!this._cache_handler) {
|
|
153
|
+
const registry = await this.getNpmRegistry();
|
|
154
|
+
|
|
155
|
+
const path = await this.getConfig('cache');
|
|
156
|
+
if (!path) throw new Error('npm config get cache返回为空');
|
|
157
|
+
|
|
158
|
+
this._cache_handler = new NpmCacheHandler(this, registry, resolve(path, '_cacache'));
|
|
159
|
+
}
|
|
160
|
+
return this._cache_handler;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PackageManager, type IUploadResult } from './driver.abstract.js';
|
|
2
|
+
|
|
3
|
+
export class NPM extends PackageManager {
|
|
4
|
+
override binary = 'npm';
|
|
5
|
+
|
|
6
|
+
override async _pack(_saveAs: string, _packagePath: string): Promise<string> {
|
|
7
|
+
throw new Error('Method not implemented.');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
override async _uploadTarball(_pack: string, _cwd: string): Promise<IUploadResult> {
|
|
11
|
+
throw new Error('Method not implemented.');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { prettyPrintError } from '@idlebox/common';
|
|
2
|
+
import { logger } from '@idlebox/logger';
|
|
3
|
+
import { execLazyError, exists } from '@idlebox/node';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { isVerbose } from '../functions/cli.js';
|
|
6
|
+
import { PackageManager, type IUploadResult } from './driver.abstract.js';
|
|
7
|
+
|
|
8
|
+
interface IPnpmPublishResult {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
version: string;
|
|
12
|
+
size: number;
|
|
13
|
+
unpackedSize: number;
|
|
14
|
+
shasum: string; // 'e320229fe019545b8e384ff19961de18d83c2673';
|
|
15
|
+
integrity: string; // 'sha512-0HwCZquIeAlDPVBfb9FEpvD+fzRaw+t7+K9L4cUKFi0l2NbClu/YnmBt9rJy/pdkkfcnuB1CS7lBfsUN6AwVCA==';
|
|
16
|
+
filename: string; // 'idlebox-common-1.4.2.tgz';
|
|
17
|
+
files: {
|
|
18
|
+
path: string;
|
|
19
|
+
size: number;
|
|
20
|
+
mode: number;
|
|
21
|
+
}[];
|
|
22
|
+
entryCount: number;
|
|
23
|
+
bundled: []; // ?
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type RegistryErrorField = {
|
|
27
|
+
code: string;
|
|
28
|
+
summary: string;
|
|
29
|
+
detail: string;
|
|
30
|
+
};
|
|
31
|
+
class RegistryError extends Error {
|
|
32
|
+
constructor(private readonly error: RegistryErrorField) {
|
|
33
|
+
super(error.summary || 'no error summary');
|
|
34
|
+
|
|
35
|
+
if (this.error.detail) {
|
|
36
|
+
this.stack = this.error.detail;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get code() {
|
|
41
|
+
return this.error.code;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const duplicatePublishRegex = /You cannot publish over the previously published versions: (.+)/;
|
|
46
|
+
const duplicatePublishOnUnpublishRegex = /Cannot publish over previously published version "(.+)"/;
|
|
47
|
+
|
|
48
|
+
export class PNPM extends PackageManager {
|
|
49
|
+
override binary = 'pnpm';
|
|
50
|
+
|
|
51
|
+
override async _pack(saveAs: string, packagePath: string) {
|
|
52
|
+
const chProcess = await execLazyError(this.binary, ['pack', '--out', saveAs], {
|
|
53
|
+
cwd: packagePath,
|
|
54
|
+
verbose: isVerbose,
|
|
55
|
+
env: { LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8' },
|
|
56
|
+
});
|
|
57
|
+
const lastLine = chProcess.stdout.trim().split('\n').pop();
|
|
58
|
+
if (!lastLine) {
|
|
59
|
+
throw new Error('impossible: string split empty?');
|
|
60
|
+
}
|
|
61
|
+
const output = resolve(packagePath, lastLine);
|
|
62
|
+
|
|
63
|
+
if (await exists(output)) {
|
|
64
|
+
return output;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
throw new Error(`pnpm pack失败: ${output} 不存在`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
override async _uploadTarball(pack: string, cwd: string): Promise<IUploadResult> {
|
|
71
|
+
let result: IPnpmPublishResult;
|
|
72
|
+
const cmds = ['publish', pack, '--json', '--no-git-checks'];
|
|
73
|
+
const { stdout, all } = await this._execGetOut(cwd, cmds, false);
|
|
74
|
+
try {
|
|
75
|
+
const out = JSON.parse(stdout);
|
|
76
|
+
if (out.error) {
|
|
77
|
+
throw new RegistryError(out.error);
|
|
78
|
+
} else {
|
|
79
|
+
result = out;
|
|
80
|
+
if (!result.name || !result.version) {
|
|
81
|
+
logger.fatal`npm registry return invalid response:\n long<${stdout}>`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch (e: any) {
|
|
85
|
+
if (e instanceof RegistryError) {
|
|
86
|
+
let dupVer: string | undefined;
|
|
87
|
+
dupVer = duplicatePublishRegex.exec(e.message)?.[1];
|
|
88
|
+
if (!dupVer) {
|
|
89
|
+
dupVer = duplicatePublishOnUnpublishRegex.exec(e.message)?.[1];
|
|
90
|
+
}
|
|
91
|
+
if (dupVer) {
|
|
92
|
+
return {
|
|
93
|
+
published: false,
|
|
94
|
+
name: '', // TODO
|
|
95
|
+
version: dupVer,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
logger.warn`publish long<${pack}> error: ${e}`;
|
|
101
|
+
console.error(all);
|
|
102
|
+
prettyPrintError(`${this.binary} ${cmds.join(' ')}`, e);
|
|
103
|
+
|
|
104
|
+
throw e;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
published: true,
|
|
109
|
+
name: result.name,
|
|
110
|
+
version: result.version,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { getNpmConfigValue } from '@idlebox/node';
|
|
2
|
+
import pacote from 'pacote';
|
|
3
|
+
import asyncPool from 'tiny-async-pool';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 将包规范字符串分割为包名和版本号
|
|
7
|
+
* @param value package spec, e.g. `@x/y@ver` or `@x/y`
|
|
8
|
+
*/
|
|
9
|
+
export function splitPackageSpecSimple(value: string) {
|
|
10
|
+
const at = value.indexOf('@', 1);
|
|
11
|
+
if (at === -1) return [value, '']; // @xxx/yyy | xxx
|
|
12
|
+
return [value.slice(0, at), value.slice(at + 1)]; // zzz@ver
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const isSingleVersionString = /^(?<prefix>[><]=?|[~^])?(?<version>(\d+\.)*\d+(-\S+)?)$/;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 检查版本号是否是 version 或者 npm:@package@version 的格式
|
|
19
|
+
* 然后返回alias和version
|
|
20
|
+
*/
|
|
21
|
+
export function splitAliasVersion(version: string): [string, string] {
|
|
22
|
+
const m = version.match(isSingleVersionString);
|
|
23
|
+
if (m?.groups?.version) {
|
|
24
|
+
return ['', m.groups.version];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (version.startsWith('npm:')) {
|
|
28
|
+
const [alias, ver] = splitPackageSpecSimple(version.slice(4));
|
|
29
|
+
const m = ver.match(isSingleVersionString);
|
|
30
|
+
if (m?.groups?.version) {
|
|
31
|
+
return [alias, m.groups.version];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return ['', ''];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function resolveNpmVersion([packageName, currentVersion]: [string, string]) {
|
|
39
|
+
let lastError: any;
|
|
40
|
+
let packageReference = packageName;
|
|
41
|
+
|
|
42
|
+
if (currentVersion.startsWith('workspace:')) {
|
|
43
|
+
throw new Error('workspace package pass into npm resolver');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (currentVersion.startsWith('npm:')) {
|
|
47
|
+
packageReference = currentVersion.substring(4);
|
|
48
|
+
const vsplit = packageReference.lastIndexOf('@');
|
|
49
|
+
if (vsplit > 1) {
|
|
50
|
+
packageReference = packageReference.substring(0, vsplit);
|
|
51
|
+
}
|
|
52
|
+
} else if (currentVersion.includes('/')) {
|
|
53
|
+
console.error('[npm:resolve] version of "%s: %s" is not supported', packageName, currentVersion);
|
|
54
|
+
return [packageName, currentVersion, currentVersion];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const d = new Date();
|
|
58
|
+
d.setMinutes(d.getMinutes() - 1);
|
|
59
|
+
let retryCnt = 5;
|
|
60
|
+
let currentTry = 0;
|
|
61
|
+
let maxRetry = 100;
|
|
62
|
+
while (--retryCnt && --maxRetry && ++currentTry) {
|
|
63
|
+
try {
|
|
64
|
+
const pkgjson = await pacote.manifest(`${packageReference}@latest`, { before: d });
|
|
65
|
+
let newVersion = `^${pkgjson.version}`;
|
|
66
|
+
if (packageReference !== packageName) {
|
|
67
|
+
newVersion = `npm:${packageReference}@${newVersion}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return [packageName, newVersion, currentVersion];
|
|
71
|
+
} catch (e: any) {
|
|
72
|
+
console.error(
|
|
73
|
+
'[npm:resolve][try %s] failed fetch package "%s": [%s] %s',
|
|
74
|
+
currentTry,
|
|
75
|
+
packageName,
|
|
76
|
+
e.code,
|
|
77
|
+
e.message
|
|
78
|
+
);
|
|
79
|
+
lastError = e;
|
|
80
|
+
if (e.code === 'ECONNRESET') {
|
|
81
|
+
retryCnt++;
|
|
82
|
+
} else if (e.code === 'E404') {
|
|
83
|
+
return [packageName, currentVersion, currentVersion];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
throw lastError;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const cs = process.stdout.isTTY ? '\x1B[38;5;12m' : '';
|
|
91
|
+
const ce = process.stdout.isTTY ? '\x1B[0m' : '';
|
|
92
|
+
|
|
93
|
+
export async function resolveNpm(versions: Map<string, string>) {
|
|
94
|
+
let i = 1;
|
|
95
|
+
const total = versions.size;
|
|
96
|
+
const padto = total.toFixed(0).length;
|
|
97
|
+
|
|
98
|
+
const nc = Number.parseInt((await getNpmConfigValue('network-concurrency')) || '4');
|
|
99
|
+
|
|
100
|
+
for await (const [packName, newVersion, currentVersion] of asyncPool(
|
|
101
|
+
nc,
|
|
102
|
+
[...versions.entries()],
|
|
103
|
+
resolveNpmVersion
|
|
104
|
+
)) {
|
|
105
|
+
versions.set(packName, newVersion);
|
|
106
|
+
|
|
107
|
+
let updated = '';
|
|
108
|
+
if (currentVersion && currentVersion !== newVersion) {
|
|
109
|
+
updated = ` ${cs}(from ${currentVersion})${ce}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(` - [${i.toFixed().padStart(padto, ' ')}/${total}] ${packName}: ${newVersion}${updated}`);
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return versions;
|
|
117
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { IPackageJson } from '@idlebox/common';
|
|
2
|
+
import { loadJsonFile, writeJsonFileBack } from '@idlebox/json-edit';
|
|
3
|
+
import { logger } from '@idlebox/logger';
|
|
4
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import { inc } from 'semver';
|
|
7
|
+
|
|
8
|
+
function sort(object: any): any {
|
|
9
|
+
if (typeof object !== 'object') {
|
|
10
|
+
// Not to sort the array
|
|
11
|
+
return object;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(object)) {
|
|
14
|
+
return object.sort();
|
|
15
|
+
}
|
|
16
|
+
const keys = Object.keys(object);
|
|
17
|
+
keys.sort();
|
|
18
|
+
const newObject: any = {};
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
newObject[key] = sort(object[key]);
|
|
21
|
+
}
|
|
22
|
+
return newObject;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function makePackageJsonOrderConsistence(root: string) {
|
|
26
|
+
const filepath = resolve(root, 'package.json');
|
|
27
|
+
const data = JSON.parse(await readFile(filepath, 'utf-8'));
|
|
28
|
+
const json = sort(data);
|
|
29
|
+
await writeFile(filepath, JSON.stringify(json, null, 2), 'utf-8');
|
|
30
|
+
|
|
31
|
+
delete pkgCache[filepath];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const pkgCache: Record<string, IPackageJson> = {};
|
|
35
|
+
export async function cachedPackageJson(path: string) {
|
|
36
|
+
const exists = pkgCache[path];
|
|
37
|
+
if (exists) {
|
|
38
|
+
return exists;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = await loadJsonFile(path);
|
|
42
|
+
pkgCache[path] = data;
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function increaseVersion(pkg: IPackageJson, current: string) {
|
|
47
|
+
const v = inc(current, 'patch');
|
|
48
|
+
if (!v) {
|
|
49
|
+
throw new Error(`无法为"${pkg.name}"当前版本"${current}"增加版本号`);
|
|
50
|
+
}
|
|
51
|
+
pkg.version = v;
|
|
52
|
+
logger.log('新版本: %s', pkg.version);
|
|
53
|
+
const ch = await writeJsonFileBack(pkg);
|
|
54
|
+
logger.debug('package.json回写: %s', ch);
|
|
55
|
+
return v;
|
|
56
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createWorkspace, type MonorepoWorkspace } from '@build-script/monorepo-lib';
|
|
2
|
+
import { commandInPath, PathEnvironment } from '@idlebox/node';
|
|
3
|
+
import type { PackageManager, PackageManagerUsageKind } from './driver.abstract.js';
|
|
4
|
+
import { NPM } from './driver.npm.js';
|
|
5
|
+
import { PNPM } from './driver.pnpm.js';
|
|
6
|
+
import { reconfigureProxyWithNpmrc } from './proxy.js';
|
|
7
|
+
|
|
8
|
+
export type IPackageManager = PackageManager;
|
|
9
|
+
|
|
10
|
+
type PackageManagerConstructor = new (
|
|
11
|
+
usage: PackageManagerUsageKind,
|
|
12
|
+
workspace: MonorepoWorkspace,
|
|
13
|
+
subdir?: string,
|
|
14
|
+
) => IPackageManager;
|
|
15
|
+
|
|
16
|
+
let pmCls: PackageManagerConstructor | undefined;
|
|
17
|
+
|
|
18
|
+
export async function createPackageManager(
|
|
19
|
+
usage: PackageManagerUsageKind,
|
|
20
|
+
workspace?: MonorepoWorkspace,
|
|
21
|
+
subdir?: string,
|
|
22
|
+
): Promise<IPackageManager> {
|
|
23
|
+
if (!workspace) workspace = await createWorkspace();
|
|
24
|
+
|
|
25
|
+
if (pmCls) {
|
|
26
|
+
return new pmCls(usage, workspace, subdir);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const supports: Record<string, PackageManagerConstructor> = { pnpm: PNPM, npm: NPM };
|
|
30
|
+
for (const [name, Cls] of Object.entries(supports)) {
|
|
31
|
+
if (!(await commandInPath(name).catch(() => false))) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pm = new Cls(usage, workspace, subdir);
|
|
36
|
+
|
|
37
|
+
await reconfigureProxyWithNpmrc(pm);
|
|
38
|
+
|
|
39
|
+
pmCls = Cls;
|
|
40
|
+
return pm;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const pathVar = new PathEnvironment();
|
|
44
|
+
console.log(`当前PATH: (${process.env.PATH})\n - ${[...pathVar.values()].join('\n - ')}`);
|
|
45
|
+
throw new Error(`未检测到任何包管理器,请在PATH中安装${Object.keys(supports).join('、')}`);
|
|
46
|
+
}
|