Skip to content

Commit 67ff9e8

Browse files
authored
feat(config-workspace-scopes): add config preset for npm and yarn workspaces (#4269)
* feat(config-workspace-scopes): add config preset for npm and yarn workspaces * fix(config-workspace-scopes): set npm constraint to 7+
1 parent b74b3ff commit 67ff9e8

File tree

17 files changed

+295
-1
lines changed

17 files changed

+295
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@packages/a",
3+
"version": "1.0.0"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@packages/b",
3+
"version": "1.0.0"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "yarn",
3+
"version": "1.0.0",
4+
"workspaces": [
5+
"@packages/*"
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "empty",
3+
"version": "1.0.0"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@packages/nested-a",
3+
"version": "1.0.0"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@packages/nested-b",
3+
"version": "1.0.0"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "nested-workspaces",
3+
"version": "1.0.0",
4+
"workspaces": [
5+
"@packages/**"
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@packages/a",
3+
"version": "1.0.0"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@packages/b",
3+
"version": "1.0.0"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"lerna": "4",
3+
"version": "1.0.0",
4+
"packages": ["@packages/*"]
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "scoped",
3+
"version": "1.0.0",
4+
"workspaces": [
5+
"@packages/**"
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import {createRequire} from 'module';
2+
import Path from 'path';
3+
4+
import {globSync} from 'glob';
5+
6+
const require = createRequire(import.meta.url);
7+
8+
export default {
9+
utils: {getPackages},
10+
rules: {
11+
'scope-enum': (ctx) =>
12+
getPackages(ctx).then((packages) => [2, 'always', packages]),
13+
},
14+
};
15+
16+
function getPackages(context) {
17+
return Promise.resolve()
18+
.then(() => {
19+
const ctx = context || {};
20+
const cwd = ctx.cwd || process.cwd();
21+
22+
const {workspaces} = require(Path.join(cwd, 'package.json'));
23+
if (!Array.isArray(workspaces)) {
24+
// no workspaces configured, skipping
25+
return [];
26+
}
27+
28+
const wsGlobs = workspaces.flatMap((ws) => {
29+
const path = Path.posix.join(ws, 'package.json');
30+
return globSync(path, {cwd, ignore: ['**/node_modules/**']});
31+
});
32+
33+
return wsGlobs.sort().map((pJson) => require(Path.join(cwd, pJson)));
34+
})
35+
.then((packages) => {
36+
return packages
37+
.map((pkg) => pkg.name)
38+
.filter(Boolean)
39+
.map((name) => (name.charAt(0) === '@' ? name.split('/')[1] : name));
40+
});
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {test, expect} from 'vitest';
2+
import path from 'path';
3+
import {fileURLToPath} from 'url';
4+
5+
import {npm} from '@commitlint/test';
6+
7+
import config from './index.js';
8+
9+
const __dirname = path.resolve(fileURLToPath(import.meta.url), '..');
10+
11+
test('exports rules key', () => {
12+
expect(config).toHaveProperty('rules');
13+
});
14+
15+
test('rules hold object', () => {
16+
expect(config).toMatchObject({
17+
rules: expect.any(Object),
18+
});
19+
});
20+
21+
test('rules contain scope-enum', () => {
22+
expect(config).toMatchObject({
23+
rules: {
24+
'scope-enum': expect.anything(),
25+
},
26+
});
27+
});
28+
29+
test('scope-enum is function', () => {
30+
expect(config).toMatchObject({
31+
rules: {
32+
'scope-enum': expect.any(Function),
33+
},
34+
});
35+
});
36+
37+
test('scope-enum does not throw for missing context', async () => {
38+
const {'scope-enum': fn} = config.rules;
39+
await expect(fn()).resolves.toBeTruthy();
40+
});
41+
42+
test('scope-enum has expected severity', async () => {
43+
const {'scope-enum': fn} = config.rules;
44+
const [severity] = await fn();
45+
expect(severity).toBe(2);
46+
});
47+
48+
test('scope-enum has expected modifier', async () => {
49+
const {'scope-enum': fn} = config.rules;
50+
const [, modifier] = await fn();
51+
expect(modifier).toBe('always');
52+
});
53+
54+
test('returns empty value for empty workspaces', async () => {
55+
const {'scope-enum': fn} = config.rules;
56+
const cwd = await npm.bootstrap('fixtures/empty', __dirname);
57+
const [, , value] = await fn({cwd});
58+
expect(value).toEqual([]);
59+
});
60+
61+
test('returns expected value for basic workspaces', async () => {
62+
const {'scope-enum': fn} = config.rules;
63+
const cwd = await npm.bootstrap('fixtures/basic', __dirname);
64+
65+
const [, , value] = await fn({cwd});
66+
expect(value).toEqual(['a', 'b']);
67+
});
68+
69+
test('returns expected value for scoped workspaces', async () => {
70+
const {'scope-enum': fn} = config.rules;
71+
const cwd = await npm.bootstrap('fixtures/scoped', __dirname);
72+
73+
const [, , value] = await fn({cwd});
74+
expect(value).toEqual(['a', 'b']);
75+
});
76+
77+
test('returns expected value for workspaces has nested packages', async () => {
78+
const {'scope-enum': fn} = config.rules;
79+
const cwd = await npm.bootstrap('fixtures/nested-workspaces', __dirname);
80+
81+
const [, , value] = await fn({cwd});
82+
expect(value).toEqual(expect.arrayContaining(['nested-a', 'nested-b']));
83+
expect(value).toEqual(
84+
expect.not.arrayContaining(['dependency-a', 'dependency-b'])
85+
);
86+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2024 - present Jan Biasi
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "@commitlint/config-workspace-scopes",
3+
"type": "module",
4+
"version": "19.7.0",
5+
"description": "Shareable commitlint config enforcing workspace names as scopes",
6+
"main": "index.js",
7+
"files": [
8+
"index.js"
9+
],
10+
"scripts": {
11+
"deps": "dep-check",
12+
"pkg": "pkg-check"
13+
},
14+
"repository": {
15+
"type": "git",
16+
"url": "https://github.com/conventional-changelog/commitlint.git",
17+
"directory": "@commitlint/config-lerna-scopes"
18+
},
19+
"keywords": [
20+
"conventional-changelog",
21+
"commitlint",
22+
"commitlint-config",
23+
"npm-workspaces",
24+
"yarn-workspaces"
25+
],
26+
"author": "Jan Biasi (https://github.com/janbiasi)",
27+
"license": "MIT",
28+
"bugs": {
29+
"url": "https://github.com/conventional-changelog/commitlint/issues"
30+
},
31+
"homepage": "https://commitlint.js.org/",
32+
"engines": {
33+
"node": ">=v18"
34+
},
35+
"dependencies": {
36+
"glob": "^10.3.10"
37+
},
38+
"devDependencies": {
39+
"@commitlint/test": "^19.5.0",
40+
"@commitlint/utils": "^19.5.0",
41+
"@types/glob": "^8.1.0"
42+
},
43+
"gitHead": "70f7f4688b51774e7ac5e40e896cdaa3f132b2bc"
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# @commitlint/config-workspaces-scopes
2+
3+
Shareable `commitlint` config enforcing workspace names as scopes.
4+
Use with [@commitlint/cli](../cli) and [@commitlint/prompt-cli](../prompt-cli).
5+
6+
## Getting started
7+
8+
```sh
9+
npm install --save-dev @commitlint/config-workspace-scopes @commitlint/cli
10+
echo "export default {extends: ['@commitlint/config-workspace-scopes']};" > commitlint.config.js
11+
```
12+
13+
## Examples
14+
15+
```text
16+
❯ cat package.json
17+
{
18+
"workspaces": ["packages/*"]
19+
}
20+
21+
❯ cat commitlint.config.js
22+
{
23+
extends: ['@commitlint/config-workspace-scopes']
24+
}
25+
26+
❯ tree packages
27+
28+
packages
29+
├── api
30+
├── app
31+
└── web
32+
33+
❯ echo "build(api): change something in api's build" | commitlint
34+
⧗ input: build(api): change something in api's build
35+
✔ found 0 problems, 0 warnings
36+
37+
❯ echo "test(foo): this won't pass" | commitlint
38+
⧗ input: test(foo): this won't pass
39+
✖ scope must be one of [api, app, web] [scope-enum]
40+
✖ found 1 problems, 0 warnings
41+
42+
❯ echo "ci: do some general maintenance" | commitlint
43+
⧗ input: ci: do some general maintenance
44+
✔ found 0 problems, 0 warnings
45+
```
46+
47+
Consult [Rules reference](https://commitlint.js.org/reference/rules) for a list of available rules.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
"@packages/*"
6363
],
6464
"engines": {
65-
"node": ">=v18"
65+
"node": ">=v18",
66+
"npm": ">=7"
6667
},
6768
"repository": {
6869
"type": "git",

0 commit comments

Comments
 (0)