Skip to content

Commit 74adeee

Browse files
authored
fix(compiler): respect project tsconfig watch options (#5916)
* fix(compiler): respect project tsconfig watch options Fixes: #5709 STENCIL-1079 * add tests * export function
1 parent fd75952 commit 74adeee

File tree

6 files changed

+121
-8
lines changed

6 files changed

+121
-8
lines changed

src/compiler/config/load-config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export const loadConfig = async (init: LoadConfigInit = {}): Promise<LoadConfigR
8282

8383
results.config.tsconfig = tsConfigResults.path;
8484
results.config.tsCompilerOptions = tsConfigResults.compilerOptions;
85+
results.config.tsWatchOptions = tsConfigResults.watchOptions;
8586

8687
results.tsconfig.path = tsConfigResults.path;
8788
results.tsconfig.compilerOptions = JSON.parse(JSON.stringify(tsConfigResults.compilerOptions));
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
1-
import { hasSrcDirectoryInclude } from '../typescript-config';
1+
import ts from 'typescript';
2+
3+
import { ValidatedConfig } from '../../../../declarations';
4+
import { mockValidatedConfig } from '../../../../testing/mocks';
5+
import { createTestingSystem, TestingSystem } from '../../../../testing/testing-sys';
6+
import * as tsConfig from '../typescript-config';
27

38
describe('typescript-config', () => {
49
describe('hasSrcDirectoryInclude', () => {
510
it('returns `false` for a non-array argument', () => {
611
// the intent of this test is to evaluate when a user doesn't provide an array, hence the type assertion
7-
expect(hasSrcDirectoryInclude('src' as unknown as string[], 'src')).toBe(false);
12+
expect(tsConfig.hasSrcDirectoryInclude('src' as unknown as string[], 'src')).toBe(false);
813
});
914

1015
it('returns `false` for an empty array', () => {
11-
expect(hasSrcDirectoryInclude([], 'src/')).toBe(false);
16+
expect(tsConfig.hasSrcDirectoryInclude([], 'src/')).toBe(false);
1217
});
1318

1419
it('returns `false` when an entry does not exist in the array', () => {
15-
expect(hasSrcDirectoryInclude(['src'], 'source')).toBe(false);
20+
expect(tsConfig.hasSrcDirectoryInclude(['src'], 'source')).toBe(false);
1621
});
1722

1823
it('returns `true` when an entry does exist in the array', () => {
19-
expect(hasSrcDirectoryInclude(['src', 'foo'], 'src')).toBe(true);
24+
expect(tsConfig.hasSrcDirectoryInclude(['src', 'foo'], 'src')).toBe(true);
2025
});
2126

2227
it('returns `true` for globs', () => {
23-
expect(hasSrcDirectoryInclude(['src/**/*.ts', 'foo/'], 'src/**/*.ts')).toBe(true);
28+
expect(tsConfig.hasSrcDirectoryInclude(['src/**/*.ts', 'foo/'], 'src/**/*.ts')).toBe(true);
2429
});
2530

2631
it.each([
@@ -29,7 +34,53 @@ describe('typescript-config', () => {
2934
[['../src'], '../src'],
3035
[['*'], './*'],
3136
])('returns `true` for relative paths', (includedPaths, srcDir) => {
32-
expect(hasSrcDirectoryInclude(includedPaths, srcDir)).toBe(true);
37+
expect(tsConfig.hasSrcDirectoryInclude(includedPaths, srcDir)).toBe(true);
38+
});
39+
});
40+
41+
describe('validateTsConfig', () => {
42+
let mockSys: TestingSystem;
43+
let config: ValidatedConfig;
44+
let tsSpy: jest.SpyInstance;
45+
46+
beforeEach(() => {
47+
mockSys = createTestingSystem();
48+
config = mockValidatedConfig();
49+
50+
jest.spyOn(tsConfig, 'getTsConfigPath').mockResolvedValue({
51+
path: 'tsconfig.json',
52+
content: '',
53+
});
54+
tsSpy = jest.spyOn(ts, 'getParsedCommandLineOfConfigFile');
55+
});
56+
57+
it('includes watchOptions when provided', async () => {
58+
tsSpy.mockReturnValueOnce({
59+
watchOptions: {
60+
excludeFiles: ['exclude.ts'],
61+
excludeDirectories: ['exclude-dir'],
62+
},
63+
options: null,
64+
fileNames: [],
65+
errors: [],
66+
});
67+
68+
const result = await tsConfig.validateTsConfig(config, mockSys, {});
69+
expect(result.watchOptions).toEqual({
70+
excludeFiles: ['exclude.ts'],
71+
excludeDirectories: ['exclude-dir'],
72+
});
73+
});
74+
75+
it('does not include watchOptions when not provided', async () => {
76+
tsSpy.mockReturnValueOnce({
77+
options: null,
78+
fileNames: [],
79+
errors: [],
80+
});
81+
82+
const result = await tsConfig.validateTsConfig(config, mockSys, {});
83+
expect(result.watchOptions).toEqual({});
3384
});
3485
});
3586
});

src/compiler/sys/typescript/typescript-config.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const validateTsConfig = async (config: d.ValidatedConfig, sys: d.Compile
1717
const tsconfig = {
1818
path: '',
1919
compilerOptions: {} as ts.CompilerOptions,
20+
watchOptions: {} as ts.WatchOptions,
2021
files: [] as string[],
2122
include: [] as string[],
2223
exclude: [] as string[],
@@ -91,6 +92,10 @@ export const validateTsConfig = async (config: d.ValidatedConfig, sys: d.Compile
9192
}
9293
}
9394

95+
if (results.watchOptions) {
96+
tsconfig.watchOptions = results.watchOptions;
97+
}
98+
9499
if (results.options) {
95100
tsconfig.compilerOptions = results.options;
96101

@@ -119,7 +124,7 @@ export const validateTsConfig = async (config: d.ValidatedConfig, sys: d.Compile
119124
return tsconfig;
120125
};
121126

122-
const getTsConfigPath = async (
127+
export const getTsConfigPath = async (
123128
config: d.ValidatedConfig,
124129
sys: d.CompilerSystem,
125130
init: d.LoadConfigInit,

src/compiler/transpile/create-watch-program.ts

+9
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ export const createTsWatchProgram = async (
8282
(reportWatchStatus) => {
8383
config.logger.debug(reportWatchStatus.messageText);
8484
},
85+
// We don't want to allow users to mess with the watch method, so
86+
// we only strip out the excludeFiles and excludeDirectories properties
87+
// to allow the user to still have control over which files get excluded from the watcher
88+
config.tsWatchOptions
89+
? {
90+
excludeFiles: config.tsWatchOptions.excludeFiles,
91+
excludeDirectories: config.tsWatchOptions.excludeDirectories,
92+
}
93+
: undefined,
8594
);
8695

8796
// Add a callback that will execute whenever a new instance
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import ts from 'typescript';
2+
3+
import { ValidatedConfig } from '../../../declarations';
4+
import { mockValidatedConfig } from '../../../testing/mocks';
5+
import { createTsWatchProgram } from '../create-watch-program';
6+
7+
describe('createWatchProgram', () => {
8+
let config: ValidatedConfig;
9+
10+
beforeEach(() => {
11+
config = mockValidatedConfig();
12+
});
13+
14+
afterEach(() => {
15+
jest.restoreAllMocks();
16+
});
17+
18+
it('includes watchOptions in the watch program creation', async () => {
19+
config.tsWatchOptions = {
20+
fallbackPolling: 3,
21+
excludeFiles: ['src/components/my-component/my-component.tsx'],
22+
excludeDirectories: ['src/components/my-other-component'],
23+
} as ts.WatchOptions;
24+
config.tsconfig = '';
25+
const tsSpy = jest.spyOn(ts, 'createWatchCompilerHost').mockReturnValue({} as any);
26+
jest.spyOn(ts, 'createWatchProgram').mockReturnValue({} as any);
27+
28+
await createTsWatchProgram(config, () => new Promise(() => {}));
29+
30+
expect(tsSpy.mock.calls[0][6]).toEqual({
31+
excludeFiles: ['src/components/my-component/my-component.tsx'],
32+
excludeDirectories: ['src/components/my-other-component'],
33+
});
34+
});
35+
36+
it('omits watchOptions when not provided', async () => {
37+
config.tsWatchOptions = undefined;
38+
config.tsconfig = '';
39+
const tsSpy = jest.spyOn(ts, 'createWatchCompilerHost').mockReturnValue({} as any);
40+
jest.spyOn(ts, 'createWatchProgram').mockReturnValue({} as any);
41+
42+
await createTsWatchProgram(config, () => new Promise(() => {}));
43+
44+
expect(tsSpy.mock.calls[0][6]).toEqual(undefined);
45+
});
46+
});

src/declarations/stencil-public-compiler.ts

+1
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ export interface Config extends StencilConfig {
428428
suppressLogs?: boolean;
429429
profile?: boolean;
430430
tsCompilerOptions?: any;
431+
tsWatchOptions?: any;
431432
_isValidated?: boolean;
432433
_isTesting?: boolean;
433434
}

0 commit comments

Comments
 (0)