Skip to content

Commit 79620f5

Browse files
petebacondarwinjosephperrott
authored andcommitted
refactor(localize): avoid free-standing FileSystem functions (angular#39006)
These free standing functions rely upon the "current" `FileSystem`, but it is safer to explicitly pass the `FileSystem` into functions or classes that need it. Fixes angular#38711 PR Close angular#39006
1 parent ac3aa04 commit 79620f5

File tree

16 files changed

+155
-87
lines changed

16 files changed

+155
-87
lines changed

packages/localize/src/tools/src/extract/extraction.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ export class MessageExtractor {
5252
sourceRoot: this.basePath,
5353
filename,
5454
plugins: [
55-
makeEs2015ExtractPlugin(messages, this.localizeName),
56-
makeEs5ExtractPlugin(messages, this.localizeName),
55+
makeEs2015ExtractPlugin(this.fs, messages, this.localizeName),
56+
makeEs5ExtractPlugin(this.fs, messages, this.localizeName),
5757
],
5858
code: false,
5959
ast: false

packages/localize/src/tools/src/extract/main.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
* Use of this source code is governed by an MIT-style license that can be
77
* found in the LICENSE file at https://angular.io/license
88
*/
9-
import {getFileSystem, setFileSystem, NodeJSFileSystem, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system';
9+
import {setFileSystem, NodeJSFileSystem, AbsoluteFsPath, FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
1010
import {ConsoleLogger, Logger, LogLevel} from '@angular/compiler-cli/src/ngtsc/logging';
1111
import {ɵParsedMessage} from '@angular/localize';
1212
import * as glob from 'glob';
1313
import * as yargs from 'yargs';
1414

15-
import {DiagnosticHandlingStrategy, Diagnostics} from '../diagnostics';
15+
import {DiagnosticHandlingStrategy} from '../diagnostics';
1616

1717
import {checkDuplicateMessages} from './duplicates';
1818
import {MessageExtractor} from './extraction';
@@ -97,8 +97,8 @@ if (require.main === module) {
9797
.help()
9898
.parse(args);
9999

100-
const fs = new NodeJSFileSystem();
101-
setFileSystem(fs);
100+
const fileSystem = new NodeJSFileSystem();
101+
setFileSystem(fileSystem);
102102

103103
const rootPath = options.r;
104104
const sourceFilePaths = glob.sync(options.s, {cwd: rootPath, nodir: true});
@@ -119,6 +119,7 @@ if (require.main === module) {
119119
useLegacyIds: options.useLegacyIds,
120120
duplicateMessageHandling,
121121
formatOptions,
122+
fileSystem,
122123
});
123124
}
124125

@@ -166,6 +167,10 @@ export interface ExtractTranslationsOptions {
166167
* A collection of formatting options to pass to the translation file serializer.
167168
*/
168169
formatOptions?: FormatOptions;
170+
/**
171+
* The file-system abstraction to use.
172+
*/
173+
fileSystem: FileSystem;
169174
}
170175

171176
export function extractTranslations({
@@ -179,8 +184,8 @@ export function extractTranslations({
179184
useLegacyIds,
180185
duplicateMessageHandling,
181186
formatOptions = {},
187+
fileSystem: fs,
182188
}: ExtractTranslationsOptions) {
183-
const fs = getFileSystem();
184189
const basePath = fs.resolve(rootPath);
185190
const extractor = new MessageExtractor(fs, logger, {basePath, useSourceMaps});
186191

@@ -196,7 +201,7 @@ export function extractTranslations({
196201

197202
const outputPath = fs.resolve(rootPath, output);
198203
const serializer =
199-
getSerializer(format, sourceLocale, fs.dirname(outputPath), useLegacyIds, formatOptions);
204+
getSerializer(format, sourceLocale, fs.dirname(outputPath), useLegacyIds, formatOptions, fs);
200205
const translationFile = serializer.serialize(messages);
201206
fs.ensureDir(fs.dirname(outputPath));
202207
fs.writeFile(outputPath, translationFile);
@@ -208,18 +213,20 @@ export function extractTranslations({
208213

209214
export function getSerializer(
210215
format: string, sourceLocale: string, rootPath: AbsoluteFsPath, useLegacyIds: boolean,
211-
formatOptions: FormatOptions = {}): TranslationSerializer {
216+
formatOptions: FormatOptions = {}, fs: FileSystem): TranslationSerializer {
212217
switch (format) {
213218
case 'xlf':
214219
case 'xlif':
215220
case 'xliff':
216-
return new Xliff1TranslationSerializer(sourceLocale, rootPath, useLegacyIds, formatOptions);
221+
return new Xliff1TranslationSerializer(
222+
sourceLocale, rootPath, useLegacyIds, formatOptions, fs);
217223
case 'xlf2':
218224
case 'xlif2':
219225
case 'xliff2':
220-
return new Xliff2TranslationSerializer(sourceLocale, rootPath, useLegacyIds, formatOptions);
226+
return new Xliff2TranslationSerializer(
227+
sourceLocale, rootPath, useLegacyIds, formatOptions, fs);
221228
case 'xmb':
222-
return new XmbTranslationSerializer(rootPath, useLegacyIds);
229+
return new XmbTranslationSerializer(rootPath, useLegacyIds, fs);
223230
case 'json':
224231
return new SimpleJsonTranslationSerializer(sourceLocale);
225232
}

packages/localize/src/tools/src/extract/source_files/es2015_extract_plugin.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,26 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8+
import {FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
89
import {ɵParsedMessage, ɵparseMessage} from '@angular/localize';
910
import {NodePath, PluginObj} from '@babel/core';
1011
import {TaggedTemplateExpression} from '@babel/types';
1112

1213
import {getLocation, isGlobalIdentifier, isNamedIdentifier, unwrapExpressionsFromTemplateLiteral, unwrapMessagePartsFromTemplateLiteral} from '../../source_file_utils';
1314

1415
export function makeEs2015ExtractPlugin(
15-
messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj {
16+
fs: FileSystem, messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj {
1617
return {
1718
visitor: {
1819
TaggedTemplateExpression(path: NodePath<TaggedTemplateExpression>) {
1920
const tag = path.get('tag');
2021
if (isNamedIdentifier(tag, localizeName) && isGlobalIdentifier(tag)) {
2122
const quasiPath = path.get('quasi');
2223
const [messageParts, messagePartLocations] =
23-
unwrapMessagePartsFromTemplateLiteral(quasiPath.get('quasis'));
24+
unwrapMessagePartsFromTemplateLiteral(quasiPath.get('quasis'), fs);
2425
const [expressions, expressionLocations] =
25-
unwrapExpressionsFromTemplateLiteral(quasiPath);
26-
const location = getLocation(quasiPath);
26+
unwrapExpressionsFromTemplateLiteral(quasiPath, fs);
27+
const location = getLocation(fs, quasiPath);
2728
const message = ɵparseMessage(
2829
messageParts, expressions, location, messagePartLocations, expressionLocations);
2930
messages.push(message);

packages/localize/src/tools/src/extract/source_files/es5_extract_plugin.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,26 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8+
import {FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
89
import {ɵParsedMessage, ɵparseMessage} from '@angular/localize';
910
import {NodePath, PluginObj} from '@babel/core';
1011
import {CallExpression} from '@babel/types';
1112

1213
import {getLocation, isGlobalIdentifier, isNamedIdentifier, unwrapMessagePartsFromLocalizeCall, unwrapSubstitutionsFromLocalizeCall} from '../../source_file_utils';
1314

1415
export function makeEs5ExtractPlugin(
15-
messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj {
16+
fs: FileSystem, messages: ɵParsedMessage[], localizeName = '$localize'): PluginObj {
1617
return {
1718
visitor: {
1819
CallExpression(callPath: NodePath<CallExpression>) {
1920
const calleePath = callPath.get('callee');
2021
if (isNamedIdentifier(calleePath, localizeName) && isGlobalIdentifier(calleePath)) {
21-
const [messageParts, messagePartLocations] = unwrapMessagePartsFromLocalizeCall(callPath);
22-
const [expressions, expressionLocations] = unwrapSubstitutionsFromLocalizeCall(callPath);
22+
const [messageParts, messagePartLocations] =
23+
unwrapMessagePartsFromLocalizeCall(callPath, fs);
24+
const [expressions, expressionLocations] =
25+
unwrapSubstitutionsFromLocalizeCall(callPath, fs);
2326
const [messagePartsArg, expressionsArg] = callPath.get('arguments');
24-
const location = getLocation(messagePartsArg, expressionsArg);
27+
const location = getLocation(fs, messagePartsArg, expressionsArg);
2528
const message = ɵparseMessage(
2629
messageParts, expressions, location, messagePartLocations, expressionLocations);
2730
messages.push(message);

packages/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {AbsoluteFsPath, relative} from '@angular/compiler-cli/src/ngtsc/file_system';
8+
import {AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
99
import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize';
1010

1111
import {FormatOptions, validateOptions} from './format_options';
@@ -28,7 +28,7 @@ const LEGACY_XLIFF_MESSAGE_LENGTH = 40;
2828
export class Xliff1TranslationSerializer implements TranslationSerializer {
2929
constructor(
3030
private sourceLocale: string, private basePath: AbsoluteFsPath, private useLegacyIds: boolean,
31-
private formatOptions: FormatOptions = {}) {
31+
private formatOptions: FormatOptions = {}, private fs: FileSystem = getFileSystem()) {
3232
validateOptions('Xliff1TranslationSerializer', [['xml:space', ['preserve']]], formatOptions);
3333
}
3434

@@ -115,7 +115,7 @@ export class Xliff1TranslationSerializer implements TranslationSerializer {
115115

116116
private serializeLocation(xml: XmlFile, location: ɵSourceLocation): void {
117117
xml.startTag('context-group', {purpose: 'location'});
118-
this.renderContext(xml, 'sourcefile', relative(this.basePath, location.file));
118+
this.renderContext(xml, 'sourcefile', this.fs.relative(this.basePath, location.file));
119119
const endLineString = location.end !== undefined && location.end.line !== location.start.line ?
120120
`,${location.end.line + 1}` :
121121
'';

packages/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {AbsoluteFsPath, relative} from '@angular/compiler-cli/src/ngtsc/file_system';
8+
import {AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
99
import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize';
1010

1111
import {FormatOptions, validateOptions} from './format_options';
@@ -28,7 +28,7 @@ export class Xliff2TranslationSerializer implements TranslationSerializer {
2828
private currentPlaceholderId = 0;
2929
constructor(
3030
private sourceLocale: string, private basePath: AbsoluteFsPath, private useLegacyIds: boolean,
31-
private formatOptions: FormatOptions = {}) {
31+
private formatOptions: FormatOptions = {}, private fs: FileSystem = getFileSystem()) {
3232
validateOptions('Xliff1TranslationSerializer', [['xml:space', ['preserve']]], formatOptions);
3333
}
3434

@@ -64,7 +64,7 @@ export class Xliff2TranslationSerializer implements TranslationSerializer {
6464
end !== undefined && end.line !== start.line ? `,${end.line + 1}` : '';
6565
this.serializeNote(
6666
xml, 'location',
67-
`${relative(this.basePath, file)}:${start.line + 1}${endLineString}`);
67+
`${this.fs.relative(this.basePath, file)}:${start.line + 1}${endLineString}`);
6868
}
6969
if (message.description) {
7070
this.serializeNote(xml, 'description', message.description);

packages/localize/src/tools/src/extract/translation_files/xmb_translation_serializer.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {AbsoluteFsPath, relative} from '@angular/compiler-cli/src/ngtsc/file_system';
8+
import {AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
99
import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize';
1010

1111
import {extractIcuPlaceholders} from './icu_parsing';
@@ -21,7 +21,9 @@ import {XmlFile} from './xml_file';
2121
* @publicApi used by CLI
2222
*/
2323
export class XmbTranslationSerializer implements TranslationSerializer {
24-
constructor(private basePath: AbsoluteFsPath, private useLegacyIds: boolean) {}
24+
constructor(
25+
private basePath: AbsoluteFsPath, private useLegacyIds: boolean,
26+
private fs: FileSystem = getFileSystem()) {}
2527

2628
serialize(messages: ɵParsedMessage[]): string {
2729
const ids = new Set<string>();
@@ -74,7 +76,8 @@ export class XmbTranslationSerializer implements TranslationSerializer {
7476
const endLineString = location.end !== undefined && location.end.line !== location.start.line ?
7577
`,${location.end.line + 1}` :
7678
'';
77-
xml.text(`${relative(this.basePath, location.file)}:${location.start.line}${endLineString}`);
79+
xml.text(
80+
`${this.fs.relative(this.basePath, location.file)}:${location.start.line}${endLineString}`);
7881
xml.endTag('source');
7982
}
8083

0 commit comments

Comments
 (0)