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 * as ts from 'typescript';
|
||||||
|
|
||||||
import {MetadataCollector} from './collector';
|
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.
|
// The character set used to produce private names.
|
||||||
|
@ -44,6 +45,10 @@ interface Symbol {
|
||||||
// a referenced symbol's value.
|
// a referenced symbol's value.
|
||||||
referenced?: boolean;
|
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().
|
// Only valid for referenced canonical symbols. Produces by convertSymbols().
|
||||||
value?: MetadataEntry;
|
value?: MetadataEntry;
|
||||||
|
|
||||||
|
@ -98,14 +103,19 @@ export class MetadataBundler {
|
||||||
module: s.declaration.module
|
module: s.declaration.module
|
||||||
}));
|
}));
|
||||||
const origins = Array.from(this.symbolMap.values())
|
const origins = Array.from(this.symbolMap.values())
|
||||||
.filter(s => s.referenced)
|
.filter(s => s.referenced && !s.reexport)
|
||||||
.reduce<{[name: string]: string}>((p, s) => {
|
.reduce<{[name: string]: string}>((p, s) => {
|
||||||
p[s.isPrivate ? s.privateName : s.name] = s.declaration.module;
|
p[s.isPrivate ? s.privateName : s.name] = s.declaration.module;
|
||||||
return p;
|
return p;
|
||||||
}, {});
|
}, {});
|
||||||
|
const exports = this.getReExports(exportedSymbols);
|
||||||
return {
|
return {
|
||||||
metadata:
|
metadata: {
|
||||||
{__symbolic: 'module', version: VERSION, metadata, origins, importAs: this.importAs},
|
__symbolic: 'module',
|
||||||
|
version: VERSION,
|
||||||
|
exports: exports.length ? exports : undefined, metadata, origins,
|
||||||
|
importAs: this.importAs
|
||||||
|
},
|
||||||
privates
|
privates
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -165,12 +175,19 @@ export class MetadataBundler {
|
||||||
for (const exportDeclaration of module.exports) {
|
for (const exportDeclaration of module.exports) {
|
||||||
const exportFrom = resolveModule(exportDeclaration.from, moduleName);
|
const exportFrom = resolveModule(exportDeclaration.from, moduleName);
|
||||||
// Record all the exports from the module even if we don't use it directly.
|
// 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) {
|
if (exportDeclaration.export) {
|
||||||
// Re-export all the named exports from a module.
|
// Re-export all the named exports from a module.
|
||||||
for (const exportItem of exportDeclaration.export) {
|
for (const exportItem of exportDeclaration.export) {
|
||||||
const name = typeof exportItem == 'string' ? exportItem : exportItem.name;
|
const name = typeof exportItem == 'string' ? exportItem : exportItem.name;
|
||||||
const exportAs = typeof exportItem == 'string' ? exportItem : exportItem.as;
|
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);
|
exportSymbol(this.symbolOf(exportFrom, name), exportAs);
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
this.exports.set(moduleName, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -207,6 +233,7 @@ export class MetadataBundler {
|
||||||
symbol.isPrivate = isPrivate;
|
symbol.isPrivate = isPrivate;
|
||||||
symbol.declaration = declaration;
|
symbol.declaration = declaration;
|
||||||
symbol.canonicalSymbol = canonicalSymbol;
|
symbol.canonicalSymbol = canonicalSymbol;
|
||||||
|
symbol.reexport = declaration.reexport;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getEntries(exportedSymbols: Symbol[]): BundleEntries {
|
private getEntries(exportedSymbols: Symbol[]): BundleEntries {
|
||||||
|
@ -233,7 +260,7 @@ export class MetadataBundler {
|
||||||
exportedSymbols.forEach(symbol => this.convertSymbol(symbol));
|
exportedSymbols.forEach(symbol => this.convertSymbol(symbol));
|
||||||
|
|
||||||
Array.from(this.symbolMap.values()).forEach(symbol => {
|
Array.from(this.symbolMap.values()).forEach(symbol => {
|
||||||
if (symbol.referenced) {
|
if (symbol.referenced && !symbol.reexport) {
|
||||||
let name = symbol.name;
|
let name = symbol.name;
|
||||||
if (symbol.isPrivate && !symbol.privateName) {
|
if (symbol.isPrivate && !symbol.privateName) {
|
||||||
name = newPrivateName();
|
name = newPrivateName();
|
||||||
|
@ -246,6 +273,36 @@ export class MetadataBundler {
|
||||||
return result;
|
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) {
|
private convertSymbol(symbol: Symbol) {
|
||||||
const canonicalSymbol = symbol.canonicalSymbol;
|
const canonicalSymbol = symbol.canonicalSymbol;
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,35 @@ describe('metadata bundler', () => {
|
||||||
expect(Object.keys(result.metadata.metadata).sort()).toEqual(['Foo', 'ɵa']);
|
expect(Object.keys(result.metadata.metadata).sort()).toEqual(['Foo', 'ɵa']);
|
||||||
expect(result.privates).toEqual([{privateName: 'ɵa', name: 'Bar', module: './bar'}]);
|
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 {
|
export class MockStringBundlerHost implements MetadataBundlerHost {
|
||||||
|
|
Loading…
Reference in New Issue