Skip to content

Commit

Permalink
feat(monorepo): update dependencies (#937)
Browse files Browse the repository at this point in the history
* feat(monorepo): update dependencies

* chore: remove unnecessary config

* docs: update the doc

Co-authored-by: shipjs <shipjs@test.com>
  • Loading branch information
Eunjae Lee and shipjs committed Nov 6, 2020
1 parent 74531a3 commit 03d47db
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 13 deletions.
104 changes: 104 additions & 0 deletions packages/shipjs/src/helper/__tests__/dependencyUpdater.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { getListToUpdate, printListToUpdate } from '../dependencyUpdater';
import { print } from '../../util';
import { mockPrint } from '../../../tests/util';

describe('getListToUpdate', () => {
const list = [
{
packagePath: 'packages/package-core',
json: {
name: 'core',
},
},
{
packagePath: 'packages/package-js',
json: {
name: 'js',
dependencies: {
core: '^0.3.1',
},
},
},
{
packagePath: 'packages/package-plugin-abc',
json: {
name: 'plugin-abc',
dependencies: {
core: '0.3.1',
},
peerDependencies: {
js: '~0.3.1',
},
devDependencies: {
js: '^0.3.1',
},
},
},
];

it('gets correct list', () => {
const actual = getListToUpdate('0.3.2', list);
expect(actual).toEqual([
{
name: 'js',
packagePath: 'packages/package-js',
updates: {
dependencies: [
{
currentVersion: '^0.3.1',
dependency: 'core',
nextVersion: '^0.3.2',
},
],
},
},
{
name: 'plugin-abc',
packagePath: 'packages/package-plugin-abc',
updates: {
dependencies: [
{
currentVersion: '0.3.1',
dependency: 'core',
nextVersion: '0.3.2',
},
],
devDependencies: [
{
currentVersion: '^0.3.1',
dependency: 'js',
nextVersion: '^0.3.2',
},
],
peerDependencies: [
{
currentVersion: '~0.3.1',
dependency: 'js',
nextVersion: '~0.3.2',
},
],
},
},
]);
});

it('prints the correct update list', () => {
const output = [];
mockPrint(print, output);
printListToUpdate(getListToUpdate('0.3.2', list));
expect(output).toMatchInlineSnapshot(`
Array [
" package: js (packages/package-js}/package.json)",
" dependencies:",
" core: ^0.3.1 -> ^0.3.2",
" package: plugin-abc (packages/package-plugin-abc}/package.json)",
" dependencies:",
" core: 0.3.1 -> 0.3.2",
" devDependencies:",
" js: ^0.3.1 -> ^0.3.2",
" peerDependencies:",
" js: ~0.3.1 -> ~0.3.2",
]
`);
});
});
119 changes: 119 additions & 0 deletions packages/shipjs/src/helper/dependencyUpdater.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { resolve } from 'path';
import { readFileSync, writeFileSync } from 'fs';
import { print } from '../util';
import { runPrettier } from '../helper';

const typesOfDependencies = [
'dependencies',
'devDependencies',
'peerDependencies',
];

export function getListToUpdate(_nextVersion, list) {
const packageNames = list.map(({ json }) => json.name);

return list
.map(({ packagePath, json }) => {
const updates = typesOfDependencies
.map((dependencyType) => {
let dependenciesToUpdate = Object.keys(
json[dependencyType] || {}
).filter((dependency) => packageNames.includes(dependency));

dependenciesToUpdate = dependenciesToUpdate
.map((dependencyToUpdate) => {
const currentVersion = json[dependencyType][dependencyToUpdate];
const prefix =
currentVersion.startsWith('~') || currentVersion.startsWith('^')
? currentVersion[0]
: '';
const nextVersion = `${prefix}${_nextVersion}`;
if (currentVersion !== nextVersion) {
return {
dependency: dependencyToUpdate,
currentVersion,
nextVersion,
};
} else {
return null;
}
})
.filter(Boolean);

if (dependenciesToUpdate.length === 0) {
return null;
} else {
return {
type: dependencyType,
dependenciesToUpdate,
};
}
})
.filter(Boolean);

if (updates.length === 0) {
return null;
} else {
return {
packagePath,
name: json.name,
updates: updates.reduce((acc, { type, dependenciesToUpdate }) => {
// eslint-disable-next-line no-param-reassign
acc[type] = dependenciesToUpdate;
return acc;
}, {}),
};
}
})
.filter(Boolean);
}

export function printListToUpdate(list) {
list.forEach(({ name, packagePath, updates }) => {
print(` package: ${name} (${packagePath}}/package.json)`);
Object.keys(updates).forEach((dependencyType) => {
print(` ${dependencyType}:`);
updates[dependencyType].forEach(
({ dependency, currentVersion, nextVersion }) => {
print(` ${dependency}: ${currentVersion} -> ${nextVersion}`);
}
);
});
});
}

export async function runUpdates(list) {
list.forEach(({ name, packagePath, updates }) => {
print(` package: ${name} (${packagePath}}/package.json)`);
const filePath = resolve(packagePath, 'package.json');
const json = JSON.parse(readFileSync(filePath).toString());
Object.keys(updates).forEach((dependencyType) => {
print(` ${dependencyType}:`);
updates[dependencyType].forEach(
({ dependency, currentVersion, nextVersion }) => {
print(` ${dependency}: ${currentVersion} -> ${nextVersion}`);
json[dependencyType][dependency] = nextVersion;
}
);
});
writeFileSync(filePath, JSON.stringify(json, null, 2));
});

await Promise.all(
list.map(({ packagePath }) =>
runPrettier({
filePath: resolve(packagePath, 'package.json'),
dir: packagePath,
})
)
);
}

export function prepareJsons(packageList) {
return packageList.map((packagePath) => ({
packagePath,
json: JSON.parse(
readFileSync(resolve(packagePath, 'package.json')).toString()
),
}));
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import { expandPackageList, updateVersion } from 'shipjs-lib';
import { print } from '../../../util';
import { prepareJsons } from '../../../helper/dependencyUpdater';

jest.mock('../../../helper/dependencyUpdater', () => {
return {
...jest.requireActual('../../../helper/dependencyUpdater'),
prepareJsons: jest.fn(() => []),
};
});

import updateVersionMonorepo from '../updateVersionMonorepo';
import { mockPrint } from '../../../../tests/util';

jest.mock('../updateVersionMonorepo', () => {
const newModule = jest.requireActual('../updateVersionMonorepo');
newModule.prepareJsons = jest.fn(() => []);
return newModule;
});

describe('updateVersionMonorepo', () => {
it('works', () => {
expandPackageList.mockImplementationOnce(() => [
Expand Down Expand Up @@ -66,6 +81,7 @@ describe('updateVersionMonorepo', () => {
]);
const output = [];
mockPrint(print, output);
prepareJsons.mockImplementation(() => []);
updateVersionMonorepo({
config: {
versionUpdated: () => {},
Expand All @@ -84,6 +100,7 @@ describe('updateVersionMonorepo', () => {
"Actual packages to bump:",
"-> packages/a/package.json",
"-> packages/b/package.json",
"Updating dependencies:",
"-> execute versionUpdated() callback.",
]
`);
Expand Down
29 changes: 28 additions & 1 deletion packages/shipjs/src/step/prepare/updateVersionMonorepo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,44 @@ import { expandPackageList, updateVersion } from 'shipjs-lib';
import runStep from '../runStep';
import { wrapExecWithDir, print } from '../../util';
import { info } from '../../color';
import {
printListToUpdate,
getListToUpdate,
prepareJsons,
runUpdates,
} from '../../helper/dependencyUpdater';

export default async ({ config, nextVersion, releaseType, dir, dryRun }) =>
await runStep(
{ title: 'Updating the versions on the monorepo.' },
async () => {
const {
versionUpdated,
monorepo: { mainVersionFile, packagesToBump },
monorepo: {
mainVersionFile,
packagesToBump,
updateDependencies = true,
},
} = config;
const packageList = expandPackageList(packagesToBump, dir);

if (dryRun) {
print(`Your configuration: ${JSON.stringify(packagesToBump)}`);
print(`Main version file: ${mainVersionFile}`);
print(`Actual packages to bump:`);
packageList.forEach((packageDir) =>
print(`-> ${info(`${packageDir}/package.json`)}`)
);

if (updateDependencies) {
print(`Updating dependencies:`);
printListToUpdate(
getListToUpdate(nextVersion, prepareJsons(packageList))
);
} else {
print(`Not updating dependencies.`);
}

if (versionUpdated) {
print(`-> execute ${info('versionUpdated()')} callback.`);
}
Expand All @@ -30,6 +51,12 @@ export default async ({ config, nextVersion, releaseType, dir, dryRun }) =>
print(`-> ${info(`${packageDir}/package.json`)}`);
updateVersion({ nextVersion, dir: packageDir });
});

if (updateDependencies) {
print(`Updating dependencies:`);
runUpdates(getListToUpdate(nextVersion, prepareJsons(packageList)));
}

if (versionUpdated) {
await versionUpdated({
version: nextVersion,
Expand Down
10 changes: 0 additions & 10 deletions ship.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,6 @@ module.exports = {
json.version = version;
});

// update package.json
updateJson(dir, "package.json", (json) => {
json.version = version;
});

// update dependency
updateJson(dir, "packages/shipjs/package.json", (json) => {
json.dependencies["shipjs-lib"] = version;
});

// update `version.js`
fs.writeFileSync(
path.resolve(dir, "packages/shipjs/src/version.js"),
Expand Down
25 changes: 23 additions & 2 deletions website/reference/all-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
mainVersionFile: 'package.json',
packagesToBump: ['packages/*', 'examples/*'],
packagesToPublish: ['packages/*'],
updateDependencies: true // optional, default: true
},
};
```
Expand All @@ -21,13 +22,33 @@ If `monorepo` is defined, Ship.js will treat the project as a monorepo.
Ship.js currently does not provide independent versioning. It means all the packages in the monorepo must have the same version.
:::

- **`shipjs prepare`**
### **`shipjs prepare`**

1. Ship.js reads version from `mainVersionFile`.
2. When next version is decided, Ship.js will update the version at `mainVersionFile`.
3. Ship.js will update all the versions in `packagesToBump`.
4. When `updateDependencies: true`, it updates the dependencies, too.

- **`shipjs trigger`**
For example,

```js
// ship.config.js
packagesToBump: ['packages/my-package-core', 'packages/my-package-js']
```

```js
// packages/my-package-js/package.json
{
...
"dependencies": {
"my-package-core": "^x.y.z"
}
}
```

Ship.js will check `dependencies`, `devDependencies` and `peerDependencies` and update the version to the latest. If you don't want this behavior, put `updateDependencies: false` in the config.

### **`shipjs trigger`**

1. Ship.js will only publish the packages from `packagesToPublish`.

Expand Down

0 comments on commit 03d47db

Please sign in to comment.