Skip to content
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

feat: send data for fallback if hmr apply fails #687

Merged
merged 2 commits into from
Oct 9, 2018
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions hot.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const log = {
};
const refresh = 'Application needs to be restarted in order to apply the changes.';
const hotOptions = {
ignoreUnaccepted: true,
ignoreDeclined: true,
ignoreErrored: true,
ignoreUnaccepted: false,
ignoreDeclined: false,
ignoreErrored: false,
onUnaccepted(data) {
const chain = [].concat(data.chain);
const last = chain[chain.length - 1];
Expand All @@ -30,10 +30,11 @@ const hotOptions = {
},
};

let lastHash;
let nextHash;
let currentHash;

function upToDate() {
return lastHash.indexOf(__webpack_hash__) >= 0;
return nextHash.indexOf(__webpack_hash__) >= 0;
}

function result(modules, appliedModules) {
Expand Down Expand Up @@ -90,17 +91,16 @@ function check(options) {
result(modules, appliedModules);

if (upToDate()) {
log.info('App is up to date.');
//Do not modify message - CLI depends on this exact content to determine hmr operation status.
log.info(`Successfully applied update with hmr hash ${currentHash}. App is up to date.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you extract the messages to constants?

}
})
.catch((err) => {
const status = module.hot.status();
if (['abort', 'fail'].indexOf(status) >= 0) {
log.warn(`Cannot apply update. ${refresh}`);
//Do not modify message - CLI depends on this exact content to determine hmr operation status.
log.warn(`Cannot apply update with hmr hash ${currentHash}.`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment to not change - it will break CLI

log.warn(err.stack || err.message);
if (options.reload) {
window.location.reload();
}
} else {
log.warn(`Update failed: ${err.stack || err.message}`);
}
Expand All @@ -123,13 +123,14 @@ if (module.hot) {
log.error('Hot Module Replacement is disabled.');
}

function update(currentHash, options) {
lastHash = currentHash;
function update(latestHash, options) {
nextHash = latestHash;
if (!upToDate()) {
const status = module.hot.status();

if (status === 'idle') {
log.info('Checking for updates to the bundle.');
//Do not modify message - CLI depends on this exact content to determine hmr operation status.
log.info(`Checking for updates to the bundle with hmr hash ${currentHash}.`);
check(options);
} else if (['abort', 'fail'].indexOf(status) >= 0) {
log.warn(
Expand All @@ -139,24 +140,24 @@ function update(currentHash, options) {
}
};

function getCurrentHash(currentHash, getFileContent) {
const file = getFileContent(`${currentHash}.hot-update.json`);
function getNextHash(hash, getFileContent) {
const file = getFileContent(`${hash}.hot-update.json`);
return file.readText().then(hotUpdateContent => {
if(hotUpdateContent) {
const manifest = JSON.parse(hotUpdateContent);
const newHash = manifest.h;
return getCurrentHash(newHash, getFileContent);
return getNextHash(newHash, getFileContent);
} else {
return Promise.resolve(currentHash);
return Promise.resolve(hash);
}
}).catch(error => Promise.reject(error));
}

module.exports = function checkState(initialHash, getFileContent) {
getCurrentHash(initialHash, getFileContent).then(currentHash => {
if(currentHash != initialHash) {
update(currentHash, {});
currentHash = initialHash;
getNextHash(initialHash, getFileContent).then(nextHash => {
if(nextHash != initialHash) {
update(nextHash, {});
}
})
}

}
13 changes: 10 additions & 3 deletions lib/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { existsSync } = require("fs");
const readline = require("readline");

const { messages } = require("../plugins/WatchStateLoggerPlugin");
const { buildEnvData, debuggingEnabled } = require("./utils");
const { buildEnvData, debuggingEnabled, getUpdatedEmittedFiles } = require("./utils");

let hasBeenInvoked = false;

Expand Down Expand Up @@ -80,11 +80,18 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $projectData, $
return;
}

const result = getUpdatedEmittedFiles(message.emittedFiles);

if (hookArgs.hmrData && hookArgs.hmrData.fallbackFiles) {
hookArgs.hmrData.fallbackFiles[platform] = result.fallbackFiles;
hookArgs.hmrData.hash = result.hash || "";
}

if (hookArgs.filesToSyncMap && hookArgs.startSyncFilesTimeout) {
hookArgs.filesToSyncMap[platform] = message.emittedFiles;
hookArgs.filesToSyncMap[platform] = result.emittedFiles;
hookArgs.startSyncFilesTimeout(platform);
} else if (hookArgs.filesToSync && hookArgs.startSyncFilesTimeout) {
hookArgs.filesToSync.push(...message.emittedFiles);
hookArgs.filesToSync.push(...result.emittedFiles);
hookArgs.startSyncFilesTimeout(platform);
}
}
Expand Down
44 changes: 43 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,46 @@ function buildEnvData($projectData, platform, env) {
return envData;
}

/**
* Checks if there's a file in the following pattern 5e0326f3bb50f9f26cf0.hot-update.json
* if yes this is a HMR update and remove all bundle files as we don't need them to be synced,
* but only the update chunks
*/
function getUpdatedEmittedFiles(emittedFiles) {
let fallbackFiles = [];
let hotHash;
if (emittedFiles.some(x => x.endsWith('.hot-update.json'))) {
let result = emittedFiles.slice();
const hotUpdateScripts = emittedFiles.filter(x => x.endsWith('.hot-update.js'));
hotUpdateScripts.forEach(hotUpdateScript => {
const { name, hash } = parseHotUpdateChunkName(hotUpdateScript);
hotHash = hash;
// remove bundle/vendor.js files if there's a bundle.XXX.hot-update.js or vendor.XXX.hot-update.js
result = result.filter(file => file !== `${name}.js`);
});
//if applying of hot update fails, we must fallback to the full files
fallbackFiles = emittedFiles.filter(file => result.indexOf(file) === -1);
return { emittedFiles: result, fallbackFiles, hash: hotHash };
}
else {
return { emittedFiles, fallbackFiles };
}
}

/**
* Parse the filename of the hot update chunk.
* @param name bundle.deccb264c01d6d42416c.hot-update.js
* @returns { name: string, hash: string } { name: 'bundle', hash: 'deccb264c01d6d42416c' }
*/
function parseHotUpdateChunkName(name) {
const matcher = /^(.+)\.(.+)\.hot-update/gm;
const matches = matcher.exec(name);
return {
name: matches[1] || "",
hash: matches[2] || "",
};
}

function shouldSnapshot(config) {
const platformSupportsSnapshot = isAndroid(config.platform);
const osSupportsSnapshot = os.type() !== "Windows_NT";
Expand All @@ -39,5 +79,7 @@ function shouldSnapshot(config) {
module.exports = {
buildEnvData,
debuggingEnabled,
shouldSnapshot
shouldSnapshot,
getUpdatedEmittedFiles,
parseHotUpdateChunkName
};
40 changes: 2 additions & 38 deletions plugins/WatchStateLoggerPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { join } from "path";
import { writeFileSync, readFileSync } from "fs";
const utils = require("../lib/utils");

export enum messages {
compilationComplete = "Webpack compilation complete.",
Expand Down Expand Up @@ -40,8 +41,6 @@ export class WatchStateLoggerPlugin {
WatchStateLoggerPlugin.rewriteHotUpdateChunk(compiler, compilation, emittedFiles);
}

emittedFiles = WatchStateLoggerPlugin.getUpdatedEmittedFiles(emittedFiles);

// provide fake paths to the {N} CLI - relative to the 'app' folder
// in order to trigger the livesync process
const emittedFilesFakePaths = emittedFiles
Expand All @@ -64,7 +63,7 @@ export class WatchStateLoggerPlugin {
return;
}

const { name } = this.parseHotUpdateChunkName(chunk);
const { name } = utils.parseHotUpdateChunkName(chunk);
if (!name) {
return;
}
Expand Down Expand Up @@ -92,26 +91,6 @@ export class WatchStateLoggerPlugin {
return content.substring(startIndex, endIndex);
}

/**
* Checks if there's a file in the following pattern 5e0326f3bb50f9f26cf0.hot-update.json
* if yes this is a HMR update and remove all bundle files as we don't need them to be synced,
* but only the update chunks
*/
static getUpdatedEmittedFiles(emittedFiles) {
if(emittedFiles.some(x => x.endsWith('.hot-update.json'))) {
let result = emittedFiles.slice();
const hotUpdateScripts = emittedFiles.filter(x => x.endsWith('.hot-update.js'));
hotUpdateScripts.forEach(hotUpdateScript => {
const { name } = this.parseHotUpdateChunkName(hotUpdateScript);
// remove bundle/vendor.js files if there's a bundle.XXX.hot-update.js or vendor.XXX.hot-update.js
result = result.filter(file => file !== `${name}.js`);
});
return result;
} else {
return emittedFiles;
}
}

/**
* Gets the webpackHotUpdate call with updated modules not to include the ones with errors
*/
Expand All @@ -130,19 +109,4 @@ export class WatchStateLoggerPlugin {
}
webpackHotUpdate('${moduleName}', filter(${updatedModules}, ${JSON.stringify(errorModuleIds)}));`;
}

/**
* Parse the filename of the hot update chunk.
* @param name bundle.deccb264c01d6d42416c.hot-update.js
* @returns { name: string, hash: string } { name: 'bundle', hash: 'deccb264c01d6d42416c' }
*/
private static parseHotUpdateChunkName(name) {
const matcher = /^(.+)\.(.+)\.hot-update/gm;
const matches = matcher.exec(name);

return {
name: matches[1] || "",
hash: matches[2] || "",
};
}
}