@three-flatland/bake 0.1.0-alpha.1 → 0.1.0-alpha.2

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/README.md CHANGED
@@ -123,7 +123,7 @@ Not a dev-iteration knob — the default path (probe → generate on miss + warn
123
123
 
124
124
  ## Documentation
125
125
 
126
- Full docs, interactive examples, and API reference at **[thejustinwalsh.com/three-flatland](https://thejustinwalsh.com/three-flatland/)**
126
+ Full docs, interactive examples, and API reference at **[tjw.dev/three-flatland](https://tjw.dev/three-flatland/)**
127
127
 
128
128
  ## License
129
129
 
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
package/dist/cli.js CHANGED
@@ -1,8 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import "node:path";
3
+ import "node:url";
4
+ import.meta.url;
5
+ import { discoverBakers } from "./discovery.js";
2
6
  import { pathToFileURL } from "node:url";
3
7
  import { existsSync } from "node:fs";
4
- import { discoverBakers } from "./discovery.js";
5
- const USAGE = `flatland-bake \u2014 unified bake entry point
8
+ //#region src/cli.ts
9
+ /**
10
+ * `flatland-bake` — the unified bake entry point. Discovers bakers
11
+ * contributed by installed packages (via a `flatland.bake` field in
12
+ * their package.json), then dispatches `flatland-bake <name> [args]` to
13
+ * the matching baker's `run()`. `--list` enumerates available bakers and
14
+ * `--help` prints usage. See {@link discoverBakers} and the {@link Baker}
15
+ * contract for how a package registers a subcommand.
16
+ */
17
+ const USAGE = `flatland-bake — unified bake entry point
6
18
 
7
19
  Usage:
8
20
  flatland-bake <name> [args...] Run a registered baker
@@ -13,80 +25,58 @@ Bakers are contributed by packages via a \`flatland.bake\` field in
13
25
  package.json. Install a package that provides one (e.g. @three-flatland/slug)
14
26
  and its subcommand will appear in --list.`;
15
27
  async function main(argv) {
16
- const [first, ...rest] = argv;
17
- if (!first || first === "--help" || first === "-h") {
18
- process.stdout.write(USAGE + "\n");
19
- return 0;
20
- }
21
- const { bakers, conflicts } = discoverBakers();
22
- for (const warning of conflicts) {
23
- process.stderr.write(`[flatland-bake] warn: ${warning}
24
- `);
25
- }
26
- if (first === "--list") {
27
- return printList(bakers);
28
- }
29
- const match = bakers.find((b) => b.name === first);
30
- if (!match) {
31
- process.stderr.write(
32
- `[flatland-bake] unknown baker "${first}". Run \`flatland-bake --list\` to see what's available.
33
- `
34
- );
35
- return 1;
36
- }
37
- const baker = await loadBaker(match);
38
- if (!baker) return 1;
39
- try {
40
- return await baker.run(rest);
41
- } catch (err) {
42
- process.stderr.write(
43
- `[flatland-bake] baker "${match.name}" threw: ${err instanceof Error ? err.message : String(err)}
44
- `
45
- );
46
- return 1;
47
- }
28
+ const [first, ...rest] = argv;
29
+ if (!first || first === "--help" || first === "-h") {
30
+ process.stdout.write(USAGE + "\n");
31
+ return 0;
32
+ }
33
+ const { bakers, conflicts } = discoverBakers();
34
+ for (const warning of conflicts) process.stderr.write(`[flatland-bake] warn: ${warning}\n`);
35
+ if (first === "--list") return printList(bakers);
36
+ const match = bakers.find((b) => b.name === first);
37
+ if (!match) {
38
+ process.stderr.write(`[flatland-bake] unknown baker "${first}". Run \`flatland-bake --list\` to see what's available.\n`);
39
+ return 1;
40
+ }
41
+ const baker = await loadBaker(match);
42
+ if (!baker) return 1;
43
+ try {
44
+ return await baker.run(rest);
45
+ } catch (err) {
46
+ process.stderr.write(`[flatland-bake] baker "${match.name}" threw: ${err instanceof Error ? err.message : String(err)}\n`);
47
+ return 1;
48
+ }
48
49
  }
49
50
  function printList(bakers) {
50
- if (bakers.length === 0) {
51
- process.stdout.write(
52
- "No bakers registered. Install a package that contributes one (e.g. @three-flatland/slug).\n"
53
- );
54
- return 0;
55
- }
56
- const nameWidth = Math.max(...bakers.map((b) => b.name.length));
57
- process.stdout.write("Registered bakers:\n");
58
- for (const b of bakers) {
59
- const pad = " ".repeat(nameWidth - b.name.length);
60
- process.stdout.write(` ${b.name}${pad} ${b.description} (${b.packageName})
61
- `);
62
- }
63
- return 0;
51
+ if (bakers.length === 0) {
52
+ process.stdout.write("No bakers registered. Install a package that contributes one (e.g. @three-flatland/slug).\n");
53
+ return 0;
54
+ }
55
+ const nameWidth = Math.max(...bakers.map((b) => b.name.length));
56
+ process.stdout.write("Registered bakers:\n");
57
+ for (const b of bakers) {
58
+ const pad = " ".repeat(nameWidth - b.name.length);
59
+ process.stdout.write(` ${b.name}${pad} ${b.description} (${b.packageName})\n`);
60
+ }
61
+ return 0;
64
62
  }
65
63
  async function loadBaker(reg) {
66
- if (!existsSync(reg.resolvedEntry)) {
67
- process.stderr.write(
68
- `[flatland-bake] baker "${reg.name}" from "${reg.packageName}" points to missing entry: ${reg.resolvedEntry}
69
- `
70
- );
71
- return null;
72
- }
73
- const mod = await import(pathToFileURL(reg.resolvedEntry).href);
74
- const baker = mod.default;
75
- if (!baker || typeof baker.run !== "function") {
76
- process.stderr.write(
77
- `[flatland-bake] baker "${reg.name}" from "${reg.packageName}" did not default-export a Baker.
78
- `
79
- );
80
- return null;
81
- }
82
- return baker;
64
+ if (!existsSync(reg.resolvedEntry)) {
65
+ process.stderr.write(`[flatland-bake] baker "${reg.name}" from "${reg.packageName}" points to missing entry: ${reg.resolvedEntry}\n`);
66
+ return null;
67
+ }
68
+ const baker = (await import(pathToFileURL(reg.resolvedEntry).href)).default;
69
+ if (!baker || typeof baker.run !== "function") {
70
+ process.stderr.write(`[flatland-bake] baker "${reg.name}" from "${reg.packageName}" did not default-export a Baker.\n`);
71
+ return null;
72
+ }
73
+ return baker;
83
74
  }
84
- main(process.argv.slice(2)).then(
85
- (code) => process.exit(code),
86
- (err) => {
87
- process.stderr.write(`[flatland-bake] fatal: ${String(err)}
88
- `);
89
- process.exit(1);
90
- }
91
- );
75
+ main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
76
+ process.stderr.write(`[flatland-bake] fatal: ${String(err)}\n`);
77
+ process.exit(1);
78
+ });
79
+ //#endregion
80
+ export {};
81
+
92
82
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `flatland-bake` — the unified bake entry point. Discovers bakers\n * contributed by installed packages (via a `flatland.bake` field in\n * their package.json), then dispatches `flatland-bake <name> [args]` to\n * the matching baker's `run()`. `--list` enumerates available bakers and\n * `--help` prints usage. See {@link discoverBakers} and the {@link Baker}\n * contract for how a package registers a subcommand.\n */\nimport { pathToFileURL } from 'node:url'\nimport { existsSync } from 'node:fs'\nimport { discoverBakers } from './discovery.js'\nimport type { Baker, BakerRegistration } from './types.js'\n\nconst USAGE = `flatland-bake — unified bake entry point\n\nUsage:\n flatland-bake <name> [args...] Run a registered baker\n flatland-bake --list List registered bakers\n flatland-bake --help Show this usage\n\nBakers are contributed by packages via a \\`flatland.bake\\` field in\npackage.json. Install a package that provides one (e.g. @three-flatland/slug)\nand its subcommand will appear in --list.`\n\nasync function main(argv: string[]): Promise<number> {\n const [first, ...rest] = argv\n\n if (!first || first === '--help' || first === '-h') {\n process.stdout.write(USAGE + '\\n')\n return 0\n }\n\n const { bakers, conflicts } = discoverBakers()\n for (const warning of conflicts) {\n process.stderr.write(`[flatland-bake] warn: ${warning}\\n`)\n }\n\n if (first === '--list') {\n return printList(bakers)\n }\n\n const match = bakers.find((b) => b.name === first)\n if (!match) {\n process.stderr.write(\n `[flatland-bake] unknown baker \"${first}\". Run \\`flatland-bake --list\\` to see what's available.\\n`\n )\n return 1\n }\n\n const baker = await loadBaker(match)\n if (!baker) return 1\n\n try {\n return await baker.run(rest)\n } catch (err) {\n process.stderr.write(\n `[flatland-bake] baker \"${match.name}\" threw: ${err instanceof Error ? err.message : String(err)}\\n`\n )\n return 1\n }\n}\n\nfunction printList(bakers: BakerRegistration[]): number {\n if (bakers.length === 0) {\n process.stdout.write(\n 'No bakers registered. Install a package that contributes one (e.g. @three-flatland/slug).\\n'\n )\n return 0\n }\n\n const nameWidth = Math.max(...bakers.map((b) => b.name.length))\n process.stdout.write('Registered bakers:\\n')\n for (const b of bakers) {\n const pad = ' '.repeat(nameWidth - b.name.length)\n process.stdout.write(` ${b.name}${pad} ${b.description} (${b.packageName})\\n`)\n }\n return 0\n}\n\nasync function loadBaker(reg: BakerRegistration): Promise<Baker | null> {\n if (!existsSync(reg.resolvedEntry)) {\n process.stderr.write(\n `[flatland-bake] baker \"${reg.name}\" from \"${reg.packageName}\" points to missing entry: ${reg.resolvedEntry}\\n`\n )\n return null\n }\n\n const mod = (await import(pathToFileURL(reg.resolvedEntry).href)) as {\n default?: Baker\n }\n const baker = mod.default\n if (!baker || typeof baker.run !== 'function') {\n process.stderr.write(\n `[flatland-bake] baker \"${reg.name}\" from \"${reg.packageName}\" did not default-export a Baker.\\n`\n )\n return null\n }\n return baker\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (err) => {\n process.stderr.write(`[flatland-bake] fatal: ${String(err)}\\n`)\n process.exit(1)\n }\n)\n"],"mappings":";AASA,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAG/B,MAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWd,eAAe,KAAK,MAAiC;AACnD,QAAM,CAAC,OAAO,GAAG,IAAI,IAAI;AAEzB,MAAI,CAAC,SAAS,UAAU,YAAY,UAAU,MAAM;AAClD,YAAQ,OAAO,MAAM,QAAQ,IAAI;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,QAAQ,UAAU,IAAI,eAAe;AAC7C,aAAW,WAAW,WAAW;AAC/B,YAAQ,OAAO,MAAM,yBAAyB,OAAO;AAAA,CAAI;AAAA,EAC3D;AAEA,MAAI,UAAU,UAAU;AACtB,WAAO,UAAU,MAAM;AAAA,EACzB;AAEA,QAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK;AACjD,MAAI,CAAC,OAAO;AACV,YAAQ,OAAO;AAAA,MACb,kCAAkC,KAAK;AAAA;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,UAAU,KAAK;AACnC,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI;AACF,WAAO,MAAM,MAAM,IAAI,IAAI;AAAA,EAC7B,SAAS,KAAK;AACZ,YAAQ,OAAO;AAAA,MACb,0BAA0B,MAAM,IAAI,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,IAClG;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,QAAqC;AACtD,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAC9D,UAAQ,OAAO,MAAM,sBAAsB;AAC3C,aAAW,KAAK,QAAQ;AACtB,UAAM,MAAM,IAAI,OAAO,YAAY,EAAE,KAAK,MAAM;AAChD,YAAQ,OAAO,MAAM,KAAK,EAAE,IAAI,GAAG,GAAG,KAAK,EAAE,WAAW,MAAM,EAAE,WAAW;AAAA,CAAK;AAAA,EAClF;AACA,SAAO;AACT;AAEA,eAAe,UAAU,KAA+C;AACtE,MAAI,CAAC,WAAW,IAAI,aAAa,GAAG;AAClC,YAAQ,OAAO;AAAA,MACb,0BAA0B,IAAI,IAAI,WAAW,IAAI,WAAW,8BAA8B,IAAI,aAAa;AAAA;AAAA,IAC7G;AACA,WAAO;AAAA,EACT;AAEA,QAAM,MAAO,MAAM,OAAO,cAAc,IAAI,aAAa,EAAE;AAG3D,QAAM,QAAQ,IAAI;AAClB,MAAI,CAAC,SAAS,OAAO,MAAM,QAAQ,YAAY;AAC7C,YAAQ,OAAO;AAAA,MACb,0BAA0B,IAAI,IAAI,WAAW,IAAI,WAAW;AAAA;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,EAAE;AAAA,EAC1B,CAAC,SAAS,QAAQ,KAAK,IAAI;AAAA,EAC3B,CAAC,QAAQ;AACP,YAAQ,OAAO,MAAM,0BAA0B,OAAO,GAAG,CAAC;AAAA,CAAI;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `flatland-bake` — the unified bake entry point. Discovers bakers\n * contributed by installed packages (via a `flatland.bake` field in\n * their package.json), then dispatches `flatland-bake <name> [args]` to\n * the matching baker's `run()`. `--list` enumerates available bakers and\n * `--help` prints usage. See {@link discoverBakers} and the {@link Baker}\n * contract for how a package registers a subcommand.\n */\nimport { pathToFileURL } from 'node:url'\nimport { existsSync } from 'node:fs'\nimport { discoverBakers } from './discovery.js'\nimport type { Baker, BakerRegistration } from './types.js'\n\nconst USAGE = `flatland-bake — unified bake entry point\n\nUsage:\n flatland-bake <name> [args...] Run a registered baker\n flatland-bake --list List registered bakers\n flatland-bake --help Show this usage\n\nBakers are contributed by packages via a \\`flatland.bake\\` field in\npackage.json. Install a package that provides one (e.g. @three-flatland/slug)\nand its subcommand will appear in --list.`\n\nasync function main(argv: string[]): Promise<number> {\n const [first, ...rest] = argv\n\n if (!first || first === '--help' || first === '-h') {\n process.stdout.write(USAGE + '\\n')\n return 0\n }\n\n const { bakers, conflicts } = discoverBakers()\n for (const warning of conflicts) {\n process.stderr.write(`[flatland-bake] warn: ${warning}\\n`)\n }\n\n if (first === '--list') {\n return printList(bakers)\n }\n\n const match = bakers.find((b) => b.name === first)\n if (!match) {\n process.stderr.write(\n `[flatland-bake] unknown baker \"${first}\". Run \\`flatland-bake --list\\` to see what's available.\\n`\n )\n return 1\n }\n\n const baker = await loadBaker(match)\n if (!baker) return 1\n\n try {\n return await baker.run(rest)\n } catch (err) {\n process.stderr.write(\n `[flatland-bake] baker \"${match.name}\" threw: ${err instanceof Error ? err.message : String(err)}\\n`\n )\n return 1\n }\n}\n\nfunction printList(bakers: BakerRegistration[]): number {\n if (bakers.length === 0) {\n process.stdout.write('No bakers registered. Install a package that contributes one (e.g. @three-flatland/slug).\\n')\n return 0\n }\n\n const nameWidth = Math.max(...bakers.map((b) => b.name.length))\n process.stdout.write('Registered bakers:\\n')\n for (const b of bakers) {\n const pad = ' '.repeat(nameWidth - b.name.length)\n process.stdout.write(` ${b.name}${pad} ${b.description} (${b.packageName})\\n`)\n }\n return 0\n}\n\nasync function loadBaker(reg: BakerRegistration): Promise<Baker | null> {\n if (!existsSync(reg.resolvedEntry)) {\n process.stderr.write(\n `[flatland-bake] baker \"${reg.name}\" from \"${reg.packageName}\" points to missing entry: ${reg.resolvedEntry}\\n`\n )\n return null\n }\n\n const mod = (await import(pathToFileURL(reg.resolvedEntry).href)) as {\n default?: Baker\n }\n const baker = mod.default\n if (!baker || typeof baker.run !== 'function') {\n process.stderr.write(\n `[flatland-bake] baker \"${reg.name}\" from \"${reg.packageName}\" did not default-export a Baker.\\n`\n )\n return null\n }\n return baker\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (err) => {\n process.stderr.write(`[flatland-bake] fatal: ${String(err)}\\n`)\n process.exit(1)\n }\n)\n"],"mappings":";;;;;;;;;;;;;;;;AAcA,MAAM,QAAQ;;;;;;;;;;AAWd,eAAe,KAAK,MAAiC;CACnD,MAAM,CAAC,OAAO,GAAG,QAAQ;CAEzB,IAAI,CAAC,SAAS,UAAU,YAAY,UAAU,MAAM;EAClD,QAAQ,OAAO,MAAM,QAAQ,IAAI;EACjC,OAAO;CACT;CAEA,MAAM,EAAE,QAAQ,cAAc,eAAe;CAC7C,KAAK,MAAM,WAAW,WACpB,QAAQ,OAAO,MAAM,yBAAyB,QAAQ,GAAG;CAG3D,IAAI,UAAU,UACZ,OAAO,UAAU,MAAM;CAGzB,MAAM,QAAQ,OAAO,MAAM,MAAM,EAAE,SAAS,KAAK;CACjD,IAAI,CAAC,OAAO;EACV,QAAQ,OAAO,MACb,kCAAkC,MAAM,2DAC1C;EACA,OAAO;CACT;CAEA,MAAM,QAAQ,MAAM,UAAU,KAAK;CACnC,IAAI,CAAC,OAAO,OAAO;CAEnB,IAAI;EACF,OAAO,MAAM,MAAM,IAAI,IAAI;CAC7B,SAAS,KAAK;EACZ,QAAQ,OAAO,MACb,0BAA0B,MAAM,KAAK,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,GACnG;EACA,OAAO;CACT;AACF;AAEA,SAAS,UAAU,QAAqC;CACtD,IAAI,OAAO,WAAW,GAAG;EACvB,QAAQ,OAAO,MAAM,6FAA6F;EAClH,OAAO;CACT;CAEA,MAAM,YAAY,KAAK,IAAI,GAAG,OAAO,KAAK,MAAM,EAAE,KAAK,MAAM,CAAC;CAC9D,QAAQ,OAAO,MAAM,sBAAsB;CAC3C,KAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,MAAM,IAAI,OAAO,YAAY,EAAE,KAAK,MAAM;EAChD,QAAQ,OAAO,MAAM,KAAK,EAAE,OAAO,IAAI,IAAI,EAAE,YAAY,KAAK,EAAE,YAAY,IAAI;CAClF;CACA,OAAO;AACT;AAEA,eAAe,UAAU,KAA+C;CACtE,IAAI,CAAC,WAAW,IAAI,aAAa,GAAG;EAClC,QAAQ,OAAO,MACb,0BAA0B,IAAI,KAAK,UAAU,IAAI,YAAY,6BAA6B,IAAI,cAAc,GAC9G;EACA,OAAO;CACT;CAKA,MAAM,SAAQ,MAHK,OAAO,cAAc,IAAI,aAAa,CAAC,CAAC,MAAA,CAGzC;CAClB,IAAI,CAAC,SAAS,OAAO,MAAM,QAAQ,YAAY;EAC7C,QAAQ,OAAO,MACb,0BAA0B,IAAI,KAAK,UAAU,IAAI,YAAY,oCAC/D;EACA,OAAO;CACT;CACA,OAAO;AACT;AAEA,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,MACzB,SAAS,QAAQ,KAAK,IAAI,IAC1B,QAAQ;CACP,QAAQ,OAAO,MAAM,0BAA0B,OAAO,GAAG,EAAE,GAAG;CAC9D,QAAQ,KAAK,CAAC;AAChB,CACF"}
@@ -0,0 +1,20 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/devtimeWarn.d.ts
4
+ /**
5
+ * Emit a console warning gated on `NODE_ENV !== 'production'`. Deduped
6
+ * per `(category, url)` — the same warning never fires twice.
7
+ *
8
+ * Shared by every sidecar-using loader so warnings surface uniformly
9
+ * across the ecosystem (normals, fonts, atlases, …).
10
+ *
11
+ * @param category short tag identifying what system warned (e.g. 'normal')
12
+ * @param url absolute URL or path of the asset
13
+ * @param message the message shown to the user
14
+ */
15
+ declare function devtimeWarn(category: string, url: string, message: string): void;
16
+ /** Clear the devtime-warning dedupe cache. Intended for tests. */
17
+ declare function _resetDevtimeWarnings(): void;
18
+ //#endregion
19
+ export { _resetDevtimeWarnings, devtimeWarn };
20
+ //# sourceMappingURL=devtimeWarn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtimeWarn.d.ts","names":[],"sources":["../src/devtimeWarn.ts"],"mappings":";;;;;;;;;;;;;;iBAagB,YAAY,kBAAkB,aAAa;;iBAa3C"}
@@ -1,24 +1,44 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/devtimeWarn.ts
1
5
  const _warned = /* @__PURE__ */ new Map();
