Files
30-seconds-of-code/node_modules/gatsby/dist/bootstrap/load-plugins/load.js
2019-08-20 15:52:05 +02:00

195 lines
5.9 KiB
JavaScript

"use strict";
const _ = require(`lodash`);
const slash = require(`slash`);
const fs = require(`fs`);
const path = require(`path`);
const crypto = require(`crypto`);
const glob = require(`glob`);
const {
warnOnIncompatiblePeerDependency
} = require(`./validate`);
const {
store
} = require(`../../redux`);
const existsSync = require(`fs-exists-cached`).sync;
const createNodeId = require(`../../utils/create-node-id`);
const createRequireFromPath = require(`../../utils/create-require-from-path`);
function createFileContentHash(root, globPattern) {
const hash = crypto.createHash(`md5`);
const files = glob.sync(`${root}/${globPattern}`, {
nodir: true
});
files.forEach(filepath => {
hash.update(fs.readFileSync(filepath));
});
return hash.digest(`hex`);
}
/**
* Make sure key is unique to plugin options. E.g there could
* be multiple source-filesystem plugins, with different names
* (docs, blogs).
* @param {*} name Name of the plugin
* @param {*} pluginObject
*/
const createPluginId = (name, pluginObject = null) => createNodeId(name + (pluginObject ? JSON.stringify(pluginObject.options) : ``), `Plugin`);
/**
* @typedef {Object} PluginInfo
* @property {string} resolve The absolute path to the plugin
* @property {string} name The plugin name
* @property {string} version The plugin version (can be content hash)
*/
/**
* resolvePlugin
* @param {string} pluginName
* This can be a name of a local plugin, the name of a plugin located in
* node_modules, or a Gatsby internal plugin. In the last case the pluginName
* will be an absolute path.
* @param {string} rootDir
* This is the project location, from which are found the plugins
* @return {PluginInfo}
*/
function resolvePlugin(pluginName, rootDir) {
// Only find plugins when we're not given an absolute path
if (!existsSync(pluginName)) {
// Find the plugin in the local plugins folder
const resolvedPath = slash(path.resolve(`./plugins/${pluginName}`));
if (existsSync(resolvedPath)) {
if (existsSync(`${resolvedPath}/package.json`)) {
const packageJSON = JSON.parse(fs.readFileSync(`${resolvedPath}/package.json`, `utf-8`));
const name = packageJSON.name || pluginName;
warnOnIncompatiblePeerDependency(name, packageJSON);
return {
resolve: resolvedPath,
name,
id: createPluginId(name),
version: packageJSON.version || createFileContentHash(resolvedPath, `**`)
};
} else {
// Make package.json a requirement for local plugins too
throw new Error(`Plugin ${pluginName} requires a package.json file`);
}
}
}
/**
* Here we have an absolute path to an internal plugin, or a name of a module
* which should be located in node_modules.
*/
try {
const requireSource = rootDir !== null ? createRequireFromPath(`${rootDir}/:internal:`) : require;
const resolvedPath = slash(path.dirname(requireSource.resolve(pluginName)));
const packageJSON = JSON.parse(fs.readFileSync(`${resolvedPath}/package.json`, `utf-8`));
warnOnIncompatiblePeerDependency(packageJSON.name, packageJSON);
return {
resolve: resolvedPath,
id: createPluginId(packageJSON.name),
name: packageJSON.name,
version: packageJSON.version
};
} catch (err) {
throw new Error(`Unable to find plugin "${pluginName}". Perhaps you need to install its package?`);
}
}
module.exports = (config = {}, rootDir = null) => {
// Instantiate plugins.
const plugins = []; // Create fake little site with a plugin for testing this
// w/ snapshots. Move plugin processing to its own module.
// Also test adding to redux store.
const processPlugin = plugin => {
if (_.isString(plugin)) {
const info = resolvePlugin(plugin, rootDir);
return Object.assign({}, info, {
pluginOptions: {
plugins: []
}
});
} else {
plugin.options = plugin.options || {}; // Plugins can have plugins.
const subplugins = [];
if (plugin.options.plugins) {
plugin.options.plugins.forEach(p => {
subplugins.push(processPlugin(p));
});
plugin.options.plugins = subplugins;
} // Add some default values for tests as we don't actually
// want to try to load anything during tests.
if (plugin.resolve === `___TEST___`) {
const name = `TEST`;
return {
id: createPluginId(name, plugin),
name,
pluginOptions: {
plugins: []
}
};
}
const info = resolvePlugin(plugin.resolve, rootDir);
return Object.assign({}, info, {
id: createPluginId(info.name, plugin),
pluginOptions: _.merge({
plugins: []
}, plugin.options)
});
}
}; // Add internal plugins
const internalPlugins = [`../../internal-plugins/dev-404-page`, `../../internal-plugins/load-babel-config`, `../../internal-plugins/internal-data-bridge`, `../../internal-plugins/prod-404`, `../../internal-plugins/webpack-theme-component-shadowing`];
internalPlugins.forEach(relPath => {
const absPath = path.join(__dirname, relPath);
plugins.push(processPlugin(absPath));
}); // Add plugins from the site config.
if (config.plugins) {
config.plugins.forEach(plugin => {
plugins.push(processPlugin(plugin));
});
} // Add the site's default "plugin" i.e. gatsby-x files in root of site.
plugins.push({
resolve: slash(process.cwd()),
id: createPluginId(`default-site-plugin`),
name: `default-site-plugin`,
version: createFileContentHash(process.cwd(), `gatsby-*`),
pluginOptions: {
plugins: []
}
});
const program = store.getState().program;
plugins.push(processPlugin({
resolve: require.resolve(`gatsby-plugin-page-creator`),
options: {
path: slash(path.join(program.directory, `src/pages`)),
pathCheck: false
}
}));
return plugins;
};
//# sourceMappingURL=load.js.map