@discord/intl-loader-core 0.21.0 → 0.21.1-canary.d6da0a2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@discord/intl-loader-core",
3
- "version": "0.21.0",
3
+ "version": "0.21.1-canary.d6da0a2",
4
4
  "license": "MIT",
5
5
  "description": "Core utilities for writing loaders and transformers using @discord/intl",
6
6
  "author": "Jon Egeland",
@@ -28,7 +28,7 @@
28
28
  "dependencies": {
29
29
  "chokidar": "^3.6.0",
30
30
  "debug": "^4.3.6",
31
- "@discord/intl-message-database": "0.21.0"
31
+ "@discord/intl-message-database": "0.21.1-canary.d6da0a2"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/debug": "^4.1.12",
package/src/processing.js CHANGED
@@ -7,6 +7,13 @@ const { database } = require('./database');
7
7
  const { findAllTranslationFiles, getLocaleFromTranslationsFileName } = require('./util');
8
8
 
9
9
  /**
10
+ * @import {
11
+ * IntlMessagesFileDescriptor,
12
+ * IntlSourceFileInsertionData,
13
+ * ProcessDefinitionsResult,
14
+ * ProcessTranslationsResult
15
+ * } from '../types';
16
+ *
10
17
  * @typedef {{
11
18
  * format?: IntlCompiledMessageFormat,
12
19
  * bundleSecrets?: boolean,
@@ -63,7 +70,6 @@ function findAllDefinitionsFilesForTranslations(filePath) {
63
70
  for (const file of sourceFiles) {
64
71
  const source = database.getSourceFile(file);
65
72
  if (source.type === 'definition' && source.meta.translationsPath === expectedTranslationsPath) {
66
- console.log(file, source.meta);
67
73
  relevantPaths.push(source.file);
68
74
  }
69
75
  }
@@ -89,7 +95,7 @@ function findAllMessagesFiles(directories, defaultLocale = 'en-US') {
89
95
  *
90
96
  * @param {string[]} files
91
97
  * @param {string} defaultLocale
92
- * @returns {import('../types').IntlMessagesFileDescriptor[]}
98
+ * @returns {IntlMessagesFileDescriptor[]}
93
99
  */
94
100
  function filterAllMessagesFiles(files, defaultLocale = 'en-US') {
95
101
  return database.filterAllMessagesFiles(files, defaultLocale);
@@ -97,14 +103,15 @@ function filterAllMessagesFiles(files, defaultLocale = 'en-US') {
97
103
 
98
104
  /**
99
105
  * Iterate the given `files`, processing each one's content into the database. Processing is done
100
- * in parallel using native acceleration, and returns the list of all file names that were used.
106
+ * in parallel using native acceleration, and returns an object describing all of the changes that
107
+ * were processed.
101
108
  *
102
109
  * Note that this method _only_ operates by reading file contents from the system, it is not
103
110
  * possible to supply preloaded content through a Buffer in the same way as `processDefinitionsFile`
104
111
  * with a `sourceContent` argument.
105
112
  *
106
- * @param {import('../types').IntlMessagesFileDescriptor[]} files
107
- * @returns {import('../types').IntlMultiProcessingResult}
113
+ * @param {IntlMessagesFileDescriptor[]} files
114
+ * @returns {IntlSourceFileInsertionData[]}
108
115
  */
109
116
  function processAllMessagesFiles(files) {
110
117
  return database.processAllMessagesFiles(files);
@@ -117,7 +124,7 @@ function processAllMessagesFiles(files) {
117
124
  * processTranslations?: boolean,
118
125
  * locale?: string
119
126
  * }=} options
120
- * @returns {import('../types').ProcessDefinitionsResult}
127
+ * @returns {ProcessDefinitionsResult}
121
128
  */
122
129
  function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
123
130
  const {
@@ -127,10 +134,15 @@ function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
127
134
  } = options;
128
135
  debug(`[${sourcePath}] Processing definitions with locale "${locale}"`);
129
136
 
137
+ /** @type {IntlSourceFileInsertionData} */
138
+ let result;
130
139
  if (sourceContent != null) {
131
- database.processDefinitionsFileContent(sourcePath, sourceContent, locale);
140
+ result = database.processDefinitionsFileContent(sourcePath, sourceContent, locale);
132
141
  } else {
133
- database.processDefinitionsFile(sourcePath, locale);
142
+ result = database.processDefinitionsFile(sourcePath, locale);
143
+ }
144
+ if (result.errors.length > 0) {
145
+ return { succeeded: false, errors: result.errors };
134
146
  }
135
147
 
136
148
  const sourceFile = database.getSourceFile(sourcePath);
@@ -151,10 +163,15 @@ function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
151
163
 
152
164
  if (processTranslations) {
153
165
  const translationsResult = database.processAllTranslationFiles(translationsLocaleMap);
166
+ const allErrors = translationsResult.flatMap((result) => result.errors);
167
+ if (allErrors.length > 0) {
168
+ return { succeeded: false, errors: allErrors };
169
+ }
154
170
  debug('[${sourcePath}] Finished processing all translations: %O', translationsResult);
155
171
  }
156
172
 
157
173
  return {
174
+ succeeded: true,
158
175
  sourceFile,
159
176
  locale,
160
177
  messageKeys,
@@ -170,14 +187,20 @@ function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
170
187
  * @param {{
171
188
  * locale?: string,
172
189
  * }=} options
173
- * @returns {import('../types').ProcessTranslationsResult}
190
+ * @returns {ProcessTranslationsResult}
174
191
  */
175
192
  function processTranslationsFile(sourcePath, sourceContent, options = {}) {
176
193
  const { locale = getLocaleFromTranslationsFileName(sourcePath) } = options;
194
+
195
+ /** @type {IntlSourceFileInsertionData} */
196
+ let result;
177
197
  if (sourceContent) {
178
- database.processTranslationFileContent(sourcePath, locale, sourceContent);
198
+ result = database.processTranslationFileContent(sourcePath, locale, sourceContent);
179
199
  } else {
180
- database.processTranslationFile(sourcePath, locale);
200
+ result = database.processTranslationFile(sourcePath, locale);
201
+ }
202
+ if (result.errors.length > 0) {
203
+ return { succeeded: false, errors: result.errors };
181
204
  }
182
205
 
183
206
  const sourceFile = database.getSourceFile(sourcePath);
@@ -189,6 +212,7 @@ function processTranslationsFile(sourcePath, sourceContent, options = {}) {
189
212
  }
190
213
 
191
214
  return {
215
+ succeeded: true,
192
216
  sourceFile,
193
217
  locale,
194
218
  messageKeys: database.getSourceFileKeyMap(sourcePath),
package/src/watcher.js CHANGED
@@ -30,7 +30,7 @@ const DEFAULT_LOCALE = 'en-US';
30
30
 
31
31
  /**
32
32
  * @typedef {{
33
- * on(event: string, callback: (event: string, path: string) => void): void
33
+ * on(event: string, callback: (event: string, path: string) => void): void,
34
34
  * }} BasicWatcher
35
35
  */
36
36
 
@@ -53,7 +53,11 @@ async function watchMessagesFiles(
53
53
  ) {
54
54
  debug('Pre-initializing database with all discoverable messages files');
55
55
  const files = findAllMessagesFiles(watchedFolders);
56
- processAllMessagesFiles(files);
56
+ const result = processAllMessagesFiles(files);
57
+ const reprocessQueue = result
58
+ .filter((data) => data.errors.length > 0)
59
+ .map((data) => data.fileKey);
60
+
57
61
  if (once) {
58
62
  debug('Performing one-shot watch because `once` was true');
59
63
  // This is a weird hack around file watching just not working well on
@@ -80,8 +84,24 @@ async function watchMessagesFiles(
80
84
  ignoreInitial: skipInitial,
81
85
  persistent: !once,
82
86
  });
87
+
83
88
  watcher.on('all', (event, path) => {
84
- processAllMessagesFiles(filterAllMessagesFiles([path]));
89
+ // Process the file that changed first, then reprocess all of the queued files from previous
90
+ // failures to hopefully get them to process successfully again.
91
+ const [result] = processAllMessagesFiles(filterAllMessagesFiles([path]));
92
+ const failed = result.errors.length > 0 ? [result.fileKey] : [];
93
+ const secondResult = processAllMessagesFiles(
94
+ filterAllMessagesFiles([...reprocessQueue, ...failed]),
95
+ );
96
+ // For any files that are still failing, keep them in the queue for next time.
97
+ const secondFailures = secondResult
98
+ .filter((data) => data.errors.length > 0)
99
+ .map((data) => data.fileKey);
100
+ if (secondFailures.length > 0) {
101
+ debug('Failed to re-process files: %O. Next update will try again.', secondFailures);
102
+ }
103
+ reprocessQueue.splice(0, Infinity, ...secondFailures);
104
+
85
105
  debug(`watcher got event: ${event}, for path ${path}`);
86
106
  });
87
107
  for (const signal of ['SIGINT', 'SIGTERM', 'SIGQUIT']) {
@@ -136,16 +156,17 @@ async function precompileMessageDefinitionsFiles(
136
156
  `.compiled.messages.${assetExtension}`,
137
157
  );
138
158
  const result = processDefinitionsFile(filePath);
159
+ if (!result.succeeded) {
160
+ debug('[INTL Error] Failed to compile messages');
161
+ console.error(result.errors);
162
+ return;
163
+ }
164
+
139
165
  precompileFileForLocale(filePath, result.locale, undefined, {
140
166
  format,
141
167
  bundleSecrets,
142
168
  });
143
169
 
144
- database.processDefinitionsFile(filePath);
145
- database.precompile(filePath, DEFAULT_LOCALE, outputPath, {
146
- format: IntlCompiledMessageFormat.KeylessJson,
147
- bundleSecrets,
148
- });
149
170
  debug(`Wrote definitions to: ${outputPath}`);
150
171
  } catch (e) {
151
172
  debug('[INTL Error] Failed to compile messages');
@@ -32,21 +32,22 @@ export function findAllMessagesFiles(directories: string[], defaultLocale?: stri
32
32
  *
33
33
  * @param {string[]} files
34
34
  * @param {string} defaultLocale
35
- * @returns {import('../types').IntlMessagesFileDescriptor[]}
35
+ * @returns {IntlMessagesFileDescriptor[]}
36
36
  */
37
- export function filterAllMessagesFiles(files: string[], defaultLocale?: string): import("../types").IntlMessagesFileDescriptor[];
37
+ export function filterAllMessagesFiles(files: string[], defaultLocale?: string): IntlMessagesFileDescriptor[];
38
38
  /**
39
39
  * Iterate the given `files`, processing each one's content into the database. Processing is done
40
- * in parallel using native acceleration, and returns the list of all file names that were used.
40
+ * in parallel using native acceleration, and returns an object describing all of the changes that
41
+ * were processed.
41
42
  *
42
43
  * Note that this method _only_ operates by reading file contents from the system, it is not
43
44
  * possible to supply preloaded content through a Buffer in the same way as `processDefinitionsFile`
44
45
  * with a `sourceContent` argument.
45
46
  *
46
- * @param {import('../types').IntlMessagesFileDescriptor[]} files
47
- * @returns {import('../types').IntlMultiProcessingResult}
47
+ * @param {IntlMessagesFileDescriptor[]} files
48
+ * @returns {IntlSourceFileInsertionData[]}
48
49
  */
49
- export function processAllMessagesFiles(files: import("../types").IntlMessagesFileDescriptor[]): import("../types").IntlMultiProcessingResult;
50
+ export function processAllMessagesFiles(files: IntlMessagesFileDescriptor[]): IntlSourceFileInsertionData[];
50
51
  /**
51
52
  * Generate a `.d.ts` file containing TypeScript type definitions for all of the messages defined in
52
53
  * `sourcePath`. This method does not process `sourcePath` at all, meaning it expects the database
@@ -93,12 +94,12 @@ export function precompileFileForLocale(sourcePath: string, locale: string, outp
93
94
  * processTranslations?: boolean,
94
95
  * locale?: string
95
96
  * }=} options
96
- * @returns {import('../types').ProcessDefinitionsResult}
97
+ * @returns {ProcessDefinitionsResult}
97
98
  */
98
99
  export function processDefinitionsFile(sourcePath: string, sourceContent?: string | undefined, options?: {
99
100
  processTranslations?: boolean;
100
101
  locale?: string;
101
- } | undefined): import("../types").ProcessDefinitionsResult;
102
+ } | undefined): ProcessDefinitionsResult;
102
103
  /**
103
104
  *
104
105
  * @param {string} sourcePath
@@ -106,9 +107,13 @@ export function processDefinitionsFile(sourcePath: string, sourceContent?: strin
106
107
  * @param {{
107
108
  * locale?: string,
108
109
  * }=} options
109
- * @returns {import('../types').ProcessTranslationsResult}
110
+ * @returns {ProcessTranslationsResult}
110
111
  */
111
112
  export function processTranslationsFile(sourcePath: string, sourceContent?: string | undefined, options?: {
112
113
  locale?: string;
113
- } | undefined): import("../types").ProcessTranslationsResult;
114
+ } | undefined): ProcessTranslationsResult;
114
115
  import { IntlCompiledMessageFormat } from "@discord/intl-message-database";
116
+ import type { IntlMessagesFileDescriptor } from '../types';
117
+ import type { IntlSourceFileInsertionData } from '../types';
118
+ import type { ProcessDefinitionsResult } from '../types';
119
+ import type { ProcessTranslationsResult } from '../types';
@@ -3,7 +3,7 @@ export type BasicWatcher = {
3
3
  };
4
4
  /**
5
5
  * @typedef {{
6
- * on(event: string, callback: (event: string, path: string) => void): void
6
+ * on(event: string, callback: (event: string, path: string) => void): void,
7
7
  * }} BasicWatcher
8
8
  */
9
9
  /**
package/types/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { IntlSourceFile } from '@discord/intl-message-database';
1
+ import { IntlSourceFile, IntlSourceFileError } from '@discord/intl-message-database';
2
2
 
3
3
  export type * from '@discord/intl-message-database';
4
4
 
@@ -72,11 +72,17 @@ export interface MessageDefinitionsTransformerOptions {
72
72
  bindMode?: 'proxy' | 'literal';
73
73
  }
74
74
 
75
- /**
76
- * The result of calling `processDefinitionsFile`, including the created source file, locale map,
77
- * and more.
78
- */
79
- export interface ProcessDefinitionsResult {
75
+ export interface IntlProcessingError {
76
+ succeeded: false;
77
+ errors: IntlSourceFileError[];
78
+ }
79
+
80
+ export interface ProcessDefinitionsSuccess {
81
+ /**
82
+ * Whether the processing was successful as a whole. If any part of the process contained errors,
83
+ * including processing translations alongside the definitions file, this will be set to `false`.
84
+ */
85
+ succeeded: true;
80
86
  /**
81
87
  * Direct source file from the database that was created or updated by this process.
82
88
  */
@@ -108,10 +114,13 @@ export interface ProcessDefinitionsResult {
108
114
  }
109
115
 
110
116
  /**
111
- * The result of calling `processTranslationsFile`, including the created source file, locale map,
117
+ * The result of calling `processDefinitionsFile`, including the created source file, locale map,
112
118
  * and more.
113
119
  */
114
- export interface ProcessTranslationsResult {
120
+ export type ProcessDefinitionsResult = ProcessDefinitionsSuccess | IntlProcessingError;
121
+
122
+ export interface ProcessTranslationsSuccess {
123
+ succeeded: true;
115
124
  /**
116
125
  * Direct source file from the database that was created or updated by this process.
117
126
  */
@@ -128,3 +137,9 @@ export interface ProcessTranslationsResult {
128
137
  */
129
138
  locale: string;
130
139
  }
140
+
141
+ /**
142
+ * The result of calling `processTranslationsFile`, including the created source file, locale map,
143
+ * and more.
144
+ */
145
+ export type ProcessTranslationsResult = ProcessTranslationsSuccess | IntlProcessingError;
package/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { IntlSourceFile } from '@discord/intl-message-database';
1
+ import { IntlSourceFile, IntlSourceFileError } from '@discord/intl-message-database';
2
2
 
3
3
  export type * from '@discord/intl-message-database';
4
4
 
@@ -72,11 +72,17 @@ export interface MessageDefinitionsTransformerOptions {
72
72
  bindMode?: 'proxy' | 'literal';
73
73
  }
74
74
 
75
- /**
76
- * The result of calling `processDefinitionsFile`, including the created source file, locale map,
77
- * and more.
78
- */
79
- export interface ProcessDefinitionsResult {
75
+ export interface IntlProcessingError {
76
+ succeeded: false;
77
+ errors: IntlSourceFileError[];
78
+ }
79
+
80
+ export interface ProcessDefinitionsSuccess {
81
+ /**
82
+ * Whether the processing was successful as a whole. If any part of the process contained errors,
83
+ * including processing translations alongside the definitions file, this will be set to `false`.
84
+ */
85
+ succeeded: true;
80
86
  /**
81
87
  * Direct source file from the database that was created or updated by this process.
82
88
  */
@@ -108,10 +114,13 @@ export interface ProcessDefinitionsResult {
108
114
  }
109
115
 
110
116
  /**
111
- * The result of calling `processTranslationsFile`, including the created source file, locale map,
117
+ * The result of calling `processDefinitionsFile`, including the created source file, locale map,
112
118
  * and more.
113
119
  */
114
- export interface ProcessTranslationsResult {
120
+ export type ProcessDefinitionsResult = ProcessDefinitionsSuccess | IntlProcessingError;
121
+
122
+ export interface ProcessTranslationsSuccess {
123
+ succeeded: true;
115
124
  /**
116
125
  * Direct source file from the database that was created or updated by this process.
117
126
  */
@@ -128,3 +137,9 @@ export interface ProcessTranslationsResult {
128
137
  */
129
138
  locale: string;
130
139
  }
140
+
141
+ /**
142
+ * The result of calling `processTranslationsFile`, including the created source file, locale map,
143
+ * and more.
144
+ */
145
+ export type ProcessTranslationsResult = ProcessTranslationsSuccess | IntlProcessingError;