1
+ use std:: collections:: HashMap ;
2
+ use std:: fs:: File ;
3
+ use std:: io:: Read ;
1
4
use std:: sync:: Arc ;
2
5
3
6
use anyhow;
7
+ use wasmparser:: { Import , Parser , Payload } ;
4
8
9
+ use crate :: ast:: file:: { Content , JsContent } ;
5
10
use crate :: compiler:: Context ;
6
- use crate :: plugin:: Plugin ;
11
+ use crate :: plugin:: { Plugin , PluginLoadParam } ;
7
12
8
13
pub struct WasmRuntimePlugin { }
9
14
15
+ const WASM_EXTENSIONS : [ & str ; 1 ] = [ "wasm" ] ;
16
+
10
17
impl Plugin for WasmRuntimePlugin {
11
18
fn name ( & self ) -> & str {
12
19
"wasm_runtime"
@@ -27,4 +34,139 @@ impl Plugin for WasmRuntimePlugin {
27
34
Ok ( vec ! [ ] )
28
35
}
29
36
}
37
+
38
+ fn load (
39
+ & self ,
40
+ param : & PluginLoadParam ,
41
+ _context : & Arc < Context > ,
42
+ ) -> anyhow:: Result < Option < Content > > {
43
+ let file = param. file ;
44
+
45
+ if WASM_EXTENSIONS . contains ( & file. extname . as_str ( ) ) {
46
+ let final_file_name = format ! (
47
+ "{}.{}.{}" ,
48
+ file. get_file_stem( ) ,
49
+ file. get_content_hash( ) ?,
50
+ file. extname
51
+ ) ;
52
+ _context. emit_assets (
53
+ file. pathname . to_string_lossy ( ) . to_string ( ) ,
54
+ final_file_name. clone ( ) ,
55
+ ) ;
56
+
57
+ let mut buffer = Vec :: new ( ) ;
58
+ File :: open ( & file. path ) ?. read_to_end ( & mut buffer) ?;
59
+ // Parse wasm file to get imports
60
+ let mut wasm_import_object_map: HashMap < & str , Vec < String > > = HashMap :: new ( ) ;
61
+ Parser :: new ( 0 ) . parse_all ( & buffer) . for_each ( |payload| {
62
+ if let Ok ( Payload :: ImportSection ( imports) ) = payload {
63
+ imports. into_iter_with_offsets ( ) . for_each ( |import| {
64
+ if let Ok ( (
65
+ _,
66
+ Import {
67
+ module,
68
+ name,
69
+ ty : _,
70
+ } ,
71
+ ) ) = import
72
+ {
73
+ if let Some ( import_object) = wasm_import_object_map. get_mut ( module) {
74
+ import_object. push ( name. to_string ( ) ) ;
75
+ } else {
76
+ wasm_import_object_map. insert ( module, vec ! [ name. to_string( ) ] ) ;
77
+ }
78
+ }
79
+ } ) ;
80
+ }
81
+ } ) ;
82
+
83
+ let mut module_import_code = String :: new ( ) ;
84
+ let mut wasm_import_object_code = String :: new ( ) ;
85
+
86
+ for ( index, ( key, value) ) in wasm_import_object_map. iter ( ) . enumerate ( ) {
87
+ module_import_code. push_str ( & format ! (
88
+ "import * as module{module_idx} from \" {module}\" ;\n " ,
89
+ module_idx = index,
90
+ module = key
91
+ ) ) ;
92
+
93
+ wasm_import_object_code. push_str ( & format ! (
94
+ "\" {module}\" : {{ {names} }}" ,
95
+ module = key,
96
+ names = value
97
+ . iter( )
98
+ . map( |name| format!( "\" {}\" : module{}[\" {}\" ]" , name, index, name) )
99
+ . collect:: <Vec <String >>( )
100
+ . join( ", " )
101
+ ) ) ;
102
+ }
103
+
104
+ let mut content = String :: new ( ) ;
105
+ content. push_str ( & module_import_code) ;
106
+
107
+ if wasm_import_object_code. is_empty ( ) {
108
+ content. push_str ( & format ! (
109
+ "module.exports = require._interopreRequireWasm(exports, \" {}\" )" ,
110
+ final_file_name
111
+ ) ) ;
112
+ } else {
113
+ content. push_str ( & format ! (
114
+ "module.exports = require._interopreRequireWasm(exports, \" {}\" , {{{}}})" ,
115
+ final_file_name, wasm_import_object_code
116
+ ) ) ;
117
+ }
118
+
119
+ return Ok ( Some ( Content :: Js ( JsContent {
120
+ content,
121
+ ..Default :: default ( )
122
+ } ) ) ) ;
123
+ }
124
+
125
+ Ok ( None )
126
+ }
127
+ }
128
+
129
+ #[ cfg( test) ]
130
+ mod tests {
131
+ use std:: sync:: Arc ;
132
+
133
+ use super :: * ;
134
+ use crate :: ast:: file:: File ;
135
+ use crate :: compiler:: Context ;
136
+
137
+ #[ test]
138
+ fn test_wasm_runtime_load_with_import_object ( ) {
139
+ let plugin = WasmRuntimePlugin { } ;
140
+ let context = Arc :: new ( Context {
141
+ ..Default :: default ( )
142
+ } ) ;
143
+ let wasm_relative_path =
144
+ std:: path:: Path :: new ( "../../examples/import-resources/minus-wasm-pack/index_bg.wasm" ) ;
145
+ let wasm_path = std:: fs:: canonicalize ( wasm_relative_path) . unwrap ( ) ;
146
+ let file = File :: new ( wasm_path. to_string_lossy ( ) . to_string ( ) , context. clone ( ) ) ;
147
+ let param = PluginLoadParam { file : & file } ;
148
+ let result: Option < Content > = plugin. load ( & param, & context) . unwrap ( ) ;
149
+
150
+ assert ! ( result. is_some( ) ) ;
151
+ if let Some ( Content :: Js ( js_content) ) = result {
152
+ assert ! ( js_content. content. contains( "import * as module0 from" ) ) ;
153
+ }
154
+ }
155
+
156
+ #[ test]
157
+ fn test_wasm_runtime_load_without_import_object ( ) {
158
+ let plugin = WasmRuntimePlugin { } ;
159
+ let context = Arc :: new ( Context {
160
+ ..Default :: default ( )
161
+ } ) ;
162
+ let wasm_relative_path = std:: path:: Path :: new ( "../../examples/import-resources/add.wasm" ) ;
163
+ let wasm_path = std:: fs:: canonicalize ( wasm_relative_path) . unwrap ( ) ;
164
+ let file = File :: new ( wasm_path. to_string_lossy ( ) . to_string ( ) , context. clone ( ) ) ;
165
+ let param = PluginLoadParam { file : & file } ;
166
+ let result = plugin. load ( & param, & context) . unwrap ( ) ;
167
+ assert ! ( result. is_some( ) ) ;
168
+ if let Some ( Content :: Js ( js_content) ) = result {
169
+ assert ! ( !js_content. content. contains( "import * as module0 from" ) )
170
+ }
171
+ }
30
172
}
0 commit comments