2022-03-09 15:13:51 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const path = require('path');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const fs = require('fs-extra');
|
2022-06-07 17:38:48 +02:00
|
|
|
const tsUtils = require('@strapi/typescript-utils');
|
2022-03-22 12:01:17 +01:00
|
|
|
const getCustomAppConfigFile = require('./get-custom-app-config-file');
|
2022-03-09 15:13:51 +01:00
|
|
|
|
2022-08-08 23:33:39 +02:00
|
|
|
const getPkgPath = (name) => path.dirname(require.resolve(`${name}/package.json`));
|
2022-03-09 15:13:51 +01:00
|
|
|
|
|
|
|
async function createPluginsJs(plugins, dest) {
|
2023-08-22 09:37:43 +01:00
|
|
|
const pluginsArray = plugins.map(({ pathToPlugin, name, info }) => {
|
2022-03-09 15:13:51 +01:00
|
|
|
const shortName = _.camelCase(name);
|
|
|
|
|
2023-08-22 09:37:43 +01:00
|
|
|
let realPath = '';
|
|
|
|
|
2022-03-09 15:13:51 +01:00
|
|
|
/**
|
2023-08-22 09:37:43 +01:00
|
|
|
* We're using a module here so we want to keep using the module resolution procedure.
|
2022-03-09 15:13:51 +01:00
|
|
|
*/
|
2023-08-22 09:37:43 +01:00
|
|
|
if (info?.packageName || info?.required) {
|
|
|
|
/**
|
|
|
|
* path.join, on windows, it uses backslashes to resolve path.
|
|
|
|
* The problem is that Webpack does not windows paths
|
|
|
|
* With this tool, we need to rely on "/" and not "\".
|
|
|
|
* This is the reason why '..\\..\\..\\node_modules\\@strapi\\plugin-content-type-builder/strapi-admin.js' was not working.
|
|
|
|
* The regexp at line 105 aims to replace the windows backslashes by standard slash so that webpack can deal with them.
|
|
|
|
* Backslash looks to work only for absolute paths with webpack => https://webpack.js.org/concepts/module-resolution/#absolute-paths
|
|
|
|
*/
|
|
|
|
realPath = path.join(pathToPlugin, 'strapi-admin').replace(/\\/g, '/');
|
|
|
|
} else {
|
|
|
|
realPath = path
|
|
|
|
.join(path.relative(path.resolve(dest, 'admin', 'src'), pathToPlugin), 'strapi-admin')
|
|
|
|
.replace(/\\/g, '/');
|
|
|
|
}
|
2022-03-09 15:13:51 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
name,
|
|
|
|
pathToPlugin: realPath,
|
|
|
|
shortName,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
const content = `
|
|
|
|
${pluginsArray
|
|
|
|
.map(({ pathToPlugin, shortName }) => {
|
|
|
|
const req = `'${pathToPlugin}'`;
|
|
|
|
|
|
|
|
return `import ${shortName} from ${req};`;
|
|
|
|
})
|
|
|
|
.join('\n')}
|
|
|
|
|
|
|
|
|
|
|
|
const plugins = {
|
|
|
|
${[...pluginsArray]
|
|
|
|
.map(({ name, shortName }) => {
|
|
|
|
return ` '${name}': ${shortName},`;
|
|
|
|
})
|
|
|
|
.join('\n')}
|
|
|
|
};
|
|
|
|
|
|
|
|
export default plugins;
|
|
|
|
`;
|
|
|
|
|
|
|
|
return fs.writeFile(path.resolve(dest, 'admin', 'src', 'plugins.js'), content);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function copyAdmin(dest) {
|
|
|
|
const adminPath = getPkgPath('@strapi/admin');
|
|
|
|
|
|
|
|
// TODO copy ee folders for plugins
|
|
|
|
await fs.copy(path.resolve(adminPath, 'ee', 'admin'), path.resolve(dest, 'ee', 'admin'));
|
|
|
|
|
|
|
|
await fs.ensureDir(path.resolve(dest, 'config'));
|
|
|
|
await fs.copy(path.resolve(adminPath, 'admin'), path.resolve(dest, 'admin'));
|
|
|
|
|
|
|
|
// Copy package.json
|
|
|
|
await fs.copy(path.resolve(adminPath, 'package.json'), path.resolve(dest, 'package.json'));
|
|
|
|
}
|
|
|
|
|
2022-04-14 10:59:42 +02:00
|
|
|
async function createCacheDir({ appDir, plugins }) {
|
2022-03-09 17:18:27 +01:00
|
|
|
const cacheDir = path.resolve(appDir, '.cache');
|
2022-03-09 15:13:51 +01:00
|
|
|
|
2022-06-07 17:38:48 +02:00
|
|
|
const useTypeScript = await tsUtils.isUsingTypeScript(
|
|
|
|
path.join(appDir, 'src', 'admin'),
|
|
|
|
'tsconfig.json'
|
|
|
|
);
|
2022-04-14 10:59:42 +02:00
|
|
|
|
2023-08-22 09:37:43 +01:00
|
|
|
const pluginsWithFront = Object.entries(plugins)
|
|
|
|
.filter(([, plugin]) => {
|
|
|
|
/**
|
|
|
|
* 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 `resolve` 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.
|
|
|
|
*
|
|
|
|
* NOTE: we should try to move to `./package.json[exports]` map with bundling of our own plugins,
|
|
|
|
* because these entry files are written in commonjs restricting features e.g. tree-shaking.
|
|
|
|
*/
|
|
|
|
try {
|
|
|
|
const isLocalPluginWithLegacyAdminFile = fs.existsSync(
|
|
|
|
path.resolve(`${plugin.pathToPlugin}/strapi-admin.js`)
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!isLocalPluginWithLegacyAdminFile) {
|
|
|
|
const isModulewithLegacyAdminFile = require.resolve(
|
|
|
|
`${plugin.pathToPlugin}/strapi-admin.js`
|
|
|
|
);
|
|
|
|
|
|
|
|
return isModulewithLegacyAdminFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
return isLocalPluginWithLegacyAdminFile;
|
|
|
|
} catch (err) {
|
|
|
|
if (err.code === 'MODULE_NOT_FOUND') {
|
|
|
|
/**
|
|
|
|
* the plugin does not contain FE code, so we
|
|
|
|
* don't want to import it anyway
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw err;
|
|
|
|
}
|
2022-03-09 15:13:51 +01:00
|
|
|
})
|
2023-08-22 09:37:43 +01:00
|
|
|
.map(([name, plugin]) => ({ name, ...plugin }));
|
2022-03-09 15:13:51 +01:00
|
|
|
|
|
|
|
// create .cache dir
|
|
|
|
await fs.emptyDir(cacheDir);
|
|
|
|
|
|
|
|
// copy admin core code
|
|
|
|
await copyAdmin(cacheDir);
|
|
|
|
|
2022-03-22 12:01:17 +01:00
|
|
|
// Retrieve the custom config file extension
|
|
|
|
const customAdminAppConfigFile = await getCustomAppConfigFile(appDir, useTypeScript);
|
2022-03-09 15:13:51 +01:00
|
|
|
|
2022-03-23 10:39:19 +01:00
|
|
|
if (customAdminAppConfigFile) {
|
2022-03-09 15:13:51 +01:00
|
|
|
const defaultAdminConfigFilePath = path.resolve(cacheDir, 'admin', 'src', 'app.js');
|
2022-03-22 12:01:17 +01:00
|
|
|
const customAdminAppConfigFilePath = path.join(
|
|
|
|
appDir,
|
|
|
|
'src',
|
|
|
|
'admin',
|
|
|
|
customAdminAppConfigFile
|
|
|
|
);
|
|
|
|
const dest = path.resolve(cacheDir, 'admin', 'src', customAdminAppConfigFile);
|
2022-03-09 15:13:51 +01:00
|
|
|
|
|
|
|
if (useTypeScript) {
|
|
|
|
// Remove the default config file
|
|
|
|
await fs.remove(defaultAdminConfigFilePath);
|
|
|
|
// Copy the custom one
|
2022-03-22 12:01:17 +01:00
|
|
|
await fs.copy(customAdminAppConfigFilePath, dest);
|
2022-03-09 15:13:51 +01:00
|
|
|
} else {
|
2022-03-22 12:01:17 +01:00
|
|
|
await fs.copy(customAdminAppConfigFilePath, dest);
|
2022-03-09 15:13:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy admin extensions folder
|
2022-03-09 17:18:27 +01:00
|
|
|
const adminExtensionFolder = path.join(appDir, 'src', 'admin', 'extensions');
|
2022-03-09 15:13:51 +01:00
|
|
|
|
|
|
|
if (fs.existsSync(adminExtensionFolder)) {
|
|
|
|
await fs.copy(adminExtensionFolder, path.resolve(cacheDir, 'admin', 'src', 'extensions'));
|
|
|
|
}
|
|
|
|
|
|
|
|
// create plugins.js with plugins requires
|
|
|
|
await createPluginsJs(pluginsWithFront, cacheDir);
|
2022-04-14 10:59:42 +02:00
|
|
|
|
|
|
|
// create the tsconfig.json file so we can develop plugins in ts while being in a JS project
|
|
|
|
if (!useTypeScript) {
|
2022-06-07 17:38:48 +02:00
|
|
|
await tsUtils.admin.createTSConfigFile(cacheDir);
|
2022-04-14 10:59:42 +02:00
|
|
|
}
|
2022-03-09 15:13:51 +01:00
|
|
|
}
|
|
|
|
|
2023-08-22 09:37:43 +01:00
|
|
|
module.exports = { createCacheDir, createPluginsJs };
|