tree-sitter-go-types 0.1.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/index.js +205 -0
  4. package/package.json +24 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Konstantin Vyatkin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # tree-sitter-go-types
2
+
3
+ Generate Go constants from a tree-sitter grammar's `node-types.json`.
4
+
5
+ Replaces hardcoded string literals like `"pipeline_chain"` with typed constants like `NodePipelineChain`, giving you compile-time safety and IDE discoverability when working with tree-sitter ASTs in Go.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install --save-dev tree-sitter-go-types
11
+ ```
12
+
13
+ Or run directly with npx:
14
+
15
+ ```bash
16
+ npx tree-sitter-go-types
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Run in a tree-sitter grammar repo root (where `src/node-types.json` exists):
22
+
23
+ ```bash
24
+ # Uses defaults: reads src/node-types.json, writes node_types.go
25
+ npx tree-sitter-go-types
26
+
27
+ # Explicit paths and options
28
+ npx tree-sitter-go-types -i src/node-types.json -o node_types.go -p my_package
29
+ ```
30
+
31
+ ### Options
32
+
33
+ | Flag | Description | Default |
34
+ |------|-------------|---------|
35
+ | `-i, --input <path>` | Path to `node-types.json` | `src/node-types.json` |
36
+ | `-o, --output <path>` | Output `.go` file | `node_types.go` |
37
+ | `-p, --package <name>` | Go package name | `tree_sitter_<grammar>` |
38
+ | `-g, --grammar <name>` | Grammar name override | From `tree-sitter.json` or directory name |
39
+
40
+ ### Typical workflow
41
+
42
+ Add it to your grammar's build step, right after `tree-sitter generate`:
43
+
44
+ ```json
45
+ {
46
+ "scripts": {
47
+ "generate": "tree-sitter generate && npx tree-sitter-go-types"
48
+ }
49
+ }
50
+ ```
51
+
52
+ ## Output
53
+
54
+ For a PowerShell grammar, the tool generates:
55
+
56
+ ```go
57
+ // Code generated by tree-sitter-go-types v0.1.0; DO NOT EDIT.
58
+
59
+ package tree_sitter_powershell
60
+
61
+ // Node kind constants for the powershell grammar.
62
+ const (
63
+ NodeCommand = "command"
64
+ NodeCommandName = "command_name"
65
+ NodePipeline = "pipeline"
66
+ NodePipelineChain = "pipeline_chain"
67
+ NodeVariable = "variable"
68
+ // ... 152 constants total
69
+ )
70
+
71
+ // Field name constants for the powershell grammar.
72
+ const (
73
+ FieldCommandName = "command_name"
74
+ FieldCondition = "condition"
75
+ FieldValue = "value"
76
+ // ...
77
+ )
78
+ ```
79
+
80
+ Consumers use the constants instead of string literals:
81
+
82
+ ```go
83
+ import ps "github.com/wharflab/tree-sitter-powershell"
84
+
85
+ if node.Kind() == ps.NodePipelineChain {
86
+ // ...
87
+ }
88
+ ```
89
+
90
+ ## What it reads
91
+
92
+ The tool reads `src/node-types.json`, which is auto-generated by `tree-sitter generate`. It contains the complete schema of named node kinds and their field names. Only **named** node types (not anonymous tokens like `"+"` or `"if"`) become constants.
93
+
94
+ ## Zero dependencies
95
+
96
+ The tool is a single JavaScript file with no runtime dependencies. It requires Node.js >= 18.
97
+
98
+ ## License
99
+
100
+ MIT
package/index.js ADDED
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync } from "node:fs";
4
+ import { basename, dirname, join, resolve } from "node:path";
5
+
6
+ // --- helpers ----------------------------------------------------------------
7
+
8
+ /** snake_case → PascalCase, preserving acronym boundaries. */
9
+ function toPascalCase(s) {
10
+ return s
11
+ .split("_")
12
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
13
+ .join("");
14
+ }
15
+
16
+ /**
17
+ * Derive a Go-friendly prefix from the grammar name.
18
+ * "powershell" → "PowerShell"
19
+ * "batch" → "Batch"
20
+ * "javascript" → "Javascript"
21
+ */
22
+ function grammarPrefix(name) {
23
+ return toPascalCase(name);
24
+ }
25
+
26
+ /** Return true when a Go identifier would collide with a builtin or keyword. */
27
+ const goReserved = new Set([
28
+ "break", "case", "chan", "const", "continue", "default", "defer", "else",
29
+ "fallthrough", "for", "func", "go", "goto", "if", "import", "interface",
30
+ "map", "package", "range", "return", "select", "struct", "switch", "type",
31
+ "var",
32
+ ]);
33
+
34
+ // --- codegen ----------------------------------------------------------------
35
+
36
+ function generate(nodeTypes, grammarName, packageName) {
37
+ const prefix = grammarPrefix(grammarName);
38
+
39
+ // Collect named node types.
40
+ const named = nodeTypes
41
+ .filter((n) => n.named)
42
+ .map((n) => n.type)
43
+ .sort();
44
+
45
+ // Collect field names across all node types.
46
+ const fields = new Set();
47
+ for (const n of nodeTypes) {
48
+ if (!n.named || !n.fields) continue;
49
+ for (const f of Object.keys(n.fields)) {
50
+ fields.add(f);
51
+ }
52
+ }
53
+ const sortedFields = [...fields].sort();
54
+
55
+ // Build output.
56
+ const lines = [];
57
+ lines.push(`// Code generated by tree-sitter-go-types v0.1.0; DO NOT EDIT.`);
58
+ lines.push(``);
59
+ lines.push(`package ${packageName}`);
60
+
61
+ // --- node kind constants ---
62
+ lines.push(``);
63
+ lines.push(`// Node kind constants for the ${grammarName} grammar.`);
64
+ lines.push(`const (`);
65
+ for (const t of named) {
66
+ let ident = `Node${toPascalCase(t)}`;
67
+ // Safety: if the identifier part after "Node" is a Go reserved word it
68
+ // would be fine because of the prefix, but guard against future changes.
69
+ lines.push(`\t${ident} = ${JSON.stringify(t)}`);
70
+ }
71
+ lines.push(`)`);
72
+
73
+ // --- field name constants ---
74
+ if (sortedFields.length > 0) {
75
+ lines.push(``);
76
+ lines.push(`// Field name constants for the ${grammarName} grammar.`);
77
+ lines.push(`const (`);
78
+ for (const f of sortedFields) {
79
+ let ident = `Field${toPascalCase(f)}`;
80
+ lines.push(`\t${ident} = ${JSON.stringify(f)}`);
81
+ }
82
+ lines.push(`)`);
83
+ }
84
+
85
+ lines.push(``); // trailing newline
86
+ return lines.join("\n");
87
+ }
88
+
89
+ // --- CLI --------------------------------------------------------------------
90
+
91
+ function usage() {
92
+ console.error(
93
+ `Usage: tree-sitter-go-types [options]
94
+
95
+ Generate Go constants from a tree-sitter node-types.json file.
96
+
97
+ Options:
98
+ -i, --input <path> Path to node-types.json (default: src/node-types.json)
99
+ -o, --output <path> Output .go file (default: node_types.go)
100
+ -p, --package <name> Go package name (default: derived from grammar name)
101
+ -g, --grammar <name> Grammar name override (default: from tree-sitter.json
102
+ or parent directory name)
103
+ -h, --help Show this help
104
+
105
+ Examples:
106
+ # In a tree-sitter grammar repo root:
107
+ npx tree-sitter-go-types
108
+
109
+ # Explicit paths:
110
+ npx tree-sitter-go-types -i src/node-types.json -o node_types.go`
111
+ );
112
+ }
113
+
114
+ function detectGrammarName(inputPath) {
115
+ // Try tree-sitter.json in the repo root (two levels up from src/node-types.json).
116
+ const repoRoot = dirname(dirname(resolve(inputPath)));
117
+ try {
118
+ const tsJson = JSON.parse(
119
+ readFileSync(join(repoRoot, "tree-sitter.json"), "utf8")
120
+ );
121
+ if (tsJson.grammars?.[0]?.name) {
122
+ return tsJson.grammars[0].name;
123
+ }
124
+ } catch {
125
+ // Fall through.
126
+ }
127
+ // Fallback: repo directory name minus "tree-sitter-" prefix.
128
+ const dir = basename(repoRoot);
129
+ return dir.replace(/^tree-sitter-/, "");
130
+ }
131
+
132
+ function defaultPackageName(grammarName) {
133
+ // tree-sitter convention: "tree_sitter_<grammar>"
134
+ return `tree_sitter_${grammarName.toLowerCase().replace(/-/g, "_")}`;
135
+ }
136
+
137
+ function main() {
138
+ const args = process.argv.slice(2);
139
+ let input = "src/node-types.json";
140
+ let output = "node_types.go";
141
+ let packageName = "";
142
+ let grammarName = "";
143
+
144
+ for (let i = 0; i < args.length; i++) {
145
+ switch (args[i]) {
146
+ case "-i":
147
+ case "--input":
148
+ input = args[++i];
149
+ break;
150
+ case "-o":
151
+ case "--output":
152
+ output = args[++i];
153
+ break;
154
+ case "-p":
155
+ case "--package":
156
+ packageName = args[++i];
157
+ break;
158
+ case "-g":
159
+ case "--grammar":
160
+ grammarName = args[++i];
161
+ break;
162
+ case "-h":
163
+ case "--help":
164
+ usage();
165
+ process.exit(0);
166
+ default:
167
+ console.error(`Unknown option: ${args[i]}`);
168
+ usage();
169
+ process.exit(1);
170
+ }
171
+ }
172
+
173
+ // Resolve defaults.
174
+ const inputPath = resolve(input);
175
+ if (!grammarName) {
176
+ grammarName = detectGrammarName(inputPath);
177
+ }
178
+ if (!packageName) {
179
+ packageName = defaultPackageName(grammarName);
180
+ }
181
+
182
+ // Read and parse.
183
+ let nodeTypes;
184
+ try {
185
+ nodeTypes = JSON.parse(readFileSync(inputPath, "utf8"));
186
+ } catch (err) {
187
+ console.error(`Error reading ${inputPath}: ${err.message}`);
188
+ process.exit(1);
189
+ }
190
+
191
+ if (!Array.isArray(nodeTypes)) {
192
+ console.error(`Expected a JSON array in ${inputPath}`);
193
+ process.exit(1);
194
+ }
195
+
196
+ // Generate and write.
197
+ const src = generate(nodeTypes, grammarName, packageName);
198
+ const outputPath = resolve(output);
199
+ writeFileSync(outputPath, src, "utf8");
200
+ console.log(
201
+ `Generated ${outputPath} (${nodeTypes.filter((n) => n.named).length} node types, grammar: ${grammarName}, package: ${packageName})`
202
+ );
203
+ }
204
+
205
+ main();
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "tree-sitter-go-types",
3
+ "version": "0.1.0",
4
+ "description": "Generate Go constants from tree-sitter node-types.json",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/tinovyatkin/tree-sitter-go-types.git"
10
+ },
11
+ "author": "Konstantin Vyatkin",
12
+ "bin": {
13
+ "tree-sitter-go-types": "./index.js"
14
+ },
15
+ "files": [
16
+ "index.js"
17
+ ],
18
+ "keywords": [
19
+ "tree-sitter",
20
+ "go",
21
+ "codegen",
22
+ "node-types"
23
+ ]
24
+ }