@discord/eslint-plugin-discord-intl 0.27.2 → 0.27.4

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.
@@ -4,29 +4,38 @@
4
4
  /** @typedef {import('eslint').Rule.ReportDescriptorLocation} ReportDescriptorLocation */
5
5
  /** @typedef {import('eslint').AST.SourceLocation} SourceLocation */
6
6
  /** @typedef {import('eslint').SourceCode} SourceCode */
7
- /** @typedef {import("@discord/intl-loader-core/types").IntlDiagnostic} IntlDiagnostic */
7
+ /** @typedef {import("@discord/intl-message-database").IntlDiagnostic} IntlDiagnostic */
8
8
 
9
9
  const crypto = require('node:crypto');
10
- const {
11
- processDefinitionsFile,
12
- database,
13
- processAllMessagesFiles,
14
- findAllMessagesFiles,
15
- } = require('@discord/intl-loader-core');
10
+ const path = require('node:path');
16
11
  const { traverseMessageDefinitions, isDefinitionsFile } = require('./traverse');
12
+ const {
13
+ IntlMessagesDatabase,
14
+ IntlDatabaseInsertStrategy,
15
+ } = require('@discord/intl-message-database');
17
16
  /** @type {Map<SourceCode, Record<string, IntlDiagnostic[]>>} */
18
17
  const FILE_VALIDATIONS = new Map();
19
18
 
20
- let isInitialized = false;
19
+ /** @type {Map<string, IntlMessagesDatabase>} */
20
+ const PROJECT_DATABASE_MAP = new Map();
21
21
 
22
22
  /**
23
23
  * @param {string} directory
24
+ * @returns {IntlMessagesDatabase}
24
25
  */
25
26
  function ensureInitialized(directory) {
26
- if (isInitialized) return;
27
+ const key = path.resolve(directory);
28
+ const existing = PROJECT_DATABASE_MAP.get(key);
29
+ if (existing != null) return existing;
27
30
 
28
- processAllMessagesFiles(findAllMessagesFiles([directory]));
29
- isInitialized = true;
31
+ const database = new IntlMessagesDatabase();
32
+ database.processAllMessagesFiles(
33
+ database.findAllMessagesFiles([directory], 'en-US'),
34
+ IntlDatabaseInsertStrategy.Create,
35
+ );
36
+
37
+ PROJECT_DATABASE_MAP.set(key, database);
38
+ return database;
30
39
  }
31
40
 
32
41
  /**
@@ -37,12 +46,13 @@ function ensureInitialized(directory) {
37
46
  * validations, allowing them to be reused across multiple rules without wasting work validating
38
47
  * the same messages multiple times.
39
48
  *
49
+ * @param {IntlMessagesDatabase} database
40
50
  * @param {SourceCode} sourceCode
41
51
  * @param {string} fileName
42
52
  * @param {string} content
43
53
  * @return {Record<string, IntlDiagnostic[]>}
44
54
  */
