@git.zone/cli 2.19.4 → 2.19.6
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/.smartconfig.json +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.gitzoneconfig.js +1 -1
- package/dist_ts/gitzone.cli.js +2 -2
- package/dist_ts/helpers.changelog.js +14 -6
- package/dist_ts/mod_commit/mod.helpers.js +5 -3
- package/dist_ts/mod_format/classes.diffreporter.js +3 -2
- package/dist_ts/mod_format/formatters/copy.formatter.js +3 -2
- package/dist_ts/mod_format/formatters/packagejson.formatter.js +3 -2
- package/dist_ts/mod_format/formatters/templates.formatter.js +3 -2
- package/dist_ts/mod_format/formatters/tsconfig.formatter.js +3 -2
- package/dist_ts/mod_meta/meta.classes.meta.js +1 -1
- package/dist_ts/mod_open/index.d.ts +1 -1
- package/dist_ts/mod_open/index.js +3 -3
- package/dist_ts/mod_services/classes.dockercontainer.js +5 -3
- package/dist_ts/mod_services/classes.serviceconfiguration.js +1 -1
- package/dist_ts/mod_services/classes.servicemanager.js +10 -7
- package/dist_ts/mod_template/index.d.ts +1 -1
- package/dist_ts/mod_template/index.js +6 -2
- package/dist_ts/mod_tools/classes.packagemanager.d.ts +4 -0
- package/dist_ts/mod_tools/classes.packagemanager.js +183 -5
- package/dist_ts/mod_tools/index.js +23 -14
- package/package.json +11 -11
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.gitzoneconfig.ts +1 -1
- package/ts/gitzone.cli.ts +1 -1
- package/ts/helpers.changelog.ts +22 -8
- package/ts/mod_commit/mod.helpers.ts +4 -2
- package/ts/mod_format/classes.diffreporter.ts +2 -1
- package/ts/mod_format/formatters/copy.formatter.ts +2 -1
- package/ts/mod_format/formatters/packagejson.formatter.ts +2 -1
- package/ts/mod_format/formatters/templates.formatter.ts +2 -1
- package/ts/mod_format/formatters/tsconfig.formatter.ts +2 -1
- package/ts/mod_meta/meta.classes.meta.ts +1 -1
- package/ts/mod_open/index.ts +2 -2
- package/ts/mod_services/classes.dockercontainer.ts +5 -3
- package/ts/mod_services/classes.serviceconfiguration.ts +2 -2
- package/ts/mod_services/classes.servicemanager.ts +9 -6
- package/ts/mod_template/index.ts +5 -1
- package/ts/mod_tools/classes.packagemanager.ts +251 -4
- package/ts/mod_tools/index.ts +31 -13
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@git.zone/cli',
|
|
6
|
-
version: '2.19.
|
|
6
|
+
version: '2.19.6',
|
|
7
7
|
description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
|
|
8
8
|
}
|
package/ts/gitzone.cli.ts
CHANGED
|
@@ -105,7 +105,7 @@ export let run = async () => {
|
|
|
105
105
|
const rawCliMode = await getRawCliMode();
|
|
106
106
|
|
|
107
107
|
// get packageInfo
|
|
108
|
-
const projectInfo =
|
|
108
|
+
const projectInfo = await plugins.projectinfo.ProjectInfo.create(paths.packageDir);
|
|
109
109
|
const projectInfoVersion = (projectInfo.npm as any)?.version;
|
|
110
110
|
const packageVersion =
|
|
111
111
|
typeof projectInfoVersion === "string" && projectInfoVersion.length > 0
|
package/ts/helpers.changelog.ts
CHANGED
|
@@ -19,6 +19,12 @@ export interface IPendingChangelog {
|
|
|
19
19
|
isEmpty: boolean;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
interface IChangelogSection {
|
|
23
|
+
start: number;
|
|
24
|
+
bodyStart: number;
|
|
25
|
+
end: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
const bucketForCommitType = (commitType: string): TChangelogBucket => {
|
|
23
29
|
switch (commitType) {
|
|
24
30
|
case "BREAKING CHANGE":
|
|
@@ -48,7 +54,7 @@ const writeChangelog = async (filePath: string, content: string): Promise<void>
|
|
|
48
54
|
const findPendingSection = (
|
|
49
55
|
content: string,
|
|
50
56
|
sectionName: string,
|
|
51
|
-
):
|
|
57
|
+
): IChangelogSection | null => {
|
|
52
58
|
const headingRegex = new RegExp(`^##\\s+${sectionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m");
|
|
53
59
|
const match = headingRegex.exec(content);
|
|
54
60
|
if (!match || match.index === undefined) {
|
|
@@ -123,9 +129,11 @@ export const readPendingChangelog = async (
|
|
|
123
129
|
filePath: string,
|
|
124
130
|
sectionName = "Pending",
|
|
125
131
|
): Promise<IPendingChangelog> => {
|
|
126
|
-
const content = await
|
|
127
|
-
const pendingSection = findPendingSection(content, sectionName)
|
|
128
|
-
const block =
|
|
132
|
+
const content = await readChangelog(filePath);
|
|
133
|
+
const pendingSection = findPendingSection(content, sectionName);
|
|
134
|
+
const block = pendingSection
|
|
135
|
+
? content.slice(pendingSection.bodyStart, pendingSection.end).trim()
|
|
136
|
+
: "";
|
|
129
137
|
return {
|
|
130
138
|
block,
|
|
131
139
|
isEmpty: block.length === 0,
|
|
@@ -149,8 +157,11 @@ export const movePendingToVersion = async (
|
|
|
149
157
|
version: string,
|
|
150
158
|
dateString: string,
|
|
151
159
|
): Promise<void> => {
|
|
152
|
-
let content = await
|
|
153
|
-
const pendingSection = findPendingSection(content, sectionName)
|
|
160
|
+
let content = await readChangelog(filePath);
|
|
161
|
+
const pendingSection = findPendingSection(content, sectionName);
|
|
162
|
+
if (!pendingSection) {
|
|
163
|
+
throw new Error("No pending changelog entries. Nothing to release.");
|
|
164
|
+
}
|
|
154
165
|
const pendingBlock = content.slice(pendingSection.bodyStart, pendingSection.end).trim();
|
|
155
166
|
if (!pendingBlock) {
|
|
156
167
|
throw new Error("No pending changelog entries. Nothing to release.");
|
|
@@ -159,7 +170,10 @@ export const movePendingToVersion = async (
|
|
|
159
170
|
const renderedHeading = versionHeading
|
|
160
171
|
.replaceAll("{{version}}", version)
|
|
161
172
|
.replaceAll("{{date}}", dateString);
|
|
162
|
-
const
|
|
163
|
-
|
|
173
|
+
const beforePending = content.slice(0, pendingSection.start).trimEnd();
|
|
174
|
+
const afterPending = content.slice(pendingSection.end).replace(/^\n+/, "").trimEnd();
|
|
175
|
+
content = [beforePending, renderedHeading, pendingBlock, afterPending]
|
|
176
|
+
.filter((block) => block.length > 0)
|
|
177
|
+
.join("\n\n");
|
|
164
178
|
await writeChangelog(filePath, content);
|
|
165
179
|
};
|
|
@@ -27,7 +27,8 @@ export async function detectCurrentBranch(): Promise<string> {
|
|
|
27
27
|
logger.log('info', `Detected current branch: ${branchName}`);
|
|
28
28
|
return branchName;
|
|
29
29
|
} catch (error) {
|
|
30
|
-
|
|
30
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
31
|
+
logger.log('warn', `Failed to detect branch: ${errorMessage}, falling back to "master"`);
|
|
31
32
|
return 'master';
|
|
32
33
|
}
|
|
33
34
|
}
|
|
@@ -225,6 +226,7 @@ export async function bumpProjectVersion(
|
|
|
225
226
|
|
|
226
227
|
return newVersion;
|
|
227
228
|
} catch (error) {
|
|
228
|
-
|
|
229
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
230
|
+
throw new Error(`Failed to bump project version: ${errorMessage}`);
|
|
229
231
|
}
|
|
230
232
|
}
|
|
@@ -42,9 +42,10 @@ export class DiffReporter {
|
|
|
42
42
|
change.content,
|
|
43
43
|
);
|
|
44
44
|
} catch (error) {
|
|
45
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
45
46
|
logger.log(
|
|
46
47
|
'error',
|
|
47
|
-
`Failed to generate diff for ${change.path}: ${
|
|
48
|
+
`Failed to generate diff for ${change.path}: ${errorMessage}`,
|
|
48
49
|
);
|
|
49
50
|
return null;
|
|
50
51
|
}
|
|
@@ -93,7 +93,8 @@ export class CopyFormatter extends BaseFormatter {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
} catch (error) {
|
|
96
|
-
|
|
96
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
97
|
+
logVerbose(`Failed to process pattern ${pattern.from}: ${errorMessage}`);
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -94,7 +94,8 @@ export class PackageJsonFormatter extends BaseFormatter {
|
|
|
94
94
|
packageJson.pnpm = packageJson.pnpm || {};
|
|
95
95
|
packageJson.pnpm.overrides = overrides;
|
|
96
96
|
} catch (error) {
|
|
97
|
-
|
|
97
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
98
|
+
logVerbose(`Could not read overrides.json: ${errorMessage}`);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
const newContent = JSON.stringify(packageJson, null, 2);
|
|
@@ -117,7 +117,8 @@ export class TemplatesFormatter extends BaseFormatter {
|
|
|
117
117
|
try {
|
|
118
118
|
renderedFiles = await this.renderTemplate(templateName);
|
|
119
119
|
} catch (error) {
|
|
120
|
-
|
|
120
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
121
|
+
logVerbose(`Failed to render template ${templateName}: ${errorMessage}`);
|
|
121
122
|
return changes;
|
|
122
123
|
}
|
|
123
124
|
|
|
@@ -46,7 +46,8 @@ export class TsconfigFormatter extends BaseFormatter {
|
|
|
46
46
|
];
|
|
47
47
|
}
|
|
48
48
|
} catch (error) {
|
|
49
|
-
|
|
49
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
50
|
+
logVerbose(`Could not get tspublish modules: ${errorMessage}`);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
tsconfigObject.compilerOptions.paths = { ...existingPaths, ...tspublishPaths };
|
package/ts/mod_open/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as plugins from './mod.plugins.js';
|
|
2
2
|
import * as paths from '../paths.js';
|
|
3
3
|
|
|
4
|
-
export let run = (argvArg) => {
|
|
5
|
-
let projectInfo =
|
|
4
|
+
export let run = async (argvArg) => {
|
|
5
|
+
let projectInfo = await plugins.projectinfo.ProjectInfo.create(paths.cwd);
|
|
6
6
|
if (argvArg._[1] === 'ci') {
|
|
7
7
|
plugins.smartopen.openUrl(
|
|
8
8
|
`https://gitlab.com/${projectInfo.git.gituser}/${projectInfo.git.gitrepo}/settings/ci_cd`,
|
|
@@ -148,7 +148,8 @@ export class DockerContainer {
|
|
|
148
148
|
const result = await this.smartshell.exec(command);
|
|
149
149
|
return result.exitCode === 0;
|
|
150
150
|
} catch (error) {
|
|
151
|
-
|
|
151
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
152
|
+
logger.log('error', `Failed to run container: ${errorMessage}`);
|
|
152
153
|
return false;
|
|
153
154
|
}
|
|
154
155
|
}
|
|
@@ -177,7 +178,8 @@ export class DockerContainer {
|
|
|
177
178
|
const result = await this.smartshell.exec(`docker logs ${tailFlag} ${containerName}`);
|
|
178
179
|
return result.stdout;
|
|
179
180
|
} catch (error) {
|
|
180
|
-
|
|
181
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
182
|
+
return `Error getting logs: ${errorMessage}`;
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
185
|
|
|
@@ -258,4 +260,4 @@ export class DockerContainer {
|
|
|
258
260
|
return null;
|
|
259
261
|
}
|
|
260
262
|
}
|
|
261
|
-
}
|
|
263
|
+
}
|
|
@@ -28,7 +28,7 @@ export interface IServiceConfig {
|
|
|
28
28
|
|
|
29
29
|
export class ServiceConfiguration {
|
|
30
30
|
private configPath: string;
|
|
31
|
-
private config
|
|
31
|
+
private config!: IServiceConfig;
|
|
32
32
|
private docker: DockerContainer;
|
|
33
33
|
|
|
34
34
|
constructor() {
|
|
@@ -515,4 +515,4 @@ export class ServiceConfiguration {
|
|
|
515
515
|
logger.log('info', ` 📍 S3 Console: ${s3ConsolePort}`);
|
|
516
516
|
logger.log('info', ` 📍 Elasticsearch: ${esPort}`);
|
|
517
517
|
}
|
|
518
|
-
}
|
|
518
|
+
}
|
|
@@ -61,13 +61,15 @@ export class ServiceManager {
|
|
|
61
61
|
default: ['mongodb', 'minio', 'elasticsearch']
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
const enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
|
65
|
+
this.enabledServices = enabledServices;
|
|
65
66
|
|
|
66
67
|
// Save to .smartconfig.json
|
|
67
|
-
await this.saveServiceConfiguration(
|
|
68
|
+
await this.saveServiceConfiguration(enabledServices);
|
|
68
69
|
} else {
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
const enabledServices = gitzoneConfig.services as string[];
|
|
71
|
+
this.enabledServices = enabledServices;
|
|
72
|
+
logger.log('info', `🔧 Enabled services: ${enabledServices.join(', ')}`);
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -902,10 +904,11 @@ export class ServiceManager {
|
|
|
902
904
|
default: currentServices
|
|
903
905
|
});
|
|
904
906
|
|
|
905
|
-
|
|
907
|
+
const enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
|
908
|
+
this.enabledServices = enabledServices;
|
|
906
909
|
|
|
907
910
|
// Save to .smartconfig.json
|
|
908
|
-
await this.saveServiceConfiguration(
|
|
911
|
+
await this.saveServiceConfiguration(enabledServices);
|
|
909
912
|
|
|
910
913
|
logger.log('ok', '✅ Service configuration updated');
|
|
911
914
|
}
|
package/ts/mod_template/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ export const isTemplate = async (templateNameArg: string) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export const getTemplate = async (templateNameArg: string) => {
|
|
18
|
-
if (isTemplate(templateNameArg)) {
|
|
18
|
+
if (await isTemplate(templateNameArg)) {
|
|
19
19
|
const localScafTemplate = new plugins.smartscaf.ScafTemplate(
|
|
20
20
|
getTemplatePath(templateNameArg),
|
|
21
21
|
);
|
|
@@ -50,6 +50,10 @@ export const run = async (argvArg: any) => {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const localScafTemplate = await getTemplate(chosenTemplate);
|
|
53
|
+
if (!localScafTemplate) {
|
|
54
|
+
logger.log('error', `Template ${chosenTemplate} not available`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
53
57
|
await localScafTemplate.askCliForMissingVariables();
|
|
54
58
|
await localScafTemplate.writeToDisk(paths.cwd);
|
|
55
59
|
};
|
|
@@ -70,7 +70,7 @@ export class PackageManagerUtil {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const currentVersion = await this.getCurrentPnpmVersion();
|
|
73
|
-
const latestVersion = await this.
|
|
73
|
+
const latestVersion = await this.getLatestMatureVersion("pnpm", [
|
|
74
74
|
"https://registry.npmjs.org",
|
|
75
75
|
]);
|
|
76
76
|
|
|
@@ -371,6 +371,51 @@ export class PackageManagerUtil {
|
|
|
371
371
|
return null;
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
+
public async getLatestMatureVersion(
|
|
375
|
+
packageName: string,
|
|
376
|
+
registries = [
|
|
377
|
+
"https://verdaccio.lossless.digital",
|
|
378
|
+
"https://registry.npmjs.org",
|
|
379
|
+
],
|
|
380
|
+
): Promise<string | null> {
|
|
381
|
+
const minimumReleaseAgeMinutes = await this.getMinimumReleaseAgeMinutes();
|
|
382
|
+
|
|
383
|
+
for (const registry of registries) {
|
|
384
|
+
const metadata = await this.getPackageMetadataFromRegistry(
|
|
385
|
+
registry,
|
|
386
|
+
packageName,
|
|
387
|
+
);
|
|
388
|
+
if (!metadata) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const latest = getLatestVersionFromMetadata(metadata);
|
|
393
|
+
if (!latest && !minimumReleaseAgeMinutes) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (!minimumReleaseAgeMinutes) {
|
|
397
|
+
return latest;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const minimumReleaseAgeExcluded = latest
|
|
401
|
+
? await this.isMinimumReleaseAgeExcluded(packageName, latest)
|
|
402
|
+
: false;
|
|
403
|
+
if (latest && minimumReleaseAgeExcluded) {
|
|
404
|
+
return latest;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const matureLatest = getLatestMatureVersionFromMetadata(
|
|
408
|
+
metadata,
|
|
409
|
+
minimumReleaseAgeMinutes,
|
|
410
|
+
);
|
|
411
|
+
if (matureLatest) {
|
|
412
|
+
return matureLatest;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
374
419
|
public async installLatest(
|
|
375
420
|
packageName: string,
|
|
376
421
|
version = "latest",
|
|
@@ -484,6 +529,42 @@ export class PackageManagerUtil {
|
|
|
484
529
|
return this.shell.execSilent(`${pnpmCommand} ${commandArgs}`);
|
|
485
530
|
}
|
|
486
531
|
|
|
532
|
+
private async getMinimumReleaseAgeMinutes(): Promise<number> {
|
|
533
|
+
try {
|
|
534
|
+
const result = await this.execPnpmSilent(
|
|
535
|
+
"config get minimum-release-age 2>/dev/null",
|
|
536
|
+
);
|
|
537
|
+
const rawValue = result?.stdout.trim() || "";
|
|
538
|
+
const value = Number.parseInt(rawValue, 10);
|
|
539
|
+
if (Number.isFinite(value) && value >= 0) {
|
|
540
|
+
return value;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const currentVersion = await this.getCurrentPnpmVersion();
|
|
544
|
+
return getDefaultMinimumReleaseAgeMinutes(currentVersion);
|
|
545
|
+
} catch {
|
|
546
|
+
const currentVersion = await this.getCurrentPnpmVersion();
|
|
547
|
+
return getDefaultMinimumReleaseAgeMinutes(currentVersion);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
private async isMinimumReleaseAgeExcluded(
|
|
552
|
+
packageName: string,
|
|
553
|
+
version: string,
|
|
554
|
+
): Promise<boolean> {
|
|
555
|
+
try {
|
|
556
|
+
const result = await this.execPnpmSilent(
|
|
557
|
+
"config get minimum-release-age-exclude 2>/dev/null",
|
|
558
|
+
);
|
|
559
|
+
const rawPatterns = parsePnpmConfigStringList(result?.stdout || "");
|
|
560
|
+
return rawPatterns.some((pattern) =>
|
|
561
|
+
packageSelectorMatches(packageName, version, pattern),
|
|
562
|
+
);
|
|
563
|
+
} catch {
|
|
564
|
+
return false;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
487
568
|
private async getPnpmListProjects(): Promise<IPnpmListProject[]> {
|
|
488
569
|
try {
|
|
489
570
|
const result = await this.execPnpmSilent(
|
|
@@ -710,6 +791,17 @@ export class PackageManagerUtil {
|
|
|
710
791
|
registry: string,
|
|
711
792
|
packageName: string,
|
|
712
793
|
): Promise<string | null> {
|
|
794
|
+
const data = await this.getPackageMetadataFromRegistry(
|
|
795
|
+
registry,
|
|
796
|
+
packageName,
|
|
797
|
+
);
|
|
798
|
+
return data ? getLatestVersionFromMetadata(data) : null;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
private async getPackageMetadataFromRegistry(
|
|
802
|
+
registry: string,
|
|
803
|
+
packageName: string,
|
|
804
|
+
): Promise<any | null> {
|
|
713
805
|
const encodedName = packageName.replace("/", "%2f");
|
|
714
806
|
const controller = new AbortController();
|
|
715
807
|
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
@@ -724,9 +816,7 @@ export class PackageManagerUtil {
|
|
|
724
816
|
if (!response.ok) {
|
|
725
817
|
return null;
|
|
726
818
|
}
|
|
727
|
-
|
|
728
|
-
const latest = (data as any)["dist-tags"]?.latest;
|
|
729
|
-
return typeof latest === "string" && latest.length > 0 ? latest : null;
|
|
819
|
+
return await response.json();
|
|
730
820
|
} catch {
|
|
731
821
|
return null;
|
|
732
822
|
} finally {
|
|
@@ -767,6 +857,163 @@ function getDependencyPackagePath(info: any): string | undefined {
|
|
|
767
857
|
: undefined;
|
|
768
858
|
}
|
|
769
859
|
|
|
860
|
+
function getLatestVersionFromMetadata(metadata: any): string | null {
|
|
861
|
+
const latest = metadata?.["dist-tags"]?.latest;
|
|
862
|
+
return typeof latest === "string" && latest.length > 0 ? latest : null;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function getDefaultMinimumReleaseAgeMinutes(currentPnpmVersion: string): number {
|
|
866
|
+
const majorVersion = normalizeSemver(currentPnpmVersion)[0] || 0;
|
|
867
|
+
return majorVersion >= 11 ? 1440 : 0;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function getLatestMatureVersionFromMetadata(
|
|
871
|
+
metadata: any,
|
|
872
|
+
minimumReleaseAgeMinutes: number,
|
|
873
|
+
): string | null {
|
|
874
|
+
const versions = metadata?.versions;
|
|
875
|
+
const versionTimes = metadata?.time;
|
|
876
|
+
if (
|
|
877
|
+
!versions ||
|
|
878
|
+
typeof versions !== "object" ||
|
|
879
|
+
!versionTimes ||
|
|
880
|
+
typeof versionTimes !== "object"
|
|
881
|
+
) {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const cutoffTimestamp = Date.now() - minimumReleaseAgeMinutes * 60 * 1000;
|
|
886
|
+
return Object.keys(versions)
|
|
887
|
+
.filter((version) => /^\d+\.\d+\.\d+$/.test(version))
|
|
888
|
+
.filter((version) => {
|
|
889
|
+
const publishedAt = Date.parse(versionTimes[version]);
|
|
890
|
+
return Number.isFinite(publishedAt) && publishedAt <= cutoffTimestamp;
|
|
891
|
+
})
|
|
892
|
+
.sort(compareSemverDescending)[0] || null;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function compareSemverDescending(versionA: string, versionB: string): number {
|
|
896
|
+
const versionAParts = normalizeSemver(versionA);
|
|
897
|
+
const versionBParts = normalizeSemver(versionB);
|
|
898
|
+
|
|
899
|
+
for (
|
|
900
|
+
let i = 0;
|
|
901
|
+
i < Math.max(versionAParts.length, versionBParts.length);
|
|
902
|
+
i++
|
|
903
|
+
) {
|
|
904
|
+
const versionAPart = versionAParts[i] || 0;
|
|
905
|
+
const versionBPart = versionBParts[i] || 0;
|
|
906
|
+
if (versionAPart !== versionBPart) {
|
|
907
|
+
return versionBPart - versionAPart;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
return 0;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function parsePnpmConfigStringList(rawValue: string): string[] {
|
|
915
|
+
const trimmedValue = rawValue.trim();
|
|
916
|
+
if (
|
|
917
|
+
!trimmedValue ||
|
|
918
|
+
trimmedValue === "undefined" ||
|
|
919
|
+
trimmedValue === "null"
|
|
920
|
+
) {
|
|
921
|
+
return [];
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
try {
|
|
925
|
+
const parsedValue = JSON.parse(trimmedValue);
|
|
926
|
+
if (Array.isArray(parsedValue)) {
|
|
927
|
+
return parsedValue
|
|
928
|
+
.filter((item) => typeof item === "string")
|
|
929
|
+
.map((item) => normalizePnpmConfigListItem(item))
|
|
930
|
+
.filter((item) => item.length > 0);
|
|
931
|
+
}
|
|
932
|
+
if (typeof parsedValue === "string") {
|
|
933
|
+
return [normalizePnpmConfigListItem(parsedValue)].filter(
|
|
934
|
+
(item) => item.length > 0,
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
} catch {
|
|
938
|
+
// pnpm commonly prints string arrays as comma-delimited text.
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
return trimmedValue
|
|
942
|
+
.split(/[\n,]/)
|
|
943
|
+
.map((item) => normalizePnpmConfigListItem(item))
|
|
944
|
+
.filter((item) => item.length > 0);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function normalizePnpmConfigListItem(rawItem: string): string {
|
|
948
|
+
return rawItem
|
|
949
|
+
.trim()
|
|
950
|
+
.replace(/^-\s*/, "")
|
|
951
|
+
.replace(/^\[/, "")
|
|
952
|
+
.replace(/\]$/, "")
|
|
953
|
+
.trim()
|
|
954
|
+
.replace(/^['"]/, "")
|
|
955
|
+
.replace(/['"]$/, "")
|
|
956
|
+
.trim();
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function packageSelectorMatches(
|
|
960
|
+
packageName: string,
|
|
961
|
+
version: string,
|
|
962
|
+
pattern: string,
|
|
963
|
+
): boolean {
|
|
964
|
+
const selectorParts = splitPackageSelector(pattern);
|
|
965
|
+
if (!globPatternMatches(packageName, selectorParts.packagePattern)) {
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
if (!selectorParts.versionSelector) {
|
|
969
|
+
return true;
|
|
970
|
+
}
|
|
971
|
+
return selectorParts.versionSelector
|
|
972
|
+
.split("||")
|
|
973
|
+
.map((versionSelector) => versionSelector.trim())
|
|
974
|
+
.some((versionSelector) => {
|
|
975
|
+
const packageVersionPrefix = `${packageName}@`;
|
|
976
|
+
const normalizedVersionSelector = versionSelector.startsWith(
|
|
977
|
+
packageVersionPrefix,
|
|
978
|
+
)
|
|
979
|
+
? versionSelector.slice(packageVersionPrefix.length)
|
|
980
|
+
: versionSelector;
|
|
981
|
+
return normalizedVersionSelector === version;
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function splitPackageSelector(pattern: string): {
|
|
986
|
+
packagePattern: string;
|
|
987
|
+
versionSelector: string | null;
|
|
988
|
+
} {
|
|
989
|
+
const versionSeparatorIndex = pattern.startsWith("@")
|
|
990
|
+
? pattern.indexOf("@", 1)
|
|
991
|
+
: pattern.indexOf("@");
|
|
992
|
+
if (versionSeparatorIndex <= 0) {
|
|
993
|
+
return {
|
|
994
|
+
packagePattern: pattern,
|
|
995
|
+
versionSelector: null,
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return {
|
|
1000
|
+
packagePattern: pattern.slice(0, versionSeparatorIndex),
|
|
1001
|
+
versionSelector: pattern.slice(versionSeparatorIndex + 1).trim() || null,
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function globPatternMatches(value: string, pattern: string): boolean {
|
|
1006
|
+
const escapedPattern = pattern
|
|
1007
|
+
.split("*")
|
|
1008
|
+
.map((patternPart) => escapeRegExp(patternPart))
|
|
1009
|
+
.join(".*");
|
|
1010
|
+
return new RegExp(`^${escapedPattern}$`).test(value);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
function escapeRegExp(value: string): string {
|
|
1014
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1015
|
+
}
|
|
1016
|
+
|
|
770
1017
|
function getPackagePath(globalDir: string, packageName: string): string {
|
|
771
1018
|
return plugins.path.join(
|
|
772
1019
|
globalDir,
|