Project Structure
Loopar Framework uses a monorepo architecture with pnpm workspaces, organizing code into logical packages for maintainability and modularity.
Directory Overview
loopar-framework/
├── app/ # Application entry points
├── bin/ # CLI scripts and tools
├── packages/ # Monorepo packages
├── public/ # Static assets
├── sites/ # Multi-tenant site configurations
├── main.html # HTML template
├── package.json # Root package configuration
├── pnpm-workspace.yaml # Workspace configuration
├── tsconfig.json # TypeScript configuration
└── vite.config.js # Vite build configuration
Root Configuration Files
package.json
The root package.json defines the monorepo and available scripts:
{
"name" : "loopar-framework" ,
"version" : "5.0.1" ,
"type" : "module" ,
"engines" : {
"node" : ">=22.12.0"
},
"private" : true ,
"workspaces" : [
"packages/*"
],
"scripts" : {
"preinstall" : "node bin/installer-clean.js" ,
"postinstall" : "node bin/ensure-site.js" ,
"dev" : "node bin/loopar-cli.js dev && pm2 logs" ,
"start" : "node bin/loopar-cli.js start" ,
"stop" : "node bin/loopar-cli.js stop" ,
"restart" : "node bin/loopar-cli.js restart" ,
"logs" : "node bin/loopar-cli.js logs" ,
"list" : "node bin/loopar-cli.js list" ,
"build" : "node bin/build-clean.js && npm-run-all build:client build:server" ,
"build:client" : "vite build --outDir dist/client" ,
"build:server" : "vite build --outDir dist/server --ssr app/entry-server.jsx"
},
"bin" : {
"loopar" : "./bin/loopar-cli.js"
}
}
The framework requires Node.js 22.12.0+ and uses ES modules ("type": "module").
pnpm-workspace.yaml
This configuration tells pnpm to treat all directories in packages/ as workspace packages.
tsconfig.json
{
"compilerOptions" : {
"lib" : [ "dom" , "dom.iterable" , "esnext" ],
"allowJs" : true ,
"skipLibCheck" : true ,
"strict" : true ,
"forceConsistentCasingInFileNames" : true ,
"noEmit" : true ,
"incremental" : true ,
"esModuleInterop" : true ,
"module" : "esnext" ,
"moduleResolution" : "node" ,
"resolveJsonModule" : true ,
"isolatedModules" : true ,
"jsx" : "preserve" ,
"strictNullChecks" : true ,
"baseUrl" : "." ,
"paths" : {
"@/*" : [
"./src/*" ,
"./.loopar/*" ,
"./.loopar/service/*"
]
}
}
}
Key features:
Path aliases for clean imports (@/*)
Strict type checking enabled
JSX preservation for React
vite.config.js
export { default } from "vite-env" ;
The Vite configuration is exported from the vite-env package, allowing centralized build configuration.
App Directory
The app/ directory contains application entry points and routing:
app/
├── App.tsx # Main application component
├── Router.jsx # Routing configuration
├── entry-client.jsx # Client-side entry point
├── entry-server.jsx # Server-side entry point
└── src/ # Application-specific source
App.tsx
The main application component that sets up providers and workspace:
import { cn } from "@cn/lib/utils" ;
import React , { useEffect } from "react" ;
import { CookiesProvider } from '@services/cookie' ;
import { WorkspaceProvider } from "@workspace/workspace-provider" ;
import { useNavigate } from 'react-router' ;
const Main = ({ __META__ } : RootLayoutProps ) => {
const { components , Document } = __META__ ;
const { Workspace , View } = components ;
return (
< main className = { cn ( "h-full font-sans antialiased" ) } >
< div className = "relative flex flex-col" >
< div className = "flex-1" translate = "yes" >
< WorkspaceProvider
__META__ = { __META__ }
Documents = { {
[Document.name]: {
... __META__ ,
View ,
active: true ,
}
} }
>
< Workspace menuData = { __META__ . menu_data } />
</ WorkspaceProvider >
</ div >
</ div >
</ main >
);
}
const App = ({ __META__ } : RootLayoutProps ) => {
const { cookieManager } = __META__ . services ;
return (
< CookiesProvider manager = { cookieManager } updater = { setUpdate } >
< Main __META__ = { __META__ } />
</ CookiesProvider >
)
}
export default App ;
Key responsibilities:
Initialize providers (Cookies, Workspace)
Set up routing and navigation
Manage application metadata (__META__)
Handle authentication state
Entry Points
entry-client.jsx : Hydrates the React app on the client
entry-server.jsx : Handles server-side rendering
These enable Loopar’s SSR capabilities for improved performance and SEO.
Binary Scripts (bin/)
The bin/ directory contains CLI tools for managing the framework:
bin/
├── loopar-cli.js # Main CLI interface
├── loopar-status.js # Process status display
├── loopar.ecosystem.config.mjs # PM2 configuration
├── ensure-site.js # Site creation utility
├── installer-clean.js # Cleanup script
└── build-clean.js # Build cleanup
loopar-cli.js
The primary CLI tool with these commands:
dev Start development server with PM2 and log tailing
start Start production server for specified site or all sites
stop Stop running processes
restart Restart processes without downtime
delete Remove processes from PM2
logs Stream application logs
list/status Display running process status
#!/usr/bin/env node
import { execSync } from 'child_process' ;
import path from 'path' ;
import chalk from 'chalk' ;
const projectPath = process . cwd ();
const projectName = path . basename ( projectPath );
function getProcessName ( siteName ) {
return ` ${ projectName } - ${ siteName } ` ;
}
const namespace = ` ${ projectName } -` ;
const commands = {
dev () {
console . log ( chalk . cyan ( `Starting ${ namespace } core site.` ));
pm2Command ( `node bin/ensure-site.js && pm2 start bin/loopar.ecosystem.config.mjs --namespace ${ namespace } --silent && node bin/loopar-status.js --env development` );
},
start ( siteName ) {
console . log ( chalk . cyan ( 'Starting core site.' ));
pm2Command ( `node bin/ensure-site.js && pm2 start bin/loopar.ecosystem.config.mjs --namespace ${ namespace } --silent && node bin/loopar-status.js --env production` );
},
// ... more commands
};
const [,, command , siteName ] = process . argv ;
commands [ command ]( siteName );
ensure-site.js
Automatically creates development site structure:
async function createDevSite () {
const siteName = 'dev' ;
const port = process . env . PORT || 3000 ;
const sitePath = path . join ( process . cwd (), 'sites' , siteName );
await fs . mkdir ( sitePath , { recursive: true });
await fs . mkdir ( path . join ( sitePath , 'sessions' ), { recursive: true });
await fs . mkdir ( path . join ( sitePath , 'config' ), { recursive: true });
await fs . mkdir ( path . join ( sitePath , 'public' , 'uploads' ), { recursive: true });
const envContent = `PORT= ${ port }
NAME= ${ siteName }
TENANT_ID= ${ siteName }
NODE_ENV=development
` ;
await fs . writeFile ( path . join ( sitePath , '.env' ), envContent );
console . log ( `✅ Dev site created: sites/ ${ siteName } ` );
console . log ( ` URL: http://localhost: ${ port } \n ` );
}
Packages Directory
The heart of Loopar’s modular architecture:
packages/
├── loopar/ # Core framework
├── vite-env/ # Vite build configuration
├── server-env/ # Express server utilities
├── db-env/ # Database and ORM
├── react-env/ # React and UI libraries
├── shadcn/ # Shadcn UI components
├── builder/ # Build tools
├── markdown/ # Markdown editor
├── radix-env/ # Radix UI primitives
├── tailwind-env/ # Tailwind CSS config
├── types-env/ # TypeScript types
└── shared/ # Shared utilities
loopar (Core Package)
The main framework package:
packages/loopar/
├── package.json
├── bin/
│ └── loopar-cli.js
├── src/
│ ├── components/ # UI component library
│ ├── workspace/ # Workspace management
│ ├── context/ # React contexts
│ ├── tools/ # Utilities
│ ├── loopar.jsx # Core framework
│ ├── loader.jsx # Module loader
│ └── components-loader.jsx # Component registry
packages/loopar/package.json
{
"name" : "loopar" ,
"version" : "2.0.6" ,
"type" : "module" ,
"bin" : {
"loopar" : "./bin/loopar-cli.js"
},
"dependencies" : {
"pm2" : "6.0.13" ,
"@pm2/io" : "6.1.0" ,
"cli-table3" : "0.6.5" ,
"inquirer" : "^9.2.0" ,
"ora" : "^7.0.0" ,
"chalk" : "^5.3.0" ,
"es-toolkit" : "v1.43.0"
}
}
Components
All UI components are in packages/loopar/src/components/:
components/
├── base/ # Base component classes
│ ├── component.jsx
│ ├── ComponentDefaults.jsx
│ └── ...
├── button.jsx
├── card.jsx
├── checkbox.jsx
├── date.jsx
├── date-time.jsx
├── designer/ # Visual designer
├── dialog.jsx
├── input.jsx
├── table/
└── ...
Each component follows this pattern:
import { ComponentUI } from "@cn/components/ui/component" ;
import loopar from "loopar" ;
export default function MetaComponent ( props ) {
const data = props . data || {};
return (
< ComponentUI
{ ... loopar . utils . renderizableProps ( props ) }
// component-specific props
>
{ /* content */ }
</ ComponentUI >
);
}
// Optional: Define configurable fields
MetaComponent . metaFields = () => {
return [ /* field definitions */ ];
};
vite-env
Centralized Vite configuration:
packages/vite-env/package.json
{
"name" : "vite-env" ,
"version" : "1.0.0" ,
"type" : "module" ,
"devDependencies" : {
"@vitejs/plugin-react" : "5.1.2" ,
"vite" : "7.3.1" ,
"express" : "5.2.1" ,
"vite-plugin-preload" : "0.4.4" ,
"vite-plugin-svgr" : "4.5.0" ,
"vite-plugin-compression2" : "2.4.0"
}
}
server-env
Server middleware and utilities:
packages/server-env/package.json
{
"name" : "server-env" ,
"version" : "1.0.0" ,
"type" : "module" ,
"dependencies" : {
"express-session" : "1.18.2" ,
"express-useragent" : "2.0.2" ,
"cookie-parser" : "1.4.7" ,
"js-cookie" : "3.0.5" ,
"universal-cookie" : "8.0.1" ,
"multer" : "2.0.2"
}
}
db-env
Database abstraction layer:
packages/db-env/package.json
{
"name" : "db-env" ,
"version" : "1.0.0" ,
"type" : "module" ,
"dependencies" : {
"@sequelize/core" : "7.0.0-alpha.47" ,
"@sequelize/mariadb" : "7.0.0-alpha.47" ,
"@sequelize/mysql" : "7.0.0-alpha.47" ,
"@sequelize/sqlite3" : "7.0.0-alpha.47" ,
"sequelize" : "v7.0.0-next.1"
}
}
Supports:
MySQL : Production-ready relational database
MariaDB : MySQL-compatible alternative
SQLite : Development and embedded scenarios
react-env
React and UI libraries:
packages/react-env/package.json
{
"dependencies" : {
"react" : "19.2.3" ,
"react-dom" : "19.2.3" ,
"react-router" : "^7.2.0" ,
"lucide-react" : "^0.545.0" ,
"@tinymce/tinymce-react" : "6.3.0"
}
}
Sites Directory
Multi-tenant site configurations:
sites/
└── dev/ # Development site
├── .env # Environment variables
├── config/ # Site-specific configuration
├── sessions/ # User sessions
└── public/
└── uploads/ # Uploaded files
Site Configuration
Each site has its own .env file:
PORT = 3000
NAME = dev
TENANT_ID = dev
NODE_ENV = development
Each site can have different database connections, ports, and configurations, enabling true multi-tenancy.
Public Directory
Static assets served directly:
public/
├── images/ # Image assets
└── ... # Other static files
Build Output
When you run pnpm run build, the output structure is:
dist/
├── client/ # Client-side bundle
│ ├── assets/
│ └── index.html
└── server/ # SSR bundle
└── entry-server.js
Import Aliases
Loopar uses path aliases for cleaner imports:
Alias Resolves To Usage @/*./src/*Application source @cncn packageShadcn UI components @contextloopar/src/contextReact contexts @workspaceloopar/src/workspaceWorkspace components @droppableComponent droppable Designer droppables @servicesServices Shared services looparloopar packageCore framework
Example:
// Instead of:
import Button from '../../../packages/loopar/src/components/button.jsx' ;
// Use:
import Button from '@/components/button.jsx' ;
import { cn } from '@cn/lib/utils' ;
import loopar from 'loopar' ;
Key Concepts
Code Sharing : Easy to share code between packages
Atomic Changes : Update multiple packages in one commit
Dependency Management : Centralized version control
Build Optimization : Shared build configuration
Packages can depend on each other: {
"dependencies" : {
"loopar" : "workspace:*" ,
"cn" : "workspace:*"
}
}
The workspace:* protocol tells pnpm to use local packages.
Components and modules are dynamically loaded:
Reduces initial bundle size
Enables lazy loading
Supports hot module replacement
Multi-Tenancy Architecture
Each site is isolated:
Separate configuration
Independent databases
Isolated sessions
Own process with PM2
Development Guidelines
Adding New Packages
Create a new package: mkdir packages/my-package
cd packages/my-package
npm init -y
Update package.json: {
"name" : "my-package" ,
"version" : "1.0.0" ,
"type" : "module"
}
Creating Components
Add components to packages/loopar/src/components/: export default function MyComponent ( props ) {
return < div > { /* component */ } </ div > ;
}
Register in components-loader.jsx.
Modifying Configuration
Build : Edit packages/vite-env
TypeScript : Update root tsconfig.json
ESLint : Modify .eslintrc.json
Prettier : Edit prettier.config.js
Next Steps
Components Explore the component library in detail
Database Learn about data modeling and ORM usage
API Reference Detailed API documentation
CLI Commands Learn about CLI commands for deployment