@rdjperron/lint 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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -0
  3. package/dist/anti-slop.d.mts +6 -0
  4. package/dist/anti-slop.mjs +1694 -0
  5. package/dist/anti-slop.mjs.map +1 -0
  6. package/dist/expo.d.mts +5 -0
  7. package/dist/expo.mjs +51 -0
  8. package/dist/expo.mjs.map +1 -0
  9. package/dist/i18n.d.mts +5 -0
  10. package/dist/i18n.mjs +50 -0
  11. package/dist/i18n.mjs.map +1 -0
  12. package/dist/react.d.mts +5 -0
  13. package/dist/react.mjs +73 -0
  14. package/dist/react.mjs.map +1 -0
  15. package/dist/rule-options-DrAXz8BD.mjs +18 -0
  16. package/dist/rule-options-DrAXz8BD.mjs.map +1 -0
  17. package/fixtures/README.md +63 -0
  18. package/fixtures/anti-slop/no-array-filter-map.fixture.ts +4 -0
  19. package/fixtures/anti-slop/no-chained-type-assertions.fixture.ts +7 -0
  20. package/fixtures/anti-slop/no-conditional-empty-object-spread.fixture.ts +4 -0
  21. package/fixtures/anti-slop/no-known-value-widening.fixture.ts +2 -0
  22. package/fixtures/anti-slop/no-module-mocking.fixture.ts +6 -0
  23. package/fixtures/anti-slop/no-object-parameters.fixture.ts +4 -0
  24. package/fixtures/anti-slop/no-reduce-accumulator-copy.fixture.ts +4 -0
  25. package/fixtures/anti-slop/no-reflect-apply.fixture.ts +6 -0
  26. package/fixtures/anti-slop/no-reflect-get.fixture.ts +4 -0
  27. package/fixtures/anti-slop/no-runtime-typeof.fixture.ts +4 -0
  28. package/fixtures/anti-slop/no-shape-in-symbol-names.fixture.ts +2 -0
  29. package/fixtures/anti-slop/no-unknown-parameters.fixture.ts +4 -0
  30. package/fixtures/anti-slop/no-unknown-returns.fixture.ts +4 -0
  31. package/fixtures/anti-slop/no-unknown-type-aliases.fixture.ts +2 -0
  32. package/fixtures/anti-slop/no-unsafe-dictionary-type.fixture.ts +2 -0
  33. package/fixtures/anti-slop/no-widen-then-assert.fixture.ts +9 -0
  34. package/fixtures/anti-slop/require-safety-comment-for-type-assertion.fixture.ts +5 -0
  35. package/fixtures/expo/static-public-env.fixture.ts +4 -0
  36. package/fixtures/i18n/no-hardcoded-jsx-text.fixture.tsx +4 -0
  37. package/fixtures/react/no-use-effect.fixture.tsx +9 -0
  38. package/package.json +50 -0
