@roots/bud-client 0.0.0

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.
Files changed (77) hide show
  1. package/LICENSE.md +19 -0
  2. package/README.md +77 -0
  3. package/lib/hot/client.d.ts +6 -0
  4. package/lib/hot/client.d.ts.map +1 -0
  5. package/lib/hot/client.js +143 -0
  6. package/lib/hot/components/index.d.ts +2 -0
  7. package/lib/hot/components/index.d.ts.map +1 -0
  8. package/lib/hot/components/index.js +32 -0
  9. package/lib/hot/components/indicator/index.d.ts +4 -0
  10. package/lib/hot/components/indicator/index.d.ts.map +1 -0
  11. package/lib/hot/components/indicator/index.js +17 -0
  12. package/lib/hot/components/indicator/indicator.component.d.ts +84 -0
  13. package/lib/hot/components/indicator/indicator.component.d.ts.map +1 -0
  14. package/lib/hot/components/indicator/indicator.component.js +175 -0
  15. package/lib/hot/components/indicator/indicator.controller.d.ts +45 -0
  16. package/lib/hot/components/indicator/indicator.controller.d.ts.map +1 -0
  17. package/lib/hot/components/indicator/indicator.controller.js +54 -0
  18. package/lib/hot/components/indicator/indicator.pulse.d.ts +9 -0
  19. package/lib/hot/components/indicator/indicator.pulse.d.ts.map +1 -0
  20. package/lib/hot/components/indicator/indicator.pulse.js +36 -0
  21. package/lib/hot/components/overlay/index.d.ts +4 -0
  22. package/lib/hot/components/overlay/index.d.ts.map +1 -0
  23. package/lib/hot/components/overlay/index.js +17 -0
  24. package/lib/hot/components/overlay/overlay.component.d.ts +20 -0
  25. package/lib/hot/components/overlay/overlay.component.d.ts.map +1 -0
  26. package/lib/hot/components/overlay/overlay.component.js +146 -0
  27. package/lib/hot/components/overlay/overlay.controller.d.ts +46 -0
  28. package/lib/hot/components/overlay/overlay.controller.d.ts.map +1 -0
  29. package/lib/hot/components/overlay/overlay.controller.js +70 -0
  30. package/lib/hot/events.d.ts +98 -0
  31. package/lib/hot/events.d.ts.map +1 -0
  32. package/lib/hot/events.js +89 -0
  33. package/lib/hot/index.cjs +5 -0
  34. package/lib/hot/index.d.cts +2 -0
  35. package/lib/hot/index.d.cts.map +1 -0
  36. package/lib/hot/index.d.mts +2 -0
  37. package/lib/hot/index.d.mts.map +1 -0
  38. package/lib/hot/index.mjs +17 -0
  39. package/lib/hot/log.d.ts +11 -0
  40. package/lib/hot/log.d.ts.map +1 -0
  41. package/lib/hot/log.js +37 -0
  42. package/lib/hot/options.d.ts +17 -0
  43. package/lib/hot/options.d.ts.map +1 -0
  44. package/lib/hot/options.js +33 -0
  45. package/lib/index.cjs +3 -0
  46. package/lib/index.d.cts +13 -0
  47. package/lib/index.d.cts.map +1 -0
  48. package/lib/index.d.mts +13 -0
  49. package/lib/index.d.mts.map +1 -0
  50. package/lib/index.mjs +3 -0
  51. package/lib/intercept/index.d.ts +3 -0
  52. package/lib/intercept/index.d.ts.map +1 -0
  53. package/lib/intercept/index.js +18 -0
  54. package/lib/intercept/proxy-click-interceptor.d.ts +2 -0
  55. package/lib/intercept/proxy-click-interceptor.d.ts.map +1 -0
  56. package/lib/intercept/proxy-click-interceptor.js +26 -0
  57. package/package.json +79 -0
  58. package/src/hot/client.test.ts +78 -0
  59. package/src/hot/client.ts +156 -0
  60. package/src/hot/components/index.ts +33 -0
  61. package/src/hot/components/indicator/index.ts +11 -0
  62. package/src/hot/components/indicator/indicator.component.ts +216 -0
  63. package/src/hot/components/indicator/indicator.controller.ts +76 -0
  64. package/src/hot/components/indicator/indicator.pulse.ts +43 -0
  65. package/src/hot/components/overlay/index.ts +12 -0
  66. package/src/hot/components/overlay/overlay.component.ts +165 -0
  67. package/src/hot/components/overlay/overlay.controller.ts +86 -0
  68. package/src/hot/events.ts +93 -0
  69. package/src/hot/index.cts +7 -0
  70. package/src/hot/index.mts +9 -0
  71. package/src/hot/log.ts +42 -0
  72. package/src/hot/options.ts +40 -0
  73. package/src/index.cts +16 -0
  74. package/src/index.mts +16 -0
  75. package/src/intercept/index.ts +33 -0
  76. package/src/intercept/proxy-click-interceptor.ts +18 -0
  77. package/src/types/index.d.ts +54 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/hot/options.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,QAAA,IAAI,IAAI,EAAE,OAST,CAAA;AAED;;;GAGG;AACH,QAAA,MAAM,GAAG,UAAW,MAAM,QAAQ,MAAM,QACJ,CAAA;AAEpC;;;GAGG;AACH,QAAA,MAAM,iBAAiB,UAAW,MAAM,KAAG,OAW1C,CAAA;AAED,OAAO,EAAC,IAAI,EAAE,GAAG,EAAE,iBAAiB,EAAC,CAAA"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Client options
