From 3a2ff79e6a5ea1055ad4d6e7a56a234557dcc559 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 18 Dec 2024 23:59:09 -0300 Subject: [PATCH 01/15] fix(ssr-compiler): harmonize wire errors --- .../src/__tests__/utils/expected-failures.ts | 5 ----- packages/@lwc/ssr-compiler/src/compile-js/wire.ts | 15 +++++++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts index 96c55c048b..6d71895d60 100644 --- a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts +++ b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts @@ -34,11 +34,6 @@ export const expectedFailures = new Set([ 'superclass/render-in-superclass/unused-default-in-superclass/index.js', 'wire/errors/throws-on-computed-key/index.js', 'wire/errors/throws-when-colliding-prop-then-method/index.js', - 'wire/errors/throws-when-computed-prop-is-expression/index.js', - 'wire/errors/throws-when-computed-prop-is-let-variable/index.js', - 'wire/errors/throws-when-computed-prop-is-regexp-literal/index.js', - 'wire/errors/throws-when-computed-prop-is-template-literal/index.js', - 'wire/errors/throws-when-using-2-wired-decorators/index.js', 'wire/errors/throws-when-wired-method-is-combined-with-@api/index.js', 'wire/errors/throws-when-wired-property-is-combined-with-@api/index.js', 'wire/errors/throws-when-wired-property-is-combined-with-@track/index.js', diff --git a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts index 2584c6c53d..44e2415d20 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts @@ -7,9 +7,9 @@ import { is, builders as b } from 'estree-toolkit'; import { produce } from 'immer'; +import { DecoratorErrors, generateErrorMessage } from '@lwc/errors'; import { esTemplate } from '../estemplate'; import type { NodePath } from 'estree-toolkit'; - import type { PropertyDefinition, ObjectExpression, @@ -36,6 +36,10 @@ function bMemberExpressionChain(props: string[]): MemberExpression { return expr; } +function generateError(error: (typeof DecoratorErrors)[keyof typeof DecoratorErrors]) { + return new Error(generateErrorMessage(error)); +} + function getWireParams( node: MethodDefinition | PropertyDefinition ): [Expression, Expression | undefined] { @@ -43,7 +47,7 @@ function getWireParams( if (decorators.length > 1) { // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - multiple decorators at once'); + throw generateError(DecoratorErrors.ONE_WIRE_DECORATOR_ALLOWED); } // validate the parameters @@ -94,7 +98,7 @@ function validateWireId( // This is not the exact same validation done in @lwc/babel-plugin-component but it accomplishes the same thing if (path.scope?.getBinding(wireAdapterVar)?.kind !== 'module') { // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - WIRE_ADAPTER_SHOULD_BE_IMPORTED'); + throw generateError(DecoratorErrors.COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL); } } @@ -128,9 +132,12 @@ function validateWireConfig( // A literal can be a regexp, template literal, or primitive; only allow primitives continue; } + } else if (is.templateLiteral(key)) { + // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` + throw generateError(DecoratorErrors.COMPUTED_PROPERTY_CANNOT_BE_TEMPLATE_LITERAL); } // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL'); + throw generateError(DecoratorErrors.COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL); } } From 72cacbe90daebd649c16a2221474228ac3814810 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 00:36:06 -0300 Subject: [PATCH 02/15] fix(ssr-compiler): harmonize decorator conflict errors --- .../src/__tests__/utils/expected-failures.ts | 3 -- .../@lwc/ssr-compiler/src/compile-js/index.ts | 45 +++++++++++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts index 6d71895d60..2d8e7185c6 100644 --- a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts +++ b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts @@ -34,7 +34,4 @@ export const expectedFailures = new Set([ 'superclass/render-in-superclass/unused-default-in-superclass/index.js', 'wire/errors/throws-on-computed-key/index.js', 'wire/errors/throws-when-colliding-prop-then-method/index.js', - 'wire/errors/throws-when-wired-method-is-combined-with-@api/index.js', - 'wire/errors/throws-when-wired-property-is-combined-with-@api/index.js', - 'wire/errors/throws-when-wired-property-is-combined-with-@track/index.js', ]); diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index 047ac3146c..416a9e03ba 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -9,6 +9,7 @@ import { generate } from 'astring'; import { traverse, builders as b, is } from 'estree-toolkit'; import { parseModule } from 'meriyah'; +import { DecoratorErrors, generateErrorMessage } from '@lwc/errors'; import { transmogrify } from '../transmogrify'; import { ImportManager } from '../imports'; import { replaceLwcImport, replaceNamedLwcExport, replaceAllLwcExport } from './lwc-import'; @@ -19,7 +20,7 @@ import { catalogWireAdapters } from './wire'; import { removeDecoratorImport } from './remove-decorator-import'; import type { ComponentTransformOptions } from '../shared'; -import type { Identifier as EsIdentifier, Program as EsProgram } from 'estree'; +import type { Identifier as EsIdentifier, Program as EsProgram, Node as EsNode } from 'estree'; import type { Visitors, ComponentMetaState } from './types'; import type { CompilationMode } from '@lwc/shared'; @@ -92,7 +93,9 @@ const visitors: Visitors = { } const { decorators } = node; - const decoratedExpression = decorators?.[0]?.expression; + const decoratedExpressions = decorators?.map((d) => d.expression) ?? []; + validateDecorators(decoratedExpressions); + const decoratedExpression = decoratedExpressions[0]; if (is.identifier(decoratedExpression) && decoratedExpression.name === 'api') { state.publicFields.push(node.key.name); } else if ( @@ -132,7 +135,12 @@ const visitors: Visitors = { const { decorators } = node; // The real type is a subset of `Expression`, which doesn't work with the `is` validators - const decoratedExpression = decorators?.[0]?.expression; + const decoratedExpressions = decorators?.map((d) => d.expression) ?? []; + + validateDecorators(decoratedExpressions); + + const decoratedExpression = decoratedExpressions[0]; + if ( is.callExpression(decoratedExpression) && is.identifier(decoratedExpression.callee) && @@ -205,6 +213,37 @@ const visitors: Visitors = { }, }; +const isIdentifier = (name: K) => + function isIdentifier(expr: EsNode | undefined | null): expr is EsIdentifier & { name: K } { + return is.identifier(expr) && expr.name === name; + }; + +function validateDecorators(decoratedExpressions: EsNode[]) { + if (decoratedExpressions.length > 1) { + const isWireAdapter = decoratedExpressions.some((expr) => + is.callExpression(expr, { callee: isIdentifier('wire') }) + ); + + const isApiDecorator = decoratedExpressions.some(isIdentifier('api')); + + if (isWireAdapter && isApiDecorator) { + // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` + throw new Error( + generateErrorMessage(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, ['api']) + ); + } + + const isTrackDecorator = decoratedExpressions.some(isIdentifier('track')); + + if (isWireAdapter && isTrackDecorator) { + // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` + throw new Error( + generateErrorMessage(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, ['track']) + ); + } + } +} + export default function compileJS( src: string, filename: string, From 5c28b5feb22fff7d3e56703e86bd4497a7e76d31 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 03:15:48 -0300 Subject: [PATCH 03/15] chore: reduce unneeded changes --- .../src/compiler/error-info/lwc-class.ts | 2 +- .../ssr-compiler/src/compile-js/errors.ts | 19 +++++ .../@lwc/ssr-compiler/src/compile-js/index.ts | 70 ++++++++----------- .../@lwc/ssr-compiler/src/compile-js/wire.ts | 10 ++- .../ssr-compiler/src/estree/validators.ts | 7 ++ 5 files changed, 61 insertions(+), 47 deletions(-) create mode 100644 packages/@lwc/ssr-compiler/src/compile-js/errors.ts diff --git a/packages/@lwc/errors/src/compiler/error-info/lwc-class.ts b/packages/@lwc/errors/src/compiler/error-info/lwc-class.ts index 90522240d9..e171ee9265 100644 --- a/packages/@lwc/errors/src/compiler/error-info/lwc-class.ts +++ b/packages/@lwc/errors/src/compiler/error-info/lwc-class.ts @@ -218,4 +218,4 @@ export const DecoratorErrors = { level: DiagnosticLevel.Error, url: '', }, -}; +} as const; diff --git a/packages/@lwc/ssr-compiler/src/compile-js/errors.ts b/packages/@lwc/ssr-compiler/src/compile-js/errors.ts new file mode 100644 index 0000000000..04abcd38e1 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-js/errors.ts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024, Salesforce, 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 { generateErrorMessage, type LWCErrorInfo } from '@lwc/errors'; + +// This type extracts the arguments in a string. Example: "Error {0} {1}" -> [string, string] +type ExtractArguments = T extends `${string}{${number}}${infer R}` + ? [string, ...ExtractArguments] + : []; + +export function generateError( + error: T, + ...args: ExtractArguments +): Error { + return new Error(generateErrorMessage(error, args)); +} diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index 416a9e03ba..1878de0261 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -9,9 +9,10 @@ import { generate } from 'astring'; import { traverse, builders as b, is } from 'estree-toolkit'; import { parseModule } from 'meriyah'; -import { DecoratorErrors, generateErrorMessage } from '@lwc/errors'; +import { DecoratorErrors } from '@lwc/errors'; import { transmogrify } from '../transmogrify'; import { ImportManager } from '../imports'; +import { every } from '../estree/validators'; import { replaceLwcImport, replaceNamedLwcExport, replaceAllLwcExport } from './lwc-import'; import { catalogTmplImport } from './catalog-tmpls'; import { catalogStaticStylesheets, catalogAndReplaceStyleImports } from './stylesheets'; @@ -19,8 +20,9 @@ import { addGenerateMarkupFunction } from './generate-markup'; import { catalogWireAdapters } from './wire'; import { removeDecoratorImport } from './remove-decorator-import'; +import { generateError } from './errors'; import type { ComponentTransformOptions } from '../shared'; -import type { Identifier as EsIdentifier, Program as EsProgram, Node as EsNode } from 'estree'; +import type { Program as EsProgram, Decorator as EsDecorator } from 'estree'; import type { Visitors, ComponentMetaState } from './types'; import type { CompilationMode } from '@lwc/shared'; @@ -93,15 +95,14 @@ const visitors: Visitors = { } const { decorators } = node; - const decoratedExpressions = decorators?.map((d) => d.expression) ?? []; - validateDecorators(decoratedExpressions); + validateDecorators(decorators); + const decoratedExpressions = decorators.map((d) => d.expression) ?? []; const decoratedExpression = decoratedExpressions[0]; - if (is.identifier(decoratedExpression) && decoratedExpression.name === 'api') { + if (is.identifier(decoratedExpression, { name: 'api' })) { state.publicFields.push(node.key.name); } else if ( is.callExpression(decoratedExpression) && - is.identifier(decoratedExpression.callee) && - decoratedExpression.callee.name === 'wire' + is.identifier(decoratedExpression.callee, { name: 'wire' }) ) { catalogWireAdapters(path, state); state.privateFields.push(node.key.name); @@ -113,10 +114,10 @@ const visitors: Visitors = { node.static && node.key.name === 'stylesheets' && is.arrayExpression(node.value) && - node.value.elements.every((el) => is.identifier(el)) + every(node.value.elements, is.identifier) ) { catalogStaticStylesheets( - node.value.elements.map((el) => (el as EsIdentifier).name), + node.value.elements.map((el) => el.name), state ); } @@ -134,17 +135,13 @@ const visitors: Visitors = { } const { decorators } = node; + validateDecorators(decorators); // The real type is a subset of `Expression`, which doesn't work with the `is` validators - const decoratedExpressions = decorators?.map((d) => d.expression) ?? []; - - validateDecorators(decoratedExpressions); - - const decoratedExpression = decoratedExpressions[0]; + const decoratedExpression = decorators.map((d) => d.expression)[0]; if ( is.callExpression(decoratedExpression) && - is.identifier(decoratedExpression.callee) && - decoratedExpression.callee.name === 'wire' + is.identifier(decoratedExpression.callee, { name: 'wire' }) ) { // Getters and setters are methods in the AST, but treated as properties by @wire // Note that this means that their implementations are ignored! @@ -213,34 +210,27 @@ const visitors: Visitors = { }, }; -const isIdentifier = (name: K) => - function isIdentifier(expr: EsNode | undefined | null): expr is EsIdentifier & { name: K } { - return is.identifier(expr) && expr.name === name; - }; - -function validateDecorators(decoratedExpressions: EsNode[]) { - if (decoratedExpressions.length > 1) { - const isWireAdapter = decoratedExpressions.some((expr) => - is.callExpression(expr, { callee: isIdentifier('wire') }) - ); +function validateDecorators(decorators: EsDecorator[]) { + if (decorators.length < 2) { + return; + } + const hasWire = decorators.some( + ({ expression }) => + is.callExpression(expression) && is.identifier(expression.callee, { name: 'wire' }) + ); - const isApiDecorator = decoratedExpressions.some(isIdentifier('api')); + const hasApi = decorators.some(({ expression }) => is.identifier(expression, { name: 'api' })); - if (isWireAdapter && isApiDecorator) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error( - generateErrorMessage(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, ['api']) - ); - } + if (hasWire && hasApi) { + throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'api'); + } - const isTrackDecorator = decoratedExpressions.some(isIdentifier('track')); + const hasTrack = decorators.some(({ expression }) => + is.identifier(expression, { name: 'track' }) + ); - if (isWireAdapter && isTrackDecorator) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error( - generateErrorMessage(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, ['track']) - ); - } + if (hasWire && hasTrack) { + throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'track'); } } diff --git a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts index 44e2415d20..0acecaae9d 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts @@ -7,8 +7,9 @@ import { is, builders as b } from 'estree-toolkit'; import { produce } from 'immer'; -import { DecoratorErrors, generateErrorMessage } from '@lwc/errors'; +import { DecoratorErrors } from '@lwc/errors'; import { esTemplate } from '../estemplate'; +import { generateError } from './errors'; import type { NodePath } from 'estree-toolkit'; import type { PropertyDefinition, @@ -36,10 +37,6 @@ function bMemberExpressionChain(props: string[]): MemberExpression { return expr; } -function generateError(error: (typeof DecoratorErrors)[keyof typeof DecoratorErrors]) { - return new Error(generateErrorMessage(error)); -} - function getWireParams( node: MethodDefinition | PropertyDefinition ): [Expression, Expression | undefined] { @@ -158,7 +155,8 @@ export function catalogWireAdapters( if ( is.literal(value) && typeof value.value === 'string' && - value.value.startsWith('$') + value.value.startsWith('$') && + value.value.length > 1 ) { prop.value = bMemberExpressionChain(value.value.slice(1).split('.')); } diff --git a/packages/@lwc/ssr-compiler/src/estree/validators.ts b/packages/@lwc/ssr-compiler/src/estree/validators.ts index f66d6e0f33..f6b55f0fab 100644 --- a/packages/@lwc/ssr-compiler/src/estree/validators.ts +++ b/packages/@lwc/ssr-compiler/src/estree/validators.ts @@ -55,3 +55,10 @@ if (process.env.NODE_ENV !== 'production') { (val as any).__debugName = key; } } + +export function every( + arr: (Node | null | undefined)[], + predicate: Checker +): arr is T[] { + return arr.every((el) => predicate(el)); +} From b2ab32626683127f3a6ce06fe8646453bd783b62 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 03:23:14 -0300 Subject: [PATCH 04/15] chore: simplify validateDecorators --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index 1878de0261..b3a9942388 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -214,12 +214,14 @@ function validateDecorators(decorators: EsDecorator[]) { if (decorators.length < 2) { return; } - const hasWire = decorators.some( - ({ expression }) => - is.callExpression(expression) && is.identifier(expression.callee, { name: 'wire' }) + + const expressions = decorators.map(({ expression }) => expression); + + const hasWire = expressions.some( + (expr) => is.callExpression(expr) && is.identifier(expr.callee, { name: 'wire' }) ); - const hasApi = decorators.some(({ expression }) => is.identifier(expression, { name: 'api' })); + const hasApi = expressions.some((expr) => is.identifier(expr, { name: 'api' })); if (hasWire && hasApi) { throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'api'); From 65f79453c61b0e1923048a04a6d4ac33ba26ec6a Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 03:26:57 -0300 Subject: [PATCH 05/15] chore: remove todos --- packages/@lwc/ssr-compiler/src/compile-js/wire.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts index 0acecaae9d..2cbb32af02 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts @@ -43,7 +43,6 @@ function getWireParams( const { decorators } = node; if (decorators.length > 1) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` throw generateError(DecoratorErrors.ONE_WIRE_DECORATOR_ALLOWED); } @@ -94,7 +93,6 @@ function validateWireId( // This is not the exact same validation done in @lwc/babel-plugin-component but it accomplishes the same thing if (path.scope?.getBinding(wireAdapterVar)?.kind !== 'module') { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` throw generateError(DecoratorErrors.COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL); } } @@ -130,10 +128,8 @@ function validateWireConfig( continue; } } else if (is.templateLiteral(key)) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` throw generateError(DecoratorErrors.COMPUTED_PROPERTY_CANNOT_BE_TEMPLATE_LITERAL); } - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` throw generateError(DecoratorErrors.COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL); } } From a7864203d72fd0215ab6bcf871bb8aa655150dc3 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 03:45:23 -0300 Subject: [PATCH 06/15] chore: remove unneeded changes --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 11 +++++++---- packages/@lwc/ssr-compiler/src/estree/validators.ts | 7 ------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index b3a9942388..16a1b6aee3 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -12,7 +12,6 @@ import { parseModule } from 'meriyah'; import { DecoratorErrors } from '@lwc/errors'; import { transmogrify } from '../transmogrify'; import { ImportManager } from '../imports'; -import { every } from '../estree/validators'; import { replaceLwcImport, replaceNamedLwcExport, replaceAllLwcExport } from './lwc-import'; import { catalogTmplImport } from './catalog-tmpls'; import { catalogStaticStylesheets, catalogAndReplaceStyleImports } from './stylesheets'; @@ -22,7 +21,11 @@ import { catalogWireAdapters } from './wire'; import { removeDecoratorImport } from './remove-decorator-import'; import { generateError } from './errors'; import type { ComponentTransformOptions } from '../shared'; -import type { Program as EsProgram, Decorator as EsDecorator } from 'estree'; +import type { + Identifier as EsIdentifier, + Program as EsProgram, + Decorator as EsDecorator, +} from 'estree'; import type { Visitors, ComponentMetaState } from './types'; import type { CompilationMode } from '@lwc/shared'; @@ -114,10 +117,10 @@ const visitors: Visitors = { node.static && node.key.name === 'stylesheets' && is.arrayExpression(node.value) && - every(node.value.elements, is.identifier) + node.value.elements.every((el) => is.identifier(el)) ) { catalogStaticStylesheets( - node.value.elements.map((el) => el.name), + node.value.elements.map((el) => (el as EsIdentifier).name), state ); } diff --git a/packages/@lwc/ssr-compiler/src/estree/validators.ts b/packages/@lwc/ssr-compiler/src/estree/validators.ts index f6b55f0fab..f66d6e0f33 100644 --- a/packages/@lwc/ssr-compiler/src/estree/validators.ts +++ b/packages/@lwc/ssr-compiler/src/estree/validators.ts @@ -55,10 +55,3 @@ if (process.env.NODE_ENV !== 'production') { (val as any).__debugName = key; } } - -export function every( - arr: (Node | null | undefined)[], - predicate: Checker -): arr is T[] { - return arr.every((el) => predicate(el)); -} From 295131608c32ad75be02d04fa8a3cae6095d94e4 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 05:42:55 -0300 Subject: [PATCH 07/15] chore: revert unneeded changes --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index 16a1b6aee3..c3de839330 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -99,8 +99,7 @@ const visitors: Visitors = { const { decorators } = node; validateDecorators(decorators); - const decoratedExpressions = decorators.map((d) => d.expression) ?? []; - const decoratedExpression = decoratedExpressions[0]; + const decoratedExpression = decorators?.[0]?.expression; if (is.identifier(decoratedExpression, { name: 'api' })) { state.publicFields.push(node.key.name); } else if ( @@ -140,7 +139,7 @@ const visitors: Visitors = { const { decorators } = node; validateDecorators(decorators); // The real type is a subset of `Expression`, which doesn't work with the `is` validators - const decoratedExpression = decorators.map((d) => d.expression)[0]; + const decoratedExpression = decorators?.[0]?.expression; if ( is.callExpression(decoratedExpression) && From 03df628de96c96607204f06de5f926c331444414 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 05:45:09 -0300 Subject: [PATCH 08/15] chore: revert even more unneeded changes --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index c3de839330..fceca9d2c5 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -100,11 +100,12 @@ const visitors: Visitors = { const { decorators } = node; validateDecorators(decorators); const decoratedExpression = decorators?.[0]?.expression; - if (is.identifier(decoratedExpression, { name: 'api' })) { + if (is.identifier(decoratedExpression) && decoratedExpression.name === 'api') { state.publicFields.push(node.key.name); } else if ( is.callExpression(decoratedExpression) && - is.identifier(decoratedExpression.callee, { name: 'wire' }) + is.identifier(decoratedExpression.callee) && + decoratedExpression.callee.name === 'wire' ) { catalogWireAdapters(path, state); state.privateFields.push(node.key.name); From 3d73c71cf537a83c46414a487d60a7a1560f31d7 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 05:48:15 -0300 Subject: [PATCH 09/15] chore: revert the last unneeded changes --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 3 ++- packages/@lwc/ssr-compiler/src/compile-js/wire.ts | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index fceca9d2c5..b1bdcddc2f 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -144,7 +144,8 @@ const visitors: Visitors = { if ( is.callExpression(decoratedExpression) && - is.identifier(decoratedExpression.callee, { name: 'wire' }) + is.identifier(decoratedExpression.callee) && + decoratedExpression.callee.name === 'wire' ) { // Getters and setters are methods in the AST, but treated as properties by @wire // Note that this means that their implementations are ignored! diff --git a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts index 2cbb32af02..71ab5486d2 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts @@ -151,8 +151,7 @@ export function catalogWireAdapters( if ( is.literal(value) && typeof value.value === 'string' && - value.value.startsWith('$') && - value.value.length > 1 + value.value.startsWith('$') ) { prop.value = bMemberExpressionChain(value.value.slice(1).split('.')); } From 7801f459bd8b7a0225658f18e215c178c8057c15 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 05:50:07 -0300 Subject: [PATCH 10/15] chore: revert the final unneeded change --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 1 - packages/@lwc/ssr-compiler/src/compile-js/wire.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index b1bdcddc2f..e42951fed5 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -141,7 +141,6 @@ const visitors: Visitors = { validateDecorators(decorators); // The real type is a subset of `Expression`, which doesn't work with the `is` validators const decoratedExpression = decorators?.[0]?.expression; - if ( is.callExpression(decoratedExpression) && is.identifier(decoratedExpression.callee) && diff --git a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts index 71ab5486d2..189153e16e 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/wire.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/wire.ts @@ -11,6 +11,7 @@ import { DecoratorErrors } from '@lwc/errors'; import { esTemplate } from '../estemplate'; import { generateError } from './errors'; import type { NodePath } from 'estree-toolkit'; + import type { PropertyDefinition, ObjectExpression, From 84e7f9c541d0de79782e7d506fe1b1ef823551d0 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 13:09:59 -0300 Subject: [PATCH 11/15] Update packages/@lwc/ssr-compiler/src/compile-js/index.ts Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index e42951fed5..c3c8e38de5 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -230,9 +230,7 @@ function validateDecorators(decorators: EsDecorator[]) { throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'api'); } - const hasTrack = decorators.some(({ expression }) => - is.identifier(expression, { name: 'track' }) - ); + const hasTrack = expressions.some((expr) => is.identifier(expr, { name: 'track' }); if (hasWire && hasTrack) { throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'track'); From 79cc4915000cf0596af0502ad92ebe3b71077535 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 13:10:12 -0300 Subject: [PATCH 12/15] Update packages/@lwc/ssr-compiler/src/compile-js/index.ts Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index c3c8e38de5..5cdd1308e8 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -232,7 +232,7 @@ function validateDecorators(decorators: EsDecorator[]) { const hasTrack = expressions.some((expr) => is.identifier(expr, { name: 'track' }); - if (hasWire && hasTrack) { + if ((hasWire || hasApi) && hasTrack) { throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'track'); } } From 7e8528726c1ddc1e7862baf4bb4112af1ef0cefa Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 13:10:22 -0300 Subject: [PATCH 13/15] Update packages/@lwc/ssr-compiler/src/compile-js/errors.ts Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> --- packages/@lwc/ssr-compiler/src/compile-js/errors.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/errors.ts b/packages/@lwc/ssr-compiler/src/compile-js/errors.ts index 04abcd38e1..62ab5c0f70 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/errors.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/errors.ts @@ -7,9 +7,16 @@ import { generateErrorMessage, type LWCErrorInfo } from '@lwc/errors'; // This type extracts the arguments in a string. Example: "Error {0} {1}" -> [string, string] -type ExtractArguments = T extends `${string}{${number}}${infer R}` - ? [string, ...ExtractArguments] - : []; +type ExtractArguments< + T extends string, + Numbers extends number = never, + Args extends string[] = [], +> = T extends `${string}{${infer N extends number}}${infer R}` + ? N extends Numbers // Is `N` in the union of seen numbers? + ? ExtractArguments // new `N`, add an argument + : ExtractArguments // `N` already accounted for + : Args; // No `N` found, nothing more to check +``` export function generateError( error: T, From 617b3ef5ded8165774212ae9c4cf462c76a9d4c3 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 13:10:32 -0300 Subject: [PATCH 14/15] Update packages/@lwc/ssr-compiler/src/compile-js/index.ts Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> --- packages/@lwc/ssr-compiler/src/compile-js/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index 5cdd1308e8..e47b3763ca 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -213,7 +213,7 @@ const visitors: Visitors = { }, }; -function validateDecorators(decorators: EsDecorator[]) { +function validateUniqueDecorator(decorators: EsDecorator[]) { if (decorators.length < 2) { return; } From 56aa004d426beb17475fac0c9c0604a4f4f4207f Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 19 Dec 2024 13:14:42 -0300 Subject: [PATCH 15/15] fix: adjust after inline suggestions --- packages/@lwc/ssr-compiler/src/compile-js/errors.ts | 1 - packages/@lwc/ssr-compiler/src/compile-js/index.ts | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-js/errors.ts b/packages/@lwc/ssr-compiler/src/compile-js/errors.ts index 62ab5c0f70..1a57ad5e4b 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/errors.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/errors.ts @@ -16,7 +16,6 @@ type ExtractArguments< ? ExtractArguments // new `N`, add an argument : ExtractArguments // `N` already accounted for : Args; // No `N` found, nothing more to check -``` export function generateError( error: T, diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index e47b3763ca..c0fe02058a 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -98,7 +98,7 @@ const visitors: Visitors = { } const { decorators } = node; - validateDecorators(decorators); + validateUniqueDecorator(decorators); const decoratedExpression = decorators?.[0]?.expression; if (is.identifier(decoratedExpression) && decoratedExpression.name === 'api') { state.publicFields.push(node.key.name); @@ -138,7 +138,7 @@ const visitors: Visitors = { } const { decorators } = node; - validateDecorators(decorators); + validateUniqueDecorator(decorators); // The real type is a subset of `Expression`, which doesn't work with the `is` validators const decoratedExpression = decorators?.[0]?.expression; if ( @@ -230,7 +230,7 @@ function validateUniqueDecorator(decorators: EsDecorator[]) { throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'api'); } - const hasTrack = expressions.some((expr) => is.identifier(expr, { name: 'track' }); + const hasTrack = expressions.some((expr) => is.identifier(expr, { name: 'track' })); if ((hasWire || hasApi) && hasTrack) { throw generateError(DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'track');