@saykit/config 0.0.0 → 0.2.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.
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @saykit/config
2
+
3
+ > Configuration schema and CLI for [SayKit](https://saykit.js.org).
4
+
5
+ [![Coverage](https://codecov.io/gh/k0d13/saykit/graph/badge.svg?flag=config)](https://codecov.io/gh/k0d13/saykit?flags%5B0%5D=config)
6
+
7
+ Provides the `defineConfig` helper, the Zod-validated schema for `saykit.config.ts`, and the `saykit` CLI used to extract messages from your source.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ pnpm add -D @saykit/config
13
+ ```
14
+
15
+ ## Configuration
16
+
17
+ ```ts title="saykit.config.ts"
18
+ import { defineConfig } from '@saykit/config';
19
+ import po from '@saykit/format-po';
20
+ import js from '@saykit/transform-js';
21
+
22
+ export default defineConfig({
23
+ locales: ['en', 'fr'],
24
+ buckets: [
25
+ {
26
+ include: ['src/**/*.ts'],
27
+ output: 'src/locales/{locale}.{extension}',
28
+ formatter: po(),
29
+ transformer: js(),
30
+ },
31
+ ],
32
+ });
33
+ ```
34
+
35
+ ## CLI
36
+
37
+ ```sh
38
+ saykit extract # extract messages once
39
+ saykit extract --watch # extract and watch for changes
40
+ ```
41
+
42
+ ## Documentation
43
+
44
+ Full configuration guide and CLI reference at [saykit.js.org](https://saykit.js.org).
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ require("../index.cjs");
3
+ const require_loader = require("../loader-DnWt_9U2.cjs");
4
+ const require_hash = require("../hash-CDfMT76E.cjs");
5
+ let _commander_js_extra_typings = require("@commander-js/extra-typings");
6
+ let node_path = require("node:path");
7
+ let node_async_hooks = require("node:async_hooks");
8
+ let node_fs_promises = require("node:fs/promises");
9
+ //#region src/features/logger.ts
10
+ const RESET = "\x1B[0m";
11
+ const DIM = "\x1B[2m";
12
+ const BRIGHT = "\x1B[1m";
13
+ const RED = "\x1B[31m";
14
+ const GREEN = "\x1B[32m";
15
+ const YELLOW = "\x1B[33m";
16
+ const BLUE = "\x1B[34m";
17
+ var Logger = class {
18
+ #quiet;
19
+ #verbose;
20
+ constructor(options = {}) {
21
+ this.#quiet = options.quiet ?? false;
22
+ this.#verbose = options.verbose ?? false;
23
+ }
24
+ log(...args) {
25
+ if (!this.#quiet) console.log(...args);
26
+ }
27
+ info(...args) {
28
+ this.log(`${BLUE}🛈${RESET}`, ...args);
29
+ }
30
+ warn(...args) {
31
+ this.log(`${YELLOW}⚠${RESET}`, ...args);
32
+ }
33
+ error(...args) {
34
+ this.log(`${RED}✖${RESET}`, ...args);
35
+ }
36
+ success(...args) {
37
+ this.log(`${GREEN}✔${RESET}`, ...args);
38
+ }
39
+ header(message) {
40
+ this.log(`${BRIGHT}${message}${RESET}`);
41
+ }
42
+ step(message) {
43
+ if (this.#verbose) this.log(` ${DIM}→ ${message}${RESET}`);
44
+ }
45
+ };
46
+ new node_async_hooks.AsyncLocalStorage({ defaultValue: new Logger() });
47
+ //#endregion
48
+ //#region src/features/catalogue/extractor.ts
49
+ async function extractMessagesFromFile(path, bucket) {
50
+ const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
51
+ if (!content) return [];
52
+ return bucket.transformer.extract(content, path).map((message) => ({
53
+ ...message,
54
+ translation: message.translation ?? message.message,
55
+ references: message.references.map((reference) => (0, node_path.relative)(process.cwd(), reference).replaceAll("\\", "/"))
56
+ }));
57
+ }
58
+ //#endregion
59
+ //#region src/features/catalogue/merge.ts
60
+ function mergeUnique(...items) {
61
+ return Array.from(new Set(items.flat()));
62
+ }
63
+ function getMessageKey(message) {
64
+ return message.id ?? require_hash.generateHash(message.message, message.context);
65
+ }
66
+ function mergeExtractedMessages(messages) {
67
+ const mergedMessages = messages.reduce((map, message) => {
68
+ const key = getMessageKey(message);
69
+ const existing = map.get(key) ?? message;
70
+ map.set(key, {
71
+ ...existing,
72
+ comments: mergeUnique(...existing.comments, ...message.comments),
73
+ references: mergeUnique(...existing.references, ...message.references)
74
+ });
75
+ return map;
76
+ }, /* @__PURE__ */ new Map());
77
+ return Array.from(mergedMessages.values());
78
+ }
79
+ function reconcileLocaleMessages(existingMessages, nextMessages) {
80
+ const existingMessagesByKey = existingMessages.reduce((map, message) => {
81
+ map.set(getMessageKey(message), message);
82
+ return map;
83
+ }, /* @__PURE__ */ new Map());
84
+ const reconciledMessages = nextMessages.reduce((map, message) => {
85
+ const key = getMessageKey(message);
86
+ const existingMessage = existingMessagesByKey.get(key);
87
+ map.set(key, {
88
+ message: message.message,
89
+ translation: void 0,
90
+ ...existingMessage,
91
+ id: message.id,
92
+ context: message.context,
93
+ comments: message.comments,
94
+ references: message.references
95
+ });
96
+ return map;
97
+ }, /* @__PURE__ */ new Map());
98
+ return Array.from(reconciledMessages.values());
99
+ }
100
+ //#endregion
101
+ //#region src/features/catalogue/path.ts
102
+ function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
103
+ return (0, node_path.resolve)(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
104
+ }
105
+ //#endregion
106
+ //#region src/features/catalogue/storage.ts
107
+ const DECLARATION_CONTENT = `
108
+ declare const messages: Record<string, string>;
109
+ export default messages;
110
+ `.trim();
111
+ async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
112
+ const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
113
+ if (!content) return [];
114
+ return bucket.formatter.parse(content);
115
+ }
116
+ async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
117
+ const existingContent = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => void 0);
118
+ const catalogueContent = bucket.formatter.stringify(messages, {
119
+ locale,
120
+ existingContent
121
+ });
122
+ const declarationPath = `${path}.d.ts`;
123
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
124
+ await Promise.all([(0, node_fs_promises.writeFile)(path, catalogueContent), (0, node_fs_promises.writeFile)(declarationPath, DECLARATION_CONTENT)]);
125
+ }
126
+ //#endregion
127
+ //#region src/features/watch.ts
128
+ /**
129
+ * Expand a buckets include and exclude patterns into a flat list of file paths.
130
+ */
131
+ async function globBucket(bucket) {
132
+ const paths = [];
133
+ for await (const file of (0, node_fs_promises.glob)(bucket.include, {
134
+ exclude: bucket.exclude,
135
+ withFileTypes: true
136
+ })) if (file.isFile()) paths.push((0, node_path.join)(file.parentPath, file.name));
137
+ return paths;
138
+ }
139
+ /**
140
+ * Watches a path for changes, emitting a debounced event every set delay.
141
+ *
142
+ * Unlike Node's native `fs.watch` method, this:
143
+ * - coalesces rapid consecutive events per file
144
+ * - emits only the final event after `delay` ms of inactivity
145
+ * - deduplicates events by filename
146
+ */
147
+ async function* watchDebounced(path, options, delay = 300) {
148
+ const timers = /* @__PURE__ */ new Map();
149
+ const queue = /* @__PURE__ */ new Map();
150
+ const resolvers = /* @__PURE__ */ new Map();
151
+ (async () => {
152
+ for await (const event of (0, node_fs_promises.watch)(path, options)) {
153
+ const key = event.filename ?? "__unknown__";
154
+ if (timers.has(key)) clearTimeout(timers.get(key));
155
+ if (!queue.has(key)) queue.set(key, new Promise((r) => resolvers.set(key, r)));
156
+ timers.set(key, setTimeout(() => {
157
+ resolvers.get(key)?.(event);
158
+ timers.delete(key);
159
+ resolvers.delete(key);
160
+ }, delay));
161
+ }
162
+ })();
163
+ while (true) if (queue.size) {
164
+ const next = await Promise.race(queue.values());
165
+ queue.delete(next.filename ?? "__unknown__");
166
+ yield next;
167
+ } else await new Promise((r) => setTimeout(r, 10));
168
+ }
169
+ //#endregion
170
+ //#region src/features/workers/shared.ts
171
+ function normalisePathForLogs(path) {
172
+ return (0, node_path.relative)(process.cwd(), path).replaceAll("\\", "/");
173
+ }
174
+ var BucketWorker = class {
175
+ config;
176
+ bucket;
177
+ logger;
178
+ constructor(config, bucket, logger) {
179
+ this.config = config;
180
+ this.bucket = bucket;
181
+ this.logger = logger;
182
+ }
183
+ };
184
+ //#endregion
185
+ //#region src/features/workers/extract-worker.ts
186
+ var BucketExtractWorker = class extends BucketWorker {
187
+ #indexedMessagesByPath = /* @__PURE__ */ new Map();
188
+ get #indexedMessages() {
189
+ return Array.from(this.#indexedMessagesByPath.values()).flat();
190
+ }
191
+ async #indexPath(path) {
192
+ const relativePath = normalisePathForLogs(path);
193
+ this.logger.step(`Processing ${relativePath}`);
194
+ const messages = await extractMessagesFromFile(path, this.bucket);
195
+ if (!messages.length) {
196
+ if (this.#indexedMessagesByPath.has(path)) {
197
+ this.#indexedMessagesByPath.delete(path);
198
+ this.logger.step(`Removed stale entries for ${relativePath}`);
199
+ return true;
200
+ }
201
+ return false;
202
+ }
203
+ this.#indexedMessagesByPath.set(path, messages);
204
+ this.logger.step(`Found ${messages.length} message(s) in ${relativePath}`);
205
+ return true;
206
+ }
207
+ async scan() {
208
+ this.logger.info(`Scanning bucket: ${this.bucket.include}`);
209
+ const paths = await globBucket(this.bucket);
210
+ this.logger.step(`Found ${paths.length} file(s)`);
211
+ await Promise.all(paths.map((p) => this.#indexPath(p)));
212
+ this.logger.info(`Total extracted messages: ${this.#indexedMessages.length}`);
213
+ }
214
+ async write() {
215
+ const mergedMessages = mergeExtractedMessages(this.#indexedMessages);
216
+ this.logger.info(`Writing ${mergedMessages.length} messages to locales`);
217
+ for (const locale of this.config.locales) {
218
+ this.logger.step(`Writing locale file for ${locale} to disk`);
219
+ const existingMessages = await readCatalogueMessages(this.bucket, locale);
220
+ const nextMessages = locale === this.config.locales[0] ? mergedMessages : reconcileLocaleMessages(existingMessages, mergedMessages);
221
+ await writeCatalogueMessages(this.bucket, locale, nextMessages);
222
+ }
223
+ this.logger.success(`Extraction complete for bucket: ${this.bucket.include}`);
224
+ }
225
+ async update(path) {
226
+ const changed = await this.#indexPath(path);
227
+ if (changed) await this.write();
228
+ return changed;
229
+ }
230
+ async watch() {
231
+ this.logger.header(`👀 Watching bucket for changes: ${this.bucket.include}`);
232
+ for await (const event of watchDebounced(".", { recursive: true })) {
233
+ if (!event.filename || !this.bucket.match(event.filename)) continue;
234
+ const path = (0, node_path.join)(process.cwd(), event.filename);
235
+ await this.update(path);
236
+ }
237
+ }
238
+ };
239
+ //#endregion
240
+ //#region src/commands/extract.ts
241
+ var extract_default = new _commander_js_extra_typings.Command("extract").description("Extract messages from source files").option("-v, --verbose", "enable verbose logging", false).option("-q, --quiet", "suppress all logging", false).option("-w, --watch", "watch source files for changes", false).action(async (options) => {
242
+ const config = require_loader.resolveConfig();
243
+ const logger = new Logger(options);
244
+ logger.header("🛠 Extracting Messages");
245
+ const workers = config.buckets.map((b) => new BucketExtractWorker(config, b, logger));
246
+ await Promise.all(workers.map((w) => w.scan().then(() => w.write())));
247
+ if (options.watch) await Promise.all(workers.map((w) => w.watch()));
248
+ });
249
+ //#endregion
250
+ //#region src/commands/index.ts
251
+ _commander_js_extra_typings.program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).parse();
252
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ import { t as resolveConfig } from "../loader-CGuahnrc.mjs";
3
+ import { t as generateHash } from "../hash-Cs0dRdGf.mjs";
4
+ import { Command, program } from "@commander-js/extra-typings";
5
+ import { dirname, join, relative, resolve } from "node:path";
6
+ import { AsyncLocalStorage } from "node:async_hooks";
7
+ import { glob, mkdir, readFile, watch, writeFile } from "node:fs/promises";
8
+ //#region src/features/logger.ts
9
+ const RESET = "\x1B[0m";
10
+ const DIM = "\x1B[2m";
11
+ const BRIGHT = "\x1B[1m";
12
+ const RED = "\x1B[31m";
13
+ const GREEN = "\x1B[32m";
14
+ const YELLOW = "\x1B[33m";
15
+ const BLUE = "\x1B[34m";
16
+ var Logger = class {
17
+ #quiet;
18
+ #verbose;
19
+ constructor(options = {}) {
20
+ this.#quiet = options.quiet ?? false;
21
+ this.#verbose = options.verbose ?? false;
22
+ }
23
+ log(...args) {
24
+ if (!this.#quiet) console.log(...args);
25
+ }
26
+ info(...args) {
27
+ this.log(`${BLUE}🛈${RESET}`, ...args);
28
+ }
29
+ warn(...args) {
30
+ this.log(`${YELLOW}⚠${RESET}`, ...args);
31
+ }
32
+ error(...args) {
33
+ this.log(`${RED}✖${RESET}`, ...args);
34
+ }
35
+ success(...args) {
36
+ this.log(`${GREEN}✔${RESET}`, ...args);
37
+ }
38
+ header(message) {
39
+ this.log(`${BRIGHT}${message}${RESET}`);
40
+ }
41
+ step(message) {
42
+ if (this.#verbose) this.log(` ${DIM}→ ${message}${RESET}`);
43
+ }
44
+ };
45
+ new AsyncLocalStorage({ defaultValue: new Logger() });
46
+ //#endregion
47
+ //#region src/features/catalogue/extractor.ts
48
+ async function extractMessagesFromFile(path, bucket) {
49
+ const content = await readFile(path, "utf8").catch(() => "");
50
+ if (!content) return [];
51
+ return bucket.transformer.extract(content, path).map((message) => ({
52
+ ...message,
53
+ translation: message.translation ?? message.message,
54
+ references: message.references.map((reference) => relative(process.cwd(), reference).replaceAll("\\", "/"))
55
+ }));
56
+ }
57
+ //#endregion
58
+ //#region src/features/catalogue/merge.ts
59
+ function mergeUnique(...items) {
60
+ return Array.from(new Set(items.flat()));
61
+ }
62
+ function getMessageKey(message) {
63
+ return message.id ?? generateHash(message.message, message.context);
64
+ }
65
+ function mergeExtractedMessages(messages) {
66
+ const mergedMessages = messages.reduce((map, message) => {
67
+ const key = getMessageKey(message);
68
+ const existing = map.get(key) ?? message;
69
+ map.set(key, {
70
+ ...existing,
71
+ comments: mergeUnique(...existing.comments, ...message.comments),
72
+ references: mergeUnique(...existing.references, ...message.references)
73
+ });
74
+ return map;
75
+ }, /* @__PURE__ */ new Map());
76
+ return Array.from(mergedMessages.values());
77
+ }
78
+ function reconcileLocaleMessages(existingMessages, nextMessages) {
79
+ const existingMessagesByKey = existingMessages.reduce((map, message) => {
80
+ map.set(getMessageKey(message), message);
81
+ return map;
82
+ }, /* @__PURE__ */ new Map());
83
+ const reconciledMessages = nextMessages.reduce((map, message) => {
84
+ const key = getMessageKey(message);
85
+ const existingMessage = existingMessagesByKey.get(key);
86
+ map.set(key, {
87
+ message: message.message,
88
+ translation: void 0,
89
+ ...existingMessage,
90
+ id: message.id,
91
+ context: message.context,
92
+ comments: message.comments,
93
+ references: message.references
94
+ });
95
+ return map;
96
+ }, /* @__PURE__ */ new Map());
97
+ return Array.from(reconciledMessages.values());
98
+ }
99
+ //#endregion
100
+ //#region src/features/catalogue/path.ts
101
+ function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
102
+ return resolve(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
103
+ }
104
+ //#endregion
105
+ //#region src/features/catalogue/storage.ts
106
+ const DECLARATION_CONTENT = `
107
+ declare const messages: Record<string, string>;
108
+ export default messages;
109
+ `.trim();
110
+ async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
111
+ const content = await readFile(path, "utf8").catch(() => "");
112
+ if (!content) return [];
113
+ return bucket.formatter.parse(content);
114
+ }
115
+ async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
116
+ const existingContent = await readFile(path, "utf8").catch(() => void 0);
117
+ const catalogueContent = bucket.formatter.stringify(messages, {
118
+ locale,
119
+ existingContent
120
+ });
121
+ const declarationPath = `${path}.d.ts`;
122
+ await mkdir(dirname(path), { recursive: true });
123
+ await Promise.all([writeFile(path, catalogueContent), writeFile(declarationPath, DECLARATION_CONTENT)]);
124
+ }
125
+ //#endregion
126
+ //#region src/features/watch.ts
127
+ /**
128
+ * Expand a buckets include and exclude patterns into a flat list of file paths.
129
+ */
130
+ async function globBucket(bucket) {
131
+ const paths = [];
132
+ for await (const file of glob(bucket.include, {
133
+ exclude: bucket.exclude,
134
+ withFileTypes: true
135
+ })) if (file.isFile()) paths.push(join(file.parentPath, file.name));
136
+ return paths;
137
+ }
138
+ /**
139
+ * Watches a path for changes, emitting a debounced event every set delay.
140
+ *
141
+ * Unlike Node's native `fs.watch` method, this:
142
+ * - coalesces rapid consecutive events per file
143
+ * - emits only the final event after `delay` ms of inactivity
144
+ * - deduplicates events by filename
145
+ */
146
+ async function* watchDebounced(path, options, delay = 300) {
147
+ const timers = /* @__PURE__ */ new Map();
148
+ const queue = /* @__PURE__ */ new Map();
149
+ const resolvers = /* @__PURE__ */ new Map();
150
+ (async () => {
151
+ for await (const event of watch(path, options)) {
152
+ const key = event.filename ?? "__unknown__";
153
+ if (timers.has(key)) clearTimeout(timers.get(key));
154
+ if (!queue.has(key)) queue.set(key, new Promise((r) => resolvers.set(key, r)));
155
+ timers.set(key, setTimeout(() => {
156
+ resolvers.get(key)?.(event);
157
+ timers.delete(key);
158
+ resolvers.delete(key);
159
+ }, delay));
160
+ }
161
+ })();
162
+ while (true) if (queue.size) {
163
+ const next = await Promise.race(queue.values());
164
+ queue.delete(next.filename ?? "__unknown__");
165
+ yield next;
166
+ } else await new Promise((r) => setTimeout(r, 10));
167
+ }
168
+ //#endregion
169
+ //#region src/features/workers/shared.ts
170
+ function normalisePathForLogs(path) {
171
+ return relative(process.cwd(), path).replaceAll("\\", "/");
172
+ }
173
+ var BucketWorker = class {
174
+ config;
175
+ bucket;
176
+ logger;
177
+ constructor(config, bucket, logger) {
178
+ this.config = config;
179
+ this.bucket = bucket;
180
+ this.logger = logger;
181
+ }
182
+ };
183
+ //#endregion
184
+ //#region src/features/workers/extract-worker.ts
185
+ var BucketExtractWorker = class extends BucketWorker {
186
+ #indexedMessagesByPath = /* @__PURE__ */ new Map();
187
+ get #indexedMessages() {
188
+ return Array.from(this.#indexedMessagesByPath.values()).flat();
189
+ }
190
+ async #indexPath(path) {
191
+ const relativePath = normalisePathForLogs(path);
192
+ this.logger.step(`Processing ${relativePath}`);
193
+ const messages = await extractMessagesFromFile(path, this.bucket);
194
+ if (!messages.length) {
195
+ if (this.#indexedMessagesByPath.has(path)) {
196
+ this.#indexedMessagesByPath.delete(path);
197
+ this.logger.step(`Removed stale entries for ${relativePath}`);
198
+ return true;
199
+ }
200
+ return false;
201
+ }
202
+ this.#indexedMessagesByPath.set(path, messages);
203
+ this.logger.step(`Found ${messages.length} message(s) in ${relativePath}`);
204
+ return true;
205
+ }
206
+ async scan() {
207
+ this.logger.info(`Scanning bucket: ${this.bucket.include}`);
208
+ const paths = await globBucket(this.bucket);
209
+ this.logger.step(`Found ${paths.length} file(s)`);
210
+ await Promise.all(paths.map((p) => this.#indexPath(p)));
211
+ this.logger.info(`Total extracted messages: ${this.#indexedMessages.length}`);
212
+ }
213
+ async write() {
214
+ const mergedMessages = mergeExtractedMessages(this.#indexedMessages);
215
+ this.logger.info(`Writing ${mergedMessages.length} messages to locales`);
216
+ for (const locale of this.config.locales) {
217
+ this.logger.step(`Writing locale file for ${locale} to disk`);
218
+ const existingMessages = await readCatalogueMessages(this.bucket, locale);
219
+ const nextMessages = locale === this.config.locales[0] ? mergedMessages : reconcileLocaleMessages(existingMessages, mergedMessages);
220
+ await writeCatalogueMessages(this.bucket, locale, nextMessages);
221
+ }
222
+ this.logger.success(`Extraction complete for bucket: ${this.bucket.include}`);
223
+ }
224
+ async update(path) {
225
+ const changed = await this.#indexPath(path);
226
+ if (changed) await this.write();
227
+ return changed;
228
+ }
229
+ async watch() {
230
+ this.logger.header(`👀 Watching bucket for changes: ${this.bucket.include}`);
231
+ for await (const event of watchDebounced(".", { recursive: true })) {
232
+ if (!event.filename || !this.bucket.match(event.filename)) continue;
233
+ const path = join(process.cwd(), event.filename);
234
+ await this.update(path);
235
+ }
236
+ }
237
+ };
238
+ //#endregion
239
+ //#region src/commands/extract.ts
240
+ var extract_default = new Command("extract").description("Extract messages from source files").option("-v, --verbose", "enable verbose logging", false).option("-q, --quiet", "suppress all logging", false).option("-w, --watch", "watch source files for changes", false).action(async (options) => {
241
+ const config = resolveConfig();
242
+ const logger = new Logger(options);
243
+ logger.header("🛠 Extracting Messages");
244
+ const workers = config.buckets.map((b) => new BucketExtractWorker(config, b, logger));
245
+ await Promise.all(workers.map((w) => w.scan().then(() => w.write())));
246
+ if (options.watch) await Promise.all(workers.map((w) => w.watch()));
247
+ });
248
+ //#endregion
249
+ //#region src/commands/index.ts
250
+ program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).parse();
251
+ //#endregion
252
+ export {};
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_loader = require("../../loader-DnWt_9U2.cjs");
3
+ exports.resolveConfig = require_loader.resolveConfig;
@@ -0,0 +1,6 @@
1
+ import { n as Config } from "../../shapes-CJyTfZXd.cjs";
2
+
3
+ //#region src/features/loader/resolve.d.ts
4
+ declare function resolveConfig(name?: string): Config;
5
+ //#endregion
6
+ export { resolveConfig };
@@ -0,0 +1,6 @@
1
+ import { n as Config } from "../../shapes-DETrtvZf.mjs";
2
+
3
+ //#region src/features/loader/resolve.d.ts
4
+ declare function resolveConfig(name?: string): Config;
5
+ //#endregion
6
+ export { resolveConfig };
@@ -0,0 +1,2 @@
1
+ import { t as resolveConfig } from "../../loader-CGuahnrc.mjs";
2
+ export { resolveConfig };
@@ -0,0 +1,101 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_hash = require("../../hash-CDfMT76E.cjs");
3
+ //#region src/features/messages/identifier.ts
4
+ const AUTO_INCREMENT_IDENTIFIER = Symbol("auto-increment");
5
+ function assignSequenceIdentifiers(message, sequence = { current: 0 }) {
6
+ if (message instanceof ArgumentMessage || message instanceof ElementMessage || message instanceof ChoiceMessage) {
7
+ if (message.identifier === AUTO_INCREMENT_IDENTIFIER) message.identifier = `${sequence.current++}`;
8
+ }
9
+ if (message instanceof CompositeMessage || message instanceof ElementMessage) for (const child of message.children) assignSequenceIdentifiers(child, sequence);
10
+ if (message instanceof ChoiceMessage) for (const branch of message.branches) {
11
+ if (branch.identifier === AUTO_INCREMENT_IDENTIFIER) branch.identifier = `${sequence.current++}`;
12
+ assignSequenceIdentifiers(branch.value, sequence);
13
+ }
14
+ }
15
+ //#endregion
16
+ //#region src/features/messages/types.ts
17
+ var Base = class {
18
+ toICUString() {
19
+ return convertMessageToIcu(this);
20
+ }
21
+ toHashString() {
22
+ const context = this instanceof CompositeMessage ? this.descriptor.context : void 0;
23
+ return require_hash.generateHash(this.toICUString(), context);
24
+ }
25
+ };
26
+ var LiteralMessage = class extends Base {
27
+ constructor(text) {
28
+ super();
29
+ this.text = text;
30
+ }
31
+ };
32
+ var ArgumentMessage = class extends Base {
33
+ constructor(identifier, expression) {
34
+ super();
35
+ this.identifier = identifier;
36
+ this.expression = expression;
37
+ }
38
+ };
39
+ var ElementMessage = class extends Base {
40
+ constructor(identifier, children, expression) {
41
+ super();
42
+ this.identifier = identifier;
43
+ this.children = children;
44
+ this.expression = expression;
45
+ }
46
+ };
47
+ var ChoiceMessage = class extends Base {
48
+ constructor(kind, identifier, branches, expression) {
49
+ super();
50
+ this.kind = kind;
51
+ this.identifier = identifier;
52
+ this.branches = branches;
53
+ this.expression = expression;
54
+ }
55
+ };
56
+ var CompositeMessage = class extends Base {
57
+ constructor(descriptor, comments, references, children, accessor, whitespace) {
58
+ super();
59
+ this.descriptor = descriptor;
60
+ this.comments = comments;
61
+ this.references = references;
62
+ this.children = children;
63
+ this.accessor = accessor;
64
+ this.whitespace = whitespace;
65
+ }
66
+ };
67
+ //#endregion
68
+ //#region src/features/messages/convert.ts
69
+ function convertMessageToIcu(message) {
70
+ function internalConvertMessageToIcu(message) {
71
+ switch (true) {
72
+ case message instanceof LiteralMessage: return String(message.text);
73
+ case message instanceof ArgumentMessage: return `{${String(message.identifier)}}`;
74
+ case message instanceof ElementMessage: {
75
+ const children = message.children.map((m) => internalConvertMessageToIcu(m)).join("");
76
+ return `<${String(message.identifier)}>${children}</${String(message.identifier)}>`;
77
+ }
78
+ case message instanceof ChoiceMessage: {
79
+ const branches = message.branches.map(({ identifier, value }) => ({
80
+ identifier: Number.isNaN(+String(identifier)) ? String(identifier) : `=${+String(identifier)}`,
81
+ value: internalConvertMessageToIcu(value)
82
+ })).map(({ identifier, value }) => ` ${identifier} {${value}}\n`).join("");
83
+ const format = message.kind === "ordinal" ? "selectordinal" : message.kind;
84
+ return `{${String(message.identifier)}, ${format},\n${branches}}`;
85
+ }
86
+ case message instanceof CompositeMessage: return Object.entries(message.children).map(([, m]) => internalConvertMessageToIcu(m)).join("");
87
+ default: throw new Error("Unknown message type", { cause: message });
88
+ }
89
+ }
90
+ return internalConvertMessageToIcu(message).trim();
91
+ }
92
+ //#endregion
93
+ exports.AUTO_INCREMENT_IDENTIFIER = AUTO_INCREMENT_IDENTIFIER;
94
+ exports.ArgumentMessage = ArgumentMessage;
95
+ exports.ChoiceMessage = ChoiceMessage;
96
+ exports.CompositeMessage = CompositeMessage;
97
+ exports.ElementMessage = ElementMessage;
98
+ exports.LiteralMessage = LiteralMessage;
99
+ exports.assignSequenceIdentifiers = assignSequenceIdentifiers;
100
+ exports.convertMessageToIcu = convertMessageToIcu;
101
+ exports.generateHash = require_hash.generateHash;