@netlify/build
Version:
Netlify build module
77 lines (76 loc) • 4.2 kB
JavaScript
import { isDeepStrictEqual } from 'util';
import pFilter from 'p-filter';
import { pathExists } from 'path-exists';
import { resolveUpdatedConfig } from '../core/config.js';
import { addErrorInfo } from '../error/info.js';
import { logConfigOnUpdate } from '../log/messages/config.js';
import { logConfigMutations, systemLogConfigMutations } from '../log/messages/mutations.js';
// If `netlifyConfig` was updated or `_redirects` was created, the configuration
// is updated by calling `@netlify/config` again.
export const updateNetlifyConfig = async function ({ configOpts, netlifyConfig, defaultConfig, headersPath, redirectsPath, configMutations, newConfigMutations, configSideFiles, errorParams, logs, systemLog, debug, source = '', }) {
if (!(await shouldUpdateConfig({ newConfigMutations, configSideFiles, headersPath, redirectsPath }))) {
return { netlifyConfig, configMutations };
}
validateConfigMutations(newConfigMutations);
// Don't log configuration mutations performed by code that has been authored
// by Netlify (i.e. core steps or build plugins in the `@netlify/` scope),
// since that won't give users any useful or actionable information. For
// these, emit a system log instead.
const shouldLogConfigMutationsToUser = source !== '' && !source.startsWith('@netlify/');
if (shouldLogConfigMutationsToUser) {
logConfigMutations(logs, newConfigMutations, debug);
}
else {
systemLogConfigMutations(systemLog, newConfigMutations);
}
const mergedConfigMutations = [...configMutations, ...newConfigMutations];
const { config: netlifyConfigA, headersPath: headersPathA, redirectsPath: redirectsPathA, } = await resolveUpdatedConfig(configOpts, mergedConfigMutations, defaultConfig);
logConfigOnUpdate({ logs, netlifyConfig: netlifyConfigA, debug });
errorParams.netlifyConfig = netlifyConfigA;
return {
netlifyConfig: netlifyConfigA,
configMutations: mergedConfigMutations,
headersPath: headersPathA,
redirectsPath: redirectsPathA,
};
};
const shouldUpdateConfig = async function ({ newConfigMutations, configSideFiles, headersPath, redirectsPath }) {
return (newConfigMutations.length !== 0 || (await haveConfigSideFilesChanged(configSideFiles, headersPath, redirectsPath)));
};
// The configuration mostly depends on `netlify.toml` and UI build settings.
// However, it also uses some additional optional side files like `_redirects`.
// Those are often created by the build command. When those are created, we need
// to update the configuration. We detect this by checking for file existence
// before and after running plugins and the build command.
const haveConfigSideFilesChanged = async function (configSideFiles, headersPath, redirectsPath) {
const newSideFiles = await listConfigSideFiles([headersPath, redirectsPath]);
return !isDeepStrictEqual(newSideFiles, configSideFiles);
};
// List all the files used for configuration besides `netlify.toml`.
// This is useful when applying configuration mutations since those files
// sometimes have higher priority and should therefore be deleted in order to
// apply any configuration update on `netlify.toml`.
export const listConfigSideFiles = async function (sideFiles) {
const configSideFiles = await pFilter(sideFiles, pathExists);
return configSideFiles.sort();
};
// Validate each new configuration change
const validateConfigMutations = function (newConfigMutations) {
try {
newConfigMutations.forEach(validateConfigMutation);
}
catch (error) {
addErrorInfo(error, { type: 'pluginValidation' });
throw error;
}
};
// Triggered when calling `netlifyConfig.{key} = undefined | null`
// We do not allow this because the back-end only receives mutations as a
// `netlify.toml`, i.e. cannot apply property deletions since `undefined` is
// not serializable in TOML.
const validateConfigMutation = function ({ value, keysString }) {
if (value === undefined || value === null) {
throw new Error(`Setting "netlifyConfig.${keysString}" to ${value} is not allowed.
Please set this property to a specific value instead.`);
}
};