toiljs 0.0.10 → 0.0.12

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 (128) hide show
  1. package/README.md +315 -1
  2. package/assets/logo.svg +37 -0
  3. package/build/cli/.tsbuildinfo +1 -1
  4. package/build/cli/configure.js +10 -4
  5. package/build/cli/create.js +60 -32
  6. package/build/cli/diagnostics.d.ts +55 -0
  7. package/build/cli/diagnostics.js +333 -0
  8. package/build/cli/doctor.d.ts +6 -0
  9. package/build/cli/doctor.js +249 -0
  10. package/build/cli/index.js +26 -0
  11. package/build/cli/proc.d.ts +5 -0
  12. package/build/cli/proc.js +20 -0
  13. package/build/cli/ui.d.ts +1 -0
  14. package/build/cli/ui.js +1 -0
  15. package/build/cli/update.d.ts +7 -0
  16. package/build/cli/update.js +117 -0
  17. package/build/cli/updates.d.ts +10 -0
  18. package/build/cli/updates.js +45 -0
  19. package/build/client/.tsbuildinfo +1 -1
  20. package/build/client/dev/error-overlay.js +1 -1
  21. package/build/client/head/metadata.js +3 -1
  22. package/build/client/index.d.ts +5 -1
  23. package/build/client/index.js +2 -0
  24. package/build/client/navigation/navigation.js +1 -1
  25. package/build/client/routing/Router.js +2 -2
  26. package/build/client/search/search.d.ts +26 -0
  27. package/build/client/search/search.js +101 -0
  28. package/build/client/search/use-page-search.d.ts +8 -0
  29. package/build/client/search/use-page-search.js +21 -0
  30. package/build/compiler/.tsbuildinfo +1 -1
  31. package/build/compiler/generate.js +35 -26
  32. package/build/compiler/index.d.ts +2 -0
  33. package/build/compiler/index.js +1 -0
  34. package/build/compiler/pages.d.ts +8 -0
  35. package/build/compiler/pages.js +37 -0
  36. package/build/compiler/plugin.js +3 -1
  37. package/build/compiler/prerender.d.ts +1 -0
  38. package/build/compiler/prerender.js +11 -5
  39. package/build/compiler/seo.js +10 -3
  40. package/build/compiler/vite.js +7 -0
  41. package/build/io/.tsbuildinfo +1 -1
  42. package/examples/basic/client/components/Header.tsx +43 -38
  43. package/examples/basic/client/components/HoneycombBackground.tsx +223 -230
  44. package/examples/basic/client/layout.tsx +4 -1
  45. package/examples/basic/client/public/index.html +18 -16
  46. package/examples/basic/client/routes/(legal)/privacy.tsx +18 -0
  47. package/examples/basic/client/routes/(legal)/terms.tsx +15 -0
  48. package/examples/basic/client/routes/about.tsx +21 -19
  49. package/examples/basic/client/routes/blog/[id].tsx +26 -12
  50. package/examples/basic/client/routes/features/actions.tsx +67 -0
  51. package/examples/basic/client/routes/features/error/error.tsx +16 -0
  52. package/examples/basic/client/routes/features/error/index.tsx +27 -0
  53. package/examples/basic/client/routes/features/head.tsx +38 -0
  54. package/examples/basic/client/routes/features/index.tsx +83 -0
  55. package/examples/basic/client/routes/features/realtime.tsx +34 -0
  56. package/examples/basic/client/routes/features/script.tsx +31 -0
  57. package/examples/basic/client/routes/features/seo.tsx +39 -0
  58. package/examples/basic/client/routes/features/template/b.tsx +14 -0
  59. package/examples/basic/client/routes/features/template/index.tsx +20 -0
  60. package/examples/basic/client/routes/features/template/template.tsx +16 -0
  61. package/examples/basic/client/routes/files/[[...slug]].tsx +21 -0
  62. package/examples/basic/client/routes/gallery/@modal/(.)photo/[id].tsx +23 -0
  63. package/examples/basic/client/routes/gallery/index.tsx +42 -0
  64. package/examples/basic/client/routes/gallery/layout.tsx +13 -0
  65. package/examples/basic/client/routes/gallery/photo/[id].tsx +18 -0
  66. package/examples/basic/client/routes/get-started.tsx +157 -84
  67. package/examples/basic/client/routes/index.tsx +137 -87
  68. package/examples/basic/client/routes/loader-demo/index.tsx +59 -50
  69. package/examples/basic/client/routes/search.tsx +61 -0
  70. package/examples/basic/client/routes/test.tsx +7 -8
  71. package/examples/basic/client/styles/main.css +624 -552
  72. package/examples/basic/client/toil.tsx +2 -4
  73. package/package.json +3 -2
  74. package/presets/eslint.js +10 -3
  75. package/src/cli/configure.ts +363 -353
  76. package/src/cli/create.ts +563 -530
  77. package/src/cli/diagnostics.ts +421 -0
  78. package/src/cli/doctor.ts +318 -0
  79. package/src/cli/features.ts +166 -160
  80. package/src/cli/index.ts +242 -211
  81. package/src/cli/proc.ts +30 -0
  82. package/src/cli/ui.ts +111 -103
  83. package/src/cli/update.ts +150 -0
  84. package/src/cli/updates.ts +69 -0
  85. package/src/client/components/Image.tsx +91 -89
  86. package/src/client/dev/error-overlay.tsx +193 -197
  87. package/src/client/head/metadata.ts +94 -92
  88. package/src/client/index.ts +79 -64
  89. package/src/client/navigation/Link.tsx +94 -100
  90. package/src/client/navigation/navigation.ts +215 -218
  91. package/src/client/routing/Router.tsx +210 -193
  92. package/src/client/routing/hooks.ts +110 -114
  93. package/src/client/routing/lazy.ts +77 -81
  94. package/src/client/search/search.ts +189 -0
  95. package/src/client/search/use-page-search.ts +73 -0
  96. package/src/compiler/config.ts +173 -171
  97. package/src/compiler/fonts.ts +89 -87
  98. package/src/compiler/generate.ts +378 -364
  99. package/src/compiler/image-report.ts +88 -85
  100. package/src/compiler/index.ts +2 -0
  101. package/src/compiler/pages.ts +70 -0
  102. package/src/compiler/plugin.ts +51 -47
  103. package/src/compiler/prerender.ts +152 -130
  104. package/src/compiler/routes.ts +132 -131
  105. package/src/compiler/seo.ts +381 -356
  106. package/src/compiler/vite.ts +155 -130
  107. package/src/io/FastSet.ts +99 -96
  108. package/test/configure.test.ts +94 -90
  109. package/test/doctor.test.ts +140 -0
  110. package/test/dom/Image.test.tsx +73 -46
  111. package/test/dom/Script.test.tsx +48 -45
  112. package/test/dom/action.test.tsx +146 -129
  113. package/test/dom/error-overlay.test.tsx +44 -44
  114. package/test/dom/loader.test.tsx +2 -2
  115. package/test/dom/revalidate.test.tsx +1 -1
  116. package/test/dom/route-head.test.tsx +35 -2
  117. package/test/dom/slot.test.tsx +131 -109
  118. package/test/dom/view-transitions.test.tsx +53 -51
  119. package/test/features.test.ts +149 -142
  120. package/test/fonts.test.ts +28 -26
  121. package/test/head.test.ts +45 -35
  122. package/test/metadata.test.ts +42 -41
  123. package/test/pages.test.ts +105 -0
  124. package/test/prerender.test.ts +54 -46
  125. package/test/search.test.ts +114 -0
  126. package/test/seo.test.ts +164 -142
  127. package/test/slot-layouts.test.ts +69 -0
  128. package/test/update.test.ts +44 -0
