@effected/cli 0.27.0 → 0.29.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/bin/effected.js +9 -2
- package/ci.js +16 -0
- package/commands/action/build.js +9 -0
- package/commands/action/errors.js +54 -0
- package/commands/action/execute-init.js +44 -0
- package/commands/action/execute.js +47 -0
- package/commands/action/flags.js +19 -0
- package/commands/action/index.js +9 -0
- package/commands/action/init.js +9 -0
- package/commands/package/build.js +9 -0
- package/commands/package/errors.js +38 -0
- package/commands/package/execute.js +52 -0
- package/commands/package/flags.js +12 -0
- package/commands/package/index.js +8 -0
- package/commands/root.js +7 -1
- package/commands/schema/execute.js +1 -1
- package/commands/schema/validate.js +1 -1
- package/index.d.ts +117 -3
- package/index.js +3 -1
- package/main.d.ts +25 -3
- package/main.js +5 -1
- package/package.json +9 -5
- package/render/ActionDoc.js +66 -0
- package/render/InitDoc.js +35 -0
- package/render/IssuesDoc.js +73 -0
- package/version.js +1 -1
package/bin/effected.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { main } from "../main.js";
|
|
3
|
-
|
|
4
2
|
// src/bin.ts
|
|
3
|
+
if (import.meta.url.endsWith(".ts")) {
|
|
4
|
+
const { register } = await import([
|
|
5
|
+
"tsx",
|
|
6
|
+
"esm",
|
|
7
|
+
"api"
|
|
8
|
+
].join("/"));
|
|
9
|
+
register();
|
|
10
|
+
}
|
|
11
|
+
const { main } = await import("../main.js");
|
|
5
12
|
main();
|
|
6
13
|
|
|
7
14
|
export { };
|
package/ci.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
2
|
+
import { RuntimeEnv } from "@effected/env";
|
|
3
|
+
|
|
4
|
+
// src/ci.ts
|
|
5
|
+
/**
|
|
6
|
+
* Whether the process runs under CI, by `@effected/env`'s one rule: a known
|
|
7
|
+
* provider's marker, or a truthy `CI` or `CONTINUOUS_INTEGRATION`; `CI=false`
|
|
8
|
+
* is not CI. Its own module so `main` and its test call the same expression,
|
|
9
|
+
* and so `main` never reaches `@effected/engine/build`, which loads the
|
|
10
|
+
* bundler and its toolchain.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
const isCI = (env) => Option.isSome(RuntimeEnv.fromRecord(env).ci);
|
|
15
|
+
|
|
16
|
+
export { isCI };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { execute } from "./execute.js";
|
|
2
|
+
import { buildFlags } from "./flags.js";
|
|
3
|
+
import { Command } from "effect/cli";
|
|
4
|
+
|
|
5
|
+
// src/commands/action/build.ts
|
|
6
|
+
/** `effected action build`: build the GitHub Action in the working directory into its `dist`. */
|
|
7
|
+
const makeBuildCommand = (deps) => Command.make("build", buildFlags, (input) => execute(input, deps)).pipe(Command.withDescription("Build the GitHub Action in the working directory from the [action] table of its effected.toml into dist"));
|
|
8
|
+
|
|
9
|
+
export { makeBuildCommand };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
// src/commands/action/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Validation stopped the build or an entry failed. Exit `1`.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
var ActionBuildFailedError = class extends Schema.TaggedError()("ActionBuildFailedError", {
|
|
10
|
+
/** How many validation errors there were. */
|
|
11
|
+
errors: Schema.Number,
|
|
12
|
+
/** How many entries failed to bundle or load. */
|
|
13
|
+
failedEntries: Schema.Number
|
|
14
|
+
}) {
|
|
15
|
+
get message() {
|
|
16
|
+
const parts = [...this.errors > 0 ? [`${this.errors} validation error(s)`] : [], ...this.failedEntries > 0 ? [`${this.failedEntries} failed entry(ies)`] : []];
|
|
17
|
+
return parts.length > 0 ? `The action build failed with ${parts.join(" and ")}.` : "The action build failed: warnings are errors in strict mode.";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The build could not start: its `effected.toml` would not load, it has no `[action]` table, or the directory is not a
|
|
22
|
+
* package. Exit `2`: exit `1` means it ran and found a problem.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* Carries the engine's message. This command's own type rather than the engine's, because the engine's build entry is
|
|
26
|
+
* loaded only when the command runs.
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
var ActionConfigError = class extends Schema.TaggedError()("ActionConfigError", { message: Schema.String }) {};
|
|
31
|
+
/**
|
|
32
|
+
* The build could not finish: an entry is missing with validation off, or a file could not be read, cleaned, written or
|
|
33
|
+
* copied. `action init` fails with it too when a write fails after its checks, naming the files already written. Exit
|
|
34
|
+
* `3`, the infrastructure tier.
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* Carries the engine's message, as {@link ActionConfigError} does.
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
var ActionRunError = class extends Schema.TaggedError()("ActionRunError", { message: Schema.String }) {};
|
|
42
|
+
/**
|
|
43
|
+
* `action init` could not start: the directory is not a package, the action's name or description was found nowhere
|
|
44
|
+
* or holds a control character, files it would write exist and `--force` was not given, or a path cannot be written
|
|
45
|
+
* over (a symbolic link, the wrong kind of entry), which `--force` never overrides. Exit `2`, and nothing was written.
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* Carries the engine's message, as {@link ActionConfigError} does.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
var ActionInitError = class extends Schema.TaggedError()("ActionInitError", { message: Schema.String }) {};
|
|
53
|
+
|
|
54
|
+
export { ActionBuildFailedError, ActionConfigError, ActionInitError, ActionRunError };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { InitDoc } from "../../render/InitDoc.js";
|
|
2
|
+
import { resolveFormat } from "../schema/execute.js";
|
|
3
|
+
import { ActionInitError, ActionRunError } from "./errors.js";
|
|
4
|
+
import { Console, Effect, Option, Schema } from "effect";
|
|
5
|
+
import { CliRuntime, Doc } from "@effected/cli-kit";
|
|
6
|
+
|
|
7
|
+
// src/commands/action/execute-init.ts
|
|
8
|
+
/**
|
|
9
|
+
* Run one `action init`.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* The engine's build entry is imported here, as `action build` imports it, so `effected --version` never loads it.
|
|
13
|
+
*/
|
|
14
|
+
const executeInit = Effect.fn("effected.action.init")(function* (input, deps) {
|
|
15
|
+
const format = yield* resolveFormat(input.format);
|
|
16
|
+
const { ActionInit, ActionInitResult } = yield* Effect.promise(() => import("@effected/engine/build"));
|
|
17
|
+
const refused = (error) => Effect.fail(CliRuntime.reported(new ActionInitError({ message: error.message }), 2));
|
|
18
|
+
const stopped = (error) => Effect.fail(CliRuntime.reported(new ActionRunError({ message: error.message }), 3));
|
|
19
|
+
const answer = yield* ActionInit.run({
|
|
20
|
+
...Option.match(input.name, {
|
|
21
|
+
onNone: () => ({}),
|
|
22
|
+
onSome: (name) => ({ name })
|
|
23
|
+
}),
|
|
24
|
+
...Option.match(input.description, {
|
|
25
|
+
onNone: () => ({}),
|
|
26
|
+
onSome: (description) => ({ description })
|
|
27
|
+
}),
|
|
28
|
+
force: input.force
|
|
29
|
+
}, { cwd: deps.cwd }).pipe(Effect.catchTags({
|
|
30
|
+
NotAPackageError: refused,
|
|
31
|
+
ActionMetadataMissingError: refused,
|
|
32
|
+
ActionMetadataInvalidError: refused,
|
|
33
|
+
ScaffoldConflictError: refused,
|
|
34
|
+
ScaffoldExistsError: (error) => Effect.fail(CliRuntime.reported(new ActionInitError({ message: `${error.message}: pass --force to replace them` }), 2)),
|
|
35
|
+
ScaffoldWriteError: stopped,
|
|
36
|
+
PlatformError: stopped
|
|
37
|
+
}));
|
|
38
|
+
if (format === "json") {
|
|
39
|
+
yield* Console.log(JSON.stringify(Schema.encodeSync(ActionInitResult)(answer)));
|
|
40
|
+
for (const line of InitDoc.lines(answer)) yield* Effect.logInfo(line);
|
|
41
|
+
} else yield* Doc.print([InitDoc.render(answer)]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export { executeInit };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ActionDoc } from "../../render/ActionDoc.js";
|
|
2
|
+
import { resolveFormat } from "../schema/execute.js";
|
|
3
|
+
import { ActionBuildFailedError, ActionConfigError, ActionRunError } from "./errors.js";
|
|
4
|
+
import { Console, Effect, Schema } from "effect";
|
|
5
|
+
import { CliRuntime, Doc } from "@effected/cli-kit";
|
|
6
|
+
import { StepSummary } from "@effected/engine/schema";
|
|
7
|
+
|
|
8
|
+
// src/commands/action/execute.ts
|
|
9
|
+
/**
|
|
10
|
+
* Run one `action build`.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* The engine's build entry is imported here rather than at the top of the module: it loads the bundler and its
|
|
14
|
+
* toolchain, which `effected --version` and every `effected schema` run have no use for. The report is emitted first and
|
|
15
|
+
* the failure follows, so a run that fails has still said why.
|
|
16
|
+
*/
|
|
17
|
+
const execute = Effect.fn("effected.action.execute")(function* (input, deps) {
|
|
18
|
+
const format = yield* resolveFormat(input.format);
|
|
19
|
+
const { ActionRun, ActionRunResult } = yield* Effect.promise(() => import("@effected/engine/build"));
|
|
20
|
+
const refused = (error) => Effect.fail(CliRuntime.reported(new ActionConfigError({ message: error.message }), 2));
|
|
21
|
+
const stopped = (error) => Effect.fail(CliRuntime.reported(new ActionRunError({ message: error.message }), 3));
|
|
22
|
+
const answer = yield* ActionRun.run({
|
|
23
|
+
validate: !input.noValidate,
|
|
24
|
+
persist: !input.noPersist
|
|
25
|
+
}, deps).pipe(Effect.catchTags({
|
|
26
|
+
EffectedConfigError: refused,
|
|
27
|
+
NotAPackageError: refused,
|
|
28
|
+
NotAnActionError: refused,
|
|
29
|
+
BundleFailed: stopped,
|
|
30
|
+
CleanError: stopped,
|
|
31
|
+
WriteError: stopped,
|
|
32
|
+
PersistLocalError: stopped,
|
|
33
|
+
ActionYmlPathError: stopped,
|
|
34
|
+
PlatformError: stopped
|
|
35
|
+
}));
|
|
36
|
+
if (format === "json") {
|
|
37
|
+
yield* Console.log(JSON.stringify(Schema.encodeSync(ActionRunResult)(answer)));
|
|
38
|
+
for (const line of ActionDoc.lines(answer.result)) yield* Effect.logInfo(line);
|
|
39
|
+
} else yield* Doc.print([ActionDoc.render(answer.result)]);
|
|
40
|
+
yield* StepSummary.append(ActionDoc.markdown(answer.result));
|
|
41
|
+
if (answer.verdict === "failed") return yield* Effect.fail(CliRuntime.reported(new ActionBuildFailedError({
|
|
42
|
+
errors: answer.result.validation.errors.length,
|
|
43
|
+
failedEntries: answer.result.build.entries.filter((entry) => !entry.success).length
|
|
44
|
+
}), 1));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export { execute };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { formatFlag } from "../schema/flags.js";
|
|
2
|
+
import { Flag } from "effect/cli";
|
|
3
|
+
|
|
4
|
+
// src/commands/action/flags.ts
|
|
5
|
+
/** What `action build` takes. */
|
|
6
|
+
const buildFlags = {
|
|
7
|
+
format: formatFlag,
|
|
8
|
+
noValidate: Flag.Boolean("no-validate").pipe(Flag.withDefault(false), Flag.withDescription("Skip validating the config and action.yml, and the bundle size check")),
|
|
9
|
+
noPersist: Flag.Boolean("no-persist").pipe(Flag.withDefault(false), Flag.withDescription("Skip copying the build to the local action directory"))
|
|
10
|
+
};
|
|
11
|
+
/** What `action init` takes. A name or description left out is read from `package.json`. */
|
|
12
|
+
const initFlags = {
|
|
13
|
+
format: formatFlag,
|
|
14
|
+
name: Flag.String("name").pipe(Flag.optional, Flag.withDescription("The action's display name in action.yml; omitted, package.json's name without its scope")),
|
|
15
|
+
description: Flag.String("description").pipe(Flag.optional, Flag.withDescription("What the action does, for action.yml; omitted, package.json's description")),
|
|
16
|
+
force: Flag.Boolean("force").pipe(Flag.withDefault(false), Flag.withDescription("Replace files that already exist; without it, any existing file stops the run before a write"))
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export { buildFlags, initFlags };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { makeBuildCommand } from "./build.js";
|
|
2
|
+
import { makeInitCommand } from "./init.js";
|
|
3
|
+
import { Command } from "effect/cli";
|
|
4
|
+
|
|
5
|
+
// src/commands/action/index.ts
|
|
6
|
+
/** `effected action`: the group, closed over the process boundary it runs against. */
|
|
7
|
+
const makeActionCommand = (deps) => Command.make("action", {}).pipe(Command.withDescription("Scaffold or build the GitHub Action in the working directory, from its effected.toml"), Command.withSubcommands([makeBuildCommand(deps), makeInitCommand(deps)]));
|
|
8
|
+
|
|
9
|
+
export { makeActionCommand };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { executeInit } from "./execute-init.js";
|
|
2
|
+
import { initFlags } from "./flags.js";
|
|
3
|
+
import { Command } from "effect/cli";
|
|
4
|
+
|
|
5
|
+
// src/commands/action/init.ts
|
|
6
|
+
/** `effected action init`: scaffold a new GitHub Action in the working directory. */
|
|
7
|
+
const makeInitCommand = (deps) => Command.make("init", initFlags, (input) => executeInit(input, deps)).pipe(Command.withDescription("Scaffold a new GitHub Action in the working directory: the [action] table of effected.toml, action.yml, src/main.ts, src/program.ts and tsconfig.json"));
|
|
8
|
+
|
|
9
|
+
export { makeInitCommand };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { execute } from "./execute.js";
|
|
2
|
+
import { buildFlags } from "./flags.js";
|
|
3
|
+
import { Command } from "effect/cli";
|
|
4
|
+
|
|
5
|
+
// src/commands/package/build.ts
|
|
6
|
+
/** `effected package build`: build the package in the working directory into its `dist/prod`. */
|
|
7
|
+
const makeBuildCommand = (deps) => Command.make("build", buildFlags, (input) => execute(input, deps)).pipe(Command.withDescription("Build the package in the working directory into dist/prod: JS, declarations, the manifest, the API model and any binary"));
|
|
8
|
+
|
|
9
|
+
export { makeBuildCommand };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
// src/commands/package/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* The build crashed or recorded an error. Exit `1`.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
var BuildFailedError = class extends Schema.TaggedError()("BuildFailedError", {
|
|
10
|
+
/** How many errors the build recorded. */
|
|
11
|
+
errors: Schema.Number,
|
|
12
|
+
/** What ended the build, when it did not reach its end. */
|
|
13
|
+
failure: Schema.optionalKey(Schema.String)
|
|
14
|
+
}) {
|
|
15
|
+
get message() {
|
|
16
|
+
const count = `${this.errors} error(s)`;
|
|
17
|
+
return this.failure !== void 0 ? `The build failed with ${count}: ${this.failure}` : `The build recorded ${count}.`;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The build could not run: its `effected.toml` or a plugin would not load, the directory is not a package, or the
|
|
22
|
+
* bundler refused the config before any work. Exit `2`: exit `1` means it ran and found a problem.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* Carries the engine's message, which names the file at fault and the key within it. This command's own type rather
|
|
26
|
+
* than the bundler's, because the CLI reaches the bundler only through the engine.
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
var PackageConfigError = class extends Schema.TaggedError()("PackageConfigError", { message: Schema.String }) {};
|
|
31
|
+
/**
|
|
32
|
+
* A file the build needs could not be read or written. Exit `3`, the infrastructure tier.
|
|
33
|
+
*
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
var PackageRunError = class extends Schema.TaggedError()("PackageRunError", { message: Schema.String }) {};
|
|
37
|
+
|
|
38
|
+
export { BuildFailedError, PackageConfigError, PackageRunError };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { IssuesDoc } from "../../render/IssuesDoc.js";
|
|
2
|
+
import { resolveFormat } from "../schema/execute.js";
|
|
3
|
+
import { BuildFailedError, PackageConfigError, PackageRunError } from "./errors.js";
|
|
4
|
+
import { Console, Effect, Schema } from "effect";
|
|
5
|
+
import { CliRuntime, Doc } from "@effected/cli-kit";
|
|
6
|
+
import { StepSummary } from "@effected/engine/schema";
|
|
7
|
+
import { Audience } from "@effected/env";
|
|
8
|
+
|
|
9
|
+
// src/commands/package/execute.ts
|
|
10
|
+
/**
|
|
11
|
+
* Run one `package build`.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* The engine's build entry is imported here rather than at the top of the module: it loads the bundler and its
|
|
15
|
+
* toolchain, which `effected --version` and every `effected schema` run have no use for. The report is emitted first and
|
|
16
|
+
* the failure follows, so a run that fails has still said why.
|
|
17
|
+
*
|
|
18
|
+
* With `--format json`, stdout holds one document, so CI's annotations go to stderr instead, where the runner reads
|
|
19
|
+
* workflow commands too.
|
|
20
|
+
*/
|
|
21
|
+
const execute = Effect.fn("effected.package.execute")(function* (input, deps) {
|
|
22
|
+
const format = yield* resolveFormat(input.format);
|
|
23
|
+
const audience = yield* Audience;
|
|
24
|
+
const { BuildRun, BuildRunResult, renderCiAnnotations } = yield* Effect.promise(() => import("@effected/engine/build"));
|
|
25
|
+
const refused = (error) => Effect.fail(CliRuntime.reported(new PackageConfigError({ message: error.message }), 2));
|
|
26
|
+
const result = yield* BuildRun.run({
|
|
27
|
+
noExe: input.noExe,
|
|
28
|
+
verbose: input.verbose
|
|
29
|
+
}, deps).pipe(Effect.catchTags({
|
|
30
|
+
EffectedConfigError: refused,
|
|
31
|
+
PluginLoadError: refused,
|
|
32
|
+
NotAPackageError: refused,
|
|
33
|
+
ConfigValidationError: refused,
|
|
34
|
+
PlatformError: (error) => Effect.fail(CliRuntime.reported(new PackageRunError({ message: error.message }), 3))
|
|
35
|
+
}));
|
|
36
|
+
const { issues } = result;
|
|
37
|
+
if (format === "json") {
|
|
38
|
+
yield* Console.log(JSON.stringify(Schema.encodeSync(BuildRunResult)(result)));
|
|
39
|
+
for (const line of IssuesDoc.lines(issues)) yield* Effect.logInfo(line);
|
|
40
|
+
} else yield* Doc.print([IssuesDoc.render(issues)]);
|
|
41
|
+
if (audience.kind === "ci") {
|
|
42
|
+
const write = format === "json" ? Console.error : Console.log;
|
|
43
|
+
for (const line of renderCiAnnotations(result.report)) yield* write(line);
|
|
44
|
+
}
|
|
45
|
+
yield* StepSummary.append(IssuesDoc.markdown(issues));
|
|
46
|
+
if (result.verdict === "failed") return yield* Effect.fail(CliRuntime.reported(new BuildFailedError({
|
|
47
|
+
errors: issues.errors.length,
|
|
48
|
+
...issues.failure !== void 0 ? { failure: issues.failure.message } : {}
|
|
49
|
+
}), 1));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export { execute };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { formatFlag } from "../schema/flags.js";
|
|
2
|
+
import { Flag } from "effect/cli";
|
|
3
|
+
|
|
4
|
+
// src/commands/package/flags.ts
|
|
5
|
+
/** What `package build` takes. */
|
|
6
|
+
const buildFlags = {
|
|
7
|
+
format: formatFlag,
|
|
8
|
+
noExe: Flag.Boolean("no-exe").pipe(Flag.withDefault(false), Flag.withDescription("Skip the single-executable compile; the manifest still points at the binary")),
|
|
9
|
+
verbose: Flag.Boolean("verbose").pipe(Flag.withDefault(false), Flag.withDescription("Measure gzip sizes for the report"))
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { buildFlags };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { makeBuildCommand } from "./build.js";
|
|
2
|
+
import { Command } from "effect/cli";
|
|
3
|
+
|
|
4
|
+
// src/commands/package/index.ts
|
|
5
|
+
/** `effected package`: the group, closed over the process boundary it runs against. */
|
|
6
|
+
const makePackageCommand = (deps) => Command.make("package", {}).pipe(Command.withDescription("Build the package in the working directory from its effected.toml"), Command.withSubcommands([makeBuildCommand(deps)]));
|
|
7
|
+
|
|
8
|
+
export { makePackageCommand };
|
package/commands/root.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { makeActionCommand } from "./action/index.js";
|
|
2
|
+
import { makePackageCommand } from "./package/index.js";
|
|
1
3
|
import { makeSchemaCommand } from "./schema/index.js";
|
|
2
4
|
import { CliAudience } from "@effected/cli-kit";
|
|
3
5
|
import { Command } from "effect/cli";
|
|
@@ -9,6 +11,10 @@ import { Command } from "effect/cli";
|
|
|
9
11
|
*
|
|
10
12
|
* @public
|
|
11
13
|
*/
|
|
12
|
-
const makeRootCommand = (deps) => Command.make("effected", {}).pipe(Command.withDescription("The effected tool: a toolchain for Effect v4 applications."), Command.withSharedFlags(CliAudience.flags()), Command.withSubcommands([
|
|
14
|
+
const makeRootCommand = (deps) => Command.make("effected", {}).pipe(Command.withDescription("The effected tool: a toolchain for Effect v4 applications."), Command.withSharedFlags(CliAudience.flags()), Command.withSubcommands([
|
|
15
|
+
makeSchemaCommand(deps),
|
|
16
|
+
makePackageCommand(deps),
|
|
17
|
+
makeActionCommand(deps)
|
|
18
|
+
]));
|
|
13
19
|
|
|
14
20
|
export { makeRootCommand };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { DiffDoc } from "../../render/DiffDoc.js";
|
|
2
2
|
import { CatalogMergeError, DriftError, GateError, NoConfigError, StaleError } from "./errors.js";
|
|
3
|
+
import { Console, Effect, Option } from "effect";
|
|
3
4
|
import { CliInteractive, CliRuntime, Doc } from "@effected/cli-kit";
|
|
4
5
|
import { ConfigLoadError, ConfigLoader, Report, SchemaRun, StepSummary } from "@effected/engine/schema";
|
|
5
6
|
import { Audience } from "@effected/env";
|
|
6
|
-
import { Console, Effect, Option } from "effect";
|
|
7
7
|
|
|
8
8
|
// src/commands/schema/execute.ts
|
|
9
9
|
/** `--format` as given, else what the audience reads: JSON for an agent, human text for a person and for CI. */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ValidationFailedError } from "./errors.js";
|
|
2
2
|
import { configFailures, resolveFormat, warnVersionMismatch } from "./execute.js";
|
|
3
3
|
import { configArgument, formatFlag, ignoreVersionMismatchFlag } from "./flags.js";
|
|
4
|
+
import { Console, Effect, Option, Schema } from "effect";
|
|
4
5
|
import { CliRuntime, Doc } from "@effected/cli-kit";
|
|
5
6
|
import { Argument, Command, Flag } from "effect/cli";
|
|
6
7
|
import { SchemaValidate, ValidateReport } from "@effected/engine/schema";
|
|
7
|
-
import { Console, Effect, Option, Schema } from "effect";
|
|
8
8
|
|
|
9
9
|
// src/commands/schema/validate.ts
|
|
10
10
|
const findingLine = (finding) => {
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,115 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ActionRunDeps, BuildRunDeps } from "@effected/engine/build";
|
|
3
|
+
import "@effected/env";
|
|
1
4
|
import "effect/cli";
|
|
2
5
|
import { SchemaRunDeps } from "@effected/engine/schema";
|
|
3
|
-
|
|
4
|
-
|
|
6
|
+
declare const ActionBuildFailedError_base: Schema.Class<ActionBuildFailedError, Schema.TaggedStruct<"ActionBuildFailedError", {
|
|
7
|
+
/** How many validation errors there were. */
|
|
8
|
+
readonly errors: Schema.Number;
|
|
9
|
+
/** How many entries failed to bundle or load. */
|
|
10
|
+
readonly failedEntries: Schema.Number;
|
|
11
|
+
}>, import("effect/Cause").YieldableError>;
|
|
12
|
+
/**
|
|
13
|
+
* Validation stopped the build or an entry failed. Exit `1`.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export declare class ActionBuildFailedError extends ActionBuildFailedError_base {
|
|
18
|
+
get message(): string;
|
|
19
|
+
}
|
|
20
|
+
declare const ActionConfigError_base: Schema.Class<ActionConfigError, Schema.TaggedStruct<"ActionConfigError", {
|
|
21
|
+
readonly message: Schema.String;
|
|
22
|
+
}>, import("effect/Cause").YieldableError>;
|
|
23
|
+
/**
|
|
24
|
+
* The build could not start: its `effected.toml` would not load, it has no `[action]` table, or the directory is not a
|
|
25
|
+
* package. Exit `2`: exit `1` means it ran and found a problem.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Carries the engine's message. This command's own type rather than the engine's, because the engine's build entry is
|
|
29
|
+
* loaded only when the command runs.
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export declare class ActionConfigError extends ActionConfigError_base {}
|
|
34
|
+
declare const ActionRunError_base: Schema.Class<ActionRunError, Schema.TaggedStruct<"ActionRunError", {
|
|
35
|
+
readonly message: Schema.String;
|
|
36
|
+
}>, import("effect/Cause").YieldableError>;
|
|
37
|
+
/**
|
|
38
|
+
* The build could not finish: an entry is missing with validation off, or a file could not be read, cleaned, written or
|
|
39
|
+
* copied. `action init` fails with it too when a write fails after its checks, naming the files already written. Exit
|
|
40
|
+
* `3`, the infrastructure tier.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* Carries the engine's message, as {@link ActionConfigError} does.
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export declare class ActionRunError extends ActionRunError_base {}
|
|
48
|
+
declare const ActionInitError_base: Schema.Class<ActionInitError, Schema.TaggedStruct<"ActionInitError", {
|
|
49
|
+
readonly message: Schema.String;
|
|
50
|
+
}>, import("effect/Cause").YieldableError>;
|
|
51
|
+
/**
|
|
52
|
+
* `action init` could not start: the directory is not a package, the action's name or description was found nowhere
|
|
53
|
+
* or holds a control character, files it would write exist and `--force` was not given, or a path cannot be written
|
|
54
|
+
* over (a symbolic link, the wrong kind of entry), which `--force` never overrides. Exit `2`, and nothing was written.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* Carries the engine's message, as {@link ActionConfigError} does.
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
export declare class ActionInitError extends ActionInitError_base {}
|
|
62
|
+
/**
|
|
63
|
+
* What `effected action` is given by the process boundary. `main` reads `cwd` and `ci`, and nothing else reads the
|
|
64
|
+
* process.
|
|
65
|
+
*
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
68
|
+
type ActionCommandDeps = ActionRunDeps;
|
|
69
|
+
declare const BuildFailedError_base: Schema.Class<BuildFailedError, Schema.TaggedStruct<"BuildFailedError", {
|
|
70
|
+
/** How many errors the build recorded. */
|
|
71
|
+
readonly errors: Schema.Number;
|
|
72
|
+
/** What ended the build, when it did not reach its end. */
|
|
73
|
+
readonly failure: Schema.optionalKey<Schema.String>;
|
|
74
|
+
}>, import("effect/Cause").YieldableError>;
|
|
75
|
+
/**
|
|
76
|
+
* The build crashed or recorded an error. Exit `1`.
|
|
77
|
+
*
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
export declare class BuildFailedError extends BuildFailedError_base {
|
|
81
|
+
get message(): string;
|
|
82
|
+
}
|
|
83
|
+
declare const PackageConfigError_base: Schema.Class<PackageConfigError, Schema.TaggedStruct<"PackageConfigError", {
|
|
84
|
+
readonly message: Schema.String;
|
|
85
|
+
}>, import("effect/Cause").YieldableError>;
|
|
86
|
+
/**
|
|
87
|
+
* The build could not run: its `effected.toml` or a plugin would not load, the directory is not a package, or the
|
|
88
|
+
* bundler refused the config before any work. Exit `2`: exit `1` means it ran and found a problem.
|
|
89
|
+
*
|
|
90
|
+
* @remarks
|
|
91
|
+
* Carries the engine's message, which names the file at fault and the key within it. This command's own type rather
|
|
92
|
+
* than the bundler's, because the CLI reaches the bundler only through the engine.
|
|
93
|
+
*
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
96
|
+
export declare class PackageConfigError extends PackageConfigError_base {}
|
|
97
|
+
declare const PackageRunError_base: Schema.Class<PackageRunError, Schema.TaggedStruct<"PackageRunError", {
|
|
98
|
+
readonly message: Schema.String;
|
|
99
|
+
}>, import("effect/Cause").YieldableError>;
|
|
100
|
+
/**
|
|
101
|
+
* A file the build needs could not be read or written. Exit `3`, the infrastructure tier.
|
|
102
|
+
*
|
|
103
|
+
* @public
|
|
104
|
+
*/
|
|
105
|
+
export declare class PackageRunError extends PackageRunError_base {}
|
|
106
|
+
/**
|
|
107
|
+
* What `effected package` is given by the process boundary, and the test seams: the engine's dependencies, less the
|
|
108
|
+
* progress callback. `main` reads `cwd` and `ci`, and nothing else reads the process.
|
|
109
|
+
*
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
type PackageCommandDeps = Omit<BuildRunDeps, "onProgress">;
|
|
5
113
|
declare const DriftError_base: Schema.Class<DriftError, Schema.TaggedStruct<"DriftError", {
|
|
6
114
|
readonly drifted: Schema.$Array<Schema.Struct<{
|
|
7
115
|
readonly $id: Schema.String;
|
|
@@ -111,6 +219,12 @@ export declare class NoConfigError extends NoConfigError_base {
|
|
|
111
219
|
* @public
|
|
112
220
|
*/
|
|
113
221
|
type SchemaCommandDeps = Omit<SchemaRunDeps, "onProgress">;
|
|
222
|
+
/**
|
|
223
|
+
* What every command is given by the process boundary, and the test seams.
|
|
224
|
+
*
|
|
225
|
+
* @public
|
|
226
|
+
*/
|
|
227
|
+
type CommandDeps = SchemaCommandDeps & PackageCommandDeps & ActionCommandDeps;
|
|
114
228
|
/**
|
|
115
229
|
* The `effected` command tree, closed over the process boundary it runs against: `main` hands in the process values, a
|
|
116
230
|
* test its own. The audience flags are shared on the root so every subcommand accepts them.
|
|
@@ -126,5 +240,5 @@ export declare const makeRootCommand: any;
|
|
|
126
240
|
* @public
|
|
127
241
|
*/
|
|
128
242
|
export declare const CLI_VERSION: string;
|
|
129
|
-
export type { SchemaCommandDeps };
|
|
243
|
+
export type { ActionCommandDeps, CommandDeps, PackageCommandDeps, SchemaCommandDeps };
|
|
130
244
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { ActionBuildFailedError, ActionConfigError, ActionInitError, ActionRunError } from "./commands/action/errors.js";
|
|
2
|
+
import { BuildFailedError, PackageConfigError, PackageRunError } from "./commands/package/errors.js";
|
|
1
3
|
import { makeRootCommand } from "./commands/root.js";
|
|
2
4
|
import { CatalogMergeError, DriftError, GateError, NoConfigError, StaleError, ValidationFailedError } from "./commands/schema/errors.js";
|
|
3
5
|
import { CLI_VERSION } from "./version.js";
|
|
4
6
|
|
|
5
|
-
export { CLI_VERSION, CatalogMergeError, DriftError, GateError, NoConfigError, StaleError, ValidationFailedError, makeRootCommand };
|
|
7
|
+
export { ActionBuildFailedError, ActionConfigError, ActionInitError, ActionRunError, BuildFailedError, CLI_VERSION, CatalogMergeError, DriftError, GateError, NoConfigError, PackageConfigError, PackageRunError, StaleError, ValidationFailedError, makeRootCommand };
|
package/main.d.ts
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { Distribution } from "@effected/kit";
|
|
2
2
|
import { Layer } from "effect";
|
|
3
|
-
import
|
|
3
|
+
import "effect/cli";
|
|
4
|
+
import { ActionRunDeps, BuildRunDeps } from "@effected/engine/build";
|
|
4
5
|
import "@effected/env";
|
|
6
|
+
import { SchemaRunDeps } from "@effected/engine/schema";
|
|
7
|
+
/**
|
|
8
|
+
* What `effected action` is given by the process boundary. `main` reads `cwd` and `ci`, and nothing else reads the
|
|
9
|
+
* process.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
type ActionCommandDeps = ActionRunDeps;
|
|
14
|
+
/**
|
|
15
|
+
* What `effected package` is given by the process boundary, and the test seams: the engine's dependencies, less the
|
|
16
|
+
* progress callback. `main` reads `cwd` and `ci`, and nothing else reads the process.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
type PackageCommandDeps = Omit<BuildRunDeps, "onProgress">;
|
|
5
21
|
/**
|
|
6
22
|
* What `effected schema` is given by the process boundary, and the test seams: the engine's dependencies, less the
|
|
7
23
|
* progress callback the command supplies itself. `main` reads `cwd`, and nothing else reads the process.
|
|
@@ -9,6 +25,12 @@ import "@effected/env";
|
|
|
9
25
|
* @public
|
|
10
26
|
*/
|
|
11
27
|
type SchemaCommandDeps = Omit<SchemaRunDeps, "onProgress">;
|
|
28
|
+
/**
|
|
29
|
+
* What every command is given by the process boundary, and the test seams.
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
type CommandDeps = SchemaCommandDeps & PackageCommandDeps & ActionCommandDeps;
|
|
12
34
|
/**
|
|
13
35
|
* Options a carrier's bin shim passes to {@link main}. `distribution` names
|
|
14
36
|
* the package the `effected` bin was installed through; omitted for a direct
|
|
@@ -39,7 +61,7 @@ export declare const mainOptions: {
|
|
|
39
61
|
*
|
|
40
62
|
* @public
|
|
41
63
|
*/
|
|
42
|
-
export declare const run: <RP, EP>(options: MainOptions, deps:
|
|
64
|
+
export declare const run: <RP, EP>(options: MainOptions, deps: CommandDeps, platform: Layer.Layer<RP, EP>) => import("effect/Effect").Effect<void, Error, Exclude<import("effect/process/ChildProcessSpawner").ChildProcessSpawner, RP> | Exclude<import("effect/FileSystem").FileSystem, RP> | Exclude<import("effect/Path").Path, RP> | Exclude<import("effect/Stdio").Stdio, RP> | Exclude<import("effect/Stdio").Stdio, RP> | Exclude<import("effect/Terminal").Terminal, RP>>;
|
|
43
65
|
/**
|
|
44
66
|
* Run the `effected` command. Owns the process: installs the runtime teardown
|
|
45
67
|
* and sets the exit code.
|
|
@@ -47,5 +69,5 @@ export declare const run: <RP, EP>(options: MainOptions, deps: SchemaCommandDeps
|
|
|
47
69
|
* @public
|
|
48
70
|
*/
|
|
49
71
|
export declare const main: (options?: MainOptions) => void;
|
|
50
|
-
export type { SchemaCommandDeps };
|
|
72
|
+
export type { ActionCommandDeps, CommandDeps, PackageCommandDeps, SchemaCommandDeps };
|
|
51
73
|
//# sourceMappingURL=main.d.ts.map
|
package/main.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isCI } from "./ci.js";
|
|
1
2
|
import { makeRootCommand } from "./commands/root.js";
|
|
2
3
|
import { CLI_VERSION } from "./version.js";
|
|
3
4
|
import { CliAudience, CliLogger, CliRuntime } from "@effected/cli-kit";
|
|
@@ -55,7 +56,10 @@ const run = (options, deps, platform) => CliRuntime.main(CliAudience.run(makeRoo
|
|
|
55
56
|
* @public
|
|
56
57
|
*/
|
|
57
58
|
const main = (options = {}) => {
|
|
58
|
-
NodeRuntime.runMain(run(options, {
|
|
59
|
+
NodeRuntime.runMain(run(options, {
|
|
60
|
+
cwd: process.cwd(),
|
|
61
|
+
ci: isCI(process.env)
|
|
62
|
+
}, NodeServices.layer));
|
|
59
63
|
};
|
|
60
64
|
|
|
61
65
|
export { main, mainOptions, run };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The effected command: the tool's command tree on the kit's front-end runtime",
|
|
6
6
|
"keywords": [
|
|
@@ -43,16 +43,20 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@effect/platform-node": "~4.1.2",
|
|
46
|
-
"@effected/cli-kit": "^0.27.
|
|
47
|
-
"@effected/engine": "0.
|
|
46
|
+
"@effected/cli-kit": "^0.27.1",
|
|
47
|
+
"@effected/engine": "0.15.0",
|
|
48
48
|
"@effected/env": "^0.11.0",
|
|
49
|
-
"@effected/glob": "^0.
|
|
49
|
+
"@effected/glob": "^0.20.0",
|
|
50
50
|
"@effected/kit": "^0.13.0",
|
|
51
|
-
"@effected/walker": "^0.
|
|
51
|
+
"@effected/walker": "^0.26.0",
|
|
52
52
|
"effect": "~4.1.2",
|
|
53
53
|
"ink": "^8.0.0",
|
|
54
54
|
"react": "^19.3.0"
|
|
55
55
|
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@types/node": "^26.6.0",
|
|
58
|
+
"typescript": "7.1.0-beta || 7.1.1-rc || >=7.1.0 <8"
|
|
59
|
+
},
|
|
56
60
|
"engines": {
|
|
57
61
|
"node": ">=24.11.0"
|
|
58
62
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Doc } from "@effected/cli-kit";
|
|
2
|
+
|
|
3
|
+
// src/render/ActionDoc.ts
|
|
4
|
+
const formatBytes = (bytes) => {
|
|
5
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
6
|
+
return bytes < 1048576 ? `${(bytes / 1024).toFixed(1)} KB` : `${(bytes / 1048576).toFixed(1)} MB`;
|
|
7
|
+
};
|
|
8
|
+
const issueLine = (issue) => `${issue.code}: ${issue.message}`;
|
|
9
|
+
/** One line for an entry, with only the first line of its error: a failed load's message can carry a require stack. */
|
|
10
|
+
const entryLine = (entry) => {
|
|
11
|
+
if (entry.stats === void 0) return `✗ ${(entry.error ?? "failed").split("\n")[0]}`;
|
|
12
|
+
const { entry: name, size, duration, outputPath } = entry.stats;
|
|
13
|
+
return `✓ ${name}: ${formatBytes(size)} (${duration}ms) → ${outputPath}`;
|
|
14
|
+
};
|
|
15
|
+
const summaryOf = ({ build }) => `built ${build.entries.filter((entry) => entry.success).length} entries in ${build.duration}ms`;
|
|
16
|
+
/** A warning as its first line, indented under the entry it belongs to. */
|
|
17
|
+
const warningLines = (entry) => (entry.warnings ?? []).map((warning) => ` ${warning.trim().split("\n")[0]}`);
|
|
18
|
+
/**
|
|
19
|
+
* An action build for a reader: validation's errors, its warnings, a line per entry, then the total.
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
var ActionDoc = class ActionDoc {
|
|
24
|
+
constructor() {}
|
|
25
|
+
/** The report as plain lines, the total last, for a log. */
|
|
26
|
+
static lines(result) {
|
|
27
|
+
return [
|
|
28
|
+
...result.validation.errors.map(issueLine),
|
|
29
|
+
...result.validation.warnings.map(issueLine),
|
|
30
|
+
...result.build.entries.flatMap((entry) => [entryLine(entry), ...warningLines(entry)]),
|
|
31
|
+
summaryOf(result)
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The report as a `Doc`.
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* Every line goes in as a `Doc.text` run, which is data: the renderers remove escape and control characters from it,
|
|
39
|
+
* so an error quoting untrusted text cannot repaint the terminal.
|
|
40
|
+
*/
|
|
41
|
+
static render(result) {
|
|
42
|
+
const rows = [
|
|
43
|
+
...result.validation.errors.map((issue) => Doc.text(issueLine(issue), "error")),
|
|
44
|
+
...result.validation.warnings.map((issue) => Doc.text(issueLine(issue), "warning")),
|
|
45
|
+
...result.build.entries.flatMap((entry) => [Doc.text(entryLine(entry), entry.success ? "success" : "error"), ...warningLines(entry).map((line) => Doc.text(line, "warning"))]),
|
|
46
|
+
Doc.text(summaryOf(result), result.build.success ? "success" : "error")
|
|
47
|
+
];
|
|
48
|
+
return Doc.lines(rows, { wrap: false });
|
|
49
|
+
}
|
|
50
|
+
/** The report for a GitHub step summary, in a code block. */
|
|
51
|
+
static markdown(result) {
|
|
52
|
+
const body = ActionDoc.lines(result).join("\n");
|
|
53
|
+
const longest = (body.match(/`+/g) ?? []).reduce((length, run) => Math.max(length, run.length), 2);
|
|
54
|
+
const fence = "`".repeat(longest + 1);
|
|
55
|
+
return [
|
|
56
|
+
"### effected action build",
|
|
57
|
+
"",
|
|
58
|
+
fence,
|
|
59
|
+
body,
|
|
60
|
+
fence,
|
|
61
|
+
""
|
|
62
|
+
].join("\n");
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export { ActionDoc };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Doc } from "@effected/cli-kit";
|
|
2
|
+
|
|
3
|
+
// src/render/InitDoc.ts
|
|
4
|
+
/** What is left to do, as lines: the packages to add, the module type, and the build. */
|
|
5
|
+
const nextSteps = (result) => [
|
|
6
|
+
...result.missingDependencies.length > 0 ? [`add dependencies: ${result.missingDependencies.join(" ")}`] : [],
|
|
7
|
+
...result.missingDevDependencies.length > 0 ? [`add devDependencies: ${result.missingDevDependencies.join(" ")}`] : [],
|
|
8
|
+
...result.typeModule ? [] : ["set \"type\": \"module\" in package.json"],
|
|
9
|
+
"then run: effected action build"
|
|
10
|
+
];
|
|
11
|
+
const fileLine = (result, file) => `${result.overwritten.includes(file) ? "replaced" : "wrote"} ${file}`;
|
|
12
|
+
/**
|
|
13
|
+
* An `action init` for a reader: a line per file written, then what is left to do.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
var InitDoc = class {
|
|
18
|
+
constructor() {}
|
|
19
|
+
/** The report as plain lines, for a log. */
|
|
20
|
+
static lines(result) {
|
|
21
|
+
return [...result.written.map((file) => fileLine(result, file)), ...nextSteps(result)];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The report as a `Doc`.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* Every line goes in as a `Doc.text` run, which is data, so a name echoed back cannot repaint the terminal.
|
|
28
|
+
*/
|
|
29
|
+
static render(result) {
|
|
30
|
+
const rows = [...result.written.map((file) => Doc.text(fileLine(result, file), result.overwritten.includes(file) ? "warning" : "success")), ...nextSteps(result).map((line) => Doc.text(line, "muted"))];
|
|
31
|
+
return Doc.lines(rows, { wrap: false });
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { InitDoc };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Doc } from "@effected/cli-kit";
|
|
2
|
+
|
|
3
|
+
// src/render/IssuesDoc.ts
|
|
4
|
+
/** `file:line:col code text`, leaving out whichever parts the diagnostic does not have. */
|
|
5
|
+
const lineOf = (diagnostic) => {
|
|
6
|
+
return [
|
|
7
|
+
...diagnostic.file === void 0 ? [] : [[
|
|
8
|
+
diagnostic.file,
|
|
9
|
+
diagnostic.line,
|
|
10
|
+
diagnostic.column
|
|
11
|
+
].filter((part) => part !== void 0).join(":")],
|
|
12
|
+
...diagnostic.code !== void 0 ? [diagnostic.code] : [],
|
|
13
|
+
diagnostic.text
|
|
14
|
+
].join(" ");
|
|
15
|
+
};
|
|
16
|
+
const summaryOf = (issues) => `built ${issues.package}: ${issues.errors.length} errors, ${issues.warnings.length} warnings, ${issues.suppressed.length} suppressed`;
|
|
17
|
+
const summaryToken = (issues) => !issues.buildOk || issues.errors.length > 0 ? "error" : issues.warnings.length > 0 ? "warning" : "success";
|
|
18
|
+
const fenceFor = (text) => {
|
|
19
|
+
const longest = (text.match(/`+/g) ?? []).reduce((length, run) => Math.max(length, run.length), 2);
|
|
20
|
+
return "`".repeat(longest + 1);
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A build's `issues.json` for a reader: errors, then warnings, then one summary line. Suppressed diagnostics are only
|
|
24
|
+
* counted.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
var IssuesDoc = class {
|
|
29
|
+
constructor() {}
|
|
30
|
+
/** The report as plain lines, the summary last, for a log. */
|
|
31
|
+
static lines(issues) {
|
|
32
|
+
return [
|
|
33
|
+
...issues.errors.map(lineOf),
|
|
34
|
+
...issues.warnings.map(lineOf),
|
|
35
|
+
summaryOf(issues)
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The report as a `Doc`.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* Every line goes in as a `Doc.text` run, which is data: the renderers remove escape and control characters from it,
|
|
43
|
+
* so a diagnostic quoting untrusted text cannot repaint the terminal.
|
|
44
|
+
*/
|
|
45
|
+
static render(issues) {
|
|
46
|
+
const rows = [
|
|
47
|
+
...issues.errors.map((diagnostic) => Doc.text(lineOf(diagnostic), "error")),
|
|
48
|
+
...issues.warnings.map((diagnostic) => Doc.text(lineOf(diagnostic), "warning")),
|
|
49
|
+
Doc.text(summaryOf(issues), summaryToken(issues))
|
|
50
|
+
];
|
|
51
|
+
return Doc.lines(rows, { wrap: false });
|
|
52
|
+
}
|
|
53
|
+
/** The report for a GitHub step summary: the failure, when there is one, and the diagnostics in a code block. */
|
|
54
|
+
static markdown(issues) {
|
|
55
|
+
const diagnostics = [...issues.errors, ...issues.warnings].map(lineOf).join("\n");
|
|
56
|
+
const fence = fenceFor(diagnostics);
|
|
57
|
+
return [
|
|
58
|
+
"### effected package build",
|
|
59
|
+
"",
|
|
60
|
+
summaryOf(issues),
|
|
61
|
+
...issues.failure !== void 0 ? ["", `Failed: ${issues.failure.message}`] : [],
|
|
62
|
+
...diagnostics !== "" ? [
|
|
63
|
+
"",
|
|
64
|
+
fence,
|
|
65
|
+
diagnostics,
|
|
66
|
+
fence
|
|
67
|
+
] : [],
|
|
68
|
+
""
|
|
69
|
+
].join("\n");
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export { IssuesDoc };
|