From 0945f3cf6fb356eb22ee71e4c1c32fd42f9f85f4 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Thu, 3 Aug 2023 16:56:22 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20improve:=20typings=20&=20tests=20(#?= =?UTF-8?q?2395)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - typings: enable strict mode for `@roots/bud-cache` - typings: enable strict mode for `@roots/bud-compiler` ## Type of change **PATCH: backwards compatible change** --- config/eslint.config.cjs | 8 +- config/vitest/alias.ts | 2 + .../@roots/blade-loader/src/asset-loader.cts | 4 +- .../@roots/blade-loader/src/loaders/index.cts | 2 +- sources/@roots/blade-loader/src/plugin.ts | 2 +- sources/@roots/bud-cache/package.json | 21 +----- sources/@roots/bud-cache/src/index.ts | 2 +- .../src/{service/index.ts => service.ts} | 6 +- sources/@roots/bud-cache/test/service.test.ts | 75 +++++++++++++++++-- sources/@roots/bud-cache/tsconfig.json | 3 +- sources/@roots/bud-client/package.json | 10 ++- sources/@roots/bud-client/src/dom-ready.ts | 20 +++++ sources/@roots/bud-client/src/index.ts | 5 +- sources/@roots/bud-client/src/lazy.ts | 41 ++++++++++ sources/@roots/bud-compiler/src/service.tsx | 20 ++--- .../@roots/bud-compiler/test/compiler.test.ts | 12 ++- sources/@roots/bud-compiler/tsconfig.json | 6 +- .../bud-dashboard/test/service.test.tsx | 3 +- .../bud-emotion/test/dependencies.test.ts | 2 +- sources/@roots/bud-sass/src/extension.ts | 2 +- .../entrypoints-webpack-plugin/src/plugin.ts | 10 ++- sources/@roots/sage/package.json | 1 + sources/@roots/sage/src/acorn/index.ts | 25 +++---- sources/@roots/sage/src/client/dom-ready.ts | 21 +----- sources/@roots/sage/src/client/index.ts | 6 +- sources/@roots/sage/src/client/lazy.ts | 42 +---------- sources/@roots/sage/src/sage/index.ts | 4 +- .../@roots/sage/test/acorn/extension.test.ts | 44 ++++++++++- sources/@roots/sage/tsconfig.json | 2 + sources/@roots/wordpress-hmr/src/editor.ts | 22 +++--- sources/@roots/wordpress-hmr/src/plugin.ts | 6 +- yarn.lock | 1 + 32 files changed, 272 insertions(+), 158 deletions(-) rename sources/@roots/bud-cache/src/{service/index.ts => service.ts} (97%) create mode 100644 sources/@roots/bud-client/src/dom-ready.ts create mode 100644 sources/@roots/bud-client/src/lazy.ts diff --git a/config/eslint.config.cjs b/config/eslint.config.cjs index d7757783d8..6bd3623df5 100644 --- a/config/eslint.config.cjs +++ b/config/eslint.config.cjs @@ -28,7 +28,13 @@ module.exports = { ], overrides: [ { - files: [`tests/**/*`, `**/*.spec.ts`, `**/*.test.ts`], + files: [ + `**/tests/**`, + `**/*.test.ts`, + `**/*.test.tsx`, + `**/*.test.cts`, + `**/*.test.mts`, + ], rules: { [`n/no-extraneous-import`]: OFF, [`n/no-unpublished-import`]: OFF, diff --git a/config/vitest/alias.ts b/config/vitest/alias.ts index e7c8b64d09..351143a2b8 100644 --- a/config/vitest/alias.ts +++ b/config/vitest/alias.ts @@ -12,8 +12,10 @@ export default { ...packages.reduce((aliases, packageRoot) => { const signifier = relative(path(), packageRoot) aliases[signifier] = join(packageRoot, `src`) + aliases[join(signifier, `*`)] = join(packageRoot, `src`, `*`) return aliases }, {}), + '@roots/bud-cache': path(`sources/@roots/bud-cache/src`), '@roots/filesystem/src/vendor/sdk': path( `sources/@roots/filesystem/vendor/sdk`, ), diff --git a/sources/@roots/blade-loader/src/asset-loader.cts b/sources/@roots/blade-loader/src/asset-loader.cts index 638b3c4468..0c0df41af7 100644 --- a/sources/@roots/blade-loader/src/asset-loader.cts +++ b/sources/@roots/blade-loader/src/asset-loader.cts @@ -1,7 +1,7 @@ -import {join} from 'node:path' - import type {LoaderDefinitionFunction} from 'webpack' +import {join} from 'node:path' + const loader: LoaderDefinitionFunction<{publicPath?: string}> = async function (source: string) { const options = this.getOptions() diff --git a/sources/@roots/blade-loader/src/loaders/index.cts b/sources/@roots/blade-loader/src/loaders/index.cts index c5aaf07843..b530edc84d 100644 --- a/sources/@roots/blade-loader/src/loaders/index.cts +++ b/sources/@roots/blade-loader/src/loaders/index.cts @@ -8,8 +8,8 @@ import * as vue from './vue.cjs' export const repository = { css: {...css, loader: require.resolve(`./css.cjs`)}, - scss: {...scss, loader: require.resolve(`./scss.cjs`)}, js: {...js, loader: require.resolve(`./js.cjs`)}, + scss: {...scss, loader: require.resolve(`./scss.cjs`)}, ts: {...ts, loader: require.resolve(`./ts.cjs`)}, vue: {...vue, loader: require.resolve(`./vue.cjs`)}, } diff --git a/sources/@roots/blade-loader/src/plugin.ts b/sources/@roots/blade-loader/src/plugin.ts index 5c07518396..74c90e9ade 100644 --- a/sources/@roots/blade-loader/src/plugin.ts +++ b/sources/@roots/blade-loader/src/plugin.ts @@ -16,7 +16,7 @@ export default class BladeWebpackPlugin implements WebpackPluginInstance { } /** - * Apply plugin + * {@link WebpackPluginInstance.apply} */ public async apply(compiler: Compiler) { const use = [`@roots/blade-loader/asset-loader`] diff --git a/sources/@roots/bud-cache/package.json b/sources/@roots/bud-cache/package.json index 4450e18c71..3d1b96297b 100644 --- a/sources/@roots/bud-cache/package.json +++ b/sources/@roots/bud-cache/package.json @@ -49,22 +49,8 @@ ], "type": "module", "exports": { - ".": { - "import": "./lib/index.js", - "default": "./lib/index.js" - }, - "./service": { - "import": "./lib/service.js", - "default": "./lib/service.js" - }, - "./invalidate-cache": { - "import": "./lib/invalidate-cache/index.js", - "default": "./lib/invalidate-cache/index.js" - }, - "./types": { - "import": "./lib/types.js", - "default": "./lib/types.js" - } + ".": "./lib/index.js", + "./service": "./lib/service.js" }, "typesVersions": { "*": { @@ -73,9 +59,6 @@ ], "service": [ "./lib/service.d.ts" - ], - "types": [ - "./lib/types.d.ts" ] } }, diff --git a/sources/@roots/bud-cache/src/index.ts b/sources/@roots/bud-cache/src/index.ts index 613b0c46be..2b35791814 100644 --- a/sources/@roots/bud-cache/src/index.ts +++ b/sources/@roots/bud-cache/src/index.ts @@ -8,4 +8,4 @@ * @see {@link https://github.com/roots/bud} */ -export {default} from './service/index.js' +export {default} from '@roots/bud-cache/service' diff --git a/sources/@roots/bud-cache/src/service/index.ts b/sources/@roots/bud-cache/src/service.ts similarity index 97% rename from sources/@roots/bud-cache/src/service/index.ts rename to sources/@roots/bud-cache/src/service.ts index 17c5836ceb..0d7071ee8f 100644 --- a/sources/@roots/bud-cache/src/service/index.ts +++ b/sources/@roots/bud-cache/src/service.ts @@ -9,16 +9,16 @@ import {bind} from '@roots/bud-support/decorators/bind' import isString from '@roots/bud-support/lodash/isString' /** - * Cache service class + * {@link Bud.cache} */ export default class Cache extends Service implements BudCache { /** * {@link BudCache.enabled} */ - public enabled: boolean + public declare enabled: boolean /** - * {@link Extension.boot} + * {@link Service.boot} */ public override async boot?(bud: Bud) { if (bud.context.force === true) { diff --git a/sources/@roots/bud-cache/test/service.test.ts b/sources/@roots/bud-cache/test/service.test.ts index 83c0ea7668..7f74464f74 100644 --- a/sources/@roots/bud-cache/test/service.test.ts +++ b/sources/@roots/bud-cache/test/service.test.ts @@ -1,9 +1,74 @@ -import {describe, expect, it} from 'vitest' - -import Service from '../src/index.js' +import {Bud, factory} from '@repo/test-kit' +import Cache from '@roots/bud-cache/service' +import {Service} from '@roots/bud-framework/service' +import {beforeEach, describe, expect, it} from 'vitest' describe(`@roots/bud-cache`, () => { - it(`should be constructable`, () => { - expect(Service).toBeInstanceOf(Function) + let bud: Bud + let cache: Cache + + beforeEach(async () => { + bud = await factory({cache: true}) + cache = new Cache(() => bud) + }) + + it(`should be a Service`, async () => { + expect(cache).toBeInstanceOf(Service) + }) + + it(`should have a boot method`, async () => { + expect(cache.boot).toBeDefined() + expect(cache.boot).toBeInstanceOf(Function) + }) + + it(`should have a register method`, async () => { + expect(cache.register).toBeDefined() + expect(cache.register).toBeInstanceOf(Function) + }) + + + it(`should have a buildDependencies accessor interface`, async () => { + expect(cache.buildDependencies).toBeDefined() + expect(cache.buildDependencies).toBeInstanceOf(Object) + }) + + it(`should have a cacheDirectory accessor interface`, async () => { + await cache.register?.(bud) + await cache.boot?.(bud) + expect(cache.cacheDirectory).toBeDefined() + expect(cache.cacheDirectory).toMatch(/@tests\/project\/cache$/) + }) + + it(`should have a configuration getter`, async () => { + await cache.register?.(bud) + await cache.boot?.(bud) + expect(cache.configuration).toBeDefined() + expect(cache.configuration).toMatchInlineSnapshot(` + { + "allowCollectingMemory": true, + "buildDependencies": { + "bud": [ + "${bud.path()}/package.json", + "${bud.path()}/config/bud.config.js", + ], + "tailwind": [ + "${bud.path()}/config/tailwind.config.js", + ], + }, + "cacheDirectory": "${bud.context.paths?.[`os-cache`]}/@tests/project/cache", + "compression": "brotli", + "hashAlgorithm": "xxhash64", + "idleTimeout": 100, + "idleTimeoutForInitialStore": 0, + "managedPaths": [ + "${bud.context.paths?.[`os-cache`]}/@tests/project/cache", + "${bud.path()}/node_modules", + ], + "name": "production", + "profile": false, + "store": "pack", + "type": "filesystem", + } + `) }) }) diff --git a/sources/@roots/bud-cache/tsconfig.json b/sources/@roots/bud-cache/tsconfig.json index 9185c543fa..684d87a0c9 100644 --- a/sources/@roots/bud-cache/tsconfig.json +++ b/sources/@roots/bud-cache/tsconfig.json @@ -3,10 +3,9 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib", - + "strict": true, }, "include": ["src"], - "exclude": ["node_modules", "lib", "**/*.test.ts"], "references": [ {"path": "./../bud-framework/tsconfig.json"}, {"path": "./../bud-support/tsconfig.json"} diff --git a/sources/@roots/bud-client/package.json b/sources/@roots/bud-client/package.json index 1d5f195e57..2cb501a14a 100644 --- a/sources/@roots/bud-client/package.json +++ b/sources/@roots/bud-client/package.json @@ -46,6 +46,8 @@ "type": "module", "exports": { ".": "./lib/index.js", + "./dom-ready": "./lib/dom-ready.js", + "./lazy": "./lib/lazy.js", "./lib/*": "./lib/*" }, "typesVersions": { @@ -53,7 +55,13 @@ ".": [ "./lib/index.d.ts" ], - "./lib/*": [ + "dom-ready": [ + "./lib/dom-ready.d.ts" + ], + "lazy": [ + "./lib/lazy.d.ts" + ], + "lib/*": [ "./lib/*" ] } diff --git a/sources/@roots/bud-client/src/dom-ready.ts b/sources/@roots/bud-client/src/dom-ready.ts new file mode 100644 index 0000000000..e3eaa7f308 --- /dev/null +++ b/sources/@roots/bud-client/src/dom-ready.ts @@ -0,0 +1,20 @@ +/** + * Calls once document has loaded. + * + * @remarks + * Callback function may be async or sync + * + * @param onReady - callback function + * @returns void + */ +interface domReady { + (onReady: () => (() => Promise) | unknown): void +} + +const domReady: domReady = onReady => { + window.requestAnimationFrame(async function check() { + document.body ? await onReady() : window.requestAnimationFrame(check) + }) +} + +export default domReady diff --git a/sources/@roots/bud-client/src/index.ts b/sources/@roots/bud-client/src/index.ts index 7195295ce0..8fcdc8afe7 100644 --- a/sources/@roots/bud-client/src/index.ts +++ b/sources/@roots/bud-client/src/index.ts @@ -13,4 +13,7 @@ * @packageDocumentation */ -export {} +import domReady from '@roots/bud-client/dom-ready' +import lazy from '@roots/bud-client/lazy' + +export {domReady, lazy} diff --git a/sources/@roots/bud-client/src/lazy.ts b/sources/@roots/bud-client/src/lazy.ts new file mode 100644 index 0000000000..77d8ec4636 --- /dev/null +++ b/sources/@roots/bud-client/src/lazy.ts @@ -0,0 +1,41 @@ +/** + * Lazy import helper + * + * @remarks + * Callback function may be async or sync + * + * @param onReady - callback function + * @returns void + */ +interface lazy { + ( + module: Promise<{default: T}>, + handler: (module: T) => Promise | unknown, + errorHandler?: (error: unknown) => unknown, + ): Promise +} + +/** + * Default error handler + * + * @throws Error + */ +const defaultErrorHandler = (error: unknown) => { + throw error +} + +const lazy: lazy = async function lazy( + module: Promise<{default: T}>, + handler: (module: T) => Promise | unknown, + errorHandler?: (error: unknown) => unknown, +) { + try { + const {default: request} = await module + return await handler(request) + } catch (error: unknown) { + const handle = errorHandler ? errorHandler : defaultErrorHandler + handle(error) + } +} + +export default lazy diff --git a/sources/@roots/bud-compiler/src/service.tsx b/sources/@roots/bud-compiler/src/service.tsx index 5943187823..9a7bf714f5 100644 --- a/sources/@roots/bud-compiler/src/service.tsx +++ b/sources/@roots/bud-compiler/src/service.tsx @@ -17,7 +17,7 @@ import {cpus} from 'node:os' import process from 'node:process' import {pathToFileURL} from 'node:url' -import {Error} from '@roots/bud-dashboard/components/error' +import {Error as DisplayError} from '@roots/bud-dashboard/components/error' import {Service} from '@roots/bud-framework/service' import {bind} from '@roots/bud-support/decorators/bind' import {BudError} from '@roots/bud-support/errors' @@ -26,7 +26,6 @@ import isNull from '@roots/bud-support/lodash/isNull' import isNumber from '@roots/bud-support/lodash/isNumber' import isString from '@roots/bud-support/lodash/isString' import stripAnsi from '@roots/bud-support/strip-ansi' -import webpack from '@roots/bud-support/webpack' /** * {@link BudCompiler} implementation @@ -35,7 +34,7 @@ export class Compiler extends Service implements BudCompiler { /** * {@link BudCompiler.compilationStats} */ - public compilationStats: BudCompiler[`compilationStats`] + public declare compilationStats: BudCompiler[`compilationStats`] /** * {@link BudCompiler.config} @@ -50,12 +49,12 @@ export class Compiler extends Service implements BudCompiler { /** * {@link BudCompiler.instance} */ - public instance: BudCompiler[`instance`] + public declare instance: BudCompiler[`instance`] /** * {@link BudCompiler.stats} */ - public stats: BudCompiler[`stats`] + public declare stats: BudCompiler[`stats`] /** * {@link BudCompiler.compile} @@ -86,8 +85,9 @@ export class Compiler extends Service implements BudCompiler { try { this.instance = this.implementation(this.config) - } catch (error) { - this.onError(error) + } catch (error: unknown) { + const normalError = error instanceof Error ? error : BudError.normalize(error) + this.onError(normalError) } this.instance.hooks.done.tap(bud.label, (stats: any) => { @@ -104,7 +104,7 @@ export class Compiler extends Service implements BudCompiler { * {@link BudCompiler.onError} */ @bind - public onError(error: BudError | webpack.WebpackError) { + public onError(error: Error) { process.exitCode = 1 if (!error) return @@ -117,9 +117,9 @@ export class Compiler extends Service implements BudCompiler { }) if (`isBudError` in error) { - render() + render() } else { - render() + render() } } diff --git a/sources/@roots/bud-compiler/test/compiler.test.ts b/sources/@roots/bud-compiler/test/compiler.test.ts index dfd9f6d08d..6e415d896f 100644 --- a/sources/@roots/bud-compiler/test/compiler.test.ts +++ b/sources/@roots/bud-compiler/test/compiler.test.ts @@ -1,4 +1,6 @@ +import {path} from '@repo/constants' import {Bud, factory} from '@repo/test-kit' +import webpack from '@roots/bud-support/webpack' import {beforeAll, describe, expect, it, vi} from 'vitest' import Compiler from '../src/index.js' @@ -12,10 +14,14 @@ describe(`@roots/bud-compiler`, function () { bud = await factory({mode: `development`}) compiler = new Compiler(() => bud) logSpy = vi.spyOn(compiler.logger, `log`) - await compiler.register(bud) + await compiler.register?.(bud) await compiler.compile(bud) }) + it(`should have implementation`, () => { + expect(compiler.implementation).toBe(webpack) + }) + it(`should have compile fn`, () => { expect(compiler.compile).toBeInstanceOf(Function) }) @@ -32,6 +38,10 @@ describe(`@roots/bud-compiler`, function () { expect(compiler.onError).toBeInstanceOf(Function) }) + it(`should have sourceError transformer`, () => { + expect(compiler.sourceErrors).toBeInstanceOf(Function) + }) + it(`should call logger.log`, async () => { expect(logSpy).toHaveBeenCalled() }) diff --git a/sources/@roots/bud-compiler/tsconfig.json b/sources/@roots/bud-compiler/tsconfig.json index a9bb689fc5..d0668b1c92 100644 --- a/sources/@roots/bud-compiler/tsconfig.json +++ b/sources/@roots/bud-compiler/tsconfig.json @@ -3,15 +3,11 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib", - "types": [ - "node", - "@roots/bud-framework", - ] + "strict": true, }, "include": [ "src" ], - "exclude": ["**/*.test.ts"], "references": [ {"path": "../bud-framework/tsconfig.json"}, {"path": "../bud-api/tsconfig.json"}, diff --git a/sources/@roots/bud-dashboard/test/service.test.tsx b/sources/@roots/bud-dashboard/test/service.test.tsx index 86c8ca7b10..fd013f4f7c 100644 --- a/sources/@roots/bud-dashboard/test/service.test.tsx +++ b/sources/@roots/bud-dashboard/test/service.test.tsx @@ -1,9 +1,8 @@ import {Bud, factory} from '@repo/test-kit' +import {Dashboard} from '@roots/bud-dashboard/service' import {Service} from '@roots/bud-framework/service' import {beforeEach, describe, expect, it} from 'vitest' -import {Dashboard} from '@roots/bud-dashboard/service' - describe(`@roots/bud-dashboard`, () => { let bud: Bud let dashboard: Dashboard diff --git a/sources/@roots/bud-emotion/test/dependencies.test.ts b/sources/@roots/bud-emotion/test/dependencies.test.ts index 381de9e697..8791abb161 100644 --- a/sources/@roots/bud-emotion/test/dependencies.test.ts +++ b/sources/@roots/bud-emotion/test/dependencies.test.ts @@ -1,5 +1,5 @@ -import {describe, expect, it} from 'vitest' import {resolve} from '@roots/bud-support/import-meta-resolve' +import {describe, expect, it} from 'vitest' describe(`@roots/bud-emotion dependencies`, () => { it(`should resolve dependencies from hoisted modules`, async () => { diff --git a/sources/@roots/bud-sass/src/extension.ts b/sources/@roots/bud-sass/src/extension.ts index a0326fd525..5f01024209 100644 --- a/sources/@roots/bud-sass/src/extension.ts +++ b/sources/@roots/bud-sass/src/extension.ts @@ -23,7 +23,7 @@ export class BudSass extends BudSassOptions { * {@link Extension.boot} */ @bind - public override async boot({ build, postcss }: Bud) { + public override async boot({build, postcss}: Bud) { if (postcss.setSyntax) postcss.setSyntax(`postcss-scss`) build.rules.sass.setUse(() => [ diff --git a/sources/@roots/entrypoints-webpack-plugin/src/plugin.ts b/sources/@roots/entrypoints-webpack-plugin/src/plugin.ts index 2bcbffa217..c8ce219831 100644 --- a/sources/@roots/entrypoints-webpack-plugin/src/plugin.ts +++ b/sources/@roots/entrypoints-webpack-plugin/src/plugin.ts @@ -124,7 +124,9 @@ export class EntrypointsWebpackPlugin { for (const entry of compilation.entrypoints.values()) { this.getChunkedFiles(entry.chunks).map(({file}) => { const ident = entry.name as string - const path = (this.options.publicPath as string).concat(file) + const path = (this.options.publicPath as string).concat( + file, + ) const type = path.split(`.`).pop() ?? `default` this.addToManifest({ident, path, type}) @@ -168,8 +170,10 @@ export class EntrypointsWebpackPlugin { /** * Get assets from an entrypoint */ - public getChunkedFiles(chunks: Webpack.Chunk[]): Array<{file: string, ident: string}> { - const files: Array<{file: string, ident: string}> = [] + public getChunkedFiles( + chunks: Webpack.Chunk[], + ): Array<{file: string; ident: string}> { + const files: Array<{file: string; ident: string}> = [] for (const chunk of chunks) { Array.from(chunk.files).map(file => { diff --git a/sources/@roots/sage/package.json b/sources/@roots/sage/package.json index 79901728e4..e9e9607545 100644 --- a/sources/@roots/sage/package.json +++ b/sources/@roots/sage/package.json @@ -107,6 +107,7 @@ "@roots/blade-loader": "workspace:*", "@roots/bud": "workspace:*", "@roots/bud-build": "workspace:*", + "@roots/bud-client": "workspace:*", "@roots/bud-entrypoints": "workspace:*", "@roots/bud-framework": "workspace:*", "@roots/bud-preset-wordpress": "workspace:*", diff --git a/sources/@roots/sage/src/acorn/index.ts b/sources/@roots/sage/src/acorn/index.ts index bba903e8a9..e6f6d7a248 100644 --- a/sources/@roots/sage/src/acorn/index.ts +++ b/sources/@roots/sage/src/acorn/index.ts @@ -28,19 +28,16 @@ export default class Acorn compiler: Compiler, compilation: Compilation, ) { - return () => { - const data: Record = { - publicPath: this.app.publicPath().replace(/auto$/, ``), - } - - if (this.app.root?.server?.publicUrl) - data.dev = urlToHttpOptions(this.app.root.server.publicUrl) - if (this.app.root?.server?.publicProxyUrl) - data.proxy = urlToHttpOptions( - this.app.root.server.publicProxyUrl, - ) + const data: Record = { + publicPath: this.app.publicPath().replace(/auto$/, ``), + } + if (this.app.root?.server?.publicUrl) + data.dev = urlToHttpOptions(this.app.root.server.publicUrl) + if (this.app.root?.server?.publicProxyUrl) + data.proxy = urlToHttpOptions(this.app.root.server.publicProxyUrl) + return () => { const source = new compiler.webpack.sources.RawSource( JSON.stringify(data, null, 2), ) @@ -53,8 +50,8 @@ export default class Acorn * {@link WebpackPluginInstance.apply} */ @bind - public override apply(compiler: Compiler) { - if (!this.app.isDevelopment) return + public override apply(compiler: Compiler): boolean { + if (!this.app.isDevelopment) return false compiler.hooks.thisCompilation.tap( `@roots/sage/acorn`, @@ -69,6 +66,8 @@ export default class Acorn ) }, ) + + return true } /** diff --git a/sources/@roots/sage/src/client/dom-ready.ts b/sources/@roots/sage/src/client/dom-ready.ts index e3eaa7f308..0b63e6600a 100644 --- a/sources/@roots/sage/src/client/dom-ready.ts +++ b/sources/@roots/sage/src/client/dom-ready.ts @@ -1,20 +1 @@ -/** - * Calls once document has loaded. - * - * @remarks - * Callback function may be async or sync - * - * @param onReady - callback function - * @returns void - */ -interface domReady { - (onReady: () => (() => Promise) | unknown): void -} - -const domReady: domReady = onReady => { - window.requestAnimationFrame(async function check() { - document.body ? await onReady() : window.requestAnimationFrame(check) - }) -} - -export default domReady +export {default} from '@roots/bud-client/dom-ready' diff --git a/sources/@roots/sage/src/client/index.ts b/sources/@roots/sage/src/client/index.ts index 398dae56a7..f21bb83e6f 100644 --- a/sources/@roots/sage/src/client/index.ts +++ b/sources/@roots/sage/src/client/index.ts @@ -1,4 +1,2 @@ -import domReady from './dom-ready.js' -import lazy from './lazy.js' - -export {domReady, lazy} +export {default as domReady} from '@roots/bud-client/dom-ready' +export {default as lazy} from '@roots/bud-client/lazy' diff --git a/sources/@roots/sage/src/client/lazy.ts b/sources/@roots/sage/src/client/lazy.ts index 77d8ec4636..9c5c2ac86a 100644 --- a/sources/@roots/sage/src/client/lazy.ts +++ b/sources/@roots/sage/src/client/lazy.ts @@ -1,41 +1 @@ -/** - * Lazy import helper - * - * @remarks - * Callback function may be async or sync - * - * @param onReady - callback function - * @returns void - */ -interface lazy { - ( - module: Promise<{default: T}>, - handler: (module: T) => Promise | unknown, - errorHandler?: (error: unknown) => unknown, - ): Promise -} - -/** - * Default error handler - * - * @throws Error - */ -const defaultErrorHandler = (error: unknown) => { - throw error -} - -const lazy: lazy = async function lazy( - module: Promise<{default: T}>, - handler: (module: T) => Promise | unknown, - errorHandler?: (error: unknown) => unknown, -) { - try { - const {default: request} = await module - return await handler(request) - } catch (error: unknown) { - const handle = errorHandler ? errorHandler : defaultErrorHandler - handle(error) - } -} - -export default lazy +export {default} from '@roots/bud-client/lazy' diff --git a/sources/@roots/sage/src/sage/index.ts b/sources/@roots/sage/src/sage/index.ts index 6143862168..f6511da5f4 100644 --- a/sources/@roots/sage/src/sage/index.ts +++ b/sources/@roots/sage/src/sage/index.ts @@ -8,7 +8,7 @@ import { } from '@roots/bud-framework/extension/decorators' import {deprecated} from '@roots/bud-support/decorators' -import type AcornExtension from '../acorn/index.js' +import type Acorn from '../acorn/index.js' /** * roots/sage @@ -27,7 +27,7 @@ class Sage extends Extension { /** * {@link Acorn} */ - public get acorn(): AcornExtension { + public get acorn(): Acorn { return this.app.extensions.get(`@roots/sage/acorn`) } diff --git a/sources/@roots/sage/test/acorn/extension.test.ts b/sources/@roots/sage/test/acorn/extension.test.ts index 75dfc37af2..bf48486131 100644 --- a/sources/@roots/sage/test/acorn/extension.test.ts +++ b/sources/@roots/sage/test/acorn/extension.test.ts @@ -1,9 +1,9 @@ -import {beforeEach, describe, expect, it} from 'vitest' import {Bud, factory} from '@repo/test-kit' +import {beforeEach, describe, expect, it, vi} from 'vitest' import Acorn from '../../src/acorn/index.js' -describe(`@roots/sage`, async () => { +describe(`@roots/sage/acorn`, async () => { let bud: Bud let acorn: Acorn @@ -12,7 +12,7 @@ describe(`@roots/sage`, async () => { acorn = new Acorn(bud) }) - it('test bud.sage.acorn.buildBefore', async () => { + it(`should set publicPath to empty strings`, async () => { bud.entrypoints.set(`publicPath`, `FOO`) bud.manifest.set(`publicPath`, `FOO`) @@ -22,4 +22,42 @@ describe(`@roots/sage`, async () => { expect(bud.entrypoints.get(`publicPath`)).toBe(``) expect(bud.manifest.get(`publicPath`)).toBe(``) }) + + it(`should execute webpack apply in development`, async () => { + const bud = await factory({mode: `development`}) + const mockCompiler = {hooks: {thisCompilation: {tap: vi.fn((...args) => {})}}} + // @ts-ignore + const result = new Acorn(bud).apply(mockCompiler) + + expect(mockCompiler.hooks.thisCompilation.tap).toHaveBeenCalled() + expect(result).toBe(true) + }) + + it(`should not execute webpack apply in production`, async () => { + const bud = await factory({mode: `production`}) + + const mockCompiler = {hooks: {thisCompilation: {tap: vi.fn((...args) => {})}}} + // @ts-ignore + const result = new Acorn(bud).apply(mockCompiler) + + expect(mockCompiler.hooks.thisCompilation.tap).not.toHaveBeenCalled() + expect(result).toBe(false) + }) + + it(`should return a fn from addAcornHotManifest`, async () => { + const bud = await factory({mode: `development`}) + const mockCompiler = {webpack: {sources: {RawSource: vi.fn((...args) => {})}}} as any + const mockCompilation = {emitAsset: vi.fn((...args) => {})} as any + + const publicPathSpy = vi.spyOn(bud, `publicPath`) + + const result = new Acorn(bud).addAcornHotManifest(mockCompiler, mockCompilation) + + expect(publicPathSpy).toHaveBeenCalledOnce() + expect(result).toEqual(expect.any(Function)) + + result() + expect(mockCompiler.webpack.sources.RawSource).toHaveBeenCalledWith(expect.stringContaining(`"publicPath": ""`)) + expect(mockCompilation.emitAsset).toHaveBeenCalledOnce() + }) }) diff --git a/sources/@roots/sage/tsconfig.json b/sources/@roots/sage/tsconfig.json index 0bb55d14c5..6bb9a5b9cf 100644 --- a/sources/@roots/sage/tsconfig.json +++ b/sources/@roots/sage/tsconfig.json @@ -9,6 +9,7 @@ "@roots/bud-framework", "@roots/bud-build", "@roots/bud-api", + "@roots/bud-client", "@roots/bud-extensions", "@roots/bud-minify", "@roots/bud-entrypoints", @@ -18,6 +19,7 @@ "include": ["./src"], "references": [ {"path": "../bud-build/tsconfig.json"}, + {"path": "../bud-client/tsconfig.json"}, {"path": "../bud-extensions/tsconfig.json"}, {"path": "../bud-framework/tsconfig.json"}, {"path": "../bud-minify/tsconfig.json"}, diff --git a/sources/@roots/wordpress-hmr/src/editor.ts b/sources/@roots/wordpress-hmr/src/editor.ts index f4fb7f410e..b8a6d3adc1 100644 --- a/sources/@roots/wordpress-hmr/src/editor.ts +++ b/sources/@roots/wordpress-hmr/src/editor.ts @@ -1,5 +1,3 @@ -import {dispatch} from '@wordpress/data' - import type { AcceptCallback, AfterCallback, @@ -7,6 +5,7 @@ import type { } from '@roots/wordpress-hmr' import {Cache} from '@roots/wordpress-hmr/cache' +import {dispatch} from '@wordpress/data' export interface Props { accept: AcceptCallback @@ -19,8 +18,10 @@ export interface Props { getContext: ContextFactory } -let initial = false +let initial = true + let notify = true + export const setNotify = (value: boolean) => { notify = value } @@ -49,14 +50,13 @@ export const load = ({accept, after, api, before, getContext}: Props) => { after && after(changed) - if (notify && import.meta.webpackHot) { - if (!initial) { - initial = true - dispatch(`core/notices`).createInfoNotice(`🔥 Reload enabled`, { - id: `hmr-enabled`, - type: `snackbar`, - }) - } + if (initial && notify && import.meta.webpackHot) { + initial = false + + dispatch(`core/notices`).createInfoNotice(`🔥 Reload enabled`, { + id: `hmr-enabled`, + type: `snackbar`, + }) } return context diff --git a/sources/@roots/wordpress-hmr/src/plugin.ts b/sources/@roots/wordpress-hmr/src/plugin.ts index 960f1c7be3..47fba42122 100644 --- a/sources/@roots/wordpress-hmr/src/plugin.ts +++ b/sources/@roots/wordpress-hmr/src/plugin.ts @@ -1,15 +1,13 @@ +import type * as Filter from '@roots/wordpress-hmr/filter' import type {WPPlugin} from '@wordpress/plugins' +import {filterCallback} from '@roots/wordpress-hmr/utility' import { getPlugins, registerPlugin, unregisterPlugin, } from '@wordpress/plugins' -import type * as Filter from '@roots/wordpress-hmr/filter' - -import {filterCallback} from '@roots/wordpress-hmr/utility' - export interface Props extends WPPlugin { filters?: Filter.KeyedFilters name: string diff --git a/yarn.lock b/yarn.lock index 37d013b33b..40c007f4a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8663,6 +8663,7 @@ __metadata: "@roots/blade-loader": "workspace:*" "@roots/bud": "workspace:*" "@roots/bud-build": "workspace:*" + "@roots/bud-client": "workspace:*" "@roots/bud-entrypoints": "workspace:*" "@roots/bud-framework": "workspace:*" "@roots/bud-preset-wordpress": "workspace:*"