@bakery-framework/core 2.0.1 → 2.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bakery-framework/core",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Bakery framework core: handlers, router, config, session, caches, logger, compiler.",
5
5
  "keywords": [
6
6
  "bakery",
@@ -54,25 +54,41 @@ function buildPaths(): MapOf<string[]> {
54
54
  const PROJECT_DIR = fs.resolve(APP_DIR, '.cache/tsconfig')
55
55
 
56
56
  /**
57
- * The two projects core always generates.
57
+ * The three projects core always generates.
58
58
  *
59
- * The split is the whole point: only `server` carries `bun-types`, so `Bun.*`
60
- * in a file bound for the browser is a type error rather than a runtime one.
61
- * Before this existed, one config covered everything and `Bun.hash()` in a
62
- * client file typechecked clean and failed in the browser.
59
+ * The split is the whole point: `server` and `api` carry `bun-types` and
60
+ * `client` does not, so `Bun.*` in a file bound for the browser is a type
61
+ * error rather than a runtime one. Before this existed, one config covered
62
+ * everything and `Bun.hash()` in a client file typechecked clean and failed
63
+ * in the browser.
63
64
  *
64
- * The includes overlap the app's own tsconfig, and that is fine *because
65
- * nothing references these projects*: each is a standalone projection of one
66
- * concern, pointed at directly (`tsc -p .cache/tsconfig/client.json`), and
67
- * subtracting the app's include would gut them into projects that check
68
- * nothing. What must never come back is the `references` wiring that composed
69
- * them with the app project. See {@link syncTSConfigProjects}.
65
+ * **Between them they claim every app file once**, because the app's root
66
+ * tsconfig claims none: it reaches these projects through
67
+ * `tsconfig.bakery.json` (see {@link syncTSConfigProjects}), and an editor
68
+ * gives a file to the first project that claims it. A file nobody claims
69
+ * falls into an inferred project with default options and no `bun-types`,
70
+ * which is how every `api/` file in a real app reported "Cannot find name
71
+ * 'Bun'". So `server` is defined by what it is not: everything outside the
72
+ * serve root (config, schema, `orm/`, `scripts/`, `tests/`, migrations) plus
73
+ * every `.tsx` page, which is server-rendered. `api` is its own project only
74
+ * because a glob cannot say "`src/**` except `api/`" in either direction.
75
+ *
76
+ * Naming an `exclude` drops TypeScript's default one, so `node_modules` is
77
+ * named again, and so are both runtime directories, taken from their one
78
+ * writer (`Bakery.cacheDir`, `Bakery.dataDir`). Neither holds source and both
79
+ * hold `.ts` files: the cache holds compiled server modules, and the data
80
+ * directory holds the ORM's `schema.pre-migrate.*.ts` backups, each of which
81
+ * redeclares the schema registry. Claimed, three of those failed a real
82
+ * app's `tsc -b` with TS2717.
70
83
  *
71
84
  * Globs are app-relative here and rewritten to be relative to the generated
72
85
  * file, which sits two levels down.
73
86
  */
74
87
  export function coreProjects(): PluginTsProject[] {
75
88
  const root = Bakery.config.root ?? 'src'
89
+ const runtime = [Bakery.cacheDir, Bakery.dataDir].map(dir =>
90
+ fs.relative(APP_DIR, dir).replace(/\\/g, '/'),
91
+ )
76
92
 
77
93
  return [
78
94
  {
@@ -86,13 +102,14 @@ export function coreProjects(): PluginTsProject[] {
86
102
  jsxFactory: 'createElement',
87
103
  jsxFragmentFactory: 'Fragment',
88
104
  },
89
- include: [
90
- `${root}/**/api/**/*.ts`,
91
- `${root}/**/*.tsx`,
92
- 'server.config.ts',
93
- 'schema.ts',
94
- 'orm/**/*.ts',
95
- ],
105
+ include: ['**/*.ts', '**/*.tsx'],
106
+ exclude: [`${root}/**/*.ts`, 'node_modules', ...runtime],
107
+ },
108
+ {
109
+ name: 'api',
110
+ server: true,
111
+ extends: '@bakery-framework/core/tsconfig.server.json',
112
+ include: [`${root}/**/api/**/*.ts`],
96
113
  },
97
114
  {
98
115
  name: 'client',
@@ -365,9 +382,15 @@ function isGeneratedReference(entry: unknown): boolean {
365
382
  * `allowImportingTsExtensions`, so they are unbuildable by design. Making them
366
383
  * `composite` instead would trade the errors above for a `tsc -b` build-order
367
384
  * requirement no consumer runs, and TS6305 would still fire for any root file
368
- * importing into one while unbuilt. The only reference shape `tsc -p`
369
- * tolerates from a root that has files of its own is no reference at all, so
370
- * the projects stand alone now and this strips what previous releases wrote.
385
+ * importing into one while unbuilt.
386
+ *
387
+ * **A root that has files of its own tolerates no reference into them,
388
+ * direct or chained.** Routed through a `composite`, `files: []` middle
389
+ * config, TS6306 and TS6310 go away and TS6305 does not: measured on a real
390
+ * app whose root claimed `src/**`, 57 of them. What works is the other
391
+ * direction, a root that claims nothing and reaches the projects through
392
+ * `tsconfig.bakery.json` (see {@link wireRoot}). This strips the direct
393
+ * entries previous releases wrote, which is still a repair worth making.
371
394
  *
372
395
  * Everything the developer owns is preserved: only entries into
373
396
  * `.cache/tsconfig/` are removed, a real project reference (say `../shared`)
@@ -394,6 +417,224 @@ export function stripGeneratedReferences(
394
417
  return repaired
395
418
  }
396
419
 
420
+ /** The generated file the root config references, and how it is spelled there. */
421
+ const CHAIN_FILE = 'tsconfig.bakery.json'
422
+ const CHAIN_PATH = fs.resolve(APP_DIR, CHAIN_FILE)
423
+ const CHAIN_REF = `./${CHAIN_FILE}`
424
+
425
+ /**
426
+ * `tsconfig.bakery.json`: every generated project, listed for an editor.
427
+ *
428
+ * **The middle layer is what makes the chain legal.** The root references
429
+ * this file, and this file references `.cache/tsconfig/*`. A root referencing
430
+ * those projects directly gets TS6306 and TS6310 for pointing at
431
+ * non-composite, `noEmit` projects. A `composite` config with `files: []` in
432
+ * between is a valid target, and tsserver follows references through it.
433
+ * Measured on a real app with TypeScript 5.9.3: every `api/` file moved from
434
+ * an inferred project with no `bun-types` to the server-side project, and
435
+ * every other `.ts` file to `client`.
436
+ *
437
+ * The server owns this file. A plugin contributes a project through
438
+ * `tsconfig.project` and the generator lists it here, so a plugin never writes
439
+ * it, and a project that failed to write is not listed.
440
+ */
441
+ export function chainConfig(names: string[]): Record<string, unknown> {
442
+ return {
443
+ $comment:
444
+ 'GENERATED by Bakery on dev boot and by `bakery --types`. Edits are lost. tsconfig.json references this file, and it references each project in .cache/tsconfig/, which is how an editor gives a file its scope.',
445
+ compilerOptions: { composite: true },
446
+ files: [],
447
+ references: names.map(name => ({ path: `./.cache/tsconfig/${name}.json` })),
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Write the chain file when its content changes, and say whether it did.
453
+ *
454
+ * A boot that rewrites an unchanged file dirties git every time and trains
455
+ * people to ignore the diff. The answer also decides when the advice for a
456
+ * root that claims files is worth giving (see {@link syncTSConfigProjects}).
457
+ */
458
+ async function writeChain(names: string[]): Promise<boolean> {
459
+ const next = `${JSON.stringify(chainConfig(names), null, 2)}\n`
460
+ const current = fs.exists(CHAIN_PATH)
461
+ ? await Bun.file(CHAIN_PATH).text()
462
+ : null
463
+ if (current === next) return false
464
+ await Bun.write(CHAIN_PATH, next)
465
+ return true
466
+ }
467
+
468
+ /** Whether a root config already references the chain file, however spelled. */
469
+ export function referencesChain(root: Record<string, unknown>): boolean {
470
+ const refs = root.references
471
+ if (!Array.isArray(refs)) return false
472
+ return refs.some(entry => {
473
+ const path = (entry as { path?: unknown } | null)?.path
474
+ return (
475
+ typeof path === 'string' &&
476
+ path.replace(/\\/g, '/').replace(RE_LEADING_SLASHES, '') === CHAIN_FILE
477
+ )
478
+ })
479
+ }
480
+
481
+ /**
482
+ * The config a root `extends`, read one level deep, or `null`.
483
+ *
484
+ * Only for {@link claimsAppFiles}: a root with no `files` or `include` of its
485
+ * own inherits the base's, and core's server config lists the three ambient
486
+ * declarations, which is what keeps such a root from claiming its whole
487
+ * directory.
488
+ */
489
+ function baseConfigOf(
490
+ root: Record<string, unknown>,
491
+ ): Record<string, unknown> | null {
492
+ if (typeof root.extends !== 'string') return null
493
+ try {
494
+ const base = Bun.resolveSync(root.extends, APP_DIR)
495
+ return parseJSONC(readFileSync(base, 'utf8'))
496
+ } catch {
497
+ // Unreadable reads as "no base", which `claimsAppFiles` takes as
498
+ // TypeScript's default of claiming everything: the root is then left
499
+ // unwired, which is the direction that cannot break `tsc -p`.
500
+ return null
501
+ }
502
+ }
503
+
504
+ /**
505
+ * Whether a root config claims app files for itself.
506
+ *
507
+ * **A root that does cannot be wired to the chain.** Each file it shares with
508
+ * a generated project is redirected to that project's never-built output, and
509
+ * `tsc -p` fails with TS6305 once per file: 57 on the app this was measured
510
+ * on. Declaration files are not redirected, so a root claiming only ambient
511
+ * `.d.ts` files, which is what it inherits from core's server config, claims
512
+ * nothing that matters.
513
+ *
514
+ * TypeScript's rules, in order: the root's own `include` and `files` win over
515
+ * the base's; `files` without `include` claims only those files; and neither,
516
+ * anywhere, claims every file under the directory.
517
+ */
518
+ export function claimsAppFiles(
519
+ root: Record<string, unknown>,
520
+ base: Record<string, unknown> | null,
521
+ ): boolean {
522
+ const pick = (key: 'include' | 'files') =>
523
+ key in root ? root[key] : base?.[key]
524
+ const include = pick('include')
525
+ const files = pick('files')
526
+
527
+ if (Array.isArray(include) && include.length > 0) return true
528
+ if (
529
+ Array.isArray(files) &&
530
+ files.some(f => typeof f === 'string' && !f.endsWith('.d.ts'))
531
+ ) {
532
+ return true
533
+ }
534
+ return include === undefined && files === undefined
535
+ }
536
+
537
+ /**
538
+ * Add the chain reference to a root with no `references` key, as text.
539
+ *
540
+ * **Textual, so the developer's comments survive.** `tsconfig.json` is JSONC
541
+ * and theirs, and a parse-and-stringify rewrite drops every comment in it.
542
+ * This finds the last member of the top-level object, skipping strings and
543
+ * comments, and adds one member after it, with a comma unless one is already
544
+ * there. `null` when the text has no top-level object to extend.
545
+ */
546
+ export function insertChainReference(text: string): string | null {
547
+ const eol = text.includes('\r\n') ? '\r\n' : '\n'
548
+ let depth = 0
549
+ let inString = false
550
+ let escaped = false
551
+ let last = -1
552
+ let close = -1
553
+
554
+ for (let i = 0; i < text.length; i++) {
555
+ const c = text[i]
556
+ if (inString) {
557
+ if (escaped) escaped = false
558
+ else if (c === '\\') escaped = true
559
+ else if (c === '"') inString = false
560
+ last = i
561
+ continue
562
+ }
563
+ if (c === '/' && text[i + 1] === '/') {
564
+ const end = text.indexOf('\n', i)
565
+ i = end === -1 ? text.length : end
566
+ continue
567
+ }
568
+ if (c === '/' && text[i + 1] === '*') {
569
+ const end = text.indexOf('*/', i + 2)
570
+ i = end === -1 ? text.length : end + 1
571
+ continue
572
+ }
573
+ if (c === '"') {
574
+ inString = true
575
+ last = i
576
+ continue
577
+ }
578
+ if (c === ' ' || c === '\t' || c === '\n' || c === '\r') continue
579
+ if (c === '{' || c === '[') depth++
580
+ else if (c === '}' || c === ']') {
581
+ depth--
582
+ if (depth === 0 && c === '}') {
583
+ close = i
584
+ break
585
+ }
586
+ }
587
+ last = i
588
+ }
589
+
590
+ if (close === -1 || last === -1) return null
591
+
592
+ const comma = text[last] === '{' || text[last] === ',' ? '' : ','
593
+ const rest = text.slice(last + 1)
594
+ const gap = rest.startsWith('\n') || rest.startsWith('\r\n') ? '' : eol
595
+ return `${text.slice(0, last + 1)}${comma}${eol} "references": [{ "path": "${CHAIN_REF}" }]${gap}${rest}`
596
+ }
597
+
598
+ /**
599
+ * What the root config should become, as text, or `null` for no write.
600
+ *
601
+ * Two repairs, in order. The direct references earlier releases wrote are
602
+ * stripped ({@link stripGeneratedReferences}). Then the chain reference is
603
+ * added, unless the root already has it or claims app files of its own, in
604
+ * which case adding it would break `tsc -p` ({@link claimsAppFiles}) and the
605
+ * caller says so instead.
606
+ *
607
+ * Pure, and the place the rule "a root never references `.cache/tsconfig/`"
608
+ * is held: the only entry it ever adds is the chain file.
609
+ */
610
+ export function wireRoot(
611
+ text: string,
612
+ base: Record<string, unknown> | null,
613
+ ): { text: string | null; stripped: boolean; wired: boolean; claims: boolean } {
614
+ let root = parseJSONC(text) as Record<string, unknown>
615
+ let out: string | null = null
616
+
617
+ const repaired = stripGeneratedReferences(root)
618
+ if (repaired) {
619
+ root = repaired
620
+ out = `${JSON.stringify(repaired, null, 2)}\n`
621
+ }
622
+ const stripped = repaired !== null
623
+
624
+ if (referencesChain(root)) {
625
+ return { text: out, stripped, wired: false, claims: false }
626
+ }
627
+ if (claimsAppFiles(root, base)) {
628
+ return { text: out, stripped, wired: false, claims: true }
629
+ }
630
+
631
+ const refs = Array.isArray(root.references) ? root.references : null
632
+ const wired =
633
+ (refs ? null : insertChainReference(out ?? text)) ??
634
+ `${JSON.stringify({ ...root, references: [...(refs ?? []), { path: CHAIN_REF }] }, null, 2)}\n`
635
+ return { text: wired, stripped, wired: true, claims: false }
636
+ }
637
+
397
638
  /**
398
639
  * The root config written when the app has none at all.
399
640
  *
@@ -402,6 +643,10 @@ export function stripGeneratedReferences(
402
643
  * only into a relative path, never a package specifier, so the file this
403
644
  * writes has to carry the JSX options itself, inline, exactly as the
404
645
  * scaffolder spells them.
646
+ *
647
+ * It claims no app files: it inherits only core's ambient declarations, and
648
+ * reaches everything else through the chain, which is the one shape the
649
+ * chain is legal in (see {@link claimsAppFiles}).
405
650
  */
406
651
  export function defaultRootConfig(): Record<string, unknown> {
407
652
  return {
@@ -411,50 +656,59 @@ export function defaultRootConfig(): Record<string, unknown> {
411
656
  jsxFactory: 'createElement',
412
657
  jsxFragmentFactory: 'Fragment',
413
658
  },
659
+ references: [{ path: CHAIN_REF }],
414
660
  }
415
661
  }
416
662
 
417
663
  /**
418
- * Generate the project configs, and keep the app's root tsconfig viable:
419
- * created with the runtime JSX options when the app has none, and stripped of
420
- * the `references` a previous release wrote into it.
664
+ * Generate the scope projects, list them in `tsconfig.bakery.json`, and wire
665
+ * the app's root tsconfig to that file.
421
666
  *
422
667
  * Separate from `syncTSConfigPaths` because an app can reasonably want one and
423
668
  * not the other: the paths sync has existed for a long time and rewrites a file
424
669
  * people keep in git, while this owns a directory nobody edits.
425
670
  *
426
- * **The generated projects are standalone on purpose; the root config does not
427
- * reference them.** They exist for direct invocation against the one concern
428
- * each covers: `vue-tsc -p .cache/tsconfig/vue.json` is the only way an SFC
429
- * typechecks at all, `tsc -p .cache/tsconfig/client.json` proves browser code
430
- * clean of `Bun.*`, and a plugin's project carries its own ambients the same
431
- * way. Their `include` deliberately overlaps the app's own project: they are
432
- * alternate projections of the same files, used *instead of* the root for
433
- * their slice, never composed with it. Wiring them in as `references` broke
434
- * `tsc -p <app>` for any consumer who had booted once (TS6305/6306/6310: the
435
- * measurements are on {@link stripGeneratedReferences}), and what the wiring
436
- * bought was less than it looked: tsserver routes a file to the project whose
437
- * `include` claims it, so for everything a scaffolded root claims, `src/**`,
438
- * `server.config.ts`, `orm/**`, the reference walk never ran anyway.
671
+ * **Who owns what.** `.cache/tsconfig/*.json` and `tsconfig.bakery.json` are
672
+ * generated, and edits to them are lost. `tsconfig.json` is the developer's:
673
+ * the only change made to it is the one `references` entry for the chain,
674
+ * inserted as text so its comments survive, plus the one-time strip of the
675
+ * direct references earlier releases wrote.
676
+ *
677
+ * **Why the chain.** An editor gives a file to the project that claims it.
678
+ * With the projects standing alone, nothing pointed an editor at them, so a
679
+ * root that claimed nothing left every app file in an inferred project, and
680
+ * `Bun` was unknown in every `api/` file. Through the chain each file gets its
681
+ * scope: `api/` and pages to server-side projects, other `.ts` to `client`, an
682
+ * SFC to plugin-vue's project. `tsc -b` checks all of them at once.
683
+ *
684
+ * **Only for a root that claims no app files.** One that does fails
685
+ * `tsc -p` with TS6305 once per shared file when chained, so it is left as it
686
+ * is, with a line saying how to move ({@link claimsAppFiles}). Said when the
687
+ * chain file changes rather than on every boot, since keeping the old shape
688
+ * can be deliberate.
689
+ *
690
+ * **A fresh clone has none of these files** until something generates them,
691
+ * and `tsc` then stops at TS6053 on the chain's references. `bakery --types`
692
+ * generates them without starting a server, so a clone, a CI job or a
693
+ * `typecheck` script can run it first. The cache wipe keeps
694
+ * `.cache/tsconfig/` for the same reason (`WIPE_KEEP` in `cache-version.ts`).
439
695
  *
440
696
  * Two earlier lessons still bind the root-config half. It used to be
441
697
  * *replaced* with a references-only stub, which silently broke every `.tsx`
442
698
  * page: Bun's runtime reads `compilerOptions.jsx*` from the root and does not
443
699
  * follow `references`, so pages transpiled against the automatic JSX runtime
444
700
  * and `GET /` answered 200 with a JSON-encoded React element tree. Hence
445
- * {@link defaultRootConfig} when no root exists, and surgical repair (never
701
+ * {@link defaultRootConfig} when no root exists, and surgical edits (never a
446
702
  * wholesale rewrite) when one does. And a boot that dirties git every time
447
- * trains people to ignore the diff, so an already-clean root is not rewritten.
703
+ * trains people to ignore the diff, so a root already in shape is not
704
+ * rewritten, and neither is an unchanged chain file.
448
705
  */
449
706
  export async function syncTSConfigProjects(): Promise<void> {
450
707
  try {
451
708
  const written = await writeProjects(buildPaths())
709
+ const chainChanged = await writeChain(written)
452
710
 
453
- const current = fs.exists(APP_CONFIG_PATH)
454
- ? parseJSONC(await Bun.file(APP_CONFIG_PATH).text())
455
- : null
456
-
457
- if (!current) {
711
+ if (!fs.exists(APP_CONFIG_PATH)) {
458
712
  await Bun.write(
459
713
  APP_CONFIG_PATH,
460
714
  `${JSON.stringify(defaultRootConfig(), null, 2)}\n`,
@@ -463,11 +717,13 @@ export async function syncTSConfigProjects(): Promise<void> {
463
717
  return
464
718
  }
465
719
 
466
- const repaired = stripGeneratedReferences(current)
467
- if (!repaired) return
720
+ const text = await Bun.file(APP_CONFIG_PATH).text()
721
+ const plan = wireRoot(text, baseConfigOf(parseJSONC(text)))
468
722
 
469
- await Bun.write(APP_CONFIG_PATH, `${JSON.stringify(repaired, null, 2)}\n`)
470
- serveLog.TSCONFIG_REFERENCES_REMOVED()
723
+ if (plan.text !== null) await Bun.write(APP_CONFIG_PATH, plan.text)
724
+ if (plan.stripped) serveLog.TSCONFIG_REFERENCES_REMOVED()
725
+ if (plan.wired) serveLog.TSCONFIG_CHAIN_WIRED()
726
+ if (plan.claims && chainChanged) serveLog.TSCONFIG_ROOT_CLAIMS_FILES()
471
727
  } catch (err: any) {
472
728
  serveLog.UNHANDLED_ERR({
473
729
  error: `TSConfig project sync error: ${errorMsg(err)}`,
@@ -153,12 +153,13 @@ export const __wipeCacheDir = wipe
153
153
  /**
154
154
  * The one thing in `.cache/` the wipe must not take.
155
155
  *
156
- * **The app's committed `tsconfig.json` *references* `.cache/tsconfig/*.json`,
157
- * so deleting them breaks the editor for the whole project**, not one setting,
158
- * everything. TypeScript reports `TS6053: File '…/server.json' not found` for
159
- * each reference, has no project left to put a file in, and falls back to an
160
- * inferred one with no ambients: `req.session`, `Bakery`, the JSX namespace and
161
- * the app's own schema types all stop resolving at once.
156
+ * **The app's `tsconfig.json` reaches `.cache/tsconfig/*.json` through
157
+ * `tsconfig.bakery.json`, so deleting them breaks the editor for the whole
158
+ * project**, not one setting, everything. TypeScript reports
159
+ * `TS6053: File '…/server.json' not found` for each reference, has no project
160
+ * left to put a file in, and falls back to an inferred one with no ambients:
161
+ * `Bun`, `req.session`, `Bakery`, the JSX namespace and the app's own schema
162
+ * types all stop resolving at once.
162
163
  *
163
164
  * That happens on every framework upgrade, because the version wipe is keyed on
164
165
  * the framework version among others. The developer sees their editor lose every
@@ -26,13 +26,21 @@ const serveMsgs = {
26
26
  TSCONFIG_SYNCED:
27
27
  'I Synced %ytsconfig.json%* paths with %yserver.config.ts%*!',
28
28
  TSCONFIG_PROJECTS_WRITTEN:
29
- 'I Wrote %y{count}%* tsconfig project(s) to %y.cache/tsconfig/%*',
29
+ 'I Wrote %y{count}%* tsconfig project(s) to %y.cache/tsconfig/%*, listed in %ytsconfig.bakery.json%*, and a %ytsconfig.json%* that references it',
30
30
  // One-time repair. Previous releases wired the generated projects into the
31
31
  // app's tsconfig.json as `references`, which broke `tsc -p <app>`
32
32
  // (TS6305/6306/6310). Named so the rewrite of a tracked file comes with a
33
33
  // line saying why it happened.
34
34
  TSCONFIG_REFERENCES_REMOVED:
35
- 'I Removed generated %yreferences%* from %ytsconfig.json%*, the %y.cache/tsconfig/%* projects are standalone, and referencing them broke %ytsc -p%*',
35
+ 'I Removed the direct %y.cache/tsconfig/%* entries from %yreferences%* in %ytsconfig.json%*: referencing those projects directly broke %ytsc -p%*, and %ytsconfig.bakery.json%* reaches them instead',
36
+ // The one edit made to a developer's tsconfig.json, named for the same
37
+ // reason as the repair above: a tracked file changed on boot.
38
+ TSCONFIG_CHAIN_WIRED:
39
+ 'I Added %y./tsconfig.bakery.json%* to %yreferences%* in %ytsconfig.json%*: an editor now gives each file its scope project',
40
+ // Said when tsconfig.bakery.json is new or changed, not on every boot: a root
41
+ // that claims files may be kept that way on purpose, and it still works.
42
+ TSCONFIG_ROOT_CLAIMS_FILES:
43
+ 'W %ytsconfig.json%* claims app files, so it is not wired to %ytsconfig.bakery.json%*: chained, it would fail %ytsc -p%* with TS6305. To give each file its scope, remove its %yinclude%* and %yfiles%*, reference %y./tsconfig.bakery.json%*, and typecheck with %ytsc -b%*',
36
44
  // A plugin asking for a project name that is taken. Named rather than
37
45
  // silent: the symptom otherwise is one plugin's types quietly not applying,
38
46
  // discovered much later and blamed on the wrong thing.