6
+ /**
7
+ * Emit a console warning gated on `NODE_ENV !== 'production'`. Deduped
8
+ * per `(category, url)` — the same warning never fires twice.
9
+ *
10
+ * Shared by every sidecar-using loader so warnings surface uniformly
11
+ * across the ecosystem (normals, fonts, atlases, …).
12
+ *
13
+ * @param category short tag identifying what system warned (e.g. 'normal')
14
+ * @param url absolute URL or path of the asset
15
+ * @param message the message shown to the user
16
+ */
2
17
  function devtimeWarn(category, url, message) {
3
- if (isProduction()) return;
4
- let set = _warned.get(category);
5
- if (!set) {
6
- set = /* @__PURE__ */ new Set();
7
- _warned.set(category, set);
8
- }
9
- if (set.has(url)) return;
10
- set.add(url);
11
- console.warn(`[${category}] ${message}`);
18
+ if (isProduction()) return;
19
+ let set = _warned.get(category);
20
+ if (!set) {
21
+ set = /* @__PURE__ */ new Set();
22
+ _warned.set(category, set);
23
+ }
24
+ if (set.has(url)) return;
25
+ set.add(url);
26
+ console.warn(`[${category}] ${message}`);
12
27
  }
28
+ /** Clear the devtime-warning dedupe cache. Intended for tests. */
13
29
  function _resetDevtimeWarnings() {
14
- _warned.clear();
30
+ _warned.clear();
15
31
  }
