From 27dd53678ffa5abc5ae9094c3240fdebc247e1ca Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 3 Jan 2023 13:55:50 -0800 Subject: [PATCH] Reuse existing module specifiers in js declaration emit --- src/compiler/checker.ts | 30 ++++--- .../importDeclFromTypeNodeInJsSource.js | 8 +- ...DeclarationEmitExportedClassWithExtends.js | 84 +++++++++++++++++++ ...rationEmitExportedClassWithExtends.symbols | 24 ++++++ ...larationEmitExportedClassWithExtends.types | 24 ++++++ ...ationsImportAliasExposedWithinNamespace.js | 2 +- .../jsDeclarationsReactComponents.js | 2 +- ...xDeclarationsWithEsModuleInteropNoCrash.js | 2 +- ...DeclarationEmitExportedClassWithExtends.ts | 45 ++++++++++ 9 files changed, 202 insertions(+), 19 deletions(-) create mode 100644 tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.js create mode 100644 tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.symbols create mode 100644 tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.types create mode 100644 tests/cases/compiler/jsDeclarationEmitExportedClassWithExtends.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4c1ad31d43ff6..fa718d3f06809 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9000,25 +9000,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Is bound into file.symbol.globalExports instead, which we don't currently traverse addResult(factory.createNamespaceExportDeclaration(idText((node as NamespaceExportDeclaration).name)), ModifierFlags.None); break; - case SyntaxKind.ImportClause: + case SyntaxKind.ImportClause: { + const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects + const specifier = bundled ? factory.createStringLiteral(generatedSpecifier) : (node as ImportClause).parent.moduleSpecifier; addResult(factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause(/*isTypeOnly*/ false, factory.createIdentifier(localName), /*namedBindings*/ undefined), - // We use `target.parent || target` below as `target.parent` is unset when the target is a module which has been export assigned - // And then made into a default by the `esModuleInterop` or `allowSyntheticDefaultImports` flag - // In such cases, the `target` refers to the module itself already - factory.createStringLiteral(getSpecifierForModuleSymbol(target.parent || target, context)), - /*assertClause*/ undefined + specifier, + (node as ImportClause).parent.assertClause ), ModifierFlags.None); break; - case SyntaxKind.NamespaceImport: + } + case SyntaxKind.NamespaceImport: { + const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects + const specifier = bundled ? factory.createStringLiteral(generatedSpecifier) : (node as NamespaceImport).parent.parent.moduleSpecifier; addResult(factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause(/*isTypeOnly*/ false, /*importClause*/ undefined, factory.createNamespaceImport(factory.createIdentifier(localName))), - factory.createStringLiteral(getSpecifierForModuleSymbol(target, context)), - /*assertClause*/ undefined + specifier, + (node as NamespaceImport).parent.parent.assertClause ), ModifierFlags.None); break; + } case SyntaxKind.NamespaceExport: addResult(factory.createExportDeclaration( /*modifiers*/ undefined, @@ -9027,7 +9030,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { factory.createStringLiteral(getSpecifierForModuleSymbol(target, context)) ), ModifierFlags.None); break; - case SyntaxKind.ImportSpecifier: + case SyntaxKind.ImportSpecifier: { + const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects + const specifier = bundled ? factory.createStringLiteral(generatedSpecifier) : (node as ImportSpecifier).parent.parent.parent.moduleSpecifier; addResult(factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause( @@ -9040,10 +9045,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { factory.createIdentifier(localName) ) ])), - factory.createStringLiteral(getSpecifierForModuleSymbol(target.parent || target, context)), - /*assertClause*/ undefined + specifier, + (node as ImportSpecifier).parent.parent.parent.assertClause, ), ModifierFlags.None); break; + } case SyntaxKind.ExportSpecifier: // does not use localName because the symbol name in this case refers to the name in the exports table, // which we must exactly preserve diff --git a/tests/baselines/reference/importDeclFromTypeNodeInJsSource.js b/tests/baselines/reference/importDeclFromTypeNodeInJsSource.js index 5140222d00ef5..a2819c731f60e 100644 --- a/tests/baselines/reference/importDeclFromTypeNodeInJsSource.js +++ b/tests/baselines/reference/importDeclFromTypeNodeInJsSource.js @@ -77,7 +77,7 @@ export class Foo3 extends d { } export class Foo4 extends c { } -import { EventEmitter } from "events"; -import { n3 } from "nestNamespaceModule"; -import { d } from "nestNamespaceModule"; -import { c } from "renameModule"; +import { EventEmitter } from 'events'; +import { n3 } from 'nestNamespaceModule'; +import { d } from 'nestNamespaceModule'; +import { c } from 'renameModule'; diff --git a/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.js b/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.js new file mode 100644 index 0000000000000..a4fcd7b6675b7 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.js @@ -0,0 +1,84 @@ +//// [tests/cases/compiler/jsDeclarationEmitExportedClassWithExtends.ts] //// + +//// [package.json] +{ + "name": "lit", + "version": "0.0.1", + "type": "module", + "exports": { + ".": { + "types": "./development/index.d.ts" + } + } +} +//// [index.d.ts] +export * from "lit-element/lit-element.js"; +//// [package.json] +{ + "name": "lit-element", + "version": "0.0.1", + "type": "module", + "exports": { + ".": { + "types": "./development/index.d.ts" + }, + "./lit-element.js": { + "types": "./development/lit-element.d.ts" + } + } +} +//// [index.d.ts] +export * from "./lit-element.js"; +//// [lit-element.d.ts] +export class LitElement {} +//// [package.json] +{ + "type": "module", + "private": true +} +//// [index.js] +import { LitElement, LitElement as LitElement2 } from "lit"; +export class ElementB extends LitElement {} +export class ElementC extends LitElement2 {} + +//// [index.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +import { LitElement, LitElement as LitElement2 } from "lit"; +var ElementB = /** @class */ (function (_super) { + __extends(ElementB, _super); + function ElementB() { + return _super !== null && _super.apply(this, arguments) || this; + } + return ElementB; +}(LitElement)); +export { ElementB }; +var ElementC = /** @class */ (function (_super) { + __extends(ElementC, _super); + function ElementC() { + return _super !== null && _super.apply(this, arguments) || this; + } + return ElementC; +}(LitElement2)); +export { ElementC }; + + +//// [index.d.ts] +export class ElementB extends LitElement { +} +export class ElementC extends LitElement { +} +import { LitElement } from "lit"; diff --git a/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.symbols b/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.symbols new file mode 100644 index 0000000000000..5da1562ff6fcb --- /dev/null +++ b/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.symbols @@ -0,0 +1,24 @@ +=== tests/cases/compiler/node_modules/lit/development/index.d.ts === + +export * from "lit-element/lit-element.js"; +=== tests/cases/compiler/node_modules/lit-element/development/index.d.ts === + +export * from "./lit-element.js"; +=== tests/cases/compiler/node_modules/lit-element/development/lit-element.d.ts === +export class LitElement {} +>LitElement : Symbol(LitElement, Decl(lit-element.d.ts, 0, 0)) + +=== tests/cases/compiler/index.js === +import { LitElement, LitElement as LitElement2 } from "lit"; +>LitElement : Symbol(LitElement, Decl(index.js, 0, 8)) +>LitElement : Symbol(LitElement, Decl(lit-element.d.ts, 0, 0)) +>LitElement2 : Symbol(LitElement2, Decl(index.js, 0, 20)) + +export class ElementB extends LitElement {} +>ElementB : Symbol(ElementB, Decl(index.js, 0, 60)) +>LitElement : Symbol(LitElement, Decl(index.js, 0, 8)) + +export class ElementC extends LitElement2 {} +>ElementC : Symbol(ElementC, Decl(index.js, 1, 43)) +>LitElement2 : Symbol(LitElement2, Decl(index.js, 0, 20)) + diff --git a/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.types b/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.types new file mode 100644 index 0000000000000..f12a93e9be794 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationEmitExportedClassWithExtends.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/node_modules/lit/development/index.d.ts === + +export * from "lit-element/lit-element.js"; +=== tests/cases/compiler/node_modules/lit-element/development/index.d.ts === + +export * from "./lit-element.js"; +=== tests/cases/compiler/node_modules/lit-element/development/lit-element.d.ts === +export class LitElement {} +>LitElement : LitElement + +=== tests/cases/compiler/index.js === +import { LitElement, LitElement as LitElement2 } from "lit"; +>LitElement : typeof LitElement +>LitElement : typeof LitElement +>LitElement2 : typeof LitElement + +export class ElementB extends LitElement {} +>ElementB : ElementB +>LitElement : LitElement + +export class ElementC extends LitElement2 {} +>ElementC : ElementC +>LitElement2 : LitElement + diff --git a/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.js b/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.js index cb8fe79110dfe..d1ba516018e48 100644 --- a/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.js +++ b/tests/baselines/reference/jsDeclarationsImportAliasExposedWithinNamespace.js @@ -83,7 +83,7 @@ export namespace testFnTypes { * @returns {number|null} Result. */ export function testFn(input: testFnTypes.input): number | null; -import { myTypes } from "./file.js"; +import { myTypes } from './file.js'; /** * @namespace testFnTypes * @global diff --git a/tests/baselines/reference/jsDeclarationsReactComponents.js b/tests/baselines/reference/jsDeclarationsReactComponents.js index fb0972b36ed3a..a59bd3b2e6763 100644 --- a/tests/baselines/reference/jsDeclarationsReactComponents.js +++ b/tests/baselines/reference/jsDeclarationsReactComponents.js @@ -243,4 +243,4 @@ declare namespace Tree { export const parentSource: string; } } -import PropTypes from "prop-types"; +import PropTypes from 'prop-types'; diff --git a/tests/baselines/reference/jsxDeclarationsWithEsModuleInteropNoCrash.js b/tests/baselines/reference/jsxDeclarationsWithEsModuleInteropNoCrash.js index 6b5117f906a43..19524e13025eb 100644 --- a/tests/baselines/reference/jsxDeclarationsWithEsModuleInteropNoCrash.js +++ b/tests/baselines/reference/jsxDeclarationsWithEsModuleInteropNoCrash.js @@ -39,4 +39,4 @@ declare namespace defaultProps { const bar_1: boolean; export { bar_1 as bar }; } -import PropTypes from "prop-types"; +import PropTypes from 'prop-types'; diff --git a/tests/cases/compiler/jsDeclarationEmitExportedClassWithExtends.ts b/tests/cases/compiler/jsDeclarationEmitExportedClassWithExtends.ts new file mode 100644 index 0000000000000..b0851437ffc1c --- /dev/null +++ b/tests/cases/compiler/jsDeclarationEmitExportedClassWithExtends.ts @@ -0,0 +1,45 @@ +// @checkJs: true +// @outDir: tests/cases/compiler/out +// @declaration: true +// @module: esnext +// @moduleResolution: nodenext +// @filename: node_modules/lit/package.json +{ + "name": "lit", + "version": "0.0.1", + "type": "module", + "exports": { + ".": { + "types": "./development/index.d.ts" + } + } +} +// @filename: node_modules/lit/development/index.d.ts +export * from "lit-element/lit-element.js"; +// @filename: node_modules/lit-element/package.json +{ + "name": "lit-element", + "version": "0.0.1", + "type": "module", + "exports": { + ".": { + "types": "./development/index.d.ts" + }, + "./lit-element.js": { + "types": "./development/lit-element.d.ts" + } + } +} +// @filename: node_modules/lit-element/development/index.d.ts +export * from "./lit-element.js"; +// @filename: node_modules/lit-element/development//lit-element.d.ts +export class LitElement {} +// @filename: package.json +{ + "type": "module", + "private": true +} +// @filename: index.js +import { LitElement, LitElement as LitElement2 } from "lit"; +export class ElementB extends LitElement {} +export class ElementC extends LitElement2 {} \ No newline at end of file