Skip to content
This repository was archived by the owner on Aug 18, 2024. It is now read-only.

Commit 53178f7

Browse files
authored
Merge pull request #20 from dominykas/cycles
Support cycles
2 parents 9328bb9 + 3883c3c commit 53178f7

File tree

10 files changed

+44
-36
lines changed

10 files changed

+44
-36
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ node_js:
55
- "10"
66
- "node"
77

8+
install:
9+
- "npm install --ignore-scripts"
10+
- "node bin/allow-scripts.js"
11+
812
jobs:
913
include:
1014
- stage: release

lib/index.js

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,31 @@ const Fs = require('fs');
66
const Npm = require('libnpm');
77
const Path = require('path');
88
const Semver = require('semver');
9-
const Topo = require('topo');
109

1110

1211
const internals = {};
1312

1413

15-
internals.scan = (tree, parent, map = new Map(), scanned = new Set()) => {
14+
internals.queue = (tree, queue = [], scanned = new Set()) => {
1615

1716
for (const [, v] of tree.dependencies) {
1817

19-
if (v.hasCycle()) {
20-
console.warn(`==========> skip ${v.path()} (because it has a cycle in dependencies)`);
21-
continue;
22-
}
23-
2418
const path = v.path();
25-
if (!map.has(path)) {
26-
map.set(path, []);
27-
}
28-
29-
const node = map.get(path);
30-
node.push(parent);
3119

32-
if (!scanned.has(v)) {
33-
scanned.add(v);
34-
internals.scan(v, v.path(), map, scanned);
20+
if (scanned.has(path)) {
21+
// prevent cycles
22+
continue;
3523
}
36-
}
37-
38-
return map;
39-
};
4024

41-
42-
internals.queue = (tree) => {
43-
44-
const map = internals.scan(tree);
45-
const topo = new Topo();
46-
for (const [group, before] of map) {
47-
topo.add(group, { group, before });
25+
scanned.add(path);
26+
internals.queue(v, queue, scanned);
27+
queue.push(v.path()); // adding deepest deps into the queue first
4828
}
4929

50-
return topo.nodes;
30+
return queue;
5131
};
5232

33+
5334
internals.runScript = (stage, { pkg, path, cwd, unsafePerm }, options) => {
5435

5536
if (!pkg.scripts || !pkg.scripts[stage]) {

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
"dependencies": {
1919
"ansicolors": "0.3.x",
2020
"libnpm": "2.x.x",
21-
"semver": "6.x.x",
22-
"topo": "3.x.x"
21+
"semver": "6.x.x"
2322
},
2423
"devDependencies": {
2524
"@commitlint/cli": "7.x.x",

test/fixtures/cycle-a.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
"version": "0.0.0",
55
"dependencies": {
66
"@example/cycle-b": "*"
7+
},
8+
"scripts": {
9+
"install": "echo install from cycle-a >> ${OUTPUT}"
710
}
811
}

test/fixtures/cycle-b.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"name": "@example/cycle-b",
44
"version": "0.0.0",
55
"dependencies": {
6-
"@example/cycle-a": "*"
6+
"@example/cycle-c": "*"
7+
},
8+
"scripts": {
9+
"install": "echo install from cycle-b >> ${OUTPUT}"
710
}
811
}

test/fixtures/cycle-c.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"private": true,
3+
"name": "@example/cycle-c",
4+
"version": "0.0.0",
5+
"dependencies": {
6+
"@example/cycle-a": "*"
7+
},
8+
"scripts": {
9+
"install": "echo install from cycle-c >> ${OUTPUT}"
10+
}
11+
}

test/fixtures/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ internals.readFile = (path) => Fs.readFileSync(path).toString().trim();
1919
exports.expectedResults = {
2020
basicFull: internals.readFile(Path.join(__dirname, 'basic.full.txt')),
2121
basicDryRun: internals.readFile(Path.join(__dirname, 'basic.dry-run.txt')),
22-
deep: internals.readFile(Path.join(__dirname, 'deep.txt'))
22+
deep: internals.readFile(Path.join(__dirname, 'deep.txt')),
23+
withCycles: internals.readFile(Path.join(__dirname, 'with-cycles.txt'))
2324
};
2425

2526

test/fixtures/with-cycles.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
"@example/with-install-script": "*"
99
},
1010
"allowScripts": {
11-
"@example/with-install-script": "*"
11+
"@example/with-install-script": "*",
12+
"@example/cycle-a": "*",
13+
"@example/cycle-b": "*",
14+
"@example/cycle-c": "*"
1215
}
1316
}

test/fixtures/with-cycles.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
install from cycle-c
2+
install from cycle-b
3+
install from cycle-a
4+
install from with-install-script

test/index.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,13 @@ describe('allow-scripts', () => {
8888
const fixture = Fixtures.setup('with-cycles', [
8989
'cycle-a',
9090
'cycle-b',
91+
'cycle-c',
9192
'with-install-script'
9293
]);
9394

9495
await Allow.run({});
9596

96-
expect(fixture.getActualResult()).to.equal('install from with-install-script');
97-
expect(fixture.getLog()).to.contain('skip node_modules/@example/cycle-a (because it has a cycle in dependencies)');
98-
expect(fixture.getLog()).to.contain('skip node_modules/@example/cycle-b (because it has a cycle in dependencies)');
97+
expect(fixture.getActualResult()).to.equal(Fixtures.expectedResults.withCycles);
9998
});
10099

101100
it('executes allowed scripts (existing shrinkwrap)', async () => {

0 commit comments

Comments
 (0)