/
buildOptions.ts
102 lines (93 loc) · 3.63 KB
/
buildOptions.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
* 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 === require.resolve('../../babel') ||
// 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!,
],
};
}