From 7d45d1b2ffb6fcb894343b83792343e9304710e9 Mon Sep 17 00:00:00 2001 From: mmews Date: Mon, 8 Jan 2024 14:15:03 +0100 Subject: [PATCH 1/4] add tests --- .../PDTs/pnpmDependency1.pdt | 19 ++++++++++++++ .../PDTs/pnpmDependency2.pdt | 25 +++++++++++++++++++ .../PDTs/pnpmSimple1.pdt | 15 +++++++++++ .../PDTs/pnpmSimple2.pdt | 18 +++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency1.pdt create mode 100644 tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency2.pdt create mode 100644 tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple1.pdt create mode 100644 tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple2.pdt diff --git a/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency1.pdt b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency1.pdt new file mode 100644 index 0000000000..967da837a7 --- /dev/null +++ b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency1.pdt @@ -0,0 +1,19 @@ +# Project Discovery Test +# working dir marked with '*' + + +FOLDERS +-*Root [PROJECT workspaces-pnpm=["packages/*"]] +-- packages +--- P1 [PROJECT dependencies={"D1":"*"}] +---- node_modules +----- D1 [SYMLINK_TO_PATH Root/node_modules/.pnpm/D1] +-- node_modules +--- .pnpm +---- P1 [SYMLINK_TO_PATH Root/packages/P1] +---- D1 [PROJECT] + +EXPECT +- Root +- Root/packages/P1 +- Root/node_modules/.pnpm/D1 diff --git a/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency2.pdt b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency2.pdt new file mode 100644 index 0000000000..20515ae066 --- /dev/null +++ b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmDependency2.pdt @@ -0,0 +1,25 @@ +# Project Discovery Test +# working dir marked with '*' + + +FOLDERS +-*Root [PROJECT workspaces-pnpm=["packages/*"]] +-- packages +--- P1 [PROJECT dependencies={"D1":"*"}] +---- node_modules +----- D1 [SYMLINK_TO_PATH Root/node_modules/.pnpm/D1@0.0.0/node_modules/D1] +-- node_modules +--- .pnpm +---- P1 [SYMLINK_TO_PATH Root/packages/P1] +---- D1@0.0.0 +----- node_modules +------ D1 [PROJECT dependencies={"D2":"*"}] +------ D2 [SYMLINK_TO_PATH Root/node_modules/.pnpm/D2] +---- D2 [PROJECT] + + +EXPECT +- Root +- Root/packages/P1 +- Root/node_modules/.pnpm/D1@0.0.0/node_modules/D1 +- Root/node_modules/.pnpm/D2 diff --git a/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple1.pdt b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple1.pdt new file mode 100644 index 0000000000..2d17917600 --- /dev/null +++ b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple1.pdt @@ -0,0 +1,15 @@ +# Project Discovery Test +# working dir marked with '*' + + +FOLDERS +-*P1 [PROJECT workspaces-pnpm=["packages/*"]] +-- packages +--- D1 [PROJECT] +-- node_modules +--- D1 [SYMLINK_TO_PATH P1/packages/D1] + + +EXPECT +- P1 +- P1/packages/D1 diff --git a/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple2.pdt b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple2.pdt new file mode 100644 index 0000000000..1999bba0ee --- /dev/null +++ b/tests/org.eclipse.n4js.ide.tests/PDTs/pnpmSimple2.pdt @@ -0,0 +1,18 @@ +# Project Discovery Test +# working dir marked with '*' + + +FOLDERS +-*P1 [PROJECT workspaces=["packages/*"]] +-- packages +--- D1 [PROJECT dependencies={"D2":"*"}] +--- D2 [PROJECT] +-- node_modules +--- D1 [SYMLINK_TO_PATH P1/packages/D1] +--- D2 [SYMLINK_TO_PATH P1/packages/D2] + + +EXPECT +- P1 +- P1/packages/D1 +- P1/packages/D2 From 180bc4fb3467b3e833e8cdfeb52681a2a4ac6050 Mon Sep 17 00:00:00 2001 From: mmews Date: Mon, 8 Jan 2024 14:15:33 +0100 Subject: [PATCH 2/4] support pnpm for project discovery tests --- .../CreateProjectStructureUtils.java | 75 +++++++++++++------ 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/projectdiscovery/CreateProjectStructureUtils.java b/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/projectdiscovery/CreateProjectStructureUtils.java index 0479144e29..a19fd17c46 100644 --- a/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/projectdiscovery/CreateProjectStructureUtils.java +++ b/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/projectdiscovery/CreateProjectStructureUtils.java @@ -30,13 +30,18 @@ */ public class CreateProjectStructureUtils { + enum WorkspaceType { + Yarn, Pnpm + } + static class Folder { final Folder parent; final String folderName; final boolean isWorkingDir; final boolean isProject; final boolean isPlainJS; - final String yarnWorkspacesFolder; + final WorkspaceType workspaceType; + final String workspacesFolder; // either yarn or pnpm final String name; final String dependencies; final String packagejson; @@ -47,14 +52,16 @@ static class Folder { final Path symLinkTarget; Folder(Folder parent, String folderName, boolean isWorkingDir, boolean isProject, boolean isPlainJS, - String yarnWorkspacesFolder, String name, String dependencies, Path symLinkTarget, String packagejson) { + WorkspaceType workspaceType, String workspacesFolder, String name, String dependencies, + Path symLinkTarget, String packagejson) { this.parent = parent; this.folderName = folderName; this.isWorkingDir = isWorkingDir; this.isProject = isProject; this.isPlainJS = isPlainJS; - this.yarnWorkspacesFolder = yarnWorkspacesFolder; + this.workspaceType = workspaceType; + this.workspacesFolder = workspacesFolder; this.name = name; this.dependencies = dependencies; this.symLinkTarget = symLinkTarget; @@ -76,7 +83,7 @@ public String toString() { str += folderName; if (isProject) { str += "[PROJECT"; - str += (yarnWorkspacesFolder == null) ? "" : " workspaces= " + yarnWorkspacesFolder + " "; + str += (workspacesFolder == null) ? "" : " workspaces= " + workspacesFolder + " "; str += (name == null) ? "" : " name= " + name + " "; str += (dependencies == null) ? "" : " dependencies= " + dependencies + " "; str += (packagejson == null) ? "" : " package.json= " + packagejson + " "; @@ -205,7 +212,8 @@ private static Folder readFolder(String folderStr, List parents) { boolean isProject = false; boolean isPlainJS = false; - String yarnWorkspacesFolder = null; + WorkspaceType workspaceType = WorkspaceType.Yarn; + String workspacesFolder = null; String dependencies = null; String name = null; Path symLinkTarget = null; @@ -246,11 +254,21 @@ private static Folder readFolder(String folderStr, List parents) { if (restLine.startsWith("workspaces")) { restLine = restLine.substring("workspaces".length()).trim(); + if (restLine.startsWith("-yarn")) { + // default + restLine = restLine.substring("-yarn".length()).trim(); + workspaceType = WorkspaceType.Yarn; + } + if (restLine.startsWith("-pnpm")) { + restLine = restLine.substring("-pnpm".length()).trim(); + workspaceType = WorkspaceType.Pnpm; + } if (restLine.startsWith("=")) { restLine = restLine.substring(1).trim(); int startIndex = restLine.indexOf("["); int endIndex = restLine.indexOf("]"); - yarnWorkspacesFolder = restLine.substring(startIndex, endIndex + 1); + // includes brackets + workspacesFolder = restLine.substring(startIndex, endIndex + 1); restLine = restLine.substring(endIndex + 1).trim(); } @@ -280,25 +298,26 @@ private static Folder readFolder(String folderStr, List parents) { } } - return new Folder(parent, folderName, isWorkingDir, isProject, isPlainJS, yarnWorkspacesFolder, name, - dependencies, - symLinkTarget, packagejson); + return new Folder(parent, folderName, isWorkingDir, isProject, isPlainJS, workspaceType, workspacesFolder, name, + dependencies, symLinkTarget, packagejson); } /** Creates the folder structure specified by {@link ProjectDiscoveryTestData} in the given dir */ public static void createFolderStructure(File dir, ProjectDiscoveryTestData pdtd) { for (Folder folder : pdtd.folders) { - String folderPath = folder.getPath(); - File folderFile = new File(dir, folderPath); - if (folder.symLinkTarget != null) { - createSymbolicLink(dir, folderFile, folder.symLinkTarget); - } else { + if (folder.symLinkTarget == null) { + File folderFile = new File(dir, folder.getPath()); folderFile.mkdir(); if (folder.isProject) { createPackageJson(folderFile, folder); } } } + for (Folder folder : pdtd.folders) { + if (folder.symLinkTarget != null) { + createSymbolicLink(dir, new File(dir, folder.getPath()), folder.symLinkTarget); + } + } } private static void createSymbolicLink(File root, File folderFile, Path symLinkTarget) { @@ -319,7 +338,7 @@ private static void createPackageJson(File folderFile, Folder folder) { contents = folder.packagejson; } else { contents = "{"; - if (folder.yarnWorkspacesFolder == null) { + if (folder.workspacesFolder == null) { String projectName = folder.folderName; if (folder.parent != null && folder.parent.folderName.startsWith("@")) { projectName = folder.parent.folderName + "/" + projectName; @@ -328,19 +347,29 @@ private static void createPackageJson(File folderFile, Folder folder) { if (!folder.isPlainJS) { contents += "\"n4js\": {\"projectType\": \"library\"}"; } - } else { - contents += "\"private\": true, \"workspaces\": " + folder.yarnWorkspacesFolder + ""; + } else if (folder.workspaceType == WorkspaceType.Yarn) { + contents += "\"private\": true, \"workspaces\": " + folder.workspacesFolder + ""; + } else if (folder.workspaceType == WorkspaceType.Pnpm) { + File pnpmWorkspaceYaml = new File(folderFile, N4JSGlobals.PNPM_WORKSPACE); + try (PrintWriter printWriter = new PrintWriter(new FileWriter(pnpmWorkspaceYaml))) { + String workspacesFolder = folder.workspacesFolder.substring(1, + folder.workspacesFolder.length() - 1); + printWriter.println("packages:\n - " + workspacesFolder); + } catch (IOException e) { + throw new WrappedException("exception while creating a package.json file", e); + } } contents += (folder.dependencies == null) ? "" : ", \"dependencies\":" + folder.dependencies; contents += "}"; } - File packageJson = new File(folderFile, N4JSGlobals.PACKAGE_JSON); - - try (PrintWriter printWriter = new PrintWriter(new FileWriter(packageJson))) { - printWriter.println(contents); - } catch (IOException e) { - throw new WrappedException("exception while creating a package.json file", e); + if (!contents.isBlank()) { + File packageJson = new File(folderFile, N4JSGlobals.PACKAGE_JSON); + try (PrintWriter printWriter = new PrintWriter(new FileWriter(packageJson))) { + printWriter.println(contents); + } catch (IOException e) { + throw new WrappedException("exception while creating a package.json file", e); + } } } } From ce20bc71921b440eb18615064e93878ad08852c0 Mon Sep 17 00:00:00 2001 From: mmews Date: Mon, 8 Jan 2024 14:15:53 +0100 Subject: [PATCH 3/4] fix bug in node_modules identification --- .../src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java index f627ee01ed..b35af93394 100644 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java @@ -431,7 +431,6 @@ private Set collectNecessaryDependencies(Set allProjectDirs, Map allProjectDirs, Path projectDir, Map pdCache, Set dependencies) { - NodeModulesFolder nodeModulesFolder = nodeModulesDiscoveryHelper.getNodeModulesFolder(projectDir, pdCache); LinkedHashSet workList = new LinkedHashSet<>(); workList.add(projectDir); while (!workList.isEmpty()) { @@ -439,6 +438,7 @@ private void collectProjectDependencies(Set allProjectDirs, Path projectDi Path nextProject = iterator.next(); iterator.remove(); + NodeModulesFolder nodeModulesFolder = nodeModulesDiscoveryHelper.getNodeModulesFolder(nextProject, pdCache); findDependencies(allProjectDirs, nextProject, nodeModulesFolder, pdCache, workList, dependencies); } } From 75a184d634f230b1bfa41cbef70027ce6480f2a4 Mon Sep 17 00:00:00 2001 From: mmews Date: Thu, 18 Jan 2024 14:19:20 +0100 Subject: [PATCH 4/4] fix project discovery and ide tests --- .../n4js/utils/NodeModulesDiscoveryHelper.java | 12 ++++++++++-- .../n4js/utils/ProjectDiscoveryHelper.java | 17 ++++++++++------- .../helper/server/TestWorkspaceManager.java | 15 +++++++++++++-- .../helper/mock/MockWorkspaceSupplier.java | 2 +- .../IncrementalBuilderCopiedProjectTest.java | 4 ++-- .../buildorder/AbstractBuildOrderTest.java | 8 ++++++-- .../buildorder/BuildOrderDependenciesTest.java | 5 +++-- .../OrganizeImportsAddMissingTest.java | 1 - 8 files changed, 45 insertions(+), 19 deletions(-) diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/NodeModulesDiscoveryHelper.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/NodeModulesDiscoveryHelper.java index 932e7d535a..1bbec41944 100644 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/NodeModulesDiscoveryHelper.java +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/NodeModulesDiscoveryHelper.java @@ -85,13 +85,21 @@ public boolean isYarnWorkspace() { return this.workspaceNodeModulesFolder != null; } - /** @return the related root of this project */ - public Path getRelatedRoot() { + /** @return the local root of this project */ + public Path getLocalRoot() { if (isYarnWorkspaceRoot || isYarnWorkspaceMember || isYarnWorkspaceDependency) { return workspaceNodeModulesFolder.getParentFile().toPath(); } return localNodeModulesFolder.getParentFile().toPath(); } + + /** @return the workspace root of this project */ + public Path getWorkspaceRoot() { + if (workspaceNodeModulesFolder != null) { + return workspaceNodeModulesFolder.getParentFile().toPath(); + } + return localNodeModulesFolder.getParentFile().toPath(); + } } /** @return the node_modules folder of the given project including a flag if this is a yarn workspace. */ diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java index b35af93394..560fd3330f 100644 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/utils/ProjectDiscoveryHelper.java @@ -431,6 +431,8 @@ private Set collectNecessaryDependencies(Set allProjectDirs, Map allProjectDirs, Path projectDir, Map pdCache, Set dependencies) { + Path wsRoot = nodeModulesDiscoveryHelper.getNodeModulesFolder(projectDir, pdCache).getWorkspaceRoot(); + LinkedHashSet workList = new LinkedHashSet<>(); workList.add(projectDir); while (!workList.isEmpty()) { @@ -439,16 +441,17 @@ private void collectProjectDependencies(Set allProjectDirs, Path projectDi iterator.remove(); NodeModulesFolder nodeModulesFolder = nodeModulesDiscoveryHelper.getNodeModulesFolder(nextProject, pdCache); - findDependencies(allProjectDirs, nextProject, nodeModulesFolder, pdCache, workList, dependencies); + findDependencies(allProjectDirs, nextProject, wsRoot, nodeModulesFolder, pdCache, workList, dependencies); } } - private void findDependencies(Set allProjectDirs, Path prjDir, NodeModulesFolder nodeModulesFolder, - Map pdCache, Set workList, Set dependencies) { + private void findDependencies(Set allProjectDirs, Path prjDir, Path wsRoot, + NodeModulesFolder nodeModulesFolder, Map pdCache, Set workList, + Set dependencies) { - Path relatedRoot = nodeModulesFolder.getRelatedRoot(); + // Path wsRoot = nodeModulesFolder.getWorkspaceRoot(); Path prjNodeModules = prjDir.resolve(N4JSGlobals.NODE_MODULES); - ProjectDescription prjDescr = getOrCreateProjectDescription(prjDir, relatedRoot, pdCache); + ProjectDescription prjDescr = getOrCreateProjectDescription(prjDir, wsRoot, pdCache); if (prjDescr == null) { return; } @@ -457,8 +460,8 @@ private void findDependencies(Set allProjectDirs, Path prjDir, NodeModules String depName = dependency.getPackageName(); Path depLocation = findDependencyLocation(nodeModulesFolder, prjNodeModules, depName); - if (isNewDependency(allProjectDirs, prjDir, pdCache, depLocation, relatedRoot)) { - addDependency(depLocation, relatedRoot, pdCache, workList, dependencies); + if (isNewDependency(allProjectDirs, prjDir, pdCache, depLocation, wsRoot)) { + addDependency(depLocation, wsRoot, pdCache, workList, dependencies); } } } diff --git a/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/TestWorkspaceManager.java b/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/TestWorkspaceManager.java index 27371cf37e..8c164f392c 100644 --- a/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/TestWorkspaceManager.java +++ b/testhelpers/org.eclipse.n4js.ide.tests.helper/src/org/eclipse/n4js/ide/tests/helper/server/TestWorkspaceManager.java @@ -582,6 +582,7 @@ private Project createSimpleProject(String projectName, Map> cy throw new RuntimeException("Never happens since toString never throws an exception. Bogus xtext warning", exc); } - assertEquals(cycles.size(), projectBuildOrderInfo.getProjectCycles().size()); + ImmutableCollection> projectCycles = projectBuildOrderInfo.getProjectCycles(); + assertEquals("Cycle: " + Strings.join(", ", projectCycles), cycles.size(), projectCycles.size()); Set expectedCycles = cycles.stream().map(it -> Strings.join(", ", it)).collect(Collectors.toSet()); - for (List cycle : projectBuildOrderInfo.getProjectCycles()) { + for (List cycle : projectCycles) { String detectedCycle = Strings.join(", ", cycle); assertTrue("Cycle " + detectedCycle + " not found in " + expectedCycles, expectedCycles.contains(detectedCycle)); diff --git a/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/buildorder/BuildOrderDependenciesTest.java b/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/buildorder/BuildOrderDependenciesTest.java index 7e7b437ba3..71c0ba20a1 100644 --- a/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/buildorder/BuildOrderDependenciesTest.java +++ b/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/buildorder/BuildOrderDependenciesTest.java @@ -17,13 +17,13 @@ /** * Test for build order */ - public class BuildOrderDependenciesTest extends AbstractBuildOrderTest { @Test public void testSingleDependency1() { testProject( "test-project/node_modules/n4js-runtime, " + + "test-project/node_modules/D2/node_modules/n4js-runtime, " + "test-project/node_modules/D2, " + "test-project/node_modules/D1, " + "test-project", @@ -45,8 +45,9 @@ public void testSingleDependency1() { @Test public void testSingleDependency2() { testProject( - "test-project/node_modules/n4js-runtime, " + + "test-project/node_modules/@scope/D2/node_modules/n4js-runtime, " + "test-project/node_modules/@scope/D2, " + + "test-project/node_modules/n4js-runtime, " + "test-project/node_modules/D1, " + "test-project", Map.of( diff --git a/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/codeActions/OrganizeImportsAddMissingTest.java b/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/codeActions/OrganizeImportsAddMissingTest.java index 9a2f7c364a..8f7b5ee4ff 100644 --- a/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/codeActions/OrganizeImportsAddMissingTest.java +++ b/tests/org.eclipse.n4js.ide.tests/src/org/eclipse/n4js/ide/tests/codeActions/OrganizeImportsAddMissingTest.java @@ -18,7 +18,6 @@ /** * Like {@link OrganizeImportsTest}, but covers cases in which a missing import has to be added during organize imports. */ - public class OrganizeImportsAddMissingTest extends AbstractOrganizeImportsTest { @Test