Skip to content

Commit 94add30

Browse files
clydinfilipesilva
authored andcommitted
fix(@angular-devkit/build-angular): validate extracted i18n messages for duplicates
This change will analyze the extract i18n messages for duplicates and issue warnings for each case. This provides the same default behavior as the standalone message extractor contained within `@angular/localize`. Configurability of the behavior of a detected duplicate (ignore, warn, error) will be added in a future feature.
1 parent 54aa891 commit 94add30

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

packages/angular_devkit/build_angular/src/extract-i18n/index.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,37 @@ export async function execute(
252252
return webpackResult;
253253
}
254254

255+
const basePath = config.context || projectRoot;
256+
257+
const { checkDuplicateMessages } = await import(
258+
// tslint:disable-next-line: trailing-comma
259+
'@angular/localize/src/tools/src/extract/duplicates'
260+
);
261+
262+
// The filesystem is used to create a relative path for each file
263+
// from the basePath. This relative path is then used in the error message.
264+
const checkFileSystem = {
265+
relative(from: string, to: string): string {
266+
return path.relative(from, to);
267+
},
268+
};
269+
const diagnostics = checkDuplicateMessages(
270+
// tslint:disable-next-line: no-any
271+
checkFileSystem as any,
272+
ivyMessages,
273+
'warning',
274+
// tslint:disable-next-line: no-any
275+
basePath as any,
276+
);
277+
if (diagnostics.messages.length > 0) {
278+
context.logger.warn(diagnostics.formatDiagnostics(''));
279+
}
280+
255281
// Serialize all extracted messages
256282
const serializer = await getSerializer(
257283
format,
258284
i18n.sourceLocale,
259-
config.context || projectRoot,
285+
basePath,
260286
useLegacyIds,
261287
);
262288
const content = serializer.serialize(ivyMessages);

packages/angular_devkit/build_angular/src/extract-i18n/works_spec.ts

+37
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ describe('Extract i18n Target', () => {
3939
}
4040
}, 30000);
4141

42+
it('does not emit the application files', async () => {
43+
host.appendToFile('src/app/app.component.html', '<p i18n>i18n test</p>');
44+
45+
const run = await architect.scheduleTarget(extractI18nTargetSpec);
46+
47+
await expectAsync(run.result).toBeResolvedTo(jasmine.objectContaining({ success: true }));
48+
49+
await run.stop();
50+
51+
expect(host.scopedSync().exists(normalize('dist/app/main.js'))).toBeFalse();
52+
}, 30000);
53+
4254
it('shows errors', async () => {
4355
const logger = new logging.Logger('');
4456
const logs: string[] = [];
@@ -126,4 +138,29 @@ describe('Extract i18n Target', () => {
126138
expect(virtualFs.fileBufferToString(host.scopedSync().read(extractionFile)))
127139
.toMatch(/i18n test/);
128140
}, 30000);
141+
142+
// DISABLED_FOR_VE
143+
(veEnabled ? xit : it)('issues warnings for duplicate message identifiers', async () => {
144+
host.appendToFile(
145+
'src/app/app.component.ts',
146+
'const c = $localize`:@@message-2:message contents`; const d = $localize`:@@message-2:different message contents`;',
147+
);
148+
149+
const logger = new logging.Logger('');
150+
const logs: string[] = [];
151+
logger.subscribe((e) => logs.push(e.message));
152+
153+
const run = await architect.scheduleTarget(extractI18nTargetSpec, undefined, { logger });
154+
await expectAsync(run.result).toBeResolvedTo(jasmine.objectContaining({ success: true }));
155+
156+
await run.stop();
157+
158+
expect(host.scopedSync().exists(extractionFile)).toBe(true);
159+
160+
const fullLog = logs.join();
161+
expect(fullLog).toContain(
162+
'Duplicate messages with id',
163+
);
164+
165+
}, 30000);
129166
});

0 commit comments

Comments
 (0)