@doubleloop-io/oxlint-plugin-effect 0.1.0-rc.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +75 -57
  3. package/RULES.md +520 -395
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +5 -4
  6. package/dist/presets/all.json +2 -2
  7. package/dist/rules/adapter-import-boundary.js +5 -12
  8. package/dist/rules/all-errors-handled-at-boundary.js +3 -9
  9. package/dist/rules/anti-slop-no-service-constructor-imports.js +1 -8
  10. package/dist/rules/exported-schema-identifier.js +15 -10
  11. package/dist/rules/no-effect-promise.js +1 -8
  12. package/dist/rules/no-manual-discriminant-comparison.js +3 -9
  13. package/dist/rules/no-manual-discriminant-construction.js +3 -9
  14. package/dist/rules/no-provide-on-definition.js +1 -8
  15. package/dist/rules/no-throw-or-primitive-failure.d.ts +1 -0
  16. package/dist/rules/{no-bare-error-in-effect.js → no-throw-or-primitive-failure.js} +2 -9
  17. package/dist/rules/no-yielded-match.js +9 -2
  18. package/dist/rules/prefer-effect-vitest.js +1 -8
  19. package/dist/rules/provide-named-layer.js +1 -8
  20. package/dist/rules/redacted-secret-names.js +18 -5
  21. package/dist/rules/run-only-in-runner.js +1 -8
  22. package/dist/rules/service-key-matches-name.js +15 -6
  23. package/dist/rules/service-port-shape-only.js +10 -18
  24. package/dist/rules/single-style-per-function.js +1 -8
  25. package/dist/rules/split-success-path-from-error-handling.d.ts +1 -0
  26. package/dist/rules/{error-handling-in-effect-fn-handler.js → split-success-path-from-error-handling.js} +2 -9
  27. package/dist/shared/entry-files.d.ts +1 -1
  28. package/dist/shared/file-globs.d.ts +3 -9
  29. package/dist/shared/file-globs.js +3 -12
  30. package/dist/shared/settings.d.ts +5 -0
  31. package/dist/shared/settings.js +26 -0
  32. package/dist/shared/tagged-values.d.ts +2 -8
  33. package/dist/shared/tagged-values.js +3 -14
  34. package/dist/shared/test-files.d.ts +1 -1
  35. package/package.json +6 -4
  36. package/dist/rules/error-handling-in-effect-fn-handler.d.ts +0 -1
  37. package/dist/rules/no-bare-error-in-effect.d.ts +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # @doubleloop-io/oxlint-plugin-effect
