mmt-testlight 0.3.0 → 0.3.1

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/esbuild.js ADDED
@@ -0,0 +1,48 @@
1
+ import * as esbuild from 'esbuild';
2
+ import path from 'path';
3
+ import {fileURLToPath} from 'url';
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const coreDir = path.resolve(__dirname, '..', 'core', 'src');
7
+
8
+ // Plugin to resolve 'mmt-core' and 'mmt-core/<subpath>' to local core sources
9
+ const mmtCorePlugin = {
10
+ name: 'mmt-core-resolver',
11
+ setup(build) {
12
+ // Resolve bare 'mmt-core' → core/src/index.ts
13
+ build.onResolve({filter: /^mmt-core$/}, () => ({
14
+ path: path.join(coreDir, 'index.ts'),
15
+ }));
16
+ // Resolve 'mmt-core/<sub>' → core/src/<sub>.ts
17
+ build.onResolve({filter: /^mmt-core\//}, (args) => {
18
+ const sub = args.path.replace(/^mmt-core\//, '');
19
+ return {path: path.join(coreDir, sub + '.ts')};
20
+ });
21
+ },
22
+ };
23
+
24
+ await esbuild.build({
25
+ entryPoints: ['src/cli.ts'],
26
+ bundle: true,
27
+ platform: 'node',
28
+ target: 'node18',
29
+ format: 'esm',
30
+ outfile: 'dist/cli.js',
31
+ banner: {
32
+ js: '#!/usr/bin/env node',
33
+ },
34
+ plugins: [mmtCorePlugin],
35
+ // Keep actual npm dependencies external (not bundled)
36
+ external: [
37
+ 'commander',
38
+ 'js-yaml',
39
+ 'axios',
40
+ 'ws',
41
+ 'yaml',
42
+ 'xml-js',
43
+ ],
44
+ // Suppress dynamic require warnings (the fallback paths in resolveCoreExport)
45
+ logOverride: {
46
+ 'unsupported-dynamic-import': 'silent',
47
+ },
48
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mmt-testlight",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Multimeter CLI test runner",
5
5
  "type": "module",
6
6
  "private": false,
@@ -9,7 +9,8 @@
9
9
  "mmt": "dist/cli.js"
10
10
  },
11
11
  "scripts": {
12
- "build": "tsc -p tsconfig.json",
12
+ "build": "node esbuild.js",
13
+ "build:tsc": "tsc -p tsconfig.json",
13
14
  "build:cjs": "tsc -p tsconfig.cjs.json",
14
15
  "dev": "tsx src/cli.ts --help",
15
16
  "clean": "rimraf dist",
@@ -36,7 +37,8 @@
36
37
  "dependencies": {
37
38
  "commander": "^12.1.0",
38
39
  "js-yaml": "^4.1.0",
39
- "mmt-core": "file:../core",
40
+ "yaml": "^2.7.0",
41
+ "xml-js": "^1.6.11",
40
42
  "axios": "^1.10.0",
41
43
  "ws": "^8.18.3"
42
44
  },
package/src/cli.ts CHANGED
@@ -159,11 +159,23 @@ program.command('run')
159
159
  console.log(js.trim());
160
160
  }
161
161
  if (!opts.quiet) {
162
- console.log(`Success: ${result.success}`);
163
- console.log(`Duration: ${mmtcore.CommonData.formatDuration(result.durationMs)}`);
164
- if (result.errors.length) {
165
- console.log('Errors:');
166
- result.errors.forEach((e: any) => console.log(' -', e));
162
+ const isTTY = process.stdout.isTTY !== false;
163
+ const R = isTTY ? '\x1b[0m' : '';
164
+ const red = isTTY ? '\x1b[31m' : '';
165
+ const green = isTTY ? '\x1b[32m' : '';
166
+ const bold = isTTY ? '\x1b[1m' : '';
167
+ const dim = isTTY ? '\x1b[2m' : '';
168
+ if (result.success) {
169
+ console.log(`\n${green}${bold}\u2713 Success${R} ${dim}(${mmtcore.CommonData.formatDuration(result.durationMs)})${R}`);
170
+ } else {
171
+ console.log(`\n${red}${bold}\u2717 Failed${R} ${dim}(${mmtcore.CommonData.formatDuration(result.durationMs)})${R}`);
172
+ if (result.errors.length) {
173
+ console.log(`${red}Errors:${R}`);
174
+ result.errors.forEach((e: any) => {
175
+ const msg = String(e).replace(/^\u2717\s*/, '');
176
+ console.log(` ${red}\u2717${R} ${msg}`);
177
+ });
178
+ }
167
179
  }
168
180
  }
169
181
  if (outFile) {
package/src/pkg-entry.cjs CHANGED
@@ -679,12 +679,35 @@ function parseLogLevel(argv) {
679
679
  function createLevelLogger(minLevel) {
680
680
  const order = { error: 0, warn: 1, info: 2, debug: 3, trace: 4 };
681
681
  const min = order[minLevel] ?? order.info;
682
+ const isTTY = process.stdout.isTTY !== false;
683
+ const R = isTTY ? '\x1b[0m' : '';
684
+ const red = isTTY ? '\x1b[31m' : '';
685
+ const green = isTTY ? '\x1b[32m' : '';
686
+ const yellow = isTTY ? '\x1b[33m' : '';
687
+ const dim = isTTY ? '\x1b[2m' : '';
688
+ const bold = isTTY ? '\x1b[1m' : '';
682
689
  return (level, msg) => {
683
690
  const lvl = order[level] ?? order.info;
684
691
  if (lvl > min) {
685
692
  return;
686
693
  }
687
- process.stdout.write(String(msg) + '\n');
694
+ const s = String(msg);
695
+ // Check/Assert results (\u2713 / \u2717 markers)
696
+ if (s.startsWith('\u2713')) {
697
+ process.stdout.write(`${green}${s}${R}\n`);
698
+ return;
699
+ }
700
+ if (s.startsWith('\u2717')) {
701
+ process.stdout.write(`${red}${bold}${s}${R}\n`);
702
+ return;
703
+ }
704
+ switch (level) {
705
+ case 'error': process.stdout.write(`${red}[${level}] ${s}${R}\n`); break;
706
+ case 'warn': process.stdout.write(`${yellow}[${level}] ${s}${R}\n`); break;
707
+ case 'trace': // fallthrough
708
+ case 'debug': process.stdout.write(`${dim}[${level}] ${s}${R}\n`); break;
709
+ default: process.stdout.write(`[${level}] ${s}\n`); break;
710
+ }
688
711
  };
689
712
  }
690
713
 
@@ -994,12 +1017,23 @@ async function main() {
994
1017
  }
995
1018
 
996
1019
  if (!parsed.opts.quiet) {
1020
+ const isTTY = process.stdout.isTTY !== false;
1021
+ const R = isTTY ? '\x1b[0m' : '';
1022
+ const red = isTTY ? '\x1b[31m' : '';
1023
+ const green = isTTY ? '\x1b[32m' : '';
1024
+ const bold = isTTY ? '\x1b[1m' : '';
1025
+ const dim = isTTY ? '\x1b[2m' : '';
997
1026
  process.stdout.write(`Loaded: ${filePath}\n`);
998
- process.stdout.write(`Success: ${String(res.result?.success)}\n`);
999
- if (Array.isArray(res.result?.errors) && res.result.errors.length) {
1000
- process.stdout.write('Errors:\n');
1001
- for (const err of res.result.errors) {
1002
- process.stdout.write(`- ${String(err)}\n`);
1027
+ if (res.result?.success) {
1028
+ process.stdout.write(`\n${green}${bold}\u2713 Success${R} ${dim}(${res.result?.durationMs ?? 0}ms)${R}\n`);
1029
+ } else {
1030
+ process.stdout.write(`\n${red}${bold}\u2717 Failed${R} ${dim}(${res.result?.durationMs ?? 0}ms)${R}\n`);
1031
+ if (Array.isArray(res.result?.errors) && res.result.errors.length) {
1032
+ process.stdout.write(`${red}Errors:${R}\n`);
1033
+ for (const err of res.result.errors) {
1034
+ const msg = String(err).replace(/^\u2717\s*/, '');
1035
+ process.stdout.write(` ${red}\u2717${R} ${msg}\n`);
1036
+ }
1003
1037
  }
1004
1038
  }
1005
1039
  }
package/src/runArgs.ts CHANGED
@@ -12,6 +12,42 @@ export type ReportFormat = 'junit' | 'mmt' | 'html' | 'md';
12
12
  const {mergeEnv, resolvePresetEnv, resolveEnvFromDoc} =
13
13
  ((mmtcore as any).runConfig || {}) as any;
14
14
 
15
+ const LOG_LEVEL_PRIORITY: Record<string, number> = {
16
+ error: 0,
17
+ warn: 1,
18
+ info: 2,
19
+ log: 2,
20
+ debug: 3,
21
+ trace: 4,
22
+ };
23
+
24
+ /* ── ANSI helpers (no external deps, works in binaries too) ── */
25
+ const useColor = process.stdout.isTTY !== false;
26
+ const ANSI_RESET = useColor ? '\x1b[0m' : '';
27
+ const ANSI_RED = useColor ? '\x1b[31m' : '';
28
+ const ANSI_GREEN = useColor ? '\x1b[32m' : '';
29
+ const ANSI_YELLOW = useColor ? '\x1b[33m' : '';
30
+ const ANSI_DIM = useColor ? '\x1b[2m' : '';
31
+ const ANSI_BOLD = useColor ? '\x1b[1m' : '';
32
+
33
+ function colorizeLogLine(level: string, msg: string): string {
34
+ // Check / Assert results (✓ / ✗ markers emitted by check_)
35
+ if (msg.startsWith('\u2713')) {
36
+ return `${ANSI_GREEN}${msg}${ANSI_RESET}`;
37
+ }
38
+ if (msg.startsWith('\u2717')) {
39
+ return `${ANSI_RED}${ANSI_BOLD}${msg}${ANSI_RESET}`;
40
+ }
41
+ // Level-based coloring
42
+ switch (level) {
43
+ case 'error': return `${ANSI_RED}[${level}] ${msg}${ANSI_RESET}`;
44
+ case 'warn': return `${ANSI_YELLOW}[${level}] ${msg}${ANSI_RESET}`;
45
+ case 'trace': return `${ANSI_DIM}[${level}] ${msg}${ANSI_RESET}`;
46
+ case 'debug': return `${ANSI_DIM}[${level}] ${msg}${ANSI_RESET}`;
47
+ default: return `[${level}] ${msg}`;
48
+ }
49
+ }
50
+
15
51
  type AnyOpts = Record<string, any>;
16
52
 
17
53
  function coerceCliValue(v: string): any {
@@ -311,7 +347,11 @@ export function buildCliRunArgs(file: string, opts: AnyOpts): ParsedCliRunArgs {
311
347
  },
312
348
  jsRunner: async () => {},
313
349
  logger: (level: any, msg: string) => {
314
- console.log(`[${level}] ${msg}`);
350
+ const maxLevel = LOG_LEVEL_PRIORITY[opts.logLevel ?? 'info'] ?? 2;
351
+ const msgLevel = LOG_LEVEL_PRIORITY[level] ?? 2;
352
+ if (msgLevel <= maxLevel) {
353
+ console.log(colorizeLogLine(level, msg));
354
+ }
315
355
  },
316
356
  reporter: (_message: RunReporterMessage) => {},
317
357
  projectRoot: findProjectRootForCli(full),