diff --git a/.gitignore b/.gitignore index 97ade93..da4358b 100644 --- a/.gitignore +++ b/.gitignore @@ -132,4 +132,5 @@ dist #project files MyAwesomeReport.html *.iml -*.idea \ No newline at end of file +*.idea +target/** \ No newline at end of file diff --git a/package.json b/package.json index 9e3d147..6f31ee2 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,8 @@ "fs-extra": "=8.1.0", "globals": "=15.3.0", "globby": "=10.0.1", + "jscpd": "=4.0.4", + "jsinspect": "^0.12.7", "lodash": "=4.17.21", "madge": "=7.0.0", "typhonjs-escomplex": "=0.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d061fa4..ea0dc1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,12 @@ importers: globby: specifier: '=10.0.1' version: 10.0.1 + jscpd: + specifier: '=4.0.4' + version: 4.0.4 + jsinspect: + specifier: ^0.12.7 + version: 0.12.7 lodash: specifier: '=4.17.21' version: 4.17.21 @@ -645,6 +651,10 @@ packages: resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==} engines: {node: '>=6.9.0'} + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} @@ -703,6 +713,18 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jscpd/core@4.0.1': + resolution: {integrity: sha512-6Migc68Z8p7q5xqW1wbF3SfIbYHPQoiLHPbJb1A1Z1H9DwImwopFkYflqRDpuamLd0Jfg2jx3ZBmHQt21NbD1g==} + + '@jscpd/finder@4.0.1': + resolution: {integrity: sha512-TcCT28686GeLl87EUmrBXYmuOFELVMDwyjKkcId+qjNS1zVWRd53Xd5xKwEDzkCEgen/vCs+lorLLToolXp5oQ==} + + '@jscpd/html-reporter@4.0.1': + resolution: {integrity: sha512-M9fFETNvXXuy4fWv0M2oMluxwrQUBtubxCHaWw21lb2G8A6SE19moe3dUkluZ/3V4BccywfeF9lSEUg84heLww==} + + '@jscpd/tokenizer@4.0.1': + resolution: {integrity: sha512-l/CPeEigadYcQUsUxf1wdCBfNjyAxYcQU04KciFNmSZAMY+ykJ8fZsiuyfjb+oOuDgsIPZZ9YvbvsCr6NBXueg==} + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} @@ -727,6 +749,9 @@ packages: '@types/node@20.12.12': resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} + '@types/sarif@2.1.7': + resolution: {integrity: sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==} + '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} @@ -755,6 +780,11 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} @@ -788,6 +818,12 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + assert-never@1.2.1: + resolution: {integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==} + ast-module-types@5.0.0: resolution: {integrity: sha512-JvqziE0Wc0rXQfma0HZC/aY7URXHFuZV84fJRtP8u+lhp0JYCNd5wJzVXP45t0PH0Mej3ynlzvdyITYIu0G4LQ==} engines: {node: '>=14'} @@ -813,6 +849,14 @@ packages: babel-runtime@6.26.0: resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==} + babel-walk@3.0.0-canary-5: + resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} + engines: {node: '>= 10.0.0'} + + babylon@6.16.1: + resolution: {integrity: sha512-WBQnJspthcRVkTCm9VPlUf6J38UZf5Sp+eV1IJoWTqZAHJBaKCuFP93fa6i/NcUhdiunFimemWn6/zDHZtUjLA==} + hasBin: true + backbone-esnext-events@0.3.5: resolution: {integrity: sha512-n208qnhO6kARjSHLZIBs1w6ECpybIBzFE6X1x2XTIGx+U+6qlSctizq9bCh+Xf5XHsSyipIgoxc1grZG7nCxfw==} @@ -825,6 +869,10 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + blamer@1.0.6: + resolution: {integrity: sha512-fv7QToPS87oD1m1bDDTf29zC/bVKJxj2Nqh1r/v4NhMtbnzDIbWOHBYIfxCjlmkVGu3FGOjKgdNG3SFm7TkvBQ==} + engines: {node: '>=8.9'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -840,6 +888,14 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -855,6 +911,9 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-parser@2.2.0: + resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -863,6 +922,10 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -886,6 +949,10 @@ packages: color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + colorspace@1.1.4: resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} @@ -896,6 +963,10 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -906,6 +977,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + constantinople@4.0.1: + resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} + convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -939,6 +1013,10 @@ packages: defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependency-tree@10.0.9: resolution: {integrity: sha512-dwc59FRIsht+HfnTVM0BCjJaEWxdq2YAvEDy4/Hn6CwS3CBWMtFnL3aZGAkQn3XCYxk/YcTDE4jX2Q7bFTwCjA==} engines: {node: '>=14'} @@ -981,16 +1059,33 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + doctypes@1.1.0: + resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==} + electron-to-chromium@1.4.783: resolution: {integrity: sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + enhanced-resolve@5.16.1: resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==} engines: {node: '>=10.13.0'} + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -1074,6 +1169,13 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1100,6 +1202,9 @@ packages: file-stream-rotator@0.6.1: resolution: {integrity: sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==} + filepaths@0.3.0: + resolution: {integrity: sha512-QFAYdzHZxWfBOHtHIlZySPAej+pxz6c2TGe8LGgHQNsgxHmcfbbQfNmsIh0kaangjL+6D6g8IoR6VDnOFrLEFw==} + filing-cabinet@4.2.0: resolution: {integrity: sha512-YZ21ryzRcyqxpyKggdYSoXx//d3sCJzM3lsYoaeg/FyXdADGJrUl+BW1KIglaVLJN5BBcMtWylkygY8zBp2MrQ==} engines: {node: '>=14'} @@ -1123,6 +1228,14 @@ packages: fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -1141,9 +1254,25 @@ packages: resolution: {integrity: sha512-jb65zDeHyDjFR1loOVk0HQGM5WNwoGB8aLWy3LKCieMKol0/ProHkhO2X1JxojuN10vbz1qNn09MJ7tNp7qMzw==} engines: {node: '>=14'} + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + get-own-enumerable-property-symbols@3.0.2: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + get-stdin@4.0.1: + resolution: {integrity: sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==} + engines: {node: '>=0.10.0'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + gitignore-to-glob@0.3.0: + resolution: {integrity: sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==} + engines: {node: '>=4.4 <5 || >=6.9'} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1181,6 +1310,9 @@ packages: engines: {node: '>=0.6.0'} hasBin: true + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1195,10 +1327,29 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + human-signals@1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1230,10 +1381,17 @@ packages: is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-expression@4.0.0: + resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1254,6 +1412,13 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + is-regexp@1.0.0: resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} engines: {node: '>=0.10.0'} @@ -1279,6 +1444,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + js-stringify@1.0.2: + resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1286,6 +1454,13 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jscpd-sarif-reporter@4.0.3: + resolution: {integrity: sha512-0T7KiWiDIVArvlBkvCorn2NFwQe7p7DJ37o4YFRuPLDpcr1jNHQlEfbFPw8hDdgJ4hpfby6A5YwyHqASKJ7drA==} + + jscpd@4.0.4: + resolution: {integrity: sha512-tmcB7uQPYzdIwc03Z7ngWCD3vrJ96B88kaAh86f9dQ7dz1Cikj29t9Lu8kzFf1NIyhdm1MMP8HHIAXUx0L9EhQ==} + hasBin: true + jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -1295,6 +1470,10 @@ packages: engines: {node: '>=4'} hasBin: true + jsinspect@0.12.7: + resolution: {integrity: sha512-9pLr5r5moX3XhACEg/nhIlprBuqRDT+loYigZo7hidmfOj0EV2l6ZMk6gmaNMiX6o1YCMod1lWSH3JoX80QHLA==} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1312,6 +1491,12 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jstransformer@1.0.0: + resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1356,6 +1541,12 @@ packages: typescript: optional: true + markdown-table@2.0.0: + resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1404,6 +1595,10 @@ packages: node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-sarif-builder@2.0.3: + resolution: {integrity: sha512-Pzr3rol8fvhG/oJjIq2NTVB0vmdNNlz22FENhhPojYRZ4/ee08CfK4YuKmuL54V9MLhI1kpzxfOJ/63LzmZzDg==} + engines: {node: '>=14'} + node-source-walk@6.0.2: resolution: {integrity: sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==} engines: {node: '>=14'} @@ -1412,6 +1607,14 @@ packages: resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} engines: {node: '>=0.10.0'} + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -1503,6 +1706,48 @@ packages: resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} engines: {node: '>=10'} + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + + pug-attrs@3.0.0: + resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==} + + pug-code-gen@3.0.3: + resolution: {integrity: sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==} + + pug-error@2.1.0: + resolution: {integrity: sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==} + + pug-filters@4.0.0: + resolution: {integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==} + + pug-lexer@5.0.1: + resolution: {integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==} + + pug-linker@4.0.0: + resolution: {integrity: sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==} + + pug-load@3.0.0: + resolution: {integrity: sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==} + + pug-parser@6.0.0: + resolution: {integrity: sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==} + + pug-runtime@3.0.1: + resolution: {integrity: sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==} + + pug-strip-comments@2.0.0: + resolution: {integrity: sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==} + + pug-walk@2.0.0: + resolution: {integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==} + + pug@3.0.3: + resolution: {integrity: sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==} + + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1548,6 +1793,13 @@ packages: remove-trailing-separator@1.1.0: resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + + reprism@0.0.11: + resolution: {integrity: sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==} + requirejs-config-file@4.0.0: resolution: {integrity: sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==} engines: {node: '>=10.13.0'} @@ -1601,6 +1853,10 @@ packages: engines: {node: '>=10'} hasBin: true + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1627,12 +1883,23 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + spark-md5@3.0.2: + resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + stack-trace@0.0.10: resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} stream-to-array@2.3.0: resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -1648,6 +1915,20 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-indent@1.0.1: + resolution: {integrity: sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==} + engines: {node: '>=0.10.0'} + hasBin: true + + strip-json-comments@1.0.2: + resolution: {integrity: sha512-zRzQSDu8YVDoLt1cVFfznCS6Y0wxWphJiG6+2d9YLZQJKooieRoVa518RFGK/P7m9ZsNyDEiknOJkdPQu7exVw==} + engines: {node: '>=0.8.0'} + hasBin: true + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -1691,6 +1972,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + token-stream@1.0.0: + resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==} + triple-beam@1.4.1: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} @@ -1765,6 +2049,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + unixify@1.0.0: resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} engines: {node: '>=0.10.0'} @@ -1781,6 +2069,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + walkdir@0.4.1: resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} engines: {node: '>=6.0.0'} @@ -1807,6 +2099,10 @@ packages: resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} engines: {node: '>= 12.0.0'} + with@7.0.2: + resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} + engines: {node: '>= 10.0.0'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -2557,6 +2853,9 @@ snapshots: '@babel/helper-validator-identifier': 7.24.6 to-fast-properties: 2.0.0 + '@colors/colors@1.5.0': + optional: true + '@colors/colors@1.6.0': {} '@dabh/diagnostics@2.0.3': @@ -2622,6 +2921,35 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@jscpd/core@4.0.1': + dependencies: + eventemitter3: 5.0.1 + + '@jscpd/finder@4.0.1': + dependencies: + '@jscpd/core': 4.0.1 + '@jscpd/tokenizer': 4.0.1 + blamer: 1.0.6 + bytes: 3.1.2 + cli-table3: 0.6.5 + colors: 1.4.0 + fast-glob: 3.3.2 + fs-extra: 11.2.0 + markdown-table: 2.0.0 + pug: 3.0.3 + + '@jscpd/html-reporter@4.0.1': + dependencies: + colors: 1.4.0 + fs-extra: 11.2.0 + pug: 3.0.3 + + '@jscpd/tokenizer@4.0.1': + dependencies: + '@jscpd/core': 4.0.1 + reprism: 0.0.11 + spark-md5: 3.0.2 + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': dependencies: eslint-scope: 5.1.1 @@ -2649,6 +2977,8 @@ snapshots: dependencies: undici-types: 5.26.5 + '@types/sarif@2.1.7': {} + '@types/triple-beam@1.3.5': {} '@typescript-eslint/types@5.62.0': {} @@ -2681,6 +3011,8 @@ snapshots: dependencies: acorn: 8.11.3 + acorn@7.4.1: {} + acorn@8.11.3: {} ajv@6.12.6: @@ -2708,6 +3040,10 @@ snapshots: array-union@2.1.0: {} + asap@2.0.6: {} + + assert-never@1.2.1: {} + ast-module-types@5.0.0: {} async@3.2.5: {} @@ -2741,6 +3077,12 @@ snapshots: core-js: 2.6.12 regenerator-runtime: 0.11.1 + babel-walk@3.0.0-canary-5: + dependencies: + '@babel/types': 7.24.6 + + babylon@6.16.1: {} + backbone-esnext-events@0.3.5: dependencies: babel-runtime: 6.26.0 @@ -2755,6 +3097,11 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + blamer@1.0.6: + dependencies: + execa: 4.1.0 + which: 2.0.2 + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -2776,6 +3123,16 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bytes@3.1.2: {} + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + callsites@3.1.0: {} caniuse-lite@1.0.30001621: {} @@ -2791,12 +3148,22 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-parser@2.2.0: + dependencies: + is-regex: 1.1.4 + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 cli-spinners@2.9.2: {} + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + clone@1.0.4: {} color-convert@1.9.3: @@ -2821,6 +3188,8 @@ snapshots: color-convert: 1.9.3 color-string: 1.9.1 + colors@1.4.0: {} + colorspace@1.1.4: dependencies: color: 3.2.1 @@ -2830,12 +3199,19 @@ snapshots: commander@2.20.3: {} + commander@5.1.0: {} + commander@7.2.0: {} commondir@1.0.1: {} concat-map@0.0.1: {} + constantinople@4.0.1: + dependencies: + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + convert-source-map@1.9.0: {} core-js-compat@3.37.1: @@ -2862,6 +3238,12 @@ snapshots: dependencies: clone: 1.0.4 + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dependency-tree@10.0.9: dependencies: commander: 10.0.1 @@ -2918,15 +3300,29 @@ snapshots: dependencies: path-type: 4.0.0 + doctypes@1.1.0: {} + electron-to-chromium@1.4.783: {} + emoji-regex@8.0.0: {} + enabled@2.0.0: {} + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + enhanced-resolve@5.16.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + escalade@3.1.2: {} escape-string-regexp@1.0.5: {} @@ -3035,6 +3431,20 @@ snapshots: esutils@2.0.3: {} + eventemitter3@5.0.1: {} + + execa@4.1.0: + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -3063,6 +3473,8 @@ snapshots: dependencies: moment: 2.30.1 + filepaths@0.3.0: {} + filing-cabinet@4.2.0: dependencies: app-module-path: 2.2.0 @@ -3096,6 +3508,18 @@ snapshots: fn.name@1.1.0: {} + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 @@ -3113,8 +3537,24 @@ snapshots: ast-module-types: 5.0.0 node-source-walk: 6.0.2 + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + get-own-enumerable-property-symbols@3.0.2: {} + get-stdin@4.0.1: {} + + get-stream@5.2.0: + dependencies: + pump: 3.0.0 + + gitignore-to-glob@0.3.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -3162,6 +3602,10 @@ snapshots: dependencies: minimist: 1.2.8 + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + graceful-fs@4.2.11: {} graphemer@1.4.0: {} @@ -3170,10 +3614,24 @@ snapshots: has-flag@4.0.0: {} + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.0.3 + hasown@2.0.2: dependencies: function-bind: 1.1.2 + human-signals@1.1.1: {} + ieee754@1.2.1: {} ignore@5.3.1: {} @@ -3200,8 +3658,15 @@ snapshots: dependencies: hasown: 2.0.2 + is-expression@4.0.0: + dependencies: + acorn: 7.4.1 + object-assign: 4.1.1 + is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -3214,6 +3679,13 @@ snapshots: is-path-inside@3.0.3: {} + is-promise@2.2.2: {} + + is-regex@1.1.4: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + is-regexp@1.0.0: {} is-relative-path@1.0.2: {} @@ -3228,16 +3700,46 @@ snapshots: isexe@2.0.0: {} + js-stringify@1.0.2: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: dependencies: argparse: 2.0.1 + jscpd-sarif-reporter@4.0.3: + dependencies: + colors: 1.4.0 + fs-extra: 11.2.0 + node-sarif-builder: 2.0.3 + + jscpd@4.0.4: + dependencies: + '@jscpd/core': 4.0.1 + '@jscpd/finder': 4.0.1 + '@jscpd/html-reporter': 4.0.1 + '@jscpd/tokenizer': 4.0.1 + colors: 1.4.0 + commander: 5.1.0 + fs-extra: 11.2.0 + gitignore-to-glob: 0.3.0 + jscpd-sarif-reporter: 4.0.3 + jsesc@0.5.0: {} jsesc@2.5.2: {} + jsinspect@0.12.7: + dependencies: + babylon: 6.16.1 + chalk: 2.4.2 + commander: 2.20.3 + filepaths: 0.3.0 + stable: 0.1.8 + strip-indent: 1.0.1 + strip-json-comments: 1.0.2 + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -3250,6 +3752,17 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jstransformer@1.0.0: + dependencies: + is-promise: 2.2.2 + promise: 7.3.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -3309,6 +3822,12 @@ snapshots: transitivePeerDependencies: - supports-color + markdown-table@2.0.0: + dependencies: + repeat-string: 1.6.1 + + merge-stream@2.0.0: {} + merge2@1.4.1: {} micromatch@4.0.7: @@ -3348,6 +3867,11 @@ snapshots: node-releases@2.0.14: {} + node-sarif-builder@2.0.3: + dependencies: + '@types/sarif': 2.1.7 + fs-extra: 10.1.0 + node-source-walk@6.0.2: dependencies: '@babel/parser': 7.24.6 @@ -3356,6 +3880,12 @@ snapshots: dependencies: remove-trailing-separator: 1.1.0 + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + object-assign@4.1.1: {} + object-hash@3.0.0: {} once@1.4.0: @@ -3457,6 +3987,82 @@ snapshots: dependencies: parse-ms: 2.1.0 + promise@7.3.1: + dependencies: + asap: 2.0.6 + + pug-attrs@3.0.0: + dependencies: + constantinople: 4.0.1 + js-stringify: 1.0.2 + pug-runtime: 3.0.1 + + pug-code-gen@3.0.3: + dependencies: + constantinople: 4.0.1 + doctypes: 1.1.0 + js-stringify: 1.0.2 + pug-attrs: 3.0.0 + pug-error: 2.1.0 + pug-runtime: 3.0.1 + void-elements: 3.1.0 + with: 7.0.2 + + pug-error@2.1.0: {} + + pug-filters@4.0.0: + dependencies: + constantinople: 4.0.1 + jstransformer: 1.0.0 + pug-error: 2.1.0 + pug-walk: 2.0.0 + resolve: 1.22.8 + + pug-lexer@5.0.1: + dependencies: + character-parser: 2.2.0 + is-expression: 4.0.0 + pug-error: 2.1.0 + + pug-linker@4.0.0: + dependencies: + pug-error: 2.1.0 + pug-walk: 2.0.0 + + pug-load@3.0.0: + dependencies: + object-assign: 4.1.1 + pug-walk: 2.0.0 + + pug-parser@6.0.0: + dependencies: + pug-error: 2.1.0 + token-stream: 1.0.0 + + pug-runtime@3.0.1: {} + + pug-strip-comments@2.0.0: + dependencies: + pug-error: 2.1.0 + + pug-walk@2.0.0: {} + + pug@3.0.3: + dependencies: + pug-code-gen: 3.0.3 + pug-filters: 4.0.0 + pug-lexer: 5.0.1 + pug-linker: 4.0.0 + pug-load: 3.0.0 + pug-parser: 6.0.0 + pug-runtime: 3.0.1 + pug-strip-comments: 2.0.0 + + pump@3.0.0: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -3505,6 +4111,10 @@ snapshots: remove-trailing-separator@1.1.0: {} + repeat-string@1.6.1: {} + + reprism@0.0.11: {} + requirejs-config-file@4.0.0: dependencies: esprima: 4.0.1 @@ -3545,6 +4155,15 @@ snapshots: semver@7.6.2: {} + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3564,12 +4183,22 @@ snapshots: source-map@0.6.1: optional: true + spark-md5@3.0.2: {} + + stable@0.1.8: {} + stack-trace@0.0.10: {} stream-to-array@2.3.0: dependencies: any-promise: 1.3.0 + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -3586,6 +4215,14 @@ snapshots: strip-bom@3.0.0: {} + strip-final-newline@2.0.0: {} + + strip-indent@1.0.1: + dependencies: + get-stdin: 4.0.1 + + strip-json-comments@1.0.2: {} + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -3616,6 +4253,8 @@ snapshots: dependencies: is-number: 7.0.0 + token-stream@1.0.0: {} + triple-beam@1.4.1: {} ts-graphviz@1.8.2: {} @@ -3690,6 +4329,8 @@ snapshots: universalify@0.1.2: {} + universalify@2.0.1: {} + unixify@1.0.0: dependencies: normalize-path: 2.1.1 @@ -3706,6 +4347,8 @@ snapshots: util-deprecate@1.0.2: {} + void-elements@3.1.0: {} + walkdir@0.4.1: {} wcwidth@1.0.1: @@ -3744,6 +4387,13 @@ snapshots: triple-beam: 1.4.1 winston-transport: 4.7.0 + with@7.0.2: + dependencies: + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + assert-never: 1.2.1 + babel-walk: 3.0.0-canary-5 + word-wrap@1.2.5: {} wrappy@1.0.2: {} diff --git a/src/commons/AuditUtils.js b/src/commons/AuditUtils.js index dfef310..e9f30db 100644 --- a/src/commons/AuditUtils.js +++ b/src/commons/AuditUtils.js @@ -1,3 +1,9 @@ +import crypto from 'crypto'; +import glob from 'globby'; +import unixify from 'unixify'; +import lodash from 'lodash'; +import AppLogger from './AppLogger.js'; + /** * Checks if a file is of an accepted type. * @param {string} fileName - The name of the file. @@ -46,18 +52,71 @@ const isExcludedFile = (filePath) => ( filePath?.toLowerCase()?.endsWith('.d.ts') || filePath?.toLowerCase()?.endsWith('.config.js') || filePath?.toLowerCase()?.endsWith('.config.ts') || + filePath?.toLowerCase()?.includes('/types/') || + filePath?.toLowerCase()?.includes('type') || + filePath?.toLowerCase()?.includes('index') || + filePath?.toLowerCase()?.includes('dico') || false ); /** - * The AuditUtils object. - * @typedef {Object} AuditUtils - * @property {function} isAcceptedFileType - Checks if a file is of an accepted type. - * @property {function} isExcludedFile - Checks if a file is excluded. + * Converts a pattern to a file. + * @param {string} pattern - The pattern to convert. + * @returns {Array} - Returns an array containing the files. + */ +const patternToFile = (pattern) => glob.sync(unixify(pattern)); + +/** + * Retrieves all files from a specified directory. + * + * @param {string} srcDir - The source directory to retrieve files from. + * @returns {Array|null} An array of files from the source directory, or null if the source directory is not specified or an error occurs. */ +const getFiles = (srcDir) => { + try{ + AppLogger.info(`[AuditUtils - parseFiles] srcDir: ${srcDir}`); + + if (!srcDir || !srcDir.length) { + return null; + } + + const files = lodash + .chain([ + srcDir, + ]) + .map(patternToFile) + .flatten() + .value(); + + AppLogger.info(`[AuditUtils - parseFiles] files: ${files?.length}`); + + return files; + }catch (error) { + AppLogger.info(`[AuditUtils - parseFiles] error: ${error.message}`); + return null; + } +}; + +/** + * Generate Hash for value string + * @param {string} value - Value to hashify + * @returns {string | null} + */ +const generateHash = (value) => { + if(!value){ + return null; + } + return crypto + .createHash('md5') + .update(JSON.stringify(value)) + .digest('hex'); +}; + const AuditUtils = { isAcceptedFileType, isExcludedFile, + getFiles, + generateHash, }; export default AuditUtils; diff --git a/src/index.js b/src/index.js index 8bc5e45..81ed0af 100755 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import { parseArgs } from 'node:util'; import AppLogger from './commons/AppLogger.js'; import CodeComplexityAuditor from './kernel/complexity/CodeComplexityAuditor.js'; import CodeCouplingAuditor from './kernel/coupling/CodeCouplingAuditor.js'; +import CodeDuplicationAuditor from './kernel/duplication/CodeDuplicationAuditor.js'; import CodeComplexityUtils from './kernel/complexity/CodeComplexityUtils.js'; import CodeCouplingUtils from './kernel/coupling/CodeCouplingUtils.js'; @@ -57,36 +58,6 @@ const codeComplexityAnalysisResult = await CodeComplexityAuditor.startAudit( } ); -/** - * Starts the code coupling audit. - * https://github.com/pahen/madge?tab=readme-ov-file#configuration - * @type {Object} - */ -const codeCouplingAnalysisResult = await CodeCouplingAuditor.startAudit(srcDir, { - 'fileExtensions': [ - 'ts', - 'tsx', - 'js', - 'jsx' - ], - excludeRegExp: [ - '.*node_modules/.*', - '.*dist/.*', - '.*__mocks__/.*', - '.*husky/.*', - '.*husky/.*', - '.*vscode/.*', - '.*idea/.*', - '.*gitlab/.*', - '.*github/.*', - '.*eslint.*', - '.*jest.*', - '.*test.*', - '.*next.config.*', - '.*.d.ts.*', - ] -}); - /** * Writes the audit result to files. */ @@ -99,6 +70,16 @@ CodeComplexityUtils codeComplexityAnalysisResult, }); +/** + * Starts the code coupling audit. + * https://github.com/pahen/madge?tab=readme-ov-file#configuration + * @type {Object} + */ +const codeCouplingAnalysisResult = await CodeCouplingAuditor.startAudit(srcDir); + +/** + * Writes the audit result to files. + */ CodeCouplingUtils .writeCodeCouplingAuditToFile({ codeCouplingOptions: { @@ -108,4 +89,12 @@ CodeCouplingUtils codeCouplingAnalysisResult, }); - +/** + * Starts the code duplication audit. + * @type {Object} + */ +CodeDuplicationAuditor.startAudit( + srcDir, + outputDir, + format +); diff --git a/src/kernel/complexity/CodeComplexityAuditor.js b/src/kernel/complexity/CodeComplexityAuditor.js index 5cbb8f6..015bf6e 100644 --- a/src/kernel/complexity/CodeComplexityAuditor.js +++ b/src/kernel/complexity/CodeComplexityAuditor.js @@ -15,7 +15,7 @@ const { } = AuditUtils; const { - inspect, + inspectDirectory, } = CodeComplexityUtils; /** @@ -155,7 +155,7 @@ const startAudit = async (directory, options) => { const { summary, files, - } = inspect( + } = inspectDirectory( { srcDir: directory, options, @@ -176,11 +176,7 @@ const startAudit = async (directory, options) => { const fileName = item.file; return ( isAcceptedFileType(fileName) && - !isExcludedFile(fileName) && - !fileName?.toLowerCase()?.includes('/types/') && - !fileName?.toLowerCase()?.includes('type') && - !fileName?.toLowerCase()?.includes('index') && - !fileName?.toLowerCase()?.includes('dico') + !isExcludedFile(fileName) ); }) .sort((a, b) => a.fileMaintainability > b.fileMaintainability ? 1 : -1); diff --git a/src/kernel/complexity/CodeComplexityConfig.js b/src/kernel/complexity/CodeComplexityConfig.js index 3287a01..14f4a0d 100644 --- a/src/kernel/complexity/CodeComplexityConfig.js +++ b/src/kernel/complexity/CodeComplexityConfig.js @@ -317,7 +317,10 @@ const formatCodeComplexityHtmlReport = (summary, helpMessages, reportsByFile) =>

