@netlify/config
Version:
Netlify config module
98 lines (97 loc) • 4.07 kB
JavaScript
import { existsSync, promises as fs } from 'fs';
import { ensureConfigPriority } from '../context.js';
import { addHeaders } from '../headers.js';
import { mergeConfigs } from '../merge.js';
import { parseOptionalConfig } from '../parse.js';
import { addRedirects } from '../redirects.js';
import { simplifyConfig } from '../simplify.js';
import { serializeToml } from '../utils/toml.js';
import { applyMutations } from './apply.js';
// Persist configuration changes to `netlify.toml`.
// If `netlify.toml` does not exist, creates it. Otherwise, merges the changes.
export const updateConfig = async function (configMutations, { buildDir, configPath, headersPath, outputConfigPath = configPath, redirectsPath, context, branch, logs, featureFlags, }) {
if (configMutations.length === 0) {
return;
}
const inlineConfig = applyMutations({}, configMutations);
const normalizedInlineConfig = ensureConfigPriority(inlineConfig, context, branch);
const updatedConfig = await mergeWithConfig(normalizedInlineConfig, configPath);
const configWithHeaders = await addHeaders({ config: updatedConfig, headersPath, logs });
const finalConfig = await addRedirects({ config: configWithHeaders, redirectsPath, logs, featureFlags });
const simplifiedConfig = simplifyConfig(finalConfig);
await backupConfig({ buildDir, configPath, headersPath, redirectsPath });
await Promise.all([
saveConfig(outputConfigPath, simplifiedConfig),
deleteSideFile(headersPath),
deleteSideFile(redirectsPath),
]);
};
// If `netlify.toml` exists, deeply merges the configuration changes.
const mergeWithConfig = async function (normalizedInlineConfig, configPath) {
const config = await parseOptionalConfig(configPath);
const updatedConfig = mergeConfigs([config, normalizedInlineConfig]);
return updatedConfig;
};
// Serialize the changes to `netlify.toml`
const saveConfig = async function (configPath, simplifiedConfig) {
const serializedConfig = serializeToml(simplifiedConfig);
await fs.writeFile(configPath, serializedConfig);
};
// Deletes `_headers/_redirects` since they are merged to `netlify.toml`,
// to fix any priority problem.
const deleteSideFile = async function (filePath) {
if (filePath === undefined || !existsSync(filePath)) {
return;
}
await fs.unlink(filePath);
};
// Modifications to `netlify.toml` and `_headers/_redirects` are only meant for
// the deployment API call. After it's been performed, we restore their former
// state.
// We do this by backing them up inside some sibling directory.
const backupConfig = async function ({ buildDir, configPath, headersPath, redirectsPath }) {
const tempDir = getTempDir(buildDir);
await fs.mkdir(tempDir, { recursive: true });
await Promise.all([
backupFile(configPath, `${tempDir}/netlify.toml`),
backupFile(headersPath, `${tempDir}/_headers`),
backupFile(redirectsPath, `${tempDir}/_redirects`),
]);
};
export const restoreConfig = async function (configMutations, { buildDir, configPath, headersPath, redirectsPath }) {
if (configMutations.length === 0) {
return;
}
const tempDir = getTempDir(buildDir);
await Promise.all([
copyOrDelete(`${tempDir}/netlify.toml`, configPath),
copyOrDelete(`${tempDir}/_headers`, headersPath),
copyOrDelete(`${tempDir}/_redirects`, redirectsPath),
]);
};
const getTempDir = function (buildDir) {
return `${buildDir}/.netlify/deploy`;
};
const backupFile = async function (original, backup) {
// this makes sure we don't restore stale files
await deleteNoError(backup);
if (!existsSync(original)) {
return;
}
await fs.copyFile(original, backup);
};
const deleteNoError = async (path) => {
try {
await fs.unlink(path);
}
catch {
// continue regardless error
}
};
const copyOrDelete = async function (src, dest) {
if (existsSync(src)) {
await fs.copyFile(src, dest);
return;
}
await deleteNoError(dest);
};