@strapi/strapi
Version:
An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite
138 lines (135 loc) • 6.09 kB
JavaScript
import os from 'node:os';
import path from 'node:path';
import fs from 'node:fs';
import camelCase from 'lodash/camelCase';
import { env } from '@strapi/utils';
import { getModule } from './dependencies.mjs';
import { convertModulePathToSystemPath, convertSystemPathToModulePath, loadFile } from './files.mjs';
import { isError } from './errors.mjs';
const validatePackageHasStrapi = (pkg)=>'strapi' in pkg && typeof pkg.strapi === 'object' && !Array.isArray(pkg.strapi) && pkg.strapi !== null;
const validatePackageIsPlugin = (pkg)=>validatePackageHasStrapi(pkg) && pkg.strapi.kind === 'plugin';
const getEnabledPlugins = async ({ cwd, logger, runtimeDir, strapi })=>{
const plugins = {};
/**
* This is the list of dependencies that are installed in the user's project.
* It will include libraries like "react", so we need to collect the ones that
* are plugins.
*/ const deps = strapi.config.get('info.dependencies', {});
logger.debug("Dependencies from user's project", os.EOL, deps);
for (const dep of Object.keys(deps)){
const pkg = await getModule(dep, cwd);
if (pkg && validatePackageIsPlugin(pkg)) {
const name = pkg.strapi.name || pkg.name;
if (!name) {
/**
* Unlikely to happen, but you never know.
*/ throw Error("You're trying to import a plugin that doesn't have a name – check the package.json of that plugin!");
}
plugins[name] = {
name,
importName: camelCase(name),
type: 'module',
modulePath: dep
};
}
}
const userPluginsFile = await loadUserPluginsFile(strapi.dirs.app.config);
logger.debug("User's plugins file", os.EOL, userPluginsFile);
for (const [userPluginName, userPluginConfig] of Object.entries(userPluginsFile)){
if (userPluginConfig.enabled && userPluginConfig.resolve) {
const sysPath = convertModulePathToSystemPath(userPluginConfig.resolve);
plugins[userPluginName] = {
name: userPluginName,
importName: camelCase(userPluginName),
type: 'local',
/**
* User plugin paths are resolved from the entry point
* of the app, because that's how you import them.
*/ modulePath: convertSystemPathToModulePath(path.relative(runtimeDir, sysPath)),
path: sysPath
};
}
}
return plugins;
};
const PLUGIN_CONFIGS = [
'plugins.js',
'plugins.mjs',
'plugins.ts'
];
const loadUserPluginsFile = async (root)=>{
for (const file of PLUGIN_CONFIGS){
const filePath = path.join(root, file);
const configFile = await loadFile(filePath);
if (configFile) {
/**
* Configs can be a function or they can be just an object!
*/ return typeof configFile === 'function' ? configFile({
env
}) : configFile;
}
}
return {};
};
const getMapOfPluginsWithAdmin = (plugins)=>{
/**
* This variable stores the import paths for plugins.
* The keys are the module paths of the plugins, and the values are the paths
* to the admin part of the plugins, which is either loaded from the
* package.json exports or from the legacy strapi-admin.js file.
*/ const pluginImportPaths = {};
return Object.values(plugins).filter((plugin)=>{
if (!plugin) {
return false;
}
/**
* There are two ways a plugin should be imported, either it's local to the strapi app,
* or it's an actual npm module that's installed and resolved via node_modules.
*
* We first check if the plugin is local to the strapi app, using a regular `fs.existsSync` because
* the pathToPlugin will be relative i.e. `/Users/my-name/strapi-app/src/plugins/my-plugin`.
*
* If the file doesn't exist well then it's probably a node_module, so instead we use `require.resolve`
* which will resolve the path to the module in node_modules. If it fails with the specific code `MODULE_NOT_FOUND`
* then it doesn't have an admin part to the package.
*/ try {
const localPluginPath = plugin.path;
if (localPluginPath) {
// Here we are loading a locally installed plugin
const packageJsonPath = path.join(localPluginPath, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const localAdminPath = packageJson?.exports?.['./strapi-admin']?.import;
if (localAdminPath) {
pluginImportPaths[plugin.modulePath] = localAdminPath;
return true;
}
}
// Check if legacy admin file exists in local plugin
if (fs.existsSync(path.join(localPluginPath, 'strapi-admin.js'))) {
pluginImportPaths[plugin.modulePath] = 'strapi-admin';
return true;
}
}
// This plugin is a module, so we need to check if it has a strapi-admin export
if (require.resolve(`${plugin.modulePath}/strapi-admin`)) {
pluginImportPaths[plugin.modulePath] = 'strapi-admin';
return true;
}
return false;
} catch (err) {
if (isError(err) && 'code' in err && (err.code === 'MODULE_NOT_FOUND' || err.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')) {
/**
* the plugin does not contain FE code, so we
* don't want to import it anyway
*/ return false;
}
throw err;
}
}).map((plugin)=>({
...plugin,
modulePath: `${plugin.modulePath}/${pluginImportPaths[plugin.modulePath]}`
}));
};
export { getEnabledPlugins, getMapOfPluginsWithAdmin };
//# sourceMappingURL=plugins.mjs.map