32
+ /**
33
+ * Type-agnostic NODE_ENV check. Reads `globalThis.process.env.NODE_ENV`
34
+ * without forcing consumers to depend on `@types/node` — browser-only
35
+ * packages (e.g. mini-breakout, example apps) inline-consume this
36
+ * module through the source export and would otherwise hit TS2591.
37
+ */
16
38
  function isProduction() {
17
- const proc = globalThis.process;
18
- return proc?.env?.["NODE_ENV"] === "production";
39
+ return globalThis.process?.env?.["NODE_ENV"] === "production";
19
40
  }
20
- export {
21
- _resetDevtimeWarnings,
22
- devtimeWarn
23
- };
41
+ //#endregion
42
+ export { _resetDevtimeWarnings, devtimeWarn };
43
+
24
44
  //# sourceMappingURL=devtimeWarn.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/devtimeWarn.ts"],"sourcesContent":["const _warned = new Map<string, Set<string>>()\n\n/**\n * Emit a console warning gated on `NODE_ENV !== 'production'`. Deduped\n * per `(category, url)` — the same warning never fires twice.\n *\n * Shared by every sidecar-using loader so warnings surface uniformly\n * across the ecosystem (normals, fonts, atlases, …).\n *\n * @param category short tag identifying what system warned (e.g. 'normal')\n * @param url absolute URL or path of the asset\n * @param message the message shown to the user\n */\nexport function devtimeWarn(category: string, url: string, message: string): void {\n if (isProduction()) return\n let set = _warned.get(category)\n if (!set) {\n set = new Set()\n _warned.set(category, set)\n }\n if (set.has(url)) return\n set.add(url)\n console.warn(`[${category}] ${message}`)\n}\n\n/** Clear the devtime-warning dedupe cache. Intended for tests. */\nexport function _resetDevtimeWarnings(): void {\n _warned.clear()\n}\n\n/**\n * Type-agnostic NODE_ENV check. Reads `globalThis.process.env.NODE_ENV`\n * without forcing consumers to depend on `@types/node` — browser-only\n * packages (e.g. mini-breakout, example apps) inline-consume this\n * module through the source export and would otherwise hit TS2591.\n */\nfunction isProduction(): boolean {\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n return proc?.env?.['NODE_ENV'] === 'production'\n}\n"],"mappings":"AAAA,MAAM,UAAU,oBAAI,IAAyB;AAatC,SAAS,YAAY,UAAkB,KAAa,SAAuB;AAChF,MAAI,aAAa,EAAG;AACpB,MAAI,MAAM,QAAQ,IAAI,QAAQ;AAC9B,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,YAAQ,IAAI,UAAU,GAAG;AAAA,EAC3B;AACA,MAAI,IAAI,IAAI,GAAG,EAAG;AAClB,MAAI,IAAI,GAAG;AACX,UAAQ,KAAK,IAAI,QAAQ,KAAK,OAAO,EAAE;AACzC;AAGO,SAAS,wBAA8B;AAC5C,UAAQ,MAAM;AAChB;AAQA,SAAS,eAAwB;AAC/B,QAAM,OAAQ,WAA0E;AACxF,SAAO,MAAM,MAAM,UAAU,MAAM;AACrC;","names":[]}
