@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
281 lines (269 loc) • 9.38 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_MONOREPO_STRUCTURE = void 0;
exports.initializeMonorepo = initializeMonorepo;
exports.getWorkspaces = getWorkspaces;
exports.isMonorepoRoot = isMonorepoRoot;
exports.findMonorepoRoot = findMonorepoRoot;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const yaml_1 = __importDefault(require("yaml"));
const glob_1 = require("glob");
exports.DEFAULT_MONOREPO_STRUCTURE = {
apps: 'apps',
packages: 'packages',
libs: 'libs',
tools: 'tools',
docs: 'docs',
};
async function initializeMonorepo(name, packageManager = 'pnpm', customStructure) {
const structure = { ...exports.DEFAULT_MONOREPO_STRUCTURE, ...customStructure };
const projectPath = path.resolve(process.cwd(), name);
// Create root directory
await fs.ensureDir(projectPath);
// Create apps directory (main directory for microfrontend apps)
await fs.ensureDir(path.join(projectPath, structure.apps));
const workspaces = [
`${structure.apps}/*`,
`${structure.packages}/*`,
`${structure.libs}/*`,
`${structure.tools}/*`,
];
// Create root package.json
const packageJson = {
name,
version: '0.1.0',
description: `${name} - A multi-framework monorepo`,
private: true,
workspaces: packageManager === 'npm' ? { packages: workspaces } : workspaces,
scripts: {
dev: `${packageManager} run --parallel -r dev`,
build: `${packageManager} run --parallel -r build`,
lint: `${packageManager} run --parallel -r lint`,
test: `${packageManager} run --parallel -r test`,
clean: `${packageManager} run --parallel -r clean`,
'type-check': `${packageManager} run --parallel -r type-check`,
'workspace:list': 're-shell workspace list',
'workspace:graph': 're-shell workspace graph',
'workspace:update': 're-shell workspace update',
},
devDependencies: {
'@re-shell/cli': '^0.2.5',
},
engines: {
node: '>=16.0.0',
},
};
await fs.writeFile(path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2));
// Create workspace configuration
if (packageManager === 'pnpm') {
const pnpmWorkspace = {
packages: workspaces,
};
await fs.writeFile(path.join(projectPath, 'pnpm-workspace.yaml'), yaml_1.default.stringify(pnpmWorkspace));
}
else if (packageManager === 'yarn') {
const yarnWorkspace = {
workspaces: workspaces,
};
await fs.writeFile(path.join(projectPath, 'package.json'), JSON.stringify({ ...packageJson, ...yarnWorkspace }, null, 2));
}
// Create .gitignore
const gitignore = `# Dependencies
node_modules/
.pnp
.pnp.js
# Production builds
dist/
build/
.next/
.nuxt/
.output/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Temporary folders
tmp/
temp/
# Cache
.cache/
.parcel-cache/
.eslintcache
.stylelintcache
# TypeScript
*.tsbuildinfo
`;
await fs.writeFile(path.join(projectPath, '.gitignore'), gitignore);
return {
name,
packageManager,
workspaces,
structure,
};
}
async function getWorkspaces(rootPath = process.cwd()) {
const packageJsonPath = path.join(rootPath, 'package.json');
if (!fs.existsSync(packageJsonPath)) {
throw new Error('Not in a monorepo root (package.json not found)');
}
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
let workspacePatterns = [];
// Extract workspace patterns
if (packageJson.workspaces) {
if (Array.isArray(packageJson.workspaces)) {
workspacePatterns = packageJson.workspaces;
}
else if (packageJson.workspaces.packages) {
workspacePatterns = packageJson.workspaces.packages;
}
}
// Check for pnpm-workspace.yaml
const pnpmWorkspacePath = path.join(rootPath, 'pnpm-workspace.yaml');
if (fs.existsSync(pnpmWorkspacePath)) {
const pnpmWorkspace = yaml_1.default.parse(await fs.readFile(pnpmWorkspacePath, 'utf8'));
if (pnpmWorkspace.packages) {
workspacePatterns = pnpmWorkspace.packages;
}
}
const workspaces = [];
// Find all workspace directories
for (const pattern of workspacePatterns) {
const matches = glob_1.glob.sync(pattern, { cwd: rootPath });
for (const match of matches) {
const workspacePath = path.join(rootPath, match);
const workspacePackageJson = path.join(workspacePath, 'package.json');
if (fs.existsSync(workspacePackageJson)) {
try {
const workspacePackage = JSON.parse(await fs.readFile(workspacePackageJson, 'utf8'));
// Determine workspace type based on path
let type = 'package';
if (match.startsWith('apps/'))
type = 'app';
else if (match.startsWith('libs/'))
type = 'lib';
else if (match.startsWith('tools/'))
type = 'tool';
// Detect framework
const framework = detectFrameworkFromPackage(workspacePackage);
workspaces.push({
name: workspacePackage.name || path.basename(match),
path: match,
type,
framework,
version: workspacePackage.version || '0.0.0',
dependencies: Object.keys({
...workspacePackage.dependencies,
...workspacePackage.devDependencies,
}),
});
}
catch (error) {
console.warn(`Failed to parse package.json for ${match}:`, error);
}
}
}
}
return workspaces;
}
function detectFrameworkFromPackage(packageJson) {
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
if (deps['@angular/core'])
return 'angular';
if (deps['vue'])
return deps['typescript'] ? 'vue-ts' : 'vue';
if (deps['svelte'])
return deps['typescript'] ? 'svelte-ts' : 'svelte';
if (deps['react'])
return deps['typescript'] ? 'react-ts' : 'react';
return undefined;
}
async function isMonorepoRoot(dirPath = process.cwd()) {
const packageJsonPath = path.join(dirPath, 'package.json');
if (!fs.existsSync(packageJsonPath)) {
return false;
}
try {
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
return !!(packageJson.workspaces || fs.existsSync(path.join(dirPath, 'pnpm-workspace.yaml')));
}
catch {
return false;
}
}
async function findMonorepoRoot(startPath = process.cwd()) {
let currentPath = path.resolve(startPath);
const rootPath = path.parse(currentPath).root;
let depth = 0;
const maxDepth = 10; // Prevent searching too far up the filesystem
while (currentPath !== rootPath && depth < maxDepth) {
if (await isMonorepoRoot(currentPath)) {
return currentPath;
}
currentPath = path.dirname(currentPath);
depth++;
}
return null;
}