@symbiotejs/symbiote 3.5.1 → 3.5.2

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/CHANGELOG.md CHANGED
@@ -1,12 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.5.2
4
+
5
+ ### Fixed
6
+
7
+ - **`devMode` flag is now cross-module safe.** Moved to `globalThis.__SYMBIOTE_DEV_MODE` so it works correctly across CDN/importmap module scopes (same pattern as `PubSub.globalStore`).
8
+
9
+ ### Improved
10
+
11
+ - **`warn.js` — minimal core dispatcher.** Stripped to just `devState`, `warnMsg`, and `errMsg`. All formatting, messages, and styling logic moved to `devMessages.js` via `globalThis.__SYMBIOTE_DEV_LOG` handler. No messages map or `Array.isArray` in core.
12
+
13
+ - **Colored console output.** `devMessages.js` now renders styled badges in browser consoles: purple `Symbiote` badge, amber/red code badge, violet `<component-name>`, and dimmed doc links. Falls back to plain text in Node.js.
14
+
15
+ - **Doc references in all warnings.** Every message now includes a `→` link to the relevant GitHub docs page.
16
+
17
+ - **`devMessages.js` auto-enables `devMode`.** Importing the module sets `globalThis.__SYMBIOTE_DEV_MODE = true` — one import for the full dev experience.
18
+
3
19
  ## 3.5.0
4
20
 
5
21
  ### Added
6
22
 
7
23
  - **External dev messages module (`core/devMessages.js`).** All warning and error message strings have been extracted from core files into an optional module. Core files now emit short numeric codes (e.g. `[Symbiote W5]`). Import `@symbiotejs/symbiote/core/devMessages.js` once to get full human-readable messages. This reduces the core bundle size by removing all formatting strings from `PubSub.js`, `Symbiote.js`, `tpl-processors.js`, `AppRouter.js`, `html.js`, and both itemize processors.
8
24
 
9
- - **`core/warn.js` — lightweight message dispatcher.** Exports `warnMsg(code, ...args)`, `errMsg(code, ...args)`, and `registerMessages(map)`. All core files now use this dispatcher instead of inline `console.warn`/`console.error` calls.
25
+ - **`core/warn.js` — lightweight message dispatcher.** Exports `warnMsg(code, ...args)` and `errMsg(code, ...args)`. All core files now use this dispatcher instead of inline `console.warn`/`console.error` calls.
10
26
 
11
27
  - **AppRouter: `title` accepts functions.** Route descriptors and `setDefaultTitle()` now accept `() => String` in addition to plain strings. The function is called at navigation time, enabling dynamic or localized page titles:
