1
- import { declare } from " @babel/helper-plugin-utils" ;
1
+ import { declare } from ' @babel/helper-plugin-utils'
2
2
import syntaxDynamicImport from '@babel/plugin-syntax-dynamic-import'
3
3
import chunkNameProperty from './properties/chunkName'
4
4
import isReadyProperty from './properties/isReady'
@@ -20,138 +20,194 @@ const properties = [
20
20
21
21
const LOADABLE_COMMENT = '#__LOADABLE__'
22
22
23
- const loadablePlugin = declare ( ( api , { defaultImportSpecifier = 'loadable' } ) => {
24
- const { types : t } = api
25
-
26
- function collectImportCallPaths ( startPath ) {
27
- const imports = [ ]
28
- startPath . traverse ( {
29
- Import ( importPath ) {
30
- imports . push ( importPath . parentPath )
31
- } ,
32
- } )
33
- return imports
34
- }
23
+ /**
24
+ * The options for the plugin
25
+ * @typedef {Object } LoadabelBabelPluginOptions
26
+ * @property {string } [defaultImportSpecifier] - The default import specifier, defaults to `loadable`
27
+ * @property {boolean } [moduleFederation] - Whether to use module federation
28
+ */
29
+
30
+ /**
31
+ * The options for the plugin
32
+ * @typedef {Object } PropertyFactoryOptions
33
+ * @property {import('@babel/core').NodePath<import('@babel/types').CallExpression> } callPath
34
+ * @property {import('@babel/core').NodePath<import('@babel/types').FunctionExpression | import('@babel/types').ArrowFunctionExpression | import('@babel/types').ObjectMethod> } funcPath
35
+ * @property {import('@babel/core').NodePath<import('@babel/types').ObjectExpression> } path
36
+ */
37
+
38
+ /**
39
+ * The factory for a property
40
+ * @typedef {Function } PropertyFactory
41
+ * @param {PropertyFactoryOptions } options
42
+ * @returns {import('@babel/types').ObjectMethod }
43
+ */
44
+
45
+ const loadablePlugin = declare (
46
+ /**
47
+ *
48
+ * @param {import('@babel/helper-plugin-utils').BabelAPI } api
49
+ * @param {LoadabelBabelPluginOptions } babelOptions
50
+ * @returns {import('@babel/core').PluginObj }
51
+ */
52
+ ( api , babelOptions ) => {
53
+ const { defaultImportSpecifier = 'loadable' } = babelOptions
54
+
55
+ const { types : t } = api
56
+
57
+ function collectImportCallPaths ( startPath ) {
58
+ const imports = [ ]
59
+ startPath . traverse ( {
60
+ Import ( importPath ) {
61
+ imports . push ( importPath . parentPath )
62
+ } ,
63
+ } )
64
+ return imports
65
+ }
35
66
36
- const propertyFactories = properties . map ( init => init ( api ) )
67
+ const propertyFactories = properties . map ( init => init ( api , babelOptions ) )
37
68
38
- function isValidIdentifier ( path , loadableImportSpecifier , lazyImportSpecifier ) {
39
- // `loadable()`
40
- if ( loadableImportSpecifier && path . get ( 'callee' ) . isIdentifier ( { name : loadableImportSpecifier } ) ) {
41
- return true
69
+ function isValidIdentifier (
70
+ path ,
71
+ loadableImportSpecifier ,
72
+ lazyImportSpecifier ,
73
+ ) {
74
+ // `loadable()`
75
+ if (
76
+ loadableImportSpecifier &&
77
+ path . get ( 'callee' ) . isIdentifier ( { name : loadableImportSpecifier } )
78
+ ) {
79
+ return true
80
+ }
81
+
82
+ // `lazy()`
83
+ if (
84
+ lazyImportSpecifier &&
85
+ path . get ( 'callee' ) . isIdentifier ( { name : lazyImportSpecifier } )
86
+ ) {
87
+ return true
88
+ }
89
+
90
+ // `loadable.lib()`
91
+ return (
92
+ loadableImportSpecifier &&
93
+ path . get ( 'callee' ) . isMemberExpression ( ) &&
94
+ path
95
+ . get ( 'callee.object' )
96
+ . isIdentifier ( { name : loadableImportSpecifier } ) &&
97
+ path . get ( 'callee.property' ) . isIdentifier ( { name : 'lib' } )
98
+ )
42
99
}
43
100
44
- // `lazy()`
45
- if ( lazyImportSpecifier && path . get ( 'callee' ) . isIdentifier ( { name : lazyImportSpecifier } ) ) {
101
+ function hasLoadableComment ( path ) {
102
+ const comments = path . get ( 'leadingComments' )
103
+ const comment = comments . find (
104
+ ( { node } ) =>
105
+ node && node . value && String ( node . value ) . includes ( LOADABLE_COMMENT ) ,
106
+ )
107
+ if ( ! comment ) return false
108
+ comment . remove ( )
46
109
return true
47
110
}
48
111
49
- // `loadable.lib()`
50
- return (
51
- loadableImportSpecifier &&
52
- path . get ( 'callee' ) . isMemberExpression ( ) &&
53
- path . get ( 'callee.object' ) . isIdentifier ( { name : loadableImportSpecifier } ) &&
54
- path . get ( 'callee.property' ) . isIdentifier ( { name : 'lib' } )
55
- )
56
- }
57
-
58
- function hasLoadableComment ( path ) {
59
- const comments = path . get ( 'leadingComments' )
60
- const comment = comments . find (
61
- ( { node } ) =>
62
- node && node . value && String ( node . value ) . includes ( LOADABLE_COMMENT ) ,
63
- )
64
- if ( ! comment ) return false
65
- comment . remove ( )
66
- return true
67
- }
68
-
69
- function getFuncPath ( path ) {
70
- const funcPath = path . isCallExpression ( ) ? path . get ( 'arguments.0' ) : path
71
- if (
72
- ! funcPath . isFunctionExpression ( ) &&
73
- ! funcPath . isArrowFunctionExpression ( ) &&
74
- ! funcPath . isObjectMethod ( )
75
- ) {
76
- return null
112
+ function getFuncPath ( path ) {
113
+ const funcPath = path . isCallExpression ( ) ? path . get ( 'arguments.0' ) : path
114
+ if (
115
+ ! funcPath . isFunctionExpression ( ) &&
116
+ ! funcPath . isArrowFunctionExpression ( ) &&
117
+ ! funcPath . isObjectMethod ( )
118
+ ) {
119
+ return null
120
+ }
121
+ return funcPath
77
122
}
78
- return funcPath
79
- }
80
-
81
- function transformImport ( path ) {
82
- const callPaths = collectImportCallPaths ( path )
83
123
84
- // Ignore loadable function that does not have any "import" call
85
- if ( callPaths . length === 0 ) return
124
+ function transformImport ( path ) {
125
+ const callPaths = collectImportCallPaths ( path )
86
126
87
- // Multiple imports call is not supported
88
- if ( callPaths . length > 1 ) {
89
- throw new Error (
90
- 'loadable: multiple import calls inside `loadable()` function are not supported.' ,
91
- )
92
- }
127
+ // Ignore loadable function that does not have any "import" call
128
+ if ( callPaths . length === 0 ) return
93
129
94
- const [ callPath ] = callPaths
130
+ // Multiple imports call is not supported
131
+ if ( callPaths . length > 1 ) {
132
+ throw new Error (
133
+ 'loadable: multiple import calls inside `loadable()` function are not supported.' ,
134
+ )
135
+ }
95
136
96
- const funcPath = getFuncPath ( path )
97
- if ( ! funcPath ) return
137
+ const [ callPath ] = callPaths
98
138
99
- funcPath . node . params = funcPath . node . params || [ ]
139
+ const funcPath = getFuncPath ( path )
140
+ if ( ! funcPath ) return
100
141
101
- const object = t . objectExpression (
102
- propertyFactories . map ( getProperty =>
103
- getProperty ( { path, callPath, funcPath } ) ,
104
- ) ,
105
- )
142
+ funcPath . node . params = funcPath . node . params || [ ]
106
143
107
- if ( funcPath . isObjectMethod ( ) ) {
108
- funcPath . replaceWith (
109
- t . objectProperty ( funcPath . node . key , object , funcPath . node . computed ) ,
144
+ const object = t . objectExpression (
145
+ propertyFactories . map ( getProperty =>
146
+ getProperty ( { path, callPath, funcPath } ) ,
147
+ ) ,
110
148
)
111
- } else {
112
- funcPath . replaceWith ( object )
149
+
150
+ if ( funcPath . isObjectMethod ( ) ) {
151
+ funcPath . replaceWith (
152
+ t . objectProperty ( funcPath . node . key , object , funcPath . node . computed ) ,
153
+ )
154
+ } else {
155
+ funcPath . replaceWith ( object )
156
+ }
113
157
}
114
- }
115
-
116
-
117
- return {
118
- inherits : syntaxDynamicImport ,
119
- visitor : {
120
- Program : {
121
- enter ( programPath ) {
122
- let loadableImportSpecifier = defaultImportSpecifier
123
- let lazyImportSpecifier = false
124
-
125
- programPath . traverse ( {
126
- ImportDefaultSpecifier ( path ) {
127
- if ( ! loadableImportSpecifier ) {
128
- const { parent } = path
129
- const { local } = path . node
130
- loadableImportSpecifier = parent . source . value == '@loadable/component' &&
131
- local && local . name
132
- }
133
- } ,
134
- ImportSpecifier ( path ) {
135
- if ( ! lazyImportSpecifier ) {
136
- const { parent } = path
137
- const { imported, local } = path . node
138
- lazyImportSpecifier = parent . source . value == '@loadable/component' &&
139
- imported && imported . name == 'lazy' && local && local . name
140
- }
141
- } ,
142
- CallExpression ( path ) {
143
- if ( ! isValidIdentifier ( path , loadableImportSpecifier , lazyImportSpecifier ) ) return
144
- transformImport ( path )
145
- } ,
146
- 'ArrowFunctionExpression|FunctionExpression|ObjectMethod' : path => {
147
- if ( ! hasLoadableComment ( path ) ) return
148
- transformImport ( path )
149
- } ,
150
- } )
158
+
159
+ return {
160
+ inherits : syntaxDynamicImport ,
161
+ visitor : {
162
+ Program : {
163
+ enter ( programPath ) {
164
+ let loadableImportSpecifier = defaultImportSpecifier
165
+ let lazyImportSpecifier = false
166
+
167
+ programPath . traverse ( {
168
+ ImportDefaultSpecifier ( path ) {
169
+ if ( ! loadableImportSpecifier ) {
170
+ const { parent } = path
171
+ const { local } = path . node
172
+ loadableImportSpecifier =
173
+ parent . source . value == '@loadable/component' &&
174
+ local &&
175
+ local . name
176
+ }
177
+ } ,
178
+ ImportSpecifier ( path ) {
179
+ if ( ! lazyImportSpecifier ) {
180
+ const { parent } = path
181
+ const { imported, local } = path . node
182
+ lazyImportSpecifier =
183
+ parent . source . value == '@loadable/component' &&
184
+ imported &&
185
+ imported . name == 'lazy' &&
186
+ local &&
187
+ local . name
188
+ }
189
+ } ,
190
+ CallExpression ( path ) {
191
+ if (
192
+ ! isValidIdentifier (
193
+ path ,
194
+ loadableImportSpecifier ,
195
+ lazyImportSpecifier ,
196
+ )
197
+ )
198
+ return
199
+ transformImport ( path )
200
+ } ,
201
+ 'ArrowFunctionExpression|FunctionExpression|ObjectMethod' : path => {
202
+ if ( ! hasLoadableComment ( path ) ) return
203
+ transformImport ( path )
204
+ } ,
205
+ } )
206
+ } ,
151
207
} ,
152
208
} ,
153
- } ,
154
- }
155
- } )
209
+ }
210
+ } ,
211
+ )
156
212
157
213
export default loadablePlugin
0 commit comments