| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- /**
- * `codegraph.json` `includeIgnored` loader (#970, #976 / #622, #699).
- *
- * Parsing, validation, and mtime-caching of the opt-in patterns that re-include
- * gitignored directories for embedded-repo discovery. The behavioral end of this
- * feature (scanDirectory / discoverEmbeddedRepoRoots / sync honoring the patterns)
- * lives in `multi-repo-workspace.test.ts`; these are the loader unit tests,
- * mirroring the `extensions` loader coverage in `extension-mapping.test.ts`.
- *
- * Invariant under test: every failure mode degrades to the zero-config default
- * (empty patterns → `.gitignore` fully respected), never a throw.
- */
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
- import * as fs from 'node:fs';
- import * as path from 'node:path';
- import * as os from 'node:os';
- import { loadIncludeIgnoredPatterns, loadExtensionOverrides, clearProjectConfigCache } from '../src/project-config';
- describe('includeIgnored loader (codegraph.json)', () => {
- let dir: string;
- beforeEach(() => {
- dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-includeignored-'));
- clearProjectConfigCache();
- });
- afterEach(() => {
- clearProjectConfigCache();
- fs.rmSync(dir, { recursive: true, force: true });
- });
- const writeConfig = (obj: unknown) =>
- fs.writeFileSync(
- path.join(dir, 'codegraph.json'),
- typeof obj === 'string' ? obj : JSON.stringify(obj)
- );
- it('returns an empty list when there is no codegraph.json (the default)', () => {
- expect(loadIncludeIgnoredPatterns(dir)).toEqual([]);
- });
- it('loads a well-formed pattern array', () => {
- writeConfig({ includeIgnored: ['packages/', 'services/'] });
- expect(loadIncludeIgnoredPatterns(dir)).toEqual(['packages/', 'services/']);
- });
- it('trims whitespace and drops blank / non-string entries', () => {
- writeConfig({ includeIgnored: [' packages/ ', '', ' ', 42, null, 'services/'] });
- expect(loadIncludeIgnoredPatterns(dir)).toEqual(['packages/', 'services/']);
- });
- it('ignores a non-array includeIgnored value without throwing', () => {
- writeConfig({ includeIgnored: 'packages/' });
- expect(loadIncludeIgnoredPatterns(dir)).toEqual([]);
- });
- it('ignores malformed JSON without throwing', () => {
- writeConfig('{ not: valid json ');
- expect(loadIncludeIgnoredPatterns(dir)).toEqual([]);
- });
- it('returns [] when the field is absent but other config is present', () => {
- writeConfig({ extensions: { '.foo': 'typescript' } });
- expect(loadIncludeIgnoredPatterns(dir)).toEqual([]);
- });
- it('coexists with extensions in one file (shared single parse)', () => {
- writeConfig({ extensions: { '.foo': 'typescript' }, includeIgnored: ['vendor/'] });
- expect(loadExtensionOverrides(dir)).toEqual({ '.foo': 'typescript' });
- expect(loadIncludeIgnoredPatterns(dir)).toEqual(['vendor/']);
- });
- it('picks up a changed config (mtime-invalidated cache)', () => {
- writeConfig({ includeIgnored: ['packages/'] });
- expect(loadIncludeIgnoredPatterns(dir)).toEqual(['packages/']);
- writeConfig({ includeIgnored: ['services/'] });
- // Force a distinct mtime in case the filesystem clock is coarse.
- const future = new Date(Date.now() + 2000);
- fs.utimesSync(path.join(dir, 'codegraph.json'), future, future);
- expect(loadIncludeIgnoredPatterns(dir)).toEqual(['services/']);
- });
- it('drops the patterns again when the config file is removed', () => {
- writeConfig({ includeIgnored: ['packages/'] });
- expect(loadIncludeIgnoredPatterns(dir)).toEqual(['packages/']);
- fs.rmSync(path.join(dir, 'codegraph.json'));
- expect(loadIncludeIgnoredPatterns(dir)).toEqual([]);
- });
- });
|