Chuyển đến nội dung chính

Bài 3: Module System & Package Management

CommonJS vs ES Modules, module resolution algorithm. Package.json exports field, conditional exports. npm, pnpm workspace. Node.js built-in test runner, permission model.

💻 Lập trình — Bài 3 Bài 3: Module System & Package Management

Node.js Core: Từ Cơ bản đến Nâng cao

Phần 1: Node.js Internals

xdev.asia

1. CommonJS vs ES Modules

FeatureCommonJS (CJS)ES Modules (ESM)
Syntaxrequire() / module.exportsimport / export
LoadingSynchronousAsynchronous
Top-level awaitKhôngCó
__dirnameCóimport.meta.dirname (Node 21+)
JSON importrequire('./data.json')import data from './data.json' with { type: 'json' }
Tree-shakingKhôngCó (static analysis)
// ESM — recommended
import { readFile } from 'node:fs/promises'
import path from 'node:path'

// Dynamic import
const module = await import('./utils.js')

// import.meta
console.log(import.meta.url)      // file:///path/to/file.js
console.log(import.meta.dirname)  // /path/to (Node.js 21+)
console.log(import.meta.filename) // /path/to/file.js

2. Package.json exports Field

{
  "name": "my-lib",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    },
    "./utils": {
      "import": "./dist/utils.mjs",
      "require": "./dist/utils.cjs"
    }
  },
  "files": ["dist"],
  "engines": {
    "node": ">=22"
  }
}

3. pnpm Workspace

# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'
# Thêm dependency cho workspace
pnpm add lodash --filter @myorg/server
pnpm add @myorg/shared --filter @myorg/server --workspace

# Chạy script cho tất cả packages
pnpm -r run build
pnpm -r --parallel run dev

4. Permission Model (Node.js 22+)

# Chạy với permission giới hạn
node --experimental-permission \
  --allow-fs-read=/app/data \
  --allow-fs-write=/app/logs \
  --allow-child-process \
  app.js

# Kiểm tra permission trong code
if (process.permission.has('fs.read', '/etc/passwd')) {
  console.log('Has read permission')
}

5. Built-in Test Runner

import { describe, it, before, after, mock } from 'node:test'
import assert from 'node:assert/strict'

describe('Calculator', () => {
  it('adds two numbers', () => {
    assert.strictEqual(1 + 2, 3)
  })

  it('handles async', async () => {
    const result = await Promise.resolve(42)
    assert.strictEqual(result, 42)
  })

  it('supports mocking', () => {
    const fn = mock.fn((a: number, b: number) => a + b)
    fn(1, 2)
    assert.strictEqual(fn.mock.calls.length, 1)
    assert.deepStrictEqual(fn.mock.calls[0].arguments, [1, 2])
  })
})
node --test --test-reporter spec

Bài tiếp theo: TypeScript & Modern JavaScript — tsx, ts-node, type stripping.