
Security News
PolinRider: North Korea-Linked Supply Chain Campaign Expands Across Open Source Ecosystems
PolinRider expands across npm, Packagist, Go modules, and Chrome extensions, using hidden loaders to target developer environments.
📁 A powerful, type-safe library for programmatically generating project structures with templates
npm install project-builder
# or
yarn add project-builder
# or
pnpm add project-builder
import { projectBuild } from 'project-builder';
async function main() {
// Define your project structure
const structure = {
'src': {
'components': {
Button$tsx: 'export const Button = () => <button>Click me</button>;',
'index$ts': 'export * from "./Button";'
},
'utils': {
helpers$ts: 'export const sum = (a: number, b: number) => a + b;'
},
index$ts: 'export * from "./components";'
},
package$json: JSON.stringify({
name: "{{projectName}}",
version: "1.0.0"
}, null, 2)
};
// Build the project with template variables
await projectBuild(structure, './my-project', {
projectName: 'awesome-app'
});
console.log('🎉 Project successfully generated!');
}
main().catch(console.error);
projectBuild(structure, rootPath, variables)🛠️ Creates a project structure based on a configuration object.
import { projectBuild } from 'project-builder';
const result = await projectBuild(
{
'src': { /* folders and files */ },
'README.md': '# My Project'
},
'./output',
{ version: '1.0.0' }
);
getFolder(folderName)📂 Retrieves the path of a folder by name.
import { getFolder } from 'project-builder';
const componentsPath = getFolder('components');
// -> '/path/to/project/src/components'
getFile(fileName)📄 Retrieves the path of a file by name.
import { getFile } from 'project-builder';
const readmePath = getFile('README.md');
// -> '/path/to/project/README.md'
getPath(name)🔍 Retrieves the path of a file or folder by name.
import { getPath } from 'project-builder';
const path = getPath('Button.tsx');
// -> '/path/to/project/src/components/Button.tsx'
findFile(fileName, directory?)🔎 Recursively searches for a file starting from a directory.
import { findFile } from 'project-builder';
const configPath = await findFile('config.json');
// -> '/path/to/project/config.json' (if found)
The ProjectBuilder class uses the singleton pattern and manages the internal state of your project.
import { ProjectBuilder } from 'project-builder';
const builder = ProjectBuilder.getInstance();
const tsFiles = builder.getFilesByExtension('ts');
getFilesByExtension(extension) - 🏷️ Returns all files with the specified extensiongetFilesInFolder(folderName) - 📁 Returns all files in a specific folderProject Builder supports powerful templating with variables that can be used in both file/folder names and content using the {{variableName}} syntax.
Variables are defined in the third parameter of projectBuild() and then referenced in your structure definition:
const structure = {
'src': {
'{{componentName}}$tsx': 'export const {{componentName}} = () => <div>Hello</div>;'
}
};
await projectBuild(structure, './output', {
componentName: 'Header'
});
// Creates: ./output/src/Header.tsx with content: export const Header = () => <div>Hello</div>;
The TemplateVariables type supports various primitive values:
type TemplateVariables = Record<string, string | number | boolean>;
// Example
await projectBuild(structure, './output', {
appName: 'MyAwesomeApp', // string
version: 1.0, // number
isProduction: true, // boolean
port: 3000 // number
});
Variables can be used in any part of a path:
const structure = {
'{{srcFolder}}': {
'{{componentFolder}}': {
'{{prefix}}Button$tsx': '// {{copyright}}'
}
}
};
await projectBuild(structure, './output', {
srcFolder: 'src',
componentFolder: 'components',
prefix: 'UI',
copyright: '© 2025 Example Corp'
});
// Creates: ./output/src/components/UIButton.tsx
Variables shine when generating dynamic file content:
const structure = {
'package$json': JSON.stringify({
name: "{{projectName}}",
version: "{{version}}",
description: "{{description}}",
scripts: {
"start": "node dist/index.js",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc",
"test": "{{testCommand}}"
},
dependencies: {{dependencies}}
}, null, 2)
};
await projectBuild(structure, './output', {
projectName: 'api-service',
version: '0.1.0',
description: 'Backend API service',
testCommand: 'jest --coverage',
dependencies: JSON.stringify({
"express": "^4.18.2",
"dotenv": "^16.0.3"
})
});
Generate configuration files with environment-specific settings:
function generateConfig(variables) {
const env = variables.environment;
let dbConfig = {};
if (env === 'development') {
dbConfig = {
host: 'localhost',
port: 5432
};
} else if (env === 'production') {
dbConfig = {
host: 'db.example.com',
port: 5432,
ssl: true
};
}
return JSON.stringify({
appName: variables.appName,
port: variables.port,
database: dbConfig,
logLevel: env === 'development' ? 'debug' : 'info'
}, null, 2);
}
const structure = {
'config$json': generateConfig
};
await projectBuild(structure, './my-app', {
appName: 'MyService',
environment: 'development',
port: 3000
});
Variables can be mixed with dynamic content generation:
const structure = {
'src': {
'models': {
'{{entityName}}$ts': () => {
// This function has access to the variables via closure
return `export interface {{entityName}} {
id: string;
name: string;
createdAt: Date;
{{customFields}}
}`;
}
}
}
};
await projectBuild(structure, './api', {
entityName: 'User',
customFields: `
email: string;
password: string;
role: 'admin' | 'user';`
});
For complex structures, consider pre-processing with variables:
function generateRouteFiles(routes) {
const files = {};
routes.forEach(route => {
const fileName = `${route.name}Route$ts`;
files[fileName] = `import { Router } from 'express';
const router = Router();
router.get('/${route.path}', (req, res) => {
res.json({ message: '${route.description}' });
});
export default router;`;
});
return files;
}
const routes = [
{ name: 'user', path: 'users', description: 'Get all users' },
{ name: 'product', path: 'products', description: 'Get all products' }
];
const structure = {
'src': {
'routes': generateRouteFiles(routes)
}
};
await projectBuild(structure, './api');
The library supports special formatting for file names:
const structure = {
// Using $ for extensions
'tsconfig$json': '{ "compilerOptions": {} }',
// Using quotes for names with special characters
'"README.md"': '# Project Documentation'
};
import { projectBuild, getFilesByExtension } from 'project-builder';
// Build project
const builder = await projectBuild(structure, './output');
// Get all TypeScript files
const tsFiles = builder.getFilesByExtension('ts');
// Get all files in the components folder
const componentFiles = builder.getFilesInFolder('components');
const reactProject = {
'src': {
'components': {
App$tsx: `import React from 'react';
export const App = () => <div>Hello World</div>;`,
index$ts: 'export * from "./App";'
},
index$tsx: `import React from 'react';
import ReactDOM from 'react-dom';
import { App } from './components';
ReactDOM.render(<App />, document.getElementById('root'));`
},
'public': {
'index.html': `<!DOCTYPE html>
<html>
<head>
<title>{{projectName}}</title>
</head>
<body>
<div id="root"></div>
</body>
</html>`
},
package$json: JSON.stringify({
name: "{{projectName}}",
version: "{{version}}",
dependencies: {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}, null, 2)
};
project-builder/
├── src/
│ ├── index.ts # Main entry point
│ ├── ProjectBuilder.ts # Core builder class
│ ├── types.ts # Type definitions
│ └── utils.ts # Utility functions
├── package.json
└── README.md
Contributions, issues and feature requests are welcome!
MIT © 🏗️ [Hicham Jebara].
FAQs
Library for building project structures with dynamic folder/file names
The npm package forji receives a total of 14 weekly downloads. As such, forji popularity was classified as not popular.
We found that forji demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
PolinRider expands across npm, Packagist, Go modules, and Chrome extensions, using hidden loaders to target developer environments.

Security News
Open source attacks are accelerating as AI coding agents pull in dependencies faster, with less human review.

Research
/Security News
Malicious Chrome and Firefox extensions posed as free VPNs while stealing clipboard data through later extension updates.