From 5d1837bdcdf75724e806a34b55c50acefd753f5d Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Mon, 16 Dec 2024 13:48:05 -0800 Subject: [PATCH] fix(compiler): log warning for missing name/namespace (#4825) --- .../src/__tests__/warnings.spec.ts | 49 +++++++++++++++++++ .../@lwc/babel-plugin-component/src/types.ts | 4 +- packages/@lwc/compiler/src/options.ts | 4 +- packages/@lwc/rollup-plugin/src/index.ts | 11 +++++ packages/@lwc/shared/src/custom-element.ts | 12 +++++ 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 packages/@lwc/babel-plugin-component/src/__tests__/warnings.spec.ts diff --git a/packages/@lwc/babel-plugin-component/src/__tests__/warnings.spec.ts b/packages/@lwc/babel-plugin-component/src/__tests__/warnings.spec.ts new file mode 100644 index 0000000000..7d3da5d275 --- /dev/null +++ b/packages/@lwc/babel-plugin-component/src/__tests__/warnings.spec.ts @@ -0,0 +1,49 @@ +/* + * 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 { afterEach, beforeEach, expect, vi, test } from 'vitest'; +import { transformSync } from '@babel/core'; +import plugin from '../index'; + +let spy; + +beforeEach(() => { + spy = vi.spyOn(console, 'warn'); +}); + +afterEach(() => { + spy!.mockReset(); +}); + +test('warns on missing name/namespace', () => { + const source = ` + import { LightningElement } from 'lwc'; + export default class extends LightningElement {}; + `; + + const { code } = transformSync(source, { + babelrc: false, + configFile: false, + filename: `foo.js`, + plugins: [ + [ + plugin, + { + namespace: '', + name: '', + }, + ], + ], + })!; + + // compilation works successfully + expect(code).toBeTypeOf('string'); + + expect(spy!).toHaveBeenCalledOnce(); + expect(spy!).toHaveBeenCalledWith( + 'The namespace and name should both be non-empty strings. You may get unexpected behavior at runtime. Found: namespace="" and name=""' + ); +}); diff --git a/packages/@lwc/babel-plugin-component/src/types.ts b/packages/@lwc/babel-plugin-component/src/types.ts index 76dfb2f5de..0c70254398 100644 --- a/packages/@lwc/babel-plugin-component/src/types.ts +++ b/packages/@lwc/babel-plugin-component/src/types.ts @@ -17,8 +17,8 @@ export interface LwcBabelPluginOptions { loader?: string; strictSpecifier?: boolean; }; - namespace?: string; - name?: string; + namespace: string; + name: string; instrumentation?: InstrumentationObject; apiVersion?: number; } diff --git a/packages/@lwc/compiler/src/options.ts b/packages/@lwc/compiler/src/options.ts index ad56230b05..576a3eb62e 100755 --- a/packages/@lwc/compiler/src/options.ts +++ b/packages/@lwc/compiler/src/options.ts @@ -95,9 +95,9 @@ export interface DynamicImportConfig { */ export interface TransformOptions { /** The name of the component. For example, the name in `` is `"component"`. */ - name?: string; + name: string; /** The namespace of the component. For example, the namespace in `` is `"my"`. */ - namespace?: string; + namespace: string; /** @deprecated Ignored by compiler. */ stylesheetConfig?: StylesheetConfig; // TODO [#5031]: Unify dynamicImports and experimentalDynamicComponent options diff --git a/packages/@lwc/rollup-plugin/src/index.ts b/packages/@lwc/rollup-plugin/src/index.ts index 7256f6e019..6004939ad1 100644 --- a/packages/@lwc/rollup-plugin/src/index.ts +++ b/packages/@lwc/rollup-plugin/src/index.ts @@ -334,6 +334,17 @@ export default function lwc(pluginOptions: RollupLwcOptions = {}): Plugin { // a '/' regardless of Windows vs Unix, since it comes from the Rollup `id` specifier?.split('/') ?? path.dirname(filename).split('/').slice(-2); + /* v8 ignore next */ + if (!namespace || !name) { + // TODO [#4824]: Make this an error rather than a warning + this.warn( + 'The component namespace and name could not be determined from the specifier ' + + JSON.stringify(specifier) + + ' or filename ' + + JSON.stringify(filename) + ); + } + const apiVersionToUse = getAPIVersionFromNumber(apiVersion); const { code, map, warnings } = transformSync(src, filename, { diff --git a/packages/@lwc/shared/src/custom-element.ts b/packages/@lwc/shared/src/custom-element.ts index 8d74ffb8cc..c8ae664f4d 100644 --- a/packages/@lwc/shared/src/custom-element.ts +++ b/packages/@lwc/shared/src/custom-element.ts @@ -15,6 +15,18 @@ * @returns component tag name */ export function generateCustomElementTagName(namespace: string = '', name: string = '') { + if (!namespace || !name) { + // TODO [#4824]: Make this an error rather than a warning + // eslint-disable-next-line no-console + console.warn( + 'The namespace and name should both be non-empty strings. ' + + 'You may get unexpected behavior at runtime. ' + + 'Found: namespace=' + + JSON.stringify(namespace) + + ' and name=' + + JSON.stringify(namespace) + ); + } const kebabCasedName = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); return `${namespace}-${kebabCasedName}`; }