next-intl 4.5.8 → 4.6.1

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.
Files changed (88) hide show
  1. package/dist/cjs/development/ExtractorCodec-D9Tw618d.cjs +7 -0
  2. package/dist/cjs/development/JSONCodec-L1_VeQBi.cjs +48 -0
  3. package/dist/cjs/development/POCodec-Be_UL6jy.cjs +105 -0
  4. package/dist/cjs/development/plugin-DDtWCyPI.cjs +1373 -0
  5. package/dist/cjs/development/plugin.cjs +8 -379
  6. package/dist/esm/development/extractor/ExtractionCompiler.js +23 -26
  7. package/dist/esm/development/extractor/catalog/CatalogLocales.js +0 -33
  8. package/dist/esm/development/extractor/catalog/CatalogManager.js +171 -110
  9. package/dist/esm/development/extractor/catalog/CatalogPersister.js +31 -13
  10. package/dist/esm/development/extractor/catalog/SaveScheduler.js +1 -1
  11. package/dist/esm/development/extractor/catalogLoader.js +10 -10
  12. package/dist/esm/development/extractor/extractMessages.js +9 -2
  13. package/dist/esm/development/extractor/extractionLoader.js +15 -12
  14. package/dist/esm/development/extractor/extractor/MessageExtractor.js +5 -4
  15. package/dist/esm/development/extractor/format/ExtractorCodec.js +5 -0
  16. package/dist/esm/development/extractor/format/codecs/JSONCodec.js +40 -0
  17. package/dist/esm/development/extractor/format/codecs/POCodec.js +93 -0
  18. package/dist/esm/development/extractor/format/index.js +44 -0
  19. package/dist/esm/development/extractor/source/SourceFileScanner.js +2 -1
  20. package/dist/esm/development/extractor/source/SourceFileWatcher.js +132 -0
  21. package/dist/esm/development/extractor/utils.js +16 -1
  22. package/dist/esm/development/extractor.js +1 -0
  23. package/dist/esm/development/plugin/createNextIntlPlugin.js +3 -1
  24. package/dist/esm/development/plugin/declaration/createMessagesDeclaration.js +2 -11
  25. package/dist/esm/development/plugin/extractor/initExtractionCompiler.js +45 -0
  26. package/dist/esm/development/plugin/getNextConfig.js +7 -4
  27. package/dist/esm/development/plugin/utils.js +16 -1
  28. package/dist/esm/production/extractor/ExtractionCompiler.js +1 -1
  29. package/dist/esm/production/extractor/catalog/CatalogLocales.js +1 -1
  30. package/dist/esm/production/extractor/catalog/CatalogManager.js +1 -1
  31. package/dist/esm/production/extractor/catalog/CatalogPersister.js +1 -1
  32. package/dist/esm/production/extractor/catalog/SaveScheduler.js +1 -1
  33. package/dist/esm/production/extractor/catalogLoader.js +1 -1
  34. package/dist/esm/production/extractor/extractMessages.js +1 -1
  35. package/dist/esm/production/extractor/extractionLoader.js +1 -1
  36. package/dist/esm/production/extractor/extractor/MessageExtractor.js +1 -1
  37. package/dist/esm/production/extractor/format/ExtractorCodec.js +1 -0
  38. package/dist/esm/production/extractor/format/codecs/JSONCodec.js +1 -0
  39. package/dist/esm/production/extractor/format/codecs/POCodec.js +1 -0
  40. package/dist/esm/production/extractor/format/index.js +1 -0
  41. package/dist/esm/production/extractor/source/SourceFileScanner.js +1 -1
  42. package/dist/esm/production/extractor/source/SourceFileWatcher.js +1 -0
  43. package/dist/esm/production/extractor/utils.js +1 -1
  44. package/dist/esm/production/extractor.js +1 -1
  45. package/dist/esm/production/plugin/createNextIntlPlugin.js +1 -1
  46. package/dist/esm/production/plugin/declaration/createMessagesDeclaration.js +1 -1
  47. package/dist/esm/production/plugin/extractor/initExtractionCompiler.js +1 -0
  48. package/dist/esm/production/plugin/getNextConfig.js +1 -1
  49. package/dist/esm/production/plugin/utils.js +1 -1
  50. package/dist/types/extractor/ExtractionCompiler.d.ts +5 -10
  51. package/dist/types/extractor/catalog/CatalogLocales.d.ts +0 -2
  52. package/dist/types/extractor/catalog/CatalogManager.d.ts +26 -15
  53. package/dist/types/extractor/catalog/CatalogPersister.d.ts +15 -6
  54. package/dist/types/extractor/catalog/SaveScheduler.d.ts +2 -2
  55. package/dist/types/extractor/extractor/MessageExtractor.d.ts +6 -6
  56. package/dist/types/extractor/format/ExtractorCodec.d.ts +33 -0
  57. package/dist/types/extractor/format/codecs/JSONCodec.d.ts +2 -0
  58. package/dist/types/extractor/format/codecs/POCodec.d.ts +2 -0
  59. package/dist/types/extractor/format/codecs/fixtures/JSONCodecStructured.d.ts +2 -0
  60. package/dist/types/extractor/format/codecs/fixtures/POCodecSourceMessageKey.d.ts +2 -0
  61. package/dist/types/extractor/format/index.d.ts +15 -0
  62. package/dist/types/extractor/format/types.d.ts +8 -0
  63. package/dist/types/extractor/index.d.ts +1 -0
  64. package/dist/types/extractor/source/SourceFileFilter.d.ts +2 -2
  65. package/dist/types/extractor/source/SourceFileScanner.d.ts +1 -1
  66. package/dist/types/extractor/source/SourceFileWatcher.d.ts +15 -0
  67. package/dist/types/extractor/types.d.ts +2 -2
  68. package/dist/types/extractor/utils.d.ts +3 -0
  69. package/dist/types/plugin/extractor/initExtractionCompiler.d.ts +2 -0
  70. package/dist/types/plugin/types.d.ts +1 -1
  71. package/dist/types/plugin/utils.d.ts +6 -0
  72. package/package.json +6 -5
  73. package/dist/esm/development/extractor/formatters/Formatter.js +0 -3
  74. package/dist/esm/development/extractor/formatters/JSONFormatter.js +0 -42
  75. package/dist/esm/development/extractor/formatters/POFormatter.js +0 -51
  76. package/dist/esm/development/extractor/formatters/index.js +0 -6
  77. package/dist/esm/development/extractor/formatters/utils.js +0 -15
  78. package/dist/esm/production/extractor/formatters/Formatter.js +0 -1
  79. package/dist/esm/production/extractor/formatters/JSONFormatter.js +0 -1
  80. package/dist/esm/production/extractor/formatters/POFormatter.js +0 -1
  81. package/dist/esm/production/extractor/formatters/index.js +0 -1
  82. package/dist/esm/production/extractor/formatters/utils.js +0 -1
  83. package/dist/types/extractor/extractor/ASTScope.d.ts +0 -12
  84. package/dist/types/extractor/formatters/Formatter.d.ts +0 -10
  85. package/dist/types/extractor/formatters/JSONFormatter.d.ts +0 -10
  86. package/dist/types/extractor/formatters/POFormatter.d.ts +0 -10
  87. package/dist/types/extractor/formatters/index.d.ts +0 -5
  88. package/dist/types/extractor/formatters/utils.d.ts +0 -2
