XeNote/.yarn/unplugged/@ampproject-toolbox-optimizer-virtual-871245ee15/node_modules/@ampproject/toolbox-optimizer/lib/fetchRuntimeParameters.js

263 lines
8.7 KiB
JavaScript
Raw Normal View History

2021-05-17 01:16:19 +00:00
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'mode strict';
const validatorRulesProvider = require('@ampproject/toolbox-validator-rules');
const {MaxAge} = require('@ampproject/toolbox-core');
const {
AMP_CACHE_HOST,
AMP_RUNTIME_CSS_PATH,
AMP_VALIDATION_RULES_URL,
appendRuntimeVersion,
} = require('./AmpConstants.js');
const KEY_VALIDATOR_RULES = 'validator-rules';
const AMP_RUNTIME_MAX_AGE = 10 * 60; // 10 min
const cache = require('./cache.js');
/**
* Initializes the runtime parameters used by the transformers based on given config and parameter values.
* If missing, the following parameters are fetched from cdn.ampproject.org:
*
* - validatorRules: the latest version of the AMP validator rules as served from https://cdn.ampproject.org/v0/validator.json
* - ampRuntimeVersion: the latest AMP runtime version or the latest lts version if the lts flag is set
* - ampRuntimeStyles: the latest AMP runtime CSS styles or the latest lts CSS styles if the lts flag is set
*
* @param {Object} config - the AMP Optimizer config
* @param {Object} customRuntimeParameters - user defined runtime parameters
* @returns {Promise<Object>} - the runtime parameters
*/
async function fetchRuntimeParameters(config, customRuntimeParameters = {}) {
const runtimeParameters = Object.assign({}, customRuntimeParameters);
// Configure the log level
runtimeParameters.verbose = customRuntimeParameters.verbose || config.verbose || false;
// Validation rules can be downloaded in parallel
const validationRulePromise = initValidatorRules(
runtimeParameters,
customRuntimeParameters,
config
);
await initRuntimeVersion(runtimeParameters, customRuntimeParameters, config);
// Runtime Styles depend on the Runtime version
await initRuntimeStyles(runtimeParameters, config);
// Make sure validation rules are downloaded
await validationRulePromise;
return runtimeParameters;
}
/**
* Fetches the AMP validator rules if they're not provided.
*
* @private
*/
async function initValidatorRules(runtimeParameters, customRuntimeParameters, config) {
try {
runtimeParameters.validatorRules =
customRuntimeParameters.validatorRules ||
config.validatorRules ||
(await fetchValidatorRulesFromCache_(config));
} catch (error) {
config.log.error('Could not fetch validator rules', error);
}
}
/**
* @private
*/
async function fetchValidatorRulesFromCache_(config) {
if (config.cache === false) {
return fetchValidatorRules_(config);
}
let rawRules = await cache.get('validator-rules');
let validatorRules;
if (!rawRules) {
validatorRules = await fetchValidatorRules_(config);
config.log.debug('Downloaded AMP validation rules');
// We save the raw rules to make the validation rules JSON serializable
cache.set(KEY_VALIDATOR_RULES, validatorRules.raw);
} else {
validatorRules = await validatorRulesProvider.fetch({rules: rawRules});
}
return validatorRules;
}
async function fetchValidatorRules_(config) {
const response = await config.fetch(AMP_VALIDATION_RULES_URL);
if (!response.ok) {
return null;
}
return validatorRulesProvider.fetch({rules: await response.json()});
}
/**
* Fetch runtime styles based on the runtime version
*
* @private
*/
async function initRuntimeStyles(runtimeParameters, config) {
try {
runtimeParameters.ampRuntimeStyles =
runtimeParameters.ampRuntimeStyles ||
(await fetchAmpRuntimeStyles_(
config,
runtimeParameters.ampUrlPrefix,
runtimeParameters.ampRuntimeVersion
));
} catch (error) {
config.log.error('Could not fetch AMP runtime CSS', error);
}
}
/**
* Use provided runtime version or fetch latest (lts) version.
*
* @private
*/
async function initRuntimeVersion(runtimeParameters, customRuntimeParameters, config) {
// Copy lts and rtv runtime flag from custom parameters or the static config. Both are disabled by default.
runtimeParameters.lts = customRuntimeParameters.lts || config.lts || false;
runtimeParameters.rtv = customRuntimeParameters.rtv || config.rtv || false;
let {ampUrlPrefix, ampRuntimeVersion, lts} = runtimeParameters;
if (lts && ampRuntimeVersion) {
config.log.warn(
'`ampRuntimeVersion` and `lts` cannot be defined at the same time. Using LTS version.'
);
ampRuntimeVersion = '';
}
try {
runtimeParameters.ampRuntimeVersion =
ampRuntimeVersion || (await fetchAmpRuntimeVersion_({config, ampUrlPrefix, lts}));
} catch (error) {
config.log.error('Could not fetch latest AMP runtime version', error);
}
}
/**
* @private
*/
async function fetchAmpRuntimeVersion_(context) {
if (context.config.cache === false) {
return (await fetchLatestRuntimeData_(context)).version;
}
const versionKey = context.ampUrlPrefix + '-' + context.lts;
let ampRuntimeData = await cache.get(versionKey);
if (!ampRuntimeData) {
ampRuntimeData = await fetchLatestRuntimeData_(context, versionKey);
context.config.log.debug('Downloaded AMP runtime v' + ampRuntimeData.version);
} else if (MaxAge.fromObject(ampRuntimeData.maxAge).isExpired()) {
// return the cached version, but update the cache in the background
fetchLatestRuntimeData_(versionKey, context);
}
return ampRuntimeData.version;
}
/**
* @private
*/
async function fetchLatestRuntimeData_({config, ampUrlPrefix, lts}, versionKey = null) {
let ampRuntimeData;
ampRuntimeData = {
version: await config.runtimeVersion.currentVersion({ampUrlPrefix, lts}),
maxAge: MaxAge.create(AMP_RUNTIME_MAX_AGE).toObject(),
};
if (!ampRuntimeData.version && ampUrlPrefix !== AMP_CACHE_HOST) {
config.log.error(
`Could not download runtime version from ${ampUrlPrefix}. Falling back to ${AMP_CACHE_HOST}`
);
ampRuntimeData = await fetchLatestRuntimeData_(
{config, ampUrlPrefix: AMP_CACHE_HOST, lts},
versionKey
);
} else if (ampRuntimeData.version && versionKey) {
cache.set(versionKey, ampRuntimeData);
}
return ampRuntimeData;
}
/**
* @private
*/
async function fetchAmpRuntimeStyles_(config, ampUrlPrefix, ampRuntimeVersion) {
if (ampUrlPrefix && !isAbsoluteUrl_(ampUrlPrefix)) {
config.log.warn(
`AMP runtime styles cannot be fetched from relative ampUrlPrefix, please use the 'ampRuntimeStyles' parameter to provide the correct runtime style. Falling back to latest v0.css on ${AMP_CACHE_HOST}`
);
// Gracefully fallback to latest runtime version
ampUrlPrefix = AMP_CACHE_HOST;
ampRuntimeVersion = ampRuntimeVersion || (await config.runtimeVersion.currentVersion());
}
// Construct the AMP runtime CSS download URL, the default is: https://cdn.ampproject.org/rtv/${ampRuntimeVersion}/v0.css
const runtimeCssUrl =
appendRuntimeVersion(ampUrlPrefix || AMP_CACHE_HOST, ampRuntimeVersion) + AMP_RUNTIME_CSS_PATH;
// Fetch runtime styles
const styles = await downloadAmpRuntimeStyles_(config, runtimeCssUrl);
if (!styles) {
config.log.error(`Could not download ${runtimeCssUrl}. Falling back to latest v0.css.`);
if (ampUrlPrefix || ampRuntimeVersion) {
// Try to download latest from cdn.ampproject.org instead
return fetchAmpRuntimeStyles_(
config,
AMP_CACHE_HOST,
await config.runtimeVersion.currentVersion()
);
} else {
return '';
}
}
return styles;
}
/**
* @private
*/
async function downloadAmpRuntimeStyles_(config, runtimeCssUrl) {
let styles;
if (config.cache !== false) {
styles = await cache.get(runtimeCssUrl);
}
if (!styles) {
const response = await config.fetch(runtimeCssUrl);
if (!response.ok) {
return null;
}
styles = await response.text();
// HACK: patch v0.css to support transforming amp-img -> img
// TODO remove once v0.css has been updated
if (!styles.includes('i-amphtml-ssr')) {
styles += `amp-img[i-amphtml-ssr]:not(.i-amphtml-element):not([layout=container])>*{display: block;}`;
}
config.log.debug(`Downloaded AMP runtime styles from ${runtimeCssUrl}`);
if (config.cache !== false) {
cache.set(runtimeCssUrl, styles);
}
}
return styles;
}
/**
* @private
*/
function isAbsoluteUrl_(url) {
try {
new URL(url);
return true;
} catch (ex) {
return false;
}
}
module.exports = fetchRuntimeParameters;