11const babel = require ( '@babel/core' ) ;
2- const parser = require ( '@babel/parser' ) ;
3- const traverse = require ( '@babel/traverse' ) . default ;
42const createTemplate = require ( '@babel/template' ) . default ;
53const t = require ( '@babel/types' ) ;
64const fs = require ( 'fs' ) ;
@@ -19,121 +17,160 @@ function convert(code, params = {}) {
1917 let {
2018 path,
2119 sourceMap = options . sourceMap ,
20+ } = params ;
21+
22+ var result = babel . transformSync ( code , {
23+ filename : path ,
24+ sourceMap,
25+ plugins : [
26+ babel . createConfigItem ( [ transformToComponent , params ] ) ,
27+ [ '@babel/plugin-transform-modules-commonjs' , { strictMode : false } ] ,
28+ babel . createConfigItem ( transformRequire ) ,
29+ '@babel/plugin-transform-react-jsx' ,
30+ ]
31+ } ) ;
32+
33+ return sourceMap ? result : result . code ;
34+ }
35+
36+ function transformToComponent ( api , params ) {
37+ let {
2238 addOnChange = options . addOnChange ,
23- parserOptions = options . parserOptions ,
2439 template,
2540 templatePath = options . templatePath ,
2641 templateOptions = options . templateOptions
2742 } = params ;
2843
29- var ast = parser . parse ( code , parserOptions ) ;
30-
31- traverse ( ast , {
32- enter : function prepare ( path ) {
33- path . get ( 'body' ) . forEach ( function ( item ) {
34- if (
35- item . isExpressionStatement ( ) &&
36- (
37- item . node . expression . type === 'JSXElement' ||
38- item . node . expression . type === 'JSXFragment'
39- )
40- ) {
41- item . replaceWith (
42- t . callExpression (
43- t . memberExpression ( t . identifier ( '__components' ) , t . identifier ( 'push' ) ) ,
44- [ item . node . expression ]
45- )
46- ) ;
47- }
48- } ) ;
49-
50- path . traverse ( {
51- JSXAttribute : function ( attr ) {
52- var name = attr . node . name . name ;
53- var parent = attr . parent ;
54-
55- if ( addOnChange && ( name === 'value' || name === 'checked' ) && parent . name && parent . name . name === 'input' ) {
56- attr . parent . attributes . push (
57- t . jsxAttribute (
58- t . jsxIdentifier ( 'onChange' ) ,
59- t . jsxExpressionContainer ( t . arrowFunctionExpression ( [ ] , t . booleanLiteral ( false ) ) )
44+ return {
45+ visitor : {
46+ Program : {
47+ enter : function ( path ) {
48+ path . get ( 'body' ) . forEach ( function ( item ) {
49+ if (
50+ item . isExpressionStatement ( ) &&
51+ (
52+ item . get ( 'expression' ) . isJSXElement ( ) ||
53+ item . get ( 'expression' ) . isJSXFragment ( )
6054 )
61- ) ;
62- }
55+ ) {
56+ item . replaceWith (
57+ t . callExpression (
58+ t . memberExpression ( t . identifier ( '__components' ) , t . identifier ( 'push' ) ) ,
59+ [ item . node . expression ]
60+ )
61+ ) ;
62+ }
63+ } ) ;
64+ } ,
65+ exit : function ( path ) {
66+ if ( template === false ) return ;
67+
68+ const { cache} = convert ;
6369
64- if ( attrMap . hasOwnProperty ( name ) ) {
65- attr . node . name . name = attrMap [ name ] ;
70+ if ( templatePath && ! template ) {
71+ template = cache [ templatePath ] || fs . readFileSync ( templatePath ) ;
6672 }
67- } ,
68- CallExpression : function ( func ) {
69- if ( func . node . callee . type === 'Identifier' && func . node . callee . name === 'require' ) {
70- func . node . callee . name = 'requireJSX' ;
71- func . node . arguments . push ( t . identifier ( '__dirname' ) ) ;
73+
74+ if ( template instanceof Buffer ) {
75+ template = template . toString ( ) ;
7276 }
73- }
74- } ) ;
7577
76- path . stop ( ) ;
77- }
78- } ) ;
78+ if ( typeof template === 'string' ) {
79+ template = createTemplate (
80+ template ,
81+ templateOptions
82+ ) ;
7983
80- if ( template === false ) {
81- return toCode ( ast , code , {
82- filename : path ,
83- sourceMap,
84- } ) ;
85- }
84+ if ( templatePath ) {
85+ cache [ templatePath ] = template ;
86+ }
87+ }
8688
87- let { cache} = convert ;
89+ if ( typeof template !== 'function' ) {
90+ throw new Error ( 'Undefined template' ) ;
91+ }
8892
89- if ( templatePath && ! template ) {
90- if ( convert . cache [ templatePath ] ) {
91- template = convert . cache [ templatePath ] ;
92- }
93- else {
94- template = fs . readFileSync ( templatePath ) ;
95- }
96- }
93+ var IMPORTS = [ ] ;
94+ var BODY = [ ] ;
9795
98- if ( template instanceof Buffer ) {
99- template = template . toString ( ) ;
100- }
96+ path . get ( 'body' ) . forEach ( function ( item ) {
97+ var { node} = item ;
10198
102- if ( typeof template === 'string' ) {
103- template = createTemplate (
104- template ,
105- templateOptions
106- ) ;
99+ if ( isExport ( node ) ) {
100+ throw item . buildCodeFrameError ( 'export is not allowed in jsx template' ) ;
101+ }
107102
108- if ( templatePath ) {
109- cache [ templatePath ] = template ;
110- }
111- }
103+ if ( t . isImportDeclaration ( node ) ) {
104+ IMPORTS . push ( node ) ;
105+ }
106+ else {
107+ BODY . push ( node ) ;
108+ }
109+ } ) ;
112110
113- if ( typeof template !== 'function' ) {
114- throw new Error ( 'Undefined template' ) ;
115- }
111+ path . node . body = template ( {
112+ IMPORTS ,
113+ BODY ,
114+ } ) ;
115+ } ,
116+ } ,
117+ JSXAttribute : function ( path ) {
118+ var name = path . node . name . name ;
119+ var parent = path . parent ;
116120
117- ast = template ( {
118- BODY : ast . program . body
119- } ) ;
121+ if (
122+ addOnChange &&
123+ ( name === 'value' || name === 'checked' ) &&
124+ parent . name &&
125+ parent . name . name === 'input' &&
126+ parent . attributes . every ( attr => attr . name . name !== 'onChange' )
127+ ) {
128+ parent . attributes . push (
129+ t . jsxAttribute (
130+ t . jsxIdentifier ( 'onChange' ) ,
131+ t . jsxExpressionContainer ( t . arrowFunctionExpression ( [ ] , t . booleanLiteral ( false ) ) )
132+ )
133+ ) ;
134+ }
120135
121- return toCode ( t . program ( ast ) , code , {
122- filename : path ,
123- sourceMap,
124- } ) ;
136+ if ( attrMap . hasOwnProperty ( name ) ) {
137+ path . node . name . name = attrMap [ name ] ;
138+ }
139+ } ,
140+ }
141+ } ;
125142}
126143
127- function toCode ( ast , code = '' , params = { } ) {
128- const { filename , sourceMap } = params ;
144+ function transformRequire ( ) {
145+ const rule = / ^ ( r e a c t | e x p r e s s - e n g i n e - j s x \/ . + ) $ /
129146
130- var result = babel . transformFromAst ( ast , code , {
131- filename,
132- sourceMap,
133- plugins : [
134- '@babel/plugin-transform-react-jsx'
135- ]
136- } ) ;
147+ return {
148+ visitor : {
149+ CallExpression : function ( { node} ) {
150+ var { callee, arguments : args } = node ;
151+ var first = args [ 0 ] ;
137152
138- return sourceMap ? result : result . code ;
153+ if (
154+ t . isIdentifier ( callee ) &&
155+ callee . name === 'require' &&
156+ args . length === 1 &&
157+ t . isStringLiteral ( first ) &&
158+ ! rule . test ( first . value )
159+ ) {
160+ callee . name = 'requireJSX' ;
161+ args . push ( t . identifier ( '__dirname' ) ) ;
162+ }
163+ }
164+ }
165+ } ;
166+ }
167+
168+ function isExport ( node ) {
169+ return (
170+ t . isExportAllDeclaration ( node ) ||
171+ t . isExportDeclaration ( node ) ||
172+ t . isExportDefaultDeclaration ( node ) ||
173+ t . isExportNamedDeclaration ( node ) ||
174+ t . isExportNamespaceSpecifier ( node )
175+ ) ;
139176}
0 commit comments