From 876416dbdb80d2d4d5f1a10ea5c70b35e711db0c Mon Sep 17 00:00:00 2001 From: Mark Lundin Date: Mon, 16 Sep 2024 16:50:35 +0100 Subject: [PATCH 1/3] 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; }); }); From 54ed89cc5ea642d8c0835416130a8ebfd33ab9f5 Mon Sep 17 00:00:00 2001 From: Mark Lundin Date: Mon, 16 Sep 2024 17:22:02 +0100 Subject: [PATCH 2/3] Add TypeScript import and update parameter types in validateTag function --- src/utils/tag-utils.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/utils/tag-utils.js b/src/utils/tag-utils.js index 937d5e3..47f479e 100644 --- a/src/utils/tag-utils.js +++ b/src/utils/tag-utils.js @@ -1,3 +1,5 @@ +import { DiagnosticCategory, flattenDiagnosticMessageText } from 'typescript'; + import { parseNumber, parseStringToNumericalArray } from './ts-utils.js'; /** @@ -35,15 +37,15 @@ export function parseTag(input = '') { /** * Validates that a tag value matches the expected type - * - * @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. + * + * @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. + * @returns {true} if validation passes without type errors. */ export function validateTag(value, typeAnnotation, env) { - const virtualFileName = "/___virtual__.ts"; + const virtualFileName = '/___virtual__.ts'; const sourceText = `let a: ${typeAnnotation} = ${value};`; // Create or overwrite the virtual file with the new source text @@ -57,16 +59,12 @@ export function validateTag(value, typeAnnotation, env) { // 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 + error => error.code === 2322 && error.category === DiagnosticCategory.Error ); // 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(); - + const errorMessage = typeErrors[0].messageText.toString(); throw new Error(`Type Validation Error: ${errorMessage}`); } From 2df1d72c490a9338f6b8e9db8e7a8fbe1b31468d Mon Sep 17 00:00:00 2001 From: Mark Lundin Date: Mon, 16 Sep 2024 17:22:30 +0100 Subject: [PATCH 3/3] Remove unused import in tag-utils.js --- src/utils/tag-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/tag-utils.js b/src/utils/tag-utils.js index 47f479e..ba0b167 100644 --- a/src/utils/tag-utils.js +++ b/src/utils/tag-utils.js @@ -1,4 +1,4 @@ -import { DiagnosticCategory, flattenDiagnosticMessageText } from 'typescript'; +import { DiagnosticCategory } from 'typescript'; import { parseNumber, parseStringToNumericalArray } from './ts-utils.js';