Skip to content

Commit

Permalink
馃┕ fix(patch): WordPress module reload failures (#2530)
Browse files Browse the repository at this point in the history
This fixes #2529 and other associated problems with hot module reload in certain WordPress environments.

## The problem

If `SCRIPT_DEBUG` is not set and a script declares `wp-react-refresh-runtime` as a dependency, WordPress will _**silently** omit the entire enqueue request_.

What the... 馃檲

Bedrock installs don't trigger this behavior because Bedrock defines it:

https://github.com/roots/bedrock/blob/master/config/environments/development.php#L14

## Solution

This change omits including `wp-react-refresh-runtime` in `entrypoints.json` and does not externalize `react-refresh/runtime.js` if `SCRIPT_DEBUG` isn't set in a .env file somewhere.

## Type of change

**PATCH: backwards compatible change**
  • Loading branch information
kellymears committed Dec 20, 2023
1 parent 965a97f commit 357d87a
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 183 deletions.
194 changes: 98 additions & 96 deletions sources/@roots/bud-preset-wordpress/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import type {Bud} from '@roots/bud-framework'
import type BudWordPressDependencies from '@roots/bud-wordpress-dependencies'
import type BudWordPressExternals from '@roots/bud-wordpress-externals'
import type WordPressThemeJSON from '@roots/bud-wordpress-theme-json'
import type WordPressThemeJson from '@roots/bud-wordpress-theme-json'

import {join} from 'node:path'

import {
DynamicOption,
Extension,
type Option,
type PublicExtensionApi,
type OptionGetter,
type OptionSetter,
} from '@roots/bud-framework/extension'
import {
bind,
Expand All @@ -25,80 +28,69 @@ interface Options {
exclude: Array<string>
hmr: boolean
notify: boolean
scriptDebug: boolean
}

/**
* WordPress Preset API
* WordPress preset
*/
interface PublicExtension extends PublicExtensionApi<BudPresetWordPress> {
/**
* {@link BudWordPressDependencies}
*/
dependencies: BudWordPressDependencies
@label(`@roots/bud-preset-wordpress`)
@dependsOn([
`@roots/bud-preset-recommend`,
`@roots/bud-wordpress-externals`,
`@roots/bud-wordpress-dependencies`,
`@roots/bud-wordpress-theme-json`,
`@roots/bud-react`,
])
@options<Options>({
exclude: [],
hmr: true,
notify: true,
scriptDebug: DynamicOption.make(({env}) => env.isTrue(`SCRIPT_DEBUG`)),
})
@expose(`wp`)
export default class BudPresetWordPress extends Extension<Options> {
public declare scriptDebug: Options[`scriptDebug`]
public declare getScriptDebug: OptionGetter<Options, `scriptDebug`>
public declare setScriptDebug: OptionSetter<
BudPresetWordPress,
Options,
`scriptDebug`
>

/**
* Exclude dependencies from externals and the `entrypoints.json` manifest
*
* @default []
*/
exclude: Option<PublicExtension, Options, `exclude`>[`value`]
/**
* {@link BudWordPressExternals}
*/
externals: BudWordPressExternals

public declare readonly exclude: Options[`exclude`]
/**
* Get excluded dependencies
*
* @returns Array<string>
*/
getExclude: Option<PublicExtension, Options, `exclude`>[`get`]
public declare getExclude: OptionGetter<Options, `exclude`>
/**
* Get `@roots/wordpress-hmr` functionality
*
* @returns boolean
*/
getHmr: Option<PublicExtension, Options, `hmr`>[`get`]
/**
* Get WordPress editor toast notifications
*
* @returns boolean
* Set excluded dependencies
*/
getNotify: Option<PublicExtension, Options, `notify`>[`get`]
public declare setExclude: OptionSetter<
BudPresetWordPress,
Options,
`exclude`
>

/**
* Enable `@roots/wordpress-hmr` functionality
*
* @default true
*/
hmr: Option<PublicExtension, Options, `hmr`>[`value`]
public declare readonly hmr: Options[`hmr`]
/**
* {@link WordPressThemeJSON}
*/
json: WordPressThemeJSON
/**
* WordPress editor toast notifications
*
* @default true
*/
notify: Option<PublicExtension, Options, `notify`>[`value`]

/**
* Set excluded dependencies
*
* @param value Array<string>
* @returns this
*
* @example
* ```js
* bud.wp.setExclude(['react'])
* ```
* Get `@roots/wordpress-hmr` functionality
*
* @example
* ```js
* bud.wp.setExclude(exclude => [...exclude, 'react'])
* ```
* @returns boolean
*/
setExclude: Option<PublicExtension, Options, `exclude`>[`set`]
public declare getHmr: OptionGetter<Options, `hmr`>
/**
* Set `@roots/wordpress-hmr` functionality
*
Expand All @@ -115,9 +107,22 @@ interface PublicExtension extends PublicExtensionApi<BudPresetWordPress> {
* bud.wp.setHmr(hmr => false)
* ```
*/
setHmr: Option<PublicExtension, Options, `hmr`>[`set`]
public declare setHmr: OptionSetter<BudPresetWordPress, Options, `hmr`>

/**
* WordPress editor toast notifications value
*
* @returns boolean
*/
public declare notify: Options[`notify`]
/**
* Get WordPress editor toast notifications
*
* @returns boolean
*/
public declare getNotify: OptionGetter<Options, `notify`>
/**
* Set WordPress editor toast notifications
* Toggle WordPress editor toast notifications
*
* @param value boolean
* @returns this
Expand All @@ -132,50 +137,31 @@ interface PublicExtension extends PublicExtensionApi<BudPresetWordPress> {
* bud.wp.setNotify(notify => false)
* ```
*/
setNotify: Option<PublicExtension, Options, `notify`>[`set`]
}

/**
* WordPress preset
*/
@label(`@roots/bud-preset-wordpress`)
@dependsOn([
`@roots/bud-preset-recommend`,
`@roots/bud-wordpress-externals`,
`@roots/bud-wordpress-dependencies`,
`@roots/bud-wordpress-theme-json`,
`@roots/bud-react`,
])
@options<Options>({
exclude: [],
hmr: true,
notify: true,
})
@expose(`wp`)
export default class BudPresetWordPress
extends Extension<Options>
implements PublicExtension
{
public declare exclude: PublicExtension[`exclude`]
public declare getExclude: PublicExtension[`getExclude`]
public declare setExclude: PublicExtension[`setExclude`]

public declare hmr: PublicExtension[`hmr`]
public declare getHmr: PublicExtension[`getHmr`]
public declare setHmr: PublicExtension[`setHmr`]

public declare notify: PublicExtension[`notify`]
public declare getNotify: PublicExtension[`getNotify`]
public declare setNotify: PublicExtension[`setNotify`]
public declare setNotify: OptionSetter<
BudPresetWordPress,
Options,
`notify`
>

/**
* {@link BudWordPressDependencies}
*/
public get dependencies(): BudWordPressDependencies {
return this.app.extensions.get(`@roots/bud-wordpress-dependencies`)
}
/**
* {@link BudWordPressExternals}
*/
public get externals(): BudWordPressExternals {
return this.app.extensions.get(`@roots/bud-wordpress-externals`)
}
public get json(): WordPressThemeJSON {
return this.app.extensions.get(`@roots/bud-wordpress-theme-json`)
/**
* {@link WordPressThemeJson}
*/
public get json(): WordPressThemeJson {
return this.app.extensions.get(
`@roots/bud-wordpress-theme-json`,
) as unknown as WordPressThemeJson
}

/**
Expand All @@ -186,7 +172,12 @@ export default class BudPresetWordPress
await this.compilerCheck(bud)

if (bud.extensions.has(`@roots/bud-tailwindcss`))
await bud.extensions.add(`@roots/bud-tailwindcss-theme-json`)
await bud.extensions.add(
await this.resolve(
`@roots/bud-tailwindcss-theme-json`,
import.meta.url,
),
)
}

/**
Expand All @@ -211,6 +202,19 @@ export default class BudPresetWordPress
provided.set(`React`, undefined)
}

/**
* If `SCRIPT_DEBUG` env value is not set, exclude `react-refresh/runtime` from externals
* and inclusion in entrypoints.json dependencies array(s).
*
* Unless user has manually overridden this. Common example: if they have set SCRIPT_DEBUG
* directly in their WordPress config file (which bud.js does not have access to it).
*/
!this.getScriptDebug() &&
this.setExclude((exclude = []) => [
...exclude,
join(`react-refresh`, `runtime`),
])

/**
* Exclude anything specified in {@link Options.exclude}
*/
Expand All @@ -224,7 +228,7 @@ export default class BudPresetWordPress
@bind
private async handleHmr({build, hooks}: Bud) {
/** Bail if hmr option is false */
if (!this.hmr) return
if (!this.getHmr()) return

/** Source loader */
const loader = await this.resolve(
Expand All @@ -249,7 +253,7 @@ export default class BudPresetWordPress
.setItem(`@roots/wordpress-hmr/loader`, {
loader: `@roots/wordpress-hmr/loader`,
options: {
notify: this.get(`notify`),
notify: this.getNotify(),
},
})

Expand Down Expand Up @@ -340,5 +344,3 @@ export default class BudPresetWordPress
}
}
}

export type {PublicExtension}
6 changes: 2 additions & 4 deletions sources/@roots/bud-preset-wordpress/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
* @see https://github.com/roots/bud
*/

import BudPresetWordPress, {
type PublicExtension,
} from '@roots/bud-preset-wordpress/extension'
import BudPresetWordPress from '@roots/bud-preset-wordpress/extension'

declare module '@roots/bud-framework' {
interface Bud {
wp: PublicExtension
wp: BudPresetWordPress
}

interface Modules {
Expand Down
4 changes: 4 additions & 0 deletions sources/@roots/bud-react/src/extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export default class BudReact extends Extension {
bud.swc.setJsc(
merge(bud.swc.jsc, {transform: {react: {runtime: `automatic`}}}),
)
bud.swc.setTransform((transform = {}) => ({
react: {runtime: `automatic`, ...(transform.react ?? {})},
...transform,
}))
}

if (bud.babel) {
Expand Down
Loading

0 comments on commit 357d87a

Please sign in to comment.