@saykit/config 0.4.1 → 0.6.0

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 (32) hide show
  1. package/dist/commands/index.cjs +6 -5
  2. package/dist/commands/index.d.cts +1 -1
  3. package/dist/commands/index.d.mts +1 -1
  4. package/dist/commands/index.mjs +6 -4
  5. package/dist/features/catalogue/index.cjs +3 -4
  6. package/dist/features/catalogue/index.d.cts +1 -2
  7. package/dist/features/catalogue/index.d.mts +1 -2
  8. package/dist/features/catalogue/index.mjs +3 -3
  9. package/dist/features/loader/index.cjs +1 -1
  10. package/dist/features/loader/index.d.cts +1 -2
  11. package/dist/features/loader/index.d.mts +1 -2
  12. package/dist/features/loader/index.mjs +1 -1
  13. package/dist/features/messages/index.cjs +61 -8
  14. package/dist/features/messages/index.d.cts +8 -2
  15. package/dist/features/messages/index.d.mts +8 -2
  16. package/dist/features/messages/index.mjs +61 -8
  17. package/dist/{hash-CxBlj5Dz.cjs → hash-51ce8YiG.cjs} +0 -1
  18. package/dist/index.cjs +53 -3
  19. package/dist/index.d.cts +11 -5
  20. package/dist/index.d.mts +11 -5
  21. package/dist/index.mjs +29 -0
  22. package/dist/loader-A1uS7qkK.cjs +92 -0
  23. package/dist/loader-DLyuZzF2.mjs +87 -0
  24. package/dist/{shapes-Ba3xS10n.d.cts → shapes-CRlXtss0.d.cts} +48 -2
  25. package/dist/{shapes-NPfwlInG.d.mts → shapes-CRlXtss0.d.mts} +48 -2
  26. package/dist/{storage-CqKPYfCR.mjs → storage-B6mn0s3V.mjs} +1 -1
  27. package/dist/{storage-wS6e6qWM.cjs → storage-DqhzA5H8.cjs} +1 -2
  28. package/package.json +6 -11
  29. package/dist/chunk-CKQMccvm.cjs +0 -28
  30. package/dist/loader-BuAqYtfl.mjs +0 -191
  31. package/dist/loader-DATEQsXj.cjs +0 -198
  32. /package/dist/{hash-DyC8GzMa.mjs → hash-DvzpieJD.mjs} +0 -0
@@ -0,0 +1,92 @@
1
+ let node_path = require("node:path");
2
+ let node_fs = require("node:fs");
3
+ let node_module = require("node:module");
4
+ //#region src/features/loader/files.ts
5
+ const SUPPORTED_CONFIG_FILES = [
6
+ "saykit.config.js",
7
+ "saykit.config.cjs",
8
+ "saykit.config.mjs",
9
+ "saykit.config.ts",
10
+ "saykit.config.mts",
11
+ "saykit.config.cts"
12
+ ];
13
+ function getConfigFileCandidates(name) {
14
+ return SUPPORTED_CONFIG_FILES.map((file) => file.replace("saykit", name));
15
+ }
16
+ function findConfigFile(moduleName, projectDir) {
17
+ for (const fileName of getConfigFileCandidates(moduleName)) {
18
+ const id = (0, node_path.join)(projectDir, fileName);
19
+ if ((0, node_fs.existsSync)(id)) return { id };
20
+ }
21
+ return null;
22
+ }
23
+ //#endregion
24
+ //#region src/features/loader/module.ts
25
+ /** Requires `path` without leaving it in (or reading it from) the require cache. */
26
+ function requireFresh(require, path) {
27
+ const resolved = require.resolve(path);
28
+ delete require.cache[resolved];
29
+ const module = require(path);
30
+ delete require.cache[resolved];
31
+ return module?.default ?? module;
32
+ }
33
+ const TYPESCRIPT = /* @__PURE__ */ new Set([
34
+ ".ts",
35
+ ".mts",
36
+ ".cts"
37
+ ]);
38
+ /**
39
+ * Adds what the runtime cannot: why a config it could not read is one it will
40
+ * never read. Everything else is the config's own problem, and its error
41
+ * already describes that better than we could.
42
+ */
43
+ function diagnose(error, path) {
44
+ const code = error?.code;
45
+ if (code === "ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX") return "Enums, namespaces and parameter properties are not erasable, so no runtime will read them.";
46
+ if (code === "ERR_REQUIRE_ASYNC_MODULE") return "The config is loaded synchronously, so it cannot use top-level await.";
47
+ if ((code === "ERR_UNKNOWN_FILE_EXTENSION" || !code && error instanceof SyntaxError) && TYPESCRIPT.has((0, node_path.extname)(path).toLowerCase())) return "Reading this config needs Node 22.18+, or a runtime that loads TypeScript itself (Bun, Deno, tsx).";
48
+ return null;
49
+ }
50
+ /**
51
+ * Loads a config file as it sits on disk, leaving the runtime to deal with the
52
+ * extension. Nothing is copied or rewritten, so `__dirname`,
53
+ * `import.meta.dirname`, relative specifiers and `require.resolve` all resolve
54
+ * against the config's own directory.
55
+ */
56
+ function loadModule(path) {
57
+ try {
58
+ return requireFresh((0, node_module.createRequire)(path), path);
59
+ } catch (error) {
60
+ const hint = diagnose(error, path);
61
+ throw new Error(hint ? `Failed to import module. ${hint}` : "Failed to import module", { cause: error });
62
+ }
63
+ }
64
+ const js = loadModule;
65
+ const ts = loadModule;
66
+ const configLoaders = Object.freeze({
67
+ ".js": js,
68
+ ".mjs": js,
69
+ ".cjs": js,
70
+ ".ts": ts,
71
+ ".mts": ts,
72
+ ".cts": ts
73
+ });
74
+ //#endregion
75
+ //#region src/features/loader/resolve.ts
76
+ function resolveConfig(name = "saykit") {
77
+ const file = findConfigFile(name, process.cwd());
78
+ if (!file) throw new Error(`Could not find config file for "${name}"`);
79
+ const ext = (0, node_path.extname)(file.id).toLowerCase();
80
+ const load = ext in configLoaders ? configLoaders[ext] : null;
81
+ if (!load) throw new Error(`Unsupported config file type "${ext}" for "${name}"`);
82
+ const config = load(file.id);
83
+ if (!config || typeof config !== "object") throw new Error(`Invalid config file for "${name}"`);
84
+ return config;
85
+ }
86
+ //#endregion
87
+ Object.defineProperty(exports, "resolveConfig", {
88
+ enumerable: true,
89
+ get: function() {
90
+ return resolveConfig;
91
+ }
92
+ });
@@ -0,0 +1,87 @@
1
+ import { createRequire } from "node:module";
2
+ import { extname, join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ //#region src/features/loader/files.ts
5
+ const SUPPORTED_CONFIG_FILES = [
6
+ "saykit.config.js",
7
+ "saykit.config.cjs",
8
+ "saykit.config.mjs",
9
+ "saykit.config.ts",
10
+ "saykit.config.mts",
11
+ "saykit.config.cts"
12
+ ];
13
+ function getConfigFileCandidates(name) {
14
+ return SUPPORTED_CONFIG_FILES.map((file) => file.replace("saykit", name));
15
+ }
16
+ function findConfigFile(moduleName, projectDir) {
17
+ for (const fileName of getConfigFileCandidates(moduleName)) {
18
+ const id = join(projectDir, fileName);
19
+ if (existsSync(id)) return { id };
20
+ }
21
+ return null;
22
+ }
23
+ //#endregion
24
+ //#region src/features/loader/module.ts
25
+ /** Requires `path` without leaving it in (or reading it from) the require cache. */
26
+ function requireFresh(require, path) {
27
+ const resolved = require.resolve(path);
28
+ delete require.cache[resolved];
29
+ const module = require(path);
30
+ delete require.cache[resolved];
31
+ return module?.default ?? module;
32
+ }
33
+ const TYPESCRIPT = /* @__PURE__ */ new Set([
34
+ ".ts",
35
+ ".mts",
36
+ ".cts"
37
+ ]);
38
+ /**
39
+ * Adds what the runtime cannot: why a config it could not read is one it will
40
+ * never read. Everything else is the config's own problem, and its error
41
+ * already describes that better than we could.
42
+ */
43
+ function diagnose(error, path) {
44
+ const code = error?.code;
45
+ if (code === "ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX") return "Enums, namespaces and parameter properties are not erasable, so no runtime will read them.";
46
+ if (code === "ERR_REQUIRE_ASYNC_MODULE") return "The config is loaded synchronously, so it cannot use top-level await.";
47
+ if ((code === "ERR_UNKNOWN_FILE_EXTENSION" || !code && error instanceof SyntaxError) && TYPESCRIPT.has(extname(path).toLowerCase())) return "Reading this config needs Node 22.18+, or a runtime that loads TypeScript itself (Bun, Deno, tsx).";
48
+ return null;
49
+ }
50
+ /**
51
+ * Loads a config file as it sits on disk, leaving the runtime to deal with the
52
+ * extension. Nothing is copied or rewritten, so `__dirname`,
53
+ * `import.meta.dirname`, relative specifiers and `require.resolve` all resolve
54
+ * against the config's own directory.
55
+ */
56
+ function loadModule(path) {
57
+ try {
58
+ return requireFresh(createRequire(path), path);
59
+ } catch (error) {
60
+ const hint = diagnose(error, path);
61
+ throw new Error(hint ? `Failed to import module. ${hint}` : "Failed to import module", { cause: error });
62
+ }
63
+ }
64
+ const js = loadModule;
65
+ const ts = loadModule;
66
+ const configLoaders = Object.freeze({
67
+ ".js": js,
68
+ ".mjs": js,
69
+ ".cjs": js,
70
+ ".ts": ts,
71
+ ".mts": ts,
72
+ ".cts": ts
73
+ });
74
+ //#endregion
75
+ //#region src/features/loader/resolve.ts
76
+ function resolveConfig(name = "saykit") {
77
+ const file = findConfigFile(name, process.cwd());
78
+ if (!file) throw new Error(`Could not find config file for "${name}"`);
79
+ const ext = extname(file.id).toLowerCase();
80
+ const load = ext in configLoaders ? configLoaders[ext] : null;
81
+ if (!load) throw new Error(`Unsupported config file type "${ext}" for "${name}"`);
82
+ const config = load(file.id);
83
+ if (!config || typeof config !== "object") throw new Error(`Invalid config file for "${name}"`);
84
+ return config;
85
+ }
86
+ //#endregion
87
+ export { resolveConfig as t };
@@ -1,6 +1,5 @@
1
1
  import picomatch from "picomatch";
2
2
  import * as z from "zod";
3
-
4
3
  //#region src/shapes.d.ts
5
4
  declare const Message: z.ZodObject<{
6
5
  message: z.ZodString;
@@ -29,10 +28,26 @@ declare const Transformer: z.ZodObject<{
29
28
  transform: z.ZodCustom<(code: string, id: string) => string, (code: string, id: string) => string>;
30
29
  }, z.core.$strip>;
31
30
  type Transformer = z.infer<typeof Transformer>;
31
+ /**
32
+ * A message declared in the config rather than found in source. The shorthand
33
+ * is the source string; the object form adds the metadata a descriptor would
34
+ * otherwise carry.
35
+ */
36
+ declare const DeclaredMessage: z.ZodUnion<[z.ZodString, z.ZodObject<{
37
+ message: z.ZodString;
38
+ context: z.ZodOptional<z.ZodString>;
39
+ comments: z.ZodOptional<z.ZodArray<z.ZodString>>;
40
+ }, z.core.$strip>]>;
41
+ type DeclaredMessage = z.infer<typeof DeclaredMessage>;
32
42
  declare const Bucket: z.ZodPipe<z.ZodObject<{
33
43
  include: z.ZodArray<z.ZodString>;
34
44
  exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
35
45
  output: z.ZodTemplateLiteral<`${string}{locale}${string}.{extension}`>;
46
+ messages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
47
+ message: z.ZodString;
48
+ context: z.ZodOptional<z.ZodString>;
49
+ comments: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
+ }, z.core.$strip>]>>>;
36
51
  formatter: z.ZodObject<{
37
52
  extension: z.ZodTemplateLiteral<`.${string}`>;
38
53
  parse: z.ZodCustom<(content: string) => Message[], (content: string) => Message[]>;
@@ -77,6 +92,14 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
77
92
  transform: (code: string, id: string) => string;
78
93
  }[]>>;
79
94
  }, z.core.$strip>, z.ZodTransform<{
95
+ messages: {
96
+ message: string;
97
+ comments: string[];
98
+ references: string[];
99
+ translation?: string | undefined;
100
+ id?: string | undefined;
101
+ context?: string | undefined;
102
+ }[];
80
103
  match: (id: string) => boolean;
81
104
  output: `${string}{locale}${string}.{extension}` & {
82
105
  match: picomatch.Matcher;
@@ -127,6 +150,11 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
127
150
  transform: (code: string, id: string) => string;
128
151
  };
129
152
  exclude?: string[] | undefined;
153
+ messages?: Record<string, string | {
154
+ message: string;
155
+ context?: string | undefined;
156
+ comments?: string[] | undefined;
157
+ }> | undefined;
130
158
  }>>;
131
159
  type Bucket = z.infer<typeof Bucket>;
132
160
  declare const Config: z.ZodObject<{
@@ -136,6 +164,11 @@ declare const Config: z.ZodObject<{
136
164
  include: z.ZodArray<z.ZodString>;
137
165
  exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
138
166
  output: z.ZodTemplateLiteral<`${string}{locale}${string}.{extension}`>;
167
+ messages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
168
+ message: z.ZodString;
169
+ context: z.ZodOptional<z.ZodString>;
170
+ comments: z.ZodOptional<z.ZodArray<z.ZodString>>;
171
+ }, z.core.$strip>]>>>;
139
172
  formatter: z.ZodObject<{
140
173
  extension: z.ZodTemplateLiteral<`.${string}`>;
141
174
  parse: z.ZodCustom<(content: string) => Message[], (content: string) => Message[]>;
@@ -180,6 +213,14 @@ declare const Config: z.ZodObject<{
180
213
  transform: (code: string, id: string) => string;
181
214
  }[]>>;
182
215
  }, z.core.$strip>, z.ZodTransform<{
216
+ messages: {
217
+ message: string;
218
+ comments: string[];
219
+ references: string[];
220
+ translation?: string | undefined;
221
+ id?: string | undefined;
222
+ context?: string | undefined;
223
+ }[];
183
224
  match: (id: string) => boolean;
184
225
  output: `${string}{locale}${string}.{extension}` & {
185
226
  match: picomatch.Matcher;
@@ -230,8 +271,13 @@ declare const Config: z.ZodObject<{
230
271
  transform: (code: string, id: string) => string;
231
272
  };
232
273
  exclude?: string[] | undefined;
274
+ messages?: Record<string, string | {
275
+ message: string;
276
+ context?: string | undefined;
277
+ comments?: string[] | undefined;
278
+ }> | undefined;
233
279
  }>>>;
234
280
  }, z.core.$strip>;
235
281
  type Config = z.infer<typeof Config>;
236
282
  //#endregion
237
- export { Transformer as a, Message as i, Config as n, Formatter as r, Bucket as t };
283
+ export { Message as a, Formatter as i, Config as n, Transformer as o, DeclaredMessage as r, Bucket as t };
@@ -1,6 +1,5 @@
1
1
  import picomatch from "picomatch";
2
2
  import * as z from "zod";
3
-
4
3
  //#region src/shapes.d.ts
5
4
  declare const Message: z.ZodObject<{
6
5
  message: z.ZodString;
@@ -29,10 +28,26 @@ declare const Transformer: z.ZodObject<{
29
28
  transform: z.ZodCustom<(code: string, id: string) => string, (code: string, id: string) => string>;
30
29
  }, z.core.$strip>;
31
30
  type Transformer = z.infer<typeof Transformer>;
31
+ /**
32
+ * A message declared in the config rather than found in source. The shorthand
33
+ * is the source string; the object form adds the metadata a descriptor would
34
+ * otherwise carry.
35
+ */
36
+ declare const DeclaredMessage: z.ZodUnion<[z.ZodString, z.ZodObject<{
37
+ message: z.ZodString;
38
+ context: z.ZodOptional<z.ZodString>;
39
+ comments: z.ZodOptional<z.ZodArray<z.ZodString>>;
40
+ }, z.core.$strip>]>;
41
+ type DeclaredMessage = z.infer<typeof DeclaredMessage>;
32
42
  declare const Bucket: z.ZodPipe<z.ZodObject<{
33
43
  include: z.ZodArray<z.ZodString>;
34
44
  exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
35
45
  output: z.ZodTemplateLiteral<`${string}{locale}${string}.{extension}`>;
46
+ messages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
47
+ message: z.ZodString;
48
+ context: z.ZodOptional<z.ZodString>;
49
+ comments: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
+ }, z.core.$strip>]>>>;
36
51
  formatter: z.ZodObject<{
37
52
  extension: z.ZodTemplateLiteral<`.${string}`>;
38
53
  parse: z.ZodCustom<(content: string) => Message[], (content: string) => Message[]>;
@@ -77,6 +92,14 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
77
92
  transform: (code: string, id: string) => string;
78
93
  }[]>>;
