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

discussion: plugin support #175

Closed
lucacasonato opened this issue Jun 17, 2020 · 17 comments
Closed

discussion: plugin support #175

lucacasonato opened this issue Jun 17, 2020 · 17 comments
Labels
request Feature request

Comments

@lucacasonato
Copy link
Member

lucacasonato commented Jun 17, 2020

It would be great if deno_lint had support for plugins that can add rules. Here are my thoughts:

Constraints:

  • Plugins should be written in Rust and use the same infrastructure as the rules that are part of this repository.
  • Plugins should be loaded at runtime, and should not have to be available at compile time. This means they need to be dynamically executed.
  • Plugins should not have to re-parse the source code.
  • Plugins should be sandboxed and safe - they should not be able to access the system directly.

Possible solution:
Most likely the best solution that satisfies these constraints is WebAssembly. You can write your rules in Rust, and then compile them to WebAssembly that deno_lint could load and run. WebAssembly is also safe because it is sandboxed and only gets access to resources that deno_lint exposes to it. Here is how I imagine this would work:

  1. deno_lint should initialise all plugins at startup, by creating a WebAssembly VM for each plugin.
  2. deno_lint parses the source code into a swc AST. This ASTs raw memory is stored in a shared memory area with the WebAssembly plugin.
  3. deno_lint calls a function in WebAssembly to kick off code checking in the plugin. It can now do its analysis and create diagnostics. These diagnostics are stored in a separate section of shared memory between the plugin and deno_lint.
  4. deno_lint extracts these diagnostics and augments them with information about plugin origin and returns them to the user.

This approach would allow you to start multiple instances of the same plugin easily, to paralelnize code analysis for large modules.
There are definitely some issues with this. The biggest one I can see right now is how to pass the AST between plugin and deno_lint. If we only support rust, we might be able to pass the raw backing of the object, but I don't know if this is feasible and how likely it is to break (probably very). Another solution would be to serialise to JSON or protobuf and then deserialise on the plugin side. This is definitely possible, but requires a lot of careful translation of the swc structs into JSON. This might be very complicated and time intensive, and would probably not be great for performance. The third solution would be to have the plugin do the parsing of code itself - this would also slow down the process significantly though.

Prior art:
dprint also makes use of WebAssembly for its plugin system. To the end user looks like this: https://dprint.dev/plugin-dev/ (very clean)

@lucacasonato lucacasonato changed the title discussion: Plugin support discussion: plugin support Jun 17, 2020
@bartlomieju bartlomieju mentioned this issue Jun 18, 2020
9 tasks
@bartlomieju bartlomieju added the request Feature request label Jun 20, 2020
@umutzd
Copy link
Contributor

umutzd commented Oct 17, 2020

The protocol for serialization between plugins and deno_lint would be cap'n proto, right? Just to have a standard. This way it wouldn't break.

@bartlomieju
Copy link
Member

The protocol for serialization between plugins and deno_lint would be cap'n proto, right? Just to have a standard. This way it wouldn't break.

That's highly unlikely, there's no need to introduce cap'n'proto because AST is already JSON serializable.

@bartlomieju
Copy link
Member

Update: the plugin system had been implemented long time ago by @magurotuna in examples/dlint:

for plugin_path in &plugin_paths {
let js_runner = js::JsRuleRunner::new(plugin_path);
linter_builder = linter_builder.add_plugin(js_runner);
}

Once denoland/deno#11776 lands we'll be unblocked to add support for plugins in deno itself.

@bartlomieju
Copy link
Member

Discussed offline with @magurotuna, we got a clear plan how to ship this feature in the near future.

Most likely we'll go with simple JS API to register multiple rules from a single plugin:

import NoConsole from "./no_console.ts";
import Eqeqeq from "./eqeqeq.ts";


DenoLint.register({
  name: "mySuperPlugin",
  rules: [
    NoConsole,
    Eqeqeq,
  ] 
});

@cdaringe
Copy link

