Skip to content

Commit

Permalink
added no-content-hashing option. fixed css-hmr, content hashing is di…
Browse files Browse the repository at this point in the history
…sabled if HMR is active (#4506)

Co-authored-by: Niklas Mischkulnig <[email protected]>
  • Loading branch information
krisnye and mischnic authored Jun 16, 2020
1 parent bb592da commit 8fdd619
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 27 deletions.
3 changes: 2 additions & 1 deletion packages/core/core/src/PackagerRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,8 @@ export default class PackagerRunner {
let {inputFS, outputFS} = this.options;
let filePath = nullthrows(bundle.filePath);
let thisHashReference = bundle.hashReference;
if (filePath.includes(thisHashReference)) {
// Without content hashing, the hash reference is already the correct id
if (this.options.contentHash && filePath.includes(thisHashReference)) {
let thisNameHash = nullthrows(hashRefToNameHash.get(thisHashReference));
filePath = filePath.replace(thisHashReference, thisNameHash);
bundle.filePath = filePath;
Expand Down
4 changes: 3 additions & 1 deletion packages/core/core/src/public/MutableBundleGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ export default class MutableBundleGraph extends BundleGraph<IBundle>
id: bundleId,
value: {
id: bundleId,
hashReference: HASH_REF_PREFIX + bundleId,
hashReference: this.#options.contentHash
? HASH_REF_PREFIX + bundleId
: bundleId.slice(0, 8),
type: opts.type ?? nullthrows(entryAsset).type,
env: opts.env
? environmentToInternalEnvironment(opts.env)
Expand Down
2 changes: 2 additions & 0 deletions packages/core/core/src/resolveOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export default async function resolveOptions(
minify,
autoinstall: initialOptions.autoinstall ?? true,
hot: initialOptions.hot ?? null,
contentHash:
initialOptions.contentHash ?? initialOptions.mode === 'production',
serve: initialOptions.serve
? {
...initialOptions.serve,
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 @@ -147,6 +147,7 @@ export type ParcelOptions = {|
publicUrl: string,
distDir: ?FilePath,
hot: ?HMROptions,
contentHash: boolean,
serve: ServerOptions | false,
autoinstall: boolean,
logLevel: LogLevel,
Expand Down
1 change: 1 addition & 0 deletions packages/core/core/test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const DEFAULT_OPTIONS: ParcelOptions = {
lockFile: undefined,
autoinstall: false,
hot: undefined,
contentHash: true,
serve: false,
mode: 'development',
scopeHoist: false,
Expand Down
95 changes: 82 additions & 13 deletions packages/core/integration-tests/test/hmr.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import {
bundler,
defaultConfig,
getNextBuild,
overlayFS,
outputFS,
ncp,
outputFS,
overlayFS,
sleep,
} from '@parcel/test-utils';
import WebSocket from 'ws';
import json5 from 'json5';
import getPort from 'get-port';
import JSDOM from 'jsdom';

const config = {
...defaultConfig,
Expand Down Expand Up @@ -40,25 +42,27 @@ async function nextWSMessage(ws: WebSocket) {

describe('hmr', function() {
let subscription;
let ws;

beforeEach(async function() {
await outputFS.rimraf(path.join(__dirname, '/input'));
await ncp(
path.join(__dirname, '/integration/commonjs'),
path.join(__dirname, '/input'),
);
});

afterEach(async () => {
if (subscription) {
await subscription.unsubscribe();
}
subscription = null;
await closeSocket(ws);
});

describe('hmr server', () => {
let ws;
beforeEach(async function() {
await outputFS.rimraf(path.join(__dirname, '/input'));
await ncp(
path.join(__dirname, '/integration/commonjs'),
path.join(__dirname, '/input'),
);
});

afterEach(async () => {
await closeSocket(ws);
});

it('should emit an HMR update for the file that changed', async function() {
let port = await getPort();
let b = bundler(path.join(__dirname, '/input/index.js'), {
Expand Down Expand Up @@ -633,5 +637,70 @@ describe('hmr', function() {
assert(logs[0].trim().startsWith('[parcel] 🚨'));
assert(logs[1].trim().startsWith('[parcel] ✨'));
});*/

it('should update CSS link tags when a CSS asset is changed', async () => {
let testDir = path.join(__dirname, '/input');
await overlayFS.rimraf(testDir);
await overlayFS.mkdirp(testDir);
await ncp(path.join(__dirname, '/integration/hmr-css'), testDir);

let port = await getPort();
let b = bundler(path.join(testDir, 'index.html'), {
inputFS: overlayFS,
outputFS: overlayFS,
serve: {
https: false,
port,
host: '127.0.0.1',
},
hot: {port},
defaultConfig: {
...defaultConfig,
reporters: ['@parcel/reporter-dev-server'],
},
});

subscription = await b.watch();
let bundleEvent = await getNextBuild(b);
assert.equal(bundleEvent.type, 'buildSuccess');

let window;
try {
let dom = await JSDOM.JSDOM.fromURL(
'http://127.0.0.1:' + port + '/index.html',
{
runScripts: 'dangerously',
resources: 'usable',
pretendToBeVisual: true,
},
);
window = dom.window;
window.WebSocket = WebSocket;
await new Promise(res =>
dom.window.document.addEventListener('load', () => {
res();
}),
);
window.console.clear = () => {};
window.console.warn = () => {};

let initialHref = window.document.querySelector('link').href;

await overlayFS.copyFile(
path.join(testDir, 'index.2.css'),
path.join(testDir, 'index.css'),
);
assert.equal((await getNextBuild(b)).type, 'buildSuccess');
await sleep(200);

let newHref = window.document.querySelector('link').href;

assert.notStrictEqual(initialHref, newHref);
} finally {
if (window) {
window.close();
}
}
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background: blue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="./index.css" />
</head>
<body>
<h1>Hello world</h1>
<script src="index.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

19 changes: 11 additions & 8 deletions packages/core/integration-tests/test/react-refresh.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import assert from 'assert';
import path from 'path';
import {
bundler,
overlayFS as fs,
getNextBuild,
defaultConfig,
getNextBuild,
overlayFS as fs,
sleep,
} from '@parcel/test-utils';
import getPort from 'get-port';
import JSDOM from 'jsdom';
Expand Down Expand Up @@ -46,7 +47,7 @@ if (MessageChannel) {
assert.equal((await getNextBuild(b)).type, 'buildSuccess');

// Wait for the hmr-runtime to process the event
await new Promise(res => setTimeout(res, 100));
await sleep(100);

let [, indexNum, appNum, fooText, fooNum] = root.textContent.match(
/^([\d.]+) ([\d.]+) ([\w]+):([\d.]+)$/,
Expand All @@ -66,7 +67,7 @@ if (MessageChannel) {
assert.equal((await getNextBuild(b)).type, 'buildSuccess');

// Wait for the hmr-runtime to process the event
await new Promise(res => setTimeout(res, 100));
await sleep(100);

let [
,
Expand Down Expand Up @@ -94,7 +95,7 @@ if (MessageChannel) {
assert.equal((await getNextBuild(b)).type, 'buildSuccess');

// Wait for the hmr-runtime to process the event
await new Promise(res => setTimeout(res, 100));
await sleep(100);

let [, indexNum, appNum, fooText, fooNum] = root.textContent.match(
/^([\d.]+) ([\d.]+) ([\w]+):([\d.]+)$/,
Expand Down Expand Up @@ -139,7 +140,7 @@ if (MessageChannel) {
assert.equal((await getNextBuild(b)).type, 'buildSuccess');

// Wait for the hmr-runtime to process the event
await new Promise(res => setTimeout(res, 100));
await sleep(100);

let [, indexNum, appNum, fooText, fooNum] = root.textContent.match(
/^([\d.]+) ([\d.]+) ([\w]+):([\d.]+)$/,
Expand Down Expand Up @@ -173,7 +174,9 @@ async function setup(entry) {
port,
host: '127.0.0.1',
},
hot: true,
hot: {
port,
},
defaultConfig: {
...defaultConfig,
reporters: ['@parcel/reporter-dev-server'],
Expand Down Expand Up @@ -209,7 +212,7 @@ async function setup(entry) {
);
// ReactDOM.render
await window.parcelRequire(bundle.getMainEntry().id).default();
await new Promise(res => setTimeout(res, 100));
await sleep(100);

let [, indexNum, appNum, fooText, fooNum] = root.textContent.match(
/^([\d.]+) ([\d.]+) ([\w]+):([\d.]+)$/,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/parcel/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const commonOptions = {
'--cache-dir <path>': 'set the cache directory. defaults to ".parcel-cache"',
'--no-source-maps': 'disable sourcemaps',
'--no-autoinstall': 'disable autoinstall',
'--no-content-hash': 'disable content hashing',
'--target [name]': [
'only build given target(s)',
(val, list) => list.concat([val]),
Expand Down Expand Up @@ -321,6 +322,7 @@ async function normalizeOptions(command): Promise<InitialParcelOptions> {
publicUrl: command.publicUrl,
distDir: command.distDir,
hot: hmr,
contentHash: hmr ? false : command.contentHash,
serve,
targets: command.target.length > 0 ? command.target : null,
autoinstall: command.autoinstall ?? true,
Expand Down
1 change: 1 addition & 0 deletions packages/core/test-utils/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export function bundler(
browsers: ['last 1 Chrome version'],
node: '8',
},
contentHash: true,
...opts,
});
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export type InitialParcelOptions = {|
+publicUrl?: string,
+distDir?: FilePath,
+hot?: ?HMROptions,
+contentHash?: boolean,
+serve?: InitialServerOptions | false,
+autoinstall?: boolean,
+logLevel?: LogLevel,
Expand Down
41 changes: 37 additions & 4 deletions packages/runtimes/hmr/src/loaders/hmr-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') {
// Handle HMR Update
var handled = false;
assets.forEach(asset => {
var didAccept = hmrAcceptCheck(global.parcelRequire, asset.id);
var didAccept =
asset.type === 'css' ||
hmrAcceptCheck(global.parcelRequire, asset.id);
if (didAccept) {
handled = true;
}
Expand Down Expand Up @@ -172,6 +174,39 @@ function getParents(bundle, id) {
return parents;
}

function updateLink(link) {
var newLink = link.cloneNode();
newLink.onload = function() {
if (link.parentNode !== null) {
link.parentNode.removeChild(link);
}
};
newLink.setAttribute(
'href',
link.getAttribute('href').split('?')[0] + '?' + Date.now(),
);
link.parentNode.insertBefore(newLink, link.nextSibling);
}

var cssTimeout = null;
function reloadCSS() {
if (cssTimeout) {
return;
}

cssTimeout = setTimeout(function() {
var links = document.querySelectorAll('link[rel="stylesheet"]');
for (var i = 0; i < links.length; i++) {
var absolute = /^https?:\/\//i.test(links[i].getAttribute('href'));
if (!absolute) {
updateLink(links[i]);
}
}

cssTimeout = null;
}, 50);
}

function hmrApply(bundle, asset) {
var modules = bundle.modules;
if (!modules) {
Expand All @@ -180,9 +215,7 @@ function hmrApply(bundle, asset) {

if (modules[asset.id] || !bundle.parent) {
if (asset.type === 'css') {
var newStyle = document.createElement('style');
newStyle.innerHTML = asset.output;
document.body.appendChild(newStyle);
reloadCSS();
} else {
var fn = new Function('require', 'module', 'exports', asset.output);
modules[asset.id] = [fn, asset.depsByBundle[bundle.HMR_BUNDLE_ID]];
Expand Down

0 comments on commit 8fdd619

Please sign in to comment.