From 2392dc94d58174a1197203c720e7fc664d4f4712 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 12:15:44 -0700 Subject: [PATCH 01/16] add white bg to error overlay --- .../@roots/bud-server/src/client/overlay/overlay.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/sources/@roots/bud-server/src/client/overlay/overlay.component.ts b/sources/@roots/bud-server/src/client/overlay/overlay.component.ts index a9b23e7c50..f7131b8185 100644 --- a/sources/@roots/bud-server/src/client/overlay/overlay.component.ts +++ b/sources/@roots/bud-server/src/client/overlay/overlay.component.ts @@ -55,6 +55,7 @@ export class Component extends HTMLElement { } .${this.name}__visible { backdrop-filter: blur(10px); + background: rgba(255, 255, 255, 0.75); border-top: 3px solid red; display: flex; align-items: center; From a9c5264f270ee303aa445059be4a2932417d6a8a Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 15:24:03 -0700 Subject: [PATCH 02/16] --overlay and --indicator flags --- sources/@roots/bud-server/src/seed.ts | 5 ++- sources/@roots/bud/src/cli/commands/build.ts | 36 ++++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/sources/@roots/bud-server/src/seed.ts b/sources/@roots/bud-server/src/seed.ts index be3a209c52..728295d5b7 100644 --- a/sources/@roots/bud-server/src/seed.ts +++ b/sources/@roots/bud-server/src/seed.ts @@ -46,7 +46,10 @@ export const seed = (app: Framework) => { .hooks.on( `dev.client.scripts`, new Set([ - app => src(`index.js?name=${app.name}&path=/__bud/hmr`), + app => + src( + `index.js?name=${app.name}&bud.overlay=${app.context.args.overlay}&bud.indicator=${app.context.args.indicator}&path=/__bud/hmr`, + ), () => src(`proxy-click-interceptor.js`), ]), ) diff --git a/sources/@roots/bud/src/cli/commands/build.ts b/sources/@roots/bud/src/cli/commands/build.ts index a51489f2b5..48e4bfee17 100644 --- a/sources/@roots/bud/src/cli/commands/build.ts +++ b/sources/@roots/bud/src/cli/commands/build.ts @@ -148,17 +148,17 @@ export class BuildCommand extends BaseCommand { }) /** - * --log + * --indicator */ - public log = Option.Boolean(`--log`, undefined, { - description: 'Enable logging', + public indicator = Option.Boolean(`--indicator`, true, { + description: 'Enable development status indicator', }) /** - * --verbose + * --log */ - public verbose = Option.Boolean(`--verbose`, false, { - description: 'Set logging level', + public log = Option.Boolean(`--log`, undefined, { + description: 'Enable logging', }) /** @@ -175,12 +175,25 @@ export class BuildCommand extends BaseCommand { description: 'Minimize compiled assets', }) + /** + * --modules + */ public modules = Option.String(`--modules`, undefined, { description: 'Module resolution path', }) + /** + * --notify + */ public notify = Option.Boolean(`--notify`, true, { - description: 'Allow OS notifications', + description: 'Enable notfication center messages', + }) + + /** + * --overlay + */ + public overlay = Option.Boolean(`--overlay`, true, { + description: 'Enable error overlay in development mode', }) /** @@ -208,6 +221,13 @@ export class BuildCommand extends BaseCommand { description: 'Limit compilation to particular compilers', }) + /** + * --verbose + */ + public verbose = Option.Boolean(`--verbose`, false, { + description: 'Set logging level', + }) + /** * Execute command */ @@ -225,6 +245,7 @@ export class BuildCommand extends BaseCommand { flush: this.flush ?? null, hash: this.hash ?? null, html: this.html ?? null, + indicator: this.indicator ?? null, inject: this.inject ?? null, log: this.log ?? null, verbose: this.verbose ?? null, @@ -233,6 +254,7 @@ export class BuildCommand extends BaseCommand { mode: this.mode ?? null, modules: this.modules ?? null, notify: this.notify ?? null, + overlay: this.overlay ?? null, publicPath: this.publicPath ?? null, src: this.src ?? null, splitChunks: this.splitChunks ?? null, From 625088c12e871128f7b6996e13ebdfc9d7bb9ccc Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 15:24:36 -0700 Subject: [PATCH 03/16] compartmentalize indicator --- .../@roots/bud-server/src/client/indicator/index.ts | 10 ++++++++++ .../src/client/indicator/indicator.component.ts | 2 +- .../src/client/indicator/indicator.controller.ts | 5 +---- 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 sources/@roots/bud-server/src/client/indicator/index.ts diff --git a/sources/@roots/bud-server/src/client/indicator/index.ts b/sources/@roots/bud-server/src/client/indicator/index.ts new file mode 100644 index 0000000000..2c74c72a53 --- /dev/null +++ b/sources/@roots/bud-server/src/client/indicator/index.ts @@ -0,0 +1,10 @@ +export const make = async (): Promise<{update: (data) => void}> => { + const {Controller} = await import('./indicator.controller') + const {Component} = await import('./indicator.component') + + if (customElements.get('bud-activity-indicator')) return + + customElements.define('bud-activity-indicator', Component) + + return new Controller() +} diff --git a/sources/@roots/bud-server/src/client/indicator/indicator.component.ts b/sources/@roots/bud-server/src/client/indicator/indicator.component.ts index 96071b3dca..4d481d8192 100644 --- a/sources/@roots/bud-server/src/client/indicator/indicator.component.ts +++ b/sources/@roots/bud-server/src/client/indicator/indicator.component.ts @@ -4,7 +4,7 @@ import {pulse} from './indicator.pulse' * Indicator web component * @public */ -export class IndicatorComponent extends HTMLElement { +export class Component extends HTMLElement { /** * Has component rendered * @public diff --git a/sources/@roots/bud-server/src/client/indicator/indicator.controller.ts b/sources/@roots/bud-server/src/client/indicator/indicator.controller.ts index 8f8dde0205..3c92160a05 100644 --- a/sources/@roots/bud-server/src/client/indicator/indicator.controller.ts +++ b/sources/@roots/bud-server/src/client/indicator/indicator.controller.ts @@ -1,10 +1,8 @@ -import {IndicatorComponent} from './indicator.component' - /** * Activity indicator controller * @public */ -export class IndicatorController { +export class Controller { /** * DOM node * @public @@ -24,7 +22,6 @@ export class IndicatorController { public constructor() { this.node = document.createElement('bud-activity-indicator') document.body && document.body.appendChild(this.node) - customElements.define('bud-activity-indicator', IndicatorComponent) } /** From 84a0ce81b02ccb815090a752106d1dd2a12c2393 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 15:25:00 -0700 Subject: [PATCH 04/16] compartmentalize overlay --- sources/@roots/bud-server/src/client/overlay/index.ts | 10 ++++++++++ .../src/client/overlay/overlay.controller.ts | 7 +------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 sources/@roots/bud-server/src/client/overlay/index.ts diff --git a/sources/@roots/bud-server/src/client/overlay/index.ts b/sources/@roots/bud-server/src/client/overlay/index.ts new file mode 100644 index 0000000000..541eb285a6 --- /dev/null +++ b/sources/@roots/bud-server/src/client/overlay/index.ts @@ -0,0 +1,10 @@ +export const make = async (): Promise<{update: (data) => void}> => { + const {Controller} = await import('./overlay.controller') + const {Component} = await import('./overlay.component') + + if (customElements.get('bud-error')) return + + customElements.define('bud-error', Component) + + return new Controller() +} diff --git a/sources/@roots/bud-server/src/client/overlay/overlay.controller.ts b/sources/@roots/bud-server/src/client/overlay/overlay.controller.ts index cb5364ef43..eb3282ba8f 100644 --- a/sources/@roots/bud-server/src/client/overlay/overlay.controller.ts +++ b/sources/@roots/bud-server/src/client/overlay/overlay.controller.ts @@ -1,8 +1,6 @@ import stripAnsi from 'strip-ansi' import {StatsError} from 'webpack' -import {Component} from './overlay.component' - interface Payload { hash: string errors: Array @@ -12,7 +10,7 @@ interface Payload { * Overlay controller * @public */ -export class OverlayController { +export class Controller { /** * Element * @public @@ -45,9 +43,6 @@ export class OverlayController { * @public */ public constructor() { - !customElements.get('bud-error') && - customElements.define('bud-error', Component) - this.element = document.createElement('bud-error') document.body && document.body.appendChild(this.element) } From 8ec6c5577ddb80ad2a3cd74c7b951a7f773474f0 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 15:25:50 -0700 Subject: [PATCH 05/16] improve client entry --- sources/@roots/bud-server/src/client/index.ts | 87 ++++++++++++++----- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/sources/@roots/bud-server/src/client/index.ts b/sources/@roots/bud-server/src/client/index.ts index 2505937a14..09aa827c5b 100644 --- a/sources/@roots/bud-server/src/client/index.ts +++ b/sources/@roots/bud-server/src/client/index.ts @@ -1,42 +1,83 @@ +/* eslint-disable no-console */ /* global __resourceQuery */ +interface Controller { + update: (payload) => void +} + +interface BaseOptions { + autoConnect: boolean + timeout: number + overlay: boolean + reload: boolean + log: boolean + warn: boolean + name: string + overlayWarnings: boolean + path: string +} +interface Options extends BaseOptions { + 'bud.overlay': boolean + 'bud.indicator': boolean +} + ;(async (query: string) => { const querystring = await import('querystring') const hmr = await import('./bridge') - const {IndicatorController} = await import( - './indicator/indicator.controller' - ) - const {OverlayController} = await import('./overlay/overlay.controller') - const indicator = new IndicatorController() - const overlay = new OverlayController() + const controllers: Array = [] - const instance = { - path: '/__bud/hmr', + const FALLBACK_OPTS: Options = { + ['bud.overlay']: true, + ['bud.indicator']: true, + autoConnect: false, timeout: 20 * 1000, - overlay: true, + overlay: false, + reload: false, log: false, - warn: true, + warn: false, name: '', - autoConnect: false, overlayWarnings: false, - ...querystring.parse(query.slice(1)), + path: '/__bud/hmr', + } + + const options: Options = Object.entries( + querystring.parse(query.slice(1)), + ).reduce((a: Options, [k, v]: [keyof Options, any]) => { + if (v === 'true') v = true + if (v === 'false') v = false + return {...a, [k]: v} + }, FALLBACK_OPTS) + + hmr.setOptionsAndConnect(options) + + const registerController = async ( + path: string, + flag: `${keyof Options & string}`, + ) => { + if (flag && !options[flag]) { + const controllerModule = await import(path) + const controller = await controllerModule.make() + controller?.update && controllers.push(controller) + } } - hmr.setOptionsAndConnect(instance) + await registerController('./indicator', 'bud.indicator') + await registerController('./overlay', 'bud.overlay') + hmr.subscribeAll(payload => { - if (payload.action === 'reload') window.location.reload() + console.table({ + name: payload.name, + action: payload.action, + hash: payload.hash, + }) + + payload.warnings.map(console.warn) + payload.errors.map(console.error) - indicator.update(payload) - overlay.update(payload) + controllers.map(controller => controller.update(payload)) - // eslint-disable-next-line no-console - console.log( - `%c[bud]%c %c${payload.action}`, - 'background: #525ddc; color: #ffffff;', - 'background: transparent;', - 'background: white; color: #343a40;', - ) + if (payload.action === 'reload') window.location.reload() }) })( // @ts-ignore From 407f723f6796d8028a0ce7ee5d0ec9543df8a7a3 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 15:33:01 -0700 Subject: [PATCH 06/16] fix: !flag --- docker-compose.yml | 4 ++-- .../src/Controller/controller.service.ts | 15 ++++++++++----- sources/@roots/bud-server/src/client/index.ts | 2 +- sources/@roots/bud-server/src/client/inject.ts | 1 + tests/unit/bud/services/project/project.test.ts | 2 ++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e19cef8094..ba6b0a260b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,6 @@ volumes: driver: local verdaccio: driver: local - - mocks: storage: + driver: local + mocks: diff --git a/sources/@roots/bud-extensions/src/Controller/controller.service.ts b/sources/@roots/bud-extensions/src/Controller/controller.service.ts index 80ae5c05a8..9c352f3218 100644 --- a/sources/@roots/bud-extensions/src/Controller/controller.service.ts +++ b/sources/@roots/bud-extensions/src/Controller/controller.service.ts @@ -371,13 +371,16 @@ export class Controller { await this.mixin() await this.api() - if (isFunction(this._module.register)) + if (isFunction(this._module.register)) { await this._module.register(this.app, this.moduleLogger) - this.moduleLogger.success({ - message: `register called`, - suffix: chalk.dim`${this.name}`, - }) + await this.app.api.processQueue() + + this.moduleLogger.success({ + message: `register called`, + suffix: chalk.dim`${this.name}`, + }) + } return this } @@ -505,6 +508,8 @@ export class Controller { if (isFunction(this._module.boot)) { await this._module.boot(this.app, this.moduleLogger) + await this.app.api.processQueue() + this.moduleLogger.success({ message: `${this.name} booted`, }) diff --git a/sources/@roots/bud-server/src/client/index.ts b/sources/@roots/bud-server/src/client/index.ts index 09aa827c5b..6f787e7362 100644 --- a/sources/@roots/bud-server/src/client/index.ts +++ b/sources/@roots/bud-server/src/client/index.ts @@ -55,7 +55,7 @@ interface Options extends BaseOptions { path: string, flag: `${keyof Options & string}`, ) => { - if (flag && !options[flag]) { + if (options[flag]) { const controllerModule = await import(path) const controller = await controllerModule.make() controller?.update && controllers.push(controller) diff --git a/sources/@roots/bud-server/src/client/inject.ts b/sources/@roots/bud-server/src/client/inject.ts index 1db7b9c2bd..90a5a0e6c8 100644 --- a/sources/@roots/bud-server/src/client/inject.ts +++ b/sources/@roots/bud-server/src/client/inject.ts @@ -30,6 +30,7 @@ export const inject: inject = async (instance: Framework, injection) => { `${instance.name} entrypoints are malformed`, `skipping inject`, ) + return entrypoints } diff --git a/tests/unit/bud/services/project/project.test.ts b/tests/unit/bud/services/project/project.test.ts index c21b22bcf0..c51bd4617d 100644 --- a/tests/unit/bud/services/project/project.test.ts +++ b/tests/unit/bud/services/project/project.test.ts @@ -1,5 +1,7 @@ import {Bud, factory} from '@repo/test-kit/bud' +jest.setTimeout(15000) + describe('bud.project', function () { let bud: Bud From 6726750e2458d893b619c0fe5a7beff6d7f8dffc Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 15:43:14 -0700 Subject: [PATCH 07/16] Critical dependency eats the bowl --- sources/@roots/bud-server/src/client/index.ts | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/sources/@roots/bud-server/src/client/index.ts b/sources/@roots/bud-server/src/client/index.ts index 6f787e7362..dea006e526 100644 --- a/sources/@roots/bud-server/src/client/index.ts +++ b/sources/@roots/bud-server/src/client/index.ts @@ -16,6 +16,7 @@ interface BaseOptions { overlayWarnings: boolean path: string } + interface Options extends BaseOptions { 'bud.overlay': boolean 'bud.indicator': boolean @@ -51,19 +52,22 @@ interface Options extends BaseOptions { hmr.setOptionsAndConnect(options) - const registerController = async ( - path: string, - flag: `${keyof Options & string}`, - ) => { - if (options[flag]) { - const controllerModule = await import(path) - const controller = await controllerModule.make() - controller?.update && controllers.push(controller) - } + const registerIndicator = async () => { + if (!options['bud.indicator']) return + const controllerModule = await import('./indicator/index.js') + const controller = await controllerModule.make() + controller?.update && controllers.push(controller) + } + + const registerOverlay = async () => { + if (!options['bud.overlay']) return + const controllerModule = await import('./overlay/index.js') + const controller = await controllerModule.make() + controller?.update && controllers.push(controller) } - await registerController('./indicator', 'bud.indicator') - await registerController('./overlay', 'bud.overlay') + await registerIndicator() + await registerOverlay() hmr.subscribeAll(payload => { console.table({ From 4f7247abc5b2f70672ee7016e90a6f3a6e275ef7 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Tue, 22 Mar 2022 19:34:22 -0700 Subject: [PATCH 08/16] fix for proxy issues --- .../bud-server/src/middleware/proxy/index.ts | 201 +++++++++--------- sources/@roots/bud-server/src/seed.ts | 2 - 2 files changed, 106 insertions(+), 97 deletions(-) diff --git a/sources/@roots/bud-server/src/middleware/proxy/index.ts b/sources/@roots/bud-server/src/middleware/proxy/index.ts index 939c0030ee..f562536563 100644 --- a/sources/@roots/bud-server/src/middleware/proxy/index.ts +++ b/sources/@roots/bud-server/src/middleware/proxy/index.ts @@ -1,5 +1,5 @@ import type {Framework} from '@roots/bud-framework' -import {createProxyMiddleware} from 'http-proxy-middleware' +import {createProxyMiddleware, Options} from 'http-proxy-middleware' import {RequestInterceptorFactory} from './req.interceptor' import {ResponseInterceptorFactory} from './res.interceptor' @@ -15,99 +15,110 @@ export const proxy = (app: Framework) => { const interceptor = new ResponseInterceptorFactory(() => app, url) const request = new RequestInterceptorFactory(() => app, url) - return createProxyMiddleware( - app.hooks.filter(`middleware.proxy.options`, { - /** - * Change origin - */ - changeOrigin: app.hooks.filter( - `middleware.proxy.options.changeOrigin`, - true, - ), - - /** - * Cookie domain rewrite - */ - cookieDomainRewrite: app.hooks.filter( - `middleware.proxy.options.cookieDomainRewrite`, - { - [url.proxy.host]: url.dev.host, - }, - ), - - /** - * Headers - */ - headers: app.hooks.filter(`middleware.proxy.options.headers`, { - [`X-Proxy-By`]: `@roots/bud`, - [`Connection`]: `keep-alive`, - [`Access-Control-Allow-Origin`]: `*`, - [`Access-Control-Allow-Credentials`]: `*`, - [`Access-Control-Allow-Methods`]: `*`, - }), - - /** - * Host rewrite - */ - hostRewrite: app.hooks.filter( - `middleware.proxy.options.hostRewrite`, - url.dev.host, - ), - - /** - * Log level - */ - logLevel: app.hooks.filter( - `middleware.proxy.options.logLevel`, - `info`, - ), - - /** - * Log provider - */ - logProvider: () => app.logger.instance.scope('proxy'), - - /** - * Proxy request handler - */ - onProxyReq: app.hooks.filter( - `middleware.proxy.options.onProxyReq`, - request.make, - ), - - /** - * Proxy response handler - */ - onProxyRes: app.hooks.filter( - `middleware.proxy.options.onProxyRes`, - interceptor.make, - ), - - /** - * Protocol rewrite - */ - protocolRewrite: app.hooks.filter( - `middleware.proxy.options.protocolRewrite`, - url.dev.protocol?.startsWith('https') ? 'https' : undefined, - ), - - /** - * Secure - */ - secure: app.hooks.filter(`middleware.proxy.options.secure`, false), - - /** - * Self handle response - */ - selfHandleResponse: app.hooks.filter( - `middleware.proxy.options.selfHandleResponse`, - true, - ), - - /** - * Target - */ - target: app.hooks.filter('middleware.proxy.target'), + const options: Options = { + autoRewrite: app.hooks.filter( + 'middleware.proxy.options.autoRewrite', + true, + ), + + /** + * Change origin + */ + changeOrigin: app.hooks.filter( + `middleware.proxy.options.changeOrigin`, + true, + ), + + /** + * Cookie domain rewrite + */ + cookieDomainRewrite: app.hooks.filter( + `middleware.proxy.options.cookieDomainRewrite`, + url.dev.host, + ), + + followRedirects: app.hooks.filter( + `middleware.proxy.options.followRedirects`, + true, + ), + + /** + * Headers + */ + headers: app.hooks.filter(`middleware.proxy.options.headers`, { + [`X-Proxy-By`]: `@roots/bud`, + [`Connection`]: `keep-alive`, + [`Access-Control-Allow-Origin`]: `*`, + [`Access-Control-Allow-Credentials`]: `*`, + [`Access-Control-Allow-Methods`]: `*`, }), - ) + + /** + * Host rewrite + */ + hostRewrite: app.hooks.filter( + `middleware.proxy.options.hostRewrite`, + `${url.dev.host}`, + ), + + /** + * Log level + */ + logLevel: app.hooks.filter( + `middleware.proxy.options.logLevel`, + `info`, + ), + + /** + * Log provider + */ + logProvider: () => app.logger.instance.scope('proxy'), + + /** + * Proxy request handler + */ + onProxyReq: app.hooks.filter( + `middleware.proxy.options.onProxyReq`, + request.make, + ), + + /** + * Proxy response handler + */ + onProxyRes: app.hooks.filter( + `middleware.proxy.options.onProxyRes`, + interceptor.make, + ), + + /** + * Protocol rewrite + */ + protocolRewrite: app.hooks.filter( + `middleware.proxy.options.protocolRewrite`, + url.dev.protocol?.startsWith('https') ? 'https' : undefined, + ), + + /** + * Secure + */ + secure: app.hooks.filter(`middleware.proxy.options.secure`, false), + + /** + * Self handle response + */ + selfHandleResponse: app.hooks.filter( + `middleware.proxy.options.selfHandleResponse`, + true, + ), + + /** + * Target + */ + target: app.hooks.filter('middleware.proxy.target', url.proxy), + } + + const {log} = app.logger.instance.scope('proxy') + Object.entries(options).map(v => log(...v)) + + return createProxyMiddleware(options) } diff --git a/sources/@roots/bud-server/src/seed.ts b/sources/@roots/bud-server/src/seed.ts index 728295d5b7..43a978b418 100644 --- a/sources/@roots/bud-server/src/seed.ts +++ b/sources/@roots/bud-server/src/seed.ts @@ -41,8 +41,6 @@ export const seed = (app: Framework) => { ) .hooks.on(`middleware.hot.options.heartbeat`, 2000) - .hooks.on(`middleware.proxy.target`, new URL(`http://localhost`)) - .hooks.on( `dev.client.scripts`, new Set([ From 618ee2950ab6ddecfb0060285362d2b33195ce3d Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Wed, 23 Mar 2022 06:20:46 -0700 Subject: [PATCH 09/16] maybe fix proxy for the kids --- .../bud-api/src/api/methods/proxy/index.ts | 5 +- .../src/Framework/framework.process.ts | 12 +++-- .../bud-framework/src/Framework/index.ts | 1 + .../bud-framework/src/Server/Middleware.ts | 1 + sources/@roots/bud-server/package.json | 4 ++ .../src/client/proxy-click-interceptor.js | 36 ------------- .../src/client/proxy-click-interceptor.ts | 54 +++++++++++++++++++ .../bud-server/src/middleware/cookie/index.ts | 13 +++++ .../@roots/bud-server/src/middleware/index.ts | 1 + .../bud-server/src/middleware/proxy/index.ts | 28 ++++++---- .../src/middleware/proxy/res.interceptor.ts | 52 +++++++++++++----- yarn.lock | 51 +++++++++++++++++- 12 files changed, 192 insertions(+), 66 deletions(-) delete mode 100644 sources/@roots/bud-server/src/client/proxy-click-interceptor.js create mode 100644 sources/@roots/bud-server/src/client/proxy-click-interceptor.ts create mode 100644 sources/@roots/bud-server/src/middleware/cookie/index.ts diff --git a/sources/@roots/bud-api/src/api/methods/proxy/index.ts b/sources/@roots/bud-api/src/api/methods/proxy/index.ts index bca903bc7c..5e3fd5da1a 100644 --- a/sources/@roots/bud-api/src/api/methods/proxy/index.ts +++ b/sources/@roots/bud-api/src/api/methods/proxy/index.ts @@ -23,13 +23,16 @@ export const enableMiddleware = ( middleware: Array, ): Array => [ ...(disableMiddleware(middleware) ?? []), + 'cookie', 'proxy', ] export const disableMiddleware = ( middleware: Array, ): Array => - middleware?.filter(middleware => middleware !== 'proxy') ?? [] + middleware?.filter( + middleware => middleware !== 'proxy' && middleware !== 'cookie', + ) ?? [] export const method: method = function (input) { const ctx = this as Framework diff --git a/sources/@roots/bud-framework/src/Framework/framework.process.ts b/sources/@roots/bud-framework/src/Framework/framework.process.ts index ea5fc7e84c..6aaaa745b5 100644 --- a/sources/@roots/bud-framework/src/Framework/framework.process.ts +++ b/sources/@roots/bud-framework/src/Framework/framework.process.ts @@ -9,10 +9,12 @@ const {removeSync} = fs */ const renderError = (msg: string, name?: string) => { global.process.stderr.write( - boxen(msg, { + boxen(`\n${msg}\n`, { title: name ?? 'error', borderStyle: 'bold', borderColor: 'red', + padding: 1, + margin: 1, }), ) } @@ -24,7 +26,11 @@ const curryHandler = function (code: number) { const ERROR = code !== 0 const close = () => { - if (ERROR) removeSync(this.path('@storage/cache')) + process.stdout.write(`\n`) + + try { + ERROR && removeSync(this.path('@storage/cache')) + } catch (err) {} global.process.exitCode = code global.process.exit() @@ -38,7 +44,7 @@ const curryHandler = function (code: number) { if (exitMessage instanceof Error) { renderError(exitMessage.message, exitMessage.name) } else { - renderError(exitMessage, 'error') + renderError(`\n${exitMessage}\n`, 'error') } return exit() diff --git a/sources/@roots/bud-framework/src/Framework/index.ts b/sources/@roots/bud-framework/src/Framework/index.ts index e1f1920d04..fb3c6da920 100644 --- a/sources/@roots/bud-framework/src/Framework/index.ts +++ b/sources/@roots/bud-framework/src/Framework/index.ts @@ -730,6 +730,7 @@ export abstract class Framework { @bind public error(...messages: any[]) { this.logger.instance.error(...messages) + if (this.isProduction) { process.exitCode = 1 process.exit() diff --git a/sources/@roots/bud-framework/src/Server/Middleware.ts b/sources/@roots/bud-framework/src/Server/Middleware.ts index 69b99a9cac..4af8c1afc2 100644 --- a/sources/@roots/bud-framework/src/Server/Middleware.ts +++ b/sources/@roots/bud-framework/src/Server/Middleware.ts @@ -26,6 +26,7 @@ export type Middleware = { export interface Available { dev?: Definition> hot?: Definition + cookie?: Definition proxy?: Definition } diff --git a/sources/@roots/bud-server/package.json b/sources/@roots/bud-server/package.json index 3967dcc1f6..ada732380c 100644 --- a/sources/@roots/bud-server/package.json +++ b/sources/@roots/bud-server/package.json @@ -46,6 +46,8 @@ }, "devDependencies": { "@skypack/package-check": "0.2.2", + "@types/cookie-parser": "^1", + "@types/cors": "^2", "@types/express": "4.17.13", "@types/lodash": "4.14.180", "@types/node": "16.11.26", @@ -58,6 +60,8 @@ "@roots/bud-support": "workspace:sources/@roots/bud-support", "@roots/container": "workspace:sources/@roots/container", "ansi-html-community": "0.0.8", + "cookie-parser": "1.4.6", + "cors": "2.8.5", "express": "^4.17.1", "html-entities": "^2.1.0", "http-proxy-middleware": "2.0.4", diff --git a/sources/@roots/bud-server/src/client/proxy-click-interceptor.js b/sources/@roots/bud-server/src/client/proxy-click-interceptor.js deleted file mode 100644 index 04a893a5f1..0000000000 --- a/sources/@roots/bud-server/src/client/proxy-click-interceptor.js +++ /dev/null @@ -1,36 +0,0 @@ -/* global __resourceQuery */ -/* eslint-disable no-console, no-extra-semi */ -// @ts-check - -;(async () => { - const main = async () => { - const {headers} = await fetch(window.location.origin, { - method: 'GET', - }) - - const origin = { - proxy: headers.get('x-bud-proxy-origin'), - dev: headers.get('x-bud-dev-origin'), - } - - if (!origin.proxy || !origin.dev) return - - document.addEventListener('click', event => { - // @ts-ignore - const el = event.target.closest('a') - const href = el?.getAttribute('href') - - if (!href || href.includes(origin.dev)) return - - Object.assign(el, { - href: href.replace(origin.proxy, origin.dev), - }) - }) - } - - window.requestAnimationFrame(async function ready() { - return document.body - ? await main() - : window.requestAnimationFrame(ready) - }) -})() diff --git a/sources/@roots/bud-server/src/client/proxy-click-interceptor.ts b/sources/@roots/bud-server/src/client/proxy-click-interceptor.ts new file mode 100644 index 0000000000..27d040f5b7 --- /dev/null +++ b/sources/@roots/bud-server/src/client/proxy-click-interceptor.ts @@ -0,0 +1,54 @@ +/* global __resourceQuery */ +/* eslint-disable no-console, no-extra-semi */ +// @ts-check + +type Target = HTMLAnchorElement | HTMLLinkElement | HTMLFormElement +type ElementTuple = [HTMLCollectionOf, any] +type Collection = Array + +type Task = [Array, any] + +const collect = (): Collection => [ + [document.getElementsByTagName('a'), 'href'], + [document.getElementsByTagName('link'), 'href'], + [document.getElementsByTagName('form'), 'action'], +] + +const main = async () => { + const {headers} = await fetch(window.location.origin, { + method: 'GET', + }) + + const origin = { + proxy: headers.get('x-bud-proxy-origin'), + dev: headers.get('x-bud-dev-origin'), + } + + const normalize = ([elements, attribute]): [Array, any] => [ + Array.from(elements), + attribute, + ] + + const filter = ([elements, attribute]: [Array, any]) => + elements.filter(el => + el.getAttribute(attribute)?.includes(origin.proxy), + ).length + + const mapElements = ([elements, attribute]: [Array, any]) => { + elements.map(el => { + const value = el.getAttribute(attribute) + if (!value) return + + el.setAttribute(attribute, value.replace(origin.proxy, origin.dev)) + }) + } + + collect().map(normalize).filter(filter).map(mapElements) +} + +;(async () => + window.requestAnimationFrame(async function ready() { + return document.body + ? await main() + : window.requestAnimationFrame(ready) + }))() diff --git a/sources/@roots/bud-server/src/middleware/cookie/index.ts b/sources/@roots/bud-server/src/middleware/cookie/index.ts new file mode 100644 index 0000000000..188b292566 --- /dev/null +++ b/sources/@roots/bud-server/src/middleware/cookie/index.ts @@ -0,0 +1,13 @@ +import {Framework} from '@roots/bud-framework/src' +import cookieParserMiddleware from 'cookie-parser' + +/** + * cookie middleware factory + * + * @public + */ +export interface cookie { + (app: Framework): any +} + +export const cookie = (app: Framework) => cookieParserMiddleware() diff --git a/sources/@roots/bud-server/src/middleware/index.ts b/sources/@roots/bud-server/src/middleware/index.ts index 81b93746da..8c5cbae0b5 100644 --- a/sources/@roots/bud-server/src/middleware/index.ts +++ b/sources/@roots/bud-server/src/middleware/index.ts @@ -1,3 +1,4 @@ export {dev} from './dev' export {hot} from './hot' +export {cookie} from './cookie' export {proxy} from './proxy' diff --git a/sources/@roots/bud-server/src/middleware/proxy/index.ts b/sources/@roots/bud-server/src/middleware/proxy/index.ts index f562536563..01f52297e4 100644 --- a/sources/@roots/bud-server/src/middleware/proxy/index.ts +++ b/sources/@roots/bud-server/src/middleware/proxy/index.ts @@ -12,7 +12,7 @@ import {ApplicationURL} from './url' */ export const proxy = (app: Framework) => { const url = new ApplicationURL(() => app) - const interceptor = new ResponseInterceptorFactory(() => app, url) + const response = new ResponseInterceptorFactory(() => app, url) const request = new RequestInterceptorFactory(() => app, url) const options: Options = { @@ -39,26 +39,32 @@ export const proxy = (app: Framework) => { followRedirects: app.hooks.filter( `middleware.proxy.options.followRedirects`, - true, + false, ), /** * Headers */ - headers: app.hooks.filter(`middleware.proxy.options.headers`, { - [`X-Proxy-By`]: `@roots/bud`, - [`Connection`]: `keep-alive`, - [`Access-Control-Allow-Origin`]: `*`, - [`Access-Control-Allow-Credentials`]: `*`, - [`Access-Control-Allow-Methods`]: `*`, - }), + headers: { + ...app.hooks.filter(`middleware.proxy.options.headers`, { + connection: 'keep-alive', + 'access-control-allow-origin': `*`, + 'access-control-allow-credentials': `*`, + 'access-control-allow-methods': `*`, + }), + 'x-proxy-by': '@roots/bud', + 'x-bud-dev-origin': url.dev.origin, + 'x-bud-dev-protocol': url.dev.protocol, + 'x-bud-dev-hostname': url.dev.hostname, + 'x-bud-proxy-origin': url.proxy.origin, + }, /** * Host rewrite */ hostRewrite: app.hooks.filter( `middleware.proxy.options.hostRewrite`, - `${url.dev.host}`, + url.dev.host, ), /** @@ -87,7 +93,7 @@ export const proxy = (app: Framework) => { */ onProxyRes: app.hooks.filter( `middleware.proxy.options.onProxyRes`, - interceptor.make, + response.make, ), /** diff --git a/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts b/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts index f02beb5f18..2dcaf35844 100644 --- a/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts +++ b/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts @@ -1,10 +1,26 @@ import {Framework} from '@roots/bud-framework' import {bind} from '@roots/bud-support' -import {IncomingMessage, ServerResponse} from 'http' +import * as http from 'http' import {responseInterceptor} from 'http-proxy-middleware' import {ApplicationURL} from './url' +interface IncomingMessage extends http.IncomingMessage { + cookies: any +} +interface ServerResponse extends http.ServerResponse { + cookie: any +} + +export interface ResponseInterceptorFactory { + interceptor( + buffer: Buffer, + proxyRes: IncomingMessage, + request: IncomingMessage, + response: ServerResponse, + ): Promise +} + /** * Proxy response interceptor * @@ -39,27 +55,37 @@ export class ResponseInterceptorFactory { * It is passed the response body, the response object, and the request object. * It can be used to modify the response body or the response object. * - * @param buffer - Buffered response body * @param proxyRes - Response from the proxy * @param req - Request from the client * @param res - Response from the server - * @returns client response body * * @public * @decorator `@bind` */ @bind - public async _interceptor( + public async interceptor( buffer: Buffer, - _proxyRes: IncomingMessage, - _req: IncomingMessage, - res: ServerResponse, - ): Promise { - res.setHeader('x-proxy-by', '@roots/bud') - res.setHeader('x-bud-proxy-origin', this.url.proxy.origin) - res.setHeader('x-bud-dev-origin', this.url.dev.origin) + proxyRes: IncomingMessage, + request: IncomingMessage, + response: ServerResponse, + ): Promise { + response.setHeader('x-proxy-by', '@roots/bud') + response.setHeader('x-bud-proxy-origin', this.url.proxy.origin) + response.setHeader('x-bud-dev-origin', this.url.dev.origin) + response.setHeader('x-http-method-override', '') + Object.entries(request.cookies).map(([k, v]) => + response.cookie(k, v, {domain: null}), + ) - return buffer + return [ + [ + ` Date: Wed, 23 Mar 2022 10:52:38 -0700 Subject: [PATCH 10/16] move proxy replacements to preset-wordpress --- sources/@roots/bud-framework/src/Hooks.ts | 1 + .../@roots/bud-preset-wordpress/src/index.ts | 21 ++++++++++++++++++- .../src/middleware/proxy/res.interceptor.ts | 15 ++++++------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/sources/@roots/bud-framework/src/Hooks.ts b/sources/@roots/bud-framework/src/Hooks.ts index fb0655649d..a98133d8ef 100644 --- a/sources/@roots/bud-framework/src/Hooks.ts +++ b/sources/@roots/bud-framework/src/Hooks.ts @@ -215,6 +215,7 @@ export namespace Hooks { [`dev.client.scripts`]: Set<(app: Framework) => string> [`middleware.enabled`]: Array [`middleware.proxy.target`]: URL + [`middleware.proxy.replacements`]: Array<[string, string]> // here down is wack [key: Server.Middleware.OptionsKey]: any diff --git a/sources/@roots/bud-preset-wordpress/src/index.ts b/sources/@roots/bud-preset-wordpress/src/index.ts index 0efbee1107..db6ec3fa58 100644 --- a/sources/@roots/bud-preset-wordpress/src/index.ts +++ b/sources/@roots/bud-preset-wordpress/src/index.ts @@ -10,7 +10,7 @@ * @packageDocumentation */ -import type {Extension} from '@roots/bud-framework' +import type {Extension, Framework} from '@roots/bud-framework' declare module '@roots/bud-framework' { interface Modules { @@ -36,4 +36,23 @@ type BudWordPressPreset = Extension.Module export const name: BudWordPressPreset['name'] = '@roots/bud-preset-wordpress' +export const boot = async (app: Framework) => { + app.hooks.on('middleware.proxy.replacements', replacements => { + const proxy = app.hooks.filter('middleware.proxy.target').origin + const dev = app.server.connection.url.origin + + return [ + ...(replacements ?? []), + [ + ` { bud.serve('http://example.com') + await bud.api.processQueue() expect(bud.hooks.filter('dev.url')).toStrictEqual( @@ -23,47 +24,20 @@ describe('bud.serve', function () { it('sets URL from URL', async () => { const testUrl = new URL('http://test-url.com') + bud.serve(testUrl) + await bud.api.processQueue() expect(bud.hooks.filter('dev.url')).toStrictEqual(testUrl) }) - it('sets URL from port number', async () => { - const port = 3000 - await bud.api.call('serve', port) - - expect(bud.hooks.filter('dev.url').origin).toStrictEqual( - `http://localhost:${port}`, - ) - }) - - it('sets URL from url prop', async () => { + it('sets options', async () => { const url = new URL('http://test-url.com') - await bud.api.call('serve', {url}) - expect(bud.hooks.filter('dev.url')).toStrictEqual(url) - }) - - it('registers cert with prop', async () => { - await bud.api.call('serve', {cert: 'foo'}) - expect(bud.hooks.filter('dev.ssl.cert')).toStrictEqual('foo') - }) + const options = {cert: 'foo', key: 'bar'} + await bud.api.call('serve', url, options) - it('registers key with prop', async () => { - await bud.api.call('serve', {key: 'foo'}) - expect(bud.hooks.filter('dev.ssl.key')).toStrictEqual('foo') - }) - - it('registers key with prop', async () => { - const props = { - watch: { - files: ['foo', 'bar'], - }, - } - await bud.api.call('serve', props) - expect(bud.hooks.filter('dev.watch.files')).toStrictEqual( - new Set(props.watch.files), - ) + expect(bud.hooks.filter('dev.options')).toStrictEqual(options) }) }) diff --git a/tests/unit/bud-api/repository/watch.test.ts b/tests/unit/bud-api/repository/watch.test.ts index b5bbe632b3..f7c8dafe7a 100644 --- a/tests/unit/bud-api/repository/watch.test.ts +++ b/tests/unit/bud-api/repository/watch.test.ts @@ -18,37 +18,16 @@ describe('bud.watch', function () { expect( Array.from(bud.hooks.filter('dev.watch.files')), - ).toMatchSnapshot(['**/*.js']) + ).toMatchSnapshot('**/*.js') }) it('merges watch files', async () => { - bud.watch(['foo/*.js']) + bud.watch('foo/*.js') await bud.api.processQueue() - expect( - Array.from(bud.hooks.filter('dev.watch.files')), - ).toMatchSnapshot(['**/*.js', 'foo/*.js']) - }) - - it('set watch options', async () => { - bud.watch([], {depth: 1}) - - await bud.api.processQueue() - - expect(bud.hooks.filter('dev.watch.options')).toMatchSnapshot({ - depth: 1, - }) - }) - - it('merges watch options', async () => { - bud.watch([], {cwd: '/srv/www'}) - - await bud.api.processQueue() - - expect(bud.hooks.filter('dev.watch.options')).toMatchSnapshot({ - cwd: '/srv/www', - depth: 1, - }) + expect(Array.from(bud.hooks.filter('dev.watch.files')).length).toEqual( + 2, + ) }) }) From 9e3cafd8dfa0933dd179c3cf21fee947478d17f2 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Thu, 24 Mar 2022 04:22:51 -0700 Subject: [PATCH 13/16] fix: test --- .../__snapshots__/watch.test.ts.snap | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/tests/unit/bud-api/repository/__snapshots__/watch.test.ts.snap b/tests/unit/bud-api/repository/__snapshots__/watch.test.ts.snap index 98260f6b08..b27d4f5128 100644 --- a/tests/unit/bud-api/repository/__snapshots__/watch.test.ts.snap +++ b/tests/unit/bud-api/repository/__snapshots__/watch.test.ts.snap @@ -1,31 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`bud.watch merges watch files 1`] = ` -Array [ - "**/*.js", - "foo/*.js", -] -`; - -exports[`bud.watch merges watch options 1`] = ` -Object { - "cwd": "/srv/www", - "depth": 1, -} -`; - -exports[`bud.watch set watch options 1`] = ` -Object { - "depth": 1, -} -`; - -exports[`bud.watch sets watch files 1`] = ` -Array [ - "**/*.js", -] -`; - exports[`bud.watch sets watch files: **/*.js 1`] = ` Array [ Array [ From 0b98030de71d42198e2479b514d1ae2c3d973416 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Thu, 24 Mar 2022 06:24:46 -0700 Subject: [PATCH 14/16] fix: test --- sources/@repo/docs/content/guides/paths.mdx | 7 ++ .../docs/content/guides/requirements.mdx | 2 +- .../src/Framework/framework.process.ts | 16 ++-- .../bud-framework/src/Server/Connection.ts | 16 ++-- sources/@roots/bud-hooks/src/Hooks/index.ts | 5 +- .../bud-server/src/middleware/proxy/index.ts | 2 +- sources/@roots/bud-server/src/seed.ts | 1 - sources/@roots/bud-server/src/server/index.ts | 4 +- .../bud-server/src/server/server.base.ts | 78 ++----------------- .../bud-server/src/server/server.http.ts | 16 ++-- .../bud-server/src/server/server.https.ts | 57 +++++++++++--- sources/@roots/bud/src/cli/commands/build.ts | 55 +++++++------ tests/util/project/src/styles/app.css | 3 + 13 files changed, 125 insertions(+), 137 deletions(-) create mode 100644 sources/@repo/docs/content/guides/paths.mdx diff --git a/sources/@repo/docs/content/guides/paths.mdx b/sources/@repo/docs/content/guides/paths.mdx new file mode 100644 index 0000000000..7c60ea8bf6 --- /dev/null +++ b/sources/@repo/docs/content/guides/paths.mdx @@ -0,0 +1,7 @@ +--- +title: Pathing +description: Working with paths +slug: installation +sidebar_label: Paths +sidebar_position: 3 +--- diff --git a/sources/@repo/docs/content/guides/requirements.mdx b/sources/@repo/docs/content/guides/requirements.mdx index 80da9e6432..28bf616826 100644 --- a/sources/@repo/docs/content/guides/requirements.mdx +++ b/sources/@repo/docs/content/guides/requirements.mdx @@ -3,7 +3,7 @@ title: Requirements description: bud.js requirements slug: requirements sidebar_label: Requirements -sidebar_position: 3 +sidebar_position: 4 --- :::danger WSL required for use in Windows diff --git a/sources/@roots/bud-framework/src/Framework/framework.process.ts b/sources/@roots/bud-framework/src/Framework/framework.process.ts index 6aaaa745b5..356e50406d 100644 --- a/sources/@roots/bud-framework/src/Framework/framework.process.ts +++ b/sources/@roots/bud-framework/src/Framework/framework.process.ts @@ -8,7 +8,7 @@ const {removeSync} = fs * Render error */ const renderError = (msg: string, name?: string) => { - global.process.stderr.write( + process.stderr.write( boxen(`\n${msg}\n`, { title: name ?? 'error', borderStyle: 'bold', @@ -32,14 +32,12 @@ const curryHandler = function (code: number) { ERROR && removeSync(this.path('@storage/cache')) } catch (err) {} - global.process.exitCode = code - global.process.exit() + process.exitCode = code + setTimeout(() => process.exit(), 100) } return (exitMessage: string | Error) => { - const exit = () => setTimeout(close, 100).unref() - - if (!ERROR) return exit() + if (!ERROR) return close() if (exitMessage instanceof Error) { renderError(exitMessage.message, exitMessage.name) @@ -47,7 +45,7 @@ const curryHandler = function (code: number) { renderError(`\n${exitMessage}\n`, 'error') } - return exit() + return close() } } @@ -65,12 +63,12 @@ const curryHandler = function (code: number) { export const initialize = (app: Framework) => { const makeHandler = curryHandler.bind(app) - global.process + process // only works when there is no task running // because we have a server always listening port, this handler will NEVER execute .on('beforeExit', makeHandler(0)) - // only works when the global.process normally exits + // only works when the process normally exits // on windows, ctrl-c will not trigger this handler (it is unnormal) // unless you listen on 'SIGINT' .on('exit', makeHandler(0)) diff --git a/sources/@roots/bud-framework/src/Server/Connection.ts b/sources/@roots/bud-framework/src/Server/Connection.ts index 483359b583..2ed78229e8 100644 --- a/sources/@roots/bud-framework/src/Server/Connection.ts +++ b/sources/@roots/bud-framework/src/Server/Connection.ts @@ -19,12 +19,12 @@ export type OptionsMap = { /** * Connection */ -export interface Connection { +export interface Connection { /** * Node server * @public */ - instance: T + instance: HttpServer | HttpsServer /** * Server URL @@ -32,19 +32,13 @@ export interface Connection { */ url: URL - /** - * Server port - * @public - */ - get port(): number - /** * Create server * @remarks * Returns Node server * @public */ - createServer: (app: any) => Promise + createServer(app: any): Promise /** * Setup @@ -103,14 +97,14 @@ export interface Connection { * * @public */ -export interface Http extends Connection {} +export interface Http extends Connection {} /** * Https Connection * * @public */ -export interface Https extends Connection { +export interface Https extends Connection { /** * Has SSL key * @public diff --git a/sources/@roots/bud-hooks/src/Hooks/index.ts b/sources/@roots/bud-hooks/src/Hooks/index.ts index e6cec941f2..0271331948 100644 --- a/sources/@roots/bud-hooks/src/Hooks/index.ts +++ b/sources/@roots/bud-hooks/src/Hooks/index.ts @@ -188,7 +188,10 @@ export class Hooks extends Service implements Contract { | ((value: Contract.Map[T]) => Contract.Map[T]) | Contract.Map[T], ) => { - return isFunction(current) ? current(accumulated) : current + const next = isFunction(current) ? current(accumulated) : current + if (this.app.context.args.debug) + this.app.info(`hooks.filter`, id, `=>`, next) + return next }, value, ) diff --git a/sources/@roots/bud-server/src/middleware/proxy/index.ts b/sources/@roots/bud-server/src/middleware/proxy/index.ts index 01f52297e4..9c12851f1d 100644 --- a/sources/@roots/bud-server/src/middleware/proxy/index.ts +++ b/sources/@roots/bud-server/src/middleware/proxy/index.ts @@ -18,7 +18,7 @@ export const proxy = (app: Framework) => { const options: Options = { autoRewrite: app.hooks.filter( 'middleware.proxy.options.autoRewrite', - true, + false, ), /** diff --git a/sources/@roots/bud-server/src/seed.ts b/sources/@roots/bud-server/src/seed.ts index 877aeda280..43a978b418 100644 --- a/sources/@roots/bud-server/src/seed.ts +++ b/sources/@roots/bud-server/src/seed.ts @@ -52,7 +52,6 @@ export const seed = (app: Framework) => { ]), ) .hooks.on(`dev.url`, new URL(`http://localhost`)) - .hooks.on(`dev.options`, {}) .hooks.on(`dev.watch.files`, new Set([])) .hooks.on(`dev.watch.options`, {}) } diff --git a/sources/@roots/bud-server/src/server/index.ts b/sources/@roots/bud-server/src/server/index.ts index e57eda6989..9466be8832 100644 --- a/sources/@roots/bud-server/src/server/index.ts +++ b/sources/@roots/bud-server/src/server/index.ts @@ -122,8 +122,8 @@ export class Server @once public async setConnection() { this.connection = - this.app.hooks.filter('dev.options.cert') && - this.app.hooks.filter('dev.options.key') + this.app.hooks.filter('dev.options').cert && + this.app.hooks.filter('dev.options').key ? new Https(this.app, this.app.hooks.filter('dev.url')) : new Http(this.app, this.app.hooks.filter('dev.url')) diff --git a/sources/@roots/bud-server/src/server/server.base.ts b/sources/@roots/bud-server/src/server/server.base.ts index 7b9c870ed8..c5e8e9ec2d 100644 --- a/sources/@roots/bud-server/src/server/server.base.ts +++ b/sources/@roots/bud-server/src/server/server.base.ts @@ -1,29 +1,22 @@ import {Framework, Server} from '@roots/bud-framework' import {Connection} from '@roots/bud-framework/src/Server/Connection' -import {bind, fs, getPort, Signale} from '@roots/bud-support' -import {IncomingMessage} from 'http' +import {bind, getPort, Signale} from '@roots/bud-support' +import {IncomingMessage, Server as HttpServer} from 'http' +import {Server as HttpsServer} from 'https' import {ServerResponse} from 'webpack-dev-middleware' -const {readFile} = fs - /** * HTTP Server * @public */ -export abstract class BaseServer implements Connection { - public abstract createServer: Connection['createServer'] +export abstract class BaseServer implements Connection { + public abstract createServer(app: any): Promise /** * Server instance * @public */ - public instance: T & Connection['instance'] - - /** - * Port number - * @public - */ - public port: number + public instance: Connection['instance'] /** * Logger @@ -45,61 +38,7 @@ export abstract class BaseServer implements Connection { * @public */ public constructor(public app: Framework, public url: URL) { - this.logger = this.app.logger.instance - } - - /** - * util: is path like - * @param subject - something or other - * @returns boolean if string and starts with '/' - */ - private isPathIsh(subject: unknown): boolean { - return typeof subject === 'string' && subject.startsWith('/') - } - - /** - * Has SSL key - * @public - */ - @bind - public hasKey(): boolean { - return this.options.key ? true : false - } - - /** - * Has SSL certificate - * @public - */ - @bind - public hasCert(): boolean { - return this.options.cert ? true : false - } - - /** - * Get SSL key - * @returns - */ - @bind - public async getKey(): Promise { - !this.hasKey() && this.app.error('Server key is not defined') - - return this.isPathIsh(this.options.key) - ? await readFile(this.options.key as string, 'utf8') - : this.options.key - } - - /** - * Get SSL certificate - * @public - * @decorator `@bind` - */ - @bind - public async getCert(): Promise { - !this.hasCert() && this.app.error('Server cert is not defined') - - return this.isPathIsh(this.options.cert) - ? await readFile(this.options.cert as string, 'utf8') - : this.options.cert + this.logger = this.app.logger.instance.scope('dev') } /** @@ -114,7 +53,7 @@ export abstract class BaseServer implements Connection { this.url.port = `${port}` this.app.hooks.on('dev.url', this.url) - this.logger.log(this.url.toString()) + this.logger.log('url', this.url.toString()) } /** @@ -166,6 +105,5 @@ export abstract class BaseServer implements Connection { @bind public onError(error: Error) { this.app.error(error) - return error } } diff --git a/sources/@roots/bud-server/src/server/server.http.ts b/sources/@roots/bud-server/src/server/server.http.ts index aa32d13841..7ee130d9b1 100644 --- a/sources/@roots/bud-server/src/server/server.http.ts +++ b/sources/@roots/bud-server/src/server/server.http.ts @@ -1,27 +1,27 @@ import {Server} from '@roots/bud-framework' +import {bind} from '@roots/bud-support' import {createServer, RequestListener, Server as HttpServer} from 'http' import {BaseServer} from './server.base' /** * HTTP Server - * * @public */ -export class Http - extends BaseServer - implements Server.Connection.Http -{ +export class Http extends BaseServer implements Server.Connection.Http { /** * createServer * - * @param app - Express application + * @param express - Express application * @returns http.Server + * @public + * @decorator `@bind` */ - public createServer = function ( + @bind + public async createServer( express: RequestListener, ): Promise { - this.instance = createServer(this.options, express) + this.instance = createServer(express) return this.instance } } diff --git a/sources/@roots/bud-server/src/server/server.https.ts b/sources/@roots/bud-server/src/server/server.https.ts index 2ce1239dd9..708eabff44 100644 --- a/sources/@roots/bud-server/src/server/server.https.ts +++ b/sources/@roots/bud-server/src/server/server.https.ts @@ -1,36 +1,75 @@ import {Server} from '@roots/bud-framework' +import {bind, fs, lodash} from '@roots/bud-support' import {RequestListener} from 'http' import {createServer, Server as HttpsServer} from 'https' import {BaseServer} from './server.base' +const {readFile} = fs +const {isUndefined} = lodash + /** * HTTPS Server * @public */ -export class Https - extends BaseServer - implements Server.Connection.Https -{ +export class Https extends BaseServer implements Server.Connection.Https { /** * Server instance * @public */ public instance: HttpsServer + /** + * Has SSL key + * @public + */ + @bind + public hasKey(): boolean { + return !isUndefined(this.options.key) + } + + /** + * Has SSL certificate + * @public + */ + @bind + public hasCert(): boolean { + return !isUndefined(this.options.cert) + } + + /** + * Get SSL key + * @returns + */ + @bind + public async getKey(): Promise { + !this.hasKey() && this.app.warn('Server key is not defined') + return await readFile(this.options.key as string, 'utf8') + } + + /** + * Get SSL certificate + * @public + * @decorator `@bind` + */ + @bind + public async getCert(): Promise { + !this.hasCert() && this.app.warn('Server cert is not defined') + return await readFile(this.options.cert as string, 'utf8') + } + /** * Create HTTPS server * @public */ - public createServer = async function ( + @bind + public async createServer( express: RequestListener & Express.Application, ): Promise { const key = await this.getKey() const cert = await this.getCert() - return (this.instance = createServer( - {...this.options, key, cert}, - express, - )) + this.instance = createServer({key, cert}, express) + return this.instance } } diff --git a/sources/@roots/bud/src/cli/commands/build.ts b/sources/@roots/bud/src/cli/commands/build.ts index 48e4bfee17..03b3f9a24c 100644 --- a/sources/@roots/bud/src/cli/commands/build.ts +++ b/sources/@roots/bud/src/cli/commands/build.ts @@ -90,6 +90,11 @@ export class BuildCommand extends BaseCommand { hidden: true, }) + public debug = Option.Boolean(`--debug`, false, { + description: + 'Enable debugging mode. Very verbose logging. Writes output files to `@storage` directory', + }) + /** * --devtool */ @@ -236,30 +241,32 @@ export class BuildCommand extends BaseCommand { this.context.stdout.write( `the --dashboard and --no-dashboard flags are deprecated and will be removed in a future release.\n`, ) - - this.context.args = { - cache: this.cache ?? null, - clean: this.clean ?? null, - devtool: this.devtool ?? null, - dist: this.dist ?? null, - flush: this.flush ?? null, - hash: this.hash ?? null, - html: this.html ?? null, - indicator: this.indicator ?? null, - inject: this.inject ?? null, - log: this.log ?? null, - verbose: this.verbose ?? null, - manifest: this.manifest ?? null, - minimize: this.minimize ?? null, - mode: this.mode ?? null, - modules: this.modules ?? null, - notify: this.notify ?? null, - overlay: this.overlay ?? null, - publicPath: this.publicPath ?? null, - src: this.src ?? null, - splitChunks: this.splitChunks ?? null, - target: this.target ?? null, - } + ;[ + 'cache', + 'ci', + 'clean', + 'debug', + 'devtool', + 'flush', + 'hash', + 'html', + 'indicator', + 'inject', + 'log', + 'manifest', + 'minimize', + 'mode', + 'modules', + 'notify', + 'overlay', + 'publicPath', + 'src', + 'splitChunks', + 'target', + 'verbose', + ].map(arg => { + this.context.args[arg] = isUndefined(arg) ? null : this[arg] + }) this.app = await factory({ name: 'bud', diff --git a/tests/util/project/src/styles/app.css b/tests/util/project/src/styles/app.css index 2ecc701ba7..667b731299 100644 --- a/tests/util/project/src/styles/app.css +++ b/tests/util/project/src/styles/app.css @@ -1 +1,4 @@ @import './common/global'; +body { + backgorun +} \ No newline at end of file From 92d1c207a3cf5dfb7097d66b56b84adaf3a3e512 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Thu, 24 Mar 2022 15:39:22 -0700 Subject: [PATCH 15/16] improve: logger-ish --- .../bud-server/src/middleware/proxy/index.ts | 5 +++- .../src/middleware/proxy/res.interceptor.ts | 6 +++-- .../bud-server/src/server/server.base.ts | 14 +++++----- .../bud-server/src/server/server.http.ts | 2 +- .../bud-server/src/server/server.https.ts | 4 ++- sources/@roots/bud/src/context/env.ts | 26 +++++++++++-------- sources/@roots/bud/src/services/Env/index.ts | 7 ----- 7 files changed, 34 insertions(+), 30 deletions(-) diff --git a/sources/@roots/bud-server/src/middleware/proxy/index.ts b/sources/@roots/bud-server/src/middleware/proxy/index.ts index 9c12851f1d..56daebf810 100644 --- a/sources/@roots/bud-server/src/middleware/proxy/index.ts +++ b/sources/@roots/bud-server/src/middleware/proxy/index.ts @@ -18,7 +18,7 @@ export const proxy = (app: Framework) => { const options: Options = { autoRewrite: app.hooks.filter( 'middleware.proxy.options.autoRewrite', - false, + true, ), /** @@ -37,6 +37,9 @@ export const proxy = (app: Framework) => { url.dev.host, ), + /** + * Follow redirects + */ followRedirects: app.hooks.filter( `middleware.proxy.options.followRedirects`, false, diff --git a/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts b/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts index 39572da432..94a4a49aba 100644 --- a/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts +++ b/sources/@roots/bud-server/src/middleware/proxy/res.interceptor.ts @@ -52,9 +52,10 @@ export class ResponseInterceptorFactory { * @remarks * This is the callback for `http-proxy-middleware`s `responseInterceptor`. * It is called after the response has been received from the target server. - * It is passed the response body, the response object, and the request object. + * It is passed the response body, and the req and res objects. * It can be used to modify the response body or the response object. * + * @param buffer - Buffered response * @param proxyRes - Response from the proxy * @param req - Request from the client * @param res - Response from the server @@ -72,7 +73,8 @@ export class ResponseInterceptorFactory { response.setHeader('x-proxy-by', '@roots/bud') response.setHeader('x-bud-proxy-origin', this.url.proxy.origin) response.setHeader('x-bud-dev-origin', this.url.dev.origin) - response.setHeader('x-http-method-override', '') + response.removeHeader('x-http-method-override') + Object.entries(request.cookies).map(([k, v]) => response.cookie(k, v, {domain: null}), ) diff --git a/sources/@roots/bud-server/src/server/server.base.ts b/sources/@roots/bud-server/src/server/server.base.ts index c5e8e9ec2d..7847e2391c 100644 --- a/sources/@roots/bud-server/src/server/server.base.ts +++ b/sources/@roots/bud-server/src/server/server.base.ts @@ -38,7 +38,7 @@ export abstract class BaseServer implements Connection { * @public */ public constructor(public app: Framework, public url: URL) { - this.logger = this.app.logger.instance.scope('dev') + this.logger = this.app.logger.instance.scope(this.url.host) } /** @@ -49,11 +49,9 @@ export abstract class BaseServer implements Connection { @bind public async setup() { const port = await getPort({port: Number(this.url.port)}) - this.url.port = `${port}` this.app.hooks.on('dev.url', this.url) - - this.logger.log('url', this.url.toString()) + this.logger.log('url', this.url) } /** @@ -91,9 +89,11 @@ export abstract class BaseServer implements Connection { request: IncomingMessage, response: ServerResponse, ) { - const kind = response.statusCode === 200 ? 'success' : 'error' - this.logger[kind]([response.statusCode], request.url) - + this.logger.log( + `[${response.statusCode}]`, + request.url, + response.statusMessage ?? '', + ) return response } diff --git a/sources/@roots/bud-server/src/server/server.http.ts b/sources/@roots/bud-server/src/server/server.http.ts index 7ee130d9b1..82637ac7b2 100644 --- a/sources/@roots/bud-server/src/server/server.http.ts +++ b/sources/@roots/bud-server/src/server/server.http.ts @@ -21,7 +21,7 @@ export class Http extends BaseServer implements Server.Connection.Http { public async createServer( express: RequestListener, ): Promise { - this.instance = createServer(express) + this.instance = createServer(this.options, express) return this.instance } } diff --git a/sources/@roots/bud-server/src/server/server.https.ts b/sources/@roots/bud-server/src/server/server.https.ts index 708eabff44..533c7dae32 100644 --- a/sources/@roots/bud-server/src/server/server.https.ts +++ b/sources/@roots/bud-server/src/server/server.https.ts @@ -61,6 +61,7 @@ export class Https extends BaseServer implements Server.Connection.Https { /** * Create HTTPS server * @public + * @decorator `@bind` */ @bind public async createServer( @@ -69,7 +70,8 @@ export class Https extends BaseServer implements Server.Connection.Https { const key = await this.getKey() const cert = await this.getCert() - this.instance = createServer({key, cert}, express) + this.instance = createServer({...this.options, key, cert}, express) + return this.instance } } diff --git a/sources/@roots/bud/src/context/env.ts b/sources/@roots/bud/src/context/env.ts index 12172a1481..9a8ca2f1a2 100644 --- a/sources/@roots/bud/src/context/env.ts +++ b/sources/@roots/bud/src/context/env.ts @@ -11,18 +11,22 @@ export class Env { * @returns */ public constructor(baseDirectory: string) { - const {parsed, error} = dotenv.config({ - path: join(baseDirectory, '.env'), - }) - if (error || !parsed) return + const paths = baseDirectory.split('/') - const expanded = dotenvExpand({ - ...parsed, - }) - if (!expanded) return + Object.entries(process.env).map(([k, v]) => (this[k] = v)) - Object.entries(expanded).map(([k, v]) => { - this[k] = v - }) + const get = (path: string) => { + const {parsed, error} = dotenv.config({path}) + if (error || !parsed) return + const expanded = dotenvExpand(parsed) + if (!expanded) return + Object.entries(expanded).map(([k, v]) => (this[k] = v)) + } + + paths.reduce((a, c) => { + const next = join(a, c) + get(join(next, '.env')) + return next + }, `/`) } } diff --git a/sources/@roots/bud/src/services/Env/index.ts b/sources/@roots/bud/src/services/Env/index.ts index 925a265180..5d1ca5515f 100644 --- a/sources/@roots/bud/src/services/Env/index.ts +++ b/sources/@roots/bud/src/services/Env/index.ts @@ -10,13 +10,6 @@ const {isString} = lodash * @public */ export class Env extends Service implements Base { - /** - * Service ident - * - * @internal - */ - public ident = 'env' - /** * Bootstrap event callback * From 388f90f898bd11021a9c26be488bb0355e85eb72 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Thu, 24 Mar 2022 16:07:47 -0700 Subject: [PATCH 16/16] fix: unbreak http server --- sources/@roots/bud-server/src/server/index.ts | 1 + .../bud-server/src/server/server.base.ts | 2 +- .../bud-server/src/server/server.http.ts | 6 +++ .../bud-server/src/server/server.https.ts | 48 +++++++++++++++---- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/sources/@roots/bud-server/src/server/index.ts b/sources/@roots/bud-server/src/server/index.ts index 9466be8832..d6568882d6 100644 --- a/sources/@roots/bud-server/src/server/index.ts +++ b/sources/@roots/bud-server/src/server/index.ts @@ -122,6 +122,7 @@ export class Server @once public async setConnection() { this.connection = + this.app.hooks.filter('dev.options') && this.app.hooks.filter('dev.options').cert && this.app.hooks.filter('dev.options').key ? new Https(this.app, this.app.hooks.filter('dev.url')) diff --git a/sources/@roots/bud-server/src/server/server.base.ts b/sources/@roots/bud-server/src/server/server.base.ts index 7847e2391c..3e56d19b1f 100644 --- a/sources/@roots/bud-server/src/server/server.base.ts +++ b/sources/@roots/bud-server/src/server/server.base.ts @@ -56,7 +56,6 @@ export abstract class BaseServer implements Connection { /** * Listen - * * @public * @decorator `@bind` */ @@ -94,6 +93,7 @@ export abstract class BaseServer implements Connection { request.url, response.statusMessage ?? '', ) + return response } diff --git a/sources/@roots/bud-server/src/server/server.http.ts b/sources/@roots/bud-server/src/server/server.http.ts index 82637ac7b2..f13f47df80 100644 --- a/sources/@roots/bud-server/src/server/server.http.ts +++ b/sources/@roots/bud-server/src/server/server.http.ts @@ -9,6 +9,12 @@ import {BaseServer} from './server.base' * @public */ export class Http extends BaseServer implements Server.Connection.Http { + /** + * Server instance + * @public + */ + public instance: HttpServer + /** * createServer * diff --git a/sources/@roots/bud-server/src/server/server.https.ts b/sources/@roots/bud-server/src/server/server.https.ts index 533c7dae32..433b1674b5 100644 --- a/sources/@roots/bud-server/src/server/server.https.ts +++ b/sources/@roots/bud-server/src/server/server.https.ts @@ -19,13 +19,24 @@ export class Https extends BaseServer implements Server.Connection.Https { */ public instance: HttpsServer + /** + * Has options + * @returns boolean + * @public + * @decorator `@bind` + */ + @bind + public hasOptions(): boolean { + return Object.keys(this.options).length > 0 + } + /** * Has SSL key * @public */ @bind public hasKey(): boolean { - return !isUndefined(this.options.key) + return this.hasOptions() && !isUndefined(this.options.key) } /** @@ -34,7 +45,7 @@ export class Https extends BaseServer implements Server.Connection.Https { */ @bind public hasCert(): boolean { - return !isUndefined(this.options.cert) + return this.hasOptions() && !isUndefined(this.options.cert) } /** @@ -43,8 +54,16 @@ export class Https extends BaseServer implements Server.Connection.Https { */ @bind public async getKey(): Promise { - !this.hasKey() && this.app.warn('Server key is not defined') - return await readFile(this.options.key as string, 'utf8') + if (!this.hasKey()) { + this.app.warn('Server key is not defined') + return + } + + try { + return await readFile(this.options.key as string, 'utf8') + } catch (err) { + this.app.error(err) + } } /** @@ -54,8 +73,16 @@ export class Https extends BaseServer implements Server.Connection.Https { */ @bind public async getCert(): Promise { - !this.hasCert() && this.app.warn('Server cert is not defined') - return await readFile(this.options.cert as string, 'utf8') + if (!this.hasKey()) { + this.app.warn('Server key is not defined') + return + } + + try { + return await readFile(this.options.cert as string, 'utf8') + } catch (err) { + this.app.error(err) + } } /** @@ -67,11 +94,16 @@ export class Https extends BaseServer implements Server.Connection.Https { public async createServer( express: RequestListener & Express.Application, ): Promise { + if (!this.hasOptions()) { + this.instance = createServer(express) + return this.instance + } + const key = await this.getKey() const cert = await this.getCert() + const options = {...this.options, ...({key} ?? {}), ...({cert} ?? {})} - this.instance = createServer({...this.options, key, cert}, express) - + this.instance = createServer(options, express) return this.instance } }