1
+ {"version":3,"file":"devtimeWarn.js","names":[],"sources":["../src/devtimeWarn.ts"],"sourcesContent":["const _warned = new Map<string, Set<string>>()\n\n/**\n * Emit a console warning gated on `NODE_ENV !== 'production'`. Deduped\n * per `(category, url)` — the same warning never fires twice.\n *\n * Shared by every sidecar-using loader so warnings surface uniformly\n * across the ecosystem (normals, fonts, atlases, …).\n *\n * @param category short tag identifying what system warned (e.g. 'normal')\n * @param url absolute URL or path of the asset\n * @param message the message shown to the user\n */\nexport function devtimeWarn(category: string, url: string, message: string): void {\n if (isProduction()) return\n let set = _warned.get(category)\n if (!set) {\n set = new Set()\n _warned.set(category, set)\n }\n if (set.has(url)) return\n set.add(url)\n console.warn(`[${category}] ${message}`)\n}\n\n/** Clear the devtime-warning dedupe cache. Intended for tests. */\nexport function _resetDevtimeWarnings(): void {\n _warned.clear()\n}\n\n/**\n * Type-agnostic NODE_ENV check. Reads `globalThis.process.env.NODE_ENV`\n * without forcing consumers to depend on `@types/node` — browser-only\n * packages (e.g. mini-breakout, example apps) inline-consume this\n * module through the source export and would otherwise hit TS2591.\n */\nfunction isProduction(): boolean {\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n return proc?.env?.['NODE_ENV'] === 'production'\n}\n"],"mappings":";;;;AAAA,MAAM,0BAAU,IAAI,IAAyB;;;;;;;;;;;;AAa7C,SAAgB,YAAY,UAAkB,KAAa,SAAuB;CAChF,IAAI,aAAa,GAAG;CACpB,IAAI,MAAM,QAAQ,IAAI,QAAQ;CAC9B,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,QAAQ,IAAI,UAAU,GAAG;CAC3B;CACA,IAAI,IAAI,IAAI,GAAG,GAAG;CAClB,IAAI,IAAI,GAAG;CACX,QAAQ,KAAK,IAAI,SAAS,IAAI,SAAS;AACzC;;AAGA,SAAgB,wBAA8B;CAC5C,QAAQ,MAAM;AAChB;;;;;;;AAQA,SAAS,eAAwB;CAE/B,OADc,WAA0E,SAC3E,MAAM,gBAAgB;AACrC"}
@@ -0,0 +1,21 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { BakerRegistration } from "./types.js";
4
+ //#region src/discovery.d.ts
5
+ /**
6
+ * Discover registered bakers by walking `node_modules` near the current
7
+ * working directory. Supports both flat and pnpm-symlinked layouts: every
8
+ * `package.json` that declares a `flatland.bake` (or legacy
9
+ * `flatland.bakers`) field is picked up.
10
+ *
11
+ * Conflicts (multiple packages registering the same baker name) are reported;
12
+ * the first match wins and the rest are returned as warnings so the caller
13
+ * can decide whether to fail or log.
14
+ */
15
+ declare function discoverBakers(cwd?: string): {
16
+ bakers: BakerRegistration[];
17
+ conflicts: string[];
18
+ };
19
+ //#endregion
20
+ export { discoverBakers };
21
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","names":[],"sources":["../src/discovery.ts"],"mappings":";;;;;;;;;;;;;;iBAmBgB,eAAe;EAC7B,QAAQ;EACR"}
package/dist/discovery.js CHANGED
@@ -1,111 +1,119 @@
1
- import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
2
- import { resolve, join } from "node:path";
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
5
+ import { join, resolve } from "node:path";
6
+ //#region src/discovery.ts
7
+ /**
8
+ * Discover registered bakers by walking `node_modules` near the current
9
+ * working directory. Supports both flat and pnpm-symlinked layouts: every
10
+ * `package.json` that declares a `flatland.bake` (or legacy
11
+ * `flatland.bakers`) field is picked up.
12
+ *
13
+ * Conflicts (multiple packages registering the same baker name) are reported;
14
+ * the first match wins and the rest are returned as warnings so the caller
15
+ * can decide whether to fail or log.
16
+ */
3
17
  function discoverBakers(cwd = process.cwd()) {
4
- const seenPackages = /* @__PURE__ */ new Set();
5
- const bakers = /* @__PURE__ */ new Map();
6
- const conflicts = [];
7
- const selfDir = findPackageRoot(cwd);
8
- if (selfDir) {
9
- readPackage(selfDir, bakers, conflicts, seenPackages);
10
- }
11
- for (const nodeModulesDir of findNodeModulesDirs(cwd)) {
12
- scanNodeModules(nodeModulesDir, bakers, conflicts, seenPackages);
13
- }
14
- return { bakers: Array.from(bakers.values()), conflicts };
18
+ const seenPackages = /* @__PURE__ */ new Set();
19
+ const bakers = /* @__PURE__ */ new Map();
20
+ const conflicts = [];
21
+ const selfDir = findPackageRoot(cwd);
22
+ if (selfDir) readPackage(selfDir, bakers, conflicts, seenPackages);
23
+ for (const nodeModulesDir of findNodeModulesDirs(cwd)) scanNodeModules(nodeModulesDir, bakers, conflicts, seenPackages);
24
+ return {
25
+ bakers: Array.from(bakers.values()),
26
+ conflicts
27
+ };
15
28
  }
