diff --git a/package-lock.json b/package-lock.json index dc4cf39098a..9f37fedd474 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,9 @@ "dependencies": { "@netlify/build": "^9.9.7", "@netlify/config": "^4.1.3", - "@netlify/framework-info": "^2.3.0", + "@netlify/framework-info": "^3.1.3", "@netlify/plugin-edge-handlers": "^1.11.5", + "@netlify/plugins-list": "^2.4.0", "@netlify/traffic-mesh-agent": "^0.27.10", "@netlify/zip-it-and-ship-it": "^2.7.1", "@oclif/command": "^1.6.1", @@ -79,6 +80,7 @@ "netlify-redirect-parser": "^3.0.7", "netlify-redirector": "^0.2.1", "node-fetch": "^2.6.0", + "node-version-alias": "^1.0.1", "open": "^7.0.0", "ora": "^4.1.1", "p-filter": "^2.1.0", @@ -135,6 +137,7 @@ "standard-version": "^9.0.0", "strip-ansi": "^6.0.0", "temp-dir": "^2.0.0", + "toml": "^3.0.0", "tomlify-j0.4": "^3.0.0" }, "engines": { @@ -2583,9 +2586,9 @@ } }, "node_modules/@netlify/framework-info": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@netlify/framework-info/-/framework-info-2.3.0.tgz", - "integrity": "sha512-vqy9wbBRP8qWnkzA/OQsThr1+cfqapMrORJ4hWcrjhIPRmXIJtwB6OWuLIUalMeSGCwqZjYpKfudc4BLuxxvjw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@netlify/framework-info/-/framework-info-3.1.3.tgz", + "integrity": "sha512-yRrndnWZfugKzHEEdL3m9tRgA4hLS3dZM9RruvpvN5CMML52KvVbSH4xdwjNG7CbCfukrUv4uZ9EABOAoxNhTg==", "dependencies": { "ajv": "^7.0.0", "filter-obj": "^2.0.1", @@ -4533,7 +4536,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", - "dev": true, "dependencies": { "defer-to-connect": "^2.0.0" }, @@ -4545,7 +4547,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", - "dev": true, "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "*", @@ -4610,8 +4611,7 @@ "node_modules/@types/http-cache-semantics": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", - "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", - "dev": true + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" }, "node_modules/@types/http-proxy": { "version": "1.17.5", @@ -4653,7 +4653,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -4738,7 +4737,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -4898,6 +4896,146 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/all-node-versions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/all-node-versions/-/all-node-versions-8.0.0.tgz", + "integrity": "sha512-cF8ibgj23U7ai4qjSFzpeccwDXUlPFMzKe0Z6qf6gChR+9S0JMyzYz6oYz4n0nHi/FLH9BJIefsONsMH/WDM2w==", + "dependencies": { + "fetch-node-website": "^5.0.3", + "filter-obj": "^2.0.1", + "get-stream": "^5.1.0", + "global-cache-dir": "^2.0.0", + "jest-validate": "^25.3.0", + "path-exists": "^4.0.0", + "semver": "^7.3.2", + "write-file-atomic": "^3.0.3" + }, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/all-node-versions/node_modules/@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/all-node-versions/node_modules/@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/all-node-versions/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/all-node-versions/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/all-node-versions/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/all-node-versions/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/all-node-versions/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/all-node-versions/node_modules/global-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-cache-dir/-/global-cache-dir-2.0.0.tgz", + "integrity": "sha512-30pvU3e8muclEhc9tt+jRMaywOS3QfNdURflJ5Zv0bohjhcVQpBe5bwRHghGSJORLOKW81/n+3iJvHRHs+/S1Q==", + "dependencies": { + "cachedir": "^2.3.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/all-node-versions/node_modules/jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/all-node-versions/node_modules/jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/all-node-versions/node_modules/pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "engines": { + "node": ">= 8.3" + } + }, "node_modules/ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -6265,7 +6403,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", - "dev": true, "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -8808,7 +8945,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, "engines": { "node": ">=10" } @@ -11224,6 +11360,243 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" }, + "node_modules/fetch-node-website": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/fetch-node-website/-/fetch-node-website-5.0.3.tgz", + "integrity": "sha512-O86T46FUWSOq4AWON39oaT8H90QFKAbmjfOVBhgaS87AFfeW00txz73KTv7QopPWtHBbGdI1S8cIT1VK1OQYLg==", + "dependencies": { + "chalk": "^4.0.0", + "cli-progress": "^3.7.0", + "figures": "^3.2.0", + "filter-obj": "^2.0.1", + "got": "^10.7.0", + "jest-validate": "^25.3.0" + }, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/fetch-node-website/node_modules/@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/fetch-node-website/node_modules/@jest/types/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fetch-node-website/node_modules/@sindresorhus/is": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", + "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/fetch-node-website/node_modules/@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/fetch-node-website/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/fetch-node-website/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fetch-node-website/node_modules/cacheable-lookup": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz", + "integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==", + "dependencies": { + "@types/keyv": "^3.1.1", + "keyv": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fetch-node-website/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fetch-node-website/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/fetch-node-website/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/fetch-node-website/node_modules/decompress-response": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz", + "integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fetch-node-website/node_modules/got": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz", + "integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==", + "dependencies": { + "@sindresorhus/is": "^2.0.0", + "@szmarczak/http-timer": "^4.0.0", + "@types/cacheable-request": "^6.0.1", + "cacheable-lookup": "^2.0.0", + "cacheable-request": "^7.0.1", + "decompress-response": "^5.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^5.0.0", + "lowercase-keys": "^2.0.0", + "mimic-response": "^2.1.0", + "p-cancelable": "^2.0.0", + "p-event": "^4.0.0", + "responselike": "^2.0.0", + "to-readable-stream": "^2.0.0", + "type-fest": "^0.10.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/fetch-node-website/node_modules/jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/fetch-node-website/node_modules/jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/fetch-node-website/node_modules/jest-validate/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fetch-node-website/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fetch-node-website/node_modules/pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/fetch-node-website/node_modules/type-fest": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz", + "integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -14171,8 +14544,7 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -14356,7 +14728,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", - "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -16101,11 +16472,261 @@ "node": ">=6.0" } }, + "node_modules/node-version-alias": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/node-version-alias/-/node-version-alias-1.0.1.tgz", + "integrity": "sha512-E9EhoJkpIIZyYplB298W8ZfhcojQrnKnUPcaOgJqVqICUZwPZkuj10nTzEscwdziOOj545v4tGPvNBG3ieUbSw==", + "dependencies": { + "all-node-versions": "^8.0.0", + "filter-obj": "^2.0.1", + "jest-validate": "^25.3.0", + "normalize-node-version": "^10.0.0", + "path-exists": "^4.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/node-version-alias/node_modules/@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/node-version-alias/node_modules/@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/node-version-alias/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/node-version-alias/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/node-version-alias/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-version-alias/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/node-version-alias/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/node-version-alias/node_modules/jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/node-version-alias/node_modules/jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/node-version-alias/node_modules/pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "engines": { + "node": ">= 8.3" + } + }, "node_modules/noop2": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/noop2/-/noop2-2.0.0.tgz", "integrity": "sha1-S2NgFemIK1R4PAK0EvaZ2MXNCls=" }, + "node_modules/normalize-node-version": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/normalize-node-version/-/normalize-node-version-10.0.0.tgz", + "integrity": "sha512-/gVbS/qAnowVxr2fJy3F0MxmCvx8QdXJDl8XUE7HT3vsDeDjQfZkX9OiPahF+51Hgy93cKG1hP6uyBjQsMCvWQ==", + "dependencies": { + "all-node-versions": "^8.0.0", + "filter-obj": "^2.0.1", + "jest-validate": "^25.3.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/normalize-node-version/node_modules/@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/normalize-node-version/node_modules/@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/normalize-node-version/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/normalize-node-version/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/normalize-node-version/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/normalize-node-version/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/normalize-node-version/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/normalize-node-version/node_modules/jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/normalize-node-version/node_modules/jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/normalize-node-version/node_modules/pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dependencies": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "engines": { + "node": ">= 8.3" + } + }, "node_modules/normalize-package-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.1.tgz", @@ -16902,7 +17523,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==", - "dev": true, "engines": { "node": ">=8" } @@ -18860,7 +19480,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "dev": true, "dependencies": { "lowercase-keys": "^2.0.0" } @@ -24209,9 +24828,9 @@ } }, "@netlify/framework-info": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@netlify/framework-info/-/framework-info-2.3.0.tgz", - "integrity": "sha512-vqy9wbBRP8qWnkzA/OQsThr1+cfqapMrORJ4hWcrjhIPRmXIJtwB6OWuLIUalMeSGCwqZjYpKfudc4BLuxxvjw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@netlify/framework-info/-/framework-info-3.1.3.tgz", + "integrity": "sha512-yRrndnWZfugKzHEEdL3m9tRgA4hLS3dZM9RruvpvN5CMML52KvVbSH4xdwjNG7CbCfukrUv4uZ9EABOAoxNhTg==", "requires": { "ajv": "^7.0.0", "filter-obj": "^2.0.1", @@ -25766,7 +26385,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", - "dev": true, "requires": { "defer-to-connect": "^2.0.0" } @@ -25775,7 +26393,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", - "dev": true, "requires": { "@types/http-cache-semantics": "*", "@types/keyv": "*", @@ -25840,8 +26457,7 @@ "@types/http-cache-semantics": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", - "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", - "dev": true + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" }, "@types/http-proxy": { "version": "1.17.5", @@ -25883,7 +26499,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", - "dev": true, "requires": { "@types/node": "*" } @@ -25967,7 +26582,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, "requires": { "@types/node": "*" } @@ -26091,6 +26705,115 @@ "uri-js": "^4.2.2" } }, + "all-node-versions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/all-node-versions/-/all-node-versions-8.0.0.tgz", + "integrity": "sha512-cF8ibgj23U7ai4qjSFzpeccwDXUlPFMzKe0Z6qf6gChR+9S0JMyzYz6oYz4n0nHi/FLH9BJIefsONsMH/WDM2w==", + "requires": { + "fetch-node-website": "^5.0.3", + "filter-obj": "^2.0.1", + "get-stream": "^5.1.0", + "global-cache-dir": "^2.0.0", + "jest-validate": "^25.3.0", + "path-exists": "^4.0.0", + "semver": "^7.3.2", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "global-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-cache-dir/-/global-cache-dir-2.0.0.tgz", + "integrity": "sha512-30pvU3e8muclEhc9tt+jRMaywOS3QfNdURflJ5Zv0bohjhcVQpBe5bwRHghGSJORLOKW81/n+3iJvHRHs+/S1Q==", + "requires": { + "cachedir": "^2.3.0", + "path-exists": "^4.0.0" + } + }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==" + }, + "jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "requires": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + } + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } + } + }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -27121,7 +27844,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", - "dev": true, "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -29129,8 +29851,7 @@ "defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" }, "define-properties": { "version": "1.1.3", @@ -30985,6 +31706,180 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" }, + "fetch-node-website": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/fetch-node-website/-/fetch-node-website-5.0.3.tgz", + "integrity": "sha512-O86T46FUWSOq4AWON39oaT8H90QFKAbmjfOVBhgaS87AFfeW00txz73KTv7QopPWtHBbGdI1S8cIT1VK1OQYLg==", + "requires": { + "chalk": "^4.0.0", + "cli-progress": "^3.7.0", + "figures": "^3.2.0", + "filter-obj": "^2.0.1", + "got": "^10.7.0", + "jest-validate": "^25.3.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@sindresorhus/is": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", + "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==" + }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cacheable-lookup": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz", + "integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==", + "requires": { + "@types/keyv": "^3.1.1", + "keyv": "^4.0.0" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "decompress-response": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz", + "integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==", + "requires": { + "mimic-response": "^2.0.0" + } + }, + "got": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz", + "integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==", + "requires": { + "@sindresorhus/is": "^2.0.0", + "@szmarczak/http-timer": "^4.0.0", + "@types/cacheable-request": "^6.0.1", + "cacheable-lookup": "^2.0.0", + "cacheable-request": "^7.0.1", + "decompress-response": "^5.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^5.0.0", + "lowercase-keys": "^2.0.0", + "mimic-response": "^2.1.0", + "p-cancelable": "^2.0.0", + "p-event": "^4.0.0", + "responselike": "^2.0.0", + "to-readable-stream": "^2.0.0", + "type-fest": "^0.10.0" + } + }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==" + }, + "jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "requires": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "type-fest": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz", + "integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==" + } + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -33162,8 +34057,7 @@ "json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "json-parse-better-errors": { "version": "1.0.2", @@ -33316,7 +34210,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", - "dev": true, "requires": { "json-buffer": "3.0.1" } @@ -34706,11 +35599,205 @@ "@babel/parser": "^7.0.0" } }, + "node-version-alias": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/node-version-alias/-/node-version-alias-1.0.1.tgz", + "integrity": "sha512-E9EhoJkpIIZyYplB298W8ZfhcojQrnKnUPcaOgJqVqICUZwPZkuj10nTzEscwdziOOj545v4tGPvNBG3ieUbSw==", + "requires": { + "all-node-versions": "^8.0.0", + "filter-obj": "^2.0.1", + "jest-validate": "^25.3.0", + "normalize-node-version": "^10.0.0", + "path-exists": "^4.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==" + }, + "jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "requires": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + } + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } + } + }, "noop2": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/noop2/-/noop2-2.0.0.tgz", "integrity": "sha1-S2NgFemIK1R4PAK0EvaZ2MXNCls=" }, + "normalize-node-version": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/normalize-node-version/-/normalize-node-version-10.0.0.tgz", + "integrity": "sha512-/gVbS/qAnowVxr2fJy3F0MxmCvx8QdXJDl8XUE7HT3vsDeDjQfZkX9OiPahF+51Hgy93cKG1hP6uyBjQsMCvWQ==", + "requires": { + "all-node-versions": "^8.0.0", + "filter-obj": "^2.0.1", + "jest-validate": "^25.3.0", + "semver": "^7.3.2" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==" + }, + "jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "requires": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + } + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } + } + }, "normalize-package-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.1.tgz", @@ -35319,8 +36406,7 @@ "p-cancelable": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", - "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==", - "dev": true + "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==" }, "p-defer": { "version": "1.0.0", @@ -36801,7 +37887,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "dev": true, "requires": { "lowercase-keys": "^2.0.0" } diff --git a/package.json b/package.json index d296615f7a7..6b2dc826567 100644 --- a/package.json +++ b/package.json @@ -76,8 +76,9 @@ "dependencies": { "@netlify/build": "^9.9.7", "@netlify/config": "^4.1.3", - "@netlify/framework-info": "^2.3.0", + "@netlify/framework-info": "^3.1.3", "@netlify/plugin-edge-handlers": "^1.11.5", + "@netlify/plugins-list": "^2.4.0", "@netlify/traffic-mesh-agent": "^0.27.10", "@netlify/zip-it-and-ship-it": "^2.7.1", "@oclif/command": "^1.6.1", @@ -143,6 +144,7 @@ "netlify-redirect-parser": "^3.0.7", "netlify-redirector": "^0.2.1", "node-fetch": "^2.6.0", + "node-version-alias": "^1.0.1", "open": "^7.0.0", "ora": "^4.1.1", "p-filter": "^2.1.0", @@ -195,6 +197,7 @@ "standard-version": "^9.0.0", "strip-ansi": "^6.0.0", "temp-dir": "^2.0.0", + "toml": "^3.0.0", "tomlify-j0.4": "^3.0.0" }, "ava": { diff --git a/src/utils/init/config-github.js b/src/utils/init/config-github.js index 5a1ba2339ab..a858ec32e79 100644 --- a/src/utils/init/config-github.js +++ b/src/utils/init/config-github.js @@ -3,7 +3,7 @@ const chalk = require('chalk') const ghauth = require('../gh-auth') -const { getBuildSettings, saveNetlifyToml, formatErrorMessage, createDeployKey, updateSite } = require('./utils') +const { getBuildSettings, saveNetlifyToml, formatErrorMessage, createDeployKey, setupSite } = require('./utils') const formatRepoAndOwner = ({ repoOwner, repoName }) => ({ name: chalk.magenta(repoName), @@ -180,11 +180,12 @@ module.exports = async function configGithub({ context, siteId, repoOwner, repoN globalConfig, config, site: { root: siteRoot }, + cachedConfig: { env }, } = netlify const token = await getGitHubToken({ log, globalConfig }) - const { buildCmd, buildDir, functionsDir } = await getBuildSettings({ siteRoot, config }) + const { buildCmd, buildDir, functionsDir, pluginsToInstall } = await getBuildSettings({ siteRoot, config, env, warn }) await saveNetlifyToml({ siteRoot, config, buildCmd, buildDir, functionsDir, warn }) const octokit = getGitHubClient({ token }) @@ -204,13 +205,15 @@ module.exports = async function configGithub({ context, siteId, repoOwner, repoN ...(buildCmd && { cmd: buildCmd }), } - await updateSite({ siteId, api, failAndExit, options: { repo } }) // calling updateSite with { repo } resets the functions dir so we need to sync it - const updatedSite = await updateSite({ - siteId, + const updatedSite = await setupSite({ api, failAndExit, - options: { build_settings: { functions_dir: functionsDir } }, + siteId, + repo, + functionsDir, + configPlugins: config.plugins, + pluginsToInstall, }) await addDeployHook({ deployHook: updatedSite.deploy_hook, octokit, repoOwner, repoName, failAndExit }) log() diff --git a/src/utils/init/config-manual.js b/src/utils/init/config-manual.js index 7d2b7d6a1eb..93aea2918a7 100644 --- a/src/utils/init/config-manual.js +++ b/src/utils/init/config-manual.js @@ -1,6 +1,6 @@ const inquirer = require('inquirer') -const { getBuildSettings, saveNetlifyToml, createDeployKey, updateSite } = require('./utils') +const { getBuildSettings, saveNetlifyToml, createDeployKey, setupSite } = require('./utils') const addDeployKey = async ({ log, exit, deployKey }) => { log('\nGive this Netlify SSH public key access to your repository:\n') @@ -55,9 +55,10 @@ module.exports = async function configManual({ context, siteId, repoData }) { api, config, site: { root: siteRoot }, + cachedConfig: { env }, } = netlify - const { buildCmd, buildDir, functionsDir } = await getBuildSettings({ siteRoot, config }) + const { buildCmd, buildDir, functionsDir, pluginsToInstall } = await getBuildSettings({ siteRoot, config, env, warn }) await saveNetlifyToml({ siteRoot, config, buildCmd, buildDir, functionsDir, warn }) const deployKey = await createDeployKey({ api, failAndExit }) @@ -74,13 +75,14 @@ module.exports = async function configManual({ context, siteId, repoData }) { ...(buildCmd && { cmd: buildCmd }), } - await updateSite({ siteId, api, failAndExit, options: { repo } }) - // calling updateSite with { repo } resets the functions dir so we need to sync it - const updatedSite = await updateSite({ - siteId, + const updatedSite = await setupSite({ api, failAndExit, - options: { build_settings: { functions_dir: functionsDir } }, + siteId, + repo, + functionsDir, + configPlugins: config.plugins, + pluginsToInstall, }) const deployHookAdded = await addDeployHook({ log, deployHook: updatedSite.deploy_hook }) if (!deployHookAdded) { diff --git a/src/utils/init/frameworks.js b/src/utils/init/frameworks.js new file mode 100644 index 00000000000..53a7126f941 --- /dev/null +++ b/src/utils/init/frameworks.js @@ -0,0 +1,24 @@ +const { listFrameworks } = require('@netlify/framework-info') + +const getFrameworkInfo = async ({ siteRoot, nodeVersion }) => { + const frameworks = await listFrameworks({ projectDir: siteRoot, nodeVersion }) + // several frameworks can be detected - first one has highest priority + if (frameworks.length !== 0) { + const [ + { + name, + build: { directory, commands }, + plugins, + }, + ] = frameworks + return { + frameworkName: name, + frameworkBuildCommand: commands[0], + frameworkBuildDir: directory, + frameworkPlugins: plugins, + } + } + return {} +} + +module.exports = { getFrameworkInfo } diff --git a/src/utils/init/node-version.js b/src/utils/init/node-version.js new file mode 100644 index 00000000000..55ea2c85421 --- /dev/null +++ b/src/utils/init/node-version.js @@ -0,0 +1,32 @@ +const { get } = require('dot-prop') +const locatePath = require('locate-path') +const nodeVersionAlias = require('node-version-alias') + +const { readFileAsync } = require('../../lib/fs') + +const DEFAULT_NODE_VERSION = '12.18.0' +const NVM_FLAG_PREFIX = '--' + +// to support NODE_VERSION=--lts, etc. +const normalizeConfiguredVersion = (version) => + version.startsWith(NVM_FLAG_PREFIX) ? version.slice(NVM_FLAG_PREFIX.length) : version + +const detectNodeVersion = async ({ siteRoot, env, warn }) => { + try { + const nodeVersionFile = await locatePath(['.nvmrc', '.node-version'], { cwd: siteRoot }) + const configuredVersion = + nodeVersionFile === undefined ? get(env, 'NODE_VERSION.value') : await readFileAsync(nodeVersionFile, 'utf8') + + const version = + configuredVersion === undefined + ? DEFAULT_NODE_VERSION + : await nodeVersionAlias(normalizeConfiguredVersion(configuredVersion)) + + return version + } catch (error) { + warn(`Failed detecting Node.js version: ${error.message}`) + return DEFAULT_NODE_VERSION + } +} + +module.exports = { detectNodeVersion } diff --git a/src/utils/init/plugins.js b/src/utils/init/plugins.js new file mode 100644 index 00000000000..36ca3152af6 --- /dev/null +++ b/src/utils/init/plugins.js @@ -0,0 +1,36 @@ +const pluginsList = require('@netlify/plugins-list') +const fetch = require('node-fetch') + +const PLUGINS_LIST_URL = 'https://netlify-plugins.netlify.app/plugins.json' +// 1 minute +const PLUGINS_LIST_TIMEOUT = 6e4 + +const getPluginsList = async () => { + try { + const response = await fetch(PLUGINS_LIST_URL, { timeout: PLUGINS_LIST_TIMEOUT }) + return await response.json() + } catch { + return pluginsList + } +} + +const getPluginInfo = (list, packageName) => list.find(({ package }) => package === packageName) + +const isPluginInstalled = (configPlugins, plugin) => + configPlugins.some(({ package: configPlugin }) => configPlugin === plugin) + +const getRecommendPlugins = (frameworkPlugins, config) => + frameworkPlugins.filter((plugin) => !isPluginInstalled(config.plugins, plugin)) + +const getPluginsToInstall = ({ plugins, installSinglePlugin, recommendedPlugins }) => { + if (Array.isArray(plugins)) { + return plugins.map((plugin) => ({ package: plugin })) + } + + return installSinglePlugin === true ? [{ package: recommendedPlugins[0] }] : [] +} + +const getUIPlugins = (configPlugins) => + configPlugins.filter(({ origin }) => origin === 'ui').map(({ package }) => ({ package })) + +module.exports = { getPluginsList, getPluginInfo, getRecommendPlugins, getPluginsToInstall, getUIPlugins } diff --git a/src/utils/init/utils.js b/src/utils/init/utils.js index 816be700254..ea52940033c 100644 --- a/src/utils/init/utils.js +++ b/src/utils/init/utils.js @@ -1,6 +1,6 @@ +const { EOL } = require('os') const path = require('path') -const { listFrameworks } = require('@netlify/framework-info') const chalk = require('chalk') const cleanDeep = require('clean-deep') const inquirer = require('inquirer') @@ -8,6 +8,10 @@ const isEmpty = require('lodash/isEmpty') const { fileExistsAsync, writeFileAsync } = require('../../lib/fs') +const { getFrameworkInfo } = require('./frameworks') +const { detectNodeVersion } = require('./node-version') +const { getPluginsList, getPluginInfo, getRecommendPlugins, getPluginsToInstall, getUIPlugins } = require('./plugins') + const normalizeDir = ({ siteRoot, dir, defaultValue }) => { if (dir === undefined) { return defaultValue @@ -17,21 +21,8 @@ const normalizeDir = ({ siteRoot, dir, defaultValue }) => { return relativeDir || defaultValue } -const getFrameworkDefaults = async ({ siteRoot }) => { - const frameworks = await listFrameworks({ projectDir: siteRoot }) - if (frameworks.length !== 0) { - const [ - { - build: { directory, commands }, - }, - ] = frameworks - return { frameworkBuildCommand: commands[0], frameworkBuildDir: directory } - } - return {} -} - -const getDefaultSettings = async ({ siteRoot, config }) => { - const { frameworkBuildCommand, frameworkBuildDir } = await getFrameworkDefaults({ siteRoot }) +const getDefaultSettings = ({ siteRoot, config, frameworkPlugins, frameworkBuildCommand, frameworkBuildDir }) => { + const recommendedPlugins = getRecommendPlugins(frameworkPlugins, config) const { command: defaultBuildCmd = frameworkBuildCommand, publish: defaultBuildDir = frameworkBuildDir, @@ -42,12 +33,18 @@ const getDefaultSettings = async ({ siteRoot, config }) => { defaultBuildCmd, defaultBuildDir: normalizeDir({ siteRoot, dir: defaultBuildDir, defaultValue: '.' }), defaultFunctionsDir: normalizeDir({ siteRoot, dir: defaultFunctionsDir, defaultValue: 'netlify/functions' }), + recommendedPlugins, } } -const getBuildSettings = async ({ siteRoot, config }) => { - const { defaultBuildCmd, defaultBuildDir, defaultFunctionsDir } = await getDefaultSettings({ siteRoot, config }) - const { buildCmd, buildDir, functionsDir } = await inquirer.prompt([ +const getPromptInputs = async ({ + defaultBuildCmd, + defaultBuildDir, + defaultFunctionsDir, + recommendedPlugins, + frameworkName, +}) => { + const inputs = [ { type: 'input', name: 'buildCmd', @@ -67,9 +64,74 @@ const getBuildSettings = async ({ siteRoot, config }) => { message: 'Netlify functions folder:', default: defaultFunctionsDir, }, - ]) + ] + + if (recommendedPlugins.length === 0) { + return inputs + } - return { buildCmd, buildDir, functionsDir } + const pluginsList = await getPluginsList() + + const prefix = `Seems like this is a ${formatTitle(frameworkName)} site.${EOL}❇️ ` + if (recommendedPlugins.length === 1) { + const { name } = getPluginInfo(pluginsList, recommendedPlugins[0]) + return [ + ...inputs, + { + type: 'confirm', + name: 'installSinglePlugin', + message: `${prefix}We're going to install this Build Plugin: ${formatTitle( + `${name} plugin`, + )}${EOL}➡️ OK to install?`, + default: true, + }, + ] + } + + const infos = recommendedPlugins.map((packageName) => getPluginInfo(pluginsList, packageName)) + return [ + ...inputs, + { + type: 'checkbox', + name: 'plugins', + message: `${prefix}We're going to install these plugins: ${infos + .map(({ name }) => `${name} plugin`) + .map(formatTitle) + .join(', ')}${EOL}➡️ OK to install??`, + choices: infos.map(({ name, package }) => ({ name: `${name} plugin`, value: package })), + default: infos.map(({ package }) => package), + }, + ] +} + +const getBuildSettings = async ({ siteRoot, config, env, warn }) => { + const nodeVersion = await detectNodeVersion({ siteRoot, env, warn }) + const { frameworkName, frameworkBuildCommand, frameworkBuildDir, frameworkPlugins = [] } = await getFrameworkInfo({ + siteRoot, + nodeVersion, + }) + const { defaultBuildCmd, defaultBuildDir, defaultFunctionsDir, recommendedPlugins } = await getDefaultSettings({ + siteRoot, + config, + frameworkBuildCommand, + frameworkBuildDir, + frameworkPlugins, + }) + const { buildCmd, buildDir, functionsDir, plugins, installSinglePlugin } = await inquirer.prompt( + await getPromptInputs({ + defaultBuildCmd, + defaultBuildDir, + defaultFunctionsDir, + recommendedPlugins, + frameworkName, + }), + ) + const pluginsToInstall = getPluginsToInstall({ + plugins, + installSinglePlugin, + recommendedPlugins, + }) + return { buildCmd, buildDir, functionsDir, pluginsToInstall } } const getNetlifyToml = ({ @@ -126,6 +188,8 @@ const saveNetlifyToml = async ({ siteRoot, config, buildCmd, buildDir, functions const formatErrorMessage = ({ message, error }) => `${message} with error: ${chalk.red(error.message)}` +const formatTitle = (title) => chalk.cyan(title) + const createDeployKey = async ({ api, failAndExit }) => { try { const deployKey = await api.createDeployKey() @@ -146,4 +210,23 @@ const updateSite = async ({ siteId, api, failAndExit, options }) => { } } -module.exports = { getBuildSettings, saveNetlifyToml, formatErrorMessage, createDeployKey, updateSite } +const setupSite = async ({ api, failAndExit, siteId, repo, functionsDir, configPlugins, pluginsToInstall }) => { + await updateSite({ + siteId, + api, + failAndExit, + // merge existing plugins with new ones + options: { repo, plugins: [...getUIPlugins(configPlugins), ...pluginsToInstall] }, + }) + // calling updateSite with { repo } resets the functions dir so we need to sync it + const updatedSite = await updateSite({ + siteId, + api, + failAndExit, + options: { build_settings: { functions_dir: functionsDir } }, + }) + + return updatedSite +} + +module.exports = { getBuildSettings, saveNetlifyToml, formatErrorMessage, createDeployKey, updateSite, setupSite } diff --git a/tests/command.init.test.js b/tests/command.init.test.js new file mode 100644 index 00000000000..2bf82580bd6 --- /dev/null +++ b/tests/command.init.test.js @@ -0,0 +1,378 @@ +const { Buffer } = require('buffer') + +const test = require('ava') +const execa = require('execa') +const toml = require('toml') + +const { readFileAsync } = require('../src/lib/fs') + +const cliPath = require('./utils/cli-path') +const { withMockApi } = require('./utils/mock-api') +const { withSiteBuilder } = require('./utils/site-builder') + +const handleQuestions = (process, questions) => { + const remainingQuestions = [...questions] + let buffer = '' + process.stdout.on('data', (data) => { + buffer += data + const index = remainingQuestions.findIndex(({ question }) => buffer.includes(question)) + if (index >= 0) { + buffer = '' + process.stdin.write(Buffer.from(remainingQuestions[index].answer)) + remainingQuestions.splice(index, 1) + } + }) +} + +const CONFIRM = '\n' +const DOWN = '\u001B[B' +const answerWithValue = (value) => `${value}${CONFIRM}` + +const assetSiteRequests = ( + t, + requests, + { command = 'custom-build-command', functions = 'custom-functions', publish = 'custom-publish', plugins = [] } = {}, +) => { + // assert updateSite was called with user inputs + const siteUpdateRequests = requests + .filter(({ path, method }) => path === '/api/v1/sites/site_id' && method === 'PATCH') + .map(({ body }) => body) + t.deepEqual(siteUpdateRequests, [ + { + plugins, + repo: { + allowed_branches: ['master'], + cmd: command, + dir: publish, + provider: 'manual', + repo_branch: 'master', + repo_path: 'git@github.com:owner/repo.git', + }, + }, + { + build_settings: { + functions_dir: functions, + }, + }, + ]) +} + +const assertSiteInit = async ( + t, + builder, + requests, + { command = 'custom-build-command', functions = 'custom-functions', publish = 'custom-publish', plugins = [] } = {}, +) => { + // assert netlify.toml was created with user inputs + const netlifyToml = toml.parse(await readFileAsync(`${builder.directory}/netlify.toml`, 'utf8')) + t.deepEqual(netlifyToml, { + build: { command, functions, publish }, + }) + + assetSiteRequests(t, requests, { command, functions, publish, plugins }) +} + +test('netlify init existing site', async (t) => { + const initQuestions = [ + { + question: 'Create & configure a new site', + answer: CONFIRM, + }, + { + question: 'How do you want to link this folder to a site', + answer: CONFIRM, + }, + { + question: 'Your build command (hugo build/yarn run build/etc)', + answer: answerWithValue('custom-build-command'), + }, + { + question: 'Directory to deploy (blank for current dir)', + answer: answerWithValue('custom-publish'), + }, + { + question: 'Netlify functions folder', + answer: answerWithValue('custom-functions'), + }, + { + question: 'No netlify.toml detected', + answer: CONFIRM, + }, + { question: 'Give this Netlify SSH public key access to your repository', answer: CONFIRM }, + { question: 'The SSH URL of the remote git repo', answer: CONFIRM }, + { question: 'Configure the following webhook for your repository', answer: CONFIRM }, + ] + + const siteInfo = { + admin_url: 'https://app.netlify.com/sites/site-name/overview', + ssl_url: 'https://site-name.netlify.app/', + id: 'site_id', + name: 'site-name', + build_settings: { repo_url: 'https://github.com/owner/repo' }, + } + const routes = [ + { + path: 'accounts', + response: [{ slug: 'test-account' }], + }, + { path: 'sites/site_id/service-instances', response: [] }, + { path: 'sites/site_id', response: siteInfo }, + { + path: 'sites', + response: [siteInfo], + }, + { path: 'deploy_keys', method: 'post', response: { public_key: 'public_key' } }, + { path: 'sites/site_id', method: 'patch', response: { deploy_hook: 'deploy_hook' } }, + ] + + await withSiteBuilder('new-site', async (builder) => { + builder.withGit({ repoUrl: 'git@github.com:owner/repo.git' }) + + await builder.buildAsync() + await withMockApi(routes, async ({ apiUrl, requests }) => { + // --force is required since we return an existing site in the `sites` route + // --manual is used to avoid the config-github flow that uses GitHub API + const childProcess = execa(cliPath, ['init', '--force', '--manual'], { + cwd: builder.directory, + // NETLIFY_SITE_ID and NETLIFY_AUTH_TOKEN are required for @netlify/config to retrieve site info + env: { NETLIFY_API_URL: apiUrl, NETLIFY_SITE_ID: 'site_id', NETLIFY_AUTH_TOKEN: 'fake-token' }, + }) + + handleQuestions(childProcess, initQuestions) + + await childProcess + + await assertSiteInit(t, builder, requests) + }) + }) +}) + +test('netlify init new site', async (t) => { + const initQuestions = [ + { + question: 'Create & configure a new site', + answer: answerWithValue(DOWN), + }, + { question: 'Team: (Use arrow keys)', answer: CONFIRM }, + { question: 'Site name (optional)', answer: answerWithValue('test-site-name') }, + { + question: 'Your build command (hugo build/yarn run build/etc)', + answer: answerWithValue('custom-build-command'), + }, + { + question: 'Directory to deploy (blank for current dir)', + answer: answerWithValue('custom-publish'), + }, + { + question: 'Netlify functions folder', + answer: answerWithValue('custom-functions'), + }, + { + question: 'No netlify.toml detected', + answer: CONFIRM, + }, + { question: 'Give this Netlify SSH public key access to your repository', answer: CONFIRM }, + { question: 'The SSH URL of the remote git repo', answer: CONFIRM }, + { question: 'Configure the following webhook for your repository', answer: CONFIRM }, + ] + + const routes = [ + { + path: 'accounts', + response: [{ slug: 'test-account' }], + }, + { + path: 'sites', + response: [], + }, + { + path: 'user', + response: { name: 'test user', slug: 'test-user', email: 'user@test.com' }, + }, + { + path: 'test-account/sites', + method: 'post', + response: { id: 'site_id', name: 'test-site-name' }, + }, + { path: 'deploy_keys', method: 'post', response: { public_key: 'public_key' } }, + { path: 'sites/site_id', method: 'patch', response: { deploy_hook: 'deploy_hook' } }, + ] + + await withSiteBuilder('new-site', async (builder) => { + builder.withGit({ repoUrl: 'git@github.com:owner/repo.git' }) + + await builder.buildAsync() + await withMockApi(routes, async ({ apiUrl, requests }) => { + // --manual is used to avoid the config-github flow that uses GitHub API + const childProcess = execa(cliPath, ['init', '--manual'], { + cwd: builder.directory, + env: { NETLIFY_API_URL: apiUrl }, + encoding: 'utf8', + }) + + handleQuestions(childProcess, initQuestions) + + await childProcess + + await assertSiteInit(t, builder, requests) + }) + }) +}) + +test('netlify init new Next.js site', async (t) => { + const initQuestions = [ + { + question: 'Create & configure a new site', + answer: answerWithValue(DOWN), + }, + { question: 'Team: (Use arrow keys)', answer: CONFIRM }, + { question: 'Site name (optional)', answer: answerWithValue('test-site-name') }, + { + question: 'Your build command (hugo build/yarn run build/etc)', + answer: answerWithValue('custom-build-command'), + }, + { + question: 'Directory to deploy (blank for current dir)', + answer: answerWithValue('custom-publish'), + }, + { + question: 'Netlify functions folder', + answer: answerWithValue('custom-functions'), + }, + { + question: 'OK to install', + answer: CONFIRM, + }, + { + question: 'No netlify.toml detected', + answer: CONFIRM, + }, + { question: 'Give this Netlify SSH public key access to your repository', answer: CONFIRM }, + { question: 'The SSH URL of the remote git repo', answer: CONFIRM }, + { question: 'Configure the following webhook for your repository', answer: CONFIRM }, + ] + + const routes = [ + { + path: 'accounts', + response: [{ slug: 'test-account' }], + }, + + { + path: 'sites', + response: [], + }, + { + path: 'user', + response: { name: 'test user', slug: 'test-user', email: 'user@test.com' }, + }, + { + path: 'test-account/sites', + method: 'post', + response: { id: 'site_id', name: 'test-site-name' }, + }, + { path: 'deploy_keys', method: 'post', response: { public_key: 'public_key' } }, + { path: 'sites/site_id', method: 'patch', response: { deploy_hook: 'deploy_hook' } }, + ] + + await withSiteBuilder('new-site', async (builder) => { + builder + .withGit({ repoUrl: 'git@github.com:owner/repo.git' }) + .withPackageJson({ packageJson: { dependencies: { next: '^10.0.0' } } }) + + await builder.buildAsync() + await withMockApi(routes, async ({ apiUrl, requests }) => { + // --manual is used to avoid the config-github flow that uses GitHub API + const childProcess = execa(cliPath, ['init', '--manual'], { + cwd: builder.directory, + env: { NETLIFY_API_URL: apiUrl }, + }) + + handleQuestions(childProcess, initQuestions) + + await childProcess + + await assertSiteInit(t, builder, requests, { plugins: [{ package: '@netlify/plugin-nextjs' }] }) + }) + }) +}) + +test('netlify init existing Next.js site with existing plugins', async (t) => { + const initQuestions = [ + { + question: 'Create & configure a new site', + answer: CONFIRM, + }, + { + question: 'How do you want to link this folder to a site', + answer: CONFIRM, + }, + { + question: 'Your build command (hugo build/yarn run build/etc)', + answer: answerWithValue('custom-build-command'), + }, + { + question: 'Directory to deploy (blank for current dir)', + answer: answerWithValue('custom-publish'), + }, + { + question: 'Netlify functions folder', + answer: answerWithValue('custom-functions'), + }, + { + question: 'OK to install', + answer: CONFIRM, + }, + { question: 'Give this Netlify SSH public key access to your repository', answer: CONFIRM }, + { question: 'The SSH URL of the remote git repo', answer: CONFIRM }, + { question: 'Configure the following webhook for your repository', answer: CONFIRM }, + ] + + const siteInfo = { + admin_url: 'https://app.netlify.com/sites/site-name/overview', + ssl_url: 'https://site-name.netlify.app/', + id: 'site_id', + name: 'site-name', + build_settings: { repo_url: 'https://github.com/owner/repo' }, + plugins: [{ package: '@netlify/plugin-lighthouse' }], + } + const routes = [ + { + path: 'accounts', + response: [{ slug: 'test-account' }], + }, + { path: 'sites/site_id/service-instances', response: [] }, + { path: 'sites/site_id', response: siteInfo }, + { + path: 'sites', + response: [siteInfo], + }, + { path: 'deploy_keys', method: 'post', response: { public_key: 'public_key' } }, + { path: 'sites/site_id', method: 'patch', response: { deploy_hook: 'deploy_hook' } }, + ] + + await withSiteBuilder('new-site', async (builder) => { + builder + .withGit({ repoUrl: 'git@github.com:owner/repo.git' }) + .withPackageJson({ packageJson: { dependencies: { next: '^10.0.0' } } }) + + await builder.buildAsync() + await withMockApi(routes, async ({ apiUrl, requests }) => { + // --force is required since we return an existing site in the `sites` route + // --manual is used to avoid the config-github flow that uses GitHub API + const childProcess = execa(cliPath, ['init', '--force', '--manual'], { + cwd: builder.directory, + // NETLIFY_SITE_ID and NETLIFY_AUTH_TOKEN are required for @netlify/config to retrieve site info + env: { NETLIFY_API_URL: apiUrl, NETLIFY_SITE_ID: 'site_id', NETLIFY_AUTH_TOKEN: 'fake-token' }, + }) + + handleQuestions(childProcess, initQuestions) + + await childProcess + + assetSiteRequests(t, requests, { + plugins: [{ package: '@netlify/plugin-lighthouse' }, { package: '@netlify/plugin-nextjs' }], + }) + }) + }) +}) diff --git a/tests/command.lm.test.js b/tests/command.lm.test.js index 0ebc63be544..6bfea026b4e 100644 --- a/tests/command.lm.test.js +++ b/tests/command.lm.test.js @@ -21,7 +21,7 @@ if (process.env.IS_FORK !== 'true') { const builder = createSiteBuilder({ siteName: 'site-with-lm' }) await builder.buildAsync() - const mockApi = startMockApi({ + const { server } = startMockApi({ routes: [ { method: 'post', path: 'sites/site_id/services/large-media/instances', status: 201 }, { path: 'sites/site_id', response: { id_domain: 'localhost' } }, @@ -33,8 +33,8 @@ if (process.env.IS_FORK !== 'true') { env: { NETLIFY_SITE_ID: siteId, SHELL: process.env.SHELL || 'bash' }, } t.context.builder = builder - t.context.mockApi = mockApi - t.context.apiUrl = `http://localhost:${mockApi.address().port}/api/v1` + t.context.mockApi = server + t.context.apiUrl = `http://localhost:${server.address().port}/api/v1` await callCli(['lm:uninstall'], t.context.execOptions) }) diff --git a/tests/utils/mock-api.js b/tests/utils/mock-api.js index c08c01cbafe..d9e5ecf8b6d 100644 --- a/tests/utils/mock-api.js +++ b/tests/utils/mock-api.js @@ -1,24 +1,48 @@ const bodyParser = require('body-parser') const express = require('express') +const addRequest = (requests, request) => { + requests.push({ + path: request.path, + body: request.body, + method: request.method, + }) +} + const startMockApi = ({ routes }) => { + const requests = [] const app = express() app.use(bodyParser.urlencoded({ extended: true })) + app.use(bodyParser.json()) + app.use(bodyParser.raw()) routes.forEach(({ method = 'get', path, response = {}, status = 200 }) => { app[method.toLowerCase()](`/api/v1/${path}`, function onRequest(req, res) { + addRequest(requests, req) res.status(status) res.json(response) }) }) app.all('*', function onRequest(req, res) { + addRequest(requests, req) console.warn(`Route not found: ${req.url}`) res.status(404) res.json({ message: 'Not found' }) }) - return app.listen() + return { server: app.listen(), requests } +} + +const withMockApi = async (routes, testHandler) => { + let mockApi + try { + mockApi = startMockApi({ routes }) + const apiUrl = `http://localhost:${mockApi.server.address().port}/api/v1` + return await testHandler({ apiUrl, requests: mockApi.requests }) + } finally { + mockApi.server.close() + } } -module.exports = { startMockApi } +module.exports = { withMockApi, startMockApi } diff --git a/tests/utils/site-builder.js b/tests/utils/site-builder.js index 4831055a4b9..2c202e9ab34 100644 --- a/tests/utils/site-builder.js +++ b/tests/utils/site-builder.js @@ -2,6 +2,7 @@ const os = require('os') const path = require('path') const process = require('process') +const execa = require('execa') const tempDirectory = require('temp-dir') const { toToml } = require('tomlify-j0.4') const { v4: uuidv4 } = require('uuid') @@ -44,10 +45,10 @@ const createSiteBuilder = ({ siteName }) => { }) return builder }, - withPackageJson: ({ object, pathPrefix = '' }) => { + withPackageJson: ({ packageJson, pathPrefix = '' }) => { const dest = path.join(directory, pathPrefix, 'package.json') tasks.push(async () => { - const content = JSON.stringify(object, null, 2) + const content = JSON.stringify(packageJson, null, 2) await ensureDir(path.dirname(dest)) await fs.writeFileAsync(dest, `${content}\n`) }) @@ -122,6 +123,13 @@ const createSiteBuilder = ({ siteName }) => { }) return builder }, + withGit: ({ repoUrl }) => { + tasks.push(async () => { + await execa('git', ['init'], { cwd: directory }) + await execa('git', ['remote', 'add', 'origin', repoUrl], { cwd: directory }) + }) + return builder + }, buildAsync: async () => { for (const task of tasks) { // eslint-disable-next-line no-await-in-loop