Skip to content

Commit ca85879

Browse files
authored
feat(config): allow suppressing reserved event name warnings (#6748)
* feat(config): add support for suppressing warnings on reserved event names * test(parse-events): update warning message for native DOM event name check * fix(event-decorator): correct typo in JSDoc for validateEventName function
1 parent 06258c8 commit ca85879

7 files changed

Lines changed: 82 additions & 5 deletions

File tree

src/compiler/config/test/validate-config.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,19 @@ describe('validation', () => {
129129
});
130130
});
131131

132+
describe('suppressReservedEventNameWarnings', () => {
133+
it.each([true, false])('sets suppressReservedEventNameWarnings to %p when provided', (bool) => {
134+
userConfig.suppressReservedEventNameWarnings = bool;
135+
const { config } = validateConfig(userConfig, bootstrapConfig);
136+
expect(config.suppressReservedEventNameWarnings).toBe(bool);
137+
});
138+
139+
it('defaults suppressReservedEventNameWarnings to false', () => {
140+
const { config } = validateConfig(userConfig, bootstrapConfig);
141+
expect(config.suppressReservedEventNameWarnings).toBe(false);
142+
});
143+
});
144+
132145
describe('enableCache', () => {
133146
it('set enableCache true', () => {
134147
userConfig.enableCache = true;

src/compiler/config/validate-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export const validateConfig = (
212212
setBooleanConfig(validatedConfig, 'validateTypes', null, !validatedConfig._isTesting);
213213
setBooleanConfig(validatedConfig, 'allowInlineScripts', null, true);
214214
setBooleanConfig(validatedConfig, 'suppressReservedPublicNameWarnings', null, false);
215+
setBooleanConfig(validatedConfig, 'suppressReservedEventNameWarnings', null, false);
215216

216217
if (!isString(validatedConfig.taskQueue)) {
217218
validatedConfig.taskQueue = 'async';

src/compiler/transformers/decorators-to-static/convert-decorators.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ const visitClassDeclaration = (
148148
);
149149
stateDecoratorsToStatic(decoratedMembers, filteredMethodsAndFields, typeChecker, importAliasMap.get('State'));
150150
eventDecoratorsToStatic(
151+
config,
151152
diagnostics,
152153
decoratedMembers,
153154
typeChecker,

src/compiler/transformers/decorators-to-static/event-decorator.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { getDecoratorParameters, isDecoratorNamed } from './decorator-utils';
1414

1515
export const eventDecoratorsToStatic = (
16+
config: d.ValidatedConfig,
1617
diagnostics: d.Diagnostic[],
1718
decoratedProps: ts.ClassElement[],
1819
typeChecker: ts.TypeChecker,
@@ -22,7 +23,7 @@ export const eventDecoratorsToStatic = (
2223
) => {
2324
const events = decoratedProps
2425
.filter(ts.isPropertyDeclaration)
25-
.map((prop) => parseEventDecorator(diagnostics, typeChecker, program, prop, decoratorName))
26+
.map((prop) => parseEventDecorator(config, diagnostics, typeChecker, program, prop, decoratorName))
2627
.filter((ev) => !!ev);
2728

2829
if (events.length > 0) {
@@ -33,6 +34,7 @@ export const eventDecoratorsToStatic = (
3334
/**
3435
* Parse a single instance of Stencil's `@Event()` decorator and generate metadata for the class member that is
3536
* decorated
37+
* @param config a user-supplied Stencil config
3638
* @param diagnostics a list of diagnostics used as a part of the parsing process. Any parse errors/warnings shall be
3739
* added to this collection
3840
* @param typeChecker an instance of the TypeScript type checker, used to generate information about the `@Event()` and
@@ -43,6 +45,7 @@ export const eventDecoratorsToStatic = (
4345
* @returns generated metadata for the class member decorated by `@Event()`, or `null` if none could be derived
4446
*/
4547
const parseEventDecorator = (
48+
config: d.ValidatedConfig,
4649
diagnostics: d.Diagnostic[],
4750
typeChecker: ts.TypeChecker,
4851
program: ts.Program,
@@ -64,7 +67,7 @@ const parseEventDecorator = (
6467
const symbol = typeChecker.getSymbolAtLocation(prop.name);
6568
const eventName = getEventName(eventOpts, memberName);
6669

67-
validateEventName(diagnostics, prop.name, eventName);
70+
validateEventName(config, diagnostics, prop.name, eventName);
6871

6972
const eventMeta = {
7073
method: memberName,
@@ -130,12 +133,18 @@ const getEventType = (type: ts.TypeNode): ts.TypeNode | null => {
130133
*
131134
* This function assumes that the name of the event has been determined prior to calling it
132135
*
136+
* @param config a user-supplied Stencil config
133137
* @param diagnostics a list of diagnostics used as a part of the validation process. Any parse errors/warnings shall be
134138
* added to this collection
135-
* @param node the node in the AT containing the class member decorated with `@Event()`
139+
* @param node the node in the AST containing the class member decorated with `@Event()`
136140
* @param eventName the name of the event
137141
*/
138-
const validateEventName = (diagnostics: d.Diagnostic[], node: ts.Node, eventName: string): void => {
142+
const validateEventName = (
143+
config: d.ValidatedConfig,
144+
diagnostics: d.Diagnostic[],
145+
node: ts.Node,
146+
eventName: string,
147+
): void => {
139148
// this regex checks for a string that begins with a capital letter - e.g. 'AskJeeves', 'Zoo', 'Spotify'
140149
if (/^[A-Z]/.test(eventName)) {
141150
const diagnostic = buildWarn(diagnostics);
@@ -157,7 +166,7 @@ const validateEventName = (diagnostics: d.Diagnostic[], node: ts.Node, eventName
157166
return;
158167
}
159168

160-
if (DOM_EVENT_NAMES.has(eventName.toLowerCase())) {
169+
if (!config.suppressReservedEventNameWarnings && DOM_EVENT_NAMES.has(eventName.toLowerCase())) {
161170
const diagnostic = buildWarn(diagnostics);
162171
diagnostic.messageText = `The event name conflicts with the "${eventName}" native DOM event name.`;
163172
augmentDiagnosticWithNode(diagnostic, node);

src/compiler/transformers/test/parse-events.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,51 @@ describe('parse events', () => {
240240
});
241241
});
242242
});
243+
244+
describe('suppressReservedEventNameWarnings', () => {
245+
it('should warn when using native DOM event name and flag is unset (default)', () => {
246+
expect(() => {
247+
transpileModule(
248+
`
249+
@Component({tag: 'cmp-a'})
250+
export class CmpA {
251+
@Event({ eventName: 'click' })
252+
clickEvent: EventEmitter<void>;
253+
}
254+
`,
255+
);
256+
}).toThrow(/"click".*native DOM event/);
257+
});
258+
259+
it('should not warn when using native DOM event name and flag is true', () => {
260+
const t = transpileModule(
261+
`
262+
@Component({tag: 'cmp-a'})
263+
export class CmpA {
264+
@Event({ eventName: 'click' })
265+
clickEvent: EventEmitter<void>;
266+
}
267+
`,
268+
{ suppressReservedEventNameWarnings: true },
269+
);
270+
271+
expect(t.event).toEqual({
272+
name: 'click',
273+
method: 'clickEvent',
274+
bubbles: true,
275+
cancelable: true,
276+
composed: true,
277+
internal: false,
278+
complexType: {
279+
original: 'void',
280+
resolved: 'void',
281+
references: {},
282+
},
283+
docs: {
284+
text: '',
285+
tags: [],
286+
},
287+
});
288+
});
289+
});
243290
});

src/declarations/stencil-public-compiler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ export interface StencilConfig {
160160
* (for example, decorating a method named `focus` with `@Method()`). Defaults to `false`.
161161
*/
162162
suppressReservedPublicNameWarnings?: boolean;
163+
/**
164+
* When `true`, Stencil will suppress diagnostics which warn about event names conflicting with native DOM event names. Defaults to `false`.
165+
*/
166+
suppressReservedEventNameWarnings?: boolean;
163167
/**
164168
* When `true`, we will validate a project's `package.json` based on the output target the user has designated
165169
* as `isPrimaryPackageOutputTarget: true` in their Stencil config.

src/testing/mocks.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export function mockValidatedConfig(overrides: Partial<d.ValidatedConfig> = {}):
5050
srcDir: '/src',
5151
srcIndexHtml: 'src/index.html',
5252
suppressReservedPublicNameWarnings: false,
53+
suppressReservedEventNameWarnings: false,
5354
sys: createTestingSystem(),
5455
testing: {},
5556
transformAliasedImportPaths: true,
@@ -108,6 +109,7 @@ export function mockConfig(overrides: Partial<d.UnvalidatedConfig> = {}): d.Unva
108109
rootDir,
109110
sourceMap: true,
110111
suppressReservedPublicNameWarnings: false,
112+
suppressReservedEventNameWarnings: false,
111113
sys,
112114
testing: null,
113115
validateTypes: false,

0 commit comments

Comments
 (0)