Skip to content

Commit eb98d97

Browse files
authored
🔄 Refactor cli and oxa-types (#45)
1 parent 89bdc4d commit eb98d97

19 files changed

+474
-340
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
src/version.ts
1+
dist/
2+
src/schema.json

‎packages/oxa-core/package.json‎

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,13 @@
99
".": {
1010
"types": "./dist/index.d.ts",
1111
"import": "./dist/index.js"
12-
},
13-
"./schema.json": "./dist/schema.json"
12+
}
1413
},
1514
"files": [
1615
"dist"
1716
],
1817
"scripts": {
19-
"copy:version": "echo \"const version = '\"$npm_package_version\"';\nexport default version;\" > src/version.ts",
20-
"build": "pnpm run copy:version && tsc && cp ../../schema/schema.json dist/ && pnpm run build:bundle",
21-
"build:bundle": "esbuild src/cli.ts --bundle --platform=node --target=node22 --format=cjs --outfile=dist/cli.bundle.cjs --banner:js='#!/usr/bin/env node' --log-override:empty-import-meta=silent",
18+
"build": "cp ../../schema/schema.json src/schema.json && tsc",
2219
"test": "vitest run",
2320
"typecheck": "tsc --noEmit",
2421
"clean": "rm -rf dist",
@@ -31,16 +28,12 @@
3128
"ajv": "^8.17.1",
3229
"ajv-formats": "^3.0.1",
3330
"better-ajv-errors": "^2.0.2",
34-
"commander": "^14.0.0",
3531
"js-yaml": "^4.1.0",
3632
"oxa-types": "workspace:*"
3733
},
3834
"devDependencies": {
3935
"@oxa/conformance": "workspace:*",
40-
"@types/js-yaml": "^4.0.9",
41-
"@types/node": "^22.0.0",
42-
"esbuild": "^0.25.0",
43-
"execa": "^9.5.0"
36+
"@types/js-yaml": "^4.0.9"
4437
},
4538
"keywords": [
4639
"oxa",
@@ -58,7 +51,6 @@
5851
"url": "git+https://github.com/oxa-dev/oxa.git",
5952
"directory": "packages/oxa-core"
6053
},
61-
"engines": {
62-
"node": ">=22"
63-
}
54+
"engines": {}
55+
6456
}

‎packages/oxa-core/src/convert.test.ts‎

Lines changed: 98 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
import { describe, it, expect, vi } from "vitest";
1+
import { describe, it, expect } from "vitest";
22
import { readFileSync } from "fs";
33
import { dirname, relative, resolve } from "path";
44
import { fileURLToPath } from "url";
5+
import type { Session } from "./types.js";
56

67
const __dirname = dirname(fileURLToPath(import.meta.url));
78
const REPO_ROOT = resolve(__dirname, "../../..");
89

