Skip to content

Commit

Permalink
Merge pull request #62 from rootstrap/refactor/cli_move_file_operatio…
Browse files Browse the repository at this point in the history
…ns_to_project_files_manager_class

refactor(cli): move file operations to ProjectFilesManager class
  • Loading branch information
asdolo authored Sep 10, 2024
2 parents 28184b1 + 29594e4 commit 92d09e7
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 70 deletions.
4 changes: 2 additions & 2 deletions cli/clone-repo.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const getLatestRelease = async () => {
}
};

const cloneLastTemplateRelease = async (projectName) => {
const cloneLatestTemplateRelease = async (projectName) => {
consola.start('Extracting last release number 👀');
const latest_release = await getLatestRelease();
consola.info(`Using Rootstrap's Template ${latest_release}`);
Expand All @@ -31,5 +31,5 @@ const cloneLastTemplateRelease = async (projectName) => {
};

module.exports = {
cloneLastTemplateRelease,
cloneLatestTemplateRelease,
};
12 changes: 6 additions & 6 deletions cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

const { consola } = require('consola');
const { showMoreDetails } = require('./utils.js');
const { cloneLastTemplateRelease } = require('./clone-repo.js');
const { setupProject, installDeps } = require('./setup-project.js');
const { cloneLatestTemplateRelease } = require('./clone-repo.js');
const { setupProject, installDependencies } = require('./setup-project.js');
const pkg = require('./package.json');

const { name: packageName } = pkg;
Expand All @@ -18,14 +18,14 @@ const createRootstrapApp = async () => {
);
process.exit(1);
}
// clone the last release of the template from github
await cloneLastTemplateRelease(projectName);
// clone the latest release of the template from github
await cloneLatestTemplateRelease(projectName);

// setup the project: remove unnecessary files, update package.json infos, name and set version to 0.0.1 + add initial version to osMetadata
// setup the project
await setupProject(projectName);

// install project dependencies using pnpm
await installDeps(projectName);
await installDependencies(projectName);

// show instructions to run the project + link to the documentation
showMoreDetails(projectName);
Expand Down
102 changes: 102 additions & 0 deletions cli/project-files-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const fs = require('fs-extra');
const path = require('path');

class ProjectFilesManager {
#projectName;

constructor(
/**
* @type {string}
*/
projectName
) {
this.#projectName = projectName;
}

static withProjectName(
/**
* @type {string}
*/
projectName
) {
return new this(projectName);
}

getAbsoluteFilePath(
/**
* A file path relative to project's root path
* @type {string}
*/
relativeFilePath
) {
return path.join(process.cwd(), `${this.#projectName}/${relativeFilePath}`);
}

removeFiles(
/**
* An array of file paths relative to project's root path
* @type {Array<string>}
*/
files
) {
files.forEach((fileName) => {
const absoluteFilePath = this.getAbsoluteFilePath(fileName);

fs.removeSync(absoluteFilePath);
});
}

renameFiles(
/**
* An array of objects containing the old and the new file names
* relative to project's root path
*
* @type {Array<{
* oldFileName: string;
* newFileName: string;
* }>}
*/
files
) {
files.forEach(({ oldFileName, newFileName }) => {
const oldAbsoluteFilePath = this.getAbsoluteFilePath(oldFileName);
const newAbsoluteFilePath = this.getAbsoluteFilePath(newFileName);

fs.renameSync(oldAbsoluteFilePath, newAbsoluteFilePath);
});
}

replaceFilesContent(
/**
* An array of objects containing the file name relative to project's
* root path and the replacement patterns to be applied
*
* @type {Array<{
* fileName: string;
* replacements: Array<{
* searchValue: string;
* replaceValue: string;
* }>
* }>}
*/
files
) {
files.forEach(({ fileName, replacements }) => {
const absoluteFilePath = this.getAbsoluteFilePath(fileName);

const contents = fs.readFileSync(absoluteFilePath, {
encoding: 'utf-8',
});

let replaced = contents;

replacements.forEach(({ searchValue, replaceValue }) => {
replaced = replaced.replace(searchValue, replaceValue);
});

fs.writeFileSync(absoluteFilePath, replaced, { spaces: 2 });
});
}
}

module.exports = ProjectFilesManager;
117 changes: 55 additions & 62 deletions cli/setup-project.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,34 @@ const {
} = require('./utils.js');
const { consola } = require('consola');
const fs = require('fs-extra');
const path = require('path');
const ProjectFilesManager = require('./project-files-manager.js');

const initGit = async (projectName) => {
/**
* @type {ProjectFilesManager}
*/
let projectFilesManager;

const initializeProjectRepository = async (projectName) => {
await execShellCommand(`cd ${projectName} && git init && cd ..`);
};

const installDeps = async (projectName) => {
const installDependencies = async (projectName) => {
await runCommand(`cd ${projectName} && pnpm install`, {
loading: 'Installing project dependencies',
success: 'Dependencies installed',
error: 'Failed to install dependencies, Make sure you have pnpm installed',
});
};

// remove unnecessary files and directories
const removeFiles = (projectName) => {
const FILES_TO_REMOVE = ['.git', 'README.md', 'docs', 'cli', 'LICENSE'];

FILES_TO_REMOVE.forEach((file) => {
fs.removeSync(path.join(process.cwd(), `${projectName}/${file}`));
});
const removeUnrelatedFiles = () => {
projectFilesManager.removeFiles(['.git', 'README.md', 'docs', 'cli', 'LICENSE']);
};

// Update package.json infos, name and set version to 0.0.1 + add initial version to osMetadata
const updatePackageInfos = async (projectName) => {
const packageJsonPath = path.join(
process.cwd(),
`${projectName}/package.json`
);
const updatePackageJson = async (projectName) => {
const packageJsonPath =
projectFilesManager.getAbsoluteFilePath('package.json');

const packageJson = fs.readJsonSync(packageJsonPath);
packageJson.osMetadata = { initVersion: packageJson.version };
packageJson.version = '0.0.1';
Expand All @@ -53,20 +52,30 @@ const updatePackageInfos = async (projectName) => {
};

const updateProjectConfig = async (projectName) => {
const configPath = path.join(process.cwd(), `${projectName}/env.js`);
const contents = fs.readFileSync(configPath, {
encoding: 'utf-8',
});
const replaced = contents
.replace(/RootstrapApp/gi, projectName)
.replace(/com.rootstrap/gi, `com.${projectName.toLowerCase()}`)
.replace(/rootstrap/gi, 'expo-owner');

fs.writeFileSync(configPath, replaced, { spaces: 2 });
projectFilesManager.replaceFilesContent([
{
fileName: 'env.js',
replacements: [
{
searchValue: /RootstrapApp/gi,
replaceValue: projectName,
},
{
searchValue: /com.rootstrap/gi,
replaceValue: `com.${projectName.toLowerCase()}`,
},
{
searchValue: /rsdevs/gi,
replaceValue: 'expo-owner',
},
],
},
]);
};

const updateGitHubWorkflows = (projectName) => {
const WORKFLOW_FILES = [
// Update useful workflows
projectFilesManager.replaceFilesContent([
{
fileName: '.github/workflows/upstream-to-pr.yml',
replacements: [
Expand All @@ -92,59 +101,43 @@ const updateGitHubWorkflows = (projectName) => {
replaceValue: 'Run App release',
},
{
searchValue: /^\s*environment:\s*\n\s*name:\s*template\s*\n\s*url:\s*.+\s*\n/m,
searchValue:
/^\s*environment:\s*\n\s*name:\s*template\s*\n\s*url:\s*.+\s*\n/m,
replaceValue: '',
},
],
},
];
]);

WORKFLOW_FILES.forEach(({ fileName, replacements }) => {
const workflowPath = path.join(process.cwd(), `${projectName}/${fileName}`);

const contents = fs.readFileSync(workflowPath, {
encoding: 'utf-8',
});

let replaced = contents;

replacements.forEach(({ searchValue, replaceValue }) => {
replaced = replaced.replace(searchValue, replaceValue);
});

fs.writeFileSync(workflowPath, replaced, { spaces: 2 });
});
projectFilesManager.renameFiles([
{
oldFileName: '.github/workflows/new-template-version.yml',
newFileName: '.github/workflows/new-app-version.yml',
},
]);
};

const renameFiles = (projectName) => {
const FILES_TO_RENAME = [
const updateProjectReadme = () => {
projectFilesManager.renameFiles([
{
oldFileName: 'README-project.md',
newFileName: 'README.md',
},
{
oldFileName: '.github/workflows/new-template-version.yml',
newFileName: '.github/workflows/new-app-version.yml',
},
];

FILES_TO_RENAME.forEach(({ oldFileName, newFileName }) => {
fs.renameSync(
path.join(process.cwd(), `${projectName}/${oldFileName}`),
path.join(process.cwd(), `${projectName}/${newFileName}`)
);
});
]);
};

const setupProject = async (projectName) => {
consola.start(`Clean up and setup your project 🧹`);

projectFilesManager = ProjectFilesManager.withProjectName(projectName);

try {
removeFiles(projectName);
await initGit(projectName);
updatePackageInfos(projectName);
removeUnrelatedFiles();
await initializeProjectRepository(projectName);
updatePackageJson(projectName);
updateProjectConfig(projectName);
updateGitHubWorkflows(projectName);
renameFiles(projectName);
updateProjectReadme();
consola.success(`Clean up and setup your project 🧹`);
} catch (error) {
consola.error(`Failed to clean up project folder`, error);
Expand All @@ -154,5 +147,5 @@ const setupProject = async (projectName) => {

module.exports = {
setupProject,
installDeps,
installDependencies,
};

0 comments on commit 92d09e7

Please sign in to comment.