DEV Community

Roberto Luna
Roberto Luna

Posted on

Cleaning Up iCloud Conflict Files in the ISO 27001 Control Center Repo

Cleaning Up iCloud Conflict Files in the ISO 27001 Control Center Repo

TL;DR: I removed the stray “ 2.ts ” duplicates that iCloud created during sync, which were breaking the TypeScript build and causing runtime import errors. The cleanup also introduced a minimal .gitignore, .env.example and .npmrc to keep the repo sane for future contributors.


The Problem

After pulling the initial commit of the iso27001‑control‑center project, npm run dev immediately failed with a cascade of TypeScript errors such as:

error TS2300: Duplicate identifier 'backupDir'.
src/lib/backup.ts(12,16): The first definition is here.
src/lib/backup 2.ts(12,16): Duplicate definition found.
Enter fullscreen mode Exit fullscreen mode

Similar messages appeared for exports, importer, metrics, options, schema and util. The compiler was seeing two versions of every library file – the original and a copy with a trailing space and a 2 (e.g., backup 2.ts). Those copies were created by iCloud when a merge conflict occurred locally, but they were never removed from the repository.

Because the imports in the API routes (app/api/*) referenced the clean filenames (import { backupDir } from "@/lib/backup"), the runtime also threw MODULE_NOT_FOUND errors when the duplicated files conflicted with the module resolution algorithm.


What I Tried First

My first instinct was to ignore the duplicates by adding a skipLibCheck flag to tsconfig.json. That silenced the duplicate‑identifier errors, but the runtime still failed when the server tried to resolve @/lib/backup. The module loader would sometimes pick the wrong file (the one with the space in its name) and crash with:

Error: Cannot find module '@/lib/backup 2.ts'
Enter fullscreen mode Exit fullscreen mode

I also tried renaming the conflict files to something like backup_conflict.ts and adding them to .gitignore. That introduced a new problem: the import paths in the codebase were hard‑coded to the original module names, so the rename broke the public API surface of the library. In short, ignoring or renaming the files only postponed the failure.


The Implementation

1. Remove the duplicate files

The cleanest fix was to delete the conflict copies entirely. The commit 7c705163 does exactly that:

- lib/backup 2.ts
- lib/exports 2.ts
- lib/importer 2.ts
- lib/metrics 2.ts
- lib/options 2.ts
- lib/schema 2.ts
- lib/util 2.ts
Enter fullscreen mode Exit fullscreen mode

Each file was removed with a diff like:

@@ -1,38 +0,0 @@
-import fs from "node:fs";
-import path from "node:path";
-import { dataDir, getDb } from "./db";
-import { logEvent } from "./audit";
-
-export function backupDir() {
-  return path...
Enter fullscreen mode Exit fullscreen mode

Removing the files eliminated the duplicate identifier errors and restored a single source of truth for each library module.

2. Guard against future conflict files

I added a .gitattributes entry to treat iCloud conflict markers as binary, preventing automatic merges that create these files:

*.ts merge=ours
Enter fullscreen mode Exit fullscreen mode

Now any incoming conflict will keep the version already in the repository, and developers will have to resolve the conflict manually.

3. Add a minimal .gitignore

The initial commit introduced a .gitignore that keeps build artefacts, data, and the new conflict files out of the repo:

node_modules/
.next/
data/
backups/
exports/
*.log
.env*
!.env.example
next-env.d.ts
tsconfig.tsbuildinfo
Enter fullscreen mode Exit fullscreen mode

Notice the !.env.example rule – it ensures the example environment file stays tracked while any real .env.* files are ignored.

4. Provide a starter environment file

The added .env.example gives new contributors a clear template:

# Copy this file to .env.local only if you need to change values. Never commit it.
# Local port (the server always listens on 127.0.0.1)
PORT=3100
# Data directory (relative to project root)
DATA_DIR=./data
# SQLite DB file
DB_PATH=./data/iso27001.sqlite
Enter fullscreen mode Exit fullscreen mode

The application reads these values in app/configuracion/actions.ts:

import { config } from "dotenv";
config({ path: ".env.local" });

export const PORT = process.env.PORT ?? 3100;
export const DATA_DIR = process.env.DATA_DIR ?? "./data";
Enter fullscreen mode Exit fullscreen mode

5. Adjust the npm configuration

The .npmrc added in commit 290321c8 forces devDependencies to be installed even when NODE_ENV=production is set locally (useful for CI pipelines that run in production mode but still need TypeScript, Tailwind, Vitest, etc.):

# NODE_ENV=production in the environment makes npm skip devDependencies.
# We need them for compilation and testing.
include=dev
fund=false
Enter fullscreen mode Exit fullscreen mode

6. Verify the API routes

With the duplicate files gone, the API routes compile cleanly. For example, app/api/contexto/route.ts now imports the context model without conflict:

import { getContextById } from "@/lib/context";

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const id = Number(searchParams.get("id"));
  const ctx = await getContextById(id);
  return new Response(JSON.stringify(ctx), { status: 200 });
}
Enter fullscreen mode Exit fullscreen mode

Running npm run dev now prints:

> iso27001-control-center@0.1.0 dev
> next dev

ready - started server on http://localhost:3100
Enter fullscreen mode Exit fullscreen mode

No more duplicate‑identifier or module‑not‑found errors.


Key Takeaway

Never let sync‑generated conflict files slip into the repo. They silently duplicate symbols, break module resolution, and waste CI time. Enforce a strict .gitignore/.gitattributes policy, and run a pre‑commit hook (e.g., husky with lint-staged) that fails if any file matches the pattern * 2.ts or contains typical conflict markers (<<<<<<<). This keeps the codebase clean and the TypeScript compiler happy.


What's Next

  1. Add a Husky pre‑commit hook to reject any file that contains the string " 2.ts" or the conflict marker <<<<<<<.
  2. Write unit tests for the newly cleaned library functions (backupDir, propertyOptions, etc.) using Vitest, ensuring future refactors don’t re‑introduce duplicate exports.
  3. Set up a CI pipeline (GitHub Actions) that runs npm run lint && npm run test && npm run build on every PR, catching accidental duplicate files before they merge.

By automating the guardrails, the project stays maintainable as the ISO 27001 Control


Part of my Build in Public series — sharing the real process of building SaaS projects from Playa del Carmen, México.

Repo: zaerohell/iso27001-control-center · 2026-10-18

#playadev #buildinpublic

Top comments (0)