create-what 0.4.0 → 0.5.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 (2) hide show
  1. package/index.js +123 -392
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -1,94 +1,70 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // create-what: npm create what@latest my-app
4
- // Scaffolds a new What Framework project with sensible defaults.
5
- //
3
+ // create-what
4
+ // Canonical scaffold for What Framework projects.
6
5
  // Usage:
7
- // npm create what@latest my-app # JSX project (default)
8
- // npm create what@latest my-app --no-jsx # vanilla h() project
9
- // npm create what@latest my-app --vanilla # vanilla h() project
6
+ // npx create-what my-app
7
+ // npx create-what my-app --vanilla
10
8
 
11
- import { mkdirSync, writeFileSync, existsSync } from 'fs';
12
- import { join } from 'path';
9
+ import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
10
+ import { join } from 'node:path';
13
11
 
14
- // --- Parse CLI arguments ---
15
12
  const args = process.argv.slice(2);
16
13
  const flags = args.filter(a => a.startsWith('-'));
17
14
  const positional = args.filter(a => !a.startsWith('-'));
18
15
 
19
- const name = positional[0] || 'my-what-app';
20
- const template = positional[1] || 'default'; // default | minimal | full
21
-
22
- // JSX is the default; opt out with --no-jsx or --vanilla
16
+ const projectName = positional[0] || 'my-what-app';
23
17
  const useJSX = !flags.includes('--no-jsx') && !flags.includes('--vanilla');
24
18
 
25
- const cwd = process.cwd();
26
- const root = join(cwd, name);
19
+ const root = join(process.cwd(), projectName);
27
20
 
28
21
  if (existsSync(root)) {
29
- console.error(`\n Error: "${name}" already exists.\n`);
22
+ console.error(`\nError: "${projectName}" already exists.`);
30
23
  process.exit(1);
31
24
  }
32
25
 
33
- const mode = useJSX ? 'JSX' : 'vanilla (h())';
34
- console.log(`\n Creating ${name} with template: ${template} (${mode})\n`);
35
-
36
- // File extension helper
37
- const ext = useJSX ? '.jsx' : '.js';
38
-
39
- // Create directory structure
40
- const dirs = [
41
- 'src/pages',
42
- 'src/components',
43
- 'src/layouts',
44
- 'src/islands',
45
- 'public',
46
- ];
47
- dirs.forEach(d => mkdirSync(join(root, d), { recursive: true }));
48
-
49
- // --- .gitignore ---
50
- writeFileSync(join(root, '.gitignore'), `node_modules
51
- dist
52
- .vite
53
- .DS_Store
54
- *.local
55
- `);
26
+ mkdirSync(join(root, 'src'), { recursive: true });
27
+ mkdirSync(join(root, 'public'), { recursive: true });
28
+
29
+ const ext = useJSX ? 'jsx' : 'js';
30
+ const entry = `src/main.${ext}`;
31
+
32
+ writeFileSync(join(root, '.gitignore'), `node_modules\ndist\n.DS_Store\n`);
56
33
 
57
- // --- package.json ---
58
- const pkgDeps = {
59
- 'what-framework': '^0.3.0',
60
- 'what-framework-cli': '^0.1.0',
61
- };
62
-
63
- const pkgDevDeps = useJSX
64
- ? {
65
- 'what-compiler': '^0.3.0',
66
- '@babel/core': '^7.23.0',
67
- 'vite': '^5.0.0',
68
- }
69
- : {};
70
-
71
- const pkg = {
72
- name,
34
+ writeFileSync(join(root, 'package.json'), JSON.stringify({
35
+ name: projectName,
73
36
  private: true,
74
- version: '0.0.1',
37
+ version: '0.1.0',
75
38
  type: 'module',
76
39
  scripts: {
77
- dev: useJSX ? 'vite' : 'what dev',
78
- build: useJSX ? 'vite build' : 'what build',
79
- preview: useJSX ? 'vite preview' : 'what preview',
80
- generate: 'what generate',
40
+ dev: 'vite',
41
+ build: 'vite build',
42
+ preview: 'vite preview',
81
43
  },
82
- dependencies: pkgDeps,
83
- };
84
-
85
- if (Object.keys(pkgDevDeps).length > 0) {
86
- pkg.devDependencies = pkgDevDeps;
87
- }
44
+ dependencies: {
45
+ 'what-framework': '^0.5.0',
46
+ },
47
+ devDependencies: {
48
+ vite: '^5.4.0',
49
+ ...(useJSX ? { 'what-compiler': '^0.5.0' } : {}),
50
+ },
51
+ }, null, 2) + '\n');
88
52
 
