pob-babel
Version:
Build and watch with babel and typescript
225 lines (205 loc) • 6.46 kB
JavaScript
/* eslint-disable max-lines */
/* eslint-disable complexity */
import { readFileSync, existsSync } from 'fs';
import path from 'path';
import babelPluginTransformRuntime from '@babel/plugin-transform-runtime';
import { babel } from '@rollup/plugin-babel';
import json from '@rollup/plugin-json';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import babelPresetEnv from 'babel-preset-pob-env';
import configExternalDependencies from 'rollup-config-external-dependencies';
import semver from 'semver';
import ignoreImport from './rollup-plugin-ignore-browser-only-imports.js';
const browserOnlyExtensions = ['.scss', '.css'];
const nodeFormatToExt = (format, pkgType) => {
if (format === 'cjs' && pkgType === 'module') return '.cjs';
if (format === 'cjs') return '.cjs.js';
if (format === 'es') return '.mjs';
return `.${format}.js`;
};
export default function createRollupConfig({
cwd = process.cwd(),
outDirectory = 'dist',
pkg = JSON.parse(readFileSync(path.join(cwd, 'package.json'))),
plugins = [],
devPlugins,
prodPlugins,
pobConfig = pkg.pob ||
JSON.parse(readFileSync(path.join(cwd, '.yo-rc.json'))).pob['pob-config'],
} = {}) {
if (devPlugins) {
throw new Error(
'"devPlugins" option is no longer supported, use "plugins" instead',
);
}
if (prodPlugins) {
throw new Error(
'"prodPlugins" option is no longer supported, use "plugins" instead',
);
}
const isIndexBrowserEntry =
pobConfig.entries[0] === 'index' && pobConfig.entries[1] === 'browser';
const entries = isIndexBrowserEntry
? ['index', ...pobConfig.entries.slice(2)]
: pobConfig.entries;
const jsx =
pobConfig.jsx ||
(pobConfig.jsx !== false &&
Boolean(
(pkg.dependencies && pkg.dependencies.react) ||
(pkg.peerDependencies && pkg.peerDependencies.react),
));
const babelRuntimeVersion =
pkg.dependencies && pkg.dependencies['@babel/runtime'];
const minBabelRuntimeVersion = babelRuntimeVersion
? semver.minVersion(babelRuntimeVersion).raw
: undefined;
const nodeVersion = (version) => {
switch (String(version)) {
case '12':
case '14':
return '14.17';
default:
return version;
}
};
const externalByPackageJson = configExternalDependencies(pkg);
const resolveEntry = (entry, target) => {
const entryName =
isIndexBrowserEntry && entry === 'index' && target === 'browser'
? 'browser'
: entry;
let entryPath;
['ts', 'tsx', 'js', 'jsx'].some((extension) => {
const potentialEntryPath = path.resolve(
cwd,
'src',
`${entryName}.${extension}`,
);
if (existsSync(potentialEntryPath)) {
entryPath = potentialEntryPath;
return true;
}
return false;
});
if (!entryPath) {
throw new Error(
`Could not find entry "src/${entryName}" in path "${cwd}"`,
);
}
return entryPath;
};
const createConfigForEnv = (entry, entryPath, env) => {
const typescript = entryPath.endsWith('.ts') || entryPath.endsWith('.tsx');
const extensions = (
typescript
? ['.ts', jsx && '.tsx', '.json']
: ['.js', jsx && '.jsx', '.json']
).filter(Boolean);
const preferConst = !(env.target === 'browser' && env.version !== 'modern');
return {
input: entryPath,
output: (env.formats || ['es']).map((format) => ({
file: path.relative(
process.cwd(),
path.join(
cwd,
`${outDirectory}/${entry}-${env.target}${env.version || ''}${
env.target === 'node'
? nodeFormatToExt(format, pkg.type)
: `.${format}.js`
}`,
),
),
format,
sourcemap: true,
exports: 'named',
generatedCode: {
constBindings: preferConst,
},
externalLiveBindings: false,
freeze: false,
})),
external:
env.target === 'browser'
? (path) => {
if (browserOnlyExtensions.some((ext) => path.endsWith(ext))) {
return true;
}
return externalByPackageJson(path);
}
: externalByPackageJson,
plugins: [
env.target !== 'browser' &&
ignoreImport({
extensions: browserOnlyExtensions,
}),
babel({
extensions,
envName: 'rollup',
babelrc: false,
configFile: false,
presets: [
jsx && [
'@babel/preset-react',
{
runtime: 'automatic',
// always disable development: babel-plugin-transform-react-jsx-source compiles with filename full path, resulting in non reproducible builds
development: false,
useBuiltIns: true,
useSpread: true,
},
],
[
babelPresetEnv,
{
loose: true,
optimizations: true,
modules: false,
typescript,
target: env.target,
version:
env.target === 'node'
? nodeVersion(env.version)
: env.version,
},
],
].filter(Boolean),
plugins: [
// use @babel/runtime
[
babelPluginTransformRuntime,
{
corejs: false,
useESModules: 'auto',
useHelpers: true,
version: minBabelRuntimeVersion,
},
],
],
skipPreflightCheck: true,
babelHelpers: 'runtime',
exclude: 'node_modules/**',
}),
json({
preferConst,
compact: true,
namedExports: true, // allow tree shaking
}),
nodeResolve({
extensions,
customResolveOptions: {
moduleDirectories: ['src'], // don't resolve node_modules, but allow src (see baseUrl in tsconfig)
},
}),
...plugins,
].filter(Boolean),
};
};
return pobConfig.babelEnvs.flatMap((env) => {
return entries.map((entry) => {
const entryPath = resolveEntry(entry, env.target);
return createConfigForEnv(entry, entryPath, env);
});
});
}