Skip to content

Commit

Permalink
chore: add git repo initialisation to remix.init script
Browse files Browse the repository at this point in the history
  • Loading branch information
sj-e2digital committed Mar 14, 2023
1 parent 6b84a69 commit bc9a0f6
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 161 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ sam.yaml
# No IDE specific files should ever make it into the repo
.idea
.vscode

.eslintrc.js
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
"playwright:install": "playwright install --with-deps",
"postinstall": "remix-esbuild-override",
"prepack": "npmignore --auto",
"prepare": "lefthook install 1>/dev/null 2>/dev/null || true",
"release": "semantic-release",
"report": "vitest --coverage",
"storybook": "run-s storybook:serve",
Expand Down
65 changes: 0 additions & 65 deletions remix.init/.eslintrc.js

This file was deleted.

15 changes: 15 additions & 0 deletions remix.init/git-init
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

if [[ -z "$1" ]]; then
echo "Usage: git-init <Git repository URL>"
echo "e.g. git-init https://github.com/meza/trance-stack.git"
exit 1
fi

GITHUB_REPO=$( if [[ "$1" == *.git ]]; then echo "$1"; else echo "$1.git"; fi )

git init && \
git add . && \
git commit -m "chore: first commit" && \
git branch -M main
git remote add origin "$GITHUB_REPO"
3 changes: 1 addition & 2 deletions remix.init/gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ deploymentSummary.md
storybook-static

# Remix/Arc
server/index.js
server/index.js.map
server/index.mjs
server/index.js
public/build

# ARC
Expand Down
181 changes: 97 additions & 84 deletions remix.init/index.js → remix.init/index.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,19 @@
const { execSync } = require('child_process');
const fs = require('fs/promises');
const path = require('node:path');
const os = require('os');
const inquirer = require('inquirer');

const getAppName = (appSlug) => {
return appSlug
.replace(/[-_]/g, ' ') // replace underscores and dashes with spaces
.replace(/\b\w/g, (l) => l.toUpperCase()) // capitalize first letter of each word
.replace(/\s/g, '') // remove spaces
.replace(/([a-z])([A-Z])/g, '$1 $2'); // split camel case words with spaces
};

const cleanUpDeploymentScripts = async (rootDirectory) => {
//remove all lines with "command: install" in all the yml files in the .github/workflows directory
//as that is only necessary for the lockfile-less installations

const workflowsPath = path.join(rootDirectory, '.github/workflows');
const workflows = await fs.readdir(workflowsPath);
for (const workflow of workflows) {
const workflowPath = path.join(workflowsPath, workflow);
const contents = await fs.readFile(workflowPath, 'utf-8');
const newContents = contents.split('\n').filter((line) => !line.includes('command: install')).join('\n');
await fs.writeFile(workflowPath, newContents, 'utf-8');
}
};

const cleanUpReadme = async (rootDirectory) => {
// remove all blocks between <!-- initremove:begin --> and <!-- initremove:end --> in the readme

const readmePath = path.join(rootDirectory, 'README.md');
const contents = await fs.readFile(readmePath, 'utf-8');

const newContents = contents.replaceAll(/<!--\s*initremove:begin\s*-->.*?<!--\s*initremove:end\s*-->/gs, '');

await fs.writeFile(readmePath, newContents, 'utf-8');
};

const replaceInFile = async (file, replacements) => {
return fs.readFile(file, 'utf-8').then((contents) => {
const githubSlug = replacements.githubRepo.replace(`https://github.com/${replacements.githubUsername}/`, '');
const newContents = contents.replace('REPL_APP_SLUG', replacements.appSlug)
.replaceAll('https://github.com/meza/trance-stack', replacements.githubRepo)
.replaceAll('Trance Stack', replacements.appName)
.replaceAll('TRANCE STACK', replacements.appName.toUpperCase())
.replaceAll('trance stack', replacements.appName.toLowerCase())
.replaceAll('remix-trance-stack', replacements.appSlug)
.replaceAll('REPL_APP_NAME', replacements.appName)
.replaceAll('REPL_APP_REPO', replacements.githubRepo)
.replaceAll('https://meza.github.io/trance-stack/', `https://${replacements.githubUsername}.github.io/${githubSlug}`);

return fs.writeFile(file, newContents, 'utf-8');
});
};

