auklet 0.0.18 → 0.0.20
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 +255 -174
- package/bin/entry.mjs +5 -0
- package/dist/build/bundleConfig.js +1 -1
- package/dist/build/cliOverrides.d.ts +22 -0
- package/dist/build/cliOverrides.js +22 -0
- package/dist/build/moduleConfig.js +1 -1
- package/dist/build/runTsdown.d.ts +2 -0
- package/dist/build/runTsdown.js +4 -3
- package/dist/build/tsdown/common.d.ts +2 -2
- package/dist/build/tsdown/context.d.ts +2 -1
- package/dist/build/tsdown/context.js +2 -1
- package/dist/build/tsdown/define.js +3 -2
- package/dist/build/tsdown/dependencies.js +3 -1
- package/dist/build/tsdown/entries.d.ts +2 -2
- package/dist/build/tsdown/entries.js +10 -10
- package/dist/build/tsdown/types.d.ts +1 -0
- package/dist/cli/build.d.ts +5 -0
- package/dist/cli/build.js +30 -0
- package/dist/cli/buildArgs.d.ts +8 -0
- package/dist/cli/buildArgs.js +114 -0
- package/dist/cli/buildCss.d.ts +12 -0
- package/dist/cli/buildCss.js +53 -0
- package/dist/cli/dev.d.ts +1 -0
- package/dist/cli/dev.js +39 -0
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +95 -0
- package/dist/cli/publish.d.ts +2 -0
- package/dist/cli/publish.js +9 -0
- package/dist/css/production/buildReporter.d.ts +3 -0
- package/dist/css/production/buildReporter.js +19 -0
- package/dist/css/production/builder.d.ts +8 -3
- package/dist/css/production/builder.js +9 -13
- package/dist/css/vite/hmr.js +7 -6
- package/dist/css/vite/moduleGraph/graph.js +0 -1
- package/dist/css/vite/moduleGraph/packageSource/monorepo.d.ts +5 -1
- package/dist/css/vite/moduleGraph/packageSource/monorepo.js +40 -31
- package/dist/css/vite/moduleGraph/types.d.ts +0 -1
- package/dist/css/watch/watcher.d.ts +0 -1
- package/dist/css/watch/watcher.js +3 -10
- package/dist/index.d.ts +5 -7
- package/dist/index.js +4 -5
- package/dist/logger.d.ts +5 -0
- package/dist/logger.js +14 -0
- package/dist/publish/api/gitApi.d.ts +7 -0
- package/dist/publish/api/gitApi.js +55 -0
- package/dist/publish/api/packageJsonApi.d.ts +7 -0
- package/dist/publish/api/packageJsonApi.js +34 -0
- package/dist/publish/api/pnpmApi.d.ts +9 -0
- package/dist/publish/api/pnpmApi.js +85 -0
- package/dist/publish/api/pnpmPublishApi.d.ts +4 -0
- package/dist/publish/api/pnpmPublishApi.js +7 -0
- package/dist/publish/api/publishArgs.d.ts +2 -0
- package/dist/publish/api/publishArgs.js +25 -0
- package/dist/publish/api/publishHookApi.d.ts +10 -0
- package/dist/publish/api/publishHookApi.js +52 -0
- package/dist/publish/cli.d.ts +2 -0
- package/dist/publish/cli.js +94 -0
- package/dist/publish/ownerRunner.d.ts +6 -0
- package/dist/publish/ownerRunner.js +22 -0
- package/dist/publish/publishRunner.d.ts +16 -0
- package/dist/publish/publishRunner.js +108 -0
- package/dist/publish/runner/packageBuilder.d.ts +4 -0
- package/dist/publish/runner/packageBuilder.js +15 -0
- package/dist/publish/runner/packagePublisher.d.ts +7 -0
- package/dist/publish/runner/packagePublisher.js +21 -0
- package/dist/publish/runner/publishFailureReporter.d.ts +3 -0
- package/dist/publish/runner/publishFailureReporter.js +14 -0
- package/dist/publish/runner/publishOutputFormatter.d.ts +2 -0
- package/dist/publish/runner/publishOutputFormatter.js +83 -0
- package/dist/publish/runner/publishPreflight.d.ts +8 -0
- package/dist/publish/runner/publishPreflight.js +26 -0
- package/dist/publish/runner/publishSummaryReporter.d.ts +10 -0
- package/dist/publish/runner/publishSummaryReporter.js +81 -0
- package/dist/publish/runner/publishTargetError.d.ts +9 -0
- package/dist/publish/runner/publishTargetError.js +14 -0
- package/dist/publish/runner/releaseGitController.d.ts +10 -0
- package/dist/publish/runner/releaseGitController.js +41 -0
- package/dist/publish/runner/versionWriter.d.ts +13 -0
- package/dist/publish/runner/versionWriter.js +78 -0
- package/dist/publish/targetResolver.d.ts +7 -0
- package/dist/publish/targetResolver.js +195 -0
- package/dist/publish/types.d.ts +70 -0
- package/dist/publish/types.js +0 -0
- package/dist/publish/version.d.ts +5 -0
- package/dist/publish/version.js +55 -0
- package/dist/types.d.ts +8 -7
- package/dist/workspace/packages.d.ts +9 -0
- package/dist/workspace/packages.js +62 -0
- package/dist/workspace/root.d.ts +1 -0
- package/dist/workspace/root.js +14 -0
- package/package.json +15 -6
- package/bin/entry.cjs +0 -163
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import minimist from 'minimist';
|
|
2
|
+
import { OwnerRunner } from '#auklet/publish/ownerRunner';
|
|
3
|
+
import { PublishRunner } from '#auklet/publish/publishRunner';
|
|
4
|
+
import { ensurePnpm } from '#auklet/publish/api/pnpmApi';
|
|
5
|
+
const publishFlags = new Set([
|
|
6
|
+
'_',
|
|
7
|
+
'filter',
|
|
8
|
+
'version',
|
|
9
|
+
'dry-run',
|
|
10
|
+
'format',
|
|
11
|
+
'otp',
|
|
12
|
+
'ignore-scripts',
|
|
13
|
+
'allow-dirty',
|
|
14
|
+
]);
|
|
15
|
+
const ownerFlags = new Set(['_', 'filter', 'package', 'otp']);
|
|
16
|
+
export async function runPublishCli(args) {
|
|
17
|
+
const cliArgs = stripLeadingArgsSeparator(args);
|
|
18
|
+
validateNoPrefixedFlags(cliArgs, new Set(['--no-format']));
|
|
19
|
+
const argv = minimist(cliArgs, {
|
|
20
|
+
string: ['filter', 'version', 'otp'],
|
|
21
|
+
boolean: ['dry-run', 'format', 'ignore-scripts', 'allow-dirty'],
|
|
22
|
+
default: {
|
|
23
|
+
format: true,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
validateFlags(argv, publishFlags);
|
|
27
|
+
if (argv._.length) {
|
|
28
|
+
throw new Error(`[auklet:publish] unknown publish argument: ${argv._.join(' ')}`);
|
|
29
|
+
}
|
|
30
|
+
await ensurePnpm();
|
|
31
|
+
await new PublishRunner({
|
|
32
|
+
cwd: process.cwd(),
|
|
33
|
+
filters: toArray(argv.filter),
|
|
34
|
+
version: stringOption(argv.version),
|
|
35
|
+
dryRun: argv['dry-run'] === true,
|
|
36
|
+
format: argv.format !== false,
|
|
37
|
+
otp: stringOption(argv.otp),
|
|
38
|
+
ignoreScripts: argv['ignore-scripts'] === true,
|
|
39
|
+
allowDirty: argv['allow-dirty'] === true,
|
|
40
|
+
}).run();
|
|
41
|
+
}
|
|
42
|
+
export async function runOwnerCli(args) {
|
|
43
|
+
const cliArgs = stripLeadingArgsSeparator(args);
|
|
44
|
+
validateNoPrefixedFlags(cliArgs, new Set());
|
|
45
|
+
const argv = minimist(cliArgs, {
|
|
46
|
+
string: ['filter', 'package', 'otp'],
|
|
47
|
+
});
|
|
48
|
+
validateFlags(argv, ownerFlags);
|
|
49
|
+
const [subcommand, ...users] = argv._;
|
|
50
|
+
if (subcommand !== 'add') {
|
|
51
|
+
throw new Error('[auklet:publish] expected owner command: auk owner add <user...>');
|
|
52
|
+
}
|
|
53
|
+
if (!users.length) {
|
|
54
|
+
throw new Error('[auklet:publish] owner add requires at least one user.');
|
|
55
|
+
}
|
|
56
|
+
await ensurePnpm();
|
|
57
|
+
await new OwnerRunner({
|
|
58
|
+
cwd: process.cwd(),
|
|
59
|
+
users,
|
|
60
|
+
filters: toArray(argv.filter),
|
|
61
|
+
packages: toArray(argv.package),
|
|
62
|
+
otp: stringOption(argv.otp),
|
|
63
|
+
}).run();
|
|
64
|
+
}
|
|
65
|
+
const validateFlags = (argv, allowedFlags) => {
|
|
66
|
+
for (const flag of Object.keys(argv)) {
|
|
67
|
+
if (!allowedFlags.has(flag)) {
|
|
68
|
+
throw new Error(`[auklet:publish] unknown option: --${flag}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const validateNoPrefixedFlags = (args, allowedFlags) => {
|
|
73
|
+
const flag = args.find((arg) => arg.startsWith('--no-') && !allowedFlags.has(arg));
|
|
74
|
+
if (flag) {
|
|
75
|
+
throw new Error(`[auklet:publish] unknown option: ${flag}`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const stripLeadingArgsSeparator = (args) => {
|
|
79
|
+
return args.filter((arg) => arg !== '--');
|
|
80
|
+
};
|
|
81
|
+
const toArray = (value) => {
|
|
82
|
+
if (value === undefined)
|
|
83
|
+
return [];
|
|
84
|
+
return Array.isArray(value)
|
|
85
|
+
? value.map(String).filter(Boolean)
|
|
86
|
+
: [String(value)].filter(Boolean);
|
|
87
|
+
};
|
|
88
|
+
const stringOption = (value) => {
|
|
89
|
+
if (value === undefined)
|
|
90
|
+
return undefined;
|
|
91
|
+
if (Array.isArray(value))
|
|
92
|
+
return String(value.at(-1));
|
|
93
|
+
return String(value);
|
|
94
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { runPnpmOwnerAdd } from '#auklet/publish/api/pnpmApi';
|
|
2
|
+
import { resolveOwnerPackageNames } from '#auklet/publish/targetResolver';
|
|
3
|
+
export class OwnerRunner {
|
|
4
|
+
options;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async run() {
|
|
9
|
+
const packageNames = await resolveOwnerPackageNames(this.options);
|
|
10
|
+
if (!packageNames.length) {
|
|
11
|
+
throw new Error('[auklet:publish] no owner target package found.');
|
|
12
|
+
}
|
|
13
|
+
for (const packageName of packageNames) {
|
|
14
|
+
for (const user of this.options.users) {
|
|
15
|
+
await runPnpmOwnerAdd(packageName, user, {
|
|
16
|
+
cwd: this.options.cwd,
|
|
17
|
+
otp: this.options.otp,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PublishOptions } from '#auklet/publish/types';
|
|
2
|
+
export declare class PublishRunner {
|
|
3
|
+
private readonly git;
|
|
4
|
+
private readonly publisher;
|
|
5
|
+
private readonly preflight;
|
|
6
|
+
private readonly versions;
|
|
7
|
+
private readonly logger;
|
|
8
|
+
private readonly summaryLogger;
|
|
9
|
+
private readonly options;
|
|
10
|
+
constructor(options: PublishOptions);
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
private preparePlan;
|
|
13
|
+
private publishWithPlan;
|
|
14
|
+
private handleFailure;
|
|
15
|
+
private runAfterPublishSuccess;
|
|
16
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createAukletLogger, createScopedAukletLogger } from '#auklet/logger';
|
|
2
|
+
import { runPublishHook } from '#auklet/publish/api/publishHookApi';
|
|
3
|
+
import { runPackageBuilds, validateBuildScript, } from '#auklet/publish/runner/packageBuilder';
|
|
4
|
+
import { formatPublishOutputs } from '#auklet/publish/runner/publishOutputFormatter';
|
|
5
|
+
import { resolvePublishPlan } from '#auklet/publish/targetResolver';
|
|
6
|
+
import { PackagePublisher } from '#auklet/publish/runner/packagePublisher';
|
|
7
|
+
import { reportPublishFailure } from '#auklet/publish/runner/publishFailureReporter';
|
|
8
|
+
import { reportPublishSummary } from '#auklet/publish/runner/publishSummaryReporter';
|
|
9
|
+
import { PublishPreflight } from '#auklet/publish/runner/publishPreflight';
|
|
10
|
+
import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
|
|
11
|
+
import { ReleaseGitController } from '#auklet/publish/runner/releaseGitController';
|
|
12
|
+
import { VersionWriter } from '#auklet/publish/runner/versionWriter';
|
|
13
|
+
export class PublishRunner {
|
|
14
|
+
git;
|
|
15
|
+
publisher;
|
|
16
|
+
preflight;
|
|
17
|
+
versions;
|
|
18
|
+
logger;
|
|
19
|
+
summaryLogger;
|
|
20
|
+
options;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.logger = createScopedAukletLogger('publish');
|
|
24
|
+
this.summaryLogger = createAukletLogger();
|
|
25
|
+
this.git = new ReleaseGitController(this.options, this.logger);
|
|
26
|
+
this.publisher = new PackagePublisher(this.options);
|
|
27
|
+
this.preflight = new PublishPreflight(this.options);
|
|
28
|
+
this.versions = new VersionWriter(this.options, this.logger);
|
|
29
|
+
}
|
|
30
|
+
async run() {
|
|
31
|
+
const plan = await this.preparePlan();
|
|
32
|
+
let failureHookEnabled = false;
|
|
33
|
+
try {
|
|
34
|
+
await runPublishHook({ status: 'beforeBuild', plan });
|
|
35
|
+
failureHookEnabled = true;
|
|
36
|
+
this.versions.writeBeforeBuild(plan);
|
|
37
|
+
await runPackageBuilds(plan.targets, this.logger);
|
|
38
|
+
await runPublishHook({ status: 'afterBuild', plan });
|
|
39
|
+
await formatPublishOutputs(plan.targets, this.options.format);
|
|
40
|
+
await runPublishHook({ status: 'beforePublish', plan });
|
|
41
|
+
await this.publishWithPlan(plan);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (!failureHookEnabled) {
|
|
45
|
+
reportPublishSummary(this.summaryLogger, {
|
|
46
|
+
plan,
|
|
47
|
+
status: 'failure',
|
|
48
|
+
error,
|
|
49
|
+
});
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
await this.handleFailure(plan, error);
|
|
53
|
+
}
|
|
54
|
+
await this.runAfterPublishSuccess(plan);
|
|
55
|
+
}
|
|
56
|
+
async preparePlan() {
|
|
57
|
+
const plan = await resolvePublishPlan(this.options, this.logger);
|
|
58
|
+
validateBuildScript(plan.targets);
|
|
59
|
+
await this.git.checkBeforePublish(plan);
|
|
60
|
+
this.versions.logDryRunPlan(plan);
|
|
61
|
+
return plan;
|
|
62
|
+
}
|
|
63
|
+
async publishWithPlan(plan) {
|
|
64
|
+
if (plan.dryRun) {
|
|
65
|
+
await this.preflight.run(plan);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
await this.preflight.run(plan);
|
|
69
|
+
await this.git.commitAndTag(plan);
|
|
70
|
+
await this.publisher.run(plan);
|
|
71
|
+
}
|
|
72
|
+
async handleFailure(plan, error) {
|
|
73
|
+
const failedTarget = error instanceof PublishTargetError ? error.target : undefined;
|
|
74
|
+
try {
|
|
75
|
+
await runPublishHook({
|
|
76
|
+
status: 'afterPublish',
|
|
77
|
+
plan,
|
|
78
|
+
result: 'failure',
|
|
79
|
+
failedTarget,
|
|
80
|
+
error: error instanceof PublishTargetError ? error.originalError : error,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (hookError) {
|
|
84
|
+
this.logger.error?.(hookError);
|
|
85
|
+
}
|
|
86
|
+
if (error instanceof PublishTargetError) {
|
|
87
|
+
reportPublishFailure(error, plan.version, this.logger);
|
|
88
|
+
}
|
|
89
|
+
reportPublishSummary(this.summaryLogger, {
|
|
90
|
+
plan,
|
|
91
|
+
status: 'failure',
|
|
92
|
+
error,
|
|
93
|
+
});
|
|
94
|
+
this.versions.logWrittenVersionFailure(plan);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
async runAfterPublishSuccess(plan) {
|
|
98
|
+
await runPublishHook({
|
|
99
|
+
status: 'afterPublish',
|
|
100
|
+
plan,
|
|
101
|
+
result: 'success',
|
|
102
|
+
});
|
|
103
|
+
reportPublishSummary(this.summaryLogger, {
|
|
104
|
+
plan,
|
|
105
|
+
status: 'success',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PublishTarget } from '#auklet/publish/types';
|
|
2
|
+
import type { AukletLogger } from '#auklet/logger';
|
|
3
|
+
export declare function validateBuildScript(targets: Array<PublishTarget>): void;
|
|
4
|
+
export declare function runPackageBuilds(targets: Array<PublishTarget>, logger: AukletLogger): Promise<void>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runPnpmBuild } from '#auklet/publish/api/pnpmApi';
|
|
2
|
+
export function validateBuildScript(targets) {
|
|
3
|
+
for (const target of targets) {
|
|
4
|
+
const buildScript = target.packageJson.scripts?.build;
|
|
5
|
+
if (typeof buildScript !== 'string' || !buildScript) {
|
|
6
|
+
throw new Error(`[auklet:publish] package ${target.packageName} must define package.json#scripts.build before publishing.`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export async function runPackageBuilds(targets, logger) {
|
|
11
|
+
for (const target of targets) {
|
|
12
|
+
logger.step('build ', logger.package(target.packageName));
|
|
13
|
+
await runPnpmBuild(target.packageRoot);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PnpmPublishApi } from '#auklet/publish/api/pnpmPublishApi';
|
|
2
|
+
import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
|
|
3
|
+
export class PackagePublisher {
|
|
4
|
+
options;
|
|
5
|
+
pnpm = new PnpmPublishApi();
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.options = options;
|
|
8
|
+
}
|
|
9
|
+
async run(plan) {
|
|
10
|
+
const publishedTargets = [];
|
|
11
|
+
for (const target of plan.targets) {
|
|
12
|
+
try {
|
|
13
|
+
await this.pnpm.publish(target, this.options);
|
|
14
|
+
publishedTargets.push(target);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw new PublishTargetError(target, 'publish', error, publishedTargets);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function reportPublishFailure(error, version, logger) {
|
|
2
|
+
if (error.phase === 'publish' && error.publishedTargets.length) {
|
|
3
|
+
logger.error('partial publish detected');
|
|
4
|
+
logger.error('published packages:');
|
|
5
|
+
for (const target of error.publishedTargets) {
|
|
6
|
+
logger.error(formatPublishedTarget(logger, target.packageName, version));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
logger.error('failed package:');
|
|
10
|
+
logger.error(formatPublishedTarget(logger, error.target.packageName, version));
|
|
11
|
+
}
|
|
12
|
+
const formatPublishedTarget = (logger, packageName, version) => {
|
|
13
|
+
return ['- ', logger.package(packageName), '@', logger.version(version)];
|
|
14
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import prettier from 'prettier';
|
|
4
|
+
const supportedExtensions = new Set([
|
|
5
|
+
'.js',
|
|
6
|
+
'.ts',
|
|
7
|
+
'.tsx',
|
|
8
|
+
'.css',
|
|
9
|
+
'.json',
|
|
10
|
+
'.yaml',
|
|
11
|
+
'.yml',
|
|
12
|
+
]);
|
|
13
|
+
export async function formatPublishOutputs(targets, enabled) {
|
|
14
|
+
if (!enabled)
|
|
15
|
+
return;
|
|
16
|
+
for (const target of targets) {
|
|
17
|
+
const files = collectPublishFiles(target);
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
await formatFile(file);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const collectPublishFiles = (target) => {
|
|
24
|
+
const patterns = target.packageJson.files ?? [];
|
|
25
|
+
const includes = patterns.filter((item) => !item.startsWith('!'));
|
|
26
|
+
const excludes = patterns
|
|
27
|
+
.filter((item) => item.startsWith('!'))
|
|
28
|
+
.map((item) => item.slice(1));
|
|
29
|
+
const files = new Set();
|
|
30
|
+
for (const include of includes) {
|
|
31
|
+
const absolutePath = path.resolve(target.packageRoot, include);
|
|
32
|
+
if (!fs.existsSync(absolutePath))
|
|
33
|
+
continue;
|
|
34
|
+
for (const file of walkFiles(absolutePath)) {
|
|
35
|
+
if (isExcluded(target.packageRoot, file, excludes))
|
|
36
|
+
continue;
|
|
37
|
+
if (!isSupportedStyleFile(file))
|
|
38
|
+
continue;
|
|
39
|
+
files.add(file);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return [...files].sort();
|
|
43
|
+
};
|
|
44
|
+
const walkFiles = (absolutePath) => {
|
|
45
|
+
const stat = fs.statSync(absolutePath);
|
|
46
|
+
if (stat.isFile())
|
|
47
|
+
return [absolutePath];
|
|
48
|
+
if (!stat.isDirectory())
|
|
49
|
+
return [];
|
|
50
|
+
const files = [];
|
|
51
|
+
for (const entry of fs.readdirSync(absolutePath)) {
|
|
52
|
+
files.push(...walkFiles(path.join(absolutePath, entry)));
|
|
53
|
+
}
|
|
54
|
+
return files;
|
|
55
|
+
};
|
|
56
|
+
const isExcluded = (packageRoot, file, excludes) => {
|
|
57
|
+
const relativePath = path
|
|
58
|
+
.relative(packageRoot, file)
|
|
59
|
+
.split(path.sep)
|
|
60
|
+
.join('/');
|
|
61
|
+
return excludes.some((exclude) => {
|
|
62
|
+
const normalized = exclude.replace(/\\/g, '/').replace(/\/$/, '');
|
|
63
|
+
return (relativePath === normalized || relativePath.startsWith(`${normalized}/`));
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const isSupportedStyleFile = (file) => {
|
|
67
|
+
if (file.endsWith('.d.ts'))
|
|
68
|
+
return true;
|
|
69
|
+
return supportedExtensions.has(path.extname(file));
|
|
70
|
+
};
|
|
71
|
+
const formatFile = async (file) => {
|
|
72
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
73
|
+
let formatted;
|
|
74
|
+
try {
|
|
75
|
+
formatted = await prettier.format(source, { filepath: file });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (formatted !== source) {
|
|
81
|
+
fs.writeFileSync(file, formatted);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PublishOptions, PublishPlan } from '#auklet/publish/types';
|
|
2
|
+
export declare class PublishPreflight {
|
|
3
|
+
private readonly options;
|
|
4
|
+
private readonly pnpm;
|
|
5
|
+
constructor(options: PublishOptions);
|
|
6
|
+
run(plan: PublishPlan): Promise<void>;
|
|
7
|
+
private verifyPnpmPublishDryRun;
|
|
8
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PnpmPublishApi } from '#auklet/publish/api/pnpmPublishApi';
|
|
2
|
+
import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
|
|
3
|
+
export class PublishPreflight {
|
|
4
|
+
options;
|
|
5
|
+
pnpm = new PnpmPublishApi();
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.options = options;
|
|
8
|
+
}
|
|
9
|
+
async run(plan) {
|
|
10
|
+
await this.verifyPnpmPublishDryRun(plan);
|
|
11
|
+
}
|
|
12
|
+
async verifyPnpmPublishDryRun(plan) {
|
|
13
|
+
const options = {
|
|
14
|
+
...this.options,
|
|
15
|
+
dryRun: true,
|
|
16
|
+
};
|
|
17
|
+
for (const target of plan.targets) {
|
|
18
|
+
try {
|
|
19
|
+
await this.pnpm.publish(target, options);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw new PublishTargetError(target, 'preflight', error, []);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AukletLogger } from '#auklet/logger';
|
|
2
|
+
import type { PublishPlan } from '#auklet/publish/types';
|
|
3
|
+
type PublishSummaryStatus = 'success' | 'failure';
|
|
4
|
+
type PublishSummaryOptions = {
|
|
5
|
+
plan: PublishPlan;
|
|
6
|
+
status: PublishSummaryStatus;
|
|
7
|
+
error?: unknown;
|
|
8
|
+
};
|
|
9
|
+
export declare function reportPublishSummary(logger: AukletLogger, options: PublishSummaryOptions): void;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
|
|
2
|
+
export function reportPublishSummary(logger, options) {
|
|
3
|
+
const publishError = options.error instanceof PublishTargetError ? options.error : undefined;
|
|
4
|
+
const failedTarget = publishError?.target;
|
|
5
|
+
const publishedTargets = publishError?.publishedTargets ?? [];
|
|
6
|
+
const publishedTargetRoots = new Set(publishedTargets.map((target) => getTargetKey(target)));
|
|
7
|
+
const publishedCount = options.status === 'success' && !options.plan.dryRun
|
|
8
|
+
? options.plan.targets.length
|
|
9
|
+
: publishedTargets.length;
|
|
10
|
+
logger.newline();
|
|
11
|
+
logger.raw(logger.colors.gray('-'.repeat(56)));
|
|
12
|
+
logger.newline();
|
|
13
|
+
logger.result({
|
|
14
|
+
title: logger.colors.bold(getSummaryTitle(options.status, options.plan)),
|
|
15
|
+
status: options.status === 'success' ? 'success' : 'error',
|
|
16
|
+
body: getSummaryBody(options.status, options.plan, failedTarget),
|
|
17
|
+
details: {
|
|
18
|
+
mode: options.plan.dryRun ? 'dry-run' : 'publish',
|
|
19
|
+
packages: String(options.plan.targets.length),
|
|
20
|
+
version: logger.version(options.plan.version),
|
|
21
|
+
published: String(publishedCount),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
logger.tasks({
|
|
25
|
+
tasks: getVersionChangeTasks(logger, {
|
|
26
|
+
targets: options.plan.targets,
|
|
27
|
+
status: options.status,
|
|
28
|
+
dryRun: options.plan.dryRun,
|
|
29
|
+
failedTarget,
|
|
30
|
+
publishedTargetRoots,
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
logger.newline();
|
|
34
|
+
}
|
|
35
|
+
const getSummaryTitle = (status, plan) => {
|
|
36
|
+
if (status === 'failure')
|
|
37
|
+
return 'Publish failed';
|
|
38
|
+
return plan.dryRun ? 'Publish dry-run complete' : 'Publish complete';
|
|
39
|
+
};
|
|
40
|
+
const getSummaryBody = (status, plan, failedTarget) => {
|
|
41
|
+
if (status === 'success') {
|
|
42
|
+
return [
|
|
43
|
+
plan.dryRun
|
|
44
|
+
? 'Preflight completed without publishing packages.'
|
|
45
|
+
: 'All selected packages were published.',
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
if (failedTarget) {
|
|
49
|
+
return [['Failed at ', failedTarget.packageName, '. Check logs above.']];
|
|
50
|
+
}
|
|
51
|
+
return ['Publish stopped before package publishing completed.'];
|
|
52
|
+
};
|
|
53
|
+
const getVersionChangeTasks = (logger, options) => {
|
|
54
|
+
return options.targets.map((target) => ({
|
|
55
|
+
title: [
|
|
56
|
+
logger.package(target.packageName),
|
|
57
|
+
' ',
|
|
58
|
+
...formatVersionChange(logger, target.version, target.publishVersion),
|
|
59
|
+
],
|
|
60
|
+
status: getVersionChangeTaskStatus(target, options),
|
|
61
|
+
}));
|
|
62
|
+
};
|
|
63
|
+
const getVersionChangeTaskStatus = (target, options) => {
|
|
64
|
+
if (options.dryRun)
|
|
65
|
+
return 'skipped';
|
|
66
|
+
if (options.status === 'success')
|
|
67
|
+
return 'success';
|
|
68
|
+
if (options.failedTarget &&
|
|
69
|
+
getTargetKey(target) === getTargetKey(options.failedTarget)) {
|
|
70
|
+
return 'error';
|
|
71
|
+
}
|
|
72
|
+
if (options.publishedTargetRoots.has(getTargetKey(target)))
|
|
73
|
+
return 'success';
|
|
74
|
+
return 'skipped';
|
|
75
|
+
};
|
|
76
|
+
const getTargetKey = (target) => {
|
|
77
|
+
return `${target.packageRoot}:${target.packageName}`;
|
|
78
|
+
};
|
|
79
|
+
const formatVersionChange = (logger, from, to) => {
|
|
80
|
+
return [logger.version(from), logger.colors.gray(' -> '), logger.version(to)];
|
|
81
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PublishTarget } from '#auklet/publish/types';
|
|
2
|
+
export type PublishPhase = 'preflight' | 'publish';
|
|
3
|
+
export declare class PublishTargetError extends Error {
|
|
4
|
+
readonly target: PublishTarget;
|
|
5
|
+
readonly phase: PublishPhase;
|
|
6
|
+
readonly originalError: unknown;
|
|
7
|
+
readonly publishedTargets: Array<PublishTarget>;
|
|
8
|
+
constructor(target: PublishTarget, phase: PublishPhase, originalError: unknown, publishedTargets: Array<PublishTarget>);
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export class PublishTargetError extends Error {
|
|
3
|
+
target;
|
|
4
|
+
phase;
|
|
5
|
+
originalError;
|
|
6
|
+
publishedTargets;
|
|
7
|
+
constructor(target, phase, originalError, publishedTargets) {
|
|
8
|
+
super(`[auklet:publish] ${phase} failed for ${target.packageName} at ${path.relative(process.cwd(), target.packageRoot) || target.packageRoot}.`);
|
|
9
|
+
this.target = target;
|
|
10
|
+
this.phase = phase;
|
|
11
|
+
this.originalError = originalError;
|
|
12
|
+
this.publishedTargets = publishedTargets;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PublishOptions, PublishPlan } from '#auklet/publish/types';
|
|
2
|
+
import type { AukletLogger } from '#auklet/logger';
|
|
3
|
+
export declare class ReleaseGitController {
|
|
4
|
+
private readonly options;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
constructor(options: PublishOptions, logger: AukletLogger);
|
|
7
|
+
checkBeforePublish(plan: PublishPlan): Promise<void>;
|
|
8
|
+
commitAndTag(plan: PublishPlan): Promise<void>;
|
|
9
|
+
private warnOnce;
|
|
10
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { commitRelease, createVersionTag, ensureGitClean, hasGitChanges, isGitRepository, } from '#auklet/publish/api/gitApi';
|
|
2
|
+
export class ReleaseGitController {
|
|
3
|
+
options;
|
|
4
|
+
logger;
|
|
5
|
+
constructor(options, logger) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
this.logger = logger;
|
|
8
|
+
}
|
|
9
|
+
async checkBeforePublish(plan) {
|
|
10
|
+
const git = await isGitRepository(plan.root);
|
|
11
|
+
if (plan.dryRun || !git)
|
|
12
|
+
return;
|
|
13
|
+
if (this.options.allowDirty) {
|
|
14
|
+
this.warnOnce('--allow-dirty enabled, skipping git clean check, commit, and tag.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
await ensureGitClean(plan.root);
|
|
18
|
+
}
|
|
19
|
+
async commitAndTag(plan) {
|
|
20
|
+
const git = await isGitRepository(plan.root);
|
|
21
|
+
if (git && this.options.allowDirty) {
|
|
22
|
+
this.warnOnce('--allow-dirty enabled, skipping git commit and tag.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!git) {
|
|
26
|
+
this.warnOnce('git repository not found, skipping commit and tag.');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const changed = await hasGitChanges(plan.root);
|
|
30
|
+
if (changed) {
|
|
31
|
+
if (!this.options.version) {
|
|
32
|
+
throw new Error('[auklet:publish] build or format changed files. Commit changes before publishing without --version.');
|
|
33
|
+
}
|
|
34
|
+
await commitRelease(plan.root, plan.version);
|
|
35
|
+
}
|
|
36
|
+
await createVersionTag(plan.root, plan.version, this.logger);
|
|
37
|
+
}
|
|
38
|
+
warnOnce(content) {
|
|
39
|
+
this.logger.warnOnce(content);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PublishOptions, PublishPlan } from '#auklet/publish/types';
|
|
2
|
+
import type { AukletLogger } from '#auklet/logger';
|
|
3
|
+
export declare class VersionWriter {
|
|
4
|
+
private readonly options;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
constructor(options: PublishOptions, logger: AukletLogger);
|
|
7
|
+
writeBeforeBuild(plan: PublishPlan): void;
|
|
8
|
+
logDryRunPlan(plan: PublishPlan): void;
|
|
9
|
+
logWrittenVersionFailure(plan: PublishPlan): void;
|
|
10
|
+
private writeVersions;
|
|
11
|
+
private formatPackage;
|
|
12
|
+
private formatVersionChange;
|
|
13
|
+
}
|