Skip to content

Commit 4c7ece6

Browse files
committed
Build: Switch from JSHint to ESLint
Motivated by fixing npm-audit warning for minimatch 3.0 (fixed in minimatch 3.1) due to hardcoded version in JSHint. https://nvd.nist.gov/vuln/detail/CVE-2026-26996 https://nvd.nist.gov/vuln/detail/CVE-2026-27903 https://nvd.nist.gov/vuln/detail/CVE-2026-27904 * Match main Grunt repo which moved to ESLint years ago This matches what gruntjs/grunt uses. Ref gruntjs/grunt#1533 * Fix ESLint output noise by overriding eslint-plugin-pabigot until the upstream config is updated accordingly. Ref markelog/eslint-config-grunt#2
1 parent 297c6cc commit 4c7ece6

10 files changed

Lines changed: 57 additions & 51 deletions

File tree

.eslintrc.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"root": true,
3+
"extends": "grunt",
4+
"parserOptions": {
5+
"ecmaVersion": 2017
6+
},
7+
"env": {
8+
"es2017": true
9+
},
10+
"rules": {
11+
"one-var": ["error", "never"]
12+
}
13+
}

.github/workflows/test.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,16 @@ jobs:
2020
fail-fast: false
2121
matrix:
2222
os: [ubuntu-latest, windows-latest]
23-
# Pin to 22.4 because of https://github.com/npm/cli/issues/7657
24-
node: [18, 20, "22.4"]
23+
node: [18, 20, 24]
2524

2625
steps:
2726
- name: Clone repository
28-
uses: actions/checkout@v4
27+
uses: actions/checkout@v6
2928
with:
3029
persist-credentials: false
3130

3231
- name: Set up Node.js
33-
uses: actions/setup-node@v4
32+
uses: actions/setup-node@v6
3433
with:
3534
node-version: ${{ matrix.node }}
3635

.jshintrc

Lines changed: 0 additions & 15 deletions
This file was deleted.

