@saykit/config 0.4.1 → 0.5.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.
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
- require("../chunk-CKQMccvm.cjs");
3
- const require_storage = require("../storage-wS6e6qWM.cjs");
4
- const require_loader = require("../loader-DATEQsXj.cjs");
2
+ const require_storage = require("../storage-DqhzA5H8.cjs");
3
+ const require_loader = require("../loader-A1uS7qkK.cjs");
5
4
  let _commander_js_extra_typings = require("@commander-js/extra-typings");
6
5
  let node_fs_promises = require("node:fs/promises");
7
6
  let node_path = require("node:path");
@@ -145,7 +144,7 @@ function exists(path) {
145
144
  var BucketExtractWorker = class extends BucketWorker {
146
145
  #indexedMessagesByPath = /* @__PURE__ */ new Map();
147
146
  get #indexedMessages() {
148
- return Array.from(this.#indexedMessagesByPath.values()).flat();
147
+ return [...this.bucket.messages, ...Array.from(this.#indexedMessagesByPath.values()).flat()];
149
148
  }
150
149
  async #indexPath(path) {
151
150
  const relativePath = normalisePathForLogs(path);
@@ -167,8 +166,10 @@ var BucketExtractWorker = class extends BucketWorker {
167
166
  this.logger.info(`Scanning bucket: ${this.bucket.include}`);
168
167
  const paths = await globBucket(this.bucket);
169
168
  this.logger.step(`Found ${paths.length} file(s)`);
169
+ if (this.bucket.messages.length) this.logger.step(`Including ${this.bucket.messages.length} message(s) declared in the config`);
170
170
  await Promise.all(paths.map((p) => this.#indexPath(p)));
171
- this.logger.info(`Total extracted messages: ${this.#indexedMessages.length}`);
171
+ const extracted = Array.from(this.#indexedMessagesByPath.values()).flat().length;
172
+ this.logger.info(`Total extracted messages: ${extracted}`);
172
173
  }
173
174
  async write() {
174
175
  const mergedMessages = require_storage.mergeExtractedMessages(this.#indexedMessages);
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as mergeExtractedMessages, i as expandBucketOutputPath, n as writeCatalogueMessages, o as pruneLocaleMessages, t as readCatalogueMessages } from "../storage-CqKPYfCR.mjs";
3
- import { t as resolveConfig } from "../loader-BuAqYtfl.mjs";
2
+ import { a as mergeExtractedMessages, i as expandBucketOutputPath, n as writeCatalogueMessages, o as pruneLocaleMessages, t as readCatalogueMessages } from "../storage-B6mn0s3V.mjs";
3
+ import { t as resolveConfig } from "../loader-DLyuZzF2.mjs";
4
4
  import { Command, program } from "@commander-js/extra-typings";
5
5
  import { access, glob, readFile, stat, watch } from "node:fs/promises";
6
6
  import { join, relative } from "node:path";
@@ -144,7 +144,7 @@ function exists(path) {
144
144
  var BucketExtractWorker = class extends BucketWorker {
145
145
  #indexedMessagesByPath = /* @__PURE__ */ new Map();
146
146
  get #indexedMessages() {
147
- return Array.from(this.#indexedMessagesByPath.values()).flat();
147
+ return [...this.bucket.messages, ...Array.from(this.#indexedMessagesByPath.values()).flat()];
148
148
  }
149
149
  async #indexPath(path) {
150
150
  const relativePath = normalisePathForLogs(path);
@@ -166,8 +166,10 @@ var BucketExtractWorker = class extends BucketWorker {
166
166
  this.logger.info(`Scanning bucket: ${this.bucket.include}`);
167
167
  const paths = await globBucket(this.bucket);
168
168
  this.logger.step(`Found ${paths.length} file(s)`);
169
+ if (this.bucket.messages.length) this.logger.step(`Including ${this.bucket.messages.length} message(s) declared in the config`);
169
170
  await Promise.all(paths.map((p) => this.#indexPath(p)));
170
- this.logger.info(`Total extracted messages: ${this.#indexedMessages.length}`);
171
+ const extracted = Array.from(this.#indexedMessagesByPath.values()).flat().length;
172
+ this.logger.info(`Total extracted messages: ${extracted}`);
171
173
  }
172
174
  async write() {
173
175
  const mergedMessages = mergeExtractedMessages(this.#indexedMessages);
@@ -1,7 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("../../chunk-CKQMccvm.cjs");
3
- const require_hash = require("../../hash-CxBlj5Dz.cjs");
4
- const require_storage = require("../../storage-wS6e6qWM.cjs");
2
+ const require_hash = require("../../hash-51ce8YiG.cjs");
3
+ const require_storage = require("../../storage-DqhzA5H8.cjs");
5
4
  let node_path = require("node:path");
6
5
  //#region src/features/catalogue/record.ts
7
6
  /**
@@ -13,7 +12,7 @@ function resolveFallbackChain(config, locale) {
13
12
  const source = config.locales[0];
14
13
  const configured = config.fallbackLocales?.[locale];
15
14
  const fallbacks = configured ? Array.isArray(configured) ? configured : [configured] : [];
16
- return Array.from(new Set([
15
+ return Array.from(/* @__PURE__ */ new Set([
17
16
  locale,
18
17
  ...fallbacks,
19
18
  source
@@ -1,5 +1,4 @@
1
- import { i as Message, n as Config, t as Bucket } from "../../shapes-Ba3xS10n.cjs";
2
-
1
+ import { a as Message, n as Config, t as Bucket } from "../../shapes-CRlXtss0.cjs";
3
2
  //#region src/features/catalogue/merge.d.ts
4
3
  declare function mergeExtractedMessages(messages: Message[]): {
5
4
  message: string;
@@ -1,5 +1,4 @@
1
- import { i as Message, n as Config, t as Bucket } from "../../shapes-NPfwlInG.mjs";
2
-
1
+ import { a as Message, n as Config, t as Bucket } from "../../shapes-CRlXtss0.mjs";
3
2
  //#region src/features/catalogue/merge.d.ts
4
3
  declare function mergeExtractedMessages(messages: Message[]): {
5
4
  message: string;
@@ -1,5 +1,5 @@
1
- import { t as generateHash } from "../../hash-DyC8GzMa.mjs";
2
- import { a as mergeExtractedMessages, i as expandBucketOutputPath, n as writeCatalogueMessages, o as pruneLocaleMessages, r as declarationPathFor, t as readCatalogueMessages } from "../../storage-CqKPYfCR.mjs";
1
+ import { t as generateHash } from "../../hash-DvzpieJD.mjs";
2
+ import { a as mergeExtractedMessages, i as expandBucketOutputPath, n as writeCatalogueMessages, o as pruneLocaleMessages, r as declarationPathFor, t as readCatalogueMessages } from "../../storage-B6mn0s3V.mjs";
3
3
  import { resolve } from "node:path";
4
4
  //#region src/features/catalogue/record.ts
5
5
  /**
@@ -11,7 +11,7 @@ function resolveFallbackChain(config, locale) {
11
11
  const source = config.locales[0];
12
12
  const configured = config.fallbackLocales?.[locale];
13
13
  const fallbacks = configured ? Array.isArray(configured) ? configured : [configured] : [];
14
- return Array.from(new Set([
14
+ return Array.from(/* @__PURE__ */ new Set([
15
15
  locale,
16
16
  ...fallbacks,
17
17
  source
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_loader = require("../../loader-DATEQsXj.cjs");
2
+ const require_loader = require("../../loader-A1uS7qkK.cjs");
3
3
  exports.resolveConfig = require_loader.resolveConfig;
@@ -1,5 +1,4 @@
1
- import { n as Config } from "../../shapes-Ba3xS10n.cjs";
2
-
1
+ import { n as Config } from "../../shapes-CRlXtss0.cjs";
3
2
  //#region src/features/loader/resolve.d.ts
4
3
  declare function resolveConfig(name?: string): Config;
5
4
  //#endregion
@@ -1,5 +1,4 @@
1
- import { n as Config } from "../../shapes-NPfwlInG.mjs";
2
-
1
+ import { n as Config } from "../../shapes-CRlXtss0.mjs";
3
2
  //#region src/features/loader/resolve.d.ts
4
3
  declare function resolveConfig(name?: string): Config;
5
4
  //#endregion
@@ -1,2 +1,2 @@
1
- import { t as resolveConfig } from "../../loader-BuAqYtfl.mjs";
1
+ import { t as resolveConfig } from "../../loader-DLyuZzF2.mjs";
2
2
  export { resolveConfig };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_hash = require("../../hash-CxBlj5Dz.cjs");
2
+ const require_hash = require("../../hash-51ce8YiG.cjs");
3
3
  //#region src/features/messages/identifier.ts
4
4
  const AUTO_INCREMENT_IDENTIFIER = Symbol("auto-increment");
5
5
  function assignSequenceIdentifiers(message, sequence = { current: 0 }) {
@@ -24,12 +24,15 @@ var Base = class {
24
24
  }
25
25
  };
26
26
  var LiteralMessage = class extends Base {
27
+ text;
27
28
  constructor(text) {
28
29
  super();
29
30
  this.text = text;
30
31
  }
31
32
  };
32
33
  var ArgumentMessage = class extends Base {
34
+ identifier;
35
+ expression;
33
36
  constructor(identifier, expression) {
34
37
  super();
35
38
  this.identifier = identifier;
@@ -37,6 +40,9 @@ var ArgumentMessage = class extends Base {
37
40
  }
38
41
  };
39
42
  var ElementMessage = class extends Base {
43
+ identifier;
44
+ children;
45
+ expression;
40
46
  constructor(identifier, children, expression) {
41
47
  super();
42
48
  this.identifier = identifier;
@@ -45,6 +51,10 @@ var ElementMessage = class extends Base {
45
51
  }
46
52
  };
47
53
  var ChoiceMessage = class extends Base {
54
+ kind;
55
+ identifier;
56
+ branches;
57
+ expression;
48
58
  constructor(kind, identifier, branches, expression) {
49
59
  super();
50
60
  this.kind = kind;
@@ -54,6 +64,12 @@ var ChoiceMessage = class extends Base {
54
64
  }
55
65
  };
56
66
  var CompositeMessage = class extends Base {
67
+ descriptor;
68
+ comments;
69
+ references;
70
+ children;
71
+ accessor;
72
+ whitespace;
57
73
  constructor(descriptor, comments, references, children, accessor, whitespace) {
58
74
  super();
59
75
  this.descriptor = descriptor;
@@ -1,4 +1,4 @@
1
- import { t as generateHash } from "../../hash-DyC8GzMa.mjs";
1
+ import { t as generateHash } from "../../hash-DvzpieJD.mjs";
2
2
  //#region src/features/messages/identifier.ts
3
3
  const AUTO_INCREMENT_IDENTIFIER = Symbol("auto-increment");
4
4
  function assignSequenceIdentifiers(message, sequence = { current: 0 }) {
@@ -23,12 +23,15 @@ var Base = class {
23
23
  }
24
24
  };
25
25
  var LiteralMessage = class extends Base {
26
+ text;
26
27
  constructor(text) {
27
28
  super();
28
29
  this.text = text;
29
30
  }
30
31
  };
31
32
  var ArgumentMessage = class extends Base {
33
+ identifier;
34
+ expression;
32
35
  constructor(identifier, expression) {
33
36
  super();
34
37
  this.identifier = identifier;
@@ -36,6 +39,9 @@ var ArgumentMessage = class extends Base {
36
39
  }
37
40
  };
38
41
  var ElementMessage = class extends Base {
42
+ identifier;
43
+ children;
44
+ expression;
39
45
  constructor(identifier, children, expression) {
40
46
  super();
41
47
  this.identifier = identifier;
@@ -44,6 +50,10 @@ var ElementMessage = class extends Base {
44
50
  }
45
51
  };
46
52
  var ChoiceMessage = class extends Base {
53
+ kind;
54
+ identifier;
55
+ branches;
56
+ expression;
47
57
  constructor(kind, identifier, branches, expression) {
48
58
  super();
49
59
  this.kind = kind;
@@ -53,6 +63,12 @@ var ChoiceMessage = class extends Base {
53
63
  }
54
64
  };
55
65
  var CompositeMessage = class extends Base {
66
+ descriptor;
67
+ comments;
68
+ references;
69
+ children;
70
+ accessor;
71
+ whitespace;
56
72
  constructor(descriptor, comments, references, children, accessor, whitespace) {
57
73
  super();
58
74
  this.descriptor = descriptor;
@@ -1,4 +1,3 @@
1
- require("./chunk-CKQMccvm.cjs");
2
1
  let js_sha256 = require("js-sha256");
3
2
  //#region src/features/messages/hash.ts
4
3
  function generateHash(input, context) {
package/dist/index.cjs CHANGED
@@ -1,9 +1,30 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_chunk = require("./chunk-CKQMccvm.cjs");
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
3
24
  let picomatch = require("picomatch");
4
- picomatch = require_chunk.__toESM(picomatch, 1);
25
+ picomatch = __toESM(picomatch, 1);
5
26
  let zod = require("zod");
6
- zod = require_chunk.__toESM(zod, 1);
27
+ zod = __toESM(zod, 1);
7
28
  zod.object({
8
29
  message: zod.string(),
9
30
  translation: zod.string().optional(),
@@ -22,6 +43,16 @@ const Transformer = zod.object({
22
43
  extract: zod.custom((v) => typeof v === "function"),
23
44
  transform: zod.custom((v) => typeof v === "function")
24
45
  });
46
+ /**
47
+ * A message declared in the config rather than found in source. The shorthand
48
+ * is the source string; the object form adds the metadata a descriptor would
49
+ * otherwise carry.
50
+ */
51
+ const DeclaredMessage = zod.string().or(zod.object({
52
+ message: zod.string(),
53
+ context: zod.string().optional(),
54
+ comments: zod.string().array().optional()
55
+ }));
25
56
  const Bucket = zod.object({
26
57
  include: zod.string().array(),
27
58
  exclude: zod.string().array().optional(),
@@ -31,6 +62,14 @@ const Bucket = zod.object({
31
62
  zod.string(),
32
63
  ".{extension}"
33
64
  ]),
65
+ /**
66
+ * Messages that belong in the catalogue but have no call site to extract
67
+ * them from — strings owned by a non-JavaScript artefact such as a manifest,
68
+ * a store listing, or an email subject. Keyed by id, so the id is stable and
69
+ * hand-written by definition, and merged into every extraction alongside
70
+ * whatever the transformers find.
71
+ */
72
+ messages: zod.record(zod.string(), DeclaredMessage).optional(),
34
73
  formatter: Formatter,
35
74
  transformer: Transformer.transform((t) => [t]).or(Transformer.array()).transform((t) => ({
36
75
  match: (id) => t.some((t) => t.match(id)),
@@ -39,6 +78,17 @@ const Bucket = zod.object({
39
78
  }))
40
79
  }).transform((v) => ({
41
80
  ...v,
81
+ messages: Object.entries(v.messages ?? {}).map(([id, declared]) => {
82
+ const entry = typeof declared === "string" ? { message: declared } : declared;
83
+ return {
84
+ id,
85
+ message: entry.message,
86
+ translation: entry.message,
87
+ context: "context" in entry ? entry.context : void 0,
88
+ comments: ("comments" in entry ? entry.comments : void 0) ?? [],
89
+ references: []
90
+ };
91
+ }),
42
92
  match: (0, picomatch.default)(v.include, { ignore: v.exclude }),
43
93
  output: Object.assign(v.output, { match: (0, picomatch.default)(v.output.replace("{locale}", "*").replace("{extension}", v.formatter.extension.slice(1))) })
44
94
  }));
package/dist/index.d.cts CHANGED
@@ -1,14 +1,20 @@
1
- import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-Ba3xS10n.cjs";
1
+ import { a as Message, i as Formatter, n as Config, o as Transformer, r as DeclaredMessage, t as Bucket } from "./shapes-CRlXtss0.cjs";
2
2
  import { input } from "zod";
3
- import * as _$picomatch_lib_picomatch_js0 from "picomatch/lib/picomatch.js";
4
-
5
3
  //#region src/index.d.ts
6
4
  declare function defineConfig<C extends input<typeof Config>>(config: C): {
7
5
  locales: [string, ...string[]];
8
6
  buckets: {
7
+ messages: {
8
+ message: string;
9
+ comments: string[];
10
+ references: string[];
11
+ translation?: string | undefined;
12
+ id?: string | undefined;
13
+ context?: string | undefined;
14
+ }[];
9
15
  match: (id: string) => boolean;
10
16
  output: `${string}{locale}${string}.{extension}` & {
11
- match: _$picomatch_lib_picomatch_js0.Matcher;
17
+ match: import("picomatch/lib/picomatch.js").Matcher;
12
18
  };
13
19
  include: string[];
14
20
  formatter: {
@@ -36,4 +42,4 @@ declare function defineConfig<C extends input<typeof Config>>(config: C): {
36
42
  fallbackLocales?: Record<string, string | string[]> | undefined;
37
43
  };
38
44
  //#endregion
39
- export { Bucket, Config, Formatter, Message, Transformer, defineConfig };
45
+ export { type Bucket, type Config, type DeclaredMessage, type Formatter, type Message, type Transformer, defineConfig };
package/dist/index.d.mts CHANGED
@@ -1,14 +1,20 @@
1
- import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-NPfwlInG.mjs";
1
+ import { a as Message, i as Formatter, n as Config, o as Transformer, r as DeclaredMessage, t as Bucket } from "./shapes-CRlXtss0.mjs";
2
2
  import { input } from "zod";
3
- import * as _$picomatch_lib_picomatch_js0 from "picomatch/lib/picomatch.js";
4
-
5
3
  //#region src/index.d.ts
6
4
  declare function defineConfig<C extends input<typeof Config>>(config: C): {
7
5
  locales: [string, ...string[]];
8
6
  buckets: {
7
+ messages: {
8
+ message: string;
9
+ comments: string[];
10
+ references: string[];
11
+ translation?: string | undefined;
12
+ id?: string | undefined;
13
+ context?: string | undefined;
14
+ }[];
9
15
  match: (id: string) => boolean;
10
16
  output: `${string}{locale}${string}.{extension}` & {
11
- match: _$picomatch_lib_picomatch_js0.Matcher;
17
+ match: import("picomatch/lib/picomatch.js").Matcher;
12
18
  };
13
19
  include: string[];
14
20
  formatter: {
@@ -36,4 +42,4 @@ declare function defineConfig<C extends input<typeof Config>>(config: C): {
36
42
  fallbackLocales?: Record<string, string | string[]> | undefined;
37
43
  };
38
44
  //#endregion
39
- export { Bucket, Config, Formatter, Message, Transformer, defineConfig };
45
+ export { type Bucket, type Config, type DeclaredMessage, type Formatter, type Message, type Transformer, defineConfig };
package/dist/index.mjs CHANGED
@@ -18,6 +18,16 @@ const Transformer = z.object({
18
18
  extract: z.custom((v) => typeof v === "function"),
19
19
  transform: z.custom((v) => typeof v === "function")
20
20
  });
21
+ /**
22
+ * A message declared in the config rather than found in source. The shorthand
23
+ * is the source string; the object form adds the metadata a descriptor would
24
+ * otherwise carry.
25
+ */
26
+ const DeclaredMessage = z.string().or(z.object({
27
+ message: z.string(),
28
+ context: z.string().optional(),
29
+ comments: z.string().array().optional()
30
+ }));
21
31
  const Bucket = z.object({
22
32
  include: z.string().array(),
23
33
  exclude: z.string().array().optional(),
@@ -27,6 +37,14 @@ const Bucket = z.object({
27
37
  z.string(),
28
38
  ".{extension}"
29
39
  ]),
40
+ /**
41
+ * Messages that belong in the catalogue but have no call site to extract
42
+ * them from — strings owned by a non-JavaScript artefact such as a manifest,
43
+ * a store listing, or an email subject. Keyed by id, so the id is stable and
44
+ * hand-written by definition, and merged into every extraction alongside
45
+ * whatever the transformers find.
46
+ */
47
+ messages: z.record(z.string(), DeclaredMessage).optional(),
30
48
  formatter: Formatter,
31
49
  transformer: Transformer.transform((t) => [t]).or(Transformer.array()).transform((t) => ({
32
50
  match: (id) => t.some((t) => t.match(id)),
@@ -35,6 +53,17 @@ const Bucket = z.object({
35
53
  }))
36
54
  }).transform((v) => ({
37
55
  ...v,
56
+ messages: Object.entries(v.messages ?? {}).map(([id, declared]) => {
57
+ const entry = typeof declared === "string" ? { message: declared } : declared;
58
+ return {
59
+ id,
60
+ message: entry.message,
61
+ translation: entry.message,
62
+ context: "context" in entry ? entry.context : void 0,
63
+ comments: ("comments" in entry ? entry.comments : void 0) ?? [],
64
+ references: []
65
+ };
66
+ }),
38
67
  match: picomatch(v.include, { ignore: v.exclude }),
39
68
  output: Object.assign(v.output, { match: picomatch(v.output.replace("{locale}", "*").replace("{extension}", v.formatter.extension.slice(1))) })
40
69
  }));
@@ -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 };