Skip to content

Commit

Permalink
Merge pull request #9 from jalal246/dev
Browse files Browse the repository at this point in the history
Adding sorting map to know what's been changed
  • Loading branch information
jalal246 authored Mar 16, 2020
2 parents 26c0004 + 8353ec2 commit b6096c3
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 64 deletions.
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Package Sorter

> A function sorts a group of packages that depends on each other :nerd_face:
> Sorting a group of packages that depends on each other :nerd_face:
When you have projects depend on each other. You have to build core first, then
the project depends on it, and so on. You probably want this step to be automated
so you can use `package-sorter(unsortedPackages[], coreDependency)`.
Having multiple projects in workspace depending on each other is a headache. You
have to build core first, then the project depends on it, and so on. You
probably want this step to be automated so you can use: `package-sorter`

```bash
npm install package-sorter
Expand All @@ -18,16 +18,20 @@ npm install package-sorter
* @param {string} coreDependency - core package that other packages depends on it.
*
* @returns {Object} result
* @returns {Array} result.sorted
* @returns {Array} result.unSorted
* @returns {Array} result.sorted - all sorted packages
* @returns {{form: number, to: number}[]} result.sortingMap- map of indexes change due to sorting
* @returns {Array} result.unSorted - packages unsortable
*/
const { sorted, unSorted } = packageSorter(packages, coreDependency);
const { sorted, sortingMap, unSorted } = packageSorter(
packages,
coreDependency
);
```

If `coreDependency` is not passed, `package-sorter` will extract it following
monorepo naming pattern as: `@coreDep/`

