Skip to content

Commit

Permalink
feat: introduce componentNameFormatter for react output target
Browse files Browse the repository at this point in the history
  • Loading branch information
markhughes committed Mar 10, 2022
1 parent f34e2d9 commit 25d663b
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 7 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,35 @@ If `true`, the output target will import the custom element instance and registe

This is the directory where the custom elements are imported from when using the `[Custom Elements Bundle](https://stenciljs.com/docs/custom-elements)`. Defaults to the `components` directory. Only applies when `includeImportCustomElements` is `true`.

### componentNameFormatter

This is an optional function that will be used to format the component name. It takes two arguments, the first argument is the suggested generated name, and the second option is the component meta. By default, the component name is converted by dash-case to PascalCase.

Example to remove first 3 characters:

```ts
import { Config } from '@stencil/core';
import { reactOutputTarget } from '@stencil/react-output-target';

export const config: Config = {
namespace: 'demo',
outputTargets: [
reactOutputTarget({
componentCorePackage: 'component-library',
proxiesFile: '../component-library-react/src/components.ts',
componentNameFormatter: (suggestedName: string) => {
return `${suggestedName.substring(3)}`;
},
}),
{
type: 'dist',
},
],
};
```

```
### Setup of React Component Library
There is an example component library package available on Github so that you can get started. This repo will likely live as a sibling to your Stencil component library. https://github.com/ionic-team/stencil-ds-react-template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ describe('createComponentDefinition', () => {
tagName: 'my-component',
methods: [],
events: [],
}, true);
}, {
includeImportCustomElements: true
});
expect(output[0]).toEqual(`export const MyComponent = /*@__PURE__*/createReactComponent<JSX.MyComponent, HTMLMyComponentElement>('my-component', undefined, undefined, defineMyComponent);`);
});

Expand All @@ -19,9 +21,23 @@ describe('createComponentDefinition', () => {
tagName: 'my-component',
methods: [],
events: [],
});
}, {});
expect(output[0]).toEqual(`export const MyComponent = /*@__PURE__*/createReactComponent<JSX.MyComponent, HTMLMyComponentElement>('my-component');`);
});

it('should create a React component with a prefix', () => {
const output = createComponentDefinition({
properties: [],
tagName: 'my-component',
methods: [],
events: [],
}, {
componentNameFormatter: (suggestedName: string, _) => {
return `Prefixed${suggestedName}`;
},
});
expect(output[0]).toEqual(`export const PrefixedMyComponent = /*@__PURE__*/createReactComponent<JSX.MyComponent, HTMLMyComponentElement>('my-component');`);
});
});

describe('generateProxies', () => {
Expand Down
25 changes: 20 additions & 5 deletions packages/react-output-target/src/output-react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ function getFilteredComponents(excludeComponents: ReadonlyArray<string> = [], cm
);
}

/**
* Formats a component's name to be used in the generated Stencil-React bindings
* @param component the component to format the name for
* @param outputTarget the output target configuration for generating the Stencil-React bindings
* @returns the formatted component name
*/
function componentNameFormatter(component: ComponentCompilerMeta, outputTarget: OutputTargetReact) {
const tagNameAsPascal = dashToPascalCase(component.tagName);
if (outputTarget.componentNameFormatter && typeof outputTarget.componentNameFormatter === 'function') {
return outputTarget.componentNameFormatter(''+tagNameAsPascal, {...component});
}
return tagNameAsPascal;
}

/**
* Generate the code that will be responsible for creating the Stencil-React bindings
* @param config the Stencil configuration associated with the project
Expand Down Expand Up @@ -118,7 +132,7 @@ import { createReactComponent } from './react-component-lib';\n`;
typeImports,
sourceImports,
registerCustomElements,
components.map(cmpMeta => createComponentDefinition(cmpMeta, outputTarget.includeImportCustomElements)).join('\n'),
components.map(cmpMeta => createComponentDefinition(cmpMeta, outputTarget)).join('\n'),
];

return final.join('\n') + '\n';
Expand All @@ -127,15 +141,16 @@ import { createReactComponent } from './react-component-lib';\n`;
/**
* Defines the React component that developers will import to use in their applications.
* @param cmpMeta Meta data for a single Web Component
* @param includeCustomElement If `true`, the Web Component instance will be passed in to createReactComponent to be
* @param outputTarget the output target configuration used to generate the Stencil-React bindings
* registered with the Custom Elements Registry.
* @returns An array where each entry is a string version of the React component definition.
*/
export function createComponentDefinition(cmpMeta: ComponentCompilerMeta, includeCustomElement: boolean = false): ReadonlyArray<string> {
export function createComponentDefinition(cmpMeta: ComponentCompilerMeta, outputTarget: OutputTargetReact): ReadonlyArray<string> {
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
let template = `export const ${tagNameAsPascal} = /*@__PURE__*/createReactComponent<${IMPORT_TYPES}.${tagNameAsPascal}, HTML${tagNameAsPascal}Element>('${cmpMeta.tagName}'`;
const componentName = componentNameFormatter(cmpMeta, outputTarget);
let template = `export const ${componentName} = /*@__PURE__*/createReactComponent<${IMPORT_TYPES}.${tagNameAsPascal}, HTML${tagNameAsPascal}Element>('${cmpMeta.tagName}'`;

if (includeCustomElement) {
if (outputTarget.includeImportCustomElements) {
template += `, undefined, undefined, define${tagNameAsPascal}`;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/react-output-target/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ComponentCompilerMeta } from "@stencil/core/internal";

/**
* An output target configuration interface used to configure Stencil to properly generate the bindings necessary to use
* Stencil components in a React application
Expand All @@ -11,6 +13,7 @@ export interface OutputTargetReact {
includeDefineCustomElements?: boolean;
includeImportCustomElements?: boolean;
customElementsDir?: string;
componentNameFormatter?: (suggestedName: string, component: ComponentCompilerMeta) => string
}

/**
Expand Down

0 comments on commit 25d663b

Please sign in to comment.