feat: enabled tsup code splitting and esm directory import, removed manual copy of files

This commit is contained in:
Akhil Mohan
2024-02-02 15:38:26 +05:30
parent 72d46efba5
commit b3d4787e21
9 changed files with 440 additions and 275 deletions

View File

@@ -104,7 +104,6 @@ ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
WORKDIR / WORKDIR /
COPY --from=backend-runner /app /backend COPY --from=backend-runner /app /backend
COPY --from=backend-runner /app/dist/services/smtp/templates /backend/dist/templates
COPY --from=frontend-runner /app ./backend/frontend-build COPY --from=frontend-runner /app ./backend/frontend-build

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,12 @@
"name": "backend", "name": "backend",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "./dist/main.mjs",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine", "dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine",
"dev:docker": "nodemon", "dev:docker": "nodemon",
"build": "rimraf dist && tsup && cp -R ./src/lib/validator/disposable_emails.txt ./dist && cp -R ./src/services/smtp/templates ./dist", "build": "tsup",
"start": "node dist/main.mjs", "start": "node dist/main.mjs",
"type:check": "tsc --noEmit", "type:check": "tsc --noEmit",
"lint:fix": "eslint --fix --ext js,ts ./src", "lint:fix": "eslint --fix --ext js,ts ./src",
@@ -44,7 +44,13 @@
"@types/pg": "^8.10.9", "@types/pg": "^8.10.9",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/prompt-sync": "^4.2.3", "@types/prompt-sync": "^4.2.3",
"@types/resolve": "^1.20.6",
"@types/uuid": "^9.0.7", "@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1", "eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
@@ -55,6 +61,7 @@
"prompt-sync": "^4.2.0", "prompt-sync": "^4.2.0",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsc-alias": "^1.8.8",
"tsconfig-paths": "^4.2.0", "tsconfig-paths": "^4.2.0",
"tsup": "^8.0.1", "tsup": "^8.0.1",
"tsx": "^4.4.0", "tsx": "^4.4.0",
@@ -80,8 +87,6 @@
"@octokit/webhooks-types": "^7.3.1", "@octokit/webhooks-types": "^7.3.1",
"@serdnam/pino-cloudwatch-transport": "^1.0.4", "@serdnam/pino-cloudwatch-transport": "^1.0.4",
"@sindresorhus/slugify": "^2.2.1", "@sindresorhus/slugify": "^2.2.1",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"@ucast/mongo2js": "^1.3.4", "@ucast/mongo2js": "^1.3.4",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"argon2": "^0.31.2", "argon2": "^0.31.2",
@@ -91,9 +96,6 @@
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bullmq": "^5.1.1", "bullmq": "^5.1.1",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"fastify": "^4.24.3", "fastify": "^4.24.3",
"fastify-plugin": "^4.5.1", "fastify-plugin": "^4.5.1",
"handlebars": "^4.7.8", "handlebars": "^4.7.8",
@@ -108,7 +110,6 @@
"nanoid": "^5.0.4", "nanoid": "^5.0.4",
"node-cache": "^5.1.2", "node-cache": "^5.1.2",
"nodemailer": "^6.9.7", "nodemailer": "^6.9.7",
"ora": "^7.0.1",
"passport-github": "^1.1.0", "passport-github": "^1.1.0",
"passport-gitlab2": "^5.0.0", "passport-gitlab2": "^5.0.0",
"passport-google-oauth20": "^2.0.0", "passport-google-oauth20": "^2.0.0",

View File

