Skip to content

Commit

Permalink
Expose ts-invariant/process entry point for optional polyfill.
Browse files Browse the repository at this point in the history
Though ts-invariant stopped polyfilling global.process in #94, there's a
chance existing applications were accidentally depending on that API, so
we can make it easy to install (and then remove!) the global.process
polyfill if you still need it:

  import { install, remove } from "ts-invariant/process"
  install();
  ...
  remove();

By default, the polyfill will be installed upon importing
ts-invariant/process for the first time, so you may not need to call
install() in most cases (though it doesn't hurt).

Calling remove() cleans up the global namespace, removing the stub
global.process object (if ts-invariant/process defined one). This allows
you to import other packages that depend on process.env before removing
the polyfill, without permanently polluting the global namespace.
  • Loading branch information
benjamn committed Jun 16, 2021
1 parent 41c1891 commit 294f183
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 39 deletions.
45 changes: 45 additions & 0 deletions packages/ts-invariant/process/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/ts-invariant/process/main.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/ts-invariant/process/module.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export declare function install(): void;
export declare function remove(): void;
37 changes: 37 additions & 0 deletions packages/ts-invariant/process/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
function maybe(thunk) {
try { return thunk() } catch (_) {}
}

let needToRemove = false;

export function install() {
if (!maybe(() => process.env.NODE_ENV) &&
!maybe(() => process)) {
Object.defineProperty(global, "process", {
value: {
env: {
// This default needs to be "production" instead of "development", to
// avoid the problem https://github.com/graphql/graphql-js/pull/2894
// will eventually solve, once merged and released.
NODE_ENV: "production",
},
},
// Let anyone else change global.process as they see fit, but hide it from
// Object.keys(global) enumeration.
configurable: true,
enumerable: false,
writable: true,
});
needToRemove = true;
}
}

// Call install() at least once, when this module is imported.
install();

export function remove() {
if (needToRemove) {
delete global.process;
needToRemove = false;
}
}
10 changes: 10 additions & 0 deletions packages/ts-invariant/process/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "ts-invariant/process",
"main": "./main.js",
"module": "./module.js",
"types": "./module.d.ts",
"sideEffects": [
"./module.js",
"./index.js"
]
}
24 changes: 21 additions & 3 deletions packages/ts-invariant/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ function external(id) {
return id in globals;
}

export default [{
const jobs = [];
export default jobs;

jobs.push({
input: "src/invariant.ts",
external,
output: {
Expand All @@ -26,7 +29,9 @@ export default [{
tsconfig: "./tsconfig.rollup.json",
}),
],
}, {
});

jobs.push({
input: "lib/invariant.esm.js",
external,
output: {
Expand All @@ -38,4 +43,17 @@ export default [{
name: "ts-invariant",
globals,
},
}];
});

jobs.push({
input: "process/module.js",
external,
output: {
file: "process/main.js",
format: "cjs",
exports: "named",
sourcemap: true,
name: "ts-invariant/process",
globals,
},
});
16 changes: 0 additions & 16 deletions packages/ts-invariant/src/invariant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,4 @@ export function setVerbosity(level: VerbosityLevel): VerbosityLevel {
return old;
}

// Code that uses ts-invariant with rollup-plugin-invariant may want to
// import this process stub to avoid errors evaluating process.env.NODE_ENV.
// However, because most ESM-to-CJS compilers will rewrite the process import
// as tsInvariant.process, which prevents proper replacement by minifiers, we
// also export processStub, so you can import { invariant, processStub } from
// "ts-invariant" and assign processStub to a local variable named process.
export const processStub: {
env: Record<string, any>;
[key: string]: any;
} = (
typeof process === "object" &&
typeof process.env === "object"
) ? process : { env: {} };

export { processStub as process };

export default invariant;
43 changes: 24 additions & 19 deletions packages/ts-invariant/src/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import defaultExport, {
invariant,
InvariantError,
setVerbosity,
process,
processStub,
} from "./invariant";
import reactInvariant from "invariant";

import { install, remove } from "../process";

describe("ts-invariant", function () {
it("should support both named and default exports", function () {
assert.strictEqual(defaultExport, invariant);
Expand Down Expand Up @@ -145,28 +145,33 @@ describe("ts-invariant", function () {
checkConsoleMethod("error", true);
});

it("should export a usable process polyfill", function () {
assert.strictEqual(typeof process, "object");
assert.strictEqual(typeof process.env, "object");
if (process.versions) {
assert.strictEqual(typeof process.versions.node, "string");
}
});

it("should export a usable processStub", function () {
const process = processStub;
assert.strictEqual(typeof process, "object");
assert.strictEqual(typeof process.env, "object");
if (process.versions) {
assert.strictEqual(typeof process.versions.node, "string");
}
});

it("should let TypeScript know about the assertion made", function () {
const value: { foo?: { bar?: string } } = {foo: {bar: "bar"}};
invariant(value.foo, 'fail');

// On compile time this should not raise "TS2532: Object is possibly 'undefined'."
assert.strictEqual(value.foo.bar, "bar");
});

describe("ts-invariant/process", function () {
it("install and remove", function () {
const origDesc = Object.getOwnPropertyDescriptor(global, "process");
Object.defineProperty(global, "process", {
value: void 0,
configurable: true,
});
assert.strictEqual(process, void 0);
install();
assert.deepStrictEqual(process, {
env: {
NODE_ENV: "production",
},
});
remove();
assert.strictEqual(typeof process, "undefined");
if (origDesc) {
Object.defineProperty(global, "process", origDesc);
}
});
});
});
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"importHelpers": true,
"declaration": true,
"sourceMap": true,
"lib": ["es2015"],
"lib": ["es2015", "DOM"],
"types": ["node", "mocha"],
"strict": true,
"noImplicitAny": true,
Expand Down

0 comments on commit 294f183

Please sign in to comment.