29
+ /** Walk upward from `start` until a directory with package.json is found. */
16
30
  function findPackageRoot(start) {
17
- let dir = resolve(start);
18
- while (true) {
19
- if (existsSync(join(dir, "package.json"))) return dir;
20
- const parent = resolve(dir, "..");
21
- if (parent === dir) return null;
22
- dir = parent;
23
- }
31
+ let dir = resolve(start);
32
+ while (true) {
33
+ if (existsSync(join(dir, "package.json"))) return dir;
34
+ const parent = resolve(dir, "..");
35
+ if (parent === dir) return null;
36
+ dir = parent;
37
+ }
24
38
  }
39
+ /** Walk upward from `cwd` collecting each `node_modules` directory we find. */
25
40
  function findNodeModulesDirs(cwd) {
26
- const dirs = [];
27
- let dir = resolve(cwd);
28
- while (true) {
29
- const nm = join(dir, "node_modules");
30
- if (existsSync(nm) && statSync(nm).isDirectory()) {
31
- dirs.push(nm);
32
- }
33
- const parent = resolve(dir, "..");
34
- if (parent === dir) break;
35
- dir = parent;
36
- }
37
- return dirs;
41
+ const dirs = [];
42
+ let dir = resolve(cwd);
43
+ while (true) {
44
+ const nm = join(dir, "node_modules");
45
+ if (existsSync(nm) && statSync(nm).isDirectory()) dirs.push(nm);
46
+ const parent = resolve(dir, "..");
47
+ if (parent === dir) break;
48
+ dir = parent;
49
+ }
50
+ return dirs;
38
51
  }
