@codefast/cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +16 -2
- package/dist/arrange/command.js +96 -132
- package/dist/arrange/domain/ast/simplify-targets.js +18 -23
- package/dist/arrange/{typescript-ast-translator.d.ts → domain/ast/translator.d.ts} +5 -4
- package/dist/arrange/{typescript-ast-translator.js → domain/ast/translator.js} +19 -22
- package/dist/arrange/group/cli-result.d.ts +7 -0
- package/dist/arrange/group/cli-result.js +12 -0
- package/dist/arrange/group/cli-schema.d.ts +17 -0
- package/dist/arrange/group/cli-schema.js +13 -0
- package/dist/arrange/group/output.d.ts +7 -0
- package/dist/arrange/group/output.js +10 -0
- package/dist/arrange/{suggest.d.ts → group/suggest.d.ts} +1 -1
- package/dist/arrange/inspect/cli-result.d.ts +7 -0
- package/dist/arrange/inspect/cli-result.js +8 -0
- package/dist/arrange/inspect/cli-schema.d.ts +15 -0
- package/dist/arrange/inspect/cli-schema.js +9 -0
- package/dist/arrange/inspect/output.d.ts +7 -0
- package/dist/arrange/inspect/output.js +42 -0
- package/dist/arrange/{analyze.d.ts → inspect/run.d.ts} +2 -2
- package/dist/arrange/{analyze.js → inspect/run.js} +2 -2
- package/dist/arrange/{workspace.d.ts → prepare.d.ts} +2 -2
- package/dist/arrange/{workspace.js → prepare.js} +5 -8
- package/dist/arrange/regroup/cli-result.d.ts +13 -0
- package/dist/arrange/regroup/cli-result.js +23 -0
- package/dist/arrange/regroup/cli-schema.d.ts +20 -0
- package/dist/arrange/regroup/cli-schema.js +14 -0
- package/dist/arrange/regroup/output.d.ts +14 -0
- package/dist/arrange/regroup/output.js +72 -0
- package/dist/arrange/{process-file.d.ts → regroup/process-file.d.ts} +2 -2
- package/dist/arrange/{sync.d.ts → regroup/run.d.ts} +3 -3
- package/dist/arrange/{sync.js → regroup/run.js} +2 -2
- package/dist/arrange/resolve-target.d.ts +2 -2
- package/dist/arrange/scan-target.d.ts +2 -2
- package/dist/arrange/simplify/cli-result.d.ts +7 -0
- package/dist/arrange/simplify/cli-result.js +8 -0
- package/dist/arrange/simplify/cli-schema.d.ts +17 -0
- package/dist/arrange/simplify/cli-schema.js +11 -0
- package/dist/arrange/simplify/fold-targets.d.ts +13 -0
- package/dist/arrange/simplify/fold-targets.js +139 -0
- package/dist/arrange/simplify/output.d.ts +7 -0
- package/dist/arrange/simplify/output.js +15 -0
- package/dist/arrange/{simplify-process-file.d.ts → simplify/process-file.d.ts} +4 -2
- package/dist/arrange/simplify/process-file.js +49 -0
- package/dist/arrange/{simplify-sync.d.ts → simplify/run.d.ts} +3 -2
- package/dist/arrange/simplify/run.js +38 -0
- package/dist/arrange/simplify/variant-classname-probe.d.ts +35 -0
- package/dist/arrange/simplify/variant-classname-probe.js +95 -0
- package/dist/arrange/source-parse.js +1 -1
- package/dist/audit/command.js +134 -211
- package/dist/audit/comments/cli-result.d.ts +13 -0
- package/dist/audit/comments/cli-result.js +22 -0
- package/dist/audit/comments/cli-schema.d.ts +19 -0
- package/dist/audit/comments/cli-schema.js +13 -0
- package/dist/audit/comments/output.d.ts +7 -0
- package/dist/audit/comments/output.js +27 -0
- package/dist/audit/comments/prepare.d.ts +16 -0
- package/dist/audit/comments/prepare.js +12 -0
- package/dist/audit/{run-comments.d.ts → comments/run.d.ts} +2 -2
- package/dist/audit/{run-comments.js → comments/run.js} +5 -5
- package/dist/audit/display-names/cli-result.d.ts +13 -0
- package/dist/audit/display-names/cli-result.js +22 -0
- package/dist/audit/display-names/cli-schema.d.ts +18 -0
- package/dist/audit/display-names/cli-schema.js +12 -0
- package/dist/audit/display-names/output.d.ts +7 -0
- package/dist/audit/display-names/output.js +21 -0
- package/dist/audit/display-names/prepare.d.ts +16 -0
- package/dist/audit/display-names/prepare.js +12 -0
- package/dist/audit/{run-display-names.d.ts → display-names/run.d.ts} +2 -2
- package/dist/audit/{run-display-names.js → display-names/run.js} +1 -1
- package/dist/audit/imports/cli-result.d.ts +13 -0
- package/dist/audit/imports/cli-result.js +22 -0
- package/dist/audit/imports/cli-schema.d.ts +18 -0
- package/dist/audit/imports/cli-schema.js +12 -0
- package/dist/audit/{domain → imports/domain}/import-policy.d.ts +2 -2
- package/dist/audit/{domain → imports/domain}/import-policy.js +4 -11
- package/dist/audit/imports/output.d.ts +7 -0
- package/dist/audit/imports/output.js +21 -0
- package/dist/audit/imports/prepare.d.ts +16 -0
- package/dist/audit/imports/prepare.js +12 -0
- package/dist/audit/{run-imports.d.ts → imports/run.d.ts} +2 -2
- package/dist/audit/{run-imports.js → imports/run.js} +2 -2
- package/dist/audit/links/cli-result.d.ts +13 -0
- package/dist/audit/links/cli-result.js +22 -0
- package/dist/audit/links/cli-schema.d.ts +18 -0
- package/dist/audit/links/cli-schema.js +12 -0
- package/dist/audit/links/output.d.ts +7 -0
- package/dist/audit/links/output.js +21 -0
- package/dist/audit/links/prepare.d.ts +16 -0
- package/dist/audit/links/prepare.js +12 -0
- package/dist/audit/{run-links.d.ts → links/run.d.ts} +2 -2
- package/dist/audit/{run-links.js → links/run.js} +1 -1
- package/dist/audit/prepare.d.ts +11 -50
- package/dist/audit/prepare.js +9 -132
- package/dist/audit/rtl/cli-result.d.ts +13 -0
- package/dist/audit/rtl/cli-result.js +22 -0
- package/dist/audit/rtl/cli-schema.d.ts +18 -0
- package/dist/audit/rtl/cli-schema.js +12 -0
- package/dist/audit/{domain → rtl/domain}/audit-file.js +2 -2
- package/dist/audit/rtl/output.d.ts +7 -0
- package/dist/audit/rtl/output.js +21 -0
- package/dist/audit/rtl/prepare.d.ts +13 -0
- package/dist/audit/rtl/prepare.js +41 -0
- package/dist/audit/{run.d.ts → rtl/run.d.ts} +2 -2
- package/dist/audit/{run.js → rtl/run.js} +1 -1
- package/dist/core/cli/command-pipeline.d.ts +91 -0
- package/dist/core/cli/command-pipeline.js +83 -0
- package/dist/core/cli/global-options.d.ts +1 -1
- package/dist/core/cli/global-options.js +1 -1
- package/dist/core/cli/resolve-root.d.ts +9 -0
- package/dist/core/cli/resolve-root.js +16 -0
- package/dist/core/cli/result-handle.d.ts +1 -7
- package/dist/core/cli/result-handle.js +0 -13
- package/dist/core/config/loader.d.ts +2 -2
- package/dist/core/config/schema.d.ts +1 -1
- package/dist/core/config/schema.js +1 -1
- package/dist/core/config.d.ts +2 -2
- package/dist/core/filesystem/{port.d.ts → filesystem.d.ts} +1 -1
- package/dist/core/filesystem/node.d.ts +3 -3
- package/dist/core/filesystem/node.js +1 -1
- package/dist/core/source-text-edit.d.ts +14 -0
- package/dist/core/source-text-edit.js +22 -0
- package/dist/core/workspace/markdown-walk.d.ts +2 -2
- package/dist/core/workspace/package-version.d.ts +2 -2
- package/dist/core/workspace/resolver.d.ts +3 -3
- package/dist/core/workspace/source-walk.d.ts +2 -2
- package/dist/core/workspace/typescript-walk.d.ts +2 -2
- package/dist/core/workspace/walk-files.d.ts +2 -2
- package/dist/mirror/cli-schema.d.ts +2 -2
- package/dist/mirror/cli-schema.js +1 -1
- package/dist/mirror/command.js +31 -60
- package/dist/mirror/{dist-filesystem-impl.d.ts → dist-filesystem-node.d.ts} +3 -3
- package/dist/mirror/{dist-filesystem-impl.js → dist-filesystem-node.js} +1 -1
- package/dist/mirror/domain/dirent-guard.d.ts +1 -1
- package/dist/mirror/domain/types.d.ts +57 -0
- package/dist/mirror/output.d.ts +1 -3
- package/dist/mirror/output.js +126 -1
- package/dist/mirror/package-path.d.ts +3 -3
- package/dist/mirror/prepare.d.ts +3 -3
- package/dist/mirror/prepare.js +6 -10
- package/dist/mirror/run.d.ts +12 -0
- package/dist/mirror/supplement-exports.d.ts +3 -3
- package/dist/mirror/sync-workspace-package.d.ts +2 -2
- package/dist/mirror/sync-workspace-package.js +1 -1
- package/dist/mirror/write-exports.d.ts +2 -2
- package/dist/pack-slim/cli-schema.d.ts +1 -1
- package/dist/pack-slim/cli-schema.js +1 -1
- package/dist/pack-slim/command.js +31 -66
- package/dist/pack-slim/prepare.d.ts +21 -0
- package/dist/pack-slim/prepare.js +14 -0
- package/dist/pack-slim/{sync.d.ts → run.d.ts} +2 -2
- package/dist/tag/cli-result.d.ts +8 -2
- package/dist/tag/cli-result.js +14 -1
- package/dist/tag/cli-schema.d.ts +4 -4
- package/dist/tag/cli-schema.js +3 -4
- package/dist/tag/command.js +33 -60
- package/dist/tag/domain/skip-filter.d.ts +13 -0
- package/dist/tag/domain/skip-filter.js +29 -0
- package/dist/tag/domain/types.d.ts +21 -1
- package/dist/tag/domain/version-summary.d.ts +13 -0
- package/dist/tag/domain/version-summary.js +24 -0
- package/dist/tag/output.d.ts +4 -4
- package/dist/tag/output.js +6 -9
- package/dist/tag/prepare.d.ts +2 -2
- package/dist/tag/prepare.js +2 -2
- package/dist/tag/run.d.ts +10 -0
- package/dist/tag/{sync.js → run.js} +20 -50
- package/dist/tag/{target-candidates.d.ts → target/candidates.d.ts} +2 -2
- package/dist/tag/{target-candidates.js → target/candidates.js} +1 -1
- package/dist/tag/{resolve-target-path.d.ts → target/resolve-path.d.ts} +2 -2
- package/dist/tag/{target-runner.d.ts → target/runner.d.ts} +2 -2
- package/dist/tag/{target-runner.js → target/runner.js} +2 -2
- package/dist/tag/{since-writer.d.ts → writer/since-writer.d.ts} +2 -2
- package/dist/tag/{version-resolver.d.ts → writer/version-resolver.d.ts} +2 -2
- package/package.json +10 -2
- package/dist/arrange/cli-schema.d.ts +0 -50
- package/dist/arrange/cli-schema.js +0 -34
- package/dist/arrange/output.d.ts +0 -26
- package/dist/arrange/output.js +0 -127
- package/dist/arrange/simplify-process-file.js +0 -30
- package/dist/arrange/simplify-sync.js +0 -30
- package/dist/audit/cli-schema.d.ts +0 -93
- package/dist/audit/cli-schema.js +0 -66
- package/dist/audit/output.d.ts +0 -91
- package/dist/audit/output.js +0 -213
- package/dist/mirror/sync-reporter.d.ts +0 -60
- package/dist/mirror/sync-reporter.js +0 -128
- package/dist/mirror/sync-types.d.ts +0 -43
- package/dist/mirror/sync-types.js +0 -1
- package/dist/mirror/sync.d.ts +0 -12
- package/dist/tag/sync.d.ts +0 -42
- /package/dist/arrange/{suggest.js → group/suggest.js} +0 -0
- /package/dist/arrange/{domain → inspect/domain}/analyze-service.d.ts +0 -0
- /package/dist/arrange/{domain → inspect/domain}/analyze-service.js +0 -0
- /package/dist/arrange/{process-file.js → regroup/process-file.js} +0 -0
- /package/dist/audit/{domain → comments/domain}/comment-content.d.ts +0 -0
- /package/dist/audit/{domain → comments/domain}/comment-content.js +0 -0
- /package/dist/audit/{domain → comments/domain}/comment-dividers.d.ts +0 -0
- /package/dist/audit/{domain → comments/domain}/comment-dividers.js +0 -0
- /package/dist/audit/{domain → comments/domain}/link-references.d.ts +0 -0
- /package/dist/audit/{domain → comments/domain}/link-references.js +0 -0
- /package/dist/audit/{domain → comments/domain}/since-versions.d.ts +0 -0
- /package/dist/audit/{domain → comments/domain}/since-versions.js +0 -0
- /package/dist/audit/{domain → comments/domain}/tsdoc-syntax.d.ts +0 -0
- /package/dist/audit/{domain → comments/domain}/tsdoc-syntax.js +0 -0
- /package/dist/audit/{domain → display-names/domain}/display-names.d.ts +0 -0
- /package/dist/audit/{domain → display-names/domain}/display-names.js +0 -0
- /package/dist/audit/{domain → links/domain}/markdown-links.d.ts +0 -0
- /package/dist/audit/{domain → links/domain}/markdown-links.js +0 -0
- /package/dist/audit/{domain → rtl/domain}/audit-file.d.ts +0 -0
- /package/dist/audit/{domain → rtl/domain}/mappings.d.ts +0 -0
- /package/dist/audit/{domain → rtl/domain}/mappings.js +0 -0
- /package/dist/audit/{domain → rtl/domain}/tokenize.d.ts +0 -0
- /package/dist/audit/{domain → rtl/domain}/tokenize.js +0 -0
- /package/dist/core/filesystem/{port.js → filesystem.js} +0 -0
- /package/dist/mirror/{sync.js → run.js} +0 -0
- /package/dist/pack-slim/{sync.js → run.js} +0 -0
- /package/dist/tag/{resolve-target-path.js → target/resolve-path.js} +0 -0
- /package/dist/tag/{since-writer.js → writer/since-writer.js} +0 -0
- /package/dist/tag/{version-resolver.js → writer/version-resolver.js} +0 -0
package/dist/audit/prepare.d.ts
CHANGED
|
@@ -1,70 +1,31 @@
|
|
|
1
|
+
import type { CodefastConfig } from "#/core/config/schema";
|
|
1
2
|
import { AppError } from "#/core/errors";
|
|
2
|
-
import type {
|
|
3
|
+
import type { Filesystem } from "#/core/filesystem/filesystem";
|
|
3
4
|
import type { Result } from "#/core/result";
|
|
4
5
|
/**
|
|
5
|
-
* Shared prelude for
|
|
6
|
+
* Shared prelude for an audit: repo root and the canonicalized scan target with its allowlist.
|
|
6
7
|
*
|
|
7
8
|
* @since 0.5.0-canary.6
|
|
8
9
|
*/
|
|
9
|
-
export type
|
|
10
|
+
export type AuditCommandPrelude = {
|
|
10
11
|
readonly rootDir: string;
|
|
11
12
|
readonly targetPath: string;
|
|
12
13
|
readonly allowlist: ReadonlyArray<string>;
|
|
13
14
|
};
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
+
* Resolves a path that may be absolute or relative to `rootDir`.
|
|
16
17
|
*
|
|
17
18
|
* @since 0.5.0-canary.6
|
|
18
19
|
*/
|
|
19
|
-
export declare function
|
|
20
|
-
readonly currentWorkingDirectory: string;
|
|
21
|
-
readonly rawTarget: string | undefined;
|
|
22
|
-
}): Promise<Result<RtlAuditCommandPrelude, AppError>>;
|
|
23
|
-
/**
|
|
24
|
-
* Loads config and resolves the scan target for `audit links`.
|
|
25
|
-
*
|
|
26
|
-
* @remarks Defaults to the repo root rather than a configured path: a link audit that only covers one
|
|
27
|
-
* package cannot see the cross-package references that are the ones most likely to rot.
|
|
28
|
-
*
|
|
29
|
-
* @since 0.5.0
|
|
30
|
-
*/
|
|
31
|
-
export declare function prepareLinkAudit(fs: FilesystemPort, args: {
|
|
32
|
-
readonly currentWorkingDirectory: string;
|
|
33
|
-
readonly rawTarget: string | undefined;
|
|
34
|
-
}): Promise<Result<RtlAuditCommandPrelude, AppError>>;
|
|
35
|
-
/**
|
|
36
|
-
* Loads config and resolves the scan target for `audit imports`.
|
|
37
|
-
*
|
|
38
|
-
* @remarks Defaults to the repo root: the import policy is repo-wide, and generated or vendored
|
|
39
|
-
* trees are already excluded by the shared walk.
|
|
40
|
-
*
|
|
41
|
-
* @since 0.10.0
|
|
42
|
-
*/
|
|
43
|
-
export declare function prepareImportsAudit(fs: FilesystemPort, args: {
|
|
44
|
-
readonly currentWorkingDirectory: string;
|
|
45
|
-
readonly rawTarget: string | undefined;
|
|
46
|
-
}): Promise<Result<RtlAuditCommandPrelude, AppError>>;
|
|
47
|
-
/**
|
|
48
|
-
* Loads config and resolves the scan target for `audit comments`.
|
|
49
|
-
*
|
|
50
|
-
* @remarks Defaults to the repo root: a divider convention that only holds inside one package
|
|
51
|
-
* is not a convention.
|
|
52
|
-
*
|
|
53
|
-
* @since 0.6.0
|
|
54
|
-
*/
|
|
55
|
-
export declare function prepareCommentAudit(fs: FilesystemPort, args: {
|
|
56
|
-
readonly currentWorkingDirectory: string;
|
|
57
|
-
readonly rawTarget: string | undefined;
|
|
58
|
-
}): Promise<Result<RtlAuditCommandPrelude, AppError>>;
|
|
20
|
+
export declare function resolveRepoRelativePath(rootDir: string, maybeRelative: string): string;
|
|
59
21
|
/**
|
|
60
|
-
* Loads config and resolves the scan target
|
|
22
|
+
* Loads config and resolves the repo root as the scan target, taking the allowlist the caller selects.
|
|
61
23
|
*
|
|
62
|
-
* @remarks
|
|
63
|
-
* has to hold across them.
|
|
24
|
+
* @remarks Every repo-wide audit shares this prelude; only the config key its allowlist comes from differs.
|
|
64
25
|
*
|
|
65
|
-
* @since 0.
|
|
26
|
+
* @since 0.11.0
|
|
66
27
|
*/
|
|
67
|
-
export declare function
|
|
28
|
+
export declare function prepareRepoRootAudit(fs: Filesystem, args: {
|
|
68
29
|
readonly currentWorkingDirectory: string;
|
|
69
30
|
readonly rawTarget: string | undefined;
|
|
70
|
-
}): Promise<Result<
|
|
31
|
+
}, selectAllowlist: (config: CodefastConfig) => ReadonlyArray<string>): Promise<Result<AuditCommandPrelude, AppError>>;
|
package/dist/audit/prepare.js
CHANGED
|
@@ -1,146 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from "node:path";
|
|
2
2
|
import { loadCodefastConfig } from "#/core/config";
|
|
3
3
|
import { AppError, messageFrom } from "#/core/errors";
|
|
4
4
|
import { err, ok } from "#/core/result";
|
|
5
5
|
import { resolveProjectRoot } from "#/core/workspace/resolver";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Resolves a path that may be absolute or relative to `rootDir`.
|
|
8
8
|
*
|
|
9
9
|
* @since 0.5.0-canary.6
|
|
10
10
|
*/
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
// Realpath so allowlist keys (`path.relative(rootDir, file)`) stay stable when cwd is a symlink.
|
|
15
|
-
rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
|
|
16
|
-
}
|
|
17
|
-
catch (caughtError) {
|
|
18
|
-
return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
|
|
19
|
-
}
|
|
20
|
-
const loadedOutcome = await loadCodefastConfig(rootDir, fs);
|
|
21
|
-
if (!loadedOutcome.ok) {
|
|
22
|
-
return loadedOutcome;
|
|
23
|
-
}
|
|
24
|
-
const { config } = loadedOutcome.value;
|
|
25
|
-
const rtlConfig = config.audit?.rtl ?? {};
|
|
26
|
-
const targetFromCli = args.rawTarget;
|
|
27
|
-
const targetFromConfig = rtlConfig.target;
|
|
28
|
-
const resolvedTargetInput = targetFromCli ?? targetFromConfig;
|
|
29
|
-
if (resolvedTargetInput === undefined) {
|
|
30
|
-
return err(new AppError("VALIDATION_ERROR", "Missing scan target: pass a path argument or set audit.rtl.target in codefast.config"));
|
|
31
|
-
}
|
|
32
|
-
const targetPath = resolveRepoRelativePath(targetFromCli !== undefined ? args.currentWorkingDirectory : rootDir, resolvedTargetInput);
|
|
33
|
-
if (!fs.existsSync(targetPath)) {
|
|
34
|
-
return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
|
|
35
|
-
}
|
|
36
|
-
return ok({
|
|
37
|
-
rootDir,
|
|
38
|
-
targetPath: fs.canonicalPathSync(targetPath),
|
|
39
|
-
allowlist: rtlConfig.allowlist ?? [],
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Loads config and resolves the scan target for `audit links`.
|
|
44
|
-
*
|
|
45
|
-
* @remarks Defaults to the repo root rather than a configured path: a link audit that only covers one
|
|
46
|
-
* package cannot see the cross-package references that are the ones most likely to rot.
|
|
47
|
-
*
|
|
48
|
-
* @since 0.5.0
|
|
49
|
-
*/
|
|
50
|
-
export async function prepareLinkAudit(fs, args) {
|
|
51
|
-
let rootDir;
|
|
52
|
-
try {
|
|
53
|
-
rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
|
|
54
|
-
}
|
|
55
|
-
catch (caughtError) {
|
|
56
|
-
return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
|
|
57
|
-
}
|
|
58
|
-
const loadedOutcome = await loadCodefastConfig(rootDir, fs);
|
|
59
|
-
if (!loadedOutcome.ok) {
|
|
60
|
-
return loadedOutcome;
|
|
61
|
-
}
|
|
62
|
-
const linksConfig = loadedOutcome.value.config.audit?.links ?? {};
|
|
63
|
-
const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
|
|
64
|
-
if (!fs.existsSync(targetPath)) {
|
|
65
|
-
return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
|
|
66
|
-
}
|
|
67
|
-
return ok({
|
|
68
|
-
rootDir,
|
|
69
|
-
targetPath: fs.canonicalPathSync(targetPath),
|
|
70
|
-
allowlist: linksConfig.allowlist ?? [],
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Loads config and resolves the scan target for `audit imports`.
|
|
75
|
-
*
|
|
76
|
-
* @remarks Defaults to the repo root: the import policy is repo-wide, and generated or vendored
|
|
77
|
-
* trees are already excluded by the shared walk.
|
|
78
|
-
*
|
|
79
|
-
* @since 0.10.0
|
|
80
|
-
*/
|
|
81
|
-
export async function prepareImportsAudit(fs, args) {
|
|
82
|
-
let rootDir;
|
|
83
|
-
try {
|
|
84
|
-
rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
|
|
85
|
-
}
|
|
86
|
-
catch (caughtError) {
|
|
87
|
-
return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
|
|
88
|
-
}
|
|
89
|
-
const loadedOutcome = await loadCodefastConfig(rootDir, fs);
|
|
90
|
-
if (!loadedOutcome.ok) {
|
|
91
|
-
return loadedOutcome;
|
|
92
|
-
}
|
|
93
|
-
const importsConfig = loadedOutcome.value.config.audit?.imports ?? {};
|
|
94
|
-
const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
|
|
95
|
-
if (!fs.existsSync(targetPath)) {
|
|
96
|
-
return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
|
|
97
|
-
}
|
|
98
|
-
return ok({
|
|
99
|
-
rootDir,
|
|
100
|
-
targetPath: fs.canonicalPathSync(targetPath),
|
|
101
|
-
allowlist: importsConfig.allowlist ?? [],
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Loads config and resolves the scan target for `audit comments`.
|
|
106
|
-
*
|
|
107
|
-
* @remarks Defaults to the repo root: a divider convention that only holds inside one package
|
|
108
|
-
* is not a convention.
|
|
109
|
-
*
|
|
110
|
-
* @since 0.6.0
|
|
111
|
-
*/
|
|
112
|
-
export async function prepareCommentAudit(fs, args) {
|
|
113
|
-
let rootDir;
|
|
114
|
-
try {
|
|
115
|
-
rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
|
|
116
|
-
}
|
|
117
|
-
catch (caughtError) {
|
|
118
|
-
return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
|
|
119
|
-
}
|
|
120
|
-
const loadedOutcome = await loadCodefastConfig(rootDir, fs);
|
|
121
|
-
if (!loadedOutcome.ok) {
|
|
122
|
-
return loadedOutcome;
|
|
123
|
-
}
|
|
124
|
-
const commentsConfig = loadedOutcome.value.config.audit?.comments ?? {};
|
|
125
|
-
const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
|
|
126
|
-
if (!fs.existsSync(targetPath)) {
|
|
127
|
-
return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
|
|
128
|
-
}
|
|
129
|
-
return ok({
|
|
130
|
-
rootDir,
|
|
131
|
-
targetPath: fs.canonicalPathSync(targetPath),
|
|
132
|
-
allowlist: commentsConfig.allowlist ?? [],
|
|
133
|
-
});
|
|
11
|
+
export function resolveRepoRelativePath(rootDir, maybeRelative) {
|
|
12
|
+
return path.isAbsolute(maybeRelative) ? path.resolve(maybeRelative) : path.resolve(rootDir, maybeRelative);
|
|
134
13
|
}
|
|
135
14
|
/**
|
|
136
|
-
* Loads config and resolves the scan target
|
|
15
|
+
* Loads config and resolves the repo root as the scan target, taking the allowlist the caller selects.
|
|
137
16
|
*
|
|
138
|
-
* @remarks
|
|
139
|
-
* has to hold across them.
|
|
17
|
+
* @remarks Every repo-wide audit shares this prelude; only the config key its allowlist comes from differs.
|
|
140
18
|
*
|
|
141
|
-
* @since 0.
|
|
19
|
+
* @since 0.11.0
|
|
142
20
|
*/
|
|
143
|
-
export async function
|
|
21
|
+
export async function prepareRepoRootAudit(fs, args, selectAllowlist) {
|
|
144
22
|
let rootDir;
|
|
145
23
|
try {
|
|
146
24
|
rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
|
|
@@ -152,7 +30,6 @@ export async function prepareDisplayNameAudit(fs, args) {
|
|
|
152
30
|
if (!loadedOutcome.ok) {
|
|
153
31
|
return loadedOutcome;
|
|
154
32
|
}
|
|
155
|
-
const displayNamesConfig = loadedOutcome.value.config.audit?.displayNames ?? {};
|
|
156
33
|
const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
|
|
157
34
|
if (!fs.existsSync(targetPath)) {
|
|
158
35
|
return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
|
|
@@ -160,6 +37,6 @@ export async function prepareDisplayNameAudit(fs, args) {
|
|
|
160
37
|
return ok({
|
|
161
38
|
rootDir,
|
|
162
39
|
targetPath: fs.canonicalPathSync(targetPath),
|
|
163
|
-
allowlist:
|
|
40
|
+
allowlist: selectAllowlist(loadedOutcome.value.config),
|
|
164
41
|
});
|
|
165
42
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RtlAuditResult } from "#/audit/domain/types";
|
|
2
|
+
/**
|
|
3
|
+
* Exit `1` when any non-allowlisted violation remains.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.5.0-canary.6
|
|
6
|
+
*/
|
|
7
|
+
export declare function exitCodeForRtlAuditResult(result: RtlAuditResult): number;
|
|
8
|
+
/**
|
|
9
|
+
* Machine-readable RTL audit summary for `--json`.
|
|
10
|
+
*
|
|
11
|
+
* @since 0.5.0-canary.6
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatRtlAuditJsonOutput(result: RtlAuditResult, rootDir: string): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CLI_EXIT_GENERAL_ERROR, CLI_EXIT_SUCCESS } from "#/core/exit-codes";
|
|
2
|
+
/**
|
|
3
|
+
* Exit `1` when any non-allowlisted violation remains.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.5.0-canary.6
|
|
6
|
+
*/
|
|
7
|
+
export function exitCodeForRtlAuditResult(result) {
|
|
8
|
+
return result.violationCount > 0 ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Machine-readable RTL audit summary for `--json`.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.5.0-canary.6
|
|
14
|
+
*/
|
|
15
|
+
export function formatRtlAuditJsonOutput(result, rootDir) {
|
|
16
|
+
return JSON.stringify({
|
|
17
|
+
schemaVersion: 1,
|
|
18
|
+
ok: result.violationCount === 0,
|
|
19
|
+
cwd: rootDir,
|
|
20
|
+
result,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Resolved request for a single RTL audit run.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.5.0-canary.6
|
|
6
|
+
*/
|
|
7
|
+
export type RtlAuditRunRequest = {
|
|
8
|
+
readonly rootDir: string;
|
|
9
|
+
readonly targetPath: string;
|
|
10
|
+
readonly allowlist?: ReadonlyArray<string> | undefined;
|
|
11
|
+
readonly json: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Zod schema for {@link RtlAuditRunRequest}.
|
|
15
|
+
*
|
|
16
|
+
* @since 0.5.0-canary.6
|
|
17
|
+
*/
|
|
18
|
+
export declare const rtlAuditRunRequestSchema: z.ZodType<RtlAuditRunRequest>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for {@link RtlAuditRunRequest}.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.5.0-canary.6
|
|
6
|
+
*/
|
|
7
|
+
export const rtlAuditRunRequestSchema = z.object({
|
|
8
|
+
rootDir: z.string().min(1),
|
|
9
|
+
targetPath: z.string().min(1),
|
|
10
|
+
allowlist: z.array(z.string()).optional(),
|
|
11
|
+
json: z.boolean(),
|
|
12
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DIRECTION_RESOLVED_VARIANT, PHYSICAL_SIDE_VARIANT, RTL_MAPPINGS, RTL_REVERSE_MAPPINGS, RTL_SWAP_MAPPINGS, RTL_TRANSLATE_X_MAPPINGS, SLIDE_PREFIXES, } from "#/audit/domain/mappings";
|
|
2
|
-
import { collectTokens } from "#/audit/domain/tokenize";
|
|
1
|
+
import { DIRECTION_RESOLVED_VARIANT, PHYSICAL_SIDE_VARIANT, RTL_MAPPINGS, RTL_REVERSE_MAPPINGS, RTL_SWAP_MAPPINGS, RTL_TRANSLATE_X_MAPPINGS, SLIDE_PREFIXES, } from "#/audit/rtl/domain/mappings";
|
|
2
|
+
import { collectTokens } from "#/audit/rtl/domain/tokenize";
|
|
3
3
|
/**
|
|
4
4
|
* A token satisfies an "rtl companion" requirement when the same file has a
|
|
5
5
|
* token whose variants include `rtl` and whose value matches. Variant order
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { logger } from "#/core/logger";
|
|
2
|
+
/**
|
|
3
|
+
* Human-readable RTL audit report (matches the former packages/ui script shape).
|
|
4
|
+
*
|
|
5
|
+
* @since 0.5.0-canary.6
|
|
6
|
+
*/
|
|
7
|
+
export function presentRtlAuditResult(result) {
|
|
8
|
+
for (const file of result.files) {
|
|
9
|
+
logger.out(`\n${file.relativePath}`);
|
|
10
|
+
for (const { line, raw, suggestion } of file.violations) {
|
|
11
|
+
logger.out(` ${line}: ${raw} → ${suggestion}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const allowlistSuffix = result.allowlistedCount > 0 ? ` (${result.allowlistedCount} allowlisted)` : "";
|
|
15
|
+
if (result.violationCount > 0) {
|
|
16
|
+
logger.out(`\n✖ ${result.violationCount} RTL violation(s)${allowlistSuffix}`);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
logger.out(`✓ No physical-direction classes outside the allowlist${allowlistSuffix}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AuditCommandPrelude } from "#/audit/prepare";
|
|
2
|
+
import { AppError } from "#/core/errors";
|
|
3
|
+
import type { Filesystem } from "#/core/filesystem/filesystem";
|
|
4
|
+
import type { Result } from "#/core/result";
|
|
5
|
+
/**
|
|
6
|
+
* Loads config and resolves the scan target for `audit rtl`.
|
|
7
|
+
*
|
|
8
|
+
* @since 0.5.0-canary.6
|
|
9
|
+
*/
|
|
10
|
+
export declare function prepareRtlAudit(fs: Filesystem, args: {
|
|
11
|
+
readonly currentWorkingDirectory: string;
|
|
12
|
+
readonly rawTarget: string | undefined;
|
|
13
|
+
}): Promise<Result<AuditCommandPrelude, AppError>>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { resolveRepoRelativePath } from "#/audit/prepare";
|
|
2
|
+
import { loadCodefastConfig } from "#/core/config";
|
|
3
|
+
import { AppError, messageFrom } from "#/core/errors";
|
|
4
|
+
import { err, ok } from "#/core/result";
|
|
5
|
+
import { resolveProjectRoot } from "#/core/workspace/resolver";
|
|
6
|
+
/**
|
|
7
|
+
* Loads config and resolves the scan target for `audit rtl`.
|
|
8
|
+
*
|
|
9
|
+
* @since 0.5.0-canary.6
|
|
10
|
+
*/
|
|
11
|
+
export async function prepareRtlAudit(fs, args) {
|
|
12
|
+
let rootDir;
|
|
13
|
+
try {
|
|
14
|
+
// Realpath so allowlist keys (`path.relative(rootDir, file)`) stay stable when cwd is a symlink.
|
|
15
|
+
rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
|
|
16
|
+
}
|
|
17
|
+
catch (caughtError) {
|
|
18
|
+
return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
|
|
19
|
+
}
|
|
20
|
+
const loadedOutcome = await loadCodefastConfig(rootDir, fs);
|
|
21
|
+
if (!loadedOutcome.ok) {
|
|
22
|
+
return loadedOutcome;
|
|
23
|
+
}
|
|
24
|
+
const { config } = loadedOutcome.value;
|
|
25
|
+
const rtlConfig = config.audit?.rtl ?? {};
|
|
26
|
+
const targetFromCli = args.rawTarget;
|
|
27
|
+
const targetFromConfig = rtlConfig.target;
|
|
28
|
+
const resolvedTargetInput = targetFromCli ?? targetFromConfig;
|
|
29
|
+
if (resolvedTargetInput === undefined) {
|
|
30
|
+
return err(new AppError("VALIDATION_ERROR", "Missing scan target: pass a path argument or set audit.rtl.target in codefast.config"));
|
|
31
|
+
}
|
|
32
|
+
const targetPath = resolveRepoRelativePath(targetFromCli !== undefined ? args.currentWorkingDirectory : rootDir, resolvedTargetInput);
|
|
33
|
+
if (!fs.existsSync(targetPath)) {
|
|
34
|
+
return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
|
|
35
|
+
}
|
|
36
|
+
return ok({
|
|
37
|
+
rootDir,
|
|
38
|
+
targetPath: fs.canonicalPathSync(targetPath),
|
|
39
|
+
allowlist: rtlConfig.allowlist ?? [],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { RtlAuditResult } from "#/audit/domain/types";
|
|
2
2
|
import { AppError } from "#/core/errors";
|
|
3
|
-
import type {
|
|
3
|
+
import type { Filesystem } from "#/core/filesystem/filesystem";
|
|
4
4
|
import type { Result } from "#/core/result";
|
|
5
5
|
/**
|
|
6
6
|
* Scans a target path for physical-direction Tailwind class violations.
|
|
7
7
|
*
|
|
8
8
|
* @since 0.5.0-canary.6
|
|
9
9
|
*/
|
|
10
|
-
export declare function runRtlAudit(fs:
|
|
10
|
+
export declare function runRtlAudit(fs: Filesystem, args: {
|
|
11
11
|
readonly rootDir: string;
|
|
12
12
|
readonly targetPath: string;
|
|
13
13
|
readonly allowlist: ReadonlyArray<string>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { auditFileContent } from "#/audit/domain/audit-file";
|
|
2
|
+
import { auditFileContent } from "#/audit/rtl/domain/audit-file";
|
|
3
3
|
import { AppError, messageFrom } from "#/core/errors";
|
|
4
4
|
import { err, ok } from "#/core/result";
|
|
5
5
|
import { walkTsxFiles } from "#/core/workspace/typescript-walk";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { ZodType } from "zod";
|
|
3
|
+
import type { GlobalCliOptions } from "#/core/cli/global-options";
|
|
4
|
+
import type { AppError } from "#/core/errors";
|
|
5
|
+
import type { Filesystem } from "#/core/filesystem/filesystem";
|
|
6
|
+
import type { Result } from "#/core/result";
|
|
7
|
+
/**
|
|
8
|
+
* The options every command shares: the `--json` machine-output flag. Per-command option shapes extend this.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.11.0
|
|
11
|
+
*/
|
|
12
|
+
export interface BaseCommandOptions {
|
|
13
|
+
readonly json?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A pipeline's `prepare` slot: resolves the argv context (cwd, positional, global options) into the command's prelude.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.11.0
|
|
19
|
+
*/
|
|
20
|
+
export type CommandPrepare<Prelude> = (fs: Filesystem, input: {
|
|
21
|
+
readonly currentWorkingDirectory: string;
|
|
22
|
+
readonly rawArg: string | undefined;
|
|
23
|
+
readonly globals: GlobalCliOptions;
|
|
24
|
+
}) => Promise<Result<Prelude, AppError>>;
|
|
25
|
+
/** The prelude and options a request is built from, before the run. */
|
|
26
|
+
interface BuildInput<Prelude, Opts> {
|
|
27
|
+
readonly prelude: Prelude;
|
|
28
|
+
readonly opts: Opts;
|
|
29
|
+
readonly globals: GlobalCliOptions;
|
|
30
|
+
}
|
|
31
|
+
/** The run result and its surrounding context, for the report phase. */
|
|
32
|
+
interface ReportInput<Prelude, RunResult, Presenter, Opts> {
|
|
33
|
+
readonly result: RunResult;
|
|
34
|
+
readonly prelude: Prelude;
|
|
35
|
+
readonly opts: Opts;
|
|
36
|
+
readonly presenter: Presenter | undefined;
|
|
37
|
+
readonly elapsedSeconds: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The invariant shape every workspace command and subcommand shares: resolve a prelude, build and
|
|
41
|
+
* validate a request, run it, then report to one of two audiences and record a single exit code.
|
|
42
|
+
*
|
|
43
|
+
* @remarks The optional slots carry the per-command variations — a working-tree guard and a streaming
|
|
44
|
+
* presenter — so the control flow itself is written once.
|
|
45
|
+
*
|
|
46
|
+
* @since 0.11.0
|
|
47
|
+
*/
|
|
48
|
+
export interface CommandPipeline<Prelude, Request, RunResult, Presenter = never, Opts extends BaseCommandOptions = BaseCommandOptions> {
|
|
49
|
+
readonly name?: string | undefined;
|
|
50
|
+
readonly description?: string | undefined;
|
|
51
|
+
readonly positional?: {
|
|
52
|
+
readonly name: string;
|
|
53
|
+
readonly help: string;
|
|
54
|
+
} | undefined;
|
|
55
|
+
readonly jsonHelp?: string | undefined;
|
|
56
|
+
readonly configureArgv?: ((command: Command) => void) | undefined;
|
|
57
|
+
readonly prepare: CommandPrepare<Prelude>;
|
|
58
|
+
readonly guard?: ((input: {
|
|
59
|
+
readonly prelude: Prelude;
|
|
60
|
+
readonly opts: Opts;
|
|
61
|
+
}) => Promise<Result<void, AppError>>) | undefined;
|
|
62
|
+
readonly schema: ZodType<Request>;
|
|
63
|
+
readonly buildRequest: (input: BuildInput<Prelude, Opts>) => unknown;
|
|
64
|
+
readonly createPresenter?: ((input: BuildInput<Prelude, Opts>) => Presenter) | undefined;
|
|
65
|
+
readonly run: (fs: Filesystem, request: Request, presenter: Presenter | undefined) => Promise<Result<RunResult, AppError>>;
|
|
66
|
+
readonly presentHuman?: ((input: ReportInput<Prelude, RunResult, Presenter, Opts>) => void) | undefined;
|
|
67
|
+
readonly formatJson: (input: ReportInput<Prelude, RunResult, Presenter, Opts>) => string;
|
|
68
|
+
readonly exitCode: (result: RunResult) => number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A pipeline that names itself, so it can register as a subcommand under a parent command.
|
|
72
|
+
*
|
|
73
|
+
* @since 0.11.0
|
|
74
|
+
*/
|
|
75
|
+
export type NamedCommandPipeline<Prelude, Request, RunResult, Presenter = never, Opts extends BaseCommandOptions = BaseCommandOptions> = CommandPipeline<Prelude, Request, RunResult, Presenter, Opts> & {
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly description: string;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Wires a pipeline onto a named Command: the shared positional, `--json`, any extra options, and the action.
|
|
81
|
+
*
|
|
82
|
+
* @since 0.11.0
|
|
83
|
+
*/
|
|
84
|
+
export declare function applyCommandPipeline<Prelude, Request, RunResult, Presenter, Opts extends BaseCommandOptions>(command: Command, fs: Filesystem, pipeline: CommandPipeline<Prelude, Request, RunResult, Presenter, Opts>): Command;
|
|
85
|
+
/**
|
|
86
|
+
* Registers a pipeline as a subcommand under `parent`, taking its name and description from the pipeline.
|
|
87
|
+
*
|
|
88
|
+
* @since 0.11.0
|
|
89
|
+
*/
|
|
90
|
+
export declare function registerPipelineSubcommand<Prelude, Request, RunResult, Presenter, Opts extends BaseCommandOptions>(parent: Command, fs: Filesystem, pipeline: NamedCommandPipeline<Prelude, Request, RunResult, Presenter, Opts>): void;
|
|
91
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { globalCliCommanderOptionsSchema } from "#/core/cli/global-options";
|
|
3
|
+
import { readOptionalPositionalArg } from "#/core/cli/positional";
|
|
4
|
+
import { consumeCliAppError } from "#/core/cli/result-handle";
|
|
5
|
+
import { logger } from "#/core/logger";
|
|
6
|
+
import { parseWithSchema } from "#/core/schema-parse";
|
|
7
|
+
const DEFAULT_JSON_HELP = "Print one JSON summary on stdout (suppresses human progress)";
|
|
8
|
+
function readGlobalOptions(command) {
|
|
9
|
+
return (command.optsWithGlobals?.() ?? command.opts());
|
|
10
|
+
}
|
|
11
|
+
function makeAction(fs, pipeline) {
|
|
12
|
+
return async (rawArg, opts, command) => {
|
|
13
|
+
const globalsOutcome = parseWithSchema(globalCliCommanderOptionsSchema, readGlobalOptions(command));
|
|
14
|
+
if (!consumeCliAppError(globalsOutcome)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const globals = globalsOutcome.value;
|
|
18
|
+
const prelude = await pipeline.prepare(fs, {
|
|
19
|
+
currentWorkingDirectory: process.cwd(),
|
|
20
|
+
rawArg: readOptionalPositionalArg(rawArg),
|
|
21
|
+
globals,
|
|
22
|
+
});
|
|
23
|
+
if (!consumeCliAppError(prelude)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (pipeline.guard) {
|
|
27
|
+
const guarded = await pipeline.guard({ prelude: prelude.value, opts });
|
|
28
|
+
if (!consumeCliAppError(guarded)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const parsed = parseWithSchema(pipeline.schema, pipeline.buildRequest({ prelude: prelude.value, opts, globals }));
|
|
33
|
+
if (!consumeCliAppError(parsed)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const json = !!opts.json;
|
|
37
|
+
const presenter = !json && pipeline.createPresenter
|
|
38
|
+
? pipeline.createPresenter({ prelude: prelude.value, opts, globals })
|
|
39
|
+
: undefined;
|
|
40
|
+
const startedAt = performance.now();
|
|
41
|
+
const outcome = await pipeline.run(fs, parsed.value, presenter);
|
|
42
|
+
if (!consumeCliAppError(outcome)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const elapsedSeconds = (performance.now() - startedAt) / 1000;
|
|
46
|
+
const report = {
|
|
47
|
+
result: outcome.value,
|
|
48
|
+
prelude: prelude.value,
|
|
49
|
+
opts,
|
|
50
|
+
presenter,
|
|
51
|
+
elapsedSeconds,
|
|
52
|
+
};
|
|
53
|
+
if (json) {
|
|
54
|
+
logger.out(pipeline.formatJson(report));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
pipeline.presentHuman?.(report);
|
|
58
|
+
}
|
|
59
|
+
process.exitCode = pipeline.exitCode(outcome.value);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Wires a pipeline onto a named Command: the shared positional, `--json`, any extra options, and the action.
|
|
64
|
+
*
|
|
65
|
+
* @since 0.11.0
|
|
66
|
+
*/
|
|
67
|
+
export function applyCommandPipeline(command, fs, pipeline) {
|
|
68
|
+
if (pipeline.positional) {
|
|
69
|
+
command.argument(pipeline.positional.name, pipeline.positional.help);
|
|
70
|
+
}
|
|
71
|
+
command.option("--json", pipeline.jsonHelp ?? DEFAULT_JSON_HELP, false);
|
|
72
|
+
pipeline.configureArgv?.(command);
|
|
73
|
+
command.action(makeAction(fs, pipeline));
|
|
74
|
+
return command;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Registers a pipeline as a subcommand under `parent`, taking its name and description from the pipeline.
|
|
78
|
+
*
|
|
79
|
+
* @since 0.11.0
|
|
80
|
+
*/
|
|
81
|
+
export function registerPipelineSubcommand(parent, fs, pipeline) {
|
|
82
|
+
applyCommandPipeline(parent.command(pipeline.name).description(pipeline.description), fs, pipeline);
|
|
83
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AppError } from "#/core/errors";
|
|
2
|
+
import type { Filesystem } from "#/core/filesystem/filesystem";
|
|
3
|
+
import type { Result } from "#/core/result";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the project root as a `Result`, mapping a resolution failure to an `INFRA_FAILURE` error.
|
|
6
|
+
*
|
|
7
|
+
* @since 0.11.0
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveProjectRootResult(fs: Filesystem, currentWorkingDirectory: string): Result<string, AppError>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AppError, messageFrom } from "#/core/errors";
|
|
2
|
+
import { err, ok } from "#/core/result";
|
|
3
|
+
import { resolveProjectRoot } from "#/core/workspace/resolver";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the project root as a `Result`, mapping a resolution failure to an `INFRA_FAILURE` error.
|
|
6
|
+
*
|
|
7
|
+
* @since 0.11.0
|
|
8
|
+
*/
|
|
9
|
+
export function resolveProjectRootResult(fs, currentWorkingDirectory) {
|
|
10
|
+
try {
|
|
11
|
+
return ok(resolveProjectRoot(currentWorkingDirectory, fs).rootDir);
|
|
12
|
+
}
|
|
13
|
+
catch (caughtError) {
|
|
14
|
+
return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
|
|
15
|
+
}
|
|
16
|
+
}
|