Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace extra source line in webpack 5 #14793

Merged
4 changes: 4 additions & 0 deletions packages/react-dev-overlay/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ function getSourcePath(source: string) {
return source.substring(15)
}

if (source.startsWith('webpack://')) {
return source.substring(10)
}

return source
}

Expand Down
1 change: 1 addition & 0 deletions packages/react-dev-overlay/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"sourceMap": true,
"esModuleInterop": true,
"target": "es3",
"lib": ["dom"],
Expand Down
1 change: 1 addition & 0 deletions packages/react-refresh-utils/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.d.ts
*.js
*.js.map
72 changes: 71 additions & 1 deletion packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Compiler, Template, version } from 'webpack'
import {
Compiler,
Template,
// @ts-ignore exists in webpack 5
RuntimeModule,
// @ts-ignore exists in webpack 5
RuntimeGlobals,
version,
} from 'webpack'

function webpack4(compiler: Compiler) {
// Webpack 4 does not have a method to handle interception of module
Expand Down Expand Up @@ -51,6 +59,64 @@ function webpack4(compiler: Compiler) {
})
}

function webpack5(compiler: Compiler) {
class ReactRefreshRuntimeModule extends RuntimeModule {
constructor() {
super('react refresh', 5)
}

generate() {
// @ts-ignore This exists in webpack 5
const { runtimeTemplate } = this.compilation
return Template.asString([
`${
RuntimeGlobals.interceptModuleExecution
}.push(${runtimeTemplate.basicFunction('options', [
`${
runtimeTemplate.supportsConst() ? 'const' : 'var'
} originalFactory = options.factory;`,
`options.factory = ${runtimeTemplate.basicFunction(
'moduleObject, moduleExports, webpackRequire',
[
// Legacy CSS implementations will `eval` browser code in a Node.js
// context to extract CSS. For backwards compatibility, we need to check
// we're in a browser context before continuing.
`${
runtimeTemplate.supportsConst() ? 'const' : 'var'
} hasRefresh = typeof self !== "undefined" && !!self.$RefreshInterceptModuleExecution$;`,
`${
runtimeTemplate.supportsConst() ? 'const' : 'var'
} cleanup = hasRefresh ? self.$RefreshInterceptModuleExecution$(moduleObject.id) : ${
runtimeTemplate.supportsArrowFunction()
? '() => {}'
: 'function() {}'
};`,
'try {',
Template.indent(
'originalFactory.call(this, moduleObject, moduleExports, webpackRequire);'
),
'} finally {',
Template.indent(`cleanup();`),
'}',
]
)}`,
])})`,
])
}
}

compiler.hooks.compilation.tap('ReactFreshWebpackPlugin', (compilation) => {
// @ts-ignore Exists in webpack 5
compilation.hooks.additionalTreeRuntimeRequirements.tap(
'ReactFreshWebpackPlugin',
(chunk: any) => {
// @ts-ignore Exists in webpack 5
compilation.addRuntimeModule(chunk, new ReactRefreshRuntimeModule())
}
)
})
}

