@taujs/create-taujs 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ τjs [ taujs ] Orchestration System
4
+ Author: John Smith
5
+ Copyright (c) Aoede Ltd 2024-present
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @taujs/create-taujs
2
+
3
+ Scaffold a new [τjs (taujs)](https://taujs.dev) application
package/dist/index.js ADDED
@@ -0,0 +1,893 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { execSync } from "child_process";
5
+ import fs from "fs-extra";
6
+ import path from "path";
7
+ import pc from "picocolors";
8
+ import prompts from "prompts";
9
+ var PACKAGE_MANAGERS = {
10
+ npm: "npm install",
11
+ pnpm: "pnpm install",
12
+ yarn: "yarn",
13
+ bun: "bun install"
14
+ };
15
+ function parseArgs() {
16
+ const rawArgs = process.argv.slice(2);
17
+ let projectName;
18
+ for (const arg of rawArgs) {
19
+ if (!arg.startsWith("-") && !projectName) {
20
+ projectName = arg;
21
+ continue;
22
+ }
23
+ }
24
+ return { projectName };
25
+ }
26
+ async function main() {
27
+ console.log(pc.cyan("\nWelcome to \u03C4js (taujs)\n"));
28
+ const { projectName: argName } = parseArgs();
29
+ const questions = [
30
+ {
31
+ type: argName ? null : "text",
32
+ name: "projectName",
33
+ message: "Project name:",
34
+ initial: "my-taujs-app",
35
+ validate: (value) => {
36
+ if (!value) return "Project name is required";
37
+ if (!/^[a-z0-9-_]+$/.test(value)) {
38
+ return "Project name can only contain lowercase letters, numbers, hyphens, and underscores";
39
+ }
40
+ return true;
41
+ }
42
+ },
43
+ {
44
+ type: "select",
45
+ name: "packageManager",
46
+ message: "Package manager:",
47
+ choices: [
48
+ { title: "npm", value: "npm" },
49
+ { title: "pnpm", value: "pnpm" },
50
+ { title: "yarn", value: "yarn" },
51
+ { title: "bun", value: "bun" }
52
+ ],
53
+ initial: 0
54
+ },
55
+ {
56
+ type: "confirm",
57
+ name: "installDeps",
58
+ message: "Install dependencies now?",
59
+ initial: true
60
+ }
61
+ ];
62
+ const answers = await prompts(questions, {
63
+ onCancel: () => {
64
+ console.log(pc.red("\n\u2716 Operation cancelled"));
65
+ process.exit(1);
66
+ }
67
+ });
68
+ const config = {
69
+ projectName: argName || answers.projectName,
70
+ packageManager: answers.packageManager,
71
+ installDeps: answers.installDeps
72
+ };
73
+ await createProject(config);
74
+ }
75
+ async function createProject(config) {
76
+ const { projectName, packageManager, installDeps } = config;
77
+ const targetDir = path.resolve(process.cwd(), projectName);
78
+ if (fs.existsSync(targetDir)) {
79
+ console.log(pc.red(`
80
+ \u2716 Directory ${projectName} already exists`));
81
+ process.exit(1);
82
+ }
83
+ console.log(pc.cyan(`
84
+ Creating project in ${pc.bold(targetDir)}...
85
+ `));
86
+ await fs.ensureDir(targetDir);
87
+ await createDirectoryStructure(targetDir);
88
+ await generateFiles(targetDir, config);
89
+ console.log(pc.green("Project files created"));
90
+ if (installDeps) {
91
+ console.log(
92
+ pc.cyan(`
93
+ Installing dependencies with ${packageManager}...
94
+ `)
95
+ );
96
+ try {
97
+ execSync(PACKAGE_MANAGERS[packageManager], {
98
+ cwd: targetDir,
99
+ stdio: "inherit"
100
+ });
101
+ console.log(pc.green("\nDependencies installed"));
102
+ } catch (error) {
103
+ console.log(
104
+ pc.yellow(
105
+ "\n\u26A0 Failed to install dependencies. You can install them manually."
106
+ )
107
+ );
108
+ }
109
+ }
110
+ console.log(
111
+ pc.green(`
112
+ \u2713 Project ${pc.bold(projectName)} created successfully!
113
+ `)
114
+ );
115
+ console.log(pc.cyan("Next steps:\n"));
116
+ console.log(` cd ${projectName}`);
117
+ if (!installDeps) {
118
+ console.log(` ${PACKAGE_MANAGERS[packageManager]}`);
119
+ }
120
+ console.log(` ${packageManager} run dev
121
+ `);
122
+ console.log(pc.dim("Documentation: https://taujs.dev\n"));
123
+ }
124
+ async function createDirectoryStructure(targetDir) {
125
+ const dirs = [
126
+ "src/server/services",
127
+ "src/client",
128
+ "src/client/public"
129
+ ];
130
+ for (const dir of dirs) {
131
+ await fs.ensureDir(path.join(targetDir, dir));
132
+ }
133
+ }
134
+ async function generateFiles(targetDir, config) {
135
+ const { projectName, packageManager } = config;
136
+ await fs.writeJSON(
137
+ path.join(targetDir, "package.json"),
138
+ generatePackageJson(projectName, packageManager),
139
+ { spaces: 2 }
140
+ );
141
+ await fs.writeFile(path.join(targetDir, "build.ts"), generateBuildTs());
142
+ await fs.writeJSON(
143
+ path.join(targetDir, "tsconfig.json"),
144
+ generateTsConfig(),
145
+ { spaces: 2 }
146
+ );
147
+ await fs.writeJSON(
148
+ path.join(targetDir, "src/server/tsconfig.json"),
149
+ generateServerTsConfig(),
150
+ { spaces: 2 }
151
+ );
152
+ await fs.writeFile(
153
+ path.join(targetDir, "taujs.config.ts"),
154
+ generateTaujsConfig()
155
+ );
156
+ await fs.writeFile(path.join(targetDir, ".gitignore"), generateGitignore());
157
+ await fs.writeFile(
158
+ path.join(targetDir, "README.md"),
159
+ generateReadme(projectName, packageManager)
160
+ );
161
+ await fs.writeFile(
162
+ path.join(targetDir, "src/client/index.html"),
163
+ generateIndexHtml("\u03C4js - Composing systems, not just apps")
164
+ );
165
+ await fs.writeFile(
166
+ path.join(targetDir, "src/client/App.tsx"),
167
+ generateAppComponent()
168
+ );
169
+ await fs.writeFile(
170
+ path.join(targetDir, "src/client/entry-client.tsx"),
171
+ generateEntryClient()
172
+ );
173
+ await fs.writeFile(
174
+ path.join(targetDir, "src/client/entry-server.tsx"),
175
+ generateEntryServer()
176
+ );
177
+ await fs.writeFile(
178
+ path.join(targetDir, "src/client/styles.css"),
179
+ generateStyles()
180
+ );
181
+ await fs.writeFile(
182
+ path.join(targetDir, "src/client/vite-env.d.ts"),
183
+ generateViteEnv()
184
+ );
185
+ await fs.writeFile(
186
+ path.join(targetDir, "src/server/index.ts"),
187
+ generateServerIndex()
188
+ );
189
+ await fs.writeFile(
190
+ path.join(targetDir, "src/server/services/registry.ts"),
191
+ generateServiceRegistry()
192
+ );
193
+ await fs.writeFile(
194
+ path.join(targetDir, "src/server/services/example.service.ts"),
195
+ generateExampleService()
196
+ );
197
+ await fs.writeFile(
198
+ path.join(targetDir, "src/server/types.d.ts"),
199
+ generateServiceTypesAugmentation()
200
+ );
201
+ await fs.writeFile(
202
+ path.join(targetDir, "src/client/public/favicon.svg"),
203
+ generateFavicon()
204
+ );
205
+ }
206
+ function generatePackageJson(projectName, packageManager) {
207
+ return {
208
+ name: projectName,
209
+ version: "0.1.0",
210
+ private: true,
211
+ type: "module",
212
+ scripts: {
213
+ dev: "cross-env NODE_ENV=development tsx watch --ignore vite.config.ts --trace-warnings --tsconfig ./src/server/tsconfig.json ./src/server/index.ts --loglevel verbose",
214
+ "build:client": "tsx build.ts",
215
+ "build:entry-server": "BUILD_MODE=ssr tsx build.ts",
216
+ "build:server": "esbuild src/server/index.ts --bundle --platform=node --format=esm --outfile=dist/server/index.js --external:fastify --external:@taujs/server --external:@taujs/react",
217
+ build: "npm run build:client && npm run build:entry-server && npm run build:server",
218
+ start: "cross-env NODE_ENV=production node dist/server/index.js",
219
+ lint: "tsc --noEmit"
220
+ },
221
+ dependencies: {
222
+ "@taujs/react": "latest",
223
+ "@taujs/server": "latest",
224
+ fastify: "^5.6.1",
225
+ react: "^19.0.0",
226
+ "react-dom": "^19.0.0"
227
+ },
228
+ devDependencies: {
229
+ "@types/node": "^22.10.5",
230
+ "@types/react": "^19.0.2",
231
+ "@types/react-dom": "^19.0.2",
232
+ "@vitejs/plugin-react": "^4.6.0",
233
+ tsx: "^4.19.3",
234
+ typescript: "^5.7.3",
235
+ vite: "^7.1.11",
236
+ "cross-env": "^7.0.3"
237
+ }
238
+ };
239
+ }
240
+ function generateBuildTs() {
241
+ return `import path from "node:path";
242
+ import { fileURLToPath } from "node:url";
243
+
244
+ import { taujsBuild } from "@taujs/server";
245
+
246
+ import config from "./taujs.config.ts";
247
+
248
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
249
+
250
+ await taujsBuild({
251
+ clientBaseDir: path.resolve(__dirname, "src/client"),
252
+ config,
253
+ projectRoot: __dirname,
254
+ mode: process.env.BUILD_MODE === 'ssr' ? 'ssr' : 'client',
255
+ });
256
+ `;
257
+ }
258
+ function generateTsConfig() {
259
+ return {
260
+ compilerOptions: {
261
+ target: "ES2022",
262
+ module: "ESNext",
263
+ lib: ["ES2022", "DOM", "DOM.Iterable"],
264
+ jsx: "react-jsx",
265
+ moduleResolution: "bundler",
266
+ resolveJsonModule: true,
267
+ allowImportingTsExtensions: true,
268
+ noEmit: true,
269
+ isolatedModules: true,
270
+ esModuleInterop: true,
271
+ forceConsistentCasingInFileNames: true,
272
+ strict: true,
273
+ skipLibCheck: true,
274
+ types: [],
275
+ paths: {
276
+ "@client/*": ["./src/client/*"],
277
+ "@server/*": ["./src/server/*"]
278
+ }
279
+ },
280
+ include: ["src/client/**/*", "src/server/**/*", "taujs.config.ts"]
281
+ };
282
+ }
283
+ function generateServerTsConfig() {
284
+ return {
285
+ extends: "../../tsconfig.json",
286
+ include: ["./**/*"]
287
+ };
288
+ }
289
+ function generateTaujsConfig() {
290
+ return `import { defineConfig } from '@taujs/server/config';
291
+
292
+ export default defineConfig({
293
+ server: {
294
+ port: 5173,
295
+ host: 'localhost',
296
+ hmrPort: 5174,
297
+ },
298
+ apps: [
299
+ {
300
+ appId: 'main',
301
+ entryPoint: '',
302
+ routes: [
303
+ {
304
+ path: '/',
305
+ attr: {
306
+ render: 'ssr',
307
+ hydrate: true,
308
+ // Direct service invocation: standard SSR
309
+ data: async (params, ctx) => {
310
+ return ctx.call('example', 'greet', { name: 'SSR' });
311
+ },
312
+ },
313
+ },
314
+ {
315
+ path: '/streaming',
316
+ attr: {
317
+ render: 'streaming',
318
+ hydrate: true,
319
+ // Descriptor-based data: resolved by the server
320
+ data: async (params) => ({
321
+ args: { name: 'Streaming' },
322
+ serviceName: 'example',
323
+ serviceMethod: 'greet',
324
+ }),
325
+ // meta recommended for streaming routes for SEO/social and render timing
326
+ meta: {
327
+ title: '\u03C4js [taujs] - streaming',
328
+ description: 'Streaming page description from route meta',
329
+ },
330
+ },
331
+ },
332
+ ],
333
+ },
334
+ ],
335
+ });
336
+ `;
337
+ }
338
+ function generateGitignore() {
339
+ return `# Dependencies
340
+ node_modules
341
+ .pnp
342
+ .pnp.js
343
+
344
+ # Production
345
+ dist
346
+ build
347
+
348
+ # Environment
349
+ .env
350
+ .env.local
351
+ .env.*.local
352
+
353
+ # Logs
354
+ logs
355
+ *.log
356
+ npm-debug.log*
357
+ yarn-debug.log*
358
+ yarn-error.log*
359
+ pnpm-debug.log*
360
+
361
+ # Editor
362
+ .vscode
363
+ .idea
364
+ *.swp
365
+ *.swo
366
+ *~
367
+
368
+ # OS
369
+ .DS_Store
370
+ Thumbs.db
371
+
372
+ # Testing
373
+ coverage
374
+
375
+ # Misc
376
+ .cache
377
+ `;
378
+ }
379
+ function generateReadme(projectName, packageManager) {
380
+ const pmRun = packageManager === "npm" ? "npm run" : packageManager;
381
+ return `# ${projectName}
382
+
383
+ A \u03C4js (taujs) application with server-side rendering, streaming, and a type-safe service layer.
384
+
385
+ ## Getting Started
386
+
387
+ ### Development
388
+
389
+ \`\`\`bash
390
+ ${pmRun} dev
391
+ \`\`\`
392
+
393
+ Visit [http://localhost:5173](http://localhost:5173)
394
+
395
+ ### Build for Production
396
+
397
+ \`\`\`bash
398
+ ${pmRun} build
399
+ \`\`\`
400
+
401
+ ### Start Production Server
402
+
403
+ \`\`\`bash
404
+ ${pmRun} start
405
+ \`\`\`
406
+
407
+ ## Project Structure
408
+
409
+ \`\`\`
410
+ ${projectName}/
411
+ \u251C\u2500\u2500 src/
412
+ \u2502 \u251C\u2500\u2500 client/
413
+ \u2502 \u2502 \u251C\u2500\u2500 App.tsx # Root component
414
+ \u2502 \u2502 \u251C\u2500\u2500 entry-client.tsx # Client hydration entry
415
+ \u2502 \u2502 \u251C\u2500\u2500 entry-server.tsx # SSR render entry
416
+ \u2502 \u2502 \u251C\u2500\u2500 styles.css # Global styles
417
+ \u2502 \u2502 \u251C\u2500\u2500 vite-env.d.ts # Vite client types
418
+ \u2502 \u2502 \u2514\u2500\u2500 public/
419
+ \u2502 \u2502 \u2514\u2500\u2500 favicon.svg # App icon
420
+ \u2502 \u2514\u2500\u2500 server/
421
+ \u2502 \u251C\u2500\u2500 index.ts # Server entry point
422
+ \u2502 \u251C\u2500\u2500 tsconfig.json # Server-only TS config (used by tsx watch)
423
+ \u2502 \u251C\u2500\u2500 types.d.ts # ServiceContext augmentation
424
+ \u2502 \u2514\u2500\u2500 services/
425
+ \u2502 \u251C\u2500\u2500 registry.ts # Service registry
426
+ \u2502 \u2514\u2500\u2500 example.service.ts # Example service
427
+ \u251C\u2500\u2500 build.ts # Production build entry point
428
+ \u251C\u2500\u2500 taujs.config.ts # \u03C4js configuration
429
+ \u2514\u2500\u2500 package.json
430
+ \`\`\`
431
+
432
+ ## Editing the App
433
+
434
+ - Main UI: \`src/client/App.tsx\`
435
+ - Styles: \`src/client/styles.css\`
436
+ - SSR entry: \`src/client/entry-server.tsx\`
437
+ - Client entry: \`src/client/entry-client.tsx\`
438
+ - Routes: \`taujs.config.ts\`
439
+ - Services: \`src/server/services/\`
440
+
441
+ ## Documentation
442
+
443
+ - [\u03C4js Documentation](https://taujs.dev)
444
+ - [Fastify Documentation](https://fastify.dev)
445
+ - [React Documentation](https://react.dev)
446
+
447
+ ## License
448
+
449
+ MIT
450
+ `;
451
+ }
452
+ function generateIndexHtml(title) {
453
+ return `<!DOCTYPE html>
454
+ <html lang="en">
455
+ <head>
456
+ <meta charset="UTF-8" />
457
+ <meta
458
+ name="viewport"
459
+ content="width=device-width, initial-scale=1.0"
460
+ />
461
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
462
+ <link rel="stylesheet" href="/styles.css" />
463
+ <title>${title}</title>
464
+ </head>
465
+ <body>
466
+ <main id="root"></main>
467
+ </body>
468
+ </html>
469
+ `;
470
+ }
471
+ function generateAppComponent() {
472
+ return `import { Suspense } from 'react';
473
+ import { useSSRStore } from '@taujs/react';
474
+
475
+ type GreetingData = {
476
+ message: string;
477
+ timestamp: string;
478
+ };
479
+
480
+ function GreetingCard() {
481
+ const data = useSSRStore<GreetingData>();
482
+
483
+ return (
484
+ <section className="card card--primary">
485
+ <p className="card-message">{data.message}</p>
486
+ <p className="card-meta">
487
+ Generated at: {new Date(data.timestamp).toLocaleString()}
488
+ </p>
489
+ </section>
490
+ );
491
+ }
492
+
493
+ export function App() {
494
+ return (
495
+ <div className="app">
496
+ <header className="app-header">
497
+ <h1 className="app-title">\u03C4js - Composing systems, not just apps</h1>
498
+ <p className="app-subtitle">
499
+ Server-first application composition with explicit per-route rendering control.
500
+ </p>
501
+ </header>
502
+
503
+ <Suspense
504
+ fallback={
505
+ <section className="card card--primary">
506
+ <p className="card-message">Loading greeting\u2026</p>
507
+ <p className="card-meta">Streaming data from the server.</p>
508
+ </section>
509
+ }
510
+ >
511
+ <GreetingCard />
512
+ </Suspense>
513
+
514
+ <section className="section">
515
+ <h2 className="section-title">Quick start</h2>
516
+ <ul className="list">
517
+ <li>Edit <code>src/client/App.tsx</code> to change this page.</li>
518
+ <li>Adjust styles in <code>src/client/styles.css</code>.</li>
519
+ <li>Configure routes in <code>taujs.config.ts</code>.</li>
520
+ <li>
521
+ Visit <a href="/">/</a> for standard SSR and{" "}
522
+ <a href="/streaming">/streaming</a> for streaming SSR.
523
+ </li>
524
+ <li>Further infomration can be found at <a href="http://taujs.dev" target="_blank">\u03C4js Documentation and Guides</a>.</li>
525
+ </ul>
526
+ </section>
527
+
528
+ <section className="tip">
529
+ <p>
530
+ <strong>SSR:</strong> The <code>/</code> route resolves all data on the server
531
+ before sending HTML. You get a complete, fully rendered document on first byte,
532
+ which is ideal for predictable latency and caching.
533
+ </p>
534
+ <p>
535
+ <strong>STREAM:</strong> The <code>/streaming</code> route uses a service descriptor
536
+ and returns a Promise. The <code>&lt;Suspense&gt;</code> boundary above shows
537
+ a fallback while the server resolves it, then progressively streams the final content.
538
+ </p>
539
+ </section>
540
+
541
+ <footer className="app-footer">
542
+ <p>
543
+ Built with{" "}
544
+ <a href="https://taujs.dev" target="_blank" rel="noopener">
545
+ \u03C4js
546
+ </a>
547
+ {" \xB7 "}
548
+ <a href="https://fastify.dev" target="_blank" rel="noopener">
549
+ Fastify
550
+ </a>
551
+ {" \xB7 "}
552
+ <a href="https://react.dev" target="_blank" rel="noopener">
553
+ React
554
+ </a>
555
+ </p>
556
+ </footer>
557
+ </div>
558
+ );
559
+ }
560
+ `;
561
+ }
562
+ function generateStyles() {
563
+ return `:root {
564
+ --accent: #38bdf8;
565
+ --accent-soft: #0ea5e9;
566
+ --accent-soft-bg: #0b1120;
567
+ --bg: #020617;
568
+ --bg-dark: #000;
569
+ --bg-elevated: #020617;
570
+ --border-subtle: #1e293b;
571
+ --color-accent-rgb: 56, 189, 248; /* #38bdf8 */
572
+ --color-app-title-rgb: 229, 231, 235; /* #e5e7eb */
573
+ --color-border-subtle-rgb: 30, 41, 59; /* #1e293b */
574
+ --color-code-border-rgb: 51, 65, 85; /* rgba(51, 65, 85, 0.9) */
575
+ --color-code-bg-rgb: 15, 23, 42; /* rgba(15, 23, 42, 0.9) */
576
+ --color-tip-border-rgb: 148, 163, 184; /* rgba(148, 163, 184, 0.9) */
577
+ --color-tip-bg-rgb: 15, 23, 42; /* rgba(15, 23, 42, 0.95) */
578
+ --color-footer-border-rgb: 30, 64, 175; /* rgba(30, 64, 175, 0.7) */
579
+ --radius-lg: 12px;
580
+ --radius-xl: 16px;
581
+ --shadow-soft: 0 18px 45px rgba(15, 23, 42, 0.7);
582
+ --text: #f9fafb;
583
+ --text-muted: #cbd5f5;
584
+ --text-soft: #9ca3af;
585
+ }
586
+
587
+ *,
588
+ *::before,
589
+ *::after {
590
+ box-sizing: border-box;
591
+ }
592
+
593
+ html,
594
+ body {
595
+ margin: 0;
596
+ min-height: 100%;
597
+ padding: 0;
598
+ }
599
+
600
+ body {
601
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
602
+ "Segoe UI", sans-serif;
603
+ background: radial-gradient(
604
+ circle at top left,
605
+ var(--border-subtle) 0,
606
+ var(--bg) 38%,
607
+ var(--bg-dark) 85%
608
+ );
609
+ color: var(--text);
610
+ }
611
+
612
+ a {
613
+ color: var(--accent);
614
+ text-decoration: none;
615
+ }
616
+
617
+ a:hover,
618
+ a:focus-visible {
619
+ text-decoration: underline;
620
+ }
621
+
622
+ code {
623
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
624
+ "Liberation Mono", "Courier New", monospace;
625
+ font-size: 0.9em;
626
+ padding: 0.15rem 0.35rem;
627
+ border-radius: 4px;
628
+ background: rgba(var(--color-code-bg-rgb), 0.9);
629
+ border: 1px solid rgba(var(--color-code-border-rgb), 0.9);
630
+ }
631
+
632
+ .app {
633
+ margin: 0 auto;
634
+ max-width: 960px;
635
+ padding: 3rem 1.5rem 4rem;
636
+ }
637
+
638
+ @media (min-width: 768px) {
639
+ .app {
640
+ padding: 4rem 2rem 5rem;
641
+ }
642
+ }
643
+
644
+ .app-header {
645
+ margin-bottom: 2.5rem;
646
+ }
647
+
648
+ .app-title {
649
+ color: rgb(var(--color-app-title-rgb));
650
+ font-size: clamp(2rem, 2.7vw + 1.5rem, 2.8rem);
651
+ letter-spacing: -0.04em;
652
+ margin: 0;
653
+ padding: 0 0 0 60px;
654
+ position: relative;
655
+ }
656
+
657
+ .app-title::before {
658
+ background: url("favicon.svg") no-repeat;
659
+ background-size: 50px 50px;
660
+ content: "";
661
+ border-radius: 4px;
662
+ display: block;
663
+ height: 50px;
664
+ left: 0;
665
+ position: absolute;
666
+ top: 0;
667
+ width: 50px;
668
+ }
669
+
670
+ .app-subtitle {
671
+ color: var(--text-soft);
672
+ font-size: 0.95rem;
673
+ margin: 0.8rem 0 0;
674
+ }
675
+
676
+ .card {
677
+ background: radial-gradient(
678
+ circle at top left,
679
+ var(--accent-soft-bg) 0,
680
+ var(--bg) 45%
681
+ );
682
+ border: 1px solid rgba(var(--color-accent-rgb), 0.7);
683
+ border-radius: var(--radius-xl);
684
+ box-shadow: var(--shadow-soft);
685
+ overflow: hidden;
686
+ padding: 1.75rem 1.5rem;
687
+ position: relative;
688
+ }
689
+
690
+ .card::before {
691
+ content: "";
692
+ position: absolute;
693
+ inset: -40%;
694
+ background:
695
+ radial-gradient(
696
+ circle at 0 0,
697
+ rgba(var(--color-accent-rgb), 0.16),
698
+ transparent 60%
699
+ ),
700
+ radial-gradient(
701
+ circle at 100% 0,
702
+ rgba(59, 130, 246, 0.2),
703
+ transparent 65%
704
+ );
705
+ opacity: 0.9;
706
+ pointer-events: none;
707
+ }
708
+
709
+ .card > * {
710
+ position: relative;
711
+ }
712
+
713
+ .card-message {
714
+ color: var(--text);
715
+ font-size: 1.25rem;
716
+ margin: 0;
717
+ }
718
+
719
+ .card-meta {
720
+ color: var(--text-soft);
721
+ font-size: 0.85rem;
722
+ margin: 0.6rem 0 0;
723
+ }
724
+
725
+ .section {
726
+ background: rgba(var(--color-code-bg-rgb), 0.9);
727
+ border: 1px solid var(--border-subtle);
728
+ border-radius: var(--radius-lg);
729
+ margin-top: 2rem;
730
+ padding: 1.6rem 1.5rem;
731
+ }
732
+
733
+ .section-title {
734
+ color: rgb(var(--color-app-title-rgb));
735
+ font-size: 1.1rem;
736
+ margin: 0 0 0.75rem;
737
+ }
738
+
739
+ .list {
740
+ color: var(--text-muted);
741
+ font-size: 0.95rem;
742
+ line-height: 1.8;
743
+ margin: 0;
744
+ padding-left: 1.1rem;
745
+ }
746
+
747
+ .tip {
748
+ background: rgba(var(--color-tip-bg-rgb), 0.95);
749
+ border: 1px solid rgba(var(--color-tip-border-rgb), 0.9);
750
+ border-radius: 10px;
751
+ color: var(--text);
752
+ font-size: 0.9rem;
753
+ line-height: 1.6;
754
+ margin-top: 1.6rem;
755
+ padding: 1.1rem 1.3rem 1.25rem;
756
+ }
757
+
758
+ .tip p {
759
+ margin: 0 0 0.6rem;
760
+ }
761
+
762
+ .tip p:last-child {
763
+ margin-bottom: 0;
764
+ }
765
+
766
+ .app-footer {
767
+ border-top: 1px solid rgba(var(--color-footer-border-rgb), 0.7);
768
+ color: var(--text-soft);
769
+ font-size: 0.85rem;
770
+ margin-top: 3rem;
771
+ padding-top: 1.4rem;
772
+ text-align: center;
773
+ }`;
774
+ }
775
+ function generateViteEnv() {
776
+ return `/// <reference types="vite/client" />
777
+ `;
778
+ }
779
+ function generateEntryClient() {
780
+ return `import { hydrateApp } from '@taujs/react';
781
+ import { App } from './App';
782
+
783
+ hydrateApp({
784
+ appComponent: <App />,
785
+ rootElementId: 'root',
786
+ enableDebug: import.meta.env.DEV,
787
+ });
788
+ `;
789
+ }
790
+ function generateEntryServer() {
791
+ return `import { createRenderer } from '@taujs/react';
792
+ import { App } from './App';
793
+
794
+ export const { renderSSR, renderStream } = createRenderer({
795
+ appComponent: () => <App />,
796
+ headContent: ({ data }) => \`
797
+ <meta charset="UTF-8">
798
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
799
+ <meta name="description" content="A \u03C4js template application">
800
+ <title>\${data?.message || "\u03C4js - Composing systems, not just apps"}</title>
801
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml"/>
802
+ \`,
803
+ enableDebug: process.env.NODE_ENV === "development",
804
+ });
805
+ `;
806
+ }
807
+ function generateServerIndex() {
808
+ return `import { createServer } from '@taujs/server';
809
+ import config from '../../taujs.config.ts';
810
+ import { serviceRegistry } from './services/registry.ts';
811
+
812
+ const { app, net } = await createServer({
813
+ config,
814
+ serviceRegistry,
815
+ clientRoot: './src/client',
816
+ debug: !!process.env.DEBUG,
817
+ });
818
+
819
+ if (app) {
820
+ await app.listen({
821
+ host: net.host,
822
+ port: net.port,
823
+ });
824
+ }
825
+ `;
826
+ }
827
+ function generateServiceRegistry() {
828
+ return `import { defineServiceRegistry } from '@taujs/server/config';
829
+ import { exampleService } from './example.service.ts';
830
+
831
+ export const serviceRegistry = defineServiceRegistry({
832
+ example: exampleService,
833
+ });
834
+
835
+ export type ServiceRegistry = typeof serviceRegistry;
836
+ `;
837
+ }
838
+ function generateServiceTypesAugmentation() {
839
+ return `import type { RegistryCaller } from '@taujs/server/config';
840
+ import type { serviceRegistry } from './registry';
841
+
842
+ declare module '@taujs/server/config' {
843
+ interface ServiceContext {
844
+ call: RegistryCaller<typeof serviceRegistry>;
845
+ }
846
+ }
847
+ `;
848
+ }
849
+ function generateExampleService() {
850
+ return `import { defineService } from '@taujs/server/config';
851
+
852
+ export const exampleService = defineService({
853
+ async greet(params: { name: string }) {
854
+ // Simulate async operation
855
+ await new Promise((resolve) => setTimeout(resolve, 750));
856
+
857
+ const modeDescription =
858
+ params.name === 'Streaming'
859
+ ? 'via service descriptors.'
860
+ : 'via direct ctx.call.';
861
+
862
+ return {
863
+ message: \`Hello, \${params.name}. Response provided by a \u03C4js service \${modeDescription}\`,
864
+ timestamp: new Date().toISOString(),
865
+ };
866
+ },
867
+
868
+ async getData(params: { id: string }) {
869
+ return {
870
+ id: params.id,
871
+ data: 'Example data from service',
872
+ timestamp: new Date().toISOString(),
873
+ };
874
+ },
875
+ });
876
+ `;
877
+ }
878
+ function generateFavicon() {
879
+ return `<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
880
+ <g transform="matrix(1.203376054763794, 0, 0, 1.203376054763794, -47.249202728271484, -58.526153564453125)">
881
+ <ellipse style="stroke: rgb(0, 0, 0); fill: rgb(255, 250, 250);" cx="245.728" cy="256.598" rx="171.553" ry="171.553"/>
882
+ <path d="M 221.7 53.324 C 210 55.024 199.4 57.324 186.1 61.424 C 157.3 70.124 136.8 80.824 114.2 99.024 C 41.1 157.824 18.8 260.524 60.7 345.424 C 67.2 358.624 83.7 382.824 94.7 395.124 C 107.2 409.224 129.3 426.424 147.7 436.424 C 162.7 444.624 187.8 453.624 205.9 457.324 C 226.8 461.624 261.4 461.824 282.7 457.824 C 315.4 451.624 353.3 434.324 375.7 415.424 C 385.3 407.424 399.8 392.224 403.3 386.724 C 404.3 385.124 406.7 381.924 408.7 379.724 C 415.4 371.824 418.7 367.524 419.9 364.624 C 420.6 363.124 422.8 359.324 424.8 356.324 C 428.3 351.024 436.7 333.524 438.2 328.324 C 438.6 326.924 440 323.924 441.3 321.724 C 444.5 316.324 450.2 291.424 451.7 276.824 C 453.4 260.624 452.5 231.724 449.9 218.824 C 444.9 194.024 436.6 172.424 424.6 152.524 C 408 125.224 387.6 103.924 361.5 86.424 C 339.6 71.824 308.9 59.824 280.2 54.724 C 270.6 52.924 231 52.024 221.7 53.324 Z M 271.2 98.324 C 296.7 101.824 323.2 112.324 344.2 127.124 C 352.8 133.224 374.6 154.424 381.4 163.324 C 391 175.924 400.4 197.524 406.4 220.824 C 410.1 235.624 410.2 236.124 410.2 254.324 C 410.1 275.524 408.6 285.624 403.1 302.324 C 392.6 333.724 374.7 359.324 347.2 382.124 C 326.6 399.124 295.4 412.124 266.7 415.524 C 255.1 416.824 229.3 416.024 217.8 413.924 C 179.9 406.924 146.7 388.924 123.2 362.524 C 103.1 339.924 89.2 312.024 83.6 283.024 C 78.9 259.124 81.6 227.024 90.2 202.024 C 92.7 195.024 95.3 188.324 96.1 187.024 C 97 185.824 98.3 182.924 99.1 180.724 C 102.8 170.224 122.6 145.824 135.2 136.424 C 153.1 123.024 158.5 119.524 169.2 114.324 C 190.2 104.124 207.1 99.424 230.7 97.324 C 241.7 96.324 259.7 96.824 271.2 98.324 Z"/>
883
+ <path d="M 278.7 156.424 C 256.4 156.724 222.2 156.824 202.7 156.524 C 160.8 155.924 164.4 155.024 149.6 169.424 C 138.6 180.324 125.7 197.124 125.7 200.824 C 125.7 201.124 148 201.224 175.2 201.124 L 224.7 200.824 L 224.4 292.824 L 224.2 384.724 L 237.2 385.124 C 244.4 385.324 253.9 385.224 258.5 384.824 L 266.7 384.124 L 266.7 292.424 L 266.7 200.724 L 316.2 200.824 C 373.4 200.824 367.8 202.424 359.5 188.324 C 351.3 174.424 343.7 164.524 338.8 161.324 C 333.8 157.924 325.7 155.224 321.8 155.524 C 320.4 155.624 301 156.024 278.7 156.424 Z"/>
884
+ <path d="M 113.7 249.324 C 113.7 256.724 113.4 267.124 113.1 272.324 L 112.4 281.824 L 131.5 281.524 L 150.5 281.224 L 151.2 287.424 C 151.9 293.624 151.1 334.524 150.1 339.524 C 149.4 343.124 154.1 348.224 166.6 357.624 C 175.8 364.524 190.4 373.324 192.7 373.324 C 193.5 373.324 193.7 354.224 193.5 304.824 L 193.2 236.324 L 153.4 236.024 L 113.7 235.824 L 113.7 249.324 Z"/>
885
+ <path d="M 298.2 281.224 C 298.2 347.124 298.6 373.324 299.6 373.324 C 300.8 373.324 317.1 363.324 322.3 359.324 C 324.8 357.424 330 352.824 333.8 349.124 L 340.8 342.324 L 340.7 337.124 C 340.6 334.224 340.6 320.424 340.6 306.524 L 340.7 281.324 L 359.7 281.324 L 378.7 281.324 L 378.6 260.024 C 378.6 248.424 378.3 238.124 377.9 237.324 C 377.3 236.024 371.8 235.824 337.7 235.724 L 298.2 235.624 L 298.2 281.224 Z"/>
886
+ </g>
887
+ </svg>
888
+ `;
889
+ }
890
+ main().catch((error) => {
891
+ console.error(pc.red("\n\u2716 Error creating project:"), error);
892
+ process.exit(1);
893
+ });
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@taujs/create-taujs",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a new τjs application",
5
+ "author": "Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
6
+ "homepage": "https://taujs.dev/",
7
+ "license": "MIT",
8
+ "type": "module",
9
+ "bin": {
10
+ "create-taujs": "dist/index.js"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "ci": "npm run build && npm run lint",
18
+ "lint": "tsc",
19
+ "dev": "tsup --watch",
20
+ "prepublishOnly": "npm run ci",
21
+ "local-release": "npm run ci && changeset version && changeset publish"
22
+ },
23
+ "dependencies": {
24
+ "@changesets/cli": "^2.29.8",
25
+ "fs-extra": "^11.2.0",
26
+ "picocolors": "^1.0.0",
27
+ "prompts": "^2.4.2"
28
+ },
29
+ "devDependencies": {
30
+ "@types/fs-extra": "^11.0.4",
31
+ "@types/node": "^20.11.0",
32
+ "@types/prompts": "^2.4.9",
33
+ "tsup": "^8.0.0",
34
+ "typescript": "^5.3.0"
35
+ },
36
+ "keywords": [
37
+ "taujs",
38
+ "create-taujs",
39
+ "scaffold",
40
+ "template",
41
+ "react",
42
+ "ssr",
43
+ "fastify",
44
+ "typescript"
45
+ ],
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/aoede3/create-taujs.git"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/aoede3/create-taujs/issues"
52
+ }
53
+ }