@vocab/core 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,7 +27,7 @@ var IntlMessageFormat__default = /*#__PURE__*/_interopDefault(IntlMessageFormat)
27
27
  var findUp__default = /*#__PURE__*/_interopDefault(findUp);
28
28
  var Validator__default = /*#__PURE__*/_interopDefault(Validator);
29
29
 
30
- const trace = debug__default['default'](`vocab:core`);
30
+ const trace = debug__default["default"](`vocab:core`);
31
31
 
32
32
  const defaultTranslationDirSuffix = '.vocab';
33
33
  const devTranslationFileName = 'translations.json';
@@ -74,37 +74,35 @@ function getAltLanguages({
74
74
  return languages.map(v => v.name).filter(lang => lang !== devLanguage);
75
75
  }
76
76
  function getDevLanguageFileFromTsFile(tsFilePath) {
77
- const directory = path__default['default'].dirname(tsFilePath);
78
- const result = path__default['default'].normalize(path__default['default'].join(directory, devTranslationFileName));
77
+ const directory = path__default["default"].dirname(tsFilePath);
78
+ const result = path__default["default"].normalize(path__default["default"].join(directory, devTranslationFileName));
79
79
  trace(`Returning dev language path ${result} for path ${tsFilePath}`);
80
80
  return result;
81
81
  }
82
82
  function getDevLanguageFileFromAltLanguageFile(altLanguageFilePath) {
83
- const directory = path__default['default'].dirname(altLanguageFilePath);
84
- const result = path__default['default'].normalize(path__default['default'].join(directory, devTranslationFileName));
83
+ const directory = path__default["default"].dirname(altLanguageFilePath);
84
+ const result = path__default["default"].normalize(path__default["default"].join(directory, devTranslationFileName));
85
85
  trace(`Returning dev language path ${result} for path ${altLanguageFilePath}`);
86
86
  return result;
87
87
  }
88
88
  function getTSFileFromDevLanguageFile(devLanguageFilePath) {
89
- const directory = path__default['default'].dirname(devLanguageFilePath);
90
- const result = path__default['default'].normalize(path__default['default'].join(directory, 'index.ts'));
89
+ const directory = path__default["default"].dirname(devLanguageFilePath);
90
+ const result = path__default["default"].normalize(path__default["default"].join(directory, 'index.ts'));
91
91
  trace(`Returning TS path ${result} for path ${devLanguageFilePath}`);
92
92
  return result;
93
93
  }
94
94
  function getAltLanguageFilePath(devLanguageFilePath, language) {
95
- const directory = path__default['default'].dirname(devLanguageFilePath);
96
- const result = path__default['default'].normalize(path__default['default'].join(directory, `${language}.translations.json`));
95
+ const directory = path__default["default"].dirname(devLanguageFilePath);
96
+ const result = path__default["default"].normalize(path__default["default"].join(directory, `${language}.translations.json`));
97
97
  trace(`Returning alt language path ${result} for path ${devLanguageFilePath}`);
98
- return path__default['default'].normalize(result);
98
+ return path__default["default"].normalize(result);
99
99
  }
100
100
  function mapValues(obj, func) {
101
101
  const newObj = {};
102
102
  const keys = Object.keys(obj);
103
-
104
103
  for (const key of keys) {
105
104
  newObj[key] = func(obj[key]);
106
105
  }
107
-
108
106
  return newObj;
109
107
  }
110
108
  function getTranslationMessages(translations) {
@@ -118,60 +116,49 @@ function generateLanguageFromTranslations({
118
116
  if (!generator.transformElement && !generator.transformMessage) {
119
117
  return baseTranslations;
120
118
  }
121
-
122
119
  const translationKeys = Object.keys(baseTranslations);
123
120
  const generatedTranslations = {};
124
-
125
121
  for (const translationKey of translationKeys) {
126
122
  const translation = baseTranslations[translationKey];
127
123
  let transformedMessage = translation.message;
128
-
129
124
  if (generator.transformElement) {
130
- const messageAst = new IntlMessageFormat__default['default'](translation.message).getAst();
125
+ const messageAst = new IntlMessageFormat__default["default"](translation.message).getAst();
131
126
  const transformedAst = messageAst.map(transformMessageFormatElement(generator.transformElement));
132
127
  transformedMessage = printer.printAST(transformedAst);
133
128
  }
134
-
135
129
  if (generator.transformMessage) {
136
130
  transformedMessage = generator.transformMessage(transformedMessage);
137
131
  }
138
-
139
132
  generatedTranslations[translationKey] = {
140
133
  message: transformedMessage
141
134
  };
142
135
  }
143
-
144
136
  return generatedTranslations;
145
137
  }
146
-
147
138
  function transformMessageFormatElement(transformElement) {
148
139
  return messageFormatElement => {
149
- const transformedMessageFormatElement = { ...messageFormatElement
140
+ const transformedMessageFormatElement = {
141
+ ...messageFormatElement
150
142
  };
151
-
152
143
  switch (transformedMessageFormatElement.type) {
153
144
  case icuMessageformatParser.TYPE.literal:
154
145
  const transformedValue = transformElement(transformedMessageFormatElement.value);
155
146
  transformedMessageFormatElement.value = transformedValue;
156
147
  break;
157
-
158
148
  case icuMessageformatParser.TYPE.select:
159
149
  case icuMessageformatParser.TYPE.plural:
160
- const transformedOptions = { ...transformedMessageFormatElement.options
150
+ const transformedOptions = {
151
+ ...transformedMessageFormatElement.options
161
152
  };
162
-
163
153
  for (const key of Object.keys(transformedOptions)) {
164
154
  transformedOptions[key].value = transformedOptions[key].value.map(transformMessageFormatElement(transformElement));
165
155
  }
166
-
167
156
  break;
168
-
169
157
  case icuMessageformatParser.TYPE.tag:
170
158
  const transformedChildren = transformedMessageFormatElement.children.map(transformMessageFormatElement(transformElement));
171
159
  transformedMessageFormatElement.children = transformedChildren;
172
160
  break;
173
161
  }
174
-
175
162
  return transformedMessageFormatElement;
176
163
  };
177
164
  }
@@ -186,7 +173,6 @@ function mergeWithDevLanguageTranslation({
186
173
  // Only use keys from the dev translation
187
174
  const keys = Object.keys(devTranslation);
188
175
  const newLanguage = {};
189
-
190
176
  for (const key of keys) {
191
177
  if (translation[key]) {
192
178
  newLanguage[key] = {
@@ -195,24 +181,19 @@ function mergeWithDevLanguageTranslation({
195
181
  };
196
182
  }
197
183
  }
198
-
199
184
  return newLanguage;
200
185
  }
201
-
202
186
  function getLanguageFallbacks({
203
187
  languages
204
188
  }) {
205
189
  const languageFallbackMap = new Map();
206
-
207
190
  for (const lang of languages) {
208
191
  if (lang.extends) {
209
192
  languageFallbackMap.set(lang.name, lang.extends);
210
193
  }
211
194
  }
212
-
213
195
  return languageFallbackMap;
214
196
  }
215
-
216
197
  function getLanguageHierarchy({
217
198
  languages
218
199
  }) {
@@ -220,19 +201,15 @@ function getLanguageHierarchy({
220
201
  const fallbacks = getLanguageFallbacks({
221
202
  languages
222
203
  });
223
-
224
204
  for (const lang of languages) {
225
205
  const langHierarchy = [];
226
206
  let currLang = lang.extends;
227
-
228
207
  while (currLang) {
229
208
  langHierarchy.push(currLang);
230
209
  currLang = fallbacks.get(currLang);
231
210
  }
232
-
233
211
  hierarchyMap.set(lang.name, langHierarchy);
234
212
  }
235
-
236
213
  return hierarchyMap;
237
214
  }
238
215
  function getFallbackLanguageOrder({
@@ -244,41 +221,31 @@ function getFallbackLanguageOrder({
244
221
  const languageHierarchy = getLanguageHierarchy({
245
222
  languages
246
223
  }).get(languageName);
247
-
248
224
  if (!languageHierarchy) {
249
225
  throw new Error(`Missing language hierarchy for ${languageName}`);
250
226
  }
251
-
252
227
  const fallbackLanguageOrder = [languageName];
253
-
254
228
  if (fallbacks !== 'none') {
255
229
  fallbackLanguageOrder.unshift(...languageHierarchy.reverse());
256
-
257
230
  if (fallbacks === 'all' && fallbackLanguageOrder[0] !== devLanguage) {
258
231
  fallbackLanguageOrder.unshift(devLanguage);
259
232
  }
260
233
  }
261
-
262
234
  return fallbackLanguageOrder;
263
235
  }
264
-
265
236
  function getNamespaceByFilePath(relativePath, {
266
237
  translationsDirectorySuffix = defaultTranslationDirSuffix
267
238
  }) {
268
- let namespace = path__default['default'].dirname(relativePath).replace(/^src\//, '').replace(/\//g, '_');
269
-
239
+ let namespace = path__default["default"].dirname(relativePath).replace(/^src\//, '').replace(/\//g, '_');
270
240
  if (namespace.endsWith(translationsDirectorySuffix)) {
271
241
  namespace = namespace.slice(0, -translationsDirectorySuffix.length);
272
242
  }
273
-
274
243
  return namespace;
275
244
  }
276
-
277
245
  function printValidationError(...params) {
278
246
  // eslint-disable-next-line no-console
279
- console.error(chalk__default['default'].red('Error loading translation:'), ...params);
247
+ console.error(chalk__default["default"].red('Error loading translation:'), ...params);
280
248
  }
281
-
282
249
  function getTranslationsFromFile(translationFileContents, {
283
250
  isAltLanguage,
284
251
  filePath,
@@ -287,29 +254,24 @@ function getTranslationsFromFile(translationFileContents, {
287
254
  if (!translationFileContents || typeof translationFileContents !== 'object') {
288
255
  throw new Error(`Unable to read translation file ${filePath}. Translations must be an object.`);
289
256
  }
290
-
291
257
  const {
292
258
  $namespace,
293
259
  _meta,
294
260
  ...keys
295
261
  } = translationFileContents;
296
-
297
262
  if (isAltLanguage && $namespace) {
298
263
  printValidationError(`Found $namespace in alt language file in ${filePath}. $namespace is only used in the dev language and will be ignored.`);
299
264
  }
300
-
301
265
  if (!isAltLanguage && $namespace && typeof $namespace !== 'string') {
302
266
  printValidationError(`Found non-string $namespace in language file in ${filePath}. $namespace must be a string.`);
303
267
  }
304
-
305
268
  if (isAltLanguage && _meta !== null && _meta !== void 0 && _meta.tags) {
306
269
  printValidationError(`Found _meta.tags in alt language file in ${filePath}. _meta.tags is only used in the dev language and will be ignored.`);
307
- } // Never return tags if we're fetching translations for an alt language
308
-
270
+ }
309
271
 
272
+ // Never return tags if we're fetching translations for an alt language
310
273
  const includeTags = !isAltLanguage && withTags;
311
274
  const validKeys = {};
312
-
313
275
  for (const [translationKey, {
314
276
  tags,
315
277
  ...translation
@@ -318,22 +280,19 @@ function getTranslationsFromFile(translationFileContents, {
318
280
  printValidationError(`Found string for a translation "${translationKey}" in ${filePath}. Translation must be an object of the format {message: string}.`);
319
281
  continue;
320
282
  }
321
-
322
283
  if (!translation) {
323
284
  printValidationError(`Found empty translation "${translationKey}" in ${filePath}. Translation must be an object of the format {message: string}.`);
324
285
  continue;
325
286
  }
326
-
327
287
  if (!translation.message || typeof translation.message !== 'string') {
328
288
  printValidationError(`No message found for translation "${translationKey}" in ${filePath}. Translation must be an object of the format {message: string}.`);
329
289
  continue;
330
290
  }
331
-
332
- validKeys[translationKey] = { ...translation,
291
+ validKeys[translationKey] = {
292
+ ...translation,
333
293
  tags: includeTags ? tags : undefined
334
294
  };
335
295
  }
336
-
337
296
  const metadata = {
338
297
  tags: includeTags ? _meta === null || _meta === void 0 ? void 0 : _meta.tags : undefined
339
298
  };
@@ -343,7 +302,6 @@ function getTranslationsFromFile(translationFileContents, {
343
302
  metadata
344
303
  };
345
304
  }
346
-
347
305
  function loadAltLanguageFile({
348
306
  filePath,
349
307
  languageName,
@@ -361,15 +319,12 @@ function loadAltLanguageFile({
361
319
  fallbacks
362
320
  });
363
321
  trace(`Loading alt language file with precedence: ${fallbackLanguageOrder.slice().reverse().join(' -> ')}`);
364
-
365
322
  for (const fallbackLanguage of fallbackLanguageOrder) {
366
323
  if (fallbackLanguage !== devLanguage) {
367
324
  try {
368
325
  const altFilePath = getAltLanguageFilePath(filePath, fallbackLanguage);
369
326
  delete require.cache[altFilePath];
370
-
371
327
  const translationFile = require(altFilePath);
372
-
373
328
  const {
374
329
  keys: fallbackLanguageTranslation
375
330
  } = getTranslationsFromFile(translationFile, {
@@ -388,17 +343,14 @@ function loadAltLanguageFile({
388
343
  Object.assign(altLanguageTranslation, devTranslation);
389
344
  }
390
345
  }
391
-
392
346
  return altLanguageTranslation;
393
347
  }
394
-
395
348
  function stripTagsFromTranslations(translations) {
396
349
  return Object.fromEntries(Object.entries(translations).map(([key, {
397
350
  tags,
398
351
  ...rest
399
352
  }]) => [key, rest]));
400
353
  }
401
-
402
354
  function loadTranslation({
403
355
  filePath,
404
356
  fallbacks,
@@ -407,10 +359,8 @@ function loadTranslation({
407
359
  trace(`Loading translation file in "${fallbacks}" fallback mode: "${filePath}"`);
408
360
  const languageSet = {};
409
361
  delete require.cache[filePath];
410
-
411
362
  const translationContent = require(filePath);
412
-
413
- const relativePath = path__default['default'].relative(userConfig.projectRoot || process.cwd(), filePath);
363
+ const relativePath = path__default["default"].relative(userConfig.projectRoot || process.cwd(), filePath);
414
364
  const {
415
365
  $namespace,
416
366
  keys: devTranslation,
@@ -425,7 +375,6 @@ function loadTranslation({
425
375
  languageSet[userConfig.devLanguage] = devTranslation;
426
376
  const devTranslationNoTags = withTags ? stripTagsFromTranslations(devTranslation) : devTranslation;
427
377
  const altLanguages = getAltLanguages(userConfig);
428
-
429
378
  for (const languageName of altLanguages) {
430
379
  languageSet[languageName] = loadAltLanguageFile({
431
380
  filePath,
@@ -434,7 +383,6 @@ function loadTranslation({
434
383
  fallbacks
435
384
  }, userConfig);
436
385
  }
437
-
438
386
  for (const generatedLanguage of userConfig.generatedLanguages || []) {
439
387
  const {
440
388
  name: generatedLanguageName,
@@ -447,7 +395,6 @@ function loadTranslation({
447
395
  generator
448
396
  });
449
397
  }
450
-
451
398
  return {
452
399
  filePath,
453
400
  keys: Object.keys(devTranslation),
@@ -466,7 +413,7 @@ async function loadAllTranslations({
466
413
  projectRoot,
467
414
  ignore = []
468
415
  } = config;
469
- const translationFiles = await glob__default['default'](getDevTranslationFileGlob(config), {
416
+ const translationFiles = await glob__default["default"](getDevTranslationFileGlob(config), {
470
417
  ignore: includeNodeModules ? ignore : [...ignore, '**/node_modules/**'],
471
418
  absolute: true,
472
419
  cwd: projectRoot
@@ -478,42 +425,33 @@ async function loadAllTranslations({
478
425
  withTags
479
426
  }, config)));
480
427
  const keys = new Set();
481
-
482
428
  for (const loadedTranslation of result) {
483
429
  for (const key of loadedTranslation.keys) {
484
430
  const uniqueKey = getUniqueKey(key, loadedTranslation.namespace);
485
-
486
431
  if (keys.has(uniqueKey)) {
487
432
  trace(`Duplicate keys found`);
488
433
  throw new Error(`Duplicate keys found. Key with namespace ${loadedTranslation.namespace} and key ${key} was found multiple times.`);
489
434
  }
490
-
491
435
  keys.add(uniqueKey);
492
436
  }
493
437
  }
494
-
495
438
  return result;
496
439
  }
497
440
 
498
441
  const encodeWithinSingleQuotes = v => v.replace(/'/g, "\\'");
499
-
500
442
  const encodeBackslash = v => v.replace(/\\/g, '\\\\');
501
-
502
443
  function extractHasTags(ast) {
503
444
  return ast.some(element => {
504
445
  if (icuMessageformatParser.isSelectElement(element)) {
505
446
  const children = Object.values(element.options).map(o => o.value);
506
447
  return children.some(child => extractHasTags(child));
507
448
  }
508
-
509
449
  return icuMessageformatParser.isTagElement(element);
510
450
  });
511
451
  }
512
-
513
452
  function extractParamTypes(ast) {
514
453
  let params = {};
515
- let imports = new Set();
516
-
454
+ let vocabTypesImports = new Set();
517
455
  for (const element of ast) {
518
456
  if (icuMessageformatParser.isArgumentElement(element)) {
519
457
  params[element.value] = 'string';
@@ -521,36 +459,49 @@ function extractParamTypes(ast) {
521
459
  params[element.value] = 'number';
522
460
  } else if (icuMessageformatParser.isPluralElement(element)) {
523
461
  params[element.value] = 'number';
462
+ const children = Object.values(element.options).map(o => o.value);
463
+ for (const child of children) {
464
+ const [subParams, subImports] = extractParamTypes(child);
465
+ vocabTypesImports = new Set([...vocabTypesImports, ...subImports]);
466
+ params = {
467
+ ...params,
468
+ ...subParams
469
+ };
470
+ }
524
471
  } else if (icuMessageformatParser.isDateElement(element) || icuMessageformatParser.isTimeElement(element)) {
525
472
  params[element.value] = 'Date | number';
526
473
  } else if (icuMessageformatParser.isTagElement(element)) {
527
474
  params[element.value] = 'FormatXMLElementFn<T>';
528
- imports.add(`import { FormatXMLElementFn } from '@vocab/types';`);
475
+ vocabTypesImports.add('FormatXMLElementFn');
529
476
  const [subParams, subImports] = extractParamTypes(element.children);
530
- imports = new Set([...imports, ...subImports]);
531
- params = { ...params,
477
+ vocabTypesImports = new Set([...vocabTypesImports, ...subImports]);
478
+ params = {
479
+ ...params,
532
480
  ...subParams
533
481
  };
534
482
  } else if (icuMessageformatParser.isSelectElement(element)) {
535
- params[element.value] = Object.keys(element.options).map(o => `'${o}'`).join(' | ');
536
- const children = Object.values(element.options).map(o => o.value);
483
+ const options = Object.keys(element.options);
537
484
 
485
+ // `other` will always be an option as the parser enforces this by default
486
+ const nonOtherOptions = options.filter(o => o !== 'other');
487
+ const nonOtherOptionsUnion = nonOtherOptions.map(o => `'${o}'`).join(' | ');
488
+ params[element.value] = `StringWithSuggestions<${nonOtherOptionsUnion}>`;
489
+ vocabTypesImports.add('StringWithSuggestions');
490
+ const children = Object.values(element.options).map(o => o.value);
538
491
  for (const child of children) {
539
492
  const [subParams, subImports] = extractParamTypes(child);
540
- imports = new Set([...imports, ...subImports]);
541
- params = { ...params,
493
+ vocabTypesImports = new Set([...vocabTypesImports, ...subImports]);
494
+ params = {
495
+ ...params,
542
496
  ...subParams
543
497
  };
544
498
  }
545
499
  }
546
500
  }
547
-
548
- return [params, imports];
501
+ return [params, vocabTypesImports];
549
502
  }
550
-
551
503
  function serialiseObjectToType(v) {
552
504
  let result = '';
553
-
554
505
  for (const [key, value] of Object.entries(v)) {
555
506
  if (value && typeof value === 'object') {
556
507
  result += `'${encodeWithinSingleQuotes(key)}': ${serialiseObjectToType(value)},`;
@@ -558,44 +509,40 @@ function serialiseObjectToType(v) {
558
509
  result += `'${encodeWithinSingleQuotes(key)}': ${value},`;
559
510
  }
560
511
  }
561
-
562
512
  return `{ ${result} }`;
563
513
  }
564
-
565
514
  const banner = `// This file is automatically generated by Vocab.\n// To make changes update translation.json files directly.`;
566
-
515
+ const serializeModuleImports = (imports, moduleName) => {
516
+ const importNames = Array.from(imports);
517
+ return `import { ${Array.from(importNames).join(', ')} } from '${moduleName}'`;
518
+ };
567
519
  function serialiseTranslationRuntime(value, imports, loadedTranslation) {
568
520
  trace('Serialising translations:', loadedTranslation);
569
521
  const translationsType = {};
570
-
571
522
  for (const [key, {
572
523
  params,
573
524
  message,
574
525
  hasTags
575
526
  }] of value.entries()) {
576
527
  let translationFunctionString = `() => ${message}`;
577
-
578
528
  if (Object.keys(params).length > 0) {
579
529
  const formatGeneric = hasTags ? '<T = string>' : '';
580
530
  const formatReturn = hasTags ? 'string | T | Array<string | T>' : 'string';
581
531
  translationFunctionString = `${formatGeneric}(values: ${serialiseObjectToType(params)}) => ${formatReturn}`;
582
532
  }
583
-
584
533
  translationsType[encodeBackslash(key)] = translationFunctionString;
585
534
  }
586
-
587
535
  const content = Object.entries(loadedTranslation.languages).map(([languageName, translations]) => `'${encodeWithinSingleQuotes(languageName)}': createLanguage(${JSON.stringify(getTranslationMessages(translations))})`).join(',');
588
536
  const languagesUnionAsString = Object.keys(loadedTranslation.languages).map(l => `'${l}'`).join(' | ');
589
537
  return `${banner}
590
538
 
591
- ${Array.from(imports).join('\n')}
539
+ ${serializeModuleImports(imports, '@vocab/types')}
592
540
  import { createLanguage, createTranslationFile } from '@vocab/core/runtime';
593
541
 
594
542
  const translations = createTranslationFile<${languagesUnionAsString}, ${serialiseObjectToType(translationsType)}>({${content}});
595
543
 
596
544
  export default translations;`;
597
545
  }
598
-
599
546
  async function generateRuntime(loadedTranslation) {
600
547
  const {
601
548
  languages: loadedLanguages,
@@ -604,25 +551,23 @@ async function generateRuntime(loadedTranslation) {
604
551
  trace('Generating types for', filePath);
605
552
  const translationTypes = new Map();
606
553
  let imports = new Set();
607
-
608
554
  for (const key of loadedTranslation.keys) {
609
555
  let params = {};
610
556
  const messages = new Set();
611
557
  let hasTags = false;
612
-
613
558
  for (const translatedLanguage of Object.values(loadedLanguages)) {
614
559
  if (translatedLanguage[key]) {
615
560
  const ast = icuMessageformatParser.parse(translatedLanguage[key].message);
616
561
  hasTags = hasTags || extractHasTags(ast);
617
- const [parsedParams, parsedImports] = extractParamTypes(ast);
618
- imports = new Set([...imports, ...parsedImports]);
619
- params = { ...params,
562
+ const [parsedParams, vocabTypesImports] = extractParamTypes(ast);
563
+ imports = new Set([...imports, ...vocabTypesImports]);
564
+ params = {
565
+ ...params,
620
566
  ...parsedParams
621
567
  };
622
568
  messages.add(`'${encodeWithinSingleQuotes(translatedLanguage[key].message)}'`);
623
569
  }
624
570
  }
625
-
626
571
  const returnType = hasTags ? 'NonNullable<ReactNode>' : 'string';
627
572
  translationTypes.set(key, {
628
573
  params,
@@ -631,10 +576,10 @@ async function generateRuntime(loadedTranslation) {
631
576
  returnType
632
577
  });
633
578
  }
634
-
635
- const prettierConfig = await prettier__default['default'].resolveConfig(filePath);
579
+ const prettierConfig = await prettier__default["default"].resolveConfig(filePath);
636
580
  const serializedTranslationType = serialiseTranslationRuntime(translationTypes, imports, loadedTranslation);
637
- const declaration = prettier__default['default'].format(serializedTranslationType, { ...prettierConfig,
581
+ const declaration = prettier__default["default"].format(serializedTranslationType, {
582
+ ...prettierConfig,
638
583
  parser: 'typescript'
639
584
  });
640
585
  const outputFilePath = getTSFileFromDevLanguageFile(filePath);
@@ -643,22 +588,19 @@ async function generateRuntime(loadedTranslation) {
643
588
  }
644
589
  function watch(config) {
645
590
  const cwd = config.projectRoot || process.cwd();
646
- const watcher = chokidar__default['default'].watch([getDevTranslationFileGlob(config), getAltTranslationFileGlob(config), getTranslationFolderGlob(config)], {
591
+ const watcher = chokidar__default["default"].watch([getDevTranslationFileGlob(config), getAltTranslationFileGlob(config), getTranslationFolderGlob(config)], {
647
592
  cwd,
648
593
  ignored: config.ignore ? [...config.ignore, '**/node_modules/**'] : ['**/node_modules/**'],
649
594
  ignoreInitial: true
650
595
  });
651
-
652
596
  const onTranslationChange = async relativePath => {
653
597
  trace(`Detected change for file ${relativePath}`);
654
598
  let targetFile;
655
-
656
599
  if (isDevLanguageFile(relativePath)) {
657
- targetFile = path__default['default'].resolve(cwd, relativePath);
600
+ targetFile = path__default["default"].resolve(cwd, relativePath);
658
601
  } else if (isAltLanguageFile(relativePath)) {
659
- targetFile = getDevLanguageFileFromAltLanguageFile(path__default['default'].resolve(cwd, relativePath));
602
+ targetFile = getDevLanguageFileFromAltLanguageFile(path__default["default"].resolve(cwd, relativePath));
660
603
  }
661
-
662
604
  if (targetFile) {
663
605
  try {
664
606
  const loadedTranslation = loadTranslation({
@@ -668,23 +610,19 @@ function watch(config) {
668
610
  await generateRuntime(loadedTranslation);
669
611
  } catch (e) {
670
612
  // eslint-disable-next-line no-console
671
- console.log('Failed to generate types for', relativePath); // eslint-disable-next-line no-console
672
-
613
+ console.log('Failed to generate types for', relativePath);
614
+ // eslint-disable-next-line no-console
673
615
  console.error(e);
674
616
  }
675
617
  }
676
618
  };
677
-
678
619
  const onNewDirectory = async relativePath => {
679
620
  trace('Detected new directory', relativePath);
680
-
681
621
  if (!isTranslationDirectory(relativePath, config)) {
682
622
  trace('Ignoring non-translation directory:', relativePath);
683
623
  return;
684
624
  }
685
-
686
- const newFilePath = path__default['default'].join(relativePath, devTranslationFileName);
687
-
625
+ const newFilePath = path__default["default"].join(relativePath, devTranslationFileName);
688
626
  if (!fs.existsSync(newFilePath)) {
689
627
  await fs.promises.writeFile(newFilePath, JSON.stringify({}, null, 2));
690
628
  trace('Created new empty translation file:', newFilePath);
@@ -692,7 +630,6 @@ function watch(config) {
692
630
  trace(`New directory already contains translation file. Skipping creation. Existing file ${newFilePath}`);
693
631
  }
694
632
  };
695
-
696
633
  watcher.on('addDir', onNewDirectory);
697
634
  watcher.on('add', onTranslationChange).on('change', onTranslationChange);
698
635
  return () => watcher.close();
@@ -704,28 +641,24 @@ async function compile({
704
641
  fallbacks: 'all',
705
642
  includeNodeModules: false
706
643
  }, config);
707
-
708
644
  for (const loadedTranslation of translations) {
709
645
  await generateRuntime(loadedTranslation);
710
646
  }
711
-
712
647
  if (shouldWatch) {
713
648
  trace('Listening for changes to files...');
714
649
  return watch(config);
715
650
  }
716
651
  }
717
-
718
652
  async function writeIfChanged(filepath, contents) {
719
653
  let hasChanged = true;
720
-
721
654
  try {
722
655
  const existingContents = await fs.promises.readFile(filepath, {
723
656
  encoding: 'utf-8'
724
657
  });
725
658
  hasChanged = existingContents !== contents;
726
- } catch (e) {// ignore error, likely a file doesn't exist error so we want to write anyway
659
+ } catch (e) {
660
+ // ignore error, likely a file doesn't exist error so we want to write anyway
727
661
  }
728
-
729
662
  if (hasChanged) {
730
663
  await fs.promises.writeFile(filepath, contents, {
731
664
  encoding: 'utf-8'
@@ -736,36 +669,28 @@ async function writeIfChanged(filepath, contents) {
736
669
  /* eslint-disable no-console */
737
670
  function findMissingKeys(loadedTranslation, devLanguageName, altLanguages) {
738
671
  const devLanguage = loadedTranslation.languages[devLanguageName];
739
-
740
672
  if (!devLanguage) {
741
673
  throw new Error(`Failed to load dev language: ${loadedTranslation.filePath}`);
742
674
  }
743
-
744
675
  const result = {};
745
676
  let valid = true;
746
677
  const requiredKeys = Object.keys(devLanguage);
747
-
748
678
  if (requiredKeys.length > 0) {
749
679
  for (const altLanguageName of altLanguages) {
750
680
  var _loadedTranslation$la;
751
-
752
681
  const altLanguage = (_loadedTranslation$la = loadedTranslation.languages[altLanguageName]) !== null && _loadedTranslation$la !== void 0 ? _loadedTranslation$la : {};
753
-
754
682
  for (const key of requiredKeys) {
755
683
  var _altLanguage$key;
756
-
757
684
  if (typeof ((_altLanguage$key = altLanguage[key]) === null || _altLanguage$key === void 0 ? void 0 : _altLanguage$key.message) !== 'string') {
758
685
  if (!result[altLanguageName]) {
759
686
  result[altLanguageName] = [];
760
687
  }
761
-
762
688
  result[altLanguageName].push(key);
763
689
  valid = false;
764
690
  }
765
691
  }
766
692
  }
767
693
  }
768
-
769
694
  return [valid, result];
770
695
  }
771
696
  async function validate(config) {
@@ -774,21 +699,17 @@ async function validate(config) {
774
699
  includeNodeModules: true
775
700
  }, config);
776
701
  let valid = true;
777
-
778
702
  for (const loadedTranslation of allTranslations) {
779
703
  const [translationValid, result] = findMissingKeys(loadedTranslation, config.devLanguage, getAltLanguages(config));
780
-
781
704
  if (!translationValid) {
782
705
  valid = false;
783
- console.log(chalk__default['default'].red`Incomplete translations: "${chalk__default['default'].bold(loadedTranslation.relativePath)}"`);
784
-
706
+ console.log(chalk__default["default"].red`Incomplete translations: "${chalk__default["default"].bold(loadedTranslation.relativePath)}"`);
785
707
  for (const lang of Object.keys(result)) {
786
708
  const missingKeys = result[lang];
787
- console.log(chalk__default['default'].yellow(lang), '->', missingKeys.map(v => `"${v}"`).join(', '));
709
+ console.log(chalk__default["default"].yellow(lang), '->', missingKeys.map(v => `"${v}"`).join(', '));
788
710
  }
789
711
  }
790
712
  }
791
-
792
713
  return valid;
793
714
  }
794
715
 
@@ -798,10 +719,9 @@ class ValidationError extends Error {
798
719
  this.code = code;
799
720
  this.rawMessage = message;
800
721
  }
801
-
802
722
  }
803
723
 
804
- const validator = new Validator__default['default']();
724
+ const validator = new Validator__default["default"]();
805
725
  const schema = {
806
726
  $$strict: true,
807
727
  devLanguage: {
@@ -866,102 +786,85 @@ const schema = {
866
786
  }
867
787
  };
868
788
  const checkConfigFile = validator.compile(schema);
869
-
870
789
  const splitMap = (message, callback) => message.split(' ,').map(v => callback(v)).join(' ,');
871
-
872
790
  function validateConfig(c) {
873
- trace('Validating configuration file'); // Note: checkConfigFile mutates the config file by applying defaults
874
-
791
+ trace('Validating configuration file');
792
+ // Note: checkConfigFile mutates the config file by applying defaults
875
793
  const isValid = checkConfigFile(c);
876
-
877
794
  if (isValid !== true) {
878
795
  throw new ValidationError('InvalidStructure', (Array.isArray(isValid) ? isValid : []).map(v => {
879
796
  if (v.type === 'objectStrict') {
880
- return `Invalid key(s) ${splitMap(v.actual, m => `"${chalk__default['default'].cyan(m)}"`)}. Expected one of ${splitMap(v.expected, chalk__default['default'].green)}`;
797
+ return `Invalid key(s) ${splitMap(v.actual, m => `"${chalk__default["default"].cyan(m)}"`)}. Expected one of ${splitMap(v.expected, chalk__default["default"].green)}`;
881
798
  }
882
-
883
799
  if (v.field) {
884
800
  var _v$message;
885
-
886
- return (_v$message = v.message) === null || _v$message === void 0 ? void 0 : _v$message.replace(v.field, chalk__default['default'].cyan(v.field));
801
+ return (_v$message = v.message) === null || _v$message === void 0 ? void 0 : _v$message.replace(v.field, chalk__default["default"].cyan(v.field));
887
802
  }
888
-
889
803
  return v.message;
890
804
  }).join(' \n'));
891
805
  }
806
+ const languageStrings = c.languages.map(v => v.name);
892
807
 
893
- const languageStrings = c.languages.map(v => v.name); // Dev Language should exist in languages
894
-
808
+ // Dev Language should exist in languages
895
809
  if (!languageStrings.includes(c.devLanguage)) {
896
- throw new ValidationError('InvalidDevLanguage', `The dev language "${chalk__default['default'].bold.cyan(c.devLanguage)}" was not found in languages ${languageStrings.join(', ')}.`);
810
+ throw new ValidationError('InvalidDevLanguage', `The dev language "${chalk__default["default"].bold.cyan(c.devLanguage)}" was not found in languages ${languageStrings.join(', ')}.`);
897
811
  }
898
-
899
812
  const foundLanguages = [];
900
-
901
813
  for (const lang of c.languages) {
902
814
  // Languages must only exist once
903
815
  if (foundLanguages.includes(lang.name)) {
904
- throw new ValidationError('DuplicateLanguage', `The language "${chalk__default['default'].bold.cyan(lang.name)}" was defined multiple times.`);
816
+ throw new ValidationError('DuplicateLanguage', `The language "${chalk__default["default"].bold.cyan(lang.name)}" was defined multiple times.`);
905
817
  }
818
+ foundLanguages.push(lang.name);
906
819
 
907
- foundLanguages.push(lang.name); // Any extends must be in languages
908
-
820
+ // Any extends must be in languages
909
821
  if (lang.extends && !languageStrings.includes(lang.extends)) {
910
- throw new ValidationError('InvalidExtends', `The language "${chalk__default['default'].bold.cyan(lang.name)}"'s extends of ${chalk__default['default'].bold.cyan(lang.extends)} was not found in languages ${languageStrings.join(', ')}.`);
822
+ throw new ValidationError('InvalidExtends', `The language "${chalk__default["default"].bold.cyan(lang.name)}"'s extends of ${chalk__default["default"].bold.cyan(lang.extends)} was not found in languages ${languageStrings.join(', ')}.`);
911
823
  }
912
824
  }
913
-
914
825
  const foundGeneratedLanguages = [];
915
-
916
826
  for (const generatedLang of c.generatedLanguages || []) {
917
827
  // Generated languages must only exist once
918
828
  if (foundGeneratedLanguages.includes(generatedLang.name)) {
919
- throw new ValidationError('DuplicateGeneratedLanguage', `The generated language "${chalk__default['default'].bold.cyan(generatedLang.name)}" was defined multiple times.`);
829
+ throw new ValidationError('DuplicateGeneratedLanguage', `The generated language "${chalk__default["default"].bold.cyan(generatedLang.name)}" was defined multiple times.`);
920
830
  }
831
+ foundGeneratedLanguages.push(generatedLang.name);
921
832
 
922
- foundGeneratedLanguages.push(generatedLang.name); // Generated language names must not conflict with language names
923
-
833
+ // Generated language names must not conflict with language names
924
834
  if (languageStrings.includes(generatedLang.name)) {
925
- throw new ValidationError('InvalidGeneratedLanguage', `The generated language "${chalk__default['default'].bold.cyan(generatedLang.name)}" is already defined as a language.`);
926
- } // Any extends must be in languages
927
-
835
+ throw new ValidationError('InvalidGeneratedLanguage', `The generated language "${chalk__default["default"].bold.cyan(generatedLang.name)}" is already defined as a language.`);
836
+ }
928
837
 
838
+ // Any extends must be in languages
929
839
  if (generatedLang.extends && !languageStrings.includes(generatedLang.extends)) {
930
- throw new ValidationError('InvalidExtends', `The generated language "${chalk__default['default'].bold.cyan(generatedLang.name)}"'s extends of ${chalk__default['default'].bold.cyan(generatedLang.extends)} was not found in languages ${languageStrings.join(', ')}.`);
840
+ throw new ValidationError('InvalidExtends', `The generated language "${chalk__default["default"].bold.cyan(generatedLang.name)}"'s extends of ${chalk__default["default"].bold.cyan(generatedLang.extends)} was not found in languages ${languageStrings.join(', ')}.`);
931
841
  }
932
842
  }
933
-
934
843
  trace('Configuration file is valid');
935
844
  return true;
936
845
  }
937
-
938
846
  function createConfig(configFilePath) {
939
- const cwd = path__default['default'].dirname(configFilePath);
847
+ const cwd = path__default["default"].dirname(configFilePath);
940
848
  return {
941
849
  projectRoot: cwd,
942
850
  ...require(configFilePath)
943
851
  };
944
852
  }
945
-
946
853
  async function resolveConfig(customConfigFilePath) {
947
- const configFilePath = customConfigFilePath ? path__default['default'].resolve(customConfigFilePath) : await findUp__default['default']('vocab.config.js');
948
-
854
+ const configFilePath = customConfigFilePath ? path__default["default"].resolve(customConfigFilePath) : await findUp__default["default"]('vocab.config.js');
949
855
  if (configFilePath) {
950
856
  trace(`Resolved configuration file to ${configFilePath}`);
951
857
  return createConfig(configFilePath);
952
858
  }
953
-
954
859
  trace('No configuration file found');
955
860
  return null;
956
861
  }
957
862
  function resolveConfigSync(customConfigFilePath) {
958
- const configFilePath = customConfigFilePath ? path__default['default'].resolve(customConfigFilePath) : findUp__default['default'].sync('vocab.config.js');
959
-
863
+ const configFilePath = customConfigFilePath ? path__default["default"].resolve(customConfigFilePath) : findUp__default["default"].sync('vocab.config.js');
960
864
  if (configFilePath) {
961
865
  trace(`Resolved configuration file to ${configFilePath}`);
962
866
  return createConfig(configFilePath);
963
867
  }
964
-
965
868
  trace('No configuration file found');
966
869
  return null;
967
870
  }