Build admin only when necessary and serve it from the admin package

This commit is contained in:
Alexandre Bodin 2021-11-29 16:13:30 +01:00
parent 3e9e3f13cb
commit b82f0a3d01
6 changed files with 130 additions and 57 deletions

View File

@ -9,7 +9,7 @@ const chalk = require('chalk');
const chokidar = require('chokidar');
const getWebpackConfig = require('./webpack.config');
const getPkgPath = name => path.dirname(require.resolve(`${name}/package.json`));
const getPkgPath = (name) => path.dirname(require.resolve(`${name}/package.json`));
function getCustomWebpackConfig(dir, config) {
const adminConfigPath = path.join(dir, 'src', 'admin', 'webpack.config.js');
@ -35,6 +35,10 @@ function getCustomWebpackConfig(dir, config) {
}
async function build({ plugins, dir, env, options, optimize }) {
if (!(await shouldBuildAdmin({ dir, plugins }))) {
return;
}
// Create the cache dir containing the front-end files.
await createCacheDir({ dir, plugins });
@ -48,7 +52,7 @@ async function build({ plugins, dir, env, options, optimize }) {
ceRoot: path.resolve(cacheDir, 'admin', 'src'),
};
const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin);
const pluginsPath = Object.keys(plugins).map((pluginName) => plugins[pluginName].pathToPlugin);
const config = getCustomWebpackConfig(dir, {
entry,
@ -161,11 +165,11 @@ async function createCacheDir({ dir, plugins }) {
const cacheDir = path.resolve(dir, '.cache');
const pluginsWithFront = Object.keys(plugins)
.filter(pluginName => {
.filter((pluginName) => {
const pluginInfo = plugins[pluginName];
return fs.existsSync(path.resolve(pluginInfo.pathToPlugin, 'strapi-admin.js'));
})
.map(name => ({ name, ...plugins[name] }));
.map((name) => ({ name, ...plugins[name] }));
// create .cache dir
await fs.emptyDir(cacheDir);
@ -206,7 +210,7 @@ async function watchAdmin({ plugins, dir, host, port, browser, options }) {
ceRoot: path.resolve(cacheDir, 'admin', 'src'),
};
const pluginsPath = Object.keys(plugins).map(pluginName => plugins[pluginName].pathToPlugin);
const pluginsPath = Object.keys(plugins).map((pluginName) => plugins[pluginName].pathToPlugin);
const args = {
entry,
@ -243,7 +247,7 @@ async function watchAdmin({ plugins, dir, host, port, browser, options }) {
const server = new WebpackDevServer(opts, webpack(webpackConfig));
server.start(port, host, function(err) {
server.start(port, host, function (err) {
if (err) {
console.log(err);
}
@ -304,6 +308,40 @@ async function watchFiles(dir) {
});
}
const hasCustomAdminCode = (dir) => {
const customAdminPath = path.join(dir, 'src/admin');
if (!fs.pathExistsSync(customAdminPath)) {
return false;
}
const files = fs.readdirSync(customAdminPath);
return files.length > 0;
};
const DEFAULT_PLUGINS = [
'content-type-builder',
'content-manager',
'upload',
'email',
'i18n',
'users-permissions',
];
const hasNonDefaultPlugins = (plugins) => {
const diff = _.difference(Object.keys(plugins), DEFAULT_PLUGINS);
return diff.length > 0;
};
async function shouldBuildAdmin({ dir, plugins }) {
const appHasCustomAdminCode = hasCustomAdminCode(dir);
const appHasNonDefaultPlugins = hasNonDefaultPlugins(plugins);
return appHasCustomAdminCode || appHasNonDefaultPlugins;
}
module.exports = {
clean,
build,

View File

@ -69,6 +69,7 @@
"jsonwebtoken": "8.5.1",
"koa-compose": "4.1.0",
"koa-passport": "4.1.4",
"koa-static": "5.0.0",
"lodash": "4.17.21",
"markdown-it": "^12.0.6",
"markdown-it-abbr": "^1.0.4",

View File

@ -1,5 +1,6 @@
'use strict';
const serveAdmin = require('./routes/serve-admin');
const adminAuthStrategy = require('./strategies/admin');
const apiTokenAuthStrategy = require('./strategies/api-token');
@ -9,4 +10,8 @@ module.exports = ({ strapi }) => {
strapi.server.api('admin').use(passportMiddleware);
strapi.container.get('auth').register('admin', adminAuthStrategy);
strapi.container.get('auth').register('content-api', apiTokenAuthStrategy);
if (strapi.config.serveAdminPanel) {
serveAdmin({ strapi });
}
};

View File

@ -0,0 +1,59 @@
'use strict';
const path = require('path');
const fse = require('fs-extra');
const koaStatic = require('koa-static');
const serveAdmin = ({ strapi }) => {
let buildDir = path.resolve(strapi.dirs.root, 'build');
if (!fse.pathExistsSync(buildDir)) {
buildDir = path.resolve(__dirname, '../../build');
}
const serveAdmin = async (ctx, next) => {
await next();
if (ctx.method !== 'HEAD' && ctx.method !== 'GET') {
return;
}
if (ctx.body != null || ctx.status !== 404) {
return;
}
ctx.type = 'html';
ctx.body = fse.createReadStream(path.join(buildDir + '/index.html'));
};
strapi.server.routes([
{
method: 'GET',
path: `${strapi.config.admin.path}/:path*`,
handler: [
serveAdmin,
serveStatic(buildDir, { maxage: 60000, defer: false, index: 'index.html' }),
],
config: { auth: false },
},
]);
};
// serveStatic is not supposed to be used to serve a folder that have sub-folders
const serveStatic = (filesDir, koaStaticOptions = {}) => {
const serve = koaStatic(filesDir, koaStaticOptions);
return async (ctx, next) => {
const prev = ctx.path;
const newPath = path.basename(ctx.path);
ctx.path = newPath;
await serve(ctx, async () => {
ctx.path = prev;
await next();
ctx.path = newPath;
});
ctx.path = prev;
};
};
module.exports = serveAdmin;

View File

@ -15,29 +15,28 @@ const strapi = require('../index');
* `$ strapi develop`
*
*/
module.exports = async function({ build, watchAdmin, polling, browser }) {
module.exports = async function ({ build, watchAdmin, polling, browser }) {
const dir = process.cwd();
const config = loadConfiguration(dir);
const logger = createLogger(config.logger, {});
const adminWatchIgnoreFiles = getOr([], 'admin.watchIgnoreFiles')(config);
const serveAdminPanel = getOr(true, 'admin.serveAdminPanel')(config);
const buildExists = fs.existsSync(path.join(dir, 'build'));
// Don't run the build process if the admin is in watch mode
if (build && !watchAdmin && serveAdminPanel && !buildExists) {
try {
execa.sync('npm run -s build -- --no-optimization', {
stdio: 'inherit',
shell: true,
});
} catch (err) {
process.exit(1);
}
}
try {
if (cluster.isMaster) {
const serveAdminPanel = getOr(true, 'admin.serveAdminPanel')(config);
const buildExists = fs.existsSync(path.join(dir, 'build'));
// Don't run the build process if the admin is in watch mode
if (build && !watchAdmin && serveAdminPanel && !buildExists) {
try {
execa.sync('npm run -s build -- --no-optimization', {
stdio: 'inherit',
shell: true,
});
} catch (err) {
process.exit(1);
}
}
if (watchAdmin) {
try {
execa('npm', ['run', '-s', 'strapi', 'watch-admin', '--', '--browser', browser], {
@ -74,6 +73,7 @@ module.exports = async function({ build, watchAdmin, polling, browser }) {
serveAdminPanel: watchAdmin ? false : true,
});
const adminWatchIgnoreFiles = getOr([], 'admin.watchIgnoreFiles')(config);
watchFileChanges({
dir,
strapiInstance,
@ -81,7 +81,7 @@ module.exports = async function({ build, watchAdmin, polling, browser }) {
polling,
});
process.on('message', async message => {
process.on('message', async (message) => {
switch (message) {
case 'kill':
await strapiInstance.destroy();
@ -140,15 +140,15 @@ function watchFileChanges({ dir, strapiInstance, watchIgnoreFiles, polling }) {
});
watcher
.on('add', path => {
.on('add', (path) => {
strapiInstance.log.info(`File created: ${path}`);
restart();
})
.on('change', path => {
.on('change', (path) => {
strapiInstance.log.info(`File changed: ${path}`);
restart();
})
.on('unlink', path => {
.on('unlink', (path) => {
strapiInstance.log.info(`File deleted: ${path}`);
restart();
});

View File

@ -89,35 +89,5 @@ module.exports = (config, { strapi }) => {
]);
}
if (!strapi.config.serveAdminPanel) return async (ctx, next) => next();
const buildDir = path.resolve(strapi.dirs.root, 'build');
const serveAdmin = async (ctx, next) => {
await next();
if (ctx.method !== 'HEAD' && ctx.method !== 'GET') {
return;
}
if (ctx.body != null || ctx.status !== 404) {
return;
}
ctx.type = 'html';
ctx.body = fs.createReadStream(path.join(buildDir + '/index.html'));
};
strapi.server.routes([
{
method: 'GET',
path: `${strapi.config.admin.path}/:path*`,
handler: [
serveAdmin,
serveStatic(buildDir, { maxage: maxAge, defer: false, index: 'index.html' }),
],
config: { auth: false },
},
]);
return null;
};