@astroscope/i18n 3.0.0 → 3.0.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.
- package/README.md +1 -1
- package/dist/{babel-plugin-DJZS9L_W.js → babel-plugin-CRgz4mri.js} +2 -3
- package/dist/{babel-plugin-DJZS9L_W.js.map → babel-plugin-CRgz4mri.js.map} +1 -1
- package/dist/client/translate.d.ts +2 -4
- package/dist/client/translate.d.ts.map +1 -1
- package/dist/client/translate.js +4 -6
- package/dist/client/translate.js.map +1 -1
- package/dist/{compiler-CkOVGWU5.js → compiler-K3uHKoFs.js} +2 -3
- package/dist/{compiler-CkOVGWU5.js.map → compiler-K3uHKoFs.js.map} +1 -1
- package/dist/{context-BCUJxb8p.js → context-CJvTEa-I.js} +2 -3
- package/dist/{context-BCUJxb8p.js.map → context-CJvTEa-I.js.map} +1 -1
- package/dist/extraction/index.d.ts +2 -6
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +2 -2
- package/dist/{i18n-C4nlkQTf.d.ts → i18n-7dnqpAwm.d.ts} +3 -7
- package/dist/i18n-7dnqpAwm.d.ts.map +1 -0
- package/dist/{i18n-C47eQi8U.js → i18n-f7qdMTwm.js} +5 -7
- package/dist/{i18n-C47eQi8U.js.map → i18n-f7qdMTwm.js.map} +1 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -4
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -2
- package/dist/{manifest-X-UhW6tH.js → manifest-DK513NbQ.js} +2 -3
- package/dist/{manifest-X-UhW6tH.js.map → manifest-DK513NbQ.js.map} +1 -1
- package/dist/{middleware-BSEtDRYy.js → middleware-CNXfXefr.js} +8 -11
- package/dist/{middleware-BSEtDRYy.js.map → middleware-CNXfXefr.js.map} +1 -1
- package/dist/{rich-BDqjhv2h.js → rich-BbDoHrd9.js} +3 -5
- package/dist/{rich-BDqjhv2h.js.map → rich-BbDoHrd9.js.map} +1 -1
- package/dist/{rich-BWszYAzS.d.ts → rich-CavnJlKg.d.ts} +1 -3
- package/dist/rich-CavnJlKg.d.ts.map +1 -0
- package/dist/server/chunk-middleware-entrypoint.d.ts +0 -2
- package/dist/server/chunk-middleware-entrypoint.d.ts.map +1 -1
- package/dist/server/chunk-middleware-entrypoint.js +2 -3
- package/dist/server/locale-middleware-entrypoint.d.ts +0 -2
- package/dist/server/locale-middleware-entrypoint.d.ts.map +1 -1
- package/dist/server/locale-middleware-entrypoint.js +2 -3
- package/dist/server/translate.d.ts +2 -4
- package/dist/server/translate.d.ts.map +1 -1
- package/dist/server/translate.js +5 -6
- package/dist/{types-BjTD5hUT.d.ts → types-DYnR9xP3.d.ts} +1 -3
- package/dist/types-DYnR9xP3.d.ts.map +1 -0
- package/dist/{types-BC-tQ30r.d.ts → types-DwM0qsLj.d.ts} +2 -4
- package/dist/types-DwM0qsLj.d.ts.map +1 -0
- package/dist/{types-LT4Zo0bM.d.ts → types-ioAI5pu0.d.ts} +1 -3
- package/dist/types-ioAI5pu0.d.ts.map +1 -0
- package/dist/{url-CifeaDzl.js → url-6HS_IPPA.js} +2 -3
- package/dist/{url-CifeaDzl.js.map → url-6HS_IPPA.js.map} +1 -1
- package/dist/{vite-plugin-CZqtAqQM.js → vite-plugin-Yc5NP6bs.js} +11 -15
- package/dist/vite-plugin-Yc5NP6bs.js.map +1 -0
- package/package.json +11 -11
- package/dist/i18n-C4nlkQTf.d.ts.map +0 -1
- package/dist/rich-BWszYAzS.d.ts.map +0 -1
- package/dist/types-BC-tQ30r.d.ts.map +0 -1
- package/dist/types-BjTD5hUT.d.ts.map +0 -1
- package/dist/types-LT4Zo0bM.d.ts.map +0 -1
- package/dist/vite-plugin-CZqtAqQM.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware-BSEtDRYy.js","names":[],"sources":["../src/server/client-state.ts","../src/server/islands-emitter.ts","../src/server/middleware.ts"],"sourcesContent":["import { i18n } from './i18n.js';\n\n/**\n * Scripts bootstrapping `window.__i18n__` on html responses, delivered through the\n * `@astroscope/node` document emitter (see islands-emitter.ts for the placement\n * policy). Both writers merge orderlessly (`??=` + assign), so emitter scripts and\n * the bootstrap can arrive in either order.\n */\n\n/** translation values may contain `</script>` — escape `<` so they cannot end the tag */\nexport function jsonForScript(value: unknown): string {\n return JSON.stringify(value).replaceAll('<', String.raw`\\u003c`);\n}\n\n/** the full dev-mode state: every translation for the locale */\nexport function createFullStateScript(locale: string): string {\n const translations = i18n.getTranslations(locale);\n const state = jsonForScript({ locale, hashes: {}, translations });\n\n return `<script>window.__i18n__=Object.assign(window.__i18n__??{hashes:{},translations:{}},${state});</script>`;\n}\n\n/** the prod bootstrap: chunk hashes for astro `<script>` entry consumers */\nexport function createScriptHashesScript(locale: string): string | null {\n const hashes = i18n.getScriptChunkHashes(locale);\n\n if (Object.keys(hashes).length === 0) {\n return null;\n }\n\n const init = jsonForScript({ locale, hashes: {}, translations: {} });\n\n return `<script>{const i=window.__i18n__??=${init};Object.assign(i.hashes,${jsonForScript(hashes)});}</script>`;\n}\n","import {\n type DocumentEmitter,\n type IslandInfo,\n registerDocumentEmitter,\n registerIslandEmitter,\n} from '@astroscope/node/islands';\nimport type { APIContext } from 'astro';\nimport { buildI18nChunkUrl } from '../shared/url.js';\nimport { createFullStateScript, createScriptHashesScript, jsonForScript } from './client-state.js';\nimport { i18n } from './i18n.js';\n\n/**\n * Contributes the i18n slice for each island the `@astroscope/node` islands\n * middleware streams past:\n *\n * - an inline merge script with the hashes of the island's full chunk closure —\n * dynamic imports included, since a `React.lazy` chunk's injected loader reads\n * its hash at execution time. Emitted before the island tag, it is parsed\n * strictly before the island connects, and the island's chunks execute at\n * least a network round-trip later — so each island's hashes win their race\n * even while the rest of the document is still streaming. Chunks already\n * covered earlier in the document are skipped.\n * - translation-chunk preload links for the static closure (link tags on\n * immediate islands).\n * - translation-chunk eager imports for the full closure, dynamic chunks\n * included, fired by the deferred gate: the chunks are idempotent data\n * modules, so early evaluation is free and the loader's awaited import hits\n * the module cache instead of fetching after the component graph evaluated.\n * No per-document dedup — a shared chunk is covered by whichever island's\n * gate fires first.\n *\n * The locale is recorded per request by the i18n middleware — the emitter runs\n * while the response streams, outside the middleware's AsyncLocalStorage scope.\n * It is keyed on `context.locals`, the one object astro keeps for the whole\n * request: `context.request` is replaced by every `next(url)` rewrite, once per\n * nesting level of `sequence`, so the request the middleware saw is not the one\n * the islands middleware holds when the emitters run.\n */\n\nconst requestLocales = new WeakMap<object, string>();\nconst emittedByRequest = new WeakMap<object, Set<string>>();\n\n// vite defines import.meta.env when bundling the middleware; typed structurally\n// so the package compiles without vite's global client types\nconst IS_DEV = !!(import.meta as { env?: { DEV?: boolean } }).env?.DEV;\n\nlet registered = false;\n\nexport function setRequestLocale(context: APIContext, locale: string): void {\n requestLocales.set(context.locals, locale);\n}\n\n/** public url of a chunk → manifest chunk name, e.g. `…/_astro/Cart.abc.js` → `Cart.abc` */\nfunction urlToChunkName(url: string): string {\n return url.slice(url.lastIndexOf('/') + 1).replace(/\\.js$/, '');\n}\n\n/** an island's translated chunks and their urls for one locale */\ntype IslandSlice = {\n /** full closure: what the merge script covers */\n chunks: [name: string, hash: string][];\n /** static closure: preload links */\n links: string[];\n /** full closure: eager imports */\n imports: string[];\n};\n\n// islands arrive as the same object per component/renderer/directive and the\n// hashes object is replaced whenever translations change, so keying on both\n// caches each slice for exactly as long as it is valid\nconst slices = new WeakMap<Record<string, string>, WeakMap<IslandInfo, IslandSlice>>();\nconst inits = new Map<string, string>();\n\nfunction sliceFor(island: IslandInfo, locale: string, hashes: Record<string, string>): IslandSlice {\n let byIsland = slices.get(hashes);\n\n if (!byIsland) {\n byIsland = new WeakMap();\n slices.set(hashes, byIsland);\n }\n\n let slice = byIsland.get(island);\n\n if (slice) {\n return slice;\n }\n\n const translated = (urls: string[]): [string, string][] => {\n const result: [string, string][] = [];\n\n for (const url of urls) {\n const chunk = urlToChunkName(url);\n const hash = hashes[chunk];\n\n if (hash) {\n result.push([chunk, hash]);\n }\n }\n\n return result;\n };\n\n const chunks = translated(island.fullClosure);\n\n slice = {\n chunks,\n links: translated(island.staticClosure).map(([chunk, hash]) => buildI18nChunkUrl(locale, chunk, hash)),\n imports: chunks.map(([chunk, hash]) => buildI18nChunkUrl(locale, chunk, hash)),\n };\n byIsland.set(island, slice);\n\n return slice;\n}\n\nfunction initFor(locale: string): string {\n let init = inits.get(locale);\n\n if (init === undefined) {\n init = jsonForScript({ locale, hashes: {}, translations: {} });\n inits.set(locale, init);\n }\n\n return init;\n}\n\n/**\n * The document-level bootstrap for `window.__i18n__`, exported for tests.\n */\nexport function createI18nDocumentEmitter(dev: boolean = IS_DEV): DocumentEmitter {\n return (context) => {\n if (!i18n.isConfigured()) {\n return null;\n }\n\n const locale = requestLocales.get(context.locals);\n\n if (locale === undefined) {\n return null;\n }\n\n if (dev) {\n return { head: createFullStateScript(locale) };\n }\n\n if (Object.keys(i18n.getManifest().chunks).length === 0) {\n return null;\n }\n\n return { end: () => createScriptHashesScript(locale) };\n };\n}\n\nexport function registerI18nEmitters(): void {\n if (registered) {\n return;\n }\n\n registered = true;\n\n registerDocumentEmitter(createI18nDocumentEmitter());\n\n registerIslandEmitter((island, context) => {\n // no request context (prerendered build pass) — the locale is unknowable\n if (!context || !i18n.isConfigured()) {\n return null;\n }\n\n const locale = requestLocales.get(context.locals);\n\n if (locale === undefined) {\n return null;\n }\n\n const hashes = i18n.getHashes(locale);\n const { chunks, links, imports } = sliceFor(island, locale, hashes);\n\n let emitted = emittedByRequest.get(context.locals);\n\n if (!emitted) {\n emitted = new Set();\n emittedByRequest.set(context.locals, emitted);\n }\n\n const merge: Record<string, string> = {};\n let fresh = false;\n\n for (const [chunk, hash] of chunks) {\n if (emitted.has(chunk)) {\n continue;\n }\n\n emitted.add(chunk);\n merge[chunk] = hash;\n fresh = true;\n }\n\n const html = fresh\n ? `<script>{const i=window.__i18n__??=${initFor(locale)};Object.assign(i.hashes,${jsonForScript(merge)});}</script>`\n : undefined;\n\n if (!html && links.length === 0 && imports.length === 0) {\n return null;\n }\n\n return {\n html,\n links: links.length > 0 ? links : undefined,\n imports: imports.length > 0 ? imports : undefined,\n };\n });\n}\n","import { type ExcludePattern, RECOMMENDED_EXCLUDES, shouldExclude } from '@astroscope/node/excludes';\nimport { log, overrideRequestRoute } from '@astroscope/node/log';\nimport type { MiddlewareHandler } from 'astro';\nimport { runWithContext } from './context.js';\nimport { i18n } from './i18n.js';\nimport { registerI18nEmitters, setRequestLocale } from './islands-emitter.js';\nimport type { I18nContext } from './types.js';\n\nexport type I18nLocaleMiddlewareOptions = {\n /** patterns to skip locale context setup for; defaults to RECOMMENDED_EXCLUDES */\n exclude?: ExcludePattern[] | undefined;\n};\n\nconst I18N_ENDPOINT_PREFIX = '/_i18n/';\n\n// astro has no route behind the chunk endpoint, so requests would otherwise be\n// logged and measured under whatever routing matched — typically `/404`\nconst I18N_ENDPOINT_ROUTE = '/_i18n/[locale]/[chunk]';\n\n/**\n * Serve a translation chunk. Returns `undefined` when the path isn't a chunk\n * request, and the caller passes it on to astro.\n */\nfunction createChunkResponse(pathname: string): Response | undefined {\n if (!pathname.startsWith(I18N_ENDPOINT_PREFIX)) {\n return undefined;\n }\n\n if (!i18n.isConfigured()) {\n log.error('i18n not configured, passing through');\n\n return undefined;\n }\n\n const path = pathname.slice(I18N_ENDPOINT_PREFIX.length);\n\n // {locale}/{chunkName}.{hash}.js — sliced by hand, this runs for every chunk request\n const slashIdx = path.indexOf('/');\n\n if (slashIdx === -1) {\n return undefined;\n }\n\n const locale = path.slice(0, slashIdx);\n\n // validate locale against configured locales to prevent arbitrary locale injection\n const config = i18n.getConfig();\n\n if (!config.locales.includes(locale)) {\n return new Response('/* unknown locale */', {\n status: 404,\n headers: { 'Content-Type': 'application/javascript' },\n });\n }\n\n const rest = path.slice(slashIdx + 1);\n\n if (!rest.endsWith('.js')) {\n return undefined;\n }\n\n const withoutJs = rest.slice(0, -3);\n const lastDotIdx = withoutJs.lastIndexOf('.');\n\n if (lastDotIdx === -1) {\n return undefined;\n }\n\n const chunkName = withoutJs.slice(0, lastDotIdx);\n const body = i18n.getChunkBody(locale, chunkName);\n\n if (!body) {\n return new Response(`/* chunk not found: ${chunkName} */`, {\n status: 404,\n headers: { 'Content-Type': 'application/javascript' },\n });\n }\n\n return new Response(body as BodyInit, {\n status: 200,\n headers: {\n 'Content-Type': 'text/javascript; charset=utf-8',\n 'Content-Length': String(body.byteLength),\n 'Cache-Control': 'public, max-age=31536000, immutable',\n },\n });\n}\n\n/**\n * Create the i18n chunk middleware that serves translation chunks at `/_i18n/` endpoints.\n *\n * Injected by the integration as `pre` middleware (before any user middleware) —\n * not part of the public API.\n *\n * @internal\n */\nexport function createI18nChunkMiddleware(): MiddlewareHandler {\n return (ctx, next) => {\n const response = createChunkResponse(ctx.url.pathname);\n\n if (!response) {\n return next();\n }\n\n // this middleware, not astro's routing, is what serves the request\n overrideRequestRoute(I18N_ENDPOINT_ROUTE);\n\n return response;\n };\n}\n\n/**\n * Create the i18n locale middleware.\n *\n * Resolves the request's locale through the resolver from `i18n.configure({ locale })`\n * (the default locale when there is none or it returns `undefined`), sets up the\n * request context for `t()`, records the locale for the islands emitter, which\n * delivers the client state, and runs the rest of the chain inside that context.\n *\n * Injected by the integration as `post` middleware, after the project's own, so the\n * resolver sees whatever a session middleware put on `locals` — not part of the\n * public API.\n *\n * @internal\n */\nexport function createI18nLocaleMiddleware(options: I18nLocaleMiddlewareOptions = {}): MiddlewareHandler {\n registerI18nEmitters();\n\n const exclude = options.exclude ?? RECOMMENDED_EXCLUDES;\n\n return (ctx, next) => {\n if (shouldExclude(ctx, exclude)) {\n return next();\n }\n\n if (!i18n.isConfigured()) {\n log.error('i18n not configured, passing through');\n\n return next();\n }\n\n const config = i18n.getConfig();\n const locale = config.locale?.(ctx) ?? config.defaultLocale;\n\n // the islands emitter runs while the response streams, outside this ALS scope\n setRequestLocale(ctx, locale);\n\n const context: I18nContext = {\n locale,\n translations: i18n.getCompiledTranslations(locale),\n rawTranslations: i18n.getTranslations(locale),\n fallback: config.fallback,\n };\n\n return runWithContext(context, () => next());\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"middleware-CNXfXefr.js","names":[],"sources":["../src/server/client-state.ts","../src/server/islands-emitter.ts","../src/server/middleware.ts"],"sourcesContent":["import { i18n } from './i18n.js';\n\n/**\n * Scripts bootstrapping `window.__i18n__` on html responses, delivered through the\n * `@astroscope/node` document emitter (see islands-emitter.ts for the placement\n * policy). Both writers merge orderlessly (`??=` + assign), so emitter scripts and\n * the bootstrap can arrive in either order.\n */\n\n/** translation values may contain `</script>` — escape `<` so they cannot end the tag */\nexport function jsonForScript(value: unknown): string {\n return JSON.stringify(value).replaceAll('<', String.raw`\\u003c`);\n}\n\n/** the full dev-mode state: every translation for the locale */\nexport function createFullStateScript(locale: string): string {\n const translations = i18n.getTranslations(locale);\n const state = jsonForScript({ locale, hashes: {}, translations });\n\n return `<script>window.__i18n__=Object.assign(window.__i18n__??{hashes:{},translations:{}},${state});</script>`;\n}\n\n/** the prod bootstrap: chunk hashes for astro `<script>` entry consumers */\nexport function createScriptHashesScript(locale: string): string | null {\n const hashes = i18n.getScriptChunkHashes(locale);\n\n if (Object.keys(hashes).length === 0) {\n return null;\n }\n\n const init = jsonForScript({ locale, hashes: {}, translations: {} });\n\n return `<script>{const i=window.__i18n__??=${init};Object.assign(i.hashes,${jsonForScript(hashes)});}</script>`;\n}\n","import {\n type DocumentEmitter,\n type IslandInfo,\n registerDocumentEmitter,\n registerIslandEmitter,\n} from '@astroscope/node/islands';\nimport type { APIContext } from 'astro';\nimport { buildI18nChunkUrl } from '../shared/url.js';\nimport { createFullStateScript, createScriptHashesScript, jsonForScript } from './client-state.js';\nimport { i18n } from './i18n.js';\n\n/**\n * Contributes the i18n slice for each island the `@astroscope/node` islands\n * middleware streams past:\n *\n * - an inline merge script with the hashes of the island's full chunk closure —\n * dynamic imports included, since a `React.lazy` chunk's injected loader reads\n * its hash at execution time. Emitted before the island tag, it is parsed\n * strictly before the island connects, and the island's chunks execute at\n * least a network round-trip later — so each island's hashes win their race\n * even while the rest of the document is still streaming. Chunks already\n * covered earlier in the document are skipped.\n * - translation-chunk preload links for the static closure (link tags on\n * immediate islands).\n * - translation-chunk eager imports for the full closure, dynamic chunks\n * included, fired by the deferred gate: the chunks are idempotent data\n * modules, so early evaluation is free and the loader's awaited import hits\n * the module cache instead of fetching after the component graph evaluated.\n * No per-document dedup — a shared chunk is covered by whichever island's\n * gate fires first.\n *\n * The locale is recorded per request by the i18n middleware — the emitter runs\n * while the response streams, outside the middleware's AsyncLocalStorage scope.\n * It is keyed on `context.locals`, the one object astro keeps for the whole\n * request: `context.request` is replaced by every `next(url)` rewrite, once per\n * nesting level of `sequence`, so the request the middleware saw is not the one\n * the islands middleware holds when the emitters run.\n */\n\nconst requestLocales = new WeakMap<object, string>();\nconst emittedByRequest = new WeakMap<object, Set<string>>();\n\n// vite defines import.meta.env when bundling the middleware; typed structurally\n// so the package compiles without vite's global client types\nconst IS_DEV = !!(import.meta as { env?: { DEV?: boolean } }).env?.DEV;\n\nlet registered = false;\n\nexport function setRequestLocale(context: APIContext, locale: string): void {\n requestLocales.set(context.locals, locale);\n}\n\n/** public url of a chunk → manifest chunk name, e.g. `…/_astro/Cart.abc.js` → `Cart.abc` */\nfunction urlToChunkName(url: string): string {\n return url.slice(url.lastIndexOf('/') + 1).replace(/\\.js$/, '');\n}\n\n/** an island's translated chunks and their urls for one locale */\ntype IslandSlice = {\n /** full closure: what the merge script covers */\n chunks: [name: string, hash: string][];\n /** static closure: preload links */\n links: string[];\n /** full closure: eager imports */\n imports: string[];\n};\n\n// islands arrive as the same object per component/renderer/directive and the\n// hashes object is replaced whenever translations change, so keying on both\n// caches each slice for exactly as long as it is valid\nconst slices = new WeakMap<Record<string, string>, WeakMap<IslandInfo, IslandSlice>>();\nconst inits = new Map<string, string>();\n\nfunction sliceFor(island: IslandInfo, locale: string, hashes: Record<string, string>): IslandSlice {\n let byIsland = slices.get(hashes);\n\n if (!byIsland) {\n byIsland = new WeakMap();\n slices.set(hashes, byIsland);\n }\n\n let slice = byIsland.get(island);\n\n if (slice) {\n return slice;\n }\n\n const translated = (urls: string[]): [string, string][] => {\n const result: [string, string][] = [];\n\n for (const url of urls) {\n const chunk = urlToChunkName(url);\n const hash = hashes[chunk];\n\n if (hash) {\n result.push([chunk, hash]);\n }\n }\n\n return result;\n };\n\n const chunks = translated(island.fullClosure);\n\n slice = {\n chunks,\n links: translated(island.staticClosure).map(([chunk, hash]) => buildI18nChunkUrl(locale, chunk, hash)),\n imports: chunks.map(([chunk, hash]) => buildI18nChunkUrl(locale, chunk, hash)),\n };\n byIsland.set(island, slice);\n\n return slice;\n}\n\nfunction initFor(locale: string): string {\n let init = inits.get(locale);\n\n if (init === undefined) {\n init = jsonForScript({ locale, hashes: {}, translations: {} });\n inits.set(locale, init);\n }\n\n return init;\n}\n\n/**\n * The document-level bootstrap for `window.__i18n__`, exported for tests.\n */\nexport function createI18nDocumentEmitter(dev: boolean = IS_DEV): DocumentEmitter {\n return (context) => {\n if (!i18n.isConfigured()) {\n return null;\n }\n\n const locale = requestLocales.get(context.locals);\n\n if (locale === undefined) {\n return null;\n }\n\n if (dev) {\n return { head: createFullStateScript(locale) };\n }\n\n if (Object.keys(i18n.getManifest().chunks).length === 0) {\n return null;\n }\n\n return { end: () => createScriptHashesScript(locale) };\n };\n}\n\nexport function registerI18nEmitters(): void {\n if (registered) {\n return;\n }\n\n registered = true;\n\n registerDocumentEmitter(createI18nDocumentEmitter());\n\n registerIslandEmitter((island, context) => {\n // no request context (prerendered build pass) — the locale is unknowable\n if (!context || !i18n.isConfigured()) {\n return null;\n }\n\n const locale = requestLocales.get(context.locals);\n\n if (locale === undefined) {\n return null;\n }\n\n const hashes = i18n.getHashes(locale);\n const { chunks, links, imports } = sliceFor(island, locale, hashes);\n\n let emitted = emittedByRequest.get(context.locals);\n\n if (!emitted) {\n emitted = new Set();\n emittedByRequest.set(context.locals, emitted);\n }\n\n const merge: Record<string, string> = {};\n let fresh = false;\n\n for (const [chunk, hash] of chunks) {\n if (emitted.has(chunk)) {\n continue;\n }\n\n emitted.add(chunk);\n merge[chunk] = hash;\n fresh = true;\n }\n\n const html = fresh\n ? `<script>{const i=window.__i18n__??=${initFor(locale)};Object.assign(i.hashes,${jsonForScript(merge)});}</script>`\n : undefined;\n\n if (!html && links.length === 0 && imports.length === 0) {\n return null;\n }\n\n return {\n html,\n links: links.length > 0 ? links : undefined,\n imports: imports.length > 0 ? imports : undefined,\n };\n });\n}\n","import { type ExcludePattern, RECOMMENDED_EXCLUDES, shouldExclude } from '@astroscope/node/excludes';\nimport { log, overrideRequestRoute } from '@astroscope/node/log';\nimport type { MiddlewareHandler } from 'astro';\nimport { runWithContext } from './context.js';\nimport { i18n } from './i18n.js';\nimport { registerI18nEmitters, setRequestLocale } from './islands-emitter.js';\nimport type { I18nContext } from './types.js';\n\nexport type I18nLocaleMiddlewareOptions = {\n /** patterns to skip locale context setup for; defaults to RECOMMENDED_EXCLUDES */\n exclude?: ExcludePattern[] | undefined;\n};\n\nconst I18N_ENDPOINT_PREFIX = '/_i18n/';\n\n// astro has no route behind the chunk endpoint, so requests would otherwise be\n// logged and measured under whatever routing matched — typically `/404`\nconst I18N_ENDPOINT_ROUTE = '/_i18n/[locale]/[chunk]';\n\n/**\n * Serve a translation chunk. Returns `undefined` when the path isn't a chunk\n * request, and the caller passes it on to astro.\n */\nfunction createChunkResponse(pathname: string): Response | undefined {\n if (!pathname.startsWith(I18N_ENDPOINT_PREFIX)) {\n return undefined;\n }\n\n if (!i18n.isConfigured()) {\n log.error('i18n not configured, passing through');\n\n return undefined;\n }\n\n const path = pathname.slice(I18N_ENDPOINT_PREFIX.length);\n\n // {locale}/{chunkName}.{hash}.js — sliced by hand, this runs for every chunk request\n const slashIdx = path.indexOf('/');\n\n if (slashIdx === -1) {\n return undefined;\n }\n\n const locale = path.slice(0, slashIdx);\n\n // validate locale against configured locales to prevent arbitrary locale injection\n const config = i18n.getConfig();\n\n if (!config.locales.includes(locale)) {\n return new Response('/* unknown locale */', {\n status: 404,\n headers: { 'Content-Type': 'application/javascript' },\n });\n }\n\n const rest = path.slice(slashIdx + 1);\n\n if (!rest.endsWith('.js')) {\n return undefined;\n }\n\n const withoutJs = rest.slice(0, -3);\n const lastDotIdx = withoutJs.lastIndexOf('.');\n\n if (lastDotIdx === -1) {\n return undefined;\n }\n\n const chunkName = withoutJs.slice(0, lastDotIdx);\n const body = i18n.getChunkBody(locale, chunkName);\n\n if (!body) {\n return new Response(`/* chunk not found: ${chunkName} */`, {\n status: 404,\n headers: { 'Content-Type': 'application/javascript' },\n });\n }\n\n return new Response(body as BodyInit, {\n status: 200,\n headers: {\n 'Content-Type': 'text/javascript; charset=utf-8',\n 'Content-Length': String(body.byteLength),\n 'Cache-Control': 'public, max-age=31536000, immutable',\n },\n });\n}\n\n/**\n * Create the i18n chunk middleware that serves translation chunks at `/_i18n/` endpoints.\n *\n * Injected by the integration as `pre` middleware (before any user middleware) —\n * not part of the public API.\n *\n * @internal\n */\nexport function createI18nChunkMiddleware(): MiddlewareHandler {\n return (ctx, next) => {\n const response = createChunkResponse(ctx.url.pathname);\n\n if (!response) {\n return next();\n }\n\n // this middleware, not astro's routing, is what serves the request\n overrideRequestRoute(I18N_ENDPOINT_ROUTE);\n\n return response;\n };\n}\n\n/**\n * Create the i18n locale middleware.\n *\n * Resolves the request's locale through the resolver from `i18n.configure({ locale })`\n * (the default locale when there is none or it returns `undefined`), sets up the\n * request context for `t()`, records the locale for the islands emitter, which\n * delivers the client state, and runs the rest of the chain inside that context.\n *\n * Injected by the integration as `post` middleware, after the project's own, so the\n * resolver sees whatever a session middleware put on `locals` — not part of the\n * public API.\n *\n * @internal\n */\nexport function createI18nLocaleMiddleware(options: I18nLocaleMiddlewareOptions = {}): MiddlewareHandler {\n registerI18nEmitters();\n\n const exclude = options.exclude ?? RECOMMENDED_EXCLUDES;\n\n return (ctx, next) => {\n if (shouldExclude(ctx, exclude)) {\n return next();\n }\n\n if (!i18n.isConfigured()) {\n log.error('i18n not configured, passing through');\n\n return next();\n }\n\n const config = i18n.getConfig();\n const locale = config.locale?.(ctx) ?? config.defaultLocale;\n\n // the islands emitter runs while the response streams, outside this ALS scope\n setRequestLocale(ctx, locale);\n\n const context: I18nContext = {\n locale,\n translations: i18n.getCompiledTranslations(locale),\n rawTranslations: i18n.getTranslations(locale),\n fallback: config.fallback,\n };\n\n return runWithContext(context, () => next());\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAUA,SAAgB,cAAc,OAAwB;CACpD,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC,WAAW,KAAK,OAAO,GAAG,QAAQ;AACjE;;AAGA,SAAgB,sBAAsB,QAAwB;CAI5D,OAAO,sFAFO,cAAc;EAAE;EAAQ,QAAQ,CAAC;EAAG,cAD7B,KAAK,gBAAgB,MACmB;CAAE,CAEkC,EAAE;AACrG;;AAGA,SAAgB,yBAAyB,QAA+B;CACtE,MAAM,SAAS,KAAK,qBAAqB,MAAM;CAE/C,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC,WAAW,GACjC,OAAO;CAKT,OAAO,sCAFM,cAAc;EAAE;EAAQ,QAAQ,CAAC;EAAG,cAAc,CAAC;CAAE,CAElB,EAAE,0BAA0B,cAAc,MAAM,EAAE;AACpG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACMA,MAAM,iCAAiB,IAAI,QAAwB;AACnD,MAAM,mCAAmB,IAAI,QAA6B;AAI1D,MAAM,SAAS,CAAC,CAAE,YAA4C,KAAK;AAEnE,IAAI,aAAa;AAEjB,SAAgB,iBAAiB,SAAqB,QAAsB;CAC1E,eAAe,IAAI,QAAQ,QAAQ,MAAM;AAC3C;;AAGA,SAAS,eAAe,KAAqB;CAC3C,OAAO,IAAI,MAAM,IAAI,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,SAAS,EAAE;AAChE;AAeA,MAAM,yBAAS,IAAI,QAAkE;AACrF,MAAM,wBAAQ,IAAI,IAAoB;AAEtC,SAAS,SAAS,QAAoB,QAAgB,QAA6C;CACjG,IAAI,WAAW,OAAO,IAAI,MAAM;CAEhC,IAAI,CAAC,UAAU;EACb,2BAAW,IAAI,QAAQ;EACvB,OAAO,IAAI,QAAQ,QAAQ;CAC7B;CAEA,IAAI,QAAQ,SAAS,IAAI,MAAM;CAE/B,IAAI,OACF,OAAO;CAGT,MAAM,cAAc,SAAuC;EACzD,MAAM,SAA6B,CAAC;EAEpC,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,QAAQ,eAAe,GAAG;GAChC,MAAM,OAAO,OAAO;GAEpB,IAAI,MACF,OAAO,KAAK,CAAC,OAAO,IAAI,CAAC;EAE7B;EAEA,OAAO;CACT;CAEA,MAAM,SAAS,WAAW,OAAO,WAAW;CAE5C,QAAQ;EACN;EACA,OAAO,WAAW,OAAO,aAAa,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,kBAAkB,QAAQ,OAAO,IAAI,CAAC;EACrG,SAAS,OAAO,KAAK,CAAC,OAAO,UAAU,kBAAkB,QAAQ,OAAO,IAAI,CAAC;CAC/E;CACA,SAAS,IAAI,QAAQ,KAAK;CAE1B,OAAO;AACT;AAEA,SAAS,QAAQ,QAAwB;CACvC,IAAI,OAAO,MAAM,IAAI,MAAM;CAE3B,IAAI,SAAS,KAAA,GAAW;EACtB,OAAO,cAAc;GAAE;GAAQ,QAAQ,CAAC;GAAG,cAAc,CAAC;EAAE,CAAC;EAC7D,MAAM,IAAI,QAAQ,IAAI;CACxB;CAEA,OAAO;AACT;;;;AAKA,SAAgB,0BAA0B,MAAe,QAAyB;CAChF,QAAQ,YAAY;EAClB,IAAI,CAAC,KAAK,aAAa,GACrB,OAAO;EAGT,MAAM,SAAS,eAAe,IAAI,QAAQ,MAAM;EAEhD,IAAI,WAAW,KAAA,GACb,OAAO;EAGT,IAAI,KACF,OAAO,EAAE,MAAM,sBAAsB,MAAM,EAAE;EAG/C,IAAI,OAAO,KAAK,KAAK,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,GACpD,OAAO;EAGT,OAAO,EAAE,WAAW,yBAAyB,MAAM,EAAE;CACvD;AACF;AAEA,SAAgB,uBAA6B;CAC3C,IAAI,YACF;CAGF,aAAa;CAEb,wBAAwB,0BAA0B,CAAC;CAEnD,uBAAuB,QAAQ,YAAY;EAEzC,IAAI,CAAC,WAAW,CAAC,KAAK,aAAa,GACjC,OAAO;EAGT,MAAM,SAAS,eAAe,IAAI,QAAQ,MAAM;EAEhD,IAAI,WAAW,KAAA,GACb,OAAO;EAIT,MAAM,EAAE,QAAQ,OAAO,YAAY,SAAS,QAAQ,QADrC,KAAK,UAAU,MACmC,CAAC;EAElE,IAAI,UAAU,iBAAiB,IAAI,QAAQ,MAAM;EAEjD,IAAI,CAAC,SAAS;GACZ,0BAAU,IAAI,IAAI;GAClB,iBAAiB,IAAI,QAAQ,QAAQ,OAAO;EAC9C;EAEA,MAAM,QAAgC,CAAC;EACvC,IAAI,QAAQ;EAEZ,KAAK,MAAM,CAAC,OAAO,SAAS,QAAQ;GAClC,IAAI,QAAQ,IAAI,KAAK,GACnB;GAGF,QAAQ,IAAI,KAAK;GACjB,MAAM,SAAS;GACf,QAAQ;EACV;EAEA,MAAM,OAAO,QACT,sCAAsC,QAAQ,MAAM,EAAE,0BAA0B,cAAc,KAAK,EAAE,iBACrG,KAAA;EAEJ,IAAI,CAAC,QAAQ,MAAM,WAAW,KAAK,QAAQ,WAAW,GACpD,OAAO;EAGT,OAAO;GACL;GACA,OAAO,MAAM,SAAS,IAAI,QAAQ,KAAA;GAClC,SAAS,QAAQ,SAAS,IAAI,UAAU,KAAA;EAC1C;CACF,CAAC;AACH;;ACrMA,MAAM,uBAAuB;AAI7B,MAAM,sBAAsB;;;;;AAM5B,SAAS,oBAAoB,UAAwC;CACnE,IAAI,CAAC,SAAS,WAAW,oBAAoB,GAC3C;CAGF,IAAI,CAAC,KAAK,aAAa,GAAG;EACxB,IAAI,MAAM,sCAAsC;EAEhD;CACF;CAEA,MAAM,OAAO,SAAS,MAAM,CAA2B;CAGvD,MAAM,WAAW,KAAK,QAAQ,GAAG;CAEjC,IAAI,aAAa,IACf;CAGF,MAAM,SAAS,KAAK,MAAM,GAAG,QAAQ;CAKrC,IAAI,CAFW,KAAK,UAEV,CAAC,CAAC,QAAQ,SAAS,MAAM,GACjC,OAAO,IAAI,SAAS,wBAAwB;EAC1C,QAAQ;EACR,SAAS,EAAE,gBAAgB,yBAAyB;CACtD,CAAC;CAGH,MAAM,OAAO,KAAK,MAAM,WAAW,CAAC;CAEpC,IAAI,CAAC,KAAK,SAAS,KAAK,GACtB;CAGF,MAAM,YAAY,KAAK,MAAM,GAAG,EAAE;CAClC,MAAM,aAAa,UAAU,YAAY,GAAG;CAE5C,IAAI,eAAe,IACjB;CAGF,MAAM,YAAY,UAAU,MAAM,GAAG,UAAU;CAC/C,MAAM,OAAO,KAAK,aAAa,QAAQ,SAAS;CAEhD,IAAI,CAAC,MACH,OAAO,IAAI,SAAS,uBAAuB,UAAU,MAAM;EACzD,QAAQ;EACR,SAAS,EAAE,gBAAgB,yBAAyB;CACtD,CAAC;CAGH,OAAO,IAAI,SAAS,MAAkB;EACpC,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,kBAAkB,OAAO,KAAK,UAAU;GACxC,iBAAiB;EACnB;CACF,CAAC;AACH;;;;;;;;;AAUA,SAAgB,4BAA+C;CAC7D,QAAQ,KAAK,SAAS;EACpB,MAAM,WAAW,oBAAoB,IAAI,IAAI,QAAQ;EAErD,IAAI,CAAC,UACH,OAAO,KAAK;EAId,qBAAqB,mBAAmB;EAExC,OAAO;CACT;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,2BAA2B,UAAuC,CAAC,GAAsB;CACvG,qBAAqB;CAErB,MAAM,UAAU,QAAQ,WAAW;CAEnC,QAAQ,KAAK,SAAS;EACpB,IAAI,cAAc,KAAK,OAAO,GAC5B,OAAO,KAAK;EAGd,IAAI,CAAC,KAAK,aAAa,GAAG;GACxB,IAAI,MAAM,sCAAsC;GAEhD,OAAO,KAAK;EACd;EAEA,MAAM,SAAS,KAAK,UAAU;EAC9B,MAAM,SAAS,OAAO,SAAS,GAAG,KAAK,OAAO;EAG9C,iBAAiB,KAAK,MAAM;EAE5B,MAAM,UAAuB;GAC3B;GACA,cAAc,KAAK,wBAAwB,MAAM;GACjD,iBAAiB,KAAK,gBAAgB,MAAM;GAC5C,UAAU,OAAO;EACnB;EAEA,OAAO,eAAe,eAAe,KAAK,CAAC;CAC7C;AACF"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
// src/shared/meta.ts
|
|
2
2
|
/**
|
|
3
3
|
* Normalize meta argument to TranslationMeta object
|
|
4
4
|
*/
|
|
5
5
|
function normalizeMeta(meta) {
|
|
6
6
|
return typeof meta === "string" ? { fallback: meta } : meta;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
//#region src/shared/rich.ts
|
|
8
|
+
// src/shared/rich.ts
|
|
10
9
|
const REACT_ELEMENT_TYPE = Symbol.for("react.element");
|
|
11
10
|
const REACT_TRANSITIONAL_ELEMENT_TYPE = Symbol.for("react.transitional.element");
|
|
12
11
|
/**
|
|
@@ -92,7 +91,6 @@ function partsToNodes(parts, components) {
|
|
|
92
91
|
}
|
|
93
92
|
return result;
|
|
94
93
|
}
|
|
95
|
-
//#endregion
|
|
96
94
|
export { normalizeMeta as n, partsToNodes as t };
|
|
97
95
|
|
|
98
|
-
//# sourceMappingURL=rich-
|
|
96
|
+
//# sourceMappingURL=rich-BbDoHrd9.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-
|
|
1
|
+
{"version":3,"file":"rich-BbDoHrd9.js","names":[],"sources":["../src/shared/meta.ts","../src/shared/rich.ts"],"sourcesContent":["import type { TranslationMeta } from './types.js';\n\n/**\n * Normalize meta argument to TranslationMeta object\n */\nexport function normalizeMeta(meta: TranslationMeta | string): TranslationMeta {\n return typeof meta === 'string' ? { fallback: meta } : meta;\n}\n","import type { MessageMarkupPart, MessagePart } from 'messageformat';\n\n// MessagePart<string> covers all built-in part types\ntype Part = MessagePart<string>;\n\n/**\n * Component callbacks for rich text rendering.\n * Each key maps to a function that wraps children in a component.\n *\n * @example\n * ```tsx\n * const components: RichComponents = {\n * link: (children) => <a href=\"/tos\">{children}</a>,\n * bold: (children) => <strong>{children}</strong>,\n * };\n * ```\n */\nexport type RichComponents<T = unknown> = Record<string, (children: (string | T)[]) => T>;\n\ntype StackFrame<T> = {\n name: string;\n children: (string | T)[];\n};\n\n// react element symbols - Symbol.for() returns the same symbol React uses internally\nconst REACT_ELEMENT_TYPE = Symbol.for('react.element');\nconst REACT_TRANSITIONAL_ELEMENT_TYPE = Symbol.for('react.transitional.element');\n\ntype ReactElementLike = {\n $$typeof: symbol;\n key: string | null;\n};\n\n/**\n * Duck-type check for React elements without importing React.\n * Uses Symbol.for() which returns the same symbol React uses internally.\n */\nfunction isReactElement(value: unknown): value is ReactElementLike {\n if (typeof value !== 'object' || value === null || !('$$typeof' in value)) {\n return false;\n }\n\n const type = (value as ReactElementLike).$$typeof;\n\n return type === REACT_ELEMENT_TYPE || type === REACT_TRANSITIONAL_ELEMENT_TYPE;\n}\n\n/**\n * Add a key to a React element if it doesn't have one.\n * Creates a shallow copy since React elements are frozen.\n */\nfunction addKeyIfNeeded<T>(element: T, key: string): T {\n if (isReactElement(element) && element.key === null) {\n return { ...element, key } as T;\n }\n\n return element;\n}\n\nfunction isMarkupPart(part: Part): part is MessageMarkupPart {\n return part.type === 'markup';\n}\n\nfunction applyMarkupPart<T>(\n part: MessageMarkupPart,\n stack: StackFrame<T>[],\n result: (string | T)[],\n components: RichComponents<T>,\n nextKey: () => string,\n): void {\n const target = stack.length > 0 ? stack.at(-1)!.children : result;\n\n switch (part.kind) {\n case 'open': {\n stack.push({ name: part.name, children: [] });\n\n break;\n }\n\n case 'close': {\n const frame = stack.pop();\n\n if (!frame) {\n // mismatched close tag, ignore\n break;\n }\n\n const component = components[frame.name];\n const parent = stack.length > 0 ? stack.at(-1)!.children : result;\n\n if (component) {\n parent.push(addKeyIfNeeded(component(frame.children), nextKey()));\n } else {\n // no component for this tag, just flatten children\n parent.push(...frame.children);\n }\n\n break;\n }\n\n case 'standalone': {\n // self-closing: {#icon/}\n const component = components[part.name];\n\n if (component) {\n target.push(addKeyIfNeeded(component([]), nextKey()));\n }\n\n break;\n }\n }\n}\n\n/**\n * Convert MF2 formatToParts() output to a tree of nodes.\n * Works with any JSX runtime (Astro, React, etc.) - the callbacks\n * return whatever JSX elements the active runtime compiles to.\n *\n * @example\n * MF2 message: \"Read our {#link}Terms{/link}\"\n * Parts: [\n * { type: 'text', value: 'Read our ' },\n * { type: 'markup', kind: 'open', name: 'link' },\n * { type: 'text', value: 'Terms' },\n * { type: 'markup', kind: 'close', name: 'link' },\n * ]\n * Result: ['Read our ', <a href=\"/tos\">Terms</a>]\n */\nexport function partsToNodes<T>(parts: Part[], components: RichComponents<T>): (string | T)[] {\n const result: (string | T)[] = [];\n const stack: StackFrame<T>[] = [];\n let keyIndex = 0;\n const nextKey = () => `rich-${keyIndex++}`;\n\n for (const part of parts) {\n const target = stack.length > 0 ? stack.at(-1)!.children : result;\n\n if (isMarkupPart(part)) {\n applyMarkupPart(part, stack, result, components, nextKey);\n } else if (part.type === 'text') {\n // plain text content - MessageTextPart.value is a string\n target.push(part.value as string);\n } else if (part.type === 'bidiIsolation') {\n // bidi isolation marks are direction control characters, not content\n } else {\n if ('value' in part && part.value !== undefined && part.value !== null) {\n const stringValue = String(part.value);\n\n target.push(stringValue);\n }\n }\n }\n\n // handle unclosed tags by flattening remaining stack frames\n while (stack.length > 0) {\n const frame = stack.pop()!;\n const parent = stack.length > 0 ? stack.at(-1)!.children : result;\n\n parent.push(...frame.children);\n }\n\n return result;\n}\n"],"mappings":";;;;AAKA,SAAgB,cAAc,MAAiD;CAC7E,OAAO,OAAO,SAAS,WAAW,EAAE,UAAU,KAAK,IAAI;AACzD;;ACkBA,MAAM,qBAAqB,OAAO,IAAI,eAAe;AACrD,MAAM,kCAAkC,OAAO,IAAI,4BAA4B;;;;;AAW/E,SAAS,eAAe,OAA2C;CACjE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,cAAc,QACjE,OAAO;CAGT,MAAM,OAAQ,MAA2B;CAEzC,OAAO,SAAS,sBAAsB,SAAS;AACjD;;;;;AAMA,SAAS,eAAkB,SAAY,KAAgB;CACrD,IAAI,eAAe,OAAO,KAAK,QAAQ,QAAQ,MAC7C,OAAO;EAAE,GAAG;EAAS;CAAI;CAG3B,OAAO;AACT;AAEA,SAAS,aAAa,MAAuC;CAC3D,OAAO,KAAK,SAAS;AACvB;AAEA,SAAS,gBACP,MACA,OACA,QACA,YACA,SACM;CACN,MAAM,SAAS,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,CAAE,WAAW;CAE3D,QAAQ,KAAK,MAAb;EACE,KAAK;GACH,MAAM,KAAK;IAAE,MAAM,KAAK;IAAM,UAAU,CAAC;GAAE,CAAC;GAE5C;EAGF,KAAK,SAAS;GACZ,MAAM,QAAQ,MAAM,IAAI;GAExB,IAAI,CAAC,OAEH;GAGF,MAAM,YAAY,WAAW,MAAM;GACnC,MAAM,SAAS,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,CAAE,WAAW;GAE3D,IAAI,WACF,OAAO,KAAK,eAAe,UAAU,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC;QAGhE,OAAO,KAAK,GAAG,MAAM,QAAQ;GAG/B;EACF;EAEA,KAAK,cAAc;GAEjB,MAAM,YAAY,WAAW,KAAK;GAElC,IAAI,WACF,OAAO,KAAK,eAAe,UAAU,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;GAGtD;EACF;CACF;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,aAAgB,OAAe,YAA+C;CAC5F,MAAM,SAAyB,CAAC;CAChC,MAAM,QAAyB,CAAC;CAChC,IAAI,WAAW;CACf,MAAM,gBAAgB,QAAQ;CAE9B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,CAAE,WAAW;EAE3D,IAAI,aAAa,IAAI,GACnB,gBAAgB,MAAM,OAAO,QAAQ,YAAY,OAAO;OACnD,IAAI,KAAK,SAAS,QAEvB,OAAO,KAAK,KAAK,KAAe;OAC3B,IAAI,KAAK,SAAS,iBAAiB,CAE1C,OACE,IAAI,WAAW,QAAQ,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,MAAM;GACtE,MAAM,cAAc,OAAO,KAAK,KAAK;GAErC,OAAO,KAAK,WAAW;EACzB;CAEJ;CAGA,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,QAAQ,MAAM,IAAI;EAGxB,CAFe,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,CAAE,WAAW,OAAA,CAEpD,KAAK,GAAG,MAAM,QAAQ;CAC/B;CAEA,OAAO;AACT"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import "messageformat";
|
|
2
|
-
//#region src/shared/rich.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Component callbacks for rich text rendering.
|
|
5
4
|
* Each key maps to a function that wraps children in a component.
|
|
@@ -13,6 +12,5 @@ import "messageformat";
|
|
|
13
12
|
* ```
|
|
14
13
|
*/
|
|
15
14
|
type RichComponents<T = unknown> = Record<string, (children: (string | T)[]) => T>;
|
|
16
|
-
//#endregion
|
|
17
15
|
export { RichComponents as t };
|
|
18
|
-
//# sourceMappingURL=rich-
|
|
16
|
+
//# sourceMappingURL=rich-CavnJlKg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-CavnJlKg.d.ts","names":[],"sources":["../src/shared/rich.ts"],"mappings":";;;;;;;;;;;;;KAiBY,eAAe,eAAe,gBAAgB,oBAAoB,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-middleware-entrypoint.d.ts","names":[],"sources":["../../src/server/chunk-middleware-entrypoint.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"chunk-middleware-entrypoint.d.ts","names":[],"sources":["../../src/server/chunk-middleware-entrypoint.ts"],"mappings":"A,qBAEa,2BAAS"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { t as createI18nChunkMiddleware } from "../middleware-
|
|
2
|
-
|
|
1
|
+
import { t as createI18nChunkMiddleware } from "../middleware-CNXfXefr.js";
|
|
2
|
+
// src/server/chunk-middleware-entrypoint.ts
|
|
3
3
|
const onRequest = createI18nChunkMiddleware();
|
|
4
|
-
//#endregion
|
|
5
4
|
export { onRequest };
|
|
6
5
|
|
|
7
6
|
//# sourceMappingURL=chunk-middleware-entrypoint.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locale-middleware-entrypoint.d.ts","names":[],"sources":["../../src/server/locale-middleware-entrypoint.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"locale-middleware-entrypoint.d.ts","names":[],"sources":["../../src/server/locale-middleware-entrypoint.ts"],"mappings":"A,qBAGa,2BAAS"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { n as createI18nLocaleMiddleware } from "../middleware-
|
|
1
|
+
import { n as createI18nLocaleMiddleware } from "../middleware-CNXfXefr.js";
|
|
2
2
|
import { exclude } from "virtual:@astroscope/i18n/config";
|
|
3
|
-
|
|
3
|
+
// src/server/locale-middleware-entrypoint.ts
|
|
4
4
|
const onRequest = createI18nLocaleMiddleware({ exclude: exclude ?? void 0 });
|
|
5
|
-
//#endregion
|
|
6
5
|
export { onRequest };
|
|
7
6
|
|
|
8
7
|
//# sourceMappingURL=locale-middleware-entrypoint.js.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { t as RichComponents } from "../rich-
|
|
2
|
-
import { a as TranslationMeta, i as TranslateFunction } from "../types-
|
|
3
|
-
//#region src/server/translate.d.ts
|
|
1
|
+
import { t as RichComponents } from "../rich-CavnJlKg.js";
|
|
2
|
+
import { a as TranslationMeta, i as TranslateFunction } from "../types-DYnR9xP3.js";
|
|
4
3
|
/**
|
|
5
4
|
* Get the current locale from request context.
|
|
6
5
|
* Returns defaultLocale if called outside a request (e.g. during build).
|
|
@@ -34,5 +33,4 @@ export declare const t: TranslateFunction;
|
|
|
34
33
|
* ```
|
|
35
34
|
*/
|
|
36
35
|
export declare function rich<T = unknown>(key: string, meta?: TranslationMeta | string | undefined, components?: RichComponents<T> | undefined, values?: Record<string, unknown> | undefined): (string | T)[];
|
|
37
|
-
//#endregion
|
|
38
36
|
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.d.ts","names":[],"sources":["../../src/server/translate.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"translate.d.ts","names":[],"sources":["../../src/server/translate.ts"],"mappings":";;;;;;wBAqDgB;;;;;;;;qBAYH,GAAG;;;;;;;;;;;;;;;;;;;;wBA2DA,KAAK,aACnB,aACA,OAAO,sCACP,aAAa,eAAe,gBAC5B,SAAS,gDACE"}
|
package/dist/server/translate.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
3
|
-
import { t as getContext } from "../context-
|
|
4
|
-
import {
|
|
1
|
+
import { r as formatMessageToParts, t as compileMessage } from "../compiler-K3uHKoFs.js";
|
|
2
|
+
import { n as normalizeMeta, t as partsToNodes } from "../rich-BbDoHrd9.js";
|
|
3
|
+
import { t as getContext } from "../context-CJvTEa-I.js";
|
|
4
|
+
import { t as i18n } from "../i18n-f7qdMTwm.js";
|
|
5
5
|
import { createCounter } from "@astroscope/node/telemetry";
|
|
6
6
|
import { log } from "@astroscope/node/log";
|
|
7
|
-
|
|
7
|
+
// src/server/translate.ts
|
|
8
8
|
const fallbackCache = /* @__PURE__ */ new Map();
|
|
9
9
|
const missing = createCounter("astro.i18n.missing", {
|
|
10
10
|
scope: "@astroscope/i18n",
|
|
@@ -97,7 +97,6 @@ function rich(key, meta, components, values) {
|
|
|
97
97
|
const parts = formatMessageToParts(locale, raw, values);
|
|
98
98
|
return partsToNodes(parts, components ?? {});
|
|
99
99
|
}
|
|
100
|
-
//#endregion
|
|
101
100
|
export { getLocale, rich, t };
|
|
102
101
|
|
|
103
102
|
//# sourceMappingURL=translate.js.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/shared/types.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Variable definition for documentation
|
|
4
3
|
*/
|
|
@@ -51,6 +50,5 @@ type I18nClientState = {
|
|
|
51
50
|
* t('cart.total', { fallback: 'Total: {$amount}' }, { amount: '$49.99' })
|
|
52
51
|
*/
|
|
53
52
|
type TranslateFunction = (key: string, meta: TranslationMeta | string, values?: Record<string, unknown> | undefined) => string;
|
|
54
|
-
//#endregion
|
|
55
53
|
export { TranslationMeta as a, TranslateFunction as i, I18nClientState as n, RawTranslations as r, CompiledTranslations as t };
|
|
56
|
-
//# sourceMappingURL=types-
|
|
54
|
+
//# sourceMappingURL=types-DYnR9xP3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DYnR9xP3.d.ts","names":[],"sources":["../src/shared/types.ts"],"mappings":";;;KAGY;EACV;EACA;;;;;KAMU;EACV;EACA,YAAY,eAAe;EAC3B;;;;;KAMU,uBAAuB,SAAS;;;;KAKhC,uBAAuB,eAAe;;;;KAKtC,kBAAkB;;;;;;KAOlB;EACV;EACA,QAAQ;EACR,cAAc;;;;;;;;;;;;;;;;KAiBJ,qBACV,aACA,MAAM,0BACN,SAAS"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { a as TranslationMeta } from "./types-
|
|
2
|
-
//#region src/extraction/types.d.ts
|
|
1
|
+
import { a as TranslationMeta } from "./types-DYnR9xP3.js";
|
|
3
2
|
/**
|
|
4
3
|
* Deduplicated translation key with all file locations (used in manifest)
|
|
5
4
|
*/
|
|
@@ -26,6 +25,5 @@ type ExtractionManifest = {
|
|
|
26
25
|
*/
|
|
27
26
|
scripts: string[];
|
|
28
27
|
};
|
|
29
|
-
//#endregion
|
|
30
28
|
export { ExtractedKey as n, ExtractionManifest as r, ChunkManifest as t };
|
|
31
|
-
//# sourceMappingURL=types-
|
|
29
|
+
//# sourceMappingURL=types-DwM0qsLj.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DwM0qsLj.d.ts","names":[],"sources":["../src/extraction/types.ts"],"mappings":";;;;KA8BY;EACV;EACA,MAAM;;EAEN;;;;;KAMU,gBAAgB;;;;KAKhB;EACV,MAAM;EACN,QAAQ;;;;;;EAMR"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ExcludePattern } from "@astroscope/node/excludes";
|
|
2
|
-
//#region src/integration/types.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Consistency check level for translation keys.
|
|
5
4
|
* Controls behavior when the same key has different fallbacks/variables across files.
|
|
@@ -21,6 +20,5 @@ interface I18nOptions {
|
|
|
21
20
|
*/
|
|
22
21
|
exclude?: ExcludePattern[] | undefined;
|
|
23
22
|
}
|
|
24
|
-
//#endregion
|
|
25
23
|
export { I18nOptions as n, ConsistencyCheckLevel as t };
|
|
26
|
-
//# sourceMappingURL=types-
|
|
24
|
+
//# sourceMappingURL=types-ioAI5pu0.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-ioAI5pu0.d.ts","names":[],"sources":["../src/integration/types.ts"],"mappings":";;;;;;;;;KAUY;UAEK;;;;;EAKf,cAAc;;;;;EAKd,UAAU"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/shared/url.ts
|
|
2
2
|
/**
|
|
3
3
|
* Convert a Vite chunk ID to a clean chunk name
|
|
4
4
|
* Strips the _astro/ prefix that Vite adds
|
|
@@ -20,7 +20,6 @@ function chunkIdToName(chunkId) {
|
|
|
20
20
|
function buildI18nChunkUrl(locale, chunkId, hash) {
|
|
21
21
|
return `/_i18n/${locale}/${chunkIdToName(chunkId)}.${hash}.js`;
|
|
22
22
|
}
|
|
23
|
-
//#endregion
|
|
24
23
|
export { chunkIdToName as n, buildI18nChunkUrl as t };
|
|
25
24
|
|
|
26
|
-
//# sourceMappingURL=url-
|
|
25
|
+
//# sourceMappingURL=url-6HS_IPPA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-
|
|
1
|
+
{"version":3,"file":"url-6HS_IPPA.js","names":[],"sources":["../src/shared/url.ts"],"sourcesContent":["/**\n * Convert a Vite chunk ID to a clean chunk name\n * Strips the _astro/ prefix that Vite adds\n *\n * @example\n * chunkIdToName('_astro/Cart.C_sxtxbl') // → 'Cart.C_sxtxbl'\n * chunkIdToName('Cart.C_sxtxbl') // → 'Cart.C_sxtxbl'\n */\nexport function chunkIdToName(chunkId: string): string {\n return chunkId.replace(/^_astro\\//, '');\n}\n\n/**\n * Extract chunk name from a component URL\n * Used by client directives to get the hash lookup key\n *\n * @example\n * componentUrlToChunkName('/_astro/Cart.C_sxtxbl.js') // → 'Cart.C_sxtxbl'\n */\nexport function componentUrlToChunkName(componentUrl: string): string {\n return componentUrl.replace(/^\\/_astro\\//, '').replace(/\\.js$/, '');\n}\n\n/**\n * Build the URL path for fetching a translation chunk\n *\n * @example\n * buildI18nChunkUrl('en', '_astro/Cart.C_sxtxbl', 'bzh6rx')\n * // → '/_i18n/en/Cart.C_sxtxbl.bzh6rx.js'\n */\nexport function buildI18nChunkUrl(locale: string, chunkId: string, hash: string): string {\n return `/_i18n/${locale}/${chunkIdToName(chunkId)}.${hash}.js`;\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,cAAc,SAAyB;CACrD,OAAO,QAAQ,QAAQ,aAAa,EAAE;AACxC;;;;;;;;AAoBA,SAAgB,kBAAkB,QAAgB,SAAiB,MAAsB;CACvF,OAAO,UAAU,OAAO,GAAG,cAAc,OAAO,EAAE,GAAG,KAAK;AAC5D"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { n as chunkIdToName } from "./url-
|
|
2
|
-
import { n as getManifest, t as getGlobalState } from "./manifest-
|
|
1
|
+
import { n as chunkIdToName } from "./url-6HS_IPPA.js";
|
|
2
|
+
import { n as getManifest, t as getGlobalState } from "./manifest-DK513NbQ.js";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { serializeExcludePatterns } from "@astroscope/node/excludes";
|
|
6
|
+
import { isScriptEntry } from "@astroscope/node/islands";
|
|
6
7
|
import MagicString from "magic-string";
|
|
7
8
|
import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
8
|
-
|
|
9
|
+
// src/extraction/extract.ts
|
|
9
10
|
/** TypeScript extensions (need 'typescript' babel plugin) */
|
|
10
11
|
const TS_EXTENSIONS = [".ts", ".tsx"];
|
|
11
12
|
/** JSX extensions (need 'jsx' babel plugin) */
|
|
@@ -25,7 +26,7 @@ async function extractKeysFromFile(options) {
|
|
|
25
26
|
const { filename, code, stripFallbacks = false, onKeyExtracted } = options;
|
|
26
27
|
const keys = [];
|
|
27
28
|
const errors = [];
|
|
28
|
-
const [{ transformAsync }, { i18nExtractPlugin }] = await Promise.all([import("@babel/core"), import("./babel-plugin-
|
|
29
|
+
const [{ transformAsync }, { i18nExtractPlugin }] = await Promise.all([import("@babel/core"), import("./babel-plugin-CRgz4mri.js")]);
|
|
29
30
|
const isTypeScript = TS_EXTENSIONS.some((ext) => filename.endsWith(ext)) || filename.endsWith(".astro");
|
|
30
31
|
const isJSX = JSX_EXTENSIONS.some((ext) => filename.endsWith(ext));
|
|
31
32
|
const result = await transformAsync(code, {
|
|
@@ -52,8 +53,7 @@ async function extractKeysFromFile(options) {
|
|
|
52
53
|
map: result?.map ?? null
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
//#region src/extraction/key-store.ts
|
|
56
|
+
// src/extraction/key-store.ts
|
|
57
57
|
/**
|
|
58
58
|
* Store for extracted i18n keys.
|
|
59
59
|
* Stores occurrences and produces deduplicated keys with all file locations.
|
|
@@ -234,8 +234,7 @@ var KeyStore = class {
|
|
|
234
234
|
return this.occurrences;
|
|
235
235
|
}
|
|
236
236
|
};
|
|
237
|
-
|
|
238
|
-
//#region src/extraction/source-map.ts
|
|
237
|
+
// src/extraction/source-map.ts
|
|
239
238
|
/**
|
|
240
239
|
* Rewrite compiled-output positions back to the authored source.
|
|
241
240
|
*
|
|
@@ -264,8 +263,7 @@ function mapErrorsToSource(errors, map) {
|
|
|
264
263
|
};
|
|
265
264
|
});
|
|
266
265
|
}
|
|
267
|
-
|
|
268
|
-
//#region src/extraction/scan.ts
|
|
266
|
+
// src/extraction/scan.ts
|
|
269
267
|
const GLOB_PATTERN = `src/**/*.{${ALL_EXTENSIONS.map((e) => e.slice(1)).join(",")}}`;
|
|
270
268
|
/**
|
|
271
269
|
* Compile .astro file to JS using Astro's compiler.
|
|
@@ -335,8 +333,7 @@ async function scan(options) {
|
|
|
335
333
|
}
|
|
336
334
|
return store;
|
|
337
335
|
}
|
|
338
|
-
|
|
339
|
-
//#region src/extraction/vite-plugin.ts
|
|
336
|
+
// src/extraction/vite-plugin.ts
|
|
340
337
|
const VIRTUAL_MODULE_ID = "virtual:@astroscope/i18n/manifest";
|
|
341
338
|
const isVirtualModuleId = (id) => id === VIRTUAL_MODULE_ID || id.startsWith(`${VIRTUAL_MODULE_ID}?`);
|
|
342
339
|
const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
@@ -470,7 +467,7 @@ export function getManifest() { return _getManifest(); }
|
|
|
470
467
|
imports.push(chunkIdToName(baseName));
|
|
471
468
|
}
|
|
472
469
|
directImports.set(chunkName, imports);
|
|
473
|
-
if ((chunk.isEntry || chunk.isDynamicEntry) && chunk.facadeModuleId
|
|
470
|
+
if ((chunk.isEntry || chunk.isDynamicEntry) && isScriptEntry(chunk.facadeModuleId)) scriptEntries.push(chunkName);
|
|
474
471
|
}
|
|
475
472
|
const scriptChunks = /* @__PURE__ */ new Set();
|
|
476
473
|
const walkScripts = (chunkName, visited) => {
|
|
@@ -507,7 +504,6 @@ export function getManifest() { return _getManifest(); }
|
|
|
507
504
|
}
|
|
508
505
|
};
|
|
509
506
|
}
|
|
510
|
-
//#endregion
|
|
511
507
|
export { i18nVitePlugin as t };
|
|
512
508
|
|
|
513
|
-
//# sourceMappingURL=vite-plugin-
|
|
509
|
+
//# sourceMappingURL=vite-plugin-Yc5NP6bs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin-Yc5NP6bs.js","names":[],"sources":["../src/extraction/extract.ts","../src/extraction/key-store.ts","../src/extraction/source-map.ts","../src/extraction/scan.ts","../src/extraction/vite-plugin.ts"],"sourcesContent":["import type { FileResult, PluginItem } from '@babel/core';\nimport type { ExtractedKeyOccurrence, ExtractionError } from './types.js';\n\n/** TypeScript extensions (need 'typescript' babel plugin) */\nexport const TS_EXTENSIONS = ['.ts', '.tsx'] as const;\n\n/** JSX extensions (need 'jsx' babel plugin) */\nexport const JSX_EXTENSIONS = ['.jsx', '.tsx'] as const;\n\n/** Plain JS extensions */\nexport const JS_EXTENSIONS = ['.js', '.jsx'] as const;\n\n/**\n * Extensions that can be parsed directly by Babel (raw source files).\n * Used for eager scanning before Vite processes them.\n */\nexport const BABEL_EXTENSIONS = [...TS_EXTENSIONS, ...JS_EXTENSIONS] as const;\n\n/**\n * All extensions that may contain t() calls.\n * Includes .astro which requires Astro's compiler before Babel can parse it.\n */\nexport const ALL_EXTENSIONS = [...BABEL_EXTENSIONS, '.astro'] as const;\n\nexport type ExtractOptions = {\n filename: string;\n code: string;\n stripFallbacks?: boolean | undefined;\n onKeyExtracted?: ((key: ExtractedKeyOccurrence) => void) | undefined;\n};\n\nexport type ExtractResult = {\n keys: ExtractedKeyOccurrence[];\n /** t() calls whose meta could not be read statically */\n errors: ExtractionError[];\n code: string | null;\n map: FileResult['map'];\n};\n\n/**\n * Extract keys from a single file using Babel.\n * Optionally strips fallbacks (for production builds).\n */\nexport async function extractKeysFromFile(options: ExtractOptions): Promise<ExtractResult> {\n const { filename, code, stripFallbacks = false, onKeyExtracted } = options;\n\n const keys: ExtractedKeyOccurrence[] = [];\n const errors: ExtractionError[] = [];\n\n // dynamic imports prevent Babel from being bundled into server runtime\n const [{ transformAsync }, { i18nExtractPlugin }] = await Promise.all([\n import('@babel/core'),\n import('./babel-plugin.js'),\n ]);\n\n // treat .astro files as TypeScript (otherwise type imports fail)\n const isTypeScript = TS_EXTENSIONS.some((ext) => filename.endsWith(ext)) || filename.endsWith('.astro');\n const isJSX = JSX_EXTENSIONS.some((ext) => filename.endsWith(ext));\n\n const result = await transformAsync(code, {\n filename,\n sourceMaps: stripFallbacks, // only need source maps when transforming\n babelrc: false,\n configFile: false,\n parserOpts: {\n plugins: [...(isTypeScript ? ['typescript' as const] : []), ...(isJSX ? ['jsx' as const] : [])],\n },\n plugins: [\n [\n i18nExtractPlugin,\n {\n stripFallbacks,\n onKeyExtracted: (key: ExtractedKeyOccurrence) => {\n keys.push(key);\n onKeyExtracted?.(key);\n },\n onExtractionError: (error: ExtractionError) => {\n errors.push(error);\n },\n },\n ] as PluginItem,\n ],\n });\n\n return {\n keys,\n errors,\n code: result?.code ?? null,\n map: result?.map ?? null,\n };\n}\n","import type { AstroIntegrationLogger } from 'astro';\nimport type { ConsistencyCheckLevel } from '../integration/types.js';\nimport type { TranslationMeta } from '../shared/types.js';\nimport type { ExtractedKey, ExtractedKeyOccurrence, ExtractionError } from './types.js';\n\n/**\n * Inconsistency between two occurrences of the same translation key\n */\nexport type KeyInconsistency = {\n key: string;\n field: 'fallback' | 'description' | 'variables';\n locations: [string, string];\n values: [string, string];\n};\n\n/**\n * Store for extracted i18n keys.\n * Stores occurrences and produces deduplicated keys with all file locations.\n */\nexport class KeyStore {\n /** All key occurrences (may have duplicates) */\n private readonly occurrences: ExtractedKeyOccurrence[] = [];\n\n /** Tracks reported inconsistencies to avoid duplicate warnings */\n private readonly reportedInconsistencies = new Set<string>();\n\n /** Whether an error-level inconsistency was found (for build failure) */\n private hasInconsistencyError = false;\n\n /** Non-extractable t() meta, per file (replaced wholesale on HMR) */\n private readonly errorsByFile = new Map<string, ExtractionError[]>();\n\n readonly fileToKeys = new Map<string, string[]>();\n readonly filesWithI18n = new Set<string>();\n\n constructor(\n private readonly logger: AstroIntegrationLogger,\n private readonly consistency: ConsistencyCheckLevel = 'error',\n ) {}\n\n /**\n * Check new keys for inconsistencies with existing occurrences.\n */\n private checkConsistency(newKeys: ExtractedKeyOccurrence[]): void {\n const existingByKey = new Map<string, ExtractedKeyOccurrence>();\n\n for (const occ of this.occurrences) {\n if (!existingByKey.has(occ.key)) {\n existingByKey.set(occ.key, occ);\n }\n }\n\n for (const newKey of newKeys) {\n const existing = existingByKey.get(newKey.key);\n\n if (!existing) continue;\n\n const inconsistencies = this.findInconsistencies(newKey, existing);\n\n for (const inc of inconsistencies) {\n this.reportInconsistency(inc);\n }\n }\n }\n\n /**\n * Compare two occurrences and find any metadata inconsistencies.\n */\n private findInconsistencies(a: ExtractedKeyOccurrence, b: ExtractedKeyOccurrence): KeyInconsistency[] {\n const result: KeyInconsistency[] = [];\n const locA = `${a.file}:${a.line}`;\n const locB = `${b.file}:${b.line}`;\n\n if (a.meta.fallback !== b.meta.fallback) {\n result.push({\n key: a.key,\n field: 'fallback',\n locations: [locA, locB],\n values: [a.meta.fallback, b.meta.fallback],\n });\n }\n\n if (a.meta.description !== b.meta.description) {\n result.push({\n key: a.key,\n field: 'description',\n locations: [locA, locB],\n values: [a.meta.description ?? '(none)', b.meta.description ?? '(none)'],\n });\n }\n\n const varsA = this.serializeVariables(a.meta);\n const varsB = this.serializeVariables(b.meta);\n\n if (varsA !== varsB) {\n result.push({\n key: a.key,\n field: 'variables',\n locations: [locA, locB],\n values: [varsA || '(none)', varsB || '(none)'],\n });\n }\n\n return result;\n }\n\n /**\n * Serialize variables for comparison.\n */\n private serializeVariables(meta: TranslationMeta): string {\n if (!meta.variables) return '';\n\n // sort keys for consistent comparison\n const sorted: Record<string, unknown> = {};\n const names = Object.keys(meta.variables).toSorted((a, b) => a.localeCompare(b));\n\n for (const key of names) {\n sorted[key] = meta.variables[key];\n }\n\n return JSON.stringify(sorted);\n }\n\n /**\n * Report an inconsistency via logger.\n */\n private reportInconsistency(inc: KeyInconsistency): void {\n // keyed on key+field, not locations — one report per inconsistent key\n const incKey = `${inc.key}:${inc.field}`;\n\n if (this.reportedInconsistencies.has(incKey)) return;\n\n this.reportedInconsistencies.add(incKey);\n\n const message =\n `inconsistent ${inc.field} for key \"${inc.key}\"\\n` +\n ` ${inc.locations[0]}: ${JSON.stringify(inc.values[0])}\\n` +\n ` ${inc.locations[1]}: ${JSON.stringify(inc.values[1])}`;\n\n if (this.consistency === 'error') {\n this.logger.error(message);\n this.hasInconsistencyError = true;\n } else {\n this.logger.warn(message);\n }\n }\n\n /**\n * Add key occurrences for a file, replacing any existing occurrences for that file.\n */\n addFileKeys(filename: string, keys: ExtractedKeyOccurrence[]): void {\n const newKeyStrings = keys.map((k) => k.key);\n const oldKeys = this.fileToKeys.get(filename);\n\n if (oldKeys) {\n const uniqueNew = [...new Set(newKeyStrings)];\n const uniqueOld = [...new Set(oldKeys)];\n const added = uniqueNew.filter((k) => !uniqueOld.includes(k));\n const removed = uniqueOld.filter((k) => !uniqueNew.includes(k));\n const file = filename.split('/').pop();\n\n if (added.length > 0 || removed.length > 0) {\n const parts: string[] = [];\n\n if (added.length > 0) parts.push(`+${added.length}`);\n if (removed.length > 0) parts.push(`-${removed.length}`);\n\n this.logger.info(`hmr: ${parts.join(' ')} key(s) in ${file}`);\n }\n }\n\n if (oldKeys) {\n for (let i = this.occurrences.length - 1; i >= 0; i--) {\n if (this.occurrences[i]?.file === filename) {\n this.occurrences.splice(i, 1);\n }\n }\n }\n\n this.filesWithI18n.add(filename);\n\n // check for inconsistencies before adding new keys\n if (this.consistency !== 'off') {\n this.checkConsistency(keys);\n }\n\n if (keys.length > 0) {\n this.occurrences.push(...keys);\n }\n\n this.fileToKeys.set(filename, newKeyStrings);\n }\n\n /**\n * Record the non-extractable t() calls found in a file, replacing any\n * previously recorded for it.\n */\n addFileErrors(filename: string, errors: ExtractionError[]): void {\n if (errors.length > 0) {\n this.errorsByFile.set(filename, errors);\n } else {\n this.errorsByFile.delete(filename);\n }\n }\n\n /**\n * All non-extractable t() calls found so far, across every scanned file.\n */\n get extractionErrors(): ExtractionError[] {\n return this.errorsByFile.values().toArray().flat();\n }\n\n /**\n * Check if any error-level inconsistencies were found.\n * Call this at the end of build to determine if it should fail.\n */\n get hasErrors(): boolean {\n return this.hasInconsistencyError;\n }\n\n /**\n * Get deduplicated keys with all file locations merged.\n * Last occurrence's meta wins for each key.\n */\n get extractedKeys(): ExtractedKey[] {\n const keyMap = new Map<string, ExtractedKey>();\n\n for (const occurrence of this.occurrences) {\n const existing = keyMap.get(occurrence.key);\n const fileLocation = `${occurrence.file}:${occurrence.line}`;\n\n if (existing) {\n if (!existing.files.includes(fileLocation)) {\n existing.files.push(fileLocation);\n }\n\n existing.meta = occurrence.meta;\n } else {\n const key: ExtractedKey = {\n key: occurrence.key,\n meta: occurrence.meta,\n files: [fileLocation],\n };\n\n keyMap.set(occurrence.key, key);\n }\n }\n\n return keyMap.values().toArray();\n }\n\n /**\n * Get count of unique keys.\n */\n get uniqueKeyCount(): number {\n return new Set(this.occurrences.map((k) => k.key)).size;\n }\n\n /**\n * Merge another store's data into this one.\n */\n merge(other: KeyStore): void {\n for (const [file, keys] of other.fileToKeys) {\n this.fileToKeys.set(file, keys);\n }\n\n for (const [file, errors] of other.errorsByFile) {\n this.errorsByFile.set(file, errors);\n }\n\n for (const file of other.filesWithI18n) {\n this.filesWithI18n.add(file);\n }\n\n if (this.consistency !== 'off') {\n this.checkConsistency(other.occurrences);\n }\n\n this.occurrences.push(...other.occurrences);\n }\n\n /**\n * Internal access to occurrences (for merge)\n */\n get occurrencesList(): ExtractedKeyOccurrence[] {\n return this.occurrences;\n }\n}\n","import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping';\nimport type { ExtractionError } from './types.js';\n\n/**\n * Rewrite compiled-output positions back to the authored source.\n *\n * Babel parses .astro files after Astro's compiler has rewritten them, so the\n * positions it reports refer to code the developer never wrote. Errors that\n * cannot be mapped keep the position babel gave them.\n */\nexport function mapErrorsToSource(errors: ExtractionError[], map: unknown): ExtractionError[] {\n if (!map || errors.length === 0) {\n return errors;\n }\n\n let tracer: TraceMap;\n\n try {\n tracer = new TraceMap(map as never);\n } catch {\n return errors;\n }\n\n return errors.map((error) => {\n const position = originalPositionFor(tracer, { line: error.line, column: error.column });\n\n if (position.line === null) {\n return error;\n }\n\n return { ...error, line: position.line, column: position.column ?? error.column };\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { AstroIntegrationLogger } from 'astro';\nimport type { ConsistencyCheckLevel } from '../integration/types.js';\nimport { ALL_EXTENSIONS, extractKeysFromFile } from './extract.js';\nimport { KeyStore } from './key-store.js';\nimport { mapErrorsToSource } from './source-map.js';\n\nconst GLOB_PATTERN = `src/**/*.{${ALL_EXTENSIONS.map((e) => e.slice(1)).join(',')}}`;\n\n/**\n * Compile .astro file to JS using Astro's compiler.\n * Returns the compiled code and its source map, or null if compilation fails.\n */\nasync function compileAstro(code: string, filename: string): Promise<{ code: string; map: string } | null> {\n try {\n const { transform } = await import('@astrojs/compiler');\n const result = await transform(code, { filename, sourcemap: 'external' });\n\n return { code: result.code, map: result.map };\n } catch {\n return null;\n }\n}\n\nexport type ScanOptions = {\n projectRoot: string;\n logger: AstroIntegrationLogger;\n consistency: ConsistencyCheckLevel;\n};\n\n/**\n * Eagerly scan all source files for t() calls.\n * Used in dev mode to extract all keys upfront, instead of waiting for\n * Vite's lazy transform hook to process files on-demand.\n */\nexport async function scan(options: ScanOptions): Promise<KeyStore> {\n const { projectRoot, logger, consistency } = options;\n const store = new KeyStore(logger, consistency);\n\n const files: string[] = [];\n const entries = fs.promises.glob(GLOB_PATTERN, {\n cwd: projectRoot,\n exclude: (name) => name === 'node_modules',\n });\n\n for await (const entry of entries) {\n files.push(path.resolve(projectRoot, entry));\n }\n\n const results = await Promise.all(\n files.map(async (file) => {\n let code = await fs.promises.readFile(file, 'utf8');\n\n // quick check: skip files without i18n translate import\n if (!code.includes('@astroscope/i18n/translate')) {\n return { file, keys: null, errors: [] };\n }\n\n // compile .astro files first, keeping the map to report authored positions\n let astroMap: string | undefined;\n\n if (file.endsWith('.astro')) {\n const compiled = await compileAstro(code, file);\n\n if (!compiled) {\n return { file, keys: null, errors: [] };\n }\n\n code = compiled.code;\n astroMap = compiled.map;\n }\n\n const result = await extractKeysFromFile({\n filename: file,\n code,\n stripFallbacks: false,\n });\n\n return {\n file,\n keys: result.keys.length > 0 ? result.keys : null,\n errors: mapErrorsToSource(result.errors, astroMap),\n };\n }),\n );\n\n for (const { file, keys, errors } of results) {\n if (keys) {\n store.addFileKeys(file, keys);\n }\n\n store.addFileErrors(file, errors);\n }\n\n return store;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { type ExcludePattern, serializeExcludePatterns } from '@astroscope/node/excludes';\nimport { isScriptEntry } from '@astroscope/node/islands';\nimport type { AstroIntegrationLogger } from 'astro';\nimport MagicString from 'magic-string';\nimport type { Plugin } from 'vite';\nimport type { ConsistencyCheckLevel } from '../integration/types.js';\nimport { chunkIdToName } from '../shared/url.js';\nimport { ALL_EXTENSIONS, extractKeysFromFile } from './extract.js';\nimport { KeyStore } from './key-store.js';\nimport { getGlobalState, getManifest } from './manifest.js';\nimport { scan } from './scan.js';\nimport { mapErrorsToSource } from './source-map.js';\nimport type { ExtractionError } from './types.js';\n\nconst VIRTUAL_MODULE_ID = 'virtual:@astroscope/i18n/manifest';\nconst isVirtualModuleId = (id: string) => id === VIRTUAL_MODULE_ID || id.startsWith(`${VIRTUAL_MODULE_ID}?`);\n\nconst RESOLVED_VIRTUAL_MODULE_ID = `\\0${VIRTUAL_MODULE_ID}`;\nconst isResolvedVirtualModuleId = (id: string) =>\n id === RESOLVED_VIRTUAL_MODULE_ID || id.startsWith(`\\0${VIRTUAL_MODULE_ID}?`);\n\nconst CONFIG_VIRTUAL_MODULE_ID = 'virtual:@astroscope/i18n/config';\nconst RESOLVED_CONFIG_VIRTUAL_MODULE_ID = `\\0${CONFIG_VIRTUAL_MODULE_ID}`;\n\nconst MANIFEST_FILE_NAME = 'i18n-manifest.json';\n\nexport type I18nVitePluginOptions = {\n logger: AstroIntegrationLogger;\n consistency: ConsistencyCheckLevel;\n /** the locale middleware's exclude patterns, null for the default */\n exclude: ExcludePattern[] | null;\n};\n\n/**\n * Describe a non-extractable t() call for a developer.\n */\nfunction formatExtractionError(error: ExtractionError, projectRoot: string): string {\n const location = `${path.relative(projectRoot, error.file)}:${error.line}:${error.column + 1}`;\n\n return `${location} — t('${error.key}') ${error.reason}`;\n}\n\n/**\n * Non-extractable meta is fatal for a production build. The fallback argument is\n * stripped from the bundle, so a key whose fallback could not be read at build\n * time has nothing to fall back to and renders its own key string to end users.\n */\nfunction formatExtractionErrors(errors: ExtractionError[], projectRoot: string): string {\n const list = errors.map((e) => ` - ${formatExtractionError(e, projectRoot)}`).join('\\n');\n\n return (\n `i18n: ${errors.length} t() call(s) have a fallback that cannot be read at build time.\\n\\n` +\n `${list}\\n\\n` +\n `Production builds strip the fallback argument, so these keys would render as their own key string. ` +\n `Fallbacks must be static string literals — pass runtime values as MF2 variables instead:\\n` +\n ' t(\\'some.key\\', \\'Hello {$name}\\', { name })'\n );\n}\n\n/**\n * Vite plugin for i18n extraction, chunk mapping, and loader injection\n *\n * This plugin:\n * 1. Extracts all t() calls using Babel AST parsing\n * 2. Strips fallback arguments in production builds\n * 3. Maps translation keys to output chunks\n * 4. Generates a manifest for syncing with translation providers\n * 5. Injects translation loader into chunks that use t()\n */\nexport function i18nVitePlugin(options: I18nVitePluginOptions): Plugin {\n const { logger, consistency, exclude } = options;\n\n const store = new KeyStore(logger, consistency);\n\n // sync store's computed keys to global state (for dev mode live access)\n const syncGlobalState = () => {\n const state = getGlobalState();\n state.extractedKeys = store.extractedKeys;\n state.version++;\n };\n\n let isBuild = false;\n let projectRoot = '';\n\n return {\n name: '@astroscope/i18n/extract',\n enforce: 'pre',\n\n configResolved(config) {\n const state = getGlobalState();\n state.projectRoot = config.root;\n\n isBuild = config.command === 'build';\n projectRoot = config.root;\n },\n\n async configureServer(server) {\n // astro's build runs an internal dev server; its configureServer must not trigger the eager scan\n if (isBuild || server.config.isProduction) {\n return;\n }\n\n const result = await scan({ projectRoot, logger, consistency });\n\n store.merge(result);\n\n syncGlobalState();\n\n logger.info(`dev mode: scanned ${result.fileToKeys.size} files, found ${result.uniqueKeyCount} keys`);\n\n // surface here what the build would later reject\n for (const error of store.extractionErrors) {\n logger.warn(formatExtractionError(error, projectRoot));\n }\n },\n\n resolveId(id) {\n if (isVirtualModuleId(id)) {\n return `\\0${id}`;\n }\n\n if (id === CONFIG_VIRTUAL_MODULE_ID) {\n return RESOLVED_CONFIG_VIRTUAL_MODULE_ID;\n }\n },\n\n load(id) {\n if (id === RESOLVED_CONFIG_VIRTUAL_MODULE_ID) {\n // exclude patterns may contain RegExp — serialized as code, not JSON\n return `export const exclude = ${exclude ? serializeExcludePatterns(exclude) : 'null'};`;\n }\n\n if (!isResolvedVirtualModuleId(id)) {\n return;\n }\n\n if (isBuild) {\n // in build mode, load manifest from JSON file at runtime\n return `\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst manifestJson = JSON.parse(readFileSync(join(__dirname, '${MANIFEST_FILE_NAME}'), 'utf-8'));\n\nexport const manifest = manifestJson;\nexport function getManifest() { return manifestJson; }\n`;\n }\n\n // in dev mode, import the getManifest function from the plugin\n // this provides live access to the extraction state as files are transformed\n return `\nimport { getManifest as _getManifest } from '@astroscope/i18n/extraction';\nexport const manifest = { keys: [], chunks: {}, scripts: [] };\nexport function getManifest() { return _getManifest(); }\n`;\n },\n\n async transform(code, filename) {\n if (ALL_EXTENSIONS.every((ext) => !filename.endsWith(ext))) {\n return null;\n }\n\n if (filename.includes('node_modules')) {\n return null;\n }\n\n // quick check: skip files without i18n translate import\n if (!code.includes('@astroscope/i18n/translate')) {\n return null;\n }\n\n const result = await extractKeysFromFile({\n filename,\n code,\n stripFallbacks: isBuild,\n });\n\n // only pay for the sourcemap lookup when there is something to report\n // eslint-disable-next-line unicorn/no-this-outside-of-class -- `this` is the vite plugin context\n const errors = result.errors.length > 0 ? mapErrorsToSource(result.errors, this.getCombinedSourcemap()) : [];\n\n store.addFileKeys(filename, result.keys);\n store.addFileErrors(filename, errors);\n\n // dev keeps the fallback argument, so a non-extractable one still renders\n // correctly at runtime — warn here and let the build be the hard gate\n if (!isBuild) {\n for (const error of errors) {\n logger.warn(formatExtractionError(error, projectRoot));\n }\n }\n\n syncGlobalState();\n\n // serialize babel's map to a string: vite accepts string source maps and\n // its structural ExistingRawSourceMap type (vite 8) rejects babel's map shape\n if (isBuild && result.code) {\n return result.map ? { code: result.code, map: JSON.stringify(result.map) } : { code: result.code };\n }\n\n return null; // dev mode: keep fallbacks\n },\n\n generateBundle(_, bundle) {\n const state = getGlobalState();\n\n // reset for each build (server + client run separately)\n state.chunkManifest = {};\n state.scriptChunks = [];\n\n // first pass: collect keys, determine which chunks have i18n, and build direct imports map\n const chunksWithI18n = new Set<string>();\n const chunkNameToFileName = new Map<string, string>();\n const directImports = new Map<string, string[]>(); // chunk → direct imports incl. dynamic (chunks only)\n const scriptEntries: string[] = [];\n\n for (const [fileName, chunk] of Object.entries(bundle)) {\n if (!(chunk.type === 'chunk' && chunk.moduleIds)) {\n continue;\n }\n\n const keys = new Set<string>();\n let hasI18nModule = false;\n\n // collect keys from all (sub)modules in the chunk\n for (const moduleId of chunk.moduleIds) {\n const moduleKeys = store.fileToKeys.get(moduleId);\n\n moduleKeys?.forEach((k) => keys.add(k));\n\n if (store.filesWithI18n.has(moduleId)) {\n hasI18nModule = true;\n }\n }\n\n const chunkName = chunkIdToName(fileName.replace(/\\.js$/, ''));\n\n if (keys.size > 0) {\n state.chunkManifest[chunkName] = [...keys];\n }\n\n if (hasI18nModule) {\n chunksWithI18n.add(chunkName);\n }\n\n chunkNameToFileName.set(chunkName, fileName);\n\n const imports: string[] = [];\n\n for (const importedFile of [...chunk.imports, ...chunk.dynamicImports]) {\n const baseName = importedFile.replace(/^.*\\//, '').replace(/\\.js$/, '');\n\n imports.push(chunkIdToName(baseName));\n }\n\n directImports.set(chunkName, imports);\n\n // astro `<script>` entries and the page script integrations inject are not islands, so no\n // island carries their hashes — collect them so the middleware can bootstrap their chunks\n if ((chunk.isEntry || chunk.isDynamicEntry) && isScriptEntry(chunk.facadeModuleId)) {\n scriptEntries.push(chunkName);\n }\n }\n\n // i18n chunks reachable from script entries (the entry itself included)\n const scriptChunks = new Set<string>();\n\n const walkScripts = (chunkName: string, visited: Set<string>): void => {\n if (visited.has(chunkName)) return;\n\n visited.add(chunkName);\n\n if (Object.hasOwn(state.chunkManifest, chunkName)) {\n scriptChunks.add(chunkName);\n }\n\n const imports = directImports.get(chunkName) ?? [];\n\n for (const imported of imports) {\n walkScripts(imported, visited);\n }\n };\n\n const visited = new Set<string>();\n\n for (const entry of scriptEntries) {\n walkScripts(entry, visited);\n }\n\n state.scriptChunks = [...scriptChunks];\n\n // second pass: inject loaders (prefetching is handled by the islands emitter)\n for (const [fileName, chunk] of Object.entries(bundle)) {\n if (chunk.type !== 'chunk') continue;\n\n const chunkName = chunkIdToName(fileName.replace(/\\.js$/, ''));\n\n if (!chunksWithI18n.has(chunkName)) continue;\n\n // the loader awaits the chunk's own translation chunk before the module body runs\n const loaderCode =\n `if(typeof window!=='undefined'&&window.__i18n__){` +\n `const _=window.__i18n__,h=_.hashes[${JSON.stringify(chunkName)}];` +\n `if(h)await import(\\`/_i18n/\\${_.locale}/${chunkName}.\\${h}.js\\`);` +\n `}`;\n\n const s = new MagicString(chunk.code);\n\n s.prepend(loaderCode);\n chunk.code = s.toString();\n\n if (chunk.map) {\n chunk.map = s.generateMap({ hires: true }) as typeof chunk.map;\n }\n }\n\n logger.info(`manifest: ${Object.keys(state.chunkManifest).length} chunks, ${store.uniqueKeyCount} keys`);\n\n // a fallback that could not be read is a broken string in production\n const extractionErrors = store.extractionErrors;\n\n if (extractionErrors.length > 0) {\n throw new Error(formatExtractionErrors(extractionErrors, projectRoot));\n }\n\n if (consistency === 'error' && store.hasErrors) {\n throw new Error('i18n: build failed due to translation key inconsistencies');\n }\n },\n\n writeBundle(outputOptions) {\n // only emit from client build (has full chunk mapping, runs after server build)\n // write to server directory so it's not publicly accessible (same location as the virtual module)\n // eslint-disable-next-line unicorn/no-this-outside-of-class -- `this` is the vite plugin context\n if (!(this.environment.name === 'client' && outputOptions.dir)) {\n return;\n }\n\n const chunksDir = path.resolve(outputOptions.dir, '..', 'server', 'chunks');\n const manifestPath = path.join(chunksDir, MANIFEST_FILE_NAME);\n\n if (fs.existsSync(chunksDir)) {\n fs.writeFileSync(manifestPath, JSON.stringify(getManifest()));\n } else {\n logger.error(`server chunks directory not found, cannot write manifest to ${manifestPath}`);\n }\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astroscope/i18n",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "i18n for Astro + React islands - MessageFormat 2, automatic tree-shaking, parallel loading, any translation source",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -66,24 +66,24 @@
|
|
|
66
66
|
"homepage": "https://github.com/entwico/astroscope/tree/main/packages/i18n#readme",
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@astrojs/compiler": "^4.0.0",
|
|
69
|
-
"@babel/core": "^8.0.
|
|
69
|
+
"@babel/core": "^8.0.10",
|
|
70
70
|
"@babel/preset-typescript": "^8.0.7",
|
|
71
71
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
72
|
-
"magic-string": "^1.4.
|
|
73
|
-
"messageformat": "^4.
|
|
72
|
+
"magic-string": "^1.4.5",
|
|
73
|
+
"messageformat": "^4.1.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@astroscope/node": "^4.
|
|
77
|
-
"@babel/types": "^8.0.
|
|
76
|
+
"@astroscope/node": "^4.2.0",
|
|
77
|
+
"@babel/types": "^8.0.10",
|
|
78
78
|
"@types/babel__core": "^7.20.5",
|
|
79
|
-
"astro": "^7.
|
|
80
|
-
"tsdown": "^0.23.
|
|
79
|
+
"astro": "^7.4.4",
|
|
80
|
+
"tsdown": "^0.23.4",
|
|
81
81
|
"typescript": "^6.0.3",
|
|
82
|
-
"vite": "^8.3.
|
|
83
|
-
"vitest": "^5.0.
|
|
82
|
+
"vite": "^8.3.5",
|
|
83
|
+
"vitest": "^5.0.5"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
|
-
"@astroscope/node": "^4.
|
|
86
|
+
"@astroscope/node": "^4.2.0",
|
|
87
87
|
"astro": "^7.0.0",
|
|
88
88
|
"vite": "^8.0.0"
|
|
89
89
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"i18n-C4nlkQTf.d.ts","names":[],"sources":["../src/server/types.ts","../src/server/i18n.ts"],"mappings":";;;;;;;KAKY,oDACuB,aAAa,OAAO;;;;KAK3C;EACV;EAEA,cAAc;EAEd,iBAAiB;EACjB,UAAU;;;;;;;;KCJA,kBAAkB,KAAK;KAEvB;EACV;EACA;EACA,WAAW;;;;;EAKX,SAAS;;KAGN;EACH;EACA;EACA,UAAU;EACV,QAAQ;;cAGJ;UACI;UAGA;UAGA;UAGA;UAGA;UAGA;UAGA;UAGA;UAGA;;UAGA;UAaA;EAYF,UAAU,QAAQ,aAAa;EAkDrC;EAIA,aAAa;EAQb,gBAAgB,gBAAgB,KAAK;EAqBrC,gBAAgB,iBAAiB;EA8BjC,wBAAwB,iBAAiB;;;;;;EAmBzC,eAAe;EAQf,UAAU,iBAAiB;;;;;;EAS3B,aAAa,gBAAgB,oBAAoB;;;;;;EA4CjD,qBAAqB,iBAAiB;;;;;EAoBtC,MAAM;;EAmBN,mBAAmB;IAAqB;IAAgB;;;cAK7C,MAAI"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rich-BWszYAzS.d.ts","names":[],"sources":["../src/shared/rich.ts"],"mappings":";;;;;;;;;;;;;;KAiBY,eAAe,eAAe,gBAAgB,oBAAoB,SAAS"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-BC-tQ30r.d.ts","names":[],"sources":["../src/extraction/types.ts"],"mappings":";;;;;KA8BY;EACV;EACA,MAAM;;EAEN;;;;;KAMU,gBAAgB;;;;KAKhB;EACV,MAAM;EACN,QAAQ;;;;;;EAMR"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-BjTD5hUT.d.ts","names":[],"sources":["../src/shared/types.ts"],"mappings":";;;;KAGY;EACV;EACA;;;;;KAMU;EACV;EACA,YAAY,eAAe;EAC3B;;;;;KAMU,uBAAuB,SAAS;;;;KAKhC,uBAAuB,eAAe;;;;KAKtC,kBAAkB;;;;;;KAOlB;EACV;EACA,QAAQ;EACR,cAAc;;;;;;;;;;;;;;;;KAiBJ,qBACV,aACA,MAAM,0BACN,SAAS"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-LT4Zo0bM.d.ts","names":[],"sources":["../src/integration/types.ts"],"mappings":";;;;;;;;;;KAUY;UAEK;;;;;EAKf,cAAc;;;;;EAKd,UAAU"}
|