@openuji/speculator-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.
- package/.speculatorlintrc.example.json +9 -0
- package/README.md +186 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +125 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +96 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/linter.d.ts +34 -0
- package/dist/linter.d.ts.map +1 -0
- package/dist/linter.js +78 -0
- package/dist/linter.js.map +1 -0
- package/dist/rule-runner.d.ts +12 -0
- package/dist/rule-runner.d.ts.map +1 -0
- package/dist/rule-runner.js +129 -0
- package/dist/rule-runner.js.map +1 -0
- package/dist/rules/index.d.ts +15 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +21 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/workspace/no-redefinition.d.ts +13 -0
- package/dist/rules/workspace/no-redefinition.d.ts.map +1 -0
- package/dist/rules/workspace/no-redefinition.js +61 -0
- package/dist/rules/workspace/no-redefinition.js.map +1 -0
- package/dist/rules/workspace/no-reverse-dependency.d.ts +13 -0
- package/dist/rules/workspace/no-reverse-dependency.d.ts.map +1 -0
- package/dist/rules/workspace/no-reverse-dependency.js +46 -0
- package/dist/rules/workspace/no-reverse-dependency.js.map +1 -0
- package/dist/types.d.ts +146 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +14 -0
- package/dist/utils.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# @openuji/speculator-lint
|
|
2
|
+
|
|
3
|
+
Standalone linter for [Speculator](../speculator) workspace AST with configurable validation rules.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add -D @openuji/speculator-lint
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### CLI
|
|
14
|
+
|
|
15
|
+
Lint a workspace AST file:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
speculator-lint workspace.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
With custom configuration:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
speculator-lint workspace.json --config .speculatorlintrc.json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Programmatic API
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { readFileSync } from 'fs';
|
|
31
|
+
import { SpeculatorLinter, builtInRules, recommendedConfig } from '@openuji/speculator-lint';
|
|
32
|
+
|
|
33
|
+
// Load workspace AST
|
|
34
|
+
const workspace = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
35
|
+
|
|
36
|
+
// Build document levels map
|
|
37
|
+
const documentLevels = new Map();
|
|
38
|
+
workspace.documents.forEach((doc, index) => {
|
|
39
|
+
documentLevels.set(doc.sourcePos?.file || '', index);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Create linter with built-in rules
|
|
43
|
+
const linter = new SpeculatorLinter(builtInRules);
|
|
44
|
+
|
|
45
|
+
// Run linter
|
|
46
|
+
const result = await linter.lint({
|
|
47
|
+
workspace,
|
|
48
|
+
documentLevels,
|
|
49
|
+
config: recommendedConfig
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Check results
|
|
53
|
+
if (result.hasErrors) {
|
|
54
|
+
for (const diagnostic of result.diagnostics) {
|
|
55
|
+
console.error(diagnostic.message);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
Create a `.speculatorlintrc.json` file in your project root:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"extends": ["recommended"],
|
|
67
|
+
"rules": {
|
|
68
|
+
"workspace/no-redefinition": "error",
|
|
69
|
+
"workspace/no-reverse-dependency": "warning"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Rule Configuration
|
|
75
|
+
|
|
76
|
+
Rules can be configured with:
|
|
77
|
+
- `"off"` - Disable the rule
|
|
78
|
+
- `"error"` - Report as error
|
|
79
|
+
- `"warning"` - Report as warning
|
|
80
|
+
- `"info"` - Report as info
|
|
81
|
+
|
|
82
|
+
### Extends
|
|
83
|
+
|
|
84
|
+
Use `"extends": ["recommended"]` to enable all built-in rules with their default severities.
|
|
85
|
+
|
|
86
|
+
## Built-in Rules
|
|
87
|
+
|
|
88
|
+
### workspace/no-redefinition
|
|
89
|
+
|
|
90
|
+
Ensures that lower-level specs do not redefine concepts from higher-level specs.
|
|
91
|
+
|
|
92
|
+
**Rationale:** In a hierarchical specification system, higher-level specs define the core vocabulary. Lower-level specs should extend, not override these definitions.
|
|
93
|
+
|
|
94
|
+
**Example violation:**
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
core.md (level 0): defines "User"
|
|
98
|
+
extension.md (level 1): defines "User" again ← ERROR
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### workspace/no-reverse-dependency
|
|
102
|
+
|
|
103
|
+
Ensures that higher-level specs do not depend on (reference) lower-level specs.
|
|
104
|
+
|
|
105
|
+
**Rationale:** Dependencies should flow downward in the hierarchy. Higher-level specs should be self-contained and not rely on lower-level implementation details.
|
|
106
|
+
|
|
107
|
+
**Example violation:**
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
core.md (level 0): references "DetailedConfig"
|
|
111
|
+
extension.md (level 1): defines "DetailedConfig" ← ERROR
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Creating Custom Rules
|
|
115
|
+
|
|
116
|
+
You can create custom rules by implementing the `LintRule` interface:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import type { LintRule } from '@openuji/speculator-lint';
|
|
120
|
+
|
|
121
|
+
export const myCustomRule: LintRule = {
|
|
122
|
+
meta: {
|
|
123
|
+
name: 'my-custom-rule',
|
|
124
|
+
code: 'my-custom-rule',
|
|
125
|
+
severity: 'warning',
|
|
126
|
+
description: 'My custom validation rule',
|
|
127
|
+
category: 'custom'
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
create(context) {
|
|
131
|
+
return {
|
|
132
|
+
// Called for each definition
|
|
133
|
+
onDefinition(entry, allEntriesForTerm) {
|
|
134
|
+
// Your validation logic
|
|
135
|
+
if (/* some condition */) {
|
|
136
|
+
context.report({
|
|
137
|
+
message: 'Issue found',
|
|
138
|
+
file: entry.sourcePos?.file,
|
|
139
|
+
sourcePos: entry.sourcePos
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// Called for each reference
|
|
145
|
+
onReference(ref, target) {
|
|
146
|
+
// Your validation logic
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// Called once per document
|
|
150
|
+
onDocument(doc) {
|
|
151
|
+
// Your validation logic
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Use with linter
|
|
158
|
+
const linter = new SpeculatorLinter([...builtInRules, myCustomRule]);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## API Reference
|
|
162
|
+
|
|
163
|
+
### SpeculatorLinter
|
|
164
|
+
|
|
165
|
+
Main linter class.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
class SpeculatorLinter {
|
|
169
|
+
constructor(rules: LintRule[]);
|
|
170
|
+
lint(options: LintOptions): Promise<LintResult>;
|
|
171
|
+
getRules(): LintRule[];
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Types
|
|
176
|
+
|
|
177
|
+
- `LintRule` - Rule interface
|
|
178
|
+
- `LintContext` - Context provided to rules
|
|
179
|
+
- `LintVisitor` - Visitor pattern for AST traversal
|
|
180
|
+
- `LintDiagnostic` - Diagnostic output
|
|
181
|
+
- `LintResult` - Lint result with diagnostics
|
|
182
|
+
- `LintConfig` - Configuration schema
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;GAKG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for speculator-lint
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* speculator-lint <workspace.json> [--config <path>]
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from 'fs';
|
|
9
|
+
import { resolve } from 'path';
|
|
10
|
+
import { SpeculatorLinter } from './linter.js';
|
|
11
|
+
import { builtInRules } from './rules/index.js';
|
|
12
|
+
import { loadConfig, loadConfigFromDefaults, recommendedConfig } from './config.js';
|
|
13
|
+
function parseArgs() {
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const result = {
|
|
16
|
+
workspacePath: '',
|
|
17
|
+
};
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
const arg = args[i];
|
|
20
|
+
if (arg === '--help' || arg === '-h') {
|
|
21
|
+
result.help = true;
|
|
22
|
+
}
|
|
23
|
+
else if (arg === '--config' || arg === '-c') {
|
|
24
|
+
result.configPath = args[++i];
|
|
25
|
+
}
|
|
26
|
+
else if (!arg.startsWith('-')) {
|
|
27
|
+
result.workspacePath = arg;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
function showHelp() {
|
|
33
|
+
console.log(`
|
|
34
|
+
speculator-lint - Lint Speculator workspace AST
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
speculator-lint <workspace.json> [options]
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
--config, -c <path> Path to configuration file
|
|
41
|
+
--help, -h Show this help message
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
speculator-lint workspace.json
|
|
45
|
+
speculator-lint workspace.json --config .speculatorlintrc.json
|
|
46
|
+
`);
|
|
47
|
+
}
|
|
48
|
+
function formatDiagnostic(diagnostic) {
|
|
49
|
+
const severity = diagnostic.severity.toUpperCase();
|
|
50
|
+
const code = diagnostic.code;
|
|
51
|
+
const file = diagnostic.file || '<unknown>';
|
|
52
|
+
const line = diagnostic.sourcePos?.line || '?';
|
|
53
|
+
const col = diagnostic.sourcePos?.column || '?';
|
|
54
|
+
return `${severity} [${code}] ${file}:${line}:${col}\n ${diagnostic.message}`;
|
|
55
|
+
}
|
|
56
|
+
async function main() {
|
|
57
|
+
const args = parseArgs();
|
|
58
|
+
if (args.help || !args.workspacePath) {
|
|
59
|
+
showHelp();
|
|
60
|
+
process.exit(args.help ? 0 : 1);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
// Load workspace
|
|
64
|
+
const workspacePath = resolve(args.workspacePath);
|
|
65
|
+
const workspaceJson = readFileSync(workspacePath, 'utf-8');
|
|
66
|
+
const workspace = JSON.parse(workspaceJson);
|
|
67
|
+
// Build document levels map
|
|
68
|
+
const documentLevels = new Map();
|
|
69
|
+
workspace.documents.forEach((doc, index) => {
|
|
70
|
+
const path = doc.sourcePos?.file || '';
|
|
71
|
+
if (path) {
|
|
72
|
+
documentLevels.set(path, index);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// Load configuration
|
|
76
|
+
let config;
|
|
77
|
+
if (args.configPath) {
|
|
78
|
+
config = loadConfig(args.configPath);
|
|
79
|
+
console.log(`Using configuration from: ${args.configPath}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const defaultConfigLoaded = loadConfigFromDefaults();
|
|
83
|
+
if (defaultConfigLoaded) {
|
|
84
|
+
config = defaultConfigLoaded;
|
|
85
|
+
console.log('Using configuration from default location');
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
config = recommendedConfig;
|
|
89
|
+
console.log('Using recommended configuration (no config file found)');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Create linter
|
|
93
|
+
const linter = new SpeculatorLinter(builtInRules);
|
|
94
|
+
// Run linter
|
|
95
|
+
console.log('\nLinting workspace...\n');
|
|
96
|
+
const result = await linter.lint({
|
|
97
|
+
workspace,
|
|
98
|
+
documentLevels,
|
|
99
|
+
config
|
|
100
|
+
});
|
|
101
|
+
// Output diagnostics
|
|
102
|
+
if (result.diagnostics.length === 0) {
|
|
103
|
+
console.log('✓ No issues found');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
for (const diagnostic of result.diagnostics) {
|
|
107
|
+
console.log(formatDiagnostic(diagnostic));
|
|
108
|
+
console.log('');
|
|
109
|
+
}
|
|
110
|
+
const errorCount = result.diagnostics.filter(d => d.severity === 'error').length;
|
|
111
|
+
const warningCount = result.diagnostics.filter(d => d.severity === 'warning').length;
|
|
112
|
+
console.log(`Found ${errorCount} error(s), ${warningCount} warning(s)`);
|
|
113
|
+
}
|
|
114
|
+
// Show timing
|
|
115
|
+
console.log(`\nCompleted in ${result.totalTime.toFixed(2)}ms`);
|
|
116
|
+
// Exit with error code if errors found
|
|
117
|
+
process.exit(result.hasErrors ? 1 : 0);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
main();
|
|
125
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader and parser for speculator-lint
|
|
3
|
+
*/
|
|
4
|
+
import type { LintConfig, RuleConfigValue } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Default configuration
|
|
7
|
+
*/
|
|
8
|
+
export declare const defaultConfig: LintConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Recommended configuration with all built-in rules enabled
|
|
11
|
+
*/
|
|
12
|
+
export declare const recommendedConfig: LintConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Load configuration from a file
|
|
15
|
+
* @param configPath Path to config file (relative or absolute)
|
|
16
|
+
* @returns Parsed configuration
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadConfig(configPath: string): LintConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Load configuration from default locations
|
|
21
|
+
* @param cwd Current working directory
|
|
22
|
+
* @returns Configuration or null if not found
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadConfigFromDefaults(cwd?: string): LintConfig | null;
|
|
25
|
+
/**
|
|
26
|
+
* Get the effective severity for a rule
|
|
27
|
+
* @param ruleConfig Rule configuration value
|
|
28
|
+
* @returns Severity or null if disabled
|
|
29
|
+
*/
|
|
30
|
+
export declare function getRuleSeverity(ruleConfig: RuleConfigValue | undefined): 'error' | 'warning' | 'info' | null;
|
|
31
|
+
/**
|
|
32
|
+
* Check if a rule is enabled
|
|
33
|
+
*/
|
|
34
|
+
export declare function isRuleEnabled(config: LintConfig, ruleName: string): boolean;
|
|
35
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAE3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAK/B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAWzD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAsB,GAAG,UAAU,GAAG,IAAI,CAerF;AA0BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAU5G;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG3E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader and parser for speculator-lint
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from 'fs';
|
|
5
|
+
import { resolve } from 'path';
|
|
6
|
+
/**
|
|
7
|
+
* Default configuration
|
|
8
|
+
*/
|
|
9
|
+
export const defaultConfig = {
|
|
10
|
+
rules: {}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Recommended configuration with all built-in rules enabled
|
|
14
|
+
*/
|
|
15
|
+
export const recommendedConfig = {
|
|
16
|
+
rules: {
|
|
17
|
+
'workspace/no-redefinition': 'error',
|
|
18
|
+
'workspace/no-reverse-dependency': 'error'
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Load configuration from a file
|
|
23
|
+
* @param configPath Path to config file (relative or absolute)
|
|
24
|
+
* @returns Parsed configuration
|
|
25
|
+
*/
|
|
26
|
+
export function loadConfig(configPath) {
|
|
27
|
+
const resolvedPath = resolve(configPath);
|
|
28
|
+
if (!existsSync(resolvedPath)) {
|
|
29
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
30
|
+
}
|
|
31
|
+
const content = readFileSync(resolvedPath, 'utf-8');
|
|
32
|
+
const config = JSON.parse(content);
|
|
33
|
+
return normalizeConfig(config);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Load configuration from default locations
|
|
37
|
+
* @param cwd Current working directory
|
|
38
|
+
* @returns Configuration or null if not found
|
|
39
|
+
*/
|
|
40
|
+
export function loadConfigFromDefaults(cwd = process.cwd()) {
|
|
41
|
+
const candidates = [
|
|
42
|
+
'.speculatorlintrc.json',
|
|
43
|
+
'.speculatorlintrc',
|
|
44
|
+
'speculator-lint.config.json'
|
|
45
|
+
];
|
|
46
|
+
for (const filename of candidates) {
|
|
47
|
+
const path = resolve(cwd, filename);
|
|
48
|
+
if (existsSync(path)) {
|
|
49
|
+
return loadConfig(path);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Normalize configuration to ensure consistency
|
|
56
|
+
*/
|
|
57
|
+
function normalizeConfig(config) {
|
|
58
|
+
const normalized = {
|
|
59
|
+
...config,
|
|
60
|
+
rules: { ...config.rules }
|
|
61
|
+
};
|
|
62
|
+
// Handle extends
|
|
63
|
+
if (config.extends) {
|
|
64
|
+
for (const extend of config.extends) {
|
|
65
|
+
if (extend === 'recommended') {
|
|
66
|
+
normalized.rules = {
|
|
67
|
+
...recommendedConfig.rules,
|
|
68
|
+
...normalized.rules
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get the effective severity for a rule
|
|
77
|
+
* @param ruleConfig Rule configuration value
|
|
78
|
+
* @returns Severity or null if disabled
|
|
79
|
+
*/
|
|
80
|
+
export function getRuleSeverity(ruleConfig) {
|
|
81
|
+
if (!ruleConfig || ruleConfig === 'off') {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(ruleConfig)) {
|
|
85
|
+
return ruleConfig[0];
|
|
86
|
+
}
|
|
87
|
+
return ruleConfig;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if a rule is enabled
|
|
91
|
+
*/
|
|
92
|
+
export function isRuleEnabled(config, ruleName) {
|
|
93
|
+
const ruleConfig = config.rules?.[ruleName];
|
|
94
|
+
return getRuleSeverity(ruleConfig) !== null;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAG/B;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAe;IACrC,KAAK,EAAE,EAAE;CACZ,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAe;IACzC,KAAK,EAAE;QACH,2BAA2B,EAAE,OAAO;QACpC,iCAAiC,EAAE,OAAO;KAC7C;CACJ,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,UAAkB;IACzC,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,iCAAiC,YAAY,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAe,CAAC;IAEjD,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC9D,MAAM,UAAU,GAAG;QACf,wBAAwB;QACxB,mBAAmB;QACnB,6BAA6B;KAChC,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACpC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACnB,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,MAAkB;IACvC,MAAM,UAAU,GAAe;QAC3B,GAAG,MAAM;QACT,KAAK,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE;KAC7B,CAAC;IAEF,iBAAiB;IACjB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;gBAC3B,UAAU,CAAC,KAAK,GAAG;oBACf,GAAG,iBAAiB,CAAC,KAAK;oBAC1B,GAAG,UAAU,CAAC,KAAK;iBACtB,CAAC;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,UAAuC;IACnE,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAAkB,EAAE,QAAgB;IAC9D,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,eAAe,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;AAChD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openuji/speculator-lint
|
|
3
|
+
*
|
|
4
|
+
* Standalone linter for Speculator workspace AST
|
|
5
|
+
*/
|
|
6
|
+
export { SpeculatorLinter } from './linter.js';
|
|
7
|
+
export type { LintRule, LintContext, LintVisitor, LintDiagnostic, LintResult, LintOptions, LintConfig, RuleMetadata, RuleResult, Severity, RuleCategory } from './types.js';
|
|
8
|
+
export { loadConfig, loadConfigFromDefaults, defaultConfig, recommendedConfig } from './config.js';
|
|
9
|
+
export { builtInRules, getRuleByName, noRedefinitionRule, noReverseDependencyRule } from './rules/index.js';
|
|
10
|
+
export { normalizeTerm } from './utils.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,YAAY,EACR,QAAQ,EACR,WAAW,EACX,WAAW,EACX,cAAc,EACd,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,YAAY,EACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EACH,UAAU,EACV,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EACpB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACH,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EAC1B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openuji/speculator-lint
|
|
3
|
+
*
|
|
4
|
+
* Standalone linter for Speculator workspace AST
|
|
5
|
+
*/
|
|
6
|
+
// Main linter class
|
|
7
|
+
export { SpeculatorLinter } from './linter.js';
|
|
8
|
+
// Configuration utilities
|
|
9
|
+
export { loadConfig, loadConfigFromDefaults, defaultConfig, recommendedConfig } from './config.js';
|
|
10
|
+
// Built-in rules
|
|
11
|
+
export { builtInRules, getRuleByName, noRedefinitionRule, noReverseDependencyRule } from './rules/index.js';
|
|
12
|
+
// Utilities for custom rules
|
|
13
|
+
export { normalizeTerm } from './utils.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,oBAAoB;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiB/C,0BAA0B;AAC1B,OAAO,EACH,UAAU,EACV,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EACpB,MAAM,aAAa,CAAC;AAErB,iBAAiB;AACjB,OAAO,EACH,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EAC1B,MAAM,kBAAkB,CAAC;AAE1B,6BAA6B;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/linter.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpeculatorLinter - Main linter class
|
|
3
|
+
*/
|
|
4
|
+
import type { LintRule, LintOptions, LintResult } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Main linter class
|
|
7
|
+
*/
|
|
8
|
+
export declare class SpeculatorLinter {
|
|
9
|
+
private rules;
|
|
10
|
+
/**
|
|
11
|
+
* Create a new linter instance
|
|
12
|
+
* @param rules Array of lint rules to use
|
|
13
|
+
*/
|
|
14
|
+
constructor(rules: LintRule[]);
|
|
15
|
+
/**
|
|
16
|
+
* Lint a workspace
|
|
17
|
+
* @param options Lint options
|
|
18
|
+
* @returns Lint result with diagnostics
|
|
19
|
+
*/
|
|
20
|
+
lint(options: LintOptions): Promise<LintResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a rule is enabled in the configuration
|
|
23
|
+
*/
|
|
24
|
+
private isRuleEnabled;
|
|
25
|
+
/**
|
|
26
|
+
* Get the configured severity for a rule
|
|
27
|
+
*/
|
|
28
|
+
private getConfiguredSeverity;
|
|
29
|
+
/**
|
|
30
|
+
* Get available rules
|
|
31
|
+
*/
|
|
32
|
+
getRules(): LintRule[];
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=linter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACR,QAAQ,EACR,WAAW,EACX,UAAU,EAGb,MAAM,YAAY,CAAC;AAIpB;;GAEG;AACH,qBAAa,gBAAgB;IACzB,OAAO,CAAC,KAAK,CAAoC;IAEjD;;;OAGG;gBACS,KAAK,EAAE,QAAQ,EAAE;IAM7B;;;;OAIG;IACG,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAwCrD;;OAEG;IACH,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;OAEG;IACH,QAAQ,IAAI,QAAQ,EAAE;CAGzB"}
|
package/dist/linter.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpeculatorLinter - Main linter class
|
|
3
|
+
*/
|
|
4
|
+
import { runRule } from './rule-runner.js';
|
|
5
|
+
import { getRuleSeverity } from './config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Main linter class
|
|
8
|
+
*/
|
|
9
|
+
export class SpeculatorLinter {
|
|
10
|
+
rules = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Create a new linter instance
|
|
13
|
+
* @param rules Array of lint rules to use
|
|
14
|
+
*/
|
|
15
|
+
constructor(rules) {
|
|
16
|
+
for (const rule of rules) {
|
|
17
|
+
this.rules.set(rule.meta.name, rule);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Lint a workspace
|
|
22
|
+
* @param options Lint options
|
|
23
|
+
* @returns Lint result with diagnostics
|
|
24
|
+
*/
|
|
25
|
+
async lint(options) {
|
|
26
|
+
const startTime = performance.now();
|
|
27
|
+
const config = options.config || { rules: {} };
|
|
28
|
+
const ruleResults = new Map();
|
|
29
|
+
const allDiagnostics = [];
|
|
30
|
+
// Run each enabled rule
|
|
31
|
+
for (const [ruleName, rule] of this.rules) {
|
|
32
|
+
// Check if rule is enabled in config
|
|
33
|
+
if (!this.isRuleEnabled(config, ruleName)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Get effective severity from config
|
|
37
|
+
const configuredSeverity = this.getConfiguredSeverity(config, ruleName);
|
|
38
|
+
// Run the rule
|
|
39
|
+
const result = await runRule(rule, options.workspace, options.documentLevels);
|
|
40
|
+
// Override severity if configured
|
|
41
|
+
if (configuredSeverity && configuredSeverity !== rule.meta.severity) {
|
|
42
|
+
for (const diagnostic of result.diagnostics) {
|
|
43
|
+
diagnostic.severity = configuredSeverity;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
ruleResults.set(ruleName, result);
|
|
47
|
+
allDiagnostics.push(...result.diagnostics);
|
|
48
|
+
}
|
|
49
|
+
const endTime = performance.now();
|
|
50
|
+
return {
|
|
51
|
+
diagnostics: allDiagnostics,
|
|
52
|
+
hasErrors: allDiagnostics.some(d => d.severity === 'error'),
|
|
53
|
+
ruleResults,
|
|
54
|
+
totalTime: endTime - startTime
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if a rule is enabled in the configuration
|
|
59
|
+
*/
|
|
60
|
+
isRuleEnabled(config, ruleName) {
|
|
61
|
+
const ruleConfig = config.rules?.[ruleName];
|
|
62
|
+
const severity = getRuleSeverity(ruleConfig);
|
|
63
|
+
return severity !== null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get the configured severity for a rule
|
|
67
|
+
*/
|
|
68
|
+
getConfiguredSeverity(config, ruleName) {
|
|
69
|
+
return getRuleSeverity(config.rules?.[ruleName]);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get available rules
|
|
73
|
+
*/
|
|
74
|
+
getRules() {
|
|
75
|
+
return Array.from(this.rules.values());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=linter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linter.js","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,OAAO,gBAAgB;IACjB,KAAK,GAA0B,IAAI,GAAG,EAAE,CAAC;IAEjD;;;OAGG;IACH,YAAY,KAAiB;QACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,OAAoB;QAC3B,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC/C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;QAC9B,MAAM,cAAc,GAAqB,EAAE,CAAC;QAE5C,wBAAwB;QACxB,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACxC,qCAAqC;YACrC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;gBACxC,SAAS;YACb,CAAC;YAED,qCAAqC;YACrC,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAExE,eAAe;YACf,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;YAE9E,kCAAkC;YAClC,IAAI,kBAAkB,IAAI,kBAAkB,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClE,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;oBAC1C,UAAU,CAAC,QAAQ,GAAG,kBAAkB,CAAC;gBAC7C,CAAC;YACL,CAAC;YAED,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAClC,cAAc,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAElC,OAAO;YACH,WAAW,EAAE,cAAc;YAC3B,SAAS,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC;YAC3D,WAAW;YACX,SAAS,EAAE,OAAO,GAAG,SAAS;SACjC,CAAC;IACN,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,MAAkB,EAAE,QAAgB;QACtD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QAC7C,OAAO,QAAQ,KAAK,IAAI,CAAC;IAC7B,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,MAAkB,EAAE,QAAgB;QAC9D,OAAO,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACrD,CAAC;IAED;;OAEG;IACH,QAAQ;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;CACJ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule execution engine
|
|
3
|
+
*
|
|
4
|
+
* Handles visiting AST nodes and invoking rule visitors
|
|
5
|
+
*/
|
|
6
|
+
import type { Workspace } from '@openuji/speculator';
|
|
7
|
+
import type { LintRule, RuleResult } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Run a single rule against a workspace
|
|
10
|
+
*/
|
|
11
|
+
export declare function runRule(rule: LintRule, workspace: Workspace, documentLevels: Map<string, number>): Promise<RuleResult>;
|
|
12
|
+
//# sourceMappingURL=rule-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-runner.d.ts","sourceRoot":"","sources":["../src/rule-runner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACR,SAAS,EAIZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACR,QAAQ,EAGR,UAAU,EACb,MAAM,YAAY,CAAC;AAGpB;;GAEG;AACH,wBAAsB,OAAO,CACzB,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,OAAO,CAAC,UAAU,CAAC,CA8DrB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule execution engine
|
|
3
|
+
*
|
|
4
|
+
* Handles visiting AST nodes and invoking rule visitors
|
|
5
|
+
*/
|
|
6
|
+
import { normalizeTerm } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Run a single rule against a workspace
|
|
9
|
+
*/
|
|
10
|
+
export async function runRule(rule, workspace, documentLevels) {
|
|
11
|
+
const startTime = performance.now();
|
|
12
|
+
const diagnostics = [];
|
|
13
|
+
// Build global definition index for quick lookups
|
|
14
|
+
const globalDefIndex = buildDefinitionIndex(workspace);
|
|
15
|
+
// Process each document
|
|
16
|
+
for (const document of workspace.documents) {
|
|
17
|
+
const level = documentLevels.get(document.sourcePos?.file || '') ?? 0;
|
|
18
|
+
// Create context for this document
|
|
19
|
+
const context = {
|
|
20
|
+
workspace,
|
|
21
|
+
documentLevels,
|
|
22
|
+
document,
|
|
23
|
+
level,
|
|
24
|
+
report: (diagnostic) => {
|
|
25
|
+
diagnostics.push({
|
|
26
|
+
code: rule.meta.code,
|
|
27
|
+
severity: rule.meta.severity,
|
|
28
|
+
...diagnostic
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
// Create visitor
|
|
33
|
+
const visitor = rule.create(context);
|
|
34
|
+
// Visit document
|
|
35
|
+
if (visitor.onDocument) {
|
|
36
|
+
visitor.onDocument(document);
|
|
37
|
+
}
|
|
38
|
+
// Visit definitions
|
|
39
|
+
if (visitor.onDefinition) {
|
|
40
|
+
const docDefs = document.indexes?.definitions || [];
|
|
41
|
+
for (const entry of docDefs) {
|
|
42
|
+
// Get all entries for this term across workspace
|
|
43
|
+
const allEntries = globalDefIndex.get(normalizeTerm(entry.term)) || [];
|
|
44
|
+
visitor.onDefinition(entry, allEntries);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Visit references
|
|
48
|
+
if (visitor.onReference) {
|
|
49
|
+
const references = collectReferences(document);
|
|
50
|
+
for (const ref of references) {
|
|
51
|
+
// Try to resolve the reference
|
|
52
|
+
const target = resolveReference(ref, globalDefIndex);
|
|
53
|
+
visitor.onReference(ref, target);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const endTime = performance.now();
|
|
58
|
+
return {
|
|
59
|
+
ruleName: rule.meta.name,
|
|
60
|
+
diagnostics,
|
|
61
|
+
executionTime: endTime - startTime
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build a global definition index from workspace
|
|
66
|
+
*/
|
|
67
|
+
function buildDefinitionIndex(workspace) {
|
|
68
|
+
const index = new Map();
|
|
69
|
+
for (const doc of workspace.documents) {
|
|
70
|
+
const definitions = doc.indexes?.definitions || [];
|
|
71
|
+
for (const entry of definitions) {
|
|
72
|
+
const linkTexts = entry.linkTexts || [entry.term];
|
|
73
|
+
const termsToProcess = new Set([entry.term, ...linkTexts]);
|
|
74
|
+
for (const term of termsToProcess) {
|
|
75
|
+
const key = normalizeTerm(term);
|
|
76
|
+
const existing = index.get(key) || [];
|
|
77
|
+
existing.push(entry);
|
|
78
|
+
index.set(key, existing);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return index;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Collect all references from a document
|
|
86
|
+
*/
|
|
87
|
+
function collectReferences(document) {
|
|
88
|
+
const references = [];
|
|
89
|
+
// Walk through nodes recursively
|
|
90
|
+
function walkNode(node) {
|
|
91
|
+
if (!node || typeof node !== 'object')
|
|
92
|
+
return;
|
|
93
|
+
// Check if this is a reference node
|
|
94
|
+
if ('type' in node && node.type === 'reference') {
|
|
95
|
+
references.push(node);
|
|
96
|
+
}
|
|
97
|
+
// Walk children array (for inline elements, blocks, sections)
|
|
98
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
99
|
+
for (const child of node.children) {
|
|
100
|
+
walkNode(child);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Document has children array containing Section and Block nodes
|
|
105
|
+
if (document.children) {
|
|
106
|
+
for (const child of document.children) {
|
|
107
|
+
walkNode(child);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return references;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolve a reference to its target definition
|
|
114
|
+
*/
|
|
115
|
+
function resolveReference(ref, index) {
|
|
116
|
+
const candidateTerms = 'candidateTerms' in ref && Array.isArray(ref.candidateTerms)
|
|
117
|
+
? ref.candidateTerms
|
|
118
|
+
: [ref.targetTerm];
|
|
119
|
+
for (const term of candidateTerms) {
|
|
120
|
+
const key = normalizeTerm(term);
|
|
121
|
+
const entries = index.get(key);
|
|
122
|
+
if (entries && entries.length > 0) {
|
|
123
|
+
// Return first match (simplified resolution)
|
|
124
|
+
return entries[0];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=rule-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-runner.js","sourceRoot":"","sources":["../src/rule-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in lint rules
|
|
3
|
+
*/
|
|
4
|
+
export { noRedefinitionRule } from './workspace/no-redefinition.js';
|
|
5
|
+
export { noReverseDependencyRule } from './workspace/no-reverse-dependency.js';
|
|
6
|
+
import type { LintRule } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* All built-in rules
|
|
9
|
+
*/
|
|
10
|
+
export declare const builtInRules: LintRule[];
|
|
11
|
+
/**
|
|
12
|
+
* Get a rule by name
|
|
13
|
+
*/
|
|
14
|
+
export declare function getRuleByName(name: string): LintRule | undefined;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAI/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,EAGlC,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAEhE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in lint rules
|
|
3
|
+
*/
|
|
4
|
+
export { noRedefinitionRule } from './workspace/no-redefinition.js';
|
|
5
|
+
export { noReverseDependencyRule } from './workspace/no-reverse-dependency.js';
|
|
6
|
+
import { noRedefinitionRule } from './workspace/no-redefinition.js';
|
|
7
|
+
import { noReverseDependencyRule } from './workspace/no-reverse-dependency.js';
|
|
8
|
+
/**
|
|
9
|
+
* All built-in rules
|
|
10
|
+
*/
|
|
11
|
+
export const builtInRules = [
|
|
12
|
+
noRedefinitionRule,
|
|
13
|
+
noReverseDependencyRule
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Get a rule by name
|
|
17
|
+
*/
|
|
18
|
+
export function getRuleByName(name) {
|
|
19
|
+
return builtInRules.find(r => r.meta.name === name);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAG/E;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAe;IACpC,kBAAkB;IAClB,uBAAuB;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACtC,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace/no-redefinition rule
|
|
3
|
+
*
|
|
4
|
+
* Ensures that lower-level specs do not redefine concepts from higher-level specs.
|
|
5
|
+
*
|
|
6
|
+
* Rule Logic:
|
|
7
|
+
* - When a term is defined in multiple documents, compare their levels
|
|
8
|
+
* - If a lower-level document (higher number) redefines a term from a higher-level document,
|
|
9
|
+
* report an error
|
|
10
|
+
*/
|
|
11
|
+
import type { LintRule } from '../../types.js';
|
|
12
|
+
export declare const noRedefinitionRule: LintRule;
|
|
13
|
+
//# sourceMappingURL=no-redefinition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-redefinition.d.ts","sourceRoot":"","sources":["../../../src/rules/workspace/no-redefinition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AAE5D,eAAO,MAAM,kBAAkB,EAAE,QAuDhC,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace/no-redefinition rule
|
|
3
|
+
*
|
|
4
|
+
* Ensures that lower-level specs do not redefine concepts from higher-level specs.
|
|
5
|
+
*
|
|
6
|
+
* Rule Logic:
|
|
7
|
+
* - When a term is defined in multiple documents, compare their levels
|
|
8
|
+
* - If a lower-level document (higher number) redefines a term from a higher-level document,
|
|
9
|
+
* report an error
|
|
10
|
+
*/
|
|
11
|
+
export const noRedefinitionRule = {
|
|
12
|
+
meta: {
|
|
13
|
+
name: 'workspace/no-redefinition',
|
|
14
|
+
code: 'no-redefinition',
|
|
15
|
+
severity: 'error',
|
|
16
|
+
description: 'Lower-level specs MUST NOT redefine concepts from higher-level specs',
|
|
17
|
+
category: 'workspace'
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
onDefinition(entry, allEntriesForTerm) {
|
|
22
|
+
// If this is the first definition of this term, no issue
|
|
23
|
+
if (allEntriesForTerm.length === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Find the highest-level (lowest number) definition
|
|
27
|
+
let highestEntry = allEntriesForTerm[0];
|
|
28
|
+
let highestLevel = Infinity;
|
|
29
|
+
for (const existingEntry of allEntriesForTerm) {
|
|
30
|
+
const docPath = existingEntry.sourcePos?.file;
|
|
31
|
+
if (!docPath)
|
|
32
|
+
continue;
|
|
33
|
+
const level = context.documentLevels.get(docPath) ?? 0;
|
|
34
|
+
if (level < highestLevel) {
|
|
35
|
+
highestLevel = level;
|
|
36
|
+
highestEntry = existingEntry;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Check if current entry is from a lower level
|
|
40
|
+
const currentDocPath = entry.sourcePos?.file;
|
|
41
|
+
if (!currentDocPath)
|
|
42
|
+
return;
|
|
43
|
+
const currentLevel = context.documentLevels.get(currentDocPath) ?? 0;
|
|
44
|
+
const higherDocPath = highestEntry.sourcePos?.file;
|
|
45
|
+
// If current level is lower (higher number) than the highest definition
|
|
46
|
+
if (currentLevel > highestLevel && higherDocPath) {
|
|
47
|
+
// Skip if this is the same document (shouldn't happen, but just in case)
|
|
48
|
+
if (currentDocPath === higherDocPath) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
context.report({
|
|
52
|
+
message: `Lower-level spec "${currentDocPath}" redefines concept "${entry.term}" already defined in higher-level spec "${higherDocPath}"`,
|
|
53
|
+
file: currentDocPath,
|
|
54
|
+
sourcePos: entry.sourcePos
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=no-redefinition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-redefinition.js","sourceRoot":"","sources":["../../../src/rules/workspace/no-redefinition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,CAAC,MAAM,kBAAkB,GAAa;IACxC,IAAI,EAAE;QACF,IAAI,EAAE,2BAA2B;QACjC,IAAI,EAAE,iBAAiB;QACvB,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,sEAAsE;QACnF,QAAQ,EAAE,WAAW;KACxB;IAED,MAAM,CAAC,OAAoB;QACvB,OAAO;YACH,YAAY,CAAC,KAAK,EAAE,iBAAiB;gBACjC,yDAAyD;gBACzD,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACjC,OAAO;gBACX,CAAC;gBAED,oDAAoD;gBACpD,IAAI,YAAY,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;gBACxC,IAAI,YAAY,GAAG,QAAQ,CAAC;gBAE5B,KAAK,MAAM,aAAa,IAAI,iBAAiB,EAAE,CAAC;oBAC5C,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC;oBAC9C,IAAI,CAAC,OAAO;wBAAE,SAAS;oBAEvB,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBACvD,IAAI,KAAK,GAAG,YAAY,EAAE,CAAC;wBACvB,YAAY,GAAG,KAAK,CAAC;wBACrB,YAAY,GAAG,aAAa,CAAC;oBACjC,CAAC;gBACL,CAAC;gBAED,+CAA+C;gBAC/C,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC;gBAC7C,IAAI,CAAC,cAAc;oBAAE,OAAO;gBAE5B,MAAM,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;gBACrE,MAAM,aAAa,GAAG,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC;gBAEnD,wEAAwE;gBACxE,IAAI,YAAY,GAAG,YAAY,IAAI,aAAa,EAAE,CAAC;oBAC/C,yEAAyE;oBACzE,IAAI,cAAc,KAAK,aAAa,EAAE,CAAC;wBACnC,OAAO;oBACX,CAAC;oBAED,OAAO,CAAC,MAAM,CAAC;wBACX,OAAO,EAAE,qBAAqB,cAAc,wBAAwB,KAAK,CAAC,IAAI,2CAA2C,aAAa,GAAG;wBACzI,IAAI,EAAE,cAAc;wBACpB,SAAS,EAAE,KAAK,CAAC,SAAS;qBAC7B,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;SACJ,CAAC;IACN,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace/no-reverse-dependency rule
|
|
3
|
+
*
|
|
4
|
+
* Ensures that higher-level specs do not depend on (reference) lower-level specs.
|
|
5
|
+
*
|
|
6
|
+
* Rule Logic:
|
|
7
|
+
* - When a reference is resolved to a definition, compare document levels
|
|
8
|
+
* - If the source document has a lower level (higher priority, lower number)
|
|
9
|
+
* than the target document, report an error
|
|
10
|
+
*/
|
|
11
|
+
import type { LintRule } from '../../types.js';
|
|
12
|
+
export declare const noReverseDependencyRule: LintRule;
|
|
13
|
+
//# sourceMappingURL=no-reverse-dependency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-reverse-dependency.d.ts","sourceRoot":"","sources":["../../../src/rules/workspace/no-reverse-dependency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AAE5D,eAAO,MAAM,uBAAuB,EAAE,QAuCrC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace/no-reverse-dependency rule
|
|
3
|
+
*
|
|
4
|
+
* Ensures that higher-level specs do not depend on (reference) lower-level specs.
|
|
5
|
+
*
|
|
6
|
+
* Rule Logic:
|
|
7
|
+
* - When a reference is resolved to a definition, compare document levels
|
|
8
|
+
* - If the source document has a lower level (higher priority, lower number)
|
|
9
|
+
* than the target document, report an error
|
|
10
|
+
*/
|
|
11
|
+
export const noReverseDependencyRule = {
|
|
12
|
+
meta: {
|
|
13
|
+
name: 'workspace/no-reverse-dependency',
|
|
14
|
+
code: 'no-reverse-dependency',
|
|
15
|
+
severity: 'error',
|
|
16
|
+
description: 'Higher-level specs MUST NOT depend on lower-level specs',
|
|
17
|
+
category: 'workspace'
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
onReference(ref, target) {
|
|
22
|
+
// If reference is not resolved, nothing to check
|
|
23
|
+
if (!target) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const sourceDocPath = context.document.sourcePos?.file;
|
|
27
|
+
const targetDocPath = target.sourcePos?.file;
|
|
28
|
+
if (!sourceDocPath || !targetDocPath) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Get levels
|
|
32
|
+
const sourceLevel = context.level;
|
|
33
|
+
const targetLevel = context.documentLevels.get(targetDocPath) ?? 0;
|
|
34
|
+
// If source is higher level (lower number) than target, that's a violation
|
|
35
|
+
if (sourceLevel < targetLevel) {
|
|
36
|
+
context.report({
|
|
37
|
+
message: `Higher-level spec "${sourceDocPath}" (level ${sourceLevel}) depends on lower-level spec "${targetDocPath}" (level ${targetLevel}) via term "${target.term}"`,
|
|
38
|
+
file: sourceDocPath,
|
|
39
|
+
sourcePos: ref.sourcePos
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=no-reverse-dependency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-reverse-dependency.js","sourceRoot":"","sources":["../../../src/rules/workspace/no-reverse-dependency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,CAAC,MAAM,uBAAuB,GAAa;IAC7C,IAAI,EAAE;QACF,IAAI,EAAE,iCAAiC;QACvC,IAAI,EAAE,uBAAuB;QAC7B,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,yDAAyD;QACtE,QAAQ,EAAE,WAAW;KACxB;IAED,MAAM,CAAC,OAAoB;QACvB,OAAO;YACH,WAAW,CAAC,GAAG,EAAE,MAAM;gBACnB,iDAAiD;gBACjD,IAAI,CAAC,MAAM,EAAE,CAAC;oBACV,OAAO;gBACX,CAAC;gBAED,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;gBACvD,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC;gBAE7C,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnC,OAAO;gBACX,CAAC;gBAED,aAAa;gBACb,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;gBAClC,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBAEnE,2EAA2E;gBAC3E,IAAI,WAAW,GAAG,WAAW,EAAE,CAAC;oBAC5B,OAAO,CAAC,MAAM,CAAC;wBACX,OAAO,EAAE,sBAAsB,aAAa,YAAY,WAAW,kCAAkC,aAAa,YAAY,WAAW,eAAe,MAAM,CAAC,IAAI,GAAG;wBACtK,IAAI,EAAE,aAAa;wBACnB,SAAS,EAAE,GAAG,CAAC,SAAS;qBAC3B,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;SACJ,CAAC;IACN,CAAC;CACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for @openuji/speculator-lint
|
|
3
|
+
*
|
|
4
|
+
* Defines the rule-based linting architecture for Speculator workspace AST.
|
|
5
|
+
*/
|
|
6
|
+
import type { Workspace, Document, IndexDefinitionEntry, InlineReference, SourcePos } from '@openuji/speculator';
|
|
7
|
+
/**
|
|
8
|
+
* Diagnostic severity levels
|
|
9
|
+
*/
|
|
10
|
+
export type Severity = 'error' | 'warning' | 'info';
|
|
11
|
+
/**
|
|
12
|
+
* Rule category for organization
|
|
13
|
+
*/
|
|
14
|
+
export type RuleCategory = 'workspace' | 'document' | 'reference' | 'custom';
|
|
15
|
+
/**
|
|
16
|
+
* Diagnostic produced by a lint rule
|
|
17
|
+
*/
|
|
18
|
+
export interface LintDiagnostic {
|
|
19
|
+
/** Rule code (e.g., 'no-redefinition') */
|
|
20
|
+
code: string;
|
|
21
|
+
/** Severity level */
|
|
22
|
+
severity: Severity;
|
|
23
|
+
/** Diagnostic message */
|
|
24
|
+
message: string;
|
|
25
|
+
/** File where the issue was found */
|
|
26
|
+
file?: string;
|
|
27
|
+
/** Source position in the file */
|
|
28
|
+
sourcePos?: SourcePos;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Context provided to lint rules
|
|
32
|
+
*/
|
|
33
|
+
export interface LintContext {
|
|
34
|
+
/** Current workspace being linted */
|
|
35
|
+
readonly workspace: Workspace;
|
|
36
|
+
/** Map of document path -> level (0 is highest) */
|
|
37
|
+
readonly documentLevels: Map<string, number>;
|
|
38
|
+
/** Current document being processed */
|
|
39
|
+
readonly document: Document;
|
|
40
|
+
/** Current document's level */
|
|
41
|
+
readonly level: number;
|
|
42
|
+
/**
|
|
43
|
+
* Report a diagnostic
|
|
44
|
+
*/
|
|
45
|
+
report(diagnostic: Omit<LintDiagnostic, 'code' | 'severity'>): void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Visitor pattern for AST traversal
|
|
49
|
+
* Rules implement the hooks they're interested in
|
|
50
|
+
*/
|
|
51
|
+
export interface LintVisitor {
|
|
52
|
+
/**
|
|
53
|
+
* Called for each definition in the document
|
|
54
|
+
* @param entry The definition entry
|
|
55
|
+
* @param allEntriesForTerm All entries for this normalized term across the workspace
|
|
56
|
+
*/
|
|
57
|
+
onDefinition?(entry: IndexDefinitionEntry, allEntriesForTerm: IndexDefinitionEntry[]): void;
|
|
58
|
+
/**
|
|
59
|
+
* Called for each reference in the document
|
|
60
|
+
* @param ref The reference node
|
|
61
|
+
* @param target The resolved target definition (null if unresolved)
|
|
62
|
+
*/
|
|
63
|
+
onReference?(ref: InlineReference, target: IndexDefinitionEntry | null): void;
|
|
64
|
+
/**
|
|
65
|
+
* Called once per document before visiting nodes
|
|
66
|
+
* @param doc The document
|
|
67
|
+
*/
|
|
68
|
+
onDocument?(doc: Document): void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Rule metadata
|
|
72
|
+
*/
|
|
73
|
+
export interface RuleMetadata {
|
|
74
|
+
/** Unique rule name (e.g., 'no-redefinition') */
|
|
75
|
+
name: string;
|
|
76
|
+
/** Diagnostic code used in reports */
|
|
77
|
+
code: string;
|
|
78
|
+
/** Default severity */
|
|
79
|
+
severity: Severity;
|
|
80
|
+
/** Human-readable description */
|
|
81
|
+
description: string;
|
|
82
|
+
/** Rule category */
|
|
83
|
+
category: RuleCategory;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Lint rule interface
|
|
87
|
+
*/
|
|
88
|
+
export interface LintRule {
|
|
89
|
+
/** Rule metadata */
|
|
90
|
+
meta: RuleMetadata;
|
|
91
|
+
/**
|
|
92
|
+
* Create a visitor for this rule
|
|
93
|
+
* @param context Lint context
|
|
94
|
+
* @returns Visitor implementation
|
|
95
|
+
*/
|
|
96
|
+
create(context: LintContext): LintVisitor;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Configuration for a single rule
|
|
100
|
+
*/
|
|
101
|
+
export type RuleConfigValue = 'off' | 'error' | 'warning' | 'info' | [Severity, Record<string, unknown>?];
|
|
102
|
+
/**
|
|
103
|
+
* Linter configuration
|
|
104
|
+
*/
|
|
105
|
+
export interface LintConfig {
|
|
106
|
+
/** Rule configurations by rule name */
|
|
107
|
+
rules?: Record<string, RuleConfigValue>;
|
|
108
|
+
/** Configurations to extend */
|
|
109
|
+
extends?: string[];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Result from a single rule execution
|
|
113
|
+
*/
|
|
114
|
+
export interface RuleResult {
|
|
115
|
+
/** Rule name */
|
|
116
|
+
ruleName: string;
|
|
117
|
+
/** Diagnostics produced by this rule */
|
|
118
|
+
diagnostics: LintDiagnostic[];
|
|
119
|
+
/** Execution time in milliseconds */
|
|
120
|
+
executionTime: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Overall lint result
|
|
124
|
+
*/
|
|
125
|
+
export interface LintResult {
|
|
126
|
+
/** All diagnostics from all rules */
|
|
127
|
+
diagnostics: LintDiagnostic[];
|
|
128
|
+
/** Quick check for errors */
|
|
129
|
+
hasErrors: boolean;
|
|
130
|
+
/** Results per rule */
|
|
131
|
+
ruleResults: Map<string, RuleResult>;
|
|
132
|
+
/** Total execution time */
|
|
133
|
+
totalTime: number;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Options for running the linter
|
|
137
|
+
*/
|
|
138
|
+
export interface LintOptions {
|
|
139
|
+
/** Workspace to lint */
|
|
140
|
+
workspace: Workspace;
|
|
141
|
+
/** Document level mapping */
|
|
142
|
+
documentLevels: Map<string, number>;
|
|
143
|
+
/** Optional configuration override */
|
|
144
|
+
config?: LintConfig;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKlD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for speculator-lint
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Normalize a term for consistent lookup
|
|
6
|
+
* Mirrors the logic from @openuji/speculator
|
|
7
|
+
*/
|
|
8
|
+
export function normalizeTerm(term) {
|
|
9
|
+
return term
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.trim()
|
|
12
|
+
.replace(/\s+/g, ' ');
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACtC,OAAO,IAAI;SACN,WAAW,EAAE;SACb,IAAI,EAAE;SACN,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC9B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openuji/speculator-lint",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Standalone linter for Speculator workspace AST with configurable validation rules",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"speculator-lint": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"imports": {
|
|
12
|
+
"#src/*": "./dist/*.js"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^20.10.0",
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
17
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
18
|
+
"eslint": "^8.57.0",
|
|
19
|
+
"typescript": "^5.3.0",
|
|
20
|
+
"vitest": "^1.0.0"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@openuji/speculator": "0.5.1"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
".speculatorlintrc.example.json"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"linter",
|
|
31
|
+
"ast",
|
|
32
|
+
"specification",
|
|
33
|
+
"validation",
|
|
34
|
+
"speculator"
|
|
35
|
+
],
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"dev": "tsc --watch",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"lint": "eslint src --ext .ts",
|
|
43
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
}
|
|
46
|
+
}
|