@projitect/cli-internals 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/dist/applier.d.ts +12 -0
- package/dist/applier.d.ts.map +1 -0
- package/dist/applier.js +69 -0
- package/dist/applier.js.map +1 -0
- package/dist/commands/add.d.ts +34 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +64 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/build.d.ts +39 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +108 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/dispatch.d.ts +5 -0
- package/dist/commands/dispatch.d.ts.map +1 -0
- package/dist/commands/dispatch.js +211 -0
- package/dist/commands/dispatch.js.map +1 -0
- package/dist/commands/explain.d.ts +10 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +46 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/init.d.ts +33 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +128 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inspect-json.d.ts +51 -0
- package/dist/commands/inspect-json.d.ts.map +1 -0
- package/dist/commands/inspect-json.js +58 -0
- package/dist/commands/inspect-json.js.map +1 -0
- package/dist/commands/inspect.d.ts +41 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +74 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/remodel.d.ts +22 -0
- package/dist/commands/remodel.d.ts.map +1 -0
- package/dist/commands/remodel.js +49 -0
- package/dist/commands/remodel.js.map +1 -0
- package/dist/config-cascade.d.ts +25 -0
- package/dist/config-cascade.d.ts.map +1 -0
- package/dist/config-cascade.js +45 -0
- package/dist/config-cascade.js.map +1 -0
- package/dist/differ.d.ts +31 -0
- package/dist/differ.d.ts.map +1 -0
- package/dist/differ.js +134 -0
- package/dist/differ.js.map +1 -0
- package/dist/edit-pjt.d.ts +19 -0
- package/dist/edit-pjt.d.ts.map +1 -0
- package/dist/edit-pjt.js +40 -0
- package/dist/edit-pjt.js.map +1 -0
- package/dist/filesystem-impl.d.ts +25 -0
- package/dist/filesystem-impl.d.ts.map +1 -0
- package/dist/filesystem-impl.js +153 -0
- package/dist/filesystem-impl.js.map +1 -0
- package/dist/git.d.ts +29 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +63 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/client.d.ts +11 -0
- package/dist/ipc/client.d.ts.map +1 -0
- package/dist/ipc/client.js +45 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/index.d.ts +5 -0
- package/dist/ipc/index.d.ts.map +1 -0
- package/dist/ipc/index.js +5 -0
- package/dist/ipc/index.js.map +1 -0
- package/dist/ipc/protocol.d.ts +251 -0
- package/dist/ipc/protocol.d.ts.map +1 -0
- package/dist/ipc/protocol.js +41 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/server.d.ts +9 -0
- package/dist/ipc/server.d.ts.map +1 -0
- package/dist/ipc/server.js +23 -0
- package/dist/ipc/server.js.map +1 -0
- package/dist/ipc/transport.d.ts +12 -0
- package/dist/ipc/transport.d.ts.map +1 -0
- package/dist/ipc/transport.js +19 -0
- package/dist/ipc/transport.js.map +1 -0
- package/dist/loader.d.ts +45 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +65 -0
- package/dist/loader.js.map +1 -0
- package/dist/lockfile.d.ts +22 -0
- package/dist/lockfile.d.ts.map +1 -0
- package/dist/lockfile.js +59 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/permissions.d.ts +20 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +86 -0
- package/dist/permissions.js.map +1 -0
- package/dist/plan.d.ts +278 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +212 -0
- package/dist/plan.js.map +1 -0
- package/dist/platform/file-system.d.ts +3 -0
- package/dist/platform/file-system.d.ts.map +1 -0
- package/dist/platform/file-system.js +109 -0
- package/dist/platform/file-system.js.map +1 -0
- package/dist/platform/index.d.ts +17 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +20 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/process-runner.d.ts +41 -0
- package/dist/platform/process-runner.d.ts.map +1 -0
- package/dist/platform/process-runner.js +120 -0
- package/dist/platform/process-runner.js.map +1 -0
- package/dist/platform/stdio.d.ts +9 -0
- package/dist/platform/stdio.d.ts.map +1 -0
- package/dist/platform/stdio.js +11 -0
- package/dist/platform/stdio.js.map +1 -0
- package/dist/platform/terminal.d.ts +3 -0
- package/dist/platform/terminal.d.ts.map +1 -0
- package/dist/platform/terminal.js +81 -0
- package/dist/platform/terminal.js.map +1 -0
- package/dist/pm.d.ts +31 -0
- package/dist/pm.d.ts.map +1 -0
- package/dist/pm.js +91 -0
- package/dist/pm.js.map +1 -0
- package/dist/post-apply.d.ts +20 -0
- package/dist/post-apply.d.ts.map +1 -0
- package/dist/post-apply.js +29 -0
- package/dist/post-apply.js.map +1 -0
- package/dist/project-file.d.ts +8 -0
- package/dist/project-file.d.ts.map +1 -0
- package/dist/project-file.js +12 -0
- package/dist/project-file.js.map +1 -0
- package/dist/region.d.ts +105 -0
- package/dist/region.d.ts.map +1 -0
- package/dist/region.js +94 -0
- package/dist/region.js.map +1 -0
- package/dist/remover.d.ts +16 -0
- package/dist/remover.d.ts.map +1 -0
- package/dist/remover.js +88 -0
- package/dist/remover.js.map +1 -0
- package/package.json +52 -3
- package/src/applier.ts +91 -0
- package/src/commands/add.ts +88 -0
- package/src/commands/build.ts +156 -0
- package/src/commands/dispatch.ts +345 -0
- package/src/commands/explain.ts +81 -0
- package/src/commands/init.ts +193 -0
- package/src/commands/inspect-json.ts +68 -0
- package/src/commands/inspect.ts +106 -0
- package/src/commands/remodel.ts +72 -0
- package/src/config-cascade.ts +60 -0
- package/src/differ.ts +188 -0
- package/src/edit-pjt.ts +56 -0
- package/src/filesystem-impl.ts +230 -0
- package/src/git.ts +87 -0
- package/src/index.ts +54 -0
- package/src/ipc/client.ts +108 -0
- package/src/ipc/index.ts +12 -0
- package/src/ipc/protocol.ts +48 -0
- package/src/ipc/server.ts +62 -0
- package/src/ipc/transport.ts +28 -0
- package/src/loader.ts +115 -0
- package/src/lockfile.ts +81 -0
- package/src/permissions.ts +106 -0
- package/src/plan.ts +459 -0
- package/src/platform/file-system.ts +155 -0
- package/src/platform/index.ts +26 -0
- package/src/platform/process-runner.ts +150 -0
- package/src/platform/stdio.ts +11 -0
- package/src/platform/terminal.ts +89 -0
- package/src/pm.ts +131 -0
- package/src/post-apply.ts +45 -0
- package/src/project-file.ts +15 -0
- package/src/region.ts +139 -0
- package/src/remover.ts +127 -0
- package/README.md +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/platform/terminal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,EAAiB,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAsF/D,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAA0C,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as keypress from "node:readline";
|
|
2
|
+
import * as readline from "node:readline/promises";
|
|
3
|
+
import { Effect, Layer, Option, Queue, Terminal } from "effect";
|
|
4
|
+
import * as PlatformError from "effect/PlatformError";
|
|
5
|
+
/**
|
|
6
|
+
* Node-backed `Terminal` Layer.
|
|
7
|
+
*
|
|
8
|
+
* - `columns` / `rows` read from `process.stdout` (fall back to sensible defaults when not a TTY)
|
|
9
|
+
* - `display(text)` writes to `process.stdout` synchronously
|
|
10
|
+
* - `readLine` uses `node:readline/promises` so `Prompt.text` and `Prompt.confirm` work
|
|
11
|
+
* - `readInput` (the low-level keypress stream used by `Prompt.select` / `Prompt.multiSelect`)
|
|
12
|
+
* puts a TTY stdin into raw mode for the lifetime of the enclosing scope and queues one
|
|
13
|
+
* `UserInput` per keypress. Raw mode means Ctrl-C arrives as a keypress rather than as SIGINT,
|
|
14
|
+
* so Ctrl-C ends the queue and the prompt fails with `QuitError`.
|
|
15
|
+
*/
|
|
16
|
+
const readInput = Effect.gen(function* () {
|
|
17
|
+
const queue = yield* Queue.unbounded();
|
|
18
|
+
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
19
|
+
const onKeypress = (input, key) => {
|
|
20
|
+
Queue.offerUnsafe(queue, {
|
|
21
|
+
input: Option.fromNullishOr(input),
|
|
22
|
+
key: {
|
|
23
|
+
name: key?.name ?? "",
|
|
24
|
+
ctrl: key?.ctrl ?? false,
|
|
25
|
+
meta: key?.meta ?? false,
|
|
26
|
+
shift: key?.shift ?? false,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
if (key?.ctrl === true && key.name === "c") {
|
|
30
|
+
// Raw mode swallows SIGINT; ending the queue makes the prompt fail with `QuitError`.
|
|
31
|
+
Queue.endUnsafe(queue);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
keypress.emitKeypressEvents(process.stdin);
|
|
35
|
+
if (process.stdin.isTTY) {
|
|
36
|
+
process.stdin.setRawMode(true);
|
|
37
|
+
}
|
|
38
|
+
process.stdin.on("keypress", onKeypress);
|
|
39
|
+
process.stdin.resume();
|
|
40
|
+
return onKeypress;
|
|
41
|
+
}), (onKeypress) => Effect.sync(() => {
|
|
42
|
+
process.stdin.off("keypress", onKeypress);
|
|
43
|
+
if (process.stdin.isTTY) {
|
|
44
|
+
process.stdin.setRawMode(false);
|
|
45
|
+
}
|
|
46
|
+
process.stdin.pause();
|
|
47
|
+
}));
|
|
48
|
+
return queue;
|
|
49
|
+
});
|
|
50
|
+
const make = Terminal.make({
|
|
51
|
+
// @types/node types columns/rows as non-nullable, but they're undefined on non-TTY stdout.
|
|
52
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
53
|
+
columns: Effect.sync(() => process.stdout.columns ?? 80),
|
|
54
|
+
rows: Effect.sync(() => process.stdout.rows ?? 24),
|
|
55
|
+
/* eslint-enable @typescript-eslint/no-unnecessary-condition */
|
|
56
|
+
display: (text) => Effect.try({
|
|
57
|
+
try: () => {
|
|
58
|
+
process.stdout.write(text);
|
|
59
|
+
},
|
|
60
|
+
catch: (cause) => PlatformError.badArgument({
|
|
61
|
+
module: "Terminal",
|
|
62
|
+
method: "display",
|
|
63
|
+
description: cause instanceof Error ? cause.message : String(cause),
|
|
64
|
+
}),
|
|
65
|
+
}),
|
|
66
|
+
readLine: Effect.tryPromise({
|
|
67
|
+
try: async () => {
|
|
68
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
69
|
+
try {
|
|
70
|
+
return await rl.question("");
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
rl.close();
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
catch: () => new Terminal.QuitError({}),
|
|
77
|
+
}),
|
|
78
|
+
readInput,
|
|
79
|
+
});
|
|
80
|
+
export const TerminalLive = Layer.succeed(Terminal.Terminal, make);
|
|
81
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/platform/terminal.ts"],"names":[],"mappings":"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"}
|
package/dist/pm.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Errors } from "@projitect/core";
|
|
3
|
+
import type { ProjitectPackageMetadata } from "@projitect/blueprint";
|
|
4
|
+
export type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
|
|
5
|
+
/**
|
|
6
|
+
* Detect the user's package manager by walking up from `projectRoot` looking for known
|
|
7
|
+
* lockfiles. Falls back to `npm` if nothing is found. Used by `pjt add` to decide which install
|
|
8
|
+
* command to shell out to.
|
|
9
|
+
*/
|
|
10
|
+
export declare const detect: (params: {
|
|
11
|
+
readonly projectRoot: string;
|
|
12
|
+
}) => Effect.Effect<PackageManager, Errors.PmNotDetected>;
|
|
13
|
+
/**
|
|
14
|
+
* Run `<pm> add -D <pkg>` in `projectRoot`. Wraps spawn failures (non-zero exit, PM not
|
|
15
|
+
* installed) as `pjt.pm.install-failed`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const installDev: (params: {
|
|
18
|
+
readonly projectRoot: string;
|
|
19
|
+
readonly pm: PackageManager;
|
|
20
|
+
readonly pkg: string;
|
|
21
|
+
}) => Effect.Effect<void, Errors.PmInstallFailed>;
|
|
22
|
+
/**
|
|
23
|
+
* Read the installed package's `package.json` and return its `"projitect"` metadata, if any.
|
|
24
|
+
* Returns null if the package doesn't have the field; `pjt add` falls back to install-only
|
|
25
|
+
* behavior with a printed instruction snippet.
|
|
26
|
+
*/
|
|
27
|
+
export declare const readProjitectMetadata: (params: {
|
|
28
|
+
readonly projectRoot: string;
|
|
29
|
+
readonly pkg: string;
|
|
30
|
+
}) => Effect.Effect<ProjitectPackageMetadata | null>;
|
|
31
|
+
//# sourceMappingURL=pm.d.ts.map
|
package/dist/pm.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pm.d.ts","sourceRoot":"","sources":["../src/pm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAS,MAAM,EAA4B,MAAM,QAAQ,CAAA;AAEhE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAEpE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;AAS5D;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,QAAQ;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B,KAAG,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,aAAa,CAcnD,CAAA;AAEH;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAA;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,eAAe,CA2B7C,CAAA;AAUD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,KAAG,MAAM,CAAC,MAAM,CAAC,wBAAwB,GAAG,IAAI,CAqC7C,CAAA"}
|
package/dist/pm.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { Array, Effect, Match, Option, Predicate } from "effect";
|
|
5
|
+
import { PredicateX, StructX } from "@nunofyobiz/effect-extras";
|
|
6
|
+
import { Errors } from "@projitect/core";
|
|
7
|
+
const LOCKFILES = [
|
|
8
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
9
|
+
["bun.lockb", "bun"],
|
|
10
|
+
["yarn.lock", "yarn"],
|
|
11
|
+
["package-lock.json", "npm"],
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Detect the user's package manager by walking up from `projectRoot` looking for known
|
|
15
|
+
* lockfiles. Falls back to `npm` if nothing is found. Used by `pjt add` to decide which install
|
|
16
|
+
* command to shell out to.
|
|
17
|
+
*/
|
|
18
|
+
export const detect = (params) => Effect.map(Effect.findFirst(LOCKFILES, ([filename]) => Effect.promise(() => fs.access(path.join(params.projectRoot, filename)).then(() => true, () => false))), Option.match({
|
|
19
|
+
onSome: ([, pm]) => pm,
|
|
20
|
+
onNone: () => "npm",
|
|
21
|
+
}));
|
|
22
|
+
/**
|
|
23
|
+
* Run `<pm> add -D <pkg>` in `projectRoot`. Wraps spawn failures (non-zero exit, PM not
|
|
24
|
+
* installed) as `pjt.pm.install-failed`.
|
|
25
|
+
*/
|
|
26
|
+
export const installDev = (params) => {
|
|
27
|
+
const args = installArgs(params.pm, params.pkg);
|
|
28
|
+
return Effect.tryPromise({
|
|
29
|
+
try: () => new Promise((resolve, reject) => {
|
|
30
|
+
const child = spawn(params.pm, args, {
|
|
31
|
+
cwd: params.projectRoot,
|
|
32
|
+
stdio: "inherit",
|
|
33
|
+
});
|
|
34
|
+
child.on("error", reject);
|
|
35
|
+
child.on("close", (code) => {
|
|
36
|
+
if (code === 0) {
|
|
37
|
+
resolve();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
reject(new Error(`${params.pm} ${args.join(" ")} exited with ${String(code)}`));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}),
|
|
44
|
+
catch: (e) => new Errors.PmInstallFailed({
|
|
45
|
+
id: "pjt.pm.install-failed",
|
|
46
|
+
packageManager: params.pm,
|
|
47
|
+
pkg: params.pkg,
|
|
48
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
49
|
+
message: `Failed to install \`${params.pkg}\` via ${params.pm}`,
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const installArgs = (pm, pkg) => Match.value(pm).pipe(Match.whenOr("pnpm", "yarn", () => ["add", "-D", pkg]), Match.when("bun", () => ["add", "-d", pkg]), Match.when("npm", () => ["install", "-D", pkg]), Match.exhaustive);
|
|
54
|
+
/**
|
|
55
|
+
* Read the installed package's `package.json` and return its `"projitect"` metadata, if any.
|
|
56
|
+
* Returns null if the package doesn't have the field; `pjt add` falls back to install-only
|
|
57
|
+
* behavior with a printed instruction snippet.
|
|
58
|
+
*/
|
|
59
|
+
export const readProjitectMetadata = (params) => Effect.promise(async () => {
|
|
60
|
+
const pkgJsonPath = path.join(params.projectRoot, "node_modules", params.pkg, "package.json");
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(await fs.readFile(pkgJsonPath, "utf8"));
|
|
63
|
+
if (!PredicateX.unsafeIsRecord(parsed)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const meta = parsed["projitect"];
|
|
67
|
+
if (!PredicateX.unsafeIsRecord(meta)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const type = meta["type"];
|
|
71
|
+
const importStmt = meta["import"];
|
|
72
|
+
if (type !== "blueprint" && type !== "blueprint-set") {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
if (!Predicate.isString(importStmt)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const sections = meta["sections"];
|
|
79
|
+
return {
|
|
80
|
+
type,
|
|
81
|
+
import: importStmt,
|
|
82
|
+
...StructX.defined("call", Predicate.isString(meta["call"]) ? meta["call"] : undefined),
|
|
83
|
+
...StructX.defined("callTemplate", Predicate.isString(meta["callTemplate"]) ? meta["callTemplate"] : undefined),
|
|
84
|
+
...StructX.defined("sections", Array.isArray(sections) ? Array.filter(sections, Predicate.isString) : undefined),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=pm.js.map
|
package/dist/pm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pm.js","sourceRoot":"","sources":["../src/pm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { Errors } from "@projitect/core";
|
|
3
|
+
import type { PlannedCommand } from "./plan.js";
|
|
4
|
+
import type { ProcessSpawner } from "./platform/process-runner.js";
|
|
5
|
+
/**
|
|
6
|
+
* The final phase of `build` and `remodel`: run the plan's `Exec` commands once every file
|
|
7
|
+
* operation has succeeded.
|
|
8
|
+
*
|
|
9
|
+
* Every declaring blueprint's permissions must cover its own command; that is checked for the whole
|
|
10
|
+
* list before any command starts, so a denial never leaves a half-run phase. Identical command
|
|
11
|
+
* strings run once, at their first position. Commands run sequentially from the project root, in
|
|
12
|
+
* blueprint tree order then op order, and the first failure stops the phase. Returns the commands
|
|
13
|
+
* that ran.
|
|
14
|
+
*/
|
|
15
|
+
export declare const runPostApply: (params: {
|
|
16
|
+
readonly commands: readonly PlannedCommand[];
|
|
17
|
+
readonly projectRoot: string;
|
|
18
|
+
readonly spawner?: ProcessSpawner;
|
|
19
|
+
}) => Effect.Effect<readonly string[], Errors.ExecError>;
|
|
20
|
+
//# sourceMappingURL=post-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-apply.d.ts","sourceRoot":"","sources":["../src/post-apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAU,MAAM,QAAQ,CAAA;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAE/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAElE;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAA;CAClC,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE,EAAE,MAAM,CAAC,SAAS,CAuBjD,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Array, Effect, Option } from "effect";
|
|
2
|
+
import { isCommandPermitted } from "./permissions.js";
|
|
3
|
+
import { commandNotPermitted, nodeSpawner, runCommand } from "./platform/process-runner.js";
|
|
4
|
+
/**
|
|
5
|
+
* The final phase of `build` and `remodel`: run the plan's `Exec` commands once every file
|
|
6
|
+
* operation has succeeded.
|
|
7
|
+
*
|
|
8
|
+
* Every declaring blueprint's permissions must cover its own command; that is checked for the whole
|
|
9
|
+
* list before any command starts, so a denial never leaves a half-run phase. Identical command
|
|
10
|
+
* strings run once, at their first position. Commands run sequentially from the project root, in
|
|
11
|
+
* blueprint tree order then op order, and the first failure stops the phase. Returns the commands
|
|
12
|
+
* that ran.
|
|
13
|
+
*/
|
|
14
|
+
export const runPostApply = (params) => Effect.gen(function* () {
|
|
15
|
+
const { commands, projectRoot, spawner = nodeSpawner } = params;
|
|
16
|
+
const denied = Array.findFirst(commands, (planned) => !isCommandPermitted(planned.permissions, planned.command));
|
|
17
|
+
if (Option.isSome(denied)) {
|
|
18
|
+
return yield* commandNotPermitted(denied.value.command);
|
|
19
|
+
}
|
|
20
|
+
const unique = Array.dedupeWith(commands, (a, b) => a.command === b.command);
|
|
21
|
+
yield* Effect.forEach(unique, (planned) => runCommand({
|
|
22
|
+
permissions: planned.permissions,
|
|
23
|
+
command: planned.command,
|
|
24
|
+
cwd: projectRoot,
|
|
25
|
+
spawner,
|
|
26
|
+
}), { discard: true });
|
|
27
|
+
return Array.map(unique, (planned) => planned.command);
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=post-apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-apply.js","sourceRoot":"","sources":["../src/post-apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AAG3F;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAI5B,EAAsD,EAAE,CACvD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,GAAG,WAAW,EAAE,GAAG,MAAM,CAAA;IAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAC5B,QAAQ,EACR,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,CACvE,CAAA;IACD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACzD,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,CAAA;IAC5E,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CACnB,MAAM,EACN,CAAC,OAAO,EAAE,EAAE,CACV,UAAU,CAAC;QACT,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,EAAE,WAAW;QAChB,OAAO;KACR,CAAC,EACJ,EAAE,OAAO,EAAE,IAAI,EAAE,CAClB,CAAA;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AACxD,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { BlueprintFileSystem } from "@projitect/core";
|
|
3
|
+
/**
|
|
4
|
+
* Read a project-relative file through the `BlueprintFileSystem` service, treating any read
|
|
5
|
+
* failure as "absent" (`null`). Shared by the lockfile, differ, applier and remover.
|
|
6
|
+
*/
|
|
7
|
+
export declare const readIfExists: (file: string) => Effect.Effect<string | null, never, BlueprintFileSystem>;
|
|
8
|
+
//# sourceMappingURL=project-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-file.d.ts","sourceRoot":"","sources":["../src/project-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;GAGG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,mBAAmB,CAKf,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { BlueprintFileSystem } from "@projitect/core";
|
|
3
|
+
/**
|
|
4
|
+
* Read a project-relative file through the `BlueprintFileSystem` service, treating any read
|
|
5
|
+
* failure as "absent" (`null`). Shared by the lockfile, differ, applier and remover.
|
|
6
|
+
*/
|
|
7
|
+
export const readIfExists = (file) => Effect.gen(function* () {
|
|
8
|
+
const fs = yield* BlueprintFileSystem;
|
|
9
|
+
const present = yield* fs.exists(file);
|
|
10
|
+
return present ? yield* fs.readFile(file) : null;
|
|
11
|
+
}).pipe(Effect.orElseSucceed(() => null));
|
|
12
|
+
//# sourceMappingURL=project-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-file.js","sourceRoot":"","sources":["../src/project-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,IAAY,EAC8C,EAAE,CAC5D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAA;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACtC,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAClD,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA"}
|
package/dist/region.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Data, Effect } from "effect";
|
|
2
|
+
import { Errors } from "@projitect/core";
|
|
3
|
+
export type RegionLookup = Data.TaggedEnum<{
|
|
4
|
+
readonly Found: {
|
|
5
|
+
readonly startLine: number;
|
|
6
|
+
readonly endLine: number;
|
|
7
|
+
readonly content: string;
|
|
8
|
+
};
|
|
9
|
+
readonly Absent: {};
|
|
10
|
+
}>;
|
|
11
|
+
export declare const RegionLookup: {
|
|
12
|
+
readonly Found: Data.TaggedEnum.ConstructorFrom<{
|
|
13
|
+
readonly _tag: "Found";
|
|
14
|
+
readonly startLine: number;
|
|
15
|
+
readonly endLine: number;
|
|
16
|
+
readonly content: string;
|
|
17
|
+
}, "_tag">;
|
|
18
|
+
readonly Absent: Data.TaggedEnum.ConstructorFrom<{
|
|
19
|
+
readonly _tag: "Absent";
|
|
20
|
+
}, "_tag">;
|
|
21
|
+
readonly $is: <Tag extends "Found" | "Absent">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
22
|
+
readonly _tag: "Found";
|
|
23
|
+
readonly startLine: number;
|
|
24
|
+
readonly endLine: number;
|
|
25
|
+
readonly content: string;
|
|
26
|
+
}, {
|
|
27
|
+
readonly _tag: Tag;
|
|
28
|
+
}> | Extract<{
|
|
29
|
+
readonly _tag: "Absent";
|
|
30
|
+
}, {
|
|
31
|
+
readonly _tag: Tag;
|
|
32
|
+
}>;
|
|
33
|
+
readonly $match: {
|
|
34
|
+
<Cases extends {
|
|
35
|
+
readonly Found: (args: {
|
|
36
|
+
readonly _tag: "Found";
|
|
37
|
+
readonly startLine: number;
|
|
38
|
+
readonly endLine: number;
|
|
39
|
+
readonly content: string;
|
|
40
|
+
}) => any;
|
|
41
|
+
readonly Absent: (args: {
|
|
42
|
+
readonly _tag: "Absent";
|
|
43
|
+
}) => any;
|
|
44
|
+
}>(cases: Cases): (value: {
|
|
45
|
+
readonly _tag: "Found";
|
|
46
|
+
readonly startLine: number;
|
|
47
|
+
readonly endLine: number;
|
|
48
|
+
readonly content: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly _tag: "Absent";
|
|
51
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Found" | "Absent"]>>;
|
|
52
|
+
<Cases extends {
|
|
53
|
+
readonly Found: (args: {
|
|
54
|
+
readonly _tag: "Found";
|
|
55
|
+
readonly startLine: number;
|
|
56
|
+
readonly endLine: number;
|
|
57
|
+
readonly content: string;
|
|
58
|
+
}) => any;
|
|
59
|
+
readonly Absent: (args: {
|
|
60
|
+
readonly _tag: "Absent";
|
|
61
|
+
}) => any;
|
|
62
|
+
}>(value: {
|
|
63
|
+
readonly _tag: "Found";
|
|
64
|
+
readonly startLine: number;
|
|
65
|
+
readonly endLine: number;
|
|
66
|
+
readonly content: string;
|
|
67
|
+
} | {
|
|
68
|
+
readonly _tag: "Absent";
|
|
69
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Found" | "Absent"]>>;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Find the existing region for `ownerId` inside `fileContent`. Returns its line range and
|
|
74
|
+
* content, or `{ kind: "absent" }`. A start marker without an end is a
|
|
75
|
+
* `pjt.region.missing-end` error; duplicate start markers are `pjt.region.duplicate`.
|
|
76
|
+
*/
|
|
77
|
+
export declare const findRegion: (params: {
|
|
78
|
+
readonly fileContent: string;
|
|
79
|
+
readonly ownerId: string;
|
|
80
|
+
readonly commentPrefix: string;
|
|
81
|
+
/**
|
|
82
|
+
* Optional closing delimiter for the comment style (e.g. `" -->"` for HTML/MDX). Defaults
|
|
83
|
+
* to the empty string so single-prefix styles (`#`, `//`) stay unchanged.
|
|
84
|
+
*/
|
|
85
|
+
readonly commentSuffix?: string;
|
|
86
|
+
readonly path: string;
|
|
87
|
+
}) => Effect.Effect<RegionLookup, Errors.RegionMissingEnd | Errors.RegionDuplicate>;
|
|
88
|
+
/**
|
|
89
|
+
* Render a region block (markers + content) ready to splice into a file.
|
|
90
|
+
*/
|
|
91
|
+
export declare const renderRegion: (params: {
|
|
92
|
+
readonly ownerId: string;
|
|
93
|
+
readonly commentPrefix: string;
|
|
94
|
+
readonly commentSuffix?: string;
|
|
95
|
+
readonly content: string;
|
|
96
|
+
}) => string;
|
|
97
|
+
/**
|
|
98
|
+
* Splice or append a region into a file's existing content. Returns the updated text.
|
|
99
|
+
*/
|
|
100
|
+
export declare const upsertRegion: (params: {
|
|
101
|
+
readonly fileContent: string;
|
|
102
|
+
readonly existing: RegionLookup;
|
|
103
|
+
readonly rendered: string;
|
|
104
|
+
}) => string;
|
|
105
|
+
//# sourceMappingURL=region.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"region.d.ts","sourceRoot":"","sources":["../src/region.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAYxC,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KACzB,CAAA;IAED,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAA;CACpB,CAAC,CAAA;AACF,eAAO,MAAM,YAAY;;;4BAPD,MAAM;0BACR,MAAM;0BACN,MAAM;;;;;;;4BAFJ,MAAM;0BACR,MAAM;0BACN,MAAM;;;;;;;;;;;;oCAFJ,MAAM;kCACR,MAAM;kCACN,MAAM;;;;;;;gCAFJ,MAAM;8BACR,MAAM;8BACN,MAAM;;;;;;;oCAFJ,MAAM;kCACR,MAAM;kCACN,MAAM;;;;;;;gCAFJ,MAAM;8BACR,MAAM;8BACN,MAAM;;;;;CAK+B,CAAA;AAE3D;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,KAAG,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,eAAe,CA4D/E,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB,KAAG,MAQH,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B,KAAG,MAWH,CAAA"}
|
package/dist/region.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Data, Effect, String } from "effect";
|
|
2
|
+
import { StringX } from "@nunofyobiz/effect-extras";
|
|
3
|
+
import { Errors } from "@projitect/core";
|
|
4
|
+
/**
|
|
5
|
+
* Render a region marker. The optional `suffix` covers comment styles that need a closing
|
|
6
|
+
* delimiter — HTML/MDX/XML need `-->`, everything else passes the empty string. Without a
|
|
7
|
+
* suffix the marker just runs to end-of-line (the `#` and `//` cases).
|
|
8
|
+
*/
|
|
9
|
+
const START = (prefix, owner, suffix = "") => `${prefix} ${owner} start${suffix}`;
|
|
10
|
+
const END = (prefix, owner, suffix = "") => `${prefix} ${owner} end${suffix}`;
|
|
11
|
+
export const RegionLookup = Data.taggedEnum();
|
|
12
|
+
/**
|
|
13
|
+
* Find the existing region for `ownerId` inside `fileContent`. Returns its line range and
|
|
14
|
+
* content, or `{ kind: "absent" }`. A start marker without an end is a
|
|
15
|
+
* `pjt.region.missing-end` error; duplicate start markers are `pjt.region.duplicate`.
|
|
16
|
+
*/
|
|
17
|
+
export const findRegion = (params) => {
|
|
18
|
+
const { fileContent, ownerId, commentPrefix, commentSuffix = "", path } = params;
|
|
19
|
+
return Effect.suspend(() => {
|
|
20
|
+
const lines = fileContent.split("\n");
|
|
21
|
+
const startMarker = START(commentPrefix, ownerId, commentSuffix);
|
|
22
|
+
const endMarker = END(commentPrefix, ownerId, commentSuffix);
|
|
23
|
+
let startLine = -1;
|
|
24
|
+
let endLine = -1;
|
|
25
|
+
let duplicateStart = false;
|
|
26
|
+
for (const [index, line_] of lines.entries()) {
|
|
27
|
+
const line = line_.trimEnd();
|
|
28
|
+
if (line === startMarker) {
|
|
29
|
+
if (startLine === -1) {
|
|
30
|
+
startLine = index;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
duplicateStart = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else if (line === endMarker) {
|
|
37
|
+
endLine = index;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (duplicateStart) {
|
|
41
|
+
return Effect.fail(new Errors.RegionDuplicate({
|
|
42
|
+
id: "pjt.region.duplicate",
|
|
43
|
+
path,
|
|
44
|
+
ownerId,
|
|
45
|
+
message: `Multiple start markers for region ${ownerId} in ${path}`,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
if (startLine === -1) {
|
|
49
|
+
return Effect.succeed(RegionLookup.Absent());
|
|
50
|
+
}
|
|
51
|
+
if (endLine === -1 || endLine < startLine) {
|
|
52
|
+
return Effect.fail(new Errors.RegionMissingEnd({
|
|
53
|
+
id: "pjt.region.missing-end",
|
|
54
|
+
path,
|
|
55
|
+
ownerId,
|
|
56
|
+
startLine,
|
|
57
|
+
message: `Region ${ownerId} started at line ${startLine + 1} of ${path} but never closed`,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
return Effect.succeed(RegionLookup.Found({
|
|
61
|
+
startLine,
|
|
62
|
+
endLine,
|
|
63
|
+
content: lines.slice(startLine + 1, endLine).join("\n"),
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Render a region block (markers + content) ready to splice into a file.
|
|
69
|
+
*/
|
|
70
|
+
export const renderRegion = (params) => {
|
|
71
|
+
const suffix = params.commentSuffix ?? "";
|
|
72
|
+
const body = params.content.endsWith("\n") ? params.content.slice(0, -1) : params.content;
|
|
73
|
+
return [
|
|
74
|
+
START(params.commentPrefix, params.ownerId, suffix),
|
|
75
|
+
body,
|
|
76
|
+
END(params.commentPrefix, params.ownerId, suffix),
|
|
77
|
+
].join("\n");
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Splice or append a region into a file's existing content. Returns the updated text.
|
|
81
|
+
*/
|
|
82
|
+
export const upsertRegion = (params) => {
|
|
83
|
+
const { fileContent, existing, rendered } = params;
|
|
84
|
+
if (existing._tag === "Absent") {
|
|
85
|
+
if (String.isEmpty(fileContent)) {
|
|
86
|
+
return `${rendered}\n`;
|
|
87
|
+
}
|
|
88
|
+
return fileContent.endsWith("\n")
|
|
89
|
+
? `${fileContent}${rendered}\n`
|
|
90
|
+
: `${fileContent}\n${rendered}\n`;
|
|
91
|
+
}
|
|
92
|
+
return StringX.replaceLineRange(fileContent, existing.startLine, existing.endLine, [rendered]);
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=region.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"region.js","sourceRoot":"","sources":["../src/region.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAExC;;;;GAIG;AACH,MAAM,KAAK,GAAG,CAAC,MAAc,EAAE,KAAa,EAAE,MAAM,GAAG,EAAE,EAAU,EAAE,CACnE,GAAG,MAAM,IAAI,KAAK,SAAS,MAAM,EAAE,CAAA;AACrC,MAAM,GAAG,GAAG,CAAC,MAAc,EAAE,KAAa,EAAE,MAAM,GAAG,EAAE,EAAU,EAAE,CACjE,GAAG,MAAM,IAAI,KAAK,OAAO,MAAM,EAAE,CAAA;AAWnC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,EAAgB,CAAA;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAU1B,EAAiF,EAAE;IAClF,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,CAAA;IAEhF,OAAO,MAAM,CAAC,OAAO,CAAC,GAAW,EAAE;QACjC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,WAAW,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,EAAE,aAAa,CAAC,CAAA;QAChE,MAAM,SAAS,GAAG,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,aAAa,CAAC,CAAA;QAE5D,IAAI,SAAS,GAAG,CAAC,CAAC,CAAA;QAClB,IAAI,OAAO,GAAG,CAAC,CAAC,CAAA;QAChB,IAAI,cAAc,GAAG,KAAK,CAAA;QAE1B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAA;YAC5B,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;gBACzB,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;oBACrB,SAAS,GAAG,KAAK,CAAA;gBACnB,CAAC;qBAAM,CAAC;oBACN,cAAc,GAAG,IAAI,CAAA;gBACvB,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC9B,OAAO,GAAG,KAAK,CAAA;YACjB,CAAC;QACH,CAAC;QAED,IAAI,cAAc,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,MAAM,CAAC,eAAe,CAAC;gBACzB,EAAE,EAAE,sBAAsB;gBAC1B,IAAI;gBACJ,OAAO;gBACP,OAAO,EAAE,qCAAqC,OAAO,OAAO,IAAI,EAAE;aACnE,CAAC,CACH,CAAA;QACH,CAAC;QAED,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;YACrB,OAAO,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAA;QAC9C,CAAC;QAED,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,GAAG,SAAS,EAAE,CAAC;YAC1C,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,MAAM,CAAC,gBAAgB,CAAC;gBAC1B,EAAE,EAAE,wBAAwB;gBAC5B,IAAI;gBACJ,OAAO;gBACP,SAAS;gBACT,OAAO,EAAE,UAAU,OAAO,oBAAoB,SAAS,GAAG,CAAC,OAAO,IAAI,mBAAmB;aAC1F,CAAC,CACH,CAAA;QACH,CAAC;QAED,OAAO,MAAM,CAAC,OAAO,CACnB,YAAY,CAAC,KAAK,CAAC;YACjB,SAAS;YACT,OAAO;YACP,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SACxD,CAAC,CACH,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAK5B,EAAU,EAAE;IACX,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,IAAI,EAAE,CAAA;IACzC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAA;IACzF,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;QACnD,IAAI;QACJ,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;KAClD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAI5B,EAAU,EAAE;IACX,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAA;IAClD,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,GAAG,QAAQ,IAAI,CAAA;QACxB,CAAC;QACD,OAAO,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC/B,CAAC,CAAC,GAAG,WAAW,GAAG,QAAQ,IAAI;YAC/B,CAAC,CAAC,GAAG,WAAW,KAAK,QAAQ,IAAI,CAAA;IACrC,CAAC;IACD,OAAO,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;AAChG,CAAC,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { BlueprintFileSystem } from "@projitect/core";
|
|
3
|
+
import type { Errors, PjtLock } from "@projitect/core";
|
|
4
|
+
/**
|
|
5
|
+
* Apply removal operations through the project `BlueprintFileSystem`. Used by `pjt remodel` / `pjt build` after a blueprint
|
|
6
|
+
* leaves `.pjt.ts` — the operations come from the diff between the previous lockfile and the
|
|
7
|
+
* current blueprint set.
|
|
8
|
+
*
|
|
9
|
+
* Returns the list of paths actually mutated.
|
|
10
|
+
*/
|
|
11
|
+
export declare const applyRemovals: (params: {
|
|
12
|
+
readonly removals: readonly PjtLock.LockOperation[];
|
|
13
|
+
}) => Effect.Effect<readonly string[], RemoveError, BlueprintFileSystem>;
|
|
14
|
+
type RemoveError = Errors.RegionMissingEnd | Errors.RegionDuplicate | Errors.FsWriteFailed | Errors.FsPermissionDenied;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=remover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remover.d.ts","sourceRoot":"","sources":["../src/remover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAA4B,MAAM,QAAQ,CAAA;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAItD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,CAAC,aAAa,EAAE,CAAA;CACpD,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,mBAAmB,CAGjC,CAAA;AAEpC,KAAK,WAAW,GACZ,MAAM,CAAC,gBAAgB,GACvB,MAAM,CAAC,eAAe,GACtB,MAAM,CAAC,aAAa,GACpB,MAAM,CAAC,kBAAkB,CAAA"}
|
package/dist/remover.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Array, Effect, Match, Option, Predicate } from "effect";
|
|
2
|
+
import { PredicateX, RecordX, StringX } from "@nunofyobiz/effect-extras";
|
|
3
|
+
import { BlueprintFileSystem } from "@projitect/core";
|
|
4
|
+
import { readIfExists } from "./project-file.js";
|
|
5
|
+
import { findRegion } from "./region.js";
|
|
6
|
+
/**
|
|
7
|
+
* Apply removal operations through the project `BlueprintFileSystem`. Used by `pjt remodel` / `pjt build` after a blueprint
|
|
8
|
+
* leaves `.pjt.ts` — the operations come from the diff between the previous lockfile and the
|
|
9
|
+
* current blueprint set.
|
|
10
|
+
*
|
|
11
|
+
* Returns the list of paths actually mutated.
|
|
12
|
+
*/
|
|
13
|
+
export const applyRemovals = (params) => Effect.forEach(params.removals, (op) => Effect.map(applyOne(op), (mutated) => (mutated ? Option.some(op.path) : Option.none()))).pipe(Effect.map(Array.getSomes));
|
|
14
|
+
const applyOne = (op) => Match.valueTags(op, {
|
|
15
|
+
Region: (region) => removeRegion(region),
|
|
16
|
+
Merge: (merge) => removeMergeKeys(merge.path, merge.ownedKeys),
|
|
17
|
+
Owned: (owned) => removeFile(owned.path, owned.ownedDir),
|
|
18
|
+
// Seed is write-once; we never delete the file when a seed blueprint leaves.
|
|
19
|
+
Seed: () => Effect.succeed(false),
|
|
20
|
+
});
|
|
21
|
+
const removeRegion = (op) => Effect.gen(function* () {
|
|
22
|
+
const fs = yield* BlueprintFileSystem;
|
|
23
|
+
const current = yield* readIfExists(op.path);
|
|
24
|
+
if (current === null) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const found = yield* findRegion({
|
|
28
|
+
fileContent: current,
|
|
29
|
+
ownerId: op.ownerId,
|
|
30
|
+
commentPrefix: op.commentPrefix,
|
|
31
|
+
commentSuffix: op.commentSuffix ?? "",
|
|
32
|
+
path: op.path,
|
|
33
|
+
});
|
|
34
|
+
if (found._tag === "Absent") {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const next = StringX.replaceLineRange(current, found.startLine, found.endLine, []);
|
|
38
|
+
yield* fs.writeFile(op.path, next);
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
const removeMergeKeys = (file, keys) => Effect.gen(function* () {
|
|
42
|
+
const fs = yield* BlueprintFileSystem;
|
|
43
|
+
const raw = yield* readIfExists(file);
|
|
44
|
+
if (raw === null) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const parsed = yield* Effect.try(() => JSON.parse(raw)).pipe(Effect.option);
|
|
48
|
+
if (Option.isNone(parsed) || !PredicateX.unsafeIsRecord(parsed.value)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
// Fold each dotted key out of the tree, tracking whether anything actually changed.
|
|
52
|
+
const seed = {
|
|
53
|
+
tree: parsed.value,
|
|
54
|
+
mutated: false,
|
|
55
|
+
};
|
|
56
|
+
const result = Array.reduce(keys, seed, (state, key) => Option.match(RecordX.deleteByPath(state.tree, key.split(".")), {
|
|
57
|
+
onSome: (tree) => ({ tree, mutated: true }),
|
|
58
|
+
onNone: () => state,
|
|
59
|
+
}));
|
|
60
|
+
if (!result.mutated) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
yield* fs.writeFile(file, `${JSON.stringify(result.tree, null, 2)}\n`);
|
|
64
|
+
return true;
|
|
65
|
+
});
|
|
66
|
+
const removeFile = (file, ownedDir) => Effect.gen(function* () {
|
|
67
|
+
const fs = yield* BlueprintFileSystem;
|
|
68
|
+
yield* fs.remove(file);
|
|
69
|
+
if (Predicate.isNotUndefined(ownedDir)) {
|
|
70
|
+
yield* pruneEmptyDirs(parentDir(file), ownedDir);
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}).pipe(Effect.orElseSucceed(() => false));
|
|
74
|
+
// Project-relative paths use `/`; the parent of a top-level name is the project root (`""`).
|
|
75
|
+
const parentDir = (file) => file.slice(0, Math.max(0, file.lastIndexOf("/")));
|
|
76
|
+
/** Remove empty directories upward from `dir` to `ownedDir` inclusive, never above it. */
|
|
77
|
+
const pruneEmptyDirs = (dir, ownedDir) => Effect.gen(function* () {
|
|
78
|
+
if (dir !== ownedDir && !dir.startsWith(`${ownedDir}/`)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const fs = yield* BlueprintFileSystem;
|
|
82
|
+
// Atomic: fails (and stops the climb) if `dir` holds any entry, however recently it appeared.
|
|
83
|
+
yield* fs.removeEmptyDir(dir);
|
|
84
|
+
if (dir !== ownedDir) {
|
|
85
|
+
yield* pruneEmptyDirs(parentDir(dir), ownedDir);
|
|
86
|
+
}
|
|
87
|
+
}).pipe(Effect.orElseSucceed(() => undefined));
|
|
88
|
+
//# sourceMappingURL=remover.js.map
|