Global Status

- Source lines of code (SLOC): ${average?.sloc || 0} + Total number of lines in the source code (physical SLOC): ${average?.psloc || 0} + + + Number of lines that will be executed (logical SLOC): ${average?.lsloc || 0}
diff --git a/src/kernel/complexity/CodeComplexityUtils.js b/src/kernel/complexity/CodeComplexityUtils.js index a7d3335..ad909e9 100644 --- a/src/kernel/complexity/CodeComplexityUtils.js +++ b/src/kernel/complexity/CodeComplexityUtils.js @@ -1,21 +1,24 @@ import fs from 'fs-extra'; import path from 'path'; -import escomplex from 'typhonjs-escomplex'; import lodash from 'lodash'; -import glob from 'globby'; -import unixify from 'unixify'; +import TyphonEscomplex from 'typhonjs-escomplex'; import AppLogger from '../../commons/AppLogger.js'; +import AuditUtils from '../../commons/AuditUtils.js'; import CodeComplexityConfig from './CodeComplexityConfig.js'; const { formatCodeComplexityHtmlReport, } = CodeComplexityConfig; +const { + getFiles, +} = AuditUtils; + /** * Parser options for the escomplex module analyzer. * @type {Object} */ -const parserOptions = { +const ComplexityParserOptions = { sourceType: 'module', plugins: [ 'jsx', @@ -29,55 +32,6 @@ const parserOptions = { ], }; -/** - * Finds the common base path among a list of files. - * @param {string[]} files - The list of file paths. - * @returns {string} - Returns the common base path. - */ -function findCommonBase(files) { - AppLogger.info(`[CodeComplexityUtils - findCommonBase] files: ${files?.length}`); - - if (!files || files.length === 0 || files.length === 1) { - return ''; - } - - const lastSlash = files[0].lastIndexOf(path.sep); - AppLogger.info(`[CodeComplexityUtils - findCommonBase] lastSlash: ${lastSlash}`); - - if (!lastSlash) { - return ''; - } - - const first = files[0].substr(0, lastSlash + 1); - AppLogger.info(`[CodeComplexityUtils - findCommonBase] first: ${first}`); - - let prefixlen = first.length; - AppLogger.info(`[CodeComplexityUtils - findCommonBase] prefixlen: ${prefixlen}`); - - /** - * Handles the prefixing of a file. - * @param {string} file - The file to handle. - */ - function handleFilePrefixing(file) { - - AppLogger.info(`[CodeComplexityUtils - findCommonBase] file: ${file}`); - - for (let i = prefixlen; i > 0; i--) { - if (file.substr(0, i) === first.substr(0, i)) { - prefixlen = i; - return; - } - } - prefixlen = 0; - } - - files.forEach(handleFilePrefixing); - - AppLogger.info(`[CodeComplexityUtils - findCommonBase] prefixlen: ${prefixlen}`); - - return first.substr(0, prefixlen); -} - /** * Processes the source code to generate a complexity report. * @param {string} source - The source code to analyze. @@ -100,7 +54,7 @@ function process(source, options, reportInfo) { // http://www.literateprogramming.com/mccabe.pdf // http://horst-zuse.homepage.t-online.de/z-halstead-final-05-1.pdf // https://avandeursen.com/2014/08/29/think-twice-before-using-the-maintainability-index/ - const report = escomplex.analyzeModule(source, options, parserOptions); + const report = TyphonEscomplex.analyzeModule(source, options, ComplexityParserOptions); AppLogger.info(`[CodeComplexityUtils - process] report: ${report}`); // Make the short filename easily accessible @@ -189,7 +143,8 @@ const getOverviewReport = (reports) => { reports.forEach((report) => { // clone objects so we don't have to worry about side effects - summary.total.sloc += report.complexity.aggregate.sloc.physical; + summary.total.psloc += report.complexity.aggregate.sloc.physical; + summary.total.lsloc += report.complexity.aggregate.sloc.logical; summary.total.maintainability += report.complexity.maintainability; const aggregate = lodash.cloneDeep(report.complexity.aggregate); @@ -202,7 +157,8 @@ const getOverviewReport = (reports) => { } }); - summary.average.sloc = Math.round(summary.total.sloc / reports.length); + summary.average.psloc = Math.round(summary.total.psloc / reports.length); + summary.average.lsloc = Math.round(summary.total.lsloc / reports.length); summary.average.maintainability = ( summary.total.maintainability / reports.length ).toFixed(2); @@ -216,11 +172,14 @@ const getOverviewReport = (reports) => { }; /** - * Parses a file. - * @param {string} file - The file to parse. - * @param {string} commonBasePath - The common base path. - * @param {Object} options - The options for the parser. - * @returns {Object} - Returns an object containing the analyzer result. + * Parses a file and returns relevant information. + * + * @param {string} file - The path to the file. + * @param {string} commonBasePath - The common base path for all files. + * @param {Object} options - The options for parsing. + * @param {RegExp} [options.exclude] - A regular expression for files to exclude. + * @param {boolean} [options.noempty] - Whether to skip empty lines. + * @returns {Object|null} An object containing the file information, or null if the file is excluded or not a JavaScript/TypeScript file. */ const parseFile = (file, commonBasePath, options) => { AppLogger.info(`[CodeComplexityUtils - parseFile] file: ${file}`); @@ -230,9 +189,11 @@ const parseFile = (file, commonBasePath, options) => { const mockPattern = /.*?(Mock).(js|jsx|ts|tsx)$/ig; const testPattern = /.*?(Test).(js|jsx|ts|tsx)$/ig; const nodeModulesPattern = /node_modules/g; + const targetModulesPattern = /target/g; if (file && ( (options.exclude && file.match(options.exclude)) || + file.match(targetModulesPattern) || file.match(mockPattern) || file.match(testPattern) || file.match(nodeModulesPattern) @@ -245,14 +206,17 @@ const parseFile = (file, commonBasePath, options) => { return null; } + AppLogger.info(`[CodeComplexityUtils - parseFile] matched file: ${file}`); + const fileShort = file.replace(commonBasePath, ''); const fileSafe = fileShort.replace(/[^a-zA-Z0-9]/g, '_'); + AppLogger.info(`[CodeComplexityUtils - parseFile] fileShort: ${fileShort}`); + AppLogger.info(`[CodeComplexityUtils - parseFile] fileSafe: ${fileSafe}`); + let source = fs.readFileSync(file).toString(); const trimmedSource = source.trim(); - AppLogger.info(`[CodeComplexityUtils - parseFile] trimmedSource: ${trimmedSource}`); - if (!trimmedSource) { return null; } @@ -267,104 +231,185 @@ const parseFile = (file, commonBasePath, options) => { source = `//${source}`; } - const reportInfo = { + return ({ file, - fileShort, fileSafe, - }; + fileShort, + source, + options, + }); +}; - // run reports against current file - return Object - .keys(analyzers) - .reduce((acc, analyzerName) => { - // if we should not execute parser - if (!options[analyzerName]) { - return acc; - } - try { - const reporter = analyzers[analyzerName]; - acc[analyzerName] = reporter?.process(source, options[analyzerName], reportInfo); - return acc; - } catch (error) { - console.log(`[parseFile]: file ${file} process error: ${error}`, options); - return acc; - } - }, {}); +/** + * Inspects a file and runs reports against it. + * + * @param {Object} params - The parameters for the function. + * @param {string} params.file - The path to the file. + * @param {string} params.commonBasePath - The common base path for all files. + * @param {Object} params.options - The options for parsing and reporting. + * @returns {Object|null} An object containing the reports for each analyzer, or null if an error occurs. + */ +const inspectFile = ({ + file, + commonBasePath, + options +}) => { + try { + const report = parseFile(file, commonBasePath, options); + if(!report){ + return null; + } + + const { + fileSafe, + fileShort, + source, + } = report; + + const reportInfo = { + file, + fileShort, + fileSafe, + }; + + // run reports against current file + return Object + .keys(analyzers) + .reduce((acc, analyzerName) => { + // if we should not execute parser + if (!options[analyzerName]) { + return acc; + } + try { + const reporter = analyzers[analyzerName]; + acc[analyzerName] = reporter?.process(source, options[analyzerName], reportInfo); + return acc; + } catch (error) { + console.log(`[parseFile]: file ${file} process error: ${error}`, options); + return acc; + } + }, {}); + } catch (error) { + AppLogger.info(`[CodeComplexityUtils - inspectFile] error: ${error.message}`); + return null; + } }; /** - * Parses multiple files. - * @param {Array} files - The files to parse. + * Finds the common base path among a list of files. + * @param {string[]} files - The list of file paths. + * @returns {string} - Returns the common base path. + */ +function findCommonBase(files) { + AppLogger.info(`[CodeComplexityUtils - findCommonBase] files: ${files?.length}`); + + if (!files + || files.length === 0 + || files.length === 1) { + return ''; + } + + const lastSlash = files[0].lastIndexOf(path.sep); + AppLogger.info(`[CodeComplexityUtils - findCommonBase] lastSlash: ${lastSlash}`); + + if (!lastSlash) { + return ''; + } + + const first = files[0].substr(0, lastSlash + 1); + AppLogger.info(`[CodeComplexityUtils - findCommonBase] first: ${first}`); + + let prefixlen = first.length; + AppLogger.info(`[CodeComplexityUtils - findCommonBase] prefixlen: ${prefixlen}`); + + /** + * Handles the prefixing of a file. + * @param {string} file - The file to handle. + */ + function handleFilePrefixing(file) { + + AppLogger.info(`[CodeComplexityUtils - findCommonBase] file: ${file}`); + + for (let i = prefixlen; i > 0; i--) { + if (file.substr(0, i) === first.substr(0, i)) { + prefixlen = i; + return; + } + } + prefixlen = 0; + } + + files.forEach(handleFilePrefixing); + + AppLogger.info(`[CodeComplexityUtils - findCommonBase] prefixlen: ${prefixlen}`); + + return first.substr(0, prefixlen); +} + +/** + * Inspect directory files. + * @param {string} srcDir - The directory to parse. * @param {Object} options - The options for the parser. * @returns {Array} - Returns an array containing the reports. */ -const parseFiles = (files, options) => { - const reports = []; +const inspectFiles = (srcDir, options) => { + try { + const files = getFiles(srcDir); + AppLogger.info(`[CodeComplexityUtils - inspectFiles] files: ${files?.length}`); + + if(!files?.length){ + return []; + } + + const mergedOptions = { + ...(options || {}), + ...complexityReportOptions, + }; - const commonBasePath = findCommonBase(files); + const commonBasePath = findCommonBase(files); - AppLogger.info(`[CodeComplexityUtils - parseFiles] commonBasePath: ${commonBasePath}`); + AppLogger.info(`[CodeComplexityUtils - inspectFiles] commonBasePath: ${commonBasePath}`); - for (let i = 0; i < files.length; i += 1) { - const file = files[i]; - const report = parseFile(file, commonBasePath, options); - if (report && + const reports = []; + + for (let i = 0; i < files.length; i += 1) { + const file = files[i]; + const report = inspectFile({ + file, + commonBasePath, + options: mergedOptions, + }); + if (report && Object.keys(report) && Object.keys(report).length > 0) { - reports.push(report); + reports.push(report); + } } - } - AppLogger.info(`[CodeComplexityUtils - parseFiles] reports: ${reports?.length}`); + AppLogger.info(`[CodeComplexityUtils - inspectFiles] reports: ${reports?.length}`); - return reports; + return reports; + } catch (error) { + AppLogger.info(`[CodeComplexityUtils - inspectFiles] error: ${error.message}`); + return null; + } }; -/** - * Converts a pattern to a file. - * @param {string} pattern - The pattern to convert. - * @returns {Array} - Returns an array containing the files. - */ -const patternToFile = (pattern) => glob.sync(unixify(pattern)); - /** * Inspects the source directory. * @param {Object} params - The parameters for the inspection. * @returns {Object} - Returns an object containing the overview report. */ -const inspect = ({ +const inspectDirectory = ({ srcDir, options, }) => { try { - AppLogger.info(`[CodeComplexityUtils - inspect] srcDir: ${srcDir}`); - - if (!srcDir || !srcDir.length) { - return null; - } - - const files = lodash - .chain([ - srcDir, - ]) - .map(patternToFile) - .flatten() - .value(); + AppLogger.info(`[CodeComplexityUtils - inspectDirectory] srcDir: ${srcDir}`); - AppLogger.info(`[CodeComplexityUtils - inspect] files: ${files?.length}`); + const reports = inspectFiles(srcDir, options); - if (!files.length) { - return null; - } - - const mergedOptions = { - ...(options || {}), - ...complexityReportOptions, - }; - - const reports = parseFiles(files, mergedOptions); - - AppLogger.info(`[CodeComplexityUtils - inspect] reports: ${reports?.length}`); + AppLogger.info(`[CodeComplexityUtils - inspectDirectory] reports: ${reports?.length}`); return getOverviewReport(reports); } catch (error) { @@ -372,7 +417,6 @@ const inspect = ({ } }; - /** * Groups reports by file. * @param {Array} reports - The reports to group. @@ -491,10 +535,10 @@ const writeCodeComplexityAuditToFile = ({ /** * The CodeComplexityUtils object. * @typedef {Object} CodeComplexityUtils - * @property {function} inspect - Inspects the source directory. + * @property {function} inspectDirectory - Inspects the source directory. */ const CodeComplexityUtils = { - inspect, + inspectDirectory, writeCodeComplexityAuditToFile, }; diff --git a/src/kernel/coupling/CodeCouplingAuditor.js b/src/kernel/coupling/CodeCouplingAuditor.js index 329f442..21902ae 100644 --- a/src/kernel/coupling/CodeCouplingAuditor.js +++ b/src/kernel/coupling/CodeCouplingAuditor.js @@ -1,18 +1,43 @@ import Madge from 'madge'; import AppLogger from '../../commons/AppLogger.js'; +const defaultOptions = { + fileExtensions: [ + 'ts', + 'tsx', + 'js', + 'jsx' + ], + excludeRegExp: [ + '.*node_modules/.*', + '.*target/.*', + '.*dist/.*', + '.*__mocks__/.*', + '.*husky/.*', + '.*husky/.*', + '.*vscode/.*', + '.*idea/.*', + '.*gitlab/.*', + '.*github/.*', + '.*eslint.*', + '.*jest.*', + '.*test.*', + '.*next.config.*', + '.*.d.ts.*', + ] +}; + /** * Starts the audit process for a given directory and options. * * @async * @param {string} directory - The directory to audit. - * @param {Object} options - The options for the audit. * @returns {Object} - Returns an object containing the results of the audit. * @throws {Error} - Throws an error if there was a problem starting the audit. */ -const startAudit = async (directory, options) => { +const startAudit = async (directory) => { try { - const dependenciesParseResult = await Madge(directory, options); + const dependenciesParseResult = await Madge(directory, defaultOptions); if(!dependenciesParseResult) { return ({}); } diff --git a/src/kernel/coupling/CodeCouplingConfig.js b/src/kernel/coupling/CodeCouplingConfig.js index 88e9fcf..e6e2754 100644 --- a/src/kernel/coupling/CodeCouplingConfig.js +++ b/src/kernel/coupling/CodeCouplingConfig.js @@ -1,8 +1,9 @@ - -const buildTableHtmlData = ({ - reports, - circular, -}) => { +/** + * This function builds the HTML data for a table. + * @param {Array} reports - The reports to be displayed in the table. + * @returns {Object} An object containing the HTML strings for the table headers and rows. + */ +const buildTableHtmlData = (reports) => { const columnsNames = [ 'file', 'Efferent Coupling', @@ -10,7 +11,29 @@ const buildTableHtmlData = ({ 'Instability Index' ]; - const tableHeaders = columnsNames.map(key => `${key}`).join('\n'); + const tableHeaders = columnsNames + .map(key => `${key}`).join('\n'); + + if(!reports?.length){ + return({ + tableHeaders, + tableRows: ` + + + No file found. + + + - + + + - + + + - + + `, + }); + } const tableRows = reports .map(report => ` @@ -36,6 +59,74 @@ const buildTableHtmlData = ({ }); }; +/** + * This function builds a list of circular dependencies in HTML format, with each group of circular dependencies separated by a header. + * + * @param {Array>} circularDependencies - An array of circular dependency arrays. Each inner array represents a set of circular dependencies. + * + * @returns {string} - A string representing the list of circular dependencies in HTML format, with each group of circular dependencies separated by a header. If no circular dependencies are found, it returns a specific message. + * + * @example + * + * const circularDependencies = [ + * ['Module1', 'Module2', 'Module3', 'Module1'], + * ['ModuleA', 'ModuleB', 'ModuleA'] + * ]; + * + * console.log(buildCircularDependenciesList(circularDependencies)); + * // Output: + * //

+ * // Group 1 + * //

+ * // + * //
+ * //

+ * // Group 2 + * //

+ * // + * //
+ */ +const buildCircularDependenciesList = (circularDependencies) => { + if(!circularDependencies?.length){ + return 'No circular dependencies found.'; + } + + let circularDependenciesList = ''; + for(let i= 0; i < circularDependencies?.length; i++){ + const circularDependencyRows = circularDependencies[i] + .filter(dependency => dependency.length); + + circularDependenciesList+=` +

+ Group ${i + 1} +

+ +
+ `; + } + + return circularDependenciesList; +}; + +/** + * This function formats the code coupling HTML reports. + * @param {Object} params - The parameters for the function. + * @param {Array} params.reports - The reports to be included in the HTML. + * @param {Array} params.circularDependencies - The circular dependencies to be included in the HTML. + * @param {string} params.svgFile - The SVG file to be included in the HTML. + * @returns {string} The formatted HTML string. + */ const formatCodeCouplingHtmlReports = ({ reports, circularDependencies, @@ -44,10 +135,8 @@ const formatCodeCouplingHtmlReports = ({ const { tableHeaders, tableRows, - } = buildTableHtmlData({ - reports, - circularDependencies, - }); + } = buildTableHtmlData(reports); + const circularDependenciesList = buildCircularDependenciesList(circularDependencies); return ` @@ -71,8 +160,12 @@ const formatCodeCouplingHtmlReports = ({ font-size: 1.5rem; } + h3 { + font-size: 1.2rem; + } + span { - font-size: 0.9rem; + font-size: 1rem; } .modal-body{ @@ -97,6 +190,7 @@ const formatCodeCouplingHtmlReports = ({ +
Coupling and Circular Dependencies Analysis
@@ -139,8 +233,18 @@ They can help identify modules that might be problematic to maintain or evolve d + +
+
+

Circular Dependencies Analysis

+ ${circularDependenciesList} +
+
+ +
+

Coupling Analysis

@@ -149,7 +253,7 @@ They can help identify modules that might be problematic to maintain or evolve d - ${tableRows} + ${tableRows}
Analysis Details
diff --git a/src/kernel/duplication/CodeDuplicationAuditor.js b/src/kernel/duplication/CodeDuplicationAuditor.js new file mode 100644 index 0000000..80173f9 --- /dev/null +++ b/src/kernel/duplication/CodeDuplicationAuditor.js @@ -0,0 +1,63 @@ +import { execSync } from 'child_process'; +import AppLogger from '../../commons/AppLogger.js'; + +const defaultOptions = { + 'mode': 'strict', + 'threshold': 0, + 'format': [ + 'javascript', + 'typescript', + 'jsx', + 'tsx' + ], + 'ignore': [ + '**/node_modules/**', + '**/target/**', + '**/dist/**', + '**/__mocks__/*', + '**/husky/**', + '**/.vscode/.*', + '**/.idea/**', + '**/.gitlab/**', + '**/.github/**', + 'eslint.*', + 'jest.*', + 'test', + 'next.*', + 'babel.*', + '.*.d.ts.*' + ] +}; + +/** + * This asynchronous function starts the audit process. + * @param {string} directory - The directory to be audited. + * @param {string} outputDir - The directory where the audit results will be stored. + * @param {string} fileFormat - The format of the audit report file. + * @returns {Promise} A promise that resolves to `true` if the audit was successful, and `false` otherwise. + * @throws Will throw an error if the audit process fails. + */ +const startAudit = async (directory, outputDir, fileFormat) => { + try { + + AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] directory: ${directory}`); + AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] outputDir: ${outputDir}`); + AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] fileFormat: ${fileFormat}`); + + const codeDuplicationCommand = `jscpd --silent --mode "${defaultOptions.mode}" --threshold ${defaultOptions.threshold} --reporters "${fileFormat}" --output "${outputDir}" --format "${defaultOptions.format}" --ignore "${defaultOptions.ignore.join(',')}" ${directory}`; + AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] jscpd script: ${codeDuplicationCommand}`); + + execSync(codeDuplicationCommand); + + return true; + } catch (error) { + AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] error: ${error.message}`); + return false; + } +}; + +const CodeDuplicationAuditor = { + startAudit, +}; + +export default CodeDuplicationAuditor; \ No newline at end of file