create-what 0.11.6 → 0.11.8

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 (2) hide show
  1. package/index.js +123 -11
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -160,24 +160,37 @@ function generatePackageJson(packageName, { reactCompat, cssApproach, template }
160
160
  // Full-stack apps are buildless: server.js does SSR + ISR and serves the
161
161
  // client entry as native ES modules, so there is no Vite/compiler toolchain.
162
162
  // `npm run dev` runs the real app with auto-restart on change.
163
+ // `what-devtools` is the browser bridge: without it the MCP server has nothing
164
+ // to talk to and every live tool reports no browser. In the SPA template the
165
+ // what-devtools-mcp Vite plugin injects it. The full-stack template is
166
+ // buildless and has no plugin to do the injecting, but it still needs the
167
+ // package present: server.js serves it over the import map in dev, the same
168
+ // way it already serves what-framework and what-core. Shipping the template
169
+ // with .mcp.json, .cursor/mcp.json and an MCP-promising CLAUDE.md while
170
+ // omitting the one package that makes any of it work is the worst combination,
171
+ // because the agent is primed to call tools that cannot answer.
163
172
  const devDeps = template === 'fullstack'
164
173
  ? {
174
+ 'what-devtools': whatVersionRange,
165
175
  'what-devtools-mcp': whatVersionRange,
166
176
  eslint: '^9.0.0',
167
177
  'eslint-plugin-what': whatVersionRange,
178
+ typescript: '^5.6.0',
168
179
  }
169
180
  : {
170
181
  vite: '^6.0.0',
171
182
  'what-compiler': whatVersionRange,
183
+ 'what-devtools': whatVersionRange,
172
184
  'what-devtools-mcp': whatVersionRange,
173
185
  '@babel/core': '^7.23.0',
174
186
  eslint: '^9.0.0',
175
187
  'eslint-plugin-what': whatVersionRange,
188
+ typescript: '^5.6.0',
176
189
  };
177
190
 
178
191
  const scripts = template === 'fullstack'
179
- ? { dev: 'node --watch server.js', start: 'node server.js', lint: 'eslint .' }
180
- : { dev: 'vite', build: 'vite build', preview: 'vite preview', lint: 'eslint .' };
192
+ ? { dev: 'node --watch server.js', start: 'node server.js', lint: 'eslint .', typecheck: 'tsc --noEmit' }
193
+ : { dev: 'vite', build: 'vite build', preview: 'vite preview', lint: 'eslint .', typecheck: 'tsc --noEmit' };
181
194
  if (template === 'fullstack') {
182
195
  deps['what-isr'] = whatVersionRange;
183
196
  }
@@ -1179,6 +1192,31 @@ function matchPage(pathname) {
1179
1192
  const vnode = matchPage(location.pathname);
1180
1193
  if (vnode) hydrate(vnode, document.body);
1181
1194
 
1195
+ // Dev-only devtools bridge. This is what makes the \\\`what_*\\\` MCP tools in
1196
+ // CLAUDE.md able to see this running app: without it every live tool reports
1197
+ // "no browser connected". The SPA template gets this from the what-devtools-mcp
1198
+ // Vite plugin; this template is buildless, so it does the same three steps by
1199
+ // hand. server.js sets __WHAT_DEV__ and __WHAT_MCP__ in dev only, and the
1200
+ // imports are dynamic, so a production page never fetches either module.
1201
+ if (globalThis.__WHAT_DEV__) {
1202
+ Promise.all([
1203
+ import('what-core'),
1204
+ import('what-devtools'),
1205
+ ]).then(async ([core, devtools]) => {
1206
+ // installDevTools alone makes the app inspectable in the browser. Connecting
1207
+ // to the MCP bridge is a second, optional step: it only happens once
1208
+ // \\\`npx what-devtools-mcp\\\` has run and written a token, so an app started
1209
+ // without the MCP server makes no connection attempts and logs nothing.
1210
+ devtools.installDevTools(core);
1211
+ if (globalThis.__WHAT_MCP__) {
1212
+ const mcp = await import('what-devtools-mcp/client');
1213
+ mcp.connectDevToolsMCP(globalThis.__WHAT_MCP__);
1214
+ }
1215
+ }).catch((err) => {
1216
+ console.warn('[what] devtools unavailable:', err.message);
1217
+ });
1218
+ }
1219
+
1182
1220
  // Progressive enhancement for server-action forms: submit as JSON to
1183
1221
  // /__what_action with the X-What-Action header (the served-action protocol),
1184
1222
  // then navigate to the result. The CSRF token comes from the double-submit
@@ -1300,7 +1338,7 @@ textarea {
1300
1338
 
1301
1339
  import http from 'node:http';
1302
1340
  import { randomBytes } from 'node:crypto';
1303
- import { existsSync, statSync, createReadStream } from 'node:fs';
1341
+ import { existsSync, statSync, createReadStream, readFileSync } from 'node:fs';
1304
1342
  import { extname, join, resolve } from 'node:path';
1305
1343
  import { fileURLToPath } from 'node:url';
1306
1344
  import { createRequestHandler, renderDocument, toNodeListener } from 'what-framework/server';
@@ -1370,15 +1408,52 @@ const importMap = {
1370
1408
  imports: {
1371
1409
  'what-framework': '/node_modules/what-framework/src/index.js',
1372
1410
  'what-core': '/node_modules/what-core/src/index.js',
1411
+ // Dev only. This template is buildless, so there is no Vite plugin to inject
1412
+ // the devtools bridge the way the SPA template gets it. Mapping the two
1413
+ // specifiers here (and serving them below) is all it takes for the MCP
1414
+ // \`what_*\` tools to see this app: entry-client.js dynamically imports them
1415
+ // behind the __WHAT_DEV__ flag, so production never fetches either module.
1416
+ ...(isProd ? {} : {
1417
+ 'what-devtools': '/node_modules/what-devtools/src/index.js',
1418
+ 'what-devtools-mcp/client': '/node_modules/what-devtools-mcp/src/client.js',
1419
+ }),
1373
1420
  },
1374
1421
  };
1375
1422
 
1423
+ // The MCP bridge writes its token here when \`npx what-devtools-mcp\` starts.
1424
+ // Read per render, not once at boot, so starting the bridge after the server is
1425
+ // already up only costs a page reload.
1426
+ //
1427
+ // The token is passed INLINE rather than via the Vite plugin's same-origin
1428
+ // discovery endpoint, which this server does not implement. That is deliberate:
1429
+ // the client only probes for a bridge when it has a token or a discovery URL, so
1430
+ // an app running without the MCP server makes no requests and logs nothing at
1431
+ // all. Handing it a discovery URL that 404s would put a red line in every
1432
+ // developer's console on every page load.
1433
+ function mcpToken() {
1434
+ try {
1435
+ return JSON.parse(readFileSync(join(ROOT, 'node_modules', '.cache', 'what-devtools-mcp', 'token'), 'utf8')).token || '';
1436
+ } catch {
1437
+ return '';
1438
+ }
1439
+ }
1440
+
1441
+ function devToolsHead() {
1442
+ if (isProd) return '';
1443
+ const token = mcpToken();
1444
+ if (!token) return '<script>globalThis.__WHAT_DEV__=true</script>';
1445
+ return \`<script>globalThis.__WHAT_DEV__=true;globalThis.__WHAT_MCP__=\${JSON.stringify({ port: 9229, token })}</script>\`;
1446
+ }
1447
+
1376
1448
  const documentOptions = {
1377
1449
  clientEntry: '/src/entry-client.js',
1378
- head:
1379
- \`<script type="importmap">\${JSON.stringify(importMap)}</script>\` +
1380
- '<link rel="stylesheet" href="/src/styles.css">' +
1381
- '<link rel="icon" type="image/svg+xml" href="/favicon.svg">',
1450
+ // A getter, so devToolsHead() re-reads the token on every render.
1451
+ get head() {
1452
+ return \`<script type="importmap">\${JSON.stringify(importMap)}</script>\` +
1453
+ devToolsHead() +
1454
+ '<link rel="stylesheet" href="/src/styles.css">' +
1455
+ '<link rel="icon" type="image/svg+xml" href="/favicon.svg">';
1456
+ },
1382
1457
  };
1383
1458
 
1384
1459
  // Render a matched route. Mirrors the framework default renderer, plus:
@@ -1437,6 +1512,9 @@ const SERVED_PREFIXES = [
1437
1512
  '/src/components/', // client-shared UI (create as needed)
1438
1513
  '/node_modules/what-framework/',
1439
1514
  '/node_modules/what-core/',
1515
+ // Dev only: the devtools bridge entry-client imports when __WHAT_DEV__ is set.
1516
+ // Gated on isProd so a production deploy never serves dev tooling.
1517
+ ...(isProd ? [] : ['/node_modules/what-devtools/', '/node_modules/what-devtools-mcp/']),
1440
1518
  ];
1441
1519
 
1442
1520
  function resolveStaticFile(pathname) {
@@ -1518,6 +1596,40 @@ async function main() {
1518
1596
  },
1519
1597
  }, null, 2) + '\n');
1520
1598
 
1599
+ const isFullstack = options.template === 'fullstack';
1600
+
1601
+ // The two templates have different authoring models: the SPA compiles JSX
1602
+ // through Vite + what-compiler, the full-stack template is buildless and has
1603
+ // neither. An agent told to write JSX in the full-stack app writes code that
1604
+ // cannot run.
1605
+ const authoringNotes = isFullstack
1606
+ ? `Components run ONCE. This is the buildless full-stack template: \`server.js\` renders
1607
+ file routes and serves \`src/\` as native ES modules. There is **no compiler and no JSX**:
1608
+ write views with \`h()\` and pass \`() => ...\` for anything reactive.
1609
+
1610
+ \`\`\`js
1611
+ h('p', {}, () => \`Count: \${count()}\`)
1612
+ \`\`\`
1613
+
1614
+ Pages live in \`src/pages/\` and export \`page\` (route config), \`loader\` (server data), and a
1615
+ default component. \`src/db.js\`, \`src/routes.js\` and \`src/actions/\` are server-only: they are
1616
+ importable by Node but never served to the browser.`
1617
+ : `Components run ONCE. This is the SPA template: Vite + \`what-compiler\` compile JSX into
1618
+ fine-grained DOM operations, so write JSX and use \`() => ...\` for reactive text.
1619
+
1620
+ \`\`\`jsx
1621
+ <p>{() => \`Count: \${count()}\`}</p>
1622
+ \`\`\`
1623
+ `;
1624
+
1625
+ const mcpIntro = isFullstack
1626
+ ? `This project includes MCP devtools that connect to the running app in the browser.
1627
+ The SPA template gets the bridge from a Vite plugin; this template is buildless, so
1628
+ \`src/entry-client.js\` imports it directly when \`server.js\` sets \`__WHAT_DEV__\` (dev only).
1629
+ Run \`npm run dev\`, open the app, then use the live \`what_*\` tools below. \`what_lint\`,
1630
+ \`what_scaffold\` and \`what_fix\` also work offline with no browser at all.`
1631
+ : `This project includes MCP devtools that connect to the running app in the browser.`;
1632
+
1521
1633
  // CLAUDE.md — agent instructions for Claude Code (also useful for other AI tools)
1522
1634
  writeFileSync(resolve(root, 'CLAUDE.md'), `# ${packageName}
1523
1635
 
@@ -1537,13 +1649,13 @@ const doubled = computed(() => count() * 2); // derived
1537
1649
  effect(() => console.log(count())); // side effect
1538
1650
  \`\`\`
1539
1651
 
1540
- Components run ONCE. Use \`signal()\` for state, \`() =>\` in JSX for reactive text.
1652
+ ${authoringNotes}
1541
1653
 
1542
1654
  **Signal scope:** \`signal()\` works anywhere — module scope (shared state), component scope (local state). Components run once, so signal declarations execute exactly once.
1543
1655
 
1544
1656
  ## MCP DevTools
1545
1657
 
1546
- This project includes MCP devtools that connect to the running app in the browser.
1658
+ ${mcpIntro}
1547
1659
 
1548
1660
  ### Quick Start (First 5 Minutes)
1549
1661
 
@@ -1577,7 +1689,7 @@ This project includes MCP devtools that connect to the running app in the browse
1577
1689
  | Change a signal live | \`what_set_signal {signalId, value}\` |
1578
1690
  | Validate code before saving | \`what_lint {code}\` |
1579
1691
  | Generate boilerplate | \`what_scaffold {type, name}\` |
1580
- | Diagnose an error code | \`what_fix {errorCode}\` |
1692
+ | Diagnose an error code | \`what_fix {error}\` |
1581
1693
 
1582
1694
  ### Workflows
1583
1695
 
@@ -1792,7 +1904,7 @@ export default [
1792
1904
  esModuleInterop: true,
1793
1905
  resolveJsonModule: true,
1794
1906
  isolatedModules: true,
1795
- types: ['vite/client'],
1907
+ ...(isFullstack ? {} : { types: ['vite/client'] }),
1796
1908
  },
1797
1909
  include: ['src'],
1798
1910
  }, null, 2) + '\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-what",
3
- "version": "0.11.6",
3
+ "version": "0.11.8",
4
4
  "description": "Scaffold a new What Framework project",
5
5
  "type": "module",
6
6
  "bin": {