From 69c8799707a47a9213098d864b41f5327e70cdf3 Mon Sep 17 00:00:00 2001 From: Lukas Streckeisen Date: Wed, 21 May 2025 14:04:03 +0200 Subject: [PATCH] make generator throw error when something goes wrong --- .../commands/GeneratorCommandExecutor.ts | 20 ++++++--------- .../generators/ContextMapperGenerator.ts | 2 +- src/language/generators/PlantUMLGenerator.ts | 7 +++--- test/commands/CommandExecution.test.ts | 25 ++++++++++++++----- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/language/commands/GeneratorCommandExecutor.ts b/src/language/commands/GeneratorCommandExecutor.ts index 4c47b96..4263656 100644 --- a/src/language/commands/GeneratorCommandExecutor.ts +++ b/src/language/commands/GeneratorCommandExecutor.ts @@ -14,31 +14,26 @@ export class GeneratorCommandExecutor { this.serviceRegistry = serviceRegistry } - async execute (generator: ContextMapperGenerator, args: unknown[], cancelToken: CancellationToken): Promise { + async execute (generator: ContextMapperGenerator, args: unknown[], cancelToken: CancellationToken): Promise { const filePath = args[0] as string const model = await this.extractModel(filePath) if (cancelToken.isCancellationRequested) { - return undefined + return [] } - if (model == null) { - return undefined - } - args.shift() + args.shift() // remove source file from args array return await generator.generate(model, filePath, args, cancelToken) } - private async extractModel (filePath: string): Promise { + private async extractModel (filePath: string): Promise { const extensions = ContextMapperDslLanguageMetaData.fileExtensions as readonly string[] if (!extensions.includes(path.extname(filePath))) { - console.error('Unsupported file extension on file', filePath) - return undefined + throw new Error(`Unsupported file extension on file ${filePath}`) } if (!fs.existsSync(filePath)) { - console.error(`File ${filePath} does not exist.`) - return undefined + throw new Error(`File ${filePath} does not exist.`) } const document = await this.getServices().shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(path.resolve(filePath))) @@ -46,8 +41,7 @@ export class GeneratorCommandExecutor { const validationErrors = (document.diagnostics ?? []).filter(e => e.severity === 1) if (validationErrors.length > 0) { - console.error(`File ${filePath} is invalid`) - return undefined + throw new Error(`File ${filePath} is invalid`) } return document.parseResult.value as ContextMappingModel diff --git a/src/language/generators/ContextMapperGenerator.ts b/src/language/generators/ContextMapperGenerator.ts index 5846504..e70ed13 100644 --- a/src/language/generators/ContextMapperGenerator.ts +++ b/src/language/generators/ContextMapperGenerator.ts @@ -2,5 +2,5 @@ import { ContextMappingModel } from '../generated/ast.js' import { CancellationToken } from 'vscode-languageserver' export interface ContextMapperGenerator { - generate(model: ContextMappingModel, filePath: string, args: unknown[], cancelToken: CancellationToken): Promise + generate(model: ContextMappingModel, filePath: string, args: unknown[], cancelToken: CancellationToken): Promise } \ No newline at end of file diff --git a/src/language/generators/PlantUMLGenerator.ts b/src/language/generators/PlantUMLGenerator.ts index 009613e..6a02375 100644 --- a/src/language/generators/PlantUMLGenerator.ts +++ b/src/language/generators/PlantUMLGenerator.ts @@ -6,16 +6,16 @@ import { ComponentDiagramGenerator } from './plantuml/ComponentDiagramGenerator. import { CancellationToken } from 'vscode-languageserver' export class PlantUMLGenerator implements ContextMapperGenerator { - async generate (model: ContextMappingModel, filePath: string, args: unknown[], cancelToken: CancellationToken): Promise { + async generate (model: ContextMappingModel, filePath: string, args: unknown[], cancelToken: CancellationToken): Promise { // there must not be any extra spaces especially at the start, since the path will be treated as relative otherwise const destination = (args[0] as string)?.trim() if (destination == null || destination === '') { console.log('Destination must be specified') - return undefined + throw Error('Destination must be specified') } if (cancelToken.isCancellationRequested) { - return undefined + return [] } const fileName = filePath.split('/').pop()!.split('.')[0] @@ -30,7 +30,6 @@ export class PlantUMLGenerator implements ContextMapperGenerator { diagrams.push(componentDiagram) } - console.log('Successfully generated PlantUML diagrams') return diagrams } diff --git a/test/commands/CommandExecution.test.ts b/test/commands/CommandExecution.test.ts index 1e174b2..ea60aa9 100644 --- a/test/commands/CommandExecution.test.ts +++ b/test/commands/CommandExecution.test.ts @@ -5,6 +5,7 @@ import { afterEach, beforeAll, describe, expect, test } from 'vitest' import { NodeFileSystem } from 'langium/node' import fs from 'node:fs' import path from 'node:path' +import { fail } from 'node:assert' const outDir = path.join(__dirname, 'out') @@ -38,8 +39,12 @@ describe('Command execution tests', () => { test('test plantUML command with invalid file extension', async () => { const file = path.join(__dirname, '..', 'invalid-files', 'test.txt') - const result = await commandHandler.executeCommand('org.contextmapper.GeneratePlantUML', [file, outDir]) - expect(result).toBeUndefined() + try { + await commandHandler.executeCommand('org.contextmapper.GeneratePlantUML', [file, outDir]) + fail('Expected generator to fail') + } catch (e) { + expect(e).not.toBeUndefined() + } const outContentExists = fs.existsSync(outDir) expect(outContentExists).toEqual(false) @@ -48,8 +53,12 @@ describe('Command execution tests', () => { test('test plantUML command with invalid file', async () => { const file = path.join(__dirname, '..', 'invalid-files', 'invalid.cml') - const result = await commandHandler.executeCommand('org.contextmapper.GeneratePlantUML', [file, outDir]) - expect(result).toBeUndefined() + try { + await commandHandler.executeCommand('org.contextmapper.GeneratePlantUML', [file, outDir]) + fail('Expected generator to fail') + } catch (e) { + expect(e).not.toBeUndefined() + } const outContentExists = fs.existsSync(outDir) expect(outContentExists).toEqual(false) @@ -58,8 +67,12 @@ describe('Command execution tests', () => { test('test plantUML command with non-existing file', async () => { const file = path.join(__dirname, '..', 'invalid-files', 'non-existing.cml') - const result = await commandHandler.executeCommand('org.contextmapper.GeneratePlantUML', [file, outDir]) - expect(result).toBeUndefined() + try { + await commandHandler.executeCommand('org.contextmapper.GeneratePlantUML', [file, outDir]) + fail('Expected generator to fail') + } catch (e) { + expect(e).not.toBeUndefined() + } const outContentExists = fs.existsSync(outDir) expect(outContentExists).toEqual(false)