Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize Lua require path #1371

Closed
jose-elias-alvarez opened this issue Dec 15, 2022 · 3 comments
Closed

Customize Lua require path #1371

jose-elias-alvarez opened this issue Dec 15, 2022 · 3 comments

Comments

@jose-elias-alvarez
Copy link

jose-elias-alvarez commented Dec 15, 2022

Hello! I'm using TypeScriptToLua to write typescript.nvim, a Neovim plugin. First, thanks for the awesome project! I maintain another, much larger Lua-only plugin, and I much prefer working in TypeScript. I also apologize if my description is lacking in detail or otherwise confusing.


The issue is as follows: If a Neovim user loads my plugin by calling require("typescript") in their config, the editor searches its runtime path (generally plugin paths + config paths) for one of the following:

  1. A lua/typescript.lua file
  2. A lua/typescript/init.lua entrypoint

At the moment, I'm using the following option to compile my code into a single init.lua. When users install my plugin, either manually or via a plugin manager, it's placed on Neovim's runtime path, which makes the above require call work as expected:

"luaBundle": "./lua/typescript/init.lua",

Ideally, instead of using a bundle, I'd like to use outDir to mirror the structure of the source TypeScript files. This would have a few advantages:

  1. Splitting files drastically reduces the time it takes to load the entrypoint file (which can potentially affect a user's startup time);
  2. Individual modules can be loaded, which is a common pattern for advanced plugin usage / tweaking; and
  3. As an author, it's easier for me to see how TypeScript code maps to Lua code, which helps with debugging.

However, this doesn't currently work from Neovim. Let's say I remove the luaBundle options above in favor of outDir and start with the following TypeScript file:

import { config } from "./config";

export const setup = () => {
  for (const key in config) {
    print(key);
  }
};

The resulting require call at the top of the transpiled file then (naturally) looks like this:

local ____config = require("config")

Unfortunately, this won't work, because when the above code is run in Neovim, the editor looks for a lua/config.lua or lua/config/init.lua in its runtime path (which may exist somewhere but won't match what we actually want).


The ideal solution on my end would be to modify the require path so that the above call points at typescript.config instead of just config. I played around with all the tsconfig.json options I could find and wasn't able to achieve that result, so I'm curious if there's a way to achieve this that I'm not aware of. (This may also be an XY problem that I've banged my head against for too long.)

Otherwise, I'm not sure whether it makes sense to add this as a feature, since I don't know if other use cases exist, but it would definitely be a boon for the Neovim community. (I've also considered just writing a script to modify these paths manually, but that's more brittle and raises the threshold for plugin authors to start using TSTL.)

@lolleko
Copy link
Member

lolleko commented Dec 15, 2022

Have you thought about writing a small transpiler plugin? (https://typescripttolua.github.io/docs/api/plugins)
The simplest option would probably be beforeemit plugin (https://typescripttolua.github.io/docs/api/plugins#beforeemit)
that uses a regex or string find to replace require("xyz") with require("typescript.xyz") / require("typescript/xyz") (not sure which path notation nvim uses).

@jose-elias-alvarez
Copy link
Author

Writing a plugin looks like a great option - easier to share and less brittle than a shell script. Thanks! If plugins seem like the right solution to this kind of problem, feel free to close this issue.

@jose-elias-alvarez
Copy link
Author

jose-elias-alvarez commented Dec 16, 2022

I played around with this a bit and was able to achieve what I wanted quite easily with a simple plugin (copy-pasted below for reference in case anyone else has the same question):

import ts from "typescript";
import * as tstl from "typescript-to-lua";

const REQUIRE_PATH_REGEX = /require\("(.+)"\)/g;

// require paths that we don't want to transform
// in my case, this is another Lua plugin that my plugin depends on
const RAW_IMPORT_PATHS = ["lspconfig"]; 

const plugin: tstl.Plugin = {
  beforeEmit(
    _program: ts.Program,
    _options: tstl.CompilerOptions,
    _emitHost: tstl.EmitHost,
    result: tstl.EmitFile[]
  ) {
    for (const file of result) {
      file.code = file.code.replaceAll(
        REQUIRE_PATH_REGEX,
        (match, path: unknown) => {
          if (typeof path !== "string" || RAW_IMPORT_PATHS.includes(path)) {
            return match;
          }

          return `require("typescript.${path}")`;
        }
      );
    }
  },
};

export default plugin;

Thanks for the guidance @lolleko! From my end this is an ideal solution so I'm going to close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants