@@ -5,6 +5,7 @@ import util = require('util');
5
5
import _ = require( "lodash" ) ;
6
6
import i18next = require( 'i18next' ) ;
7
7
import Backend = require( 'i18next-node-fs-backend' ) ;
8
+ import { SourceMapConsumer } from 'source-map' ;
8
9
const VirtualModulePlugin = require ( 'virtual-module-webpack-plugin' ) ;
9
10
10
11
const readFile = util . promisify ( fs . readFile ) ;
@@ -88,7 +89,7 @@ function getPath(template: string, language?: string, namespace?: string) {
88
89
return template ;
89
90
}
90
91
91
- export type CollectedKeys = { [ language : string ] : { [ namespace : string ] : { [ key : string ] : string [ ] } } } ;
92
+ export type CollectedKeys = { [ language : string ] : { [ namespace : string ] : { [ key : string ] : { [ module : string ] : [ number , number ] } } } } ;
92
93
93
94
function removeMap < T > ( obj : _ . Dictionary < T > , keys : ( string | undefined ) [ ] ) {
94
95
for ( const emptyKey of keys ) {
@@ -107,6 +108,7 @@ export default class I18nextPlugin {
107
108
protected missingKeys : CollectedKeys = { } ;
108
109
protected startTime = Date . now ( ) ;
109
110
protected prevTimestamps : { [ file : string ] : number } = { } ;
111
+ protected sourceMaps : { [ key : string ] : SourceMapConsumer } = { } ;
110
112
111
113
public constructor ( option : Option ) {
112
114
this . option = _ . defaults ( option , {
@@ -152,14 +154,19 @@ export default class I18nextPlugin {
152
154
watchfile => ( this . prevTimestamps [ watchfile ] || this . startTime ) < ( compilation . fileTimestamps [ watchfile ] || Infinity )
153
155
) ;
154
156
157
+ for ( const changed of changedFiles ) {
158
+ delete this . sourceMaps [ changed ] ;
159
+ }
155
160
removeMap ( this . missingKeys , _ . map ( this . missingKeys , ( namespaces , lng ) =>
156
- _ . size ( removeMap ( namespaces , _ . map ( namespaces , ( values , ns ) =>
157
- _ . size ( removeMap ( values , _ . map ( values , ( deps , key ) => {
158
- _ . remove ( deps , dep => _ . includes ( changedFiles , dep ) ) ;
159
-
160
- return deps . length === 0 ? key : undefined ;
161
- } ) ) ) === 0 ? ns : undefined
162
- ) ) ) === 0 ? lng : undefined
161
+ _ . isEmpty ( removeMap ( namespaces , _ . map ( namespaces , ( values , ns ) =>
162
+ _ . isEmpty ( removeMap ( values , _ . map ( values , ( deps , key ) => {
163
+ for ( const changed of changedFiles ) {
164
+ delete deps [ changed ] ;
165
+ }
166
+
167
+ return _ . isEmpty ( deps ) ? key : undefined ;
168
+ } ) ) ) ? ns : undefined
169
+ ) ) ) ? lng : undefined
163
170
) ) ;
164
171
165
172
data . normalModuleFactory . plugin (
@@ -243,7 +250,12 @@ export default class I18nextPlugin {
243
250
} ) ;
244
251
const keys = _ . sortedUniq ( _ . sortBy ( _ . keys ( values ) ) ) ;
245
252
stream . write ( "{\n" ) ;
246
- stream . write ( _ . map ( keys , key => `\t"${ key } ": "${ key } "` ) . join ( ",\n" ) ) ;
253
+ stream . write ( _ . map (
254
+ keys ,
255
+ key => `\t"${ key } ": [\n${ _ . map (
256
+ values [ key ] , ( pos , module ) => `\t\t"${ _ . trim ( JSON . stringify ( path . relative ( this . context , module ) ) , '"' ) } (${ pos } )"` ) . join ( "\n" )
257
+ } \n\t]`) . join ( ",\n" )
258
+ ) ;
247
259
stream . end ( "\n}" ) ;
248
260
stream . on ( "close" , ( ) => resolve ( ) ) ;
249
261
@@ -267,27 +279,32 @@ export default class I18nextPlugin {
267
279
268
280
protected static onTranslateFunctionCall ( this : wp . Parser , plugin : I18nextPlugin , expr : wp . Expression ) {
269
281
const args = expr . arguments . map ( ( arg : any ) => extractArgs ( arg , plugin . warningOnCompilation . bind ( plugin ) ) ) ;
282
+ const resource = this . state . current . resource ;
283
+ if ( plugin . sourceMaps [ resource ] === undefined ) {
284
+ plugin . sourceMaps [ resource ] = new SourceMapConsumer ( this . state . current . _source . _sourceMap ) ;
285
+ }
286
+ const sourceMap = plugin . sourceMaps [ resource ] ;
287
+ const startPos = sourceMap . originalPositionFor ( expr . loc . start ) ;
288
+ const pos = [ resource , startPos . line , startPos . column ] ;
270
289
271
290
for ( const lng of plugin . option . languages ) {
272
291
const keyOrKeys : string | string [ ] = args [ 0 ] ;
273
292
const option : i18next . TranslationOptionsBase = Object . assign ( _ . defaults ( args [ 1 ] , { } ) , {
274
293
lng,
275
- defaultValue : this . state . current . resource
294
+ defaultValue : pos
276
295
} as i18next . TranslationOptionsBase ) ;
277
296
i18next . t ( keyOrKeys , option ) ;
278
297
}
279
298
}
280
299
281
- protected onKeyMissing ( lng : string , ns : string , key : string , moduleName : string ) {
282
- const p = [ lng , ns , key ] ;
283
- let arr : string [ ] = _ . get ( this . missingKeys , p ) ;
300
+ protected onKeyMissing ( lng : string , ns : string , key : string , pos : [ string , number , number ] ) {
301
+ const p = [ lng , ns , key , pos [ 0 ] ] ;
302
+ let arr : [ number , number ] [ ] = _ . get ( this . missingKeys , p ) ;
284
303
if ( arr === undefined ) {
285
304
_ . set ( this . missingKeys , p , [ ] ) ;
286
305
arr = _ . get ( this . missingKeys , p ) ;
287
306
}
288
- if ( arr . indexOf ( moduleName ) === - 1 ) {
289
- arr . push ( moduleName ) ;
290
- }
307
+ arr . push ( [ pos [ 1 ] , pos [ 2 ] ] ) ;
291
308
}
292
309
293
310
protected warningOnCompilation ( msg : string ) {
0 commit comments