39
52
  function scanNodeModules(nodeModulesDir, bakers, conflicts, seenPackages) {
40
- let entries;
41
- try {
42
- entries = readdirSync(nodeModulesDir);
43
- } catch {
44
- return;
45
- }
46
- for (const entry of entries) {
47
- if (entry.startsWith(".")) continue;
48
- const entryPath = join(nodeModulesDir, entry);
49
- if (entry.startsWith("@")) {
50
- let scoped;
51
- try {
52
- scoped = readdirSync(entryPath);
53
- } catch {
54
- continue;
55
- }
56
- for (const scopedName of scoped) {
57
- readPackage(join(entryPath, scopedName), bakers, conflicts, seenPackages);
58
- }
59
- } else {
60
- readPackage(entryPath, bakers, conflicts, seenPackages);
61
- }
62
- }
53
+ let entries;
54
+ try {
55
+ entries = readdirSync(nodeModulesDir);
56
+ } catch {
57
+ return;
58
+ }
59
+ for (const entry of entries) {
60
+ if (entry.startsWith(".")) continue;
61
+ const entryPath = join(nodeModulesDir, entry);
62
+ if (entry.startsWith("@")) {
63
+ let scoped;
64
+ try {
65
+ scoped = readdirSync(entryPath);
66
+ } catch {
67
+ continue;
68
+ }
69
+ for (const scopedName of scoped) readPackage(join(entryPath, scopedName), bakers, conflicts, seenPackages);
70
+ } else readPackage(entryPath, bakers, conflicts, seenPackages);
71
+ }
63
72
  }
64
73
  function readPackage(packageDir, bakers, conflicts, seenPackages) {
65
- const pkgPath = join(packageDir, "package.json");
66
- if (!existsSync(pkgPath)) return;
67
- let pkg;
68
- try {
69
- pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
70
- } catch {
71
- return;
72
- }
73
- const name = pkg.name ?? packageDir;
74
- if (seenPackages.has(name)) return;
75
- seenPackages.add(name);
76
- const manifest = resolveManifest(pkg.flatland, name, conflicts);
77
- if (!manifest || manifest.length === 0) return;
78
- for (const decl of manifest) {
79
- const resolvedEntry = resolve(packageDir, decl.entry);
80
- const registration = {
81
- name: decl.name,
82
- description: decl.description,
83
- entry: decl.entry,
84
- packageName: name,
85
- resolvedEntry
86
- };
87
- const existing = bakers.get(decl.name);
88
- if (existing) {
89
- conflicts.push(
90
- `baker "${decl.name}" is registered by both "${existing.packageName}" and "${name}" \u2014 using "${existing.packageName}"`
91
- );
92
- continue;
93
- }
94
- bakers.set(decl.name, registration);
95
- }
74
+ const pkgPath = join(packageDir, "package.json");
75
+ if (!existsSync(pkgPath)) return;
76
+ let pkg;
77
+ try {
78
+ pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
79
+ } catch {
80
+ return;
81
+ }
82
+ const name = pkg.name ?? packageDir;
83
+ if (seenPackages.has(name)) return;
84
+ seenPackages.add(name);
85
+ const manifest = resolveManifest(pkg.flatland, name, conflicts);
86
+ if (!manifest || manifest.length === 0) return;
87
+ for (const decl of manifest) {
88
+ const resolvedEntry = resolve(packageDir, decl.entry);
89
+ const registration = {
90
+ name: decl.name,
91
+ description: decl.description,
92
+ entry: decl.entry,
93
+ packageName: name,
94
+ resolvedEntry
95
+ };
96
+ const existing = bakers.get(decl.name);
97
+ if (existing) {
98
+ conflicts.push(`baker "${decl.name}" is registered by both "${existing.packageName}" and "${name}" — using "${existing.packageName}"`);
99
+ continue;
100
+ }
101
+ bakers.set(decl.name, registration);
102
+ }
96
103
  }
104
+ /**
105
+ * Prefer `flatland.bake` (current); fall back to `flatland.bakers`
106
+ * (legacy) with a one-time deprecation warning per package.
107
+ */
97
108
  function resolveManifest(manifest, packageName, conflicts) {
98
- if (!manifest) return void 0;
99
- if (manifest.bake && manifest.bake.length > 0) return manifest.bake;
100
- if (manifest.bakers && manifest.bakers.length > 0) {
101
- conflicts.push(
102
- `"${packageName}" uses deprecated \`flatland.bakers\` \u2014 rename to \`flatland.bake\` (the legacy key is accepted for one release)`
103
- );
104
- return manifest.bakers;
105
- }
106
- return void 0;
109
+ if (!manifest) return void 0;
110
+ if (manifest.bake && manifest.bake.length > 0) return manifest.bake;
111
+ if (manifest.bakers && manifest.bakers.length > 0) {
112
+ conflicts.push(`"${packageName}" uses deprecated \`flatland.bakers\` — rename to \`flatland.bake\` (the legacy key is accepted for one release)`);
113
+ return manifest.bakers;
114
+ }
107
115
  }