3
+ * @public
4
+ */
5
+ let data = {
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
+ * Get client option
17
+ * @public
18
+ */
19
+ const get = (name, key) => key ? data[name][key] : data[name];
20
+ /**
21
+ * Set client data based on URL parameters
22
+ * @public
23
+ */
24
+ const setFromParameters = (query) => {
25
+ let parsedParams = {};
26
+ new window.URLSearchParams(query).forEach((value, key) => {
27
+ parsedParams[key] =
28
+ value === `true` ? true : value === `false` ? false : value;
29
+ });
30
+ data[parsedParams.name] = Object.assign(Object.assign({}, data), parsedParams);
31
+ return data[parsedParams.name];
32
+ };
33
+ export { data, get, setFromParameters };
package/lib/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ // Copyright © Roots Software Foundation LLC
2
+ // Licensed under the MIT license.
3
+ export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@roots/bud` client scripts
3
+ *
4
+ * You should not import this root module.
5
+ * Import the components from the submodules instead.
6
+ *
7
+ * @see https://bud.js.org
8
+ * @see https://github.com/roots/bud
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,CAAA"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@roots/bud` client scripts
3
+ *
4
+ * You should not import this root module.
5
+ * Import the components from the submodules instead.
6
+ *
7
+ * @see https://bud.js.org
8
+ * @see https://github.com/roots/bud
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,CAAA"}
package/lib/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ // Copyright © Roots Software Foundation LLC
2
+ // Licensed under the MIT license.
3
+ export {};
@@ -0,0 +1,3 @@
1
+ declare const intercept: (args_0: string, args_1: string) => void;
2
+ export default intercept;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/intercept/index.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,SAAS,0CA2Bd,CAAA;AAED,eAAe,SAAS,CAAA"}
@@ -0,0 +1,18 @@
1
+ const intercept = (...args) => {
2
+ args.every(arg => typeof arg === `string`) &&
3
+ setInterval(() => [
4
+ [document.getElementsByTagName(`a`), `href`],
5
+ [document.getElementsByTagName(`link`), `href`],
6
+ ]
7
+ .map(([elements, attribute]) => [Array.from(elements), attribute])
8
+ .forEach(([elements, attribute]) => elements
9
+ .filter(el => el.hasAttribute(attribute))
10
+ .filter(el => !el.hasAttribute(`__bud_processed`))
11
+ .filter(el => el.getAttribute(attribute).startsWith(args[0]))
12
+ .map(el => {
13
+ const value = el.getAttribute(attribute).replace(...args);
14
+ el.setAttribute(attribute, value);
15
+ el.toggleAttribute(`__bud_processed`);
16
+ })), 1000);
17
+ };
18
+ export default intercept;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proxy-click-interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-click-interceptor.d.ts","sourceRoot":"","sources":["../../src/intercept/proxy-click-interceptor.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ /* eslint-disable no-console */
2
+ /* global __resourceQuery */
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ import intercept from './index.js';
13
+ window.requestAnimationFrame(function ready() {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ if (!__resourceQuery)
16
+ return;
17
+ const params = new URLSearchParams(__resourceQuery);
18
+ if (!params || !params.has(`search`) || !params.has(`replace`))
19
+ return;
20
+ const search = decodeURI(params.get(`search`));
21
+ const replace = decodeURI(params.get(`replace`));
22
+ return document.body
23
+ ? intercept(search, replace)
24
+ : window.requestAnimationFrame(ready);
25
+ });
26
+ });
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@roots/bud-client",
3
+ "description": "Client scripts for @roots/bud",
4
+ "version": "0.0.0",
5
+ "homepage": "https://roots.io/bud",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/roots/bud.git",
9
+ "directory": "sources/@roots/bud-client"
10
+ },
11
+ "contributors": [
12
+ {
13
+ "name": "Kelly Mears",
14
+ "email": "developers@tinypixel.dev",
15
+ "url": "https://github.com/kellymears"
16
+ },
17
+ {
18
+ "name": "Ben Word",
19
+ "email": "ben@benword.com",
20
+ "url": "https://github.com/retlehs"
21
+ }
22
+ ],
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/roots/bud/issues"
26
+ },
27
+ "funding": {
28
+ "type": "github sponsors",
29
+ "url": "https://github.com/sponsors/roots"
30
+ },
31
+ "keywords": [
32
+ "bud",
33
+ "bud-framework"
34
+ ],
35
+ "engines": {
36
+ "node": ">=16"
37
+ },
38
+ "files": [
39
+ "docs",
40
+ "lib",
41
+ "src"
42
+ ],
43
+ "main": "./lib/index.cjs",
44
+ "module": "./lib/index.mjs",
45
+ "types": "./lib/index.d.ts",
46
+ "exports": {
47
+ ".": {
48
+ "types": "./lib/index.d.mts",
49
+ "require": "./lib/index.cjs",
50
+ "default": "./lib/index.mjs"
51
+ },
52
+ "./lib/*": {
53
+ "types": "./lib/*.d.mts",
54
+ "require": "./lib/*",
55
+ "default": "./lib/*"
56
+ }
57
+ },
58
+ "devDependencies": {
59
+ "@roots/bud": "0.0.0",
60
+ "@skypack/package-check": "0.2.2",
61
+ "@types/node": "18.11.18",
62
+ "@types/webpack-env": "1.18.0"
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
+ },
76
+ "volta": {
77
+ "extends": "../../../package.json"
78
+ }
79
+ }
@@ -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
+ }