toiljs 0.0.113 → 0.0.115

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 (41) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/build/backend/.tsbuildinfo +1 -1
  3. package/build/cli/.tsbuildinfo +1 -1
  4. package/build/cli/index.js +198 -41
  5. package/build/client/.tsbuildinfo +1 -1
  6. package/build/compiler/.tsbuildinfo +1 -1
  7. package/build/compiler/index.js +1 -1
  8. package/build/compiler/pages.js +1 -13
  9. package/build/compiler/prerender.d.ts +1 -0
  10. package/build/compiler/prerender.js +39 -2
  11. package/build/compiler/toil-docs.generated.js +3 -3
  12. package/build/devserver/.tsbuildinfo +1 -1
  13. package/build/devserver/daemon/host.d.ts +2 -1
  14. package/build/devserver/daemon/host.js +12 -12
  15. package/build/devserver/daemon/index.js +3 -3
  16. package/build/io/.tsbuildinfo +1 -1
  17. package/build/logger/.tsbuildinfo +1 -1
  18. package/build/shared/.tsbuildinfo +1 -1
  19. package/docs/background/daemons.md +49 -3
  20. package/docs/cli/README.md +4 -2
  21. package/docs/getting-started/installation.md +18 -0
  22. package/package.json +15 -15
  23. package/src/cli/create.ts +171 -25
  24. package/src/cli/diagnostics.ts +74 -0
  25. package/src/cli/doctor.ts +105 -27
  26. package/src/cli/features.ts +7 -3
  27. package/src/cli/index.ts +2 -2
  28. package/src/cli/update.ts +30 -3
  29. package/src/cli/updates.ts +23 -0
  30. package/src/compiler/index.ts +11 -2
  31. package/src/compiler/pages.ts +1 -22
  32. package/src/compiler/prerender.ts +57 -3
  33. package/src/compiler/toil-docs.generated.ts +3 -3
  34. package/src/devserver/daemon/host.ts +29 -19
  35. package/src/devserver/daemon/index.ts +12 -5
  36. package/test/daemon-emulation.test.ts +44 -2
  37. package/test/doctor.test.ts +28 -0
  38. package/test/fixtures/daemon-app.ts +9 -4
  39. package/test/prerender.test.ts +64 -2
  40. package/test/update.test.ts +15 -1
  41. package/tsconfig.base.json +0 -1
@@ -50,6 +50,29 @@ export function parseNcuJson(stdout: string): Record<string, string> {
50
50
  }
51
51
  }
52
52
 
53
+ /**
54
+ * Majors `toiljs update` refuses to upgrade into, by package. TypeScript 7 is the native (Go) port:
55
+ * it ships `tsc` but no JavaScript compiler API, which toiljs's route-metadata extractor, the eslint
56
+ * preset, and typedoc all load. Upgrading into it silently stops SEO metadata being baked into the
57
+ * built HTML and hard-crashes typescript-eslint, so the upgrade is withheld until the ecosystem
58
+ * catches up. Bumps *within* the supported major are still offered.
59
+ */
60
+ export const UNSUPPORTED_MAJOR: Readonly<Record<string, number>> = { typescript: 7 };
61
+
62
+ /**
63
+ * The names ncu wants to upgrade into a major toiljs does not support (see {@link UNSUPPORTED_MAJOR}).
64
+ * These are dropped from the picker and passed to ncu's `--reject`, so `-u` cannot apply them either.
65
+ */
66
+ export function withheldUpgrades(upgraded: Record<string, string>): string[] {
67
+ return Object.entries(upgraded)
68
+ .filter(([name, to]) => {
69
+ const ceiling = UNSUPPORTED_MAJOR[name];
70
+ return ceiling !== undefined && parseVersion(to)[0] >= ceiling;
71
+ })
72
+ .map(([name]) => name)
73
+ .sort();
74
+ }
75
+
53
76
  const SEVERITY: Record<Bump, number> = { major: 0, minor: 1, patch: 2, other: 3 };
54
77
 
