Skip to content

Commit

Permalink
feat: improve module formats (cjs, esm, iife) (#1247)
Browse files Browse the repository at this point in the history
* chore: adopt "tsup"

* chore: copy worker with tsup

* chore: bundle iife correctly, fix call frames

* chore: inject "setTimeout" for node build

* chore: log worker checksum

* chore(postinstall): use early return over process.exit
  • Loading branch information
kettanaito committed May 20, 2022
1 parent 77d1612 commit 00e9c58
Show file tree
Hide file tree
Showing 25 changed files with 515 additions and 621 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
@@ -1 +1,4 @@
lib
node
native
test/typings
4 changes: 0 additions & 4 deletions .gitignore
Expand Up @@ -2,10 +2,6 @@ __*
.DS_*
node_modules
/lib
/native/**/*
/node/**/*
!/node/package.json
!/native/package.json
tmp
*-error.log
./package-lock.json
Expand Down
2 changes: 0 additions & 2 deletions CONTRIBUTING.md
Expand Up @@ -244,8 +244,6 @@ Build the library with the following command:
$ yarn build
```

> Learn more about the build in the [Rollup configuration file](../rollup.config.ts).
[yarn-url]: https://classic.yarnpkg.com/en/
[jest-url]: https://jestjs.io
[page-with-url]: https://github.com/kettanaito/page-with
4 changes: 1 addition & 3 deletions config/constants.js
@@ -1,5 +1,4 @@
const path = require('path')
const packageJson = require('../package.json')

const SERVICE_WORKER_SOURCE_PATH = path.resolve(
__dirname,
Expand All @@ -9,8 +8,7 @@ const SERVICE_WORKER_SOURCE_PATH = path.resolve(

const SERVICE_WORKER_BUILD_PATH = path.resolve(
__dirname,
'../',
path.dirname(packageJson.module),
'../lib',
path.basename(SERVICE_WORKER_SOURCE_PATH),
)

Expand Down
11 changes: 9 additions & 2 deletions config/copyServiceWorker.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs'
import * as path from 'path'
import * as chalk from 'chalk'
import chalk from 'chalk'
import { until } from '@open-draft/until'

/**
Expand Down Expand Up @@ -30,7 +30,14 @@ export default async function copyServiceWorker(
await fs.promises.mkdir(destFileDirectory, { recursive: true })
}

const nextFileContent = fileContent.replace('<INTEGRITY_CHECKSUM>', checksum)
const packageJson = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf8'),
)

const nextFileContent = fileContent
.replace('<INTEGRITY_CHECKSUM>', checksum)
.replace('<PACKAGE_VERSION>', packageJson.version)

const [writeFileError] = await until(() =>
fs.promises.writeFile(destFilePath, nextFileContent),
)
Expand Down
82 changes: 82 additions & 0 deletions config/plugins/esbuild/workerScriptPlugin.ts
@@ -0,0 +1,82 @@
import path from 'path'
import fs from 'fs-extra'
import crypto from 'crypto'
import minify from 'babel-minify'
import { invariant } from 'outvariant'
import type { Plugin } from 'esbuild'
import copyServiceWorker from '../../copyServiceWorker'

function getChecksum(contents: string): string {
const { code } = minify(
contents,
{},
{
// @ts-ignore "babel-minify" has no type definitions.
comments: false,
},
)

return crypto.createHash('md5').update(code, 'utf8').digest('hex')
}

let hasRunAlready = false

export function workerScriptPlugin(): Plugin {
return {
name: 'workerScriptPlugin',
async setup(build) {
const workerSourcePath = path.resolve(
process.cwd(),
'./src/mockServiceWorker.js',
)
const workerOutputPath = path.resolve(
process.cwd(),
'./lib/mockServiceWorker.js',
)

invariant(
workerSourcePath,
'Failed to locate the worker script source file',
)
invariant(
workerOutputPath,
'Failed to locate the worker script output file',
)

// Generate the checksum from the worker script's contents.
const workerContents = await fs.readFile(workerSourcePath, 'utf8')
const checksum = getChecksum(workerContents)

// Inject the global "SERVICE_WORKER_CHECKSUM" variable
// for runtime worker integrity check.
build.initialOptions.define = {
SERVICE_WORKER_CHECKSUM: JSON.stringify(checksum),
}

// Prevent from copying the worker script multiple times.
// esbuild will execute this plugin for *each* format.
if (hasRunAlready) {
return
}

hasRunAlready = true

build.onLoad({ filter: /mockServiceWorker\.js$/ }, async () => {
return {
// Prevent the worker script from being transpiled.
// But, generally, the worker script is not in the entrypoints.
contents: '',
}
})

build.onEnd(() => {
console.log('worker script checksum:', checksum)

// Copy the worker script on the next tick.
setTimeout(async () => {
await copyServiceWorker(workerSourcePath, workerOutputPath, checksum)
}, 100)
})
},
}
}
24 changes: 0 additions & 24 deletions config/plugins/rollup-integrity-check-plugin/getChecksum.js

This file was deleted.

49 changes: 0 additions & 49 deletions config/plugins/rollup-integrity-check-plugin/index.js

This file was deleted.

8 changes: 8 additions & 0 deletions config/polyfills-node.ts
@@ -0,0 +1,8 @@
import { setTimeout as nodeSetTimeout } from 'timers'

// Polyfill the global "setTimeout" so MSW could be used
// with "jest.useFakeTimers()". MSW response handling
// is wrapped in "setTimeout", and without this polyfill
// you'd have to manually advance the timers for the response
// to finally resolve.
export const setTimeout = nodeSetTimeout
57 changes: 32 additions & 25 deletions config/scripts/postinstall.js
Expand Up @@ -7,35 +7,42 @@ const { execSync } = require('child_process')
// NPM stores the parent project directory in the "INIT_CWD" env variable.
const parentPackageCwd = process.env.INIT_CWD

// 1. Check if "package.json" has "msw.workerDirectory" property set.
const packageJson = JSON.parse(
fs.readFileSync(path.resolve(parentPackageCwd, 'package.json'), 'utf8'),
)

if (!packageJson.msw || !packageJson.msw.workerDirectory) {
return
}
function postinstall() {
// 1. Check if "package.json" has "msw.workerDirectory" property set.
const packageJson = JSON.parse(
fs.readFileSync(path.resolve(parentPackageCwd, 'package.json'), 'utf8'),
)

// 2. Check if the worker directory is an existing path.
const { workerDirectory } = packageJson.msw
const absoluteWorkerDirectory = path.resolve(parentPackageCwd, workerDirectory)
if (!packageJson.msw || !packageJson.msw.workerDirectory) {
return
}

if (!fs.existsSync(absoluteWorkerDirectory)) {
return console.error(
`[MSW] Failed to automatically update the worker script at "%s": given path does not exist.`,
// 2. Check if the worker directory is an existing path.
const { workerDirectory } = packageJson.msw
const absoluteWorkerDirectory = path.resolve(
parentPackageCwd,
workerDirectory,
)
}

// 3. Update the worker script.
const cliExecutable = path.resolve(process.cwd(), 'cli/index.js')
if (!fs.existsSync(absoluteWorkerDirectory)) {
return console.error(
`[MSW] Failed to automatically update the worker script at "%s": given path does not exist.`,
workerDirectory,
)
}

try {
execSync(`node ${cliExecutable} init ${absoluteWorkerDirectory}`, {
cwd: parentPackageCwd,
})
} catch (error) {
console.error(
`[MSW] Failed to automatically update the worker script:\n${error}`,
)
// 3. Update the worker script.
const cliExecutable = path.resolve(process.cwd(), 'cli/index.js')

try {
execSync(`node ${cliExecutable} init ${absoluteWorkerDirectory}`, {
cwd: parentPackageCwd,
})
} catch (error) {
console.error(
`[MSW] Failed to automatically update the worker script:\n${error}`,
)
}
}

postinstall()
5 changes: 3 additions & 2 deletions native/package.json
@@ -1,4 +1,5 @@
{
"main": "./lib/index.js",
"types": "../lib/types/native"
"main": "../lib/native/index.js",
"module": "../lib/native/index.mjs",
"typings": "../lib/native/index.d.ts"
}
5 changes: 3 additions & 2 deletions node/package.json
@@ -1,4 +1,5 @@
{
"main": "./lib/index.js",
"types": "../lib/types/node"
"main": "../lib/node/index.js",
"module": "../lib/node/index.mjs",
"typings": "../lib/node/index.d.ts"
}
29 changes: 10 additions & 19 deletions package.json
Expand Up @@ -2,27 +2,25 @@
"name": "msw",
"version": "0.40.2",
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
"main": "lib/umd/index.js",
"module": "lib/esm/index.js",
"types": "lib/types/index.d.ts",
"main": "./lib/index.js",
"module": "./lib/esm/index.js",
"types": "./lib/index.d.ts",
"bin": {
"msw": "cli/index.js"
},
"engines": {
"node": ">=14"
},
"scripts": {
"start": "cross-env NODE_ENV=development rollup -c rollup.config.ts -w",
"clean": "rimraf lib {native,node}/lib",
"start": "tsup --watch",
"clean": "rimraf ./lib",
"lint": "eslint \"{cli,config,src,test}/**/*.ts\"",
"prebuild": "yarn clean && yarn lint",
"build": "cross-env NODE_ENV=production rollup -c rollup.config.ts",
"postbuild": "yarn test:ts",
"build": "cross-env NODE_ENV=production tsup",
"test": "yarn test:unit && yarn test:integration",
"test:unit": "cross-env BABEL_ENV=test jest --maxWorkers=3",
"test:integration": "jest --config=test/jest.config.js --maxWorkers=1",
"test:smoke": "config/scripts/smoke.sh",
"test:ts": "yarn tsc -p test/typings/tsconfig.json",
"test:integration": "jest --config=./test/jest.config.js --maxWorkers=1",
"test:smoke": "./config/scripts/smoke.sh",
"test:ts": "yarn tsc -p ./test/typings/tsconfig.json",
"prepare": "yarn simple-git-hooks init",
"prepack": "yarn build",
"release": "release publish",
Expand Down Expand Up @@ -96,11 +94,6 @@
"@commitlint/config-conventional": "^16.0.0",
"@open-draft/test-server": "^0.2.3",
"@ossjs/release": "^0.3.0",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-inject": "^4.0.4",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-replace": "^3.1.0",
"@types/fs-extra": "^9.0.13",
"@types/jest": "26",
"@types/json-bigint": "^1.0.1",
Expand Down Expand Up @@ -128,13 +121,11 @@
"prettier": "^2.3.2",
"regenerator-runtime": "^0.13.9",
"rimraf": "^3.0.2",
"rollup": "^2.67.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.2",
"simple-git-hooks": "^2.7.0",
"ts-jest": "26",
"ts-loader": "^9.2.6",
"ts-node": "^10.1.0",
"tsup": "^5.12.8",
"typescript": "^4.6.4",
"url-loader": "^4.1.1",
"webpack": "^5.68.0",
Expand Down

0 comments on commit 00e9c58

Please sign in to comment.