Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fixes #6541

Merged
merged 8 commits into from
Jul 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/core/core/src/AssetGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,9 +475,14 @@ export default class AssetGraph extends ContentGraph<AssetGraphNode> {
this.normalizeEnvironment(dep);
let depNode = nodeFromDep(dep);
let existing = this.getNodeByContentKey(depNode.id);
if (existing) {
invariant(existing.type === 'dependency');
depNode.value.meta = existing.value.meta;
if (
existing?.type === 'dependency' &&
existing.value.resolverMeta != null
) {
depNode.value.meta = {
...depNode.value.meta,
...existing.value.resolverMeta,
};
}
let dependentAsset = dependentAssets.find(
a => a.uniqueKey === dep.specifier,
Expand Down
1 change: 1 addition & 0 deletions packages/core/core/src/RequestTracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ export class RequestGraph extends ContentGraph<
this.invalidNodeIds.delete(nodeId);
this.incompleteNodeIds.delete(nodeId);
this.incompleteNodePromises.delete(nodeId);
this.unpredicatableNodeIds.delete(nodeId);
let node = nullthrows(this.getNode(nodeId));
if (node.type === 'glob') {
this.globNodeIds.delete(nodeId);
Expand Down
1 change: 1 addition & 0 deletions packages/core/core/src/requests/PathRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ export class ResolverRunner {

if (result) {
if (result.meta) {
dependency.resolverMeta = result.meta;
dependency.meta = {
...dependency.meta,
...result.meta,
Expand Down
1 change: 1 addition & 0 deletions packages/core/core/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export type Dependency = {|
loc: ?SourceLocation,
env: Environment,
meta: Meta,
resolverMeta?: ?Meta,
target: ?Target,
sourceAssetId: ?string,
sourcePath: ?string,
Expand Down
121 changes: 121 additions & 0 deletions packages/core/integration-tests/test/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -5276,6 +5276,75 @@ describe('cache', function() {
let res = await run(b.bundleGraph);
assert.equal(res, 4);
});

it('should invalidate when a terser config is modified', async function() {
let b = await testCache({
mode: 'production',
async setup() {
await overlayFS.writeFile(
path.join(inputDir, '.terserrc'),
JSON.stringify({
mangle: false,
}),
);
},
async update(b) {
let contents = await overlayFS.readFile(
b.bundleGraph.getBundles()[0].filePath,
'utf8',
);
assert(contents.includes('$parcel$interopDefault'));

await overlayFS.writeFile(
path.join(inputDir, '.terserrc'),
JSON.stringify({
mangle: true,
}),
);
},
});

let contents = await overlayFS.readFile(
b.bundleGraph.getBundles()[0].filePath,
'utf8',
);
assert(!contents.includes('$parcel$interopDefault'));
});

it('should invalidate when an htmlnano config is modified', async function() {
let b = await testCache({
mode: 'production',
entries: ['src/index.html'],
async setup() {
await overlayFS.writeFile(
path.join(inputDir, '.htmlnanorc'),
JSON.stringify({
removeAttributeQuotes: true,
}),
);
},
async update(b) {
let contents = await overlayFS.readFile(
b.bundleGraph.getBundles()[0].filePath,
'utf8',
);
assert(contents.includes('type=module'));

await overlayFS.writeFile(
path.join(inputDir, '.htmlnanorc'),
JSON.stringify({
removeAttributeQuotes: false,
}),
);
},
});

let contents = await overlayFS.readFile(
b.bundleGraph.getBundles()[0].filePath,
'utf8',
);
assert(contents.includes('type="module"'));
});
});

describe('scope hoisting', function() {
Expand All @@ -5284,6 +5353,58 @@ describe('cache', function() {
it('should support updating sideEffects config', function() {});

it('should support removing sideEffects config', function() {});

it('should wrap modules when they become conditional', async function() {
let b = await testCache(
{
defaultTargetOptions: {
shouldScopeHoist: true,
},
entries: ['a.js'],
async setup() {
let contents = await overlayFS.readFile(
path.join(inputDir, 'a.js'),
'utf8',
);
await overlayFS.writeFile(
path.join(inputDir, 'a.js'),
contents.replace(/if \(b\) \{((?:.|\n)+)\}/, '$1'),
);
},
async update(b) {
let out = [];
await run(b.bundleGraph, {
b: false,
output(o) {
out.push(o);
},
});

assert.deepEqual(out, ['a', 'b', 'c', 'd']);

let contents = await overlayFS.readFile(
path.join(
__dirname,
'integration/scope-hoisting/commonjs/require-conditional/a.js',
),
'utf8',
);
await overlayFS.writeFile(path.join(inputDir, 'a.js'), contents);
},
},
'scope-hoisting/commonjs/require-conditional',
);

let out = [];
await run(b.bundleGraph, {
b: false,
output(o) {
out.push(o);
},
});

assert.deepEqual(out, ['a', 'd']);
});
});

describe('runtime', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = new Promise(resolve => {
let b = require('./async');
resolve(b + 3);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
if (typeof require !== 'undefined' && require.extensions) {
require.extensions['.handlebars'] = extension;
require.extensions['.hbs'] = extension;
}
19 changes: 17 additions & 2 deletions packages/core/integration-tests/test/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -2163,8 +2163,8 @@ describe('javascript', function() {
assert(!js.includes('local.a'));
});

it('should use uglify config', async function() {
await bundle(path.join(__dirname, '/integration/uglify-config/index.js'), {
it('should use terser config', async function() {
await bundle(path.join(__dirname, '/integration/terser-config/index.js'), {
defaultTargetOptions: {
shouldOptimize: true,
shouldScopeHoist: false,
Expand Down Expand Up @@ -3569,6 +3569,21 @@ describe('javascript', function() {
assert.equal(await run(b), 2);
});

it('should only detect requires that are returned from the promise', async () => {
let b = await bundle(
path.join(__dirname, '/integration/require-async/sync.js'),
);

assertBundles(b, [
{
name: 'sync.js',
assets: ['sync.js', 'async.js'],
},
]);

assert.equal(await run(b), 5);
});

it('should detect parcel style async requires in commonjs', async () => {
let b = await bundle(
path.join(__dirname, '/integration/require-async/parcel.js'),
Expand Down
11 changes: 11 additions & 0 deletions packages/core/integration-tests/test/scope-hoisting.js
Original file line number Diff line number Diff line change
Expand Up @@ -4764,6 +4764,17 @@ describe('scope hoisting', function() {
assert.equal(await run(b), 'foo');
});

it('replaces properties of require with undefined', async function() {
let b = await bundle(
path.join(
__dirname,
'/integration/scope-hoisting/commonjs/require-extensions/index.js',
),
);

await run(b);
});

it('should support two aliases to the same module', async function() {
let b = await bundle(
path.join(
Expand Down
1 change: 0 additions & 1 deletion packages/optimizers/htmlnano/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
},
"dependencies": {
"@parcel/plugin": "2.0.0-beta.3.1",
"@parcel/utils": "2.0.0-beta.3.1",
"htmlnano": "^1.0.0",
"nullthrows": "^1.1.1",
"posthtml": "^0.15.1"
Expand Down
27 changes: 17 additions & 10 deletions packages/optimizers/htmlnano/src/HTMLNanoOptimizer.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
// @flow strict-local

import htmlnano from 'htmlnano';
import {loadConfig} from '@parcel/utils';
import {Optimizer} from '@parcel/plugin';
import posthtml from 'posthtml';
import path from 'path';

export default (new Optimizer({
async optimize({bundle, contents, map, options}) {
async loadConfig({config, options}) {
let userConfig = await config.getConfigFrom(
path.join(options.entryRoot, 'index.html'),
['.htmlnanorc', '.htmlnanorc.js'],
);

if (userConfig) {
let isJavascript = path.extname(userConfig.filePath) === '.js';
if (isJavascript) {
config.invalidateOnStartup();
}
}

return userConfig?.contents;
},
async optimize({bundle, contents, map, config}) {
if (!bundle.env.shouldOptimize) {
return {contents, map};
}
Expand All @@ -18,16 +32,9 @@ export default (new Optimizer({
);
}

let userConfig = await loadConfig(
options.inputFS,
path.join(options.entryRoot, 'index.html'),
['.htmlnanorc', '.htmlnanorc.js'],
options.projectRoot,
);

const htmlNanoConfig = {
minifyJs: false,
...userConfig?.config,
...config,
};

return {
Expand Down
35 changes: 25 additions & 10 deletions packages/optimizers/terser/src/TerserOptimizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,45 @@
import nullthrows from 'nullthrows';
import {minify} from 'terser';
import {Optimizer} from '@parcel/plugin';
import {blobToString, loadConfig} from '@parcel/utils';
import {blobToString} from '@parcel/utils';
import SourceMap from '@parcel/source-map';
import ThrowableDiagnostic, {escapeMarkdown} from '@parcel/diagnostic';

import path from 'path';

export default (new Optimizer({
async optimize({contents, map, bundle, options, getSourceMapReference}) {
async loadConfig({config, options}) {
let userConfig = await config.getConfigFrom(
path.join(options.entryRoot, 'index'),
['.terserrc', '.terserrc.js'],
);

if (userConfig) {
let isJavascript = path.extname(userConfig.filePath) === '.js';
if (isJavascript) {
config.invalidateOnStartup();
}
}

return userConfig?.contents;
},
async optimize({
contents,
map,
bundle,
config: userConfig,
options,
getSourceMapReference,
}) {
if (!bundle.env.shouldOptimize) {
return {contents, map};
}

let code = await blobToString(contents);

let userConfig = await loadConfig(
options.inputFS,
path.join(options.entryRoot, 'index'),
['.terserrc', '.uglifyrc', '.uglifyrc.js', '.terserrc.js'],
options.projectRoot,
);

let originalMap = map ? await map.stringify({}) : null;
let config = {
...userConfig?.config,
...userConfig,
sourceMap: bundle.env.sourceMap
? {
filename: path.relative(
Expand Down