class ReactFreshWebpackPlugin {
apply(compiler: Compiler) {
const webpackMajorVersion = parseInt(version ?? '', 10)
Expand All @@ -60,6 +126,10 @@ class ReactFreshWebpackPlugin {
webpack4(compiler)
break
}
case 5: {
webpack5(compiler)
break
}
default: {
throw new Error(
`ReactFreshWebpackPlugin does not support webpack v${webpackMajorVersion}.`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export default function () {
// AMP / No-JS mode does not inject these helpers:
'$RefreshHelpers$' in self
) {
const currentExports = module.__proto__.exports
const prevExports = module.hot.data?.prevExports ?? null
var currentExports = module.__proto__.exports
var prevExports = module.hot.data?.prevExports ?? null

// This cannot happen in MainTemplate because the exports mismatch between
// templating and execution.
Expand All @@ -40,7 +40,7 @@ export default function () {
if (self.$RefreshHelpers$.isReactRefreshBoundary(currentExports)) {
// Save the previous exports on update so we can compare the boundary
// signatures.
module.hot.dispose((data) => {
module.hot.dispose(function (data) {
data.prevExports = currentExports
})
// Unconditionally accept an update to this module, we'll check if it's
Expand Down Expand Up @@ -74,7 +74,7 @@ export default function () {
// new exports made it ineligible for being a boundary.
// We only care about the case when we were _previously_ a boundary,
// because we already accepted this update (accidental side effect).
const isNoLongerABoundary = prevExports !== null
var isNoLongerABoundary = prevExports !== null
if (isNoLongerABoundary) {
module.hot.invalidate()
}
Expand Down
30 changes: 15 additions & 15 deletions packages/react-refresh-utils/internal/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ function registerExportsForReactRefresh(
// (This is important for legacy environments.)
return
}
for (const key in moduleExports) {
for (var key in moduleExports) {
if (isSafeExport(key)) {
continue
}
const exportValue = moduleExports[key]
const typeID = moduleID + ' %exports% ' + key
var exportValue = moduleExports[key]
var typeID = moduleID + ' %exports% ' + key
RefreshRuntime.register(exportValue, typeID)
}
}
Expand All @@ -81,14 +81,14 @@ function isReactRefreshBoundary(moduleExports: unknown): boolean {
// Exit if we can't iterate over exports.
return false
}
let hasExports = false
let areAllExportsComponents = true
for (const key in moduleExports) {
var hasExports = false
var areAllExportsComponents = true
for (var key in moduleExports) {
hasExports = true
if (isSafeExport(key)) {
continue
}
const exportValue = moduleExports[key]
var exportValue = moduleExports[key]
if (!RefreshRuntime.isLikelyComponentType(exportValue)) {
areAllExportsComponents = false
}
Expand All @@ -100,12 +100,12 @@ function shouldInvalidateReactRefreshBoundary(
prevExports: unknown,
nextExports: unknown
): boolean {
const prevSignature = getRefreshBoundarySignature(prevExports)
const nextSignature = getRefreshBoundarySignature(nextExports)
var prevSignature = getRefreshBoundarySignature(prevExports)
var nextSignature = getRefreshBoundarySignature(nextExports)
if (prevSignature.length !== nextSignature.length) {
return true
}
for (let i = 0; i < nextSignature.length; i++) {
for (var i = 0; i < nextSignature.length; i++) {
if (prevSignature[i] !== nextSignature[i]) {
return true
}
Expand All @@ -114,25 +114,25 @@ function shouldInvalidateReactRefreshBoundary(
}

function getRefreshBoundarySignature(moduleExports: unknown): Array<unknown> {
const signature = []
var signature = []
signature.push(RefreshRuntime.getFamilyByType(moduleExports))
if (moduleExports == null || typeof moduleExports !== 'object') {
// Exit if we can't iterate over exports.
// (This is important for legacy environments.)
return signature
}
for (const key in moduleExports) {
for (var key in moduleExports) {
if (isSafeExport(key)) {
continue
}
const exportValue = moduleExports[key]
var exportValue = moduleExports[key]
signature.push(key)
signature.push(RefreshRuntime.getFamilyByType(exportValue))
}
return signature
}

let isUpdateScheduled: boolean = false
var isUpdateScheduled: boolean = false
function scheduleUpdate() {
if (isUpdateScheduled) {
return
Expand All @@ -143,7 +143,7 @@ function scheduleUpdate() {
}

isUpdateScheduled = true
setTimeout(() => {
setTimeout(function () {
isUpdateScheduled = false

// Only trigger refresh if the webpack HMR state is idle
Expand Down
3 changes: 1 addition & 2 deletions packages/react-refresh-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"webpack": "^4"
},
"devDependencies": {
"react-refresh": "0.8.3",
"webpack": "^4.42.1"
"react-refresh": "0.8.3"
}
}
16 changes: 10 additions & 6 deletions packages/react-refresh-utils/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,29 @@ declare const self: Window & RefreshRuntimeGlobals
RefreshRuntime.injectIntoGlobalHook(self)

// noop fns to prevent runtime errors during initialization
self.$RefreshReg$ = () => {}
self.$RefreshSig$ = () => (type) => type
self.$RefreshReg$ = function () {}
self.$RefreshSig$ = function () {
return function (type) {
return type
}
}

// Register global helpers
self.$RefreshHelpers$ = RefreshHelpers

// Register a helper for module execution interception
self.$RefreshInterceptModuleExecution$ = function (webpackModuleId) {
const prevRefreshReg = self.$RefreshReg$
const prevRefreshSig = self.$RefreshSig$
var prevRefreshReg = self.$RefreshReg$
var prevRefreshSig = self.$RefreshSig$

self.$RefreshReg$ = (type, id) => {
self.$RefreshReg$ = function (type, id) {
RefreshRuntime.register(type, webpackModuleId + ' ' + id)
}
self.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform

// Modeled after `useEffect` cleanup pattern:
// https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup
return () => {
return function () {
self.$RefreshReg$ = prevRefreshReg
self.$RefreshSig$ = prevRefreshSig
}
Expand Down
4 changes: 3 additions & 1 deletion packages/react-refresh-utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"compilerOptions": {
"module": "commonjs",
"sourceMap": true,
"esModuleInterop": true,
"target": "es3",
"target": "es2015",
"lib": ["dom"],
"downlevelIteration": true,
"preserveWatchOutput": true
Expand Down