Skip to content

Commit

Permalink
HMR fixes for URL dependencies (#6986)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett authored Oct 1, 2021
1 parent 5277d24 commit 7468864
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 36 deletions.
22 changes: 12 additions & 10 deletions packages/core/core/src/applyRuntimes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {ContentKey} from '@parcel/graph';
import type {Dependency, NamedBundle as INamedBundle} from '@parcel/types';
import type {SharedReference} from '@parcel/workers';
import type {
Asset,
AssetGroup,
Bundle as InternalBundle,
Config,
Expand All @@ -18,7 +19,7 @@ import path from 'path';
import assert from 'assert';
import invariant from 'assert';
import nullthrows from 'nullthrows';
import AssetGraph, {nodeFromAssetGroup} from './AssetGraph';
import {nodeFromAssetGroup} from './AssetGraph';
import BundleGraph from './public/BundleGraph';
import InternalBundleGraph, {bundleGraphEdgeTypes} from './BundleGraph';
import {NamedBundle} from './public/Bundle';
Expand Down Expand Up @@ -58,7 +59,7 @@ export default async function applyRuntimes({
previousDevDeps: Map<string, string>,
devDepRequests: Map<string, DevDepRequest>,
configs: Map<string, Config>,
|}): Promise<void> {
|}): Promise<Map<string, Asset>> {
let runtimes = await config.getRuntimes();
let connections: Array<RuntimeConnection> = [];

Expand Down Expand Up @@ -137,11 +138,10 @@ export default async function applyRuntimes({
await runDevDepRequest(api, devDepRequest);
}

let runtimesAssetGraph = await reconcileNewRuntimes(
api,
connections,
optionsRef,
);
let {
assetGraph: runtimesAssetGraph,
changedAssets,
} = await reconcileNewRuntimes(api, connections, optionsRef);

let runtimesGraph = InternalBundleGraph.fromAssetGraph(
runtimesAssetGraph,
Expand Down Expand Up @@ -245,13 +245,15 @@ export default async function applyRuntimes({
bundleGraph._graph.addEdge(dependencyNodeId, bundleGraphRuntimeNodeId);
}
}

return changedAssets;
}

async function reconcileNewRuntimes(
function reconcileNewRuntimes(
api: RunAPI,
connections: Array<RuntimeConnection>,
optionsRef: SharedReference,
): Promise<AssetGraph> {
) {
let assetGroups = connections.map(t => t.assetGroup);
let request = createAssetGraphRequest({
name: 'Runtimes',
Expand All @@ -260,5 +262,5 @@ async function reconcileNewRuntimes(
});

// rebuild the graph
return (await api.runRequest(request, {force: true})).assetGraph;
return api.runRequest(request, {force: true});
}
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 @@ -191,7 +191,9 @@ export default class MutableBundleGraph extends BundleGraph<IBundle>
id: bundleId,
value: {
id: bundleId,
hashReference: HASH_REF_PREFIX + bundleId,
hashReference: this.#options.shouldContentHash
? HASH_REF_PREFIX + bundleId
: bundleId.slice(-8),
type: opts.entryAsset ? opts.entryAsset.type : opts.type,
env: opts.env
? environmentToInternalEnvironment(opts.env)
Expand Down
34 changes: 28 additions & 6 deletions packages/core/core/src/requests/BundleGraphRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {SharedReference} from '@parcel/workers';
import type ParcelConfig, {LoadedPlugin} from '../ParcelConfig';
import type {StaticRunOpts, RunAPI} from '../RequestTracker';
import type {
Asset,
Bundle as InternalBundle,
Config,
DevDepRequest,
Expand Down Expand Up @@ -63,10 +64,15 @@ type RunInput = {|
...StaticRunOpts,
|};

type BundleGraphResult = {|
bundleGraph: InternalBundleGraph,
changedAssets: Map<string, Asset>,
|};

type BundleGraphRequest = {|
id: string,
+type: 'bundle_graph_request',
run: RunInput => Async<InternalBundleGraph>,
run: RunInput => Async<BundleGraphResult>,
input: BundleGraphRequestInput,
|};

Expand Down Expand Up @@ -164,7 +170,7 @@ class BundlerRunner {
await runDevDepRequest(this.api, devDepRequest);
}

async bundle(graph: AssetGraph): Promise<InternalBundleGraph> {
async bundle(graph: AssetGraph): Promise<BundleGraphResult> {
report({
type: 'buildProgress',
phase: 'bundling',
Expand Down Expand Up @@ -194,7 +200,13 @@ class BundlerRunner {
// Deserialize, and store the original buffer in an in memory cache so we avoid
// re-serializing it when sending to workers, and in build mode, when writing to cache on shutdown.
let graph = deserializeToCache(cached);
this.api.storeResult(graph, cacheKey);
this.api.storeResult(
{
bundleGraph: graph,
changedAssets: new Map(),
},
cacheKey,
);
return graph;
}
}
Expand Down Expand Up @@ -273,7 +285,7 @@ class BundlerRunner {

await this.nameBundles(internalBundleGraph);

await applyRuntimes({
let changedAssets = await applyRuntimes({
bundleGraph: internalBundleGraph,
api: this.api,
config: this.config,
Expand All @@ -300,8 +312,18 @@ class BundlerRunner {

// Recompute the cache key to account for new dev dependencies and invalidations.
cacheKey = await this.getCacheKey(graph);
this.api.storeResult(internalBundleGraph, cacheKey);
return internalBundleGraph;
this.api.storeResult(
{
bundleGraph: internalBundleGraph,
changedAssets: new Map(),
},
cacheKey,
);

return {
bundleGraph: internalBundleGraph,
changedAssets,
};
}

async getCacheKey(assetGraph: AssetGraph): Promise<string> {
Expand Down
7 changes: 6 additions & 1 deletion packages/core/core/src/requests/ParcelBuildRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ async function run({input, api, options}: RunInput) {
optionsRef,
});

let bundleGraph = await api.runRequest(bundleGraphRequest);
let {bundleGraph, changedAssets: changedRuntimeAssets} = await api.runRequest(
bundleGraphRequest,
);
for (let [id, asset] of changedRuntimeAssets) {
changedAssets.set(id, asset);
}

// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381 (Windows only)
dumpGraphToGraphViz(bundleGraph._graph, 'BundleGraph', bundleGraphEdgeTypes);
Expand Down
4 changes: 4 additions & 0 deletions packages/optimizers/image/src/ImageOptimizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {optimize} from '../native';

export default (new Optimizer({
async optimize({bundle, contents}) {
if (!bundle.env.shouldOptimize) {
return {contents};
}

let buffer = await blobToBuffer(contents);
let optimized = optimize(bundle.type, buffer);
return {
Expand Down
43 changes: 38 additions & 5 deletions packages/reporters/dev-server/src/HMRServer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type {BuildSuccessEvent, PluginOptions} from '@parcel/types';
import type {BuildSuccessEvent, Dependency, PluginOptions} from '@parcel/types';
import type {Diagnostic} from '@parcel/diagnostic';
import type {AnsiDiagnosticResult} from '@parcel/utils';
import type {ServerError, HMRServerOptions} from './types.js.flow';
Expand Down Expand Up @@ -95,11 +95,35 @@ export default class HMRServer {
async emitUpdate(event: BuildSuccessEvent) {
this.unresolvedError = null;

let changedAssets = Array.from(event.changedAssets.values());
if (changedAssets.length === 0) return;
let changedAssets = new Set(event.changedAssets.values());
if (changedAssets.size === 0) return;

let queue = new PromiseQueue({maxConcurrent: FS_CONCURRENCY});
for (let asset of changedAssets) {
if (asset.type !== 'js') {
// If all of the incoming dependencies of the asset actually resolve to a JS asset
// rather than the original, we can mark the runtimes as changed instead. URL runtimes
// have a cache busting query param added with HMR enabled which will trigger a reload.
let runtimes = new Set();
let incomingDeps = event.bundleGraph.getIncomingDependencies(asset);
let isOnlyReferencedByRuntimes = incomingDeps.every(dep => {
let resolved = event.bundleGraph.getResolvedAsset(dep);
let isRuntime = resolved?.type === 'js' && resolved !== asset;
if (resolved && isRuntime) {
runtimes.add(resolved);
}
return isRuntime;
});

if (isOnlyReferencedByRuntimes) {
for (let runtime of runtimes) {
changedAssets.add(runtime);
}

continue;
}
}

queue.add(async () => {
let dependencies = event.bundleGraph.getDependencies(asset);
let depsByBundle = {};
Expand All @@ -108,7 +132,7 @@ export default class HMRServer {
for (let dep of dependencies) {
let resolved = event.bundleGraph.getResolvedAsset(dep, bundle);
if (resolved) {
deps[dep.specifier] = event.bundleGraph.getAssetPublicId(
deps[getSpecifier(dep)] = event.bundleGraph.getAssetPublicId(
resolved,
);
}
Expand All @@ -119,7 +143,8 @@ export default class HMRServer {
return {
id: event.bundleGraph.getAssetPublicId(asset),
type: asset.type,
output: await asset.getCode(),
// No need to send the contents of non-JS assets to the client.
output: asset.type === 'js' ? await asset.getCode() : '',
envHash: asset.env.id,
depsByBundle,
};
Expand Down Expand Up @@ -153,3 +178,11 @@ export default class HMRServer {
}
}
}

function getSpecifier(dep: Dependency): string {
if (typeof dep.meta.placeholder === 'string') {
return dep.meta.placeholder;
}

return dep.specifier;
}
3 changes: 2 additions & 1 deletion packages/runtimes/hmr/src/HMRRuntime.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export default (new Runtime({
bundle.type !== 'js' ||
!options.hmrOptions ||
bundle.env.isLibrary ||
bundle.env.isWorklet()
bundle.env.isWorklet() ||
bundle.env.sourceType === 'script'
) {
return;
}
Expand Down
28 changes: 17 additions & 11 deletions packages/runtimes/hmr/src/loaders/hmr-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,15 +289,14 @@ function hmrApply(bundle /*: ParcelRequire */, asset /*: HMRAsset */) {

if (asset.type === 'css') {
reloadCSS();
return;
}

let deps = asset.depsByBundle[bundle.HMR_BUNDLE_ID];
if (deps) {
var fn = new Function('require', 'module', 'exports', asset.output);
modules[asset.id] = [fn, deps];
} else if (bundle.parent) {
hmrApply(bundle.parent, asset);
} else if (asset.type === 'js') {
let deps = asset.depsByBundle[bundle.HMR_BUNDLE_ID];
if (deps) {
var fn = new Function('require', 'module', 'exports', asset.output);
modules[asset.id] = [fn, deps];
} else if (bundle.parent) {
hmrApply(bundle.parent, asset);
}
}
}

Expand All @@ -322,7 +321,7 @@ function hmrAcceptCheck(
}

if (checkedAssets[id]) {
return;
return true;
}

checkedAssets[id] = true;
Expand All @@ -335,7 +334,14 @@ function hmrAcceptCheck(
return true;
}

return getParents(module.bundle.root, id).some(function(v) {
let parents = getParents(module.bundle.root, id);

// If no parents, the asset is new. Prevent reloading the page.
if (!parents.length) {
return true;
}

return parents.some(function(v) {
return hmrAcceptCheck(v[0], v[1], null);
});
}
Expand Down
7 changes: 6 additions & 1 deletion packages/runtimes/js/src/JSRuntime.js
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,12 @@ function getRelativePathExpr(
);
}

return JSON.stringify(relativePath);
let res = JSON.stringify(relativePath);
if (options.hmrOptions) {
res += ' + "?" + Date.now()';
}

return res;
}

function getAbsoluteUrlExpr(relativePathExpr: string, bundle: NamedBundle) {
Expand Down

0 comments on commit 7468864

Please sign in to comment.