From bffd4eb75403aa5cdec17b7612e50a0ae5082178 Mon Sep 17 00:00:00 2001 From: Andy Eskridge Date: Wed, 29 Jun 2022 14:12:15 -0500 Subject: [PATCH 1/3] add nx to blues-stack --- nx.json | 31 ++++++++++++ package.json | 19 ++++--- project.json | 121 ++++++++++++++++++++++++++++++++++++++++++++ remix.init/index.js | 29 ++++++++--- 4 files changed, 184 insertions(+), 16 deletions(-) create mode 100644 nx.json create mode 100644 project.json diff --git a/nx.json b/nx.json new file mode 100644 index 00000000..a3cd7ac7 --- /dev/null +++ b/nx.json @@ -0,0 +1,31 @@ +{ + "extends": "nx/presets/core.json", + "npmScope": "remix", + "tasksRunnerOptions": { + "default": { + "runner": "nx/tasks-runners/default", + "options": { + "cacheableOperations": [ + "build-all", + "validate-all", + "build:css", + "build:remix", + "build:server", + "typecheck", + "test:run", + "lint", + "test:e2e:run" + ] + } + } + }, + "cli": { + "defaultProjectName": "blues-stack-template" + }, + "pluginsConfig": { + "@nrwl/js": { + "analyzeSourceFiles": true, + "analyzePackageJson": true + } + } +} diff --git a/package.json b/package.json index 10490442..19f3ec75 100644 --- a/package.json +++ b/package.json @@ -3,28 +3,26 @@ "private": true, "sideEffects": false, "scripts": { - "build": "run-s build:*", - "build:css": "npm run generate:css -- --minify", + "build:css": "tailwindcss -o ./app/styles/tailwind.css --minify", "build:remix": "remix build", "build:server": "esbuild --platform=node --format=cjs ./server.ts --outdir=build --bundle", - "dev": "run-p dev:*", - "dev:server": "cross-env NODE_ENV=development node --inspect --require ./node_modules/dotenv/config --require ./mocks ./build/server.js", - "dev:build": "cross-env NODE_ENV=development npm run build:server -- --watch", + "dev:server": "cross-env NODE_ENV=development nodemon --inspect --require ./node_modules/dotenv/config --require ./mocks ./build/server.js --watch ./build/server.js", + "dev:build": "cross-env NODE_ENV=development esbuild --platform=node --format=cjs ./server.ts --outdir=build --bundle --sourcemap --watch", "dev:remix": "cross-env NODE_ENV=development remix watch", - "dev:css": "cross-env NODE_ENV=development npm run generate:css -- --watch", + "dev:css": "cross-env NODE_ENV=development tailwindcss -o ./app/styles/tailwind.css --watch", "docker": "docker-compose up -d", "format": "prettier --write .", - "generate:css": "tailwindcss -o ./app/styles/tailwind.css", "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .", "setup": "prisma generate && prisma migrate deploy && prisma db seed", "start": "cross-env NODE_ENV=production node ./build/server.js", "start:mocks": "cross-env NODE_ENV=production node --require ./mocks --require dotenv/config ./build/server.js", "test": "vitest", "test:e2e:dev": "start-server-and-test dev http://localhost:3000 \"npx cypress open\"", - "pretest:e2e:run": "npm run build", "test:e2e:run": "cross-env PORT=8811 start-server-and-test start:mocks http://localhost:8811 \"npx cypress run\"", "typecheck": "tsc -b && tsc -b cypress", - "validate": "run-p \"test -- --run\" lint typecheck test:e2e:run" + "build": "nx build-all", + "dev": "nx dev-all", + "validate": "nx validate-all" }, "prettier": {}, "eslintIgnore": [ @@ -74,7 +72,8 @@ "eslint-config-prettier": "^8.5.0", "happy-dom": "^5.2.0", "msw": "^0.42.1", - "npm-run-all": "^4.1.5", + "nodemon": "^2.0.18", + "nx": "^14.3.6", "postcss": "^8.4.14", "prettier": "^2.6.2", "prettier-plugin-tailwindcss": "^0.1.11", diff --git a/project.json b/project.json new file mode 100644 index 00000000..74df4142 --- /dev/null +++ b/project.json @@ -0,0 +1,121 @@ +{ + "name": "blues-stack-template", + "targets": { + "dev-all": { + "executor": "nx:run-commands", + "options": { + "commands": [ + { + "command": "nx dev:server blues-stack-template", + "prefix": "[SERVER]", + "color": "blue" + }, + { + "command": "nx dev:build blues-stack-template", + "prefix": "[BUILD-]", + "color": "green" + }, + { + "command": "nx dev:remix blues-stack-template", + "prefix": "[REMIX-]", + "color": "yellow" + }, + { + "command": "nx dev:css blues-stack-template", + "prefix": "[CSS---]", + "color": "cyan" + } + ], + "__unparsed__": [] + } + }, + "validate-all": { + "executor": "nx:run-commands", + "options": { + "commands": [ + { + "command": "nx test blues-stack-template --run", + "prefix": "[test-----]", + "color": "blue" + }, + { + "command": "nx lint blues-stack-template", + "prefix": "[lint-----]", + "color": "green" + }, + { + "command": "nx typecheck blues-stack-template", + "prefix": "[typecheck]", + "color": "yellow" + }, + { + "command": "nx test:e2e:run blues-stack-template", + "prefix": "[e2e------]", + "color": "cyan" + } + ], + "__unparsed__": [] + } + }, + "build-all": { + "executor": "nx:run-commands", + "options": { + "commands": [ + { + "command": "nx build:server blues-stack-template", + "prefix": "[SERVER]", + "color": "blue" + }, + { + "command": "nx build:remix blues-stack-template", + "prefix": "[REMIX-]", + "color": "yellow" + } + ], + "__unparsed__": [] + } + }, + "build:remix": { + "executor": "nx:run-script", + "options": { + "script": "build:remix", + "__unparsed__": [] + }, + "outputs": ["/build/index.js", "/public/build"], + "dependsOn": ["build:css"] + }, + "dev:build": { + "executor": "nx:run-script", + "options": { + "script": "dev:build", + "__unparsed__": [] + }, + "outputs": ["/build/server.js"] + }, + "build:css": { + "executor": "nx:run-script", + "options": { + "script": "build:css", + "__unparsed__": [] + }, + "outputs": ["/app/styles/tailwind.css"] + }, + "test:e2e:run": { + "executor": "nx:run-script", + "options": { + "script": "test:e2e:run", + "__unparsed__": [] + }, + "outputs": ["/cypress/screenshots", "/cypress/videos"], + "dependsOn": ["build-all"] + }, + "test:e2e:dev": { + "executor": "nx:run-script", + "options": { + "script": "test:e2e:dev", + "__unparsed__": [] + }, + "dependsOn": ["build-all"] + } + } +} diff --git a/remix.init/index.js b/remix.init/index.js index 56f718f0..a76f205f 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -20,6 +20,8 @@ async function main({ rootDirectory }) { const EXAMPLE_ENV_PATH = path.join(rootDirectory, ".env.example"); const ENV_PATH = path.join(rootDirectory, ".env"); const PACKAGE_JSON_PATH = path.join(rootDirectory, "package.json"); + const PROJECT_JSON_PATH = path.join(rootDirectory, "project.json"); + const NX_JSON_PATH = path.join(rootDirectory, "nx.json"); const REPLACER = "blues-stack-template"; @@ -30,12 +32,15 @@ async function main({ rootDirectory }) { // get rid of anything that's not allowed in an app name .replace(/[^a-zA-Z0-9-_]/g, "-"); - const [prodContent, readme, env, packageJson] = await Promise.all([ - fs.readFile(FLY_TOML_PATH, "utf-8"), - fs.readFile(README_PATH, "utf-8"), - fs.readFile(EXAMPLE_ENV_PATH, "utf-8"), - fs.readFile(PACKAGE_JSON_PATH, "utf-8"), - ]); + const [prodContent, readme, env, packageJson, projectJson, nxJson] = + await Promise.all([ + fs.readFile(FLY_TOML_PATH, "utf-8"), + fs.readFile(README_PATH, "utf-8"), + fs.readFile(EXAMPLE_ENV_PATH, "utf-8"), + fs.readFile(PACKAGE_JSON_PATH, "utf-8"), + fs.readFile(PROJECT_JSON_PATH, "utf-8"), + fs.readFile(NX_JSON_PATH, "utf-8"), + ]); const newEnv = env.replace( /^SESSION_SECRET=.*$/m, @@ -57,11 +62,23 @@ async function main({ rootDirectory }) { 2 ) + "\n"; + const newProjectJson = projectJson.replace( + new RegExp(escapeRegExp(REPLACER), "g"), + APP_NAME + ); + + const newNxJson = nxJson.replace( + new RegExp(escapeRegExp(REPLACER), "g"), + APP_NAME + ); + await Promise.all([ fs.writeFile(FLY_TOML_PATH, toml.stringify(prodToml)), fs.writeFile(README_PATH, newReadme), fs.writeFile(ENV_PATH, newEnv), fs.writeFile(PACKAGE_JSON_PATH, newPackageJson), + fs.writeFile(PROJECT_JSON_PATH, newProjectJson), + fs.writeFile(NX_JSON_PATH, newNxJson), fs.copyFile( path.join(rootDirectory, "remix.init", "gitignore"), path.join(rootDirectory, ".gitignore") From 98154eaa9cd4cd26fb5425ce37f60430b000426b Mon Sep 17 00:00:00 2001 From: Andy Eskridge Date: Wed, 29 Jun 2022 14:18:18 -0500 Subject: [PATCH 2/3] switch to @vsavkin's implementation of remix init and remove the package.json --- remix.init/index.js | 112 ++++++++++++++-------------------------- remix.init/package.json | 10 ---- 2 files changed, 39 insertions(+), 83 deletions(-) delete mode 100644 remix.init/package.json diff --git a/remix.init/index.js b/remix.init/index.js index a76f205f..b78bd8fc 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -2,92 +2,58 @@ const crypto = require("crypto"); const fs = require("fs/promises"); const path = require("path"); -const toml = require("@iarna/toml"); -const sort = require("sort-package-json"); - -function escapeRegExp(string) { - // $& means the whole matched string - return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} - function getRandomString(length) { return crypto.randomBytes(length).toString("hex"); } async function main({ rootDirectory }) { - const README_PATH = path.join(rootDirectory, "README.md"); - const FLY_TOML_PATH = path.join(rootDirectory, "fly.toml"); - const EXAMPLE_ENV_PATH = path.join(rootDirectory, ".env.example"); - const ENV_PATH = path.join(rootDirectory, ".env"); - const PACKAGE_JSON_PATH = path.join(rootDirectory, "package.json"); - const PROJECT_JSON_PATH = path.join(rootDirectory, "project.json"); - const NX_JSON_PATH = path.join(rootDirectory, "nx.json"); - - const REPLACER = "blues-stack-template"; - - const DIR_NAME = path.basename(rootDirectory); - const SUFFIX = getRandomString(2); - - const APP_NAME = (DIR_NAME + "-" + SUFFIX) + const APP_NAME = path.basename(rootDirectory); + const APP_ID = (APP_NAME + "-" + getRandomString(2)) // get rid of anything that's not allowed in an app name .replace(/[^a-zA-Z0-9-_]/g, "-"); - const [prodContent, readme, env, packageJson, projectJson, nxJson] = - await Promise.all([ - fs.readFile(FLY_TOML_PATH, "utf-8"), - fs.readFile(README_PATH, "utf-8"), - fs.readFile(EXAMPLE_ENV_PATH, "utf-8"), - fs.readFile(PACKAGE_JSON_PATH, "utf-8"), - fs.readFile(PROJECT_JSON_PATH, "utf-8"), - fs.readFile(NX_JSON_PATH, "utf-8"), - ]); + // copy files + const filesToCopy = [["remix.init/gitignore", ".gitignore"]]; + for (const [from, to] of filesToCopy) { + await fs.copyFile( + path.join(rootDirectory, from), + path.join(rootDirectory, to) + ); + } + // update env to have SESSION_SECRET + const EXAMPLE_ENV_PATH = path.join(rootDirectory, ".env.example"); + const ENV_PATH = path.join(rootDirectory, ".env"); + const env = await fs.readFile(EXAMPLE_ENV_PATH, "utf-8"); const newEnv = env.replace( /^SESSION_SECRET=.*$/m, `SESSION_SECRET="${getRandomString(16)}"` ); - - const prodToml = toml.parse(prodContent); - prodToml.app = prodToml.app.replace(REPLACER, APP_NAME); - - const newReadme = readme.replace( - new RegExp(escapeRegExp(REPLACER), "g"), - APP_NAME - ); - - const newPackageJson = - JSON.stringify( - sort({ ...JSON.parse(packageJson), name: APP_NAME }), - null, - 2 - ) + "\n"; - - const newProjectJson = projectJson.replace( - new RegExp(escapeRegExp(REPLACER), "g"), - APP_NAME - ); - - const newNxJson = nxJson.replace( - new RegExp(escapeRegExp(REPLACER), "g"), - APP_NAME - ); - - await Promise.all([ - fs.writeFile(FLY_TOML_PATH, toml.stringify(prodToml)), - fs.writeFile(README_PATH, newReadme), - fs.writeFile(ENV_PATH, newEnv), - fs.writeFile(PACKAGE_JSON_PATH, newPackageJson), - fs.writeFile(PROJECT_JSON_PATH, newProjectJson), - fs.writeFile(NX_JSON_PATH, newNxJson), - fs.copyFile( - path.join(rootDirectory, "remix.init", "gitignore"), - path.join(rootDirectory, ".gitignore") - ), - fs.rm(path.join(rootDirectory, ".github/ISSUE_TEMPLATE"), { - recursive: true, - }), - fs.rm(path.join(rootDirectory, ".github/PULL_REQUEST_TEMPLATE.md")), - ]); + await fs.writeFile(ENV_PATH, newEnv); + + // delete files only needed for the template + const filesToDelete = [ + ".github/ISSUE_TEMPLATE", + ".github/PULL_REQUEST_TEMPLATE.md", + ]; + for (const file of filesToDelete) { + await fs.rm(path.join(rootDirectory, file), { recursive: true }); + } + + // replace "blues-stack-template" in all files with the app name + const filesToReplaceIn = [ + "README.md", + "package.json", + "fly.toml", + "project.json", + "nx.json", + ]; + for (const file of filesToReplaceIn) { + const filePath = path.join(rootDirectory, file); + const contents = await fs.readFile(filePath, "utf-8"); + const newContents = contents.replace(/blues-stack-template/g, APP_ID); + await fs.writeFile(filePath, newContents); + } console.log( ` diff --git a/remix.init/package.json b/remix.init/package.json deleted file mode 100644 index 933cc1cd..00000000 --- a/remix.init/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "remix.init", - "private": true, - "main": "index.js", - "license": "MIT", - "dependencies": { - "@iarna/toml": "^2.2.5", - "sort-package-json": "^1.57.0" - } -} From 058cb0c2da67fa99b61980c1f77afa3e14036829 Mon Sep 17 00:00:00 2001 From: Andy Eskridge Date: Wed, 29 Jun 2022 14:27:55 -0500 Subject: [PATCH 3/3] update workflow to work around nx issue https://github.com/nrwl/nx/issues/10244 --- .github/workflows/deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a13c38d7..9c6710a7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -102,6 +102,9 @@ jobs: with: useLockFile: false + - name: ⚙️ Build + run: npm run build + - name: 🐳 Docker compose # the sleep is just there to give time for postgres to get started run: docker-compose up -d && sleep 3 @@ -111,9 +114,6 @@ jobs: - name: 🛠 Setup Database run: npx prisma migrate reset --force - - name: ⚙️ Build - run: npm run build - - name: 🌳 Cypress run uses: cypress-io/github-action@v4 with: