-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
171 lines (136 loc) · 5.58 KB
/
index.js
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
var types = require('@babel/types');
var isValidPath = require('is-valid-path');
var pathLib = require('path');
var { addSideEffect } = require('@babel/helper-module-imports')
var noop = function() {}
function isObject(val) {
return Object.prototype.toString.call(val).slice(8, -1) === 'Object'
}
function findOptionFromSource(source, state) {
var opts = state.opts;
if (opts[source]) return source;
var opt = Object.keys(opts).find(function (key) {
return !isValidPath(key) && new RegExp(key).test(source);
});
if (opt) return opt;
var isRelativePath = source.match(/^\.{0,2}\//);
// This block handles relative paths, such as ./components, ../../components, etc.
if (isRelativePath) {
var dirname = source[0] === '/' ? '' : state.file.opts.filename ? pathLib.dirname(state.file.opts.filename) : '.'
var _source = pathLib.resolve(pathLib.join(dirname, source));
if (opts[_source]) {
return _source;
}
}
}
function getMatchesFromSource(opt, source) {
var regex = new RegExp(opt, 'g');
var matches = [];
var m;
while ((m = regex.exec(source)) !== null) {
if (m.index === regex.lastIndex) regex.lastIndex++;
m.forEach(function (match) {
matches.push(match);
});
}
return matches;
}
function barf(msg) {
throw new Error('babel-plugin-transform-imports: ' + msg);
}
function transform(transformOption, importName, matches) {
if (typeof transformOption === 'function') {
return transformOption(importName, matches);
}
return transformOption.replace(/\$\{\s?([\w\d]*)\s?\}/ig, function (str, g1) {
if (g1 === 'member') return importName;
return matches[g1];
});
}
function addStyleEffect(style){
return typeof style === 'function' ? style : noop
}
module.exports = function () {
return {
visitor: {
ImportDeclaration: function (path, state) {
// https://github.com/babel/babel/tree/master/packages/babel-types#timportdeclarationspecifiers-source
// path.node has properties 'source' and 'specifiers' attached.
// path.node.source is the library/module name, aka 'react-bootstrap'.
// path.node.specifiers is an array of ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier
var source = path.node.source.value;
var opt = findOptionFromSource(source, state);
var isRegexp = opt && !isValidPath(opt);
var opts = state.opts[opt];
var hasOpts = !!opts;
if (hasOpts) {
if (!opts.transform) {
barf('transform option is required for module ' + source);
}
var transforms = [];
var styles = []
var fullImports = path.node.specifiers.filter(function (specifier) { return specifier.type !== 'ImportSpecifier' });
var memberImports = path.node.specifiers.filter(function (specifier) { return specifier.type === 'ImportSpecifier' });
if (fullImports.length > 0) {
// Examples of "full" imports:
// import * as name from 'module'; (ImportNamespaceSpecifier)
// import name from 'module'; (ImportDefaultSpecifier)
if (opts.preventFullImport) {
barf('import of entire module ' + source + ' not allowed due to preventFullImport setting');
}
if (memberImports.length > 0) {
// Swap out the import with one that doesn't include member imports. Member imports should each get their own import line
// transform this:
// import Bootstrap, { Grid } from 'react-bootstrap';
// into this:
// import Bootstrap from 'react-bootstrap';
transforms.push(types.importDeclaration(fullImports, types.stringLiteral(source)));
}
}
var matches = isRegexp ? getMatchesFromSource(opt, source) : [];
var styled = addStyleEffect(opts.style)
var styleMap = {}
memberImports.forEach(function (memberImport) {
// Examples of member imports:
// import { member } from 'module'; (ImportSpecifier)
// import { member as alias } from 'module' (ImportSpecifier)
// transform this:
// import { Grid as gird } from 'react-bootstrap';
// into this:
// import gird from 'react-bootstrap/lib/Grid';
// or this, if skipDefaultConversion = true:
// import { Grid as gird } from 'react-bootstrap/lib/Grid';
var importName = memberImport.imported.name;
var replace = transform(opts.transform, importName, matches);
var skipDefault = false
// import style
var style = styled(importName, replace)
if(style){
if(!styleMap[replace]){
styleMap[replace] = importName
styles.unshift(style)
}
}
if(isObject(replace)){
skipDefault = !replace.default
replace = replace.replace
}
var newImportSpecifier = (skipDefault || opts.skipDefaultConversion)
? memberImport
: types.importDefaultSpecifier(types.identifier(memberImport.local.name));
transforms.push(types.importDeclaration(
[newImportSpecifier],
types.stringLiteral(replace)
));
});
if (transforms.length > 0) {
path.replaceWithMultiple(transforms);
}
if(styles.length > 0 ){
styles.forEach(style => addSideEffect(path, style))
}
}
}
}
}
}