@discord/intl-loader-core 0.21.1 → 0.22.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/processing.js +35 -11
- package/src/watcher.js +36 -12
- package/types/src/processing.d.ts +15 -10
- package/types/src/watcher.d.ts +1 -1
- package/types/types.d.ts +23 -8
- package/types.d.ts +23 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@discord/intl-loader-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0-rc.0",
|
|
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.
|
|
31
|
+
"@discord/intl-message-database": "0.22.0-rc.0"
|
|
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 {
|
|
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
|
|
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 {
|
|
107
|
-
* @returns {
|
|
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 {
|
|
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 {
|
|
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
|
|
|
@@ -49,11 +49,15 @@ const DEFAULT_LOCALE = 'en-US';
|
|
|
49
49
|
*/
|
|
50
50
|
async function watchMessagesFiles(
|
|
51
51
|
watchedFolders,
|
|
52
|
-
{ ignore = [], skipInitial = false, once = true } = {}
|
|
52
|
+
{ ignore = [], skipInitial = false, once = true } = {}
|
|
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
|
|
@@ -71,7 +75,7 @@ async function watchMessagesFiles(
|
|
|
71
75
|
|
|
72
76
|
const ignoredPatterns = ignore.concat(ALWAYS_IGNORE_PATTERNS);
|
|
73
77
|
const globs = watchedFolders.flatMap((folder) =>
|
|
74
|
-
MESSAGES_FILE_PATTERNS.map((pattern) => path.join(folder, pattern))
|
|
78
|
+
MESSAGES_FILE_PATTERNS.map((pattern) => path.join(folder, pattern))
|
|
75
79
|
);
|
|
76
80
|
|
|
77
81
|
debug(`Initializing watch for patterns:\n- ${globs.join('\n- ')}`);
|
|
@@ -80,8 +84,27 @@ async function watchMessagesFiles(
|
|
|
80
84
|
ignoreInitial: skipInitial,
|
|
81
85
|
persistent: !once,
|
|
82
86
|
});
|
|
87
|
+
|
|
83
88
|
watcher.on('all', (event, path) => {
|
|
84
|
-
|
|
89
|
+
const files = filterAllMessagesFiles([path]);
|
|
90
|
+
if (files.length === 0) return;
|
|
91
|
+
|
|
92
|
+
// Process the file that changed first, then reprocess all of the queued files from previous
|
|
93
|
+
// failures to hopefully get them to process successfully again.
|
|
94
|
+
const [result] = processAllMessagesFiles(files);
|
|
95
|
+
const failed = result.errors.length > 0 ? [result.fileKey] : [];
|
|
96
|
+
const secondResult = processAllMessagesFiles(
|
|
97
|
+
filterAllMessagesFiles([...reprocessQueue, ...failed])
|
|
98
|
+
);
|
|
99
|
+
// For any files that are still failing, keep them in the queue for next time.
|
|
100
|
+
const secondFailures = secondResult
|
|
101
|
+
.filter((data) => data.errors.length > 0)
|
|
102
|
+
.map((data) => data.fileKey);
|
|
103
|
+
if (secondFailures.length > 0) {
|
|
104
|
+
debug('Failed to re-process files: %O. Next update will try again.', secondFailures);
|
|
105
|
+
}
|
|
106
|
+
reprocessQueue.splice(0, Infinity, ...secondFailures);
|
|
107
|
+
|
|
85
108
|
debug(`watcher got event: ${event}, for path ${path}`);
|
|
86
109
|
});
|
|
87
110
|
for (const signal of ['SIGINT', 'SIGTERM', 'SIGQUIT']) {
|
|
@@ -121,7 +144,7 @@ async function generateMessageTypes(watcher) {
|
|
|
121
144
|
*/
|
|
122
145
|
async function precompileMessageDefinitionsFiles(
|
|
123
146
|
watcher,
|
|
124
|
-
{ assetExtension = 'json', precompileOptions = {} } = {}
|
|
147
|
+
{ assetExtension = 'json', precompileOptions = {} } = {}
|
|
125
148
|
) {
|
|
126
149
|
watcher.on('all', (_, filePath) => {
|
|
127
150
|
const { format = IntlCompiledMessageFormat.KeylessJson, bundleSecrets = false } =
|
|
@@ -133,19 +156,20 @@ async function precompileMessageDefinitionsFiles(
|
|
|
133
156
|
// Convert the file name from `.messages.js` to `.compiled.messages.jsona` for output.
|
|
134
157
|
const outputPath = filePath.replace(
|
|
135
158
|
/\.messages\.js$/,
|
|
136
|
-
`.compiled.messages.${assetExtension}
|
|
159
|
+
`.compiled.messages.${assetExtension}`
|
|
137
160
|
);
|
|
138
161
|
const result = processDefinitionsFile(filePath);
|
|
162
|
+
if (!result.succeeded) {
|
|
163
|
+
debug('[INTL Error] Failed to compile messages');
|
|
164
|
+
console.error(result.errors);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
139
168
|
precompileFileForLocale(filePath, result.locale, undefined, {
|
|
140
169
|
format,
|
|
141
170
|
bundleSecrets,
|
|
142
171
|
});
|
|
143
172
|
|
|
144
|
-
database.processDefinitionsFile(filePath);
|
|
145
|
-
database.precompile(filePath, DEFAULT_LOCALE, outputPath, {
|
|
146
|
-
format: IntlCompiledMessageFormat.KeylessJson,
|
|
147
|
-
bundleSecrets,
|
|
148
|
-
});
|
|
149
173
|
debug(`Wrote definitions to: ${outputPath}`);
|
|
150
174
|
} catch (e) {
|
|
151
175
|
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 {
|
|
35
|
+
* @returns {IntlMessagesFileDescriptor[]}
|
|
36
36
|
*/
|
|
37
|
-
export function filterAllMessagesFiles(files: string[], defaultLocale?: string):
|
|
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
|
|
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 {
|
|
47
|
-
* @returns {
|
|
47
|
+
* @param {IntlMessagesFileDescriptor[]} files
|
|
48
|
+
* @returns {IntlSourceFileInsertionData[]}
|
|
48
49
|
*/
|
|
49
|
-
export function processAllMessagesFiles(files:
|
|
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 {
|
|
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):
|
|
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 {
|
|
110
|
+
* @returns {ProcessTranslationsResult}
|
|
110
111
|
*/
|
|
111
112
|
export function processTranslationsFile(sourcePath: string, sourceContent?: string | undefined, options?: {
|
|
112
113
|
locale?: string;
|
|
113
|
-
} | undefined):
|
|
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';
|
package/types/src/watcher.d.ts
CHANGED
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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 `
|
|
117
|
+
* The result of calling `processDefinitionsFile`, including the created source file, locale map,
|
|
112
118
|
* and more.
|
|
113
119
|
*/
|
|
114
|
-
export
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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 `
|
|
117
|
+
* The result of calling `processDefinitionsFile`, including the created source file, locale map,
|
|
112
118
|
* and more.
|
|
113
119
|
*/
|
|
114
|
-
export
|
|
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;
|