@automattic/i18n-loader-webpack-plugin 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +3 -0
- package/CHANGELOG.md +6 -0
- package/README.md +22 -13
- package/package.json +2 -3
- package/src/I18nLoaderPlugin.js +55 -84
- package/src/I18nLoaderRuntimeModule.js +31 -83
package/.eslintignore
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.0] - 2022-01-25
|
|
9
|
+
### Changed
|
|
10
|
+
- BREAKING: Remove the downloading logic from the runtime by requiring a "loader" module rather than a "state" module. This gives more flexibility for different implementations in the future.
|
|
11
|
+
- Update documentation for moving of the handling of package→plugin path mapping into jetpack-composer-plugin and jetpack-assets.
|
|
12
|
+
|
|
8
13
|
## [1.0.2] - 2022-01-18
|
|
9
14
|
### Changed
|
|
10
15
|
- General: update required node version to v16.13.2
|
|
@@ -20,5 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
20
25
|
### Added
|
|
21
26
|
- Initial release.
|
|
22
27
|
|
|
28
|
+
[2.0.0]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v1.0.2...v2.0.0
|
|
23
29
|
[1.0.2]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v1.0.1...v1.0.2
|
|
24
30
|
[1.0.1]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v1.0.0...v1.0.1
|
package/README.md
CHANGED
|
@@ -28,9 +28,10 @@ This goes in the `plugins` section of your Webpack config, e.g.
|
|
|
28
28
|
Parameters recognized by the plugin are:
|
|
29
29
|
|
|
30
30
|
- `textdomain`: The text domain used in the JavaScript code. This is required, unless nothing in your JS actually uses [@wordpress/i18n].
|
|
31
|
-
- `
|
|
31
|
+
- `loaderModule`: The name of a module supplying the i18n loader. See [Loader module](#loader-module) below for details.
|
|
32
|
+
- `loaderMethod`: The name of the function from `loaderModule` to download the i18n. See [Loader module](#loader-module) below for details.
|
|
32
33
|
- `target`: The target of the build: 'plugin' (the default), 'theme', or 'core'. This is used to determine where in WordPress's languages directory to look for the translation files.
|
|
33
|
-
- `path`: See [Webpack context](#webpack-context)
|
|
34
|
+
- `path`: See [Webpack context](#webpack-context) below for details.
|
|
34
35
|
- `ignoreModules`: If some bundles in your build depend on [@wordpress/i18n] for purposes other than translating strings, i18n-loader-webpack-plugin will none the less count them as "using @wordpress/i18n" which may result in it trying to load translations for bundles that do not need it. This option may be used to ignore the relevant source files when examining the bundles.
|
|
35
36
|
|
|
36
37
|
The value may be a function, which will be passed the file path relative to [Webpack's context] and the Webpack Module object and which should return true if the file should be ignored, or a string or RegExp to be compared with the relative file path, or an array of such strings, RegExps, and/or functions.
|
|
@@ -50,23 +51,32 @@ But if for some reason you want to do it manually, something like this in your W
|
|
|
50
51
|
}
|
|
51
52
|
```
|
|
52
53
|
|
|
53
|
-
###
|
|
54
|
+
### Loader module
|
|
54
55
|
|
|
55
|
-
In order to load the translations, the generated bundle needs to
|
|
56
|
+
In order to load the translations, the generated bundle needs to call a method to do the actual downloading at runtime. This is handled by loading a module that must be externalized by your Webpack configuration.
|
|
56
57
|
|
|
57
|
-
The default module name is `@wordpress/jp-i18n-
|
|
58
|
+
The default module name is `@wordpress/jp-i18n-loader`, which will automatically be externalized by [@wordpress/dependency-extraction-webpack-plugin], which will also register it as a dependency for "wp-jp-i18n-loader" in the generated `.asset.php` file. That, in turn, is provided by the [automattic/jetpack-assets] Composer package (which also provides an `Automattic\Jetpack\Assets::register_script()` function to easily consume the `.asset.php` file).
|
|
58
59
|
|
|
59
|
-
But if for some reason you don't want to use those packages, you can set the plugin's `
|
|
60
|
+
But if for some reason you don't want to use those packages, you can set the plugin's `loaderModule` and `loaderMethod` options to point to a different module name, use [Webpack's externals configuration] to externalize it, and appropriate PHP code to provide the corresponding global variable for your externals configuration to retrieve.
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
The loader method might be documented like this:
|
|
63
|
+
```js
|
|
64
|
+
/**
|
|
65
|
+
* Download and register translations for a bundle.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} path - Bundle path being fetched. May have a query part.
|
|
68
|
+
* @param {string} domain - Text domain to register into.
|
|
69
|
+
* @param {string} location - Location for the translation: 'plugin', 'theme', or 'core'.
|
|
70
|
+
* @returns {Promise} Resolved when the translations are registered, or rejected with an `Error`.
|
|
71
|
+
*/
|
|
72
|
+
```
|
|
73
|
+
Most likely the method will separate any query part from the path, hash it, build the download url, fetch it, then register it via `@wordpress/i18n`'s `setLocaleData()` method.
|
|
64
74
|
|
|
65
75
|
### Webpack context
|
|
66
76
|
|
|
67
77
|
WordPress's translation infrastructure generates a file for each JS script named like "_textdomain_-_locale_-_hash_.json". The _hash_ is an MD5 hash of the path of the script file relative to the plugin's root.
|
|
68
78
|
|
|
69
|
-
I18n-loader-webpack-plugin assumes that [Webpack's context] is the base of the WordPress plugin in which the bundles will be included.
|
|
79
|
+
I18n-loader-webpack-plugin assumes that [Webpack's context] is the base of the WordPress package or plugin in which the bundles will be included, and that the [loader module](#loader-module) will handle mapping from package root to plugin root.
|
|
70
80
|
If this is not the case, you'll need to set the plugin's `path` parameter to the relative path from the plugin's root to Webpack's `output.path`.
|
|
71
81
|
|
|
72
82
|
### Other useful Webpack configuration
|
|
@@ -88,9 +98,8 @@ WordPress's plugin infrastructure doesn't natively support Composer packages, so
|
|
|
88
98
|
That won't work for packages needing translation, though, as WordPress's translation infrastructure ignores the `vendor/` directory when looking for strings to be translated.
|
|
89
99
|
You'll need to use something like [automattic/jetpack-composer-plugin] so that the composer packages with translated strings are installed to a different path.
|
|
90
100
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
The consuming plugin will also need to arrange for the [state module](#state-module)'s `domainMap` to include a mapping from your Composer package's textdomain to the plugin's textdomain (prefixed with "plugins/"), as that's where the translations will end up. This may be done using [automattic/jetpack-assets] along with [automattic/jetpack-composer-plugin], as described in the latter's documentation.
|
|
101
|
+
Also, as the translation file will be named using the plugin's textdomain rather than the Composer package's, the consuming plugin will also need to arrange for the [loader module](#loader-module) to fetch the proper file.
|
|
102
|
+
This may be done using [automattic/jetpack-assets] along with [automattic/jetpack-composer-plugin], as described in the latter's documentation.
|
|
94
103
|
|
|
95
104
|
## Security
|
|
96
105
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/i18n-loader-webpack-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A Webpack plugin to load WordPress i18n when Webpack lazy-loads a bundle.",
|
|
5
5
|
"homepage": "https://jetpack.com",
|
|
6
6
|
"bugs": {
|
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
"test-coverage": "jest tests --coverage --collectCoverageFrom='src/**/*.js' --coverageDirectory=\"$COVERAGE_DIR\" --coverageReporters=clover"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"debug": "^4.3.2"
|
|
21
|
-
"md5-es": "^1.8.2"
|
|
20
|
+
"debug": "^4.3.2"
|
|
22
21
|
},
|
|
23
22
|
"devDependencies": {
|
|
24
23
|
"@wordpress/dependency-extraction-webpack-plugin": "3.2.1",
|
package/src/I18nLoaderPlugin.js
CHANGED
|
@@ -31,8 +31,12 @@ const schema = {
|
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
properties: {
|
|
34
|
-
|
|
35
|
-
description: 'Externalized module supplying the i18n
|
|
34
|
+
loaderModule: {
|
|
35
|
+
description: 'Externalized module supplying the i18n loader.',
|
|
36
|
+
type: 'string',
|
|
37
|
+
},
|
|
38
|
+
loaderMethod: {
|
|
39
|
+
description: 'Method on the loader module to call to load the i18n.',
|
|
36
40
|
type: 'string',
|
|
37
41
|
},
|
|
38
42
|
textdomain: {
|
|
@@ -44,7 +48,7 @@ const schema = {
|
|
|
44
48
|
enum: [ 'plugin', 'theme', 'core' ],
|
|
45
49
|
},
|
|
46
50
|
path: {
|
|
47
|
-
description: 'Path (relative to the plugin) to locate the output assets.',
|
|
51
|
+
description: 'Path (relative to the package or plugin) to locate the output assets.',
|
|
48
52
|
type: 'string',
|
|
49
53
|
},
|
|
50
54
|
ignoreModules: {
|
|
@@ -79,7 +83,8 @@ class I18nLoaderPlugin {
|
|
|
79
83
|
|
|
80
84
|
this.options = {
|
|
81
85
|
target: 'plugin',
|
|
82
|
-
|
|
86
|
+
loaderModule: '@wordpress/jp-i18n-loader',
|
|
87
|
+
loaderMethod: 'downloadI18n',
|
|
83
88
|
...options,
|
|
84
89
|
};
|
|
85
90
|
|
|
@@ -114,51 +119,30 @@ class I18nLoaderPlugin {
|
|
|
114
119
|
} );
|
|
115
120
|
|
|
116
121
|
// At the "make" hook, inject Dependency objects into the build so we can get the modules we need.
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
const i18nModuleDep = new I18nLoaderModuleDependency( '@wordpress/i18n' );
|
|
120
|
-
i18nModuleDep.optional = true;
|
|
122
|
+
const loaderModuleDep = new I18nLoaderModuleDependency( this.options.loaderModule );
|
|
123
|
+
loaderModuleDep.optional = true;
|
|
121
124
|
compiler.hooks.make.tapPromise( PLUGIN_NAME, compilation => {
|
|
122
|
-
return Promise
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"You'll need to add the appropriate externals directive to your Webpack config.";
|
|
138
|
-
}
|
|
139
|
-
compilation.errors.push( new webpack.WebpackError( msg ) );
|
|
140
|
-
}
|
|
141
|
-
resolve();
|
|
142
|
-
} );
|
|
143
|
-
} ),
|
|
144
|
-
new Promise( ( resolve, reject ) => {
|
|
145
|
-
compilation.addModuleChain( compiler.context, i18nModuleDep, ( err, module ) => {
|
|
146
|
-
if ( err ) {
|
|
147
|
-
return reject( err );
|
|
148
|
-
}
|
|
149
|
-
// Webpack bug; Until 5.51.0 it didn't pass the module to the callback.
|
|
150
|
-
if ( ! module && ! compilation.moduleGraph.getModule( i18nModuleDep ) ) {
|
|
151
|
-
compilation.errors.push(
|
|
152
|
-
new webpack.WebpackError(
|
|
153
|
-
`${ PLUGIN_NAME }:\nFailed to add i18n module @wordpress/i18n to the build.\n` +
|
|
154
|
-
"You'll need to add @wordpress/dependency-extraction-webpack-plugin or an appropriate externals directive to your Webpack config, or add @wordpress/i18n to your package.json."
|
|
155
|
-
)
|
|
156
|
-
);
|
|
125
|
+
return new Promise( ( resolve, reject ) => {
|
|
126
|
+
compilation.addModuleChain( compiler.context, loaderModuleDep, ( err, module ) => {
|
|
127
|
+
if ( err ) {
|
|
128
|
+
return reject( err );
|
|
129
|
+
}
|
|
130
|
+
// Webpack bug; Until 5.51.0 it didn't pass the module to the callback.
|
|
131
|
+
if ( ! module && ! compilation.moduleGraph.getModule( loaderModuleDep ) ) {
|
|
132
|
+
// prettier-ignore
|
|
133
|
+
let msg = `${ PLUGIN_NAME }:\nFailed to add loader module ${ this.options.loaderModule } to the build.\n`;
|
|
134
|
+
if ( this.options.loaderModule.startsWith( '@wordpress/' ) ) {
|
|
135
|
+
msg +=
|
|
136
|
+
"You'll need to add @wordpress/dependency-extraction-webpack-plugin or an appropriate externals directive to your Webpack config.";
|
|
137
|
+
} else {
|
|
138
|
+
msg +=
|
|
139
|
+
"You'll need to add the appropriate externals directive to your Webpack config.";
|
|
157
140
|
}
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
141
|
+
compilation.errors.push( new webpack.WebpackError( msg ) );
|
|
142
|
+
}
|
|
143
|
+
resolve();
|
|
144
|
+
} );
|
|
145
|
+
} );
|
|
162
146
|
} );
|
|
163
147
|
|
|
164
148
|
compiler.hooks.thisCompilation.tap( PLUGIN_NAME, compilation => {
|
|
@@ -168,10 +152,9 @@ class I18nLoaderPlugin {
|
|
|
168
152
|
* @returns {object} Stuff.
|
|
169
153
|
*/
|
|
170
154
|
function getStuff() {
|
|
171
|
-
const
|
|
172
|
-
const i18nModule = compilation.moduleGraph.getModule( i18nModuleDep );
|
|
155
|
+
const loaderModule = compilation.moduleGraph.getModule( loaderModuleDep );
|
|
173
156
|
|
|
174
|
-
const i18nModules = new Set(
|
|
157
|
+
const i18nModules = new Set();
|
|
175
158
|
for ( const module of compilation.modules ) {
|
|
176
159
|
// rawRequest is for a NormalModule. userRequest is for an ExternalModule. Other types we don't really care about.
|
|
177
160
|
if (
|
|
@@ -183,18 +166,18 @@ class I18nLoaderPlugin {
|
|
|
183
166
|
}
|
|
184
167
|
const i18nModulesArr = [ ...i18nModules ];
|
|
185
168
|
|
|
186
|
-
return {
|
|
169
|
+
return { loaderModule, i18nModulesArr, chunkGraph: compilation.chunkGraph };
|
|
187
170
|
}
|
|
188
171
|
|
|
189
172
|
// After chunks have been optimized (e.g. chunk splitting happened), determine which chunks need
|
|
190
173
|
// our deps and inject them.
|
|
191
174
|
compilation.hooks.afterOptimizeChunks.tap( PLUGIN_NAME, chunks => {
|
|
192
|
-
const {
|
|
193
|
-
if ( !
|
|
194
|
-
debug( "
|
|
175
|
+
const { loaderModule, i18nModulesArr, chunkGraph } = getStuff();
|
|
176
|
+
if ( ! loaderModule ) {
|
|
177
|
+
debug( "Loader module is missing, can't run." );
|
|
195
178
|
return;
|
|
196
179
|
}
|
|
197
|
-
if (
|
|
180
|
+
if ( i18nModulesArr.length <= 0 ) {
|
|
198
181
|
debug( "I18n module is missing, can't run." );
|
|
199
182
|
return;
|
|
200
183
|
}
|
|
@@ -239,41 +222,28 @@ class I18nLoaderPlugin {
|
|
|
239
222
|
}
|
|
240
223
|
|
|
241
224
|
// Inject into the chunk!
|
|
242
|
-
if ( chunkGraph.isModuleInChunk(
|
|
243
|
-
debug( ` Already had ${ this.options.
|
|
225
|
+
if ( chunkGraph.isModuleInChunk( loaderModule, chunk ) ) {
|
|
226
|
+
debug( ` Already had ${ this.options.loaderModule }` );
|
|
244
227
|
} else {
|
|
245
|
-
chunkGraph.connectChunkAndModule( chunk,
|
|
228
|
+
chunkGraph.connectChunkAndModule( chunk, loaderModule );
|
|
246
229
|
chunkGraph.addModuleRuntimeRequirements(
|
|
247
|
-
|
|
230
|
+
loaderModule,
|
|
248
231
|
chunk.runtime,
|
|
249
232
|
new Set( [ RuntimeGlobals.module ] )
|
|
250
233
|
);
|
|
251
234
|
}
|
|
252
|
-
if ( ! i18nModulesArr.some( m => chunkGraph.isModuleInChunk( m, chunk ) ) ) {
|
|
253
|
-
debug( " Didn't itself use @wordpress/i18n" );
|
|
254
|
-
chunkGraph.connectChunkAndModule( chunk, i18nModule );
|
|
255
|
-
}
|
|
256
235
|
|
|
257
236
|
// Any chunk using this as a runtime doesn't itself need the injected modules.
|
|
258
237
|
for ( const c of chunk.getAllReferencedChunks() ) {
|
|
259
238
|
if ( c === chunk ) {
|
|
260
239
|
continue;
|
|
261
240
|
}
|
|
262
|
-
if ( chunkGraph.isModuleInChunk(
|
|
241
|
+
if ( chunkGraph.isModuleInChunk( loaderModule, c ) ) {
|
|
263
242
|
debug(
|
|
264
243
|
// prettier-ignore
|
|
265
|
-
` Removing redundant ${ this.options.
|
|
244
|
+
` Removing redundant ${ this.options.loaderModule } from chunk ${ c.name || c.id || c.debugId }`
|
|
266
245
|
);
|
|
267
|
-
chunkGraph.disconnectChunkAndModule( c,
|
|
268
|
-
}
|
|
269
|
-
for ( const m of i18nModulesArr ) {
|
|
270
|
-
if ( chunkGraph.isModuleInChunk( m, c ) ) {
|
|
271
|
-
debug(
|
|
272
|
-
// prettier-ignore
|
|
273
|
-
` Removing redundant @wordpress/i18n from chunk ${ c.name || c.id || c.debugId }`
|
|
274
|
-
);
|
|
275
|
-
chunkGraph.disconnectChunkAndModule( c, m );
|
|
276
|
-
}
|
|
246
|
+
chunkGraph.disconnectChunkAndModule( c, loaderModule );
|
|
277
247
|
}
|
|
278
248
|
}
|
|
279
249
|
}
|
|
@@ -281,22 +251,22 @@ class I18nLoaderPlugin {
|
|
|
281
251
|
|
|
282
252
|
// This is just for debugging, to see if later optimizations removed our modules.
|
|
283
253
|
compilation.hooks.afterOptimizeChunkModules.tap( PLUGIN_NAME, chunks => {
|
|
284
|
-
const {
|
|
285
|
-
if ( !
|
|
254
|
+
const { loaderModule, i18nModulesArr, chunkGraph } = getStuff();
|
|
255
|
+
if ( ! loaderModule || i18nModulesArr.length <= 0 ) {
|
|
286
256
|
return;
|
|
287
257
|
}
|
|
288
258
|
|
|
289
259
|
debug( 'After optimizations,' );
|
|
290
260
|
for ( const chunk of chunks ) {
|
|
291
|
-
if ( chunkGraph.isModuleInChunk(
|
|
261
|
+
if ( chunkGraph.isModuleInChunk( loaderModule, chunk ) ) {
|
|
292
262
|
debug(
|
|
293
263
|
// prettier-ignore
|
|
294
|
-
` ✅ Chunk ${ chunk.name || chunk.id || chunk.debugId } contains ${ this.options.
|
|
264
|
+
` ✅ Chunk ${ chunk.name || chunk.id || chunk.debugId } contains ${ this.options.loaderModule }`
|
|
295
265
|
);
|
|
296
266
|
} else {
|
|
297
267
|
debug(
|
|
298
268
|
// prettier-ignore
|
|
299
|
-
` ❌ Chunk ${ chunk.name || chunk.id || chunk.debugId } does not contain ${ this.options.
|
|
269
|
+
` ❌ Chunk ${ chunk.name || chunk.id || chunk.debugId } does not contain ${ this.options.loaderModule }`
|
|
300
270
|
);
|
|
301
271
|
}
|
|
302
272
|
if ( i18nModulesArr.some( m => chunkGraph.isModuleInChunk( m, chunk ) ) ) {
|
|
@@ -317,18 +287,19 @@ class I18nLoaderPlugin {
|
|
|
317
287
|
compilation.hooks.runtimeRequirementInTree
|
|
318
288
|
.for( webpack.RuntimeGlobals.ensureChunkHandlers )
|
|
319
289
|
.tap( PLUGIN_NAME, ( chunk, set ) => {
|
|
320
|
-
const {
|
|
321
|
-
if ( !
|
|
290
|
+
const { loaderModule, i18nModulesArr } = getStuff();
|
|
291
|
+
if ( ! loaderModule || i18nModulesArr.length <= 0 ) {
|
|
322
292
|
return;
|
|
323
293
|
}
|
|
324
294
|
|
|
325
295
|
debug( `Queuing runtime module for ${ chunk.name || chunk.id || chunk.debugId }.` );
|
|
296
|
+
set.add( webpack.RuntimeGlobals.getChunkScriptFilename );
|
|
326
297
|
compilation.addRuntimeModule(
|
|
327
298
|
chunk,
|
|
328
299
|
new I18nLoaderRuntimeModule( set, {
|
|
329
300
|
...this.options,
|
|
330
|
-
|
|
331
|
-
|
|
301
|
+
loaderModuleName: this.options.loaderModule,
|
|
302
|
+
loaderModule,
|
|
332
303
|
i18nModulesArr,
|
|
333
304
|
} )
|
|
334
305
|
);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const { default: md5 } = require( 'md5-es' );
|
|
2
1
|
const path = require( 'path' );
|
|
3
2
|
const webpack = require( 'webpack' );
|
|
4
3
|
const { Template, RuntimeGlobals } = webpack;
|
|
@@ -44,12 +43,12 @@ class I18nLoaderRuntimeModule extends webpack.RuntimeModule {
|
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
|
-
* Get
|
|
46
|
+
* Get set of chunks we need to care about.
|
|
48
47
|
*
|
|
49
|
-
* @returns {
|
|
48
|
+
* @returns {Set} Chunk IDs.
|
|
50
49
|
*/
|
|
51
|
-
|
|
52
|
-
const ret = new
|
|
50
|
+
getChunks() {
|
|
51
|
+
const ret = new Set();
|
|
53
52
|
const { chunkGraph } = this.compilation;
|
|
54
53
|
|
|
55
54
|
for ( const chunk of this.chunk.getAllAsyncChunks() ) {
|
|
@@ -61,12 +60,7 @@ class I18nLoaderRuntimeModule extends webpack.RuntimeModule {
|
|
|
61
60
|
module.userRequest === '@wordpress/i18n' ||
|
|
62
61
|
module.dependencies.some( d => d.request === '@wordpress/i18n' )
|
|
63
62
|
) {
|
|
64
|
-
|
|
65
|
-
ret.set( chunk.id, {
|
|
66
|
-
chunkPath,
|
|
67
|
-
query,
|
|
68
|
-
hash: md5.hash( chunkPath ),
|
|
69
|
-
} );
|
|
63
|
+
ret.add( chunk.id );
|
|
70
64
|
continue;
|
|
71
65
|
}
|
|
72
66
|
}
|
|
@@ -76,34 +70,24 @@ class I18nLoaderRuntimeModule extends webpack.RuntimeModule {
|
|
|
76
70
|
|
|
77
71
|
generate() {
|
|
78
72
|
const { chunk, compilation, runtimeOptions, runtimeRequirements } = this;
|
|
79
|
-
const {
|
|
73
|
+
const { loaderModule, loaderMethod, loaderModuleName, textdomain, target } = runtimeOptions;
|
|
80
74
|
const { chunkGraph, runtimeTemplate } = compilation;
|
|
81
|
-
const
|
|
75
|
+
const basepath =
|
|
76
|
+
this.runtimeOptions.path ||
|
|
77
|
+
path.relative( compilation.compiler.context, compilation.outputOptions.path );
|
|
78
|
+
const chunks = this.getChunks();
|
|
82
79
|
|
|
83
|
-
if ( !
|
|
80
|
+
if ( ! chunks.size ) {
|
|
84
81
|
debug( `No async submodules using @wordpress/i18n in ${ this.getChunkPath( chunk ) }.` );
|
|
85
82
|
return null;
|
|
86
83
|
}
|
|
87
84
|
debug( `Adding i18n-loading runtime for ${ this.getChunkPath( chunk ) }.` );
|
|
88
85
|
|
|
89
|
-
// The hooks should have injected the
|
|
90
|
-
if ( ! chunkGraph.isModuleInChunk(
|
|
91
|
-
throw new webpack.WebpackError(
|
|
92
|
-
// prettier-ignore
|
|
93
|
-
`Chunk ${ chunk.name || chunk.id || chunk.debugId } (${ this.getChunkPath( chunk ) }) has submodules using @wordpress/i18n, but is missing the state module ${ stateModuleName }.`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
let i18nModule;
|
|
97
|
-
for ( const m of i18nModulesArr ) {
|
|
98
|
-
if ( chunkGraph.isModuleInChunk( m, chunk ) ) {
|
|
99
|
-
i18nModule = m;
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if ( ! i18nModule ) {
|
|
86
|
+
// The hooks should have injected the loader module. Check to make sure it's still there.
|
|
87
|
+
if ( ! chunkGraph.isModuleInChunk( loaderModule, chunk ) ) {
|
|
104
88
|
throw new webpack.WebpackError(
|
|
105
89
|
// prettier-ignore
|
|
106
|
-
`Chunk ${ chunk.name || chunk.id || chunk.debugId } (${ this.getChunkPath( chunk ) }) has submodules using @wordpress/i18n, but is
|
|
90
|
+
`Chunk ${ chunk.name || chunk.id || chunk.debugId } (${ this.getChunkPath( chunk ) }) has submodules using @wordpress/i18n, but is missing the loader module ${ loaderModuleName }.`
|
|
107
91
|
);
|
|
108
92
|
}
|
|
109
93
|
|
|
@@ -116,82 +100,46 @@ class I18nLoaderRuntimeModule extends webpack.RuntimeModule {
|
|
|
116
100
|
}
|
|
117
101
|
|
|
118
102
|
// Determine the WordPress module name that @wordpress/dependency-extraction-webpack-plugin will (by default) use.
|
|
119
|
-
let depName =
|
|
103
|
+
let depName = loaderModuleName;
|
|
120
104
|
if ( depName.startsWith( '@wordpress/' ) ) {
|
|
121
105
|
// prettier-ignore
|
|
122
106
|
depName = 'wp.' + depName.substring( 11 ).replace( /-([a-z])/g, ( _, letter ) => letter.toUpperCase() );
|
|
123
107
|
}
|
|
124
108
|
|
|
125
|
-
const targetcode = {
|
|
126
|
-
plugin: '"plugins/" + ',
|
|
127
|
-
theme: '"themes/" + ',
|
|
128
|
-
core: '',
|
|
129
|
-
}[ this.runtimeOptions.target ];
|
|
130
|
-
const domaincode = `state.domainMap[textdomain] || ( ${ targetcode }textdomain )`;
|
|
131
|
-
|
|
132
109
|
// Build the runtime code.
|
|
133
110
|
// prettier-ignore
|
|
134
111
|
return Template.asString( [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Template.indent(
|
|
138
|
-
Array.from( chunkInfo.entries(), ( [ k, v ] ) => {
|
|
139
|
-
const items = [
|
|
140
|
-
Template.toNormalComment( v.chunkPath ) + ' ' + JSON.stringify( v.hash ),
|
|
141
|
-
v.query ? JSON.stringify( '?' + v.query ) : '""',
|
|
142
|
-
];
|
|
143
|
-
return `${ JSON.stringify( k ) }: [ ${ items.join( ', ' ) } ]`;
|
|
144
|
-
} ).join( ',\n' )
|
|
145
|
-
),
|
|
112
|
+
'var installedChunks = {',
|
|
113
|
+
Template.indent( Array.from( chunks.values(), id => `${ JSON.stringify( id ) }: 0` ).join( ',\n' ) ),
|
|
146
114
|
'};',
|
|
147
115
|
'',
|
|
148
116
|
'var loadI18n = ' +
|
|
149
|
-
runtimeTemplate.basicFunction( '
|
|
150
|
-
'var
|
|
151
|
-
module:
|
|
152
|
-
chunkGraph,
|
|
153
|
-
request: '@wordpress/i18n',
|
|
154
|
-
runtimeRequirements,
|
|
155
|
-
} ) + ';',
|
|
156
|
-
'var state = ' + runtimeTemplate.moduleExports( {
|
|
157
|
-
module: stateModule,
|
|
117
|
+
runtimeTemplate.basicFunction( 'chunkId', [
|
|
118
|
+
'var loader = ' + runtimeTemplate.moduleExports( {
|
|
119
|
+
module: loaderModule,
|
|
158
120
|
chunkGraph,
|
|
159
|
-
request:
|
|
121
|
+
request: loaderModuleName,
|
|
160
122
|
runtimeRequirements,
|
|
161
123
|
} ) + ';',
|
|
162
|
-
`if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
runtimeTemplate.supportTemplateLiteral()
|
|
168
|
-
? '`${ state.baseUrl }${ ' + domaincode + ' }-${ state.locale }-${ info[0] }.json${ info[1] }`'
|
|
169
|
-
: 'state.baseUrl + ( ' + domaincode + ' ) + "-" + state.locale + "-" + info[0] + ".json" + info[1]',
|
|
170
|
-
] ),
|
|
171
|
-
').then( ' + runtimeTemplate.basicFunction( 'res', [
|
|
172
|
-
'if ( ! res.ok ) throw new Error( "HTTP request failed: " + res.status + " " + res.statusText );',
|
|
173
|
-
'return res.json();',
|
|
174
|
-
] ) + ' ).then( ' + runtimeTemplate.basicFunction( 'data', [
|
|
175
|
-
'var data2 = data.locale_data;',
|
|
176
|
-
'var localeData = data2[ textdomain ] || data2.messages;',
|
|
177
|
-
'localeData[""].domain = textdomain;',
|
|
178
|
-
'i18n.setLocaleData( localeData, textdomain );',
|
|
179
|
-
] ) + ' );',
|
|
124
|
+
`if ( loader && loader.${ loaderMethod } )`,
|
|
125
|
+
Template.indent(
|
|
126
|
+
`return loader.${ loaderMethod }( ${ JSON.stringify( basepath + '/' ) } + ${ RuntimeGlobals.getChunkScriptFilename }( chunkId ), ${ JSON.stringify( textdomain ) }, ${ JSON.stringify( target ) } );`,
|
|
127
|
+
),
|
|
128
|
+
`return Promise.reject( new Error( ${ JSON.stringify( 'I18n loader is not available. Check that WordPress is exporting ' + depName + '.' ) } ) );`,
|
|
180
129
|
] ) + ';',
|
|
181
130
|
'',
|
|
182
|
-
'var installedChunks = {};',
|
|
183
131
|
`${ RuntimeGlobals.ensureChunkHandlers }.wpI18n = ` + runtimeTemplate.basicFunction( 'chunkId, promises', [
|
|
184
132
|
'if ( installedChunks[chunkId] ) {',
|
|
185
133
|
Template.indent( 'promises.push( installedChunks[chunkId] );' ),
|
|
186
|
-
'} else if ( installedChunks[chunkId]
|
|
134
|
+
'} else if ( installedChunks[chunkId] === 0 ) {',
|
|
187
135
|
Template.indent( [
|
|
188
|
-
'promises.push( installedChunks[chunkId] = loadI18n(
|
|
136
|
+
'promises.push( installedChunks[chunkId] = loadI18n( chunkId ).then( ',
|
|
189
137
|
Template.indent( [
|
|
190
|
-
runtimeTemplate.basicFunction( '', [ 'installedChunks[chunkId] =
|
|
138
|
+
runtimeTemplate.basicFunction( '', [ 'installedChunks[chunkId] = false;' ] ) + ',',
|
|
191
139
|
runtimeTemplate.basicFunction( 'e', [
|
|
192
|
-
'
|
|
140
|
+
'installedChunks[chunkId] = 0;',
|
|
193
141
|
"// Log only, we don't want i18n failure to break the entire page.",
|
|
194
|
-
'console.error( "Failed to fetch i18n data:", e );',
|
|
142
|
+
'console.error( "Failed to fetch i18n data: ", e );',
|
|
195
143
|
] ),
|
|
196
144
|
] ),
|
|
197
145
|
') );',
|