From 04895fa7236e1e11fbc661747d14baee2fccb8c2 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 7 Jan 2022 16:45:52 -0800 Subject: [PATCH] feat: typeofReplacements option for plugin-replace --- packages/replace/README.md | 33 ++++++++++++++++++ packages/replace/src/index.js | 22 ++++++++++-- .../fixtures/form/process-check/_config.js | 8 +++++ .../test/fixtures/form/process-check/input.js | 3 ++ .../fixtures/form/process-check/output.js | 1 + packages/replace/test/snapshots/form.js.md | 10 +++++- packages/replace/test/snapshots/form.js.snap | Bin 529 -> 595 bytes 7 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 packages/replace/test/fixtures/form/process-check/_config.js create mode 100644 packages/replace/test/fixtures/form/process-check/input.js create mode 100644 packages/replace/test/fixtures/form/process-check/output.js diff --git a/packages/replace/README.md b/packages/replace/README.md index 533e09c9d..8b1e4185d 100644 --- a/packages/replace/README.md +++ b/packages/replace/README.md @@ -70,6 +70,39 @@ For example, if you pass `typeof window` in `values` to-be-replaced, then you co - `typeof window.document` **will not** be replaced due to `(?!\.)` boundary - `typeof windowSmth` **will not** be replaced due to a `\b` boundary +### `typeofReplacements` + +Type: `Boolean`
+Default: `false` + +When replacing dot-separated object properties like `process.env.NODE_ENV`, will also replace typeof checks against the objects +with `"object"`. + +For example: + +```js +replace({ + values: { + 'process.env.NODE_ENV': '"production"', + }, +}); +``` + +```js +// Input +if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') { + console.log('production'); +} +// Without `typeofReplacements` +if (typeof process !== 'undefined' && 'production' === 'production') { + console.log('production'); +} +// With `typeofReplacements` +if ('object' !== 'undefined' && 'production' === 'production') { + console.log('production'); +} +``` + ### `preventAssignment` Type: `Boolean`
diff --git a/packages/replace/src/index.js b/packages/replace/src/index.js index f2085e7e8..5747ec2e7 100755 --- a/packages/replace/src/index.js +++ b/packages/replace/src/index.js @@ -24,6 +24,7 @@ function getReplacements(options) { delete values.exclude; delete values.sourcemap; delete values.sourceMap; + delete values.typeofReplacements; return values; } @@ -35,10 +36,27 @@ function mapToFunctions(object) { }, {}); } +const objKeyRegEx = /^([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)(\.([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*))+$/; +function expandTypeofReplacements(replacements) { + Object.keys(replacements).forEach((key) => { + const objMatch = key.match(objKeyRegEx); + if (!objMatch) return; + let dotIndex = objMatch[1].length; + let lastIndex = 0; + do { + replacements[`typeof ${key.slice(lastIndex, dotIndex)}`] = '"object"'; + lastIndex = dotIndex + 1; + dotIndex = key.indexOf('.', lastIndex); + } while (dotIndex !== -1); + }); +} + export default function replace(options = {}) { const filter = createFilter(options.include, options.exclude); - const { delimiters, preventAssignment } = options; - const functionValues = mapToFunctions(getReplacements(options)); + const { delimiters, preventAssignment, typeofReplacements } = options; + const replacements = getReplacements(options); + if (typeofReplacements) expandTypeofReplacements(replacements); + const functionValues = mapToFunctions(replacements); const keys = Object.keys(functionValues).sort(longest).map(escape); const lookahead = preventAssignment ? '(?!\\s*=[^=])' : ''; const pattern = delimiters diff --git a/packages/replace/test/fixtures/form/process-check/_config.js b/packages/replace/test/fixtures/form/process-check/_config.js new file mode 100644 index 000000000..48b59e8ee --- /dev/null +++ b/packages/replace/test/fixtures/form/process-check/_config.js @@ -0,0 +1,8 @@ +module.exports = { + description: 'Handles process type guards in replacements', + options: { + 'process.env.NODE_ENV': '"production"', + preventAssignment: true, + typeofReplacements: true + } +}; diff --git a/packages/replace/test/fixtures/form/process-check/input.js b/packages/replace/test/fixtures/form/process-check/input.js new file mode 100644 index 000000000..dcce0a89f --- /dev/null +++ b/packages/replace/test/fixtures/form/process-check/input.js @@ -0,0 +1,3 @@ +if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') { + console.log('production'); +} diff --git a/packages/replace/test/fixtures/form/process-check/output.js b/packages/replace/test/fixtures/form/process-check/output.js new file mode 100644 index 000000000..5fa7f8f80 --- /dev/null +++ b/packages/replace/test/fixtures/form/process-check/output.js @@ -0,0 +1 @@ +process.env.DEBUG = 'test'; diff --git a/packages/replace/test/snapshots/form.js.md b/packages/replace/test/snapshots/form.js.md index eaeb3189d..efc780818 100644 --- a/packages/replace/test/snapshots/form.js.md +++ b/packages/replace/test/snapshots/form.js.md @@ -39,12 +39,20 @@ Generated by [AVA](https://avajs.dev). exclude␊ `);` +## process-check: Handles process type guards in replacements + +> Snapshot 1 + + `if ("object" !== 'undefined' && "production" === 'production') {␊ + console.log('production');␊ + }` + ## replace-nothing: replaces nothing > Snapshot 1 `console.log('as-it'); // eslint-disable-line␊ - console.log(typeof window.document); // eslint-disable-line` + console.log("object".document); // eslint-disable-line` ## replace-strings: replaces strings diff --git a/packages/replace/test/snapshots/form.js.snap b/packages/replace/test/snapshots/form.js.snap index f0d05a94e1b772b14bfd01f9f6bcadf20a2e1a6a..0ec95e2fa48c512d2d129b1a56b14b1408acc34c 100644 GIT binary patch literal 595 zcmV-Z0<8T(RzVJ_HuNxpMLz-Y(Vq-$6DGfn zlH%2}4HTKCzz7z#16p@>@z&5LrTKjCEFUR8oh{YG2o_xp#J3{F{~j{T+QzWPW(kku z8dpZJ=vN@V_jXN7%Ew1%*Xt$uTw}bmnGq}+!NkDuYIW**;R9W}Z|z)nS@z6Kbw;r0 zJRoLfUS1zQF6lGNf7 zbyS7PC=N(0*3B#d+Nq$guaH`tlbKhdo03_an3R*M3*@A7AuCqOPs&P7E>Y4;$xklL zP0cI8sSU+KJ7hCVj7YN!Ei~dY(-h!x3W~NsFP7#3g9fNQMO{HnO+g753@N3_CBX1h zQm_TdBLvhn6{@)u6j1z+ENjhGi(;7#vdRRY&Z5+UoW$hR6d=<^RUyA9Gd(jeF-O%7 z%!hFmfcykBXNTf3DJ3;0GdHsYm8Rql=&{Rp@$JH`W=Xm{$yyIF!^ni z6t9+TpvW`@MzE+e(7IcZ;(rerW^H3wW3z3wC8Q8%-U}R+wWK>Gd&nwQ)N!82A zPuEaSN-arLC`&CW&dkqKFw`^9Gf>yGMo}-7T2YW+RHBfQnwD6aQ=*`hnO9I+qL)>y zgsM)upeR2%wYXR>HLpz1#nma)UBOmCy(G1`L>*OO28sg`i*++gfOabA>no%d=Va!U z=%!>ACnn{j>H;~bT*!(`DhpEc(-g`x^HTE5^-}VaOLJ56N^t5$vDFUQAQL0vEL6%* z%1TWxQ9=t68)P*JTnY+BsRcQS$*Czorj4pXeoD zH77GSvjiA7Ku&RfX;E@&ZejtP?F(jS<|XHprlbN{sTB|wx)l~6D@qjd^HPEFVTjY) TDE3