diff --git a/packages/ts-transformers/src/core/ct-helpers.ts b/packages/ts-transformers/src/core/ct-helpers.ts index eff061d62..3f21b920f 100644 --- a/packages/ts-transformers/src/core/ct-helpers.ts +++ b/packages/ts-transformers/src/core/ct-helpers.ts @@ -79,13 +79,8 @@ export class CTHelpers { export function transformCtDirective( source: string, ): string { - // Throw when this symbol name is already in use - // for some reason. - if (source.indexOf(CT_HELPERS_IDENTIFIER) !== -1) { - throw new Error( - `Source cannot contain reserved '${CT_HELPERS_IDENTIFIER}' symbol.`, - ); - } + checkCTHelperVar(source); + const lines = source.split("\n"); if (!lines[0] || !isCTSEnabled(lines[0])) { return source; @@ -101,6 +96,27 @@ function isCTSEnabled(line: string) { return /^\/\/\/\s*/m.test(line); } +// Throws if `__ctHelpers` was found as an Identifier +// in the source code. +function checkCTHelperVar(source: string) { + const sourceFile = ts.createSourceFile( + "source.tsx", + source, + ts.ScriptTarget.ES2023, + ); + const visitor = (node: ts.Node): ts.Node => { + if (ts.isIdentifier(node)) { + if (node.text === CT_HELPERS_IDENTIFIER) { + throw new Error( + `Source cannot contain reserved '${CT_HELPERS_IDENTIFIER}' symbol.`, + ); + } + } + return ts.visitEachChild(node, visitor, undefined); + }; + ts.visitNode(sourceFile, visitor); +} + function getCTHelpersIdentifier( statement: ts.Statement, ): ts.Identifier | undefined { diff --git a/packages/ts-transformers/test/transform.test.ts b/packages/ts-transformers/test/transform.test.ts index 2cf4777b4..84ab9b432 100644 --- a/packages/ts-transformers/test/transform.test.ts +++ b/packages/ts-transformers/test/transform.test.ts @@ -1,5 +1,5 @@ import { describe, it } from "@std/testing/bdd"; -import { assert } from "@std/assert"; +import { assert, assertRejects } from "@std/assert"; import { transformFiles } from "./utils.ts"; const fixture = ` @@ -34,3 +34,37 @@ describe("CommonToolsTransformerPipeline", () => { ); }); }); + +describe("CTHelpers handling", () => { + it("Throws if __ctHelpers variable is used in source", async () => { + const statements = [ + "function __ctHelpers() {}", + "function foo(): number { var __ctHelpers = 5; return __ctHelpers; }", + "var __ctHelpers: number = 5;", + "declare global { var __ctHelpers: any; }\nglobalThis.__ctHelpers = 5;", + ]; + + for (const statement of statements) { + await assertRejects(() => + transformFiles({ + "/main.ts": fixture + `\n${statement}`, + }) + ); + } + }); + + it("Allows '__ctHelpers' in comments and in other forms", async () => { + const statements = [ + "var x = 5; // __ctHelpers", + "// __ctHelpers", + "/* __ctHelpers */", + "var __ctHelpers123: number = 5;", + "declare global {\nvar __ctHelpers1: any;\n}\nglobalThis.__ctHelpers1 = 5;", + ]; + for (const statement of statements) { + await transformFiles({ + "/main.ts": fixture + `\n${statement}`, + }); + } + }); +});