diff --git a/src/utils/tag-utils.js b/src/utils/tag-utils.js index ace641e..ba0b167 100644 --- a/src/utils/tag-utils.js +++ b/src/utils/tag-utils.js @@ -1,3 +1,5 @@ +import { DiagnosticCategory } from 'typescript'; + import { parseNumber, parseStringToNumericalArray } from './ts-utils.js'; /** @@ -35,31 +37,37 @@ 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 {String} value - The value to be validated. + * @param {String} typeAnnotation - The TypeScript type annotation as a string. + * @param {import('@typescript/vfs').VirtualTypeScriptEnvironment} 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; - // Filter against the type errors we're concerned with - const typeErrors = errors.filter(error => error.category === 1 && error.code === 2322); + // Fetch semantic diagnostics only for the virtual file + const errors = languageService.getSemanticDiagnostics(virtualFileName); - // return first error - const typeError = typeErrors[0]; + // 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 === DiagnosticCategory.Error + ); - if (typeError) { - throw new Error(`${typeError.messageText}`); + // If any type error is found, throw an error with the diagnostic message + if (typeErrors.length > 0) { + const errorMessage = typeErrors[0].messageText.toString(); + 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; }); });