Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

One Monorepo, Two Outputs: How I Eliminated Duplicate Starter Templates

Part 2 of the Building create-notils series. In my previous article, I explained why I stopped copy-pasting repositories and started building my own project scaffolding tool. However, one major architectural problem remained: I wanted…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Part 2 of the Building create-notils series.




In my previous article, I explained why I stopped copy-pasting repositories and started building my own project scaffolding tool. However, one major architectural problem remained: I wanted create-notils to support both of these primary project structures.



A standalone Next.js application:




my-app/
├── src/
├── public/
├── package.json
└── components.json







And a Turborepo monorepo:




my-app/
├── apps/
│ └── app/
├── packages/
│ ├── ui/
│ └── config/
├── turbo.json
└── package.json







At first glance, the obvious solution is to maintain two separate templates—one for standalone and one for monorepo. Problem solved, right?



Except... it isn't.









The Hidden Cost of Multiple Templates



Every starter template starts out identical. Then one day, you fix a subtle bug in one template and forget to update the other. A week later, you upgrade Next.js in one repository before getting around to the second. A month later, you improve your UI package and find yourself manually copying files back and forth between folders.



Eventually, the templates slowly drift apart. The true cost isn't creating templates; the cost is maintaining them forever.









What Actually Changes?



When I sat down and compared the two project layouts side by side, surprisingly little was different. The actual application code, UI components, theming, and utility functions were 100% identical.



The only real differences were the structural project boundaries:






































Concern Monorepo Standalone
UI Package packages/ui src/components/ui
Utilities @notils/ui/lib/utils @/lib/utils
Configuration Shared workspace package Local configuration
Package Manifests Multiple (package.json files) Single root manifest
Workspace Tooling Present (turbo.json, workspaces) Removed entirely


Everything else was effectively the exact same code. That single observation changed the entire architecture of create-notils.









A Different Approach: The Canonical Source of Truth



Instead of maintaining two templates, I decided to maintain only one. The monorepo became the canonical source of truth.



Whenever a developer chooses the monorepo option, the CLI simply clones and scaffolds it directly:




Canonical Monorepo
│
▼
Generated Monorepo







When someone chooses a standalone project, something much more interesting happens. The CLI dynamically transforms the canonical monorepo into a flattened standalone application:




Canonical Monorepo
│
▼
Flatten Transform
│
▼
Generated Standalone







Instead of storing two separate templates, I generate one directly from the other.









Thinking Like a Compiler



This shift changed how I thought about the CLI. It isn't just copying static files anymore; it is compiling a project. The monorepo acts as the source code, and the generated starter is the compiled output.



Because of this, the transformation can be cleanly broken down into deterministic, sequential steps:




Canonical Project
│
▼
1. Resolve Workspace Packages
│
▼
2. Rewrite Import Paths
│
▼
3. Move & Flatten Files
│
▼
4. Merge package.json Manifests
│
▼
5. Inline Shared Configurations
│
▼
6. Output Standalone Project







Each step in this pipeline has a single, isolated responsibility. That makes the entire process predictable, highly testable, and easy to evolve over time.









How the Monorepo Flattening Works



The transformation itself is surprisingly straightforward under the hood. Workspace packages simply become internal directories inside the application structure. For example, UI components are shifted directly into the source tree:




packages/ui/src/components/ui  ──►  src/components/ui







Shared utility functions are moved alongside them:




packages/ui/src/lib/utils.ts  ──►  src/lib/utils.ts







Next, the Abstract Syntax Tree (AST) or file contents are processed to rewrite import paths. A workspace import like this:




// Monorepo Workspace Import
import { Button } from "@notils/ui/components/ui/button";







Is automatically rewritten to a standard Next.js path alias:




// Standalone Local Import
import { Button } from "@/components/ui/button";







The exact same transformation happens for every shared workspace reference across the codebase. Zero component logic changes—only the artificial project boundaries disappear.









The Architectural Benefits



Treating the monorepo as a canonical compilation source provides several massive advantages for long-term maintenance:






1. One Source of Truth



Every bug fix, UI tweak, and architectural improvement is made exactly once. I never have to second-guess whether both project styles are synchronized.






2. Effortless Upgrades



When breaking changes drop for Next.js, Tailwind CSS, or React, I only update the canonical monorepo. Every generated project shape automatically inherits the upgraded foundation.






3. Automated CI Testing



Because generation is 100% deterministic, I can rigorously verify both outputs in a automated Continuous Integration (CI) pipeline:




Generate Monorepo ──► Build & Typecheck
│
▼
Generate Standalone ──► Build & Typecheck ──► Verify Zero "@notils/*" Imports Remain







If the standalone transformation accidentally leaves behind even a single @notils/* workspace import, the CI pipeline fails immediately. Subtle scaffolding bugs become instant, actionable build failures.









Why Not Keep a "Shared Core" Submodule?



One common alternative I explored was keeping shared files in a common repository or submodule and wrapping two thin project templates around them. While this sounds clean in theory, it doesn't actually eliminate maintenance duplication.



You still end up manually managing:




  • Two separate package.json manifests

  • Two TypeScript (tsconfig.json) configurations

  • Two distinct linting and formatting setups

  • Two shadcn/ui configuration files

  • Two separate build and deployment pipelines



The duplication doesn't disappear; it just gets pushed to a different layer of the codebase. By treating the monorepo as the canonical compiler source, the standalone application becomes a zero-maintenance generated artifact.









Looking Ahead: From Templates to Targets



Right now, create-notils focuses exclusively on modern Next.js environments. But this compiler-style architecture opens up an exciting possibility: instead of thinking in terms of static templates, I'm starting to think in terms of compilation targets.



A single canonical project can eventually compile into completely different project shapes depending on the exact requirements of the developer. Today's supported targets are simply monorepo and standalone. Tomorrow, they could expand to include mobile-first layouts, desktop wrappers, or alternate frameworks that share a unified design system and backend layer.









Conclusion



The most valuable lesson I learned while building create-notils wasn't about Next.js app routers or Turborepo caching—it was about aggressively reducing maintenance overhead.



Every duplicated template is future technical debt. Every generated template is automation working in your favor.



When building developer tooling, the fundamental question stopped being:




"How many templates should I maintain?"




And became:




"How many project shapes can I generate from a single source of truth?"




That mindset shift fundamentally transformed how I build and maintain open-source software.






What do you think of generating standalone projects from a monorepo source of truth? Have you tackled template drift in your own tooling before? Let's discuss in the comments!









2. Cyber Threat Intelligence & Forensik

CTI Threat Relationship Graph3 Knoten / 2 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten One Monorepo, Two Outputs: How I Eliminated Duplicate Starter Templates

Thematisch verwandte Begriffe: Monorepo, Outputs, Eliminated, Duplicate · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-101003 | A weakness has been identified in Cesanta Mongoose up to 7.21. Affected…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag