@pixui-dev/inflight-test-webpack-plugin 0.0.7

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/dist/index.cjs ADDED
@@ -0,0 +1,202 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ //#region \0rolldown/runtime.js
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14
+ key = keys[i];
15
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: ((k) => from[k]).bind(null, key),
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+ //#endregion
27
+ let node_fs = require("node:fs");
28
+ let node_path = require("node:path");
29
+ let picomatch = require("picomatch");
30
+ picomatch = __toESM(picomatch, 1);
31
+ //#region src/index.ts
32
+ /**
33
+ * @pixui-dev/inflight-test-webpack-plugin
34
+ *
35
+ * Wires inflight-test into a PixUI activity's webpack build:
36
+ * - discovers test files by glob (default `**\/*.test.{ts,tsx,js,jsx}`),
37
+ * - generates a runner module that binds the discovered cases to the
38
+ * inflight-test runtime, exporting `start` / `startWithoutReport`,
39
+ * - and aliases `@pixui-dev/inflight-test/runner` to that generated module,
40
+ * so the activity just does
41
+ * `import { start } from '@pixui-dev/inflight-test/runner'`
42
+ * and calls it (from a button / QA panel) — no globals, no separate
43
+ * entry/chunk to load.
44
+ *
45
+ * The cases are inlined (`webpackMode: "eager"`) into whatever chunk imports the
46
+ * runner, because the PixUI QuickJS runtime has no async chunk loader.
47
+ *
48
+ * Cross-version: it only mutates `resolve.alias` (+ writes a file), which is
49
+ * identical on webpack 4 and 5 — no version-specific entry/asset APIs.
50
+ */
51
+ const ALWAYS_EXCLUDE = [
52
+ "**/node_modules/**",
53
+ "**/dist/**",
54
+ "**/build/**",
55
+ "**/.git/**"
56
+ ];
57
+ function toArray(v, fallback) {
58
+ if (v == null) return fallback;
59
+ return Array.isArray(v) ? v : [v];
60
+ }
61
+ var InflightTestPlugin = class {
62
+ _opts;
63
+ constructor(options = {}) {
64
+ this._opts = options;
65
+ }
66
+ apply(compiler) {
67
+ if (this._opts.enabled === false) return;
68
+ const cwd = (0, node_path.resolve)(this._opts.cwd ?? compiler.context ?? process.cwd());
69
+ const runtimeImport = this._opts.runtimeImport ?? "@pixui-dev/inflight-test";
70
+ const opts = {
71
+ include: toArray(this._opts.include, ["**/*.test.{ts,tsx,js,jsx}"]),
72
+ exclude: toArray(this._opts.exclude, []),
73
+ cwd,
74
+ runtimeImport,
75
+ runnerImport: this._opts.runnerImport ?? `${runtimeImport}/runner`,
76
+ bridge: this._opts.bridge ?? "gamelet",
77
+ gameletImport: this._opts.gameletImport ?? "gamelet-pixui-frame"
78
+ };
79
+ const testFiles = this._discover(opts);
80
+ const runnerFile = this._writeRunnerModule(opts, testFiles);
81
+ this._aliasRunner(compiler, opts.runnerImport, runnerFile);
82
+ }
83
+ /**
84
+ * Point the runner specifier at the generated module via `resolve.alias`.
85
+ * Works identically on webpack 4 and 5 (object or array alias forms).
86
+ */
87
+ _aliasRunner(compiler, specifier, file) {
88
+ const resolveOpts = compiler.options.resolve ??= {};
89
+ const alias = resolveOpts.alias ??= {};
90
+ if (Array.isArray(alias)) alias.push({
91
+ name: specifier,
92
+ alias: file,
93
+ onlyModule: true
94
+ });
95
+ else alias[`${specifier}$`] = file;
96
+ }
97
+ _discover(opts) {
98
+ const isMatch = (0, picomatch.default)(opts.include, { dot: false });
99
+ const isExcluded = (0, picomatch.default)([...ALWAYS_EXCLUDE, ...opts.exclude], { dot: true });
100
+ const out = [];
101
+ const walk = (dir) => {
102
+ let entries;
103
+ try {
104
+ entries = (0, node_fs.readdirSync)(dir);
105
+ } catch {
106
+ return;
107
+ }
108
+ for (const name of entries) {
109
+ const abs = (0, node_path.join)(dir, name);
110
+ const rel = (0, node_path.relative)(opts.cwd, abs).replace(/\\/g, "/");
111
+ if (isExcluded(rel)) continue;
112
+ let st;
113
+ try {
114
+ st = (0, node_fs.statSync)(abs);
115
+ } catch {
116
+ continue;
117
+ }
118
+ if (st.isDirectory()) walk(abs);
119
+ else if (isMatch(rel)) out.push({
120
+ id: rel,
121
+ absolute: abs.replace(/\\/g, "/")
122
+ });
123
+ }
124
+ };
125
+ walk(opts.cwd);
126
+ out.sort((a, b) => a.id.localeCompare(b.id));
127
+ return out;
128
+ }
129
+ _writeRunnerModule(opts, testFiles) {
130
+ const cacheDir = (0, node_path.join)(opts.cwd, "node_modules", ".cache", "inflight-test");
131
+ if (!(0, node_fs.existsSync)(cacheDir)) (0, node_fs.mkdirSync)(cacheDir, { recursive: true });
132
+ const runnerFile = (0, node_path.join)(cacheDir, "runner.mjs");
133
+ const specs = testFiles.map((f) => {
134
+ return ` { id: ${JSON.stringify(f.id)}, load: () => import(/* webpackMode: "eager" */ ${JSON.stringify(f.absolute)}) },`;
135
+ }).join("\n");
136
+ const useGamelet = opts.bridge === "gamelet";
137
+ const gameletImportLine = useGamelet ? `import __inflightGamelet from ${JSON.stringify(opts.gameletImport)}\n` : "";
138
+ const bridgeBlock = useGamelet ? `
139
+ // Default activity bridge over gamelet-pixui-frame (host screenshot protocol etc.).
140
+ var GameletAPI = __inflightGamelet.GameletAPI
141
+ function makeActivityBridge() {
142
+ return {
143
+ callGame: function (protocol, payload) {
144
+ // gamelet-pixui-frame fills appId/appName automatically; we only send the
145
+ // type + content envelope and the call-specific payload.
146
+ GameletAPI.callGame(JSON.stringify(Object.assign({ type: protocol, content: '' }, payload)))
147
+ },
148
+ onSDKMessage: function (protocol, handler) {
149
+ var myAppId = GameletAPI.getAppID()
150
+ var listener = function (cmd) {
151
+ var msg
152
+ try { msg = JSON.parse(cmd) } catch (e) { return }
153
+ if (!msg || msg.type !== protocol) return
154
+ // Ignore messages addressed to another app: addOnGameCommandListener
155
+ // also subscribes the GLOBAL game-message channel, which isn't
156
+ // app-scoped, so a different app's callback can reach us. Compare as
157
+ // strings — the backend sends appId as a string ("7442") while
158
+ // getAppID() returns a number (7442).
159
+ if (msg.appId != null && String(msg.appId) !== String(myAppId)) return
160
+ handler(msg)
161
+ }
162
+ GameletAPI.addOnGameCommandListener(listener)
163
+ return function () { GameletAPI.removeOnGameCommandListener(listener) }
164
+ },
165
+ }
166
+ }
167
+ ` : "";
168
+ const applyBridge = useGamelet ? ` if (!o.activityBridge) o.activityBridge = makeActivityBridge()\n` : "";
169
+ (0, node_fs.writeFileSync)(runnerFile, `// AUTO-GENERATED by @pixui-dev/inflight-test-webpack-plugin — do not edit.
170
+ import { start as _start } from ${JSON.stringify(opts.runtimeImport)}
171
+ ${gameletImportLine}
172
+ const specs = [
173
+ ${specs}
174
+ ]
175
+
176
+ /** Ids of the discovered test files. */
177
+ export const testFiles = specs.map(function (s) { return s.id })
178
+ ${bridgeBlock}
179
+ /**
180
+ * Run the discovered inflight tests. Writes a Vitest blob report by default
181
+ * (auto-wired from the PixUI host \`os\` / \`external.writeablePath\`); pass
182
+ * \`{ report: false }\` to skip. Call from a button / QA panel — no options
183
+ * needed in the normal case.
184
+ */
185
+ export function start(options) {
186
+ var o = Object.assign({}, options)
187
+ ${applyBridge} return _start(specs, o)
188
+ }
189
+
190
+ /** Run the discovered inflight tests without writing a report. */
191
+ export function startWithoutReport(options) {
192
+ return start(Object.assign({}, options, { report: false }))
193
+ }
194
+ `, "utf8");
195
+ return runnerFile;
196
+ }
197
+ };
198
+ //#endregion
199
+ exports.InflightTestPlugin = InflightTestPlugin;
200
+ exports.default = InflightTestPlugin;
201
+
202
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { Compiler } from "webpack";
2
+
3
+ //#region src/index.d.ts
4
+ interface InflightTestPluginOptions {
5
+ /** Glob(s) for discovering test files. Default: `**\/*.test.{ts,tsx,js,jsx}`. */
6
+ include?: string | string[];
7
+ /** Glob(s) to exclude. `node_modules` / build dirs are always excluded. */
8
+ exclude?: string | string[];
9
+ /** Root directory to scan. Default: the webpack `context`. */
10
+ cwd?: string;
11
+ /**
12
+ * Enable the plugin. Default `true` — inflight-test is meant to ship with the
13
+ * activity (including production), that's the point. Set `false` to opt out.
14
+ */
15
+ enabled?: boolean;
16
+ /** The inflight-test runtime package the generated runner imports. */
17
+ runtimeImport?: string;
18
+ /**
19
+ * The subpath specifier the activity imports the runner from. Default
20
+ * `${runtimeImport}/runner` (i.e. `@pixui-dev/inflight-test/runner`).
21
+ */
22
+ runnerImport?: string;
23
+ /**
24
+ * Default activity bridge wired into `start()` so the activity needs no
25
+ * options for screenshots:
26
+ * - `'gamelet'` (default): wire a bridge over `gamelet-pixui-frame`
27
+ * (`GameletAPI.callGame` + `addOnGameCommandListener`). All normal PixUI
28
+ * activities use this SDK, so the bridge lives here — no per-repo file.
29
+ * - `'none'`: wire nothing (caller passes their own `activityBridge`); use
30
+ * for non-gamelet hosts.
31
+ */
32
+ bridge?: 'gamelet' | 'none';
33
+ /** The SDK package the `'gamelet'` bridge imports. Default `gamelet-pixui-frame`. */
34
+ gameletImport?: string;
35
+ }
36
+ declare class InflightTestPlugin {
37
+ private readonly _opts;
38
+ constructor(options?: InflightTestPluginOptions);
39
+ apply(compiler: Compiler): void;
40
+ /**
41
+ * Point the runner specifier at the generated module via `resolve.alias`.
42
+ * Works identically on webpack 4 and 5 (object or array alias forms).
43
+ */
44
+ private _aliasRunner;
45
+ private _discover;
46
+ private _writeRunnerModule;
47
+ }
48
+ //#endregion
49
+ export { InflightTestPlugin, InflightTestPlugin as default, InflightTestPluginOptions };
50
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;UAyBiB,yBAAA;EAmDE;EAjDjB,OAAA;EAmDqB;EAjDrB,OAAA;EAqDA;EAnDA,GAAA;EAmDM;;;;EA9CN,OAAA;EA2H0B;EAzH1B,aAAA;;;;;EAKA,YAAA;;;;;;;;;;EAUA,MAAA;;EAEA,aAAA;AAAA;AAAA,cAoBW,kBAAA;EAAA,iBACM,KAAA;cAEL,OAAA,GAAS,yBAAA;EAIrB,KAAA,CAAM,QAAA,EAAU,QAAA;;;;;UAyBR,YAAA;EAAA,QAaA,SAAA;EAAA,QAuCA,kBAAA;AAAA"}
@@ -0,0 +1,50 @@
1
+ import { Compiler } from "webpack";
2
+
3
+ //#region src/index.d.ts
4
+ interface InflightTestPluginOptions {
5
+ /** Glob(s) for discovering test files. Default: `**\/*.test.{ts,tsx,js,jsx}`. */
6
+ include?: string | string[];
7
+ /** Glob(s) to exclude. `node_modules` / build dirs are always excluded. */
8
+ exclude?: string | string[];
9
+ /** Root directory to scan. Default: the webpack `context`. */
10
+ cwd?: string;
11
+ /**
12
+ * Enable the plugin. Default `true` — inflight-test is meant to ship with the
13
+ * activity (including production), that's the point. Set `false` to opt out.
14
+ */
15
+ enabled?: boolean;
16
+ /** The inflight-test runtime package the generated runner imports. */
17
+ runtimeImport?: string;
18
+ /**
19
+ * The subpath specifier the activity imports the runner from. Default
20
+ * `${runtimeImport}/runner` (i.e. `@pixui-dev/inflight-test/runner`).
21
+ */
22
+ runnerImport?: string;
23
+ /**
24
+ * Default activity bridge wired into `start()` so the activity needs no
25
+ * options for screenshots:
26
+ * - `'gamelet'` (default): wire a bridge over `gamelet-pixui-frame`
27
+ * (`GameletAPI.callGame` + `addOnGameCommandListener`). All normal PixUI
28
+ * activities use this SDK, so the bridge lives here — no per-repo file.
29
+ * - `'none'`: wire nothing (caller passes their own `activityBridge`); use
30
+ * for non-gamelet hosts.
31
+ */
32
+ bridge?: 'gamelet' | 'none';
33
+ /** The SDK package the `'gamelet'` bridge imports. Default `gamelet-pixui-frame`. */
34
+ gameletImport?: string;
35
+ }
36
+ declare class InflightTestPlugin {
37
+ private readonly _opts;
38
+ constructor(options?: InflightTestPluginOptions);
39
+ apply(compiler: Compiler): void;
40
+ /**
41
+ * Point the runner specifier at the generated module via `resolve.alias`.
42
+ * Works identically on webpack 4 and 5 (object or array alias forms).
43
+ */
44
+ private _aliasRunner;
45
+ private _discover;
46
+ private _writeRunnerModule;
47
+ }
48
+ //#endregion
49
+ export { InflightTestPlugin, InflightTestPlugin as default, InflightTestPluginOptions };
50
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;UAyBiB,yBAAA;EAmDE;EAjDjB,OAAA;EAmDqB;EAjDrB,OAAA;EAqDA;EAnDA,GAAA;EAmDM;;;;EA9CN,OAAA;EA2H0B;EAzH1B,aAAA;;;;;EAKA,YAAA;;;;;;;;;;EAUA,MAAA;;EAEA,aAAA;AAAA;AAAA,cAoBW,kBAAA;EAAA,iBACM,KAAA;cAEL,OAAA,GAAS,yBAAA;EAIrB,KAAA,CAAM,QAAA,EAAU,QAAA;;;;;UAyBR,YAAA;EAAA,QAaA,SAAA;EAAA,QAuCA,kBAAA;AAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,174 @@
1
+ import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
+ import { join, relative, resolve } from "node:path";
3
+ import picomatch from "picomatch";
4
+ //#region src/index.ts
5
+ /**
6
+ * @pixui-dev/inflight-test-webpack-plugin
7
+ *
8
+ * Wires inflight-test into a PixUI activity's webpack build:
9
+ * - discovers test files by glob (default `**\/*.test.{ts,tsx,js,jsx}`),
10
+ * - generates a runner module that binds the discovered cases to the
11
+ * inflight-test runtime, exporting `start` / `startWithoutReport`,
12
+ * - and aliases `@pixui-dev/inflight-test/runner` to that generated module,
13
+ * so the activity just does
14
+ * `import { start } from '@pixui-dev/inflight-test/runner'`
15
+ * and calls it (from a button / QA panel) — no globals, no separate
16
+ * entry/chunk to load.
17
+ *
18
+ * The cases are inlined (`webpackMode: "eager"`) into whatever chunk imports the
19
+ * runner, because the PixUI QuickJS runtime has no async chunk loader.
20
+ *
21
+ * Cross-version: it only mutates `resolve.alias` (+ writes a file), which is
22
+ * identical on webpack 4 and 5 — no version-specific entry/asset APIs.
23
+ */
24
+ const ALWAYS_EXCLUDE = [
25
+ "**/node_modules/**",
26
+ "**/dist/**",
27
+ "**/build/**",
28
+ "**/.git/**"
29
+ ];
30
+ function toArray(v, fallback) {
31
+ if (v == null) return fallback;
32
+ return Array.isArray(v) ? v : [v];
33
+ }
34
+ var InflightTestPlugin = class {
35
+ _opts;
36
+ constructor(options = {}) {
37
+ this._opts = options;
38
+ }
39
+ apply(compiler) {
40
+ if (this._opts.enabled === false) return;
41
+ const cwd = resolve(this._opts.cwd ?? compiler.context ?? process.cwd());
42
+ const runtimeImport = this._opts.runtimeImport ?? "@pixui-dev/inflight-test";
43
+ const opts = {
44
+ include: toArray(this._opts.include, ["**/*.test.{ts,tsx,js,jsx}"]),
45
+ exclude: toArray(this._opts.exclude, []),
46
+ cwd,
47
+ runtimeImport,
48
+ runnerImport: this._opts.runnerImport ?? `${runtimeImport}/runner`,
49
+ bridge: this._opts.bridge ?? "gamelet",
50
+ gameletImport: this._opts.gameletImport ?? "gamelet-pixui-frame"
51
+ };
52
+ const testFiles = this._discover(opts);
53
+ const runnerFile = this._writeRunnerModule(opts, testFiles);
54
+ this._aliasRunner(compiler, opts.runnerImport, runnerFile);
55
+ }
56
+ /**
57
+ * Point the runner specifier at the generated module via `resolve.alias`.
58
+ * Works identically on webpack 4 and 5 (object or array alias forms).
59
+ */
60
+ _aliasRunner(compiler, specifier, file) {
61
+ const resolveOpts = compiler.options.resolve ??= {};
62
+ const alias = resolveOpts.alias ??= {};
63
+ if (Array.isArray(alias)) alias.push({
64
+ name: specifier,
65
+ alias: file,
66
+ onlyModule: true
67
+ });
68
+ else alias[`${specifier}$`] = file;
69
+ }
70
+ _discover(opts) {
71
+ const isMatch = picomatch(opts.include, { dot: false });
72
+ const isExcluded = picomatch([...ALWAYS_EXCLUDE, ...opts.exclude], { dot: true });
73
+ const out = [];
74
+ const walk = (dir) => {
75
+ let entries;
76
+ try {
77
+ entries = readdirSync(dir);
78
+ } catch {
79
+ return;
80
+ }
81
+ for (const name of entries) {
82
+ const abs = join(dir, name);
83
+ const rel = relative(opts.cwd, abs).replace(/\\/g, "/");
84
+ if (isExcluded(rel)) continue;
85
+ let st;
86
+ try {
87
+ st = statSync(abs);
88
+ } catch {
89
+ continue;
90
+ }
91
+ if (st.isDirectory()) walk(abs);
92
+ else if (isMatch(rel)) out.push({
93
+ id: rel,
94
+ absolute: abs.replace(/\\/g, "/")
95
+ });
96
+ }
97
+ };
98
+ walk(opts.cwd);
99
+ out.sort((a, b) => a.id.localeCompare(b.id));
100
+ return out;
101
+ }
102
+ _writeRunnerModule(opts, testFiles) {
103
+ const cacheDir = join(opts.cwd, "node_modules", ".cache", "inflight-test");
104
+ if (!existsSync(cacheDir)) mkdirSync(cacheDir, { recursive: true });
105
+ const runnerFile = join(cacheDir, "runner.mjs");
106
+ const specs = testFiles.map((f) => {
107
+ return ` { id: ${JSON.stringify(f.id)}, load: () => import(/* webpackMode: "eager" */ ${JSON.stringify(f.absolute)}) },`;
108
+ }).join("\n");
109
+ const useGamelet = opts.bridge === "gamelet";
110
+ const gameletImportLine = useGamelet ? `import __inflightGamelet from ${JSON.stringify(opts.gameletImport)}\n` : "";
111
+ const bridgeBlock = useGamelet ? `
112
+ // Default activity bridge over gamelet-pixui-frame (host screenshot protocol etc.).
113
+ var GameletAPI = __inflightGamelet.GameletAPI
114
+ function makeActivityBridge() {
115
+ return {
116
+ callGame: function (protocol, payload) {
117
+ // gamelet-pixui-frame fills appId/appName automatically; we only send the
118
+ // type + content envelope and the call-specific payload.
119
+ GameletAPI.callGame(JSON.stringify(Object.assign({ type: protocol, content: '' }, payload)))
120
+ },
121
+ onSDKMessage: function (protocol, handler) {
122
+ var myAppId = GameletAPI.getAppID()
123
+ var listener = function (cmd) {
124
+ var msg
125
+ try { msg = JSON.parse(cmd) } catch (e) { return }
126
+ if (!msg || msg.type !== protocol) return
127
+ // Ignore messages addressed to another app: addOnGameCommandListener
128
+ // also subscribes the GLOBAL game-message channel, which isn't
129
+ // app-scoped, so a different app's callback can reach us. Compare as
130
+ // strings — the backend sends appId as a string ("7442") while
131
+ // getAppID() returns a number (7442).
132
+ if (msg.appId != null && String(msg.appId) !== String(myAppId)) return
133
+ handler(msg)
134
+ }
135
+ GameletAPI.addOnGameCommandListener(listener)
136
+ return function () { GameletAPI.removeOnGameCommandListener(listener) }
137
+ },
138
+ }
139
+ }
140
+ ` : "";
141
+ const applyBridge = useGamelet ? ` if (!o.activityBridge) o.activityBridge = makeActivityBridge()\n` : "";
142
+ writeFileSync(runnerFile, `// AUTO-GENERATED by @pixui-dev/inflight-test-webpack-plugin — do not edit.
143
+ import { start as _start } from ${JSON.stringify(opts.runtimeImport)}
144
+ ${gameletImportLine}
145
+ const specs = [
146
+ ${specs}
147
+ ]
148
+
149
+ /** Ids of the discovered test files. */
150
+ export const testFiles = specs.map(function (s) { return s.id })
151
+ ${bridgeBlock}
152
+ /**
153
+ * Run the discovered inflight tests. Writes a Vitest blob report by default
154
+ * (auto-wired from the PixUI host \`os\` / \`external.writeablePath\`); pass
155
+ * \`{ report: false }\` to skip. Call from a button / QA panel — no options
156
+ * needed in the normal case.
157
+ */
158
+ export function start(options) {
159
+ var o = Object.assign({}, options)
160
+ ${applyBridge} return _start(specs, o)
161
+ }
162
+
163
+ /** Run the discovered inflight tests without writing a report. */
164
+ export function startWithoutReport(options) {
165
+ return start(Object.assign({}, options, { report: false }))
166
+ }
167
+ `, "utf8");
168
+ return runnerFile;
169
+ }
170
+ };
171
+ //#endregion
172
+ export { InflightTestPlugin, InflightTestPlugin as default };
173
+
174
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@pixui-dev/inflight-test-webpack-plugin",
3
+ "license": "UNLICENSED",
4
+ "version": "0.0.7",
5
+ "description": "Webpack plugin that discovers *.test.ts cases and wires them into a PixUI activity as an opt-in inflight-test chunk",
6
+ "type": "module",
7
+ "types": "./dist/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "main": "./dist/index.cjs",
22
+ "module": "./dist/index.mjs",
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsdown",
28
+ "test": "vitest run"
29
+ },
30
+ "dependencies": {
31
+ "picomatch": "^4.0.4"
32
+ },
33
+ "peerDependencies": {
34
+ "webpack": "^4.0.0 || ^5.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/picomatch": "^4.0.3",
38
+ "tsdown": "^0.21.10",
39
+ "webpack": "^5.0.0"
40
+ }
41
+ }