45
- function processAndValidateNative(sourceCode, fileName, content) {
55
+ function processAndValidateNative(database, sourceCode, fileName, content) {
46
56
  const fileKey = crypto.hash('sha1', content);
47
57
  const existing = FILE_VALIDATIONS.get(sourceCode);
48
58
  if (existing != null) return existing;
@@ -51,30 +61,31 @@ function processAndValidateNative(sourceCode, fileName, content) {
51
61
  // native database that this is a JS message definition _somehow_, so we'll make a random
52
62
  // name that should be relatively unique.
53
63
  const processingFileName = fileName === '<input>' ? `${fileKey}.messages.js` : fileName;
54
- const processResult = processDefinitionsFile(processingFileName, content, {
55
- processTranslations: false,
56
- });
64
+ const processResult = database.processDefinitionsFileContent(
65
+ processingFileName,
66
+ content,
67
+ 'en-US',
68
+ IntlDatabaseInsertStrategy.Update,
69
+ );
57
70
 
58
71
  /** @type {Record<string, IntlDiagnostic[]>} */
59
72
  const validations = {};
60
73
 
61
- if (!processResult.succeeded) {
62
- for (const error of processResult.errors) {
63
- const key = error.key ?? 'file';
64
- (validations[key] ??= []).push({
65
- name: 'Processing::' + error.name,
66
- description: error.message,
67
- key: error.key ?? 'file',
68
- file: error.file ?? processingFileName,
69
- messageLine: error.line ?? 0,
70
- messageCol: error.col ?? 0,
71
- start: 0,
72
- end: content.length,
73
- locale: error.locale ?? 'definition',
74
- category: 'correctness',
75
- fixes: [],
76
- });
77
- }
74
+ for (const error of processResult.errors) {
75
+ const key = error.key ?? 'file';
76
+ (validations[key] ??= []).push({
77
+ name: 'Processing::' + error.name,
78
+ description: error.message,
79
+ key: error.key ?? 'file',
80
+ file: error.file ?? processingFileName,
81
+ messageLine: error.line ?? 0,
82
+ messageCol: error.col ?? 0,
83
+ start: 0,
84
+ end: 2,
85
+ locale: error.locale ?? 'definition',
86
+ category: 'correctness',
87
+ fixes: [],
88
+ });
78
89
  }
79
90
 
80
91
  for (const diagnostic of database.validateMessages()) {
@@ -97,8 +108,11 @@ function processAndValidateNative(sourceCode, fileName, content) {
97
108
  */
98
109
  function traverseAndReportMatchingNativeValidations(context, predicate) {
99
110
  if (!isDefinitionsFile(context.filename, context.sourceCode.text)) return {};
100
- ensureInitialized(context.cwd);
111
+
112
+ const projectDirectory = context.settings['intl']?.['projectDirectory'] ?? context.cwd;
113
+ const database = ensureInitialized(projectDirectory);
101
114
  const validations = processAndValidateNative(
115
+ database,
102
116
  context.sourceCode,
103
117
  context.filename,
104
118
  context.sourceCode.text,
@@ -114,6 +128,24 @@ function traverseAndReportMatchingNativeValidations(context, predicate) {
114
128
  column: diagnostic.messageCol,
115
129
  });
116
130
 
131
+ // Sometimes we get a diagnostic pointing to a different file than the
132
+ // current source code. Not totally sure why for now, so lets just noisily
133
+ // log the diagnostic to be able to investigate further when it happens.
134
+ const textLen = context.sourceCode.text.length;
135
+ if (
136
+ diagnostic.file !== context.physicalFilename ||
137
+ messageOffset + diagnostic.start > textLen ||
138
+ messageOffset + diagnostic.end > textLen
139
+ ) {
140
+ console.error('Invalid diagnostic span from intl vs eslint:');
141
+ console.log('ESLint source filename:', context.filename);
142
+ console.log(diagnostic);
143
+ return {
144
+ start: context.sourceCode.getLocFromIndex(1),
145
+ end: context.sourceCode.getLocFromIndex(1),
146
+ };
147
+ }
148
+
117
149
  return {
118
150
  start: context.sourceCode.getLocFromIndex(messageOffset + diagnostic.start),
119
151
  end: context.sourceCode.getLocFromIndex(messageOffset + diagnostic.end),
package/lib/traverse.js CHANGED
@@ -1,4 +1,4 @@
1
- const { isMessageDefinitionsFile } = require('@discord/intl-loader-core');
1
+ const { isMessageDefinitionsFile } = require('@discord/intl-message-database');
2
2
 
3
3
  /** @typedef {import('eslint').Rule.NodeParentExtension} NodeParentExtension */
4
4
  /** @typedef {import('eslint').SourceCode} SourceCode */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@discord/eslint-plugin-discord-intl",
3
- "version": "0.27.2",
3
+ "version": "0.27.4",
4
4
  "license": "MIT",
5
5
  "type": "commonjs",
6
6
  "description": "ESLint plugin for validating and linting messages and translations using @discord/intl",
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@typescript-eslint/scope-manager": "^8.8.1",
14
- "@discord/intl-loader-core": "0.27.2"
14
+ "@discord/intl-message-database": "0.27.4"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/eslint": "^8.56.12",