Skip to content

Commit

Permalink
Merge pull request #14 from playcanvas/fix-semantic-scope
Browse files Browse the repository at this point in the history
Scoped Tag Validation
  • Loading branch information
marklundin authored Sep 16, 2024
2 parents c74bda5 + 2df1d72 commit 8885e14
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 18 deletions.
40 changes: 24 additions & 16 deletions src/utils/tag-utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DiagnosticCategory } from 'typescript';

import { parseNumber, parseStringToNumericalArray } from './ts-utils.js';

/**
Expand Down Expand Up @@ -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;
}
8 changes: 6 additions & 2 deletions test/fixtures/program.valid.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
}

Expand Down
1 change: 1 addition & 0 deletions test/tests/valid/program.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
});

0 comments on commit 8885e14

Please sign in to comment.