Skip to content

Commit b6c0276

Browse files
fix: v2 non-field api support (#5178)
* fix: support non-field api in ssr * chore: tests * fix: additional coverage, redundant api decorator error * chore: add throw TODO * fix: added tests
1 parent 476bb41 commit b6c0276

File tree

33 files changed

+177
-24
lines changed

33 files changed

+177
-24
lines changed

packages/@lwc/engine-server/src/__tests__/fixtures/api/decorated-getter/error.txt

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<x-parent>
2+
<template shadowrootmode="open">
3+
<x-child>
4+
<template shadowrootmode="open">
5+
const setter getter api value
6+
</template>
7+
</x-child>
8+
<x-child>
9+
<template shadowrootmode="open">
10+
setter getter api value
11+
</template>
12+
</x-child>
13+
</template>
14+
</x-parent>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const tagName = 'x-parent';
2+
export { default } from 'x/parent';
3+
export * from 'x/parent';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
{setterGetterApi}
3+
</template>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { LightningElement, api } from 'lwc';
2+
3+
export default class Child extends LightningElement {
4+
set setterGetterApi(value) {
5+
this._someApi = value;
6+
}
7+
8+
@api
9+
get setterGetterApi() {
10+
return this._someApi;
11+
}
12+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<template>
2+
<x-child setter-getter-api="const setter getter api value"></x-child>
3+
<x-child setter-getter-api={setterGetterApiValue}></x-child>
4+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { LightningElement } from 'lwc';
2+
3+
export default class Parent extends LightningElement {
4+
setterGetterApiValue = 'setter getter api value';
5+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
LWC1112: @api get setterGetterApi and @api set setterGetterApi detected in class declaration. Only one of the two needs to be decorated with @api.

packages/@lwc/engine-server/src/__tests__/fixtures/api/decorated-setter-getter/expected.html

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const tagName = 'x-parent';
2+
export { default } from 'x/parent';
3+
export * from 'x/parent';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
{setterGetterApi}
3+
</template>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { LightningElement, api } from 'lwc';
2+
3+
export default class Child extends LightningElement {
4+
@api
5+
set setterGetterApi(value) {
6+
this._someApi = value;
7+
}
8+
9+
@api
10+
get setterGetterApi() {
11+
return this._someApi;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<template>
2+
<x-child setter-getter-api="const setter getter api value"></x-child>
3+
<x-child setter-getter-api={setterGetterApiValue}></x-child>
4+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { LightningElement } from 'lwc';
2+
3+
export default class Parent extends LightningElement {
4+
setterGetterApiValue = 'setter getter api value';
5+
}

packages/@lwc/engine-server/src/__tests__/fixtures/api/decorated-setter/error.txt

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<x-parent>
2+
<template shadowrootmode="open">
3+
<x-child>
4+
<template shadowrootmode="open">
5+
const setter getter api value
6+
</template>
7+
</x-child>
8+
<x-child>
9+
<template shadowrootmode="open">
10+
setter getter api value
11+
</template>
12+
</x-child>
13+
</template>
14+
</x-parent>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const tagName = 'x-parent';
2+
export { default } from 'x/parent';
3+
export * from 'x/parent';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
{setterGetterApi}
3+
</template>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { LightningElement, api } from 'lwc';
2+
3+
export default class Child extends LightningElement {
4+
@api
5+
set setterGetterApi(value) {
6+
this._someApi = value;
7+
}
8+
9+
get setterGetterApi() {
10+
return this._someApi;
11+
}
12+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<template>
2+
<x-child setter-getter-api="const setter getter api value"></x-child>
3+
<x-child setter-getter-api={setterGetterApiValue}></x-child>
4+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { LightningElement } from 'lwc';
2+
3+
export default class Parent extends LightningElement {
4+
setterGetterApiValue = 'setter getter api value';
5+
}

packages/@lwc/engine-server/src/__tests__/fixtures/api/property/error.txt

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<x-parent>
2+
<template shadowrootmode="open">
3+
<x-child>
4+
<template shadowrootmode="open">
5+
const field api value
6+
</template>
7+
</x-child>
8+
<x-child>
9+
<template shadowrootmode="open">
10+
field api value
11+
</template>
12+
</x-child>
13+
</template>
14+
</x-parent>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const tagName = 'x-parent';
2+
export { default } from 'x/parent';
3+
export * from 'x/parent';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
{fieldApi}
3+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { LightningElement, api } from 'lwc';
2+
3+
export default class Child extends LightningElement {
4+
@api fieldApi;
5+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<template>
2+
<x-child field-api="const field api value"></x-child>
3+
<x-child field-api={fieldApiValue}></x-child>
4+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { LightningElement } from 'lwc';
2+
3+
export default class Parent extends LightningElement {
4+
fieldApiValue = 'field api value';
5+
}

packages/@lwc/engine-server/src/__tests__/fixtures/reserved-keywords/modules/x/cmp/cmp.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { LightningElement } from 'lwc';
22

3-
const privateFields = undefined;
4-
const publicFields = undefined;
3+
const privateProperties = undefined;
4+
const publicProperties = undefined;
55
const stylesheetScopeToken = undefined;
66
const hasScopedStylesheets = undefined;
77
const defaultScopedStylesheets = undefined;
@@ -12,8 +12,8 @@ export default class extends LightningElement {
1212
Object.assign(
1313
{},
1414
{
15-
privateFields,
16-
publicFields,
15+
privateProperties,
16+
publicProperties,
1717
stylesheetScopeToken,
1818
hasScopedStylesheets,
1919
defaultScopedStylesheets,

packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import type { ComponentMetaState } from './types';
1616

1717
const bGenerateMarkup = esTemplate`
1818
// These variables may mix with component-authored variables, so should be reasonably unique
19-
const __lwcPublicFields__ = new Set(${/*public fields*/ is.arrayExpression});
20-
const __lwcPrivateFields__ = new Set(${/*private fields*/ is.arrayExpression});
19+
const __lwcPublicProperties__ = new Set(${/*api*/ is.arrayExpression});
20+
const __lwcPrivateProperties__ = new Set(${/*private fields*/ is.arrayExpression});
2121
2222
async function* generateMarkup(
2323
tagName,
@@ -43,8 +43,8 @@ const bGenerateMarkup = esTemplate`
4343
instance[__SYMBOL__SET_INTERNALS](
4444
props,
4545
attrs,
46-
__lwcPublicFields__,
47-
__lwcPrivateFields__,
46+
__lwcPublicProperties__,
47+
__lwcPrivateProperties__,
4848
);
4949
instance.isConnected = true;
5050
if (instance.connectedCallback) {
@@ -101,7 +101,7 @@ export function addGenerateMarkupFunction(
101101
tagName: string,
102102
filename: string
103103
) {
104-
const { privateFields, publicFields, tmplExplicitImports } = state;
104+
const { privateProperties, publicProperties, tmplExplicitImports } = state;
105105

106106
// The default tag name represents the component name that's passed to the transformer.
107107
// This is needed to generate markup for dynamic components which are invoked through
@@ -141,8 +141,8 @@ export function addGenerateMarkupFunction(
141141
);
142142
program.body.push(
143143
...bGenerateMarkup(
144-
b.arrayExpression(publicFields.map(b.literal)),
145-
b.arrayExpression(privateFields.map(b.literal)),
144+
b.arrayExpression(publicProperties.map(b.literal)),
145+
b.arrayExpression(privateProperties.map(b.literal)),
146146
defaultTagName,
147147
classIdentifier,
148148
connectWireAdapterCode

packages/@lwc/ssr-compiler/src/compile-js/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ const visitors: Visitors = {
105105
validateUniqueDecorator(decorators);
106106
const decoratedExpression = decorators?.[0]?.expression;
107107
if (is.identifier(decoratedExpression) && decoratedExpression.name === 'api') {
108-
state.publicFields.push(node.key.name);
108+
state.publicProperties.push(node.key.name);
109109
} else if (
110110
is.callExpression(decoratedExpression) &&
111111
is.identifier(decoratedExpression.callee) &&
@@ -116,9 +116,9 @@ const visitors: Visitors = {
116116
throw new Error('@wire cannot be used on computed properties in SSR context.');
117117
}
118118
catalogWireAdapters(path, state);
119-
state.privateFields.push(node.key.name);
119+
state.privateProperties.push(node.key.name);
120120
} else {
121-
state.privateFields.push(node.key.name);
121+
state.privateProperties.push(node.key.name);
122122
}
123123

124124
if (
@@ -180,6 +180,14 @@ const visitors: Visitors = {
180180
} else {
181181
catalogWireAdapters(path, state);
182182
}
183+
} else if (is.identifier(decoratedExpression) && decoratedExpression.name === 'api') {
184+
if (state.publicProperties.includes(node.key.name)) {
185+
// TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler`
186+
throw new Error(
187+
`LWC1112: @api get ${node.key.name} and @api set ${node.key.name} detected in class declaration. Only one of the two needs to be decorated with @api.`
188+
);
189+
}
190+
state.publicProperties.push(node.key.name);
183191
}
184192

185193
switch (node.key.name) {
@@ -285,8 +293,8 @@ export default function compileJS(
285293
tmplExplicitImports: null,
286294
cssExplicitImports: null,
287295
staticStylesheetIds: null,
288-
publicFields: [],
289-
privateFields: [],
296+
publicProperties: [],
297+
privateProperties: [],
290298
wireAdapters: [],
291299
experimentalDynamicComponent: options.experimentalDynamicComponent,
292300
importManager: new ImportManager(),

packages/@lwc/ssr-compiler/src/compile-js/types.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ export interface ComponentMetaState {
4848
cssExplicitImports: Map<string, string> | null;
4949
// the set of variable names associated with explicitly imported CSS files
5050
staticStylesheetIds: Set<string> | null;
51-
// the public (`@api`-annotated) fields of the component class
52-
publicFields: Array<string>;
53-
// the private fields of the component class
54-
privateFields: Array<string>;
51+
// the public (`@api`-annotated) properties of the component class
52+
publicProperties: Array<string>;
53+
// the private properties of the component class
54+
privateProperties: Array<string>;
5555
// indicates whether the LightningElement has any wired props
5656
wireAdapters: WireAdapter[];
5757
// dynamic imports configuration

packages/@lwc/ssr-runtime/src/lightning-element.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ export class LightningElement implements PropsAvailableAtConstruction {
7878
[SYMBOL__SET_INTERNALS](
7979
props: Properties,
8080
attrs: Attributes,
81-
publicFields: Set<string>,
82-
privateFields: Set<string>
81+
publicProperties: Set<string>,
82+
privateProperties: Set<string>
8383
) {
8484
this.#props = props;
8585
this.#attrs = attrs;
@@ -91,9 +91,9 @@ export class LightningElement implements PropsAvailableAtConstruction {
9191
for (const propName of keys(props)) {
9292
const attrName = htmlPropertyToAttribute(propName);
9393
if (
94-
publicFields.has(propName) ||
94+
publicProperties.has(propName) ||
9595
((REFLECTIVE_GLOBAL_PROPERTY_SET.has(propName) || isAriaAttribute(attrName)) &&
96-
!privateFields.has(propName))
96+
!privateProperties.has(propName))
9797
) {
9898
// For props passed from parents to children, they are intended to be read-only
9999
// to avoid a child mutating its parent's state

0 commit comments

Comments
 (0)