@depup/next-intl 4.8.3-depup.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/LICENSE +21 -0
- package/README.md +33 -0
- package/changes.json +18 -0
- package/config.d.ts +4 -0
- package/dist/cjs/development/ExtractorCodec-D9Tw618d.cjs +7 -0
- package/dist/cjs/development/JSONCodec-rhejs716.cjs +50 -0
- package/dist/cjs/development/POCodec-Cv3VdXQG.cjs +105 -0
- package/dist/cjs/development/plugin-YzBhQjAo.cjs +1457 -0
- package/dist/cjs/development/plugin.cjs +13 -0
- package/dist/esm/development/config.js +5 -0
- package/dist/esm/development/extractor/ExtractionCompiler.js +38 -0
- package/dist/esm/development/extractor/catalog/CatalogLocales.js +84 -0
- package/dist/esm/development/extractor/catalog/CatalogManager.js +370 -0
- package/dist/esm/development/extractor/catalog/CatalogPersister.js +63 -0
- package/dist/esm/development/extractor/catalog/SaveScheduler.js +85 -0
- package/dist/esm/development/extractor/catalogLoader.js +96 -0
- package/dist/esm/development/extractor/extractMessages.js +15 -0
- package/dist/esm/development/extractor/extractionLoader.js +26 -0
- package/dist/esm/development/extractor/extractor/LRUCache.js +30 -0
- package/dist/esm/development/extractor/extractor/MessageExtractor.js +66 -0
- package/dist/esm/development/extractor/format/ExtractorCodec.js +5 -0
- package/dist/esm/development/extractor/format/codecs/JSONCodec.js +42 -0
- package/dist/esm/development/extractor/format/codecs/POCodec.js +93 -0
- package/dist/esm/development/extractor/format/index.js +44 -0
- package/dist/esm/development/extractor/source/SourceFileFilter.js +29 -0
- package/dist/esm/development/extractor/source/SourceFileScanner.js +31 -0
- package/dist/esm/development/extractor/source/SourceFileWatcher.js +132 -0
- package/dist/esm/development/extractor/utils.js +46 -0
- package/dist/esm/development/extractor.js +2 -0
- package/dist/esm/development/index.react-client.js +4 -0
- package/dist/esm/development/index.react-server.js +9 -0
- package/dist/esm/development/middleware/getAlternateLinksHeaderValue.js +91 -0
- package/dist/esm/development/middleware/middleware.js +168 -0
- package/dist/esm/development/middleware/resolveLocale.js +129 -0
- package/dist/esm/development/middleware/syncCookie.js +27 -0
- package/dist/esm/development/middleware/utils.js +194 -0
- package/dist/esm/development/middleware.js +1 -0
- package/dist/esm/development/navigation/react-client/createNavigation.js +80 -0
- package/dist/esm/development/navigation/react-client/useBasePathname.js +36 -0
- package/dist/esm/development/navigation/react-server/createNavigation.js +22 -0
- package/dist/esm/development/navigation/react-server/getServerLocale.js +12 -0
- package/dist/esm/development/navigation/shared/BaseLink.js +51 -0
- package/dist/esm/development/navigation/shared/createSharedNavigationFns.js +105 -0
- package/dist/esm/development/navigation/shared/syncLocaleCookie.js +47 -0
- package/dist/esm/development/navigation/shared/utils.js +165 -0
- package/dist/esm/development/navigation.react-client.js +1 -0
- package/dist/esm/development/navigation.react-server.js +1 -0
- package/dist/esm/development/plugin/config.js +10 -0
- package/dist/esm/development/plugin/createNextIntlPlugin.js +26 -0
- package/dist/esm/development/plugin/declaration/createMessagesDeclaration.js +70 -0
- package/dist/esm/development/plugin/extractor/initExtractionCompiler.js +59 -0
- package/dist/esm/development/plugin/getNextConfig.js +267 -0
- package/dist/esm/development/plugin/nextFlags.js +32 -0
- package/dist/esm/development/plugin/utils.js +26 -0
- package/dist/esm/development/plugin/watchFile.js +21 -0
- package/dist/esm/development/plugin.js +1 -0
- package/dist/esm/development/react-client/index.js +34 -0
- package/dist/esm/development/react-server/NextIntlClientProviderServer.js +34 -0
- package/dist/esm/development/react-server/useConfig.js +21 -0
- package/dist/esm/development/react-server/useExtracted.js +9 -0
- package/dist/esm/development/react-server/useFormatter.js +9 -0
- package/dist/esm/development/react-server/useLocale.js +8 -0
- package/dist/esm/development/react-server/useMessages.js +9 -0
- package/dist/esm/development/react-server/useNow.js +12 -0
- package/dist/esm/development/react-server/useTimeZone.js +8 -0
- package/dist/esm/development/react-server/useTranslations.js +9 -0
- package/dist/esm/development/routing/config.js +26 -0
- package/dist/esm/development/routing/defineRouting.js +25 -0
- package/dist/esm/development/routing.js +1 -0
- package/dist/esm/development/server/react-client/index.js +32 -0
- package/dist/esm/development/server/react-server/RequestLocale.js +36 -0
- package/dist/esm/development/server/react-server/RequestLocaleCache.js +18 -0
- package/dist/esm/development/server/react-server/createRequestConfig.js +2 -0
- package/dist/esm/development/server/react-server/getConfig.js +59 -0
- package/dist/esm/development/server/react-server/getConfigNow.js +10 -0
- package/dist/esm/development/server/react-server/getDefaultNow.js +9 -0
- package/dist/esm/development/server/react-server/getExtracted.js +24 -0
- package/dist/esm/development/server/react-server/getFormats.js +10 -0
- package/dist/esm/development/server/react-server/getFormatter.js +21 -0
- package/dist/esm/development/server/react-server/getLocale.js +10 -0
- package/dist/esm/development/server/react-server/getMessages.js +19 -0
- package/dist/esm/development/server/react-server/getNow.js +8 -0
- package/dist/esm/development/server/react-server/getRequestConfig.js +8 -0
- package/dist/esm/development/server/react-server/getServerExtractor.js +38 -0
- package/dist/esm/development/server/react-server/getServerFormatter.js +17 -0
- package/dist/esm/development/server/react-server/getServerTranslator.js +12 -0
- package/dist/esm/development/server/react-server/getTimeZone.js +13 -0
- package/dist/esm/development/server/react-server/getTranslations.js +28 -0
- package/dist/esm/development/server/react-server/validateLocale.js +12 -0
- package/dist/esm/development/server.react-client.js +1 -0
- package/dist/esm/development/server.react-server.js +9 -0
- package/dist/esm/development/shared/NextIntlClientProvider.js +18 -0
- package/dist/esm/development/shared/constants.js +4 -0
- package/dist/esm/development/shared/use.js +13 -0
- package/dist/esm/development/shared/utils.js +140 -0
- package/dist/esm/production/config.js +1 -0
- package/dist/esm/production/extractor/ExtractionCompiler.js +1 -0
- package/dist/esm/production/extractor/catalog/CatalogLocales.js +1 -0
- package/dist/esm/production/extractor/catalog/CatalogManager.js +1 -0
- package/dist/esm/production/extractor/catalog/CatalogPersister.js +1 -0
- package/dist/esm/production/extractor/catalog/SaveScheduler.js +1 -0
- package/dist/esm/production/extractor/catalogLoader.js +1 -0
- package/dist/esm/production/extractor/extractMessages.js +1 -0
- package/dist/esm/production/extractor/extractionLoader.js +1 -0
- package/dist/esm/production/extractor/extractor/LRUCache.js +1 -0
- package/dist/esm/production/extractor/extractor/MessageExtractor.js +1 -0
- package/dist/esm/production/extractor/format/ExtractorCodec.js +1 -0
- package/dist/esm/production/extractor/format/codecs/JSONCodec.js +1 -0
- package/dist/esm/production/extractor/format/codecs/POCodec.js +1 -0
- package/dist/esm/production/extractor/format/index.js +1 -0
- package/dist/esm/production/extractor/source/SourceFileFilter.js +1 -0
- package/dist/esm/production/extractor/source/SourceFileScanner.js +1 -0
- package/dist/esm/production/extractor/source/SourceFileWatcher.js +1 -0
- package/dist/esm/production/extractor/utils.js +1 -0
- package/dist/esm/production/extractor.js +1 -0
- package/dist/esm/production/index.react-client.js +1 -0
- package/dist/esm/production/index.react-server.js +1 -0
- package/dist/esm/production/middleware/getAlternateLinksHeaderValue.js +1 -0
- package/dist/esm/production/middleware/middleware.js +1 -0
- package/dist/esm/production/middleware/resolveLocale.js +1 -0
- package/dist/esm/production/middleware/syncCookie.js +1 -0
- package/dist/esm/production/middleware/utils.js +1 -0
- package/dist/esm/production/middleware.js +1 -0
- package/dist/esm/production/navigation/react-client/createNavigation.js +1 -0
- package/dist/esm/production/navigation/react-client/useBasePathname.js +1 -0
- package/dist/esm/production/navigation/react-server/createNavigation.js +1 -0
- package/dist/esm/production/navigation/react-server/getServerLocale.js +1 -0
- package/dist/esm/production/navigation/shared/BaseLink.js +2 -0
- package/dist/esm/production/navigation/shared/createSharedNavigationFns.js +1 -0
- package/dist/esm/production/navigation/shared/syncLocaleCookie.js +1 -0
- package/dist/esm/production/navigation/shared/utils.js +1 -0
- package/dist/esm/production/navigation.react-client.js +1 -0
- package/dist/esm/production/navigation.react-server.js +1 -0
- package/dist/esm/production/plugin/config.js +1 -0
- package/dist/esm/production/plugin/createNextIntlPlugin.js +1 -0
- package/dist/esm/production/plugin/declaration/createMessagesDeclaration.js +1 -0
- package/dist/esm/production/plugin/extractor/initExtractionCompiler.js +1 -0
- package/dist/esm/production/plugin/getNextConfig.js +1 -0
- package/dist/esm/production/plugin/nextFlags.js +1 -0
- package/dist/esm/production/plugin/utils.js +1 -0
- package/dist/esm/production/plugin/watchFile.js +1 -0
- package/dist/esm/production/plugin.js +1 -0
- package/dist/esm/production/react-client/index.js +1 -0
- package/dist/esm/production/react-server/NextIntlClientProviderServer.js +1 -0
- package/dist/esm/production/react-server/useConfig.js +1 -0
- package/dist/esm/production/react-server/useExtracted.js +1 -0
- package/dist/esm/production/react-server/useFormatter.js +1 -0
- package/dist/esm/production/react-server/useLocale.js +1 -0
- package/dist/esm/production/react-server/useMessages.js +1 -0
- package/dist/esm/production/react-server/useNow.js +1 -0
- package/dist/esm/production/react-server/useTimeZone.js +1 -0
- package/dist/esm/production/react-server/useTranslations.js +1 -0
- package/dist/esm/production/routing/config.js +1 -0
- package/dist/esm/production/routing/defineRouting.js +1 -0
- package/dist/esm/production/routing.js +1 -0
- package/dist/esm/production/server/react-client/index.js +1 -0
- package/dist/esm/production/server/react-server/RequestLocale.js +1 -0
- package/dist/esm/production/server/react-server/RequestLocaleCache.js +1 -0
- package/dist/esm/production/server/react-server/createRequestConfig.js +1 -0
- package/dist/esm/production/server/react-server/getConfig.js +1 -0
- package/dist/esm/production/server/react-server/getConfigNow.js +1 -0
- package/dist/esm/production/server/react-server/getDefaultNow.js +1 -0
- package/dist/esm/production/server/react-server/getExtracted.js +1 -0
- package/dist/esm/production/server/react-server/getFormats.js +1 -0
- package/dist/esm/production/server/react-server/getFormatter.js +1 -0
- package/dist/esm/production/server/react-server/getLocale.js +1 -0
- package/dist/esm/production/server/react-server/getMessages.js +1 -0
- package/dist/esm/production/server/react-server/getNow.js +1 -0
- package/dist/esm/production/server/react-server/getRequestConfig.js +1 -0
- package/dist/esm/production/server/react-server/getServerExtractor.js +1 -0
- package/dist/esm/production/server/react-server/getServerFormatter.js +1 -0
- package/dist/esm/production/server/react-server/getServerTranslator.js +1 -0
- package/dist/esm/production/server/react-server/getTimeZone.js +1 -0
- package/dist/esm/production/server/react-server/getTranslations.js +1 -0
- package/dist/esm/production/server.react-client.js +1 -0
- package/dist/esm/production/server.react-server.js +1 -0
- package/dist/esm/production/shared/NextIntlClientProvider.js +2 -0
- package/dist/esm/production/shared/constants.js +1 -0
- package/dist/esm/production/shared/use.js +1 -0
- package/dist/esm/production/shared/utils.js +1 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/extractor/ExtractionCompiler.d.ts +15 -0
- package/dist/types/extractor/catalog/CatalogLocales.d.ts +29 -0
- package/dist/types/extractor/catalog/CatalogManager.d.ts +59 -0
- package/dist/types/extractor/catalog/CatalogPersister.d.ts +20 -0
- package/dist/types/extractor/catalog/SaveScheduler.d.ts +18 -0
- package/dist/types/extractor/extractMessages.d.ts +2 -0
- package/dist/types/extractor/extractor/KeyGenerator.d.ts +3 -0
- package/dist/types/extractor/extractor/LRUCache.d.ts +7 -0
- package/dist/types/extractor/extractor/MessageExtractor.bench.d.ts +1 -0
- package/dist/types/extractor/extractor/MessageExtractor.d.ts +21 -0
- package/dist/types/extractor/format/ExtractorCodec.d.ts +33 -0
- package/dist/types/extractor/format/codecs/JSONCodec.d.ts +2 -0
- package/dist/types/extractor/format/codecs/POCodec.d.ts +2 -0
- package/dist/types/extractor/format/codecs/fixtures/JSONCodecStructured.d.ts +2 -0
- package/dist/types/extractor/format/codecs/fixtures/POCodecSourceMessageKey.d.ts +2 -0
- package/dist/types/extractor/format/index.d.ts +15 -0
- package/dist/types/extractor/format/types.d.ts +8 -0
- package/dist/types/extractor/index.d.ts +2 -0
- package/dist/types/extractor/source/SourceFileFilter.d.ts +8 -0
- package/dist/types/extractor/source/SourceFileScanner.d.ts +4 -0
- package/dist/types/extractor/source/SourceFileWatcher.d.ts +15 -0
- package/dist/types/extractor/types.d.ts +28 -0
- package/dist/types/extractor/utils.d.ts +7 -0
- package/dist/types/extractor.d.ts +1 -0
- package/dist/types/index.react-client.d.ts +8 -0
- package/dist/types/index.react-server.d.ts +1 -0
- package/dist/types/middleware/getAlternateLinksHeaderValue.d.ts +13 -0
- package/dist/types/middleware/index.d.ts +4 -0
- package/dist/types/middleware/middleware.d.ts +4 -0
- package/dist/types/middleware/resolveLocale.d.ts +9 -0
- package/dist/types/middleware/syncCookie.d.ts +7 -0
- package/dist/types/middleware/utils.d.ts +26 -0
- package/dist/types/middleware.d.ts +1 -0
- package/dist/types/navigation/react-client/createNavigation.d.ts +464 -0
- package/dist/types/navigation/react-client/index.d.ts +2 -0
- package/dist/types/navigation/react-client/useBasePathname.d.ts +5 -0
- package/dist/types/navigation/react-server/createNavigation.d.ts +384 -0
- package/dist/types/navigation/react-server/getServerLocale.d.ts +5 -0
- package/dist/types/navigation/react-server/index.d.ts +2 -0
- package/dist/types/navigation/shared/BaseLink.d.ts +11 -0
- package/dist/types/navigation/shared/StrictParams.d.ts +8 -0
- package/dist/types/navigation/shared/createSharedNavigationFns.d.ts +370 -0
- package/dist/types/navigation/shared/syncLocaleCookie.d.ts +8 -0
- package/dist/types/navigation/shared/utils.d.ts +53 -0
- package/dist/types/navigation.react-client.d.ts +1 -0
- package/dist/types/navigation.react-server.d.ts +1 -0
- package/dist/types/plugin/catalog/catalogLoader.d.ts +10 -0
- package/dist/types/plugin/config.d.ts +3 -0
- package/dist/types/plugin/createNextIntlPlugin.d.ts +3 -0
- package/dist/types/plugin/declaration/createMessagesDeclaration.d.ts +1 -0
- package/dist/types/plugin/declaration/index.d.ts +1 -0
- package/dist/types/plugin/extractor/extractionLoader.d.ts +3 -0
- package/dist/types/plugin/extractor/initExtractionCompiler.d.ts +2 -0
- package/dist/types/plugin/getNextConfig.d.ts +3 -0
- package/dist/types/plugin/index.d.ts +1 -0
- package/dist/types/plugin/nextFlags.d.ts +2 -0
- package/dist/types/plugin/types.d.ts +29 -0
- package/dist/types/plugin/utils.d.ts +8 -0
- package/dist/types/plugin/watchFile.d.ts +6 -0
- package/dist/types/plugin.d.ts +1 -0
- package/dist/types/react-client/index.d.ts +16 -0
- package/dist/types/react-server/NextIntlClientProviderServer.d.ts +5 -0
- package/dist/types/react-server/index.d.ts +16 -0
- package/dist/types/react-server/testUtils.d.ts +2 -0
- package/dist/types/react-server/useConfig.d.ts +2 -0
- package/dist/types/react-server/useExtracted.d.ts +2 -0
- package/dist/types/react-server/useFormatter.d.ts +2 -0
- package/dist/types/react-server/useLocale.d.ts +2 -0
- package/dist/types/react-server/useMessages.d.ts +2 -0
- package/dist/types/react-server/useNow.d.ts +2 -0
- package/dist/types/react-server/useTimeZone.d.ts +2 -0
- package/dist/types/react-server/useTranslations.d.ts +2 -0
- package/dist/types/routing/config.d.ts +73 -0
- package/dist/types/routing/defineRouting.d.ts +3 -0
- package/dist/types/routing/index.d.ts +3 -0
- package/dist/types/routing/types.d.ts +24 -0
- package/dist/types/routing.d.ts +1 -0
- package/dist/types/server/react-client/index.d.ts +10 -0
- package/dist/types/server/react-server/RequestLocale.d.ts +1 -0
- package/dist/types/server/react-server/RequestLocaleCache.d.ts +3 -0
- package/dist/types/server/react-server/createRequestConfig.d.ts +3 -0
- package/dist/types/server/react-server/getConfig.d.ts +13 -0
- package/dist/types/server/react-server/getConfigNow.d.ts +4 -0
- package/dist/types/server/react-server/getDefaultNow.d.ts +3 -0
- package/dist/types/server/react-server/getExtracted.d.ts +10 -0
- package/dist/types/server/react-server/getFormats.d.ts +3 -0
- package/dist/types/server/react-server/getFormatter.d.ts +10 -0
- package/dist/types/server/react-server/getLocale.d.ts +4 -0
- package/dist/types/server/react-server/getMessages.d.ts +6 -0
- package/dist/types/server/react-server/getNow.d.ts +4 -0
- package/dist/types/server/react-server/getRequestConfig.d.ts +35 -0
- package/dist/types/server/react-server/getServerExtractor.d.ts +5 -0
- package/dist/types/server/react-server/getServerFormatter.d.ts +25 -0
- package/dist/types/server/react-server/getServerTranslator.d.ts +4 -0
- package/dist/types/server/react-server/getTimeZone.d.ts +4 -0
- package/dist/types/server/react-server/getTranslations.d.ts +8 -0
- package/dist/types/server/react-server/index.d.ts +12 -0
- package/dist/types/server/react-server/validateLocale.d.ts +1 -0
- package/dist/types/server.react-client.d.ts +1 -0
- package/dist/types/server.react-server.d.ts +1 -0
- package/dist/types/shared/NextIntlClientProvider.d.ts +9 -0
- package/dist/types/shared/constants.d.ts +1 -0
- package/dist/types/shared/types.d.ts +5 -0
- package/dist/types/shared/use.d.ts +3 -0
- package/dist/types/shared/utils.d.ts +20 -0
- package/extractor/catalogLoader.d.ts +4 -0
- package/extractor/extractionLoader.d.ts +4 -0
- package/extractor.d.ts +2 -0
- package/middleware.d.ts +4 -0
- package/navigation.d.ts +2 -0
- package/package.json +175 -0
- package/plugin.d.cts +3 -0
- package/plugin.d.ts +4 -0
- package/routing.d.ts +2 -0
- package/server.d.ts +2 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import compile from 'icu-minify/compile';
|
|
3
|
+
import { getFormatExtension, resolveCodec } from './format/index.js';
|
|
4
|
+
import { setNestedProperty } from './utils.js';
|
|
5
|
+
|
|
6
|
+
// The module scope is safe for some caching, but Next.js can
|
|
7
|
+
// create multiple loader instances so don't expect a singleton.
|
|
8
|
+
let cachedCodec = null;
|
|
9
|
+
const messageCacheByCatalog = new Map();
|
|
10
|
+
function getMessageCache(catalogId) {
|
|
11
|
+
let cache = messageCacheByCatalog.get(catalogId);
|
|
12
|
+
if (!cache) {
|
|
13
|
+
cache = new Map();
|
|
14
|
+
messageCacheByCatalog.set(catalogId, cache);
|
|
15
|
+
}
|
|
16
|
+
return cache;
|
|
17
|
+
}
|
|
18
|
+
async function getCodec(options, projectRoot) {
|
|
19
|
+
if (!cachedCodec) {
|
|
20
|
+
cachedCodec = await resolveCodec(options.messages.format, projectRoot);
|
|
21
|
+
}
|
|
22
|
+
return cachedCodec;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parses and optimizes catalog files.
|
|
27
|
+
*
|
|
28
|
+
* Note that if we use a dynamic import like `import(`${locale}.json`)`, then
|
|
29
|
+
* the loader will optimistically run for all candidates in this folder (both
|
|
30
|
+
* during dev as well as at build time).
|
|
31
|
+
*/
|
|
32
|
+
function catalogLoader(source) {
|
|
33
|
+
const options = this.getOptions();
|
|
34
|
+
const callback = this.async();
|
|
35
|
+
const extension = getFormatExtension(options.messages.format);
|
|
36
|
+
getCodec(options, this.rootContext).then(codec => {
|
|
37
|
+
const locale = path.basename(this.resourcePath, extension);
|
|
38
|
+
let outputString;
|
|
39
|
+
if (options.messages.precompile) {
|
|
40
|
+
const decoded = codec.decode(source, {
|
|
41
|
+
locale
|
|
42
|
+
});
|
|
43
|
+
const cache = getMessageCache(this.resourcePath);
|
|
44
|
+
const precompiled = precompileMessages(decoded, cache);
|
|
45
|
+
outputString = JSON.stringify(precompiled);
|
|
46
|
+
} else {
|
|
47
|
+
outputString = codec.toJSONString(source, {
|
|
48
|
+
locale
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// https://v8.dev/blog/cost-of-javascript-2019#json
|
|
53
|
+
const result = `export default JSON.parse(${JSON.stringify(outputString)});`;
|
|
54
|
+
callback(null, result);
|
|
55
|
+
}).catch(callback);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Recursively precompiles all ICU message strings in a messages object
|
|
60
|
+
* using icu-minify/compile for smaller runtime bundles.
|
|
61
|
+
*/
|
|
62
|
+
function precompileMessages(messages, cache) {
|
|
63
|
+
const result = {};
|
|
64
|
+
const cacheKeysToEvict = new Set(cache.keys());
|
|
65
|
+
for (const message of messages) {
|
|
66
|
+
cacheKeysToEvict.delete(message.id);
|
|
67
|
+
const messageValue = message.message;
|
|
68
|
+
if (Array.isArray(messageValue)) {
|
|
69
|
+
throw new Error(`Message at \`${message.id}\` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/translations#arrays-of-messages`);
|
|
70
|
+
}
|
|
71
|
+
if (typeof messageValue === 'object') {
|
|
72
|
+
throw new Error(`Message at \`${message.id}\` resolved to \`${typeof messageValue}\`, but only strings are supported. Use a \`.\` to retrieve nested messages. See https://next-intl.dev/docs/usage/translations#structuring-messages`);
|
|
73
|
+
}
|
|
74
|
+
const cachedEntry = cache.get(message.id);
|
|
75
|
+
const hasCacheMatch = cachedEntry?.messageValue === messageValue;
|
|
76
|
+
let compiledMessage;
|
|
77
|
+
if (hasCacheMatch) {
|
|
78
|
+
compiledMessage = cachedEntry.compiledMessage;
|
|
79
|
+
} else {
|
|
80
|
+
compiledMessage = compile(messageValue);
|
|
81
|
+
cache.set(message.id, {
|
|
82
|
+
compiledMessage,
|
|
83
|
+
messageValue
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
setNestedProperty(result, message.id, compiledMessage);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Evict unused cache entries
|
|
90
|
+
for (const cachedId of cacheKeysToEvict) {
|
|
91
|
+
cache.delete(cachedId);
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { catalogLoader as default };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ExtractionCompiler from './ExtractionCompiler.js';
|
|
2
|
+
import MessageExtractor from './extractor/MessageExtractor.js';
|
|
3
|
+
import { getDefaultProjectRoot } from './utils.js';
|
|
4
|
+
|
|
5
|
+
async function extractMessages(params) {
|
|
6
|
+
const compiler = new ExtractionCompiler(params, {
|
|
7
|
+
extractor: new MessageExtractor({
|
|
8
|
+
isDevelopment: false,
|
|
9
|
+
projectRoot: getDefaultProjectRoot()
|
|
10
|
+
})
|
|
11
|
+
});
|
|
12
|
+
await compiler.extractAll();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { extractMessages as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import MessageExtractor from './extractor/MessageExtractor.js';
|
|
2
|
+
|
|
3
|
+
// Module-level extractor instance for transformation caching.
|
|
4
|
+
// Note: Next.js/Turbopack may create multiple loader instances, but each
|
|
5
|
+
// only handles file transformation. The ExtractionCompiler (which manages
|
|
6
|
+
// catalogs) is initialized separately in createNextIntlPlugin.
|
|
7
|
+
let extractor;
|
|
8
|
+
function extractionLoader(source) {
|
|
9
|
+
const callback = this.async();
|
|
10
|
+
const projectRoot = this.rootContext;
|
|
11
|
+
|
|
12
|
+
// Avoid rollup's `replace` plugin to compile this away
|
|
13
|
+
const isDevelopment = process.env['NODE_ENV'.trim()] === 'development';
|
|
14
|
+
if (!extractor) {
|
|
15
|
+
extractor = new MessageExtractor({
|
|
16
|
+
isDevelopment,
|
|
17
|
+
projectRoot,
|
|
18
|
+
sourceMap: this.sourceMap
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
extractor.extract(this.resourcePath, source).then(result => {
|
|
22
|
+
callback(null, result.code, result.map);
|
|
23
|
+
}).catch(callback);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { extractionLoader as default };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class LRUCache {
|
|
2
|
+
constructor(maxSize) {
|
|
3
|
+
this.maxSize = maxSize;
|
|
4
|
+
this.cache = new Map();
|
|
5
|
+
}
|
|
6
|
+
set(key, value) {
|
|
7
|
+
const isNewKey = !this.cache.has(key);
|
|
8
|
+
if (isNewKey && this.cache.size >= this.maxSize) {
|
|
9
|
+
const lruKey = this.cache.keys().next().value;
|
|
10
|
+
if (lruKey !== undefined) {
|
|
11
|
+
this.cache.delete(lruKey);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
this.cache.set(key, {
|
|
15
|
+
key,
|
|
16
|
+
value
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
get(key) {
|
|
20
|
+
const item = this.cache.get(key);
|
|
21
|
+
if (item) {
|
|
22
|
+
this.cache.delete(key);
|
|
23
|
+
this.cache.set(key, item);
|
|
24
|
+
return item.value;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { LRUCache as default };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { transform } from '@swc/core';
|
|
4
|
+
import { getDefaultProjectRoot, normalizePathToPosix } from '../utils.js';
|
|
5
|
+
import LRUCache from './LRUCache.js';
|
|
6
|
+
|
|
7
|
+
const require$1 = createRequire(import.meta.url);
|
|
8
|
+
class MessageExtractor {
|
|
9
|
+
compileCache = new LRUCache(750);
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
this.isDevelopment = opts.isDevelopment ?? false;
|
|
12
|
+
this.projectRoot = opts.projectRoot ?? getDefaultProjectRoot();
|
|
13
|
+
this.sourceMap = opts.sourceMap ?? false;
|
|
14
|
+
}
|
|
15
|
+
async extract(absoluteFilePath, source) {
|
|
16
|
+
const cacheKey = [source, absoluteFilePath].join('!');
|
|
17
|
+
const cached = this.compileCache.get(cacheKey);
|
|
18
|
+
if (cached) return cached;
|
|
19
|
+
|
|
20
|
+
// Shortcut parsing if hook is not used. The Turbopack integration already
|
|
21
|
+
// pre-filters this, but for webpack this feature doesn't exist, so we need
|
|
22
|
+
// to do it here.
|
|
23
|
+
if (!source.includes('useExtracted') && !source.includes('getExtracted')) {
|
|
24
|
+
return {
|
|
25
|
+
messages: [],
|
|
26
|
+
code: source
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const filePath = normalizePathToPosix(path.relative(this.projectRoot, absoluteFilePath));
|
|
30
|
+
const result = await transform(source, {
|
|
31
|
+
jsc: {
|
|
32
|
+
target: 'esnext',
|
|
33
|
+
parser: {
|
|
34
|
+
syntax: 'typescript',
|
|
35
|
+
tsx: true,
|
|
36
|
+
decorators: true
|
|
37
|
+
},
|
|
38
|
+
experimental: {
|
|
39
|
+
cacheRoot: 'node_modules/.cache/swc',
|
|
40
|
+
disableBuiltinTransformsForInternalTesting: true,
|
|
41
|
+
disableAllLints: true,
|
|
42
|
+
plugins: [[require$1.resolve('next-intl-swc-plugin-extractor'), {
|
|
43
|
+
isDevelopment: this.isDevelopment,
|
|
44
|
+
filePath
|
|
45
|
+
}]]
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
sourceMaps: this.sourceMap,
|
|
49
|
+
sourceFileName: filePath,
|
|
50
|
+
filename: filePath
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// TODO: Improve the typing of @swc/core
|
|
54
|
+
const output = result.output;
|
|
55
|
+
const messages = JSON.parse(JSON.parse(output).results);
|
|
56
|
+
const extractionResult = {
|
|
57
|
+
code: result.code,
|
|
58
|
+
map: result.map,
|
|
59
|
+
messages
|
|
60
|
+
};
|
|
61
|
+
this.compileCache.set(cacheKey, extractionResult);
|
|
62
|
+
return extractionResult;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { MessageExtractor as default };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getSortedMessages, setNestedProperty } from '../../utils.js';
|
|
2
|
+
import { defineCodec } from '../ExtractorCodec.js';
|
|
3
|
+
|
|
4
|
+
var JSONCodec = defineCodec(() => ({
|
|
5
|
+
decode(source) {
|
|
6
|
+
const json = JSON.parse(source);
|
|
7
|
+
const messages = [];
|
|
8
|
+
traverseMessages(json, (message, id) => {
|
|
9
|
+
messages.push({
|
|
10
|
+
id,
|
|
11
|
+
message
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
return messages;
|
|
15
|
+
},
|
|
16
|
+
encode(messages) {
|
|
17
|
+
const root = {};
|
|
18
|
+
for (const message of getSortedMessages(messages)) {
|
|
19
|
+
setNestedProperty(root, message.id, message.message);
|
|
20
|
+
}
|
|
21
|
+
return JSON.stringify(root, null, 2) + '\n';
|
|
22
|
+
},
|
|
23
|
+
toJSONString(source) {
|
|
24
|
+
return source;
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
function traverseMessages(obj, callback, path = '') {
|
|
28
|
+
const NAMESPACE_SEPARATOR = '.';
|
|
29
|
+
for (const key of Object.keys(obj)) {
|
|
30
|
+
const newPath = path ? path + NAMESPACE_SEPARATOR + key : key;
|
|
31
|
+
const value = obj[key];
|
|
32
|
+
if (typeof value === 'string') {
|
|
33
|
+
callback(value, newPath);
|
|
34
|
+
} else if (Array.isArray(value)) {
|
|
35
|
+
throw new Error(`Message at \`${newPath}\` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/translations#arrays-of-messages`);
|
|
36
|
+
} else if (typeof value === 'object') {
|
|
37
|
+
traverseMessages(value, callback, newPath);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { JSONCodec as default };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import POParser from 'po-parser';
|
|
2
|
+
import { setNestedProperty, getSortedMessages } from '../../utils.js';
|
|
3
|
+
import { defineCodec } from '../ExtractorCodec.js';
|
|
4
|
+
|
|
5
|
+
var POCodec = defineCodec(() => {
|
|
6
|
+
// See also https://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html
|
|
7
|
+
const DEFAULT_METADATA = {
|
|
8
|
+
// Recommended by spec
|
|
9
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
10
|
+
'Content-Transfer-Encoding': '8bit',
|
|
11
|
+
// Otherwise other tools might set this
|
|
12
|
+
'X-Generator': 'next-intl',
|
|
13
|
+
// Crowdin defaults to using msgid as source key
|
|
14
|
+
'X-Crowdin-SourceKey': 'msgstr'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Move all parts before the last dot to msgctxt
|
|
18
|
+
const NAMESPACE_SEPARATOR = '.';
|
|
19
|
+
|
|
20
|
+
// Metadata is stored so it can be retained when writing
|
|
21
|
+
const metadataByLocale = new Map();
|
|
22
|
+
return {
|
|
23
|
+
decode(content, context) {
|
|
24
|
+
const catalog = POParser.parse(content);
|
|
25
|
+
if (catalog.meta) {
|
|
26
|
+
metadataByLocale.set(context.locale, catalog.meta);
|
|
27
|
+
}
|
|
28
|
+
const messages = catalog.messages || [];
|
|
29
|
+
return messages.map(msg => {
|
|
30
|
+
const {
|
|
31
|
+
extractedComments,
|
|
32
|
+
msgctxt,
|
|
33
|
+
msgid,
|
|
34
|
+
msgstr,
|
|
35
|
+
...rest
|
|
36
|
+
} = msg;
|
|
37
|
+
if (extractedComments && extractedComments.length > 1) {
|
|
38
|
+
throw new Error(`Multiple extracted comments are not supported. Found ${extractedComments.length} comments for msgid "${msgid}".`);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...rest,
|
|
42
|
+
id: msgctxt ? [msgctxt, msgid].join(NAMESPACE_SEPARATOR) : msgid,
|
|
43
|
+
message: msgstr,
|
|
44
|
+
...(extractedComments && extractedComments.length > 0 && {
|
|
45
|
+
description: extractedComments[0]
|
|
46
|
+
})
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
encode(messages, context) {
|
|
51
|
+
const encodedMessages = getSortedMessages(messages).map(msg => {
|
|
52
|
+
const {
|
|
53
|
+
description,
|
|
54
|
+
id,
|
|
55
|
+
message,
|
|
56
|
+
...rest
|
|
57
|
+
} = msg;
|
|
58
|
+
const lastDotIndex = id.lastIndexOf(NAMESPACE_SEPARATOR);
|
|
59
|
+
const hasNamespace = id.includes(NAMESPACE_SEPARATOR);
|
|
60
|
+
const msgid = hasNamespace ? id.slice(lastDotIndex + NAMESPACE_SEPARATOR.length) : id;
|
|
61
|
+
return {
|
|
62
|
+
msgid,
|
|
63
|
+
msgstr: message,
|
|
64
|
+
...(description && {
|
|
65
|
+
extractedComments: [description]
|
|
66
|
+
}),
|
|
67
|
+
...(hasNamespace && {
|
|
68
|
+
msgctxt: id.slice(0, lastDotIndex)
|
|
69
|
+
}),
|
|
70
|
+
...rest
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
return POParser.serialize({
|
|
74
|
+
meta: {
|
|
75
|
+
Language: context.locale,
|
|
76
|
+
...DEFAULT_METADATA,
|
|
77
|
+
...metadataByLocale.get(context.locale)
|
|
78
|
+
},
|
|
79
|
+
messages: encodedMessages
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
toJSONString(source, context) {
|
|
83
|
+
const parsed = this.decode(source, context);
|
|
84
|
+
const messagesObject = {};
|
|
85
|
+
for (const message of parsed) {
|
|
86
|
+
setNestedProperty(messagesObject, message.id, message.message);
|
|
87
|
+
}
|
|
88
|
+
return JSON.stringify(messagesObject);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export { POCodec as default };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { throwError } from '../../plugin/utils.js';
|
|
3
|
+
|
|
4
|
+
const formats = {
|
|
5
|
+
json: {
|
|
6
|
+
codec: () => import('./codecs/JSONCodec.js'),
|
|
7
|
+
extension: '.json'
|
|
8
|
+
},
|
|
9
|
+
po: {
|
|
10
|
+
codec: () => import('./codecs/POCodec.js'),
|
|
11
|
+
extension: '.po'
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function isBuiltInFormat(format) {
|
|
15
|
+
return typeof format === 'string' && format in formats;
|
|
16
|
+
}
|
|
17
|
+
function getFormatExtension(format) {
|
|
18
|
+
if (isBuiltInFormat(format)) {
|
|
19
|
+
return formats[format].extension;
|
|
20
|
+
} else {
|
|
21
|
+
return format.extension;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function resolveCodec(format, projectRoot) {
|
|
25
|
+
if (isBuiltInFormat(format)) {
|
|
26
|
+
const factory = (await formats[format].codec()).default;
|
|
27
|
+
return factory();
|
|
28
|
+
} else {
|
|
29
|
+
const resolvedPath = path.isAbsolute(format.codec) ? format.codec : path.resolve(projectRoot, format.codec);
|
|
30
|
+
let module;
|
|
31
|
+
try {
|
|
32
|
+
module = await import(resolvedPath);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throwError(`Could not load codec from "${resolvedPath}".\n${error}`);
|
|
35
|
+
}
|
|
36
|
+
const factory = module.default;
|
|
37
|
+
if (!factory || typeof factory !== 'function') {
|
|
38
|
+
throwError(`Codec at "${resolvedPath}" must have a default export returned from \`defineCodec\`.`);
|
|
39
|
+
}
|
|
40
|
+
return factory();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { formats as default, getFormatExtension, resolveCodec };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
class SourceFileFilter {
|
|
4
|
+
static EXTENSIONS = ['ts', 'tsx', 'js', 'jsx'];
|
|
5
|
+
|
|
6
|
+
// Will not be entered, except if explicitly asked for
|
|
7
|
+
// TODO: At some point we should infer these from .gitignore
|
|
8
|
+
static IGNORED_DIRECTORIES = ['node_modules', '.next', '.git'];
|
|
9
|
+
static isSourceFile(filePath) {
|
|
10
|
+
const ext = path.extname(filePath);
|
|
11
|
+
return SourceFileFilter.EXTENSIONS.map(cur => '.' + cur).includes(ext);
|
|
12
|
+
}
|
|
13
|
+
static shouldEnterDirectory(dirPath, srcPaths) {
|
|
14
|
+
const dirName = path.basename(dirPath);
|
|
15
|
+
if (SourceFileFilter.IGNORED_DIRECTORIES.includes(dirName)) {
|
|
16
|
+
return SourceFileFilter.isIgnoredDirectoryExplicitlyIncluded(dirPath, srcPaths);
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
static isIgnoredDirectoryExplicitlyIncluded(ignoredDirPath, srcPaths) {
|
|
21
|
+
return srcPaths.some(srcPath => SourceFileFilter.isWithinPath(srcPath, ignoredDirPath));
|
|
22
|
+
}
|
|
23
|
+
static isWithinPath(targetPath, basePath) {
|
|
24
|
+
const relativePath = path.relative(basePath, targetPath);
|
|
25
|
+
return relativePath === '' || !relativePath.startsWith('..');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { SourceFileFilter as default };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import SourceFileFilter from './SourceFileFilter.js';
|
|
4
|
+
|
|
5
|
+
class SourceFileScanner {
|
|
6
|
+
static async walkSourceFiles(dir, srcPaths, acc = []) {
|
|
7
|
+
const entries = await fs.readdir(dir, {
|
|
8
|
+
withFileTypes: true
|
|
9
|
+
});
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const entryPath = path.join(dir, entry.name);
|
|
12
|
+
if (entry.isDirectory()) {
|
|
13
|
+
if (!SourceFileFilter.shouldEnterDirectory(entryPath, srcPaths)) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
await SourceFileScanner.walkSourceFiles(entryPath, srcPaths, acc);
|
|
17
|
+
} else {
|
|
18
|
+
if (SourceFileFilter.isSourceFile(entry.name)) {
|
|
19
|
+
acc.push(entryPath);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return acc;
|
|
24
|
+
}
|
|
25
|
+
static async getSourceFiles(srcPaths) {
|
|
26
|
+
const files = (await Promise.all(srcPaths.map(srcPath => SourceFileScanner.walkSourceFiles(srcPath, srcPaths)))).flat();
|
|
27
|
+
return new Set(files);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { SourceFileScanner as default };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { subscribe } from '@parcel/watcher';
|
|
4
|
+
import SourceFileFilter from './SourceFileFilter.js';
|
|
5
|
+
import SourceFileScanner from './SourceFileScanner.js';
|
|
6
|
+
|
|
7
|
+
class SourceFileWatcher {
|
|
8
|
+
subscriptions = [];
|
|
9
|
+
constructor(roots, onChange) {
|
|
10
|
+
this.roots = roots;
|
|
11
|
+
this.onChange = onChange;
|
|
12
|
+
}
|
|
13
|
+
async start() {
|
|
14
|
+
if (this.subscriptions.length > 0) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const ignore = SourceFileFilter.IGNORED_DIRECTORIES.map(dir => `**/${dir}/**`);
|
|
18
|
+
for (const root of this.roots) {
|
|
19
|
+
const sub = await subscribe(root, async (err, events) => {
|
|
20
|
+
if (err) {
|
|
21
|
+
console.error(err);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const filtered = await this.normalizeEvents(events);
|
|
25
|
+
if (filtered.length > 0) {
|
|
26
|
+
void this.onChange(filtered);
|
|
27
|
+
}
|
|
28
|
+
}, {
|
|
29
|
+
ignore
|
|
30
|
+
});
|
|
31
|
+
this.subscriptions.push(sub);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async normalizeEvents(events) {
|
|
35
|
+
const directoryCreatePaths = [];
|
|
36
|
+
const otherEvents = [];
|
|
37
|
+
|
|
38
|
+
// We need to expand directory creates because during rename operations,
|
|
39
|
+
// @parcel/watcher emits a directory create event but may not emit individual
|
|
40
|
+
// file events for the moved files
|
|
41
|
+
await Promise.all(events.map(async event => {
|
|
42
|
+
if (event.type === 'create') {
|
|
43
|
+
try {
|
|
44
|
+
const stats = await fs.stat(event.path);
|
|
45
|
+
if (stats.isDirectory()) {
|
|
46
|
+
directoryCreatePaths.push(event.path);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// Path doesn't exist or is inaccessible, treat as file
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
otherEvents.push(event);
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
// Expand directory create events to find source files inside
|
|
57
|
+
let expandedCreateEvents = [];
|
|
58
|
+
if (directoryCreatePaths.length > 0) {
|
|
59
|
+
try {
|
|
60
|
+
const sourceFiles = await SourceFileScanner.getSourceFiles(directoryCreatePaths);
|
|
61
|
+
expandedCreateEvents = Array.from(sourceFiles).map(filePath => ({
|
|
62
|
+
type: 'create',
|
|
63
|
+
path: filePath
|
|
64
|
+
}));
|
|
65
|
+
} catch {
|
|
66
|
+
// Directories might have been deleted or are inaccessible
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Combine original events with expanded directory creates.
|
|
71
|
+
// Deduplicate by path to avoid processing the same file twice
|
|
72
|
+
// in case @parcel/watcher also emitted individual file events.
|
|
73
|
+
const allEvents = [...otherEvents, ...expandedCreateEvents];
|
|
74
|
+
const seenPaths = new Set();
|
|
75
|
+
const deduplicated = [];
|
|
76
|
+
for (const event of allEvents) {
|
|
77
|
+
const key = `${event.type}:${event.path}`;
|
|
78
|
+
if (!seenPaths.has(key)) {
|
|
79
|
+
seenPaths.add(key);
|
|
80
|
+
deduplicated.push(event);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return deduplicated.filter(event => {
|
|
84
|
+
// Keep all delete events (might be deleted directories that no longer exist)
|
|
85
|
+
if (event.type === 'delete') {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
// Keep source files
|
|
89
|
+
return SourceFileFilter.isSourceFile(event.path);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async expandDirectoryDeleteEvents(events, prevKnownFiles) {
|
|
93
|
+
const expanded = [];
|
|
94
|
+
for (const event of events) {
|
|
95
|
+
if (event.type === 'delete' && !SourceFileFilter.isSourceFile(event.path)) {
|
|
96
|
+
const dirPath = path.resolve(event.path);
|
|
97
|
+
const filesInDirectory = [];
|
|
98
|
+
for (const filePath of prevKnownFiles) {
|
|
99
|
+
if (SourceFileFilter.isWithinPath(filePath, dirPath)) {
|
|
100
|
+
filesInDirectory.push(filePath);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If we found files within this path, it was a directory
|
|
105
|
+
if (filesInDirectory.length > 0) {
|
|
106
|
+
for (const filePath of filesInDirectory) {
|
|
107
|
+
expanded.push({
|
|
108
|
+
type: 'delete',
|
|
109
|
+
path: filePath
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
// Not a directory or no files in it, pass through as-is
|
|
114
|
+
expanded.push(event);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
// Pass through as-is
|
|
118
|
+
expanded.push(event);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return expanded;
|
|
122
|
+
}
|
|
123
|
+
async stop() {
|
|
124
|
+
await Promise.all(this.subscriptions.map(sub => sub.unsubscribe()));
|
|
125
|
+
this.subscriptions = [];
|
|
126
|
+
}
|
|
127
|
+
[Symbol.dispose]() {
|
|
128
|
+
void this.stop();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { SourceFileWatcher as default };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
function normalizePathToPosix(filePath) {
|
|
4
|
+
// `path.relative` uses OS-specific separators. For stable `.po` references we
|
|
5
|
+
// always use POSIX separators, regardless of the OS that ran extraction.
|
|
6
|
+
return path.posix.normalize(filePath.split(path.win32.sep).join(path.posix.sep));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Essentialls lodash/set, but we avoid this dependency
|
|
10
|
+
function setNestedProperty(obj, keyPath, value) {
|
|
11
|
+
const keys = keyPath.split('.');
|
|
12
|
+
let current = obj;
|
|
13
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
14
|
+
const key = keys[i];
|
|
15
|
+
if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
|
|
16
|
+
current[key] = {};
|
|
17
|
+
}
|
|
18
|
+
current = current[key];
|
|
19
|
+
}
|
|
20
|
+
current[keys[keys.length - 1]] = value;
|
|
21
|
+
}
|
|
22
|
+
function getSortedMessages(messages) {
|
|
23
|
+
return messages.toSorted((messageA, messageB) => {
|
|
24
|
+
const refA = messageA.references?.[0];
|
|
25
|
+
const refB = messageB.references?.[0];
|
|
26
|
+
|
|
27
|
+
// No references: preserve original (extraction) order
|
|
28
|
+
if (!refA || !refB) return 0;
|
|
29
|
+
|
|
30
|
+
// Sort by path, then line. Same path+line: preserve original order
|
|
31
|
+
return compareReferences(refA, refB);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function localeCompare(a, b) {
|
|
35
|
+
return a.localeCompare(b, 'en');
|
|
36
|
+
}
|
|
37
|
+
function compareReferences(refA, refB) {
|
|
38
|
+
const pathCompare = localeCompare(refA.path, refB.path);
|
|
39
|
+
if (pathCompare !== 0) return pathCompare;
|
|
40
|
+
return (refA.line ?? 0) - (refB.line ?? 0);
|
|
41
|
+
}
|
|
42
|
+
function getDefaultProjectRoot() {
|
|
43
|
+
return process.cwd();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { compareReferences, getDefaultProjectRoot, getSortedMessages, localeCompare, normalizePathToPosix, setNestedProperty };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { useFormatter, useTranslations } from './react-client/index.js';
|
|
2
|
+
export { default as NextIntlClientProvider } from './shared/NextIntlClientProvider.js';
|
|
3
|
+
export { IntlProvider, _useExtracted as useExtracted, useLocale, useMessages, useNow, useTimeZone } from 'use-intl/react';
|
|
4
|
+
export * from 'use-intl/core';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as useLocale } from './react-server/useLocale.js';
|
|
2
|
+
export { default as useTranslations } from './react-server/useTranslations.js';
|
|
3
|
+
export { default as useFormatter } from './react-server/useFormatter.js';
|
|
4
|
+
export { default as useNow } from './react-server/useNow.js';
|
|
5
|
+
export { default as useTimeZone } from './react-server/useTimeZone.js';
|
|
6
|
+
export { default as useMessages } from './react-server/useMessages.js';
|
|
7
|
+
export { default as NextIntlClientProvider } from './react-server/NextIntlClientProviderServer.js';
|
|
8
|
+
export { default as useExtracted } from './react-server/useExtracted.js';
|
|
9
|
+
export * from 'use-intl/core';
|