@mnemonik/shared 7.142.0 → 7.144.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.
- package/dist/ast/astChunker.d.ts.map +1 -1
- package/dist/ast/astChunker.js +6 -1
- package/dist/ast/astChunker.js.map +1 -1
- package/dist/ast/grammars.d.ts +4 -2
- package/dist/ast/grammars.d.ts.map +1 -1
- package/dist/ast/grammars.js +47 -1
- package/dist/ast/grammars.js.map +1 -1
- package/dist/codeScanner.d.ts +6 -0
- package/dist/codeScanner.d.ts.map +1 -1
- package/dist/codeScanner.js +12 -0
- package/dist/codeScanner.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/sourceRedaction.d.ts.map +1 -1
- package/dist/sourceRedaction.js +1013 -21
- package/dist/sourceRedaction.js.map +1 -1
- package/dist/vueSource.d.ts +17 -0
- package/dist/vueSource.d.ts.map +1 -0
- package/dist/vueSource.js +63 -0
- package/dist/vueSource.js.map +1 -0
- package/package.json +3 -1
- package/queries/cmake.tags.scm +6 -0
- package/queries/nix.tags.scm +4 -0
- package/queries/objcpp.tags.scm +4 -0
- package/queries/sql.tags.scm +5 -0
- package/queries/swift.tags.scm +11 -0
- package/queries/terraform.tags.scm +8 -0
- package/scripts/jsx-bare-ampersand.patch +61 -0
- package/scripts/vendor-grammars.ts +731 -37
- package/src/ast/astChunker.ts +10 -1
- package/src/ast/grammars.ts +44 -2
- package/src/codeScanner.ts +12 -0
- package/src/index.ts +7 -0
- package/src/sourceRedaction.ts +1099 -21
- package/src/vueSource.ts +77 -0
- package/wasm/bash.wasm +0 -0
- package/wasm/cmake.LICENSE +21 -0
- package/wasm/cmake.tags.scm +6 -0
- package/wasm/cmake.wasm +0 -0
- package/wasm/cpp.LICENSE +21 -0
- package/wasm/cpp.wasm +0 -0
- package/wasm/groovy.LICENSE +25 -0
- package/wasm/groovy.wasm +0 -0
- package/wasm/javascript.wasm +0 -0
- package/wasm/lua.LICENSE +21 -0
- package/wasm/lua.tags.scm +36 -0
- package/wasm/lua.wasm +0 -0
- package/wasm/nix.LICENSE +21 -0
- package/wasm/nix.tags.scm +4 -0
- package/wasm/nix.wasm +0 -0
- package/wasm/objcpp.LICENSE +28 -0
- package/wasm/objcpp.tags.scm +4 -0
- package/wasm/objcpp.wasm +0 -0
- package/wasm/sql.LICENSE +21 -0
- package/wasm/sql.tags.scm +5 -0
- package/wasm/sql.wasm +0 -0
- package/wasm/swift.LICENSE +21 -0
- package/wasm/swift.tags.scm +11 -0
- package/wasm/swift.wasm +0 -0
- package/wasm/terraform.LICENSE +201 -0
- package/wasm/terraform.tags.scm +8 -0
- package/wasm/terraform.wasm +0 -0
- package/wasm/tsx.wasm +0 -0
- package/wasm/typescript.wasm +0 -0
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* nothing at runtime - which is the whole reason this plan chose WebAssembly
|
|
18
18
|
* over the native bindings.
|
|
19
19
|
*
|
|
20
|
-
* Run: npx tsx packages/shared/scripts/vendor-grammars.ts [--check]
|
|
20
|
+
* Run: npx tsx packages/shared/scripts/vendor-grammars.ts [--check | --only=<id>]
|
|
21
|
+
* JSX repair: run --only=javascript and --only=tsx; other assets stay untouched.
|
|
21
22
|
* --check verify every manifest entry is present and non-empty; write nothing.
|
|
22
23
|
* This is what CI should run to catch a grammar that silently stopped
|
|
23
24
|
* shipping an artifact.
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
|
|
26
27
|
import { execFileSync } from 'node:child_process';
|
|
27
28
|
import {
|
|
29
|
+
chmodSync,
|
|
28
30
|
copyFileSync,
|
|
29
31
|
existsSync,
|
|
30
32
|
mkdirSync,
|
|
@@ -42,13 +44,13 @@ import { Language, Parser, Query } from 'web-tree-sitter';
|
|
|
42
44
|
import { AST_LANGUAGE_IDS, QUERY_CHAIN, grammarArtifactBasename } from '../src/ast/grammars.js';
|
|
43
45
|
|
|
44
46
|
const WASM_DIR = fileURLToPath(new URL('../wasm/', import.meta.url));
|
|
45
|
-
/**
|
|
47
|
+
/** Local queries for missing or unsuitable upstream declaration captures. */
|
|
46
48
|
const OURS_DIR = fileURLToPath(new URL('../queries/', import.meta.url));
|
|
47
49
|
|
|
48
50
|
interface GrammarSpec {
|
|
49
51
|
/** Vendored artifact basename, and the id the registry keys on. */
|
|
50
52
|
id: string;
|
|
51
|
-
/** npm package
|
|
53
|
+
/** npm package/source archive, or the pinned ObjC++ source repository. */
|
|
52
54
|
pkg: string;
|
|
53
55
|
/**
|
|
54
56
|
* `.wasm` basename inside the tarball when it differs from the package's own
|
|
@@ -59,7 +61,7 @@ interface GrammarSpec {
|
|
|
59
61
|
/**
|
|
60
62
|
* Where the tag query comes from. 'upstream' copies queries/tags.scm out of
|
|
61
63
|
* the tarball; 'ours' copies packages/shared/queries/<id>.tags.scm, which we
|
|
62
|
-
* maintain
|
|
64
|
+
* maintain when the upstream query is absent or captures unsuitable ranges.
|
|
63
65
|
*/
|
|
64
66
|
tags: 'upstream' | 'ours' | 'inherited';
|
|
65
67
|
}
|
|
@@ -71,22 +73,36 @@ interface GrammarSpec {
|
|
|
71
73
|
* remove.
|
|
72
74
|
*
|
|
73
75
|
* Absent on purpose:
|
|
74
|
-
* julia,
|
|
75
|
-
*
|
|
76
|
-
*
|
|
76
|
+
* julia, haskell - large grammars that ALSO ship no tag query, so each
|
|
77
|
+
* costs both its size (6.1/3.7 MB) and a hand-written query. Dropped to
|
|
78
|
+
* bound the payload; still indexed via the
|
|
77
79
|
* allowlist, still chunked heuristically.
|
|
78
80
|
* dart - its bundled .wasm (package v1.0.0, 2023) fails Language.load under
|
|
79
81
|
* web-tree-sitter 0.26.11 with a dylink-metadata error: the artifact
|
|
80
82
|
* predates the current wasm ABI. Presence is not loadability, which is why
|
|
81
83
|
* --check verifies both. Flutter matters, so this is worth revisiting by
|
|
82
84
|
* compiling from source with tree-sitter-cli.
|
|
83
|
-
* kotlin,
|
|
85
|
+
* kotlin, perl, zig - bundle no .wasm, so they need a build step
|
|
84
86
|
* with tree-sitter-cli. Deferred until the grammars above are proven; they
|
|
85
87
|
* keep the heuristic chunker meanwhile.
|
|
86
88
|
* markdown - keeps chunkMarkdown. Headers already are a document's semantic
|
|
87
89
|
* unit; an AST would not improve on them.
|
|
88
90
|
*/
|
|
89
91
|
const GRAMMARS: GrammarSpec[] = [
|
|
92
|
+
{
|
|
93
|
+
// Uy Ha 0.7.5, MIT; build the committed ABI-14 source without native dependencies.
|
|
94
|
+
id: 'cmake',
|
|
95
|
+
pkg: 'https://codeload.github.com/uyha/tree-sitter-cmake/tar.gz/58993af75218bc99a1f5a04c832a5937e7c422cb',
|
|
96
|
+
wasmFile: 'tree-sitter-cmake.wasm',
|
|
97
|
+
tags: 'ours',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
// nix-community 0.3.0, MIT; use its committed generated ABI-15 parser unchanged.
|
|
101
|
+
id: 'nix',
|
|
102
|
+
pkg: 'https://codeload.github.com/nix-community/tree-sitter-nix/tar.gz/32c3ce98afdafc2ec9aedf0280a678750f76254c',
|
|
103
|
+
wasmFile: 'tree-sitter-nix.wasm',
|
|
104
|
+
tags: 'ours',
|
|
105
|
+
},
|
|
90
106
|
{
|
|
91
107
|
id: 'typescript',
|
|
92
108
|
pkg: 'tree-sitter-typescript@0.23.2',
|
|
@@ -99,12 +115,20 @@ const GRAMMARS: GrammarSpec[] = [
|
|
|
99
115
|
wasmFile: 'tree-sitter-tsx.wasm',
|
|
100
116
|
tags: 'inherited',
|
|
101
117
|
},
|
|
102
|
-
{ id: 'javascript', pkg: 'tree-sitter-javascript', tags: 'upstream' },
|
|
118
|
+
{ id: 'javascript', pkg: 'tree-sitter-javascript@0.25.0', tags: 'upstream' },
|
|
103
119
|
{ id: 'python', pkg: 'tree-sitter-python', tags: 'upstream' },
|
|
104
120
|
{ id: 'go', pkg: 'tree-sitter-go', tags: 'upstream' },
|
|
105
121
|
{ id: 'java', pkg: 'tree-sitter-java', tags: 'upstream' },
|
|
106
122
|
{ id: 'c', pkg: 'tree-sitter-c', tags: 'upstream' },
|
|
107
|
-
|
|
123
|
+
// MIT 0.23.4, gitHead f41e1a044c8a84ea9fa8577fdd2eab92ec96de02.
|
|
124
|
+
{ id: 'cpp', pkg: 'tree-sitter-cpp@0.23.4', tags: 'upstream' },
|
|
125
|
+
{
|
|
126
|
+
// MIT Objective-C++ 0.1.0; committed ABI-15 parser, not the pure Objective-C npm grammar.
|
|
127
|
+
id: 'objcpp',
|
|
128
|
+
pkg: 'https://tangled.org/dunkirk.sh/dots',
|
|
129
|
+
wasmFile: 'tree-sitter-objcpp.wasm',
|
|
130
|
+
tags: 'ours',
|
|
131
|
+
},
|
|
108
132
|
{
|
|
109
133
|
id: 'c-sharp',
|
|
110
134
|
pkg: 'tree-sitter-c-sharp',
|
|
@@ -117,9 +141,39 @@ const GRAMMARS: GrammarSpec[] = [
|
|
|
117
141
|
{ id: 'scala', pkg: 'tree-sitter-scala', tags: 'upstream' },
|
|
118
142
|
{ id: 'elixir', pkg: 'tree-sitter-elixir', tags: 'upstream' },
|
|
119
143
|
{ id: 'solidity', pkg: 'tree-sitter-solidity', tags: 'upstream' },
|
|
120
|
-
|
|
121
|
-
{ id: '
|
|
144
|
+
// MIT 0.25.1, gitHead 801326684a26ffc4e749bb016c50c6c30bdfa345.
|
|
145
|
+
{ id: 'bash', pkg: 'tree-sitter-bash@0.25.1', tags: 'ours' },
|
|
146
|
+
{
|
|
147
|
+
// Upstream MIT 0.4.1, gitHead 816840c592ab973500ae9750763c707b447e7fef; shipped ABI-15 WASM.
|
|
148
|
+
id: 'lua',
|
|
149
|
+
pkg: '@tree-sitter-grammars/tree-sitter-lua@0.4.1',
|
|
150
|
+
wasmFile: 'tree-sitter-lua.wasm',
|
|
151
|
+
tags: 'upstream',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
// Apache-2.0 v1.2.0, gitHead fad991865fee927dd1de5e172fb3f08ac674d914; packaged ABI-14 WASM.
|
|
155
|
+
id: 'terraform',
|
|
156
|
+
pkg: '@tree-sitter-grammars/tree-sitter-hcl@1.2.0',
|
|
157
|
+
wasmFile: 'tree-sitter-terraform.wasm',
|
|
158
|
+
tags: 'ours',
|
|
159
|
+
},
|
|
160
|
+
// MIT 0.1.2, gitHead 70efb0b9b50f95bcbd89dcfd42b275e0304e10cf.
|
|
161
|
+
{ id: 'groovy', pkg: 'tree-sitter-groovy@0.1.2', tags: 'ours' },
|
|
122
162
|
{ id: 'powershell', pkg: 'tree-sitter-powershell', tags: 'ours' },
|
|
163
|
+
{
|
|
164
|
+
id: 'swift',
|
|
165
|
+
// Upstream 0.7.3, MIT. Pin source, not an opaque workflow WASM.
|
|
166
|
+
pkg: 'https://codeload.github.com/alex-pinkus/tree-sitter-swift/tar.gz/b8b22bffbb3441780e6471665bacfb263741c86a',
|
|
167
|
+
wasmFile: 'tree-sitter-swift.wasm',
|
|
168
|
+
tags: 'ours',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'sql',
|
|
172
|
+
// @derekstride/tree-sitter-sql 0.3.11 gitHead, MIT; source includes the external scanner.
|
|
173
|
+
pkg: 'https://codeload.github.com/derekstride/tree-sitter-sql/tar.gz/7b51ecda191d36b92f5a90a8d1bc3faef1c7b8b8',
|
|
174
|
+
wasmFile: 'tree-sitter-sql.wasm',
|
|
175
|
+
tags: 'ours',
|
|
176
|
+
},
|
|
123
177
|
];
|
|
124
178
|
|
|
125
179
|
/**
|
|
@@ -229,7 +283,7 @@ async function checkOnly(): Promise<Problem[]> {
|
|
|
229
283
|
* Source: https://github.com/tree-sitter/tree-sitter-typescript/tree/v0.23.2
|
|
230
284
|
* MIT; CLI 0.24.4 uses emscripten/emsdk:3.1.64 for the WASM build.
|
|
231
285
|
*/
|
|
232
|
-
function rebuildTypescript(dir: string, work: string): void {
|
|
286
|
+
function rebuildTypescript(dir: string, work: string, only?: string): void {
|
|
233
287
|
const grammarPath = join(dir, 'common', 'define-grammar.js');
|
|
234
288
|
let grammar = readFileSync(grammarPath, 'utf8');
|
|
235
289
|
for (const [before, after] of [
|
|
@@ -246,7 +300,37 @@ function rebuildTypescript(dir: string, work: string): void {
|
|
|
246
300
|
if (!grammar.includes(before)) throw new Error('TypeScript grammar patch no longer applies');
|
|
247
301
|
grammar = grammar.replace(before, after);
|
|
248
302
|
}
|
|
303
|
+
// Import types are primary types, so postfix [] applies without changing their expression aliases.
|
|
304
|
+
const importTypes = ` prec(-1, alias($._type_query_member_expression_in_type_annotation, $.member_expression)),
|
|
305
|
+
prec(-1, alias($._type_query_call_expression_in_type_annotation, $.call_expression)),
|
|
306
|
+
`;
|
|
307
|
+
const exportSpecifier = ` export_specifier: (_, previous) => seq(
|
|
308
|
+
optional(choice('type', 'typeof')),
|
|
309
|
+
previous,
|
|
310
|
+
),`;
|
|
311
|
+
if (
|
|
312
|
+
!grammar.includes(importTypes) ||
|
|
313
|
+
!grammar.includes(exportSpecifier) ||
|
|
314
|
+
!grammar.includes(' primary_type: $ => choice(')
|
|
315
|
+
)
|
|
316
|
+
throw new Error('TypeScript import-type/export patch no longer applies');
|
|
317
|
+
grammar = grammar
|
|
318
|
+
.replace(importTypes, '')
|
|
319
|
+
.replace(
|
|
320
|
+
' primary_type: $ => choice(',
|
|
321
|
+
' primary_type: $ => choice(\n' + importTypes.trimEnd()
|
|
322
|
+
);
|
|
323
|
+
grammar = grammar.replace(
|
|
324
|
+
exportSpecifier,
|
|
325
|
+
` export_specifier: $ => seq(
|
|
326
|
+
optional(choice('type', 'typeof')),
|
|
327
|
+
field('name', choice($._module_export_name, alias('type', $.identifier))),
|
|
328
|
+
optional(seq('as', field('alias', $._module_export_name))),
|
|
329
|
+
),`
|
|
330
|
+
);
|
|
249
331
|
writeFileSync(grammarPath, grammar);
|
|
332
|
+
// Retain our type/enum declaration captures when the vendor loop copies the query.
|
|
333
|
+
copyFileSync(join(WASM_DIR, 'typescript.tags.scm'), join(dir, 'queries', 'tags.scm'));
|
|
250
334
|
const tools = join(work, 'typescript-build');
|
|
251
335
|
execFileSync('npm', [
|
|
252
336
|
'install',
|
|
@@ -260,6 +344,12 @@ function rebuildTypescript(dir: string, work: string): void {
|
|
|
260
344
|
]);
|
|
261
345
|
const cli = join(tools, 'node_modules', '.bin', 'tree-sitter');
|
|
262
346
|
for (const dialect of ['typescript', 'tsx']) {
|
|
347
|
+
if (only && only !== dialect) continue;
|
|
348
|
+
// Apply the JSX scanner and attribute repairs only to TSX.
|
|
349
|
+
if (dialect === 'tsx') {
|
|
350
|
+
patchJsxScanner(join(dir, 'common', 'scanner.h'));
|
|
351
|
+
patchJsxAttributes(join(tools, 'node_modules', 'tree-sitter-javascript', 'grammar.js'));
|
|
352
|
+
}
|
|
263
353
|
const options = {
|
|
264
354
|
cwd: join(dir, dialect),
|
|
265
355
|
env: { ...process.env, NODE_PATH: join(tools, 'node_modules') },
|
|
@@ -270,10 +360,586 @@ function rebuildTypescript(dir: string, work: string): void {
|
|
|
270
360
|
['build', '--wasm', '--docker', '--output', join(dir, `tree-sitter-${dialect}.wasm`)],
|
|
271
361
|
options
|
|
272
362
|
);
|
|
363
|
+
chmodSync(join(dir, `tree-sitter-${dialect}.wasm`), 0o644);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Upstream f8637eb: preserve entity boundaries while accepting bare JSX ampersands. */
|
|
368
|
+
function patchJsxScanner(scannerPath: string): void {
|
|
369
|
+
execFileSync('patch', ['--batch', scannerPath], {
|
|
370
|
+
input: readFileSync(new URL('./jsx-bare-ampersand.patch', import.meta.url)),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Bare attribute ampersands are fragments; complete entity tokens retain precedence. */
|
|
375
|
+
function patchJsxAttributes(grammarPath: string): void {
|
|
376
|
+
let grammar = readFileSync(grammarPath, 'utf8');
|
|
377
|
+
for (const [before, after] of [
|
|
378
|
+
[
|
|
379
|
+
'alias($.unescaped_double_jsx_string_fragment, $.string_fragment),',
|
|
380
|
+
"alias($.unescaped_double_jsx_string_fragment, $.string_fragment),\n alias(token.immediate(prec(-1, '&')), $.string_fragment),",
|
|
381
|
+
],
|
|
382
|
+
[
|
|
383
|
+
'alias($.unescaped_single_jsx_string_fragment, $.string_fragment),',
|
|
384
|
+
"alias($.unescaped_single_jsx_string_fragment, $.string_fragment),\n alias(token.immediate(prec(-1, '&')), $.string_fragment),",
|
|
385
|
+
],
|
|
386
|
+
['/([^"&]|&[^#A-Za-z])+/', '/[^"&]+/'],
|
|
387
|
+
["/([^'&]|&[^#A-Za-z])+/", "/[^'&]+/"],
|
|
388
|
+
] as const) {
|
|
389
|
+
if (!grammar.includes(before)) throw new Error('JSX attribute patch no longer applies');
|
|
390
|
+
grammar = grammar.replace(before, after);
|
|
391
|
+
}
|
|
392
|
+
writeFileSync(grammarPath, grammar);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** 0.25.0's reserved-word DSL needs generator 0.25.8; keep the existing WASM build tool. */
|
|
396
|
+
function rebuildJavascript(dir: string, work: string): void {
|
|
397
|
+
patchJsxScanner(join(dir, 'src', 'scanner.c'));
|
|
398
|
+
patchJsxAttributes(join(dir, 'grammar.js'));
|
|
399
|
+
const generator = join(work, 'javascript-generator');
|
|
400
|
+
execFileSync('npm', [
|
|
401
|
+
'install',
|
|
402
|
+
'--prefix',
|
|
403
|
+
generator,
|
|
404
|
+
'--no-audit',
|
|
405
|
+
'--no-fund',
|
|
406
|
+
'--no-package-lock',
|
|
407
|
+
'tree-sitter-cli@0.25.8',
|
|
408
|
+
]);
|
|
409
|
+
execFileSync(join(generator, 'node_modules', '.bin', 'tree-sitter'), ['generate'], { cwd: dir });
|
|
410
|
+
const tools = join(work, 'javascript-build');
|
|
411
|
+
execFileSync('npm', [
|
|
412
|
+
'install',
|
|
413
|
+
'--prefix',
|
|
414
|
+
tools,
|
|
415
|
+
'--no-audit',
|
|
416
|
+
'--no-fund',
|
|
417
|
+
'--no-package-lock',
|
|
418
|
+
'tree-sitter-cli@0.24.4',
|
|
419
|
+
]);
|
|
420
|
+
// CLI 0.24.4 pins emscripten/emsdk:3.1.64 for --docker.
|
|
421
|
+
execFileSync(
|
|
422
|
+
join(tools, 'node_modules', '.bin', 'tree-sitter'),
|
|
423
|
+
['build', '--wasm', '--docker', '--output', join(dir, 'tree-sitter-javascript.wasm')],
|
|
424
|
+
{ cwd: dir }
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** Same CLI/emsdk as the repaired TS grammars; no native binding or grammar dependency. */
|
|
429
|
+
function rebuildSwift(dir: string, work: string): void {
|
|
430
|
+
// A double optional marker must be adjacent to its type. The external ?? token
|
|
431
|
+
// skips whitespace, swallowing `as? Int ?? 0` into optional_type and rejecting 0.
|
|
432
|
+
const grammarPath = join(dir, 'grammar.js');
|
|
433
|
+
const grammar = readFileSync(grammarPath, 'utf8');
|
|
434
|
+
const before = ' alias($._nil_coalescing_operator, "??")';
|
|
435
|
+
if (!grammar.includes(before)) throw new Error('Swift optional-marker patch no longer applies');
|
|
436
|
+
writeFileSync(grammarPath, grammar.replace(before, ' token.immediate("??")'));
|
|
437
|
+
const scannerPath = join(dir, 'src', 'scanner.c');
|
|
438
|
+
const scanner = readFileSync(scannerPath, 'utf8');
|
|
439
|
+
const continuation =
|
|
440
|
+
' // Let\'s consume operators that can live after a "semicolon" style newline.';
|
|
441
|
+
if (!scanner.includes(continuation))
|
|
442
|
+
throw new Error('Swift multiplication patch no longer applies');
|
|
443
|
+
writeFileSync(
|
|
444
|
+
scannerPath,
|
|
445
|
+
scanner.replace(
|
|
446
|
+
continuation,
|
|
447
|
+
` // A whitespace-delimited multiplication operator continues the preceding expression.
|
|
448
|
+
// Keep mark_end before '*' so a non-binary spelling can still emit the newline.
|
|
449
|
+
if (ws_directive == CONTINUE_PARSING_TOKEN_FOUND && lookahead == '*') {
|
|
450
|
+
lexer->advance(lexer, false);
|
|
451
|
+
if (iswspace(lexer->lookahead)) return STOP_PARSING_NOTHING_FOUND;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
${continuation}`
|
|
455
|
+
)
|
|
456
|
+
);
|
|
457
|
+
const tools = join(work, 'swift-build');
|
|
458
|
+
execFileSync('npm', [
|
|
459
|
+
'install',
|
|
460
|
+
'--prefix',
|
|
461
|
+
tools,
|
|
462
|
+
'--no-audit',
|
|
463
|
+
'--no-fund',
|
|
464
|
+
'--no-package-lock',
|
|
465
|
+
'tree-sitter-cli@0.24.4',
|
|
466
|
+
]);
|
|
467
|
+
const cli = join(tools, 'node_modules', '.bin', 'tree-sitter');
|
|
468
|
+
execFileSync(cli, ['generate'], { cwd: dir });
|
|
469
|
+
// CLI 0.24.4 pins emscripten/emsdk:3.1.64 for --docker.
|
|
470
|
+
execFileSync(
|
|
471
|
+
cli,
|
|
472
|
+
['build', '--wasm', '--docker', '--output', join(dir, 'tree-sitter-swift.wasm')],
|
|
473
|
+
{ cwd: dir }
|
|
474
|
+
);
|
|
475
|
+
copyFileSync(join(dir, 'LICENSE'), join(WASM_DIR, 'swift.LICENSE'));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** Build committed generated C without installing the dots repository or its grammar ancestors. */
|
|
479
|
+
function vendorObjcpp(work: string, repository: string): string {
|
|
480
|
+
const root = join(work, 'objcpp-source');
|
|
481
|
+
mkdirSync(root);
|
|
482
|
+
const git = (args: string[]): void => {
|
|
483
|
+
execFileSync('git', args, { cwd: root });
|
|
484
|
+
};
|
|
485
|
+
git(['init', '--quiet']);
|
|
486
|
+
git(['remote', 'add', 'origin', repository]);
|
|
487
|
+
git(['config', 'remote.origin.promisor', 'true']);
|
|
488
|
+
git(['config', 'remote.origin.partialclonefilter', 'blob:none']);
|
|
489
|
+
git(['sparse-checkout', 'set', '--no-cone', '/LICENSE.md', '/packages/tree-sitter-objcpp/']);
|
|
490
|
+
git([
|
|
491
|
+
'fetch',
|
|
492
|
+
'--quiet',
|
|
493
|
+
'--depth=1',
|
|
494
|
+
'--filter=blob:none',
|
|
495
|
+
'origin',
|
|
496
|
+
'8c861a98d84c6325a0300ec88dfa334aa6ab150e',
|
|
497
|
+
]);
|
|
498
|
+
git(['checkout', '--quiet', '--detach', 'FETCH_HEAD']);
|
|
499
|
+
const dir = join(root, 'packages', 'tree-sitter-objcpp');
|
|
500
|
+
const tools = join(work, 'objcpp-build');
|
|
501
|
+
execFileSync('npm', [
|
|
502
|
+
'install',
|
|
503
|
+
'--prefix',
|
|
504
|
+
tools,
|
|
505
|
+
'--no-audit',
|
|
506
|
+
'--no-fund',
|
|
507
|
+
'--no-package-lock',
|
|
508
|
+
'tree-sitter-cli@0.24.4',
|
|
509
|
+
]);
|
|
510
|
+
// Upstream generated this ABI-15 parser with CLI 0.25.10; 0.24.4 builds it with emsdk 3.1.64.
|
|
511
|
+
execFileSync(
|
|
512
|
+
join(tools, 'node_modules', '.bin', 'tree-sitter'),
|
|
513
|
+
['build', '--wasm', '--docker', '--output', join(dir, 'tree-sitter-objcpp.wasm')],
|
|
514
|
+
{ cwd: dir }
|
|
515
|
+
);
|
|
516
|
+
writeFileSync(
|
|
517
|
+
join(WASM_DIR, 'objcpp.LICENSE'),
|
|
518
|
+
readFileSync(join(root, 'LICENSE.md'), 'utf8').replace(/[\u201c\u201d]/g, '"') +
|
|
519
|
+
'\nInherited C/C++ parser and scanner portions: Copyright (c) 2014 Max Brunsfeld.\nThe MIT grant and conditions above apply to these portions too.\n'
|
|
520
|
+
);
|
|
521
|
+
return dir;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** Upstream package.json pins CLI 0.24.7. SQL function bodies only, not general PL/pgSQL. */
|
|
525
|
+
function rebuildSql(dir: string, work: string): void {
|
|
526
|
+
const grammarPath = join(dir, 'grammar.js');
|
|
527
|
+
let grammar = readFileSync(grammarPath, 'utf8');
|
|
528
|
+
const patch = (before: string, after: string): void => {
|
|
529
|
+
if (!grammar.includes(before)) throw new Error(`SQL patch no longer applies: ${before}`);
|
|
530
|
+
grammar = grammar.replace(before, after);
|
|
531
|
+
};
|
|
532
|
+
// Bounded repairs demonstrated by migrations 289/279/283 and the escaped-name regression.
|
|
533
|
+
const constraint = ` _constraint_literal: $ => seq(
|
|
534
|
+
$.keyword_constraint,
|
|
535
|
+
field('name', $.identifier),
|
|
536
|
+
choice(`;
|
|
537
|
+
patch(constraint, constraint + '\n $._key_constraint,');
|
|
538
|
+
patch('_double_quote_string: _ => /"[^"]*"/', '_double_quote_string: _ => /"([^"]|"")*"/');
|
|
539
|
+
patch(
|
|
540
|
+
'wrapped_in_parenthesis($.binary_expression)\n ),\n\n _default_expression',
|
|
541
|
+
'wrapped_in_parenthesis($._expression)\n ),\n\n _default_expression'
|
|
542
|
+
);
|
|
543
|
+
patch(
|
|
544
|
+
'field("custom_type", $.object_reference)',
|
|
545
|
+
'seq(field("custom_type", $.object_reference), optional(wrapped_in_parenthesis($._natural_number)))'
|
|
546
|
+
);
|
|
547
|
+
patch(
|
|
548
|
+
` $.keyword_conflict,
|
|
549
|
+
seq(`,
|
|
550
|
+
` $.keyword_conflict,
|
|
551
|
+
optional($._column_list),
|
|
552
|
+
seq(`
|
|
553
|
+
);
|
|
554
|
+
patch(
|
|
555
|
+
` $.keyword_brin
|
|
556
|
+
),`,
|
|
557
|
+
` $.keyword_brin,
|
|
558
|
+
$.identifier
|
|
559
|
+
),`
|
|
560
|
+
);
|
|
561
|
+
patch(
|
|
562
|
+
` $.index_fields
|
|
563
|
+
),`,
|
|
564
|
+
` $.index_fields
|
|
565
|
+
),
|
|
566
|
+
optional($.storage_parameters),`
|
|
567
|
+
);
|
|
568
|
+
patch(
|
|
569
|
+
` $.drop_function,
|
|
570
|
+
),`,
|
|
571
|
+
` $.drop_function,
|
|
572
|
+
$.drop_trigger,
|
|
573
|
+
),`
|
|
574
|
+
);
|
|
575
|
+
patch(
|
|
576
|
+
' drop_table: $ => seq(',
|
|
577
|
+
` drop_trigger: $ => seq(
|
|
578
|
+
$.keyword_drop, $.keyword_trigger, optional($._if_exists),
|
|
579
|
+
$.identifier, $.keyword_on, $.object_reference, optional($._drop_behavior),
|
|
580
|
+
),
|
|
581
|
+
|
|
582
|
+
drop_table: $ => seq(`
|
|
583
|
+
);
|
|
584
|
+
// Only the policy clauses and RLS action observed in the four fixed PostgreSQL migrations.
|
|
585
|
+
patch(' $.create_trigger,', ' $.create_trigger,\n $.create_policy,');
|
|
586
|
+
patch(
|
|
587
|
+
' _alter_specifications: $ => choice(',
|
|
588
|
+
` _alter_specifications: $ => choice(
|
|
589
|
+
seq(make_keyword("enable"), $.keyword_row, $.keyword_level, $.keyword_security),`
|
|
590
|
+
);
|
|
591
|
+
patch(
|
|
592
|
+
' create_table: $ => prec.left(',
|
|
593
|
+
` policy_relation: $ => $.object_reference,
|
|
594
|
+
create_policy: $ => seq(
|
|
595
|
+
$.keyword_create, make_keyword("policy"), field('name', $.identifier),
|
|
596
|
+
$.keyword_on, alias($.policy_relation, $.relation),
|
|
597
|
+
optional(seq($.keyword_for, choice($.keyword_select, $.keyword_insert, $.keyword_update, $.keyword_delete))),
|
|
598
|
+
optional(seq($.keyword_to, $.identifier)),
|
|
599
|
+
optional(seq($.keyword_using, wrapped_in_parenthesis($._expression))),
|
|
600
|
+
optional(seq($.keyword_with, $.keyword_check, wrapped_in_parenthesis($._expression))),
|
|
601
|
+
),
|
|
602
|
+
create_table: $ => prec.left(`
|
|
603
|
+
);
|
|
604
|
+
// Dollar-quoted procedural code is one literal, never a parsed SQL statement list.
|
|
605
|
+
patch(' $.reset_statement,', ' $.reset_statement,\n $.do_statement,');
|
|
606
|
+
patch(
|
|
607
|
+
' _cte: $ => seq(',
|
|
608
|
+
` do_statement: $ => seq($.keyword_do, alias($._dollar_quoted_string, $.literal)),
|
|
609
|
+
|
|
610
|
+
_cte: $ => seq(`
|
|
611
|
+
);
|
|
612
|
+
// Keep both parses until NOT NULL (constraint) or NOT LIKE (expression) is known.
|
|
613
|
+
// Match _expression's precedence so it cannot discard the completed default first.
|
|
614
|
+
patch(
|
|
615
|
+
' _inner_default_expression: $ => choice(',
|
|
616
|
+
' _inner_default_expression: $ => prec(1, choice('
|
|
617
|
+
);
|
|
618
|
+
patch(
|
|
619
|
+
'alias($.implicit_cast, $.cast),\n ),\n\n constraints:',
|
|
620
|
+
'alias($.implicit_cast, $.cast),\n )),\n\n constraints:'
|
|
621
|
+
);
|
|
622
|
+
patch(
|
|
623
|
+
' conflicts: $ => [',
|
|
624
|
+
' conflicts: $ => [\n [$._expression, $._inner_default_expression],\n [$.create_function],'
|
|
625
|
+
);
|
|
626
|
+
const functionStart = grammar.indexOf(' create_function: $ => seq(');
|
|
627
|
+
const functionEnd = grammar.indexOf(' _function_return:', functionStart);
|
|
628
|
+
if (functionStart < 0 || functionEnd < 0) throw new Error('SQL function patch no longer applies');
|
|
629
|
+
const original = grammar.slice(functionStart, functionEnd).trimEnd();
|
|
630
|
+
const languageOption = ' $.function_language,\n';
|
|
631
|
+
const optionsStart = original.indexOf(' repeat(');
|
|
632
|
+
const optionsEnd = original.indexOf(' // ensure');
|
|
633
|
+
const options = original.slice(optionsStart, optionsEnd).replace(languageOption, '');
|
|
634
|
+
const procedural =
|
|
635
|
+
original.slice(0, optionsStart) +
|
|
636
|
+
options +
|
|
637
|
+
' alias($.plpgsql_function_language, $.function_language),\n' +
|
|
638
|
+
options +
|
|
639
|
+
' alias($.opaque_function_body, $.function_body),\n' +
|
|
640
|
+
options +
|
|
641
|
+
' )';
|
|
642
|
+
patch(
|
|
643
|
+
original,
|
|
644
|
+
procedural.replace('create_function: $ => seq(', 'create_function: $ => choice(prec(1, seq(') +
|
|
645
|
+
'),\n' +
|
|
646
|
+
original.slice(original.indexOf('seq('), -1) +
|
|
647
|
+
'),'
|
|
648
|
+
);
|
|
649
|
+
patch(
|
|
650
|
+
' $._dollar_quoted_string,\n ],',
|
|
651
|
+
' $._dollar_quoted_string,\n $._trailing_plpgsql_body,\n ],'
|
|
652
|
+
);
|
|
653
|
+
// Named rules retain complete body/language ranges; aliasing a sequence only names its first token.
|
|
654
|
+
patch(
|
|
655
|
+
' function_body: $ => choice(',
|
|
656
|
+
` opaque_function_body: $ => seq(
|
|
657
|
+
$.keyword_as,
|
|
658
|
+
alias(choice($._dollar_quoted_string, $._single_quote_string, $._double_quote_string), $.literal),
|
|
659
|
+
),
|
|
660
|
+
plpgsql_function_language: $ => seq(
|
|
661
|
+
$.keyword_language, alias(make_keyword("plpgsql"), $.identifier),
|
|
662
|
+
),
|
|
663
|
+
function_body: $ => choice(
|
|
664
|
+
seq(
|
|
665
|
+
$.keyword_as,
|
|
666
|
+
alias($._dollar_quoted_string_start_tag, $.dollar_quote),
|
|
667
|
+
alias($._trailing_plpgsql_body, $.literal),
|
|
668
|
+
alias($._dollar_quoted_string_end_tag, $.dollar_quote),
|
|
669
|
+
),`
|
|
670
|
+
);
|
|
671
|
+
const languageStart = grammar.indexOf(' function_language: $ => seq(');
|
|
672
|
+
const languageEnd = grammar.indexOf(' function_volatility:', languageStart);
|
|
673
|
+
if (languageStart < 0 || languageEnd < 0) throw new Error('SQL language patch no longer applies');
|
|
674
|
+
const language = grammar.slice(languageStart, languageEnd);
|
|
675
|
+
patch(
|
|
676
|
+
language,
|
|
677
|
+
language.replace(
|
|
678
|
+
' $.identifier',
|
|
679
|
+
` choice(
|
|
680
|
+
$.identifier,
|
|
681
|
+
alias(token(prec(1, seq("'", make_keyword("plpgsql"), "'"))), $.identifier),
|
|
682
|
+
)`
|
|
683
|
+
)
|
|
684
|
+
);
|
|
685
|
+
writeFileSync(grammarPath, grammar);
|
|
686
|
+
const scannerPath = join(dir, 'src', 'scanner.c');
|
|
687
|
+
let scanner = readFileSync(scannerPath, 'utf8');
|
|
688
|
+
const token = ' DOLLAR_QUOTED_STRING\n';
|
|
689
|
+
const start =
|
|
690
|
+
' if (valid_symbols[DOLLAR_QUOTED_STRING_START_TAG] && state->start_tag == NULL) {';
|
|
691
|
+
if (!scanner.includes(token) || !scanner.includes(start))
|
|
692
|
+
throw new Error('SQL procedural scanner patch no longer applies');
|
|
693
|
+
scanner = scanner.replace(token, ' DOLLAR_QUOTED_STRING,\n TRAILING_PLPGSQL_BODY\n');
|
|
694
|
+
scanner = scanner.replace(
|
|
695
|
+
start,
|
|
696
|
+
() =>
|
|
697
|
+
` if (valid_symbols[TRAILING_PLPGSQL_BODY] && state->start_tag != NULL) {
|
|
698
|
+
bool consumed = false;
|
|
699
|
+
for (;;) {
|
|
700
|
+
if (lexer->eof(lexer)) return false;
|
|
701
|
+
if (lexer->lookahead != '$') {
|
|
702
|
+
lexer->advance(lexer, false);
|
|
703
|
+
consumed = true;
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
lexer->mark_end(lexer);
|
|
707
|
+
char *tag = scan_dollar_string_tag(lexer);
|
|
708
|
+
bool closing = tag != NULL && strcmp(tag, state->start_tag) == 0;
|
|
709
|
+
free(tag);
|
|
710
|
+
if (closing) break;
|
|
711
|
+
consumed = true;
|
|
712
|
+
}
|
|
713
|
+
if (!consumed) return false;
|
|
714
|
+
// Look ahead without moving the marked body end; SQL bodies keep their normal AST.
|
|
715
|
+
while (iswspace(lexer->lookahead)) lexer->advance(lexer, false);
|
|
716
|
+
const char *keyword = "language";
|
|
717
|
+
for (unsigned i = 0; keyword[i]; i++) {
|
|
718
|
+
if (towlower(lexer->lookahead) != keyword[i]) return false;
|
|
719
|
+
lexer->advance(lexer, false);
|
|
720
|
+
}
|
|
721
|
+
while (iswspace(lexer->lookahead)) lexer->advance(lexer, false);
|
|
722
|
+
bool quoted = lexer->lookahead == 39;
|
|
723
|
+
if (quoted) lexer->advance(lexer, false);
|
|
724
|
+
const char *language = "plpgsql";
|
|
725
|
+
for (unsigned i = 0; language[i]; i++) {
|
|
726
|
+
if (towlower(lexer->lookahead) != language[i]) return false;
|
|
727
|
+
lexer->advance(lexer, false);
|
|
728
|
+
}
|
|
729
|
+
if (quoted ? lexer->lookahead != 39 : (iswalnum(lexer->lookahead) || lexer->lookahead == '_' || lexer->lookahead == '$')) return false;
|
|
730
|
+
lexer->result_symbol = TRAILING_PLPGSQL_BODY;
|
|
731
|
+
return true;
|
|
273
732
|
}
|
|
733
|
+
|
|
734
|
+
` + start
|
|
735
|
+
);
|
|
736
|
+
writeFileSync(scannerPath, scanner);
|
|
737
|
+
const tools = join(work, 'sql-build');
|
|
738
|
+
execFileSync('npm', [
|
|
739
|
+
'install',
|
|
740
|
+
'--prefix',
|
|
741
|
+
tools,
|
|
742
|
+
'--no-audit',
|
|
743
|
+
'--no-fund',
|
|
744
|
+
'--no-package-lock',
|
|
745
|
+
'tree-sitter-cli@0.24.7',
|
|
746
|
+
]);
|
|
747
|
+
const cli = join(tools, 'node_modules', '.bin', 'tree-sitter');
|
|
748
|
+
execFileSync(cli, ['generate'], { cwd: dir });
|
|
749
|
+
execFileSync(
|
|
750
|
+
cli,
|
|
751
|
+
['build', '--wasm', '--docker', '--output', join(dir, 'tree-sitter-sql.wasm')],
|
|
752
|
+
{ cwd: dir }
|
|
753
|
+
);
|
|
754
|
+
copyFileSync(join(dir, 'LICENSE'), join(WASM_DIR, 'sql.LICENSE'));
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/** Permit existing dotted property expressions as command-call arguments. */
|
|
758
|
+
function rebuildGroovy(dir: string, work: string): void {
|
|
759
|
+
const path = join(dir, 'grammar.js');
|
|
760
|
+
let source = readFileSync(path, 'utf8');
|
|
761
|
+
const before =
|
|
762
|
+
' _juxt_argument_list: $ => commaSep1(choice($._literal, $.map_item, $.identifier)),';
|
|
763
|
+
if (!source.includes(before)) throw new Error('Groovy argument patch no longer applies');
|
|
764
|
+
source = source.replace(before, before.replace('$.identifier)', '$.identifier, $.field_access)'));
|
|
765
|
+
const conflicts = ' conflicts: ($, original) => original.concat([';
|
|
766
|
+
if (!source.includes(conflicts)) throw new Error('Groovy conflict patch no longer applies');
|
|
767
|
+
source = source.replace(
|
|
768
|
+
conflicts,
|
|
769
|
+
conflicts +
|
|
770
|
+
`
|
|
771
|
+
[$.method_invocation, $._juxt_argument_list],
|
|
772
|
+
[$.generic_type, $._juxt_argument_list],
|
|
773
|
+
[$.primary_expression, $._unannotated_type, $._juxt_argument_list],
|
|
774
|
+
[$._unannotated_type, $._juxt_argument_list],
|
|
775
|
+
[$.primary_expression, $._juxt_argument_list],`
|
|
776
|
+
);
|
|
777
|
+
writeFileSync(path, source);
|
|
778
|
+
const tools = join(work, 'groovy-build');
|
|
779
|
+
execFileSync('npm', [
|
|
780
|
+
'install',
|
|
781
|
+
'--prefix',
|
|
782
|
+
tools,
|
|
783
|
+
'--no-audit',
|
|
784
|
+
'--no-fund',
|
|
785
|
+
'--no-package-lock',
|
|
786
|
+
'tree-sitter-cli@0.24.4',
|
|
787
|
+
'tree-sitter-java@0.23.4',
|
|
788
|
+
]);
|
|
789
|
+
const cli = join(tools, 'node_modules', '.bin', 'tree-sitter');
|
|
790
|
+
const options = { cwd: dir, env: { ...process.env, NODE_PATH: join(tools, 'node_modules') } };
|
|
791
|
+
execFileSync(cli, ['generate'], options);
|
|
792
|
+
execFileSync(
|
|
793
|
+
cli,
|
|
794
|
+
['build', '--wasm', '--docker', '--output', join(dir, 'tree-sitter-groovy.wasm')],
|
|
795
|
+
options
|
|
796
|
+
);
|
|
797
|
+
// Upstream publishes its MIT declaration in package.json/grammar.js, with no LICENSE file.
|
|
798
|
+
writeFileSync(
|
|
799
|
+
join(WASM_DIR, 'groovy.LICENSE'),
|
|
800
|
+
'tree-sitter-groovy 0.1.2, by Amaan Qureshi <amaanq12@gmail.com>.\n' +
|
|
801
|
+
'Upstream package.json and grammar.js declare MIT. Source: https://github.com/amaanq/tree-sitter-groovy/tree/70efb0b9b50f95bcbd89dcfd42b275e0304e10cf\n\n' +
|
|
802
|
+
'Inherited tree-sitter-java 0.23.4 (MIT):\n' +
|
|
803
|
+
readFileSync(join(tools, 'node_modules', 'tree-sitter-java', 'LICENSE'), 'utf8')
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/** Build the official committed Nix parser without installing its native Node binding. */
|
|
808
|
+
function rebuildNixOrCmake(dir: string, work: string, id: 'nix' | 'cmake'): void {
|
|
809
|
+
const tools = join(work, `${id}-build`);
|
|
810
|
+
execFileSync('npm', [
|
|
811
|
+
'install',
|
|
812
|
+
'--prefix',
|
|
813
|
+
tools,
|
|
814
|
+
'--no-audit',
|
|
815
|
+
'--no-fund',
|
|
816
|
+
'--no-package-lock',
|
|
817
|
+
'tree-sitter-cli@0.24.4',
|
|
818
|
+
]);
|
|
819
|
+
// Build the pinned generated source; CLI 0.24.4 pins emscripten/emsdk:3.1.64.
|
|
820
|
+
execFileSync(
|
|
821
|
+
join(tools, 'node_modules', '.bin', 'tree-sitter'),
|
|
822
|
+
['build', '--wasm', '--docker', '--output', join(dir, `tree-sitter-${id}.wasm`)],
|
|
823
|
+
{ cwd: dir }
|
|
824
|
+
);
|
|
825
|
+
copyFileSync(join(dir, 'LICENSE'), join(WASM_DIR, `${id}.LICENSE`));
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/** The published grammar JSON freezes its C ancestor; add only the two JNI modifier spellings. */
|
|
829
|
+
function rebuildCpp(dir: string, work: string): void {
|
|
830
|
+
const path = join(dir, 'src', 'grammar.json');
|
|
831
|
+
const grammar = JSON.parse(readFileSync(path, 'utf8')) as {
|
|
832
|
+
rules: Record<string, { type: string; members: unknown[] }>;
|
|
833
|
+
};
|
|
834
|
+
for (const [name, value] of [
|
|
835
|
+
['type_qualifier', 'JNIEXPORT'],
|
|
836
|
+
['ms_call_modifier', 'JNICALL'],
|
|
837
|
+
] as const) {
|
|
838
|
+
const rule = grammar.rules[name];
|
|
839
|
+
if (!rule || rule.type !== 'CHOICE' || !Array.isArray(rule.members))
|
|
840
|
+
throw new Error(`C++ JNI patch no longer applies: ${name}`);
|
|
841
|
+
rule.members.push({ type: 'STRING', value });
|
|
842
|
+
}
|
|
843
|
+
writeFileSync(path, JSON.stringify(grammar));
|
|
844
|
+
const tools = join(work, 'cpp-build');
|
|
845
|
+
execFileSync('npm', [
|
|
846
|
+
'install',
|
|
847
|
+
'--prefix',
|
|
848
|
+
tools,
|
|
849
|
+
'--no-audit',
|
|
850
|
+
'--no-fund',
|
|
851
|
+
'--no-package-lock',
|
|
852
|
+
'tree-sitter-cli@0.24.4',
|
|
853
|
+
]);
|
|
854
|
+
const cli = join(tools, 'node_modules', '.bin', 'tree-sitter');
|
|
855
|
+
execFileSync(cli, ['generate', 'src/grammar.json'], { cwd: dir });
|
|
856
|
+
execFileSync(
|
|
857
|
+
cli,
|
|
858
|
+
['build', '--wasm', '--docker', '--output', join(dir, 'tree-sitter-cpp.wasm')],
|
|
859
|
+
{ cwd: dir }
|
|
860
|
+
);
|
|
861
|
+
copyFileSync(join(dir, 'LICENSE'), join(WASM_DIR, 'cpp.LICENSE'));
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/** A radix value may use $name as well as ${name}; keep the source and expansion node intact. */
|
|
865
|
+
function rebuildBash(dir: string, work: string): void {
|
|
866
|
+
const path = join(dir, 'grammar.js');
|
|
867
|
+
const grammar = readFileSync(path, 'utf8');
|
|
868
|
+
const before = 'seq(/-?(0x)?[0-9]+#/, choice($.expansion, $.command_substitution))';
|
|
869
|
+
if (!grammar.includes(before)) throw new Error('Bash radix patch no longer applies');
|
|
870
|
+
writeFileSync(
|
|
871
|
+
path,
|
|
872
|
+
grammar.replace(
|
|
873
|
+
before,
|
|
874
|
+
'seq(/-?(0x)?[0-9]+#/, choice($.simple_expansion, $.expansion, $.command_substitution))'
|
|
875
|
+
)
|
|
876
|
+
);
|
|
877
|
+
const scannerPath = join(dir, 'src', 'scanner.c');
|
|
878
|
+
const scanner = readFileSync(scannerPath, 'utf8');
|
|
879
|
+
const variableScan = ` return false;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (valid_symbols[BARE_DOLLAR] && !in_error_recovery(valid_symbols) && scan_bare_dollar(lexer)) {`;
|
|
883
|
+
if (!scanner.includes(variableScan))
|
|
884
|
+
throw new Error('Bash expansion-word patch no longer applies');
|
|
885
|
+
const parenStart = scanner.indexOf(
|
|
886
|
+
" if (lexer->lookahead == '(' && !(advanced_once || advance_once_space)) {"
|
|
887
|
+
);
|
|
888
|
+
const parenEnd = scanner.indexOf(
|
|
889
|
+
String.raw` if (lexer->lookahead == '\'') {`,
|
|
890
|
+
parenStart
|
|
891
|
+
);
|
|
892
|
+
const concatParens = "lexer->lookahead == ')' || lexer->lookahead == '('";
|
|
893
|
+
if (parenStart < 0 || parenEnd <= parenStart || !scanner.includes(concatParens))
|
|
894
|
+
throw new Error('Bash literal-parenthesis patch no longer applies');
|
|
895
|
+
// The variable-name scan consumes a default's prefix but rejects | before reaching expansion_word.
|
|
896
|
+
// Inside ${...}, parentheses are word pieces around expansions, not shell groups.
|
|
897
|
+
writeFileSync(
|
|
898
|
+
scannerPath,
|
|
899
|
+
(scanner.slice(0, parenStart) + scanner.slice(parenEnd))
|
|
900
|
+
.replace(
|
|
901
|
+
variableScan,
|
|
902
|
+
" if (valid_symbols[EXPANSION_WORD] && lexer->lookahead == '|') goto expansion_word;\n\n" +
|
|
903
|
+
variableScan
|
|
904
|
+
)
|
|
905
|
+
.replace(
|
|
906
|
+
concatParens,
|
|
907
|
+
"((lexer->lookahead == ')' || lexer->lookahead == '(') && !valid_symbols[CLOSING_BRACE])"
|
|
908
|
+
)
|
|
909
|
+
);
|
|
910
|
+
const generator = join(work, 'bash-generator');
|
|
911
|
+
execFileSync('npm', [
|
|
912
|
+
'install',
|
|
913
|
+
'--prefix',
|
|
914
|
+
generator,
|
|
915
|
+
'--no-audit',
|
|
916
|
+
'--no-fund',
|
|
917
|
+
'--no-package-lock',
|
|
918
|
+
'tree-sitter-cli@0.25.8',
|
|
919
|
+
]);
|
|
920
|
+
execFileSync(join(generator, 'node_modules', '.bin', 'tree-sitter'), ['generate'], { cwd: dir });
|
|
921
|
+
const tools = join(work, 'bash-build');
|
|
922
|
+
execFileSync('npm', [
|
|
923
|
+
'install',
|
|
924
|
+
'--prefix',
|
|
925
|
+
tools,
|
|
926
|
+
'--no-audit',
|
|
927
|
+
'--no-fund',
|
|
928
|
+
'--no-package-lock',
|
|
929
|
+
'tree-sitter-cli@0.24.4',
|
|
930
|
+
]);
|
|
931
|
+
// CLI 0.24.4 pins emscripten/emsdk:3.1.64 for --docker.
|
|
932
|
+
execFileSync(
|
|
933
|
+
join(tools, 'node_modules', '.bin', 'tree-sitter'),
|
|
934
|
+
['build', '--wasm', '--docker', '--output', join(dir, 'tree-sitter-bash.wasm')],
|
|
935
|
+
{ cwd: dir }
|
|
936
|
+
);
|
|
274
937
|
}
|
|
275
938
|
|
|
276
939
|
function vendor(): Problem[] {
|
|
940
|
+
const only = process.argv.find((arg) => arg.startsWith('--only='))?.slice(7);
|
|
941
|
+
if (only && !GRAMMARS.some((spec) => spec.id === only))
|
|
942
|
+
throw new Error(`Unknown grammar: ${only}`);
|
|
277
943
|
mkdirSync(WASM_DIR, { recursive: true });
|
|
278
944
|
const work = mkdtempSync(join(tmpdir(), 'ts-grammars-'));
|
|
279
945
|
const problems: Problem[] = [];
|
|
@@ -283,28 +949,40 @@ function vendor(): Problem[] {
|
|
|
283
949
|
|
|
284
950
|
try {
|
|
285
951
|
for (const spec of GRAMMARS) {
|
|
952
|
+
if (only && spec.id !== only) continue;
|
|
286
953
|
let dir = extracted.get(spec.pkg);
|
|
287
954
|
if (!dir) {
|
|
288
955
|
try {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
'
|
|
302
|
-
|
|
303
|
-
'
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
956
|
+
if (spec.id === 'objcpp') {
|
|
957
|
+
dir = vendorObjcpp(work, spec.pkg);
|
|
958
|
+
} else {
|
|
959
|
+
const out = execFileSync(
|
|
960
|
+
'npm',
|
|
961
|
+
['pack', spec.pkg, '--silent', '--pack-destination', work],
|
|
962
|
+
{
|
|
963
|
+
cwd: work,
|
|
964
|
+
encoding: 'utf8',
|
|
965
|
+
}
|
|
966
|
+
);
|
|
967
|
+
const tarball = out.trim().split('\n').pop() ?? '';
|
|
968
|
+
dir = join(work, spec.pkg.replace(/[^a-z0-9-]/gi, '_'));
|
|
969
|
+
mkdirSync(dir, { recursive: true });
|
|
970
|
+
execFileSync('tar', [
|
|
971
|
+
'xzf',
|
|
972
|
+
join(work, basename(tarball)),
|
|
973
|
+
'-C',
|
|
974
|
+
dir,
|
|
975
|
+
'--strip-components=1',
|
|
976
|
+
]);
|
|
977
|
+
if (spec.id === 'typescript' || spec.id === 'tsx') rebuildTypescript(dir, work, only);
|
|
978
|
+
if (spec.id === 'javascript') rebuildJavascript(dir, work);
|
|
979
|
+
if (spec.id === 'swift') rebuildSwift(dir, work);
|
|
980
|
+
if (spec.id === 'sql') rebuildSql(dir, work);
|
|
981
|
+
if (spec.id === 'groovy') rebuildGroovy(dir, work);
|
|
982
|
+
if (spec.id === 'cpp') rebuildCpp(dir, work);
|
|
983
|
+
if (spec.id === 'bash') rebuildBash(dir, work);
|
|
984
|
+
if (spec.id === 'nix' || spec.id === 'cmake') rebuildNixOrCmake(dir, work, spec.id);
|
|
985
|
+
}
|
|
308
986
|
extracted.set(spec.pkg, dir);
|
|
309
987
|
} catch (err) {
|
|
310
988
|
problems.push({
|
|
@@ -322,8 +1000,24 @@ function vendor(): Problem[] {
|
|
|
322
1000
|
problems.push({ id: spec.id, detail: `${spec.pkg} ships no ${wasmName}` });
|
|
323
1001
|
} else {
|
|
324
1002
|
copyFileSync(wasmSrc, join(WASM_DIR, `${spec.id}.wasm`));
|
|
1003
|
+
if (spec.id === 'lua') copyFileSync(join(dir, 'LICENSE.md'), join(WASM_DIR, 'lua.LICENSE'));
|
|
1004
|
+
if (spec.id === 'terraform')
|
|
1005
|
+
copyFileSync(join(dir, 'LICENSE'), join(WASM_DIR, 'terraform.LICENSE'));
|
|
1006
|
+
if (
|
|
1007
|
+
spec.id === 'javascript' ||
|
|
1008
|
+
spec.id === 'tsx' ||
|
|
1009
|
+
spec.id === 'objcpp' ||
|
|
1010
|
+
spec.id === 'groovy' ||
|
|
1011
|
+
spec.id === 'cpp' ||
|
|
1012
|
+
spec.id === 'bash' ||
|
|
1013
|
+
spec.id === 'nix' ||
|
|
1014
|
+
spec.id === 'cmake'
|
|
1015
|
+
)
|
|
1016
|
+
chmodSync(join(WASM_DIR, `${spec.id}.wasm`), 0o644);
|
|
325
1017
|
}
|
|
326
1018
|
|
|
1019
|
+
// A targeted JS scanner rebuild must retain our module-scope binding query additions.
|
|
1020
|
+
if (only === 'javascript') continue;
|
|
327
1021
|
if (spec.tags === 'upstream') {
|
|
328
1022
|
const tagsSrc = join(dir, 'queries', 'tags.scm');
|
|
329
1023
|
if (!existsSync(tagsSrc)) {
|
|
@@ -363,13 +1057,13 @@ if (problems.length > 0) {
|
|
|
363
1057
|
}
|
|
364
1058
|
|
|
365
1059
|
const sizes = GRAMMARS.map((g) => statSync(join(WASM_DIR, `${g.id}.wasm`)).size);
|
|
366
|
-
const
|
|
1060
|
+
const totalBytes = sizes.reduce((a, b) => a + b, 0);
|
|
1061
|
+
const totalMB = +(totalBytes / 1048576).toFixed(1);
|
|
367
1062
|
console.log(
|
|
368
1063
|
`VENDOR_GRAMMARS_OK ${JSON.stringify({ grammars: GRAMMARS.length, totalWasmMB: totalMB })}`
|
|
369
1064
|
);
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
console.error(`VENDOR_GRAMMARS_TOO_LARGE ${totalMB}MB exceeds the 40MB ceiling`);
|
|
1065
|
+
// Owner-approved Objective-C++ cost: repaired bundle 43.58 MiB (43.67 with Terraform 667475e93), bounded at 45 MiB.
|
|
1066
|
+
if (totalBytes > 45 * 1048576) {
|
|
1067
|
+
console.error(`VENDOR_GRAMMARS_TOO_LARGE ${totalMB}MB exceeds the 45MB ceiling`);
|
|
374
1068
|
process.exit(1);
|
|
375
1069
|
}
|