2020-10-27 11:27:17 +01:00
|
|
|
'use strict';
|
2021-09-08 17:25:24 +02:00
|
|
|
|
2019-04-29 15:48:16 +02:00
|
|
|
const path = require('path');
|
2020-04-15 12:56:51 +02:00
|
|
|
const _ = require('lodash');
|
2019-04-29 15:48:16 +02:00
|
|
|
const fs = require('fs-extra');
|
|
|
|
const webpack = require('webpack');
|
2019-09-23 19:27:40 +02:00
|
|
|
const WebpackDevServer = require('webpack-dev-server');
|
2019-09-24 14:10:57 +02:00
|
|
|
const chalk = require('chalk');
|
2021-07-27 09:26:37 +02:00
|
|
|
const chokidar = require('chokidar');
|
2021-06-23 20:18:13 +02:00
|
|
|
const getWebpackConfig = require('./webpack.config');
|
2019-04-29 15:48:16 +02:00
|
|
|
|
2021-11-30 09:03:52 +01:00
|
|
|
const getPkgPath = name => path.dirname(require.resolve(`${name}/package.json`));
|
|
|
|
|
|
|
|
const DEFAULT_PLUGINS = [
|
|
|
|
'content-type-builder',
|
|
|
|
'content-manager',
|
|
|
|
'upload',
|
|
|
|
'email',
|
|
|
|
'i18n',
|
|
|
|
'users-permissions',
|
|
|
|
];
|
2019-04-29 15:48:16 +02:00
|
|
|
|
2020-04-15 12:56:51 +02:00
|
|
|
function getCustomWebpackConfig(dir, config) {
|
2021-09-22 12:38:12 +02:00
|
|
|
const adminConfigPath = path.join(dir, 'src', 'admin', 'webpack.config.js');
|
2020-07-01 14:02:39 +02:00
|
|
|
|
2021-07-01 15:50:11 +02:00
|
|
|
let webpackConfig = getWebpackConfig(config);
|
2020-04-15 12:56:51 +02:00
|
|
|
|
|
|
|
if (fs.existsSync(adminConfigPath)) {
|
2021-07-21 15:47:55 +02:00
|
|
|
const webpackAdminConfig = require(path.resolve(adminConfigPath));
|
2020-04-15 12:56:51 +02:00
|
|
|
|
2021-07-21 15:47:55 +02:00
|
|
|
if (_.isFunction(webpackAdminConfig)) {
|
|
|
|
webpackConfig = webpackAdminConfig(webpackConfig, webpack);
|
2020-04-15 15:03:53 +02:00
|
|
|
|
|
|
|
if (!webpackConfig) {
|
|
|
|
console.error(
|
|
|
|
`${chalk.red('Error:')} Nothing was returned from your custom webpack configuration`
|
|
|
|
);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2020-04-15 12:56:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return webpackConfig;
|
|
|
|
}
|
|
|
|
|
2021-12-20 15:51:19 +01:00
|
|
|
async function build({ plugins, dir, env, options, optimize, forceBuild }) {
|
2021-11-30 09:03:52 +01:00
|
|
|
const buildAdmin = await shouldBuildAdmin({ dir, plugins });
|
2021-12-20 14:56:06 +01:00
|
|
|
|
2021-12-20 15:51:19 +01:00
|
|
|
if (!buildAdmin && !forceBuild) {
|
2021-11-29 16:13:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-15 11:56:30 +02:00
|
|
|
// Create the cache dir containing the front-end files.
|
2021-09-07 17:11:16 +02:00
|
|
|
await createCacheDir({ dir, plugins });
|
2020-04-15 11:56:30 +02:00
|
|
|
|
|
|
|
const cacheDir = path.resolve(dir, '.cache');
|
2021-05-05 15:21:53 +02:00
|
|
|
const entry = path.resolve(cacheDir, 'admin', 'src');
|
2020-04-15 11:56:30 +02:00
|
|
|
const dest = path.resolve(dir, 'build');
|
2021-07-01 14:19:50 +02:00
|
|
|
|
|
|
|
// Roots for the @strapi/babel-plugin-switch-ee-ce
|
|
|
|
const roots = {
|
|
|
|
eeRoot: path.resolve(cacheDir, 'ee', 'admin'),
|
|
|
|
ceRoot: path.resolve(cacheDir, 'admin', 'src'),
|
|
|
|
};
|
|
|
|
|
2021-11-30 09:03:52 +01:00
|
|
|
const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin);
|
2021-09-09 10:56:57 +02:00
|
|
|
|
|
|
|
const config = getCustomWebpackConfig(dir, {
|
|
|
|
entry,
|
|
|
|
pluginsPath,
|
|
|
|
cacheDir,
|
|
|
|
dest,
|
|
|
|
env,
|
|
|
|
options,
|
|
|
|
optimize,
|
|
|
|
roots,
|
|
|
|
});
|
2020-04-15 11:56:30 +02:00
|
|
|
|
|
|
|
const compiler = webpack(config);
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
compiler.run((err, stats) => {
|
|
|
|
if (err) {
|
2021-07-01 14:19:50 +02:00
|
|
|
console.error(err.stack || err);
|
|
|
|
|
|
|
|
if (err.details) {
|
|
|
|
console.error(err.details);
|
2020-04-15 11:56:30 +02:00
|
|
|
}
|
2021-07-01 14:19:50 +02:00
|
|
|
return reject(err);
|
2020-04-15 11:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-07-01 14:19:50 +02:00
|
|
|
const info = stats.toJson();
|
|
|
|
|
|
|
|
if (stats.hasErrors()) {
|
|
|
|
console.error(info.errors);
|
2020-04-15 11:56:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return resolve({
|
|
|
|
stats,
|
2021-07-01 14:19:50 +02:00
|
|
|
|
|
|
|
warnings: info.warnings,
|
2020-04-15 11:56:30 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-09-07 17:11:16 +02:00
|
|
|
async function createPluginsJs(plugins, dest) {
|
2021-09-08 17:25:24 +02:00
|
|
|
const pluginsArray = plugins.map(({ pathToPlugin, name }) => {
|
2021-09-07 17:11:16 +02:00
|
|
|
const shortName = _.camelCase(name);
|
2021-09-30 14:21:40 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* path.join, on windows, it uses backslashes to resolve path.
|
2021-10-01 10:00:00 +02:00
|
|
|
* 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
|
2021-09-30 14:21:40 +02:00
|
|
|
*/
|
|
|
|
const realPath = path
|
2021-10-01 09:47:42 +02:00
|
|
|
.join(path.relative(path.resolve(dest, 'admin', 'src'), pathToPlugin), 'strapi-admin.js')
|
2021-09-30 14:21:40 +02:00
|
|
|
.replace(/\\/g, '/');
|
|
|
|
|
2021-09-08 17:25:24 +02:00
|
|
|
return {
|
|
|
|
name,
|
2021-09-30 14:21:40 +02:00
|
|
|
pathToPlugin: realPath,
|
2021-09-08 17:25:24 +02:00
|
|
|
shortName,
|
|
|
|
};
|
2021-09-07 17:11:16 +02:00
|
|
|
});
|
2021-05-12 11:42:20 +02:00
|
|
|
|
2019-04-29 15:48:16 +02:00
|
|
|
const content = `
|
2021-09-07 17:11:16 +02:00
|
|
|
${pluginsArray
|
2021-09-08 17:25:24 +02:00
|
|
|
.map(({ pathToPlugin, shortName }) => {
|
2021-09-30 14:21:40 +02:00
|
|
|
const req = `'${pathToPlugin}'`;
|
2021-05-12 11:42:20 +02:00
|
|
|
|
|
|
|
return `import ${shortName} from ${req};`;
|
|
|
|
})
|
|
|
|
.join('\n')}
|
|
|
|
|
|
|
|
|
|
|
|
const plugins = {
|
2021-09-07 17:11:16 +02:00
|
|
|
${[...pluginsArray]
|
2021-05-12 11:42:20 +02:00
|
|
|
.map(({ name, shortName }) => {
|
|
|
|
return ` '${name}': ${shortName},`;
|
|
|
|
})
|
|
|
|
.join('\n')}
|
|
|
|
};
|
|
|
|
|
|
|
|
export default plugins;
|
|
|
|
`;
|
2019-04-29 15:48:16 +02:00
|
|
|
|
2020-03-10 18:59:52 +01:00
|
|
|
return fs.writeFile(path.resolve(dest, 'admin', 'src', 'plugins.js'), content);
|
2019-04-29 15:48:16 +02:00
|
|
|
}
|
|
|
|
|
2020-04-14 15:24:14 +02:00
|
|
|
async function clean({ dir }) {
|
|
|
|
const buildDir = path.join(dir, 'build');
|
|
|
|
const cacheDir = path.join(dir, '.cache');
|
|
|
|
|
|
|
|
fs.removeSync(buildDir);
|
|
|
|
fs.removeSync(cacheDir);
|
|
|
|
}
|
|
|
|
|
2019-04-29 15:48:16 +02:00
|
|
|
async function copyAdmin(dest) {
|
2021-04-29 14:20:36 +02:00
|
|
|
const adminPath = getPkgPath('@strapi/admin');
|
2019-04-29 15:48:16 +02:00
|
|
|
|
2020-07-08 18:34:49 +02:00
|
|
|
// TODO copy ee folders for plugins
|
|
|
|
await fs.copy(path.resolve(adminPath, 'ee', 'admin'), path.resolve(dest, 'ee', 'admin'));
|
|
|
|
|
2019-04-29 15:48:16 +02:00
|
|
|
await fs.ensureDir(path.resolve(dest, 'config'));
|
|
|
|
await fs.copy(path.resolve(adminPath, 'admin'), path.resolve(dest, 'admin'));
|
2020-03-10 18:59:52 +01:00
|
|
|
|
|
|
|
// Copy package.json
|
|
|
|
await fs.copy(path.resolve(adminPath, 'package.json'), path.resolve(dest, 'package.json'));
|
2019-04-29 15:48:16 +02:00
|
|
|
}
|
|
|
|
|
2021-09-07 17:11:16 +02:00
|
|
|
async function createCacheDir({ dir, plugins }) {
|
2019-04-29 15:48:16 +02:00
|
|
|
const cacheDir = path.resolve(dir, '.cache');
|
|
|
|
|
2021-09-08 17:25:24 +02:00
|
|
|
const pluginsWithFront = Object.keys(plugins)
|
2021-11-30 09:03:52 +01:00
|
|
|
.filter(pluginName => {
|
2021-09-07 17:11:16 +02:00
|
|
|
const pluginInfo = plugins[pluginName];
|
2021-09-23 11:24:15 +02:00
|
|
|
return fs.existsSync(path.resolve(pluginInfo.pathToPlugin, 'strapi-admin.js'));
|
2021-09-07 17:11:16 +02:00
|
|
|
})
|
2021-11-30 09:03:52 +01:00
|
|
|
.map(name => ({ name, ...plugins[name] }));
|
2019-10-15 12:05:08 +02:00
|
|
|
|
2019-04-29 15:48:16 +02:00
|
|
|
// create .cache dir
|
2019-05-15 11:05:34 +02:00
|
|
|
await fs.emptyDir(cacheDir);
|
2019-04-29 15:48:16 +02:00
|
|
|
|
2019-05-16 10:14:13 +02:00
|
|
|
// copy admin core code
|
2019-04-29 15:48:16 +02:00
|
|
|
await copyAdmin(cacheDir);
|
|
|
|
|
2021-07-27 08:25:24 +02:00
|
|
|
// Copy app.js
|
2021-09-22 12:38:12 +02:00
|
|
|
const customAdminConfigFilePath = path.join(dir, 'src', 'admin', 'app.js');
|
2021-06-09 12:11:51 +02:00
|
|
|
|
|
|
|
if (fs.existsSync(customAdminConfigFilePath)) {
|
2021-07-27 08:25:24 +02:00
|
|
|
await fs.copy(customAdminConfigFilePath, path.resolve(cacheDir, 'admin', 'src', 'app.js'));
|
2021-06-09 12:11:51 +02:00
|
|
|
}
|
|
|
|
|
2021-07-27 09:26:37 +02:00
|
|
|
// Copy admin extensions folder
|
2021-09-23 11:39:24 +02:00
|
|
|
const adminExtensionFolder = path.join(dir, 'src', 'admin', 'extensions');
|
2021-07-27 09:26:37 +02:00
|
|
|
|
|
|
|
if (fs.existsSync(adminExtensionFolder)) {
|
|
|
|
await fs.copy(adminExtensionFolder, path.resolve(cacheDir, 'admin', 'src', 'extensions'));
|
|
|
|
}
|
|
|
|
|
2020-06-19 01:07:09 +08:00
|
|
|
// create plugins.js with plugins requires
|
2021-09-08 17:25:24 +02:00
|
|
|
await createPluginsJs(pluginsWithFront, cacheDir);
|
2019-09-23 12:42:17 +02:00
|
|
|
}
|
|
|
|
|
2021-09-07 17:11:16 +02:00
|
|
|
async function watchAdmin({ plugins, dir, host, port, browser, options }) {
|
2019-09-26 11:52:02 +02:00
|
|
|
// Create the cache dir containing the front-end files.
|
2021-09-09 10:56:57 +02:00
|
|
|
const cacheDir = path.join(dir, '.cache');
|
2021-09-07 17:11:16 +02:00
|
|
|
await createCacheDir({ dir, plugins });
|
2019-09-26 11:52:02 +02:00
|
|
|
|
2021-09-09 10:56:57 +02:00
|
|
|
const entry = path.join(cacheDir, 'admin', 'src');
|
2019-09-23 19:27:40 +02:00
|
|
|
const dest = path.join(dir, 'build');
|
|
|
|
const env = 'development';
|
|
|
|
|
2021-07-05 11:24:24 +02:00
|
|
|
// Roots for the @strapi/babel-plugin-switch-ee-ce
|
|
|
|
const roots = {
|
|
|
|
eeRoot: path.resolve(cacheDir, 'ee', 'admin'),
|
|
|
|
ceRoot: path.resolve(cacheDir, 'admin', 'src'),
|
|
|
|
};
|
|
|
|
|
2021-11-30 09:03:52 +01:00
|
|
|
const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin);
|
2021-09-09 10:56:57 +02:00
|
|
|
|
2019-09-23 19:27:40 +02:00
|
|
|
const args = {
|
|
|
|
entry,
|
2021-09-09 10:56:57 +02:00
|
|
|
cacheDir,
|
|
|
|
pluginsPath,
|
2019-09-23 19:27:40 +02:00
|
|
|
dest,
|
|
|
|
env,
|
2019-09-24 14:10:57 +02:00
|
|
|
port,
|
2019-09-23 19:27:40 +02:00
|
|
|
options,
|
2021-07-05 11:24:24 +02:00
|
|
|
roots,
|
2019-09-23 19:27:40 +02:00
|
|
|
};
|
|
|
|
|
2021-08-04 19:24:10 +02:00
|
|
|
const webpackConfig = getCustomWebpackConfig(dir, args);
|
2019-09-23 19:27:40 +02:00
|
|
|
const opts = {
|
2021-11-09 15:55:30 +01:00
|
|
|
client: {
|
|
|
|
logging: 'none',
|
|
|
|
overlay: {
|
|
|
|
errors: true,
|
|
|
|
warnings: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2020-09-29 20:12:38 +08:00
|
|
|
open: browser === 'true' ? true : browser,
|
2021-11-09 15:55:30 +01:00
|
|
|
devMiddleware: {
|
|
|
|
publicPath: options.adminPath,
|
|
|
|
},
|
2019-09-23 19:27:40 +02:00
|
|
|
historyApiFallback: {
|
2021-05-12 11:42:20 +02:00
|
|
|
index: options.adminPath,
|
2021-02-02 12:41:47 +01:00
|
|
|
disableDotRule: true,
|
2019-09-23 19:27:40 +02:00
|
|
|
},
|
2021-11-09 15:55:30 +01:00
|
|
|
|
2021-08-04 19:24:10 +02:00
|
|
|
...webpack(webpackConfig).options.devServer,
|
2019-09-23 19:27:40 +02:00
|
|
|
};
|
|
|
|
|
2021-11-09 15:55:30 +01:00
|
|
|
const server = new WebpackDevServer(opts, webpack(webpackConfig));
|
2019-09-23 19:27:40 +02:00
|
|
|
|
2021-11-30 09:03:52 +01:00
|
|
|
server.start(port, host, function(err) {
|
2019-09-23 19:27:40 +02:00
|
|
|
if (err) {
|
|
|
|
console.log(err);
|
|
|
|
}
|
2019-09-24 07:10:02 +02:00
|
|
|
|
2019-09-24 14:10:57 +02:00
|
|
|
console.log(chalk.green('Starting the development server...'));
|
|
|
|
console.log();
|
2021-11-09 15:55:30 +01:00
|
|
|
console.log(chalk.green(`Admin development at http://${host}:${port}${options.adminPath}`));
|
2019-09-23 19:27:40 +02:00
|
|
|
});
|
2021-07-27 09:26:37 +02:00
|
|
|
|
|
|
|
watchFiles(dir);
|
|
|
|
}
|
|
|
|
|
2021-07-27 10:23:04 +02:00
|
|
|
/**
|
|
|
|
* Listen to files change and copy the changed files in the .cache/admin folder
|
|
|
|
* when using the dev mode
|
|
|
|
* @param {string} dir
|
|
|
|
*/
|
2021-07-27 09:26:37 +02:00
|
|
|
async function watchFiles(dir) {
|
|
|
|
const cacheDir = path.join(dir, '.cache');
|
2021-09-23 16:39:42 +02:00
|
|
|
const appExtensionFile = path.join(dir, 'src', 'admin', 'app.js');
|
|
|
|
const extensionsPath = path.join(dir, 'src', 'admin', 'extensions');
|
2021-07-27 09:26:37 +02:00
|
|
|
|
2021-07-27 10:23:04 +02:00
|
|
|
// Only watch the admin/app.js file and the files that are in the ./admin/extensions/folder
|
2021-07-27 09:26:37 +02:00
|
|
|
const filesToWatch = [appExtensionFile, extensionsPath];
|
|
|
|
|
|
|
|
const watcher = chokidar.watch(filesToWatch, {
|
|
|
|
ignoreInitial: true,
|
|
|
|
ignorePermissionErrors: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
watcher.on('all', async (event, filePath) => {
|
|
|
|
const isAppFile = filePath.includes(appExtensionFile);
|
|
|
|
|
2021-07-27 10:23:04 +02:00
|
|
|
// The app.js file needs to be copied in the .cache/admin/src/app.js and the other ones needs to
|
|
|
|
// be copied in the .cache/admin/src/extensions folder
|
2021-07-27 09:26:37 +02:00
|
|
|
const targetPath = isAppFile
|
|
|
|
? path.join(path.normalize(filePath.split(appExtensionFile)[1]), 'app.js')
|
|
|
|
: path.join('extensions', path.normalize(filePath.split(extensionsPath)[1]));
|
|
|
|
|
|
|
|
const destFolder = path.join(cacheDir, 'admin', 'src');
|
|
|
|
|
|
|
|
if (event === 'unlink' || event === 'unlinkDir') {
|
|
|
|
// Remove the file or folder
|
|
|
|
// We need to copy the original files when deleting an override one
|
|
|
|
try {
|
|
|
|
fs.removeSync(path.join(destFolder, targetPath));
|
|
|
|
} catch (err) {
|
|
|
|
console.log('An error occured while deleting the file', err);
|
|
|
|
}
|
|
|
|
} else {
|
2021-07-27 10:23:04 +02:00
|
|
|
// In any other case just copy the file into the .cache/admin/src folder
|
2021-07-27 09:26:37 +02:00
|
|
|
try {
|
|
|
|
await fs.copy(filePath, path.join(destFolder, targetPath));
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-09-23 19:27:40 +02:00
|
|
|
}
|
|
|
|
|
2021-11-30 09:03:52 +01:00
|
|
|
const hasCustomAdminCode = async dir => {
|
2021-12-20 14:56:06 +01:00
|
|
|
const customAdminPath = path.join(dir, 'src', 'admin');
|
|
|
|
const customAdminConfigFile = path.join(customAdminPath, 'app.js');
|
|
|
|
const customAdminWebpackFile = path.join(customAdminPath, 'webpack.config.js');
|
2021-11-29 16:13:30 +01:00
|
|
|
|
2021-12-20 14:56:06 +01:00
|
|
|
const hasCustomConfigFile = await fs.pathExists(customAdminConfigFile);
|
|
|
|
const hasCustomWebpackFile = await fs.pathExists(customAdminWebpackFile);
|
2021-11-29 16:13:30 +01:00
|
|
|
|
2021-12-20 14:56:06 +01:00
|
|
|
return hasCustomConfigFile || hasCustomWebpackFile;
|
2021-11-29 16:13:30 +01:00
|
|
|
};
|
|
|
|
|
2022-01-11 17:27:53 +01:00
|
|
|
/**
|
|
|
|
* Checks if the project's installed plugins are not the same as a default one.
|
|
|
|
* @param {Object} plugins
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
2021-11-30 09:03:52 +01:00
|
|
|
const hasNonDefaultPlugins = plugins => {
|
2022-01-11 17:27:53 +01:00
|
|
|
// List of plugins that are not the ones installed in a generated app
|
|
|
|
const installedPlugins = Object.keys(plugins).filter(x => !DEFAULT_PLUGINS.includes(x));
|
|
|
|
|
|
|
|
// List of default plugins uninstalled from a generated app
|
|
|
|
const missingPlugins = DEFAULT_PLUGINS.filter(x => !Object.keys(plugins).includes(x));
|
|
|
|
|
|
|
|
const diff = [...installedPlugins, ...missingPlugins];
|
2021-11-29 16:13:30 +01:00
|
|
|
|
|
|
|
return diff.length > 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
async function shouldBuildAdmin({ dir, plugins }) {
|
2021-11-29 16:33:36 +01:00
|
|
|
const appHasCustomAdminCode = await hasCustomAdminCode(dir);
|
2021-11-29 16:13:30 +01:00
|
|
|
const appHasNonDefaultPlugins = hasNonDefaultPlugins(plugins);
|
|
|
|
|
|
|
|
return appHasCustomAdminCode || appHasNonDefaultPlugins;
|
|
|
|
}
|
|
|
|
|
2019-04-29 15:48:16 +02:00
|
|
|
module.exports = {
|
2020-04-14 15:24:14 +02:00
|
|
|
clean,
|
2019-04-29 15:48:16 +02:00
|
|
|
build,
|
2019-09-26 11:52:02 +02:00
|
|
|
watchAdmin,
|
2019-04-29 15:48:16 +02:00
|
|
|
};
|