Skip to content

Commit

Permalink
✨ improve: improved webpack internals visibility (#2284)
Browse files Browse the repository at this point in the history
- improved visibility into webpack lifecycle via `@roots/bud-extensions/webpack-lifecycle-plugin`
- flatten `context` (provided to `@roots/bud/factory`)
- organize CLI flags using modules
- downgrade `import-meta-resolve` to 2.2.2 (from 3.0.0) for backwards compatibility
- disable `experiments.backCompat` for improved performance: see https://www.tines.com/blog/understanding-why-our-build-got-15x-slower-with-webpack-5

## Type of change

**MINOR: feature**
  • Loading branch information
kellymears authored May 22, 2023
1 parent 360aa61 commit b64c725
Show file tree
Hide file tree
Showing 165 changed files with 1,936 additions and 1,150 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-simple-import-sort": "10.0.0",
"execa": "7.1.1",
"import-meta-resolve": "3.0.0",
"import-meta-resolve": "2.2.2",
"lodash": "4.17.21",
"playwright": "1.33.0",
"pm2": "^5.3.0",
Expand Down
20 changes: 7 additions & 13 deletions sources/@repo/test-kit/bud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,19 @@ export const repoPath = (...path: Array<string>) =>
export const basedir = repoPath(`tests`, `util`, `project`)

export const factory = async (
overrides?: Partial<Options.CommandContext>,
run?: boolean,
overrides: Partial<Options.Context> = {},
): Promise<Bud> => {
const bud = await makeInstance({
basedir,
force: true,
dry: true,
log: false,
notify: false,
mode: `production`,
...overrides,
args: {
force: true,
dry: true,
log: false,
notify: false,
...(overrides?.args ?? {}),
},
})

if (!bud.isCLI())
throw new Error(`test error: bud is not a CLI instance`)

if (run) await bud.run()
await bud.run()

return bud
}
Expand Down
16 changes: 0 additions & 16 deletions sources/@roots/bud-api/src/methods/html/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {dirname, resolve} from 'node:path'
import {fileURLToPath} from 'node:url'

import type * as HTMLExtension from '@roots/bud-extensions/html-webpack-plugin'
import type * as InterpolateHTMLExtension from '@roots/bud-extensions/interpolate-html-webpack-plugin'
import type {Bud} from '@roots/bud-framework'
import isObject from '@roots/bud-support/lodash/isObject'
import isUndefined from '@roots/bud-support/lodash/isUndefined'
Expand Down Expand Up @@ -34,18 +33,3 @@ export const getHtmlPluginOptions = (

return {template, ...omit(options, `replace`, `template`)}
}

export const getInterpolatePluginOptions = (
bud: Bud,
options: Parameters[0],
): InterpolateHTMLExtension.Options => {
if (
!isObject(options) ||
isUndefined(options) ||
isUndefined(options.replace)
) {
return bud.env.getPublicEnv()
}

return {...bud.env.getPublicEnv(), ...options.replace}
}
91 changes: 15 additions & 76 deletions sources/@roots/bud-api/src/methods/html/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ describe(`bud.html`, () => {
`@roots/bud-extensions/interpolate-html-webpack-plugin`,
)
htmlEnableSpy = vi.spyOn(htmlPlugin, `enable`)
htmlSetOptionsSpy = vi.spyOn(htmlPlugin, `setOptions`)
htmlSetOptionsSpy = vi.spyOn(htmlPlugin, `set`)
interpolateEnableSpy = vi.spyOn(interpolatePlugin, `enable`)
interpolateSetOptionsSpy = vi.spyOn(interpolatePlugin, `setOptions`)
interpolateSetOptionsSpy = vi.spyOn(interpolatePlugin, `set`)

html = source.html.bind(bud)
})
Expand All @@ -50,14 +50,9 @@ describe(`bud.html`, () => {
const returned = await html(false)

expect(htmlEnableSpy).toHaveBeenCalledWith(false)
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(
helpers.defaultHtmlPluginOptions,
Object.entries(helpers.defaultHtmlPluginOptions).forEach(v =>
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(...v),
)
expect(interpolateEnableSpy).toHaveBeenCalledWith(false)
expect(interpolateSetOptionsSpy).toHaveBeenCalledWith({
APP_DESCRIPTION: `test app description`,
APP_TITLE: `bud.js test app`,
})

expect(returned).toBe(bud)
})
Expand All @@ -66,14 +61,9 @@ describe(`bud.html`, () => {
const returned = await html(true)

expect(htmlEnableSpy).toHaveBeenCalledWith(true)
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(
helpers.defaultHtmlPluginOptions,
Object.entries(helpers.defaultHtmlPluginOptions).forEach(v =>
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(...v),
)
expect(interpolateEnableSpy).toHaveBeenCalledWith(true)
expect(interpolateSetOptionsSpy).toHaveBeenCalledWith({
APP_DESCRIPTION: `test app description`,
APP_TITLE: `bud.js test app`,
})

expect(returned).toBe(bud)
})
Expand All @@ -82,14 +72,9 @@ describe(`bud.html`, () => {
const returned = await html()

expect(htmlEnableSpy).toHaveBeenCalledWith(true)
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(
helpers.defaultHtmlPluginOptions,
Object.entries(helpers.defaultHtmlPluginOptions).forEach(v =>
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(...v),
)
expect(interpolateEnableSpy).toHaveBeenCalledWith(true)
expect(interpolateSetOptionsSpy).toHaveBeenCalledWith({
APP_DESCRIPTION: `test app description`,
APP_TITLE: `bud.js test app`,
})

expect(returned).toBe(bud)
})
Expand All @@ -98,14 +83,9 @@ describe(`bud.html`, () => {
const returned = await html({})

expect(htmlEnableSpy).toHaveBeenCalledWith(true)
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(
helpers.defaultHtmlPluginOptions,
Object.entries(helpers.defaultHtmlPluginOptions).forEach(v =>
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(...v),
)
expect(interpolateEnableSpy).toHaveBeenCalledWith(true)
expect(interpolateSetOptionsSpy).toHaveBeenCalledWith({
APP_DESCRIPTION: `test app description`,
APP_TITLE: `bud.js test app`,
})

expect(returned).toBe(bud)
})
Expand All @@ -114,33 +94,20 @@ describe(`bud.html`, () => {
await html({template: `test`, replace: {foo: `bar`}})

expect(interpolateEnableSpy).toHaveBeenCalledWith(true)
expect(interpolateSetOptionsSpy).toHaveBeenCalledWith({
APP_DESCRIPTION: `test app description`,
APP_TITLE: `bud.js test app`,
foo: `bar`,
})
expect(interpolateSetOptionsSpy).toHaveBeenCalledWith(`foo`, `bar`)
expect(htmlEnableSpy).toHaveBeenCalledWith(true)
expect(budPathSpy).toHaveBeenCalledWith(`test`)
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(
expect.objectContaining({
template: expect.stringMatching(/\/test$/),
}),
`template`,
bud.path(`test`),
)
})

it(`should leave absolute paths alone when passed as options.template`, async () => {
await html({template: `/test`, replace: {foo: `bar`}})

expect(interpolateSetOptionsSpy).toHaveBeenCalledWith({
APP_DESCRIPTION: `test app description`,
APP_TITLE: `bud.js test app`,
foo: `bar`,
})
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(
expect.objectContaining({
template: expect.stringMatching(/\/test$/),
}),
)
expect(interpolateSetOptionsSpy).toHaveBeenCalledWith(`foo`, `bar`)
expect(htmlSetOptionsSpy).toHaveBeenCalledWith(`template`, `/test`)
})

it(`getHtmlPluginOptions returns normalized options with \`template\` when it is not included`, async () => {
Expand Down Expand Up @@ -193,32 +160,4 @@ describe(`bud.html`, () => {
helpers.getHtmlPluginOptions(bud, {template: `foo`})
expect(budPathSpy).toHaveBeenCalledWith(`foo`)
})

it.each([true, false, undefined, {}])(
`getInterpolatePluginOptions calls bud env`,
async value => {
const envSpy = vi.spyOn(bud.env, `getPublicEnv`)
helpers.getInterpolatePluginOptions(bud, value)
expect(envSpy).toHaveBeenCalledOnce()
},
)

it(`appends options.replace values to publicEnv`, async () => {
const envSpy = vi.spyOn(bud.env, `getPublicEnv`)

const result = helpers.getInterpolatePluginOptions(bud, {
replace: {
foo: `bar`,
},
})

expect(envSpy).toHaveBeenCalledOnce()
expect(result).toEqual(
expect.objectContaining({
foo: `bar`,
APP_DESCRIPTION: `test app description`,
APP_TITLE: `bud.js test app`,
}),
)
})
}, 60000)
33 changes: 22 additions & 11 deletions sources/@roots/bud-api/src/methods/html/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type * as HTMLExtension from '@roots/bud-extensions/html-webpack-plugin'
import type * as InterpolateHTMLExtension from '@roots/bud-extensions/interpolate-html-webpack-plugin'
import type {Bud} from '@roots/bud-framework'
import isObject from '@roots/bud-support/lodash/isObject'

type Options = HTMLExtension.Options & {
replace?: InterpolateHTMLExtension.Options
Expand All @@ -16,21 +17,31 @@ export interface html {
* Set HTML template
*/
export const html: html = async function (this: Bud, options) {
const {getHtmlPluginOptions, getInterpolatePluginOptions} = await import(
`./helpers.js`
)
const {getHtmlPluginOptions} = await import(`./helpers.js`)

const enabled = options !== false

this.extensions
.get(`@roots/bud-extensions/html-webpack-plugin`)
?.setOptions(getHtmlPluginOptions(this, options))
.enable(enabled)
const htmlExtension = this.extensions.get(
`@roots/bud-extensions/html-webpack-plugin`,
)
htmlExtension.enable(enabled)

const htmlOptions = getHtmlPluginOptions(this, options)
if (isObject(htmlOptions)) {
Object.entries(htmlOptions).forEach(v => htmlExtension.set(...v))
}

this.extensions
.get(`@roots/bud-extensions/interpolate-html-webpack-plugin`)
?.setOptions(getInterpolatePluginOptions(this, options))
.enable(enabled)
const interpolateVariablesExtension = this.extensions.get(
`@roots/bud-extensions/interpolate-html-webpack-plugin`,
)
interpolateVariablesExtension.enable(enabled)

if (isObject(options) && isObject(options.replace)) {
Object.entries(options.replace).forEach(
(v: [string, string | RegExp]) =>
interpolateVariablesExtension.set(...v),
)
}

return this
}
2 changes: 1 addition & 1 deletion sources/@roots/bud-api/vendor/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
<head>
<meta charset="utf-8" />
<meta name=viewport content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="%APP_DESCRIPTION%" />
<title>%APP_TITLE%</title>
</head>

<body>
<noscript>%NO_SCRIPT%</noscript>
<div id="root"></div>
</body>

Expand Down
2 changes: 1 addition & 1 deletion sources/@roots/bud-babel/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default class BabelExtension extends Extension {
)

this.configFileOptions =
this.configFile.module.default ?? this.configFile.module
this.configFile.module?.default ?? this.configFile.module

hooks.on(`build.cache.buildDependencies`, paths => {
if (isString(this.configFile)) {
Expand Down
4 changes: 3 additions & 1 deletion sources/@roots/bud-build/src/config/experiments.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type {Factory} from './index.js'

export const experiments: Factory<`experiments`> = async ({hooks}) =>
hooks.filter(`build.experiments`, undefined)
hooks.filter(`build.experiments`, {
backCompat: false,
})
2 changes: 1 addition & 1 deletion sources/@roots/bud-build/src/config/output/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const output: Factory<`output`> = async ({
filename: filename({filter, relPath}),
module: filter(`build.output.module`, false),
path: filter(`build.output.path`, path(`@dist`)),
pathinfo: filter(`build.output.pathinfo`),
pathinfo: filter(`build.output.pathinfo`, false),
publicPath: filter(`build.output.publicPath`, `auto`),
scriptType: filter(
`build.output.scriptType`,
Expand Down
5 changes: 1 addition & 4 deletions sources/@roots/bud-build/src/config/profile.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import type {Factory} from './index.js'

export const profile: Factory<`profile`> = async bud =>
bud.hooks.filter(
`build.profile`,
bud.isCLI() && bud.context.args[`debug`],
)
bud.hooks.filter(`build.profile`, bud.context.debug)
2 changes: 1 addition & 1 deletion sources/@roots/bud-build/src/handlers/items/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const minicss: Factory<Item> = async ({makeItem}) =>
.setLoader(`minicss`)
.setIdent(`minicss`)
.setOptions(app => ({
publicPath: app.hooks.filter(`build.output.publicPath`),
publicPath: app.publicPath(),
}))

/**
Expand Down
4 changes: 3 additions & 1 deletion sources/@roots/bud-build/src/handlers/rules/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export const json: Factory = async ({filter, makeRule, path}) =>
.setType(`json`)
.setInclude([() => path()])
.setTest(filter(`pattern.json`))
.setParser({parse: json5.parse})
.setParser({
parse: json5.parse,
})
4 changes: 3 additions & 1 deletion sources/@roots/bud-build/src/handlers/rules/toml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export const toml: Factory = async ({filter, makeRule, path}) =>
.setType(`json`)
.setInclude([() => path()])
.setTest(() => filter(`pattern.toml`))
.setParser({parse: tomlParser.parse})
.setParser({
parse: tomlParser.parse,
})
2 changes: 1 addition & 1 deletion sources/@roots/bud-cache/src/invalidate-cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default class InvalidateCacheExtension extends Extension {
public override async register(bud: Bud) {
const invalidate = await bud.fs?.exists(this.invalidationFile)

if (invalidate || (bud.isCLI() && bud.context.args.force)) {
if (invalidate || bud.context.force) {
await bud.fs.remove(this.invalidationFile)
await bud.fs.remove(bud.cache.cacheDirectory)
}
Expand Down
4 changes: 2 additions & 2 deletions sources/@roots/bud-cache/src/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export default class Cache
public get type(): 'memory' | 'filesystem' {
return this.app.hooks.filter(
`build.cache.type`,
this.app.isCLI() && isString(this.app.context.args.cache)
? this.app.context.args.cache
isString(this.app.context.cache)
? this.app.context.cache
: `filesystem`,
)
}
Expand Down
6 changes: 4 additions & 2 deletions sources/@roots/bud-client/src/hot/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,14 @@ export const client = async (
}

/* Instantiate indicator, overlay */
await components.make(options)
try {
await components.make(options)
} catch (error) {}

/* Instantiate eventSource */
const events = injectEvents(EventSource).make(options)

if (!window.bud.listeners[options.name]) {
if (!window.bud.listeners?.[options.name]) {
window.bud.listeners[options.name] = async payload => {
if (!payload) return

Expand Down
7 changes: 1 addition & 6 deletions sources/@roots/bud-compiler/src/compiler.service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,8 @@ export class Compiler extends Service implements Contract.Service {

await this.app.hooks.fire(`compiler.before`, this.app)

if (this.app.isCLI() && this.app.context.args.dry) {
this.logger.timeEnd(`initialize`)
this.logger.log(`running in dry mode. exiting early.`)
return
}

this.logger.timeEnd(`initialize`)

this.logger.await(`compilation`)

this.instance = this.implementation(this.config)
Expand Down
Loading

0 comments on commit b64c725

Please sign in to comment.