in server/aws-lsp-identity/src/sharedConfig/saveSharedConfigFile.ts [12:93]
export async function saveSharedConfigFile(
filepath: string,
filetype: IniFileType,
file: ParsedIniData
): Promise<void> {
const output: string[] = []
const filepathExists = fs.existsSync(filepath)
if (filepathExists) {
// Be tolerant of line terminator in file regardless of OS
const input = (await readFile(filepath, { encoding: 'utf-8' })).split(/\r?\n/)
for (let it = new IniFileIterator(input.values(), output), { done, value } = it.nextSectionHeader(); !done; ) {
if (!value) {
continue
}
// Write updated section
const parsedSectionName = value.toParsedSectionName()
const newSettingsIndices: Record<string, number> = {}
if (file[parsedSectionName] && Object.keys(file[parsedSectionName]).length > 0) {
const fileSection = file[parsedSectionName]
newSettingsIndices[sectionHeaderKey] = output.push(value.toIniSectionName(filetype))
// Remove/update settings in file based on in-memory section
;({ done, value } = it.nextSectionHeader((setting: Setting) => {
const parsedSettingName = setting.toParsedSettingName()
// If setting in file, but not in-memory then skip it (it's been deleted)
if (!fileSection[parsedSettingName]) {
return
}
// Otherwise, update setting in file regardless
setting.value = fileSection[parsedSettingName]
// Done with this setting, delete so that only new settings are left at the end of the loop
// This also prevents writing redundant settings in the same section
delete fileSection[parsedSettingName]
if (setting.subsection && !newSettingsIndices[setting.subsection]) {
newSettingsIndices[setting.subsection] = output.push(setting.toIniSubsectionHeaderLine())
}
newSettingsIndices[nextSettingKey] = output.push(setting.toIniSettingLine())
}))
// Insert remaining settings (i.e. new settings) after last setting written or under section header
// to avoid splitting settings across commented out sections at the end (which may be a commented out section itself)
outputNewSettings(fileSection, newSettingsIndices, output)
} else {
// Delete no longer existing section by not emitting anything until the next section header is read
// eslint-disable-next-line no-extra-semi
;({ done, value } = it.nextSectionHeader())
}
// Delete processed sections from file so only new sections in file are left at the end of this loop
delete file[parsedSectionName]
}
}
// Add remaining sections (i.e. new sections) to the end of the file
for (const [parsedSectionName, parsedSectionSettings] of Object.entries(file).sort(compareObjectEntries)) {
if (parsedSectionName && parsedSectionSettings && Object.keys(parsedSectionSettings).length > 0) {
const newSettingsIndices: Record<string, number> = {}
output.push('') // Empty line
newSettingsIndices[sectionHeaderKey] = output.push(
SectionHeader.fromParsedSectionName(parsedSectionName).toIniSectionName(filetype)
)
outputNewSettings(parsedSectionSettings, newSettingsIndices, output)
}
}
// Backup file just in case
if (filepathExists) {
await rename(filepath, filepath + '~')
}
await mkdir(path.dirname(filepath), { mode: 0o755, recursive: true })
await writeFile(filepath, output.join(EOL), { encoding: 'utf-8', flush: true })
}