github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/www/combine.go (about)

     1  /* For license and copyright information please see LEGAL file in repository */
     2  
     3  package www
     4  
     5  import (
     6  	"strings"
     7  
     8  	"../convert"
     9  	"../file"
    10  	language "../language"
    11  	"../mediatype"
    12  	"../protocol"
    13  )
    14  
    15  type combine struct {
    16  	guiDir      protocol.FileDirectory
    17  	dlDir       protocol.FileDirectory
    18  	pagesDir    protocol.FileDirectory
    19  	widgetsDir  protocol.FileDirectory
    20  	landingsDir protocol.FileDirectory
    21  	mainHTMLDir protocol.FileDirectory // files name is just language in iso format e.g. "en", "fa",
    22  
    23  	inlined       map[string]protocol.File // map key is file path
    24  	mainHTMLFiles map[string]protocol.File // map key is language in iso format e.g. "en", "fa", ...
    25  	mainJSFiles   map[string]protocol.File // map key is language in iso format e.g. "en", "fa", ...
    26  
    27  	contentEncodings []string
    28  }
    29  
    30  // Update use to add needed repo files that get from disk or network to the assets!!
    31  func (c *combine) update() {
    32  	c.inlined = make(map[string]protocol.File)
    33  	c.mainHTMLFiles = make(map[string]protocol.File)
    34  	c.mainJSFiles = make(map[string]protocol.File)
    35  
    36  	c.guiDir, _ = protocol.App.Files().Directory(guiDirectoryName)
    37  	c.dlDir, _ = c.guiDir.Directory("design-languages")
    38  	c.pagesDir, _ = c.guiDir.Directory("pages")
    39  	c.widgetsDir, _ = c.guiDir.Directory("widgets")
    40  	c.landingsDir, _ = c.guiDir.Directory("landings")
    41  	c.mainHTMLDir, _ = c.guiDir.Directory(mainHTMLDirectoryName)
    42  
    43  	c.readyLandingsFiles()
    44  	switch "js" {
    45  	case "go":
    46  		c.readyAppGoToJSFile()
    47  	case "js":
    48  		c.readyAppJSFiles()
    49  	}
    50  	c.readySWFile()
    51  	c.readyDesignLanguagesFiles()
    52  }
    53  
    54  func (c *combine) readyAppGoToJSFile() {
    55  	var mainGOFile, _ = c.guiDir.File("main.go")
    56  	if mainGOFile == nil {
    57  		protocol.App.Log(protocol.LogType_Warning, "WWW - no main.go file exist to ready UI apps to serve!")
    58  		return
    59  	}
    60  
    61  	// TODO:: compile to JS by https://github.com/gopherjs/gopherjs
    62  }
    63  
    64  func (c *combine) readyAppJSFiles() {
    65  	var mainHTMLFile, _ = c.landingsDir.File("main.html")
    66  	if mainHTMLFile == nil {
    67  		protocol.App.Log(protocol.LogType_Warning, "WWW - no main.html file exist to ready UI apps to serve!")
    68  		return
    69  	}
    70  
    71  	var mainJSFile, _ = c.guiDir.File("main.js")
    72  	if mainJSFile == nil {
    73  		protocol.App.Log(protocol.LogType_Warning, "WWW - no main.js file exist to ready UI apps to serve!")
    74  		return
    75  	}
    76  
    77  	var lj = make(localize, 8)
    78  	var jsonFile, _ = c.guiDir.File("main.json")
    79  	if jsonFile == nil {
    80  		protocol.App.Log(protocol.LogType_Warning, "WWW - Can't find json localize file for /main.js")
    81  		return
    82  	}
    83  	lj.jsonDecoder(jsonFile.Data().Marshal())
    84  	if len(lj) == 0 {
    85  		protocol.App.Log(protocol.LogType_Warning, "WWW - Founded json not meet standards to localize for main.js")
    86  		return
    87  	}
    88  
    89  	var mainJSCreator = JS{
    90  		imports: make([]protocol.File, 1, 100),
    91  		inlined: c.inlined,
    92  	}
    93  	mainJSCreator.imports[0] = mainJSFile
    94  	_ = mainJSCreator.extractJSImportsRecursively(mainJSFile)
    95  	if protocol.AppDevMode {
    96  		var name strings.Builder
    97  		for _, imp := range mainJSCreator.imports {
    98  			name.WriteString(imp.Metadata().URI().Name())
    99  			name.WriteByte(',')
   100  		}
   101  		protocol.App.Log(protocol.LogType_Warning, "WWW - main.js imports recursively:", name.String())
   102  	}
   103  
   104  	for lang := range lj {
   105  		var localeJSFileName = "main-" + lang + ".js"
   106  		var localeJSFile, _ = c.guiDir.File(localeJSFileName)
   107  		// write some data if file exist before
   108  		localeJSFile.Data().Unmarshal([]byte("\n"))
   109  		c.mainJSFiles[lang] = localeJSFile
   110  	}
   111  
   112  	for i := len(mainJSCreator.imports) - 1; i >= 0; i-- {
   113  		c.addLocalized(mainJSCreator.imports[i])
   114  	}
   115  
   116  	for lang, localeMainJSFile := range c.mainJSFiles {
   117  		// Complete main JS file making proccess
   118  		var sdk, _ = protocol.App.ErrorsSDK(language.GetLanguageByISO(lang), mediatype.JavaScript)
   119  		// Add platfrom errors!
   120  		localeMainJSFile.Data().Append(sdk)
   121  		file.AddHashToFileName(localeMainJSFile)
   122  		file.Compress(localeMainJSFile, c.contentEncodings, protocol.BestCompression)
   123  
   124  		// make main.html
   125  		var localeJSFileName = localeMainJSFile.Metadata().URI().Name()
   126  		var localeMainHTMLFileName = "main-" + lang + ".html"
   127  		var localeMainHTMLFile, _ = c.guiDir.File(localeMainHTMLFileName)
   128  		var err = file.Minify(localeMainHTMLFile)
   129  		if err != nil && protocol.AppDebugMode {
   130  			protocol.App.Log(protocol.LogType_Warning, "Minify -", localeMainHTMLFileName, "occur this error:", err)
   131  		}
   132  		localeMainHTMLFile.Data().Replace([]byte("/main.js"), []byte(localeJSFileName), 1)
   133  		file.AddHashToFileName(localeMainHTMLFile)
   134  		file.Compress(localeMainHTMLFile, c.contentEncodings, protocol.BestCompression)
   135  		c.mainHTMLFiles[lang] = localeMainHTMLFile
   136  
   137  		// Add localized main html files without any compression to specific serve directory
   138  		var localeMainHTMLFileLangName, _ = c.mainHTMLDir.File(lang)
   139  		localeMainHTMLFileLangName.Data().Unmarshal(localeMainHTMLFile.Data().Marshal())
   140  	}
   141  }
   142  
   143  func (c *combine) addLocalized(file protocol.File) {
   144  	if c.isInlined(file.Metadata().URI().Path()) {
   145  		return
   146  	}
   147  	c.inlined[file.Metadata().URI().Path()] = file
   148  
   149  	var mixedData = localeAndMixJSFile(file)
   150  	for lang, mainFile := range c.mainJSFiles {
   151  		var localeData = mixedData[lang]
   152  		if localeData != nil {
   153  			mainFile.Data().Append(localeData)
   154  		} else {
   155  			if protocol.AppDevMode {
   156  				protocol.App.Log(protocol.LogType_Warning, "WWW - ", file.Metadata().URI().Name(), "don't have locale files in '", lang, "' language that main.js support this language. WWW Use pure file in combine proccess.")
   157  			}
   158  		}
   159  	}
   160  
   161  	// reset file to storage state to undo extract import from file
   162  	file.Data().Unmarshal(nil)
   163  }
   164  
   165  func (c *combine) isInlined(uriPath string) (ok bool) {
   166  	_, ok = c.inlined[uriPath]
   167  	return
   168  }
   169  
   170  // readyDesignLanguagesFiles read needed files from given repo folder and do some logic and return files.
   171  func (c *combine) readyDesignLanguagesFiles() {
   172  	var err protocol.Error
   173  	for _, dlFile := range c.dlDir.Files(0, 0) {
   174  		if dlFile.Metadata().URI().Extension() == "css" {
   175  			var cssName = dlFile.Metadata().URI().Name()
   176  			var cssFile, _ = c.dlDir.File("h-" + cssName)
   177  			cssFile.Data().Unmarshal(dlFile.Data().Marshal())
   178  			err = file.Minify(cssFile)
   179  			if err != nil && protocol.AppDebugMode {
   180  				protocol.App.Log(protocol.LogType_Warning, "Minify -", cssName, "occur this error:", err)
   181  			}
   182  			file.AddHashToFileName(cssFile)
   183  			file.Compress(cssFile, c.contentEncodings, protocol.BestCompression)
   184  
   185  			for _, mainFile := range c.mainJSFiles {
   186  				var fileNameWithoutHashAdded = convert.UnsafeStringToByteSlice(cssName)
   187  				var fileNameWithHashAdded = convert.UnsafeStringToByteSlice(cssFile.Metadata().URI().Name())
   188  				mainFile.Data().Replace(fileNameWithoutHashAdded, fileNameWithHashAdded, -1)
   189  			}
   190  		}
   191  	}
   192  	for _, dir := range c.dlDir.Directories(0, 0) {
   193  		var combinedFileName = dir.Metadata().URI().Name() + ".css"
   194  		var combinedFile, _ = c.dlDir.File(combinedFileName)
   195  		var combinedFileData []byte
   196  		for _, file := range dir.Files(0, 0) {
   197  			if file.Metadata().URI().Extension() == "css" {
   198  				combinedFileData = append(combinedFileData, file.Data().Marshal()...)
   199  			}
   200  		}
   201  		combinedFile.Data().Unmarshal(combinedFileData)
   202  		err = file.Minify(combinedFile)
   203  		if err != nil && protocol.AppDebugMode {
   204  			protocol.App.Log(protocol.LogType_Warning, "Minify -", combinedFileName, "occur this error:", err)
   205  		}
   206  		file.AddHashToFileName(combinedFile)
   207  		file.Compress(combinedFile, c.contentEncodings, protocol.BestCompression)
   208  
   209  		for _, mainFile := range c.mainJSFiles {
   210  			var fileNameWithoutHashAdded = convert.UnsafeStringToByteSlice(combinedFileName)
   211  			var fileNameWithHashAdded = convert.UnsafeStringToByteSlice(combinedFile.Metadata().URI().Name())
   212  			mainFile.Data().Replace(fileNameWithoutHashAdded, fileNameWithHashAdded, -1)
   213  		}
   214  	}
   215  }
   216  
   217  // readyLandingsFiles read needed files from given repo folder and do some logic and return files.
   218  func (c *combine) readyLandingsFiles() {
   219  	var err protocol.Error
   220  	// TODO::: Need to change landings file name to hash of data??
   221  	for _, landing := range c.landingsDir.Files(0, 0) {
   222  		switch landing.Metadata().URI().Extension() {
   223  		case "html":
   224  			var landingName = landing.Metadata().URI().NameWithoutExtension()
   225  			var landingJsonFile, _ = c.landingsDir.File(landingName + ".json")
   226  			if landingJsonFile == nil {
   227  				protocol.App.Log(protocol.LogType_Warning, "WWW - Can't find json localize json file for ", landing.Metadata().URI().Name())
   228  				continue
   229  			}
   230  			var lj = make(localize)
   231  			lj.jsonDecoder(landingJsonFile.Data().Marshal())
   232  			if len(lj) == 0 {
   233  				protocol.App.Log(protocol.LogType_Warning, "WWW - Founded json not meet standards to localize for ", landing.Metadata().URI().Name())
   234  				continue
   235  			}
   236  
   237  			var landingCssFile, _ = c.landingsDir.File(landingName + ".css")
   238  			var mixedHTMLFile = mixCSSToHTML(landing.Data().Marshal(), landingCssFile.Data().Marshal())
   239  
   240  			var localized map[string][]byte
   241  			localized, err = localizeHTMLFile(mixedHTMLFile, lj)
   242  			if err != nil {
   243  				protocol.App.Log(protocol.LogType_Warning, "WWW - Can't localize ", landing.Metadata().URI().Name(), " landing page due to ", err)
   244  			}
   245  			for lang, data := range localized {
   246  				var localizedFileName = landingName + "-" + lang + ".html"
   247  				var localizedLanding, _ = c.landingsDir.File(localizedFileName)
   248  				localizedLanding.Data().Unmarshal(data)
   249  				err = file.Minify(localizedLanding)
   250  				if err != nil && protocol.AppDebugMode {
   251  					protocol.App.Log(protocol.LogType_Warning, "WWW Minify -", localizedFileName, "occur this error:", err)
   252  				}
   253  				file.Compress(localizedLanding, c.contentEncodings, protocol.BestCompression)
   254  			}
   255  		}
   256  	}
   257  }
   258  
   259  // readySWFile read needed files from given repo folder and do some logic to make main.html.
   260  func (c *combine) readySWFile() {
   261  	var libJSDir, _ = c.guiDir.Directory("libjs")
   262  	var osDir, _ = libJSDir.Directory("os")
   263  	var swFile, _ = osDir.File("sw.js")
   264  	if swFile == nil {
   265  		protocol.App.Log(protocol.LogType_Warning, "WWW - Service-Worker(sw.js) not exist in /gui/libjs/os/sw.js to make locales sw-{{lang}}.js files")
   266  		return
   267  	}
   268  
   269  	for lang, mainHTMLFile := range c.mainHTMLFiles {
   270  		var localeSWFileName = "sw-" + lang + ".js"
   271  		var localeSWFile, _ = c.guiDir.File(localeSWFileName)
   272  		localeSWFile.Data().Unmarshal(swFile.Data().Marshal())
   273  		localeSWFile.Data().Replace([]byte("main.html"), []byte(mainHTMLFile.Metadata().URI().Name()), -1)
   274  		var err = file.Minify(localeSWFile)
   275  		if err != nil && protocol.AppDebugMode {
   276  			protocol.App.Log(protocol.LogType_Warning, "Minify -", localeSWFileName, "occur this error:", err)
   277  		}
   278  		file.Compress(localeSWFile, c.contentEncodings, protocol.BestCompression)
   279  	}
   280  }