package/src/cli/index.ts CHANGED
@@ -1,211 +1,242 @@
1
- #!/usr/bin/env node
2
- /**
3
- * toiljs CLI. Routes `create` / `dev` / `build` and wraps them in the toiljs brand banner.
4
- * The compiler stays presentation-free (imported via the package's own `toiljs/compiler`
5
- * export); the epic bits, banner, the Clack scaffolding wizard, live here.
6
- */
7
- import { build, dev, start } from 'toiljs/compiler';
8
-
9
- import { runConfigure } from './configure.js';
10
- import { runCreate, type Template } from './create.js';
11
- import { PREPROCESSORS, type Preprocessor } from './features.js';
12
- import { accent, banner, bold, danger, dim, success, version } from './ui.js';
13
-
14
- interface Flags {
15
- root?: string;
16
- port?: number;
17
- name?: string;
18
- template?: Template;
19
- preprocessor?: Preprocessor;
20
- tailwind?: boolean;
21
- ai?: boolean;
22
- images?: boolean;
23
- install?: boolean;
24
- git?: boolean;
25
- pm?: string;
26
- yes?: boolean;
27
- }
28
-
29
- function parseArgs(argv: string[]): Flags {
30
- const flags: Flags = {};
31
- for (let i = 0; i < argv.length; i++) {
32
- const arg = argv[i];
33
- switch (arg) {
34
- case '--root':
35
- flags.root = argv[++i];
36
- break;
37
- case '--port': {
38
- const port = Number(argv[++i]);
39
- if (!Number.isNaN(port)) flags.port = port;
40
- break;
41
- }
42
- case '--template':
43
- case '-t': {
44
- const t = argv[++i];
45
- if (t === 'app' || t === 'minimal') flags.template = t;
46
- break;
47
- }
48
- case '--pm':
49
- flags.pm = argv[++i];
50
- break;
51
- case '--style': {
52
- const s = argv[++i];
53
- if ((PREPROCESSORS as readonly string[]).includes(s)) flags.preprocessor = s as Preprocessor;
54
- break;
55
- }
56
- case '--tailwind':
57
- flags.tailwind = true;
58
- break;
59
- case '--no-tailwind':
60
- flags.tailwind = false;
61
- break;
62
- case '--ai':
63
- flags.ai = true;
64
- break;
65
- case '--no-ai':
66
- flags.ai = false;
67
- break;
68
- case '--images':
69
- flags.images = true;
70
- break;
71
- case '--no-images':
72
- flags.images = false;
73
- break;
74
- case '--install':
75
- flags.install = true;
76
- break;
77
- case '--no-install':
78
- flags.install = false;
79
- break;
80
- case '--git':
81
- flags.git = true;
82
- break;
83
- case '--no-git':
84
- flags.git = false;
85
- break;
86
- case '-y':
87
- case '--yes':
88
- flags.yes = true;
89
- break;
90
- default:
91
- if (!arg.startsWith('-') && flags.name === undefined) flags.name = arg;
92
- }
93
- }
94
- return flags;
95
- }
96
-
97
- function printHelp(): void {
98
- const cmd = (name: string, desc: string): string => ` ${accent(name.padEnd(15))}${dim(desc)}`;
99
- process.stdout.write(
100
- [
101
- `${bold('Usage')} ${dim('toiljs')} <command> [options]`,
102
- '',
103
- bold('Commands'),
104
- cmd('create [name]', 'scaffold a new toiljs app'),
105
- cmd('configure', 'toggle styling features (Sass/Less/Stylus, Tailwind)'),
106
- cmd('dev', 'start the dev server with HMR'),
107
- cmd('build', 'build the optimized production bundle'),
108
- cmd('start', 'self-host the built app (hyper-express / uWS)'),
109
- '',
110
- bold('Options'),
111
- cmd('--root <dir>', 'project root (default: current directory)'),
112
- cmd('--port <n>', 'dev server port'),
113
- cmd('-t, --template', 'create: app | minimal'),
114
- cmd('--style <name>', 'create/configure: css | sass | less | stylus'),
115
- cmd('--tailwind', 'create/configure: enable Tailwind (--no-tailwind to remove)'),
116
- cmd('--no-ai', 'create: skip AI assistant files (CLAUDE.md, etc.)'),
117
- cmd('-y, --yes', 'create: accept defaults (non-interactive)'),
118
- cmd('--no-install', "create: don't install dependencies"),
119
- cmd('-v, --version', 'print the toiljs version'),
120
- '',
121
- ].join('\n') + '\n',
122
- );
123
- }
124
-
125
- async function main(): Promise<void> {
126
- const [command, ...rest] = process.argv.slice(2);
127
-
128
- if (command === '--version' || command === '-v') {
129
- process.stdout.write(version() + '\n');
130
- return;
131
- }
132
-
133
- const flags = parseArgs(rest);
134
-
135
- switch (command) {
136
- case 'create':
137
- banner();
138
- await runCreate({
139
- name: flags.name,
140
- template: flags.template,
141
- preprocessor: flags.preprocessor,
142
- tailwind: flags.tailwind,
143
- ai: flags.ai,
144
- images: flags.images,
145
- install: flags.install,
146
- git: flags.git,
147
- pm: flags.pm,
148
- yes: flags.yes,
149
- cwd: process.cwd(),
150
- });
151
- break;
152
-
153
- case 'configure':
154
- banner();
155
- await runConfigure({
156
- root: flags.root,
157
- preprocessor: flags.preprocessor,
158
- tailwind: flags.tailwind,
159
- images: flags.images,
160
- install: flags.install,
161
- cwd: process.cwd(),
162
- });
163
- break;
164
-
165
- case 'dev':
166
- banner();
167
- process.stdout.write(dim(' starting dev server…') + '\n\n');
168
- await dev({ root: flags.root, port: flags.port });
169
- break;
170
-
171
- case 'build':
172
- banner();
173
- process.stdout.write(dim(' building for production…') + '\n\n');
174
- await build({ root: flags.root });
175
- process.stdout.write('\n' + success(' ✓ ') + bold('build complete') + '\n\n');
176
- break;
177
-
178
- case 'start': {
179
- banner();
180
- process.stdout.write(dim(' self-hosting the built app…') + '\n\n');
181
- const server = await start({ root: flags.root, port: flags.port });
182
- process.stdout.write(
183
- accent(' ➜ ') +
184
- bold(`http://localhost:${String(server.port)}`) +
185
- dim(` ws channel: ${server.wsPath}`) +
186
- '\n\n',
187
- );
188
- break;
189
- }
190
-
191
- case 'help':
192
- case '--help':
193
- case '-h':
194
- case undefined:
195
- banner();
196
- printHelp();
197
- break;
198
-
199
- default:
200
- banner();
201
- process.stdout.write(dim(` unknown command: ${command}`) + '\n\n');
202
- printHelp();
203
- process.exitCode = 1;
204
- }
205
- }
206
-
207
- main().catch((err: unknown) => {
208
- const message = err instanceof Error ? err.message : String(err);
209
- process.stderr.write('\n' + danger(' ✗ ') + message + '\n');
210
- process.exitCode = 1;
211
- });
1
+ #!/usr/bin/env node
2
+ /**
3
+ * toiljs CLI. Routes `create` / `dev` / `build` and wraps them in the toiljs brand banner.
4
+ * The compiler stays presentation-free (imported via the package's own `toiljs/compiler`
5
+ * export); the epic bits, banner, the Clack scaffolding wizard, live here.
6
+ */
7
+ import { build, dev, start } from 'toiljs/compiler';
8
+
9
+ import { runConfigure } from './configure.js';
10
+ import { runCreate, type Template } from './create.js';
11
+ import { runDoctor } from './doctor.js';
12
+ import { runUpdate } from './update.js';
13
+ import { type Preprocessor, PREPROCESSORS } from './features.js';
14
+ import { accent, banner, bold, danger, dim, success, version } from './ui.js';
15
+
16
+ interface Flags {
17
+ root?: string;
18
+ port?: number;
19
+ name?: string;
20
+ template?: Template;
21
+ preprocessor?: Preprocessor;
22
+ tailwind?: boolean;
23
+ ai?: boolean;
24
+ images?: boolean;
25
+ install?: boolean;
26
+ git?: boolean;
27
+ pm?: string;
28
+ yes?: boolean;
29
+ json?: boolean;
30
+ target?: string;
31
+ }
32
+
33
+ function parseArgs(argv: string[]): Flags {
34
+ const flags: Flags = {};
35
+ for (let i = 0; i < argv.length; i++) {
36
+ const arg = argv[i];
37
+ switch (arg) {
38
+ case '--root':
39
+ flags.root = argv[++i];
40
+ break;
41
+ case '--port': {
42
+ const port = Number(argv[++i]);
43
+ if (!Number.isNaN(port)) flags.port = port;
44
+ break;
45
+ }
46
+ case '--template':
47
+ case '-t': {
48
+ const t = argv[++i];
49
+ if (t === 'app' || t === 'minimal') flags.template = t;
50
+ break;
51
+ }
52
+ case '--pm':
53
+ flags.pm = argv[++i];
54
+ break;
55
+ case '--style': {
56
+ const s = argv[++i];
57
+ if ((PREPROCESSORS as readonly string[]).includes(s))
58
+ flags.preprocessor = s as Preprocessor;
59
+ break;
60
+ }
61
+ case '--tailwind':
62
+ flags.tailwind = true;
63
+ break;
64
+ case '--no-tailwind':
65
+ flags.tailwind = false;
66
+ break;
67
+ case '--ai':
68
+ flags.ai = true;
69
+ break;
70
+ case '--no-ai':
71
+ flags.ai = false;
72
+ break;
73
+ case '--images':
74
+ flags.images = true;
75
+ break;
76
+ case '--no-images':
77
+ flags.images = false;
78
+ break;
79
+ case '--install':
80
+ flags.install = true;
81
+ break;
82
+ case '--no-install':
83
+ flags.install = false;
84
+ break;
85
+ case '--git':
86
+ flags.git = true;
87
+ break;
88
+ case '--no-git':
89
+ flags.git = false;
90
+ break;
91
+ case '-y':
92
+ case '--yes':
93
+ flags.yes = true;
94
+ break;
95
+ case '--json':
96
+ flags.json = true;
97
+ break;
98
+ case '--target':
99
+ flags.target = argv[++i];
100
+ break;
101
+ default:
102
+ if (!arg.startsWith('-') && flags.name === undefined) flags.name = arg;
103
+ }
104
+ }
105
+ return flags;
106
+ }
107
+
108
+ function printHelp(): void {
109
+ const cmd = (name: string, desc: string): string => ` ${accent(name.padEnd(15))}${dim(desc)}`;
110
+ process.stdout.write(
111
+ [
112
+ `${bold('Usage')} ${dim('toiljs')} <command> [options]`,
113
+ '',
114
+ bold('Commands'),
115
+ cmd('create [name]', 'scaffold a new toiljs app'),
116
+ cmd('configure', 'toggle styling features (Sass/Less/Stylus, Tailwind)'),
117
+ cmd('dev', 'start the dev server with HMR'),
118
+ cmd('build', 'build the optimized production bundle'),
119
+ cmd('start', 'self-host the built app (hyper-express / uWS)'),
120
+ cmd('doctor', 'diagnose project setup and dependencies'),
121
+ cmd('update', 'check for and apply dependency updates'),
122
+ '',
123
+ bold('Options'),
124
+ cmd('--root <dir>', 'project root (default: current directory)'),
125
+ cmd('--port <n>', 'dev server port'),
126
+ cmd('-t, --template', 'create: app | minimal'),
127
+ cmd('--style <name>', 'create/configure: css | sass | less | stylus'),
128
+ cmd('--tailwind', 'create/configure: enable Tailwind (--no-tailwind to remove)'),
129
+ cmd('--no-ai', 'create: skip AI assistant files (CLAUDE.md, etc.)'),
130
+ cmd('-y, --yes', 'create: accept defaults (non-interactive)'),
131
+ cmd('--no-install', "create: don't install dependencies"),
132
+ cmd('--json', 'doctor: machine-readable output'),
133
+ cmd('--target <t>', 'update: latest | minor | patch | newest | greatest'),
134
+ cmd('-v, --version', 'print the toiljs version'),
135
+ '',
136
+ ].join('\n') + '\n',
137
+ );
138
+ }
139
+
140
+ async function main(): Promise<void> {
141
+ const [command, ...rest] = process.argv.slice(2);
142
+
143
+ if (command === '--version' || command === '-v') {
144
+ process.stdout.write(version() + '\n');
145
+ return;
146
+ }
147
+
148
+ const flags = parseArgs(rest);
149
+
150
+ switch (command) {
151
+ case 'create':
152
+ banner();
153
+ await runCreate({
154
+ name: flags.name,
155
+ template: flags.template,
156
+ preprocessor: flags.preprocessor,
157
+ tailwind: flags.tailwind,
158
+ ai: flags.ai,
159
+ images: flags.images,
160
+ install: flags.install,
161
+ git: flags.git,
162
+ pm: flags.pm,
163
+ yes: flags.yes,
164
+ cwd: process.cwd(),
165
+ });
166
+ break;
167
+
168
+ case 'configure':
169
+ banner();
170
+ await runConfigure({
171
+ root: flags.root,
172
+ preprocessor: flags.preprocessor,
173
+ tailwind: flags.tailwind,
174
+ images: flags.images,
175
+ install: flags.install,
176
+ cwd: process.cwd(),
177
+ });
178
+ break;
179
+
180
+ case 'dev':
181
+ banner();
182
+ process.stdout.write(dim(' starting dev server…') + '\n\n');
183
+ await dev({ root: flags.root, port: flags.port });
184
+ break;
185
+
186
+ case 'build':
187
+ banner();
188
+ process.stdout.write(dim(' building for production…') + '\n\n');
189
+ await build({ root: flags.root });
190
+ process.stdout.write('\n' + success(' ✓ ') + bold('build complete') + '\n\n');
191
+ break;
192
+
193
+ case 'start': {
194
+ banner();
195
+ process.stdout.write(dim(' self-hosting the built app…') + '\n\n');
196
+ const server = await start({ root: flags.root, port: flags.port });
197
+ process.stdout.write(
198
+ accent(' ➜ ') +
199
+ bold(`http://localhost:${String(server.port)}`) +
200
+ dim(` ws channel: ${server.wsPath}`) +
201
+ '\n\n',
202
+ );
203
+ break;
204
+ }
205
+
206
+ case 'doctor':
207
+ // Skip the banner for --json so stdout stays valid JSON.
208
+ if (!flags.json) banner();
209
+ await runDoctor({ root: flags.root, cwd: process.cwd(), json: flags.json });
210
+ break;
211
+
212
+ case 'update':
213
+ banner();
214
+ await runUpdate({
215
+ root: flags.root,
216
+ cwd: process.cwd(),
217
+ yes: flags.yes,
218
+ target: flags.target,
219
+ });
220
+ break;
221
+
222
+ case 'help':
223
+ case '--help':
224
+ case '-h':
225
+ case undefined:
226
+ banner();
227
+ printHelp();
228
+ break;
229
+
230
+ default:
231
+ banner();
232
+ process.stdout.write(dim(` unknown command: ${command}`) + '\n\n');
233
+ printHelp();
234
+ process.exitCode = 1;
235
+ }
236
+ }
237
+
238
+ main().catch((err: unknown) => {
239
+ const message = err instanceof Error ? err.message : String(err);
240
+ process.stderr.write('\n' + danger(' ✗ ') + message + '\n');
241
+ process.exitCode = 1;
242
+ });
package/src/cli/proc.ts CHANGED
@@ -18,3 +18,33 @@ export function run(cmd: string, args: string[], cwd: string): Promise<void> {
18
18
  );
