i18next-cli 1.58.3 → 1.59.1

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
@@ -263,6 +263,8 @@ npx i18next-cli types [options]
263
263
 
264
264
  **Options:**
265
265
  - `--watch, -w`: Re-run automatically when translation files change
266
+ - `--ci`: Exit with a non-zero status if the generated TypeScript definitions are out of date (check-only, writes nothing). Cannot be combined with `--watch`.
267
+ - `--quiet, -q`: Suppress spinner and non-essential output (for CI or scripting)
266
268
 
267
269
  ### `sync`
268
270
  Synchronizes secondary language files against your primary language file, adding missing keys and removing extraneous ones.
@@ -1325,6 +1327,14 @@ Use the `--ci` flag to fail builds when translations are outdated:
1325
1327
  run: npx i18next-cli extract --ci
1326
1328
  ```
1327
1329
 
1330
+ Likewise, fail the build when the generated TypeScript definitions are out of date:
1331
+
1332
+ ```yaml
1333
+ # Fail the build if generated TypeScript definitions are out of date
1334
+ - name: Check i18n types
1335
+ run: npx i18next-cli types --ci
1336
+ ```
1337
+
1328
1338
  ## Watch Mode
1329
1339
 
1330
1340
  For development, use watch mode to automatically update translations:
package/dist/cjs/cli.js CHANGED
@@ -32,7 +32,7 @@ const program = new commander.Command();
32
32
  program
33
33
  .name('i18next-cli')
34
34
  .description('A unified, high-performance i18next CLI.')
35
- .version('1.58.3'); // This string is replaced with the actual version at build time by rollup
35
+ .version('1.59.1'); // This string is replaced with the actual version at build time by rollup
36
36
  // new: global config override option
37
37
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
38
38
  program
@@ -133,10 +133,27 @@ program
133
133
  .command('types')
134
134
  .description('Generate TypeScript definitions from translation resource files.')
135
135
  .option('-w, --watch', 'Watch for file changes and re-run the type generator.')
136
+ .option('--ci', 'Exit with a non-zero status code if type definitions are not up to date (check-only, no writes).')
136
137
  .option('-q, --quiet', 'Suppress spinner and output')
137
138
  .action(async (options) => {
138
139
  const cfgPath = program.opts().config;
139
140
  const config$1 = await config.ensureConfig(cfgPath);
141
+ if (options.ci && options.watch) {
142
+ console.error(node_util.styleText('red', '--ci and --watch cannot be used together'));
143
+ process.exit(1);
144
+ }
145
+ if (options.ci) {
146
+ const { changed, changedFiles } = await typesGenerator.runTypesGenerator(config$1, { checkOnly: true });
147
+ if (changed) {
148
+ console.error(node_util.styleText('red', "❌ TypeScript definitions are out of date. Run 'i18next-cli types' and commit the changes."));
149
+ for (const file of changedFiles) {
150
+ console.error(` ${file}`);
151
+ }
152
+ process.exit(1);
153
+ }
154
+ console.log(node_util.styleText('green', '✅ TypeScript definitions are up to date.'));
155
+ return;
156
+ }
140
157
  const run = () => typesGenerator.runTypesGenerator(config$1, { quiet: !!options.quiet });
141
158
  await run();
142
159
  if (options.watch) {
@@ -94,7 +94,7 @@ function getNamespaceFromPath(filePath, basePath) {
94
94
  */
95
95
  async function runTypesGenerator(config, options = {}) {
96
96
  const internalLogger = options.logger ?? new logger.ConsoleLogger();
97
- const spinner = wrapOra.createSpinnerLike('Generating TypeScript types for translations...\n', { quiet: !!options.quiet, logger: options.logger });
97
+ const spinner = wrapOra.createSpinnerLike('Generating TypeScript types for translations...\n', { quiet: !!options.quiet || !!options.checkOnly, logger: options.logger });
98
98
  try {
99
99
  config.extract.primaryLanguage ||= config.locales[0] || 'en';
100
100
  let defaultTypesInputPath = config.extract.output || `locales/${config.extract.primaryLanguage}/*.json`;
@@ -109,7 +109,7 @@ async function runTypesGenerator(config, options = {}) {
109
109
  config.types.resourcesFile = node_path.join(node_path.dirname(config.types?.output), 'resources.d.ts');
110
110
  if (!config.types?.input || config.types?.input.length < 0) {
111
111
  console.log('No input defined!');
112
- return;
112
+ return { changed: false, changedFiles: [] };
113
113
  }
114
114
  const resourceFiles = await glob.glob(config.types.input, { cwd: process.cwd() });
115
115
  const basePath = config.types.basePath ? fileUtils.getOutputPath(config.types.basePath, config.extract.primaryLanguage) : undefined;
@@ -150,6 +150,26 @@ async function runTypesGenerator(config, options = {}) {
150
150
  ${i18nextResourcesForTs.mergeResourcesAsInterface(resources, { optimize: !!enableSelector, indentation })}`;
151
151
  const outputPath = node_path.resolve(process.cwd(), config.types?.output || '');
152
152
  const resourcesOutputPath = node_path.resolve(process.cwd(), config.types.resourcesFile);
153
+ if (options.checkOnly) {
154
+ const changedFiles = [];
155
+ let existingResources = null;
156
+ try {
157
+ existingResources = await promises.readFile(resourcesOutputPath, 'utf-8');
158
+ }
159
+ catch {
160
+ existingResources = null;
161
+ }
162
+ if (existingResources !== interfaceDefinition) {
163
+ changedFiles.push(config.types.resourcesFile);
164
+ }
165
+ try {
166
+ await promises.access(outputPath);
167
+ }
168
+ catch {
169
+ changedFiles.push(config.types.output || '');
170
+ }
171
+ return { changed: changedFiles.length > 0, changedFiles };
172
+ }
153
173
  await promises.mkdir(node_path.dirname(resourcesOutputPath), { recursive: true });
154
174
  await promises.writeFile(resourcesOutputPath, interfaceDefinition);
155
175
  logMessages.push(` ${node_util.styleText('green', '✓')} Resources interface written to ${config.types.resourcesFile}`);
@@ -183,6 +203,7 @@ declare module 'i18next' {
183
203
  }
184
204
  spinner.succeed(node_util.styleText('bold', 'TypeScript definitions generated successfully.'));
185
205
  logMessages.forEach(msg => typeof internalLogger.info === 'function' ? internalLogger.info(msg) : console.log(msg));
206
+ return { changed: false, changedFiles: [] };
186
207
  }
187
208
  catch (error) {
188
209
  spinner.fail(node_util.styleText('red', 'Failed to generate TypeScript definitions.'));
@@ -190,6 +211,7 @@ declare module 'i18next' {
190
211
  internalLogger.error(error);
191
212
  else
192
213
  console.error(error);
214
+ return { changed: false, changedFiles: [] };
193
215
  }
194
216
  }
195
217
 
@@ -7,6 +7,24 @@ var config = require('../config.js');
7
7
  var json5Parser = require('@croct/json5-parser');
8
8
  var yaml = require('yaml');
9
9
 
10
+ /**
11
+ * Thrown when an existing translation file in a structured data format
12
+ * (JSON/JSON5/YAML) exists on disk but cannot be parsed. Callers should treat
13
+ * this as fatal rather than as an empty/missing file, since overwriting an
14
+ * unparseable file would silently destroy its contents (e.g. a merge conflict
15
+ * marker accidentally committed into an en.json).
16
+ */
17
+ class ParseTranslationFileError extends Error {
18
+ filePath;
19
+ cause;
20
+ constructor(filePath, cause) {
21
+ const detail = cause instanceof Error ? cause.message : String(cause);
22
+ super(`Could not parse translation file ${filePath}: ${detail}`);
23
+ this.filePath = filePath;
24
+ this.cause = cause;
25
+ this.name = 'ParseTranslationFileError';
26
+ }
27
+ }
10
28
  /**
11
29
  * Resolve an output template (string or function) into an actual path string.
12
30
  *
@@ -58,23 +76,48 @@ async function loadTranslationFile(filePath) {
58
76
  catch {
59
77
  return null; // File doesn't exist
60
78
  }
61
- try {
62
- const ext = node_path.extname(fullPath).toLowerCase();
63
- if (ext === '.json5') {
64
- const content = await promises.readFile(fullPath, 'utf-8');
79
+ const ext = node_path.extname(fullPath).toLowerCase();
80
+ // Structured data formats (JSON/JSON5/YAML): the file exists (it passed the
81
+ // access() check above) but could not be parsed. Treating this as `null` is
82
+ // dangerous: callers coalesce it to an empty object and may overwrite the
83
+ // existing file, silently destroying its contents (e.g. when a merge conflict
84
+ // marker breaks an en.json that a watcher then re-extracts). Fail loudly so
85
+ // the caller can stop instead of overwriting good data with nothing.
86
+ if (ext === '.json5') {
87
+ const content = await promises.readFile(fullPath, 'utf-8');
88
+ try {
65
89
  // Parse as a JSON5 object node
66
90
  const node = json5Parser.JsonParser.parse(content, json5Parser.JsonObjectNode);
67
91
  return node.toJSON();
68
92
  }
69
- else if (ext === '.yaml' || ext === '.yml') {
70
- const content = await promises.readFile(fullPath, 'utf-8');
93
+ catch (error) {
94
+ throw new ParseTranslationFileError(filePath, error);
95
+ }
96
+ }
97
+ else if (ext === '.yaml' || ext === '.yml') {
98
+ const content = await promises.readFile(fullPath, 'utf-8');
99
+ try {
71
100
  return yaml.parse(content);
72
101
  }
73
- else if (ext === '.json') {
74
- const content = await promises.readFile(fullPath, 'utf-8');
102
+ catch (error) {
103
+ throw new ParseTranslationFileError(filePath, error);
104
+ }
105
+ }
106
+ else if (ext === '.json') {
107
+ const content = await promises.readFile(fullPath, 'utf-8');
108
+ try {
75
109
  return JSON.parse(content);
76
110
  }
77
- else if (ext === '.ts' || ext === '.js') {
111
+ catch (error) {
112
+ throw new ParseTranslationFileError(filePath, error);
113
+ }
114
+ }
115
+ else if (ext === '.ts' || ext === '.js') {
116
+ // .ts/.js resource files are loaded via jiti, which can fail for reasons
117
+ // unrelated to corruption (e.g. a transitive import or a side-effectful
118
+ // module). This path has been deliberately lenient since #59, so keep
119
+ // degrading to `null` here rather than aborting the whole command.
120
+ try {
78
121
  // Load TypeScript path aliases for proper module resolution
79
122
  const aliases = await config.getTsConfigAliases();
80
123
  const jiti$1 = jiti.createJiti(process.cwd(), {
@@ -84,12 +127,12 @@ async function loadTranslationFile(filePath) {
84
127
  const module = await jiti$1.import(fullPath, { default: true });
85
128
  return module;
86
129
  }
87
- return null; // Unsupported file type
88
- }
89
- catch (error) {
90
- console.warn(`Could not parse translation file ${filePath}:`, error);
91
- return null;
130
+ catch (error) {
131
+ console.warn(`Could not parse translation file ${filePath}:`, error);
132
+ return null;
133
+ }
92
134
  }
135
+ return null; // Unsupported file type
93
136
  }
94
137
  // Helper to load raw JSON5 content for preservation
95
138
  async function loadRawJson5Content(filePath) {
@@ -156,6 +199,7 @@ function inferFormatFromPath(filePath, defaultFormat = 'json') {
156
199
  return defaultFormat || 'json';
157
200
  }
158
201
 
202
+ exports.ParseTranslationFileError = ParseTranslationFileError;
159
203
  exports.getOutputPath = getOutputPath;
160
204
  exports.inferFormatFromPath = inferFormatFromPath;
161
205
  exports.loadRawJson5Content = loadRawJson5Content;
package/dist/esm/cli.js CHANGED
@@ -30,7 +30,7 @@ const program = new Command();
30
30
  program
31
31
  .name('i18next-cli')
32
32
  .description('A unified, high-performance i18next CLI.')
33
- .version('1.58.3'); // This string is replaced with the actual version at build time by rollup
33
+ .version('1.59.1'); // This string is replaced with the actual version at build time by rollup
34
34
  // new: global config override option
35
35
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
36
36
  program
@@ -131,10 +131,27 @@ program
131
131
  .command('types')
132
132
  .description('Generate TypeScript definitions from translation resource files.')
133
133
  .option('-w, --watch', 'Watch for file changes and re-run the type generator.')
134
+ .option('--ci', 'Exit with a non-zero status code if type definitions are not up to date (check-only, no writes).')
134
135
  .option('-q, --quiet', 'Suppress spinner and output')
135
136
  .action(async (options) => {
136
137
  const cfgPath = program.opts().config;
137
138
  const config = await ensureConfig(cfgPath);
139
+ if (options.ci && options.watch) {
140
+ console.error(styleText('red', '--ci and --watch cannot be used together'));
141
+ process.exit(1);
142
+ }
143
+ if (options.ci) {
144
+ const { changed, changedFiles } = await runTypesGenerator(config, { checkOnly: true });
145
+ if (changed) {
146
+ console.error(styleText('red', "❌ TypeScript definitions are out of date. Run 'i18next-cli types' and commit the changes."));
147
+ for (const file of changedFiles) {
148
+ console.error(` ${file}`);
149
+ }
150
+ process.exit(1);
151
+ }
152
+ console.log(styleText('green', '✅ TypeScript definitions are up to date.'));
153
+ return;
154
+ }
138
155
  const run = () => runTypesGenerator(config, { quiet: !!options.quiet });
139
156
  await run();
140
157
  if (options.watch) {
@@ -3,7 +3,7 @@ import { mergeResourcesAsInterface } from 'i18next-resources-for-ts';
3
3
  import { glob } from 'glob';
4
4
  import { createSpinnerLike } from './utils/wrap-ora.js';
5
5
  import { styleText } from 'node:util';
6
- import { mkdir, writeFile, access, readFile } from 'node:fs/promises';
6
+ import { readFile, access, mkdir, writeFile } from 'node:fs/promises';
7
7
  import { join, dirname, basename, extname, resolve, relative } from 'node:path';
8
8
  import { transform } from '@swc/core';
9
9
  import { getOutputPath } from './utils/file-utils.js';
@@ -92,7 +92,7 @@ function getNamespaceFromPath(filePath, basePath) {
92
92
  */
