Skip to content

Commit

Permalink
Initial one-version rule (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
finn-orsini authored Apr 30, 2022
1 parent 5ac733d commit 3bc1d05
Show file tree
Hide file tree
Showing 26 changed files with 888 additions and 23 deletions.
24 changes: 12 additions & 12 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module.exports = {
"env": {
"commonjs": true,
"es2021": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": "latest"
},
"rules": {
}
}
env: {
commonjs: true,
es2021: true,
node: true,
jest: true,
},
extends: "eslint:recommended",
parserOptions: {
ecmaVersion: "latest",
},
rules: {},
};
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Description

<!-- Please provide a meaningful description of what this change will do, or is for. Bonus points for including links to related issues, other PRs, or technical references.-- >
<!-- Please provide a meaningful description of what this change will do, or is for. Bonus points for including links to related issues, other PRs, or technical references. -->

## Type of Change

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12.x
node-version: 14.x
cache: yarn
- name: Validate cache
run: yarn install --frozen-lockfile
Expand All @@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12.x
node-version: 14.x
cache: yarn
- name: install
run: yarn install --frozen-lockfile
Expand All @@ -47,7 +47,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12.x
node-version: 14.x
cache: yarn
- name: install
run: yarn install --frozen-lockfile
Expand All @@ -65,7 +65,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12.x
node-version: 14.x
cache: yarn
- name: install
run: yarn install --frozen-lockfile
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 12
node-version: 14
- run: yarn
- run: yarn test

Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ This changelog format is largely based on [Keep A Changelog](https://keepachange

#### 🔨 Chores & Documentation

## [0.1.0] - 2022-04-29

#### 🚀 New Features & Enhancements

- Initial package. Full yarn support.

## [0.0.0] - 2022-04-29

#### 🔨 Chores & Documentation

- Initial package & repo setup, empty release
- Initial repo setup, empty release
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports =
(resolve) =>
({ additionalIgnorePatterns = [] } = {}) => ({
testPathIgnorePatterns: ["/node_modules/", ...additionalIgnorePatterns],
testResultsProcessor: "jest-sonar-reporter",
resetModules: true,
});
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
{
"name": "@wayfair/one-version",
"version": "0.0.0",
"version": "0.1.0",
"description": "Opinionated Monorepo Dependency Management CLI",
"main": "index.js",
"bin": "src/index.js",
"main": "src/index.js",
"engines": {
"node": "^14"
},
"repository": "git@github.com:wayfair-incubator/one-version.git",
"author": "finn-orsini <orsini.seraphina@gmail.com>",
"license": "MIT",
"private": false,
"scripts": {
"test": "jest --passWithNoTests",
"test": "jest",
"lint": "eslint src/**/*.js",
"lint:md": "markdownlint **/*.md --ignore node_modules",
"format": "prettier --write src/**/*.js",
Expand All @@ -19,5 +23,9 @@
"jest": "^28.0.3",
"markdownlint-cli": "^0.31.1",
"prettier": "^2.6.2"
},
"dependencies": {
"chalk": "^4",
"commander": "^9.1"
}
}
14 changes: 14 additions & 0 deletions src/__fixtures__/mock-app-a/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "mock-app-a",
"version": "1.0.0",
"type": "module",
"devDependencies": {
"cypress": "^2"
},
"dependencies": {
"eslint": "7.0.1",
"jest": "^27.2",
"react": "^17",
"react-dom": "17"
}
}
14 changes: 14 additions & 0 deletions src/__fixtures__/mock-app-b/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "mock-app-b",
"version": "1.0.0",
"type": "module",
"devDependencies": {
"cypress": "^2"
},
"dependencies": {
"eslint": "7.0.1",
"jest": "^26.5",
"react": "^17",
"react-dom": "16"
}
}
14 changes: 14 additions & 0 deletions src/__fixtures__/mock-app-c/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "mock-app-c",
"version": "1.0.0",
"type": "module",
"devDependencies": {
"cypress": "^2"
},
"dependencies": {
"eslint": "7.0.1",
"jest": "^27.2",
"react": "^17",
"react-dom": "17"
}
}
12 changes: 12 additions & 0 deletions src/__fixtures__/mock-lib-a/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "mock-lib-a",
"version": "1.2.0",
"type": "module",
"devDependencies": {
"cypress": "^2"
},
"peerDependencies": {
"react": "^17",
"react-dom": "17"
}
}
10 changes: 10 additions & 0 deletions src/__fixtures__/oneversion.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"overrides": {
"jest": {
"^26.5": ["mock-app-b"]
},
"react-dom": {
"16": ["mock-app-b"]
}
}
}
49 changes: 49 additions & 0 deletions src/__tests__/check.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { check } = require("../check");
const {
NO_CHECK_API_ERROR,
FAILED_CHECK_ERROR,
} = require("../shared/constants");

