• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hexojs / hexo-util / 16635873005

30 Jul 2025 11:21PM UTC coverage: 83.147% (-13.7%) from 96.875%
16635873005

Pull #426

github

web-flow
Merge 09cb5b4f8 into d497bc760
Pull Request #426: build: support both ESM and CommonJS

676 of 866 branches covered (78.06%)

1473 of 1845 new or added lines in 38 files covered. (79.84%)

6 existing lines in 1 file now uncovered.

4677 of 5625 relevant lines covered (83.15%)

89.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.22
/scripts/pretest.mjs
1
import { spawnSync } from 'child_process';
1✔
2
import fs from 'fs';
1✔
3
import path from 'path';
1✔
4
import { fileURLToPath } from 'url';
1✔
5

1✔
6
const __filename = fileURLToPath(import.meta.url);
1✔
7
const __dirname = path.dirname(__filename);
1✔
8
const libDir = path.resolve(__dirname, '../lib');
1✔
9
const markerFile = path.resolve(__dirname, '../.last_build');
1✔
10
const extraFiles = [
1✔
11
  path.resolve(__dirname, '../test/utils.cjs'),
1✔
12
  path.resolve(__dirname, '../test/dual_mode.spec.ts'),
1✔
13
  __filename
1✔
14
];
1✔
15

1✔
NEW
16
function getAllFiles(dir) {
×
NEW
17
  let results = [];
×
NEW
18
  const list = fs.readdirSync(dir);
×
NEW
19
  list.forEach(file => {
×
NEW
20
    const filePath = path.join(dir, file);
×
NEW
21
    const stat = fs.statSync(filePath);
×
NEW
22
    if (stat && stat.isDirectory()) {
×
NEW
23
      results = results.concat(getAllFiles(filePath));
×
NEW
24
    } else {
×
NEW
25
      results.push(filePath);
×
NEW
26
    }
×
NEW
27
  });
×
NEW
28
  return results;
×
NEW
29
}
×
30

1✔
31
function needsBuild() {
1✔
32
  if (!fs.existsSync(markerFile)) return true;
1✔
NEW
33
  const markerMtime = fs.statSync(markerFile).mtimeMs;
×
NEW
34
  const files = getAllFiles(libDir).concat(extraFiles);
×
NEW
35
  return files.some(f => fs.existsSync(f) && fs.statSync(f).mtimeMs > markerMtime);
×
36
}
1✔
37

1✔
38
function isTestEnv() {
1✔
39
  return (
1✔
40
    process.env.MOCHA || process.argv.some(arg => arg.includes('mocha')) || process.env.JEST_WORKER_ID !== undefined
1✔
41
  );
1✔
42
}
1✔
43

1✔
44
// If the marker file is older than the source files, rebuild
1✔
45
if (needsBuild() && !isTestEnv()) {
1✔
46
  console.log('Building hexo-util...');
1✔
47
  const result = spawnSync('npm', ['run', 'build'], {
1✔
48
    stdio: 'inherit',
1✔
49
    cwd: process.cwd(),
1✔
50
    shell: true
1✔
51
  });
1✔
52
  if (result.status !== 0) {
1!
NEW
53
    throw new Error(`Build failed with exit code ${result.status || 1}`);
×
NEW
54
  }
×
55
  fs.writeFileSync(markerFile, String(Date.now()));
1✔
56
}
1✔
57

1✔
58
// Convert extension to .cjs for CommonJS files
1✔
59

1✔
60
/**
1✔
61
 * Recursively updates all import/require extensions from .js to .cjs, then copies .js files to .cjs in the specified directory and its subdirectories.
1✔
62
 * > while running CJS under ESM environment, we need to ensure that all CommonJS files use .cjs extension to avoid conflicts.
1✔
63
 * @param {string} [dir] - The directory to start from. Defaults to '../dist/cjs' relative to this file.
1✔
64
 */
1✔
65
function convertCjs(dir) {
1✔
66
  if (!dir) {
1✔
67
    dir = path.join(__dirname, '../dist/cjs');
1✔
68
  }
1✔
69
  if (!fs.existsSync(dir)) return;
1!
70
  fs.readdirSync(dir).forEach(file => {
1✔
71
    const filePath = path.join(dir, file);
108✔
72
    const stat = fs.statSync(filePath);
108✔
73
    if (stat.isDirectory()) {
108!
NEW
74
      convertCjs(filePath);
×
75
    } else if (file.endsWith('.js')) {
108✔
76
      // Update import/require extensions in the file
36✔
77
      let content = fs.readFileSync(filePath, 'utf8');
36✔
78
      // Replace only local require/import paths ending with .js to .cjs, but not directory imports (e.g. './dir')
36✔
79
      // require('./foo.js') or require('../foo.js'), but NOT require('./dir')
36✔
80
      content = content.replace(/(require\(["'`].*?)([^/]+)\.js(["'`]\))/g, (match, p1, filename, p3) => {
36✔
81
        // Only replace if path is local and not a directory import
56✔
82
        return /require\(["'`](\.|\.\.)\//.test(match) ? p1 + filename + '.cjs' + p3 : match;
56✔
83
      });
36✔
84
      // import ... from './foo.js' or '../foo.js', but NOT from './dir'
36✔
85
      content = content.replace(/(from\s+["'`].*?)([^/]+)\.js(["'`])/g, (match, p1, filename, p3) => {
36✔
NEW
86
        return /from\s+["'`](\.|\.\.)\//.test(match) ? p1 + filename + '.cjs' + p3 : match;
×
87
      });
36✔
88
      // dynamic import('./foo.js') or import('../foo.js'), but NOT import('./dir')
36✔
89
      content = content.replace(/(import\s*\(["'`].*?)([^/]+)\.js(["'`]\))/g, (match, p1, filename, p3) => {
36✔
NEW
90
        return /import\s*\(["'`](\.|\.\.)\//.test(match) ? p1 + filename + '.cjs' + p3 : match;
×
91
      });
36✔
92
      fs.writeFileSync(filePath, content, 'utf8');
36✔
93
      // Copy the file with .cjs extension instead of renaming
36✔
94
      const newPath = filePath.replace(/\.js$/, '.cjs');
36✔
95
      fs.copyFileSync(filePath, newPath);
36✔
96
      // console.log(`Updated imports and copied: ${filePath} -> ${newPath}`);
36✔
97
    }
36✔
98
  });
1✔
99
}
1✔
100

1✔
101
convertCjs(); // Ensure CJS files are renamed before running tests
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc