From a5abe0f3317dd024abd6e3fe8c84c62daf0cb339 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:09:33 +0100 Subject: [PATCH 01/65] chore(deps): update all minor dependencies (master) (minor) (#2050) --- package-lock.json | 36 ++++++++++++++++++------------------ package.json | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index c01bf0aa50..80c3568867 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1706,9 +1706,9 @@ "dev": true }, "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "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" @@ -4196,9 +4196,9 @@ } }, "es6-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", - "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { @@ -6581,9 +6581,9 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -9679,9 +9679,9 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "puppeteer": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.17.0.tgz", - "integrity": "sha512-3EXZSximCzxuVKpIHtyec8Wm2dWZn1fc5tQi34qWfiUgubEVYHjUvr0GOJojqf3mifI6oyKnCdrGxaOI+lWReA==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.18.0.tgz", + "integrity": "sha512-NCwSN4wEIj43k4jO8Asa5nzibrIDFHWykqkZFjkGr0/f6U73k1ysql0gadQmOGLtZewXvvWqlNo+4ZMgX+5vZA==", "dev": true, "requires": { "debug": "^4.1.0", @@ -9695,9 +9695,9 @@ }, "dependencies": { "mime": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", - "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true } } @@ -11981,9 +11981,9 @@ "dev": true }, "webpack": { - "version": "4.34.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.34.0.tgz", - "integrity": "sha512-ry2IQy1wJjOefLe1uJLzn5tG/DdIKzQqNlIAd2L84kcaADqNvQDTBlo8UcCNyDaT5FiaB+16jhAkb63YeG3H8Q==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.35.0.tgz", + "integrity": "sha512-M5hL3qpVvtr8d4YaJANbAQBc4uT01G33eDpl/psRTBCfjxFTihdhin1NtAKB1ruDwzeVdcsHHV3NX+QsAgOosw==", "dev": true, "requires": { "@webassemblyjs/ast": "1.8.5", diff --git a/package.json b/package.json index 715246c308..2bffa026f3 100644 --- a/package.json +++ b/package.json @@ -98,14 +98,14 @@ "memfs": "^2.15.4", "npm-run-all": "^4.1.5", "prettier": "^1.18.2", - "puppeteer": "^1.17.0", + "puppeteer": "^1.18.0", "rimraf": "^2.6.3", "standard-version": "^6.0.1", "style-loader": "^0.23.1", "supertest": "^4.0.2", "tcp-port-used": "^1.0.1", "url-loader": "^1.1.2", - "webpack": "^4.34.0", + "webpack": "^4.35.0", "webpack-cli": "^3.3.4", "ws": "^6.2.1" }, From 410d04aeb7f6a64963a1a191081573287adfd0e9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2019 14:41:11 +0100 Subject: [PATCH 02/65] chore(deps): update dependency husky to ^2.5.0 (master) (#2060) --- package-lock.json | 38 +++++++++++++++++++++----------------- package.json | 2 +- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 80c3568867..e4768e017c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6589,23 +6589,32 @@ } }, "husky": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-2.4.1.tgz", - "integrity": "sha512-ZRwMWHr7QruR22dQ5l3rEGXQ7rAQYsJYqaeCd+NyOsIFczAtqaApZQP3P4HwLZjCtFbm3SUNYoKuoBXX3AYYfw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-2.5.0.tgz", + "integrity": "sha512-/aQIBaVMuzGi5X5BPliDPbHE+G+HDpWV7Zu28DiiXFMvHQcOeTsEnODWIGKyGBp7GM7rOgkxQdF+6AEo6xNtkw==", "dev": true, "requires": { - "cosmiconfig": "^5.2.0", + "cosmiconfig": "^5.2.1", "execa": "^1.0.0", - "find-up": "^3.0.0", "get-stdin": "^7.0.0", "is-ci": "^2.0.0", - "pkg-dir": "^4.1.0", + "pkg-dir": "^4.2.0", "please-upgrade-node": "^3.1.1", "read-pkg": "^5.1.1", "run-node": "^1.0.0", "slash": "^3.0.0" }, "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -6624,6 +6633,12 @@ "p-limit": "^2.2.0" } }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -6631,17 +6646,6 @@ "dev": true, "requires": { "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.0.0.tgz", - "integrity": "sha512-zoH7ZWPkRdgwYCDVoQTzqjG8JSPANhtvLhh4KVUHyKnaUJJrNeFmWIkTcNuJmR3GLMEmGYEf2S2bjgx26JTF+Q==", - "dev": true, - "requires": { - "locate-path": "^5.0.0" - } - } } }, "read-pkg": { diff --git a/package.json b/package.json index 2bffa026f3..aba3800c0b 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "file-loader": "^3.0.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", - "husky": "^2.4.1", + "husky": "^2.5.0", "jest": "^24.8.0", "jest-junit": "^6.4.0", "jquery": "^3.4.1", From bec95d4fd25a78b022f0d6585476a8945cbc7711 Mon Sep 17 00:00:00 2001 From: Eslam El-Hakmey Date: Mon, 24 Jun 2019 16:44:30 +0200 Subject: [PATCH 03/65] fix: allow openPage to open absolute urls (#2048) * fix: allow openPage to open absolute urls * test: add http test case * fix: use is-absolute-url package --- lib/utils/runOpen.js | 8 ++++++- package-lock.json | 5 +++++ package.json | 1 + test/server/utils/runOpen.test.js | 36 +++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/lib/utils/runOpen.js b/lib/utils/runOpen.js index cf4de34279..a2757ab919 100644 --- a/lib/utils/runOpen.js +++ b/lib/utils/runOpen.js @@ -1,6 +1,7 @@ 'use strict'; const open = require('opn'); +const isAbsoluteUrl = require('is-absolute-url'); function runOpen(uri, options, log) { // https://github.com/webpack/webpack-dev-server/issues/1990 @@ -12,7 +13,12 @@ function runOpen(uri, options, log) { openMessage += `: ${options.open}`; } - return open(`${uri}${options.openPage || ''}`, openOptions).catch(() => { + const pageUrl = + options.openPage && isAbsoluteUrl(options.openPage) + ? options.openPage + : `${uri}${options.openPage || ''}`; + + return open(pageUrl, openOptions).catch(() => { log.warn( `${openMessage}. If you are running in a headless environment, please do not use the --open flag` ); diff --git a/package-lock.json b/package-lock.json index e4768e017c..390f9ea2ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6863,6 +6863,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, + "is-absolute-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.0.tgz", + "integrity": "sha512-3OkP8XrM2Xq4/IxsJnClfMp3OaM3TAatLPLKPeWcxLBTrpe6hihwtX+XZfJTcXg/FTRi4qjy0y/C5qiyNxY24g==" + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", diff --git a/package.json b/package.json index aba3800c0b..d0fd3c39b7 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "import-local": "^2.0.0", "internal-ip": "^4.3.0", "ip": "^1.1.5", + "is-absolute-url": "^3.0.0", "killable": "^1.0.1", "loglevel": "^1.6.3", "opn": "^5.5.0", diff --git a/test/server/utils/runOpen.test.js b/test/server/utils/runOpen.test.js index fcb4b42225..e6fa3b155a 100644 --- a/test/server/utils/runOpen.test.js +++ b/test/server/utils/runOpen.test.js @@ -80,6 +80,42 @@ describe('runOpen util', () => { `); }); }); + + it('on specify absolute https URL with page in Google Chrome ', () => { + return runOpen( + 'https://example.com', + { open: 'Google Chrome', openPage: 'https://example2.com' }, + console + ).then(() => { + expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "https://example2.com", + Object { + "app": "Google Chrome", + "wait": false, + }, + ] + `); + }); + }); + + it('on specify absolute http URL with page in Google Chrome ', () => { + return runOpen( + 'https://example.com', + { open: 'Google Chrome', openPage: 'http://example2.com' }, + console + ).then(() => { + expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "http://example2.com", + Object { + "app": "Google Chrome", + "wait": false, + }, + ] + `); + }); + }); }); describe('should not open browser', () => { From 17b88199c3a91e1f40d2b51c76e02af285435d21 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2019 16:20:00 +0100 Subject: [PATCH 04/65] chore(deps): update all patch dependencies (master) (patch) (#2057) --- package-lock.json | 180 ++++++++++++++++++++++++++++++++++------------ package.json | 4 +- 2 files changed, 138 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 390f9ea2ad..5183310c2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5110,26 +5110,15 @@ } }, "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", "dev": true, "requires": { "detect-file": "^1.0.0", - "is-glob": "^3.1.0", + "is-glob": "^4.0.0", "micromatch": "^3.0.4", "resolve-dir": "^1.0.1" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } } }, "flat-cache": { @@ -6162,14 +6151,25 @@ } }, "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", "dev": true, "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } } }, "global-prefix": { @@ -10164,6 +10164,19 @@ "requires": { "expand-tilde": "^2.0.0", "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } } }, "resolve-from": { @@ -10335,9 +10348,9 @@ } }, "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", + "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==" }, "semver-compare": { "version": "1.0.0", @@ -12022,32 +12035,111 @@ } }, "webpack-cli": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.4.tgz", - "integrity": "sha512-ubJGQEKMtBSpT+LiL5hXvn2GIOWiRWItR1DGUqJRhwRBeGhpRXjvF5f0erqdRJLErkfqS5/Ldkkedh4AL5Q1ZQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.5.tgz", + "integrity": "sha512-w0j/s42c5UhchwTmV/45MLQnTVwRoaUTu9fM5LuyOd/8lFoCNCELDogFoecx5NzRUndO0yD/gF2b02XKMnmAWQ==", "dev": true, "requires": { - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.0", - "findup-sync": "^2.0.0", - "global-modules": "^1.0.0", - "import-local": "^2.0.0", - "interpret": "^1.1.0", - "loader-utils": "^1.1.0", - "prettier": "^1.17.0", - "supports-color": "^5.5.0", - "v8-compile-cache": "^2.0.2", - "yargs": "^12.0.5" + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" }, "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } diff --git a/package.json b/package.json index d0fd3c39b7..f34c3ddf83 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "portfinder": "^1.0.20", "schema-utils": "^1.0.0", "selfsigned": "^1.10.4", - "semver": "^6.1.1", + "semver": "^6.1.2", "serve-index": "^1.9.1", "sockjs": "0.3.19", "sockjs-client": "1.3.0", @@ -107,7 +107,7 @@ "tcp-port-used": "^1.0.1", "url-loader": "^1.1.2", "webpack": "^4.35.0", - "webpack-cli": "^3.3.4", + "webpack-cli": "^3.3.5", "ws": "^6.2.1" }, "peerDependencies": { From c6ee4d5f232271c5bc8585fbb2ec8daa75ff52e3 Mon Sep 17 00:00:00 2001 From: Loonride Date: Tue, 25 Jun 2019 04:12:19 -0500 Subject: [PATCH 05/65] feat(experimentally): websocket client implementation (#2058) --- client-src/clients/WebsocketClient.js | 31 +++++++++- package.json | 4 +- test/client/clients/WebsocketClient.test.js | 62 +++++++++++++++++++ .../WebsocketClient.test.js.snap | 9 +++ test/ports-map.js | 1 + 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 test/client/clients/WebsocketClient.test.js create mode 100644 test/client/clients/__snapshots__/WebsocketClient.test.js.snap diff --git a/client-src/clients/WebsocketClient.js b/client-src/clients/WebsocketClient.js index 1ae8ecf2fb..c451310062 100644 --- a/client-src/clients/WebsocketClient.js +++ b/client-src/clients/WebsocketClient.js @@ -1,5 +1,34 @@ 'use strict'; +/* global WebSocket */ + +/* eslint-disable + no-unused-vars +*/ const BaseClient = require('./BaseClient'); -module.exports = class WebsocketClient extends BaseClient {}; +module.exports = class WebsocketClient extends BaseClient { + constructor(url) { + super(); + this.client = new WebSocket(url.replace(/^http/, 'ws')); + } + + static getClientPath(options) { + return require.resolve('./WebsocketClient'); + } + + onOpen(f) { + this.client.onopen = f; + } + + onClose(f) { + this.client.onclose = f; + } + + // call f with the message string as the first argument + onMessage(f) { + this.client.onmessage = (e) => { + f(e.data); + }; + } +}; diff --git a/package.json b/package.json index f34c3ddf83..860b6638a7 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "url": "^0.11.0", "webpack-dev-middleware": "^3.7.0", "webpack-log": "^2.0.0", + "ws": "^6.2.1", "yargs": "12.0.5" }, "devDependencies": { @@ -107,8 +108,7 @@ "tcp-port-used": "^1.0.1", "url-loader": "^1.1.2", "webpack": "^4.35.0", - "webpack-cli": "^3.3.5", - "ws": "^6.2.1" + "webpack-cli": "^3.3.5" }, "peerDependencies": { "webpack": "^4.0.0" diff --git a/test/client/clients/WebsocketClient.test.js b/test/client/clients/WebsocketClient.test.js new file mode 100644 index 0000000000..47c3ddd79a --- /dev/null +++ b/test/client/clients/WebsocketClient.test.js @@ -0,0 +1,62 @@ +'use strict'; + +const http = require('http'); +const express = require('express'); +const ws = require('ws'); +const WebsocketClient = require('../../../client-src/clients/WebsocketClient'); +const port = require('../../ports-map').WebsocketClient; + +describe('WebsocketClient', () => { + let socketServer; + let listeningApp; + + beforeAll((done) => { + // eslint-disable-next-line new-cap + const app = new express(); + + listeningApp = http.createServer(app); + listeningApp.listen(port, 'localhost', () => { + socketServer = new ws.Server({ + server: listeningApp, + path: '/ws-server', + }); + done(); + }); + }); + + describe('client', () => { + it('should open, receive message, and close', (done) => { + socketServer.on('connection', (connection) => { + connection.send('hello world'); + + setTimeout(() => { + connection.close(); + }, 1000); + }); + + const client = new WebsocketClient(`http://localhost:${port}/ws-server`); + const data = []; + + client.onOpen(() => { + data.push('open'); + }); + client.onClose(() => { + data.push('close'); + }); + client.onMessage((msg) => { + data.push(msg); + }); + + setTimeout(() => { + expect(data).toMatchSnapshot(); + done(); + }, 3000); + }); + }); + + afterAll((done) => { + listeningApp.close(() => { + done(); + }); + }); +}); diff --git a/test/client/clients/__snapshots__/WebsocketClient.test.js.snap b/test/client/clients/__snapshots__/WebsocketClient.test.js.snap new file mode 100644 index 0000000000..1ffe286435 --- /dev/null +++ b/test/client/clients/__snapshots__/WebsocketClient.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`WebsocketClient client should open, receive message, and close 1`] = ` +Array [ + "open", + "hello world", + "close", +] +`; diff --git a/test/ports-map.js b/test/ports-map.js index eee60008e4..46348ceb13 100644 --- a/test/ports-map.js +++ b/test/ports-map.js @@ -35,6 +35,7 @@ const portsList = { 'sockPath-option': 1, 'stats-option': 1, ProvidePlugin: 1, + WebsocketClient: 1, }; let startPort = 8079; From 09d888fd5ed4ac2976f3c731388dfaec74832d04 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2019 12:12:44 +0300 Subject: [PATCH 06/65] chore(deps): update all minor dependencies (#2062) --- package-lock.json | 12 ++++++------ package.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5183310c2d..19e786fc52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4333,9 +4333,9 @@ } }, "eslint-config-prettier": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-5.0.0.tgz", - "integrity": "sha512-c17Aqiz5e8LEqoc/QPmYnaxQFAHTx2KlCZBPxXXjEMmNchOLnV/7j0HoPZuC+rL/tDC9bazUYOKJW9bOhftI/w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-5.1.0.tgz", + "integrity": "sha512-+tpiaLm3wl6fPW5nq0dDyVowQM0FT61lAdWZ+sDWgk6kKzgbOnCDwlcbwI38cyCBhq+Z3ret5Iofp6/gZpO0zw==", "dev": true, "requires": { "get-stdin": "^6.0.0" @@ -4450,9 +4450,9 @@ } }, "eslint-plugin-import": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz", - "integrity": "sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.0.tgz", + "integrity": "sha512-PZpAEC4gj/6DEMMoU2Df01C5c50r7zdGIN52Yfi7CvvWaYssG7Jt5R9nFG5gmqodxNOz9vQS87xk6Izdtpdrig==", "dev": true, "requires": { "array-includes": "^3.0.3", diff --git a/package.json b/package.json index 860b6638a7..95a457b3d6 100644 --- a/package.json +++ b/package.json @@ -82,9 +82,9 @@ "copy-webpack-plugin": "^5.0.3", "css-loader": "^2.1.1", "eslint": "^5.16.0", - "eslint-config-prettier": "^5.0.0", + "eslint-config-prettier": "^5.1.0", "eslint-config-webpack": "^1.2.5", - "eslint-plugin-import": "^2.17.3", + "eslint-plugin-import": "^2.18.0", "execa": "^1.0.0", "file-loader": "^3.0.1", "html-loader": "^0.5.5", From 7542f185f372b1c8f46f1268b99833812ef919db Mon Sep 17 00:00:00 2001 From: Loonride Date: Tue, 25 Jun 2019 17:50:52 -0500 Subject: [PATCH 07/65] test(server/client): make sockjs implementation tests use snapshots (#2070) --- test/client/clients/SockJSClient.test.js | 5 +---- .../clients/__snapshots__/SockJSClient.test.js.snap | 9 +++++++++ test/server/servers/SockJSServer.test.js | 5 +---- .../servers/__snapshots__/SockJSServer.test.js.snap | 9 +++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 test/client/clients/__snapshots__/SockJSClient.test.js.snap create mode 100644 test/server/servers/__snapshots__/SockJSServer.test.js.snap diff --git a/test/client/clients/SockJSClient.test.js b/test/client/clients/SockJSClient.test.js index a5b667c03a..9efe13ad95 100644 --- a/test/client/clients/SockJSClient.test.js +++ b/test/client/clients/SockJSClient.test.js @@ -48,10 +48,7 @@ describe('SockJSClient', () => { }); setTimeout(() => { - expect(data.length).toEqual(3); - expect(data[0]).toEqual('open'); - expect(data[1]).toEqual('hello world'); - expect(data[2]).toEqual('close'); + expect(data).toMatchSnapshot(); done(); }, 3000); }); diff --git a/test/client/clients/__snapshots__/SockJSClient.test.js.snap b/test/client/clients/__snapshots__/SockJSClient.test.js.snap new file mode 100644 index 0000000000..d68fbcda0e --- /dev/null +++ b/test/client/clients/__snapshots__/SockJSClient.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SockJSClient client should open, receive message, and close 1`] = ` +Array [ + "open", + "hello world", + "close", +] +`; diff --git a/test/server/servers/SockJSServer.test.js b/test/server/servers/SockJSServer.test.js index d1024c5910..9ea6d39b2a 100644 --- a/test/server/servers/SockJSServer.test.js +++ b/test/server/servers/SockJSServer.test.js @@ -54,10 +54,7 @@ describe('SockJSServer', () => { }; setTimeout(() => { - expect(data.length).toEqual(3); - expect(data[0]).toEqual('open'); - expect(data[1]).toEqual('hello world'); - expect(data[2]).toEqual('close'); + expect(data).toMatchSnapshot(); done(); }, 3000); }); diff --git a/test/server/servers/__snapshots__/SockJSServer.test.js.snap b/test/server/servers/__snapshots__/SockJSServer.test.js.snap new file mode 100644 index 0000000000..34076e2176 --- /dev/null +++ b/test/server/servers/__snapshots__/SockJSServer.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SockJSServer server should recieve connection, send message, and close client 1`] = ` +Array [ + "open", + "hello world", + "close", +] +`; From a732303eb95d9f7b28d4e0f7bde1e2d5b017e154 Mon Sep 17 00:00:00 2001 From: Loonride Date: Tue, 25 Jun 2019 17:52:57 -0500 Subject: [PATCH 08/65] feat(experimentally): WebsocketServer implementation and tests (#2056) --- lib/servers/WebsocketServer.js | 34 ++++++++-- test/ports-map.js | 1 + test/server/servers/WebsocketServer.test.js | 67 +++++++++++++++++++ .../WebsocketServer.test.js.snap | 9 +++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 test/server/servers/WebsocketServer.test.js create mode 100644 test/server/servers/__snapshots__/WebsocketServer.test.js.snap diff --git a/lib/servers/WebsocketServer.js b/lib/servers/WebsocketServer.js index e95f088f56..0b282cd8c4 100644 --- a/lib/servers/WebsocketServer.js +++ b/lib/servers/WebsocketServer.js @@ -1,8 +1,34 @@ 'use strict'; -// const ws = require('ws'); +/* eslint-disable + class-methods-use-this +*/ +const ws = require('ws'); const BaseServer = require('./BaseServer'); -// ws server implementation under construction -// will need changes in the client as well to function -module.exports = class WebsocketServer extends BaseServer {}; +module.exports = class WebsocketServer extends BaseServer { + constructor(server) { + super(server); + this.wsServer = new ws.Server({ + server: this.server.listeningApp, + path: this.server.sockPath, + }); + + this.wsServer.on('error', (err) => { + this.server.log.error(err.message); + }); + } + + send(connection, message) { + connection.send(message); + } + + close(connection) { + connection.close(); + } + + // f should return the resulting connection + onConnection(f) { + this.wsServer.on('connection', f); + } +}; diff --git a/test/ports-map.js b/test/ports-map.js index 46348ceb13..d8db8aab20 100644 --- a/test/ports-map.js +++ b/test/ports-map.js @@ -36,6 +36,7 @@ const portsList = { 'stats-option': 1, ProvidePlugin: 1, WebsocketClient: 1, + WebsocketServer: 1, }; let startPort = 8079; diff --git a/test/server/servers/WebsocketServer.test.js b/test/server/servers/WebsocketServer.test.js new file mode 100644 index 0000000000..705bb548fa --- /dev/null +++ b/test/server/servers/WebsocketServer.test.js @@ -0,0 +1,67 @@ +'use strict'; + +const http = require('http'); +const express = require('express'); +const ws = require('ws'); +const WebsocketServer = require('../../../lib/servers/WebsocketServer'); +const port = require('../../ports-map').WebsocketServer; + +describe('WebsocketServer', () => { + let socketServer; + let listeningApp; + + beforeAll((done) => { + // eslint-disable-next-line new-cap + const app = new express(); + + listeningApp = http.createServer(app); + listeningApp.listen(port, 'localhost', () => { + const server = { + log: { + error: () => {}, + debug: () => {}, + }, + sockPath: '/ws-server', + listeningApp, + }; + + socketServer = new WebsocketServer(server); + + done(); + }); + }); + + describe('server', () => { + it('should recieve connection, send message, and close client', (done) => { + const data = []; + + socketServer.onConnection((connection) => { + data.push('open'); + socketServer.send(connection, 'hello world'); + setTimeout(() => { + socketServer.close(connection); + }, 1000); + }); + + // eslint-disable-next-line new-cap + const client = new ws(`http://localhost:${port}/ws-server`); + + client.onmessage = (e) => { + data.push(e.data); + }; + + client.onclose = () => { + data.push('close'); + }; + + setTimeout(() => { + expect(data).toMatchSnapshot(); + done(); + }, 3000); + }); + }); + + afterAll((done) => { + listeningApp.close(done); + }); +}); diff --git a/test/server/servers/__snapshots__/WebsocketServer.test.js.snap b/test/server/servers/__snapshots__/WebsocketServer.test.js.snap new file mode 100644 index 0000000000..ade8385a89 --- /dev/null +++ b/test/server/servers/__snapshots__/WebsocketServer.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`WebsocketServer server should recieve connection, send message, and close client 1`] = ` +Array [ + "open", + "hello world", + "close", +] +`; From 143762596682d8da4fdc73555880be05255734d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2019 15:11:25 +0100 Subject: [PATCH 09/65] chore(deps): update dependency husky to ^2.6.0 (master) (#2071) --- package-lock.json | 17 +++++++++++------ package.json | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 19e786fc52..9d0d25b76b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1985,8 +1985,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "asynckit": { "version": "0.4.0", @@ -6589,15 +6588,16 @@ } }, "husky": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/husky/-/husky-2.5.0.tgz", - "integrity": "sha512-/aQIBaVMuzGi5X5BPliDPbHE+G+HDpWV7Zu28DiiXFMvHQcOeTsEnODWIGKyGBp7GM7rOgkxQdF+6AEo6xNtkw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-2.6.0.tgz", + "integrity": "sha512-AbBod6i0qmaPv/fb9XjOR+m/cJ6MKToLpJh+pYV8Pl7VKvOWvCXpjQK1MkFC4XLoxGX6mqBFfA/H5ugxUd9nLg==", "dev": true, "requires": { "cosmiconfig": "^5.2.1", "execa": "^1.0.0", "get-stdin": "^7.0.0", "is-ci": "^2.0.0", + "opencollective-postinstall": "^2.0.2", "pkg-dir": "^4.2.0", "please-upgrade-node": "^3.1.1", "read-pkg": "^5.1.1", @@ -9040,6 +9040,12 @@ } } }, + "opencollective-postinstall": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", + "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", + "dev": true + }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -12315,7 +12321,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, "requires": { "async-limiter": "~1.0.0" } diff --git a/package.json b/package.json index 95a457b3d6..0399bb613e 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "file-loader": "^3.0.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", - "husky": "^2.5.0", + "husky": "^2.6.0", "jest": "^24.8.0", "jest-junit": "^6.4.0", "jquery": "^3.4.1", From 8bbd08caa18e8bf4e73782c6c27c12baa1b51f2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2019 21:04:22 +0300 Subject: [PATCH 10/65] chore(deps): update dependency puppeteer to ^1.18.1 (#2072) --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d0d25b76b..200fd4f915 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9694,9 +9694,9 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "puppeteer": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.18.0.tgz", - "integrity": "sha512-NCwSN4wEIj43k4jO8Asa5nzibrIDFHWykqkZFjkGr0/f6U73k1ysql0gadQmOGLtZewXvvWqlNo+4ZMgX+5vZA==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.18.1.tgz", + "integrity": "sha512-luUy0HPSuWPsPZ1wAp6NinE0zgetWtudf5zwZ6dHjMWfYpTQcmKveFRox7VBNhQ98OjNA9PQ9PzQyX8k/KrxTg==", "dev": true, "requires": { "debug": "^4.1.0", diff --git a/package.json b/package.json index 0399bb613e..0b99c24415 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "memfs": "^2.15.4", "npm-run-all": "^4.1.5", "prettier": "^1.18.2", - "puppeteer": "^1.18.0", + "puppeteer": "^1.18.1", "rimraf": "^2.6.3", "standard-version": "^6.0.1", "style-loader": "^0.23.1", From 614101869d53c5d31e7c7cfb510ee3916f5dce64 Mon Sep 17 00:00:00 2001 From: Loonride Date: Thu, 27 Jun 2019 11:36:29 -0500 Subject: [PATCH 11/65] feat(client): add clientMode option (#1977) --- lib/Server.js | 13 ++- lib/options.json | 6 +- lib/utils/getSocketClientPath.js | 36 ++++++++ lib/utils/updateCompiler.js | 6 +- test/e2e/ClientMode.test.js | 82 +++++++++++++++++++ .../e2e/__snapshots__/ClientMode.test.js.snap | 22 +++++ .../custom-client/CustomSockJSClient.js | 41 ++++++++++ test/options.test.js | 4 + test/ports-map.js | 2 + test/server/utils/getSocketClientPath.test.js | 50 +++++++++++ 10 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 lib/utils/getSocketClientPath.js create mode 100644 test/e2e/ClientMode.test.js create mode 100644 test/e2e/__snapshots__/ClientMode.test.js.snap create mode 100644 test/fixtures/custom-client/CustomSockJSClient.js create mode 100644 test/server/utils/getSocketClientPath.test.js diff --git a/lib/Server.js b/lib/Server.js index 1c58eaea43..28986dfdaa 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -54,8 +54,6 @@ class Server { validateOptions(schema, options, 'webpack Dev Server'); - updateCompiler(compiler, options); - this.compiler = compiler; this.options = options; @@ -67,6 +65,7 @@ class Server { this.log = _log || createLogger(options); + // set serverMode default if (this.options.serverMode === undefined) { this.options.serverMode = 'sockjs'; } else { @@ -74,6 +73,16 @@ class Server { 'serverMode is an experimental option, meaning its usage could potentially change without warning' ); } + // set clientMode default + if (this.options.clientMode === undefined) { + this.options.clientMode = 'sockjs'; + } else { + this.log.warn( + 'clientMode is an experimental option, meaning its usage could potentially change without warning' + ); + } + + updateCompiler(this.compiler, this.options); // this.SocketServerImplementation is a class, so it must be instantiated before use this.socketServerImplementation = getSocketServerImplementation( diff --git a/lib/options.json b/lib/options.json index 349973498b..f410e4927b 100644 --- a/lib/options.json +++ b/lib/options.json @@ -48,6 +48,9 @@ "warning" ] }, + "clientMode": { + "type": "string" + }, "compress": { "type": "boolean" }, @@ -390,6 +393,7 @@ "ca": "should be {String|Buffer}", "cert": "should be {String|Buffer}", "clientLogLevel": "should be {String} and equal to one of the allowed values\n\n [ 'none', 'silent', 'info', 'debug', 'trace', 'error', 'warning', 'warn' ]\n\n (https://webpack.js.org/configuration/dev-server/#devserverclientloglevel)", + "clientMode": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverclientmode)", "compress": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devservercompress)", "contentBase": "should be {Number|String|Array} (https://webpack.js.org/configuration/dev-server/#devservercontentbase)", "disableHostCheck": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverdisablehostcheck)", @@ -430,7 +434,7 @@ "reporter": "should be {Function} (https://github.com/webpack/webpack-dev-middleware#reporter)", "requestCert": "should be {Boolean}", "serveIndex": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverserveindex)", - "serverMode": "should be {String|Function} (https://webpack.js.org/configuration/dev-server/#devserverservermode-)", + "serverMode": "should be {String|Function} (https://webpack.js.org/configuration/dev-server/#devserverservermode)", "serverSideRender": "should be {Boolean} (https://github.com/webpack/webpack-dev-middleware#serversiderender)", "setup": "should be {Function} (https://webpack.js.org/configuration/dev-server/#devserversetup)", "sockHost": "should be {String|Null} (https://webpack.js.org/configuration/dev-server/#devserversockhost)", diff --git a/lib/utils/getSocketClientPath.js b/lib/utils/getSocketClientPath.js new file mode 100644 index 0000000000..950d07802a --- /dev/null +++ b/lib/utils/getSocketClientPath.js @@ -0,0 +1,36 @@ +'use strict'; + +function getSocketClientPath(options) { + let ClientImplementation; + let clientImplFound = true; + switch (typeof options.clientMode) { + case 'string': + // could be 'sockjs', in the future 'ws', or a path that should be required + if (options.clientMode === 'sockjs') { + // eslint-disable-next-line global-require + ClientImplementation = require('../../client/clients/SockJSClient'); + } else { + try { + // eslint-disable-next-line global-require, import/no-dynamic-require + ClientImplementation = require(options.clientMode); + } catch (e) { + clientImplFound = false; + } + } + break; + default: + clientImplFound = false; + } + + if (!clientImplFound) { + throw new Error( + "clientMode must be a string denoting a default implementation (e.g. 'sockjs') or a full path to " + + 'a JS file which exports a class extending BaseClient (webpack-dev-server/client-src/clients/BaseClient) ' + + 'via require.resolve(...)' + ); + } + + return ClientImplementation.getClientPath(options); +} + +module.exports = getSocketClientPath; diff --git a/lib/utils/updateCompiler.js b/lib/utils/updateCompiler.js index 60e7ff8a5a..9f701e81b4 100644 --- a/lib/utils/updateCompiler.js +++ b/lib/utils/updateCompiler.js @@ -6,6 +6,7 @@ */ const webpack = require('webpack'); const addEntries = require('./addEntries'); +const getSocketClientPath = require('./getSocketClientPath'); function updateCompiler(compiler, options) { if (options.inline !== false) { @@ -50,10 +51,7 @@ function updateCompiler(compiler, options) { compiler.hooks.entryOption.call(config.context, config.entry); const providePlugin = new webpack.ProvidePlugin({ - // SockJSClient.getClientPath(options) - __webpack_dev_server_client__: require.resolve( - '../../client/clients/SockJSClient.js' - ), + __webpack_dev_server_client__: getSocketClientPath(options), }); providePlugin.apply(compiler); }); diff --git a/test/e2e/ClientMode.test.js b/test/e2e/ClientMode.test.js new file mode 100644 index 0000000000..8f7b5d7a54 --- /dev/null +++ b/test/e2e/ClientMode.test.js @@ -0,0 +1,82 @@ +'use strict'; + +const testServer = require('../helpers/test-server'); +const config = require('../fixtures/client-config/webpack.config'); +const runBrowser = require('../helpers/run-browser'); +const port = require('../ports-map').ClientMode; + +describe('clientMode', () => { + describe('sockjs', () => { + beforeAll((done) => { + const options = { + port, + host: '0.0.0.0', + inline: true, + clientMode: 'sockjs', + }; + testServer.startAwaitingCompilation(config, options, done); + }); + + describe('on browser client', () => { + it('logs as usual', (done) => { + runBrowser().then(({ page, browser }) => { + const res = []; + page.goto(`http://localhost:${port}/main`); + page.on('console', ({ _text }) => { + res.push(_text); + }); + + setTimeout(() => { + testServer.close(() => { + // make sure the client gets the close message + setTimeout(() => { + browser.close().then(() => { + expect(res).toMatchSnapshot(); + done(); + }); + }, 1000); + }); + }, 3000); + }); + }); + }); + }); + + describe('custom client', () => { + beforeAll((done) => { + const options = { + port, + host: '0.0.0.0', + inline: true, + clientMode: require.resolve( + '../fixtures/custom-client/CustomSockJSClient' + ), + }; + testServer.startAwaitingCompilation(config, options, done); + }); + + describe('on browser client', () => { + it('logs additional messages to console', (done) => { + runBrowser().then(({ page, browser }) => { + const res = []; + page.goto(`http://localhost:${port}/main`); + page.on('console', ({ _text }) => { + res.push(_text); + }); + + setTimeout(() => { + testServer.close(() => { + // make sure the client gets the close message + setTimeout(() => { + browser.close().then(() => { + expect(res).toMatchSnapshot(); + done(); + }); + }, 1000); + }); + }, 3000); + }); + }); + }); + }); +}); diff --git a/test/e2e/__snapshots__/ClientMode.test.js.snap b/test/e2e/__snapshots__/ClientMode.test.js.snap new file mode 100644 index 0000000000..e7517d8b16 --- /dev/null +++ b/test/e2e/__snapshots__/ClientMode.test.js.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`clientMode custom client on browser client logs additional messages to console 1`] = ` +Array [ + "Hey.", + "open", + "liveReload", + "[WDS] Live Reloading enabled.", + "hash", + "ok", + "close", + "[WDS] Disconnected!", +] +`; + +exports[`clientMode sockjs on browser client logs as usual 1`] = ` +Array [ + "Hey.", + "[WDS] Live Reloading enabled.", + "[WDS] Disconnected!", +] +`; diff --git a/test/fixtures/custom-client/CustomSockJSClient.js b/test/fixtures/custom-client/CustomSockJSClient.js new file mode 100644 index 0000000000..8412abeba7 --- /dev/null +++ b/test/fixtures/custom-client/CustomSockJSClient.js @@ -0,0 +1,41 @@ +'use strict'; + +/* eslint-disable + no-unused-vars +*/ +const SockJS = require('sockjs-client/dist/sockjs'); +const BaseClient = require('../../../client/clients/BaseClient'); + +module.exports = class SockJSClient extends BaseClient { + constructor(url) { + super(); + this.sock = new SockJS(url); + } + + static getClientPath(options) { + return require.resolve('./CustomSockJSClient'); + } + + onOpen(f) { + this.sock.onopen = () => { + console.log('open'); + f(); + }; + } + + onClose(f) { + this.sock.onclose = () => { + console.log('close'); + f(); + }; + } + + // call f with the message string as the first argument + onMessage(f) { + this.sock.onmessage = (e) => { + const obj = JSON.parse(e.data); + console.log(obj.type); + f(e.data); + }; + } +}; diff --git a/test/options.test.js b/test/options.test.js index 7e59879bb0..3ba18e17f7 100644 --- a/test/options.test.js +++ b/test/options.test.js @@ -137,6 +137,10 @@ describe('options', () => { ], failure: ['whoops!'], }, + clientMode: { + success: ['sockjs', require.resolve('../client/clients/SockJSClient')], + failure: [false], + }, compress: { success: [true], failure: [''], diff --git a/test/ports-map.js b/test/ports-map.js index d8db8aab20..5031f898be 100644 --- a/test/ports-map.js +++ b/test/ports-map.js @@ -1,6 +1,7 @@ 'use strict'; // test-file-name: the number of ports +// important: new port mappings must be added to the bottom of this list const portsList = { cli: 2, sockJSClient: 1, @@ -37,6 +38,7 @@ const portsList = { ProvidePlugin: 1, WebsocketClient: 1, WebsocketServer: 1, + ClientMode: 1, }; let startPort = 8079; diff --git a/test/server/utils/getSocketClientPath.test.js b/test/server/utils/getSocketClientPath.test.js new file mode 100644 index 0000000000..4eef174189 --- /dev/null +++ b/test/server/utils/getSocketClientPath.test.js @@ -0,0 +1,50 @@ +'use strict'; + +const getSocketClientPath = require('../../../lib/utils/getSocketClientPath'); +// 'npm run prepare' must be done for this test to pass +const sockjsClientPath = require.resolve( + '../../../client/clients/SockJSClient' +); +const baseClientPath = require.resolve('../../../client/clients/BaseClient'); + +describe('getSocketClientPath', () => { + it("should work with clientMode: 'sockjs'", () => { + let result; + + expect(() => { + result = getSocketClientPath({ + clientMode: 'sockjs', + }); + }).not.toThrow(); + + expect(result).toEqual(sockjsClientPath); + }); + + it('should work with clientMode: SockJSClient full path', () => { + let result; + + expect(() => { + result = getSocketClientPath({ + clientMode: sockjsClientPath, + }); + }).not.toThrow(); + + expect(result).toEqual(sockjsClientPath); + }); + + it('should throw with clientMode: bad path', () => { + expect(() => { + getSocketClientPath({ + clientMode: '/bad/path/to/implementation', + }); + }).toThrow(/clientMode must be a string/); + }); + + it('should throw with clientMode: unimplemented client', () => { + expect(() => { + getSocketClientPath({ + clientMode: baseClientPath, + }); + }).toThrow('Client needs implementation'); + }); +}); From 764b24ef3d79ba7b8e6cc09d7125d8c3955e2671 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2019 19:09:28 +0100 Subject: [PATCH 12/65] chore(deps): update dependency husky to ^2.7.0 (master) (#2076) --- package-lock.json | 40 ++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 200fd4f915..fceaa7fad2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6588,33 +6588,23 @@ } }, "husky": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/husky/-/husky-2.6.0.tgz", - "integrity": "sha512-AbBod6i0qmaPv/fb9XjOR+m/cJ6MKToLpJh+pYV8Pl7VKvOWvCXpjQK1MkFC4XLoxGX6mqBFfA/H5ugxUd9nLg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-2.7.0.tgz", + "integrity": "sha512-LIi8zzT6PyFpcYKdvWRCn/8X+6SuG2TgYYMrM6ckEYhlp44UcEduVymZGIZNLiwOUjrEud+78w/AsAiqJA/kRg==", "dev": true, "requires": { - "cosmiconfig": "^5.2.1", + "cosmiconfig": "^5.2.0", "execa": "^1.0.0", + "find-up": "^3.0.0", "get-stdin": "^7.0.0", "is-ci": "^2.0.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^4.2.0", + "pkg-dir": "^4.1.0", "please-upgrade-node": "^3.1.1", "read-pkg": "^5.1.1", "run-node": "^1.0.0", "slash": "^3.0.0" }, "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -6646,6 +6636,18 @@ "dev": true, "requires": { "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + } } }, "read-pkg": { @@ -9040,12 +9042,6 @@ } } }, - "opencollective-postinstall": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", - "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", - "dev": true - }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", diff --git a/package.json b/package.json index 0b99c24415..7ba80bcdb1 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "file-loader": "^3.0.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", - "husky": "^2.6.0", + "husky": "^2.7.0", "jest": "^24.8.0", "jest-junit": "^6.4.0", "jquery": "^3.4.1", From f984e539705f783cd1411b6fadd4e859ce1c724a Mon Sep 17 00:00:00 2001 From: Loonride Date: Fri, 28 Jun 2019 16:53:16 -0500 Subject: [PATCH 13/65] test(client): added clientMode option test (#2078) --- test/ports-map.js | 1 + test/server/clientMode-option.test.js | 102 ++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 test/server/clientMode-option.test.js diff --git a/test/ports-map.js b/test/ports-map.js index 5031f898be..6c56f8100c 100644 --- a/test/ports-map.js +++ b/test/ports-map.js @@ -39,6 +39,7 @@ const portsList = { WebsocketClient: 1, WebsocketServer: 1, ClientMode: 1, + 'clientMode-option': 1, }; let startPort = 8079; diff --git a/test/server/clientMode-option.test.js b/test/server/clientMode-option.test.js new file mode 100644 index 0000000000..05f51e5201 --- /dev/null +++ b/test/server/clientMode-option.test.js @@ -0,0 +1,102 @@ +'use strict'; + +const config = require('../fixtures/simple-config/webpack.config'); +const port = require('../ports-map')['clientMode-option']; + +describe('clientMode option', () => { + let mockedTestServer; + let testServer; + let getSocketClientPath; + + const clientModes = [ + { + title: 'as a string ("sockjs")', + clientMode: 'sockjs', + shouldThrow: false, + }, + { + title: 'as a path ("sockjs")', + clientMode: require.resolve('../../client-src/clients/SockJSClient'), + shouldThrow: false, + }, + { + title: 'as a nonexistent path', + clientMode: '/bad/path/to/implementation', + shouldThrow: true, + }, + ]; + + describe('is passed to getSocketClientPath correctly', () => { + beforeEach(() => { + jest.mock('../../lib/utils/getSocketClientPath'); + // eslint-disable-next-line global-require + getSocketClientPath = require('../../lib/utils/getSocketClientPath'); + }); + + afterEach((done) => { + jest.resetAllMocks(); + jest.resetModules(); + + mockedTestServer.close(done); + }); + + clientModes.forEach((data) => { + it(data.title, (done) => { + // eslint-disable-next-line global-require + mockedTestServer = require('../helpers/test-server'); + mockedTestServer.start( + config, + { + inline: true, + clientMode: data.clientMode, + port, + }, + () => { + expect(getSocketClientPath.mock.calls.length).toEqual(1); + expect(getSocketClientPath.mock.calls[0].length).toEqual(1); + expect(getSocketClientPath.mock.calls[0][0].clientMode).toEqual( + data.clientMode + ); + done(); + } + ); + }); + }); + }); + + describe('passed to server', () => { + beforeAll(() => { + jest.unmock('../../lib/utils/getSocketClientPath'); + // eslint-disable-next-line global-require + testServer = require('../helpers/test-server'); + }); + + afterEach((done) => { + testServer.close(done); + }); + + clientModes.forEach((data) => { + it(`${data.title} ${ + data.shouldThrow ? 'should throw' : 'should not throw' + }`, (done) => { + const res = () => { + testServer.start( + config, + { + inline: true, + clientMode: data.clientMode, + port, + }, + done + ); + }; + if (data.shouldThrow) { + expect(res).toThrow(/clientMode must be a string/); + done(); + } else { + expect(res).not.toThrow(); + } + }); + }); + }); +}); From 96b5ab992cef457ae5db52da6e2c999b80b040f6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 15:11:41 +0100 Subject: [PATCH 14/65] chore(deps): update all patch dependencies (master) (patch) (#2081) --- package-lock.json | 30 +++++++++++++++--------------- package.json | 6 +++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index fceaa7fad2..2cf48721ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4096,9 +4096,9 @@ "dev": true }, "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz", + "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -8388,9 +8388,9 @@ } }, "marked": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.2.tgz", - "integrity": "sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.3.tgz", + "integrity": "sha512-Fqa7eq+UaxfMriqzYLayfqAE40WN03jf+zHjT18/uXNuzjq3TY0XTbrAoPeqSJrAmPz11VuUA+kBPYOhHt9oOQ==", "dev": true }, "matcher": { @@ -10350,9 +10350,9 @@ } }, "semver": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", - "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==" + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.3.tgz", + "integrity": "sha512-aymF+56WJJMyXQHcd4hlK4N75rwj5RQpfW8ePlQnJsTYOBLlLbcIErR/G1s9SkIvKBqOudR3KAx4wEqP+F1hNQ==" }, "semver-compare": { "version": "1.0.0", @@ -11369,9 +11369,9 @@ } }, "terser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.0.0.tgz", - "integrity": "sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.0.2.tgz", + "integrity": "sha512-IWLuJqTvx97KP3uTYkFVn93cXO+EtlzJu8TdJylq+H0VBDlPMIfQA9MBS5Vc5t3xTEUG1q0hIfHMpAP2R+gWTw==", "dev": true, "requires": { "commander": "^2.19.0", @@ -12005,9 +12005,9 @@ "dev": true }, "webpack": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.35.0.tgz", - "integrity": "sha512-M5hL3qpVvtr8d4YaJANbAQBc4uT01G33eDpl/psRTBCfjxFTihdhin1NtAKB1ruDwzeVdcsHHV3NX+QsAgOosw==", + "version": "4.35.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.35.2.tgz", + "integrity": "sha512-TZAmorNymV4q66gAM/h90cEjG+N3627Q2MnkSgKlX/z3DlNVKUtqy57lz1WmZU2+FUZwzM+qm7cGaO95PyrX5A==", "dev": true, "requires": { "@webassemblyjs/ast": "1.8.5", diff --git a/package.json b/package.json index 7ba80bcdb1..e81df075d3 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "portfinder": "^1.0.20", "schema-utils": "^1.0.0", "selfsigned": "^1.10.4", - "semver": "^6.1.2", + "semver": "^6.1.3", "serve-index": "^1.9.1", "sockjs": "0.3.19", "sockjs-client": "1.3.0", @@ -96,7 +96,7 @@ "less": "^3.9.0", "less-loader": "^5.0.0", "lint-staged": "^8.2.1", - "marked": "^0.6.2", + "marked": "^0.6.3", "memfs": "^2.15.4", "npm-run-all": "^4.1.5", "prettier": "^1.18.2", @@ -107,7 +107,7 @@ "supertest": "^4.0.2", "tcp-port-used": "^1.0.1", "url-loader": "^1.1.2", - "webpack": "^4.35.0", + "webpack": "^4.35.2", "webpack-cli": "^3.3.5" }, "peerDependencies": { From 7f518590d99e3fb2a4bbc6ac3c7c7692a796ecfb Mon Sep 17 00:00:00 2001 From: Loonride Date: Mon, 1 Jul 2019 10:12:23 -0500 Subject: [PATCH 15/65] fix(server): fix header check for socket server (#2077) * fix(server): fix header check for socket server * test(server): tests to check header error and server methods --- lib/Server.js | 11 +-- lib/servers/SockJSServer.js | 10 +- lib/servers/WebsocketServer.js | 8 +- .../serverMode-option.test.js.snap | 9 ++ test/server/serverMode-option.test.js | 92 ++++++++++++++++++- test/server/servers/SockJSServer.test.js | 28 +++++- test/server/servers/WebsocketServer.test.js | 28 +++++- .../__snapshots__/SockJSServer.test.js.snap | 4 +- .../WebsocketServer.test.js.snap | 4 +- 9 files changed, 177 insertions(+), 17 deletions(-) create mode 100644 test/server/__snapshots__/serverMode-option.test.js.snap diff --git a/lib/Server.js b/lib/Server.js index 28986dfdaa..597a4c61ce 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -677,25 +677,22 @@ class Server { const SocketServerImplementation = this.socketServerImplementation; this.socketServer = new SocketServerImplementation(this); - this.socketServer.onConnection((connection) => { + this.socketServer.onConnection((connection, headers) => { if (!connection) { return; } - if ( - !this.checkHost(connection.headers) || - !this.checkOrigin(connection.headers) - ) { + if (headers && (!this.checkHost(headers) || !this.checkOrigin(headers))) { this.sockWrite([connection], 'error', 'Invalid Host/Origin header'); - connection.close(); + this.socketServer.close(connection); return; } this.sockets.push(connection); - connection.on('close', () => { + this.socketServer.onConnectionClose(connection, () => { const idx = this.sockets.indexOf(connection); if (idx >= 0) { diff --git a/lib/servers/SockJSServer.js b/lib/servers/SockJSServer.js index 8c67abd4e5..49c42538d2 100644 --- a/lib/servers/SockJSServer.js +++ b/lib/servers/SockJSServer.js @@ -57,8 +57,14 @@ module.exports = class SockJSServer extends BaseServer { connection.close(); } - // f should return the resulting connection + // f should return the resulting connection and, optionally, the connection headers onConnection(f) { - this.socket.on('connection', f); + this.socket.on('connection', (connection) => { + f(connection, connection.headers); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); } }; diff --git a/lib/servers/WebsocketServer.js b/lib/servers/WebsocketServer.js index 0b282cd8c4..03c5a56e46 100644 --- a/lib/servers/WebsocketServer.js +++ b/lib/servers/WebsocketServer.js @@ -29,6 +29,12 @@ module.exports = class WebsocketServer extends BaseServer { // f should return the resulting connection onConnection(f) { - this.wsServer.on('connection', f); + this.wsServer.on('connection', (connection, req) => { + f(connection, req.headers); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); } }; diff --git a/test/server/__snapshots__/serverMode-option.test.js.snap b/test/server/__snapshots__/serverMode-option.test.js.snap new file mode 100644 index 0000000000..a7ad43ca4e --- /dev/null +++ b/test/server/__snapshots__/serverMode-option.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`serverMode option with a bad host header results in an error 1`] = ` +Array [ + "open", + "{\\"type\\":\\"error\\",\\"data\\":\\"Invalid Host/Origin header\\"}", + "close", +] +`; diff --git a/test/server/serverMode-option.test.js b/test/server/serverMode-option.test.js index b0ef7357fb..8a660ddab6 100644 --- a/test/server/serverMode-option.test.js +++ b/test/server/serverMode-option.test.js @@ -5,6 +5,7 @@ */ const request = require('supertest'); const sockjs = require('sockjs'); +const SockJS = require('sockjs-client/dist/sockjs'); const SockJSServer = require('../../lib/servers/SockJSServer'); const config = require('../fixtures/simple-config/webpack.config'); const testServer = require('../helpers/test-server'); @@ -77,6 +78,7 @@ describe('serverMode option', () => { describe('as a class (custom "sockjs" implementation)', () => { it('uses supplied server implementation', (done) => { + let sockPath; server = testServer.start( config, { @@ -102,7 +104,7 @@ describe('serverMode option', () => { prefix: this.server.sockPath, }); - expect(server.options.sockPath).toEqual('/foo/test/bar/'); + sockPath = server.options.sockPath; } send(connection, message) { @@ -114,11 +116,20 @@ describe('serverMode option', () => { } onConnection(f) { - this.socket.on('connection', f); + this.socket.on('connection', (connection) => { + f(connection, connection.headers); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); } }, }, - done + () => { + expect(sockPath).toEqual('/foo/test/bar/'); + done(); + } ); }); }); @@ -137,4 +148,79 @@ describe('serverMode option', () => { }).toThrow(/serverMode must be a string/); }); }); + + describe('with a bad host header', () => { + beforeAll((done) => { + server = testServer.start( + config, + { + port, + serverMode: class MySockJSServer extends BaseServer { + constructor(serv) { + super(serv); + this.socket = sockjs.createServer({ + // Use provided up-to-date sockjs-client + sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', + // Limit useless logs + log: (severity, line) => { + if (severity === 'error') { + this.server.log.error(line); + } else { + this.server.log.debug(line); + } + }, + }); + + this.socket.installHandlers(this.server.listeningApp, { + prefix: this.server.sockPath, + }); + } + + send(connection, message) { + connection.write(message); + } + + close(connection) { + connection.close(); + } + + onConnection(f) { + this.socket.on('connection', (connection) => { + f(connection, { + host: null, + }); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); + } + }, + }, + done + ); + }); + + it('results in an error', (done) => { + const data = []; + const client = new SockJS(`http://localhost:${port}/sockjs-node`); + + client.onopen = () => { + data.push('open'); + }; + + client.onmessage = (e) => { + data.push(e.data); + }; + + client.onclose = () => { + data.push('close'); + }; + + setTimeout(() => { + expect(data).toMatchSnapshot(); + done(); + }, 5000); + }); + }); }); diff --git a/test/server/servers/SockJSServer.test.js b/test/server/servers/SockJSServer.test.js index 9ea6d39b2a..9c61c67e14 100644 --- a/test/server/servers/SockJSServer.test.js +++ b/test/server/servers/SockJSServer.test.js @@ -35,10 +35,13 @@ describe('SockJSServer', () => { it('should recieve connection, send message, and close client', (done) => { const data = []; - socketServer.onConnection((connection) => { + let headers; + socketServer.onConnection((connection, h) => { + headers = h; data.push('open'); socketServer.send(connection, 'hello world'); setTimeout(() => { + // the server closes the connection with the client socketServer.close(connection); }, 1000); }); @@ -54,10 +57,33 @@ describe('SockJSServer', () => { }; setTimeout(() => { + expect(headers.host).toMatchSnapshot(); expect(data).toMatchSnapshot(); done(); }, 3000); }); + + it('should receive client close event', (done) => { + let receivedClientClose = false; + socketServer.onConnection((connection) => { + socketServer.onConnectionClose(connection, () => { + receivedClientClose = true; + }); + }); + + // eslint-disable-next-line new-cap + const client = new SockJS(`http://localhost:${port}/sockjs-node`); + + setTimeout(() => { + // the client closes itself, the server does not close it + client.close(); + }, 1000); + + setTimeout(() => { + expect(receivedClientClose).toBeTruthy(); + done(); + }, 3000); + }); }); afterAll((done) => { diff --git a/test/server/servers/WebsocketServer.test.js b/test/server/servers/WebsocketServer.test.js index 705bb548fa..e8e59a786b 100644 --- a/test/server/servers/WebsocketServer.test.js +++ b/test/server/servers/WebsocketServer.test.js @@ -35,10 +35,13 @@ describe('WebsocketServer', () => { it('should recieve connection, send message, and close client', (done) => { const data = []; - socketServer.onConnection((connection) => { + let headers; + socketServer.onConnection((connection, h) => { + headers = h; data.push('open'); socketServer.send(connection, 'hello world'); setTimeout(() => { + // the server closes the connection with the client socketServer.close(connection); }, 1000); }); @@ -55,10 +58,33 @@ describe('WebsocketServer', () => { }; setTimeout(() => { + expect(headers.host).toMatchSnapshot(); expect(data).toMatchSnapshot(); done(); }, 3000); }); + + it('should receive client close event', (done) => { + let receivedClientClose = false; + socketServer.onConnection((connection) => { + socketServer.onConnectionClose(connection, () => { + receivedClientClose = true; + }); + }); + + // eslint-disable-next-line new-cap + const client = new ws(`http://localhost:${port}/ws-server`); + + setTimeout(() => { + // the client closes itself, the server does not close it + client.close(); + }, 1000); + + setTimeout(() => { + expect(receivedClientClose).toBeTruthy(); + done(); + }, 3000); + }); }); afterAll((done) => { diff --git a/test/server/servers/__snapshots__/SockJSServer.test.js.snap b/test/server/servers/__snapshots__/SockJSServer.test.js.snap index 34076e2176..8bc35a0b99 100644 --- a/test/server/servers/__snapshots__/SockJSServer.test.js.snap +++ b/test/server/servers/__snapshots__/SockJSServer.test.js.snap @@ -1,6 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`SockJSServer server should recieve connection, send message, and close client 1`] = ` +exports[`SockJSServer server should recieve connection, send message, and close client 1`] = `"localhost:8083"`; + +exports[`SockJSServer server should recieve connection, send message, and close client 2`] = ` Array [ "open", "hello world", diff --git a/test/server/servers/__snapshots__/WebsocketServer.test.js.snap b/test/server/servers/__snapshots__/WebsocketServer.test.js.snap index ade8385a89..abaaaa8c48 100644 --- a/test/server/servers/__snapshots__/WebsocketServer.test.js.snap +++ b/test/server/servers/__snapshots__/WebsocketServer.test.js.snap @@ -1,6 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`WebsocketServer server should recieve connection, send message, and close client 1`] = ` +exports[`WebsocketServer server should recieve connection, send message, and close client 1`] = `"localhost:8121"`; + +exports[`WebsocketServer server should recieve connection, send message, and close client 2`] = ` Array [ "open", "hello world", From fa78347d089ed440c03ed09eb93606f40b4b55b8 Mon Sep 17 00:00:00 2001 From: Eslam El-Hakmey Date: Mon, 1 Jul 2019 18:03:42 +0200 Subject: [PATCH 16/65] fix(server): check for external urls in array (#1980) * fix: check for external urls in array * test: move tests to contentBase * fix: use is-absolute-url & add test case for number type --- lib/Server.js | 15 ++++---- lib/utils/createConfig.js | 5 +-- test/server/contentBase-option.test.js | 50 ++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/lib/Server.js b/lib/Server.js index 597a4c61ce..26d16dfadf 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -23,6 +23,7 @@ const serveIndex = require('serve-index'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const validateOptions = require('schema-utils'); +const isAbsoluteUrl = require('is-absolute-url'); const updateCompiler = require('./utils/updateCompiler'); const createLogger = require('./utils/createLogger'); const getCertificate = require('./utils/getCertificate'); @@ -356,7 +357,7 @@ class Server { contentBase.forEach((item) => { this.app.get('*', express.static(item)); }); - } else if (/^(https?:)?\/\//.test(contentBase)) { + } else if (isAbsoluteUrl(String(contentBase))) { this.log.warn( 'Using a URL as contentBase is deprecated and will be removed in the next major version. Please use the proxy option instead.' ); @@ -408,8 +409,8 @@ class Server { this.app.get('*', serveIndex(item)); }); } else if ( - !/^(https?:)?\/\//.test(contentBase) && - typeof contentBase !== 'number' + typeof contentBase !== 'number' && + !isAbsoluteUrl(String(contentBase)) ) { this.app.get('*', serveIndex(contentBase)); } @@ -418,13 +419,13 @@ class Server { setupWatchStaticFeature() { const contentBase = this.options.contentBase; - if ( - /^(https?:)?\/\//.test(contentBase) || - typeof contentBase === 'number' - ) { + if (isAbsoluteUrl(String(contentBase)) || typeof contentBase === 'number') { throw new Error('Watching remote files is not supported.'); } else if (Array.isArray(contentBase)) { contentBase.forEach((item) => { + if (isAbsoluteUrl(String(item))) { + throw new Error('Watching remote files is not supported.'); + } this._watch(item); }); } else { diff --git a/lib/utils/createConfig.js b/lib/utils/createConfig.js index c16a95138b..1902466620 100644 --- a/lib/utils/createConfig.js +++ b/lib/utils/createConfig.js @@ -1,6 +1,7 @@ 'use strict'; const path = require('path'); +const isAbsoluteUrl = require('is-absolute-url'); const defaultTo = require('./defaultTo'); function createConfig(config, argv, { port }) { @@ -60,7 +61,7 @@ function createConfig(config, argv, { port }) { (firstWpOpt.output && firstWpOpt.output.publicPath) || ''; if ( - !/^(https?:)?\/\//.test(options.publicPath) && + !isAbsoluteUrl(String(options.publicPath)) && options.publicPath[0] !== '/' ) { options.publicPath = `/${options.publicPath}`; @@ -109,7 +110,7 @@ function createConfig(config, argv, { port }) { options.contentBase = options.contentBase.map((p) => path.resolve(p)); } else if (/^[0-9]$/.test(options.contentBase)) { options.contentBase = +options.contentBase; - } else if (!/^(https?:)?\/\//.test(options.contentBase)) { + } else if (!isAbsoluteUrl(String(options.contentBase))) { options.contentBase = path.resolve(options.contentBase); } } diff --git a/test/server/contentBase-option.test.js b/test/server/contentBase-option.test.js index baa4111124..3d6ab7de58 100644 --- a/test/server/contentBase-option.test.js +++ b/test/server/contentBase-option.test.js @@ -267,6 +267,56 @@ describe('contentBase option', () => { }); }); + describe('testing single & multiple external paths', () => { + afterAll((done) => { + testServer.close(() => { + done(); + }); + }); + it('Should throw exception (string)', (done) => { + try { + // eslint-disable-next-line no-unused-vars + server = testServer.start(config, { + contentBase: 'https://example.com/', + watchContentBase: true, + }); + + expect(true).toBe(false); + } catch (e) { + expect(e.message).toBe('Watching remote files is not supported.'); + done(); + } + }); + it('Should throw exception (number)', (done) => { + try { + // eslint-disable-next-line no-unused-vars + server = testServer.start(config, { + contentBase: 2, + watchContentBase: true, + }); + + expect(true).toBe(false); + } catch (e) { + expect(e.message).toBe('Watching remote files is not supported.'); + done(); + } + }); + it('Should throw exception (array)', (done) => { + try { + // eslint-disable-next-line no-unused-vars + server = testServer.start(config, { + contentBase: [contentBasePublic, 'https://example.com/'], + watchContentBase: true, + }); + + expect(true).toBe(false); + } catch (e) { + expect(e.message).toBe('Watching remote files is not supported.'); + done(); + } + }); + }); + describe('default to PWD', () => { beforeAll((done) => { jest.spyOn(process, 'cwd').mockImplementation(() => contentBasePublic); From 56274e4baf8737873ce6d97cb4e8a4b8d4c95039 Mon Sep 17 00:00:00 2001 From: Loonride Date: Mon, 1 Jul 2019 11:43:01 -0500 Subject: [PATCH 17/65] feat(server/client): made progress option available to API (#1961) * feat(server/client): made progress option available to API * test(client): switched snapshot test to single line confirmation * refactor(server): removed this.profile * test(client): fixed progress test css path * test(client): remove jest timeout * test(e2e): change how use of progress on client checked * test(e2e): moved css unlink into afterAll * test(e2e): use port assigner * test(client): add full progress snapshot * test(client): reg exp progress test * test(client): check end of progress updates in console * test(client): more generalized test reg exp * test(client): test to isolate ci problem * test(client): made custom progress plugin to test * test(client): new progress plugin multi handler test * test(client): more testing to identify progress problem * test(client): test with 1 progress plugin * test(client): new console.log to test sending of data * feat(server): re-add two progress plugins * test(client): revert to original changes * test(progress): added progress and profile option tests * fix(test): fix profile test port map --- bin/options.js | 4 ++ bin/webpack-dev-server.js | 6 --- lib/Server.js | 26 ++++++----- lib/options.json | 4 ++ lib/utils/createConfig.js | 4 ++ test/cli/cli.test.js | 17 +++++++ test/e2e/Progress.test.js | 71 +++++++++++++++++++++++++++++ test/options.test.js | 4 ++ test/ports-map.js | 3 ++ test/server/profile-option.test.js | 53 +++++++++++++++++++++ test/server/progress-option.test.js | 51 +++++++++++++++++++++ 11 files changed, 225 insertions(+), 18 deletions(-) create mode 100644 test/e2e/Progress.test.js create mode 100644 test/server/profile-option.test.js create mode 100644 test/server/progress-option.test.js diff --git a/bin/options.js b/bin/options.js index 3c46d9be9f..424ac2a21a 100644 --- a/bin/options.js +++ b/bin/options.js @@ -37,6 +37,10 @@ const options = { describe: 'Inline mode (set to false to disable including client scripts like livereload)', }, + profile: { + type: 'boolean', + describe: 'Print compilation profile data for progress steps', + }, progress: { type: 'boolean', describe: 'Print compilation progress in percentage', diff --git a/bin/webpack-dev-server.js b/bin/webpack-dev-server.js index deb015f6f1..26de7d4ba6 100755 --- a/bin/webpack-dev-server.js +++ b/bin/webpack-dev-server.js @@ -97,12 +97,6 @@ function startDevServer(config, options) { throw err; } - if (options.progress) { - new webpack.ProgressPlugin({ - profile: argv.profile, - }).apply(compiler); - } - try { server = new Server(compiler, options, log); } catch (err) { diff --git a/lib/Server.js b/lib/Server.js index 26d16dfadf..5538f6f454 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -154,23 +154,25 @@ class Server { } setupProgressPlugin() { - const progressPlugin = new webpack.ProgressPlugin( - (percent, msg, addInfo) => { - percent = Math.floor(percent * 100); + // for CLI output + new webpack.ProgressPlugin({ + profile: !!this.options.profile, + }).apply(this.compiler); - if (percent === 100) { - msg = 'Compilation completed'; - } + // for browser console output + new webpack.ProgressPlugin((percent, msg, addInfo) => { + percent = Math.floor(percent * 100); - if (addInfo) { - msg = `${msg} (${addInfo})`; - } + if (percent === 100) { + msg = 'Compilation completed'; + } - this.sockWrite(this.sockets, 'progress-update', { percent, msg }); + if (addInfo) { + msg = `${msg} (${addInfo})`; } - ); - progressPlugin.apply(this.compiler); + this.sockWrite(this.sockets, 'progress-update', { percent, msg }); + }).apply(this.compiler); } setupApp() { diff --git a/lib/options.json b/lib/options.json index f410e4927b..26dba30afc 100644 --- a/lib/options.json +++ b/lib/options.json @@ -258,6 +258,9 @@ } ] }, + "profile": { + "type": "boolean" + }, "progress": { "type": "boolean" }, @@ -426,6 +429,7 @@ "pfx": "should be {String|Buffer} (https://webpack.js.org/configuration/dev-server/#devserverpfx)", "pfxPassphrase": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpfxpassphrase)", "port": "should be {Number|String|Null} (https://webpack.js.org/configuration/dev-server/#devserverport)", + "profile": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverprofile)", "progress": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverprogress---cli-only)", "proxy": "should be {Object|Array} (https://webpack.js.org/configuration/dev-server/#devserverproxy)", "public": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpublic)", diff --git a/lib/utils/createConfig.js b/lib/utils/createConfig.js index 1902466620..0211561235 100644 --- a/lib/utils/createConfig.js +++ b/lib/utils/createConfig.js @@ -47,6 +47,10 @@ function createConfig(config, argv, { port }) { options.liveReload = false; } + if (argv.profile) { + options.profile = argv.profile; + } + if (argv.progress) { options.progress = argv.progress; } diff --git a/test/cli/cli.test.js b/test/cli/cli.test.js index 8bd1e04ddd..91e82b7bc6 100644 --- a/test/cli/cli.test.js +++ b/test/cli/cli.test.js @@ -20,6 +20,23 @@ describe('CLI', () => { .then((output) => { expect(output.code).toEqual(0); expect(output.stderr.includes('0% compiling')).toBe(true); + // should not profile + expect( + output.stderr.includes('ms after chunk modules optimization') + ).toBe(false); + done(); + }) + .catch(done); + }); + + it('--progress --profile', (done) => { + testBin('--progress --profile') + .then((output) => { + expect(output.code).toEqual(0); + // should profile + expect( + output.stderr.includes('ms after chunk modules optimization') + ).toBe(true); done(); }) .catch(done); diff --git a/test/e2e/Progress.test.js b/test/e2e/Progress.test.js new file mode 100644 index 0000000000..dd1a3588a6 --- /dev/null +++ b/test/e2e/Progress.test.js @@ -0,0 +1,71 @@ +'use strict'; + +/* eslint-disable + no-undef +*/ +const fs = require('fs'); +const { resolve } = require('path'); +const testServer = require('../helpers/test-server'); +const reloadConfig = require('../fixtures/reload-config/webpack.config'); +const runBrowser = require('../helpers/run-browser'); +const port = require('../ports-map').Progress; + +const cssFilePath = resolve(__dirname, '../fixtures/reload-config/main.css'); + +describe('client progress', () => { + describe('using hot', () => { + beforeAll((done) => { + fs.writeFileSync( + cssFilePath, + 'body { background-color: rgb(0, 0, 255); }' + ); + const options = { + port, + host: '0.0.0.0', + inline: true, + hot: true, + progress: true, + watchOptions: { + poll: 500, + }, + }; + testServer.startAwaitingCompilation(reloadConfig, options, done); + }); + + afterAll((done) => { + fs.unlinkSync(cssFilePath); + testServer.close(done); + }); + + describe('on browser client', () => { + it('should console.log progress', (done) => { + runBrowser().then(({ page, browser }) => { + const res = []; + page.waitForNavigation({ waitUntil: 'load' }).then(() => { + fs.writeFileSync( + cssFilePath, + 'body { background-color: rgb(255, 0, 0); }' + ); + page.waitFor(10000).then(() => { + browser.close().then(() => { + // check that there is some percentage progress output + const regExp = /^\[WDS\] [0-9]{1,3}% - /; + const match = res.find((line) => { + return regExp.test(line); + }); + // eslint-disable-next-line no-undefined + expect(match).not.toEqual(undefined); + done(); + }); + }); + }); + + page.goto(`http://localhost:${port}/main`); + page.on('console', (data) => { + res.push(data.text()); + }); + }); + }); + }); + }); +}); diff --git a/test/options.test.js b/test/options.test.js index 3ba18e17f7..a515ea28dc 100644 --- a/test/options.test.js +++ b/test/options.test.js @@ -309,6 +309,10 @@ describe('options', () => { success: ['', 0, null], failure: [false], }, + profile: { + success: [false], + failure: [''], + }, progress: { success: [false], failure: [''], diff --git a/test/ports-map.js b/test/ports-map.js index 6c56f8100c..551a9f514f 100644 --- a/test/ports-map.js +++ b/test/ports-map.js @@ -40,6 +40,9 @@ const portsList = { WebsocketServer: 1, ClientMode: 1, 'clientMode-option': 1, + Progress: 1, + 'progress-option': 1, + 'profile-option': 1, }; let startPort = 8079; diff --git a/test/server/profile-option.test.js b/test/server/profile-option.test.js new file mode 100644 index 0000000000..025e502d4f --- /dev/null +++ b/test/server/profile-option.test.js @@ -0,0 +1,53 @@ +'use strict'; + +const webpack = require('webpack'); +const Server = require('../../lib/Server'); +const config = require('../fixtures/simple-config/webpack.config'); +const port = require('../ports-map')['profile-option']; + +describe('profile', () => { + describe('output', () => { + let mockStderr; + + beforeAll(() => { + mockStderr = jest + .spyOn(process.stderr, 'write') + .mockImplementation(() => {}); + }); + + it('should show percentage progress with profile data', (done) => { + const compiler = webpack(config); + const server = new Server(compiler, { + port, + // profile will only have an effect when progress is enabled + progress: true, + profile: true, + }); + + compiler.hooks.done.tap('webpack-dev-server', () => { + const calls = mockStderr.mock.calls; + mockStderr.mockRestore(); + let foundProgress = false; + let foundProfile = false; + calls.forEach((call) => { + if (call[0].includes('0% compiling')) { + foundProgress = true; + } + + // this is an indicator that the profile option is enabled, + // so we should expect to find it in stderr since profile is enabled + if (call[0].includes('ms after chunk modules optimization')) { + foundProfile = true; + } + }); + expect(foundProgress).toBeTruthy(); + expect(foundProfile).toBeTruthy(); + + server.close(done); + }); + + compiler.run(() => {}); + server.listen(port, 'localhost'); + }); + }); +}); diff --git a/test/server/progress-option.test.js b/test/server/progress-option.test.js new file mode 100644 index 0000000000..ae2d23c07f --- /dev/null +++ b/test/server/progress-option.test.js @@ -0,0 +1,51 @@ +'use strict'; + +const webpack = require('webpack'); +const Server = require('../../lib/Server'); +const config = require('../fixtures/simple-config/webpack.config'); +const port = require('../ports-map')['progress-option']; + +describe('progress', () => { + describe('output', () => { + let mockStderr; + + beforeAll(() => { + mockStderr = jest + .spyOn(process.stderr, 'write') + .mockImplementation(() => {}); + }); + + it('should show percentage progress without profile data', (done) => { + const compiler = webpack(config); + const server = new Server(compiler, { + port, + progress: true, + }); + + compiler.hooks.done.tap('webpack-dev-server', () => { + const calls = mockStderr.mock.calls; + mockStderr.mockRestore(); + let foundProgress = false; + let foundProfile = false; + calls.forEach((call) => { + if (call[0].includes('0% compiling')) { + foundProgress = true; + } + + // this is an indicator that the profile option is enabled, + // so we should expect to not find it in stderr since it is not enabled + if (call[0].includes('ms after chunk modules optimization')) { + foundProfile = true; + } + }); + expect(foundProgress).toBeTruthy(); + expect(foundProfile).toBeFalsy(); + + server.close(done); + }); + + compiler.run(() => {}); + server.listen(port, 'localhost'); + }); + }); +}); From 078ddca5c93a49ef02b68b098ca05134f2ee9016 Mon Sep 17 00:00:00 2001 From: Loonride Date: Wed, 3 Jul 2019 16:21:02 -0500 Subject: [PATCH 18/65] fix(server): stricter headers security check (#2092) * fix(server): stricter headers security check * fix(server): changed comments explaining how server impl works --- lib/Server.js | 9 +- lib/servers/SockJSServer.js | 2 +- lib/servers/WebsocketServer.js | 2 +- .../serverMode-option.test.js.snap | 8 ++ test/server/serverMode-option.test.js | 91 +++++++++++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) diff --git a/lib/Server.js b/lib/Server.js index 5538f6f454..5cdeccffaf 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -685,7 +685,14 @@ class Server { return; } - if (headers && (!this.checkHost(headers) || !this.checkOrigin(headers))) { + if (!headers) { + this.log.warn( + 'serverMode implementation must pass headers to the callback of onConnection(f) ' + + 'via f(connection, headers) in order for clients to pass a headers security check' + ); + } + + if (!headers || !this.checkHost(headers) || !this.checkOrigin(headers)) { this.sockWrite([connection], 'error', 'Invalid Host/Origin header'); this.socketServer.close(connection); diff --git a/lib/servers/SockJSServer.js b/lib/servers/SockJSServer.js index 49c42538d2..464bc3cbd1 100644 --- a/lib/servers/SockJSServer.js +++ b/lib/servers/SockJSServer.js @@ -57,7 +57,7 @@ module.exports = class SockJSServer extends BaseServer { connection.close(); } - // f should return the resulting connection and, optionally, the connection headers + // f should be passed the resulting connection and the connection headers onConnection(f) { this.socket.on('connection', (connection) => { f(connection, connection.headers); diff --git a/lib/servers/WebsocketServer.js b/lib/servers/WebsocketServer.js index 03c5a56e46..2e237d2856 100644 --- a/lib/servers/WebsocketServer.js +++ b/lib/servers/WebsocketServer.js @@ -27,7 +27,7 @@ module.exports = class WebsocketServer extends BaseServer { connection.close(); } - // f should return the resulting connection + // f should be passed the resulting connection and the connection headers onConnection(f) { this.wsServer.on('connection', (connection, req) => { f(connection, req.headers); diff --git a/test/server/__snapshots__/serverMode-option.test.js.snap b/test/server/__snapshots__/serverMode-option.test.js.snap index a7ad43ca4e..ef6ab793a5 100644 --- a/test/server/__snapshots__/serverMode-option.test.js.snap +++ b/test/server/__snapshots__/serverMode-option.test.js.snap @@ -7,3 +7,11 @@ Array [ "close", ] `; + +exports[`serverMode option without a header results in an error 1`] = ` +Array [ + "open", + "{\\"type\\":\\"error\\",\\"data\\":\\"Invalid Host/Origin header\\"}", + "close", +] +`; diff --git a/test/server/serverMode-option.test.js b/test/server/serverMode-option.test.js index 8a660ddab6..6f64331b2b 100644 --- a/test/server/serverMode-option.test.js +++ b/test/server/serverMode-option.test.js @@ -223,4 +223,95 @@ describe('serverMode option', () => { }, 5000); }); }); + + describe('without a header', () => { + let mockWarn; + beforeAll((done) => { + server = testServer.start( + config, + { + port, + serverMode: class MySockJSServer extends BaseServer { + constructor(serv) { + super(serv); + this.socket = sockjs.createServer({ + // Use provided up-to-date sockjs-client + sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', + // Limit useless logs + log: (severity, line) => { + if (severity === 'error') { + this.server.log.error(line); + } else { + this.server.log.debug(line); + } + }, + }); + + this.socket.installHandlers(this.server.listeningApp, { + prefix: this.server.sockPath, + }); + } + + send(connection, message) { + connection.write(message); + } + + close(connection) { + connection.close(); + } + + onConnection(f) { + this.socket.on('connection', (connection) => { + f(connection); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); + } + }, + }, + done + ); + + mockWarn = jest.spyOn(server.log, 'warn').mockImplementation(() => {}); + }); + + it('results in an error', (done) => { + const data = []; + const client = new SockJS(`http://localhost:${port}/sockjs-node`); + + client.onopen = () => { + data.push('open'); + }; + + client.onmessage = (e) => { + data.push(e.data); + }; + + client.onclose = () => { + data.push('close'); + }; + + setTimeout(() => { + expect(data).toMatchSnapshot(); + const calls = mockWarn.mock.calls; + mockWarn.mockRestore(); + + let foundWarning = false; + const regExp = /serverMode implementation must pass headers to the callback of onConnection\(f\)/; + calls.every((call) => { + if (regExp.test(call)) { + foundWarning = true; + return false; + } + return true; + }); + + expect(foundWarning).toBeTruthy(); + + done(); + }, 5000); + }); + }); }); From 9a094202a2918ae025c207a92ddf71ceb179ff44 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2019 22:22:47 +0100 Subject: [PATCH 19/65] chore(deps): update dependency semver to ^6.2.0 (master) (#2088) --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2cf48721ae..0aa282f07d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10350,9 +10350,9 @@ } }, "semver": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.3.tgz", - "integrity": "sha512-aymF+56WJJMyXQHcd4hlK4N75rwj5RQpfW8ePlQnJsTYOBLlLbcIErR/G1s9SkIvKBqOudR3KAx4wEqP+F1hNQ==" + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" }, "semver-compare": { "version": "1.0.0", diff --git a/package.json b/package.json index e81df075d3..c32e1cfdb6 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "portfinder": "^1.0.20", "schema-utils": "^1.0.0", "selfsigned": "^1.10.4", - "semver": "^6.1.3", + "semver": "^6.2.0", "serve-index": "^1.9.1", "sockjs": "0.3.19", "sockjs-client": "1.3.0", From aa31d87a0d46ae18162afa5d755e3a27304e6c63 Mon Sep 17 00:00:00 2001 From: Loonride Date: Thu, 4 Jul 2019 13:13:11 -0500 Subject: [PATCH 20/65] test(client): socket helper tests (#2095) * test(client): socket helper tests * test(client): rename socket test, rename mock variable * test(client): renamed socket helper test filename --- .../__snapshots__/socket-helper.test.js.snap | 77 +++++++++++++++++++ test/client/utils/socket-helper.test.js | 71 +++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 test/client/utils/__snapshots__/socket-helper.test.js.snap create mode 100644 test/client/utils/socket-helper.test.js diff --git a/test/client/utils/__snapshots__/socket-helper.test.js.snap b/test/client/utils/__snapshots__/socket-helper.test.js.snap new file mode 100644 index 0000000000..e971ef199f --- /dev/null +++ b/test/client/utils/__snapshots__/socket-helper.test.js.snap @@ -0,0 +1,77 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`socket should default to SockJSClient when no __webpack_dev_server_client__ set 1`] = ` +Array [ + "my.url", +] +`; + +exports[`socket should default to SockJSClient when no __webpack_dev_server_client__ set 2`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`socket should default to SockJSClient when no __webpack_dev_server_client__ set 3`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`socket should default to SockJSClient when no __webpack_dev_server_client__ set 4`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`socket should default to SockJSClient when no __webpack_dev_server_client__ set 5`] = ` +Array [ + Array [ + "hello world", + ], +] +`; + +exports[`socket should use __webpack_dev_server_client__ when set 1`] = ` +Array [ + "my.url", +] +`; + +exports[`socket should use __webpack_dev_server_client__ when set 2`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`socket should use __webpack_dev_server_client__ when set 3`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`socket should use __webpack_dev_server_client__ when set 4`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`socket should use __webpack_dev_server_client__ when set 5`] = ` +Array [ + Array [ + "hello world", + ], +] +`; diff --git a/test/client/utils/socket-helper.test.js b/test/client/utils/socket-helper.test.js new file mode 100644 index 0000000000..49da4a3dd5 --- /dev/null +++ b/test/client/utils/socket-helper.test.js @@ -0,0 +1,71 @@ +'use strict'; + +describe('socket', () => { + afterEach(() => { + jest.resetAllMocks(); + jest.resetModules(); + }); + + it('should default to SockJSClient when no __webpack_dev_server_client__ set', () => { + jest.mock('../../../client/clients/SockJSClient'); + // eslint-disable-next-line global-require + const socket = require('../../../client/socket'); + // eslint-disable-next-line global-require + const SockJSClient = require('../../../client/clients/SockJSClient'); + + const mockHandler = jest.fn(); + socket('my.url', { + example: mockHandler, + }); + + const mockClientInstance = SockJSClient.mock.instances[0]; + + // this simulates recieving a message from the server and passing it + // along to the callback of onMessage + mockClientInstance.onMessage.mock.calls[0][0]( + JSON.stringify({ + type: 'example', + data: 'hello world', + }) + ); + + expect(SockJSClient.mock.calls[0]).toMatchSnapshot(); + expect(mockClientInstance.onOpen.mock.calls).toMatchSnapshot(); + expect(mockClientInstance.onClose.mock.calls).toMatchSnapshot(); + expect(mockClientInstance.onMessage.mock.calls).toMatchSnapshot(); + expect(mockHandler.mock.calls).toMatchSnapshot(); + }); + + it('should use __webpack_dev_server_client__ when set', () => { + jest.mock('../../../client/clients/SockJSClient'); + // eslint-disable-next-line global-require + const socket = require('../../../client/socket'); + // eslint-disable-next-line global-require + global.__webpack_dev_server_client__ = require('../../../client/clients/SockJSClient'); + + const mockHandler = jest.fn(); + socket('my.url', { + example: mockHandler, + }); + + const mockClientInstance = + global.__webpack_dev_server_client__.mock.instances[0]; + + // this simulates recieving a message from the server and passing it + // along to the callback of onMessage + mockClientInstance.onMessage.mock.calls[0][0]( + JSON.stringify({ + type: 'example', + data: 'hello world', + }) + ); + + expect( + global.__webpack_dev_server_client__.mock.calls[0] + ).toMatchSnapshot(); + expect(mockClientInstance.onOpen.mock.calls).toMatchSnapshot(); + expect(mockClientInstance.onClose.mock.calls).toMatchSnapshot(); + expect(mockClientInstance.onMessage.mock.calls).toMatchSnapshot(); + expect(mockHandler.mock.calls).toMatchSnapshot(); + }); +}); From 04483f4ab7192a5d8656894ca8edc5ec66b43d7b Mon Sep 17 00:00:00 2001 From: Loonride Date: Fri, 5 Jul 2019 00:12:28 -0500 Subject: [PATCH 21/65] feat(server): serverMode 'ws' option (#2082) * feat(server): server mode ws string option * test(server): rearrange bad host test * test(server): added mock server implementation tests * test(server): remove bad host test temporarily * test(server): re added bad host test --- lib/utils/getSocketServerImplementation.js | 5 +- .../serverMode-option.test.js.snap | 78 ++- test/server/serverMode-option.test.js | 647 +++++++++++------- .../getSocketServerImplementation.test.js | 43 +- 4 files changed, 528 insertions(+), 245 deletions(-) diff --git a/lib/utils/getSocketServerImplementation.js b/lib/utils/getSocketServerImplementation.js index 6df634085f..c60376a9ba 100644 --- a/lib/utils/getSocketServerImplementation.js +++ b/lib/utils/getSocketServerImplementation.js @@ -9,6 +9,9 @@ function getSocketServerImplementation(options) { if (options.serverMode === 'sockjs') { // eslint-disable-next-line global-require ServerImplementation = require('../servers/SockJSServer'); + } else if (options.serverMode === 'ws') { + // eslint-disable-next-line global-require + ServerImplementation = require('../servers/WebsocketServer'); } else { try { // eslint-disable-next-line global-require, import/no-dynamic-require @@ -29,7 +32,7 @@ function getSocketServerImplementation(options) { if (!serverImplFound) { throw new Error( - "serverMode must be a string denoting a default implementation (e.g. 'sockjs'), a full path to " + + "serverMode must be a string denoting a default implementation (e.g. 'sockjs', 'ws'), a full path to " + 'a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer) ' + 'via require.resolve(...), or the class itself which extends BaseServer' ); diff --git a/test/server/__snapshots__/serverMode-option.test.js.snap b/test/server/__snapshots__/serverMode-option.test.js.snap index ef6ab793a5..01d175c0cc 100644 --- a/test/server/__snapshots__/serverMode-option.test.js.snap +++ b/test/server/__snapshots__/serverMode-option.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`serverMode option with a bad host header results in an error 1`] = ` +exports[`serverMode option passed to server with a bad host header results in an error 1`] = ` Array [ "open", "{\\"type\\":\\"error\\",\\"data\\":\\"Invalid Host/Origin header\\"}", @@ -8,10 +8,84 @@ Array [ ] `; -exports[`serverMode option without a header results in an error 1`] = ` +exports[`serverMode option passed to server without a header results in an error 1`] = ` Array [ "open", "{\\"type\\":\\"error\\",\\"data\\":\\"Invalid Host/Origin header\\"}", "close", ] `; + +exports[`serverMode option server should close client with bad headers 1`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`serverMode option server should close client with bad headers 2`] = ` +Array [ + Array [ + Object { + "foo": "bar", + }, + "{\\"type\\":\\"error\\",\\"data\\":\\"Invalid Host/Origin header\\"}", + ], +] +`; + +exports[`serverMode option server should close client with bad headers 3`] = ` +Array [ + Array [ + Object { + "foo": "bar", + }, + ], +] +`; + +exports[`serverMode option server should use server implementation correctly 1`] = ` +Array [ + Object { + "foo": "bar", + }, +] +`; + +exports[`serverMode option server should use server implementation correctly 2`] = ` +Array [ + Array [ + [Function], + ], +] +`; + +exports[`serverMode option server should use server implementation correctly 3`] = ` +Array [ + Object { + "foo": "bar", + }, + "{\\"type\\":\\"liveReload\\"}", +] +`; + +exports[`serverMode option server should use server implementation correctly 4`] = ` +Array [ + Object { + "foo": "bar", + }, + "{\\"type\\":\\"ok\\"}", +] +`; + +exports[`serverMode option server should use server implementation correctly 5`] = ` +Array [ + Array [ + Object { + "foo": "bar", + }, + [Function], + ], +] +`; diff --git a/test/server/serverMode-option.test.js b/test/server/serverMode-option.test.js index 6f64331b2b..ba4e924aee 100644 --- a/test/server/serverMode-option.test.js +++ b/test/server/serverMode-option.test.js @@ -8,310 +8,479 @@ const sockjs = require('sockjs'); const SockJS = require('sockjs-client/dist/sockjs'); const SockJSServer = require('../../lib/servers/SockJSServer'); const config = require('../fixtures/simple-config/webpack.config'); -const testServer = require('../helpers/test-server'); const BaseServer = require('../../lib/servers/BaseServer'); const port = require('../ports-map')['serverMode-option']; describe('serverMode option', () => { + let mockedTestServer; + let testServer; let server; let req; + let getSocketServerImplementation; + + const serverModes = [ + { + title: 'as a string ("sockjs")', + serverMode: 'sockjs', + }, + { + title: 'as a path ("sockjs")', + serverMode: require.resolve('../../lib/servers/SockJSServer'), + }, + { + title: 'as a string ("ws")', + serverMode: 'ws', + }, + { + title: 'as a path ("ws")', + serverMode: require.resolve('../../lib/servers/WebsocketServer'), + }, + { + title: 'as a class (custom implementation)', + serverMode: class CustomServer {}, + }, + { + title: 'as a nonexistent path', + serverMode: '/bad/path/to/implementation', + }, + ]; + + describe('is passed to getSocketServerImplementation correctly', () => { + beforeEach(() => { + jest.mock('../../lib/utils/getSocketServerImplementation'); + // eslint-disable-next-line global-require + getSocketServerImplementation = require('../../lib/utils/getSocketServerImplementation'); + getSocketServerImplementation.mockImplementation(() => { + return class MockServer { + // eslint-disable-next-line no-empty-function + onConnection() {} + }; + }); + }); - afterEach((done) => { - testServer.close(done); - req = null; - server = null; - }); + afterEach((done) => { + jest.resetAllMocks(); + jest.resetModules(); - describe('as a string ("sockjs")', () => { - beforeEach((done) => { - server = testServer.start( - config, - { - serverMode: 'sockjs', - port, - }, - done - ); - req = request(`http://localhost:${port}`); + mockedTestServer.close(done); }); - it('sockjs path responds with a 200', (done) => { - req.get('/sockjs-node').expect(200, done); + serverModes.forEach((data) => { + it(data.title, (done) => { + // eslint-disable-next-line global-require + mockedTestServer = require('../helpers/test-server'); + mockedTestServer.start( + config, + { + serverMode: data.serverMode, + port, + }, + () => { + expect(getSocketServerImplementation.mock.calls.length).toEqual(1); + expect(getSocketServerImplementation.mock.calls[0].length).toEqual( + 1 + ); + expect( + getSocketServerImplementation.mock.calls[0][0].serverMode + ).toEqual(data.serverMode); + done(); + } + ); + }); }); }); - describe('as a path ("sockjs")', () => { - beforeEach((done) => { - server = testServer.start( - config, - { - serverMode: require.resolve('../../lib/servers/SockJSServer'), - port, - }, - done - ); - req = request(`http://localhost:${port}`); + describe('passed to server', () => { + beforeAll(() => { + jest.unmock('../../lib/utils/getSocketServerImplementation'); + // eslint-disable-next-line global-require + testServer = require('../helpers/test-server'); }); - it('sockjs path responds with a 200', (done) => { - req.get('/sockjs-node').expect(200, done); + afterEach((done) => { + testServer.close(done); + req = null; + server = null; }); - }); - describe('as a class ("sockjs")', () => { - beforeEach((done) => { - server = testServer.start( - config, - { - serverMode: SockJSServer, - port, - }, - done - ); - req = request(`http://localhost:${port}`); + describe('as a string ("sockjs")', () => { + beforeEach((done) => { + server = testServer.start( + config, + { + serverMode: 'sockjs', + port, + }, + done + ); + req = request(`http://localhost:${port}`); + }); + + it('sockjs path responds with a 200', (done) => { + req.get('/sockjs-node').expect(200, done); + }); }); - it('sockjs path responds with a 200', (done) => { - req.get('/sockjs-node').expect(200, done); + describe('as a path ("sockjs")', () => { + beforeEach((done) => { + server = testServer.start( + config, + { + serverMode: require.resolve('../../lib/servers/SockJSServer'), + port, + }, + done + ); + req = request(`http://localhost:${port}`); + }); + + it('sockjs path responds with a 200', (done) => { + req.get('/sockjs-node').expect(200, done); + }); }); - }); - describe('as a class (custom "sockjs" implementation)', () => { - it('uses supplied server implementation', (done) => { + describe('as a class ("sockjs")', () => { + beforeEach((done) => { + server = testServer.start( + config, + { + serverMode: SockJSServer, + port, + }, + done + ); + req = request(`http://localhost:${port}`); + }); + + it('sockjs path responds with a 200', (done) => { + req.get('/sockjs-node').expect(200, done); + }); + }); + + describe('as a class (custom "sockjs" implementation)', () => { let sockPath; - server = testServer.start( - config, - { - port, - sockPath: '/foo/test/bar/', - serverMode: class MySockJSServer extends BaseServer { - constructor(serv) { - super(serv); - this.socket = sockjs.createServer({ - // Use provided up-to-date sockjs-client - sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', - // Limit useless logs - log: (severity, line) => { - if (severity === 'error') { - this.server.log.error(line); - } else { - this.server.log.debug(line); - } - }, - }); - - this.socket.installHandlers(this.server.listeningApp, { - prefix: this.server.sockPath, - }); - - sockPath = server.options.sockPath; - } + it('uses supplied server implementation', (done) => { + server = testServer.start( + config, + { + port, + sockPath: '/foo/test/bar/', + serverMode: class MySockJSServer extends BaseServer { + constructor(serv) { + super(serv); + this.socket = sockjs.createServer({ + // Use provided up-to-date sockjs-client + sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', + // Limit useless logs + log: (severity, line) => { + if (severity === 'error') { + this.server.log.error(line); + } else { + this.server.log.debug(line); + } + }, + }); - send(connection, message) { - connection.write(message); - } + this.socket.installHandlers(this.server.listeningApp, { + prefix: this.server.sockPath, + }); - close(connection) { - connection.close(); - } + sockPath = server.options.sockPath; + } - onConnection(f) { - this.socket.on('connection', (connection) => { - f(connection, connection.headers); - }); - } + send(connection, message) { + connection.write(message); + } - onConnectionClose(connection, f) { - connection.on('close', f); - } + close(connection) { + connection.close(); + } + + onConnection(f) { + this.socket.on('connection', (connection) => { + f(connection, connection.headers); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); + } + }, }, - }, - () => { - expect(sockPath).toEqual('/foo/test/bar/'); - done(); - } - ); + () => { + expect(sockPath).toEqual('/foo/test/bar/'); + done(); + } + ); + }); + }); + + describe('as a path with nonexistent path', () => { + it('should throw an error', () => { + expect(() => { + server = testServer.start( + config, + { + serverMode: '/bad/path/to/implementation', + port, + }, + () => {} + ); + }).toThrow(/serverMode must be a string/); + }); }); - }); - describe('as a path with nonexistent path', () => { - it('should throw an error', () => { - expect(() => { + describe('without a header', () => { + let mockWarn; + beforeAll((done) => { server = testServer.start( config, { - serverMode: '/bad/path/to/implementation', port, + serverMode: class MySockJSServer extends BaseServer { + constructor(serv) { + super(serv); + this.socket = sockjs.createServer({ + // Use provided up-to-date sockjs-client + sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', + // Limit useless logs + log: (severity, line) => { + if (severity === 'error') { + this.server.log.error(line); + } else { + this.server.log.debug(line); + } + }, + }); + + this.socket.installHandlers(this.server.listeningApp, { + prefix: this.server.sockPath, + }); + } + + send(connection, message) { + connection.write(message); + } + + close(connection) { + connection.close(); + } + + onConnection(f) { + this.socket.on('connection', (connection) => { + f(connection); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); + } + }, }, - () => {} + done ); - }).toThrow(/serverMode must be a string/); - }); - }); - describe('with a bad host header', () => { - beforeAll((done) => { - server = testServer.start( - config, - { - port, - serverMode: class MySockJSServer extends BaseServer { - constructor(serv) { - super(serv); - this.socket = sockjs.createServer({ - // Use provided up-to-date sockjs-client - sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', - // Limit useless logs - log: (severity, line) => { - if (severity === 'error') { - this.server.log.error(line); - } else { - this.server.log.debug(line); - } - }, - }); - - this.socket.installHandlers(this.server.listeningApp, { - prefix: this.server.sockPath, - }); - } + mockWarn = jest.spyOn(server.log, 'warn').mockImplementation(() => {}); + }); - send(connection, message) { - connection.write(message); - } + it('results in an error', (done) => { + const data = []; + const client = new SockJS(`http://localhost:${port}/sockjs-node`); + + client.onopen = () => { + data.push('open'); + }; + + client.onmessage = (e) => { + data.push(e.data); + }; + + client.onclose = () => { + data.push('close'); + }; - close(connection) { - connection.close(); + setTimeout(() => { + expect(data).toMatchSnapshot(); + const calls = mockWarn.mock.calls; + mockWarn.mockRestore(); + + let foundWarning = false; + const regExp = /serverMode implementation must pass headers to the callback of onConnection\(f\)/; + calls.every((call) => { + if (regExp.test(call)) { + foundWarning = true; + return false; } + return true; + }); + + expect(foundWarning).toBeTruthy(); + + done(); + }, 5000); + }); + }); - onConnection(f) { - this.socket.on('connection', (connection) => { - f(connection, { - host: null, + describe('with a bad host header', () => { + beforeAll((done) => { + server = testServer.start( + config, + { + port, + serverMode: class MySockJSServer extends BaseServer { + constructor(serv) { + super(serv); + this.socket = sockjs.createServer({ + // Use provided up-to-date sockjs-client + sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', + // Limit useless logs + log: (severity, line) => { + if (severity === 'error') { + this.server.log.error(line); + } else { + this.server.log.debug(line); + } + }, }); - }); - } - onConnectionClose(connection, f) { - connection.on('close', f); - } + this.socket.installHandlers(this.server.listeningApp, { + prefix: this.server.sockPath, + }); + } + + send(connection, message) { + connection.write(message); + } + + close(connection) { + connection.close(); + } + + onConnection(f) { + this.socket.on('connection', (connection) => { + f(connection, { + host: null, + }); + }); + } + + onConnectionClose(connection, f) { + connection.on('close', f); + } + }, }, - }, - done - ); - }); + done + ); + }); - it('results in an error', (done) => { - const data = []; - const client = new SockJS(`http://localhost:${port}/sockjs-node`); + it('results in an error', (done) => { + const data = []; + const client = new SockJS(`http://localhost:${port}/sockjs-node`); - client.onopen = () => { - data.push('open'); - }; + client.onopen = () => { + data.push('open'); + }; - client.onmessage = (e) => { - data.push(e.data); - }; + client.onmessage = (e) => { + data.push(e.data); + }; - client.onclose = () => { - data.push('close'); - }; + client.onclose = () => { + data.push('close'); + }; - setTimeout(() => { - expect(data).toMatchSnapshot(); - done(); - }, 5000); + setTimeout(() => { + expect(data).toMatchSnapshot(); + done(); + }, 5000); + }); }); }); - describe('without a header', () => { - let mockWarn; - beforeAll((done) => { - server = testServer.start( + describe('server', () => { + let MockSockJSServer; + beforeEach((done) => { + jest.mock('../../lib/servers/SockJSServer'); + // eslint-disable-next-line global-require + mockedTestServer = require('../helpers/test-server'); + // eslint-disable-next-line global-require + MockSockJSServer = require('../../lib/servers/SockJSServer'); + + server = mockedTestServer.start( config, { port, - serverMode: class MySockJSServer extends BaseServer { - constructor(serv) { - super(serv); - this.socket = sockjs.createServer({ - // Use provided up-to-date sockjs-client - sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js', - // Limit useless logs - log: (severity, line) => { - if (severity === 'error') { - this.server.log.error(line); - } else { - this.server.log.debug(line); - } - }, - }); - - this.socket.installHandlers(this.server.listeningApp, { - prefix: this.server.sockPath, - }); - } - - send(connection, message) { - connection.write(message); - } - - close(connection) { - connection.close(); - } - - onConnection(f) { - this.socket.on('connection', (connection) => { - f(connection); - }); - } - - onConnectionClose(connection, f) { - connection.on('close', f); - } - }, }, done ); - - mockWarn = jest.spyOn(server.log, 'warn').mockImplementation(() => {}); }); - it('results in an error', (done) => { - const data = []; - const client = new SockJS(`http://localhost:${port}/sockjs-node`); + afterEach((done) => { + mockedTestServer.close(done); + jest.resetAllMocks(); + jest.resetModules(); - client.onopen = () => { - data.push('open'); - }; + server = null; + }); - client.onmessage = (e) => { - data.push(e.data); - }; + it('should use server implementation correctly', () => { + const mockServerInstance = MockSockJSServer.mock.instances[0]; - client.onclose = () => { - data.push('close'); + const connectionObj = { + foo: 'bar', }; + // this simulates a client connecting to the server + mockServerInstance.onConnection.mock.calls[0][0](connectionObj, { + host: `localhost:${port}`, + origin: `http://localhost:${port}`, + }); + + expect(server.sockets.length).toEqual(1); + expect(server.sockets).toMatchSnapshot(); + + // this simulates a client leaving the server + mockServerInstance.onConnectionClose.mock.calls[0][1](connectionObj); + + expect(server.sockets.length).toEqual(0); + + // check that the dev server was passed to the socket server implementation constructor + expect(MockSockJSServer.mock.calls[0].length).toEqual(1); + expect(MockSockJSServer.mock.calls[0][0].options.port).toEqual(port); + + expect(mockServerInstance.onConnection.mock.calls).toMatchSnapshot(); + expect(mockServerInstance.send.mock.calls.length).toEqual(3); + // call 0 to the send() method is liveReload + expect(mockServerInstance.send.mock.calls[0]).toMatchSnapshot(); + // call 1 to the send() method is hash data, so we skip it + // call 2 to the send() method is the "ok" message + expect(mockServerInstance.send.mock.calls[2]).toMatchSnapshot(); + // close should not be called because the server never forcefully closes + // a successful client connection + expect(mockServerInstance.close.mock.calls.length).toEqual(0); + expect(mockServerInstance.onConnectionClose.mock.calls).toMatchSnapshot(); + }); - setTimeout(() => { - expect(data).toMatchSnapshot(); - const calls = mockWarn.mock.calls; - mockWarn.mockRestore(); - - let foundWarning = false; - const regExp = /serverMode implementation must pass headers to the callback of onConnection\(f\)/; - calls.every((call) => { - if (regExp.test(call)) { - foundWarning = true; - return false; - } - return true; - }); - - expect(foundWarning).toBeTruthy(); + it('should close client with bad headers', () => { + const mockServerInstance = MockSockJSServer.mock.instances[0]; - done(); - }, 5000); + // this simulates a client connecting to the server + mockServerInstance.onConnection.mock.calls[0][0]( + { + foo: 'bar', + }, + { + host: null, + } + ); + expect(server.sockets.length).toEqual(0); + expect(MockSockJSServer.mock.calls[0].length).toEqual(1); + expect(MockSockJSServer.mock.calls[0][0].options.port).toEqual(port); + expect(mockServerInstance.onConnection.mock.calls).toMatchSnapshot(); + // the only call to send() here should be an invalid header message + expect(mockServerInstance.send.mock.calls).toMatchSnapshot(); + expect(mockServerInstance.close.mock.calls).toMatchSnapshot(); + // onConnectionClose should never get called since the client should be closed first + expect(mockServerInstance.onConnectionClose.mock.calls.length).toEqual(0); }); }); }); diff --git a/test/server/utils/getSocketServerImplementation.test.js b/test/server/utils/getSocketServerImplementation.test.js index 0a157c39a9..1898b8addf 100644 --- a/test/server/utils/getSocketServerImplementation.test.js +++ b/test/server/utils/getSocketServerImplementation.test.js @@ -2,9 +2,10 @@ const getSocketServerImplementation = require('../../../lib/utils/getSocketServerImplementation'); const SockJSServer = require('../../../lib/servers/SockJSServer'); +const WebsocketServer = require('../../../lib/servers/WebsocketServer'); describe('getSocketServerImplementation util', () => { - it("should works with string serverMode ('sockjs')", () => { + it("should work with string serverMode ('sockjs')", () => { let result; expect(() => { @@ -16,7 +17,7 @@ describe('getSocketServerImplementation util', () => { expect(result).toEqual(SockJSServer); }); - it('should works with serverMode (SockJSServer class)', () => { + it('should work with serverMode (SockJSServer class)', () => { let result; expect(() => { @@ -40,7 +41,43 @@ describe('getSocketServerImplementation util', () => { expect(result).toEqual(SockJSServer); }); - it('should throws with serverMode (bad path)', () => { + it("should work with string serverMode ('ws')", () => { + let result; + + expect(() => { + result = getSocketServerImplementation({ + serverMode: 'ws', + }); + }).not.toThrow(); + + expect(result).toEqual(WebsocketServer); + }); + + it('should work with serverMode (WebsocketServer class)', () => { + let result; + + expect(() => { + result = getSocketServerImplementation({ + serverMode: WebsocketServer, + }); + }).not.toThrow(); + + expect(result).toEqual(WebsocketServer); + }); + + it('should work with serverMode (WebsocketServer full path)', () => { + let result; + + expect(() => { + result = getSocketServerImplementation({ + serverMode: require.resolve('../../../lib/servers/WebsocketServer'), + }); + }).not.toThrow(); + + expect(result).toEqual(WebsocketServer); + }); + + it('should throw with serverMode (bad path)', () => { expect(() => { getSocketServerImplementation({ serverMode: '/bad/path/to/implementation', From 99192c8261fca1169fe9a51990a0c6013946bbeb Mon Sep 17 00:00:00 2001 From: Loonride Date: Fri, 5 Jul 2019 05:39:31 -0500 Subject: [PATCH 22/65] refactor(client): move socket helper test out of util directory (#2101) --- .../__snapshots__/socket-helper.test.js.snap | 0 test/client/{utils => }/socket-helper.test.js | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) rename test/client/{utils => }/__snapshots__/socket-helper.test.js.snap (100%) rename test/client/{utils => }/socket-helper.test.js (84%) diff --git a/test/client/utils/__snapshots__/socket-helper.test.js.snap b/test/client/__snapshots__/socket-helper.test.js.snap similarity index 100% rename from test/client/utils/__snapshots__/socket-helper.test.js.snap rename to test/client/__snapshots__/socket-helper.test.js.snap diff --git a/test/client/utils/socket-helper.test.js b/test/client/socket-helper.test.js similarity index 84% rename from test/client/utils/socket-helper.test.js rename to test/client/socket-helper.test.js index 49da4a3dd5..73553ece60 100644 --- a/test/client/utils/socket-helper.test.js +++ b/test/client/socket-helper.test.js @@ -7,11 +7,11 @@ describe('socket', () => { }); it('should default to SockJSClient when no __webpack_dev_server_client__ set', () => { - jest.mock('../../../client/clients/SockJSClient'); + jest.mock('../../client/clients/SockJSClient'); // eslint-disable-next-line global-require - const socket = require('../../../client/socket'); + const socket = require('../../client/socket'); // eslint-disable-next-line global-require - const SockJSClient = require('../../../client/clients/SockJSClient'); + const SockJSClient = require('../../client/clients/SockJSClient'); const mockHandler = jest.fn(); socket('my.url', { @@ -37,11 +37,11 @@ describe('socket', () => { }); it('should use __webpack_dev_server_client__ when set', () => { - jest.mock('../../../client/clients/SockJSClient'); + jest.mock('../../client/clients/SockJSClient'); // eslint-disable-next-line global-require - const socket = require('../../../client/socket'); + const socket = require('../../client/socket'); // eslint-disable-next-line global-require - global.__webpack_dev_server_client__ = require('../../../client/clients/SockJSClient'); + global.__webpack_dev_server_client__ = require('../../client/clients/SockJSClient'); const mockHandler = jest.fn(); socket('my.url', { From 461b87683fa786a7faccd1a8dd9eb05cb1394d37 Mon Sep 17 00:00:00 2001 From: Loonride Date: Sat, 6 Jul 2019 07:44:04 -0500 Subject: [PATCH 23/65] test(server): add updateCompiler tests (#2102) --- test/fixtures/multi-compiler-2-config/foo.js | 3 + .../multi-compiler-2-config/webpack.config.js | 22 +++ .../__snapshots__/updateCompiler.test.js.snap | 47 +++++ test/server/utils/updateCompiler.test.js | 162 ++++++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 test/fixtures/multi-compiler-2-config/foo.js create mode 100644 test/fixtures/multi-compiler-2-config/webpack.config.js create mode 100644 test/server/utils/__snapshots__/updateCompiler.test.js.snap create mode 100644 test/server/utils/updateCompiler.test.js diff --git a/test/fixtures/multi-compiler-2-config/foo.js b/test/fixtures/multi-compiler-2-config/foo.js new file mode 100644 index 0000000000..eab26534f3 --- /dev/null +++ b/test/fixtures/multi-compiler-2-config/foo.js @@ -0,0 +1,3 @@ +'use strict'; + +console.log('Hey.'); diff --git a/test/fixtures/multi-compiler-2-config/webpack.config.js b/test/fixtures/multi-compiler-2-config/webpack.config.js new file mode 100644 index 0000000000..73fdb7e357 --- /dev/null +++ b/test/fixtures/multi-compiler-2-config/webpack.config.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = [ + { + mode: 'development', + context: __dirname, + entry: './foo.js', + output: { + path: '/', + }, + node: false, + }, + { + mode: 'development', + context: __dirname, + entry: './foo.js', + output: { + path: '/', + }, + node: false, + }, +]; diff --git a/test/server/utils/__snapshots__/updateCompiler.test.js.snap b/test/server/utils/__snapshots__/updateCompiler.test.js.snap new file mode 100644 index 0000000000..d922651e48 --- /dev/null +++ b/test/server/utils/__snapshots__/updateCompiler.test.js.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`updateCompiler multi compiler config, hot and inline should apply plugins 1`] = ` +Array [ + HotModuleReplacementPlugin { + "fullBuildTimeout": 200, + "multiStep": undefined, + "options": Object {}, + "requestTimeout": 10000, + }, +] +`; + +exports[`updateCompiler multi compiler config, hot and inline should apply plugins 2`] = ` +Array [ + HotModuleReplacementPlugin { + "fullBuildTimeout": 200, + "multiStep": undefined, + "options": Object {}, + "requestTimeout": 10000, + }, +] +`; + +exports[`updateCompiler simple config with HMR already, hot and inline should apply plugins 1`] = ` +Array [ + HotModuleReplacementPlugin { + "fullBuildTimeout": 200, + "multiStep": undefined, + "options": Object {}, + "requestTimeout": 10000, + }, +] +`; + +exports[`updateCompiler simple config, hot and inline should apply plugins 1`] = ` +Array [ + HotModuleReplacementPlugin { + "fullBuildTimeout": 200, + "multiStep": undefined, + "options": Object {}, + "requestTimeout": 10000, + }, +] +`; + +exports[`updateCompiler simple config, inline should apply plugins without HMR 1`] = `undefined`; diff --git a/test/server/utils/updateCompiler.test.js b/test/server/utils/updateCompiler.test.js new file mode 100644 index 0000000000..64bb5dfac2 --- /dev/null +++ b/test/server/utils/updateCompiler.test.js @@ -0,0 +1,162 @@ +'use strict'; + +const webpack = require('webpack'); +const updateCompiler = require('../../../lib/utils/updateCompiler'); + +describe('updateCompiler', () => { + describe('simple config, inline', () => { + let compiler; + beforeAll(() => { + // eslint-disable-next-line global-require + const webpackConfig = require('../../fixtures/simple-config/webpack.config'); + compiler = webpack(webpackConfig); + }); + + it('should apply plugins without HMR', () => { + const spy = jest.spyOn(compiler.hooks.entryOption, 'call'); + + updateCompiler(compiler, { + serverMode: 'sockjs', + clientMode: 'sockjs', + inline: true, + }); + + let tapsByHMR = 0; + let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { + if (tap.name === 'HotModuleReplacementPlugin') { + tapsByHMR += 1; + } else if (tap.name === 'ProvidePlugin') { + tapsByProvidePlugin += 1; + } + }); + + expect(spy).toHaveBeenCalledTimes(1); + expect(tapsByHMR).toEqual(0); + expect(tapsByProvidePlugin).toEqual(1); + // should be empty + expect(compiler.options.plugins).toMatchSnapshot(); + + spy.mockRestore(); + }); + }); + + describe('simple config, hot and inline', () => { + let compiler; + beforeAll(() => { + // eslint-disable-next-line global-require + const webpackConfig = require('../../fixtures/simple-config/webpack.config'); + compiler = webpack(webpackConfig); + }); + + it('should apply plugins', () => { + const spy = jest.spyOn(compiler.hooks.entryOption, 'call'); + + updateCompiler(compiler, { + serverMode: 'sockjs', + clientMode: 'sockjs', + inline: true, + hot: true, + }); + + let tapsByHMR = 0; + let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { + if (tap.name === 'HotModuleReplacementPlugin') { + tapsByHMR += 1; + } else if (tap.name === 'ProvidePlugin') { + tapsByProvidePlugin += 1; + } + }); + + expect(spy).toHaveBeenCalledTimes(1); + expect(tapsByHMR).toEqual(1); + expect(tapsByProvidePlugin).toEqual(1); + expect(compiler.options.plugins).toMatchSnapshot(); + + spy.mockRestore(); + }); + }); + + describe('simple config with HMR already, hot and inline', () => { + let compiler; + beforeAll(() => { + // eslint-disable-next-line global-require + const webpackConfig = require('../../fixtures/simple-config/webpack.config'); + webpackConfig.plugins = [new webpack.HotModuleReplacementPlugin()]; + compiler = webpack(webpackConfig); + }); + + it('should apply plugins', () => { + const spy = jest.spyOn(compiler.hooks.entryOption, 'call'); + + updateCompiler(compiler, { + serverMode: 'sockjs', + clientMode: 'sockjs', + inline: true, + hot: true, + }); + + let tapsByHMR = 0; + let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { + if (tap.name === 'HotModuleReplacementPlugin') { + tapsByHMR += 1; + } else if (tap.name === 'ProvidePlugin') { + tapsByProvidePlugin += 1; + } + }); + + expect(spy).toHaveBeenCalledTimes(1); + expect(tapsByHMR).toEqual(1); + expect(tapsByProvidePlugin).toEqual(1); + expect(compiler.options.plugins).toMatchSnapshot(); + + spy.mockRestore(); + }); + }); + + describe('multi compiler config, hot and inline', () => { + let multiCompiler; + beforeAll(() => { + // eslint-disable-next-line global-require + const webpackConfig = require('../../fixtures/multi-compiler-2-config/webpack.config'); + webpackConfig[1].plugins = [new webpack.HotModuleReplacementPlugin()]; + multiCompiler = webpack(webpackConfig); + }); + + it('should apply plugins', () => { + const spies = []; + multiCompiler.compilers.forEach((compiler) => { + spies.push(jest.spyOn(compiler.hooks.entryOption, 'call')); + }); + + updateCompiler(multiCompiler, { + serverMode: 'sockjs', + clientMode: 'sockjs', + inline: true, + hot: true, + }); + + spies.forEach((spy) => { + expect(spy).toHaveBeenCalledTimes(1); + spy.mockRestore(); + }); + + multiCompiler.compilers.forEach((compiler) => { + let tapsByHMR = 0; + let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { + if (tap.name === 'HotModuleReplacementPlugin') { + tapsByHMR += 1; + } else if (tap.name === 'ProvidePlugin') { + tapsByProvidePlugin += 1; + } + }); + expect(tapsByHMR).toEqual(1); + expect(tapsByProvidePlugin).toEqual(1); + expect(compiler.options.plugins).toMatchSnapshot(); + }); + }); + }); +}); From fa73768d64cb79934bc3cb7c49e964b35447eff6 Mon Sep 17 00:00:00 2001 From: Anix Date: Mon, 8 Jul 2019 20:45:21 +0530 Subject: [PATCH 24/65] docs: added browser option in issue template (#2100) --- .github/ISSUE_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 7799f16431..93075089c5 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -11,6 +11,7 @@ - NPM Version: - webpack Version: - webpack-dev-server Version: +- Browser: