@andy2639/jest-context 1.0.0 → 1.0.2

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +23 -1
  3. package/dist/bin/console-context.cjs +31 -8
  4. package/dist/bin/console-context.cjs.map +1 -1
  5. package/dist/bin/console-context.mjs +2 -2
  6. package/dist/bin/coverage-context.cjs +47 -30
  7. package/dist/bin/coverage-context.cjs.map +1 -1
  8. package/dist/bin/coverage-context.mjs +2 -2
  9. package/dist/bin/jest-context-init.cjs +142 -0
  10. package/dist/bin/jest-context-init.cjs.map +1 -0
  11. package/dist/bin/jest-context-init.d.mts +1 -0
  12. package/dist/bin/jest-context-init.d.ts +1 -0
  13. package/dist/bin/jest-context-init.mjs +14 -0
  14. package/dist/bin/jest-context-init.mjs.map +1 -0
  15. package/dist/bin/test-context.cjs +30 -7
  16. package/dist/bin/test-context.cjs.map +1 -1
  17. package/dist/bin/test-context.mjs +2 -2
  18. package/dist/{chunk-WPFTKCAT.mjs → chunk-5QGDUTB3.mjs} +4 -4
  19. package/dist/chunk-5QGDUTB3.mjs.map +1 -0
  20. package/dist/{chunk-YTFA3KPD.mjs → chunk-GV4AYRBZ.mjs} +43 -25
  21. package/dist/chunk-GV4AYRBZ.mjs.map +1 -0
  22. package/dist/{chunk-DUQBPBV4.mjs → chunk-RCPJ7B43.mjs} +4 -4
  23. package/dist/{chunk-DUQBPBV4.mjs.map → chunk-RCPJ7B43.mjs.map} +1 -1
  24. package/dist/chunk-UPBDVC5T.mjs +71 -0
  25. package/dist/chunk-UPBDVC5T.mjs.map +1 -0
  26. package/dist/{chunk-DEJBEL4M.mjs → chunk-WEP26C5H.mjs} +10 -11
  27. package/dist/chunk-WEP26C5H.mjs.map +1 -0
  28. package/dist/index.cjs +115 -36
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.mts +3 -1
  31. package/dist/index.d.ts +3 -1
  32. package/dist/index.mjs +8 -4
  33. package/package.json +3 -3
  34. package/dist/chunk-DEJBEL4M.mjs.map +0 -1
  35. package/dist/chunk-WPFTKCAT.mjs.map +0 -1
  36. package/dist/chunk-YTFA3KPD.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  JEST_PATTERNS,
3
- createProgressBar,
3
+ createSpinner,
4
4
  displayBanner,
5
5
  displayHelp,
6
6
  handleExportOrDisplay,
@@ -9,7 +9,7 @@ import {
9
9
  runJest,
10
10
  shouldShowUI,
11
11
  validateCliArgs
12
- } from "./chunk-YTFA3KPD.mjs";
12
+ } from "./chunk-GV4AYRBZ.mjs";
13
13
 
14
14
  // src/commands/coverage-context.ts
15
15
  import path from "path";