19
19
  });
20
20
  }
21
+
22
+ /**
23
+ * Like {@link run}, but captures stdout/stderr and resolves with them plus the exit code (it never
24
+ * rejects on a non-zero exit, the caller decides). Used to read JSON from tools like
25
+ * `npm-check-updates`. Same Windows shell handling as {@link run}; args are fixed/allowlisted.
26
+ */
27
+ export function capture(
28
+ cmd: string,
29
+ args: string[],
30
+ cwd: string,
31
+ ): Promise<{ stdout: string; stderr: string; code: number }> {
32
+ return new Promise((resolve, reject) => {
33
+ const onWindows = process.platform === 'win32';
34
+ const child = onWindows
35
+ ? spawn([cmd, ...args].join(' '), { cwd, shell: true })
36
+ : spawn(cmd, args, { cwd });
37
+ let stdout = '';
38
+ let stderr = '';
39
+ child.stdout?.on('data', (d: Buffer) => {
40
+ stdout += d.toString();
41
+ });
42
+ child.stderr?.on('data', (d: Buffer) => {
43
+ stderr += d.toString();
44
+ });
45
+ child.on('error', reject);
46
+ child.on('close', (code) => {
47
+ resolve({ stdout, stderr, code: code ?? 1 });
48
+ });
49
+ });
50
+ }