1 package jspacker 2 3 import ( 4 "strings" 5 6 "vimagination.zapto.org/javascript" 7 "vimagination.zapto.org/javascript/scope" 8 "vimagination.zapto.org/javascript/walk" 9 ) 10 11 type plugin struct { 12 imports uint 13 importURLs map[string]string 14 importBindings importBindingMap 15 importObjectBindings []javascript.BindingElement 16 importURLsArrayE []javascript.ArrayElement 17 importURLsArray []javascript.Argument 18 javascript.Module 19 d dependency 20 } 21 22 type importBindingMap map[string]javascript.MemberExpression 23 24 func (i importBindingMap) Handle(t javascript.Type) error { 25 if me, ok := t.(*javascript.MemberExpression); ok && me.PrimaryExpression != nil && me.PrimaryExpression.IdentifierReference != nil { 26 if nme, ok := i[me.PrimaryExpression.IdentifierReference.Data]; ok { 27 *me = nme 28 29 return nil 30 } 31 } 32 33 return walk.Walk(t, i) 34 } 35 36 // Plugin converts a single JavaScript module to make use of the processed 37 // exports from package. 38 func Plugin(m *javascript.Module, url string) (*javascript.Module, error) { 39 if !strings.HasPrefix(url, "/") { 40 return nil, ErrInvalidURL 41 } 42 43 p := plugin{ 44 importURLs: make(map[string]string), 45 importBindings: make(importBindingMap), 46 Module: javascript.Module{ 47 ModuleListItems: make([]javascript.ModuleItem, 1, len(m.ModuleListItems)), 48 }, 49 d: dependency{ 50 config: &config{ 51 resolveURL: RelTo, 52 }, 53 url: url, 54 prefix: "_", 55 }, 56 } 57 58 scope, err := scope.ModuleScope(m, nil) 59 if err != nil { 60 return nil, err 61 } 62 63 p.process(m, scope) 64 p.d.processBindings(scope) 65 p.addIncludes() 66 walk.Walk(&p.Module, &p.d) 67 walk.Walk(&p.Module, p.importBindings) 68 69 return &p.Module, nil 70 } 71 72 func (p *plugin) process(m *javascript.Module, scope *scope.Scope) { 73 for _, li := range m.ModuleListItems { 74 if li.ImportDeclaration != nil { 75 p.processImport(li.ImportDeclaration, scope) 76 } else if li.StatementListItem != nil { 77 p.ModuleListItems = append(p.ModuleListItems, li) 78 } else if li.ExportDeclaration != nil { 79 p.processExport(li.ExportDeclaration) 80 } 81 } 82 } 83 84 func (p *plugin) processImport(id *javascript.ImportDeclaration, scope *scope.Scope) { 85 durl, _ := javascript.Unquote(id.ModuleSpecifier.Data) 86 iurl := p.d.RelTo(durl) 87 88 ib, ok := p.importURLs[iurl] 89 if !ok { 90 p.imports++ 91 92 ib = id2String(p.imports) 93 p.importURLs[iurl] = ib 94 ae := wrapArgument(iurl) 95 p.importURLsArray = append(p.importURLsArray, ae) 96 p.importURLsArrayE = append(p.importURLsArrayE, javascript.ArrayElement{ 97 AssignmentExpression: ae.AssignmentExpression, 98 }) 99 p.importObjectBindings = append(p.importObjectBindings, javascript.BindingElement{ 100 SingleNameBinding: jToken(ib), 101 }) 102 } 103 104 if id.ImportClause != nil { 105 p.processImportClause(id, scope, ib) 106 } 107 } 108 109 func (p *plugin) processImportClause(id *javascript.ImportDeclaration, scope *scope.Scope, ib string) { 110 if id.NameSpaceImport != nil { 111 for _, binding := range scope.Bindings[id.NameSpaceImport.Data] { 112 binding.Data = ib 113 } 114 } 115 116 if id.ImportedDefaultBinding != nil { 117 p.importBindings[id.ImportedDefaultBinding.Data] = wrapMemberIdentifier(ib, jToken("default")) 118 } 119 120 if id.NamedImports != nil { 121 for _, is := range id.NamedImports.ImportList { 122 tk := is.ImportedBinding 123 124 if is.IdentifierName != nil { 125 tk = is.IdentifierName 126 } 127 128 p.importBindings[is.ImportedBinding.Data] = wrapMemberIdentifier(ib, tk) 129 } 130 } 131 } 132 133 func (p *plugin) processExport(ed *javascript.ExportDeclaration) { 134 if ed.VariableStatement != nil { 135 p.ModuleListItems = append(p.ModuleListItems, wrapVariableStatement(ed.VariableStatement)) 136 } else if ed.Declaration != nil { 137 p.ModuleListItems = append(p.ModuleListItems, wrapDeclaration(ed.Declaration)) 138 } else if ed.DefaultFunction != nil { 139 if ed.DefaultFunction.BindingIdentifier != nil { 140 p.ModuleListItems = append(p.ModuleListItems, wrapFunctionDeclaration(ed.DefaultFunction)) 141 } 142 } else if ed.DefaultClass != nil { 143 if ed.DefaultClass.BindingIdentifier != nil { 144 p.ModuleListItems = append(p.ModuleListItems, wrapClassDeclaration(ed.DefaultClass)) 145 } 146 } else if ed.DefaultAssignmentExpression != nil { 147 p.ModuleListItems = append(p.ModuleListItems, wrapAssignmentExpression(*ed.DefaultAssignmentExpression)) 148 } 149 } 150 151 func (p *plugin) addIncludes() { 152 if p.imports == 0 { 153 p.ModuleListItems = p.ModuleListItems[1:] 154 } else if p.imports == 1 { 155 p.ModuleListItems[0] = wrapIncludeCall(p.importObjectBindings[0].SingleNameBinding, p.importURLsArray) 156 } else { 157 p.ModuleListItems[0] = wrapIncludeAllCall(p.importObjectBindings, p.importURLsArrayE) 158 } 159 } 160