Repository: sanctuary-js/sanctuary
Pull request #512 (2018-03-21 17:07:33)

disable type checking for default module in "production"
In #506, @blackxored has made a case for changing the default value of `checkTypes` from `true` to an expression involving `process.env.NODE_ENV`. I have long opposed this change, but when Aldwin disagrees with me it's wise to reconsider my position. I have done so, and I have changed my mind.

Requirements:

  - default Sanctuary module should perform type checking *by default*;
  - behaviour must be easy to understand; and
  - the user must be permitted to specify whether type checking should be performed.

Although not a requirement, it would be nice to reduce the proportion of users who find it necessary to use [`S.create`][1]. I believe this is your primary goal, Adrian.

The current rule is as follows:

> Type checking is enabled for the default module.

This satisfies the requirements but forces many users to use `S.create` even though they are happy with the default environment.

The new rule is as follows:

> Type checking is enabled for the default module _unless `process.env.NODE_ENV` is `'production'`_.

This satisfies the requirements and obviates the need for `S.create` in many cases. :)


[1]: https://sanctuary.js.org/#create


blackxored: This looks great, thanks a lot for considering this. 
davidchambers: > Note that this could be a breaking change to some (those who have come to rely on type checking in production, if any).

This is well worth mentioning in the release notes.


Patch:
diff --git a/eslint/es6.js b/eslint/es6.js
index c9eb482..7257f9f 100644
--- a/eslint/es6.js
+++ b/eslint/es6.js
@@ -25,6 +25,8 @@ module.exports = {
         Right: false,
         S: false,
         Sum: false,
+        localStorage: false,
+        sanctuary: false,
       },
       rules: {
         'array-bracket-spacing': ['off'],
diff --git a/index.js b/index.js
index abcea0f..88b1e5e 100644
--- a/index.js
+++ b/index.js
@@ -172,19 +172,16 @@
 //. // => 3
 //. ```
 //.
-//. There is a performance cost to run-time type checking. One may wish to
-//. disable type checking in certain contexts to avoid paying this cost.
-//. [`create`](#create) facilitates the creation of a Sanctuary module which
-//. does not perform type checking.
-//.
-//. In Node, one could use an environment variable to determine whether to
-//. perform type checking:
+//. There is a performance cost to run-time type checking. Type checking is
+//. disabled by default if `process.env.NODE_ENV` is `'production'`. If this
+//. rule is unsuitable for a given program, one may use [`create`](#create)
+//. to create a Sanctuary module based on a different rule. For example:
 //.
 //. ```javascript
-//. const {create, env} = require ('sanctuary');
-//.
-//. const checkTypes = process.env.NODE_ENV !== 'production';
-//. const S = create ({checkTypes, env});
+//. const S = sanctuary.create ({
+//.   checkTypes: localStorage.getItem ('SANCTUARY_CHECK_TYPES') === 'true',
+//.   env: sanctuary.env,
+//. });
 //. ```
 //.
 //. ## API
@@ -5136,7 +5133,13 @@
   };
 
   return create ({
-    checkTypes: true,
+    checkTypes: (
+      /* global process:false */
+      typeof process === 'undefined'
+      || process == null
+      || process.env == null
+      || process.env.NODE_ENV !== 'production'
+    ),
     env: Z.concat ($.env, [
       $.FiniteNumber,
       $.NonZeroFiniteNumber,
diff --git a/karma.conf.js b/karma.conf.js
index 8cb06cb..0c5b80c 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -90,6 +90,8 @@ var options = {
   files: S.concat (S.map (depMain) (dependencies))
                   (['index.js', 'test/**/*.js']),
 
+  exclude: ['test/NODE_ENV.js'],
+
   preprocessors: {
     'test/**/*.js': ['browserify']
   },


Test patch:
diff --git a/test/NODE_ENV.js b/test/NODE_ENV.js
new file mode 100644
index 0000000..406be9a
--- /dev/null
+++ b/test/NODE_ENV.js
@@ -0,0 +1,97 @@
+'use strict';
+
+var fs = require ('fs');
+var path = require ('path');
+var vm = require ('vm');
+
+var $$version = (require ('sanctuary-def/package.json')).version;
+
+var eq = require ('./internal/eq');
+var throws = require ('./internal/throws');
+
+
+suite ('NODE_ENV', function() {
+
+  var source = fs.readFileSync (path.join (__dirname, '..', 'index.js'), 'utf8');
+
+  var expected = new TypeError ([
+    'Invalid value',
+    '',
+    'add :: FiniteNumber -> FiniteNumber -> FiniteNumber',
+    '       ^^^^^^^^^^^^',
+    '            1',
+    '',
+    '1)  "foo" :: String',
+    '',
+    'The value at position 1 is not a member of ‘FiniteNumber’.',
+    '',
+    'See https://github.com/sanctuary-js/sanctuary-def/tree/v' + $$version + '#FiniteNumber for information about the sanctuary-def/FiniteNumber type.',
+    ''
+  ].join ('\n'));
+
+  test ('typeof process === "undefined"', function() {
+    var context = {
+      module: {exports: {}},
+      require: require
+    };
+    vm.runInNewContext (source, context);
+
+    throws (function() { context.module.exports.add ('foo'); }) (expected);
+  });
+
+  test ('typeof process !== "undefined" && process == null', function() {
+    var context = {
+      module: {exports: {}},
+      process: null,
+      require: require
+    };
+    vm.runInNewContext (source, context);
+
+    throws (function() { context.module.exports.add ('foo'); }) (expected);
+  });
+
+  test ('typeof process !== "undefined" && process != null && process.env == null', function() {
+    var context = {
+      module: {exports: {}},
+      process: {},
+      require: require
+    };
+    vm.runInNewContext (source, context);
+
+    throws (function() { context.module.exports.add ('foo'); }) (expected);
+  });
+
+  test ('typeof process !== "undefined" && process != null && process.env != null && process.env.NODE_ENV == null', function() {
+    var context = {
+      module: {exports: {}},
+      process: {env: {}},
+      require: require
+    };
+    vm.runInNewContext (source, context);
+
+    throws (function() { context.module.exports.add ('foo'); }) (expected);
+  });
+
+  test ('typeof process !== "undefined" && process != null && process.env != null && process.env.NODE_ENV !== "production"', function() {
+    var context = {
+      module: {exports: {}},
+      process: {env: {NODE_ENV: 'XXX'}},
+      require: require
+    };
+    vm.runInNewContext (source, context);
+
+    throws (function() { context.module.exports.add ('foo'); }) (expected);
+  });
+
+  test ('typeof process !== "undefined" && process != null && process.env != null && process.env.NODE_ENV === "production"', function() {
+    var context = {
+      module: {exports: {}},
+      process: {env: {NODE_ENV: 'production'}},
+      require: require
+    };
+    vm.runInNewContext (source, context);
+
+    eq (context.module.exports.add ('foo') ('bar')) ('foobar');
+  });
+
+});