12
28
  ```js
package/core/Symbiote.js CHANGED
@@ -430,7 +430,7 @@ export class Symbiote extends HTMLElement {
430
430
  if (Symbiote.devMode && this.sharedCtx.has(sharedName)) {
431
431
  let existing = this.sharedCtx.read(sharedName);
432
432
  if (existing !== sharedVal && typeof sharedVal !== 'function') {
433
- warnMsg(7, sharedName);
433
+ warnMsg(7, this.localName, sharedName);
434
434
  }
435
435
  }
436
436
  this.sharedCtx.add(sharedName, sharedVal);
@@ -1,58 +1,102 @@
1
- import { registerMessages } from './warn.js';
2
-
3
1
  globalThis.__SYMBIOTE_DEV_MODE = true;
4
2
 
5
- registerMessages(new Map([
3
+ let isBrowser = !globalThis.process;
4
+
5
+ // CSS styles for browser console
6
+ let S = {
7
+ badge: 'background:#7c3aed;color:#fff;border-radius:3px;padding:1px 4px;font-weight:bold',
8
+ code: 'background:#f59e0b;color:#000;border-radius:3px;padding:1px 4px;font-weight:bold',
9
+ errCode: 'background:#ef4444;color:#fff;border-radius:3px;padding:1px 4px;font-weight:bold',
10
+ tag: 'color:#7c3aed;font-weight:bold',
11
+ dim: 'color:#888',
12
+ reset: 'color:inherit',
13
+ };
14
+
15
+ let DOCS = 'https://github.com/symbiotejs/symbiote.js/blob/main/docs/';
16
+
17
+ /**
18
+ * @param {string} code - e.g. 'W1' or 'E15'
19
+ * @param {string} body - message body
20
+ * @param {string} doc - doc page name
21
+ * @param {string} [tag] - component tag name
22
+ */
23
+ function fmt(code, body, doc, tag) {
24
+ if (!isBrowser) {
25
+ return `[Symbiote ${code}]${tag ? ` <${tag}>:` : ''} ${body}\n→ ${DOCS}${doc}.md`;
26
+ }
27
+ let isErr = code.startsWith('E');
28
+ let parts = `%cSymbiote%c${code}%c`;
29
+ let styles = [S.badge, isErr ? S.errCode : S.code, S.reset];
30
+ if (tag) {
31
+ parts += ` %c<${tag}>%c`;
32
+ styles.push(S.tag, S.reset);
33
+ }
34
+ parts += ` ${body} %c→ ${doc}`;
35
+ styles.push(S.dim);
36
+ return [parts, ...styles];
37
+ }
38
+
39
+ /** @type {Map<number, (...args: any[]) => (string | any[])>} */
40
+ let messages = new Map([
6
41
  // PubSub
7
42
  [1, (uid, action, prop) =>
8
- `[Symbiote] PubSub (${uid}): cannot ${action}. Property: "${prop}"`],
43
+ fmt('W1', `PubSub (${uid}): cannot ${action}. Property: "${prop}"`, 'pubsub')],
9
44
  [2, (uid, prop, prevType, newType, prev, next) =>
10
- `[Symbiote] PubSub (${uid}): type change for "${prop}" [${prevType} → ${newType}].\n`
11
- + `Previous: ${prev}\nNew: ${next}`],
45
+ fmt('W2', `PubSub (${uid}): type change for "${prop}" [${prevType} → ${newType}]. Previous: ${prev} | New: ${next}`, 'pubsub')],
12
46
  [3, (uid) =>
13
- `[Symbiote] PubSub: context "${uid}" is already registered. Returning existing instance.`],
47
+ fmt('W3', `PubSub: context "${uid}" is already registered. Returning existing instance.`, 'context')],
14
48
  [4, (uid, keys) =>
15
- `[Symbiote] PubSub: context "${uid}" not found.\n`
16
- + `Available contexts: [${keys}]`],
49
+ fmt('W4', `PubSub: context "${uid}" not found. Available: [${keys}]`, 'context')],
17
50
 
18
51
  // Symbiote
19
52
  [5, (localName, selector) =>
20
- `[Symbiote] <${localName}>: custom template "${selector}" not found.`],
53
+ fmt('W5', `custom template "${selector}" not found.`, 'templates', localName)],
21
54
  [6, (localName, sharedName) =>
22
- `[Symbiote] "${localName}" uses *${sharedName} without ctx attribute or --ctx CSS variable. `
23
- + 'Set ctx="name" or --ctx to share state.'],
24
- [7, (sharedName) =>
25
- `[Symbiote] Shared prop "${sharedName}" already has value. Keeping existing.`],
55
+ fmt('W6', `uses *${sharedName} without ctx attribute or --ctx CSS variable. Set ctx="name" or --ctx to share state.`, 'context', localName)],
56
+ [7, (localName, sharedName) =>
57
+ fmt('W7', `shared prop "${sharedName}" already has value. Keeping existing.`, 'context', localName)],
26
58
  [8, (tagName, existingClass, newClass) =>
27
- `[Symbiote] <${tagName}> is already registered (class: ${existingClass}).\n`
28
- + `Attempted re-registration with class "${newClass}" — skipped.`],
59
+ fmt('W8', `already registered (class: ${existingClass}). Re-registration with "${newClass}" — skipped.`, 'get-started', tagName)],
29
60
  [9, (localName, propName) =>
30
- `[Symbiote] <${localName}>: CSS data parse error for "${propName}". Check that the CSS custom property is defined.`],
61
+ fmt('W9', `CSS data parse error for "${propName}". Check that the CSS custom property is defined.`, 'css-data', localName)],
31
62
  [10, (localName, propName) =>
32
- `[Symbiote dev] <${localName}>: CSS data binding "${propName}" will not read computed styles during SSR. `
33
- + 'The init value will be used instead.'],
63
+ fmt('W10', `CSS data binding "${propName}" will not read computed styles during SSR. The init value will be used instead.`, 'css-data', localName)],
34
64
 
35
65
  // tpl-processors
36
66
  [11, (localName, valKey, knownKeys) =>
37
- `[Symbiote dev] <${localName}>: binding key "${valKey}" not found in init$ (auto-initialized to null).\n`
38
- + `Known keys: [${knownKeys}]`],
67
+ fmt('W11', `binding key "${valKey}" not found in init$ (auto-initialized to null). Known keys: [${knownKeys}]`, 'properties', localName)],
39
68
  [12, (localName, prop) =>
40
- `[Symbiote dev] <${localName}>: text-node binding "{{${prop}}}" has no hydration attribute. `
41
- + 'In ssrMode/isoMode it will be rendered by the server but won\'t update on the client. '
42
- + 'Use property binding (${{textContent: \'' + prop + '\'}}) for hydratable text.'],
69
+ fmt('W12', `text-node binding "{{${prop}}}" has no hydration attribute. `
70
+ + 'In ssrMode/isoMode it will be rendered by the server but won\'t update on the client. '
71
+ + 'Use property binding (${{textContent: \'' + prop + '\'}}) for hydratable text.', 'ssr', localName)],
43
72
 
44
73
  // AppRouter
45
74
  [13, (msg) =>
46
- `[Symbiote > AppRouter] ${msg}`],
75
+ fmt('W13', msg, 'routing')],
47
76
  [14, () =>
48
- '[Symbiote > AppRouter] History API is not available.'],
77
+ fmt('W14', 'History API is not available.', 'routing')],
49
78
 
50
79
  // html
51
80
  [15, (val) =>
52
- `[Symbiote > html] \`this\` used in template interpolation (value: ${val}).\n`
53
- + 'Templates are context-free — use ${{ prop: \'stateKey\' }} binding syntax instead.'],
81
+ fmt('E15', `\`this\` used in template interpolation (value: ${val}). `
82
+ + 'Templates are context-free — use ${{ prop: \'stateKey\' }} binding syntax instead.', 'templates')],
54
83
 
