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

hexojs / hexo-util / 16650178400

31 Jul 2025 01:21PM UTC coverage: 73.042% (-23.8%) from 96.875%
16650178400

Pull #426

github

web-flow
Merge ca6058b70 into d497bc760
Pull Request #426: build: restructure for dual ESM/CJS output, update tooling, and enhance utilities

811 of 1128 branches covered (71.9%)

1593 of 2027 new or added lines in 40 files covered. (78.59%)

6 existing lines in 1 file now uncovered.

5289 of 7241 relevant lines covered (73.04%)

69.15 hits per line

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

53.33
/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 distDir = path.resolve(__dirname, '../dist');
1✔
11
const extraFiles = [
1✔
12
  path.resolve(__dirname, '../test/utils.cjs'),
1✔
13
  path.resolve(__dirname, '../build.js'),
1✔
14
  path.resolve(__dirname, '../package.json'),
1✔
15
  path.resolve(__dirname, '../test/dual_mode.spec.ts'),
1✔
16
  __filename
1✔
17
];
1✔
18

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

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

1✔
42
function isTestEnv() {
1✔
43
  return (
1✔
44
    process.env.MOCHA || process.argv.some(arg => arg.includes('mocha')) || process.env.JEST_WORKER_ID !== undefined
1✔
45
  );
1✔
46
}
1✔
47

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

1✔
62
// Convert extension to .cjs for CommonJS files
1✔
63

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

1✔
105
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