From fcea8f221b454819096299e9019aab1c37741734 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:27:27 +0700 Subject: [PATCH] feat: implement @repo/brand package for centralized asset management and automated distribution to consuming apps --- .gitignore | 3 ++ apps/landing/index.html | 7 +++- apps/landing/package.json | 6 ++- apps/web/index.html | 2 +- apps/web/package.json | 6 ++- apps/web/public/typescript.svg | 1 - apps/web/public/vite.svg | 1 - packages/brand/README.md | 32 ++++++++++++++++ packages/brand/package.json | 17 +++++++++ packages/brand/public/favicon.svg | 6 +++ packages/brand/scripts/copy-brand-assets.js | 31 +++++++++++++++ packages/brand/src/data/info.json | 10 +++++ packages/brand/src/index.ts | 3 ++ packages/brand/tsconfig.json | 5 +++ pnpm-lock.yaml | 42 ++++++++++++++++++++- 15 files changed, 162 insertions(+), 10 deletions(-) delete mode 100644 apps/web/public/typescript.svg delete mode 100644 apps/web/public/vite.svg create mode 100644 packages/brand/README.md create mode 100644 packages/brand/package.json create mode 100644 packages/brand/public/favicon.svg create mode 100644 packages/brand/scripts/copy-brand-assets.js create mode 100644 packages/brand/src/data/info.json create mode 100644 packages/brand/src/index.ts create mode 100644 packages/brand/tsconfig.json diff --git a/.gitignore b/.gitignore index e4ba428..5e0724f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ dist-ssr server/dist public/dist +# Auto-generated brand assets (copied from @repo/brand) +**/public/brand/ + # Storybook Build Outputs *storybook.log storybook-static diff --git a/apps/landing/index.html b/apps/landing/index.html index ceabddf..96cdf05 100644 --- a/apps/landing/index.html +++ b/apps/landing/index.html @@ -2,10 +2,13 @@ - + Eigen — Company Profile - +
diff --git a/apps/landing/package.json b/apps/landing/package.json index 2ca4eef..af45cde 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -4,13 +4,15 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --clearScreen false", - "build": "tsc && vite build", + "copy:brand": "node ../../packages/brand/scripts/copy-brand-assets.js", + "dev": "pnpm run copy:brand && vite --clearScreen false", + "build": "pnpm run copy:brand && tsc && vite build", "preview": "vite preview", "lint": "eslint \"src/**/*.ts\"", "typecheck": "tsc --noEmit" }, "dependencies": { + "@repo/brand": "workspace:*", "@repo/core-api": "workspace:*", "@repo/core-i18n": "workspace:*", "@repo/core-storage": "workspace:*", diff --git a/apps/web/index.html b/apps/web/index.html index eed3f7d..b209b4f 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -2,7 +2,7 @@ - + Applications diff --git a/apps/web/package.json b/apps/web/package.json index fc8add5..ef5ab0c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -4,8 +4,9 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --clearScreen false", - "build": "tsc && vite build", + "copy:brand": "node ../../packages/brand/scripts/copy-brand-assets.js", + "dev": "pnpm run copy:brand && vite --clearScreen false", + "build": "pnpm run copy:brand && tsc && vite build", "preview": "vite preview", "lint": "eslint \"src/**/*.ts\"", "test": "vitest run", @@ -14,6 +15,7 @@ }, "dependencies": { "@hookform/resolvers": "^5.0.1", + "@repo/brand": "workspace:*", "@repo/core-api": "workspace:*", "@repo/core-events": "workspace:*", "@repo/core-i18n": "workspace:*", diff --git a/apps/web/public/typescript.svg b/apps/web/public/typescript.svg deleted file mode 100644 index d91c910..0000000 --- a/apps/web/public/typescript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/public/vite.svg b/apps/web/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/apps/web/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/brand/README.md b/packages/brand/README.md new file mode 100644 index 0000000..b4345c6 --- /dev/null +++ b/packages/brand/README.md @@ -0,0 +1,32 @@ +# @repo/brand + +This package serves as the single source of truth for our brand identity, containing static visual assets (logos, favicons) and brand information. + +## 📁 Public Assets + +Place your raw, static brand assets inside the `public/` directory: + +- **`logo.png`** — Primary brand logo +- **`favicon.svg`** — Browser favicon + +### How Assets are Distributed + +Due to monorepo constraints, frameworks like Vite or Next.js do not natively serve static assets from external packages. To solve this, these files are automatically copied into the consuming apps' `public/brand/` directories via the `scripts/copy-brand-assets.js` script. + +This makes them directly available in the browser at: +- `/brand/logo.png` +- `/brand/favicon.svg` + +--- + +## 🚀 Usage in Consuming Apps + +### 1. Automate the Copy Process +To ensure the consuming app always has the latest brand assets, add the copy script to the app's `package.json` (e.g., in `apps/web/package.json`): + +```json +"scripts": { + "copy:brand": "node ../../packages/brand/scripts/copy-brand-assets.js", + "dev": "pnpm run copy:brand && vite --clearScreen false", + "build": "pnpm run copy:brand && tsc && vite build" +} \ No newline at end of file diff --git a/packages/brand/package.json b/packages/brand/package.json new file mode 100644 index 0000000..d17cbe7 --- /dev/null +++ b/packages/brand/package.json @@ -0,0 +1,17 @@ +{ + "name": "@repo/brand", + "version": "0.0.0", + "private": true, + "exports": { + ".": "./src/index.ts", + "./public/*": "./public/*" + }, + "license": "MIT", + "scripts": { + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@repo/typescript-config": "workspace:*", + "typescript": "5.5.4" + } +} diff --git a/packages/brand/public/favicon.svg b/packages/brand/public/favicon.svg new file mode 100644 index 0000000..7abbacf --- /dev/null +++ b/packages/brand/public/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/brand/scripts/copy-brand-assets.js b/packages/brand/scripts/copy-brand-assets.js new file mode 100644 index 0000000..6fcdedf --- /dev/null +++ b/packages/brand/scripts/copy-brand-assets.js @@ -0,0 +1,31 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +// Resolve the source directory (packages/brand/public) +const brandPublicDir = path.resolve(__dirname, '../public'); + +// Resolve the destination directory (public/brand folder in the calling app) +// process.cwd() points to the app folder (e.g., apps/web) when the script is executed +const destDir = path.resolve(process.cwd(), 'public', 'brand'); + +// Ensure the destination folder exists +if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); +} + +// Copy files using fs.cpSync (Available in Node.js 16.7+) +try { + fs.cpSync(brandPublicDir, destDir, { + recursive: true, + force: true, // Overwrite files if they already exist + filter: (src) => { + const filename = path.basename(src); + // Exclude unwanted files + return !['.gitkeep', 'README.md'].includes(filename); + }, + }); + console.log(`✓ Brand assets successfully copied to ${destDir}`); +} catch (error) { + console.error(`❌ Failed to copy brand assets:`, error.message); + process.exit(1); +} diff --git a/packages/brand/src/data/info.json b/packages/brand/src/data/info.json new file mode 100644 index 0000000..b72a7be --- /dev/null +++ b/packages/brand/src/data/info.json @@ -0,0 +1,10 @@ +{ + "name": "My Brand", + "description": "A short description of your brand or product.", + "tagline": "Building the future, one pixel at a time.", + "socialLinks": { + "twitter": "https://twitter.com/mybrand", + "github": "https://github.com/mybrand", + "website": "https://mybrand.com" + } +} diff --git a/packages/brand/src/index.ts b/packages/brand/src/index.ts new file mode 100644 index 0000000..fb17fae --- /dev/null +++ b/packages/brand/src/index.ts @@ -0,0 +1,3 @@ +import brandInfo from './data/info.json'; + +export type BrandInfo = typeof brandInfo; diff --git a/packages/brand/tsconfig.json b/packages/brand/tsconfig.json new file mode 100644 index 0000000..f1277d8 --- /dev/null +++ b/packages/brand/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@repo/typescript-config/library.json", + "include": ["."], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a833d8e..68c45b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,9 @@ importers: apps/landing: dependencies: + '@repo/brand': + specifier: workspace:* + version: link:../../packages/brand '@repo/core-api': specifier: workspace:* version: link:../../packages/core-api @@ -243,6 +246,9 @@ importers: '@hookform/resolvers': specifier: ^5.0.1 version: 5.4.0(react-hook-form@7.79.0) + '@repo/brand': + specifier: workspace:* + version: link:../../packages/brand '@repo/core-api': specifier: workspace:* version: link:../../packages/core-api @@ -329,6 +335,15 @@ importers: specifier: ^4.0.17 version: 4.0.17(jsdom@26.1.0) + packages/brand: + devDependencies: + '@repo/typescript-config': + specifier: workspace:* + version: link:../configs/typescript + typescript: + specifier: 5.5.4 + version: 5.5.4 + packages/configs/eslint: dependencies: '@typescript-eslint/eslint-plugin': @@ -7444,6 +7459,18 @@ packages: dependencies: picomatch: 4.0.3 + /fdir@6.5.0(picomatch@4.0.5): + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dependencies: + picomatch: 4.0.5 + dev: false + /fetch-cookie@2.2.0: resolution: {integrity: sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==} dependencies: @@ -9989,6 +10016,11 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + /picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + dev: false + /plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} @@ -11252,7 +11284,7 @@ packages: is-plain-obj: 4.1.0 semver: 7.7.1 sort-object-keys: 2.0.1 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 dev: false /source-map-js@1.2.1: @@ -11627,6 +11659,14 @@ packages: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + /tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + dev: false + /tinyrainbow@3.0.3: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'}