auklet 0.0.29 → 0.1.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.
@@ -0,0 +1,206 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { isArray, isPlainObject, isString } from 'aidly';
4
+ import { createAukletLogger } from '#auklet/logger';
5
+ import { resolvePublishPlan } from '#auklet/publish/targetResolver';
6
+ export async function runInspectPackCli(args) {
7
+ const options = resolveInspectPackOptions(args);
8
+ const logger = createAukletLogger({ scope: 'inspect' });
9
+ const plan = await resolvePublishPlan({
10
+ cwd: options.cwd,
11
+ filters: options.filters,
12
+ dryRun: true,
13
+ version: undefined,
14
+ }, logger);
15
+ const checks = inspectPackageFiles(plan.targets);
16
+ new PackInspectReporter(options.cwd, plan.targets, checks).report();
17
+ return checks.some((check) => check.status === 'missing') ? 1 : 0;
18
+ }
19
+ const resolveInspectPackOptions = (args) => {
20
+ const filters = [];
21
+ const cliArgs = args.filter((arg) => arg !== '--');
22
+ for (let index = 0; index < cliArgs.length; index += 1) {
23
+ const arg = cliArgs[index];
24
+ if (arg === '--filter') {
25
+ const value = cliArgs[index + 1];
26
+ if (!value)
27
+ throw new Error('[inspect] --filter requires a value.');
28
+ filters.push(value);
29
+ index += 1;
30
+ continue;
31
+ }
32
+ if (arg.startsWith('--filter=')) {
33
+ filters.push(arg.slice('--filter='.length));
34
+ continue;
35
+ }
36
+ throw new Error(`[inspect] unknown inspect pack argument: ${arg}`);
37
+ }
38
+ return {
39
+ cwd: process.cwd(),
40
+ filters,
41
+ };
42
+ };
43
+ export function inspectPackageFiles(targets) {
44
+ return targets.flatMap((target) => inspectTargetPackage(target));
45
+ }
46
+ const inspectTargetPackage = (target) => {
47
+ return collectPackageFileReferences(target.packageJson).map((reference) => ({
48
+ target,
49
+ field: reference.field,
50
+ file: reference.file,
51
+ status: getFileStatus(target.packageRoot, reference.file),
52
+ }));
53
+ };
54
+ const collectPackageFileReferences = (packageJson) => {
55
+ const references = [];
56
+ addStringField(references, packageJson, 'main');
57
+ addStringField(references, packageJson, 'module');
58
+ addStringField(references, packageJson, 'types');
59
+ addStringField(references, packageJson, 'typings');
60
+ addStringField(references, packageJson, 'style');
61
+ addStringField(references, packageJson, 'stylesheet');
62
+ addBinFields(references, packageJson.bin);
63
+ addExportFields(references, packageJson.exports, 'exports');
64
+ addFilesFields(references, packageJson.files);
65
+ return references;
66
+ };
67
+ const addStringField = (references, packageJson, field) => {
68
+ const value = Reflect.get(packageJson, field);
69
+ if (isString(value))
70
+ references.push({ field, file: value });
71
+ };
72
+ const addBinFields = (references, value) => {
73
+ if (isString(value)) {
74
+ references.push({ field: 'bin', file: value });
75
+ return;
76
+ }
77
+ if (!isPlainObject(value))
78
+ return;
79
+ for (const [name, file] of Object.entries(value)) {
80
+ if (isString(file))
81
+ references.push({ field: `bin.${name}`, file });
82
+ }
83
+ };
84
+ const addExportFields = (references, value, field) => {
85
+ if (isString(value)) {
86
+ references.push({ field, file: value });
87
+ return;
88
+ }
89
+ if (isArray(value)) {
90
+ value.forEach((item, index) => {
91
+ addExportFields(references, item, `${field}[${index}]`);
92
+ });
93
+ return;
94
+ }
95
+ if (!isPlainObject(value))
96
+ return;
97
+ for (const [key, item] of Object.entries(value)) {
98
+ addExportFields(references, item, formatFieldKey(field, key));
99
+ }
100
+ };
101
+ const addFilesFields = (references, value) => {
102
+ if (!isArray(value))
103
+ return;
104
+ value.forEach((file, index) => {
105
+ if (isString(file))
106
+ references.push({ field: `files[${index}]`, file });
107
+ });
108
+ };
109
+ const getFileStatus = (packageRoot, file) => {
110
+ if (file.startsWith('!'))
111
+ return 'skipped';
112
+ if (file.startsWith('#') || file.includes('://'))
113
+ return 'skipped';
114
+ if (file.includes('*'))
115
+ return 'pattern';
116
+ const absolutePath = path.resolve(packageRoot, file);
117
+ const relativePath = path.relative(packageRoot, absolutePath);
118
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
119
+ return 'skipped';
120
+ }
121
+ return fs.existsSync(absolutePath) ? 'exists' : 'missing';
122
+ };
123
+ const formatFieldKey = (field, key) => {
124
+ return /^[a-zA-Z_$][\w$-]*$/.test(key)
125
+ ? `${field}.${key}`
126
+ : `${field}[${JSON.stringify(key)}]`;
127
+ };
128
+ export class PackInspectReporter {
129
+ cwd;
130
+ targets;
131
+ checks;
132
+ title;
133
+ logger = createAukletLogger();
134
+ constructor(cwd, targets, checks, title = 'Pack inspect') {
135
+ this.cwd = cwd;
136
+ this.targets = targets;
137
+ this.checks = checks;
138
+ this.title = title;
139
+ }
140
+ report() {
141
+ const missingChecks = this.checks.filter((check) => check.status === 'missing');
142
+ this.logger.newline();
143
+ this.logger.result({
144
+ title: this.formatResultTitle(this.title, missingChecks.length > 0),
145
+ status: missingChecks.length ? 'error' : 'info',
146
+ body: ['Read-only package file checks. No files or packages changed.'],
147
+ details: {
148
+ packages: this.formatValue(this.targets.length),
149
+ checks: this.formatValue(this.checks.length),
150
+ missing: missingChecks.length
151
+ ? this.logger.colors.bold(this.logger.colors.red(missingChecks.length))
152
+ : this.formatValue(0),
153
+ },
154
+ });
155
+ this.logger.newline();
156
+ this.writeSectionTitle('Package files');
157
+ this.logger.rows({
158
+ columns: ['package', 'field', 'file', 'status'],
159
+ rows: this.checks.map((check) => [
160
+ this.logger.package(check.target.packageName),
161
+ this.logger.colors.gray(check.field),
162
+ this.logger.path(check.file),
163
+ this.formatStatus(check.status),
164
+ ]),
165
+ empty: this.logger.colors.gray('No package file references found.'),
166
+ });
167
+ this.logger.newline();
168
+ this.writeSectionTitle('Package roots');
169
+ this.logger.rows({
170
+ columns: ['package', 'root'],
171
+ rows: this.targets.map((target) => [
172
+ this.logger.package(target.packageName),
173
+ this.logger.path(path.relative(this.cwd, target.packageRoot) || '.'),
174
+ ]),
175
+ });
176
+ if (missingChecks.length) {
177
+ this.logger.newline();
178
+ this.writeSectionTitle('Missing files', 'error');
179
+ for (const [index, check] of missingChecks.entries()) {
180
+ if (index > 0)
181
+ this.logger.newline();
182
+ this.logger.item(this.logger.package(check.target.packageName), ': ', this.logger.colors.red(check.field), ' -> ', this.logger.colors.red(check.file));
183
+ }
184
+ }
185
+ this.logger.newline();
186
+ }
187
+ writeSectionTitle(title, status = 'normal') {
188
+ const content = status === 'error' ? this.logger.colors.red(title) : title;
189
+ this.logger.raw(content);
190
+ }
191
+ formatStatus(status) {
192
+ if (status === 'exists')
193
+ return this.logger.colors.green('exists');
194
+ if (status === 'missing')
195
+ return this.logger.colors.red('missing');
196
+ if (status === 'pattern')
197
+ return this.logger.colors.yellow('pattern');
198
+ return this.logger.colors.gray('skipped');
199
+ }
200
+ formatValue(value) {
201
+ return this.logger.colors.bold(this.logger.colors.cyan(String(value)));
202
+ }
203
+ formatResultTitle(title, failed) {
204
+ return this.logger.colors.bold(failed ? this.logger.colors.red(title) : this.logger.colors.green(title));
205
+ }
206
+ }
@@ -0,0 +1,23 @@
1
+ import type { PublishPlan } from '#auklet/publish/types';
2
+ export type PublishRegistryCheckStatus = 'success' | 'error';
3
+ export type PublishRegistryCheck = {
4
+ packageName: string;
5
+ registry: string;
6
+ auth: PublishRegistryCheckStatus;
7
+ version: PublishRegistryCheckStatus;
8
+ reason: string | null;
9
+ };
10
+ export type PublishRegistryRetryInfo = {
11
+ packageName: string;
12
+ registry: string;
13
+ check: 'auth' | 'version';
14
+ attempt: number;
15
+ maxAttempts: number;
16
+ error: Error;
17
+ };
18
+ export type PublishRegistryCheckInfo = Pick<PublishRegistryRetryInfo, 'packageName' | 'registry' | 'check'>;
19
+ export type InspectPublishRegistryOptions = {
20
+ onCheck?: (info: PublishRegistryCheckInfo) => void;
21
+ onRetry?: (info: PublishRegistryRetryInfo) => void;
22
+ };
23
+ export declare function inspectPublishRegistry(plan: PublishPlan, options?: InspectPublishRegistryOptions): Promise<PublishRegistryCheck[]>;
@@ -0,0 +1,107 @@
1
+ import { retry } from 'aidly';
2
+ import { getPublishRegistry } from '#auklet/publish/api/registry';
3
+ import { hasPublishedPackageVersion, runPnpmWhoami, } from '#auklet/publish/api/pnpmApi';
4
+ const registryCheckTimeout = 5_000;
5
+ const registryCheckRetryTimes = 2;
6
+ export async function inspectPublishRegistry(plan, options = {}) {
7
+ const authResults = new Map();
8
+ const checks = [];
9
+ for (const target of plan.targets) {
10
+ const registry = getPublishRegistry(target.packageJson.publishConfig);
11
+ const registryLabel = registry ?? 'default';
12
+ const authKey = `${target.packageRoot}\n${registryLabel}`;
13
+ if (!authResults.has(authKey)) {
14
+ await checkAuth(authResults, authKey, target.packageRoot, target.packageName, registry, options);
15
+ }
16
+ const authError = authResults.get(authKey) ?? null;
17
+ const versionError = await checkVersion(target.packageRoot, target.packageName, target.publishVersion, registry, options);
18
+ checks.push({
19
+ packageName: target.packageName,
20
+ registry: registryLabel,
21
+ auth: authError ? 'error' : 'success',
22
+ version: versionError ? 'error' : 'success',
23
+ reason: getReason(authError, versionError),
24
+ });
25
+ }
26
+ return checks;
27
+ }
28
+ const checkAuth = async (authResults, authKey, packageRoot, packageName, registry, options) => {
29
+ try {
30
+ options.onCheck?.({
31
+ packageName,
32
+ registry: registry ?? 'default',
33
+ check: 'auth',
34
+ });
35
+ await retryWithLog(() => runPnpmWhoami(packageRoot, {
36
+ packageName,
37
+ registry,
38
+ timeout: registryCheckTimeout,
39
+ }), {
40
+ check: 'auth',
41
+ packageName,
42
+ registry,
43
+ onRetry: options.onRetry,
44
+ });
45
+ authResults.set(authKey, null);
46
+ return null;
47
+ }
48
+ catch (error) {
49
+ const authError = toError(error);
50
+ authResults.set(authKey, authError);
51
+ return authError;
52
+ }
53
+ };
54
+ const checkVersion = async (packageRoot, packageName, version, registry, options) => {
55
+ try {
56
+ options.onCheck?.({
57
+ packageName,
58
+ registry: registry ?? 'default',
59
+ check: 'version',
60
+ });
61
+ const exists = await retryWithLog(() => hasPublishedPackageVersion(packageRoot, packageName, version, {
62
+ registry,
63
+ timeout: registryCheckTimeout,
64
+ }), {
65
+ check: 'version',
66
+ packageName,
67
+ registry,
68
+ onRetry: options.onRetry,
69
+ });
70
+ return exists
71
+ ? new Error(`version already exists: ${packageName}@${version}`)
72
+ : null;
73
+ }
74
+ catch (error) {
75
+ return toError(error);
76
+ }
77
+ };
78
+ const getReason = (authError, versionError) => {
79
+ return authError?.message ?? versionError?.message ?? null;
80
+ };
81
+ const toError = (error) => {
82
+ return error instanceof Error ? error : new Error(String(error));
83
+ };
84
+ const retryWithLog = (fn, options) => {
85
+ let attempt = 0;
86
+ let previousError = null;
87
+ return retry(async () => {
88
+ attempt += 1;
89
+ if (attempt > 1) {
90
+ options.onRetry?.({
91
+ packageName: options.packageName,
92
+ registry: options.registry ?? 'default',
93
+ check: options.check,
94
+ attempt: attempt - 1,
95
+ maxAttempts: registryCheckRetryTimes,
96
+ error: previousError ?? new Error('publish registry check failed.'),
97
+ });
98
+ }
99
+ try {
100
+ return await fn();
101
+ }
102
+ catch (error) {
103
+ previousError = toError(error);
104
+ throw error;
105
+ }
106
+ }, registryCheckRetryTimes);
107
+ };
@@ -1,6 +1,6 @@
1
- import { isPlainObject, isString } from 'aidly';
2
1
  import { PnpmPublishApi } from '#auklet/publish/api/pnpmPublishApi';
