@pyreon/lint 0.11.4 → 0.11.6
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.
- package/README.md +91 -91
- package/lib/analysis/cli.js.html +5406 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/cli.js +3290 -0
- package/lib/cli.js.map +1 -0
- package/lib/index.js +220 -29
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +30 -5
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +19 -19
- package/src/cache.ts +1 -1
- package/src/cli.ts +39 -28
- package/src/config/ignore.ts +23 -23
- package/src/config/loader.ts +8 -8
- package/src/config/presets.ts +11 -11
- package/src/index.ts +14 -12
- package/src/lint.ts +19 -25
- package/src/lsp/index.ts +225 -0
- package/src/reporter.ts +17 -17
- package/src/rules/accessibility/dialog-a11y.ts +10 -10
- package/src/rules/accessibility/overlay-a11y.ts +11 -11
- package/src/rules/accessibility/toast-a11y.ts +11 -11
- package/src/rules/architecture/dev-guard-warnings.ts +19 -19
- package/src/rules/architecture/no-circular-import.ts +16 -16
- package/src/rules/architecture/no-cross-layer-import.ts +35 -35
- package/src/rules/architecture/no-deep-import.ts +7 -7
- package/src/rules/architecture/no-error-without-prefix.ts +20 -20
- package/src/rules/form/no-submit-without-validation.ts +13 -13
- package/src/rules/form/no-unregistered-field.ts +12 -12
- package/src/rules/form/prefer-field-array.ts +11 -11
- package/src/rules/hooks/no-raw-addeventlistener.ts +9 -9
- package/src/rules/hooks/no-raw-localstorage.ts +11 -11
- package/src/rules/hooks/no-raw-setinterval.ts +11 -11
- package/src/rules/index.ts +60 -57
- package/src/rules/jsx/no-and-conditional.ts +8 -8
- package/src/rules/jsx/no-children-access.ts +12 -12
- package/src/rules/jsx/no-classname.ts +10 -10
- package/src/rules/jsx/no-htmlfor.ts +10 -10
- package/src/rules/jsx/no-index-as-by.ts +17 -17
- package/src/rules/jsx/no-map-in-jsx.ts +9 -9
- package/src/rules/jsx/no-missing-for-by.ts +9 -9
- package/src/rules/jsx/no-onchange.ts +12 -12
- package/src/rules/jsx/no-props-destructure.ts +11 -11
- package/src/rules/jsx/no-ternary-conditional.ts +8 -8
- package/src/rules/jsx/use-by-not-key.ts +12 -12
- package/src/rules/lifecycle/no-dom-in-setup.ts +18 -18
- package/src/rules/lifecycle/no-effect-in-mount.ts +11 -11
- package/src/rules/lifecycle/no-missing-cleanup.ts +19 -19
- package/src/rules/lifecycle/no-mount-in-effect.ts +11 -11
- package/src/rules/performance/no-eager-import.ts +7 -7
- package/src/rules/performance/no-effect-in-for.ts +10 -10
- package/src/rules/performance/no-large-for-without-by.ts +9 -9
- package/src/rules/performance/prefer-show-over-display.ts +16 -16
- package/src/rules/reactivity/no-bare-signal-in-jsx.ts +10 -10
- package/src/rules/reactivity/no-context-destructure.ts +45 -0
- package/src/rules/reactivity/no-effect-assignment.ts +16 -16
- package/src/rules/reactivity/no-nested-effect.ts +10 -10
- package/src/rules/reactivity/no-peek-in-tracked.ts +10 -10
- package/src/rules/reactivity/no-signal-in-loop.ts +13 -13
- package/src/rules/reactivity/no-signal-leak.ts +9 -9
- package/src/rules/reactivity/no-unbatched-updates.ts +12 -12
- package/src/rules/reactivity/prefer-computed.ts +13 -13
- package/src/rules/router/index.ts +4 -4
- package/src/rules/router/no-href-navigation.ts +14 -14
- package/src/rules/router/no-imperative-navigate-in-render.ts +19 -19
- package/src/rules/router/no-missing-fallback.ts +16 -16
- package/src/rules/router/prefer-use-is-active.ts +11 -11
- package/src/rules/ssr/no-mismatch-risk.ts +11 -11
- package/src/rules/ssr/no-window-in-ssr.ts +22 -22
- package/src/rules/ssr/prefer-request-context.ts +14 -14
- package/src/rules/store/no-duplicate-store-id.ts +9 -9
- package/src/rules/store/no-mutate-store-state.ts +11 -11
- package/src/rules/store/no-store-outside-provider.ts +15 -15
- package/src/rules/styling/no-dynamic-styled.ts +13 -13
- package/src/rules/styling/no-inline-style-object.ts +10 -10
- package/src/rules/styling/no-theme-outside-provider.ts +11 -11
- package/src/rules/styling/prefer-cx.ts +12 -12
- package/src/runner.ts +13 -14
- package/src/tests/lsp.test.ts +88 -0
- package/src/tests/runner.test.ts +325 -325
- package/src/types.ts +15 -15
- package/src/utils/ast.ts +50 -50
- package/src/utils/imports.ts +53 -53
- package/src/utils/index.ts +12 -3
- package/src/utils/source.ts +2 -2
- package/src/watcher.ts +19 -25
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { getPreset } from '../config/presets'
|
|
2
|
+
import { allRules } from '../rules/index'
|
|
3
|
+
import { lintFile } from '../runner'
|
|
4
|
+
|
|
5
|
+
describe('LSP diagnostic conversion', () => {
|
|
6
|
+
const config = getPreset('recommended')
|
|
7
|
+
|
|
8
|
+
it('converts lint diagnostics to LSP format', () => {
|
|
9
|
+
const source = `
|
|
10
|
+
import { useContext } from '@pyreon/core'
|
|
11
|
+
const { mode } = useContext(ThemeCtx)
|
|
12
|
+
`
|
|
13
|
+
const result = lintFile('test.tsx', source, allRules, config)
|
|
14
|
+
const diag = result.diagnostics.find(
|
|
15
|
+
(d) => d.ruleId === 'pyreon/no-context-destructure',
|
|
16
|
+
)
|
|
17
|
+
expect(diag).toBeDefined()
|
|
18
|
+
expect(diag!.loc.line).toBeGreaterThan(0)
|
|
19
|
+
expect(diag!.loc.column).toBeGreaterThan(0)
|
|
20
|
+
expect(diag!.span.start).toBeLessThan(diag!.span.end)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('LSP severity mapping — error=1, warn=2, info=3', () => {
|
|
24
|
+
const severityMap: Record<string, number> = {
|
|
25
|
+
error: 1,
|
|
26
|
+
warn: 2,
|
|
27
|
+
info: 3,
|
|
28
|
+
}
|
|
29
|
+
expect(severityMap.error).toBe(1)
|
|
30
|
+
expect(severityMap.warn).toBe(2)
|
|
31
|
+
expect(severityMap.info).toBe(3)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('source diagnostics use 1-based lines (LSP layer converts to 0-based)', () => {
|
|
35
|
+
const source = `import { signal } from '@pyreon/reactivity'\nconst x = signal(0)\n{x}\n`
|
|
36
|
+
const result = lintFile('test.tsx', source, allRules, config)
|
|
37
|
+
|
|
38
|
+
for (const d of result.diagnostics) {
|
|
39
|
+
expect(d.loc.line).toBeGreaterThanOrEqual(1)
|
|
40
|
+
expect(d.loc.column).toBeGreaterThanOrEqual(1)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('handles empty source without crashing', () => {
|
|
45
|
+
const result = lintFile('test.tsx', '', allRules, config)
|
|
46
|
+
expect(result.diagnostics).toEqual([])
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('handles non-JS file gracefully', () => {
|
|
50
|
+
const result = lintFile('test.css', 'body { color: red }', allRules, config)
|
|
51
|
+
expect(result.diagnostics).toEqual([])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('handles syntax errors gracefully', () => {
|
|
55
|
+
const result = lintFile('test.tsx', 'const x = {{{', allRules, config)
|
|
56
|
+
// Should not throw — returns empty or partial diagnostics
|
|
57
|
+
expect(result.diagnostics).toBeDefined()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('multiple diagnostics are sorted by position', () => {
|
|
61
|
+
const source = `
|
|
62
|
+
import { useContext } from '@pyreon/core'
|
|
63
|
+
const { a } = useContext(Ctx1)
|
|
64
|
+
const { b } = useContext(Ctx2)
|
|
65
|
+
`
|
|
66
|
+
const result = lintFile('test.tsx', source, allRules, config)
|
|
67
|
+
const ctxDiags = result.diagnostics.filter(
|
|
68
|
+
(d) => d.ruleId === 'pyreon/no-context-destructure',
|
|
69
|
+
)
|
|
70
|
+
expect(ctxDiags.length).toBe(2)
|
|
71
|
+
expect(ctxDiags[0]!.span.start).toBeLessThan(ctxDiags[1]!.span.start)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('diagnostic span covers the destructured pattern', () => {
|
|
75
|
+
const source = `
|
|
76
|
+
import { useContext } from '@pyreon/core'
|
|
77
|
+
const { mode, theme } = useContext(ThemeCtx)
|
|
78
|
+
`
|
|
79
|
+
const result = lintFile('test.tsx', source, allRules, config)
|
|
80
|
+
const diag = result.diagnostics.find(
|
|
81
|
+
(d) => d.ruleId === 'pyreon/no-context-destructure',
|
|
82
|
+
)
|
|
83
|
+
expect(diag).toBeDefined()
|
|
84
|
+
const spanned = source.slice(diag!.span.start, diag!.span.end)
|
|
85
|
+
expect(spanned).toContain('mode')
|
|
86
|
+
expect(spanned).toContain('theme')
|
|
87
|
+
})
|
|
88
|
+
})
|