diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 601948a3ea0..c325a2b92e3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6783,6 +6783,37 @@ namespace ts { const targetName = getInternalSymbolName(target, verbatimTargetName); includePrivateSymbol(target); // the target may be within the same scope - attempt to serialize it first switch (node.kind) { + case SyntaxKind.BindingElement: + if (node.parent?.parent?.kind === SyntaxKind.VariableDeclaration) { + // const { SomeClass } = require('./lib'); + const specifier = getSpecifierForModuleSymbol(target.parent || target, context); // 'y' + addResult(factory.createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamedImports([factory.createImportSpecifier( + (node as BindingElement).propertyName && isIdentifier((node as BindingElement).propertyName!) ? factory.createIdentifier(idText((node as BindingElement).propertyName as Identifier)) : undefined, + factory.createIdentifier(localName) + )])), + factory.createStringLiteral(specifier) + ), ModifierFlags.None); + break; + } + // At present, the below case should be entirely unhit, as, generally speaking, the below case is *usually* bound + // such that the `BinaryExpression` is the declaration rather than the specific, nested binding element + // (because we don't seek to emit an alias in these forms yet). As such, the `BinaryExpression` switch case + // will be what actually handles this form. _However_, in anticipation of binding the below with proper + // alias symbols, I'm _pretty comfortable_ including the case here, even though it is not yet live. + if (node.parent?.parent?.kind === SyntaxKind.BinaryExpression) { + // module.exports = { SomeClass } + serializeExportSpecifier( + unescapeLeadingUnderscores(symbol.escapedName), + targetName + ); + break; + } + // We don't know how to serialize this (nested?) binding element + Debug.failBadSyntaxKind(node.parent?.parent || node, "Unhandled binding element grandparent kind in declaration serialization"); + break; case SyntaxKind.VariableDeclaration: // commonjs require: const x = require('y') if (isPropertyAccessExpression((node as VariableDeclaration).initializer!)) { diff --git a/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.js b/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.js new file mode 100644 index 00000000000..d8f635713a5 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.js @@ -0,0 +1,67 @@ +//// [tests/cases/conformance/jsdoc/declarations/jsDeclarationsReexportedCjsAlias.ts] //// + +//// [lib.js] +/** + * @param {string} a + */ +function bar(a) { + return a + a; +} + +class SomeClass { + a() { + return 1; + } +} + +module.exports = { + bar, + SomeClass +} +//// [main.js] +const { SomeClass, SomeClass: Another } = require('./lib'); + +module.exports = { + SomeClass, + Another +} + +//// [lib.js] +/** + * @param {string} a + */ +function bar(a) { + return a + a; +} +var SomeClass = /** @class */ (function () { + function SomeClass() { + } + SomeClass.prototype.a = function () { + return 1; + }; + return SomeClass; +}()); +module.exports = { + bar: bar, + SomeClass: SomeClass +}; +//// [main.js] +var _a = require('./lib'), SomeClass = _a.SomeClass, Another = _a.SomeClass; +module.exports = { + SomeClass: SomeClass, + Another: Another +}; + + +//// [lib.d.ts] +/** + * @param {string} a + */ +export function bar(a: string): string; +export class SomeClass { + a(): number; +} +//// [main.d.ts] +import { SomeClass } from "./lib"; +import { SomeClass as Another } from "./lib"; +export { SomeClass, Another }; diff --git a/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.symbols b/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.symbols new file mode 100644 index 00000000000..f069a1426af --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.symbols @@ -0,0 +1,53 @@ +=== tests/cases/conformance/jsdoc/declarations/main.js === +const { SomeClass, SomeClass: Another } = require('./lib'); +>SomeClass : Symbol(SomeClass, Decl(main.js, 0, 7)) +>SomeClass : Symbol(SomeClass, Decl(lib.js, 14, 8)) +>Another : Symbol(Another, Decl(main.js, 0, 18)) +>require : Symbol(require) +>'./lib' : Symbol("tests/cases/conformance/jsdoc/declarations/lib", Decl(lib.js, 0, 0)) + +module.exports = { +>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/main", Decl(main.js, 0, 0)) +>module : Symbol(export=, Decl(main.js, 0, 59)) +>exports : Symbol(export=, Decl(main.js, 0, 59)) + + SomeClass, +>SomeClass : Symbol(SomeClass, Decl(main.js, 2, 18)) + + Another +>Another : Symbol(Another, Decl(main.js, 3, 14)) +} +=== tests/cases/conformance/jsdoc/declarations/lib.js === +/** + * @param {string} a + */ +function bar(a) { +>bar : Symbol(bar, Decl(lib.js, 0, 0)) +>a : Symbol(a, Decl(lib.js, 3, 13)) + + return a + a; +>a : Symbol(a, Decl(lib.js, 3, 13)) +>a : Symbol(a, Decl(lib.js, 3, 13)) +} + +class SomeClass { +>SomeClass : Symbol(SomeClass, Decl(lib.js, 5, 1)) + + a() { +>a : Symbol(SomeClass.a, Decl(lib.js, 7, 17)) + + return 1; + } +} + +module.exports = { +>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/lib", Decl(lib.js, 0, 0)) +>module : Symbol(export=, Decl(lib.js, 11, 1)) +>exports : Symbol(export=, Decl(lib.js, 11, 1)) + + bar, +>bar : Symbol(bar, Decl(lib.js, 13, 18)) + + SomeClass +>SomeClass : Symbol(SomeClass, Decl(lib.js, 14, 8)) +} diff --git a/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.types b/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.types new file mode 100644 index 00000000000..116dccdd6e0 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsReexportedCjsAlias.types @@ -0,0 +1,60 @@ +=== tests/cases/conformance/jsdoc/declarations/main.js === +const { SomeClass, SomeClass: Another } = require('./lib'); +>SomeClass : typeof SomeClass +>SomeClass : any +>Another : typeof SomeClass +>require('./lib') : { bar: (a: string) => string; SomeClass: typeof SomeClass; } +>require : any +>'./lib' : "./lib" + +module.exports = { +>module.exports = { SomeClass, Another} : { SomeClass: typeof SomeClass; Another: typeof SomeClass; } +>module.exports : { SomeClass: typeof SomeClass; Another: typeof SomeClass; } +>module : { "\"tests/cases/conformance/jsdoc/declarations/main\"": { SomeClass: typeof SomeClass; Another: typeof SomeClass; }; } +>exports : { SomeClass: typeof SomeClass; Another: typeof SomeClass; } +>{ SomeClass, Another} : { SomeClass: typeof SomeClass; Another: typeof SomeClass; } + + SomeClass, +>SomeClass : typeof SomeClass + + Another +>Another : typeof SomeClass +} +=== tests/cases/conformance/jsdoc/declarations/lib.js === +/** + * @param {string} a + */ +function bar(a) { +>bar : (a: string) => string +>a : string + + return a + a; +>a + a : string +>a : string +>a : string +} + +class SomeClass { +>SomeClass : SomeClass + + a() { +>a : () => number + + return 1; +>1 : 1 + } +} + +module.exports = { +>module.exports = { bar, SomeClass} : { bar: (a: string) => string; SomeClass: typeof SomeClass; } +>module.exports : { bar: (a: string) => string; SomeClass: typeof SomeClass; } +>module : { "\"tests/cases/conformance/jsdoc/declarations/lib\"": { bar: (a: string) => string; SomeClass: typeof SomeClass; }; } +>exports : { bar: (a: string) => string; SomeClass: typeof SomeClass; } +>{ bar, SomeClass} : { bar: (a: string) => string; SomeClass: typeof SomeClass; } + + bar, +>bar : (a: string) => string + + SomeClass +>SomeClass : typeof SomeClass +} diff --git a/tests/cases/conformance/jsdoc/declarations/jsDeclarationsReexportedCjsAlias.ts b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsReexportedCjsAlias.ts new file mode 100644 index 00000000000..9b58a6d5313 --- /dev/null +++ b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsReexportedCjsAlias.ts @@ -0,0 +1,30 @@ +// @allowJs: true +// @checkJs: true +// @target: es5 +// @outDir: ./out +// @declaration: true +// @filename: lib.js +/** + * @param {string} a + */ +function bar(a) { + return a + a; +} + +class SomeClass { + a() { + return 1; + } +} + +module.exports = { + bar, + SomeClass +} +// @filename: main.js +const { SomeClass, SomeClass: Another } = require('./lib'); + +module.exports = { + SomeClass, + Another +} \ No newline at end of file