Files
30-seconds-of-code/node_modules/gatsby-plugin-netlify/build-headers-program.js
2019-08-20 15:52:05 +02:00

213 lines
7.1 KiB
JavaScript

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = buildHeadersProgram;
var _lodash = _interopRequireDefault(require("lodash"));
var _fsExtra = require("fs-extra");
var _path = require("path");
var _kebabHash = _interopRequireDefault(require("kebab-hash"));
var _constants = require("./constants");
function validHeaders(headers) {
if (!headers || !_lodash.default.isObject(headers)) {
return false;
}
return _lodash.default.every(headers, (headersList, path) => _lodash.default.isArray(headersList) && _lodash.default.every(headersList, header => _lodash.default.isString(header)));
}
function linkTemplate(assetPath, type = `script`) {
return `Link: <${assetPath}>; rel=preload; as=${type}`;
}
function pathChunkName(path) {
const name = path === `/` ? `index` : (0, _kebabHash.default)(path);
return `path---${name}`;
}
function createScriptHeaderGenerator(manifest, pathPrefix) {
return script => {
const chunk = manifest[script];
if (!chunk) {
return null;
} // convert to array if it's not already
const chunks = _lodash.default.isArray(chunk) ? chunk : [chunk];
return chunks.filter(script => {
const parsed = (0, _path.parse)(script); // handle only .js, .css content is inlined already
// and doesn't need to be pushed
return parsed.ext === `.js`;
}).map(script => linkTemplate(`${pathPrefix}/${script}`)).join(`\n `);
};
}
function linkHeaders(scripts, manifest, pathPrefix) {
return _lodash.default.compact(scripts.map(createScriptHeaderGenerator(manifest, pathPrefix)));
}
function headersPath(pathPrefix, path) {
return `${pathPrefix}${path}`;
}
function preloadHeadersByPage(pages, manifest, pathPrefix) {
let linksByPage = {};
pages.forEach(page => {
const scripts = [..._constants.COMMON_BUNDLES, pathChunkName(page.path), page.componentChunkName];
const pathKey = headersPath(pathPrefix, page.path);
linksByPage[pathKey] = linkHeaders(scripts, manifest, pathPrefix);
});
return linksByPage;
}
function defaultMerge(...headers) {
function unionMerge(objValue, srcValue) {
if (_lodash.default.isArray(objValue)) {
return _lodash.default.union(objValue, srcValue);
} else {
return undefined; // opt into default merge behavior
}
}
return _lodash.default.mergeWith({}, ...headers, unionMerge);
}
function transformLink(manifest, publicFolder, pathPrefix) {
return header => header.replace(_constants.LINK_REGEX, (__, prefix, file, suffix) => {
const hashed = manifest[file];
if (hashed) {
return `${prefix}${pathPrefix}${hashed}${suffix}`;
} else if ((0, _fsExtra.existsSync)(publicFolder(file))) {
return `${prefix}${pathPrefix}${file}${suffix}`;
} else {
throw new Error(`Could not find the file specified in the Link header \`${header}\`.` + `The gatsby-plugin-netlify is looking for a matching file (with or without a ` + `webpack hash). Check the public folder and your gatsby-config.js to ensure you are ` + `pointing to a public file.`);
}
});
} // Writes out headers file format, with two spaces for indentation
// https://www.netlify.com/docs/headers-and-basic-auth/
function stringifyHeaders(headers) {
return _lodash.default.reduce(headers, (text, headerList, path) => {
const headersString = _lodash.default.reduce(headerList, (accum, header) => `${accum} ${header}\n`, ``);
return `${text}${path}\n${headersString}`;
}, ``);
} // program methods
const validateUserOptions = pluginOptions => headers => {
if (!validHeaders(headers)) {
throw new Error(`The "headers" option to gatsby-plugin-netlify is in the wrong shape. ` + `You should pass in a object with string keys (representing the paths) and an array ` + `of strings as the value (representing the headers). ` + `Check your gatsby-config.js.`);
}
;
[`mergeSecurityHeaders`, `mergeLinkHeaders`, `mergeCachingHeaders`].forEach(mergeOption => {
if (!_lodash.default.isBoolean(pluginOptions[mergeOption])) {
throw new Error(`The "${mergeOption}" option to gatsby-plugin-netlify must be a boolean. ` + `Check your gatsby-config.js.`);
}
});
if (!_lodash.default.isFunction(pluginOptions.transformHeaders)) {
throw new Error(`The "transformHeaders" option to gatsby-plugin-netlify must be a function ` + `that returns a array of header strings.` + `Check your gatsby-config.js.`);
}
return headers;
};
const mapUserLinkHeaders = ({
manifest,
pathPrefix,
publicFolder
}) => headers => _lodash.default.mapValues(headers, headerList => _lodash.default.map(headerList, transformLink(manifest, publicFolder, pathPrefix)));
const mapUserLinkAllPageHeaders = (pluginData, {
allPageHeaders
}) => headers => {
if (!allPageHeaders) {
return headers;
}
const {
pages,
manifest,
publicFolder,
pathPrefix
} = pluginData;
const headersList = _lodash.default.map(allPageHeaders, transformLink(manifest, publicFolder, pathPrefix));
const duplicateHeadersByPage = {};
pages.forEach(page => {
const pathKey = headersPath(pathPrefix, page.path);
duplicateHeadersByPage[pathKey] = headersList;
});
return defaultMerge(headers, duplicateHeadersByPage);
};
const applyLinkHeaders = (pluginData, {
mergeLinkHeaders
}) => headers => {
if (!mergeLinkHeaders) {
return headers;
}
const {
pages,
manifest,
pathPrefix
} = pluginData;
const perPageHeaders = preloadHeadersByPage(pages, manifest, pathPrefix);
return defaultMerge(headers, perPageHeaders);
};
const applySecurityHeaders = ({
mergeSecurityHeaders
}) => headers => {
if (!mergeSecurityHeaders) {
return headers;
}
return defaultMerge(headers, _constants.SECURITY_HEADERS);
};
const applyCachingHeaders = (pluginData, {
mergeCachingHeaders
}) => headers => {
if (!mergeCachingHeaders) {
return headers;
}
const chunks = Array.from(pluginData.pages.values()).map(page => page.componentChunkName);
chunks.push(`pages-manifest`, `app`);
const files = [].concat(...chunks.map(chunk => pluginData.manifest[chunk]));
const cachingHeaders = {};
files.forEach(file => {
cachingHeaders[`/` + file] = [_constants.IMMUTABLE_CACHING_HEADER];
});
return defaultMerge(headers, cachingHeaders, _constants.CACHING_HEADERS);
};
const applyTransfromHeaders = ({
transformHeaders
}) => headers => _lodash.default.mapValues(headers, transformHeaders);
const transformToString = headers => `${_constants.HEADER_COMMENT}\n\n${stringifyHeaders(headers)}`;
const writeHeadersFile = ({
publicFolder
}) => contents => (0, _fsExtra.writeFile)(publicFolder(_constants.NETLIFY_HEADERS_FILENAME), contents);
function buildHeadersProgram(pluginData, pluginOptions) {
return _lodash.default.flow(validateUserOptions(pluginOptions), mapUserLinkHeaders(pluginData), applySecurityHeaders(pluginOptions), applyCachingHeaders(pluginData, pluginOptions), mapUserLinkAllPageHeaders(pluginData, pluginOptions), applyLinkHeaders(pluginData, pluginOptions), applyTransfromHeaders(pluginOptions), transformToString, writeHeadersFile(pluginData))(pluginOptions.headers);
}