@netlify/build
Version:
Netlify build module
126 lines (125 loc) • 6.46 kB
JavaScript
import { relative, normalize, join } from 'path';
import { getCacheDir } from '@netlify/cache-utils';
import mapObj from 'map-obj';
import { pathExists } from 'path-exists';
import { ROOT_PACKAGE_JSON } from '../utils/json.js';
/**
* Retrieve constants passed to plugins
*/
export const getConstants = async function ({ configPath, buildDir, packagePath, functionsDistDir, edgeFunctionsDistDir, cacheDir, netlifyConfig, siteInfo: { id: siteId, account_id: accountId }, apiHost, token, mode, }) {
const isLocal = mode !== 'buildbot';
const normalizedCacheDir = getCacheDir({ cacheDir, cwd: join(buildDir, packagePath || '') });
const constants = {
// Path to the Netlify configuration file
CONFIG_PATH: configPath,
// In monorepos this is the path that is used to point to a package that should be deployed
PACKAGE_PATH: packagePath,
// The directory where built serverless functions are placed before deployment
// only on local development join with the packagePath as this directory
// on buildbot this `functionsDistDir` is an absolute path to `/tmp/zisi-.....` so we cannot join it with the pacakgePath
FUNCTIONS_DIST: !isLocal ? functionsDistDir : join(packagePath || '', functionsDistDir),
// The directory where built Edge Functions are placed before deployment
// only on local development join with the packagePath as this directory
// on buildbot this `functionsDistDir` is an absolute path to `/tmp/zisi-.....` so we cannot join it with the pacakgePath
EDGE_FUNCTIONS_DIST: !isLocal ? edgeFunctionsDistDir : join(packagePath || '', edgeFunctionsDistDir),
// Path to the Netlify build cache folder
CACHE_DIR: normalizedCacheDir,
// Boolean indicating whether the build was run locally (Netlify CLI) or in the production CI
IS_LOCAL: isLocal,
// The version of Netlify Build
NETLIFY_BUILD_VERSION: ROOT_PACKAGE_JSON.version,
// The Netlify Site ID
SITE_ID: siteId,
// The Netlify Account ID
ACCOUNT_ID: accountId,
// The Netlify API access token
NETLIFY_API_TOKEN: token,
// The Netlify API host
NETLIFY_API_HOST: apiHost,
// The directory where internal functions (i.e. generated programmatically
// via plugins or others) live
INTERNAL_FUNCTIONS_SRC: join(buildDir, packagePath || '', INTERNAL_FUNCTIONS_SRC),
// The directory where internal Edge Functions (i.e. generated programmatically
// via plugins or others) live
INTERNAL_EDGE_FUNCTIONS_SRC: join(buildDir, packagePath || '', INTERNAL_EDGE_FUNCTIONS_SRC),
};
return (await addMutableConstants({ constants, buildDir, netlifyConfig }));
};
const INTERNAL_EDGE_FUNCTIONS_SRC = '.netlify/edge-functions';
const INTERNAL_FUNCTIONS_SRC = '.netlify/functions-internal';
// Retrieve constants which might change during the build if a plugin modifies
// `netlifyConfig` or creates some default directories.
// Unlike readonly constants, this is called again before each build step.
export const addMutableConstants = async function ({ constants, buildDir, netlifyConfig: { build: { publish, edge_functions: edgeFunctions }, functionsDirectory, }, }) {
const constantsA = {
...constants,
// Directory that contains the deploy-ready HTML files and assets generated by the build
PUBLISH_DIR: publish,
// The directory where function source code lives
FUNCTIONS_SRC: functionsDirectory,
// The directory where Edge Functions source code lives
EDGE_FUNCTIONS_SRC: edgeFunctions,
};
const constantsB = await addDefaultConstants(constantsA, buildDir);
const constantsC = normalizeConstantsPaths(constantsB, buildDir);
return constantsC;
};
// Some `constants` have a default value when a specific file exists.
// Those default values are assigned by `@netlify/config`. However, the build
// command or plugins might create those specific files, in which case, the
// related `constant` should be updated, unless the user has explicitly
// configured it.
const addDefaultConstants = async function (constants, buildDir) {
const newConstants = await Promise.all(DEFAULT_PATHS.map(({ constantName, defaultPath }) => addDefaultConstant({ constants, constantName, defaultPath, buildDir })));
return Object.assign({}, constants, ...newConstants);
};
// The current directory is the build directory, which is correct, so we don't
// need to resolve paths
const DEFAULT_PATHS = [
// @todo Remove once we drop support for the legacy default functions directory.
{ constantName: 'FUNCTIONS_SRC', defaultPath: 'netlify-automatic-functions' },
{ constantName: 'FUNCTIONS_SRC', defaultPath: 'netlify/functions' },
{ constantName: 'EDGE_FUNCTIONS_SRC', defaultPath: 'netlify/edge-functions' },
];
const addDefaultConstant = async function ({ constants, constantName, defaultPath, buildDir }) {
// Configuration paths are relative to the build directory.
if (!isEmptyValue(constants[constantName]) || !(await pathExists(`${buildDir}/${defaultPath}`))) {
return {};
}
// However, the plugin child process' current directory is the build directory,
// so we can pass the relative path instead of the resolved absolute path.
return { [constantName]: defaultPath };
};
const normalizeConstantsPaths = function (constants, buildDir) {
return mapObj(constants, (key, path) => [key, normalizePath(path, buildDir, key)]);
};
// The current directory is `buildDir`. Most constants are inside this `buildDir`.
// Instead of passing absolute paths, we pass paths relative to `buildDir`, so
// that logs are less verbose.
const normalizePath = function (path, buildDir, key) {
if (path === undefined || path === '' || !CONSTANT_PATHS.has(key)) {
return path;
}
const pathA = normalize(path);
if (pathA === buildDir) {
return '.';
}
if (pathA.startsWith(buildDir)) {
return relative(buildDir, pathA);
}
return pathA;
};
const isEmptyValue = function (path) {
return path === undefined || path === '';
};
const CONSTANT_PATHS = new Set([
'CONFIG_PATH',
'PUBLISH_DIR',
'FUNCTIONS_SRC',
'FUNCTIONS_DIST',
'INTERNAL_EDGE_FUNCTIONS_SRC',
'INTERNAL_FUNCTIONS_SRC',
'EDGE_FUNCTIONS_DIST',
'EDGE_FUNCTIONS_SRC',
'CACHE_DIR',
]);