mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-07 02:58:02 +00:00
fix(ingestion): ignore emitted Next.js build output, and restore the dead public/build entry
`DEFAULT_IGNORE_LIST` contained `.next` — the build CACHE — but not `_next`, the emitted OUTPUT, which are different directories. A Capacitor/Cordova shell copies a built Next.js bundle to `<platform>/app/src/main/assets/public/_next/static/`, where no path segment hits the list, so the walker indexed the bundle as source. On a real mobile-wrapped Next.js app that was 256 minified chunk files, and every `Route` node the repo produced pointed at a webpack chunk rather than at source. The filename heuristics did not catch them either: they match `.bundle.`, `.chunk.`, `.generated.` and `.d.ts`, while Next.js emits hashed names like `6862-9d1cdcb99f169a06.js`. Separately, `'public/build'` had been sitting in `DEFAULT_IGNORE_LIST` matching nothing at all. That set is tested one path SEGMENT at a time, and is also read by `isHardcodedIgnoredDirectory(name)`, which receives a bare directory name — so a slash-containing member can never compare equal to anything. Rather than delete the entry and lose its intent, multi-segment paths now live in `DEFAULT_IGNORED_PATH_FRAGMENTS` and are matched against the whole path, so Remix / Laravel Mix asset output is ignored as originally intended. A guard test pins the invariant that made the dead entry possible: no member of the name set may contain a slash. Measured against a production Capacitor-wrapped Next.js app (1558 JS/TS files on disk): 256 newly ignored, none of them under `src/`, and zero files that were previously ignored become indexed. Closes #3007 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
aac7515d2a
commit
f71e718d4d
2 changed files with 114 additions and 1 deletions
|
|
@ -54,13 +54,19 @@ const DEFAULT_IGNORE_LIST = new Set([
|
|||
'obj',
|
||||
'target', // Java/Rust
|
||||
'.next',
|
||||
// `.next` is Next.js's build CACHE; `_next` is the EMITTED output, and the two
|
||||
// are different directories. A Capacitor/Cordova shell copies the emitted
|
||||
// bundle to `<platform>/app/src/main/assets/public/_next/static/…`, where none
|
||||
// of the path segments hit this list — so a mobile-wrapped Next.js app had 40%
|
||||
// of its indexed files come from minified chunks, and every Route node it
|
||||
// produced pointed at a webpack bundle rather than source (#3007).
|
||||
'_next',
|
||||
'.nuxt',
|
||||
'.output',
|
||||
'.vercel',
|
||||
'.netlify',
|
||||
'.serverless',
|
||||
'_build',
|
||||
'public/build',
|
||||
'.parcel-cache',
|
||||
'.turbo',
|
||||
'.svelte-kit',
|
||||
|
|
@ -106,6 +112,31 @@ const DEFAULT_IGNORE_LIST = new Set([
|
|||
'__snapshots__',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Multi-segment paths to ignore, matched against the whole POSIX path.
|
||||
*
|
||||
* These CANNOT live in {@link DEFAULT_IGNORE_LIST}: that set is tested one path
|
||||
* SEGMENT at a time (`parts.some(p => DEFAULT_IGNORE_LIST.has(p))`) and is also
|
||||
* exposed through `isHardcodedIgnoredDirectory(name)`, which receives a bare
|
||||
* directory name. A slash-containing member can never equal a single segment,
|
||||
* so `'public/build'` sat in that set matching nothing at all until #3007. A
|
||||
* guard test pins the invariant that the name set stays slash-free.
|
||||
*/
|
||||
const DEFAULT_IGNORED_PATH_FRAGMENTS: readonly string[] = [
|
||||
'public/build', // Remix / Laravel Mix compiled asset output
|
||||
];
|
||||
|
||||
/** True when `normalizedPath` contains any ignored multi-segment fragment. */
|
||||
function hasIgnoredPathFragment(normalizedPath: string): boolean {
|
||||
return DEFAULT_IGNORED_PATH_FRAGMENTS.some(
|
||||
(fragment) =>
|
||||
normalizedPath === fragment ||
|
||||
normalizedPath.startsWith(`${fragment}/`) ||
|
||||
normalizedPath.includes(`/${fragment}/`) ||
|
||||
normalizedPath.endsWith(`/${fragment}`),
|
||||
);
|
||||
}
|
||||
|
||||
const IGNORED_EXTENSIONS = new Set([
|
||||
// Images
|
||||
'.png',
|
||||
|
|
@ -306,6 +337,11 @@ export const shouldIgnorePath = (filePath: string): boolean => {
|
|||
}
|
||||
}
|
||||
|
||||
// Multi-segment entries cannot be matched by the per-segment loop above.
|
||||
if (hasIgnoredPathFragment(normalizedPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check exact filename matches
|
||||
if (IGNORED_FILES.has(fileName) || IGNORED_FILES.has(fileNameLower)) {
|
||||
return true;
|
||||
|
|
|
|||
77
gitnexus/test/unit/ignore-build-output.test.ts
Normal file
77
gitnexus/test/unit/ignore-build-output.test.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { shouldIgnorePath } from '../../src/config/ignore-service.js';
|
||||
|
||||
/**
|
||||
* Emitted build output must not be indexed as source (#3007).
|
||||
*
|
||||
* `.next` (the build cache) was listed but `_next` (the emitted output) was
|
||||
* not, so a Capacitor/Cordova shell that copies a Next.js bundle into
|
||||
* `<platform>/app/src/main/assets/public/_next/static/` had 40% of its indexed
|
||||
* files come from minified chunks — and every `Route` node the repo produced
|
||||
* pointed at a webpack bundle instead of source.
|
||||
*/
|
||||
|
||||
describe('build-output ignores', () => {
|
||||
it('ignores emitted _next output, including the Capacitor/Cordova copy', () => {
|
||||
expect(shouldIgnorePath('_next/static/chunks/main.js')).toBe(true);
|
||||
expect(shouldIgnorePath('.next/server/app/page.js')).toBe(true);
|
||||
expect(
|
||||
shouldIgnorePath(
|
||||
'android/app/src/main/assets/public/_next/static/chunks/6862-9d1cdcb99f169a06.js',
|
||||
),
|
||||
).toBe(true);
|
||||
expect(shouldIgnorePath('ios/App/App/public/_next/static/chunks/framework-abc123.js')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not ignore ordinary source that merely mentions next', () => {
|
||||
expect(shouldIgnorePath('src/next-steps.ts')).toBe(false);
|
||||
expect(shouldIgnorePath('src/nextConfig/index.ts')).toBe(false);
|
||||
expect(shouldIgnorePath('packages/next-auth/src/index.ts')).toBe(false);
|
||||
});
|
||||
|
||||
it('ignores public/build, which never matched while it sat in the name set', () => {
|
||||
// A slash-containing member of DEFAULT_IGNORE_LIST can never equal a single
|
||||
// path segment, so this entry matched nothing at all before #3007.
|
||||
expect(shouldIgnorePath('public/build/entry.client.js')).toBe(true);
|
||||
expect(shouldIgnorePath('apps/web/public/build/manifest.js')).toBe(true);
|
||||
});
|
||||
|
||||
it('does not ignore public/ or build/-adjacent source outside that pair', () => {
|
||||
expect(shouldIgnorePath('public/favicon-loader.ts')).toBe(false);
|
||||
expect(shouldIgnorePath('src/public/api.ts')).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps the name set free of slashes so a fragment cannot silently die', () => {
|
||||
// The invariant that makes the bug above impossible to reintroduce: entries
|
||||
// needing a slash belong in DEFAULT_IGNORED_PATH_FRAGMENTS.
|
||||
const source = fs.readFileSync(
|
||||
path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
'..',
|
||||
'src',
|
||||
'config',
|
||||
'ignore-service.ts',
|
||||
),
|
||||
'utf8',
|
||||
);
|
||||
const block = source.slice(
|
||||
source.indexOf('const DEFAULT_IGNORE_LIST = new Set(['),
|
||||
source.indexOf(']);', source.indexOf('const DEFAULT_IGNORE_LIST = new Set([')),
|
||||
);
|
||||
// Parse ENTRY LINES only. Scanning the raw block would also read prose in
|
||||
// the comments (an apostrophe in "Next.js's" opens a spurious quote).
|
||||
const entries = block
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line.startsWith("'"))
|
||||
.map((line) => line.slice(1, line.indexOf("'", 1)));
|
||||
expect(entries.length).toBeGreaterThan(20); // parsed something real
|
||||
expect(entries.filter((e) => e.includes('/'))).toEqual([]);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue