feat(bazel): prefix private-export (barred-latin-o) symbols (#23007)

This allows a bundle index to be re-exported by a higher-level module without fear of collisions.
Under bazel, we always set the prefix to be underscore-joined workspace, package, label

PR Close #23007
This commit is contained in:
Alex Eagle 2018-03-26 14:34:44 -07:00 committed by Matias Niemelä
parent 7a406a32fa
commit 27e14b2fb3
6 changed files with 24 additions and 14 deletions

View File

@ -280,6 +280,7 @@ def _write_bundle_index(ctx):
tsconfig = dict(tsc_wrapped_tsconfig(ctx, ctx.files.srcs, ctx.files.srcs), **{
"angularCompilerOptions": {
"flatModuleOutFile": flat_module_out_file,
"flatModulePrivateSymbolPrefix": "_".join([ctx.workspace_name] + ctx.label.package.split("/") + [ctx.label.name, ""]),
},
})
if not ctx.attr.module_name:

View File

@ -15,7 +15,8 @@ describe('flat module index', () => {
require.resolve(`${PKG}/flat_module_filename.metadata.json`), {encoding: 'utf-8'});
expect(metadata).toContain('"__symbolic":"module"');
expect(metadata).toContain('"__symbolic":"reference","module":"@angular/core"');
expect(metadata).toContain('"origins":{"Child":"./child","ɵa":"./parent"}');
expect(metadata).toContain(
'"origins":{"Child":"./child","ɵangular_packages_compiler_cli_integrationtest_bazel_ng_module_test_module_a":"./parent"}');
expect(metadata).toContain('"importAs":"some_npm_module"');
});
});
@ -25,7 +26,8 @@ describe('flat module index', () => {
fs.readFileSync(require.resolve(`${PKG}/flat_module_filename.d.ts`), {encoding: 'utf-8'});
expect(dts).toContain('export * from \'./index\';');
expect(dts).toContain('export { Parent as ɵa } from \'./parent\';');
expect(dts).toContain(
'export { Parent as ɵangular_packages_compiler_cli_integrationtest_bazel_ng_module_test_module_a } from \'./parent\';');
});
});
});

View File

@ -77,8 +77,9 @@ export function createBundleIndexHost<H extends ts.CompilerHost>(
}
const file = files[0];
const indexModule = file.replace(/\.ts$/, '');
const bundler =
new MetadataBundler(indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host));
const bundler = new MetadataBundler(
indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host),
ngOptions.flatModulePrivateSymbolPrefix);
const metadataBundle = bundler.getMetadataBundle();
const metadata = JSON.stringify(metadataBundle.metadata);
const name =

View File

@ -83,11 +83,14 @@ export class MetadataBundler {
private metadataCache = new Map<string, ModuleMetadata|undefined>();
private exports = new Map<string, Symbol[]>();
private rootModule: string;
private privateSymbolPrefix: string;
private exported: Set<Symbol>;
constructor(
private root: string, private importAs: string|undefined, private host: MetadataBundlerHost) {
private root: string, private importAs: string|undefined, private host: MetadataBundlerHost,
privateSymbolPrefix?: string) {
this.rootModule = `./${path.basename(root)}`;
this.privateSymbolPrefix = (privateSymbolPrefix || '').replace(/\W/g, '_');
}
getMetadataBundle(): BundledModule {
@ -244,7 +247,7 @@ export class MetadataBundler {
const exportedNames = new Set(exportedSymbols.map(s => s.name));
let privateName = 0;
function newPrivateName(): string {
function newPrivateName(prefix: string): string {
while (true) {
let digits: string[] = [];
let index = privateName++;
@ -253,8 +256,7 @@ export class MetadataBundler {
digits.unshift(base[index % base.length]);
index = Math.floor(index / base.length);
}
digits.unshift('\u0275');
const result = digits.join('');
const result = `\u0275${prefix}${digits.join('')}`;
if (!exportedNames.has(result)) return result;
}
}
@ -267,7 +269,7 @@ export class MetadataBundler {
let name = symbol.name;
const identifier = `${symbol.declaration!.module}:${symbol.declaration !.name}`;
if (symbol.isPrivate && !symbol.privateName) {
name = newPrivateName();
name = newPrivateName(this.privateSymbolPrefix);
symbol.privateName = name;
}
if (symbolsMap.has(identifier)) {

View File

@ -99,6 +99,10 @@ export interface CompilerOptions extends ts.CompilerOptions {
// meaningful when `flatModuleOutFile` is also supplied. It is otherwise ignored.
flatModuleId?: string;
// A prefix to insert in generated private symbols, e.g. for "my_prefix_" we
// would generate private symbols named like `ɵmy_prefix_a`.
flatModulePrivateSymbolPrefix?: string;
// Whether to generate code for library code.
// If true, produce .ngfactory.ts and .ngstyle.ts files for .d.ts inputs.
// Default is true.

View File

@ -19,10 +19,10 @@ describe('metadata bundler', () => {
it('should be able to bundle a simple library', () => {
const host = new MockStringBundlerHost('/', SIMPLE_LIBRARY);
const bundler = new MetadataBundler('/lib/index', undefined, host);
const bundler = new MetadataBundler('/lib/index', undefined, host, 'prfx_');
const result = bundler.getMetadataBundle();
expect(Object.keys(result.metadata.metadata).sort()).toEqual([
'ONE_CLASSES', 'One', 'OneMore', 'TWO_CLASSES', 'Two', 'TwoMore', a', b'
'ONE_CLASSES', 'One', 'OneMore', 'TWO_CLASSES', 'Two', 'TwoMore', prfx_a', prfx_b'
]);
const originalOne = './src/one';
@ -34,11 +34,11 @@ describe('metadata bundler', () => {
{name: 'ONE_CLASSES', value: originalOne}, {name: 'One', value: originalOne},
{name: 'OneMore', value: originalOne}, {name: 'TWO_CLASSES', value: originalTwo},
{name: 'Two', value: originalTwo}, {name: 'TwoMore', value: originalTwo},
{name: a', value: originalOne}, {name: b', value: originalTwo}
{name: prfx_a', value: originalOne}, {name: prfx_b', value: originalTwo}
]);
expect(result.privates).toEqual([
{privateName: a', name: 'PrivateOne', module: originalOne},
{privateName: b', name: 'PrivateTwo', module: originalTwo}
{privateName: prfx_a', name: 'PrivateOne', module: originalOne},
{privateName: prfx_b', name: 'PrivateTwo', module: originalTwo}
]);
});