@confect/cli 9.0.0-next.0 → 9.0.0-next.10
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 +351 -1
- package/dist/BuildError.mjs +9 -2
- package/dist/BuildError.mjs.map +1 -1
- package/dist/Bundler.mjs +67 -57
- package/dist/Bundler.mjs.map +1 -1
- package/dist/CodeBlockWriter.mjs +1 -1
- package/dist/CodeBlockWriter.mjs.map +1 -1
- package/dist/CodegenError.mjs +36 -21
- package/dist/CodegenError.mjs.map +1 -1
- package/dist/ConfectDirectory.mjs +5 -2
- package/dist/ConfectDirectory.mjs.map +1 -1
- package/dist/ConvexDirectory.mjs +6 -2
- package/dist/ConvexDirectory.mjs.map +1 -1
- package/dist/FunctionPath.mjs +1 -1
- package/dist/FunctionPath.mjs.map +1 -1
- package/dist/FunctionPaths.mjs +5 -1
- package/dist/FunctionPaths.mjs.map +1 -1
- package/dist/GroupPath.mjs +9 -2
- package/dist/GroupPath.mjs.map +1 -1
- package/dist/GroupPaths.mjs +1 -1
- package/dist/GroupPaths.mjs.map +1 -1
- package/dist/LeafModule.mjs +20 -24
- package/dist/LeafModule.mjs.map +1 -1
- package/dist/ProjectRoot.mjs +8 -3
- package/dist/ProjectRoot.mjs.map +1 -1
- package/dist/SpecAssemblyNode.mjs +9 -11
- package/dist/SpecAssemblyNode.mjs.map +1 -1
- package/dist/TableModule.mjs +94 -0
- package/dist/TableModule.mjs.map +1 -0
- package/dist/cliApp.mjs +1 -1
- package/dist/cliApp.mjs.map +1 -1
- package/dist/confect/codegen.mjs +272 -141
- package/dist/confect/codegen.mjs.map +1 -1
- package/dist/confect/dev.mjs +36 -17
- package/dist/confect/dev.mjs.map +1 -1
- package/dist/confect.mjs +2 -2
- package/dist/confect.mjs.map +1 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/log.mjs +9 -4
- package/dist/log.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/templates.mjs +139 -48
- package/dist/templates.mjs.map +1 -1
- package/dist/utils.mjs +42 -22
- package/dist/utils.mjs.map +1 -1
- package/package.json +33 -50
- package/dist/index.d.mts +0 -1
- package/src/BuildError.ts +0 -210
- package/src/Bundler.ts +0 -144
- package/src/CodeBlockWriter.ts +0 -65
- package/src/CodegenError.ts +0 -344
- package/src/ConfectDirectory.ts +0 -42
- package/src/ConvexDirectory.ts +0 -68
- package/src/FunctionPath.ts +0 -27
- package/src/FunctionPaths.ts +0 -103
- package/src/GroupPath.ts +0 -118
- package/src/GroupPaths.ts +0 -7
- package/src/LeafModule.ts +0 -313
- package/src/ProjectRoot.ts +0 -50
- package/src/SpecAssemblyNode.ts +0 -82
- package/src/cliApp.ts +0 -8
- package/src/confect/codegen.ts +0 -589
- package/src/confect/dev.ts +0 -749
- package/src/confect.ts +0 -19
- package/src/index.ts +0 -22
- package/src/log.ts +0 -104
- package/src/templates.ts +0 -477
- package/src/utils.ts +0 -429
package/package.json
CHANGED
|
@@ -1,42 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@confect/cli",
|
|
3
|
-
"version": "9.0.0-next.0",
|
|
4
3
|
"description": "Developer tooling for codegen and sync",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"url": "https://github.com/rjdellecese/confect.git"
|
|
8
|
-
},
|
|
9
|
-
"bugs": {
|
|
10
|
-
"url": "https://github.com/rjdellecese/confect/issues"
|
|
11
|
-
},
|
|
12
|
-
"homepage": "https://confect.dev",
|
|
13
|
-
"sideEffects": false,
|
|
14
|
-
"type": "module",
|
|
4
|
+
"version": "9.0.0-next.10",
|
|
5
|
+
"author": "RJ Dellecese",
|
|
15
6
|
"bin": {
|
|
16
7
|
"confect": "./dist/index.mjs"
|
|
17
8
|
},
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"LICENSE",
|
|
21
|
-
"README.md",
|
|
22
|
-
"dist",
|
|
23
|
-
"package.json",
|
|
24
|
-
"src"
|
|
25
|
-
],
|
|
26
|
-
"exports": {
|
|
27
|
-
".": {
|
|
28
|
-
"types": "./dist/index.d.ts",
|
|
29
|
-
"default": "./dist/index.js"
|
|
30
|
-
},
|
|
31
|
-
"./package.json": "./package.json"
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/rjdellecese/confect/issues"
|
|
32
11
|
},
|
|
33
|
-
"keywords": [
|
|
34
|
-
"effect",
|
|
35
|
-
"convex",
|
|
36
|
-
"cli"
|
|
37
|
-
],
|
|
38
|
-
"author": "RJ Dellecese",
|
|
39
|
-
"license": "ISC",
|
|
40
12
|
"dependencies": {
|
|
41
13
|
"@effect/cli": "^0.75.1",
|
|
42
14
|
"@effect/platform": "0.96.1",
|
|
@@ -44,45 +16,56 @@
|
|
|
44
16
|
"@effect/platform-node-shared": "0.59.0",
|
|
45
17
|
"@effect/printer": "^0.49.0",
|
|
46
18
|
"@effect/printer-ansi": "^0.49.0",
|
|
19
|
+
"bundle-require": "^5.1.0",
|
|
47
20
|
"code-block-writer": "^13.0.3",
|
|
48
21
|
"esbuild": "^0.27.3"
|
|
49
22
|
},
|
|
50
23
|
"devDependencies": {
|
|
51
24
|
"@effect/language-service": "0.86.1",
|
|
52
25
|
"@effect/vitest": "0.29.0",
|
|
53
|
-
"@eslint/js": "10.0.1",
|
|
54
26
|
"@types/node": "25.3.3",
|
|
55
27
|
"@vitest/coverage-v8": "3.2.4",
|
|
56
28
|
"effect": "3.21.2",
|
|
57
|
-
"eslint": "10.0.2",
|
|
58
|
-
"prettier": "3.8.1",
|
|
59
29
|
"tsdown": "0.20.3",
|
|
60
30
|
"typescript": "5.9.3",
|
|
61
|
-
"typescript-eslint": "8.56.1",
|
|
62
31
|
"vite": "7.3.1",
|
|
63
32
|
"vite-tsconfig-paths": "6.1.1",
|
|
64
33
|
"vitest": "3.2.4"
|
|
65
34
|
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22"
|
|
37
|
+
},
|
|
38
|
+
"exports": {
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"LICENSE",
|
|
44
|
+
"README.md",
|
|
45
|
+
"dist",
|
|
46
|
+
"package.json"
|
|
47
|
+
],
|
|
48
|
+
"homepage": "https://confect.dev",
|
|
49
|
+
"keywords": [
|
|
50
|
+
"cli",
|
|
51
|
+
"convex",
|
|
52
|
+
"effect"
|
|
53
|
+
],
|
|
54
|
+
"license": "ISC",
|
|
66
55
|
"peerDependencies": {
|
|
67
56
|
"effect": "^3.21.2",
|
|
68
|
-
"@confect/core": "^9.0.0-next.
|
|
69
|
-
"@confect/server": "^9.0.0-next.
|
|
57
|
+
"@confect/core": "^9.0.0-next.10",
|
|
58
|
+
"@confect/server": "^9.0.0-next.10"
|
|
70
59
|
},
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/rjdellecese/confect.git"
|
|
74
63
|
},
|
|
75
|
-
"
|
|
76
|
-
"module": "./dist/index.js",
|
|
77
|
-
"types": "./dist/index.d.ts",
|
|
64
|
+
"type": "module",
|
|
78
65
|
"scripts": {
|
|
79
|
-
"
|
|
66
|
+
"clean": "rm -rf dist coverage node_modules",
|
|
80
67
|
"dev": "tsdown --watch --config-loader unrun",
|
|
81
68
|
"test": "vitest run",
|
|
82
|
-
"typecheck": "tsc --noEmit --project tsconfig.json"
|
|
83
|
-
"fix": "prettier --write . && eslint --fix . --max-warnings=0",
|
|
84
|
-
"format": "prettier --check .",
|
|
85
|
-
"lint": "eslint . --max-warnings=0",
|
|
86
|
-
"clean": "rm -rf dist coverage node_modules"
|
|
69
|
+
"typecheck": "tsc --noEmit --project tsconfig.json"
|
|
87
70
|
}
|
|
88
71
|
}
|
package/dist/index.d.mts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
package/src/BuildError.ts
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { Ansi, AnsiDoc } from "@effect/printer-ansi";
|
|
2
|
-
import { Array, Effect, Match, Option, pipe, Schema, String } from "effect";
|
|
3
|
-
import * as esbuild from "esbuild";
|
|
4
|
-
import { formatPathDoc } from "./log";
|
|
5
|
-
|
|
6
|
-
// --- Variants ---
|
|
7
|
-
|
|
8
|
-
export class BundleFailedError extends Schema.TaggedError<BundleFailedError>()(
|
|
9
|
-
"BundleFailedError",
|
|
10
|
-
{
|
|
11
|
-
file: Schema.String,
|
|
12
|
-
errors: Schema.Array(Schema.Unknown),
|
|
13
|
-
},
|
|
14
|
-
) {}
|
|
15
|
-
|
|
16
|
-
export class ImportFailedError extends Schema.TaggedError<ImportFailedError>()(
|
|
17
|
-
"ImportFailedError",
|
|
18
|
-
{
|
|
19
|
-
file: Schema.String,
|
|
20
|
-
cause: Schema.Unknown,
|
|
21
|
-
},
|
|
22
|
-
) {}
|
|
23
|
-
|
|
24
|
-
export const BuildError = Schema.Union(BundleFailedError, ImportFailedError);
|
|
25
|
-
export type BuildError = typeof BuildError.Type;
|
|
26
|
-
|
|
27
|
-
export const isBuildError = (error: unknown): error is BuildError =>
|
|
28
|
-
Schema.is(BuildError)(error);
|
|
29
|
-
|
|
30
|
-
// --- Bundler adapter ---
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Internal failure produced by the esbuild bundle/import pipeline. Always
|
|
34
|
-
* remapped to a {@link BuildError} (which carries enough context for the CLI
|
|
35
|
-
* to render it) before reaching a user-surface boundary.
|
|
36
|
-
*/
|
|
37
|
-
export class BundlerError extends Schema.TaggedError<BundlerError>()(
|
|
38
|
-
"BundlerError",
|
|
39
|
-
{
|
|
40
|
-
cause: Schema.Unknown,
|
|
41
|
-
},
|
|
42
|
-
) {}
|
|
43
|
-
|
|
44
|
-
const isEsbuildBuildFailure = (error: unknown): error is esbuild.BuildFailure =>
|
|
45
|
-
typeof error === "object" &&
|
|
46
|
-
error !== null &&
|
|
47
|
-
"errors" in error &&
|
|
48
|
-
globalThis.Array.isArray((error as esbuild.BuildFailure).errors);
|
|
49
|
-
|
|
50
|
-
export const fromBundlerError = (
|
|
51
|
-
file: string,
|
|
52
|
-
error: BundlerError,
|
|
53
|
-
): BuildError =>
|
|
54
|
-
isEsbuildBuildFailure(error.cause)
|
|
55
|
-
? new BundleFailedError({ file, errors: error.cause.errors })
|
|
56
|
-
: new ImportFailedError({ file, cause: error.cause });
|
|
57
|
-
|
|
58
|
-
// --- Rendering ---
|
|
59
|
-
|
|
60
|
-
const cross = pipe(AnsiDoc.char("✘"), AnsiDoc.annotate(Ansi.red));
|
|
61
|
-
|
|
62
|
-
const errorGutter = pipe(
|
|
63
|
-
AnsiDoc.char("│"),
|
|
64
|
-
AnsiDoc.annotate(Ansi.red),
|
|
65
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const withErrorGutterBlock = (output: string): string =>
|
|
69
|
-
pipe(
|
|
70
|
-
String.split(output, "\n"),
|
|
71
|
-
Array.map((line) =>
|
|
72
|
-
pipe(line, String.trim) === "" ? errorGutter : `${errorGutter} ${line}`,
|
|
73
|
-
),
|
|
74
|
-
Array.join("\n"),
|
|
75
|
-
(guttered) => `${errorGutter}\n${guttered}\n${errorGutter}`,
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const formatBuildMessage = (
|
|
79
|
-
error: esbuild.Message | undefined,
|
|
80
|
-
formattedMessage: string,
|
|
81
|
-
): string => {
|
|
82
|
-
const lines = String.split(formattedMessage, "\n");
|
|
83
|
-
const redErrorText = pipe(
|
|
84
|
-
AnsiDoc.text(error?.text ?? ""),
|
|
85
|
-
AnsiDoc.annotate(Ansi.red),
|
|
86
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
87
|
-
);
|
|
88
|
-
const replaced = pipe(
|
|
89
|
-
Array.findFirstIndex(lines, (l) => pipe(l, String.trim, String.isNonEmpty)),
|
|
90
|
-
Option.match({
|
|
91
|
-
onNone: () => lines,
|
|
92
|
-
onSome: (index) => Array.modify(lines, index, () => redErrorText),
|
|
93
|
-
}),
|
|
94
|
-
);
|
|
95
|
-
return pipe(replaced, Array.join("\n"));
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Render a list of esbuild messages into a styled, gutter-prefixed block.
|
|
100
|
-
* Used by both {@link renderBundleFailedError} and the dev-mode esbuild
|
|
101
|
-
* watcher's `onEnd` hook (where the messages don't flow through the
|
|
102
|
-
* tagged-error pipeline).
|
|
103
|
-
*/
|
|
104
|
-
export const formatEsbuildMessages = (
|
|
105
|
-
errors: readonly esbuild.Message[],
|
|
106
|
-
formattedMessages: readonly string[],
|
|
107
|
-
): string =>
|
|
108
|
-
pipe(
|
|
109
|
-
formattedMessages,
|
|
110
|
-
Array.map((message, i) => formatBuildMessage(errors[i], message)),
|
|
111
|
-
Array.join(""),
|
|
112
|
-
String.trimEnd,
|
|
113
|
-
withErrorGutterBlock,
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const renderImportFailedError = (error: ImportFailedError): AnsiDoc.AnsiDoc => {
|
|
117
|
-
const causeMessage =
|
|
118
|
-
error.cause instanceof Error
|
|
119
|
-
? error.cause.message
|
|
120
|
-
: typeof error.cause === "string"
|
|
121
|
-
? error.cause
|
|
122
|
-
: globalThis.String(error.cause);
|
|
123
|
-
const oneLineCause = pipe(
|
|
124
|
-
String.split(causeMessage, "\n"),
|
|
125
|
-
Array.findFirst((line) => pipe(line, String.trim, String.isNonEmpty)),
|
|
126
|
-
Option.map(String.trim),
|
|
127
|
-
Option.getOrElse(() => "unknown error"),
|
|
128
|
-
);
|
|
129
|
-
return pipe(
|
|
130
|
-
cross,
|
|
131
|
-
AnsiDoc.catWithSpace(
|
|
132
|
-
AnsiDoc.hcat([
|
|
133
|
-
AnsiDoc.text("Failed to load bundled module "),
|
|
134
|
-
formatPathDoc(error.file),
|
|
135
|
-
AnsiDoc.text(
|
|
136
|
-
`: ${oneLineCause}; check the file's top-level imports and side effects.`,
|
|
137
|
-
),
|
|
138
|
-
]),
|
|
139
|
-
),
|
|
140
|
-
);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Render a {@link BundleFailedError} as a multi-line, ANSI-styled string:
|
|
145
|
-
* a one-line `✘ <file>: build errors` header followed by the
|
|
146
|
-
* gutter-prefixed esbuild diagnostic block. Multi-line is appropriate
|
|
147
|
-
* here because a single `BundleFailedError` carries an array of distinct
|
|
148
|
-
* esbuild messages.
|
|
149
|
-
*/
|
|
150
|
-
const renderBundleFailedError = (error: BundleFailedError): string => {
|
|
151
|
-
const messages = error.errors as readonly esbuild.Message[];
|
|
152
|
-
const formatted = esbuild.formatMessagesSync(messages as esbuild.Message[], {
|
|
153
|
-
kind: "error",
|
|
154
|
-
color: true,
|
|
155
|
-
terminalWidth: 80,
|
|
156
|
-
});
|
|
157
|
-
const header = pipe(
|
|
158
|
-
cross,
|
|
159
|
-
AnsiDoc.catWithSpace(
|
|
160
|
-
AnsiDoc.hcat([formatPathDoc(error.file), AnsiDoc.text(": build errors")]),
|
|
161
|
-
),
|
|
162
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
163
|
-
);
|
|
164
|
-
return `${header}\n${formatEsbuildMessages(messages, formatted)}`;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Render any {@link BuildError} into a styled, ready-to-print string.
|
|
169
|
-
* `ImportFailedError` collapses to a single line; `BundleFailedError`
|
|
170
|
-
* expands to a header plus one diagnostic block per esbuild message.
|
|
171
|
-
*/
|
|
172
|
-
export const renderBuildError = (error: BuildError): string =>
|
|
173
|
-
Match.value(error).pipe(
|
|
174
|
-
Match.tag("BundleFailedError", renderBundleFailedError),
|
|
175
|
-
Match.tag("ImportFailedError", (e) =>
|
|
176
|
-
pipe(renderImportFailedError(e), AnsiDoc.render({ style: "pretty" })),
|
|
177
|
-
),
|
|
178
|
-
Match.exhaustive,
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
export const logBuildError = (error: BuildError) =>
|
|
182
|
-
Effect.sync(() => console.error(renderBuildError(error)));
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Render a flat list of esbuild messages as a single error block with a
|
|
186
|
-
* generic `✘ Build errors` header. Used by dev-mode when multiple
|
|
187
|
-
* watchers surface the same underlying error and we want to log the
|
|
188
|
-
* coalesced set rather than one block per watcher.
|
|
189
|
-
*/
|
|
190
|
-
const renderCoalescedBuildErrors = (
|
|
191
|
-
messages: readonly esbuild.Message[],
|
|
192
|
-
): string => {
|
|
193
|
-
const formatted = esbuild.formatMessagesSync(messages as esbuild.Message[], {
|
|
194
|
-
kind: "error",
|
|
195
|
-
color: true,
|
|
196
|
-
terminalWidth: 80,
|
|
197
|
-
});
|
|
198
|
-
const header = pipe(
|
|
199
|
-
cross,
|
|
200
|
-
AnsiDoc.catWithSpace(AnsiDoc.text("Build errors")),
|
|
201
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
202
|
-
);
|
|
203
|
-
return `${header}\n${formatEsbuildMessages(messages, formatted)}`;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
export const logCoalescedBuildErrors = (messages: readonly esbuild.Message[]) =>
|
|
207
|
-
Effect.sync(() => {
|
|
208
|
-
if (messages.length === 0) return;
|
|
209
|
-
console.error(renderCoalescedBuildErrors(messages));
|
|
210
|
-
});
|
package/src/Bundler.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { pathToFileURL } from "node:url";
|
|
3
|
-
import { Path } from "@effect/platform";
|
|
4
|
-
import { Array, Effect, Option, pipe } from "effect";
|
|
5
|
-
import * as esbuild from "esbuild";
|
|
6
|
-
import { BundlerError } from "./BuildError";
|
|
7
|
-
|
|
8
|
-
export interface Bundled {
|
|
9
|
-
readonly module: any;
|
|
10
|
-
readonly metafile: esbuild.Metafile;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const EXTERNAL_PACKAGES = [
|
|
14
|
-
"@confect/core",
|
|
15
|
-
"@confect/server",
|
|
16
|
-
"effect",
|
|
17
|
-
"@effect/*",
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
const isExternalImport = (path: string) =>
|
|
21
|
-
EXTERNAL_PACKAGES.some((p) => {
|
|
22
|
-
if (p.endsWith("/*")) {
|
|
23
|
-
return path.startsWith(p.slice(0, -1));
|
|
24
|
-
}
|
|
25
|
-
return path === p || path.startsWith(p + "/");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export const absoluteExternalsPlugin: esbuild.Plugin = {
|
|
29
|
-
name: "absolute-externals",
|
|
30
|
-
setup(build) {
|
|
31
|
-
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
32
|
-
if (args.kind !== "import-statement" && args.kind !== "dynamic-import")
|
|
33
|
-
return;
|
|
34
|
-
if (!isExternalImport(args.path)) return;
|
|
35
|
-
// `import.meta.resolve`'s second argument is silently ignored in modern
|
|
36
|
-
// Node, so resolution would always walk up from the CLI's bundled file
|
|
37
|
-
// (`packages/cli/dist/utils.mjs`) instead of from the user's project.
|
|
38
|
-
// Use `createRequire` keyed on the importing file's directory so we
|
|
39
|
-
// resolve out of *their* `node_modules`. The synthetic filename is just
|
|
40
|
-
// a CommonJS resolution anchor; the file does not need to exist.
|
|
41
|
-
const parentFile = pathToFileURL(args.resolveDir + "/_").href;
|
|
42
|
-
const require_ = createRequire(parentFile);
|
|
43
|
-
const resolvedPath = require_.resolve(args.path);
|
|
44
|
-
const resolved = pathToFileURL(resolvedPath).href;
|
|
45
|
-
return { path: resolved, external: true };
|
|
46
|
-
});
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const buildEntry = (entryPoint: string) =>
|
|
51
|
-
Effect.tryPromise({
|
|
52
|
-
try: () =>
|
|
53
|
-
esbuild.build({
|
|
54
|
-
entryPoints: [entryPoint],
|
|
55
|
-
bundle: true,
|
|
56
|
-
write: false,
|
|
57
|
-
platform: "node",
|
|
58
|
-
format: "esm",
|
|
59
|
-
logLevel: "silent",
|
|
60
|
-
metafile: true,
|
|
61
|
-
plugins: [absoluteExternalsPlugin],
|
|
62
|
-
}),
|
|
63
|
-
catch: (cause) => new BundlerError({ cause }),
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const importBundledModule = (result: esbuild.BuildResult) => {
|
|
67
|
-
const code = result.outputFiles![0]!.text;
|
|
68
|
-
const dataUrl =
|
|
69
|
-
"data:text/javascript;base64," + Buffer.from(code).toString("base64");
|
|
70
|
-
return import(dataUrl);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Bundle a TypeScript entry point with esbuild and import the result via a
|
|
75
|
-
* data URL. This handles extensionless `.ts` imports regardless of whether
|
|
76
|
-
* the user's project sets `"type": "module"` in package.json. The returned
|
|
77
|
-
* pair carries both the imported module and the esbuild metafile so callers
|
|
78
|
-
* can inspect the import graph (see {@link directlyImports}).
|
|
79
|
-
*/
|
|
80
|
-
export const bundle = (
|
|
81
|
-
entryPoint: string,
|
|
82
|
-
): Effect.Effect<Bundled, BundlerError> =>
|
|
83
|
-
Effect.gen(function* () {
|
|
84
|
-
const result = yield* buildEntry(entryPoint);
|
|
85
|
-
const module = yield* Effect.tryPromise({
|
|
86
|
-
try: () => importBundledModule(result),
|
|
87
|
-
catch: (cause) => new BundlerError({ cause }),
|
|
88
|
-
});
|
|
89
|
-
if (!result.metafile) {
|
|
90
|
-
return yield* Effect.dieMessage("esbuild metafile missing");
|
|
91
|
-
}
|
|
92
|
-
return { module, metafile: result.metafile };
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const findMetafileInputKey = (
|
|
96
|
-
metafile: esbuild.Metafile,
|
|
97
|
-
absolutePath: string,
|
|
98
|
-
) =>
|
|
99
|
-
Effect.gen(function* () {
|
|
100
|
-
const path = yield* Path.Path;
|
|
101
|
-
const resolved = path.resolve(absolutePath);
|
|
102
|
-
return Array.findFirst(
|
|
103
|
-
Object.keys(metafile.inputs),
|
|
104
|
-
(key) => path.resolve(key) === resolved,
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Returns `true` when the module bundled from `sourceAbsolutePath` declares a
|
|
110
|
-
* direct import of `targetAbsolutePath` (according to the bundle's esbuild
|
|
111
|
-
* metafile). Returns `false` if either path is missing from the metafile.
|
|
112
|
-
*/
|
|
113
|
-
export const directlyImports = (
|
|
114
|
-
bundled: Bundled,
|
|
115
|
-
sourceAbsolutePath: string,
|
|
116
|
-
targetAbsolutePath: string,
|
|
117
|
-
) =>
|
|
118
|
-
Effect.gen(function* () {
|
|
119
|
-
const path = yield* Path.Path;
|
|
120
|
-
const sourceKey = yield* findMetafileInputKey(
|
|
121
|
-
bundled.metafile,
|
|
122
|
-
sourceAbsolutePath,
|
|
123
|
-
);
|
|
124
|
-
const targetKey = yield* findMetafileInputKey(
|
|
125
|
-
bundled.metafile,
|
|
126
|
-
targetAbsolutePath,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
return pipe(
|
|
130
|
-
Option.all([sourceKey, targetKey]),
|
|
131
|
-
Option.flatMap(([sourceKey_, targetKey_]) =>
|
|
132
|
-
Option.fromNullable(bundled.metafile.inputs[sourceKey_]).pipe(
|
|
133
|
-
Option.map((sourceInput) => {
|
|
134
|
-
const targetResolved = path.resolve(targetKey_);
|
|
135
|
-
return sourceInput.imports.some(
|
|
136
|
-
(importedFile) =>
|
|
137
|
-
path.resolve(importedFile.path) === targetResolved,
|
|
138
|
-
);
|
|
139
|
-
}),
|
|
140
|
-
),
|
|
141
|
-
),
|
|
142
|
-
Option.getOrElse(() => false),
|
|
143
|
-
);
|
|
144
|
-
});
|
package/src/CodeBlockWriter.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { Options as CodeBlockWriterOptions } from "code-block-writer";
|
|
2
|
-
import CodeBlockWriter_ from "code-block-writer";
|
|
3
|
-
import { Effect } from "effect";
|
|
4
|
-
|
|
5
|
-
export class CodeBlockWriter {
|
|
6
|
-
private readonly writer: CodeBlockWriter_;
|
|
7
|
-
|
|
8
|
-
constructor(opts?: Partial<CodeBlockWriterOptions>) {
|
|
9
|
-
this.writer = new CodeBlockWriter_(opts);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
indent<E = never, R = never>(
|
|
13
|
-
effect: Effect.Effect<void, E, R>,
|
|
14
|
-
): Effect.Effect<void, E, R> {
|
|
15
|
-
return Effect.gen(this, function* () {
|
|
16
|
-
const indentationLevel = this.writer.getIndentationLevel();
|
|
17
|
-
this.writer.setIndentationLevel(indentationLevel + 1);
|
|
18
|
-
yield* effect;
|
|
19
|
-
this.writer.setIndentationLevel(indentationLevel);
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
writeLine<E = never, R = never>(line: string): Effect.Effect<void, E, R> {
|
|
24
|
-
return Effect.sync(() => {
|
|
25
|
-
this.writer.writeLine(line);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
write<E = never, R = never>(text: string): Effect.Effect<void, E, R> {
|
|
30
|
-
return Effect.sync(() => {
|
|
31
|
-
this.writer.write(text);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
quote<E = never, R = never>(text: string): Effect.Effect<void, E, R> {
|
|
36
|
-
return Effect.sync(() => {
|
|
37
|
-
this.writer.quote(text);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
conditionalWriteLine<E = never, R = never>(
|
|
42
|
-
condition: boolean,
|
|
43
|
-
text: string,
|
|
44
|
-
): Effect.Effect<void, E, R> {
|
|
45
|
-
return Effect.sync(() => {
|
|
46
|
-
this.writer.conditionalWriteLine(condition, text);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
newLine<E = never, R = never>(): Effect.Effect<void, E, R> {
|
|
51
|
-
return Effect.sync(() => {
|
|
52
|
-
this.writer.newLine();
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
blankLine<E = never, R = never>(): Effect.Effect<void, E, R> {
|
|
57
|
-
return Effect.sync(() => {
|
|
58
|
-
this.writer.blankLine();
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
toString<E = never, R = never>(): Effect.Effect<string, E, R> {
|
|
63
|
-
return Effect.sync(() => this.writer.toString());
|
|
64
|
-
}
|
|
65
|
-
}
|