qunitx-cli 0.17.6 → 0.17.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.
Files changed (2) hide show
  1. package/dist/cli.js +113 -43
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -53,6 +53,76 @@ var init_kill_process_group = __esm({
53
53
  }
54
54
  });
55
55
 
56
+ // lib/utils/cleanup-browser-dir.ts
57
+ import fs from "node:fs/promises";
58
+ async function cleanupBrowserDir(dirPath) {
59
+ if (process.platform !== "linux") {
60
+ await fs.rm(dirPath, { recursive: true, force: true }).catch(() => {
61
+ });
62
+ return;
63
+ }
64
+ const dirName = dirPath.split("/").pop();
65
+ const killedPids = /* @__PURE__ */ new Set();
66
+ const procEntries = await fs.readdir("/proc").catch(() => []);
67
+ await Promise.all(
68
+ procEntries.map(async (entry) => {
69
+ if (!/^\d+$/.test(entry)) return;
70
+ const pid = parseInt(entry);
71
+ try {
72
+ const [cwd, cmdline] = await Promise.all([
73
+ fs.readlink(`/proc/${entry}/cwd`).catch(() => ""),
74
+ fs.readFile(`/proc/${entry}/cmdline`, "utf8").catch(() => "")
75
+ ]);
76
+ if (!cwd.startsWith(dirPath) && !cmdline.includes(dirName)) return;
77
+ try {
78
+ process.kill(pid, "SIGKILL");
79
+ killedPids.add(pid);
80
+ } catch {
81
+ }
82
+ } catch {
83
+ }
84
+ })
85
+ );
86
+ while (killedPids.size > 0) {
87
+ await new Promise((resolve) => setTimeout(resolve, 20));
88
+ for (const pid of killedPids) {
89
+ try {
90
+ process.kill(pid, 0);
91
+ } catch {
92
+ killedPids.delete(pid);
93
+ }
94
+ }
95
+ }
96
+ const deadline = Date.now() + 1e3;
97
+ while (Date.now() < deadline) {
98
+ const removed = await fs.rm(dirPath, { recursive: true, force: true }).then(() => true).catch(() => false);
99
+ if (removed) break;
100
+ await new Promise((resolve) => setTimeout(resolve, 20));
101
+ }
102
+ if (await fs.access(dirPath).then(() => true).catch(() => false)) {
103
+ const diagEntries = await fs.readdir("/proc").catch(() => []);
104
+ await Promise.all(
105
+ diagEntries.map(async (entry) => {
106
+ if (!/^\d+$/.test(entry)) return;
107
+ try {
108
+ const cwd = await fs.readlink(`/proc/${entry}/cwd`).catch(() => "");
109
+ if (!cwd.startsWith(dirPath)) return;
110
+ const cmdline = await fs.readFile(`/proc/${entry}/cmdline`, "utf8").catch(() => "");
111
+ process.stderr.write(
112
+ `# [qunitx] cleanup failed: pid ${entry} still holds ${dirPath} as cwd (cmdline: ${cmdline.replace(/\0/g, " ").slice(0, 120)})
113
+ `
114
+ );
115
+ } catch {
116
+ }
117
+ })
118
+ );
119
+ }
120
+ }
121
+ var init_cleanup_browser_dir = __esm({
122
+ "lib/utils/cleanup-browser-dir.ts"() {
123
+ }
124
+ });
125
+
56
126
  // lib/utils/pre-launch-chrome.ts
57
127
  import { spawn } from "node:child_process";
58
128
  import { mkdtemp, rm } from "node:fs/promises";
@@ -123,11 +193,10 @@ async function preLaunchChrome(chromePath, args, headless = true) {
123
193
  } catch {
124
194
  break;
125
195
  }
126
- await new Promise((r) => setTimeout(r, 20));
196
+ await new Promise((resolve) => setTimeout(resolve, 20));
127
197
  }
128
198
  clearTimeout(warnTimer);
129
- await rm(userDataDir, { recursive: true, force: true }).catch(() => {
130
- });
199
+ await cleanupBrowserDir(userDataDir);
131
200
  });
132
201
  }
133
202
  }
@@ -135,6 +204,7 @@ var CDP_URL_REGEX;
135
204
  var init_pre_launch_chrome = __esm({
136
205
  "lib/utils/pre-launch-chrome.ts"() {
137
206
  init_kill_process_group();
207
+ init_cleanup_browser_dir();
138
208
  CDP_URL_REGEX = /DevTools listening on (ws:\/\/[^\s]+)/;
139
209
  }
140
210
  });