55
78
  /**
@@ -308,9 +308,18 @@ export async function buildServer(root: string, auth: boolean = false): Promise<
308
308
  return;
309
309
  }
310
310
 
311
- // Default request-only single-artifact path (no daemon/stream surface).
311
+ // Default request-only single-artifact path (no daemon/stream surface). Declare `hot` rather
312
+ // than leaving --targetMode off: with no explicit mode toilscript treats a source tree that
313
+ // declares no surface decorator as a plain AssemblyScript compile and omits `toil.surface`
314
+ // (to keep an ordinary AS build byte-identical). A toiljs server IS a Toil request artifact
315
+ // even before it grows its first `@rest`/`@data`, and the host refuses to load an artifact
316
+ // without that section, so a freshly scaffolded app would never deploy. `hot` is the mode the
317
+ // section already records for the default request artifact, so the bytes are unchanged for a
318
+ // project that has a surface; it only stops the section from going missing for one that
319
+ // doesn't. (`@daemon`/`@scheduled` are the only decorators `hot` rejects, and this branch runs
320
+ // exactly when the tree has neither.)
312
321
  await runToilscriptPass(root, binJs, files, {
313
- mode: null,
322
+ mode: 'hot',
314
323
  outFile: null,
315
324
  withRpc: true,
316
325
  authUser: authOn,
@@ -1,13 +1,6 @@
1
- import { createRequire } from 'node:module';
2
- import path from 'node:path';
3
-
4
- import type * as TS from 'typescript';
5
-
6
- import { extractStaticExports } from './prerender.js';
1
+ import { extractStaticExports, loadTypeScriptSync } from './prerender.js';
7
2
  import type { ScannedRoute } from './routes.js';
8
3
 
9
- type Ts = typeof TS;
10
-
11
4
  /**
12
5
  * A page in the build-time search index: its URL pattern, whether it's dynamic, and the
13
6
  * statically-extracted `metadata` literal (the searchable subset; dynamic `generateMetadata` and
@@ -20,20 +13,6 @@ export interface PageIndexEntry {
20
13
  readonly metadata: Record<string, unknown>;
21
14
  }
22
15
 
23
- /**
24
- * Loads the project's TypeScript synchronously (so {@link buildPageIndex} can run inside the sync
25
- * `generate()`), or `null` if it isn't installed, in which case pages are indexed by path only.
26
- */
27
- function loadTypeScriptSync(root: string): Ts | null {
28
- try {
29
- const require = createRequire(path.join(root, 'package.json'));
30
- const mod = require('typescript') as { default?: Ts } & Ts;
31
- return mod.default ?? mod;
32
- } catch {
33
- return null;
34
- }
35
- }
36
-
37
16
  /** True when a route pattern has dynamic (`:param` / `*catch-all`) segments. */
38
17
  function isDynamic(pattern: string): boolean {
39
18
  return /[:*]/.test(pattern);
@@ -12,15 +12,69 @@ import { injectSeoHtml, routeSeo } from './seo.js';
12
12
 
13
13
  type Ts = typeof TS;
14
14
 
15
- /** Loads the project's TypeScript (used to read each route's static `metadata`), or `null` if absent. */
15
+ /**
16
+ * True when `mod` exposes the classic TypeScript compiler API the static extractor drives.
17
+ *
18
+ * Resolving `typescript` is not enough to know it can parse: TypeScript 7 (the native port) moved
19
+ * the compiler API off the package's main entry, which now exports only `version`. That module is a
20
+ * perfectly good object, so a truthy check passes and the first `ts.ScriptTarget.Latest` then dies
21
+ * with `Cannot read properties of undefined (reading 'Latest')`. Probe the members we actually use.
22
+ */
23
+ function isCompilerApi(mod: unknown): mod is Ts {
24
+ const ts = mod as Partial<Ts> | null | undefined;
25
+ return (
26
+ typeof ts?.createSourceFile === 'function' &&
27
+ typeof ts.isVariableStatement === 'function' &&
28
+ typeof ts.ScriptTarget === 'object' &&
29
+ typeof ts.ScriptKind === 'object' &&
30
+ typeof ts.SyntaxKind === 'object'
31
+ );
32
+ }
33
+
34
+ /** Warn once per process: an unusable TypeScript silently costs baked metadata, so say so. */
35
+ let warnedUnusable = false;
36
+ function warnUnusableTypeScript(mod: unknown): void {
37
+ if (warnedUnusable) return;
38
+ warnedUnusable = true;
39
+ const version = (mod as { version?: string } | null)?.version;
40
+ process.stderr.write(
41
+ ` toil: typescript${version ? `@${version}` : ''} does not expose the compiler API ` +
42
+ `(TypeScript 7 moved it to 'typescript/unstable/*').\n` +
43
+ ` toil: route metadata will NOT be baked into the built HTML. Install typescript ^6 to restore it.\n`,
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Resolves the project's TypeScript, used to read each route's static `metadata`. Returns `null`
49
+ * when it isn't installed (callers then index pages by path only) *or* when the resolved version
50
+ * doesn't expose the compiler API, which is warned about rather than crashing the build.
51
+ */
16
52
  export async function loadTypeScript(root: string): Promise<Ts | null> {
53
+ let mod: unknown;
17
54
  try {
18
55
  const resolved = createRequire(path.join(root, 'package.json')).resolve('typescript');
19
- const mod = (await import(pathToFileURL(resolved).href)) as { default?: Ts } & Ts;
20
- return mod.default ?? mod;
56
+ const ns = (await import(pathToFileURL(resolved).href)) as { default?: Ts } & Ts;
57
+ mod = ns.default ?? ns;
58
+ } catch {
59
+ return null;
60
+ }
61
+ if (isCompilerApi(mod)) return mod;
62
+ warnUnusableTypeScript(mod);
63
+ return null;
64
+ }
65
+
66
+ /** The sync twin of {@link loadTypeScript}, for callers that can't await (e.g. `buildPageIndex`). */
67
+ export function loadTypeScriptSync(root: string): Ts | null {
68
+ let mod: unknown;
69
+ try {
70
+ mod = createRequire(path.join(root, 'package.json'))('typescript');
21
71
  } catch {
22
72
  return null;
23
73
  }
74
+ const ts = (mod as { default?: Ts })?.default ?? mod;
75
+ if (isCompilerApi(ts)) return ts;
76
+ warnUnusableTypeScript(ts);
77
+ return null;
24
78
  }
25
79
 
26
80
  /** Marks an AST node that isn't a static literal (so its value can't be baked at build). */