fix(tsc-wrapped): emit exports metadata in flat modules (#17893)
Fixes: #17888
This commit is contained in:
parent
3b2d2c467a
commit
cb16e9c747
|
@ -9,7 +9,8 @@ import * as path from 'path';
|
|||
import * as ts from 'typescript';
|
||||
|
||||
import {MetadataCollector} from './collector';
|
||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMethodMetadata} from './schema';
|
||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMethodMetadata} from './schema';
|
||||
|
||||
|
||||
|
||||
// The character set used to produce private names.
|
||||
|
@ -44,6 +45,10 @@ interface Symbol {
|
|||
// a referenced symbol's value.
|
||||
referenced?: boolean;
|
||||
|
||||
// A symbol is marked as a re-export the symbol was rexported from a module that is
|
||||
// not part of the flat module bundle.
|
||||
reexport?: boolean;
|
||||
|
||||
// Only valid for referenced canonical symbols. Produces by convertSymbols().
|
||||
value?: MetadataEntry;
|
||||
|
||||
|
@ -98,14 +103,19 @@ export class MetadataBundler {
|
|||
module: s.declaration.module
|
||||
}));
|
||||
const origins = Array.from(this.symbolMap.values())
|
||||
.filter(s => s.referenced)
|
||||
.filter(s => s.referenced && !s.reexport)
|
||||
.reduce<{[name: string]: string}>((p, s) => {
|
||||
p[s.isPrivate ? s.privateName : s.name] = s.declaration.module;
|
||||
return p;
|
||||
}, {});
|
||||
const exports = this.getReExports(exportedSymbols);
|
||||
return {
|
||||
metadata:
|
||||
{__symbolic: 'module', version: VERSION, metadata, origins, importAs: this.importAs},
|
||||
metadata: {
|
||||
__symbolic: 'module',
|
||||
version: VERSION,
|
||||
exports: exports.length ? exports : undefined, metadata, origins,
|
||||
importAs: this.importAs
|
||||
},
|
||||
privates
|
||||
};
|
||||
}
|
||||
|
@ -165,12 +175,19 @@ export class MetadataBundler {
|
|||
for (const exportDeclaration of module.exports) {
|
||||
const exportFrom = resolveModule(exportDeclaration.from, moduleName);
|
||||
// Record all the exports from the module even if we don't use it directly.
|
||||
this.exportAll(exportFrom);
|
||||
const exportedSymbols = this.exportAll(exportFrom);
|
||||
if (exportDeclaration.export) {
|
||||
// Re-export all the named exports from a module.
|
||||
for (const exportItem of exportDeclaration.export) {
|
||||
const name = typeof exportItem == 'string' ? exportItem : exportItem.name;
|
||||
const exportAs = typeof exportItem == 'string' ? exportItem : exportItem.as;
|
||||
const symbol = this.symbolOf(exportFrom, name);
|
||||
if (exportedSymbols && exportedSymbols.length == 1 && exportedSymbols[0].reexport &&
|
||||
exportedSymbols[0].name == '*') {
|
||||
// This is a named export from a module we have no metadata about. Record the named
|
||||
// export as a re-export.
|
||||
symbol.reexport = true;
|
||||
}
|
||||
exportSymbol(this.symbolOf(exportFrom, name), exportAs);
|
||||
}
|
||||
} else {
|
||||
|
@ -183,6 +200,15 @@ export class MetadataBundler {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!module) {
|
||||
// If no metadata is found for this import then it is considered external to the
|
||||
// library and should be recorded as a re-export in the final metadata if it is
|
||||
// eventually re-exported.
|
||||
const symbol = this.symbolOf(moduleName, '*');
|
||||
symbol.reexport = true;
|
||||
result.push(symbol);
|
||||
}
|
||||
this.exports.set(moduleName, result);
|
||||
|
||||
return result;
|
||||
|
@ -207,6 +233,7 @@ export class MetadataBundler {
|
|||
symbol.isPrivate = isPrivate;
|
||||
symbol.declaration = declaration;
|
||||
symbol.canonicalSymbol = canonicalSymbol;
|
||||
symbol.reexport = declaration.reexport;
|
||||
}
|
||||
|
||||
private getEntries(exportedSymbols: Symbol[]): BundleEntries {
|
||||
|
@ -233,7 +260,7 @@ export class MetadataBundler {
|
|||
exportedSymbols.forEach(symbol => this.convertSymbol(symbol));
|
||||
|
||||
Array.from(this.symbolMap.values()).forEach(symbol => {
|
||||
if (symbol.referenced) {
|
||||
if (symbol.referenced && !symbol.reexport) {
|
||||
let name = symbol.name;
|
||||
if (symbol.isPrivate && !symbol.privateName) {
|
||||
name = newPrivateName();
|
||||
|
@ -246,6 +273,36 @@ export class MetadataBundler {
|
|||
return result;
|
||||
}
|
||||
|
||||
private getReExports(exportedSymbols: Symbol[]): ModuleExportMetadata[] {
|
||||
type ExportClause = {name: string, as: string}[];
|
||||
const modules = new Map<string, ExportClause>();
|
||||
const exportAlls = new Set<string>();
|
||||
for (const symbol of exportedSymbols) {
|
||||
if (symbol.reexport) {
|
||||
const declaration = symbol.declaration;
|
||||
const module = declaration.module;
|
||||
if (declaration.name == '*') {
|
||||
// Reexport all the symbols.
|
||||
exportAlls.add(declaration.module);
|
||||
} else {
|
||||
// Re-export the symbol as the exported name.
|
||||
let entry = modules.get(module);
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
modules.set(module, entry);
|
||||
}
|
||||
const as = symbol.name;
|
||||
const name = declaration.name;
|
||||
entry.push({name, as});
|
||||
}
|
||||
}
|
||||
}
|
||||
return [
|
||||
...Array.from(exportAlls.values()).map(from => ({from})),
|
||||
...Array.from(modules.entries()).map(([from, exports]) => ({export: exports, from}))
|
||||
];
|
||||
}
|
||||
|
||||
private convertSymbol(symbol: Symbol) {
|
||||
const canonicalSymbol = symbol.canonicalSymbol;
|
||||
|
||||
|
|
|
@ -163,6 +163,35 @@ describe('metadata bundler', () => {
|
|||
expect(Object.keys(result.metadata.metadata).sort()).toEqual(['Foo', 'ɵa']);
|
||||
expect(result.privates).toEqual([{privateName: 'ɵa', name: 'Bar', module: './bar'}]);
|
||||
});
|
||||
|
||||
it('should be able to bundle a library with re-exported symbols', () => {
|
||||
const host = new MockStringBundlerHost('/', {
|
||||
'public-api.ts': `
|
||||
export * from './src/core';
|
||||
export * from './src/externals';
|
||||
`,
|
||||
'src': {
|
||||
'core.ts': `
|
||||
export class A {}
|
||||
export class B extends A {}
|
||||
`,
|
||||
'externals.ts': `
|
||||
export {E, F, G} from 'external_one';
|
||||
export * from 'external_two';
|
||||
`
|
||||
}
|
||||
});
|
||||
|
||||
const bundler = new MetadataBundler('/public-api', undefined, host);
|
||||
const result = bundler.getMetadataBundle();
|
||||
expect(result.metadata.exports).toEqual([
|
||||
{from: 'external_two'}, {
|
||||
export: [{name: 'E', as: 'E'}, {name: 'F', as: 'F'}, {name: 'G', as: 'G'}],
|
||||
from: 'external_one'
|
||||
}
|
||||
]);
|
||||
expect(result.metadata.origins['E']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
export class MockStringBundlerHost implements MetadataBundlerHost {
|
||||
|
|
Loading…
Reference in New Issue