@@ -14,11 +14,10 @@ import fasitfy from "fastify";
import { Knex } from "knex"; import { Knex } from "knex";
import { Logger } from "pino"; import { Logger } from "pino";
import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue"; import { TQueueServiceFactory } from "@app/queue";
import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSmtpService } from "@app/services/smtp/smtp-service";
import { getConfig } from "@lib/config/env";
import { globalRateLimiterCfg } from "./config/rateLimiter"; import { globalRateLimiterCfg } from "./config/rateLimiter";
import { fastifyErrHandler } from "./plugins/error-handler"; import { fastifyErrHandler } from "./plugins/error-handler";
import { registerExternalNextjs } from "./plugins/external-nextjs"; import { registerExternalNextjs } from "./plugins/external-nextjs";
@@ -40,6 +39,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => {
const server = fasitfy({ const server = fasitfy({
logger, logger,
trustProxy: true, trustProxy: true,
connectionTimeout: 30 * 1000,
ignoreTrailingSlash: true ignoreTrailingSlash: true
}).withTypeProvider<ZodTypeProvider>(); }).withTypeProvider<ZodTypeProvider>();
@@ -75,7 +75,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => {
if (appCfg.isProductionMode) { if (appCfg.isProductionMode) {
await server.register(registerExternalNextjs, { await server.register(registerExternalNextjs, {
standaloneMode: appCfg.STANDALONE_MODE, standaloneMode: appCfg.STANDALONE_MODE,
dir: path.join(__dirname, "../"), dir: path.join(__dirname, "../../"),
port: appCfg.PORT port: appCfg.PORT
}); });
} }

View File

@@ -45,6 +45,9 @@ export const registerExternalNextjs = async (
server.route({ server.route({
method: ["GET", "PUT", "PATCH", "POST", "DELETE"], method: ["GET", "PUT", "PATCH", "POST", "DELETE"],
url: "/*", url: "/*",
schema: {
hide: true
},
handler: (req, res) => handler: (req, res) =>
nextApp nextApp
.getRequestHandler()(req.raw, res.raw) .getRequestHandler()(req.raw, res.raw)

View File

@@ -43,6 +43,7 @@ export const fastifySwagger = fp(async (fastify) => {
}); });
await fastify.register(swaggerUI, { await fastify.register(swaggerUI, {
routePrefix: "/docs" routePrefix: "/api/docs",
prefix: "/api/docs"
}); });
}); });

View File

@@ -108,7 +108,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
} }
}); });
await res.setCookie("jid", refreshToken, { void res.setCookie("jid", refreshToken, {
httpOnly: true, httpOnly: true,
path: "/", path: "/",
sameSite: "strict", sameSite: "strict",
@@ -159,7 +159,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
userAgent userAgent
}); });
await res.setCookie("jid", refreshToken, { void res.setCookie("jid", refreshToken, {
httpOnly: true, httpOnly: true,
path: "/", path: "/",
sameSite: "strict", sameSite: "strict",

View File

@@ -22,11 +22,9 @@
"skipLibCheck": true, "skipLibCheck": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@app/*": ["./src/*"], "@app/*": ["./src/*"]
"@lib/*": ["./src/lib/*"],
"@server/*": ["./src/server/*"]
} }
}, },
"include": ["src/**/*", "scripts/**/*", "e2e-test/**/*","./.eslintrc.js"], "include": ["src/**/*", "scripts/**/*", "e2e-test/**/*", "./.eslintrc.js", "./tsup.config.js"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }

View File

@@ -1,14 +1,73 @@
/* eslint-disable */
import path from "node:path";
import fs from "fs/promises";
import { replaceTscAliasPaths } from "tsc-alias";
import { defineConfig } from "tsup"; import { defineConfig } from "tsup";
// what you need tsup to build instead of tsx or tsc
// tsx is a drop in replacement for nodejs - rather build on top of nodejs runtime
// it works exactly like node only catch is it doesn't have a final build step else prod should be started with tsx
// tsc is great for final build issue is we need to then do a manual copy of all static files like handlebar email etc
// another issue is commonjs. final output needs to be esm because more and more packages are migrating to esm
// if the final output is commonjs it will just throw an error
// the below configuration is best of both worlds
export default defineConfig({ export default defineConfig({
shims: true, shims: true,
clean: true,
minify: false,
keepNames: true,
splitting: false,
format: "esm", format: "esm",
// copy the files to output
loader: { loader: {
".handlebars": "copy", ".handlebars": "copy",
".md": "copy" ".md": "copy",
".txt": "copy"
}, },
external: ["../../../frontend/node_modules/next/dist/server/next-server.js"], external: ["../../../frontend/node_modules/next/dist/server/next-server.js"],
outDir: "dist", outDir: "dist",
tsconfig: "./tsconfig.json",
entry: ["./src"], entry: ["./src"],
sourceMap: "inline" sourceMap: true,
skipNodeModulesBundle: true,
esbuildPlugins: [
{
// esm directory import are not allowed
// /folder1 should be explicitly imported as /folder1/index.ts
// this plugin will append it automatically on build time to all imports
name: "commonjs-esm-directory-import",
setup(build) {
build.onResolve({ filter: /.*/ }, async (args) => {
if (args.importer) {
if (args.kind === "import-statement") {
const isRelativePath = args.path.startsWith(".");
const absPath = isRelativePath
? path.join(args.resolveDir, args.path)
: path.join(args.path.replace("@app", "./src"));
const isFile = await fs
.stat(`${absPath}.ts`)
.then((el) => el.isFile)
.catch((err) => err.code === "ENOTDIR");
return {
path: isFile ? `${args.path}.mjs` : `${args.path}/index.mjs`,
external: true
};
}
}
return undefined;
});
}
}
],
async onSuccess() {
// this will replace all tsconfig paths
await replaceTscAliasPaths({
configFile: "tsconfig.json",
watch: false,
outDir: "dist"
});
}
}); });