@playfast/reform-proof-vitest 0.0.2 → 0.0.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@playfast/reform-proof-vitest",
3
3
  "playbook": "./playbook",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "type": "module",
6
6
  "description": "Vitest adapter for reform-proof — a vite plugin that auto-registers every *.proof.ts suite as native vitest tests, run through the proof engine.",
7
7
  "keywords": [
@@ -27,7 +27,7 @@
27
27
  "./*": "./src/*.ts"
28
28
  },
29
29
  "files": [
30
- "dist",
30
+ "src",
31
31
  "README.md"
32
32
  ],
33
33
  "scripts": {
@@ -1,58 +1,62 @@
1
- import { isProofSuite, withProofRunner } from '@playfast/reform-proof';
2
- import { describe, test } from 'vitest';
1
+ import { isProofSuite, type ProofSuite, withProofRunner } from '@playfast/reform-proof'
2
+ import { describe, test } from 'vitest'
3
+
3
4
  // reform-proof vitest adapter — turns a reform proof `ProofSuite` into native
4
5
  // vitest tests (a `describe` per product, a `test` per requirement), each run
5
6
  // through the proof engine via the `ProofRunner` layer. Pair it with
6
7
  // `reformProofPlugin` (./plugin), which appends a `registerSuites(...)` call to
7
8
  // every `*.proof.ts` so suites become tests with no per-feature wrapper file.
9
+
8
10
  // A suite registers exactly once even if several modules import it (its own test
9
11
  // entry plus any flow test that pulls it in). Identity-keyed, so a suite value is
10
12
  // never double-registered within a worker.
11
- const registered = new WeakSet();
13
+ const registered = new WeakSet<ProofSuite>()
14
+
12
15
  // Per-proof-test setup hooks. An app registers one (via a module the plugin's
13
16
  // `setup` option imports into each `*.proof.ts`) to run app-specific preparation
14
17
  // right before each proof renders — e.g. installing a render-free React-hooks
15
18
  // dispatcher for view bodies that reach for hooks. The adapter stays view-agnostic:
16
19
  // it just runs whatever the app registered, only inside proof-test bodies, so a
17
20
  // `*.test.ts` that controls global state is never disturbed.
18
- const setupHooks = new Set();
21
+ const setupHooks = new Set<() => void>()
22
+
19
23
  /**
20
24
  * Register a function to run immediately before every proof test. Idempotent by
21
25
  * reference; intended to be called from a side-effect module the plugin imports
22
26
  * into `*.proof.ts` files only.
23
27
  */
24
- export const setProofTestHook = (hook) => {
25
- setupHooks.add(hook);
26
- };
27
- const runSetupHooks = () => {
28
- for (const hook of setupHooks)
29
- hook();
30
- };
31
- const registerSuite = (suite) => {
32
- describe(suite.product.manifest.name, () => {
33
- for (const proof of suite.proofs) {
34
- const statement = proof.requirement.manifest.statement;
35
- test(statement, async () => {
36
- runSetupHooks();
37
- const result = await withProofRunner((runner) => runner.executeProof(proof));
38
- if (!result.ok) {
39
- throw new Error(`reform-proof: ${statement} — ${result.error ?? 'failed'}`);
40
- }
41
- });
28
+ export const setProofTestHook = (hook: () => void): void => {
29
+ setupHooks.add(hook)
30
+ }
31
+
32
+ const runSetupHooks = (): void => {
33
+ for (const hook of setupHooks) hook()
34
+ }
35
+
36
+ const registerSuite = (suite: ProofSuite): void => {
37
+ describe(suite.product.manifest.name, () => {
38
+ for (const proof of suite.proofs) {
39
+ const statement = proof.requirement.manifest.statement
40
+ test(statement, async () => {
41
+ runSetupHooks()
42
+ const result = await withProofRunner((runner) => runner.executeProof(proof))
43
+ if (!result.ok) {
44
+ throw new Error(`reform-proof: ${statement} — ${result.error ?? 'failed'}`)
42
45
  }
43
- });
44
- };
46
+ })
47
+ }
48
+ })
49
+ }
50
+
45
51
  /**
46
52
  * Register every `ProofSuite` among the given values as vitest tests. Non-suite
47
53
  * values are ignored, so a whole module's exports can be passed verbatim — which
48
54
  * is exactly what `reformProofPlugin` does.
49
55
  */
50
- export const registerSuites = (values) => {
51
- for (const value of values) {
52
- if (!isProofSuite(value) || registered.has(value))
53
- continue;
54
- registered.add(value);
55
- registerSuite(value);
56
- }
57
- };
58
- //# sourceMappingURL=index.js.map
56
+ export const registerSuites = (values: ReadonlyArray<unknown>): void => {
57
+ for (const value of values) {
58
+ if (!isProofSuite(value) || registered.has(value)) continue
59
+ registered.add(value)
60
+ registerSuite(value)
61
+ }
62
+ }
package/src/plugin.ts ADDED
@@ -0,0 +1,68 @@
1
+ import { init, parse } from 'es-module-lexer'
2
+
3
+ // reform-proof vitest plugin — a vite/vitest plugin that auto-registers proof
4
+ // suites. For every `*.proof.ts` file vitest collects, it lexes the module's
5
+ // export names and appends a `registerSuites([...exports])` call, so each exported
6
+ // `ProofSuite` becomes native vitest tests. This is what lets a feature ship a
7
+ // `*.proof.ts` with NO hand-written `*.proof.test.ts` wrapper: add this plugin and
8
+ // the glob `**/*.proof.ts` to the vitest config, and the suites run themselves.
9
+
10
+ const PROOF_RE = /\.proof\.ts$/
11
+
12
+ /** Options for `reformProofPlugin`. */
13
+ export interface ReformProofPluginOptions {
14
+ /**
15
+ * Module specifier(s) the plugin side-effect-imports into every `*.proof.ts`,
16
+ * before registering its suites. Use to register per-proof-test setup
17
+ * (`setProofTestHook`) that must run ONLY in proof-test workers — e.g. an app's
18
+ * headless React-hooks dispatcher. Imported only into `*.proof.ts`, so regular
19
+ * `*.test.ts` files are never affected.
20
+ */
21
+ readonly setup?: string | ReadonlyArray<string>
22
+ }
23
+
24
+ /** The minimal vite plugin shape this adapter needs — structurally a `Plugin`,
25
+ * declared locally so the package need not depend on vite's types. */
26
+ export interface ReformProofPlugin {
27
+ readonly name: string
28
+ readonly enforce: 'post'
29
+ transform(
30
+ code: string,
31
+ id: string,
32
+ ): Promise<{ readonly code: string; readonly map: null } | null>
33
+ }
34
+
35
+ const sourcePath = (id: string): string => id.split('?')[0] ?? id
36
+
37
+ const setupList = (setup: ReformProofPluginOptions['setup']): ReadonlyArray<string> =>
38
+ setup === undefined ? [] : typeof setup === 'string' ? [setup] : setup
39
+
40
+ /**
41
+ * The plugin. Runs `post` (after TS→JS), so it lexes plain JS exports reliably.
42
+ * Only `*.proof.ts` modules are touched; everything else passes through.
43
+ */
44
+ export const reformProofPlugin = (options?: ReformProofPluginOptions): ReformProofPlugin => {
45
+ const setupImports = setupList(options?.setup)
46
+ .map((spec) => `import '${spec}'`)
47
+ .join('\n')
48
+ return {
49
+ name: 'reform-proof-vitest',
50
+ enforce: 'post',
51
+ async transform(code, id) {
52
+ if (!PROOF_RE.test(sourcePath(id))) return null
53
+ await init
54
+ const [, exports] = parse(code)
55
+ // Reference each export's LOCAL binding (in scope after the module body); a
56
+ // re-export-from has no local name (`ln` undefined) and is skipped.
57
+ const names = exports
58
+ .map((exportSpec) => exportSpec.ln)
59
+ .filter((local): local is string => typeof local === 'string' && local.length > 0)
60
+ if (names.length === 0) return null
61
+ const epilogue =
62
+ `\n${setupImports}\n` +
63
+ `import { registerSuites as __registerReformProofSuites } from '@playfast/reform-proof-vitest'\n` +
64
+ `__registerReformProofSuites([${names.join(', ')}])\n`
65
+ return { code: code + epilogue, map: null }
66
+ },
67
+ }
68
+ }
package/dist/index.d.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * Register a function to run immediately before every proof test. Idempotent by
3
- * reference; intended to be called from a side-effect module the plugin imports
4
- * into `*.proof.ts` files only.
5
- */
6
- export declare const setProofTestHook: (hook: () => void) => void;
7
- /**
8
- * Register every `ProofSuite` among the given values as vitest tests. Non-suite
9
- * values are ignored, so a whole module's exports can be passed verbatim — which
10
- * is exactly what `reformProofPlugin` does.
11
- */
12
- export declare const registerSuites: (values: ReadonlyArray<unknown>) => void;
13
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAsBA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,IAAI,KAAG,IAEnD,CAAA;AAqBD;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,aAAa,CAAC,OAAO,CAAC,KAAG,IAM/D,CAAA"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAmB,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACvF,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAEvC,8EAA8E;AAC9E,8EAA8E;AAC9E,qEAAqE;AACrE,gFAAgF;AAChF,8EAA8E;AAE9E,iFAAiF;AACjF,kFAAkF;AAClF,2CAA2C;AAC3C,MAAM,UAAU,GAAG,IAAI,OAAO,EAAc,CAAA;AAE5C,8EAA8E;AAC9E,iFAAiF;AACjF,8EAA8E;AAC9E,oFAAoF;AACpF,gFAAgF;AAChF,6DAA6D;AAC7D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAc,CAAA;AAExC;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAgB,EAAQ,EAAE;IACzD,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACtB,CAAC,CAAA;AAED,MAAM,aAAa,GAAG,GAAS,EAAE;IAC/B,KAAK,MAAM,IAAI,IAAI,UAAU;QAAE,IAAI,EAAE,CAAA;AACvC,CAAC,CAAA;AAED,MAAM,aAAa,GAAG,CAAC,KAAiB,EAAQ,EAAE;IAChD,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE;QACzC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAA;YACtD,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;gBACzB,aAAa,EAAE,CAAA;gBACf,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;gBAC5E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,MAAM,IAAI,KAAK,CAAC,iBAAiB,SAAS,MAAM,MAAM,CAAC,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAA;gBAC7E,CAAC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAA8B,EAAQ,EAAE;IACrE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAQ;QAC3D,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACrB,aAAa,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;AACH,CAAC,CAAA"}
package/dist/plugin.d.ts DELETED
@@ -1,27 +0,0 @@
1
- /** Options for `reformProofPlugin`. */
2
- export interface ReformProofPluginOptions {
3
- /**
4
- * Module specifier(s) the plugin side-effect-imports into every `*.proof.ts`,
5
- * before registering its suites. Use to register per-proof-test setup
6
- * (`setProofTestHook`) that must run ONLY in proof-test workers — e.g. an app's
7
- * headless React-hooks dispatcher. Imported only into `*.proof.ts`, so regular
8
- * `*.test.ts` files are never affected.
9
- */
10
- readonly setup?: string | ReadonlyArray<string>;
11
- }
12
- /** The minimal vite plugin shape this adapter needs — structurally a `Plugin`,
13
- * declared locally so the package need not depend on vite's types. */
14
- export interface ReformProofPlugin {
15
- readonly name: string;
16
- readonly enforce: 'post';
17
- transform(code: string, id: string): Promise<{
18
- readonly code: string;
19
- readonly map: null;
20
- } | null>;
21
- }
22
- /**
23
- * The plugin. Runs `post` (after TS→JS), so it lexes plain JS exports reliably.
24
- * Only `*.proof.ts` modules are touched; everything else passes through.
25
- */
26
- export declare const reformProofPlugin: (options?: ReformProofPluginOptions) => ReformProofPlugin;
27
- //# sourceMappingURL=plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAWA,uCAAuC;AACvC,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;CAChD;AAED;uEACuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;CACjE;AAOD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,wBAAwB,KAAG,iBAwBtE,CAAA"}
package/dist/plugin.js DELETED
@@ -1,41 +0,0 @@
1
- import { init, parse } from 'es-module-lexer';
2
- // reform-proof vitest plugin — a vite/vitest plugin that auto-registers proof
3
- // suites. For every `*.proof.ts` file vitest collects, it lexes the module's
4
- // export names and appends a `registerSuites([...exports])` call, so each exported
5
- // `ProofSuite` becomes native vitest tests. This is what lets a feature ship a
6
- // `*.proof.ts` with NO hand-written `*.proof.test.ts` wrapper: add this plugin and
7
- // the glob `**/*.proof.ts` to the vitest config, and the suites run themselves.
8
- const PROOF_RE = /\.proof\.ts$/;
9
- const sourcePath = (id) => id.split('?')[0] ?? id;
10
- const setupList = (setup) => setup === undefined ? [] : typeof setup === 'string' ? [setup] : setup;
11
- /**
12
- * The plugin. Runs `post` (after TS→JS), so it lexes plain JS exports reliably.
13
- * Only `*.proof.ts` modules are touched; everything else passes through.
14
- */
15
- export const reformProofPlugin = (options) => {
16
- const setupImports = setupList(options?.setup)
17
- .map((spec) => `import '${spec}'`)
18
- .join('\n');
19
- return {
20
- name: 'reform-proof-vitest',
21
- enforce: 'post',
22
- async transform(code, id) {
23
- if (!PROOF_RE.test(sourcePath(id)))
24
- return null;
25
- await init;
26
- const [, exports] = parse(code);
27
- // Reference each export's LOCAL binding (in scope after the module body); a
28
- // re-export-from has no local name (`ln` undefined) and is skipped.
29
- const names = exports
30
- .map((exportSpec) => exportSpec.ln)
31
- .filter((local) => typeof local === 'string' && local.length > 0);
32
- if (names.length === 0)
33
- return null;
34
- const epilogue = `\n${setupImports}\n` +
35
- `import { registerSuites as __registerReformProofSuites } from '@playfast/reform-proof-vitest'\n` +
36
- `__registerReformProofSuites([${names.join(', ')}])\n`;
37
- return { code: code + epilogue, map: null };
38
- },
39
- };
40
- };
41
- //# sourceMappingURL=plugin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAE7C,8EAA8E;AAC9E,6EAA6E;AAC7E,mFAAmF;AACnF,+EAA+E;AAC/E,mFAAmF;AACnF,gFAAgF;AAEhF,MAAM,QAAQ,GAAG,cAAc,CAAA;AAyB/B,MAAM,UAAU,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAEjE,MAAM,SAAS,GAAG,CAAC,KAAwC,EAAyB,EAAE,CACpF,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AAExE;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAkC,EAAqB,EAAE;IACzF,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC;SAC3C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,IAAI,GAAG,CAAC;SACjC,IAAI,CAAC,IAAI,CAAC,CAAA;IACb,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,MAAM;QACf,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE;YACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC/C,MAAM,IAAI,CAAA;YACV,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAA;YAC/B,4EAA4E;YAC5E,oEAAoE;YACpE,MAAM,KAAK,GAAG,OAAO;iBAClB,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;iBAClC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YACpF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAA;YACnC,MAAM,QAAQ,GACZ,KAAK,YAAY,IAAI;gBACrB,iGAAiG;gBACjG,gCAAgC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAA;YACxD,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QAC7C,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}