@cr_docs_t/dts 0.26.0 → 0.28.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.
@@ -0,0 +1,2 @@
1
+ export * from "./AST.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/treesitter/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC"}
@@ -0,0 +1 @@
1
+ export * from "./AST.js";
@@ -0,0 +1,2 @@
1
+ export * from "./treesitter/index.js";
2
+ //# sourceMappingURL=treesitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treesitter.d.ts","sourceRoot":"","sources":["../src/treesitter.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from "./treesitter/index.js";
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=type-gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-gen.d.ts","sourceRoot":"","sources":["../src/type-gen.ts"],"names":[],"mappings":""}
@@ -0,0 +1,184 @@
1
+ // Inspired by https://github.com/github/semantic/tree/main/semantic-ast
2
+ import { writeFileSync, readFileSync } from "fs";
3
+ import { dirname, join } from "path";
4
+ import { fileURLToPath } from "url";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const dir = dirname(__filename);
7
+ const INPUT_PATH = join(dir, "node-types.json"); // Adjust as needed
8
+ const OUTPUT_PATH = join(dir, "treesitter", "types", "AST.ts");
9
+ const toPascalCase = (str) => {
10
+ return (str
11
+ .match(/[a-z_]+/gi)
12
+ ?.map((word) => word
13
+ .split("_")
14
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
15
+ .join(""))
16
+ .join("") || str);
17
+ };
18
+ function generate() {
19
+ const rawData = readFileSync(INPUT_PATH, "utf8");
20
+ const nodes = JSON.parse(rawData);
21
+ let output = `// Auto-generated from node-types.json\n\n`;
22
+ output += `import { Node } from 'web-tree-sitter';
23
+ import { v4 } from 'uuid'
24
+
25
+ export type NodeId = string;
26
+
27
+ export interface ParserContext {
28
+ nodes: Map<NodeId, AstNode>;
29
+ }
30
+
31
+ `;
32
+ let interfacesOut = `// Interfaces
33
+
34
+ `;
35
+ let unmarshalersOut = `// Unmarshalers
36
+
37
+ `;
38
+ let switches = "";
39
+ const allNamedNodes = [];
40
+ for (const node of nodes) {
41
+ if (!node.named)
42
+ continue;
43
+ // Generate interfaces
44
+ const interfaceName = toPascalCase(node.type) + "Node";
45
+ // Handle Supertypes
46
+ if (node.subtypes && node.subtypes.length > 0) {
47
+ const subtypeNames = node.subtypes.filter((st) => st.named).map((st) => toPascalCase(st.type) + "Node");
48
+ if (subtypeNames.length > 0) {
49
+ interfacesOut += `export type ${interfaceName} = ${subtypeNames.join(" | ")};\n\n`;
50
+ }
51
+ continue;
52
+ }
53
+ allNamedNodes.push(interfaceName);
54
+ // Handle Standard Concrete Nodes
55
+ interfacesOut += `export interface ${interfaceName} {\n`;
56
+ // Use a Map to track properties so we can merge collisions
57
+ const props = new Map();
58
+ // Set default properties
59
+ props.set("id", { type: "NodeId", optional: false });
60
+ props.set("parentId", { type: "NodeId | null", optional: false });
61
+ props.set("type", { type: `'${node.type}'`, optional: false });
62
+ props.set("text", { type: "string", optional: false });
63
+ // Map Fields
64
+ if (node.fields) {
65
+ for (const [fieldName, fieldData] of Object.entries(node.fields)) {
66
+ let typeString = fieldData.multiple ? "NodeId[]" : "NodeId";
67
+ const isOptional = !fieldData.required;
68
+ if (props.has(fieldName)) {
69
+ // Merge types on collision
70
+ const existing = props.get(fieldName);
71
+ existing.type = `${existing.type} | ${typeString}`;
72
+ existing.optional = existing.optional && isOptional;
73
+ }
74
+ else {
75
+ props.set(fieldName, { type: typeString, optional: isOptional });
76
+ }
77
+ }
78
+ }
79
+ props.set("childrenIds", { type: "NodeId[]", optional: false });
80
+ // Write all properties to the interface
81
+ for (const [propName, propData] of props.entries()) {
82
+ const optMod = propData.optional ? "?" : "";
83
+ interfacesOut += ` ${propName}${optMod}: ${propData.type};\n`;
84
+ }
85
+ interfacesOut += `}\n\n`;
86
+ // Generate Unmarshalers
87
+ const funcName = `unmarshaler${interfaceName}`;
88
+ switches += `case '${node.type}': return ${funcName}(node, ctx, parentId);\n`;
89
+ unmarshalersOut += `function ${funcName}(node: Node, ctx: ParserContext, parentId: NodeId | null): NodeId {
90
+ const id = v4();
91
+ const n: Partial<${interfaceName}> = {
92
+ id,
93
+ parentId,
94
+ type: '${node.type}',
95
+ text: node.text,
96
+ };
97
+ ctx.nodes.set(id, n as AstNode);
98
+
99
+ `;
100
+ let fieldExtractionNodes = "";
101
+ if (node.fields) {
102
+ for (const [fieldName, fieldData] of Object.entries(node.fields)) {
103
+ if (fieldData.multiple) {
104
+ unmarshalersOut += `n.${fieldName} = node.childrenForFieldName('${fieldName}').map(n => unmarshalNode(n, ctx, id));\n`;
105
+ fieldExtractionNodes += `...node.childrenForFieldName('${fieldName}').map(n => n.id), `;
106
+ }
107
+ else {
108
+ if (fieldData.required) {
109
+ unmarshalersOut += `n.${fieldName} = unmarshalNode(node.childForFieldName('${fieldName}')!, ctx, id);\n`;
110
+ fieldExtractionNodes += `node.childForFieldName('${fieldName}')!.id, `;
111
+ }
112
+ else {
113
+ unmarshalersOut += `const ${fieldName}Node = node.childForFieldName('${fieldName}');
114
+ n.${fieldName} = ${fieldName}Node ? unmarshalNode(${fieldName}Node, ctx, id) : undefined;
115
+ `;
116
+ fieldExtractionNodes += `${fieldName}Node ? ${fieldName}Node.id : undefined, `;
117
+ }
118
+ }
119
+ }
120
+ }
121
+ if (fieldExtractionNodes.length > 0) {
122
+ unmarshalersOut += `
123
+ const fieldNodes = new Set([${fieldExtractionNodes}].filter(id => id !== undefined));
124
+ n.childrenIds = node.namedChildren.filter(n => !fieldNodes.has(n.id)).map(n => unmarshalNode(n, ctx, id));
125
+ `;
126
+ }
127
+ else {
128
+ unmarshalersOut += `n.childrenIds = node.namedChildren.map(n => unmarshalNode(n, ctx, id));\n`;
129
+ }
130
+ unmarshalersOut += `return id;
131
+ }
132
+
133
+ `;
134
+ }
135
+ const core = `// Parser core
136
+
137
+ export const unmarshalNode = (node: Node, ctx: ParserContext, parentId: NodeId | null): NodeId => {
138
+ switch(node.type) {
139
+ ${switches}
140
+ default: {
141
+ const id = v4();
142
+ const n = {
143
+ id,
144
+ parentId,
145
+ type: node.type as any,
146
+ text: node.text,
147
+ childrenIds: [] as NodeId[],
148
+ };
149
+ ctx.nodes.set(id, n as AstNode);
150
+ n.childrenIds = node.namedChildren.map(n => unmarshalNode(n, ctx, id));
151
+ return id;
152
+ }
153
+ }
154
+ }
155
+
156
+ export type BragiAST = {rootId: NodeId, nodes: Map<NodeId, AstNode> };
157
+
158
+ // Parses a treesitter CST into a Bragi AST node map
159
+ export const parseCST = (root: Node | null) : BragiAST => {
160
+ if (!root) throw new Error ("No root node provided");
161
+ const ctx: ParserContext = { nodes: new Map() };
162
+ const rootId = unmarshalNode(root, ctx, null);
163
+ return {rootId, nodes: ctx.nodes};
164
+ }
165
+ `;
166
+ // Create a generic ASTNode type
167
+ const concreteNodes = nodes
168
+ .filter((n) => n.named && (!n.subtypes || n.subtypes.length === 0))
169
+ .map((n) => toPascalCase(n.type) + "Node");
170
+ const genericType = `export type AstNode = ${concreteNodes.join(" | ")};\n`;
171
+ // Consolidate output
172
+ output += `
173
+ ${interfacesOut}
174
+
175
+ ${genericType}
176
+
177
+ ${unmarshalersOut}
178
+
179
+ ${core}
180
+ `;
181
+ writeFileSync(OUTPUT_PATH, output);
182
+ console.log(`Successfully generated AST types at ${OUTPUT_PATH}`);
183
+ }
184
+ generate();
@@ -1,4 +1,4 @@
1
- // export * from "./Fugue/index.js";
1
+ // dxport * from "./Fugue/index.js";
2
2
  export * from "./FugueTree/index.js";
