From 017733ee4900777ebecd3979a6539b3148a64c7b Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Wed, 10 Jan 2024 17:24:51 -0600 Subject: [PATCH 1/2] Add prettier formatting of JavaScript, style, and HTML files, and check formatting in a workflow. Prettier is used to format JavaScript, style, and HTML files in htdocs. The configuration chosen is in .prettierrc and .editorconfig. Prettier doesn't really have many options as it is very opinionated. I am not entirely happy with everything prettier does, but it does give a more uniform code format to files. Furthermore, it is nice to be able to be able to just run prettier and not need to worry about formatting code. A workflow now checks formatting of JavaScript, style, and HTML files with prettier in addition checking formatting of perl files with perltidy. Note the "linter" workflow has been renamed to "check-formats", because that is what it does, and this really does not have anything to do with linting. Note that developers can (and should) run `npm run prettier-format` in the htdocs directory to format files. You can also run `npm run prettier-check` to check that formatting of these files is correct, and not actually change the files. This is what the workflow does. All `.dist.yml` files are also formatted and checked with prettier. --- .editorconfig | 17 +++++++++++ .github/workflows/check-formats.yml | 44 +++++++++++++++++++++++++++++ .github/workflows/linter.yml | 32 --------------------- .prettierrc | 8 ++++++ htdocs/package-lock.json | 22 +++++++++++++++ htdocs/package.json | 5 +++- 6 files changed, 95 insertions(+), 33 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/check-formats.yml delete mode 100644 .github/workflows/linter.yml create mode 100644 .prettierrc diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..ee82116646 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +max_line_length = 120 +trim_trailing_whitespace = true +indent_style = tab +indent_size = 4 + +[*.yml] +indent_style = space +indent_size = 2 + +[*.pg] +trim_trailing_whitespace = false diff --git a/.github/workflows/check-formats.yml b/.github/workflows/check-formats.yml new file mode 100644 index 0000000000..ec47d6c741 --- /dev/null +++ b/.github/workflows/check-formats.yml @@ -0,0 +1,44 @@ +--- +name: Check Formatting of Code Base + +defaults: + run: + shell: bash + +on: + push: + branches-ignore: [main, develop] + pull_request: + +jobs: + perltidy: + name: Check Perl file formatting with perltidy + runs-on: ubuntu-22.04 + container: + image: perl:5.34 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install dependencies + run: cpanm -n Perl::Tidy@20220613 + - name: Run perltidy + shell: bash + run: | + git config --global --add safe.directory "$GITHUB_WORKSPACE" + shopt -s extglob globstar nullglob + perltidy --pro=./.perltidyrc -b -bext='/' ./**/*.p[lm] ./**/*.t && git diff --exit-code + + prettier: + name: Check JavaScript, style, and HTML file formatting with prettier + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install Dependencies + run: cd htdocs && npm ci --ignore-scripts + - name: Check formatting with prettier + run: cd htdocs && npm run prettier-check diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml deleted file mode 100644 index d64ec1b02a..0000000000 --- a/.github/workflows/linter.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Lint Code Base - -defaults: - run: - shell: bash - -on: - push: - branches-ignore: [main, develop] - pull_request: - -jobs: - perltidy: - name: Run perltidy on Perl Files - runs-on: ubuntu-22.04 - container: - image: perl:5.34 - steps: - - uses: actions/checkout@v4 - - name: perl -V - run: perl -V - - name: Install dependencies - run: cpanm -n Perl::Tidy@20220613 - - name: perltidy --version - run: perltidy --version - - name: Run perltidy - shell: bash - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - shopt -s extglob globstar nullglob - perltidy --pro=./.perltidyrc -b -bext='/' ./**/*.p[lm] ./**/*.t && git diff --exit-code diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..b21dab0657 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "printWidth": 120, + "semi": true, + "singleQuote": true, + "trailingComma": "none" +} diff --git a/htdocs/package-lock.json b/htdocs/package-lock.json index 28ebb0033b..9caff05ca4 100644 --- a/htdocs/package-lock.json +++ b/htdocs/package-lock.json @@ -24,6 +24,7 @@ "chokidar": "^3.5.3", "cssnano": "^6.0.1", "postcss": "^8.4.31", + "prettier": "^3.1.1", "rtlcss": "^4.1.1", "sass": "^1.69.5", "terser": "^5.24.0", @@ -1370,6 +1371,21 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2544,6 +2560,12 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", diff --git a/htdocs/package.json b/htdocs/package.json index 13ef2aabea..50d0fb21e5 100644 --- a/htdocs/package.json +++ b/htdocs/package.json @@ -4,7 +4,9 @@ "license": "GPL-2.0+", "scripts": { "generate-assets": "node generate-assets", - "prepare": "npm run generate-assets" + "prepare": "npm run generate-assets", + "prettier-format": "prettier --ignore-path=../.gitignore --write \"**/*.{js,css,scss,html}\" \"../**/*.dist.yml\"", + "prettier-check": "prettier --ignore-path=../.gitignore --check \"**/*.{js,css,scss,html}\" \"../**/*.dist.yml\"" }, "repository": { "type": "git", @@ -28,6 +30,7 @@ "chokidar": "^3.5.3", "cssnano": "^6.0.1", "postcss": "^8.4.31", + "prettier": "^3.1.1", "rtlcss": "^4.1.1", "sass": "^1.69.5", "terser": "^5.24.0", From 3f7416690a547699383dc88e04060042b5d046b8 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Wed, 10 Jan 2024 22:23:44 -0600 Subject: [PATCH 2/2] Apply prettier to all JavaScript, style, and HTML files in htdocs. --- htdocs/css/rtl.css | 1 - htdocs/generate-assets.js | 85 +- htdocs/index.dist.html | 8 +- htdocs/js/DatePicker/datepicker.js | 10 +- htdocs/js/GatewayQuiz/gateway.js | 60 +- htdocs/js/InstructorTools/instructortools.js | 22 +- htdocs/js/LocalStorage/localstorage.js | 5 +- htdocs/js/MathJaxConfig/mathjax-config.js | 144 +- htdocs/js/PGCodeMirror/PG.js | 2322 +++++++++-------- htdocs/js/PGCodeMirror/comment.js | 172 +- htdocs/js/PGCodeMirror/pgeditor.js | 28 +- htdocs/js/PGProblemEditor/pgproblemeditor.js | 288 +- htdocs/js/Problem/problem.js | 4 +- htdocs/js/ProblemGrader/problemgrader.js | 16 +- .../js/ProblemSetDetail/problemsetdetail.js | 345 ++- htdocs/js/RenderProblem/renderproblem.js | 148 +- htdocs/js/SampleProblemViewer/viewer.js | 4 +- htdocs/js/SelectAll/selectall.js | 20 +- htdocs/js/SetMaker/setmaker.js | 143 +- htdocs/js/UserDetail/userdetail.js | 40 +- htdocs/themes/math4-green/_theme-colors.scss | 2 +- htdocs/themes/math4-red/_theme-colors.scss | 2 +- htdocs/themes/math4-red/_theme-overrides.scss | 3 +- htdocs/themes/math4-yellow/_theme-colors.scss | 12 +- .../themes/math4-yellow/_theme-overrides.scss | 36 +- htdocs/themes/math4/_theme-colors.scss | 4 +- htdocs/themes/math4/achievements.scss | 4 +- htdocs/themes/math4/bootstrap.scss | 82 +- htdocs/themes/math4/gateway.scss | 20 +- htdocs/themes/math4/math4.js | 45 +- htdocs/themes/math4/math4.scss | 92 +- 31 files changed, 2300 insertions(+), 1867 deletions(-) diff --git a/htdocs/css/rtl.css b/htdocs/css/rtl.css index 95691f2401..8a2379a99c 100644 --- a/htdocs/css/rtl.css +++ b/htdocs/css/rtl.css @@ -17,4 +17,3 @@ /* The changes which were needed here in WeBWorK 2.16 are no * longer needed in WeBWorK 2.17. The file is being retained * for potential future use. */ - diff --git a/htdocs/generate-assets.js b/htdocs/generate-assets.js index 5f275e266f..81ee6c7b10 100755 --- a/htdocs/generate-assets.js +++ b/htdocs/generate-assets.js @@ -15,7 +15,10 @@ const rtlcss = require('rtlcss'); const cssMinify = require('cssnano'); const argv = yargs - .usage('$0 Options').version(false).alias('help', 'h').wrap(100) + .usage('$0 Options') + .version(false) + .alias('help', 'h') + .wrap(100) .option('enable-sourcemaps', { alias: 's', description: 'Generate source maps. (Not for use in production!)', @@ -30,8 +33,7 @@ const argv = yargs alias: 'd', description: 'Delete all generated files.', type: 'boolean' - }) - .argv; + }).argv; const assetFile = path.resolve(__dirname, 'static-assets.json'); const assets = {}; @@ -48,7 +50,7 @@ const cleanDir = (dir) => { } } } -} +}; // The is set to true after all files are processed for the first time. let ready = false; @@ -75,12 +77,13 @@ const processFile = async (file, _details) => { return; } - const minJS = result.code + ( - argv.enableSourcemaps && result.map - ? `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${ - Buffer.from(result.map).toString('base64')}` - : '' - ); + const minJS = + result.code + + (argv.enableSourcemaps && result.map + ? `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${Buffer.from( + result.map + ).toString('base64')}` + : ''); const contentHash = crypto.createHash('sha256'); contentHash.update(minJS); @@ -114,18 +117,19 @@ const processFile = async (file, _details) => { return; } - if (result.sourceMap) result.sourceMap.sources = [ baseName ]; + if (result.sourceMap) result.sourceMap.sources = [baseName]; // Pass the compiled css through the autoprefixer. // This is really only needed for the bootstrap.css files, but doesn't hurt for the rest. let prefixedResult = await postcss([autoprefixer, cssMinify]).process(result.css, { from: baseName }); - const minCSS = prefixedResult.css + ( - argv.enableSourcemaps && result.sourceMap - ? `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${ - Buffer.from(JSON.stringify(result.sourceMap)).toString('base64')}*/` - : '' - ); + const minCSS = + prefixedResult.css + + (argv.enableSourcemaps && result.sourceMap + ? `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${Buffer.from( + JSON.stringify(result.sourceMap) + ).toString('base64')}*/` + : ''); const contentHash = crypto.createHash('sha256'); contentHash.update(minCSS); @@ -149,18 +153,21 @@ const processFile = async (file, _details) => { // Pass the compiled css through rtlcss and autoprefixer to generate css for right-to-left languages. let rtlResult = await postcss([rtlcss, autoprefixer, cssMinify]).process(result.css, { from: baseName }); - const rtlCSS = rtlResult.css + ( - argv.enableSourcemaps && result.sourceMap - ? `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${ - Buffer.from(JSON.stringify(result.sourceMap)).toString('base64')}*/` - : '' - ); + const rtlCSS = + rtlResult.css + + (argv.enableSourcemaps && result.sourceMap + ? `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${Buffer.from( + JSON.stringify(result.sourceMap) + ).toString('base64')}*/` + : ''); const rtlContentHash = crypto.createHash('sha256'); rtlContentHash.update(rtlCSS); - const newRTLVersion = file.replace(/\.s?css$/, - `.rtl.${rtlContentHash.digest('hex').substring(0, 8)}.min.css`); + const newRTLVersion = file.replace( + /\.s?css$/, + `.rtl.${rtlContentHash.digest('hex').substring(0, 8)}.min.css` + ); fs.writeFileSync(path.resolve(__dirname, newRTLVersion), rtlCSS); const rtlAssetName = file.replace(/\.s?css$/, '.rtl.css'); @@ -180,8 +187,9 @@ const processFile = async (file, _details) => { } } else { if (argv.watchFiles) - console.log('\x1b[33mWatches established, and initial build complete.\n' - + 'Press Control-C to stop.\x1b[0m'); + console.log( + '\x1b[33mWatches established, and initial build complete.\n' + 'Press Control-C to stop.\x1b[0m' + ); ready = true; } @@ -202,20 +210,25 @@ for (const file of fs.readdirSync(themesDir, { withFileTypes: true })) { if (!file.isDirectory()) continue; if (!fs.existsSync(path.resolve(themesDir, file.name, 'math4-overrides.js'))) fs.closeSync(fs.openSync(path.resolve(themesDir, file.name, 'math4-overrides.js'), 'w')); - if (!fs.existsSync(path.resolve(themesDir, file.name, 'math4-overrides.css')) - && !fs.existsSync(path.resolve(themesDir, file.name, 'math4-overrides.scss'))) + if ( + !fs.existsSync(path.resolve(themesDir, file.name, 'math4-overrides.css')) && + !fs.existsSync(path.resolve(themesDir, file.name, 'math4-overrides.scss')) + ) fs.closeSync(fs.openSync(path.resolve(themesDir, file.name, 'math4-overrides.css'), 'w')); } // Set up the watcher. if (argv.watchFiles) console.log('\x1b[32mEstablishing watches and performing initial build.\x1b[0m'); -chokidar.watch(['js', 'themes'], { - ignored: /layouts|\.min\.(js|css)$/, - cwd: __dirname, // Make sure all paths are given relative to the htdocs directory. - awaitWriteFinish: { stabilityThreshold: 500 }, - persistent: argv.watchFiles ? true : false -}) - .on('add', processFile).on('change', processFile).on('ready', processFile) +chokidar + .watch(['js', 'themes'], { + ignored: /layouts|\.min\.(js|css)$/, + cwd: __dirname, // Make sure all paths are given relative to the htdocs directory. + awaitWriteFinish: { stabilityThreshold: 500 }, + persistent: argv.watchFiles ? true : false + }) + .on('add', processFile) + .on('change', processFile) + .on('ready', processFile) .on('unlink', (file) => { // If a file is deleted, then also delete the corresponding generated file. if (assets[file]) { diff --git a/htdocs/index.dist.html b/htdocs/index.dist.html index 532c6f4032..a82efd7355 100644 --- a/htdocs/index.dist.html +++ b/htdocs/index.dist.html @@ -1,4 +1,4 @@ - + WeBWorK Placeholder Page @@ -6,12 +6,10 @@

WeBWorK Placeholder Page

Exploring?

-

- This is the default page for the root url of this site. -

+

This is the default page for the root url of this site.

If you want to see something better here, then copy webwork2/htdocs/index.dist.html (this file) to - webwork/htdocs/index.html, and modify it to show what you want to show. Then that file will be displayed + webwork/htdocs/index.html, and modify it to show what you want to show. Then that file will be displayed instead.

diff --git a/htdocs/js/DatePicker/datepicker.js b/htdocs/js/DatePicker/datepicker.js index bdaef40623..3cbe63dfc6 100644 --- a/htdocs/js/DatePicker/datepicker.js +++ b/htdocs/js/DatePicker/datepicker.js @@ -50,11 +50,11 @@ // Compute the time difference between the current browser timezone and the course timezone. // flatpickr gives the time in the browser's timezone, and this is used to adjust to the course timezone. // Note that this is in seconds. - const timezoneAdjustment = ( - (new Date((new Date).toLocaleString('en-US'))).getTime() - - (new Date((new Date).toLocaleString('en-US', - { timeZone: rule.dataset.timezone ?? 'America/New_York' }))).getTime() - ); + const timezoneAdjustment = + new Date(new Date().toLocaleString('en-US')).getTime() - + new Date( + new Date().toLocaleString('en-US', { timeZone: rule.dataset.timezone ?? 'America/New_York' }) + ).getTime(); const fp = flatpickr(rule.parentNode, { allowInput: true, diff --git a/htdocs/js/GatewayQuiz/gateway.js b/htdocs/js/GatewayQuiz/gateway.js index 09f97d2c63..26ddd35fc1 100644 --- a/htdocs/js/GatewayQuiz/gateway.js +++ b/htdocs/js/GatewayQuiz/gateway.js @@ -12,9 +12,10 @@ const timerDiv = document.getElementById('gwTimer'); // The timer div element let actuallySubmit = false; // This needs to be set to true to allow an actual submission. // The 'Grade Test' submit button. - const submitAnswers = document.gwquiz.elements.submitAnswers instanceof NodeList - ? document.gwquiz.elements.submitAnswers[document.gwquiz.elements.submitAnswers.length - 1] - : document.gwquiz.elements.submitAnswers; + const submitAnswers = + document.gwquiz.elements.submitAnswers instanceof NodeList + ? document.gwquiz.elements.submitAnswers[document.gwquiz.elements.submitAnswers.length - 1] + : document.gwquiz.elements.submitAnswers; let timeDelta; // The difference between the browser time and the server time let serverDueTime; // The time the test is due let gracePeriod; // The grace period @@ -32,7 +33,14 @@ const alertToast = (message, delay = 5000) => { const toastContainer = document.createElement('div'); toastContainer.classList.add( - 'gwAlert', 'toast-container', 'position-fixed', 'top-0', 'start-50', 'translate-middle-x', 'p-3'); + 'gwAlert', + 'toast-container', + 'position-fixed', + 'top-0', + 'start-50', + 'translate-middle-x', + 'p-3' + ); toastContainer.innerHTML = '