Skip to content

Commit ac7509b

Browse files
committed
feat(compiler): add noDashCaseTypes option to control dash-cased type generation
Allows for `noDashCaseTypes` to be set in Config to prevent generation of non-camelCase types from attribute names in `components.d.ts` during build. fixes: stenciljs#6178 feat: allow dash-case type generation to be optional
1 parent 03a5ff9 commit ac7509b

File tree

6 files changed

+129
-17
lines changed

6 files changed

+129
-17
lines changed

src/compiler/config/validate-config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export const validateConfig = (
131131
hydratedFlag: validateHydrated(config),
132132
logLevel,
133133
logger,
134+
noDashCaseTypes: config.noDashCaseTypes ?? false,
134135
minifyCss: config.minifyCss ?? !devMode,
135136
minifyJs: config.minifyJs ?? !devMode,
136137
outputTargets: config.outputTargets ?? [],

src/compiler/types/generate-app-types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const generateComponentTypesFile = (
8989
*/
9090
componentEventDetailTypes.push(generateEventDetailTypes(cmp));
9191
}
92-
return generateComponentTypes(cmp, typeImportData, areTypesInternal);
92+
return generateComponentTypes(config, cmp, typeImportData, areTypesInternal);
9393
});
9494

9595
c.push(COMPONENTS_DTS_HEADER);

src/compiler/types/generate-component-types.ts

+31-16
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,22 @@ import { generatePropTypes } from './generate-prop-types';
88

