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  }