From 3ad1df5184e329d803a584704a3c9b3b01330824 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Mon, 10 Apr 2023 11:24:08 -0400 Subject: [PATCH] Use tsup to build and modernize build artifacts --- package.json | 14 +++---- src/core/finalize.ts | 3 +- src/core/proxy.ts | 10 ++++- src/plugins/patches.ts | 2 +- src/utils/errors.ts | 75 ++++++++++++++++++----------------- tsup.config.ts | 90 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 50 deletions(-) create mode 100644 tsup.config.ts diff --git a/package.json b/package.json index 28c156fc..3e81974c 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,15 @@ "name": "immer", "version": "10.0.0-beta.6", "description": "Create your next immutable state by mutating the current one", - "main": "dist/index.js", - "module": "dist/immer.mjs", + "main": "./dist/cjs/index.js", + "module": "./dist/immer.legacy-esm.js", "exports": { ".": { "types": "./dist/immer.d.ts", "import": "./dist/immer.mjs", - "require": "./dist/index.js" + "require": "./dist/cjs/index.js" } }, - "umd:main": "dist/immer.umd.production.min.js", - "unpkg": "dist/immer.umd.production.min.js", - "jsdelivr": "dist/immer.umd.production.min.js", "jsnext:main": "dist/immer.mjs", "react-native": "dist/immer.mjs", "source": "src/immer.ts", @@ -27,12 +24,11 @@ "watch": "jest --watch", "coverage": "jest --coverage", "coveralls": "jest --coverage && cat ./coverage/lcov.info | ./node_modules/.bin/coveralls && rm -rf ./coverage", - "build": "rimraf dist/ && tsdx build --name immer --format esm,cjs,umd && mv dist/immer.esm.js dist/immer.mjs && yarn build:flow", - "build:flow": "cpx 'src/types/index.js.flow' dist -v", + "build": "tsup", "publish-docs": "cd website && GIT_USER=mweststrate USE_SSH=true yarn docusaurus deploy", "start": "cd website && yarn start", "test:size": "yarn build && yarn import-size --report . produce enableMapSet enablePatches", - "test:sizequick": "tsdx build --name immer --format esm && yarn import-size . produce" + "test:sizequick": "yarn build && yarn import-size . produce" }, "husky": { "hooks": { diff --git a/src/core/finalize.ts b/src/core/finalize.ts index a104a336..c205a467 100644 --- a/src/core/finalize.ts +++ b/src/core/finalize.ts @@ -116,7 +116,8 @@ function finalizeProperty( rootPath?: PatchPath, targetIsSet?: boolean ) { - if (__DEV__ && childValue === targetObject) die(5) + if (process.env.NODE_ENV === "development" && childValue === targetObject) + die(5) if (isDraft(childValue)) { const path = rootPath && diff --git a/src/core/proxy.ts b/src/core/proxy.ts index 4776b10c..00761cfd 100644 --- a/src/core/proxy.ts +++ b/src/core/proxy.ts @@ -220,12 +220,18 @@ each(objectTraps, (key, fn) => { } }) arrayTraps.deleteProperty = function(state, prop) { - if (__DEV__ && isNaN(parseInt(prop as any))) die(13) + if (process.env.NODE_ENV === "development" && isNaN(parseInt(prop as any))) + die(13) // @ts-ignore return arrayTraps.set!.call(this, state, prop, undefined) } arrayTraps.set = function(state, prop, value) { - if (__DEV__ && prop !== "length" && isNaN(parseInt(prop as any))) die(14) + if ( + process.env.NODE_ENV === "development" && + prop !== "length" && + isNaN(parseInt(prop as any)) + ) + die(14) return objectTraps.set!.call(this, state[0], prop, value, state[0]) } diff --git a/src/plugins/patches.ts b/src/plugins/patches.ts index c882915e..988a0556 100644 --- a/src/plugins/patches.ts +++ b/src/plugins/patches.ts @@ -24,7 +24,7 @@ import { export function enablePatches() { const errorOffset = 16 - if (__DEV__) { + if (process.env.NODE_ENV === "development") { errors.push( 'Sets cannot have "replace" patches.', function(op: string) { diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 3152655d..bcf643f3 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,42 +1,43 @@ -export const errors = __DEV__ - ? [ - // All error codes, starting by 0: - function(plugin: string) { - return `The plugin for '${plugin}' has not been loaded into Immer. To enable the plugin, import and call \`enable${plugin}()\` when initializing your application.` - }, - function(thing: string) { - return `produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${thing}'` - }, - "This object has been frozen and should not be mutated", - function(data: any) { - return ( - "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + - data - ) - }, - "An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.", - "Immer forbids circular references", - "The first or second argument to `produce` must be a function", - "The third argument to `produce` must be a function or undefined", - "First argument to `createDraft` must be a plain object, an array, or an immerable object", - "First argument to `finishDraft` must be a draft returned by `createDraft`", - function(thing: string) { - return `'current' expects a draft, got: ${thing}` - }, - "Object.defineProperty() cannot be used on an Immer draft", - "Object.setPrototypeOf() cannot be used on an Immer draft", - "Immer only supports deleting array indices", - "Immer only supports setting array indices and the 'length' property", - function(thing: string) { - return `'original' expects a draft, got: ${thing}` - } - // Note: if more errors are added, the errorOffset in Patches.ts should be increased - // See Patches.ts for additional errors - ] - : [] +export const errors = + process.env.NODE_ENV === "development" + ? [ + // All error codes, starting by 0: + function(plugin: string) { + return `The plugin for '${plugin}' has not been loaded into Immer. To enable the plugin, import and call \`enable${plugin}()\` when initializing your application.` + }, + function(thing: string) { + return `produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${thing}'` + }, + "This object has been frozen and should not be mutated", + function(data: any) { + return ( + "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + + data + ) + }, + "An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.", + "Immer forbids circular references", + "The first or second argument to `produce` must be a function", + "The third argument to `produce` must be a function or undefined", + "First argument to `createDraft` must be a plain object, an array, or an immerable object", + "First argument to `finishDraft` must be a draft returned by `createDraft`", + function(thing: string) { + return `'current' expects a draft, got: ${thing}` + }, + "Object.defineProperty() cannot be used on an Immer draft", + "Object.setPrototypeOf() cannot be used on an Immer draft", + "Immer only supports deleting array indices", + "Immer only supports setting array indices and the 'length' property", + function(thing: string) { + return `'original' expects a draft, got: ${thing}` + } + // Note: if more errors are added, the errorOffset in Patches.ts should be increased + // See Patches.ts for additional errors + ] + : [] export function die(error: number, ...args: any[]): never { - if (__DEV__) { + if (process.env.NODE_ENV === "development") { const e = errors[error] const msg = typeof e === "function" ? e.apply(null, args as any) : e throw new Error(`[Immer] ${msg}`) diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 00000000..1aa7490a --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,90 @@ +import {defineConfig, Options} from "tsup" +import fs from "fs" + +export default defineConfig(options => { + const commonOptions: Partial = { + entry: { + immer: "src/immer.ts" + }, + sourcemap: true, + ...options + } + + const productionOptions = { + minify: true, + define: { + "process.env.NODE_ENV": JSON.stringify("production") + } + } + + return [ + // ESM, standard bundler dev, embedded `process` references + { + ...commonOptions, + format: ["esm"], + dts: true, + clean: true, + sourcemap: true, + onSuccess() { + // Support Flow types + fs.copyFileSync("src/types/index.js.flow", "dist/index.js.flow") + } + }, + // ESM, Webpack 4 support. Target ES2018 syntax to compile away optional chaining and spreads + { + ...commonOptions, + entry: { + "immer.legacy-esm": "src/immer.ts" + }, + // ESBuild outputs `'.mjs'` by default for the 'esm' format. Force '.js' + outExtension: () => ({js: ".js"}), + target: "es2017", + format: ["esm"], + sourcemap: true + }, + // ESM for use in browsers. Minified, with `process` compiled away + { + ...commonOptions, + ...productionOptions, + entry: { + "immer.production": "src/immer.ts" + }, + format: ["esm"], + outExtension: () => ({js: ".mjs"}) + }, + // CJS development + { + ...commonOptions, + entry: { + "immer.cjs.development": "src/immer.ts" + }, + format: "cjs", + + outDir: "./dist/cjs/" + }, + // CJS production + { + ...commonOptions, + ...productionOptions, + entry: { + "immer.cjs.production": "src/immer.ts" + }, + format: "cjs", + outDir: "./dist/cjs/", + onSuccess: () => { + // Write the CJS index file + fs.writeFileSync( + "dist/cjs/index.js", + ` +'use strict' + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./immer.cjs.production.min.js') +} else { + module.exports = require('./immer.cjs.development.js') +}` + ) + } + } + ] +})