Skip to content
Permalink
Browse files

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
  • Loading branch information...
alexeagle authored and matsko committed Mar 26, 2018
1 parent 7a406a3 commit 27e14b2fb3458e6eeb45c5582ed936700b67b9a8
@@ -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:
@@ -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\';');
});
});
});
@@ -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 =
@@ -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)) {
@@ -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.
@@ -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}
]);
});

0 comments on commit 27e14b2

Please sign in to comment.
You can’t perform that action at this time.