@@ -16,7 +16,11 @@ import {
1616 startTranslationServer ,
1717 type TranslationServer ,
1818} from "../translation-server" ;
19- import { getMetadataPath , MetadataManager } from "../metadata/manager" ;
19+ import {
20+ cleanupExistingMetadata ,
21+ getMetadataPath ,
22+ MetadataManager ,
23+ } from "../metadata/manager" ;
2024import { createLingoConfig } from "../utils/config-factory" ;
2125import { logger } from "../utils/logger" ;
2226import { getCacheDir } from "../utils/path-helpers" ;
@@ -36,60 +40,29 @@ let translationServer: TranslationServer;
3640
3741const PLUGIN_NAME = "lingo-compiler" ;
3842
39- export async function cleanup (
40- server : TranslationServer | undefined ,
41- metadataFilePath : string ,
42- ) {
43- // General cleanup. Delete metadata and stop the server if any was started.
44- logger . debug ( `Attempting to cleanup metadata file: ${ metadataFilePath } ` ) ;
45-
46- try {
47- fs . unlinkSync ( metadataFilePath ) ;
48- logger . info ( `🧹 Cleaned up build metadata file: ${ metadataFilePath } ` ) ;
49- } catch ( error : any ) {
50- // Ignore if file doesn't exist
51- if ( error . code === "ENOENT" ) {
52- logger . debug (
53- `Metadata file already deleted or doesn't exist: ${ metadataFilePath } ` ,
54- ) ;
55- } else {
56- logger . warn ( `Failed to cleanup metadata file: ${ error . message } ` ) ;
57- }
58- }
59-
60- if ( server ) {
61- logger . debug ( "Stopping translation server..." ) ;
62- await server . stop ( ) ;
63- logger . info ( "Translation server stopped" ) ;
64- }
65- }
66-
6743/**
6844 * Universal plugin for Lingo.dev compiler
6945 * Supports Vite, Webpack, Rollup, and esbuild
7046 */
7147export const lingoUnplugin = createUnplugin < LingoPluginOptions > ( ( options ) => {
7248 const config = createLingoConfig ( options ) ;
7349
74- const isDev = process . env . NODE_ENV === "development" ;
50+ // Won't work for webpack most likely. Use mode there to set correct environment in configs.
51+ const isDev = config . environment === "development" ;
7552 const startPort = config . dev . translationServerStartPort ;
7653 const metadataFilePath = getMetadataPath ( config ) ;
7754
78- // Warn if NODE_ENV is not set (common webpack issue)
79- if ( ! process . env . NODE_ENV ) {
80- logger . warn (
81- "⚠️ process.env.NODE_ENV is undefined. Lingo will assume production mode.\n" +
82- " For webpack users: Add webpack.DefinePlugin to set process.env.NODE_ENV.\n" +
83- " See: https://webpack.js.org/plugins/define-plugin/" ,
84- ) ;
85- }
86-
8755 return {
8856 name : PLUGIN_NAME ,
8957 enforce : "pre" , // Run before other plugins (especially before React plugin)
9058
9159 vite : {
9260 async buildStart ( ) {
61+ cleanupExistingMetadata ( metadataFilePath ) ;
62+
63+ registerCleanupOnCurrentProcess ( {
64+ cleanup : ( ) => cleanupExistingMetadata ( metadataFilePath ) ,
65+ } ) ;
9366 if ( isDev && ! translationServer ) {
9467 translationServer = await startTranslationServer ( {
9568 startPort,
@@ -112,7 +85,6 @@ export const lingoUnplugin = createUnplugin<LingoPluginOptions>((options) => {
11285 }
11386 } ,
11487
115- // Doesn't fire when error happens
11688 async buildEnd ( ) {
11789 if ( ! isDev ) {
11890 try {
@@ -123,15 +95,64 @@ export const lingoUnplugin = createUnplugin<LingoPluginOptions>((options) => {
12395 } ) ;
12496 } catch ( error ) {
12597 logger . error ( "Build-time translation processing failed:" , error ) ;
126- // throw error;
12798 }
12899 }
129-
130- logger . warn ( "Build end" ) ;
131- await cleanup ( translationServer , metadataFilePath ) ;
132100 } ,
133101 } ,
134102
103+ webpack ( compiler ) {
104+ // if (config.isEmbeddedIntoNext) {
105+ // return;
106+ // }
107+ compiler . hooks . watchRun . tapPromise ( PLUGIN_NAME , async ( ) => {
108+ cleanupExistingMetadata ( metadataFilePath ) ;
109+ registerCleanupOnCurrentProcess ( {
110+ cleanup : ( ) => cleanupExistingMetadata ( metadataFilePath ) ,
111+ } ) ;
112+
113+ if ( compiler . options . mode === "development" && ! translationServer ) {
114+ translationServer = await startTranslationServer ( {
115+ startPort,
116+ onError : ( err ) => {
117+ logger . error ( "Translation server error:" , err ) ;
118+ } ,
119+ onReady : ( port ) => {
120+ logger . info (
121+ `Translation server started successfully on port: ${ port } ` ,
122+ ) ;
123+ } ,
124+ config : { ...config , environment : compiler . options . mode } ,
125+ } ) ;
126+ registerCleanupOnCurrentProcess ( {
127+ asyncCleanup : async ( ) => {
128+ await translationServer . stop ( ) ;
129+ } ,
130+ } ) ;
131+ }
132+ } ) ;
133+
134+ compiler . hooks . additionalPass . tapPromise ( PLUGIN_NAME , async ( ) => {
135+ if ( compiler . options . mode === "production" ) {
136+ try {
137+ await processBuildTranslations ( {
138+ config : { ...config , environment : compiler . options . mode } ,
139+ publicOutputPath : "public/translations" ,
140+ metadataFilePath : metadataFilePath ,
141+ } ) ;
142+ } catch ( error ) {
143+ logger . error ( "Build-time translation processing failed:" , error ) ;
144+ throw error ;
145+ }
146+ }
147+ } ) ;
148+
149+ // Duplicates process handlers, but won't hurt
150+ compiler . hooks . shutdown . tapPromise ( PLUGIN_NAME , async ( ) => {
151+ cleanupExistingMetadata ( metadataFilePath ) ;
152+ await translationServer ?. stop ( ) ;
153+ } ) ;
154+ } ,
155+
135156 resolveId ( id ) {
136157 if ( id === "@lingo.dev/compiler/dev-config" ) {
137158 // Return a virtual module ID (prefix with \0 to mark it as virtual)
@@ -169,6 +190,7 @@ export const lingoUnplugin = createUnplugin<LingoPluginOptions>((options) => {
169190 } ,
170191
171192 load ( id ) {
193+ logger . warn ( `ID: ${ id } ` ) ;
172194 if ( id === "\0virtual:lingo-dev-config" ) {
173195 const serverUrl =
174196 translationServer ?. getUrl ( ) || `http://127.0.0.1:${ startPort } ` ;
@@ -218,6 +240,7 @@ export function persistLocale(locale) {
218240 } ,
219241 handler : async ( code , id ) => {
220242 try {
243+ // TODO (AleksandrSl 13/12/2025): How do I get Webpack mode here?
221244 // Transform the component
222245 const result = transformComponent ( {
223246 code,
@@ -243,7 +266,6 @@ export function persistLocale(locale) {
243266
244267 await metadataManager . saveMetadataWithEntries ( result . newEntries ) ;
245268
246- // Log new translations discovered (in dev mode)
247269 if ( isDev ) {
248270 logger . info (
249271 `Found ${ result . newEntries . length } translatable text(s) in ${ id } ` ,
0 commit comments