@maligator/cli 0.1.0-alpha.2 → 0.1.0-alpha.20
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 +118 -1
- package/index.d.ts +19 -1
- package/package.json +3 -2
- package/test-api.d.ts +167 -0
package/README.md
CHANGED
|
@@ -50,11 +50,126 @@ maligator init
|
|
|
50
50
|
maligator doctor [--verbose] [--target rust-triple]
|
|
51
51
|
maligator build [entry] [--production] [--artifact directory] [--target rust-triple] [--config path]
|
|
52
52
|
maligator run [entry] [--config path] [-- args...]
|
|
53
|
+
maligator dev [entry] [--config path] [-- args...]
|
|
54
|
+
maligator test [path ...] [--run name] [--shuffle [seed]] [--repeat count] [--bail]
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
Run commands from the project root. Maligator does not search parent directories
|
|
56
58
|
for configuration.
|
|
57
59
|
|
|
60
|
+
`run` and `dev` use the runtime embedded in the platform package for the supported
|
|
61
|
+
single-process, asset-free, Intl-disabled development profile, so they do not need
|
|
62
|
+
a local native toolchain. `dev` retains compiler identities between edits and
|
|
63
|
+
restarts a fresh application VM after every successful rebuild. Build failures keep
|
|
64
|
+
the watcher alive for recovery on the next edit.
|
|
65
|
+
|
|
66
|
+
## Testing
|
|
67
|
+
|
|
68
|
+
`maligator test` discovers conventional JavaScript and erasable-TypeScript test
|
|
69
|
+
files and runs them through Maligator's embedded interpreter. Test runs do not
|
|
70
|
+
generate C or invoke the native toolchain. Import the Maligator-owned API from
|
|
71
|
+
`maligator:test`:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { beforeEach, describe, expect, test } from "maligator:test";
|
|
75
|
+
import { createRouter, RouteError, type Router } from "./router.ts";
|
|
76
|
+
|
|
77
|
+
describe("router", () => {
|
|
78
|
+
let router: Router;
|
|
79
|
+
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
router = createRouter();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("matches parameters", async () => {
|
|
85
|
+
await expect(router.match("/users/42")).resolves.toMatchObject({
|
|
86
|
+
id: expect.any(Number),
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("rejects malformed parameters", async () => {
|
|
91
|
+
await expect(router.match("/users/nope")).rejects.toThrow(RouteError);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
By default, discovery includes `*.test.js`, `*.test.mjs`, `*.test.ts`,
|
|
97
|
+
`*.test.mts`, and the corresponding `*.spec.*` names. Pass files, directories,
|
|
98
|
+
or both:
|
|
99
|
+
|
|
100
|
+
```shell
|
|
101
|
+
maligator test
|
|
102
|
+
maligator test src/router
|
|
103
|
+
maligator test src/router/router.test.ts
|
|
104
|
+
maligator test --run "router > parameters"
|
|
105
|
+
maligator test --shuffle
|
|
106
|
+
maligator test --shuffle 18492
|
|
107
|
+
maligator test --repeat 10
|
|
108
|
+
maligator test --bail
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`--run` filters hierarchical suite and test names. `--shuffle [seed]` changes
|
|
112
|
+
execution order reproducibly and always prints the seed. `--repeat` executes the
|
|
113
|
+
selected tests again while reusing unchanged compilation artifacts. The default
|
|
114
|
+
policy completes the selected suite; `--bail` stops scheduling after the first
|
|
115
|
+
failure.
|
|
116
|
+
|
|
117
|
+
The authoring API includes nested `describe` suites, `beforeAll`, `afterAll`,
|
|
118
|
+
`beforeEach`, `afterEach`, synchronous and promise-returning callbacks,
|
|
119
|
+
`test.skip`, `test.todo`, `test.only`, and `test.each`. A committed `.only`
|
|
120
|
+
focuses execution and prints a warning. Matchers include identity, structural
|
|
121
|
+
equality, truthiness, containment, length, string/regular-expression matching,
|
|
122
|
+
partial-object matching, throwing, `.not`, `.resolves`, `.rejects`, and the
|
|
123
|
+
`expect.any`, `expect.anything`, `expect.stringMatching`,
|
|
124
|
+
`expect.objectContaining`, and `expect.arrayContaining` asymmetric helpers.
|
|
125
|
+
Maligator owns this API and does not claim Jest, Vitest, or `node:test`
|
|
126
|
+
compatibility.
|
|
127
|
+
|
|
128
|
+
Test files currently execute serially in one Realm. Registration and lifecycle
|
|
129
|
+
state are separated by test file, but globals, prototypes, imported module
|
|
130
|
+
singletons, host state, and asynchronous work left running by a test can be
|
|
131
|
+
shared. Do not rely on process-per-file isolation.
|
|
132
|
+
|
|
133
|
+
Frontend bytecode is content-addressed by source, transitive dependencies,
|
|
134
|
+
resolved module identities, relevant build configuration, and compiler/runtime
|
|
135
|
+
format versions. A warm run restores a shared base plus independently cached
|
|
136
|
+
test-file registration fragments. Test results are never cached: every selected
|
|
137
|
+
test executes on every invocation.
|
|
138
|
+
|
|
139
|
+
The package ships documented declarations for `maligator:test`. Projects whose
|
|
140
|
+
TypeScript configuration does not include `maligator.build.ts` should load the
|
|
141
|
+
package declarations explicitly:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"compilerOptions": {
|
|
146
|
+
"types": ["@maligator/cli"]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Build cache
|
|
152
|
+
|
|
153
|
+
Normal `maligator build`, `maligator run`, and `maligator test` commands share a
|
|
154
|
+
content-addressed portable VM definition store. On a valid hit, Maligator skips
|
|
155
|
+
parsing, module-graph construction, semantic analysis, optimization, register
|
|
156
|
+
allocation, and VM lowering. For the single-process, asset-free, Intl-disabled
|
|
157
|
+
development profile, `run` executes the restored definition with the isolated
|
|
158
|
+
runtime embedded in the platform CLI; it does not generate C or invoke a native
|
|
159
|
+
toolchain. Normal and production `build` commands emit
|
|
160
|
+
native translation units from the same restored definition. Those units and the native driver are
|
|
161
|
+
compiled into independently content-addressed objects, so unchanged objects can
|
|
162
|
+
be relinked without repeating C compilation. The final link still runs, and
|
|
163
|
+
native runtime and Rust archives remain separately content-addressed.
|
|
164
|
+
|
|
165
|
+
Build cache invalidation includes source and transitive dependency content,
|
|
166
|
+
package-resolution metadata, relevant configuration, the TypeScript erasure
|
|
167
|
+
frontend, and compiler/wire versions. Generated-object identity additionally
|
|
168
|
+
includes the exact C source, source identity, native headers/runtime artifact,
|
|
169
|
+
compiler/toolchain, target, flags, and build environment. Compiler diagnostic
|
|
170
|
+
dump flags deliberately recompile the frontend because they require live
|
|
171
|
+
semantic and IR objects.
|
|
172
|
+
|
|
58
173
|
## Production builds
|
|
59
174
|
|
|
60
175
|
Use production mode for a deployable binary:
|
|
@@ -142,7 +257,9 @@ explicitly:
|
|
|
142
257
|
|
|
143
258
|
## Build toolchain
|
|
144
259
|
|
|
145
|
-
|
|
260
|
+
`maligator test` and supported `maligator run` profiles use the development runtime
|
|
261
|
+
embedded in the platform CLI and do not require a native toolchain. Native `build`
|
|
262
|
+
commands—and development runs using bundled assets, Intl, or multiprocessing—require:
|
|
146
263
|
|
|
147
264
|
- A C23 compiler and archive tool: Apple clang, clang 19 or newer, or GCC 15 or
|
|
148
265
|
newer
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
|
|
3
|
+
import "./test-api.d.ts";
|
|
4
|
+
|
|
3
5
|
export type MaligatorIntlFeature =
|
|
4
6
|
| "collator"
|
|
5
7
|
| "number-format"
|
|
@@ -19,10 +21,26 @@ export interface MaligatorBuildConfig {
|
|
|
19
21
|
entry?: string;
|
|
20
22
|
outputName?: string;
|
|
21
23
|
assets?: Record<string, AssetInclusion>;
|
|
24
|
+
modules?: {
|
|
25
|
+
/** Exact specifier replacements applied before module resolution. */
|
|
26
|
+
aliases?: Record<string, string>;
|
|
27
|
+
};
|
|
22
28
|
engine?: {
|
|
23
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Lock ECMAScript primordials for this entire build. Locked is the default;
|
|
31
|
+
* mutable is intended for conformance suites and compatibility experiments.
|
|
32
|
+
*/
|
|
33
|
+
primordials?: "locked" | "mutable";
|
|
34
|
+
/**
|
|
35
|
+
* `true` embeds the runtime compiler. `false` keeps eval/Function present
|
|
36
|
+
* but makes dynamic compilation throw at runtime. `"compile-check"` also
|
|
37
|
+
* rejects statically visible eval/Function calls during the build.
|
|
38
|
+
*/
|
|
39
|
+
eval?: boolean | "compile-check";
|
|
24
40
|
realms?: boolean;
|
|
25
41
|
regexp?: boolean;
|
|
42
|
+
/** Include the Temporal global and its calendar/time-zone data. */
|
|
43
|
+
temporal?: boolean;
|
|
26
44
|
intl?: {
|
|
27
45
|
enabled?: boolean;
|
|
28
46
|
features?: Array<MaligatorIntlFeature>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maligator/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.20",
|
|
4
4
|
"description": "Ahead-of-time JavaScript compiler and native runtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/dirkdev98/maligator#readme",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"bin/maligator.js",
|
|
31
31
|
"index.js",
|
|
32
32
|
"index.d.ts",
|
|
33
|
+
"test-api.d.ts",
|
|
33
34
|
"README.md",
|
|
34
35
|
"LICENSE"
|
|
35
36
|
],
|
|
@@ -44,6 +45,6 @@
|
|
|
44
45
|
}
|
|
45
46
|
},
|
|
46
47
|
"optionalDependencies": {
|
|
47
|
-
"@maligator/cli-darwin-arm64": "0.1.0-alpha.
|
|
48
|
+
"@maligator/cli-darwin-arm64": "0.1.0-alpha.20"
|
|
48
49
|
}
|
|
49
50
|
}
|
package/test-api.d.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
declare module "maligator:test" {
|
|
2
|
+
/**
|
|
3
|
+
* A test or hook body. Maligator waits for a returned promise or thenable
|
|
4
|
+
* before advancing the lifecycle.
|
|
5
|
+
*/
|
|
6
|
+
export type TestCallback = () => unknown;
|
|
7
|
+
/** A lifecycle hook body, with the same async completion contract as a test. */
|
|
8
|
+
export type HookCallback = TestCallback;
|
|
9
|
+
/** A constructable value accepted by {@link expect.any}. */
|
|
10
|
+
export type Constructor = abstract new (...args: Array<never>) => unknown;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Opaque partial-match value produced by helpers such as
|
|
14
|
+
* {@link expect.objectContaining}. It may be nested inside `toEqual`,
|
|
15
|
+
* `toStrictEqual`, and `toMatchObject` expectations.
|
|
16
|
+
*/
|
|
17
|
+
export interface AsymmetricMatcher {
|
|
18
|
+
readonly __maligator_asymmetric__: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Matchers for a synchronously received value. */
|
|
22
|
+
export interface Matchers {
|
|
23
|
+
/** Negate the following matcher. */
|
|
24
|
+
readonly not: Matchers;
|
|
25
|
+
/** Wait for the received promise to fulfill, then match its value. */
|
|
26
|
+
readonly resolves: AsyncMatchers;
|
|
27
|
+
/** Wait for the received promise to reject, then match its reason. */
|
|
28
|
+
readonly rejects: AsyncMatchers;
|
|
29
|
+
/** Require ECMAScript `Object.is` identity. */
|
|
30
|
+
toBe(expected: unknown): void;
|
|
31
|
+
/** Recursively compare enumerable object properties and array elements. */
|
|
32
|
+
toEqual(expected: unknown): void;
|
|
33
|
+
/**
|
|
34
|
+
* Recursively compare values while also requiring matching prototypes and
|
|
35
|
+
* matching sparse-array holes.
|
|
36
|
+
*/
|
|
37
|
+
toStrictEqual(expected: unknown): void;
|
|
38
|
+
/** Require a value other than `undefined`. */
|
|
39
|
+
toBeDefined(): void;
|
|
40
|
+
/** Require `undefined`. */
|
|
41
|
+
toBeUndefined(): void;
|
|
42
|
+
/** Require `null`. */
|
|
43
|
+
toBeNull(): void;
|
|
44
|
+
/** Require a truthy value. */
|
|
45
|
+
toBeTruthy(): void;
|
|
46
|
+
/** Require a falsy value. */
|
|
47
|
+
toBeFalsy(): void;
|
|
48
|
+
/** Require a string substring or an array element matched by identity. */
|
|
49
|
+
toContain(expected: unknown): void;
|
|
50
|
+
/** Require a numeric `.length` equal to `expected`. */
|
|
51
|
+
toHaveLength(expected: number): void;
|
|
52
|
+
/** Match a string against a substring or regular expression. */
|
|
53
|
+
toMatch(expected: string | RegExp): void;
|
|
54
|
+
/** Recursively require the enumerable properties present in `expected`. */
|
|
55
|
+
toMatchObject(expected: object): void;
|
|
56
|
+
/**
|
|
57
|
+
* Invoke the received function and require it to throw. The optional
|
|
58
|
+
* expectation may be a message substring, regular expression, error
|
|
59
|
+
* constructor, or error instance.
|
|
60
|
+
*/
|
|
61
|
+
toThrow(
|
|
62
|
+
expected?:
|
|
63
|
+
| string
|
|
64
|
+
| RegExp
|
|
65
|
+
| Error
|
|
66
|
+
| (abstract new (...args: Array<never>) => Error),
|
|
67
|
+
): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Promise-returning matcher surface exposed by {@link Matchers.resolves} and
|
|
72
|
+
* {@link Matchers.rejects}. Await these calls so the test cannot finish before
|
|
73
|
+
* the assertion.
|
|
74
|
+
*/
|
|
75
|
+
export interface AsyncMatchers {
|
|
76
|
+
/** Negate the following asynchronous matcher. */
|
|
77
|
+
readonly not: AsyncMatchers;
|
|
78
|
+
toBe(expected: unknown): Promise<void>;
|
|
79
|
+
toEqual(expected: unknown): Promise<void>;
|
|
80
|
+
toStrictEqual(expected: unknown): Promise<void>;
|
|
81
|
+
toBeDefined(): Promise<void>;
|
|
82
|
+
toBeUndefined(): Promise<void>;
|
|
83
|
+
toBeNull(): Promise<void>;
|
|
84
|
+
toBeTruthy(): Promise<void>;
|
|
85
|
+
toBeFalsy(): Promise<void>;
|
|
86
|
+
toContain(expected: unknown): Promise<void>;
|
|
87
|
+
toHaveLength(expected: number): Promise<void>;
|
|
88
|
+
toMatch(expected: string | RegExp): Promise<void>;
|
|
89
|
+
toMatchObject(expected: object): Promise<void>;
|
|
90
|
+
toThrow(
|
|
91
|
+
expected?:
|
|
92
|
+
| string
|
|
93
|
+
| RegExp
|
|
94
|
+
| Error
|
|
95
|
+
| (abstract new (...args: Array<never>) => Error),
|
|
96
|
+
): Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Assertion entrypoint and Maligator-owned asymmetric matcher factories. */
|
|
100
|
+
export interface ExpectFunction {
|
|
101
|
+
/** Create matchers for `received`. The assertion position is captured here. */
|
|
102
|
+
(received: unknown): Matchers;
|
|
103
|
+
/** Match a primitive of the corresponding built-in kind or an instance. */
|
|
104
|
+
any(constructorValue: Constructor): AsymmetricMatcher;
|
|
105
|
+
/** Match any value except `null` and `undefined`. */
|
|
106
|
+
anything(): AsymmetricMatcher;
|
|
107
|
+
/** Match a string containing `pattern` or satisfying the regular expression. */
|
|
108
|
+
stringMatching(pattern: string | RegExp): AsymmetricMatcher;
|
|
109
|
+
/** Match an object containing all recursively matched properties in `value`. */
|
|
110
|
+
objectContaining(value: object): AsymmetricMatcher;
|
|
111
|
+
/** Match an array containing a match for every element in `value`. */
|
|
112
|
+
arrayContaining(value: Array<unknown>): AsymmetricMatcher;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Register tests in the current suite during module evaluation. */
|
|
116
|
+
export interface TestFunction {
|
|
117
|
+
/** Register a test. Returned promises are awaited by the runner. */
|
|
118
|
+
(name: string, callback: TestCallback): void;
|
|
119
|
+
/** Register a skipped test without invoking its callback. */
|
|
120
|
+
skip(name: string, callback: TestCallback): void;
|
|
121
|
+
/** Register a named placeholder with no callback. */
|
|
122
|
+
todo(name: string): void;
|
|
123
|
+
/**
|
|
124
|
+
* Register a focused test. When any `.only` exists, non-focused tests are
|
|
125
|
+
* skipped and the runner emits a warning.
|
|
126
|
+
*/
|
|
127
|
+
only(name: string, callback: TestCallback): void;
|
|
128
|
+
/**
|
|
129
|
+
* Register one test for each row. Use `%#` in `name` for the zero-based row
|
|
130
|
+
* index. Array rows are spread into callback parameters.
|
|
131
|
+
*/
|
|
132
|
+
each<const Row extends ReadonlyArray<unknown>>(
|
|
133
|
+
rows: ReadonlyArray<Row>,
|
|
134
|
+
): (name: string, callback: (...values: [...Row]) => unknown) => void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Register nested suites synchronously during module evaluation. */
|
|
138
|
+
export interface DescribeFunction {
|
|
139
|
+
/** Register a suite. Suite callbacks must not return a promise. */
|
|
140
|
+
(name: string, callback: () => void): void;
|
|
141
|
+
/** Register a suite whose descendants are skipped. */
|
|
142
|
+
skip(name: string, callback: () => void): void;
|
|
143
|
+
/** Register a focused suite and emit the runner's focused-test warning. */
|
|
144
|
+
only(name: string, callback: () => void): void;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Register a test in the current suite. */
|
|
148
|
+
export const test: TestFunction;
|
|
149
|
+
/** Register a nested suite in the current suite. */
|
|
150
|
+
export const describe: DescribeFunction;
|
|
151
|
+
/** Create fluent matchers for a received value. */
|
|
152
|
+
export const expect: ExpectFunction;
|
|
153
|
+
/** Run once before tests in the current suite. */
|
|
154
|
+
export function beforeAll(callback: HookCallback): void;
|
|
155
|
+
/** Run once after tests in the current suite, including after test failures. */
|
|
156
|
+
export function afterAll(callback: HookCallback): void;
|
|
157
|
+
/**
|
|
158
|
+
* Run before every selected descendant test. Ancestor hooks run before hooks
|
|
159
|
+
* declared by a nested suite.
|
|
160
|
+
*/
|
|
161
|
+
export function beforeEach(callback: HookCallback): void;
|
|
162
|
+
/**
|
|
163
|
+
* Run after every selected descendant test. Nested-suite hooks run before
|
|
164
|
+
* ancestor hooks.
|
|
165
|
+
*/
|
|
166
|
+
export function afterEach(callback: HookCallback): void;
|
|
167
|
+
}
|