@motifx/compiler 1.0.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/LICENSE +21 -0
- package/README.md +91 -0
- package/bin/motif-explain.cjs +47 -0
- package/bin/motif-lint.cjs +33 -0
- package/dist/index.cjs +3422 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +83 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.esm.js +3388 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +95 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Ekrem Erşahin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @motifx/compiler
|
|
2
|
+
|
|
3
|
+
The JSX compiler for [MotifJS](https://www.npmjs.com/package/@motifx/core). It lowers JSX at build time into fine-grained reactive bindings on real DOM nodes: `{state.name}` becomes a binding on that one text node, not a re-rendered tree.
|
|
4
|
+
|
|
5
|
+
It ships as a Vite plugin (also usable in Rollup) and two command-line tools.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -D @motifx/compiler typescript
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Vite
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineConfig } from 'vite';
|
|
17
|
+
import compiler from '@motifx/compiler';
|
|
18
|
+
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
plugins: [compiler()],
|
|
21
|
+
esbuild: { jsx: 'preserve' },
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Set `"jsx": "preserve"` in `tsconfig.json` as well, so that only `@motifx/compiler` compiles JSX. Vitest picks up the same plugin from the Vite config.
|
|
26
|
+
|
|
27
|
+
## Rollup
|
|
28
|
+
|
|
29
|
+
The same plugin works in a plain Rollup build:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import compiler from '@motifx/compiler';
|
|
33
|
+
|
|
34
|
+
export default {
|
|
35
|
+
input: 'src/index.tsx',
|
|
36
|
+
output: { dir: 'dist', format: 'esm' },
|
|
37
|
+
plugins: [compiler()],
|
|
38
|
+
};
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Which files are compiled
|
|
42
|
+
|
|
43
|
+
Files ending in `.tsx`, `.jsx`, `.mtsx`, `.mjsx` or `.aio`. In `.ts`, `.mts`, `.cts`, `.js`, `.mjs` and `.cjs` files that contain decorators, only the standard (TC39) decorators are compiled, so `@Injectable` behaves the same in every file in `vite dev` and `vite build`. Files whose nearest `tsconfig.json` enables `experimentalDecorators`, `.d.ts` files and files under `node_modules` are left to Vite. Imports with a `?raw`, `?url`, `?worker`, `?sharedworker` or `?inline` query and virtual modules are left untouched.
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
compiler({
|
|
49
|
+
diagnostics: true,
|
|
50
|
+
explain: false,
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Option | Default | Description |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| `diagnostics` | `true` | Print compiler warnings (`MJX001`–`MJX004`, `MJX007`) for patterns that compile but do not behave as they read. |
|
|
57
|
+
| `explain` | `false` | Print what every JSX expression was lowered to. `true` for all files, or a string / `RegExp` matched against the file path. |
|
|
58
|
+
|
|
59
|
+
## Command-line tools
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx motif-lint [--project tsconfig.json] [--json] [--no-fail] [file.tsx …]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Type-aware lint (`MJX005`): reports a ternary passed to a component prop whose declared type does not accept a getter. Exits with code 1 when there are findings (`--no-fail` to disable).
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx motif-explain src/App.tsx [more.tsx …] [--json] [--site child|attr|prop|…] [--code]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Shows the call each JSX expression compiles to, whether it is reactive, and what it depends on. `--code` also prints the full generated code.
|
|
72
|
+
|
|
73
|
+
Both tools are also available as functions: `lintProject`, `lintProgram`, `explain`, `printDiagnostics`, `printExplanations`, `formatExplanations`.
|
|
74
|
+
|
|
75
|
+
## Exports
|
|
76
|
+
|
|
77
|
+
The default export is the plugin function. Named exports: `Compiler` (`new Compiler().start(code, filename)` returns the Babel result, with `diagnostics` on the instance), `explain`, `lintProject`, `lintProgram`, `printDiagnostics`, `printExplanations`, `formatExplanations`, and the types `MotifVitePluginOptions`, `MotifDiagnostic`, `MotifExplanation`, `ExplainSite`, `ExplainReactivity` and `LintOptions`.
|
|
78
|
+
|
|
79
|
+
## Runtime contract
|
|
80
|
+
|
|
81
|
+
Compiled code calls into the `@motifx/core` runtime:
|
|
82
|
+
|
|
83
|
+
- the `motifComponent`, `motifFragment`, `FNComponent`, `motifCompiled`, `asyncTracking` and `Component` exports;
|
|
84
|
+
- component members: `bindings.*` (each binding directive `x-wait`, `x-display`, `x-model`, `x-text`, `x-html`, `x-value` and `x-watch` calls the method of the same name), `class.add`, `attr.add`, `style`, `motif.on`, `controls.add`, `setText`, and `navigate` on the frame passed to conditional branches;
|
|
85
|
+
- property keys: `runover`, `initializeComponent`, `preconfig`, `ref`, `childs`, `__childExpr`, `__isSvgElement`, `onRefCreated`, the lifecycle hook props that lifecycle directives become (`x-mounted` becomes `onmounted`), and the static class fields `elementTag` and `elementNamespace`.
|
|
86
|
+
|
|
87
|
+
This set is the compiler contract. It changes only in a major version, so `@motifx/compiler` 1.x works with any `@motifx/core` 1.x. Every compiled module calls `motifCompiled(<contract>)` once; in development the runtime warns with `MJX121` when the numbers differ, for example when a package that ships compiled JSX was built for another major version. The call is marked pure, so production builds drop it.
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* motif-explain — "what did this expression compile to?" (the compiler's -S flag)
|
|
4
|
+
*
|
|
5
|
+
* npx motif-explain src/App.tsx [more.tsx…] [--json] [--site child|attr|prop|…] [--code]
|
|
6
|
+
*
|
|
7
|
+
* Compiles the source and prints, for every JSX expression, the actual call it lowers to,
|
|
8
|
+
* its reactivity class and its dependency surface. `--code` also prints the full generated code.
|
|
9
|
+
*/
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const compiler = require('../dist/index.cjs');
|
|
13
|
+
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const files = [];
|
|
16
|
+
let json = false, code = false, site = null;
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const a = args[i];
|
|
19
|
+
if (a === '--json') json = true;
|
|
20
|
+
else if (a === '--code') code = true;
|
|
21
|
+
else if (a === '--site') site = args[++i];
|
|
22
|
+
else if (a === '-h' || a === '--help') { console.log(fs.readFileSync(__filename, 'utf8').split('*/')[0]); process.exit(0); }
|
|
23
|
+
else files.push(a);
|
|
24
|
+
}
|
|
25
|
+
if (files.length === 0) {
|
|
26
|
+
console.error('usage: motif-explain <file.tsx> [...] [--json] [--site child|attr|prop|event|directive] [--code]');
|
|
27
|
+
process.exit(2);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const Compiler = compiler.Compiler;
|
|
31
|
+
const all = [];
|
|
32
|
+
for (const f of files) {
|
|
33
|
+
const abs = path.resolve(f);
|
|
34
|
+
const src = fs.readFileSync(abs, 'utf8');
|
|
35
|
+
const c = new Compiler();
|
|
36
|
+
const shown = path.relative(process.cwd(), abs).split(path.sep).join('/');
|
|
37
|
+
const result = c.explain(src, shown);
|
|
38
|
+
let list = c.explanations;
|
|
39
|
+
if (site) list = list.filter(e => e.site === site);
|
|
40
|
+
all.push(...list);
|
|
41
|
+
if (!json) {
|
|
42
|
+
process.stdout.write(compiler.formatExplanations(list) + '\n');
|
|
43
|
+
if (c.diagnostics.length) compiler.printDiagnostics(c.diagnostics);
|
|
44
|
+
if (code && result && result.code) process.stdout.write('\n--- generated code ---\n' + result.code + '\n');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (json) process.stdout.write(JSON.stringify(all, null, 2) + '\n');
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* motif-lint — type-aware JSX check (MJX005: ternary passed to a prop that is not Bind<T>)
|
|
4
|
+
*
|
|
5
|
+
* npx motif-lint [--project tsconfig.json] [--json] [--no-fail] [file.tsx …]
|
|
6
|
+
*
|
|
7
|
+
* Builds the TypeScript program and reports a ternary written to a component prop whose
|
|
8
|
+
* declared type does not accept a function (the compiler always wraps a ternary in a getter).
|
|
9
|
+
* Exits with code 1 when there are findings (0 with `--no-fail`).
|
|
10
|
+
*/
|
|
11
|
+
const compiler = require('../dist/index.cjs');
|
|
12
|
+
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
let project = 'tsconfig.json', json = false, fail = true;
|
|
15
|
+
const files = [];
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
const a = args[i];
|
|
18
|
+
if (a === '--project' || a === '-p') project = args[++i];
|
|
19
|
+
else if (a === '--json') json = true;
|
|
20
|
+
else if (a === '--no-fail') fail = false;
|
|
21
|
+
else if (a === '-h' || a === '--help') { console.log(require('fs').readFileSync(__filename, 'utf8').split('*/')[0]); process.exit(0); }
|
|
22
|
+
else files.push(a);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const t0 = Date.now();
|
|
26
|
+
const list = compiler.lintProject({ project, files: files.length ? files : undefined });
|
|
27
|
+
if (json) {
|
|
28
|
+
process.stdout.write(JSON.stringify(list, null, 2) + '\n');
|
|
29
|
+
} else {
|
|
30
|
+
compiler.printDiagnostics(list);
|
|
31
|
+
console.error(`[motifjs] lint: ${list.length} findings (${Date.now() - t0} ms)`);
|
|
32
|
+
}
|
|
33
|
+
process.exit(list.length && fail ? 1 : 0);
|