Gruntfile.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,6 @@ module.exports = function(grunt) {
1212

1313
// Project configuration.
1414
grunt.initConfig({
15-
jshint: {
16-
all: [
17-
'Gruntfile.js',
18-
'tasks/**/*.js'
19-
],
20-
options: {
21-
jshintrc: '.jshintrc'
22-
}
23-
},
24-
2515
// Create a local web server for testing http:// URIs.
2616
connect: {
2717
rootServer: {
@@ -254,13 +244,12 @@ module.exports = function(grunt) {
254244
grunt.loadTasks('tasks');
255245

256246
// These plugins provide necessary tasks.
257-
grunt.loadNpmTasks('grunt-contrib-jshint');
258247
grunt.loadNpmTasks('grunt-contrib-connect');
259248
grunt.loadNpmTasks('grunt-contrib-internal');
260249
grunt.loadNpmTasks('grunt-shell');
261250

262251
// Whenever the "test" task is run, run some basic tests.
263-
grunt.registerTask('test', ['jshint', 'connect', 'qunit', 'shell', 'really-test']);
252+
grunt.registerTask('test', ['connect', 'qunit', 'shell', 'really-test']);
264253

265254
// By default, lint and run all tests.
266255
grunt.registerTask('default', ['test', 'contrib-core', 'contrib-ci:skipIfExists']);

chrome/bridge.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Licensed under the MIT license.
77
*/
88

9-
/* global QUnit:true */
9+
/* eslint-env qunit, browser */
1010
(function() {
1111
'use strict';
1212

@@ -56,14 +56,14 @@
5656
// In that case, replace actual and expected
5757
var errors = obj.errors;
5858
if (!canBeJSONStringified(errors)) {
59-
errors = obj.errors.map(function (error) {
59+
errors = obj.errors.map(function(error) {
6060
return {
6161
passed: error.passed,
6262
message: error.message,
6363
stack: error.stack,
6464
actual: replaceIfCannotBeJSONStringified(error.actual),
6565
expected: replaceIfCannotBeJSONStringified(error.expected)
66-
}
66+
};
6767
});
6868
}
6969

package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@
1313
},
1414
"main": "tasks/qunit.js",
1515
"scripts": {
16-
"test": "grunt test --stack"
16+
"test": "eslint . && grunt test --stack"
1717
},
1818
"dependencies": {
1919
"eventemitter2": "^6.4.9",
2020
"puppeteer": "^22.0.0"
2121
},
22+
"overrides": {
23+
"eslint-plugin-pabigot": "^1.1.0"
24+
},
2225
"devDependencies": {
2326
"difflet": "^1.0.1",
27+
"eslint-config-grunt": "^2.0.1",
28+
"eslint-plugin-pabigot": "^1.1.0",
29+
"eslint": "^8.57.0",
2430
"grunt": "^1.6.1",
25-
"grunt-contrib-connect": "^4.0.0",
31+
"grunt-contrib-connect": "^5.0.0",
2632
"grunt-contrib-internal": "^9.0.0",
27-
"grunt-contrib-jshint": "^3.2.0",
2833
"grunt-shell": "^4.0.0",
2934
"qunit": "^2.21.0"
3035
},

tasks/qunit.js

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ var Promise = global.Promise;
2121
// Shared functions
2222

2323
// Allow an error message to retain its color when split across multiple lines.
24-
function formatMessage (message) {
24+
function formatMessage(message) {
2525
var str = String(message);
2626
if (typeof message === 'object' && /^\[object .*\]$/.test(str)) {
2727
// try to use the JSON as a better string representation
2828
try {
2929
str = JSON.stringify(message, null, 2);
30-
} catch ( _ ) {
30+
} catch (_) {
3131
}
3232
}
3333
return String(str).split('\n')
@@ -38,7 +38,7 @@ function formatMessage (message) {
3838
}
3939

4040

41-
function createRunEnd () {
41+
function createRunEnd() {
4242
return {
4343
status: 'passed',
4444
testCounts: {
@@ -80,7 +80,7 @@ function generateMessage(combined) {
8080
}
8181

8282
// Copied from QUnit source code
83-
function generateHash (module) {
83+
function generateHash(module) {
8484
var hex;
8585
var i = 0;
8686
var hash = 0;
@@ -104,7 +104,7 @@ function generateHash (module) {
104104
}
105105

106106
function getPath(url) {
107-
if (url.substr( 0, 7 ) === 'http://' || url.substr( 0, 8 ) === 'https://') {
107+
if (url.substr(0, 7) === 'http://' || url.substr(0, 8) === 'https://') {
108108
return url;
109109
}
110110

@@ -127,7 +127,7 @@ module.exports = function(grunt) {
127127
var asset = path.join.bind(null, __dirname, '..');
128128

129129
// If options.force then log an error, otherwise exit with a warning
130-
function warnUnlessForced (message) {
130+
function warnUnlessForced(message) {
131131
if (options && options.force) {
132132
grunt.log.error(message);
133133
} else {
@@ -231,7 +231,7 @@ module.exports = function(grunt) {
231231
combinedRunEnd.status = 'failed';
232232
});
233233

234-
eventBus.on('qunit.on.error', function (err) {
234+
eventBus.on('qunit.on.error', function(err) {
235235
// It is the responsibility of QUnit to ensure a run is marked as failure
236236
// if there are (unexpected) messages received from window.onerror.
237237
//
@@ -250,7 +250,7 @@ module.exports = function(grunt) {
250250
grunt.event.emit('qunit.error.onError', err);
251251
});
252252

253-
eventBus.on('error.onError', function (msg) {
253+
eventBus.on('error.onError', function(msg) {
254254
// This is important in addition to `QUnit.on('error')` to catch uncaught
255255
// errors that happen before the bridge is in effect (which in practice
256256
// will happen at DOMContentLoaded, after qunit.js and test files have done
@@ -296,7 +296,7 @@ module.exports = function(grunt) {
296296
// Read the content of the specified bridge files
297297
var bridgeFiles = Array.isArray(options.inject) ? options.inject : [options.inject];
298298
var bridgContents = [
299-
"__grunt_contrib_qunit_timeout__ = " + JSON.stringify( options.timeout ) + ";"
299+
'__grunt_contrib_qunit_timeout__ = ' + JSON.stringify(options.timeout) + ';'
300300
];
301301

302302
for (var i = 0; i < bridgeFiles.length; i++) {
@@ -329,7 +329,7 @@ module.exports = function(grunt) {
329329
done(success);
330330
}
331331

332-
function appendToUrls (queryParam, value) {
332+
function appendToUrls(queryParam, value) {
333333
// Append the query param to all urls
334334
urls = urls.map(function(testUrl) {
335335
var parsed = url.parse(testUrl, true);
@@ -415,7 +415,14 @@ module.exports = function(grunt) {
415415
// Tell the client that when DOMContentLoaded fires, it needs to tell this
416416
// script to inject the bridge. This should ensure that the bridge gets
417417
// injected before any other DOMContentLoaded or window.load event handler.
418-
page.evaluateOnNewDocument('if (window.QUnit) {\n' + bridgContents.join(";") + '\n} else {\n' + 'document.addEventListener("DOMContentLoaded", function() {\n' + bridgContents.join(";") + '\n});\n}\n');
418+
page.evaluateOnNewDocument(
419+
'if (window.QUnit) {\n' +
420+
bridgContents.join(';') +
421+
'\n} else {\n' +
422+
'document.addEventListener("DOMContentLoaded", function() {\n' +
423+
bridgContents.join(';') +
424+
'\n});\n}\n'
425+
);
419426

420427
for (const url of urls) {
421428
// Reset current module.
@@ -453,7 +460,7 @@ module.exports = function(grunt) {
453460
})
454461
.catch(function(err) {
455462
// If anything goes wrong, terminate the grunt task
456-
grunt.log.error("There was an error with headless chrome");
463+
grunt.log.error('There was an error with headless chrome');
457464
grunt.fail.fatal(err);
458465
finishTask(false);
459466
});

test/.eslintrc.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "../.eslintrc.json",
3+
"env": {
4+
"browser": true,
5+
"qunit": true
6+
}
7+
}

test/qunit_noglobals.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ QUnit.on('testEnd', function(test) {
99

1010
QUnit.todo('global pollution', function(assert) {
1111
window.myPollution = true;
12+
// eslint-disable-next-line no-undef
1213
assert.true(myPollution, 'nasty pollution');
1314
// We expect QUnit to add an error to the end of this test
1415
});

test/qunit_page_timeout.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
QUnit.module('grunt-contrib-qunit timeout');
22

33
QUnit.test('last forever', function(assert) {
4-
var done = assert.async();
4+
assert.async();
55

6-
assert.ok( true );
6+
assert.ok(true);
77
});

0 commit comments

Comments
 (0)