From ec34118223224c570bc4f2d30b9483f464db13f4 Mon Sep 17 00:00:00 2001 From: Will Binns-Smith Date: Tue, 14 Apr 2020 13:59:28 -0700 Subject: [PATCH 1/3] Don't treeshake assignments to unknown objects (#4473) * Add test for not tree-shaking unknown object * Only shake assignments to known exports objects * Add test and implementation for CommonJS Co-authored-by: Niklas Mischkulnig Co-authored-by: Niklas Mischkulnig --- .../tree-shaking-no-unknown-objects/a.js | 1 + .../tree-shaking-no-unknown-objects/index.js | 3 + .../es6/tree-shaking-no-unknown-objects/a.js | 1 + .../tree-shaking-no-unknown-objects/index.js | 5 ++ .../integration-tests/test/scope-hoisting.js | 25 +++++++- packages/shared/scope-hoisting/src/link.js | 2 +- packages/shared/scope-hoisting/src/shake.js | 63 ++++++++++++++----- 7 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/a.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/index.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/a.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/index.js diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/a.js new file mode 100644 index 00000000000..338b25885b0 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/a.js @@ -0,0 +1 @@ +module.exports = window; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/index.js new file mode 100644 index 00000000000..ab708f47628 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/index.js @@ -0,0 +1,3 @@ +const foo = require('./a'); +foo.bar = 42; +output = window.bar; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/a.js new file mode 100644 index 00000000000..5db55959a24 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/a.js @@ -0,0 +1 @@ +export const foo = window; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/index.js new file mode 100644 index 00000000000..af2d7c29e79 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/index.js @@ -0,0 +1,5 @@ +import {foo} from './a'; + +foo.bar = 42; + +output = window.bar; diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js index 347be11fb6c..7b4967cab97 100644 --- a/packages/core/integration-tests/test/scope-hoisting.js +++ b/packages/core/integration-tests/test/scope-hoisting.js @@ -904,8 +904,7 @@ describe('scope hoisting', function() { b.getBundles()[0].filePath, 'utf8', ); - assert(!/bar/.test(contents)); - assert(!/displayName/.test(contents)); + assert(!contents.includes('exports.bar =')); }); it('should correctly rename references to default exported classes', async function() { @@ -930,6 +929,17 @@ describe('scope hoisting', function() { let output = await run(b); assert.deepEqual(output.foo, 'bar'); }); + + it('does not tree-shake assignments to unknown objects', async () => { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/es6/tree-shaking-no-unknown-objects/index.js', + ), + ); + + assert.equal(await run(b), 42); + }); }); describe('commonjs', function() { @@ -1879,6 +1889,17 @@ describe('scope hoisting', function() { let output = await run(b); assert.deepEqual(output, [4, 2]); }); + + it('does not tree-shake assignments to unknown objects', async () => { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/commonjs/tree-shaking-no-unknown-objects/index.js', + ), + ); + + assert.equal(await run(b), 42); + }); }); it('should not throw with JS included from HTML', async function() { diff --git a/packages/shared/scope-hoisting/src/link.js b/packages/shared/scope-hoisting/src/link.js index 58ee2781c47..d5ad0096ddc 100644 --- a/packages/shared/scope-hoisting/src/link.js +++ b/packages/shared/scope-hoisting/src/link.js @@ -697,7 +697,7 @@ export function link({ verifyScopeState(path.scope); } - treeShake(path.scope, exported); + treeShake(path.scope, exported, exportsMap); }, }, }); diff --git a/packages/shared/scope-hoisting/src/shake.js b/packages/shared/scope-hoisting/src/shake.js index 21f5003a406..30cc5129c3c 100644 --- a/packages/shared/scope-hoisting/src/shake.js +++ b/packages/shared/scope-hoisting/src/shake.js @@ -1,5 +1,5 @@ // @flow -import type {Symbol} from '@parcel/types'; +import type {Asset, Symbol} from '@parcel/types'; import type {NodePath, Scope} from '@babel/traverse'; import type {Expression, Identifier, Node} from '@babel/types'; @@ -25,6 +25,7 @@ import {pathDereference, pathRemove} from './utils'; export default function treeShake( scope: Scope, exportedIdentifiers: Set, + exportsMap: Map, ) { // Keep passing over all bindings in the scope until we don't remove any. // This handles cases where we remove one binding which had a reference to @@ -34,7 +35,7 @@ export default function treeShake( removed = false; Object.keys(scope.bindings).forEach((name: string) => { - let binding = getUnusedBinding(scope.path, name); + let binding = getUnusedBinding(scope.path, name, exportsMap); // If it is not safe to remove the binding don't touch it. if (!binding || exportedIdentifiers.has(name)) { @@ -44,7 +45,7 @@ export default function treeShake( // Remove the binding and all references to it. pathRemove(binding.path); [...binding.referencePaths, ...binding.constantViolations].forEach(p => - remove(p, scope), + remove(p, scope, exportsMap), ); scope.removeBinding(name); @@ -54,24 +55,33 @@ export default function treeShake( } // Check if a binding is safe to remove and returns it if it is. -function getUnusedBinding(path, name) { +function getUnusedBinding(path, name, exportsMap) { let binding = path.scope.getBinding(name); if (!binding) { return null; } - if (!isPure(binding.path)) { + if (!isPure(binding)) { // declaration (~= init) isn't pure return null; } + if (hasSideEffects(binding)) { + // e.g. + // let foo = {}; + // foo = window; + // foo.xyz = 2; + // console.log(window.xyz); + return null; + } + if (!binding.referenced) { return binding; } // Is there any references which aren't simple assignments? let bailout = binding.referencePaths.some( - path => !isExportAssignment(path) && !isWildcardDest(path), + path => !isExportAssignment(path, exportsMap) && !isWildcardDest(path), ); if (!bailout) { @@ -81,7 +91,8 @@ function getUnusedBinding(path, name) { return null; } -function isPure(path) { +function isPure(binding) { + let {path} = binding; let {node} = path; if (isVariableDeclarator(node) && isIdentifier(node.id)) { let init = path.get>('init'); @@ -97,12 +108,30 @@ function isPure(path) { return path.isPure(); } -function isExportAssignment(path) { +function hasSideEffects(binding) { + let {node} = binding.path; + if (isVariableDeclarator(node)) { + return !( + (!binding.referenced || isObjectExpression(node.init)) && + (binding.constant || + binding.constantViolations.every( + ({node}) => + !isAssignmentExpression(node) || isObjectExpression(node.right), + )) + ); + } + + return false; +} + +function isExportAssignment(path, exportsMap: Map) { let {parent} = path; - // match "path.foo = bar;" + // match "path.foo = bar;", where path is a known exports identifier. if ( isMemberExpression(parent) && parent.object === path.node && + isIdentifier(path.node) && + exportsMap.has(path.node.name) && ((isIdentifier(parent.property) && !parent.computed) || isStringLiteral(parent.property)) ) { @@ -123,7 +152,11 @@ function isWildcardDest(path) { ); } -function remove(path: NodePath, scope: Scope) { +function remove( + path: NodePath, + scope: Scope, + exportsMap: Map, +) { let {node, parent} = path; if (isAssignmentExpression(node)) { let right; @@ -131,7 +164,7 @@ function remove(path: NodePath, scope: Scope) { // TODO missing test coverage // replace sequence expression with it's sole child path.parentPath.replaceWith(node); - remove(path.parentPath, scope); + remove(path.parentPath, scope, exportsMap); } else if ( //e.g. `exports.foo = bar;`, `bar` needs to be pure (an Identifier isn't ?!) isExpressionStatement(parent) && @@ -142,8 +175,8 @@ function remove(path: NodePath, scope: Scope) { // right side isn't pure path.replaceWith(node.right); } - } else if (isExportAssignment(path)) { - remove(path.parentPath.parentPath, scope); + } else if (isExportAssignment(path, exportsMap)) { + remove(path.parentPath.parentPath, scope, exportsMap); } else if (isWildcardDest(path)) { let wildcard = path.parent; invariant(isCallExpression(wildcard)); @@ -163,14 +196,14 @@ function remove(path: NodePath, scope: Scope) { isIdentifier(src) || (isObjectExpression(src) && src.properties.length === 0), ); - remove(path.parentPath, scope); + remove(path.parentPath, scope, exportsMap); } } else if (!path.removed) { if (isSequenceExpression(parent) && parent.expressions.length === 1) { // TODO missing test coverage // replace sequence expression with it's sole child path.parentPath.replaceWith(node); - remove(path.parentPath, scope); + remove(path.parentPath, scope, exportsMap); } else { pathRemove(path); } From 3a4e13fa424ef7c17dfc3d52ee1638a61dd58fdf Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Tue, 14 Apr 2020 23:31:54 +0200 Subject: [PATCH 2/3] Ignore `url(#...)` (`behavior` property) in Less assets (#4475) * Add test * Support `url(#...)` in less --- .../integration/less-url-behavior/index.less | 3 +++ packages/core/integration-tests/test/less.js | 17 +++++++++++++++++ .../transformers/less/src/LessTransformer.js | 12 ++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 packages/core/integration-tests/test/integration/less-url-behavior/index.less diff --git a/packages/core/integration-tests/test/integration/less-url-behavior/index.less b/packages/core/integration-tests/test/integration/less-url-behavior/index.less new file mode 100644 index 00000000000..93b69e599f4 --- /dev/null +++ b/packages/core/integration-tests/test/integration/less-url-behavior/index.less @@ -0,0 +1,3 @@ +.index { + behavior: url(#default#VML); +} diff --git a/packages/core/integration-tests/test/less.js b/packages/core/integration-tests/test/less.js index 889a53dc281..49a0e2b25e4 100644 --- a/packages/core/integration-tests/test/less.js +++ b/packages/core/integration-tests/test/less.js @@ -216,4 +216,21 @@ describe('less', function() { assert(css.includes('.a')); assert(css.includes('.b')); }); + + it('should ignore url() with IE behavior specifiers', async function() { + let b = await bundle( + path.join(__dirname, '/integration/less-url-behavior/index.less'), + ); + + assertBundles(b, [ + { + name: 'index.css', + assets: ['index.less'], + }, + ]); + + let css = await outputFS.readFile(path.join(distDir, 'index.css'), 'utf8'); + + assert(css.includes('url(#default#VML)')); + }); }); diff --git a/packages/transformers/less/src/LessTransformer.js b/packages/transformers/less/src/LessTransformer.js index 20cecf2c77d..295ca648810 100644 --- a/packages/transformers/less/src/LessTransformer.js +++ b/packages/transformers/less/src/LessTransformer.js @@ -55,10 +55,14 @@ function urlPlugin({asset}) { install(less, pluginManager) { const visitor = new less.visitors.Visitor({ visitUrl(node) { - node.value.value = asset.addURLDependency( - node.value.value, - node.currentFileInfo.filename, - ); + if ( + !node.value.value.startsWith('#') // IE's `behavior: url(#default#VML)`) + ) { + node.value.value = asset.addURLDependency( + node.value.value, + node.currentFileInfo.filename, + ); + } return node; }, }); From f9f7320bed19a9a2a7d75d5c6da657272b4b44ef Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Wed, 15 Apr 2020 00:14:15 +0200 Subject: [PATCH 3/3] Fix resolveSymbol when working across bundle boundary (#4467) * Development fixups * Really fix resolveSymbol with boundary * Add test * Add another test * Really really fix resolveSymbol for real * Cleanup --- packages/core/core/src/BundleGraph.js | 22 +++++++++------- packages/core/core/src/public/Asset.js | 11 ++++++-- packages/core/core/src/public/Dependency.js | 11 ++++++-- .../es6/re-export-bundle-boundary2/async.js | 4 +++ .../es6/re-export-bundle-boundary2/index.js | 3 +++ .../library/components.js | 4 +++ .../library/constants.js | 3 +++ .../library/index.js | 1 + .../es6/re-export-bundle-boundary3/index.js | 3 +++ .../media-card/index.js | 3 +++ .../theme/components.js | 4 +++ .../theme/createTheme.js | 3 +++ .../re-export-bundle-boundary3/theme/index.js | 5 ++++ .../integration-tests/test/scope-hoisting.js | 26 ++++++++++++++++++- packages/dev/babel-preset/index.js | 20 +++++++------- 15 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/async.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/index.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/components.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/constants.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/index.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/index.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/media-card/index.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/components.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/createTheme.js create mode 100644 packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/index.js diff --git a/packages/core/core/src/BundleGraph.js b/packages/core/core/src/BundleGraph.js index 8d58c504eb2..a84ff906be8 100644 --- a/packages/core/core/src/BundleGraph.js +++ b/packages/core/core/src/BundleGraph.js @@ -647,15 +647,9 @@ export default class BundleGraph { } // Resolve the export `symbol` of `asset` to the source, - // stopping at the first asset after leaving `bundle` (symbol is null in that case) + // stopping at the first asset after leaving `bundle` (symbol is nullish in that case) resolveSymbol(asset: Asset, symbol: Symbol, boundary: ?Bundle) { - if (boundary && !this.bundleHasAsset(boundary, asset)) { - return { - asset, - exportSymbol: symbol, - symbol: undefined, - }; - } + let assetOutside = boundary && !this.bundleHasAsset(boundary, asset); let identifier = asset.symbols.get(symbol); if (symbol === '*') { @@ -676,6 +670,11 @@ export default class BundleGraph { break; } + if (assetOutside) { + // We found the symbol, but `asset` is outside, return `asset` and the original symbol + break; + } + let { asset: resolvedAsset, symbol: resolvedSymbol, @@ -701,7 +700,12 @@ export default class BundleGraph { let resolved = this.getDependencyResolution(dep); if (!resolved) continue; let result = this.resolveSymbol(resolved, symbol, boundary); - if (result.symbol != null) { + if (result.symbol != undefined) { + if (assetOutside) { + // We found the symbol, but `asset` is outside, return `asset` and the original symbol + break; + } + return { asset: result.asset, symbol: result.symbol, diff --git a/packages/core/core/src/public/Asset.js b/packages/core/core/src/public/Asset.js index ffaf2e2ca5b..3621e775d94 100644 --- a/packages/core/core/src/public/Asset.js +++ b/packages/core/core/src/public/Asset.js @@ -20,7 +20,7 @@ import type { MutableAsset as IMutableAsset, PackageJSON, Stats, - Symbol, + Symbol as ISymbol, } from '@parcel/types'; import type {Asset as AssetValue, ParcelOptions} from '../types'; @@ -31,6 +31,8 @@ import UncommittedAsset from '../UncommittedAsset'; import CommittedAsset from '../CommittedAsset'; import {createEnvironment} from '../Environment'; +const inspect = Symbol.for('nodejs.util.inspect.custom'); + const assetValueToAsset: WeakMap = new WeakMap(); const assetValueToMutableAsset: WeakMap< AssetValue, @@ -76,6 +78,11 @@ class BaseAsset { _assetToAssetValue.set(this, asset.value); } + // $FlowFixMe + [inspect]() { + return `Asset(${this.filePath})`; + } + get id(): string { return this.#asset.value.id; } @@ -120,7 +127,7 @@ class BaseAsset { return this.#asset.value.sideEffects; } - get symbols(): Map { + get symbols(): Map { return this.#asset.value.symbols; } diff --git a/packages/core/core/src/public/Dependency.js b/packages/core/core/src/public/Dependency.js index 79b934f77bb..0fef3bc3a55 100644 --- a/packages/core/core/src/public/Dependency.js +++ b/packages/core/core/src/public/Dependency.js @@ -4,13 +4,15 @@ import type { Environment as IEnvironment, SourceLocation, Meta, - Symbol, + Symbol as ISymbol, } from '@parcel/types'; import type {Dependency as InternalDependency} from '../types'; import Environment from './Environment'; import Target from './Target'; import nullthrows from 'nullthrows'; +const inspect = Symbol.for('nodejs.util.inspect.custom'); + const internalDependencyToDependency: WeakMap< InternalDependency, Dependency, @@ -39,6 +41,11 @@ export default class Dependency implements IDependency { internalDependencyToDependency.set(dep, this); } + // $FlowFixMe + [inspect]() { + return `Dependency(${String(this.sourcePath)} -> ${this.moduleSpecifier})`; + } + get id(): string { return this.#dep.id; } @@ -98,7 +105,7 @@ export default class Dependency implements IDependency { return this.#dep.sourcePath; } - get symbols(): Map { + get symbols(): Map { return this.#dep.symbols; } diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/async.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/async.js new file mode 100644 index 00000000000..66df1add877 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/async.js @@ -0,0 +1,4 @@ +import { layers } from './library/index.js'; + +export default layers.a; + diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/index.js new file mode 100644 index 00000000000..d90cf3271ae --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/index.js @@ -0,0 +1,3 @@ +import t from "./library/components.js"; + +output = import("./async.js").then((c) => [c.default, t().a]); diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/components.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/components.js new file mode 100644 index 00000000000..ab092c14637 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/components.js @@ -0,0 +1,4 @@ +import { layers } from './constants.js'; +export default function(){ + return layers; +} diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/constants.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/constants.js new file mode 100644 index 00000000000..67f98a196f3 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/constants.js @@ -0,0 +1,3 @@ +export const layers = { + a: "foo", +}; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/index.js new file mode 100644 index 00000000000..548d69e6822 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary2/library/index.js @@ -0,0 +1 @@ +export * from "./constants.js"; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/index.js new file mode 100644 index 00000000000..54a1dc8861c --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/index.js @@ -0,0 +1,3 @@ +import { themed } from './theme/components.js'; + +output = import('./media-card/index.js').then(m => [m.default, themed()]) diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/media-card/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/media-card/index.js new file mode 100644 index 00000000000..5a1c8882ce3 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/media-card/index.js @@ -0,0 +1,3 @@ +import { layers } from '../theme/index.js'; + +export default Object.keys(layers); diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/components.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/components.js new file mode 100644 index 00000000000..11f7147e701 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/components.js @@ -0,0 +1,4 @@ +export { createTheme } from "./createTheme.js"; +export function themed() { + return "themed"; +} diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/createTheme.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/createTheme.js new file mode 100644 index 00000000000..d2767a1c85c --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/createTheme.js @@ -0,0 +1,3 @@ +export function createTheme() { + return "foo"; +} diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/index.js new file mode 100644 index 00000000000..5003f715f3a --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-bundle-boundary3/theme/index.js @@ -0,0 +1,5 @@ +export var layers = { + a: 1, + b: 2 +}; +export * from './createTheme.js'; diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js index 7b4967cab97..de4de40be33 100644 --- a/packages/core/integration-tests/test/scope-hoisting.js +++ b/packages/core/integration-tests/test/scope-hoisting.js @@ -201,7 +201,7 @@ describe('scope hoisting', function() { assert.equal(output, 15); }); - it('can import from a different bundle via a re-export', async function() { + it('can import from a different bundle via a re-export (1)', async function() { let b = await bundle( path.join( __dirname, @@ -225,6 +225,30 @@ describe('scope hoisting', function() { assert.deepEqual(output, ['operational', 'ui']); }); + it('can import from a different bundle via a re-export (2)', async function() { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/es6/re-export-bundle-boundary2/index.js', + ), + ); + + let output = await run(b); + assert.deepEqual(output, ['foo', 'foo']); + }); + + it('can import from its own bundle with a split package', async function() { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/es6/re-export-bundle-boundary3/index.js', + ), + ); + + let output = await run(b); + assert.deepEqual(output, [['a', 'b'], 'themed']); + }); + it('supports importing all exports re-exported from multiple modules deep', async function() { let b = await bundle( path.join( diff --git a/packages/dev/babel-preset/index.js b/packages/dev/babel-preset/index.js index e75ec51f14c..593f17a4fa5 100644 --- a/packages/dev/babel-preset/index.js +++ b/packages/dev/babel-preset/index.js @@ -4,17 +4,17 @@ module.exports = () => ({ require('@babel/preset-env'), { targets: { - node: 8 - } - } + node: 10, + }, + }, ], require('@babel/preset-react'), - require('@babel/preset-flow') + require('@babel/preset-flow'), ], plugins: [ require('@babel/plugin-proposal-class-properties'), require('@babel/plugin-proposal-nullish-coalescing-operator'), - require('@babel/plugin-proposal-optional-chaining') + require('@babel/plugin-proposal-optional-chaining'), ], env: { production: { @@ -23,10 +23,10 @@ module.exports = () => ({ // it can be removed through dead code elimination below [ 'babel-plugin-transform-inline-environment-variables', - {include: ['PARCEL_BUILD_ENV']} + {include: ['PARCEL_BUILD_ENV']}, ], - 'babel-plugin-minify-dead-code-elimination' - ] - } - } + 'babel-plugin-minify-dead-code-elimination', + ], + }, + }, });