diff --git a/packages/lwc-language-server/src/javascript/__tests__/compiler.test.ts b/packages/lwc-language-server/src/javascript/__tests__/compiler.test.ts index eb1c1a3f..5466182d 100644 --- a/packages/lwc-language-server/src/javascript/__tests__/compiler.test.ts +++ b/packages/lwc-language-server/src/javascript/__tests__/compiler.test.ts @@ -33,6 +33,21 @@ export default class Foo extends LightningElement { @api property = true; } `; +const codeWithoutDefaultExportSingleClass = ` +import { api, LightningElement } from 'lwc'; +class Foo extends LightningElement { + @api foo; +} +`; +const codeWithoutDefaultExportMultipleClasses = ` +import { api, LightningElement } from 'lwc'; +class Foo extends LightningElement { + @api foo; +} +class Bar extends LightningElement { + @api bar; +} +`; it('can get metadata from a simple component', async () => { await compileSource(codeOk, 'foo.js'); @@ -46,6 +61,21 @@ it('displays an error for a component with syntax error', async () => { expect(diagnostic.message).toMatch('Unexpected token (4:17)'); }); +it('returns empty metadata for a script without a clear main component class', async () => { + const result = await compileSource(codeWithoutDefaultExportMultipleClasses, 'foo.js'); + expect(result.metadata?.decorators).toHaveLength(0); + expect(result.metadata?.classMembers).toHaveLength(0); + expect(result.metadata?.exports).toHaveLength(0); +}); + +it('returns metadata for a script with one component class, even when not exported', async () => { + const result = await compileSource(codeWithoutDefaultExportSingleClass, 'foo.js'); + console.log(result); + expect(result.metadata?.decorators).toHaveLength(1); + expect(result.metadata?.classMembers).toHaveLength(1); + expect(result.metadata?.exports).toHaveLength(0); +}); + it('displays an error for a component with other errors', async () => { const result = await compileSource(codeError, 'foo.js'); expect(result.metadata).toBeUndefined(); diff --git a/packages/lwc-language-server/src/javascript/type-mapping.ts b/packages/lwc-language-server/src/javascript/type-mapping.ts index f99b7628..d037f0cb 100644 --- a/packages/lwc-language-server/src/javascript/type-mapping.ts +++ b/packages/lwc-language-server/src/javascript/type-mapping.ts @@ -541,9 +541,24 @@ function getExports(lwcExports: LwcExport[]): InternalModuleExports[] { * LWC language server to analyze code in a user's IDE. */ export function mapLwcMetadataToInternal(lwcMeta: ScriptFile): InternalMetadata { - const mainClassObj = lwcMeta.classes.find(classObj => { - return classObj.id == lwcMeta.mainClass.refId; - }); + let mainClassObj; + if (lwcMeta.mainClass) { + mainClassObj = lwcMeta.classes.find(classObj => { + return classObj.id == lwcMeta.mainClass.refId; + }); + } else if (lwcMeta.classes.length === 1) { + mainClassObj = lwcMeta.classes[0]; + } + + // If we are unable to identify the main class object from the provided metadata, + // it will not be possible calculate decorators, members, etc. + if (!mainClassObj) { + return { + decorators: [], + classMembers: [], + exports: [], + }; + } const defaultExport = lwcMeta.exports.filter((exp) => exp.defaultExport)[0]; const declarationLoc = externalToInternalLoc(defaultExport?.location ?? mainClassObj.location);