Skip to content

Commit df34d80

Browse files
committed
feat: conditionally support loading mjs config based on the node version
1 parent 145ca9d commit df34d80

File tree

20 files changed

+183
-77
lines changed

20 files changed

+183
-77
lines changed

.github/workflows/CI.yml

Lines changed: 12 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -8,77 +8,28 @@ on:
88
types: [opened, synchronize]
99

1010
jobs:
11-
v18:
12-
runs-on: ubuntu-22.04
13-
container:
14-
image: 'ubuntu:22.04'
11+
build:
12+
strategy:
13+
matrix:
14+
os: [ubuntu-22.04, windows-2022]
15+
node: [18, 20]
16+
runs-on: ${{ matrix.os }}
1517
steps:
16-
- name: Install required dependencies
17-
run: |
18-
apt update
19-
apt install --yes sudo
20-
sudo apt install --yes git
21-
sudo apt install --yes curl
22-
curl --location https://deb.nodesource.com/setup_18.x | sudo --preserve-env bash -
23-
sudo DEBIAN_FRONTEND=noninteractive apt install --yes nodejs
24-
- uses: actions/checkout@v4
25-
# workaround for https://github.com/actions/runner/issues/2033
26-
- name: ownership workaround
27-
run: git config --global --add safe.directory '*'
28-
- name: Install yarn
29-
run: |
30-
npm install --global yarn
31-
node --version
32-
yarn global add yarn@latest
33-
- name: Install dependencies
34-
run: yarn install --ignore-engines --frozen-lockfile
35-
- name: Build packages
36-
run: yarn build
37-
- name: Test
38-
run: yarn test-ci
18+
- uses: actions/setup-node@v3
19+
with:
20+
node-version: ${{ matrix.node }}
3921

40-
v20:
41-
runs-on: ubuntu-22.04
42-
container:
43-
image: 'ubuntu:22.04'
44-
steps:
45-
- name: Install required dependencies
46-
run: |
47-
apt update
48-
apt install --yes sudo
49-
sudo apt install --yes git
50-
sudo apt install --yes curl
51-
curl --location https://deb.nodesource.com/setup_20.x | sudo --preserve-env bash -
52-
sudo DEBIAN_FRONTEND=noninteractive apt install --yes nodejs
5322
- uses: actions/checkout@v4
23+
5424
# workaround for https://github.com/actions/runner/issues/2033
5525
- name: ownership workaround
5626
run: git config --global --add safe.directory '*'
57-
- name: Install yarn
58-
run: |
59-
npm install --global yarn
60-
node --version
61-
yarn global add yarn@latest
62-
- name: Install dependencies
63-
run: yarn install --ignore-engines --frozen-lockfile
64-
- name: Build packages
65-
run: yarn build
66-
- name: Test
67-
run: yarn test-ci
6827

69-
windows:
70-
runs-on: windows-2022
71-
steps:
72-
- uses: actions/checkout@v4
73-
with:
74-
max_attempts: 3
75-
- name: Update yarn
76-
run: |
77-
node --version
78-
yarn global add yarn@latest
7928
- name: Install dependencies
8029
run: yarn install --ignore-engines --frozen-lockfile
30+
8131
- name: Build packages
8232
run: yarn build
33+
8334
- name: Test
8435
run: yarn test-ci
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
extends:
2+
- './first-extended'
3+
rules:
4+
zero: [0, 'never']
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ['./first-extended'],
3+
rules: {
4+
zero: [0, 'never'],
5+
},
6+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ['./first-extended'],
3+
rules: {
4+
zero: [0, 'never'],
5+
},
6+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": ["./first-extended"],
3+
"rules": {
4+
"zero": [0, "never"]
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
extends: ['./first-extended'],
3+
rules: {
4+
zero: [0, 'never'],
5+
},
6+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
extends:
2+
- './first-extended'
3+
rules:
4+
zero: [0, 'never']
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
extends:
2+
- './first-extended'
3+
rules:
4+
zero: [0, 'never']
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ['./first-extended'],
3+
rules: {
4+
zero: [0, 'never'],
5+
},
6+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ['./first-extended'],
3+
rules: {
4+
zero: [0, 'never'],
5+
},
6+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
extends: ['./first-extended'],
3+
rules: {
4+
zero: [0, 'never'],
5+
},
6+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"commitlint": {
3+
"extends": [
4+
"./first-extended"
5+
],
6+
"rules": {
7+
"zero": [
8+
0,
9+
"never"
10+
]
11+
}
12+
}
13+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ['./second-extended'],
3+
rules: {
4+
one: [1, 'always'],
5+
},
6+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
rules: {
3+
two: [2, 'never'],
4+
},
5+
};

