@excom/neutron 0.1.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 (63) hide show
  1. package/.rush/temp/chunked-rush-logs/neutron.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/neutron.build_package-metas.chunks.jsonl +1 -0
  3. package/.rush/temp/operation/apply-exports/all.log +1 -0
  4. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  5. package/.rush/temp/operation/apply-exports/state.json +3 -0
  6. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  7. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  8. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  9. package/.rush/temp/shrinkwrap-deps.json +3 -0
  10. package/config/rig.json +6 -0
  11. package/index.ts +9 -0
  12. package/package.json +45 -0
  13. package/rush-logs/neutron.apply-exports.cache.log +1 -0
  14. package/rush-logs/neutron.apply-exports.log +1 -0
  15. package/rush-logs/neutron.build_package-metas.cache.log +1 -0
  16. package/rush-logs/neutron.build_package-metas.log +1 -0
  17. package/src/command.ts +102 -0
  18. package/src/common-element.ts +377 -0
  19. package/src/constants.ts +101 -0
  20. package/src/devtools-hook.ts +93 -0
  21. package/src/lifecycle-configs.ts +304 -0
  22. package/src/neutron-element.ts +72 -0
  23. package/src/neutron-error.ts +6 -0
  24. package/src/neutron-internal.ts +550 -0
  25. package/src/neutron.ts +36 -0
  26. package/src/types/effect.types.ts +104 -0
  27. package/src/types/element.types.ts +263 -0
  28. package/src/types/index.ts +4 -0
  29. package/src/types/new.types.ts +159 -0
  30. package/src/types/shared.types.ts +25 -0
  31. package/src/utils/effect.ts +357 -0
  32. package/src/utils/element.ts +382 -0
  33. package/src/utils/index.ts +2 -0
  34. package/support/docs/COMMANDS.md +58 -0
  35. package/support/docs/COMPOSE.md +32 -0
  36. package/support/docs/DEBUG.md +11 -0
  37. package/support/docs/DEFINE.md +20 -0
  38. package/support/docs/EFFECTS.md +49 -0
  39. package/support/docs/EVENTS.md +57 -0
  40. package/support/docs/LIFECYCLES.md +69 -0
  41. package/support/docs/METHODS.md +49 -0
  42. package/support/docs/PROMISE_PROPS.md +29 -0
  43. package/support/docs/PROPS.md +64 -0
  44. package/support/docs/PROP_REACTIONS.md +35 -0
  45. package/support/docs/PROVISION.md +31 -0
  46. package/support/docs/README.md +118 -0
  47. package/support/docs/RECOMPOSE.md +70 -0
  48. package/support/docs/TYPESCRIPT.md +51 -0
  49. package/support/docs-sections.json +42 -0
  50. package/support/package-meta.json +129 -0
  51. package/support/tests/commands.test.ts +330 -0
  52. package/support/tests/common-element.test.ts +342 -0
  53. package/support/tests/devtools-hook.test.ts +209 -0
  54. package/support/tests/devtools-renderer.test.ts +125 -0
  55. package/support/tests/effects.test.ts +253 -0
  56. package/support/tests/element-config.test.ts +331 -0
  57. package/support/tests/entry.test.ts +68 -0
  58. package/support/tests/lifecycles.test.ts +489 -0
  59. package/support/tests/loop-guard.test.ts +162 -0
  60. package/support/tests/neutron.test.ts +1286 -0
  61. package/support/tests/recompose.test.ts +129 -0
  62. package/support/tests/utils.test.ts +75 -0
  63. package/tsconfig.json +5 -0
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 34.12787900000001
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 100.51614900000004
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "../../packages/neutron": "../../packages/neutron:2ltidcL43Q8EBcT7X0HR04ZwzOZtzgd4fdh0lnnFXxA=:"
3
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
3
+ "rigPackageName": "@excom/heft-rig",
4
+ "rigProfile": "default"
5
+ }
6
+
package/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ export * from "./src/command";
2
+ export * from "./src/devtools-hook";
3
+ export * from "./src/neutron";
4
+ export * from "./src/neutron-element";
5
+ export * from "./src/neutron-internal";
6
+ export * from "./src/types";
7
+ export * from "./src/utils";
8
+ // Prop-type marker for space-separated token attributes (runtime value: `string[]`).
9
+ export { TokenList } from "@excom/kit-utils";
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@excom/neutron",
3
+ "version": "0.1.0",
4
+ "description": "Element factory of the Nucleus Stack — typed custom elements with effect-based lifecycles",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=24.13.0"
8
+ },
9
+ "type": "module",
10
+ "dependencies": {
11
+ "@excom/kit-devtools": "^0.1.0",
12
+ "@excom/kit-logger": "^0.1.0",
13
+ "@excom/kit-utils": "^0.1.0"
14
+ },
15
+ "peerDependencies": {},
16
+ "devDependencies": {
17
+ "@excom/heft-rig": "^0.1.0"
18
+ },
19
+ "repository": {
20
+ "url": "excom-dev/nucleus",
21
+ "directory": "packages/neutron"
22
+ },
23
+ "homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/neutron/support/docs/README.md",
24
+ "bugs": "https://github.com/excom-dev/nucleus/issues",
25
+ "keywords": [
26
+ "Neutron",
27
+ "neutron",
28
+ "kit-element-base",
29
+ "custom-elements",
30
+ "nucleus-stack"
31
+ ],
32
+ "excom": {
33
+ "packageType": "element-base"
34
+ },
35
+ "scripts": {
36
+ "build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
37
+ "build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
38
+ "format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
39
+ "test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
40
+ "coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
41
+ "dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
42
+ "preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
43
+ "build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs"
44
+ }
45
+ }
@@ -0,0 +1 @@
1
+ Caching has been disabled for this project's "apply-exports" command.
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
package/src/command.ts ADDED
@@ -0,0 +1,102 @@
1
+ import { KitLogger } from "@excom/kit-logger";
2
+
3
+ /**
4
+ * The `command` event an element receives from the HTML Command API — a
5
+ * `<button command="--verb" commandfor="id">`, `<event-handler
6
+ * command-name="--verb">`, or a script-built `CommandEvent`. It never
7
+ * bubbles, is cancelable and composed. `command` is the verb; `source` is
8
+ * the invoker (the button) or `null`.
9
+ */
10
+ export type TCommandEvent = Event & {
11
+ readonly command: string;
12
+ readonly source: Element | null;
13
+ };
14
+
15
+ export type CommandInit = {
16
+ /** Element that receives the command. Defaults to the dispatching element. */
17
+ target?: Element | null;
18
+ /** Reported as `event.source`. Defaults to the dispatching element. */
19
+ source?: Element | null;
20
+ };
21
+
22
+ /** Arguments of the `command` effect key: `command: ["--fetch", { target }]`. */
23
+ export type CommandArgs = [string, CommandInit?];
24
+
25
+ export const COMMAND_EVENT = "command";
26
+
27
+ /**
28
+ * Custom commands are dashed-idents (`--fetch`), like CSS custom
29
+ * properties; the platform reserves every other name for built-ins.
30
+ */
31
+ export const isCustomCommand = (name: unknown): name is string =>
32
+ typeof name === "string" && /^--\S+$/.test(name);
33
+
34
+ type CommandEventCtor = new (
35
+ type: string,
36
+ init?: EventInit & { command?: string; source?: Element | null }
37
+ ) => TCommandEvent;
38
+
39
+ /**
40
+ * Build a `command` event. Uses the platform `CommandEvent` when the
41
+ * browser has one; otherwise a plain `Event` carrying the same two
42
+ * read-only fields, so `onCommand` handlers run identically either way.
43
+ */
44
+ export function createCommandEvent(
45
+ command: string,
46
+ init: { source?: Element | null } = {}
47
+ ): TCommandEvent {
48
+ const source = init.source ?? null;
49
+ const eventInit = { bubbles: false, cancelable: true, composed: true };
50
+ const Ctor = (globalThis as { CommandEvent?: CommandEventCtor }).CommandEvent;
51
+ if (Ctor) {
52
+ return new Ctor(COMMAND_EVENT, { ...eventInit, command, source });
53
+ }
54
+ const event = new Event(COMMAND_EVENT, eventInit) as TCommandEvent;
55
+ Object.defineProperties(event, {
56
+ command: { value: command, enumerable: true },
57
+ source: { value: source, enumerable: true },
58
+ });
59
+ return event;
60
+ }
61
+
62
+ /**
63
+ * Invoke `command` on `target`. A custom command (`--verb`) is dispatched
64
+ * directly and the event is returned. A built-in verb (`show-modal`,
65
+ * `toggle-popover`, …) can only run through the platform: an invisible
66
+ * proxy `<button command commandfor>` is clicked and removed, and `null`
67
+ * is returned (browsers without the Command API log a warning and do
68
+ * nothing).
69
+ */
70
+ export function invokeCommand(
71
+ target: Element,
72
+ command: string,
73
+ source: Element | null = null
74
+ ): TCommandEvent | null {
75
+ if (isCustomCommand(command)) {
76
+ const event = createCommandEvent(command, { source });
77
+ target.dispatchEvent(event);
78
+ return event;
79
+ }
80
+ const proto = HTMLButtonElement.prototype as { commandForElement?: unknown };
81
+ if (!("commandForElement" in proto)) {
82
+ KitLogger.warn(
83
+ `Command "${command}" needs the HTML Command API (\`commandfor\`), which this browser lacks. Custom commands (\`--name\`) work everywhere.`
84
+ );
85
+ return null;
86
+ }
87
+ const proxy = target.ownerDocument.createElement(
88
+ "button"
89
+ ) as HTMLButtonElement & { commandForElement: Element; command: string };
90
+ // No attribute writes here: test complexity meters count `setAttribute`.
91
+ // Detached from any form, the proxy cannot submit, so `type` can stay.
92
+ proxy.style.display = "none";
93
+ target.ownerDocument.body.appendChild(proxy);
94
+ proxy.commandForElement = target;
95
+ proxy.command = command;
96
+ try {
97
+ proxy.click();
98
+ } finally {
99
+ proxy.remove();
100
+ }
101
+ return null;
102
+ }
@@ -0,0 +1,377 @@
1
+ import { CommandArgs, invokeCommand } from "./command";
2
+ import type { NeutronElement as TNeutronElement } from "./neutron-element";
3
+ import type { NeutronInternal as TNeutronInternal } from "./neutron-internal";
4
+ import type {
5
+ EmitArgs,
6
+ NativeNElement,
7
+ RuntimeConfig,
8
+ SuperAddEventListenerOptions,
9
+ } from "./types";
10
+ import { KitLogger } from "@excom/kit-logger";
11
+
12
+ const findExistingListenerIndex = (listenerArray, type, fn) => {
13
+ return listenerArray.findIndex(([t, f]) => t === type && f === fn);
14
+ };
15
+
16
+ export const deref = <T extends HTMLElement | Node | EventTarget>(
17
+ el: T | WeakRef<T>
18
+ ): T | undefined => {
19
+ return el instanceof WeakRef ? el.deref() : el;
20
+ };
21
+
22
+ export const getNamespace = (
23
+ _element: NativeNElement | HTMLElement | TNeutronElement
24
+ ): NativeNElement["_n_"] => {
25
+ const element = _element as NativeNElement;
26
+ if (!element._n_) {
27
+ element._n_ = {
28
+ element: element,
29
+ eventListeners: [],
30
+ disconnectedEventListeners: [],
31
+ disconnectedBroadcastListeners: [],
32
+ broadcastListeners: [],
33
+ };
34
+ }
35
+ if (!element._n_.eventListeners) {
36
+ element._n_.eventListeners = [];
37
+ }
38
+ if (!element._n_.broadcastListeners) {
39
+ element._n_.broadcastListeners = [];
40
+ }
41
+ return element._n_;
42
+ };
43
+
44
+ export const buildEventType = (
45
+ type: string,
46
+ ctr: typeof TNeutronInternal | undefined,
47
+ opts: OptsWithInternal = { _broadcast: false }
48
+ ): string => {
49
+ const config = ctr?.runtimeConfig as RuntimeConfig;
50
+ if (!opts._broadcast && config?.events?.[type]?.prefixWithTag) {
51
+ return `${config.tag}-${type}`;
52
+ } else if (opts._broadcast && config?.broadcasts?.[type]?.prefixWithTag) {
53
+ return `${config.tag}-${type}`;
54
+ }
55
+ return type;
56
+ };
57
+
58
+ export const BROADCAST_CHANNEL = new EventTarget();
59
+
60
+ interface OptsWithInternal extends SuperAddEventListenerOptions {
61
+ _broadcast?: boolean;
62
+ }
63
+
64
+ const CommonElement = {
65
+ addListener(_type, fn, _opts: OptsWithInternal = { _broadcast: false }) {
66
+ const { element, ctr } = getNamespace(this);
67
+ const type = buildEventType(_type, ctr, _opts);
68
+
69
+ // TODO clean up this inconsistent _parent args approach
70
+ const { _broadcast, target, ...opts } = _opts;
71
+ const listenerArray = _broadcast
72
+ ? element._n_.broadcastListeners
73
+ : element._n_.eventListeners;
74
+ const foundIndex = findExistingListenerIndex(listenerArray, type, fn);
75
+ const host = _broadcast ? BROADCAST_CHANNEL : target || element;
76
+ if (foundIndex < 0) {
77
+ // first registration of this (type, fn)
78
+ host.addEventListener.apply(host, [type, fn, opts]);
79
+ listenerArray.push([
80
+ type,
81
+ fn,
82
+ // @ts-ignore fix weakref typing here
83
+ {
84
+ ...opts,
85
+ ...(target ? { target: new WeakRef(target) } : {}),
86
+ },
87
+ ]);
88
+ }
89
+ // same fn already listed: leave it
90
+ },
91
+
92
+ addBroadcastListener(type, fn, opts: SuperAddEventListenerOptions = {}) {
93
+ CommonElement.addListener.apply(this, [
94
+ type,
95
+ fn,
96
+ { ...opts, _broadcast: true },
97
+ ]);
98
+ },
99
+
100
+ removeListener(_type, fn, _opts: OptsWithInternal = { _broadcast: false }) {
101
+ const { element, ctr } = getNamespace(this);
102
+ const type = buildEventType(_type, ctr, _opts);
103
+
104
+ const { _broadcast } = _opts;
105
+
106
+ const listenerArray = _broadcast
107
+ ? element._n_.broadcastListeners
108
+ : element._n_.eventListeners;
109
+ const foundIndex = findExistingListenerIndex(listenerArray, type, fn);
110
+ if (foundIndex > -1) {
111
+ const listenerArgsToRemove = listenerArray[foundIndex];
112
+ if (!_broadcast) {
113
+ const host = _opts.target || element;
114
+ host.removeEventListener.apply(host, listenerArgsToRemove);
115
+ element._n_.eventListeners = element._n_.eventListeners.filter(
116
+ ([t, f]) =>
117
+ !(t === listenerArgsToRemove[0] && f === listenerArgsToRemove[1])
118
+ );
119
+ } else {
120
+ BROADCAST_CHANNEL.removeEventListener.apply(
121
+ BROADCAST_CHANNEL,
122
+ listenerArgsToRemove
123
+ );
124
+ element._n_.broadcastListeners = element._n_.broadcastListeners.filter(
125
+ ([t, f]) =>
126
+ !(t === listenerArgsToRemove[0] && f === listenerArgsToRemove[1])
127
+ );
128
+ }
129
+ }
130
+ },
131
+
132
+ removeBroadcastListener(type, fn, opts: SuperAddEventListenerOptions = {}) {
133
+ CommonElement.removeListener.apply(this, [
134
+ type,
135
+ fn,
136
+ { ...opts, _broadcast: true },
137
+ ]);
138
+ },
139
+
140
+ addListeners(...args: [string, Function, OptsWithInternal][]) {
141
+ args.forEach(([type, listener, options]) =>
142
+ CommonElement.addListener.apply(this, [type, listener, options])
143
+ );
144
+ },
145
+
146
+ addBroadcastListeners(
147
+ ...args: [string, Function, SuperAddEventListenerOptions][]
148
+ ) {
149
+ args.forEach(([type, listener, options]) =>
150
+ CommonElement.addListener.apply(this, [
151
+ type,
152
+ listener,
153
+ { ...(options || {}), _broadcast: true },
154
+ ])
155
+ );
156
+ },
157
+
158
+ removeListeners(...args: [string, Function, OptsWithInternal][]) {
159
+ args.forEach((params) => CommonElement.removeListener.apply(this, params));
160
+ },
161
+
162
+ removeBroadcastListeners(
163
+ ...args: [string, Function, SuperAddEventListenerOptions][]
164
+ ) {
165
+ args.forEach(([type, listener, options]) =>
166
+ CommonElement.removeListener.apply(this, [
167
+ type,
168
+ listener,
169
+ { ...(options || {}), _broadcast: true },
170
+ ])
171
+ );
172
+ },
173
+
174
+ toggleListeners(...args: [string, Function, boolean, OptsWithInternal][]) {
175
+ args.forEach(([type, listener, toggle, options]) => {
176
+ const action = toggle ? "addListener" : "removeListener";
177
+ CommonElement[action].apply(this, [type, listener, options]);
178
+ });
179
+ },
180
+
181
+ toggleBroadcastListeners(
182
+ ...args: [string, Function, boolean, SuperAddEventListenerOptions][]
183
+ ) {
184
+ args.forEach(([type, listener, toggle, options]) => {
185
+ const action = toggle ? "addListener" : "removeListener";
186
+ CommonElement[action].apply(this, [
187
+ type,
188
+ listener,
189
+ { ...(options || {}), _broadcast: true },
190
+ ]);
191
+ });
192
+ },
193
+
194
+ removeAllListeners(opts = { _broadcast: false }) {
195
+ const { element } = getNamespace(this);
196
+ if (!opts?._broadcast) {
197
+ element._n_.eventListeners
198
+ ?.filter((params) =>
199
+ params?.[2]?.target ? !!deref(params[2].target) : true
200
+ )
201
+ ?.forEach((params) =>
202
+ CommonElement.removeListener.apply(element, [
203
+ params[0],
204
+ params[1],
205
+ {
206
+ ...params[2],
207
+ ...(params[2]?.target ? { target: deref(params[2].target) } : {}),
208
+ },
209
+ ])
210
+ );
211
+ } else {
212
+ element._n_.broadcastListeners?.forEach((params) =>
213
+ CommonElement.removeListener.apply(element, [
214
+ params[0],
215
+ params[1],
216
+ { ...(params[2] || {}), _broadcast: true },
217
+ ])
218
+ );
219
+ }
220
+ },
221
+
222
+ removeAllBroadcastListeners() {
223
+ CommonElement.removeAllListeners.apply(this, [{ _broadcast: true }]);
224
+ },
225
+
226
+ internal_disconnectEventListeners(opts = { _broadcast: false }) {
227
+ const { element } = getNamespace(this);
228
+ if (!opts?._broadcast) {
229
+ element._n_.disconnectedEventListeners = element._n_.eventListeners || [];
230
+ element._n_.eventListeners
231
+ ?.filter((params) =>
232
+ params?.[2]?.target ? !!deref(params[2].target) : true
233
+ )
234
+ ?.forEach((params) =>
235
+ CommonElement.removeListener.apply(element, [
236
+ params[0],
237
+ params[1],
238
+ {
239
+ ...params[2],
240
+ ...(params[2]?.target ? { target: deref(params[2].target) } : {}),
241
+ },
242
+ ])
243
+ );
244
+ } else {
245
+ element._n_.disconnectedBroadcastListeners =
246
+ element._n_.broadcastListeners || [];
247
+ element._n_.broadcastListeners?.forEach((params) =>
248
+ CommonElement.removeListener.apply(element, [
249
+ params[0],
250
+ params[1],
251
+ { ...(params[2] || {}), _broadcast: true },
252
+ ])
253
+ );
254
+ }
255
+ },
256
+ internal_disconnectBroadcastListeners(opts = {}) {
257
+ CommonElement.internal_disconnectEventListeners.apply(this, [
258
+ { ...opts, _broadcast: true },
259
+ ]);
260
+ },
261
+
262
+ internal_reconnectEventListeners(opts = { _broadcast: false }) {
263
+ const { element } = getNamespace(this);
264
+ if (!opts?._broadcast) {
265
+ element._n_.disconnectedEventListeners
266
+ // `once` is not reconnected: we cannot tell if it already fired
267
+ ?.filter(
268
+ (params) =>
269
+ !params?.[2]?.once &&
270
+ (params?.[2]?.target ? !!deref(params[2].target) : true)
271
+ )
272
+ ?.forEach((params) =>
273
+ CommonElement.addListener.apply(element, [
274
+ params[0],
275
+ params[1],
276
+ {
277
+ ...params[2],
278
+ ...(params[2]?.target ? { target: deref(params[2].target) } : {}),
279
+ },
280
+ ])
281
+ );
282
+ element._n_.disconnectedEventListeners = [];
283
+ } else {
284
+ element._n_.disconnectedBroadcastListeners
285
+ ?.filter((params) => !params?.[2]?.once)
286
+ ?.forEach((params) =>
287
+ CommonElement.addListener.apply(element, [
288
+ params[0],
289
+ params[1],
290
+ { ...(params[2] || {}), _broadcast: true },
291
+ ])
292
+ );
293
+ element._n_.disconnectedBroadcastListeners = [];
294
+ }
295
+ },
296
+ internal_reconnectBroadcastListeners(opts = {}) {
297
+ CommonElement.internal_reconnectEventListeners.apply(this, [
298
+ { ...opts, _broadcast: true },
299
+ ]);
300
+ },
301
+
302
+ emit(
303
+ _type: EmitArgs[0],
304
+ eventInitObj: EmitArgs[1] = {},
305
+ opts = { _broadcast: false }
306
+ ) {
307
+ const { target, ...eventInit } = eventInitObj;
308
+ const el = target || this;
309
+ if (!_type || !el) {
310
+ throw new Error(
311
+ `CommonElement.emit: Event target and type are required.`
312
+ );
313
+ }
314
+ const { element, ctr } = getNamespace(el);
315
+ const type = buildEventType(_type, ctr, opts);
316
+
317
+ const event = new CustomEvent(type, {
318
+ bubbles: true,
319
+ cancelable: true,
320
+ composed: true,
321
+ ...eventInit,
322
+ });
323
+ if (!element.isConnected) {
324
+ KitLogger.warn(
325
+ `CommonElement.emit: Element is not connected: "${
326
+ element.localName
327
+ }". Dispatched ${
328
+ opts._broadcast ? "broadcast" : "event"
329
+ } "${type}". This means your element is likely incorrectly emitting events either before connection (possibly eager prop handlers or firing event in .onConstructed()) or after disconnection (possibly due to a lack of cleanup code in .onDisconnected()). Failure to fix this issue may lead to memory leaks or unexpected behavior.`
330
+ );
331
+ }
332
+ (opts._broadcast ? BROADCAST_CHANNEL : element).dispatchEvent(event);
333
+ return event;
334
+ },
335
+
336
+ emits(...eventConfigs) {
337
+ const { element } = getNamespace(this);
338
+ return eventConfigs.map((conf) => CommonElement.emit.apply(element, conf));
339
+ },
340
+
341
+ broadcast(type, eventInitObj = {}) {
342
+ CommonElement.emit.apply(this, [
343
+ type,
344
+ { ...eventInitObj, bubbles: false },
345
+ { _broadcast: true },
346
+ ]);
347
+ },
348
+
349
+ broadcasts(...eventConfigs) {
350
+ const { element } = getNamespace(this);
351
+ return eventConfigs.map((conf) =>
352
+ CommonElement.broadcast.apply(element, conf)
353
+ );
354
+ },
355
+
356
+ /*
357
+ * Invoke a command: `command: ["--fetch", { target }]`. The element is
358
+ * the `source` (and the target, when none is given). A `--` command
359
+ * dispatches a `command` event; a built-in verb goes through a proxy
360
+ * button (see `invokeCommand`).
361
+ */
362
+ command(name: CommandArgs[0], init: CommandArgs[1] = {}) {
363
+ const { element } = getNamespace(this);
364
+ const target = init.target || element;
365
+ const source = init.source === undefined ? element : init.source;
366
+ return invokeCommand(target, name, source);
367
+ },
368
+
369
+ commands(...commandConfigs: CommandArgs[]) {
370
+ const { element } = getNamespace(this);
371
+ return commandConfigs.map((conf) =>
372
+ CommonElement.command.apply(element, conf)
373
+ );
374
+ },
375
+ };
376
+
377
+ export { CommonElement };
@@ -0,0 +1,101 @@
1
+ import { CommonElement } from "./common-element";
2
+ import type { DefaultConfig } from "./types";
3
+ import { camelToDash } from "@excom/kit-utils";
4
+
5
+ const ALWAYS_IGNORE = [undefined, NaN, 0n];
6
+ export const IGNORED_STR_VALUES = [false, 0, ...ALWAYS_IGNORE];
7
+ export const IGNORED_NUM_VALUES = [false, "", ...ALWAYS_IGNORE];
8
+ export const IGNORED_BOOL_VALUES = [0, "", ...ALWAYS_IGNORE];
9
+ export const IGNORED_PROP_VALUES = [0, false, "", ...ALWAYS_IGNORE];
10
+ export const IGNORED_FUNC_VALUES = [null, ...IGNORED_PROP_VALUES];
11
+ export const IGNORED_EFFECTOR_RESULT = [null, ...IGNORED_PROP_VALUES];
12
+ export type IgnoredEffectorResult = void | undefined | null | false | "" | 0;
13
+
14
+ export const DEFAULT_CONFIG: DefaultConfig = {
15
+ props: {
16
+ isMounted: {
17
+ type: Boolean,
18
+ notify: false,
19
+ attr: false,
20
+ },
21
+ isAdopted: {
22
+ type: Boolean,
23
+ notify: false,
24
+ attr: false,
25
+ },
26
+ wasMounted: {
27
+ type: Boolean,
28
+ notify: false,
29
+ attr: false,
30
+ },
31
+ isMoving: {
32
+ type: Boolean,
33
+ notify: false,
34
+ attr: false,
35
+ },
36
+ renderRoot: {
37
+ type: HTMLElement,
38
+ notify: false,
39
+ attr: false,
40
+ },
41
+ },
42
+ };
43
+
44
+ export const PROTECTED_PROP_NAMES = [
45
+ // NeutronInternal statics
46
+ "builtConfig",
47
+ "runtimeConfig",
48
+ "connected",
49
+ "constructed",
50
+ "adopted",
51
+ "error",
52
+ "propSet",
53
+ "propUnset",
54
+ "promiseResolved",
55
+ "promiseRejected",
56
+ "disconnected",
57
+ "broadcast",
58
+ "event",
59
+ "eventDefault",
60
+ "command",
61
+ "NeutronElement",
62
+ "CustomElement",
63
+ // NeutronElement instances
64
+ "element",
65
+ "ctr",
66
+ "eventListeners",
67
+ "disconnectedEventListeners",
68
+ "broadcastListeners",
69
+ "disconnectedBroadcastListeners",
70
+ "props",
71
+ "propStore",
72
+ "queueManager",
73
+ "debug",
74
+ // CommonElement keys
75
+ ...Object.keys(CommonElement),
76
+ // custom-element statics
77
+ "observedAttributes",
78
+ "NeutronInternal",
79
+ // custom-element instances
80
+ "_n_",
81
+ "_q_",
82
+ "connectedCallback",
83
+ "adoptedCallback",
84
+ "disconnectedCallback",
85
+ "attributeChangedCallback",
86
+ ...Object.keys(DEFAULT_CONFIG.props),
87
+ // Other
88
+ "returns",
89
+ // `content` is too generic; Quark and CSS already use it for children.
90
+ "content",
91
+ ];
92
+
93
+ export const PROTECTED_ATTR_NAMES = [
94
+ /^q-/,
95
+ /^n-/,
96
+ /^on-/,
97
+ /^off-/,
98
+ ...PROTECTED_PROP_NAMES.map((p) => new RegExp(`^${camelToDash(p)}$`)),
99
+ ];
100
+
101
+ export const PROPS_TO_DEEP_MERGE = ["style"];