Use existing environment variables as defaults for the generator command.

This commit is contained in:
giancarlo
2024-06-05 20:02:29 +07:00
parent a3f339aaf6
commit aba9076805
3 changed files with 100 additions and 65 deletions

View File

@@ -1,9 +1,19 @@
import type { PlopTypes } from '@turbo/gen'; import type { PlopTypes } from '@turbo/gen';
import { writeFileSync } from 'node:fs'; import { writeFileSync } from 'node:fs';
import { generator } from '../../utils';
export function createEnvironmentVariablesGenerator( export function createEnvironmentVariablesGenerator(
plop: PlopTypes.NodePlopAPI, plop: PlopTypes.NodePlopAPI,
) { ) {
const allVariables = generator.loadAllEnvironmentVariables('apps/web');
if (allVariables) {
console.log(
`Loaded ${Object.values(allVariables).length} default environment variables in your env files. We use these as defaults.`,
);
}
return plop.setGenerator('env', { return plop.setGenerator('env', {
description: 'Generate the environment variables to be used in the app', description: 'Generate the environment variables to be used in the app',
actions: [ actions: [
@@ -31,129 +41,130 @@ export function createEnvironmentVariablesGenerator(
name: 'values.NEXT_PUBLIC_SITE_URL', name: 'values.NEXT_PUBLIC_SITE_URL',
message: message:
'What is the site URL of you website? (Ex. https://makerkit.dev)', 'What is the site URL of you website? (Ex. https://makerkit.dev)',
default: allVariables.NEXT_PUBLIC_SITE_URL,
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_PRODUCT_NAME', name: 'values.NEXT_PUBLIC_PRODUCT_NAME',
message: 'What is the name of your product? (Ex. MakerKit)', message: 'What is the name of your product? (Ex. MakerKit)',
default: allVariables.NEXT_PUBLIC_PRODUCT_NAME,
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_SITE_TITLE', name: 'values.NEXT_PUBLIC_SITE_TITLE',
message: message:
'What is the title of your website? (Ex. MakerKit - The best way to make things)', 'What is the title of your website? (Ex. MakerKit - The best way to make things)',
default: allVariables.NEXT_PUBLIC_SITE_TITLE,
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_SITE_DESCRIPTION', name: 'values.NEXT_PUBLIC_SITE_DESCRIPTION',
message: message:
'What is the description of your website? (Ex. MakerKit is the best way to make things and stuff)', 'What is the description of your website? (Ex. MakerKit is the best way to make things and stuff)',
default: allVariables.NEXT_PUBLIC_SITE_DESCRIPTION,
}, },
{ {
type: 'list', type: 'list',
name: 'values.NEXT_PUBLIC_DEFAULT_THEME_MODE', name: 'values.NEXT_PUBLIC_DEFAULT_THEME_MODE',
message: message: 'What is the default theme mode of your website?',
'What is the default theme mode of your website? (leave empty for light)',
choices: ['light', 'dark', 'system'], choices: ['light', 'dark', 'system'],
default: 'light', default: allVariables.NEXT_PUBLIC_DEFAULT_THEME_MODE ?? 'light',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_DEFAULT_LOCALE', name: 'values.NEXT_PUBLIC_DEFAULT_LOCALE',
message: message: 'What is the default locale of your website?',
'What is the default locale of your website? (leave empty for en)', default: allVariables.NEXT_PUBLIC_DEFAULT_LOCALE ?? 'en',
default: 'en',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_AUTH_PASSWORD', name: 'values.NEXT_PUBLIC_AUTH_PASSWORD',
message: message: 'Do you want to use email/password authentication?',
'Do you want to use email/password authentication? (leave empty for true)', default: allVariables.NEXT_PUBLIC_DEFAULT_LOCALE ?? 'true',
default: 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_AUTH_MAGIC_LINK', name: 'values.NEXT_PUBLIC_AUTH_MAGIC_LINK',
message: message:
'Do you want to use magic link authentication? (leave empty for false)', 'Do you want to use magic link authentication? (leave empty for false)',
default: 'false', default: allVariables.NEXT_PUBLIC_AUTH_MAGIC_LINK ?? 'false',
}, },
{ {
type: 'input', type: 'input',
name: 'values.CONTACT_EMAIL', name: 'values.CONTACT_EMAIL',
message: 'What is the contact email you want to receive emails to?', message: 'What is the contact email you want to receive emails to?',
default: allVariables.CONTACT_EMAIL,
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_THEME_TOGGLE', name: 'values.NEXT_PUBLIC_ENABLE_THEME_TOGGLE',
message: message: 'Do you want to enable the theme toggle?',
'Do you want to enable the theme toggle? (leave empty for true)', default: allVariables.NEXT_PUBLIC_ENABLE_THEME_TOGGLE ?? 'true',
default: 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION', name: 'values.NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION',
message: message: 'Do you want to enable personal account deletion?',
'Do you want to enable personal account deletion? (leave empty for true)', default:
default: 'true', allVariables.NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION ?? 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING', name: 'values.NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING',
message: message: 'Do you want to enable personal account billing?',
'Do you want to enable personal account billing? (leave empty for true)', default:
default: 'true', allVariables.NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING ?? 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS', name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS',
message: 'Do you want to enable team accounts? (leave empty for true)', message: 'Do you want to enable team accounts?',
default: 'true', default: allVariables.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS ?? 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNT_DELETION', name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNT_DELETION',
message: message: 'Do you want to enable team account deletion?',
'Do you want to enable team account deletion? (leave empty for true)', default:
default: 'true', allVariables.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNT_DELETION ?? 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING', name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING',
message: message: 'Do you want to enable team account billing?',
'Do you want to enable team account billing? (leave empty for true)', default:
default: 'true', allVariables.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING ?? 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION', name: 'values.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION',
message: message: 'Do you want to enable team account creation?',
'Do you want to enable team account creation? (leave empty for true)', default:
default: 'true', allVariables.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION ?? 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_REALTIME_NOTIFICATIONS', name: 'values.NEXT_PUBLIC_REALTIME_NOTIFICATIONS',
message: message: 'Do you want to enable realtime notifications?',
'Do you want to enable realtime notifications? (leave empty for false)', default: allVariables.NEXT_PUBLIC_REALTIME_NOTIFICATIONS ?? 'true',
default: 'false',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_ENABLE_NOTIFICATIONS', name: 'values.NEXT_PUBLIC_ENABLE_NOTIFICATIONS',
message: message: 'Do you want to enable email notifications?',
'Do you want to enable email notifications? (leave empty for true)', default: allVariables.NEXT_PUBLIC_ENABLE_NOTIFICATIONS ?? 'true',
default: 'true',
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_SUPABASE_URL', name: 'values.NEXT_PUBLIC_SUPABASE_URL',
message: 'What is the Supabase URL? (Ex. https://yourapp.supabase.co)', message: 'What is the Supabase URL? (Ex. https://yourapp.supabase.co)',
default: allVariables.NEXT_PUBLIC_SUPABASE_URL,
}, },
{ {
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_SUPABASE_ANON_KEY', name: 'values.NEXT_PUBLIC_SUPABASE_ANON_KEY',
message: 'What is the Supabase anon key?', message: 'What is the Supabase anon key?',
default: allVariables.NEXT_PUBLIC_SUPABASE_ANON_KEY,
}, },
{ {
type: 'input', type: 'input',
@@ -163,10 +174,9 @@ export function createEnvironmentVariablesGenerator(
{ {
type: 'list', type: 'list',
name: 'values.NEXT_PUBLIC_BILLING_PROVIDER', name: 'values.NEXT_PUBLIC_BILLING_PROVIDER',
message: message: 'What is the billing provider you want to use?',
'What is the billing provider you want to use? (leave empty for stripe)',
choices: ['stripe', 'lemon-squeezy'], choices: ['stripe', 'lemon-squeezy'],
default: 'stripe', default: allVariables.NEXT_PUBLIC_BILLING_PROVIDER ?? 'stripe',
}, },
{ {
when: (answers) => when: (answers) =>
@@ -174,6 +184,7 @@ export function createEnvironmentVariablesGenerator(
type: 'input', type: 'input',
name: 'values.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY', name: 'values.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY',
message: 'What is the Stripe publishable key?', message: 'What is the Stripe publishable key?',
default: allVariables.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
}, },
{ {
when: (answers) => when: (answers) =>
@@ -202,6 +213,7 @@ export function createEnvironmentVariablesGenerator(
type: 'input', type: 'input',
name: 'values.LEMON_SQUEEZY_STORE_ID', name: 'values.LEMON_SQUEEZY_STORE_ID',
message: 'What is the Lemon Squeezy store ID?', message: 'What is the Lemon Squeezy store ID?',
default: allVariables.LEMON_SQUEEZY_STORE_ID,
}, },
{ {
when: (answers) => when: (answers) =>
@@ -218,17 +230,16 @@ export function createEnvironmentVariablesGenerator(
{ {
type: 'list', type: 'list',
name: 'values.CMS_CLIENT', name: 'values.CMS_CLIENT',
message: message: 'What is the CMS client you want to use?',
'What is the CMS client you want to use? (leave empty for keystatic)',
choices: ['keystatic', 'wordpress'], choices: ['keystatic', 'wordpress'],
default: 'keystatic', default: allVariables.CMS_CLIENT ?? 'keystatic',
}, },
{ {
type: 'list', type: 'list',
name: 'values.MAILER_PROVIDER', name: 'values.MAILER_PROVIDER',
message: 'What is the mailer provider you want to use?', message: 'What is the mailer provider you want to use?',
choices: ['nodemailer', 'resend'], choices: ['nodemailer', 'resend'],
default: 'nodemailer', default: allVariables.MAILER_PROVIDER ?? 'nodemailer',
}, },
{ {
when: (answers) => answers.values.MAILER_PROVIDER === 'resend', when: (answers) => answers.values.MAILER_PROVIDER === 'resend',
@@ -270,8 +281,7 @@ export function createEnvironmentVariablesGenerator(
when: (answers) => answers.values.MAILER_PROVIDER === 'nodemailer', when: (answers) => answers.values.MAILER_PROVIDER === 'nodemailer',
type: 'input', type: 'input',
name: 'values.EMAIL_TLS', name: 'values.EMAIL_TLS',
message: message: 'Do you want to enable TLS for your emails?',
'Do you want to enable TLS for your emails? (leave empty for true)',
default: 'true', default: 'true',
}, },
], ],

View File

@@ -1,8 +1,8 @@
import type { PlopTypes } from '@turbo/gen'; import type { PlopTypes } from '@turbo/gen';
import { readFileSync } from 'node:fs';
// quick hack to avoid installing zod globally // quick hack to avoid installing zod globally
import { z } from '../../../../apps/web/node_modules/zod'; import { z } from '../../../../apps/web/node_modules/zod';
import { generator } from '../../utils';
const BooleanStringEnum = z.enum(['true', 'false']); const BooleanStringEnum = z.enum(['true', 'false']);
@@ -156,26 +156,11 @@ export function createEnvironmentVariablesValidatorGenerator(
throw new Error('URL is required'); throw new Error('URL is required');
} }
const file = readFileSync(answers.path as string, 'utf-8'); const env = generator.loadEnvironmentVariables(answers.path as string);
if (!file) {
throw new Error('File is empty');
}
const vars = file.split('\n').filter((line) => line.trim() !== '');
for (const line of vars) {
const [key, value] = line.split('=');
if (!key) {
throw new Error(`The line ${line} has no key`);
}
if (!value) {
console.warn(`The value ${key} has no value`);
}
for (const key of Object.keys(env)) {
const property = Schema[key]; const property = Schema[key];
const value = env[key];
if (property) { if (property) {
// parse with Zod // parse with Zod
@@ -183,7 +168,7 @@ export function createEnvironmentVariablesValidatorGenerator(
if (error) { if (error) {
throw new Error( throw new Error(
`Encountered a validation error for key ${key}: ${JSON.stringify(error, null, 2)}`, `Encountered a validation error for key ${key}:${value} \n\n${JSON.stringify(error, null, 2)}`,
); );
} else { } else {
console.log(`Key ${key} is valid!`); console.log(`Key ${key} is valid!`);

View File

@@ -0,0 +1,40 @@
import { readFileSync } from 'node:fs';
export namespace generator {
export function loadAllEnvironmentVariables(basePath: string) {
const sharedEnv = loadEnvironmentVariables(basePath + '/.env');
const productionEnv = loadEnvironmentVariables(
basePath + '/.env.production',
);
return {
...sharedEnv,
...productionEnv,
};
}
export function loadEnvironmentVariables(filePath: string) {
const file = readFileSync(filePath, 'utf-8');
const vars = file.split('\n').filter((line) => line.trim() !== '');
const env: Record<string, string> = {};
for (const line of vars) {
const isComment = line.startsWith('#');
if (isComment) {
continue;
}
const [key, value] = line.split('=');
if (!key) {
continue;
}
env[key] = value ?? '';
}
return env;
}
}