oxidil 0.0.1 → 0.2.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/LICENSE +21 -0
- package/README.md +101 -1
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +213 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +39 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +6 -0
- package/package.json +51 -1
- package/wasm/oxidil.d.ts +34 -0
- package/wasm/oxidil.js +245 -0
- package/wasm/oxidil_bg.wasm +0 -0
- package/wasm/oxidil_bg.wasm.d.ts +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Astrid Gealer
|
|
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
CHANGED
|
@@ -1 +1,101 @@
|
|
|
1
|
-
|
|
1
|
+
# oxidil
|
|
2
|
+
|
|
3
|
+
A **JavaScript / TypeScript optimizing compiler** — the Rust [oxidil](https://github.com/astridgealer/oxidil)
|
|
4
|
+
crate (built on the [oxc](https://github.com/oxc-project/oxc) ecosystem) compiled
|
|
5
|
+
to WebAssembly and wrapped in a typed Node API and CLI.
|
|
6
|
+
|
|
7
|
+
It parses JS/TS, optionally strips TypeScript types, runs a set of **sound**
|
|
8
|
+
optimization passes gated by a GCC-flavored `-O` level, and emits optimized
|
|
9
|
+
JavaScript plus a v3 source map. Everything runs in-process inside the WASM
|
|
10
|
+
module — no `node`/`terser`/`esbuild`/`swc` is shelled out to.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install oxidil
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires Node.js >= 18. The WebAssembly module is bundled — there is no native
|
|
19
|
+
build step at install time.
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { compile } from "oxidil";
|
|
25
|
+
|
|
26
|
+
const { code, map } = compile("const a = 1 + 2 * 3;\nconsole.log(a);\n", {
|
|
27
|
+
level: "2",
|
|
28
|
+
sourceMap: true,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
console.log(code); // "const a = 7;\nconsole.log(a);\n"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `compile(source, options?) => { code, map? }`
|
|
35
|
+
|
|
36
|
+
| Option | Type | Default | Meaning |
|
|
37
|
+
|--------|------|---------|---------|
|
|
38
|
+
| `filename` | `string` | `"input.js"` | Source type is inferred from the extension (`.js .jsx .mjs .cjs .ts .tsx`); also used as the `sources` entry of the map. |
|
|
39
|
+
| `level` | `"0"\|"1"\|"2"\|"3"\|"s"\|"z"` (or `0`–`3`) | `"2"` | Optimization level (see below). |
|
|
40
|
+
| `tsTypeof` | `boolean` | `false` | Enable the `ts-typeof-elimination` pass (effective only at level >= 1). |
|
|
41
|
+
| `enable` | `string[]` | `[]` | Force-include passes by id even if the level gates them off. |
|
|
42
|
+
| `disable` | `string[]` | `[]` | Force-exclude passes by id. Disable wins over enable. |
|
|
43
|
+
| `sourceMap` | `boolean` | `false` | Produce an output v3 source map (returned as `map`, a JSON string). |
|
|
44
|
+
| `inputSourceMap` | `string` | — | JSON of an input map; the output map is composed back to the original sources. |
|
|
45
|
+
|
|
46
|
+
## CLI
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
oxidil <INPUT> --out <FILE> [--out-map <FILE>] [--source-map <FILE>]
|
|
50
|
+
[-O<level>] [--ts-typeof]
|
|
51
|
+
[--enable <ID>]... [--disable <ID>]...
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
npx oxidil app.ts --out app.js --out-map app.js.map -O2
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
| Flag | Meaning |
|
|
59
|
+
|------|---------|
|
|
60
|
+
| `<INPUT>` | Input file. Source type inferred from extension. |
|
|
61
|
+
| `--out <FILE>` | **Required.** Where optimized JS is written. |
|
|
62
|
+
| `--out-map <FILE>` | Where the output source map is written (and a `//# sourceMappingURL=` comment is appended). |
|
|
63
|
+
| `--source-map <FILE>` | Input source map for `<INPUT>`; the output map is composed back to the original sources. |
|
|
64
|
+
| `--ts-typeof` | Enable `ts-typeof-elimination`. |
|
|
65
|
+
| `-O0`/`-O1`/`-O2`/`-O3`/`-Os` | Optimization level (default `-O2`). `-O` == `-O1`, `-Oz` == `-Os`. Aliases `-0`..`-3`/`-s` and `--O0`..`--Os` also work. |
|
|
66
|
+
| `--enable <ID>` / `--disable <ID>` | Force a pass on/off (repeatable; disable wins). |
|
|
67
|
+
|
|
68
|
+
Exit codes: `0` ok, `1` parse error, `2` IO error, `64` usage error.
|
|
69
|
+
|
|
70
|
+
## Optimization levels
|
|
71
|
+
|
|
72
|
+
| Level | Passes |
|
|
73
|
+
|-------|--------|
|
|
74
|
+
| `-O0` | None (parse → type-strip if TS → passthrough). |
|
|
75
|
+
| `-O1` | Constant folding + peephole/algebraic. |
|
|
76
|
+
| `-O2` *(default)* | `-O1` + dead-code elimination at full fixpoint. |
|
|
77
|
+
| `-O3` | `-O2` with a higher fixpoint cap (more aggressive). |
|
|
78
|
+
| `-Os` / `-Oz` | `-O2` + identifier minify/rename + compact codegen (size). |
|
|
79
|
+
|
|
80
|
+
## Building from source
|
|
81
|
+
|
|
82
|
+
The published package ships prebuilt WASM. To rebuild it you need the Rust
|
|
83
|
+
toolchain plus [`wasm-pack`](https://rustwasm.github.io/wasm-pack/). The version
|
|
84
|
+
lives in the crate's `Cargo.toml` (the source of truth); `npm/package.json` and
|
|
85
|
+
`npm/src/version.ts` are generated from it and are gitignored — so build from the
|
|
86
|
+
repo root with `make`:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
make npm # generates the version files, then runs wasm-pack + tsc
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Or, if the generated files already exist (`make generate`):
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
npm run build # build:wasm (wasm-pack) + build:ts (tsc)
|
|
96
|
+
npm test
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* oxidil CLI — a thin Node front-end over the wasm compile core.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the native Rust binary's interface:
|
|
6
|
+
* oxidil <INPUT> --out <FILE> [--out-map <FILE>] [--source-map <FILE>]
|
|
7
|
+
* [-O<level>] [--ts-typeof]
|
|
8
|
+
* [--enable <ID>]... [--disable <ID>]...
|
|
9
|
+
*
|
|
10
|
+
* Exit codes match the native binary: 0 ok, 1 parse error, 2 IO/other,
|
|
11
|
+
* 64 usage error.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* oxidil CLI — a thin Node front-end over the wasm compile core.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the native Rust binary's interface:
|
|
7
|
+
* oxidil <INPUT> --out <FILE> [--out-map <FILE>] [--source-map <FILE>]
|
|
8
|
+
* [-O<level>] [--ts-typeof]
|
|
9
|
+
* [--enable <ID>]... [--disable <ID>]...
|
|
10
|
+
*
|
|
11
|
+
* Exit codes match the native binary: 0 ok, 1 parse error, 2 IO/other,
|
|
12
|
+
* 64 usage error.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const node_fs_1 = require("node:fs");
|
|
16
|
+
const node_path_1 = require("node:path");
|
|
17
|
+
const index_js_1 = require("./index.js");
|
|
18
|
+
// `version.ts` is generated from Cargo.toml (the source of truth) by the
|
|
19
|
+
// Makefile and is gitignored. Run `make generate` (or `make`) before building.
|
|
20
|
+
const version_js_1 = require("./version.js");
|
|
21
|
+
const USAGE = `oxidil ${version_js_1.VERSION} — a JS/TS optimizing compiler (oxc front-end, wasm)
|
|
22
|
+
|
|
23
|
+
USAGE:
|
|
24
|
+
oxidil <INPUT> --out <FILE> [OPTIONS]
|
|
25
|
+
|
|
26
|
+
ARGS:
|
|
27
|
+
<INPUT> Input file (.js/.jsx/.mjs/.cjs/.ts/.tsx).
|
|
28
|
+
|
|
29
|
+
OPTIONS:
|
|
30
|
+
--out <FILE> Where optimized JS is written. (required)
|
|
31
|
+
--out-map <FILE> Where the output source map is written.
|
|
32
|
+
--source-map <FILE> Input source map for <INPUT>; output map is composed
|
|
33
|
+
back to the original sources.
|
|
34
|
+
--ts-typeof Enable ts-typeof-elimination (level >= 1 only).
|
|
35
|
+
-O0|-O1|-O2|-O3|-Os Optimization level (default -O2). -O == -O1, -Oz == -Os.
|
|
36
|
+
Aliases: -0/-1/-2/-3/-s and --O0..--Os.
|
|
37
|
+
--enable <ID> Force-include a pass by id (repeatable).
|
|
38
|
+
--disable <ID> Force-exclude a pass by id (repeatable). Disable wins.
|
|
39
|
+
-h, --help Print help.
|
|
40
|
+
-V, --version Print version.`;
|
|
41
|
+
class UsageError extends Error {
|
|
42
|
+
}
|
|
43
|
+
/** Map an `-O`/`-N` token to a level, or `undefined` if it isn't one. */
|
|
44
|
+
function levelOfToken(tok) {
|
|
45
|
+
switch (tok) {
|
|
46
|
+
case "-O":
|
|
47
|
+
case "-O1":
|
|
48
|
+
case "--O1":
|
|
49
|
+
case "-1":
|
|
50
|
+
return "1";
|
|
51
|
+
case "-O0":
|
|
52
|
+
case "--O0":
|
|
53
|
+
case "-0":
|
|
54
|
+
return "0";
|
|
55
|
+
case "-O2":
|
|
56
|
+
case "--O2":
|
|
57
|
+
case "-2":
|
|
58
|
+
return "2";
|
|
59
|
+
case "-O3":
|
|
60
|
+
case "--O3":
|
|
61
|
+
case "-3":
|
|
62
|
+
return "3";
|
|
63
|
+
case "-Os":
|
|
64
|
+
case "--Os":
|
|
65
|
+
case "-s":
|
|
66
|
+
return "s";
|
|
67
|
+
case "-Oz":
|
|
68
|
+
return "z";
|
|
69
|
+
default:
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function needValue(argv, i, flag) {
|
|
74
|
+
const v = argv[i + 1];
|
|
75
|
+
if (v === undefined || v.startsWith("-")) {
|
|
76
|
+
throw new UsageError(`option '${flag}' requires a value`);
|
|
77
|
+
}
|
|
78
|
+
return v;
|
|
79
|
+
}
|
|
80
|
+
function parseArgs(argv) {
|
|
81
|
+
const parsed = {
|
|
82
|
+
tsTypeof: false,
|
|
83
|
+
level: "2",
|
|
84
|
+
enable: [],
|
|
85
|
+
disable: [],
|
|
86
|
+
};
|
|
87
|
+
for (let i = 0; i < argv.length; i++) {
|
|
88
|
+
const a = argv[i];
|
|
89
|
+
const lvl = levelOfToken(a);
|
|
90
|
+
if (lvl !== undefined) {
|
|
91
|
+
parsed.level = lvl;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Support `--flag=value` spellings as well as `--flag value`.
|
|
95
|
+
const eq = a.startsWith("--") ? a.indexOf("=") : -1;
|
|
96
|
+
const flag = eq >= 0 ? a.slice(0, eq) : a;
|
|
97
|
+
const inlineVal = eq >= 0 ? a.slice(eq + 1) : undefined;
|
|
98
|
+
const value = (f) => inlineVal ?? needValue(argv, i, f);
|
|
99
|
+
const consume = () => {
|
|
100
|
+
if (inlineVal === undefined)
|
|
101
|
+
i++;
|
|
102
|
+
};
|
|
103
|
+
switch (flag) {
|
|
104
|
+
case "-h":
|
|
105
|
+
case "--help":
|
|
106
|
+
console.log(USAGE);
|
|
107
|
+
process.exit(0);
|
|
108
|
+
// eslint-disable-next-line no-fallthrough
|
|
109
|
+
case "-V":
|
|
110
|
+
case "--version":
|
|
111
|
+
console.log(version_js_1.VERSION);
|
|
112
|
+
process.exit(0);
|
|
113
|
+
// eslint-disable-next-line no-fallthrough
|
|
114
|
+
case "--out":
|
|
115
|
+
parsed.out = value(flag);
|
|
116
|
+
consume();
|
|
117
|
+
break;
|
|
118
|
+
case "--out-map":
|
|
119
|
+
parsed.outMap = value(flag);
|
|
120
|
+
consume();
|
|
121
|
+
break;
|
|
122
|
+
case "--source-map":
|
|
123
|
+
parsed.sourceMap = value(flag);
|
|
124
|
+
consume();
|
|
125
|
+
break;
|
|
126
|
+
case "--ts-typeof":
|
|
127
|
+
parsed.tsTypeof = true;
|
|
128
|
+
break;
|
|
129
|
+
case "--enable":
|
|
130
|
+
parsed.enable.push(value(flag));
|
|
131
|
+
consume();
|
|
132
|
+
break;
|
|
133
|
+
case "--disable":
|
|
134
|
+
parsed.disable.push(value(flag));
|
|
135
|
+
consume();
|
|
136
|
+
break;
|
|
137
|
+
default:
|
|
138
|
+
if (a.startsWith("-")) {
|
|
139
|
+
throw new UsageError(`unexpected option '${a}'`);
|
|
140
|
+
}
|
|
141
|
+
if (parsed.input !== undefined) {
|
|
142
|
+
throw new UsageError(`unexpected argument '${a}' (input already set)`);
|
|
143
|
+
}
|
|
144
|
+
parsed.input = a;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
149
|
+
function main() {
|
|
150
|
+
let args;
|
|
151
|
+
try {
|
|
152
|
+
args = parseArgs(process.argv.slice(2));
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
process.stderr.write(`${e.message}\n\n${USAGE}\n`);
|
|
156
|
+
process.exit(64);
|
|
157
|
+
}
|
|
158
|
+
if (args.input === undefined) {
|
|
159
|
+
process.stderr.write(`missing required <INPUT>\n\n${USAGE}\n`);
|
|
160
|
+
process.exit(64);
|
|
161
|
+
}
|
|
162
|
+
if (args.out === undefined) {
|
|
163
|
+
process.stderr.write(`missing required --out <FILE>\n\n${USAGE}\n`);
|
|
164
|
+
process.exit(64);
|
|
165
|
+
}
|
|
166
|
+
let source;
|
|
167
|
+
let inputSourceMap;
|
|
168
|
+
try {
|
|
169
|
+
source = (0, node_fs_1.readFileSync)(args.input, "utf8");
|
|
170
|
+
if (args.sourceMap !== undefined) {
|
|
171
|
+
inputSourceMap = (0, node_fs_1.readFileSync)(args.sourceMap, "utf8");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
process.stderr.write(`IO error: ${e.message}\n`);
|
|
176
|
+
process.exit(2);
|
|
177
|
+
}
|
|
178
|
+
let code;
|
|
179
|
+
let map;
|
|
180
|
+
try {
|
|
181
|
+
const result = (0, index_js_1.compile)(source, {
|
|
182
|
+
filename: args.input,
|
|
183
|
+
level: args.level,
|
|
184
|
+
tsTypeof: args.tsTypeof,
|
|
185
|
+
enable: args.enable,
|
|
186
|
+
disable: args.disable,
|
|
187
|
+
sourceMap: true,
|
|
188
|
+
inputSourceMap,
|
|
189
|
+
});
|
|
190
|
+
code = result.code;
|
|
191
|
+
map = result.map;
|
|
192
|
+
}
|
|
193
|
+
catch (e) {
|
|
194
|
+
// The wasm core throws a single Error whose message is the formatted
|
|
195
|
+
// compiler diagnostic (parse errors, etc.).
|
|
196
|
+
process.stderr.write(`${e.message}\n`);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
if (args.outMap !== undefined && map !== undefined) {
|
|
201
|
+
(0, node_fs_1.writeFileSync)(args.outMap, map);
|
|
202
|
+
if (!code.endsWith("\n"))
|
|
203
|
+
code += "\n";
|
|
204
|
+
code += `//# sourceMappingURL=${(0, node_path_1.basename)(args.outMap)}\n`;
|
|
205
|
+
}
|
|
206
|
+
(0, node_fs_1.writeFileSync)(args.out, code);
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
process.stderr.write(`IO error: ${e.message}\n`);
|
|
210
|
+
process.exit(2);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
main();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oxidil — programmatic API.
|
|
3
|
+
*
|
|
4
|
+
* Thin, typed wrapper over the wasm-bindgen glue generated from the Rust crate.
|
|
5
|
+
* The heavy lifting (parse, type-strip, optimization passes, codegen) all runs
|
|
6
|
+
* inside the WebAssembly module; this layer only marshals options and results.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Optimization level, GCC-flavored:
|
|
10
|
+
* - `"0"` no optimization (parse -> type-strip if TS -> passthrough)
|
|
11
|
+
* - `"1"` constant folding + peephole/algebraic
|
|
12
|
+
* - `"2"` (default) `1` + dead-code elimination at full fixpoint
|
|
13
|
+
* - `"3"` `2` with a higher fixpoint cap (more aggressive)
|
|
14
|
+
* - `"s"` / `"z"` `2` + identifier minify/rename + compact codegen (size)
|
|
15
|
+
*
|
|
16
|
+
* Numbers (`0`–`3`) are accepted too and coerced to the string form.
|
|
17
|
+
*/
|
|
18
|
+
export type OptLevel = "0" | "1" | "2" | "3" | "s" | "z" | 0 | 1 | 2 | 3;
|
|
19
|
+
export interface CompileOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Logical input name. The `SourceType` is inferred from its extension
|
|
22
|
+
* (`.js .jsx .mjs .cjs .ts .tsx`) and it becomes the `sources` entry of the
|
|
23
|
+
* output map. Defaults to `"input.js"`.
|
|
24
|
+
*/
|
|
25
|
+
filename?: string;
|
|
26
|
+
/** Optimization level. Defaults to `"2"`. */
|
|
27
|
+
level?: OptLevel;
|
|
28
|
+
/** Enable the `ts-typeof-elimination` pass (effective only at level >= 1). */
|
|
29
|
+
tsTypeof?: boolean;
|
|
30
|
+
/** Force-include passes by id, even if the level would gate them off. */
|
|
31
|
+
enable?: string[];
|
|
32
|
+
/** Force-exclude passes by id. Disable wins over enable. */
|
|
33
|
+
disable?: string[];
|
|
34
|
+
/** Produce an output source map. Defaults to `false`. */
|
|
35
|
+
sourceMap?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* JSON of an INPUT source map (the map for `filename`). When provided together
|
|
38
|
+
* with `sourceMap`, the output map is composed so it points back to the
|
|
39
|
+
* ORIGINAL authored sources.
|
|
40
|
+
*/
|
|
41
|
+
inputSourceMap?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface CompileResult {
|
|
44
|
+
/** Optimized JavaScript source. */
|
|
45
|
+
code: string;
|
|
46
|
+
/** v3 source map (JSON string), present only when `sourceMap` was enabled. */
|
|
47
|
+
map?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compile (optimize) a JavaScript or TypeScript source string.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { compile } from "oxidil";
|
|
55
|
+
* const { code } = compile("const a = 1 + 2 * 3;", { level: "2" });
|
|
56
|
+
* // code === "const a = 7;\n"
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function compile(source: string, options?: CompileOptions): CompileResult;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* oxidil — programmatic API.
|
|
4
|
+
*
|
|
5
|
+
* Thin, typed wrapper over the wasm-bindgen glue generated from the Rust crate.
|
|
6
|
+
* The heavy lifting (parse, type-strip, optimization passes, codegen) all runs
|
|
7
|
+
* inside the WebAssembly module; this layer only marshals options and results.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.compile = compile;
|
|
11
|
+
const oxidil_js_1 = require("../wasm/oxidil.js");
|
|
12
|
+
/**
|
|
13
|
+
* Compile (optimize) a JavaScript or TypeScript source string.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { compile } from "oxidil";
|
|
18
|
+
* const { code } = compile("const a = 1 + 2 * 3;", { level: "2" });
|
|
19
|
+
* // code === "const a = 7;\n"
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
function compile(source, options = {}) {
|
|
23
|
+
const filename = options.filename ?? "input.js";
|
|
24
|
+
const level = String(options.level ?? "2");
|
|
25
|
+
const sourceMap = options.sourceMap ?? false;
|
|
26
|
+
const result = (0, oxidil_js_1.compile)(source, filename, level, options.tsTypeof ?? false, options.enable ?? [], options.disable ?? [], sourceMap, options.inputSourceMap ?? undefined);
|
|
27
|
+
try {
|
|
28
|
+
const out = { code: result.code };
|
|
29
|
+
const map = result.map;
|
|
30
|
+
if (map !== undefined) {
|
|
31
|
+
out.map = map;
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
// Release the wasm-owned struct; getters above already copied the strings.
|
|
37
|
+
result.free();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.2.0";
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,4 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxidil",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A JavaScript/TypeScript optimizing compiler (oxc front-end), compiled to WebAssembly. Parses JS/TS, strips types, runs sound -O-gated optimization passes, and emits optimized JS + a v3 source map.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"oxidil": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/IAmJSD/oxidil.git"
|
|
13
|
+
},
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist/index.js",
|
|
18
|
+
"dist/index.d.ts",
|
|
19
|
+
"dist/cli.js",
|
|
20
|
+
"dist/cli.d.ts",
|
|
21
|
+
"dist/version.js",
|
|
22
|
+
"dist/version.d.ts",
|
|
23
|
+
"wasm/oxidil.js",
|
|
24
|
+
"wasm/oxidil.d.ts",
|
|
25
|
+
"wasm/oxidil_bg.wasm",
|
|
26
|
+
"wasm/oxidil_bg.wasm.d.ts",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"keywords": [
|
|
31
|
+
"javascript",
|
|
32
|
+
"typescript",
|
|
33
|
+
"compiler",
|
|
34
|
+
"optimizer",
|
|
35
|
+
"minify",
|
|
36
|
+
"oxc",
|
|
37
|
+
"wasm",
|
|
38
|
+
"webassembly"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build:wasm": "wasm-pack build .. --target nodejs --out-dir npm/wasm --out-name oxidil --features wasm",
|
|
45
|
+
"build:ts": "tsc -p tsconfig.json",
|
|
46
|
+
"build": "npm run build:wasm && npm run build:ts",
|
|
47
|
+
"prepublishOnly": "npm run build",
|
|
48
|
+
"test": "node --test \"dist/test/**/*.test.js\""
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^20.11.0",
|
|
52
|
+
"typescript": "^5.4.0"
|
|
53
|
+
}
|
|
4
54
|
}
|
package/wasm/oxidil.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Result of a successful compile, surfaced to JS as an object with `code` and
|
|
6
|
+
* (optional) `map` string getters.
|
|
7
|
+
*/
|
|
8
|
+
export class CompileResult {
|
|
9
|
+
private constructor();
|
|
10
|
+
free(): void;
|
|
11
|
+
[Symbol.dispose](): void;
|
|
12
|
+
/**
|
|
13
|
+
* Optimized JavaScript source.
|
|
14
|
+
*/
|
|
15
|
+
readonly code: string;
|
|
16
|
+
/**
|
|
17
|
+
* v3 source map as a JSON string, or `undefined` when maps were disabled.
|
|
18
|
+
*/
|
|
19
|
+
readonly map: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Compile a JS/TS source string. Mirrors the native CLI semantics.
|
|
24
|
+
*
|
|
25
|
+
* - `source`: the input source text.
|
|
26
|
+
* - `filename`: logical name; `SourceType` is inferred from its extension and
|
|
27
|
+
* it becomes the `sources` entry of the output map.
|
|
28
|
+
* - `level`: optimization level — `"0".."3"` or `"s"`/`"z"` (default `"2"`).
|
|
29
|
+
* - `ts_typeof`: enable the `ts-typeof-elimination` pass.
|
|
30
|
+
* - `enable` / `disable`: pass ids to force on/off (disable wins).
|
|
31
|
+
* - `source_map`: produce an output map.
|
|
32
|
+
* - `input_source_map`: JSON of an input map to compose against.
|
|
33
|
+
*/
|
|
34
|
+
export function compile(source: string, filename: string, level: string, ts_typeof: boolean, enable: string[], disable: string[], source_map: boolean, input_source_map?: string | null): CompileResult;
|
package/wasm/oxidil.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/* @ts-self-types="./oxidil.d.ts" */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Result of a successful compile, surfaced to JS as an object with `code` and
|
|
5
|
+
* (optional) `map` string getters.
|
|
6
|
+
*/
|
|
7
|
+
class CompileResult {
|
|
8
|
+
static __wrap(ptr) {
|
|
9
|
+
const obj = Object.create(CompileResult.prototype);
|
|
10
|
+
obj.__wbg_ptr = ptr;
|
|
11
|
+
CompileResultFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
__destroy_into_raw() {
|
|
15
|
+
const ptr = this.__wbg_ptr;
|
|
16
|
+
this.__wbg_ptr = 0;
|
|
17
|
+
CompileResultFinalization.unregister(this);
|
|
18
|
+
return ptr;
|
|
19
|
+
}
|
|
20
|
+
free() {
|
|
21
|
+
const ptr = this.__destroy_into_raw();
|
|
22
|
+
wasm.__wbg_compileresult_free(ptr, 0);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Optimized JavaScript source.
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
get code() {
|
|
29
|
+
let deferred1_0;
|
|
30
|
+
let deferred1_1;
|
|
31
|
+
try {
|
|
32
|
+
const ret = wasm.compileresult_code(this.__wbg_ptr);
|
|
33
|
+
deferred1_0 = ret[0];
|
|
34
|
+
deferred1_1 = ret[1];
|
|
35
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
36
|
+
} finally {
|
|
37
|
+
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* v3 source map as a JSON string, or `undefined` when maps were disabled.
|
|
42
|
+
* @returns {string | undefined}
|
|
43
|
+
*/
|
|
44
|
+
get map() {
|
|
45
|
+
const ret = wasm.compileresult_map(this.__wbg_ptr);
|
|
46
|
+
let v1;
|
|
47
|
+
if (ret[0] !== 0) {
|
|
48
|
+
v1 = getStringFromWasm0(ret[0], ret[1]).slice();
|
|
49
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
50
|
+
}
|
|
51
|
+
return v1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (Symbol.dispose) CompileResult.prototype[Symbol.dispose] = CompileResult.prototype.free;
|
|
55
|
+
exports.CompileResult = CompileResult;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Compile a JS/TS source string. Mirrors the native CLI semantics.
|
|
59
|
+
*
|
|
60
|
+
* - `source`: the input source text.
|
|
61
|
+
* - `filename`: logical name; `SourceType` is inferred from its extension and
|
|
62
|
+
* it becomes the `sources` entry of the output map.
|
|
63
|
+
* - `level`: optimization level — `"0".."3"` or `"s"`/`"z"` (default `"2"`).
|
|
64
|
+
* - `ts_typeof`: enable the `ts-typeof-elimination` pass.
|
|
65
|
+
* - `enable` / `disable`: pass ids to force on/off (disable wins).
|
|
66
|
+
* - `source_map`: produce an output map.
|
|
67
|
+
* - `input_source_map`: JSON of an input map to compose against.
|
|
68
|
+
* @param {string} source
|
|
69
|
+
* @param {string} filename
|
|
70
|
+
* @param {string} level
|
|
71
|
+
* @param {boolean} ts_typeof
|
|
72
|
+
* @param {string[]} enable
|
|
73
|
+
* @param {string[]} disable
|
|
74
|
+
* @param {boolean} source_map
|
|
75
|
+
* @param {string | null} [input_source_map]
|
|
76
|
+
* @returns {CompileResult}
|
|
77
|
+
*/
|
|
78
|
+
function compile(source, filename, level, ts_typeof, enable, disable, source_map, input_source_map) {
|
|
79
|
+
const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
80
|
+
const len0 = WASM_VECTOR_LEN;
|
|
81
|
+
const ptr1 = passStringToWasm0(filename, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
82
|
+
const len1 = WASM_VECTOR_LEN;
|
|
83
|
+
const ptr2 = passStringToWasm0(level, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
84
|
+
const len2 = WASM_VECTOR_LEN;
|
|
85
|
+
const ptr3 = passArrayJsValueToWasm0(enable, wasm.__wbindgen_malloc);
|
|
86
|
+
const len3 = WASM_VECTOR_LEN;
|
|
87
|
+
const ptr4 = passArrayJsValueToWasm0(disable, wasm.__wbindgen_malloc);
|
|
88
|
+
const len4 = WASM_VECTOR_LEN;
|
|
89
|
+
var ptr5 = isLikeNone(input_source_map) ? 0 : passStringToWasm0(input_source_map, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
90
|
+
var len5 = WASM_VECTOR_LEN;
|
|
91
|
+
const ret = wasm.compile(ptr0, len0, ptr1, len1, ptr2, len2, ts_typeof, ptr3, len3, ptr4, len4, source_map, ptr5, len5);
|
|
92
|
+
if (ret[2]) {
|
|
93
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
94
|
+
}
|
|
95
|
+
return CompileResult.__wrap(ret[0]);
|
|
96
|
+
}
|
|
97
|
+
exports.compile = compile;
|
|
98
|
+
function __wbg_get_imports() {
|
|
99
|
+
const import0 = {
|
|
100
|
+
__proto__: null,
|
|
101
|
+
__wbg_Error_ef53bc310eb298a0: function(arg0, arg1) {
|
|
102
|
+
const ret = Error(getStringFromWasm0(arg0, arg1));
|
|
103
|
+
return ret;
|
|
104
|
+
},
|
|
105
|
+
__wbg___wbindgen_string_get_72bdf95d3ae505b1: function(arg0, arg1) {
|
|
106
|
+
const obj = arg1;
|
|
107
|
+
const ret = typeof(obj) === 'string' ? obj : undefined;
|
|
108
|
+
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
109
|
+
var len1 = WASM_VECTOR_LEN;
|
|
110
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
111
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
112
|
+
},
|
|
113
|
+
__wbg___wbindgen_throw_1506f2235d1bdba0: function(arg0, arg1) {
|
|
114
|
+
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
115
|
+
},
|
|
116
|
+
__wbindgen_init_externref_table: function() {
|
|
117
|
+
const table = wasm.__wbindgen_externrefs;
|
|
118
|
+
const offset = table.grow(4);
|
|
119
|
+
table.set(0, undefined);
|
|
120
|
+
table.set(offset + 0, undefined);
|
|
121
|
+
table.set(offset + 1, null);
|
|
122
|
+
table.set(offset + 2, true);
|
|
123
|
+
table.set(offset + 3, false);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
__proto__: null,
|
|
128
|
+
"./oxidil_bg.js": import0,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const CompileResultFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
133
|
+
? { register: () => {}, unregister: () => {} }
|
|
134
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_compileresult_free(ptr, 1));
|
|
135
|
+
|
|
136
|
+
function addToExternrefTable0(obj) {
|
|
137
|
+
const idx = wasm.__externref_table_alloc();
|
|
138
|
+
wasm.__wbindgen_externrefs.set(idx, obj);
|
|
139
|
+
return idx;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let cachedDataViewMemory0 = null;
|
|
143
|
+
function getDataViewMemory0() {
|
|
144
|
+
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
|
145
|
+
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
|
146
|
+
}
|
|
147
|
+
return cachedDataViewMemory0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getStringFromWasm0(ptr, len) {
|
|
151
|
+
return decodeText(ptr >>> 0, len);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let cachedUint8ArrayMemory0 = null;
|
|
155
|
+
function getUint8ArrayMemory0() {
|
|
156
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
157
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
158
|
+
}
|
|
159
|
+
return cachedUint8ArrayMemory0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isLikeNone(x) {
|
|
163
|
+
return x === undefined || x === null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function passArrayJsValueToWasm0(array, malloc) {
|
|
167
|
+
const ptr = malloc(array.length * 4, 4) >>> 0;
|
|
168
|
+
for (let i = 0; i < array.length; i++) {
|
|
169
|
+
const add = addToExternrefTable0(array[i]);
|
|
170
|
+
getDataViewMemory0().setUint32(ptr + 4 * i, add, true);
|
|
171
|
+
}
|
|
172
|
+
WASM_VECTOR_LEN = array.length;
|
|
173
|
+
return ptr;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
177
|
+
if (realloc === undefined) {
|
|
178
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
179
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
180
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
181
|
+
WASM_VECTOR_LEN = buf.length;
|
|
182
|
+
return ptr;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let len = arg.length;
|
|
186
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
187
|
+
|
|
188
|
+
const mem = getUint8ArrayMemory0();
|
|
189
|
+
|
|
190
|
+
let offset = 0;
|
|
191
|
+
|
|
192
|
+
for (; offset < len; offset++) {
|
|
193
|
+
const code = arg.charCodeAt(offset);
|
|
194
|
+
if (code > 0x7F) break;
|
|
195
|
+
mem[ptr + offset] = code;
|
|
196
|
+
}
|
|
197
|
+
if (offset !== len) {
|
|
198
|
+
if (offset !== 0) {
|
|
199
|
+
arg = arg.slice(offset);
|
|
200
|
+
}
|
|
201
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
202
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
203
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
204
|
+
|
|
205
|
+
offset += ret.written;
|
|
206
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
WASM_VECTOR_LEN = offset;
|
|
210
|
+
return ptr;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function takeFromExternrefTable0(idx) {
|
|
214
|
+
const value = wasm.__wbindgen_externrefs.get(idx);
|
|
215
|
+
wasm.__externref_table_dealloc(idx);
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
220
|
+
cachedTextDecoder.decode();
|
|
221
|
+
function decodeText(ptr, len) {
|
|
222
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const cachedTextEncoder = new TextEncoder();
|
|
226
|
+
|
|
227
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
228
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
229
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
230
|
+
view.set(buf);
|
|
231
|
+
return {
|
|
232
|
+
read: arg.length,
|
|
233
|
+
written: buf.length
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let WASM_VECTOR_LEN = 0;
|
|
239
|
+
|
|
240
|
+
const wasmPath = `${__dirname}/oxidil_bg.wasm`;
|
|
241
|
+
const wasmBytes = require('fs').readFileSync(wasmPath);
|
|
242
|
+
const wasmModule = new WebAssembly.Module(wasmBytes);
|
|
243
|
+
let wasmInstance = new WebAssembly.Instance(wasmModule, __wbg_get_imports());
|
|
244
|
+
let wasm = wasmInstance.exports;
|
|
245
|
+
wasm.__wbindgen_start();
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
export const memory: WebAssembly.Memory;
|
|
4
|
+
export const __wbg_compileresult_free: (a: number, b: number) => void;
|
|
5
|
+
export const compile: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number) => [number, number, number];
|
|
6
|
+
export const compileresult_code: (a: number) => [number, number];
|
|
7
|
+
export const compileresult_map: (a: number) => [number, number];
|
|
8
|
+
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
9
|
+
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
10
|
+
export const __wbindgen_externrefs: WebAssembly.Table;
|
|
11
|
+
export const __externref_table_alloc: () => number;
|
|
12
|
+
export const __externref_table_dealloc: (a: number) => void;
|
|
13
|
+
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
14
|
+
export const __wbindgen_start: () => void;
|