79
94
  }, z.core.$strip>, z.ZodTransform<{
95
+ messages: {
96
+ message: string;
97
+ comments: string[];
98
+ references: string[];
99
+ translation?: string | undefined;
100
+ id?: string | undefined;
101
+ context?: string | undefined;
102
+ }[];
80
103
  match: (id: string) => boolean;
81
104
  output: `${string}{locale}${string}.{extension}` & {
82
105
  match: picomatch.Matcher;
@@ -127,6 +150,11 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
127
150
  transform: (code: string, id: string) => string;
128
151
  };
129
152
  exclude?: string[] | undefined;
153
+ messages?: Record<string, string | {
154
+ message: string;
155
+ context?: string | undefined;
156
+ comments?: string[] | undefined;
157
+ }> | undefined;
130
158
  }>>;
131
159
  type Bucket = z.infer<typeof Bucket>;
132
160
  declare const Config: z.ZodObject<{
@@ -136,6 +164,11 @@ declare const Config: z.ZodObject<{
136
164
  include: z.ZodArray<z.ZodString>;
137
165
  exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
138
166
  output: z.ZodTemplateLiteral<`${string}{locale}${string}.{extension}`>;
167
+ messages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
168
+ message: z.ZodString;
169
+ context: z.ZodOptional<z.ZodString>;
170
+ comments: z.ZodOptional<z.ZodArray<z.ZodString>>;
171
+ }, z.core.$strip>]>>>;
139
172
  formatter: z.ZodObject<{
140
173
  extension: z.ZodTemplateLiteral<`.${string}`>;
141
174
  parse: z.ZodCustom<(content: string) => Message[], (content: string) => Message[]>;
@@ -180,6 +213,14 @@ declare const Config: z.ZodObject<{
180
213
  transform: (code: string, id: string) => string;
181
214
  }[]>>;
182
215
  }, z.core.$strip>, z.ZodTransform<{
216
+ messages: {
217
+ message: string;
218
+ comments: string[];
219
+ references: string[];
220
+ translation?: string | undefined;
221
+ id?: string | undefined;
222
+ context?: string | undefined;
223
+ }[];
183
224
  match: (id: string) => boolean;
184
225
  output: `${string}{locale}${string}.{extension}` & {
185
226
  match: picomatch.Matcher;
@@ -230,8 +271,13 @@ declare const Config: z.ZodObject<{
230
271
  transform: (code: string, id: string) => string;
231
272
  };
232
273
  exclude?: string[] | undefined;
274
+ messages?: Record<string, string | {
275
+ message: string;
276
+ context?: string | undefined;
277
+ comments?: string[] | undefined;
278
+ }> | undefined;
233
279
  }>>>;
234
280
  }, z.core.$strip>;
