# node 内置模块常用API

# 1. path

# 1.1. 获取路径的最后一节

path.basename('/foo/bar/baz/asdf/quux.html');
// Returns: 'quux.html'

path.basename('/foo/bar/baz/asdf/quux.html', '.html');
// Returns: 'quux'

# 1.2. 路径分隔符

path.sep

# 2. fs

# 2.1. 重命名

fs.renameSync(oldPath, newPath)

  • oldPath: String | Buffer | URL
  • newPath: String | Buffer | URL

Renames the file from oldPath to newPath. Returns undefined.

# 2.2. 删除文件或目录

fs.rmSync(path[, options])

  • path: String | Buffer | URL
  • options: Object
    • recursive: boolean. If true, perform a recursive directory removal. In recursive mode, operations are retried on failure. Default: false. Deprecated.

Synchronously removes files and directories. Returns undefined.

# 2.3. 拷贝文件

fs.copyFileSync(src, dest[, mode])

  • src: string.
  • dest: string.

Synchronously copies src to dest. By default, dest is overwritten if it already exists. Returns undefined.

# 2.4. 读取目录下的文件(夹)

fs.readdirSync(path[, options])

Reads the contents of the directory.

fs.readdirSync(dirPath);
/*
[
    'dir1',
    'dir2',
    '1.txt',
    '2.txt'
]
*/

# 2.5. 判断是否为目录

fs.lstatSync(path_string).isDirectory() 

// Objects returned from fs.stat() and fs.lstat() are of this type.

stats.isFile()
stats.isDirectory()
stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink() // (only valid with fs.lstat())
stats.isFIFO()
stats.isSocket()

# 2.6. 判断是否存在(文件/目录)

说明:

/*
Returns true if the path exists, false otherwise.

fs.exists() is deprecated, but fs.existsSync() is not. 
The callback parameter to fs.exists() accepts parameters that are inconsistent with other Node.js callbacks. 
fs.existsSync() does not use a callback.
 */
export function existsSync(path: PathLike): boolean

示例:

import { existsSync } from 'node:fs';  

if (existsSync('/ etc/ passwd')) {
  console. log('The path exists.');
}

# 2.7. 创建目录

// fs.mkdirSync(path[, options])

// 创建单层目录
fs.mkdirSync('/a');

// 创建多层目录
fs.mkdirSync('a/b/c', { recursive: true });

# 2.8. 拷贝目录(包含文件)

安装:

npm install fs-extra

使用:

const fse = require('fs-extra')

// 将 d:/proj/dist 目录里的内容拷贝到 d:/nginx/www
fs.copySync('d:/proj/dist', 'd:/nginx/www', { recursive: true })

参考:

  • https://www.npmjs.com/package/fs-extra

# 2.9. 递归遍历目录中所有文件,并拷贝到其它目录

const path = require('path');
const fs = require('fs');

// 遍历
const traverse = (dir, callback) => {
  const filenameList = fs.readdirSync(dir);

  for (let i = 0; i < filenameList.length; i++) {
    const filename = filenameList[i];

    const filePath = path.join(dir, filename);

    const isDir = fs.lstatSync(filePath).isDirectory();
    
    callback({ filePath, isDir });

    if (isDir) {
      const subDirPath = path.join(dir, filename);
      traverse(subDirPath, callback);
    }
  }
};

// 拷贝:src/** -> dest/**
const srcRootPath = PROJ_DIST_PATH;
const destRootPath = WWW_PATH;

const copyFile = (filePath) => {
  const srcFilePath = filePath;
  const destFilePath = filePath.replace(srcRootPath, destRootPath);

  fs.copyFileSync(srcFilePath, destFilePath);
};

const copyDir = (dir) => {
  fs.mkdirSync(dir, { recursive: true });
};

traverse(srcRootPath, ({ filePath, isDir}) => {
  if (isDir) {
    copyDir(filePath);
    return;
  }

  copyFile(filePath);
});

# 2.10. 读取文件内容

fs.readFileSync(absPath).toString()

# 3. os

# 3.1. 获取IPv4

const os = require('os');

const v4Ips = [];
const networkInterfaces = os.networkInterfaces();

Reflect.ownKeys(networkInterfaces).forEach((netName) => {
  const networkInterfaceInfoList = networkInterfaces[netName];

  networkInterfaceInfoList.forEach((networkInterfaceInfo) => {
    const { family, internal, address } = networkInterfaceInfo;

    if (family === 'IPv4' && !internal) {
      v4Ips.push(address);
    }
  });
});

# 3.2. 获取 Home 目录

os.homedir();// C:\Users\wuqinfei

# 4. child_process

每一条指令是在不同的进程中执行的,exec('cd ../../') 是不能切换工作目录的

# 4.1. 执行脚本命令

const { exec } = require("child_process");

