toiljs 0.0.15 → 0.0.19

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 (273) hide show
  1. package/.babelrc +13 -13
  2. package/.gitattributes +2 -2
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  4. package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -90
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -8
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  7. package/.github/PULL_REQUEST_TEMPLATE.md +43 -43
  8. package/.github/changelog-config.json +45 -45
  9. package/.github/dependabot.yml +27 -27
  10. package/.github/workflows/ci.yml +191 -191
  11. package/.prettierrc.json +11 -11
  12. package/.vscode/settings.json +9 -9
  13. package/CHANGELOG.md +116 -5
  14. package/LICENSE +187 -187
  15. package/README.md +524 -315
  16. package/as-pect.asconfig.json +34 -34
  17. package/as-pect.config.js +65 -65
  18. package/assets/logo.svg +36 -36
  19. package/build/backend/.tsbuildinfo +1 -1
  20. package/build/backend/index.d.ts +1 -0
  21. package/build/backend/index.js +20 -1
  22. package/build/cli/.tsbuildinfo +1 -1
  23. package/build/cli/index.js +1320 -696
  24. package/build/client/.tsbuildinfo +1 -1
  25. package/build/client/dev/devtools.d.ts +6 -0
  26. package/build/client/dev/devtools.js +479 -0
  27. package/build/client/dev/error-overlay.d.ts +9 -0
  28. package/build/client/dev/error-overlay.js +19 -4
  29. package/build/client/errors.d.ts +1 -0
  30. package/build/client/errors.js +3 -0
  31. package/build/client/index.d.ts +2 -0
  32. package/build/client/index.js +2 -0
  33. package/build/client/navigation/prefetch.d.ts +1 -0
  34. package/build/client/navigation/prefetch.js +35 -0
  35. package/build/client/routing/Router.js +1 -1
  36. package/build/client/routing/hooks.js +6 -2
  37. package/build/client/routing/loader.d.ts +23 -0
  38. package/build/client/routing/loader.js +53 -7
  39. package/build/client/routing/mount.js +4 -3
  40. package/build/client/rpc.d.ts +1 -0
  41. package/build/client/rpc.js +37 -0
  42. package/build/compiler/.tsbuildinfo +1 -1
  43. package/build/compiler/config.d.ts +16 -0
  44. package/build/compiler/config.js +9 -0
  45. package/build/compiler/docs.js +78 -21
  46. package/build/compiler/generate.js +5 -4
  47. package/build/compiler/index.d.ts +3 -2
  48. package/build/compiler/index.js +2 -2
  49. package/build/compiler/plugin.js +228 -0
  50. package/build/compiler/prerender.d.ts +1 -0
  51. package/build/compiler/prerender.js +1 -1
  52. package/build/compiler/seo.d.ts +1 -1
  53. package/build/compiler/seo.js +20 -5
  54. package/build/compiler/ssg.js +39 -2
  55. package/build/compiler/vite.js +25 -0
  56. package/build/io/.tsbuildinfo +1 -1
  57. package/build/io/codec.d.ts +54 -0
  58. package/build/io/codec.js +143 -0
  59. package/build/io/index.d.ts +1 -2
  60. package/build/io/index.js +1 -2
  61. package/build/logger/.tsbuildinfo +1 -1
  62. package/build/shared/.tsbuildinfo +1 -1
  63. package/eslint.config.js +48 -48
  64. package/examples/basic/client/404.tsx +11 -11
  65. package/examples/basic/client/components/.gitkeep +1 -1
  66. package/examples/basic/client/global-error.tsx +13 -13
  67. package/examples/basic/client/layout.tsx +25 -25
  68. package/examples/basic/client/public/images/.gitkeep +1 -1
  69. package/examples/basic/client/public/images/logo.svg +36 -36
  70. package/examples/basic/client/public/robots.txt +2 -2
  71. package/examples/basic/client/routes/docs/[...slug].tsx +12 -12
  72. package/examples/basic/client/routes/features/error/error.tsx +16 -16
  73. package/examples/basic/client/routes/features/index.tsx +1 -1
  74. package/examples/basic/client/routes/features/template/b.tsx +14 -14
  75. package/examples/basic/client/routes/files/[[...slug]].tsx +21 -21
  76. package/examples/basic/client/routes/gallery/layout.tsx +13 -13
  77. package/examples/basic/client/routes/io.tsx +23 -24
  78. package/examples/basic/client/routes/loader-demo/loading.tsx +13 -13
  79. package/examples/basic/client/routes/rest.tsx +74 -0
  80. package/examples/basic/client/routes/rpc.tsx +43 -0
  81. package/examples/basic/client/routes/search.tsx +61 -61
  82. package/examples/basic/client/toil.tsx +5 -5
  83. package/package.json +167 -148
  84. package/presets/eslint.js +88 -88
  85. package/presets/no-uint8array-tostring.js +200 -200
  86. package/presets/prettier-plugin.js +51 -0
  87. package/presets/prettier.json +19 -18
  88. package/presets/tsconfig.json +37 -37
  89. package/server/runtime/README.md +97 -0
  90. package/server/runtime/abort/abort.ts +27 -0
  91. package/server/runtime/env/Server.ts +61 -0
  92. package/server/runtime/envelope.ts +191 -0
  93. package/server/runtime/exports/index.ts +52 -0
  94. package/server/runtime/handlers/ToilHandler.ts +34 -0
  95. package/server/runtime/index.ts +26 -0
  96. package/server/runtime/lang/Potential.ts +5 -0
  97. package/server/runtime/memory.ts +81 -0
  98. package/server/runtime/request.ts +55 -0
  99. package/server/runtime/response.ts +86 -0
  100. package/server/runtime/rest/Rest.ts +39 -0
  101. package/server/runtime/rest/RestHandler.ts +20 -0
  102. package/server/runtime/rest/RouteContext.ts +82 -0
  103. package/server/runtime/rest/match.ts +48 -0
  104. package/server/runtime/tsconfig.json +7 -0
  105. package/src/backend/index.ts +202 -160
  106. package/src/cli/create.ts +15 -5
  107. package/src/cli/diagnostics.ts +81 -0
  108. package/src/cli/doctor.ts +384 -7
  109. package/src/cli/index.ts +11 -2
  110. package/src/cli/proc.ts +50 -50
  111. package/src/cli/updates.ts +69 -69
  112. package/src/cli/validate.ts +31 -31
  113. package/src/client/channel/channel.ts +146 -146
  114. package/src/client/components/Form.tsx +65 -65
  115. package/src/client/components/Script.tsx +113 -113
  116. package/src/client/components/Slot.tsx +21 -21
  117. package/src/client/dev/devtools.tsx +1018 -0
  118. package/src/client/dev/error-overlay.tsx +30 -4
  119. package/src/client/errors.ts +11 -0
  120. package/src/client/head/head.ts +167 -167
  121. package/src/client/head/metadata.ts +112 -112
  122. package/src/client/index.ts +91 -89
  123. package/src/client/navigation/NavLink.tsx +86 -86
  124. package/src/client/navigation/navigation.ts +235 -235
  125. package/src/client/navigation/prefetch.ts +169 -130
  126. package/src/client/navigation/scroll.ts +53 -53
  127. package/src/client/routing/Router.tsx +8 -2
  128. package/src/client/routing/action.ts +122 -122
  129. package/src/client/routing/error-boundary.tsx +43 -43
  130. package/src/client/routing/hooks.ts +21 -6
  131. package/src/client/routing/loader.ts +325 -235
  132. package/src/client/routing/match.ts +47 -47
  133. package/src/client/routing/mount.tsx +54 -52
  134. package/src/client/routing/params-context.ts +10 -10
  135. package/src/client/routing/slot-context.ts +7 -7
  136. package/src/client/rpc.ts +64 -0
  137. package/src/client/search/search.ts +189 -189
  138. package/src/client/search/use-page-search.ts +73 -73
  139. package/src/client/types.ts +73 -73
  140. package/src/compiler/config.ts +221 -182
  141. package/src/compiler/docs.ts +285 -228
  142. package/src/compiler/generate.ts +395 -394
  143. package/src/compiler/index.ts +66 -57
  144. package/src/compiler/pages.ts +70 -70
  145. package/src/compiler/plugin.ts +258 -2
  146. package/src/compiler/prerender.ts +156 -156
  147. package/src/compiler/seo.ts +417 -390
  148. package/src/compiler/ssg.ts +171 -126
  149. package/src/compiler/vite.ts +34 -0
  150. package/src/io/FastMap.ts +151 -127
  151. package/src/io/FastSet.ts +15 -1
  152. package/src/io/codec.ts +217 -0
  153. package/src/io/index.ts +10 -11
  154. package/src/io/lengths.ts +14 -14
  155. package/src/io/types.ts +19 -18
  156. package/src/logger/index.ts +22 -22
  157. package/src/shared/index.ts +10 -10
  158. package/std/client/index.d.ts +15 -15
  159. package/std/client/package.json +3 -3
  160. package/test/assembly/example.spec.ts +17 -7
  161. package/test/channel.test.ts +21 -21
  162. package/test/doctor.test.ts +65 -0
  163. package/test/dom/Link.test.tsx +47 -47
  164. package/test/dom/NavLink.test.tsx +37 -37
  165. package/test/dom/error-overlay.test.tsx +44 -44
  166. package/test/dom/loader.test.tsx +121 -121
  167. package/test/dom/navigation.test.ts +59 -59
  168. package/test/dom/revalidate.test.tsx +38 -38
  169. package/test/dom/route-head.test.tsx +78 -78
  170. package/test/dom/router-loading.test.tsx +44 -44
  171. package/test/dom/scroll.test.ts +56 -56
  172. package/test/dom/use-metadata.test.tsx +58 -58
  173. package/test/errors.test.ts +21 -0
  174. package/test/io.test.ts +117 -93
  175. package/test/navlink.test.ts +28 -28
  176. package/test/placeholder.test.ts +9 -9
  177. package/test/prettier-plugin.test.ts +46 -0
  178. package/test/routes.test.ts +76 -76
  179. package/test/rpc.test.ts +50 -0
  180. package/test/seo.test.ts +175 -164
  181. package/test/slot-layouts.test.ts +69 -69
  182. package/test/ssg.test.ts +36 -36
  183. package/test/update.test.ts +44 -44
  184. package/test/validate.test.ts +42 -42
  185. package/tests/data-parity/generated-parity.ts +99 -0
  186. package/tests/data-parity/parity.ts +80 -0
  187. package/tests/data-parity/spec.ts +46 -0
  188. package/toil-routes.d.ts +7 -0
  189. package/tsconfig.backend.json +13 -13
  190. package/tsconfig.base.json +35 -35
  191. package/tsconfig.cli.json +13 -13
  192. package/tsconfig.client.json +14 -14
  193. package/tsconfig.compiler.json +13 -13
  194. package/tsconfig.io.json +12 -12
  195. package/tsconfig.json +22 -22
  196. package/tsconfig.logger.json +12 -12
  197. package/tsconfig.server.json +10 -10
  198. package/tsconfig.shared.json +12 -12
  199. package/vitest.config.ts +26 -26
  200. package/.idea/codeStyles/Project.xml +0 -54
  201. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  202. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  203. package/.idea/modules.xml +0 -8
  204. package/.idea/prettier.xml +0 -7
  205. package/.idea/toiljs.iml +0 -8
  206. package/.idea/vcs.xml +0 -6
  207. package/.toil/entry.tsx +0 -9
  208. package/.toil/index.html +0 -12
  209. package/.toil/routes.ts +0 -9
  210. package/build/cli/configure.d.ts +0 -16
  211. package/build/cli/configure.js +0 -272
  212. package/build/cli/create.d.ts +0 -16
  213. package/build/cli/create.js +0 -420
  214. package/build/cli/diagnostics.d.ts +0 -55
  215. package/build/cli/diagnostics.js +0 -333
  216. package/build/cli/doctor.d.ts +0 -6
  217. package/build/cli/doctor.js +0 -249
  218. package/build/cli/features.d.ts +0 -25
  219. package/build/cli/features.js +0 -107
  220. package/build/cli/index.d.ts +0 -2
  221. package/build/cli/proc.d.ts +0 -6
  222. package/build/cli/proc.js +0 -31
  223. package/build/cli/ui.d.ts +0 -9
  224. package/build/cli/ui.js +0 -75
  225. package/build/cli/update.d.ts +0 -7
  226. package/build/cli/update.js +0 -117
  227. package/build/cli/updates.d.ts +0 -10
  228. package/build/cli/updates.js +0 -45
  229. package/build/cli/validate.d.ts +0 -4
  230. package/build/cli/validate.js +0 -19
  231. package/build/client/Link.d.ts +0 -8
  232. package/build/client/Link.js +0 -44
  233. package/build/client/NavLink.d.ts +0 -14
  234. package/build/client/NavLink.js +0 -37
  235. package/build/client/Router.d.ts +0 -7
  236. package/build/client/Router.js +0 -55
  237. package/build/client/channel.d.ts +0 -23
  238. package/build/client/channel.js +0 -94
  239. package/build/client/error-boundary.d.ts +0 -16
  240. package/build/client/error-boundary.js +0 -19
  241. package/build/client/head.d.ts +0 -26
  242. package/build/client/head.js +0 -87
  243. package/build/client/hooks.d.ts +0 -17
  244. package/build/client/hooks.js +0 -48
  245. package/build/client/lazy.d.ts +0 -16
  246. package/build/client/lazy.js +0 -53
  247. package/build/client/match.d.ts +0 -2
  248. package/build/client/match.js +0 -32
  249. package/build/client/mount.d.ts +0 -2
  250. package/build/client/mount.js +0 -13
  251. package/build/client/navigation.d.ts +0 -13
  252. package/build/client/navigation.js +0 -97
  253. package/build/client/params-context.d.ts +0 -2
  254. package/build/client/params-context.js +0 -2
  255. package/build/client/prefetch.d.ts +0 -11
  256. package/build/client/prefetch.js +0 -100
  257. package/build/client/runtime.d.ts +0 -31
  258. package/build/client/runtime.js +0 -112
  259. package/build/client/scroll.d.ts +0 -8
  260. package/build/client/scroll.js +0 -36
  261. package/build/io/BinaryReader.d.ts +0 -44
  262. package/build/io/BinaryReader.js +0 -244
  263. package/build/io/BinaryWriter.d.ts +0 -44
  264. package/build/io/BinaryWriter.js +0 -297
  265. package/build/server/release.wasm +0 -0
  266. package/build/server/release.wat +0 -9
  267. package/src/io/BinaryReader.ts +0 -340
  268. package/src/io/BinaryWriter.ts +0 -385
  269. package/src/server/index.ts +0 -10
  270. package/src/server/main.ts +0 -13
  271. package/src/server/tsconfig.json +0 -4
  272. package/toil-env.d.ts +0 -16
  273. package/toilconfig.json +0 -30
