summaryrefslogtreecommitdiff
path: root/@linaria/packages/babel/src/evaluators/buildOptions.ts
diff options
context:
space:
mode:
Diffstat (limited to '@linaria/packages/babel/src/evaluators/buildOptions.ts')
-rw-r--r--@linaria/packages/babel/src/evaluators/buildOptions.ts104
1 files changed, 104 insertions, 0 deletions
diff --git a/@linaria/packages/babel/src/evaluators/buildOptions.ts b/@linaria/packages/babel/src/evaluators/buildOptions.ts
new file mode 100644
index 0000000..a938d4b
--- /dev/null
+++ b/@linaria/packages/babel/src/evaluators/buildOptions.ts
@@ -0,0 +1,104 @@
+/**
+ * This file handles preparing babel config for Linaria preevaluation.
+ */
+
+import type { PluginItem, TransformOptions } from '@babel/core';
+import type { StrictOptions } from '../types';
+
+type DefaultOptions = Partial<TransformOptions> & {
+ plugins: PluginItem[];
+ presets: PluginItem[];
+ caller: { evaluate: boolean };
+};
+
+export default function buildOptions(
+ filename: string,
+ options?: StrictOptions
+): TransformOptions {
+ const plugins: Array<string | object> = [
+ // Include these plugins to avoid extra config when using { module: false } for webpack
+ '@babel/plugin-transform-modules-commonjs',
+ '@babel/plugin-proposal-export-namespace-from',
+ ];
+
+ const defaults: DefaultOptions = {
+ caller: { name: 'linaria', evaluate: true },
+ filename: filename,
+ presets: [
+ [
+ require.resolve('../index'),
+ {
+ ...(options || {}),
+ },
+ ],
+ ],
+ plugins: [
+ ...plugins.map((name) => require.resolve(name as string)),
+ // We don't support dynamic imports when evaluating, but don't wanna syntax error
+ // This will replace dynamic imports with an object that does nothing
+ require.resolve('../dynamic-import-noop'),
+ ],
+ };
+
+ const babelOptions =
+ // Shallow copy the babel options because we mutate it later
+ options?.babelOptions ? { ...options.babelOptions } : {};
+
+ // If we programmatically pass babel options while there is a .babelrc, babel might throw
+ // We need to filter out duplicate presets and plugins so that this doesn't happen
+ // This workaround isn't full proof, but it's still better than nothing
+ const keys: Array<keyof TransformOptions & ('presets' | 'plugins')> = [
+ 'presets',
+ 'plugins',
+ ];
+ keys.forEach((field) => {
+ babelOptions[field] = babelOptions[field]
+ ? babelOptions[field]!.filter((item: PluginItem) => {
+ // If item is an array it's a preset/plugin with options ([preset, options])
+ // Get the first item to get the preset.plugin name
+ // Otherwise it's a plugin name (can be a function too)
+ const name = Array.isArray(item) ? item[0] : item;
+
+ if (
+ // In our case, a preset might also be referring to linaria/babel
+ // We require the file from internal path which is not the same one that we export
+ // This case won't get caught and the preset won't filtered, even if they are same
+ // So we add an extra check for top level linaria/babel
+ name === 'linaria/babel' ||
+ name === '@linaria' ||
+ name === '@linaria/babel-preset' ||
+ name === require.resolve('../index') ||
+ // Also add a check for the plugin names we include for bundler support
+ plugins.includes(name)
+ ) {
+ return false;
+ }
+
+ // Loop through the default presets/plugins to see if it already exists
+ return !defaults[field].some((it) =>
+ // The default presets/plugins can also have nested arrays,
+ Array.isArray(it) ? it[0] === name : it === name
+ );
+ })
+ : [];
+ });
+
+ return {
+ // Passed options shouldn't be able to override the options we pass
+ // Linaria's plugins rely on these (such as filename to generate consistent hash)
+ ...babelOptions,
+ ...defaults,
+ presets: [
+ // Preset order is last to first, so add the extra presets to start
+ // This makes sure that our preset is always run first
+ ...babelOptions.presets!,
+ ...defaults.presets,
+ ],
+ plugins: [
+ ...defaults.plugins,
+ // Plugin order is first to last, so add the extra presets to end
+ // This makes sure that the plugins we specify always run first
+ ...babelOptions.plugins!,
+ ],
+ };
+}