what-compiler 0.5.1 → 0.5.4

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/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # what-compiler
2
+
3
+ JSX compiler for [What Framework](https://whatfw.com). Transforms JSX into optimized DOM operations via a Babel plugin, with a Vite plugin for seamless integration. Also provides file-based routing via `virtual:what-routes`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install what-compiler --save-dev
9
+ ```
10
+
11
+ ## Vite Plugin
12
+
13
+ The recommended way to use the compiler. Add it to your Vite config:
14
+
15
+ ```js
16
+ // vite.config.js
17
+ import { defineConfig } from 'vite';
18
+ import what from 'what-compiler/vite';
19
+
20
+ export default defineConfig({
21
+ plugins: [what()],
22
+ });
23
+ ```
24
+
25
+ ### Options
26
+
27
+ ```js
28
+ what({
29
+ include: /\.[jt]sx$/, // File extensions to process (default: .jsx/.tsx)
30
+ exclude: /node_modules/, // Files to exclude (default: node_modules)
31
+ sourceMaps: true, // Enable source maps (default: true)
32
+ production: false, // Enable production optimizations (default: auto)
33
+ pages: 'src/pages', // Pages directory for file-based routing
34
+ })
35
+ ```
36
+
37
+ ## Babel Plugin
38
+
39
+ Use the Babel plugin directly for custom build setups:
40
+
41
+ ```js
42
+ // babel.config.js
43
+ export default {
44
+ plugins: [
45
+ ['what-compiler/babel', { production: false }],
46
+ ],
47
+ };
48
+ ```
49
+
50
+ ## File-Based Routing
51
+
52
+ The compiler scans a pages directory and generates a virtual routes module.
53
+
54
+ ```
55
+ src/pages/
56
+ index.jsx -> /
57
+ about.jsx -> /about
58
+ blog/
59
+ index.jsx -> /blog
60
+ [slug].jsx -> /blog/:slug
61
+ [...all].jsx -> /*
62
+ ```
63
+
64
+ Import the generated routes in your app:
65
+
66
+ ```jsx
67
+ import { routes } from 'virtual:what-routes';
68
+ import { FileRouter } from 'what-router';
69
+ import { mount } from 'what-framework';
70
+
71
+ mount(<FileRouter routes={routes} />, '#app');
72
+ ```
73
+
74
+ ## Sub-path Exports
75
+
76
+ | Path | Contents |
77
+ |---|---|
78
+ | `what-compiler` | All exports |
79
+ | `what-compiler/vite` | Vite plugin |
80
+ | `what-compiler/babel` | Babel plugin |
81
+ | `what-compiler/runtime` | Compiler runtime helpers |
82
+ | `what-compiler/file-router` | `scanPages`, `extractPageConfig`, `generateRoutesModule` |
83
+
84
+ ## API
85
+
86
+ | Export | Description |
87
+ |---|---|
88
+ | `vitePlugin(options?)` | Vite plugin for JSX transform and file routing |
89
+ | `what(options?)` | Named alias for the Vite plugin |
90
+ | `babelPlugin` | Babel plugin for JSX transformation |
91
+ | `scanPages(dir)` | Scan a directory for page files |
92
+ | `extractPageConfig(file)` | Extract page configuration from a file |
93
+ | `generateRoutesModule(pagesDir, rootDir)` | Generate a routes module string |
94
+
95
+ ## Links
96
+
97
+ - [Documentation](https://whatfw.com)
98
+ - [GitHub](https://github.com/zvndev/what-fw)
99
+
100
+ ## License
101
+
102
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-compiler",
3
- "version": "0.5.1",
3
+ "version": "0.5.4",
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://whatfw.com"
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
  }