@netlify/build
Version:
Netlify build module
78 lines (77 loc) • 3.64 kB
JavaScript
import { pluginsUrl, pluginsList as oldPluginsList } from '@netlify/plugins-list';
import got from 'got';
import isPlainObj from 'is-plain-obj';
import { logPluginsList, logPluginsFetchError } from '../log/messages/plugins.js';
import { CONDITIONS } from './plugin_conditions.js';
/** 1 minute HTTP request timeout */
const PLUGINS_LIST_TIMEOUT = 6e4;
/**
* Retrieve the list of plugins officially vetted by us and displayed in our
* plugins directory UI.
* We fetch this list during each build (no caching) because we want new
* versions of plugins to be available instantly to all users. The time to
* make this request is somewhat ok (in the 100ms range).
* We only fetch this plugins list when needed, i.e. we defer it as much as
* possible.
*/
export const getPluginsList = async function ({ debug, logs, testOpts: { pluginsListUrl } = {}, }) {
// We try not to mock in integration tests. However, sending a request for
// each test would be too slow and make tests unreliable.
if (pluginsListUrl === 'test') {
return {};
}
const pluginsListUrlA = pluginsListUrl === undefined ? pluginsUrl : pluginsListUrl;
const pluginsList = await fetchPluginsList({ logs, pluginsListUrl: pluginsListUrlA });
const pluginsListA = normalizePluginsList(pluginsList);
logPluginsList({ pluginsList: pluginsListA, debug, logs });
return pluginsListA;
};
const fetchPluginsList = async function ({ logs, pluginsListUrl, }) {
try {
const { body } = await got(pluginsListUrl, { responseType: 'json', timeout: { request: PLUGINS_LIST_TIMEOUT } });
if (!isValidPluginsList(body)) {
throw new Error(`Request succeeded but with an invalid response:\n${JSON.stringify(body, null, 2)}`);
}
return body;
// The Netlify Site should be up. This is a fallback.
// `oldPluginsList` might not contain the latest plugins versions:
// - We should do `npm publish` as soon as a PR is merged in
// `netlify/plugins` but it is possible we don't.
// - Releasing it requires a @netlify/buld release, which requires itself a
// buildbot release.
}
catch (error) {
logPluginsFetchError(logs, error.message);
return oldPluginsList;
}
};
const isValidPluginsList = function (pluginsList) {
return Array.isArray(pluginsList) && pluginsList.every(isPlainObj);
};
const normalizePluginsList = function (pluginsList) {
return Object.fromEntries(pluginsList.map(normalizePluginItem));
};
/**
* `version` in `plugins.json` is the latest version.
* A `compatibility` array of objects can be added to specify conditions to
* apply different versions.
* `netlify/plugins` ensures that `compatibility`:
* - Has the proper shape.
* - Is sorted from the highest to lowest version.
* - Does not include the latest `version`.
*/
const normalizePluginItem = function ({ package: packageName, version, compatibility = [] }) {
const versions = compatibility.length === 0 ? [{ version }] : compatibility;
const versionsA = versions.map(normalizeCompatVersion);
return [packageName, versionsA];
};
const normalizeCompatVersion = function ({ version, migrationGuide, featureFlag, overridePinnedVersion, ...otherProperties }) {
const conditions = Object.entries(otherProperties).filter(isCondition).map(normalizeCondition);
return { version, migrationGuide, overridePinnedVersion, featureFlag, conditions };
};
const isCondition = function ([type]) {
return type in CONDITIONS;
};
const normalizeCondition = function ([type, condition]) {
return { type, condition };
};