Skip to content

Commit

Permalink
refactor: ship less client code (#2227)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenjiahan authored Apr 28, 2024
1 parent 6eb09fa commit 2401468
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 108 deletions.
5 changes: 5 additions & 0 deletions .changeset/happy-fishes-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rsbuild/core': patch
---

release: 0.6.8
8 changes: 3 additions & 5 deletions packages/core/src/client/formatStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ function resolveFileName(stats: webpack.StatsError) {
// Cleans up webpack error messages.
function formatMessage(stats: webpack.StatsError | string) {
let lines: string[] = [];

let message: string;

// webpack 5 stats error object
Expand Down Expand Up @@ -98,18 +97,17 @@ function formatMessage(stats: webpack.StatsError | string) {
}

export function formatStatsMessages(
json?: Pick<StatsCompilation, 'errors' | 'warnings'>,
stats: Pick<StatsCompilation, 'errors' | 'warnings'>,
): {
errors: string[];
warnings: string[];
} {
const formattedErrors = json?.errors?.map(formatMessage);
const formattedWarnings = json?.warnings?.map(formatMessage);
const formattedErrors = stats.errors?.map(formatMessage);
const formattedWarnings = stats.warnings?.map(formatMessage);

const result = {
errors: formattedErrors || [],
warnings: formattedWarnings || [],
errorTips: [],
};

if (result.errors?.some(isLikelyASyntaxError)) {
Expand Down
123 changes: 42 additions & 81 deletions packages/core/src/client/hmr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,18 @@
*
* Tips: this package will be bundled and running in the browser, do not import any Node.js modules.
*/
import type { ClientConfig, StatsError } from '@rsbuild/shared';
import type { StatsError } from '@rsbuild/shared';
import { formatStatsMessages } from '../formatStats';
import { createSocketUrl } from './createSocketUrl';

const options: ClientConfig = RSBUILD_CLIENT_CONFIG;

// Connect to Dev Server
const socketUrl = createSocketUrl(options);
import { getSocketUrl } from './url';

// Remember some state related to hot module replacement.
let isFirstCompilation = true;
let mostRecentCompilationHash: string | null = null;
let lastCompilationHash: string | null = null;
let hasCompileErrors = false;

function clearOutdatedErrors() {
// Clean up outdated compile errors, if any.
if (
typeof console !== 'undefined' &&
typeof console.clear === 'function' &&
hasCompileErrors
) {
if (console.clear && hasCompileErrors) {
console.clear();
}
}
Expand Down Expand Up @@ -62,29 +53,21 @@ function handleWarnings(warnings: StatsError[]) {
isFirstCompilation = false;
hasCompileErrors = false;

function printWarnings() {
// Print warnings to the console.
const formatted = formatStatsMessages({
warnings,
errors: [],
});

if (typeof console !== 'undefined' && typeof console.warn === 'function') {
for (let i = 0; i < formatted.warnings.length; i++) {
if (i === 5) {
console.warn(
'There were more warnings in other files.\n' +
'You can find a complete log in the terminal.',
);
break;
}
console.warn(formatted.warnings[i]);
}
const formatted = formatStatsMessages({
warnings,
errors: [],
});

for (let i = 0; i < formatted.warnings.length; i++) {
if (i === 5) {
console.warn(
'There were more warnings in other files, you can find a complete log in the terminal.',
);
break;
}
console.warn(formatted.warnings[i]);
}

printWarnings();

// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdates();
Expand All @@ -105,10 +88,8 @@ function handleErrors(errors: StatsError[]) {
});

// Also log them to the console.
if (typeof console !== 'undefined' && typeof console.error === 'function') {
for (const error of formatted.errors) {
console.error(error);
}
for (const error of formatted.errors) {
console.error(error);
}

if (createOverlay) {
Expand All @@ -119,21 +100,10 @@ function handleErrors(errors: StatsError[]) {
// We will reload on next success instead.
}

// There is a newer version of the code available.
function handleAvailableHash(hash: string) {
// Update last known compilation hash.
mostRecentCompilationHash = hash;
}

function isUpdateAvailable() {
// __webpack_hash__ is the hash of the current compilation.
// It's a global variable injected by webpack / Rspack.
return mostRecentCompilationHash !== __webpack_hash__;
}

// webpack disallows updates in other states.
function canApplyUpdates() {
return import.meta.webpackHot.status() === 'idle';
return lastCompilationHash !== __webpack_hash__;
}

// Attempt to update code on the fly, fall back to a hard reload.
Expand All @@ -149,21 +119,18 @@ function tryApplyUpdates() {
return;
}

if (!canApplyUpdates()) {
// webpack disallows updates in other states.
if (import.meta.webpackHot.status() !== 'idle') {
return;
}

function handleApplyUpdates(
err: unknown,
updatedModules: (string | number)[] | null,
) {
const wantsForcedReload = err || !updatedModules;
if (wantsForcedReload) {
if (
err &&
typeof console !== 'undefined' &&
typeof console.error === 'function'
) {
const forcedReload = err || !updatedModules;
if (forcedReload) {
if (err) {
console.error('[HMR] Forced reload caused by: ', err);
}

Expand All @@ -190,20 +157,20 @@ function tryApplyUpdates() {

const MAX_RETRIES = 100;
let connection: WebSocket | null = null;
let retryCounter = 0;
let retryCount = 0;

function onOpen() {
if (typeof console !== 'undefined' && typeof console.info === 'function') {
// Notify users that the HMR has successfully connected.
console.info('[HMR] connected.');
}
// Notify users that the HMR has successfully connected.
console.info('[HMR] connected.');
}

function onMessage(e: MessageEvent<string>) {
const message = JSON.parse(e.data);
switch (message.type) {
case 'hash':
handleAvailableHash(message.data);
// Update the last compilation hash
lastCompilationHash = message.data;

if (clearOverlay && isUpdateAvailable()) {
clearOverlay();
}
Expand All @@ -212,11 +179,9 @@ function onMessage(e: MessageEvent<string>) {
case 'ok':
handleSuccess();
break;
// Triggered when static files changed
case 'static-changed':
reloadPage();
break;
case 'content-changed':
// Triggered when a file from `contentBase` changed.
reloadPage();
break;
case 'warnings':
Expand All @@ -225,44 +190,38 @@ function onMessage(e: MessageEvent<string>) {
case 'errors':
handleErrors(message.data);
break;
default:
// Do nothing.
}
}

async function sleep(msec = 1000) {
function sleep(msec = 1000) {
return new Promise((resolve) => {
setTimeout(resolve, msec);
});
}

async function onClose() {
if (typeof console !== 'undefined' && typeof console.info === 'function') {
console.info('[HMR] disconnected. Attempting to reconnect.');
}
console.info('[HMR] disconnected. Attempting to reconnect.');

removeListeners();

await sleep(1000);
retryCounter++;
retryCount++;

if (
connection &&
(connection.readyState === connection.CONNECTING ||
connection.readyState === connection.OPEN)
) {
retryCounter = 0;
retryCount = 0;
return;
}

// Exceeded max retry attempts, stop retry.
if (retryCounter > MAX_RETRIES) {
if (typeof console !== 'undefined' && typeof console.info === 'function') {
console.info(
'[HMR] Unable to establish a connection after exceeding the maximum retry attempts.',
);
}
retryCounter = 0;
if (retryCount > MAX_RETRIES) {
console.info(
'[HMR] Unable to establish a connection after exceeding the maximum retry attempts.',
);
retryCount = 0;
return;
}

Expand All @@ -271,6 +230,8 @@ async function onClose() {

// Establishing a WebSocket connection with the server.
function connect() {
const socketUrl = getSocketUrl(RSBUILD_CLIENT_CONFIG);

connection = new WebSocket(socketUrl);
connection.addEventListener('open', onOpen);
// Attempt to reconnect after disconnection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ import type { ClientConfig } from '@rsbuild/shared';
*/
export const HMR_SOCK_PATH = '/rsbuild-hmr';

export function createSocketUrl(options: ClientConfig = {}) {
const currentLocation = self.location;

return getSocketUrl(options, currentLocation);
}

export function formatURL({
function formatURL({
port,
protocol,
hostname,
Expand All @@ -36,7 +30,8 @@ export function formatURL({
return `${protocol}${colon}//${hostname}:${port}${pathname}`;
}

function getSocketUrl(urlParts: ClientConfig, location: Location) {
export function getSocketUrl(urlParts: ClientConfig) {
const { location } = self;
const { host, port, path, protocol } = urlParts;

return formatURL({
Expand Down
21 changes: 7 additions & 14 deletions packages/core/src/client/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,28 +220,21 @@ if (customElements && !customElements.get(overlayId)) {
customElements.define(overlayId, ErrorOverlay);
}

const documentAvailable = typeof document !== 'undefined';

function createOverlay(err: string[]) {
if (!documentAvailable) {
console.info(
'[Rsbuild] Failed to display error overlay as document is not available, you can disable the `dev.client.overlay` option.',
);
return;
}

clearOverlay();
document.body.appendChild(new ErrorOverlay(err));
}

function clearOverlay() {
if (!documentAvailable) {
return;
}

// use NodeList's forEach api instead of dom.iterable
// biome-ignore lint/complexity/noForEach: <explanation>
document.querySelectorAll<ErrorOverlay>(overlayId).forEach((n) => n.close());
}

registerOverlay(createOverlay, clearOverlay);
if (typeof document !== 'undefined') {
registerOverlay(createOverlay, clearOverlay);
} else {
console.info(
'[Rsbuild] Failed to display error overlay as document is not available, you can disable the `dev.client.overlay` option.',
);
}

0 comments on commit 2401468

Please sign in to comment.