> Why returns `unSorted`?
> `unSorted`
> Just in case, packages are missing the main dependency will be added to
> unSorted. Then you can figure out what's missing before production.
Expand Down Expand Up @@ -59,9 +63,10 @@ const pkg2 = {
const packages = [pkg2, pkg1, pkg0];

// our core dependency in this case is: @folo.
const { sorted, unSorted } = sortPackages(packages, "@folo");
const { sorted, sortingMap, unSorted } = sortPackages(packages, "@folo");

// sorted: [pkg0, pkg1, pkg2];
// sortingMap: [ { from: 2, to: 0 }, { from: 1, to: 1 }, { from: 0, to: 2 } ]
// unSorted: []
```

Expand Down Expand Up @@ -91,9 +96,10 @@ const pkg2 = {
const packages = [pkg2, pkg1, pkg0];

// let's the function get core dependency.
const { sorted } = sortPackages(packages);
const { sorted, sortingMap, unSorted } = sortPackages(packages);

// sorted: [pkg2, pkg0, pkg1]
// sortingMap: [ { from: 0, to: 0 }, { from: 2, to: 1 }, { from: 1, to: 2 } ]
// unSorted: []
```

Expand Down Expand Up @@ -124,9 +130,10 @@ const pkg2 = {

const packages = [pkg2, pkg1, pkg0];

const { sorted } = sortPackages(packages);
const { sorted, sortingMap, unSorted } = sortPackages(packages);

// sorted: [pkg0, pkg1]
// sortingMap: [ { from: 2, to: 0 }, { from: 1, to: 1 } ]
// unSorted: [pkg2]
```

Expand All @@ -139,7 +146,10 @@ const { sorted } = sortPackages(packages);

- [corename](https://github.com/jalal246/corename) - Extracts package name.

- [get-info](https://github.com/jalal246/get-info) - Utility functions for projects production.
- [get-info](https://github.com/jalal246/get-info) - Utility functions for
projects production.

- [textics](https://github.com/jalal246/textics) & [textics-stream](https://github.com/jalal246/textics-stream) - Counts lines, words, chars and spaces for a given string.

### Test

Expand Down
82 changes: 59 additions & 23 deletions src/packageSorter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
const getCoreName = require("corename");

let sorted;
let sortingMap;
let unSorted;
let coreDep;
let elemAdded;
let indexesAdded;

/**
* Checks if targeted dependency is already added to sorted array.
Expand Down Expand Up @@ -59,12 +61,15 @@ function isPackageNeedCoreDep(packageDeps) {
function addTo(packages, at, isSorted) {
const target = isSorted ? sorted : unSorted;

target.push(packages[at]);
const to = target.push(packages[at]) - 1;
indexesAdded[at] = true;

/**
* remove it from packages so it won't be checked next time.
*/
packages.splice(at, 1);
if (isSorted) {
sortingMap.push({
from: at,
to
});
}

elemAdded += 1;
}
Expand All @@ -81,24 +86,30 @@ function sort(packages) {
let hasCoreDep = false;
let dep = {};

let lastI = 0;

for (let i = 0; i < packages.length; i += 1) {
const pkg = packages[i];
if (!indexesAdded[i]) {
const pkg = packages[i];

const { dependencies } = pkg;
const { dependencies } = pkg;

({ hasCoreDep, dep } = isPackageNeedCoreDep(dependencies));
({ hasCoreDep, dep } = isPackageNeedCoreDep(dependencies));

/**
* When to add package to sorted?
* - Neutral. Doesn't have hasCoreDep, then add it to sorted.
* - Not natural, but its core dep is already added.
*/
isAddToSorted = !hasCoreDep || isDepInSorted(dep);
/**
* When to add package to sorted?
* - Neutral. Doesn't have hasCoreDep, then add it to sorted.
* - Not natural, but its core dep is already added.
*/
isAddToSorted = !hasCoreDep || isDepInSorted(dep);

if (isAddToSorted) {
addTo(packages, i, true);
if (isAddToSorted) {
addTo(packages, i, true);

break;
break;
}

lastI = i;
}
}

Expand All @@ -108,7 +119,7 @@ function sort(packages) {
* - remove it form packages.
*/
if (!isAddToSorted && hasCoreDep) {
addTo(packages, 0, false);
addTo(packages, lastI, false);
}
}

Expand All @@ -120,38 +131,63 @@ function sort(packages) {
* @param {string} coreDependency - core package that other packages depend on.
*
* @returns {Object} result
* @returns {Array} result.sorted
* @returns {Array} result.unSorted
* @returns {Array} result.sorted - all sorted packages
* @returns {{form: number, to: number}[]} result.sortingMap- indexes change due to sorting
* @returns {Array} result.unSorted - packages unsortable
*/
function packageSorter(packages = [], coreDependency) {
unSorted = [];
sortingMap = [];
indexesAdded = {};

/**
* Nothing to sort when:
* 1- have only one package.
* 2- can't discover the coreDep (which may be due to packages not depending
* on each other aka already sorted)
*/
if (packages.length <= 1) return { sorted: packages, unSorted };
if (packages.length <= 1)
return {
sorted: packages,
unSorted,
sortingMap
};

coreDep = coreDependency || getCoreName(packages);

if (!coreDep) return { sorted: packages, unSorted };
/**
* TODO: sortingMap should not be empty
*/
if (!coreDep)
return {
sorted: packages,
unSorted,
sortingMap
};

const totalLength = packages.length;
sorted = [];

elemAdded = 0;

let i = 0;

while (sorted.length < totalLength) {
sort(packages);
i += 1;

if (i === 10) break;

if (elemAdded === totalLength) {
break;
}
}

return { sorted, unSorted };
return {
sorted,
unSorted,
sortingMap
};
}

module.exports = packageSorter;
73 changes: 44 additions & 29 deletions test/packageSorter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe("sortPackages test", () => {
pkgFoloContext,
pkgFoloForms
];
const { sorted, unSorted } = sortPackages(packages, "@folo");
const { sorted, unSorted, sortingMap } = sortPackages(packages, "@folo");

const expectedResult = [
pkgFoloUtils,
Expand All @@ -53,7 +53,18 @@ describe("sortPackages test", () => {
];

expect(sorted).to.have.ordered.members(expectedResult);
expect(unSorted.length).to.be.equal(0);

// all sorted, so unSorted is empty
expect(unSorted).to.be.deep.equal([]);

// checking sorting map
expect(sortingMap).to.be.deep.equal([
{ from: 1, to: 0 },
{ from: 3, to: 1 },
{ from: 0, to: 2 },
{ from: 2, to: 3 },
{ from: 4, to: 4 }
]);
});

it("it extracts core dependency if not passed by default then sorts", () => {
Expand All @@ -67,7 +78,7 @@ describe("sortPackages test", () => {
pkgFoloContext,
pkgFoloForms
];
const { sorted, unSorted } = sortPackages(packages, "@folo");
const { sorted, sortingMap, unSorted } = sortPackages(packages, "@folo");

const expectedResult = [
pkgFoloUtils,
Expand All @@ -78,7 +89,14 @@ describe("sortPackages test", () => {
];

expect(sorted).to.have.ordered.members(expectedResult);
expect(unSorted.length).to.be.equal(0);
expect(unSorted).to.be.deep.equal([]);
expect(sortingMap).to.be.deep.equal([
{ from: 1, to: 0 },
{ from: 3, to: 1 },
{ from: 0, to: 2 },
{ from: 2, to: 3 },
{ from: 4, to: 4 }
]);
});

it("sorts all mixed-package some sortable, others don't have related core dep", () => {
Expand Down Expand Up @@ -115,7 +133,7 @@ describe("sortPackages test", () => {
pkgUN3
];

const { sorted, unSorted } = sortPackages(packages);
const { sorted, sortingMap, unSorted } = sortPackages(packages);

const expectedResult = [
pkgFoloUtils,
Expand All @@ -127,26 +145,17 @@ describe("sortPackages test", () => {
];

expect(sorted).to.have.ordered.members(expectedResult);
expect(unSorted.length).to.be.equal(0);
expect(unSorted).to.be.deep.equal([]);
expect(sortingMap).to.be.deep.equal([
{ from: 0, to: 0 },
{ from: 1, to: 1 },
{ from: 3, to: 2 },
{ from: 4, to: 3 },
{ from: 2, to: 4 },
{ from: 5, to: 5 }
]);
});

// it("it sorts packages and associated arrays", () => {
// /**
// * Same as above but without passing core dep.
// */
// const packages = [pkgFoloValues, pkgFoloContext, pkgFoloForms];

// const distPath = ["1", "0", "4"];
// const result = sortPackages(packages, null, [distPath]);
// // console.log("result", result);

// const expectedResult = [pkgFoloContext, pkgFoloValues, pkgFoloForms];
// // console.log("distPath", distPath);

// expect(result).to.have.ordered.members(expectedResult);
// // expect(distPath).to.have.ordered.members(["0", "1", "4"]);
// });

it("returns all unsorted packages that have core dep", () => {
const pkg10 = {
name: "@folo/withcontext",
Expand All @@ -170,10 +179,11 @@ describe("sortPackages test", () => {
};

const packages = [pkg10, pkg11, pkg12];
const { sorted, unSorted } = sortPackages(packages, "@folo");
const { sorted, sortingMap, unSorted } = sortPackages(packages, "@folo");

expect(sorted.length).to.be.equal(0);
expect(unSorted).to.have.ordered.members([pkg10, pkg11, pkg12]);
expect(sorted).to.be.deep.equal([]);
expect(unSorted).to.have.ordered.members([pkg12, pkg11, pkg10]);
expect(sortingMap).to.be.deep.equal([]);
});

it("returns only packages that able to sort them, ignore the other", () => {
Expand All @@ -197,9 +207,13 @@ describe("sortPackages test", () => {
};

const packages = [pkg20, pkg21, pkg22];
const { sorted, unSorted } = sortPackages(packages, "@folo");
const { sorted, sortingMap, unSorted } = sortPackages(packages, "@folo");

expect(sorted).to.have.ordered.members([pkg22, pkg21]);
expect(sortingMap).to.be.deep.equal([
{ from: 2, to: 0 },
{ from: 1, to: 1 }
]);
expect(unSorted).to.have.ordered.members([pkg20]);
});

Expand Down Expand Up @@ -229,9 +243,10 @@ describe("sortPackages test", () => {
};
const packages = [pkgUN1, pkgUN2, pkgUN3];

const { sorted, unSorted } = sortPackages(packages);
const { sorted, sortingMap, unSorted } = sortPackages(packages);

expect(sorted).to.have.ordered.members(packages);
expect(unSorted.length).to.be.equal(0);
expect(sortingMap).to.be.deep.equal([]);
expect(unSorted).to.be.deep.equal([]);
});
});

0 comments on commit b6096c3

Please sign in to comment.