@@ -1,384 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var fs = require('fs');
4
- var path = require('path');
5
- var module$1 = require('module');
3
+ var plugin = require('./plugin-DDtWCyPI.cjs');
4
+ require('fs/promises');
5
+ require('path');
6
+ require('@parcel/watcher');
7
+ require('fs');
8
+ require('module');
9
+ require('@swc/core');
6
10
 
7
- var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
8
- function formatMessage(message) {
9
- return `\n[next-intl] ${message}\n`;
10
- }
11
- function throwError(message) {
12
- throw new Error(formatMessage(message));
13
- }
14
- function warn(message) {
15
- console.warn(formatMessage(message));
16
- }
17
11
 
18
- /**
19
- * Wrapper around `fs.watch` that provides a workaround
20
- * for https://github.com/nodejs/node/issues/5039.
21
- */
22
- function watchFile(filepath, callback) {
23
- const directory = path.dirname(filepath);
24
- const filename = path.basename(filepath);
25
- return fs.watch(directory, {
26
- persistent: false,
27
- recursive: false
28
- }, (event, changedFilename) => {
29
- if (changedFilename === filename) {
30
- callback();
31
- }
32
- });
33
- }
34
12
 
35
- function runOnce(fn) {
36
- if (process.env._NEXT_INTL_COMPILE_MESSAGES === '1') {
37
- return;
38
- }
39
- process.env._NEXT_INTL_COMPILE_MESSAGES = '1';
40
- fn();
41
- }
42
- function createMessagesDeclaration(messagesPaths) {
43
- // Instead of running _only_ in certain cases, it's
44
- // safer to _avoid_ running for certain known cases.
45
- // https://github.com/amannn/next-intl/issues/2006
46
- const shouldBailOut = ['info', 'start'
47
-
48
- // Note: These commands don't consult the
49
- // Next.js config, so we can't detect them here.
50
- // - telemetry
51
- // - lint
52
- //
53
- // What remains are:
54
- // - dev
55
- // - build
56
- // - typegen
57
- ].some(arg => process.argv.includes(arg));
58
- if (shouldBailOut) {
59
- return;
60
- }
61
-
62
- // Next.js can call the Next.js config multiple
63
- // times - ensure we only run once.
64
- runOnce(() => {
65
- for (const messagesPath of messagesPaths) {
66
- const fullPath = path.resolve(messagesPath);
67
- if (!fs.existsSync(fullPath)) {
68
- throwError(`\`createMessagesDeclaration\` points to a non-existent file: ${fullPath}`);
69
- }
70
- if (!fullPath.endsWith('.json')) {
71
- throwError(`\`createMessagesDeclaration\` needs to point to a JSON file. Received: ${fullPath}`);
72
- }
73
-
74
- // Keep this as a runtime check and don't replace
75
- // this with a constant during the build process
76
- const env = process.env['NODE_ENV'.trim()];
77
- compileDeclaration(messagesPath);
78
- if (env === 'development') {
79
- startWatching(messagesPath);
80
- }
81
- }
82
- });
83
- }
84
- function startWatching(messagesPath) {
85
- const watcher = watchFile(messagesPath, () => {
86
- compileDeclaration(messagesPath, true);
87
- });
88
- process.on('exit', () => {
89
- watcher.close();
90
- });
91
- }
92
- function compileDeclaration(messagesPath, async = false) {
93
- const declarationPath = messagesPath.replace(/\.json$/, '.d.json.ts');
94
- function createDeclaration(content) {
95
- return `// This file is auto-generated by next-intl, do not edit directly.
96
- // See: https://next-intl.dev/docs/workflows/typescript#messages-arguments
97
-
98
- declare const messages: ${content.trim()};
99
- export default messages;`;
100
- }
101
- if (async) {
102
- return fs.promises.readFile(messagesPath, 'utf-8').then(content => fs.promises.writeFile(declarationPath, createDeclaration(content)));
103
- }
104
- const content = fs.readFileSync(messagesPath, 'utf-8');
105
- fs.writeFileSync(declarationPath, createDeclaration(content));
106
- }
107
-
108
- class SourceFileFilter {
109
- static EXTENSIONS = ['ts', 'tsx', 'js', 'jsx'];
110
-
111
- // Will not be entered, except if explicitly asked for
112
- // TODO: At some point we should infer these from .gitignore
113
- static IGNORED_DIRECTORIES = ['node_modules', '.next', '.git'];
114
- static isSourceFile(filePath) {
115
- const ext = path.extname(filePath);
116
- return SourceFileFilter.EXTENSIONS.map(cur => '.' + cur).includes(ext);
117
- }
118
- static shouldEnterDirectory(dirPath, srcPaths) {
119
- const dirName = path.basename(dirPath);
120
- if (SourceFileFilter.IGNORED_DIRECTORIES.includes(dirName)) {
121
- return SourceFileFilter.isIgnoredDirectoryExplicitlyIncluded(dirPath, srcPaths);
122
- }
123
- return true;
124
- }
125
- static isIgnoredDirectoryExplicitlyIncluded(ignoredDirPath, srcPaths) {
126
- return srcPaths.some(srcPath => SourceFileFilter.isWithinPath(srcPath, ignoredDirPath));
127
- }
128
- static isWithinPath(targetPath, basePath) {
129
- const relativePath = path.relative(basePath, targetPath);
130
- return relativePath === '' || !relativePath.startsWith('..');
131
- }
132
- }
133
-
134
- function getCurrentVersion() {
135
- try {
136
- const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('plugin.cjs', document.baseURI).href)));
137
- const pkg = require$1('next/package.json');
138
- return pkg.version;
139
- } catch (error) {
140
- throw new Error('Failed to get current Next.js version. This can happen if next-intl/plugin is imported into your app code outside of your next.config.js.', {
141
- cause: error
142
- });
143
- }
144
- }
145
- function compareVersions(version1, version2) {
146
- const v1Parts = version1.split('.').map(Number);
147
- const v2Parts = version2.split('.').map(Number);
148
- for (let i = 0; i < 3; i++) {
149
- const v1 = v1Parts[i] || 0;
150
- const v2 = v2Parts[i] || 0;
151
- if (v1 > v2) return 1;
152
- if (v1 < v2) return -1;
153
- }
154
- return 0;
155
- }
156
- function hasStableTurboConfig() {
157
- return compareVersions(getCurrentVersion(), '15.3.0') >= 0;
158
- }
159
- function isNextJs16OrHigher() {
160
- return compareVersions(getCurrentVersion(), '16.0.0') >= 0;
161
- }
162
-
163
- function withExtensions(localPath) {
164
- return [`${localPath}.ts`, `${localPath}.tsx`, `${localPath}.js`, `${localPath}.jsx`];
165
- }
166
- function resolveI18nPath(providedPath, cwd) {
167
- function resolvePath(pathname) {
168
- const parts = [];
169
- if (cwd) parts.push(cwd);
170
- parts.push(pathname);
171
- return path.resolve(...parts);
172
- }
173
- function pathExists(pathname) {
174
- return fs.existsSync(resolvePath(pathname));
175
- }
176
- if (providedPath) {
177
- if (!pathExists(providedPath)) {
178
- throwError(`Could not find i18n config at ${providedPath}, please provide a valid path.`);
179
- }
180
- return providedPath;
181
- } else {
182
- for (const candidate of [...withExtensions('./i18n/request'), ...withExtensions('./src/i18n/request')]) {
183
- if (pathExists(candidate)) {
184
- return candidate;
185
- }
186
- }
187
- throwError(`Could not locate request configuration module.\n\nThis path is supported by default: ./(src/)i18n/request.{js,jsx,ts,tsx}\n\nAlternatively, you can specify a custom location in your Next.js config:\n\nconst withNextIntl = createNextIntlPlugin(
188
-
189
- Alternatively, you can specify a custom location in your Next.js config:
190
-
191
- const withNextIntl = createNextIntlPlugin(
192
- './path/to/i18n/request.tsx'
193
- );`);
194
- }
195
- }
196
- function getNextConfig(pluginConfig, nextConfig) {
197
- const useTurbo = process.env.TURBOPACK != null;
198
- const nextIntlConfig = {};
199
- function getExtractMessagesLoaderConfig() {
200
- const experimental = pluginConfig.experimental;
201
- if (!experimental.srcPath || !experimental.messages) {
202
- throwError('`srcPath` and `messages` are required when using `extractor`.');
203
- }
204
- return {
205
- loader: 'next-intl/extractor/extractionLoader',
206
- options: {
207
- srcPath: experimental.srcPath,
208
- sourceLocale: experimental.extract.sourceLocale,
209
- messages: experimental.messages
210
- }
211
- };
212
- }
213
- function getCatalogLoaderConfig() {
214
- return {
215
- loader: 'next-intl/extractor/catalogLoader',
216
- options: {
217
- messages: pluginConfig.experimental.messages
218
- }
219
- };
220
- }
221
- function getTurboRules() {
222
- return nextConfig?.turbopack?.rules ||
223
- // @ts-expect-error -- For Next.js <16
224
- nextConfig?.experimental?.turbo?.rules || {};
225
- }
226
- function addTurboRule(rules, glob, rule) {
227
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
228
- if (rules[glob]) {
229
- if (Array.isArray(rules[glob])) {
230
- rules[glob].push(rule);
231
- } else {
232
- rules[glob] = [rules[glob], rule];
233
- }
234
- } else {
235
- rules[glob] = rule;
236
- }
237
- }
238
- if (useTurbo) {
239
- if (pluginConfig.requestConfig && path.isAbsolute(pluginConfig.requestConfig)) {
240
- throwError("Turbopack support for next-intl currently does not support absolute paths, please provide a relative one (e.g. './src/i18n/config.ts').\n\nFound: " + pluginConfig.requestConfig);
241
- }
242
-
243
- // Assign alias for `next-intl/config`
244
- const resolveAlias = {
245
- // Turbo aliases don't work with absolute
246
- // paths (see error handling above)
247
- 'next-intl/config': resolveI18nPath(pluginConfig.requestConfig)
248
- };
249
-
250
- // Add loaders
251
- let rules;
252
-
253
- // Add loader for extractor
254
- if (pluginConfig.experimental?.extract) {
255
- if (!isNextJs16OrHigher()) {
256
- throwError('Message extraction requires Next.js 16 or higher.');
257
- }
258
- rules ??= getTurboRules();
259
- const srcPaths = (Array.isArray(pluginConfig.experimental.srcPath) ? pluginConfig.experimental.srcPath : [pluginConfig.experimental.srcPath]).map(srcPath => srcPath.endsWith('/') ? srcPath.slice(0, -1) : srcPath);
260
- addTurboRule(rules, `*.{${SourceFileFilter.EXTENSIONS.join(',')}}`, {
261
- loaders: [getExtractMessagesLoaderConfig()],
262
- condition: {
263
- // Note: We don't need `not: 'foreign'`, because this is
264
- // implied by the filter based on `srcPath`.
265
- path: `{${srcPaths.join(',')}}` + '/**/*',
266
- content: /(useExtracted|getExtracted)/
267
- }
268
- });
269
- }
270
-
271
- // Add loader for catalog
272
- if (pluginConfig.experimental?.messages) {
273
- if (!isNextJs16OrHigher()) {
274
- throwError('Message catalog loading requires Next.js 16 or higher.');
275
- }
276
- rules ??= getTurboRules();
277
- addTurboRule(rules, `*.${pluginConfig.experimental.messages.format}`, {
278
- loaders: [getCatalogLoaderConfig()],
279
- condition: {
280
- path: `${pluginConfig.experimental.messages.path}/**/*`
281
- },
282
- as: '*.js'
283
- });
284
- }
285
- if (hasStableTurboConfig() &&
286
- // @ts-expect-error -- For Next.js <16
287
- !nextConfig?.experimental?.turbo) {
288
- nextIntlConfig.turbopack = {
289
- ...nextConfig?.turbopack,
290
- ...(rules && {
291
- rules
292
- }),
293
- resolveAlias: {
294
- ...nextConfig?.turbopack?.resolveAlias,
295
- ...resolveAlias
296
- }
297
- };
298
- } else {
299
- nextIntlConfig.experimental = {
300
- ...nextConfig?.experimental,
301
- // @ts-expect-error -- For Next.js <16
302
- turbo: {
303
- // @ts-expect-error -- For Next.js <16
304
- ...nextConfig?.experimental?.turbo,
305
- ...(rules && {
306
- rules
307
- }),
308
- resolveAlias: {
309
- // @ts-expect-error -- For Next.js <16
310
- ...nextConfig?.experimental?.turbo?.resolveAlias,
311
- ...resolveAlias
312
- }
313
- }
314
- };
315
- }
316
- } else {
317
- nextIntlConfig.webpack = function webpack(config, context) {
318
- if (!config.resolve) config.resolve = {};
319
- if (!config.resolve.alias) config.resolve.alias = {};
320
-
321
- // Assign alias for `next-intl/config`
322
- // (Webpack requires absolute paths)
323
- config.resolve.alias['next-intl/config'] = path.resolve(config.context, resolveI18nPath(pluginConfig.requestConfig, config.context));
324
-
325
- // Add loader for extractor
326
- if (pluginConfig.experimental?.extract) {
327
- if (!config.module) config.module = {};
328
- if (!config.module.rules) config.module.rules = [];
329
- const srcPath = pluginConfig.experimental.srcPath;
330
- config.module.rules.push({
331
- test: new RegExp(`\\.(${SourceFileFilter.EXTENSIONS.join('|')})$`),
332
- include: Array.isArray(srcPath) ? srcPath.map(cur => path.resolve(config.context, cur)) : path.resolve(config.context, srcPath || ''),
333
- use: [getExtractMessagesLoaderConfig()]
334
- });
335
- }
336
-
337
- // Add loader for catalog
338
- if (pluginConfig.experimental?.messages) {
339
- if (!config.module) config.module = {};
340
- if (!config.module.rules) config.module.rules = [];
341
- config.module.rules.push({
342
- test: new RegExp(`\\.${pluginConfig.experimental.messages.format}$`),
343
- include: path.resolve(config.context, pluginConfig.experimental.messages.path),
344
- use: [getCatalogLoaderConfig()],
345
- type: 'javascript/auto'
346
- });
347
- }
348
- if (typeof nextConfig?.webpack === 'function') {
349
- return nextConfig.webpack(config, context);
350
- }
351
- return config;
352
- };
353
- }
354
-
355
- // Forward config
356
- if (nextConfig?.trailingSlash) {
357
- nextIntlConfig.env = {
358
- ...nextConfig.env,
359
- _next_intl_trailing_slash: 'true'
360
- };
361
- }
362
- return Object.assign({}, nextConfig, nextIntlConfig);
363
- }
364
-
365
- function initPlugin(pluginConfig, nextConfig) {
366
- if (nextConfig?.i18n != null) {
367
- warn("\n[next-intl] An `i18n` property was found in your Next.js config. This likely causes conflicts and should therefore be removed if you use the App Router.\n\nIf you're in progress of migrating from the Pages Router, you can refer to this example: https://next-intl.dev/examples#app-router-migration\n");
368
- }
369
- const messagesPathOrPaths = pluginConfig.experimental?.createMessagesDeclaration;
370
- if (messagesPathOrPaths) {
371
- createMessagesDeclaration(typeof messagesPathOrPaths === 'string' ? [messagesPathOrPaths] : messagesPathOrPaths);
372
- }
373
- return getNextConfig(pluginConfig, nextConfig);
374
- }
375
- function createNextIntlPlugin(i18nPathOrConfig = {}) {
376
- const config = typeof i18nPathOrConfig === 'string' ? {
377
- requestConfig: i18nPathOrConfig
378
- } : i18nPathOrConfig;
379
- return function withNextIntl(nextConfig) {
380
- return initPlugin(config, nextConfig);
381
- };
382
- }
383
-
384
- module.exports = createNextIntlPlugin;
13
+ module.exports = plugin.createNextIntlPlugin;
@@ -1,40 +1,37 @@
1
1
  import CatalogManager from './catalog/CatalogManager.js';