@@ -0,0 +1,18 @@
1
+ //#region src/shared/rule-options.ts
2
+ /**
3
+ * Rule options arrive as parsed JSON from the linter config, so this is the one
4
+ * place their shape is checked. Returns the first option when it is an object.
5
+ */
6
+ function firstOptionObject(options) {
7
+ const first = options[0];
8
+ return typeof first === "object" && first !== null && !Array.isArray(first) ? first : {};
9
+ }
10
+ /** A string-valued key of the first option object, or undefined when absent or not a string. */
11
+ function stringOption(options, key) {
12
+ const value = firstOptionObject(options)[key];
13
+ return typeof value === "string" ? value : void 0;
14
+ }
15
+ //#endregion
16
+ export { stringOption as t };
17
+
18
+ //# sourceMappingURL=rule-options-DrAXz8BD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-options-DrAXz8BD.mjs","names":[],"sources":["../src/shared/rule-options.ts"],"sourcesContent":["import type { Options } from \"@oxlint/plugins\";\n\ntype OptionValue = Options[number];\n\n/**\n * Rule options arrive as parsed JSON from the linter config, so this is the one\n * place their shape is checked. Returns the first option when it is an object.\n */\nfunction firstOptionObject(options: Readonly<Options>): { readonly [key: string]: OptionValue } {\n const first = options[0];\n return typeof first === \"object\" && first !== null && !Array.isArray(first) ? first : {};\n}\n\n/** A string-valued key of the first option object, or undefined when absent or not a string. */\nexport function stringOption(options: Readonly<Options>, key: string): string | undefined {\n const value = firstOptionObject(options)[key];\n return typeof value === \"string\" ? value : undefined;\n}\n"],"mappings":";;;;;AAQA,SAAS,kBAAkB,SAAqE;CAC9F,MAAM,QAAQ,QAAQ;CACtB,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AACzF;;AAGA,SAAgB,aAAa,SAA4B,KAAiC;CACxF,MAAM,QAAQ,kBAAkB,OAAO,CAAC,CAAC;CACzC,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C"}
@@ -0,0 +1,63 @@
1
+ # fixtures: load-bearing test data, do not delete
2
+
3
+ One deliberately violating file per rule, in one directory per pack:
4
+ `anti-slop/<rule>.fixture.ts`, `react/no-use-effect.fixture.tsx`,
5
+ `i18n/no-hardcoded-jsx-text.fixture.tsx` and `expo/static-public-env.fixture.ts`. Each file trips
6
+ exactly the rule it is named after, and nothing else.
7
+
8
+ These ship in the published package (`files` in `package.json`), so a consumer proves the rules
9
+ fire through its own linter, not ours.
10
+
11
+ ## Why this exists
12
+
13
+ The linter lives inside the consumer's toolchain (under Vite+, inside the `vp` binary) and moves
14
+ independently of any lockfile. The rules run through oxlint's JS-plugin bridge. A rule can stop
15
+ firing with no diff anywhere: after a linter upgrade, after a plugin API skew, after an ignore
16
+ pattern swallows the wrong path.
17
+
18
+ And a dead lint rule is silent. "Rule reports nothing" looks identical to "code has no
19
+ violations". Nothing else would notice.
20
+
21
+ The source repo's `scripts/verify-lint-fixtures.mjs` (in CI and in `vp run ready`) lints each
22
+ pack's fixtures and asserts a positive: every rule must fire, each on its own fixture. If a rule
23
+ goes dead, the run fails loudly instead of going quietly green.
24
+
25
+ ## How it relates to the unit tests
26
+
27
+ Two layers, different questions:
28
+
29
+ | Layer | Question it answers | Where |
30
+ | -------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
31
+ | This directory + a verify script | Does each rule fire through the real pipeline: the bundled oxlint, the `jsPlugins` wiring, the actual config? | `fixtures/`, the consumer's `scripts/verify-lint-fixtures.mjs` |
32
+ | Unit tests (RuleTester) | Is each rule's logic right? Valid cases stay quiet, invalid cases report, with per-case precision. | `tests/` in the source repo (not published) |
33
+
34
+ The unit tests catch a locally edited rule that starts over- or under-firing. This layer catches
35
+ the rule not running at all. Both are needed.
36
+
37
+ ## Rules of the house
38
+
39
+ - Do not delete or "clean up" these files. They are the regression proof for rules nobody
40
+ upstream maintains for us.
41
+ - In the source repo they are in `lint.ignorePatterns` and `fmt.ignorePatterns`, so a plain
42
+ `vp check` stays quiet. The verify script reaches them through a transient symlink at
43
+ `scripts/.lint-fixtures/<pack>`, because no `vp lint` flag re-includes a config-ignored path and
44
+ oxlint skips `node_modules` by default.
45
+ - Never gitignore that view. oxlint honours `.gitignore`, so gitignoring anything on the
46
+ verification path silently disables the whole check (this happened once; the script now detects
47
+ it with `git check-ignore` and says so).
48
+ - Name fixtures `*.fixture.ts` / `*.fixture.tsx`, never `*.test.ts`, or Vitest collects them and
49
+ the deliberately broken code fails the test run.
50
+ - One rule per file. A fixture that trips two rules makes the per-rule assertion ambiguous.
51
+ - Expected advisory noise in the source repo: the type-aware pass also reports `typescript(TS…)`
52
+ on the view, because no tsconfig covers `scripts/.lint-fixtures/` (no React types, no `process`
53
+ types, no `jsx` setting). The script lists those as non-pack diagnostics rather than hiding
54
+ them; only a pack rule that did not fire fails the run.
55
+
56
+ ## When a rule stops firing after a `vp` upgrade
57
+
58
+ 1. `vp toolchain oxlint`: did the bundled oxlint move?
59
+ 2. `vp why @oxlint/plugins`: if `vite-plus` now declares a different version, move the exact
60
+ pin to it and reinstall. In the source repo the parity test in `tests/` fails until you do.
61
+ 3. Re-run the verify script.
62
+ 4. Still dead? The rule's AST expectations may have changed under the new oxc version. Read the
63
+ rule in `src/<pack>/rules/`, fix it, add the case to `tests/rules/<pack>/`, and release.
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-array-filter-map.
2
+ export function activeEmails(users: { active: boolean; email: string }[]): string[] {
3
+ return users.filter((user) => user.active).map((user) => user.email);
4
+ }
@@ -0,0 +1,7 @@
1
+ // Fixture for anti-slop/no-chained-type-assertions.
2
+ const rawCount = "12";
3
+
4
+ // SAFETY: fixture file; the assertion chain is the behaviour under test.
5
+ const parsedCount = rawCount as unknown as number;
6
+
7
+ export { parsedCount };
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-conditional-empty-object-spread.
2
+ const includeExtra = true;
3
+
4
+ export const requestBody = { name: "widget", ...(includeExtra ? { extra: 1 } : {}) };
@@ -0,0 +1,2 @@
1
+ // Fixture for anti-slop/no-known-value-widening.
2
+ export const retrySettings: unknown = { retries: 3 };
@@ -0,0 +1,6 @@
1
+ // Fixture for anti-slop/no-module-mocking.
2
+ // `vi` is deliberately left undeclared: the rule's global/unresolved-reference
3
+ // path is what recognises `vi.mock` without importing from "vitest" (which
4
+ // `vite-plus/prefer-vite-plus-imports` forbids in this repo).
5
+ // @ts-expect-error -- `vi` is intentionally not declared in this fixture.
6
+ vi.mock("./clock.ts");
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-object-parameters.
2
+ export function serialize(payload: object): string {
3
+ return JSON.stringify(payload);
4
+ }
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-reduce-accumulator-copy.
2
+ export function indexById(items: { id: string }[]): Record<string, { id: string }> {
3
+ return items.reduce((acc, item) => Object.assign({}, acc, { [item.id]: item }), {});
4
+ }
@@ -0,0 +1,6 @@
1
+ // Fixture for anti-slop/no-reflect-apply.
2
+ function add(left: number, right: number): number {
3
+ return left + right;
4
+ }
5
+
6
+ export const total = Reflect.apply(add, undefined, [1, 2]);
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-reflect-get.
2
+ const entry = { id: "a" };
3
+
4
+ export const identifier = Reflect.get(entry, "id");
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-runtime-typeof.
2
+ export function describeValue(value: string | number): string {
3
+ return typeof value === "string" ? "text" : "number";
4
+ }
@@ -0,0 +1,2 @@
1
+ // Fixture for anti-slop/no-shape-in-symbol-names.
2
+ export const shapeCount = 1;
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-unknown-parameters.
2
+ export function record(payload: unknown): string {
3
+ return String(payload);
4
+ }
@@ -0,0 +1,4 @@
1
+ // Fixture for anti-slop/no-unknown-returns.
2
+ export function load(serialized: string): unknown {
3
+ return JSON.parse(serialized);
4
+ }
@@ -0,0 +1,2 @@
1
+ // Fixture for anti-slop/no-unknown-type-aliases.
2
+ export type Payload = unknown;
@@ -0,0 +1,2 @@
1
+ // Fixture for anti-slop/no-unsafe-dictionary-type.
2
+ export type Registry = Record<string, unknown>;
@@ -0,0 +1,9 @@
1
+ // Fixture for anti-slop/no-widen-then-assert.
2
+ function project(entry: { id: string }): string {
3
+ const widened: unknown = entry;
4
+ // SAFETY: fixture file; the widen-then-assert flow is the behaviour under test.
5
+ const narrowed = widened as { id: string };
6
+ return narrowed.id;
7
+ }
8
+
9
+ export const projected = project({ id: "a" });
@@ -0,0 +1,5 @@
1
+ // Fixture for anti-slop/require-safety-comment-for-type-assertion.
2
+ export function lengthOf(entry: { id: string }): number {
3
+ const widerEntry = entry as { id: string; extra?: number };
4
+ return widerEntry.id.length;
5
+ }
@@ -0,0 +1,4 @@
1
+ // Fixture for expo/static-public-env.
2
+ const { EXPO_PUBLIC_API_URL } = process.env;
3
+
4
+ export const apiUrl = EXPO_PUBLIC_API_URL;
@@ -0,0 +1,4 @@
1
+ // Fixture for i18n/no-hardcoded-jsx-text.
2
+ export function Fixture() {
3
+ return <button type="button">Save changes</button>;
4
+ }
@@ -0,0 +1,9 @@
1
+ // Fixture for react-policy/no-use-effect.
2
+ import { useEffect } from "react";
3
+
4
+ export function Fixture({ title }: { title: string }) {
5
+ useEffect(() => {
6
+ document.title = title;
7
+ }, [title]);
8
+ return null;
9
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@rdjperron/lint",
3
+ "version": "0.1.0",
4
+ "description": "Oxlint JS plugins: the anti-slop, react-policy, i18n and expo rule packs, shipped with the fixture that proves each rule fires.",
5
+ "license": "MIT",
6
+ "files": [
7
+ "dist",
8
+ "fixtures",
9
+ "README.md"
10
+ ],
11
+ "type": "module",
12
+ "exports": {
13
+ "./anti-slop": {
14
+ "types": "./dist/anti-slop.d.mts",
15
+ "default": "./dist/anti-slop.mjs"
16
+ },
17
+ "./react": {
18
+ "types": "./dist/react.d.mts",
19
+ "default": "./dist/react.mjs"
20
+ },
21
+ "./i18n": {
22
+ "types": "./dist/i18n.d.mts",
23
+ "default": "./dist/i18n.mjs"
24
+ },
25
+ "./expo": {
26
+ "types": "./dist/expo.d.mts",
27
+ "default": "./dist/expo.mjs"
28
+ },
29
+ "./fixtures/*": "./fixtures/*",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "devDependencies": {
33
+ "@oxlint/plugins": "1.79.0",
34
+ "@types/node": "^24",
35
+ "typescript": "^7.0.2",
36
+ "vite": "npm:@voidzero-dev/vite-plus-core@0.3.1",
37
+ "vite-plus": "0.3.1"
38
+ },
39
+ "peerDependencies": {
40
+ "@oxlint/plugins": "^1.79.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=24"
44
+ },
45
+ "scripts": {
46
+ "build": "vp pack",
47
+ "test": "vp test",
48
+ "check": "vp check"
49
+ }
50
+ }