Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler): allow dash-case type generation to be disabled #6179

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/compiler/config/validate-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const validateConfig = (
hydratedFlag: validateHydrated(config),
logLevel,
logger,
noDashCaseTypes: config.noDashCaseTypes ?? false,
minifyCss: config.minifyCss ?? !devMode,
minifyJs: config.minifyJs ?? !devMode,
outputTargets: config.outputTargets ?? [],
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/types/generate-app-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const generateComponentTypesFile = (
*/
componentEventDetailTypes.push(generateEventDetailTypes(cmp));
}
return generateComponentTypes(cmp, typeImportData, areTypesInternal);
return generateComponentTypes(config, cmp, typeImportData, areTypesInternal);
});

c.push(COMPONENTS_DTS_HEADER);
Expand Down
47 changes: 31 additions & 16 deletions src/compiler/types/generate-component-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ import { generatePropTypes } from './generate-prop-types';

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

const propAttributes = generatePropTypes(cmp, typeImportData);
const methodAttributes = generateMethodTypes(cmp, typeImportData);
Expand All @@ -31,9 +34,15 @@ export const generateComponentTypes = (
[...propAttributes, ...methodAttributes],
false,
areTypesInternal,
noDashCaseTypes,
);
const isDep = cmp.isCollectionDependency;
const jsxAttributes = attributesToMultiLineString([...propAttributes, ...eventAttributes], true, areTypesInternal);
const jsxAttributes = attributesToMultiLineString(
[...propAttributes, ...eventAttributes],
true,
areTypesInternal,
noDashCaseTypes,
);

const element = [
...htmlElementEventMap,
Expand All @@ -60,7 +69,12 @@ export const generateComponentTypes = (
};
};

const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: boolean, internal: boolean) => {
const attributesToMultiLineString = (
attributes: d.TypeInfo,
jsxAttributes: boolean,
internal: boolean,
noDashCaseTypes: boolean,
) => {
const attributesStr = sortBy(attributes, (a) => a.name)
.filter((type) => {
if (jsxAttributes && !internal && type.internal) {
Expand All @@ -76,20 +90,21 @@ const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: bool
}
const optional = jsxAttributes ? !type.required : type.optional;
fullList.push(` "${type.name}"${optional ? '?' : ''}: ${type.type};`);

/**
* deprecated usage of dash-casing in JSX, use camelCase instead
*/
if (type.attributeName && type.attributeName !== type.name) {
const padding = ' '.repeat(8);
fullList.push(
[
`${padding}/**`,
`${padding} * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5.`,
`${padding} */`,
].join('\n'),
);
fullList.push(`${padding}"${type.attributeName}"?: ${type.type};`);
if (noDashCaseTypes === false) {
/**
* deprecated usage of dash-casing in JSX, use camelCase instead
*/
if (type.attributeName && type.attributeName !== type.name) {
const padding = ' '.repeat(8);
fullList.push(
[
`${padding}/**`,
`${padding} * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5.`,
`${padding} */`,
].join('\n'),
);
fullList.push(`${padding}"${type.attributeName}"?: ${type.type};`);
}
}

return fullList;
Expand Down
89 changes: 89 additions & 0 deletions src/compiler/types/tests/generate-app-types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,95 @@ declare module "@stencil/core" {
}
}
}
`,
{
immediateWrite: true,
},
);
});

it('should not generate dash-case types if noDashCaseTypes is set to true', async () => {
const compilerComponentMeta = stubComponentCompilerMeta({
tagName: 'my-component',
componentClassName: 'MyComponent',
hasProp: true,
properties: [
stubComponentCompilerProperty({
name: 'name',
complexType: {
original: 'UserImplementedPropType',
resolved: '"foo" | "bar"',
references: {
UserImplementedPropType: {
location: 'import',
path: './resources',
id: './resources::UserImplementedPropType',
},
},
},
}),
],
});
buildCtx.components = [compilerComponentMeta];
config.noDashCaseTypes = true;

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
/**
* This is an autogenerated file created by the Stencil compiler.
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "./some/stubbed/path/resources";
export { UserImplementedPropType } from "./some/stubbed/path/resources";
export namespace Components {
/**
* docs
*/
interface MyComponent {
"name": UserImplementedPropType;
}
}
declare global {
/**
* docs
*/
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
}
var HTMLMyComponentElement: {
prototype: HTMLMyComponentElement;
new (): HTMLMyComponentElement;
};
interface HTMLElementTagNameMap {
"my-component": HTMLMyComponentElement;
}
}
declare namespace LocalJSX {
/**
* docs
*/
interface MyComponent {
"name"?: UserImplementedPropType;
}
interface IntrinsicElements {
"my-component": MyComponent;
}
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
export namespace JSX {
interface IntrinsicElements {
/**
* docs
*/
"my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
}
}
}
`,
{
immediateWrite: true,
Expand Down
6 changes: 6 additions & 0 deletions src/declarations/stencil-public-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ export interface StencilConfig {
*/
excludeUnusedDependencies?: boolean;
stencilCoreResolvedId?: string;
/**
* Sets whether Stencil will generate dash-cased types (with deprecated comment)
* in `components.d.ts`. Defaults to `false`
*/
noDashCaseTypes?: boolean;
}

interface ConfigExtrasBase {
Expand Down Expand Up @@ -501,6 +506,7 @@ type StrictConfigFields = keyof Pick<
| 'minifyCss'
| 'minifyJs'
| 'namespace'
| 'noDashCaseTypes'
| 'outputTargets'
| 'packageJsonFilePath'
| 'rollupConfig'
Expand Down
1 change: 1 addition & 0 deletions src/testing/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function mockValidatedConfig(overrides: Partial<d.ValidatedConfig> = {}):
minifyCss: false,
minifyJs: false,
namespace: 'Testing',
noDashCaseTypes: false,
outputTargets: baseConfig.outputTargets ?? [],
packageJsonFilePath: path.join(rootDir, 'package.json'),
rootDir,
Expand Down
Loading