Files
angular/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts
George Kalpakas e6850a3c51 fix(ngcc): do not add trailing commas in UMD imports (#34545)
Previously, if `UmdRenderingFormatter#addImports()` was called with an
empty list of imports to add (i.e. no new imports were needed), it would
add trailing commas in several locations (arrays, function arguments,
function parameters), thus making the code imcompatible with legacy
browsers such as IE11.

This commit fixes it by ensuring that no trailing commas are added if
`addImports()` is called with an empty list of imports.
This is a follow-up to #34353.

Fixes #34525

PR Close #34545
2020-01-07 10:42:06 -08:00

88 lines
3.4 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {dirname, relative} from 'canonical-path';
import * as ts from 'typescript';
import MagicString from 'magic-string';
import {Reexport} from '../../../src/ngtsc/imports';
import {Import, ImportManager} from '../../../src/ngtsc/translator';
import {ExportInfo} from '../analysis/private_declarations_analyzer';
import {isRequireCall} from '../host/commonjs_host';
import {NgccReflectionHost} from '../host/ngcc_host';
import {Esm5RenderingFormatter} from './esm5_rendering_formatter';
import {stripExtension} from './utils';
/**
* A RenderingFormatter that works with CommonJS files, instead of `import` and `export` statements
* the module is an IIFE with a factory function call with dependencies, which are defined in a
* wrapper function for AMD, CommonJS and global module formats.
*/
export class CommonJsRenderingFormatter extends Esm5RenderingFormatter {
constructor(protected commonJsHost: NgccReflectionHost, isCore: boolean) {
super(commonJsHost, isCore);
}
/**
* Add the imports below any in situ imports as `require` calls.
*/
addImports(output: MagicString, imports: Import[], file: ts.SourceFile): void {
// Avoid unnecessary work if there are no imports to add.
if (imports.length === 0) {
return;
}
const insertionPoint = this.findEndOfImports(file);
const renderedImports =
imports.map(i => `var ${i.qualifier} = require('${i.specifier}');\n`).join('');
output.appendLeft(insertionPoint, renderedImports);
}
/**
* Add the exports to the bottom of the file.
*/
addExports(
output: MagicString, entryPointBasePath: string, exports: ExportInfo[],
importManager: ImportManager, file: ts.SourceFile): void {
exports.forEach(e => {
const basePath = stripExtension(e.from);
const relativePath = './' + relative(dirname(entryPointBasePath), basePath);
const namedImport = entryPointBasePath !== basePath ?
importManager.generateNamedImport(relativePath, e.identifier) :
{symbol: e.identifier, moduleImport: null};
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`;
output.append(exportStr);
});
}
addDirectExports(
output: MagicString, exports: Reexport[], importManager: ImportManager,
file: ts.SourceFile): void {
for (const e of exports) {
const namedImport = importManager.generateNamedImport(e.fromModule, e.symbolName);
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
const exportStr = `\nexports.${e.asAlias} = ${importNamespace}${namedImport.symbol};`;
output.append(exportStr);
}
}
protected findEndOfImports(sf: ts.SourceFile): number {
for (const statement of sf.statements) {
if (ts.isExpressionStatement(statement) && isRequireCall(statement.expression)) {
continue;
}
const declarations = ts.isVariableStatement(statement) ?
Array.from(statement.declarationList.declarations) :
[];
if (declarations.some(d => !d.initializer || !isRequireCall(d.initializer))) {
return statement.getStart();
}
}
return 0;
}
}