@roots/bud-client 6.6.8 → 6.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/hot/client.d.ts +0 -2
- package/lib/hot/client.d.ts.map +1 -1
- package/lib/hot/client.js +0 -2
- package/lib/hot/components/overlay/overlay.component.d.ts +0 -2
- package/lib/hot/components/overlay/overlay.component.d.ts.map +1 -1
- package/lib/hot/components/overlay/overlay.component.js +0 -2
- package/package.json +23 -6
- package/src/hot/client.test.ts +78 -0
- package/src/hot/client.ts +156 -0
- package/src/hot/components/index.ts +33 -0
- package/src/hot/components/indicator/index.ts +11 -0
- package/src/hot/components/indicator/indicator.component.ts +216 -0
- package/src/hot/components/indicator/indicator.controller.ts +76 -0
- package/src/hot/components/indicator/indicator.pulse.ts +43 -0
- package/src/hot/components/overlay/index.ts +12 -0
- package/src/hot/components/overlay/overlay.component.ts +165 -0
- package/src/hot/components/overlay/overlay.controller.ts +86 -0
- package/src/hot/events.ts +95 -0
- package/src/hot/index.cts +7 -0
- package/src/hot/index.mts +9 -0
- package/src/hot/log.ts +42 -0
- package/src/hot/options.ts +40 -0
- package/src/index.cts +16 -0
- package/src/index.mts +16 -0
- package/src/intercept/index.ts +33 -0
- package/src/intercept/proxy-click-interceptor.ts +18 -0
- package/src/types/index.d.ts +54 -0
package/lib/hot/client.d.ts
CHANGED
package/lib/hot/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/hot/client.ts"],"names":[],"mappings":";AASA
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/hot/client.ts"],"names":[],"mappings":";AASA;;GAEG;AACH,eAAO,MAAM,MAAM,gBACJ,MAAM,cACP,mBAAmB,GAAG,qBA6InC,CAAA"}
|
package/lib/hot/client.js
CHANGED
|
@@ -16,8 +16,6 @@ import { makeLogger } from './log.js';
|
|
|
16
16
|
import * as clientOptions from './options.js';
|
|
17
17
|
/**
|
|
18
18
|
* Initializes bud.js HMR handling
|
|
19
|
-
*
|
|
20
|
-
* @public
|
|
21
19
|
*/
|
|
22
20
|
export const client = (queryString, webpackHot) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
21
|
/* Guard: EventSource browser support */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.component.d.ts","sourceRoot":"","sources":["../../../../src/hot/components/overlay/overlay.component.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"overlay.component.d.ts","sourceRoot":"","sources":["../../../../src/hot/components/overlay/overlay.component.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,SAAU,SAAQ,WAAW;IACjC,IAAI,EAAE,MAAM,CAAgB;IAEnC;;;;OAIG;IACI,OAAO,EAAE,GAAG,CAAA;IAEZ,iBAAiB,EAAE,GAAG,CAAA;IAE7B,IAAW,OAAO,WAEjB;;IAOM,YAAY,IAAI,IAAI;IA+G3B,WAAkB,kBAAkB,aAEnC;IAEM,wBAAwB;IAsBxB,iBAAiB;CAGzB"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roots/bud-client",
|
|
3
3
|
"description": "Client scripts for @roots/bud",
|
|
4
|
-
"version": "6.6.
|
|
4
|
+
"version": "6.6.10",
|
|
5
5
|
"homepage": "https://roots.io/bud",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
},
|
|
11
11
|
"contributors": [
|
|
12
12
|
{
|
|
13
|
-
"name": "
|
|
13
|
+
"name": "Kelly Mears",
|
|
14
|
+
"email": "developers@tinypixel.dev",
|
|
14
15
|
"url": "https://github.com/kellymears"
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
|
-
"name": "
|
|
18
|
-
"
|
|
18
|
+
"name": "Ben Word",
|
|
19
|
+
"email": "ben@benword.com",
|
|
20
|
+
"url": "https://github.com/retlehs"
|
|
19
21
|
}
|
|
20
22
|
],
|
|
21
23
|
"license": "MIT",
|
|
@@ -34,7 +36,9 @@
|
|
|
34
36
|
"node": ">=16"
|
|
35
37
|
},
|
|
36
38
|
"files": [
|
|
37
|
-
"
|
|
39
|
+
"docs",
|
|
40
|
+
"lib",
|
|
41
|
+
"src"
|
|
38
42
|
],
|
|
39
43
|
"main": "./lib/index.cjs",
|
|
40
44
|
"module": "./lib/index.mjs",
|
|
@@ -52,10 +56,23 @@
|
|
|
52
56
|
}
|
|
53
57
|
},
|
|
54
58
|
"devDependencies": {
|
|
59
|
+
"@roots/bud": "6.6.10",
|
|
55
60
|
"@skypack/package-check": "0.2.2",
|
|
56
|
-
"@types/node": "16.18.
|
|
61
|
+
"@types/node": "16.18.11",
|
|
57
62
|
"@types/webpack-env": "1.18.0"
|
|
58
63
|
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@roots/bud": "*",
|
|
66
|
+
"@types/webpack-env": "*"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"@roots/bud": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"@types/webpack-env": {
|
|
73
|
+
"optional": true
|
|
74
|
+
}
|
|
75
|
+
},
|
|
59
76
|
"volta": {
|
|
60
77
|
"extends": "../../../package.json"
|
|
61
78
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/**
|
|
3
|
+
* @vitest-environment jsdom
|
|
4
|
+
*/
|
|
5
|
+
import {describe, expect, it, vi} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import {client} from './client.js'
|
|
8
|
+
import {injectEvents} from './events.js'
|
|
9
|
+
import * as options from './options.js'
|
|
10
|
+
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
global.EventSource = class Events {
|
|
13
|
+
public constructor() {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
window.EventSource = global.EventSource
|
|
17
|
+
|
|
18
|
+
const Events = injectEvents(global.EventSource)
|
|
19
|
+
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
const webpackHotMock = {
|
|
22
|
+
hot: vi.fn(),
|
|
23
|
+
status: vi.fn(),
|
|
24
|
+
} as __WebpackModuleApi.Hot
|
|
25
|
+
|
|
26
|
+
describe(`@roots/bud-client`, () => {
|
|
27
|
+
it(`should be a fn module`, () => {
|
|
28
|
+
expect(client).toBeInstanceOf(Function)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it(`should add window.bud`, async () => {
|
|
32
|
+
await client(`?name=test`, webpackHotMock)
|
|
33
|
+
expect(window.bud).toBeDefined()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it(`should add window.bud.hmr as an instance of EventSource`, async () => {
|
|
37
|
+
await client(`?name=test`, webpackHotMock)
|
|
38
|
+
expect(window.bud?.hmr?.test).toBeInstanceOf(EventSource)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it(`should set clientOptions`, async () => {
|
|
42
|
+
await client(`?name=test`, webpackHotMock)
|
|
43
|
+
expect(options.data).toEqual(
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
debug: true,
|
|
46
|
+
indicator: true,
|
|
47
|
+
log: true,
|
|
48
|
+
name: `@roots/bud-client`,
|
|
49
|
+
overlay: true,
|
|
50
|
+
path: `/bud/hot`,
|
|
51
|
+
reload: true,
|
|
52
|
+
timeout: 2000,
|
|
53
|
+
}),
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it(`should call listener from onmessage`, async () => {
|
|
58
|
+
await client(`?name=test`, webpackHotMock)
|
|
59
|
+
const events = Events.make(options.data)
|
|
60
|
+
|
|
61
|
+
const listenerMock = vi.fn(async () => {})
|
|
62
|
+
events.addListener(listenerMock)
|
|
63
|
+
|
|
64
|
+
await events.onmessage(
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
{
|
|
67
|
+
data: `{
|
|
68
|
+
"data": {
|
|
69
|
+
"hash": "test",
|
|
70
|
+
"action": "update"
|
|
71
|
+
}
|
|
72
|
+
}`,
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
expect(listenerMock).toHaveBeenCalled()
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/* global __resourceQuery */
|
|
3
|
+
/* global __webpack_hash__ */
|
|
4
|
+
|
|
5
|
+
import * as components from './components/index.js'
|
|
6
|
+
import {injectEvents} from './events.js'
|
|
7
|
+
import {makeLogger} from './log.js'
|
|
8
|
+
import * as clientOptions from './options.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initializes bud.js HMR handling
|
|
12
|
+
*/
|
|
13
|
+
export const client = async (
|
|
14
|
+
queryString: string,
|
|
15
|
+
webpackHot: __WebpackModuleApi.Hot,
|
|
16
|
+
) => {
|
|
17
|
+
/* Guard: EventSource browser support */
|
|
18
|
+
if (typeof window?.EventSource === `undefined`) {
|
|
19
|
+
console.error(
|
|
20
|
+
`[bud] hot module reload requires EventSource to work. https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events#Tools`,
|
|
21
|
+
)
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
/* Guard: webpackHot api availability */
|
|
25
|
+
if (!webpackHot) {
|
|
26
|
+
console.error(
|
|
27
|
+
`[bud] hot module reload requires the webpack hot api to be available`,
|
|
28
|
+
)
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Set client options from URL params */
|
|
33
|
+
const options = clientOptions.setFromParameters(queryString)
|
|
34
|
+
/* Setup logger */
|
|
35
|
+
const logger = makeLogger(options)
|
|
36
|
+
|
|
37
|
+
if (typeof window.bud === `undefined`) {
|
|
38
|
+
window.bud = {
|
|
39
|
+
current: {},
|
|
40
|
+
hmr: {},
|
|
41
|
+
controllers: [],
|
|
42
|
+
listeners: {},
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!window.bud.current[options.name]) {
|
|
47
|
+
window.bud.current[options.name] = null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const isStale = (hash?: string) => {
|
|
51
|
+
if (hash) window.bud.current[options.name] = hash
|
|
52
|
+
return __webpack_hash__ === window.bud.current[options.name]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Webpack HMR check handler
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
const check = async () => {
|
|
61
|
+
if (webpackHot.status() === `idle`) {
|
|
62
|
+
await webpackHot.check(false)
|
|
63
|
+
|
|
64
|
+
requestAnimationFrame(async function whenReady() {
|
|
65
|
+
if (webpackHot.status() === `ready`) {
|
|
66
|
+
await update()
|
|
67
|
+
} else {
|
|
68
|
+
requestAnimationFrame(whenReady)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Webpack HMR unaccepted module handler
|
|
76
|
+
*
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
const onUnacceptedOrDeclined = (
|
|
80
|
+
info: __WebpackModuleApi.HotNotifierInfo,
|
|
81
|
+
) => {
|
|
82
|
+
console.warn(`[${options.name}] ${info.type}`, info)
|
|
83
|
+
options.reload && window.location.reload()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Webpack HMR error handler
|
|
88
|
+
*
|
|
89
|
+
* @internal
|
|
90
|
+
*/
|
|
91
|
+
const onErrored = (error: any) => {
|
|
92
|
+
window.bud.controllers.map(controller =>
|
|
93
|
+
controller?.update({
|
|
94
|
+
errors: [error],
|
|
95
|
+
}),
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Webpack HMR update handler
|
|
101
|
+
*
|
|
102
|
+
* @internal
|
|
103
|
+
*/
|
|
104
|
+
const update = async () => {
|
|
105
|
+
try {
|
|
106
|
+
await webpackHot.apply({
|
|
107
|
+
ignoreUnaccepted: true,
|
|
108
|
+
ignoreDeclined: true,
|
|
109
|
+
ignoreErrored: true,
|
|
110
|
+
onErrored,
|
|
111
|
+
onUnaccepted: onUnacceptedOrDeclined,
|
|
112
|
+
onDeclined: onUnacceptedOrDeclined,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
if (!isStale()) await check()
|
|
116
|
+
} catch (error) {
|
|
117
|
+
logger.error(error)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Instantiate indicator, overlay */
|
|
122
|
+
await components.make(options)
|
|
123
|
+
|
|
124
|
+
/* Instantiate eventSource */
|
|
125
|
+
const events = injectEvents(EventSource).make(options)
|
|
126
|
+
|
|
127
|
+
if (!window.bud.listeners[options.name]) {
|
|
128
|
+
window.bud.listeners[options.name] = async payload => {
|
|
129
|
+
if (!payload) return
|
|
130
|
+
|
|
131
|
+
if (options.reload && payload.action === `reload`)
|
|
132
|
+
return window.location.reload()
|
|
133
|
+
|
|
134
|
+
if (payload.name !== options.name) return
|
|
135
|
+
window.bud.controllers.map(controller => controller?.update(payload))
|
|
136
|
+
|
|
137
|
+
if (payload.errors?.length > 0) return
|
|
138
|
+
|
|
139
|
+
if (payload.action === `built` || payload.action === `sync`) {
|
|
140
|
+
if (isStale(payload.hash)) return
|
|
141
|
+
|
|
142
|
+
if (payload.action === `built`) {
|
|
143
|
+
logger.log(`built in ${payload.time}ms`)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await check()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* Instantiate HMR event source
|
|
152
|
+
* and register client listeners
|
|
153
|
+
*/
|
|
154
|
+
events.addListener(window.bud.listeners[options.name])
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface ControllerModule {
|
|
2
|
+
make: () => Promise<Controller>
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const make: (
|
|
6
|
+
options: Options,
|
|
7
|
+
) => Promise<Array<Controller>> = async options => {
|
|
8
|
+
if (options.indicator && !customElements.get(`bud-activity-indicator`)) {
|
|
9
|
+
await import(`./indicator/index.js`)
|
|
10
|
+
.then(makeController)
|
|
11
|
+
.then(maybePushController)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (options.overlay && !customElements.get(`bud-error`)) {
|
|
15
|
+
await import(`./overlay/index.js`)
|
|
16
|
+
.then(makeController)
|
|
17
|
+
.then(maybePushController)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return window.bud.controllers
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const makeController = async (
|
|
24
|
+
module: ControllerModule,
|
|
25
|
+
): Promise<Controller | undefined> => {
|
|
26
|
+
if (!module) return
|
|
27
|
+
return await module.make()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const maybePushController = (controller: Controller | undefined) => {
|
|
31
|
+
if (!controller) return
|
|
32
|
+
window.bud.controllers.push(controller)
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {Component} from './indicator.component.js'
|
|
2
|
+
import {Controller} from './indicator.controller.js'
|
|
3
|
+
|
|
4
|
+
export const make = async (): Promise<{
|
|
5
|
+
update: (data: Payload) => void
|
|
6
|
+
}> => {
|
|
7
|
+
if (customElements.get(`bud-activity-indicator`)) return
|
|
8
|
+
customElements.define(`bud-activity-indicator`, Component)
|
|
9
|
+
|
|
10
|
+
return new Controller()
|
|
11
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {pulse} from './indicator.pulse.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Indicator web component
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export class Component extends HTMLElement {
|
|
8
|
+
/**
|
|
9
|
+
* Has component rendered
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
public rendered: boolean
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Component name
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
public name: string = `bud-activity-indicator`
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Root div querySelector selector
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
public get selector() {
|
|
25
|
+
return `.${this.name}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Timer
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
public hideTimeout: NodeJS.Timer
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get accessor: has errors
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
public get hasErrors(): boolean {
|
|
39
|
+
return this.getAttribute(`has-errors`) == `true`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get accessor: has warnings
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
public get hasWarnings(): boolean {
|
|
47
|
+
return this.getAttribute(`has-warnings`) == `true`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Status indicator colors
|
|
52
|
+
* @public
|
|
53
|
+
*/
|
|
54
|
+
public colors: Record<string, [number, number, number, number]> = {
|
|
55
|
+
success: [4, 120, 87, 1],
|
|
56
|
+
error: [220, 38, 38, 1],
|
|
57
|
+
warn: [252, 211, 77, 1],
|
|
58
|
+
pending: [59, 130, 246, 1],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Class constructor
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
public constructor() {
|
|
66
|
+
super()
|
|
67
|
+
this.renderShadow()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Render status indicator
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
public renderShadow() {
|
|
75
|
+
const container = document.createElement(`div`)
|
|
76
|
+
container.classList.add(this.name)
|
|
77
|
+
container.innerHTML = `
|
|
78
|
+
<style>
|
|
79
|
+
.bud-activity-indicator {
|
|
80
|
+
position: fixed;
|
|
81
|
+
width: 10px;
|
|
82
|
+
height: 10px;
|
|
83
|
+
left: 10px;
|
|
84
|
+
bottom: 10px;
|
|
85
|
+
z-index: 9999;
|
|
86
|
+
margin: 5px;
|
|
87
|
+
padding: 5px;
|
|
88
|
+
-webkit-transition:
|
|
89
|
+
all .6s ease-in-out,
|
|
90
|
+
transition:
|
|
91
|
+
all .6s ease-in-out;
|
|
92
|
+
animation-fill-mode: forwards;
|
|
93
|
+
pointer-events: none;
|
|
94
|
+
border-radius: 50%;
|
|
95
|
+
transform: scale(0);
|
|
96
|
+
opacity: 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.show {
|
|
100
|
+
opacity: 1;
|
|
101
|
+
background-color: rgba(255, 255, 255, 1);
|
|
102
|
+
transform: scale(1);
|
|
103
|
+
transition:
|
|
104
|
+
all .6s ease-in-out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
${pulse(`success`, this.colors.success)}
|
|
108
|
+
${pulse(`error`, this.colors.error)}
|
|
109
|
+
${pulse(`warning`, this.colors.warn)}
|
|
110
|
+
${pulse(`pending`, this.colors.pending)}
|
|
111
|
+
|
|
112
|
+
</style>
|
|
113
|
+
`
|
|
114
|
+
|
|
115
|
+
this.attachShadow({mode: `open`}).appendChild(container)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Show status indicator
|
|
120
|
+
* @public
|
|
121
|
+
*/
|
|
122
|
+
public show() {
|
|
123
|
+
this.hideTimeout && clearTimeout(this.hideTimeout)
|
|
124
|
+
this.shadowRoot.querySelector(this.selector).classList.add(`show`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Hide status indicator
|
|
129
|
+
*/
|
|
130
|
+
public hide() {
|
|
131
|
+
this.hideTimeout = setTimeout(() => {
|
|
132
|
+
this.shadowRoot.querySelector(this.selector).classList.remove(`show`)
|
|
133
|
+
}, 2000)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Status is pending
|
|
138
|
+
* @public
|
|
139
|
+
*/
|
|
140
|
+
public onPending() {
|
|
141
|
+
this.show()
|
|
142
|
+
|
|
143
|
+
this.shadowRoot
|
|
144
|
+
.querySelector(this.selector)
|
|
145
|
+
.classList.remove(`error`, `warning`, `success`)
|
|
146
|
+
|
|
147
|
+
this.shadowRoot.querySelector(this.selector).classList.add(`pending`)
|
|
148
|
+
|
|
149
|
+
this.hide()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Status is success
|
|
154
|
+
* @public
|
|
155
|
+
*/
|
|
156
|
+
public onSuccess() {
|
|
157
|
+
this.show()
|
|
158
|
+
|
|
159
|
+
this.shadowRoot
|
|
160
|
+
.querySelector(this.selector)
|
|
161
|
+
.classList.remove(`error`, `warning`, `pending`)
|
|
162
|
+
|
|
163
|
+
this.shadowRoot.querySelector(this.selector).classList.add(`success`)
|
|
164
|
+
|
|
165
|
+
this.hide()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Status is error
|
|
170
|
+
* @public
|
|
171
|
+
*/
|
|
172
|
+
public onError() {
|
|
173
|
+
this.show()
|
|
174
|
+
|
|
175
|
+
this.shadowRoot
|
|
176
|
+
.querySelector(this.selector)
|
|
177
|
+
.classList.remove(`warning`, `success`, `pending`)
|
|
178
|
+
this.shadowRoot.querySelector(this.selector).classList.add(`error`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Status is warning
|
|
183
|
+
* @public
|
|
184
|
+
*/
|
|
185
|
+
public onWarning() {
|
|
186
|
+
this.show()
|
|
187
|
+
|
|
188
|
+
this.shadowRoot
|
|
189
|
+
.querySelector(this.selector)
|
|
190
|
+
.classList.remove(`error`, `success`, `pending`)
|
|
191
|
+
|
|
192
|
+
this.shadowRoot.querySelector(this.selector).classList.add(`warning`)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
public static get observedAttributes() {
|
|
196
|
+
return [`has-errors`, `has-warnings`, `action`]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public attributeChangedCallback() {
|
|
200
|
+
if (this.hasAttribute(`has-errors`)) return this.onError()
|
|
201
|
+
if (this.hasAttribute(`has-warnings`)) return this.onWarning()
|
|
202
|
+
|
|
203
|
+
if (
|
|
204
|
+
!this.hasAttribute(`has-errors`) &&
|
|
205
|
+
!this.hasAttribute(`has-warnings`) &&
|
|
206
|
+
this.getAttribute(`action`) === `built`
|
|
207
|
+
)
|
|
208
|
+
return this.onSuccess()
|
|
209
|
+
|
|
210
|
+
if (
|
|
211
|
+
this.getAttribute(`action`) == `building` ||
|
|
212
|
+
this.getAttribute(`action`) == `sync`
|
|
213
|
+
)
|
|
214
|
+
return this.onPending()
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity indicator controller
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
export class Controller {
|
|
6
|
+
/**
|
|
7
|
+
* DOM node
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
public node: HTMLElement
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Active WHM payload
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
public payload = null
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Timer handler
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
public timer: NodeJS.Timeout
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initialization
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
public constructor() {
|
|
29
|
+
this.node = document.createElement(`bud-activity-indicator`)
|
|
30
|
+
this.update = this.update.bind(this)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Append `bud-error` element to the DOM
|
|
35
|
+
*
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
public addNode() {
|
|
39
|
+
if (document.body.querySelector(`bud-activity-indicator`)) {
|
|
40
|
+
if (typeof this.timer.unref === `function`) this.timer.unref()
|
|
41
|
+
this.removeNode()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
document.body?.appendChild(this.node)
|
|
45
|
+
this.timer = setTimeout(this.removeNode, 3000)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Remove `bud-error` element from the DOM (if present)
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
public removeNode() {
|
|
54
|
+
document.body.querySelector(`bud-activity-indicator`)?.remove()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Update activity indicator
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
public update(payload: Payload) {
|
|
62
|
+
this.node.toggleAttribute(
|
|
63
|
+
`has-errors`,
|
|
64
|
+
payload.errors?.length ? true : false,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
this.node.toggleAttribute(
|
|
68
|
+
`has-warnings`,
|
|
69
|
+
payload.warnings?.length ? true : false,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
this.node.setAttribute(`action`, payload.action)
|
|
73
|
+
|
|
74
|
+
this.addNode()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface pulse {
|
|
2
|
+
(name: string, color: [number, number, number]): string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CSS animation for reload indicator
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export const pulse = (
|
|
10
|
+
name: string,
|
|
11
|
+
color: [number, number, number, number],
|
|
12
|
+
): string => `
|
|
13
|
+
.${name} {
|
|
14
|
+
box-shadow: 0 0 0 0 rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]});
|
|
15
|
+
animation: ${name}__pulse 2s infinite;
|
|
16
|
+
transition: all 0.4s ease-in-out;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.${name}:not(.show) {
|
|
20
|
+
background-color: rgba(${color[0]}, ${color[1]}, ${color[2]}, 0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.${name}.show {
|
|
24
|
+
background-color: rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@keyframes ${name}__pulse {
|
|
28
|
+
0% {
|
|
29
|
+
transform: scale(0.95);
|
|
30
|
+
box-shadow: 0 0 0 0 rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.7);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
70% {
|
|
34
|
+
transform: scale(1);
|
|
35
|
+
box-shadow: 0 0 0 10px rgba(${color[0]}, ${color[1]}, ${color[2]}, 0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
100% {
|
|
39
|
+
transform: scale(0.95);
|
|
40
|
+
box-shadow: 0 0 0 0 rgba(${color[0]}, ${color[1]}, ${color[2]}, 0);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {Component} from './overlay.component.js'
|
|
2
|
+
import {Controller} from './overlay.controller.js'
|
|
3
|
+
|
|
4
|
+
export const make = async (): Promise<{
|
|
5
|
+
update: (data: Payload) => void
|
|
6
|
+
}> => {
|
|
7
|
+
if (customElements.get(`bud-error`)) return
|
|
8
|
+
|
|
9
|
+
customElements.define(`bud-error`, Component)
|
|
10
|
+
|
|
11
|
+
return new Controller()
|
|
12
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component container
|
|
3
|
+
*/
|
|
4
|
+
export class Component extends HTMLElement {
|
|
5
|
+
public name: string = `bud-overlay`
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* WHM payload
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
public payload: any
|
|
13
|
+
|
|
14
|
+
public documentBodyStyle: any
|
|
15
|
+
|
|
16
|
+
public get message() {
|
|
17
|
+
return this.getAttribute(`message`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public constructor() {
|
|
21
|
+
super()
|
|
22
|
+
this.renderShadow()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public renderShadow(): void {
|
|
26
|
+
const container = document.createElement(`div`)
|
|
27
|
+
container.classList.add(`overlay`)
|
|
28
|
+
container.innerHTML = `
|
|
29
|
+
<style>
|
|
30
|
+
.overlay {
|
|
31
|
+
width: 100vw;
|
|
32
|
+
backdrop-filter: blur(10px);
|
|
33
|
+
display: flex;
|
|
34
|
+
height: 100vh;
|
|
35
|
+
border-top: 2px solid transparent;
|
|
36
|
+
overflow-x: hidden;
|
|
37
|
+
overflow-y: scroll;
|
|
38
|
+
position: absolute;
|
|
39
|
+
top: -1000px;
|
|
40
|
+
left: 0;
|
|
41
|
+
right: 0;
|
|
42
|
+
bottom: 0;
|
|
43
|
+
opacity: 0;
|
|
44
|
+
transition: opacity 0.2s ease-in-out, border 0.4s ease-in-out;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.visible {
|
|
49
|
+
position: fixed;
|
|
50
|
+
top: 0;
|
|
51
|
+
z-index: 9998;
|
|
52
|
+
opacity: 1;
|
|
53
|
+
border-top: 5px solid red;
|
|
54
|
+
transition: opacity 0.2s ease-in-out, border 0.4s ease-in-out;
|
|
55
|
+
max-width: 100vw;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.messages {
|
|
59
|
+
background-color: white;
|
|
60
|
+
border-radius: 5px;
|
|
61
|
+
filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
|
|
62
|
+
display: flex;
|
|
63
|
+
align-self: center;
|
|
64
|
+
width: 800px;
|
|
65
|
+
max-width: 90vw;
|
|
66
|
+
margin-left: auto;
|
|
67
|
+
margin-right: auto;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
flex-wrap: wrap;
|
|
70
|
+
align-items: center;
|
|
71
|
+
align-content: center;
|
|
72
|
+
padding: 2rem 2rem 0rem 2rem;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.visible .messages > div {
|
|
76
|
+
position: relative;
|
|
77
|
+
top: 0;
|
|
78
|
+
opacity: 1;
|
|
79
|
+
transition: all: 0.2s ease-in-out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.messages > div {
|
|
83
|
+
position: relative;
|
|
84
|
+
top: 20px;
|
|
85
|
+
opacity: 0;
|
|
86
|
+
transition: all: 0.2s ease-in-out;
|
|
87
|
+
align-items: center;
|
|
88
|
+
align-content: center;
|
|
89
|
+
color: rgba(0, 0, 0, 0.87);
|
|
90
|
+
flex-direction: column;
|
|
91
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
92
|
+
padding: 0rem 2rem 2rem 2rem;
|
|
93
|
+
width: 100%;
|
|
94
|
+
max-width:95vw;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.messages > div > pre {
|
|
98
|
+
font-weight: 300;
|
|
99
|
+
font-size: 0.8rem;
|
|
100
|
+
overflow-x: scroll;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
pre {
|
|
104
|
+
background: #303030;
|
|
105
|
+
color: #f1f1f1;
|
|
106
|
+
padding: 10px 16px;
|
|
107
|
+
border-radius: 2px;
|
|
108
|
+
border-top: 4px solid #dd0303;
|
|
109
|
+
-moz-box-shadow: inset 0 0 10px #000;
|
|
110
|
+
box-shadow: inset 0 0 10px #000;
|
|
111
|
+
counter-reset: line;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
pre span {
|
|
115
|
+
display: block;
|
|
116
|
+
line-height: 1.5rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pre span:before {
|
|
120
|
+
counter-increment: line;
|
|
121
|
+
content: counter(line);
|
|
122
|
+
display: inline-block;
|
|
123
|
+
border-right: 1px solid #ddd;
|
|
124
|
+
padding: 0 .5em;
|
|
125
|
+
margin-right: .5em;
|
|
126
|
+
color: #888;
|
|
127
|
+
width: 30px;
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
130
|
+
<div class="messages"></div>
|
|
131
|
+
`
|
|
132
|
+
|
|
133
|
+
this.attachShadow({mode: `open`}).appendChild(container)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public static get observedAttributes() {
|
|
137
|
+
return [`message`]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public attributeChangedCallback() {
|
|
141
|
+
if (!this.documentBodyStyle)
|
|
142
|
+
this.documentBodyStyle = document.body?.style
|
|
143
|
+
|
|
144
|
+
if (this.getAttribute(`message`)) {
|
|
145
|
+
document.body.style.overflow = `hidden`
|
|
146
|
+
|
|
147
|
+
this.shadowRoot.querySelector(`.overlay`).classList.add(`visible`)
|
|
148
|
+
|
|
149
|
+
this.shadowRoot.querySelector(`.messages`).innerHTML =
|
|
150
|
+
this.getAttribute(`message`)
|
|
151
|
+
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (this.documentBodyStyle?.overflow && document?.body?.style) {
|
|
156
|
+
document.body.style.overflow = this.documentBodyStyle.overflow
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.shadowRoot.querySelector(`.overlay`).classList.remove(`visible`)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public connectedCallback() {
|
|
163
|
+
if (document.body?.style) this.documentBodyStyle = document.body.style
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const ansiPattern = [
|
|
2
|
+
`[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)`,
|
|
3
|
+
`(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))`,
|
|
4
|
+
].join(`|`)
|
|
5
|
+
|
|
6
|
+
const stripAnsi = (body: string) =>
|
|
7
|
+
body?.replace?.(new RegExp(ansiPattern, `g`), ``) ?? body
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Overlay controller
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export class Controller {
|
|
14
|
+
/**
|
|
15
|
+
* Element
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
public element: HTMLElement
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* HMR update
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
public payload: Payload
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Formatted error message
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
public get message(): string {
|
|
31
|
+
return this.payload.errors?.reduce((a, c) => {
|
|
32
|
+
const msg = c?.message ?? c?.error ?? c
|
|
33
|
+
if (!msg) return a
|
|
34
|
+
return `${a}
|
|
35
|
+
<div>
|
|
36
|
+
<pre>${stripAnsi(msg)}</pre>
|
|
37
|
+
</div>`
|
|
38
|
+
}, ``)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Class constructor
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
public constructor() {
|
|
47
|
+
this.update = this.update.bind(this)
|
|
48
|
+
this.element = document.createElement(`bud-error`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Append `bud-error` element to the DOM
|
|
53
|
+
*
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
public createError() {
|
|
57
|
+
!document.body.querySelector(`bud-error`) &&
|
|
58
|
+
document.body?.appendChild(this.element)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Remove `bud-error` element from the DOM (if present)
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
public removeError() {
|
|
67
|
+
document.body.querySelector(`bud-error`)?.remove()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Update DOM
|
|
72
|
+
*
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
public update(payload: Payload): void {
|
|
76
|
+
this.payload = payload
|
|
77
|
+
|
|
78
|
+
this.element.setAttribute(`message`, this.message ?? ``)
|
|
79
|
+
|
|
80
|
+
if (this.payload.errors?.length > 0) {
|
|
81
|
+
return this.createError()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.removeError()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
export const injectEvents = (
|
|
4
|
+
eventSource: new (path: string) => EventSource,
|
|
5
|
+
) => {
|
|
6
|
+
/**
|
|
7
|
+
* EventSource wrapper
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* wraps EventSource in a function to allow for
|
|
11
|
+
* mocking in tests
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
return class Events extends eventSource {
|
|
16
|
+
/**
|
|
17
|
+
* Registered listeners
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
public listeners: Set<Listener> = new Set<Listener>()
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Class constructor
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* Singleton interface, so this is private.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
private constructor(
|
|
32
|
+
public options: Partial<Options> & {name: string; path: string},
|
|
33
|
+
) {
|
|
34
|
+
super(options.path)
|
|
35
|
+
|
|
36
|
+
this.onopen = this.onopen.bind(this)
|
|
37
|
+
this.onmessage = this.onmessage.bind(this)
|
|
38
|
+
this.addListener = this.addListener.bind(this)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Singleton constructor
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
public static make(
|
|
47
|
+
options: Partial<Options> & {name: string; path: string},
|
|
48
|
+
): Events {
|
|
49
|
+
if (typeof window.bud.hmr[options.name] === `undefined`)
|
|
50
|
+
Object.assign(window.bud.hmr, {
|
|
51
|
+
[options.name]: new Events(options),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return window.bud.hmr[options.name]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* EventSource `onopen` handler
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
public override onopen = function () {}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* EventSource `onmessage` handler
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
public override onmessage = async function (payload: MessageEvent) {
|
|
68
|
+
if (!payload?.data || payload.data == `\uD83D\uDC93`) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const data = JSON.parse(payload.data)
|
|
74
|
+
if (!data) return
|
|
75
|
+
|
|
76
|
+
await Promise.all(
|
|
77
|
+
[...this.listeners].map(async listener => {
|
|
78
|
+
return await listener(data)
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
} catch (ex) {
|
|
82
|
+
console.warn(`Invalid HMR message: ${payload.data} \n ${ex}`)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* EventSource `addMessageListener` handler
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
90
|
+
public addListener(listener: Listener): this {
|
|
91
|
+
this.listeners.add(listener)
|
|
92
|
+
return this
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/hot/log.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
export const makeLogger = (options: Options) => {
|
|
4
|
+
return {
|
|
5
|
+
log: makeLog(options),
|
|
6
|
+
error: makeError(options),
|
|
7
|
+
warn: makeWarn(options),
|
|
8
|
+
info: makeInfo(options),
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let lastLog: string = null
|
|
13
|
+
export const makeLog = options => {
|
|
14
|
+
return (...args) => {
|
|
15
|
+
if (options.log) {
|
|
16
|
+
if (lastLog === args.join(``)) return
|
|
17
|
+
lastLog = args.join(``)
|
|
18
|
+
|
|
19
|
+
console.log(`[${options.name}]`, ...args)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const makeInfo = options => {
|
|
25
|
+
return (...args) => {
|
|
26
|
+
if (options.log) {
|
|
27
|
+
console.info(`[${options.name}]`, ...args)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const makeError = options => {
|
|
33
|
+
return (...args) => {
|
|
34
|
+
console.error(`[${options.name}]`, ...args)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const makeWarn = options => {
|
|
39
|
+
return (...args) => {
|
|
40
|
+
console.warn(`[${options.name}]`, ...args)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client options
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
let data: Options = {
|
|
6
|
+
timeout: 2000,
|
|
7
|
+
reload: true,
|
|
8
|
+
name: `@roots/bud-client`,
|
|
9
|
+
debug: true,
|
|
10
|
+
log: true,
|
|
11
|
+
indicator: true,
|
|
12
|
+
overlay: true,
|
|
13
|
+
path: `/bud/hot`,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get client option
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
const get = (name?: string, key?: string) =>
|
|
21
|
+
key ? data[name][key] : data[name]
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set client data based on URL parameters
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
const setFromParameters = (query: string): Options => {
|
|
28
|
+
let parsedParams: Partial<Options> = {}
|
|
29
|
+
|
|
30
|
+
new window.URLSearchParams(query).forEach((value, key) => {
|
|
31
|
+
parsedParams[key] =
|
|
32
|
+
value === `true` ? true : value === `false` ? false : value
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
data[parsedParams.name] = {...data, ...parsedParams}
|
|
36
|
+
|
|
37
|
+
return data[parsedParams.name]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {data, get, setFromParameters}
|
package/src/index.cts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright © Roots Software Foundation LLC
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `@roots/bud` client scripts
|
|
6
|
+
*
|
|
7
|
+
* You should not import this root module.
|
|
8
|
+
* Import the components from the submodules instead.
|
|
9
|
+
*
|
|
10
|
+
* @see https://bud.js.org
|
|
11
|
+
* @see https://github.com/roots/bud
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export {}
|
package/src/index.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright © Roots Software Foundation LLC
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `@roots/bud` client scripts
|
|
6
|
+
*
|
|
7
|
+
* You should not import this root module.
|
|
8
|
+
* Import the components from the submodules instead.
|
|
9
|
+
*
|
|
10
|
+
* @see https://bud.js.org
|
|
11
|
+
* @see https://github.com/roots/bud
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export {}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type Target = HTMLAnchorElement | HTMLLinkElement | HTMLFormElement
|
|
2
|
+
type ElementTuple = [HTMLCollectionOf<Target>, any]
|
|
3
|
+
|
|
4
|
+
const intercept = (...args: [string, string]) => {
|
|
5
|
+
args.every(arg => typeof arg === `string`) &&
|
|
6
|
+
setInterval(
|
|
7
|
+
() =>
|
|
8
|
+
[
|
|
9
|
+
[document.getElementsByTagName(`a`), `href`],
|
|
10
|
+
[document.getElementsByTagName(`link`), `href`],
|
|
11
|
+
]
|
|
12
|
+
.map(
|
|
13
|
+
([elements, attribute]: ElementTuple): [
|
|
14
|
+
Array<Target>,
|
|
15
|
+
any,
|
|
16
|
+
] => [Array.from(elements), attribute],
|
|
17
|
+
)
|
|
18
|
+
.forEach(([elements, attribute]: [Array<Target>, any]) =>
|
|
19
|
+
elements
|
|
20
|
+
.filter(el => el.hasAttribute(attribute))
|
|
21
|
+
.filter(el => !el.hasAttribute(`__bud_processed`))
|
|
22
|
+
.filter(el => el.getAttribute(attribute).startsWith(args[0]))
|
|
23
|
+
.map(el => {
|
|
24
|
+
const value = el.getAttribute(attribute).replace(...args)
|
|
25
|
+
el.setAttribute(attribute, value)
|
|
26
|
+
el.toggleAttribute(`__bud_processed`)
|
|
27
|
+
}),
|
|
28
|
+
),
|
|
29
|
+
1000,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default intercept
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/* global __resourceQuery */
|
|
3
|
+
|
|
4
|
+
import intercept from './index.js'
|
|
5
|
+
|
|
6
|
+
window.requestAnimationFrame(async function ready() {
|
|
7
|
+
if (!__resourceQuery) return
|
|
8
|
+
|
|
9
|
+
const params = new URLSearchParams(__resourceQuery)
|
|
10
|
+
if (!params || !params.has(`search`) || !params.has(`replace`)) return
|
|
11
|
+
|
|
12
|
+
const search = decodeURI(params.get(`search`))
|
|
13
|
+
const replace = decodeURI(params.get(`replace`))
|
|
14
|
+
|
|
15
|
+
return document.body
|
|
16
|
+
? intercept(search, replace)
|
|
17
|
+
: window.requestAnimationFrame(ready)
|
|
18
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
declare var __resourceQuery: string
|
|
2
|
+
declare var __webpack_hash__: string
|
|
3
|
+
|
|
4
|
+
interface Listener {
|
|
5
|
+
(event: Payload): void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare interface Events {
|
|
9
|
+
options: Partial<Options> & {name: string; path: string}
|
|
10
|
+
listeners: Set<Listener>
|
|
11
|
+
addListener(fn: Listener): this
|
|
12
|
+
onopen: (ev?: Event) => void
|
|
13
|
+
onmessage: (ev?: MessageEvent) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare interface Payload {
|
|
17
|
+
name: string
|
|
18
|
+
type: `middleware` | __WebpackModuleApi.HotNotifierInfo[`type`]
|
|
19
|
+
action: 'reload' | 'sync' | 'building' | 'built'
|
|
20
|
+
hash?: string
|
|
21
|
+
time?: number
|
|
22
|
+
errors?: Array<Record<string, any>>
|
|
23
|
+
warnings?: Array<string>
|
|
24
|
+
message?: string
|
|
25
|
+
modules?: Record<string, Array<string>>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare interface Controller {
|
|
29
|
+
update: (payload: Partial<Payload>) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare interface Options {
|
|
33
|
+
timeout: number
|
|
34
|
+
reload: boolean
|
|
35
|
+
debug: boolean
|
|
36
|
+
log: boolean
|
|
37
|
+
name: string
|
|
38
|
+
path: string
|
|
39
|
+
indicator: boolean
|
|
40
|
+
overlay: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare var bud: {
|
|
44
|
+
current?: Record<string, string>
|
|
45
|
+
controllers?: Array<Controller>
|
|
46
|
+
hmr?: Record<string, Events & EventSource>
|
|
47
|
+
listeners?: Record<string, Listener>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
declare module global {
|
|
51
|
+
interface Window {
|
|
52
|
+
bud: typeof bud
|
|
53
|
+
}
|
|
54
|
+
}
|