mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-05 02:43:32 +00:00
* docs: add Objective-C fork provider notes * feat(objective-c): add deterministic provider and grammar * feat(objective-c): finalize provider MVP * fix(objective-c): harden provider integration * fix(objective-c): normalize bare macro markers * docs(objective-c): integrate provider documentation * fix(objective-c): harden resolution and header classification * fix(objective-c): complete provider follow-ups * fix: address Objective-C review follow-ups * chore: format Objective-C grammar sources * fix(objective-c): harden review follow-ups * Address PR review feedback (#3179) Keep Objective-C chunking and macro recovery aligned with the grammar, and stop Community MEMBER_OF edges from leaking into symbol context. Co-authored-by: Cursor <cursoragent@cursor.com> * Address follow-up review on ObjC chunking and language fallback. Keep preprocessor directive text from changing file-scope brace depth, group real ivar nodes, skip header modifiers, and restore Rakefile/Gemfile detection through getLanguageFromFilename. Co-authored-by: Cursor <cursoragent@cursor.com> * Parse Objective-C headers with the objc grammar in embeddings. ensureAndParse and structural extraction now use the same content classifier as ingest, including method snippets from .h files, so Protocol/Category/Class chunks are not re-parsed as C++. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) Keep file-scope macro elision off C line splices and @interface/@protocol/@implementation bodies, and attach ivar attributes to the following instance variable when chunking. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(bench): rebaseline Objective-C CSV emit * feat(objective-c): add workspace resolution and linear emit benches Plain .h files are classified as C++, so the ObjC pass could not resolve #import of those headers. Load a C/C#-style workspace once per pass, and keep protocol-candidate USES linear. Refs #3179 Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) - Compare LadybugDB labels() as a scalar when excluding Community MEMBER_OF edges. - Walk superclass members, skip file-static C sibling defs, and ignore comments in ObjC header/macro scans. Note: pre-existing failure in objective-c-provider integration (worker-pool ready timeout) not addressed by this PR. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) Emit Objective-C declaration captures so compilation-unit siblings can share header/implementation bindings, and keep class vs protocol visibility groups distinct. Note: pre-existing failure in worker-pool startup (GITNEXUS_WORKER_READY_TIMEOUT_MS) not addressed by this PR. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) Emit every comma-separated property/ivar declarator, and count @interface after a multiline block comment closes so in-declaration macros stay intact. Note: pre-existing failure in worker-pool startup (GITNEXUS_WORKER_READY_TIMEOUT_MS) not addressed by this PR. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: ximengkai <ximengkai@soyoung.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
5.8 KiB
TypeScript
131 lines
5.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { readdirSync, readFileSync, existsSync, writeFileSync, rmSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import {
|
|
VENDOR_ROOT,
|
|
VENDORED_GRAMMAR_PACKAGES,
|
|
vendoredGrammarDir,
|
|
requireVendoredGrammar,
|
|
} from '../../src/core/tree-sitter/vendored-grammars.js';
|
|
|
|
/**
|
|
* Regression guard for #2111 / #1728.
|
|
*
|
|
* The seven vendored tree-sitter grammars (c/objc/dart/proto/swift/kotlin/zig) MUST load
|
|
* from `vendor/` by absolute path and MUST NEVER be copied into / required from
|
|
* `node_modules`. An undeclared package under node_modules is "extraneous" to
|
|
* every subsequent npm/npx arborist reify, which prunes/relocates it — on
|
|
* Windows that threw `EPERM: operation not permitted, symlink` during the
|
|
* npx-cache reify an MCP client triggers, and on every OS it silently deleted
|
|
* the grammars on the 2nd run. These tests fail if anyone reintroduces a bare
|
|
* `require('tree-sitter-<vendored>')` / `import … from 'tree-sitter-<vendored>'`
|
|
* (which would force a node_modules copy back into existence).
|
|
*/
|
|
|
|
const SRC_ROOT = fileURLToPath(new URL('../../src', import.meta.url));
|
|
|
|
const TEST_ROOT = fileURLToPath(new URL('..', import.meta.url));
|
|
|
|
/**
|
|
* All `.ts` files we police: every file under src/, plus test/ EXCEPT
|
|
* test/fixtures/ (fixtures are arbitrary sample code to be analyzed, not our
|
|
* code). A reintroduced bare load can defeat the fix from test/ too, so the
|
|
* guard must cover it — not just src/.
|
|
*/
|
|
function policedFiles(): string[] {
|
|
const self = fileURLToPath(import.meta.url);
|
|
const under = (root: string) =>
|
|
readdirSync(root, { recursive: true, encoding: 'utf8' })
|
|
.filter((p) => p.endsWith('.ts'))
|
|
.map((p) => path.join(root, p));
|
|
return [
|
|
...under(SRC_ROOT),
|
|
...under(TEST_ROOT).filter((p) => !p.includes(`${path.sep}fixtures${path.sep}`)),
|
|
// This guard file itself holds the bad-load patterns as regex-probe fixtures.
|
|
].filter((p) => p !== self);
|
|
}
|
|
|
|
/**
|
|
* A bare ESM/CJS load of a vendored grammar package in real code. Covers every
|
|
* node_modules-forcing form — static `import … from`, side-effect `import 'x'`,
|
|
* dynamic `import('x')`, `require('x')`, `require.resolve('x')` — with single,
|
|
* double, OR backtick quotes, and an optional `/subpath`. Skips matches inside a
|
|
* leading-`//` or `*` comment (several query.ts files mention the bad pattern in
|
|
* prose, e.g. "`import Dart from 'tree-sitter-dart'` would throw"). Biased toward
|
|
* over-matching: a missed real load defeats the guard, a flagged trailing-comment
|
|
* mention only costs a glance.
|
|
*/
|
|
function bareVendoredLoadLines(file: string): string[] {
|
|
const names = [...VENDORED_GRAMMAR_PACKAGES].join('|');
|
|
// prefix: `from`, `import(`, `import ` (side-effect), `require(`, `require.resolve(`
|
|
const re = new RegExp(
|
|
`(?:from|import|require\\(|require\\.resolve\\()\\s*\\(?\\s*['"\\\`](?:${names})(?:/[^'"\\\`]*)?['"\\\`]`,
|
|
);
|
|
const hits: string[] = [];
|
|
for (const raw of readFileSync(file, 'utf8').split('\n')) {
|
|
const m = re.exec(raw);
|
|
if (!m) continue;
|
|
const trimmed = raw.trimStart();
|
|
const isComment = trimmed.startsWith('//') || trimmed.startsWith('*');
|
|
if (!isComment) hits.push(`${path.basename(file)}: ${raw.trim()}`);
|
|
}
|
|
return hits;
|
|
}
|
|
|
|
describe('vendored grammars load from vendor/ (#2111)', () => {
|
|
it('resolves every vendored grammar to a real dir under vendor/, never node_modules', () => {
|
|
expect(VENDOR_ROOT.endsWith(`${path.sep}vendor`)).toBe(true);
|
|
for (const pkg of VENDORED_GRAMMAR_PACKAGES) {
|
|
const dir = vendoredGrammarDir(pkg);
|
|
expect(dir.startsWith(VENDOR_ROOT)).toBe(true);
|
|
expect(dir.includes(`${path.sep}node_modules${path.sep}`)).toBe(false);
|
|
expect(existsSync(dir), `${pkg} missing under vendor/`).toBe(true);
|
|
}
|
|
});
|
|
|
|
it('loads each vendored grammar by absolute path (committed prebuild, no node_modules copy)', () => {
|
|
for (const pkg of VENDORED_GRAMMAR_PACKAGES) {
|
|
const grammar = requireVendoredGrammar(pkg);
|
|
expect(grammar, `${pkg} failed to load from vendor/`).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
it('no src/test file bare-imports/requires a vendored grammar (would force a node_modules copy back)', () => {
|
|
const offenders = policedFiles().flatMap(bareVendoredLoadLines);
|
|
expect(
|
|
offenders,
|
|
`Use requireVendoredGrammar(...) instead of a bare specifier:\n${offenders.join('\n')}`,
|
|
).toEqual([]);
|
|
});
|
|
|
|
it('guard regex catches every node_modules-forcing load form (and ignores prose mentions)', () => {
|
|
// Sanity-check the guard itself so the adversarial bypasses (#2144 review)
|
|
// stay closed: static/side-effect/dynamic/subpath/backtick all flagged,
|
|
// requireVendoredGrammar + leading-comment prose ignored.
|
|
const tmp = path.join(fileURLToPath(new URL('.', import.meta.url)), `__guard_probe__.ts.txt`);
|
|
const caught = [
|
|
`import C from 'tree-sitter-c';`,
|
|
`import 'tree-sitter-dart';`,
|
|
`await import('tree-sitter-kotlin');`,
|
|
`const x = require('tree-sitter-swift');`,
|
|
`require.resolve('tree-sitter-proto');`,
|
|
'const y = require(`tree-sitter-c`);',
|
|
`import Node from 'tree-sitter-c/bindings/node';`,
|
|
];
|
|
const ignored = [
|
|
`// import C from 'tree-sitter-c' would throw`,
|
|
` * mentions 'tree-sitter-dart' in a block comment`,
|
|
`requireVendoredGrammar('tree-sitter-c');`,
|
|
`import Cpp from 'tree-sitter-cpp';`, // not a vendored grammar
|
|
];
|
|
writeFileSync(tmp, [...caught, ...ignored].join('\n'));
|
|
try {
|
|
const flagged = bareVendoredLoadLines(tmp).map((l) => l.split(': ').slice(1).join(': '));
|
|
for (const c of caught) expect(flagged, `should flag: ${c}`).toContain(c);
|
|
for (const i of ignored) expect(flagged, `should NOT flag: ${i}`).not.toContain(i);
|
|
} finally {
|
|
rmSync(tmp, { force: true });
|
|
}
|
|
});
|
|
});
|