From c04b0b7d91823266d271b64283f8a00575c05b9e Mon Sep 17 00:00:00 2001 From: Will Harney <62956339+wjhsf@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:58:03 -0500 Subject: [PATCH] chore(ssr): use real error messages for errors @W-17408217 (#5249) * chore(ssr): use real error messages for errors No more TODO! * chore: update test fixture with new error * chore: fix expected error message --- .../prohibited-variable-name/error-ssr.txt | 2 +- .../errors/src/compiler/error-info/index.ts | 3 +- .../errors/src/compiler/error-info/ssr.ts | 20 ++++++++ .../src/__tests__/dynamic-imports.spec.ts | 2 +- .../src/compile-js/decorators/wire.ts | 51 ++++++++++--------- .../@lwc/ssr-compiler/src/compile-js/index.ts | 13 +++-- 6 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 packages/@lwc/errors/src/compiler/error-info/ssr.ts diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/prohibited-variable-name/error-ssr.txt b/packages/@lwc/engine-server/src/__tests__/fixtures/prohibited-variable-name/error-ssr.txt index fab580ddda..057c1267c0 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/prohibited-variable-name/error-ssr.txt +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/prohibited-variable-name/error-ssr.txt @@ -1 +1 @@ -LWCTODO: identifier name '__lwcThrowAnError__' cannot start with '__lwc' \ No newline at end of file +LWC1202: Identifier name cannot start with "__lwc". \ No newline at end of file diff --git a/packages/@lwc/errors/src/compiler/error-info/index.ts b/packages/@lwc/errors/src/compiler/error-info/index.ts index aad52bc788..99107e2034 100644 --- a/packages/@lwc/errors/src/compiler/error-info/index.ts +++ b/packages/@lwc/errors/src/compiler/error-info/index.ts @@ -5,9 +5,10 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ /** - * Next error code: 1202 + * Next error code: 1203 */ export * from './compiler'; export * from './lwc-class'; export * from './template-transform'; +export * from './ssr'; diff --git a/packages/@lwc/errors/src/compiler/error-info/ssr.ts b/packages/@lwc/errors/src/compiler/error-info/ssr.ts new file mode 100644 index 0000000000..2392517e0a --- /dev/null +++ b/packages/@lwc/errors/src/compiler/error-info/ssr.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025, 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 { DiagnosticLevel } from '../../shared/types'; + +/* + * For the next available error code, reference (and update!) the value in ./index.ts + */ + +export const SsrCompilerErrors = { + RESERVED_IDENTIFIER_PREFIX: { + code: 1202, + message: 'Identifier name cannot start with "__lwc".', + level: DiagnosticLevel.Error, + url: '', + }, +} as const; diff --git a/packages/@lwc/ssr-compiler/src/__tests__/dynamic-imports.spec.ts b/packages/@lwc/ssr-compiler/src/__tests__/dynamic-imports.spec.ts index 25178ba1fe..751ce70ad8 100644 --- a/packages/@lwc/ssr-compiler/src/__tests__/dynamic-imports.spec.ts +++ b/packages/@lwc/ssr-compiler/src/__tests__/dynamic-imports.spec.ts @@ -50,7 +50,7 @@ describe('dynamic imports', () => { }; if (strictSpecifier && !isStrict) { - expect(callback).toThrowError(/INVALID_DYNAMIC_IMPORT_SOURCE_STRICT/); + expect(callback).toThrowError(/LWC1121/); return; } else { callback(); diff --git a/packages/@lwc/ssr-compiler/src/compile-js/decorators/wire.ts b/packages/@lwc/ssr-compiler/src/compile-js/decorators/wire.ts index b733ace220..a631825699 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/decorators/wire.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/decorators/wire.ts @@ -24,6 +24,7 @@ import type { BlockStatement, Decorator, CallExpression, + SpreadElement, } from 'estree'; import type { ComponentMetaState, WireAdapter } from '../types'; @@ -42,36 +43,29 @@ function bMemberExpressionChain(props: string[]): MemberExpression { function getWireParams( node: MethodDefinition | PropertyDefinition -): [Expression, Expression | undefined] { +): (Expression | SpreadElement)[] { const { decorators } = node; if (decorators.length > 1) { throw generateError(node, DecoratorErrors.ONE_WIRE_DECORATOR_ALLOWED); } - // validate the parameters + // Before calling this function, we validate that it has exactly one decorator, @wire const wireDecorator = decorators[0].expression; if (!is.callExpression(wireDecorator)) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - invalid usage'); + throw generateError(node, DecoratorErrors.FUNCTION_IDENTIFIER_SHOULD_BE_FIRST_PARAMETER); } const args = wireDecorator.arguments; - if (args.length === 0 || args.length > 2) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - wrong number of args'); + if (args.length === 0) { + throw generateError(node, DecoratorErrors.ADAPTER_SHOULD_BE_FIRST_PARAMETER); } - const [id, config] = args; - if (is.spreadElement(id) || is.spreadElement(config)) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - spread in params'); - } - return [id, config]; + return args; } function validateWireId( - id: Expression, + id: Expression | SpreadElement, path: NodePath ): asserts id is Identifier | MemberExpression { // name of identifier or object used in member expression (e.g. "foo" for `foo.bar`) @@ -79,17 +73,23 @@ function validateWireId( if (is.memberExpression(id)) { if (id.computed) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - FUNCTION_IDENTIFIER_CANNOT_HAVE_COMPUTED_PROPS'); + throw generateError( + path.node!, + DecoratorErrors.FUNCTION_IDENTIFIER_CANNOT_HAVE_COMPUTED_PROPS + ); } if (!is.identifier(id.object)) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - FUNCTION_IDENTIFIER_CANNOT_HAVE_NESTED_MEMBER_EXRESSIONS'); + throw generateError( + path.node!, + DecoratorErrors.FUNCTION_IDENTIFIER_CANNOT_HAVE_NESTED_MEMBER_EXRESSIONS + ); } wireAdapterVar = id.object.name; } else if (!is.identifier(id)) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - invalid adapter name'); + throw generateError( + path.node!, + DecoratorErrors.FUNCTION_IDENTIFIER_SHOULD_BE_FIRST_PARAMETER + ); } else { wireAdapterVar = id.name; } @@ -104,12 +104,11 @@ function validateWireId( } function validateWireConfig( - config: Expression, + config: Expression | SpreadElement | undefined, path: NodePath ): asserts config is NoSpreadObjectExpression { if (!is.objectExpression(config)) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - CONFIG_OBJECT_SHOULD_BE_SECOND_PARAMETER'); + throw generateError(path.node!, DecoratorErrors.CONFIG_OBJECT_SHOULD_BE_SECOND_PARAMETER); } for (const property of config.properties) { // Only validate computed object properties because static props are all valid @@ -127,8 +126,10 @@ function validateWireConfig( if (is.templateLiteral(key)) { // A template literal is not guaranteed to always result in the same value // (e.g. `${Math.random()}`), so we disallow them entirely. - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - COMPUTED_PROPERTY_CANNOT_BE_TEMPLATE_LITERAL'); + throw generateError( + path.node!, + DecoratorErrors.COMPUTED_PROPERTY_CANNOT_BE_TEMPLATE_LITERAL + ); } else if (!('regex' in key)) { // A literal can be a regexp, template literal, or primitive; only allow primitives continue; diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index eecb39860a..97f160f3ce 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -10,6 +10,7 @@ import { traverse, builders as b, is } from 'estree-toolkit'; import { parseModule } from 'meriyah'; import { LWC_VERSION_COMMENT, type CompilationMode } from '@lwc/shared'; +import { LWCClassErrors, SsrCompilerErrors } from '@lwc/errors'; import { transmogrify } from '../transmogrify'; import { ImportManager } from '../imports'; import { replaceLwcImport, replaceNamedLwcExport, replaceAllLwcExport } from './lwc-import'; @@ -24,6 +25,7 @@ import { removeDecoratorImport } from './remove-decorator-import'; import { type Visitors, type ComponentMetaState } from './types'; import { validateUniqueDecorator } from './decorators'; +import { generateError } from './errors'; import type { ComponentTransformOptions } from '../shared'; import type { Identifier as EsIdentifier, @@ -60,8 +62,10 @@ const visitors: Visitors = { } if (experimentalDynamicComponent.strictSpecifier) { if (!is.literal(path.node?.source) || typeof path.node.source.value !== 'string') { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error('todo - LWCClassErrors.INVALID_DYNAMIC_IMPORT_SOURCE_STRICT'); + throw generateError( + path.node!, + LWCClassErrors.INVALID_DYNAMIC_IMPORT_SOURCE_STRICT + ); } } const loader = experimentalDynamicComponent.loader; @@ -162,7 +166,7 @@ const visitors: Visitors = { state.publicProperties.set(node.key.name, node); } else if (isWireDecorator(decorators[0])) { if (node.computed) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` + // TODO [W-17758410]: implement throw new Error('@wire cannot be used on computed properties in SSR context.'); } const isRealMethod = node.kind === 'method'; @@ -245,8 +249,7 @@ const visitors: Visitors = { Identifier(path, _state) { const { node } = path; if (node?.name.startsWith('__lwc')) { - // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler` - throw new Error(`LWCTODO: identifier name '${node.name}' cannot start with '__lwc'`); + throw generateError(node, SsrCompilerErrors.RESERVED_IDENTIFIER_PREFIX); } }, };