github.com/keysonZZZ/kmg@v0.0.0-20151121023212-05317bfd7d39/kmgView/kmgViewResource/kmgViewResource.go (about) 1 package kmgViewResource 2 3 import ( 4 "fmt" 5 "github.com/bronze1man/kmg/kmgConfig" 6 "github.com/bronze1man/kmg/kmgCrypto" 7 "github.com/bronze1man/kmg/kmgFile" 8 "github.com/bronze1man/kmg/kmgGoSource/kmgFormat" 9 "github.com/bronze1man/kmg/kmgStrings" 10 "github.com/bronze1man/kmg/third/kmgQiniu" 11 "path" 12 "path/filepath" 13 "sort" 14 ) 15 16 type ResourceUploadRequest struct { 17 ImportPathList []string 18 //ResourceList []string //传入一堆资源目录的列表,然后传到七牛上. 19 Qiniu *kmgQiniu.Context // 如果传入qiniu相关的对象,生成代码时,会向七牛上传资源文件 20 QiniuPrefix string 21 OutGoFilePath string 22 Name string //缓存和区分不同实例使用. 23 } 24 25 var allowResourceExt = []string{".otf", ".eot", ".svg", ".ttf", ".woff", ".woff2", ".jpg", ".jpeg", ".png", ".gif", ".ico", ".html"} 26 27 func ResourceBuild(req *ResourceUploadRequest) { 28 if req.Name == "" { 29 panic(`[ResourceBuild] req.Name == ""`) 30 } 31 tmpDirPath := kmgConfig.DefaultEnv().PathInTmp("kmgViewResource_build/" + req.Name) 32 kmgFile.MustDelete(tmpDirPath) 33 response := resourceBuildToDir(req.ImportPathList, tmpDirPath) 34 req.Qiniu.MustUploadFromFile(tmpDirPath, req.QiniuPrefix) 35 36 packageName := filepath.Base(filepath.Dir(req.OutGoFilePath)) 37 38 urlPrefix := req.Qiniu.GetSchemeAndDomain() + "/" + req.QiniuPrefix 39 //jsUrl:=urlPrefix+"/"+response.JsFileName 40 //cssUrl:=urlPrefix+"/"+response.CssFileName 41 42 // 不可以使用 fmt.Sprintf("%#v",generated) 会导出私有变量. 43 //generated:=&Generated{ 44 // Name: req.Name, 45 // GeneratedJsFileUrl: jsUrl, 46 // GeneratedCssFileUrl: cssUrl, 47 // GeneratedUrlPrefix: urlPrefix, 48 // RequestImportList: req.ImportPathList, 49 //} 50 outGoContent := []byte(`package ` + packageName + ` 51 import ( 52 "github.com/bronze1man/kmg/kmgView/kmgViewResource" 53 "sync" 54 ) 55 var ` + req.Name + `Once sync.Once 56 var ` + req.Name + `generated *kmgViewResource.Generated 57 func get` + req.Name + `ViewResource() *kmgViewResource.Generated{ 58 ` + req.Name + `Once.Do(func(){ 59 ` + req.Name + `generated = &kmgViewResource.Generated{ 60 Name: ` + fmt.Sprintf("%#v", req.Name) + `, 61 GeneratedJsFileName: ` + fmt.Sprintf("%#v", response.JsFileName) + `, 62 GeneratedCssFileName: ` + fmt.Sprintf("%#v", response.CssFileName) + `, 63 GeneratedUrlPrefix: ` + fmt.Sprintf("%#v", urlPrefix) + `, 64 RequestImportList: ` + fmt.Sprintf("%#v", req.ImportPathList) + `, 65 } 66 ` + req.Name + `generated.Init() 67 }) 68 return ` + req.Name + `generated 69 } 70 `) 71 outGoContent, err := kmgFormat.Source(outGoContent) 72 if err != nil { 73 panic(err) 74 } 75 kmgFile.MustWriteFile(req.OutGoFilePath, outGoContent) 76 } 77 78 type resourceBuildToDirResponse struct { 79 NeedCachePathList []string 80 CssFileName string 81 JsFileName string 82 83 ImportPackageList []string //需要导入的 package 的列表, 此处对这个信息进行缓存验证 84 } 85 86 func resourceBuildToDir(ImportPackageList []string, tmpDirPath string) (response resourceBuildToDirResponse) { 87 builder := &tBuilder{ 88 pkgMap: map[string]*pkg{}, 89 } 90 for _, importPath := range ImportPackageList { 91 builder.handlePkg(importPath) 92 } 93 for _, pkg := range builder.pkgDepOrder { 94 builder.JsContent = append(builder.JsContent, pkg.JsContent...) 95 builder.JsContent = append(builder.JsContent, byte('\n')) 96 builder.CssContent = append(builder.CssContent, pkg.CssContent...) 97 builder.CssContent = append(builder.CssContent, byte('\n')) 98 } 99 100 response.CssFileName = kmgCrypto.Md5Hex(builder.CssContent) + ".css" 101 response.JsFileName = kmgCrypto.Md5Hex(builder.JsContent) + ".js" 102 103 kmgFile.MustMkdir(tmpDirPath) 104 kmgFile.MustWriteFile(filepath.Join(tmpDirPath, response.CssFileName), builder.CssContent) 105 kmgFile.MustWriteFile(filepath.Join(tmpDirPath, response.JsFileName), builder.JsContent) 106 for _, pkg := range builder.pkgDepOrder { 107 for _, filePath := range pkg.ResourceFilePathList { 108 kmgFile.MustWriteFile(filepath.Join(tmpDirPath, filepath.Base(filePath)), kmgFile.MustReadFile(filePath)) 109 } 110 } 111 for _, pkg := range builder.pkgDepOrder { 112 response.NeedCachePathList = append(response.NeedCachePathList, pkg.Dirpath) 113 } 114 response.ImportPackageList = ImportPackageList 115 return response 116 } 117 118 type tBuilder struct { 119 pkgMap map[string]*pkg // pkg是否访问过表.保证一个pkg在后面只出现一次 120 pkgDepOrder []*pkg // 依赖引用树,从叶子开始遍历,保证前面的不会引用后面的. 121 pkgDepStack pkgStack // 依赖循环检查堆栈,保证系统不存在依赖循环. 122 123 JsContent []byte 124 CssContent []byte 125 CacheNeedCheckDir []string 126 127 ResourceFileNameMap map[string]bool // 不允许资源文件的名称完全相同. 128 } 129 130 type pkg struct { 131 PackageName string 132 Dirpath string 133 ImportPathList []string 134 135 JsFilePathList []string 136 CssFilePathList []string 137 138 ResourceFilePathList []string 139 // 合并好的js和css的内容, 140 JsContent []byte 141 CssContent []byte 142 } 143 144 func (b *tBuilder) handlePkg(packageName string) { 145 for _, thisPkg := range b.pkgDepStack.arr { 146 if thisPkg.PackageName == packageName { 147 panic("[kmgViewResource] import circle " + packageName) 148 } 149 } 150 thisPkg, ok := b.pkgMap[packageName] 151 if ok { 152 return 153 } 154 thisPkg = b.parsePkg(packageName) 155 b.pkgMap[packageName] = thisPkg 156 b.pkgDepStack.push(thisPkg) 157 for _, importPath := range thisPkg.ImportPathList { 158 b.handlePkg(importPath) 159 } 160 b.pkgDepStack.pop() 161 b.pkgDepOrder = append(b.pkgDepOrder, thisPkg) 162 } 163 164 func (b *tBuilder) parsePkg(packageName string) *pkg { 165 thisPkg := &pkg{ 166 PackageName: packageName, 167 } 168 thisPkg.Dirpath = path.Join(kmgConfig.DefaultEnv().GetFirstGOPATH(), "src", packageName) 169 if !kmgFile.MustDirectoryExist(thisPkg.Dirpath) { 170 panic("[kmgViewResource] can not found dir " + thisPkg.Dirpath) 171 } 172 fileList := kmgFile.MustGetAllFileOneLevel(thisPkg.Dirpath) 173 for _, file := range fileList { 174 ext := kmgFile.GetExt(file) 175 if ext == ".js" { 176 thisPkg.JsFilePathList = append(thisPkg.JsFilePathList, file) 177 178 importPathList := parseImportPath(file, kmgFile.MustReadFile(file)) 179 thisPkg.ImportPathList = kmgStrings.SliceNoRepeatMerge(thisPkg.ImportPathList, importPathList) 180 } else if ext == ".css" { 181 thisPkg.CssFilePathList = append(thisPkg.CssFilePathList, file) 182 183 importPathList := parseImportPath(file, kmgFile.MustReadFile(file)) 184 thisPkg.ImportPathList = kmgStrings.SliceNoRepeatMerge(thisPkg.ImportPathList, importPathList) 185 } else if kmgStrings.IsInSlice(allowResourceExt, ext) { 186 name := filepath.Base(file) 187 if b.ResourceFileNameMap[name] { 188 panic("[kmgViewResource] resource file name " + name + " repeat path " + file) 189 } 190 thisPkg.ResourceFilePathList = append(thisPkg.ResourceFilePathList, file) 191 } 192 } 193 sort.Strings(thisPkg.JsFilePathList) 194 sort.Strings(thisPkg.CssFilePathList) 195 196 for _, file := range thisPkg.JsFilePathList { 197 // 这个泄漏信息比较严重.暂时关掉吧. 198 //thisPkg.JsContent = append(thisPkg.JsContent, []byte("\n/* "+file+" */\n\n")...) 199 thisPkg.JsContent = append(thisPkg.JsContent, kmgFile.MustReadFile(file)...) 200 thisPkg.JsContent = append(thisPkg.JsContent, byte('\n')) 201 } 202 203 for _, file := range thisPkg.CssFilePathList { 204 //thisPkg.CssContent = append(thisPkg.CssContent, []byte("\n/* "+file+"*/\n\n")...) 205 thisPkg.CssContent = append(thisPkg.CssContent, kmgFile.MustReadFile(file)...) 206 thisPkg.CssContent = append(thisPkg.CssContent, byte('\n')) 207 } 208 209 return thisPkg 210 } 211 212 type pkgStack struct { 213 arr []*pkg 214 pos int 215 } 216 217 func (stack *pkgStack) push(p *pkg) { 218 stack.arr = append(stack.arr, p) 219 stack.pos++ 220 } 221 222 func (stack *pkgStack) pop() *pkg { 223 if stack.pos == 0 { 224 panic("[pkgStack.pop] stack.pos==0") 225 } 226 stack.pos-- 227 p := stack.arr[stack.pos] 228 stack.arr = stack.arr[:stack.pos] 229 return p 230 }