// options.cwd : 指定工作目录
function execCommand(command, options = {}) {
  console.log(`start executing command: ${command}`);

  return new Promise((resolve, reject) => {
    exec(command, options, (error, stdout, stderr) => {
        if (error) {
          console.error(`~~~~~~~~~~~~ [ ${command} ] error start ~~~~~~~~~~~~`);
          console.error(error.message);
          console.error(`~~~~~~~~~~~~ [ ${command} ] error end ~~~~~~~~~~~~~~`);
          reject(error);
          return;
        }

        if (stderr) {
          console.warn(`~~~~~~~~~~~~ [ ${command} ] stderr start ~~~~~~~~~~~~`);
          console.warn(stderr);
          console.warn(`~~~~~~~~~~~~ [ ${command} ] stderr end ~~~~~~~~~~~~~~`);
        }
        
        if (stdout) {
          console.log(`~~~~~~~~~~~~ [ ${command} ] stdout start ~~~~~~~~~~~~`);
          console.log(stdout);
          console.log(`~~~~~~~~~~~~ [ ${command} ] stdout end ~~~~~~~~~~~~~~`);
        }

        resolve(stdout);
      }, 
    );
  });
}

async function run() {
  await execCommand('git pull', { cwd: 'xxx' });
  await execCommand('npm run build', { cwd: 'xxx' });
}

# 5. process

# 5.1. 获取用户 home 目录

/*
  mac、linux:
    process.env.HOME
  windows:
    process.env.USERPROFILE
 */
const USER_HOME = process.env.HOME || process.env.USERPROFILE; // C:\Users\wuqinfei

# 5.2. 获取 nodejs 的版本

安装:

npm install semver

示例:

const semver = require('semver');
 
const version = process.version; // 获取版本号,例如 'v14.17.0'
console.log(semver.clean(version)); // 清理版本号,例如 '14.17.0'
 
// 比较版本号
const majorVersion = semver.major(version); // 获取主版本号,例如 14
console.log(`Major version: ${majorVersion}`);
 
if (semver.gte(version, '12.0.0')) { // 检查版本是否大于等于 12.0.0
    console.log('Node version is >= 12.0.0');
} else {
    console.log('Node version is < 12.0.0');
}

# 5.3. 接收命令行参数

// D:\> node scripts.js arg1 arg2 arg3

const args = process.argv;

console.log(args);
/* =>
[
  'C:\\nvm4w\\nodejs\\node.exe',  // node 命令的路径
  'D:\\scripts.js',               // 当前脚本的路径
  'arg1',
  'arg2',
  'arg3'
]
*/

// 从第三个(索引为2)的元素开始
const [ arg1, arg2, arg3 ] = args.slice(2);

# 6. 压缩、解压缩

依赖:

npm i archiver
npm i -D @types/archiver

# "archiver": "^7.0.1",
# "@types/archiver": "^7.0.0",


npm i decompress
npm i -D @types/decompress

# "decompress": "^4.2.1",
# "@types/decompress": "^4.2.7",

示例:

import fs from 'fs';
import archiver from 'archiver';
import decompress from 'decompress';

export class ZipUtil {
  /**
   * 压缩文件夹 src ,zip文件的根路径不包含 src
   * @param folderPath 文件夹全路径
   * @param zipFilePath zip文件全路径
   * @example
   * compressFolder(`D:/folder`, `D:/1.zip`): 将 `D:/folder` 压缩到 `D:/1.zip`
   */
  static async compress(folderPath: string, zipFilePath: string) {
    // 创建文件写入流
    const output = fs.createWriteStream(zipFilePath);
    const archive = archiver('zip', {
      zlib: { level: 9 }, // 设置压缩级别
    });

    // 监听错误事件
    archive.on('error', (err) => {
      throw err;
    });

    // 监听完成事件
    archive.on('end', () => {
      console.log('归档完成。');
    });

    // 通过管道将输出流传递给archiver,并开始归档过程
    archive.pipe(output);
    archive.directory(folderPath, false); // 添加目录但不包含根目录名(第二个参数为false)

    await archive.finalize(); // 完成归档过程
  }

  /**
   * @example
   * decompressZipFile(`D:/1.zip`, `D:/folder`): 将 `D:/1.zip` 解压到 `D:/folder`
   */
  static async decompress(zipFilePath: string, outputFolderPath: string) {
    try {
      fs.mkdirSync(outputFolderPath, { recursive: true }); // 确保目录存在

      // 如下的代码有问题,会导致 zip 文件一直被占用,需要等进程关闭才会解除占用
      // await fs.createReadStream(zipFilePath, { autoClose: true })
      //   .pipe(unzipper.Extract({ path: outputFolderPath }))
      //   .promise(); // 使用promise()方法等待解压完成

      await decompress(zipFilePath, outputFolderPath);

      console.log('解压完成');
      return true;
    } catch (err) {
      console.error('解压失败:', err);
      return false;
    }
  }
}
本章目录