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 f627ee01ed..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,7 +431,8 @@ private Set collectNecessaryDependencies(Set allProjectDirs, Map allProjectDirs, Path projectDir, Map pdCache, Set dependencies) { - NodeModulesFolder nodeModulesFolder = nodeModulesDiscoveryHelper.getNodeModulesFolder(projectDir, pdCache); + Path wsRoot = nodeModulesDiscoveryHelper.getNodeModulesFolder(projectDir, pdCache).getWorkspaceRoot(); + LinkedHashSet workList = new LinkedHashSet<>(); workList.add(projectDir); while (!workList.isEmpty()) { @@ -439,16 +440,18 @@ private void collectProjectDependencies(Set allProjectDirs, Path projectDi Path nextProject = iterator.next(); iterator.remove(); - findDependencies(allProjectDirs, nextProject, nodeModulesFolder, pdCache, workList, dependencies); + NodeModulesFolder nodeModulesFolder = nodeModulesDiscoveryHelper.getNodeModulesFolder(nextProject, pdCache); + 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 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); + } } } }