Skip to content
Draft
2 changes: 1 addition & 1 deletion packages/core/installMachine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
deployVercelProjectActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
await deployVercelProject();
await deployVercelProject(input.stateData.options.usePayload);
input.stateData.stepsCompleted.deployVercelProject = true;
saveStateToRcFile(input.stateData, input.projectDir);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { execAsync } from '../../../utils/execAsync';
import { logger } from '../../../utils/logger';

export const createMigration = async () => {
await logger.withSpinner('payload', 'Creating migration...', async (spinner) => {
try {
await execAsync('mkdir migrations');
await execAsync('npx payload migrate:create');
spinner.succeed('Migration created.');
} catch (error) {
spinner.fail('Failed to create migration.');
console.error(error);
process.exit(1);
}
});
};
11 changes: 8 additions & 3 deletions packages/core/installMachine/installSteps/payload/install.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { preparePayloadConfig } from './preparePayloadConfig';
import { prepareTsConfig } from './prepareTsConfig';
import { removeTurboFlag } from './removeTurboFlag';
import { updatePackages } from './updatePackages';
import { moveFilesToAppDir } from './moveFilesToAppDir';
import { runInstallCommand } from './runInstallCommand';
import { updatePackageJson } from './updatePackageJson';
import { preparePayloadConfig } from './preparePayloadConfig';
import { createMigration } from './createMigration';
import { updateTurboJson } from './updateTurboJson';