@commitlint/load/fixtures/recursive-extends-ts/commitlint.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type {UserConfig} from './types';
22

33
const Configuration: UserConfig = {
4-
extends: ['./first-extended'],
4+
extends: ['./first-extended/index.ts'],
55
rules: {
66
zero: [0, 'never', 'zero']
77
}

@commitlint/load/fixtures/recursive-extends-ts/first-extended/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {UserConfig} from '../types';
22
module.exports = {
3-
extends: ['./second-extended'],
3+
extends: ['./second-extended/index.ts'],
44
rules: {
55
one: [1, 'never', 'one']
66
}

@commitlint/load/src/load.test.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ jest.mock('@scope/commitlint-plugin-example', () => scopedPlugin, {
77
});
88

99
import path from 'path';
10+
import {readFileSync, writeFileSync} from 'fs';
1011
import resolveFrom from 'resolve-from';
1112
import {fix, git, npm} from '@commitlint/test';
1213

1314
import load from './load';
15+
import {isDynamicAwaitSupported} from './utils/load-config';
1416

1517
const fixBootstrap = (name: string) => fix.bootstrap(name, __dirname);
1618
const gitBootstrap = (name: string) => git.bootstrap(name, __dirname);
@@ -186,6 +188,44 @@ test('respects cwd option', async () => {
186188
});
187189
});
188190

191+
const mjsConfigFiles = isDynamicAwaitSupported()
192+
? ['commitlint.config.mjs', '.commitlintrc.mjs']
193+
: [];
194+
195+
test.each(
196+
[
197+
'commitlint.config.cjs',
198+
'commitlint.config.js',
199+
'package.json',
200+
'.commitlintrc.cjs',
201+
'.commitlintrc.js',
202+
'.commitlintrc.json',
203+
'.commitlintrc.yml',
204+
'.commitlintrc.yaml',
205+
'.commitlintrc',
206+
...mjsConfigFiles,
207+
].map((configFile) => [configFile])
208+
)('recursive extends with %s', async (configFile) => {
209+
const cwd = await gitBootstrap(`fixtures/recursive-extends-js-template`);
210+
const configPath = path.join(__dirname, `../fixtures/config/${configFile}`);
211+
const config = readFileSync(configPath);
212+
213+
writeFileSync(path.join(cwd, configFile), config);
214+
215+
const actual = await load({}, {cwd});
216+
217+
expect(actual).toMatchObject({
218+
formatter: '@commitlint/format',
219+
extends: ['./first-extended'],
220+
plugins: {},
221+
rules: {
222+
zero: [0, 'never'],
223+
one: [1, 'always'],
224+
two: [2, 'never'],
225+
},
226+
});
227+
});
228+
189229
test('recursive extends', async () => {
190230
const cwd = await gitBootstrap('fixtures/recursive-extends');
191231
const actual = await load({}, {cwd});
@@ -266,15 +306,13 @@ test('recursive extends with package.json file', async () => {
266306
});
267307
});
268308

