-
Notifications
You must be signed in to change notification settings - Fork 63
Support plugin config values in bsconfig and CLI #1706
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
Draft
TwitchBronBron
wants to merge
2
commits into
master
Choose a base branch
from
plugin-configuration-support
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,11 +21,349 @@ Those plugins will be loaded by the VSCode extension and can provide live diagno | |
| } | ||
| ``` | ||
|
|
||
| ### Plugin configuration objects | ||
|
|
||
| Instead of a plain string, a plugin entry can be an object with `src`, `name`, and `config` properties. This lets you pass plugin-specific configuration directly in `bsconfig.json`. | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| { | ||
| "src": "./scripts/myPlugin.js", | ||
| "name": "my-plugin", | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chrisdp wants this named |
||
| "config": { | ||
| "severity": "error", | ||
| "ignorePatterns": ["**/*.spec.bs"] | ||
| } | ||
| }, | ||
| { | ||
| "src": "@rokucommunity/bslint", | ||
| "config": "./path/to/bslint-config.json" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| The supported properties are: | ||
| - **`src`** *(required)* — the path to the plugin file or npm package name, same as the string shorthand. | ||
| - **`name`** *(optional)* — overrides the plugin's own `name` property. Useful for distinguishing multiple instances of the same plugin loaded with different configs. | ||
| - **`config`** *(optional)* — plugin-specific configuration, either: | ||
| - **An inline object** — passed directly to the plugin. | ||
| - **A path to a JSONC file** — the file is parsed and its contents are passed to the plugin. JSONC files support JavaScript-style comments (`//` and `/* */`). Paths in `bsconfig.json` are resolved relative to the `bsconfig.json` file's directory. | ||
|
|
||
| Both the string shorthand and the object form can be mixed in the same `plugins` array. | ||
|
|
||
| #### Example: JSONC config file | ||
|
|
||
| A JSONC config file lets you keep large or commented plugin configuration out of `bsconfig.json`. Given this `bsconfig.json`: | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| { | ||
| "src": "@rokucommunity/bslint", | ||
| "config": "./bslint.jsonc" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| …the referenced `bslint.jsonc` might look like this: | ||
|
|
||
| ```jsonc | ||
| { | ||
| // overall severity for all rules | ||
| "severity": "error", | ||
|
|
||
| // skip these files when linting | ||
| "ignorePatterns": [ | ||
| "**/*.spec.bs", | ||
| "source/generated/**/*.brs" | ||
| ], | ||
|
|
||
| "rules": { | ||
| "no-print": "error", | ||
| "no-underscores": "warn", | ||
| /* allow `m.foo` style access in test files only */ | ||
| "consistent-this": "off" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| The compiler parses the file (comments and trailing commas allowed) and hands the resulting object to the plugin via `onSetConfiguration`. | ||
|
|
||
| ### Receiving configuration in your plugin | ||
|
|
||
| The compiler calls the `onSetConfiguration` lifecycle hook on every plugin immediately after loading it, before any other lifecycle events fire. Plugins loaded via the string shorthand receive an empty object (`{}`); plugins loaded via the object form receive their resolved `config` value (or `{}` if no `config` was specified). Plugins may be reconfigured multiple times in watch mode if the `bsconfig.json` or plugin config files change. | ||
|
|
||
| ```typescript | ||
| import type { CompilerPlugin } from 'brighterscript'; | ||
|
|
||
| interface MyPluginConfig { | ||
| severity?: 'error' | 'warn'; | ||
| ignorePatterns?: string[]; | ||
| } | ||
|
|
||
| export default function () { | ||
| return new MyPlugin(); | ||
| } | ||
|
|
||
| class MyPlugin implements CompilerPlugin { | ||
| public name = 'my-plugin'; | ||
| private config: MyPluginConfig = {}; | ||
|
|
||
| public onSetConfiguration(newConfig: MyPluginConfig) { | ||
| this.config = newConfig; | ||
| } | ||
|
|
||
| public afterFileValidate(file) { | ||
| // use this.config here | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Usage on the CLI | ||
|
|
||
| Load plugins by path or package name with the existing `--plugins` (plural) flag: | ||
| ```bash | ||
| npx bsc --plugins "./scripts/myPlugin.js" "@rokucommunity/bslint" | ||
| ``` | ||
|
|
||
| #### Naming a plugin from the CLI | ||
|
|
||
| Use the `--plugin` (singular) flag to load a plugin **and give it a name in the same flag**. The flag takes 1 or 2 positional values: | ||
|
|
||
| ```bash | ||
| # --plugin <src> — load a plugin (no inline name) | ||
| # --plugin <src> <name> — load a plugin and assign it a name | ||
| ``` | ||
|
|
||
| Examples: | ||
|
|
||
| ```bash | ||
| # Load @rokucommunity/bslint and name it "bslint" for CLI targeting | ||
| npx bsc --plugin "@rokucommunity/bslint" bslint | ||
|
|
||
| # Load a local script and name it "house-rules" | ||
| npx bsc --plugin ./scripts/customLinter.js house-rules | ||
|
|
||
| # Load multiple named plugins by repeating --plugin | ||
| npx bsc --plugin "@rokucommunity/bslint" bslint \ | ||
| --plugin ./scripts/customLinter.js house-rules | ||
|
|
||
| # Mix with --plugins (plural) — names from --plugin take effect on those entries only | ||
| npx bsc --plugins alpha beta charlie --plugin "@rokucommunity/bslint" bslint | ||
| ``` | ||
|
|
||
| The name you supply on the CLI is equivalent to the `name` field in a `bsconfig.json` plugin entry — it becomes the highest-priority identifier for `--plugin.<id>...` config overrides (see [How the `<id>` is resolved](#how-the-id-is-resolved) below). | ||
|
|
||
| ```bash | ||
| # Load and name in one place, then override config keyed by that name | ||
| npx bsc --plugin "@rokucommunity/bslint" bslint --plugin.bslint.severity=error | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chrisdp wants to consider removing the equals from all these examples, since it's optional |
||
| ``` | ||
|
|
||
| If the name is omitted, the plugin's factory `name` and its src string remain available as fallback identifiers, exactly like a bare string entry in `bsconfig.json`. | ||
|
|
||
| A few details worth knowing: | ||
|
|
||
| - `--plugin <src>` consumes the next token as the name **only if** that token does not start with `-`. So `--plugin ./local.js --watch` is parsed as a single src-only entry; `--watch` is its own flag. | ||
| - `--plugin=<src>` (single-token form, with `=`) does **not** support an inline name — use the space-separated pair form when you need a name. | ||
| - The dotted form `--plugin.<id>.<prop>=<value>` is unaffected — it remains a *config override*, not a load directive. | ||
| - Plugin paths starting with `.` are resolved relative to the working directory, the same as `--plugins` entries. | ||
|
|
||
| #### Overriding plugin config from the CLI | ||
|
|
||
| Override individual plugin config properties using `--plugin.<id>.<prop>=<value>`: | ||
| ```bash | ||
| npx bsc --plugin.my-plugin.severity=error --plugin.my-plugin.ignorePatterns="**/*.spec.bs" | ||
| ``` | ||
|
|
||
| Nested properties work too: | ||
| ```bash | ||
| npx bsc --plugin.my-plugin.rules.noUnderscores=true | ||
| ``` | ||
|
|
||
| CLI overrides are deep-merged on top of whatever `config` the plugin has in `bsconfig.json`. | ||
|
|
||
| ##### How the `<id>` is resolved | ||
|
|
||
| A plugin can be identified three different ways, and the `<id>` you write after `--plugin.` is matched against loaded plugins in this strict precedence order: | ||
|
|
||
| 1. **bsconfig user-supplied `name`** — the `name` field on a plugin entry in `bsconfig.json`. This is the **highest priority**: if any loaded plugin's bsconfig entry has `name === <id>`, that plugin is the winner and other plugins are ignored — even if they have a factory name or src that also matches. | ||
| 2. **Plugin factory `name`** — the `name` returned from the plugin's factory function (e.g. `return { name: 'bslint' }`). Only consulted when no bsconfig-name match was found. Plugins that already have a user-supplied bsconfig name are not eligible for factory-name targeting (the user renamed them deliberately). | ||
| 3. **bsconfig `src`** — the path or package name string exactly as you wrote it in `bsconfig.json` (e.g. `"./scripts/myPlugin.js"` or `"@rokucommunity/bslint"`). Used as a final fallback for unnamed local scripts. | ||
|
|
||
| If an identifier matches multiple plugins at the chosen precedence level, the build fails with an error and you must set a unique `name` on the conflicting entries in `bsconfig.json` to disambiguate. Collisions at *lower* precedence levels are ignored when a *higher* level produced an unambiguous match. | ||
|
|
||
| ###### Name priority examples | ||
|
|
||
| Given this `bsconfig.json`: | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| "@rokucommunity/bslint", | ||
| { "src": "./scripts/customLinter.js", "name": "bslint" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| Both plugins happen to export `name: 'bslint'` from their factory. With the rules above: | ||
|
|
||
| ```bash | ||
| # Targets ./scripts/customLinter.js only. | ||
| # The bsconfig user-supplied `name` "bslint" on the second entry wins outright; | ||
| # the factory-name match on the first entry is ignored. | ||
| npx bsc --plugin.bslint.severity=error | ||
|
|
||
| # Targets @rokucommunity/bslint only — match by bsconfig src. | ||
| npx bsc --plugin.@rokucommunity/bslint.severity=error | ||
|
|
||
| # Targets ./scripts/customLinter.js — also matched by bsconfig src. | ||
| npx bsc --plugin.\"./scripts/customLinter.js\".severity=error | ||
| ``` | ||
|
|
||
| If two entries shared the **same** user-supplied `name`, that's the case that fails: | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| { "src": "@rokucommunity/bslint", "name": "linter" }, | ||
| { "src": "./scripts/customLinter.js", "name": "linter" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ```bash | ||
| # Error: --plugin.linter is ambiguous. Rename one of the entries in bsconfig.json. | ||
| npx bsc --plugin.linter.severity=error | ||
| ``` | ||
|
|
||
| ##### Ways to configure a plugin | ||
|
|
||
| There are three layers, each one optional, applied in this order: | ||
|
|
||
| 1. **Inline `config` in `bsconfig.json`** — the base configuration that always applies. | ||
| 2. **CLI total replacement** via a bare `--plugin.<id>=<value>` (no dotted property path) — *replaces* the bsconfig `config` entirely instead of merging on top of it. If `<value>` is a string that points to a JSONC file, the file is parsed and its contents become the new config. | ||
| 3. **CLI property overrides** via `--plugin.<id>.<prop>=<value>` — deep-merged on top of whatever the layers above produced. | ||
|
|
||
| The order of CLI flags does not matter — total-replacement is always applied first, then merge overrides. | ||
|
|
||
| ###### Examples | ||
|
|
||
| **1. Inline config in bsconfig only:** | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| { | ||
| "src": "@rokucommunity/bslint", | ||
| "config": { "severity": "warn", "rules": { "no-print": "error" } } | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| The plugin receives `{ severity: 'warn', rules: { 'no-print': 'error' } }`. | ||
|
|
||
| **2. JSONC file referenced from bsconfig:** | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| { "src": "@rokucommunity/bslint", "config": "./bslint.jsonc" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| The plugin receives the parsed contents of `./bslint.jsonc`. | ||
|
|
||
| **3. Inline config + CLI merge overrides (most common):** | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| { | ||
| "src": "@rokucommunity/bslint", | ||
| "name": "bslint", | ||
| "config": { "severity": "warn", "rules": { "no-print": "error", "no-underscores": "warn" } } | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ```bash | ||
| npx bsc --plugin.bslint.severity=error --plugin.bslint.rules.no-underscores=off | ||
| ``` | ||
|
|
||
| The plugin receives: | ||
| ```json | ||
| { | ||
| "severity": "error", | ||
| "rules": { "no-print": "error", "no-underscores": "off" } | ||
| } | ||
| ``` | ||
|
|
||
| `severity` was overridden, `rules.no-underscores` was overridden, and `rules.no-print` was preserved from bsconfig (deep merge). | ||
|
|
||
| **4. CLI total replacement (discards bsconfig config entirely):** | ||
|
|
||
| A bare `--plugin.<id>=<value>` has no dotted property path, so it is a *total replacement* and the bsconfig `config` is thrown away: | ||
|
|
||
| ```json | ||
| { | ||
| "plugins": [ | ||
| { | ||
| "src": "@rokucommunity/bslint", | ||
| "name": "bslint", | ||
| "config": { "severity": "warn", "rules": { "no-print": "error" } } | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ```bash | ||
| # The plugin receives the contents of ./ci-bslint.jsonc — the bsconfig `config` (severity, rules) is discarded entirely. | ||
| npx bsc --plugin.bslint=./ci-bslint.jsonc | ||
| ``` | ||
|
|
||
| If `./ci-bslint.jsonc` contains `{ "severity": "error" }`, the plugin receives exactly `{ "severity": "error" }` — *not* a merge with the bsconfig `config`. | ||
|
|
||
| **5. CLI total replacement + CLI merge on top:** | ||
|
|
||
| You can combine total replacement with merge overrides in a single command. The replacement is applied first (regardless of CLI order), then merges layer on top: | ||
|
|
||
| ```bash | ||
| # Equivalent regardless of the order on the command line: | ||
| npx bsc --plugin.bslint=./ci-bslint.jsonc --plugin.bslint.severity=hint | ||
| npx bsc --plugin.bslint.severity=hint --plugin.bslint=./ci-bslint.jsonc | ||
| ``` | ||
|
|
||
| If `./ci-bslint.jsonc` is `{ "severity": "error", "rules": { "no-print": "warn" } }`, the plugin receives: | ||
| ```json | ||
| { "severity": "hint", "rules": { "no-print": "warn" } } | ||
| ``` | ||
|
|
||
| ##### Quoting ids and properties with dots | ||
|
|
||
| Plugin ids and property names that contain dots can be double-quoted to prevent the dot being interpreted as a path separator. Any segment of the dotted key may be quoted: | ||
|
|
||
| ```bash | ||
| # scoped npm package with no dot in the name — no quoting needed | ||
| npx bsc --plugin.@rokucommunity/bslint.enabled=false | ||
|
|
||
| # id with literal dots in it — quote the id segment | ||
| npx bsc --plugin.\"my-module.with.dots\".enabled=false | ||
|
|
||
| # property name with a dot — quote the property segment | ||
| npx bsc --plugin.bslint.\"rules.no-underscores\"=warn | ||
|
|
||
| # target a local script by its path (the path contains a `.`, so quote it) | ||
| npx bsc --plugin.\"./scripts/myPlugin.js\".enabled=true | ||
| ``` | ||
|
|
||
| > Plugin configuration objects are only supported in `bsconfig.json` — the CLI `--plugins` flag accepts string values only. To configure a plugin from the CLI, load it via `--plugins` (or `bsconfig.json`) and then use `--plugin.<id>...` flags to override its config. | ||
|
|
||
| ### Programmatic configuration | ||
|
|
||
| When using the compiler API directly, plugins can directly reference your code: | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chrisdp wants this named
"plugin"instead of"src"