93
93
  async function runTypesGenerator(config, options = {}) {
94
94
  const internalLogger = options.logger ?? new ConsoleLogger();
95
- const spinner = createSpinnerLike('Generating TypeScript types for translations...\n', { quiet: !!options.quiet, logger: options.logger });
95
+ const spinner = createSpinnerLike('Generating TypeScript types for translations...\n', { quiet: !!options.quiet || !!options.checkOnly, logger: options.logger });
96
96
  try {
97
97
  config.extract.primaryLanguage ||= config.locales[0] || 'en';
98
98
  let defaultTypesInputPath = config.extract.output || `locales/${config.extract.primaryLanguage}/*.json`;
@@ -107,7 +107,7 @@ async function runTypesGenerator(config, options = {}) {
107
107
  config.types.resourcesFile = join(dirname(config.types?.output), 'resources.d.ts');
108
108
  if (!config.types?.input || config.types?.input.length < 0) {
109
109
  console.log('No input defined!');
110
- return;
110
+ return { changed: false, changedFiles: [] };
111
111
  }
112
112
  const resourceFiles = await glob(config.types.input, { cwd: process.cwd() });
113
113
  const basePath = config.types.basePath ? getOutputPath(config.types.basePath, config.extract.primaryLanguage) : undefined;
@@ -148,6 +148,26 @@ async function runTypesGenerator(config, options = {}) {
148
148
  ${mergeResourcesAsInterface(resources, { optimize: !!enableSelector, indentation })}`;
149
149
  const outputPath = resolve(process.cwd(), config.types?.output || '');
150
150
  const resourcesOutputPath = resolve(process.cwd(), config.types.resourcesFile);
151
+ if (options.checkOnly) {
152
+ const changedFiles = [];
153
+ let existingResources = null;
154
+ try {
155
+ existingResources = await readFile(resourcesOutputPath, 'utf-8');
156
+ }
157
+ catch {
158
+ existingResources = null;
159
+ }
160
+ if (existingResources !== interfaceDefinition) {
161
+ changedFiles.push(config.types.resourcesFile);
162
+ }
163
+ try {
164
+ await access(outputPath);
165
+ }
166
+ catch {
167
+ changedFiles.push(config.types.output || '');
168
+ }
169
+ return { changed: changedFiles.length > 0, changedFiles };
170
+ }
151
171
  await mkdir(dirname(resourcesOutputPath), { recursive: true });
152
172
  await writeFile(resourcesOutputPath, interfaceDefinition);
153
173
  logMessages.push(` ${styleText('green', '✓')} Resources interface written to ${config.types.resourcesFile}`);
@@ -181,6 +201,7 @@ declare module 'i18next' {
181
201
  }
182
202
  spinner.succeed(styleText('bold', 'TypeScript definitions generated successfully.'));
183
203
  logMessages.forEach(msg => typeof internalLogger.info === 'function' ? internalLogger.info(msg) : console.log(msg));
204
+ return { changed: false, changedFiles: [] };
184
205
  }
185
206
  catch (error) {
186
207
  spinner.fail(styleText('red', 'Failed to generate TypeScript definitions.'));
@@ -188,6 +209,7 @@ declare module 'i18next' {
188
209
  internalLogger.error(error);
189
210
  else
190
211
  console.error(error);
212
+ return { changed: false, changedFiles: [] };
191
213
  }
192
214
  }
193
215
 
@@ -5,6 +5,24 @@ import { getTsConfigAliases } from '../config.js';
5
5
  import { JsonParser, JsonObjectNode } from '@croct/json5-parser';
6
6
  import yaml from 'yaml';
7
7
 
8
+ /**
9
+ * Thrown when an existing translation file in a structured data format
10
+ * (JSON/JSON5/YAML) exists on disk but cannot be parsed. Callers should treat
11
+ * this as fatal rather than as an empty/missing file, since overwriting an
12
+ * unparseable file would silently destroy its contents (e.g. a merge conflict
13
+ * marker accidentally committed into an en.json).
14
+ */
15
+ class ParseTranslationFileError extends Error {
16
+ filePath;
17
+ cause;
18
+ constructor(filePath, cause) {
19
+ const detail = cause instanceof Error ? cause.message : String(cause);
20
+ super(`Could not parse translation file ${filePath}: ${detail}`);
21
+ this.filePath = filePath;
22
+ this.cause = cause;
23
+ this.name = 'ParseTranslationFileError';
24
+ }
25
+ }
8
26
  /**
9
27
  * Resolve an output template (string or function) into an actual path string.
10
28
  *
@@ -56,23 +74,48 @@ async function loadTranslationFile(filePath) {
56
74
  catch {
57
75
  return null; // File doesn't exist
58
76
  }
59
- try {
60
- const ext = extname(fullPath).toLowerCase();
61
- if (ext === '.json5') {
62
- const content = await readFile(fullPath, 'utf-8');
77
+ const ext = extname(fullPath).toLowerCase();
78
+ // Structured data formats (JSON/JSON5/YAML): the file exists (it passed the
79
+ // access() check above) but could not be parsed. Treating this as `null` is
80
+ // dangerous: callers coalesce it to an empty object and may overwrite the
81
+ // existing file, silently destroying its contents (e.g. when a merge conflict
82
+ // marker breaks an en.json that a watcher then re-extracts). Fail loudly so
83
+ // the caller can stop instead of overwriting good data with nothing.
84
+ if (ext === '.json5') {
85
+ const content = await readFile(fullPath, 'utf-8');
86
+ try {
63
87
  // Parse as a JSON5 object node
64
88
  const node = JsonParser.parse(content, JsonObjectNode);
65
89
  return node.toJSON();
66
90
  }
67
- else if (ext === '.yaml' || ext === '.yml') {
68
- const content = await readFile(fullPath, 'utf-8');
91
+ catch (error) {
92
+ throw new ParseTranslationFileError(filePath, error);
93
+ }
94
+ }
95
+ else if (ext === '.yaml' || ext === '.yml') {
96
+ const content = await readFile(fullPath, 'utf-8');
97
+ try {
69
98
  return yaml.parse(content);
70
99
  }
71
- else if (ext === '.json') {
72
- const content = await readFile(fullPath, 'utf-8');
100
+ catch (error) {
101
+ throw new ParseTranslationFileError(filePath, error);
102
+ }
103
+ }
104
+ else if (ext === '.json') {
105
+ const content = await readFile(fullPath, 'utf-8');
106
+ try {
73
107
  return JSON.parse(content);
74
108
  }
75
- else if (ext === '.ts' || ext === '.js') {
109
+ catch (error) {
110
+ throw new ParseTranslationFileError(filePath, error);
111
+ }
112
+ }
113
+ else if (ext === '.ts' || ext === '.js') {
114
+ // .ts/.js resource files are loaded via jiti, which can fail for reasons
115
+ // unrelated to corruption (e.g. a transitive import or a side-effectful
116
+ // module). This path has been deliberately lenient since #59, so keep
117
+ // degrading to `null` here rather than aborting the whole command.
118
+ try {
76
119
  // Load TypeScript path aliases for proper module resolution
77
120
  const aliases = await getTsConfigAliases();
78
121
  const jiti = createJiti(process.cwd(), {
@@ -82,12 +125,12 @@ async function loadTranslationFile(filePath) {
82
125
  const module = await jiti.import(fullPath, { default: true });
83
126
  return module;
84
127
  }
85
- return null; // Unsupported file type
86
- }
87
- catch (error) {
88
- console.warn(`Could not parse translation file ${filePath}:`, error);
89
- return null;
128
+ catch (error) {
129
+ console.warn(`Could not parse translation file ${filePath}:`, error);
130
+ return null;
131
+ }
90
132
  }
133
+ return null; // Unsupported file type
91
134
  }
92
135
  // Helper to load raw JSON5 content for preservation
93
136
  async function loadRawJson5Content(filePath) {
@@ -154,4 +197,4 @@ function inferFormatFromPath(filePath, defaultFormat = 'json') {
154
197
  return defaultFormat || 'json';
155
198
  }
156
199
 
157
- export { getOutputPath, inferFormatFromPath, loadRawJson5Content, loadTranslationFile, serializeTranslationFile };
200
+ export { ParseTranslationFileError, getOutputPath, inferFormatFromPath, loadRawJson5Content, loadTranslationFile, serializeTranslationFile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.58.3",
3
+ "version": "1.59.1",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAoBnC,QAAA,MAAM,OAAO,SAAgB,CAAA;AA8Z7B,OAAO,EAAE,OAAO,EAAE,CAAA"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAoBnC,QAAA,MAAM,OAAO,SAAgB,CAAA;AAib7B,OAAO,EAAE,OAAO,EAAE,CAAA"}
@@ -28,5 +28,9 @@ import type { I18nextToolkitConfig, Logger } from './types.js';
28
28
  export declare function runTypesGenerator(config: I18nextToolkitConfig, options?: {
29
29
  quiet?: boolean;
30
30
  logger?: Logger;
31
- }): Promise<void>;
31
+ checkOnly?: boolean;
32
+ }): Promise<{
33
+ changed: boolean;
34
+ changedFiles: string[];
35
+ }>;
32
36
  //# sourceMappingURL=types-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-generator.d.ts","sourceRoot":"","sources":["../src/types-generator.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AA6E9D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,iBA+GnD"}
1
+ {"version":3,"file":"types-generator.d.ts","sourceRoot":"","sources":["../src/types-generator.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AA6E9D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACtE,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAuIvD"}
@@ -1,4 +1,16 @@
1
1
  import type { I18nextToolkitConfig } from '../types.js';
2
+ /**
3
+ * Thrown when an existing translation file in a structured data format
4
+ * (JSON/JSON5/YAML) exists on disk but cannot be parsed. Callers should treat
5
+ * this as fatal rather than as an empty/missing file, since overwriting an
6
+ * unparseable file would silently destroy its contents (e.g. a merge conflict
7
+ * marker accidentally committed into an en.json).
8
+ */
9
+ export declare class ParseTranslationFileError extends Error {
10
+ readonly filePath: string;
11
+ readonly cause?: unknown | undefined;
12
+ constructor(filePath: string, cause?: unknown | undefined);
13
+ }
2
14
  /**
3
15
  * Ensures that the directory for a given file path exists.
4
16
  * Creates all necessary parent directories recursively if they don't exist.
@@ -1 +1 @@
1
- {"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAKvD;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,EACvF,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA8BR;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,CAwChG;AAGD,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQnF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,EAChE,WAAW,GAAE,MAAM,GAAG,MAAU,EAChC,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CA6BR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,GACtE,WAAW,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC,CAO9D"}
1
+ {"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAKvD;;;;;;GAMG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aACrB,QAAQ,EAAE,MAAM;aAAkB,KAAK,CAAC,EAAE,OAAO;gBAAjD,QAAQ,EAAE,MAAM,EAAkB,KAAK,CAAC,EAAE,OAAO,YAAA;CAK/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,EACvF,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA8BR;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,CA8DhG;AAGD,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQnF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,EAChE,WAAW,GAAE,MAAM,GAAG,MAAU,EAChC,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CA6BR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,GACtE,WAAW,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC,CAO9D"}