learning-website-nextjs1-1 Exercise 2: Three Apps That Share the Package
===========================================================================
Each site is its own Next.js app: its own build, its own port, its own URL once deployed.
Generated by a small script (the three apps differ only in their name and port); the files for the
languages app are:
Save as apps/languages/package.json:
{
"name": "@lw/languages",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3001",
"build": "next build",
"start": "next start -p 3001",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@lw/sites": "*",
"next": "^16",
"react": "^19",
"react-dom": "^19"
}
}
Save as apps/languages/next.config.ts:
import type { NextConfig } from "next";
const config: NextConfig = {
// the shared package is TypeScript source, so Next has to compile it too
transpilePackages: ["@lw/sites"],
};
export default config;
Save as apps/languages/tsconfig.json:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"plugins": [
{
"name": "next"
}
],
"incremental": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
Save as apps/languages/app/layout.tsx:
import type { ReactNode } from "react";
export const metadata = { title: "languages | Philip's Learning Notes" };
export default function RootLayout({ children }: { children: ReactNode }) {
return (
{children}
);
}
Save as apps/languages/app/page.tsx:
import { SITES, SITE_NAMES, environment, siteUrl, type SiteName } from "@lw/sites";
const SITE: SiteName = "languages";
export default function Home() {
const env = environment();
return (
{SITES[SITE].title}
Folders on this site: {SITES[SITE].folders.join(", ")}.
);
}
Save as apps/portfolio/app/page.tsx:
import { SITES, SITE_NAMES, environment, siteUrl } from "@lw/sites";
export default function Home() {
const env = environment();
return (
);
}
Why transpilePackages: Next compiles the app's own files but treats packages in node_modules as
already compiled. @lw/sites is TypeScript source, so it must be named here or the build stops at
its first "export const".
Each app has its own port so all of them can run at once: portfolio 3000, languages 3001,
webdevelopment 3002 (the table in the shared package gives every site its own, 3003 to 3008 for the
rest). Links to another site come from siteUrl(), so one place decides whether the link is
http://systems.localhost:3004/ (development) or https://systems.osztromok.com/ (LW_ENV=prod).
Checked, on the real project:
npm run typecheck all three apps pass (tsc --noEmit)
npm run build:languages one app, from a cold start
Compiled successfully in 2.6s ... Generating static pages (3/3) ...
Route (app) ┌ ○ / └ ○ /_not-found ○ (Static) prerendered as static content
real 11.4s
npm run build all three apps
real 18.6s
Then each was started with its own "next start" and fetched:
localhost:3001
Languages
"Folders on this site: france, germany, hungary, japan, culture."
localhost:3002
links on the languages page: http://webdevelopment.localhost:3002/ , http://programming.localhost:3003/ ,
http://systems.localhost:3004/ ... (every OTHER site, none to itself)
a missing page: 404
Both pages are "Static": they were produced at build time and need no server code to answer.
What was NOT verified: the *.localhost names were not opened in a browser (the requests were made to
localhost with curl, and the host name does not matter to these apps because each app serves only
its own site); nothing here has run under Apache or on the server.
WHY THIS WORKS AS AN ANSWER
---------------------------
It shows that a monorepo is not "one big app": each site still builds, starts and fails on its own,
and the shared part is small and tested.