From e4aa9e7126ed72ebe4bde555eb23018452ebf8a5 Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Thu, 13 Aug 2020 16:12:50 +0300 Subject: [PATCH 1/6] feat: merge passed options and config options --- package-lock.json | 384 +++++++++++------- package.json | 2 +- src/index.js | 177 ++++++-- src/utils.js | 124 ++---- .../validate-options.test.js.snap | 2 - test/config-autoload.test.js | 85 ++-- test/fixtures/css/index2.js | 3 + test/fixtures/css/style2.css | 4 + test/loader.test.js | 15 +- .../__snapshots__/plugins.test.js.snap | 23 ++ test/options/parser.test.js | 9 +- test/options/plugins.test.js | 32 ++ test/options/stringifier.test.js | 6 +- test/options/syntax.test.js | 3 +- test/validate-options.test.js | 2 +- 15 files changed, 531 insertions(+), 340 deletions(-) create mode 100644 test/fixtures/css/index2.js create mode 100644 test/fixtures/css/style2.css diff --git a/package-lock.json b/package-lock.json index 65331afb..498b63a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1586,13 +1586,13 @@ } }, "@jest/core": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.3.0.tgz", - "integrity": "sha512-WAAqGMpc+U+GS0oSr/ikI1JdRyPQyTZSVOr1xjnVcfvfUTZCK+wGoN0Cb7dm7HVdpbMQr/NvtM6vBVChctmzHA==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.4.0.tgz", + "integrity": "sha512-mpXm4OjWQbz7qbzGIiSqvfNZ1FxX6ywWgLtdSD2luPORt5zKPtqcdDnX7L8RdfMaj1znDBgN2+gB094ZIr7vnA==", "dev": true, "requires": { "@jest/console": "^26.3.0", - "@jest/reporters": "^26.3.0", + "@jest/reporters": "^26.4.0", "@jest/test-result": "^26.3.0", "@jest/transform": "^26.3.0", "@jest/types": "^26.3.0", @@ -1602,17 +1602,17 @@ "exit": "^0.1.2", "graceful-fs": "^4.2.4", "jest-changed-files": "^26.3.0", - "jest-config": "^26.3.0", + "jest-config": "^26.4.0", "jest-haste-map": "^26.3.0", "jest-message-util": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.3.0", - "jest-resolve-dependencies": "^26.3.0", - "jest-runner": "^26.3.0", - "jest-runtime": "^26.3.0", - "jest-snapshot": "^26.3.0", + "jest-resolve": "^26.4.0", + "jest-resolve-dependencies": "^26.4.0", + "jest-runner": "^26.4.0", + "jest-runtime": "^26.4.0", + "jest-snapshot": "^26.4.0", "jest-util": "^26.3.0", - "jest-validate": "^26.3.0", + "jest-validate": "^26.4.0", "jest-watcher": "^26.3.0", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", @@ -1749,20 +1749,20 @@ } }, "@jest/globals": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.3.0.tgz", - "integrity": "sha512-oPe30VG9zor2U3Ev7khCM2LkjO3D+mgAv6s5D3Ed0sxfELxoRZwR8d1VgYWVQljcpumMwe9tDrKNuzgVjbEt7g==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.4.0.tgz", + "integrity": "sha512-QKwoVAeL9d0xaEM9ebPvfc+bolN04F+o3zM2jswGDBiiNjCogZ3LvOaqumRdDyz6kLmbx+UhgMBAVuLunbXZ2A==", "dev": true, "requires": { "@jest/environment": "^26.3.0", "@jest/types": "^26.3.0", - "expect": "^26.3.0" + "expect": "^26.4.0" } }, "@jest/reporters": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.3.0.tgz", - "integrity": "sha512-MfLJOUPxhGb3sRT/wFjHXd6gyVQ1Fb1XxbEwY+gqdDBpg3pq5qAB5eiBUvcTheFRHmhu3gOv3UZ/gtxmqGBA+Q==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.4.0.tgz", + "integrity": "sha512-14OPAAuYhgRBSNxAocVluX6ksdMdK/EuP9NmtBXU9g1uKaVBrPnohn/CVm6iMot1a9iU8BCxa5715YRf8FEg/A==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", @@ -1781,7 +1781,7 @@ "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", "jest-haste-map": "^26.3.0", - "jest-resolve": "^26.3.0", + "jest-resolve": "^26.4.0", "jest-util": "^26.3.0", "jest-worker": "^26.3.0", "node-notifier": "^7.0.0", @@ -1874,16 +1874,16 @@ } }, "@jest/test-sequencer": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.3.0.tgz", - "integrity": "sha512-G7TA0Z85uj5l1m9UKZ/nXbArn0y+MeLKbojNLDHgjb1PpNNFDAOO6FJhk9We34m/hadcciMcJFnxV94dV2TX+w==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.4.0.tgz", + "integrity": "sha512-9Z7lCShS7vERp+DRwIVNH/6sHMWwJK1DPnGCpGeVLGJJWJ4Y08sQI3vIKdmKHu2KmwlUBpRM+BFf7NlVUkl5XA==", "dev": true, "requires": { "@jest/test-result": "^26.3.0", "graceful-fs": "^4.2.4", "jest-haste-map": "^26.3.0", - "jest-runner": "^26.3.0", - "jest-runtime": "^26.3.0" + "jest-runner": "^26.4.0", + "jest-runtime": "^26.4.0" } }, "@jest/transform": { @@ -2875,9 +2875,9 @@ "dev": true }, "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", "dev": true }, "babel-code-frame": { @@ -4156,19 +4156,19 @@ } }, "conventional-changelog-core": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.1.7.tgz", - "integrity": "sha512-UBvSrQR2RdKbSQKh7RhueiiY4ZAIOW3+CSWdtKOwRv+KxIMNFKm1rOcGBFx0eA8AKhGkkmmacoTWJTqyz7Q0VA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.0.tgz", + "integrity": "sha512-8+xMvN6JvdDtPbGBqA7oRNyZD4od1h/SIzrWqHcKZjitbVXrFpozEeyn4iI4af1UwdrabQpiZMaV07fPUTGd4w==", "dev": true, "requires": { "add-stream": "^1.0.0", - "conventional-changelog-writer": "^4.0.16", + "conventional-changelog-writer": "^4.0.17", "conventional-commits-parser": "^3.1.0", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", "git-raw-commits": "2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^4.0.0", + "git-semver-tags": "^4.1.0", "lodash": "^4.17.15", "normalize-package-data": "^2.3.5", "q": "^1.5.1", @@ -5856,9 +5856,9 @@ } }, "electron-to-chromium": { - "version": "1.3.529", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.529.tgz", - "integrity": "sha512-n3sriLldqNyjBlosbnPftjCY+m1dVOY307I1Y0HaHAqDGe3hRvK7ksJwWd+qs599ybR4jobCo1+7zXM9GyNMSA==", + "version": "1.3.533", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.533.tgz", + "integrity": "sha512-YqAL+NXOzjBnpY+dcOKDlZybJDCOzgsq4koW3fvyty/ldTmsb4QazZpOWmVvZ2m0t5jbBf7L0lIGU3BUipwG+A==", "dev": true }, "elliptic": { @@ -6699,15 +6699,15 @@ } }, "expect": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.3.0.tgz", - "integrity": "sha512-3tC0dpPgkTGkycM9H+mMjzIhm8I3ZAOV+y1Cj3xmF9iKxDeHBCAB64hf1OY//bMzQ/AftfodNy2pQWMKpTIV8Q==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.4.0.tgz", + "integrity": "sha512-dbYDJhFcqQsamlos6nEwAMe+ahdckJBk5fmw1DYGLQGabGSlUuT+Fm2jHYw5119zG3uIhP+lCQbjJhFEdZMJtg==", "dev": true, "requires": { "@jest/types": "^26.3.0", "ansi-styles": "^4.0.0", "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.3.0", + "jest-matcher-utils": "^26.4.0", "jest-message-util": "^26.3.0", "jest-regex-util": "^26.0.0" }, @@ -7599,9 +7599,9 @@ } }, "git-semver-tags": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.0.0.tgz", - "integrity": "sha512-LajaAWLYVBff+1NVircURJFL8TQ3EMIcLAfHisWYX/nPoMwnTYfWAznQDmMujlLqoD12VtLmoSrF1sQ5MhimEQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.0.tgz", + "integrity": "sha512-TcxAGeo03HdErzKzi4fDD+xEL7gi8r2Y5YSxH6N2XYdVSV5UkBwfrt7Gqo1b+uSHCjy/sa9Y6BBBxxFLxfbhTg==", "dev": true, "requires": { "meow": "^7.0.0", @@ -8092,14 +8092,6 @@ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, - "import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "requires": { - "import-from": "^3.0.0" - } - }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -8109,21 +8101,6 @@ "resolve-from": "^4.0.0" } }, - "import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - } - } - }, "import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -8760,14 +8737,14 @@ } }, "jest": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.3.0.tgz", - "integrity": "sha512-LFCry7NS6bTa4BUGUHC+NvZ3B9WG7Jv8F+Lb96dAJFM23LMwSsL5RiJcw9S+nejsh8lS1VxHq+RSH4Xa9tujpA==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.4.0.tgz", + "integrity": "sha512-lNCOS+ckRHE1wFyVtQClBmbsOVuH2GWUTJMDL3vunp9DXcah+V8vfvVVApngClcdoc3rgZpqOfCNKLjxjj2l4g==", "dev": true, "requires": { - "@jest/core": "^26.3.0", + "@jest/core": "^26.4.0", "import-local": "^3.0.2", - "jest-cli": "^26.3.0" + "jest-cli": "^26.4.0" }, "dependencies": { "ansi-styles": { @@ -8812,12 +8789,12 @@ "dev": true }, "jest-cli": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.3.0.tgz", - "integrity": "sha512-vrlDluEjnNTJNpmw+lJ1Dvjhc+2o7QG0dG8n+iDu3NaoQ9OzqNeZsZZ0a9KP7SdtD5BXgvGSpCWTlLH5SqtxcA==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.4.0.tgz", + "integrity": "sha512-kw2Pr3V2x9/WzSDGsbz/MJBNlCoPMxMudrIavft4bqRlv5tASjU51tyO+1Os1LdW2dAnLQZYsxFUZ8oWPyssGQ==", "dev": true, "requires": { - "@jest/core": "^26.3.0", + "@jest/core": "^26.4.0", "@jest/test-result": "^26.3.0", "@jest/types": "^26.3.0", "chalk": "^4.0.0", @@ -8825,9 +8802,9 @@ "graceful-fs": "^4.2.4", "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^26.3.0", + "jest-config": "^26.4.0", "jest-util": "^26.3.0", - "jest-validate": "^26.3.0", + "jest-validate": "^26.4.0", "prompts": "^2.0.1", "yargs": "^15.3.1" } @@ -8939,13 +8916,13 @@ } }, "jest-config": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.3.0.tgz", - "integrity": "sha512-xzvmhKYOXOc/JjGabUUXoi7Nxu6QpY5zJxND85wdqFrdP7raJT5wqlrVJbp6Bv4Sj1e83Z8bkxjsZCpwPASaPw==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.4.0.tgz", + "integrity": "sha512-MxsvrBug8YY+C4QcUBtmgnHyFeW7w3Ouk/w9eplCDN8VJGVyBEZFe8Lxzfp2pSqh0Dqurqv8Oik2YkbekGUlxg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.3.0", + "@jest/test-sequencer": "^26.4.0", "@jest/types": "^26.3.0", "babel-jest": "^26.3.0", "chalk": "^4.0.0", @@ -8955,13 +8932,13 @@ "jest-environment-jsdom": "^26.3.0", "jest-environment-node": "^26.3.0", "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.3.0", + "jest-jasmine2": "^26.4.0", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.3.0", + "jest-resolve": "^26.4.0", "jest-util": "^26.3.0", - "jest-validate": "^26.3.0", + "jest-validate": "^26.4.0", "micromatch": "^4.0.2", - "pretty-format": "^26.3.0" + "pretty-format": "^26.4.0" }, "dependencies": { "ansi-styles": { @@ -9060,15 +9037,15 @@ } }, "jest-diff": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.3.0.tgz", - "integrity": "sha512-q5OZAtnr5CbHzrhjANzc3wvROk7+rcjCUI5uqM4cjOjtscNKfbJKBs3YhsWWhsdsIZzI3gc6wOpm49r6S61beg==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.0.tgz", + "integrity": "sha512-wwC38HlOW+iTq6j5tkj/ZamHn6/nrdcEOc/fKaVILNtN2NLWGdkfRaHWwfNYr5ehaLvuoG2LfCZIcWByVj0gjg==", "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^26.3.0", "jest-get-type": "^26.3.0", - "pretty-format": "^26.3.0" + "pretty-format": "^26.4.0" }, "dependencies": { "ansi-styles": { @@ -9133,16 +9110,16 @@ } }, "jest-each": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.3.0.tgz", - "integrity": "sha512-OSAnLv0Eo/sDVhV0ifT2u6Q4aYUBoZ97R4k9cQshUFLTco0iRDbViJiW3Y6ySZjW95Tb83/xMYCppBih/7sW/A==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.4.0.tgz", + "integrity": "sha512-+cyBh1ehs6thVT/bsZVG+WwmRn2ix4Q4noS9yLZgM10yGWPW12/TDvwuOV2VZXn1gi09/ZwJKJWql6YW1C9zNw==", "dev": true, "requires": { "@jest/types": "^26.3.0", "chalk": "^4.0.0", "jest-get-type": "^26.3.0", "jest-util": "^26.3.0", - "pretty-format": "^26.3.0" + "pretty-format": "^26.4.0" }, "dependencies": { "ansi-styles": { @@ -9317,9 +9294,9 @@ } }, "jest-jasmine2": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.3.0.tgz", - "integrity": "sha512-ZPkkA2XfH/fcLOp0SjeR4uDrMoNFilcwxLHORpjfMrcU0BFHNNRaF3DnslCdmewzqaERqtmHpYo8jj34RT+m2g==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.4.0.tgz", + "integrity": "sha512-cGBxwzDDKB09EPJ4pE69BMDv+2lO442IB1xQd+vL3cua2OKdeXQK6iDlQKoRX/iP0RgU5T8sn9yahLcx/+ox8Q==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", @@ -9330,15 +9307,15 @@ "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^26.3.0", + "expect": "^26.4.0", "is-generator-fn": "^2.0.0", - "jest-each": "^26.3.0", - "jest-matcher-utils": "^26.3.0", + "jest-each": "^26.4.0", + "jest-matcher-utils": "^26.4.0", "jest-message-util": "^26.3.0", - "jest-runtime": "^26.3.0", - "jest-snapshot": "^26.3.0", + "jest-runtime": "^26.4.0", + "jest-snapshot": "^26.4.0", "jest-util": "^26.3.0", - "pretty-format": "^26.3.0", + "pretty-format": "^26.4.0", "throat": "^5.0.0" }, "dependencies": { @@ -9436,25 +9413,25 @@ } }, "jest-leak-detector": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.3.0.tgz", - "integrity": "sha512-8C2Bur0S6n2xgW5kx22bDbe+Jjz9sM7/abr7DRQ48ww6q4w7vVzEpDEZiY7KatjTHtUloLTAqwTXEXg+tuETTg==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.4.0.tgz", + "integrity": "sha512-7EXKKEKnAWUPyiVtGZzJflbPOtYUdlNoevNVOkAcPpdR8xWiYKPGNGA6sz25S+8YhZq3rmkQJYAh3/P0VnoRwA==", "dev": true, "requires": { "jest-get-type": "^26.3.0", - "pretty-format": "^26.3.0" + "pretty-format": "^26.4.0" } }, "jest-matcher-utils": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.3.0.tgz", - "integrity": "sha512-M5ZRSp6qpyzZyrLwXD2Sop7xaxm6qu/mKvqWU+BOSPTa4Y0ZEoKUYBzus/emg6kaVt7Ov9xMDLLZR1SrC8FxCw==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.4.0.tgz", + "integrity": "sha512-u+xdCdq+F262DH+PutJKXLGr2H5P3DImdJCir51PGSfi3TtbLQ5tbzKaN8BkXbiTIU6ayuAYBWTlU1nyckVdzA==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^26.3.0", + "jest-diff": "^26.4.0", "jest-get-type": "^26.3.0", - "pretty-format": "^26.3.0" + "pretty-format": "^26.4.0" }, "dependencies": { "ansi-styles": { @@ -9649,9 +9626,9 @@ "dev": true }, "jest-resolve": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.3.0.tgz", - "integrity": "sha512-+oKVWDkXjdZ4Xciuxv+M5e5v/Z3RLjrKIzen9tq3IO6HpzsLf9Mk3rET5du1uU8iVUCvz4/1PmjzNF50Uc7l2A==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.4.0.tgz", + "integrity": "sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg==", "dev": true, "requires": { "@jest/types": "^26.3.0", @@ -9723,20 +9700,20 @@ } }, "jest-resolve-dependencies": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.3.0.tgz", - "integrity": "sha512-j5rZ2BUh8vVjJZ7bpgCre0t6mbFLm5BWfVhYb1H35A3nbPN3kepzMqkMnKXPhwyLIVwn25uYkv6LHc2/Xa1sGw==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.0.tgz", + "integrity": "sha512-hznK/hlrlhu8hwdbieRdHFKmcV83GW8t30libt/v6j1L3IEzb8iN21SaWzV8KRAAK4ijiU0kuge0wnHn+0rytQ==", "dev": true, "requires": { "@jest/types": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.3.0" + "jest-snapshot": "^26.4.0" } }, "jest-runner": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.3.0.tgz", - "integrity": "sha512-eiPKgbhTM4q6A7RBh4qzKf6hwFDJMfqoFJubFvWSrHdZUsvSiBWYDqQI+FUXDFxDAOn/AfZjKURACAH3fUDjwA==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.4.0.tgz", + "integrity": "sha512-XF+tnUGolnPriu6Gg+HHWftspMjD5NkTV2mQppQnpZe39GcUangJ0al7aBGtA3GbVAcRd048DQiJPmsQRdugjw==", "dev": true, "requires": { "@jest/console": "^26.3.0", @@ -9748,13 +9725,13 @@ "emittery": "^0.7.1", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-config": "^26.3.0", + "jest-config": "^26.4.0", "jest-docblock": "^26.0.0", "jest-haste-map": "^26.3.0", - "jest-leak-detector": "^26.3.0", + "jest-leak-detector": "^26.4.0", "jest-message-util": "^26.3.0", - "jest-resolve": "^26.3.0", - "jest-runtime": "^26.3.0", + "jest-resolve": "^26.4.0", + "jest-runtime": "^26.4.0", "jest-util": "^26.3.0", "jest-worker": "^26.3.0", "source-map-support": "^0.5.6", @@ -9814,15 +9791,15 @@ } }, "jest-runtime": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.3.0.tgz", - "integrity": "sha512-cqCz+S76qwZcPnddkLCjuNw9O8/lB+i1odjz2hpvpDogXLp0qSMs+Slh1gBjB5V4feUyBHav/550Mr3FeTdmnA==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.4.0.tgz", + "integrity": "sha512-1fjZgGpkyQBUTo59Vi19I4IcsBwzY6uwVFNjUmR06iIi3XRErkY28yimi4IUDRrofQErqcDEw2n3DF9WmQ6vEg==", "dev": true, "requires": { "@jest/console": "^26.3.0", "@jest/environment": "^26.3.0", "@jest/fake-timers": "^26.3.0", - "@jest/globals": "^26.3.0", + "@jest/globals": "^26.4.0", "@jest/source-map": "^26.3.0", "@jest/test-result": "^26.3.0", "@jest/transform": "^26.3.0", @@ -9833,15 +9810,15 @@ "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.4", - "jest-config": "^26.3.0", + "jest-config": "^26.4.0", "jest-haste-map": "^26.3.0", "jest-message-util": "^26.3.0", "jest-mock": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.3.0", - "jest-snapshot": "^26.3.0", + "jest-resolve": "^26.4.0", + "jest-snapshot": "^26.4.0", "jest-util": "^26.3.0", - "jest-validate": "^26.3.0", + "jest-validate": "^26.4.0", "slash": "^3.0.0", "strip-bom": "^4.0.0", "yargs": "^15.3.1" @@ -9922,25 +9899,25 @@ } }, "jest-snapshot": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.3.0.tgz", - "integrity": "sha512-tHVUIeOTN/0SZN2ZjBZHzPG5txs/6uEQx2mwjxIT7QRE7pddPLd8jktXthyIz6bV+3GKetWXSV4YAoPUQwrfMA==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.4.0.tgz", + "integrity": "sha512-vFGmNGWHMBomrlOpheTMoqihymovuH3GqfmaEIWoPpsxUXyxT3IlbxI5I4m2vg0uv3HUJYg5JoGrkgMzVsAwCg==", "dev": true, "requires": { "@babel/types": "^7.0.0", "@jest/types": "^26.3.0", "@types/prettier": "^2.0.0", "chalk": "^4.0.0", - "expect": "^26.3.0", + "expect": "^26.4.0", "graceful-fs": "^4.2.4", - "jest-diff": "^26.3.0", + "jest-diff": "^26.4.0", "jest-get-type": "^26.3.0", "jest-haste-map": "^26.3.0", - "jest-matcher-utils": "^26.3.0", + "jest-matcher-utils": "^26.4.0", "jest-message-util": "^26.3.0", - "jest-resolve": "^26.3.0", + "jest-resolve": "^26.4.0", "natural-compare": "^1.4.0", - "pretty-format": "^26.3.0", + "pretty-format": "^26.4.0", "semver": "^7.3.2" }, "dependencies": { @@ -10112,9 +10089,9 @@ } }, "jest-validate": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.3.0.tgz", - "integrity": "sha512-oIJWqkIdgh1Q1O7ku4kDGkQoFKUOtZyDMbfYs4DsBi6r+FDY37xKTyZ30nM8F6yGZfB72qc7XB+3qKRgokwoXg==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.4.0.tgz", + "integrity": "sha512-t56Z/FRMrLP6mpmje7/YgHy0wOzcuc6i3LBXz6kjmsUWYN62OuMdC86Vg9/dX59SvyitSqqegOrx+h7BkNXeaQ==", "dev": true, "requires": { "@jest/types": "^26.3.0", @@ -10122,7 +10099,7 @@ "chalk": "^4.0.0", "jest-get-type": "^26.3.0", "leven": "^3.1.0", - "pretty-format": "^26.3.0" + "pretty-format": "^26.4.0" }, "dependencies": { "ansi-styles": { @@ -12747,6 +12724,15 @@ "postcss": "^7.0.0" } }, + "postcss-font-weights": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-weights/-/postcss-font-weights-5.0.0.tgz", + "integrity": "sha512-n/1sLibDcRcFS4HcGVzB81eUdxjG8p3XZFvM3Q+0Szz+e8f64dV3bJyUMs7Nw8ZV4qmO293SUVmOMmmhNMkzjw==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, "postcss-import": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", @@ -13144,6 +13130,96 @@ "uniq": "^1.0.1" } }, + "postcss-short": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-short/-/postcss-short-5.0.0.tgz", + "integrity": "sha512-7VvAeY8Bm01cK+honNsQIisaJsUdhdNTPlYuo7iQq80Fbjl844pVNBXhjXjVeXi/+dKK5aRtCNKod7U0hSeTug==", + "dev": true, + "requires": { + "postcss": "^7.0.5", + "postcss-font-weights": "^5.0.0", + "postcss-short-border": "^4.0.0", + "postcss-short-border-radius": "^3.0.0", + "postcss-short-color": "^4.0.0", + "postcss-short-font-size": "^5.0.0", + "postcss-short-overflow": "^1.0.0", + "postcss-short-position": "^4.0.1", + "postcss-short-size": "^4.0.0", + "postcss-short-spacing": "^4.0.0" + } + }, + "postcss-short-border": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-short-border/-/postcss-short-border-4.0.0.tgz", + "integrity": "sha512-o9lZZ7yavQAs2Tj1BazltLBTDH4XdNtUuY15NhV6Nr1R+1sG4e9k2QZRhaOazPbntrw7pDHFqRwDYQND9DB8jw==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-short-border-radius": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-short-border-radius/-/postcss-short-border-radius-3.0.0.tgz", + "integrity": "sha512-s4g/NSUFA71+s4fJMUFo/IqcJL1ZdmWSz90GctW/f3msdhetgaU2OuORYQtUB2HGX5BdsvBFai8FBv/D3/lrVQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-short-color": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-short-color/-/postcss-short-color-4.0.0.tgz", + "integrity": "sha512-2cIbOysfLRXweXDqVpt+csXNaAF46S150lN8SgItvKN3naWXfKSanYLefzF9xZ2eVl7qgkv7Fkd8NgNI0PZFHQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-short-font-size": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-short-font-size/-/postcss-short-font-size-5.0.0.tgz", + "integrity": "sha512-CaEzUc/YSOq2PDblqZuSxdecmCqN5HbxLLLu5h53GoKJpXPNxrk6R9cfBvB/cO1LQyXNFtzZuQwL/JglkuBy4A==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-short-overflow": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-short-overflow/-/postcss-short-overflow-1.0.0.tgz", + "integrity": "sha512-nYFZGCt4+/x3kyQjgX3unxx7942sU/7ZI3JE8jHdgJZ/c/sAOrwWyjrNdrdckJzUj1xrCYcrp+vcrj2wspsCJA==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-short-position": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-short-position/-/postcss-short-position-4.0.1.tgz", + "integrity": "sha512-/k2v8v+Wwrb+nIcXqOuD32y6Yss1lci1fq7FMzsJsu7nfWHf48mpgPR/afliGdZvrXRRB9IW2aJbwugoOCgDsw==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-short-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-short-size/-/postcss-short-size-4.0.0.tgz", + "integrity": "sha512-/RRw2JFj+7xmX+GO5HIp9+sVNZTm+bXWKwHlxxXcxEJDPng/dNeWGXIdExbuknqgDeDX/lv5YfgjD2g5TUtUtA==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-short-spacing": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-short-spacing/-/postcss-short-spacing-4.0.0.tgz", + "integrity": "sha512-ThWP8vse9jtT26+l8fqfCriPVR7QGYUX7gEXYNPJ2+smD4XHpMJAmbhqa8aKuYsCMhvzeC9ZxCsR/B+nXA7iEw==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, "postcss-svgo": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", @@ -13194,9 +13270,9 @@ "dev": true }, "pretty-format": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.3.0.tgz", - "integrity": "sha512-24kRw4C2Ok8+SHquydTZZCZPF2fvANI7rChGs8sNu784+1Jkq5jVFMvNAJSLuLy6XUcP3Fnw+SscLIQag/CG8Q==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", + "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", "dev": true, "requires": { "@jest/types": "^26.3.0", @@ -14063,9 +14139,9 @@ "dev": true }, "serialize-javascript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", - "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -15350,16 +15426,16 @@ } }, "terser-webpack-plugin": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz", - "integrity": "sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", "dev": true, "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^3.1.0", + "serialize-javascript": "^4.0.0", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", diff --git a/package.json b/package.json index 7226c866..3a14ba90 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ }, "dependencies": { "cosmiconfig": "^7.0.0", - "import-cwd": "^3.0.0", "loader-utils": "^2.0.0", "postcss": "^7.0.0", "schema-utils": "^2.7.0" @@ -74,6 +73,7 @@ "postcss-import": "^12.0.1", "postcss-js": "^2.0.0", "postcss-nested": "^4.2.3", + "postcss-short": "^5.0.0", "prettier": "^2.0.5", "standard": "^14.3.4", "standard-version": "^8.0.2", diff --git a/src/index.js b/src/index.js index 2d6c8651..77f9a590 100644 --- a/src/index.js +++ b/src/index.js @@ -5,12 +5,82 @@ import validateOptions from 'schema-utils'; import postcss from 'postcss'; +import postcssPkg from 'postcss/package.json'; + import Warning from './Warning'; import SyntaxError from './Error'; import parseOptions from './options'; import schema from './options.json'; import { exec, loadConfig } from './utils'; +const load = (plugin, options, file) => { + try { + if ( + options === null || + typeof options === 'undefined' || + Object.keys(options).length === 0 + ) { + // eslint-disable-next-line global-require,import/no-dynamic-require + return require(plugin); + } + + // eslint-disable-next-line global-require,import/no-dynamic-require + return require(plugin)(options); + } catch (err) { + throw new Error( + `Loading PostCSS Plugin failed: ${err.message}\n\n(@${file})` + ); + } +}; + +const loadPlugins = (pluginEntry, file) => { + let plugins = []; + + if (Array.isArray(pluginEntry)) { + plugins = pluginEntry.filter(Boolean); + } else { + plugins = Object.entries(pluginEntry).filter((i) => { + const [, options] = i; + + return options !== false ? pluginEntry : ''; + }); + } + + plugins = plugins.map((plugin) => { + const [pluginName, pluginOptions] = plugin; + + return load(pluginName, pluginOptions, file); + }); + + if (plugins.length && plugins.length > 0) { + plugins.forEach((plugin, i) => { + if (plugin.postcss) { + // eslint-disable-next-line no-param-reassign + plugin = plugin.postcss; + } + + if (plugin.default) { + // eslint-disable-next-line no-param-reassign + plugin = plugin.default; + } + + if ( + // eslint-disable-next-line + !( + (typeof plugin === 'object' && Array.isArray(plugin.plugins)) || + typeof plugin === 'function' + ) + ) { + throw new TypeError( + `Invalid PostCSS Plugin found at: plugins[${i}]\n\n(@${file})` + ); + } + }); + } + + return plugins; +}; + /** * **PostCSS Loader** * @@ -34,29 +104,16 @@ export default async function loader(content, sourceMap, meta = {}) { const callback = this.async(); const file = this.resourcePath; - let config; + let configRc = {}; - const { length } = Object.keys(options).filter((option) => { - switch (option) { - // case 'exec': - case 'ident': - case 'config': - case 'sourceMap': - return false; - default: - return option; - } - }); + options.config = + options.config === false + ? options.config + : typeof options.config !== 'undefined' + ? options.config + : true; - if (length) { - try { - config = await parseOptions.call(this, options); - } catch (error) { - callback(error); - - return; - } - } else { + if (options.config) { const rc = { path: path.dirname(file), ctx: { @@ -69,20 +126,64 @@ export default async function loader(content, sourceMap, meta = {}) { }, }; - if (options.config) { - if (options.config.path) { - rc.path = path.resolve(options.config.path); - } + if (typeof options.config.path !== 'undefined') { + rc.path = path.resolve(options.config.path); + } - if (options.config.ctx) { - rc.ctx.options = options.config.ctx; - } + if (typeof options.config.ctx !== 'undefined') { + rc.ctx.options = options.config.ctx; } rc.ctx.webpack = this; try { - config = await loadConfig(options.config, rc.ctx, rc.path, this.fs); + configRc = await loadConfig(options.config, rc.ctx, rc.path, this.fs); + delete options.config; + } catch (error) { + callback(error); + + return; + } + } + + function pluginsToArray(plugins) { + if (typeof plugins === 'undefined') { + return []; + } + + if (Array.isArray(plugins)) { + return plugins; + } + + return [plugins]; + } + + const mergedOptions = { + ...configRc, + ...options, + plugins: [ + ...pluginsToArray(configRc.plugins), + ...pluginsToArray(options.plugins), + ], + }; + + let config; + + const { length } = Object.keys(mergedOptions).filter((option) => { + switch (option) { + // case 'exec': + // case 'ident': + case 'config': + case 'sourceMap': + return false; + default: + return option; + } + }); + + if (length) { + try { + config = await parseOptions.call(this, mergedOptions); } catch (error) { callback(error); @@ -160,10 +261,26 @@ export default async function loader(content, sourceMap, meta = {}) { postcssOptions.map.prev = sourceMap; } + let resultPlugins = []; + + for (const plugin of plugins) { + if (plugin === false) { + // eslint-disable-next-line no-continue + continue; + } + if (plugin.postcssVersion === postcssPkg.version) { + resultPlugins.push(plugin); + } else if (typeof plugin === 'function') { + resultPlugins.push(plugin); + } else { + resultPlugins = resultPlugins.concat(loadPlugins(plugin, file)); + } + } + let result; try { - result = await postcss(plugins).process(content, postcssOptions); + result = await postcss(resultPlugins).process(content, postcssOptions); } catch (error) { if (error.file) { this.addDependency(error.file); diff --git a/src/utils.js b/src/utils.js index 7280ea3e..2136de6d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -3,7 +3,6 @@ import path from 'path'; import Module from 'module'; import { cosmiconfig } from 'cosmiconfig'; -import importCwd from 'import-cwd'; const parentModule = module; const moduleName = 'postcss'; @@ -37,7 +36,8 @@ const loadOptions = (config, file) => { if (config.parser && typeof config.parser === 'string') { try { - result.parser = importCwd(config.parser); + // eslint-disable-next-line global-require,import/no-dynamic-require + result.parser = require(config.parser); } catch (err) { throw new Error( `Loading PostCSS Parser failed: ${err.message}\n\n(@${file})` @@ -47,7 +47,8 @@ const loadOptions = (config, file) => { if (config.syntax && typeof config.syntax === 'string') { try { - result.syntax = importCwd(config.syntax); + // eslint-disable-next-line global-require,import/no-dynamic-require + result.syntax = require(config.syntax); } catch (err) { throw new Error( `Loading PostCSS Syntax failed: ${err.message}\n\n(@${file})` @@ -57,7 +58,8 @@ const loadOptions = (config, file) => { if (config.stringifier && typeof config.stringifier === 'string') { try { - result.stringifier = importCwd(config.stringifier); + // eslint-disable-next-line global-require,import/no-dynamic-require + result.stringifier = require(config.stringifier); } catch (err) { throw new Error( `Loading PostCSS Stringifier failed: ${err.message}\n\n(@${file})` @@ -65,97 +67,9 @@ const loadOptions = (config, file) => { } } - if (config.plugins) { - // eslint-disable-next-line no-param-reassign - delete config.plugins; - } - return { ...config, ...result }; }; -const load = (plugin, options, file) => { - try { - if ( - options === null || - typeof options === 'undefined' || - Object.keys(options).length === 0 - ) { - return importCwd(plugin); - } - - return importCwd(plugin)(options); - } catch (err) { - throw new Error( - `Loading PostCSS Plugin failed: ${err.message}\n\n(@${file})` - ); - } -}; - -const loadPlugins = (config, file) => { - let plugins = []; - - if (Array.isArray(config.plugins)) { - plugins = config.plugins.filter(Boolean); - } else { - plugins = Object.keys(config.plugins) - .filter((plugin) => { - return config.plugins[plugin] !== false ? plugin : ''; - }) - .map((plugin) => { - return load(plugin, config.plugins[plugin], file); - }); - } - - if (plugins.length && plugins.length > 0) { - plugins.forEach((plugin, i) => { - if (plugin.postcss) { - // eslint-disable-next-line no-param-reassign - plugin = plugin.postcss; - } - - if (plugin.default) { - // eslint-disable-next-line no-param-reassign - plugin = plugin.default; - } - - if ( - // eslint-disable-next-line - !( - (typeof plugin === 'object' && Array.isArray(plugin.plugins)) || - typeof plugin === 'function' - ) - ) { - throw new TypeError( - `Invalid PostCSS Plugin found at: plugins[${i}]\n\n(@${file})` - ); - } - }); - } - - return plugins; -}; - -const processResult = (context, result) => { - const file = result.filepath || ''; - let config = result.config || {}; - - if (typeof config === 'function') { - config = config(context); - } else { - config = Object.assign({}, config, context); - } - - if (!config.plugins) { - config.plugins = []; - } - - return { - plugins: loadPlugins(config, file), - options: loadOptions(config, file), - file, - }; -}; - function exec(code, loaderContext) { const { resource, context } = loaderContext; @@ -172,10 +86,6 @@ function exec(code, loaderContext) { } async function loadConfig(config, context, configPath, inputFileSystem) { - if (config === false) { - return {}; - } - let searchPath = configPath ? path.resolve(configPath) : process.cwd(); if (typeof config === 'string') { @@ -204,7 +114,27 @@ async function loadConfig(config, context, configPath, inputFileSystem) { throw new Error(`No PostCSS Config found in: ${searchPath}`); } - return processResult(createContext(context), result); + const patchedContext = createContext(context); + + let resultConfig = result.config || {}; + + if (typeof resultConfig === 'function') { + resultConfig = resultConfig(patchedContext); + } else { + resultConfig = Object.assign({}, resultConfig, patchedContext); + } + + if (!resultConfig.plugins) { + resultConfig.plugins = []; + } + + resultConfig.file = result.filepath || ''; + + delete resultConfig.webpack; + + const options = loadOptions(resultConfig, resultConfig.file); + + return { ...resultConfig, ...options }; } export { exec, loadConfig }; diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index 85b98925..ca7a2491 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -100,8 +100,6 @@ exports[`validate options should throw an error on the "parser" option with "tru object { … }" `; -exports[`validate options should throw an error on the "plugins" option with "/test/" value 1`] = `"/test/ is not a PostCSS plugin"`; - exports[`validate options should throw an error on the "plugins" option with "1" value 1`] = ` "Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema. - options.plugins should be one of these: diff --git a/test/config-autoload.test.js b/test/config-autoload.test.js index 2bd1762b..46f9f70c 100644 --- a/test/config-autoload.test.js +++ b/test/config-autoload.test.js @@ -15,15 +15,13 @@ describe('config-autoload', () => { it('.postcssrc - {Object} - Load Config', async () => { const expected = (config) => { - expect(config.options.parser).toEqual(require('sugarss')); - expect(config.options.syntax).toEqual(require('sugarss')); - expect(config.options.map).toEqual(false); - expect(config.options.from).toEqual('./test/rc/fixtures/index.css'); - expect(config.options.to).toEqual('./test/rc/expect/index.css'); + expect(config.parser).toEqual(require('sugarss')); + expect(config.syntax).toEqual(require('sugarss')); + expect(config.map).toEqual(false); + expect(config.from).toEqual('./test/rc/fixtures/index.css'); + expect(config.to).toEqual('./test/rc/expect/index.css'); - expect(config.plugins.length).toEqual(2); - expect(typeof config.plugins[0]).toBe('function'); - expect(typeof config.plugins[1]).toBe('function'); + expect(Object.keys(config.plugins).length).toEqual(3); expect(config.file).toEqual( path.resolve(testDirectory, 'rc', '.postcssrc') @@ -31,7 +29,7 @@ describe('config-autoload', () => { }; const config = await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'rc'), fs @@ -42,17 +40,13 @@ describe('config-autoload', () => { it('postcss.config.js - {Object} - Load Config', async () => { const expected = (config) => { - expect(config.options.parser).toEqual(require('sugarss')); - expect(config.options.syntax).toEqual(require('sugarss')); - expect(config.options.map).toEqual(false); - expect(config.options.from).toEqual( - './test/js/object/fixtures/index.css' - ); - expect(config.options.to).toEqual('./test/js/object/expect/index.css'); + expect(config.parser).toEqual(require('sugarss')); + expect(config.syntax).toEqual(require('sugarss')); + expect(config.map).toEqual(false); + expect(config.from).toEqual('./test/js/object/fixtures/index.css'); + expect(config.to).toEqual('./test/js/object/expect/index.css'); - expect(config.plugins.length).toEqual(2); - expect(typeof config.plugins[0]).toBe('function'); - expect(typeof config.plugins[1]).toBe('function'); + expect(Object.keys(config.plugins).length).toEqual(3); expect(config.file).toEqual( path.resolve(testDirectory, 'js/object', 'postcss.config.js') @@ -60,7 +54,7 @@ describe('config-autoload', () => { }; const config = await loadConfig( - null, + true, ctx, path.resolve(testDirectory, 'js/object'), fs @@ -71,15 +65,13 @@ describe('config-autoload', () => { it('postcss.config.js - {Array} - Load Config', async () => { const expected = (config) => { - expect(config.options.parser).toEqual(require('sugarss')); - expect(config.options.syntax).toEqual(require('sugarss')); - expect(config.options.map).toEqual(false); - expect(config.options.from).toEqual('./test/js/array/fixtures/index.css'); - expect(config.options.to).toEqual('./test/js/array/expect/index.css'); + expect(config.parser).toEqual(require('sugarss')); + expect(config.syntax).toEqual(require('sugarss')); + expect(config.map).toEqual(false); + expect(config.from).toEqual('./test/js/array/fixtures/index.css'); + expect(config.to).toEqual('./test/js/array/expect/index.css'); - expect(config.plugins.length).toEqual(2); - expect(typeof config.plugins[0]).toBe('function'); - expect(typeof config.plugins[1]).toBe('function'); + expect(Object.keys(config.plugins).length).toEqual(3); expect(config.file).toEqual( path.resolve(testDirectory, 'js/array', 'postcss.config.js') @@ -87,7 +79,7 @@ describe('config-autoload', () => { }; const config = await loadConfig( - null, + true, ctx, path.resolve(testDirectory, 'js/array'), fs @@ -98,15 +90,12 @@ describe('config-autoload', () => { it('package.json - {Object} - Load Config', async () => { const expected = (config) => { - expect(config.options.parser).toEqual(false); - expect(config.options.syntax).toEqual(false); - expect(config.options.map).toEqual(false); - expect(config.options.from).toEqual('./test/pkg/fixtures/index.css'); - expect(config.options.to).toEqual('./test/pkg/expect/index.css'); - - expect(config.plugins.length).toEqual(2); - expect(typeof config.plugins[0]).toBe('function'); - expect(typeof config.plugins[1]).toBe('function'); + expect(config.parser).toEqual(false); + expect(config.syntax).toEqual(false); + expect(config.map).toEqual(false); + expect(config.from).toEqual('./test/pkg/fixtures/index.css'); + expect(config.to).toEqual('./test/pkg/expect/index.css'); + expect(Object.keys(config.plugins).length).toEqual(3); expect(config.file).toEqual( path.resolve(testDirectory, 'pkg', 'package.json') @@ -114,7 +103,7 @@ describe('config-autoload', () => { }; const config = await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'pkg'), fs @@ -125,7 +114,7 @@ describe('config-autoload', () => { it('Loading Config - {Error}', async () => { try { - await loadConfig(null, {}, path.resolve('unresolved'), fs); + await loadConfig(true, {}, path.resolve('unresolved'), fs); } catch (error) { expect(error.message).toMatch(/^No PostCSS Config found in: (.*)$/); } @@ -134,7 +123,7 @@ describe('config-autoload', () => { it('Plugin - {Type} - Invalid', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/plugins'), fs @@ -149,7 +138,7 @@ describe('config-autoload', () => { it('Loading Plugin - {Object} - {Error}', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/plugins/object'), fs @@ -162,7 +151,7 @@ describe('config-autoload', () => { it('Loading Plugin - {Object} - Options - {Error}', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/plugins/object/options'), fs @@ -175,7 +164,7 @@ describe('config-autoload', () => { it('Loading Plugin - {Array} - {Error}', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/plugins/array'), fs @@ -188,7 +177,7 @@ describe('config-autoload', () => { it('Loading Plugin - {Array} - Options - {Error}', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/plugins/array/options'), fs @@ -203,7 +192,7 @@ describe('Loading Options - {Error}', () => { it('Parser - {String}', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/options/parser'), fs @@ -216,7 +205,7 @@ describe('Loading Options - {Error}', () => { it('Syntax - {String}', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/options/syntax'), fs @@ -229,7 +218,7 @@ describe('Loading Options - {Error}', () => { it('Stringifier - {String}', async () => { try { await loadConfig( - null, + true, {}, path.resolve(testDirectory, 'err/options/stringifier'), fs diff --git a/test/fixtures/css/index2.js b/test/fixtures/css/index2.js new file mode 100644 index 00000000..9415e9b0 --- /dev/null +++ b/test/fixtures/css/index2.js @@ -0,0 +1,3 @@ +import style from './style2.css' + +export default style diff --git a/test/fixtures/css/style2.css b/test/fixtures/css/style2.css new file mode 100644 index 00000000..3bab8470 --- /dev/null +++ b/test/fixtures/css/style2.css @@ -0,0 +1,4 @@ +a { + -x-border-color: blue blue *; + -x-color: * #fafafa; +} diff --git a/test/loader.test.js b/test/loader.test.js index 8f0c194c..c41289eb 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -8,7 +8,10 @@ import { describe('loader', () => { it('should work', async () => { - const compiler = getCompiler('./css/index.js', { plugins: [] }); + const compiler = getCompiler('./css/index.js', { + plugins: [], + config: false, + }); const stats = await compile(compiler); const codeFromBundle = getCodeFromBundle('style.css', stats); @@ -25,7 +28,10 @@ describe('loader', () => { }); }; - const compiler = getCompiler('./css/index.js', { plugins: [plugin()] }); + const compiler = getCompiler('./css/index.js', { + plugins: [plugin()], + config: false, + }); const stats = await compile(compiler); const codeFromBundle = getCodeFromBundle('style.css', stats); @@ -36,7 +42,10 @@ describe('loader', () => { }); it('should emit Syntax Error', async () => { - const compiler = getCompiler('./css/index.js', { parser: 'sugarss' }); + const compiler = getCompiler('./css/index.js', { + parser: 'sugarss', + config: false, + }); const stats = await compile(compiler); expect(getWarnings(stats)).toMatchSnapshot('warnings'); diff --git a/test/options/__snapshots__/plugins.test.js.snap b/test/options/__snapshots__/plugins.test.js.snap index eea69ea2..0d565d0e 100644 --- a/test/options/__snapshots__/plugins.test.js.snap +++ b/test/options/__snapshots__/plugins.test.js.snap @@ -27,6 +27,29 @@ exports[`Options Plugins should work Plugins - {Function} - {Object}: errors 1`] exports[`Options Plugins should work Plugins - {Function} - {Object}: warnings 1`] = `Array []`; +exports[`Options Plugins should work Plugins - {Object without require} + options: css 1`] = ` +"a { + border-top-color: blue; + border-right-color: blue; + border-left-color: blue; + background-color: #fafafa; +} +" +`; + +exports[`Options Plugins should work Plugins - {Object without require} + options: errors 1`] = `Array []`; + +exports[`Options Plugins should work Plugins - {Object without require} + options: warnings 1`] = `Array []`; + +exports[`Options Plugins should work Plugins - {Object without require}: css 1`] = ` +"a { color: rgba(255, 0, 0, 1.0) } +" +`; + +exports[`Options Plugins should work Plugins - {Object without require}: errors 1`] = `Array []`; + +exports[`Options Plugins should work Plugins - {Object without require}: warnings 1`] = `Array []`; + exports[`Options Plugins should work Plugins - {Object}: css 1`] = ` "a { color: rgba(255, 0, 0, 1.0) } " diff --git a/test/options/parser.test.js b/test/options/parser.test.js index c3d8034c..8748bd4a 100644 --- a/test/options/parser.test.js +++ b/test/options/parser.test.js @@ -25,7 +25,7 @@ describe('Options Parser', () => { }, { loader: path.resolve(__dirname, '../../src'), - options: { parser: 'sugarss' }, + options: { parser: 'sugarss', config: false }, }, ], }, @@ -59,7 +59,12 @@ describe('Options Parser', () => { { loader: path.resolve(__dirname, '../../src'), // eslint-disable-next-line global-require - options: { ident: 'postcss', parser: require('sugarss') }, + options: { + ident: 'postcss', + // eslint-disable-next-line global-require,import/no-dynamic-require + parser: require('sugarss'), + config: false, + }, }, ], }, diff --git a/test/options/plugins.test.js b/test/options/plugins.test.js index a53a3d14..6f6a292f 100644 --- a/test/options/plugins.test.js +++ b/test/options/plugins.test.js @@ -66,4 +66,36 @@ describe('Options Plugins', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should work Plugins - {Object without require}', async () => { + const compiler = getCompiler('./css/index.js', { + plugins: { + 'postcss-import': {}, + 'postcss-nested': {}, + }, + }); + const stats = await compile(compiler); + + const codeFromBundle = getCodeFromBundle('style.css', stats); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + + it('should work Plugins - {Object without require} + options', async () => { + const compiler = getCompiler('./css/index2.js', { + plugins: { + 'postcss-short': { prefix: 'x' }, + }, + config: false, + }); + const stats = await compile(compiler); + + const codeFromBundle = getCodeFromBundle('style2.css', stats); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); }); diff --git a/test/options/stringifier.test.js b/test/options/stringifier.test.js index 667aa517..133423cf 100644 --- a/test/options/stringifier.test.js +++ b/test/options/stringifier.test.js @@ -8,7 +8,10 @@ import { describe('Options Stringifier', () => { it('should work Stringifier - {String}', async () => { - const compiler = getCompiler('./css/index.js', { stringifier: 'sugarss' }); + const compiler = getCompiler('./css/index.js', { + stringifier: 'sugarss', + config: false, + }); const stats = await compile(compiler); const codeFromBundle = getCodeFromBundle('style.css', stats); @@ -23,6 +26,7 @@ describe('Options Stringifier', () => { ident: 'postcss', // eslint-disable-next-line global-require stringifier: require('sugarss'), + config: false, }); const stats = await compile(compiler); diff --git a/test/options/syntax.test.js b/test/options/syntax.test.js index 083ed933..cfcb61b4 100644 --- a/test/options/syntax.test.js +++ b/test/options/syntax.test.js @@ -25,7 +25,7 @@ describe('Options Syntax', () => { }, { loader: path.resolve(__dirname, '../../src'), - options: { syntax: 'sugarss' }, + options: { syntax: 'sugarss', config: false }, }, ], }, @@ -62,6 +62,7 @@ describe('Options Syntax', () => { ident: 'postcss', // eslint-disable-next-line global-require syntax: require('sugarss'), + config: false, }, }, ], diff --git a/test/validate-options.test.js b/test/validate-options.test.js index a0757af4..d39cf972 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -44,7 +44,7 @@ describe('validate options', () => { require('./fixtures/config/plugin'), () => [require('./fixtures/config/plugin')()], ], - failure: [1, true, false, /test/], + failure: [1, true, false], }, }; From c28461ab65dbcf67d80b90369c0496d563a63d07 Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Thu, 13 Aug 2020 19:23:34 +0300 Subject: [PATCH 2/6] refactor: code --- src/index.js | 100 +-------------- src/options.js | 15 +-- src/utils.js | 114 +++++++++++++++++- test/fixtures/css/invalid.config.js | 1 + test/loader.test.js | 6 +- .../options/__snapshots__/config.test.js.snap | 21 ++++ .../__snapshots__/plugins.test.js.snap | 37 ++++++ test/options/config.test.js | 20 +++ test/options/plugins.test.js | 43 +++++++ 9 files changed, 242 insertions(+), 115 deletions(-) create mode 100644 test/fixtures/css/invalid.config.js diff --git a/src/index.js b/src/index.js index 77f9a590..645b41fd 100644 --- a/src/index.js +++ b/src/index.js @@ -5,81 +5,11 @@ import validateOptions from 'schema-utils'; import postcss from 'postcss'; -import postcssPkg from 'postcss/package.json'; - import Warning from './Warning'; import SyntaxError from './Error'; import parseOptions from './options'; import schema from './options.json'; -import { exec, loadConfig } from './utils'; - -const load = (plugin, options, file) => { - try { - if ( - options === null || - typeof options === 'undefined' || - Object.keys(options).length === 0 - ) { - // eslint-disable-next-line global-require,import/no-dynamic-require - return require(plugin); - } - - // eslint-disable-next-line global-require,import/no-dynamic-require - return require(plugin)(options); - } catch (err) { - throw new Error( - `Loading PostCSS Plugin failed: ${err.message}\n\n(@${file})` - ); - } -}; - -const loadPlugins = (pluginEntry, file) => { - let plugins = []; - - if (Array.isArray(pluginEntry)) { - plugins = pluginEntry.filter(Boolean); - } else { - plugins = Object.entries(pluginEntry).filter((i) => { - const [, options] = i; - - return options !== false ? pluginEntry : ''; - }); - } - - plugins = plugins.map((plugin) => { - const [pluginName, pluginOptions] = plugin; - - return load(pluginName, pluginOptions, file); - }); - - if (plugins.length && plugins.length > 0) { - plugins.forEach((plugin, i) => { - if (plugin.postcss) { - // eslint-disable-next-line no-param-reassign - plugin = plugin.postcss; - } - - if (plugin.default) { - // eslint-disable-next-line no-param-reassign - plugin = plugin.default; - } - - if ( - // eslint-disable-next-line - !( - (typeof plugin === 'object' && Array.isArray(plugin.plugins)) || - typeof plugin === 'function' - ) - ) { - throw new TypeError( - `Invalid PostCSS Plugin found at: plugins[${i}]\n\n(@${file})` - ); - } - }); - } - - return plugins; -}; +import { exec, loadConfig, createPostCssPlugins } from './utils'; /** * **PostCSS Loader** @@ -182,17 +112,7 @@ export default async function loader(content, sourceMap, meta = {}) { }); if (length) { - try { - config = await parseOptions.call(this, mergedOptions); - } catch (error) { - callback(error); - - return; - } - } - - if (typeof config === 'undefined') { - config = {}; + config = parseOptions.call(this, mergedOptions); } if (config.file) { @@ -261,21 +181,7 @@ export default async function loader(content, sourceMap, meta = {}) { postcssOptions.map.prev = sourceMap; } - let resultPlugins = []; - - for (const plugin of plugins) { - if (plugin === false) { - // eslint-disable-next-line no-continue - continue; - } - if (plugin.postcssVersion === postcssPkg.version) { - resultPlugins.push(plugin); - } else if (typeof plugin === 'function') { - resultPlugins.push(plugin); - } else { - resultPlugins = resultPlugins.concat(loadPlugins(plugin, file)); - } - } + const resultPlugins = createPostCssPlugins(plugins, file); let result; diff --git a/src/options.js b/src/options.js index c227b4b1..4ad723e3 100644 --- a/src/options.js +++ b/src/options.js @@ -14,26 +14,13 @@ * @return {Promise} PostCSS Config */ function parseOptions({ exec, parser, syntax, stringifier, plugins }) { - if (typeof plugins === 'function') { - // eslint-disable-next-line no-param-reassign - plugins = plugins.call(this, this); - } - - if (typeof plugins === 'undefined') { - // eslint-disable-next-line no-param-reassign - plugins = []; - } else if (!Array.isArray(plugins)) { - // eslint-disable-next-line no-param-reassign - plugins = [plugins]; - } - const options = {}; options.parser = parser; options.syntax = syntax; options.stringifier = stringifier; - return Promise.resolve({ options, plugins, exec }); + return { options, plugins, exec }; } module.exports = parseOptions; diff --git a/src/utils.js b/src/utils.js index 2136de6d..fd93bf32 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,6 +2,7 @@ import path from 'path'; import Module from 'module'; +import postcssPkg from 'postcss/package.json'; import { cosmiconfig } from 'cosmiconfig'; const parentModule = module; @@ -31,6 +32,26 @@ const createContext = (context) => { return result; }; +const load = (plugin, options, file) => { + try { + if ( + options === null || + typeof options === 'undefined' || + Object.keys(options).length === 0 + ) { + // eslint-disable-next-line global-require,import/no-dynamic-require + return require(plugin); + } + + // eslint-disable-next-line global-require,import/no-dynamic-require + return require(plugin)(options); + } catch (err) { + throw new Error( + `Loading PostCSS Plugin failed: ${err.message}\n\n(@${file})` + ); + } +}; + const loadOptions = (config, file) => { const result = {}; @@ -70,6 +91,54 @@ const loadOptions = (config, file) => { return { ...config, ...result }; }; +function loadPlugins(pluginEntry, file) { + let plugins = []; + + if (Array.isArray(pluginEntry)) { + plugins = pluginEntry.filter(Boolean); + } else { + plugins = Object.entries(pluginEntry).filter((i) => { + const [, options] = i; + + return options !== false ? pluginEntry : ''; + }); + } + + plugins = plugins.map((plugin) => { + const [pluginName, pluginOptions] = plugin; + + return load(pluginName, pluginOptions, file); + }); + + if (plugins.length && plugins.length > 0) { + plugins.forEach((plugin, i) => { + if (plugin.postcss) { + // eslint-disable-next-line no-param-reassign + plugin = plugin.postcss; + } + + if (plugin.default) { + // eslint-disable-next-line no-param-reassign + plugin = plugin.default; + } + + if ( + // eslint-disable-next-line + !( + (typeof plugin === 'object' && Array.isArray(plugin.plugins)) || + typeof plugin === 'function' + ) + ) { + throw new TypeError( + `Invalid PostCSS Plugin found at: plugins[${i}]\n\n(@${file})` + ); + } + }); + } + + return plugins; +} + function exec(code, loaderContext) { const { resource, context } = loaderContext; @@ -130,11 +199,50 @@ async function loadConfig(config, context, configPath, inputFileSystem) { resultConfig.file = result.filepath || ''; - delete resultConfig.webpack; - const options = loadOptions(resultConfig, resultConfig.file); return { ...resultConfig, ...options }; } -export { exec, loadConfig }; +function createPostCssPlugins(items, file) { + function iterator(plugins, plugin, acc) { + if (typeof plugin === 'undefined') { + return acc; + } + + if (plugin === false) { + return iterator(plugins, plugins.pop(), acc); + } + + if (plugin.postcssVersion === postcssPkg.version) { + acc.push(plugin); + return iterator(plugins, plugins.pop(), acc); + } + + if (typeof plugin === 'function') { + const postcssPlugin = plugin.call(this, this); + + if (Array.isArray(postcssPlugin)) { + acc.concat(postcssPlugin); + } else { + acc.push(postcssPlugin); + } + + return iterator(plugins, plugins.pop(), acc); + } + + if (Object.keys(plugin).length === 0) { + return iterator(plugins, plugins.pop(), acc); + } + + const concatPlugins = [...plugins, ...loadPlugins(plugin, file)]; + + return iterator(concatPlugins, concatPlugins.pop(), acc); + } + + const pl = [...items]; + + return iterator(pl, pl.pop(), []); +} + +export { exec, loadConfig, createPostCssPlugins }; diff --git a/test/fixtures/css/invalid.config.js b/test/fixtures/css/invalid.config.js new file mode 100644 index 00000000..7dd96ea6 --- /dev/null +++ b/test/fixtures/css/invalid.config.js @@ -0,0 +1 @@ +throw new Error('invalid postcss config'); diff --git a/test/loader.test.js b/test/loader.test.js index c41289eb..54c67984 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -1,3 +1,5 @@ +import postcss from 'postcss'; + import { compile, getCompiler, @@ -28,8 +30,10 @@ describe('loader', () => { }); }; + const postcssPlugin = postcss.plugin('postcss-plugin', plugin); + const compiler = getCompiler('./css/index.js', { - plugins: [plugin()], + plugins: [postcssPlugin()], config: false, }); const stats = await compile(compiler); diff --git a/test/options/__snapshots__/config.test.js.snap b/test/options/__snapshots__/config.test.js.snap index 7cd81570..407b84e6 100644 --- a/test/options/__snapshots__/config.test.js.snap +++ b/test/options/__snapshots__/config.test.js.snap @@ -1,5 +1,26 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Config Options should emit error when invalid config : errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +Error: No PostCSS Config found in: /test/fixtures/css/invalid.config.js + at loadConfig (/src/utils.js:183:11)", +] +`; + +exports[`Config Options should emit error when invalid config : warnings 1`] = `Array []`; + +exports[`Config Options should emit error when unresolved config : errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +Error: No PostCSS Config found in: /test/fixtures/css/unresolve.js + at loadConfig (/src/utils.js:169:11) + at processTicksAndRejections (internal/process/task_queues.js:97:5)", +] +`; + +exports[`Config Options should emit error when unresolved config : warnings 1`] = `Array []`; + exports[`Config Options should work Config - "string" with path directory: css 1`] = ` "a { color: rgba(255, 0, 0, 1.0) } " diff --git a/test/options/__snapshots__/plugins.test.js.snap b/test/options/__snapshots__/plugins.test.js.snap index 0d565d0e..25649fd9 100644 --- a/test/options/__snapshots__/plugins.test.js.snap +++ b/test/options/__snapshots__/plugins.test.js.snap @@ -1,5 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Options Plugins should work Plugins - {Array} + options: css 1`] = ` +"a { + border-top-color: blue; + border-right-color: blue; + border-left-color: blue; + background-color: #fafafa; +} +" +`; + +exports[`Options Plugins should work Plugins - {Array} + options: errors 1`] = `Array []`; + +exports[`Options Plugins should work Plugins - {Array} + options: warnings 1`] = `Array []`; + exports[`Options Plugins should work Plugins - {Array}: css 1`] = ` "a { color: rgba(255, 0, 0, 1.0) } " @@ -50,6 +64,20 @@ exports[`Options Plugins should work Plugins - {Object without require}: errors exports[`Options Plugins should work Plugins - {Object without require}: warnings 1`] = `Array []`; +exports[`Options Plugins should work Plugins - {Object} + options: css 1`] = ` +"a { + border-top-color: blue; + border-right-color: blue; + border-left-color: blue; + background-color: #fafafa; +} +" +`; + +exports[`Options Plugins should work Plugins - {Object} + options: errors 1`] = `Array []`; + +exports[`Options Plugins should work Plugins - {Object} + options: warnings 1`] = `Array []`; + exports[`Options Plugins should work Plugins - {Object}: css 1`] = ` "a { color: rgba(255, 0, 0, 1.0) } " @@ -58,3 +86,12 @@ exports[`Options Plugins should work Plugins - {Object}: css 1`] = ` exports[`Options Plugins should work Plugins - {Object}: errors 1`] = `Array []`; exports[`Options Plugins should work Plugins - {Object}: warnings 1`] = `Array []`; + +exports[`Options Plugins should work Plugins - {empty Object}: css 1`] = ` +"a { color: rgba(255, 0, 0, 1.0) } +" +`; + +exports[`Options Plugins should work Plugins - {empty Object}: errors 1`] = `Array []`; + +exports[`Options Plugins should work Plugins - {empty Object}: warnings 1`] = `Array []`; diff --git a/test/options/config.test.js b/test/options/config.test.js index 7ba9f46a..120a723d 100644 --- a/test/options/config.test.js +++ b/test/options/config.test.js @@ -212,4 +212,24 @@ describe('Config Options', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should emit error when unresolved config ', async () => { + const compiler = getCompiler('./css/index.js', { + config: path.resolve(__dirname, '../fixtures/css/unresolve.js'), + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + + it('should emit error when invalid config ', async () => { + const compiler = getCompiler('./css/index.js', { + config: path.resolve(__dirname, '../fixtures/css/invalid.config.js'), + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); }); diff --git a/test/options/plugins.test.js b/test/options/plugins.test.js index 6f6a292f..68fe9a95 100644 --- a/test/options/plugins.test.js +++ b/test/options/plugins.test.js @@ -83,6 +83,19 @@ describe('Options Plugins', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); }); + it('should work Plugins - {empty Object}', async () => { + const compiler = getCompiler('./css/index.js', { + plugins: {}, + }); + const stats = await compile(compiler); + + const codeFromBundle = getCodeFromBundle('style.css', stats); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + it('should work Plugins - {Object without require} + options', async () => { const compiler = getCompiler('./css/index2.js', { plugins: { @@ -98,4 +111,34 @@ describe('Options Plugins', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should work Plugins - {Object} + options', async () => { + const compiler = getCompiler('./css/index2.js', { + // eslint-disable-next-line global-require + plugins: require('postcss-short')({ prefix: 'x' }), + config: false, + }); + const stats = await compile(compiler); + + const codeFromBundle = getCodeFromBundle('style2.css', stats); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + + it('should work Plugins - {Array} + options', async () => { + const compiler = getCompiler('./css/index2.js', { + // eslint-disable-next-line global-require + plugins: [require('postcss-short')({ prefix: 'x' })], + config: false, + }); + const stats = await compile(compiler); + + const codeFromBundle = getCodeFromBundle('style2.css', stats); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); }); From 8de6d55cbc2219ac1c3cd29819bcf45ae1a4e3a0 Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Fri, 14 Aug 2020 13:11:45 +0300 Subject: [PATCH 3/6] refactor: remove loadOptions from load config --- src/index.js | 94 +++++++------ src/utils.js | 56 ++------ test/config-autoload.test.js | 129 ++---------------- .../options/__snapshots__/config.test.js.snap | 4 +- 4 files changed, 74 insertions(+), 209 deletions(-) diff --git a/src/index.js b/src/index.js index 645b41fd..cdfe6d9b 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,18 @@ import parseOptions from './options'; import schema from './options.json'; import { exec, loadConfig, createPostCssPlugins } from './utils'; +function pluginsToArray(plugins) { + if (typeof plugins === 'undefined') { + return []; + } + + if (Array.isArray(plugins)) { + return plugins; + } + + return [plugins]; +} + /** * **PostCSS Loader** * @@ -34,17 +46,13 @@ export default async function loader(content, sourceMap, meta = {}) { const callback = this.async(); const file = this.resourcePath; - let configRc = {}; + let loadedConfig = {}; - options.config = - options.config === false - ? options.config - : typeof options.config !== 'undefined' - ? options.config - : true; + const configOptions = + typeof options.config === 'undefined' ? true : options.config; - if (options.config) { - const rc = { + if (configOptions) { + const dataForLoadConfig = { path: path.dirname(file), ctx: { file: { @@ -56,19 +64,23 @@ export default async function loader(content, sourceMap, meta = {}) { }, }; - if (typeof options.config.path !== 'undefined') { - rc.path = path.resolve(options.config.path); + if (typeof configOptions.path !== 'undefined') { + dataForLoadConfig.path = path.resolve(configOptions.path); } - if (typeof options.config.ctx !== 'undefined') { - rc.ctx.options = options.config.ctx; + if (typeof configOptions.ctx !== 'undefined') { + dataForLoadConfig.ctx.options = configOptions.ctx; } - rc.ctx.webpack = this; + dataForLoadConfig.ctx.webpack = this; try { - configRc = await loadConfig(options.config, rc.ctx, rc.path, this.fs); - delete options.config; + loadedConfig = await loadConfig( + configOptions, + dataForLoadConfig.ctx, + dataForLoadConfig.path, + this + ); } catch (error) { callback(error); @@ -76,23 +88,11 @@ export default async function loader(content, sourceMap, meta = {}) { } } - function pluginsToArray(plugins) { - if (typeof plugins === 'undefined') { - return []; - } - - if (Array.isArray(plugins)) { - return plugins; - } - - return [plugins]; - } - const mergedOptions = { - ...configRc, + ...loadedConfig, ...options, plugins: [ - ...pluginsToArray(configRc.plugins), + ...pluginsToArray(loadedConfig.plugins), ...pluginsToArray(options.plugins), ], }; @@ -115,10 +115,6 @@ export default async function loader(content, sourceMap, meta = {}) { config = parseOptions.call(this, mergedOptions); } - if (config.file) { - this.addDependency(config.file); - } - if (typeof config.options !== 'undefined') { if (typeof config.options.to !== 'undefined') { delete config.options.to; @@ -151,18 +147,36 @@ export default async function loader(content, sourceMap, meta = {}) { } if (typeof postcssOptions.parser === 'string') { - // eslint-disable-next-line import/no-dynamic-require,global-require - postcssOptions.parser = require(postcssOptions.parser); + try { + // eslint-disable-next-line import/no-dynamic-require,global-require + postcssOptions.parser = require(postcssOptions.parser); + } catch (error) { + throw new Error( + `Loading PostCSS Parser failed: ${error.message}\n\n(@${file})` + ); + } } if (typeof postcssOptions.syntax === 'string') { - // eslint-disable-next-line import/no-dynamic-require,global-require - postcssOptions.syntax = require(postcssOptions.syntax); + try { + // eslint-disable-next-line import/no-dynamic-require,global-require + postcssOptions.syntax = require(postcssOptions.syntax); + } catch (error) { + throw new Error( + `Loading PostCSS Syntax failed: ${error.message}\n\n(@${file})` + ); + } } if (typeof postcssOptions.stringifier === 'string') { - // eslint-disable-next-line import/no-dynamic-require,global-require - postcssOptions.stringifier = require(postcssOptions.stringifier); + try { + // eslint-disable-next-line import/no-dynamic-require,global-require + postcssOptions.stringifier = require(postcssOptions.stringifier); + } catch (error) { + throw new Error( + `Loading PostCSS Stringifier failed: ${error.message}\n\n(@${file})` + ); + } } // Loader API Exec (Deprecated) diff --git a/src/utils.js b/src/utils.js index fd93bf32..1500715d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -45,52 +45,13 @@ const load = (plugin, options, file) => { // eslint-disable-next-line global-require,import/no-dynamic-require return require(plugin)(options); - } catch (err) { + } catch (error) { throw new Error( - `Loading PostCSS Plugin failed: ${err.message}\n\n(@${file})` + `Loading PostCSS Plugin failed: ${error.message}\n\n(@${file})` ); } }; -const loadOptions = (config, file) => { - const result = {}; - - if (config.parser && typeof config.parser === 'string') { - try { - // eslint-disable-next-line global-require,import/no-dynamic-require - result.parser = require(config.parser); - } catch (err) { - throw new Error( - `Loading PostCSS Parser failed: ${err.message}\n\n(@${file})` - ); - } - } - - if (config.syntax && typeof config.syntax === 'string') { - try { - // eslint-disable-next-line global-require,import/no-dynamic-require - result.syntax = require(config.syntax); - } catch (err) { - throw new Error( - `Loading PostCSS Syntax failed: ${err.message}\n\n(@${file})` - ); - } - } - - if (config.stringifier && typeof config.stringifier === 'string') { - try { - // eslint-disable-next-line global-require,import/no-dynamic-require - result.stringifier = require(config.stringifier); - } catch (err) { - throw new Error( - `Loading PostCSS Stringifier failed: ${err.message}\n\n(@${file})` - ); - } - } - - return { ...config, ...result }; -}; - function loadPlugins(pluginEntry, file) { let plugins = []; @@ -154,7 +115,7 @@ function exec(code, loaderContext) { return module.exports; } -async function loadConfig(config, context, configPath, inputFileSystem) { +async function loadConfig(config, context, configPath, loaderContext) { let searchPath = configPath ? path.resolve(configPath) : process.cwd(); if (typeof config === 'string') { @@ -164,7 +125,7 @@ async function loadConfig(config, context, configPath, inputFileSystem) { let stats; try { - stats = await stat(inputFileSystem, searchPath); + stats = await stat(loaderContext.fs, searchPath); } catch (errorIgnore) { throw new Error(`No PostCSS Config found in: ${searchPath}`); } @@ -197,11 +158,12 @@ async function loadConfig(config, context, configPath, inputFileSystem) { resultConfig.plugins = []; } - resultConfig.file = result.filepath || ''; - - const options = loadOptions(resultConfig, resultConfig.file); + if (result.filepath) { + resultConfig.file = result.filepath; + loaderContext.addDependency(result.filepath); + } - return { ...resultConfig, ...options }; + return resultConfig; } function createPostCssPlugins(items, file) { diff --git a/test/config-autoload.test.js b/test/config-autoload.test.js index 46f9f70c..9c9c795b 100644 --- a/test/config-autoload.test.js +++ b/test/config-autoload.test.js @@ -7,6 +7,11 @@ import { loadConfig } from '../src/utils'; const testDirectory = path.resolve(__dirname, 'fixtures', 'config-autoload'); +const loaderContext = { + fs, + addDependency: () => true, +}; + describe('config-autoload', () => { const ctx = { parser: true, @@ -15,8 +20,6 @@ describe('config-autoload', () => { it('.postcssrc - {Object} - Load Config', async () => { const expected = (config) => { - expect(config.parser).toEqual(require('sugarss')); - expect(config.syntax).toEqual(require('sugarss')); expect(config.map).toEqual(false); expect(config.from).toEqual('./test/rc/fixtures/index.css'); expect(config.to).toEqual('./test/rc/expect/index.css'); @@ -32,7 +35,7 @@ describe('config-autoload', () => { true, {}, path.resolve(testDirectory, 'rc'), - fs + loaderContext ); expected(config); @@ -40,8 +43,6 @@ describe('config-autoload', () => { it('postcss.config.js - {Object} - Load Config', async () => { const expected = (config) => { - expect(config.parser).toEqual(require('sugarss')); - expect(config.syntax).toEqual(require('sugarss')); expect(config.map).toEqual(false); expect(config.from).toEqual('./test/js/object/fixtures/index.css'); expect(config.to).toEqual('./test/js/object/expect/index.css'); @@ -57,7 +58,7 @@ describe('config-autoload', () => { true, ctx, path.resolve(testDirectory, 'js/object'), - fs + loaderContext ); expected(config); @@ -65,8 +66,6 @@ describe('config-autoload', () => { it('postcss.config.js - {Array} - Load Config', async () => { const expected = (config) => { - expect(config.parser).toEqual(require('sugarss')); - expect(config.syntax).toEqual(require('sugarss')); expect(config.map).toEqual(false); expect(config.from).toEqual('./test/js/array/fixtures/index.css'); expect(config.to).toEqual('./test/js/array/expect/index.css'); @@ -82,7 +81,7 @@ describe('config-autoload', () => { true, ctx, path.resolve(testDirectory, 'js/array'), - fs + loaderContext ); expected(config); @@ -106,7 +105,7 @@ describe('config-autoload', () => { true, {}, path.resolve(testDirectory, 'pkg'), - fs + loaderContext ); expected(config); @@ -119,114 +118,4 @@ describe('config-autoload', () => { expect(error.message).toMatch(/^No PostCSS Config found in: (.*)$/); } }); - - it('Plugin - {Type} - Invalid', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/plugins'), - fs - ); - } catch (error) { - expect(error.message).toMatch( - /^Invalid PostCSS Plugin found at: (.*)\n\n\(@.*\)$/ - ); - } - }); - - it('Loading Plugin - {Object} - {Error}', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/plugins/object'), - fs - ); - } catch (error) { - expect(error.message).toMatch(/^Loading PostCSS Plugin failed: .*$/m); - } - }); - - it('Loading Plugin - {Object} - Options - {Error}', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/plugins/object/options'), - fs - ); - } catch (error) { - expect(error.message).toMatch(/^Loading PostCSS Plugin failed: .*$/m); - } - }); - - it('Loading Plugin - {Array} - {Error}', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/plugins/array'), - fs - ); - } catch (error) { - expect(error.message).toMatch(/^Cannot find (.*)$/); - } - }); - - it('Loading Plugin - {Array} - Options - {Error}', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/plugins/array/options'), - fs - ); - } catch (error) { - expect(error.message).toMatch(/^Cannot find (.*)$/); - } - }); -}); - -describe('Loading Options - {Error}', () => { - it('Parser - {String}', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/options/parser'), - fs - ); - } catch (error) { - expect(error.message).toMatch(/^Loading PostCSS Parser failed: .*$/m); - } - }); - - it('Syntax - {String}', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/options/syntax'), - fs - ); - } catch (error) { - expect(error.message).toMatch(/^Loading PostCSS Syntax failed: .*$/m); - } - }); - - it('Stringifier - {String}', async () => { - try { - await loadConfig( - true, - {}, - path.resolve(testDirectory, 'err/options/stringifier'), - fs - ); - } catch (error) { - expect(error.message).toMatch( - /^Loading PostCSS Stringifier failed: .*$/m - ); - } - }); }); diff --git a/test/options/__snapshots__/config.test.js.snap b/test/options/__snapshots__/config.test.js.snap index 407b84e6..d8a56334 100644 --- a/test/options/__snapshots__/config.test.js.snap +++ b/test/options/__snapshots__/config.test.js.snap @@ -4,7 +4,7 @@ exports[`Config Options should emit error when invalid config : errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): Error: No PostCSS Config found in: /test/fixtures/css/invalid.config.js - at loadConfig (/src/utils.js:183:11)", + at loadConfig (/src/utils.js:144:11)", ] `; @@ -14,7 +14,7 @@ exports[`Config Options should emit error when unresolved config : errors 1`] = Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): Error: No PostCSS Config found in: /test/fixtures/css/unresolve.js - at loadConfig (/src/utils.js:169:11) + at loadConfig (/src/utils.js:130:11) at processTicksAndRejections (internal/process/task_queues.js:97:5)", ] `; From fed0ab9b43147a7450c076fd22d9aa009c8f91c3 Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Fri, 14 Aug 2020 16:19:33 +0300 Subject: [PATCH 4/6] refactor: process plugins --- src/index.js | 77 ++++--------------- src/options.js | 26 ------- src/utils.js | 60 ++++++--------- .../options/__snapshots__/config.test.js.snap | 7 +- test/options/config.test.js | 4 +- 5 files changed, 46 insertions(+), 128 deletions(-) delete mode 100644 src/options.js diff --git a/src/index.js b/src/index.js index cdfe6d9b..26a2abc6 100644 --- a/src/index.js +++ b/src/index.js @@ -7,21 +7,8 @@ import postcss from 'postcss'; import Warning from './Warning'; import SyntaxError from './Error'; -import parseOptions from './options'; import schema from './options.json'; -import { exec, loadConfig, createPostCssPlugins } from './utils'; - -function pluginsToArray(plugins) { - if (typeof plugins === 'undefined') { - return []; - } - - if (Array.isArray(plugins)) { - return plugins; - } - - return [plugins]; -} +import { exec, loadConfig, getArrayPlugins } from './utils'; /** * **PostCSS Loader** @@ -92,52 +79,26 @@ export default async function loader(content, sourceMap, meta = {}) { ...loadedConfig, ...options, plugins: [ - ...pluginsToArray(loadedConfig.plugins), - ...pluginsToArray(options.plugins), + ...getArrayPlugins(loadedConfig.plugins, file), + ...getArrayPlugins(options.plugins, file), ], }; - let config; - - const { length } = Object.keys(mergedOptions).filter((option) => { - switch (option) { - // case 'exec': - // case 'ident': - case 'config': - case 'sourceMap': - return false; - default: - return option; - } - }); + const resultPlugins = mergedOptions.plugins; - if (length) { - config = parseOptions.call(this, mergedOptions); - } + const { parser, syntax, stringifier } = mergedOptions; - if (typeof config.options !== 'undefined') { - if (typeof config.options.to !== 'undefined') { - delete config.options.to; - } - - if (typeof config.options.from !== 'undefined') { - delete config.options.from; - } - } - - const plugins = config.plugins || []; - - const postcssOptions = Object.assign( - { - from: file, - map: options.sourceMap - ? options.sourceMap === 'inline' - ? { inline: true, annotation: false } - : { inline: false, annotation: false } - : false, - }, - config.options - ); + const postcssOptions = { + from: file, + map: options.sourceMap + ? options.sourceMap === 'inline' + ? { inline: true, annotation: false } + : { inline: false, annotation: false } + : false, + parser, + syntax, + stringifier, + }; // Loader Exec (Deprecated) // https://webpack.js.org/api/loaders/#deprecated-context-properties @@ -181,7 +142,7 @@ export default async function loader(content, sourceMap, meta = {}) { // Loader API Exec (Deprecated) // https://webpack.js.org/api/loaders/#deprecated-context-properties - if (config.exec) { + if (mergedOptions.exec) { // eslint-disable-next-line no-param-reassign content = exec(content, this); } @@ -195,8 +156,6 @@ export default async function loader(content, sourceMap, meta = {}) { postcssOptions.map.prev = sourceMap; } - const resultPlugins = createPostCssPlugins(plugins, file); - let result; try { @@ -268,9 +227,7 @@ export default async function loader(content, sourceMap, meta = {}) { * @requires schema-utils * * @requires postcss - * @requires postcss-load-config * - * @requires ./options.js * @requires ./Warning.js * @requires ./SyntaxError.js */ diff --git a/src/options.js b/src/options.js deleted file mode 100644 index 4ad723e3..00000000 --- a/src/options.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * **PostCSS Options Parser** - * - * Transforms the loader options into a valid postcss config `{Object}` - * - * @method parseOptions - * - * @param {Boolean} exec Execute CSS-in-JS - * @param {String|Object} parser PostCSS Parser - * @param {String|Object} syntax PostCSS Syntax - * @param {String|Object} stringifier PostCSS Stringifier - * @param {Array|Object|Function} plugins PostCSS Plugins - * - * @return {Promise} PostCSS Config - */ -function parseOptions({ exec, parser, syntax, stringifier, plugins }) { - const options = {}; - - options.parser = parser; - options.syntax = syntax; - options.stringifier = stringifier; - - return { options, plugins, exec }; -} - -module.exports = parseOptions; diff --git a/src/utils.js b/src/utils.js index 1500715d..6d19f970 100644 --- a/src/utils.js +++ b/src/utils.js @@ -151,11 +151,7 @@ async function loadConfig(config, context, configPath, loaderContext) { if (typeof resultConfig === 'function') { resultConfig = resultConfig(patchedContext); } else { - resultConfig = Object.assign({}, resultConfig, patchedContext); - } - - if (!resultConfig.plugins) { - resultConfig.plugins = []; + resultConfig = { ...resultConfig, ...patchedContext }; } if (result.filepath) { @@ -166,45 +162,39 @@ async function loadConfig(config, context, configPath, loaderContext) { return resultConfig; } -function createPostCssPlugins(items, file) { - function iterator(plugins, plugin, acc) { - if (typeof plugin === 'undefined') { - return acc; - } +function getPlugin(pluginEntry) { + if (!pluginEntry) { + return []; + } - if (plugin === false) { - return iterator(plugins, plugins.pop(), acc); - } + if (pluginEntry.postcssVersion === postcssPkg.version) { + return [pluginEntry]; + } - if (plugin.postcssVersion === postcssPkg.version) { - acc.push(plugin); - return iterator(plugins, plugins.pop(), acc); - } + const result = pluginEntry.call(this, this); - if (typeof plugin === 'function') { - const postcssPlugin = plugin.call(this, this); + return Array.isArray(result) ? result : [result]; +} - if (Array.isArray(postcssPlugin)) { - acc.concat(postcssPlugin); - } else { - acc.push(postcssPlugin); - } +function getArrayPlugins(plugins, file) { + if (Array.isArray(plugins)) { + return plugins.reduce((accumulator, plugin) => { + // eslint-disable-next-line no-param-reassign + accumulator = accumulator.concat(getArrayPlugins(plugin)); - return iterator(plugins, plugins.pop(), acc); - } + return accumulator; + }, []); + } - if (Object.keys(plugin).length === 0) { - return iterator(plugins, plugins.pop(), acc); + if (typeof plugins === 'object' && typeof plugins !== 'function') { + if (Object.keys(plugins).length === 0) { + return []; } - const concatPlugins = [...plugins, ...loadPlugins(plugin, file)]; - - return iterator(concatPlugins, concatPlugins.pop(), acc); + return getArrayPlugins(loadPlugins(plugins, file), file); } - const pl = [...items]; - - return iterator(pl, pl.pop(), []); + return getPlugin(plugins); } -export { exec, loadConfig, createPostCssPlugins }; +export { exec, loadConfig, getArrayPlugins }; diff --git a/test/options/__snapshots__/config.test.js.snap b/test/options/__snapshots__/config.test.js.snap index d8a56334..6883a980 100644 --- a/test/options/__snapshots__/config.test.js.snap +++ b/test/options/__snapshots__/config.test.js.snap @@ -3,8 +3,7 @@ exports[`Config Options should emit error when invalid config : errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): -Error: No PostCSS Config found in: /test/fixtures/css/invalid.config.js - at loadConfig (/src/utils.js:144:11)", +Error: No PostCSS Config found in: /test/fixtures/css/invalid.config.js", ] `; @@ -13,9 +12,7 @@ exports[`Config Options should emit error when invalid config : warnings 1`] = ` exports[`Config Options should emit error when unresolved config : errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): -Error: No PostCSS Config found in: /test/fixtures/css/unresolve.js - at loadConfig (/src/utils.js:130:11) - at processTicksAndRejections (internal/process/task_queues.js:97:5)", +Error: No PostCSS Config found in: /test/fixtures/css/unresolve.js", ] `; diff --git a/test/options/config.test.js b/test/options/config.test.js index 120a723d..033cd7a8 100644 --- a/test/options/config.test.js +++ b/test/options/config.test.js @@ -220,7 +220,7 @@ describe('Config Options', () => { const stats = await compile(compiler); expect(getWarnings(stats)).toMatchSnapshot('warnings'); - expect(getErrors(stats)).toMatchSnapshot('errors'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); }); it('should emit error when invalid config ', async () => { @@ -230,6 +230,6 @@ describe('Config Options', () => { const stats = await compile(compiler); expect(getWarnings(stats)).toMatchSnapshot('warnings'); - expect(getErrors(stats)).toMatchSnapshot('errors'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); }); }); From e45092a8b4bfd7c921fa5c3ce6011b425eff3b71 Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Fri, 14 Aug 2020 17:11:53 +0300 Subject: [PATCH 5/6] test: code --- src/index.js | 6 ++-- test/options/__snapshots__/exec.test.js.snap | 5 +-- .../options/__snapshots__/parser.test.js.snap | 11 +++++++ .../__snapshots__/stringifier.test.js.snap | 11 +++++++ .../options/__snapshots__/syntax.test.js.snap | 11 +++++++ test/options/exec.test.js | 2 +- test/options/parser.test.js | 31 +++++++++++++++++++ test/options/stringifier.test.js | 13 ++++++++ test/options/syntax.test.js | 30 ++++++++++++++++++ 9 files changed, 114 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 26a2abc6..68580316 100644 --- a/src/index.js +++ b/src/index.js @@ -112,7 +112,7 @@ export default async function loader(content, sourceMap, meta = {}) { // eslint-disable-next-line import/no-dynamic-require,global-require postcssOptions.parser = require(postcssOptions.parser); } catch (error) { - throw new Error( + this.emitError( `Loading PostCSS Parser failed: ${error.message}\n\n(@${file})` ); } @@ -123,7 +123,7 @@ export default async function loader(content, sourceMap, meta = {}) { // eslint-disable-next-line import/no-dynamic-require,global-require postcssOptions.syntax = require(postcssOptions.syntax); } catch (error) { - throw new Error( + this.emitError( `Loading PostCSS Syntax failed: ${error.message}\n\n(@${file})` ); } @@ -134,7 +134,7 @@ export default async function loader(content, sourceMap, meta = {}) { // eslint-disable-next-line import/no-dynamic-require,global-require postcssOptions.stringifier = require(postcssOptions.stringifier); } catch (error) { - throw new Error( + this.emitError( `Loading PostCSS Stringifier failed: ${error.message}\n\n(@${file})` ); } diff --git a/test/options/__snapshots__/exec.test.js.snap b/test/options/__snapshots__/exec.test.js.snap index 61611464..edc3fde8 100644 --- a/test/options/__snapshots__/exec.test.js.snap +++ b/test/options/__snapshots__/exec.test.js.snap @@ -11,8 +11,9 @@ exports[`Options Exec should work Exec - {Boolean}: errors 1`] = `Array []`; exports[`Options Exec should work Exec - {Boolean}: warnings 1`] = `Array []`; exports[`Options Exec should work JSS - {String}: css 1`] = ` -"{ a: { color: 'yellow' } } -" +"a { + color: yellow +}" `; exports[`Options Exec should work JSS - {String}: errors 1`] = `Array []`; diff --git a/test/options/__snapshots__/parser.test.js.snap b/test/options/__snapshots__/parser.test.js.snap index e0e4b314..86684afc 100644 --- a/test/options/__snapshots__/parser.test.js.snap +++ b/test/options/__snapshots__/parser.test.js.snap @@ -1,5 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Options Parser should emit error Parser: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +TypeError: parser is not a function", + "ModuleError: Module Error (from \`replaced original path\`): +(Emitted value instead of an instance of Error) Loading PostCSS Parser failed: Cannot find module 'unresolve' from 'src/index.js'", +] +`; + +exports[`Options Parser should emit error Parser: warnings 1`] = `Array []`; + exports[`Options Parser should work Parser - {Object}: css 1`] = ` "a { color: black diff --git a/test/options/__snapshots__/stringifier.test.js.snap b/test/options/__snapshots__/stringifier.test.js.snap index 53afa6a2..2d229b46 100644 --- a/test/options/__snapshots__/stringifier.test.js.snap +++ b/test/options/__snapshots__/stringifier.test.js.snap @@ -1,5 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Options Stringifier should emit error Stringifier: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +TypeError: this.stringify is not a function", + "ModuleError: Module Error (from \`replaced original path\`): +(Emitted value instead of an instance of Error) Loading PostCSS Stringifier failed: Cannot find module 'unresolved' from 'src/index.js'", +] +`; + +exports[`Options Stringifier should emit error Stringifier: warnings 1`] = `Array []`; + exports[`Options Stringifier should work Stringifier - {Object}: css 1`] = ` "a color: black " diff --git a/test/options/__snapshots__/syntax.test.js.snap b/test/options/__snapshots__/syntax.test.js.snap index 0a56f772..80bcdaff 100644 --- a/test/options/__snapshots__/syntax.test.js.snap +++ b/test/options/__snapshots__/syntax.test.js.snap @@ -1,5 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Options Syntax should emit error Syntax: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +TypeError: Cannot read property 'parse' of undefined", + "ModuleError: Module Error (from \`replaced original path\`): +(Emitted value instead of an instance of Error) Loading PostCSS Syntax failed: Cannot find module 'unresolve' from 'src/index.js'", +] +`; + +exports[`Options Syntax should emit error Syntax: warnings 1`] = `Array []`; + exports[`Options Syntax should work Syntax - {Object}: css 1`] = ` "a color: black diff --git a/test/options/exec.test.js b/test/options/exec.test.js index 179a8761..39be2ecf 100644 --- a/test/options/exec.test.js +++ b/test/options/exec.test.js @@ -50,7 +50,7 @@ describe('Options Exec', () => { module: { rules: [ { - test: /style.\.js$/i, + test: /style\.js$/i, use: [ { loader: require.resolve('../helpers/testLoader'), diff --git a/test/options/parser.test.js b/test/options/parser.test.js index 8748bd4a..21a94572 100644 --- a/test/options/parser.test.js +++ b/test/options/parser.test.js @@ -80,4 +80,35 @@ describe('Options Parser', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should emit error Parser', async () => { + const compiler = getCompiler( + './sss/index.js', + {}, + { + module: { + rules: [ + { + test: /\.sss$/i, + use: [ + { + loader: require.resolve('../helpers/testLoader'), + options: {}, + }, + { + loader: path.resolve(__dirname, '../../src'), + options: { parser: 'unresolve', config: false }, + }, + ], + }, + ], + }, + } + ); + + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); + }); }); diff --git a/test/options/stringifier.test.js b/test/options/stringifier.test.js index 133423cf..efb1b6db 100644 --- a/test/options/stringifier.test.js +++ b/test/options/stringifier.test.js @@ -36,4 +36,17 @@ describe('Options Stringifier', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should emit error Stringifier', async () => { + const compiler = getCompiler('./css/index.js', { + ident: 'postcss', + // eslint-disable-next-line global-require + stringifier: 'unresolved', + config: false, + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); + }); }); diff --git a/test/options/syntax.test.js b/test/options/syntax.test.js index cfcb61b4..d3c6a9b8 100644 --- a/test/options/syntax.test.js +++ b/test/options/syntax.test.js @@ -80,4 +80,34 @@ describe('Options Syntax', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should emit error Syntax', async () => { + const compiler = getCompiler( + './sss/index.js', + {}, + { + module: { + rules: [ + { + test: /\.sss$/i, + use: [ + { + loader: require.resolve('../helpers/testLoader'), + options: {}, + }, + { + loader: path.resolve(__dirname, '../../src'), + options: { syntax: 'unresolve', config: false }, + }, + ], + }, + ], + }, + } + ); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); + }); }); From 1e4cd69183b38931fc16fffc626246000b00ad4d Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Fri, 14 Aug 2020 18:10:05 +0300 Subject: [PATCH 6/6] test: code --- src/index.js | 16 ++++++++--- src/utils.js | 27 ++++++------------- .../__snapshots__/plugins.test.js.snap | 11 ++++++++ test/options/plugins.test.js | 12 +++++++++ 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/index.js b/src/index.js index 68580316..d694eb01 100644 --- a/src/index.js +++ b/src/index.js @@ -75,13 +75,21 @@ export default async function loader(content, sourceMap, meta = {}) { } } + let plugins; + + try { + plugins = [ + ...getArrayPlugins(loadedConfig.plugins, file), + ...getArrayPlugins(options.plugins, file), + ]; + } catch (error) { + this.emitError(error); + } + const mergedOptions = { ...loadedConfig, ...options, - plugins: [ - ...getArrayPlugins(loadedConfig.plugins, file), - ...getArrayPlugins(options.plugins, file), - ], + plugins, }; const resultPlugins = mergedOptions.plugins; diff --git a/src/utils.js b/src/utils.js index 6d19f970..1ec7d0ec 100644 --- a/src/utils.js +++ b/src/utils.js @@ -53,31 +53,20 @@ const load = (plugin, options, file) => { }; function loadPlugins(pluginEntry, file) { - let plugins = []; + const plugins = Object.entries(pluginEntry).filter((i) => { + const [, options] = i; - if (Array.isArray(pluginEntry)) { - plugins = pluginEntry.filter(Boolean); - } else { - plugins = Object.entries(pluginEntry).filter((i) => { - const [, options] = i; - - return options !== false ? pluginEntry : ''; - }); - } + return options !== false ? pluginEntry : ''; + }); - plugins = plugins.map((plugin) => { + const loadedPlugins = plugins.map((plugin) => { const [pluginName, pluginOptions] = plugin; return load(pluginName, pluginOptions, file); }); - if (plugins.length && plugins.length > 0) { - plugins.forEach((plugin, i) => { - if (plugin.postcss) { - // eslint-disable-next-line no-param-reassign - plugin = plugin.postcss; - } - + if (loadedPlugins.length && loadedPlugins.length > 0) { + loadedPlugins.forEach((plugin, i) => { if (plugin.default) { // eslint-disable-next-line no-param-reassign plugin = plugin.default; @@ -97,7 +86,7 @@ function loadPlugins(pluginEntry, file) { }); } - return plugins; + return loadedPlugins; } function exec(code, loaderContext) { diff --git a/test/options/__snapshots__/plugins.test.js.snap b/test/options/__snapshots__/plugins.test.js.snap index 25649fd9..d2b9cffe 100644 --- a/test/options/__snapshots__/plugins.test.js.snap +++ b/test/options/__snapshots__/plugins.test.js.snap @@ -1,5 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Options Plugins should emit error on load plugin: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +TypeError: Cannot read property 'postcss' of undefined", + "ModuleError: Module Error (from \`replaced original path\`): +Loading PostCSS Plugin failed: Cannot find module 'postcss-unresolved' from 'src/utils.js'", +] +`; + +exports[`Options Plugins should emit error on load plugin: warnings 1`] = `Array []`; + exports[`Options Plugins should work Plugins - {Array} + options: css 1`] = ` "a { border-top-color: blue; diff --git a/test/options/plugins.test.js b/test/options/plugins.test.js index 68fe9a95..9a8516c6 100644 --- a/test/options/plugins.test.js +++ b/test/options/plugins.test.js @@ -141,4 +141,16 @@ describe('Options Plugins', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should emit error on load plugin', async () => { + const compiler = getCompiler('./css/index2.js', { + plugins: { + 'postcss-unresolved': {}, + }, + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats, true)).toMatchSnapshot('errors'); + }); });