2
+ import MessageExtractor from './extractor/MessageExtractor.js';
2
3
 
3
4
  class ExtractionCompiler {
4
- isDevelopment = false;
5
5
  constructor(config, opts = {}) {
6
- this.manager = new CatalogManager(config, opts);
7
- this.isDevelopment = opts.isDevelopment ?? false;
8
-
9
- // Kick off the initial scan as early as possible,
10
- // while awaiting it in `compile`. This also ensures
11
- // we're only scanning once.
12
- this.initialScanPromise = this.performInitialScan();
13
- }
14
- async compile(resourcePath, source) {
15
- if (this.initialScanPromise) {
16
- await this.initialScanPromise;
17
- this.initialScanPromise = undefined;
18
- }
19
- const result = await this.manager.extractFileMessages(resourcePath, source);
20
- if (this.isDevelopment && result.changed) {
21
- // While we await the AST modification, we
22
- // don't need to await the persistence
23
- void this.manager.save();
24
- }
25
- return result;
6
+ const extractor = opts.extractor ?? new MessageExtractor(opts);
7
+ this.manager = new CatalogManager(config, {
8
+ ...opts,
9
+ extractor
10
+ });
11
+ this[Symbol.dispose] = this[Symbol.dispose].bind(this);
12
+ this.installExitHandlers();
26
13
  }
27
- async performInitialScan() {
14
+ async extractAll() {
28
15
  // We can't rely on all files being compiled (e.g. due to persistent
29
16
  // caching), so loading the messages initially is necessary.
30
17
  await this.manager.loadMessages();
31
18
  await this.manager.save();
32
19
  }
33
- async extract() {
34
- await this.initialScanPromise;
35
- }
36
20
  [Symbol.dispose]() {
37
- this.manager.destroy();
21
+ this.uninstallExitHandlers();
22
+ this.manager[Symbol.dispose]();
23
+ }
24
+ installExitHandlers() {
25
+ const cleanup = this[Symbol.dispose];
26
+ process.on('exit', cleanup);
27
+ process.on('SIGINT', cleanup);
28
+ process.on('SIGTERM', cleanup);
29
+ }
30
+ uninstallExitHandlers() {
31
+ const cleanup = this[Symbol.dispose];
32
+ process.off('exit', cleanup);
33
+ process.off('SIGINT', cleanup);
34
+ process.off('SIGTERM', cleanup);
38
35
  }
39
36
  }
40
37
 
@@ -3,7 +3,6 @@ import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
 
5
5
  class CatalogLocales {
6
- cleanupHandlers = [];
7
6
  onChangeCallbacks = (() => new Set())();
8
7
  constructor(params) {
9
8
  this.messagesDir = params.messagesDir;
@@ -58,17 +57,12 @@ class CatalogLocales {
58
57
  void this.onChange();
59
58
  }
60
59
  });
61
- this.setupCleanupHandlers();
62
60
  }
