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: React next fails on build (#726) #732

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
9 changes: 8 additions & 1 deletion src/flush-microtasks.js
@@ -1,3 +1,5 @@
import scheduleCallback from './scheduler-compat'
Copy link
Member

Choose a reason for hiding this comment

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

I don't think having the compat file is necessary.

Suggested change
import scheduleCallback from './scheduler-compat'
import React from 'react'
import semver from 'semver'
const globalObj = typeof window === 'undefined' ? global : window
let Scheduler = globalObj.Scheduler
const isModernScheduleCallbackSupported = semver.satisfies(React.version, '>=16.8.6')

Also, let's move this code to below the comments, just above the getIsUsingFakeTimers

Copy link
Member Author

Choose a reason for hiding this comment

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

You're definitely right about this one, I thought we don't want to use semver.. This was my go-to solution but since act-compat isn't using it and I didn't see it in the project, I didn't want to add it.
This simplifies everything.


/* istanbul ignore file */
// the part of this file that we need tested is definitely being run
// and the part that is not cannot easily have useful tests written
Expand All @@ -17,6 +19,7 @@ function getIsUsingFakeTimers() {

let didWarnAboutMessageChannel = false
let enqueueTask

try {
// read require off the module object to get around the bundlers.
// we don't want them to detect a require and bundle a Node polyfill.
kentcdodds marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -59,7 +62,11 @@ export default function flushMicroTasks() {
jest.advanceTimersByTime(0)
resolve()
} else {
enqueueTask(resolve)
scheduleCallback(null, () => {
enqueueTask(() => {
resolve()
})
})
Copy link
Member

Choose a reason for hiding this comment

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

With the change above, we no longer need this first argument

Suggested change
scheduleCallback(null, () => {
enqueueTask(() => {
resolve()
})
})
scheduleCallback(() => enqueueTask(resolve))

}
},
}
Expand Down
76 changes: 76 additions & 0 deletions src/scheduler-compat.js
@@ -0,0 +1,76 @@
const globalObj = typeof window === 'undefined' ? global : window

let Scheduler = globalObj.Scheduler
try {
const requireString = `require${Math.random()}`.slice(0, 7)
const nodeRequire = module && module[requireString]
// import React's scheduler so we'll be able to schedule our tasks later on.
Scheduler = nodeRequire.call(module, 'scheduler')
} catch (_err) {
console.error("The react version you're using doesn't support Scheduling")
}
// in case this react version has a Scheduler implementation, we use it,
// if not, we just create a function calling our callback
const NormalPriority = Scheduler
? Scheduler.NormalPriority || Scheduler.unstable_NormalPriority
: null

const isScheduleCallbackSupported = Scheduler !== undefined
let isModernScheduleCallbackSupported = null

const errorHandler = e => {
// If the error originated from Scheduler, it means we're in v16.8.6
if (
e.message === 'callback is not a function' &&
e.filename.includes('scheduler')
) {
console.error(e.error.stack, e.error.detail)
e.preventDefault()
} else {
console.error(e.error)
}
}

export default function scheduleCallback(_, cb) {
if (!isScheduleCallbackSupported) {
return cb()
}

if (isModernScheduleCallbackSupported === null) {
// patch console.error here
const originalConsoleError = console.error
console.error = function error(...args) {
/* if console.error fired *with that specific message* */
/* istanbul ignore next */
const firstArgIsString = typeof args[0] === 'string'
if (
firstArgIsString &&
args[0].indexOf('TypeError: callback is not a function') === 0
) {
// v16.8.6
isModernScheduleCallbackSupported = false
globalObj.removeEventListener('error', errorHandler)
console.error = originalConsoleError
return cb()
} else {
originalConsoleError.apply(console, args)
console.error = originalConsoleError
return cb()
}
}

globalObj.addEventListener('error', errorHandler)
return Scheduler.unstable_scheduleCallback(NormalPriority, () => {
console.error = originalConsoleError
isModernScheduleCallbackSupported = true
globalObj.removeEventListener('error', errorHandler)
return cb()
})
} else if (isModernScheduleCallbackSupported === false) {
return cb()
}

return Scheduler.unstable_scheduleCallback(NormalPriority, cb)
}

/* eslint no-console:0 */
Copy link
Member

Choose a reason for hiding this comment

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

We don't need this file anymore.

Suggested change
const globalObj = typeof window === 'undefined' ? global : window
let Scheduler = globalObj.Scheduler
try {
const requireString = `require${Math.random()}`.slice(0, 7)
const nodeRequire = module && module[requireString]
// import React's scheduler so we'll be able to schedule our tasks later on.
Scheduler = nodeRequire.call(module, 'scheduler')
} catch (_err) {
console.error("The react version you're using doesn't support Scheduling")
}
// in case this react version has a Scheduler implementation, we use it,
// if not, we just create a function calling our callback
const NormalPriority = Scheduler
? Scheduler.NormalPriority || Scheduler.unstable_NormalPriority
: null
const isScheduleCallbackSupported = Scheduler !== undefined
let isModernScheduleCallbackSupported = null
const errorHandler = e => {
// If the error originated from Scheduler, it means we're in v16.8.6
if (
e.message === 'callback is not a function' &&
e.filename.includes('scheduler')
) {
console.error(e.error.stack, e.error.detail)
e.preventDefault()
} else {
console.error(e.error)
}
}
export default function scheduleCallback(_, cb) {
if (!isScheduleCallbackSupported) {
return cb()
}
if (isModernScheduleCallbackSupported === null) {
// patch console.error here
const originalConsoleError = console.error
console.error = function error(...args) {
/* if console.error fired *with that specific message* */
/* istanbul ignore next */
const firstArgIsString = typeof args[0] === 'string'
if (
firstArgIsString &&
args[0].indexOf('TypeError: callback is not a function') === 0
) {
// v16.8.6
isModernScheduleCallbackSupported = false
globalObj.removeEventListener('error', errorHandler)
console.error = originalConsoleError
return cb()
} else {
originalConsoleError.apply(console, args)
console.error = originalConsoleError
return cb()
}
}
globalObj.addEventListener('error', errorHandler)
return Scheduler.unstable_scheduleCallback(NormalPriority, () => {
console.error = originalConsoleError
isModernScheduleCallbackSupported = true
globalObj.removeEventListener('error', errorHandler)
return cb()
})
} else if (isModernScheduleCallbackSupported === false) {
return cb()
}
return Scheduler.unstable_scheduleCallback(NormalPriority, cb)
}
/* eslint no-console:0 */