Skip to content

Commit

Permalink
feat!: migrate to pure ESM (#3850)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: migrate to pure ESM

* feat: migrate to pure ESM

* chore: update snapshot

* fix: load `parserPreset` with another `await`

* test: migrate to vitest

* test: remove no replacement `--runInBand` test-ci script

* chore: fix code reviews

* refactor(load): rewrite resolve logic

* fix(config-nx-scopes): fix syntax error

* feat(resolve-extends): add resolveFrom and loadParserPreset

* feat(load): use resolveFrom and loadParserPreset from resolve-extends

* test: include only @commitlint/* packages src in coverage

* test: explicit import vitest utilities

* test: remove @jest/globals from dependencies

* fix(resolve-extends): `resolveFrom` output should be platform aware

* test: restore NO_COLOR to test script

* chore: fix linting issues

* fix: should use fileURLToPath instead of pathname for Windows compatibility

* Apply suggestions from code review

* fix: should reuse `cli` instead call `yargs()`

* feat(cli): set terminalWidth as wrap to avoid work break on help

* Update .eslintrc.cjs

* feat: migrate @commitlint/config-conventional to pure ESM

---------

Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
  • Loading branch information
2 people authored and escapedcat committed Feb 27, 2024
1 parent 93fa15e commit 3423735
Show file tree
Hide file tree
Showing 225 changed files with 3,258 additions and 3,352 deletions.
7 changes: 3 additions & 4 deletions .eslintrc.js → .eslintrc.cjs
Expand Up @@ -31,7 +31,7 @@ module.exports = {
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: ['**/*.test.js', '**/*.test.ts'],
devDependencies: ['**/*.test.js', '**/*.test.ts', 'vitest'],
},
],
},
Expand Down Expand Up @@ -61,15 +61,14 @@ module.exports = {
},
{
files: ['*.test.ts', '*.test.js'],
env: {
jest: true,
},
extends: ['plugin:jest/recommended'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
// disallow non-import statements appearing before import statements
'import/first': 'off',
'import/no-extraneous-dependencies': 'off',
'jest/no-deprecated-functions': 'off'
},
},
],
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/CI.yml
Expand Up @@ -29,13 +29,13 @@ jobs:
run: yarn build

- name: Test
run: yarn test-ci
run: yarn test

nodeJsBaselineAptCompatibility:
name: NodeJS installed from stock Ubuntu-LTS packages (not external sources)
runs-on: ubuntu-22.04
container:
image: "ubuntu:24.04"
image: 'ubuntu:24.04'
steps:
- uses: actions/checkout@v4

Expand All @@ -55,5 +55,4 @@ jobs:
run: yarn build

- name: Run Tests
run: yarn test-ci

run: yarn test
2 changes: 1 addition & 1 deletion @alias/commitlint-config-angular/index.js
@@ -1 +1 @@
module.exports = require('@commitlint/config-angular');
export {default} from '@commitlint/config-angular';
1 change: 1 addition & 0 deletions @alias/commitlint-config-angular/package.json
@@ -1,5 +1,6 @@
{
"name": "commitlint-config-angular",
"type": "module",
"version": "18.6.1",
"description": "Shareable commitlint config enforcing the angular commit convention",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion @alias/commitlint-config-lerna-scopes/index.js
@@ -1 +1 @@
module.exports = require('@commitlint/config-lerna-scopes');
export {default} from '@commitlint/config-lerna-scopes';
1 change: 1 addition & 0 deletions @alias/commitlint-config-lerna-scopes/package.json
@@ -1,5 +1,6 @@
{
"name": "commitlint-config-lerna-scopes",
"type": "module",
"version": "18.6.1",
"description": "Shareable commitlint config enforcing lerna package names as scopes",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion @alias/commitlint-config-nx-scopes/index.js
@@ -1 +1 @@
module.exports = require('@commitlint/config-nx-scopes');
export {default} from '@commitlint/config-nx-scopes';
1 change: 1 addition & 0 deletions @alias/commitlint-config-nx-scopes/package.json
@@ -1,5 +1,6 @@
{
"name": "commitlint-config-nx-scopes",
"type": "module",
"version": "18.6.1",
"description": "Shareable commitlint config enforcing nx project names as scopes",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion @alias/commitlint-config-patternplate/index.js
@@ -1 +1 @@
module.exports = require('@commitlint/config-patternplate');
export {default} from '@commitlint/config-patternplate';
1 change: 1 addition & 0 deletions @alias/commitlint-config-patternplate/package.json
@@ -1,5 +1,6 @@
{
"name": "commitlint-config-patternplate",
"type": "module",
"version": "18.6.1",
"description": "Lint your commits, patternplate-style",
"files": [
Expand Down
3 changes: 1 addition & 2 deletions @alias/commitlint/cli.js
@@ -1,3 +1,2 @@
#!/usr/bin/env node
const pkgDir = require('@commitlint/cli');
require(pkgDir);
import '@commitlint/cli/cli.js';
9 changes: 9 additions & 0 deletions @alias/commitlint/cli.test.js
@@ -1,6 +1,15 @@
import {test, expect} from 'vitest';
import {createRequire} from 'module';
import path from 'path';
import {fileURLToPath} from 'url';

import execa from 'execa';
import {fix} from '@commitlint/test';

const require = createRequire(import.meta.url);

const __dirname = path.resolve(fileURLToPath(import.meta.url), '..');

const bin = require.resolve('./cli.js');

function cli(args, options, input) {
Expand Down
1 change: 1 addition & 0 deletions @alias/commitlint/package.json
@@ -1,5 +1,6 @@
{
"name": "commitlint",
"type": "module",
"version": "18.6.1",
"description": "Lint your commit messages",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion @commitlint/cli/cli.js
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require('./lib/cli.js');
import './lib/cli.js';
3 changes: 3 additions & 0 deletions @commitlint/cli/fixtures/package.json
@@ -0,0 +1,3 @@
{
"type": "commonjs"
}
File renamed without changes.
6 changes: 3 additions & 3 deletions @commitlint/cli/package.json
@@ -1,12 +1,14 @@
{
"name": "@commitlint/cli",
"type": "module",
"version": "18.6.1",
"description": "Lint your commit messages",
"files": [
"index.js",
"index.cjs",
"cli.js",
"lib"
],
"main": "index.cjs",
"bin": {
"commitlint": "./cli.js"
},
Expand Down Expand Up @@ -39,7 +41,6 @@
"devDependencies": {
"@commitlint/test": "^18.0.0",
"@commitlint/utils": "^18.6.1",
"@types/lodash.isfunction": "^3.0.8",
"@types/lodash.merge": "^4.6.8",
"@types/node": "^18.11.9",
"@types/yargs": "^17.0.29",
Expand All @@ -53,7 +54,6 @@
"@commitlint/read": "^18.6.1",
"@commitlint/types": "^18.6.1",
"execa": "^5.0.0",
"lodash.isfunction": "^3.0.9",
"resolve-from": "5.0.0",
"resolve-global": "1.0.0",
"yargs": "^17.0.0"
Expand Down
58 changes: 28 additions & 30 deletions @commitlint/cli/src/cli.test.ts
@@ -1,8 +1,16 @@
import {describe, test, expect} from 'vitest';
import {createRequire} from 'module';
import path from 'path';
import {fileURLToPath} from 'url';

import {fix, git} from '@commitlint/test';
import execa from 'execa';
import fs from 'fs-extra';
import merge from 'lodash.merge';
import path from 'path';

const require = createRequire(import.meta.url);

const __dirname = path.resolve(fileURLToPath(import.meta.url), '..');

const bin = require.resolve('../cli.js');

Expand Down Expand Up @@ -511,34 +519,24 @@ test('should print help', async () => {
[input] reads from stdin if --edit, --env, --from and --to are omitted
Options:
-c, --color toggle colored output [boolean] [default: true]
-g, --config path to the config file [string]
--print-config print resolved config
[string] [choices: "", "text", "json"]
-d, --cwd directory to execute in
[string] [default: (Working Directory)]
-e, --edit read last commit message from the specified file or
fallbacks to ./.git/COMMIT_EDITMSG [string]
-E, --env check message in the file at path given by environment
variable value [string]
-x, --extends array of shareable configurations to extend [array]
-H, --help-url help url in error message [string]
-f, --from lower end of the commit range to lint; applies if
edit=false [string]
--git-log-args additional git log arguments as space separated string,
example '--first-parent --cherry-pick' [string]
-o, --format output format of the results [string]
-p, --parser-preset configuration preset to use for
conventional-commits-parser [string]
-q, --quiet toggle console output [boolean] [default: false]
-t, --to upper end of the commit range to lint; applies if
edit=false [string]
-V, --verbose enable verbose output for reports without problems
[boolean]
-s, --strict enable strict mode; result code 2 for warnings, 3 for
errors [boolean]
-v, --version display version information [boolean]
-h, --help Show help [boolean]"
-c, --color toggle colored output [boolean] [default: true]
-g, --config path to the config file [string]
--print-config print resolved config [string] [choices: "", "text", "json"]
-d, --cwd directory to execute in [string] [default: (Working Directory)]
-e, --edit read last commit message from the specified file or fallbacks to ./.git/COMMIT_EDITMSG [string]
-E, --env check message in the file at path given by environment variable value [string]
-x, --extends array of shareable configurations to extend [array]
-H, --help-url help url in error message [string]
-f, --from lower end of the commit range to lint; applies if edit=false [string]
--git-log-args additional git log arguments as space separated string, example '--first-parent --cherry-pick' [string]
-o, --format output format of the results [string]
-p, --parser-preset configuration preset to use for conventional-commits-parser [string]
-q, --quiet toggle console output [boolean] [default: false]
-t, --to upper end of the commit range to lint; applies if edit=false [string]
-V, --verbose enable verbose output for reports without problems [boolean]
-s, --strict enable strict mode; result code 2 for warnings, 3 for errors [boolean]
-v, --version display version information [boolean]
-h, --help Show help [boolean]"
`);
});

Expand Down Expand Up @@ -600,7 +598,7 @@ describe('should print config', () => {
const actual = await cli(['--print-config=json', '--no-color'], {cwd})();

expect(actual.stdout).toMatchInlineSnapshot(
`"{"extends":[],"formatter":"@commitlint/format","plugins":{},"rules":{"type-enum":[2,"never",["foo"]]},"helpUrl":"https://github.com/conventional-changelog/commitlint/#what-is-commitlint\","prompt":{}}"`
`"{"extends":[],"formatter":"@commitlint/format","plugins":{},"rules":{"type-enum":[2,"never",["foo"]]},"helpUrl":"https://github.com/conventional-changelog/commitlint/#what-is-commitlint","prompt":{}}"`
);
});
});
Expand Down
70 changes: 44 additions & 26 deletions @commitlint/cli/src/cli.ts
@@ -1,30 +1,45 @@
import execa, {ExecaError} from 'execa';
import load from '@commitlint/load';
import lint from '@commitlint/lint';
import read from '@commitlint/read';
import isFunction from 'lodash.isfunction';
import resolveFrom from 'resolve-from';
import resolveGlobal from 'resolve-global';
import yargs, {Arguments} from 'yargs';
import {createRequire} from 'module';
import path from 'path';
import {fileURLToPath, pathToFileURL} from 'url';
import util from 'util';

import {CliFlags} from './types';
import {
import lint from '@commitlint/lint';
import load from '@commitlint/load';
import read from '@commitlint/read';
import type {
Formatter,
LintOptions,
LintOutcome,
ParserOptions,
ParserPreset,
QualifiedConfig,
Formatter,
UserConfig,
} from '@commitlint/types';
import {CliError} from './cli-error';
import type {Options} from 'conventional-commits-parser';
import execa, {ExecaError} from 'execa';
import resolveFrom from 'resolve-from';
import resolveGlobal from 'resolve-global';
import yargs, {type Arguments} from 'yargs';

import {CliFlags} from './types.js';

import {CliError} from './cli-error.js';

const require = createRequire(import.meta.url);

const __dirname = path.resolve(fileURLToPath(import.meta.url), '..');

const dynamicImport = async <T>(id: string): Promise<T> => {
const imported = await import(
path.isAbsolute(id) ? pathToFileURL(id).toString() : id
);
return ('default' in imported && imported.default) || imported;
};

const pkg = require('../package');
const pkg: typeof import('../package.json') = require('../package.json');

const gitDefaultCommentChar = '#';

const cli = yargs
const cli = yargs(process.argv.slice(2))
.options({
color: {
alias: 'c',
Expand Down Expand Up @@ -131,6 +146,12 @@ const cli = yargs
)
.strict();

/**
* avoid description words to be divided in new lines when there is enough space
* @see https://github.com/conventional-changelog/commitlint/pull/3850#discussion_r1472251234
*/
cli.wrap(cli.terminalWidth());

main(cli.argv).catch((err) => {
setTimeout(() => {
if (err.type === pkg.name) {
Expand Down Expand Up @@ -215,7 +236,7 @@ async function main(args: MainArgs): Promise<void> {
'[input] is required: supply via stdin, or --env or --edit or --from and --to',
pkg.name
);
yargs.showHelp('log');
cli.showHelp('log');
console.log(err.message);
throw err;
}
Expand All @@ -225,7 +246,7 @@ async function main(args: MainArgs): Promise<void> {
file: flags.config,
});
const parserOpts = selectParserOpts(loaded.parserPreset);
const opts: LintOptions & {parserOpts: ParserOptions} = {
const opts: LintOptions & {parserOpts: Options} = {
parserOpts: {},
plugins: {},
ignores: [],
Expand All @@ -243,7 +264,7 @@ async function main(args: MainArgs): Promise<void> {
if (loaded.defaultIgnores === false) {
opts.defaultIgnores = false;
}
const format = loadFormatter(loaded, flags);
const format = await loadFormatter(loaded, flags);

// If reading from `.git/COMMIT_EDIT_MSG`, strip comments using
// core.commentChar from git configuration, falling back to '#'.
Expand Down Expand Up @@ -431,21 +452,18 @@ function selectParserOpts(parserPreset: ParserPreset | undefined) {
return parserPreset.parserOpts;
}

function loadFormatter(config: QualifiedConfig, flags: CliFlags): Formatter {
function loadFormatter(
config: QualifiedConfig,
flags: CliFlags
): Promise<Formatter> {
const moduleName = flags.format || config.formatter || '@commitlint/format';
const modulePath =
resolveFrom.silent(__dirname, moduleName) ||
resolveFrom.silent(flags.cwd, moduleName) ||
resolveGlobal.silent(moduleName);

if (modulePath) {
const moduleInstance = require(modulePath);

if (isFunction(moduleInstance.default)) {
return moduleInstance.default;
}

return moduleInstance;
return dynamicImport<Formatter>(modulePath);
}

throw new Error(`Using format ${moduleName}, but cannot find the module.`);
Expand Down
9 changes: 5 additions & 4 deletions @commitlint/config-angular-type-enum/index.js
Expand Up @@ -11,8 +11,9 @@ const types = [
'test',
];

module.exports.rules = {
'type-enum': [2, 'always', types],
export default {
rules: {
'type-enum': [2, 'always', types],
},
value: () => types,
};

module.exports.value = () => types;
1 change: 1 addition & 0 deletions @commitlint/config-angular-type-enum/package.json
@@ -1,5 +1,6 @@
{
"name": "@commitlint/config-angular-type-enum",
"type": "module",
"version": "18.6.1",
"description": "Shareable commitlint config enforcing the angular commit convention types",
"files": [
Expand Down
4 changes: 2 additions & 2 deletions @commitlint/config-angular/index.js
@@ -1,6 +1,6 @@
const typeEnum = require('@commitlint/config-angular-type-enum');
import typeEnum from '@commitlint/config-angular-type-enum';

module.exports = {
export default {
parserPreset: {parserOpts: {headerPattern: /^(\w*)(?:\((.*)\))?!?: (.*)$/}},
rules: {
'subject-exclamation-mark': [2, 'never'],
Expand Down

0 comments on commit 3423735

Please sign in to comment.