Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Do not elide any imports or exports in preserve-exact
  • Loading branch information
andrewbranch committed May 17, 2021
commit a755070e45a5aef610d03f452e7c8e34395fc131
21 changes: 12 additions & 9 deletions src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ namespace ts {
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
const { importsNotUsedAsValues } = compilerOptions;

// Save the previous transformation hooks.
const previousOnEmitNode = context.onEmitNode;
Expand Down Expand Up @@ -2802,7 +2803,7 @@ namespace ts {
}

/**
* Visits an import declaration, eliding it if it is not referenced and `importsNotUsedAsValues` is not 'preserve'.
* Visits an import declaration, eliding it if it is not referenced and `importsNotUsedAsValues` is not 'preserve' or 'preserve-exact'.
*
* @param node The import declaration node.
*/
Expand All @@ -2820,8 +2821,9 @@ namespace ts {
// Elide the declaration if the import clause was elided.
const importClause = visitNode(node.importClause, visitImportClause, isImportClause);
return importClause ||
compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve ||
compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error
importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve ||
importsNotUsedAsValues === ImportsNotUsedAsValues.PreserveExact ||
importsNotUsedAsValues === ImportsNotUsedAsValues.Error
? factory.updateImportDeclaration(
node,
/*decorators*/ undefined,
Expand All @@ -2832,13 +2834,14 @@ namespace ts {
}

/**
* Visits an import clause, eliding it if it is not referenced.
* Visits an import clause, eliding it if it is not referenced and `importsNotUsedAsValues` is not 'preserve-exact'.
*
* @param node The import clause node.
*/
function visitImportClause(node: ImportClause): VisitResult<ImportClause> {
if (node.isTypeOnly) {
return undefined;
Debug.assert(!node.isTypeOnly);
if (importsNotUsedAsValues === ImportsNotUsedAsValues.PreserveExact) {
return node;
}
// Elide the import clause if we elide both its name and its named bindings.
const name = resolver.isReferencedAliasDeclaration(node) ? node.name : undefined;
Expand Down Expand Up @@ -2888,7 +2891,7 @@ namespace ts {

/**
* Visits an export declaration, eliding it if it does not contain a clause that resolves
* to a value.
* to a value and if `importsNotUsedAsValues` is not 'preserve-exact'.
*
* @param node The export declaration node.
*/
Expand All @@ -2897,7 +2900,7 @@ namespace ts {
return undefined;
}

if (!node.exportClause || isNamespaceExport(node.exportClause)) {
if (!node.exportClause || isNamespaceExport(node.exportClause) || importsNotUsedAsValues === ImportsNotUsedAsValues.PreserveExact) {
// never elide `export <whatever> from <whereever>` declarations -
// they should be kept for sideffects/untyped exports, even when the
// type checker doesn't know about any exports
Expand Down Expand Up @@ -2980,7 +2983,7 @@ namespace ts {
if (isExternalModuleImportEqualsDeclaration(node)) {
const isReferenced = resolver.isReferencedAliasDeclaration(node);
// If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'.
if (!isReferenced && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve) {
if (!isReferenced && importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve) {
return setOriginalNode(
setTextRange(
factory.createImportDeclaration(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
tests/cases/conformance/externalModules/typeOnly/d.ts(1,1): error TS1203: Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.
tests/cases/conformance/externalModules/typeOnly/e.ts(1,1): error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
tests/cases/conformance/externalModules/typeOnly/e.ts(2,1): error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.


==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
export default {};
export const b = 0;
export const c = 1;

==== tests/cases/conformance/externalModules/typeOnly/b.ts (0 errors) ====
import a, { b, c } from "./a";

==== tests/cases/conformance/externalModules/typeOnly/c.ts (0 errors) ====
import * as a from "./a";

==== tests/cases/conformance/externalModules/typeOnly/d.ts (1 errors) ====
export = {};
~~~~~~~~~~~~
!!! error TS1203: Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.

==== tests/cases/conformance/externalModules/typeOnly/e.ts (2 errors) ====
import D = require("./d");
~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
import DD = require("./d");
~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
DD;

==== tests/cases/conformance/externalModules/typeOnly/f.ts (0 errors) ====
import type a from "./a";
import { b, c } from "./a";
b;

43 changes: 43 additions & 0 deletions tests/baselines/reference/importsNotUsedAsValues_preserve-exact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//// [tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValues_preserve-exact.ts] ////

//// [a.ts]
export default {};
export const b = 0;
export const c = 1;

//// [b.ts]
import a, { b, c } from "./a";

//// [c.ts]
import * as a from "./a";

//// [d.ts]
export = {};

//// [e.ts]
import D = require("./d");
import DD = require("./d");
DD;

//// [f.ts]
import type a from "./a";
import { b, c } from "./a";
b;


//// [a.js]
export default {};
export var b = 0;
export var c = 1;
//// [b.js]
import a, { b, c } from "./a";
//// [c.js]
import * as a from "./a";
//// [d.js]
export {};
//// [e.js]
DD;
export {};
//// [f.js]
import { b, c } from "./a";
b;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export default {};
export const b = 0;
>b : Symbol(b, Decl(a.ts, 1, 12))

export const c = 1;
>c : Symbol(c, Decl(a.ts, 2, 12))

=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
import a, { b, c } from "./a";
>a : Symbol(a, Decl(b.ts, 0, 6))
>b : Symbol(b, Decl(b.ts, 0, 11))
>c : Symbol(c, Decl(b.ts, 0, 14))

=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
import * as a from "./a";
>a : Symbol(a, Decl(c.ts, 0, 6))

=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
export = {};
No type information for this code.
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/e.ts ===
import D = require("./d");
>D : Symbol(D, Decl(e.ts, 0, 0))

import DD = require("./d");
>DD : Symbol(DD, Decl(e.ts, 0, 26))

DD;
>DD : Symbol(DD, Decl(e.ts, 0, 26))

=== tests/cases/conformance/externalModules/typeOnly/f.ts ===
import type a from "./a";
>a : Symbol(a, Decl(f.ts, 0, 6))

import { b, c } from "./a";
>b : Symbol(b, Decl(f.ts, 1, 8))
>c : Symbol(c, Decl(f.ts, 1, 11))

b;
>b : Symbol(b, Decl(f.ts, 1, 8))

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export default {};
>{} : {}

export const b = 0;
>b : 0
>0 : 0

export const c = 1;
>c : 1
>1 : 1

=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
import a, { b, c } from "./a";
>a : {}
>b : 0
>c : 1

=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
import * as a from "./a";
>a : typeof a

=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
export = {};
>{} : {}

=== tests/cases/conformance/externalModules/typeOnly/e.ts ===
import D = require("./d");
>D : {}

import DD = require("./d");
>DD : {}

DD;
>DD : {}

=== tests/cases/conformance/externalModules/typeOnly/f.ts ===
import type a from "./a";
>a : any

import { b, c } from "./a";
>b : 0
>c : 1

b;
>b : 0

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @importsNotUsedAsValues: preserve-exact
// @module: esnext

// @Filename: a.ts
export default {};
export const b = 0;
export const c = 1;

// @Filename: b.ts
import a, { b, c } from "./a";

// @Filename: c.ts
import * as a from "./a";

// @Filename: d.ts
export = {};

// @Filename: e.ts
import D = require("./d");
import DD = require("./d");
DD;

// @Filename: f.ts
import type a from "./a";
import { b, c } from "./a";
b;