Skip to content

Commit b5e1e60

Browse files
committed
Simplify and fix bugs in rebranding
1 parent 6bcea53 commit b5e1e60

File tree

2 files changed

+55
-152
lines changed

2 files changed

+55
-152
lines changed
Lines changed: 52 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,12 @@
11
/**
22
* ZeroPoint Template Rebranding Script
3-
* Declarative configuration with simple implementation.
3+
* Simple setup script to customize the template for a new project.
44
*/
55

66
import { existsSync, unlinkSync, readFileSync, writeFileSync } from 'fs';
77
import { execSync } from 'child_process';
88
import { createInterface } from 'readline';
99

10-
// Declarative configuration - all rebranding behavior defined as data
11-
const CONFIG = {
12-
markers: {
13-
templateFile: '.template',
14-
originalName: 'ZeroPoint'
15-
},
16-
17-
prompts: [
18-
{ key: 'projectName', question: 'Project name?', gitSource: 'repoName' },
19-
{ key: 'author', question: 'Your name/username?', gitSource: 'gitAuthor' },
20-
{ key: 'description', question: 'Project description (optional)?', optional: true },
21-
{ key: 'proceed', question: 'Ready to rebrand? (y/n)', defaultValue: 'y', validation: 'confirmation' }
22-
],
23-
24-
replacements: [
25-
{ search: 'ZeroPoint', replace: 'projectName' },
26-
{ search: 'MWDelaney', replace: 'author' },
27-
{ search: 'zeropoint', replace: 'projectName', transform: 'kebabCase' }
28-
],
29-
30-
fileOperations: [
31-
{ type: 'updateJson', file: 'package.json', updates: { description: 'description' }, condition: 'description' },
32-
{ type: 'copy', from: 'README.md', to: 'README.template.md' },
33-
{ type: 'copy', from: 'README.ZeroPoint.md', to: 'README.md' }
34-
],
35-
36-
findOptions: {
37-
fileTypes: ['*.md', '*.json', '*.js', '*.njk', '*.yml', '*.toml'],
38-
excludePaths: ['./node_modules/*', './.git/*']
39-
}
40-
};
41-
42-
/**
43-
* Detects Git information from the current repository
44-
*/
45-
function detectGitInfo() {
46-
try {
47-
const gitAuthor = execSync('git config user.name', { encoding: 'utf8' }).trim() || 'username';
48-
const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf8' }).trim();
49-
const match = remoteUrl.match(/[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
50-
51-
return {
52-
gitAuthor: gitAuthor === 'username' && match ? match[1] : gitAuthor,
53-
repoName: match ? match[2] : 'my-website'
54-
};
55-
} catch {
56-
console.warn('Could not detect git info, using defaults');
57-
return { gitAuthor: 'username', repoName: 'my-website' };
58-
}
59-
}
60-
61-
/**
62-
* Prompts user for input with default value
63-
*/
6410
async function askQuestion(question, defaultValue = '') {
6511
const rl = createInterface({ input: process.stdin, output: process.stdout });
6612
return new Promise(resolve => {
@@ -72,126 +18,81 @@ async function askQuestion(question, defaultValue = '') {
7218
});
7319
}
7420

75-
/**
76-
* Collects answers for all configured prompts
77-
*/
78-
async function collectAnswers(prompts, gitInfo) {
79-
const answers = {};
80-
81-
for (const prompt of prompts) {
82-
const defaultValue = prompt.gitSource ? gitInfo[prompt.gitSource] : prompt.defaultValue || '';
83-
const answer = await askQuestion(prompt.question, defaultValue);
84-
85-
// Apply validation
86-
if (prompt.validation === 'confirmation' && !['y', 'yes'].includes(answer.toLowerCase())) {
87-
console.log('Cancelled. Run npm run dev again to retry.');
88-
process.exit(0);
89-
}
21+
function getGitInfo() {
22+
try {
23+
const gitAuthor = execSync('git config user.name', { encoding: 'utf8' }).trim();
24+
const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf8' }).trim();
25+
const match = remoteUrl.match(/[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
9026

91-
answers[prompt.key] = answer;
27+
return {
28+
author: gitAuthor || 'username',
29+
repoName: match ? match[2] : 'my-website'
30+
};
31+
} catch {
32+
return { author: 'username', repoName: 'my-website' };
9233
}
93-
94-
return answers;
9534
}
9635

97-
/**
98-
* Performs find and replace across all relevant files
99-
*/
100-
function replaceInAllFiles(search, replace, findOptions) {
36+
function replaceInFiles(search, replace) {
10137
try {
102-
const fileTypePattern = findOptions.fileTypes.map(type => `-name "${type}"`).join(' -o ');
103-
const excludePattern = findOptions.excludePaths.map(path => `-not -path "${path}"`).join(' ');
104-
105-
execSync(`find . -type f \\( ${fileTypePattern} \\) ${excludePattern} -exec sed -i.bak "s/${search}/${replace}/g" {} + 2>/dev/null || true`);
106-
execSync(`find . -name "*.bak" ${excludePattern} -delete 2>/dev/null || true`);
38+
execSync(`find . -type f \\( -name "*.md" -o -name "*.json" -o -name "*.js" -o -name "*.njk" -o -name "*.yml" -o -name "*.toml" \\) -not -path "./node_modules/*" -not -path "./.git/*" -exec sed -i.bak "s/${search}/${replace}/g" {} + 2>/dev/null || true`);
39+
execSync(`find . -name "*.bak" -not -path "./node_modules/*" -not -path "./.git/*" -delete 2>/dev/null || true`);
10740
} catch (error) {
10841
console.warn(`Could not replace ${search}: ${error.message}`);
10942
}
11043
}
11144

112-
/**
113-
* Executes all configured replacements
114-
*/
115-
function executeReplacements(replacements, answers, findOptions) {
116-
console.log('\n🔄 Updating files...');
117-
118-
replacements.forEach(replacement => {
119-
let replaceValue = answers[replacement.replace];
120-
121-
// Apply transform if specified
122-
if (replacement.transform === 'kebabCase') {
123-
replaceValue = replaceValue.toLowerCase().replace(/\s+/g, '-');
124-
}
125-
126-
replaceInAllFiles(replacement.search, replaceValue, findOptions);
127-
});
128-
}
129-
130-
/**
131-
* Executes all configured file operations
132-
*/
133-
function executeFileOperations(operations, answers) {
134-
operations.forEach(operation => {
135-
try {
136-
// Skip if condition not met
137-
if (operation.condition && !answers[operation.condition]) {
138-
return;
139-
}
140-
141-
switch (operation.type) {
142-
case 'updateJson': {
143-
const content = JSON.parse(readFileSync(operation.file, 'utf8'));
144-
Object.entries(operation.updates).forEach(([key, answerKey]) => {
145-
if (answers[answerKey]) {
146-
content[key] = answers[answerKey];
147-
}
148-
});
149-
writeFileSync(operation.file, JSON.stringify(content, null, 2) + '\n', 'utf8');
150-
break;
151-
}
152-
153-
case 'copy': {
154-
if (existsSync(operation.from)) {
155-
execSync(`cp "${operation.from}" "${operation.to}"`);
156-
}
157-
break;
158-
}
159-
}
160-
} catch (error) {
161-
console.warn(`Could not perform ${operation.type} operation: ${error.message}`);
162-
}
163-
});
164-
}
165-
166-
/**
167-
* Main rebranding function - orchestrates the declarative configuration
168-
*/
16945
async function rebrand() {
170-
// Early exit if already rebranded
171-
if (!existsSync(CONFIG.markers.templateFile)) {
46+
// Exit if already rebranded
47+
if (!existsSync('.template')) {
17248
process.exit(0);
17349
}
17450

175-
// Safety check
51+
// Safety check - make sure we're in a ZeroPoint template
17652
const packageJson = JSON.parse(readFileSync('package.json', 'utf8'));
177-
if (packageJson.name !== CONFIG.markers.originalName) {
178-
unlinkSync(CONFIG.markers.templateFile);
53+
if (packageJson.name !== 'ZeroPoint') {
54+
unlinkSync('.template');
17955
process.exit(0);
18056
}
18157

18258
console.log('🪐 Welcome to ZeroPoint! Let\'s customize this for your project.\n');
18359

184-
// Execute configuration-driven workflow
185-
const gitInfo = detectGitInfo();
186-
const answers = await collectAnswers(CONFIG.prompts, gitInfo);
60+
// Get user input
61+
const gitInfo = getGitInfo();
62+
const projectName = await askQuestion('Project name?', gitInfo.repoName);
63+
const author = await askQuestion('Your name/username?', gitInfo.author);
64+
const description = await askQuestion('Project description (optional)?');
65+
const proceed = await askQuestion('Ready to rebrand? (y/n)', 'y');
66+
67+
if (!['y', 'yes'].includes(proceed.toLowerCase())) {
68+
console.log('Cancelled. Run npm run dev again to retry.');
69+
process.exit(0);
70+
}
71+
72+
console.log('\n🔄 Updating files...');
73+
74+
// Replace text in files
75+
replaceInFiles('ZeroPoint', projectName);
76+
replaceInFiles('MWDelaney', author);
77+
replaceInFiles('zeropoint', projectName.toLowerCase().replace(/\\s+/g, '-'));
18778

188-
executeReplacements(CONFIG.replacements, answers, CONFIG.findOptions);
189-
executeFileOperations(CONFIG.fileOperations, answers);
79+
// Update package.json
80+
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
81+
if (description) pkg.description = description;
82+
delete pkg.scripts['pre🪐']; // Remove the rebranding script
83+
writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
84+
85+
// Copy README files
86+
if (existsSync('README.md')) {
87+
execSync('cp README.md README.template.md');
88+
}
89+
if (existsSync('README.ZeroPoint.md')) {
90+
execSync('cp README.ZeroPoint.md README.md');
91+
}
19092

19193
// Clean up
192-
unlinkSync(CONFIG.markers.templateFile);
94+
unlinkSync('.template');
19395
console.log('✅ Rebranding complete! Starting development server...\n');
19496
}
19597

196-
// Run the rebranding
19798
await rebrand();

src/config/plugins.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export default {
3333
*/
3434
// async reusableComponents (eleventyConfig) {
3535
// // Add plugin to eleventyConfig
36-
// eleventyConfig.addPlugin(reusableComponents);
36+
// eleventyConfig.addPlugin(reusableComponents, {
37+
// componentsDir: "src/assets/components/*.njk"
38+
// });
3739
// }
3840
};

0 commit comments

Comments
 (0)