10+
function createTestSession(): Session & { messages: string[] } {
11+
const messages: string[] = [];
12+
return {
13+
messages,
14+
log: {
15+
debug: (...args: unknown[]) => messages.push(String(args.join(" "))),
16+
info: (...args: unknown[]) => messages.push(String(args.join(" "))),
17+
warn: (...args: unknown[]) => messages.push(String(args.join(" "))),
18+
error: (...args: unknown[]) => messages.push(String(args.join(" "))),
19+
},
20+
};
21+
}
22+
923
const lexiconFiles = {
1024
facet: {
1125
id: "pub.oxa.richtext.facet",
@@ -154,7 +168,7 @@ function documentNode(
154168
} satisfies TestDocumentNode;
155169
}
156170

157-
async function flatten(inlines: unknown[]) {
171+
async function flatten(inlines: unknown[], session?: Session) {
158172
const convertModule = await import("./convert.js").catch((error) => {
159173
if (error instanceof Error && error.message.includes("/src/convert.js")) {
160174
return undefined;
@@ -170,10 +184,10 @@ async function flatten(inlines: unknown[]) {
170184
"Expected packages/oxa-core/src/convert.ts to export flattenInlines",
171185
).toBeTypeOf("function");
172186

173-
return flattenInlines!(inlines as never);
187+
return flattenInlines!(session ?? createTestSession(), inlines as never);
174188
}
175189

176-
async function map(block: unknown) {
190+
async function map(block: unknown, session?: Session) {
177191
const convertModule = await import("./convert.js");
178192
const mapBlock = convertModule.mapBlock;
179193

@@ -182,10 +196,14 @@ async function map(block: unknown) {
182196
"Expected packages/oxa-core/src/convert.ts to export mapBlock",
183197
).toBeTypeOf("function");
184198

185-
return mapBlock!(block as never);
199+
return mapBlock!(session ?? createTestSession(), block as never);
186200
}
187201

188-
async function convertDocument(document: unknown, options?: unknown) {
202+
async function convertDocument(
203+
document: unknown,
204+
options?: unknown,
205+
session?: Session,
206+
) {
189207
const convertModule = await import("./convert.js");
190208
const oxaToAtproto = convertModule.oxaToAtproto;
191209

@@ -194,7 +212,11 @@ async function convertDocument(document: unknown, options?: unknown) {
194212
"Expected packages/oxa-core/src/convert.ts to export oxaToAtproto",
195213
).toBeTypeOf("function");
196214

197-
return oxaToAtproto!(document as never, options as never);
215+
return oxaToAtproto!(
216+
session ?? createTestSession(),
217+
document as never,
218+
options as never,
219+
);
198220
}
199221

200222
function readLexicon(filePath: string): LexiconDoc {
@@ -208,12 +230,6 @@ function readLexicon(filePath: string): LexiconDoc {
208230
}
209231
}
210232

211-
function stderrOutput(writeSpy: ReturnType<typeof vi.spyOn>) {
212-
return writeSpy.mock.calls
213-
.map(([chunk]: [unknown, ...unknown[]]) => String(chunk))
214-
.join("");
215-
}
216-
217233
// eslint-disable-next-line @typescript-eslint/no-explicit-any
218234
function getLexiconDefs(filePath: string): Record<string, any> {
219235
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -402,29 +418,25 @@ describe("mapBlock", () => {
402418
});
403419

404420
it("warns and omits unknown block types instead of coercing them into known ATProto blocks", async () => {
405-
const writeSpy = vi
406-
.spyOn(process.stderr, "write")
407-
.mockImplementation(() => true);
421+
const session = createTestSession();
408422

409-
try {
410-
await expect(
411-
map({
412-
type: "Callout",
413-
children: [text("Should be dropped")],
414-
id: "callout-1",
415-
classes: ["note"],
416-
data: { severity: "warning" },
417-
}),
418-
).resolves.toBeUndefined();
423+
const result = await map(
424+
{
425+
type: "Callout",
426+
children: [text("Should be dropped")],
427+
id: "callout-1",
428+
classes: ["note"],
429+
data: { severity: "warning" },
430+
},
431+
session,
432+
);
419433

420-
expect(writeSpy).toHaveBeenCalled();
434+
expect(result).toBeUndefined();
435+
expect(session.messages.length).toBeGreaterThan(0);
421436

422-
const warning = stderrOutput(writeSpy);
423-
expect(warning).toContain("unknown block type");
424-
expect(warning).toContain("Callout");
425-
} finally {
426-
writeSpy.mockRestore();
427-
}
437+
const warning = session.messages.join("\n");
438+
expect(warning).toContain("unknown block type");
439+
expect(warning).toContain("Callout");
428440
});
429441
});
430442

@@ -507,50 +519,45 @@ describe("oxaToAtproto", () => {
507519
tags: ["oxa", "atproto"],
508520
},
509521
};
510-
const writeSpy = vi
511-
.spyOn(process.stderr, "write")
512-
.mockImplementation(() => true);
513-
514-
try {
515-
const converted = await convertDocument(
516-
{
517-
type: "Document",
518-
metadata,
519-
children: [
520-
paragraph([text("Keep this paragraph")]),
521-
{
522-
type: "Callout",
523-
children: [text("Drop this block")],
524-
data: { severity: "warning" },
525-
},
526-
],
527-
},
528-
{ createdAt },
529-
);
522+
const session = createTestSession();
530523

531-
expect(converted).toEqual({
532-
$type: "pub.oxa.document.document",
524+
const converted = await convertDocument(
525+
{
526+
type: "Document",
533527
metadata,
534528
children: [
529+
paragraph([text("Keep this paragraph")]),
535530
{
536-
$type: "pub.oxa.document.defs#paragraph",
537-
text: "Keep this paragraph",
538-
facets: [],
531+
type: "Callout",
532+
children: [text("Drop this block")],
533+
data: { severity: "warning" },
539534
},
540535
],
541-
createdAt,
542-
});
543-
expect(converted.metadata).toBe(metadata);
544-
expect("title" in converted).toBe(false);
536+
},
537+
{ createdAt },
538+
session,
539+
);
540+
541+
expect(converted).toEqual({
542+
$type: "pub.oxa.document.document",
543+
metadata,
544+
children: [
545+
{
546+
$type: "pub.oxa.document.defs#paragraph",
547+
text: "Keep this paragraph",
548+
facets: [],
549+
},
550+
],
551+
createdAt,
552+
});
553+
expect(converted.metadata).toBe(metadata);
554+
expect("title" in converted).toBe(false);
545555

546-
expect(writeSpy).toHaveBeenCalled();
556+
expect(session.messages.length).toBeGreaterThan(0);
547557

548-
const warning = stderrOutput(writeSpy);
549-
expect(warning).toContain("unknown block type");
550-
expect(warning).toContain("Callout");
551-
} finally {
552-
writeSpy.mockRestore();
553-
}
558+
const warning = session.messages.join("\n");
559+
expect(warning).toContain("unknown block type");
560+
expect(warning).toContain("Callout");
554561
});
555562
});
556563

@@ -769,39 +776,36 @@ describe("flattenInlines", () => {
769776
);
770777
});
771778

772-
it("warns to stderr and drops inline id, classes, and data properties on formatting nodes", async () => {
773-
const writeSpy = vi
774-
.spyOn(process.stderr, "write")
775-
.mockImplementation(() => true);
779+
it("warns and drops inline id, classes, and data properties on formatting nodes", async () => {
780+
const session = createTestSession();
776781

777-
try {
778-
const richText = await flatten([
782+
const richText = await flatten(
783+
[
779784
strong([text("styled")], {
780785
id: "inline-id",
781786
classes: ["callout", "accent"],
782787
data: { note: "keep warning only" },
783788
}),
784-
]);
789+
],
790+
session,
791+
);
785792

786-
expect(richText).toEqual({
787-
text: "styled",
788-
facets: [
789-
{
790-
index: { byteStart: 0, byteEnd: 6 },
791-
features: [{ $type: "pub.oxa.richtext.facet#strong" }],
792-
},
793-
],
794-
});
793+
expect(richText).toEqual({
794+
text: "styled",
795+
facets: [
796+
{
797+
index: { byteStart: 0, byteEnd: 6 },
798+
features: [{ $type: "pub.oxa.richtext.facet#strong" }],
799+
},
800+
],
801+
});
795802

796-
expect(writeSpy).toHaveBeenCalled();
803+
expect(session.messages.length).toBeGreaterThan(0);
797804

798-
const warning = stderrOutput(writeSpy);
799-
expect(warning).toContain("Strong");
800-
expect(warning).toContain("id");
801-
expect(warning).toContain("classes");
802-
expect(warning).toContain("data");
803-
} finally {
804-
writeSpy.mockRestore();
805-
}
805+
const warning = session.messages.join("\n");
806+
expect(warning).toContain("Strong");
807+
expect(warning).toContain("id");
808+
expect(warning).toContain("classes");
809+
expect(warning).toContain("data");
806810
});
807811
});

0 commit comments

Comments
 (0)