Skip to content
This repository was archived by the owner on Jul 10, 2025. It is now read-only.

Commit 514663a

Browse files
author
Akim
authored
fix(npm-aqua-compiler): Support aquaDir inside the project's node_nodules (#427)
When aqua dir inside the project's node_nodules dir, return only the subtree based on that internal path
1 parent fa38328 commit 514663a

File tree

4 files changed

+177
-66
lines changed

4 files changed

+177
-66
lines changed

packages/core/npm-aqua-compiler/src/imports.spec.ts

Lines changed: 119 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,67 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { join } from "path";
17+
import { join, resolve } from "path";
1818
import { fileURLToPath } from "url";
1919

2020
import { assert, describe, expect, it } from "vitest";
2121

22-
import { gatherImportsFromNpm } from "./imports.js";
22+
import { gatherImportsFromNpm, GatherImportsResult } from "./imports.js";
23+
24+
const prefix = join(
25+
fileURLToPath(new URL("./", import.meta.url)),
26+
"..",
27+
"test",
28+
"transitive-deps",
29+
"project",
30+
);
31+
32+
function buildResolutionKey(str: string) {
33+
return str
34+
.slice(prefix.length)
35+
.split("/node_modules/")
36+
.filter(Boolean)
37+
.join("/");
38+
}
39+
40+
function matchTree(
41+
expected: GatherImportsResult,
42+
actual: GatherImportsResult,
43+
aquaToCompileDirPath: string | undefined,
44+
) {
45+
if (aquaToCompileDirPath !== undefined) {
46+
aquaToCompileDirPath = resolve(aquaToCompileDirPath);
47+
}
48+
49+
expect(Object.keys(actual).length).toBe(Object.keys(expected).length);
50+
51+
Object.entries(actual).forEach(([key, value]) => {
52+
const resolutionKey =
53+
key === aquaToCompileDirPath ? key : buildResolutionKey(key);
54+
55+
const resolutionValues = expected[resolutionKey];
56+
57+
assert(resolutionValues);
58+
59+
expect(Object.keys(value).length).toBe(
60+
Object.keys(resolutionValues).length,
61+
);
62+
63+
for (const [dep, path] of Object.entries(value)) {
64+
if (Array.isArray(path)) {
65+
expect(dep).toBe("");
66+
expect(expected[resolutionKey]).toHaveProperty(dep, path);
67+
68+
continue;
69+
}
70+
71+
expect(expected[resolutionKey]).toHaveProperty(
72+
dep,
73+
buildResolutionKey(path),
74+
);
75+
}
76+
});
77+
}
2378

