2018-10-07 01:15:19 -05:00

137 lines
4.6 KiB
JavaScript

import fs from 'fs';
import path from 'path';
import JSON5 from 'json5';
import findCacheDir from 'find-cache-dir';
import { createDefaultWebpackConfig } from '@storybook/core/server';
// avoid ESLint errors
const logger = console;
function removeReactHmre(presets) {
const index = presets.indexOf('react-hmre');
if (index > -1) {
presets.splice(index, 1);
}
}
// Tries to load a .babelrc and returns the parsed object if successful
function loadBabelConfig(babelConfigPath) {
let config;
if (fs.existsSync(babelConfigPath)) {
const content = fs.readFileSync(babelConfigPath, 'utf-8');
try {
config = JSON5.parse(content);
config.babelrc = false;
} catch (e) {
logger.error(`=> Error parsing .babelrc file: ${e.message}`);
throw e;
}
}
if (!config) return null;
// Remove react-hmre preset.
// It causes issues with react-storybook.
// We don't really need it.
// Earlier, we fix this by runnign storybook in the production mode.
// But, that hide some useful debug messages.
if (config.presets) {
removeReactHmre(config.presets);
}
if (config.env && config.env.development && config.env.development.presets) {
removeReactHmre(config.env.development.presets);
}
return config;
}
// `baseConfig` is a webpack configuration bundled with storybook.
// Storybook will look in the `configDir` directory
// (inside working directory) if a config path is not provided.
export default function(configType, baseConfig, projectDir, configDir) {
const config = baseConfig;
// Search for a .babelrc in project directory, config directory, and storybook
// module directory. If found, use that to extend webpack configurations.
const babelConfigInConfig = loadBabelConfig(path.resolve(configDir, '.babelrc'));
const babelConfigInProject = loadBabelConfig(path.resolve(projectDir, '.babelrc'));
const babelConfigInModule = loadBabelConfig('.babelrc');
let babelConfig = null;
let babelConfigDir = '';
if (babelConfigInConfig) {
logger.info('=> Loading custom .babelrc from config directory.');
babelConfig = babelConfigInConfig;
babelConfigDir = configDir;
} else if (babelConfigInProject) {
logger.info('=> Loading custom .babelrc from project directory.');
babelConfig = babelConfigInProject;
babelConfigDir = projectDir;
} else {
babelConfig = babelConfigInModule;
}
if (babelConfig) {
// If the custom config uses babel's `extends` clause, then replace it with
// an absolute path. `extends` will not work unless we do this.
if (babelConfig.extends) {
babelConfig.extends = babelConfigDir
? path.resolve(babelConfigDir, babelConfig.extends)
: path.resolve(babelConfig.extends);
}
config.module.rules[0].query = babelConfig;
}
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables a cache directory for faster-rebuilds
// `find-cache-dir` will create the cache directory under the node_modules directory.
config.module.rules[0].query.cacheDirectory = findCacheDir({
name: 'react-storybook',
});
// Check whether addons.js file exists inside the storybook.
const storybookCustomAddonsPath = path.resolve(configDir, 'addons.js');
if (fs.existsSync(storybookCustomAddonsPath)) {
logger.info('=> Loading custom addons config.');
config.entry.manager.unshift(storybookCustomAddonsPath);
}
const defaultConfig = createDefaultWebpackConfig(config);
// Check whether user has a custom webpack config file and
// return the (extended) base configuration if it's not available.
const customConfigPath = path.resolve(configDir, 'webpack.config.js');
if (!fs.existsSync(customConfigPath)) {
logger.info('=> Using default webpack setup based on "Create React App".');
return defaultConfig;
}
const customConfig = require(customConfigPath); // eslint-disable-line
if (typeof customConfig === 'function') {
logger.info('=> Loading custom webpack config (full-control mode).');
return customConfig(config, configType, defaultConfig, configDir);
}
logger.info('=> Loading custom webpack config.');
customConfig.module = customConfig.module || {};
return {
...customConfig,
// We'll always load our configurations after the custom config.
// So, we'll always load the stuff we need.
...config,
// We need to use our and custom plugins.
plugins: [...config.plugins, ...(customConfig.plugins || [])],
module: {
...config.module,
// We need to use our and custom rules.
...customConfig.module,
rules: [...config.module.rules, ...(customConfig.module.rules || [])],
},
};
}