@@ -116,25 +116,24 @@ async function runCoverageContext(argv = process.argv.slice(2)) {
116
116
  const showUI = shouldShowUI(exportFormat, noBanner);
117
117
  if (showUI) {
118
118
  displayBanner({
119
- text: "COVERAGE",
119
+ text: "JEST",
120
+ subtitle: "COVERAGE",
120
121
  info: {
121
122
  Threshold: `>=${threshold}%`
122
123
  },
123
124
  colors: ["magenta", "blue"]
124
125
  });
125
126
  }
126
- const bar = showUI ? createProgressBar(100, "Running coverage") : null;
127
- bar?.update(10, { status: "Running Jest..." });
127
+ const spinner = showUI ? createSpinner("Running Jest coverage...", "magenta") : null;
128
+ spinner?.start();
128
129
  const result = await runJest([], {
129
130
  coverage: true,
130
131
  coverageReporters: ["text", "text-summary"],
131
132
  ignoreErrors: true
132
133
  });
133
- bar?.update(70, { status: "Parsing output..." });
134
134
  const rows = parseCoverage(result.output);
135
135
  if (rows.length === 0) {
136
- bar?.update(100, { status: "Done" });
137
- bar?.stop();
136
+ spinner?.warn("No coverage rows parsed");
138
137
  handleExportOrDisplay("No coverage rows parsed from Jest output.", {
139
138
  exportFormat,
140
139
  prefix: "export-coverage-context",
@@ -144,9 +143,8 @@ async function runCoverageContext(argv = process.argv.slice(2)) {
144
143
  }
145
144
  const incomplete = filterIncomplete(rows, threshold);
146
145
  const grouped = groupByFolder(incomplete);
147
- bar?.update(100, { status: "Done" });
148
- bar?.stop();
149
146
  if (Object.keys(grouped).length === 0) {
147
+ spinner?.succeed(`All files meet threshold >= ${threshold}%`);
150
148
  handleExportOrDisplay(`All files meet threshold >= ${threshold}%`, {
151
149
  exportFormat,
152
150
  prefix: "export-coverage-context",
@@ -155,6 +153,7 @@ async function runCoverageContext(argv = process.argv.slice(2)) {
155
153
  return;
156
154
  }
157
155
  const formatted = formatCoverageOutput(grouped);
156
+ spinner?.warn(`Found ${incomplete.length} file(s) below threshold`);
158
157
  handleExportOrDisplay(formatted, {
159
158
  exportFormat,
160
159
  prefix: "export-coverage-context",
@@ -165,4 +164,4 @@ async function runCoverageContext(argv = process.argv.slice(2)) {
165
164
  export {
166
165
  runCoverageContext
167
166
  };
168
- //# sourceMappingURL=chunk-DEJBEL4M.mjs.map
167
+ //# sourceMappingURL=chunk-WEP26C5H.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/coverage-context.ts"],"sourcesContent":["import path from \"node:path\";\n\nimport {\n createSpinner,\n displayBanner,\n displayHelp,\n exitWithError,\n handleExportOrDisplay,\n JEST_PATTERNS,\n parseExportCliArgs,\n runJest,\n shouldShowUI,\n validateCliArgs,\n logWarning,\n} from \"../core\";\n\nfunction parseCoverage(text: string) {\n const lines = text.split(\"\\n\");\n const rows: Array<{\n folder: string;\n file: string;\n stmts: number;\n branch: number;\n funcs: number;\n lines: number;\n uncovered?: string;\n }> = [];\n\n let currentFolder: string | null = null;\n\n for (const line of lines) {\n const match = line.match(JEST_PATTERNS.COVERAGE_LINE);\n if (!match) continue;\n\n const [, rawFile, stmts, branch, funcs, linesPct, uncovered] = match;\n const normalized = rawFile.trim();\n\n if (normalized === \"All files\") continue;\n\n const isFile = /\\.(ts|tsx)$/.test(normalized);\n if (!isFile) {\n currentFolder = normalized;\n continue;\n }\n\n if (/index\\.(ts|tsx)$/.test(normalized)) continue;\n if (!currentFolder) currentFolder = path.dirname(normalized);\n\n rows.push({\n folder: currentFolder,\n file: path.basename(normalized),\n stmts: Number(stmts),\n branch: Number(branch),\n funcs: Number(funcs),\n lines: Number(linesPct),\n uncovered: uncovered?.trim(),\n });\n }\n\n return rows;\n}\n\nfunction filterIncomplete(rows: ReturnType<typeof parseCoverage>, threshold = 100) {\n return rows.filter(\n (r) => !(r.stmts >= threshold && r.branch >= threshold && r.funcs >= threshold && r.lines >= threshold)\n );\n}\n\nfunction groupByFolder(rows: ReturnType<typeof parseCoverage>) {\n const grouped: Record<string, Array<{ file: string; coverage: Omit<(typeof rows)[number], \"folder\" | \"file\"> }>> = {};\n\n for (const row of rows) {\n const { folder, file, ...rest } = row;\n if (!grouped[folder]) grouped[folder] = [];\n grouped[folder].push({ file, coverage: rest });\n }\n\n return grouped;\n}\n\nfunction formatCoverageOutput(grouped: ReturnType<typeof groupByFolder>): string {\n let output = \"\";\n\n for (const folder of Object.keys(grouped)) {\n output += `📁 Folder: ${folder}\\n`;\n\n for (const item of grouped[folder]) {\n const { file, coverage } = item;\n output += ` 📄 File: ${file}\\n`;\n output += `- Coverage:\\n`;\n output += ` - Stmts: ${coverage.stmts}\\n`;\n output += ` - Branch: ${coverage.branch}\\n`;\n output += ` - Funcs: ${coverage.funcs}\\n`;\n output += ` - Lines: ${coverage.lines}\\n`;\n output += ` - Uncovered: ${coverage.uncovered || \"N/A\"}\\n\\n`;\n }\n\n output += \"\\n\";\n }\n\n return output.trim();\n}\n\nexport async function runCoverageContext(argv = process.argv.slice(2)): Promise<void> {\n if (argv.includes(\"-h\") || argv.includes(\"--help\")) {\n displayHelp(\"Coverage Context Script\", {\n description: \"Run Jest coverage and print files below threshold.\",\n usage: [\"coverage-context\", \"coverage-context --threshold=90\"],\n options: [\n \"--threshold=0..100 Minimum percentage for stmts/branch/funcs/lines (default: 100)\",\n \"--export Export output as txt\",\n \"--export=txt|md Export output in selected format\",\n \"--no-banner Disable fancy terminal UI\",\n \"-h, --help Show this help\",\n ],\n examples: [\"coverage-context --threshold=90 --export=md\"],\n });\n }\n\n const noBanner = argv.includes(\"--no-banner\");\n const args = argv.filter((arg) => arg !== \"--no-banner\");\n\n const { exportFormat, exportArgs } = parseExportCliArgs(args);\n const { parsed, unknownArgs } = validateCliArgs(args, {\n threshold: {\n prefix: \"--threshold=\",\n allowedValues: Array.from({ length: 101 }, (_, i) => String(i)),\n },\n });\n\n const exportArgSet = new Set(exportArgs);\n const filteredUnknownArgs = unknownArgs.filter((arg) => !exportArgSet.has(arg));\n if (filteredUnknownArgs.length > 0) {\n logWarning(`Unknown arguments ignored: ${filteredUnknownArgs.join(\", \")}`);\n }\n\n const threshold = parsed.threshold ? Number(parsed.threshold) : 100;\n const showUI = shouldShowUI(exportFormat, noBanner);\n\n if (showUI) {\n displayBanner({\n text: \"JEST\",\n subtitle: \"COVERAGE\",\n info: {\n Threshold: `>=${threshold}%`,\n },\n colors: [\"magenta\", \"blue\"],\n });\n }\n\n const spinner = showUI ? createSpinner(\"Running Jest coverage...\", \"magenta\") : null;\n spinner?.start();\n\n const result = await runJest([], {\n coverage: true,\n coverageReporters: [\"text\", \"text-summary\"],\n ignoreErrors: true,\n });\n\n const rows = parseCoverage(result.output);\n if (rows.length === 0) {\n spinner?.warn(\"No coverage rows parsed\");\n\n handleExportOrDisplay(\"No coverage rows parsed from Jest output.\", {\n exportFormat,\n prefix: \"export-coverage-context\",\n title: \"Coverage Context\",\n });\n\n return;\n }\n\n const incomplete = filterIncomplete(rows, threshold);\n const grouped = groupByFolder(incomplete);\n\n if (Object.keys(grouped).length === 0) {\n spinner?.succeed(`All files meet threshold >= ${threshold}%`);\n\n handleExportOrDisplay(`All files meet threshold >= ${threshold}%`, {\n exportFormat,\n prefix: \"export-coverage-context\",\n title: \"Coverage Context\",\n });\n\n return;\n }\n\n const formatted = formatCoverageOutput(grouped);\n spinner?.warn(`Found ${incomplete.length} file(s) below threshold`);\n\n handleExportOrDisplay(formatted, {\n exportFormat,\n prefix: \"export-coverage-context\",\n title: \"Coverage Context\",\n });\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -69,6 +69,7 @@ __export(index_exports, {
69
69
  resolveCommand: () => resolveCommand,
70
70
  runConsoleContext: () => runConsoleContext,
71
71
  runCoverageContext: () => runCoverageContext,
72
+ runInitContextScripts: () => runInitContextScripts,
72
73
  runJest: () => runJest,
73
74
  runTestContext: () => runTestContext,
74
75
  shouldShowUI: () => shouldShowUI,
@@ -147,7 +148,7 @@ async function execCommand(cmd, args = [], options = {}) {
147
148
  return new Promise((resolve, reject) => {
148
149
  const resolvedCmd = resolveCommand(cmd);
149
150
  const child = (0, import_node_child_process.spawn)(resolvedCmd, args, {
150
- shell: options.shell ?? process.platform === "win32"
151
+ shell: options.shell ?? false
151
152
  });
152
153
  let stdout = "";
153
154
  let stderr = "";
@@ -227,6 +228,7 @@ async function runJest(args = [], options = {}) {
227
228
  disableVerbose = false
228
229
  } = options;
229
230
  const jestCmd = getJestCommand(packageManager);
231
+ const command = resolveCommand(jestCmd[0]);
230
232
  const jestArgs = [...jestCmd.slice(1)];
231
233
  if (verbose && !disableVerbose) {
232
234
  jestArgs.push("--verbose");
@@ -244,8 +246,8 @@ async function runJest(args = [], options = {}) {
244
246
  }
245
247
  jestArgs.push(...args);
246
248
  return new Promise((resolve, reject) => {
247
- const child = (0, import_node_child_process2.spawn)(jestCmd[0], jestArgs, {
248
- shell: isWindows()
249
+ const child = (0, import_node_child_process2.spawn)(command, jestArgs, {
250
+ shell: false
249
251
  });
250
252
  let stdout = "";
251
253
  let stderr = "";
@@ -470,6 +472,14 @@ function logSuccess(message) {
470
472
  }
471
473
 
472
474
  // src/core/ui.ts
475
+ var NOOP_PROGRESS_BAR = {
476
+ update: () => {
477
+ },
478
+ stop: () => {
479
+ }
480
+ };
481
+ var hasWarnedSpinnerFallback = false;
482
+ var hasWarnedProgressFallback = false;
473
483
  function shouldShowUI(exportMode, noBanner = false) {
474
484
  return !exportMode && !noBanner && Boolean(process.stdout.isTTY);
475
485
  }
@@ -515,29 +525,38 @@ function displayBanner(config) {
515
525
  console.log(boxed);
516
526
  }
517
527
  function createSpinner(text, color = "cyan") {
518
- const ora = require("ora");
519
- return ora({ text, color, spinner: "dots" });
528
+ try {
529
+ const ora = require("ora");
530
+ return ora({ text, color, spinner: "dots" });
531
+ } catch {
532
+ if (!hasWarnedSpinnerFallback) {
533
+ hasWarnedSpinnerFallback = true;
534
+ console.warn("Warning: spinner UI unavailable, continuing without spinner.");
535
+ }
536
+ return {
537
+ start: () => NOOP_SPINNER,
538
+ stop: () => NOOP_SPINNER,
539
+ succeed: () => NOOP_SPINNER,
540
+ warn: () => NOOP_SPINNER,
541
+ fail: () => NOOP_SPINNER
542
+ };
543
+ }
520
544
  }
545
+ var NOOP_SPINNER = {
546
+ start: () => NOOP_SPINNER,
547
+ stop: () => NOOP_SPINNER,
548
+ succeed: () => NOOP_SPINNER,
549
+ warn: () => NOOP_SPINNER,
550
+ fail: () => NOOP_SPINNER
551
+ };
521
552
  function createProgressBar(total = 100, task = "Progress") {
522
- const cliProgress = require("cli-progress");
523
- const bar = new cliProgress.SingleBar(
524
- {
525
- format: "{task} [{bar}] {percentage}% | {status}",
526
- barCompleteChar: "\u2588",
527
- barIncompleteChar: "\u2591",
528
- hideCursor: true,
529
- barsize: 24
530
- },
531
- cliProgress.Presets.shades_classic
532
- );
533
- bar.start(total, 0, {
534
- task,
535
- status: "Starting..."
536
- });
537
- return {
538
- update: (value, payload) => bar.update(value, payload),
539
- stop: () => bar.stop()
540
- };
553
+ if (!hasWarnedProgressFallback) {
554
+ hasWarnedProgressFallback = true;
555
+ console.warn(
556
+ `Warning: progress bar UI is no longer used. Ignoring createProgressBar(${total}, ${JSON.stringify(task)}).`
557
+ );
558
+ }
559
+ return NOOP_PROGRESS_BAR;
541
560
  }
542
561
 
543
562
  // src/commands/test-context.ts
@@ -627,8 +646,8 @@ async function runTestContext(argv = process.argv.slice(2)) {
627
646
  const showUI = shouldShowUI(exportFormat, noBanner);
628
647
  if (showUI) {
629
648
  displayBanner({
630
- text: "TEST",
631
- subtitle: "CONTEXT",
649
+ text: "JEST",
650
+ subtitle: "TEST",
632
651
  info: {
633
652
  Mode: mode
634
653
  },
@@ -792,25 +811,24 @@ async function runCoverageContext(argv = process.argv.slice(2)) {
792
811
  const showUI = shouldShowUI(exportFormat, noBanner);
793
812
  if (showUI) {
794
813
  displayBanner({
795
- text: "COVERAGE",
814
+ text: "JEST",
815
+ subtitle: "COVERAGE",
796
816
  info: {
797
817
  Threshold: `>=${threshold}%`
798
818
  },
799
819
  colors: ["magenta", "blue"]
800
820
  });
801
821
  }
802
- const bar = showUI ? createProgressBar(100, "Running coverage") : null;
803
- bar?.update(10, { status: "Running Jest..." });
822
+ const spinner = showUI ? createSpinner("Running Jest coverage...", "magenta") : null;
823
+ spinner?.start();
804
824
  const result = await runJest([], {
805
825
  coverage: true,
806
826
  coverageReporters: ["text", "text-summary"],
807
827
  ignoreErrors: true
808
828
  });
809
- bar?.update(70, { status: "Parsing output..." });
810
829
  const rows = parseCoverage(result.output);
811
830
  if (rows.length === 0) {
812
- bar?.update(100, { status: "Done" });
813
- bar?.stop();
831
+ spinner?.warn("No coverage rows parsed");
814
832
  handleExportOrDisplay("No coverage rows parsed from Jest output.", {
815
833
  exportFormat,
816
834
  prefix: "export-coverage-context",
@@ -820,9 +838,8 @@ async function runCoverageContext(argv = process.argv.slice(2)) {
820
838
  }
821
839
  const incomplete = filterIncomplete(rows, threshold);
822
840
  const grouped = groupByFolder(incomplete);
823
- bar?.update(100, { status: "Done" });
824
- bar?.stop();
825
841
  if (Object.keys(grouped).length === 0) {
842
+ spinner?.succeed(`All files meet threshold >= ${threshold}%`);
826
843
  handleExportOrDisplay(`All files meet threshold >= ${threshold}%`, {
827
844
  exportFormat,
828
845
  prefix: "export-coverage-context",
@@ -831,6 +848,7 @@ async function runCoverageContext(argv = process.argv.slice(2)) {
831
848
  return;
832
849
  }
833
850
  const formatted = formatCoverageOutput(grouped);
851
+ spinner?.warn(`Found ${incomplete.length} file(s) below threshold`);
834
852
  handleExportOrDisplay(formatted, {
835
853
  exportFormat,
836
854
  prefix: "export-coverage-context",
@@ -1021,8 +1039,8 @@ async function runConsoleContext(argv = process.argv.slice(2)) {
1021
1039
  const showUI = shouldShowUI(exportFormat, noBanner);
1022
1040
  if (showUI) {
1023
1041
  displayBanner({
1024
- text: "CONSOLE",
1025
- subtitle: "WARNINGS",
1042
+ text: "JEST",
1043
+ subtitle: "CONSOLE",
1026
1044
  info: {
1027
1045
  Mode: mode,
1028
1046
  Filter: filter ?? "none"
@@ -1062,6 +1080,66 @@ async function runConsoleContext(argv = process.argv.slice(2)) {
1062
1080
  title: "Console Warnings Context"
1063
1081
  });
1064
1082
  }
1083
+
1084
+ // src/commands/init-context-scripts.ts
1085
+ var import_node_fs2 = require("fs");
1086
+ var import_node_path4 = __toESM(require("path"));
1087
+ var CONTEXT_SCRIPTS = {
1088
+ "console:context": "console-context",
1089
+ "coverage:context": "coverage-context",
1090
+ "test:context": "test-context"
1091
+ };
1092
+ function runInitContextScripts(argv = process.argv.slice(2)) {
1093
+ if (argv.includes("-h") || argv.includes("--help")) {
1094
+ displayHelp("Jest Context Init Script", {
1095
+ description: "Inject context scripts into package.json in the current project.",
1096
+ usage: ["jest-context-init", "jest-context-init --help"],
1097
+ options: ["-h, --help Show this help"],
1098
+ examples: ["npx jest-context-init", "pnpm dlx @andy2639/jest-context jest-context-init"]
1099
+ });
1100
+ }
1101
+ const packageJsonPath = import_node_path4.default.resolve(process.cwd(), "package.json");
1102
+ let raw = "";
1103
+ try {
1104
+ raw = (0, import_node_fs2.readFileSync)(packageJsonPath, "utf-8");
1105
+ } catch {
1106
+ exitWithError(`Could not read package.json at: ${packageJsonPath}`);
1107
+ }
1108
+ let packageJson;
1109
+ try {
1110
+ packageJson = JSON.parse(raw);
1111
+ } catch {
1112
+ exitWithError(`Invalid JSON in package.json: ${packageJsonPath}`);
1113
+ }
1114
+ const scripts = packageJson.scripts && typeof packageJson.scripts === "object" ? { ...packageJson.scripts } : {};
1115
+ const added = [];
1116
+ const updated = [];
1117
+ for (const [name, value] of Object.entries(CONTEXT_SCRIPTS)) {
1118
+ if (!(name in scripts)) {
1119
+ scripts[name] = value;
1120
+ added.push(name);
1121
+ continue;
1122
+ }
1123
+ if (scripts[name] !== value) {
1124
+ scripts[name] = value;
1125
+ updated.push(name);
1126
+ }
1127
+ }
1128
+ if (added.length === 0 && updated.length === 0) {
1129
+ logInfo("package.json already has the expected context scripts.");
1130
+ return;
1131
+ }
1132
+ packageJson.scripts = scripts;
1133
+ (0, import_node_fs2.writeFileSync)(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
1134
+ `, "utf-8");
1135
+ logSuccess(`Updated package.json scripts at: ${packageJsonPath}`);
1136
+ if (added.length > 0) {
1137
+ logInfo(`Added scripts: ${added.join(", ")}`);
1138
+ }
1139
+ if (updated.length > 0) {
1140
+ logInfo(`Updated scripts: ${updated.join(", ")}`);
1141
+ }
1142
+ }
1065
1143
  // Annotate the CommonJS export names for ESM import in node:
1066
1144
  0 && (module.exports = {
1067
1145
  CONSOLE_LOG_TYPES,
@@ -1103,6 +1181,7 @@ async function runConsoleContext(argv = process.argv.slice(2)) {
1103
1181
  resolveCommand,
1104
1182
  runConsoleContext,
1105
1183
  runCoverageContext,
1184
+ runInitContextScripts,
1106
1185
  runJest,
1107
1186
  runTestContext,
1108
1187
  shouldShowUI,