Skip to content

Commit 1825cca

Browse files
committed
Simplify the localized serve scenario.
1 parent e5baafb commit 1825cca

6 files changed

Lines changed: 153 additions & 129 deletions

File tree

build-tests/localization-plugin-test/serve.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const child_process = require('child_process');
33
const path = require('path');
44
const process = require('process');
55

6-
const { LocJsonPreprocessor } = require('@rushstack/localization-plugin');
6+
const { LocFilePreprocessor } = require('@rushstack/localization-plugin');
77

88
function executeCommand(command) {
99
console.log('---> ' + command);
@@ -16,10 +16,11 @@ FileSystem.ensureEmptyFolder('dist');
1616
FileSystem.ensureEmptyFolder('lib');
1717
FileSystem.ensureEmptyFolder('temp');
1818

19-
LocJsonPreprocessor.preprocessLocJsonFiles({
19+
const preprocessor = new LocFilePreprocessor({
2020
srcFolder: path.resolve(__dirname, 'src'),
2121
generatedTsFolder: path.resolve(__dirname, 'temp', 'loc-json-ts')
2222
});
23+
preprocessor.generateTypings();
2324

2425
// Run Webpack
2526
executeCommand('node node_modules/webpack-dev-server/bin/webpack-dev-server');

common/reviews/api/localization-plugin.api.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ export interface ILocalizationPluginOptions {
4040
localizationStatsDropPath?: string;
4141
// (undocumented)
4242
localizedStrings: ILocales;
43-
// (undocumented)
44-
serveLocale: IDefaultLocaleOptions;
4543
}
4644

4745
// @public (undocumented)

webpack/localization-plugin/src/LocalizationPlugin.ts

Lines changed: 80 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ export interface IDefaultLocaleOptions {
5050
export interface ILocalizationPluginOptions {
5151
localizedStrings: ILocales;
5252
defaultLocale: IDefaultLocaleOptions;
53-
serveLocale: IDefaultLocaleOptions;
5453
filesToIgnore?: string[];
5554
localizationStatsDropPath?: string;
5655
localizationStatsCallback?: (stats: ILocalizationStats) => void;
@@ -133,8 +132,7 @@ export class LocalizationPlugin implements Webpack.Plugin {
133132
private _locales: Set<string>;
134133
private _localeNamePlaceholder: IStringPlaceholder;
135134
private _defaultLocale: string;
136-
private _serveLocale: string;
137-
private _usePassthroughForServe: boolean | undefined;
135+
138136
/**
139137
* The outermost map's keys are the locale names.
140138
* The middle map's keys are the resolved, uppercased file names.
@@ -153,22 +151,23 @@ export class LocalizationPlugin implements Webpack.Plugin {
153151
throw new Error('The localization plugin requires webpack 4');
154152
}
155153

156-
// https://github.com/webpack/webpack-dev-server/pull/1929/files#diff-15fb51940da53816af13330d8ce69b4eR66
157-
const isWebpackDevServer: boolean = process.env.WEBPACK_DEV_SERVER === 'true';
158-
159-
const errors: Error[] = this._initializeAndValidateOptions(compiler.options, isWebpackDevServer);
154+
const errors: Error[] = this._initializeAndValidateOptions(compiler.options);
160155

161156
if (errors.length > 0) {
162157
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation: Webpack.compilation.Compilation) => {
163158
compilation.errors.push(...errors);
164159
});
165160

166-
this._amendWebpackConfiguration(compiler.options);
161+
this._amendWebpackConfigurationForInPlaceLocFiles(compiler.options);
167162

168163
return;
169164
}
170165

171-
if (this._locales.size === 1) {
166+
// https://github.com/webpack/webpack-dev-server/pull/1929/files#diff-15fb51940da53816af13330d8ce69b4eR66
167+
const isWebpackDevServer: boolean = process.env.WEBPACK_DEV_SERVER === 'true';
168+
if (isWebpackDevServer) {
169+
this._amendWebpackConfigurationForInPlaceLocFiles(compiler.options);
170+
} else if (this._locales.size === 1) {
172171
const singleLocale: string = Array.from(this._locales.keys())[0];
173172
const resolvedStrings: Map<string, Map<string, string>> = this._resolvedLocalizedStrings.get(singleLocale)!;
174173
this._amendWebpackConfigurationForSingleLocale(
@@ -179,19 +178,6 @@ export class LocalizationPlugin implements Webpack.Plugin {
179178
resolvedStrings
180179
}
181180
);
182-
} else if (isWebpackDevServer) {
183-
const firstLocaleName: string = Array.from(this._locales.keys())[0];
184-
const singleLocale: string = this._usePassthroughForServe ? firstLocaleName : this._serveLocale;
185-
186-
const resolvedStrings: Map<string, Map<string, string>> = this._resolvedLocalizedStrings.get(singleLocale)!;
187-
this._amendWebpackConfigurationForSingleLocale(
188-
compiler.options,
189-
{
190-
localeName: this._serveLocale || firstLocaleName,
191-
passthroughLocale: !!this._usePassthroughForServe,
192-
resolvedStrings
193-
}
194-
);
195181
} else {
196182
this._amendWebpackConfigurationForMultiLocale(compiler.options);
197183

@@ -402,17 +388,17 @@ export class LocalizationPlugin implements Webpack.Plugin {
402388
}
403389

404390
private _amendWebpackConfigurationForMultiLocale(configuration: Webpack.Configuration): void {
405-
this._amendWebpackConfiguration(
391+
this._addRulesAndWarningLoaderToConfiguration(
406392
configuration,
407-
(rules: Webpack.RuleSetRule[]) => {
408-
rules.push({
393+
[
394+
{
409395
test: (filePath: string) => this._locFiles.has(filePath),
410396
loader: path.resolve(__dirname, 'loaders', 'LocJsonLoader.js'),
411397
options: {
412398
pluginInstance: this
413399
}
414-
});
415-
}
400+
}
401+
]
416402
);
417403
}
418404

@@ -432,51 +418,93 @@ export class LocalizationPlugin implements Webpack.Plugin {
432418
);
433419
}
434420

435-
this._amendWebpackConfiguration(
421+
const loader: string = path.resolve(__dirname, 'loaders', 'SingleLocaleLoader.js');
422+
const loaderOptions: Webpack.RuleSetQuery = {
423+
resolvedStrings: options.resolvedStrings,
424+
passthroughLocale: options.passthroughLocale
425+
};
426+
427+
this._addRulesAndWarningLoaderToConfiguration(
436428
configuration,
437-
(rules: Webpack.RuleSetRule[]) => {
438-
rules.push({
429+
[
430+
{
439431
test: {
440432
and: [
441433
(filePath: string) => this._locFiles.has(filePath),
442434
/\.loc\.json$/i
443435
]
444436
},
445-
loader: path.resolve(__dirname, 'loaders', 'SingleLocaleLoader.js'),
446-
options: {
447-
resolvedStrings: options.resolvedStrings,
448-
passthroughLocale: options.passthroughLocale
449-
}
450-
});
451-
452-
rules.push({
437+
loader: loader,
438+
options: loaderOptions
439+
},
440+
{
453441
test: {
454442
and: [
455443
(filePath: string) => this._locFiles.has(filePath),
456444
/\.resx$/i
457445
]
458446
},
459447
use: [
448+
require.resolve('json-loader'),
460449
{
461-
loader: require.resolve('json-loader')
462-
},
463-
{
464-
loader: path.resolve(__dirname, 'loaders', 'SingleLocaleLoader.js'),
465-
options: {
466-
resolvedStrings: options.resolvedStrings,
467-
passthroughLocale: options.passthroughLocale
468-
}
450+
loader: loader,
451+
options: loaderOptions
469452
}
470453
]
471-
});
472-
}
454+
}
455+
]
456+
);
457+
}
458+
459+
private _amendWebpackConfigurationForInPlaceLocFiles(configuration: Webpack.Configuration): void {
460+
const loader: string = path.resolve(__dirname, 'loaders', 'InPlaceLocFileLoader.js');
461+
462+
this._addRulesToConfiguration(
463+
configuration,
464+
[
465+
{
466+
test: /\.loc\.json$/i,
467+
loader: loader
468+
},
469+
{
470+
test: /\.resx$/i,
471+
use: [
472+
require.resolve('json-loader'),
473+
loader
474+
]
475+
}
476+
]
473477
);
474478
}
475479

476-
private _amendWebpackConfiguration(
480+
private _addRulesAndWarningLoaderToConfiguration(
477481
configuration: Webpack.Configuration,
478-
beforeWarningLoader: (rules: Webpack.RuleSetRule[]) => void = () => { /* no-op */ }
482+
rules: Webpack.RuleSetRule[]
479483
): void {
484+
this._addRulesToConfiguration(
485+
configuration,
486+
[
487+
...rules,
488+
{
489+
test: {
490+
and: [
491+
(filePath: string) => !this._locFiles.has(filePath),
492+
(filePath: string) => !this._filesToIgnore.has(filePath),
493+
{
494+
or: [
495+
/\.loc\.json$/i,
496+
/\.resx$/i
497+
]
498+
}
499+
]
500+
},
501+
loader: path.resolve(__dirname, 'loaders', 'MissingLocDataWarningLoader.js')
502+
}
503+
]
504+
);
505+
}
506+
507+
private _addRulesToConfiguration(configuration: Webpack.Configuration, rules: Webpack.RuleSetRule[]): void {
480508
if (!configuration.module) {
481509
configuration.module = {
482510
rules: []
@@ -487,26 +515,10 @@ export class LocalizationPlugin implements Webpack.Plugin {
487515
configuration.module.rules = [];
488516
}
489517

490-
beforeWarningLoader(configuration.module.rules);
491-
492-
configuration.module.rules.push({
493-
test: {
494-
and: [
495-
(filePath: string) => !this._locFiles.has(filePath),
496-
(filePath: string) => !this._filesToIgnore.has(filePath),
497-
{
498-
or: [
499-
/\.loc\.json$/i,
500-
/\.resx$/i
501-
]
502-
}
503-
]
504-
},
505-
loader: path.resolve(__dirname, 'loaders', 'MissingLocDataWarningLoader.js')
506-
});
518+
configuration.module.rules.push(...rules);
507519
}
508520

509-
private _initializeAndValidateOptions(configuration: Webpack.Configuration, isWebpackDevServer: boolean): Error[] {
521+
private _initializeAndValidateOptions(configuration: Webpack.Configuration): Error[] {
510522
const errors: Error[] = [];
511523

512524
// START configuration
@@ -708,42 +720,6 @@ export class LocalizationPlugin implements Webpack.Plugin {
708720
}
709721
// END options.defaultLocale
710722

711-
// START options.serveLocale
712-
if (isWebpackDevServer) {
713-
if (
714-
!this._options.serveLocale ||
715-
(!this._options.serveLocale.locale && !this._options.serveLocale.usePassthroughLocale)
716-
) {
717-
if (this._locales.size === 1) {
718-
this._serveLocale = this._locales.entries[0];
719-
} else {
720-
errors.push(new Error(
721-
'Either options.serveLocale.locale must be provided or options.serveLocale.usePassthroughLocale ' +
722-
'must be set to true if more than one locale\'s data is provided. An arbitrary locale will be used.'
723-
));
724-
}
725-
} else {
726-
const { locale, usePassthroughLocale, passthroughLocaleName } = this._options.serveLocale;
727-
if (locale && usePassthroughLocale) {
728-
errors.push(new Error(
729-
'Either options.serveLocale.locale must be provided or options.serveLocale.usePassthroughLocale ' +
730-
'must be set to true, but not both. An arbitrary locale will be used.'
731-
));
732-
} else if (usePassthroughLocale) {
733-
this._serveLocale = passthroughLocaleName || 'passthrough';
734-
this._usePassthroughForServe = true;
735-
} else if (locale) {
736-
this._serveLocale = locale;
737-
if (!this._locales.has(locale)) {
738-
errors.push(new Error(`The specified serve locale "${locale}" was not provided in the localized data`));
739-
}
740-
} else {
741-
errors.push(new Error('Unknown error occurred processing serve locale.'));
742-
}
743-
}
744-
}
745-
// END options.serveLocale
746-
747723
return errors;
748724
}
749725

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
import { loader } from 'webpack';
5+
6+
import { ILocFile } from '../interfaces';
7+
import { LocFileParser } from '../utilities/LocFileParser';
8+
9+
export default function (this: loader.LoaderContext, content: string): string {
10+
const locFilePath: string = this.resourcePath;
11+
12+
const locJsonFileData: ILocFile = LocFileParser.parseLocFile({
13+
filePath: locFilePath,
14+
loggerOptions: { writeError: this.emitError, writeWarning: this.emitWarning },
15+
content
16+
});
17+
const resultObject: { [stringName: string]: string } = {};
18+
for (const stringName in locJsonFileData) { // eslint-disable-line guard-for-in
19+
resultObject[stringName] = locJsonFileData[stringName].value;
20+
}
21+
22+
return JSON.stringify(resultObject);
23+
}

webpack/localization-plugin/src/loaders/SingleLocaleLoader.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33

44
import { loader } from 'webpack';
55
import * as loaderUtils from 'loader-utils';
6-
import * as jju from 'jju';
76

87
import { ILocFile } from '../interfaces';
9-
import { Constants } from '../utilities/Constants';
10-
import { ResxReader } from '../utilities/ResxReader';
11-
import { Logging } from '../utilities/Logging';
8+
import { LocFileParser } from '../utilities/LocFileParser';
129

1310
export interface ISingleLocaleLoaderOptions {
1411
/**
@@ -37,23 +34,11 @@ export default function (this: loader.LoaderContext, content: string): string {
3734
`Strings for file ${locFilePath} were not provided in the LocalizationPlugin configuration.`
3835
));
3936
} else {
40-
let locJsonFileData: ILocFile;
41-
if (/\.resx$/i.test(locFilePath)) {
42-
locJsonFileData = ResxReader.readResxAsLocFile(
43-
content,
44-
{
45-
...Logging.getLoggingFunctions({ writeError: this.emitError, writeWarning: this.emitWarning }),
46-
resxFilePath: locFilePath
47-
}
48-
);
49-
} else {
50-
locJsonFileData = jju.parse(content);
51-
try {
52-
Constants.LOC_JSON_SCHEMA.validateObject(locJsonFileData, locFilePath);
53-
} catch (e) {
54-
this.emitError(`The loc file is invalid. Error: ${e}`);
55-
}
56-
}
37+
const locJsonFileData: ILocFile = LocFileParser.parseLocFile({
38+
filePath: locFilePath,
39+
loggerOptions: { writeError: this.emitError, writeWarning: this.emitWarning },
40+
content
41+
});
5742

5843
for (const stringName in locJsonFileData) {
5944
if (!stringMap.has(stringName)) {

0 commit comments

Comments
 (0)