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
66import { existsSync , unlinkSync , readFileSync , writeFileSync } from 'fs' ;
77import { execSync } from 'child_process' ;
88import { 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 ( / [: / ] ( [ ^ / ] + ) \/ ( [ ^ / ] + ?) (?: \. g i t ) ? $ / ) ;
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- */
6410async 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 ( / [: / ] ( [ ^ / ] + ) \/ ( [ ^ / ] + ?) (?: \. g i t ) ? $ / ) ;
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- */
16945async 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
19798await rebrand ( ) ;
0 commit comments