Skip to content

Commit b7b30c1

Browse files
Make more typesafe (#24)
* chore: release v0.2.0-alpha.3 * fix circular imports generate by generate-types.ts * type block renderers image and rich text wip * update readme
1 parent 4361630 commit b7b30c1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+315
-539
lines changed

typescript/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,13 +256,15 @@ MIT
256256
- [x] equation
257257
- [x] image
258258
- [x] blockquote
259-
- [] fix ts and eslint errors in these dirs and remove from ignore list of ts and eslint
259+
- [x] fix ts and eslint errors in these dirs and remove from ignore list of ts and eslint
260260
```
261261
"**/generated/**",
262262
"src/serialization/**",
263263
```
264+
264265
- [x] add counter for numbered lists
265266
- [x] setup up bumpp: https://www.npmjs.com/package/bumpp
266267
- [] migrate test runner to vitest
267268
- [x] move vite app to typescript root examples dir
268269
- [] setup monorepo tooling
270+
- [] fix model generation for Image and RichText, then type renderers

typescript/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

typescript/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@textcortex/jsondoc",
3-
"version": "0.2.0-alpha.2",
3+
"version": "0.2.0-alpha.3",
44
"description": "JSON-DOC TypeScript implementation",
55
"main": "dist/index.js",
66
"module": "dist/index.mjs",

typescript/scripts/generate-types.ts

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,19 @@ async function resolveRefs(obj: any, sourceDir: string): Promise<any> {
128128
customTypePath = [...refTokens, title].join(".");
129129
}
130130

131-
// Create a simplified reference object
132-
return {
133-
type: "object",
134-
title,
135-
properties: {},
136-
additionalProperties: false,
137-
// Add metadata for TypeScript generator
138-
// These will be processed by json-schema-to-typescript
139-
description: `Reference to ${customTypePath}`,
140-
tsType: customTypePath.split(".").pop(),
141-
};
131+
// For external references, just return a simple type reference
132+
// This prevents inlining the entire interface definition
133+
if (title) {
134+
return {
135+
type: "object",
136+
title: title,
137+
// Mark as external reference to prevent inlining
138+
"x-external-ref": true,
139+
};
140+
}
141+
142+
// Fallback: return the resolved object if no title
143+
return refObj || {};
142144
} catch (error) {
143145
console.error(`Error resolving reference ${ref}:`, error);
144146
return {};
@@ -198,8 +200,14 @@ async function convertSchemaToTypeScript(
198200
const outputDir = path.dirname(outputPath);
199201
await mkdir(outputDir, { recursive: true });
200202

203+
// Post-process to fix external references
204+
const processedTypeScript = await postProcessTypeScript(
205+
typeScript,
206+
outputPath
207+
);
208+
201209
// Write the TypeScript interface to file
202-
await writeFile(outputPath, typeScript);
210+
await writeFile(outputPath, processedTypeScript);
203211
console.log(`Generated: ${outputPath}`);
204212
} catch (error) {
205213
console.error(`Error converting schema ${schemaPath}:`, error);
@@ -274,6 +282,76 @@ function createBarrelFiles(dir: string): void {
274282
}
275283
}
276284

285+
/**
286+
* Post-process generated TypeScript to fix external references
287+
*/
288+
async function postProcessTypeScript(
289+
typeScript: string,
290+
outputPath: string
291+
): Promise<string> {
292+
let processed = typeScript;
293+
294+
// Calculate the correct import path for each interface based on current output path
295+
const getImportPath = (interfaceName: string): string => {
296+
const outputDir = path.dirname(outputPath);
297+
const modelsGenerated = path.resolve(__dirname, "../src/models/generated");
298+
299+
// Define the actual locations of each interface
300+
const interfaceLocations: Record<string, string> = {
301+
BlockBase: path.join(modelsGenerated, "block/base"),
302+
Block: path.join(modelsGenerated, "block"),
303+
RichText: path.join(modelsGenerated, "block/types/rich_text"),
304+
File: path.join(modelsGenerated, "file"),
305+
ColumnBlock: path.join(modelsGenerated, "block/types/column"),
306+
TableRowBlock: path.join(modelsGenerated, "block/types/table_row"),
307+
FileExternal: path.join(modelsGenerated, "file/external"),
308+
RichTextText: path.join(modelsGenerated, "block/types/rich_text/text"),
309+
};
310+
311+
const targetPath = interfaceLocations[interfaceName];
312+
if (!targetPath) return "";
313+
314+
const relativePath = path.relative(outputDir, targetPath);
315+
return relativePath.startsWith(".")
316+
? relativePath.replace(/\\/g, "/")
317+
: `./${relativePath.replace(/\\/g, "/")}`;
318+
};
319+
320+
// Find empty interface definitions and replace with imports
321+
const emptyInterfaceRegex = /export interface (\w+) \{\}\s*/g;
322+
const imports: string[] = [];
323+
324+
processed = processed.replace(emptyInterfaceRegex, (match, interfaceName) => {
325+
const importPath = getImportPath(interfaceName);
326+
if (importPath) {
327+
imports.push(`import type { ${interfaceName} } from '${importPath}';`);
328+
return ""; // Remove the empty interface
329+
}
330+
return match; // Keep if not in our external interfaces map
331+
});
332+
333+
// Add imports at the top if any were found
334+
if (imports.length > 0) {
335+
processed = imports.join("\n") + "\n\n" + processed;
336+
}
337+
338+
// Fix Block type to extend BlockBase if this is the block.ts file
339+
if (outputPath.endsWith("block/block.ts")) {
340+
// Add import for BlockBase if not already present
341+
if (!processed.includes("import type { BlockBase }")) {
342+
processed = `import type { BlockBase } from './base';\n\n` + processed;
343+
}
344+
345+
// Replace the Block type definition to extend BlockBase
346+
processed = processed.replace(
347+
/export type Block = \{[^}]*\} & \{/,
348+
"export type Block = BlockBase & {"
349+
);
350+
}
351+
352+
return processed;
353+
}
354+
277355
/**
278356
* Extract enum values from a JSON schema file
279357
*/

0 commit comments

Comments
 (0)