@@ -1,333 +0,0 @@
1
- function parseVersion(v) {
2
- const m = /(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(v);
3
- if (!m)
4
- return [0, 0, 0];
5
- return [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)];
6
- }
7
- export function satisfiesMin(version, range) {
8
- const [a, b, c] = parseVersion(version);
9
- const [x, y, z] = parseVersion(range);
10
- if (a !== x)
11
- return a > x;
12
- if (b !== y)
13
- return b > y;
14
- return c >= z;
15
- }
16
- export function checkNode(current, requiredRange) {
17
- const ok = satisfiesMin(current, requiredRange);
18
- return {
19
- id: 'node',
20
- label: 'Node.js',
21
- status: ok ? 'pass' : 'fail',
22
- detail: `${current} (requires ${requiredRange})`,
23
- fix: ok ? undefined : `Upgrade Node to ${requiredRange}.`,
24
- };
25
- }
26
- export function checkPeer(name, installed, range) {
27
- if (installed === null) {
28
- return {
29
- id: `peer:${name}`,
30
- label: name,
31
- status: 'fail',
32
- detail: `not installed (requires ${range})`,
33
- fix: `Install ${name}@"${range}".`,
34
- };
35
- }
36
- const ok = satisfiesMin(installed, range);
37
- return {
38
- id: `peer:${name}`,
39
- label: name,
40
- status: ok ? 'pass' : 'warn',
41
- detail: `${installed} (requires ${range})`,
42
- fix: ok ? undefined : `Update ${name} to ${range}.`,
43
- };
44
- }
45
- export function checkPackageManager(lockfiles) {
46
- if (lockfiles.length === 0) {
47
- return {
48
- id: 'pm',
49
- label: 'Package manager',
50
- status: 'warn',
51
- detail: 'no lockfile found',
52
- fix: 'Run an install (npm/pnpm/yarn/bun) to create a lockfile.',
53
- };
54
- }
55
- return { id: 'pm', label: 'Package manager', status: 'pass', detail: lockfiles.join(', ') };
56
- }
57
- export function checkToiljsInstalled(version) {
58
- return version
59
- ? { id: 'toiljs', label: 'toiljs', status: 'pass', detail: version }
60
- : {
61
- id: 'toiljs',
62
- label: 'toiljs',
63
- status: 'fail',
64
- detail: 'not a dependency of this project',
65
- fix: 'Add toiljs to dependencies, or run from the project root with --root.',
66
- };
67
- }
68
- export function checkDir(id, label, exists, fix) {
69
- return exists
70
- ? { id, label, status: 'pass' }
71
- : { id, label, status: 'fail', detail: 'missing', fix };
72
- }
73
- export function checkMountSlots(entrySource) {
74
- const label = 'App entry mount()';
75
- if (entrySource === null) {
76
- return {
77
- id: 'mount',
78
- label,
79
- status: 'warn',
80
- detail: 'entry file not found',
81
- fix: 'Ensure client/toil.tsx calls Toil.mount(...).',
82
- };
83
- }
84
- const call = /\bmount\s*\(([^)]*)\)/.exec(entrySource);
85
- if (!call) {
86
- return {
87
- id: 'mount',
88
- label,
89
- status: 'warn',
90
- detail: 'no mount() call found',
91
- fix: 'Call Toil.mount(routes, layout, notFound, globalError, slots).',
92
- };
93
- }
94
- const hasSlots = /\bslots\b/.test(call[1]);
95
- return hasSlots
96
- ? { id: 'mount', label, status: 'pass', detail: 'passes slots' }
97
- : {
98
- id: 'mount',
99
- label,
100
- status: 'warn',
101
- detail: 'mount() is missing the slots argument',
102
- fix: 'Pass slots last: mount(routes, layout, notFound, globalError, slots). Without it, parallel and intercepting routes are ignored.',
103
- };
104
- }
105
- export function checkRootElement(indexHtml) {
106
- const label = 'index.html mount target';
107
- if (indexHtml === null) {
108
- return {
109
- id: 'root-el',
110
- label,
111
- status: 'fail',
112
- detail: 'index.html not found',
113
- fix: 'Add public/index.html with <div id="root"></div>.',
114
- };
115
- }
116
- const ok = /id\s*=\s*["']root["']/.test(indexHtml);
117
- return ok
118
- ? { id: 'root-el', label, status: 'pass' }
119
- : {
120
- id: 'root-el',
121
- label,
122
- status: 'fail',
123
- detail: 'no element with id="root"',
124
- fix: 'Add <div id="root"></div> to public/index.html.',
125
- };
126
- }
127
- export function checkRoutesPresent(routeCount) {
128
- return routeCount > 0
129
- ? {
130
- id: 'routes',
131
- label: 'Routes',
132
- status: 'pass',
133
- detail: `${routeCount} route${routeCount === 1 ? '' : 's'}`,
134
- }
135
- : {
136
- id: 'routes',
137
- label: 'Routes',
138
- status: 'fail',
139
- detail: 'no routes found',
140
- fix: 'Add a page, e.g. client/routes/index.tsx.',
141
- };
142
- }
143
- export function checkDuplicatePatterns(patterns) {
144
- const seen = new Set();
145
- const dupes = new Set();
146
- for (const p of patterns) {
147
- if (seen.has(p))
148
- dupes.add(p);
149
- else
150
- seen.add(p);
151
- }
152
- return dupes.size === 0
153
- ? { id: 'route-dupes', label: 'Unique route patterns', status: 'pass' }
154
- : {
155
- id: 'route-dupes',
156
- label: 'Unique route patterns',
157
- status: 'warn',
158
- detail: `duplicate: ${[...dupes].join(', ')}`,
159
- fix: 'Two route files map to the same URL; rename or remove one.',
160
- };
161
- }
162
- function isBrokenRelativeAsset(value) {
163
- if (value === '')
164
- return false;
165
- if (value.startsWith('/'))
166
- return false;
167
- if (/^[a-z][a-z0-9+.-]*:/i.test(value))
168
- return false;
169
- if (value.startsWith('#') || value.startsWith('?'))
170
- return false;
171
- return /\.(svgz?|png|jpe?g|gif|webp|avif|ico|css|m?js|woff2?|ttf|otf|eot|mp4|webm|json)$/i.test(value);
172
- }
173
- export function findRelativeAssets(files) {
174
- const issues = [];
175
- const attr = /\b(?:src|href)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
176
- for (const file of files) {
177
- const lines = file.source.split('\n');
178
- for (let i = 0; i < lines.length; i++) {
179
- attr.lastIndex = 0;
180
- let m;
181
- while ((m = attr.exec(lines[i])) !== null) {
182
- const value = m[1] ?? m[2] ?? '';
183
- if (isBrokenRelativeAsset(value)) {
184
- issues.push({ file: file.path, line: i + 1, value });
185
- }
186
- }
187
- }
188
- }
189
- return issues;
190
- }
191
- export function checkRelativeAssets(issues) {
192
- if (issues.length === 0)
193
- return { id: 'rel-assets', label: 'Asset paths', status: 'pass' };
194
- const shown = issues
195
- .slice(0, 5)
196
- .map((i) => `${i.file}:${String(i.line)} "${i.value}"`)
197
- .join('; ');
198
- const more = issues.length > 5 ? `, and ${String(issues.length - 5)} more` : '';
199
- return {
200
- id: 'rel-assets',
201
- label: 'Asset paths',
202
- status: 'warn',
203
- detail: `${String(issues.length)} relative reference(s): ${shown}${more}`,
204
- fix: 'Use a root-absolute path (e.g. "/images/logo.svg") or import the asset; relative paths 404 on nested routes.',
205
- };
206
- }
207
- export function checkConfigLoads(loaded, error) {
208
- return loaded
209
- ? { id: 'config', label: 'toil.config loads', status: 'pass' }
210
- : {
211
- id: 'config',
212
- label: 'toil.config loads',
213
- status: 'fail',
214
- detail: error ?? 'failed to load',
215
- fix: 'Fix the error in your toil.config.* so dev/build can read it.',
216
- };
217
- }
218
- export function checkBasePath(base) {
219
- const ok = base === '/' || (base.startsWith('/') && base.endsWith('/'));
220
- return ok
221
- ? { id: 'base', label: 'Base path', status: 'pass', detail: base }
222
- : {
223
- id: 'base',
224
- label: 'Base path',
225
- status: 'warn',
226
- detail: `"${base}"`,
227
- fix: 'A non-root base should start and end with "/" (e.g. "/app/").',
228
- };
229
- }
230
- export function checkSeoUrl(seoConfigured, hasUrl) {
231
- if (!seoConfigured) {
232
- return {
233
- id: 'seo-url',
234
- label: 'SEO site url',
235
- status: 'pass',
236
- detail: 'SEO not configured',
237
- };
238
- }
239
- return hasUrl
240
- ? { id: 'seo-url', label: 'SEO site url', status: 'pass' }
241
- : {
242
- id: 'seo-url',
243
- label: 'SEO site url',
244
- status: 'warn',
245
- detail: 'seo is set without a url',
246
- fix: 'Set client.seo.url so sitemap.xml and canonical links are absolute.',
247
- };
248
- }
249
- export function checkStyling(f) {
250
- const label = 'Styling';
251
- if (f.preprocessorImported && f.preprocessorImported !== 'css' && !f.preprocessorInstalled) {
252
- return {
253
- id: 'styling',
254
- label,
255
- status: 'fail',
256
- detail: `${f.preprocessorImported} stylesheet imported but ${f.preprocessorImported} is not installed`,
257
- fix: `Install ${f.preprocessorImported}, or run toiljs configure.`,
258
- };
259
- }
260
- if (f.tailwindImported && !f.tailwindInstalled) {
261
- return {
262
- id: 'styling',
263
- label,
264
- status: 'fail',
265
- detail: 'Tailwind entry imported but @tailwindcss/vite is not installed',
266
- fix: 'Run toiljs configure --tailwind, or install @tailwindcss/vite.',
267
- };
268
- }
269
- return { id: 'styling', label, status: 'pass' };
270
- }
271
- export function checkToilconfig(present) {
272
- return present
273
- ? { id: 'toilconfig', label: 'Server target (toilconfig.json)', status: 'pass' }
274
- : {
275
- id: 'toilconfig',
276
- label: 'Server target (toilconfig.json)',
277
- status: 'warn',
278
- detail: 'no toilconfig.json (no WebAssembly server)',
279
- fix: 'Add toilconfig.json + a server/ entry if you want a WASM backend.',
280
- };
281
- }
282
- export function checkServerEntry(missing) {
283
- return missing.length === 0
284
- ? { id: 'server-entry', label: 'Server entries', status: 'pass' }
285
- : {
286
- id: 'server-entry',
287
- label: 'Server entries',
288
- status: 'fail',
289
- detail: `missing: ${missing.join(', ')}`,
290
- fix: 'Create the entry file(s) listed in toilconfig.json "entries", or update them.',
291
- };
292
- }
293
- export function checkToilscriptInstalled(installed) {
294
- return installed
295
- ? { id: 'toilscript', label: 'toilscript compiler', status: 'pass' }
296
- : {
297
- id: 'toilscript',
298
- label: 'toilscript compiler',
299
- status: 'warn',
300
- detail: 'toilconfig.json present but toilscript is not installed',
301
- fix: 'Install toilscript to compile the server to WebAssembly.',
302
- };
303
- }
304
- export function checkWasmBuilt(exists) {
305
- return exists
306
- ? { id: 'wasm', label: 'Server build', status: 'pass' }
307
- : {
308
- id: 'wasm',
309
- label: 'Server build',
310
- status: 'warn',
311
- detail: 'no compiled .wasm found',
312
- fix: 'Run your server build (toilscript) before toiljs start.',
313
- };
314
- }
315
- export function summarize(groups) {
316
- let pass = 0;
317
- let warn = 0;
318
- let fail = 0;
319
- for (const group of groups) {
320
- for (const check of group.checks) {
321
- if (check.status === 'pass')
322
- pass++;
323
- else if (check.status === 'warn')
324
- warn++;
325
- else
326
- fail++;
327
- }
328
- }
329
- return { pass, warn, fail };
330
- }
331
- export function hasFailures(summary) {
332
- return summary.fail > 0;
333
- }
@@ -1,6 +0,0 @@
1
- export interface DoctorOptions {
2
- readonly root?: string;
3
- readonly cwd: string;
4
- readonly json?: boolean;
5
- }
6
- export declare function runDoctor(opts: DoctorOptions): Promise<void>;
@@ -1,249 +0,0 @@
1
- import fs from 'node:fs';
2
- import { createRequire } from 'node:module';
3
- import path from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import { loadConfig, scanRoutes } from 'toiljs/compiler';
6
- import { checkBasePath, checkConfigLoads, checkDir, checkDuplicatePatterns, checkMountSlots, checkNode, checkPackageManager, checkPeer, checkRelativeAssets, checkRootElement, checkRoutesPresent, checkSeoUrl, checkServerEntry, checkStyling, checkToilconfig, checkToiljsInstalled, checkToilscriptInstalled, checkWasmBuilt, findRelativeAssets, hasFailures, summarize, } from './diagnostics.js';
7
- import { detectTailwind, PREPROCESSOR_PKG, preprocessorForExt, TAILWIND_ENTRY, } from './features.js';
8
- import { accent, bold, danger, dim, success, version, warn } from './ui.js';
9
- function readJsonObject(file) {
10
- try {
11
- const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
12
- return typeof parsed === 'object' && parsed !== null
13
- ? parsed
14
- : null;
15
- }
16
- catch {
17
- return null;
18
- }
19
- }
20
- function stringRecord(value) {
21
- if (typeof value !== 'object' || value === null)
22
- return {};
23
- const out = {};
24
- for (const [k, v] of Object.entries(value))
25
- if (typeof v === 'string')
26
- out[k] = v;
27
- return out;
28
- }
29
- function readFile(file) {
30
- try {
31
- return fs.readFileSync(file, 'utf8');
32
- }
33
- catch {
34
- return null;
35
- }
36
- }
37
- function frameworkMeta() {
38
- const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
39
- const pkg = readJsonObject(pkgPath);
40
- const engines = pkg ? stringRecord(pkg.engines) : {};
41
- const peers = pkg ? stringRecord(pkg.peerDependencies) : {};
42
- return { node: engines.node ?? '>=24.0.0', peers };
43
- }
44
- const LOCKFILES = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb'];
45
- function readEntry(clientAbsDir) {
46
- for (const name of ['toil.tsx', 'toil.jsx', 'main.tsx', 'main.jsx']) {
47
- const source = readFile(path.join(clientAbsDir, name));
48
- if (source !== null && /toiljs\/routes|\bmount\s*\(/.test(source))
49
- return source;
50
- }
51
- return null;
52
- }
53
- function collectSources(root, dir, cap) {
54
- const out = [];
55
- const visit = (current) => {
56
- if (out.length >= cap)
57
- return;
58
- let entries;
59
- try {
60
- entries = fs.readdirSync(current, { withFileTypes: true });
61
- }
62
- catch {
63
- return;
64
- }
65
- for (const entry of entries) {
66
- if (out.length >= cap)
67
- break;
68
- const full = path.join(current, entry.name);
69
- if (entry.isDirectory()) {
70
- if (entry.name !== 'node_modules')
71
- visit(full);
72
- }
73
- else if (/\.(tsx|jsx)$/.test(entry.name)) {
74
- const source = readFile(full);
75
- if (source !== null)
76
- out.push({ path: path.relative(root, full), source });
77
- }
78
- }
79
- };
80
- visit(dir);
81
- return out;
82
- }
83
- function glyph(status) {
84
- if (status === 'pass')
85
- return success('✓');
86
- if (status === 'warn')
87
- return warn('⚠');
88
- return danger('✗');
89
- }
90
- function renderHuman(groups) {
91
- const summary = summarize(groups);
92
- const out = [];
93
- for (const group of groups) {
94
- out.push(' ' + bold(group.title));
95
- for (const check of group.checks) {
96
- let line = ` ${glyph(check.status)} ${check.label}`;
97
- if (check.detail)
98
- line += dim(` ${check.detail}`);
99
- out.push(line);
100
- if (check.fix && check.status !== 'pass')
101
- out.push(' ' + dim(`fix: ${check.fix}`));
102
- }
103
- out.push('');
104
- }
105
- const parts = [success(`${String(summary.pass)} passed`)];
106
- if (summary.warn > 0) {
107
- parts.push(warn(`${String(summary.warn)} warning${summary.warn === 1 ? '' : 's'}`));
108
- }
109
- if (summary.fail > 0)
110
- parts.push(danger(`${String(summary.fail)} failed`));
111
- out.push(' ' + parts.join(dim(', ')));
112
- out.push('');
113
- process.stdout.write(out.join('\n') + '\n');
114
- }
115
- export async function runDoctor(opts) {
116
- const root = path.resolve(opts.root ?? opts.cwd);
117
- const meta = frameworkMeta();
118
- const projectPkg = readJsonObject(path.join(root, 'package.json'));
119
- const deps = {
120
- ...(projectPkg ? stringRecord(projectPkg.dependencies) : {}),
121
- ...(projectPkg ? stringRecord(projectPkg.devDependencies) : {}),
122
- };
123
- let cfg = null;
124
- let configError;
125
- try {
126
- cfg = await loadConfig({ root });
127
- }
128
- catch (err) {
129
- configError = err instanceof Error ? err.message : String(err);
130
- }
131
- const clientAbsDir = cfg ? cfg.clientAbsDir : path.join(root, 'client');
132
- const routesAbsDir = cfg ? cfg.routesAbsDir : path.join(clientAbsDir, 'routes');
133
- const publicDir = cfg ? cfg.publicDir : path.join(clientAbsDir, 'public');
134
- const entrySource = readEntry(clientAbsDir);
135
- const indexHtml = readFile(path.join(publicDir, 'index.html'));
136
- const routes = scanRoutes(routesAbsDir);
137
- const mainPatterns = routes.filter((r) => r.slot === undefined).map((r) => r.pattern);
138
- const assetIssues = findRelativeAssets(collectSources(root, clientAbsDir, 200));
139
- let preprocessorImported = null;
140
- let tailwindImported = false;
141
- if (entrySource) {
142
- const styleImport = /import\s+['"]\.\/styles\/main\.([a-z]+)['"]/.exec(entrySource);
143
- if (styleImport)
144
- preprocessorImported = preprocessorForExt(styleImport[1]);
145
- tailwindImported = entrySource.includes(TAILWIND_ENTRY);
146
- }
147
- const ppPkg = preprocessorImported ? PREPROCESSOR_PKG[preprocessorImported] : null;
148
- const preprocessorInstalled = ppPkg === null || ppPkg in deps;
149
- const toilconfig = readJsonObject(path.join(root, 'toilconfig.json'));
150
- const serverPresent = toilconfig !== null;
151
- let missingEntries = [];
152
- let toilscriptInstalled = false;
153
- let wasmExists = false;
154
- if (toilconfig) {
155
- const entries = Array.isArray(toilconfig.entries)
156
- ? toilconfig.entries.filter((e) => typeof e === 'string')
157
- : [];
158
- missingEntries = entries.filter((e) => !fs.existsSync(path.join(root, e)));
159
- try {
160
- createRequire(path.join(root, 'package.json')).resolve('toilscript');
161
- toilscriptInstalled = true;
162
- }
163
- catch {
164
- toilscriptInstalled = false;
165
- }
166
- const targets = typeof toilconfig.targets === 'object' && toilconfig.targets !== null
167
- ? toilconfig.targets
168
- : {};
169
- const outFiles = [];
170
- for (const target of Object.values(targets)) {
171
- if (typeof target === 'object' && target !== null) {
172
- const outFile = target.outFile;
173
- if (typeof outFile === 'string')
174
- outFiles.push(outFile);
175
- }
176
- }
177
- wasmExists = outFiles.some((f) => fs.existsSync(path.join(root, f)));
178
- if (!wasmExists && outFiles.length === 0) {
179
- try {
180
- wasmExists = fs
181
- .readdirSync(path.join(root, 'build', 'server'))
182
- .some((f) => f.endsWith('.wasm'));
183
- }
184
- catch {
185
- wasmExists = false;
186
- }
187
- }
188
- }
189
- const peerName = (n) => checkPeer(n, deps[n] ?? null, meta.peers[n] ?? '*');
190
- const peerChecks = Object.keys(meta.peers).map(peerName);
191
- const groups = [
192
- {
193
- title: 'Environment',
194
- checks: [
195
- checkNode(process.versions.node, meta.node),
196
- checkToiljsInstalled('toiljs' in deps ? version() : null),
197
- ...peerChecks,
198
- checkPackageManager(LOCKFILES.filter((f) => fs.existsSync(path.join(root, f)))),
199
- ],
200
- },
201
- {
202
- title: 'Project + routing',
203
- checks: [
204
- checkDir('client-dir', 'client/ directory', fs.existsSync(clientAbsDir), 'Create a client/ directory for your app.'),
205
- checkDir('routes-dir', 'routes/ directory', fs.existsSync(routesAbsDir), 'Create client/routes/ and add an index.tsx.'),
206
- checkRootElement(indexHtml),
207
- checkMountSlots(entrySource),
208
- checkRoutesPresent(routes.length),
209
- checkDuplicatePatterns(mainPatterns),
210
- checkRelativeAssets(assetIssues),
211
- ],
212
- },
213
- {
214
- title: 'Config + assets',
215
- checks: [
216
- checkConfigLoads(cfg !== null, configError),
217
- checkBasePath(cfg ? cfg.base : '/'),
218
- checkSeoUrl(cfg?.seo != null, cfg?.seo?.url != null),
219
- checkStyling({
220
- preprocessorImported,
221
- preprocessorInstalled,
222
- tailwindImported,
223
- tailwindInstalled: detectTailwind(deps),
224
- }),
225
- ],
226
- },
227
- {
228
- title: 'Server / WASM',
229
- checks: serverPresent
230
- ? [
231
- checkToilconfig(true),
232
- checkServerEntry(missingEntries),
233
- checkToilscriptInstalled(toilscriptInstalled),
234
- checkWasmBuilt(wasmExists),
235
- ]
236
- : [checkToilconfig(false)],
237
- },
238
- ];
239
- const summary = summarize(groups);
240
- if (opts.json) {
241
- process.stdout.write(JSON.stringify({ groups, summary }, null, 2) + '\n');
242
- }
243
- else {
244
- process.stdout.write('\n' + accent(' Doctor') + dim(` ${root}`) + '\n\n');
245
- renderHuman(groups);
246
- }
247
- if (hasFailures(summary))
248
- process.exitCode = 1;
249
- }
@@ -1,25 +0,0 @@
1
- export type Preprocessor = 'css' | 'sass' | 'less' | 'stylus';
2
- export interface StyleFeatures {
3
- readonly preprocessor: Preprocessor;
4
- readonly tailwind: boolean;
5
- }
6
- export declare const PREPROCESSORS: readonly Preprocessor[];
7
- export declare const STYLE_EXT: Record<Preprocessor, string>;
8
- export declare const PREPROCESSOR_PKG: Record<Preprocessor, string | null>;
9
- export declare const TAILWIND_PKGS: readonly string[];
10
- export declare const PKG_VERSION: Record<string, string>;
11
- export declare const TAILWIND_ENTRY = "styles/tailwind.css";
12
- export declare const TAILWIND_CSS = "@import 'tailwindcss';\n";
13
- export declare function styleEntry(p: Preprocessor): string;
14
- export declare function preprocessorForExt(ext: string): Preprocessor | null;
15
- export declare function requiredPackages(f: StyleFeatures): string[];
16
- export declare function packageDiff(from: StyleFeatures, to: StyleFeatures): {
17
- add: string[];
18
- remove: string[];
19
- };
20
- export declare function styleImportLines(f: StyleFeatures): string[];
21
- export declare function setStyleImports(source: string, f: StyleFeatures): string;
22
- export declare function defaultConfigSource(images: boolean): string;
23
- export declare function setConfigImages(source: string, enabled: boolean): string | null;
24
- export declare function detectPreprocessor(deps: Record<string, string>): Preprocessor;
25
- export declare function detectTailwind(deps: Record<string, string>): boolean;