@discord/intl-loader-core 0.20.1 → 0.21.1-canary.ad1c466

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.20.1",
3
+ "version": "0.21.1-canary.ad1c466",
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.20.1"
31
+ "@discord/intl-message-database": "0.21.1-canary.ad1c466"
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,
@@ -89,7 +96,7 @@ function findAllMessagesFiles(directories, defaultLocale = 'en-US') {
89
96
  *
90
97
  * @param {string[]} files
91
98
  * @param {string} defaultLocale
92
- * @returns {import('../types').IntlMessagesFileDescriptor[]}
99
+ * @returns {IntlMessagesFileDescriptor[]}
93
100
  */
94
101
  function filterAllMessagesFiles(files, defaultLocale = 'en-US') {
95
102
  return database.filterAllMessagesFiles(files, defaultLocale);
@@ -97,14 +104,15 @@ function filterAllMessagesFiles(files, defaultLocale = 'en-US') {
97
104
 
98
105
  /**
99
106
  * 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.
107
+ * in parallel using native acceleration, and returns an object describing all of the changes that
108
+ * were processed.
101
109
  *
102
110
  * Note that this method _only_ operates by reading file contents from the system, it is not
103
111
  * possible to supply preloaded content through a Buffer in the same way as `processDefinitionsFile`
104
112
  * with a `sourceContent` argument.
105
113
  *
106
- * @param {import('../types').IntlMessagesFileDescriptor[]} files
107
- * @returns {import('../types').IntlMultiProcessingResult}
114
+ * @param {IntlMessagesFileDescriptor[]} files
115
+ * @returns {IntlSourceFileInsertionData[]}
108
116
  */
109
117
  function processAllMessagesFiles(files) {
110
118
  return database.processAllMessagesFiles(files);
@@ -117,7 +125,7 @@ function processAllMessagesFiles(files) {
117
125
  * processTranslations?: boolean,
118
126
  * locale?: string
119
127
  * }=} options
120
- * @returns {import('../types').ProcessDefinitionsResult}
128
+ * @returns {ProcessDefinitionsResult}
121
129
  */
122
130
  function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
123
131
  const {
@@ -127,10 +135,15 @@ function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
127
135
  } = options;
128
136
  debug(`[${sourcePath}] Processing definitions with locale "${locale}"`);
129
137
 
138
+ /** @type {IntlSourceFileInsertionData} */
139
+ let result;
130
140
  if (sourceContent != null) {
131
- database.processDefinitionsFileContent(sourcePath, sourceContent, locale);
141
+ result = database.processDefinitionsFileContent(sourcePath, sourceContent, locale);
132
142
  } else {
133
- database.processDefinitionsFile(sourcePath, locale);
143
+ result = database.processDefinitionsFile(sourcePath, locale);
144
+ }
145
+ if (result.errors.length > 0) {
146
+ return { succeeded: false, errors: result.errors };
134
147
  }
135
148
 
136
149
  const sourceFile = database.getSourceFile(sourcePath);
@@ -151,10 +164,15 @@ function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
151
164
 
152
165
  if (processTranslations) {
153
166
  const translationsResult = database.processAllTranslationFiles(translationsLocaleMap);
167
+ const allErrors = translationsResult.flatMap((result) => result.errors);
168
+ if (allErrors.length > 0) {
169
+ return { succeeded: false, errors: allErrors };
170
+ }
154
171
  debug('[${sourcePath}] Finished processing all translations: %O', translationsResult);
155
172
  }
156
173
 
157
174
  return {
175
+ succeeded: true,
158
176
  sourceFile,
159
177
  locale,
160
178
  messageKeys,
@@ -170,14 +188,20 @@ function processDefinitionsFile(sourcePath, sourceContent, options = {}) {
170
188
  * @param {{
171
189
  * locale?: string,
172
190
  * }=} options
173
- * @returns {import('../types').ProcessTranslationsResult}
191
+ * @returns {ProcessTranslationsResult}
174
192
  */
175
193
  function processTranslationsFile(sourcePath, sourceContent, options = {}) {
176
194
  const { locale = getLocaleFromTranslationsFileName(sourcePath) } = options;
195
+
196
+ /** @type {IntlSourceFileInsertionData} */
197
+ let result;
177
198
  if (sourceContent) {
178
- database.processTranslationFileContent(sourcePath, locale, sourceContent);
199
+ result = database.processTranslationFileContent(sourcePath, locale, sourceContent);
179
200
  } else {
180
- database.processTranslationFile(sourcePath, locale);
201
+ result = database.processTranslationFile(sourcePath, locale);
202
+ }
203
+ if (result.errors.length > 0) {
204
+ return { succeeded: false, errors: result.errors };
181
205
  }
182
206
 
183
207
  const sourceFile = database.getSourceFile(sourcePath);
@@ -189,6 +213,7 @@ function processTranslationsFile(sourcePath, sourceContent, options = {}) {
189
213
  }
190
214
 
191
215
  return {
216
+ succeeded: true,
192
217
  sourceFile,
193
218
  locale,
194
219
  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;