2479
describe("imports", () => {
2580
/**
@@ -35,44 +90,56 @@ describe("imports", () => {
3590
string,
3691
Record<string, string[] | string>
3792
> = {
38-
[aquaToCompileDirPath]: {
93+
[resolve(aquaToCompileDirPath)]: {
3994
"": globalImports,
40-
A: "./A",
41-
B: "./B",
95+
A: "A",
96+
B: "B",
4297
},
43-
"./A": {
44-
C: "./C",
45-
D: "./D",
98+
A: {
99+
C: "C",
100+
D: "D",
46101
},
47-
"./B": {
48-
C: "./B/C",
49-
D: "./B/D",
102+
B: {
103+
C: "B/C",
104+
D: "B/D",
50105
},
51-
"./C": {
52-
D: "./C/D",
106+
C: {
107+
D: "C/D",
53108
},
54-
"./B/C": {
55-
D: "./B/C/D",
109+
"B/C": {
110+
D: "B/C/D",
56111
},
57112
};
58113

59-
const prefix = join(
60-
fileURLToPath(new URL("./", import.meta.url)),
61-
"..",
62-
"test",
63-
"transitive-deps",
64-
"project",
65-
);
114+
const imports = await gatherImportsFromNpm({
115+
npmProjectDirPath,
116+
aquaToCompileDirPath,
117+
globalImports,
118+
});
66119

67-
const buildResolutionKey = (str: string) => {
68-
return (
69-
"./" +
70-
str
71-
.slice(prefix.length)
72-
.split("/node_modules/")
73-
.filter(Boolean)
74-
.join("/")
75-
);
120+
matchTree(expectedResolution, imports, aquaToCompileDirPath);
121+
});
122+
123+
it("should resolve transitive dependencies and return a subtree when 'aquaToCompileDirPath' inside project 'node_modules' folder", async () => {
124+
const npmProjectDirPath = "./test/transitive-deps/project";
125+
126+
const aquaToCompileDirPath =
127+
"./test/transitive-deps/project/node_modules/A";
128+
129+
const globalImports = ["./.fluence/aqua"];
130+
131+
const expectedResolution: Record<
132+
string,
133+
Record<string, string[] | string>
134+
> = {
135+
[resolve(aquaToCompileDirPath)]: {
136+
"": globalImports,
137+
C: "C",
138+
D: "D",
139+
},
140+
C: {
141+
D: "C/D",
142+
},
76143
};
77144

78145
const imports = await gatherImportsFromNpm({
@@ -81,35 +148,32 @@ describe("imports", () => {
81148
globalImports,
82149
});
83150

84-
expect(Object.keys(imports).length).toBe(
85-
Object.keys(expectedResolution).length,
86-
);
87-
88-
Object.entries(imports).forEach(([key, value]) => {
89-
const resolutionKey =
90-
key === aquaToCompileDirPath ? key : buildResolutionKey(key);
91-
92-
const resolutionValues = expectedResolution[resolutionKey];
151+
matchTree(expectedResolution, imports, aquaToCompileDirPath);
152+
});
93153

94-
assert(resolutionValues);
154+
it("should resolve transitive dependencies when project is empty", async () => {
155+
const npmProjectDirPath = "./test/transitive-deps/empty-project";
95156

96-
expect(Object.keys(value).length).toBe(
97-
Object.keys(resolutionValues).length,
98-
);
157+
const aquaToCompileDirPath =
158+
"./test/transitive-deps/empty-project/node_modules/A";
99159

100-
for (const [dep, path] of Object.entries(value)) {
101-
if (Array.isArray(path)) {
102-
expect(dep).toBe("");
103-
expect(expectedResolution[resolutionKey]).toHaveProperty(dep, path);
160+
const globalImports = ["./.fluence/aqua"];
104161

105-
continue;
106-
}
162+
const expectedResolution: Record<
163+
string,
164+
Record<string, string[] | string>
165+
> = {
166+
[resolve(aquaToCompileDirPath)]: {
167+
"": globalImports,
168+
},
169+
};
107170

108-
expect(expectedResolution[resolutionKey]).toHaveProperty(
109-
dep,
110-
buildResolutionKey(path),
111-
);
112-
}
171+
const imports = await gatherImportsFromNpm({
172+
npmProjectDirPath,
173+
aquaToCompileDirPath,
174+
globalImports,
113175
});
176+
177+
matchTree(expectedResolution, imports, aquaToCompileDirPath);
114178
});
115179
});

packages/core/npm-aqua-compiler/src/imports.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { resolve } from "path";
18+
1719
import Arborist from "@npmcli/arborist";
1820
import { breadth } from "treeverse";
1921

@@ -40,7 +42,9 @@ export async function gatherImportsFromNpm({
4042
* Traverse dependency tree to construct map
4143
* (real path of a package) -> (real paths of its immediate dependencies)
4244
*/
43-
const result: GatherImportsResult = {};
45+
let result: Record<string, Record<string, string>> = {};
46+
const rootDepsKey = "";
47+
const aquaDepPath = resolve(aquaToCompileDirPath ?? npmProjectDirPath);
4448

4549
breadth({
4650
tree,
@@ -64,15 +68,8 @@ export async function gatherImportsFromNpm({
6468

6569
// Root node should have top-level property pointed to aqua dependency folder
6670
if (node.isRoot) {
67-
const aquaDepPath = aquaToCompileDirPath ?? npmProjectDirPath;
68-
69-
result[aquaDepPath] = {
70-
...(result[aquaDepPath] ??
71-
(globalImports.length > 0
72-
? {
73-
"": globalImports,
74-
}
75-
: {})),
71+
result[rootDepsKey] = {
72+
...result[rootDepsKey],
7673
[dep.name]: dep.realpath,
7774
};
7875
} else {
@@ -88,5 +85,38 @@ export async function gatherImportsFromNpm({
8885
},
8986
});
9087

91-
return result;
88+
// In case 'aquaToCompileDirPath' points to any dependency inside current project
89+
// Only the subtree with 'aquaToCompileDirPath' as root node is returned
90+
if (aquaToCompileDirPath !== undefined && aquaDepPath in result) {
91+
// Other nodes which are not included in the subtree simply dropped
92+
const newResult: Record<string, Record<string, string>> = {};
93+
94+
breadth({
95+
tree: aquaDepPath,
96+
getChildren: (node) => {
97+
const deps = result[node];
98+
99+
if (deps === undefined) {
100+
return [];
101+
}
102+
103+
const isRootNode = node === aquaDepPath;
104+
newResult[isRootNode ? rootDepsKey : node] = deps;
105+
return Object.values(deps);
106+
},
107+
});
108+
109+
result = newResult;
110+
}
111+
112+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
113+
const { [rootDepsKey]: _, ...rest } = result;
114+
115+
return {
116+
...rest,
117+
[aquaDepPath]: {
118+
...result[rootDepsKey],
119+
"": globalImports,
120+
},
121+
};
92122
}

packages/core/npm-aqua-compiler/test/transitive-deps/empty-project/package-lock.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "empty-project",
3+
"version": "0.1.0",
4+
"dependencies": {}
5+
}

0 commit comments

Comments
 (0)