@effectweb/compiler 0.2.3 → 0.3.1
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 +29 -1
- package/dist/compile.d.ts +9 -0
- package/dist/files.d.ts +1 -0
- package/dist/files.js +3 -0
- package/dist/oxlint.d.ts +46 -3
- package/dist/oxlint.js +32 -9
- package/dist/vite.js +3 -2
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -19,4 +19,32 @@ See [EffectWeb](https://github.com/DerpyCrabs/EffectWeb) for authoring and devel
|
|
|
19
19
|
|
|
20
20
|
For editor and CI diagnostics, add `@effectweb/compiler/oxlint` to Oxlint's `jsPlugins` and enable `effectweb/valid-view` as an error. The optional `effectweb/whole-model-dependency` rule reports coarse dependencies. Both use the compiler's Rust analysis; no second purity implementation is maintained. `diagnose` exposes the same structured view diagnostics for other tooling. Invalid syntax still throws and is handled by the host parser.
|
|
21
21
|
|
|
22
|
-
The
|
|
22
|
+
The runtime freezes published plain objects and arrays in every build without changing their identity or introducing proxies. Opaque mutable resources retain their own lifecycle. The Vite plugin adds source-dependency instrumentation during development; production builds omit that metadata.
|
|
23
|
+
|
|
24
|
+
Diagnostics include `code`, `category`, `severity`, source `file`/`line`/`column` (one-based UTF-16 columns), and an actionable `remedy`:
|
|
25
|
+
|
|
26
|
+
| Code | Category | Meaning and remedy |
|
|
27
|
+
| ------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| EW1000 | correctness | Lint could not parse/analyze the file; fix syntax or the native compiler installation. |
|
|
29
|
+
| EW1001 | correctness | Invalid view syntax or impure render work; the message identifies the supported form or command boundary. |
|
|
30
|
+
| EW1002 | correctness | A known raw object array is rendered with `.map`; use `entities(items)`, a declared `collection(identity).from(items)`, or explicit positional `sequence(items)`. |
|
|
31
|
+
| EW2001 | unprovable-dependency | A mutable capture is outside snapshot dependencies; pass immutable data through the model. Compilation rejects this. |
|
|
32
|
+
| EW2002 | unprovable-dependency | A query declares a custom `key`; remove it. Every request argument already participates in cache identity. This is a compilation error. |
|
|
33
|
+
| EW3001 | performance | A helper depends on the entire model; optionally pass the individual fields it uses. |
|
|
34
|
+
|
|
35
|
+
`effectweb/valid-view` reports compilation errors; `effectweb/whole-model-dependency` is optional performance advice. `effectweb/query-key` reports rejected custom query keys in `.ts` and `.tsx`. Configure these rules as follows:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"jsPlugins": ["@effectweb/compiler/oxlint"],
|
|
40
|
+
"rules": {
|
|
41
|
+
"effectweb/valid-view": "error",
|
|
42
|
+
"effectweb/query-key": "error",
|
|
43
|
+
"effectweb/whole-model-dependency": "warn"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Identity checks use conservative same-file evidence: object array literals, typed variables or parameters, explicit `view<Model>`, local non-generic aliases/interfaces, `Array`/`ReadonlyArray`/`readonly` annotations, and common `filter`/`slice` operations. They do not run a TypeScript project checker, resolve imported types, or infer domain keys. Unknown types remain subject to runtime identity checks. Collections validate identities when constructed or shared, including unchanged-array paths; duplicate errors report both zero-based positions.
|
|
49
|
+
|
|
50
|
+
Query checks recognize imported `query` (including aliases and `effectweb/query`) and reject literal definitions containing `key`. TypeScript and the runtime also reject custom keys, including definitions assembled outside the compiler's static analysis. Pass all request data as arguments and supply services through the Effect environment.
|
package/dist/compile.d.ts
CHANGED
|
@@ -4,12 +4,21 @@ export interface Diagnostic {
|
|
|
4
4
|
readonly column: number;
|
|
5
5
|
readonly message: string;
|
|
6
6
|
readonly severity: 'error' | 'warning';
|
|
7
|
+
readonly code: string;
|
|
8
|
+
readonly category: 'correctness' | 'unprovable-dependency' | 'performance';
|
|
9
|
+
readonly remedy: string;
|
|
7
10
|
}
|
|
8
11
|
export interface CompilerOptions {
|
|
9
12
|
/** The module exporting view and slot. Defaults to effectweb. */
|
|
10
13
|
importSource?: string;
|
|
11
14
|
runtimeModule?: string;
|
|
12
15
|
development?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Audited render calls, keyed by import specifier and exported member path.
|
|
18
|
+
* These contracts promise immutable inputs, no observable effects or ambient
|
|
19
|
+
* reads, and no eager execution of unchecked callbacks. They do not load source.
|
|
20
|
+
*/
|
|
21
|
+
pureImports?: Readonly<Record<string, readonly string[]>>;
|
|
13
22
|
onDiagnostic?: (diagnostic: Diagnostic) => void;
|
|
14
23
|
}
|
|
15
24
|
export interface CompilerResult {
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isCompilerFile(filename: string): boolean;
|
package/dist/files.js
ADDED
package/dist/oxlint.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import { type CompilerOptions } from './compile.js';
|
|
1
2
|
type Source = {
|
|
2
3
|
text: string;
|
|
3
4
|
};
|
|
4
5
|
type Context = {
|
|
5
6
|
filename: string;
|
|
6
7
|
sourceCode: Source;
|
|
7
|
-
options: readonly
|
|
8
|
-
importSource?: string;
|
|
9
|
-
}[];
|
|
8
|
+
options: readonly Pick<CompilerOptions, 'importSource' | 'pureImports'>[];
|
|
10
9
|
report: (diagnostic: {
|
|
11
10
|
loc: {
|
|
12
11
|
line: number;
|
|
@@ -29,6 +28,15 @@ declare const _default: {
|
|
|
29
28
|
importSource: {
|
|
30
29
|
type: string;
|
|
31
30
|
};
|
|
31
|
+
pureImports: {
|
|
32
|
+
type: string;
|
|
33
|
+
additionalProperties: {
|
|
34
|
+
type: string;
|
|
35
|
+
items: {
|
|
36
|
+
type: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
};
|
|
32
40
|
};
|
|
33
41
|
additionalProperties: boolean;
|
|
34
42
|
}[];
|
|
@@ -46,6 +54,41 @@ declare const _default: {
|
|
|
46
54
|
importSource: {
|
|
47
55
|
type: string;
|
|
48
56
|
};
|
|
57
|
+
pureImports: {
|
|
58
|
+
type: string;
|
|
59
|
+
additionalProperties: {
|
|
60
|
+
type: string;
|
|
61
|
+
items: {
|
|
62
|
+
type: string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
additionalProperties: boolean;
|
|
68
|
+
}[];
|
|
69
|
+
};
|
|
70
|
+
create(context: Context): {
|
|
71
|
+
Program(): void;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
'query-key': {
|
|
75
|
+
meta: {
|
|
76
|
+
type: "problem" | "suggestion";
|
|
77
|
+
schema: {
|
|
78
|
+
type: string;
|
|
79
|
+
properties: {
|
|
80
|
+
importSource: {
|
|
81
|
+
type: string;
|
|
82
|
+
};
|
|
83
|
+
pureImports: {
|
|
84
|
+
type: string;
|
|
85
|
+
additionalProperties: {
|
|
86
|
+
type: string;
|
|
87
|
+
items: {
|
|
88
|
+
type: string;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
};
|
|
49
92
|
};
|
|
50
93
|
additionalProperties: boolean;
|
|
51
94
|
}[];
|
package/dist/oxlint.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { diagnose } from './compile.js';
|
|
2
|
+
import { isCompilerFile } from './files.js';
|
|
2
3
|
const results = new WeakMap();
|
|
3
|
-
function rule(
|
|
4
|
+
function rule(category) {
|
|
4
5
|
return {
|
|
5
6
|
meta: {
|
|
6
|
-
type:
|
|
7
|
+
type: category !== 'performance' ? 'problem' : 'suggestion',
|
|
7
8
|
schema: [
|
|
8
9
|
{
|
|
9
10
|
type: 'object',
|
|
10
|
-
properties: {
|
|
11
|
+
properties: {
|
|
12
|
+
importSource: { type: 'string' },
|
|
13
|
+
pureImports: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
additionalProperties: { type: 'array', items: { type: 'string' } },
|
|
16
|
+
},
|
|
17
|
+
},
|
|
11
18
|
additionalProperties: false,
|
|
12
19
|
},
|
|
13
20
|
],
|
|
@@ -15,19 +22,23 @@ function rule(severity) {
|
|
|
15
22
|
create(context) {
|
|
16
23
|
return {
|
|
17
24
|
Program() {
|
|
18
|
-
if (!context.filename
|
|
25
|
+
if (!isCompilerFile(context.filename))
|
|
19
26
|
return;
|
|
20
27
|
const importSource = context.options[0]?.importSource ?? 'effectweb';
|
|
28
|
+
const pureImports = context.options[0]?.pureImports;
|
|
21
29
|
let cached = results.get(context.sourceCode);
|
|
22
30
|
if (!cached || cached.text !== context.sourceCode.text) {
|
|
23
31
|
cached = { text: context.sourceCode.text, diagnostics: new Map() };
|
|
24
32
|
results.set(context.sourceCode, cached);
|
|
25
33
|
}
|
|
26
|
-
const key = `${context.filename}\0${importSource}`;
|
|
34
|
+
const key = `${context.filename}\0${importSource}\0${JSON.stringify(pureImports)}`;
|
|
27
35
|
let diagnostics = cached.diagnostics.get(key);
|
|
28
36
|
if (!diagnostics) {
|
|
29
37
|
try {
|
|
30
|
-
diagnostics = diagnose(context.sourceCode.text, context.filename, {
|
|
38
|
+
diagnostics = diagnose(context.sourceCode.text, context.filename, {
|
|
39
|
+
importSource,
|
|
40
|
+
...(pureImports ? { pureImports } : {}),
|
|
41
|
+
});
|
|
31
42
|
}
|
|
32
43
|
catch (error) {
|
|
33
44
|
// Parser failures and unavailable native binaries must not silently pass lint.
|
|
@@ -37,6 +48,9 @@ function rule(severity) {
|
|
|
37
48
|
line: 1,
|
|
38
49
|
column: 1,
|
|
39
50
|
severity: 'error',
|
|
51
|
+
code: 'EW1000',
|
|
52
|
+
category: 'correctness',
|
|
53
|
+
remedy: 'Fix the parser error or install the native compiler binary for this platform.',
|
|
40
54
|
message: error instanceof Error ? error.message : String(error),
|
|
41
55
|
},
|
|
42
56
|
];
|
|
@@ -44,11 +58,16 @@ function rule(severity) {
|
|
|
44
58
|
cached.diagnostics.set(key, diagnostics);
|
|
45
59
|
}
|
|
46
60
|
for (const diagnostic of diagnostics) {
|
|
47
|
-
|
|
61
|
+
const selected = category === 'unprovable-dependency'
|
|
62
|
+
? diagnostic.code === 'EW2002' || diagnostic.code === 'EW1000'
|
|
63
|
+
: category === 'errors'
|
|
64
|
+
? diagnostic.severity === 'error'
|
|
65
|
+
: diagnostic.category === category;
|
|
66
|
+
if (!selected)
|
|
48
67
|
continue;
|
|
49
68
|
context.report({
|
|
50
69
|
loc: { line: diagnostic.line, column: diagnostic.column - 1 },
|
|
51
|
-
message: diagnostic.message
|
|
70
|
+
message: `[${diagnostic.code}] ${diagnostic.message}`,
|
|
52
71
|
});
|
|
53
72
|
}
|
|
54
73
|
},
|
|
@@ -58,5 +77,9 @@ function rule(severity) {
|
|
|
58
77
|
}
|
|
59
78
|
export default {
|
|
60
79
|
meta: { name: 'effectweb' },
|
|
61
|
-
rules: {
|
|
80
|
+
rules: {
|
|
81
|
+
'valid-view': rule('errors'),
|
|
82
|
+
'whole-model-dependency': rule('performance'),
|
|
83
|
+
'query-key': rule('unprovable-dependency'),
|
|
84
|
+
},
|
|
62
85
|
};
|
package/dist/vite.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { compile } from './compile.js';
|
|
2
|
+
import { isCompilerFile } from './files.js';
|
|
2
3
|
export function effectweb(options = {}) {
|
|
3
4
|
let development = false;
|
|
4
5
|
return {
|
|
@@ -15,13 +16,13 @@ export function effectweb(options = {}) {
|
|
|
15
16
|
enforce: 'pre',
|
|
16
17
|
transform(code, id) {
|
|
17
18
|
const filename = id.split('?')[0];
|
|
18
|
-
if (!filename
|
|
19
|
+
if (!filename || !isCompilerFile(filename))
|
|
19
20
|
return;
|
|
20
21
|
const result = compile(code, filename, {
|
|
21
22
|
...options,
|
|
22
23
|
development,
|
|
23
24
|
onDiagnostic: options.onDiagnostic ??
|
|
24
|
-
((diagnostic) => this.warn(`${diagnostic.file}:${diagnostic.line}:${diagnostic.column} ${diagnostic.message}`)),
|
|
25
|
+
((diagnostic) => this.warn(`${diagnostic.file}:${diagnostic.line}:${diagnostic.column} [${diagnostic.code}] ${diagnostic.message}`)),
|
|
25
26
|
});
|
|
26
27
|
return { code: result.code, map: result.map };
|
|
27
28
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effectweb/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Native Oxc compiler and Vite plugin for effectweb",
|
|
5
5
|
"homepage": "https://github.com/DerpyCrabs/EffectWeb",
|
|
6
6
|
"bugs": {
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"optionalDependencies": {
|
|
49
|
-
"@effectweb/compiler-darwin-arm64": "0.
|
|
50
|
-
"@effectweb/compiler-darwin-x64": "0.
|
|
51
|
-
"@effectweb/compiler-linux-arm64-gnu": "0.
|
|
52
|
-
"@effectweb/compiler-linux-x64-gnu": "0.
|
|
53
|
-
"@effectweb/compiler-win32-x64-msvc": "0.
|
|
49
|
+
"@effectweb/compiler-darwin-arm64": "0.3.1",
|
|
50
|
+
"@effectweb/compiler-darwin-x64": "0.3.1",
|
|
51
|
+
"@effectweb/compiler-linux-arm64-gnu": "0.3.1",
|
|
52
|
+
"@effectweb/compiler-linux-x64-gnu": "0.3.1",
|
|
53
|
+
"@effectweb/compiler-win32-x64-msvc": "0.3.1"
|
|
54
54
|
},
|
|
55
55
|
"engines": {
|
|
56
56
|
"node": ">=22.14"
|