@@ -316,10 +386,10 @@ var init_color = __esm({
316
386
  });
317
387
 
318
388
  // lib/utils/path-exists.ts
319
- import fs from "node:fs/promises";
389
+ import fs2 from "node:fs/promises";
320
390
  async function pathExists(path6) {
321
391
  try {
322
- await fs.access(path6);
392
+ await fs2.access(path6);
323
393
  return true;
324
394
  } catch {
325
395
  return false;
@@ -331,7 +401,7 @@ var init_path_exists = __esm({
331
401
  });
332
402
 
333
403
  // lib/utils/read-boilerplate.ts
334
- import fs2 from "node:fs/promises";
404
+ import fs3 from "node:fs/promises";
335
405
  import { dirname, join as join2 } from "node:path";
336
406
  import { fileURLToPath } from "node:url";
337
407
  async function readBoilerplate(relativePath) {
@@ -340,7 +410,7 @@ async function readBoilerplate(relativePath) {
340
410
  const __dirname = dirname(fileURLToPath(import.meta.url));
341
411
  for (const base of ["../templates", "../../templates"]) {
342
412
  try {
343
- return (await fs2.readFile(join2(__dirname, base, relativePath))).toString();
413
+ return (await fs3.readFile(join2(__dirname, base, relativePath))).toString();
344
414
  } catch {
345
415
  }
346
416
  }
@@ -810,7 +880,7 @@ var init_http = __esm({
810
880
  });
811
881
 
812
882
  // lib/setup/web-server.ts
813
- import fs7 from "node:fs";
883
+ import fs8 from "node:fs";
814
884
  import path4 from "node:path";
815
885
  function setupWebServer(config, cachedContent) {
816
886
  const STATIC_FILES_PATH = path4.join(config.projectRoot, config.output);
@@ -954,7 +1024,7 @@ function setupWebServer(config, cachedContent) {
954
1024
  if (statusCode === 404) {
955
1025
  res.end();
956
1026
  } else {
957
- fs7.createReadStream(filePath).pipe(res);
1027
+ fs8.createReadStream(filePath).pipe(res);
958
1028
  }
959
1029
  console.log(`# [HTTPServer] GET ${url} ${statusCode} - ${/* @__PURE__ */ new Date() - requestStartedAt}ms`);
960
1030
  });
@@ -1134,7 +1204,7 @@ var init_web_server = __esm({
1134
1204
  init_color();
1135
1205
  init_path_exists();
1136
1206
  init_http();
1137
- fsPromise = fs7.promises;
1207
+ fsPromise = fs8.promises;
1138
1208
  }
1139
1209
  });
1140
1210
 
@@ -1314,7 +1384,7 @@ var init_display_final_result = __esm({
1314
1384
  });
1315
1385
 
1316
1386
  // lib/commands/run/tests-in-browser.ts
1317
- import fs8 from "node:fs/promises";
1387
+ import fs9 from "node:fs/promises";
1318
1388
  import esbuild from "esbuild";
1319
1389
  async function buildTestBundle(config, cachedContent) {
1320
1390
  const { projectRoot, output } = config;
@@ -1324,7 +1394,7 @@ async function buildTestBundle(config, cachedContent) {
1324
1394
  return;
1325
1395
  }
1326
1396
  const outfile = `${projectRoot}/${output}/tests.js`;
1327
- await fs8.mkdir(`${projectRoot}/${output}`, { recursive: true });
1397
+ await fs9.mkdir(`${projectRoot}/${output}`, { recursive: true });
1328
1398
  const sourcemap = config.debug ? "inline" : config.watch ? "linked" : false;
1329
1399
  const needsDisk = true;
1330
1400
  const [allTestCode] = await Promise.all([
@@ -1353,8 +1423,8 @@ async function buildTestBundle(config, cachedContent) {
1353
1423
  cachedContent.htmlPathsToRunTests.map(async (htmlPath) => {
1354
1424
  const targetPath = `${config.projectRoot}/${config.output}${htmlPath}`;
1355
1425
  if (htmlPath !== "/") {
1356
- await fs8.rm(targetPath, { force: true, recursive: true });
1357
- await fs8.mkdir(targetPath.split("/").slice(0, -1).join("/"), { recursive: true });
1426
+ await fs9.rm(targetPath, { force: true, recursive: true });
1427
+ await fs9.mkdir(targetPath.split("/").slice(0, -1).join("/"), { recursive: true });
1358
1428
  }
1359
1429
  })
1360
1430
  )
@@ -1467,7 +1537,7 @@ async function buildWithOverlayfsRetry(options, needsDisk) {
1467
1537
  }
1468
1538
  if (needsDisk) {
1469
1539
  await Promise.all(
1470
- result.outputFiles.map((outputFile) => fs8.writeFile(outputFile.path, outputFile.contents))
1540
+ result.outputFiles.map((outputFile) => fs9.writeFile(outputFile.path, outputFile.contents))
1471
1541
  );
1472
1542
  }
1473
1543
  return js;
@@ -1579,7 +1649,7 @@ var init_tests_in_browser = __esm({
1579
1649
  });
1580
1650
 
1581
1651
  // lib/setup/file-watcher.ts
1582
- import fs9 from "node:fs";
1652
+ import fs10 from "node:fs";
1583
1653
  import { stat, lstat } from "node:fs/promises";
1584
1654
  import path5 from "node:path";
1585
1655
  function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFunc) {
@@ -1592,15 +1662,15 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
1592
1662
  if (symlinkPollers.has(filePath)) return;
1593
1663
  const handler = (curr) => {
1594
1664
  if (curr.nlink === 0) {
1595
- fs9.unwatchFile(filePath, handler);
1665
+ fs10.unwatchFile(filePath, handler);
1596
1666
  symlinkPollers.delete(filePath);
1597
1667
  if (filePath in config.fsTree) {
1598
1668
  handleWatchEvent(config, extensions, "unlink", filePath, onEventFunc, onFinishFunc);
1599
1669
  }
1600
1670
  }
1601
1671
  };
1602
- fs9.watchFile(filePath, { interval: 500, persistent: false }, handler);
1603
- symlinkPollers.set(filePath, () => fs9.unwatchFile(filePath, handler));
1672
+ fs10.watchFile(filePath, { interval: 500, persistent: false }, handler);
1673
+ symlinkPollers.set(filePath, () => fs10.unwatchFile(filePath, handler));
1604
1674
  }
1605
1675
  function untrackSymlink(filePath) {
1606
1676
  symlinkPollers.get(filePath)?.();
@@ -1609,7 +1679,7 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
1609
1679
  for (const watchPath of testFileLookupPaths) {
1610
1680
  let ready = false;
1611
1681
  const lastChangeMs = {};
1612
- const childWatcher = fs9.watch(watchPath, { recursive: true }, async (eventType, filename) => {
1682
+ const childWatcher = fs10.watch(watchPath, { recursive: true }, async (eventType, filename) => {
1613
1683
  if (!ready || !filename) return;
1614
1684
  const fullPath = path5.join(watchPath, filename);
1615
1685
  if (eventType === "change") {
@@ -1639,7 +1709,7 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
1639
1709
  const parentDir = path5.dirname(watchPath);
1640
1710
  const watchedBasename = path5.basename(watchPath);
1641
1711
  let parentUnlinkFired = false;
1642
- const parentWatcher = fs9.watch(parentDir, async (eventType, filename) => {
1712
+ const parentWatcher = fs10.watch(parentDir, async (eventType, filename) => {
1643
1713
  if (!ready || filename !== watchedBasename || eventType !== "rename") return;
1644
1714
  if (parentUnlinkFired) return;
1645
1715
  parentUnlinkFired = true;
@@ -1840,12 +1910,12 @@ var init_keyboard_events = __esm({
1840
1910
  });
1841
1911
 
1842
1912
  // lib/setup/write-output-static-files.ts
1843
- import fs10 from "node:fs/promises";
1913
+ import fs11 from "node:fs/promises";
1844
1914
  async function writeOutputStaticFiles({ projectRoot, output }, cachedContent) {
1845
1915
  const staticHTMLPromises = Object.keys(cachedContent.staticHTMLs).map(async (staticHTMLKey) => {
1846
1916
  const htmlRelativePath = staticHTMLKey.replace(`${projectRoot}/`, "");
1847
1917
  await ensureFolderExists(`${projectRoot}/${output}/${htmlRelativePath}`);
1848
- await fs10.writeFile(
1918
+ await fs11.writeFile(
1849
1919
  `${projectRoot}/${output}/${htmlRelativePath}`,
1850
1920
  cachedContent.staticHTMLs[staticHTMLKey]
1851
1921
  );
@@ -1853,12 +1923,12 @@ async function writeOutputStaticFiles({ projectRoot, output }, cachedContent) {
1853
1923
  const assetPromises = Array.from(cachedContent.assets).map(async (assetAbsolutePath) => {
1854
1924
  const assetRelativePath = assetAbsolutePath.replace(`${projectRoot}/`, "");
1855
1925
  await ensureFolderExists(`${projectRoot}/${output}/${assetRelativePath}`);
1856
- await fs10.copyFile(assetAbsolutePath, `${projectRoot}/${output}/${assetRelativePath}`);
1926
+ await fs11.copyFile(assetAbsolutePath, `${projectRoot}/${output}/${assetRelativePath}`);
1857
1927
  });
1858
1928
  await Promise.all(staticHTMLPromises.concat(assetPromises));
1859
1929
  }
1860
1930
  async function ensureFolderExists(assetPath) {
1861
- await fs10.mkdir(assetPath.split("/").slice(0, -1).join("/"), { recursive: true });
1931
+ await fs11.mkdir(assetPath.split("/").slice(0, -1).join("/"), { recursive: true });
1862
1932
  }
1863
1933
  var init_write_output_static_files = __esm({
1864
1934
  "lib/setup/write-output-static-files.ts"() {
@@ -1871,7 +1941,7 @@ __export(run_exports, {
1871
1941
  default: () => run,
1872
1942
  run: () => run
1873
1943
  });
1874
- import fs11 from "node:fs/promises";
1944
+ import fs12 from "node:fs/promises";
1875
1945
  import { normalize } from "node:path";
1876
1946
  import { availableParallelism } from "node:os";
1877
1947
  async function run(config) {
@@ -2036,7 +2106,7 @@ async function run(config) {
2036
2106
  }
2037
2107
  async function buildCachedContent(config, htmlPaths) {
2038
2108
  const htmlBuffers = await Promise.all(
2039
- config.htmlPaths.map((htmlPath) => fs11.readFile(htmlPath).catch(() => null))
2109
+ config.htmlPaths.map((htmlPath) => fs12.readFile(htmlPath).catch(() => null))
2040
2110
  );
2041
2111
  const cachedContent = htmlPaths.reduce(
2042
2112
  (result, _htmlPath, index) => {
@@ -2141,7 +2211,7 @@ init_color();
2141
2211
  var package_default = {
2142
2212
  name: "qunitx-cli",
2143
2213
  type: "module",
2144
- version: "0.17.6",
2214
+ version: "0.17.7",
2145
2215
  description: "Browser runner for QUnitx: run your qunitx tests in google-chrome",
2146
2216
  author: "Izel Nakri",
2147
2217
  license: "MIT",
@@ -2251,7 +2321,7 @@ ${color("$ qunitx new $testFileName")} # Creates a qunitx test file
2251
2321
  }
2252
2322
 
2253
2323
  // lib/commands/init.ts
2254
- import fs3 from "node:fs/promises";
2324
+ import fs4 from "node:fs/promises";
2255
2325
  import path2 from "node:path";
2256
2326
 
2257
2327
  // lib/utils/find-project-root.ts
@@ -2303,7 +2373,7 @@ var defaultProjectConfigValues = {
2303
2373
  init_read_boilerplate();
2304
2374
  async function initializeProject() {
2305
2375
  const projectRoot = await findProjectRoot();
2306
- const oldPackageJSON = JSON.parse(await fs3.readFile(`${projectRoot}/package.json`));
2376
+ const oldPackageJSON = JSON.parse(await fs4.readFile(`${projectRoot}/package.json`));
2307
2377
  const existingQunitx = oldPackageJSON.qunitx || {};
2308
2378
  const cliHtmlPaths = process.argv.slice(2).filter((arg) => arg.endsWith(".html"));
2309
2379
  const config = Object.assign({}, defaultProjectConfigValues, existingQunitx, {
@@ -2332,8 +2402,8 @@ async function writeTestsHTML(projectRoot, config, oldPackageJSON) {
2332
2402
  "{{applicationName}}",
2333
2403
  oldPackageJSON.name
2334
2404
  );
2335
- await fs3.mkdir(targetDirectory, { recursive: true });
2336
- await fs3.writeFile(targetPath, testHTMLTemplate);
2405
+ await fs4.mkdir(targetDirectory, { recursive: true });
2406
+ await fs4.writeFile(targetPath, testHTMLTemplate);
2337
2407
  console.log(`${targetPath} written`);
2338
2408
  }
2339
2409
  })
@@ -2341,20 +2411,20 @@ async function writeTestsHTML(projectRoot, config, oldPackageJSON) {
2341
2411
  }
2342
2412
  async function rewritePackageJSON(projectRoot, config, oldPackageJSON) {
2343
2413
  const newPackageJSON = Object.assign(oldPackageJSON, { qunitx: config });
2344
- await fs3.writeFile(`${projectRoot}/package.json`, JSON.stringify(newPackageJSON, null, 2));
2414
+ await fs4.writeFile(`${projectRoot}/package.json`, JSON.stringify(newPackageJSON, null, 2));
2345
2415
  }
2346
2416
  async function writeTSConfigIfNeeded(projectRoot) {
2347
2417
  const targetPath = `${projectRoot}/tsconfig.json`;
2348
2418
  if (!await pathExists(targetPath)) {
2349
2419
  const tsConfigTemplate = await readBoilerplate("setup/tsconfig.json");
2350
- await fs3.writeFile(targetPath, tsConfigTemplate);
2420
+ await fs4.writeFile(targetPath, tsConfigTemplate);
2351
2421
  console.log(`${targetPath} written`);
2352
2422
  }
2353
2423
  }
2354
2424
 
2355
2425
  // lib/commands/generate.ts
2356
2426
  init_color();
2357
- import fs4 from "node:fs/promises";
2427
+ import fs5 from "node:fs/promises";
2358
2428
  init_path_exists();
2359
2429
  init_read_boilerplate();
2360
2430
 
@@ -2381,22 +2451,22 @@ async function generateTestFiles() {
2381
2451
  const testJSContent = await readBoilerplate("test.js");
2382
2452
  const targetFolderPaths = path6.split("/");
2383
2453
  targetFolderPaths.pop();
2384
- await fs4.mkdir(targetFolderPaths.join("/"), { recursive: true });
2385
- await fs4.writeFile(path6, testJSContent.replace("{{moduleName}}", moduleName));
2454
+ await fs5.mkdir(targetFolderPaths.join("/"), { recursive: true });
2455
+ await fs5.writeFile(path6, testJSContent.replace("{{moduleName}}", moduleName));
2386
2456
  console.log(green(`${path6} written`));
2387
2457
  }
2388
2458
 
2389
2459
  // lib/setup/config.ts
2390
- import fs6 from "node:fs/promises";
2460
+ import fs7 from "node:fs/promises";
2391
2461
 
2392
2462
  // lib/setup/fs-tree.ts
2393
- import fs5, { glob as fsGlob } from "node:fs/promises";
2463
+ import fs6, { glob as fsGlob } from "node:fs/promises";
2394
2464
  import path3 from "node:path";
2395
2465
  function isGlob(str) {
2396
2466
  return /[*?{[]/.test(str);
2397
2467
  }
2398
2468
  async function readDirRecursive(dir, filter) {
2399
- const entries = await fs5.readdir(dir, { recursive: true, withFileTypes: true });
2469
+ const entries = await fs6.readdir(dir, { recursive: true, withFileTypes: true });
2400
2470
  const candidates = entries.filter(
2401
2471
  (dirent) => (dirent.isFile() || dirent.isSymbolicLink()) && filter(dirent.name)
2402
2472
  );
@@ -2405,7 +2475,7 @@ async function readDirRecursive(dir, filter) {
2405
2475
  const fullPath = path3.join(dirent.parentPath, dirent.name);
2406
2476
  if (dirent.isFile()) return fullPath;
2407
2477
  try {
2408
- const statResult = await fs5.stat(fullPath);
2478
+ const statResult = await fs6.stat(fullPath);
2409
2479
  return statResult.isFile() ? fullPath : null;
2410
2480
  } catch {
2411
2481
  return null;
@@ -2427,7 +2497,7 @@ async function buildFSTree(fileAbsolutePaths, config = {}) {
2427
2497
  }
2428
2498
  }
2429
2499
  } else {
2430
- const entry = await fs5.stat(fileAbsolutePath);
2500
+ const entry = await fs6.stat(fileAbsolutePath);
2431
2501
  if (entry.isFile()) {
2432
2502
  fsTree[fileAbsolutePath] = null;
2433
2503
  } else if (entry.isDirectory()) {
@@ -2600,7 +2670,7 @@ async function setupConfig() {
2600
2670
  return config;
2601
2671
  }
2602
2672
  async function readConfigFromPackageJSON(projectRoot) {
2603
- const packageJSON = await fs6.readFile(`${projectRoot}/package.json`);
2673
+ const packageJSON = await fs7.readFile(`${projectRoot}/package.json`);
2604
2674
  return JSON.parse(packageJSON.toString());
2605
2675
  }
2606
2676
  function normalizeHTMLPaths(projectRoot, htmlPaths) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qunitx-cli",
3
3
  "type": "module",
4
- "version": "0.17.6",
4
+ "version": "0.17.7",
5
5
  "description": "Browser runner for QUnitx: run your qunitx tests in google-chrome",
6
6
  "author": "Izel Nakri",
7
7
  "license": "MIT",