Skip to content

Commit 53a621f

Browse files
committed
Update eslint to make use of the new concurrent option
1 parent 6d794a7 commit 53a621f

File tree

11 files changed

+136
-50
lines changed

11 files changed

+136
-50
lines changed

devserver/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"@types/react": "^18.3.1",
3131
"@types/react-dom": "^18.3.1",
3232
"@vitest/browser": "^3.2.3",
33-
"eslint": "^9.31.0",
33+
"eslint": "^9.35.0",
3434
"playwright": "^1.54.1",
3535
"sass": "^1.85.0",
3636
"typescript": "^5.8.2",

docs/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
"vitepress-sidebar": "^1.31.1"
2222
},
2323
"devDependencies": {
24-
"@sourceacademy/markdown-plugin-directory-tree": "workspace:^",
25-
"eslint": "^9.31.0"
24+
"@sourceacademy/markdown-plugin-directory-tree": "workspace:^"
2625
}
2726
}

lib/buildtools/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"chalk": "^5.0.1",
2727
"commander": "^13.0.0",
2828
"esbuild": "^0.25.8",
29-
"eslint": "^9.31.0",
29+
"eslint": "^9.35.0",
3030
"http-server": "^14.1.1",
3131
"jsdom": "^26.1.0",
3232
"lodash": "^4.17.21",

lib/buildtools/src/commands/__tests__/lint.test.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { Command } from '@commander-js/extra-typings';
12
import * as manifest from '@sourceacademy/modules-repotools/manifest';
23
import { ESLint } from 'eslint';
34
import { beforeEach, describe, expect, test, vi } from 'vitest';
4-
import { getLintCommand } from '../prebuild.js';
5+
import { concurrencyOption, getLintCommand } from '../prebuild.js';
56
import { getCommandRunner } from './testingUtils.js';
67

78
const lintFilesMock = vi.hoisted(() => vi.fn());
@@ -154,3 +155,34 @@ describe('Test Lint command directory resolution', () => {
154155
expect(lintFilesMock).not.toHaveBeenCalled();
155156
});
156157
});
158+
159+
describe('Test concurrency option', () => {
160+
const runOption = (...args: string[]) => new Promise<ESLint.Options['concurrency']>((resolve, reject) => {
161+
new Command()
162+
.addOption(concurrencyOption)
163+
.exitOverride(reject)
164+
.configureOutput({
165+
writeErr: () => {}
166+
})
167+
.action(({ concurrency }) => resolve(concurrency))
168+
.parse(args, { from: 'user' });
169+
});
170+
171+
test('number for option uses that number', () => {
172+
return expect(runOption('--concurrency', '3')).resolves.toEqual(3);
173+
});
174+
175+
test('off returns \'off\'', () => {
176+
return expect(runOption('--concurrency', 'off')).resolves.toEqual('off');
177+
});
178+
179+
test('auto returns \'auto\'', () => {
180+
return expect(runOption('--concurrency', 'auto')).resolves.toEqual('auto');
181+
});
182+
183+
test('unknown option throws error', () => {
184+
return expect(runOption('--concurrency', 'unknown'))
185+
.rejects
186+
.toThrowError('Expected auto, off or a number, got unknown');
187+
});
188+
});