const packageManager = "fake-package-manager";

const mockGetConfig = () => ({ overrides: {} });
const mockGetMissingPackageApi = () => {};

describe("one-version: check", () => {
it("throws if package manager specified does not have check api", () => {
expect(() => {
check({
packageManager,
getConfig: mockGetConfig,
getCheckApi: mockGetMissingPackageApi,
});
}).toThrow(`${NO_CHECK_API_ERROR} ${packageManager}`);
});

it("calls check api if found for package manager", () => {
const mockCheckApi = jest.fn();
mockCheckApi.mockReturnValue({ duplicateDependencies: [] });

check({
packageManager,
getConfig: mockGetConfig,
getCheckApi: () => mockCheckApi,
});

expect(mockCheckApi).toHaveBeenCalledWith({ overrides: {} });
});

it("throws if check api finds duplicate dependencies", () => {
const mockCheckApi = jest.fn();
mockCheckApi.mockReturnValue({ duplicateDependencies: ["foo"] });

expect(() => {
check({
packageManager,
getConfig: mockGetConfig,
getCheckApi: () => mockCheckApi,
prettify: () => {},
});
}).toThrow(FAILED_CHECK_ERROR);
});
});
61 changes: 61 additions & 0 deletions src/check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
One Version to rule them all, One Version to find them,
One Version to bring them all, and in the darkness bind them.
Enforcing only one version of any direct dependency is specified in the repo.
Note: Currently enforces the specifications match exactly, i.e. `^17` != `17`.
*/
const chalk = require('chalk');
const {parseConfig} = require('./shared/util');
const {format} = require('./format-output');
const {checkYarn} = require('./yarn/check');
const {checkPnpm} = require('./pnpm/check');
const {FAILED_CHECK_ERROR, NO_CHECK_API_ERROR} = require('./shared/constants');

const PACKAGE_MANGER_API = {
pnpm: checkPnpm,
yarn: checkYarn,
};

const getCheckPackageApi = (packageManager) => {
return PACKAGE_MANGER_API[packageManager];
};

const check = ({
packageManager,
getConfig = parseConfig,
getCheckApi = getCheckPackageApi,
prettify = format,
} = {}) => {
const checkApi = getCheckApi(packageManager);
if (checkApi) {
const {overrides} = getConfig();

const {duplicateDependencies} = checkApi({
overrides,
});

if (duplicateDependencies.length > 0) {
console.log(
chalk.dim('You shall not pass!\n'),
chalk.reset(
'🚫 One Version Rule Failure - found multiple versions of the following dependencies:\n'
),
prettify(duplicateDependencies)
);

throw new Error(FAILED_CHECK_ERROR);
}

console.log(
chalk.dim('My preciousss\n'),
chalk.reset('✨ One Version Rule Success - found no version conflicts!')
);
} else {
throw new Error(`${NO_CHECK_API_ERROR} ${packageManager}`);
}
};

module.exports = {
check,
};
54 changes: 54 additions & 0 deletions src/format-output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const chalk = require('chalk');

const SINGLE_INDENT = 2;
const DOUBLE_INDENT = SINGLE_INDENT * 2;

/**
Get a string in the format:
[version]:
[...dependencyTypeStrings]
that is
16
direct: mock-app-b
*/
const getVersionString = (version, dependencyTypeStrings) => {
return (
chalk.magentaBright(version.padStart(SINGLE_INDENT + version.length)) +
'\n' +
dependencyTypeStrings.join('\n')
);
};

/**
Get a string in the format:
[type]: [...names], that is:
direct: name1, name2
*/
const getTypeString = ({type, names}) => {
const padded = type.padStart(DOUBLE_INDENT + type.length);
return chalk.yellowBright(`${padded}: `) + chalk.white(names.join(', '));
};

const format = (packages) => {
return packages
.map(([name, versions]) => {
const str = chalk.cyanBright.underline(name);

const versionsStr = Object.entries(versions)
.map(([version, depTypes]) => {
const depTypeStrings = Object.entries(depTypes).map(([type, names]) =>
getTypeString({type, names})
);

return getVersionString(version, depTypeStrings);
})
.join('\n');

return `${str}\n${versionsStr}`;
})
.join('\n');
};

module.exports = {format};
Loading

0 comments on commit 3bc1d05

Please sign in to comment.