Created
September 15, 2025 09:04
-
-
Save dmitry-stepanenko/cf0d1e81bc1a214fb932ab8991077a9b to your computer and use it in GitHub Desktop.
nx large mock repo seed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const noop = () => null; | |
| // const writeFileSync = mkdirSync = cpSync = noop; | |
| const { | |
| writeFileSync, mkdirSync, cpSync, | |
| existsSync, readFileSync} = require("fs"); | |
| const { workspaceRoot } = require("nx/src/devkit-exports"); | |
| const path = require("path"); | |
| const CONFIG = { | |
| appName: "myapp", // must exist under apps/ | |
| routesPerApp: 250, | |
| componentsPerRoute: 250, | |
| internalPerComponent: 1, | |
| templateLibPath: path.join(workspaceRoot, "libs", "template-lib"), | |
| }; | |
| const rootDir = workspaceRoot; | |
| /** Utils */ | |
| function pascalCase(str) { | |
| return str | |
| .replace(/(^|[-_])(.)/g, (_, __, c) => c.toUpperCase()) | |
| .replace(/[^a-zA-Z0-9]/g, ""); | |
| } | |
| function writeFileSyncRecursive(filename, content) { | |
| mkdirSync(path.dirname(filename), { recursive: true }); | |
| writeFileSync(filename, content, "utf8"); | |
| } | |
| function copyTemplateLib(targetDir, newName, importPath) { | |
| if (!existsSync(CONFIG.templateLibPath)) { | |
| throw new Error(`Template lib not found at ${CONFIG.templateLibPath}. Please generate one first.`); | |
| } | |
| cpSync(CONFIG.templateLibPath, targetDir, { recursive: true }); | |
| // Replace placeholders in project.json, tsconfig, etc. | |
| const projectJsonPath = path.join(targetDir, "project.json"); | |
| if (existsSync(projectJsonPath)) { | |
| let pj = readFileSync(projectJsonPath, "utf8"); | |
| pj = pj.replace(/template-lib/g, newName); | |
| pj = pj.replace(/__template__/g, importPath); // adjust directory if needed | |
| writeFileSync(projectJsonPath, pj, "utf8"); | |
| } | |
| const jestConfigPath = path.join(targetDir, "jest.config.js"); | |
| if (existsSync(jestConfigPath)) { | |
| let pj = readFileSync(jestConfigPath, "utf8"); | |
| pj = pj.replace(/template-lib/g, newName); | |
| pj = pj.replace(/__template__/g, importPath); // adjust directory if needed | |
| writeFileSync(jestConfigPath, pj, "utf8"); | |
| } | |
| } | |
| /** ---- Templates ---- */ | |
| /** Route lib module */ | |
| function generateRouteModule(routeName, innerLibs) { | |
| return `import { NgModule } from '@angular/core'; | |
| import { RouterModule, Routes } from '@angular/router'; | |
| import { ${pascalCase(routeName)}Component } from './${routeName}.component'; | |
| const routes: Routes = [ | |
| { path: '', component: ${pascalCase(routeName)}Component }, | |
| ]; | |
| @NgModule({ | |
| imports: [ | |
| RouterModule.forChild(routes), | |
| ], | |
| }) | |
| export class ${pascalCase(routeName)}Module {} | |
| `; | |
| } | |
| /** Route component */ | |
| function generateRouteComponent(routeName, innerLibs) { | |
| const tags = innerLibs.map((lib) => `<app-${lib}></app-${lib}>`).join("\n "); | |
| return `import { Component } from '@angular/core'; | |
| ${innerLibs | |
| .map((lib) => `import { ${pascalCase(lib)}Component } from '@${CONFIG.appName}/components/${lib}';`) | |
| .join("\n")} | |
| @Component({ | |
| selector: 'app-${routeName}', | |
| template: \` | |
| <h2>${routeName} works!</h2> | |
| ${tags} | |
| \`, | |
| imports: [ | |
| ${innerLibs.map((lib) => `${pascalCase(lib)}Component`).join(", ")} | |
| ] | |
| }) | |
| export class ${pascalCase(routeName)}Component {} | |
| `; | |
| } | |
| /** Inner component (lib root component) */ | |
| function generateInnerComponent(compName, internalComps) { | |
| const tags = internalComps.map((ic) => `<app-${ic}></app-${ic}>`).join("\n "); | |
| const internals = internalComps.map((ic) => pascalCase(ic) + "Component").join(", "); | |
| return `import { Component } from '@angular/core'; | |
| import { CommonModule } from '@angular/common'; | |
| ${internalComps.map((ic) => `import { ${pascalCase(ic)}Component } from './${ic}.component';`).join( | |
| "\n" | |
| )} | |
| @Component({ | |
| selector: 'app-${compName}', | |
| template: \` | |
| <div class="${compName}"> | |
| <h3>${compName} works!</h3> | |
| ${tags} | |
| </div> | |
| \`, | |
| imports: [${internals}] | |
| }) | |
| export class ${pascalCase(compName)}Component {} | |
| `; | |
| } | |
| /** Internal component inside an inner lib */ | |
| function generateInternalComponent(name) { | |
| return `import { Component } from '@angular/core'; | |
| @Component({ | |
| selector: 'app-${name}', | |
| template: \` | |
| <div class="${name}"> | |
| <p>${name} internal component works!</p> | |
| </div> | |
| \` | |
| }) | |
| export class ${pascalCase(name)}Component {} | |
| `; | |
| } | |
| /** App routes update */ | |
| function updateAppRoutes(appName, routes) { | |
| const appRoutesPath = path.join(rootDir, "apps", appName, "src/app/app.routes.ts"); | |
| const content = `import { Route } from '@angular/router'; | |
| export const appRoutes: Route[] = [ | |
| ${routes | |
| .map( | |
| (r) => | |
| `{ path: '${r}', loadChildren: () => import('@${appName}/routes/${r}').then(m => m.${pascalCase( | |
| r | |
| )}Module) }` | |
| ) | |
| .join(",\n ")} | |
| ]; | |
| `; | |
| writeFileSyncRecursive(appRoutesPath, content); | |
| } | |
| /** Update tsconfig.base.json with new paths */ | |
| function updateTsconfigBase(appName, libs) { | |
| const tsconfigPath = path.join(rootDir, "tsconfig.base.json"); | |
| const tsconfig = JSON.parse(readFileSync(tsconfigPath, "utf8")); | |
| tsconfig.compilerOptions.paths = tsconfig.compilerOptions.paths || {}; | |
| for (const lib of libs) { | |
| const alias = `@${appName}/${lib}`; | |
| tsconfig.compilerOptions.paths[alias] = [`libs/${appName}/${lib}/src/index.ts`]; | |
| } | |
| writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2)); | |
| } | |
| /** ---- Generator ---- */ | |
| function generate() { | |
| const appName = CONFIG.appName; | |
| const routes = []; | |
| const allLibs = []; | |
| for (let r = 1; r <= CONFIG.routesPerApp; r++) { | |
| const routeName = `route${r}`; | |
| routes.push(routeName); | |
| const routeLibRel = `routes/${routeName}`; | |
| const routeLibRoot = path.join(rootDir, "libs", appName, routeLibRel); | |
| const routeLibDir = path.join(routeLibRoot, "src/lib"); | |
| // Copy Nx lib template | |
| copyTemplateLib(path.join(rootDir, "libs", appName, routeLibRel), routeName, routeLibRel); | |
| allLibs.push(routeLibRel); | |
| // inner component libs for this route | |
| const innerLibs = []; | |
| for (let c = 1; c <= CONFIG.componentsPerRoute; c++) { | |
| const compName = `${routeName}-cmp${c}`; | |
| const compLibRel = `components/${compName}`; | |
| const compLiRoot = path.join(rootDir, "libs", appName, compLibRel); | |
| const compLibDir = path.join(compLiRoot, "src/lib"); | |
| innerLibs.push(compName); | |
| allLibs.push(compLibRel); | |
| copyTemplateLib(path.join(rootDir, "libs", appName, compLibRel), compName, compLibRel); | |
| // internal components inside this component lib | |
| const internalComps = []; | |
| for (let ic = 1; ic <= CONFIG.internalPerComponent; ic++) { | |
| const icName = `${compName}-internal${ic}`; | |
| internalComps.push(icName); | |
| writeFileSyncRecursive( | |
| path.join(compLibDir, `${icName}.component.ts`), | |
| generateInternalComponent(icName) | |
| ); | |
| } | |
| // inner component (root of lib) | |
| writeFileSyncRecursive( | |
| path.join(compLibDir, `${compName}.component.ts`), | |
| generateInnerComponent(compName, internalComps) | |
| ); | |
| writeFileSync(path.join(compLiRoot, `src/index.ts`), `export * from './lib/${compName}.component'`) | |
| } | |
| // route component | |
| writeFileSyncRecursive( | |
| path.join(routeLibDir, `${routeName}.component.ts`), | |
| generateRouteComponent(routeName, innerLibs) | |
| ); | |
| // route module | |
| writeFileSyncRecursive( | |
| path.join(routeLibDir, `${routeName}.module.ts`), | |
| generateRouteModule(routeName, innerLibs) | |
| ); | |
| writeFileSync(path.join(routeLibRoot, `src/index.ts`), `export * from './lib/${routeName}.module'`) | |
| } | |
| updateAppRoutes(appName, routes); | |
| updateTsconfigBase(appName, allLibs); | |
| } | |
| generate(); | |
| console.log("✅ Done."); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment