what-compiler 0.5.1 → 0.5.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-compiler",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "JSX compiler for What Framework - transforms JSX to optimized DOM operations",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "license": "MIT",
24
24
  "peerDependencies": {
25
25
  "@babel/core": "^7.0.0",
26
- "what-core": "^0.5.1"
26
+ "what-core": "^0.5.3"
27
27
  },
28
28
  "files": [
29
29
  "src"
@@ -31,5 +31,13 @@
31
31
  "devDependencies": {
32
32
  "@babel/core": "^7.23.0",
33
33
  "@babel/preset-env": "^7.23.0"
34
- }
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/zvndev/what-fw"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/zvndev/what-fw/issues"
41
+ },
42
+ "homepage": "https://whatframework.dev"
35
43
  }
@@ -0,0 +1,357 @@
1
+ /**
2
+ * What Framework — Vite Error Overlay
3
+ *
4
+ * Custom error overlay injected during dev mode. Shows compiler transform errors
5
+ * and runtime signal errors with What Framework branding and helpful context.
6
+ *
7
+ * This is client-side code that Vite injects into the page during development.
8
+ */
9
+
10
+ // CSS for the overlay — scoped to avoid style conflicts
11
+ const OVERLAY_STYLES = `
12
+ :host {
13
+ position: fixed;
14
+ inset: 0;
15
+ z-index: 99999;
16
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
17
+ }
18
+
19
+ .backdrop {
20
+ position: fixed;
21
+ inset: 0;
22
+ background: rgba(0, 0, 0, 0.66);
23
+ }
24
+
25
+ .panel {
26
+ position: fixed;
27
+ inset: 2rem;
28
+ overflow: auto;
29
+ background: #1a1a2e;
30
+ border: 1px solid #2a2a4a;
31
+ border-radius: 12px;
32
+ box-shadow: 0 25px 80px rgba(0, 0, 0, 0.5);
33
+ color: #e0e0e0;
34
+ }
35
+
36
+ .header {
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: space-between;
40
+ padding: 1rem 1.5rem;
41
+ border-bottom: 1px solid #2a2a4a;
42
+ background: #16163a;
43
+ border-radius: 12px 12px 0 0;
44
+ }
45
+
46
+ .header-left {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: 0.75rem;
50
+ }
51
+
52
+ .logo {
53
+ width: 28px;
54
+ height: 28px;
55
+ background: linear-gradient(135deg, #2563eb, #1d4ed8);
56
+ border-radius: 6px;
57
+ display: grid;
58
+ place-items: center;
59
+ font-weight: 800;
60
+ font-size: 14px;
61
+ color: #fff;
62
+ }
63
+
64
+ .brand {
65
+ font-size: 14px;
66
+ font-weight: 600;
67
+ color: #a0a0c0;
68
+ }
69
+
70
+ .tag {
71
+ font-size: 11px;
72
+ padding: 2px 8px;
73
+ border-radius: 4px;
74
+ font-weight: 600;
75
+ }
76
+
77
+ .tag-error {
78
+ background: #3b1219;
79
+ color: #f87171;
80
+ }
81
+
82
+ .tag-warning {
83
+ background: #3b2f19;
84
+ color: #fbbf24;
85
+ }
86
+
87
+ .close-btn {
88
+ background: none;
89
+ border: 1px solid #3a3a5a;
90
+ color: #a0a0c0;
91
+ border-radius: 6px;
92
+ padding: 4px 12px;
93
+ cursor: pointer;
94
+ font-family: inherit;
95
+ font-size: 12px;
96
+ }
97
+
98
+ .close-btn:hover {
99
+ background: #2a2a4a;
100
+ color: #fff;
101
+ }
102
+
103
+ .body {
104
+ padding: 1.5rem;
105
+ }
106
+
107
+ .error-title {
108
+ font-size: 16px;
109
+ font-weight: 700;
110
+ color: #f87171;
111
+ margin: 0 0 0.5rem;
112
+ }
113
+
114
+ .error-message {
115
+ font-size: 14px;
116
+ color: #e0e0e0;
117
+ margin: 0 0 1rem;
118
+ line-height: 1.6;
119
+ white-space: pre-wrap;
120
+ }
121
+
122
+ .file-path {
123
+ display: inline-flex;
124
+ align-items: center;
125
+ gap: 0.5rem;
126
+ font-size: 12px;
127
+ color: #818cf8;
128
+ margin-bottom: 1rem;
129
+ padding: 0.25rem 0;
130
+ }
131
+
132
+ .code-frame {
133
+ background: #0d0d1a;
134
+ border: 1px solid #2a2a4a;
135
+ border-radius: 8px;
136
+ overflow-x: auto;
137
+ margin-bottom: 1rem;
138
+ }
139
+
140
+ .code-line {
141
+ display: flex;
142
+ padding: 0 1rem;
143
+ font-size: 13px;
144
+ line-height: 1.7;
145
+ }
146
+
147
+ .code-line.highlight {
148
+ background: rgba(248, 113, 113, 0.1);
149
+ }
150
+
151
+ .line-number {
152
+ color: #4a4a6a;
153
+ min-width: 3ch;
154
+ text-align: right;
155
+ margin-right: 1rem;
156
+ user-select: none;
157
+ }
158
+
159
+ .line-content {
160
+ white-space: pre;
161
+ }
162
+
163
+ .tip {
164
+ margin-top: 1rem;
165
+ padding: 0.75rem 1rem;
166
+ background: #1a2744;
167
+ border: 1px solid #1e3a5f;
168
+ border-radius: 8px;
169
+ font-size: 13px;
170
+ color: #93c5fd;
171
+ line-height: 1.5;
172
+ }
173
+
174
+ .tip-label {
175
+ font-weight: 700;
176
+ color: #60a5fa;
177
+ }
178
+
179
+ .stack {
180
+ margin-top: 1rem;
181
+ font-size: 12px;
182
+ color: #6a6a8a;
183
+ white-space: pre-wrap;
184
+ line-height: 1.5;
185
+ }
186
+ `;
187
+
188
+ /**
189
+ * Build the overlay HTML for an error
190
+ */
191
+ function buildOverlayHTML(err) {
192
+ const isCompilerError = err._isCompilerError || err.plugin === 'vite-plugin-what';
193
+ const type = isCompilerError ? 'Compiler Error' : 'Runtime Error';
194
+ const tagClass = isCompilerError ? 'tag-error' : 'tag-warning';
195
+
196
+ let codeFrame = '';
197
+ if (err.frame || err._frame) {
198
+ const frame = err.frame || err._frame;
199
+ const lines = frame.split('\n');
200
+ codeFrame = `<div class="code-frame">${
201
+ lines.map(line => {
202
+ const isHighlight = line.trimStart().startsWith('>');
203
+ const cleaned = line.replace(/^\s*>\s?/, ' ').replace(/^\s{2}/, '');
204
+ const match = cleaned.match(/^(\s*\d+)\s*\|(.*)$/);
205
+ if (match) {
206
+ return `<div class="code-line${isHighlight ? ' highlight' : ''}"><span class="line-number">${match[1].trim()}</span><span class="line-content">${escapeHTML(match[2])}</span></div>`;
207
+ }
208
+ // Caret line (^^^)
209
+ if (cleaned.trim().startsWith('|')) {
210
+ return `<div class="code-line highlight"><span class="line-number"></span><span class="line-content" style="color:#f87171">${escapeHTML(cleaned.replace(/^\s*\|/, ''))}</span></div>`;
211
+ }
212
+ return '';
213
+ }).join('')
214
+ }</div>`;
215
+ }
216
+
217
+ const filePath = err.id || err.loc?.file || '';
218
+ const line = err.loc?.line ?? '';
219
+ const col = err.loc?.column ?? '';
220
+ const location = filePath
221
+ ? `<div class="file-path">${escapeHTML(filePath)}${line ? `:${line}` : ''}${col ? `:${col}` : ''}</div>`
222
+ : '';
223
+
224
+ const tip = getTip(err);
225
+ const tipHTML = tip ? `<div class="tip"><span class="tip-label">Tip: </span>${escapeHTML(tip)}</div>` : '';
226
+
227
+ const stack = err.stack && !isCompilerError
228
+ ? `<div class="stack">${escapeHTML(cleanStack(err.stack))}</div>`
229
+ : '';
230
+
231
+ return `
232
+ <div class="backdrop"></div>
233
+ <div class="panel">
234
+ <div class="header">
235
+ <div class="header-left">
236
+ <div class="logo">W</div>
237
+ <span class="brand">What Framework</span>
238
+ <span class="tag ${tagClass}">${type}</span>
239
+ </div>
240
+ <button class="close-btn">Dismiss (Esc)</button>
241
+ </div>
242
+ <div class="body">
243
+ <h2 class="error-title">${escapeHTML(err.name || 'Error')}</h2>
244
+ ${location}
245
+ <pre class="error-message">${escapeHTML(err.message || String(err))}</pre>
246
+ ${codeFrame}
247
+ ${tipHTML}
248
+ ${stack}
249
+ </div>
250
+ </div>
251
+ `;
252
+ }
253
+
254
+ /**
255
+ * Context-aware tips for common What Framework errors
256
+ */
257
+ function getTip(err) {
258
+ const msg = (err.message || '').toLowerCase();
259
+
260
+ if (msg.includes('infinite') && msg.includes('effect')) {
261
+ return 'An effect is writing to a signal it also reads. Use untrack() to read without subscribing, or move the write to a different effect.';
262
+ }
263
+ if (msg.includes('jsx') && msg.includes('unexpected')) {
264
+ return 'Make sure your vite.config includes the What compiler plugin: import what from "what-compiler/vite"';
265
+ }
266
+ if (msg.includes('not a function') && msg.includes('signal')) {
267
+ return 'Signals are functions: call sig() to read, sig(value) to write. Check you\'re not destructuring a signal.';
268
+ }
269
+ if (msg.includes('hydrat')) {
270
+ return 'Hydration mismatches happen when SSR output differs from client render. Ensure server and client see the same initial state.';
271
+ }
272
+ return '';
273
+ }
274
+
275
+ function escapeHTML(str) {
276
+ return str
277
+ .replace(/&/g, '&amp;')
278
+ .replace(/</g, '&lt;')
279
+ .replace(/>/g, '&gt;')
280
+ .replace(/"/g, '&quot;');
281
+ }
282
+
283
+ function cleanStack(stack) {
284
+ return stack
285
+ .split('\n')
286
+ .filter(line => !line.includes('node_modules'))
287
+ .slice(0, 10)
288
+ .join('\n');
289
+ }
290
+
291
+ /**
292
+ * Client-side overlay component — injected as a custom element
293
+ * to avoid style conflicts with the user's application.
294
+ */
295
+ const OVERLAY_ELEMENT = `
296
+ class WhatErrorOverlay extends HTMLElement {
297
+ constructor(err) {
298
+ super();
299
+ this.root = this.attachShadow({ mode: 'open' });
300
+ this.root.innerHTML = \`<style>${OVERLAY_STYLES}</style>\`;
301
+ this.show(err);
302
+ }
303
+
304
+ show(err) {
305
+ const template = document.createElement('template');
306
+ template.innerHTML = (${buildOverlayHTML.toString()})(err);
307
+ this.root.appendChild(template.content.cloneNode(true));
308
+
309
+ // Close handlers
310
+ this.root.querySelector('.close-btn')?.addEventListener('click', () => this.close());
311
+ this.root.querySelector('.backdrop')?.addEventListener('click', () => this.close());
312
+ document.addEventListener('keydown', this._onKey = (e) => {
313
+ if (e.key === 'Escape') this.close();
314
+ });
315
+ }
316
+
317
+ close() {
318
+ document.removeEventListener('keydown', this._onKey);
319
+ this.remove();
320
+ }
321
+ }
322
+
323
+ // Helper functions bundled into the overlay element
324
+ ${escapeHTML.toString()}
325
+ ${cleanStack.toString()}
326
+ ${getTip.toString()}
327
+
328
+ if (!customElements.get('what-error-overlay')) {
329
+ customElements.define('what-error-overlay', WhatErrorOverlay);
330
+ }
331
+ `;
332
+
333
+ /**
334
+ * Generate the client-side error overlay injection script.
335
+ * Called by the Vite plugin to inject into the dev server.
336
+ */
337
+ export function getErrorOverlayCode() {
338
+ return OVERLAY_ELEMENT;
339
+ }
340
+
341
+ /**
342
+ * Create the error overlay middleware for Vite's dev server.
343
+ * Intercepts Vite's error events and shows a custom What-branded overlay.
344
+ */
345
+ export function setupErrorOverlay(server) {
346
+ // Listen for Vite errors and enrich with What Framework context
347
+ const origSend = server.ws.send.bind(server.ws);
348
+ server.ws.send = function (payload) {
349
+ if (payload?.type === 'error') {
350
+ // Tag compiler errors
351
+ if (payload.err?.plugin === 'vite-plugin-what') {
352
+ payload.err._isCompilerError = true;
353
+ }
354
+ }
355
+ return origSend(payload);
356
+ };
357
+ }
@@ -10,6 +10,7 @@ import path from 'path';
10
10
  import { transformSync } from '@babel/core';
11
11
  import whatBabelPlugin from './babel-plugin.js';
12
12
  import { generateRoutesModule, scanPages } from './file-router.js';
13
+ import { setupErrorOverlay } from './error-overlay.js';
13
14
 
14
15
  const VIRTUAL_ROUTES_ID = 'virtual:what-routes';
15
16
  const RESOLVED_VIRTUAL_ID = '\0' + VIRTUAL_ROUTES_ID;
@@ -43,6 +44,9 @@ export default function whatVitePlugin(options = {}) {
43
44
  configureServer(devServer) {
44
45
  server = devServer;
45
46
 
47
+ // Set up What-branded error overlay
48
+ setupErrorOverlay(devServer);
49
+
46
50
  // Watch the pages directory for file additions/removals
47
51
  devServer.watcher.on('add', (file) => {
48
52
  if (file.startsWith(pagesDir)) {
@@ -107,6 +111,12 @@ export default function whatVitePlugin(options = {}) {
107
111
  map: result.map
108
112
  };
109
113
  } catch (error) {
114
+ // Enrich Babel errors with file context for the error overlay
115
+ error.plugin = 'vite-plugin-what';
116
+ if (!error.id) error.id = id;
117
+ if (error.loc === undefined && error._loc) {
118
+ error.loc = { file: id, line: error._loc.line, column: error._loc.column };
119
+ }
110
120
  console.error(`[what] Error transforming ${id}:`, error.message);
111
121
  throw error;
112
122
  }