mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2024-12-04 18:00:18 +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.
|
||||
*/
|
||||
|
||||
import { join } from "path";
|
||||
import { join, resolve } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
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", () => {
|
||||
/**
|
||||
@ -35,81 +90,90 @@ describe("imports", () => {
|
||||
string,
|
||||
Record<string, string[] | string>
|
||||
> = {
|
||||
[aquaToCompileDirPath]: {
|
||||
[resolve(aquaToCompileDirPath)]: {
|
||||
"": globalImports,
|
||||
A: "./A",
|
||||
B: "./B",
|
||||
A: "A",
|
||||
B: "B",
|
||||
},
|
||||
"./A": {
|
||||
C: "./C",
|
||||
D: "./D",
|
||||
A: {
|
||||
C: "C",
|
||||
D: "D",
|
||||
},
|
||||
"./B": {
|
||||
C: "./B/C",
|
||||
D: "./B/D",
|
||||
B: {
|
||||
C: "B/C",
|
||||
D: "B/D",
|
||||
},
|
||||
"./C": {
|
||||
D: "./C/D",
|
||||
C: {
|
||||
D: "C/D",
|
||||
},
|
||||
"./B/C": {
|
||||
D: "./B/C/D",
|
||||
"B/C": {
|
||||
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({
|
||||
npmProjectDirPath,
|
||||
aquaToCompileDirPath,
|
||||
globalImports,
|
||||
});
|
||||
|
||||
expect(Object.keys(imports).length).toBe(
|
||||
Object.keys(expectedResolution).length,
|
||||
);
|
||||
matchTree(expectedResolution, imports, aquaToCompileDirPath);
|
||||
});
|
||||
|
||||
Object.entries(imports).forEach(([key, value]) => {
|
||||
const resolutionKey =
|
||||
key === aquaToCompileDirPath ? key : buildResolutionKey(key);
|
||||
it("should resolve transitive dependencies and return a subtree when 'aquaToCompileDirPath' inside project 'node_modules' folder", async () => {
|
||||
const npmProjectDirPath = "./test/transitive-deps/project";
|
||||
|
||||
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(
|
||||
Object.keys(resolutionValues).length,
|
||||
);
|
||||
const expectedResolution: Record<
|
||||
string,
|
||||
Record<string, string[] | string>
|
||||
> = {
|
||||
[resolve(aquaToCompileDirPath)]: {
|
||||
"": globalImports,
|
||||
C: "C",
|
||||
D: "D",
|
||||
},
|
||||
C: {
|
||||
D: "C/D",
|
||||
},
|
||||
};
|
||||
|
||||
for (const [dep, path] of Object.entries(value)) {
|
||||
if (Array.isArray(path)) {
|
||||
expect(dep).toBe("");
|
||||
expect(expectedResolution[resolutionKey]).toHaveProperty(dep, path);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
expect(expectedResolution[resolutionKey]).toHaveProperty(
|
||||
dep,
|
||||
buildResolutionKey(path),
|
||||
);
|
||||
}
|
||||
const imports = await gatherImportsFromNpm({
|
||||
npmProjectDirPath,
|
||||
aquaToCompileDirPath,
|
||||
globalImports,
|
||||
});
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import { resolve } from "path";
|
||||
|
||||
import Arborist from "@npmcli/arborist";
|
||||
import { breadth } from "treeverse";
|
||||
|
||||
@ -40,7 +42,9 @@ export async function gatherImportsFromNpm({
|
||||
* Traverse dependency tree to construct map
|
||||
* (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({
|
||||
tree,
|
||||
@ -64,15 +68,8 @@ export async function gatherImportsFromNpm({
|
||||
|
||||
// Root node should have top-level property pointed to aqua dependency folder
|
||||
if (node.isRoot) {
|
||||
const aquaDepPath = aquaToCompileDirPath ?? npmProjectDirPath;
|
||||
|
||||
result[aquaDepPath] = {
|
||||
...(result[aquaDepPath] ??
|
||||
(globalImports.length > 0
|
||||
? {
|
||||
"": globalImports,
|
||||
}
|
||||
: {})),
|
||||
result[rootDepsKey] = {
|
||||
...result[rootDepsKey],
|
||||
[dep.name]: dep.realpath,
|
||||
};
|
||||
} 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…
Reference in New Issue
Block a user