const main = async ({ isTypeScript, packageManager, rootDirectory }) => {
import { execSync } from 'child_process';
import fs from 'fs/promises';
import path from 'node:path';
import os from 'os';
import inquirer from 'inquirer';
import isGitRepo from 'is-git-repository';

interface PromptAnswers {
appSlug: string;
appName: string;
githubUsername: string;
githubRepo: string;
validate: boolean;
}

export default async function main({ isTypeScript, rootDirectory }: { isTypeScript: boolean; rootDirectory: string; }) {
const { default: chalk } = await import('chalk');
const appSlug = path.basename(rootDirectory);
const username = os.userInfo().username;
Expand All @@ -79,15 +38,15 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => {
const fundingPath = path.join(rootDirectory, 'FUNDING.yml');
const licensePath = path.join(rootDirectory, 'LICENSE');
const contributingPath = path.join(rootDirectory, 'CONTRIBUTING.md');
const awsPath = path.join(rootDirectory, 'deployment');
// const awsPath = path.join(rootDirectory, 'deployment');
const rootPath = path.join(rootDirectory, 'src/root.tsx');
const rootTestPath = path.join(rootDirectory, 'src/root.test.tsx');
const e2ePath = path.join(rootDirectory, 'playwright/e2e/example.spec.ts');

const filesToDelete = [
fundingPath,
licensePath,
contributingPath
contributingPath,
];

const filesToReplaceThingsIn = [
Expand All @@ -102,30 +61,30 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => {
ephemeralDestroyWorkflowPath,
rootPath,
rootTestPath,
e2ePath
e2ePath,
];

console.log('\n🚀 Initializing your app...\n\n');
const answers = await inquirer.prompt([
const answers = await inquirer.prompt<PromptAnswers>([
{
type: 'input',
name: 'appSlug',
message: `${chalk.cyan('What is the slug of your app?')} ${chalk.white('This will be used in the Arc file and the deployment stages of your app')}`,
default: appSlug,
validate: (input, answers) => {
validate: (input: string, answers: PromptAnswers) => {
if (input.match(/^[a-zA-Z0-9-_]*$/)) {
return true;
}
return chalk.yellow('The app slug can only contain letters, numbers, dashes, and underscores.');
}
},
},
{
type: 'input',
name: 'appName',
message: `${chalk.cyan('What is the name of your app?')} ${chalk.white('This will be used in the README and the Meta of your app.')}`,
default: (answers) => {
default: (answers: PromptAnswers) => {
return getAppName(answers.appSlug);
}
},
},
// {
// type: 'confirm',
Expand All @@ -143,49 +102,47 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => {
type: 'input',
name: 'githubUsername',
message: `${chalk.cyan('What is your GitHub username?')} ${chalk.white('This will be used to construct a repo URL for your app.')}`,
default: username
default: username,
// when: (answers) => answers.useGithub
},
{
type: 'input',
name: 'githubRepo',
message: `${chalk.cyan('What is the name of your GitHub repo?')} ${chalk.white('This will be used in the github related documentation/policies.')}`,
default: (answers) => {
default: (answers: PromptAnswers) => {
return `https://github.com/${answers.githubUsername}/${answers.appSlug}`;
}
},
// when: (answers) => answers.useGithub
},
{
name: 'validate',
type: 'confirm',
default: true,
message:
'Do you want to run the build/tests/etc to verify things are setup properly?'
}
'Do you want to run the build/tests/etc to verify things are setup properly?',
},
]);

await Promise.all(filesToDelete.map(async (file) => {
await fs.rm(file, {
await Promise.all(filesToDelete.map((file) =>
fs.rm(file, {
recursive: true,
force: true
});
}));
force: true,
}),
));

await Promise.all(filesToReplaceThingsIn.map(async (file) => {
await replaceInFile(file, answers);
}));
await Promise.all(filesToReplaceThingsIn.map((file) => replaceInFile(file, answers)));

await Promise.all([
cleanUpReadme(rootDirectory),
cleanUpDeploymentScripts(rootDirectory),
fs.copyFile(
envExamplePath,
envPath
envPath,
),
fs.copyFile(
path.join(rootDirectory, 'remix.init', 'gitignore'),
path.join(rootDirectory, '.gitignore')
)
path.join(rootDirectory, '.gitignore'),
),
]);

// if (!answers.useAWS) {
Expand All @@ -198,14 +155,70 @@ const main = async ({ isTypeScript, packageManager, rootDirectory }) => {

if (answers.validate) {
console.log(
'Running the validate script to make sure everything was set up properly'
'Running the validate script to make sure everything was set up properly',
);
execSync('npm run validate', { cwd: rootDirectory, stdio: 'inherit' });
}

if (!isGitRepo()) {
console.log('Initializing git repo...');
execSync(`./remix.init/git-init ${answers.githubRepo}`, { cwd: rootDirectory, stdio: 'inherit' });
}

console.log('Installing Lefthook...');
execSync('npx lefthook install', { cwd: rootDirectory, stdio: 'inherit' });

console.log(
'✅ Project is ready! Start development with "npm run dev"'
'✅ Project is ready! Start development with "npm run dev"',
);
};

module.exports = main;
function getAppName(appSlug: string) {
return appSlug
.replace(/[-_]/g, ' ') // replace underscores and dashes with spaces
.replace(/\b\w/g, (l) => l.toUpperCase()) // capitalize first letter of each word
.replace(/\s/g, '') // remove spaces
.replace(/([a-z])([A-Z])/g, '$1 $2'); // split camel case words with spaces
}

async function cleanUpDeploymentScripts(rootDirectory: string) {
//remove all lines with "command: install" in all the yml files in the .github/workflows directory
//as that is only necessary for the lockfile-less installations

const workflowsPath = path.join(rootDirectory, '.github/workflows');
const workflows = await fs.readdir(workflowsPath);
for (const workflow of workflows) {
const workflowPath = path.join(workflowsPath, workflow);
const contents = await fs.readFile(workflowPath, 'utf-8');
const newContents = contents.split('\n').filter((line) => !line.includes('command: install')).join('\n');
await fs.writeFile(workflowPath, newContents, 'utf-8');
}
}

async function cleanUpReadme(rootDirectory: string) {
// remove all blocks between <!-- initremove:begin --> and <!-- initremove:end --> in the readme

const readmePath = path.join(rootDirectory, 'README.md');
const contents = await fs.readFile(readmePath, 'utf-8');

const newContents = contents.replaceAll(/<!--\s*initremove:begin\s*-->.*?<!--\s*initremove:end\s*-->/gs, '');

await fs.writeFile(readmePath, newContents, 'utf-8');
}

async function replaceInFile(file: string, replacements: PromptAnswers) {
return fs.readFile(file, 'utf-8').then((contents) => {
const githubSlug = replacements.githubRepo.replace(`https://github.com/${replacements.githubUsername}/`, '');
const newContents = contents.replace('REPL_APP_SLUG', replacements.appSlug)
.replaceAll('https://github.com/meza/trance-stack', replacements.githubRepo)
.replaceAll('Trance Stack', replacements.appName)
.replaceAll('TRANCE STACK', replacements.appName.toUpperCase())
.replaceAll('trance stack', replacements.appName.toLowerCase())
.replaceAll('remix-trance-stack', replacements.appSlug)
.replaceAll('REPL_APP_NAME', replacements.appName)
.replaceAll('REPL_APP_REPO', replacements.githubRepo)
.replaceAll('https://meza.github.io/trance-stack/', `https://${replacements.githubUsername}.github.io/${githubSlug}`);

return fs.writeFile(file, newContents, 'utf-8');
});
}
8 changes: 0 additions & 8 deletions remix.init/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,5 @@
"inquirer": "^8.2.5",
"is-git-repository": "^2.0.0",
"semver": "^7.3.8"
},
"devDependencies": {
"eslint": "^8.27.0",
"eslint-config-tailored-tunes": "5.0.2",
"eslint-gitignore": "^0.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-json": "3.1.0",
"eslint-plugin-security": "1.7.1"
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"include": ["./*", "src", "testUtils", "types", "deployment"],
"include": ["./*", "src", "testUtils", "types", "deployment", "remix.init"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"isolatedModules": true,
Expand Down

0 comments on commit bc9a0f6

Please sign in to comment.