@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 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
- The compiler CLI is prebuilt, but compiling an application requires:
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
- eval?: boolean;
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.2",
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.2"
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
+ }