Bind all module export clauses
This completes support for module imports and exports. Many forms of exports are supported, and are all now tested, for example: // Export individual declarations as we go: export class C {} export interface I {} export let x = 42; // Export individual declarations explicitly: class D {} interface J {} let w = 42; export {D, J, w}; // Rename exports: export {D as E, J as K, w as x}; // Re-export members from other modules: export {A, B, c} from "other"; // Rename re-exports: export {A as M, B as N, c as o} from "other"; // Re-export entire module as a submodule: import * as other from "other"; export {other};
This commit is contained in:
parent
b13ad61d89
commit
8b1a814d17
7 changed files with 576 additions and 88 deletions
|
@ -155,10 +155,16 @@ function notYetImplemented(node: ts.Node | undefined, label?: string): never {
|
||||||
|
|
||||||
// A transpiler is responsible for transforming TypeScript program artifacts into MuPack/MuIL AST forms.
|
// A transpiler is responsible for transforming TypeScript program artifacts into MuPack/MuIL AST forms.
|
||||||
export class Transformer {
|
export class Transformer {
|
||||||
private meta: pack.Metadata; // the package's metadata.
|
// Immutable elements of the transformer that exist throughout an entire pass:
|
||||||
private script: Script; // the package's compiled TypeScript tree and context.
|
private readonly meta: pack.Metadata; // the package's metadata.
|
||||||
private dctx: diag.Context; // the diagnostics context.
|
private readonly script: Script; // the package's compiled TypeScript tree and context.
|
||||||
private diagnostics: diag.Diagnostic[]; // any diagnostics encountered during translation.
|
private readonly dctx: diag.Context; // the diagnostics context.
|
||||||
|
private readonly diagnostics: diag.Diagnostic[]; // any diagnostics encountered during translation.
|
||||||
|
|
||||||
|
// Mutable elements of the transformer that are pushed/popped as we perform visitations:
|
||||||
|
private currentSourceFile: ts.SourceFile | undefined;
|
||||||
|
private currentModuleMembers: ast.ModuleMembers | undefined;
|
||||||
|
private currentModuleImports: Map<string, ModuleReference>;
|
||||||
|
|
||||||
constructor(meta: pack.Metadata, script: Script) {
|
constructor(meta: pack.Metadata, script: Script) {
|
||||||
contract.requires(!!script.tree, "script", "A valid MuJS AST is required to lower to MuPack/MuIL");
|
contract.requires(!!script.tree, "script", "A valid MuJS AST is required to lower to MuPack/MuIL");
|
||||||
|
@ -252,8 +258,8 @@ export class Transformer {
|
||||||
return moduleTok;
|
return moduleTok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// createModuleDefinitionToken binds a string-based export name to the associated token that references it.
|
// createModuleMemberToken binds a string-based exported member name to the associated token that references it.
|
||||||
private createModuleDefinitionToken(mod: ModuleReference, name: string): symbols.Token {
|
private createModuleMemberToken(mod: ModuleReference, name: string): symbols.Token {
|
||||||
// The concatenated name of the module plus identifier will resolve correctly to an exported definition.
|
// The concatenated name of the module plus identifier will resolve correctly to an exported definition.
|
||||||
let modtok: symbols.ModuleToken = this.createModuleToken(mod);
|
let modtok: symbols.ModuleToken = this.createModuleToken(mod);
|
||||||
return `${modtok}${symbols.tokenSep}${name}`;
|
return `${modtok}${symbols.tokenSep}${name}`;
|
||||||
|
@ -330,7 +336,7 @@ export class Transformer {
|
||||||
`Expected discovered module '${this.createModuleReference(moduleSymbol.name)}' to equal '${mod}'`,
|
`Expected discovered module '${this.createModuleReference(moduleSymbol.name)}' to equal '${mod}'`,
|
||||||
);
|
);
|
||||||
for (let expsym of this.checker().getExportsOfModule(moduleSymbol)) {
|
for (let expsym of this.checker().getExportsOfModule(moduleSymbol)) {
|
||||||
exports.push(this.createModuleDefinitionToken(mod, expsym.name));
|
exports.push(this.createModuleMemberToken(mod, expsym.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
return exports;
|
return exports;
|
||||||
|
@ -352,59 +358,71 @@ export class Transformer {
|
||||||
// MuPack/MuIL. As such, the appropriate top-level definitions (variables, functions, and classes) are returned as
|
// MuPack/MuIL. As such, the appropriate top-level definitions (variables, functions, and classes) are returned as
|
||||||
// definitions, while any loose code (including variable initializers) is bundled into module inits and entrypoints.
|
// definitions, while any loose code (including variable initializers) is bundled into module inits and entrypoints.
|
||||||
private transformSourceFile(node: ts.SourceFile): ast.Module {
|
private transformSourceFile(node: ts.SourceFile): ast.Module {
|
||||||
// All definitions will go into a map keyed by their identifier.
|
// Each source file is a separate module, and we maintain some amount of context about it. Push some state.
|
||||||
let members: ast.ModuleMembers = {};
|
let priorSourceFile: ts.SourceFile | undefined = this.currentSourceFile;
|
||||||
|
let priorModuleMembers: ast.ModuleMembers | undefined = this.currentModuleMembers;
|
||||||
|
let priorModuleImports: Map<string, ModuleReference> | undefined = this.currentModuleImports;
|
||||||
|
try {
|
||||||
|
this.currentSourceFile = node;
|
||||||
|
this.currentModuleMembers = {};
|
||||||
|
this.currentModuleImports = new Map<string, ModuleReference>();
|
||||||
|
|
||||||
// Any top-level non-definition statements will pile up into the module initializer.
|
// Any top-level non-definition statements will pile up into the module initializer.
|
||||||
let statements: ast.Statement[] = [];
|
let statements: ast.Statement[] = [];
|
||||||
|
|
||||||
// Enumerate the module's statements and put them in the respective places.
|
// Enumerate the module's statements and put them in the respective places.
|
||||||
for (let statement of node.statements) {
|
for (let statement of node.statements) {
|
||||||
let elements: ModuleElement[] = this.transformSourceFileStatement(statement);
|
let elements: ModuleElement[] = this.transformSourceFileStatement(statement);
|
||||||
for (let element of elements) {
|
for (let element of elements) {
|
||||||
if (isVariableDeclaration(element)) {
|
if (isVariableDeclaration(element)) {
|
||||||
// This is a module property with a possible initializer. The property should get registered as a
|
// This is a module property with a possible initializer. The property must be registered as a
|
||||||
// member in this module's member map, and the initializer must happen in the module initializer.
|
// member in this module's member map, and the initializer must go into the module initializer.
|
||||||
// TODO(joe): respect legacyVar to emulate "var"-like scoping.
|
// TODO(joe): respect legacyVar to emulate "var"-like scoping.
|
||||||
let decl = <VariableDeclaration<ast.ModuleProperty>>element;
|
let decl = <VariableDeclaration<ast.ModuleProperty>>element;
|
||||||
if (decl.initializer) {
|
if (decl.initializer) {
|
||||||
statements.push(this.makeVariableInitializer(decl));
|
statements.push(this.makeVariableInitializer(decl));
|
||||||
|
}
|
||||||
|
this.currentModuleMembers[decl.variable.name.ident] = decl.variable;
|
||||||
|
}
|
||||||
|
else if (ast.isDefinition(<ast.Node>element)) {
|
||||||
|
// This is a module member; simply add it to the list.
|
||||||
|
let member = <ast.ModuleMember>element;
|
||||||
|
this.currentModuleMembers[member.name.ident] = member;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This is a top-level module statement; place it into the module initializer.
|
||||||
|
statements.push(<ast.Statement>element);
|
||||||
}
|
}
|
||||||
members[decl.variable.name.ident] = decl.variable;
|
|
||||||
}
|
|
||||||
else if (ast.isDefinition(<ast.Node>element)) {
|
|
||||||
// This is a module member; simply add it to the list.
|
|
||||||
let member = <ast.ModuleMember>element;
|
|
||||||
members[member.name.ident] = member;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// This is a top-level module statement; place it into the module initializer.
|
|
||||||
statements.push(<ast.Statement>element);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the initialization statements are non-empty, add an initializer method.
|
||||||
|
if (statements.length > 0) {
|
||||||
|
let initializer: ast.ModuleMethod = {
|
||||||
|
kind: ast.moduleMethodKind,
|
||||||
|
name: ident(symbols.specialFunctionInitializer),
|
||||||
|
access: symbols.publicAccessibility,
|
||||||
|
body: {
|
||||||
|
kind: ast.blockKind,
|
||||||
|
statements: statements,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.currentModuleMembers[initializer.name.ident] = initializer;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If the initialization statements are non-empty, add an initializer method.
|
let modref: ModuleReference = this.createModuleReference(node.fileName);
|
||||||
if (statements.length > 0) {
|
let modtok: symbols.ModuleToken = this.createModuleToken(modref);
|
||||||
let initializer: ast.ModuleMethod = {
|
return this.withLocation(node, <ast.Module>{
|
||||||
kind: ast.moduleMethodKind,
|
kind: ast.moduleKind,
|
||||||
name: ident(symbols.specialFunctionInitializer),
|
name: ident(modtok),
|
||||||
access: symbols.publicAccessibility,
|
members: this.currentModuleMembers,
|
||||||
body: {
|
});
|
||||||
kind: ast.blockKind,
|
}
|
||||||
statements: statements,
|
finally {
|
||||||
},
|
this.currentSourceFile = priorSourceFile;
|
||||||
};
|
this.currentModuleMembers = priorModuleMembers;
|
||||||
members[initializer.name.ident] = initializer;
|
this.currentModuleImports = priorModuleImports;
|
||||||
}
|
}
|
||||||
|
|
||||||
let modref: ModuleReference = this.createModuleReference(node.fileName);
|
|
||||||
let modtok: symbols.ModuleToken = this.createModuleToken(modref);
|
|
||||||
return this.withLocation(node, <ast.Module>{
|
|
||||||
kind: ast.moduleKind,
|
|
||||||
name: ident(modtok),
|
|
||||||
members: members,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This transforms a top-level TypeScript module statement. It might return multiple elements in the rare
|
// This transforms a top-level TypeScript module statement. It might return multiple elements in the rare
|
||||||
|
@ -459,54 +477,83 @@ export class Transformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private transformExportDeclaration(node: ts.ExportDeclaration): ast.ModuleMember[] {
|
private transformExportDeclaration(node: ts.ExportDeclaration): ast.ModuleMember[] {
|
||||||
// In the case of a module specifier, we are re-exporting elements from another module.
|
let exports: ast.Export[] = [];
|
||||||
if (node.moduleSpecifier) {
|
|
||||||
return this.transformReExportDeclaration(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we are exporting already-imported names from the current module.
|
// Otherwise, we are exporting already-imported names from the current module.
|
||||||
// TODO(joe): support this, by enumerating all exports, and flipping any privates to publics. As we proceed, we
|
// TODO: in ECMAScript, this is order independent, so we can actually export before declaring something.
|
||||||
// will need to keep an eye out for exporting whole sub-modules.
|
// To simplify things, we are only allowing you to export things declared lexically before the export.
|
||||||
return notYetImplemented(node, "manual exports");
|
|
||||||
}
|
|
||||||
|
|
||||||
private transformReExportDeclaration(node: ts.ExportDeclaration): ast.ModuleMember[] {
|
// In the case of a module specifier, we are re-exporting elements from another module.
|
||||||
contract.assert(!!node.moduleSpecifier);
|
let sourceModule: ModuleReference | undefined;
|
||||||
contract.assert(node.moduleSpecifier!.kind === ts.SyntaxKind.StringLiteral);
|
if (node.moduleSpecifier) {
|
||||||
|
// The module specifier will be a string literal; fetch and resolve it to a module.
|
||||||
|
contract.assert(node.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral);
|
||||||
|
let spec: ts.StringLiteral = <ts.StringLiteral>node.moduleSpecifier;
|
||||||
|
let source: string = this.transformStringLiteral(spec).value;
|
||||||
|
sourceModule = this.resolveModuleReferenceByName(node, source);
|
||||||
|
}
|
||||||
|
|
||||||
let exports: ast.Export[] = [];
|
|
||||||
let spec: ts.StringLiteral = <ts.StringLiteral>node.moduleSpecifier!;
|
|
||||||
|
|
||||||
// The module specifier will be a string literal; fetch that so we can resolve to a symbol token.
|
|
||||||
let source: string = this.transformStringLiteral(spec).value;
|
|
||||||
let sourceModule: ModuleReference = this.resolveModuleReferenceByName(node, source);
|
|
||||||
if (node.exportClause) {
|
if (node.exportClause) {
|
||||||
// This is an export declaration of the form
|
// This is an export declaration of the form
|
||||||
//
|
//
|
||||||
// export { a, b, c } from "module";
|
// export { a, b, c }[ from "module"];
|
||||||
//
|
//
|
||||||
// in which a, b, and c are elements from another module that shall be exported. Each re-export may
|
// in which a, b, and c are elements that shall be exported, possibly from another module "module". If not
|
||||||
|
// another module, then these are expected to be definitions within the current module. Each re-export may
|
||||||
// optionally rename the symbol being exported. For example:
|
// optionally rename the symbol being exported. For example:
|
||||||
//
|
//
|
||||||
// export { a as x, b as y, c as z } from "module";
|
// export { a as x, b as y, c as z }[ from "module"];
|
||||||
//
|
//
|
||||||
// For every export clause, we will issue a top-level MuIL re-export AST node.
|
// For every export clause, we will issue a top-level MuIL re-export AST node.
|
||||||
for (let exportClause of node.exportClause.elements) {
|
for (let exportClause of node.exportClause.elements) {
|
||||||
let name: ast.Identifier = this.transformIdentifier(exportClause.name);
|
let name: ast.Identifier = this.transformIdentifier(exportClause.name);
|
||||||
let propertyName: ast.Identifier;
|
|
||||||
if (exportClause.propertyName) {
|
if (exportClause.propertyName) {
|
||||||
// The export is being renamed (`<propertyName> as <name`). Bind the rename.
|
// The export is being renamed (`<propertyName> as <name>`). This yields an export node, even for
|
||||||
propertyName = this.transformIdentifier(exportClause.propertyName);
|
// elements exported from the current module.
|
||||||
|
let propertyName: ast.Identifier = this.transformIdentifier(exportClause.propertyName);
|
||||||
|
let token: symbols.Token = propertyName.ident;
|
||||||
|
if (sourceModule) {
|
||||||
|
token = this.createModuleMemberToken(sourceModule, token);
|
||||||
|
}
|
||||||
|
exports.push(<ast.Export>{
|
||||||
|
kind: ast.exportKind,
|
||||||
|
name: name,
|
||||||
|
token: token,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
propertyName = name;
|
// If this is an export from another module, create an export definition. Otherwise, for exports
|
||||||
|
// from within the same module, just look up the definition and change its accessibility to public.
|
||||||
|
if (sourceModule) {
|
||||||
|
exports.push(<ast.Export>{
|
||||||
|
kind: ast.exportKind,
|
||||||
|
name: name,
|
||||||
|
token: this.createModuleMemberToken(sourceModule, name.ident),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
contract.assert(!!this.currentModuleMembers);
|
||||||
|
contract.assert(!!this.currentModuleImports);
|
||||||
|
// First look for a module member, for reexporting classes, interfaces, and variables.
|
||||||
|
let member: ast.ModuleMember = this.currentModuleMembers![name.ident];
|
||||||
|
if (member) {
|
||||||
|
contract.assert(member.access !== symbols.publicAccessibility);
|
||||||
|
member.access = symbols.publicAccessibility;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If that failed, look for a known import. This enables reexporting whole modules, e.g.:
|
||||||
|
// import * as other from "other";
|
||||||
|
// export {other};
|
||||||
|
let otherModule: ModuleReference | undefined = this.currentModuleImports!.get(name.ident);
|
||||||
|
contract.assert(!!otherModule, "Expected either a member or import match for export name");
|
||||||
|
exports.push(<ast.Export>{
|
||||||
|
kind: ast.exportKind,
|
||||||
|
name: name,
|
||||||
|
token: this.createModuleToken(otherModule!),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.push(<ast.Export>{
|
|
||||||
kind: ast.exportKind,
|
|
||||||
name: name,
|
|
||||||
token: this.createModuleDefinitionToken(sourceModule, propertyName.ident),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -515,7 +562,8 @@ export class Transformer {
|
||||||
// export * from "module";
|
// export * from "module";
|
||||||
//
|
//
|
||||||
// For this to work, we simply enumerate all known exports from "module".
|
// For this to work, we simply enumerate all known exports from "module".
|
||||||
for (let name of this.resolveModuleExportNames(node, sourceModule)) {
|
contract.assert(!!sourceModule);
|
||||||
|
for (let name of this.resolveModuleExportNames(node, sourceModule!)) {
|
||||||
exports.push(<ast.Export>{
|
exports.push(<ast.Export>{
|
||||||
kind: ast.exportKind,
|
kind: ast.exportKind,
|
||||||
name: <ast.Identifier>{
|
name: <ast.Identifier>{
|
||||||
|
@ -526,14 +574,69 @@ export class Transformer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
private transformImportDeclaration(node: ts.ImportDeclaration): ModuleElement {
|
private transformImportDeclaration(node: ts.ImportDeclaration): ModuleElement {
|
||||||
// TODO[marapongo/mu#46]: we are ignoring import declarations for the time being. Eventually we need to
|
// An import declaration is erased in the output AST, however, we must keep track of the set of known import
|
||||||
// transform all dependency symbols into real MuIL references. (Today, bound node information is
|
// names so that we can easily look them up by name later on (e.g., in the case of reexporting whole modules).
|
||||||
// discarded.) When that day comes (soon), import declarations will most likely still be ignored, however,
|
if (node.importClause) {
|
||||||
// I am leaving this comment in here so that we can make an explicit decision about this.
|
// First turn the module path into a reference. The module path may be relative, so we need to consult the
|
||||||
|
// current file's module table in order to find its fully resolved path.
|
||||||
|
contract.assert(node.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral);
|
||||||
|
let importModule: ModuleReference =
|
||||||
|
this.resolveModuleReferenceByName(node, (<ts.StringLiteral>node.moduleSpecifier).text);
|
||||||
|
|
||||||
|
// Figure out what kind of import statement this is (there are many, see below).
|
||||||
|
let name: ts.Identifier | undefined;
|
||||||
|
let namedImports: ts.NamedImports | undefined;
|
||||||
|
if (node.importClause.name) {
|
||||||
|
name = name;
|
||||||
|
}
|
||||||
|
else if (node.importClause.namedBindings) {
|
||||||
|
if (node.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
|
||||||
|
name = (<ts.NamespaceImport>node.importClause.namedBindings).name;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
contract.assert(node.importClause.namedBindings.kind === ts.SyntaxKind.NamedImports);
|
||||||
|
namedImports = <ts.NamedImports>node.importClause.namedBindings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now associate the import names with the module and/or members within it.
|
||||||
|
if (name) {
|
||||||
|
// This is an import of the form
|
||||||
|
// import * as <name> from "<module>";
|
||||||
|
// Just bind the name to an identifier and module to its module reference, and remember the association.
|
||||||
|
let importName: ast.Identifier = this.transformIdentifier(name);
|
||||||
|
log.out(5).info(`Detected bulk import ${importName.ident}=${importModule}`);
|
||||||
|
this.currentModuleImports.set(importName.ident, importModule);
|
||||||
|
}
|
||||||
|
else if (namedImports) {
|
||||||
|
// This is an import of the form
|
||||||
|
// import {a, b, c} from "<module>";
|
||||||
|
// In which case we will need to bind each name and associate it with a fully qualified token.
|
||||||
|
for (let importSpec of namedImports.elements) {
|
||||||
|
let member: ast.Identifier = this.transformIdentifier(importSpec.name);
|
||||||
|
let memberToken: symbols.Token = this.createModuleMemberToken(importModule, member.ident);
|
||||||
|
let memberName: string;
|
||||||
|
if (importSpec.propertyName) {
|
||||||
|
// This is of the form
|
||||||
|
// import {a as x} from "<module>";
|
||||||
|
// in other words, the member is renamed for purposes of this source file. But we still need to
|
||||||
|
// be able to trace it back to the actual fully qualified exported token name later on.
|
||||||
|
memberName = this.transformIdentifier(importSpec.propertyName).ident;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise, simply associate the raw member name with the fully qualified member token.
|
||||||
|
memberName = member.ident;
|
||||||
|
}
|
||||||
|
this.currentModuleImports.set(memberName, memberToken);
|
||||||
|
log.out(5).info(`Detected named import ${memberToken} as ${memberName} from ${importModule}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return <ast.EmptyStatement>{ kind: ast.emptyStatementKind };
|
return <ast.EmptyStatement>{ kind: ast.emptyStatementKind };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ let testCases: string[] = [
|
||||||
"modules/reexport",
|
"modules/reexport",
|
||||||
"modules/reexport_all",
|
"modules/reexport_all",
|
||||||
"modules/reexport_rename",
|
"modules/reexport_rename",
|
||||||
|
"modules/export",
|
||||||
|
|
||||||
// These are not quite real-world-code, but they are more complex "integration" style tests.
|
// These are not quite real-world-code, but they are more complex "integration" style tests.
|
||||||
"scenarios/point",
|
"scenarios/point",
|
||||||
|
|
4
tools/mujs/tests/output/modules/export/Mu.json
Normal file
4
tools/mujs/tests/output/modules/export/Mu.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "export"
|
||||||
|
}
|
||||||
|
|
359
tools/mujs/tests/output/modules/export/Mu.out.json
Normal file
359
tools/mujs/tests/output/modules/export/Mu.out.json
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
{
|
||||||
|
"name": "export",
|
||||||
|
"modules": {
|
||||||
|
"other": {
|
||||||
|
"kind": "Module",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "other"
|
||||||
|
},
|
||||||
|
"members": {
|
||||||
|
"D": {
|
||||||
|
"kind": "Class",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "D",
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 13
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"members": {},
|
||||||
|
"abstract": false,
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"J": {
|
||||||
|
"kind": "Class",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "J",
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 2,
|
||||||
|
"column": 17
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 2,
|
||||||
|
"column": 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"members": {},
|
||||||
|
"interface": true,
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 2,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 2,
|
||||||
|
"column": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"w": {
|
||||||
|
"kind": "ModuleProperty",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "w",
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 11
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"type": "any"
|
||||||
|
},
|
||||||
|
".init": {
|
||||||
|
"kind": "ModuleMethod",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": ".init"
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"body": {
|
||||||
|
"kind": "Block",
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"kind": "BinaryOperatorExpression",
|
||||||
|
"left": {
|
||||||
|
"kind": "LoadLocationExpression",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "w",
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 11
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"operator": "=",
|
||||||
|
"right": {
|
||||||
|
"kind": "NumberLiteral",
|
||||||
|
"raw": "42",
|
||||||
|
"value": 42,
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 15
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loc": {
|
||||||
|
"file": "other.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 5,
|
||||||
|
"column": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"kind": "Module",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "index"
|
||||||
|
},
|
||||||
|
"members": {
|
||||||
|
"other": {
|
||||||
|
"kind": "Export",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "other",
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 8
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 3,
|
||||||
|
"column": 13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"token": "other"
|
||||||
|
},
|
||||||
|
"C": {
|
||||||
|
"kind": "Class",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "C",
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 6,
|
||||||
|
"column": 6
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 6,
|
||||||
|
"column": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"members": {},
|
||||||
|
"abstract": false,
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 6,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 6,
|
||||||
|
"column": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"I": {
|
||||||
|
"kind": "Class",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "I",
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 7,
|
||||||
|
"column": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 7,
|
||||||
|
"column": 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"members": {},
|
||||||
|
"interface": true,
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 7,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 7,
|
||||||
|
"column": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"v": {
|
||||||
|
"kind": "ModuleProperty",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "v",
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 4
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"type": "any"
|
||||||
|
},
|
||||||
|
".init": {
|
||||||
|
"kind": "ModuleMethod",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": ".init"
|
||||||
|
},
|
||||||
|
"access": "public",
|
||||||
|
"body": {
|
||||||
|
"kind": "Block",
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"kind": "EmptyStatement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "BinaryOperatorExpression",
|
||||||
|
"left": {
|
||||||
|
"kind": "LoadLocationExpression",
|
||||||
|
"name": {
|
||||||
|
"kind": "Identifier",
|
||||||
|
"ident": "v",
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 4
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"operator": "=",
|
||||||
|
"right": {
|
||||||
|
"kind": "NumberLiteral",
|
||||||
|
"raw": "42",
|
||||||
|
"value": 42,
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 8
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 8,
|
||||||
|
"column": 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loc": {
|
||||||
|
"file": "index.ts",
|
||||||
|
"start": {
|
||||||
|
"line": 2,
|
||||||
|
"column": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 11,
|
||||||
|
"column": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
tools/mujs/tests/output/modules/export/index.ts
Normal file
10
tools/mujs/tests/output/modules/export/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Export a whole submodule:
|
||||||
|
import * as other from "./other";
|
||||||
|
export {other};
|
||||||
|
|
||||||
|
// Manually export C, I, and v without using export declarations:
|
||||||
|
class C {}
|
||||||
|
interface I {}
|
||||||
|
let v = 42;
|
||||||
|
export {C, I, v};
|
||||||
|
|
4
tools/mujs/tests/output/modules/export/other.ts
Normal file
4
tools/mujs/tests/output/modules/export/other.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export class D {}
|
||||||
|
export interface J {}
|
||||||
|
export let w = 42;
|
||||||
|
|
7
tools/mujs/tests/output/modules/export/tsconfig.json
Normal file
7
tools/mujs/tests/output/modules/export/tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"index.ts",
|
||||||
|
"other.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue