From 3ffcae2a7a5e343c0b07c03bd5c29170800af358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Wed, 6 Sep 2023 22:03:48 +0200 Subject: [PATCH] feat: upgrade to Remix v2 --- app/routes/_index.tsx | 4 +- app/routes/join.tsx | 4 +- app/routes/login.tsx | 4 +- cypress/tsconfig.json | 4 +- package.json | 14 ++--- remix.config.js | 13 +---- remix.init/index.js | 116 ++++++---------------------------------- remix.init/package.json | 5 +- tsconfig.json | 6 +-- 9 files changed, 38 insertions(+), 132 deletions(-) diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 65f79afd..9ac99571 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,9 +1,9 @@ -import type { V2_MetaFunction } from "@remix-run/node"; +import type { MetaFunction } from "@remix-run/node"; import { Link } from "@remix-run/react"; import { useOptionalUser } from "~/utils"; -export const meta: V2_MetaFunction = () => [{ title: "Remix Notes" }]; +export const meta: MetaFunction = () => [{ title: "Remix Notes" }]; export default function Index() { const user = useOptionalUser(); diff --git a/app/routes/join.tsx b/app/routes/join.tsx index 2ca5d882..10e7a4c0 100644 --- a/app/routes/join.tsx +++ b/app/routes/join.tsx @@ -1,4 +1,4 @@ -import type { ActionArgs, LoaderArgs, V2_MetaFunction } from "@remix-run/node"; +import type { ActionArgs, LoaderArgs, MetaFunction } from "@remix-run/node"; import { json, redirect } from "@remix-run/node"; import { Form, Link, useActionData, useSearchParams } from "@remix-run/react"; import { useEffect, useRef } from "react"; @@ -63,7 +63,7 @@ export const action = async ({ request }: ActionArgs) => { }); }; -export const meta: V2_MetaFunction = () => [{ title: "Sign Up" }]; +export const meta: MetaFunction = () => [{ title: "Sign Up" }]; export default function Join() { const [searchParams] = useSearchParams(); diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 1dbf77ba..27c962be 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -1,4 +1,4 @@ -import type { ActionArgs, LoaderArgs, V2_MetaFunction } from "@remix-run/node"; +import type { ActionArgs, LoaderArgs, MetaFunction } from "@remix-run/node"; import { json, redirect } from "@remix-run/node"; import { Form, Link, useActionData, useSearchParams } from "@remix-run/react"; import { useEffect, useRef } from "react"; @@ -58,7 +58,7 @@ export const action = async ({ request }: ActionArgs) => { }); }; -export const meta: V2_MetaFunction = () => [{ title: "Login" }]; +export const meta: MetaFunction = () => [{ title: "Login" }]; export default function LoginPage() { const [searchParams] = useSearchParams(); diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index 88e648c5..732bfd9a 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -15,8 +15,8 @@ "types": ["node", "cypress", "@testing-library/cypress"], "esModuleInterop": true, "jsx": "react-jsx", - "moduleResolution": "node", - "target": "es2019", + "moduleResolution": "Bundler", + "target": "ES2020", "strict": true, "skipLibCheck": true, "resolveJsonModule": true, diff --git a/package.json b/package.json index 88a5317a..c804b070 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ "dependencies": { "@isaacs/express-prometheus-middleware": "^1.2.1", "@prisma/client": "^4.16.2", - "@remix-run/css-bundle": "*", - "@remix-run/express": "*", - "@remix-run/node": "*", - "@remix-run/react": "*", + "@remix-run/css-bundle": "^2.0.0-pre.8", + "@remix-run/express": "^2.0.0-pre.8", + "@remix-run/node": "^2.0.0-pre.8", + "@remix-run/react": "^2.0.0-pre.8", "bcryptjs": "^2.4.3", "chokidar": "^3.5.3", "compression": "^1.7.4", @@ -54,8 +54,8 @@ }, "devDependencies": { "@faker-js/faker": "^8.0.2", - "@remix-run/dev": "*", - "@remix-run/eslint-config": "*", + "@remix-run/dev": "^2.0.0-pre.8", + "@remix-run/eslint-config": "^2.0.0-pre.8", "@testing-library/cypress": "^9.0.0", "@testing-library/jest-dom": "^5.17.0", "@types/bcryptjs": "^2.4.2", @@ -96,7 +96,7 @@ "vitest": "^0.34.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" }, "prisma": { "seed": "ts-node --esm --require tsconfig-paths/register prisma/seed.ts" diff --git a/remix.config.js b/remix.config.js index feda42dd..4047ae45 100644 --- a/remix.config.js +++ b/remix.config.js @@ -1,16 +1,5 @@ /** @type {import('@remix-run/dev').AppConfig} */ export default { cacheDirectory: "./node_modules/.cache/remix", - future: { - v2_dev: true, - v2_errorBoundary: true, - v2_headers: true, - v2_meta: true, - v2_normalizeFormMethod: true, - v2_routeConvention: true, - }, - ignoredRouteFiles: ["**/.*", "**/*.test.{js,jsx,ts,tsx}"], - postcss: true, - serverModuleFormat: "esm", - tailwind: true, + ignoredRouteFiles: ["**/.*", "**/*.test.{ts,tsx}"], }; diff --git a/remix.init/index.js b/remix.init/index.js index a539dd36..c11f24ad 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -1,47 +1,22 @@ -const { execSync } = require("child_process"); -const crypto = require("crypto"); -const fs = require("fs/promises"); -const path = require("path"); +const { execSync } = require("node:child_process"); +const crypto = require("node:crypto"); +const fs = require("node:fs/promises"); +const path = require("node:path"); const toml = require("@iarna/toml"); const PackageJson = require("@npmcli/package-json"); const semver = require("semver"); -const YAML = require("yaml"); -const cleanupCypressFiles = ({ fileEntries, isTypeScript, packageManager }) => +const cleanupCypressFiles = ({ fileEntries, packageManager }) => fileEntries.flatMap(([filePath, content]) => { - let newContent = content.replace( + const newContent = content.replace( new RegExp("npx ts-node", "g"), - isTypeScript ? `${packageManager.exec} ts-node` : "node", + `${packageManager.exec} ts-node`, ); - if (!isTypeScript) { - newContent = newContent - .replace(new RegExp("create-user.ts", "g"), "create-user.js") - .replace(new RegExp("delete-user.ts", "g"), "delete-user.js"); - } - return [fs.writeFile(filePath, newContent)]; }); -const cleanupDeployWorkflow = (deployWorkflow, deployWorkflowPath) => { - delete deployWorkflow.jobs.typecheck; - deployWorkflow.jobs.deploy.needs = deployWorkflow.jobs.deploy.needs.filter( - (need) => need !== "typecheck", - ); - - return [fs.writeFile(deployWorkflowPath, YAML.stringify(deployWorkflow))]; -}; - -const cleanupVitestConfig = (vitestConfig, vitestConfigPath) => { - const newVitestConfig = vitestConfig.replace( - "setup-test-env.ts", - "setup-test-env.js", - ); - - return [fs.writeFile(vitestConfigPath, newVitestConfig)]; -}; - const escapeRegExp = (string) => // $& means the whole matched string string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); @@ -81,15 +56,6 @@ const getPackageManagerVersion = (packageManager) => const getRandomString = (length) => crypto.randomBytes(length).toString("hex"); -const readFileIfNotTypeScript = ( - isTypeScript, - filePath, - parseFunction = (result) => result, -) => - isTypeScript - ? Promise.resolve() - : fs.readFile(filePath, "utf-8").then(parseFunction); - const removeUnusedDependencies = (dependencies, unusedDependencies) => Object.fromEntries( Object.entries(dependencies).filter( @@ -97,15 +63,12 @@ const removeUnusedDependencies = (dependencies, unusedDependencies) => ), ); -const updatePackageJson = ({ APP_NAME, isTypeScript, packageJson }) => { +const updatePackageJson = ({ APP_NAME, packageJson }) => { const { devDependencies, - prisma: { seed: prismaSeed, ...prisma }, scripts: { "format:repo": _repoFormatScript, "lint:repo": _repoLintScript, - typecheck, - validate, ...scripts }, } = packageJson.content; @@ -115,55 +78,29 @@ const updatePackageJson = ({ APP_NAME, isTypeScript, packageJson }) => { devDependencies: removeUnusedDependencies( devDependencies, // packages that are only used for linting the repo - ["eslint-plugin-markdown", "eslint-plugin-prefer-let"].concat( - isTypeScript ? [] : ["ts-node"], - ), + ["eslint-plugin-markdown", "eslint-plugin-prefer-let"], ), - prisma: isTypeScript - ? { ...prisma, seed: prismaSeed } - : { - ...prisma, - seed: prismaSeed - .replace("ts-node", "node") - .replace("seed.ts", "seed.js"), - }, - scripts: isTypeScript - ? { ...scripts, typecheck, validate } - : { ...scripts, validate: validate.replace(" typecheck", "") }, + scripts, }); }; -const main = async ({ isTypeScript, packageManager, rootDirectory }) => { +const main = async ({ packageManager, rootDirectory }) => { const pm = getPackageManagerCommand(packageManager); - const FILE_EXTENSION = isTypeScript ? "ts" : "js"; 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 DEPLOY_WORKFLOW_PATH = path.join( - rootDirectory, - ".github", - "workflows", - "deploy.yml", - ); const DOCKERFILE_PATH = path.join(rootDirectory, "Dockerfile"); const CYPRESS_SUPPORT_PATH = path.join(rootDirectory, "cypress", "support"); - const CYPRESS_COMMANDS_PATH = path.join( - CYPRESS_SUPPORT_PATH, - `commands.${FILE_EXTENSION}`, - ); + const CYPRESS_COMMANDS_PATH = path.join(CYPRESS_SUPPORT_PATH, "commands.ts"); const CREATE_USER_COMMAND_PATH = path.join( CYPRESS_SUPPORT_PATH, - `create-user.${FILE_EXTENSION}`, + "create-user.ts", ); const DELETE_USER_COMMAND_PATH = path.join( CYPRESS_SUPPORT_PATH, - `delete-user.${FILE_EXTENSION}`, - ); - const VITEST_CONFIG_PATH = path.join( - rootDirectory, - `vitest.config.${FILE_EXTENSION}`, + "delete-user.ts", ); const REPLACER = "blues-stack-template"; @@ -183,8 +120,6 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => { cypressCommands, createUserCommand, deleteUserCommand, - deployWorkflow, - vitestConfig, packageJson, ] = await Promise.all([ fs.readFile(FLY_TOML_PATH, "utf-8"), @@ -194,10 +129,6 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => { fs.readFile(CYPRESS_COMMANDS_PATH, "utf-8"), fs.readFile(CREATE_USER_COMMAND_PATH, "utf-8"), fs.readFile(DELETE_USER_COMMAND_PATH, "utf-8"), - readFileIfNotTypeScript(isTypeScript, DEPLOY_WORKFLOW_PATH, (s) => - YAML.parse(s), - ), - readFileIfNotTypeScript(isTypeScript, VITEST_CONFIG_PATH), PackageJson.load(rootDirectory), ]); @@ -231,9 +162,9 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => { ) : dockerfile; - updatePackageJson({ APP_NAME, isTypeScript, packageJson }); + updatePackageJson({ APP_NAME, packageJson }); - const fileOperationPromises = [ + await Promise.all([ fs.writeFile(FLY_TOML_PATH, toml.stringify(prodToml)), fs.writeFile(README_PATH, newReadme), fs.writeFile(ENV_PATH, newEnv), @@ -244,7 +175,6 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => { [CREATE_USER_COMMAND_PATH, createUserCommand], [DELETE_USER_COMMAND_PATH, deleteUserCommand], ], - isTypeScript, packageManager: pm, }), packageJson.save(), @@ -262,19 +192,7 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => { fs.rm(path.join(rootDirectory, ".github", "PULL_REQUEST_TEMPLATE.md")), fs.rm(path.join(rootDirectory, ".eslintrc.repo.cjs")), fs.rm(path.join(rootDirectory, "LICENSE.md")), - ]; - - if (!isTypeScript) { - fileOperationPromises.push( - ...cleanupDeployWorkflow(deployWorkflow, DEPLOY_WORKFLOW_PATH), - ); - - fileOperationPromises.push( - ...cleanupVitestConfig(vitestConfig, VITEST_CONFIG_PATH), - ); - } - - await Promise.all(fileOperationPromises); + ]); execSync(pm.run("format", "--loglevel warn"), { cwd: rootDirectory, diff --git a/remix.init/package.json b/remix.init/package.json index 5f161b81..7c795c83 100644 --- a/remix.init/package.json +++ b/remix.init/package.json @@ -5,8 +5,7 @@ "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", - "@npmcli/package-json": "^2.0.0", - "semver": "^7.5.4", - "yaml": "^2.3.1" + "@npmcli/package-json": "^5.0.0", + "semver": "^7.5.4" } } diff --git a/tsconfig.json b/tsconfig.json index 76e38a03..efda85a0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,15 +2,15 @@ "exclude": ["./cypress", "./cypress.config.ts"], "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2019"], + "lib": ["DOM", "DOM.Iterable", "ES2020"], "types": ["vitest/globals"], "isolatedModules": true, "esModuleInterop": true, "jsx": "react-jsx", "module": "ES2020", - "moduleResolution": "node", + "moduleResolution": "Bundler", "resolveJsonModule": true, - "target": "ES2019", + "target": "ES2020", "strict": true, "allowJs": true, "forceConsistentCasingInFileNames": true,