chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
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())
|
||||
}
|
||||
Reference in New Issue
Block a user