269-
// fails since a jest update: https://github.com/conventional-changelog/commitlint/pull/3362
270-
// eslint-disable-next-line jest/no-disabled-tests
271-
test.skip('recursive extends with ts file', async () => {
309+
test('recursive extends with ts file', async () => {
272310
const cwd = await gitBootstrap('fixtures/recursive-extends-ts');
273311
const actual = await load({}, {cwd});
274312

275313
expect(actual).toMatchObject({
276314
formatter: '@commitlint/format',
277-
extends: ['./first-extended'],
315+
extends: ['./first-extended/index.ts'],
278316
plugins: {},
279317
rules: {
280318
zero: [0, 'never', 'zero'],

@commitlint/load/src/utils/load-config.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import {cosmiconfig, type Loader} from 'cosmiconfig';
1+
import {
2+
cosmiconfig,
3+
defaultLoadersSync,
4+
Options,
5+
type Loader,
6+
} from 'cosmiconfig';
27
import {TypeScriptLoader} from 'cosmiconfig-typescript-loader';
38
import path from 'path';
49

@@ -8,12 +13,12 @@ export interface LoadConfigResult {
813
isEmpty?: boolean;
914
}
1015

16+
const moduleName = 'commitlint';
17+
1118
export async function loadConfig(
1219
cwd: string,
1320
configPath?: string
1421
): Promise<LoadConfigResult | null> {
15-
const moduleName = 'commitlint';
16-
1722
let tsLoaderInstance: Loader | undefined;
1823
const tsLoader: Loader = (...args) => {
1924
if (!tsLoaderInstance) {
@@ -22,6 +27,8 @@ export async function loadConfig(
2227
return tsLoaderInstance(...args);
2328
};
2429

30+
const {searchPlaces, loaders} = getDynamicAwaitConfig();
31+
2532
const explorer = cosmiconfig(moduleName, {
2633
searchPlaces: [
2734
// cosmiconfig overrides default searchPlaces if any new search place is added (For e.g. `*.ts` files),
@@ -41,10 +48,14 @@ export async function loadConfig(
4148
`.${moduleName}rc.cts`,
4249
`${moduleName}.config.ts`,
4350
`${moduleName}.config.cts`,
51+
52+
...(searchPlaces || []),
4453
],
4554
loaders: {
4655
'.ts': tsLoader,
4756
'.cts': tsLoader,
57+
58+
...(loaders || {}),
4859
},
4960
});
5061

@@ -59,3 +70,31 @@ export async function loadConfig(
5970

6071
return null;
6172
}
73+
74+
// See the following issues for more context:
75+
// - Issue: https://github.com/nodejs/node/issues/40058
76+
// - Resolution: https://github.com/nodejs/node/pull/48510 (Node v20.8.0)
77+
export const isDynamicAwaitSupported = () => {
78+
const [major, minor] = process.version
79+
.replace('v', '')
80+
.split('.')
81+
.map((val) => parseInt(val));
82+
83+
return major >= 20 && minor >= 8;
84+
};
85+
86+
// If dynamic await is supported (Node >= v20.8.0), support mjs config.
87+
// Otherwise, don't support mjs and use synchronous js/cjs loaders.
88+
export const getDynamicAwaitConfig = (): Partial<Options> =>
89+
isDynamicAwaitSupported()
90+
? {
91+
searchPlaces: [`.${moduleName}rc.mjs`, `${moduleName}.config.mjs`],
92+
loaders: {},
93+
}
94+
: {
95+
searchPlaces: [],
96+
loaders: {
97+
'.cjs': defaultLoadersSync['.cjs'],
98+
'.js': defaultLoadersSync['.js'],
99+
},
100+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"publish": "lerna publish --conventional-commits",
2020
"reinstall": "yarn clean && yarn install",
2121
"start": "yarn watch",
22-
"test": "cross-env HOME=$PWD jest",
22+
"test": "cross-env HOME=$PWD NODE_OPTIONS=--experimental-vm-modules jest",
2323
"test-ci": "cross-env HOME=$PWD jest --runInBand",
2424
"postinstall": "yarn husky install"
2525
},

0 commit comments

Comments
 (0)