Porting to TypeScript (#28)
* moving files to TypeScript * moving to ES import/export * adding midding types * more work to port to TypeScript * fixing environment vars issue * adding sourcemaps * making the auth functions TypeScript too * adding vscode build and launch tasks * fixing some typescript compile issues * overhaul of tests - added ts-jest and a jest config to handle it - removed unionfs (license is not a good license) - removed memfs (not needed) - using mock-fs to mock the fs easily - forced time lies about types to mock shell.exit properly: * chore: refactor TS migration Closes #22 * removing unneeded file * including json files in TS output * making all functions async, to force a Promise return * adding a watch command * allowing SSL backends with dev certs * running everything from the right path * removing directive * including custom env settings * moving 404 page to a non-compile location Co-authored-by: Aaron Powell <me@aaron-powell.com>
This commit is contained in:
Родитель
398bc596db
Коммит
dc469f6975
|
@ -41,3 +41,5 @@ venv.bak/
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
dist
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"command": "npm start",
|
||||
"name": "Run npm start",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"name": "Jest Tests",
|
||||
"command": "node ${workspaceRoot}/node_modules/jest/bin/jest.js -i",
|
||||
// "args": ["-i"],
|
||||
"preLaunchTask": "build",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": ["$tsc"],
|
||||
"label": "build",
|
||||
"detail": "tsc"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
StaticWebAppsAuthCookie?: string;
|
||||
StaticWebAppsAuthContextCookie: string;
|
||||
AppServiceAuthSession: string;
|
||||
DEBUG: string;
|
||||
GITHUB_CLIENT_ID: string;
|
||||
GITHUB_CLIENT_SECRET: string;
|
||||
SWA_EMU_AUTH_URI: string;
|
||||
SWA_EMU_API_URI: string;
|
||||
SWA_EMU_API_PREFIX: string;
|
||||
SWA_EMU_APP_URI: string;
|
||||
SWA_EMU_APP_LOCATION: string;
|
||||
SWA_EMU_HOST: string;
|
||||
SWA_EMU_PORT: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
};
|
|
@ -4,6 +4,12 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@azure/functions": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/functions/-/functions-1.2.2.tgz",
|
||||
"integrity": "sha512-p/dDHq1sG/iAib+eDY4NxskWHoHW1WFzD85s0SfWxc2wVjJbxB0xz/zBF4s7ymjVgTu+0ceipeBk+tmpnt98oA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
||||
|
@ -607,6 +613,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@jest/create-cache-key-function": {
|
||||
"version": "26.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-26.5.0.tgz",
|
||||
"integrity": "sha512-DJ+pEBUIqarrbv1W/C39f9YH0rJ4wsXZ/VC6JafJPlHW2HOucKceeaqTOQj9MEDQZjySxMLkOq5mfXZXNZcmWw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jest/environment": {
|
||||
"version": "26.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.3.0.tgz",
|
||||
|
@ -1136,6 +1148,15 @@
|
|||
"@babel/types": "^7.3.0"
|
||||
}
|
||||
},
|
||||
"@types/blessed": {
|
||||
"version": "0.1.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/blessed/-/blessed-0.1.17.tgz",
|
||||
"integrity": "sha512-BKvUtnrXksNdK0fOYV/9HJGkjCcAvOGMSCJsiHaBFyBeyqHwy2OHK32r5XNI+q0eXuAuGqtPOnDetHnbZoYqag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/cacheable-request": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
|
||||
|
@ -1154,6 +1175,22 @@
|
|||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/cookie": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz",
|
||||
"integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
|
||||
"integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/minimatch": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/graceful-fs": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz",
|
||||
|
@ -1169,6 +1206,15 @@
|
|||
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/http-proxy": {
|
||||
"version": "1.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.4.tgz",
|
||||
"integrity": "sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
|
||||
|
@ -1194,129 +1240,22 @@
|
|||
}
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "26.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.14.tgz",
|
||||
"integrity": "sha512-Hz5q8Vu0D288x3iWXePSn53W7hAjP0H7EQ6QvDO9c7t46mR0lNOLlfuwQ+JkVxuhygHzlzPX+0jKdA3ZgSh+Vg==",
|
||||
"version": "26.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz",
|
||||
"integrity": "sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jest-diff": "^25.2.1",
|
||||
"pretty-format": "^25.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jest/types": {
|
||||
"version": "25.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz",
|
||||
"integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"@types/istanbul-reports": "^1.1.1",
|
||||
"@types/yargs": "^15.0.0",
|
||||
"chalk": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@types/istanbul-reports": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz",
|
||||
"integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/istanbul-lib-coverage": "*",
|
||||
"@types/istanbul-lib-report": "*"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"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==",
|
||||
"dev": true,
|
||||
"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==",
|
||||
"dev": true,
|
||||
"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==",
|
||||
"dev": true
|
||||
},
|
||||
"diff-sequences": {
|
||||
"version": "25.2.6",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz",
|
||||
"integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"jest-diff": {
|
||||
"version": "25.5.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz",
|
||||
"integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^3.0.0",
|
||||
"diff-sequences": "^25.2.6",
|
||||
"jest-get-type": "^25.2.6",
|
||||
"pretty-format": "^25.5.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==",
|
||||
"dev": true
|
||||
},
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^25.5.0",
|
||||
"ansi-regex": "^5.0.0",
|
||||
"ansi-styles": "^4.0.0",
|
||||
"react-is": "^16.12.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
"jest-diff": "^26.0.0",
|
||||
"pretty-format": "^26.0.0"
|
||||
}
|
||||
},
|
||||
"@types/jsonwebtoken": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
|
||||
"integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/keyv": {
|
||||
|
@ -1328,18 +1267,43 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mock-fs": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.0.tgz",
|
||||
"integrity": "sha512-FUqxhURwqFtFBCuUj3uQMp7rPSQs//b3O9XecAVxhqS9y4/W8SIJEZFq2mmpnFVZBXwR/2OyPLE97CpyYiB8Mw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.0.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz",
|
||||
"integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz",
|
||||
"integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||
|
@ -1367,6 +1331,16 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/shelljs": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.8.tgz",
|
||||
"integrity": "sha512-lD3LWdg6j8r0VRBFahJVaxoW0SIcswxKaFUrmKl33RJVeeoNYQAz4uqCJ5Z6v4oIBOsC5GozX+I5SorIKiTcQA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/glob": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
||||
|
@ -2287,6 +2261,15 @@
|
|||
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
|
||||
"dev": true
|
||||
},
|
||||
"bs-logger": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
|
||||
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-json-stable-stringify": "2.x"
|
||||
}
|
||||
},
|
||||
"bser": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
||||
|
@ -3824,12 +3807,6 @@
|
|||
"map-cache": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"fs-monkey": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz",
|
||||
"integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
|
@ -6912,6 +6889,12 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
|
@ -6990,6 +6973,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"makeerror": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
|
||||
|
@ -7020,15 +7009,6 @@
|
|||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"memfs": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz",
|
||||
"integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs-monkey": "1.0.1"
|
||||
}
|
||||
},
|
||||
"meow": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/meow/-/meow-7.0.1.tgz",
|
||||
|
@ -7297,6 +7277,12 @@
|
|||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mock-fs": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.13.0.tgz",
|
||||
"integrity": "sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA==",
|
||||
"dev": true
|
||||
},
|
||||
"modify-values": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz",
|
||||
|
@ -10005,6 +9991,46 @@
|
|||
"integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=",
|
||||
"dev": true
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "26.4.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.3.tgz",
|
||||
"integrity": "sha512-pFDkOKFGY+nL9v5pkhm+BIFpoAuno96ff7GMnIYr/3L6slFOS365SI0fGEVYx2RKGji5M2elxhWjDMPVcOCdSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/create-cache-key-function": "^26.5.0",
|
||||
"@types/jest": "26.x",
|
||||
"bs-logger": "0.x",
|
||||
"buffer-from": "1.x",
|
||||
"fast-json-stable-stringify": "2.x",
|
||||
"jest-util": "^26.1.0",
|
||||
"json5": "2.x",
|
||||
"lodash.memoize": "4.x",
|
||||
"make-error": "1.x",
|
||||
"mkdirp": "1.x",
|
||||
"semver": "7.x",
|
||||
"yargs-parser": "20.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
|
||||
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz",
|
||||
"integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||
|
@ -10061,6 +10087,12 @@
|
|||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz",
|
||||
"integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz",
|
||||
|
@ -10088,15 +10120,6 @@
|
|||
"set-value": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"unionfs": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/unionfs/-/unionfs-4.4.0.tgz",
|
||||
"integrity": "sha512-N+TuJHJ3PjmzIRCE1d2N3VN4qg/P78eh/nxzwHnzpg3W2Mvf8Wvi7J1mvv6eNkb8neUeSdFSQsKna0eXVyF4+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs-monkey": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"unique-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
||||
|
|
24
package.json
24
package.json
|
@ -4,11 +4,14 @@
|
|||
"description": "Azure Static Web Apps Emulator for Auth, API and static content",
|
||||
"scripts": {
|
||||
"release": "release-it --preRelease=alpha",
|
||||
"test": "jest"
|
||||
"test": "jest",
|
||||
"build": "tsc",
|
||||
"prebuild": "rm -fr dist",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"bin": {
|
||||
"swa": "./bin/index.js",
|
||||
"swa-emu": "./bin/index.js"
|
||||
"swa": "./dist/cli.js",
|
||||
"swa-emu": "./dist/cli.js"
|
||||
},
|
||||
"author": "Wassim Chegham <github@wassim.dev>",
|
||||
"dependencies": {
|
||||
|
@ -25,13 +28,22 @@
|
|||
"yaml": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@azure/functions": "^1.2.2",
|
||||
"@release-it/conventional-changelog": "^1.1.4",
|
||||
"@types/jest": "^26.0.14",
|
||||
"@types/blessed": "^0.1.17",
|
||||
"@types/cookie": "^0.4.0",
|
||||
"@types/http-proxy": "^1.17.4",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/mock-fs": "^4.13.0",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/shelljs": "^0.8.8",
|
||||
"jest": "^26.4.2",
|
||||
"memfs": "^3.2.0",
|
||||
"mock-fs": "^4.13.0",
|
||||
"release-it": "^13.6.4",
|
||||
"supertest": "^4.0.2",
|
||||
"unionfs": "^4.4.0"
|
||||
"ts-jest": "^26.4.3",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"homepage": "https://github.com/manekinekko/swa-emulator#readme",
|
||||
"private": false,
|
||||
|
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_HOST = "http://localhost:" + process.env.SWA_EMU_PORT;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const { provider } = context.bindingData;
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const { post_login_redirect_uri = "" } = req.query;
|
||||
|
||||
context.res = response({
|
||||
|
@ -31,3 +31,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, _req: HttpRequest) {
|
||||
const { provider } = context.bindingData;
|
||||
const { post_login_redirect_uri } = req.query;
|
||||
|
||||
const location = `${SWA_EMU_AUTH_URI}/.redirect/${provider}?hostName=localhost&staticWebAppsAuthNonce=${context.invocationId}`;
|
||||
|
||||
|
@ -35,3 +35,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
const { response, validateCookie } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response, validateCookie } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const cookie = req.headers.cookie;
|
||||
const { post_logout_redirect_uri } = req.query;
|
||||
|
||||
if (!cookie || !validateCookie(cookie)) {
|
||||
return response({
|
||||
context.res = response({
|
||||
context,
|
||||
status: 401,
|
||||
});
|
||||
|
@ -31,3 +31,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_HOST = "http://localhost:" + process.env.SWA_EMU_PORT;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, _req: HttpRequest) {
|
||||
context.res = response({
|
||||
context,
|
||||
status: 302,
|
||||
|
@ -39,3 +40,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
const { response, validateCookie, getProviderFromCookie } = require("../../utils");
|
||||
const { currentUser } = require("../../userManager");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response, validateCookie } from "../../utils";
|
||||
import { currentUser } from "../../userManager";
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const { cookie } = req.headers;
|
||||
|
||||
if (!cookie || !validateCookie(cookie)) {
|
||||
|
@ -27,3 +28,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
const { response, ɵɵUseGithubDevToken } = require("../../utils");
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const { provider } = context.bindingData;
|
||||
const { redirect_uri, state, client_id } = req.query;
|
||||
|
||||
console.log("+++++++++");
|
||||
console.log(req.query);
|
||||
console.log("+++++++++");
|
||||
|
||||
location = `${redirect_uri}?code=CODE&state=${state}`;
|
||||
|
||||
context.res = response({
|
||||
context,
|
||||
status: 302,
|
||||
headers: {
|
||||
location,
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const { redirect_uri, state } = req.query;
|
||||
|
||||
console.log("+++++++++");
|
||||
console.log(req.query);
|
||||
console.log("+++++++++");
|
||||
|
||||
const location = `${redirect_uri}?code=CODE&state=${state}`;
|
||||
|
||||
context.res = response({
|
||||
context,
|
||||
status: 302,
|
||||
headers: {
|
||||
location,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
const { response } = require("../../utils");
|
||||
const jwt = require("jsonwebtoken");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
import jwt from "jsonwebtoken";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
const { currentUser } = require("../../userManager");
|
||||
import { currentUser } from "../../userManager";
|
||||
|
||||
const jwtKey = "123";
|
||||
const jwtExpirySeconds = 300;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const { cookie } = req.headers;
|
||||
const payload = {
|
||||
...currentUser(cookie),
|
||||
|
@ -46,3 +47,5 @@ module.exports = async function (context, req) {
|
|||
<script>f.submit();</script>`,
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
const { response, ɵɵUseGithubDevToken } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response, ɵɵUseGithubDevToken } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const { provider } = context.bindingData;
|
||||
const { post_login_redirect_uri } = req.query;
|
||||
|
||||
|
@ -52,3 +53,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
let { state, code, nonce } = req.query;
|
||||
|
||||
const httpTrigger: AzureFunction = async function (context, _req: HttpRequest) {
|
||||
const location = `${SWA_EMU_AUTH_URI}/.auth/login/done`;
|
||||
context.res = response({
|
||||
context,
|
||||
|
@ -35,3 +34,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, _req: HttpRequest) {
|
||||
context.res = response({
|
||||
context,
|
||||
status: 302,
|
||||
|
@ -20,3 +21,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, _req: HttpRequest) {
|
||||
const location = `${SWA_EMU_AUTH_URI}/app/.auth/logout/complete`;
|
||||
|
||||
context.res = response({
|
||||
|
@ -23,3 +24,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const { hostName, post_logout_redirect_uri = "/" } = req.query;
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const { post_logout_redirect_uri = "/" } = req.query;
|
||||
|
||||
const location = `${SWA_EMU_AUTH_URI}/.auth/logout?post_login_redirect_uri=${post_logout_redirect_uri}`;
|
||||
|
||||
|
@ -25,3 +26,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -15,5 +15,6 @@
|
|||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
],
|
||||
"scriptFile": "./index.js"
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
const { response } = require("../../utils");
|
||||
import { AzureFunction, HttpRequest } from "@azure/functions";
|
||||
import { response } from "../../utils";
|
||||
const SWA_EMU_AUTH_URI = process.env.SWA_EMU_AUTH_URI || `http://localhost:4242`;
|
||||
|
||||
module.exports = async function (context, req) {
|
||||
const httpTrigger: AzureFunction = async function (context, req: HttpRequest) {
|
||||
const { provider } = context.bindingData;
|
||||
const { hostName, post_login_redirect_uri = "/.auth/login/done" } = req.query;
|
||||
|
||||
|
@ -26,3 +27,5 @@ module.exports = async function (context, req) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default httpTrigger;
|
|
@ -1,15 +1,15 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const shell = require("shelljs");
|
||||
const { readConfigFile } = require("./utils");
|
||||
const { detectRuntime, RuntimeType } = require("./runtimes");
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import shell from "shelljs";
|
||||
import { readConfigFile } from "./utils";
|
||||
import { detectRuntime, RuntimeType } from "./runtimes";
|
||||
|
||||
const exec = (command, options = {}) => shell.exec(command, { async: false, ...options });
|
||||
const exec = (command: string, options = {}) => shell.exec(command, { async: false, ...options });
|
||||
|
||||
// use the concurrently binary provided by this emulator
|
||||
const concurrentlyBin = path.resolve(__dirname, "..", "./node_modules/.bin/concurrently");
|
||||
|
||||
const nodeBuilder = (location, buildCommand, name, colour) => {
|
||||
const nodeBuilder = (location: string, buildCommand: string, name: string, colour: string) => {
|
||||
const appBuildCommand = [
|
||||
"CI=1",
|
||||
concurrentlyBin,
|
||||
|
@ -24,7 +24,7 @@ const nodeBuilder = (location, buildCommand, name, colour) => {
|
|||
});
|
||||
};
|
||||
|
||||
const dotnetBuilder = (location, name, colour) => {
|
||||
const dotnetBuilder = (location: string, name: string, colour: string) => {
|
||||
const appBuildCommand = [
|
||||
"CI=1",
|
||||
concurrentlyBin,
|
||||
|
@ -39,7 +39,7 @@ const dotnetBuilder = (location, name, colour) => {
|
|||
});
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
const builder = () => {
|
||||
const { app_location, api_location, app_build_command, api_build_command } = readConfigFile();
|
||||
const runtimeType = detectRuntime(app_location);
|
||||
|
||||
|
@ -84,3 +84,4 @@ module.exports = () => {
|
|||
console.error(stderr);
|
||||
}
|
||||
};
|
||||
export default builder;
|
|
@ -1,14 +1,15 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const shell = require("shelljs");
|
||||
const path = require("path");
|
||||
const program = require("commander");
|
||||
const builder = require("../src/builder");
|
||||
const { readConfigFile } = require("../src/utils");
|
||||
const { spawn } = require("child_process");
|
||||
const { createRuntimeHost } = require("../src/runtimeHost");
|
||||
import shell from "shelljs";
|
||||
import path from "path";
|
||||
import program from "commander";
|
||||
import builder from "./builder";
|
||||
import { readConfigFile } from "./utils";
|
||||
import { spawn } from "child_process";
|
||||
import { createRuntimeHost } from "./runtimeHost";
|
||||
import { dashboard } from "./dashboard";
|
||||
|
||||
const EMU_PORT = 80;
|
||||
const EMU_PORT = "80";
|
||||
const AUTH_PORT = 4242;
|
||||
const API_PORT = 7071;
|
||||
const APP_PORT = 4200;
|
||||
|
@ -21,8 +22,8 @@ program
|
|||
.option("--api-uri <apiUri>", "set API uri", `http://localhost:${API_PORT}`)
|
||||
.option("--api-prefix <apiPrefix>", "set API prefix", "api")
|
||||
.option("--app-uri <appUri>", "set APP uri", `http://localhost:${APP_PORT}`)
|
||||
.option("--use-api <useApi>", "Use running API dev server", null)
|
||||
.option("--use-app <useApp>", "Use running APP dev server", null)
|
||||
.option("--use-api <useApi>", "Use running API dev server", undefined)
|
||||
.option("--use-app <useApp>", "Use running APP dev server", undefined)
|
||||
.option("--host <host>", "set emulator host address", "0.0.0.0")
|
||||
.option("--port <port>", "set emulator port value", EMU_PORT)
|
||||
.option("--build", "build the API and APP before starting the emulator", false)
|
||||
|
@ -47,7 +48,7 @@ const { app_artifact_location, api_location } = readConfigFile();
|
|||
|
||||
const envVarsObj = {
|
||||
// set env vars for current command
|
||||
StaticWebAppsAuthCookie: 123,
|
||||
StaticWebAppsAuthCookie: "123",
|
||||
StaticWebAppsAuthContextCookie: "abc",
|
||||
AppServiceAuthSession: "1a2b3c",
|
||||
DEBUG: program.debug ? "*" : "",
|
||||
|
@ -84,10 +85,10 @@ const startCommand = [
|
|||
`-c 'bgYellow.bold,bgMagenta.bold,bgCyan.bold,bgGreen.bold'`,
|
||||
|
||||
// start the reverse proxy
|
||||
`"node ./src/proxy.js"`,
|
||||
`"node ./dist/proxy.js"`,
|
||||
|
||||
// emulate auth
|
||||
`"(cd ./src/auth/; func start --cors=* --port=${authUriPort})"`,
|
||||
`"(cd ./dist/auth/; func start --cors=* --port=${authUriPort})"`,
|
||||
|
||||
// serve the app
|
||||
`"${serveStaticContent}"`,
|
||||
|
@ -110,8 +111,7 @@ if (program.build) {
|
|||
if (program.ui) {
|
||||
// print the dashboard UI
|
||||
|
||||
const { dashboard } = require("../src/dashboard");
|
||||
const spawnx = (command, args) =>
|
||||
const spawnx = (command: string, args: string[]) =>
|
||||
spawn(`${command}`, args, {
|
||||
shell: true,
|
||||
env: { ...process.env, ...envVarsObj },
|
||||
|
@ -127,11 +127,11 @@ if (program.ui) {
|
|||
dashboard.stream("functions", functions);
|
||||
|
||||
// start auth
|
||||
const auth = spawnx(`(cd ./src/auth/; func start --cors=* --port=${authUriPort})`, []);
|
||||
const auth = spawnx(`(cd ./dist/auth/; func start --cors=* --port=${authUriPort})`, []);
|
||||
dashboard.stream("auth", auth);
|
||||
|
||||
// start proxy
|
||||
const status = spawnx(`node`, [`./src/proxy`]);
|
||||
const status = spawnx(`node`, [`./dist/proxy`]);
|
||||
dashboard.stream("status", status);
|
||||
|
||||
process.on("exit", () => {
|
||||
|
@ -146,7 +146,7 @@ if (program.ui) {
|
|||
cwd: path.resolve(__dirname, ".."),
|
||||
env: { ...process.env, ...envVarsObj },
|
||||
},
|
||||
(code, stdout, stderr) => {
|
||||
(_code, _stdout, stderr) => {
|
||||
if (stderr.length) {
|
||||
console.error(stderr);
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
// @ts-check
|
||||
|
||||
const blessed = require("blessed");
|
||||
|
||||
module.exports.dashboard = new (class Dashboard {
|
||||
logText;
|
||||
log;
|
||||
|
||||
constructor() {
|
||||
this.screen = blessed.screen({
|
||||
smartCSR: true,
|
||||
dockBorders: false,
|
||||
fullUnicode: true,
|
||||
autoPadding: true,
|
||||
|
||||
});
|
||||
this.screen.title = "Azure Static Web Apps Emulator";
|
||||
this.screen.key(["escape", "q", "C-c"], () => {
|
||||
process.kill(process.pid, "SIGINT");
|
||||
});
|
||||
|
||||
this.hosting = this.addLogSection({ label: "Hosting" });
|
||||
this.mapNavKeys({ logWidget: this.hosting });
|
||||
|
||||
this.functions = this.addLogSection({ top: "30%", label: "Functions" });
|
||||
this.mapNavKeys({ logWidget: this.functions });
|
||||
|
||||
this.auth = this.addLogSection({ top: "58%", label: "Auth" });
|
||||
this.mapNavKeys({ logWidget: this.auth });
|
||||
|
||||
this.status = this.addLogSection({ top: "90%", height: "15%", label: "Status" });
|
||||
this.mapNavKeys({ logWidget: this.status });
|
||||
|
||||
this.screen.render();
|
||||
}
|
||||
|
||||
addLogSection({ label, top = "0%", height = "30%" }) {
|
||||
let logBox = blessed.log({
|
||||
label,
|
||||
padding: 1,
|
||||
width: "100%",
|
||||
height,
|
||||
left: "0%",
|
||||
top,
|
||||
border: {
|
||||
type: "line",
|
||||
},
|
||||
clickable: true,
|
||||
focus: {
|
||||
border: {
|
||||
fg: 'green'
|
||||
}
|
||||
},
|
||||
mouse: true,
|
||||
});
|
||||
|
||||
this.screen.append(logBox);
|
||||
const logWidget = blessed.log({ parent: logBox, tags: true, width: "100%-5" });
|
||||
return logWidget;
|
||||
}
|
||||
|
||||
mapNavKeys({ logWidget }) {
|
||||
this.screen.key(["up"], () => {
|
||||
logWidget.scroll(-1);
|
||||
logWidget.screen.render();
|
||||
});
|
||||
this.screen.key(["down"], () => {
|
||||
logWidget.scroll(1);
|
||||
logWidget.screen.render();
|
||||
});
|
||||
}
|
||||
|
||||
stream(type, proc) {
|
||||
if (proc) {
|
||||
proc.stdout.on("data", (data) => this[type].log(data.toString("utf8")));
|
||||
proc.stderr.on("data", (data) => this[type].log(data.toString("utf8")));
|
||||
|
||||
process.on("exit", () => {
|
||||
process.kill(proc.pid);
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,116 @@
|
|||
import { ChildProcessWithoutNullStreams } from "child_process";
|
||||
import blessed from "blessed";
|
||||
|
||||
export const dashboard = new (class Dashboard {
|
||||
#screen: blessed.Widgets.Screen;
|
||||
#hosting: blessed.Widgets.Log;
|
||||
#functions: blessed.Widgets.Log;
|
||||
#auth: blessed.Widgets.Log;
|
||||
#status: blessed.Widgets.Log;
|
||||
|
||||
constructor() {
|
||||
this.#screen = blessed.screen({
|
||||
smartCSR: true,
|
||||
dockBorders: false,
|
||||
fullUnicode: true,
|
||||
autoPadding: true,
|
||||
});
|
||||
this.#screen.title = "Azure Static Web Apps Emulator";
|
||||
this.#screen.key(["escape", "q", "C-c"], () => {
|
||||
process.kill(process.pid, "SIGINT");
|
||||
});
|
||||
|
||||
this.#hosting = this.addLogSection({ label: "Hosting" });
|
||||
this.mapNavKeys({ logWidget: this.#hosting });
|
||||
|
||||
this.#functions = this.addLogSection({ top: "30%", label: "Functions" });
|
||||
this.mapNavKeys({ logWidget: this.#functions });
|
||||
|
||||
this.#auth = this.addLogSection({ top: "58%", label: "Auth" });
|
||||
this.mapNavKeys({ logWidget: this.#auth });
|
||||
|
||||
this.#status = this.addLogSection({ top: "90%", height: "15%", label: "Status" });
|
||||
this.mapNavKeys({ logWidget: this.#status });
|
||||
|
||||
this.#screen.render();
|
||||
}
|
||||
|
||||
addLogSection({ label = "", top = "0%", height = "30%" }) {
|
||||
let logBox = blessed.log({
|
||||
label,
|
||||
padding: 1,
|
||||
width: "100%",
|
||||
height,
|
||||
left: "0%",
|
||||
top,
|
||||
border: {
|
||||
type: "line",
|
||||
},
|
||||
clickable: true,
|
||||
focus: {
|
||||
border: {
|
||||
fg: "green",
|
||||
},
|
||||
},
|
||||
mouse: true,
|
||||
});
|
||||
|
||||
this.#screen.append(logBox);
|
||||
const logWidget = blessed.log({ parent: logBox, tags: true, width: "100%-5" });
|
||||
return logWidget;
|
||||
}
|
||||
|
||||
mapNavKeys({ logWidget }: { logWidget: blessed.Widgets.Log }) {
|
||||
this.#screen.key(["up"], () => {
|
||||
logWidget.scroll(-1);
|
||||
logWidget.screen.render();
|
||||
});
|
||||
this.#screen.key(["down"], () => {
|
||||
logWidget.scroll(1);
|
||||
logWidget.screen.render();
|
||||
});
|
||||
}
|
||||
|
||||
stream(type: "hosting" | "functions" | "auth" | "status", proc: ChildProcessWithoutNullStreams) {
|
||||
if (proc) {
|
||||
proc.stdout.on("data", (data) => {
|
||||
const msg = data.toString("utf8");
|
||||
switch (type) {
|
||||
case "auth":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
case "functions":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
case "hosting":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
case "status":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
}
|
||||
});
|
||||
proc.stderr.on("data", (data) => {
|
||||
const msg = data.toString("utf8");
|
||||
switch (type) {
|
||||
case "auth":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
case "functions":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
case "hosting":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
case "status":
|
||||
this.#auth.log(msg);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
process.on("exit", () => {
|
||||
process.kill(proc.pid);
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,14 +1,21 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const http = require("http");
|
||||
const httpProxy = require("http-proxy");
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import http from "http";
|
||||
import httpProxy from "http-proxy";
|
||||
const proxyApp = httpProxy.createProxyServer({ autoRewrite: true });
|
||||
const proxyApi = httpProxy.createProxyServer({ autoRewrite: true });
|
||||
const proxyAuth = httpProxy.createProxyServer({ autoRewrite: false });
|
||||
const { validateCookie } = require("./utils");
|
||||
const { currentUser } = require("./userManager");
|
||||
import { validateCookie } from "./utils";
|
||||
import { currentUser } from "./userManager";
|
||||
|
||||
const serveStatic = (file, res) => {
|
||||
type UserDefinedRoute = {
|
||||
route: string;
|
||||
allowedRoles?: string[];
|
||||
statusCode?: number;
|
||||
serve?: string;
|
||||
};
|
||||
|
||||
const serveStatic = (file: string, res: http.ServerResponse) => {
|
||||
fs.readFile(file, (err, data) => {
|
||||
if (err) {
|
||||
res.writeHead(404);
|
||||
|
@ -21,7 +28,7 @@ const serveStatic = (file, res) => {
|
|||
});
|
||||
};
|
||||
|
||||
const readRoutes = (folder) => {
|
||||
const readRoutes = (folder: string): UserDefinedRoute[] => {
|
||||
if (!fs.existsSync(folder)) {
|
||||
return [];
|
||||
}
|
||||
|
@ -35,9 +42,9 @@ const readRoutes = (folder) => {
|
|||
return require(path.join(folder, routesFile)).routes || [];
|
||||
};
|
||||
|
||||
const routes = readRoutes(process.env.SWA_EMU_APP_LOCATION);
|
||||
const routes = readRoutes(process.env.SWA_EMU_APP_LOCATION || "");
|
||||
|
||||
const routeTest = (userDefinedRoute, currentRoute) => {
|
||||
const routeTest = (userDefinedRoute: string, currentRoute: string) => {
|
||||
if (userDefinedRoute === currentRoute) {
|
||||
return true;
|
||||
}
|
||||
|
@ -50,7 +57,12 @@ const routeTest = (userDefinedRoute, currentRoute) => {
|
|||
};
|
||||
|
||||
const server = http.createServer(function (req, res) {
|
||||
const userDefinedRoute = routes.find((route) => req.url.endsWith(route.route));
|
||||
// not quite sure how you'd hit an undefined url, but the types say you can
|
||||
if (!req.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userDefinedRoute = routes.find((route) => req.url!.endsWith(route.route));
|
||||
|
||||
// something from the `routes.json`
|
||||
if (userDefinedRoute && routeTest(userDefinedRoute.route, req.url)) {
|
||||
|
@ -67,7 +79,7 @@ const server = http.createServer(function (req, res) {
|
|||
// Wanting a specific status code but no attached route
|
||||
else if (userDefinedRoute.statusCode && !userDefinedRoute.serve) {
|
||||
if (userDefinedRoute.statusCode === 404) {
|
||||
const file404 = path.resolve(__dirname, "404.html");
|
||||
const file404 = path.resolve(__dirname, "..", "mock-pages", "404.html");
|
||||
serveStatic(file404, res);
|
||||
return;
|
||||
} else {
|
||||
|
@ -94,7 +106,7 @@ const server = http.createServer(function (req, res) {
|
|||
});
|
||||
proxyAuth.on("proxyRes", function (proxyRes, req) {
|
||||
console.log("auth>>", req.method, target + req.url);
|
||||
console.log(JSON.stringify(proxyRes.headers, true, 2));
|
||||
console.log(JSON.stringify(proxyRes.headers, undefined, 2));
|
||||
});
|
||||
proxyAuth.on("error", function (err, req) {
|
||||
console.log("auth>>", req.method, target + req.url);
|
||||
|
@ -148,6 +160,7 @@ const server = http.createServer(function (req, res) {
|
|||
|
||||
proxyApp.web(req, res, {
|
||||
target,
|
||||
secure: false,
|
||||
});
|
||||
|
||||
proxyApp.on("error", function (err, req, res) {
|
||||
|
@ -161,6 +174,8 @@ const server = http.createServer(function (req, res) {
|
|||
}
|
||||
});
|
||||
|
||||
const address = `${process.env.SWA_EMU_HOST || "0.0.0.0"}:${process.env.SWA_EMU_PORT || 80}`;
|
||||
const port = parseInt(process.env.SWA_EMU_PORT || "", 10) || 80;
|
||||
const host = process.env.SWA_EMU_HOST || "0.0.0.0";
|
||||
const address = `${host}:${port}`;
|
||||
console.log(`>> SWA listening on ${address}`);
|
||||
server.listen(process.env.SWA_EMU_PORT || 80, process.env.SWA_EMU_HOST || "0.0.0.0");
|
||||
server.listen(port, host);
|
|
@ -1,10 +1,10 @@
|
|||
const path = require("path");
|
||||
const { readConfigFile } = require("./utils");
|
||||
const { detectRuntime, RuntimeType } = require("./runtimes");
|
||||
import path from "path";
|
||||
import { readConfigFile } from "./utils";
|
||||
import { detectRuntime, RuntimeType } from "./runtimes";
|
||||
|
||||
const httpServerBin = path.resolve(__dirname, "..", "./node_modules/.bin/http-server");
|
||||
|
||||
module.exports.createRuntimeHost = (port, proxyHost, proxyPort) => {
|
||||
export const createRuntimeHost = (port: number, proxyHost: string, proxyPort: number) => {
|
||||
const { app_location, app_artifact_location } = readConfigFile();
|
||||
const runtimeType = detectRuntime(app_location);
|
||||
|
|
@ -1,16 +1,13 @@
|
|||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
const RuntimeType = {
|
||||
dotnet: "dotnet",
|
||||
node: "node",
|
||||
unknown: "unknown",
|
||||
export enum RuntimeType {
|
||||
dotnet = "dotnet",
|
||||
node ="node",
|
||||
unknown = "unknown",
|
||||
};
|
||||
|
||||
module.exports.RuntimeType = RuntimeType;
|
||||
|
||||
module.exports.detectRuntime = (app_location) => {
|
||||
|
||||
export const detectRuntime = (app_location: string) => {
|
||||
if (fs.existsSync(app_location) === false) {
|
||||
console.error(`The provided "app_location" was not found. Can't detect runtime!`);
|
||||
console.error(app_location);
|
|
@ -1,7 +0,0 @@
|
|||
const users = require("./users.json");
|
||||
const { getProviderFromCookie } = require("./utils");
|
||||
|
||||
module.exports.currentUser = (cookie) => {
|
||||
const provider = getProviderFromCookie(cookie);
|
||||
return users[provider] || null;
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
import { getProviderFromCookie, SwaProviders } from "./utils";
|
||||
|
||||
type MockUser = {
|
||||
identityProvider: string;
|
||||
userId: string;
|
||||
userDetails: string;
|
||||
userRoles: string[];
|
||||
};
|
||||
|
||||
type MockUsers = {
|
||||
[key in SwaProviders]: MockUser;
|
||||
};
|
||||
const users: MockUsers = require("./users.json");
|
||||
|
||||
export const currentUser = (cookie?: string) => {
|
||||
const provider = getProviderFromCookie(cookie);
|
||||
return users[provider] || null;
|
||||
};
|
|
@ -1,27 +1,12 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { Volume } = require("memfs");
|
||||
const shell = require("shelljs");
|
||||
import mockFs from "mock-fs";
|
||||
import path from "path";
|
||||
import shell from "shelljs";
|
||||
|
||||
const { response, validateCookie, getProviderFromCookie, readConfigFile } = require("./utils");
|
||||
|
||||
// mock fs
|
||||
jest.mock(`fs`, () => {
|
||||
const fs = jest.requireActual(`fs`);
|
||||
const unionfs = require(`unionfs`).default;
|
||||
unionfs.reset = () => {
|
||||
unionfs.fss = [fs];
|
||||
};
|
||||
return unionfs.use(fs);
|
||||
});
|
||||
import { response, validateCookie, getProviderFromCookie, readConfigFile } from "./utils";
|
||||
|
||||
describe("Utils", () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.DEBUG;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.reset();
|
||||
process.env.DEBUG = "";
|
||||
});
|
||||
|
||||
describe("response()", () => {
|
||||
|
@ -312,7 +297,7 @@ describe("Utils", () => {
|
|||
},
|
||||
});
|
||||
expect(res.cookies).toBeDefined();
|
||||
expect(res.cookies.foo).toBe("bar");
|
||||
expect(res.cookies!.foo).toBe("bar");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -419,12 +404,12 @@ describe("Utils", () => {
|
|||
|
||||
describe("readConfigFile()", () => {
|
||||
it("config file not found should throw", () => {
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(() => {});
|
||||
jest.spyOn(shell, "exit").mockImplementation(((_) => {}) as (code?: number | undefined) => never);
|
||||
expect(() => readConfigFile()).toThrow(/TypeError: GitHub action file content should be a string/);
|
||||
});
|
||||
|
||||
it("config file not found should process.exit(0)", () => {
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(() => {});
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(((_) => {}) as (code?: number | undefined) => never);
|
||||
|
||||
// we know this will throw. Check previous test
|
||||
try {
|
||||
|
@ -435,149 +420,122 @@ describe("Utils", () => {
|
|||
});
|
||||
|
||||
it("config file with wrong filename should process.exit(0)", () => {
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(() => {});
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"wrong-file-name-pattern.yml": "",
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(((_) => {}) as (code?: number | undefined) => never);
|
||||
|
||||
mockFs({
|
||||
".github/workflows/wrong-file-name-pattern.yml": "",
|
||||
});
|
||||
|
||||
expect(() => readConfigFile()).toThrow(/TypeError: GitHub action file content should be a string/);
|
||||
expect(mockExit).toHaveBeenCalledWith(0);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("invalid YAML file should throw", () => {
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(() => {});
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps__not-valid.yml": "",
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(((_) => {}) as (code?: number | undefined) => never);
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps__not-valid.yml": "",
|
||||
});
|
||||
|
||||
expect(() => readConfigFile()).toThrow(/could not parse the SWA workflow file/);
|
||||
expect(mockExit).toHaveBeenCalledWith(0);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
describe("checking workflow properties", () => {
|
||||
it("missing property 'jobs' should throw", () => {
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(() => {});
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps__not-valid.yml": `name: Azure Static Web Apps CI/CD`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(((_) => {}) as (code?: number | undefined) => never);
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps__not-valid.yml": `name: Azure Static Web Apps CI/CD`,
|
||||
});
|
||||
|
||||
expect(() => readConfigFile()).toThrow(/missing property 'jobs'/);
|
||||
expect(mockExit).toHaveBeenCalledWith(0);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("missing property 'jobs.build_and_deploy_job' should throw", () => {
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(() => {});
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(((_) => {}) as (code?: number | undefined) => never);
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
invalid_property:
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
|
||||
});
|
||||
expect(() => readConfigFile()).toThrow(/missing property 'jobs.build_and_deploy_job'/);
|
||||
expect(mockExit).toHaveBeenCalledWith(0);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("missing property 'jobs.build_and_deploy_job.steps' should throw", () => {
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(() => {});
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
const mockExit = jest.spyOn(shell, "exit").mockImplementation(((_) => {}) as (code?: number | undefined) => never);
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
invalid_property:
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(() => readConfigFile()).toThrow(/missing property 'jobs.build_and_deploy_job.steps'/);
|
||||
expect(mockExit).toHaveBeenCalledWith(0);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("invalid property 'jobs.build_and_deploy_job.steps' should throw", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
|
||||
});
|
||||
expect(() => readConfigFile()).toThrow(/missing property 'jobs.build_and_deploy_job.steps'/);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("invalid property 'jobs.build_and_deploy_job.steps[]' should throw", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
- name: Build And Deploy
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(() => readConfigFile()).toThrow(/invalid property 'jobs.build_and_deploy_job.steps\[\]'/);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("missing property 'jobs.build_and_deploy_job.steps[].with' should throw", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
- name: Build And Deploy
|
||||
uses: Azure/static-web-apps-deploy@v0.0.1-preview
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(() => readConfigFile()).toThrow(/missing property 'jobs.build_and_deploy_job.steps\[\].with'/);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("checking SWA properties", () => {
|
||||
it("property 'app_location' should be set", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -586,19 +544,16 @@ jobs:
|
|||
with:
|
||||
app_location: "/"
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().app_location).toBe(path.normalize(process.cwd() + "/"));
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'app_location' should be set to '/' if missing", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -607,19 +562,16 @@ jobs:
|
|||
with:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().app_location).toBe(path.normalize(process.cwd() + "/"));
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'api_location' should be set", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -628,19 +580,16 @@ jobs:
|
|||
with:
|
||||
api_location: "/api"
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().api_location).toBe(path.normalize(process.cwd() + "/api"));
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'api_location' should be set to 'api' if missing", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -649,19 +598,16 @@ jobs:
|
|||
with:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().api_location).toBe(path.normalize(process.cwd() + "/api"));
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'app_artifact_location' should be set", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -670,19 +616,16 @@ jobs:
|
|||
with:
|
||||
app_artifact_location: "/"
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().app_artifact_location).toBe(path.normalize(process.cwd() + "/"));
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'app_artifact_location' should be set to '/' if missing", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -691,19 +634,16 @@ jobs:
|
|||
with:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().app_artifact_location).toBe(path.normalize(process.cwd() + "/"));
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'app_build_command' should be set", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -712,19 +652,16 @@ jobs:
|
|||
with:
|
||||
app_build_command: "echo test"
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().app_build_command).toBe("echo test");
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'app_build_command' should be set to default if missing", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -733,19 +670,16 @@ jobs:
|
|||
with:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().app_build_command).toBe("npm run build --if-present");
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'api_build_command' should be set", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -754,19 +688,16 @@ jobs:
|
|||
with:
|
||||
api_build_command: "echo test"
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().api_build_command).toBe("echo test");
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("property 'api_build_command' should be set to default if missing", () => {
|
||||
fs.use(
|
||||
Volume.fromJSON(
|
||||
{
|
||||
"azure-static-web-apps.yml": `
|
||||
mockFs({
|
||||
".github/workflows/azure-static-web-apps.yml": `
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
steps:
|
||||
|
@ -775,12 +706,11 @@ jobs:
|
|||
with:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
".github/workflows"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
expect(readConfigFile().api_build_command).toBe("npm run build --if-present");
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,12 +1,16 @@
|
|||
const cookie = require("cookie");
|
||||
const { default: fetch } = require("node-fetch");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const shell = require("shelljs");
|
||||
const YAML = require("yaml");
|
||||
const { detectRuntime, RuntimeType } = require("./runtimes");
|
||||
import cookie from "cookie";
|
||||
import fetch from "node-fetch";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import shell from "shelljs";
|
||||
import YAML from "yaml";
|
||||
import { detectRuntime, RuntimeType } from "./runtimes";
|
||||
|
||||
module.exports.response = ({ context, status, headers, cookies, body = "" }) => {
|
||||
type ResponseOptions = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export const response = ({ context, status, headers, cookies, body = "" }: ResponseOptions) => {
|
||||
if (!context || !context.bindingData) {
|
||||
throw Error(
|
||||
"TypeError: context must be a valid Azure Functions context object. " +
|
||||
|
@ -66,7 +70,7 @@ module.exports.response = ({ context, status, headers, cookies, body = "" }) =>
|
|||
return res;
|
||||
};
|
||||
|
||||
module.exports.validateCookie = (cookieValue) => {
|
||||
export const validateCookie = (cookieValue: any) => {
|
||||
if (typeof cookieValue !== "string") {
|
||||
throw Error("TypeError: cookie value must be a string");
|
||||
}
|
||||
|
@ -80,16 +84,17 @@ module.exports.validateCookie = (cookieValue) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
module.exports.getProviderFromCookie = (cookieValue) => {
|
||||
export type SwaProviders = "aad" | "github" | "twitter" | "facebook" | "google";
|
||||
export const getProviderFromCookie = (cookieValue: any): SwaProviders => {
|
||||
if (typeof cookieValue !== "string") {
|
||||
throw Error("TypeError: cookie value must be a string");
|
||||
}
|
||||
|
||||
const cookies = cookie.parse(cookieValue);
|
||||
return cookies.StaticWebAppsAuthCookie__PROVIDER;
|
||||
return cookies.StaticWebAppsAuthCookie__PROVIDER as SwaProviders;
|
||||
};
|
||||
|
||||
module.exports.ɵɵUseGithubDevToken = async () => {
|
||||
export const ɵɵUseGithubDevToken = async () => {
|
||||
console.log("!!!! Notice: You are using a dev GitHub token. You should create and use your own!");
|
||||
console.log("!!!! Read https://docs.github.com/en/developers/apps/building-oauth-apps");
|
||||
const swaTokens = `https://gist.githubusercontent.com/manekinekko/7fbfc79a85b0f1f312715f1beda26236/raw/740c51aac5b1fb970e69408067a49907485d1e31/swa-emu.json`;
|
||||
|
@ -98,7 +103,7 @@ module.exports.ɵɵUseGithubDevToken = async () => {
|
|||
return token.github;
|
||||
};
|
||||
|
||||
module.exports.readConfigFile = () => {
|
||||
export const readConfigFile = () => {
|
||||
const githubActionFolder = path.resolve(process.cwd(), ".github/workflows/");
|
||||
|
||||
// find the SWA GitHub action file
|
||||
|
@ -110,6 +115,10 @@ module.exports.readConfigFile = () => {
|
|||
.filter((file) => file.includes("azure-static-web-apps") && file.endsWith(".yml"))
|
||||
.pop();
|
||||
|
||||
if (!githubActionFile) {
|
||||
throw Error("No SWA configuration build found. A SWA folder must contain a GitHub workflow file. Read more: https://bit.ly/31RAODu");
|
||||
}
|
||||
|
||||
githubActionFile = path.resolve(githubActionFolder, githubActionFile);
|
||||
|
||||
githubActionContent = fs.readFileSync(githubActionFile, "utf8");
|
||||
|
@ -144,7 +153,8 @@ module.exports.readConfigFile = () => {
|
|||
);
|
||||
}
|
||||
|
||||
const swaBuildConfig = swaYaml.jobs.build_and_deploy_job.steps.find((step) => step.uses && step.uses.includes("static-web-apps-deploy"));
|
||||
// hacking this to have an `any` on the type in .find, mainly because a typescript definition for the YAML file is painful...
|
||||
const swaBuildConfig = swaYaml.jobs.build_and_deploy_job.steps.find((step: any) => step.uses && step.uses.includes("static-web-apps-deploy"));
|
||||
|
||||
if (!swaBuildConfig) {
|
||||
throw Error(
|
||||
|
@ -177,10 +187,11 @@ module.exports.readConfigFile = () => {
|
|||
app_artifact_location = path.normalize(app_artifact_location);
|
||||
|
||||
const detectedRuntimeType = detectRuntime(app_location);
|
||||
if (detectedRuntimeType === RuntimeType.node) {
|
||||
app_artifact_location = path.join(app_location, app_artifact_location);
|
||||
} else {
|
||||
if (detectedRuntimeType === RuntimeType.dotnet) {
|
||||
// TODO: work out what runtime is being used for .NET rather than hard-coded
|
||||
app_artifact_location = path.join(app_location, "bin", "Debug", "netstandard2.1", "publish", app_artifact_location);
|
||||
} else {
|
||||
app_artifact_location = path.join(app_location, app_artifact_location);
|
||||
}
|
||||
|
||||
const config = {
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2019",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["src/**/*.ts", "src/**/*.json", "environment.d.ts"]
|
||||
}
|
Загрузка…
Ссылка в новой задаче