@effectweb/compiler 0.3.1 → 0.5.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/README.md CHANGED
@@ -11,28 +11,33 @@ export default defineConfig({ plugins: [effectweb()] });
11
11
 
12
12
  Install `effectweb` and its required Effect version in the application. Set TypeScript `jsx` to `preserve` and `jsxImportSource` to `effectweb`.
13
13
 
14
- For direct compilation, import `compile` from `@effectweb/compiler`; the result contains generated code, a source map, and diagnostics. The Vite plugin reports diagnostics and enables development instrumentation during serve.
14
+ For direct compilation, import `compile` from `@effectweb/compiler`; the result contains generated code, a source map, and diagnostics. The Vite plugin reports syntax diagnostics and enables development instrumentation during serve.
15
+
16
+ The compiler lowers JSX syntax. Calls, callbacks, local variables, control flow, and JSX helpers retain ordinary JavaScript behavior. `view` is an executable runtime function; the compiler does not prove render purity, infer model dependencies, memoize helpers, or reinterpret `.map` as keyed rendering. Use the runtime's explicit `list(rows, render)` operation for keyed lists.
17
+
18
+ Files opt in by importing `effectweb` or one of its subpaths, or by declaring `/** @jsxImportSource effectweb */`. A different `@jsxImportSource` pragma leaves the file to its chosen JSX runtime. `compile` returns other files unchanged. `importSource` and `runtimeModule` options customize these module names. The package also supports the standard automatic JSX transform through `effectweb/jsx-runtime`; the native compiler adds source metadata in development.
15
19
 
16
20
  Requires Node.js 22.14+. Optional packages provide binaries for glibc Linux x64/arm64, macOS x64/arm64, and Windows x64. Keep optional dependencies enabled. Installation does not compile Rust. Framework contributors build from source using Rust 1.96+.
17
21
 
18
22
  See [EffectWeb](https://github.com/DerpyCrabs/EffectWeb) for authoring and development instructions.
19
23
 
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.
24
+ For editor and CI diagnostics, add `@effectweb/compiler/oxlint` to Oxlint's `jsPlugins`. `effectweb/valid-view` checks supported JSX syntax, and `diagnose` exposes the same structured diagnostics without emitting code. Parser failures throw.
21
25
 
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.
26
+ `effectweb/render-safety` is an optional heuristic lint rule for suspicious render work and captures. The separate `lint` API exposes these checks. They never participate in compilation or change generated code, and they are not a proof of purity. The former `whole-model-dependency` rule and collection-identity inference have been removed.
27
+
28
+ 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. Development instrumentation labels actual text and attribute expressions and their evaluated values; production builds omit that metadata. It does not derive model-field dependency paths.
23
29
 
24
30
  Diagnostics include `code`, `category`, `severity`, source `file`/`line`/`column` (one-based UTF-16 columns), and an actionable `remedy`:
25
31
 
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. |
32
+ | Code | Category | Meaning and remedy |
33
+ | ------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
34
+ | EW1000 | correctness | Lint could not parse the file or load the native compiler. Fix syntax or installation. |
35
+ | EW1001 | correctness | Unsupported intrinsic JSX syntax, such as `key`, `ref`, or `innerHTML`. Use explicit lists or owned DOM bindings. |
36
+ | EW1003 | correctness | Optional render-safety lint found potentially impure work. Review it and move side effects into owned work. |
37
+ | EW2001 | unprovable-dependency | Optional render-safety lint found a potentially mutable capture. Review its ownership; compilation itself accepts ordinary JavaScript captures. |
38
+ | EW2002 | unprovable-dependency | Query-key lint found a custom `key`. Remove it; every request argument participates in cache identity. The public query types and runtime also reject custom keys. |
34
39
 
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:
40
+ `effectweb/query-key` recognizes imported `query` definitions, including aliases and `effectweb/query`, with literal custom keys. Types and the runtime cover definitions assembled outside that lint analysis. Supply service instances through the Effect environment. For example:
36
41
 
37
42
  ```json
38
43
  {
@@ -40,11 +45,9 @@ Diagnostics include `code`, `category`, `severity`, source `file`/`line`/`column
40
45
  "rules": {
41
46
  "effectweb/valid-view": "error",
42
47
  "effectweb/query-key": "error",
43
- "effectweb/whole-model-dependency": "warn"
48
+ "effectweb/render-safety": "warn"
44
49
  }
45
50
  }