99
/**
1010
* Generate a string based on the types that are defined within a component
11+
* @param config the validated config for the Stencil project
1112
* @param cmp the metadata for the component that a type definition string is generated for
1213
* @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
1314
* @param areTypesInternal `true` if types being generated are for a project's internal purposes, `false` otherwise
1415
* @returns the generated types string alongside additional metadata
1516
*/
1617
export const generateComponentTypes = (
18+
config: d.ValidatedConfig,
1719
cmp: d.ComponentCompilerMeta,
1820
typeImportData: d.TypesImportData,
1921
areTypesInternal: boolean,
2022
): d.TypesModule => {
2123
const tagName = cmp.tagName.toLowerCase();
2224
const tagNameAsPascal = dashToPascalCase(tagName);
2325
const htmlElementName = `HTML${tagNameAsPascal}Element`;
26+
const noDashCaseTypes = config.noDashCaseTypes;
2427

2528
const propAttributes = generatePropTypes(cmp, typeImportData);
2629
const methodAttributes = generateMethodTypes(cmp, typeImportData);
@@ -31,9 +34,15 @@ export const generateComponentTypes = (
3134
[...propAttributes, ...methodAttributes],
3235
false,
3336
areTypesInternal,
37+
noDashCaseTypes,
3438
);
3539
const isDep = cmp.isCollectionDependency;
36-
const jsxAttributes = attributesToMultiLineString([...propAttributes, ...eventAttributes], true, areTypesInternal);
40+
const jsxAttributes = attributesToMultiLineString(
41+
[...propAttributes, ...eventAttributes],
42+
true,
43+
areTypesInternal,
44+
noDashCaseTypes,
45+
);
3746

3847
const element = [
3948
...htmlElementEventMap,
@@ -60,7 +69,12 @@ export const generateComponentTypes = (
6069
};
6170
};
6271

63-
const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: boolean, internal: boolean) => {
72+
const attributesToMultiLineString = (
73+
attributes: d.TypeInfo,
74+
jsxAttributes: boolean,
75+
internal: boolean,
76+
noDashCaseTypes: boolean,
77+
) => {
6478
const attributesStr = sortBy(attributes, (a) => a.name)
6579
.filter((type) => {
6680
if (jsxAttributes && !internal && type.internal) {
@@ -76,20 +90,21 @@ const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: bool
7690
}
7791
const optional = jsxAttributes ? !type.required : type.optional;
7892
fullList.push(` "${type.name}"${optional ? '?' : ''}: ${type.type};`);
79-
80-
/**
81-
* deprecated usage of dash-casing in JSX, use camelCase instead
82-
*/
83-
if (type.attributeName && type.attributeName !== type.name) {
84-
const padding = ' '.repeat(8);
85-
fullList.push(
86-
[
87-
`${padding}/**`,
88-
`${padding} * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5.`,
89-
`${padding} */`,
90-
].join('\n'),
91-
);
92-
fullList.push(`${padding}"${type.attributeName}"?: ${type.type};`);
93+
if (noDashCaseTypes === false) {
94+
/**
95+
* deprecated usage of dash-casing in JSX, use camelCase instead
96+
*/
97+
if (type.attributeName && type.attributeName !== type.name) {
98+
const padding = ' '.repeat(8);
99+
fullList.push(
100+
[
101+
`${padding}/**`,
102+
`${padding} * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5.`,
103+
`${padding} */`,
104+
].join('\n'),
105+
);
106+
fullList.push(`${padding}"${type.attributeName}"?: ${type.type};`);
107+
}
93108
}
94109

95110
return fullList;

src/compiler/types/tests/generate-app-types.spec.ts

+89
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,95 @@ declare module "@stencil/core" {
14881488
}
14891489
}
14901490
}
1491+
`,
1492+
{
1493+
immediateWrite: true,
1494+
},
1495+
);
1496+
});
1497+
1498+
it('should not generate dash-case types if noDashCaseTypes is set to true', async () => {
1499+
const compilerComponentMeta = stubComponentCompilerMeta({
1500+
tagName: 'my-component',
1501+
componentClassName: 'MyComponent',
1502+
hasProp: true,
1503+
properties: [
1504+
stubComponentCompilerProperty({
1505+
name: 'name',
1506+
complexType: {
1507+
original: 'UserImplementedPropType',
1508+
resolved: '"foo" | "bar"',
1509+
references: {
1510+
UserImplementedPropType: {
1511+
location: 'import',
1512+
path: './resources',
1513+
id: './resources::UserImplementedPropType',
1514+
},
1515+
},
1516+
},
1517+
}),
1518+
],
1519+
});
1520+
buildCtx.components = [compilerComponentMeta];
1521+
config.noDashCaseTypes = true;
1522+
1523+
await generateAppTypes(config, compilerCtx, buildCtx, 'src');
1524+
1525+
expect(mockWriteFile).toHaveBeenCalledWith(
1526+
'/components.d.ts',
1527+
`/* eslint-disable */
1528+
/* tslint:disable */
1529+
/**
1530+
* This is an autogenerated file created by the Stencil compiler.
1531+
* It contains typing information for all components that exist in this project.
1532+
*/
1533+
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
1534+
import { UserImplementedPropType } from "./some/stubbed/path/resources";
1535+
export { UserImplementedPropType } from "./some/stubbed/path/resources";
1536+
export namespace Components {
1537+
/**
1538+
* docs
1539+
*/
1540+
interface MyComponent {
1541+
"name": UserImplementedPropType;
1542+
}
1543+
}
1544+
declare global {
1545+
/**
1546+
* docs
1547+
*/
1548+
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
1549+
}
1550+
var HTMLMyComponentElement: {
1551+
prototype: HTMLMyComponentElement;
1552+
new (): HTMLMyComponentElement;
1553+
};
1554+
interface HTMLElementTagNameMap {
1555+
"my-component": HTMLMyComponentElement;
1556+
}
1557+
}
1558+
declare namespace LocalJSX {
1559+
/**
1560+
* docs
1561+
*/
1562+
interface MyComponent {
1563+
"name"?: UserImplementedPropType;
1564+
}
1565+
interface IntrinsicElements {
1566+
"my-component": MyComponent;
1567+
}
1568+
}
1569+
export { LocalJSX as JSX };
1570+
declare module "@stencil/core" {
1571+
export namespace JSX {
1572+
interface IntrinsicElements {
1573+
/**
1574+
* docs
1575+
*/
1576+
"my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
1577+
}
1578+
}
1579+
}
14911580
`,
14921581
{
14931582
immediateWrite: true,

src/declarations/stencil-public-compiler.ts

+6
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,11 @@ export interface StencilConfig {
309309
*/
310310
excludeUnusedDependencies?: boolean;
311311
stencilCoreResolvedId?: string;
312+
/**
313+
* Sets whether Stencil will generate dash-cased types (with deprecated comment)
314+
* in `components.d.ts`. Defaults to `false`
315+
*/
316+
noDashCaseTypes?: boolean;
312317
}
313318

314319
interface ConfigExtrasBase {
@@ -501,6 +506,7 @@ type StrictConfigFields = keyof Pick<
501506
| 'minifyCss'
502507
| 'minifyJs'
503508
| 'namespace'
509+
| 'noDashCaseTypes'
504510
| 'outputTargets'
505511
| 'packageJsonFilePath'
506512
| 'rollupConfig'

src/testing/mocks.ts

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export function mockValidatedConfig(overrides: Partial<d.ValidatedConfig> = {}):
4040
minifyCss: false,
4141
minifyJs: false,
4242
namespace: 'Testing',
43+
noDashCaseTypes: false,
4344
outputTargets: baseConfig.outputTargets ?? [],
4445
packageJsonFilePath: path.join(rootDir, 'package.json'),
4546
rootDir,

0 commit comments

Comments
 (0)