3
2
  import { hasPublishedPackageVersion, NpmPackageVersionExistsError, runPnpmWhoami, } from '#auklet/publish/api/pnpmApi';
3
+ import { getPublishRegistry } from '#auklet/publish/api/registry';
4
4
  import { logAuthenticationError } from '#auklet/publish/runner/packagePublisher';
5
5
  import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
6
6
  export class PublishPreflight {
@@ -66,9 +66,3 @@ export class PublishPreflight {
66
66
  }
67
67
  }
68
68
  }
69
- const getPublishRegistry = (publishConfig) => {
70
- if (!isPlainObject(publishConfig))
71
- return undefined;
72
- const registry = Reflect.get(publishConfig, 'registry');
73
- return isString(registry) && registry.length > 0 ? registry : undefined;
74
- };
@@ -18,6 +18,10 @@ export class ReleaseGitController {
18
18
  }
19
19
  async commitAndTag(plan) {
20
20
  const git = await isGitRepository(plan.root);
21
+ if (git && this.options.git === false) {
22
+ this.warnOnce('--no-git enabled, skipping git commit and tag.');
23
+ return;
24
+ }
21
25
  if (git && this.options.allowDirty) {
22
26
  this.warnOnce('--allow-dirty enabled, skipping git commit and tag.');
23
27
  return;
@@ -43,13 +43,14 @@ export type WorkspacePackage = {
43
43
  };
44
44
  export type PublishOptions = {
45
45
  cwd: string;
46
- filters: Array<string>;
47
- version?: string;
48
46
  dryRun: boolean;
49
47
  format: boolean;
50
- otp?: string;
51
- ignoreScripts: boolean;
52
48
  allowDirty: boolean;
49
+ ignoreScripts: boolean;
50
+ filters: Array<string>;
51
+ git?: boolean;
52
+ otp?: string;
53
+ version?: string;
53
54
  };
54
55
  export type OwnerOptions = {
55
56
  cwd: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auklet",
3
- "version": "0.0.29",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "author": "chentao.arthur",
6
6
  "description": "Build utilities for TypeScript packages and module CSS output.",
@@ -99,7 +99,7 @@
99
99
  "dev:registry-login": "npm adduser --registry http://127.0.0.1:4873 --auth-type=legacy",
100
100
  "dev:examples": "pnpm --filter './examples/*' --if-present dev",
101
101
  "build:examples": "pnpm --filter './examples/*' build",
102
- "format:md": "prettier --write \"*.md\" \"examples/**/*.md\"",
103
- "format": "prettier --write \"package.json\" \"pnpm-workspace.yaml\" \"(bin|src|dist|examples)/**/*.{js,mjs,cjs,ts,tsx,json,md,yaml,css}\""
102
+ "format:md": "prettier --write \"*.md\" \"docs/**/*.md\" \"examples/**/*.md\"",
103
+ "format": "prettier --write \"package.json\" \"pnpm-workspace.yaml\" \"*.md\" \"docs/**/*.md\" \"(bin|src|dist|examples)/**/*.{js,mjs,cjs,ts,tsx,json,md,yaml,css}\""
104
104
  }
105
105
  }