349 lines
9.8 KiB
JavaScript
Raw Normal View History

2019-09-26 11:52:02 +02:00
/* eslint-disable no-useless-escape */
2019-04-29 15:48:16 +02:00
const path = require('path');
const fs = require('fs-extra');
const webpack = require('webpack');
const getWebpackConfig = require('./webpack.config.js');
const WebpackDevServer = require('webpack-dev-server');
const chalk = require('chalk');
2019-09-26 11:52:02 +02:00
const chokidar = require('chokidar');
2019-04-29 15:48:16 +02:00
const getPkgPath = name =>
path.dirname(require.resolve(`${name}/package.json`));
2019-10-15 12:05:08 +02:00
async function createPluginsJs(plugins, localPlugins, dest) {
2019-04-29 15:48:16 +02:00
const content = `
2019-10-15 12:05:08 +02:00
const injectReducer = require('./utils/injectReducer').default;
const injectSaga = require('./utils/injectSaga').default;
const useInjectReducer = require('./utils/injectReducer').useInjectReducer;
const useInjectSaga = require('./utils/injectSaga').useInjectSaga;
const { languages } = require('./i18n');
window.strapi = Object.assign(window.strapi || {}, {
node: MODE || 'host',
backendURL: BACKEND_URL === '/' ? window.location.origin : BACKEND_URL,
languages,
currentLanguage:
window.localStorage.getItem('strapi-admin-language') ||
window.navigator.language ||
window.navigator.userLanguage ||
'en',
injectReducer,
injectSaga,
useInjectReducer,
useInjectSaga,
});
module.exports = {
${plugins
.map(name => {
const shortName = name.replace(/^strapi-plugin-/i, '');
const req = `require('../../plugins/${name}/admin/src').default`;
return `'${shortName}': ${req},`;
})
.join('\n')}
${localPlugins
.map(name => {
const shortName = name.replace(/^strapi-plugin-/i, '');
const req = `require('../../../plugins/${name}/admin/src').default`;
return `'${shortName}': ${req}`;
})
.join(',\n')}
}
2019-04-29 15:48:16 +02:00
`;
return fs.writeFile(
path.resolve(dest, 'admin', 'src', 'plugins.js'),
content
);
2019-04-29 15:48:16 +02:00
}
async function copyPlugin(name, dest) {
const pkgFilePath = getPkgPath(name);
const resolveDepPath = (...args) => path.resolve(pkgFilePath, ...args);
const resolveDest = (...args) => path.resolve(dest, 'plugins', name, ...args);
const copy = (...args) => {
return fs.copy(resolveDepPath(...args), resolveDest(...args));
2019-04-29 15:48:16 +02:00
};
// Copy the entire admin folder
await copy('admin');
2019-04-29 15:48:16 +02:00
// Copy the layout.js if it exists
if (await fs.exists(path.resolve(pkgFilePath, 'config', 'layout.js'))) {
await fs.ensureDir(resolveDest('config'));
await copy('config', 'layout.js');
2019-04-29 15:48:16 +02:00
}
await copy('package.json');
2019-04-29 15:48:16 +02:00
}
async function copyAdmin(dest) {
const adminPath = getPkgPath('strapi-admin');
await fs.ensureDir(path.resolve(dest, 'config'));
await fs.copy(path.resolve(adminPath, 'admin'), path.resolve(dest, 'admin'));
await fs.copy(
path.resolve(adminPath, 'config', 'layout.js'),
path.resolve(dest, 'config', 'layout.js')
);
}
2019-05-15 11:05:34 +02:00
async function copyCustomAdmin(src, dest) {
await fs.copy(src, path.resolve(dest, 'admin'));
}
2019-09-23 12:42:17 +02:00
async function createCacheDir(dir) {
2019-04-29 15:48:16 +02:00
const cacheDir = path.resolve(dir, '.cache');
const pkgJSON = require(path.join(dir, 'package.json'));
const pluginsToCopy = Object.keys(pkgJSON.dependencies).filter(
dep =>
dep.startsWith('strapi-plugin') &&
fs.existsSync(path.resolve(getPkgPath(dep), 'admin', 'src', 'index.js'))
);
2019-10-15 18:33:27 +02:00
let localPluginsToCopy = [];
if (fs.existsSync(path.join(dir, 'plugins'))) {
localPluginsToCopy = fs
.readdirSync(path.join(dir, 'plugins'))
.filter(plugin =>
fs.existsSync(
path.resolve(dir, 'plugins', plugin, 'admin', 'src', 'index.js')
)
);
}
2019-10-15 12:05:08 +02:00
// TODO: add logic to avoid copying files if not necessary
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
// copy admin core code
2019-04-29 15:48:16 +02:00
await copyAdmin(cacheDir);
// copy plugins code
await Promise.all(pluginsToCopy.map(name => copyPlugin(name, cacheDir)));
// create plugins.js with plugins requires
2019-10-15 12:05:08 +02:00
await createPluginsJs(pluginsToCopy, localPluginsToCopy, cacheDir);
// override admin code with user customizations
2019-05-15 11:05:34 +02:00
if (fs.pathExistsSync(path.join(dir, 'admin'))) {
await copyCustomAdmin(path.join(dir, 'admin'), cacheDir);
}
2019-09-23 11:50:36 +02:00
// override plugins' admin code with user customizations
const pluginsToOverride = pluginsToCopy.reduce((acc, current) => {
const pluginName = current.replace(/^strapi-plugin-/i, '');
if (fs.pathExistsSync(path.join(dir, 'extensions', pluginName, 'admin'))) {
acc.push(pluginName);
}
return acc;
}, []);
await Promise.all(
pluginsToOverride.map(plugin =>
copyCustomAdmin(
path.join(dir, 'extensions', plugin, 'admin'),
path.join(cacheDir, 'plugins', `strapi-plugin-${plugin}`)
)
)
);
2019-09-23 12:42:17 +02:00
}
async function build({ dir, env, options, optimize }) {
2019-09-26 11:52:02 +02:00
// Create the cache dir containing the front-end files.
await createCacheDir(dir);
2019-09-23 12:42:17 +02:00
const cacheDir = path.resolve(dir, '.cache');
2019-04-29 15:48:16 +02:00
const entry = path.resolve(cacheDir, 'admin', 'src', 'app.js');
const dest = path.resolve(dir, 'build');
const config = getWebpackConfig({ entry, dest, env, options, optimize });
2019-04-29 15:48:16 +02:00
const compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
if (err) {
if (!err.message) {
return reject(err);
}
messages = {
errors: [err.message],
warnings: [],
};
} else {
messages = stats.toJson({ all: false, warnings: true, errors: true });
}
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join('\n\n')));
}
return resolve({
stats,
warnings: messages.warnings,
});
});
2019-04-30 14:47:49 +02:00
});
2019-04-29 15:48:16 +02:00
}
2019-12-12 14:32:37 +01:00
async function watchAdmin({ dir, host, port, options }) {
2019-09-26 11:52:02 +02:00
// Create the cache dir containing the front-end files.
await createCacheDir(dir);
const entry = path.join(dir, '.cache', 'admin', 'src', 'app.js');
const dest = path.join(dir, 'build');
const env = 'development';
const args = {
entry,
dest,
env,
port,
options,
};
const opts = {
clientLogLevel: 'silent',
hot: true,
quiet: true,
2019-10-10 06:34:31 +02:00
open: true,
2019-09-26 11:52:02 +02:00
publicPath: options.publicPath,
historyApiFallback: {
2019-09-26 11:52:02 +02:00
index: options.publicPath,
},
};
const server = new WebpackDevServer(webpack(getWebpackConfig(args)), opts);
2019-12-12 14:32:37 +01:00
server.listen(port, host, function(err) {
if (err) {
console.log(err);
}
2019-09-24 07:10:02 +02:00
console.log(chalk.green('Starting the development server...'));
console.log();
console.log(
chalk.green(
2019-12-12 14:32:37 +01:00
`Admin development at http://${host}:${port}${opts.publicPath}`
)
);
});
2019-09-26 11:52:02 +02:00
watchFiles(dir, options.watchIgnoreFiles);
2019-09-26 11:52:02 +02:00
}
async function watchFiles(dir, ignoreFiles = []) {
2019-09-26 11:52:02 +02:00
const cacheDir = path.join(dir, '.cache');
const pkgJSON = require(path.join(dir, 'package.json'));
const admin = path.join(dir, 'admin');
const extensionsPath = path.join(dir, 'extensions');
2019-09-26 11:52:02 +02:00
const appPlugins = Object.keys(pkgJSON.dependencies).filter(
dep =>
dep.startsWith('strapi-plugin') &&
fs.existsSync(path.resolve(getPkgPath(dep), 'admin', 'src', 'index.js'))
);
const pluginsToWatch = appPlugins.map(plugin =>
path.join(extensionsPath, plugin.replace(/^strapi-plugin-/i, ''), 'admin')
2019-09-26 11:52:02 +02:00
);
const filesToWatch = [admin, ...pluginsToWatch];
const watcher = chokidar.watch(filesToWatch, {
ignoreInitial: true,
ignorePermissionErrors: true,
ignored: [...ignoreFiles],
2019-09-26 11:52:02 +02:00
});
watcher.on('all', async (event, filePath) => {
const isExtension = filePath.includes(extensionsPath);
const pluginName = isExtension
? filePath.replace(extensionsPath, '').split(path.sep)[1]
: '';
2019-09-26 11:52:02 +02:00
const packageName = isExtension
? `strapi-plugin-${pluginName}`
: 'strapi-admin';
2019-09-26 11:52:02 +02:00
const targetPath = isExtension
? path.normalize(
filePath.split(extensionsPath)[1].replace(pluginName, '')
)
: path.normalize(filePath.split(admin)[1]);
2019-09-26 11:52:02 +02:00
const destFolder = isExtension
? path.join(cacheDir, 'plugins', packageName)
: path.join(cacheDir, 'admin');
if (event === 'unlink' || event === 'unlinkDir') {
const originalFilePathInNodeModules = path.join(
getPkgPath(packageName),
isExtension ? '' : 'admin',
targetPath
);
// 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);
}
// Check if the file or folder exists in node_modules
// If so copy the old one
if (fs.pathExistsSync(path.resolve(originalFilePathInNodeModules))) {
try {
await fs.copy(
path.resolve(originalFilePathInNodeModules),
path.join(destFolder, targetPath)
);
// The plugins.js file needs to be recreated
// when we delete either the admin folder
// the admin/src folder
// or the plugins.js file
// since the path are different when developing inside the monorepository or inside an app
const shouldCopyPluginsJSFile =
filePath.split('/admin/src').filter(p => !!p).length === 1;
if (
(event === 'unlinkDir' &&
!isExtension &&
shouldCopyPluginsJSFile) ||
(!isExtension && filePath.includes('plugins.js'))
) {
await createPluginsJs(appPlugins, path.join(cacheDir));
}
} catch (err) {
// Do nothing
}
}
} else {
// In any other case just copy the file into the .cache folder
try {
await fs.copy(filePath, path.join(destFolder, targetPath));
} catch (err) {
console.log(err);
}
}
});
}
2019-04-29 15:48:16 +02:00
module.exports = {
build,
2019-09-26 11:52:02 +02:00
watchAdmin,
2019-04-29 15:48:16 +02:00
};