55
84
  // itemizeProcessor
56
85
  [16, (localName, dataType, data) =>
57
- `[Symbiote] <${localName}>: itemize data must be Array or Object, got ${dataType}: ${data}`],
58
- ]));
86
+ fmt('W16', `itemize data must be Array or Object, got ${dataType}: ${data}`, 'list-rendering', localName)],
87
+ ]);
88
+
89
+ /**
90
+ * @param {'warn' | 'error'} type
91
+ * @param {number} code
92
+ * @param {any[]} args
93
+ */
94
+ globalThis.__SYMBIOTE_DEV_LOG = (type, code, args) => {
95
+ let formatter = messages.get(code);
96
+ if (formatter) {
97
+ let result = formatter(...args);
98
+ Array.isArray(result) ? console[type](...result) : console[type](result);
99
+ } else {
100
+ console[type](`[Symbiote ${type === 'error' ? 'E' : 'W'}${code}]`);
101
+ }
102
+ };
package/core/warn.js CHANGED
@@ -1,6 +1,3 @@
1
- /** @type {Map<number, (...args: any[]) => string>} */
2
- let messages = globalThis.__SYMBIOTE_DEV_MESSAGES || (globalThis.__SYMBIOTE_DEV_MESSAGES = new Map());
3
-
4
1
  /** @type {{ devMode: boolean }} */
