Skip to content

Commit

Permalink
Merge pull request #1 from tonik/feat/create-supabase-project
Browse files Browse the repository at this point in the history
feat: add create project for supabase
  • Loading branch information
maneike authored Oct 17, 2024
2 parents 3048dc2 + 74872b5 commit 6c04091
Show file tree
Hide file tree
Showing 11 changed files with 693 additions and 511 deletions.
11 changes: 8 additions & 3 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { installSupabase } from './utils/supabase/install';
import { prettify } from './utils/prettier/prettify';
import { prepareDrink } from './utils/bar/prepareDrink';
import { initializeRepository } from './utils/github/install';
import { connectSupabaseProject } from './utils/supabase/connectProject';
import { createSupabaseProject } from './utils/supabase/createProject';

interface ProjectOptions {
name: string;
Expand All @@ -19,21 +21,24 @@ export async function createProject(options: ProjectOptions) {
await createTurboRepo(name);

process.chdir(name);

const currentDir = process.cwd();

createEnvFile(currentDir);

if (usePayload) await preparePayload();

await installSupabase(currentDir);
installSupabase(currentDir);

await prettify();

initializeRepository({
await initializeRepository({
projectName: name,
visibility: 'private',
});

await createSupabaseProject(name);

await connectSupabaseProject(name, currentDir);

prepareDrink(name);
}
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"dev": "tsc -w"
},
"dependencies": {
"fs-extra": "^11.2.0"
"fs-extra": "^11.2.0",
"inquirer": "^10.2.2"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
Expand Down
78 changes: 39 additions & 39 deletions packages/core/templates/supabase/files/package
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
{
"name": "@stapler/supabase",
"version": "0.1.0",
"private": true,
"type": "module",
"license": "SEE LICENSE IN LICENSE",
"exports": {
".": "./src/index.ts",
"./server": "./src/server.ts",
"./middleware": "./src/middleware.ts",
"./client": "./src/client.ts",
"./types": "./src/types.ts"
},
"scripts": {
"clean": "rm -rf .turbo node_modules",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"typecheck": "tsc --noEmit",
"start": "pnpm with-env supabase start",
"stop": "supabase stop",
"with-env": "dotenv -e ../../.env --",
"db:reset": "supabase db reset",
"generate:types": "supabase gen types typescript --local > src/types.gen.ts",
"prepare-tests": "tsx scripts/prepare-tests.ts && pnpm db:reset",
"test": "supabase test db"
},
"dependencies": {
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.41.1",
"supabase": "^1.150.0",
"tsx": "^4.7.2"
},
"peerDependencies": {
"next": "^14.2.4"
},
"devDependencies": {
"next": "14.2.4",
"typescript": "^5.5.3"
}
"name": "@stapler/supabase",
"version": "0.1.0",
"private": true,
"type": "module",
"license": "SEE LICENSE IN LICENSE",
"exports": {
".": "./src/index.ts",
"./server": "./src/server.ts",
"./middleware": "./src/middleware.ts",
"./client": "./src/client.ts",
"./types": "./src/types.ts"
},
"scripts": {
"clean": "rm -rf .turbo node_modules",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"typecheck": "tsc --noEmit",
"start": "pnpm with-env supabase start",
"stop": "supabase stop",
"with-env": "dotenv -e ../../.env --",
"db:reset": "supabase db reset",
"generate:types": "supabase gen types typescript --local > src/types.gen.ts",
"prepare-tests": "tsx scripts/prepare-tests.ts && pnpm db:reset",
"test": "supabase test db"
},
"dependencies": {
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.41.1",
"supabase": "^1.150.0",
"dotenv": "^16.4.5",
"tsx": "^4.7.2"
},
"peerDependencies": {
"next": "^14.2.4"
},
"devDependencies": {
"next": "14.2.4",
"typescript": "^5.5.3"
}
}
42 changes: 20 additions & 22 deletions packages/core/utils/generator/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,27 @@ interface TemplateFilesObject {
}
export type Template = TemplateFilesObject[];

export const templateGenerator = (template: Template, templateDir: string, destinationDir: string) => {
template.forEach((templateFilesObject) => {
templateFilesObject.files.forEach((file) => {
// Construct source and destination paths
const source = path.join(templateDir, file);
// console.log(source);
const destination = path.join(destinationDir, templateFilesObject.path, file);
// check if the directory exists, if not create it
if (!fs.existsSync(path.join(destinationDir, templateFilesObject.path))) {
fs.mkdirSync(path.join(destinationDir, templateFilesObject.path), {
recursive: true,
});
}
fs.copyFileSync(source, destination);
export const templateGenerator = (filesConfig: Template, templateDir: string, destinationDir: string) => {
filesConfig.forEach(({ path: filePath, files, rename }) => {
const fullPath = path.join(destinationDir, filePath);
fs.mkdirSync(fullPath, { recursive: true }); // Create the directory if it doesn't exist

// Handle file renaming if needed
if (templateFilesObject.rename) {
templateFilesObject.rename.forEach((rename) => {
const oldPath = path.join(process.cwd(), templateFilesObject.path, rename.from);
const newPath = path.join(process.cwd(), templateFilesObject.path, rename.to);
fs.renameSync(oldPath, newPath);
});
}
files.forEach((file) => {
const sourceFile = path.join(templateDir, file);
const destinationFile = path.join(fullPath, file);

// Copy the file from the template directory to the destination
fs.copyFileSync(sourceFile, destinationFile);
});

// Handle renaming if applicable
if (rename) {
rename.forEach(({ from, to }) => {
const originalFilePath = path.join(fullPath, from);
const renamedFilePath = path.join(fullPath, to);

fs.renameSync(originalFilePath, renamedFilePath);
});
}
});
};
35 changes: 35 additions & 0 deletions packages/core/utils/shared/continueOnKeypress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as readline from 'readline';

export const continueOnAnyKeypress = async (message: string): Promise<void> => {
console.log(message);

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

const cleanup = () => {
if (process.stdin.isTTY) {
process.stdin.setRawMode(false);
}
process.stdin.removeListener('keypress', onKeyPress);
rl.close();
};

let resolvePromise: () => void;

const onKeyPress = () => {
cleanup();
resolvePromise();
};

readline.emitKeypressEvents(process.stdin, rl);
if (process.stdin.isTTY) {
process.stdin.setRawMode(true);
}

return new Promise<void>((resolve) => {
resolvePromise = resolve;
process.stdin.on('keypress', onKeyPress);
});
};
51 changes: 51 additions & 0 deletions packages/core/utils/shared/updateEnvFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as fs from 'fs';
import * as util from 'util';
import * as path from 'path';

const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const access = util.promisify(fs.access);

type EnvPair = [string, string];

interface EnvUpdateConfig {
currentDir: string;
pairs: EnvPair[];
}

// Function to update a single line
const updateLine = (line: string, key: string, value: string): string => {
if (line.startsWith(`${key}=`)) {
return `${key}=${value}`;
}
return line;
};

export async function updateEnvFile({ currentDir, pairs }: EnvUpdateConfig): Promise<void> {
const envFilePath = path.join(currentDir, '.env');
try {
// Check if the .env file exists
try {
await access(envFilePath); // Check if file exists
} catch {
// If it doesn't exist, create it with default values
await writeFile(envFilePath, '', 'utf8');
}

const data = await readFile(envFilePath, 'utf8');
let lines = data.split('\n');

lines = lines.map((line) => {
for (const [key, value] of pairs) {
line = updateLine(line, key, value);
}
return line;
});

const updatedContent = lines.join('\n');
await writeFile(envFilePath, updatedContent, 'utf8');
} catch (error) {
console.error('🖇️ Error updating .env file:', error);
throw error; // Re-throw the error for the caller to handle
}
}
71 changes: 71 additions & 0 deletions packages/core/utils/supabase/connectProject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { execSync } from 'child_process';
import inquirer from 'inquirer';
import { continueOnAnyKeypress } from '../shared/continueOnKeypress';
import { updateEnvFile } from '../shared/updateEnvFile';
import { getSupabaseKeys, parseProjectsList } from './utils';

const instructions = [
'\n=== Instructions for Supabase Integration with GitHub and Vercel ===',
'🖇️ 1. You will be redirected to your Supabase project dashboard',
'🖇️ 2. Find the "GitHub" section and click "Connect".',
' - Follow the prompts to connect Supabase with your GitHub repository.',
'🖇️ 3. Then, find the "Vercel" section and click "Connect".',
' - Follow the prompts to connect Supabase with your Vercel project.',
'\n 🖇️ Please note that these steps require manual configuration in the Supabase interface.\n',
];

export const connectSupabaseProject = async (projectName: string, currentDir: string) => {
console.log('🖇️ Getting information about newly created Supabase project...');
const projectsList = execSync('supabase projects list', { encoding: 'utf-8' });
const projects = parseProjectsList(projectsList);
const newProject = projects.find((project) => project.name === projectName);

console.log('🖇️ Getting Supabase project keys...');
const projectAPIKeys = execSync(`supabase projects api-keys --project-ref ${newProject?.refId}`, {
encoding: 'utf-8',
});

const SUPABASE_ANON_KEY = getSupabaseKeys(projectAPIKeys).anonKey;
const SUPABASE_SERVICE_ROLE_KEY = getSupabaseKeys(projectAPIKeys).serviceRoleKey;
const SUPABASE_URL = `https://${newProject?.refId}.supabase.co/`;

console.log(`🖇️ Saving keys to .env...`);
await updateEnvFile({
currentDir,
pairs: [
['SUPABASE_ANON_KEY', `${SUPABASE_ANON_KEY}`],
['SUPABASE_SERVICE_ROLE_KEY', `${SUPABASE_SERVICE_ROLE_KEY}`],
['SUPABASE_URL', `${SUPABASE_URL}`],
],
});

console.log('🖇️ Linking Supabase project...');
execSync(`supabase link --project-ref ${newProject?.refId}`, {
stdio: 'inherit',
});

for (const instruction of instructions) {
console.log(instruction);
}

await continueOnAnyKeypress('🖇️ When you are ready to be redirected to the Supabase page press any key');

execSync(`open https://supabase.com/dashboard/project/${newProject?.refId}/settings/integrations`);

const { isIntegrationReady } = await inquirer.prompt([
{
type: 'confirm',
name: 'isIntegrationReady',
message: '🖇️ Have you completed the GitHub and Vercel integration setup?',
default: false,
},
]);

if (!isIntegrationReady) {
// Uncomment after CLI progress task is done.
// console.log("🖇️ Run \x1b[36mcreate-stapler-app\x1b[0m again when you've completed the integration.");
console.log(
`🖇️ You can access your project dashboard at: https://supabase.com/dashboard/project/${newProject?.refId}/settings/integrations`,
);
}
};
9 changes: 9 additions & 0 deletions packages/core/utils/supabase/createProject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { execSync } from 'child_process';

export const createSupabaseProject = async (name: string) => {
console.log('🖇️ Creating Supabase project...');

execSync(`supabase projects create ${name}`, {
stdio: 'inherit',
});
};
4 changes: 4 additions & 0 deletions packages/core/utils/supabase/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const installSupabase = async (destinationDirectory: string) => {
execSync(`supabase init`, { stdio: 'inherit' });

console.log('🖇️ Adding Supabase Files...');

const templateDirectory = path.join(__dirname, '../templates/supabase/files');

templateGenerator(supabaseFiles, templateDirectory, destinationDirectory);
Expand All @@ -18,7 +19,10 @@ export const installSupabase = async (destinationDirectory: string) => {
fs.appendFileSync(workspacePath, addSupabaseToWorkspace);

process.chdir('supabase');

console.log('🖇️ Installing Supabase dependencies...');

execSync('pnpm install', { stdio: 'inherit' });

process.chdir('..');
};
Loading

0 comments on commit 6c04091

Please sign in to comment.