Skip to content

Instantly share code, notes, and snippets.

@dmitry-stepanenko
Created September 15, 2025 09:04
Show Gist options
  • Select an option

  • Save dmitry-stepanenko/cf0d1e81bc1a214fb932ab8991077a9b to your computer and use it in GitHub Desktop.

Select an option

Save dmitry-stepanenko/cf0d1e81bc1a214fb932ab8991077a9b to your computer and use it in GitHub Desktop.
nx large mock repo seed
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