def test_build(main="test.Main"): """Generates build (deployment) version of test runner""" session.setField("debug", True) session.permutateField("json") session.permutateField("engine") session.permutateField("runtime") # Initialize shared objects assetManager = AssetManager(session).addBuildProfile() outputManager = OutputManager(session, assetManager, compressionLevel=2) fileManager = FileManager(session) # Deploy assets outputManager.deployAssets([main]) # Store kernel script outputManager.storeKernel("{{prefix}}/script/kernel.js", bootCode="test.Kernel.init();") # Copy files from source for name in ["index.html", "phantom.js", "node.js"]: fileManager.updateFile("source/%s" % fileName, "{{prefix}}/%s" % fileName) for permutation in session.permutate(): # Resolving dependencies classes = Resolver(session).addClassName(main).getSortedClasses() # Compressing classes outputManager.storeCompressed(classes, "{{prefix}}/script/test-{{id}}.js")
def build(session, config, cdnPrefix="asset", compressionLevel=2, formattingLevel=0, kernelName="unify.Kernel"): name = config.get("name") assetManager = AssetManager(session) outputManager = OutputManager(session, assetManager, compressionLevel, formattingLevel) fileManager = FileManager(session) session.setField("unify.application.namespace", name) # Assets assetManager.addBuildProfile(cdnPrefix) assetManager.deploy(Resolver(session).addClassName("%s.Application" % name).getIncludedClasses()) # Store loader script if jasy.__version__ < "1.1": outputManager.storeKernel("$prefix/script/kernel.js", classes=[kernelName]) else: outputManager.storeKernel("$prefix/script/kernel.js", kernelName) # Copy files from source fileManager.updateFile("source/index.html", "$prefix/index.html") # Process every possible permutation for permutation in session.permutate(): # Resolving dependencies resolver = Resolver(session).addClassName("%s.Application" % name) # Compressing classes outputManager.storeCompressed(resolver.getSortedClasses(), "$prefix/script/%s-$permutation.js" % name, "unify.core.Init.startUp();")
def clean(): """Deletes generated JavaScript files, the build folder and clears all caches.""" session.clean() fm = FileManager(session) fm.removeDir("build") fm.removeDir("source/script")
def cacheManifest(profile): PREFIX = "{{prefix}}" HASH = "{{id}}" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") appcache = """CACHE MANIFEST # Jasy AppCache Manifest file # Version: {version} CACHE: {htmlfile} {scripts} {assets} NETWORK: *""" htmlcache = '<!DOCTYPE html><html manifest="{manifestfile}"></html>' destinationPath = profile.getDestinationPath() fileManager = FileManager(profile) session = profile.getSession() parts = profile.getParts() assetBuilder = AssetBuilder.AssetBuilder(profile) scriptBuilder = ScriptBuilder.ScriptBuilder(profile) styleBuilder = StyleBuilder.StyleBuilder(profile) assetManager = profile.getAssetManager() htmlfile = "index.html" for permutation in profile.permutate(): scripts = [] assets = [] if KERNEL_NAME in parts: scripts.append("js/kernel.js") for part in parts: if part != KERNEL_NAME: scripts.append(profile.expandFileName("js/%s-{{id}}.js" % part)) assets.append(profile.expandFileName("css/%s-{{id}}.css" % part)) # TODO: How to get permutated asset list? for (srcFile, dstFile) in assetManager.getAssetList(): assets.append(os.path.relpath(dstFile, profile.getDestinationPath())) appcacheFilename = "appcache-{{id}}.manifest" fileManager.writeFile( "{{destination}}/" + appcacheFilename, appcache.format(version=timestamp, htmlfile=htmlfile, scripts="\n".join(scripts), assets="\n".join(assets)) ) fileManager.writeFile("{{destination}}/index-{{id}}.html", htmlcache.format(manifestfile=profile.expandFileName(appcacheFilename))) Console.info("Generate manifest file...")
def cacheManifest(session, startClassName, scripts = ["script/application-%s.js"], htmlfile = "index.html", kernel = "script/kernel.js", ignoreAssets=False): # Check for new jasy replacement system (1.1.0-rc4) if session.expandFileName("{{id}}") != "{{id}}": PREFIX = "{{prefix}}" HASH = "{{id}}" else: PREFIX = "$prefix" HASH = "$permutation" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") appcache = """CACHE MANIFEST # Jasy AppCache Manifest file # Version: {version} CACHE: {htmlfile} {kernel} {scripts} {assets} NETWORK: *""" htmlcache = '<!DOCTYPE html><html manifest="%s"></html>' assetManager = AssetManager(session).addBuildProfile() outputManager = OutputManager(session, assetManager) fileManager = FileManager(session) # Create an application cache file for each permutation for permutation in session.permutate(): if ignoreAssets: assets = [] else: classes = Resolver(session).addClassName(startClassName).getSortedClasses() assetConfig = json.loads(assetManager.export(classes)) assets = filenamesFromAsset("", assetConfig["assets"], assetConfig["profiles"]) # Set options if hasattr(permutation, "getId"): checksum = permutation.getId() else: checksum = session.expandFileName(HASH) #instead of permutation.getChecksum() scriptFiles = [] for script in scripts: scriptFiles.append(script % checksum) manifestFilename = "appcache-%s.manifest" % (checksum) fileManager.writeFile(PREFIX + "/" + manifestFilename, appcache.format(version=timestamp, htmlfile=htmlfile, kernel=kernel, scripts="\n".join(scriptFiles), assets="\n".join(assets))) fileManager.writeFile(PREFIX + "/index-%s.html" % (checksum), htmlcache % manifestFilename)
def build(regenerate = False): """Generate pages""" profile = Profile(session) profile.registerPart("$${name}", styleName="$${name}.Main") profile.setHashAssets(True) profile.setCopyAssets(True) konstrukteur.build(profile, regenerate) Build.run(profile) fileManager = FileManager(profile) fileManager.updateFile("source/apache.htaccess", "{{destination}}/.htaccess")
def __init__(self, session, assetManager=None, compressionLevel=1, formattingLevel=0): Console.info("Initializing OutputManager...") Console.indent() Console.info("Formatting Level: %s", formattingLevel) Console.info("Compression Level: %s", compressionLevel) self.__session = session self.__assetManager = assetManager self.__fileManager = FileManager(session) self.__scriptOptimization = Optimization() self.__compressGeneratedCode = False self.__kernelClasses = [] if compressionLevel > 0: self.__scriptOptimization.enable("variables") self.__scriptOptimization.enable("declarations") self.__compressGeneratedCode = True if compressionLevel > 1: self.__scriptOptimization.enable("blocks") self.__scriptOptimization.enable("privates") self.__scriptFormatting = Formatting() if formattingLevel > 0: self.__scriptFormatting.enable("semicolon") self.__scriptFormatting.enable("comma") Console.outdent()
def __init__(self, session, assetManager=None, compressionLevel=1, formattingLevel=0): self.__session = session self.__assetManager = assetManager self.__fileManager = FileManager(session) self.__kernelClasses = [] self.__scriptOptimization = ScriptOptimization.Optimization() self.__scriptFormatting = ScriptFormatting.Formatting() self.__styleOptimization = StyleOptimization.Optimization() self.__styleFormatting = StyleFormatting.Formatting() self.__addDividers = formattingLevel > 0 if compressionLevel > 0: self.__scriptOptimization.enable("variables") self.__scriptOptimization.enable("declarations") if compressionLevel > 1: self.__scriptOptimization.enable("blocks") self.__scriptOptimization.enable("privates") if formattingLevel > 0: self.__styleFormatting.enable("selector") if formattingLevel > 1: self.__scriptFormatting.enable("semicolon") self.__scriptFormatting.enable("comma") self.__styleFormatting.enable("rule")
def distclean(): """Deletes all generated folders like api, build, external and all caches.""" session.clean() Repository.distclean() fm = FileManager(session) session.close() fm.removeDir("build") fm.removeDir("source/script") fm.removeDir("api") fm.removeDir("external")
def api(): """Generates the API viewer for the current project""" sourceFolder = session.getProjectByName("core").getPath() + "/source/" # Configure fields session.setField("debug", False) session.permutateField("es5") # Initialize shared objects assetManager = AssetManager(session).addBuildProfile() outputManager = OutputManager(session, assetManager, compressionLevel=2) fileManager = FileManager(session) # Deploy assets outputManager.deployAssets(["core.apibrowser.Browser"]) # Write kernel script outputManager.storeKernel("{{prefix}}/script/kernel.js", bootCode="core.apibrowser.Kernel.init();") # Copy files from source fileManager.updateFile(sourceFolder + "/apibrowser.html", "{{prefix}}/index.html") # Rewrite template as jsonp for tmpl in ["main", "error", "entry", "type", "params", "info", "origin", "tags"]: jsonTemplate = json.dumps({ "template" : open(sourceFolder + "/tmpl/apibrowser/%s.mustache" % tmpl).read() }) fileManager.writeFile("{{prefix}}/tmpl/%s.js" % tmpl, "apiload(%s, '%s.mustache')" % (jsonTemplate, tmpl)) # Process every possible permutation for permutation in session.permutate(): # Resolving dependencies resolver = Resolver(session).addClassName("core.apibrowser.Browser") # Compressing classes outputManager.storeCompressed(resolver.getSortedClasses(), "{{prefix}}/script/apibrowser-{{id}}.js", "new core.apibrowser.Browser;") # Write API data ApiWriter(session).write("{{prefix}}/data")
def build(session, config, cdnPrefix="asset", compressionLevel=2, formattingLevel=0, kernelName="unify.Kernel"): # Check for new jasy replacement system (1.1.0-rc4) if session.expandFileName("{{id}}") != "{{id}}": PREFIX = "{{prefix}}" HASH = "{{id}}" else: PREFIX = "$prefix" HASH = "$permutation" name = config.get("name") assetManager = AssetManager(session) outputManager = OutputManager(session, assetManager, compressionLevel, formattingLevel) fileManager = FileManager(session) session.setField("unify.application.namespace", name) # Assets assetManager.addBuildProfile(cdnPrefix) assetManager.deploy(Resolver(session).addClassName("%s.Application" % name).getIncludedClasses()) # Store loader script if jasy.__version__ < "1.1": outputManager.storeKernel(PREFIX + "/script/kernel.js", classes=[kernelName]) else: outputManager.storeKernel(PREFIX + "/script/kernel.js", kernelName) # Copy files from source fileManager.updateFile("source/index.html", PREFIX + "/index.html") # Process every possible permutation for permutation in session.permutate(): # Resolving dependencies resolver = Resolver(session).addClassName("%s.Application" % name) # Compressing classes outputManager.storeCompressed( resolver.getSortedClasses(), (PREFIX + "/script/%s-" + HASH + ".js") % name, "unify.core.Init.startUp();" )
def cacheManifest(session, startClassName, scripts=["script/application-%s.js"], htmlfile="index.html", kernel="script/kernel.js", ignoreAssets=False): # Check for new jasy replacement system (1.1.0-rc4) if session.expandFileName("{{id}}") != "{{id}}": PREFIX = "{{prefix}}" HASH = "{{id}}" else: PREFIX = "$prefix" HASH = "$permutation" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") appcache = """CACHE MANIFEST # Jasy AppCache Manifest file # Version: {version} CACHE: {htmlfile} {kernel} {scripts} {assets} NETWORK: *""" htmlcache = '<!DOCTYPE html><html manifest="%s"></html>' assetManager = AssetManager(session).addBuildProfile() outputManager = OutputManager(session, assetManager) fileManager = FileManager(session) # Create an application cache file for each permutation for permutation in session.permutate(): if ignoreAssets: assets = [] else: classes = Resolver(session).addClassName( startClassName).getSortedClasses() assetConfig = json.loads(assetManager.export(classes)) assets = filenamesFromAsset("", assetConfig["assets"], assetConfig["profiles"]) # Set options if hasattr(permutation, "getId"): checksum = permutation.getId() else: checksum = session.expandFileName( HASH) #instead of permutation.getChecksum() scriptFiles = [] for script in scripts: scriptFiles.append(script % checksum) manifestFilename = "appcache-%s.manifest" % (checksum) fileManager.writeFile( PREFIX + "/" + manifestFilename, appcache.format(version=timestamp, htmlfile=htmlfile, kernel=kernel, scripts="\n".join(scriptFiles), assets="\n".join(assets))) fileManager.writeFile(PREFIX + "/index-%s.html" % (checksum), htmlcache % manifestFilename)
class OutputManager: def __init__(self, session, assetManager=None, compressionLevel=1, formattingLevel=0): Console.info("Initializing OutputManager...") Console.indent() Console.info("Formatting Level: %s", formattingLevel) Console.info("Compression Level: %s", compressionLevel) self.__session = session self.__assetManager = assetManager self.__fileManager = FileManager(session) self.__scriptOptimization = Optimization() self.__compressGeneratedCode = False self.__kernelClasses = [] if compressionLevel > 0: self.__scriptOptimization.enable("variables") self.__scriptOptimization.enable("declarations") self.__compressGeneratedCode = True if compressionLevel > 1: self.__scriptOptimization.enable("blocks") self.__scriptOptimization.enable("privates") self.__scriptFormatting = Formatting() if formattingLevel > 0: self.__scriptFormatting.enable("semicolon") self.__scriptFormatting.enable("comma") Console.outdent() def deployAssets(self, classes, assetFolder=None): """ Deploys assets for the given classes and all their dependencies :param classes: List of classes to deploy assets for :type classes: list :param assetFolder: Destination folder of assets (defaults to $prefix/asset) :type assetFolder: string """ Console.info("Deploying assets...") Console.indent() resolver = Resolver(self.__session) for className in classes: resolver.addClassName(className) self.__assetManager.deploy(resolver.getIncludedClasses(), assetFolder=assetFolder) Console.outdent() def storeKernel(self, fileName, classes=None, debug=False): """ Writes a so-called kernel script to the given location. This script contains data about possible permutations based on current session values. It optionally might include asset data (useful when boot phase requires some assets) and localization data (if only one locale is built). Optimization of the script is auto-enabled when no other information is given. This method returns the classes which are included by the script so you can exclude it from the real other generated output files. """ Console.info("Storing kernel...") Console.indent() # Use a new permutation based on debug settings and statically configured fields self.__session.setStaticPermutation(debug=debug) # Build resolver # We need the permutation here because the field configuration might rely on detection classes resolver = Resolver(self.__session) detectionClasses = self.__session.getFieldDetectionClasses() for className in detectionClasses: resolver.addClassName(className) # Jasy client side classes to hold data resolver.addClassName("jasy.Env") resolver.addClassName("jasy.Asset") resolver.addClassName("jasy.Translate") # Allow kernel level mass loading of scripts (required for source, useful for build) resolver.addClassName("core.io.Script") resolver.addClassName("core.io.Queue") if classes: for className in classes: resolver.addClassName(className) # Generate boot code bootCode = "jasy.Env.setFields(%s);" % self.__session.exportFields() if self.__compressGeneratedCode: bootCode = packCode(bootCode) # Sort resulting class list sortedClasses = resolver.getSortedClasses() self.storeCompressed(sortedClasses, fileName, bootCode) # Remember classes for filtering in storeLoader/storeCompressed self.__kernelClasses = set(sortedClasses) # Reset static permutation self.__session.resetCurrentPermutation() Console.outdent() def storeCompressed(self, classes, fileName, bootCode=None): """ Combines the compressed result of the stored class list :param classes: List of sorted classes to compress :type classes: list :param fileName: Filename to write result to :type fileName: string :param bootCode: Code to execute once all the classes are loaded :type bootCode: string """ if self.__kernelClasses: filtered = [ classObj for classObj in classes if not classObj in self.__kernelClasses ] else: filtered = classes Console.info("Compressing %s classes...", len(filtered)) Console.indent() result = [] if self.__assetManager: assetData = self.__assetManager.export(filtered) if assetData: assetCode = "jasy.Asset.addData(%s);" % assetData if self.__compressGeneratedCode: result.append(packCode(assetCode)) else: result.append(assetCode) permutation = self.__session.getCurrentPermutation() try: for classObj in filtered: result.append( classObj.getCompressed( permutation, self.__session.getCurrentTranslationBundle(), self.__scriptOptimization, self.__scriptFormatting)) except ClassError as error: raise UserError("Error during class compression! %s" % error) Console.outdent() if bootCode: bootCode = "(function(){%s})();" % bootCode if self.__compressGeneratedCode: result.append(packCode(bootCode)) else: result.append(bootCode) if self.__compressGeneratedCode: compressedCode = "".join(result) else: compressedCode = "\n\n".join(result) self.__fileManager.writeFile(fileName, compressedCode) def storeLoader(self, classes, fileName, bootCode="", urlPrefix=""): """ Generates a source loader which is basically a file which loads the original JavaScript files. This is super useful during development of a project as it supports pretty fast workflows where most often a simple reload in the browser is enough to get the newest sources. :param classes: List of sorted classes to compress :type classes: list :param fileName: Filename to write result to :type fileName: string :param bootCode: Code to execute once all classes have been loaded :type bootCode: string :param urlPrefix: Prepends the given URL prefix to all class URLs to load :type urlPrefix: string """ if self.__kernelClasses: filtered = [ classObj for classObj in classes if not classObj in self.__kernelClasses ] else: filtered = classes Console.info("Generating loader for %s classes...", len(classes)) Console.indent() main = self.__session.getMain() files = [] for classObj in filtered: path = classObj.getPath() # Support for multi path classes # (typically in projects with custom layout/structure e.g. 3rd party) if type(path) is list: for singleFileName in path: files.append(main.toRelativeUrl(singleFileName, urlPrefix)) else: files.append(main.toRelativeUrl(path, urlPrefix)) result = [] Console.outdent() if self.__assetManager: assetData = self.__assetManager.export(filtered) if assetData: assetCode = "jasy.Asset.addData(%s);" % assetData if self.__compressGeneratedCode: result.append(packCode(assetCode)) else: result.append(assetCode) translationBundle = self.__session.getCurrentTranslationBundle() if translationBundle: translationData = translationBundle.export(filtered) if translationData: translationCode = 'jasy.Translate.addData(%s);' % translationData if self.__compressGeneratedCode: result.append(packCode(translationCode)) else: result.append(translationCode) if self.__compressGeneratedCode: loaderList = '"%s"' % '","'.join(files) else: loaderList = '"%s"' % '",\n"'.join(files) wrappedBootCode = "function(){ %s }" % bootCode if bootCode else "null" loaderCode = 'core.io.Queue.load([%s], %s, null, true);' % ( loaderList, wrappedBootCode) if self.__compressGeneratedCode: result.append(packCode(loaderCode)) else: result.append(loaderCode) if self.__compressGeneratedCode: loaderCode = "".join(result) else: loaderCode = "\n\n".join(result) self.__fileManager.writeFile(fileName, loaderCode)
class OutputManager: def __init__(self, session, assetManager=None, compressionLevel=1, formattingLevel=0): Console.info("Initializing OutputManager...") Console.indent() Console.info("Formatting Level: %s", formattingLevel) Console.info("Compression Level: %s", compressionLevel) self.__session = session self.__assetManager = assetManager self.__fileManager = FileManager(session) self.__scriptOptimization = Optimization() self.__compressGeneratedCode = False self.__kernelClasses = [] if compressionLevel > 0: self.__scriptOptimization.enable("variables") self.__scriptOptimization.enable("declarations") self.__compressGeneratedCode = True if compressionLevel > 1: self.__scriptOptimization.enable("blocks") self.__scriptOptimization.enable("privates") self.__scriptFormatting = Formatting() if formattingLevel > 0: self.__scriptFormatting.enable("semicolon") self.__scriptFormatting.enable("comma") Console.outdent() def deployAssets(self, classes, assetFolder=None): """ Deploys assets for the given classes and all their dependencies :param classes: List of classes to deploy assets for :type classes: list :param assetFolder: Destination folder of assets (defaults to $prefix/asset) :type assetFolder: string """ Console.info("Deploying assets...") Console.indent() resolver = Resolver(self.__session) for className in classes: resolver.addClassName(className) self.__assetManager.deploy(resolver.getIncludedClasses(), assetFolder=assetFolder) Console.outdent() def storeKernel(self, fileName, classes=None, debug=False): """ Writes a so-called kernel script to the given location. This script contains data about possible permutations based on current session values. It optionally might include asset data (useful when boot phase requires some assets) and localization data (if only one locale is built). Optimization of the script is auto-enabled when no other information is given. This method returns the classes which are included by the script so you can exclude it from the real other generated output files. """ Console.info("Storing kernel...") Console.indent() # Use a new permutation based on debug settings and statically configured fields self.__session.setStaticPermutation(debug=debug) # Build resolver # We need the permutation here because the field configuration might rely on detection classes resolver = Resolver(self.__session) detectionClasses = self.__session.getFieldDetectionClasses() for className in detectionClasses: resolver.addClassName(className) # Jasy client side classes to hold data resolver.addClassName("jasy.Env") resolver.addClassName("jasy.Asset") resolver.addClassName("jasy.Translate") # Allow kernel level mass loading of scripts (required for source, useful for build) resolver.addClassName("core.io.Script") resolver.addClassName("core.io.Queue") if classes: for className in classes: resolver.addClassName(className) # Generate boot code bootCode = "jasy.Env.setFields(%s);" % self.__session.exportFields() if self.__compressGeneratedCode: bootCode = packCode(bootCode) # Sort resulting class list sortedClasses = resolver.getSortedClasses() self.storeCompressed(sortedClasses, fileName, bootCode) # Remember classes for filtering in storeLoader/storeCompressed self.__kernelClasses = set(sortedClasses) # Reset static permutation self.__session.resetCurrentPermutation() Console.outdent() def storeCompressed(self, classes, fileName, bootCode=None): """ Combines the compressed result of the stored class list :param classes: List of sorted classes to compress :type classes: list :param fileName: Filename to write result to :type fileName: string :param bootCode: Code to execute once all the classes are loaded :type bootCode: string """ if self.__kernelClasses: filtered = [classObj for classObj in classes if not classObj in self.__kernelClasses] else: filtered = classes Console.info("Compressing %s classes...", len(filtered)) Console.indent() result = [] if self.__assetManager: assetData = self.__assetManager.export(filtered) if assetData: assetCode = "jasy.Asset.addData(%s);" % assetData if self.__compressGeneratedCode: result.append(packCode(assetCode)) else: result.append(assetCode) permutation = self.__session.getCurrentPermutation() try: for classObj in filtered: result.append( classObj.getCompressed( permutation, self.__session.getCurrentTranslationBundle(), self.__scriptOptimization, self.__scriptFormatting, ) ) except ClassError as error: raise UserError("Error during class compression! %s" % error) Console.outdent() if bootCode: bootCode = "(function(){%s})();" % bootCode if self.__compressGeneratedCode: result.append(packCode(bootCode)) else: result.append(bootCode) if self.__compressGeneratedCode: compressedCode = "".join(result) else: compressedCode = "\n\n".join(result) self.__fileManager.writeFile(fileName, compressedCode) def storeLoader(self, classes, fileName, bootCode="", urlPrefix=""): """ Generates a source loader which is basically a file which loads the original JavaScript files. This is super useful during development of a project as it supports pretty fast workflows where most often a simple reload in the browser is enough to get the newest sources. :param classes: List of sorted classes to compress :type classes: list :param fileName: Filename to write result to :type fileName: string :param bootCode: Code to execute once all classes have been loaded :type bootCode: string :param urlPrefix: Prepends the given URL prefix to all class URLs to load :type urlPrefix: string """ if self.__kernelClasses: filtered = [classObj for classObj in classes if not classObj in self.__kernelClasses] else: filtered = classes Console.info("Generating loader for %s classes...", len(classes)) Console.indent() main = self.__session.getMain() files = [] for classObj in filtered: path = classObj.getPath() # Support for multi path classes # (typically in projects with custom layout/structure e.g. 3rd party) if type(path) is list: for singleFileName in path: files.append(main.toRelativeUrl(singleFileName, urlPrefix)) else: files.append(main.toRelativeUrl(path, urlPrefix)) result = [] Console.outdent() if self.__assetManager: assetData = self.__assetManager.export(filtered) if assetData: assetCode = "jasy.Asset.addData(%s);" % assetData if self.__compressGeneratedCode: result.append(packCode(assetCode)) else: result.append(assetCode) translationBundle = self.__session.getCurrentTranslationBundle() if translationBundle: translationData = translationBundle.export(filtered) if translationData: translationCode = "jasy.Translate.addData(%s);" % translationData if self.__compressGeneratedCode: result.append(packCode(translationCode)) else: result.append(translationCode) if self.__compressGeneratedCode: loaderList = '"%s"' % '","'.join(files) else: loaderList = '"%s"' % '",\n"'.join(files) wrappedBootCode = "function(){ %s }" % bootCode if bootCode else "null" loaderCode = "core.io.Queue.load([%s], %s, null, true);" % (loaderList, wrappedBootCode) if self.__compressGeneratedCode: result.append(packCode(loaderCode)) else: result.append(loaderCode) if self.__compressGeneratedCode: loaderCode = "".join(result) else: loaderCode = "\n\n".join(result) self.__fileManager.writeFile(fileName, loaderCode)
class OutputManager: def __init__(self, session, assetManager=None, compressionLevel=1, formattingLevel=0): self.__session = session self.__assetManager = assetManager self.__fileManager = FileManager(session) self.__kernelClasses = [] self.__scriptOptimization = ScriptOptimization.Optimization() self.__scriptFormatting = ScriptFormatting.Formatting() self.__styleOptimization = StyleOptimization.Optimization() self.__styleFormatting = StyleFormatting.Formatting() self.__addDividers = formattingLevel > 0 if compressionLevel > 0: self.__scriptOptimization.enable("variables") self.__scriptOptimization.enable("declarations") if compressionLevel > 1: self.__scriptOptimization.enable("blocks") self.__scriptOptimization.enable("privates") if formattingLevel > 0: self.__styleFormatting.enable("selector") if formattingLevel > 1: self.__scriptFormatting.enable("semicolon") self.__scriptFormatting.enable("comma") self.__styleFormatting.enable("rule") def deployAssets(self, classes, assetFolder=None, hashNames=False): """ Deploys assets for the given classes and all their dependencies :param classes: List of classes to deploy assets for :type classes: list :param assetFolder: Destination folder of assets (defaults to {{prefix}}/asset) :type assetFolder: string """ Console.info("Deploying assets...") Console.indent() resolver = Resolver(self.__session) for className in classes: resolver.addClassName(className) self.__assetManager.deploy(resolver.getIncludedClasses(), assetFolder=assetFolder, hashNames=hashNames) Console.outdent() def __buildClassList(self, classes, bootCode=None, filterBy=None, inlineTranslations=False): session = self.__session # 1. Add given set of classes resolver = Resolver(session) for classItem in classes: resolver.addClass(classItem) # 2. Add optional boot code if bootCode: bootClassItem = session.getVirtualItem("jasy.generated.BootCode", ClassItem, "(function(){%s})();" % bootCode, ".js") resolver.addClass(bootClassItem) # 3. Check for asset usage includedClasses = resolver.getIncludedClasses() usesAssets = False for classItem in includedClasses: if classItem.getId() == "jasy.Asset": usesAssets = True break # 4. Add asset data if needed if usesAssets: assetData = self.__assetManager.export(includedClasses) assetClassItem = session.getVirtualItem("jasy.generated.AssetData", ClassItem, "jasy.Asset.addData(%s);" % assetData, ".js") resolver.addClass(assetClassItem, prepend=True) # 5. Add translation data if not inlineTranslations: translationBundle = self.__session.getCurrentTranslationBundle() if translationBundle: translationData = translationBundle.export(includedClasses) if translationData: translationClassItem = session.getVirtualItem("jasy.generated.TranslationData", ClassItem, "jasy.Translate.addData(%s);" % translationData, ".js") resolver.addClass(translationClassItem, prepend=True) # 6. Sorting classes sortedClasses = resolver.getSortedClasses() # 7. Apply filter if filterBy: filteredClasses = [] for classObj in sortedClasses: if not classObj in filterBy: filteredClasses.append(classObj) sortedClasses = filteredClasses return sortedClasses def __compressClasses(self, classes): try: session = self.__session result = [] for classObj in classes: compressed = classObj.getCompressed(session.getCurrentPermutation(), session.getCurrentTranslationBundle(), self.__scriptOptimization, self.__scriptFormatting) if self.__addDividers: result.append("// FILE ID: %s\n%s\n\n" % (classObj.getId(), compressed)) else: result.append(compressed) except ClassError as error: raise UserError("Error during class compression! %s" % error) return "".join(result) def __compressStyles(self, styles): try: session = self.__session result = [] for styleObj in styles: compressed = styleObj.getCompressed(session, session.getCurrentPermutation(), session.getCurrentTranslationBundle(), self.__styleOptimization, self.__styleFormatting) if self.__addDividers: result.append("/* FILE ID: %s */\n%s\n\n" % (styleObj.getId(), compressed)) else: result.append(compressed) except StyleError as error: raise UserError("Error during stylesheet compression! %s" % error) return "".join(result) def loadClasses(self, classes, urlPrefix=None): # For loading classes we require core.ui.Queue and core.io.Script # being available. If they are not part of the kernel, we have to # prepend them as compressed code into the resulting output. hasLoader = False hasQueue = False for classObj in self.__kernelClasses: className = classObj.getId() if className == "core.io.Queue": hasQueue = True elif className == "core.io.Script": hasLoader = True code = "" if not hasQueue or not hasLoader: compress = [] if not hasQueue: compress.append("core.io.Queue") if not hasLoader: compress.append("core.io.Script") compressedList = self.__buildClassList(compress, filterBy=self.__kernelClasses) code += self.__compressClasses(compressedList) main = self.__session.getMain() files = [] for classObj in classes: # Ignore already ompressed classes if classObj.getId() in ("core.io.Script", "core.io.Queue"): continue path = classObj.getPath() # Support for multi path classes # (typically in projects with custom layout/structure e.g. 3rd party) if type(path) is list: for singleFileName in path: files.append(main.toRelativeUrl(singleFileName, urlPrefix)) else: files.append(main.toRelativeUrl(path, urlPrefix)) if self.__addDividers: loaderList = '"%s"' % '",\n"'.join(files) else: loaderList = '"%s"' % '","'.join(files) code += 'core.io.Queue.load([%s], null, null, true);' % loaderList return code def storeKernel(self, fileName, bootCode=""): Console.info("Storing kernel...") Console.indent() # Export all field data for the kernel classes = [] fieldSetupClasses = self.__session.getFieldSetupClasses() for fieldName in fieldSetupClasses: classes.append(fieldSetupClasses[fieldName]) # Transfer all hard-wired fields into a permutation self.__session.setStaticPermutation() # Sort and compress sortedClasses = self.__buildClassList(classes, bootCode, inlineTranslations=True) Console.info("Compressing %s classes...", len(sortedClasses)) compressedCode = self.__compressClasses(sortedClasses) # Write file to disk self.__fileManager.writeFile(fileName, compressedCode) # Remember kernel level classes self.__kernelClasses = sortedClasses Console.outdent() def storeLoader(self, classes, fileName, bootCode="", urlPrefix=None): Console.info("Storing loader...") Console.indent() # Build class list sortedClasses = self.__buildClassList(classes, bootCode, filterBy=self.__kernelClasses) # Compress code Console.info("Including %s classes...", len(sortedClasses)) loaderCode = self.loadClasses(sortedClasses, urlPrefix=urlPrefix) # Write file to disk self.__fileManager.writeFile(fileName, loaderCode) Console.outdent() def storeCompressed(self, classes, fileName, bootCode=""): Console.info("Storing compressed classes...") Console.indent() # Build class list sortedClasses = self.__buildClassList(classes, bootCode, filterBy=self.__kernelClasses, inlineTranslations=True) # Compress code Console.info("Including %s classes...", len(sortedClasses)) compressedCode = self.__compressClasses(sortedClasses) # Write file to disk self.__fileManager.writeFile(fileName, compressedCode) Console.outdent() def storeCompressedStyleSheet(self, styles, fileName, bootCode=""): Console.info("Storing compressed stylesheet...") Console.indent() # Compress code Console.info("Including %s styles...", len(styles)) compressedCode = self.__compressStyles(styles) # Write file to disk self.__fileManager.writeFile(fileName, compressedCode) Console.outdent()