-
Notifications
You must be signed in to change notification settings - Fork 392
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add dynamic components for @lwc/ssr-compiler (#4847)
- Loading branch information
Showing
17 changed files
with
179 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-component-invalid-ctor/error.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Invalid constructor: "frankenstein" is not a LightningElement constructor. |
Empty file.
3 changes: 3 additions & 0 deletions
3
packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-component-invalid-ctor/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const tagName = 'x-dynamic-component-invalid-ctor'; | ||
export { default } from 'x/dynamic-invalid-ctor'; | ||
export * from 'x/dynamic-invalid-ctor'; |
3 changes: 3 additions & 0 deletions
3
...s/dynamic-component-invalid-ctor/modules/x/dynamic-invalid-ctor/dynamic-invalid-ctor.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<template> | ||
<lwc:component lwc:is={customCtor}></lwc:component> | ||
</template> |
5 changes: 5 additions & 0 deletions
5
...res/dynamic-component-invalid-ctor/modules/x/dynamic-invalid-ctor/dynamic-invalid-ctor.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { LightningElement } from 'lwc'; | ||
|
||
export default class extends LightningElement { | ||
customCtor = 'frankenstein'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: MIT | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT | ||
*/ | ||
|
||
/** | ||
* Generates a custom element tag name given a namespace and component name. | ||
* Based on the LWC file system requirements, component names come from the file system name which is | ||
* camel cased. The component's name will be converted to kebab case when the tag name is produced. | ||
* | ||
* @param namespace component namespace | ||
* @param name component name | ||
* @returns component tag name | ||
*/ | ||
export function generateCustomElementTagName(namespace: string = '', name: string = '') { | ||
const kebabCasedName = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | ||
return `${namespace}-${kebabCasedName}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
packages/@lwc/ssr-compiler/src/compile-template/transformers/lwc-component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: MIT | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT | ||
*/ | ||
import { is } from 'estree-toolkit'; | ||
import { isUndefined } from '@lwc/shared'; | ||
import { Transformer } from '../types'; | ||
import { expressionIrToEs } from '../expression'; | ||
import { esTemplate, esTemplateWithYield } from '../../estemplate'; | ||
import { bImportDeclaration } from '../../estree/builders'; | ||
import { getChildAttrsOrProps } from '../shared'; | ||
import type { | ||
LwcComponent as IrLwcComponent, | ||
Expression as IrExpression, | ||
} from '@lwc/template-compiler'; | ||
import type { | ||
IfStatement as EsIfStatement, | ||
VariableDeclaration as EsVariableDeclaration, | ||
} from 'estree'; | ||
|
||
const bDynamicComponentConstructorDeclaration = esTemplate` | ||
const Ctor = '${/*lwcIs attribute value*/ is.expression}'; | ||
`<EsVariableDeclaration>; | ||
|
||
const bYieldFromDynamicComponentConstructorGenerator = esTemplateWithYield` | ||
if (Ctor) { | ||
if (typeof Ctor !== 'function' || !(Ctor.prototype instanceof LightningElement)) { | ||
throw new Error(\`Invalid constructor: "\${String(Ctor)}" is not a LightningElement constructor.\`) | ||
} | ||
const childProps = __getReadOnlyProxy(${/* child props */ is.objectExpression}); | ||
const childAttrs = ${/* child attrs */ is.objectExpression}; | ||
yield* Ctor[SYMBOL__GENERATE_MARKUP](null, childProps, childAttrs); | ||
} | ||
`<EsIfStatement>; | ||
|
||
export const LwcComponent: Transformer<IrLwcComponent> = function LwcComponent(node, cxt) { | ||
const { directives } = node; | ||
|
||
const lwcIs = directives.find((directive) => directive.name === 'Is'); | ||
if (!isUndefined(lwcIs)) { | ||
cxt.hoist(bImportDeclaration(['LightningElement']), 'import:LightningElement'); | ||
cxt.hoist( | ||
bImportDeclaration(['SYMBOL__GENERATE_MARKUP']), | ||
'import:SYMBOL__GENERATE_MARKUP' | ||
); | ||
cxt.hoist( | ||
bImportDeclaration([{ getReadOnlyProxy: '__getReadOnlyProxy' }]), | ||
'import:getReadOnlyProxy' | ||
); | ||
|
||
return [ | ||
bDynamicComponentConstructorDeclaration( | ||
// The template compiler has validation to prevent lwcIs.value from being a literal | ||
expressionIrToEs(lwcIs.value as IrExpression, cxt) | ||
), | ||
bYieldFromDynamicComponentConstructorGenerator( | ||
getChildAttrsOrProps(node.properties, cxt), | ||
getChildAttrsOrProps(node.attributes, cxt) | ||
), | ||
]; | ||
} else { | ||
return []; | ||
} | ||
}; |
Oops, something went wrong.