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

fix: allow certain variable names in development #33638

Merged
merged 4 commits into from Jan 27, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
115 changes: 59 additions & 56 deletions packages/react-refresh-utils/internal/ReactRefreshModule.runtime.ts
Expand Up @@ -17,67 +17,70 @@ declare const module: {
// This function gets unwrapped into global scope, which is why we don't invert
// if-blocks. Also, you cannot use `return`.
export default function () {
// 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.
if (
typeof self !== 'undefined' &&
// AMP / No-JS mode does not inject these helpers:
'$RefreshHelpers$' in self
) {
var currentExports = module.__proto__.exports
var prevExports = module.hot.data?.prevExports ?? null
// Wrapped in an IIFE to avoid polluting the global scope
;(function () {
// 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.
if (
typeof self !== 'undefined' &&
// AMP / No-JS mode does not inject these helpers:
'$RefreshHelpers$' in self
) {
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.
self.$RefreshHelpers$.registerExportsForReactRefresh(
currentExports,
module.id
)
// This cannot happen in MainTemplate because the exports mismatch between
// templating and execution.
self.$RefreshHelpers$.registerExportsForReactRefresh(
currentExports,
module.id
)

// A module can be accepted automatically based on its exports, e.g. when
// it is a Refresh Boundary.
if (self.$RefreshHelpers$.isReactRefreshBoundary(currentExports)) {
// Save the previous exports on update so we can compare the boundary
// signatures.
module.hot.dispose(function (data) {
data.prevExports = currentExports
})
// Unconditionally accept an update to this module, we'll check if it's
// still a Refresh Boundary later.
module.hot.accept()
// A module can be accepted automatically based on its exports, e.g. when
// it is a Refresh Boundary.
if (self.$RefreshHelpers$.isReactRefreshBoundary(currentExports)) {
// Save the previous exports on update so we can compare the boundary
// signatures.
module.hot.dispose(function (data) {
data.prevExports = currentExports
})
// Unconditionally accept an update to this module, we'll check if it's
// still a Refresh Boundary later.
module.hot.accept()

// This field is set when the previous version of this module was a
// Refresh Boundary, letting us know we need to check for invalidation or
// enqueue an update.
if (prevExports !== null) {
// A boundary can become ineligible if its exports are incompatible
// with the previous exports.
//
// For example, if you add/remove/change exports, we'll want to
// re-execute the importing modules, and force those components to
// re-render. Similarly, if you convert a class component to a
// function, we want to invalidate the boundary.
if (
self.$RefreshHelpers$.shouldInvalidateReactRefreshBoundary(
prevExports,
currentExports
)
) {
// This field is set when the previous version of this module was a
// Refresh Boundary, letting us know we need to check for invalidation or
// enqueue an update.
if (prevExports !== null) {
// A boundary can become ineligible if its exports are incompatible
// with the previous exports.
//
// For example, if you add/remove/change exports, we'll want to
// re-execute the importing modules, and force those components to
// re-render. Similarly, if you convert a class component to a
// function, we want to invalidate the boundary.
if (
self.$RefreshHelpers$.shouldInvalidateReactRefreshBoundary(
prevExports,
currentExports
)
) {
module.hot.invalidate()
} else {
self.$RefreshHelpers$.scheduleUpdate()
}
}
} else {
// Since we just executed the code for the module, it's possible that the
// 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).
var isNoLongerABoundary = prevExports !== null
if (isNoLongerABoundary) {
module.hot.invalidate()
} else {
self.$RefreshHelpers$.scheduleUpdate()
}
}
} else {
// Since we just executed the code for the module, it's possible that the
// 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).
var isNoLongerABoundary = prevExports !== null
if (isNoLongerABoundary) {
module.hot.invalidate()
}
}
}
})()
}
44 changes: 44 additions & 0 deletions test/development/acceptance/ReactRefreshModule.test.ts
@@ -0,0 +1,44 @@
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { sandbox } from './helpers'

describe('ReactRefreshModule', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {},
skipStart: true,
})
})
afterAll(() => next.destroy())

it('should allow any variable names', async () => {
const { session, cleanup } = await sandbox(next, new Map([]))
expect(await session.hasRedbox()).toBe(false)

const variables = [
'_a',
'_b',
'currentExports',
'prevExports',
'isNoLongerABoundary',
]

for await (const variable of variables) {
await session.patch(
'pages/index.js',
`import { default as ${variable} } from 'next/link'
export default function Page() {
return null
}`
)
expect(await session.hasRedbox()).toBe(false)
expect(next.cliOutput).not.toContain(
`'${variable}' has already been declared`
)
}

await cleanup()
})
})