Skip to content

Commit b09052c

Browse files
fix(parse-tsconfig): preserve files when include is also present (#128)
When a tsconfig extends a base config that defines `files`, and the child config defines `include`, the inherited `files` array was deleted (`delete config.files`). TypeScript itself preserves both properties — `files` entries are always included regardless of `include`/`exclude`. **Before:** `files` from extended config silently dropped when child has `include` **After:** `files` paths are normalized instead of deleted, matching TypeScript behavior ### Scenario ```json // tsconfig.base.json { "files": ["types/globals.d.ts"] } // tsconfig.json { "extends": "./tsconfig.base.json", "include": ["src"] } ``` `get-tsconfig` returned `{ include: ["src"] }` — `files` was lost. TypeScript resolves both: the `files` entries *and* the `include` glob. ### Verified against TypeScript source `commandLineParser.ts` lines 3463–3473: `files`, `include`, and `exclude` are independently inheritable via `setPropertyInResultIfNotUndefined`. Line 3143: the default `include: ["**/*"]` only applies when both `files` and `include` are absent. --------- Co-authored-by: Hiroki Osame <hiroki.osame@gmail.com>
1 parent e64637d commit b09052c

2 files changed

Lines changed: 75 additions & 4 deletions

File tree

src/parse-tsconfig/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,9 @@ const _parseTsconfig = (
233233

234234
if (config.include) {
235235
config.include = config.include.map(slash);
236+
}
236237

237-
if (config.files) {
238-
delete config.files;
239-
}
240-
} else if (config.files) {
238+
if (config.files) {
241239
config.files = config.files.map(file => (
242240
file.startsWith(configDirPlaceholder)
243241
? file

tests/specs/parse-tsconfig/extends/merges.spec.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,79 @@ export default testSuite(({ describe }) => {
140140
expect(tsconfig).toStrictEqual(expectedTsconfig);
141141
});
142142

143+
test('preserved when child has include', async () => {
144+
await using fixture = await createFixture({
145+
src: {
146+
'a.ts': '',
147+
},
148+
types: {
149+
'globals.d.ts': '',
150+
},
151+
'tsconfig.base.json': createTsconfigJson({
152+
files: [
153+
'types/globals.d.ts',
154+
],
155+
}),
156+
'tsconfig.json': createTsconfigJson({
157+
extends: './tsconfig.base.json',
158+
include: ['src'],
159+
}),
160+
});
161+
162+
const expectedTsconfig = await getTscTsconfig(fixture.path);
163+
// tsc expands include globs into the files array, so compare
164+
// include/files/compilerOptions independently
165+
const tsconfig = parseTsconfig(fixture.getPath('tsconfig.json'));
166+
expect(tsconfig.include).toStrictEqual(expectedTsconfig.include);
167+
expect(tsconfig.files).toStrictEqual(['./types/globals.d.ts']);
168+
expect(tsconfig.compilerOptions).toStrictEqual(expectedTsconfig.compilerOptions);
169+
});
170+
171+
test('preserved when child has files and parent has include', async () => {
172+
await using fixture = await createFixture({
173+
src: {
174+
'a.ts': '',
175+
},
176+
'globals.d.ts': '',
177+
'tsconfig.base.json': createTsconfigJson({
178+
include: ['src'],
179+
}),
180+
'tsconfig.json': createTsconfigJson({
181+
extends: './tsconfig.base.json',
182+
files: ['globals.d.ts'],
183+
}),
184+
});
185+
186+
const expectedTsconfig = await getTscTsconfig(fixture.path);
187+
const tsconfig = parseTsconfig(fixture.getPath('tsconfig.json'));
188+
expect(tsconfig.include).toStrictEqual(expectedTsconfig.include);
189+
expect(tsconfig.files).toStrictEqual(['./globals.d.ts']);
190+
});
191+
192+
test('both inherited from parent', async () => {
193+
await using fixture = await createFixture({
194+
src: {
195+
'a.ts': '',
196+
},
197+
types: {
198+
'globals.d.ts': '',
199+
},
200+
'tsconfig.base.json': createTsconfigJson({
201+
files: ['types/globals.d.ts'],
202+
include: ['src'],
203+
}),
204+
'tsconfig.json': createTsconfigJson({
205+
extends: './tsconfig.base.json',
206+
}),
207+
});
208+
209+
const expectedTsconfig = await getTscTsconfig(fixture.path);
210+
const tsconfig = parseTsconfig(fixture.getPath('tsconfig.json'));
211+
expect(tsconfig.include).toStrictEqual(expectedTsconfig.include);
212+
// tsc expands include globs into files; get-tsconfig keeps them separate
213+
expect(tsconfig.files).toStrictEqual(['./types/globals.d.ts']);
214+
});
215+
143216
test('gets overwritten', async () => {
144217
await using fixture = await createFixture({
145218
'some-dir': {

0 commit comments

Comments
 (0)