mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-04-25 09:52:12 +00:00
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
This commit is contained in:
parent
fa38328fdd
commit
514663a4fd
@ -14,12 +14,67 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { join } from "path";
|
import { join, resolve } from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
import { assert, describe, expect, it } from "vitest";
|
import { assert, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { gatherImportsFromNpm } from "./imports.js";
|
import { gatherImportsFromNpm, GatherImportsResult } from "./imports.js";
|
||||||
|
|
||||||
|
const prefix = join(
|
||||||
|
fileURLToPath(new URL("./", import.meta.url)),
|
||||||
|
"..",
|
||||||
|
"test",
|
||||||
|
"transitive-deps",
|
||||||
|
"project",
|
||||||
|
);
|
||||||
|
|
||||||
|
function buildResolutionKey(str: string) {
|
||||||
|
return str
|
||||||
|
.slice(prefix.length)
|
||||||
|
.split("/node_modules/")
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchTree(
|
||||||
|
expected: GatherImportsResult,
|
||||||
|
actual: GatherImportsResult,
|
||||||
|
aquaToCompileDirPath: string | undefined,
|
||||||
|
) {
|
||||||
|
if (aquaToCompileDirPath !== undefined) {
|
||||||
|
aquaToCompileDirPath = resolve(aquaToCompileDirPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(Object.keys(actual).length).toBe(Object.keys(expected).length);
|
||||||
|
|
||||||
|
Object.entries(actual).forEach(([key, value]) => {
|
||||||
|
const resolutionKey =
|
||||||
|
key === aquaToCompileDirPath ? key : buildResolutionKey(key);
|
||||||
|
|
||||||
|
const resolutionValues = expected[resolutionKey];
|
||||||
|
|
||||||
|
assert(resolutionValues);
|
||||||
|
|
||||||
|
expect(Object.keys(value).length).toBe(
|
||||||
|
Object.keys(resolutionValues).length,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [dep, path] of Object.entries(value)) {
|
||||||
|
if (Array.isArray(path)) {
|
||||||
|
expect(dep).toBe("");
|
||||||
|
expect(expected[resolutionKey]).toHaveProperty(dep, path);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(expected[resolutionKey]).toHaveProperty(
|
||||||
|
dep,
|
||||||
|
buildResolutionKey(path),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe("imports", () => {
|
describe("imports", () => {
|
||||||
/**
|
/**
|
||||||
@ -35,81 +90,90 @@ describe("imports", () => {
|
|||||||
string,
|
string,
|
||||||
Record<string, string[] | string>
|
Record<string, string[] | string>
|
||||||
> = {
|
> = {
|
||||||
[aquaToCompileDirPath]: {
|
[resolve(aquaToCompileDirPath)]: {
|
||||||
"": globalImports,
|
"": globalImports,
|
||||||
A: "./A",
|
A: "A",
|
||||||
B: "./B",
|
B: "B",
|
||||||
},
|
},
|
||||||
"./A": {
|
A: {
|
||||||
C: "./C",
|
C: "C",
|
||||||
D: "./D",
|
D: "D",
|
||||||
},
|
},
|
||||||
"./B": {
|
B: {
|
||||||
C: "./B/C",
|
C: "B/C",
|
||||||
D: "./B/D",
|
D: "B/D",
|
||||||
},
|
},
|
||||||
"./C": {
|
C: {
|
||||||
D: "./C/D",
|
D: "C/D",
|
||||||
},
|
},
|
||||||
"./B/C": {
|
"B/C": {
|
||||||
D: "./B/C/D",
|
D: "B/C/D",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const prefix = join(
|
|
||||||
fileURLToPath(new URL("./", import.meta.url)),
|
|
||||||
"..",
|
|
||||||
"test",
|
|
||||||
"transitive-deps",
|
|
||||||
"project",
|
|
||||||
);
|
|
||||||
|
|
||||||
const buildResolutionKey = (str: string) => {
|
|
||||||
return (
|
|
||||||
"./" +
|
|
||||||
str
|
|
||||||
.slice(prefix.length)
|
|
||||||
.split("/node_modules/")
|
|
||||||
.filter(Boolean)
|
|
||||||
.join("/")
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const imports = await gatherImportsFromNpm({
|
const imports = await gatherImportsFromNpm({
|
||||||
npmProjectDirPath,
|
npmProjectDirPath,
|
||||||
aquaToCompileDirPath,
|
aquaToCompileDirPath,
|
||||||
globalImports,
|
globalImports,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(Object.keys(imports).length).toBe(
|
matchTree(expectedResolution, imports, aquaToCompileDirPath);
|
||||||
Object.keys(expectedResolution).length,
|
});
|
||||||
);
|
|
||||||
|
|
||||||
Object.entries(imports).forEach(([key, value]) => {
|
it("should resolve transitive dependencies and return a subtree when 'aquaToCompileDirPath' inside project 'node_modules' folder", async () => {
|
||||||
const resolutionKey =
|
const npmProjectDirPath = "./test/transitive-deps/project";
|
||||||
key === aquaToCompileDirPath ? key : buildResolutionKey(key);
|
|
||||||
|
|
||||||
const resolutionValues = expectedResolution[resolutionKey];
|
const aquaToCompileDirPath =
|
||||||
|
"./test/transitive-deps/project/node_modules/A";
|
||||||
|
|
||||||
assert(resolutionValues);
|
const globalImports = ["./.fluence/aqua"];
|
||||||
|
|
||||||
expect(Object.keys(value).length).toBe(
|
const expectedResolution: Record<
|
||||||
Object.keys(resolutionValues).length,
|
string,
|
||||||
);
|
Record<string, string[] | string>
|
||||||
|
> = {
|
||||||
|
[resolve(aquaToCompileDirPath)]: {
|
||||||
|
"": globalImports,
|
||||||
|
C: "C",
|
||||||
|
D: "D",
|
||||||
|
},
|
||||||
|
C: {
|
||||||
|
D: "C/D",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
for (const [dep, path] of Object.entries(value)) {
|
const imports = await gatherImportsFromNpm({
|
||||||
if (Array.isArray(path)) {
|
npmProjectDirPath,
|
||||||
expect(dep).toBe("");
|
aquaToCompileDirPath,
|
||||||
expect(expectedResolution[resolutionKey]).toHaveProperty(dep, path);
|
globalImports,
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(expectedResolution[resolutionKey]).toHaveProperty(
|
|
||||||
dep,
|
|
||||||
buildResolutionKey(path),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
matchTree(expectedResolution, imports, aquaToCompileDirPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve transitive dependencies when project is empty", async () => {
|
||||||
|
const npmProjectDirPath = "./test/transitive-deps/empty-project";
|
||||||
|
|
||||||
|
const aquaToCompileDirPath =
|
||||||
|
"./test/transitive-deps/empty-project/node_modules/A";
|
||||||
|
|
||||||
|
const globalImports = ["./.fluence/aqua"];
|
||||||
|
|
||||||
|
const expectedResolution: Record<
|
||||||
|
string,
|
||||||
|
Record<string, string[] | string>
|
||||||
|
> = {
|
||||||
|
[resolve(aquaToCompileDirPath)]: {
|
||||||
|
"": globalImports,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const imports = await gatherImportsFromNpm({
|
||||||
|
npmProjectDirPath,
|
||||||
|
aquaToCompileDirPath,
|
||||||
|
globalImports,
|
||||||
|
});
|
||||||
|
|
||||||
|
matchTree(expectedResolution, imports, aquaToCompileDirPath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { resolve } from "path";
|
||||||
|
|
||||||
import Arborist from "@npmcli/arborist";
|
import Arborist from "@npmcli/arborist";
|
||||||
import { breadth } from "treeverse";
|
import { breadth } from "treeverse";
|
||||||
|
|
||||||
@ -40,7 +42,9 @@ export async function gatherImportsFromNpm({
|
|||||||
* Traverse dependency tree to construct map
|
* Traverse dependency tree to construct map
|
||||||
* (real path of a package) -> (real paths of its immediate dependencies)
|
* (real path of a package) -> (real paths of its immediate dependencies)
|
||||||
*/
|
*/
|
||||||
const result: GatherImportsResult = {};
|
let result: Record<string, Record<string, string>> = {};
|
||||||
|
const rootDepsKey = "";
|
||||||
|
const aquaDepPath = resolve(aquaToCompileDirPath ?? npmProjectDirPath);
|
||||||
|
|
||||||
breadth({
|
breadth({
|
||||||
tree,
|
tree,
|
||||||
@ -64,15 +68,8 @@ export async function gatherImportsFromNpm({
|
|||||||
|
|
||||||
// Root node should have top-level property pointed to aqua dependency folder
|
// Root node should have top-level property pointed to aqua dependency folder
|
||||||
if (node.isRoot) {
|
if (node.isRoot) {
|
||||||
const aquaDepPath = aquaToCompileDirPath ?? npmProjectDirPath;
|
result[rootDepsKey] = {
|
||||||
|
...result[rootDepsKey],
|
||||||
result[aquaDepPath] = {
|
|
||||||
...(result[aquaDepPath] ??
|
|
||||||
(globalImports.length > 0
|
|
||||||
? {
|
|
||||||
"": globalImports,
|
|
||||||
}
|
|
||||||
: {})),
|
|
||||||
[dep.name]: dep.realpath,
|
[dep.name]: dep.realpath,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -88,5 +85,38 @@ export async function gatherImportsFromNpm({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
// In case 'aquaToCompileDirPath' points to any dependency inside current project
|
||||||
|
// Only the subtree with 'aquaToCompileDirPath' as root node is returned
|
||||||
|
if (aquaToCompileDirPath !== undefined && aquaDepPath in result) {
|
||||||
|
// Other nodes which are not included in the subtree simply dropped
|
||||||
|
const newResult: Record<string, Record<string, string>> = {};
|
||||||
|
|
||||||
|
breadth({
|
||||||
|
tree: aquaDepPath,
|
||||||
|
getChildren: (node) => {
|
||||||
|
const deps = result[node];
|
||||||
|
|
||||||
|
if (deps === undefined) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRootNode = node === aquaDepPath;
|
||||||
|
newResult[isRootNode ? rootDepsKey : node] = deps;
|
||||||
|
return Object.values(deps);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
result = newResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { [rootDepsKey]: _, ...rest } = result;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
[aquaDepPath]: {
|
||||||
|
...result[rootDepsKey],
|
||||||
|
"": globalImports,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
12
packages/core/npm-aqua-compiler/test/transitive-deps/empty-project/package-lock.json
generated
Normal file
12
packages/core/npm-aqua-compiler/test/transitive-deps/empty-project/package-lock.json
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "empty-project",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "empty-project",
|
||||||
|
"version": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "empty-project",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user