From 3d420f1d89a6d861c8cb3e9560e126792faaf4b7 Mon Sep 17 00:00:00 2001 From: mmews-n4 Date: Tue, 23 Jan 2024 11:01:22 +0100 Subject: [PATCH] GH-2595: The d.ts export should append .js file extensions in import statements (#2598) * d.ts exports emit type imports including file extension * respect existing file extension * add test * fix fileExtension() * fix xt framework * respect bare imports * add test --- .../dts/print/PrettyPrinterDts.java | 13 ++++ .../src/org/eclipse/n4js/utils/URIUtils.java | 5 ++ .../ide/tests/helper/server/xt/XtIdeTest.java | 4 +- .../ReferenceToNonDtsProject.n4js.xt | 2 +- .../dts-export/imports/BareImport.n4js.xt | 40 +++++++++++ .../imports/DefaultAsImport.n4js.xt | 2 +- .../imports/ImportForLiteralType1.n4js.xt | 8 +-- .../imports/ImportForLiteralType2.n4js.xt | 8 +-- .../imports/ImportMustBeAdded1.n4js.xt | 2 +- .../ImportMustBeAdded2_withAlias.n4js.xt | 2 +- ...BeAdded3_forIndirectlyExportedElem.n4js.xt | 2 +- .../imports/ImportWithExtension.n4js.xt | 70 +++++++++++++++++++ .../imports/ModuleSpecifiers.n4js.xt | 4 +- .../imports/NamespaceAccess.n4js.xt | 2 +- .../UseExistingNamespaceImport.n4js.xt | 2 +- .../inferredTypes/InferredTypes2.n4js.xt | 2 +- ...InferredTypesImportUsedInStatement.n4js.xt | 2 +- .../inferredTypes/ReplaceWildcard.n4js.xt | 4 +- .../inferredTypes/UpperBounds.n4js.xt | 6 +- 19 files changed, 155 insertions(+), 25 deletions(-) create mode 100644 tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/BareImport.n4js.xt create mode 100644 tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportWithExtension.n4js.xt diff --git a/plugins/org.eclipse.n4js.transpiler.dts/src/org/eclipse/n4js/transpiler/dts/print/PrettyPrinterDts.java b/plugins/org.eclipse.n4js.transpiler.dts/src/org/eclipse/n4js/transpiler/dts/print/PrettyPrinterDts.java index 5a6650d623..2bc5c6a22d 100644 --- a/plugins/org.eclipse.n4js.transpiler.dts/src/org/eclipse/n4js/transpiler/dts/print/PrettyPrinterDts.java +++ b/plugins/org.eclipse.n4js.transpiler.dts/src/org/eclipse/n4js/transpiler/dts/print/PrettyPrinterDts.java @@ -43,6 +43,7 @@ import org.eclipse.n4js.n4JS.ImportDeclaration; import org.eclipse.n4js.n4JS.ImportSpecifier; import org.eclipse.n4js.n4JS.LiteralOrComputedPropertyName; +import org.eclipse.n4js.n4JS.ModuleSpecifierForm; import org.eclipse.n4js.n4JS.N4ClassDeclaration; import org.eclipse.n4js.n4JS.N4ClassifierDeclaration; import org.eclipse.n4js.n4JS.N4EnumDeclaration; @@ -89,6 +90,7 @@ import org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions; import org.eclipse.n4js.utils.N4JSLanguageUtils; import org.eclipse.n4js.utils.N4JSLanguageUtils.EnumKind; +import org.eclipse.n4js.utils.URIUtils; import org.eclipse.n4js.utils.parser.conversion.ValueConverterUtils; import org.eclipse.xtext.EcoreUtil2; @@ -214,8 +216,19 @@ public Boolean caseImportDeclaration(ImportDeclaration original) { ? original.getModuleSpecifierAsText().replace("%3A", ":") // see ModuleSpecifierValueConverter : original.getModule().getQualifiedName(); + if (original.getModuleSpecifierForm() != ModuleSpecifierForm.PROJECT + && Strings.isNullOrEmpty(URIUtils.fileExtension(URIUtils.toFileUri(moduleSpecifier)))) { + + String extension = original.isBare() ? N4JSGlobals.JS_FILE_EXTENSION : N4JSGlobals.DTS_FILE_EXTENSION; + moduleSpecifier += "." + extension; + } + processAnnotations(original.getAnnotations()); write("import "); + if (!original.isBare()) { + write("type "); + } + // 1) import specifiers List importSpecifiers = new ArrayList<>(original.getImportSpecifiers()); if (!importSpecifiers.isEmpty() && importSpecifiers.get(0) instanceof DefaultImportSpecifier) { diff --git a/plugins/org.eclipse.n4js.utils/src/org/eclipse/n4js/utils/URIUtils.java b/plugins/org.eclipse.n4js.utils/src/org/eclipse/n4js/utils/URIUtils.java index 589b80d8ff..612b850d49 100644 --- a/plugins/org.eclipse.n4js.utils/src/org/eclipse/n4js/utils/URIUtils.java +++ b/plugins/org.eclipse.n4js.utils/src/org/eclipse/n4js/utils/URIUtils.java @@ -93,12 +93,17 @@ static private int fileExtensionIndex(URI uri) { if (firstIdx < 0) { return -1; } + boolean foundDot = false; for (int idx = firstIdx; idx > 0; idx--) { if (lastSegment.charAt(idx) == '.') { + foundDot = true; firstIdx = idx + 1; break; } } + if (!foundDot) { + return -1; + } String ext1 = lastSegment.substring(firstIdx); if (extensionPrefixes.containsKey(ext1)) { int scndIdx = firstIdx - 2; diff --git a/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/xt/XtIdeTest.java b/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/xt/XtIdeTest.java index d4912189fb..2b998542b6 100644 --- a/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/xt/XtIdeTest.java +++ b/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/xt/XtIdeTest.java @@ -773,10 +773,12 @@ public void generated_dts(XtMethodData data) throws IOException { for (Project project : allProjectsWithGenerateDts) { File workingDir = getProjectRoot(project.getName()); + Path wdSrcDir = workingDir.toPath().resolve("src-gen"); + wdSrcDir.toFile().mkdirs(); // might not exist e.g. if is a definition project // copy n4jsglobals.d.ts to output dir to make d.ts globals available Path n4jsGlobalsDTS = N4jsLibsAccess.getN4JSGlobalsDTS(); - Files.copy(n4jsGlobalsDTS, workingDir.toPath().resolve("src-gen/n4jsglobals.d.ts")); + Files.copy(n4jsGlobalsDTS, wdSrcDir.resolve("n4jsglobals.d.ts")); ProcessResult result; try { diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/cutOffReferences/ReferenceToNonDtsProject.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/cutOffReferences/ReferenceToNonDtsProject.n4js.xt index 54f0ce532d..5a5a2027cc 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/cutOffReferences/ReferenceToNonDtsProject.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/cutOffReferences/ReferenceToNonDtsProject.n4js.xt @@ -51,7 +51,7 @@ export public class Cls1 extends ClassWithDts {} export public class Cls2 extends ClassWithoutDts {} /* XPECT generated_dts --- -import {ClassWithDts} from 'ProjectOtherWithDts/src-gen/OtherWithDts' +import type {ClassWithDts} from 'ProjectOtherWithDts/src-gen/OtherWithDts.d.ts' export let v1: ClassWithDts; export let v2: any; diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/BareImport.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/BareImport.n4js.xt new file mode 100644 index 0000000000..5a5b1aff60 --- /dev/null +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/BareImport.n4js.xt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ + +/* XPECT_SETUP org.eclipse.n4js.spec.tests.SpecXtTest + + Workspace { + Project "TestProject" { + Folder "src" { + ThisFile {} + File "BareImportMe.js" { } + } + } + } + + File "BareImportMe.js" { + export public class CP {} + } + + GENERATE_DTS + +END_SETUP */ + + + +import "BareImportMe"; + + + +/* XPECT generated_dts --- +import './BareImportMe.js' + +--- */ diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/DefaultAsImport.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/DefaultAsImport.n4js.xt index 2b63bbb7f9..59fc56e903 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/DefaultAsImport.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/DefaultAsImport.n4js.xt @@ -34,7 +34,7 @@ export public class Dummy extends MyClass {} // ensures that the import is not r /* XPECT generated_dts --- -import {default as MyClass} from './Other' +import type {default as MyClass} from './Other.d.ts' export class Dummy extends MyClass {} --- */ diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType1.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType1.n4js.xt index 92984a903e..2ae867d1ff 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType1.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType1.n4js.xt @@ -38,10 +38,10 @@ export public const redStringBased = ColorStringBased.RED; /* XPECT generated_dts --- -import {Color} from './Other' -import {ColorWithLiteralValues} from './Other' -import {ColorNumberBased} from './Other' -import {ColorStringBased} from './Other' +import type {Color} from './Other.d.ts' +import type {ColorWithLiteralValues} from './Other.d.ts' +import type {ColorNumberBased} from './Other.d.ts' +import type {ColorStringBased} from './Other.d.ts' export const red: Color; export const redWithLiteralValues: ColorWithLiteralValues; diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType2.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType2.n4js.xt index ae8ec42eee..18a28f899a 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType2.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportForLiteralType2.n4js.xt @@ -38,10 +38,10 @@ export public const redStringBased = getRedStringBased(); /* XPECT generated_dts --- -import {ColorStringBased} from './Other' -import {ColorNumberBased} from './Other' -import {ColorWithLiteralValues} from './Other' -import {Color} from './Other' +import type {ColorStringBased} from './Other.d.ts' +import type {ColorNumberBased} from './Other.d.ts' +import type {ColorWithLiteralValues} from './Other.d.ts' +import type {Color} from './Other.d.ts' export const red: Color; export const redWithLiteralValues: ColorWithLiteralValues; diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded1.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded1.n4js.xt index 16553ff463..d6cbc6a26e 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded1.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded1.n4js.xt @@ -32,7 +32,7 @@ export const C = F(); /* XPECT generated_dts --- -import {Cls} from './Other' +import type {Cls} from './Other.d.ts' export const C: Cls; --- */ diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded2_withAlias.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded2_withAlias.n4js.xt index ea9e4715de..21d13e0678 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded2_withAlias.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded2_withAlias.n4js.xt @@ -37,7 +37,7 @@ class Cls { /* XPECT generated_dts --- -import {Cls as Cls2} from './Other' +import type {Cls as Cls2} from './Other.d.ts' export const C: Cls2; declare class Cls {} diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded3_forIndirectlyExportedElem.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded3_forIndirectlyExportedElem.n4js.xt index d8ec8d886b..9dc41b4194 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded3_forIndirectlyExportedElem.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportMustBeAdded3_forIndirectlyExportedElem.n4js.xt @@ -43,7 +43,7 @@ export const C = F(); /* XPECT generated_dts --- -import {ClsExportedName} from './Other' +import type {ClsExportedName} from './Other.d.ts' export const C: ClsExportedName; --- */ diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportWithExtension.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportWithExtension.n4js.xt new file mode 100644 index 0000000000..3d194a8606 --- /dev/null +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ImportWithExtension.n4js.xt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ + +/* XPECT_SETUP org.eclipse.n4js.spec.tests.SpecXtTest + + Workspace { + Project "TestProject" { + Folder "src" { + ThisFile {} + } + DEPENDS_ON "ImportMe" + } + Project "ImportMe" { + Folder "src" { + File "index.n4js" { } + } + File "package.json" {} + } + } + + File "index.n4js" { + export public class CP {} + } + + File "package.json" { + { + "name": "ImportMe", + "version": "1.0.0", + "type": "module", + "types": "src-gen/index.d.ts", + "scripts": {}, + "n4js": { + "vendorId": "VENDOR", + "vendorName": "VENDOR_NAME", + "projectType": "library", + "output": "src-gen", + "generator": { "d.ts": true }, + "sources": { + "source": ["./src"] + } + }, + "dependencies": { + "n4js-runtime": "" + } + } + } + + GENERATE_DTS + +END_SETUP */ + + +import * as TS from "ImportMe"; + +export public class CPX extends TS.CP {} + + +/* XPECT generated_dts --- +import type * as TS from 'ImportMe' + +export class CPX extends TS.CP {} +--- */ diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ModuleSpecifiers.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ModuleSpecifiers.n4js.xt index 7ad8dbe387..688acc7358 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ModuleSpecifiers.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/ModuleSpecifiers.n4js.xt @@ -43,8 +43,8 @@ export let clsOtherLocal: ClsOtherLocal; /* XPECT generated_dts --- -import {ClsOther as ClsOtherRemote} from 'ProjectOther/src-gen/RemoteOther' -import {ClsOther as ClsOtherLocal} from './LocalOther' +import type {ClsOther as ClsOtherRemote} from 'ProjectOther/src-gen/RemoteOther.d.ts' +import type {ClsOther as ClsOtherLocal} from './LocalOther.d.ts' export let clsOtherRemote: ClsOtherRemote; export let clsOtherLocal: ClsOtherLocal; diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/NamespaceAccess.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/NamespaceAccess.n4js.xt index ba63efebe5..e0add12c13 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/NamespaceAccess.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/NamespaceAccess.n4js.xt @@ -36,7 +36,7 @@ export public class Cls extends NS.SomeClass {} /* XPECT generated_dts --- -import * as NS from './Other' +import type * as NS from './Other.d.ts' export let x: NS.SomeClass; export function foo(p: NS.SomeClass): NS.SomeClass; diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/UseExistingNamespaceImport.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/UseExistingNamespaceImport.n4js.xt index 228f6837ca..06709ba431 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/UseExistingNamespaceImport.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/imports/UseExistingNamespaceImport.n4js.xt @@ -30,7 +30,7 @@ export public let x = NS.foo(); /* XPECT generated_dts --- -import * as NS from './Other' +import type * as NS from './Other.d.ts' export let x: NS.ClsOther; --- */ diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypes2.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypes2.n4js.xt index e0ae62d480..08d88eebd5 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypes2.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypes2.n4js.xt @@ -58,7 +58,7 @@ export class ClassWithFields { /* XPECT generated_dts --- -import {ClassDirectExported} from 'ProjectOtherDirect/src-gen/OtherDirect' +import type {ClassDirectExported} from 'ProjectOtherDirect/src-gen/OtherDirect.d.ts' export let v1: ClassDirectExported; export let v2: any; diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypesImportUsedInStatement.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypesImportUsedInStatement.n4js.xt index fadb21228c..e46cb9c6cd 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypesImportUsedInStatement.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/InferredTypesImportUsedInStatement.n4js.xt @@ -37,7 +37,7 @@ export class ClassWithFields { /* XPECT generated_dts --- -import {ClassOther} from './Other' +import type {ClassOther} from './Other.d.ts' export let v: ClassOther; export class ClassWithFields { diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/ReplaceWildcard.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/ReplaceWildcard.n4js.xt index e8e6fe0e28..0aee3d89ee 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/ReplaceWildcard.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/ReplaceWildcard.n4js.xt @@ -29,8 +29,8 @@ import {C} from "Other"; const c: C = null /* XPECT generated_dts --- -import {D} from './Other' -import {C} from './Other' +import type {D} from './Other.d.ts' +import type {C} from './Other.d.ts' declare const c: C; --- */ diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/UpperBounds.n4js.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/UpperBounds.n4js.xt index b14a9dc340..88f4eb0872 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/UpperBounds.n4js.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/dts-export/inferredTypes/UpperBounds.n4js.xt @@ -43,9 +43,9 @@ export class ClassWithFields { /* XPECT generated_dts --- -import {ClassExported} from 'ProjectOther/src-gen/Other' -import {GenClassOfClassExported} from 'ProjectOther/src-gen/Other' -import {GenClassOfClassNotExported} from 'ProjectOther/src-gen/Other' +import type {ClassExported} from 'ProjectOther/src-gen/Other.d.ts' +import type {GenClassOfClassExported} from 'ProjectOther/src-gen/Other.d.ts' +import type {GenClassOfClassNotExported} from 'ProjectOther/src-gen/Other.d.ts' export let v1: GenClassOfClassExported; export let v2: GenClassOfClassNotExported;