diff --git a/client/package-lock.json b/client/package-lock.json index 26908f9..14cd0ca 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,6 +8,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, "requires": { "es6-promisify": "^5.0.0" } @@ -16,6 +17,7 @@ "version": "6.10.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -27,6 +29,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -34,32 +37,38 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, "requires": { "tweetnacl": "^0.14.3" } @@ -68,6 +77,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -76,22 +86,26 @@ "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -99,22 +113,26 @@ "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -123,6 +141,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -130,17 +149,20 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -149,12 +171,14 @@ "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true }, "es6-promisify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, "requires": { "es6-promise": "^4.0.3" } @@ -162,37 +186,44 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -202,12 +233,14 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -216,6 +249,7 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -228,17 +262,20 @@ "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -247,17 +284,20 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true }, "http-proxy-agent": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, "requires": { "agent-base": "4", "debug": "3.1.0" @@ -267,6 +307,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -277,6 +318,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "dev": true, "requires": { "agent-base": "^4.3.0", "debug": "^3.1.0" @@ -286,6 +328,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -294,42 +337,50 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -340,12 +391,14 @@ "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true }, "mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, "requires": { "mime-db": "1.40.0" } @@ -354,6 +407,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -361,12 +415,14 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, "requires": { "minimist": "0.0.8" } @@ -375,6 +431,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, "requires": { "browser-stdout": "1.3.1", "commander": "2.15.1", @@ -393,6 +450,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -407,17 +465,20 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -425,37 +486,44 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, "psl": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.0.tgz", - "integrity": "sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag==" + "integrity": "sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag==", + "dev": true }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true }, "querystringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -482,32 +550,38 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true }, "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -517,6 +591,7 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -533,6 +608,7 @@ "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -541,6 +617,7 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -549,7 +626,8 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true } } }, @@ -557,6 +635,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -564,12 +643,14 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, "requires": { "punycode": "^2.1.0" } @@ -578,6 +659,7 @@ "version": "1.4.7", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -586,12 +668,14 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -602,6 +686,7 @@ "version": "1.1.36", "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.36.tgz", "integrity": "sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ==", + "dev": true, "requires": { "glob": "^7.1.2", "mocha": "^5.2.0", @@ -643,6 +728,7 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-0.4.3.tgz", "integrity": "sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==", + "dev": true, "requires": { "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.1" @@ -651,7 +737,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true } } } diff --git a/client/package.json b/client/package.json index 65ec046..5c35c08 100644 --- a/client/package.json +++ b/client/package.json @@ -19,9 +19,9 @@ "compile": "tsc -p ./" }, "dependencies": { - "vscode-languageclient": "^4.4.2" + "vscode-languageclient": "^4.4.2" }, "devDependencies": { - "vscode": "^1.1.36" + "vscode": "^1.1.36" } } diff --git a/server/package-lock.json b/server/package-lock.json index 5c6a300..daafc86 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -4,6 +4,14 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/adm-zip": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.32.tgz", + "integrity": "sha512-hv1O7ySn+XvP5OeDQcJFWwVb2v+GFGO1A9aMTQ5B/bzxb7WW21O8iRhVdsKKr8QwuiagzGmPP+gsUAYZ6bRddQ==", + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "12.7.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.1.tgz", @@ -17,27 +25,10 @@ "@types/node": "*" } }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", - "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - } - }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" - }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", - "requires": { - "traverse": ">=0.3.0 <0.4" - } + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==" }, "error-stack-parser": { "version": "1.3.6", @@ -47,19 +38,6 @@ "stackframe": "^0.3.1" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", @@ -109,11 +87,6 @@ "stacktrace-gps": "^2.4.3" } }, - "traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" - }, "typescript-logging": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/typescript-logging/-/typescript-logging-0.6.3.tgz", @@ -122,15 +95,6 @@ "stacktrace-js": "1.3.1" } }, - "unzip-stream": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.0.tgz", - "integrity": "sha512-NG1h/MdGIX3HzyqMjyj1laBCmlPYhcO4xEy7gEqqzGiSLw7XqDQCnY4nYSn5XSaH8mQ6TFkaujrO8d/PIZN85A==", - "requires": { - "binary": "^0.3.0", - "mkdirp": "^0.5.1" - } - }, "vscode-jsonrpc": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz", diff --git a/server/package.json b/server/package.json index 2d75c1b..c606227 100644 --- a/server/package.json +++ b/server/package.json @@ -12,10 +12,11 @@ "node": "*" }, "dependencies": { + "@types/adm-zip": "^0.4.32", "@types/node-fetch": "^2.5.0", + "adm-zip": "^0.4.13", "node-fetch": "^2.6.0", "typescript-logging": "^0.6.3", - "unzip-stream": "^0.3.0", "vscode-languageserver": "^4.4.2" }, "scripts": { diff --git a/server/src/config.ts b/server/src/config.ts index 2271d15..4b64a93 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -1,14 +1,8 @@ -import { connection, documents, onEvent } from './server' -import fetch from 'node-fetch' -import { platform } from 'os' -import { createWriteStream, chmodSync, createReadStream, unlinkSync, read } from 'fs' -import * as unzip from 'unzip-stream' -import { postError } from './utils' -import { execSync } from 'child_process' -import { serverLog } from './logging' +import { connection } from './server' +import { serverLog as log } from './logging' import { dirname } from 'path' import { DidChangeConfigurationParams } from 'vscode-languageserver' -import { win } from './linter' +import { GLSLangProvider } from './glslangValidator' const url = { 'win32': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Release.zip', @@ -18,26 +12,75 @@ const url = { export let glslangReady = false -export interface Config { - readonly shaderpacksPath: string - readonly glslangPath: string +export class ConfigProvider { + private _config: Config + private _onChange: (settings: Config) => void + private _glslang: GLSLangProvider + + public constructor(func?: (confProv: ConfigProvider, settings: Config) => void) { + this._config = { + shaderpacksPath: '', + glslangValidatorPath: '' + } + + if (!func) { + this._onChange = (settings: Config) => { + onConfigChange(this, settings) + } + } else { + this._onChange = (settings: Config) => { + func(this, settings) + } + } + } + + public set config(c: Config) { + Object.assign(this._config, c) + } + + public get config(): Config { + return this._config + } + + public set onChange(func: (confProv: ConfigProvider, settings: Config) => void) { + this._onChange = (settings: Config) => { + func(this, settings) + } + } + + public onConfigChange = (change: DidChangeConfigurationParams) => { + this._onChange(change.settings.mcglsl as Config) + } + + public set glslang(glslang: GLSLangProvider) { + this._glslang = glslang + } + + public get glslang(): GLSLangProvider { + return this._glslang + } } -export let conf: Config = {shaderpacksPath: '', glslangPath: ''} +interface Config { + shaderpacksPath: string + glslangValidatorPath: string +} let supress = false -export async function onConfigChange(change: DidChangeConfigurationParams) { - const temp = change.settings.mcglsl as Config - if (temp.shaderpacksPath === conf.shaderpacksPath && temp.glslangPath === conf.glslangPath) return - conf = {shaderpacksPath: temp['shaderpacksPath'].replace(/\\/g, '/'), glslangPath: temp['glslangValidatorPath'].replace(/\\/g, '/')} - serverLog.debug(() => 'new config: ' + JSON.stringify(temp)) - serverLog.debug(() => 'old config: ' + JSON.stringify(conf)) +async function onConfigChange(confProv: ConfigProvider, old: Config) { + if (!confProv.config == undefined && + old.shaderpacksPath === confProv.config.shaderpacksPath && + old.glslangValidatorPath === confProv.config.glslangValidatorPath) return - if (conf.shaderpacksPath === '' || conf.shaderpacksPath.replace(dirname(conf.shaderpacksPath), '') !== '/shaderpacks') { + confProv.config = {shaderpacksPath: old['shaderpacksPath'], glslangValidatorPath: old['glslangValidatorPath']} + log.debug(() => 'new config: ' + JSON.stringify(old)) + log.debug(() => 'old config: ' + JSON.stringify(confProv.config)) + + if (confProv.config.shaderpacksPath === '' || confProv.config.shaderpacksPath.replace(dirname(confProv.config.shaderpacksPath), '') !== '/shaderpacks') { if (supress) return - serverLog.error(() => 'shaderpack path not set or doesn\'t end in \'shaderpacks\'', null) + log.error(() => `shaderpack path '${confProv.config.shaderpacksPath.replace(dirname(confProv.config.shaderpacksPath), '')}' not set or doesn't end in 'shaderpacks'`, null) supress = true const clicked = await connection.window.showErrorMessage( @@ -48,81 +91,9 @@ export async function onConfigChange(change: DidChangeConfigurationParams) { return } - try { - if (!execSync(conf.glslangPath).toString().startsWith('Usage')) { - documents.all().forEach(onEvent) - glslangReady = true - } else { - promptDownloadGlslang() - } - } catch (e) { - if ((e.stdout.toString() as string).startsWith('Usage')) { - documents.all().forEach(onEvent) - glslangReady = true - } else { - promptDownloadGlslang() - } + if (!confProv.glslang.testExecutable()) { + await confProv.glslang.promptDownload() + } else { + glslangReady = true } } - -async function promptDownloadGlslang() { - const chosen = await connection.window.showErrorMessage( - `[mc-glsl] glslangValidator not found at: '${conf.glslangPath}'.`, - {title: 'Download'}, - {title: 'Cancel'} - ) - - if (!chosen || chosen.title !== 'Download') return - - downloadGlslang() -} - -async function downloadGlslang() { - connection.window.showInformationMessage('Downloading. Your settings will be updated automatically and you\'ll be notified when its done.') - - serverLog.info(() => 'downloading glslangValidator...') - - const res = await fetch(url[platform()]) - - serverLog.info(() => 'glslangValidator downloaded. Extracting...') - - try { - const zip = createWriteStream(conf.shaderpacksPath + '/glslangValidator.zip') - res.body.pipe(zip) - - const glslang = '/glslangValidator' + (win ? '.exe' : '') - - zip.on('finish', () => { - try { - createReadStream(conf.shaderpacksPath + '/glslangValidator.zip') - .pipe(unzip.Parse()) - .on('entry', entry => { - try { - if (entry.path === 'bin' + glslang) { - entry.pipe(createWriteStream(conf.shaderpacksPath + glslang)) - return - } - entry.autodrain() - } catch (e) { - postError(e) - } - }) - .on('close', () => { - try { - chmodSync(conf.shaderpacksPath + glslang, 0o775) - unlinkSync(conf.shaderpacksPath + '/glslangValidator.zip') - connection.sendNotification('update-config', conf.shaderpacksPath + glslang) - connection.window.showInformationMessage('glslangValidator has been downloaded to ' + conf.shaderpacksPath + '/glslangValidator. Your config should be updated automatically.') - glslangReady = true - } catch (e) { - postError(e) - } - }) - } catch (e) { - postError(e) - } - }) - } catch (e) { - postError(e) - } -} \ No newline at end of file diff --git a/server/src/fileTypes.ts b/server/src/fileTypes.ts new file mode 100644 index 0000000..7dc6fa2 --- /dev/null +++ b/server/src/fileTypes.ts @@ -0,0 +1,9 @@ + +export type ShaderFileExtension = '.fsh' | '.gsh' | '.vsh' +export type ShaderFileType = 'frag' | 'geom' | 'vert' + +export const extensionMap = new Map([ + ['.fsh', 'frag'], + ['.gsh', 'geom'], + ['.vsh', 'vert'], +]) \ No newline at end of file diff --git a/server/src/glslangValidator.ts b/server/src/glslangValidator.ts new file mode 100644 index 0000000..89f42f2 --- /dev/null +++ b/server/src/glslangValidator.ts @@ -0,0 +1,100 @@ +import { ConfigProvider } from './config' +import { execSync } from 'child_process' +import { extensionMap, ShaderFileExtension } from './fileTypes' +import * as path from 'path' +import fetch from 'node-fetch'; +import { platform } from 'os'; +import * as unzip from 'adm-zip' +import { createWriteStream } from 'fs' +import { writeFileSync, fstat } from 'fs'; +import { connection } from './server'; +import { glslProviderLog as log } from './logging'; + + +const url = { + 'win32': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Release.zip', + 'linux': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip', + 'darwin': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-osx-Release.zip' +} + +export class GLSLangProvider { + private _config: ConfigProvider + + public constructor(c: ConfigProvider) { + this._config = c + } + + public lint(document: string, fileExtension: ShaderFileExtension): string { + try { + execSync(`${this._config.config.glslangValidatorPath} --stdin -S ${extensionMap.get(fileExtension)}`, {input: document}) + } catch (e) { + return e.stdout.toString() + } + } + + public promptDownload = async () => { + const chosen = await connection.window.showErrorMessage( + `[mc-glsl] glslangValidator not found at: '${this._config.config.glslangValidatorPath}'.`, + {title: 'Download'}, + {title: 'Cancel'} + ) + + if (!chosen || chosen.title !== 'Download') return + + await this.installExecutable() + } + + public installExecutable = async () => { + try { + const glslangBin = '/glslangValidator' + (platform() === 'win32' ? '.exe' : '') + const glslangPath = this._config.config.shaderpacksPath + glslangBin + + const response = await fetch(url[platform()]) + + const zip = new unzip(await response.buffer()) + + const bin = zip.readFile('bin' + glslangBin) + + writeFileSync(glslangPath, bin) + + if (!this.testExecutable()) { + connection.window.showErrorMessage( + 'Unexpected error occurred. Please try again' + ) + return + } + + // why doesnt this work???????? + log.info(() => `successfully downloaded glslangValidator to ${glslangPath}`) + connection.window.showInformationMessage( + `glslangValidator has been downloaded to ${glslangPath}. Your config should be updated automatically.` + ) + connection.sendNotification('update-config', glslangPath) + } catch (e) { + log.error(() => `failed downloading glslangValidator`, e) + connection.window.showErrorMessage( + `Failed to install glslangValidator: ${e}` + ) + } + } + + public testExecutable(glslangPath?: string): boolean { + let success = false + try { + const stdout = execSync(glslangPath || this._config.config.glslangValidatorPath, { + stdio: 'pipe', + }).toString() + success = stdout.startsWith('Usage') + } catch (e) { + success = (e.stdout.toString() as string).startsWith('Usage') + } + + if (success) { + log.info(() => `glslangValidator found at ${this._config.config.glslangValidatorPath}`) + } else { + log.error(() => `glslangValidator not found at ${this._config.config.glslangValidatorPath}`, null) + } + + return success + } +} \ No newline at end of file diff --git a/server/src/graph.ts b/server/src/graph.ts index b904452..593dbf4 100644 --- a/server/src/graph.ts +++ b/server/src/graph.ts @@ -38,4 +38,24 @@ export class Graph { } this.nodes.set(parent, par) } + + public toString(): string { + let result = '' + let start = true + this.nodes.forEach((node, key) => { + if (!start) { + key += '\n' + start = false + } + result += `${key}:\n\tchildren: ` + node.children.forEach((_, key) => { + result += key + ' ' + }) + result + '\n\tparents: ' + node.parents.forEach((_, key) => { + result += key + ' ' + }) + }) + return result + } } \ No newline at end of file diff --git a/server/src/linksProvider.ts b/server/src/linksProvider.ts index d98f496..2a87ea7 100644 --- a/server/src/linksProvider.ts +++ b/server/src/linksProvider.ts @@ -1,8 +1,8 @@ import * as vsclang from 'vscode-languageserver' -import { linkLog } from './logging' +import { linkLog as log} from './logging' import { formatURI } from './utils' export function getDocumentLinks(file: string): vsclang.DocumentLink[] { - linkLog.debug(() => formatURI(file) + ' ' + file) - return [vsclang.DocumentLink.create(vsclang.Range.create(8, 0, 8, 32), 'file:///home/noah/.minecraft/shaderpacks/robobo1221Shaders-7.9.01/shaders/lib/utilities.glsl')] + log.debug(() => formatURI(file) + ' ' + file) + return [vsclang.DocumentLink.create(vsclang.Range.create(8, 0, 8, 32), 'file:///e:\\shaderpacks\\Sushi-Shader\\shaders\\composite1.vsh')] } \ No newline at end of file diff --git a/server/src/linter.ts b/server/src/linter.ts index d76a2fd..08beabc 100644 --- a/server/src/linter.ts +++ b/server/src/linter.ts @@ -1,383 +1,4 @@ -import { execSync } from 'child_process' -import { readFileSync, statSync } from 'fs' -import { platform } from 'os' -import * as path from 'path' -import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver' -import { Comment } from './comment' -import { conf } from './config' -import { Graph } from './graph' -import { linterLog } from './logging' -import { connection } from './server' -import { formatURI, getDocumentContents, trimPath } from './utils' - -const reDiag = /^(ERROR|WARNING): ([^?<>*|"]+?):(\d+): (?:'.*?' : )?(.+)\r?/ -const reVersion = /#version [\d]{3}/ -const reInclude = /^(?:\s)*?(?:#include) "(.+)"\r?/ -const reIncludeExt = /#extension GL_GOOGLE_include_directive ?: ?require/ -const include = '#extension GL_GOOGLE_include_directive : require' -export const win = platform() === 'win32' - -const errorFilters = [ - /stdin/, - /(No code generated)/, - /(compilation terminated)/, - /Could not process include directive for header name:/, -] - -export const includeGraph = new Graph() - -type IncludeObj = { - lineNum: number, - lineNumTopLevel: number, - path: string, - parent: string, - match: RegExpMatchArray -} - -type ErrorMatch = { - type: DiagnosticSeverity, - file: string, - line: number, - msg: string, -} - -type LinesProcessingInfo = { - total: number, - comment: Comment.State, - parStack: string[], - count: number[], -} - -export const ext = new Map([ - ['.fsh', 'frag'], - ['.gsh', 'geom'], - ['.vsh', 'vert'], -]) - -const tokens = new Map([ - ['SEMICOLON', ';'], - ['COMMA', ','], - ['COLON', ':'], - ['EQUAL', '='], - ['LEFT_PAREN', '('], - ['RIGHT_PAREN', ')'], - ['DOT', '.'], - ['BANG', '!'], - ['DASH', '-'], - ['TILDE', '~'], - ['PLUS', '+'], - ['STAR', '*'], - ['SLASH', '/'], - ['PERCENT', '%'], - ['LEFT_ANGEL', '<'], - ['RIGHT_ANGEL', '>'], - ['VERICAL_BAR', '|'], - ['CARET', '^'], - ['AMPERSAND', '&'], - ['QUESTION', '?'], - ['[LEFT_BRACKET', '['], - ['RIGHT_BRACKET', ']'], - ['LEFT_BRACE', '{'], - ['RIGHT_BRACE', '}'], -]) - -export function preprocess(lines: string[], docURI: string) { - const hasDirective = includeDirective(lines) - - const diagnostics = new Map() - - processIncludes(lines, [docURI], new Set(), diagnostics, hasDirective) - - //const includeMap = new Map(Array.from(allIncludes).map(obj => [obj.path, obj]) as [string, IncludeObj][]) - - lint(docURI, lines, diagnostics, hasDirective) -} - -function includeDirective(lines: string[]): boolean { - if (lines.findIndex(x => reIncludeExt.test(x)) > -1) { - linterLog.info(() => 'include directive found') - return true - } - - let hasDirective = true - linterLog.info(() => 'include directive not found') - hasDirective = false - for (let i = 0; i < lines.length; i++) { - const line = lines[i] - if (reVersion.test(line)) { - linterLog.info(() => 'found version on line ' + (i + 1)) - lines.splice(i + 1, 0, include) - break - } - - /* if (i === lines.length - 1) { - linterLog.warn(() => `no version found for ${docURI}. inserting at top`) - lines.splice(0, 0, include) - break - } */ - } - return hasDirective -} - -const buildIncludeGraph = (inc: IncludeObj) => includeGraph.setParent(inc.path, inc.parent, inc.lineNum) - -function processIncludes(lines: string[], incStack: string[], allIncludes: Set, diagnostics: Map, hasDirective: boolean) { - const parent = incStack[incStack.length - 1] - const includes = getIncludes(parent, lines) - includes.forEach(i => allIncludes.add(i)) - - includeGraph.nodes.get(parent).children.forEach((node, uri) => { - if (!includes.has(uri)) { - includeGraph.nodes.get(parent).children.delete(uri) - node.parents.delete(parent) - } - }) - - const includeList = Array.from(includes.values()) - - if (includeList.length > 0) { - linterLog.info(() => `${trimPath(parent)} has ${includeList.length} include(s). [${includeList.map(i => '\n\t\t' + trimPath(i.path))}\n\t]`) - - includeList.reverse().forEach(inc => { - buildIncludeGraph(inc) - mergeInclude(inc, lines, incStack, diagnostics, hasDirective) - }) - // recursively check for more includes to be merged - processIncludes(lines, incStack, allIncludes, diagnostics, hasDirective) - } -} - -function getIncludes(uri: string, lines: string[]) { - const lineInfo: LinesProcessingInfo = { - total: 0, - comment: Comment.State.No, - parStack: [uri], - count: [0], - } - - return lines.reduce>((out, line, i) => processLine(out, line, lines, i, lineInfo), new Map()) -} - -// TODO can surely be reworked -function processLine(includes: Map, line: string, lines: string[], i: number, linesInfo: LinesProcessingInfo): Map { - const updated = Comment.update(line, linesInfo.comment) - linesInfo.comment = updated[0] - - if (updated[1] !== line) linterLog.debug(() => `change:\n\t'${line}'\n\t'${updated[1]}'`) - - line = updated[1] - lines[i] = line - - linesInfo.count[linesInfo.count.length - 1]++ - linesInfo.total++ - - if (linesInfo.comment) return includes - - if (line.startsWith('#line')) { - const inc = line.slice(line.indexOf('"') + 1, line.lastIndexOf('"')) - if (inc.length + 1 === line.length) lines[i] = '' - if (inc === linesInfo.parStack[linesInfo.parStack.length - 2]) { - linesInfo.count.pop() - linesInfo.parStack.pop() - } else { - linesInfo.count.push(0) - linesInfo.parStack.push(inc) - } - return includes - } - - const match = line.match(reInclude) - - if (match) { - includes.set( - formatURI(absPath(linesInfo.parStack[linesInfo.parStack.length - 1], match[1])), - { - path: formatURI(absPath(linesInfo.parStack[linesInfo.parStack.length - 1], match[1])), - lineNum: linesInfo.count[linesInfo.count.length - 1] - 1, - lineNumTopLevel: linesInfo.total - 1, - parent: formatURI(linesInfo.parStack[linesInfo.parStack.length - 1]), - match - } - ) - } - return includes -} - -function ifInvalidFile(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map, hasDirective: boolean) { - const msg = `${trimPath(inc.path)} is missing or an invalid file.` - - linterLog.error(msg, null) - - const file = incStack[incStack.length - 1] - const diag: Diagnostic = { - severity: DiagnosticSeverity.Error, - range: calcRange(inc.lineNum - ((!hasDirective && includeGraph.get(file).parents.size === 0) ? 1 : 0), file), - message: msg, - source: 'mc-glsl' - } - - diagnostics.set(inc.parent, [...(diagnostics.get(inc.parent) || []), diag]) - lines[inc.lineNumTopLevel] = lines[inc.lineNumTopLevel].replace(reInclude, '') - - const error: ErrorMatch = { - type: DiagnosticSeverity.Error, - line: inc.lineNum, - msg, file, - } - propogateDiagnostic(error, diagnostics, hasDirective) -} - -function mergeInclude(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map, hasDirective: boolean) { - try { - const stats = statSync(inc.path) - if (!stats.isFile()) { - const err = new Error() - err['code'] = 'ENOENT' - throw err - } - } catch (e) { - if (e.code === 'ENOENT') { - ifInvalidFile(inc, lines, incStack, diagnostics, hasDirective) - return - } - throw e - } - - const dataLines = readFileSync(inc.path).toString().split('\n') - - // if the includes parent is the top level (aka where the include directive is placed) - // and we had to manually add the directive, - 1 the line number to account for the extra line - if (inc.parent === incStack[0] && !hasDirective) inc.lineNum = inc.lineNum - 1 - - incStack.push(inc.path) - - // add #line indicating we are entering a new include block - lines[inc.lineNumTopLevel] = `#line 0 "${formatURI(inc.path)}"` - // merge the lines of the file into the current document - lines.splice(inc.lineNumTopLevel + 1, 0, ...dataLines) - // add the closing #line indicating we're re-entering a block a level up - lines.splice(inc.lineNumTopLevel + 1 + dataLines.length, 0, `#line ${inc.lineNum + 1} "${inc.parent}"`) -} - -function lint(docURI: string, lines: string[], diagnostics: Map, hasDirective: boolean) { - console.log(lines.join('\n')) - - let out: string = '' - try { - execSync(`${conf.glslangPath} --stdin -S ${ext.get(path.extname(docURI))}`, {input: lines.join('\n')}) - } catch (e) { - out = e.stdout.toString() - } - - if (!diagnostics.has(docURI)) diagnostics.set(docURI, []) - includeGraph.nodes.forEach((node, key) => { - if (!diagnostics.has(key)) diagnostics.set(key, []) - }) - - processErrors(out, docURI, diagnostics, hasDirective) - - diagnostics.forEach((diags, uri) => { - if (diags.length === 0) return - const errors = diags.filter(d => d.severity === DiagnosticSeverity.Error) - const warnings = diags.filter(d => d.severity === DiagnosticSeverity.Warning) - linterLog.info(() => `found ${errors.length} error(s) and ${warnings.length} warning(s) for ${trimPath(uri)}`) - }) - - const diagsList = daigsArray(diagnostics) - - if (diagsList.filter(d => d.diag.length > 0).length === 0) linterLog.info(() => 'no errors found') - - //console.log(JSON.stringify(diagsList.filter(d => d.diag.length > 0), null, 2)) - - diagsList.forEach(d => { - if (win) { - const drive = d.uri[7] - d.uri = d.uri.replace(`file://${drive}:`, `file:///${drive.toLowerCase()}%3A`) - } - connection.sendDiagnostics({uri: d.uri, diagnostics: d.diag}) - }) -} - -function processErrors(out: string, docURI: string, diagnostics: Map, hasDirective: boolean) { - linterLog.debug(() => out.split('\n').filter(s => s.length > 2).join('\n')) - filterMatches(out).forEach(match => { - const error: ErrorMatch = { - type: errorType(match[1]), - file: match[2], - line: parseInt(match[3]), - msg: match[4] - } - - const fileName = error.file.length - 1 ? error.file : docURI - - const diag: Diagnostic = { - severity: error.type, - //range: calcRange(error.line - 1, fileName), - range: calcRange(error.line - ((!hasDirective && includeGraph.get(fileName).parents.size === 0) ? 2 : 1), fileName), - message: `Line ${error.line + ((!hasDirective && includeGraph.get(fileName).parents.size === 0) ? 2 : 1)} ${replaceWords(error.msg)}`, - source: 'mc-glsl' - } - - diagnostics.get(fileName).push(diag) - - // if is an include, highlight an error in the parents line of inclusion - propogateDiagnostic(error, diagnostics, hasDirective) - }) -} - -function propogateDiagnostic(error: ErrorMatch, diagnostics: Map, hasDirective: boolean, parentURI?: string) { - includeGraph.get(parentURI || error.file).parents.forEach((pair, parURI) => { - const diag: Diagnostic = { - severity: error.type, - range: calcRange(pair.first - ((!hasDirective && pair.second.parents.size === 0) ? 1 : 0), parURI), - message: `Line ${error.line + 1} ${trimPath(error.file)} ${replaceWords(error.msg)}`, - source: 'mc-glsl' - } - - if (!diagnostics.has(parURI)) diagnostics.set(parURI, []) - diagnostics.get(parURI).push(diag) - - if (pair.second.parents.size > 0) { - propogateDiagnostic(error, diagnostics, hasDirective, parURI) - } - }) -} - -const replaceWords = (msg: string) => Array.from(tokens.entries()).reduce((acc, [key, value]) => acc.replace(key, value), msg) - -const errorType = (error: string) => error === 'ERROR' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning - -const daigsArray = (diags: Map) => Array.from(diags).map(kv => ({uri: 'file://' + kv[0], diag: kv[1]})) - -const filterMatches = (output: string) => output - .split('\n') - .filter(s => s.length > 1 && !errorFilters.some(reg => reg.test(s))) - .map(s => s.match(reDiag)) - .filter(match => match && match.length === 5) - -function calcRange(lineNum: number, uri: string) { - linterLog.debug(() => `calculating range for ${trimPath(uri)} at L${lineNum + 1}, index ${lineNum}`) - - const lines = getDocumentContents(uri).split('\n') - const line = lines[Math.min(Math.max(lineNum, 0), lines.length - 1)] - const startOfLine = line.length - line.trimLeft().length - const endOfLine = line.trimRight().length + 1 - //const endOfLine = line.slice(0, line.indexOf('//')).trimRight().length + 2 - return Range.create(lineNum, startOfLine, lineNum, endOfLine) -} - -function absPath(currFile: string, includeFile: string) { - if (!currFile.startsWith(conf.shaderpacksPath) || conf.shaderpacksPath === '') { - connection.window.showErrorMessage(`Shaderpacks path may not be correct. Current file is in '${currFile}' but the path is set to '${conf.shaderpacksPath}'`) - return '' - } - - // TODO add explanation comment - if (includeFile.charAt(0) === '/' || (includeFile.charAt(0) === '.' && includeFile.charAt(1) === '.')) { - const shaderPath = trimPath(currFile).split('/').slice(0, 3).join('/') - return path.join(conf.shaderpacksPath, shaderPath, includeFile) - } /* else if (includeFile.charAt(0) === '.' && includeFile.charAt(1) === '.') { - - } */ - return path.join(path.dirname(currFile), includeFile) + +export class Linter { + } \ No newline at end of file diff --git a/server/src/linter.ts.old b/server/src/linter.ts.old new file mode 100644 index 0000000..6ffa2e4 --- /dev/null +++ b/server/src/linter.ts.old @@ -0,0 +1,376 @@ +import { execSync } from 'child_process' +import { readFileSync, statSync } from 'fs' +import { platform } from 'os' +import * as path from 'path' +import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver' +import { Comment } from './comment' +import { Graph } from './graph' +import { linterLog } from './logging' +import { connection } from './server' +import { formatURI, getDocumentContents, trimPath } from './utils' + +const reDiag = /^(ERROR|WARNING): ([^?<>*|"]+?):(\d+): (?:'.*?' : )?(.+)\r?/ +const reVersion = /#version [\d]{3}/ +const reInclude = /^(?:\s)*?(?:#include) "(.+)"\r?/ +const reIncludeExt = /#extension GL_GOOGLE_include_directive ?: ?require/ +const include = '#extension GL_GOOGLE_include_directive : require' +export const win = platform() === 'win32' + +const errorFilters = [ + /stdin/, + /(No code generated)/, + /(compilation terminated)/, + /Could not process include directive for header name:/, +] + +export const includeGraph = new Graph() + +type IncludeObj = { + lineNum: number, + lineNumTopLevel: number, + path: string, + parent: string, + match: RegExpMatchArray +} + +type ErrorMatch = { + type: DiagnosticSeverity, + file: string, + line: number, + msg: string, +} + +type LinesProcessingInfo = { + total: number, + comment: Comment.State, + parStack: string[], + count: number[], +} + +const tokens = new Map([ + ['SEMICOLON', ';'], + ['COMMA', ','], + ['COLON', ':'], + ['EQUAL', '='], + ['LEFT_PAREN', '('], + ['RIGHT_PAREN', ')'], + ['DOT', '.'], + ['BANG', '!'], + ['DASH', '-'], + ['TILDE', '~'], + ['PLUS', '+'], + ['STAR', '*'], + ['SLASH', '/'], + ['PERCENT', '%'], + ['LEFT_ANGEL', '<'], + ['RIGHT_ANGEL', '>'], + ['VERICAL_BAR', '|'], + ['CARET', '^'], + ['AMPERSAND', '&'], + ['QUESTION', '?'], + ['[LEFT_BRACKET', '['], + ['RIGHT_BRACKET', ']'], + ['LEFT_BRACE', '{'], + ['RIGHT_BRACE', '}'], +]) + +export function preprocess(lines: string[], docURI: string) { + const hasDirective = includeDirective(lines) + + const diagnostics = new Map() + + processIncludes(lines, [docURI], new Set(), diagnostics, hasDirective) + + //const includeMap = new Map(Array.from(allIncludes).map(obj => [obj.path, obj]) as [string, IncludeObj][]) + + lint(docURI, lines, diagnostics, hasDirective) +} + +function includeDirective(lines: string[]): boolean { + if (lines.findIndex(x => reIncludeExt.test(x)) > -1) { + linterLog.info(() => 'include directive found') + return true + } + + let hasDirective = true + linterLog.info(() => 'include directive not found') + hasDirective = false + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (reVersion.test(line)) { + linterLog.info(() => 'found version on line ' + (i + 1)) + lines.splice(i + 1, 0, include) + break + } + + /* if (i === lines.length - 1) { + linterLog.warn(() => `no version found for ${docURI}. inserting at top`) + lines.splice(0, 0, include) + break + } */ + } + return hasDirective +} + +const buildIncludeGraph = (inc: IncludeObj) => includeGraph.setParent(inc.path, inc.parent, inc.lineNum) + +function processIncludes(lines: string[], incStack: string[], allIncludes: Set, diagnostics: Map, hasDirective: boolean) { + const parent = incStack[incStack.length - 1] + const includes = getIncludes(parent, lines) + includes.forEach(i => allIncludes.add(i)) + + includeGraph.nodes.get(parent).children.forEach((node, uri) => { + if (!includes.has(uri)) { + includeGraph.nodes.get(parent).children.delete(uri) + node.parents.delete(parent) + } + }) + + const includeList = Array.from(includes.values()) + + if (includeList.length > 0) { + linterLog.info(() => `${trimPath(parent)} has ${includeList.length} include(s). [${includeList.map(i => '\n\t\t' + trimPath(i.path))}\n\t]`) + + includeList.reverse().forEach(inc => { + buildIncludeGraph(inc) + mergeInclude(inc, lines, incStack, diagnostics, hasDirective) + }) + // recursively check for more includes to be merged + processIncludes(lines, incStack, allIncludes, diagnostics, hasDirective) + } +} + +function getIncludes(uri: string, lines: string[]) { + const lineInfo: LinesProcessingInfo = { + total: 0, + comment: Comment.State.No, + parStack: [uri], + count: [0], + } + + return lines.reduce>((out, line, i) => processLine(out, line, lines, i, lineInfo), new Map()) +} + +// TODO can surely be reworked +function processLine(includes: Map, line: string, lines: string[], i: number, linesInfo: LinesProcessingInfo): Map { + const updated = Comment.update(line, linesInfo.comment) + linesInfo.comment = updated[0] + + if (updated[1] !== line) linterLog.debug(() => `change:\n\t'${line}'\n\t'${updated[1]}'`) + + line = updated[1] + lines[i] = line + + linesInfo.count[linesInfo.count.length - 1]++ + linesInfo.total++ + + if (linesInfo.comment) return includes + + if (line.startsWith('#line')) { + const inc = line.slice(line.indexOf('"') + 1, line.lastIndexOf('"')) + if (inc.length + 1 === line.length) lines[i] = '' + if (inc === linesInfo.parStack[linesInfo.parStack.length - 2]) { + linesInfo.count.pop() + linesInfo.parStack.pop() + } else { + linesInfo.count.push(0) + linesInfo.parStack.push(inc) + } + return includes + } + + const match = line.match(reInclude) + + if (match) { + includes.set( + formatURI(absPath(linesInfo.parStack[linesInfo.parStack.length - 1], match[1])), + { + path: formatURI(absPath(linesInfo.parStack[linesInfo.parStack.length - 1], match[1])), + lineNum: linesInfo.count[linesInfo.count.length - 1] - 1, + lineNumTopLevel: linesInfo.total - 1, + parent: formatURI(linesInfo.parStack[linesInfo.parStack.length - 1]), + match + } + ) + } + return includes +} + +function ifInvalidFile(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map, hasDirective: boolean) { + const msg = `${trimPath(inc.path)} is missing or an invalid file.` + + linterLog.error(msg, null) + + const file = incStack[incStack.length - 1] + const diag: Diagnostic = { + severity: DiagnosticSeverity.Error, + range: calcRange(inc.lineNum - ((!hasDirective && includeGraph.get(file).parents.size === 0) ? 1 : 0), file), + message: msg, + source: 'mc-glsl' + } + + diagnostics.set(inc.parent, [...(diagnostics.get(inc.parent) || []), diag]) + lines[inc.lineNumTopLevel] = lines[inc.lineNumTopLevel].replace(reInclude, '') + + const error: ErrorMatch = { + type: DiagnosticSeverity.Error, + line: inc.lineNum, + msg, file, + } + propogateDiagnostic(error, diagnostics, hasDirective) +} + +function mergeInclude(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map, hasDirective: boolean) { + try { + const stats = statSync(inc.path) + if (!stats.isFile()) { + const err = new Error() + err['code'] = 'ENOENT' + throw err + } + } catch (e) { + if (e.code === 'ENOENT') { + ifInvalidFile(inc, lines, incStack, diagnostics, hasDirective) + return + } + throw e + } + + const dataLines = readFileSync(inc.path).toString().split('\n') + + // if the includes parent is the top level (aka where the include directive is placed) + // and we had to manually add the directive, - 1 the line number to account for the extra line + if (inc.parent === incStack[0] && !hasDirective) inc.lineNum = inc.lineNum - 1 + + incStack.push(inc.path) + + // add #line indicating we are entering a new include block + lines[inc.lineNumTopLevel] = `#line 0 "${formatURI(inc.path)}"` + // merge the lines of the file into the current document + lines.splice(inc.lineNumTopLevel + 1, 0, ...dataLines) + // add the closing #line indicating we're re-entering a block a level up + lines.splice(inc.lineNumTopLevel + 1 + dataLines.length, 0, `#line ${inc.lineNum + 1} "${inc.parent}"`) +} + +function lint(docURI: string, lines: string[], diagnostics: Map, hasDirective: boolean) { + console.log(lines.join('\n')) + + let out: string = '' + try { + //execSync(`${conf.glslangPath} --stdin -S ${ext.get(path.extname(docURI))}`, {input: lines.join('\n')}) + } catch (e) { + out = e.stdout.toString() + } + + if (!diagnostics.has(docURI)) diagnostics.set(docURI, []) + includeGraph.nodes.forEach((node, key) => { + if (!diagnostics.has(key)) diagnostics.set(key, []) + }) + + processErrors(out, docURI, diagnostics, hasDirective) + + diagnostics.forEach((diags, uri) => { + if (diags.length === 0) return + const errors = diags.filter(d => d.severity === DiagnosticSeverity.Error) + const warnings = diags.filter(d => d.severity === DiagnosticSeverity.Warning) + linterLog.info(() => `found ${errors.length} error(s) and ${warnings.length} warning(s) for ${trimPath(uri)}`) + }) + + const diagsList = daigsArray(diagnostics) + + if (diagsList.filter(d => d.diag.length > 0).length === 0) linterLog.info(() => 'no errors found') + + //console.log(JSON.stringify(diagsList.filter(d => d.diag.length > 0), null, 2)) + + diagsList.forEach(d => { + if (win) { + const drive = d.uri[7] + d.uri = d.uri.replace(`file://${drive}:`, `file:///${drive.toLowerCase()}%3A`) + } + connection.sendDiagnostics({uri: d.uri, diagnostics: d.diag}) + }) +} + +function processErrors(out: string, docURI: string, diagnostics: Map, hasDirective: boolean) { + linterLog.debug(() => out.split('\n').filter(s => s.length > 2).join('\n')) + filterMatches(out).forEach(match => { + const error: ErrorMatch = { + type: errorType(match[1]), + file: match[2], + line: parseInt(match[3]), + msg: match[4] + } + + const fileName = error.file.length - 1 ? error.file : docURI + + const diag: Diagnostic = { + severity: error.type, + //range: calcRange(error.line - 1, fileName), + range: calcRange(error.line - ((!hasDirective && includeGraph.get(fileName).parents.size === 0) ? 2 : 1), fileName), + message: `Line ${error.line + ((!hasDirective && includeGraph.get(fileName).parents.size === 0) ? 2 : 1)} ${replaceWords(error.msg)}`, + source: 'mc-glsl' + } + + diagnostics.get(fileName).push(diag) + + // if is an include, highlight an error in the parents line of inclusion + propogateDiagnostic(error, diagnostics, hasDirective) + }) +} + +function propogateDiagnostic(error: ErrorMatch, diagnostics: Map, hasDirective: boolean, parentURI?: string) { + includeGraph.get(parentURI || error.file).parents.forEach((pair, parURI) => { + const diag: Diagnostic = { + severity: error.type, + range: calcRange(pair.first - ((!hasDirective && pair.second.parents.size === 0) ? 1 : 0), parURI), + message: `Line ${error.line + 1} ${trimPath(error.file)} ${replaceWords(error.msg)}`, + source: 'mc-glsl' + } + + if (!diagnostics.has(parURI)) diagnostics.set(parURI, []) + diagnostics.get(parURI).push(diag) + + if (pair.second.parents.size > 0) { + propogateDiagnostic(error, diagnostics, hasDirective, parURI) + } + }) +} + +const replaceWords = (msg: string) => Array.from(tokens.entries()).reduce((acc, [key, value]) => acc.replace(key, value), msg) + +const errorType = (error: string) => error === 'ERROR' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning + +const daigsArray = (diags: Map) => Array.from(diags).map(kv => ({uri: 'file://' + kv[0], diag: kv[1]})) + +const filterMatches = (output: string) => output + .split('\n') + .filter(s => s.length > 1 && !errorFilters.some(reg => reg.test(s))) + .map(s => s.match(reDiag)) + .filter(match => match && match.length === 5) + +function calcRange(lineNum: number, uri: string) { + linterLog.debug(() => `calculating range for ${trimPath(uri)} at L${lineNum + 1}, index ${lineNum}`) + + const lines = getDocumentContents(uri).split('\n') + const line = lines[Math.min(Math.max(lineNum, 0), lines.length - 1)] + const startOfLine = line.length - line.trimLeft().length + const endOfLine = line.trimRight().length + 1 + //const endOfLine = line.slice(0, line.indexOf('//')).trimRight().length + 2 + return Range.create(lineNum, startOfLine, lineNum, endOfLine) +} + +function absPath(currFile: string, includeFile: string) { + if (!currFile.startsWith(conf.shaderpacksPath) || conf.shaderpacksPath === '') { + connection.window.showErrorMessage(`Shaderpacks path may not be correct. Current file is in '${currFile}' but the path is set to '${conf.shaderpacksPath}'`) + return '' + } + + // TODO add explanation comment + if (includeFile.charAt(0) === '/' || (includeFile.charAt(0) === '.' && includeFile.charAt(1) === '.')) { + const shaderPath = trimPath(currFile).split('/').slice(0, 3).join('/') + return path.join(conf.shaderpacksPath, shaderPath, includeFile) + } /* else if (includeFile.charAt(0) === '.' && includeFile.charAt(1) === '.') { + + } */ + return path.join(path.dirname(currFile), includeFile) +} \ No newline at end of file diff --git a/server/src/logging.ts b/server/src/logging.ts index ca71fbe..b3ea067 100644 --- a/server/src/logging.ts +++ b/server/src/logging.ts @@ -2,7 +2,10 @@ import { Category, CategoryConfiguration, CategoryServiceFactory, LogLevel } fro CategoryServiceFactory.setDefaultConfiguration(new CategoryConfiguration(LogLevel.Debug)) + export const linterLog = new Category('linter') export const completionLog = new Category('completion') export const serverLog = new Category('server') -export const linkLog = new Category('links') \ No newline at end of file +export const linkLog = new Category('links') +export const glslProviderLog = new Category('glslangProvider') +export const uriLog = new Category('uri') diff --git a/server/src/server.ts b/server/src/server.ts index c1de334..e10e529 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,11 +1,9 @@ -import { dirname } from 'path' import * as vsclang from 'vscode-languageserver' import * as vsclangproto from 'vscode-languageserver-protocol' import { completions } from './completionProvider' -import { conf, glslangReady, onConfigChange } from './config' +import { ConfigProvider } from './config' import { getDocumentLinks } from './linksProvider' -import { includeGraph, preprocess } from './linter' -import { formatURI, getDocumentContents, postError } from './utils' +import { GLSLangProvider } from './glslangValidator'; const reVersion = /#version [\d]{3}/ @@ -14,10 +12,14 @@ export let connection = vsclang.createConnection(vsclang.ProposedFeatures.all) console.log = connection.console.log.bind(connection.console) console.error = connection.console.error.bind(connection.console) +const configProvider = new ConfigProvider() +const glslangValidator = new GLSLangProvider(configProvider) +configProvider.glslang = glslangValidator + export const documents = new vsclang.TextDocuments() documents.listen(connection) -connection.onInitialize((params) => ( +connection.onInitialize((_) => ( { capabilities: { textDocumentSync: documents.syncKind, @@ -33,9 +35,9 @@ connection.onInitialize((params) => ( connection.onExit(() => {}) -documents.onDidOpen((event) => onEvent(event.document)) +documents.onDidOpen((event) => lint(event.document)) -documents.onDidSave((event) => onEvent(event.document)) +documents.onDidSave((event) => lint(event.document)) // what am i saying here // dont do this for include files, for non-include files, clear diags for all its includes. Cache this maybe @@ -45,8 +47,13 @@ documents.onDidClose((event) => { //documents.onDidChangeContent(onEvent) -export function onEvent(document: vsclangproto.TextDocument) { - if (conf.shaderpacksPath.replace(dirname(conf.shaderpacksPath), '') !== '/shaderpacks' || !glslangReady) return +export function lint(document: vsclangproto.TextDocument) { + if (!glslangValidator.testExecutable()) { + + } + /* + let sanitizedPath = conf.shaderpacksPath.replace(dirname(conf.shaderpacksPath), '') + if (sanitizedPath.startsWith('/shaderpacks') || glslangReady) return const uri = formatURI(document.uri) if (includeGraph.get(uri).parents.size > 0) { @@ -63,10 +70,10 @@ export function onEvent(document: vsclangproto.TextDocument) { preprocess(document.getText().split('\n'), uri) } catch (e) { postError(e) - } + } */ } -function lintBubbleDown(uri: string) { +/* function lintBubbleDown(uri: string) { includeGraph.get(uri).parents.forEach((parent, parentURI) => { if (parent.second.parents.size > 0) { lintBubbleDown(parentURI) @@ -82,11 +89,11 @@ function lintBubbleDown(uri: string) { } } }) -} +} */ connection.onDocumentLinks((params: vsclang.DocumentLinkParams) => getDocumentLinks(params.textDocument.uri)) -connection.onDidChangeConfiguration(onConfigChange) +connection.onDidChangeConfiguration(configProvider.onConfigChange) connection.onCompletion((textDocumentPosition: vsclang.TextDocumentPositionParams) => completions) diff --git a/server/src/uri.ts b/server/src/uri.ts new file mode 100644 index 0000000..ea65970 --- /dev/null +++ b/server/src/uri.ts @@ -0,0 +1,52 @@ +import { Category } from 'typescript-logging' +import { platform } from 'os' +import { uriLog as log } from './logging'; + + +export function formatURI(uri: string): string { + const drive = uri[7] + uri = uri.replace(`file:///${drive.toUpperCase()}%3A`, `file://${drive}:`) + return uri.replace(/^file:\/\//, '').replace(/\\/, '/') + } + +export class URI { + public static fromFileURI(uri: string): string { + log.debug(() => `normalizing ${uri}`) + if (URI.isNormalized(uri)) { + log.debug(() => `already normalized ${uri}`) + return uri + } + + + + return '' + } + + public static toFileURI(uri: string): string { + let fileURI = uri + + if (!fileURI.startsWith('file:///')) { + if (/^\\[a-zA-Z]/.test(fileURI)) { + fileURI = 'file:///' + fileURI.substr(1) + } else if (fileURI.startsWith('/')) { + fileURI = 'file://' + fileURI + } + } else if (fileURI.startsWith('file://\\')) { + fileURI = fileURI.replace('file://\\', 'file:///') + } else if (/^file:\/\/[a-zA-Z]/.test(fileURI)) { + fileURI = fileURI.replace('file://', 'file:///') + } + + log.debug(() => `formatted '${uri}' to '${fileURI}'`) + return fileURI + } + + private static isNormalized(uri: string): boolean { + if (uri.startsWith('file://') || uri.includes('%3A')) { + return false + } + + return true + } +} + \ No newline at end of file diff --git a/server/src/utils.ts b/server/src/utils.ts index e894c5a..c374986 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -1,18 +1,19 @@ -import { readFileSync } from 'fs' -import { conf } from './config' -import { serverLog } from './logging' +import { readFileSync, FSWatcher, ReadStream } from 'fs' +import * as urL from 'url' +import { serverLog as log } from './logging' import { connection, documents } from './server' +import { url } from 'inspector'; +import { Readable } from 'stream'; export function postError(e: Error) { connection.window.showErrorMessage(e.message) - serverLog.error(e.message, null) - console.log(e) + log.error(() => e.message, null) } export function formatURI(uri: string): string { const drive = uri[7] uri = uri.replace(`file:///${drive.toUpperCase()}%3A`, `file://${drive}:`) - return uri.replace(/^file:\/\//, '').replace(/\\/g, '/') + return uri.replace(/^file:\/\//, '').replace(/\\/, '/') } export function getDocumentContents(uri: string): string { @@ -20,6 +21,6 @@ export function getDocumentContents(uri: string): string { else return readFileSync(uri).toString() } -export function trimPath(path: string): string { +/* export function trimPath(path: string): string { return path.replace(conf.shaderpacksPath, '') -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/server/tsconfig.json b/server/tsconfig.json index fa74049..1dcf3a6 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -6,7 +6,7 @@ "sourceMap": true, "outDir": "out", "rootDir": "src", - "lib": [ "es6", "es2017" ] + "lib": [ "es6", "es2017", "dom" ] }, "include": [ "src"