2
+
3
+ ## 0.1.0
4
+
5
+ First stable release.
6
+
7
+ - 30 rules for Effect code, grouped by topic in [`RULES.md`](RULES.md): error channel, tagged values, services and Layers, runtime and entry point, design and boundaries, effectful functions, code shape, Schema, config and secrets. Three are absorbed from [`dmmulroy/anti-slop`](https://github.com/dmmulroy/anti-slop).
8
+ - An `all` preset that enables every rule at `warn`, for `oxlint.config.ts` and `.oxlintrc.json`.
9
+ - Shared settings under `settings["dl-effect"]`: `entryFiles`, `testFiles`, `discriminants`.
10
+ - Written for Effect 4; rules also recognize the Effect 3 name of the same API.
11
+ - Requires Node `^22.20.0 || >=24.8.0` and oxlint `^1.82.0`.
package/README.md CHANGED
@@ -2,11 +2,9 @@
2
2
 
3
3
  An oxlint plugin that enforces a catalog of patterns for Effect 4 code.
4
4
 
5
- The rules are written and tested against Effect 4. On an Effect 3 codebase most of them do not recognize the APIs and stay silent: silence there is not compliance.
5
+ The rules are written and tested against Effect 4. Where an Effect 3 API has the same intent, the rules recognize its v3 name too: each rule's Trigger in [`RULES.md`](RULES.md) names them.
6
6
 
7
- ## Install `@effect/tsgo` first
8
-
9
- [`@effect/tsgo`](https://github.com/Effect-TS/tsgo) is the official Effect language service on top of the TypeScript Go compiler; its `effecttsgo` oxlint presets ship more than 100 type-aware rules. This plugin is meant to be used alongside them: its rules target what those presets do not check. Set it up with its [Oxlint Setup guide](https://github.com/Effect-TS/tsgo/blob/main/docs/README.md) and put its `extends` **before** this plugin's preset, which does not include it.
7
+ It complements the type-aware rules of [`@effect/tsgo`](#alongside-effecttsgo): it does not need them, and it checks what they do not.
10
8
 
11
9
  ## Installation
12
10
 
@@ -51,33 +49,42 @@ To pick rules without the preset, load the plugin by package name and list the r
51
49
 
52
50
  ### Configuration options
53
51
 
54
- Two options tell the rules where your files are. Their defaults fit a Node CLI; on another layout (Next.js, a monorepo) set them before reading the warnings.
52
+ Three settings describe your project, not a rule, so they are declared once under `settings["dl-effect"]` and every rule that uses them reads them from there. Their defaults fit a Node CLI; on another layout (Next.js, a monorepo) set them before reading the warnings.
55
53
 
56
- - `entryFiles`: the files allowed to run Effects and import adapters. Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. Used by `run-only-in-runner` and `adapter-import-boundary`.
57
- - `testFiles`: the test files, exempt from the rules that tests legitimately break and the only files `prefer-effect-vitest` checks. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. Used by `run-only-in-runner`, `prefer-effect-vitest`, `adapter-import-boundary`, `no-effect-promise`, `provide-named-layer`, `no-provide-on-definition`, `error-handling-in-effect-fn-handler`, `all-errors-handled-at-boundary` and `anti-slop-no-service-constructor-imports`.
54
+ - `entryFiles`: the files allowed to run Effects and import adapters. Default `**/main.ts`, `**/cli.ts`, `**/bin/**`.
55
+ - `testFiles`: the test files, exempt from the rules that tests legitimately break and the only files `prefer-effect-vitest` checks. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`.
56
+ - `discriminants`: the fields that discriminate a closed union. Default `_tag`; add the ones your unions use, such as `type` or `kind`.
58
57
 
59
- Set an option only where its default does not fit. It **replaces** the default instead of extending it, and every rule that uses it must get the same value. With `oxlint.config.ts`, declare it once and override the rules that use it after `extends`:
58
+ The rules that use each one are listed in [Shared settings](RULES.md#shared-settings). Set one only where its default does not fit: it **replaces** the default instead of extending it. An unknown key or a value that is not an array of strings stops the lint with an error, so a typo is never silently ignored.
60
59
 
61
60
  ```ts
62
61
  import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
63
62
  import { defineConfig } from "oxlint"
64
63
 
65
- const entryFiles = ["apps/web/src/app/**/route.ts", "apps/web/src/runtime.ts"]
66
-
67
64
  export default defineConfig({
68
65
  extends: [dlEffectAll],
69
- rules: {
70
- "dl-effect/run-only-in-runner": ["warn", { entryFiles }],
71
- "dl-effect/adapter-import-boundary": ["warn", { entryFiles }],
66
+ settings: {
67
+ "dl-effect": { entryFiles: ["apps/web/src/app/**/route.ts", "apps/web/src/runtime.ts"] },
72
68
  },
73
69
  })
74
70
  ```
75
71
 
76
- The package exports the defaults as `defaultEntryFiles` and `defaultTestFiles`: spread them to extend a default (`testFiles: [...defaultTestFiles, "e2e/**"]`), or reuse them in your own `overrides`. With `.oxlintrc.json` the overrides go under `rules`, repeating each list in every rule.
72
+ ```json
73
+ {
74
+ "extends": ["./node_modules/@doubleloop-io/oxlint-plugin-effect/dist/presets/all.json"],
75
+ "settings": {
76
+ "dl-effect": { "entryFiles": ["apps/web/src/app/**/route.ts", "apps/web/src/runtime.ts"] }
77
+ }
78
+ }
79
+ ```
80
+
81
+ The package exports the defaults as `defaultEntryFiles`, `defaultTestFiles` and `defaultDiscriminants`: spread them to extend a default (`testFiles: [...defaultTestFiles, "e2e/**"]`).
82
+
83
+ Settings belong to the config that lints: oxlint does not inherit them from a preset in `extends`, and rejects them in `overrides`. For a value that differs by group of files, use `overrides` on the rules instead.
77
84
 
78
85
  The other options and every default are in [Default configuration](#default-configuration).
79
86
 
80
- ### Alongside `@effect/tsgo` (optional)
87
+ ### Alongside `@effect/tsgo`
81
88
 
82
89
  [`@effect/tsgo`](https://github.com/Effect-TS/tsgo) is the official Effect language service on top of the TypeScript Go compiler; its `effecttsgo` oxlint presets add more than 100 type-aware rules. This plugin does not need it, and its rules target what those presets do not check, so the two complement each other. To use both, follow its [Oxlint Setup guide](https://github.com/Effect-TS/tsgo/blob/main/docs/README.md) and put its preset in `extends` **before** `all`:
83
90
 
@@ -100,80 +107,90 @@ export default defineConfig({ extends: [effectTsgoRecommended, dlEffectAll] })
100
107
 
101
108
  ## Rules
102
109
 
103
- What each rule reports and why is in [`RULES.md`](RULES.md).
110
+ What each rule reports and why is in [`RULES.md`](RULES.md). Report false positives and missed cases in the [GitHub issues](https://github.com/doubleloop-io/oxlint-plugin-effect/issues).
104
111
 
105
- | Topic | Rules |
106
- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
107
- | Error channel | [`no-bare-error-in-effect`](RULES.md#no-bare-error-in-effect), [`try-object-form`](RULES.md#try-object-form), [`no-effect-promise`](RULES.md#no-effect-promise), [`no-parameterless-catch-all`](RULES.md#no-parameterless-catch-all), [`error-handling-in-effect-fn-handler`](RULES.md#error-handling-in-effect-fn-handler), [`anti-slop-no-manual-effect-error-tag`](RULES.md#anti-slop-no-manual-effect-error-tag) |
108
- | Tagged values | [`no-manual-discriminant-comparison`](RULES.md#no-manual-discriminant-comparison), [`no-manual-discriminant-construction`](RULES.md#no-manual-discriminant-construction) |
109
- | Services & Layers | [`service-port-shape-only`](RULES.md#service-port-shape-only), [`service-key-matches-name`](RULES.md#service-key-matches-name), [`service-built-with-of`](RULES.md#service-built-with-of), [`provide-named-layer`](RULES.md#provide-named-layer), [`no-provide-on-definition`](RULES.md#no-provide-on-definition), [`anti-slop-no-service-constructor-imports`](RULES.md#anti-slop-no-service-constructor-imports) |
110
- | Runtime & entrypoint | [`run-only-in-runner`](RULES.md#run-only-in-runner), [`all-errors-handled-at-boundary`](RULES.md#all-errors-handled-at-boundary) |
111
- | Design & boundaries | [`adapter-import-boundary`](RULES.md#adapter-import-boundary), [`no-transport-types-in-generator`](RULES.md#no-transport-types-in-generator) |
112
- | Effectful functions | [`prefer-effect-fn`](RULES.md#prefer-effect-fn), [`untraced-recursion`](RULES.md#untraced-recursion), [`single-style-per-function`](RULES.md#single-style-per-function) |
113
- | Code shape | [`no-nested-match`](RULES.md#no-nested-match), [`no-yielded-match`](RULES.md#no-yielded-match), [`no-inline-yield-star`](RULES.md#no-inline-yield-star), [`no-effect-return-type`](RULES.md#no-effect-return-type), [`no-asymmetric-match-handlers`](RULES.md#no-asymmetric-match-handlers), [`anti-slop-prefer-effect-match`](RULES.md#anti-slop-prefer-effect-match) |
114
- | Schema | [`exported-schema-identifier`](RULES.md#exported-schema-identifier), [`no-inline-schema-codec`](RULES.md#no-inline-schema-codec) |
115
- | Config & secrets | [`redacted-secret-names`](RULES.md#redacted-secret-names) |
112
+ | Topic | Rules |
113
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
114
+ | Error channel | [`no-throw-or-primitive-failure`](RULES.md#no-throw-or-primitive-failure), [`try-object-form`](RULES.md#try-object-form), [`no-effect-promise`](RULES.md#no-effect-promise), [`no-parameterless-catch-all`](RULES.md#no-parameterless-catch-all), [`split-success-path-from-error-handling`](RULES.md#split-success-path-from-error-handling), [`anti-slop-no-manual-effect-error-tag`](RULES.md#anti-slop-no-manual-effect-error-tag) |
115
+ | Tagged values | [`no-manual-discriminant-comparison`](RULES.md#no-manual-discriminant-comparison), [`no-manual-discriminant-construction`](RULES.md#no-manual-discriminant-construction) |
116
+ | Services & Layers | [`service-port-shape-only`](RULES.md#service-port-shape-only), [`service-key-matches-name`](RULES.md#service-key-matches-name), [`service-built-with-of`](RULES.md#service-built-with-of), [`provide-named-layer`](RULES.md#provide-named-layer), [`no-provide-on-definition`](RULES.md#no-provide-on-definition), [`anti-slop-no-service-constructor-imports`](RULES.md#anti-slop-no-service-constructor-imports) |
117
+ | Runtime & entrypoint | [`run-only-in-runner`](RULES.md#run-only-in-runner), [`prefer-effect-vitest`](RULES.md#prefer-effect-vitest), [`all-errors-handled-at-boundary`](RULES.md#all-errors-handled-at-boundary) |
118
+ | Design & boundaries | [`adapter-import-boundary`](RULES.md#adapter-import-boundary), [`no-transport-types-in-generator`](RULES.md#no-transport-types-in-generator) |
119
+ | Effectful functions | [`prefer-effect-fn`](RULES.md#prefer-effect-fn), [`untraced-recursion`](RULES.md#untraced-recursion), [`single-style-per-function`](RULES.md#single-style-per-function) |
120
+ | Code shape | [`no-nested-match`](RULES.md#no-nested-match), [`no-yielded-match`](RULES.md#no-yielded-match), [`no-inline-yield-star`](RULES.md#no-inline-yield-star), [`no-effect-return-type`](RULES.md#no-effect-return-type), [`no-asymmetric-match-handlers`](RULES.md#no-asymmetric-match-handlers), [`anti-slop-prefer-effect-match`](RULES.md#anti-slop-prefer-effect-match) |
121
+ | Schema | [`exported-schema-identifier`](RULES.md#exported-schema-identifier), [`no-inline-schema-codec`](RULES.md#no-inline-schema-codec) |
122
+ | Config & secrets | [`redacted-secret-names`](RULES.md#redacted-secret-names) |
116
123
 
117
124
  ### Absorbed from anti-slop
118
125
 
119
- The `anti-slop-*` rules come from the five Effect rules of [`dmmulroy/anti-slop`](https://github.com/dmmulroy/anti-slop) (plugin `anti-slop-effect`, commit [`c44ef22`](https://github.com/dmmulroy/anti-slop/tree/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b/src/effect)), ported into this plugin; its generic TypeScript rules stay out. `no-manual-tag-comparison` and `no-manual-tagged-construction` are retired: [`no-manual-discriminant-comparison`](RULES.md#no-manual-discriminant-comparison) and [`no-manual-discriminant-construction`](RULES.md#no-manual-discriminant-construction) cover them with their default. In the others, detection logic and messages are unchanged; [`anti-slop-no-service-constructor-imports`](RULES.md#anti-slop-no-service-constructor-imports) recognizes test files through the shared [`testFiles`](RULES.md#file-options) option instead of its own `*.test.*`/`*.spec.*` check. Do not enable the `anti-slop-effect` plugin alongside this one: every finding would be reported twice. The original code is MIT-licensed: see [`LICENSE.anti-slop`](LICENSE.anti-slop).
126
+ Some rules come from the Effect rules of [`dmmulroy/anti-slop`](https://github.com/dmmulroy/anti-slop), MIT-licensed: see [`LICENSE.anti-slop`](LICENSE.anti-slop).
127
+
128
+ - Imported: [`anti-slop-no-manual-effect-error-tag`](RULES.md#anti-slop-no-manual-effect-error-tag), [`anti-slop-no-service-constructor-imports`](RULES.md#anti-slop-no-service-constructor-imports), [`anti-slop-prefer-effect-match`](RULES.md#anti-slop-prefer-effect-match).
129
+ - Superseded: `no-manual-tag-comparison` by [`no-manual-discriminant-comparison`](RULES.md#no-manual-discriminant-comparison), `no-manual-tagged-construction` by [`no-manual-discriminant-construction`](RULES.md#no-manual-discriminant-construction).
120
130
 
121
131
  ## Default configuration
122
132
 
123
- `all` is equivalent to the config below, with every option spelled out at its default. File options **replace** the defaults instead of extending them, and every rule sharing an option must get the same value: see [File options](RULES.md#file-options).
133
+ `all` is equivalent to the config below, with every setting and option spelled out at its default. Both **replace** the defaults instead of extending them: see [Shared settings](RULES.md#shared-settings).
124
134
 
125
135
  ```ts
126
136
  import { defineConfig } from "oxlint"
127
137
 
128
- // Files allowed to run Effects (Effect.run*, runMain, ManagedRuntime.make) and to import
129
- // adapters. `index.ts` is left out on purpose: as a barrel it would let adapters leak
130
- // through re-exports.
131
- const entryFiles = ["**/main.ts", "**/cli.ts", "**/bin/**"]
132
-
133
- // Files exempt from the rules that tests legitimately break. Helper directories such as
134
- // `src/testing/` are support code, not tests, so they are not matched.
135
- const testFiles = ["**/*.test.*", "**/*.spec.*", "**/__tests__/**", "**/test/**", "**/tests/**"]
136
-
137
138
  // Constructors that must not appear inside an Effect generator: the conversion to the
138
139
  // transport belongs in the second argument of Effect.fn. Add your own transport DTOs.
139
140
  const transportTypes = ["Response", "NextResponse"]
140
141
 
141
- // Fields that discriminate a closed union, compared or switched on only through an exhaustive
142
- // match and set only by a constructor. Add the ones your unions use, such as `type` or `kind`.
143
- const discriminants = ["_tag"]
144
-
145
142
  // Schema fields holding a secret: a field matches an entry or ends with it capitalized
146
143
  // (`password` matches `dbPassword`). No generic `token`: `nextPageToken` is not a secret.
147
144
  const secretFields = ["password", "secret", "apiKey", "accessToken", "refreshToken", "idToken"]
148
145
 
149
146
  export default defineConfig({
150
147
  jsPlugins: ["@doubleloop-io/oxlint-plugin-effect"],
148
+ settings: {
149
+ "dl-effect": {
150
+ // Files allowed to run Effects (Effect.run*, runMain, ManagedRuntime.make) and to
151
+ // import adapters. `index.ts` is left out on purpose: as a barrel it would let
152
+ // adapters leak through re-exports.
153
+ entryFiles: ["**/main.ts", "**/cli.ts", "**/bin/**"],
154
+ // Files exempt from the rules that tests legitimately break. Helper directories such
155
+ // as `src/testing/` are support code, not tests, so they are not matched.
156
+ testFiles: [
157
+ "**/*.test.*",
158
+ "**/*.spec.*",
159
+ "**/__tests__/**",
160
+ "**/test/**",
161
+ "**/tests/**",
162
+ ],
163
+ // Fields that discriminate a closed union, compared or switched on only through an
164
+ // exhaustive match and set only by a constructor. Add the ones your unions use, such
165
+ // as `type` or `kind`.
166
+ discriminants: ["_tag"],
167
+ },
168
+ },
151
169
  // Every rule at `warn`, to measure noise without blocking.
152
170
  rules: {
153
171
  // Error channel
154
- "dl-effect/no-bare-error-in-effect": "warn",
172
+ "dl-effect/no-throw-or-primitive-failure": "warn",
155
173
  "dl-effect/try-object-form": "warn",
156
- "dl-effect/no-effect-promise": ["warn", { testFiles }],
174
+ "dl-effect/no-effect-promise": "warn",
157
175
  "dl-effect/no-parameterless-catch-all": "warn",
158
- "dl-effect/error-handling-in-effect-fn-handler": ["warn", { testFiles }],
159
- "dl-effect/all-errors-handled-at-boundary": ["warn", { testFiles }],
176
+ "dl-effect/split-success-path-from-error-handling": "warn",
160
177
  "dl-effect/anti-slop-no-manual-effect-error-tag": "warn",
161
178
  // Tagged values
162
- "dl-effect/no-manual-discriminant-comparison": ["warn", { discriminants }],
163
- "dl-effect/no-manual-discriminant-construction": ["warn", { discriminants }],
179
+ "dl-effect/no-manual-discriminant-comparison": "warn",
180
+ "dl-effect/no-manual-discriminant-construction": "warn",
164
181
  // Services & Layers
165
182
  "dl-effect/service-port-shape-only": "warn",
166
183
  "dl-effect/service-key-matches-name": "warn",
167
184
  "dl-effect/service-built-with-of": "warn",
168
- "dl-effect/provide-named-layer": ["warn", { testFiles }],
169
- "dl-effect/no-provide-on-definition": ["warn", { testFiles }],
170
- "dl-effect/anti-slop-no-service-constructor-imports": ["warn", { testFiles }],
185
+ "dl-effect/provide-named-layer": "warn",
186
+ "dl-effect/no-provide-on-definition": "warn",
187
+ "dl-effect/anti-slop-no-service-constructor-imports": "warn",
171
188
  // Runtime & entrypoint
172
- "dl-effect/run-only-in-runner": ["warn", { entryFiles, testFiles }],
173
- "dl-effect/prefer-effect-vitest": ["warn", { testFiles }],
189
+ "dl-effect/run-only-in-runner": "warn",
190
+ "dl-effect/prefer-effect-vitest": "warn",
174
191
  "dl-effect/all-errors-handled-at-boundary": "warn",
175
192
  // Design & boundaries
176
- "dl-effect/adapter-import-boundary": ["warn", { entryFiles, testFiles }],
193
+ "dl-effect/adapter-import-boundary": "warn",
177
194
  "dl-effect/no-transport-types-in-generator": ["warn", { transportTypes }],
178
195
  // Effectful functions
179
196
  "dl-effect/prefer-effect-fn": "warn",
@@ -195,11 +212,11 @@ export default defineConfig({
195
212
  })
196
213
  ```
197
214
 
198
- To change one option, extend `all` and override only the rules that use it.
215
+ To change a setting, declare it in `settings`; to change a rule option, extend `all` and override only that rule.
199
216
 
200
217
  ## Versioning
201
218
 
202
- Versions are `0.x`: any minor release may add, remove or change rules, options and messages.
219
+ Versions are `0.x`: any minor release may add, remove or change rules, options, settings and messages.
203
220
 
204
221
  Supported oxlint is the `peerDependencies` range, `^1.82.0`; CI tests the tarball against `1.82.0`. oxlint's JS plugin API is still experimental, so a newer oxlint minor may break the plugin before the range is narrowed.
205
222
 
@@ -208,9 +225,10 @@ Supported oxlint is the `peerDependencies` range, `^1.82.0`; CI tests the tarbal
208
225
  - `pnpm format`: formats with oxfmt; the only script that rewrites sources.
209
226
  - `pnpm check`: format check, typecheck, lint and tests, writing nothing. The tests are the `RuleTester`s from `oxlint/plugins-dev` with `node --test`, plus an alignment check of `RULES.md` with one section per rule.
210
227
  - `pnpm build`: compiles to `dist/` and generates `dist/presets/all.json` from `presets/all.ts` (the rule list comes from the plugin).
211
- - `pnpm pack:check`: checks the built preset, then the tarball `npm pack` would publish: only the files in `files`, no internal names, `publint --strict`.
228
+ - `pnpm pack:check`: checks the built preset, then the tarball `npm pack` would publish: only the files in `files`, no local absolute paths, `publint --strict`.
212
229
  - `pnpm smoke-test`: installs the tarball in `smoke-fixture/` with pnpm and checks the diagnostics of oxlint with each config form: `oxlint.config.ts` and `.oxlintrc.json` with the preset, `.oxlintrc.json` with the plugin by package name.
213
230
  - `pnpm build-and-pack`: `check`, `build`, `pack:check` and `smoke-test`; run it after every change.
231
+ - `pnpm release <bump>`: from a clean `main` equal to `origin/main`, runs `build-and-pack`, bumps with `npm version <bump> --preid rc` (`prerelease` for the next rc, `x.y.z` for a stable), publishes, then pushes the commit and the tag. A prerelease is `next`, and also `latest` until the first stable version; a stable version is `latest`. A failed publish undoes the bump. Without `<bump>`, prints the version in `package.json` and the npm dist-tags.
214
232
  - `pnpm build:w`: reruns `pnpm build`, preset included, on every change to `index.ts`, `rules/`, `presets/`, `shared/` or `tsconfig.build.json`; a failed build waits for the next change.
215
233
  - `pnpm consumer:link <consumer>`: after `pnpm build`, replaces the pnpm symlinks of the plugin in every workspace package of `<consumer>` with links to the checkout, so the consumer's own lint runs the checkout's rules; no tracked file of the consumer changes. `pnpm consumer:unlink <consumer>` restores them with `pnpm install --frozen-lockfile`, as does any `pnpm install` in the consumer.
216
234