toiljs 0.0.11 → 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 (119) hide show
  1. package/README.md +2 -0
  2. package/build/cli/.tsbuildinfo +1 -1
  3. package/build/cli/configure.js +10 -4
  4. package/build/cli/create.js +58 -30
  5. package/build/cli/diagnostics.d.ts +55 -0
  6. package/build/cli/diagnostics.js +333 -0
  7. package/build/cli/doctor.d.ts +6 -0
  8. package/build/cli/doctor.js +249 -0
  9. package/build/cli/index.js +26 -0
  10. package/build/cli/proc.d.ts +5 -0
  11. package/build/cli/proc.js +20 -0
  12. package/build/cli/ui.d.ts +1 -0
  13. package/build/cli/ui.js +1 -0
  14. package/build/cli/update.d.ts +7 -0
  15. package/build/cli/update.js +117 -0
  16. package/build/cli/updates.d.ts +10 -0
  17. package/build/cli/updates.js +45 -0
  18. package/build/client/.tsbuildinfo +1 -1
  19. package/build/client/dev/error-overlay.js +1 -1
  20. package/build/client/head/metadata.js +3 -1
  21. package/build/client/index.d.ts +5 -1
  22. package/build/client/index.js +2 -0
  23. package/build/client/navigation/navigation.js +1 -1
  24. package/build/client/routing/Router.js +2 -2
  25. package/build/client/search/search.d.ts +26 -0
  26. package/build/client/search/search.js +101 -0
  27. package/build/client/search/use-page-search.d.ts +8 -0
  28. package/build/client/search/use-page-search.js +21 -0
  29. package/build/compiler/.tsbuildinfo +1 -1
  30. package/build/compiler/generate.js +26 -23
  31. package/build/compiler/index.d.ts +2 -0
  32. package/build/compiler/index.js +1 -0
  33. package/build/compiler/pages.d.ts +8 -0
  34. package/build/compiler/pages.js +37 -0
  35. package/build/compiler/plugin.js +3 -1
  36. package/build/compiler/prerender.d.ts +1 -0
  37. package/build/compiler/prerender.js +11 -5
  38. package/build/compiler/seo.js +10 -3
  39. package/build/io/.tsbuildinfo +1 -1
  40. package/examples/basic/client/components/Header.tsx +43 -41
  41. package/examples/basic/client/components/HoneycombBackground.tsx +223 -230
  42. package/examples/basic/client/public/index.html +18 -16
  43. package/examples/basic/client/routes/(legal)/privacy.tsx +18 -19
  44. package/examples/basic/client/routes/(legal)/terms.tsx +15 -16
  45. package/examples/basic/client/routes/about.tsx +21 -22
  46. package/examples/basic/client/routes/blog/[id].tsx +26 -18
  47. package/examples/basic/client/routes/features/actions.tsx +67 -67
  48. package/examples/basic/client/routes/features/error/index.tsx +27 -27
  49. package/examples/basic/client/routes/features/head.tsx +38 -38
  50. package/examples/basic/client/routes/features/index.tsx +83 -75
  51. package/examples/basic/client/routes/features/realtime.tsx +34 -32
  52. package/examples/basic/client/routes/features/script.tsx +31 -31
  53. package/examples/basic/client/routes/features/seo.tsx +39 -39
  54. package/examples/basic/client/routes/features/template/index.tsx +20 -20
  55. package/examples/basic/client/routes/features/template/template.tsx +16 -18
  56. package/examples/basic/client/routes/gallery/@modal/(.)photo/[id].tsx +23 -23
  57. package/examples/basic/client/routes/gallery/index.tsx +42 -42
  58. package/examples/basic/client/routes/gallery/photo/[id].tsx +18 -18
  59. package/examples/basic/client/routes/get-started.tsx +157 -84
  60. package/examples/basic/client/routes/index.tsx +137 -96
  61. package/examples/basic/client/routes/loader-demo/index.tsx +59 -52
  62. package/examples/basic/client/routes/search.tsx +61 -0
  63. package/examples/basic/client/routes/test.tsx +7 -8
  64. package/examples/basic/client/styles/main.css +624 -552
  65. package/package.json +2 -2
  66. package/presets/eslint.js +10 -3
  67. package/src/cli/configure.ts +363 -353
  68. package/src/cli/create.ts +563 -530
  69. package/src/cli/diagnostics.ts +421 -0
  70. package/src/cli/doctor.ts +318 -0
  71. package/src/cli/features.ts +166 -160
  72. package/src/cli/index.ts +242 -211
  73. package/src/cli/proc.ts +30 -0
  74. package/src/cli/ui.ts +111 -103
  75. package/src/cli/update.ts +150 -0
  76. package/src/cli/updates.ts +69 -0
  77. package/src/client/components/Image.tsx +91 -89
  78. package/src/client/dev/error-overlay.tsx +193 -197
  79. package/src/client/head/metadata.ts +94 -92
  80. package/src/client/index.ts +79 -64
  81. package/src/client/navigation/Link.tsx +94 -100
  82. package/src/client/navigation/navigation.ts +215 -218
  83. package/src/client/routing/Router.tsx +210 -193
  84. package/src/client/routing/hooks.ts +110 -114
  85. package/src/client/routing/lazy.ts +77 -81
  86. package/src/client/search/search.ts +189 -0
  87. package/src/client/search/use-page-search.ts +73 -0
  88. package/src/compiler/config.ts +173 -171
  89. package/src/compiler/fonts.ts +89 -87
  90. package/src/compiler/generate.ts +378 -373
  91. package/src/compiler/image-report.ts +88 -85
  92. package/src/compiler/index.ts +2 -0
  93. package/src/compiler/pages.ts +70 -0
  94. package/src/compiler/plugin.ts +51 -47
  95. package/src/compiler/prerender.ts +152 -130
  96. package/src/compiler/routes.ts +132 -131
  97. package/src/compiler/seo.ts +381 -356
  98. package/src/compiler/vite.ts +155 -145
  99. package/src/io/FastSet.ts +99 -96
  100. package/test/configure.test.ts +94 -90
  101. package/test/doctor.test.ts +140 -0
  102. package/test/dom/Image.test.tsx +73 -46
  103. package/test/dom/Script.test.tsx +48 -45
  104. package/test/dom/action.test.tsx +146 -129
  105. package/test/dom/error-overlay.test.tsx +44 -44
  106. package/test/dom/loader.test.tsx +2 -2
  107. package/test/dom/revalidate.test.tsx +1 -1
  108. package/test/dom/route-head.test.tsx +1 -2
  109. package/test/dom/slot.test.tsx +131 -109
  110. package/test/dom/view-transitions.test.tsx +53 -51
  111. package/test/features.test.ts +149 -142
  112. package/test/fonts.test.ts +28 -26
  113. package/test/head.test.ts +45 -35
  114. package/test/metadata.test.ts +42 -41
  115. package/test/pages.test.ts +105 -0
  116. package/test/prerender.test.ts +54 -46
  117. package/test/search.test.ts +114 -0
  118. package/test/seo.test.ts +164 -142
  119. 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
+ }