diff --git a/.gitignore b/.gitignore index fb361f80a..bcd8b0059 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /dist /tmp /out-tsc +/tools/executors/**/*.js # dependencies /node_modules diff --git a/apps/explorer/README.md b/apps/explorer/README.md index 42963ce9d..22368e944 100644 --- a/apps/explorer/README.md +++ b/apps/explorer/README.md @@ -32,12 +32,18 @@ yarn nx serve explorer Example configurations are provided here: - [Mainnet](./.env.mainnet) -- [Testnet](./.env.testnet) +- [Devnet](./.env.devnet) - [Capsule](./.env.capsule) - [Testnet](./.env.testnet) - [Stagnet1](./.env.stagnet1) - [Stagnet2](./.env.stagnet2) +For convenience, you can boot the app injecting one of the configurations above by running: + +```bash +yarn nx run explorer:serve --env={env} # e.g. stagnet1 +``` + There are a few different configuration options offered for this app: | **Flag** | **Purpose** | diff --git a/apps/explorer/project.json b/apps/explorer/project.json index cdafb1cf9..a97dc0c4d 100644 --- a/apps/explorer/project.json +++ b/apps/explorer/project.json @@ -38,7 +38,7 @@ } }, "serve": { - "executor": "@nrwl/web:dev-server", + "executor": "./tools/executors/serve:serve", "options": { "port": 3000, "buildTarget": "explorer:build", diff --git a/apps/simple-trading-app/project.json b/apps/simple-trading-app/project.json index 82331f164..3905d0432 100644 --- a/apps/simple-trading-app/project.json +++ b/apps/simple-trading-app/project.json @@ -41,7 +41,7 @@ } }, "serve": { - "executor": "@nrwl/web:dev-server", + "executor": "./tools/executors/serve:serve", "options": { "buildTarget": "simple-trading-app:build", "hmr": true, diff --git a/apps/static/project.json b/apps/static/project.json index d2c834cb9..3e569e6d5 100644 --- a/apps/static/project.json +++ b/apps/static/project.json @@ -36,7 +36,7 @@ } }, "serve": { - "executor": "@nrwl/web:dev-server", + "executor": "./tools/executors/serve:serve", "options": { "buildTarget": "static:build" }, diff --git a/apps/stats/project.json b/apps/stats/project.json index b25a2b727..9287fa439 100644 --- a/apps/stats/project.json +++ b/apps/stats/project.json @@ -37,7 +37,7 @@ } }, "serve": { - "executor": "@nrwl/web:dev-server", + "executor": "./tools/executors/serve:serve", "options": { "port": 3010, "buildTarget": "stats:build", diff --git a/apps/token/README.md b/apps/token/README.md index 2be98295a..ada33d46e 100644 --- a/apps/token/README.md +++ b/apps/token/README.md @@ -16,10 +16,26 @@ Starting the app: -`yarn nx serve token` +```bash +yarn nx serve token +``` ## Configuration +Example configurations are provided here: + +- [Mainnet](./.env.mainnet) +- [Devnet](./.env.devnet) +- [Testnet](./.env.testnet) +- [Stagnet1](./.env.stagnet1) +- [Stagnet2](./.env.stagnet2) + +For convenience, you can boot the app injecting one of the configurations above by running: + +```bash +yarn nx run token:serve --env={env} # e.g. stagnet1 +``` + There are a few different configuration options offered for this app: | **Flag** | **Purpose** | diff --git a/apps/token/project.json b/apps/token/project.json index 0535d474d..cac931f70 100644 --- a/apps/token/project.json +++ b/apps/token/project.json @@ -38,7 +38,7 @@ } }, "serve": { - "executor": "@nrwl/web:dev-server", + "executor": "./tools/executors/serve:serve", "options": { "port": 4210, "buildTarget": "token:build", diff --git a/apps/trading/README.md b/apps/trading/README.md index 5d256bc19..29150e050 100644 --- a/apps/trading/README.md +++ b/apps/trading/README.md @@ -19,7 +19,7 @@ yarn nx serve explorer Example configurations are provided here: - [Mainnet](./.env.mainnet) -- [Testnet](./.env.testnet) +- [Devnet](./.env.devnet) - [Testnet](./.env.testnet) - [Stagnet1](./.env.stagnet1) - [Stagnet2](./.env.stagnet2) diff --git a/package.json b/package.json index 00c5287d6..86ff7b9e9 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "start": "nx serve", "build": "nx build", "test": "nx test", - "postinstall": "husky install" + "postinstall": "husky install && yarn tsc -b tools/executors/**" }, "engines": { "node": ">=16.14.0" @@ -122,6 +122,7 @@ "babel-loader": "8.1.0", "cypress": "^9.5.4", "cypress-cucumber-preprocessor": "^4.3.1", + "dotenv": "^16.0.1", "eslint": "8.12.0", "eslint-config-next": "12.1.2", "eslint-config-prettier": "8.1.0", @@ -138,6 +139,7 @@ "jest-canvas-mock": "^2.3.1", "jest-websocket-mock": "^2.3.0", "lint-staged": "^12.3.3", + "npmlog": "^6.0.2", "nx": "13.10.1", "prettier": "^2.5.1", "react-test-renderer": "17.0.2", diff --git a/tools/executors/serve/executor.json b/tools/executors/serve/executor.json new file mode 100644 index 000000000..26d70afc8 --- /dev/null +++ b/tools/executors/serve/executor.json @@ -0,0 +1,9 @@ +{ + "executors": { + "serve": { + "implementation": "./impl", + "schema": "./schema.json", + "description": "Starts a dev-server with an optional explicit environment." + } + } +} diff --git a/tools/executors/serve/impl.ts b/tools/executors/serve/impl.ts new file mode 100644 index 000000000..72dcf4334 --- /dev/null +++ b/tools/executors/serve/impl.ts @@ -0,0 +1,89 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as dotenv from 'dotenv'; +import * as log from 'npmlog'; +import type { ExecutorContext } from '@nrwl/devkit'; +import devServerExecutor, { + WebDevServerOptions, +} from '@nrwl/web/src/executors/dev-server/dev-server.impl'; + +type Schema = WebDevServerOptions & { + env?: string; +}; + +const LOGGER_SCOPE = 'tools/executors/serve'; + +const logEnvData = ( + envMap: Record, + envFiles: string[], + env?: string, + defaultEnvFile?: string +) => { + if (env && !envMap[env]) { + log.warn(LOGGER_SCOPE, `No environment called "${env}" found.`); + log.info( + LOGGER_SCOPE, + envFiles.length > 0 + ? `You can create a new environment by putting an ".env.${env}" file in your project root, or you can use the following available ones: ${envFiles.join( + ', ' + )}.` + : 'To get started with environments, you can create an ".env" file in your project root with the desired variables.' + ); + } + + if (!envMap[env]) { + log.info( + LOGGER_SCOPE, + defaultEnvFile + ? `Using "${defaultEnvFile}" as the default project environment.` + : 'Serving the project only using the environment variables scoped to your CLI.' + ); + } else { + log.info( + LOGGER_SCOPE, + `Using "${envMap[env]}" as the default project environment.` + ); + } +}; + +const filenameToEnv = (filename: string) => filename.replace('.env.', ''); + +const getDefaultEnvFile = (envMap: Record) => { + return envMap['local'] || envMap['.env']; +}; + +const getEnvFile = (env: string, envFiles: string[]) => { + const envMap = envFiles.reduce( + (acc, filename) => ({ + ...acc, + [filenameToEnv(filename)]: filename, + }), + {} + ); + + const defaultEnvFile = getDefaultEnvFile(envMap); + logEnvData(envMap, envFiles, env, defaultEnvFile); + + return envMap[env] || defaultEnvFile; +}; + +export default async function* serve( + options: Schema, + context: ExecutorContext +): ReturnType { + const { env, ...dsOptions } = options; + const { root } = context.workspace.projects[context.projectName]; + const workspacePath = path.join(context.cwd, root); + + const files = await fs.promises.readdir(workspacePath); + const envFile = getEnvFile( + env, + files.filter((f) => f.startsWith('.env')) + ); + + if (env && envFile) { + dotenv.config({ path: path.join(workspacePath, envFile), override: true }); + } + + return yield* devServerExecutor(dsOptions, context); +} diff --git a/tools/executors/serve/package.json b/tools/executors/serve/package.json new file mode 100644 index 000000000..712554f04 --- /dev/null +++ b/tools/executors/serve/package.json @@ -0,0 +1,3 @@ +{ + "executors": "./executor.json" +} diff --git a/tools/executors/serve/schema.json b/tools/executors/serve/schema.json new file mode 100644 index 000000000..500252bf0 --- /dev/null +++ b/tools/executors/serve/schema.json @@ -0,0 +1,79 @@ +{ + "cli": "nx", + "id": "serve", + "description": "Serves an app using @nrwl/web:dev-server", + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "Target environment to run the application in. This assumes an .env file present in the project's root in the following format: .env.{envName}" + }, + "buildTarget": { + "type": "string", + "description": "Target which builds the application." + }, + "port": { + "type": "number", + "description": "Port to listen on.", + "default": 4200 + }, + "host": { + "type": "string", + "description": "Host to listen on.", + "default": "localhost" + }, + "ssl": { + "type": "boolean", + "description": "Serve using `HTTPS`.", + "default": false + }, + "sslKey": { + "type": "string", + "description": "SSL key to use for serving `HTTPS`." + }, + "sslCert": { + "type": "string", + "description": "SSL certificate to use for serving `HTTPS`." + }, + "watch": { + "type": "boolean", + "description": "Watches for changes and rebuilds application.", + "default": true + }, + "liveReload": { + "type": "boolean", + "description": "Whether to reload the page on change, using live-reload.", + "default": true + }, + "hmr": { + "type": "boolean", + "description": "Enable hot module replacement.", + "default": false + }, + "publicHost": { + "type": "string", + "description": "Public URL where the application will be served." + }, + "open": { + "type": "boolean", + "description": "Open the application in the browser.", + "default": false + }, + "allowedHosts": { + "type": "string", + "description": "This option allows you to whitelist services that are allowed to access the dev server." + }, + "memoryLimit": { + "type": "number", + "description": "Memory limit for type checking service process in `MB`." + }, + "maxWorkers": { + "type": "number", + "description": "Number of workers to use for type checking." + }, + "baseHref": { + "type": "string", + "description": "Base url for the application being built." + } + } +} diff --git a/tools/executors/serve/tsconfig.json b/tools/executors/serve/tsconfig.json new file mode 100644 index 000000000..6d5259d33 --- /dev/null +++ b/tools/executors/serve/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "types": ["node"], + "importHelpers": false, + "sourceMap": false, + "inlineSourceMap": true + }, + "include": ["impl.ts"], + "exclude": ["node_modules"] +} diff --git a/yarn.lock b/yarn.lock index 82cf389cb..1c97bd565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7340,6 +7340,14 @@ are-we-there-yet@^2.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +are-we-there-yet@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz#ba20bd6b553e31d62fc8c31bd23d22b95734390d" + integrity sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + are-we-there-yet@~1.1.2: version "1.1.7" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" @@ -9206,7 +9214,7 @@ color-name@^1.1.4, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.2: +color-support@^1.1.2, color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== @@ -10670,6 +10678,11 @@ dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== +dotenv@^16.0.1: + version "16.0.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d" + integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ== + dotenv@^8.0.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" @@ -12290,6 +12303,20 @@ gauge@^3.0.0: strip-ansi "^6.0.1" wide-align "^1.1.2" +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -16237,6 +16264,16 @@ npmlog@^5.0.1: gauge "^3.0.0" set-blocking "^2.0.0" +npmlog@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" + nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -19314,7 +19351,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -21826,7 +21863,7 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0, wide-align@^1.1.2: +wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==