@ 261027.7 · fetching
current
const { test, snapshot } = require("node:test");
const { transformSync } = require("../dist/index.js");
const path = require("node:path");
const assert = require("node:assert");
const vm = require("node:vm");

// Set the path for the snapshots directory
snapshot.setResolveSnapshotPath((testPath) => {
	return path.join(
		__dirname,
		"snapshots",
		`${path.basename(testPath)}.snapshot`,
	);
});

test("should perform transformation without source maps", (t) => {
	const tsCode = `
    enum Foo {
        Bar = 7,
        Baz = 2,
    }
    output = Foo.Bar`;

	const { code, map } = transformSync(tsCode, {
		mode: "transform",
	});

	t.assert.snapshot(code);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, 7);
	assert.strictEqual(map, undefined);
});

test("should perform transformation without source maps and filename", (t) => {
	const tsCode = `
    enum Foo {
        Bar = 7,
        Baz = 2,
    }
    output = Foo.Bar`;

	const { code, map } = transformSync(tsCode, {
		mode: "transform",
		filename: "foo.ts",
	});

	t.assert.snapshot(code);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, 7);
	assert.strictEqual(map, undefined);
});

test("should perform transformation with source maps", (t) => {
	const tsCode = `
    enum Foo {
        Bar = 7,
        Baz = 2,
    }

    output = Foo.Bar`;
	const { code, map } = transformSync(tsCode, {
		mode: "transform",
		sourceMap: true,
		filename: "foo.ts",
	});

	t.assert.snapshot(code);
	t.assert.snapshot(map);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, 7);
});

test("should perform transformation with source maps with uncommon chars", (t) => {
	const tsCode = `
    enum Foo {
        Bar = 7,
        Baz = 2,
    }

    output = Foo.Bar`;
	const { code, map } = transformSync(tsCode, {
		mode: "transform",
		sourceMap: true,
		filename: "dir%20with $unusual\"chars?'åß∂ƒ©∆¬…`.cts",
	});

	t.assert.snapshot(code);
	t.assert.snapshot(map);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, 7);
});

test("should perform transformation with source maps no filename", (t) => {
	const tsCode = `
    enum Foo {
        Bar = 7,
        Baz = 2,
    }
    output = Foo.Bar`;

	const { code, map } = transformSync(tsCode, {
		mode: "transform",
		sourceMap: true,
	});

	t.assert.snapshot(code);
	t.assert.snapshot(map);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, 7);
});

test("should perform transformation with error", (t) => {
	const tsCode = `
    enum Foo {
        Bar = 7,
        Baz = 2,
    }
    output = Foo.Bar;
    throw new Error("foo");`;

	const { code, map } = transformSync(tsCode, {
		mode: "transform",
		sourceMap: true,
		filename: "foo.ts",
	});

	t.assert.snapshot(code);
	t.assert.snapshot(map);
	const context = {};
	try {
		assert.throws(vm.runInContext(code, vm.createContext(context)));
	} catch (error) {
		assert.strictEqual(error.message, "foo");
	}
	assert.strictEqual(context.output, 7);
});

test("should transform TypeScript class fields", (t) => {
	const inputCode = `
    class Counter {
      count: number = 0;
      increment() {
        this.count++;
      }
    }

	const counter = new Counter();
    counter.increment();
    output = counter.count;`;

	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});

	t.assert.snapshot(code);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, 1);
});

test("should transform TypeScript private class fields", (t) => {
	const inputCode = `
    class Counter {
      #count: number = 0;
      increment() {
        this.#count++;
      }
      getCount(): number {
        return this.#count;
      }
    }

	const counter = new Counter();
    counter.increment();
    output = counter.getCount();`;

	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});

	t.assert.snapshot(code);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, 1);
});

test("should transform TypeScript type annotations and type guards", (t) => {
	const inputCode = `
    function isString(value: unknown): value is string {
      return typeof value === 'string';
    }
	const check = isString("hello");
    output = check;`;

	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMaps: true,
	});

	t.assert.snapshot(code);
	const result = vm.runInContext(code, vm.createContext());
	assert.strictEqual(result, true);
});

test("should not transform TypeScript class decorators with multiple decorators", (t) => {
	const inputCode = `
	@sealed
	@log
	class BugReport {
		type = "report";
		title: string;
		constructor(t: string) {
		this.title = t;
		}
	}

	function sealed(constructor: Function) {
		Object.seal(constructor);
		Object.seal(constructor.prototype);
	}

	function log(constructor: Function) {
		console.log("Creating instance of", constructor.name);
	}

	output = new BugReport("Test");`;

	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});

	t.assert.snapshot(code);
	const context = {};
	try {
		assert.throws(vm.runInContext(code, vm.createContext(context)));
	} catch (error) {
		assert.strictEqual(error.message, "Invalid or unexpected token");
	}
});

test("should transform TypeScript namespaces with additional functionality", (t) => {
	const inputCode = `
	namespace Validation {
		export interface StringValidator {
			isAcceptable(s: string): boolean;
		}
		const lettersRegexp = /^[A-Za-z]+$/;
		export class LettersOnlyValidator implements StringValidator {
			isAcceptable(s: string) {
				return lettersRegexp.test(s);
			}
			static createValidator(): LettersOnlyValidator {
				return new LettersOnlyValidator();
			}
		}
	}
	const validator = Validation.LettersOnlyValidator.createValidator();
	const isValid = validator.isAcceptable("test");
	output = {
		validator,
		isValid
	}`;

	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});

	t.assert.snapshot(code);
	const result = vm.runInContext(code, vm.createContext());
	assert.ok(result.validator);
	assert.strictEqual(result.isValid, true);
});

test("test native class properties", (t) => {
	const inputCode = `
	class Foo {
		x = console.log(1)
		constructor(public y = console.log(2)) {
			console.log(3)
		}
	}`;
	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});
	t.assert.snapshot(code);
});

test("test noEmptyExport", (t) => {
	const inputCode = `
	import fs = require("fs");
	`;
	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});
	t.assert.snapshot(code);
});

test("should not polyfill using Symbol.Dispose", (t) => {
	const inputCode = `
	using r = 0;`;
	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});
	t.assert.snapshot(code);
});

test("should not polyfill using Symbol.asyncDispose", (t) => {
	const inputCode = `
	await using r = 0;`;
	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});
	t.assert.snapshot(code);
});

test("should have proper error code", (t) => {
	const inputCode = "module F { export type x = number }";
	try {
		transformSync(inputCode, {
			mode: "transform",
		});
	} catch (error) {
		t.assert.snapshot(error);
		assert.strictEqual(error.code, "UnsupportedSyntax");
	}
});

test("should have proper error code", (t) => {
	const inputCode = "const foo;";
	try {
		transformSync(inputCode, {
			mode: "transform",
		});
	} catch (error) {
		t.assert.snapshot(error);
		assert.strictEqual(error.code, "InvalidSyntax");
	}
});

test("should handle short-circuit assignments (&&=, ||=, ??=)", (t) => {
	const inputCode = `
		const a = {}

		a.foo &&= \`\${a.foo}1\`
		console.log(a.foo)

		const b = {}

		b.foo ||= \`\${b.foo}1\`
		console.log(b.foo)

		const c = {}

		c.foo ??= \`\${c.foo}1\`
		console.log(c.foo)
	`;

	const { code } = transformSync(inputCode, {
		mode: "transform",
		sourceMap: true,
	});
	t.assert.snapshot(code);
});