export const preparePayload = async () => {
process.chdir('./apps/web/');
Expand All @@ -12,8 +14,11 @@ export const preparePayload = async () => {
await updatePackages();
await moveFilesToAppDir();
await runInstallCommand();
await removeTurboFlag();
await updatePackageJson();
await preparePayloadConfig();
await createMigration();

process.chdir('../../');

await updateTurboJson();
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { existsSync, type PathLike } from 'fs';
import { existsSync } from 'fs';
import fs from 'fs/promises';
import { logger } from '../../../utils/logger';
import { join } from 'path';
Expand All @@ -14,19 +14,40 @@ export const preparePayloadConfig = async () => {
await logger.withSpinner('payload', 'Preparing config...', async (spinner) => {
try {
// Read the payload.config.ts file
const data = await fs.readFile(payloadConfigPath, 'utf8');
let data = await fs.readFile(payloadConfigPath, 'utf8');

// Use regex to find the "pool" object and append "schemaName: 'payload'" to the pool configuration
const updatedConfig = data.replace(/pool:\s*{([^}]*)connectionString[^}]*}/, (match, group1) => {
if (match.includes('schemaName')) {
return match; // If "schemaName" already exists, return the match unchanged
}
// Append schemaName to the existing pool configuration (avoiding the extra comma)
return match.replace(group1.trimEnd(), `${group1.trimEnd()} schemaName: 'payload',\n`);
});
const postgresAdapterImport = `import { postgresAdapter } from '@payloadcms/db-postgres'`;
const vercelPostgresAdapterImport = `import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'`;

// Add the vercelPostgresAdapter import after postgresAdapter if it's not already present
if (!data.includes(vercelPostgresAdapterImport)) {
data = data.replace(postgresAdapterImport, `${postgresAdapterImport}\n${vercelPostgresAdapterImport}`);
} else {
console.log('vercelPostgresAdapter import is already present.');
}

// Step 2: Replace the db configuration with conditional configuration
const newDbConfig = `db: process.env.POSTGRES_URL
? vercelPostgresAdapter({
schemaName: "payload",
pool: {
connectionString: process.env.POSTGRES_URL || "",
},
})
: postgresAdapter({
schemaName: "payload",
pool: {
connectionString: process.env.DATABASE_URI || "",
},
})`;

data = data.replace(
/db:\s*postgresAdapter\(\{[\s\S]*?pool:\s*\{[\s\S]*?connectionString:[\s\S]*?\}[\s\S]*?\}\)/m,
newDbConfig,
);

// Write the updated payload.config.ts back to the file
await fs.writeFile(payloadConfigPath, updatedConfig);
await fs.writeFile(payloadConfigPath, data);

spinner.succeed('Config prepared.');
} catch (err) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { execAsync } from '../../../utils/execAsync';
import { logger } from '../../../utils/logger';
import { loadEnvFile } from './utils/loadEnvFile';

export const runInstallCommand = async () => {
await logger.withSpinner('payload', 'Installing to Next.js...', async (spinner) => {
loadEnvFile('../../supabase/.env');
try {
await execAsync(
`echo y | npx create-payload-app@beta --db postgres --db-connection-string ${process.env.DB_URL}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { promises as fs } from 'fs';
import path from 'path';
import { logger } from '../../../utils/logger';

export const updatePackageJson = async () => {
const packageJsonPath = path.resolve('package.json');
logger.withSpinner('payload', 'Updating package.json...', async (spinner) => {
try {
// Read and parse package.json
const packageData = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));

// Add db script to package.json
packageData.scripts = {
...packageData.scripts,
'db:migrate': 'npx payload migrate',
};

// Payload doesn't work with Turbopack yet
// Remove '--turbo' flag from the "dev" script
if (packageData.scripts && packageData.scripts.dev) {
packageData.scripts.dev = packageData.scripts.dev.replace('--turbo', '').trim();
}

// Write the modified package.json
await fs.writeFile(packageJsonPath, JSON.stringify(packageData, null, 2));
spinner.succeed('Updated package.json');
} catch (error) {
spinner.fail('Failed to update package.json');
console.error('Error updating files:', error);
}
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const updatePackages = async () => {

await logger.withSpinner('payload', 'Installing necessary packages...', async (spinner) => {
try {
await execAsync(`pnpm i pg sharp --reporter silent`);
await execAsync(`pnpm i pg sharp @payloadcms/db-vercel-postgres --reporter silent`);
spinner.succeed('Installed necessary packages!');
} catch (error) {
spinner.fail('Failed to install necessary packages!');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { promises as fs } from 'fs';
import path from 'path';
import { logger } from '../../../utils/logger';

export const updateTurboJson = async () => {
const turboJsonPath = path.resolve('turbo.json');
await logger.withSpinner('payload', 'Updating turbo.json...', async (spinner) => {
try {
// Read and parse turbo.json
const turboData = JSON.parse(await fs.readFile(turboJsonPath, 'utf8'));

// Update turbo.json with the new structure
turboData.tasks = {
...turboData.tasks,
'web#db:migrate': {
cache: false,
},
build: {
dependsOn: ['^web#db:migrate', '^build'],
outputs: ['dist/**'],
},
'build:core': {
outputs: ['dist/**'],
},
};

turboData.globalEnv = [
'SUPABASE_JWT_SECRET',
'POSTGRES_URL',
'PAYLOAD_SECRET',
'SUPABASE_SERVICE_ROLE_KEY',
'NEXT_PUBLIC_*',
'PORT',
];

// Write the modified turbo.json
await fs.writeFile(turboJsonPath, JSON.stringify(turboData, null, 2));
spinner.succeed('turbo.json updated.');
} catch (error) {
spinner.fail('Failed to update turbo.json.');
console.error('Error updating files:', error);
}
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { execAsync } from "../../../utils/execAsync";
import { logger } from "../../../utils/logger";

export const initializeSupabaseProject = async () => {
await logger.withSpinner('supabase', 'Initializing project...', async (spinner) => {
try {
await execAsync(`npx supabase init`);
spinner.succeed('Project initialized.');
} catch (error: any) {
const errorMessage = error.stderr;
if (errorMessage.includes('file exists')) {
spinner.succeed('Configuration file already exists.');
} else {
spinner.fail('Failed to initialize project.');
console.error(
'Please review the error message below, follow the initialization instructions, and try running "create-stapler-app" again.',
);
process.exit(1);
}
}
});
};
44 changes: 6 additions & 38 deletions packages/core/installMachine/installSteps/supabase/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,9 @@ import { templateGenerator } from '../../../utils/generator/generator';
import { getTemplateDirectory } from '../../../utils/getTemplateDirectory';
import { logger } from '../../../utils/logger';
import { execAsync } from '../../../utils/execAsync';

const supabaseLogin = async () => {
await logger.withSpinner('supabase', 'Logging in...', async (spinner) => {
try {
await execAsync('npx supabase projects list');
spinner.succeed('Already logged in.');
} catch (error) {
try {
await execAsync('npx supabase login');
spinner.succeed('Logged in successfully.');
} catch {
spinner.fail('Failed to log in to Supabase.');
console.error('Please log in manually with "supabase login" and re-run "create-stapler-app".');
process.exit(1);
}
}
});
};

const initializeSupabaseProject = async () => {
await logger.withSpinner('supabase', 'Initializing project...', async (spinner) => {
try {
await execAsync(`npx supabase init`);
spinner.succeed('Project initialized.');
} catch (error: any) {
const errorMessage = error.stderr;
if (errorMessage.includes('file exists')) {
spinner.succeed('Configuration file already exists.');
} else {
spinner.fail('Failed to initialize project.');
console.error(
'Please review the error message below, follow the initialization instructions, and try running "create-stapler-app" again.',
);
process.exit(1);
}
}
});
};
import { initializeSupabaseProject } from './initializeSupabaseProject';
import { modifySupabaseConfig } from './modifySupabaseConfig';
import { supabaseLogin } from './supabaseLogin';

export const installSupabase = async (destinationDirectory: string) => {
try {
Expand All @@ -69,6 +34,9 @@ export const installSupabase = async (destinationDirectory: string) => {
spinner.succeed('Files added.');
});

// Modify supabase/config.toml to enable db.pooler
await modifySupabaseConfig(destinationDirectory);

process.chdir('supabase');

await logger.withSpinner('supabase', 'Installing dependencies...', async (spinner) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import path from 'path';
import fs from 'fs';
import { logger } from '../../../utils/logger';

export const modifySupabaseConfig = async (destinationDirectory: string) => {
const configPath = path.join(destinationDirectory, 'supabase', 'config.toml');
await logger.withSpinner('supabase', 'Modifying config.toml...', async (spinner) => {
if (!fs.existsSync(configPath)) {
console.error(`config.toml file not found at ${configPath}`);
process.exit(1);
}

try {
const configContent = fs.readFileSync(configPath, 'utf-8');

// Modify [db.pooler] enabled = false to enabled = true
let modifiedContent;
if (configContent.includes('[db.pooler]')) {
modifiedContent = configContent.replace(/\[db\.pooler\]\s+enabled\s*=\s*false/, '[db.pooler]\nenabled = true');
} else {
// Append the [db.pooler] section at the end if it doesn't exist
modifiedContent = `${configContent}\n[db.pooler]\nenabled = true\n`;
}

fs.writeFileSync(configPath, modifiedContent, 'utf-8');
spinner.succeed('config.toml modified.');
} catch (error) {
spinner.fail('Failed to modify config.toml.');
console.error(error);
process.exit(1);
}
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { execAsync } from "../../../utils/execAsync";
import { logger } from "../../../utils/logger";

export const supabaseLogin = async () => {
await logger.withSpinner('supabase', 'Logging in...', async (spinner) => {
try {
await execAsync('npx supabase projects list');
spinner.succeed('Already logged in.');
} catch (error) {
try {
await execAsync('npx supabase login');
spinner.succeed('Logged in successfully.');
} catch {
spinner.fail('Failed to log in to Supabase.');
console.error('Please log in manually with "supabase login" and re-run "create-stapler-app".');
process.exit(1);
}
}
});
};
Loading