89
- writeFileSync(join(root, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
53
+ writeFileSync(join(root, 'index.html'), `<!doctype html>
54
+ <html lang="en">
55
+ <head>
56
+ <meta charset="UTF-8" />
57
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
58
+ <title>${projectName}</title>
59
+ <link rel="stylesheet" href="/src/styles.css" />
60
+ </head>
61
+ <body>
62
+ <div id="app"></div>
63
+ <script type="module" src="/${entry}"></script>
64
+ </body>
65
+ </html>
66
+ `);
90
67
 
91
- // --- vite.config.js (JSX only) ---
92
68
  if (useJSX) {
93
69
  writeFileSync(join(root, 'vite.config.js'), `import { defineConfig } from 'vite';
94
70
  import what from 'what-compiler/vite';
@@ -97,376 +73,131 @@ export default defineConfig({
97
73
  plugins: [what()],
98
74
  });
99
75
  `);
100
- }
101
-
102
- // --- what.config.js ---
103
- writeFileSync(join(root, 'what.config.js'), `// What Framework Configuration
104
- export default {
105
- // Rendering mode: 'static' | 'server' | 'client' | 'hybrid'
106
- mode: 'hybrid',
107
-
108
- // Pages directory (file-based routing)
109
- pagesDir: 'src/pages',
110
-
111
- // Build output
112
- outDir: 'dist',
113
-
114
- // Islands: components that hydrate independently
115
- islands: true,
116
- };
117
- `);
118
76
 
119
- // --- index.html (root required by Vite) ---
120
- const appScript = useJSX ? '/src/app.jsx' : '/src/app.js';
121
- writeFileSync(join(root, 'index.html'), `<!DOCTYPE html>
122
- <html lang="en">
123
- <head>
124
- <meta charset="UTF-8">
125
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
126
- <title>${name}</title>
127
- <link rel="stylesheet" href="/styles.css">
128
- </head>
129
- <body>
130
- <div id="app"></div>
131
- <script type="module" src="${appScript}"></script>
132
- </body>
133
- </html>
134
- `);
135
-
136
- // ==========================================================================
137
- // JSX templates
138
- // ==========================================================================
139
- if (useJSX) {
140
-
141
- // --- src/app.jsx ---
142
- writeFileSync(join(root, 'src/app.jsx'), `import { mount } from 'what-framework';
143
- import { Router, Link, defineRoutes } from 'what-framework/router';
144
- import { Layout } from './layouts/main.jsx';
145
- import { Home } from './pages/index.jsx';
146
- import { About } from './pages/about.jsx';
147
-
148
- const routes = defineRoutes({
149
- '/': { component: Home, layout: Layout },
150
- '/about': { component: About, layout: Layout },
151
- });
77
+ writeFileSync(join(root, 'src', 'main.jsx'), `import { mount, useSignal } from 'what-framework';
152
78
 
153
79
  function App() {
154
- return <Router routes={routes} fallback={NotFound} />;
155
- }
80
+ const count = useSignal(0);
156
81
 
157
- function NotFound() {
158
82
  return (
159
- <div class="not-found">
160
- <h1>404</h1>
161
- <p>Page not found</p>
162
- <Link href="/">Go home</Link>
163
- </div>
83
+ <main className="app-shell">
84
+ <h1>What Framework</h1>
85
+ <p>Compiler-first JSX, React-familiar authoring.</p>
86
+
87
+ <section className="counter">
88
+ <button onClick={() => count.set(c => c - 1)}>-</button>
89
+ <output>{count()}</output>
90
+ <button onClick={() => count.set(c => c + 1)}>+</button>
91
+ </section>
92
+ </main>
164
93
  );
165
94
  }
166
95
 
167
96
  mount(<App />, '#app');
168
97
  `);
98
+ } else {
99
+ writeFileSync(join(root, 'vite.config.js'), `import { defineConfig } from 'vite';
169
100
 
170
- // --- src/layouts/main.jsx ---
171
- writeFileSync(join(root, 'src/layouts/main.jsx'), `import { Nav } from '../components/nav.jsx';
172
-
173
- export function Layout({ children }) {
174
- return (
175
- <div class="layout">
176
- <Nav />
177
- <main class="content">{children}</main>
178
- <footer class="footer">
179
- <p>Built with What</p>
180
- </footer>
181
- </div>
182
- );
183
- }
184
- `);
185
-
186
- // --- src/components/nav.jsx ---
187
- writeFileSync(join(root, 'src/components/nav.jsx'), `import { Link } from 'what-framework/router';
188
-
189
- export function Nav() {
190
- return (
191
- <nav class="nav">
192
- <a href="/" class="nav-logo">What</a>
193
- <div class="nav-links">
194
- <Link href="/">Home</Link>
195
- <Link href="/about">About</Link>
196
- </div>
197
- </nav>
198
- );
199
- }
200
- `);
201
-
202
- // --- src/pages/index.jsx ---
203
- writeFileSync(join(root, 'src/pages/index.jsx'), `import { useState } from 'what-framework';
204
-
205
- export function Home() {
206
- const [count, setCount] = useState(0);
207
-
208
- return (
209
- <div class="page home">
210
- <h1>Welcome to What</h1>
211
- <p>The closest framework to vanilla JS.</p>
212
- <div class="counter">
213
- <button onClick={() => setCount(c => c - 1)}>-</button>
214
- <span class="count">{count}</span>
215
- <button onClick={() => setCount(c => c + 1)}>+</button>
216
- </div>
217
- </div>
218
- );
219
- }
220
-
221
- export default Home;
101
+ export default defineConfig({});
222
102
  `);
223
103
 
224
- // --- src/pages/about.jsx ---
225
- writeFileSync(join(root, 'src/pages/about.jsx'), `export function About() {
226
- return (
227
- <div class="page about">
228
- <h1>About What</h1>
229
- <p>What is a vanilla JS framework that gives you:</p>
230
- <ul>
231
- <li>Fine-grained reactivity (signals)</li>
232
- <li>Surgical DOM updates (no virtual DOM diffing)</li>
233
- <li>Islands architecture (ship zero JS by default)</li>
234
- <li>File-based routing</li>
235
- <li>SSR + SSG + client rendering</li>
236
- <li>React-familiar hooks API</li>
237
- </ul>
238
- </div>
239
- );
240
- }
241
-
242
- export default About;
243
- `);
244
-
245
- } else {
246
- // ==========================================================================
247
- // Vanilla h() templates
248
- // ==========================================================================
249
-
250
- // --- src/app.js ---
251
- writeFileSync(join(root, 'src/app.js'), `import { h, mount, signal } from 'what-framework';
252
- import { Router, Link, defineRoutes } from 'what-framework/router';
253
- import { Layout } from './layouts/main.js';
254
- import { Home } from './pages/index.js';
255
- import { About } from './pages/about.js';
256
-
257
- const routes = defineRoutes({
258
- '/': { component: Home, layout: Layout },
259
- '/about': { component: About, layout: Layout },
260
- });
104
+ writeFileSync(join(root, 'src', 'main.js'), `import { h, mount, signal } from 'what-framework';
261
105
 
262
106
  function App() {
263
- return h(Router, { routes, fallback: NotFound });
264
- }
265
-
266
- function NotFound() {
267
- return h('div', { class: 'not-found' },
268
- h('h1', null, '404'),
269
- h('p', null, 'Page not found'),
270
- h(Link, { href: '/' }, 'Go home'),
107
+ const count = signal(0);
108
+
109
+ return h('main', { class: 'app-shell' },
110
+ h('h1', null, 'What Framework'),
111
+ h('p', null, 'Runtime h() path (advanced).'),
112
+ h('section', { class: 'counter' },
113
+ h('button', { onClick: () => count.set(c => c - 1) }, '-'),
114
+ h('output', null, () => count()),
115
+ h('button', { onClick: () => count.set(c => c + 1) }, '+'),
116
+ ),
271
117
  );
272
118
  }
273
119
 
274
120
  mount(h(App), '#app');
275
121
  `);
276
-
277
- // --- src/layouts/main.js ---
278
- writeFileSync(join(root, 'src/layouts/main.js'), `import { h } from 'what-framework';
279
- import { Link } from 'what-framework/router';
280
- import { Nav } from '../components/nav.js';
281
-
282
- export function Layout({ children }) {
283
- return h('div', { class: 'layout' },
284
- h(Nav),
285
- h('main', { class: 'content' }, children),
286
- h('footer', { class: 'footer' },
287
- h('p', null, 'Built with What'),
288
- ),
289
- );
290
- }
291
- `);
292
-
293
- // --- src/components/nav.js ---
294
- writeFileSync(join(root, 'src/components/nav.js'), `import { h } from 'what-framework';
295
- import { Link } from 'what-framework/router';
296
-
297
- export function Nav() {
298
- return h('nav', { class: 'nav' },
299
- h('a', { href: '/', class: 'nav-logo' }, 'What'),
300
- h('div', { class: 'nav-links' },
301
- h(Link, { href: '/' }, 'Home'),
302
- h(Link, { href: '/about' }, 'About'),
303
- ),
304
- );
305
122
  }
306
- `);
307
-
308
- // --- src/pages/index.js ---
309
- writeFileSync(join(root, 'src/pages/index.js'), `import { h, useState } from 'what-framework';
310
-
311
- export function Home() {
312
- const [count, setCount] = useState(0);
313
123
 
314
- return h('div', { class: 'page home' },
315
- h('h1', null, 'Welcome to What'),
316
- h('p', null, 'The closest framework to vanilla JS.'),
317
- h('div', { class: 'counter' },
318
- h('button', { onClick: () => setCount(c => c - 1) }, '-'),
319
- h('span', { class: 'count' }, count),
320
- h('button', { onClick: () => setCount(c => c + 1) }, '+'),
321
- ),
322
- );
124
+ writeFileSync(join(root, 'src', 'styles.css'), `:root {
125
+ color-scheme: light;
126
+ font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
323
127
  }
324
128
 
325
- export default Home;
326
- `);
327
-
328
- // --- src/pages/about.js ---
329
- writeFileSync(join(root, 'src/pages/about.js'), `import { h } from 'what-framework';
330
-
331
- export function About() {
332
- return h('div', { class: 'page about' },
333
- h('h1', null, 'About What'),
334
- h('p', null, 'What is a vanilla JS framework that gives you:'),
335
- h('ul', null,
336
- h('li', null, 'Fine-grained reactivity (signals)'),
337
- h('li', null, 'Surgical DOM updates (no virtual DOM diffing)'),
338
- h('li', null, 'Islands architecture (ship zero JS by default)'),
339
- h('li', null, 'File-based routing'),
340
- h('li', null, 'SSR + SSG + client rendering'),
341
- h('li', null, 'React-familiar hooks API'),
342
- ),
343
- );
129
+ * {
130
+ box-sizing: border-box;
344
131
  }
345
132
 
346
- export default About;
347
- `);
348
-
349
- } // end vanilla mode
350
-
351
- // --- public/styles.css ---
352
- writeFileSync(join(root, 'public/styles.css'), `* { margin: 0; padding: 0; box-sizing: border-box; }
353
-
354
133
  body {
355
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
356
- line-height: 1.6;
357
- color: #1a1a2e;
358
- background: #fafafa;
134
+ margin: 0;
135
+ min-height: 100vh;
136
+ display: grid;
137
+ place-items: center;
138
+ background: #f4f6fb;
139
+ color: #0f172a;
359
140
  }
360
141
 
361
- .layout { min-height: 100vh; display: flex; flex-direction: column; }
362
- .content { flex: 1; max-width: 800px; margin: 0 auto; padding: 2rem; width: 100%; }
363
-
364
- .nav {
365
- display: flex; align-items: center; gap: 2rem;
366
- padding: 1rem 2rem; background: #1a1a2e; color: white;
142
+ .app-shell {
143
+ width: min(560px, calc(100vw - 2rem));
144
+ background: #ffffff;
145
+ border: 1px solid #dbe2ee;
146
+ border-radius: 16px;
147
+ padding: 2rem;
148
+ box-shadow: 0 16px 40px rgba(15, 23, 42, 0.06);
367
149
  }
368
- .nav-logo { color: white; text-decoration: none; font-weight: 700; font-size: 1.25rem; }
369
- .nav-links { display: flex; gap: 1rem; }
370
- .nav-links a { color: #a0a0c0; text-decoration: none; transition: color 0.2s; }
371
- .nav-links a:hover { color: white; }
372
-
373
- .footer { padding: 1rem 2rem; text-align: center; color: #666; font-size: 0.875rem; }
374
-
375
- h1 { font-size: 2.5rem; margin-bottom: 1rem; }
376
- p { margin-bottom: 1rem; }
377
150
 
378
151
  .counter {
379
- display: flex; align-items: center; gap: 1rem; margin-top: 1.5rem;
152
+ margin-top: 1rem;
153
+ display: inline-flex;
154
+ align-items: center;
155
+ gap: 0.75rem;
380
156
  }
381
- .counter button {
382
- width: 40px; height: 40px; border-radius: 8px; border: 2px solid #1a1a2e;
383
- background: white; font-size: 1.25rem; cursor: pointer; transition: all 0.15s;
157
+
158
+ button {
159
+ border: 1px solid #9aa7bb;
160
+ background: #ffffff;
161
+ color: #0f172a;
162
+ width: 2.25rem;
163
+ height: 2.25rem;
164
+ border-radius: 10px;
165
+ cursor: pointer;
384
166
  }
385
- .counter button:hover { background: #1a1a2e; color: white; }
386
- .count { font-size: 2rem; font-weight: 700; min-width: 3rem; text-align: center; }
387
167
 
388
- ul { margin-left: 1.5rem; margin-bottom: 1rem; }
389
- li { margin-bottom: 0.25rem; }
168
+ button:hover {
169
+ border-color: #2563eb;
170
+ }
390
171
 
391
- .not-found { text-align: center; padding: 4rem 2rem; }
392
- .not-found h1 { font-size: 6rem; color: #ddd; }
172
+ output {
173
+ min-width: 2ch;
174
+ text-align: center;
175
+ font-weight: 700;
176
+ }
393
177
  `);
394
178
 
395
- // --- README.md ---
396
- const jsxNote = useJSX
397
- ? `This project uses **JSX** syntax, compiled by \`what-compiler\` via the Vite plugin.
398
-
399
- To switch to vanilla \`h()\` calls instead, re-create with:
400
- \`\`\`
401
- npm create what@latest ${name} --vanilla
402
- \`\`\``
403
- : `This project uses the vanilla **h()** API for building components.
404
-
405
- To use JSX syntax instead (recommended), re-create with:
406
- \`\`\`
407
- npm create what@latest ${name}
408
- \`\`\``;
409
-
410
- writeFileSync(join(root, 'README.md'), `# ${name}
411
-
412
- A [What Framework](https://github.com/aspect/what-fw) project.
179
+ writeFileSync(join(root, 'README.md'), `# ${projectName}
413
180
 
414
- ## Authoring Mode
415
-
416
- ${jsxNote}
417
-
418
- ## Getting Started
181
+ ## Run
419
182
 
420
183
  \`\`\`bash
421
- cd ${name}
422
184
  npm install
423
185
  npm run dev
424
186
  \`\`\`
425
187
 
426
- ## Scripts
427
-
428
- | Command | Description |
429
- | --------------- | ------------------------------------ |
430
- | \`npm run dev\` | Start development server |
431
- | \`npm run build\` | Build for production |
432
- | \`npm run preview\`| Preview production build |
433
- | \`npm run generate\`| Static site generation (SSG) |
434
-
435
- ## Project Structure
436
-
437
- \`\`\`
438
- ${name}/
439
- src/
440
- pages/ # File-based routes
441
- components/ # Shared components
442
- layouts/ # Page layouts
443
- islands/ # Hydrated interactive islands
444
- ${useJSX ? ' app.jsx # App entry point (JSX)' : ' app.js # App entry point'}
445
- public/ # Static assets
446
- index.html # HTML shell (Vite entry)
447
- what.config.js # Framework configuration
448
- ${useJSX ? ' vite.config.js # Vite + what-compiler plugin\n' : ''} package.json
449
- \`\`\`
188
+ Open [http://localhost:5173](http://localhost:5173).
450
189
 
451
- ## Learn More
190
+ ## Notes
452
191
 
453
- - [What Framework docs](https://github.com/aspect/what-fw)
454
- - JSX authoring: uses \`what-compiler\` to compile JSX to h() calls through the core reconciler
455
- - Vanilla authoring: use \`h(tag, props, ...children)\` directly -- zero build step needed
192
+ - Canonical package name is \`what-framework\`.
193
+ - JSX path is compiler-first and recommended.
194
+ - Runtime \`h()\` path is available with \`--vanilla\`.
195
+ - Vite is preconfigured under the hood; use \`npm run dev/build/preview\`.
196
+ - Event handlers accept both \`onClick\` and \`onclick\`; docs and templates use \`onClick\`.
456
197
  `);
457
198
 
458
- // --- styles.css (move from public/ to root for Vite) ---
459
-
460
- // --- Done ---
461
- console.log(` Done! Next steps:\n`);
462
- console.log(` cd ${name}`);
463
- console.log(` npm install`);
464
- console.log(` npm run dev\n`);
465
-
466
- if (useJSX) {
467
- console.log(` JSX mode enabled. Components use .jsx extensions.`);
468
- console.log(` The what-compiler Vite plugin compiles JSX to h() calls.`);
469
- console.log(` Open http://localhost:5173 after running dev.\n`);
470
- } else {
471
- console.log(` Vanilla mode. Components use h() calls -- no compiler needed.\n`);
472
- }
199
+ console.log(`\nCreated ${projectName} (${useJSX ? 'jsx' : 'vanilla'} mode).`);
200
+ console.log('Next steps:');
201
+ console.log(` cd ${projectName}`);
202
+ console.log(' npm install');
203
+ console.log(' npm run dev\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-what",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Scaffold a new What Framework project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,6 +21,6 @@
21
21
  "license": "MIT",
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "https://github.com/aspect/what-fw"
24
+ "url": "git+https://github.com/aspect/what-fw.git"
25
25
  }
26
26
  }