235
281
  type Config = z.infer<typeof Config>;
236
282
  //#endregion
237
- export { Transformer as a, Message as i, Config as n, Formatter as r, Bucket as t };
283
+ export { Message as a, Formatter as i, Config as n, Transformer as o, DeclaredMessage as r, Bucket as t };
@@ -1,4 +1,4 @@
1
- import { t as generateHash } from "./hash-DyC8GzMa.mjs";
1
+ import { t as generateHash } from "./hash-DvzpieJD.mjs";
2
2
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { dirname, parse, resolve } from "node:path";
4
4
  //#region src/features/catalogue/merge.ts
@@ -1,5 +1,4 @@
1
- require("./chunk-CKQMccvm.cjs");
2
- const require_hash = require("./hash-CxBlj5Dz.cjs");
1
+ const require_hash = require("./hash-51ce8YiG.cjs");
3
2
  let node_fs_promises = require("node:fs/promises");
4
3
  let node_path = require("node:path");
5
4
  //#region src/features/catalogue/merge.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saykit/config",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "CLI and configuration tooling for saykit",
5
5
  "keywords": [
6
6
  "cli",
@@ -73,22 +73,17 @@
73
73
  "provenance": true
74
74
  },
75
75
  "dependencies": {
76
- "@commander-js/extra-typings": "^14.0.0",
77
- "commander": "^14.0.3",
78
- "js-sha256": "^0.11.1",
76
+ "@commander-js/extra-typings": "^15.0.0",
77
+ "commander": "^15.0.0",
78
+ "js-sha256": "^0.12.0",
79
79
  "picomatch": "^4.0.5",
80
80
  "zod": "^4.4.3"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@types/picomatch": "^4.0.3"
84
84
  },
