Hi Analyzer Team,
I'm developing an analyzer plugin, and had a question about how versioning is handled via the Dart Analysis Server, Dart Code extension that spins up the server for a workspace, etc.
As I've mentioned in previous discussions, I've been working on a plugin that would allow lints/edits defined in external packages to run off of a single set of resources (as opposed to duplicating resources for each plugin). I do have a working PoC, but something I overlooked was how versioning would be handled for projects with conflicting plugin dependencies, and I wanted to get your 2 cents before proceeding.
I'm not 100% sure about this, but from what I can tell analyzer plugins run with a plugin-workspace relationship of one-to-one , where any workspace can have 1+ AnalysisContexts. I'm not exactly sure how the analysis server determines which version of "my_plugin" is used, but from brief testing I believe the server first looks at each context's analysis_options.yaml for the list of activated plugins, and then attempts to find a matching package name within the pubspec, then copies the plugin's bootstrapper from root>tools>analyzer_plugin to .dartServer/.plugin_manager/, etc. etc.
The problems that I foresee with this architecture is in the one-to-one server-to-workspace relationship and the possibility that AnalysisContexts have different package plugin configurations - and in my case, if these packages have different 3rd party lint dependencies. For example, one context may depend on my_plugin v1.0.0, while another depends on my_plugin v2.0.0.
With most other dart tools, take build_runner for example, the analyzer is versioned and run within the context of one single root package. With the analysis server + LSP, the server version seems to run from the SDK version defined via the Dart Code extension, and therefore seems to attempt to run just 1 plugin instance for the whole workspace / AnalysisContextCollection.
If this is the case, some brainstormed solutions to allow different packages to define their own plugin dependencies, while minimizing the amount of duplicated resources, include the following:
Option #1:
Combine lint dependencies from all AnalysisContexts into one package_config.json (e.g. the one defined in ~/.dartServer/.plugin-manager), run the plugin, determine which lints/edits to use for each context during runtime via pubspec/analysis options. This would be very easy to develop. Risks:
- different plugin / lint versions cannot co-exist
- inevitable dependency conflicts would not be associated with a single root package, and reporting/debugging conflicts would require users to learn new workflows specific to analyzer_plugin
Option #2:
Server runs middleman-plugin (1:1 relationship with analysis server), which bootstraps and runs 1+ instances of my_plugin with a 1:1 relationship between my_plugin and AnalysisContexts that have the plugin declared, where the my_plugin isolate would be initialized with its own package_config from the analysis context root. The plugin would analyze the context root and all of its dependencies. Risks:
- In the case of workspace-package-A depending on workspace-package-B, resource overlap would exist, meaning duplicated resources
- Higher complexity from adding another plugin layer
Option #3
Middleman-plugin runs my_plugin with a 1:1 relationship between my_plugin:package, where the package is any root package or dependency in all package_configs within the workspace. We would have theoretically 0 resource overlap while allowing any plugin version or dependency to run alongside one another (within reason, of course). Risks:
- Cross-isolate communication is essential and would require designing a new and fairly advanced communication protocol and communication architecture
- Much higher complexity from communication protocol and additional plugin layer
Option #4, 5, 6
??
I'm leaning towards option #2, as it seems to have the best combination of meeting the goal of the project, matching the existing pub dependency developer workflows, and - in the case of high resource load due to significant overlap - it would be the easiest to control from the users end (e.g. disable plugins in dependency packages, only enable the plugin in your main application).
As always, I seriously appreciate your time and any input you can provide. Thanks and hope you have a great weekend!
Patt