@codehance/rapid-stack
Version:
A modern full-stack development toolkit for rapid application development
213 lines (179 loc) • 7.05 kB
JavaScript
const Generator = require('yeoman-generator');
const fs = require('fs');
const path = require('path');
const rimraf = require('rimraf');
class RemovePlatformGenerator extends Generator {
constructor(args, opts) {
super(args, opts);
this.frontendPath = path.resolve(this.destinationPath(), './frontend/src/app/platforms');
}
_toKebabCase(str) {
return str
// Convert camelCase to kebab-case (handle uppercase letters)
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
// Replace any non-alphanumeric characters with hyphens
.replace(/[^a-zA-Z0-9]+/g, '-')
// Convert to lowercase
.toLowerCase()
// Remove leading/trailing hyphens
.replace(/^-+|-+$/g, '');
}
_toCamelCase(str) {
// First convert to kebab case (handle spaces, underscores, etc)
const kebabCase = str
.toLowerCase()
.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
.replace(/[^a-zA-Z0-9]/g, '');
// Then ensure first character is lowercase
return kebabCase.charAt(0).toLowerCase() + kebabCase.slice(1);
}
_capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
_getPlatforms() {
try {
return fs.readdirSync(this.frontendPath)
.filter(item => {
const fullPath = path.join(this.frontendPath, item);
return fs.statSync(fullPath).isDirectory();
})
.map(dir => ({
name: this._capitalize(dir),
value: dir
}));
} catch (error) {
return [];
}
}
async prompting() {
const platformChoices = this._getPlatforms();
if (platformChoices.length === 0) {
this.log('No platforms found in the frontend directory');
return;
}
const answers = await this.prompt([
{
type: 'list',
name: 'selectedPlatform',
message: 'Which platform would you like to remove?',
choices: platformChoices,
},
{
type: 'confirm',
name: 'confirmRemoval',
message: answers => `Are you sure you want to remove the ${answers.selectedPlatform} platform? This action cannot be undone.`,
default: false
}
]);
this.answers = answers;
}
_removeFromAppRoutes(platformName) {
const appRoutesPath = path.join(process.cwd(), 'frontend/src/app/app.routes.ts');
if (!fs.existsSync(appRoutesPath)) {
this.log.error('app.routes.ts not found');
return;
}
try {
let content = fs.readFileSync(appRoutesPath, 'utf8');
let routesRemoved = 0;
// Special case for "my-section" which we know exists in the routes
if (platformName.toLowerCase() === "mysection") {
this.log(`Special case handling for 'my-section' route`);
// Look for the route with 'my-section' path
if (content.includes(`path: 'my-section'`)) {
this.log(`Found hardcoded path: 'my-section'`);
// Extract the route containing 'my-section'
const parts = content.split(`path: 'my-section'`);
if (parts.length >= 2) {
// Find the closing brace for this route
let openBraces = 1;
let closingPos = 0;
const routePart = parts[1];
for (let i = 0; i < routePart.length; i++) {
if (routePart[i] === '{') openBraces++;
if (routePart[i] === '}') openBraces--;
if (openBraces === 0) {
closingPos = i;
break;
}
}
// Get the full route
const fullRoute = ` {\n path: 'my-section'${routePart.substring(0, closingPos+1)}`;
this.log(`Removing route: ${fullRoute}`);
// Remove this route
content = content.replace(fullRoute, '');
// Clean up the file
const cleanedContent = this._cleanupRoutesFile(content);
fs.writeFileSync(appRoutesPath, cleanedContent, 'utf8');
this.log.ok(`Removed 'my-section' route from app.routes.ts`);
return;
}
}
}
// If we didn't handle the special case, resort to regex approach
const kebabPath = this._toKebabCase(platformName);
this.log(`Trying to remove route with path: '${kebabPath}'`);
// Look for any routes with the kebab path
const routeRegex = new RegExp(`\\s*{[^{]*?path:\\s*['"]${kebabPath}['"][^}]*?},?`, 'g');
// Remove matching routes
const newContent = content.replace(routeRegex, '');
if (newContent !== content) {
// We found and removed at least one route
const cleanedContent = this._cleanupRoutesFile(newContent);
fs.writeFileSync(appRoutesPath, cleanedContent, 'utf8');
this.log.ok(`Removed route for '${kebabPath}' from app.routes.ts`);
} else {
this.log.error(`Could not find any routes with path: '${kebabPath}' in app.routes.ts`);
}
} catch (error) {
this.log.error(`Error updating app.routes.ts: ${error.message}`);
}
}
_cleanupRoutesFile(content) {
// First, normalize line endings and remove extra whitespace
let cleaned = content.replace(/\r\n/g, '\n').replace(/\s+\n/g, '\n');
// Fix route array formatting
cleaned = cleaned
// Remove empty lines between routes
.replace(/\n\s*\n/g, '\n')
// Fix multiple commas
.replace(/,\s*,/g, ',')
// Fix trailing comma before closing bracket
.replace(/,(\s*)\]/g, '\n]')
// Fix leading comma after opening brace
.replace(/{\s*,/g, '{')
// Fix leading comma after opening bracket
.replace(/\[\s*,/g, '[')
// Fix trailing comma before closing brace
.replace(/,(\s*)\}/g, '\n }')
// Ensure proper spacing around routes array
.replace(/(\nexport const routes: Routes = \[)/, '\n$1\n')
.replace(/\];(?!\n)/, '\n];')
// Ensure proper indentation
.replace(/^( {0,2}){(?!\s*$)/gm, ' {')
// Remove extra newlines at the end
.replace(/\n+$/, '\n');
return cleaned;
}
writing() {
if (!this.answers.selectedPlatform || !this.answers.confirmRemoval) {
this.log('Platform removal cancelled');
return;
}
const platformPath = path.join(this.frontendPath, this.answers.selectedPlatform);
try {
// Remove the platform directory
if (fs.existsSync(platformPath)) {
rimraf.sync(platformPath);
this.log(`Successfully removed ${this.answers.selectedPlatform} platform directory`);
// Remove the platform's route from app.routes.ts
this._removeFromAppRoutes(this.answers.selectedPlatform);
} else {
this.log(`Platform directory not found: ${this.answers.selectedPlatform}`);
}
} catch (error) {
this.log(`Error during platform removal: ${error.message}`);
}
}
}
module.exports = RemovePlatformGenerator;