@@ -6,21 +6,41 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
66const packageRoot = dirname ( __dirname ) ;
77
88/**
9- * Convert CommonJS locale module to ESM named export format
10- * From: module.exports={messages:JSON.parse("{...}")};
11- * To: export const messages=JSON.parse("{...}");
9+ * Copy Lingui-compiled locale catalogs from `src/**\/locales/` into `dist/`.
1210 *
13- * The locale-registry imports `{ messages }` as a named export,
14- * so we must produce a named export, not `export default`.
11+ * Lingui is configured with `compileNamespace: 'es'`, so it emits ESM modules
12+ * as `*.mjs` with a named export:
13+ *
14+ * /\*eslint-disable*\/export const messages=JSON.parse("{...}");
15+ *
16+ * For `dist/` we need:
17+ * - `*.js` — ESM consumers (identical contents to the `.mjs`, just renamed
18+ * so rslib's `.mjs → .js` import rewriting in compiled output
19+ * resolves correctly).
20+ * - `*.cjs` — CommonJS consumers, synthesized by wrapping the RHS of the
21+ * single `export const messages=…;` statement in
22+ * `module.exports = { messages: … };`.
23+ * - `*.json` — copied verbatim for runtime catalog loaders.
24+ *
25+ * The CJS synthesis is intentionally tied to the exact shape Lingui emits.
26+ * If Lingui ever changes its ESM output format, we fail loudly (throw) so
27+ * the breakage surfaces here rather than as a silent link-time error in a
28+ * downstream package (see `locale-registry.ts`, which imports
29+ * `{ messages }` as a named export).
1530 */
16- function convertToESM ( content : string ) : string {
17- const prefix = 'module.exports={messages:' ;
18- const start = content . indexOf ( prefix ) ;
19- if ( start !== - 1 ) {
20- const value = content . slice ( start + prefix . length , - 2 ) ; // strip trailing " };"
21- return `/*eslint-disable*/export const messages=${ value } ;` ;
31+ const ESM_PREFIX = '/*eslint-disable*/export const messages=' ;
32+
33+ function esmToCjs ( esmContent : string , srcFile : string ) : string {
34+ const trimmed = esmContent . trimEnd ( ) ;
35+ if ( ! trimmed . startsWith ( ESM_PREFIX ) || ! trimmed . endsWith ( ';' ) ) {
36+ throw new Error (
37+ `plugin-copy-locales: unexpected Lingui ESM output in ${ srcFile } .\n` +
38+ `Expected a file starting with '${ ESM_PREFIX } ' and ending with ';'.\n` +
39+ `Got:\n${ trimmed . slice ( 0 , 200 ) } ...`
40+ ) ;
2241 }
23- return content . replace ( / m o d u l e \. e x p o r t s \s * = \s * / , 'export default ' ) ;
42+ const rhs = trimmed . slice ( ESM_PREFIX . length , - 1 ) ; // strip prefix + trailing `;`
43+ return `/*eslint-disable*/module.exports={messages:${ rhs } };` ;
2444}
2545
2646export function pluginCopyLocales ( ) {
@@ -41,53 +61,26 @@ export function pluginCopyLocales() {
4161 const distLocales = join ( distDir , relativePath ) ;
4262
4363 if ( existsSync ( srcLocales ) ) {
44- // Ensure dist directory exists
4564 mkdirSync ( distLocales , { recursive : true } ) ;
4665
47- // Process each locale file
48- const files = readdirSync ( srcLocales ) ;
49- for ( const file of files ) {
50- if ( file . endsWith ( '.js' ) ) {
51- const srcFile = join ( srcLocales , file ) ;
52- const content = readFileSync ( srcFile , 'utf-8' ) ;
53-
54- // Write .cjs (CommonJS format)
55- const cjsFile = join ( distLocales , file . replace ( '.js' , '.cjs' ) ) ;
56- writeFileSync ( cjsFile , content , 'utf-8' ) ;
66+ for ( const file of readdirSync ( srcLocales ) ) {
67+ const srcFile = join ( srcLocales , file ) ;
5768
58- // Write .js (ESM named export format)
59- const esmFile = join ( distLocales , file ) ;
60- const esmContent = convertToESM ( content ) ;
61- writeFileSync ( esmFile , esmContent , 'utf-8' ) ;
62- } else if ( file . endsWith ( '.mjs' ) ) {
63- // ESM compiled files (from compileNamespace: 'es') — already correct format
64- const srcFile = join ( srcLocales , file ) ;
69+ if ( file . endsWith ( '.mjs' ) ) {
6570 const content = readFileSync ( srcFile , 'utf-8' ) ;
66- const baseName = file . replace ( ' .mjs' , '' ) ;
71+ const baseName = file . replace ( / \ .m j s $ / , '' ) ;
6772
68- // Write .js only if not already created from a .js source above
69- const jsFile = join ( distLocales , `${ baseName } .js` ) ;
70- if ( ! existsSync ( jsFile ) ) {
71- writeFileSync ( jsFile , content , 'utf-8' ) ;
72- }
73+ // ESM: same contents, .js extension for rslib import rewriting.
74+ writeFileSync ( join ( distLocales , `${ baseName } .js` ) , content , 'utf-8' ) ;
7375
74- // Write .cjs only if not already created from a .js source above
75- const cjsFile = join ( distLocales , `${ baseName } .cjs` ) ;
76- if ( ! existsSync ( cjsFile ) ) {
77- const cjsContent = content
78- . replace (
79- / \/ \* e s l i n t - d i s a b l e \* \/ e x p o r t c o n s t m e s s a g e s = / ,
80- '/*eslint-disable*/module.exports={messages:'
81- )
82- . replace ( / ; $ / , '};' ) ;
83- writeFileSync ( cjsFile , cjsContent , 'utf-8' ) ;
84- }
76+ // CJS: synthesize from the known ESM shape.
77+ writeFileSync (
78+ join ( distLocales , `${ baseName } .cjs` ) ,
79+ esmToCjs ( content , srcFile ) ,
80+ 'utf-8'
81+ ) ;
8582 } else if ( file . endsWith ( '.json' ) ) {
86- // Copy JSON files as-is
87- const srcFile = join ( srcLocales , file ) ;
88- const destFile = join ( distLocales , file ) ;
89- const content = readFileSync ( srcFile , 'utf-8' ) ;
90- writeFileSync ( destFile , content , 'utf-8' ) ;
83+ writeFileSync ( join ( distLocales , file ) , readFileSync ( srcFile , 'utf-8' ) , 'utf-8' ) ;
9184 }
9285 }
9386
0 commit comments