From 1b58f0a9f98288ad078e752112b826964815186e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 18 Dec 2019 06:38:38 +0000 Subject: [PATCH 01/10] Load plugins from `tsconfig.json` --- src/CompilerOptions.ts | 8 ++- src/cli/parse.ts | 18 ++++--- src/transpilation/diagnostics.ts | 64 ++++++++++-------------- src/transpilation/index.ts | 1 + src/transpilation/plugins.ts | 36 +++++++++++--- src/transpilation/transformers.ts | 81 +++++++++---------------------- src/transpilation/transpile.ts | 8 +-- src/transpilation/utils.ts | 53 ++++++++++++++++++++ 8 files changed, 152 insertions(+), 117 deletions(-) create mode 100644 src/transpilation/utils.ts diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index be3aa09b4..5b069903f 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -17,6 +17,11 @@ export interface TransformerImport { [option: string]: any; } +export interface LuaPluginImport { + plugin: string; + import?: string; +} + export type CompilerOptions = OmitIndexSignature & { noImplicitSelf?: boolean; noHeader?: boolean; @@ -26,8 +31,9 @@ export type CompilerOptions = OmitIndexSignature & { luaLibImport?: LuaLibImportKind; noHoisting?: boolean; sourceMapTraceback?: boolean; + luaPlugins?: LuaPluginImport[]; plugins?: Array; - [option: string]: ts.CompilerOptions[string] | Array; + [option: string]: ts.CompilerOptions[string] | LuaPluginImport[] | Array; }; export enum LuaLibImportKind { diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 40f564268..0ebeaf7ed 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -17,15 +17,11 @@ interface CommandLineOptionOfEnum extends CommandLineOptionBase { choices: string[]; } -interface CommandLineOptionOfBoolean extends CommandLineOptionBase { - type: "boolean"; +interface CommandLineOptionOfPrimitive extends CommandLineOptionBase { + type: "boolean" | "string" | "object"; } -interface CommandLineOptionOfString extends CommandLineOptionBase { - type: "string"; -} - -type CommandLineOption = CommandLineOptionOfEnum | CommandLineOptionOfBoolean | CommandLineOptionOfString; +type CommandLineOption = CommandLineOptionOfEnum | CommandLineOptionOfPrimitive; export const optionDeclarations: CommandLineOption[] = [ { @@ -71,6 +67,11 @@ export const optionDeclarations: CommandLineOption[] = [ description: "Applies the source map to show source TS files and lines in error tracebacks.", type: "boolean", }, + { + name: "luaPlugins", + description: "TODO", + type: "object", + }, ]; export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): ParsedCommandLine { @@ -178,8 +179,9 @@ function readValue(option: CommandLineOption, value: unknown): ReadValueResult { if (value === null) return { value }; switch (option.type) { + case "boolean": case "string": - case "boolean": { + case "object": { if (typeof value !== option.type) { return { value: undefined, diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index 3383c3cfd..18b5a716f 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -1,51 +1,37 @@ import * as ts from "typescript"; -export const toLoadTransformerItShouldBeTranspiled = (transform: string): ts.Diagnostic => ({ +const createDiagnostic = (messageText: string, category = ts.DiagnosticCategory.Error): ts.Diagnostic => ({ file: undefined, start: undefined, length: undefined, - category: ts.DiagnosticCategory.Error, + category, code: 0, source: "typescript-to-lua", - messageText: `To load "${transform}" transformer it should be transpiled or "ts-node" should be installed`, + messageText, }); -export const couldNotResolveTransformerFrom = (transform: string, base: string): ts.Diagnostic => ({ - file: undefined, - start: undefined, - length: undefined, - category: ts.DiagnosticCategory.Error, - code: 0, - source: "typescript-to-lua", - messageText: `Could not resolve "${transform}" transformer from "${base}".`, -}); +export const toLoadPluginItShouldBeTranspiled = (transform: string) => + createDiagnostic(`To load "${transform}" plugin it should be transpiled or "ts-node" should be installed`); -export const transformerShouldHaveAExport = (transform: string, importName: string): ts.Diagnostic => ({ - file: undefined, - start: undefined, - length: undefined, - category: ts.DiagnosticCategory.Error, - code: 0, - source: "typescript-to-lua", - messageText: `"${transform}" transformer should have a "${importName}" export`, -}); +export const couldNotResolvePluginFrom = (transform: string, base: string) => + createDiagnostic(`Could not resolve "${transform}" plugin from "${base}".`); -export const transformerShouldBeATsTransformerFactory = (transform: string): ts.Diagnostic => ({ - file: undefined, - start: undefined, - length: undefined, - category: ts.DiagnosticCategory.Error, - code: 0, - source: "typescript-to-lua", - messageText: `"${transform}" transformer should be a ts.TransformerFactory or an object with ts.TransformerFactory values`, -}); +export const pluginShouldHaveAExport = (transform: string, importName: string) => + createDiagnostic(`"${transform}" plugin should have a "${importName}" export`); -export const couldNotFindBundleEntryPoint = (entryPoint: string): ts.Diagnostic => ({ - file: undefined, - start: undefined, - length: undefined, - category: ts.DiagnosticCategory.Error, - code: 0, - source: "typescript-to-lua", - messageText: `Could not find bundle entry point '${entryPoint}'. It should be a file in the project.`, -}); +export const toLoadTransformerItShouldBeTranspiled = (transform: string) => + createDiagnostic(`To load "${transform}" transformer it should be transpiled or "ts-node" should be installed`); + +export const couldNotResolveTransformerFrom = (transform: string, base: string) => + createDiagnostic(`Could not resolve "${transform}" transformer from "${base}".`); + +export const transformerShouldHaveAExport = (transform: string, importName: string) => + createDiagnostic(`"${transform}" transformer should have a "${importName}" export`); + +export const transformerShouldBeATsTransformerFactory = (transform: string) => + createDiagnostic( + `"${transform}" transformer should be a ts.TransformerFactory or an object with ts.TransformerFactory values` + ); + +export const couldNotFindBundleEntryPoint = (entryPoint: string) => + createDiagnostic(`Could not find bundle entry point '${entryPoint}'. It should be a file in the project.`); diff --git a/src/transpilation/index.ts b/src/transpilation/index.ts index 530fbdd4c..1d15e59c4 100644 --- a/src/transpilation/index.ts +++ b/src/transpilation/index.ts @@ -7,6 +7,7 @@ import { emitTranspiledFiles, OutputFile } from "./emit"; import { transpile, TranspiledFile, TranspileResult } from "./transpile"; export * from "./emit"; +export { Plugin } from "./plugins"; export * from "./transpile"; export interface TranspileFilesResult { diff --git a/src/transpilation/plugins.ts b/src/transpilation/plugins.ts index 8731211d6..999188c16 100644 --- a/src/transpilation/plugins.ts +++ b/src/transpilation/plugins.ts @@ -1,6 +1,9 @@ import * as ts from "typescript"; +import { CompilerOptions } from "../CompilerOptions"; import { Printer } from "../LuaPrinter"; import { Visitors } from "../transformation/context"; +import * as diagnosticFactories from "./diagnostics"; +import { getConfigDirectory, resolvePlugin } from "./utils"; export interface Plugin { /** @@ -18,10 +21,31 @@ export interface Plugin { printer?: Printer; } -export function getPlugins( - _program: ts.Program, - _diagnostics: ts.Diagnostic[], - pluginsFromOptions: Plugin[] -): Plugin[] { - return pluginsFromOptions; +export function getPlugins(program: ts.Program, diagnostics: ts.Diagnostic[], customPlugins: Plugin[]): Plugin[] { + const pluginsFromOptions: Plugin[] = []; + const options = program.getCompilerOptions() as CompilerOptions; + + for (const [index, pluginOption] of (options.luaPlugins ?? []).entries()) { + const optionName = `tstl.luaPlugins[${index}]`; + + const { error: resolveError, result: factory } = resolvePlugin( + { + couldNotResolveFrom: diagnosticFactories.couldNotResolvePluginFrom, + toLoadItShouldBeTranspiled: diagnosticFactories.toLoadPluginItShouldBeTranspiled, + shouldHaveAExport: diagnosticFactories.pluginShouldHaveAExport, + }, + getConfigDirectory(options), + `${optionName}.plugin`, + pluginOption.plugin, + pluginOption.import + ); + + if (resolveError) diagnostics.push(resolveError); + if (factory === undefined) continue; + + const plugin = typeof factory === "function" ? factory(pluginOption) : factory; + pluginsFromOptions.push(plugin); + } + + return [...customPlugins, ...pluginsFromOptions]; } diff --git a/src/transpilation/transformers.ts b/src/transpilation/transformers.ts index 71371e208..ee93360e8 100644 --- a/src/transpilation/transformers.ts +++ b/src/transpilation/transformers.ts @@ -1,10 +1,9 @@ -import * as path from "path"; -import * as resolve from "resolve"; import * as ts from "typescript"; // TODO: Don't depend on CLI? import * as cliDiagnostics from "../cli/diagnostics"; import { CompilerOptions, TransformerImport } from "../CompilerOptions"; import * as diagnosticFactories from "./diagnostics"; +import { getConfigDirectory, resolvePlugin } from "./utils"; export const noImplicitSelfTransformer: ts.TransformerFactory = () => node => { const transformSourceFile: ts.Transformer = node => { @@ -18,7 +17,7 @@ export const noImplicitSelfTransformer: ts.TransformerFactory = { before: [], after: [], @@ -64,19 +63,27 @@ function loadTransformersFromOptions(program: ts.Program, allDiagnostics: ts.Dia const options = program.getCompilerOptions() as CompilerOptions; if (!options.plugins) return customTransformers; - const configFileName = options.configFilePath as string | undefined; - const basedir = configFileName ? path.dirname(configFileName) : process.cwd(); - for (const [index, transformerImport] of options.plugins.entries()) { if (!("transform" in transformerImport)) continue; const optionName = `compilerOptions.plugins[${index}]`; - const { error: resolveError, factory } = resolveTransformerFactory(basedir, optionName, transformerImport); - if (resolveError) allDiagnostics.push(resolveError); + const { error: resolveError, result: factory } = resolvePlugin( + { + couldNotResolveFrom: diagnosticFactories.couldNotResolveTransformerFrom, + toLoadItShouldBeTranspiled: diagnosticFactories.toLoadTransformerItShouldBeTranspiled, + shouldHaveAExport: diagnosticFactories.transformerShouldHaveAExport, + }, + getConfigDirectory(options), + `${optionName}.transform`, + transformerImport.transform, + transformerImport.import + ); + + if (resolveError) diagnostics.push(resolveError); if (factory === undefined) continue; const { error: loadError, transformer } = loadTransformer(optionName, program, factory, transformerImport); - if (loadError) allDiagnostics.push(loadError); + if (loadError) diagnostics.push(loadError); if (transformer === undefined) continue; if (transformer.before) { @@ -103,12 +110,6 @@ type CompilerOptionsTransformerFactory = ( ) => Transformer; type TypeCheckerTransformerFactory = (typeChecker: ts.TypeChecker, options: Record) => Transformer; type RawTransformerFactory = Transformer; -type TransformerFactory = - | ProgramTransformerFactory - | ConfigTransformerFactory - | CompilerOptionsTransformerFactory - | TypeCheckerTransformerFactory - | RawTransformerFactory; type Transformer = GroupTransformer | ts.TransformerFactory; interface GroupTransformer { @@ -117,48 +118,10 @@ interface GroupTransformer { afterDeclarations?: ts.TransformerFactory; } -function resolveTransformerFactory( - basedir: string, - transformerOptionPath: string, - { transform, import: importName = "default" }: TransformerImport -): { error?: ts.Diagnostic; factory?: TransformerFactory } { - if (typeof transform !== "string") { - const optionName = `${transformerOptionPath}.transform`; - return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "string") }; - } - - let resolved: string; - try { - resolved = resolve.sync(transform, { basedir, extensions: [".js", ".ts", ".tsx"] }); - } catch (err) { - if (err.code !== "MODULE_NOT_FOUND") throw err; - return { error: diagnosticFactories.couldNotResolveTransformerFrom(transform, basedir) }; - } - - // tslint:disable-next-line: deprecation - const hasNoRequireHook = require.extensions[".ts"] === undefined; - if (hasNoRequireHook && (resolved.endsWith(".ts") || resolved.endsWith(".tsx"))) { - try { - const tsNode: typeof import("ts-node") = require("ts-node"); - tsNode.register({ transpileOnly: true }); - } catch (err) { - if (err.code !== "MODULE_NOT_FOUND") throw err; - return { error: diagnosticFactories.toLoadTransformerItShouldBeTranspiled(transform) }; - } - } - - const factory: TransformerFactory = require(resolved)[importName]; - if (factory === undefined) { - return { error: diagnosticFactories.transformerShouldHaveAExport(transform, importName) }; - } - - return { factory }; -} - function loadTransformer( - transformerOptionPath: string, + optionPath: string, program: ts.Program, - factory: TransformerFactory, + factory: unknown, { transform, after = false, afterDeclarations = false, type = "program", ...extraOptions }: TransformerImport ): { error?: ts.Diagnostic; transformer?: GroupTransformer } { let transformer: Transformer; @@ -179,18 +142,18 @@ function loadTransformer( transformer = (factory as CompilerOptionsTransformerFactory)(program.getCompilerOptions(), extraOptions); break; default: { - const optionName = `--${transformerOptionPath}.type`; + const optionName = `--${optionPath}.type`; return { error: cliDiagnostics.argumentForOptionMustBe(optionName, "program") }; } } if (typeof after !== "boolean") { - const optionName = `${transformerOptionPath}.after`; + const optionName = `${optionPath}.after`; return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "boolean") }; } if (typeof afterDeclarations !== "boolean") { - const optionName = `${transformerOptionPath}.afterDeclarations`; + const optionName = `${optionPath}.afterDeclarations`; return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "boolean") }; } diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index df8214718..9046477e8 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -7,7 +7,7 @@ import { createVisitorMap, transformSourceFile } from "../transformation"; import { isNonNull } from "../utils"; import { bundleTranspiledFiles } from "./bundle"; import { getPlugins, Plugin } from "./plugins"; -import { getCustomTransformers } from "./transformers"; +import { getTransformers } from "./transformers"; export interface TranspiledFile { fileName: string; @@ -42,7 +42,7 @@ export function transpile({ program, sourceFiles: targetSourceFiles, customTransformers = {}, - plugins: pluginsFromOptions = [], + plugins: customPlugins = [], emitHost = ts.sys, }: TranspileOptions): TranspileResult { const options = program.getCompilerOptions() as CompilerOptions; @@ -85,7 +85,7 @@ export function transpile({ } } - const plugins = getPlugins(program, diagnostics, pluginsFromOptions); + const plugins = getPlugins(program, diagnostics, customPlugins); const visitorMap = createVisitorMap(plugins.map(p => p.visitors).filter(isNonNull)); const printer = createPrinter(plugins.map(p => p.printer).filter(isNonNull)); const processSourceFile = (sourceFile: ts.SourceFile) => { @@ -107,7 +107,7 @@ export function transpile({ } }; - const transformers = getCustomTransformers(program, diagnostics, customTransformers, processSourceFile); + const transformers = getTransformers(program, diagnostics, customTransformers, processSourceFile); const writeFile: ts.WriteFileCallback = (fileName, data, _bom, _onError, sourceFiles = []) => { for (const sourceFile of sourceFiles) { diff --git a/src/transpilation/utils.ts b/src/transpilation/utils.ts new file mode 100644 index 000000000..35a705ea5 --- /dev/null +++ b/src/transpilation/utils.ts @@ -0,0 +1,53 @@ +import * as resolve from "resolve"; +import * as ts from "typescript"; +import * as path from "path"; +// TODO: Don't depend on CLI? +import * as cliDiagnostics from "../cli/diagnostics"; + +export const getConfigDirectory = (options: ts.CompilerOptions) => + options.configFilePath ? path.dirname(options.configFilePath) : process.cwd(); + +interface ResolvePluginDiagnostics { + couldNotResolveFrom(query: string, base: string): ts.Diagnostic; + toLoadItShouldBeTranspiled(query: string): ts.Diagnostic; + shouldHaveAExport(query: string, importName: string): ts.Diagnostic; +} + +export function resolvePlugin( + diagnostics: ResolvePluginDiagnostics, + basedir: string, + optionName: string, + query: string, + importName = "default" +): { error?: ts.Diagnostic; result?: unknown } { + if (typeof query !== "string") { + return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "string") }; + } + + let resolved: string; + try { + resolved = resolve.sync(query, { basedir, extensions: [".js", ".ts", ".tsx"] }); + } catch (err) { + if (err.code !== "MODULE_NOT_FOUND") throw err; + return { error: diagnostics.couldNotResolveFrom(query, basedir) }; + } + + // tslint:disable-next-line: deprecation + const hasNoRequireHook = require.extensions[".ts"] === undefined; + if (hasNoRequireHook && (resolved.endsWith(".ts") || resolved.endsWith(".tsx"))) { + try { + const tsNode: typeof import("ts-node") = require("ts-node"); + tsNode.register({ transpileOnly: true }); + } catch (err) { + if (err.code !== "MODULE_NOT_FOUND") throw err; + return { error: diagnostics.toLoadItShouldBeTranspiled(query) }; + } + } + + const result = require(resolved)[importName]; + if (result === undefined) { + return { error: diagnostics.shouldHaveAExport(query, importName) }; + } + + return { result }; +} From d1a02914c0b55accdfaa59ab5a12255501bc13fc Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 18 Dec 2019 06:42:42 +0000 Subject: [PATCH 02/10] Refactor `transformers.spec.ts` test --- .../transformers/import.ts | 0 .../transformers/resolve.js | 0 .../transformers/resolve.ts | 0 .../transformers/transformers.spec.ts | 67 ++++++++++++++ .../{unit => transpile}/transformers/types.ts | 18 ++-- .../{unit => transpile}/transformers/utils.ts | 0 test/unit/transformers/transformers.spec.ts | 87 ------------------- 7 files changed, 75 insertions(+), 97 deletions(-) rename test/{unit => transpile}/transformers/import.ts (100%) rename test/{unit => transpile}/transformers/resolve.js (100%) rename test/{unit => transpile}/transformers/resolve.ts (100%) create mode 100644 test/transpile/transformers/transformers.spec.ts rename test/{unit => transpile}/transformers/types.ts (73%) rename test/{unit => transpile}/transformers/utils.ts (100%) delete mode 100644 test/unit/transformers/transformers.spec.ts diff --git a/test/unit/transformers/import.ts b/test/transpile/transformers/import.ts similarity index 100% rename from test/unit/transformers/import.ts rename to test/transpile/transformers/import.ts diff --git a/test/unit/transformers/resolve.js b/test/transpile/transformers/resolve.js similarity index 100% rename from test/unit/transformers/resolve.js rename to test/transpile/transformers/resolve.js diff --git a/test/unit/transformers/resolve.ts b/test/transpile/transformers/resolve.ts similarity index 100% rename from test/unit/transformers/resolve.ts rename to test/transpile/transformers/resolve.ts diff --git a/test/transpile/transformers/transformers.spec.ts b/test/transpile/transformers/transformers.spec.ts new file mode 100644 index 000000000..fb72a7a93 --- /dev/null +++ b/test/transpile/transformers/transformers.spec.ts @@ -0,0 +1,67 @@ +import * as path from "path"; +import * as tstl from "../../../src"; +import * as util from "../../util"; + +const optionsOfTransformer = (transformer: tstl.TransformerImport): tstl.CompilerOptions => ({ + plugins: [transformer], +}); + +test("should ignore language service plugins", () => { + util.testFunction` + return; + ` + .setOptions({ plugins: [{ name: path.join(__dirname, "resolve.ts") }] }) + .expectToEqual(undefined); +}); + +describe("resolution", () => { + const testTransform = (transformer: tstl.TransformerImport) => { + util.testFunction` + return; + ` + .setOptions(optionsOfTransformer(transformer)) + .expectToEqual(true); + }; + + test("resolve relative transformer paths", () => { + jest.spyOn(process, "cwd").mockReturnValue(__dirname); + testTransform({ transform: "./resolve.ts" }); + }); + + test("load .js transformers", () => { + testTransform({ transform: path.join(__dirname, "resolve.js") }); + }); + + test("load .ts transformers", () => { + testTransform({ transform: path.join(__dirname, "resolve.ts") }); + }); + + test('"import" option', () => { + testTransform({ transform: path.join(__dirname, "import.ts"), import: "transformer" }); + }); + + test("error if transformer could not be resolved", () => { + util.testModule`` + .setOptions(optionsOfTransformer({ transform: path.join(__dirname, "error.ts") })) + .expectToHaveDiagnostics(); + }); +}); + +describe("factory types", () => { + for (const type of ["program", "config", "checker", "raw", "compilerOptions"] as const) { + test(type, () => { + const options = optionsOfTransformer({ + transform: path.join(__dirname, "types.ts"), + type, + import: type, + value: true, + }); + + util.testFunction` + return false; + ` + .setOptions(options) + .expectToEqual(true); + }); + } +}); diff --git a/test/unit/transformers/types.ts b/test/transpile/transformers/types.ts similarity index 73% rename from test/unit/transformers/types.ts rename to test/transpile/transformers/types.ts index dc8ecf71c..c998d6efc 100644 --- a/test/unit/transformers/types.ts +++ b/test/transpile/transformers/types.ts @@ -1,3 +1,4 @@ +import * as assert from "assert"; import * as ts from "typescript"; import * as tstl from "../../../src"; import { visitAndReplace } from "./utils"; @@ -7,7 +8,7 @@ export const program = (program: ts.Program, options: { value: any }): ts.Transf export const config = ({ value }: { value: any }): ts.TransformerFactory => context => file => visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node) || node.expression) return; + if (!ts.isReturnStatement(node)) return; return ts.updateReturn(node, ts.createLiteral(value)); }); @@ -24,19 +25,16 @@ export const checker = ( export const raw: ts.TransformerFactory = context => file => visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node) || node.expression) return; + if (!ts.isReturnStatement(node)) return; return ts.updateReturn(node, ts.createLiteral(true)); }); -export const compilerOptions = ({ - luaTarget, -}: tstl.CompilerOptions): ts.TransformerFactory => context => file => { - if (luaTarget !== tstl.LuaTarget.LuaJIT) { - throw new Error("Transformer supports only LuaJIT target"); - } - +export const compilerOptions = ( + options: tstl.CompilerOptions +): ts.TransformerFactory => context => file => { + assert(options.plugins?.length === 1); return visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node) || node.expression) return; + if (!ts.isReturnStatement(node)) return; return ts.updateReturn(node, ts.createLiteral(true)); }); }; diff --git a/test/unit/transformers/utils.ts b/test/transpile/transformers/utils.ts similarity index 100% rename from test/unit/transformers/utils.ts rename to test/transpile/transformers/utils.ts diff --git a/test/unit/transformers/transformers.spec.ts b/test/unit/transformers/transformers.spec.ts deleted file mode 100644 index db36c27dc..000000000 --- a/test/unit/transformers/transformers.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as path from "path"; -import * as tstl from "../../../src"; -import * as util from "../../util"; - -const optionsOfTransformer = (transformer: tstl.TransformerImport): tstl.CompilerOptions => ({ - plugins: [transformer], -}); - -test("should ignore language service plugins", () => { - const options: tstl.CompilerOptions = { - plugins: [{ name: path.join(__dirname, "resolve.ts") }], - }; - - expect(util.transpileAndExecute("return", options)).toBe(undefined); -}); - -describe("resolution", () => { - const testTransform = (transformer: tstl.TransformerImport) => { - const options = optionsOfTransformer(transformer); - expect(util.transpileAndExecute("return", options)).toBe(true); - }; - - test("should resolve relative transformer paths", () => { - jest.spyOn(process, "cwd").mockReturnValue(__dirname); - testTransform({ transform: "./resolve.ts" }); - }); - - test("should load js transformers", () => { - testTransform({ transform: path.join(__dirname, "resolve.js") }); - }); - - test("should load ts transformers", () => { - testTransform({ transform: path.join(__dirname, "resolve.ts") }); - }); - - test('should support "import" option', () => { - testTransform({ - transform: path.join(__dirname, "import.ts"), - import: "transformer", - }); - }); - - test("should error if transformer could not be resolved", () => { - const transform = path.join(__dirname, "error.ts"); - const options = optionsOfTransformer({ transform }); - const { diagnostics } = util.transpileStringResult("", options); - expect(diagnostics).toHaveDiagnostics(); - }); -}); - -describe("factory types", () => { - const value = "foo"; - const getOptions = (options: Partial) => - optionsOfTransformer({ - transform: path.join(__dirname, "types.ts"), - ...options, - }); - - test('should support "program" type', () => { - const options = getOptions({ type: "program", import: "program", value }); - expect(util.transpileAndExecute("return false", options)).toBe(value); - }); - - test('should support "config" type', () => { - const options = getOptions({ type: "config", import: "config", value }); - expect(util.transpileAndExecute("return", options)).toBe(value); - }); - - test('should support "checker" type', () => { - const options = getOptions({ type: "checker", import: "checker", value }); - expect(util.transpileAndExecute("return false", options)).toBe(value); - }); - - test('should support "raw" type', () => { - const options = getOptions({ type: "raw", import: "raw" }); - expect(util.transpileAndExecute("return", options)).toBe(true); - }); - - test('should support "compilerOptions" type', () => { - const options: tstl.CompilerOptions = { - ...getOptions({ type: "compilerOptions", import: "compilerOptions" }), - luaTarget: tstl.LuaTarget.LuaJIT, - }; - - expect(util.transpileAndExecute("return", options)).toBe(true); - }); -}); From 9d20e413e06dcaa8a04f156677ce7b81ab988ab2 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 18 Dec 2019 07:08:14 +0000 Subject: [PATCH 03/10] Add few basic tests --- test/transpile/plugins/plugins.spec.ts | 24 +++++++++++++++++++ test/transpile/plugins/printer.ts | 12 ++++++++++ test/transpile/plugins/visitor-super.ts | 19 +++++++++++++++ test/transpile/plugins/visitor.ts | 11 +++++++++ .../transformers/transformers.spec.ts | 19 ++++----------- 5 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 test/transpile/plugins/plugins.spec.ts create mode 100644 test/transpile/plugins/printer.ts create mode 100644 test/transpile/plugins/visitor-super.ts create mode 100644 test/transpile/plugins/visitor.ts diff --git a/test/transpile/plugins/plugins.spec.ts b/test/transpile/plugins/plugins.spec.ts new file mode 100644 index 000000000..81cb6db44 --- /dev/null +++ b/test/transpile/plugins/plugins.spec.ts @@ -0,0 +1,24 @@ +import * as path from "path"; +import * as util from "../../util"; + +test("printer", () => { + util.testModule`` + .setOptions({ luaPlugins: [{ plugin: path.join(__dirname, "printer.ts") }] }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).toMatch("Plugin")); +}); + +test("visitor", () => { + util.testFunction` + return false; + ` + .setOptions({ luaPlugins: [{ plugin: path.join(__dirname, "visitor.ts") }] }) + .expectToEqual(true); +}); + +test("visitor using super", () => { + util.testFunction` + return "foo"; + ` + .setOptions({ luaPlugins: [{ plugin: path.join(__dirname, "visitor-super.ts") }] }) + .expectToEqual("bar"); +}); diff --git a/test/transpile/plugins/printer.ts b/test/transpile/plugins/printer.ts new file mode 100644 index 000000000..771f0f4a6 --- /dev/null +++ b/test/transpile/plugins/printer.ts @@ -0,0 +1,12 @@ +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + printer: (program, emitHost, fileName, ...args) => { + const result = new tstl.LuaPrinter(program.getCompilerOptions(), emitHost, fileName).print(...args); + result.code = `-- Plugin\n${result.code}`; + return result; + }, +}; + +// tslint:disable-next-line: no-default-export +export default plugin; diff --git a/test/transpile/plugins/visitor-super.ts b/test/transpile/plugins/visitor-super.ts new file mode 100644 index 000000000..87f665749 --- /dev/null +++ b/test/transpile/plugins/visitor-super.ts @@ -0,0 +1,19 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + visitors: { + [ts.SyntaxKind.StringLiteral]: (node, context) => { + const result = context.superTransformExpression(node); + + if (tstl.isStringLiteral(result)) { + result.value = "bar"; + } + + return result; + }, + }, +}; + +// tslint:disable-next-line: no-default-export +export default plugin; diff --git a/test/transpile/plugins/visitor.ts b/test/transpile/plugins/visitor.ts new file mode 100644 index 000000000..fdbf50782 --- /dev/null +++ b/test/transpile/plugins/visitor.ts @@ -0,0 +1,11 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + visitors: { + [ts.SyntaxKind.ReturnStatement]: () => tstl.createReturnStatement([tstl.createBooleanLiteral(true)]), + }, +}; + +// tslint:disable-next-line: no-default-export +export default plugin; diff --git a/test/transpile/transformers/transformers.spec.ts b/test/transpile/transformers/transformers.spec.ts index fb72a7a93..b890d1477 100644 --- a/test/transpile/transformers/transformers.spec.ts +++ b/test/transpile/transformers/transformers.spec.ts @@ -2,10 +2,6 @@ import * as path from "path"; import * as tstl from "../../../src"; import * as util from "../../util"; -const optionsOfTransformer = (transformer: tstl.TransformerImport): tstl.CompilerOptions => ({ - plugins: [transformer], -}); - test("should ignore language service plugins", () => { util.testFunction` return; @@ -19,7 +15,7 @@ describe("resolution", () => { util.testFunction` return; ` - .setOptions(optionsOfTransformer(transformer)) + .setOptions({ plugins: [transformer] }) .expectToEqual(true); }; @@ -42,7 +38,7 @@ describe("resolution", () => { test("error if transformer could not be resolved", () => { util.testModule`` - .setOptions(optionsOfTransformer({ transform: path.join(__dirname, "error.ts") })) + .setOptions({ plugins: [{ transform: path.join(__dirname, "error.ts") }] }) .expectToHaveDiagnostics(); }); }); @@ -50,17 +46,12 @@ describe("resolution", () => { describe("factory types", () => { for (const type of ["program", "config", "checker", "raw", "compilerOptions"] as const) { test(type, () => { - const options = optionsOfTransformer({ - transform: path.join(__dirname, "types.ts"), - type, - import: type, - value: true, - }); - util.testFunction` return false; ` - .setOptions(options) + .setOptions({ + plugins: [{ transform: path.join(__dirname, "types.ts"), type, import: type, value: true }], + }) .expectToEqual(true); }); } From 2c97f522c816fd9fbe3ba3f514a3f507f451d829 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 23 Feb 2020 20:36:30 +0000 Subject: [PATCH 04/10] Resolve `ts-node` on behalf of the root application --- src/transpilation/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/transpilation/utils.ts b/src/transpilation/utils.ts index 35a705ea5..980561a72 100644 --- a/src/transpilation/utils.ts +++ b/src/transpilation/utils.ts @@ -36,7 +36,8 @@ export function resolvePlugin( const hasNoRequireHook = require.extensions[".ts"] === undefined; if (hasNoRequireHook && (resolved.endsWith(".ts") || resolved.endsWith(".tsx"))) { try { - const tsNode: typeof import("ts-node") = require("ts-node"); + const tsNodePath = resolve.sync("ts-node", { basedir }); + const tsNode: typeof import("ts-node") = require(tsNodePath); tsNode.register({ transpileOnly: true }); } catch (err) { if (err.code !== "MODULE_NOT_FOUND") throw err; From b1069da0a9f51d478509bad0a071fd0ef0983ffb Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 13:44:46 +0000 Subject: [PATCH 05/10] Merge plugin and transformer diagnostics --- src/transpilation/diagnostics.ts | 21 ++++++--------------- src/transpilation/plugins.ts | 8 ++------ src/transpilation/transformers.ts | 8 ++------ src/transpilation/utils.ts | 19 +++++++------------ 4 files changed, 17 insertions(+), 39 deletions(-) diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index 18b5a716f..7c6dfcffd 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -10,23 +10,14 @@ const createDiagnostic = (messageText: string, category = ts.DiagnosticCategory. messageText, }); -export const toLoadPluginItShouldBeTranspiled = (transform: string) => - createDiagnostic(`To load "${transform}" plugin it should be transpiled or "ts-node" should be installed`); +export const toLoadItShouldBeTranspiled = (kind: string, transform: string) => + createDiagnostic(`To load "${transform}" ${kind} it should be transpiled or "ts-node" should be installed`); -export const couldNotResolvePluginFrom = (transform: string, base: string) => - createDiagnostic(`Could not resolve "${transform}" plugin from "${base}".`); +export const couldNotResolveFrom = (kind: string, transform: string, base: string) => + createDiagnostic(`Could not resolve "${transform}" ${kind} from "${base}".`); -export const pluginShouldHaveAExport = (transform: string, importName: string) => - createDiagnostic(`"${transform}" plugin should have a "${importName}" export`); - -export const toLoadTransformerItShouldBeTranspiled = (transform: string) => - createDiagnostic(`To load "${transform}" transformer it should be transpiled or "ts-node" should be installed`); - -export const couldNotResolveTransformerFrom = (transform: string, base: string) => - createDiagnostic(`Could not resolve "${transform}" transformer from "${base}".`); - -export const transformerShouldHaveAExport = (transform: string, importName: string) => - createDiagnostic(`"${transform}" transformer should have a "${importName}" export`); +export const shouldHaveAExport = (kind: string, transform: string, importName: string) => + createDiagnostic(`"${transform}" ${kind} should have a "${importName}" export`); export const transformerShouldBeATsTransformerFactory = (transform: string) => createDiagnostic( diff --git a/src/transpilation/plugins.ts b/src/transpilation/plugins.ts index 999188c16..0958dbd09 100644 --- a/src/transpilation/plugins.ts +++ b/src/transpilation/plugins.ts @@ -29,13 +29,9 @@ export function getPlugins(program: ts.Program, diagnostics: ts.Diagnostic[], cu const optionName = `tstl.luaPlugins[${index}]`; const { error: resolveError, result: factory } = resolvePlugin( - { - couldNotResolveFrom: diagnosticFactories.couldNotResolvePluginFrom, - toLoadItShouldBeTranspiled: diagnosticFactories.toLoadPluginItShouldBeTranspiled, - shouldHaveAExport: diagnosticFactories.pluginShouldHaveAExport, - }, - getConfigDirectory(options), + "plugin", `${optionName}.plugin`, + getConfigDirectory(options), pluginOption.plugin, pluginOption.import ); diff --git a/src/transpilation/transformers.ts b/src/transpilation/transformers.ts index ee93360e8..cefb4dd90 100644 --- a/src/transpilation/transformers.ts +++ b/src/transpilation/transformers.ts @@ -68,13 +68,9 @@ function loadTransformersFromOptions(program: ts.Program, diagnostics: ts.Diagno const optionName = `compilerOptions.plugins[${index}]`; const { error: resolveError, result: factory } = resolvePlugin( - { - couldNotResolveFrom: diagnosticFactories.couldNotResolveTransformerFrom, - toLoadItShouldBeTranspiled: diagnosticFactories.toLoadTransformerItShouldBeTranspiled, - shouldHaveAExport: diagnosticFactories.transformerShouldHaveAExport, - }, - getConfigDirectory(options), + "transformer", `${optionName}.transform`, + getConfigDirectory(options), transformerImport.transform, transformerImport.import ); diff --git a/src/transpilation/utils.ts b/src/transpilation/utils.ts index 980561a72..2e71b7273 100644 --- a/src/transpilation/utils.ts +++ b/src/transpilation/utils.ts @@ -1,22 +1,17 @@ +import * as path from "path"; import * as resolve from "resolve"; import * as ts from "typescript"; -import * as path from "path"; // TODO: Don't depend on CLI? import * as cliDiagnostics from "../cli/diagnostics"; +import * as diagnosticFactories from "./diagnostics"; export const getConfigDirectory = (options: ts.CompilerOptions) => options.configFilePath ? path.dirname(options.configFilePath) : process.cwd(); -interface ResolvePluginDiagnostics { - couldNotResolveFrom(query: string, base: string): ts.Diagnostic; - toLoadItShouldBeTranspiled(query: string): ts.Diagnostic; - shouldHaveAExport(query: string, importName: string): ts.Diagnostic; -} - export function resolvePlugin( - diagnostics: ResolvePluginDiagnostics, - basedir: string, + kind: string, optionName: string, + basedir: string, query: string, importName = "default" ): { error?: ts.Diagnostic; result?: unknown } { @@ -29,7 +24,7 @@ export function resolvePlugin( resolved = resolve.sync(query, { basedir, extensions: [".js", ".ts", ".tsx"] }); } catch (err) { if (err.code !== "MODULE_NOT_FOUND") throw err; - return { error: diagnostics.couldNotResolveFrom(query, basedir) }; + return { error: diagnosticFactories.couldNotResolveFrom(kind, query, basedir) }; } // tslint:disable-next-line: deprecation @@ -41,13 +36,13 @@ export function resolvePlugin( tsNode.register({ transpileOnly: true }); } catch (err) { if (err.code !== "MODULE_NOT_FOUND") throw err; - return { error: diagnostics.toLoadItShouldBeTranspiled(query) }; + return { error: diagnosticFactories.toLoadItShouldBeTranspiled(kind, query) }; } } const result = require(resolved)[importName]; if (result === undefined) { - return { error: diagnostics.shouldHaveAExport(query, importName) }; + return { error: diagnosticFactories.shouldHaveAExport(kind, query, importName) }; } return { result }; From 95895bf846b3fbdd2c06a06d1b46c70f1f99da10 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 13:45:04 +0000 Subject: [PATCH 06/10] Refactor tests --- src/transpilation/plugins.ts | 1 - test/transpile/resolve-plugin/import.ts | 1 + .../resolve-plugin/resolve-plugin.spec.ts | 26 +++++++ test/transpile/resolve-plugin/resolve.js | 1 + test/transpile/resolve-plugin/resolve.ts | 2 + .../transformers/{types.ts => fixtures.ts} | 0 test/transpile/transformers/import.ts | 9 --- test/transpile/transformers/resolve.js | 11 --- test/transpile/transformers/resolve.ts | 9 --- .../transformers/transformers.spec.ts | 70 +++++++------------ 10 files changed, 54 insertions(+), 76 deletions(-) create mode 100644 test/transpile/resolve-plugin/import.ts create mode 100644 test/transpile/resolve-plugin/resolve-plugin.spec.ts create mode 100644 test/transpile/resolve-plugin/resolve.js create mode 100644 test/transpile/resolve-plugin/resolve.ts rename test/transpile/transformers/{types.ts => fixtures.ts} (100%) delete mode 100644 test/transpile/transformers/import.ts delete mode 100644 test/transpile/transformers/resolve.js delete mode 100644 test/transpile/transformers/resolve.ts diff --git a/src/transpilation/plugins.ts b/src/transpilation/plugins.ts index 0958dbd09..cb8f80443 100644 --- a/src/transpilation/plugins.ts +++ b/src/transpilation/plugins.ts @@ -2,7 +2,6 @@ import * as ts from "typescript"; import { CompilerOptions } from "../CompilerOptions"; import { Printer } from "../LuaPrinter"; import { Visitors } from "../transformation/context"; -import * as diagnosticFactories from "./diagnostics"; import { getConfigDirectory, resolvePlugin } from "./utils"; export interface Plugin { diff --git a/test/transpile/resolve-plugin/import.ts b/test/transpile/resolve-plugin/import.ts new file mode 100644 index 000000000..3d831e6ad --- /dev/null +++ b/test/transpile/resolve-plugin/import.ts @@ -0,0 +1 @@ +export const named = true; diff --git a/test/transpile/resolve-plugin/resolve-plugin.spec.ts b/test/transpile/resolve-plugin/resolve-plugin.spec.ts new file mode 100644 index 000000000..b58b07e89 --- /dev/null +++ b/test/transpile/resolve-plugin/resolve-plugin.spec.ts @@ -0,0 +1,26 @@ +import * as path from "path"; +import { resolvePlugin } from "../../../src/transpilation/utils"; + +test("resolve relative module paths", () => { + expect(resolvePlugin("test", "test", __dirname, "./resolve.ts")).toMatchObject({ + result: true, + }); +}); + +test("load .js modules", () => { + expect(resolvePlugin("test", "test", __dirname, path.join(__dirname, "resolve.js"))).toMatchObject({ + result: true, + }); +}); + +test("load .ts modules", () => { + expect(resolvePlugin("test", "test", __dirname, path.join(__dirname, "resolve.ts"))).toMatchObject({ + result: true, + }); +}); + +test('"import" option', () => { + expect(resolvePlugin("test", "test", __dirname, path.join(__dirname, "import.ts"), "named")).toMatchObject({ + result: true, + }); +}); diff --git a/test/transpile/resolve-plugin/resolve.js b/test/transpile/resolve-plugin/resolve.js new file mode 100644 index 000000000..3b42547f6 --- /dev/null +++ b/test/transpile/resolve-plugin/resolve.js @@ -0,0 +1 @@ +module.exports.default = true; diff --git a/test/transpile/resolve-plugin/resolve.ts b/test/transpile/resolve-plugin/resolve.ts new file mode 100644 index 000000000..0622167b7 --- /dev/null +++ b/test/transpile/resolve-plugin/resolve.ts @@ -0,0 +1,2 @@ +// tslint:disable-next-line: no-default-export +export default true; diff --git a/test/transpile/transformers/types.ts b/test/transpile/transformers/fixtures.ts similarity index 100% rename from test/transpile/transformers/types.ts rename to test/transpile/transformers/fixtures.ts diff --git a/test/transpile/transformers/import.ts b/test/transpile/transformers/import.ts deleted file mode 100644 index 8dafa5886..000000000 --- a/test/transpile/transformers/import.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as ts from "typescript"; -import { visitAndReplace } from "./utils"; - -// tslint:disable-next-line: no-default-export -export const transformer = (): ts.TransformerFactory => context => file => - visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node) || node.expression) return; - return ts.updateReturn(node, ts.createLiteral(true)); - }); diff --git a/test/transpile/transformers/resolve.js b/test/transpile/transformers/resolve.js deleted file mode 100644 index 3ba87c73f..000000000 --- a/test/transpile/transformers/resolve.js +++ /dev/null @@ -1,11 +0,0 @@ -const ts = require("typescript"); - -module.exports.default = () => context => file => { - const replaceNode = node => { - if (!ts.isReturnStatement(node) || node.expression) return; - return ts.updateReturn(node, ts.createLiteral(true)); - }; - - const visit = node => replaceNode(node) || ts.visitEachChild(node, visit, context); - return ts.visitNode(file, visit); -}; diff --git a/test/transpile/transformers/resolve.ts b/test/transpile/transformers/resolve.ts deleted file mode 100644 index 8b0aacff7..000000000 --- a/test/transpile/transformers/resolve.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as ts from "typescript"; -import { visitAndReplace } from "./utils"; - -// tslint:disable-next-line: no-default-export -export default (): ts.TransformerFactory => context => file => - visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node) || node.expression) return; - return ts.updateReturn(node, ts.createLiteral(true)); - }); diff --git a/test/transpile/transformers/transformers.spec.ts b/test/transpile/transformers/transformers.spec.ts index b890d1477..e166198bd 100644 --- a/test/transpile/transformers/transformers.spec.ts +++ b/test/transpile/transformers/transformers.spec.ts @@ -1,58 +1,36 @@ import * as path from "path"; -import * as tstl from "../../../src"; import * as util from "../../util"; -test("should ignore language service plugins", () => { +test("ignore language service plugins", () => { util.testFunction` - return; + return false; ` - .setOptions({ plugins: [{ name: path.join(__dirname, "resolve.ts") }] }) - .expectToEqual(undefined); + .setOptions({ plugins: [{ name: path.join(__dirname, "types.ts") }] }) + .expectToEqual(false); }); -describe("resolution", () => { - const testTransform = (transformer: tstl.TransformerImport) => { - util.testFunction` - return; - ` - .setOptions({ plugins: [transformer] }) - .expectToEqual(true); - }; - - test("resolve relative transformer paths", () => { - jest.spyOn(process, "cwd").mockReturnValue(__dirname); - testTransform({ transform: "./resolve.ts" }); - }); - - test("load .js transformers", () => { - testTransform({ transform: path.join(__dirname, "resolve.js") }); - }); - - test("load .ts transformers", () => { - testTransform({ transform: path.join(__dirname, "resolve.ts") }); - }); - - test('"import" option', () => { - testTransform({ transform: path.join(__dirname, "import.ts"), import: "transformer" }); - }); +test("default type", () => { + util.testFunction` + return false; + ` + .setOptions({ plugins: [{ transform: path.join(__dirname, "fixtures.ts"), import: "program", value: true }] }) + .expectToEqual(true); +}); - test("error if transformer could not be resolved", () => { - util.testModule`` - .setOptions({ plugins: [{ transform: path.join(__dirname, "error.ts") }] }) - .expectToHaveDiagnostics(); - }); +test("transformer resolution error", () => { + util.testModule`` + .setOptions({ plugins: [{ transform: path.join(__dirname, "error.ts") }] }) + .expectToHaveDiagnostics(); }); describe("factory types", () => { - for (const type of ["program", "config", "checker", "raw", "compilerOptions"] as const) { - test(type, () => { - util.testFunction` - return false; - ` - .setOptions({ - plugins: [{ transform: path.join(__dirname, "types.ts"), type, import: type, value: true }], - }) - .expectToEqual(true); - }); - } + test.each(["program", "config", "checker", "raw", "compilerOptions"] as const)("%s", type => { + util.testFunction` + return false; + ` + .setOptions({ + plugins: [{ transform: path.join(__dirname, "fixtures.ts"), type, import: type, value: true }], + }) + .expectToEqual(true); + }); }); From 1e7a0ed29e1be996133509283b880953a1eddb35 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 13:57:14 +0000 Subject: [PATCH 07/10] Fix CJS factory loading --- src/transpilation/utils.ts | 4 ++- test/transpile/resolve-plugin/cjs.js | 1 + .../resolve-plugin/resolve-plugin.spec.ts | 29 ++++++++++--------- test/transpile/resolve-plugin/resolve.js | 1 - .../resolve-plugin/transpiled-esm.js | 2 ++ .../resolve-plugin/{resolve.ts => ts.ts} | 0 6 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 test/transpile/resolve-plugin/cjs.js delete mode 100644 test/transpile/resolve-plugin/resolve.js create mode 100644 test/transpile/resolve-plugin/transpiled-esm.js rename test/transpile/resolve-plugin/{resolve.ts => ts.ts} (100%) diff --git a/src/transpilation/utils.ts b/src/transpilation/utils.ts index 2e71b7273..e6f975be4 100644 --- a/src/transpilation/utils.ts +++ b/src/transpilation/utils.ts @@ -40,7 +40,9 @@ export function resolvePlugin( } } - const result = require(resolved)[importName]; + const commonjsModule = require(resolved); + const factoryModule = commonjsModule.__esModule ? commonjsModule : { default: commonjsModule }; + const result = factoryModule[importName]; if (result === undefined) { return { error: diagnosticFactories.shouldHaveAExport(kind, query, importName) }; } diff --git a/test/transpile/resolve-plugin/cjs.js b/test/transpile/resolve-plugin/cjs.js new file mode 100644 index 000000000..ec01c2c14 --- /dev/null +++ b/test/transpile/resolve-plugin/cjs.js @@ -0,0 +1 @@ +module.exports = true; diff --git a/test/transpile/resolve-plugin/resolve-plugin.spec.ts b/test/transpile/resolve-plugin/resolve-plugin.spec.ts index b58b07e89..7b48bad8f 100644 --- a/test/transpile/resolve-plugin/resolve-plugin.spec.ts +++ b/test/transpile/resolve-plugin/resolve-plugin.spec.ts @@ -2,25 +2,26 @@ import * as path from "path"; import { resolvePlugin } from "../../../src/transpilation/utils"; test("resolve relative module paths", () => { - expect(resolvePlugin("test", "test", __dirname, "./resolve.ts")).toMatchObject({ - result: true, - }); + const result = resolvePlugin("test", "test", __dirname, "./ts.ts"); + expect(result).toMatchObject({ result: true }); }); -test("load .js modules", () => { - expect(resolvePlugin("test", "test", __dirname, path.join(__dirname, "resolve.js"))).toMatchObject({ - result: true, - }); +test("load .ts modules", () => { + const result = resolvePlugin("test", "test", __dirname, path.join(__dirname, "ts.ts")); + expect(result).toMatchObject({ result: true }); }); -test("load .ts modules", () => { - expect(resolvePlugin("test", "test", __dirname, path.join(__dirname, "resolve.ts"))).toMatchObject({ - result: true, - }); +test("load CJS .js modules", () => { + const result = resolvePlugin("test", "test", __dirname, path.join(__dirname, "cjs.js")); + expect(result).toMatchObject({ result: true }); +}); + +test("load transpiled ESM .js modules", () => { + const result = resolvePlugin("test", "test", __dirname, path.join(__dirname, "transpiled-esm.js")); + expect(result).toMatchObject({ result: true }); }); test('"import" option', () => { - expect(resolvePlugin("test", "test", __dirname, path.join(__dirname, "import.ts"), "named")).toMatchObject({ - result: true, - }); + const result = resolvePlugin("test", "test", __dirname, path.join(__dirname, "import.ts"), "named"); + expect(result).toMatchObject({ result: true }); }); diff --git a/test/transpile/resolve-plugin/resolve.js b/test/transpile/resolve-plugin/resolve.js deleted file mode 100644 index 3b42547f6..000000000 --- a/test/transpile/resolve-plugin/resolve.js +++ /dev/null @@ -1 +0,0 @@ -module.exports.default = true; diff --git a/test/transpile/resolve-plugin/transpiled-esm.js b/test/transpile/resolve-plugin/transpiled-esm.js new file mode 100644 index 000000000..ebf774058 --- /dev/null +++ b/test/transpile/resolve-plugin/transpiled-esm.js @@ -0,0 +1,2 @@ +module.exports.__esModule = true; +module.exports.default = true; diff --git a/test/transpile/resolve-plugin/resolve.ts b/test/transpile/resolve-plugin/ts.ts similarity index 100% rename from test/transpile/resolve-plugin/resolve.ts rename to test/transpile/resolve-plugin/ts.ts From 8fc3a986d0db415a5b7356101eb931527fa1859e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 14:08:52 +0000 Subject: [PATCH 08/10] Add description to `luaPlugins` option --- src/cli/parse.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 61268a086..ce0985913 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -64,7 +64,7 @@ export const optionDeclarations: CommandLineOption[] = [ }, { name: "luaPlugins", - description: "TODO", + description: "List of TypeScriptToLua plugins.", type: "object", }, ]; From 91d4a37e521a750db5fe459b614a0f6810fab5d0 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 14:25:21 +0000 Subject: [PATCH 09/10] Add changelog entry --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac2e2d9f8..6e5963c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## Unreleased + +- Added `tstl.luaPlugins` option, allowing to specify plugins in a `tsconfig.json` file: + + ```json + { + "tstl": { + "luaPlugins": [{ "plugin": "./plugin.ts" }] + } + } + ``` + ## 0.31.0 - **Breaking:** The old annotation syntax (`/* !varArg */`) **no longer works**, the only currently supported syntax is: From e1f7af6180e43165b083f1907d4088fcb0f3b69f Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 11 Mar 2020 21:12:48 +0000 Subject: [PATCH 10/10] Rename `luaPlugins.plugin` to `luaPlugins.name` --- CHANGELOG.md | 2 +- src/CompilerOptions.ts | 2 +- src/transpilation/plugins.ts | 4 ++-- test/transpile/plugins/plugins.spec.ts | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e5963c5d..63aa189b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ```json { "tstl": { - "luaPlugins": [{ "plugin": "./plugin.ts" }] + "luaPlugins": [{ "name": "./plugin.ts" }] } } ``` diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index 7a27bb36f..6fc4f020d 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -18,7 +18,7 @@ export interface TransformerImport { } export interface LuaPluginImport { - plugin: string; + name: string; import?: string; } diff --git a/src/transpilation/plugins.ts b/src/transpilation/plugins.ts index cb8f80443..0311efbbd 100644 --- a/src/transpilation/plugins.ts +++ b/src/transpilation/plugins.ts @@ -29,9 +29,9 @@ export function getPlugins(program: ts.Program, diagnostics: ts.Diagnostic[], cu const { error: resolveError, result: factory } = resolvePlugin( "plugin", - `${optionName}.plugin`, + `${optionName}.name`, getConfigDirectory(options), - pluginOption.plugin, + pluginOption.name, pluginOption.import ); diff --git a/test/transpile/plugins/plugins.spec.ts b/test/transpile/plugins/plugins.spec.ts index 81cb6db44..ce2ebc7a6 100644 --- a/test/transpile/plugins/plugins.spec.ts +++ b/test/transpile/plugins/plugins.spec.ts @@ -3,7 +3,7 @@ import * as util from "../../util"; test("printer", () => { util.testModule`` - .setOptions({ luaPlugins: [{ plugin: path.join(__dirname, "printer.ts") }] }) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "printer.ts") }] }) .tap(builder => expect(builder.getMainLuaCodeChunk()).toMatch("Plugin")); }); @@ -11,7 +11,7 @@ test("visitor", () => { util.testFunction` return false; ` - .setOptions({ luaPlugins: [{ plugin: path.join(__dirname, "visitor.ts") }] }) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "visitor.ts") }] }) .expectToEqual(true); }); @@ -19,6 +19,6 @@ test("visitor using super", () => { util.testFunction` return "foo"; ` - .setOptions({ luaPlugins: [{ plugin: path.join(__dirname, "visitor-super.ts") }] }) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "visitor-super.ts") }] }) .expectToEqual("bar"); });