119 lines
3.2 KiB
JavaScript
119 lines
3.2 KiB
JavaScript
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
import { PROFILE_DOT_ENV_PATH } from '@/constants'
|
|
|
|
import { createSetupStatus } from './setup-status'
|
|
|
|
const DOT_ENV_PATH = PROFILE_DOT_ENV_PATH
|
|
const DOT_ENV_SAMPLE_PATH = path.join(process.cwd(), '.env.sample')
|
|
const ENV_LINE_SEPARATOR_PATTERN = /\r?\n/
|
|
|
|
function splitEnvLines(content) {
|
|
return content.split(ENV_LINE_SEPARATOR_PATTERN)
|
|
}
|
|
|
|
function getEnvVariableName(line) {
|
|
const trimmedLine = line.trim()
|
|
|
|
if (
|
|
trimmedLine === '' ||
|
|
trimmedLine.startsWith('#') ||
|
|
!trimmedLine.includes('=')
|
|
) {
|
|
return null
|
|
}
|
|
|
|
const variableName = trimmedLine.slice(0, trimmedLine.indexOf('=')).trim()
|
|
|
|
return /^[A-Z0-9_]+$/.test(variableName) ? variableName : null
|
|
}
|
|
|
|
async function mergeMissingEnvVariables() {
|
|
const sampleContent = await fs.promises.readFile(DOT_ENV_SAMPLE_PATH, 'utf8')
|
|
const dotEnvContent = await fs.promises.readFile(DOT_ENV_PATH, 'utf8')
|
|
const dotEnvLines = splitEnvLines(dotEnvContent)
|
|
const existingVariableNames = new Set(
|
|
dotEnvLines
|
|
.map((line) => getEnvVariableName(line))
|
|
.filter((variableName) => variableName !== null)
|
|
)
|
|
const missingSampleLines = sampleContent
|
|
.split(ENV_LINE_SEPARATOR_PATTERN)
|
|
.filter((line) => {
|
|
const variableName = getEnvVariableName(line)
|
|
|
|
return variableName !== null && !existingVariableNames.has(variableName)
|
|
})
|
|
|
|
if (missingSampleLines.length === 0) {
|
|
return '.env: up-to-date'
|
|
}
|
|
|
|
const normalizedDotEnvContent = dotEnvContent.endsWith('\n')
|
|
? dotEnvContent
|
|
: `${dotEnvContent}\n`
|
|
const separator = normalizedDotEnvContent.trim() === '' ? '' : '\n'
|
|
const mergedContent = `${normalizedDotEnvContent}${separator}${missingSampleLines.join('\n')}\n`
|
|
|
|
await fs.promises.writeFile(DOT_ENV_PATH, mergedContent)
|
|
|
|
return `.env: +${missingSampleLines.length} variable${
|
|
missingSampleLines.length > 1 ? 's' : ''
|
|
}`
|
|
}
|
|
|
|
/**
|
|
* Upsert a single variable inside `.env`.
|
|
*/
|
|
export async function updateDotEnvVariable(variableName, value) {
|
|
const dotEnvContent = fs.existsSync(DOT_ENV_PATH)
|
|
? await fs.promises.readFile(DOT_ENV_PATH, 'utf8')
|
|
: ''
|
|
const dotEnvLines = dotEnvContent === '' ? [] : splitEnvLines(dotEnvContent)
|
|
const nextLine = `${variableName}=${value}`
|
|
let hasUpdatedLine = false
|
|
|
|
const updatedLines = dotEnvLines.map((line) => {
|
|
if (getEnvVariableName(line) !== variableName) {
|
|
return line
|
|
}
|
|
|
|
hasUpdatedLine = true
|
|
|
|
return nextLine
|
|
})
|
|
|
|
if (!hasUpdatedLine) {
|
|
updatedLines.push(nextLine)
|
|
}
|
|
|
|
const normalizedLines = updatedLines.filter(
|
|
(line, index, lines) => !(index === lines.length - 1 && line === '')
|
|
)
|
|
|
|
await fs.promises.mkdir(path.dirname(DOT_ENV_PATH), { recursive: true })
|
|
|
|
await fs.promises.writeFile(
|
|
DOT_ENV_PATH,
|
|
`${normalizedLines.join('\n')}\n`
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Duplicate the .env.sample to .env file
|
|
*/
|
|
export default async () => {
|
|
const status = createSetupStatus('Preparing .env...').start()
|
|
|
|
if (!fs.existsSync(DOT_ENV_PATH)) {
|
|
await fs.promises.mkdir(path.dirname(DOT_ENV_PATH), { recursive: true })
|
|
await fs.promises.copyFile(DOT_ENV_SAMPLE_PATH, DOT_ENV_PATH)
|
|
status.succeed('.env: created')
|
|
|
|
return
|
|
}
|
|
|
|
status.succeed(await mergeMissingEnvVariables())
|
|
}
|