63
61
  stopWatcher() {
64
62
  if (this.watcher) {
65
63
  this.watcher.close();
66
64
  this.watcher = undefined;
67
65
  }
68
- for (const handler of this.cleanupHandlers) {
69
- handler();
70
- }
71
- this.cleanupHandlers = [];
72
66
  }
73
67
  async onChange() {
74
68
  const oldLocales = new Set(this.targetLocales || []);
@@ -85,33 +79,6 @@ class CatalogLocales {
85
79
  }
86
80
  }
87
81
  }
88
- setupCleanupHandlers() {
89
- const cleanup = () => {
90
- if (this.watcher) {
91
- this.watcher.close();
92
- this.watcher = undefined;
93
- }
94
- };
95
- function exitHandler() {
96
- cleanup();
97
- }
98
- function sigintHandler() {
99
- cleanup();
100
- process.exit(0);
101
- }
102
- function sigtermHandler() {
103
- cleanup();
104
- process.exit(0);
105
- }
106
- process.once('exit', exitHandler);
107
- process.once('SIGINT', sigintHandler);
108
- process.once('SIGTERM', sigtermHandler);
109
- this.cleanupHandlers.push(() => {
110
- process.removeListener('exit', exitHandler);
111
- process.removeListener('SIGINT', sigintHandler);
112
- process.removeListener('SIGTERM', sigtermHandler);
113
- });
114
- }
115
82
  }
116
83
 
117
84
  export { CatalogLocales as default };