3
3
  export * from "./Enums.js";
4
4
  export * from "./Models/index.js";
package/package.json CHANGED
@@ -1,22 +1,29 @@
1
1
  {
2
2
  "name": "@cr_docs_t/dts",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "types": "dist/index.d.ts",
8
8
  "scripts": {
9
9
  "build": "tsc",
10
+ "prebuild": "npm run gen-types",
10
11
  "prepublishOnly": "npm run build",
11
12
  "ci": "npm install && npm run build",
12
13
  "pretest": "npm run build",
13
- "sest": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
14
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
15
+ "gen-types": "tsx ./src/type-gen.ts && biome format --write ./src/treesitter/types/AST.ts"
14
16
  },
15
17
  "exports": {
16
18
  ".": {
17
19
  "import": "./dist/index.js",
18
20
  "require": "./dist/index.js",
19
21
  "types": "./dist/index.d.ts"
22
+ },
23
+ "./treesitter": {
24
+ "import": "./dist/treesitter.js",
25
+ "require": "./dist/treesitter.js",
26
+ "types": "./dist/treesitter.d.ts"
20
27
  }
21
28
  },
22
29
  "publishConfig": {
@@ -56,6 +63,7 @@
56
63
  "license": "ISC",
57
64
  "packageManager": "pnpm@10.20.0",
58
65
  "devDependencies": {
66
+ "@biomejs/biome": "2.4.4",
59
67
  "@semantic-release/changelog": "^6.0.3",
60
68
  "@semantic-release/git": "^10.0.1",
61
69
  "@types/jest": "^30.0.0",
@@ -64,6 +72,7 @@
64
72
  "jest": "^30.2.0",
65
73
  "semantic-release": "^25.0.2",
66
74
  "ts-jest": "^29.4.6",
75
+ "tsx": "^4.21.0",
67
76
  "typescript": "^5.9.3"
68
77
  },
69
78
  "files": [
@@ -72,6 +81,8 @@
72
81
  "dependencies": {
73
82
  "@msgpack/msgpack": "^3.1.3",
74
83
  "lz4js": "^0.2.0",
84
+ "uuid": "^13.0.0",
85
+ "web-tree-sitter": "^0.26.5",
75
86
  "zod": "^4.3.6"
76
87
  }
77
88
  }