diff --git a/config/tsconfig.json b/config/tsconfig.json index c1d5f48b8b..48c510fb61 100644 --- a/config/tsconfig.json +++ b/config/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "allowJs": true, + "allowJs": false, "allowUnreachableCode": false, "composite": true, "declaration": true, @@ -12,7 +12,7 @@ "importHelpers": true, "incremental": true, "jsx": "react-jsx", - "lib": ["ES2021", "DOM", "DOM.Iterable"], + "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "NodeNext", "moduleDetection": "force", "moduleResolution": "NodeNext", @@ -73,26 +73,16 @@ {"path": "../sources/@roots/bud-wordpress-manifests/tsconfig.json"}, {"path": "../sources/@roots/bud-wordpress-theme-json/tsconfig.json"}, {"path": "../sources/@roots/container/tsconfig.json"}, - { - "path": "../sources/@roots/critical-css-webpack-plugin/tsconfig.json" - }, + {"path": "../sources/@roots/critical-css-webpack-plugin/tsconfig.json"}, {"path": "../sources/@roots/dependencies/tsconfig.json"}, {"path": "../sources/@roots/entrypoints-webpack-plugin/tsconfig.json"}, {"path": "../sources/@roots/filesystem/tsconfig.json"}, - { - "path": "../sources/@roots/merged-manifest-webpack-plugin/tsconfig.json" - }, + {"path": "../sources/@roots/merged-manifest-webpack-plugin/tsconfig.json"}, {"path": "../sources/@roots/sage/tsconfig.json"}, - { - "path": "../sources/@roots/wordpress-dependencies-webpack-plugin/tsconfig.json" - }, - { - "path": "../sources/@roots/wordpress-externals-webpack-plugin/tsconfig.json" - }, + {"path": "../sources/@roots/wordpress-dependencies-webpack-plugin/tsconfig.json"}, + {"path": "../sources/@roots/wordpress-externals-webpack-plugin/tsconfig.json"}, {"path": "../sources/@roots/wordpress-hmr/tsconfig.json"}, - { - "path": "../sources/@roots/wordpress-theme-json-webpack-plugin/tsconfig.json" - }, + {"path": "../sources/@roots/wordpress-theme-json-webpack-plugin/tsconfig.json"}, {"path": "../sources/create-bud-app/tsconfig.json"} ] } diff --git a/examples/babel/.env b/examples/babel/.env deleted file mode 100644 index 3d4743f3ea..0000000000 --- a/examples/babel/.env +++ /dev/null @@ -1,3 +0,0 @@ -PUBLIC_APP_TITLE='Create Bud App' -PUBLIC_APP_DESCRIPTION='Project description' -PUBLIC_APP_COLOR='#008080' diff --git a/examples/babel/bud.config.cjs b/examples/babel/bud.config.js similarity index 70% rename from examples/babel/bud.config.cjs rename to examples/babel/bud.config.js index 1db70b4059..e5d0b9ef15 100644 --- a/examples/babel/bud.config.cjs +++ b/examples/babel/bud.config.js @@ -1,7 +1,7 @@ -module.exports = async app => { - app.entry('app', ['app.js', 'app.css']).splitChunks(app.isProduction) +export default async bud => { + bud.entry('app', ['app.js', 'app.css']).splitChunks(bud.isProduction) - app.babel + bud.babel .setPresets({ '@babel/preset-env': require.resolve('@babel/preset-env'), }) diff --git a/examples/babel/package.json b/examples/babel/package.json index 58930e870b..c03d374a38 100644 --- a/examples/babel/package.json +++ b/examples/babel/package.json @@ -1,13 +1,10 @@ { "name": "@examples/babel", "private": true, - "packageManager": "npm@8.19.2", - "engines": { - "node": "18.12.1" - }, "browserslist": [ "extends @roots/browserslist-config" ], + "type": "module", "devDependencies": { "@roots/bud": "latest", "@roots/bud-babel": "latest", diff --git a/examples/basic/package.json b/examples/basic/package.json index fe6b838c36..e0de4c6339 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -1,14 +1,11 @@ { "name": "@examples/basic", "private": true, - "engines": { - "node": "18.12.1" - }, "browserslist": [ "extends @roots/browserslist-config" ], + "type": "module", "devDependencies": { - "@roots/browserslist-config": "latest", "@roots/bud": "latest" } } diff --git a/examples/config-json/bud.config.json b/examples/config-json/bud.config.json index 86fa4d63d8..4505432ed9 100644 --- a/examples/config-json/bud.config.json +++ b/examples/config-json/bud.config.json @@ -1,10 +1,9 @@ { entry: ['app', ['app.js']], - runtime: [true], assets: [['src/**/*.html']], babel: { setPluginOptions: [ - "@babel/plugin-transform-runtime", + '@babel/plugin-transform-runtime', {helpers: false, regenerator: false}, ], }, diff --git a/examples/config-json/package.json b/examples/config-json/package.json index a2c0b868fb..2c78b551bc 100644 --- a/examples/config-json/package.json +++ b/examples/config-json/package.json @@ -1,13 +1,10 @@ { "name": "@examples/config-json", "private": true, - "packageManager": "npm@8.19.2", - "engines": { - "node": "18.12.1" - }, "browserslist": [ "extends @roots/browserslist-config" ], + "type": "module", "devDependencies": { "@roots/bud": "latest", "@roots/bud-babel": "latest" diff --git a/examples/config-yml/bud.config.yml b/examples/config-yml/bud.config.yml index 57b4e31a5c..af5ea26f78 100644 --- a/examples/config-yml/bud.config.yml +++ b/examples/config-yml/bud.config.yml @@ -38,7 +38,6 @@ webpackConfig: > # or dynamic values log: - - 'example:' - => 2 + 2 - => `equals ${2 + 2}` diff --git a/examples/config-yml/package.json b/examples/config-yml/package.json index 744137cf7a..9d91c12c0c 100644 --- a/examples/config-yml/package.json +++ b/examples/config-yml/package.json @@ -1,10 +1,6 @@ { "name": "@examples/config-yml", "private": true, - "packageManager": "npm@8.19.2", - "engines": { - "node": "18.12.1" - }, "browserslist": [ "extends @roots/browserslist-config" ], diff --git a/examples/critical-css/.env b/examples/critical-css/.env deleted file mode 100644 index a3320e8b21..0000000000 --- a/examples/critical-css/.env +++ /dev/null @@ -1,3 +0,0 @@ -APP_TITLE='Create Bud App' -APP_DESCRIPTION='Project description' -APP_COLOR='#fff' diff --git a/examples/critical-css/package.json b/examples/critical-css/package.json index 5556ef9f8a..f80704c198 100644 --- a/examples/critical-css/package.json +++ b/examples/critical-css/package.json @@ -1,15 +1,10 @@ { "name": "@examples/critical-css", "private": true, - "packageManager": "npm@8.19.2", - "engines": { - "node": "18.12.1" - }, "browserslist": [ "extends @roots/browserslist-config" ], "devDependencies": { - "@roots/browserslist-config": "latest", "@roots/bud": "latest", "@roots/bud-criticalcss": "latest", "@roots/bud-postcss": "latest", diff --git a/examples/emotion/.env b/examples/emotion/.env deleted file mode 100644 index a3320e8b21..0000000000 --- a/examples/emotion/.env +++ /dev/null @@ -1,3 +0,0 @@ -APP_TITLE='Create Bud App' -APP_DESCRIPTION='Project description' -APP_COLOR='#fff' diff --git a/examples/emotion/package.json b/examples/emotion/package.json index 618882f87d..1eeaa2403b 100644 --- a/examples/emotion/package.json +++ b/examples/emotion/package.json @@ -1,7 +1,6 @@ { "name": "@examples/emotion", "private": true, - "packageManager": "npm@8.19.2", "browserslist": [ "extends @roots/browserslist-config" ], diff --git a/examples/esbuild/.env b/examples/esbuild/.env deleted file mode 100644 index a3320e8b21..0000000000 --- a/examples/esbuild/.env +++ /dev/null @@ -1,3 +0,0 @@ -APP_TITLE='Create Bud App' -APP_DESCRIPTION='Project description' -APP_COLOR='#fff' diff --git a/examples/esbuild/package.json b/examples/esbuild/package.json index 173eabb44e..11ae3f97c9 100644 --- a/examples/esbuild/package.json +++ b/examples/esbuild/package.json @@ -1,15 +1,10 @@ { "name": "@examples/esbuild", "private": true, - "packageManager": "npm@8.19.2", - "engines": { - "node": "18.12.1" - }, "browserslist": [ "extends @roots/browserslist-config" ], "devDependencies": { - "@roots/browserslist-config": "latest", "@roots/bud": "latest", "@roots/bud-esbuild": "latest" } diff --git a/examples/multi-compiler/bud.config.cjs b/examples/multi-compiler/bud.config.cjs deleted file mode 100644 index 114a0f6fc9..0000000000 --- a/examples/multi-compiler/bud.config.cjs +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Configuration which runs two instances of bud. - * - * Each can be uniquely configured. - * ``` - */ -module.exports = async ({make, path}) => { - /** - * Make `theme` workspace in `./theme` and setup entrypoints - * Files will be output to `./theme/dist` - */ - await make({label: 'theme', basedir: path('theme')}, async theme => - theme.entry('theme', ['theme.js', 'theme.css']), - ) - - /** - * Make plugin workspace in `./plugin` and setup entrypoints - * Files will be output to `./plugin/dist` - */ - await make({label: 'plugin', basedir: path('plugin')}, async plugin => - plugin.entry('plugin', ['plugin.js', 'plugin.css']), - ) -} diff --git a/examples/multi-compiler/bud.config.mts b/examples/multi-compiler/bud.config.mts new file mode 100644 index 0000000000..2e11394a86 --- /dev/null +++ b/examples/multi-compiler/bud.config.mts @@ -0,0 +1,27 @@ +import type {Bud} from '@roots/bud' + +/** + * Configuration which runs two instances of bud. + * + * Each can be uniquely configured. + * ``` + */ +export default async ({make, path, ...app}: Bud) => { + await Promise.all([ + /** + * Make `theme` workspace in `./theme` and setup entrypoints + * Files will be output to `./theme/dist` + */ + make({label: 'theme', basedir: path('theme')}, async theme => + theme.entry('theme', ['theme.js', 'theme.css']), + ), + + /** + * Make plugin workspace in `./plugin` and setup entrypoints + * Files will be output to `./plugin/dist` + */ + make({label: 'plugin', basedir: path('plugin')}, async plugin => + plugin.entry('plugin', ['plugin.js', 'plugin.css']), + ), + ]) +} diff --git a/examples/multi-compiler/package.json b/examples/multi-compiler/package.json index b9f5606321..f44d5a9502 100644 --- a/examples/multi-compiler/package.json +++ b/examples/multi-compiler/package.json @@ -1,16 +1,11 @@ { "name": "@examples/multi", "private": true, - "packageManager": "npm@8.19.2", - "engines": { - "node": "18.12.1" - }, + "type": "module", "browserslist": [ "extends @roots/browserslist-config" ], "devDependencies": { - "@roots/browserslist-config": "latest", - "@roots/bud": "latest", - "@roots/bud-babel": "latest" + "@roots/bud": "latest" } } diff --git a/examples/multi-compiler/plugin/src/plugin.js b/examples/multi-compiler/plugin/src/plugin.js index 68f664bca8..2115032bd9 100644 --- a/examples/multi-compiler/plugin/src/plugin.js +++ b/examples/multi-compiler/plugin/src/plugin.js @@ -1,12 +1,12 @@ -const features = ['fancy', 'ecma'] +const features = ["fancy", "ecma"]; -features.map(feat => { - console.log(feat) -}) +await Promise.all( + features.map(async (feat) => { + const { testString } = await import(`./test-module.js`); + console.log(`${feat}: ${testString}`); + }) +); -/** - * Accept module updates - * - * @see https://webpack.js.org/api/hot-module-replacement - */ -import.meta.webpackHot?.accept(console.error) +if (import.meta.webpackHot) { + import.meta.webpackHot.accept(console.error); +} diff --git a/examples/multi-compiler/plugin/src/test-module.js b/examples/multi-compiler/plugin/src/test-module.js new file mode 100644 index 0000000000..9a45134461 --- /dev/null +++ b/examples/multi-compiler/plugin/src/test-module.js @@ -0,0 +1 @@ +export const testString = `test`; diff --git a/examples/multi-compiler/theme/src/theme.js b/examples/multi-compiler/theme/src/theme.js index 68f664bca8..7dba18aa62 100644 --- a/examples/multi-compiler/theme/src/theme.js +++ b/examples/multi-compiler/theme/src/theme.js @@ -1,12 +1,9 @@ -const features = ['fancy', 'ecma'] +const features = ["fancy", "ecma"]; -features.map(feat => { - console.log(feat) -}) +features.map((feat) => { + console.log(feat); +}); -/** - * Accept module updates - * - * @see https://webpack.js.org/api/hot-module-replacement - */ -import.meta.webpackHot?.accept(console.error) +if (import.meta.webpackHot) { + import.meta.webpackHot.accept(console.error); +} diff --git a/examples/react/package.json b/examples/react/package.json index 5ab7e3af8e..c2b007aa23 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -1,12 +1,7 @@ { "name": "@examples/react", "private": true, - "packageManager": "npm@8.19.2", "type": "module", - "engines": { - "node": "18.12.1", - "npm": "8.19.2" - }, "browserslist": [ "extends @roots/browserslist-config" ], diff --git a/examples/typescript/bud.config.ts b/examples/typescript/bud.config.ts index 2002ef2e64..3f4b706dc8 100644 --- a/examples/typescript/bud.config.ts +++ b/examples/typescript/bud.config.ts @@ -1,5 +1,4 @@ import {Bud} from '@roots/bud' -import '@roots/bud-typescript' export default async (bud: Bud) => { bud.entry({app: ['app']}).html() diff --git a/examples/typescript/package.json b/examples/typescript/package.json index e3a5bdda19..ee6a3fe6a5 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -1,10 +1,6 @@ { "name": "@examples/typescript", "private": true, - "packageManager": "npm@8.19.2", - "engines": { - "node": "18.12.1" - }, "browserslist": [ "extends @roots/browserslist-config" ], @@ -13,11 +9,9 @@ "build": "yarn ts-bud build" }, "devDependencies": { - "@roots/browserslist-config": "latest", "@roots/bud": "latest", "@roots/bud-babel": "latest", "@roots/bud-typescript": "latest", - "@types/webpack-env": "latest", "ts-node": "10.9.1" } } diff --git a/examples/typescript/src/app.ts b/examples/typescript/src/app.ts index 12725e5ebb..bd71f0910a 100644 --- a/examples/typescript/src/app.ts +++ b/examples/typescript/src/app.ts @@ -1,7 +1,4 @@ -/* eslint-disable no-console */ -const foo: string = 'black' +const el: HTMLBodyElement = document.querySelector('body') +if (el) el.style.backgroundColor = `black` -document.querySelector('body').style.backgroundColor = foo - -// @ts-ignore -import.meta.webpackHot?.accept(console.error) +if (import.meta.webpackHot) import.meta.webpackHot.accept(console.error) diff --git a/examples/typescript/tsconfig.json b/examples/typescript/tsconfig.json index 22ba0eea15..941db74da4 100644 --- a/examples/typescript/tsconfig.json +++ b/examples/typescript/tsconfig.json @@ -4,9 +4,9 @@ "module": "esnext", "target": "esnext", "paths": { - "@src/*": ["./src/*"] + "@src": ["./src"] }, - "types": ["@roots/bud", "@roots/bud-typescript"] + "types": ["@roots/bud", "@roots/bud-typescript", "webpack/module"] }, "include": ["./src", "./types"], "exclude": ["./dist"] diff --git a/package.json b/package.json index 2e53b41bec..4349aaff03 100644 --- a/package.json +++ b/package.json @@ -71,5 +71,8 @@ "node": "18.12.1", "yarn": "1.22.19", "npm": "8.19.2" + }, + "devDependencies": { + "ink-testing-library": "3.0.0" } } diff --git a/sources/@repo/test-kit/index.ts b/sources/@repo/test-kit/index.ts index 2d40f4647b..219f7d40da 100644 --- a/sources/@repo/test-kit/index.ts +++ b/sources/@repo/test-kit/index.ts @@ -1,11 +1,11 @@ import type {Bud} from '@roots/bud-framework' -import type * as Options from '@roots/bud-framework/options' +import type {Context} from '@roots/bud-framework/context' import {path} from '@repo/constants' import {factory} from '@roots/bud/factory' const makeTestBud = async ( - overrides: Partial = {}, + overrides: Partial = {}, ): Promise => { const bud = await factory({ basedir: overrides.basedir ?? path(`tests`, `util`, `project`), diff --git a/sources/@roots/bud-api/src/api/service.ts b/sources/@roots/bud-api/src/api/service.ts index 06ded18d10..a6480b2ca4 100644 --- a/sources/@roots/bud-api/src/api/service.ts +++ b/sources/@roots/bud-api/src/api/service.ts @@ -1,5 +1,5 @@ import type {Bud} from '@roots/bud-framework' -import type {Api as Contract} from '@roots/bud-framework/services' +import type {Api as BudApi} from '@roots/bud-framework' import {ServiceContainer} from '@roots/bud-framework/service' import {bind} from '@roots/bud-support/decorators/bind' @@ -16,16 +16,18 @@ import * as methods from '../methods/index.js' * Binds facade methods to the framework and provides a way to list them, * call them, and otherwise manipulate them. */ -export class Api extends ServiceContainer implements Contract { +export class Api extends ServiceContainer implements BudApi { + public override label: ServiceContainer[`label`] = `api` + /** * Queued method calls */ - public queue: Contract['queue'] = [] + public queue: BudApi['queue'] = [] /** * Called methods */ - public trace: Contract['trace'] = [] + public trace: BudApi['trace'] = [] /** * Bind a synchronous facade for use in configs diff --git a/sources/@roots/bud-api/src/index.ts b/sources/@roots/bud-api/src/index.ts index ab089c89bf..fed354b978 100644 --- a/sources/@roots/bud-api/src/index.ts +++ b/sources/@roots/bud-api/src/index.ts @@ -8,6 +8,7 @@ * @see https://github.com/roots/bud */ +import type {Api as Service} from './api/service.js' import type * as Alias from './methods/alias/index.js' import type * as Assets from './methods/assets/index.js' import type * as Bundle from './methods/bundle/index.js' @@ -45,8 +46,6 @@ declare module '@roots/bud-framework' { */ alias: (...params: Alias.Parameters) => Bud - api: Bud[`api`] - /** * ## bud.assets * @@ -297,6 +296,8 @@ declare module '@roots/bud-framework' { */ webpackConfig(...params: Config.Parameters): Bud } + + type Api = Service } export {Api as default} from './api/service.js' diff --git a/sources/@roots/bud-api/src/methods/bundle/__snapshots__/index.test.ts.snap b/sources/@roots/bud-api/src/methods/bundle/__snapshots__/index.test.ts.snap index 4f21b4fbd9..b4ab98ba66 100644 --- a/sources/@roots/bud-api/src/methods/bundle/__snapshots__/index.test.ts.snap +++ b/sources/@roots/bud-api/src/methods/bundle/__snapshots__/index.test.ts.snap @@ -4,15 +4,17 @@ exports[`bud.bundle > should set the bundle using a string 1`] = ` { "automaticNameDelimiter": "/", "cacheGroups": { + "default": false, "react": { - "filename": "js/bundle/react/[name].js", + "chunks": "all", + "enforce": true, + "filename": "js/bundle/[name].js", "idHint": "react", + "name": "react", "priority": -10, "test": /\\[\\\\/\\]\\(react\\)\\[\\\\/\\]/, }, }, - "chunks": "all", - "minSize": 0, } `; @@ -20,15 +22,17 @@ exports[`bud.bundle > should set the bundle using a string name and a string tes { "automaticNameDelimiter": "/", "cacheGroups": { + "default": false, "react": { - "filename": "js/bundle/react/[name].js", + "chunks": "all", + "enforce": true, + "filename": "js/bundle/[name].js", "idHint": "react", + "name": "react", "priority": -10, "test": /\\[\\\\/\\]\\(react\\)\\[\\\\/\\]/, }, }, - "chunks": "all", - "minSize": 0, } `; @@ -36,15 +40,17 @@ exports[`bud.bundle > should set the bundle using a string name and array of str { "automaticNameDelimiter": "/", "cacheGroups": { + "default": false, "react": { - "filename": "js/bundle/react/[name].js", + "chunks": "all", + "enforce": true, + "filename": "js/bundle/[name].js", "idHint": "react", + "name": "react", "priority": -10, "test": /\\[\\\\/\\]\\(react\\|react-dom\\)\\[\\\\/\\]/, }, }, - "chunks": "all", - "minSize": 0, } `; @@ -52,16 +58,16 @@ exports[`bud.bundle > should set the bundle using a string name and regular expr { "automaticNameDelimiter": "/", "cacheGroups": { + "default": false, "react": { - "filename": "js/bundle/react/[name].js", + "chunks": "all", + "enforce": true, + "filename": "js/bundle/[name].js", "idHint": "react", + "name": "react", "priority": -10, "test": /react/, }, }, - "chunks": "all", - "minSize": 0, } `; - -exports[`bud.bundle > when hashing is enabled > should include the hash in the \`filename\` property > js/bundle/react/[name].[contenthash].js 1`] = `"js/bundle/react/[name].[contenthash].js"`; diff --git a/sources/@roots/bud-api/src/methods/bundle/index.test.ts b/sources/@roots/bud-api/src/methods/bundle/index.test.ts index ddaec0aae6..daee749db0 100644 --- a/sources/@roots/bud-api/src/methods/bundle/index.test.ts +++ b/sources/@roots/bud-api/src/methods/bundle/index.test.ts @@ -45,16 +45,4 @@ describe(`bud.bundle`, () => { bud.hooks.filter(`build.optimization.splitChunks`), ).toMatchSnapshot() }) - - describe(`when hashing is enabled`, () => { - it(`should include the hash in the \`filename\` property`, async () => { - bud.context.hash = true - await instance(`react`, [`react`, `react-dom`]) - - expect( - bud.hooks.filter(`build.optimization.splitChunks`).cacheGroups - .react.filename, - ).toMatchSnapshot(`js/bundle/react/[name].[contenthash].js`) - }) - }) }) diff --git a/sources/@roots/bud-api/src/methods/bundle/index.ts b/sources/@roots/bud-api/src/methods/bundle/index.ts index 9379e7ff74..0eb4b918da 100644 --- a/sources/@roots/bud-api/src/methods/bundle/index.ts +++ b/sources/@roots/bud-api/src/methods/bundle/index.ts @@ -33,33 +33,30 @@ export interface bundle { export const bundle: bundle = function (this: Bud, name, matcher) { const test = normalize(matcher ?? name) - this.hooks.on(`build.optimization.splitChunks`, splitChunks => { - const template = this.context.hash - ? `[name].[contenthash].js` - : `[name].js` - + this.hooks.on(`build.optimization.splitChunks`, options => { const entry = { [name]: { - filename: join(`js`, `bundle`, name, template), + chunks: `all` as any, + enforce: true, + filename: join(`js`, `bundle`, `[name].js`), idHint: name, + name, priority: -10, test, }, } - if (splitChunks === false || splitChunks === undefined) { + if (options === false || options === undefined) { return { automaticNameDelimiter: sep, - cacheGroups: {...entry}, - chunks: `all`, - minSize: 0, + cacheGroups: {default: false, ...entry}, } } return { - ...splitChunks, + ...options, cacheGroups: { - ...(splitChunks.cacheGroups ?? {}), + ...(options.cacheGroups ?? {}), ...entry, }, } diff --git a/sources/@roots/bud-api/src/methods/entry/types.ts b/sources/@roots/bud-api/src/methods/entry/types.ts index 6f786158be..626cddb27b 100644 --- a/sources/@roots/bud-api/src/methods/entry/types.ts +++ b/sources/@roots/bud-api/src/methods/entry/types.ts @@ -1,4 +1,4 @@ -import type {EntryObject} from '@roots/bud-framework/types/config' +import type {EntryObject} from '@roots/bud-framework/config' export type {EntryObject} diff --git a/sources/@roots/bud-api/src/methods/runtime/index.ts b/sources/@roots/bud-api/src/methods/runtime/index.ts index 284884147b..fbdc32e11e 100644 --- a/sources/@roots/bud-api/src/methods/runtime/index.ts +++ b/sources/@roots/bud-api/src/methods/runtime/index.ts @@ -1,5 +1,5 @@ import type {Bud} from '@roots/bud-framework' -import type {Optimization} from '@roots/bud-framework/types/config' +import type {Optimization} from '@roots/bud-framework/config' export type Parameters = [ | (( diff --git a/sources/@roots/bud-api/src/methods/splitChunks/index.ts b/sources/@roots/bud-api/src/methods/splitChunks/index.ts index bdba6e34ff..5356efbd7d 100644 --- a/sources/@roots/bud-api/src/methods/splitChunks/index.ts +++ b/sources/@roots/bud-api/src/methods/splitChunks/index.ts @@ -42,36 +42,41 @@ export interface splitChunks { * bud.splitChunks({chunks: 'all'}) * ``` */ -export const splitChunks: splitChunks = async function ( - this: Bud, - options, -) { +export const splitChunks: splitChunks = async function (this: Bud, value) { /** * For `true` and `undefined` options the default * cache groups are added to the build */ - if (options === true || isUndefined(options)) { - this.hooks.on(`build.optimization.splitChunks`, { - automaticNameDelimiter: sep, - cacheGroups: { - vendor: { - filename: join(`js`, `bundle`, `vendor`, `[name].js`), - idHint: `vendor`, - priority: -20, - test: /[\\/]node_modules[\\/]/, + if (isUndefined(value) || value === true) { + this.hooks.on(`build.optimization.splitChunks`, (options = {}) => { + if (options === false) options = {} + + return { + automaticNameDelimiter: sep, + ...options, + cacheGroups: { + ...(options.cacheGroups ?? {}), + default: false, + vendor: { + chunks: `all`, + enforce: true, + filename: join(`js`, `bundle`, `[name].js`), + idHint: `vendor`, + name: `vendor`, + priority: -20, + test: /[\\/]node_modules[\\/]/, + }, }, - }, - chunks: `all`, - minSize: 0, + } }) return this } /** - * Otherwise we just pass the options through + * Otherwise we just pass the value through */ - this.hooks.on(`build.optimization.splitChunks`, options) + this.hooks.on(`build.optimization.splitChunks`, value) return this } diff --git a/sources/@roots/bud-api/src/methods/splitChunks/splitChunks.test.ts b/sources/@roots/bud-api/src/methods/splitChunks/splitChunks.test.ts index 3157b39919..aa41a71ad5 100644 --- a/sources/@roots/bud-api/src/methods/splitChunks/splitChunks.test.ts +++ b/sources/@roots/bud-api/src/methods/splitChunks/splitChunks.test.ts @@ -30,19 +30,10 @@ describe(`bud.splitChunks`, async () => { await splitChunks() - expect(onSpy).toHaveBeenCalledWith(`build.optimization.splitChunks`, { - chunks: `all`, - automaticNameDelimiter: `/`, - minSize: 0, - cacheGroups: { - vendor: { - idHint: `vendor`, - filename: `js/bundle/vendor/[name].js`, - test: /[\\/]node_modules[\\/]/, - priority: -20, - }, - }, - }) + expect(onSpy).toHaveBeenCalledWith( + `build.optimization.splitChunks`, + expect.any(Function), + ) }) it(`should call bud.hooks.on with default chunk group when called with true`, async () => { @@ -50,19 +41,10 @@ describe(`bud.splitChunks`, async () => { await splitChunks(true) - expect(onSpy).toHaveBeenCalledWith(`build.optimization.splitChunks`, { - chunks: `all`, - automaticNameDelimiter: `/`, - minSize: 0, - cacheGroups: { - vendor: { - idHint: `vendor`, - filename: `js/bundle/vendor/[name].js`, - test: /[\\/]node_modules[\\/]/, - priority: -20, - }, - }, - }) + expect(onSpy).toHaveBeenCalledWith( + `build.optimization.splitChunks`, + expect.any(Function), + ) }) it(`should call bud.hooks.on with custom chunk`, async () => { diff --git a/sources/@roots/bud-babel/tsconfig.json b/sources/@roots/bud-babel/tsconfig.json index e5fb5cf0bc..1374746e13 100644 --- a/sources/@roots/bud-babel/tsconfig.json +++ b/sources/@roots/bud-babel/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../../config/tsconfig.json", "compilerOptions": { "rootDir": "./src", - "outDir": "./lib" + "outDir": "./lib", + "types": ["@roots/bud-framework", "@roots/bud-build"] }, "include": ["src"], "exclude": ["lib", "node_modules", "**/*.test.ts"], diff --git a/sources/@roots/bud-build/src/config/experiments.ts b/sources/@roots/bud-build/src/config/experiments.ts index cf8a732205..0547424c3e 100644 --- a/sources/@roots/bud-build/src/config/experiments.ts +++ b/sources/@roots/bud-build/src/config/experiments.ts @@ -1,6 +1,13 @@ import type {Factory} from './index.js' -export const experiments: Factory<`experiments`> = async ({hooks}) => +export const experiments: Factory<`experiments`> = async ({ + hooks, + isDevelopment, +}) => hooks.filter(`build.experiments`, { backCompat: false, + lazyCompilation: isDevelopment + ? {entries: false, imports: true} + : false, + topLevelAwait: true, }) diff --git a/sources/@roots/bud-build/src/config/infrastructureLogging.ts b/sources/@roots/bud-build/src/config/infrastructureLogging.ts index ff2476619b..efb1a28a06 100644 --- a/sources/@roots/bud-build/src/config/infrastructureLogging.ts +++ b/sources/@roots/bud-build/src/config/infrastructureLogging.ts @@ -1,4 +1,4 @@ -import logger from '@roots/bud-support/utilities/logger' +import logger from '@roots/bud-support/logger' import type {Factory} from './index.js' diff --git a/sources/@roots/bud-build/src/config/output/chunkFilename.ts b/sources/@roots/bud-build/src/config/output/chunkFilename.ts deleted file mode 100644 index a20ee25065..0000000000 --- a/sources/@roots/bud-build/src/config/output/chunkFilename.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type {Bud} from '@roots/bud-framework' - -import {join} from 'node:path' - -import {scriptExtension} from '../../helpers/scriptExtension.js' - -interface Props { - filter: Bud[`hooks`][`filter`] - relPath: Bud[`relPath`] -} - -export const chunkFilename = ({filter, relPath}: Props) => - filter( - `build.output.chunkFilename`, - join(`js`, `dynamic`, relPath(`@name.chunk`)).concat( - scriptExtension(filter, `.mjs`, `.js`), - ), - ) diff --git a/sources/@roots/bud-build/src/config/output/index.ts b/sources/@roots/bud-build/src/config/output/index.ts index 050c97c223..b57464ebec 100644 --- a/sources/@roots/bud-build/src/config/output/index.ts +++ b/sources/@roots/bud-build/src/config/output/index.ts @@ -2,7 +2,6 @@ import type {Factory} from '../index.js' import {isMjs} from '../../helpers/isMjs.js' import {assetModuleFilename} from './assetModuleFilename.js' -import {chunkFilename} from './chunkFilename.js' import {filename} from './filename.js' export const output: Factory<`output`> = async ({ @@ -13,17 +12,23 @@ export const output: Factory<`output`> = async ({ }) => filter(`build.output`, { assetModuleFilename: assetModuleFilename({filter, relPath}), - chunkFilename: chunkFilename({filter, relPath}), + /** + * This should be kept undefined as documented here: + * {@see https://webpack.js.org/plugins/split-chunks-plugin/#splitchunkscachegroupscachegroupfilename} + */ + // chunkFilename: chunkFilename({filter, relPath}), clean: filter(`build.output.clean`, isProduction), environment: filter(`build.output.environment`, undefined), filename: filename({filter, relPath}), + hashFunction: filter(`build.output.hashFunction`, `xxhash64`), + iife: filter(`build.output.iife`, undefined), module: filter(`build.output.module`, false), path: filter(`build.output.path`, path(`@dist`)), pathinfo: filter(`build.output.pathinfo`, false), publicPath: filter(`build.output.publicPath`, `auto`), scriptType: filter( `build.output.scriptType`, - isMjs(filter) ? `module` : false, + isMjs(filter) ? `module` : `text/javascript`, ), uniqueName: filter(`build.output.uniqueName`, `@roots/bud`), }) diff --git a/sources/@roots/bud-build/src/config/parallelism.ts b/sources/@roots/bud-build/src/config/parallelism.ts index c4f3e76ccb..dee5e235ba 100644 --- a/sources/@roots/bud-build/src/config/parallelism.ts +++ b/sources/@roots/bud-build/src/config/parallelism.ts @@ -2,5 +2,15 @@ import {cpus} from 'node:os' import type {Factory} from './index.js' -export const parallelism: Factory<`parallelism`> = async ({hooks}) => - hooks.filter(`build.parallelism`, 10 * Math.max(cpus().length - 1, 1)) +export const parallelism: Factory<`parallelism`> = async ({ + hooks, + root, +}) => { + const factor = 10 + const procs = Math.min(cpus().length - 1, 1) + const comps = root.hasChildren + ? Math.min(Object.keys(root.children).length) + : 1 + + return hooks.filter(`build.parallelism`, (procs * factor) / comps) +} diff --git a/sources/@roots/bud-build/src/config/resolve.ts b/sources/@roots/bud-build/src/config/resolve.ts index 068ed7e918..cf29ccd0d7 100644 --- a/sources/@roots/bud-build/src/config/resolve.ts +++ b/sources/@roots/bud-build/src/config/resolve.ts @@ -26,6 +26,9 @@ export const resolve: Factory<`resolve`> = async bud => { /** * Leave `undefined` to use webpack default (true in dev, false in production) */ - unsafeCache: bud.hooks.filter(`build.module.unsafeCache`, undefined), + unsafeCache: bud.hooks.filter( + `build.module.unsafeCache`, + bud.isDevelopment, + ), }) } diff --git a/sources/@roots/bud-build/src/config/resolveLoader.ts b/sources/@roots/bud-build/src/config/resolveLoader.ts index dfbc4a994f..ae4fdd9b8e 100644 --- a/sources/@roots/bud-build/src/config/resolveLoader.ts +++ b/sources/@roots/bud-build/src/config/resolveLoader.ts @@ -1,6 +1,23 @@ import type {Factory} from './index.js' -export const resolveLoader: Factory<`resolveLoader`> = async ({hooks}) => +export const resolveLoader: Factory<`resolveLoader`> = async ({ + hooks, + module, +}) => hooks.filter(`build.resolveLoader`, { - alias: hooks.filter(`build.resolveLoader.alias`, {}), + alias: hooks.filter(`build.resolveLoader.alias`, { + 'css-loader': await module.resolve(`@roots/bud-support/css-loader`), + 'file-loader': await module.resolve( + `@roots/bud-support/file-loader`, + ), + 'html-loader': await module.resolve( + `@roots/bud-support/html-loader`, + ), + json5: await module.resolve(`@roots/bud-support/json5`), + 'mini-svg-data-uri': await module.resolve(`mini-svg-data-uri`), + 'style-loader': await module.resolve( + `@roots/bud-support/style-loader`, + ), + toml: await module.resolve(`@roots/bud-support/toml`), + }), }) diff --git a/sources/@roots/bud-build/src/config/stats.ts b/sources/@roots/bud-build/src/config/stats.ts index c79d81b1c7..3914d99dda 100644 --- a/sources/@roots/bud-build/src/config/stats.ts +++ b/sources/@roots/bud-build/src/config/stats.ts @@ -7,7 +7,10 @@ export const stats: Factory<`stats`> = async app => ? { all: false, assets: true, + assetsSort: `size`, + builtAt: false, children: false, + chunks: false, entrypoints: true, errors: true, errorsCount: true, diff --git a/sources/@roots/bud-build/src/service.ts b/sources/@roots/bud-build/src/service.ts index 70b1ac274f..025c87bb7e 100644 --- a/sources/@roots/bud-build/src/service.ts +++ b/sources/@roots/bud-build/src/service.ts @@ -1,6 +1,6 @@ +import type {Build as FrameworkService} from '@roots/bud-framework' import type {Items, Loaders, Rules} from '@roots/bud-framework' import type {Configuration} from '@roots/bud-framework/config' -import type * as Base from '@roots/bud-framework/services/build' import {Service} from '@roots/bud-framework/service' import {bind} from '@roots/bud-support/decorators/bind' @@ -18,7 +18,7 @@ import {Rule} from './rule/index.js' /** * Webpack configuration builder class */ -export class Build extends Service implements Base.Service { +export class Build extends Service implements FrameworkService { /** * Built config object */ diff --git a/sources/@roots/bud-build/src/types.ts b/sources/@roots/bud-build/src/types.ts index 7d45661de8..7e62536646 100644 --- a/sources/@roots/bud-build/src/types.ts +++ b/sources/@roots/bud-build/src/types.ts @@ -1,8 +1,14 @@ -import type {Item} from './item/index.js' -import type {Loader} from './loader/index.js' -import type {Rule} from './rule/index.js' +import type {Item} from '@roots/bud-build/item' +import type {Loader} from '@roots/bud-build/loader' +import type {Rule} from '@roots/bud-build/rule' + +import type {Build} from './service.js' declare module '@roots/bud-framework' { + interface Services { + build: Build + } + interface Loaders { css: Loader csv: Loader diff --git a/sources/@roots/bud-build/tsconfig.json b/sources/@roots/bud-build/tsconfig.json index f79937ac01..fd5274aac7 100644 --- a/sources/@roots/bud-build/tsconfig.json +++ b/sources/@roots/bud-build/tsconfig.json @@ -6,7 +6,7 @@ "types": ["node", "@roots/bud-framework", "@roots/bud-hooks"] }, "include": ["./src"], - "exclude": ["./lib", "./node_modules", "**/*.test.ts"], + "exclude": ["**/*.test.ts"], "references": [ {"path": "./../bud-framework/tsconfig.json"}, {"path": "./../bud-hooks/tsconfig.json"}, diff --git a/sources/@roots/bud-cache/src/service/index.ts b/sources/@roots/bud-cache/src/service/index.ts index 38b71036de..cb338e92b9 100644 --- a/sources/@roots/bud-cache/src/service/index.ts +++ b/sources/@roots/bud-cache/src/service/index.ts @@ -1,6 +1,6 @@ import type {Bud} from '@roots/bud-framework' import type {Configuration} from '@roots/bud-framework/config' -import type * as Services from '@roots/bud-framework/services' +import type {Cache as BudCache} from '@roots/bud-framework/services' import {Service} from '@roots/bud-framework/service' import {bind} from '@roots/bud-support/decorators/bind' @@ -14,10 +14,7 @@ import InvalidateCacheExtension from '../invalidate-cache/index.js' /** * Cache service class */ -export default class Cache - extends Service - implements Services.Cache.Service -{ +export default class Cache extends Service implements BudCache { /** * Enabled */ diff --git a/sources/@roots/bud-client/src/hot/components/index.ts b/sources/@roots/bud-client/src/hot/components/index.ts index 3603d54a0d..a12ae1bfe1 100644 --- a/sources/@roots/bud-client/src/hot/components/index.ts +++ b/sources/@roots/bud-client/src/hot/components/index.ts @@ -1,32 +1,20 @@ -interface ControllerModule { - make: () => Promise -} +import * as Indicator from './indicator/index.js' +import * as Overlay from './overlay/index.js' export const make: ( options: Options, ) => Promise> = async options => { if (options.indicator && !customElements.get(`bud-activity-indicator`)) { - await import(`./indicator/index.js`) - .then(makeController) - .then(maybePushController) + maybePushController(Indicator.make()) } if (options.overlay && !customElements.get(`bud-error`)) { - await import(`./overlay/index.js`) - .then(makeController) - .then(maybePushController) + maybePushController(Overlay.make()) } return window.bud.controllers } -const makeController = async ( - module: ControllerModule, -): Promise => { - if (!module) return - return await module.make() -} - const maybePushController = (controller: Controller | undefined) => { if (!controller) return window.bud.controllers.push(controller) diff --git a/sources/@roots/bud-client/src/hot/components/indicator/index.ts b/sources/@roots/bud-client/src/hot/components/indicator/index.ts index a06802a994..e5a3766c6a 100644 --- a/sources/@roots/bud-client/src/hot/components/indicator/index.ts +++ b/sources/@roots/bud-client/src/hot/components/indicator/index.ts @@ -1,9 +1,7 @@ import {Component} from './indicator.component.js' import {Controller} from './indicator.controller.js' -export const make = async (): Promise<{ - update: (data: Payload) => void -}> => { +export const make = () => { if (customElements.get(`bud-activity-indicator`)) return customElements.define(`bud-activity-indicator`, Component) diff --git a/sources/@roots/bud-client/src/hot/components/overlay/index.ts b/sources/@roots/bud-client/src/hot/components/overlay/index.ts index 915a3608a4..05c2f5a13c 100644 --- a/sources/@roots/bud-client/src/hot/components/overlay/index.ts +++ b/sources/@roots/bud-client/src/hot/components/overlay/index.ts @@ -1,9 +1,9 @@ import {Component} from './overlay.component.js' import {Controller} from './overlay.controller.js' -export const make = async (): Promise<{ +export const make = (): { update: (data: Payload) => void -}> => { +} => { if (customElements.get(`bud-error`)) return customElements.define(`bud-error`, Component) diff --git a/sources/@roots/bud-client/src/hot/events.ts b/sources/@roots/bud-client/src/hot/events.ts index b9eb028042..a4682a6f00 100644 --- a/sources/@roots/bud-client/src/hot/events.ts +++ b/sources/@roots/bud-client/src/hot/events.ts @@ -1,8 +1,8 @@ -/* eslint-disable no-console */ +interface EventSourceFactory { + new (path: string): EventSource +} -export const injectEvents = ( - eventSource: new (path: string) => EventSource, -) => { +export const injectEvents = (eventSource: EventSourceFactory) => { /** * EventSource wrapper * @@ -13,14 +13,11 @@ export const injectEvents = ( return class Events extends eventSource { /** * Registered listeners - * - * @public */ public listeners: Set = new Set() /** * EventSource `onmessage` handler - * @public */ public override onmessage = async function (payload: MessageEvent) { if (!payload?.data || payload.data == `\uD83D\uDC93`) { @@ -41,7 +38,6 @@ export const injectEvents = ( /** * EventSource `onopen` handler - * @public */ public override onopen = function () {} @@ -51,7 +47,6 @@ export const injectEvents = ( * @remarks * Singleton interface, so this is private. * - * @public */ private constructor( public options: Partial & {name: string; path: string}, @@ -66,7 +61,6 @@ export const injectEvents = ( /** * Singleton constructor * - * @public */ public static make( options: Partial & {name: string; path: string}, @@ -81,7 +75,6 @@ export const injectEvents = ( /** * EventSource `addMessageListener` handler - * @public */ public addListener(listener: Listener): this { this.listeners.add(listener) diff --git a/sources/@roots/bud-client/src/hot/index.ts b/sources/@roots/bud-client/src/hot/index.ts index e3c5555462..d175bbb30a 100644 --- a/sources/@roots/bud-client/src/hot/index.ts +++ b/sources/@roots/bud-client/src/hot/index.ts @@ -4,14 +4,13 @@ import {client} from './client.js' -try { - client(__resourceQuery, import.meta.webpackHot) -} catch (err) { - console.error(err) - +/** + * Client entrypoint + */ +;(async function () { try { - client(__resourceQuery, module.hot) - } catch (error) { - console.error(error) + await client(__resourceQuery, import.meta.webpackHot) + } catch (err) { + console.error(err) } -} +})() diff --git a/sources/@roots/bud-client/src/hot/log.ts b/sources/@roots/bud-client/src/hot/log.ts index e35c116fea..fbfa0ff626 100644 --- a/sources/@roots/bud-client/src/hot/log.ts +++ b/sources/@roots/bud-client/src/hot/log.ts @@ -9,34 +9,30 @@ export const makeLogger = (options: Options) => { } } -let lastLog: string = null -export const makeLog = options => { - return (...args) => { +export const makeLog = (options: Options) => { + return (...args: Array) => { if (options.log) { - if (lastLog === args.join(``)) return - lastLog = args.join(``) - console.log(`[${options.name}]`, ...args) } } } -export const makeInfo = options => { - return (...args) => { +export const makeInfo = (options: Options) => { + return (...args: Array) => { if (options.log) { console.info(`[${options.name}]`, ...args) } } } -export const makeError = options => { - return (...args) => { +export const makeError = (options: Options) => { + return (...args: Array) => { console.error(`[${options.name}]`, ...args) } } -export const makeWarn = options => { - return (...args) => { +export const makeWarn = (options: Options) => { + return (...args: Array) => { console.warn(`[${options.name}]`, ...args) } } diff --git a/sources/@roots/bud-compiler/package.json b/sources/@roots/bud-compiler/package.json index aa4b3be153..800a105e18 100644 --- a/sources/@roots/bud-compiler/package.json +++ b/sources/@roots/bud-compiler/package.json @@ -49,22 +49,12 @@ ], "type": "module", "exports": { - ".": { - "import": "./lib/index.js", - "default": "./lib/index.js" - }, - "./types": { - "import": "./lib/types.js", - "default": "./lib/types.js" - } + ".": "./lib/index.js" }, "typesVersions": { "*": { ".": [ "./lib/index.d.ts" - ], - "types": [ - "./lib/types.d.ts" ] } }, @@ -81,8 +71,7 @@ "@roots/bud-framework": "workspace:*", "@roots/bud-support": "workspace:*", "react": "18.2.0", - "tslib": "2.5.3", - "webpack": "5.86.0" + "tslib": "2.5.3" }, "volta": { "extends": "../../../package.json" diff --git a/sources/@roots/bud-compiler/src/index.ts b/sources/@roots/bud-compiler/src/index.ts index 713f67c078..149e783d72 100644 --- a/sources/@roots/bud-compiler/src/index.ts +++ b/sources/@roots/bud-compiler/src/index.ts @@ -2,15 +2,18 @@ // Licensed under the MIT license. /** - * The bud compiler interface + * @roots/bud-compiler * * @see https://bud.js.org * @see https://github.com/roots/bud - * - * @packageDocumentation */ -import {Compiler} from './compiler.service.js' -import './types.js' +import {Compiler} from './service.js' + +declare module '@roots/bud-framework' { + interface Services { + compiler: Compiler + } +} -export default Compiler +export {Compiler as default} diff --git a/sources/@roots/bud-compiler/src/compiler.service.tsx b/sources/@roots/bud-compiler/src/service.tsx similarity index 63% rename from sources/@roots/bud-compiler/src/compiler.service.tsx rename to sources/@roots/bud-compiler/src/service.tsx index 4e6c0c332a..71ef6ac958 100644 --- a/sources/@roots/bud-compiler/src/compiler.service.tsx +++ b/sources/@roots/bud-compiler/src/service.tsx @@ -1,11 +1,13 @@ import type {Bud} from '@roots/bud-framework' +import type {Compiler as BudCompiler} from '@roots/bud-framework' import type { + Configuration, MultiCompiler, MultiStats, + Stats, StatsCompilation, StatsError, } from '@roots/bud-framework/config' -import type {Compiler as Contract} from '@roots/bud-framework/services' import type { ErrorWithSourceFile, SourceFile, @@ -14,21 +16,28 @@ import type { import {Error} from '@roots/bud-dashboard/app' import {Service} from '@roots/bud-framework/service' import {bind} from '@roots/bud-support/decorators/bind' -import {BudError} from '@roots/bud-support/errors' +import {BudError, type BudHandler} from '@roots/bud-support/errors' import {duration} from '@roots/bud-support/human-readable' import {render} from '@roots/bud-support/ink' import stripAnsi from '@roots/bud-support/strip-ansi' import webpack from '@roots/bud-support/webpack' +import {cpus} from 'node:os' +import process from 'node:process' import {pathToFileURL} from 'node:url' /** * Wepback compilation controller class */ -export class Compiler extends Service implements Contract.Service { +export class Compiler extends Service implements BudCompiler { + /** + * Compilation stats + */ + public compilationStats: StatsCompilation + /** * Configuration */ - public config: Contract.Service[`config`] = [] + public config: Array & {parallelism?: number} = [] /** * Compiler implementation @@ -38,33 +47,20 @@ export class Compiler extends Service implements Contract.Service { /** * Compiler instance */ - public instance: Contract.Service[`instance`] + public instance: BudCompiler[`instance`] + + public label = `compiler` /** - * Compilation stats + * Raw stats */ - public stats: Contract.Service[`stats`] + public stats: BudCompiler[`stats`] /** * Initiates compilation */ @bind - public async compile(): Promise { - const compilerPath = await this.app.module - .resolve(`webpack`, import.meta.url) - .catch(error => { - throw BudError.normalize(error) - }) - - this.implementation = await this.app.module - .import(compilerPath, import.meta.url) - .catch(error => { - throw BudError.normalize(error) - }) - .finally(() => { - this.logger.info(`imported webpack from ${compilerPath}`) - }) - + public async compile(bud: Bud): Promise { this.config = !this.app.hasChildren ? [await this.app.build.make()] : await Promise.all( @@ -76,6 +72,8 @@ export class Compiler extends Service implements Contract.Service { ), ) + this.config.parallelism = Math.min(cpus().length - 1, 1) + await this.app.hooks.fire(`compiler.before`, this.app) this.logger.timeEnd(`initialize`) @@ -88,16 +86,11 @@ export class Compiler extends Service implements Contract.Service { this.instance.hooks.done.tap(this.app.label, async (stats: any) => { await this.onStats(stats) - await this.app.hooks - .fire(`compiler.after`, this.app) - .catch(error => { - this.logger.error(error) - }) await this.app.hooks - .fire(`compiler.close`, this.app) + .fire(`compiler.done`, [this.app, this.stats]) .catch(error => { - this.logger.error(error) + throw error }) }) @@ -108,57 +101,80 @@ export class Compiler extends Service implements Contract.Service { * Compiler error event */ @bind - public async onError(error: webpack.WebpackError) { - global.process.exitCode = 1 + public async onError(error: BudHandler | webpack.WebpackError) { + process.exitCode = 1 this.app.isDevelopment && this.app.server.appliedMiddleware?.hot?.publish({error}) - // @eslint-disable-next-line no-console - render( - , - ) - - await this.app.hooks.fire(`compiler.error`, error) + await this.app.hooks + .fire(`compiler.error`, error) + .catch(this.app.error) this.app.notifier.notify({ group: this.app.label, message: error.message, subtitle: error.name, }) + + if (`isBudError` in error) { + render() + } else { + render() + } } /** * Stats handler */ @bind - public async onStats(stats: MultiStats) { + public async onStats(stats: Stats & MultiStats) { const makeNoticeTitle = (child: StatsCompilation) => this.app.label !== child.name ? `${this.app.label} (${child.name})` : child.name - this.stats = stats.toJson(this.app.hooks.filter(`build.stats`)) - - await this.app.hooks.fire(`compiler.stats`, stats) + this.stats = stats + + this.compilationStats = stats.toJson({ + all: false, + children: { + all: false, + assets: true, + cached: true, + cachedAssets: true, + cachedModules: true, + entrypoints: true, + errorDetails: false, + errors: true, + errorsCount: true, + errorStack: false, + hash: true, + modules: true, + name: true, + outputPath: true, + reasons: this.app.context.debug ? true : false, + runtime: true, + timings: true, + warnings: true, + warningsCount: true, + }, + name: true, + }) - const statsUpdate = this.app.dashboard.update(stats) + this.app.dashboard.update(this.compilationStats) if (stats.hasErrors()) { process.exitCode = 1 - this.stats.children = this.stats.children?.map(child => ({ - ...child, - errors: this.sourceErrors(child.errors), - })) + this.compilationStats.children = this.compilationStats.children?.map( + child => ({ + ...child, + errors: this.sourceErrors(child.errors), + }), + ) - this.stats.children + this.compilationStats.children ?.filter(child => child.errorsCount > 0) .forEach(child => { try { @@ -179,7 +195,7 @@ export class Compiler extends Service implements Contract.Service { }) } - this.stats.children + this.compilationStats.children ?.filter(child => child.errorsCount === 0) .forEach(child => { try { @@ -194,13 +210,24 @@ export class Compiler extends Service implements Contract.Service { subtitle: `Build successful`, title: makeNoticeTitle(child), }) + this.app.notifier.openBrowser(this.app.server?.publicUrl.href) } catch (error) { this.logger.error(error) } }) + } - await statsUpdate + /** + * {@link Service.register} + */ + @bind + public override async register(bud: Bud): Promise { + this.implementation = await this.app.module + .import(`webpack`, import.meta.url) + .catch(error => { + throw BudError.normalize(error) + }) } /** @@ -218,13 +245,14 @@ export class Compiler extends Service implements Contract.Service { ): ErrorWithSourceFile | StatsError => { let file: SourceFile[`file`] | undefined - const modules = this.stats.children.flatMap(child => child.modules) const moduleIdent = error.moduleId ?? error.moduleName - const module = modules.find( - module => - module?.id === moduleIdent || module?.name === moduleIdent, - ) + const module = this.compilationStats.children + .flatMap(child => child?.modules) + .find( + module => + module?.id === moduleIdent || module?.name === moduleIdent, + ) if (!module) return error @@ -234,11 +262,9 @@ export class Compiler extends Service implements Contract.Service { file = this.app.path(`@src`, module.name) } - if (!file) { - return error - } - - return {...error, file, name: module.name ?? error.name} + return !file + ? {...error, name: module.name ?? error.name} + : {...error, file, name: module.name ?? error.name} } return errors?.map(parseError).filter(Boolean) diff --git a/sources/@roots/bud-compiler/src/types.ts b/sources/@roots/bud-compiler/src/types.ts deleted file mode 100644 index 972f3589b2..0000000000 --- a/sources/@roots/bud-compiler/src/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -/** - * @package @roots/bud-compiler - */ diff --git a/sources/@roots/bud-compiler/src/compiler.test.ts b/sources/@roots/bud-compiler/test/compiler.test.ts similarity index 87% rename from sources/@roots/bud-compiler/src/compiler.test.ts rename to sources/@roots/bud-compiler/test/compiler.test.ts index 3f11c069ec..c8c8e3cead 100644 --- a/sources/@roots/bud-compiler/src/compiler.test.ts +++ b/sources/@roots/bud-compiler/test/compiler.test.ts @@ -1,7 +1,7 @@ import {Bud, factory} from '@repo/test-kit' import {beforeEach, describe, expect, it, vi} from 'vitest' -import Compiler from './index.js' +import Compiler from '../src/index.js' describe(`@roots/bud-compiler`, function () { let bud: Bud @@ -9,9 +9,9 @@ describe(`@roots/bud-compiler`, function () { beforeEach(async () => { vi.clearAllMocks() - bud = await factory({mode: `development`}) compiler = new Compiler(() => bud) + await compiler.register(bud) }) it(`has compile fn`, () => { @@ -20,12 +20,12 @@ describe(`@roots/bud-compiler`, function () { it(`should call logger.log`, async () => { const logSpy = vi.spyOn(compiler.logger, `log`) - await compiler.compile() + await compiler.compile(bud) expect(logSpy).toHaveBeenCalled() }) it(`should have config with array length 1`, async () => { - await compiler.compile() + await compiler.compile(bud) expect(compiler.config).toHaveLength(1) }) @@ -39,13 +39,13 @@ describe(`@roots/bud-compiler`, function () { bar: compiler.app, } - await compiler.compile() + await compiler.compile(bud) expect(compiler.config).toHaveLength(2) }) it(`should set done tap`, async () => { try { - await compiler.compile() + await compiler.compile(bud) expect(compiler.instance.hooks.done.tap).toHaveBeenCalledWith( `MOCK-dev-handle`, compiler.onStats, diff --git a/sources/@roots/bud-criticalcss/src/api/extract.ts b/sources/@roots/bud-criticalcss/src/api/extract.ts index 221f3b4570..7da9dfa492 100644 --- a/sources/@roots/bud-criticalcss/src/api/extract.ts +++ b/sources/@roots/bud-criticalcss/src/api/extract.ts @@ -14,7 +14,7 @@ export const extractCss = function ( ): Bud { const bud = this as Bud - bud.hooks.action(`compiler.close`, async () => { + bud.hooks.action(`compiler.done`, async () => { options = { ...(bud.extensions.get(`@roots/bud-criticalcss`).getOptions() ?? {}), ...(options ?? {}), diff --git a/sources/@roots/bud-dashboard/package.json b/sources/@roots/bud-dashboard/package.json index 0a82f83109..8401790cc0 100644 --- a/sources/@roots/bud-dashboard/package.json +++ b/sources/@roots/bud-dashboard/package.json @@ -60,10 +60,6 @@ "./service": { "import": "./lib/service.js", "default": "./lib/service.js" - }, - "./types": { - "import": "./lib/types.js", - "default": "./lib/types.js" } }, "typesVersions": { @@ -79,9 +75,6 @@ ], "service": [ "./lib/service.d.ts" - ], - "types": [ - "./lib/types.d.ts" ] } }, @@ -96,8 +89,6 @@ "dependencies": { "@roots/bud-framework": "workspace:*", "@roots/bud-support": "workspace:*", - "human-readable": "0.2.1", - "ink-text-input": "5.0.1", "react": "18.2.0", "tslib": "2.5.3", "webpack": "5.86.0" diff --git a/sources/@roots/bud-dashboard/src/dashboard/app.tsx b/sources/@roots/bud-dashboard/src/dashboard/app.tsx index c0af5030d0..de79b7e007 100644 --- a/sources/@roots/bud-dashboard/src/dashboard/app.tsx +++ b/sources/@roots/bud-dashboard/src/dashboard/app.tsx @@ -1,104 +1,111 @@ -import figures from '@roots/bud-support/figures' -import * as Ink from '@roots/bud-support/ink' -import {platform} from 'node:os' +import { + Box, + type PropsWithChildren, + Spinner, + Text, + useApp, + useInput, + useState, + useStdout, +} from '@roots/bud-support/ink' -import type {Props} from './index.js' +import {type Props} from './index.js' +import Compilation from './views/compilation.view.js' +import Debug from './views/debug.view.js' +import {Error} from './views/node-error.view.js' +import {Server} from './views/server.view.js' -import Compilation from './compilation/compilation.component.js' -import Messages from './messages/messages.component.js' -import {Server} from './server/index.js' - -const App = ({ +export const Application = ({ compilations, context, + debug, devUrl, displayAssets, displayEntrypoints, displayServerInfo, + error, mode, + proxy, proxyUrl, - publicDevUrl, - publicProxyUrl, - watchFiles = new Set(), + status, }: Props) => { - if (!compilations.length) - return ( - - No compilations - - ) - - return ( - - {platform() === `win32` ? ( - - 🚫 Your OS is not a supported platform - {` `} - - You will likely encounter problems.{` `} - - Do not make github issues or support requests about Windows - builds - - {` `} - unless you are also submitting accompanying PRs. - - {` `} - - bud.js supports Windows through the Windows Subsystem for Linux - (WSL): https://learn.microsoft.com/en-us/windows/wsl/install - - - ) : null} + const {stdout} = useStdout() - {compilations.map((compilation, id) => ( - - {compilation.errors && ( - - )} + if (error) return - {compilation.warnings && ( - - )} + return ( + + {status && ( + + + {` `} + {status} + + )} + {compilations?.map((compilation, id) => ( + - + + ))} - {mode === `development` ? ( - - - - ) : null} - + 0} + mode={mode} + proxy={proxy} + proxyUrl={proxyUrl} + /> + ) } -export default App +export const TeletypeApplication = ({ + children, + close, + ...props +}: PropsWithChildren) => { + const app = useApp() + + const [displayServerInfo, setDisplayServerInfo] = useState( + props.displayServerInfo, + ) + const [debug, setDisplayDebug] = useState(props.debug) + const [displayEntrypoints, setDisplayEntrypoints] = useState(true) + const [displayAssets, setDisplayAssets] = useState(true) + const [closed, setClosed] = useState(false) + + useInput((key, input) => { + key === `a` && setDisplayAssets(!displayAssets) + key === `e` && setDisplayEntrypoints(!displayEntrypoints) + key === `d` && setDisplayDebug(!debug) + key === `s` && setDisplayServerInfo(!displayServerInfo) + + if (input.escape) { + setClosed(true) + close((error?) => { + app.exit(error) + // eslint-disable-next-line n/no-process-exit + process.exit(error ? 1 : 0) + }) + } + }) + + return ( + + ) +} diff --git a/sources/@roots/bud-dashboard/src/dashboard/chunk/asset.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/chunk/asset.component.tsx deleted file mode 100644 index b5ce4213eb..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/chunk/asset.component.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type {StatsAsset} from '@roots/bud-framework/config' - -import {size as formatSize} from '@roots/bud-support/human-readable' -import {Box, Text} from '@roots/bud-support/ink' - -import Title from '../display/title.component.js' - -interface Props { - cached?: boolean - emitted?: boolean - final?: boolean - indent?: any - info?: StatsAsset['info'] - minWidth: number - name?: string - size?: number -} - -const Asset = ({final, indent, minWidth, name, size}: Props) => { - return ( - - <Box minWidth={minWidth}> - <Text dimColor>{name}</Text> - </Box> - - <Box justifyContent="flex-end" minWidth={10}> - <Text dimColor>{(formatSize(size) as string).trim()}</Text> - </Box> - - ) -} - -export {Asset as default} diff --git a/sources/@roots/bud-dashboard/src/dashboard/chunk/chunk.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/chunk/chunk.component.tsx deleted file mode 100644 index a05d487b98..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/chunk/chunk.component.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type {StatsAsset} from '@roots/bud-framework/config' - -import {Box} from '@roots/bud-support/ink' - -import {longestAssetNameLength} from '../format.js' -import Asset from './asset.component.js' - -const Chunk = ({ - final, - indent, - minWidth, - ...chunk -}: { - assets: Array> - emitted?: boolean - final?: boolean - indent?: any - minWidth?: number -}) => { - return ( - - {chunk?.assets?.map((asset, index) => ( - - ))} - - ) -} - -export default Chunk diff --git a/sources/@roots/bud-dashboard/src/dashboard/chunk/chunkgroup.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/chunk/chunkgroup.component.tsx deleted file mode 100644 index 3f693de51a..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/chunk/chunkgroup.component.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import * as Ink from '@roots/bud-support/ink' - -import Title from '../display/title.component.js' -import {color} from '../format.js' -import Asset from './asset.component.js' - -const ChunkGroup = ({ - final, - indent, - minWidth, - ...chunk -}: { - assets?: Array - assetsSize?: number - cached?: boolean - emitted?: boolean - final: boolean - indent: Array - minWidth?: number - name?: string -}) => { - return ( - - - - <Ink.Text color={color.foregroundColor}> - {chunk.name ?? ``} - </Ink.Text> - - - - {chunk.assets?.map((asset, index) => ( - - ))} - - ) -} - -export default ChunkGroup diff --git a/sources/@roots/bud-dashboard/src/dashboard/compilation/compilation.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/compilation/compilation.component.tsx deleted file mode 100644 index 1a065a390e..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/compilation/compilation.component.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import type { - StatsAsset, - StatsChunkGroup, - StatsCompilation, -} from '@roots/bud-framework/config' -import type {Context} from '@roots/bud-framework/options/context' - -import figures from '@roots/bud-support/figures' -import {duration} from '@roots/bud-support/human-readable' -import * as Ink from '@roots/bud-support/ink' -import {relative} from 'node:path' - -import Chunk from '../chunk/chunk.component.js' -import ChunkGroup from '../chunk/chunkgroup.component.js' -import Space from '../display/space.component.js' -import Title from '../display/title.component.js' -import {color, colorFromStats, longestAssetNameLength} from '../format.js' - -interface Props { - compilation: StatsCompilation - context: Context - displayAssets: boolean - displayEntrypoints: boolean -} - -interface AssetGroup extends StatsChunkGroup { - assets?: Array & {name: string}> -} - -const onlyNotHot = ({name}: StatsAsset) => !name?.includes(`hot-update`) -const onlyStatic = ({name}: StatsAsset) => - ![`.css`, `.js`].some(ext => name?.includes(ext)) - -const makeAssetGroupCallback = - (assets?: {name: string}[]) => - (asset: Partial): Partial => { - const assetModule = assets?.find(a => a?.name === asset?.name) - return {...(asset ?? {}), ...(assetModule ?? {})} - } - -const Compilation = ({ - compilation, - context, - displayAssets, - displayEntrypoints, -}: Props) => { - if (!compilation) return null - - const groupAssets = makeAssetGroupCallback(compilation.assets) - - const entrypoints: AssetGroup = Object.values(compilation.entrypoints) - .filter(Boolean) - .map(entrypoint => ({ - ...entrypoint, - assets: entrypoint.assets.map(groupAssets), - })) - - const assets: Array = compilation.assets - .filter(onlyNotHot) - .filter(onlyStatic) - .filter(Boolean) - .map(groupAssets) - - const truncatedAssets: Array = assets.splice(5) - - const longestEntrypointAssetLength: number = entrypoints.reduce( - (longest: number, entry: AssetGroup) => - Math.max(longestAssetNameLength(entry.assets), longest), - 0, - ) - - const uncachedModuleCount = compilation.modules?.filter( - module => !module.cached, - )?.length - - return ( - - - - {figures.lineDownRightArc} - {figures.line} - - - {compilation.errorsCount > 0 ? ` ${figures.cross}` : ``} - - {` `} - {compilation.name} - {` `} - - {compilation.outputPath ? ( - - ./{relative(context.basedir, compilation.outputPath)} - - ) : null} - - {` `} - - [{compilation.hash}] - - - {!compilation.isChild ? ( - <> - {figures.lineVertical} - - - {entrypoints.some(({assets}) => assets?.length > 0) ? ( - - - <Ink.Text - color={colorFromStats(compilation)} - dimColor={displayEntrypoints === false} - > - entrypoints - </Ink.Text> - - - {displayEntrypoints - ? entrypoints - .filter(({assets}) => assets.length > 0) - .map((chunk: StatsChunkGroup, id: number) => ( - - - - )) - : null} - - ) : null} - - - {assets?.length > 0 ? ( - <> - - - - - - - <Ink.Text - color={colorFromStats(compilation)} - dimColor={displayAssets === false} - > - assets - </Ink.Text> - - - {displayAssets ? ( - <> - - - {truncatedAssets?.length > 0 ? ( - <> - - - - - - - {` `} - {figures.ellipsis} - {` `} - {truncatedAssets.length} - {` `} - additional asset(s) not shown - - - - ) : null} - - ) : null} - - - ) : null} - - ) : null} - - - - - - - <Ink.Text dimColor> - compiled {compilation.modules?.length} modules ( - {compilation.modules?.length - uncachedModuleCount} cached) in - {` `} - {duration(compilation.time) as string} - </Ink.Text> - - - ) -} - -export default Compilation diff --git a/sources/@roots/bud-dashboard/src/dashboard/components/asset.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/components/asset.component.tsx new file mode 100644 index 0000000000..4d4ffc9984 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/components/asset.component.tsx @@ -0,0 +1,64 @@ +import figures from '@roots/bud-support/figures' +import {size as formatSize} from '@roots/bud-support/human-readable' +import {Box, Text} from '@roots/bud-support/ink' + +interface Props { + auxillaryChunkNames?: string[] + cached?: boolean + emitted?: boolean + id?: string + indent?: any + info?: { + chunkhash?: string | string[] + contenthash?: string | string[] + development?: boolean + fullhash?: string | string[] + hotModuleReplacement?: boolean + immutable?: boolean + javascriptModule?: boolean + minimized?: boolean + modulehash?: string | string[] + related?: Record + size?: number + sourceFilename?: string + } + minWidth: number + name?: string + size?: number +} + +const Asset = (asset: Props) => { + if (!asset) return null + + return ( + + + + {!asset.emitted ? figures.almostEqual : figures.pointerSmall} + {` `} + {asset.name} + + + + + + {asset.info?.minimized && ( + + {figures.tick} + {` `} + + )} + + {`${formatSize(asset.size)}`.trim()} + + + ) +} + +export {Asset as default} diff --git a/sources/@roots/bud-dashboard/src/dashboard/components/assets.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/components/assets.component.tsx new file mode 100644 index 0000000000..5f60a6ad3c --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/components/assets.component.tsx @@ -0,0 +1,33 @@ +import type {StatsAsset} from '@roots/bud-framework/config' + +import {Box} from '@roots/bud-support/ink' + +import {useLongestNamedObjectLength} from '../hooks/useLongestNamedObjectLength.js' +import Asset from './asset.component.js' + +interface Props { + assets: Array & {name?: string; size?: number}> + minWidth?: number +} + +const Assets = ({assets, minWidth}: Props) => { + const fallbackMinWidth = useLongestNamedObjectLength(assets) + + if (!assets) return null + + return ( + + {assets + ?.filter(({type}) => type === `asset`) + .map((asset, index) => ( + + ))} + + ) +} + +export default Assets diff --git a/sources/@roots/bud-dashboard/src/dashboard/components/messages.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/components/messages.component.tsx new file mode 100644 index 0000000000..fefb9f4dd6 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/components/messages.component.tsx @@ -0,0 +1,36 @@ +import {Box, Text} from '@roots/bud-support/ink' + +export default function Messages({ + messages, + ...props +}: { + color: string + messages: Array<{message: string}> +}) { + if (!messages?.length) return null + + return ( + + {messages.map((error, id: number) => ( + + ))} + + ) +} + +const Message = ({color, error}) => + error?.message && ( + + {error.message.trim()} + + ) diff --git a/sources/@roots/bud-dashboard/src/dashboard/components/view.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/components/view.component.tsx new file mode 100644 index 0000000000..9ec42633d0 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/components/view.component.tsx @@ -0,0 +1,46 @@ +import figures from '@roots/bud-support/figures' +import {Box, Text} from '@roots/bud-support/ink' + +const View = ({borderColor = `dim`, children, footer, head}) => { + return ( + + + + {figures.lineDownRightArc} + {figures.line} + + + {head} + + + + {children} + + + + + {figures.lineUpRightArc} + {figures.line} + + + {footer} + + + ) +} + +export default View diff --git a/sources/@roots/bud-dashboard/src/dashboard/display/space.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/display/space.component.tsx deleted file mode 100644 index add1af6dde..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/display/space.component.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import figures from '@roots/bud-support/figures' -import * as Ink from '@roots/bud-support/ink' - -const Space = ({ - children, - final = false, -}: React.PropsWithChildren<{final?: boolean}>) => { - const arrayedChildren = Array.isArray(children) ? children : [children] - - return ( - - {arrayedChildren.map((Child, index) => ( - - - {!final ? figures.lineVertical : ` `} - - {Child} - - ))} - - ) -} - -export default Space diff --git a/sources/@roots/bud-dashboard/src/dashboard/display/title.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/display/title.component.tsx deleted file mode 100644 index 43fa21a750..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/display/title.component.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import figures from '@roots/bud-support/figures' -import * as Ink from '@roots/bud-support/ink' - -interface Props - extends React.PropsWithChildren<{ - final?: boolean - finalFigure?: string - indent?: Array - }> {} - -const Title = ({ - children, - final = false, - finalFigure = figures.lineUpRight, - indent = [], -}: Props) => { - return ( - - - {indent.map(indent => - indent ? figures.lineVertical.concat(` `) : ` `, - )} - {final ? finalFigure : figures.lineUpDownRight} - {figures.line} - {` `} - - {children} - - ) -} - -export default Title diff --git a/sources/@roots/bud-dashboard/src/dashboard/format.ts b/sources/@roots/bud-dashboard/src/dashboard/format.ts deleted file mode 100644 index e6040db741..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/format.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { - StatsAsset, - StatsChunkGroup, -} from '@roots/bud-framework/config' - -export const color = { - backgroundColor: `backgroundColor`, - blue: `blue`, - cyan: `cyan`, - dim: `dim`, - foregroundColor: `foregroundColor`, - green: `green`, - magenta: `magenta`, - red: `red`, - yellow: `yellow`, -} - -export const longestAssetNameLength = (chunks: StatsChunkGroup) => - chunks?.reduce((longest: number, asset: StatsAsset) => { - return Math.max(asset.name?.length, longest) - }, 0) + 1 - -export const colorFromStats = (compilation: { - errorsCount?: number - warningsCount?: number -}) => - compilation.errorsCount > 0 - ? color.red - : compilation.warningsCount > 0 - ? color.yellow - : color.green diff --git a/sources/@roots/bud-dashboard/src/dashboard/hooks/useCompilationColor.ts b/sources/@roots/bud-dashboard/src/dashboard/hooks/useCompilationColor.ts new file mode 100644 index 0000000000..1ebc16473a --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/hooks/useCompilationColor.ts @@ -0,0 +1,12 @@ +export const useCompilationColor = ( + compilation: { + errorsCount?: number + warningsCount?: number + }, + successColor: string = `green`, +) => { + if (!compilation) return `dim` + if (compilation.errorsCount > 0) return `red` + if (compilation.warningsCount > 0) return `yellow` + return successColor +} diff --git a/sources/@roots/bud-dashboard/src/dashboard/hooks/useLongestNamedObjectLength.ts b/sources/@roots/bud-dashboard/src/dashboard/hooks/useLongestNamedObjectLength.ts new file mode 100644 index 0000000000..657dae27c0 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/hooks/useLongestNamedObjectLength.ts @@ -0,0 +1,10 @@ +export const useLongestNamedObjectLength = ( + items: Array<{name?: string}> = [], +) => longestNamedObjectLength(items) + +export const longestNamedObjectLength = ( + items: Array<{name?: string}> = [], +) => + items?.reduce((longest: number, item: {name?: string}) => { + return Math.max(item?.name?.length, longest) + }, 0) + 1 diff --git a/sources/@roots/bud-dashboard/src/dashboard/index.tsx b/sources/@roots/bud-dashboard/src/dashboard/index.tsx index 28af6eb7eb..405e5cab85 100644 --- a/sources/@roots/bud-dashboard/src/dashboard/index.tsx +++ b/sources/@roots/bud-dashboard/src/dashboard/index.tsx @@ -1,23 +1,25 @@ import type {StatsCompilation} from '@roots/bud-framework/config' +import type {BudHandler} from '@roots/bud-support/errors' import {type Bud} from '@roots/bud-framework' -import App from './app.js' -import {TTYApp} from './input.js' +export {Application, TeletypeApplication} from './app.js' export interface Props { - compilations: Array - context: Bud['context'] + close?: (callback: (error?: Error | null) => any) => void + closed?: boolean + compilations?: Array + context: Bud[`context`] + debug: boolean devUrl?: URL - displayAssets: boolean - displayEntrypoints: boolean - displayServerInfo: boolean - isTTY?: boolean + displayAssets?: boolean + displayEntrypoints?: boolean + displayServerInfo?: boolean + error?: BudHandler + errors?: StatsCompilation[`errors`] mode: Bud['mode'] + proxy?: boolean proxyUrl?: URL - publicDevUrl?: URL - publicProxyUrl?: URL - watchFiles?: Set + status?: false | string + warnings?: StatsCompilation[`warnings`] } - -export {App, TTYApp} diff --git a/sources/@roots/bud-dashboard/src/dashboard/input.tsx b/sources/@roots/bud-dashboard/src/dashboard/input.tsx deleted file mode 100644 index 9e4d50bceb..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/input.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as Ink from '@roots/bud-support/ink' -import React from 'react' - -import type {Props} from './index.js' - -import {App} from './index.js' - -export const TTYApp = (props: Props) => { - const [displayServerInfo, setDisplayServerInfo] = React.useState(true) - const [displayEntrypoints, setDisplayEntrypoints] = React.useState(true) - const [displayAssets, setDisplayAssets] = React.useState(true) - const app = Ink.useApp() - - Ink.useInput((key, input) => { - key === `s` && setDisplayServerInfo(!displayServerInfo) - key === `e` && setDisplayEntrypoints(!displayEntrypoints) - key === `a` && setDisplayAssets(!displayAssets) - - if (input.escape) { - app.exit() - // eslint-disable-next-line n/no-process-exit - process.exit(0) - } - }) - - return ( - - ) -} diff --git a/sources/@roots/bud-dashboard/src/dashboard/messages/messages.component.tsx b/sources/@roots/bud-dashboard/src/dashboard/messages/messages.component.tsx deleted file mode 100644 index c2ac5020df..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/messages/messages.component.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type {StatsCompilation} from 'webpack' - -import {Box, Text} from '@roots/bud-support/ink' - -export default function Messages({ - color, - figure, - messages, - type, -}: { - color: string - figure: string - messages: StatsCompilation['errors'] | StatsCompilation['warnings'] - type: `error` | `warning` -}) { - if (!messages?.length) return null - - return ( - - {messages.map((error, id: number) => ( - - ))} - - ) -} - -const Message = ({color, error, figure, type}) => - !error ? null : ( - - - {error.message.trim()} - - - ) diff --git a/sources/@roots/bud-dashboard/src/dashboard/server/index.tsx b/sources/@roots/bud-dashboard/src/dashboard/server/index.tsx deleted file mode 100644 index 31a6aeeab1..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/server/index.tsx +++ /dev/null @@ -1,100 +0,0 @@ -// @ts-nocheck - -import figures from '@roots/bud-support/figures' -import {Box, Text} from '@roots/bud-support/ink' -import {externalNetworkInterface} from '@roots/bud-support/os' -import React from 'react' - -import Space from '../display/space.component.js' -import Title from '../display/title.component.js' -import {color} from '../format.js' -import useWatchedFilesCount from './useWatchedFilesCount.js' - -interface Props { - devUrl?: URL - displayServerInfo: boolean - proxyUrl?: URL - publicDevUrl?: URL - publicProxyUrl?: URL - watchFiles: Set -} - -/** - * Server info ink component - */ -export const Server = ({ - devUrl, - displayServerInfo, - proxyUrl, - publicDevUrl, - publicProxyUrl, - watchFiles = new Set(), -}: Props) => { - const ipv4 = externalNetworkInterface.ipv4Url(publicDevUrl.protocol) - ipv4.port = publicDevUrl.port - - const watchedFilesCount = useWatchedFilesCount(watchFiles) - - return ( - - - - {figures.lineDownRightArc} - {figures.line} - - - {` `}server - - - - {displayServerInfo && ( - <> - - - - - - {proxyUrl && ( - - <Text> - proxy:{` `} - {publicProxyUrl && publicProxyUrl.href !== proxyUrl.href - ? `${publicProxyUrl.href}` - : proxyUrl.href} - </Text> - - )} - - {devUrl && ( - - <Text> - dev:{` `} - {publicDevUrl && publicDevUrl.href !== devUrl.href - ? `${publicDevUrl.href}` - : devUrl.href} - </Text> - - )} - - - - - - - - <Text dimColor> - watching project sources - {watchedFilesCount > 0 ? ( - <Text dimColor> - {` `} - (and {watchedFilesCount} other{` `} - {watchedFilesCount > 1 ? `files` : `file`}){` `} - </Text> - ) : null} - </Text> - - - )} - - ) -} diff --git a/sources/@roots/bud-dashboard/src/dashboard/server/useWatchedFilesCount.ts b/sources/@roots/bud-dashboard/src/dashboard/server/useWatchedFilesCount.ts deleted file mode 100644 index 8ff773ed7f..0000000000 --- a/sources/@roots/bud-dashboard/src/dashboard/server/useWatchedFilesCount.ts +++ /dev/null @@ -1,14 +0,0 @@ -import globby from '@roots/bud-support/globby' -import {useEffect, useState} from 'react' - -const useWatchedFilesCount = (patterns: Set) => { - const [count, setCount] = useState(0) - - useEffect(() => { - globby(Array.from(patterns)).then(files => setCount(files.length)) - }, [patterns, setCount]) - - return count -} - -export default useWatchedFilesCount diff --git a/sources/@roots/bud-dashboard/src/dashboard/views/assets.view.tsx b/sources/@roots/bud-dashboard/src/dashboard/views/assets.view.tsx new file mode 100644 index 0000000000..3de259130a --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/views/assets.view.tsx @@ -0,0 +1,81 @@ +import type {StatsCompilation} from '@roots/bud-framework/config' + +import figures from '@roots/bud-support/figures' +import {size} from '@roots/bud-support/human-readable' +import {Text} from '@roots/bud-support/ink' + +import Assets from '../components/assets.component.js' +import View from '../components/view.component.js' +import {useCompilationColor} from '../hooks/useCompilationColor.js' +import {useLongestNamedObjectLength} from '../hooks/useLongestNamedObjectLength.js' + +export interface Props { + compilation?: StatsCompilation + displayAssets?: boolean + limit?: number +} + +export default function CompilationAssets({ + compilation, + displayAssets = true, + limit = 5, +}: Props) { + const minWidth = useLongestNamedObjectLength( + [...(compilation?.assets ?? [])] + .filter( + asset => + !asset.name?.endsWith(`js`) && + !asset.name?.endsWith(`css`) && + asset.size > 0, + ) + .slice(0, limit), + ) + const compilationColor = useCompilationColor(compilation, `cyan`) + + if (!displayAssets) return null + if (!compilation?.assets) return null + + const assets = [...compilation.assets].filter( + asset => + !asset.name?.endsWith(`js`) && + !asset.name?.endsWith(`css`) && + asset.size > 0, + ) + + const hidden = assets.splice(limit) + + return ( + + ) +} + +const Footer = ({ + assets, + hidden, +}: { + assets?: Array<{size: number}> + hidden?: Array<{size: number}> +}) => { + if (!assets) return ... + + const totalFileSize = size( + [...assets, ...(hidden ?? [])].reduce( + (value, asset): number => value + asset.size, + 0, + ), + ) + return {`${totalFileSize}`} +} diff --git a/sources/@roots/bud-dashboard/src/dashboard/views/compilation.view.tsx b/sources/@roots/bud-dashboard/src/dashboard/views/compilation.view.tsx new file mode 100644 index 0000000000..74a3cd55b4 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/views/compilation.view.tsx @@ -0,0 +1,152 @@ +import type { + StatsAsset, + StatsCompilation, +} from '@roots/bud-framework/config' +import type {Context} from '@roots/bud-framework/context' + +import figures from '@roots/bud-support/figures' +import {duration} from '@roots/bud-support/human-readable' +import {Box, Text} from '@roots/bud-support/ink' +import {relative} from 'node:path' + +import Messages from '../components/messages.component.js' +import View from '../components/view.component.js' +import {useCompilationColor} from '../hooks/useCompilationColor.js' +import Assets from './assets.view.js' +import Entrypoints from './entrypoints.view.js' + +export interface Props { + borderColor?: string + compilation: StatsCompilation + context: Context + debug?: boolean + displayAssets?: boolean + displayEntrypoints?: boolean +} + +export interface Asset extends Partial {} + +export interface AssetGroup { + assets?: Array +} + +const Compilation = ({ + compilation, + context, + displayAssets = true, + displayEntrypoints = true, +}: Props) => { + const compilationColor = useCompilationColor(compilation) + return ( + } + head={} + > + + + + + + + + + + ) +} + +const Head = ({compilation, context}: Props) => { + const color = useCompilationColor(compilation) + + if (!compilation) return Loading + + return ( + + + + + {compilation.errorsCount > 0 + ? figures.cross + : figures.hamburger} + {` `} + {compilation.name?.split(`/`)?.pop() ?? ``} + + + {compilation.hash && ( + + {` `}[{compilation.hash ?? ``}]{` `} + + )} + + + + {context.basedir && compilation.outputPath && ( + + {` `}./{relative(context.basedir, compilation.outputPath)} + + )} + + ) +} + +const Footer = ({compilation}: Partial) => { + if (!compilation?.assets) return ... + + const moduleCount = compilation.modules?.filter(mod => + mod.hasOwnProperty(`cached`), + ).length + const cachedModuleCount = compilation.modules?.filter( + mod => mod?.cached, + ).length + + const formattedModuleCount = `${`${cachedModuleCount}/${moduleCount} modules cached`}` + const formattedTime = `${duration(compilation.time)}` + + if (compilation.errorsCount > 0) { + return ( + + + {figures.cross} + {` `} + {formattedTime} + + + ) + } + + if (compilation.warningsCount > 0) { + return ( + + + {figures.warning} + {` `} + {formattedTime} + {` `} + [{formattedModuleCount}] + + + ) + } + + return ( + + + {figures.tick} + {` `} + {formattedTime} + {` `} + [{formattedModuleCount}] + + + ) +} + +export default Compilation diff --git a/sources/@roots/bud-dashboard/src/dashboard/views/debug.view.tsx b/sources/@roots/bud-dashboard/src/dashboard/views/debug.view.tsx new file mode 100644 index 0000000000..02ec7a6199 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/views/debug.view.tsx @@ -0,0 +1,104 @@ +import {Box, Text} from '@roots/bud-support/ink' + +import View from '../components/view.component.js' + +type Fields = Array | number | Record | string + +export interface Props { + compilation?: Record + config?: Record + debug?: boolean +} + +export default function Debug({compilation, debug}: Props) { + if (!debug) return null + if (!compilation) return null + + const format = (obj: Record) => + Object.entries(obj ?? {}) + .filter( + ([k, v]) => + typeof v !== `undefined` && + typeof v !== `string` && + typeof v !== `number` && + v !== null && + !(Array.isArray(v) && v.length === 0), + ) + .map(([k, v]) => [k, typeof v === `function` ? `function` : v]) + + return ( + <> + {format(compilation).map(([key, value]: [string, Fields], id) => ( + + debug config: {`${id + 1}`} /{` `} + {`${format(compilation).length}`} + + } + head={stats: {key}} + key={id} + > + + + + + ))} + + ) +} + +const isPrimitive = ( + field: unknown, +): field is [] | boolean | number | string => { + return ( + typeof field === `boolean` || + typeof field === `string` || + typeof field === `number` || + (Array.isArray(field) && field.length === 0) + ) +} + +const Fields = ({fields}: {fields: Fields}) => { + if (typeof fields === `undefined`) { + return undefined + } + + if (typeof fields === `boolean`) { + return {`${fields}`} + } + + if (fields === null) { + return null + } + + if (Array.isArray(fields) && fields.length === 0) { + return [] + } + + if (isPrimitive(fields)) { + return ( + + {`${fields}`.split(`!`).pop().split(`?`).pop()} + + ) + } + + return Object.entries(fields) + .filter(([k, v]) => v !== undefined && v !== null) + .map(([key, fields]: [string, Fields], id) => { + return ( + + {typeof key === `string` ? key : `-`}: + + + ) + }) +} diff --git a/sources/@roots/bud-dashboard/src/dashboard/views/entrypoints.view.tsx b/sources/@roots/bud-dashboard/src/dashboard/views/entrypoints.view.tsx new file mode 100644 index 0000000000..382d574cdb --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/views/entrypoints.view.tsx @@ -0,0 +1,66 @@ +import type {StatsCompilation} from '@roots/bud-support/webpack' + +import {size} from '@roots/bud-support/human-readable' +import {Box, Text} from '@roots/bud-support/ink' + +import Assets from '../components/assets.component.js' +import View from '../components/view.component.js' +import {useCompilationColor} from '../hooks/useCompilationColor.js' +import {longestNamedObjectLength} from '../hooks/useLongestNamedObjectLength.js' + +interface Props { + compilation: StatsCompilation + displayEntrypoints: boolean +} + +const Entrypoints = ({compilation, displayEntrypoints}: Props) => { + const compilationColor = useCompilationColor(compilation, `cyan`) + + if (!displayEntrypoints) return null + if (!compilation?.entrypoints) return null + + const entrypoints = Object.values(compilation.entrypoints) + .filter(asset => asset?.name) + .map(entrypoint => ({ + ...entrypoint, + assets: entrypoint.assets.map(asset => ({ + ...(asset ?? {}), + ...(compilation?.assets?.find(a => a?.name === asset?.name) ?? {}), + })), + })) + + const minWidth = entrypoints.reduce( + (longest, entry) => + Math.max( + entry.assets ? longestNamedObjectLength(entry.assets) : 0, + longest, + ), + 0, + ) + + return entrypoints.map(({assets, assetsSize, name}, key) => ( + } + head={} + key={key} + > + + + )) +} + +const Head = ({color, name}) => ( + + {name} + +) + +const Foot = ({bytes}: {bytes: number}) => { + return ( + + {`${size(bytes)}`} + + ) +} + +export default Entrypoints diff --git a/sources/@roots/bud-dashboard/src/dashboard/views/node-error.view.tsx b/sources/@roots/bud-dashboard/src/dashboard/views/node-error.view.tsx new file mode 100644 index 0000000000..48ba19c3ff --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/views/node-error.view.tsx @@ -0,0 +1,136 @@ +/* eslint-disable n/no-process-env */ +import cleanStack from '@roots/bud-support/clean-stack' +import {BudError, type BudHandler} from '@roots/bud-support/errors' +import figures from '@roots/bud-support/figures' +import {Box, Text} from '@roots/bud-support/ink' +import isString from '@roots/bud-support/lodash/isString' + +export type Props = React.PropsWithChildren<{ + error: BudHandler + message?: string + name?: string +}> + +const basePath = + process.env.PROJECT_CWD ?? process.env.INIT_CWD ?? process.cwd() + +export const Error = ({error, ...props}: Props) => { + if (!error) { + error = BudError.normalize({...props}) + } + + if (isString(error)) { + return ( + + + {` Error `} + + + + {figures.cross} + {` `} + {error} + + + + ) + } + + return ( + + + {` `} + {error.name ?? `Error`} + {` `} + + + {error.message && ( + + + {figures.cross} + {` `} + {error.message} + + + )} + + {error.details && ( + + + + {figures.ellipsis} + {` `}Error details{` `} + + + {error.details} + + + )} + + {!error.origin && error.stack && ( + + + {figures.hamburger} + {` `}Stack trace + + + + {cleanStack(error.stack, { + basePath, + pretty: true, + })} + + + )} + + {error.docs && ( + + + + {figures.arrowRight} + {` `}Documentation + + {` `} + {error.docs.href} + + + )} + + {error.issues && ( + + + + {figures.arrowRight} + {` `} + Issues + + {` `} + {error.issues.href} + + + )} + + {error.origin && ( + + + + )} + + {error.file && ( + + + {figures.info} + {` `}See file{` `} + + {error.file.path} + + )} + + ) +} + +export const Message = ({children}: React.PropsWithChildren<{}>) => ( + + {children} + +) diff --git a/sources/@roots/bud-dashboard/src/dashboard/views/server.view.tsx b/sources/@roots/bud-dashboard/src/dashboard/views/server.view.tsx new file mode 100644 index 0000000000..041e8b7e97 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/dashboard/views/server.view.tsx @@ -0,0 +1,78 @@ +import figures from '@roots/bud-support/figures' +import {Box, Text} from '@roots/bud-support/ink' +import {externalNetworkInterface} from '@roots/bud-support/os' + +import View from '../components/view.component.js' + +interface Props { + devUrl?: URL + displayServerInfo?: boolean + mode?: `development` | `production` + proxy?: unknown + proxyUrl?: URL +} + +/** + * Server info ink component + */ +export const Server = ({ + devUrl, + displayServerInfo = true, + mode, + proxy = false, + proxyUrl, +}: Props) => { + if (!displayServerInfo) return null + if (mode !== `development`) return null + if (!devUrl || !(devUrl instanceof URL)) return null + + const ipv4 = externalNetworkInterface.ipv4Url(devUrl.protocol) + ipv4.port = devUrl.port + + return ( + + + Watching project sources + + + } + borderColor="blue" + head={{figures.nodejs} Server info} + > + + {proxy && proxyUrl?.href && ( + + + proxy + + + + {figures.lineDashed0} {proxyUrl.href} + + + )} + + {devUrl?.href && ( + + + dev + + + + + {figures.lineDashed0} {devUrl.href} + + {ipv4.href !== devUrl.href && ( + + {figures.lineDashed0} {ipv4.href} + + )} + + + )} + + + ) +} diff --git a/sources/@roots/bud-dashboard/src/formatErrors.ts b/sources/@roots/bud-dashboard/src/formatErrors.ts new file mode 100644 index 0000000000..31c3933b64 --- /dev/null +++ b/sources/@roots/bud-dashboard/src/formatErrors.ts @@ -0,0 +1,73 @@ +import type {Bud} from '@roots/bud-framework' +import type {StatsError} from '@roots/bud-framework/config' + +export const makeErrorFormatter = (bud: Bud) => (errors: StatsError[]) => + errors + .filter(filterInternalErrors) + .map(prettifyErrors) + .map(error => { + const unhandledModule = `You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders` + + if (error.message.includes(unhandledModule)) { + error.message = error.message.replace(unhandledModule, ``) + + const isUnhandledVueModule = + error.moduleName?.match(bud.hooks.filter(`pattern.vue`)) && + !bud.extensions.has(`@roots/bud-vue`) + + if (isUnhandledVueModule) { + error.message = [ + error.message, + `You need to install @roots/bud-vue to compile this module.`, + ].join(`\n\n`) + } + + const isUnhandledSassModule = + error.moduleName?.match(bud.hooks.filter(`pattern.sass`)) && + !bud.extensions.has(`@roots/bud-sass`) + + if (isUnhandledSassModule) { + error.message = [ + error.message, + `You need to install @roots/bud-sass to compile this module.`, + ].join(`\n\n`) + } + + const isUnhandledTsModule = + error.moduleName?.match(bud.hooks.filter(`pattern.ts`)) && + !bud.extensions.has(`@roots/bud-typescript`) && + !bud.extensions.has(`@roots/bud-esbuild`) && + !bud.extensions.has(`@roots/bud-swc`) + + if (isUnhandledTsModule) { + error.message = [ + error.message, + `You need to install a TypeScript compatible extension to compile this module.`, + ].join(`\n\n`) + } + } + + return error + }) + +/** + * Filter internal errors + */ +const filterInternalErrors = (error: StatsError) => + error.message && !error.message?.includes(`HookWebpackError`) + +/** + * Prettify errors + */ +const prettifyErrors = (error: StatsError) => { + error.message = error.message + .replace(`Module parse failed:`, ``) + .replace(/Module build failed \(.*\):?/, ``) + .replace(/ at .*?\/(webpack|tapable)\/?.*/gm, ``) + .trim() + .split(`Error:`) + .pop() + .trim() + + return error +} diff --git a/sources/@roots/bud-dashboard/src/index.ts b/sources/@roots/bud-dashboard/src/index.ts index 1197e3240c..d3d79e124a 100644 --- a/sources/@roots/bud-dashboard/src/index.ts +++ b/sources/@roots/bud-dashboard/src/index.ts @@ -6,11 +6,12 @@ * * @see https://bud.js.org * @see https://github.com/roots/bud - * - * @packageDocumentation */ -import {Dashboard} from './service.js' -import './types.js' +import {Dashboard as Service} from './service.js' + +declare module '@roots/bud-framework' { + type Dashboard = Service +} -export default Dashboard +export {Service as default} diff --git a/sources/@roots/bud-dashboard/src/service.tsx b/sources/@roots/bud-dashboard/src/service.tsx index 1cece628ca..4a30f63333 100644 --- a/sources/@roots/bud-dashboard/src/service.tsx +++ b/sources/@roots/bud-dashboard/src/service.tsx @@ -1,166 +1,158 @@ /* eslint-disable no-console */ +import type {Bud} from '@roots/bud-framework' import type { - MultiStats, StatsCompilation, StatsError, } from '@roots/bud-framework/config' -import type {Service as Contract} from '@roots/bud-framework/services/dashboard' +import type {Dashboard as BudDashboard} from '@roots/bud-framework/services' +import type {BudHandler} from '@roots/bud-support/errors' import {Service} from '@roots/bud-framework/service' import {bind} from '@roots/bud-support/decorators/bind' -import {Box, render, Text} from '@roots/bud-support/ink' -import isString from '@roots/bud-support/lodash/isString' -import stripAnsi from '@roots/bud-support/strip-ansi' -import {sep} from 'node:path' +import {Box, type ReactElement, render} from '@roots/bud-support/ink' +import process from 'node:process' import {Console} from './console/index.js' +import {Application, TeletypeApplication} from './dashboard/index.js' +import {makeErrorFormatter} from './formatErrors.js' type Compilations = Array> -const tagInnerChilds = ({children}: StatsCompilation) => - children.map(child => ({...child, isChild: true})) - /** * Dashboard service */ -export class Dashboard extends Service implements Contract { +export class Dashboard extends Service implements BudDashboard { + /** + * Error formatter + */ + public formatStatsErrors: (errors: StatsError[]) => StatsError[] + + /** + * Previously rendered hashes + */ public hashes = new Set() + /** + * Ink instance + */ + public instance?: BudDashboard[`instance`] + /** * Received stats */ public stats?: StatsCompilation /** - * Error formatter + * Stdout + */ + public stderr = process.stderr + + /** + * Stderr + */ + public stdout = process.stdout + + /** + * Class constructor + * @param app + */ + public constructor(app: () => Bud) { + super(app) + this.formatStatsErrors = makeErrorFormatter(this.app) + this.render({status: `Initializing bud.js`}) + } + + /** + * {@link Service.register} */ @bind - public compilationErrors?(errors: StatsError[]) { - try { - return ( - errors - /* Filter unhelpful errors from compiler internals */ - .filter( - error => error && !error.message?.includes(`HookWebpackError`), - ) - /* Format errors */ - .map(({message, ...error}: StatsError) => ({ - ...error, - message: message - /* Discard unhelpful stack traces */ - .split(/ at /) - .shift() - - /* Discard unhelpful stuff preceeding message */ - .split(/SyntaxError:?/) - .pop() - .split(/ModuleError:/) - .pop() - .split(/Error:/) - .pop() - - /* Process line-by-line */ - .split(`\n`) - - /* Discard emoji */ - .map(ln => ln.replaceAll(/×/g, ``)) - /* Discard origin */ - .map(ln => ln.replaceAll(/\[.*\]/g, ``)) - /* Replace project path with . */ - .map(ln => - ln.replaceAll( - new RegExp(this.app.path().concat(sep), `g`), - `.`, - ), - ) - - /* Discard empty lines */ - .filter( - ln => - isString(ln) && - ![` `, `\n`, ``].includes(ln) && - !ln.match(/^\s*$/), - ) - .map(stripAnsi) - - /* Reform message */ - .join(`\n`), - })) - ) - } catch (error) { - return errors - } + public override async boot(bud: Bud) { + this.render({status: `Booting bud.js`}) } /** - * Render webpack stats + * {@link Service.register} */ @bind - public async render(stats: StatsCompilation) { - try { - const Dashboard = await import(`./dashboard/index.js`) - - if (this.hashes.has(stats.hash)) return - this.hashes.add(stats.hash) - - const compilations: Compilations = stats.children?.length - ? [ - ...stats.children, - ...stats.children?.map(tagInnerChilds), - ].flat() - : stats - ? [stats] - : [] - - const App = - process.stdout.isTTY && !this.app.isProduction - ? Dashboard.TTYApp - : Dashboard.App - - render( - - <> - - - - ({ - ...compilation, - assets: compilation.assets ?? {}, - entrypoints: compilation.entrypoints ?? {}, - errors: this.compilationErrors(compilation.errors), - warnings: this.compilationErrors(compilation.warnings), - }))} - context={this.app.context} - devUrl={this.app.server?.url} - displayAssets={true} - displayEntrypoints={true} - displayServerInfo={false} - mode={this.app.mode} - proxyUrl={this.app.hooks.filter(`dev.proxyUrl`)} - publicDevUrl={this.app.server?.publicUrl} - publicProxyUrl={this.app.hooks.filter(`dev.publicProxyUrl`)} - watchFiles={this.app.server?.watcher?.files} - /> - , - ) - } catch (error) {} + public override async buildBefore(bud: Bud) { + this.render({status: `Building configuration`}) } /** - * Render queued messages + * {@link Service.register} */ @bind - public async renderQueuedMessages() { - render( - this.app.consoleBuffer.queue?.length > 0 && ( - <> - - - {` `} - - - ), + public override async compilerBefore(bud: Bud) { + this.render({status: `Compiling application modules`}) + } + + /** + * {@link Service.register} + */ + @bind + public override async register(bud: Bud) { + this.render({status: `Registering bud.js services and extensions`}) + } + + /** + * Render console messages, build stats and development server information + */ + @bind + public render({ + error, + status, + }: { + error?: BudHandler + status?: false | string + }) { + if (this.silent) return + if (this.app.context.ci) return + + const renderApplication = this.instance?.rerender + ? this.instance.rerender + : (node: ReactElement) => { + this.instance = render(node) + } + + const getCompilations = (): Compilations => { + if (!this.stats) return [] + if (!this.stats.children?.length) return [this.stats] + return this.stats.children.flat() + } + + const compilations = getCompilations().map(compilation => ({ + ...compilation, + errors: this.formatStatsErrors(compilation.errors), + warnings: this.formatStatsErrors(compilation.warnings), + })) + + const messages = this.app?.console?.fetchAndRemove() ?? [] + + const App = + process.stdin.isTTY && this.app.isDevelopment + ? TeletypeApplication + : Application + + renderApplication( + + + + + this.app.compiler?.instance?.compilers?.map(c => c.close(cb)) + } + compilations={compilations} + context={this.app.context} + debug={this.app.context.debug} + devUrl={this.app.server?.publicUrl} + displayServerInfo={this.app.mode === `development`} + error={error} + mode={this.app.mode} + proxy={this.app.server?.enabledMiddleware?.[`proxy`]} + proxyUrl={this.app.server?.publicProxyUrl} + status={status} + /> + , ) } @@ -168,13 +160,9 @@ export class Dashboard extends Service implements Contract { * Render stats as a simple string */ @bind - public renderString(stats: MultiStats) { - const stringCompilation = stats.toString({ - colors: true, - preset: `minimal`, - }) - - process.stdout.write(stringCompilation) + public renderString(stats: string) { + if (this.silent) return + process.stdout.write(stats) } /** @@ -188,26 +176,27 @@ export class Dashboard extends Service implements Contract { * Run dashboard */ @bind - public async update(stats: MultiStats): Promise { - if (!stats) return this + public update( + stats: StatsCompilation, + status: false | string = false, + ): this { + if (this.hashes.has(stats.hash)) return + if (stats.hash) this.hashes.add(stats.hash) this.stats = stats - if (this.silent) { - this.logger.log(`dashboard called but --silent flag is set.`) - return this - } - if (this.app.context.ci === true) { - this.renderString(stats) + this.renderString( + this.app.compiler.stats.toString({ + color: true, + preset: `minimal`, + }), + ) + return this } - try { - await this.render(stats.toJson(this.app.hooks.filter(`build.stats`))) - } catch (error) { - this.renderString(stats) - } + this.render({status}) return this } diff --git a/sources/@roots/bud-dashboard/src/types.ts b/sources/@roots/bud-dashboard/src/types.ts deleted file mode 100644 index 2e5ce0f922..0000000000 --- a/sources/@roots/bud-dashboard/src/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -/** - * @package @roots/bud-dashboard - */ diff --git a/sources/@roots/bud-dashboard/test/app.test.tsx b/sources/@roots/bud-dashboard/test/app.test.tsx index 2eebd9c680..6ea618dd97 100644 --- a/sources/@roots/bud-dashboard/test/app.test.tsx +++ b/sources/@roots/bud-dashboard/test/app.test.tsx @@ -1,131 +1,103 @@ -import {factory} from '@repo/test-kit' +import React from '@roots/bud-support/ink' +import {Bud, factory} from '@repo/test-kit' +// @ts-ignore import {render} from 'ink-testing-library' import {beforeEach, describe, expect, it} from 'vitest' -import App from '../src/dashboard/app.js' +import {Application} from '../src/dashboard/app.js' const mockCompilations = [ { assets: [], entrypoints: {}, errors: [], + modules: [], + warnings: [], }, ] describe(`@roots/bud-dashboard app component`, () => { - let bud + let bud: Bud beforeEach(async () => { bud = await factory() }) - it(`should render stats`, async () => { + it(`should render stats`, () => { const {lastFrame} = render( - , ) - expect(lastFrame()).toContain(`compiled modules`) + expect(lastFrame()).toContain(`✔`) }) - it(`should render server info`, async () => { + it(`should render server info`, () => { const {lastFrame} = render( - // @ts-ignore - , ) - expect(lastFrame()).toContain(`proxy`) + expect(lastFrame()).toContain(`:3000/`) + expect(lastFrame()).toContain(`:8080/`) }) - it(`should render server info`, async () => { + it(`should render watch list`, () => { const {lastFrame} = render( - // @ts-ignore - , ) - expect(lastFrame()).toContain(`proxy`) - expect(lastFrame()).toContain(`dev`) - expect(lastFrame()).not.toContain(`internal`) - expect(lastFrame()).not.toContain(`external`) + expect(lastFrame()).toContain(`Watching project sources`) }) - it(`should render watch list`, async () => { + it(`should not render watch list in prod`, () => { const {lastFrame} = render( - , - ) - - expect(lastFrame()).toContain(`watching project sources`) - }) - - it(`should not render watch list in prod`, async () => { - const {lastFrame} = render( - , ) - expect(lastFrame()).not.toContain(`watching project sources`) + expect(lastFrame()).not.toContain(`Watching project sources`) }) }) diff --git a/sources/@roots/bud-dashboard/tsconfig.json b/sources/@roots/bud-dashboard/tsconfig.json index dfbc2f40ad..936b523eca 100644 --- a/sources/@roots/bud-dashboard/tsconfig.json +++ b/sources/@roots/bud-dashboard/tsconfig.json @@ -5,7 +5,7 @@ "outDir": "./lib" }, "include": ["./src/**/*.tsx", "./src/**/*.ts"], - "exclude": ["./lib", "./node_modules", "**/*.test.ts", "**/*.test.tsx"], + "exclude": ["**/*.test.ts", "**/*.test.tsx"], "references": [ {"path": "./../bud-framework/tsconfig.json"}, {"path": "./../bud-support/tsconfig.json"} diff --git a/sources/@roots/bud-eslint/src/extension.ts b/sources/@roots/bud-eslint/src/extension.ts index cef83691b7..ad8ab9cdd7 100644 --- a/sources/@roots/bud-eslint/src/extension.ts +++ b/sources/@roots/bud-eslint/src/extension.ts @@ -1,4 +1,4 @@ -import type {Bud} from '@roots/bud' +import type {Bud} from '@roots/bud-framework' import {Extension} from '@roots/bud-framework/extension' import { diff --git a/sources/@roots/bud-eslint/src/types.ts b/sources/@roots/bud-eslint/src/types.ts index 7069300f46..7cdb1bda7c 100644 --- a/sources/@roots/bud-eslint/src/types.ts +++ b/sources/@roots/bud-eslint/src/types.ts @@ -1,19 +1,11 @@ -import {type Api, type BudEslint} from './extension.js' +import * as Extension from './extension.js' declare module '@roots/bud-framework' { interface Bud { - eslint: Api & BudEslint + eslint: Extension.Api & Extension.BudEslint } - interface Modules { - '@roots/bud-eslint': BudEslint - } -} -declare module '@roots/bud' { - interface Bud { - eslint: Api & BudEslint - } interface Modules { - '@roots/bud-eslint': BudEslint + '@roots/bud-eslint': Extension.BudEslint } } diff --git a/sources/@roots/bud-eslint/tsconfig.json b/sources/@roots/bud-eslint/tsconfig.json index 904658780d..9f147fc5fe 100644 --- a/sources/@roots/bud-eslint/tsconfig.json +++ b/sources/@roots/bud-eslint/tsconfig.json @@ -2,11 +2,11 @@ "extends": "../../../config/tsconfig.json", "compilerOptions": { "rootDir": "src", - "outDir": "lib" + "outDir": "lib", + "types": ["@roots/bud", "@roots/bud-framework"] }, "include": ["src"], "exclude": ["node_modules", "lib", "test"], - "types": ["@roots/bud", "@roots/bud-framework"], "references": [ {"path": "./../bud-framework/tsconfig.json"}, {"path": "./../bud/tsconfig.json"}, diff --git a/sources/@roots/bud-extensions/package.json b/sources/@roots/bud-extensions/package.json index 4ef8c7c75e..db11325dcd 100644 --- a/sources/@roots/bud-extensions/package.json +++ b/sources/@roots/bud-extensions/package.json @@ -49,22 +49,24 @@ ], "type": "module", "exports": { - ".": { - "import": "./lib/index.js", - "default": "./lib/index.js" - }, - "./service": { - "import": "./lib/service/index.js", - "default": "./lib/service/index.js" - }, - "./types": { - "import": "./lib/types.js", - "default": "./lib/types.js" - }, - "./*": { - "import": "./lib/extensions/*/index.js", - "default": "./lib/extensions/*/index.js" - } + ".": "./lib/index.js", + "./service": "./lib/service.js", + "./cdn": "./lib/cdn/index.js", + "./clean-webpack-plugin": "./lib/clean-webpack-plugin/index.js", + "./copy-webpack-plugin": "./lib/copy-webpack-plugin/index.js", + "./esm": "./lib/esm/index.js", + "./fix-style-only-entrypoints": "./lib/fix-style-only-entrypoints/index.js", + "./html-webpack-plugin": "./lib/html-webpack-plugin/index.js", + "./import-map": "./lib/import-map/index.js", + "./interpolate-html-webpack-plugin": "./lib/interpolate-html-webpack-plugin/index.js", + "./interpolate-html-webpack-plugin/plugin": "./lib/interpolate-html-webpack-plugin/plugin/index.js", + "./mini-css-extract-plugin": "./lib/mini-css-extract-plugin/index.js", + "./tsconfig-values": "./lib/tsconfig-values/index.js", + "./webpack-define-plugin": "./lib/webpack-define-plugin/index.js", + "./webpack-hot-module-replacement-plugin": "./lib/webpack-hot-module-replacement-plugin/index.js", + "./webpack-lifecycle-plugin": "./lib/webpack-lifecycle-plugin/index.js", + "./webpack-manifest-plugin": "./lib/webpack-manifest-plugin/index.js", + "./webpack-provide-plugin": "./lib/webpack-provide-plugin/index.js" }, "typesVersions": { "*": { @@ -72,13 +74,55 @@ "./lib/index.d.ts" ], "service": [ - "./lib/service/index.d.ts" + "./lib/service.d.ts" + ], + "cdn": [ + "./lib/cdn/index.d.ts" + ], + "clean-webpack-plugin": [ + "./lib/clean-webpack-plugin/index.d.ts" + ], + "copy-webpack-plugin": [ + "./lib/copy-webpack-plugin/index.d.ts" + ], + "esm": [ + "./lib/esm/index.d.ts" + ], + "fix-style-only-entrypoints": [ + "./lib/fix-style-only-entrypoints/index.d.ts" + ], + "html-webpack-plugin": [ + "./lib/html-webpack-plugin/index.d.ts" + ], + "import-map": [ + "./lib/import-map/index.d.ts" + ], + "interpolate-html-webpack-plugin": [ + "./lib/interpolate-html-webpack-plugin/index.d.ts" + ], + "interpolate-html-webpack-plugin/plugin": [ + "./lib/interpolate-html-webpack-plugin/plugin/index.d.ts" + ], + "mini-css-extract-plugin": [ + "./lib/mini-css-extract-plugin/index.d.ts" + ], + "tsconfig-values": [ + "./lib/tsconfig-values/index.d.ts" + ], + "webpack-define-plugin": [ + "./lib/webpack-define-plugin/index.d.ts" + ], + "webpack-hot-module-replacement-plugin": [ + "./lib/webpack-hot-module-replacement-plugin/index.d.ts" + ], + "webpack-lifecycle-plugin": [ + "./lib/webpack-lifecycle-plugin/index.d.ts" ], - "types": [ - "./lib/types.d.ts" + "webpack-manifest-plugin": [ + "./lib/webpack-manifest-plugin/index.d.ts" ], - "*": [ - "./lib/extensions/*/index.d.ts" + "webpack-provide-plugin": [ + "./lib/webpack-provide-plugin/index.d.ts" ] } }, @@ -94,8 +138,7 @@ "@roots/bud-minify": "workspace:*", "@roots/bud-support": "workspace:*", "@roots/container": "workspace:*", - "tslib": "2.5.3", - "webpack": "5.86.0" + "tslib": "2.5.3" }, "volta": { "extends": "../../../package.json" diff --git a/sources/@roots/bud-extensions/src/extensions/cdn/index.ts b/sources/@roots/bud-extensions/src/cdn/index.ts similarity index 91% rename from sources/@roots/bud-extensions/src/extensions/cdn/index.ts rename to sources/@roots/bud-extensions/src/cdn/index.ts index 71e8bceabd..3ffb74eac6 100644 --- a/sources/@roots/bud-extensions/src/extensions/cdn/index.ts +++ b/sources/@roots/bud-extensions/src/cdn/index.ts @@ -80,6 +80,7 @@ export default class Cdn extends Extension implements Api { * CDN key to URL mapping */ public sources = new Map([ + [`esm.sh`, `https://esm.sh/`], [`gist`, `https://gist.githubusercontent.com/`], [`github`, `https://raw.githubusercontent.com/`], [`skypack`, `https://cdn.skypack.dev/`], @@ -131,22 +132,9 @@ export default class Cdn extends Extension implements Api { }, ), } as any) - - await Promise.all( - (bud.context.manifest?.bud?.imports?.[ident] ?? []).map( - async ([signifier, remote]) => { - await bud.extensions.add({ - make: async () => - new NormalModuleReplacementPlugin( - new RegExp(`^${signifier}`), - `${url}${remote}`, - ), - }) - }, - ), - ) } } + @bind public disableCache(): this { this.cacheEnabled = false diff --git a/sources/@roots/bud-extensions/src/extensions/clean-webpack-plugin/index.ts b/sources/@roots/bud-extensions/src/clean-webpack-plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/clean-webpack-plugin/index.ts rename to sources/@roots/bud-extensions/src/clean-webpack-plugin/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/copy-webpack-plugin/index.ts b/sources/@roots/bud-extensions/src/copy-webpack-plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/copy-webpack-plugin/index.ts rename to sources/@roots/bud-extensions/src/copy-webpack-plugin/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/esm/index.ts b/sources/@roots/bud-extensions/src/esm/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/esm/index.ts rename to sources/@roots/bud-extensions/src/esm/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/index.ts b/sources/@roots/bud-extensions/src/extensions/index.ts deleted file mode 100644 index 607263ace2..0000000000 --- a/sources/@roots/bud-extensions/src/extensions/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import BudCDN from './cdn/index.js' -import CleanWebpackPlugin from './clean-webpack-plugin/index.js' -import CopyWebpackPlugin from './copy-webpack-plugin/index.js' -import BudESM from './esm/index.js' -import BudFixStyleOnlyEntrypoints from './fix-style-only-entrypoints/index.js' -import HtmlWebpackPlugin from './html-webpack-plugin/index.js' -import InterpolateHtmlPlugin from './interpolate-html-webpack-plugin/index.js' -import MiniCssExtractPlugin from './mini-css-extract-plugin/index.js' -import BudTsConfigValues from './tsconfig-values/index.js' -import WebpackDefinePlugin from './webpack-define-plugin/index.js' -import WebpackHotModuleReplacementPlugin from './webpack-hot-module-replacement-plugin/index.js' -import BudWebpackLifecyclePlugin from './webpack-lifecycle-plugin/index.js' -import WebpackManifestPlugin from './webpack-manifest-plugin/index.js' -import WebpackProvidePlugin from './webpack-provide-plugin/index.js' - -export { - BudCDN, - BudESM, - BudFixStyleOnlyEntrypoints, - BudTsConfigValues, - BudWebpackLifecyclePlugin, - CleanWebpackPlugin, - CopyWebpackPlugin, - HtmlWebpackPlugin, - InterpolateHtmlPlugin, - MiniCssExtractPlugin, - WebpackDefinePlugin, - WebpackHotModuleReplacementPlugin, - WebpackManifestPlugin, - WebpackProvidePlugin, -} diff --git a/sources/@roots/bud-extensions/src/extensions/fix-style-only-entrypoints/index.ts b/sources/@roots/bud-extensions/src/fix-style-only-entrypoints/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/fix-style-only-entrypoints/index.ts rename to sources/@roots/bud-extensions/src/fix-style-only-entrypoints/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/fix-style-only-entrypoints/plugin.ts b/sources/@roots/bud-extensions/src/fix-style-only-entrypoints/plugin.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/fix-style-only-entrypoints/plugin.ts rename to sources/@roots/bud-extensions/src/fix-style-only-entrypoints/plugin.ts diff --git a/sources/@roots/bud-extensions/src/service/util/handleManifestSchemaWarning.ts b/sources/@roots/bud-extensions/src/helpers/handleManifestSchemaWarning.ts similarity index 93% rename from sources/@roots/bud-extensions/src/service/util/handleManifestSchemaWarning.ts rename to sources/@roots/bud-extensions/src/helpers/handleManifestSchemaWarning.ts index 27cb16b5a1..e00edc4aae 100644 --- a/sources/@roots/bud-extensions/src/service/util/handleManifestSchemaWarning.ts +++ b/sources/@roots/bud-extensions/src/helpers/handleManifestSchemaWarning.ts @@ -1,6 +1,7 @@ -import type Extensions from '@roots/bud-extensions' import type {Bud} from '@roots/bud-framework' +import type Extensions from '../index.js' + export function handleManifestSchemaWarning(this: Extensions, bud: Bud) { if (!bud.context.manifest?.bud) return diff --git a/sources/@roots/bud-extensions/src/service/util/isConstructor.ts b/sources/@roots/bud-extensions/src/helpers/isConstructor.ts similarity index 57% rename from sources/@roots/bud-extensions/src/service/util/isConstructor.ts rename to sources/@roots/bud-extensions/src/helpers/isConstructor.ts index 9178c76461..f49a67ee2b 100644 --- a/sources/@roots/bud-extensions/src/service/util/isConstructor.ts +++ b/sources/@roots/bud-extensions/src/helpers/isConstructor.ts @@ -1,8 +1,10 @@ /** * isConstructor * - * @param subject Any js entity - * @returns true if subject is a constructor + * This function checks if the given subject is a constructor. + * + * @param subject Any JavaScript entity + * @returns true if subject is a constructor, false otherwise */ export const isConstructor = ( subject: any, diff --git a/sources/@roots/bud-extensions/src/extensions/html-webpack-plugin/index.ts b/sources/@roots/bud-extensions/src/html-webpack-plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/html-webpack-plugin/index.ts rename to sources/@roots/bud-extensions/src/html-webpack-plugin/index.ts diff --git a/sources/@roots/bud-extensions/src/import-map/index.ts b/sources/@roots/bud-extensions/src/import-map/index.ts new file mode 100644 index 0000000000..a16046ac15 --- /dev/null +++ b/sources/@roots/bud-extensions/src/import-map/index.ts @@ -0,0 +1,22 @@ +import {Extension} from '@roots/bud-framework/extension' +import {bind, label} from '@roots/bud-framework/extension/decorators' + +/** + * Import map extension + */ +@label(`@roots/bud-extensions/import-map`) +export default class BudImportMapExtension extends Extension { + /** + * {@link Extension.register} + */ + @bind + public override async register(bud) { + if (!bud.context.manifest?.imports) return + + Object.entries(bud.context.manifest.imports).map( + ([k, v]: [string, string]) => { + if (!v.match(/https?:$/)) bud.alias(k, bud.path(v)) + }, + ) + } +} diff --git a/sources/@roots/bud-extensions/src/index.ts b/sources/@roots/bud-extensions/src/index.ts index a0d3e02295..0b07dbd351 100644 --- a/sources/@roots/bud-extensions/src/index.ts +++ b/sources/@roots/bud-extensions/src/index.ts @@ -2,15 +2,59 @@ // Licensed under the MIT license. /** - * Extensions controller and built-in extensions + * @roots/bud-extensions * * @see {@link https://bud.js.org} * @see {@link https://github.com/roots/bud} - * - * @packageDocumentation */ -import Extensions from './service/index.js' -import './types.js' +import type BudCDN from '@roots/bud-extensions/cdn' +import type CleanWebpackPlugin from '@roots/bud-extensions/clean-webpack-plugin' +import type CopyWebpackPlugin from '@roots/bud-extensions/copy-webpack-plugin' +import type BudESM from '@roots/bud-extensions/esm' +import type BudFixStyleOnlyEntrypoints from '@roots/bud-extensions/fix-style-only-entrypoints' +import type HtmlWebpackPlugin from '@roots/bud-extensions/html-webpack-plugin' +import type BudImportMap from '@roots/bud-extensions/import-map' +import type InterpolateHtmlPlugin from '@roots/bud-extensions/interpolate-html-webpack-plugin' +import type MiniCssExtractPlugin from '@roots/bud-extensions/mini-css-extract-plugin' +import type BudTsConfigValues from '@roots/bud-extensions/tsconfig-values' +import type WebpackDefinePlugin from '@roots/bud-extensions/webpack-define-plugin' +import type WebpackHotModuleReplacementPlugin from '@roots/bud-extensions/webpack-hot-module-replacement-plugin' +import type BudWebpackLifecyclePlugin from '@roots/bud-extensions/webpack-lifecycle-plugin' +import type WebpackManifestPlugin from '@roots/bud-extensions/webpack-manifest-plugin' +import type WebpackProvidePlugin from '@roots/bud-extensions/webpack-provide-plugin' + +import {Extensions} from '@roots/bud-extensions/service' + +declare module '@roots/bud-framework' { + interface Bud { + cdn: BudCDN + esm: BudESM + manifest: WebpackManifestPlugin + tsconfig: BudTsConfigValues + } + + interface Modules { + '@roots/bud-extensions/cdn': BudCDN + '@roots/bud-extensions/clean-webpack-plugin': CleanWebpackPlugin + '@roots/bud-extensions/copy-webpack-plugin': CopyWebpackPlugin + '@roots/bud-extensions/esm': BudESM + '@roots/bud-extensions/fix-style-only-entrypoints': BudFixStyleOnlyEntrypoints + '@roots/bud-extensions/html-webpack-plugin': HtmlWebpackPlugin + '@roots/bud-extensions/import-map': BudImportMap + '@roots/bud-extensions/interpolate-html-webpack-plugin': InterpolateHtmlPlugin + '@roots/bud-extensions/mini-css-extract-plugin': MiniCssExtractPlugin + '@roots/bud-extensions/tsconfig-values': BudTsConfigValues + '@roots/bud-extensions/webpack-define-plugin': WebpackDefinePlugin + '@roots/bud-extensions/webpack-hot-module-replacement-plugin': WebpackHotModuleReplacementPlugin + '@roots/bud-extensions/webpack-lifecycle-plugin': BudWebpackLifecyclePlugin + '@roots/bud-extensions/webpack-manifest-plugin': WebpackManifestPlugin + '@roots/bud-extensions/webpack-provide-plugin': WebpackProvidePlugin + } + + interface Services { + extensions: Extensions + } +} export default Extensions diff --git a/sources/@roots/bud-extensions/src/extensions/interpolate-html-webpack-plugin/index.ts b/sources/@roots/bud-extensions/src/interpolate-html-webpack-plugin/index.ts similarity index 95% rename from sources/@roots/bud-extensions/src/extensions/interpolate-html-webpack-plugin/index.ts rename to sources/@roots/bud-extensions/src/interpolate-html-webpack-plugin/index.ts index 409137cf54..6a7bda8525 100644 --- a/sources/@roots/bud-extensions/src/extensions/interpolate-html-webpack-plugin/index.ts +++ b/sources/@roots/bud-extensions/src/interpolate-html-webpack-plugin/index.ts @@ -1,9 +1,5 @@ import type {Bud} from '@roots/bud-framework' -import { - default as InterpolateHtmlWebpackPlugin, - type Options, -} from '@roots/bud-extensions/interpolate-html-webpack-plugin/plugin' import {DynamicOption, Extension} from '@roots/bud-framework/extension' import { bind, @@ -12,6 +8,11 @@ import { options, } from '@roots/bud-framework/extension/decorators' +import { + default as InterpolateHtmlWebpackPlugin, + type Options, +} from './plugin/index.js' + /** * Interpolate html webpack plugin configuration */ diff --git a/sources/@roots/bud-extensions/src/extensions/interpolate-html-webpack-plugin/plugin/index.ts b/sources/@roots/bud-extensions/src/interpolate-html-webpack-plugin/plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/interpolate-html-webpack-plugin/plugin/index.ts rename to sources/@roots/bud-extensions/src/interpolate-html-webpack-plugin/plugin/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/mini-css-extract-plugin/index.ts b/sources/@roots/bud-extensions/src/mini-css-extract-plugin/index.ts similarity index 51% rename from sources/@roots/bud-extensions/src/extensions/mini-css-extract-plugin/index.ts rename to sources/@roots/bud-extensions/src/mini-css-extract-plugin/index.ts index 71f08abda4..72b7b97c1a 100644 --- a/sources/@roots/bud-extensions/src/extensions/mini-css-extract-plugin/index.ts +++ b/sources/@roots/bud-extensions/src/mini-css-extract-plugin/index.ts @@ -1,6 +1,9 @@ import type {Options} from '@roots/bud-support/mini-css-extract-plugin' -import {Extension} from '@roots/bud-framework/extension' +import { + Extension, + type StrictPublicExtensionApi, +} from '@roots/bud-framework/extension' import { label, options, @@ -10,16 +13,26 @@ import { import {Plugin} from '@roots/bud-support/mini-css-extract-plugin' import Value from '@roots/bud-support/value' +interface ExtOpts + extends StrictPublicExtensionApi {} + /** * Mini CSS Extract Plugin configuration */ @label(`@roots/bud-extensions/mini-css-extract-plugin`) @plugin(Plugin) @options({ - /** - * css output filename - */ + attributes: undefined, filename: Value.make(({relPath}) => relPath(`css`, `@name.css`)), }) @production -export default class MiniCssExtract extends Extension {} +export default class MiniCssExtract extends Extension { + public declare attributes: ExtOpts[`attributes`] + public declare filename: ExtOpts[`filename`] + + public declare getAttributes: ExtOpts[`getAttributes`] + public declare getFilename: ExtOpts[`getFilename`] + + public declare setAttributes: ExtOpts[`setAttributes`] + public declare setFilename: ExtOpts[`setFilename`] +} diff --git a/sources/@roots/bud-extensions/src/service/index.ts b/sources/@roots/bud-extensions/src/service.ts similarity index 91% rename from sources/@roots/bud-extensions/src/service/index.ts rename to sources/@roots/bud-extensions/src/service.ts index c5de910d2a..18da8b1dd5 100644 --- a/sources/@roots/bud-extensions/src/service/index.ts +++ b/sources/@roots/bud-extensions/src/service.ts @@ -1,6 +1,10 @@ import type {Bud, Modules} from '@roots/bud-framework' +import type {MultiStats, Stats} from '@roots/bud-framework/config' import type {ApplyPlugin} from '@roots/bud-framework/extension' -import type {Extensions as Contract} from '@roots/bud-framework/services' +import type { + Extensions as BudExtensionsService, + LifecycleMethods, +} from '@roots/bud-framework/services/extensions' import {Extension} from '@roots/bud-framework/extension' import {Service} from '@roots/bud-framework/service' @@ -10,19 +14,13 @@ import isUndefined from '@roots/bud-support/lodash/isUndefined' import Container from '@roots/container' import {randomUUID} from 'node:crypto' -import {handleManifestSchemaWarning} from './util/handleManifestSchemaWarning.js' -import {isConstructor} from './util/isConstructor.js' +import {handleManifestSchemaWarning} from './helpers/handleManifestSchemaWarning.js' +import {isConstructor} from './helpers/isConstructor.js' /** * Extensions Service */ -export default class Extensions - extends Service - implements Contract.Service -{ - /** - * Registered extensions - */ +export class Extensions extends Service implements BudExtensionsService { /** * Resolved options */ @@ -32,21 +30,27 @@ export default class Extensions discover: boolean }> - // @ts-ignore + /** + * Registered extensions + */ public repository: Modules /** -- * Modules on which an import attempt was made and failed -- * -- * @remarks -- * This doesn't mean an error, per se. This should only -- * be used in the context of trying to import `optionalDependencies` -- * of a given extension module. -- * -- * @public -- */ + * Modules on which an import attempt was made and failed + * + * @remarks + * This doesn't mean an error, per se. This should only + * be used in the context of trying to import `optionalDependencies` + * of a given extension module. + * + * @public + */ public unresolvable: Set = new Set() + /** + * + * @param bud Class constructor + */ public constructor(bud: () => Bud) { super(bud) this.options = new Container({ @@ -180,7 +184,7 @@ export default class Extensions * {@link Extension.buildBefore} */ @bind - public override async buildAfter?() { + public override async buildAfter(bud: Bud) { await this.runAll(`buildAfter`) } @@ -188,15 +192,23 @@ export default class Extensions * {@link Extension.buildBefore} */ @bind - public override async buildBefore?() { + public override async buildBefore(bud: Bud) { await this.runAll(`buildBefore`) } + @bind + public override async compilerDone([bud, _stats]: [ + Bud, + Stats & MultiStats, + ]) { + await this.runAll(`compilerDone`) + } + /** * `configAfter` callback */ @bind - public override async configAfter?() { + public override async configAfter(bud: Bud) { await this.runAll(`configAfter`) } @@ -384,7 +396,7 @@ export default class Extensions @bind public async run( extension: Extension, - methodName: Contract.LifecycleMethods, + methodName: LifecycleMethods, ): Promise { try { await this.runDependencies(extension, methodName) @@ -402,9 +414,7 @@ export default class Extensions * Execute a extension lifecycle method on all registered extensions */ @bind - public async runAll( - methodName: Contract.LifecycleMethods, - ): Promise { + public async runAll(methodName: LifecycleMethods): Promise { return await Object.values(this.repository).reduce( async (promised, extension) => { await promised @@ -424,7 +434,7 @@ export default class Extensions @bind public async runDependencies( extension: Extension | K, - methodName: Contract.LifecycleMethods, + methodName: LifecycleMethods, ): Promise { const instance: Extension = typeof extension === `string` ? this.get(extension) : extension diff --git a/sources/@roots/bud-extensions/src/extensions/tsconfig-values/index.ts b/sources/@roots/bud-extensions/src/tsconfig-values/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/tsconfig-values/index.ts rename to sources/@roots/bud-extensions/src/tsconfig-values/index.ts diff --git a/sources/@roots/bud-extensions/src/types.ts b/sources/@roots/bud-extensions/src/types.ts deleted file mode 100644 index 488fa6bcc3..0000000000 --- a/sources/@roots/bud-extensions/src/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type {PublicExtensionApi} from '@roots/bud-framework/extension' - -import type BudCDN from './extensions/cdn/index.js' -import type CleanWebpackPlugin from './extensions/clean-webpack-plugin/index.js' -import type CopyWebpackPlugin from './extensions/copy-webpack-plugin/index.js' -import type BudESM from './extensions/esm/index.js' -import type BudFixStyleOnlyEntrypoints from './extensions/fix-style-only-entrypoints/index.js' -import type HtmlWebpackPlugin from './extensions/html-webpack-plugin/index.js' -import type InterpolateHtmlPlugin from './extensions/interpolate-html-webpack-plugin/index.js' -import type MiniCssExtractPlugin from './extensions/mini-css-extract-plugin/index.js' -import type BudTsConfigValues from './extensions/tsconfig-values/index.js' -import type WebpackDefinePlugin from './extensions/webpack-define-plugin/index.js' -import type WebpackHotModuleReplacementPlugin from './extensions/webpack-hot-module-replacement-plugin/index.js' -import type BudWebpackLifecyclePlugin from './extensions/webpack-lifecycle-plugin/index.js' -import type WebpackManifestPlugin from './extensions/webpack-manifest-plugin/index.js' -import type WebpackProvidePlugin from './extensions/webpack-provide-plugin/index.js' - -declare module '@roots/bud-framework' { - interface Bud { - cdn: Modules[`@roots/bud-extensions/cdn`] - esm: Modules[`@roots/bud-extensions/esm`] - manifest: PublicExtensionApi - tsconfig: BudTsConfigValues - } - - interface Modules { - '@roots/bud-extensions/cdn': BudCDN - '@roots/bud-extensions/clean-webpack-plugin': CleanWebpackPlugin - '@roots/bud-extensions/copy-webpack-plugin': CopyWebpackPlugin - '@roots/bud-extensions/esm': BudESM - '@roots/bud-extensions/fix-style-only-entrypoints': BudFixStyleOnlyEntrypoints - '@roots/bud-extensions/html-webpack-plugin': HtmlWebpackPlugin - '@roots/bud-extensions/interpolate-html-webpack-plugin': InterpolateHtmlPlugin - '@roots/bud-extensions/mini-css-extract-plugin': MiniCssExtractPlugin - '@roots/bud-extensions/tsconfig-values': BudTsConfigValues - '@roots/bud-extensions/webpack-define-plugin': WebpackDefinePlugin - '@roots/bud-extensions/webpack-hot-module-replacement-plugin': WebpackHotModuleReplacementPlugin - '@roots/bud-extensions/webpack-lifecycle-plugin': BudWebpackLifecyclePlugin - '@roots/bud-extensions/webpack-manifest-plugin': WebpackManifestPlugin - '@roots/bud-extensions/webpack-provide-plugin': WebpackProvidePlugin - } -} diff --git a/sources/@roots/bud-extensions/src/extensions/webpack-define-plugin/index.ts b/sources/@roots/bud-extensions/src/webpack-define-plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/webpack-define-plugin/index.ts rename to sources/@roots/bud-extensions/src/webpack-define-plugin/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/webpack-hot-module-replacement-plugin/index.ts b/sources/@roots/bud-extensions/src/webpack-hot-module-replacement-plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/webpack-hot-module-replacement-plugin/index.ts rename to sources/@roots/bud-extensions/src/webpack-hot-module-replacement-plugin/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/webpack-lifecycle-plugin/index.ts b/sources/@roots/bud-extensions/src/webpack-lifecycle-plugin/index.ts similarity index 57% rename from sources/@roots/bud-extensions/src/extensions/webpack-lifecycle-plugin/index.ts rename to sources/@roots/bud-extensions/src/webpack-lifecycle-plugin/index.ts index dc4d51a107..7dddb10933 100644 --- a/sources/@roots/bud-extensions/src/extensions/webpack-lifecycle-plugin/index.ts +++ b/sources/@roots/bud-extensions/src/webpack-lifecycle-plugin/index.ts @@ -1,4 +1,8 @@ -import type {Compilation, Compiler} from '@roots/bud-framework/config' +import type { + Compilation, + Compiler, + WebpackError, +} from '@roots/bud-framework/config' import {Extension} from '@roots/bud-framework/extension' import {bind, label} from '@roots/bud-framework/extension/decorators' @@ -8,9 +12,6 @@ import {bind, label} from '@roots/bud-framework/extension/decorators' */ @label(`@roots/bud-extensions/webpack-lifecycle-plugin`) export default class BudWebpackLifecyclePlugin extends Extension { - @bind - public async additionalPass() {} - @bind public async afterCompile(compilation: Compilation) { this.logger.log(`compilation completed:`, compilation.hash) @@ -18,26 +19,19 @@ export default class BudWebpackLifecyclePlugin extends Extension { } @bind - public async afterEmit(compilation: Compilation) { + public async afterEmit(_compilation: Compilation) { this.logger.timeEnd(`emit`) } - @bind - public afterEnvironment() {} - - @bind - public afterPlugins() {} - /** * {@link Extension.apply} + * {@link WebpackPluginInstance.apply} */ @bind public override apply(compiler: Compiler) { ;[ `environment`, - `afterEnvironment`, `afterResolvers`, - `afterPlugins`, `compile`, `failed`, `invalid`, @@ -64,11 +58,7 @@ export default class BudWebpackLifecyclePlugin extends Extension { compiler.hooks[k].tapPromise( this.label, async (...args: any[]) => { - try { - await this[k](...args) - } catch (error) { - this.logger.error(error) - } + await this[k](...args) }, ), ) @@ -93,51 +83,40 @@ export default class BudWebpackLifecyclePlugin extends Extension { ) } - @bind - public async beforeCompile(compilationParams: any) {} - - @bind - public async beforeRun(compiler: Compiler) { - this.logger.log(`beforeRun`, compiler.name) - } - - @bind - public compile(...compilationParams: any[]) {} - @bind public async emit(compilation: Compilation) { this.logger.time(`emit`) } @bind - public environment() {} - - @bind - public failed(error: Error) { - this.logger.error(`compilation failed`) - - if ( - error.message.includes( - `Module not found: Error: Can't resolve 'index' in '${this.app.path( - `@src`, - )}'`, - ) && - !this.app.hooks.filter(`build.entry`) - ) { - this.logger.error( - `No entrypoints specified and no module found at @src/index. Either create a file at ${this.app.relPath( - `@src`, - `index.js`, - )} or use the bud.entry method to specify an entrypoint.`, - ) + public failed(error: Error & Partial & {error?: Error}) { + const {message, name} = error + + const moduleNotFoundError = name === `ModuleNotFoundError` + + error.name = name + error.message = message + + if (moduleNotFoundError) { + if ( + error.message.includes( + `Error: Can't resolve 'index' in '${this.app.path(`@src`)}'`, + ) + ) { + error.message = error.message.replace( + `Module not found: Error: Can't resolve 'index' in '${this.app.path( + `@src`, + )}'`, + `Either create a file at ${this.app.relPath( + `@src/index.js`, + )} or specify the path to your entry file with bud.entry`, + ) + return error + } } - } - - @bind - public initialize() {} - @bind - public invalid() {} + return error + } @bind public async run(compiler: Compiler) { @@ -146,10 +125,7 @@ export default class BudWebpackLifecyclePlugin extends Extension { @bind public shouldEmit() { - // @ts-ignore - const emitCheck = this.app.context.dry !== true - emitCheck ? this.logger.success(`emit`) : this.logger.log(`dry run`) - return emitCheck + return this.app.context.dry !== true } @bind diff --git a/sources/@roots/bud-extensions/src/extensions/webpack-manifest-plugin/index.ts b/sources/@roots/bud-extensions/src/webpack-manifest-plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/webpack-manifest-plugin/index.ts rename to sources/@roots/bud-extensions/src/webpack-manifest-plugin/index.ts diff --git a/sources/@roots/bud-extensions/src/extensions/webpack-provide-plugin/index.ts b/sources/@roots/bud-extensions/src/webpack-provide-plugin/index.ts similarity index 100% rename from sources/@roots/bud-extensions/src/extensions/webpack-provide-plugin/index.ts rename to sources/@roots/bud-extensions/src/webpack-provide-plugin/index.ts diff --git a/sources/@roots/bud-extensions/test/extensions/cdn.test.ts b/sources/@roots/bud-extensions/test/extensions/cdn.test.ts index 8f04997984..2d68f602d1 100644 --- a/sources/@roots/bud-extensions/test/extensions/cdn.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/cdn.test.ts @@ -2,7 +2,7 @@ import {factory} from '@repo/test-kit' import {Bud} from '@roots/bud-framework' import {beforeEach, describe, expect, it, vi} from 'vitest' -import source from '../../src/extensions/cdn/index.js' +import source from '../../src/cdn/index.js' describe(`@roots/bud-extensions/cdn`, () => { let bud: Bud diff --git a/sources/@roots/bud-extensions/test/extensions/clean-webpack-plugin.test.ts b/sources/@roots/bud-extensions/test/extensions/clean-webpack-plugin.test.ts index 777ba42ea4..ffb4ce169d 100644 --- a/sources/@roots/bud-extensions/test/extensions/clean-webpack-plugin.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/clean-webpack-plugin.test.ts @@ -1,7 +1,7 @@ import {factory} from '@repo/test-kit' import {describe, expect, it} from 'vitest' -import extensionConstructor from '../../src/extensions/clean-webpack-plugin/index.js' +import extensionConstructor from '../../src/clean-webpack-plugin/index.js' describe(`@roots/bud-extensions/clean-webpack-plugin`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/copy-webpack-plugin.test.ts b/sources/@roots/bud-extensions/test/extensions/copy-webpack-plugin.test.ts index 45f14bac78..3928f8fa14 100644 --- a/sources/@roots/bud-extensions/test/extensions/copy-webpack-plugin.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/copy-webpack-plugin.test.ts @@ -1,6 +1,6 @@ import {describe, expect, it, test} from 'vitest' -import extensionConstructor from '../../src/extensions/copy-webpack-plugin/index.js' +import extensionConstructor from '../../src/copy-webpack-plugin/index.js' describe(`@roots/bud-extensions/copy-webpack-plugin`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/esm.test.ts b/sources/@roots/bud-extensions/test/extensions/esm.test.ts index 5f964a7bb5..62f6ca7079 100644 --- a/sources/@roots/bud-extensions/test/extensions/esm.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/esm.test.ts @@ -3,7 +3,7 @@ import '../../src/index.js' import {factory} from '@repo/test-kit' import {describe, expect, it, vi} from 'vitest' -import extensionConstructor from '../../src/extensions/esm/index.js' +import extensionConstructor from '../../src/esm/index.js' describe(`@roots/bud-extensions/esm`, () => { it(`is constructable`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/fix-style-only-entrypoints.test.ts b/sources/@roots/bud-extensions/test/extensions/fix-style-only-entrypoints.test.ts index 8c4b5cf277..435746baf3 100644 --- a/sources/@roots/bud-extensions/test/extensions/fix-style-only-entrypoints.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/fix-style-only-entrypoints.test.ts @@ -1,6 +1,6 @@ import {describe, expect, it, test} from 'vitest' -import extensionConstructor from '../../src/extensions/fix-style-only-entrypoints/index.js' +import extensionConstructor from '../../src/fix-style-only-entrypoints/index.js' describe(`bud-esm`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/index.test.ts b/sources/@roots/bud-extensions/test/extensions/index.test.ts index 19c581ca70..90f311a0c4 100644 --- a/sources/@roots/bud-extensions/test/extensions/index.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/index.test.ts @@ -1,22 +1,34 @@ import {describe, expect, it} from 'vitest' -import * as Extensions from '../../src/extensions/index.js' +import CDN from '../../src/cdn/index.js' +import ESM from '../../src/esm/index.js' +import FixStyleOnlyEntrypoints from '../../src/fix-style-only-entrypoints/index.js' +import Clean from '../../src/clean-webpack-plugin/index.js' +import Copy from '../../src/copy-webpack-plugin/index.js' +import Html from '../../src/html-webpack-plugin/index.js' +import Interpolate from '../../src/interpolate-html-webpack-plugin/index.js' +import MiniCssExtract from '../../src/mini-css-extract-plugin/index.js' +import TSConfigValues from '../../src/tsconfig-values/index.js' + +import Define from '../../src/webpack-define-plugin/index.js' +import HotModuleReplacement from '../../src/webpack-hot-module-replacement-plugin/index.js' +import Manifest from '../../src/webpack-manifest-plugin/index.js' +import Provide from '../../src/webpack-provide-plugin/index.js' describe(`@roots/bud-extensions`, () => { it(`exports extensions`, async () => { - expect(Extensions.BudCDN).toBeInstanceOf(Function) - expect(Extensions.BudESM).toBeInstanceOf(Function) - expect(Extensions.BudFixStyleOnlyEntrypoints).toBeInstanceOf(Function) - expect(Extensions.CleanWebpackPlugin).toBeInstanceOf(Function) - expect(Extensions.CopyWebpackPlugin).toBeInstanceOf(Function) - expect(Extensions.HtmlWebpackPlugin).toBeInstanceOf(Function) - expect(Extensions.InterpolateHtmlPlugin).toBeInstanceOf(Function) - expect(Extensions.MiniCssExtractPlugin).toBeInstanceOf(Function) - expect(Extensions.WebpackDefinePlugin).toBeInstanceOf(Function) - expect(Extensions.WebpackHotModuleReplacementPlugin).toBeInstanceOf( - Function, - ) - expect(Extensions.WebpackManifestPlugin).toBeInstanceOf(Function) - expect(Extensions.WebpackProvidePlugin).toBeInstanceOf(Function) + expect(CDN).toBeInstanceOf(Function) + expect(ESM).toBeInstanceOf(Function) + expect(FixStyleOnlyEntrypoints).toBeInstanceOf(Function) + expect(Clean).toBeInstanceOf(Function) + expect(Copy).toBeInstanceOf(Function) + expect(Html).toBeInstanceOf(Function) + expect(Interpolate).toBeInstanceOf(Function) + expect(MiniCssExtract).toBeInstanceOf(Function) + expect(Define).toBeInstanceOf(Function) + expect(HotModuleReplacement).toBeInstanceOf(Function) + expect(Manifest).toBeInstanceOf(Function) + expect(Provide).toBeInstanceOf(Function) + expect(TSConfigValues).toBeInstanceOf(Function) }) }) diff --git a/sources/@roots/bud-extensions/test/extensions/mini-css-extract-plugin.test.ts b/sources/@roots/bud-extensions/test/extensions/mini-css-extract-plugin.test.ts index eb281344d7..06ff133741 100644 --- a/sources/@roots/bud-extensions/test/extensions/mini-css-extract-plugin.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/mini-css-extract-plugin.test.ts @@ -1,6 +1,6 @@ import {describe, expect, it, test} from 'vitest' -import extensionConstructor from '../../src/extensions/mini-css-extract-plugin/index.js' +import extensionConstructor from '../../src/mini-css-extract-plugin/index.js' describe(`mini-css-extract-plugin`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/webpack-define-plugin.test.ts b/sources/@roots/bud-extensions/test/extensions/webpack-define-plugin.test.ts index 899ad2031b..d907649afa 100644 --- a/sources/@roots/bud-extensions/test/extensions/webpack-define-plugin.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/webpack-define-plugin.test.ts @@ -1,6 +1,6 @@ import {describe, expect, it, test} from 'vitest' -import defineExtension from '../../src/extensions/webpack-define-plugin/index.js' +import defineExtension from '../../src/webpack-define-plugin/index.js' describe(`@roots/bud-extensions/webpack-define-plugin`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/webpack-hot-module-replacement.test.ts b/sources/@roots/bud-extensions/test/extensions/webpack-hot-module-replacement.test.ts index 32b9c28a06..1b8008bec7 100644 --- a/sources/@roots/bud-extensions/test/extensions/webpack-hot-module-replacement.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/webpack-hot-module-replacement.test.ts @@ -3,7 +3,7 @@ import {Extension} from '@roots/bud-framework/extension' import webpack from '@roots/bud-support/webpack' import {describe, expect, it, test, vi} from 'vitest' -import HmrExtension from '../../src/extensions/webpack-hot-module-replacement-plugin/index.js' +import HmrExtension from '../../src/webpack-hot-module-replacement-plugin/index.js' describe(`webpack-hot-module-replacement-plugin`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/webpack-manifest-plugin.test.ts b/sources/@roots/bud-extensions/test/extensions/webpack-manifest-plugin.test.ts index bc5d641a88..b54413c7f5 100644 --- a/sources/@roots/bud-extensions/test/extensions/webpack-manifest-plugin.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/webpack-manifest-plugin.test.ts @@ -1,6 +1,6 @@ import {describe, expect, it, test} from 'vitest' -import manifestExtension from '../../src/extensions/webpack-manifest-plugin/index.js' +import manifestExtension from '../../src/webpack-manifest-plugin/index.js' describe(`webpack-manifest-plugin`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/extensions/webpack-provide-plugin.test.ts b/sources/@roots/bud-extensions/test/extensions/webpack-provide-plugin.test.ts index 9c0adc6db7..a14280ead4 100644 --- a/sources/@roots/bud-extensions/test/extensions/webpack-provide-plugin.test.ts +++ b/sources/@roots/bud-extensions/test/extensions/webpack-provide-plugin.test.ts @@ -1,6 +1,6 @@ import {describe, expect, it, test} from 'vitest' -import provideExtension from '../../src/extensions/webpack-provide-plugin/index.js' +import provideExtension from '../../src/webpack-provide-plugin/index.js' describe(`@roots/bud-extensions/webpack-provide-plugin`, () => { it(`is an instance of Extension`, () => { diff --git a/sources/@roots/bud-extensions/test/helpers/isConstructor.test.ts b/sources/@roots/bud-extensions/test/helpers/isConstructor.test.ts new file mode 100644 index 0000000000..b580c61e76 --- /dev/null +++ b/sources/@roots/bud-extensions/test/helpers/isConstructor.test.ts @@ -0,0 +1,23 @@ +import {describe, expect, it} from 'vitest' +import {isConstructor} from '../../src/helpers/isConstructor.js' + +describe('isConstructor', () => { + it('should return true for a constructor function', () => { + class TestClass {} + expect(isConstructor(TestClass)).toBe(true) + }) + + it('should return false for a non-constructor function', () => { + const testFunction = () => {} + expect(isConstructor(testFunction)).toBe(false) + }) + + it('should return false for a non-function object', () => { + const testObject = {} + expect(isConstructor(testObject)).toBe(false) + }) + + it('should return false for a primitive value', () => { + expect(isConstructor('test')).toBe(false) + }) +}) diff --git a/sources/@roots/bud-extensions/test/service/__snapshots__/index.test.ts.snap b/sources/@roots/bud-extensions/test/service/__snapshots__/index.test.ts.snap index 489cbc3267..eb2afbd889 100644 --- a/sources/@roots/bud-extensions/test/service/__snapshots__/index.test.ts.snap +++ b/sources/@roots/bud-extensions/test/service/__snapshots__/index.test.ts.snap @@ -9,6 +9,7 @@ exports[`@roots/bud-extensions > bud.extensions.repository options should match "@roots/bud-extensions/esm", "@roots/bud-extensions/fix-style-only-entrypoints", "@roots/bud-extensions/html-webpack-plugin", + "@roots/bud-extensions/import-map", "@roots/bud-extensions/interpolate-html-webpack-plugin", "@roots/bud-extensions/mini-css-extract-plugin", "@roots/bud-extensions/tsconfig-values", diff --git a/sources/@roots/bud-extensions/test/service/index.test.ts b/sources/@roots/bud-extensions/test/service/index.test.ts index 5279d2c9b8..1faf23804f 100644 --- a/sources/@roots/bud-extensions/test/service/index.test.ts +++ b/sources/@roots/bud-extensions/test/service/index.test.ts @@ -3,7 +3,7 @@ import type {Modules} from '@roots/bud-framework' import type {ApplyPlugin} from '@roots/bud-framework/extension' import {beforeEach, describe, expect, it, vi} from 'vitest' -import Extensions from '../../src/service/index.js' +import {Extensions} from '../../src/service.js' describe(`@roots/bud-extensions`, () => { let bud: Bud diff --git a/sources/@roots/bud-extensions/tsconfig.json b/sources/@roots/bud-extensions/tsconfig.json index 4440ef9237..434d46fdc4 100644 --- a/sources/@roots/bud-extensions/tsconfig.json +++ b/sources/@roots/bud-extensions/tsconfig.json @@ -2,13 +2,18 @@ "extends": "../../../config/tsconfig.json", "compilerOptions": { "rootDir": "./src", - "outDir": "./lib" + "outDir": "./lib", + "paths": { + "@roots/bud-extensions": ["./src/index.ts"], + "@roots/bud-extensions/service": ["./src/service.ts"], + "@roots/bud-extensions/*": ["./src/*/index.ts"] + }, + "types": ["node", "@roots/bud-framework", "@roots/bud-minify"] }, - "include": ["src"], + "include": ["./src"], + "exclude": ["**/*.test.ts", "./test"], "references": [ {"path": "../bud-framework/tsconfig.json"}, - {"path": "../bud-support/tsconfig.json"}, {"path": "../bud-minify/tsconfig.json"}, - {"path": "../container/tsconfig.json"}, ] } diff --git a/sources/@roots/bud-framework/package.json b/sources/@roots/bud-framework/package.json index 0d2ffc8467..53cf5b6f60 100644 --- a/sources/@roots/bud-framework/package.json +++ b/sources/@roots/bud-framework/package.json @@ -53,25 +53,26 @@ "exports": { ".": "./lib/index.js", "./bud": "./lib/bud.js", - "./extension": "./lib/extension/index.js", - "./extension/decorators": "./lib/extension/decorators/index.js", + "./context": "./lib/context.js", "./extension/decorators/*": "./lib/extension/decorators/*.js", + "./extension/decorators": "./lib/extension/decorators/index.js", + "./extension": "./lib/extension/index.js", "./methods/*": "./lib/methods/*.js", "./methods": "./lib/methods/index.js", "./module": "./lib/module.js", - "./options": "./lib/types/options/index.js", - "./config": "./lib/types/config/index.js", - "./options/*": "./lib/types/options/*.js", - "./registry": "./lib/types/registry/index.js", - "./registry/*": "./lib/types/registry/*.js", + "./options": "./lib/options/index.js", + "./config": "./lib/config/index.js", + "./options/*": "./lib/options/*.js", + "./registry": "./lib/registry/index.js", + "./registry/*": "./lib/registry/*.js", "./service": "./lib/service.js", - "./services": "./lib/types/services/index.js", + "./services": "./lib/services/index.js", "./services/api": "./lib/services/api.js", - "./services/console": "./lib/services/console.js", - "./services/fs": "./lib/services/fs.js", - "./services/notifier": "./lib/services/notifier/index.js", - "./services/build/*": "./lib/types/services/build/*.js", - "./services/*": "./lib/types/services/*/index.js", + "./console": "./lib/console.js", + "./fs": "./lib/fs.js", + "./notifier": "./lib/notifier/index.js", + "./services/build/*": "./lib/services/build/*.js", + "./services/*": "./lib/services/*/index.js", "./value": "./lib/value.js", "./*": "./lib/*/index.js" }, @@ -87,13 +88,16 @@ "./lib/bud/index.d.ts" ], "config": [ - "./lib/types/config/index.d.ts" + "./lib/config/index.d.ts" + ], + "context": [ + "./lib/context.d.ts" ], "console": [ - "./lib/services/console.d.ts" + "./lib/console.d.ts" ], "fs": [ - "./lib/services/fs.d.ts" + "./lib/fs.d.ts" ], "extension": [ "./lib/extension/index.d.ts" @@ -104,9 +108,6 @@ "extension/decorators/*": [ "./lib/extension/decorators/*.d.ts" ], - "logger": [ - "./lib/logger/index.d.ts" - ], "methods": [ "./lib/methods/index.d.ts" ], diff --git a/sources/@roots/bud-framework/src/lifecycle/bootstrap.ts b/sources/@roots/bud-framework/src/bootstrap.ts similarity index 88% rename from sources/@roots/bud-framework/src/lifecycle/bootstrap.ts rename to sources/@roots/bud-framework/src/bootstrap.ts index de5da77d97..d922ebc653 100644 --- a/sources/@roots/bud-framework/src/lifecycle/bootstrap.ts +++ b/sources/@roots/bud-framework/src/bootstrap.ts @@ -1,18 +1,16 @@ +import type {Bud, Registry, Service} from '@roots/bud-framework' + import chalk from '@roots/bud-support/chalk' import {BudError} from '@roots/bud-support/errors' import figures from '@roots/bud-support/figures' import camelCase from '@roots/bud-support/lodash/camelCase' -import isFunction from '@roots/bud-support/lodash/isFunction' import isString from '@roots/bud-support/lodash/isString' import logger from '@roots/bud-support/logger' -import type {Bud} from '../index.js' -import type {Service} from '../service.js' -import type * as Registry from '../types/registry/index.js' - -import methods from '../methods/index.js' -import {Module} from '../module.js' -import FS from '../services/fs.js' +import {FS} from './fs.js' +import methods from './methods/index.js' +import {Module} from './module.js' +import {Notifier} from './notifier.js' /** * Define the list of lifecycle events that are handled by the system @@ -27,7 +25,7 @@ export const lifecycleHookHandles: Partial< `compiler.before`, `build.before`, `build.after`, - `compiler.after`, + `compiler.done`, ] /** @@ -41,7 +39,7 @@ export const lifecycleMethods: Partial> = [ `compilerBefore`, `buildBefore`, `buildAfter`, - `compilerAfter`, + `compilerDone`, ] /** @@ -66,8 +64,8 @@ export const LIFECYCLE_EVENT_MAP: Partial< > = { [`build.after`]: `buildAfter`, [`build.before`]: `buildBefore`, - [`compiler.after`]: `compilerAfter`, [`compiler.before`]: `compilerBefore`, + [`compiler.done`]: `compilerDone`, [`config.after`]: `configAfter`, boot: `boot`, bootstrap: `bootstrap`, @@ -104,7 +102,7 @@ const instantiateServices = (app: Bud) => async (signifier: string): Promise => { const Service = await app.module.import(signifier) - const service: Service = new Service(() => app) + const service = new Service(() => app) const label = service.label ?? service.constructor?.name @@ -157,6 +155,9 @@ export const bootstrap = async function (this: Bud) { throw BudError.normalize(error) }) + this.notifier = new Notifier(() => this) + await this.notifier.make(this) + this.hooks .fromMap({ 'pattern.css': /(?!.*\.module)\.css$/, @@ -169,7 +170,7 @@ export const bootstrap = async function (this: Bud) { 'pattern.json': /\.json$/, 'pattern.json5': /\.json5$/, 'pattern.md': /\.md$/, - 'pattern.modules': /(node_modules|bower_components)/, + 'pattern.modules': /(node_modules|bower_components|vendor)/, 'pattern.sass': /(?!.*\.module)\.(scss|sass)$/, 'pattern.sassModule': /\.module\.(scss|sass)$/, 'pattern.svg': /\.svg$/, @@ -215,13 +216,10 @@ export const bootstrap = async function (this: Bud) { logger.error(`service not found: ${label}`, this.services) } - if (!isFunction(service[callbackName])) return - this.hooks.action( eventHandle, service[callbackName].bind(service), ) - logger.info(`${label}.${callbackName}`, `bound to`, eventHandle) }), ) @@ -238,18 +236,4 @@ export const bootstrap = async function (this: Bud) { }, Promise.resolve(), ) - - /** - * Checksums - */ - this.after(async bud => { - await bud.fs - .write(bud.module.cacheLocation, { - resolutions: bud.module.resolved, - version: bud.context.bud.version, - }) - .catch(error => { - throw new Error(error) - }) - }) } diff --git a/sources/@roots/bud-framework/src/bud.ts b/sources/@roots/bud-framework/src/bud.ts index 442bb8e437..0d02cda4b0 100644 --- a/sources/@roots/bud-framework/src/bud.ts +++ b/sources/@roots/bud-framework/src/bud.ts @@ -1,3 +1,19 @@ +import type { + Api, + Build, + Cache, + Compiler, + Context, + Dashboard, + Env, + Extensions, + Hooks, + Project, + Server, + Service, +} from '@roots/bud-framework' +import type {Console} from '@roots/bud-framework/console' + import {bind} from '@roots/bud-support/decorators/bind' import {InputError} from '@roots/bud-support/errors' import isFunction from '@roots/bud-support/lodash/isFunction' @@ -5,15 +21,12 @@ import isString from '@roots/bud-support/lodash/isString' import isUndefined from '@roots/bud-support/lodash/isUndefined' import logger from '@roots/bud-support/logger' +import type {FS} from './fs.js' import type methods from './methods/index.js' import type {Module} from './module.js' -import type ConsoleBuffer from './services/console.js' -import type FS from './services/fs.js' -import type * as Options from './types/options/index.js' -import type Hooks from './types/services/hooks/index.js' -import type * as Services from './types/services/index.js' +import type {Notifier} from './notifier.js' -import {bootstrap} from './lifecycle/bootstrap.js' +import {bootstrap} from './bootstrap.js' /** * Bud core class @@ -21,11 +34,11 @@ import {bootstrap} from './lifecycle/bootstrap.js' export class Bud { public declare after: typeof methods.after - public declare api: Services.Api + public declare api: Service & Api - public declare build: Services.Build.Service + public declare build: Service & Build - public declare cache: Services.Cache.Service + public declare cache: Service & Cache /** * {@link Bud} instances @@ -34,22 +47,22 @@ export class Bud { public declare close: typeof methods.close - public declare compiler: Services.Compiler.Service + public declare compiler: Service & Compiler - public declare consoleBuffer: ConsoleBuffer + public declare console: Console public declare container: typeof methods.container /** * Context */ - public declare context: Options.Context + public declare context: Context - public declare dashboard: Services.Dashboard.Service + public declare dashboard: Service & Dashboard - public declare env: Services.Env + public declare env: Service & Env - public declare extensions: Services.Extensions.Service + public declare extensions: Service & Extensions public declare fs: FS @@ -59,10 +72,10 @@ export class Bud { public declare globSync: typeof methods.globSync - public declare hooks: Hooks + public declare hooks: Service & Hooks /** - * Implementation + * {@link Bud} Implementation */ public declare implementation: new () => Bud @@ -76,7 +89,7 @@ export class Bud { public declare module: Module - public declare notifier: Services.Notifier + public declare notifier: Notifier public declare path: typeof methods.path @@ -84,7 +97,7 @@ export class Bud { public declare processConfigs: typeof methods.processConfigs - public declare project: Services.Project.Service + public declare project: Project public declare publicPath: typeof methods.publicPath @@ -96,9 +109,9 @@ export class Bud { public declare sequenceSync: typeof methods.sequenceSync - public declare server: Services.Server.Service + public declare server: Server & Service - public declare services: Array<`${keyof Services.Registry & string}`> + public declare services: Array public declare setPath: typeof methods.setPath @@ -216,7 +229,7 @@ export class Bud { } @bind - public async lifecycle(context: Options.Context): Promise { + public async lifecycle(context: Context): Promise { Object.assign(this, {}, {context: {...context}}) await bootstrap.bind(this)() return this @@ -236,7 +249,7 @@ export class Bud { */ @bind public async make( - request: Partial | string, + request: Partial | string, setupFn?: (app: Bud) => Promise, ) { if (!this.isRoot) { @@ -245,7 +258,7 @@ export class Bud { ) } - const context: Options.Context = isString(request) + const context: Context = isString(request) ? {...this.context, label: request, root: this} : {...this.context, ...request, root: this} diff --git a/sources/@roots/bud-framework/src/types/config/entry.ts b/sources/@roots/bud-framework/src/config/entry.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/config/entry.ts rename to sources/@roots/bud-framework/src/config/entry.ts diff --git a/sources/@roots/bud-framework/src/types/config/index.ts b/sources/@roots/bud-framework/src/config/index.ts similarity index 98% rename from sources/@roots/bud-framework/src/types/config/index.ts rename to sources/@roots/bud-framework/src/config/index.ts index 78975ec1b7..a42f670feb 100644 --- a/sources/@roots/bud-framework/src/types/config/index.ts +++ b/sources/@roots/bud-framework/src/config/index.ts @@ -14,6 +14,7 @@ export type { MultiStats, ProvidePlugin, RuleSetRule, + Stats, StatsAsset, StatsChunkGroup, StatsCompilation, diff --git a/sources/@roots/bud-framework/src/types/config/optimization/index.ts b/sources/@roots/bud-framework/src/config/optimization/index.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/config/optimization/index.ts rename to sources/@roots/bud-framework/src/config/optimization/index.ts diff --git a/sources/@roots/bud-framework/src/types/config/optimization/runtimeChunk.ts b/sources/@roots/bud-framework/src/config/optimization/runtimeChunk.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/config/optimization/runtimeChunk.ts rename to sources/@roots/bud-framework/src/config/optimization/runtimeChunk.ts diff --git a/sources/@roots/bud-framework/src/types/config/optimization/splitChunks.ts b/sources/@roots/bud-framework/src/config/optimization/splitChunks.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/config/optimization/splitChunks.ts rename to sources/@roots/bud-framework/src/config/optimization/splitChunks.ts diff --git a/sources/@roots/bud-framework/src/configuration/configuration.ts b/sources/@roots/bud-framework/src/configuration/configuration.ts index a5107aec03..18a67d0bfe 100644 --- a/sources/@roots/bud-framework/src/configuration/configuration.ts +++ b/sources/@roots/bud-framework/src/configuration/configuration.ts @@ -1,3 +1,6 @@ +import type {Bud} from '@roots/bud-framework' +import type {File} from '@roots/bud-framework/context' + import {bind} from '@roots/bud-support/decorators/bind' import {BudError} from '@roots/bud-support/errors' import get from '@roots/bud-support/lodash/get' @@ -6,9 +9,6 @@ import isFunction from '@roots/bud-support/lodash/isFunction' import isObject from '@roots/bud-support/lodash/isObject' import isString from '@roots/bud-support/lodash/isString' -import type {Bud} from '../index.js' -import type {File} from '../types/options/context.js' - /** * User config parser */ diff --git a/sources/@roots/bud-framework/src/services/console.ts b/sources/@roots/bud-framework/src/console.ts similarity index 91% rename from sources/@roots/bud-framework/src/services/console.ts rename to sources/@roots/bud-framework/src/console.ts index b3c0509447..d648a13d57 100644 --- a/sources/@roots/bud-framework/src/services/console.ts +++ b/sources/@roots/bud-framework/src/console.ts @@ -1,10 +1,7 @@ +import {type Bud, Service} from '@roots/bud-framework' import {bind} from '@roots/bud-support/decorators/bind' import patchConsole from '@roots/bud-support/patch-console' -import type {Bud} from '../index.js' - -import {Service} from '../service.js' - /** * Received messages */ @@ -14,13 +11,13 @@ type MessagesCache = Array<{ }> /** - * ConsoleBuffer {@link Service} + * Console stream interceptor * * @remarks * Intercepts console function calls and emits them using the bud logger. * Deduplicates and trims console output. */ -export default class ConsoleBuffer extends Service { +export class Console extends Service { /** * Received messages */ diff --git a/sources/@roots/bud-framework/src/types/options/context.ts b/sources/@roots/bud-framework/src/context.ts similarity index 99% rename from sources/@roots/bud-framework/src/types/options/context.ts rename to sources/@roots/bud-framework/src/context.ts index f942eba356..244b6db839 100644 --- a/sources/@roots/bud-framework/src/types/options/context.ts +++ b/sources/@roots/bud-framework/src/context.ts @@ -1,4 +1,4 @@ -import type {Bud} from '../../index.js' +import type {Bud} from '@roots/bud-framework' /** * Bud context object diff --git a/sources/@roots/bud-framework/src/extension/decorators/dependsOn.ts b/sources/@roots/bud-framework/src/extension/decorators/dependsOn.ts index a5456fa2a6..7a0349b967 100644 --- a/sources/@roots/bud-framework/src/extension/decorators/dependsOn.ts +++ b/sources/@roots/bud-framework/src/extension/decorators/dependsOn.ts @@ -1,4 +1,4 @@ -import type {Modules} from '../../types/registry/modules.js' +import type {Modules} from '@roots/bud-framework/registry/modules' export const dependsOn = (dependsOn: Array<`${keyof Modules & string}`>) => diff --git a/sources/@roots/bud-framework/src/extension/decorators/dependsOnOptional.ts b/sources/@roots/bud-framework/src/extension/decorators/dependsOnOptional.ts index 94d4750e62..0c225a00bf 100644 --- a/sources/@roots/bud-framework/src/extension/decorators/dependsOnOptional.ts +++ b/sources/@roots/bud-framework/src/extension/decorators/dependsOnOptional.ts @@ -1,4 +1,4 @@ -import type {Modules} from '../../types/registry/modules.js' +import type {Modules} from '@roots/bud-framework' export const dependsOnOptional = (dependsOnOptional: Array<`${keyof Modules & string}`>) => diff --git a/sources/@roots/bud-framework/src/extension/decorators/label.ts b/sources/@roots/bud-framework/src/extension/decorators/label.ts index 97823fbbd6..f87d0543fa 100644 --- a/sources/@roots/bud-framework/src/extension/decorators/label.ts +++ b/sources/@roots/bud-framework/src/extension/decorators/label.ts @@ -1,4 +1,4 @@ -import type {Modules} from '../../types/registry/modules.js' +import type {Modules} from '@roots/bud-framework' /** * A decorator that adds a `label` property to the class. diff --git a/sources/@roots/bud-framework/src/extension/index.ts b/sources/@roots/bud-framework/src/extension/index.ts index 1dfbf66a56..a61bafce01 100644 --- a/sources/@roots/bud-framework/src/extension/index.ts +++ b/sources/@roots/bud-framework/src/extension/index.ts @@ -1,3 +1,6 @@ +import type {Bud} from '@roots/bud-framework' +import type {Modules} from '@roots/bud-framework' +import type {Compiler} from '@roots/bud-framework/config' import type {ApplyPluginConstructor} from '@roots/bud-framework/extension/decorators/plugin' import {bind} from '@roots/bud-support/decorators/bind' @@ -10,10 +13,6 @@ import set from '@roots/bud-support/lodash/set' import logger from '@roots/bud-support/logger' import DynamicOption from '@roots/bud-support/value' -import type {Bud} from '../index.js' -import type {Modules} from '../index.js' -import type {Compiler} from '../types/config/index.js' - export type Options> = { [K in keyof T as `${K & string}`]?: T[K] } @@ -357,7 +356,7 @@ export class Extension< /** * {@link ApplyPlugin.apply} */ - public apply?(compiler: Compiler): unknown + public apply?(compiler: Compiler): unknown | void /** * `boot` callback @@ -365,22 +364,22 @@ export class Extension< * @param options - Extension options * @param app - Bud instance */ - public async boot(app: Bud): Promise {} + public async boot(app: Bud) {} /** * `buildAfter` callback */ - public async buildAfter?(app: Bud): Promise + public async buildAfter?(app: Bud): Promise /** * `buildBefore` callback */ - public async buildBefore?(app: Bud): Promise + public async buildBefore?(app: Bud): Promise /** * `configAfter` callback */ - public async configAfter(app: Bud): Promise {} + public async configAfter(app: Bud) {} /** * Disable extension diff --git a/sources/@roots/bud-framework/src/services/fs.ts b/sources/@roots/bud-framework/src/fs.ts similarity index 85% rename from sources/@roots/bud-framework/src/services/fs.ts rename to sources/@roots/bud-framework/src/fs.ts index cd73af9690..7c0d37c8d0 100644 --- a/sources/@roots/bud-framework/src/services/fs.ts +++ b/sources/@roots/bud-framework/src/fs.ts @@ -1,3 +1,7 @@ +import type {Bud} from '@roots/bud-framework' +import type {MultiStats, Stats} from '@roots/bud-framework/config' +import type {Contract} from '@roots/bud-framework/service' + import {bind} from '@roots/bud-support/decorators/bind' import {Filesystem, json, yml} from '@roots/bud-support/filesystem' import globby from '@roots/bud-support/globby' @@ -6,13 +10,10 @@ import logger from '@roots/bud-support/logger' import {S3} from '@roots/filesystem' import {join} from 'node:path' -import type {Bud} from '../index.js' -import type {Contract} from '../service.js' - /** * {@link Filesystem} service */ -export default class FS extends Filesystem implements Contract { +export class FS extends Filesystem implements Contract { /** * JSON * @@ -63,6 +64,37 @@ export default class FS extends Filesystem implements Contract { * Fulfills {@link Contract.bootstrap} */ public async bootstrap() {} + /** + * Fulfills {@link Contract.buildAfter} + */ + public async buildAfter() {} + /** + * Fulfills {@link Contract.buildBefore} + */ + public async buildBefore() {} + /** + * Fulfills {@link Contract.compilerBefore} + */ + public async compilerBefore() {} + /** + * Fulfills {@link Contract.compilerDone} + */ + public async compilerDone([bud, _stats]: [Bud, Stats & MultiStats]) {} + /** + * Fulfills {@link Contract.configAfter} + */ + public async configAfter() {} + /** + * Fulfills {@link Contract.configBefore} + */ + public async configBefore() {} + + /** + * {@link Contract.done} + */ + public done() { + return this.app + } /** * {@link Contract.logger} @@ -72,9 +104,9 @@ export default class FS extends Filesystem implements Contract { } /** - * Fulfills {@link Contract.bootstrap} + * Fulfills {@link Contract.register} */ - public async register(bud: Bud) {} + public async register() {} /** * Set bucket @@ -167,6 +199,7 @@ export default class FS extends Filesystem implements Contract { destination ? join(destination, path) : path this.app.after(async () => { + this.app.dashboard.render(`Deploying files to S3`) await globby(files, {cwd: source}).then(async files => { const descriptions = await Promise.all( files.map(async file => { diff --git a/sources/@roots/bud-framework/src/index.ts b/sources/@roots/bud-framework/src/index.ts index 6b4bce47eb..6499757919 100644 --- a/sources/@roots/bud-framework/src/index.ts +++ b/sources/@roots/bud-framework/src/index.ts @@ -10,18 +10,27 @@ export * from './bud.js' export * from './service.js' +export type {Context} from '@roots/bud-framework/context' +export type * from '@roots/bud-framework/config' +export type { + Api, + Build, + Cache, + Compiler, + Dashboard, + Env, + Extensions, + Hooks, + Project, + Server, +} from '@roots/bud-framework/services' -export type * as Config from './types/config/index.js' -export type * from './types/options/index.js' -export * from './types/services/index.js' -export * from './types/services/hooks/index.js' - -export type {Items} from './types/registry/items.js' -export type {Loaders} from './types/registry/loaders.js' -export type {Rules} from './types/registry/rules.js' -export type {Item} from './types/services/build/item.js' -export type {Loader} from './types/services/build/loader.js' -export type {Rule} from './types/services/build/rule.js' -export type * as Registry from './types/registry/index.js' -export type {Modules} from './types/registry/modules.js' -export type {Locations} from './types/registry/locations.js' +export type {Items} from './registry/items.js' +export type {Loaders} from './registry/loaders.js' +export type {Rules} from './registry/rules.js' +export type {Item} from './services/build/item.js' +export type {Loader} from './services/build/loader.js' +export type {Rule} from './services/build/rule.js' +export type * as Registry from './registry/index.js' +export type {Modules} from './registry/modules.js' +export type {Locations} from './registry/locations.js' diff --git a/sources/@roots/bud-framework/src/methods/after/after.ts b/sources/@roots/bud-framework/src/methods/after/after.ts index c03105da17..c75d4a61fd 100644 --- a/sources/@roots/bud-framework/src/methods/after/after.ts +++ b/sources/@roots/bud-framework/src/methods/after/after.ts @@ -1,4 +1,4 @@ -import {Bud} from '../../index.js' +import {Bud} from '@roots/bud-framework' export interface after { ( @@ -15,13 +15,11 @@ export const after: after = function ( action: (app: Bud) => Promise, errorHandler?: (error: Error) => unknown, ): Bud { - this.hooks.action(`compiler.close`, async bud => { - try { - await action(bud) - } catch (error) { + this.hooks.action(`compiler.done`, async ([bud]) => { + await action(bud).catch(error => { if (!errorHandler) throw error errorHandler(error) - } + }) }) return this diff --git a/sources/@roots/bud-framework/src/methods/run.ts b/sources/@roots/bud-framework/src/methods/run.ts index 5161ed0071..73272e9052 100644 --- a/sources/@roots/bud-framework/src/methods/run.ts +++ b/sources/@roots/bud-framework/src/methods/run.ts @@ -1,4 +1,5 @@ -import type {Bud} from '../index.js' +import type {Bud} from '@roots/bud-framework' +import type {MultiStats, WebpackError} from '@roots/bud-framework/config' /** * Run the build @@ -9,19 +10,20 @@ export interface run { export const run: run = async function (this: Bud) { if (this.isProduction) { - const compilation = await this.compiler.compile() - if (!compilation) return + const compilation = await this.compiler.compile(this) - compilation.run(async (error, stats) => { - if (error) await this.compiler.onError(error) + compilation?.run(async (error: WebpackError, stats: MultiStats) => { + if (error) { + await this.compiler.onError(error) + } compilation.close(async error => { - if (error) await this.compiler.onError(error) + if (error) { + await this.compiler.onError(error) + } }) }) } - if (this.isDevelopment) { - await this.server.run() - } + if (this.isDevelopment) await this.server.run() } diff --git a/sources/@roots/bud-framework/src/methods/setPath/setPath.ts b/sources/@roots/bud-framework/src/methods/setPath/setPath.ts index c89b55b903..6291270e51 100644 --- a/sources/@roots/bud-framework/src/methods/setPath/setPath.ts +++ b/sources/@roots/bud-framework/src/methods/setPath/setPath.ts @@ -1,9 +1,9 @@ +import type {Bud} from '@roots/bud-framework' +import type {SyncRegistry} from '@roots/bud-framework/registry' + import {InputError} from '@roots/bud-support/errors' import {isAbsolute} from 'node:path' -import type {Bud} from '../../index.js' -import type {SyncRegistry} from '../../types/registry/index.js' - import * as isType from './isType.js' import * as validate from './validate.js' diff --git a/sources/@roots/bud-framework/src/module.ts b/sources/@roots/bud-framework/src/module.ts index e87c05797b..49c9f264f0 100644 --- a/sources/@roots/bud-framework/src/module.ts +++ b/sources/@roots/bud-framework/src/module.ts @@ -1,10 +1,8 @@ import {bind} from '@roots/bud-support/decorators/bind' import {ModuleError} from '@roots/bud-support/errors' import {resolve} from '@roots/bud-support/import-meta-resolve' -import get from '@roots/bud-support/lodash/get' -import set from '@roots/bud-support/lodash/set' +import logger from '@roots/bud-support/logger' import args from '@roots/bud-support/utilities/args' -import logger from '@roots/bud-support/utilities/logger' import {paths} from '@roots/bud-support/utilities/paths' import {join, normalize, relative} from 'node:path' import {fileURLToPath, pathToFileURL} from 'node:url' @@ -16,11 +14,6 @@ import {Service} from './service.js' * Module resolver */ export class Module extends Service { - /** - * Cache exists - */ - public cacheValid: boolean - /** * Resolved module cache */ @@ -31,26 +24,36 @@ export class Module extends Service { */ @bind public override async bootstrap(bud: Bud) { - if (this.cacheEnabled && (await bud.fs.exists(this.cacheLocation))) { - try { - const data = await bud.fs.read(this.cacheLocation) + if (!this.cacheEnabled) { + this.resolved = {} + return + } - logger - .scope(`module`) - .info(`cache is enabled and cached resolutions exist`) - .info(data) - - if (data.version && data.version === bud.context?.bud?.version) { - set(this, `resolved`, data.resolutions) - set(this, `cacheValid`, true) - return - } - } catch (e) { - // noop - } + if (!(await bud.fs.exists(this.cacheLocation))) { + this.resolved = {} + return + } + + const data = await bud.fs.read(this.cacheLocation) + + if (!data?.resolutions) { + logger + .scope(`module`) + .warn( + `cache is enabled but resolution data is missing. resetting cache.`, + ) + .info(data) + + this.resolved = {} + return } - set(this, `cacheValid`, false) + logger + .scope(`module`) + .info(`cache is enabled and cached resolutions exist`) + .info(data) + + this.resolved = data.resolutions } /** @@ -79,6 +82,9 @@ export class Module extends Service { .then(path => relative(this.app.context.basedir, path)) .then(path => path.split(signifier).shift()) .then(path => this.app.path(path as any, signifier)) + .catch(error => { + throw error + }) } /** @@ -86,9 +92,11 @@ export class Module extends Service { */ @bind public async getManifestPath(pkgName: string) { - return await this.getDirectory(pkgName).then(dir => - this.app.path(dir, `package.json`), - ) + return await this.getDirectory(pkgName) + .then(dir => this.app.path(dir, `package.json`)) + .catch(error => { + throw error + }) } /** @@ -96,24 +104,41 @@ export class Module extends Service { */ @bind public async import(signifier: T, context?: string) { - if (this.resolved && signifier in this.resolved) { - const m = await import(get(this.resolved, [signifier])) - return m?.default ?? m + if (this.resolved?.[signifier]) { + const result = await import(this.resolved[signifier]) + .then(m => m.default ?? m) + .catch(error => { + logger + .scope(`module`) + .warn( + `Could not import ${signifier} from ${this.resolved[signifier]}. Removing from cached module registry.`, + ) + + this.resolved[signifier] = undefined + }) + + if (result) return result } - try { - const path = await this.resolve(signifier, context) - const result = await import(path) - logger.scope(`module`).info(`imported`, signifier) - return result?.default ?? result - } catch (error) { - throw new ModuleError(`could not import ${signifier}`, { - props: { - details: `Could not import ${signifier}`, - origin: error, - }, + const path = await this.resolve(signifier, context).catch(error => { + throw error + }) + + const result = await import(path) + .then(m => m.default ?? m) + .catch(error => { + throw new ModuleError(`could not import ${signifier}`, { + props: { + details: `Could not import ${signifier}`, + origin: error, + }, + }) }) - } + + if (result) + logger.scope(`module`).info(`[cache miss]`, `imported`, signifier) + + return result } /** @@ -150,50 +175,56 @@ export class Module extends Service { ): Promise { let errors = [] - if (this.resolved && signifier in this.resolved) { + if (this.resolved?.[signifier]) { logger .scope(`module`) - .info(`[cache hit] ${signifier} => ${this.resolved[signifier]}`) + .info( + `[cache hit]`, + `resolved ${signifier} to ${this.resolved[signifier]}`, + ) return this.resolved[signifier] } - logger.scope(`module`).info(`resolving`, signifier) - - try { - const path = await resolve(signifier, this.makeContextURL()) - set(this.resolved, [signifier], normalize(fileURLToPath(path))) - - logger - .scope(`module`) - .info( - `[cache miss]`, - `resolved ${signifier} to ${get(this.resolved, [signifier])}`, + await resolve(signifier, this.makeContextURL()) + .then(path => { + this.resolved[signifier] = normalize(fileURLToPath(path)) + logger + .scope(`module`) + .info( + `[cache miss]`, + `resolved ${signifier} to ${this.resolved[signifier]}`, + ) + }) + .catch(error => { + errors.push( + `Could not resolve ${signifier} from ${context}: ${error.message}`, ) + }) - return get(this.resolved, [signifier]) - } catch (err) { - errors.push(err.toString()) - } + if (this.resolved[signifier]) return this.resolved[signifier] - try { - const path = await resolve(signifier, this.makeContextURL()) - set(this.resolved, [signifier], normalize(fileURLToPath(path))) - logger - .scope(`module`) - .info( - `[cache miss]`, - `resolved ${signifier} to ${get(this.resolved, [signifier])}`, + await resolve(signifier, this.makeContextURL(context)) + .then(path => { + this.resolved[signifier] = normalize(fileURLToPath(path)) + logger + .scope(`module`) + .info( + `[cache miss]`, + `resolved ${signifier} to ${this.resolved[signifier]}`, + ) + }) + .catch(error => { + errors.push( + `Could not resolve ${signifier} from ${this.makeContextURL( + context, + )}: ${error.message}`, ) + }) - return get(this.resolved, [signifier]) - } catch (err) { - errors.push(err.toString()) - } - - errors.push(`Could not resolve ${signifier} from ${context}`) + if (this.resolved[signifier]) return this.resolved[signifier] - throw new ModuleError(`could not resolve ${signifier}`, { + throw new ModuleError(`Could not resolve ${signifier}`, { cause: errors.reverse().join(`\n`), }) } diff --git a/sources/@roots/bud-framework/src/services/notifier/notifier.ts b/sources/@roots/bud-framework/src/notifier.ts similarity index 83% rename from sources/@roots/bud-framework/src/services/notifier/notifier.ts rename to sources/@roots/bud-framework/src/notifier.ts index b5cad923a9..bb30038869 100644 --- a/sources/@roots/bud-framework/src/services/notifier/notifier.ts +++ b/sources/@roots/bud-framework/src/notifier.ts @@ -1,17 +1,26 @@ +import type {Bud} from '@roots/bud-framework' import type { Notification as NodeNotification, NotificationCallback, } from '@roots/bud-support/node-notifier' -import {type Bud} from '@roots/bud-framework' -import {Service} from '@roots/bud-framework/service' -import {bind} from '@roots/bud-support/decorators/bind' import isEmpty from '@roots/bud-support/lodash/isEmpty' import isString from '@roots/bud-support/lodash/isString' import {open, openEditor} from '@roots/bud-support/open' import {platform} from 'node:os' - -import {notifierPath} from './notifierPath.js' +import {dirname, resolve} from 'node:path' +import {fileURLToPath} from 'node:url' + +const notifierPath = resolve( + dirname(fileURLToPath(import.meta.url)), + `..`, // bud-framework + `vendor`, + `mac.no-index`, + `roots-notifier.app`, + `Contents`, + `MacOS`, + `roots-notifier`, +) interface Notification extends NodeNotification { actions?: string | string[] | undefined @@ -29,7 +38,7 @@ interface Notification extends NodeNotification { /** * Notifier */ -export class Notifier extends Service { +export class Notifier { /** * Browser to open on error */ @@ -63,11 +72,24 @@ export class Notifier extends Service { ): Notifier[`notificationCenter`] } + public constructor(public _app: () => Bud) { + this.make = this.make.bind(this) + this.notify = this.notify.bind(this) + this.openBrowser = this.openBrowser.bind(this) + this.openEditor = this.openEditor.bind(this) + } + + /** + * Get bud instance + */ + public get app(): Bud { + return this._app() + } + /** - * {@link Service.boot} + * Make notifier */ - @bind - public override async boot(bud: Bud) { + public async make(bud: Bud) { if (this.notificationsEnabled) { const {NotificationCenter} = await import( `@roots/bud-support/node-notifier` @@ -97,7 +119,6 @@ export class Notifier extends Service { /** * Emit OS notification center notice */ - @bind public notify( notification: Notification, callback?: NotificationCallback, @@ -117,7 +138,6 @@ export class Notifier extends Service { /** * Open browser in development */ - @bind public async openBrowser(url: string) { if (!this.app.isDevelopment) return if (!this.openBrowserEnabled) return @@ -143,7 +163,6 @@ export class Notifier extends Service { /** * Open editor on error */ - @bind public openEditor(input: Array | string) { if (!this.openEditorEnabled) return if (!isString(this.editor)) return diff --git a/sources/@roots/bud-framework/src/types/registry/build.ts b/sources/@roots/bud-framework/src/registry/build.ts similarity index 97% rename from sources/@roots/bud-framework/src/types/registry/build.ts rename to sources/@roots/bud-framework/src/registry/build.ts index d4e50f1982..07698148be 100644 --- a/sources/@roots/bud-framework/src/types/registry/build.ts +++ b/sources/@roots/bud-framework/src/registry/build.ts @@ -5,7 +5,7 @@ import type { Optimization, RuleSetRule, StatsOptions, -} from '../config/index.js' +} from '@roots/bud-framework/config' export interface Sync { bail: boolean @@ -64,10 +64,12 @@ export interface Sync { 'output.clean': Configuration['output']['clean'] & boolean 'output.environment': Configuration['output']['environment'] 'output.filename': Configuration['output']['filename'] + 'output.hashFunction': Configuration['output']['hashFunction'] 'output.hotUpdateChunkFilename': | Configuration['output']['hotUpdateChunkFilename'] 'output.hotUpdateMainFilename': | Configuration['output']['hotUpdateMainFilename'] + 'output.iife': Configuration['output']['iife'] 'output.module': Configuration['output']['module'] 'output.path': Configuration['output']['path'] 'output.pathinfo': Configuration['output']['pathinfo'] diff --git a/sources/@roots/bud-framework/src/registry/dev.ts b/sources/@roots/bud-framework/src/registry/dev.ts new file mode 100644 index 0000000000..00105e714d --- /dev/null +++ b/sources/@roots/bud-framework/src/registry/dev.ts @@ -0,0 +1,136 @@ +import type {Bud} from '@roots/bud-framework' +import type { + Connection, + Middleware, + Options, +} from '@roots/bud-framework/services/server' +import type {WatchOptions} from '@roots/bud-support/chokidar' +import type {ListenOptions} from 'node:net' + +export interface Sync { + 'client.dist': string + + 'client.path': string + + /** + * Scripts included in dev builds + */ + 'client.scripts': Set<(app: Bud) => string> + + 'client.standalone': boolean + + listenOptions: ListenOptions + + 'middleware.dev.options': Middleware.Available['dev']['options'] + + 'middleware.dev.options.headers': Middleware.Available['dev']['options']['headers'] + + 'middleware.dev.options.index': Middleware.Available['dev']['options']['index'] + + 'middleware.dev.options.methods': Middleware.Available['dev']['options']['methods'] + + 'middleware.dev.options.publicPath': Middleware.Available['dev']['options']['publicPath'] + + 'middleware.dev.options.writeToDisk': Middleware.Available['dev']['options']['writeToDisk'] + + /** + * Enabled middleware + */ + 'middleware.enabled': Array + 'middleware.proxy.options': Middleware.Available['proxy']['options'] + 'middleware.proxy.options.agent': Middleware.Available['proxy']['options']['agent'] + 'middleware.proxy.options.auth': Middleware.Available['proxy']['options']['auth'] + + 'middleware.proxy.options.autoRewrite': Middleware.Available['proxy']['options']['autoRewrite'] + + 'middleware.proxy.options.buffer': Middleware.Available['proxy']['options']['buffer'] + 'middleware.proxy.options.changeOrigin': Middleware.Available['proxy']['options']['changeOrigin'] + 'middleware.proxy.options.cookieDomainRewrite': Middleware.Available['proxy']['options']['cookieDomainRewrite'] + 'middleware.proxy.options.cookiePathRewrite': Middleware.Available['proxy']['options']['cookiePathRewrite'] + 'middleware.proxy.options.ejectPlugins': Middleware.Available['proxy']['options']['ejectPlugins'] + 'middleware.proxy.options.followRedirects': Middleware.Available['proxy']['options']['followRedirects'] + + 'middleware.proxy.options.forward': Middleware.Available['proxy']['options']['forward'] + 'middleware.proxy.options.headers': Record + 'middleware.proxy.options.hostRewrite': Middleware.Available['proxy']['options']['hostRewrite'] + 'middleware.proxy.options.ignorePath': Middleware.Available['proxy']['options']['ignorePath'] + 'middleware.proxy.options.localAddress': Middleware.Available['proxy']['options']['localAddress'] + 'middleware.proxy.options.logger': Middleware.Available['proxy']['options']['logger'] + 'middleware.proxy.options.on': Middleware.Available['proxy']['options']['on'] + 'middleware.proxy.options.onProxyReq': Middleware.Available['proxy']['options']['on']['proxyReq'] + 'middleware.proxy.options.onProxyRes': Middleware.Available['proxy']['options']['on']['proxyRes'] + 'middleware.proxy.options.pathFilter': Middleware.Available['proxy']['options']['pathFilter'] + 'middleware.proxy.options.pathRewrite': Middleware.Available['proxy']['options']['pathRewrite'] + 'middleware.proxy.options.plugins': Middleware.Available['proxy']['options']['plugins'] + 'middleware.proxy.options.prependPath': Middleware.Available['proxy']['options']['prependPath'] + 'middleware.proxy.options.preserveHeaderKeyCase': Middleware.Available['proxy']['options']['preserveHeaderKeyCase'] + 'middleware.proxy.options.protocolRewrite': Middleware.Available['proxy']['options']['protocolRewrite'] + 'middleware.proxy.options.proxyTimeout': Middleware.Available['proxy']['options']['proxyTimeout'] + 'middleware.proxy.options.router': Middleware.Available['proxy']['options']['router'] + 'middleware.proxy.options.secure': Middleware.Available['proxy']['options']['secure'] + 'middleware.proxy.options.selfHandleResponse': Middleware.Available['proxy']['options']['selfHandleResponse'] + 'middleware.proxy.options.ssl': Middleware.Available['proxy']['options']['ssl'] + 'middleware.proxy.options.target': Middleware.Available['proxy']['options']['target'] & + URL + 'middleware.proxy.options.timeout': Middleware.Available['proxy']['options']['timeout'] + 'middleware.proxy.options.toProxy': Middleware.Available['proxy']['options']['toProxy'] + 'middleware.proxy.options.ws': Middleware.Available['proxy']['options']['ws'] + 'middleware.proxy.options.xfwd': Middleware.Available['proxy']['options']['xfwd'] + /** + * Proxy middleware replacements + */ + 'middleware.proxy.replacements': Array<[string, string]> + /** + * Error callback + */ + onError: Connection['onError'] + /** + * On listening callback + */ + onListening: Connection['onListening'] + /** + * Request callback + */ + onRequest: Connection['onRequest'] + /** + * Dev server connection options + */ + options: Options + /** + * Proxy URL + */ + proxyUrl: URL + /** + * Public proxy URL + */ + publicProxyUrl: URL + /** + * External URL + */ + publicUrl: URL + /** + * Server URL + */ + url: URL + /** + * Files which trigger a full browser reload + */ + 'watch.files': Set + + /** + * FS.Watcher options + */ + 'watch.options': WatchOptions +} + +export type SyncRegistry = { + [P in keyof Sync as `dev.${P & string}`]: Sync[P] +} + +export interface Async {} + +export type AsyncRegistry = { + [P in keyof Async as `dev.${P & string}`]: Async[P] +} + +export type Registry = SyncRegistry & AsyncRegistry diff --git a/sources/@roots/bud-framework/src/types/registry/events.ts b/sources/@roots/bud-framework/src/registry/events.ts similarity index 73% rename from sources/@roots/bud-framework/src/types/registry/events.ts rename to sources/@roots/bud-framework/src/registry/events.ts index 865652e6bd..1c915b3abb 100644 --- a/sources/@roots/bud-framework/src/types/registry/events.ts +++ b/sources/@roots/bud-framework/src/registry/events.ts @@ -1,5 +1,9 @@ -import type {Bud} from '../../index.js' -import type {Compilation, MultiStats} from '../config/index.js' +import type {Bud} from '@roots/bud-framework' +import type { + Compilation, + MultiStats, + Stats, +} from '@roots/bud-framework/config' export interface Events { boot: Bud @@ -9,9 +13,8 @@ export interface Events { 'build.after': Bud 'build.before': Bud 'compilation.afterEmit': Compilation - 'compiler.after': Bud 'compiler.before': Bud - 'compiler.close': Bud + 'compiler.done': [Bud, Stats] 'compiler.error': Error 'compiler.stats': MultiStats 'config.after': Bud diff --git a/sources/@roots/bud-framework/src/types/registry/flags.ts b/sources/@roots/bud-framework/src/registry/flags.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/flags.ts rename to sources/@roots/bud-framework/src/registry/flags.ts diff --git a/sources/@roots/bud-framework/src/types/registry/index.ts b/sources/@roots/bud-framework/src/registry/index.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/index.ts rename to sources/@roots/bud-framework/src/registry/index.ts diff --git a/sources/@roots/bud-framework/src/types/registry/items.ts b/sources/@roots/bud-framework/src/registry/items.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/items.ts rename to sources/@roots/bud-framework/src/registry/items.ts diff --git a/sources/@roots/bud-framework/src/types/registry/loaders.ts b/sources/@roots/bud-framework/src/registry/loaders.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/loaders.ts rename to sources/@roots/bud-framework/src/registry/loaders.ts diff --git a/sources/@roots/bud-framework/src/types/registry/locations.ts b/sources/@roots/bud-framework/src/registry/locations.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/locations.ts rename to sources/@roots/bud-framework/src/registry/locations.ts diff --git a/sources/@roots/bud-framework/src/types/registry/modules.ts b/sources/@roots/bud-framework/src/registry/modules.ts similarity index 82% rename from sources/@roots/bud-framework/src/types/registry/modules.ts rename to sources/@roots/bud-framework/src/registry/modules.ts index 934e2a9ebf..af9f20b78d 100644 --- a/sources/@roots/bud-framework/src/types/registry/modules.ts +++ b/sources/@roots/bud-framework/src/registry/modules.ts @@ -1,4 +1,4 @@ -import type {Extension} from '../../extension/index.js' +import type {Extension} from '@roots/bud-framework/extension' interface Modules { _?: Extension diff --git a/sources/@roots/bud-framework/src/types/registry/patterns.ts b/sources/@roots/bud-framework/src/registry/patterns.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/patterns.ts rename to sources/@roots/bud-framework/src/registry/patterns.ts diff --git a/sources/@roots/bud-framework/src/types/registry/rules.ts b/sources/@roots/bud-framework/src/registry/rules.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/rules.ts rename to sources/@roots/bud-framework/src/registry/rules.ts diff --git a/sources/@roots/bud-framework/src/types/registry/values.ts b/sources/@roots/bud-framework/src/registry/values.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/registry/values.ts rename to sources/@roots/bud-framework/src/registry/values.ts diff --git a/sources/@roots/bud-framework/src/service.ts b/sources/@roots/bud-framework/src/service.ts index 23b34dbda8..3a512fd5dc 100644 --- a/sources/@roots/bud-framework/src/service.ts +++ b/sources/@roots/bud-framework/src/service.ts @@ -1,4 +1,5 @@ import type {Bud} from '@roots/bud-framework' +import type {MultiStats, Stats} from '@roots/bud-framework/config' import camelCase from '@roots/bud-support/lodash/camelCase' import logger from '@roots/bud-support/logger' @@ -32,27 +33,32 @@ interface Contract { /** * After build service */ - buildAfter?(app?: Bud): Promise + buildAfter(app: Bud): Promise /** * Before build service */ - buildBefore?(app?: Bud): Promise + buildBefore(app: Bud): Promise /** - * After Compiler service + * Before Compiler service */ - compilerAfter?(app?: Bud): Promise + compilerBefore(app: Bud): Promise /** - * Before Compiler service + * After Compiler service */ - compilerBefore?(app?: Bud): Promise + compilerDone([bud, stats]: [Bud, Stats & MultiStats]): Promise /** * After config callback */ - configAfter?(app?: Bud): Promise + configAfter(app: Bud): Promise + + /** + * Return the bud instance from the service context + */ + done(): Bud /** * Service label @@ -83,11 +89,6 @@ interface Contract { * A Service interfaces with the Framework through a series of callbacks at different points in the build. */ abstract class Base implements Partial { - /** - * Service ID - */ - public ident?: string - /** * Class constructor */ @@ -121,33 +122,36 @@ abstract class Base implements Partial { /** * After build service */ - public buildAfter?(app?: Bud): Promise + public async buildAfter(_app: Bud): Promise {} /** * Before build service */ - public buildBefore?(app?: Bud): Promise + public async buildBefore(_app: Bud): Promise {} /** - * After Compiler service + * Before Compiler service */ - public compilerAfter?(app?: Bud): Promise + public async compilerBefore(_app: Bud): Promise {} /** - * Before Compiler service + * After Compiler service */ - public compilerBefore?(app?: Bud): Promise + public async compilerDone([bud, stats]: [ + Bud, + Stats & MultiStats, + ]): Promise {} /** * After config callback */ - public configAfter?(app?: Bud): Promise + public async configAfter(app: Bud): Promise {} /** - * Service label + * Return the Bud instance from the service context */ - public get label() { - return this.ident ?? camelCase(this.constructor.name) + public done() { + return this.app } /** @@ -179,6 +183,8 @@ abstract class BaseContainer extends Container implements Partial { + public declare label: string + /** * Class constructor */ @@ -211,29 +217,39 @@ abstract class BaseContainer public async bootstrap(app: Bud): Promise {} /** - * After build service + * After configuration build */ - public buildAfter?(app?: Bud): Promise + public async buildAfter(app: Bud): Promise {} /** - * Before build service + * Before configuration build */ - public buildBefore?(app?: Bud): Promise + public async buildBefore(app: Bud): Promise {} /** - * After Compiler service + * Before Compiler */ - public compilerAfter?(app?: Bud): Promise + public async compilerBefore(app: Bud): Promise {} /** - * Before Compiler service + * After Compiler service */ - public compilerBefore?(app?: Bud): Promise + public async compilerDone([bud, stats]: [ + Bud, + Stats & MultiStats, + ]): Promise {} /** * After config callback */ - public configAfter?(app?: Bud): Promise + public async configAfter(app: Bud): Promise {} + + /** + * Return the Bud instance from the service context + */ + public done() { + return this.app + } /** * Logger instance diff --git a/sources/@roots/bud-framework/src/types/services/api/index.ts b/sources/@roots/bud-framework/src/services/api/index.ts similarity index 84% rename from sources/@roots/bud-framework/src/types/services/api/index.ts rename to sources/@roots/bud-framework/src/services/api/index.ts index ae292eecf2..5b9286ca94 100644 --- a/sources/@roots/bud-framework/src/types/services/api/index.ts +++ b/sources/@roots/bud-framework/src/services/api/index.ts @@ -1,5 +1,5 @@ -import type {Bud} from '../../../index.js' -import type {ServiceContainer} from '../../../service.js' +import type {Bud} from '@roots/bud-framework' +import type {ServiceContainer} from '@roots/bud-framework/service' /** * API service diff --git a/sources/@roots/bud-framework/src/types/services/build/base.ts b/sources/@roots/bud-framework/src/services/build/base.ts similarity index 85% rename from sources/@roots/bud-framework/src/types/services/build/base.ts rename to sources/@roots/bud-framework/src/services/build/base.ts index a3ef37b557..9f627cd0c8 100644 --- a/sources/@roots/bud-framework/src/types/services/build/base.ts +++ b/sources/@roots/bud-framework/src/services/build/base.ts @@ -1,4 +1,4 @@ -import type {Bud} from '../../../index.js' +import type {Bud} from '@roots/bud-framework' /** * Base interface for Loaders, Items, and rules diff --git a/sources/@roots/bud-framework/src/types/services/build/index.ts b/sources/@roots/bud-framework/src/services/build/index.ts similarity index 88% rename from sources/@roots/bud-framework/src/types/services/build/index.ts rename to sources/@roots/bud-framework/src/services/build/index.ts index e22ad7396c..688f9c1c65 100644 --- a/sources/@roots/bud-framework/src/types/services/build/index.ts +++ b/sources/@roots/bud-framework/src/services/build/index.ts @@ -1,7 +1,10 @@ -import type {Configuration} from '@roots/bud-framework/config' +import type { + Configuration, + Items, + Loaders, + Rules, +} from '@roots/bud-framework' -import type {Items, Loaders, Rules} from '../../../index.js' -import type {Service as BaseService} from '../../../service.js' import type {Base} from './base.js' import type {Item} from './item.js' import type {Loader} from './loader.js' @@ -39,7 +42,7 @@ import type { * bud.hooks.filter('build.entry', {}) * ``` */ -export interface Service extends BaseService { +export interface Build { /** * Compiler configuration */ @@ -73,7 +76,7 @@ export interface Service extends BaseService { /** * Make {@link Build.config} */ - make(): Promise + make(): Promise /** * Make a new {@link Item} instance diff --git a/sources/@roots/bud-framework/src/types/services/build/item.ts b/sources/@roots/bud-framework/src/services/build/item.ts similarity index 95% rename from sources/@roots/bud-framework/src/types/services/build/item.ts rename to sources/@roots/bud-framework/src/services/build/item.ts index d1bba092ec..4698325775 100644 --- a/sources/@roots/bud-framework/src/types/services/build/item.ts +++ b/sources/@roots/bud-framework/src/services/build/item.ts @@ -1,5 +1,5 @@ -import type {Bud} from '../../../index.js' -import type {Loaders} from '../../../index.js' +import type {Bud, Loaders} from '@roots/bud-framework' + import type {Base} from './base.js' import type {Loader} from './loader.js' diff --git a/sources/@roots/bud-framework/src/types/services/build/loader.ts b/sources/@roots/bud-framework/src/services/build/loader.ts similarity index 92% rename from sources/@roots/bud-framework/src/types/services/build/loader.ts rename to sources/@roots/bud-framework/src/services/build/loader.ts index 178753d4fc..37cd217efe 100644 --- a/sources/@roots/bud-framework/src/types/services/build/loader.ts +++ b/sources/@roots/bud-framework/src/services/build/loader.ts @@ -1,4 +1,5 @@ -import type {Bud} from '../../../index.js' +import type {Bud} from '@roots/bud-framework' + import type {Base} from './base.js' /** diff --git a/sources/@roots/bud-framework/src/types/services/build/rule.ts b/sources/@roots/bud-framework/src/services/build/rule.ts similarity index 96% rename from sources/@roots/bud-framework/src/types/services/build/rule.ts rename to sources/@roots/bud-framework/src/services/build/rule.ts index 4f37dfdea9..e8d484c51b 100644 --- a/sources/@roots/bud-framework/src/types/services/build/rule.ts +++ b/sources/@roots/bud-framework/src/services/build/rule.ts @@ -1,6 +1,6 @@ -import type {Bud} from '../../../index.js' -import type {Items} from '../../../index.js' -import type {RuleSetRule} from '../../config/index.js' +import type {Bud, Items} from '@roots/bud-framework' +import type {RuleSetRule} from '@roots/bud-framework/config' + import type {Base} from './base.js' import type {Item} from './item.js' diff --git a/sources/@roots/bud-framework/src/types/services/cache/index.ts b/sources/@roots/bud-framework/src/services/cache/index.ts similarity index 83% rename from sources/@roots/bud-framework/src/types/services/cache/index.ts rename to sources/@roots/bud-framework/src/services/cache/index.ts index 5f45b7fa58..f8a8c7717d 100644 --- a/sources/@roots/bud-framework/src/types/services/cache/index.ts +++ b/sources/@roots/bud-framework/src/services/cache/index.ts @@ -1,11 +1,9 @@ import type {Configuration} from '@roots/bud-framework/config' -import type {Service as BaseService} from '../../../service.js' - /** * Cache service Interface */ -export interface Service extends BaseService { +export interface Cache { /** * Cache build dependencies */ diff --git a/sources/@roots/bud-framework/src/types/services/compiler/index.ts b/sources/@roots/bud-framework/src/services/compiler/index.ts similarity index 66% rename from sources/@roots/bud-framework/src/types/services/compiler/index.ts rename to sources/@roots/bud-framework/src/services/compiler/index.ts index 85fa18b124..8b1d4842d7 100644 --- a/sources/@roots/bud-framework/src/types/services/compiler/index.ts +++ b/sources/@roots/bud-framework/src/services/compiler/index.ts @@ -1,18 +1,23 @@ -import type {ErrorWithSourceFile} from '@roots/bud-support/open' - -import type {Contract} from '../../../service.js' +import type {Bud} from '@roots/bud-framework' import type { Configuration, MultiCompiler, MultiStats, + Stats, StatsCompilation, StatsError, -} from '../../config/index.js' +} from '@roots/bud-framework/config' +import type {ErrorWithSourceFile} from '@roots/bud-support/open' /** * Compiler service */ -interface Service extends Contract { +export interface Compiler { + /** + * Compilation stats + */ + compilationStats: StatsCompilation + /** * Returns a {@link WebpackMultiCompiler} instance * @@ -28,12 +33,12 @@ interface Service extends Contract { * }]) * ``` */ - compile(): Promise + compile(bud: Bud): Promise /** * The compiler configuration */ - config: Array + config: Array & {parallelism?: number} /** * Compiler implementation @@ -45,18 +50,29 @@ interface Service extends Contract { */ instance: MultiCompiler + label: string + + /** + * Compiler error handler + */ onError(error: Error): Promise + /** + * Stats handler + */ onStats(stats: MultiStats): Promise + /** + * Determine source of module errors + */ sourceErrors( errors: Array, ): Array /** - * Contains compilation stats, if available. + * Raw stats */ - stats: StatsCompilation + stats: Stats & MultiStats } export type BudError = { @@ -68,5 +84,3 @@ export type BudError = { } export type Config = Configuration - -export type {Service} diff --git a/sources/@roots/bud-framework/src/services/dashboard/index.ts b/sources/@roots/bud-framework/src/services/dashboard/index.ts new file mode 100644 index 0000000000..e9522850a2 --- /dev/null +++ b/sources/@roots/bud-framework/src/services/dashboard/index.ts @@ -0,0 +1,57 @@ +import type { + StatsCompilation, + StatsError, +} from '@roots/bud-framework/config' + +/** + * Dashboard service container + */ +export interface Dashboard { + /** + * Format stats errors + */ + formatStatsErrors: (stats: StatsError[]) => StatsError[] + + /** + * IDs of rendered stats for debouncing + */ + hashes: Set + + /** + * CLI instance + */ + instance?: { + rerender: (...args: any[]) => void + waitUntilExit: () => Promise + } + + /** + * Render stats fully + */ + render: any + + /** + * Render string to stdout + */ + renderString(stats: string): void + + /** + * Silent mode is enabled? + */ + silent: boolean + + /** + * Stderr stream + */ + stderr: NodeJS.WriteStream & {fd: 2} + + /** + * Stdout stream + */ + stdout: NodeJS.WriteStream & {fd: 1} + + /** + * Update the dashboard + */ + update(stats: StatsCompilation): this +} diff --git a/sources/@roots/bud-framework/src/types/services/env/index.ts b/sources/@roots/bud-framework/src/services/env/index.ts similarity index 82% rename from sources/@roots/bud-framework/src/types/services/env/index.ts rename to sources/@roots/bud-framework/src/services/env/index.ts index d398147633..00a4a265fc 100644 --- a/sources/@roots/bud-framework/src/types/services/env/index.ts +++ b/sources/@roots/bud-framework/src/services/env/index.ts @@ -1,4 +1,4 @@ -import type {ServiceContainer} from '../../../service.js' +import type {ServiceContainer} from '@roots/bud-framework/service' /** * Env container interface diff --git a/sources/@roots/bud-framework/src/types/services/extensions/index.ts b/sources/@roots/bud-framework/src/services/extensions/index.ts similarity index 89% rename from sources/@roots/bud-framework/src/types/services/extensions/index.ts rename to sources/@roots/bud-framework/src/services/extensions/index.ts index 97a8762a76..f44908e676 100644 --- a/sources/@roots/bud-framework/src/types/services/extensions/index.ts +++ b/sources/@roots/bud-framework/src/services/extensions/index.ts @@ -1,18 +1,16 @@ -import type Container from '@roots/container' - +import type {Bud, Modules} from '@roots/bud-framework' import type { ApplyPlugin, Extension, ExtensionLiteral, -} from '../../../extension/index.js' -import type {Bud} from '../../../index.js' -import type {Service as BaseService} from '../../../service.js' -import type {Modules} from '../../registry/modules.js' +} from '@roots/bud-framework/extension' +import type Container from '@roots/container' export type LifecycleMethods = | 'boot' | 'buildAfter' | 'buildBefore' + | 'compilerDone' | 'configAfter' | 'make' | 'register' @@ -26,7 +24,7 @@ export type LifecycleMethods = * They can also be defined as a {@link Plugin} which is a {@link Module} * yielding a {@link PluginInstance}. */ -export interface Service extends BaseService { +export interface Extensions { /** * Add an extension */ diff --git a/sources/@roots/bud-framework/src/types/services/hooks/index.ts b/sources/@roots/bud-framework/src/services/hooks/index.ts similarity index 92% rename from sources/@roots/bud-framework/src/types/services/hooks/index.ts rename to sources/@roots/bud-framework/src/services/hooks/index.ts index 70ee610066..a88f5560b4 100644 --- a/sources/@roots/bud-framework/src/types/services/hooks/index.ts +++ b/sources/@roots/bud-framework/src/services/hooks/index.ts @@ -1,11 +1,9 @@ -import type {Bud} from '../../../index.js' -import type {Service as BaseService} from '../../../service.js' -import type * as Registry from '../../registry/index.js' +import type {Bud, Registry} from '@roots/bud-framework' /** * Hooks service */ -export default interface Hooks extends BaseService { +export interface Hooks { /** * Store callback to an action handler */ diff --git a/sources/@roots/bud-framework/src/services/index.ts b/sources/@roots/bud-framework/src/services/index.ts new file mode 100644 index 0000000000..6865e912a7 --- /dev/null +++ b/sources/@roots/bud-framework/src/services/index.ts @@ -0,0 +1,23 @@ +import type {Api} from './api/index.js' +import type {Build} from './build/index.js' +import type {Cache} from './cache/index.js' +import type {Compiler} from './compiler/index.js' +import type {Dashboard} from './dashboard/index.js' +import type {Env} from './env/index.js' +import type {Extensions} from './extensions/index.js' +import type {Hooks} from './hooks/index.js' +import type {Project} from './project/index.js' +import type {Server} from './server/index.js' + +export type { + Api, + Build, + Cache, + Compiler, + Dashboard, + Env, + Extensions, + Hooks, + Project, + Server, +} diff --git a/sources/@roots/bud-framework/src/services/notifier/index.ts b/sources/@roots/bud-framework/src/services/notifier/index.ts deleted file mode 100644 index e540add23f..0000000000 --- a/sources/@roots/bud-framework/src/services/notifier/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {Notifier} from './notifier.js' - -export default Notifier diff --git a/sources/@roots/bud-framework/src/services/notifier/notifierPath.ts b/sources/@roots/bud-framework/src/services/notifier/notifierPath.ts deleted file mode 100644 index 8806485f05..0000000000 --- a/sources/@roots/bud-framework/src/services/notifier/notifierPath.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {dirname, resolve} from 'node:path' -import {fileURLToPath} from 'node:url' - -export const notifierPath = resolve( - dirname(fileURLToPath(import.meta.url)), - `..`, // services - `..`, // src - `..`, // bud-framework - `vendor`, - `mac.no-index`, - `roots-notifier.app`, - `Contents`, - `MacOS`, - `roots-notifier`, -) diff --git a/sources/@roots/bud-framework/src/services/project/index.ts b/sources/@roots/bud-framework/src/services/project/index.ts new file mode 100644 index 0000000000..ed6080685f --- /dev/null +++ b/sources/@roots/bud-framework/src/services/project/index.ts @@ -0,0 +1,6 @@ +import type {Service} from '@roots/bud-framework' + +/** + * Peer service interface + */ +export interface Project extends Service {} diff --git a/sources/@roots/bud-framework/src/types/services/server/connection.ts b/sources/@roots/bud-framework/src/services/server/connection.ts similarity index 100% rename from sources/@roots/bud-framework/src/types/services/server/connection.ts rename to sources/@roots/bud-framework/src/services/server/connection.ts diff --git a/sources/@roots/bud-framework/src/types/services/server/index.ts b/sources/@roots/bud-framework/src/services/server/index.ts similarity index 84% rename from sources/@roots/bud-framework/src/types/services/server/index.ts rename to sources/@roots/bud-framework/src/services/server/index.ts index 9962bbdb28..e20f3c35f6 100644 --- a/sources/@roots/bud-framework/src/types/services/server/index.ts +++ b/sources/@roots/bud-framework/src/services/server/index.ts @@ -6,7 +6,7 @@ import type { OptionsMap, } from './connection.js' import type * as Middleware from './middleware.js' -import type {Server as Service} from './service.js' +import type {Server} from './service.js' import type {Watcher} from './watcher.js' export type { @@ -16,6 +16,6 @@ export type { Middleware, Options, OptionsMap, - Service, + Server, Watcher, } diff --git a/sources/@roots/bud-framework/src/types/services/server/middleware.ts b/sources/@roots/bud-framework/src/services/server/middleware.ts similarity index 96% rename from sources/@roots/bud-framework/src/types/services/server/middleware.ts rename to sources/@roots/bud-framework/src/services/server/middleware.ts index 123abf3984..b0561515c8 100644 --- a/sources/@roots/bud-framework/src/types/services/server/middleware.ts +++ b/sources/@roots/bud-framework/src/services/server/middleware.ts @@ -1,7 +1,6 @@ +import type {Bud} from '@roots/bud-framework' import type {HttpProxy} from '@roots/bud-support/http-proxy-middleware' -import type {Bud} from '../../../index.js' - export interface ProxyOptions extends HttpProxy.Options { ejectPlugins: any logger: any diff --git a/sources/@roots/bud-framework/src/types/services/server/service.ts b/sources/@roots/bud-framework/src/services/server/service.ts similarity index 94% rename from sources/@roots/bud-framework/src/types/services/server/service.ts rename to sources/@roots/bud-framework/src/services/server/service.ts index 2bfde48466..5a04be9f95 100644 --- a/sources/@roots/bud-framework/src/types/services/server/service.ts +++ b/sources/@roots/bud-framework/src/services/server/service.ts @@ -1,4 +1,3 @@ -import type {Service} from '../../../service.js' import type {Connection} from './connection.js' import type * as Middleware from './middleware.js' import type {Watcher} from './watcher.js' @@ -25,7 +24,7 @@ export interface Connections extends Record { /** * Server interface */ -export interface Server extends Service { +export interface Server { /** * Server application */ diff --git a/sources/@roots/bud-framework/src/types/services/server/watcher.ts b/sources/@roots/bud-framework/src/services/server/watcher.ts similarity index 91% rename from sources/@roots/bud-framework/src/types/services/server/watcher.ts rename to sources/@roots/bud-framework/src/services/server/watcher.ts index 650c7627da..b98492ad38 100644 --- a/sources/@roots/bud-framework/src/types/services/server/watcher.ts +++ b/sources/@roots/bud-framework/src/services/server/watcher.ts @@ -1,7 +1,6 @@ +import type {Bud} from '@roots/bud-framework' import type {FSWatcher, WatchOptions} from 'node:fs' -import type {Bud} from '../../../index.js' - /** * Watcher */ diff --git a/sources/@roots/bud-framework/src/types/options/index.ts b/sources/@roots/bud-framework/src/types/options/index.ts deleted file mode 100644 index 6f6c679b68..0000000000 --- a/sources/@roots/bud-framework/src/types/options/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type * from './context.js' diff --git a/sources/@roots/bud-framework/src/types/registry/dev.ts b/sources/@roots/bud-framework/src/types/registry/dev.ts deleted file mode 100644 index 9cfb5b89d3..0000000000 --- a/sources/@roots/bud-framework/src/types/registry/dev.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type {WatchOptions} from '@roots/bud-support/chokidar' -import type {ListenOptions} from 'node:net' - -import type {Bud} from '../../index.js' -import type * as Server from '../services/server/index.js' - -export interface Sync { - 'client.dist': string - - 'client.path': string - - /** - * Scripts included in dev builds - */ - 'client.scripts': Set<(app: Bud) => string> - - 'client.standalone': boolean - - listenOptions: ListenOptions - - 'middleware.dev.options': Server.Middleware.Available['dev']['options'] - - 'middleware.dev.options.headers': Server.Middleware.Available['dev']['options']['headers'] - - 'middleware.dev.options.index': Server.Middleware.Available['dev']['options']['index'] - - 'middleware.dev.options.methods': Server.Middleware.Available['dev']['options']['methods'] - - 'middleware.dev.options.publicPath': Server.Middleware.Available['dev']['options']['publicPath'] - - 'middleware.dev.options.writeToDisk': Server.Middleware.Available['dev']['options']['writeToDisk'] - - /** - * Enabled middleware - */ - 'middleware.enabled': Array - 'middleware.proxy.options': Server.Middleware.Available['proxy']['options'] - 'middleware.proxy.options.agent': Server.Middleware.Available['proxy']['options']['agent'] - 'middleware.proxy.options.auth': Server.Middleware.Available['proxy']['options']['auth'] - - 'middleware.proxy.options.autoRewrite': Server.Middleware.Available['proxy']['options']['autoRewrite'] - - 'middleware.proxy.options.buffer': Server.Middleware.Available['proxy']['options']['buffer'] - 'middleware.proxy.options.changeOrigin': Server.Middleware.Available['proxy']['options']['changeOrigin'] - 'middleware.proxy.options.cookieDomainRewrite': Server.Middleware.Available['proxy']['options']['cookieDomainRewrite'] - 'middleware.proxy.options.cookiePathRewrite': Server.Middleware.Available['proxy']['options']['cookiePathRewrite'] - 'middleware.proxy.options.ejectPlugins': Server.Middleware.Available['proxy']['options']['ejectPlugins'] - 'middleware.proxy.options.followRedirects': Server.Middleware.Available['proxy']['options']['followRedirects'] - - 'middleware.proxy.options.forward': Server.Middleware.Available['proxy']['options']['forward'] - 'middleware.proxy.options.headers': Record - 'middleware.proxy.options.hostRewrite': Server.Middleware.Available['proxy']['options']['hostRewrite'] - 'middleware.proxy.options.ignorePath': Server.Middleware.Available['proxy']['options']['ignorePath'] - 'middleware.proxy.options.localAddress': Server.Middleware.Available['proxy']['options']['localAddress'] - 'middleware.proxy.options.logger': Server.Middleware.Available['proxy']['options']['logger'] - 'middleware.proxy.options.on': Server.Middleware.Available['proxy']['options']['on'] - 'middleware.proxy.options.onProxyReq': Server.Middleware.Available['proxy']['options']['on']['proxyReq'] - 'middleware.proxy.options.onProxyRes': Server.Middleware.Available['proxy']['options']['on']['proxyRes'] - 'middleware.proxy.options.pathFilter': Server.Middleware.Available['proxy']['options']['pathFilter'] - 'middleware.proxy.options.pathRewrite': Server.Middleware.Available['proxy']['options']['pathRewrite'] - 'middleware.proxy.options.plugins': Server.Middleware.Available['proxy']['options']['plugins'] - 'middleware.proxy.options.prependPath': Server.Middleware.Available['proxy']['options']['prependPath'] - 'middleware.proxy.options.preserveHeaderKeyCase': Server.Middleware.Available['proxy']['options']['preserveHeaderKeyCase'] - 'middleware.proxy.options.protocolRewrite': Server.Middleware.Available['proxy']['options']['protocolRewrite'] - 'middleware.proxy.options.proxyTimeout': Server.Middleware.Available['proxy']['options']['proxyTimeout'] - 'middleware.proxy.options.router': Server.Middleware.Available['proxy']['options']['router'] - 'middleware.proxy.options.secure': Server.Middleware.Available['proxy']['options']['secure'] - 'middleware.proxy.options.selfHandleResponse': Server.Middleware.Available['proxy']['options']['selfHandleResponse'] - 'middleware.proxy.options.ssl': Server.Middleware.Available['proxy']['options']['ssl'] - 'middleware.proxy.options.target': Server.Middleware.Available['proxy']['options']['target'] & - URL - 'middleware.proxy.options.timeout': Server.Middleware.Available['proxy']['options']['timeout'] - 'middleware.proxy.options.toProxy': Server.Middleware.Available['proxy']['options']['toProxy'] - 'middleware.proxy.options.ws': Server.Middleware.Available['proxy']['options']['ws'] - 'middleware.proxy.options.xfwd': Server.Middleware.Available['proxy']['options']['xfwd'] - /** - * Proxy middleware replacements - */ - 'middleware.proxy.replacements': Array<[string, string]> - /** - * Error callback - */ - onError: Server.Connection['onError'] - /** - * On listening callback - */ - onListening: Server.Connection['onListening'] - /** - * Request callback - */ - onRequest: Server.Connection['onRequest'] - /** - * Dev server connection options - */ - options: Server.Options - /** - * Proxy URL - */ - proxyUrl: URL - /** - * Public proxy URL - */ - publicProxyUrl: URL - /** - * External URL - */ - publicUrl: URL - /** - * Server URL - */ - url: URL - /** - * Files which trigger a full browser reload - */ - 'watch.files': Set - - /** - * FS.Watcher options - */ - 'watch.options': WatchOptions -} - -export type SyncRegistry = { - [P in keyof Sync as `dev.${P & string}`]: Sync[P] -} - -export interface Async {} - -export type AsyncRegistry = { - [P in keyof Async as `dev.${P & string}`]: Async[P] -} - -export type Registry = SyncRegistry & AsyncRegistry diff --git a/sources/@roots/bud-framework/src/types/services/dashboard/index.ts b/sources/@roots/bud-framework/src/types/services/dashboard/index.ts deleted file mode 100644 index 77935d3265..0000000000 --- a/sources/@roots/bud-framework/src/types/services/dashboard/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type {Service as Contract} from '../../../service.js' -import type {MultiStats} from '../../config/index.js' - -/** - * Dashboard service container - */ -export interface Service extends Contract { - /** - * IDs of rendered stats for debouncing - */ - hashes: Set - - /** - * Render stats fully - */ - render: any - - /** - * Render queued messages - */ - renderQueuedMessages(): Promise - - /** - * Render string to stdout - */ - renderString(stats: MultiStats): void - - silent: boolean - - /** - * Update the dashboard - */ - update(stats: MultiStats): Promise -} diff --git a/sources/@roots/bud-framework/src/types/services/index.ts b/sources/@roots/bud-framework/src/types/services/index.ts deleted file mode 100644 index 87ab0d0ca5..0000000000 --- a/sources/@roots/bud-framework/src/types/services/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type * as Service from '../../service.js' -import type FS from '../../services/fs.js' -import type {Notifier} from '../../services/notifier/notifier.js' -import type {Api} from './api/index.js' -import type * as Build from './build/index.js' -import type * as Cache from './cache/index.js' -import type * as Compiler from './compiler/index.js' -import type * as Dashboard from './dashboard/index.js' -import type {Env} from './env/index.js' -import type * as Extensions from './extensions/index.js' -import type Hooks from './hooks/index.js' -import type * as Project from './project/index.js' -import type * as Server from './server/index.js' - -/** - * Service registry - * - * @virtual @public - */ -export interface Registry extends Record { - api: Api - build: Build.Service - cache: Cache.Service - compiler: Compiler.Service - dashboard: Dashboard.Service - env: Env - extensions: Extensions.Service - fs: FS & Service.Contract - hooks: Hooks - notifier: Notifier - project: Project.Service - server: Server.Service -} - -export type { - Api, - Build, - Cache, - Compiler, - Dashboard, - Env, - Extensions, - FS, - Hooks, - Notifier, - Project, - Server, -} diff --git a/sources/@roots/bud-framework/src/types/services/logger/index.ts b/sources/@roots/bud-framework/src/types/services/logger/index.ts deleted file mode 100644 index eca01dc119..0000000000 --- a/sources/@roots/bud-framework/src/types/services/logger/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {Instance} from '@roots/bud-support/signale' - -export interface Logger { - await(...messages: Array): Logger - debug(...messages: Array): Logger - error(...messages: Array): Logger - info(...messages: Array): Logger - instance: Instance - log(...messages: Array): Logger - scope(...scopes: Array): Logger - success(...messages: Array): Logger - time(label: string): Logger - timeEnd(label: string): Logger - unscope(): Logger - warn(...messages: Array): Logger -} diff --git a/sources/@roots/bud-framework/src/types/services/project/index.ts b/sources/@roots/bud-framework/src/types/services/project/index.ts deleted file mode 100644 index 433ab65198..0000000000 --- a/sources/@roots/bud-framework/src/types/services/project/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type {Service as Base} from '../../../service.js' - -/** - * Peer service interface - */ -export interface Service extends Base {} diff --git a/sources/@roots/bud-framework/test/services/__snapshots__/console.test.ts.snap b/sources/@roots/bud-framework/test/__snapshots__/console.test.ts.snap similarity index 79% rename from sources/@roots/bud-framework/test/services/__snapshots__/console.test.ts.snap rename to sources/@roots/bud-framework/test/__snapshots__/console.test.ts.snap index 9344a6ab82..6e7bf16c75 100644 --- a/sources/@roots/bud-framework/test/services/__snapshots__/console.test.ts.snap +++ b/sources/@roots/bud-framework/test/__snapshots__/console.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`ConsoleBuffer > should store console.log to consoleBuffer.queue 1`] = ` +exports[`ConsoleBuffer > should store instance.log to console.queue 1`] = ` [ { "message": "log-test", diff --git a/sources/@roots/bud-framework/test/lifecycle/bootstrap.test.ts b/sources/@roots/bud-framework/test/bootstrap.test.ts similarity index 88% rename from sources/@roots/bud-framework/test/lifecycle/bootstrap.test.ts rename to sources/@roots/bud-framework/test/bootstrap.test.ts index 52099165cf..d27e04735c 100644 --- a/sources/@roots/bud-framework/test/lifecycle/bootstrap.test.ts +++ b/sources/@roots/bud-framework/test/bootstrap.test.ts @@ -1,7 +1,7 @@ import {Bud, factory} from '@repo/test-kit' import {beforeEach, describe, expect, it, vi} from 'vitest' -import {bootstrap as subject} from '../../src/lifecycle/bootstrap.js' +import {bootstrap as subject} from '../src/bootstrap.js' describe( `bootstrap`, diff --git a/sources/@roots/bud-framework/test/console.test.ts b/sources/@roots/bud-framework/test/console.test.ts new file mode 100644 index 0000000000..fc44b43cd8 --- /dev/null +++ b/sources/@roots/bud-framework/test/console.test.ts @@ -0,0 +1,31 @@ +import {factory} from '@repo/test-kit' +import {beforeEach, describe, expect, it} from 'vitest' +import {Console} from '../src/console.js' + +describe(`ConsoleBuffer`, () => { + let bud: any + let instance: Console + + beforeEach(async () => { + bud = await factory() + instance = new Console(() => bud as any) + }) + + it(`should not patch the instance in ci`, async () => { + bud.context.ci = true + + await instance.register(bud) + expect(instance.restore).not.toBeDefined() + }) + + it(`should store instance.log to console.queue`, async () => { + await instance.register(bud) + + console.log(`log-test`) + console.info(`info-test`) + console.error(`error-test`) + console.warn(`warn-test`) + + expect(instance.queue).toMatchSnapshot() + }) +}) diff --git a/sources/@roots/bud-framework/test/index.test.ts b/sources/@roots/bud-framework/test/index.test.ts index e2a48c90fe..a4c8851eae 100644 --- a/sources/@roots/bud-framework/test/index.test.ts +++ b/sources/@roots/bud-framework/test/index.test.ts @@ -1,8 +1,12 @@ import {describe, expect, it} from 'vitest' -import {Bud, Extension, Service, ServiceContainer} from '../src/index.js' +import {Bud, Service, ServiceContainer} from '../src/index.js' describe(`@roots/bud-framework`, () => { + it(`should have a Bud export`, () => { + expect(Bud).toBeInstanceOf(Function) + }) + it(`should have a Service export`, () => { expect(Service).toBeInstanceOf(Function) }) diff --git a/sources/@roots/bud-framework/test/services/notifier/index.test.ts b/sources/@roots/bud-framework/test/notifier.test.ts similarity index 89% rename from sources/@roots/bud-framework/test/services/notifier/index.test.ts rename to sources/@roots/bud-framework/test/notifier.test.ts index 31c9f010d6..43b9c489f9 100644 --- a/sources/@roots/bud-framework/test/services/notifier/index.test.ts +++ b/sources/@roots/bud-framework/test/notifier.test.ts @@ -2,7 +2,7 @@ import {Bud, factory} from '@repo/test-kit' import nodeNotifier from 'node-notifier' import {beforeEach, describe, expect, it, vi} from 'vitest' -import Notifier from '../../../src/services/notifier/index.js' +import {Notifier} from '../src/notifier.js' describe(`notifier`, () => { let notifier: Notifier @@ -14,7 +14,7 @@ describe(`notifier`, () => { bud = await factory({notify: true}) notifier = new Notifier(() => bud) - await notifier.boot(bud) + await notifier.make(bud) notifier.notify = vi.fn() }) diff --git a/sources/@roots/bud-framework/test/services/console.test.ts b/sources/@roots/bud-framework/test/services/console.test.ts deleted file mode 100644 index fed8fb3140..0000000000 --- a/sources/@roots/bud-framework/test/services/console.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {factory} from '@repo/test-kit' -import {beforeEach, describe, expect, it} from 'vitest' -import ConsoleBuffer from '../../src/services/console.js' - -describe(`ConsoleBuffer`, () => { - let bud: any - let consoleBuffer: ConsoleBuffer - - beforeEach(async () => { - bud = await factory() - consoleBuffer = new ConsoleBuffer(() => bud as any) - }) - - it(`should not patch the console in ci`, async () => { - bud.context.ci = true - - await consoleBuffer.register(bud) - expect(consoleBuffer.restore).not.toBeDefined() - }) - - it(`should store console.log to consoleBuffer.queue`, async () => { - await consoleBuffer.register(bud) - - console.log(`log-test`) - console.info(`info-test`) - console.error(`error-test`) - console.warn(`warn-test`) - - expect(consoleBuffer.queue).toMatchSnapshot() - }) -}) diff --git a/sources/@roots/bud-imagemin/src/types/index.ts b/sources/@roots/bud-imagemin/src/types/index.ts index 35f40aaaad..afad127515 100644 --- a/sources/@roots/bud-imagemin/src/types/index.ts +++ b/sources/@roots/bud-imagemin/src/types/index.ts @@ -1,5 +1,3 @@ -/// - import type {PublicExtensionApi} from '@roots/bud-framework/extension' import type BudImageminExtension from '../extension/index.js' diff --git a/sources/@roots/bud-imagemin/tsconfig.json b/sources/@roots/bud-imagemin/tsconfig.json index 26774d7a75..72811718ed 100644 --- a/sources/@roots/bud-imagemin/tsconfig.json +++ b/sources/@roots/bud-imagemin/tsconfig.json @@ -7,10 +7,11 @@ "@roots/bud-imagemin": ["./src/index.ts"], "@roots/bud-imagemin/sharp": ["./src/sharp/index.ts"], "@roots/bud-imagemin/svgo": ["./src/svgo/index.ts"], - } + }, + "types": ["@roots/bud-framework", "@roots/bud-extensions"] }, "include": ["src"], - "exclude": ["./lib", "./node_modules", "**/*.test.ts", "**/*.test.tsx"], + "exclude": ["**/*.test.ts", "**/*.test.tsx"], "references": [ {"path": "../bud-framework/tsconfig.json"}, diff --git a/sources/@roots/bud-postcss/package.json b/sources/@roots/bud-postcss/package.json index bcb495fc57..2a778d91c2 100644 --- a/sources/@roots/bud-postcss/package.json +++ b/sources/@roots/bud-postcss/package.json @@ -76,7 +76,6 @@ "@types/postcss-import": "14.0.0" }, "dependencies": { - "@roots/bud": "workspace:*", "@roots/bud-build": "workspace:*", "@roots/bud-framework": "workspace:*", "@roots/bud-support": "workspace:*", diff --git a/sources/@roots/bud-postcss/src/types.ts b/sources/@roots/bud-postcss/src/types.ts index ae1d6f4a36..3cec37aa2f 100644 --- a/sources/@roots/bud-postcss/src/types.ts +++ b/sources/@roots/bud-postcss/src/types.ts @@ -1,5 +1,3 @@ -/// - /* eslint-disable n/no-unpublished-import */ import type {Item} from '@roots/bud-build/item' import type {Loader} from '@roots/bud-build/loader' diff --git a/sources/@roots/bud-postcss/tsconfig.json b/sources/@roots/bud-postcss/tsconfig.json index edf4c84173..bf8b9ce999 100644 --- a/sources/@roots/bud-postcss/tsconfig.json +++ b/sources/@roots/bud-postcss/tsconfig.json @@ -3,11 +3,12 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib", + "types": ["node", "@roots/bud-framework", "@roots/bud-api", "@roots/bud-build"] }, "include": ["./src"], - "exclude": ["./lib", "./node_modules", "**/*.test.ts", "**/*.test.tsx"], + "exclude": ["**/*.test.ts", "**/*.test.tsx"], "references": [ - {"path": "../bud/tsconfig.json"}, + {"path": "../bud-build/tsconfig.json"}, {"path": "../bud-framework/tsconfig.json"}, {"path": "../bud-support/tsconfig.json"}, ] diff --git a/sources/@roots/bud-preset-recommend/package.json b/sources/@roots/bud-preset-recommend/package.json index 97ebe5bcba..1b8e15641f 100644 --- a/sources/@roots/bud-preset-recommend/package.json +++ b/sources/@roots/bud-preset-recommend/package.json @@ -47,22 +47,12 @@ ], "type": "module", "exports": { - ".": { - "import": "./lib/index.js", - "default": "./lib/index.js" - }, - "./types": { - "import": "./lib/types.js", - "default": "./lib/types.js" - } + ".": "./lib/index.js" }, "typesVersions": { "*": { ".": [ "./lib/index.d.ts" - ], - "types": [ - "./lib/types.d.ts" ] } }, @@ -71,6 +61,7 @@ "devDependencies": { "@roots/bud-esbuild": "workspace:*", "@roots/bud-swc": "workspace:*", + "@roots/bud-typescript": "workspace:*", "@skypack/package-check": "0.2.2", "@types/node": "18.16.16" }, diff --git a/sources/@roots/bud-preset-recommend/src/types.ts b/sources/@roots/bud-preset-recommend/src/types.ts deleted file mode 100644 index 24feef1ace..0000000000 --- a/sources/@roots/bud-preset-recommend/src/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type BudPresetRecommend from './extension.js' - -declare module '@roots/bud-framework' { - interface Modules { - '@roots/bud-preset-recommend': BudPresetRecommend - } -} diff --git a/sources/@roots/bud-preset-recommend/tsconfig.json b/sources/@roots/bud-preset-recommend/tsconfig.json index 0f2ce4ed00..2cd5127893 100644 --- a/sources/@roots/bud-preset-recommend/tsconfig.json +++ b/sources/@roots/bud-preset-recommend/tsconfig.json @@ -10,14 +10,19 @@ "@roots/bud-extensions", "@roots/bud-esbuild", "@roots/bud-swc", - "@roots/bud-typescript", - "@roots/bud-postcss" + "@roots/bud-postcss", + "@roots/bud-typescript" ] }, "include": ["src"], "exclude": ["test"], "references": [ + {"path": "./../bud-babel/tsconfig.json"}, + {"path": "./../bud-esbuild/tsconfig.json"}, + {"path": "./../bud-swc/tsconfig.json"}, + {"path": "./../bud-postcss/tsconfig.json"}, {"path": "./../bud-extensions/tsconfig.json"}, - {"path": "./../bud-framework/tsconfig.json"} + {"path": "./../bud-framework/tsconfig.json"}, + {"path": "./../bud-typescript/tsconfig.json"} ] } diff --git a/sources/@roots/bud-preset-wordpress/package.json b/sources/@roots/bud-preset-wordpress/package.json index aab73a9974..b25f2efeb1 100644 --- a/sources/@roots/bud-preset-wordpress/package.json +++ b/sources/@roots/bud-preset-wordpress/package.json @@ -49,30 +49,11 @@ ], "type": "module", "exports": { - ".": { - "import": "./lib/index.js", - "default": "./lib/index.js" - }, - "./extension": { - "import": "./lib/extension.js", - "default": "./lib/extension.js" - }, - "./stylelint-config": { - "require": "./config/stylelint.cjs", - "default": "./config/stylelint.cjs" - }, - "./stylelint": { - "require": "./config/stylelint.cjs", - "default": "./config/stylelint.cjs" - }, - "./config/stylelint": { - "require": "./config/stylelint.cjs", - "default": "./config/stylelint.cjs" - }, - "./types": { - "import": "./lib/types.js", - "default": "./lib/types.js" - } + ".": "./lib/index.js", + "./extension": "./lib/extension.js", + "./stylelint-config": "./config/stylelint.cjs", + "./stylelint": "./config/stylelint.cjs", + "./config/stylelint": "./config/stylelint.cjs" }, "typesVersions": { "*": { @@ -81,12 +62,6 @@ ], "extension": [ "./lib/extension.d.ts" - ], - "theme": [ - "./lib/theme.d.ts" - ], - "types": [ - "./lib/types.d.ts" ] } }, diff --git a/sources/@roots/bud-preset-wordpress/src/index.ts b/sources/@roots/bud-preset-wordpress/src/index.ts index 819566730b..548c338d76 100644 --- a/sources/@roots/bud-preset-wordpress/src/index.ts +++ b/sources/@roots/bud-preset-wordpress/src/index.ts @@ -8,4 +8,24 @@ * @see https://github.com/roots/bud */ -export {default} from './extension.js' +import type {PublicExtensionApi} from '@roots/bud-framework/extension' + +import BudPresetWordPress from '@roots/bud-preset-wordpress/extension' + +declare module '@roots/bud-framework' { + interface Bud { + wp: PublicExtensionApi + } + + interface Modules { + '@roots/bud-preset-wordpress': BudPresetWordPress + } + interface Loaders { + '@roots/wordpress-hmr/loader': any + } + interface Items { + '@roots/wordpress-hmr/loader': any + } +} + +export {BudPresetWordPress as default} diff --git a/sources/@roots/bud-preset-wordpress/src/types.ts b/sources/@roots/bud-preset-wordpress/src/types.ts deleted file mode 100644 index d68c346f89..0000000000 --- a/sources/@roots/bud-preset-wordpress/src/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type {PublicExtensionApi} from '@roots/bud-framework/extension' - -import type BudPresetWordPress from './extension.js' - -declare module '@roots/bud-framework' { - interface Bud { - wp: PublicExtensionApi - } - - interface Modules { - '@roots/bud-preset-wordpress': BudPresetWordPress - } - interface Loaders { - '@roots/wordpress-hmr/loader': any - } - interface Items { - '@roots/wordpress-hmr/loader': any - } -} diff --git a/sources/@roots/bud-preset-wordpress/tsconfig.json b/sources/@roots/bud-preset-wordpress/tsconfig.json index 447a5ad873..c1992560fa 100644 --- a/sources/@roots/bud-preset-wordpress/tsconfig.json +++ b/sources/@roots/bud-preset-wordpress/tsconfig.json @@ -14,7 +14,6 @@ "@roots/bud-tailwindcss-theme-json" ] }, - "files": ["./src/types.ts"], "include": ["src"], "exclude": ["test"], "references": [ diff --git a/sources/@roots/bud-purgecss/src/api.ts b/sources/@roots/bud-purgecss/src/api.ts index d5c01832d3..a0fea6d7af 100644 --- a/sources/@roots/bud-purgecss/src/api.ts +++ b/sources/@roots/bud-purgecss/src/api.ts @@ -1,4 +1,4 @@ -import type {Bud} from '@roots/bud' +import type {Bud} from '@roots/bud-framework' /** * Purge unused CSS from compiled stylesheets diff --git a/sources/@roots/bud-purgecss/src/index.ts b/sources/@roots/bud-purgecss/src/index.ts index f0f22f7aa5..2c4978ff42 100644 --- a/sources/@roots/bud-purgecss/src/index.ts +++ b/sources/@roots/bud-purgecss/src/index.ts @@ -2,22 +2,24 @@ // Licensed under the MIT license. /** - * Adds purgecss support to Bud - * - * @example - * ```ts - * app.purge({ - * content: [app.path('resources/views/**')], - * allow: require('purgecss-with-wordpress').whitelist, - * allowPatterns: require('purgecss-with-wordpress').whitelistPatterns, - * }) - * ``` + * @roots/bud-purgecss * * @see https://bud.js.org * @see https://github.com/roots/bud */ +import type {purgecss} from './api.js' + import BudPurgeCSS from './extension.js' -import './types.js' -export default BudPurgeCSS +declare module '@roots/bud-framework' { + interface Bud { + purgecss: typeof purgecss + } + + interface Modules { + '@roots/bud-purgecss': BudPurgeCSS + } +} + +export {BudPurgeCSS as default} diff --git a/sources/@roots/bud-purgecss/src/types.ts b/sources/@roots/bud-purgecss/src/types.ts deleted file mode 100644 index c051849724..0000000000 --- a/sources/@roots/bud-purgecss/src/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// - -import type {purgecss} from './api.js' -import type BudPurgeCSS from './extension.js' - -declare module '@roots/bud-framework' { - interface Bud { - purgecss: typeof purgecss - } - - interface Modules { - '@roots/bud-purgecss': BudPurgeCSS - } -} diff --git a/sources/@roots/bud-purgecss/tsconfig.json b/sources/@roots/bud-purgecss/tsconfig.json index e8ad323af9..01a1e333ef 100644 --- a/sources/@roots/bud-purgecss/tsconfig.json +++ b/sources/@roots/bud-purgecss/tsconfig.json @@ -3,18 +3,13 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib", + "types": ["@roots/bud-framework", "@roots/bud-postcss"] }, "include": ["src"], - "exclude": ["lib", "types", "node_modules", "**/*.test.ts"], + "exclude": ["**/*.test.ts"], "references": [ - { - "path": "./../bud-framework/tsconfig.json" - }, - { - "path": "./../bud-api/tsconfig.json" - }, - { - "path": "./../bud-postcss/tsconfig.json" - } + {"path": "./../bud-framework/tsconfig.json"}, + {"path": "./../bud-api/tsconfig.json"}, + {"path": "./../bud-postcss/tsconfig.json"} ] } diff --git a/sources/@roots/bud-sass/package.json b/sources/@roots/bud-sass/package.json index 98bcad0827..e62df41e94 100644 --- a/sources/@roots/bud-sass/package.json +++ b/sources/@roots/bud-sass/package.json @@ -106,7 +106,6 @@ "webpack": "5.86.0" }, "dependencies": { - "@roots/bud": "workspace:*", "@roots/bud-framework": "workspace:*", "@roots/bud-support": "workspace:*", "@types/sass-loader": "8.0.5", diff --git a/sources/@roots/bud-sass/src/types.ts b/sources/@roots/bud-sass/src/types.ts index baee2a0a9c..16e643c990 100644 --- a/sources/@roots/bud-sass/src/types.ts +++ b/sources/@roots/bud-sass/src/types.ts @@ -1,6 +1,4 @@ -/// - -import type {Build} from '@roots/bud-framework/services' +import type {Item, Loader, Rule} from '@roots/bud-framework' import type {BudSass, BudSassApi} from './extension.js' import type {BudResolveUrl} from './resolve-url/extension.js' @@ -16,41 +14,16 @@ declare module '@roots/bud-framework' { } interface Loaders { - 'resolve-url': Build.Loader - sass: Build.Loader - } - - interface Items { - 'resolve-url': Build.Item - sass: Build.Item - } - - interface Rules { - sass: Build.Rule - } -} - -declare module '@roots/bud' { - interface Bud { - sass: BudSassApi - } - - interface Modules { - '@roots/bud-sass': BudSass - '@roots/bud-sass/resolve-url': BudResolveUrl - } - - interface Loaders { - 'resolve-url': Build.Loader - sass: Build.Loader + 'resolve-url': Loader + sass: Loader } interface Items { - 'resolve-url': Build.Item - sass: Build.Item + 'resolve-url': Item + sass: Item } interface Rules { - sass: Build.Rule + sass: Rule } } diff --git a/sources/@roots/bud-sass/tsconfig.json b/sources/@roots/bud-sass/tsconfig.json index 8faf911fb0..ebfe6a6d09 100644 --- a/sources/@roots/bud-sass/tsconfig.json +++ b/sources/@roots/bud-sass/tsconfig.json @@ -3,12 +3,11 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib", - "types": ["node", "@roots/bud", "@roots/bud-framework", "@roots/bud-build", "@roots/bud-postcss"] + "types": ["node", "@roots/bud-framework", "@roots/bud-build", "@roots/bud-postcss"] }, "include": ["src"], "exclude": ["**/*.test.ts", "*.test.ts"], "references": [ - {"path": "../bud/tsconfig.json"}, {"path": "../bud-build/tsconfig.json"}, {"path": "../bud-framework/tsconfig.json"}, {"path": "../bud-postcss/tsconfig.json"}, diff --git a/sources/@roots/bud-server/package.json b/sources/@roots/bud-server/package.json index 663bf629e6..4b217808b7 100644 --- a/sources/@roots/bud-server/package.json +++ b/sources/@roots/bud-server/package.json @@ -95,7 +95,8 @@ "module": "./lib/index.js", "devDependencies": { "@roots/bud-api": "workspace:*", - "@skypack/package-check": "0.2.2" + "@skypack/package-check": "0.2.2", + "ink-testing-library": "3.0.0" }, "dependencies": { "@roots/bud-client": "workspace:*", diff --git a/sources/@roots/bud-server/src/index.ts b/sources/@roots/bud-server/src/index.ts index eeb9b8a482..42bec3e6f9 100644 --- a/sources/@roots/bud-server/src/index.ts +++ b/sources/@roots/bud-server/src/index.ts @@ -7,4 +7,10 @@ import {Server} from '@roots/bud-server/service' +declare module '@roots/bud-framework' { + interface Services { + server: Server + } +} + export default Server diff --git a/sources/@roots/bud-server/src/middleware/proxy/middleware.test.ts b/sources/@roots/bud-server/src/middleware/proxy/middleware.test.ts index f1e077cc17..2bbe921422 100644 --- a/sources/@roots/bud-server/src/middleware/proxy/middleware.test.ts +++ b/sources/@roots/bud-server/src/middleware/proxy/middleware.test.ts @@ -1,5 +1,5 @@ import {Bud, factory} from '@repo/test-kit' -import logger from '@roots/bud-support/utilities/logger' +import logger from '@roots/bud-support/logger' import {beforeEach, describe, expect, it} from 'vitest' import * as middleware from './factory.js' diff --git a/sources/@roots/bud-server/src/server/base.ts b/sources/@roots/bud-server/src/server/base.ts index f863b017b5..cab9bbc6a9 100644 --- a/sources/@roots/bud-server/src/server/base.ts +++ b/sources/@roots/bud-server/src/server/base.ts @@ -1,6 +1,8 @@ import type {Bud} from '@roots/bud-framework' -import type {Server} from '@roots/bud-framework/services' -import type {Connection} from '@roots/bud-framework/services/server' +import type { + Connection, + Options, +} from '@roots/bud-framework/services/server' import type { Server as HttpServer, IncomingMessage, @@ -98,7 +100,7 @@ export abstract class BaseServer implements Connection { /** * Options */ - public get options(): Server.Options { + public get options(): Options { return this.app.hooks.filter(`dev.options`, {}) } diff --git a/sources/@roots/bud-server/src/server/watcher.ts b/sources/@roots/bud-server/src/server/watcher.ts index 27ec8b9435..462fb3dc58 100644 --- a/sources/@roots/bud-server/src/server/watcher.ts +++ b/sources/@roots/bud-server/src/server/watcher.ts @@ -1,5 +1,5 @@ import type {Bud} from '@roots/bud-framework' -import type {Server} from '@roots/bud-framework/services' +import type {Watcher as BudWatcher} from '@roots/bud-framework/services/server' import type {FSWatcher} from 'node:fs' import * as chokidar from '@roots/bud-support/chokidar' @@ -8,7 +8,7 @@ import {bind} from '@roots/bud-support/decorators/bind' /** * FS Watcher */ -export class Watcher implements Server.Watcher { +export class Watcher implements BudWatcher { /** * Watch files */ diff --git a/sources/@roots/bud-server/src/service/index.ts b/sources/@roots/bud-server/src/service/index.ts index 8f677e6a9d..a85dcadf08 100644 --- a/sources/@roots/bud-server/src/service/index.ts +++ b/sources/@roots/bud-server/src/service/index.ts @@ -1,10 +1,10 @@ +import type {Server as BudServer} from '@roots/bud-framework' import type {Bud} from '@roots/bud-framework' import type { - Service as BaseService, Connection, Middleware, + Watcher, } from '@roots/bud-framework/services/server' -import type {Watcher} from '@roots/bud-server/server/watcher' import {Service} from '@roots/bud-framework/service' import {inject} from '@roots/bud-server/inject' @@ -14,7 +14,7 @@ import {BudError, ServerError} from '@roots/bud-support/errors' /** * Server service class */ -export class Server extends Service implements BaseService { +export class Server extends Service implements BudServer { /** * Express instance */ @@ -83,7 +83,7 @@ export class Server extends Service implements BaseService { /** * Utilized middleware */ - public get enabledMiddleware(): BaseService['enabledMiddleware'] { + public get enabledMiddleware(): BudServer['enabledMiddleware'] { return this.app.hooks.filter(`dev.middleware.enabled`, [])?.reduce( (enabled, key) => ({ ...enabled, @@ -159,7 +159,7 @@ export class Server extends Service implements BaseService { bud.hooks.action(`server.before`, async () => { await this.setConnection() await this.injectScripts() - await bud.compiler.compile() + await bud.compiler.compile(bud) await this.applyMiddleware() await this.watcher.watch() }) diff --git a/sources/@roots/bud-stylelint/src/api.ts b/sources/@roots/bud-stylelint/src/api.ts index e5cab95b80..6dfa2aaec5 100644 --- a/sources/@roots/bud-stylelint/src/api.ts +++ b/sources/@roots/bud-stylelint/src/api.ts @@ -52,7 +52,7 @@ export type Api = PublicExtensionApi & { ({context, env}) => !context.ci && !env.isTrue(`CI`), ), cacheLocation: DynamicOption.make(({cache, path}) => - path(cache.cacheDirectory, `stylelint`), + path(cache.cacheDirectory, `stylelint.json`), ), config: DynamicOption.make( ({context}) => diff --git a/sources/@roots/bud-support/package.json b/sources/@roots/bud-support/package.json index 0f4605a681..279566f805 100644 --- a/sources/@roots/bud-support/package.json +++ b/sources/@roots/bud-support/package.json @@ -122,6 +122,8 @@ "human-readable": "0.2.1", "import-meta-resolve": "2.2.2", "ink": "4.2.0", + "ink-big-text": "2.0.0", + "ink-gradient": "3.0.0", "ink-spinner": "5.0.0", "ink-text-input": "5.0.1", "json5": "2.2.3", diff --git a/sources/@roots/bud-support/src/errors/errors.ts b/sources/@roots/bud-support/src/errors/errors.ts index 208ddde77d..e9c4bbcf2b 100644 --- a/sources/@roots/bud-support/src/errors/errors.ts +++ b/sources/@roots/bud-support/src/errors/errors.ts @@ -59,52 +59,32 @@ const BudError = BudBaseError.subclass(`BudError`, { custom: BudHandler, }) -const CLIError = BudError.subclass(`CLIError`) - const ModuleError = BudBaseError.subclass(`ModuleError`, { custom: BudHandler, - props: { - details: `Error accessing, writing to, importing or resolving a module.`, - issues: new URL(`https://github.com/roots/bud/issues`), - }, }) -const ConfigError = BudError.subclass(`ConfigurationError`, { - props: { - details: `Error processing a project configuration file`, - docs: new URL(`https://bud.js.org`), - }, +const ConfigError = BudBaseError.subclass(`ConfigurationError`, { + custom: BudHandler, }) -const InputError = BudError.subclass(`InputError`, { - props: { - details: `Error stemming from user input`, - docs: new URL(`https://bud.js.org`), - }, + +const InputError = BudBaseError.subclass(`InputError`, { + custom: BudHandler, }) -const CompilerError = BudError.subclass(`CompilerError`, { - props: { - details: `Error running the compiler instance.`, - docs: new URL(`https://bud.js.org`), - issues: new URL(`https://github.com/roots/bud/issues`), - }, + +const CompilerError = BudBaseError.subclass(`CompilerError`, { + custom: BudHandler, }) -const ServerError = BudError.subclass(`ServerError`, { - props: { - details: `Error in the bud.js development server`, - docs: new URL(`https://bud.js.org/docs/bud.serve`), - }, + +const ServerError = BudBaseError.subclass(`ServerError`, { + custom: BudHandler, }) -const ExtensionError = BudError.subclass(`ExtensionError`, { - props: { - details: `Error in an extension`, - docs: new URL(`https://bud.js.org`), - }, +const ExtensionError = BudBaseError.subclass(`ExtensionError`, { + custom: BudHandler, }) export { BudError, BudHandler, - CLIError, CompilerError, ConfigError, ExtensionError, diff --git a/sources/@roots/bud-support/src/ink/index.ts b/sources/@roots/bud-support/src/ink/index.ts index 11965db275..c556af8ed1 100644 --- a/sources/@roots/bud-support/src/ink/index.ts +++ b/sources/@roots/bud-support/src/ink/index.ts @@ -5,6 +5,8 @@ export { default, forwardRef, memo, + type PropsWithChildren, + type ReactElement, useCallback, useContext, useDebugValue, @@ -21,6 +23,10 @@ export { useSyncExternalStore, useTransition, } from 'react' + export * from 'ink' + +export {default as BigText} from 'ink-big-text' +export {default as Gradient} from 'ink-gradient' export {default as Spinner} from 'ink-spinner' export {default as TextInput} from 'ink-text-input' diff --git a/sources/@roots/bud-support/src/logger/index.ts b/sources/@roots/bud-support/src/logger/index.ts index 3cc0cc69d5..186f00146a 100644 --- a/sources/@roots/bud-support/src/logger/index.ts +++ b/sources/@roots/bud-support/src/logger/index.ts @@ -1,3 +1,116 @@ -import logger from '../utilities/logger.js' +/* eslint-disable n/no-process-env */ +import type {SignaleOptions} from 'signale' -export default logger +import {bind} from '@roots/bud-support/decorators/bind' +import isUndefined from '@roots/bud-support/lodash/isUndefined' +import args from '@roots/bud-support/utilities/args' +import Signale from 'signale' + +class Logger { + public instance: Signale.Signale + + public constructor() { + let options: SignaleOptions = {} + + if (args.log === false) options.disabled = true + options.logLevel = args.verbose ? `info` : args.log ? `log` : `warn` + + if (process.env) { + options.secrets = Object.entries(process.env) + .filter( + ( + entry: [string, string | undefined], + ): entry is [string, string] => + !isUndefined(entry[1]) && entry[0].includes(`SECRET`), + ) + .map(([k, v]): string => v) + } + + this.instance = new Signale.Signale(options) + this.instance.config({displayLabel: false}) + if (args.silent && !args.log && !args.verbose) { + this.instance.disable() + } + } + + @bind + public await(...messages: Array) { + this.instance.await(...messages) + return this + } + + @bind + public debug(...messages: Array) { + if (!(`verbose` in args)) return this + this.instance.debug(...messages) + return this + } + + @bind + public error(...messages: Array) { + this.instance.error(...messages) + return this + } + + @bind + public info(...messages: Array) { + if (!(`verbose` in args)) return this + this.instance.info(...messages) + return this + } + + @bind + public log(...messages: Array) { + this.instance.log(...messages) + return this + } + + @bind + public scope(...scopes: Array) { + if (scopes.length === 0) return this + this.instance = this.instance.scope( + ...(scopes.filter(Boolean) ?? [`bud.js`]), + ) + return this + } + @bind + public success(...messages: Array) { + this.instance.success(...messages) + return this + } + + @bind + public time(label: string = `default`) { + this.instance.time(label) + return this + } + + @bind + public timeEnd(label: string = `default`) { + this.instance.timeEnd(label) + return this + } + + @bind + public unscope() { + this.instance.unscope() + return this + } + + @bind + public warn(...messages: Array) { + this.instance.warn(...messages) + return this + } +} + +let instance = undefined + +export const initialize = () => { + instance = new Logger() + instance.log(`logger initialized`) + return instance +} + +export default instance ?? initialize() +export {instance, Logger} diff --git a/sources/@roots/bud-support/src/utilities/env.ts b/sources/@roots/bud-support/src/utilities/env.ts index 94163cc632..0bfe5e204a 100644 --- a/sources/@roots/bud-support/src/utilities/env.ts +++ b/sources/@roots/bud-support/src/utilities/env.ts @@ -1,8 +1,8 @@ /* eslint-disable n/no-process-env */ +import logger from '@roots/bud-support/logger' import {join, sep} from 'node:path' import {dotenv, dotenvExpand} from '../dotenv/index.js' -import logger from './logger.js' let env: Record = {} diff --git a/sources/@roots/bud-support/src/utilities/files.ts b/sources/@roots/bud-support/src/utilities/files.ts index 9c4a6a0c6c..59f0792641 100644 --- a/sources/@roots/bud-support/src/utilities/files.ts +++ b/sources/@roots/bud-support/src/utilities/files.ts @@ -6,8 +6,8 @@ import _get from '@roots/bud-support/lodash/get' import isEqual from '@roots/bud-support/lodash/isEqual' import omit from '@roots/bud-support/lodash/omit' import _set from '@roots/bud-support/lodash/set' +import logger from '@roots/bud-support/logger' import * as filesystem from '@roots/bud-support/utilities/filesystem' -import logger from '@roots/bud-support/utilities/logger' import {get as getPaths} from '@roots/bud-support/utilities/paths' import {dirname, join, parse} from 'node:path' diff --git a/sources/@roots/bud-support/src/utilities/filesystem.ts b/sources/@roots/bud-support/src/utilities/filesystem.ts index 57b0a2a1c9..c0ef08b826 100644 --- a/sources/@roots/bud-support/src/utilities/filesystem.ts +++ b/sources/@roots/bud-support/src/utilities/filesystem.ts @@ -1,9 +1,8 @@ +import {BudError} from '@roots/bud-support/errors' +import {Filesystem} from '@roots/bud-support/filesystem' +import {paths} from '@roots/bud-support/utilities/paths' import {isMainThread} from 'node:worker_threads' -import {BudError} from '../errors/errors.js' -import {Filesystem} from '../filesystem/index.js' -import {paths} from './paths.js' - let filesystem: Filesystem export const get = (basedir?: string) => { diff --git a/sources/@roots/bud-support/src/utilities/logger.ts b/sources/@roots/bud-support/src/utilities/logger.ts deleted file mode 100644 index 4f8d07c6f2..0000000000 --- a/sources/@roots/bud-support/src/utilities/logger.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* eslint-disable n/no-process-env */ -import type {SignaleOptions} from 'signale' - -import Signale from 'signale' - -import {bind} from '../decorators/bind.js' -import isUndefined from '../lodash/isUndefined/index.js' -import args from './args.js' - -class Logger { - public instance: Signale.Signale - - public constructor() { - let options: SignaleOptions = {} - - if (args.log === false) options.disabled = true - options.logLevel = args.verbose ? `info` : args.log ? `log` : `warn` - - if (process.env) { - options.secrets = Object.entries(process.env) - .filter( - ( - entry: [string, string | undefined], - ): entry is [string, string] => - !isUndefined(entry[1]) && entry[0].includes(`SECRET`), - ) - .map(([k, v]): string => v) - } - - this.instance = new Signale.Signale(options) - this.instance.config({displayLabel: false}) - if (args.silent && !args.log && !args.verbose) { - this.instance.disable() - } - } - - @bind - public await(...messages: Array) { - this.instance.await(...messages) - return this - } - - @bind - public debug(...messages: Array) { - if (!(`verbose` in args)) return this - this.instance.debug(...messages) - return this - } - - @bind - public error(...messages: Array) { - this.instance.error(...messages) - return this - } - - @bind - public info(...messages: Array) { - if (!(`verbose` in args)) return this - this.instance.info(...messages) - return this - } - - @bind - public log(...messages: Array) { - this.instance.log(...messages) - return this - } - - @bind - public scope(...scopes: Array) { - if (scopes.length === 0) return this - this.instance = this.instance.scope( - ...(scopes.filter(Boolean) ?? [`bud.js`]), - ) - return this - } - @bind - public success(...messages: Array) { - this.instance.success(...messages) - return this - } - - @bind - public time(label: string = `default`) { - this.instance.time(label) - return this - } - - @bind - public timeEnd(label: string = `default`) { - this.instance.timeEnd(label) - return this - } - - @bind - public unscope() { - this.instance.unscope() - return this - } - - @bind - public warn(...messages: Array) { - this.instance.warn(...messages) - return this - } -} - -let instance = undefined - -export const initialize = () => { - instance = new Logger() - instance.log(`logger initialized`) - return instance -} - -export default instance ?? initialize() -export {instance, Logger} diff --git a/sources/@roots/bud-support/src/utilities/paths.ts b/sources/@roots/bud-support/src/utilities/paths.ts index 07d47ac71e..9789ff9ce1 100644 --- a/sources/@roots/bud-support/src/utilities/paths.ts +++ b/sources/@roots/bud-support/src/utilities/paths.ts @@ -1,11 +1,10 @@ +import {BudError} from '@roots/bud-support/errors' +import args from '@roots/bud-support/utilities/args' +import * as envBootstrap from '@roots/bud-support/utilities/env' import envPaths from 'env-paths' import {createHash} from 'node:crypto' import {join, normalize} from 'node:path' -import {BudError} from '../errors/errors.js' -import args from './args.js' -import * as envBootstrap from './env.js' - const systemPaths = envPaths(`bud`) let env: ReturnType diff --git a/sources/@roots/bud-support/src/webpack/index.ts b/sources/@roots/bud-support/src/webpack/index.ts index e8669422bb..9457e49271 100644 --- a/sources/@roots/bud-support/src/webpack/index.ts +++ b/sources/@roots/bud-support/src/webpack/index.ts @@ -13,6 +13,7 @@ export type { PathData, ProvidePlugin, RuleSetRule, + Stats, StatsAsset, StatsChunkGroup, StatsCompilation, diff --git a/sources/@roots/bud-swc/src/extension/index.ts b/sources/@roots/bud-swc/src/extension/index.ts index 297d9324ee..a7f97b595e 100644 --- a/sources/@roots/bud-swc/src/extension/index.ts +++ b/sources/@roots/bud-swc/src/extension/index.ts @@ -31,7 +31,7 @@ class BudSWC extends BudSWCApi { * {@link Extension.boot} */ @bind - public override async boot({build, hooks, path, when}) { + public override async boot({build, hooks, when}) { build.getRule(`js`).setUse(() => [`swc-ecmascript`]) when( diff --git a/sources/@roots/bud-swc/src/extension/options.ts b/sources/@roots/bud-swc/src/extension/options.ts index ba4f74f42c..9bddfa28c6 100644 --- a/sources/@roots/bud-swc/src/extension/options.ts +++ b/sources/@roots/bud-swc/src/extension/options.ts @@ -116,25 +116,20 @@ type BudSWCPublicInterface = StrictPublicExtensionApi< env: undefined, exclude: undefined, inlineSourcesContent: undefined, - jsc: { - baseUrl: undefined, + jsc: DynamicOption.make(bud => ({ experimental: { - cacheRoot: undefined, + cacheRoot: bud.path(bud.cache.cacheDirectory, `swc`), plugins: [], }, - externalHelpers: undefined, - keepClassNames: undefined, - loose: undefined, - minify: undefined, parser: { + dynamicImport: true, syntax: `ecmascript`, }, - paths: undefined, - preserveAllComments: undefined, - target: undefined, - transform: undefined, + target: `es2022`, + })), + module: { + type: `es6`, }, - module: undefined, sourceMaps: DynamicOption.make(({hooks}) => { const devtool = hooks.filter(`build.devtool`, false) if (devtool === false) return false diff --git a/sources/@roots/bud-swc/src/index.ts b/sources/@roots/bud-swc/src/index.ts index ec7b57fe1e..81f7b8b28d 100644 --- a/sources/@roots/bud-swc/src/index.ts +++ b/sources/@roots/bud-swc/src/index.ts @@ -8,7 +8,7 @@ * @see https://github.com/roots/bud */ -import type {Build} from '@roots/bud-framework' +import type {Item, Loader, Rule} from '@roots/bud-framework' import {BudSWC, type BudSWCPublicInterface} from './extension/index.js' @@ -22,17 +22,17 @@ declare module '@roots/bud-framework' { } interface Loaders { - swc: Build.Loader + swc: Loader } interface Items { - 'swc-ecmascript': Build.Item - 'swc-typescript': Build.Item + 'swc-ecmascript': Item + 'swc-typescript': Item } interface Rules { - js: Build.Rule - ts: Build.Rule + js: Rule + ts: Rule } } diff --git a/sources/@roots/bud-typescript/package.json b/sources/@roots/bud-typescript/package.json index d58e7c23e4..ceba47ac55 100644 --- a/sources/@roots/bud-typescript/package.json +++ b/sources/@roots/bud-typescript/package.json @@ -85,7 +85,9 @@ "types": "./lib/index.d.ts", "module": "./lib/index.js", "devDependencies": { + "@roots/bud-api": "workspace:*", "@roots/bud-babel": "workspace:*", + "@roots/bud-build": "workspace:*", "@skypack/package-check": "0.2.2", "@types/fork-ts-checker-webpack-plugin": "0.4.5", "@types/node": "18.16.16", diff --git a/sources/@roots/bud-typescript/src/types.ts b/sources/@roots/bud-typescript/src/types.ts index 2c711f647c..f96eb58fac 100644 --- a/sources/@roots/bud-typescript/src/types.ts +++ b/sources/@roots/bud-typescript/src/types.ts @@ -1,7 +1,5 @@ -/// -/// - -import type {Item, Loader, Rule} from '@roots/bud-framework' +// eslint-disable-next-line n/no-unpublished-import +import type {Item, Loader, Rule} from '@roots/bud-build' import type BudTypeScript from './extension.js' import type BudTypeCheckPlugin from './typecheck/index.js' diff --git a/sources/@roots/bud-typescript/tsconfig.json b/sources/@roots/bud-typescript/tsconfig.json index 6e5f9a7150..4282765fa5 100644 --- a/sources/@roots/bud-typescript/tsconfig.json +++ b/sources/@roots/bud-typescript/tsconfig.json @@ -2,12 +2,15 @@ "extends": "../../../config/tsconfig.json", "compilerOptions": { "rootDir": "./src", - "outDir": "./lib" + "outDir": "./lib", + "types": ["node", "@roots/bud-framework", "@roots/bud-build", "@roots/bud-babel", "@roots/bud-api", "@roots/bud"] }, "include": ["./src"], - "exclude": ["./lib", "./node_modules", "**/*.test.ts"], + "exclude": ["**/*.test.ts"], "references": [ {"path": "./../bud/tsconfig.json"}, + {"path": "./../bud-framework/tsconfig.json"}, + {"path": "./../bud-api/tsconfig.json"}, {"path": "./../bud-babel/tsconfig.json"}, {"path": "./../bud-build/tsconfig.json"}, diff --git a/sources/@roots/bud-vue/package.json b/sources/@roots/bud-vue/package.json index e760fbb65f..b1683bb894 100644 --- a/sources/@roots/bud-vue/package.json +++ b/sources/@roots/bud-vue/package.json @@ -70,7 +70,6 @@ "types": "./lib/index.d.ts", "module": "./lib/index.js", "devDependencies": { - "@roots/bud": "workspace:*", "@roots/bud-postcss": "workspace:*", "@roots/bud-sass": "workspace:*", "@roots/bud-typescript": "workspace:*", diff --git a/sources/@roots/bud-vue/src/types.ts b/sources/@roots/bud-vue/src/types.ts index eabadb4a97..65f6e60da1 100644 --- a/sources/@roots/bud-vue/src/types.ts +++ b/sources/@roots/bud-vue/src/types.ts @@ -1,10 +1,5 @@ -/// -/// -/// -/// - +import type {Item, Loader, Rule} from '@roots/bud-framework' import type {Extension} from '@roots/bud-framework/extension' -import type {Build} from '@roots/bud-framework/services' import type Vue from './extension.js' @@ -19,16 +14,16 @@ declare module '@roots/bud-framework' { } interface Loaders { - vue: Build.Loader - 'vue-style': Build.Loader + vue: Loader + 'vue-style': Loader } interface Items { - vue: Build.Item - 'vue-style': Build.Item + vue: Item + 'vue-style': Item } interface Rules { - vue: Build.Rule + vue: Rule } } diff --git a/sources/@roots/bud-vue/tsconfig.json b/sources/@roots/bud-vue/tsconfig.json index 5845df85ee..6bdeea9888 100644 --- a/sources/@roots/bud-vue/tsconfig.json +++ b/sources/@roots/bud-vue/tsconfig.json @@ -2,10 +2,11 @@ "extends": "../../../config/tsconfig.json", "compilerOptions": { "rootDir": "./src", - "outDir": "./lib" + "outDir": "./lib", + "types": ["node", "@roots/bud-framework", "@roots/bud-build", "@roots/bud-postcss", "@roots/bud-sass", "@roots/bud-support", "@roots/bud-api", "@roots/bud-typescript"] }, "include": ["./src"], - "exclude": ["./lib", "./node_modules", "**/*.test.ts"], + "exclude": ["**/*.test.ts"], "references": [ {"path": "./../bud-api/tsconfig.json"}, {"path": "./../bud/tsconfig.json"}, @@ -13,6 +14,7 @@ {"path": "./../bud-framework/tsconfig.json"}, {"path": "./../bud-postcss/tsconfig.json"}, {"path": "./../bud-sass/tsconfig.json"}, - {"path": "./../bud-support/tsconfig.json"} + {"path": "./../bud-support/tsconfig.json"}, + {"path": "./../bud-typescript/tsconfig.json"} ] } diff --git a/sources/@roots/bud-wordpress-theme-json/README.md b/sources/@roots/bud-wordpress-theme-json/README.md index fbaa726d6b..1c8c475a63 100644 --- a/sources/@roots/bud-wordpress-theme-json/README.md +++ b/sources/@roots/bud-wordpress-theme-json/README.md @@ -39,7 +39,7 @@ You can manage [WordPress' `theme.json` config file](https://developer.wordpress In order to emit the file you will need to enable the feature: ```ts title="bud.config.mjs" -bud.wpjson.enable(); +bud.wpjson.enable() ``` ### Managing generic `theme.json` values @@ -47,7 +47,7 @@ bud.wpjson.enable(); You can use `setOption` from the bud.js extensions API to set `theme.json` values: ```ts title="bud.config.mjs" -bud.wpjson.setOption("customTemplates", []).enable(); +bud.wpjson.setOption('customTemplates', []).enable() ``` ### Managing the `settings` field @@ -57,13 +57,13 @@ container interface exposed by `bud.wpjson.settings`. ```ts title="bud.config.mjs" bud.wptheme - .settings((theme) => + .settings(theme => theme - .set("typography.customFontSizes", true) - .set("typography.fontWeight", false) - .merge("spacing.units", ["px", "%", "em"]) + .set('typography.customFontSizes', true) + .set('typography.fontWeight', false) + .merge('spacing.units', ['px', '%', 'em']), ) - .enable(); + .enable() ``` ### Using tailwindcss config values @@ -76,7 +76,7 @@ opt-in config functions that allow you to generate `theme.json` values directly Convert `theme.colors` to a `theme.json` palette. ```ts title="bud.config.mjs" -bud.wpjson.useTailwindColors().enable(); +bud.wpjson.useTailwindColors().enable() ``` #### wpjson.useTailwindFontSize() @@ -84,7 +84,7 @@ bud.wpjson.useTailwindColors().enable(); Emits values from `theme.fontSize` as the `typography.fontSizes` property of `theme.json`. ```ts title="bud.config.mjs" -bud.wpjson.useTailwindFontSize().enable(); +bud.wpjson.useTailwindFontSize().enable() ``` #### wpjson.useTailwindFontFamily() @@ -92,7 +92,7 @@ bud.wpjson.useTailwindFontSize().enable(); Emits values from `theme.fontFamily` as the `typography.fontFamilies` property of `theme.json`. ```ts title="bud.config.mjs" -bud.wpjson.useTailwindFontFamily().enable(); +bud.wpjson.useTailwindFontFamily().enable() ``` #### Limiting values to those defined in `theme.extend` @@ -100,7 +100,7 @@ bud.wpjson.useTailwindFontFamily().enable(); You can pass `true` to any of the the above functions to limit the values emitted to those defined under tailwind's `theme.extend` key. ```ts title="bud.config.mjs" -bud.wpjson.useTailwindColors(true).enable(); +bud.wpjson.useTailwindColors(true).enable() ``` ### In combination @@ -112,8 +112,8 @@ bud.wpjson .useTailwindColors() .useTailwindFontSize() .useTailwindFontFamily() - .setOption("typography.fontWeight", false) - .enable(); + .setOption('typography.fontWeight', false) + .enable() ``` ## Contributing diff --git a/sources/@roots/bud-wordpress-theme-json/package.json b/sources/@roots/bud-wordpress-theme-json/package.json index 52a64f0a93..f43b76ebe2 100644 --- a/sources/@roots/bud-wordpress-theme-json/package.json +++ b/sources/@roots/bud-wordpress-theme-json/package.json @@ -44,22 +44,13 @@ ], "type": "module", "exports": { - ".": { - "import": "./lib/index.js", - "default": "./lib/index.js" - }, - "./types": { - "import": "./lib/types.js", - "default": "./lib/types.js" - } + ".": "./lib/index.js", + "./extension": "./lib/extension.js" }, "typesVersions": { "*": { ".": [ "./lib/index.d.ts" - ], - "./types": [ - "./lib/types.d.ts" ] } }, @@ -70,7 +61,6 @@ "@types/node": "18.16.16" }, "dependencies": { - "@roots/bud": "workspace:*", "@roots/bud-framework": "workspace:*", "@roots/bud-support": "workspace:*", "@roots/container": "workspace:*", diff --git a/sources/@roots/bud-wordpress-theme-json/src/index.ts b/sources/@roots/bud-wordpress-theme-json/src/index.ts index 0f8a6b0a08..ebcaa5bfc6 100644 --- a/sources/@roots/bud-wordpress-theme-json/src/index.ts +++ b/sources/@roots/bud-wordpress-theme-json/src/index.ts @@ -1,27 +1,16 @@ -import type {Api, WordPressThemeJSON} from './extension.js' +import type * as Extension from '@roots/bud-wordpress-theme-json/extension' declare module '@roots/bud-framework' { interface Bud { - wpjson: Api + wpjson: Extension.Api } interface Modules { '@roots/bud-tailwindcss-theme-json?': any - '@roots/bud-wordpress-theme-json': WordPressThemeJSON + '@roots/bud-wordpress-theme-json': Extension.WordPressThemeJSON } } -declare module '@roots/bud' { - interface Bud { - wpjson: Api - } - - interface Modules { - '@roots/bud-tailwindcss-theme-json?': any - '@roots/bud-wordpress-theme-json': WordPressThemeJSON - } -} - -export {WordPressThemeJSON as default} from './extension.js' +export {WordPressThemeJSON as default} from '@roots/bud-wordpress-theme-json/extension' export type {Schema as Theme} from '@roots/wordpress-theme-json-webpack-plugin' diff --git a/sources/@roots/bud-wordpress-theme-json/src/types.ts b/sources/@roots/bud-wordpress-theme-json/src/types.ts deleted file mode 100644 index 03e14878e4..0000000000 --- a/sources/@roots/bud-wordpress-theme-json/src/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type {Api, WordPressThemeJSON} from './extension.js' - -declare module '@roots/bud-framework' { - interface Bud { - wpjson: Api - } - - interface Modules { - '@roots/bud-tailwindcss-theme-json?': any - '@roots/bud-wordpress-theme-json': WordPressThemeJSON - } -} - -declare module '@roots/bud' { - interface Bud { - wpjson: Api - } - - interface Modules { - '@roots/bud-tailwindcss-theme-json?': any - '@roots/bud-wordpress-theme-json': WordPressThemeJSON - } -} diff --git a/sources/@roots/bud-wordpress-theme-json/tsconfig.json b/sources/@roots/bud-wordpress-theme-json/tsconfig.json index 7cf3824adb..3731e8f860 100644 --- a/sources/@roots/bud-wordpress-theme-json/tsconfig.json +++ b/sources/@roots/bud-wordpress-theme-json/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "rootDir": "./src", "outDir": "./lib", - "types": ["@roots/bud-framework", "@roots/bud"] + "types": ["@roots/bud-framework"] }, "include": ["src"], "references": [ diff --git a/sources/@roots/bud/README.md b/sources/@roots/bud/README.md index 53a173b07e..f34d047278 100644 --- a/sources/@roots/bud/README.md +++ b/sources/@roots/bud/README.md @@ -45,9 +45,9 @@ Call `bud --help` for usage information. Instantiate **bud** is using the `factory` export: ```js -import { factory } from "@roots/bud/factory"; +import {factory} from '@roots/bud/factory' -const bud = await factory(); +const bud = await factory() ``` ## Contributing diff --git a/sources/@roots/bud/config/tsconfig.json b/sources/@roots/bud/config/tsconfig.json index 49d628b2c5..3a51940f9e 100644 --- a/sources/@roots/bud/config/tsconfig.json +++ b/sources/@roots/bud/config/tsconfig.json @@ -18,7 +18,6 @@ "target": "esnext", "types": [ "node", - "@roots/bud-framework", "@roots/bud-api", "@roots/bud-build", "@roots/bud-cache", diff --git a/sources/@roots/bud/package.json b/sources/@roots/bud/package.json index 7c47e92264..0e9caa585c 100644 --- a/sources/@roots/bud/package.json +++ b/sources/@roots/bud/package.json @@ -106,8 +106,7 @@ "module": "./lib/index.js", "devDependencies": { "@skypack/package-check": "0.2.2", - "@types/node": "18.16.16", - "@types/react": "18.2.9" + "@types/node": "18.16.16" }, "dependencies": { "@roots/browserslist-config": "workspace:*", diff --git a/sources/@roots/bud/src/bud.ts b/sources/@roots/bud/src/bud.ts index 3708a81ecd..1218a0cae6 100644 --- a/sources/@roots/bud/src/bud.ts +++ b/sources/@roots/bud/src/bud.ts @@ -1,9 +1,86 @@ -import {Service, ServiceContainer} from '@roots/bud-framework' +import type Api from '@roots/bud-api' +import type Build from '@roots/bud-build' +import type Cache from '@roots/bud-cache' +import type Compiler from '@roots/bud-compiler' +import type Dashboard from '@roots/bud-dashboard' +import type Entrypoints from '@roots/bud-entrypoints' +import type Extensions from '@roots/bud-extensions' +import type BudCDN from '@roots/bud-extensions/cdn' +import type CleanWebpackPlugin from '@roots/bud-extensions/clean-webpack-plugin' +import type CopyWebpackPlugin from '@roots/bud-extensions/copy-webpack-plugin' +import type BudESM from '@roots/bud-extensions/esm' +import type BudFixStyleOnlyEntrypoints from '@roots/bud-extensions/fix-style-only-entrypoints' +import type HtmlWebpackPlugin from '@roots/bud-extensions/html-webpack-plugin' +import type BudImportMap from '@roots/bud-extensions/import-map' +import type InterpolateHtmlPlugin from '@roots/bud-extensions/interpolate-html-webpack-plugin' +import type MiniCssExtractPlugin from '@roots/bud-extensions/mini-css-extract-plugin' +import type BudTsConfigValues from '@roots/bud-extensions/tsconfig-values' +import type WebpackDefinePlugin from '@roots/bud-extensions/webpack-define-plugin' +import type WebpackHotModuleReplacementPlugin from '@roots/bud-extensions/webpack-hot-module-replacement-plugin' +import type BudWebpackLifecyclePlugin from '@roots/bud-extensions/webpack-lifecycle-plugin' +import type WebpackManifestPlugin from '@roots/bud-extensions/webpack-manifest-plugin' +import type WebpackProvidePlugin from '@roots/bud-extensions/webpack-provide-plugin' +import type Hooks from '@roots/bud-hooks' +import type Minify from '@roots/bud-minify' +import type Server from '@roots/bud-server' + import * as Framework from '@roots/bud-framework' +declare module '@roots/bud-framework' { + interface Bud { + cdn: BudCDN + esm: BudESM + manifest: WebpackManifestPlugin + tsconfig: BudTsConfigValues + } + + interface Modules { + '@roots/bud-entrypoints': Entrypoints + '@roots/bud-extensions/cdn': BudCDN + '@roots/bud-extensions/clean-webpack-plugin': CleanWebpackPlugin + '@roots/bud-extensions/copy-webpack-plugin': CopyWebpackPlugin + '@roots/bud-extensions/esm': BudESM + '@roots/bud-extensions/fix-style-only-entrypoints': BudFixStyleOnlyEntrypoints + '@roots/bud-extensions/html-webpack-plugin': HtmlWebpackPlugin + '@roots/bud-extensions/import-map': BudImportMap + '@roots/bud-extensions/interpolate-html-webpack-plugin': InterpolateHtmlPlugin + '@roots/bud-extensions/mini-css-extract-plugin': MiniCssExtractPlugin + '@roots/bud-extensions/tsconfig-values': BudTsConfigValues + '@roots/bud-extensions/webpack-define-plugin': WebpackDefinePlugin + '@roots/bud-extensions/webpack-hot-module-replacement-plugin': WebpackHotModuleReplacementPlugin + '@roots/bud-extensions/webpack-lifecycle-plugin': BudWebpackLifecyclePlugin + '@roots/bud-extensions/webpack-manifest-plugin': WebpackManifestPlugin + '@roots/bud-extensions/webpack-provide-plugin': WebpackProvidePlugin + '@roots/bud-minify': Minify + } +} + class Bud extends Framework.Bud { - public override implementation = Bud + public declare api: Api + + public declare build: Build + + public declare cache: Cache + + public declare cdn: BudCDN + + public declare compiler: Compiler + + public declare dashboard: Dashboard + + public declare esm: BudESM + + public declare extensions: Extensions + + public declare hooks: Hooks + + public override implementation: new () => Framework.Bud = Bud + + public declare manifest: WebpackManifestPlugin + + public declare server: Server + + public declare tsconfig: BudTsConfigValues } -export default Bud -export {Service, ServiceContainer} +export {Bud} diff --git a/sources/@roots/bud/src/cli/app.tsx b/sources/@roots/bud/src/cli/app.tsx index 5595bf3d85..d01ccfa6c4 100644 --- a/sources/@roots/bud/src/cli/app.tsx +++ b/sources/@roots/bud/src/cli/app.tsx @@ -2,7 +2,7 @@ import type {CommandClass} from '@roots/bud-support/clipanion' import {Error} from '@roots/bud-dashboard/app' import {Builtins, Cli} from '@roots/bud-support/clipanion' -import {CLIError} from '@roots/bud-support/errors' +import {BudError} from '@roots/bud-support/errors' import {render} from '@roots/bud-support/ink' import logger from '@roots/bud-support/logger' import * as args from '@roots/bud-support/utilities/args' @@ -16,6 +16,7 @@ import BudViewCommand from '@roots/bud/cli/commands/bud.view' import BudWebpackCommand from '@roots/bud/cli/commands/bud.webpack' import {Commands} from '@roots/bud/cli/finder' import getContext, {type Context} from '@roots/bud/context' +import process from 'node:process' import type {CLIContext} from './index.js' @@ -40,40 +41,24 @@ const commands = [ BudWebpackCommand, ] -const isCLIContext = ( - context: Context & { - stderr?: NodeJS.WriteStream - stdin?: NodeJS.ReadStream - stdio?: NodeJS.WriteStream - stdout?: NodeJS.WriteStream - }, -): context is CLIContext => - context.basedir !== undefined && - context.stdout !== undefined && - context.stderr !== undefined && - context.stdin !== undefined - try { context = { ...(await getContext()), colorDepth: 256, - stderr: global.process.stderr, - stdin: global.process.stdin, - stdout: global.process.stdout, + stderr: process.stderr, + stdin: process.stdin, + stdout: process.stdout, } - - if (!isCLIContext(context)) throw `Invalid context` } catch (error) { - render() - global.process.exit(1) + render() } application = new Cli({ binaryLabel: `bud`, binaryName: `bud`, binaryVersion: context.bud.version, - enableCapture: true, - enableColors: true, + enableCapture: false, + enableColors: context.env?.color !== `false`, }) commands.map(command => application.register(command)) @@ -98,7 +83,7 @@ await Commands.get(application, context) application .runExit(args.raw, context) - .catch(error => render()) + .catch(error => render()) export {application, Builtins, Cli} export type {CommandClass} diff --git a/sources/@roots/bud/src/cli/commands/bud.build.development.tsx b/sources/@roots/bud/src/cli/commands/bud.build.development.tsx index c93ed3dca3..ba327b1593 100644 --- a/sources/@roots/bud/src/cli/commands/bud.build.development.tsx +++ b/sources/@roots/bud/src/cli/commands/bud.build.development.tsx @@ -1,4 +1,4 @@ -import type {Context} from '@roots/bud-framework/options' +import type {Context} from '@roots/bud-framework/context' import BuildCommand from '@roots/bud/cli/commands/bud.build' import browser from '@roots/bud/cli/flags/browser' diff --git a/sources/@roots/bud/src/cli/commands/bud.build.tsx b/sources/@roots/bud/src/cli/commands/bud.build.tsx index ffb9d03561..a14bb106cf 100644 --- a/sources/@roots/bud/src/cli/commands/bud.build.tsx +++ b/sources/@roots/bud/src/cli/commands/bud.build.tsx @@ -1,4 +1,4 @@ -import type {Context} from '@roots/bud-framework/options/context' +import type {Context} from '@roots/bud-framework/context' import {Command, Option} from '@roots/bud-support/clipanion' import BudCommand from '@roots/bud/cli/commands/bud' diff --git a/sources/@roots/bud/src/cli/commands/bud.tsx b/sources/@roots/bud/src/cli/commands/bud.tsx index c7e927ddce..47f091a6f7 100644 --- a/sources/@roots/bud/src/cli/commands/bud.tsx +++ b/sources/@roots/bud/src/cli/commands/bud.tsx @@ -1,7 +1,6 @@ -import type {Context} from '@roots/bud-framework/options/context' +import type {Context} from '@roots/bud-framework/context' import type {BaseContext} from '@roots/bud-support/clipanion' -import {Console} from '@roots/bud-dashboard/console' import {Bud} from '@roots/bud-framework' import {Command, Option} from '@roots/bud-support/clipanion' import {bind} from '@roots/bud-support/decorators/bind' @@ -23,10 +22,11 @@ import verbose from '@roots/bud/cli/flags/verbose' import {checkDependencies} from '@roots/bud/cli/helpers/checkDependencies' import {isset} from '@roots/bud/cli/helpers/isset' import * as instance from '@roots/bud/instance' +import process from 'node:process' import type {CLIContext} from '../index.js' -import * as Display from '../components/Error.js' +import * as Fallback from '../components/Error.js' import {Menu} from '../components/Menu.js' export type {BaseContext, Context} @@ -313,7 +313,8 @@ export default class BudCommand extends Command { * Handle errors */ public override async catch(error: BudHandler): Promise { - global.process.exitCode = 1 + process.exitCode = 1 + if (!error.isBudError) error = BudError.normalize(error) if (this.bud?.notifier?.notify) { @@ -329,30 +330,30 @@ export default class BudCommand extends Command { } } - try { - const queuedMessages = - this.bud?.consoleBuffer?.fetchAndRemove() ?? [] - - if (queuedMessages.length) { - await this.renderStatic( - - - , - ) + if (this.bud?.dashboard?.instance) { + this.bud.dashboard.render({error}) + + if (this.bud.isProduction) { + const unmountDashboard = async () => + await this.bud.dashboard.instance.waitUntilExit() + + this.bud.compiler?.instance?.close + ? this.bud.compiler.instance.close(unmountDashboard) + : await unmountDashboard() } - } catch (error) { - logger.warn(error.message ?? error) } await this.renderStatic( - + , ).catch(error => { logger.warn(error.message ?? error) }) - if (this.bud.isProduction) global.process.exit(1) + // fallthrough + // eslint-disable-next-line n/no-process-exit + process.exit() } /** diff --git a/sources/@roots/bud/src/cli/commands/doctor/index.tsx b/sources/@roots/bud/src/cli/commands/doctor/index.tsx index 3404fc7541..240872c168 100644 --- a/sources/@roots/bud/src/cli/commands/doctor/index.tsx +++ b/sources/@roots/bud/src/cli/commands/doctor/index.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/no-unescaped-entities */ import type {Bud} from '@roots/bud' +import type {Context} from '@roots/bud-framework/context' import type {Extension} from '@roots/bud-framework/extension' -import type {Context} from '@roots/bud-framework/options' import type {InspectTreeResult} from 'fs-jetpack/types.js' import {Error} from '@roots/bud-dashboard/app' diff --git a/sources/@roots/bud/src/cli/finder.ts b/sources/@roots/bud/src/cli/finder.ts index 16b3929e72..16f25aab15 100644 --- a/sources/@roots/bud/src/cli/finder.ts +++ b/sources/@roots/bud/src/cli/finder.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import type {Context} from '@roots/bud-framework/options' +import type {Context} from '@roots/bud-framework/context' import {bind} from '@roots/bud-support/decorators/bind' import {resolve} from '@roots/bud-support/import-meta-resolve' diff --git a/sources/@roots/bud/src/cli/index.ts b/sources/@roots/bud/src/cli/index.ts index 732d9c302a..48da9f54d4 100644 --- a/sources/@roots/bud/src/cli/index.ts +++ b/sources/@roots/bud/src/cli/index.ts @@ -1,4 +1,4 @@ -import type {Context} from '@roots/bud-framework/options/context' +import type {Context} from '@roots/bud-framework/context' import type {ReadStream, WriteStream} from 'node:tty' export interface CLIContext extends Context { diff --git a/sources/@roots/bud/src/context/bud.ts b/sources/@roots/bud/src/context/bud.ts index f3048bedcb..8576da189b 100644 --- a/sources/@roots/bud/src/context/bud.ts +++ b/sources/@roots/bud/src/context/bud.ts @@ -1,4 +1,4 @@ -import type {Context} from '@roots/bud-framework/options' +import type {Context} from '@roots/bud-framework/context' import {dirname, join, resolve, sep} from 'node:path' import {fileURLToPath} from 'node:url' diff --git a/sources/@roots/bud/src/context/extensions.ts b/sources/@roots/bud/src/context/extensions.ts index 26ba89bf70..ea86579321 100644 --- a/sources/@roots/bud/src/context/extensions.ts +++ b/sources/@roots/bud/src/context/extensions.ts @@ -1,5 +1,5 @@ import type {Modules} from '@roots/bud-framework' -import type {Context} from '@roots/bud-framework/options' +import type {Context} from '@roots/bud-framework/context' import args from '@roots/bud-support/utilities/args' @@ -31,6 +31,7 @@ const extensions: Extensions = { `@roots/bud-extensions/clean-webpack-plugin`, `@roots/bud-extensions/copy-webpack-plugin`, `@roots/bud-extensions/html-webpack-plugin`, + `@roots/bud-extensions/import-map`, `@roots/bud-extensions/interpolate-html-webpack-plugin`, `@roots/bud-extensions/mini-css-extract-plugin`, `@roots/bud-extensions/webpack-define-plugin`, diff --git a/sources/@roots/bud/src/context/index.ts b/sources/@roots/bud/src/context/index.ts index de91692cc0..d3eeb1032b 100644 --- a/sources/@roots/bud/src/context/index.ts +++ b/sources/@roots/bud/src/context/index.ts @@ -1,11 +1,11 @@ /* eslint-disable no-console */ -import type {Context} from '@roots/bud-framework/options' +import type {Context} from '@roots/bud-framework' +import logger from '@roots/bud-support/logger' import args from '@roots/bud-support/utilities/args' import * as projectEnv from '@roots/bud-support/utilities/env' import * as projectFiles from '@roots/bud-support/utilities/files' import * as filesystem from '@roots/bud-support/utilities/filesystem' -import logger from '@roots/bud-support/utilities/logger' import * as projectPaths from '@roots/bud-support/utilities/paths' import {join} from 'node:path' diff --git a/sources/@roots/bud/src/context/services.ts b/sources/@roots/bud/src/context/services.ts index 269ad5930c..2b570fb43b 100644 --- a/sources/@roots/bud/src/context/services.ts +++ b/sources/@roots/bud/src/context/services.ts @@ -9,6 +9,4 @@ export default [ `@roots/bud-extensions`, `@roots/bud-server`, `@roots/bud/services/project`, - `@roots/bud-framework/services/console`, - `@roots/bud-framework/services/notifier`, ] diff --git a/sources/@roots/bud/src/factory.ts b/sources/@roots/bud/src/factory.ts index e5dc9a19de..bee0d491df 100644 --- a/sources/@roots/bud/src/factory.ts +++ b/sources/@roots/bud/src/factory.ts @@ -1,6 +1,6 @@ -import type {Context} from '@roots/bud-framework/options' +import type {Bud} from '@roots/bud' +import type {Context} from '@roots/bud-framework/context' -import {Bud} from '@roots/bud' import makeContext from '@roots/bud/context' import * as instance from '@roots/bud/instance' diff --git a/sources/@roots/bud/src/global.d.ts b/sources/@roots/bud/src/global.d.ts deleted file mode 100644 index 07af7cd8cc..0000000000 --- a/sources/@roots/bud/src/global.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type * as Framework from '@roots/bud-framework' - -declare module '@roots/bud' { - interface Bud extends Framework.Bud {} -} diff --git a/sources/@roots/bud/src/index.ts b/sources/@roots/bud/src/index.ts index ad14ba6ea1..fd57ccdb6d 100644 --- a/sources/@roots/bud/src/index.ts +++ b/sources/@roots/bud/src/index.ts @@ -8,37 +8,17 @@ * @see https://github.com/roots/bud */ -import type Api from '@roots/bud-api' -import type Build from '@roots/bud-build' -import type Cache from '@roots/bud-cache' -import type Compiler from '@roots/bud-compiler' -import type Dashboard from '@roots/bud-dashboard' -import type Entrypoints from '@roots/bud-entrypoints' -import type Extensions from '@roots/bud-extensions' -import type * as Framework from '@roots/bud-framework' -import type Hooks from '@roots/bud-hooks' -import type Minify from '@roots/bud-minify' -import type Server from '@roots/bud-server' - -declare module '@roots/bud' { - interface Bud extends Framework.Bud { - api: Api - build: Build - cache: Cache - compiler: Compiler - dashboard: Dashboard - extensions: Extensions - hooks: Hooks - server: Server - } - - interface Modules { - '@roots/bud-entrypoints': Entrypoints - '@roots/bud-minify': Minify - } -} +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +export {Bud} from '@roots/bud/bud' export * from '@roots/bud/factory' export * from '@roots/bud/instance' - -export {default as Bud} from '@roots/bud/bud' diff --git a/sources/@roots/bud/src/services/project.ts b/sources/@roots/bud/src/services/project.ts index 651b0bf36d..b90cbbe956 100644 --- a/sources/@roots/bud/src/services/project.ts +++ b/sources/@roots/bud/src/services/project.ts @@ -10,61 +10,91 @@ import * as args from '@roots/bud-support/utilities/args' * Project service */ export default class Project extends Service { + public promised: Array> = [] /** - * `build.after` hook callback + * {@link Service.buildAfter} */ @bind - public override async buildAfter?(bud: Bud) { - if (!bud.context.debug) - return bud.info(`--debug not \`true\`. skipping fs write.`) - - try { - const path = bud.path(`@storage`, bud.label, `debug`, `profile.yml`) - - await bud.fs.write(path, { - ...omit(bud.context, [`env`]), - args: args.raw, - children: bud.children ? Object.keys(bud.children) : [], - env: bud.env.getKeys(), - loaded: Object.entries(bud.extensions?.repository).map( - ([key, extension]) => ({ - key, - label: extension.label, - meta: extension.meta, - options: extension.options, - }), - ), + public override async buildAfter(bud: Bud) { + this.promised.push( + /** + * Module cache + */ + bud.fs.write(bud.module.cacheLocation, { resolutions: bud.module.resolved, - services: bud.context?.services, - }) + version: bud.context.bud.version, + }), + ) - bud.success(`profile written to `, path) - } catch (error) { - throw new BudError(`Could not write profile.yml`, { - props: { - details: `An error occurred while writing \`profile.yml\` to the filesystem.`, - origin: BudError.normalize(error), - }, - }) + if (bud.context.debug) { + this.promised.push( + bud.fs + .write(bud.path(`@storage`, bud.label, `debug`, `profile.yml`), { + ...omit(bud.context, [`env`]), + bootstrap: { + args: args.raw, + resolutions: bud.module.resolved, + }, + children: bud.children ? Object.keys(bud.children) : [], + env: bud.env.getKeys(), + loaded: Object.entries(bud.extensions?.repository).map( + ([key, extension]) => ({ + key, + label: extension.label, + meta: extension.meta, + options: extension.options, + }), + ), + }) + .then(() => { + this.logger.success(`profile.yml written to disk`) + }) + .catch(error => { + throw new BudError(`Could not write profile.yml`, { + props: { + details: `An error occurred while writing \`profile.yml\` to the filesystem.`, + origin: BudError.normalize(error), + }, + }) + }), + bud.fs + .write( + bud.path(`@storage`, bud.label, `debug`, `build.config.yml`), + bud.build.config, + ) + .then(() => { + this.logger.success(`webpack.output.yml written to disk`) + }) + .catch(error => { + throw new BudError(`Could not write webpack.output.yml`, { + props: { + details: `An error occurred while writing \`webpack.output.yml\` to the filesystem.`, + origin: BudError.normalize(error), + }, + }) + }), + ) } - try { - const path = bud.path( - `@storage`, - bud.label, - `debug`, - `build.config.yml`, - ) - await bud.fs.write(path, bud.build.config) + await Promise.all(this.promised) + } - bud.success(`webpack.output.yml written to`, path) - } catch (error) { - throw new BudError(`Could not write webpack.output.yml`, { - props: { - details: `An error occurred while writing \`webpack.output.yml\` to the filesystem.`, - origin: BudError.normalize(error), - }, - }) - } + /** + * {@link Service.compilerDone} + */ + @bind + public override async compilerDone([bud, stats]) { + this.logger.log(`compiler done`) + + if (!bud.context.debug) return + if (!stats) return + + await bud.fs.write( + bud.path(`@storage`, bud.label, `debug`, `stats.yml`), + { + compilation: bud.compiler.stats.toJson({all: true}), + message: stats.toString(), + }, + ) } } diff --git a/sources/@roots/bud/test/project.test.ts b/sources/@roots/bud/test/project.test.ts index 8dd014c5f7..2e69bbab1c 100644 --- a/sources/@roots/bud/test/project.test.ts +++ b/sources/@roots/bud/test/project.test.ts @@ -16,7 +16,6 @@ describe(`@roots/bud/services/project`, () => { it(`returns early if debug not set`, async () => { bud.context.debug = false - const infoSpy = vi.spyOn(bud, `info`) const pathSpy = vi.spyOn(bud, `path`) if (!project.buildAfter) return @@ -24,8 +23,5 @@ describe(`@roots/bud/services/project`, () => { await project.buildAfter(bud) expect(pathSpy).not.toHaveBeenCalled() - expect(infoSpy).toHaveBeenCalledWith( - `--debug not \`true\`. skipping fs write.`, - ) }) }) diff --git a/sources/@roots/bud/tsconfig.json b/sources/@roots/bud/tsconfig.json index 67dd829c2d..c3f84879cc 100644 --- a/sources/@roots/bud/tsconfig.json +++ b/sources/@roots/bud/tsconfig.json @@ -19,26 +19,24 @@ "@roots/bud-minify", "@roots/bud-server", "@roots/bud-support", - "vitest/importMeta", "webpack/module" ], }, - "files": ["./src/global.d.ts"], "include": ["./src"], - "exclude": ["./lib", "./node_modules", "./test"], + "exclude": ["./test"], "references": [ - {"path": "./../bud-api/tsconfig.json"}, - {"path": "./../bud-build/tsconfig.json"}, - {"path": "./../bud-cache/tsconfig.json"}, - {"path": "./../bud-compiler/tsconfig.json"}, - {"path": "./../bud-dashboard/tsconfig.json"}, - {"path": "./../bud-entrypoints/tsconfig.json"}, - {"path": "./../bud-extensions/tsconfig.json"}, - {"path": "./../bud-hooks/tsconfig.json"}, - {"path": "./../bud-minify/tsconfig.json"}, - {"path": "./../bud-server/tsconfig.json"}, - {"path": "./../bud-support/tsconfig.json"}, - {"path": "./../bud-minify/tsconfig.json"}, - {"path": "./../bud-framework/tsconfig.json"} + {"path": "../bud-api/tsconfig.json"}, + {"path": "../bud-build/tsconfig.json"}, + {"path": "../bud-cache/tsconfig.json"}, + {"path": "../bud-compiler/tsconfig.json"}, + {"path": "../bud-dashboard/tsconfig.json"}, + {"path": "../bud-entrypoints/tsconfig.json"}, + {"path": "../bud-extensions/tsconfig.json"}, + {"path": "../bud-framework/tsconfig.json"}, + {"path": "../bud-hooks/tsconfig.json"}, + {"path": "../bud-minify/tsconfig.json"}, + {"path": "../bud-server/tsconfig.json"}, + {"path": "../bud-support/tsconfig.json"}, + {"path": "../bud-minify/tsconfig.json"}, ] } diff --git a/sources/@roots/sage/src/acorn/index.ts b/sources/@roots/sage/src/acorn/index.ts index 367f7ec9a1..f432d0610b 100644 --- a/sources/@roots/sage/src/acorn/index.ts +++ b/sources/@roots/sage/src/acorn/index.ts @@ -20,7 +20,7 @@ export default class Acorn extends Extension { bud.manifest.set(`publicPath`, ``) bud.when(bud.isDevelopment, ({hooks}) => - hooks.action(`compiler.close`, this.writeHMR), + hooks.action(`compiler.done`, this.writeHMR), ) } @@ -28,7 +28,7 @@ export default class Acorn extends Extension { * Write hmr.json */ @bind - public async writeHMR(bud: Bud) { + public async writeHMR([bud, stats]: [Bud, unknown]) { await bud.fs.write(bud.path(`@dist`, `hmr.json`), { dev: urlToHttpOptions(bud.root.server.publicUrl), proxy: urlToHttpOptions(bud.root.server.publicProxyUrl), diff --git a/sources/@roots/wordpress-hmr/tsconfig.json b/sources/@roots/wordpress-hmr/tsconfig.json index 90421d7cfa..b97fc7223f 100644 --- a/sources/@roots/wordpress-hmr/tsconfig.json +++ b/sources/@roots/wordpress-hmr/tsconfig.json @@ -4,12 +4,6 @@ "rootDir": "./src", "outDir": "./lib", }, - "include": [ - "./src" - ], - "exclude": [ - "./lib", - "./node_modules/**/*", - "./**/*.test.ts" - ], + "include": ["./src"], + "exclude": ["./**/*.test.ts"], } diff --git a/tests/integration/__snapshots__/typescript.test.ts.snap b/tests/integration/__snapshots__/typescript.test.ts.snap deleted file mode 100644 index 19a24f9252..0000000000 --- a/tests/integration/__snapshots__/typescript.test.ts.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`examples/typescript > should compile js and css as expected (bud) 1`] = ` -{ - "app.js": "js/app.js", - "index.html": "index.html", - "runtime.js": "js/runtime.js", -} -`; - -exports[`examples/typescript > should compile js and css as expected (ts-bud) 1`] = ` -{ - "app.js": "js/app.js", - "index.html": "index.html", - "runtime.js": "js/runtime.js", -} -`; diff --git a/tests/integration/emotion.test.ts b/tests/integration/emotion.test.ts index db116f2afd..dd0ac17387 100644 --- a/tests/integration/emotion.test.ts +++ b/tests/integration/emotion.test.ts @@ -16,7 +16,7 @@ describe(`examples/emotion`, () => { expect(test.manifest[`components/logo.svg`]).toBe( `components/logo.svg`, ) - expect(Object.keys(test.manifest).length).toBe(5) + expect(Object.keys(test.manifest).length).toBeGreaterThanOrEqual(5) expect(test.assets[`app.js`].length).toBeGreaterThan(10) expect(test.assets[`app.js`].includes(`import `)).toBeFalsy() diff --git a/tests/integration/react.test.ts b/tests/integration/react.test.ts index 6767f8c4a2..fe4d8af3a2 100644 --- a/tests/integration/react.test.ts +++ b/tests/integration/react.test.ts @@ -21,6 +21,6 @@ describe(`examples/react`, () => { test.assets[`components/logo.svg`].includes(` { expect(test.assets[`app.js`].length).toBeGreaterThan(10) expect(test.assets[`app.js`].includes(`from '`)).toBeFalsy() - expect(test.manifest).toMatchSnapshot() }) it(`should compile js and css as expected (ts-bud)`, async () => { @@ -26,6 +25,5 @@ describe(`examples/typescript`, () => { expect(test.assets[`app.js`].length).toBeGreaterThan(10) expect(test.assets[`app.js`].includes(`from '`)).toBeFalsy() - expect(test.manifest).toMatchSnapshot() }) }) diff --git a/tests/util/project/config/bud.config.ts b/tests/util/project/config/bud.config.ts index ca7f7328e6..a0ac1e8335 100644 --- a/tests/util/project/config/bud.config.ts +++ b/tests/util/project/config/bud.config.ts @@ -2,8 +2,9 @@ import type {Bud} from '@roots/bud' export default async (bud: Bud) => { bud - .entry(`app`, [`scripts/index`, `styles/app`]) + .entry(`app`, [`scripts/index`, `scripts/scripts`, `styles/app`]) .watch([bud.path(`@src`, `images`)]) .serve(3015) .html() + .bundle(`react`, [`react`, `react-dom`]) } diff --git a/tests/util/project/src/scripts/components/app.tsx b/tests/util/project/src/scripts/components/app.tsx index 8449299b3b..a812a53b9e 100644 --- a/tests/util/project/src/scripts/components/app.tsx +++ b/tests/util/project/src/scripts/components/app.tsx @@ -3,7 +3,7 @@ import logo from '@components/logo.svg' export const App = () => { return (
- logo + logo Edit src/components/app.tsx and save to reload diff --git a/tests/util/project/src/scripts/index.tsx b/tests/util/project/src/scripts/index.tsx index 8a6aa921ff..e1c574a2a6 100644 --- a/tests/util/project/src/scripts/index.tsx +++ b/tests/util/project/src/scripts/index.tsx @@ -1,11 +1,4 @@ -import './scripts.js' - -import React from 'react' import {createRoot} from 'react-dom/client' import {App} from '@components/app' -createRoot(document.querySelector(`#root`)).render( - - - , -) +createRoot(document.querySelector(`#root`)).render() diff --git a/yarn.lock b/yarn.lock index 7d53ba2b07..009c1a8351 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6680,7 +6680,6 @@ __metadata: "@types/react": 18.2.9 react: 18.2.0 tslib: 2.5.3 - webpack: 5.86.0 languageName: unknown linkType: soft @@ -6725,9 +6724,7 @@ __metadata: "@skypack/package-check": 0.2.2 "@types/node": 18.16.16 "@types/react": 18.2.9 - human-readable: 0.2.1 ink-testing-library: 3.0.0 - ink-text-input: 5.0.1 react: 18.2.0 tslib: 2.5.3 webpack: 5.86.0 @@ -6816,7 +6813,6 @@ __metadata: "@types/node": 18.16.16 palette-webpack-plugin: 1.0.5 tslib: 2.5.3 - webpack: 5.86.0 languageName: unknown linkType: soft @@ -6909,7 +6905,6 @@ __metadata: version: 0.0.0-use.local resolution: "@roots/bud-postcss@workspace:sources/@roots/bud-postcss" dependencies: - "@roots/bud": "workspace:*" "@roots/bud-build": "workspace:*" "@roots/bud-framework": "workspace:*" "@roots/bud-support": "workspace:*" @@ -6936,6 +6931,7 @@ __metadata: "@roots/bud-framework": "workspace:*" "@roots/bud-postcss": "workspace:*" "@roots/bud-swc": "workspace:*" + "@roots/bud-typescript": "workspace:*" "@skypack/package-check": 0.2.2 "@types/node": 18.16.16 tslib: 2.5.3 @@ -7025,7 +7021,6 @@ __metadata: version: 0.0.0-use.local resolution: "@roots/bud-sass@workspace:sources/@roots/bud-sass" dependencies: - "@roots/bud": "workspace:*" "@roots/bud-build": "workspace:*" "@roots/bud-framework": "workspace:*" "@roots/bud-postcss": "workspace:*" @@ -7055,6 +7050,7 @@ __metadata: "@roots/bud-framework": "workspace:*" "@roots/bud-support": "workspace:*" "@skypack/package-check": 0.2.2 + ink-testing-library: 3.0.0 tslib: 2.5.3 languageName: unknown linkType: soft @@ -7134,6 +7130,8 @@ __metadata: human-readable: 0.2.1 import-meta-resolve: 2.2.2 ink: 4.2.0 + ink-big-text: 2.0.0 + ink-gradient: 3.0.0 ink-spinner: 5.0.0 ink-text-input: 5.0.1 json5: 2.2.3 @@ -7247,7 +7245,9 @@ __metadata: resolution: "@roots/bud-typescript@workspace:sources/@roots/bud-typescript" dependencies: "@roots/bud": "workspace:*" + "@roots/bud-api": "workspace:*" "@roots/bud-babel": "workspace:*" + "@roots/bud-build": "workspace:*" "@roots/bud-framework": "workspace:*" "@roots/bud-support": "workspace:*" "@skypack/package-check": 0.2.2 @@ -7269,7 +7269,6 @@ __metadata: version: 0.0.0-use.local resolution: "@roots/bud-vue@workspace:sources/@roots/bud-vue" dependencies: - "@roots/bud": "workspace:*" "@roots/bud-framework": "workspace:*" "@roots/bud-postcss": "workspace:*" "@roots/bud-sass": "workspace:*" @@ -7331,7 +7330,6 @@ __metadata: version: 0.0.0-use.local resolution: "@roots/bud-wordpress-theme-json@workspace:sources/@roots/bud-wordpress-theme-json" dependencies: - "@roots/bud": "workspace:*" "@roots/bud-framework": "workspace:*" "@roots/bud-support": "workspace:*" "@roots/container": "workspace:*" @@ -7362,7 +7360,6 @@ __metadata: "@roots/bud-support": "workspace:*" "@skypack/package-check": 0.2.2 "@types/node": 18.16.16 - "@types/react": 18.2.9 tslib: 2.5.3 bin: bud: ./bin/bud.mjs @@ -8668,6 +8665,15 @@ __metadata: languageName: node linkType: hard +"@types/gradient-string@npm:^1.1.2": + version: 1.1.2 + resolution: "@types/gradient-string@npm:1.1.2" + dependencies: + "@types/tinycolor2": "*" + checksum: 8bc665bcf277b4da9d30eb1dc5d83990678145700df991a24441948cb86b8e9c453cb701a370d3363dddafdd365748ef3190140601c82916ea70ee577de98dc3 + languageName: node + linkType: hard + "@types/hast@npm:^2.0.0": version: 2.3.4 resolution: "@types/hast@npm:2.3.4" @@ -9169,7 +9175,7 @@ __metadata: languageName: node linkType: hard -"@types/tinycolor2@npm:*": +"@types/tinycolor2@npm:*, @types/tinycolor2@npm:^1.4.0": version: 1.4.3 resolution: "@types/tinycolor2@npm:1.4.3" checksum: 61984b2825d4ee902016ef24777787bb2fb9e4999ccd4f7e5a709442c00cf90ba4afa510b9c78f18dcc83c03305d597d5fe3825a6aad38354f95c68af70ebc1b @@ -12830,6 +12836,7 @@ __metadata: html-loader: 4.2.0 html-webpack-plugin: 5.5.3 import-meta-resolve: 2.2.2 + ink-testing-library: 3.0.0 lodash: 4.17.21 parse5-htmlparser2-tree-adapter: 7.0.0 patch-console: 2.0.0 @@ -13167,9 +13174,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001464, caniuse-lite@npm:^1.0.30001489": - version: 1.0.30001502 - resolution: "caniuse-lite@npm:1.0.30001502" - checksum: 801a9fd4b635082a976a2a0238c59e106f37238f93afddb504b88fa434d521cd9c600d8d9f305a0f4611c0b53c6d6c164e81ee38f0130ff46636c3b280195a0c + version: 1.0.30001506 + resolution: "caniuse-lite@npm:1.0.30001506" + checksum: 0a090745824622df146e2f6dde79c7f7920a899dec1b3a599d2ef9acf41cef5e179fd133bb59f2030286a4ea935f4230e05438d7394694c414e8ada345eb5268 languageName: node linkType: hard @@ -13205,6 +13212,18 @@ __metadata: languageName: node linkType: hard +"cfonts@npm:^3.1.1": + version: 3.2.0 + resolution: "cfonts@npm:3.2.0" + dependencies: + supports-color: ^8 + window-size: ^1.1.1 + bin: + cfonts: bin/index.js + checksum: 08c412fce235e210925e44f3293fe44017acbb067b56cf60a1453894fc116374d1cedd1373d6648f688f01548a8cccd340dcd646118f67a3b90d2e54e0fe33a3 + languageName: node + linkType: hard + "chai@npm:^4.3.7": version: 4.3.7 resolution: "chai@npm:4.3.7" @@ -18901,6 +18920,16 @@ __metadata: languageName: node linkType: hard +"gradient-string@npm:^2.0.2": + version: 2.0.2 + resolution: "gradient-string@npm:2.0.2" + dependencies: + chalk: ^4.1.2 + tinygradient: ^1.1.5 + checksum: 3b3cdfff83df0a82060bd3c3f32b911a0cdb22fb211a61f24ddf76660d670e79487db2a99fc0c2402c54cd7fe83cd224bf916a712e199c7f210f480313b4ce45 + languageName: node + linkType: hard + "grapheme-splitter@npm:^1.0.4": version: 1.0.4 resolution: "grapheme-splitter@npm:1.0.4" @@ -20063,6 +20092,33 @@ __metadata: languageName: node linkType: hard +"ink-big-text@npm:2.0.0": + version: 2.0.0 + resolution: "ink-big-text@npm:2.0.0" + dependencies: + cfonts: ^3.1.1 + prop-types: ^15.8.1 + peerDependencies: + ink: ">=4" + react: ">=18" + checksum: e923e31751c87ed044435c00b163c1fae4b79edc394b59c03c291e93ec81d28be11113d6f985f78b1b91dde36c1e6945f63404409410ecff36533b3472ba4c0b + languageName: node + linkType: hard + +"ink-gradient@npm:3.0.0": + version: 3.0.0 + resolution: "ink-gradient@npm:3.0.0" + dependencies: + "@types/gradient-string": ^1.1.2 + gradient-string: ^2.0.2 + prop-types: ^15.8.1 + strip-ansi: ^7.1.0 + peerDependencies: + ink: ">=4" + checksum: 243bf94dc93eac06fbfee6c99cf143752ea512e712cbebc783294aa9a9bed60c810e8fcce647b97e6400eed2dbcb1400ec7eb96cf1ee3b7406c99b72387eb7b8 + languageName: node + linkType: hard + "ink-spinner@npm:5.0.0": version: 5.0.0 resolution: "ink-spinner@npm:5.0.0" @@ -30802,7 +30858,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:7.1.0, strip-ansi@npm:^7.0.0, strip-ansi@npm:^7.0.1": +"strip-ansi@npm:7.1.0, strip-ansi@npm:^7.0.0, strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -31179,7 +31235,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.0.0": +"supports-color@npm:^8, supports-color@npm:^8.0.0": version: 8.1.1 resolution: "supports-color@npm:8.1.1" dependencies: @@ -31718,6 +31774,23 @@ __metadata: languageName: node linkType: hard +"tinycolor2@npm:^1.0.0": + version: 1.6.0 + resolution: "tinycolor2@npm:1.6.0" + checksum: 6df4d07fceeedc0a878d7bac47e2cd47c1ceeb1078340a9eb8a295bc0651e17c750f73d47b3028d829f30b85c15e0572c0fd4142083e4c21a30a597e47f47230 + languageName: node + linkType: hard + +"tinygradient@npm:^1.1.5": + version: 1.1.5 + resolution: "tinygradient@npm:1.1.5" + dependencies: + "@types/tinycolor2": ^1.4.0 + tinycolor2: ^1.0.0 + checksum: d41771d553fff884830f60029ba32163066221f1c6ea28ddf9751b10d54015fa47597bdefa164e20434ba3018e499dbd17b465fd43a095c587cac28bb7de5a0e + languageName: node + linkType: hard + "tinylogic@npm:^2.0.0": version: 2.0.0 resolution: "tinylogic@npm:2.0.0" @@ -34038,6 +34111,18 @@ __metadata: languageName: node linkType: hard +"window-size@npm:^1.1.1": + version: 1.1.1 + resolution: "window-size@npm:1.1.1" + dependencies: + define-property: ^1.0.0 + is-number: ^3.0.0 + bin: + window-size: cli.js + checksum: 4ab5ea0d5f224d6859eb0747049cbebf43ed3ba9fe535a2cbd2024b1f9cfd3c9021db33a3b20ee42ef8b5c5a036ddca46271bf1fc471aef7fc4cb4a98bfb2e67 + languageName: node + linkType: hard + "windows-release@npm:^5.0.1": version: 5.0.1 resolution: "windows-release@npm:5.0.1"