The bees knees would be an AST bridge between (deno_lint plugin, swc-ast) => (eslint plugin, babel-ast). i'd be excited to even contribute to it. iirc there was a thread w/ one of the eslint maintainers talkin' about ways to help. maybe we can start talkin along these lines w/ him?

@bartlomieju
Copy link
Member

@cdaringe that would require support for emitting ESLint compatible AST (estree) from SWC. There was swc-project/swc#2123 but it hadn't moved anywhere. Current idea by @dsherret is to add a slower path to deno_lint that would re-parse files in typescript-eslint and then pass it down to eslint. However no work has started on this subject, and it might wait some more before we look into it.

@cdaringe
Copy link

Gotcha, thanks for the ref. Genuine ask, is the typescript-eslint idea worth it if we can't go fast? As a deno user, I can already work up node to call eslint w/ the ts parser. I don't need a solve from deno for that. I want deno for goin fast--i can use node if i'm ok goin slow.

I'd vote to keep deno developers' precious time (your precious time!) to be reserved for doing the rad stuff, vs shimming in the less rad stuff (wiring in a JS parser runnin in v8 :) ).

@jespertheend
Copy link

+1 on having a js api available.
I'm currently using eslint-plugin-rulesdir for custom rules, which essentially makes a folder available for custom rules that you can put your own scripts in.
When writing fully fledged plugins I can see the benefits of using rust over js. But if you want to add just a single custom rule for a specific project/repository, the setup needed when using rust wouldn't be worth it for me personally.

@bartlomieju
Copy link
Member

@jespertheend the trouble is, we got a bespoke API ready for plugins, but that API is completely different to ESLint's API. Different as in, the AST is different and methods for visiting AST nodes are different. It is still unclear if we want to do that, instead of adding ESLint compatible API that would allow to use existing ESLint plugins in Deno. @dsherret and yours truly are gonna look into PoC for ESLint compatibility in Q1 2022.

@MarkBennett
Copy link

Just a suggestion for dogfooding this... perhaps leverage mdn/browser-compat-data to add a linting rule to check that Deno code can be deployed to Deno Deploy? You'd need to add a Deploy target to the browser compat JSON but that might have other uses as well.

@ild0tt0re
Copy link

@jespertheend the trouble is, we got a bespoke API ready for plugins, but that API is completely different to ESLint's API. Different as in, the AST is different and methods for visiting AST nodes are different. It is still unclear if we want to do that, instead of adding ESLint compatible API that would allow to use existing ESLint plugins in Deno. @dsherret and yours truly are gonna look into PoC for ESLint compatibility in Q1 2022.

The mantainer of swc is working on a separate package that can emit ESTree AST
swc-project/extra-bindings#4

@marcesengel
Copy link

Any updates on this? For now waiting for ESTree compatibility doesn't seem feasible to me (unless I've overlooked a finished implementation): swc-project/extra-bindings#24. What do you think about exposing an incompatible (low effort from your side) api for now, just to enable the community to recreate plugins if they want to? Personally I wouldn't mind doing it in Rust (compiled to WASM I guess) either, I just want to have the option and have it go fast :D this is what's stopping our project from adopting the linter.
Thanks for any feedback!

@Mqxx
Copy link

Mqxx commented Jul 10, 2024

Hey, any update on when plugin support will be available?

@bartlomieju
Copy link
Member

There's no concrete ETA at the moment, we plan to look into this after Deno 2 is released.

@bartlomieju
Copy link
Member

Follow denoland/deno#27203 for updates.

@scarf005
Copy link

scarf005 commented Dec 17, 2024

supporting gritql would make it a lot more easier to write lint rules, as it's very declarative:

image

pair(key=property_identifier() as $key, value=identifier() as $value) => $key where { $key == $value }

rule #1184 can be written in 1 line.

biome also plans to support gritql: https://biomejs.dev/reference/gritql/

@marvinhagemeister
Copy link
Contributor

Closing: denoland/deno#27203 has landed and linting plugins will be supported in the upcoming Deno 2.2.0 release.

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

No branches or pull requests