108
- export {
109
- discoverBakers
110
- };
116
+ //#endregion
117
+ export { discoverBakers };
118
+
111
119
  //# sourceMappingURL=discovery.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/discovery.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport type {\n BakerRegistration,\n FlatlandManifest,\n FlatlandManifestEntry,\n} from './types.js'\n\ninterface PackageJson {\n name?: string\n flatland?: FlatlandManifest\n}\n\n/**\n * Discover registered bakers by walking `node_modules` near the current\n * working directory. Supports both flat and pnpm-symlinked layouts: every\n * `package.json` that declares a `flatland.bake` (or legacy\n * `flatland.bakers`) field is picked up.\n *\n * Conflicts (multiple packages registering the same baker name) are reported;\n * the first match wins and the rest are returned as warnings so the caller\n * can decide whether to fail or log.\n */\nexport function discoverBakers(cwd: string = process.cwd()): {\n bakers: BakerRegistration[]\n conflicts: string[]\n} {\n const seenPackages = new Set<string>()\n const bakers = new Map<string, BakerRegistration>()\n const conflicts: string[] = []\n\n // CWD-package self-discovery: if the user is iterating inside a package\n // that declares a baker, let them invoke it without symlinking into\n // node_modules first. Registers the baker before node_modules scans so a\n // local version always wins against an older installed copy.\n const selfDir = findPackageRoot(cwd)\n if (selfDir) {\n readPackage(selfDir, bakers, conflicts, seenPackages)\n }\n\n for (const nodeModulesDir of findNodeModulesDirs(cwd)) {\n scanNodeModules(nodeModulesDir, bakers, conflicts, seenPackages)\n }\n\n return { bakers: Array.from(bakers.values()), conflicts }\n}\n\n/** Walk upward from `start` until a directory with package.json is found. */\nfunction findPackageRoot(start: string): string | null {\n let dir = resolve(start)\n while (true) {\n if (existsSync(join(dir, 'package.json'))) return dir\n const parent = resolve(dir, '..')\n if (parent === dir) return null\n dir = parent\n }\n}\n\n/** Walk upward from `cwd` collecting each `node_modules` directory we find. */\nfunction findNodeModulesDirs(cwd: string): string[] {\n const dirs: string[] = []\n let dir = resolve(cwd)\n while (true) {\n const nm = join(dir, 'node_modules')\n if (existsSync(nm) && statSync(nm).isDirectory()) {\n dirs.push(nm)\n }\n const parent = resolve(dir, '..')\n if (parent === dir) break\n dir = parent\n }\n return dirs\n}\n\nfunction scanNodeModules(\n nodeModulesDir: string,\n bakers: Map<string, BakerRegistration>,\n conflicts: string[],\n seenPackages: Set<string>\n): void {\n let entries: string[]\n try {\n entries = readdirSync(nodeModulesDir)\n } catch {\n return\n }\n\n for (const entry of entries) {\n if (entry.startsWith('.')) continue\n const entryPath = join(nodeModulesDir, entry)\n\n if (entry.startsWith('@')) {\n // Scoped packages: @scope/name\n let scoped: string[]\n try {\n scoped = readdirSync(entryPath)\n } catch {\n continue\n }\n for (const scopedName of scoped) {\n readPackage(join(entryPath, scopedName), bakers, conflicts, seenPackages)\n }\n } else {\n readPackage(entryPath, bakers, conflicts, seenPackages)\n }\n }\n}\n\nfunction readPackage(\n packageDir: string,\n bakers: Map<string, BakerRegistration>,\n conflicts: string[],\n seenPackages: Set<string>\n): void {\n const pkgPath = join(packageDir, 'package.json')\n if (!existsSync(pkgPath)) return\n\n let pkg: PackageJson\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson\n } catch {\n return\n }\n\n const name = pkg.name ?? packageDir\n if (seenPackages.has(name)) return\n seenPackages.add(name)\n\n const manifest = resolveManifest(pkg.flatland, name, conflicts)\n if (!manifest || manifest.length === 0) return\n\n for (const decl of manifest) {\n const resolvedEntry = resolve(packageDir, decl.entry)\n const registration: BakerRegistration = {\n name: decl.name,\n description: decl.description,\n entry: decl.entry,\n packageName: name,\n resolvedEntry,\n }\n\n const existing = bakers.get(decl.name)\n if (existing) {\n conflicts.push(\n `baker \"${decl.name}\" is registered by both \"${existing.packageName}\" and \"${name}\" — using \"${existing.packageName}\"`\n )\n continue\n }\n bakers.set(decl.name, registration)\n }\n}\n\n/**\n * Prefer `flatland.bake` (current); fall back to `flatland.bakers`\n * (legacy) with a one-time deprecation warning per package.\n */\nfunction resolveManifest(\n manifest: FlatlandManifest | undefined,\n packageName: string,\n conflicts: string[]\n): FlatlandManifestEntry[] | undefined {\n if (!manifest) return undefined\n if (manifest.bake && manifest.bake.length > 0) return manifest.bake\n if (manifest.bakers && manifest.bakers.length > 0) {\n conflicts.push(\n `\"${packageName}\" uses deprecated \\`flatland.bakers\\` — rename to \\`flatland.bake\\` (the legacy key is accepted for one release)`\n )\n return manifest.bakers\n }\n return undefined\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa,YAAY,gBAAgB;AAChE,SAAS,SAAS,YAAY;AAsBvB,SAAS,eAAe,MAAc,QAAQ,IAAI,GAGvD;AACA,QAAM,eAAe,oBAAI,IAAY;AACrC,QAAM,SAAS,oBAAI,IAA+B;AAClD,QAAM,YAAsB,CAAC;AAM7B,QAAM,UAAU,gBAAgB,GAAG;AACnC,MAAI,SAAS;AACX,gBAAY,SAAS,QAAQ,WAAW,YAAY;AAAA,EACtD;AAEA,aAAW,kBAAkB,oBAAoB,GAAG,GAAG;AACrD,oBAAgB,gBAAgB,QAAQ,WAAW,YAAY;AAAA,EACjE;AAEA,SAAO,EAAE,QAAQ,MAAM,KAAK,OAAO,OAAO,CAAC,GAAG,UAAU;AAC1D;AAGA,SAAS,gBAAgB,OAA8B;AACrD,MAAI,MAAM,QAAQ,KAAK;AACvB,SAAO,MAAM;AACX,QAAI,WAAW,KAAK,KAAK,cAAc,CAAC,EAAG,QAAO;AAClD,UAAM,SAAS,QAAQ,KAAK,IAAI;AAChC,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;AAGA,SAAS,oBAAoB,KAAuB;AAClD,QAAM,OAAiB,CAAC;AACxB,MAAI,MAAM,QAAQ,GAAG;AACrB,SAAO,MAAM;AACX,UAAM,KAAK,KAAK,KAAK,cAAc;AACnC,QAAI,WAAW,EAAE,KAAK,SAAS,EAAE,EAAE,YAAY,GAAG;AAChD,WAAK,KAAK,EAAE;AAAA,IACd;AACA,UAAM,SAAS,QAAQ,KAAK,IAAI;AAChC,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,SAAS,gBACP,gBACA,QACA,WACA,cACM;AACN,MAAI;AACJ,MAAI;AACF,cAAU,YAAY,cAAc;AAAA,EACtC,QAAQ;AACN;AAAA,EACF;AAEA,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,WAAW,GAAG,EAAG;AAC3B,UAAM,YAAY,KAAK,gBAAgB,KAAK;AAE5C,QAAI,MAAM,WAAW,GAAG,GAAG;AAEzB,UAAI;AACJ,UAAI;AACF,iBAAS,YAAY,SAAS;AAAA,MAChC,QAAQ;AACN;AAAA,MACF;AACA,iBAAW,cAAc,QAAQ;AAC/B,oBAAY,KAAK,WAAW,UAAU,GAAG,QAAQ,WAAW,YAAY;AAAA,MAC1E;AAAA,IACF,OAAO;AACL,kBAAY,WAAW,QAAQ,WAAW,YAAY;AAAA,IACxD;AAAA,EACF;AACF;AAEA,SAAS,YACP,YACA,QACA,WACA,cACM;AACN,QAAM,UAAU,KAAK,YAAY,cAAc;AAC/C,MAAI,CAAC,WAAW,OAAO,EAAG;AAE1B,MAAI;AACJ,MAAI;AACF,UAAM,KAAK,MAAM,aAAa,SAAS,MAAM,CAAC;AAAA,EAChD,QAAQ;AACN;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,QAAQ;AACzB,MAAI,aAAa,IAAI,IAAI,EAAG;AAC5B,eAAa,IAAI,IAAI;AAErB,QAAM,WAAW,gBAAgB,IAAI,UAAU,MAAM,SAAS;AAC9D,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG;AAExC,aAAW,QAAQ,UAAU;AAC3B,UAAM,gBAAgB,QAAQ,YAAY,KAAK,KAAK;AACpD,UAAM,eAAkC;AAAA,MACtC,MAAM,KAAK;AAAA,MACX,aAAa,KAAK;AAAA,MAClB,OAAO,KAAK;AAAA,MACZ,aAAa;AAAA,MACb;AAAA,IACF;AAEA,UAAM,WAAW,OAAO,IAAI,KAAK,IAAI;AACrC,QAAI,UAAU;AACZ,gBAAU;AAAA,QACR,UAAU,KAAK,IAAI,4BAA4B,SAAS,WAAW,UAAU,IAAI,mBAAc,SAAS,WAAW;AAAA,MACrH;AACA;AAAA,IACF;AACA,WAAO,IAAI,KAAK,MAAM,YAAY;AAAA,EACpC;AACF;AAMA,SAAS,gBACP,UACA,aACA,WACqC;AACrC,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,QAAQ,SAAS,KAAK,SAAS,EAAG,QAAO,SAAS;AAC/D,MAAI,SAAS,UAAU,SAAS,OAAO,SAAS,GAAG;AACjD,cAAU;AAAA,MACR,IAAI,WAAW;AAAA,IACjB;AACA,WAAO,SAAS;AAAA,EAClB;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"file":"discovery.js","names":[],"sources":["../src/discovery.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport type { BakerRegistration, FlatlandManifest, FlatlandManifestEntry } from './types.js'\n\ninterface PackageJson {\n name?: string\n flatland?: FlatlandManifest\n}\n\n/**\n * Discover registered bakers by walking `node_modules` near the current\n * working directory. Supports both flat and pnpm-symlinked layouts: every\n * `package.json` that declares a `flatland.bake` (or legacy\n * `flatland.bakers`) field is picked up.\n *\n * Conflicts (multiple packages registering the same baker name) are reported;\n * the first match wins and the rest are returned as warnings so the caller\n * can decide whether to fail or log.\n */\nexport function discoverBakers(cwd: string = process.cwd()): {\n bakers: BakerRegistration[]\n conflicts: string[]\n} {\n const seenPackages = new Set<string>()\n const bakers = new Map<string, BakerRegistration>()\n const conflicts: string[] = []\n\n // CWD-package self-discovery: if the user is iterating inside a package\n // that declares a baker, let them invoke it without symlinking into\n // node_modules first. Registers the baker before node_modules scans so a\n // local version always wins against an older installed copy.\n const selfDir = findPackageRoot(cwd)\n if (selfDir) {\n readPackage(selfDir, bakers, conflicts, seenPackages)\n }\n\n for (const nodeModulesDir of findNodeModulesDirs(cwd)) {\n scanNodeModules(nodeModulesDir, bakers, conflicts, seenPackages)\n }\n\n return { bakers: Array.from(bakers.values()), conflicts }\n}\n\n/** Walk upward from `start` until a directory with package.json is found. */\nfunction findPackageRoot(start: string): string | null {\n let dir = resolve(start)\n while (true) {\n if (existsSync(join(dir, 'package.json'))) return dir\n const parent = resolve(dir, '..')\n if (parent === dir) return null\n dir = parent\n }\n}\n\n/** Walk upward from `cwd` collecting each `node_modules` directory we find. */\nfunction findNodeModulesDirs(cwd: string): string[] {\n const dirs: string[] = []\n let dir = resolve(cwd)\n while (true) {\n const nm = join(dir, 'node_modules')\n if (existsSync(nm) && statSync(nm).isDirectory()) {\n dirs.push(nm)\n }\n const parent = resolve(dir, '..')\n if (parent === dir) break\n dir = parent\n }\n return dirs\n}\n\nfunction scanNodeModules(\n nodeModulesDir: string,\n bakers: Map<string, BakerRegistration>,\n conflicts: string[],\n seenPackages: Set<string>\n): void {\n let entries: string[]\n try {\n entries = readdirSync(nodeModulesDir)\n } catch {\n return\n }\n\n for (const entry of entries) {\n if (entry.startsWith('.')) continue\n const entryPath = join(nodeModulesDir, entry)\n\n if (entry.startsWith('@')) {\n // Scoped packages: @scope/name\n let scoped: string[]\n try {\n scoped = readdirSync(entryPath)\n } catch {\n continue\n }\n for (const scopedName of scoped) {\n readPackage(join(entryPath, scopedName), bakers, conflicts, seenPackages)\n }\n } else {\n readPackage(entryPath, bakers, conflicts, seenPackages)\n }\n }\n}\n\nfunction readPackage(\n packageDir: string,\n bakers: Map<string, BakerRegistration>,\n conflicts: string[],\n seenPackages: Set<string>\n): void {\n const pkgPath = join(packageDir, 'package.json')\n if (!existsSync(pkgPath)) return\n\n let pkg: PackageJson\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson\n } catch {\n return\n }\n\n const name = pkg.name ?? packageDir\n if (seenPackages.has(name)) return\n seenPackages.add(name)\n\n const manifest = resolveManifest(pkg.flatland, name, conflicts)\n if (!manifest || manifest.length === 0) return\n\n for (const decl of manifest) {\n const resolvedEntry = resolve(packageDir, decl.entry)\n const registration: BakerRegistration = {\n name: decl.name,\n description: decl.description,\n entry: decl.entry,\n packageName: name,\n resolvedEntry,\n }\n\n const existing = bakers.get(decl.name)\n if (existing) {\n conflicts.push(\n `baker \"${decl.name}\" is registered by both \"${existing.packageName}\" and \"${name}\" — using \"${existing.packageName}\"`\n )\n continue\n }\n bakers.set(decl.name, registration)\n }\n}\n\n/**\n * Prefer `flatland.bake` (current); fall back to `flatland.bakers`\n * (legacy) with a one-time deprecation warning per package.\n */\nfunction resolveManifest(\n manifest: FlatlandManifest | undefined,\n packageName: string,\n conflicts: string[]\n): FlatlandManifestEntry[] | undefined {\n if (!manifest) return undefined\n if (manifest.bake && manifest.bake.length > 0) return manifest.bake\n if (manifest.bakers && manifest.bakers.length > 0) {\n conflicts.push(\n `\"${packageName}\" uses deprecated \\`flatland.bakers\\` — rename to \\`flatland.bake\\` (the legacy key is accepted for one release)`\n )\n return manifest.bakers\n }\n return undefined\n}\n"],"mappings":"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"}