5
2
  export let devState = {
6
3
  get devMode() {
@@ -11,13 +8,17 @@ export let devState = {
11
8
  },
12
9
  };
13
10
 
11
+ /** @param {string} type @param {number} code */
12
+ function _log(type, code) {
13
+ console[type](`[Symbiote ${type === 'error' ? 'E' : 'W'}${code}]`);
14
+ }
15
+
14
16
  /**
15
17
  * @param {number} code
16
18
  * @param {...any} args
17
19
  */
18
20
  export function warnMsg(code, ...args) {
19
- let fmt = messages.get(code);
20
- console.warn(fmt ? fmt(...args) : `[Symbiote W${code}]`);
21
+ (globalThis.__SYMBIOTE_DEV_LOG || _log)('warn', code, args);
21
22
  }
22
23
 
23
24
  /**
@@ -25,14 +26,5 @@ export function warnMsg(code, ...args) {
25
26
  * @param {...any} args
26
27
  */
27
28
  export function errMsg(code, ...args) {
28
- let fmt = messages.get(code);
29
- console.error(fmt ? fmt(...args) : `[Symbiote E${code}]`);
29
+ (globalThis.__SYMBIOTE_DEV_LOG || _log)('error', code, args);
30
30
  }
31
-
32
- /** @param {Map<number, (...args: any[]) => string>} map */
33
- export function registerMessages(map) {
34
- for (let [code, fmt] of map) {
35
- messages.set(code, fmt);
36
- }
37
- }
38
-
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@symbiotejs/symbiote",
4
- "version": "3.5.1",
4
+ "version": "3.5.2",
5
5
  "description": "Symbiote.js - zero-dependency close-to-platform frontend library to build super-powered web components",
6
6
  "author": "team@rnd-pro.com",
7
7
  "license": "MIT",
@@ -158,6 +158,7 @@
158
158
  "keywords": [
159
159
  "web components",
160
160
  "SSR",
161
+ "SSG",
161
162
  "server side rendering for web components",
162
163
  "ui library",
163
164
  "data flow",
@@ -182,7 +183,8 @@
182
183
  "https imports",
183
184
  "reactive html attributes",
184
185
  "MIT",
185
- "JSDA"
186
+ "JSDA",
187
+ "jsda-kit"
186
188
  ],
187
189
  "devDependencies": {
188
190
  "@playwright/test": "^1.58.2",
@@ -1,2 +1,13 @@
1
- export {};
1
+ declare function fmt(code: string, body: string, doc: string, tag?: string): string | string[];
2
+ declare let isBrowser: boolean;
3
+ declare namespace S {
4
+ let badge: string;
5
+ let code: string;
6
+ let errCode: string;
7
+ let tag: string;
8
+ let dim: string;
9
+ let reset: string;
10
+ }
11
+ declare let DOCS: string;
12
+ declare let messages: Map<number, (...args: any[]) => (string | any[])>;
2
13
  //# sourceMappingURL=devMessages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"devMessages.d.ts","sourceRoot":"","sources":["../../core/devMessages.js"],"names":[],"mappings":""}
1
+ {"version":3,"file":"devMessages.d.ts","sourceRoot":"","sources":["../../core/devMessages.js"],"names":[],"mappings":"AAsBA,2BALW,MAAM,QACN,MAAM,OACN,MAAM,QACN,MAAM,qBAgBhB;AAlCD,+BAAoC;;;;;;;;;AAYpC,yBAAuE;AAyBvE,sBADW,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAgDzD"}
@@ -1,6 +1,5 @@
1
1
  export function warnMsg(code: number, ...args: any[]): void;
2
2
  export function errMsg(code: number, ...args: any[]): void;
3
- export function registerMessages(map: Map<number, (...args: any[]) => string>): void;
4
3
  export let devState: {
5
4
  devMode: boolean;
6
5
  };
@@ -1 +1 @@
1
- {"version":3,"file":"warn.d.ts","sourceRoot":"","sources":["../../core/warn.js"],"names":[],"mappings":"AAiBA,8BAHW,MAAM,WACF,GAAG,EAAA,QAKjB;AAMD,6BAHW,MAAM,WACF,GAAG,EAAA,QAKjB;AAGD,sCADY,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC,QAKlD;AAhCD,qBADW;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAQ7B"}
1
+ {"version":3,"file":"warn.d.ts","sourceRoot":"","sources":["../../core/warn.js"],"names":[],"mappings":"AAmBA,8BAHW,MAAM,WACF,GAAG,EAAA,QAIjB;AAMD,6BAHW,MAAM,WACF,GAAG,EAAA,QAIjB;AA5BD,qBADW;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAQ7B"}