46
51
  ```
47
52
 
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.
53
+ Remove `effectweb/render-safety` when heuristic advice is not wanted. TypeScript checks props, snapshots, and Effect service/error contracts; the compiler does not load or check the consumer's TypeScript project.
package/dist/compile.d.ts CHANGED
@@ -13,12 +13,6 @@ export interface CompilerOptions {
13
13
  importSource?: string;
14
14
  runtimeModule?: string;
15
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[]>>;
22
16
  onDiagnostic?: (diagnostic: Diagnostic) => void;
23
17
  }
24
18
  export interface CompilerResult {
@@ -27,5 +21,8 @@ export interface CompilerResult {
27
21
  readonly diagnostics: readonly Diagnostic[];
28
22
  }
29
23
  export declare function compile(source: string, filename: string, options?: CompilerOptions): CompilerResult;
30
- /** Run the same view analysis as compilation, collecting one error per invalid view. */
24
+ /** Collect compiler syntax diagnostics without emitting code. */
31
25
  export declare function diagnose(source: string, filename: string, options?: Omit<CompilerOptions, 'onDiagnostic'>): readonly Diagnostic[];
26
+ export type LintOptions = Pick<CompilerOptions, 'importSource'>;
27
+ /** Optional heuristic lint checks. These never participate in compilation. */
28
+ export declare function lint(source: string, filename: string, options?: LintOptions): readonly Diagnostic[];
package/dist/compile.js CHANGED
@@ -6,12 +6,20 @@ export function compile(source, filename, options = {}) {
6
6
  onDiagnostic?.(diagnostic);
7
7
  return result;
8
8
  }
9
- /** Run the same view analysis as compilation, collecting one error per invalid view. */
9
+ /** Collect compiler syntax diagnostics without emitting code. */
10
10
  export function diagnose(source, filename, options = {}) {
11
+ return diagnostics(source, filename, options, false);
12
+ }
13
+ /** Optional heuristic lint checks. These never participate in compilation. */
14
+ export function lint(source, filename, options = {}) {
15
+ return diagnostics(source, filename, options, true);
16
+ }
17
+ function diagnostics(source, filename, options, lint) {
11
18
  return JSON.parse(nativeCompile(source, filename, JSON.stringify({
12
19
  importSource: 'effectweb',
13
20
  ...options,
14
21
  development: true,
15
22
  diagnosticsOnly: true,
23
+ lint,
16
24
  }))).diagnostics;
17
25
  }
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { compile, diagnose, type CompilerOptions, type CompilerResult, type Diagnostic, } from './compile.js';
1
+ export { compile, diagnose, lint, type LintOptions, type CompilerOptions, type CompilerResult, type Diagnostic, } from './compile.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { compile, diagnose, } from './compile.js';
1
+ export { compile, diagnose, lint, } from './compile.js';
package/dist/oxlint.d.ts CHANGED
@@ -5,7 +5,7 @@ type Source = {
5
5
  type Context = {
6
6
  filename: string;
7
7
  sourceCode: Source;
8
- options: readonly Pick<CompilerOptions, 'importSource' | 'pureImports'>[];
8
+ options: readonly Pick<CompilerOptions, 'importSource'>[];
9
9
  report: (diagnostic: {
10
10
  loc: {
11
11
  line: number;
@@ -28,15 +28,6 @@ declare const _default: {
28
28
  importSource: {
29
29
  type: string;
30
30
  };
31
- pureImports: {
32
- type: string;
33
- additionalProperties: {
34
- type: string;
35
- items: {
36
- type: string;
37
- };
38
- };
39
- };
40
31
  };
41
32
  additionalProperties: boolean;
42
33
  }[];
@@ -45,7 +36,7 @@ declare const _default: {
45
36
  Program(): void;
46
37
  };
47
38
  };
48
- 'whole-model-dependency': {
39
+ 'render-safety': {
49
40
  meta: {
50
41
  type: "problem" | "suggestion";
51
42
  schema: {
@@ -54,15 +45,6 @@ declare const _default: {
54
45
  importSource: {
55
46
  type: string;
56
47
  };
57
- pureImports: {
58
- type: string;
59
- additionalProperties: {
60
- type: string;
61
- items: {
62
- type: string;
63
- };
64
- };
65
- };
66
48
  };
67
49
  additionalProperties: boolean;
68
50
  }[];
@@ -80,15 +62,6 @@ declare const _default: {
80
62
  importSource: {
81
63
  type: string;
82
64
  };
83
- pureImports: {
84
- type: string;
85
- additionalProperties: {
86
- type: string;
87
- items: {
88
- type: string;
89
- };
90
- };
91
- };
92
65
  };
93
66
  additionalProperties: boolean;
94
67
  }[];
package/dist/oxlint.js CHANGED
@@ -1,4 +1,4 @@
1
- import { diagnose } from './compile.js';
1
+ import { diagnose, lint } from './compile.js';
2
2
  import { isCompilerFile } from './files.js';
3
3
  const results = new WeakMap();
4
4
  function rule(category) {
@@ -8,13 +8,7 @@ function rule(category) {
8
8
  schema: [
9
9
  {
10
10
  type: 'object',
11
- properties: {
12
- importSource: { type: 'string' },
13
- pureImports: {
14
- type: 'object',
15
- additionalProperties: { type: 'array', items: { type: 'string' } },
16
- },
17
- },
11
+ properties: { importSource: { type: 'string' } },
18
12
  additionalProperties: false,
19
13
  },
20
14
  ],
@@ -25,20 +19,17 @@ function rule(category) {
25
19
  if (!isCompilerFile(context.filename))
26
20
  return;
27
21
  const importSource = context.options[0]?.importSource ?? 'effectweb';
28
- const pureImports = context.options[0]?.pureImports;
29
22
  let cached = results.get(context.sourceCode);
30
23
  if (!cached || cached.text !== context.sourceCode.text) {
31
24
  cached = { text: context.sourceCode.text, diagnostics: new Map() };
32
25
  results.set(context.sourceCode, cached);
33
26
  }
34
- const key = `${context.filename}\0${importSource}\0${JSON.stringify(pureImports)}`;
27
+ const heuristic = category === 'render-safety' || category === 'unprovable-dependency';
28
+ const key = `${context.filename}\0${importSource}\0${heuristic}`;
35
29
  let diagnostics = cached.diagnostics.get(key);
36
30
  if (!diagnostics) {
37
31
  try {
38
- diagnostics = diagnose(context.sourceCode.text, context.filename, {
39
- importSource,
40
- ...(pureImports ? { pureImports } : {}),
41
- });
32
+ diagnostics = (heuristic ? lint : diagnose)(context.sourceCode.text, context.filename, { importSource });
42
33
  }
43
34
  catch (error) {
44
35
  // Parser failures and unavailable native binaries must not silently pass lint.
@@ -58,11 +49,13 @@ function rule(category) {
58
49
  cached.diagnostics.set(key, diagnostics);
59
50
  }
60
51
  for (const diagnostic of diagnostics) {
61
- const selected = category === 'unprovable-dependency'
62
- ? diagnostic.code === 'EW2002' || diagnostic.code === 'EW1000'
63
- : category === 'errors'
64
- ? diagnostic.severity === 'error'
65
- : diagnostic.category === category;
52
+ const selected = category === 'render-safety'
53
+ ? ['EW1000', 'EW1003', 'EW2001'].includes(diagnostic.code)
54
+ : category === 'unprovable-dependency'
55
+ ? diagnostic.code === 'EW2002' || diagnostic.code === 'EW1000'
56
+ : category === 'errors'
57
+ ? diagnostic.severity === 'error'
58
+ : diagnostic.category === category;
66
59
  if (!selected)
67
60
  continue;
68
61
  context.report({
@@ -79,7 +72,7 @@ export default {
79
72
  meta: { name: 'effectweb' },
80
73
  rules: {
81
74
  'valid-view': rule('errors'),
82
- 'whole-model-dependency': rule('performance'),
75
+ 'render-safety': rule('render-safety'),
83
76
  'query-key': rule('unprovable-dependency'),
84
77
  },
85
78
  };
package/dist/vite.js CHANGED
@@ -6,7 +6,8 @@ export function effectweb(options = {}) {
6
6
  config(_config, environment) {
7
7
  return {
8
8
  define: { __EFFECTWEB_DEV__: JSON.stringify(environment.command === 'serve') },
9
- resolve: { dedupe: ['effect'] },
9
+ resolve: { dedupe: ['effect', 'effectweb'] },
10
+ optimizeDeps: { exclude: ['effectweb'] },
10
11
  };
11
12
  },
12
13
  configResolved(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effectweb/compiler",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
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.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"
49
+ "@effectweb/compiler-darwin-arm64": "0.5.0",
50
+ "@effectweb/compiler-darwin-x64": "0.5.0",
51
+ "@effectweb/compiler-linux-arm64-gnu": "0.5.0",
52
+ "@effectweb/compiler-linux-x64-gnu": "0.5.0",
53
+ "@effectweb/compiler-win32-x64-msvc": "0.5.0"
54
54
  },
55
55
  "engines": {
56
56
  "node": ">=22.14"