From 055297065b06d22ae41c8940d918978b82d5adc3 Mon Sep 17 00:00:00 2001 From: Shinigami Date: Sun, 24 Sep 2023 00:59:37 +0200 Subject: [PATCH] Add eslint-plugin-unicorn (#470) --- .eslintrc.cjs | 7 + package.json | 1 + pnpm-lock.yaml | 294 ++++++++++--- src/logger.ts | 10 +- src/options/attribute-sorting/utils.ts | 2 +- src/options/common.ts | 4 +- src/options/converge.ts | 4 +- src/options/empty-attributes/utils.ts | 4 +- src/options/pug-attribute-separator.ts | 3 +- src/options/pug-comment-preserve-spaces.ts | 7 +- src/options/pug-wrap-attributes.ts | 2 +- src/printer.ts | 410 +++++++++--------- src/utils/angular.ts | 4 +- src/utils/common.ts | 26 +- src/utils/script-mime-types.ts | 4 +- .../sortAttributes/sort-attributes.test.ts | 8 +- tests/pug-tests/pug.test.ts | 6 +- 17 files changed, 509 insertions(+), 287 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index afdf43f6..429ab654 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -18,6 +18,7 @@ module.exports = defineConfig({ 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking', 'plugin:prettier/recommended', + 'plugin:unicorn/recommended', ], parser: '@typescript-eslint/parser', parserOptions: { @@ -30,6 +31,7 @@ module.exports = defineConfig({ 'prettier', 'spellcheck', 'inclusive-language', + 'unicorn', ], rules: { curly: ['error'], @@ -45,6 +47,11 @@ module.exports = defineConfig({ quotes: ['error', 'single', { avoidEscape: true }], semi: ['error', 'always'], + 'unicorn/consistent-destructuring': 'off', + 'unicorn/no-null': 'off', + 'unicorn/no-useless-switch-case': 'off', + 'unicorn/prevent-abbreviations': 'off', + '@typescript-eslint/array-type': [ 'warn', { default: 'array-simple', readonly: 'generic' }, diff --git a/package.json b/package.json index 447c5e74..ebf15710 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "eslint-plugin-jsdoc": "~46.8.1", "eslint-plugin-prettier": "~5.0.0", "eslint-plugin-spellcheck": "~0.0.20", + "eslint-plugin-unicorn": "~48.0.1", "npm-run-all": "~4.1.5", "prettier": "3.0.3", "prettier-plugin-organize-imports": "~3.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c266912..23b39ac5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ dependencies: devDependencies: '@types/node': specifier: ~20.6.3 - version: 20.6.3 + version: 20.6.4 '@typescript-eslint/eslint-plugin': specifier: ~6.7.2 version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) @@ -21,7 +21,7 @@ devDependencies: version: 6.7.2(eslint@8.49.0)(typescript@5.2.2) '@vitest/coverage-v8': specifier: ~0.34.4 - version: 0.34.4(vitest@0.34.4) + version: 0.34.5(vitest@0.34.5) benchmark: specifier: ~2.1.4 version: 2.1.4 @@ -39,16 +39,19 @@ devDependencies: version: 0.1.0(eslint@8.49.0) eslint-plugin-inclusive-language: specifier: ~2.2.0 - version: 2.2.0 + version: 2.2.1 eslint-plugin-jsdoc: specifier: ~46.8.1 - version: 46.8.1(eslint@8.49.0) + version: 46.8.2(eslint@8.49.0) eslint-plugin-prettier: specifier: ~5.0.0 version: 5.0.0(eslint-config-prettier@9.0.0)(eslint@8.49.0)(prettier@3.0.3) eslint-plugin-spellcheck: specifier: ~0.0.20 version: 0.0.20(eslint@8.49.0) + eslint-plugin-unicorn: + specifier: ~48.0.1 + version: 48.0.1(eslint@8.49.0) npm-run-all: specifier: ~4.1.5 version: 4.1.5 @@ -69,13 +72,13 @@ devDependencies: version: 5.2.2 vite: specifier: ~4.4.9 - version: 4.4.9(@types/node@20.6.3) + version: 4.4.9(@types/node@20.6.4) vitepress: specifier: 1.0.0-rc.14 - version: 1.0.0-rc.14(@types/node@20.6.3)(search-insights@2.8.2) + version: 1.0.0-rc.14(@types/node@20.6.4)(search-insights@2.8.2) vitest: specifier: ~0.34.4 - version: 0.34.4 + version: 0.34.5 packages: @@ -230,6 +233,14 @@ packages: '@jridgewell/trace-mapping': 0.3.19 dev: true + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} @@ -240,6 +251,15 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + /@babel/parser@7.22.16: resolution: {integrity: sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==} engines: {node: '>=6.0.0'} @@ -533,7 +553,7 @@ packages: ajv: 6.12.6 debug: 4.3.4 espree: 9.6.1 - globals: 13.21.0 + globals: 13.22.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -684,8 +704,12 @@ packages: resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} dev: true - /@types/node@20.6.3: - resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==} + /@types/node@20.6.4: + resolution: {integrity: sha512-nU6d9MPY0NBUMiE/nXd2IIoC4OLvsLpwAjheoAeuzgvDZA1Cb10QYg+91AF6zQiKWRN5i1m07x6sMe0niBznoQ==} + dev: true + + /@types/normalize-package-data@2.4.2: + resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==} dev: true /@types/semver@7.5.2: @@ -827,8 +851,8 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@vitest/coverage-v8@0.34.4(vitest@0.34.4): - resolution: {integrity: sha512-TZ5ghzhmg3COQqfBShL+zRQEInHmV9TSwghTdfkHpCTyTOr+rxo6x41vCNcVfWysWULtqtBVpY6YFNovxnESfA==} + /@vitest/coverage-v8@0.34.5(vitest@0.34.5): + resolution: {integrity: sha512-97xjhRTSdmeeHCm2nNHhT3hLsMYkAhHXm/rwj6SZ3voka8xiCJrwgtfIjoZIFEL4OO0KezGmVuHWQXcMunULIA==} peerDependencies: vitest: '>=0.32.0 <1' dependencies: @@ -843,43 +867,43 @@ packages: std-env: 3.4.3 test-exclude: 6.0.0 v8-to-istanbul: 9.1.0 - vitest: 0.34.4 + vitest: 0.34.5 transitivePeerDependencies: - supports-color dev: true - /@vitest/expect@0.34.4: - resolution: {integrity: sha512-XlMKX8HyYUqB8dsY8Xxrc64J2Qs9pKMt2Z8vFTL4mBWXJsg4yoALHzJfDWi8h5nkO4Zua4zjqtapQ/IluVkSnA==} + /@vitest/expect@0.34.5: + resolution: {integrity: sha512-/3RBIV9XEH+nRpRMqDJBufKIOQaYUH2X6bt0rKSCW0MfKhXFLYsR5ivHifeajRSTsln0FwJbitxLKHSQz/Xwkw==} dependencies: - '@vitest/spy': 0.34.4 - '@vitest/utils': 0.34.4 + '@vitest/spy': 0.34.5 + '@vitest/utils': 0.34.5 chai: 4.3.8 dev: true - /@vitest/runner@0.34.4: - resolution: {integrity: sha512-hwwdB1StERqUls8oV8YcpmTIpVeJMe4WgYuDongVzixl5hlYLT2G8afhcdADeDeqCaAmZcSgLTLtqkjPQF7x+w==} + /@vitest/runner@0.34.5: + resolution: {integrity: sha512-RDEE3ViVvl7jFSCbnBRyYuu23XxmvRTSZWW6W4M7eC5dOsK75d5LIf6uhE5Fqf809DQ1+9ICZZNxhIolWHU4og==} dependencies: - '@vitest/utils': 0.34.4 + '@vitest/utils': 0.34.5 p-limit: 4.0.0 pathe: 1.1.1 dev: true - /@vitest/snapshot@0.34.4: - resolution: {integrity: sha512-GCsh4coc3YUSL/o+BPUo7lHQbzpdttTxL6f4q0jRx2qVGoYz/cyTRDJHbnwks6TILi6560bVWoBpYC10PuTLHw==} + /@vitest/snapshot@0.34.5: + resolution: {integrity: sha512-+ikwSbhu6z2yOdtKmk/aeoDZ9QPm2g/ZO5rXT58RR9Vmu/kB2MamyDSx77dctqdZfP3Diqv4mbc/yw2kPT8rmA==} dependencies: magic-string: 0.30.3 pathe: 1.1.1 pretty-format: 29.7.0 dev: true - /@vitest/spy@0.34.4: - resolution: {integrity: sha512-PNU+fd7DUPgA3Ya924b1qKuQkonAW6hL7YUjkON3wmBwSTIlhOSpy04SJ0NrRsEbrXgMMj6Morh04BMf8k+w0g==} + /@vitest/spy@0.34.5: + resolution: {integrity: sha512-epsicsfhvBjRjCMOC/3k00mP/TBGQy8/P0DxOFiWyLt55gnZ99dqCfCiAsKO17BWVjn4eZRIjKvcqNmSz8gvmg==} dependencies: tinyspy: 2.1.1 dev: true - /@vitest/utils@0.34.4: - resolution: {integrity: sha512-yR2+5CHhp/K4ySY0Qtd+CAL9f5Yh1aXrKfAT42bq6CtlGPh92jIDDDSg7ydlRow1CP+dys4TrOrbELOyNInHSg==} + /@vitest/utils@0.34.5: + resolution: {integrity: sha512-ur6CmmYQoeHMwmGb0v+qwkwN3yopZuZyf4xt1DBBSGBed8Hf9Gmbm/5dEWqgpLPdRx6Av6jcWXrjcKfkTzg/pw==} dependencies: diff-sequences: 29.6.3 loupe: 2.3.6 @@ -1345,6 +1369,18 @@ packages: fsevents: 2.3.3 dev: true + /ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + dev: true + + /clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -1646,14 +1682,14 @@ packages: - supports-color dev: true - /eslint-plugin-inclusive-language@2.2.0: - resolution: {integrity: sha512-RzPeSjuw1NYiTSQyFzYl2uTDgiPQWDUmFCiGAMCITmXN627DsWZ9rR4KNzrb8vnk/gLL5qdYr8oxh44xsrcO5Q==} + /eslint-plugin-inclusive-language@2.2.1: + resolution: {integrity: sha512-RL6avDWXCS0Dcp9axhvHRUp65qG07qjOrh6J4BNNahPvRY3PuYGnAd0H1strZ9cob79JiEW4Bq0j3gEuzbv0/A==} dependencies: humps: 2.0.1 dev: true - /eslint-plugin-jsdoc@46.8.1(eslint@8.49.0): - resolution: {integrity: sha512-uTce7IBluPKXIQMWJkIwFsI1gv7sZRmLjctca2K5DIxPi8fSBj9f4iru42XmGwuiMyH2f3nfc60sFmnSGv4Z/A==} + /eslint-plugin-jsdoc@46.8.2(eslint@8.49.0): + resolution: {integrity: sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ==} engines: {node: '>=16'} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1699,11 +1735,35 @@ packages: eslint: '>=0.8.0' dependencies: eslint: 8.49.0 - globals: 13.21.0 + globals: 13.22.0 hunspell-spellchecker: 1.0.2 lodash: 4.17.21 dev: true + /eslint-plugin-unicorn@48.0.1(eslint@8.49.0): + resolution: {integrity: sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==} + engines: {node: '>=16'} + peerDependencies: + eslint: '>=8.44.0' + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + ci-info: 3.8.0 + clean-regexp: 1.0.0 + eslint: 8.49.0 + esquery: 1.5.0 + indent-string: 4.0.0 + is-builtin-module: 3.2.1 + jsesc: 3.0.2 + lodash: 4.17.21 + pluralize: 8.0.0 + read-pkg-up: 7.0.1 + regexp-tree: 0.1.27 + regjsparser: 0.10.0 + semver: 7.5.4 + strip-indent: 3.0.0 + dev: true + /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1744,7 +1804,7 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.21.0 + globals: 13.22.0 graphemer: 1.4.0 ignore: 5.2.4 imurmurhash: 0.1.4 @@ -1877,6 +1937,14 @@ packages: to-regex-range: 5.0.1 dev: true + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1986,8 +2054,8 @@ packages: is-glob: 4.0.3 dev: true - /glob@10.3.4: - resolution: {integrity: sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==} + /glob@10.3.6: + resolution: {integrity: sha512-mEfImdc/fiYHEcF6pHFfD2b/KrdFB1qH9mRe5vI5HROF8G51SWxQJ2V56Ezl6ZL9y86gsxQ1Lgo2S746KGUPSQ==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true dependencies: @@ -2020,8 +2088,8 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals@13.21.0: - resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + /globals@13.22.0: + resolution: {integrity: sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -2145,6 +2213,11 @@ packages: engines: {node: '>=0.8.19'} dev: true + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -2401,6 +2474,10 @@ packages: engines: {node: '>=10'} dev: true + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -2413,6 +2490,17 @@ packages: engines: {node: '>=12.0.0'} dev: true + /jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + dev: true + /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: true @@ -2421,6 +2509,10 @@ packages: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} dev: true + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true @@ -2476,6 +2568,13 @@ packages: engines: {node: '>=14'} dev: true + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -2563,6 +2662,11 @@ packages: engines: {node: '>=12'} dev: true + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -2729,6 +2833,13 @@ packages: type-check: 0.4.0 dev: true + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2743,6 +2854,13 @@ packages: yocto-queue: 1.0.0 dev: true + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -2750,6 +2868,11 @@ packages: p-limit: 3.1.0 dev: true + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2765,6 +2888,16 @@ packages: json-parse-better-errors: 1.0.2 dev: true + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2859,6 +2992,11 @@ packages: resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} dev: true + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + /postcss-load-config@4.0.1: resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} @@ -2957,6 +3095,15 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true + /read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + /read-pkg@3.0.0: resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} engines: {node: '>=4'} @@ -2966,6 +3113,16 @@ packages: path-type: 3.0.0 dev: true + /read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.2 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2973,6 +3130,11 @@ packages: picomatch: 2.3.1 dev: true + /regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + dev: true + /regexp.prototype.flags@1.5.1: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} @@ -2982,6 +3144,13 @@ packages: set-function-name: 2.0.1 dev: true + /regjsparser@0.10.0: + resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3018,7 +3187,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - glob: 10.3.4 + glob: 10.3.6 dev: true /rollup@3.29.2: @@ -3277,6 +3446,13 @@ packages: engines: {node: '>=12'} dev: true + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3471,6 +3647,16 @@ packages: engines: {node: '>=10'} dev: true + /type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + /typed-array-buffer@1.0.0: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} engines: {node: '>= 0.4'} @@ -3555,8 +3741,8 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /vite-node@0.34.4(@types/node@20.6.3): - resolution: {integrity: sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==} + /vite-node@0.34.5(@types/node@20.6.4): + resolution: {integrity: sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: @@ -3565,7 +3751,7 @@ packages: mlly: 1.4.2 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.9(@types/node@20.6.3) + vite: 4.4.9(@types/node@20.6.4) transitivePeerDependencies: - '@types/node' - less @@ -3577,7 +3763,7 @@ packages: - terser dev: true - /vite@4.4.9(@types/node@20.6.3): + /vite@4.4.9(@types/node@20.6.4): resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -3605,7 +3791,7 @@ packages: terser: optional: true dependencies: - '@types/node': 20.6.3 + '@types/node': 20.6.4 esbuild: 0.18.20 postcss: 8.4.30 rollup: 3.29.2 @@ -3613,7 +3799,7 @@ packages: fsevents: 2.3.3 dev: true - /vitepress@1.0.0-rc.14(@types/node@20.6.3)(search-insights@2.8.2): + /vitepress@1.0.0-rc.14(@types/node@20.6.4)(search-insights@2.8.2): resolution: {integrity: sha512-yChIeXOAcNvVnSVjhziH1vte0uhKb00PuZf7KdIMfx3ixTMAz73Nn+6gREvCv0SdH+anteGUKz5eljv0ygcgGQ==} hasBin: true peerDependencies: @@ -3631,7 +3817,7 @@ packages: mark.js: 8.11.1 minisearch: 6.1.0 shiki: 0.14.4 - vite: 4.4.9(@types/node@20.6.3) + vite: 4.4.9(@types/node@20.6.4) vue: 3.3.4 transitivePeerDependencies: - '@algolia/client-search' @@ -3660,8 +3846,8 @@ packages: - universal-cookie dev: true - /vitest@0.34.4: - resolution: {integrity: sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==} + /vitest@0.34.5: + resolution: {integrity: sha512-CPI68mmnr2DThSB3frSuE5RLm9wo5wU4fbDrDwWQQB1CWgq9jQVoQwnQSzYAjdoBOPoH2UtXpOgHVge/uScfZg==} engines: {node: '>=v14.18.0'} hasBin: true peerDependencies: @@ -3693,12 +3879,12 @@ packages: dependencies: '@types/chai': 4.3.6 '@types/chai-subset': 1.3.3 - '@types/node': 20.6.3 - '@vitest/expect': 0.34.4 - '@vitest/runner': 0.34.4 - '@vitest/snapshot': 0.34.4 - '@vitest/spy': 0.34.4 - '@vitest/utils': 0.34.4 + '@types/node': 20.6.4 + '@vitest/expect': 0.34.5 + '@vitest/runner': 0.34.5 + '@vitest/snapshot': 0.34.5 + '@vitest/spy': 0.34.5 + '@vitest/utils': 0.34.5 acorn: 8.10.0 acorn-walk: 8.2.0 cac: 6.7.14 @@ -3712,8 +3898,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.7.0 - vite: 4.4.9(@types/node@20.6.3) - vite-node: 0.34.4(@types/node@20.6.3) + vite: 4.4.9(@types/node@20.6.4) + vite-node: 0.34.5(@types/node@20.6.4) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/src/logger.ts b/src/logger.ts index d66ad18f..f04a3c2f 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -118,10 +118,12 @@ export class Logger implements ILogger { message?: any, ...optionalParams: any[] ): void { - if (this.level !== LogLevel.OFF && this.level <= level) { - if (level !== LogLevel.OFF) { - this.logger[level](message, ...optionalParams); - } + if ( + this.level !== LogLevel.OFF && + this.level <= level && + level !== LogLevel.OFF + ) { + this.logger[level](message, ...optionalParams); } } } diff --git a/src/options/attribute-sorting/utils.ts b/src/options/attribute-sorting/utils.ts index 2db9cdc6..197586a5 100644 --- a/src/options/attribute-sorting/utils.ts +++ b/src/options/attribute-sorting/utils.ts @@ -109,7 +109,7 @@ export function stableSort( entries.sort((a, b) => { const order: CompareResult = compare(a[0], b[0]); // When order is 0, sort by index to make the sort stable - return order !== 0 ? order : a[1] - b[1]; + return order === 0 ? a[1] - b[1] : order; }); return entries.map(([value]) => value); } diff --git a/src/options/common.ts b/src/options/common.ts index cb2f7bd4..425b9124 100644 --- a/src/options/common.ts +++ b/src/options/common.ts @@ -8,7 +8,7 @@ export const PUG_PRINT_WIDTH_OPTION: IntSupportOption = { type: 'int', default: -1, description: 'The line length where Prettier will try wrap.', - range: { start: -1, end: Infinity, step: 1 }, + range: { start: -1, end: Number.POSITIVE_INFINITY, step: 1 }, }; /** Pug single quote option. */ @@ -48,7 +48,7 @@ export const PUG_TAB_WIDTH_OPTION: IntSupportOption = { type: 'int', default: -1, description: 'Number of spaces per indentation level.', - range: { start: -1, end: Infinity, step: 1 }, + range: { start: -1, end: Number.POSITIVE_INFINITY, step: 1 }, }; /** Pug use tabs option. */ diff --git a/src/options/converge.ts b/src/options/converge.ts index de3013a4..4a21cb29 100644 --- a/src/options/converge.ts +++ b/src/options/converge.ts @@ -15,12 +15,12 @@ export function convergeOptions( // Prettier base options printWidth: options.printWidth, pugPrintWidth: - options.pugPrintWidth !== -1 ? options.pugPrintWidth : options.printWidth, + options.pugPrintWidth === -1 ? options.printWidth : options.pugPrintWidth, singleQuote: options.singleQuote, pugSingleQuote: options.pugSingleQuote ?? options.singleQuote, tabWidth: options.tabWidth, pugTabWidth: - options.pugTabWidth !== -1 ? options.pugTabWidth : options.tabWidth, + options.pugTabWidth === -1 ? options.tabWidth : options.pugTabWidth, useTabs: options.useTabs ?? false, pugUseTabs: options.pugUseTabs ?? options.useTabs ?? false, bracketSpacing: options.bracketSpacing, diff --git a/src/options/empty-attributes/utils.ts b/src/options/empty-attributes/utils.ts index 9f8b2792..7b0832e0 100644 --- a/src/options/empty-attributes/utils.ts +++ b/src/options/empty-attributes/utils.ts @@ -4,7 +4,7 @@ import type { PugEmptyAttributesForceQuotes, } from './types'; -const EMPTY_VALUES: [boolean, string, string] = [true, '""', "''"]; +const EMPTY_VALUES: Set = new Set([true, '""', "''"]); /** * Formats the token's `val` if it's empty, based on the `pugEmptyAttributes` option. @@ -33,7 +33,7 @@ export function formatEmptyAttribute( return; } - if (pugEmptyAttributes === 'as-is' || !EMPTY_VALUES.includes(val)) { + if (pugEmptyAttributes === 'as-is' || !EMPTY_VALUES.has(val)) { return; } diff --git a/src/options/pug-attribute-separator.ts b/src/options/pug-attribute-separator.ts index 63789f39..d1a7f0de 100644 --- a/src/options/pug-attribute-separator.ts +++ b/src/options/pug-attribute-separator.ts @@ -44,8 +44,9 @@ export function resolvePugAttributeSeparatorOption( switch (pugAttributeSeparator) { case 'always': case 'as-needed': - case 'none': + case 'none': { return pugAttributeSeparator; + } } throw new Error( `Invalid option for pugAttributeSeparator. Found '${pugAttributeSeparator}'. Possible options: 'always', 'as-needed' or 'none'`, diff --git a/src/options/pug-comment-preserve-spaces.ts b/src/options/pug-comment-preserve-spaces.ts index 47271858..314326ba 100644 --- a/src/options/pug-comment-preserve-spaces.ts +++ b/src/options/pug-comment-preserve-spaces.ts @@ -55,20 +55,21 @@ export function formatPugCommentPreserveSpaces( ) { result += ' '; } - result += input.slice(firstNonSpace).trim().replace(/\s\s+/g, ' '); + result += input.slice(firstNonSpace).trim().replaceAll(/\s\s+/g, ' '); return result; } case 'trim-all': { let result: string = input.trim(); - result = result.replace(/\s\s+/g, ' '); + result = result.replaceAll(/\s\s+/g, ' '); if (!pipeless && input[0] === ' ') { result = ` ${result}`; } return result; } case 'keep-all': - default: + default: { // Don't touch comment return input; + } } } diff --git a/src/options/pug-wrap-attributes.ts b/src/options/pug-wrap-attributes.ts index d9d1a721..387df3be 100644 --- a/src/options/pug-wrap-attributes.ts +++ b/src/options/pug-wrap-attributes.ts @@ -9,7 +9,7 @@ export const PUG_WRAP_ATTRIBUTES_THRESHOLD: IntSupportOption = { default: -1, description: 'The maximum amount of attributes that an element can appear with on one line before it gets wrapped.', - range: { start: -1, end: Infinity, step: 1 }, + range: { start: -1, end: Number.POSITIVE_INFINITY, step: 1 }, }; /** Wrap attributes pattern. */ diff --git a/src/printer.ts b/src/printer.ts index 8962a69e..238ddbb3 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -245,9 +245,9 @@ export class PugPrinter { this.indentLevel++; } this.framework = - options.pugFramework !== 'auto' - ? options.pugFramework - : detectFramework(); + options.pugFramework === 'auto' + ? detectFramework() + : options.pugFramework; this.quotes = this.options.pugSingleQuote ? "'" : '"'; this.otherQuotes = this.options.pugSingleQuote ? '"' : "'"; @@ -288,12 +288,15 @@ export class PugPrinter { private get computedIndent(): string { switch (this.previousToken?.type) { case 'newline': - case 'outdent': + case 'outdent': { return this.indentString.repeat(this.indentLevel); - case 'indent': + } + case 'indent': { return this.indentString; - case 'start-pug-interpolation': + } + case 'start-pug-interpolation': { return ''; + } } return this.options.pugSingleFileComponentIndentation ? this.indentString @@ -333,7 +336,7 @@ export class PugPrinter { case 'class': case 'end-attributes': case 'id': - case 'eos': + case 'eos': { // TODO: These tokens write directly into the result this.result = results.join(''); await this[token.type]( @@ -343,16 +346,19 @@ export class PugPrinter { results.length = 0; results.push(this.result); break; + } case 'tag': case 'start-attributes': case 'interpolation': case 'call': - case ':': + case ':': { // TODO: These tokens read the length of the result this.result = results.join(''); + } // eslint-disable-next-line no-fallthrough default: { if (typeof this[token.type] !== 'function') { + // eslint-disable-next-line unicorn/prefer-type-error throw new Error('Unhandled token: ' + JSON.stringify(token)); } results.push( @@ -392,7 +398,7 @@ export class PugPrinter { private tokenNeedsSeparator(token: AttributeToken): boolean { return this.neverUseAttributeSeparator ? false - : this.alwaysUseAttributeSeparator || /^(\(|\[|:).*/.test(token.name); + : this.alwaysUseAttributeSeparator || /^([(:[]).*/.test(token.name); } private getUnformattedContentLines( @@ -463,14 +469,17 @@ export class PugPrinter { }; switch (this.framework) { - case 'angular': + case 'angular': { options.parser = '__ng_interpolation'; break; + } + case 'svelte': case 'vue': - default: + default: { options.parser = 'babel'; options.semi = false; + } } let result: string = await format(code, options); @@ -485,11 +494,77 @@ export class PugPrinter { while (text) { // Find double curly brackets const start: number = text.indexOf('{{'); - if (start !== -1) { + if (start === -1) { + // Find single curly brackets for svelte + const start2: number = text.indexOf('{'); + if (this.options.pugFramework === 'svelte' && start2 !== -1) { + result += text.slice(0, start2); + text = text.slice(start2 + 1); + const end2: number = text.indexOf('}'); + if (end2 === -1) { + result += '{'; + result += text; + text = ''; + } else { + let code: string = text.slice(0, end2); + try { + const dangerousQuoteCombination: boolean = + detectDangerousQuoteCombination( + code, + this.quotes, + this.otherQuotes, + logger, + ); + if (dangerousQuoteCombination) { + logger.warn( + 'The following expression could not be formatted correctly. Please try to fix it yourself and if there is a problem, please open a bug issue:', + code, + ); + result += handleBracketSpacing( + this.options.pugBracketSpacing, + code, + ); + text = text.slice(end2 + 1); + continue; + } else { + code = await this.frameworkFormat(code); + } + } catch (error: unknown) { + logger.warn('[PugPrinter:formatText]: ', error); + try { + code = await format(code, { + parser: 'babel', + ...this.codeInterpolationOptions, + semi: false, + }); + if (code[0] === ';') { + code = code.slice(1); + } + } catch (error: unknown) { + logger.warn(error); + } + } + code = unwrapLineFeeds(code); + result += handleBracketSpacing( + this.options.pugBracketSpacing, + code, + ['{', '}'], + ); + text = text.slice(end2 + 1); + } + } else { + result += text; + text = ''; + } + } else { result += text.slice(0, start); text = text.slice(start + 2); const end: number = text.indexOf('}}'); - if (end !== -1) { + if (end === -1) { + result += '{{'; + result += text; + text = ''; + } else { let code: string = text.slice(0, end); try { const dangerousQuoteCombination: boolean = @@ -572,72 +647,6 @@ export class PugPrinter { code = unwrapLineFeeds(code); result += handleBracketSpacing(this.options.pugBracketSpacing, code); text = text.slice(end + 2); - } else { - result += '{{'; - result += text; - text = ''; - } - } else { - // Find single curly brackets for svelte - const start2: number = text.indexOf('{'); - if (this.options.pugFramework === 'svelte' && start2 !== -1) { - result += text.slice(0, start2); - text = text.slice(start2 + 1); - const end2: number = text.indexOf('}'); - if (end2 !== -1) { - let code: string = text.slice(0, end2); - try { - const dangerousQuoteCombination: boolean = - detectDangerousQuoteCombination( - code, - this.quotes, - this.otherQuotes, - logger, - ); - if (dangerousQuoteCombination) { - logger.warn( - 'The following expression could not be formatted correctly. Please try to fix it yourself and if there is a problem, please open a bug issue:', - code, - ); - result += handleBracketSpacing( - this.options.pugBracketSpacing, - code, - ); - text = text.slice(end2 + 1); - continue; - } else { - code = await this.frameworkFormat(code); - } - } catch (error: unknown) { - logger.warn('[PugPrinter:formatText]: ', error); - try { - code = await format(code, { - parser: 'babel', - ...this.codeInterpolationOptions, - semi: false, - }); - if (code[0] === ';') { - code = code.slice(1); - } - } catch (error: unknown) { - logger.warn(error); - } - } - code = unwrapLineFeeds(code); - result += handleBracketSpacing( - this.options.pugBracketSpacing, - code, - ['{', '}'], - ); - text = text.slice(end2 + 1); - } else { - result += '{'; - result += text; - text = ''; - } - } else { - result += text; - text = ''; } } } @@ -659,13 +668,12 @@ export class PugPrinter { val = val.slice(1, -1); // Remove quotes } val = await format(val, { parser, ...options }); - if (this.quotes === '"') { - val = val.replace(/"/g, '\\"'); - } else { - val = val.replace(/'/g, "\\'"); - } + val = + this.quotes === '"' + ? val.replaceAll('"', '\\"') + : val.replaceAll("'", "\\'"); val = unwrapLineFeeds(val); - if (trimTrailingSemicolon && val[val.length - 1] === ';') { + if (trimTrailingSemicolon && val.at(-1) === ';') { val = val.slice(0, -1); } if (trimLeadingSemicolon && val[0] === ';') { @@ -725,7 +733,7 @@ export class PugPrinter { }; try { val = await format(val, { parser, ...options }); - } catch (error) { + } catch { logger.warn( 'The following expression could not be formatted correctly. Please try to fix it yourself and if there is a problem, please open a bug issue:', val, @@ -868,15 +876,13 @@ export class PugPrinter { | EndAttributesToken; } logger.debug('after token', this.currentLineLength); - if (hasLiteralAttributes) { - // Remove div as it will be replaced with the literal for id and/or class - if ( - this.previousToken?.type === 'tag' && - this.previousToken.val === 'div' && - !this.options.pugExplicitDiv - ) { - this.currentLineLength -= 3; - } + if ( + hasLiteralAttributes && // Remove div as it will be replaced with the literal for id and/or class + this.previousToken?.type === 'tag' && + this.previousToken.val === 'div' && + !this.options.pugExplicitDiv + ) { + this.currentLineLength -= 3; } if (numNormalAttributes > 0) { // Add leading and trailing parentheses @@ -948,81 +954,83 @@ export class PugPrinter { this.options.pugEmptyAttributesForceQuotes, ); - if (typeof token.val === 'string') { - if (isQuoted(token.val) && token.val[0] !== '`') { - if ( - token.name === 'class' && - this.options.pugClassNotation === 'literal' - ) { - // Handle class attribute - const val: string = token.val.slice(1, -1).trim(); - const classes: string[] = val.split(/\s+/); - const specialClasses: string[] = []; - const normalClasses: string[] = []; - const validClassNameRegex: RegExp = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/; - for (const className of classes) { - if (!validClassNameRegex.test(className)) { - specialClasses.push(className); + if ( + typeof token.val === 'string' && + isQuoted(token.val) && + token.val[0] !== '`' + ) { + if ( + token.name === 'class' && + this.options.pugClassNotation === 'literal' + ) { + // Handle class attribute + const val: string = token.val.slice(1, -1).trim(); + const classes: string[] = val.split(/\s+/); + const specialClasses: string[] = []; + const normalClasses: string[] = []; + const validClassNameRegex: RegExp = /^-?[A-Z_a-z]+[\w-]*$/; + for (const className of classes) { + if (validClassNameRegex.test(className)) { + if (this.options.pugClassLocation === 'after-attributes') { + this.classLiteralAfterAttributes.push(className); } else { - if (this.options.pugClassLocation === 'after-attributes') { - this.classLiteralAfterAttributes.push(className); - } else { - normalClasses.push(className); - } + normalClasses.push(className); } - } - if (normalClasses.length > 0) { - // Write css-class in front of attributes - const position: number = this.possibleClassPosition; - this.result = [ - this.result.slice(0, position), - '.', - normalClasses.join('.'), - this.result.slice(position), - ].join(''); - this.possibleClassPosition += 1 + normalClasses.join('.').length; - if (this.options.pugClassLocation === 'before-attributes') { - this.replaceTagWithLiteralIfPossible(/div\./, '.'); - } - } - if (specialClasses.length > 0) { - token.val = makeString(specialClasses.join(' '), this.quotes); - this.previousAttributeRemapped = false; } else { - this.previousAttributeRemapped = true; - return; + specialClasses.push(className); } - } else if ( - token.name === 'id' && - this.options.pugIdNotation !== 'as-is' - ) { - // Handle id attribute - let val: string = token.val; - val = val.slice(1, -1); - val = val.trim(); - const validIdNameRegex: RegExp = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/; - if (!validIdNameRegex.test(val)) { - val = makeString(val, this.quotes); - this.result += 'id'; - if (token.mustEscape === false) { - this.result += '!'; - } - this.result += `=${val}`; - return; - } - // Write css-id in front of css-classes - const position: number = this.possibleIdPosition; - const literal: string = `#${val}`; + } + if (normalClasses.length > 0) { + // Write css-class in front of attributes + const position: number = this.possibleClassPosition; this.result = [ this.result.slice(0, position), - literal, + '.', + normalClasses.join('.'), this.result.slice(position), ].join(''); - this.possibleClassPosition += literal.length; - this.replaceTagWithLiteralIfPossible(/div#/, '#'); + this.possibleClassPosition += 1 + normalClasses.join('.').length; + if (this.options.pugClassLocation === 'before-attributes') { + this.replaceTagWithLiteralIfPossible(/div\./, '.'); + } + } + if (specialClasses.length > 0) { + token.val = makeString(specialClasses.join(' '), this.quotes); + this.previousAttributeRemapped = false; + } else { this.previousAttributeRemapped = true; return; } + } else if ( + token.name === 'id' && + this.options.pugIdNotation !== 'as-is' + ) { + // Handle id attribute + let val: string = token.val; + val = val.slice(1, -1); + val = val.trim(); + const validIdNameRegex: RegExp = /^-?[A-Z_a-z]+[\w-]*$/; + if (!validIdNameRegex.test(val)) { + val = makeString(val, this.quotes); + this.result += 'id'; + if (token.mustEscape === false) { + this.result += '!'; + } + this.result += `=${val}`; + return; + } + // Write css-id in front of css-classes + const position: number = this.possibleIdPosition; + const literal: string = `#${val}`; + this.result = [ + this.result.slice(0, position), + literal, + this.result.slice(position), + ].join(''); + this.possibleClassPosition += literal.length; + this.replaceTagWithLiteralIfPossible(/div#/, '#'); + this.previousAttributeRemapped = true; + return; } } @@ -1060,7 +1068,7 @@ export class PugPrinter { : token.val; const classes: string[] = val.split(/\s+/); - if (this.classLiteralToAttribute.length) { + if (this.classLiteralToAttribute.length > 0) { for ( let i: number = this.classLiteralToAttribute.length - 1; i > -1; @@ -1140,7 +1148,7 @@ export class PugPrinter { } else { // The value is not quoted and may be js-code val = val.trim(); - val = val.replace(/\s\s+/g, ' '); + val = val.replaceAll(/\s\s+/g, ' '); if (val[0] === '{' && val[1] === ' ') { val = `{${val.slice(2, val.length)}`; } @@ -1156,7 +1164,7 @@ export class PugPrinter { } private ['end-attributes'](token: EndAttributesToken): void { - if (this.wrapAttributes && this.result[this.result.length - 1] !== '(') { + if (this.wrapAttributes && this.result.at(-1) !== '(') { if (!this.options.pugBracketSameLine) { this.result += '\n'; } @@ -1164,7 +1172,7 @@ export class PugPrinter { } this.wrapAttributes = false; - if (this.classLiteralToAttribute.length) { + if (this.classLiteralToAttribute.length > 0) { if (this.previousToken?.type === 'start-attributes') { this.result += '('; } else if (this.previousToken?.type === 'attribute') { @@ -1182,7 +1190,7 @@ export class PugPrinter { } } - if (this.result[this.result.length - 1] === '(') { + if (this.result.at(-1) === '(') { // There were no attributes this.result = this.result.slice(0, -1); } else if (this.previousToken?.type === 'attribute') { @@ -1197,7 +1205,7 @@ export class PugPrinter { this.result += '()'; } if ( - this.result[this.result.length - 1] === ')' && + this.result.at(-1) === ')' && this.classLiteralAfterAttributes.length > 0 ) { const classes: string[] = this.classLiteralAfterAttributes.splice( @@ -1390,7 +1398,7 @@ export class PugPrinter { private eos(token: EosToken): void { // Remove all newlines at the end - while (this.result[this.result.length - 1] === '\n') { + while (this.result.at(-1) === '\n') { this.result = this.result.slice(0, -1); } // Insert one newline @@ -1402,7 +1410,7 @@ export class PugPrinter { // See if this is a `//- prettier-ignore` comment, which would indicate that the part of the template // that follows should be left unformatted. Support the same format as typescript-eslint is using for descriptions: // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-ts-comment.md#allow-with-description - if (/^ prettier-ignore($|[: ])/.test(commentToken.val)) { + if (/^ prettier-ignore($|[ :])/.test(commentToken.val)) { // Use a separate token processing loop to find the end of the stream of tokens to be ignored by formatting, // and uses their `loc` properties to retrieve the original pug code to be used instead. let token: Token | null = this.getNextToken(); @@ -1418,7 +1426,9 @@ export class PugPrinter { } else { break; } - } else if (type === 'indent') { + } + // eslint-disable-next-line unicorn/prefer-switch + else if (type === 'indent') { ignoreLevel++; } else if (type === 'outdent') { ignoreLevel--; @@ -1499,19 +1509,20 @@ export class PugPrinter { let val: string = token.val; let needsTrailingWhitespace: boolean = false; - let endsWithWhitespace: boolean = - val[val.length - 1] === ' ' && !/^\s+$/.test(val); + let endsWithWhitespace: boolean = val.at(-1) === ' ' && !/^\s+$/.test(val); if (this.pipelessText) { switch (this.previousToken?.type) { - case 'newline': + case 'newline': { if (val.trim().length > 0) { result += this.indentString.repeat(this.indentLevel + 1); } break; - case 'start-pipeless-text': + } + case 'start-pipeless-text': { result += this.indentString; break; + } } if (this.pipelessComment) { @@ -1525,16 +1536,17 @@ export class PugPrinter { if (this.nextToken && endsWithWhitespace) { switch (this.nextToken.type) { case 'interpolated-code': - case 'start-pug-interpolation': + case 'start-pug-interpolation': { needsTrailingWhitespace = true; break; + } } } - val = val.replace(/\s\s+/g, ' '); + val = val.replaceAll(/\s\s+/g, ' '); switch (this.previousToken?.type) { - case 'newline': + case 'newline': { result += this.indentString.repeat(this.indentLevel); if (/^ .+$/.test(val)) { result += '|\n'; @@ -1548,8 +1560,9 @@ export class PugPrinter { result += ' '; } break; + } case 'indent': - case 'outdent': + case 'outdent': { result += this.computedIndent; if (/^ .+$/.test(val)) { result += '|\n'; @@ -1560,19 +1573,21 @@ export class PugPrinter { result += ' '; } break; + } case 'interpolated-code': - case 'end-pug-interpolation': + case 'end-pug-interpolation': { if (/^ .+$/.test(val) || val === ' ') { result += ' '; } else if (/^.+ $/.test(val)) { needsTrailingWhitespace = true; } break; + } } val = val.trim(); val = await this.formatText(val); - val = val.replace(/#(\{|\[)/g, '\\#$1'); + val = val.replaceAll(/#([[{])/g, '\\#$1'); } if ( @@ -1610,18 +1625,21 @@ export class PugPrinter { case 'tag': case 'class': case 'id': - case 'end-attributes': + case 'end-attributes': { result = ' '; break; - case 'start-pug-interpolation': + } + case 'start-pug-interpolation': { result = '| '; break; + } case 'indent': case 'newline': - case 'outdent': + case 'outdent': { result = this.computedIndent; result += this.pipelessText ? this.indentString : '| '; break; + } } result += token.mustEscape ? '#' : '!'; result += handleBracketSpacing( @@ -1709,16 +1727,19 @@ export class PugPrinter { let parser: BuiltInParserName | undefined; switch (lastTagToken?.val) { - case 'script': + case 'script': { parser = getScriptParserName( previousTypeAttributeToken(this.tokens, this.currentIndex), ); break; - case 'style': + } + case 'style': { parser = 'css'; break; - default: + } + default: { break; + } } if (parser) { @@ -1728,24 +1749,28 @@ export class PugPrinter { let usedInterpolatedCode: boolean = false; while (tok && tok?.type !== 'end-pipeless-text') { switch (tok.type) { - case 'text': + case 'text': { rawText += tok.val; break; - case 'newline': + } + case 'newline': { rawText += '\n'; break; - case 'interpolated-code': + } + case 'interpolated-code': { usedInterpolatedCode = true; rawText += tok.mustEscape ? '#' : '!'; rawText += `{${tok.val}}`; break; - default: + } + default: { logger.warn( '[PugPrinter:start-pipeless-text]:', 'Unhandled token for pipeless script tag:', JSON.stringify(tok), ); break; + } } index++; @@ -1772,10 +1797,9 @@ export class PugPrinter { 'If you think this is a bug, please open a bug issue.', ]; - warningContext.push(`\ncode: \`${rawText.trim()}\``); - // TODO: If other token types occur use `if (usedInterpolatedCode)` warningContext.push( + `\ncode: \`${rawText.trim()}\``, '\nYou used interpolated code in your pipeless script tag, so you may ignore this warning.', ); @@ -1904,7 +1928,7 @@ export class PugPrinter { let args: string | null = token.args; if (args) { args = args.trim(); - args = args.replace(/\s\s+/g, ' '); + args = args.replaceAll(/\s\s+/g, ' '); result += `(${args})`; } this.currentLineLength += result.length; @@ -1918,7 +1942,7 @@ export class PugPrinter { let args: string | null = token.args; if (args) { args = args.trim(); - args = args.replace(/\s\s+/g, ' '); + args = args.replaceAll(/\s\s+/g, ' '); result += `(${args})`; } return result; @@ -1928,7 +1952,7 @@ export class PugPrinter { let result: string = this.computedIndent; const match: RegExpExecArray | null = /^!\((.*)\)$/.exec(token.val); logger.debug('[PugPrinter]:', match); - result += !match ? `if ${token.val}` : `unless ${match[1]}`; + result += match ? `unless ${match[1]}` : `if ${token.val}`; return result; } diff --git a/src/utils/angular.ts b/src/utils/angular.ts index 7de521bf..7e029725 100644 --- a/src/utils/angular.ts +++ b/src/utils/angular.ts @@ -18,7 +18,7 @@ import { isQuoted, isWrappedWith } from './common'; * @returns `true` if `name` passes the angular binding check, otherwise `false`. */ export function isAngularBinding(name: string): boolean { - return name.length >= 3 && name[0] === '[' && name[name.length - 1] === ']'; + return name.length >= 3 && name[0] === '[' && name.at(-1) === ']'; } /** @@ -39,7 +39,7 @@ export function isAngularBinding(name: string): boolean { * @returns `true` if `name` passes the angular action check, otherwise `false`. */ export function isAngularAction(name: string): boolean { - return name.length >= 3 && name[0] === '(' && name[name.length - 1] === ')'; + return name.length >= 3 && name[0] === '(' && name.at(-1) === ')'; } /** diff --git a/src/utils/common.ts b/src/utils/common.ts index 01f5e9f1..4e25ba8c 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -41,10 +41,12 @@ export function previousNormalAttributeToken( if (!token || token.type === 'start-attributes') { return; } - if (token.type === 'attribute') { - if (token.name !== 'class' && token.name !== 'id') { - return token; - } + if ( + token.type === 'attribute' && + token.name !== 'class' && + token.name !== 'id' + ) { + return token; } } return; @@ -66,10 +68,8 @@ export function previousTypeAttributeToken( if (!token || token.type === 'start-attributes' || token.type === 'tag') { return; } - if (token.type === 'attribute') { - if (token.name === 'type') { - return token; - } + if (token.type === 'attribute' && token.name === 'type') { + return token; } } return; @@ -196,7 +196,7 @@ export function isQuoted(val: string): boolean { * @returns `true` if it's a single line interpolation, otherwise `false`. */ export function isSingleLineWithInterpolation(val: string): boolean { - return /^`[\s\S]*`$/.test(val) && val.includes('${'); + return /^`[\S\s]*`$/.test(val) && val.includes('${'); } /** @@ -206,7 +206,7 @@ export function isSingleLineWithInterpolation(val: string): boolean { * @returns `true` if it's a multiline interpolation, otherwise `false`. */ export function isMultilineInterpolation(val: string): boolean { - return /^`[\s\S]*`$/m.test(val) && val.includes('\n'); + return /^`[\S\s]*`$/m.test(val) && val.includes('\n'); } /** @@ -244,8 +244,8 @@ export function makeString( unescapeUnnecessaryEscapes: boolean = false, ): string { const otherQuote: "'" | '"' = enclosingQuote === '"' ? "'" : '"'; - const newContent: string = rawContent.replace( - /\\([\s\S])|(['"])/g, + const newContent: string = rawContent.replaceAll( + /\\([\S\s])|(["'])/g, (match, escaped: "'" | '"', quote: "'" | '"') => { if (escaped === otherQuote) { return escaped; @@ -257,7 +257,7 @@ export function makeString( return quote; } return unescapeUnnecessaryEscapes && - /^[^\\nrvtbfux\r\n\u2028\u2029"'0-7]$/.test(escaped) + /^[^\n\r"'0-7\\bfnrt-vx\u2028\u2029]$/.test(escaped) ? escaped : `\\${escaped}`; }, diff --git a/src/utils/script-mime-types.ts b/src/utils/script-mime-types.ts index 503d4d78..aed03db8 100644 --- a/src/utils/script-mime-types.ts +++ b/src/utils/script-mime-types.ts @@ -4,7 +4,7 @@ import type { AttributeToken } from 'pug-lexer'; // NOTE: XML would be useful, but it's not a default parser. // YAML is not official, but it costs nothing to support it right now. const jsonSuffixRe: RegExp = /\+(json|yaml)$/i; -const wrappingQuotesRe: RegExp = /(^("|'|`))|(("|'|`)$)/g; +const wrappingQuotesRe: RegExp = /(^(["'`]))|((["'`])$)/g; // Matches IANA media types to the required parser for them // https://iana.org/assignments/media-types/media-types.xhtml @@ -42,7 +42,7 @@ export function getScriptParserName( return; } - const type: string = typeRaw.replace(wrappingQuotesRe, ''); + const type: string = typeRaw.replaceAll(wrappingQuotesRe, ''); // Empty type is equivalent to omission if (!type) { diff --git a/tests/options/sortAttributes/sort-attributes.test.ts b/tests/options/sortAttributes/sort-attributes.test.ts index 7a800bf9..2d85021f 100644 --- a/tests/options/sortAttributes/sort-attributes.test.ts +++ b/tests/options/sortAttributes/sort-attributes.test.ts @@ -247,9 +247,9 @@ describe('Options', () => { const pugSortAttributesBeginning: string[] = ['a']; const pugSortAttributesEnd: string[] = ['b']; // eslint-disable-next-line spellcheck/spell-checker - const expected: string[] = 'aedcfghilnb'.split(''); + const expected: string[] = [...'aedcfghilnb']; // eslint-disable-next-line spellcheck/spell-checker - const code: string[] = 'aedcfbghiln'.split(''); + const code: string[] = [...'aedcfbghiln']; const actual: string[] = stableSort(code, (a, b) => compareAttributeToken( createAttributeToken(a), @@ -268,9 +268,9 @@ describe('Options', () => { const pugSortAttributesBeginning: string[] = []; const pugSortAttributesEnd: string[] = []; // eslint-disable-next-line spellcheck/spell-checker - const expected: string[] = 'aedcfghilnb'.split(''); + const expected: string[] = [...'aedcfghilnb']; // eslint-disable-next-line spellcheck/spell-checker - const code: string[] = 'aedcfghilnb'.split(''); + const code: string[] = [...'aedcfghilnb']; const actual: string[] = stableSort(code, (a, b) => compareAttributeToken( createAttributeToken(a), diff --git a/tests/pug-tests/pug.test.ts b/tests/pug-tests/pug.test.ts index 2fd74371..2ec6cb8d 100644 --- a/tests/pug-tests/pug.test.ts +++ b/tests/pug-tests/pug.test.ts @@ -10,7 +10,7 @@ const __dirname: string = fileURLToPath(new URL('.', import.meta.url)); describe('Pug Tests', () => { const filenames: string[] = readdirSync(resolve(__dirname), 'utf8'); - const ignores: string[] = [ + const ignores: Set = new Set([ 'attrs.pug', 'attrs.js.pug', 'comments.pug', @@ -27,12 +27,12 @@ describe('Pug Tests', () => { 'tags.self-closing.pug', 'template.pug', 'text.pug', - ]; + ]); for (const filename of filenames) { if (filename.endsWith('.formatted.pug')) { const unformattedFilename: string = filename.replace('.formatted', ''); - if (!ignores.includes(unformattedFilename)) { + if (!ignores.has(unformattedFilename)) { it(unformattedFilename, async () => { const expected: string = readFileSync( resolve(__dirname, filename),