From 960ca8499b7b09bf017e0206bbc81832be4c62b7 Mon Sep 17 00:00:00 2001 From: Jannick Garthen Date: Sat, 18 Jan 2020 14:36:11 +0100 Subject: [PATCH] Inline process.browser for better code elimination --- .../core/integration-tests/test/javascript.js | 96 ++++++++++++++++++- packages/core/test-utils/src/utils.js | 3 +- packages/transformers/js/src/JSTransformer.js | 18 +++- .../js/src/visitors/{env.js => process.js} | 17 +++- 4 files changed, 125 insertions(+), 9 deletions(-) rename packages/transformers/js/src/visitors/{env.js => process.js} (50%) diff --git a/packages/core/integration-tests/test/javascript.js b/packages/core/integration-tests/test/javascript.js index ad9a94c98ad..a7c8bbc8568 100644 --- a/packages/core/integration-tests/test/javascript.js +++ b/packages/core/integration-tests/test/javascript.js @@ -1005,9 +1005,29 @@ describe('javascript', function() { assert.equal(output(), 'test:test'); }); - it.skip('should not insert environment variables in electron environment', async function() { + it('should not insert environment variables in electron-main environment', async function() { let b = await bundle(path.join(__dirname, '/integration/env/index.js'), { - target: 'electron', + targets: { + main: { + context: 'electron-main', + distDir: path.join(__dirname, '/integration/env/dist.js'), + }, + }, + }); + + let output = await run(b); + assert.ok(output.toString().indexOf('process.env') > -1); + assert.equal(output(), 'test:test'); + }); + + it('should not insert environment variables in electron-renderer environment', async function() { + let b = await bundle(path.join(__dirname, '/integration/env/index.js'), { + targets: { + main: { + context: 'electron-renderer', + distDir: path.join(__dirname, '/integration/env/dist.js'), + }, + }, }); let output = await run(b); @@ -1057,6 +1077,78 @@ describe('javascript', function() { assert.equal(output, 'productiontest'); }); + it('should replace process.browser for target browser', async function() { + let b = await bundle( + path.join(__dirname, '/integration/process/index.js'), + { + targets: { + main: { + context: 'browser', + distDir: path.join(__dirname, '/integration/process/dist.js'), + }, + }, + }, + ); + + let output = await run(b); + assert.ok(output.toString().indexOf('process.browser') === -1); + assert.equal(output(), true); + }); + + it('should not touch process.browser for target node', async function() { + let b = await bundle( + path.join(__dirname, '/integration/process/index.js'), + { + targets: { + main: { + context: 'node', + distDir: path.join(__dirname, '/integration/process/dist.js'), + }, + }, + }, + ); + + let output = await run(b); + assert.ok(output.toString().indexOf('process.browser') !== -1); + assert.equal(output(), false); + }); + + it('should not touch process.browser for target electron-main', async function() { + let b = await bundle( + path.join(__dirname, '/integration/process/index.js'), + { + targets: { + main: { + context: 'electron-main', + distDir: path.join(__dirname, '/integration/process/dist.js'), + }, + }, + }, + ); + + let output = await run(b); + assert.ok(output.toString().indexOf('process.browser') !== -1); + assert.equal(output(), false); + }); + + it('should replace process.browser for target electron-renderer', async function() { + let b = await bundle( + path.join(__dirname, '/integration/process/index.js'), + { + targets: { + main: { + context: 'electron-renderer', + distDir: path.join(__dirname, '/integration/process/dist.js'), + }, + }, + }, + ); + + let output = await run(b); + assert.ok(output.toString().indexOf('process.browser') === -1); + assert.equal(output(), true); + }); + it.skip('should support adding implicit dependencies', async function() { let b = await bundle(path.join(__dirname, '/integration/json/index.js'), { delegate: { diff --git a/packages/core/test-utils/src/utils.js b/packages/core/test-utils/src/utils.js index ea1cc1ccbd1..938d82882aa 100644 --- a/packages/core/test-utils/src/utils.js +++ b/packages/core/test-utils/src/utils.js @@ -175,9 +175,10 @@ export async function run( break; } case 'node': + case 'electron-main': ctx = prepareNodeContext(nullthrows(bundle.filePath), globals); break; - case 'electron': { + case 'electron-renderer': { let browser = prepareBrowserContext(nullthrows(bundle.filePath), globals); ctx = { ...browser.ctx, diff --git a/packages/transformers/js/src/JSTransformer.js b/packages/transformers/js/src/JSTransformer.js index 9c339f72470..423a398621a 100644 --- a/packages/transformers/js/src/JSTransformer.js +++ b/packages/transformers/js/src/JSTransformer.js @@ -4,7 +4,7 @@ import semver from 'semver'; import generate from '@babel/generator'; import {Transformer} from '@parcel/plugin'; import collectDependencies from './visitors/dependencies'; -import envVisitor from './visitors/env'; +import processVisitor from './visitors/process'; import fsVisitor from './visitors/fs'; import insertGlobals from './visitors/globals'; import {parse} from '@babel/parser'; @@ -17,6 +17,7 @@ import SourceMap from '@parcel/source-map'; const IMPORT_RE = /\b(?:import\b|export\b|require\s*\()/; const ENV_RE = /\b(?:process\.env)\b/; +const BROWSER_RE = /\b(?:process\.browser)\b/; const GLOBAL_RE = /\b(?:process|__dirname|__filename|global|Buffer|define)\b/; const FS_RE = /\breadFileSync\b/; const SW_RE = /\bnavigator\s*\.\s*serviceWorker\s*\.\s*register\s*\(/; @@ -46,6 +47,7 @@ export default new Transformer({ !options.scopeHoist && !canHaveDependencies(code) && !ENV_RE.test(code) && + !BROWSER_RE.test(code) && !FS_RE.test(code) ) { return null; @@ -74,9 +76,17 @@ export default new Transformer({ let ast = asset.ast; let code = await asset.getCode(); - // Inline environment variables - if (!asset.env.isNode() && (ast.isDirty || ENV_RE.test(code))) { - walk.simple(ast.program, envVisitor, {asset, env: options.env}); + // Inline process/ environment variables + if ( + (!asset.env.isNode() && (ast.isDirty || ENV_RE.test(code))) || + (asset.env.isBrowser() && (ast.isDirty || BROWSER_RE.test(code))) + ) { + walk.ancestor(ast.program, processVisitor, { + asset, + env: options.env, + isNode: asset.env.isNode(), + isBrowser: asset.env.isBrowser(), + }); } // Collect dependencies diff --git a/packages/transformers/js/src/visitors/env.js b/packages/transformers/js/src/visitors/process.js similarity index 50% rename from packages/transformers/js/src/visitors/env.js rename to packages/transformers/js/src/visitors/process.js index 2b61d822850..b15acedb5e5 100644 --- a/packages/transformers/js/src/visitors/env.js +++ b/packages/transformers/js/src/visitors/process.js @@ -2,9 +2,9 @@ import * as types from '@babel/types'; import {morph} from './utils'; export default { - MemberExpression(node, {asset, env}) { + MemberExpression(node, {asset, env, isBrowser, isNode}, ancestors) { // Inline environment variables accessed on process.env - if (types.matchesPattern(node.object, 'process.env')) { + if (!isNode && types.matchesPattern(node.object, 'process.env')) { let key = types.toComputedKey(node); if (types.isStringLiteral(key)) { // Try using the value from the passed env (either from new Parcel @@ -17,6 +17,19 @@ export default { // asset.meta.env[key.value] = process.env[key.value]; } } + // Inline process.browser + } else if (isBrowser && types.matchesPattern(node, 'process.browser')) { + // the last ancestor is the node itself, the one before may be it's parent + const parent = ancestors[ancestors.length - 2]; + const isAssignmentExpression = Boolean( + parent && types.isAssignmentExpression(parent) && parent.left === node, + ); + + if (isAssignmentExpression) { + parent.right = types.booleanLiteral(true); + } else { + morph(node, types.booleanLiteral(true)); + } } }, };