@pmmmwh/react-refresh-webpack-plugin
Advanced tools
Comparing version 0.5.15 to 0.6.0-beta.0
@@ -1,6 +0,5 @@ | ||
/* global __react_refresh_error_overlay__, __react_refresh_socket__, __resourceQuery */ | ||
/* global __react_refresh_error_overlay__, __react_refresh_socket__ */ | ||
const events = require('./utils/errorEventHandlers.js'); | ||
const formatWebpackErrors = require('./utils/formatWebpackErrors.js'); | ||
const runWithPatchedUrl = require('./utils/patchUrl.js'); | ||
const runWithRetry = require('./utils/retry.js'); | ||
@@ -77,25 +76,27 @@ | ||
if (typeof window !== 'undefined') { | ||
runWithPatchedUrl(function setupOverlay() { | ||
// Only register if no other overlay have been registered | ||
if (!window.__reactRefreshOverlayInjected && __react_refresh_socket__) { | ||
// Registers handlers for compile errors with retry - | ||
// This is to prevent mismatching injection order causing errors to be thrown | ||
runWithRetry(function initSocket() { | ||
__react_refresh_socket__.init(compileMessageHandler, __resourceQuery); | ||
}, 3); | ||
// Registers handlers for runtime errors | ||
events.handleError(function handleError(error) { | ||
hasRuntimeErrors = true; | ||
__react_refresh_error_overlay__.handleRuntimeError(error); | ||
}); | ||
events.handleUnhandledRejection(function handleUnhandledPromiseRejection(error) { | ||
hasRuntimeErrors = true; | ||
__react_refresh_error_overlay__.handleRuntimeError(error); | ||
}); | ||
// Only register if no other overlay have been registered | ||
if (!window.__reactRefreshOverlayInjected && __react_refresh_socket__) { | ||
// Registers handlers for compile errors with retry - | ||
// This is to prevent mismatching injection order causing errors to be thrown | ||
runWithRetry( | ||
function initSocket() { | ||
__react_refresh_socket__.init(compileMessageHandler); | ||
}, | ||
3, | ||
'Failed to set up the socket connection.' | ||
); | ||
// Registers handlers for runtime errors | ||
events.handleError(function handleError(error) { | ||
hasRuntimeErrors = true; | ||
__react_refresh_error_overlay__.handleRuntimeError(error); | ||
}); | ||
events.handleUnhandledRejection(function handleUnhandledPromiseRejection(error) { | ||
hasRuntimeErrors = true; | ||
__react_refresh_error_overlay__.handleRuntimeError(error); | ||
}); | ||
// Mark overlay as injected to prevent double-injection | ||
window.__reactRefreshOverlayInjected = true; | ||
} | ||
}); | ||
// Mark overlay as injected to prevent double-injection | ||
window.__reactRefreshOverlayInjected = true; | ||
} | ||
} | ||
} |
@@ -1,6 +0,6 @@ | ||
function runWithRetry(callback, maxRetries) { | ||
function runWithRetry(callback, maxRetries, message) { | ||
function executeWithRetryAndTimeout(currentCount) { | ||
try { | ||
if (currentCount > maxRetries - 1) { | ||
console.warn('[React Refresh] Failed to set up the socket connection.'); | ||
console.warn('[React Refresh]', message); | ||
return; | ||
@@ -7,0 +7,0 @@ } |
@@ -9,15 +9,1 @@ /** | ||
}; | ||
/** | ||
* Gets current Webpack version according to features on the compiler instance. | ||
* @param {import('webpack').Compiler} compiler The current Webpack compiler instance. | ||
* @returns {number} The current Webpack version. | ||
*/ | ||
module.exports.getWebpackVersion = (compiler) => { | ||
if (!compiler.hooks) { | ||
throw new Error(`[ReactRefreshPlugin] Webpack version is not supported!`); | ||
} | ||
// Webpack v5+ implements compiler caching | ||
return 'cache' in compiler ? 5 : 4; | ||
}; |
326
lib/index.js
const { validate: validateOptions } = require('schema-utils'); | ||
const { getRefreshGlobalScope, getWebpackVersion } = require('./globals'); | ||
const { getRefreshGlobalScope } = require('./globals'); | ||
const { | ||
getAdditionalEntries, | ||
getIntegrationEntry, | ||
getRefreshGlobal, | ||
getSocketIntegration, | ||
injectRefreshEntry, | ||
injectRefreshLoader, | ||
@@ -51,3 +49,2 @@ makeRefreshRuntimeModule, | ||
const webpackVersion = getWebpackVersion(compiler); | ||
const logger = compiler.getInfrastructureLogger(this.constructor.name); | ||
@@ -72,75 +69,71 @@ | ||
// and fallback to patching the `entry` object for legacy workflows. | ||
const addEntries = getAdditionalEntries({ | ||
devServer: compiler.options.devServer, | ||
options: this.options, | ||
const addEntries = getAdditionalEntries(this.options); | ||
// Prepended entries does not care about injection order, | ||
// so we can utilise EntryPlugin for simpler logic. | ||
addEntries.prependEntries.forEach((entry) => { | ||
new EntryPlugin(compiler.context, entry, { name: undefined }).apply(compiler); | ||
}); | ||
if (EntryPlugin) { | ||
// Prepended entries does not care about injection order, | ||
// so we can utilise EntryPlugin for simpler logic. | ||
addEntries.prependEntries.forEach((entry) => { | ||
new EntryPlugin(compiler.context, entry, { name: undefined }).apply(compiler); | ||
}); | ||
const integrationEntry = getIntegrationEntry(this.options.overlay.sockIntegration); | ||
const socketEntryData = []; | ||
compiler.hooks.make.tap( | ||
{ name: this.constructor.name, stage: Number.POSITIVE_INFINITY }, | ||
(compilation) => { | ||
// Exhaustively search all entries for `integrationEntry`. | ||
// If found, mark those entries and the index of `integrationEntry`. | ||
for (const [name, entryData] of compilation.entries.entries()) { | ||
const index = entryData.dependencies.findIndex( | ||
(dep) => dep.request && dep.request.includes(integrationEntry) | ||
); | ||
if (index !== -1) { | ||
socketEntryData.push({ name, index }); | ||
} | ||
const integrationEntry = getIntegrationEntry(this.options.overlay.sockIntegration); | ||
const socketEntryData = []; | ||
compiler.hooks.make.tap( | ||
{ name: this.constructor.name, stage: Number.POSITIVE_INFINITY }, | ||
(compilation) => { | ||
// Exhaustively search all entries for `integrationEntry`. | ||
// If found, mark those entries and the index of `integrationEntry`. | ||
for (const [name, entryData] of compilation.entries.entries()) { | ||
const index = entryData.dependencies.findIndex( | ||
(dep) => dep.request && dep.request.includes(integrationEntry) | ||
); | ||
if (index !== -1) { | ||
socketEntryData.push({ name, index }); | ||
} | ||
} | ||
); | ||
} | ||
); | ||
// Overlay entries need to be injected AFTER integration's entry, | ||
// so we will loop through everything in `finishMake` instead of `make`. | ||
// This ensures we can traverse all entry points and inject stuff with the correct order. | ||
addEntries.overlayEntries.forEach((entry, idx, arr) => { | ||
compiler.hooks.finishMake.tapPromise( | ||
{ name: this.constructor.name, stage: Number.MIN_SAFE_INTEGER + (arr.length - idx - 1) }, | ||
(compilation) => { | ||
// Only hook into the current compiler | ||
if (compilation.compiler !== compiler) { | ||
return Promise.resolve(); | ||
} | ||
// Overlay entries need to be injected AFTER integration's entry, | ||
// so we will loop through everything in `finishMake` instead of `make`. | ||
// This ensures we can traverse all entry points and inject stuff with the correct order. | ||
for (const [idx, entry] of addEntries.overlayEntries.entries()) { | ||
compiler.hooks.finishMake.tapPromise( | ||
{ | ||
name: this.constructor.name, | ||
stage: Number.MIN_SAFE_INTEGER + (addEntries.overlayEntries.length - idx - 1), | ||
}, | ||
(compilation) => { | ||
// Only hook into the current compiler | ||
if (compilation.compiler !== compiler) { | ||
return Promise.resolve(); | ||
} | ||
const injectData = socketEntryData.length ? socketEntryData : [{ name: undefined }]; | ||
return Promise.all( | ||
injectData.map(({ name, index }) => { | ||
return new Promise((resolve, reject) => { | ||
const options = { name }; | ||
const dep = EntryPlugin.createDependency(entry, options); | ||
compilation.addEntry(compiler.context, dep, options, (err) => { | ||
if (err) return reject(err); | ||
const injectData = socketEntryData.length ? socketEntryData : [{ name: undefined }]; | ||
return Promise.all( | ||
injectData.map(({ name, index }) => { | ||
return new Promise((resolve, reject) => { | ||
const options = { name }; | ||
const dep = EntryPlugin.createDependency(entry, options); | ||
compilation.addEntry(compiler.context, dep, options, (err) => { | ||
if (err) return reject(err); | ||
// If the entry is not a global one, | ||
// and we have registered the index for integration entry, | ||
// we will reorder all entry dependencies to our desired order. | ||
// That is, to have additional entries DIRECTLY behind integration entry. | ||
if (name && typeof index !== 'undefined') { | ||
const entryData = compilation.entries.get(name); | ||
entryData.dependencies.splice( | ||
index + 1, | ||
0, | ||
entryData.dependencies.splice(entryData.dependencies.length - 1, 1)[0] | ||
); | ||
} | ||
// If the entry is not a global one, | ||
// and we have registered the index for integration entry, | ||
// we will reorder all entry dependencies to our desired order. | ||
// That is, to have additional entries DIRECTLY behind integration entry. | ||
if (name && typeof index !== 'undefined') { | ||
const entryData = compilation.entries.get(name); | ||
entryData.dependencies.splice( | ||
index + 1, | ||
0, | ||
entryData.dependencies.splice(entryData.dependencies.length - 1, 1)[0] | ||
); | ||
} | ||
resolve(); | ||
}); | ||
resolve(); | ||
}); | ||
}) | ||
).then(() => {}); | ||
} | ||
); | ||
}); | ||
} else { | ||
compiler.options.entry = injectRefreshEntry(compiler.options.entry, addEntries); | ||
}); | ||
}) | ||
); | ||
} | ||
); | ||
} | ||
@@ -175,7 +168,4 @@ | ||
definedModules.__react_refresh_error_overlay__ = false; | ||
definedModules.__react_refresh_polyfill_url__ = false; | ||
definedModules.__react_refresh_socket__ = false; | ||
} else { | ||
definedModules.__react_refresh_polyfill_url__ = this.options.overlay.useURLPolyfill || false; | ||
if (this.options.overlay.module) { | ||
@@ -206,160 +196,48 @@ providedModules.__react_refresh_error_overlay__ = require.resolve( | ||
// Tap into version-specific compilation hooks | ||
switch (webpackVersion) { | ||
case 4: { | ||
const outputOptions = compilation.mainTemplate.outputOptions; | ||
compilation.mainTemplate.hooks.require.tap( | ||
this.constructor.name, | ||
// Constructs the module template for react-refresh | ||
(source, chunk, hash) => { | ||
// Check for the output filename | ||
// This is to ensure we are processing a JS-related chunk | ||
let filename = outputOptions.filename; | ||
if (typeof filename === 'function') { | ||
// Only usage of the `chunk` property is documented by Webpack. | ||
// However, some internal Webpack plugins uses other properties, | ||
// so we also pass them through to be on the safe side. | ||
filename = filename({ | ||
contentHashType: 'javascript', | ||
chunk, | ||
hash, | ||
}); | ||
} | ||
// Set factory for EntryDependency which is used to initialise the module | ||
compilation.dependencyFactories.set(EntryDependency, normalModuleFactory); | ||
// Check whether the current compilation is outputting to JS, | ||
// since other plugins can trigger compilations for other file types too. | ||
// If we apply the transform to them, their compilation will break fatally. | ||
// One prominent example of this is the HTMLWebpackPlugin. | ||
// If filename is falsy, something is terribly wrong and there's nothing we can do. | ||
if (!filename || !filename.includes('.js')) { | ||
return source; | ||
} | ||
const ReactRefreshRuntimeModule = makeRefreshRuntimeModule(webpack); | ||
compilation.hooks.additionalTreeRuntimeRequirements.tap( | ||
this.constructor.name, | ||
// Setup react-refresh globals with a Webpack runtime module | ||
(chunk, runtimeRequirements) => { | ||
runtimeRequirements.add(RuntimeGlobals.interceptModuleExecution); | ||
runtimeRequirements.add(RuntimeGlobals.moduleCache); | ||
runtimeRequirements.add(refreshGlobal); | ||
compilation.addRuntimeModule(chunk, new ReactRefreshRuntimeModule()); | ||
} | ||
); | ||
// Split template source code into lines for easier processing | ||
const lines = source.split('\n'); | ||
// Webpack generates this line when the MainTemplate is called | ||
const moduleInitializationLineNumber = lines.findIndex((line) => | ||
line.includes('modules[moduleId].call(') | ||
); | ||
// Unable to find call to module execution - | ||
// this happens if the current module does not call MainTemplate. | ||
// In this case, we will return the original source and won't mess with it. | ||
if (moduleInitializationLineNumber === -1) { | ||
return source; | ||
} | ||
const moduleInterceptor = Template.asString([ | ||
`${refreshGlobal}.setup(moduleId);`, | ||
'try {', | ||
Template.indent(lines[moduleInitializationLineNumber]), | ||
'} finally {', | ||
Template.indent(`${refreshGlobal}.cleanup(moduleId);`), | ||
'}', | ||
]); | ||
return Template.asString([ | ||
...lines.slice(0, moduleInitializationLineNumber), | ||
'', | ||
outputOptions.strictModuleExceptionHandling | ||
? Template.indent(moduleInterceptor) | ||
: moduleInterceptor, | ||
'', | ||
...lines.slice(moduleInitializationLineNumber + 1, lines.length), | ||
]); | ||
} | ||
); | ||
compilation.mainTemplate.hooks.requireExtensions.tap( | ||
this.constructor.name, | ||
// Setup react-refresh globals as extensions to Webpack's require function | ||
(source) => { | ||
return Template.asString([source, '', getRefreshGlobal(Template)]); | ||
} | ||
); | ||
normalModuleFactory.hooks.afterResolve.tap( | ||
this.constructor.name, | ||
// Add react-refresh loader to process files that matches specified criteria | ||
(data) => { | ||
return injectRefreshLoader(data, { | ||
match, | ||
options: { const: false, esModule: false }, | ||
}); | ||
} | ||
); | ||
compilation.hooks.normalModuleLoader.tap( | ||
// `Number.POSITIVE_INFINITY` ensures this check will run only after all other taps | ||
{ name: this.constructor.name, stage: Number.POSITIVE_INFINITY }, | ||
// Check for existence of the HMR runtime - | ||
// it is the foundation to this plugin working correctly | ||
(context) => { | ||
if (!context.hot && !loggedHotWarning) { | ||
logger.warn( | ||
[ | ||
'Hot Module Replacement (HMR) is not enabled!', | ||
'React Refresh requires HMR to function properly.', | ||
].join(' ') | ||
); | ||
loggedHotWarning = true; | ||
} | ||
} | ||
); | ||
break; | ||
normalModuleFactory.hooks.afterResolve.tap( | ||
this.constructor.name, | ||
// Add react-refresh loader to process files that matches specified criteria | ||
(resolveData) => { | ||
injectRefreshLoader(resolveData.createData, { | ||
match, | ||
options: { | ||
const: compilation.runtimeTemplate.supportsConst(), | ||
esModule: this.options.esModule, | ||
}, | ||
}); | ||
} | ||
case 5: { | ||
// Set factory for EntryDependency which is used to initialise the module | ||
compilation.dependencyFactories.set(EntryDependency, normalModuleFactory); | ||
); | ||
const ReactRefreshRuntimeModule = makeRefreshRuntimeModule(webpack); | ||
compilation.hooks.additionalTreeRuntimeRequirements.tap( | ||
this.constructor.name, | ||
// Setup react-refresh globals with a Webpack runtime module | ||
(chunk, runtimeRequirements) => { | ||
runtimeRequirements.add(RuntimeGlobals.interceptModuleExecution); | ||
runtimeRequirements.add(RuntimeGlobals.moduleCache); | ||
runtimeRequirements.add(refreshGlobal); | ||
compilation.addRuntimeModule(chunk, new ReactRefreshRuntimeModule()); | ||
} | ||
); | ||
normalModuleFactory.hooks.afterResolve.tap( | ||
this.constructor.name, | ||
// Add react-refresh loader to process files that matches specified criteria | ||
(resolveData) => { | ||
injectRefreshLoader(resolveData.createData, { | ||
match, | ||
options: { | ||
const: compilation.runtimeTemplate.supportsConst(), | ||
esModule: this.options.esModule, | ||
}, | ||
}); | ||
} | ||
); | ||
NormalModule.getCompilationHooks(compilation).loader.tap( | ||
// `Infinity` ensures this check will run only after all other taps | ||
{ name: this.constructor.name, stage: Infinity }, | ||
// Check for existence of the HMR runtime - | ||
// it is the foundation to this plugin working correctly | ||
(context) => { | ||
if (!context.hot && !loggedHotWarning) { | ||
logger.warn( | ||
[ | ||
'Hot Module Replacement (HMR) is not enabled!', | ||
'React Refresh requires HMR to function properly.', | ||
].join(' ') | ||
); | ||
loggedHotWarning = true; | ||
} | ||
} | ||
); | ||
break; | ||
NormalModule.getCompilationHooks(compilation).loader.tap( | ||
// `Infinity` ensures this check will run only after all other taps | ||
{ name: this.constructor.name, stage: Infinity }, | ||
// Check for existence of the HMR runtime - | ||
// it is the foundation to this plugin working correctly | ||
(context) => { | ||
if (!context.hot && !loggedHotWarning) { | ||
logger.warn( | ||
[ | ||
'Hot Module Replacement (HMR) is not enabled!', | ||
'React Refresh requires HMR to function properly.', | ||
].join(' ') | ||
); | ||
loggedHotWarning = true; | ||
} | ||
} | ||
default: { | ||
// Do nothing - this should be an impossible case | ||
} | ||
} | ||
); | ||
} | ||
@@ -366,0 +244,0 @@ ); |
@@ -48,8 +48,3 @@ { | ||
] | ||
}, | ||
"sockHost": { "type": "string" }, | ||
"sockPath": { "type": "string" }, | ||
"sockPort": { "type": "number", "minimum": 0 }, | ||
"sockProtocol": { "enum": ["http", "https", "ws", "wss"] }, | ||
"useURLPolyfill": { "type": "boolean" } | ||
} | ||
} | ||
@@ -56,0 +51,0 @@ } |
@@ -5,8 +5,3 @@ /** | ||
* @property {string | false} [module] The error overlay module to use. | ||
* @property {string} [sockHost] The socket host to use (WDS only). | ||
* @property {import('type-fest').LiteralUnion<'wds' | 'whm' | 'wps' | false, string>} [sockIntegration] Path to a JS file that sets up the Webpack socket integration. | ||
* @property {string} [sockPath] The socket path to use (WDS only). | ||
* @property {number} [sockPort] The socket port to use (WDS only). | ||
* @property {'http' | 'https' | 'ws' | 'wss'} [sockProtocol] The socket protocol to use (WDS only). | ||
* @property {boolean} [useURLPolyfill] Uses a polyfill for the DOM URL API (WDS only). | ||
*/ | ||
@@ -13,0 +8,0 @@ |
@@ -1,3 +0,1 @@ | ||
const querystring = require('querystring'); | ||
/** | ||
@@ -11,71 +9,6 @@ * @typedef {Object} AdditionalEntries | ||
* Creates an object that contains two entry arrays: the prependEntries and overlayEntries | ||
* @param {Object} optionsContainer This is the container for the options to this function | ||
* @param {import('../types').NormalizedPluginOptions} optionsContainer.options Configuration options for this plugin. | ||
* @param {import('webpack').Compiler["options"]["devServer"]} [optionsContainer.devServer] The webpack devServer config | ||
* @param {import('../types').NormalizedPluginOptions} options Configuration options for this plugin. | ||
* @returns {AdditionalEntries} An object that contains the Webpack entries for prepending and the overlay feature | ||
*/ | ||
function getAdditionalEntries({ devServer, options }) { | ||
/** @type {Record<string, string | number>} */ | ||
let resourceQuery = {}; | ||
if (devServer) { | ||
const { client, https, http2, sockHost, sockPath, sockPort } = devServer; | ||
let { host, path, port } = devServer; | ||
let protocol = https || http2 ? 'https' : 'http'; | ||
if (sockHost) host = sockHost; | ||
if (sockPath) path = sockPath; | ||
if (sockPort) port = sockPort; | ||
if (client && client.webSocketURL != null) { | ||
let parsedUrl = client.webSocketURL; | ||
if (typeof parsedUrl === 'string') parsedUrl = new URL(parsedUrl); | ||
let auth; | ||
if (parsedUrl.username) { | ||
auth = parsedUrl.username; | ||
if (parsedUrl.password) { | ||
auth += ':' + parsedUrl.password; | ||
} | ||
} | ||
if (parsedUrl.hostname != null) { | ||
host = [auth != null && auth, parsedUrl.hostname].filter(Boolean).join('@'); | ||
} | ||
if (parsedUrl.pathname != null) { | ||
path = parsedUrl.pathname; | ||
} | ||
if (parsedUrl.port != null) { | ||
port = !['0', 'auto'].includes(String(parsedUrl.port)) ? parsedUrl.port : undefined; | ||
} | ||
if (parsedUrl.protocol != null) { | ||
protocol = parsedUrl.protocol !== 'auto' ? parsedUrl.protocol.replace(':', '') : 'ws'; | ||
} | ||
} | ||
if (host) resourceQuery.sockHost = host; | ||
if (path) resourceQuery.sockPath = path; | ||
if (port) resourceQuery.sockPort = port; | ||
resourceQuery.sockProtocol = protocol; | ||
} | ||
if (options.overlay) { | ||
const { sockHost, sockPath, sockPort, sockProtocol } = options.overlay; | ||
if (sockHost) resourceQuery.sockHost = sockHost; | ||
if (sockPath) resourceQuery.sockPath = sockPath; | ||
if (sockPort) resourceQuery.sockPort = sockPort; | ||
if (sockProtocol) resourceQuery.sockProtocol = sockProtocol; | ||
} | ||
// We don't need to URI encode the resourceQuery as it will be parsed by Webpack | ||
const queryString = querystring.stringify(resourceQuery, undefined, undefined, { | ||
/** | ||
* @param {string} string | ||
* @returns {string} | ||
*/ | ||
encodeURIComponent(string) { | ||
return string; | ||
}, | ||
}); | ||
function getAdditionalEntries(options) { | ||
const prependEntries = [ | ||
@@ -88,5 +21,3 @@ // React-refresh runtime | ||
// Error overlay runtime | ||
options.overlay && | ||
options.overlay.entry && | ||
`${require.resolve(options.overlay.entry)}${queryString ? `?${queryString}` : ''}`, | ||
options.overlay && options.overlay.entry && require.resolve(options.overlay.entry), | ||
].filter(Boolean); | ||
@@ -93,0 +24,0 @@ |
@@ -1,2 +0,2 @@ | ||
const path = require('path'); | ||
const path = require('node:path'); | ||
@@ -3,0 +3,0 @@ /** |
@@ -32,7 +32,2 @@ const { d, n } = require('../../options'); | ||
d(overlay, 'sockIntegration', defaults.sockIntegration); | ||
d(overlay, 'sockHost'); | ||
d(overlay, 'sockPath'); | ||
d(overlay, 'sockPort'); | ||
d(overlay, 'sockProtocol'); | ||
d(options, 'useURLPolyfill'); | ||
@@ -39,0 +34,0 @@ return overlay; |
@@ -8,3 +8,2 @@ // This is a patch for mozilla/source-map#349 - | ||
const { getOptions } = require('loader-utils'); | ||
const { validate: validateOptions } = require('schema-utils'); | ||
@@ -36,3 +35,3 @@ const { SourceMapConsumer, SourceNode } = require('source-map'); | ||
function ReactRefreshLoader(source, inputSourceMap, meta) { | ||
let options = getOptions(this); | ||
let options = this.getOptions(); | ||
validateOptions(schema, options, { | ||
@@ -39,0 +38,0 @@ baseDataPath: 'options', |
@@ -13,3 +13,3 @@ const { SourceMapGenerator } = require('source-map'); | ||
source.split('\n').forEach((line, index) => { | ||
source.split('\n').forEach((_, index) => { | ||
sourceMap.addMapping({ | ||
@@ -16,0 +16,0 @@ source: resourcePath, |
@@ -1,3 +0,3 @@ | ||
const { promises: fsPromises } = require('fs'); | ||
const path = require('path'); | ||
const fs = require('node:fs/promises'); | ||
const path = require('node:path'); | ||
@@ -89,3 +89,3 @@ /** @type {Map<string, string | undefined>} */ | ||
try { | ||
const packageSource = await fsPromises.readFile(packageJsonPath, 'utf-8'); | ||
const packageSource = await fs.readFile(packageJsonPath, 'utf-8'); | ||
try { | ||
@@ -92,0 +92,0 @@ const packageObject = JSON.parse(packageSource); |
@@ -1,2 +0,2 @@ | ||
const ansiHTML = require('ansi-html'); | ||
const ansiHTML = require('ansi-html-community'); | ||
const entities = require('html-entities'); | ||
@@ -3,0 +3,0 @@ const theme = require('../theme.js'); |
{ | ||
"name": "@pmmmwh/react-refresh-webpack-plugin", | ||
"version": "0.5.15", | ||
"version": "0.6.0-beta.0", | ||
"description": "An **EXPERIMENTAL** Webpack plugin to enable \"Fast Refresh\" (also previously known as _Hot Reloading_) for React components.", | ||
@@ -39,8 +39,13 @@ "keywords": [ | ||
], | ||
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610", | ||
"scripts": { | ||
"test": "run-s -c test:pre \"test:exec {@}\" test:post --", | ||
"test:webpack-4": "cross-env WEBPACK_VERSION=4 yarn test", | ||
"test:pre": "yarn add -D file:./", | ||
"test:wds-4": "cross-env WDS_VERSION=4 yarn test", | ||
"test:exec": "node scripts/test.js", | ||
"test:post": "yarn remove @pmmmwh/react-refresh-webpack-plugin", | ||
"test:pre": "run-s -c test:pre:*", | ||
"test:pre:0": "yarn link", | ||
"test:pre:1": "yarn link @pmmmwh/react-refresh-webpack-plugin", | ||
"test:post": "run-s -c test:post:*", | ||
"test:post:0": "yarn unlink @pmmmwh/react-refresh-webpack-plugin", | ||
"test:post:1": "yarn unlink", | ||
"lint": "eslint --report-unused-disable-directives --ext .js,.jsx .", | ||
@@ -61,3 +66,2 @@ "lint:fix": "yarn lint --fix", | ||
"html-entities": "^2.1.0", | ||
"loader-utils": "^2.0.4", | ||
"schema-utils": "^4.2.0", | ||
@@ -67,13 +71,12 @@ "source-map": "^0.7.3" | ||
"devDependencies": { | ||
"@babel/core": "^7.14.2", | ||
"@babel/plugin-transform-modules-commonjs": "^7.14.0", | ||
"@types/cross-spawn": "^6.0.2", | ||
"@babel/core": "^7.24.6", | ||
"@babel/plugin-transform-modules-commonjs": "^7.24.6", | ||
"@types/cross-spawn": "^6.0.6", | ||
"@types/fs-extra": "^11.0.4", | ||
"@types/jest": "^29.5.12", | ||
"@types/json-schema": "^7.0.6", | ||
"@types/module-alias": "^2.0.0", | ||
"@types/node": "^20.5.0", | ||
"@types/semver": "^7.3.9", | ||
"@types/webpack": "^5.28.0", | ||
"babel-loader": "^8.1.0", | ||
"@types/json-schema": "^7.0.15", | ||
"@types/module-alias": "^2.0.4", | ||
"@types/node": "^20.13.0", | ||
"@types/webpack": "^5.28.5", | ||
"babel-loader": "^9.1.3", | ||
"cross-env": "^7.0.3", | ||
@@ -83,4 +86,4 @@ "cross-spawn": "^7.0.3", | ||
"eslint": "^8.6.0", | ||
"eslint-config-prettier": "^9.0.0", | ||
"eslint-plugin-prettier": "^5.0.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-prettier": "^5.1.3", | ||
"fs-extra": "^11.2.0", | ||
@@ -93,32 +96,29 @@ "get-port": "^5.1.1", | ||
"jest-location-mock": "^2.0.0", | ||
"module-alias": "^2.2.2", | ||
"memfs": "^4.9.2", | ||
"module-alias": "^2.2.3", | ||
"nanoid": "^3.1.31", | ||
"npm-run-all": "^4.1.5", | ||
"npm-run-all2": "^6.2.0", | ||
"prettier": "^3.3.0", | ||
"puppeteer": "^22.10.0", | ||
"react-refresh": "^0.14.0", | ||
"semver": "^7.5.2", | ||
"react-refresh": "^0.14.2", | ||
"sourcemap-validator": "^2.1.0", | ||
"type-fest": "^4.0.0", | ||
"type-fest": "^4.18.3", | ||
"typescript": "~5.4.5", | ||
"webpack": "^5.76.0", | ||
"webpack": "^5.91.0", | ||
"webpack-cli": "^5.1.4", | ||
"webpack-cli-v4": "npm:webpack-cli@4.x", | ||
"webpack-dev-server": "^5.0.4", | ||
"webpack-dev-server-v3": "npm:webpack-dev-server@3.x", | ||
"webpack-dev-server-v4": "npm:webpack-dev-server@4.x", | ||
"webpack-hot-middleware": "^2.25.0", | ||
"webpack-plugin-serve": "^1.4.1", | ||
"webpack-v4": "npm:webpack@4.x", | ||
"webpack-dev-server-v4": "npm:webpack-dev-server@^4.8.0", | ||
"webpack-hot-middleware": "^2.26.1", | ||
"webpack-plugin-serve": "^1.6.0", | ||
"yn": "^4.0.0" | ||
}, | ||
"peerDependencies": { | ||
"@types/webpack": "4.x || 5.x", | ||
"@types/webpack": "5.x", | ||
"react-refresh": ">=0.10.0 <1.0.0", | ||
"sockjs-client": "^1.4.0", | ||
"type-fest": ">=0.17.0 <5.0.0", | ||
"webpack": ">=4.43.0 <6.0.0", | ||
"webpack-dev-server": "3.x || 4.x || 5.x", | ||
"webpack": "^5.0.0", | ||
"webpack-dev-server": "^4.8.0 || 5.x", | ||
"webpack-hot-middleware": "2.x", | ||
"webpack-plugin-serve": "0.x || 1.x" | ||
"webpack-plugin-serve": "1.x" | ||
}, | ||
@@ -149,5 +149,4 @@ "peerDependenciesMeta": { | ||
"engines": { | ||
"node": ">= 10.13" | ||
}, | ||
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" | ||
"node": ">=18.12" | ||
} | ||
} |
@@ -1,32 +0,26 @@ | ||
/* global __webpack_dev_server_client__ */ | ||
const getSocketUrlParts = require('./utils/getSocketUrlParts.js'); | ||
const getUrlFromParts = require('./utils/getUrlFromParts'); | ||
const getWDSMetadata = require('./utils/getWDSMetadata'); | ||
/** | ||
* Initializes a socket server for HMR for webpack-dev-server. | ||
* @param {function(*): void} messageHandler A handler to consume Webpack compilation messages. | ||
* @param {string} [resourceQuery] Webpack's `__resourceQuery` string. | ||
* @returns {void} | ||
*/ | ||
function initWDSSocket(messageHandler, resourceQuery) { | ||
if (typeof __webpack_dev_server_client__ !== 'undefined') { | ||
let SocketClient = __webpack_dev_server_client__; | ||
if (typeof __webpack_dev_server_client__.default !== 'undefined') { | ||
SocketClient = __webpack_dev_server_client__.default; | ||
} | ||
function initWDSSocket(messageHandler) { | ||
const { default: SockJSClient } = require('webpack-dev-server/client/clients/SockJSClient'); | ||
const { default: WebSocketClient } = require('webpack-dev-server/client/clients/WebSocketClient'); | ||
const { client } = require('webpack-dev-server/client/socket'); | ||
const wdsMeta = getWDSMetadata(SocketClient); | ||
const urlParts = getSocketUrlParts(resourceQuery, wdsMeta); | ||
/** @type {WebSocket} */ | ||
let connection; | ||
if (client instanceof SockJSClient) { | ||
connection = client.sock; | ||
} else if (client instanceof WebSocketClient) { | ||
connection = client.client; | ||
} else { | ||
throw new Error('Failed to determine WDS client type'); | ||
} | ||
const connection = new SocketClient(getUrlFromParts(urlParts, wdsMeta)); | ||
connection.onMessage(function onSocketMessage(data) { | ||
const message = JSON.parse(data); | ||
messageHandler(message); | ||
}); | ||
} | ||
connection.addEventListener('message', function onSocketMessage(message) { | ||
messageHandler(JSON.parse(message.data)); | ||
}); | ||
} | ||
module.exports = { init: initWDSSocket }; |
@@ -11,6 +11,2 @@ export type ErrorOverlayOptions = { | ||
/** | ||
* The socket host to use (WDS only). | ||
*/ | ||
sockHost?: string | undefined; | ||
/** | ||
* Path to a JS file that sets up the Webpack socket integration. | ||
@@ -21,18 +17,2 @@ */ | ||
| undefined; | ||
/** | ||
* The socket path to use (WDS only). | ||
*/ | ||
sockPath?: string | undefined; | ||
/** | ||
* The socket port to use (WDS only). | ||
*/ | ||
sockPort?: number | undefined; | ||
/** | ||
* The socket protocol to use (WDS only). | ||
*/ | ||
sockProtocol?: 'http' | 'https' | 'ws' | 'wss' | undefined; | ||
/** | ||
* Uses a polyfill for the DOM URL API (WDS only). | ||
*/ | ||
useURLPolyfill?: boolean | undefined; | ||
}; | ||
@@ -39,0 +19,0 @@ export type NormalizedErrorOverlayOptions = import('type-fest').SetRequired< |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
14
40
7
114014
53
2710
- Removedloader-utils@^2.0.4
- Removedbig.js@5.2.2(transitive)
- Removedemojis-list@3.0.0(transitive)
- Removedjson5@2.2.3(transitive)
- Removedloader-utils@2.0.4(transitive)