lib/buildtools/src/commands/__tests__/testing.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ describe('Test silent option', () => {
140140
const command = new Command()
141141
.exitOverride()
142142
.addOption(silentOption)
143+
.configureOutput({ writeErr: () => {} })
143144
.action(option => {
144145
resolve(option.silent);
145146
});

lib/buildtools/src/commands/prebuild.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
11
import pathlib from 'path';
2-
import { Command } from '@commander-js/extra-typings';
2+
import { Command, InvalidArgumentError, Option } from '@commander-js/extra-typings';
33
import { outDir } from '@sourceacademy/modules-repotools/getGitRoot';
44
import { resolveEitherBundleOrTab } from '@sourceacademy/modules-repotools/manifest';
55
import { divideAndRound } from '@sourceacademy/modules-repotools/utils';
66
import chalk from 'chalk';
7+
import type { ESLint } from 'eslint';
78
import { runPrebuild } from '../prebuild/index.js';
89
import { formatLintResult, lintGlobal, runEslint } from '../prebuild/lint.js';
910
import { formatTscResult, runTsc } from '../prebuild/tsc.js';
1011
import { logCommandErrorAndExit } from './commandUtils.js';
1112

13+
export const concurrencyOption = new Option('--concurrency <value>')
14+
.argParser((value): Exclude<ESLint.Options['concurrency'], undefined> => {
15+
switch (value) {
16+
case 'auto':
17+
case 'off':
18+
return value;
19+
default: {
20+
const count = parseInt(value);
21+
22+
if (Number.isNaN(count)) {
23+
throw new InvalidArgumentError(`Expected auto, off or a number, got ${value}`);
24+
}
25+
26+
return count;
27+
}
28+
}
29+
})
30+
.default('auto');
31+
1232
/*
1333
* The lint command is provided as part of buildtools so that each bundle and tab doesn't
1434
* have to install its own copy of ESLint
@@ -20,7 +40,8 @@ export const getLintCommand = () => new Command('lint')
2040
.option('--fix', 'Output linting fixes', false)
2141
.option('--stats', 'Output linting stats', false)
2242
.option('--ci', process.env.CI)
23-
.action(async (directory, { fix, ci, stats }) => {
43+
.addOption(concurrencyOption)
44+
.action(async (directory, { ci, ...opts }) => {
2445
const fullyResolved = pathlib.resolve(directory);
2546
const resolveResult = await resolveEitherBundleOrTab(fullyResolved);
2647

@@ -32,7 +53,7 @@ export const getLintCommand = () => new Command('lint')
3253
logCommandErrorAndExit(resolveResult);
3354
}
3455

35-
const result = await runEslint(resolveResult.asset, fix, stats);
56+
const result = await runEslint(resolveResult.asset, opts);
3657
console.log(formatLintResult(result));
3758

3859
switch (result.severity) {
@@ -50,10 +71,16 @@ export const getLintGlobalCommand = () => new Command('lintglobal')
5071
.option('--fix', 'Output linting fixes', false)
5172
.option('--stats', 'Output linting stats', false)
5273
.option('--ci', process.env.CI)
53-
.action(async ({ fix, stats, ci }) => {
54-
console.log(chalk.cyanBright('Beginning lint global'));
55-
const result = await lintGlobal(fix, stats);
74+
.addOption(concurrencyOption)
75+
.action(async ({ ci, ...opts }) => {
5676
const prefix = chalk.blueBright('[lintglobal]');
77+
78+
console.log(`${prefix} ${chalk.cyanBright('Beginning linting with the following options:')}`);
79+
Object.entries(opts).forEach(([key, value], i) => {
80+
console.log(` ${i+1}. ${chalk.greenBright(key)}: ${chalk.cyanBright(value)}`);
81+
});
82+
83+
const result = await lintGlobal(opts);
5784
const logs = [
5885
`${prefix} Took ${divideAndRound(result.filesElapsed, 1000)}s to find files`,
5986
`${prefix} Took ${divideAndRound(result.lintElapsed, 1000)}s to lint files`,
@@ -65,7 +92,7 @@ export const getLintGlobalCommand = () => new Command('lintglobal')
6592

6693
console.log(logs.join('\n'));
6794

68-
if (stats) {
95+
if (opts.stats) {
6996
const statPath = pathlib.join(outDir, 'lintstats.csv');
7097
console.log(chalk.greenBright(`Stats written to ${statPath}`));
7198
}

lib/buildtools/src/prebuild/lint.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ import { findSeverity, flatMapAsync, isNodeError } from '@sourceacademy/modules-
66
import chalk from 'chalk';
77
import { ESLint } from 'eslint';
88

9+
export interface LintOptions {
10+
concurrency: Exclude<ESLint.Options['concurrency'], undefined>;
11+
fix: boolean;
12+
stats: boolean;
13+
}
14+
915
export interface LintResult {
1016
formatted: string;
1117
severity: Severity;
@@ -35,11 +41,12 @@ async function timePromise<T>(f: () => Promise<T>) {
3541
}
3642

3743
// #region runEslint
38-
export async function runEslint(input: InputAsset, fix: boolean, stats: boolean): Promise<LintResult> {
44+
export async function runEslint(input: InputAsset, { fix, stats, concurrency }: LintOptions): Promise<LintResult> {
3945
const linter = new ESLint({
4046
fix,
4147
stats,
42-
cwd: gitRoot
48+
concurrency,
49+
cwd: gitRoot,
4350
});
4451

4552
try {
@@ -104,8 +111,8 @@ interface LintGlobalResults {
104111
* all together causes ESLint to run out of memory, we have this function that lints everything else
105112
* so that the bundles and tabs can be linted separately.
106113
*/
107-
export async function lintGlobal(fix: boolean, stats: boolean): Promise<LintGlobalResults> {
108-
const linter = new ESLint({ fix, stats, cwd: gitRoot });
114+
export async function lintGlobal(opts: LintOptions): Promise<LintGlobalResults> {
115+
const linter = new ESLint({ cwd: gitRoot, ...opts });
109116

110117
/**
111118
* Recursively determine what files to to lint. Just supplying folder paths
@@ -147,21 +154,21 @@ export async function lintGlobal(fix: boolean, stats: boolean): Promise<LintGlob
147154
const { result: lintResults, elapsed: lintElapsed } = await timePromise(() => flatMapAsync(toLint, each => linter.lintFiles(each)));
148155

149156
let fixElapsed: number | undefined = undefined;
150-
if (fix) {
157+
if (opts.fix) {
151158
;({ elapsed: fixElapsed } = await timePromise(() => ESLint.outputFixes(lintResults)));
152159
}
153160

154161
const formatter = await linter.loadFormatter('stylish');
155162
const formatted = await formatter.format(lintResults);
156163

157-
if (stats) {
164+
if (opts.stats) {
158165
const csvFormatter = await linter.loadFormatter(pathlib.join(import.meta.dirname, '../../lintplugin/dist/formatter.js'));
159166
const csvFormatted = await csvFormatter.format(lintResults);
160167
await fs.mkdir(outDir, { recursive: true });
161168
await fs.writeFile(pathlib.join(outDir, 'lintstats.csv'), csvFormatted);
162169
}
163170

164-
const severity = findSeverity(lintResults, each => severityFinder(each, fix));
171+
const severity = findSeverity(lintResults, each => severityFinder(each, opts.fix));
165172

166173
return {
167174
formatted,

lib/lintplugin/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"./formatter": "./dist/formatter.js"
1212
},
1313
"dependencies": {
14-
"eslint": "^9.31.0"
14+
"eslint": "^9.35.0"
1515
},
1616
"peerDependencies": {
1717
"@eslint/markdown": "^6.6.0",

lib/modules-lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"@types/react-dom": "^18.3.1",
1010
"@vitejs/plugin-react": "^4.7.0",
1111
"@vitest/browser": "^3.2.3",
12-
"eslint": "^9.31.0",
12+
"eslint": "^9.35.0",
1313
"playwright": "^1.54.1",
1414
"typedoc": "^0.28.9",
1515
"typedoc-plugin-frontmatter": "^1.3.0",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"@vitest/eslint-plugin": "^1.3.4",
6565
"@yarnpkg/types": "^4.0.1",
6666
"esbuild": "^0.25.8",
67-
"eslint": "^9.31.0",
67+
"eslint": "^9.35.0",
6868
"eslint-plugin-import": "^2.32.0",
6969
"eslint-plugin-jsdoc": "^51.3.1",
7070
"eslint-plugin-mdx": "^3.6.2",

0 commit comments

Comments
 (0)