@pyreon/lint 0.12.0 → 0.12.2
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/lib/analysis/cli.js.html +1 -1
- package/lib/analysis/index.js.html +1 -1
- package/lib/cli.js +54 -2
- package/lib/cli.js.map +1 -1
- package/lib/index.js +54 -2
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/rules/index.ts +4 -1
- package/src/rules/reactivity/no-signal-in-props.ts +53 -0
- package/src/runner.ts +15 -2
- package/src/tests/runner.test.ts +3 -3
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/types.ts","../../../src/utils/source.ts","../../../src/cache.ts","../../../src/config/ignore.ts","../../../src/config/loader.ts","../../../src/config/presets.ts","../../../src/lint.ts","../../../src/reporter.ts","../../../src/lsp/index.ts","../../../src/rules/index.ts","../../../src/runner.ts","../../../src/utils/imports.ts","../../../src/watcher.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/types.ts","../../../src/utils/source.ts","../../../src/cache.ts","../../../src/config/ignore.ts","../../../src/config/loader.ts","../../../src/config/presets.ts","../../../src/lint.ts","../../../src/reporter.ts","../../../src/lsp/index.ts","../../../src/rules/index.ts","../../../src/runner.ts","../../../src/utils/imports.ts","../../../src/watcher.ts"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/lint",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "Pyreon-specific linter — 56 rules for signals, JSX, SSR, performance, router, and architecture",
|
|
5
5
|
"homepage": "https://github.com/pyreon/pyreon/tree/main/packages/lint#readme",
|
|
6
6
|
"bugs": {
|
package/src/rules/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ import { noEffectAssignment } from './reactivity/no-effect-assignment'
|
|
|
46
46
|
import { noNestedEffect } from './reactivity/no-nested-effect'
|
|
47
47
|
import { noPeekInTracked } from './reactivity/no-peek-in-tracked'
|
|
48
48
|
import { noSignalInLoop } from './reactivity/no-signal-in-loop'
|
|
49
|
+
import { noSignalInProps } from './reactivity/no-signal-in-props'
|
|
49
50
|
import { noSignalLeak } from './reactivity/no-signal-leak'
|
|
50
51
|
import { noUnbatchedUpdates } from './reactivity/no-unbatched-updates'
|
|
51
52
|
import { preferComputed } from './reactivity/prefer-computed'
|
|
@@ -69,10 +70,11 @@ import { noThemeOutsideProvider } from './styling/no-theme-outside-provider'
|
|
|
69
70
|
import { preferCx } from './styling/prefer-cx'
|
|
70
71
|
|
|
71
72
|
export const allRules: Rule[] = [
|
|
72
|
-
// Reactivity (
|
|
73
|
+
// Reactivity (10)
|
|
73
74
|
noBareSignalInJsx,
|
|
74
75
|
noContextDestructure,
|
|
75
76
|
noSignalInLoop,
|
|
77
|
+
noSignalInProps,
|
|
76
78
|
noNestedEffect,
|
|
77
79
|
noPeekInTracked,
|
|
78
80
|
noUnbatchedUpdates,
|
|
@@ -187,6 +189,7 @@ export {
|
|
|
187
189
|
noRawLocalStorage,
|
|
188
190
|
noRawSetInterval,
|
|
189
191
|
noSignalInLoop,
|
|
192
|
+
noSignalInProps,
|
|
190
193
|
noSignalLeak,
|
|
191
194
|
// Store
|
|
192
195
|
noStoreOutsideProvider,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Rule, VisitorCallbacks } from '../../types'
|
|
2
|
+
import { getSpan } from '../../utils/ast'
|
|
3
|
+
|
|
4
|
+
function isComponentTag(name: string): boolean {
|
|
5
|
+
return name.length > 0 && name[0] === name[0]?.toUpperCase() && name[0] !== name[0]?.toLowerCase()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Warn when a known signal/computed is called in a component prop position.
|
|
10
|
+
* Component props are evaluated once at mount — signal reads are NOT reactive
|
|
11
|
+
* unless the compiler wraps them with _rp(). The compiler handles this
|
|
12
|
+
* automatically, but this rule catches manual h() calls and educates developers.
|
|
13
|
+
*/
|
|
14
|
+
export const noSignalInProps: Rule = {
|
|
15
|
+
meta: {
|
|
16
|
+
id: 'pyreon/no-signal-in-props',
|
|
17
|
+
category: 'reactivity',
|
|
18
|
+
description:
|
|
19
|
+
'Signal call in component prop — value captured once unless compiler wraps it. Use props.x pattern for reactivity.',
|
|
20
|
+
severity: 'warn',
|
|
21
|
+
fixable: false,
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
const callbacks: VisitorCallbacks = {
|
|
25
|
+
JSXExpressionContainer(node: any) {
|
|
26
|
+
const expr = node.expression
|
|
27
|
+
if (!expr || expr.type !== 'CallExpression') return
|
|
28
|
+
const callee = expr.callee
|
|
29
|
+
if (!callee || callee.type !== 'Identifier') return
|
|
30
|
+
|
|
31
|
+
const source = context.getSourceText()
|
|
32
|
+
const start = node.start as number
|
|
33
|
+
|
|
34
|
+
let i = start - 1
|
|
35
|
+
while (i >= 0 && source[i] !== '<' && source[i] !== '>') i--
|
|
36
|
+
if (i < 0 || source[i] !== '<') return
|
|
37
|
+
|
|
38
|
+
const tagStart = i + 1
|
|
39
|
+
let tagEnd = tagStart
|
|
40
|
+
while (tagEnd < source.length && /[\w.]/.test(source[tagEnd] ?? '')) tagEnd++
|
|
41
|
+
const tagName = source.slice(tagStart, tagEnd)
|
|
42
|
+
|
|
43
|
+
if (!tagName || !isComponentTag(tagName)) return
|
|
44
|
+
|
|
45
|
+
context.report({
|
|
46
|
+
message: `Signal call in <${tagName}> prop — use props.x pattern inside the component for reactive access.`,
|
|
47
|
+
span: getSpan(expr),
|
|
48
|
+
})
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
return callbacks
|
|
52
|
+
},
|
|
53
|
+
}
|
package/src/runner.ts
CHANGED
|
@@ -138,8 +138,21 @@ export function lintFile(
|
|
|
138
138
|
const visitor = new Visitor(mergeCallbacks(allCallbacks))
|
|
139
139
|
visitor.visit(program)
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
// Filter suppressed diagnostics:
|
|
142
|
+
// // pyreon-lint-ignore — suppress all on next line
|
|
143
|
+
// // pyreon-lint-ignore rule-name — suppress specific rule on next line
|
|
144
|
+
const lines = sourceText.split('\n')
|
|
145
|
+
const filtered = diagnostics.filter((d) => {
|
|
146
|
+
const prevLineIdx = d.loc.line - 2
|
|
147
|
+
if (prevLineIdx < 0) return true
|
|
148
|
+
const prevLine = lines[prevLineIdx]?.trim()
|
|
149
|
+
if (!prevLine?.startsWith('// pyreon-lint-ignore')) return true
|
|
150
|
+
const rest = prevLine.slice('// pyreon-lint-ignore'.length).trim()
|
|
151
|
+
return rest.length > 0 && rest !== d.ruleId
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
filtered.sort((a, b) => a.span.start - b.span.start)
|
|
155
|
+
return { filePath, diagnostics: filtered }
|
|
143
156
|
}
|
|
144
157
|
|
|
145
158
|
/**
|
package/src/tests/runner.test.ts
CHANGED
|
@@ -37,7 +37,7 @@ function lintWith(ruleId: string, source: string, filePath?: string) {
|
|
|
37
37
|
|
|
38
38
|
describe('Rule metadata', () => {
|
|
39
39
|
it('should have 56 rules', () => {
|
|
40
|
-
expect(allRules.length).toBe(
|
|
40
|
+
expect(allRules.length).toBe(57)
|
|
41
41
|
})
|
|
42
42
|
|
|
43
43
|
it('should have unique rule IDs', () => {
|
|
@@ -77,7 +77,7 @@ describe('Rule metadata', () => {
|
|
|
77
77
|
for (const rule of allRules) {
|
|
78
78
|
counts[rule.meta.category] = (counts[rule.meta.category] ?? 0) + 1
|
|
79
79
|
}
|
|
80
|
-
expect(counts.reactivity).toBe(
|
|
80
|
+
expect(counts.reactivity).toBe(10)
|
|
81
81
|
expect(counts.jsx).toBe(11)
|
|
82
82
|
expect(counts.lifecycle).toBe(4)
|
|
83
83
|
expect(counts.performance).toBe(4)
|
|
@@ -1013,7 +1013,7 @@ describe('Ignore filter', () => {
|
|
|
1013
1013
|
describe('Presets', () => {
|
|
1014
1014
|
it('recommended should include all rules', () => {
|
|
1015
1015
|
const config = getPreset('recommended')
|
|
1016
|
-
expect(Object.keys(config.rules).length).toBe(
|
|
1016
|
+
expect(Object.keys(config.rules).length).toBe(57)
|
|
1017
1017
|
})
|
|
1018
1018
|
|
|
1019
1019
|
it('strict should promote all warns to errors', () => {
|