85
- "peerDependencies": {
86
- "typescript": "*"
87
- },
88
- "peerDependenciesMeta": {
89
- "typescript": {
90
- "optional": true
91
- }
85
+ "engines": {
86
+ "node": ">=22.18"
92
87
  },
93
88
  "scripts": {
94
89
  "check": "tsc --noEmit",
@@ -1,28 +0,0 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
- //#endregion
23
- Object.defineProperty(exports, "__toESM", {
24
- enumerable: true,
25
- get: function() {
26
- return __toESM;
27
- }
28
- });
@@ -1,191 +0,0 @@
1
- import nodeModule, { createRequire } from "node:module";
2
- import { dirname, extname, join, parse } from "node:path";
3
- import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
- import { createHash } from "node:crypto";
5
- import { tmpdir, userInfo } from "node:os";
6
- //#region src/features/loader/files.ts
7
- const SUPPORTED_CONFIG_FILES = [
8
- "saykit.config.js",
9
- "saykit.config.cjs",
10
- "saykit.config.mjs",
11
- "saykit.config.ts",
12
- "saykit.config.mts",
13
- "saykit.config.cts"
14
- ];
15
- function getConfigFileCandidates(name) {
16
- return SUPPORTED_CONFIG_FILES.map((file) => file.replace("saykit", name));
17
- }
18
- function findConfigFile(moduleName, projectDir) {
19
- for (const fileName of getConfigFileCandidates(moduleName)) {
20
- const id = join(projectDir, fileName);
21
- if (existsSync(id)) return { id };
22
- }
23
- return null;
24
- }
25
- //#endregion
26
- //#region src/features/loader/module.ts
27
- function findUpwards(fromPath, visit) {
28
- let dir = dirname(fromPath);
29
- const { root } = parse(dir);
30
- while (true) {
31
- const found = visit(dir);
32
- if (found !== null) return found;
33
- if (dir === root) return null;
34
- dir = dirname(dir);
35
- }
36
- }
37
- function findNearestTsConfig(fromPath) {
38
- return findUpwards(fromPath, (dir) => {
39
- const candidate = join(dir, "tsconfig.json");
40
- return existsSync(candidate) ? candidate : null;
41
- });
42
- }
43
- function digest(value) {
44
- return createHash("sha1").update(value).digest("hex").slice(0, 16);
45
- }
46
- /**
47
- * Builds are executable, so they belong beside the project's dependencies. The
48
- * fallback lands in a shared temp directory instead, where a fixed path would
49
- * let any other user on the machine plant code we later require — hence a
50
- * per-user directory, created private in `compileToCache`.
51
- */
52
- function findCacheDir(fromPath) {
53
- const nodeModules = findUpwards(fromPath, (dir) => {
54
- const candidate = join(dir, "node_modules");
55
- return existsSync(candidate) ? candidate : null;
56
- });
57
- if (nodeModules) return join(nodeModules, ".cache", "saykit", "config");
58
- const { uid, username, homedir } = userInfo();
59
- return join(tmpdir(), `saykit-config-${digest(`${uid}\0${username}\0${homedir}`)}`);
60
- }
61
- function mtimeOf(path) {
62
- return path ? statSync(path).mtimeMs : 0;
63
- }
64
- /** Removes every build of `file` in `dir` except `keep`. */
65
- function pruneCache(dir, file, keep) {
66
- for (const entry of readdirSync(dir)) {
67
- if (!entry.startsWith(`${file}.`) || join(dir, entry) === keep) continue;
68
- try {
69
- unlinkSync(join(dir, entry));
70
- } catch {}
71
- }
72
- }
73
- /**
74
- * Preferred: the classic TypeScript compiler API, which honours the project's
75
- * tsconfig and emits CommonJS. TypeScript 7's root export no longer ships it,
76
- * and the dependency is optional, so this gives up when it isn't there.
77
- */
78
- function transpileWithCompilerApi(source, tsConfigPath, require) {
79
- let ts;
80
- try {
81
- ts = require("typescript");
82
- } catch {
83
- return null;
84
- }
85
- if (typeof ts?.transpileModule !== "function" || typeof ts.sys?.readFile !== "function") return null;
86
- const { config, error } = tsConfigPath ? ts.readConfigFile(tsConfigPath, ts.sys.readFile) : {
87
- config: {},
88
- error: null
89
- };
90
- if (error) throw error;
91
- config.compilerOptions = {
92
- ...config.compilerOptions,
93
- allowJs: true,
94
- esModuleInterop: true,
95
- noEmit: false,
96
- module: ts.ModuleKind.CommonJS,
97
- moduleResolution: ts.ModuleResolutionKind.NodeJs,
98
- target: ts.ScriptTarget.ES2022
99
- };
100
- return {
101
- code: ts.transpileModule(source, config).outputText,
102
- extension: "cjs"
103
- };
104
- }
105
- /**
106
- * Fallback: Node erases the types itself. It ignores tsconfig and leaves the
107
- * module syntax alone, so the extension has to follow the source.
108
- */
109
- function transpileWithNode(source) {
110
- if (typeof nodeModule.stripTypeScriptTypes !== "function") throw new Error("Loading TypeScript config files requires the TypeScript compiler API (typescript <= 6), Node 22.13+, or a runtime that loads TypeScript itself (Bun, Deno, tsx)");
111
- const code = nodeModule.stripTypeScriptTypes(source, { mode: "transform" });
112
- return {
113
- code,
114
- extension: /^\s*(?:import|export)[\s({]/m.test(code) ? "mjs" : "cjs"
115
- };
116
- }
117
- /**
118
- * Writes a plain JavaScript copy of `path` next to the project's dependencies
119
- * and returns it. Copies are named `<file>.<inputs>.<extension>`, so a build
120
- * can be reused until its inputs change, and earlier builds of the same file
121
- * can be pruned once it does.
122
- */
123
- function compileToCache(path, require) {
124
- const tsConfigPath = findNearestTsConfig(path);
125
- const dir = findCacheDir(path);
126
- const file = digest(path);
127
- const inputs = digest(`${mtimeOf(path)}\0${tsConfigPath}\0${mtimeOf(tsConfigPath)}`);
128
- const cached = ["cjs", "mjs"].map((ext) => join(dir, `${file}.${inputs}.${ext}`)).find(existsSync);
129
- if (cached) return cached;
130
- const source = readFileSync(path, "utf8");
131
- const { code, extension } = transpileWithCompilerApi(source, tsConfigPath, require) ?? transpileWithNode(source);
132
- const target = join(dir, `${file}.${inputs}.${extension}`);
133
- mkdirSync(dir, {
134
- recursive: true,
135
- mode: 448
136
- });
137
- writeFileSync(target, code, { mode: 384 });
138
- pruneCache(dir, file, target);
139
- return target;
140
- }
141
- /**
142
- * Bun, Deno and hooks like tsx or ts-node load TypeScript better than we can,
143
- * resolving tsconfig `paths` and the project's own module resolution with it.
144
- */
145
- function runtimeLoadsTypeScript(require) {
146
- return Boolean(process.versions.bun || globalThis.Deno || require.extensions?.[".ts"] || Reflect.get(process, Symbol.for("ts-node.register.instance")));
147
- }
148
- /** Requires `path` without leaving it in (or reading it from) the require cache. */
149
- function requireFresh(require, path) {
150
- const resolved = require.resolve(path);
151
- delete require.cache[resolved];
152
- const module = require(path);
153
- delete require.cache[resolved];
154
- return module?.default ?? module;
155
- }
156
- /**
157
- * Loads a config file, compiling it first unless the runtime reads TypeScript
158
- * on its own.
159
- */
160
- function loadModule(path) {
161
- const require = createRequire(path);
162
- try {
163
- return requireFresh(require, runtimeLoadsTypeScript(require) ? path : compileToCache(path, require));
164
- } catch (error) {
165
- throw new Error("Failed to import module", { cause: error });
166
- }
167
- }
168
- const js = loadModule;
169
- const ts = loadModule;
170
- const configLoaders = Object.freeze({
171
- ".js": js,
172
- ".mjs": js,
173
- ".cjs": js,
174
- ".ts": ts,
175
- ".mts": ts,
176
- ".cts": ts
177
- });
178
- //#endregion
179
- //#region src/features/loader/resolve.ts
180
- function resolveConfig(name = "saykit") {
181
- const file = findConfigFile(name, process.cwd());
182
- if (!file) throw new Error(`Could not find config file for "${name}"`);
183
- const ext = extname(file.id).toLowerCase();
184
- const load = ext in configLoaders ? configLoaders[ext] : null;
185
- if (!load) throw new Error(`Unsupported config file type "${ext}" for "${name}"`);
186
- const config = load(file.id);
187
- if (!config || typeof config !== "object") throw new Error(`Invalid config file for "${name}"`);
188
- return config;
189
- }
190
- //#endregion
191
- export { resolveConfig as t };