From 876416dbdb80d2d4d5f1a10ea5c70b35e711db0c Mon Sep 17 00:00:00 2001 From: Mark Lundin Date: Mon, 16 Sep 2024 16:50:35 +0100 Subject: [PATCH] Fix type validation error in tag-utils.js --- src/utils/tag-utils.js | 44 ++++++++++++++++++++------------ test/fixtures/program.valid.js | 8 ++++-- test/tests/valid/program.test.js | 1 + 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/utils/tag-utils.js b/src/utils/tag-utils.js index ace641e..937d5e3 100644 --- a/src/utils/tag-utils.js +++ b/src/utils/tag-utils.js @@ -35,31 +35,41 @@ export function parseTag(input = '') { /** * Validates that a tag value matches the expected type - * @throws {Error} - If the tag value is not the expected type - * - * @param {String} value - The string representation of the value to type check - * @param {string} typeAnnotation - The expected type - * @param {import('@typescript/vfs').VirtualTypeScriptEnvironment} env - The environment to validate in - * @returns {boolean} - Whether the tag value is valid + * + * @param value - The value to be validated. + * @param typeAnnotation - The TypeScript type annotation as a string. + * @param env - The environment containing the language service and file creation utilities. + * @throws Will throw an error if the value does not conform to the typeAnnotation. + * @returns `true` if validation passes without type errors. */ export function validateTag(value, typeAnnotation, env) { - + const virtualFileName = "/___virtual__.ts"; const sourceText = `let a: ${typeAnnotation} = ${value};`; - env.createFile('/___virtual__.ts', sourceText); - // Get the program and check for semantic errors - const program = env.languageService.getProgram(); - const errors = program.getSemanticDiagnostics(); + // Create or overwrite the virtual file with the new source text + env.createFile(virtualFileName, sourceText); + + // Retrieve the language service from the environment + const languageService = env.languageService; + + // Fetch semantic diagnostics only for the virtual file + const errors = languageService.getSemanticDiagnostics(virtualFileName); - // Filter against the type errors we're concerned with - const typeErrors = errors.filter(error => error.category === 1 && error.code === 2322); + // Filter for type assignment errors (Error Code 2322: Type 'X' is not assignable to type 'Y') + const typeErrors = errors.filter( + error => error.code === 2322 && error.category === ts.DiagnosticCategory.Error + ); - // return first error - const typeError = typeErrors[0]; + // If any type error is found, throw an error with the diagnostic message + if (typeErrors.length > 0) { + // TypeScript's messageText can be a string or a DiagnosticMessageChain + const errorMessage = typeErrors[0].messageText instanceof ts.DiagnosticMessageChain + ? flattenDiagnosticMessageText(typeErrors[0].messageText, "\n") + : typeErrors[0].messageText.toString(); - if (typeError) { - throw new Error(`${typeError.messageText}`); + throw new Error(`Type Validation Error: ${errorMessage}`); } + // If no type errors are found, return true indicating successful validation return true; } diff --git a/test/fixtures/program.valid.js b/test/fixtures/program.valid.js index 2cb91ed..6ff593d 100644 --- a/test/fixtures/program.valid.js +++ b/test/fixtures/program.valid.js @@ -8,13 +8,17 @@ export const MyEnum = { value: 0 }; class Example extends Script { /** * @attribute - * @type {boolean} + * @precision 1 + * @type {number} */ - a = false; + a; initialize() { confetti(); new TWEEN.Tween({ x: 0 }).to({ x: 100 }, 1000).start(); + + // This is an intentional type error, but the parser should ignore these + this.a = 'string'; } } diff --git a/test/tests/valid/program.test.js b/test/tests/valid/program.test.js index 427b4d7..0d03ea3 100644 --- a/test/tests/valid/program.test.js +++ b/test/tests/valid/program.test.js @@ -13,5 +13,6 @@ describe('VALID: Program ', function () { expect(data).to.exist; expect(data[0]).to.not.be.empty; expect(data[1]).to.be.empty; + expect(data[0].example.errors).to.be.empty; }); });