def deploy(self, classes, assetFolder="asset"): """Deploys all asset files to the destination asset folder""" assets = self.__assets projects = session.getProjects() copyAssetFolder = prependPrefix(assetFolder) filterExpr = self.__compileFilterExpr(classes) info("Deploying assets...") counter = 0 length = len(assets) for fileId in assets: if not filterExpr.match(fileId): length -= 1 continue srcFile = assets[fileId].getPath() dstFile = os.path.join(copyAssetFolder, fileId.replace("/", os.sep)) if updateFile(srcFile, dstFile): counter += 1 info("Updated %s/%s files" % (counter, length))
def __parseTemplate(self): """ Process all templates to support jasy commands """ for project in session.getProjects(): templates = project.getItems("jasy.Template") if templates: for template, content in templates.items(): template = self.__fixTemplateName(template) self.__templates[template] = konstrukteur.Util.fixCoreTemplating(self.__fixJasyCommands(content.getText())) self.__renderer = pystache.Renderer(partials=self.__templates, escape=lambda u: u) self.__safeRenderer = pystache.Renderer(partials=self.__templates)
def index(self): if self.__assets is not None: return self.__assets # Loop though all projects and merge assets assets = self.__assets = {} for project in session.getProjects(): assets.update(project.assets) self.__processSprites() self.__processAnimations() info("Initialized %s assets" % len(assets)) return assets
def __initializeTemplates(self): """Process all templates to support jasy commands.""" # Build a map of all known templates self.__templates = {} for project in session.getProjects(): templates = project.getItems("jasy.Template") if templates: for name, item in templates.items(): self.__templates[name] = item.getText() for name in self.__templates: content = self.__templates[name] compiled = TemplateCompiler.compile(content) self.__templates[name] = compiled
def __init__(self): # Keep permutation reference self.__permutation = getPermutation() # Required classes by the user self.__required = [] # Hard excluded classes (used for filtering previously included classes etc.) self.__excluded = [] # Included classes after dependency calculation self.__included = [] # Collecting all available classes self.__classes = {} for project in session.getProjects(): self.__classes.update(project.getClasses())
def __init__(self): self.__data = {} header("Preparing assets") # Registry for profiles aka asset groups self.__profiles = [] # Initialize storage pool assets = self.__assets = {} # Loop though all projects and merge assets for project in session.getProjects(): assets.update(project.assets) self.__processSprites() self.__processAnimations() info("Initialized %s assets" % len(assets))
def process(self, apiData, classFilter=None, internals=False, privates=False, printErrors=True): knownClasses = set(list(apiData)) # # Attaching Links to Source Code (Lines) # Building Documentation Summaries # info("Adding Source Links...") for className in apiData: classApi = apiData[className] constructData = getattr(classApi, "construct", None) if constructData is not None: if "line" in constructData: constructData["sourceLink"] = "source:%s~%s" % (className, constructData["line"]) for section in ("properties", "events", "statics", "members"): sectionData = getattr(classApi, section, None) if sectionData is not None: for name in sectionData: if "line" in sectionData[name]: sectionData[name]["sourceLink"] = "source:%s~%s" % (className, sectionData[name]["line"]) # # Including Mixins / IncludedBy # info("Resolving Mixins...") # Just used temporary to keep track of which classes are merged mergedClasses = set() def getApi(className): classApi = apiData[className] if className in mergedClasses: return classApi classIncludes = getattr(classApi, "includes", None) if classIncludes: for mixinName in classIncludes: if not mixinName in apiData: error("Invalid mixin %s in class %s", className, mixinName) continue mixinApi = apiData[mixinName] if not hasattr(mixinApi, "includedBy"): mixinApi.includedBy = set() mixinApi.includedBy.add(className) mergeMixin(className, mixinName, classApi, getApi(mixinName)) mergedClasses.add(className) return classApi for className in apiData: apiData[className] = getApi(className) # # Checking links # info("Checking Links...") additionalTypes = ("Call", "Identifier", "Map", "Integer", "Node", "Element") def checkInternalLink(link, className): match = internalLinkParse.match(link) if not match: return 'Invalid link "#%s"' % link if match.group(3) is not None: className = match.group(3) if not className in knownClasses and not className in apiData: return 'Invalid class in link "#%s"' % link # Accept all section/item values for named classes, # as it might be pretty complicated to verify this here. if not className in apiData: return True classApi = apiData[className] sectionName = match.group(2) itemName = match.group(5) if itemName is None: return True if sectionName is not None: if not sectionName in linkMap: return 'Invalid section in link "#%s"' % link section = getattr(classApi, linkMap[sectionName], None) if section is None: return 'Invalid section in link "#%s"' % link else: if itemName in section: return True return 'Invalid item in link "#%s"' % link for sectionName in ("statics", "members", "properties", "events"): section = getattr(classApi, sectionName, None) if section and itemName in section: return True return 'Invalid item link "#%s"' % link def checkLinksInItem(item): # Process types if "type" in item: if item["type"] == "Function": # Check param types if "params" in item: for paramName in item["params"]: paramEntry = item["params"][paramName] if "type" in paramEntry: for paramTypeEntry in paramEntry["type"]: if not paramTypeEntry["name"] in knownClasses and not paramTypeEntry["name"] in additionalTypes and not ("builtin" in paramTypeEntry or "pseudo" in paramTypeEntry): item["errornous"] = True error('- Invalid param type "%s" in %s at line %s', paramTypeEntry["name"], className, item["line"]) if not "pseudo" in paramTypeEntry and paramTypeEntry["name"] in knownClasses: paramTypeEntry["linkable"] = True # Check return types if "returns" in item: for returnTypeEntry in item["returns"]: if not returnTypeEntry["name"] in knownClasses and not returnTypeEntry["name"] in additionalTypes and not ("builtin" in returnTypeEntry or "pseudo" in returnTypeEntry): item["errornous"] = True error('- Invalid return type "%s" in %s at line %s', returnTypeEntry["name"], className, item["line"]) if not "pseudo" in returnTypeEntry and returnTypeEntry["name"] in knownClasses: returnTypeEntry["linkable"] = True elif not item["type"] in builtinTypes and not item["type"] in pseudoTypes and not item["type"] in additionalTypes: error('- Invalid type "%s" in %s at line %s', item["type"], className, item["line"]) # Process doc if "doc" in item: def processInternalLink(match): linkUrl = match.group(2) if linkUrl.startswith("#"): linkCheck = checkInternalLink(linkUrl[1:], className) if linkCheck is not True: item["errornous"] = True if sectionName: error("- %s in %s:%s~%s at line %s" % (linkCheck, sectionName, className, name, item["line"])) else: error("- %s in %s at line %s" % (linkCheck, className, item["line"])) linkExtract.sub(processInternalLink, item["doc"]) # Process APIs for className in apiData: classApi = apiData[className] sectionName = None constructData = getattr(classApi, "construct", None) if constructData is not None: checkLinksInItem(constructData) for sectionName in ("properties", "events", "statics", "members"): section = getattr(classApi, sectionName, None) if section is not None: for name in section: checkLinksInItem(section[name]) # # Filter Internals/Privates # info("Filtering Items...") def isVisible(entry): if "visibility" in entry: visibility = entry["visibility"] if visibility == "private" and not privates: return False if visibility == "internal" and not internals: return False return True def filterInternalsPrivates(classApi, field): data = getattr(classApi, field, None) if data: for name in list(data): if not isVisible(data[name]): del data[name] for className in apiData: filterInternalsPrivates(apiData[className], "statics") filterInternalsPrivates(apiData[className], "members") # # Connection Interfaces / ImplementedBy # info("Connecting Interfaces...") for className in apiData: classApi = getApi(className) if not hasattr(classApi, "main"): continue classType = classApi.main["type"] if classType == "core.Class": classImplements = getattr(classApi, "implements", None) if classImplements: for interfaceName in classImplements: interfaceApi = apiData[interfaceName] implementedBy = getattr(interfaceApi, "implementedBy", None) if not implementedBy: implementedBy = interfaceApi.implementedBy = [] implementedBy.append(className) connectInterface(className, interfaceName, classApi, interfaceApi) # # Merging Named Classes # info("Merging Named Classes...") indent() for className in list(apiData): classApi = apiData[className] destName = classApi.main["name"] if destName is not None and destName != className: debug("Extending class %s with %s", destName, className) if destName in apiData: destApi = apiData[destName] destApi.main["from"].append(className) else: destApi = apiData[destName] = ApiData(destName) destApi.main = { "type" : "Extend", "name" : destName, "from" : [className] } # If there is a "main" tag found in the class use its API description if "tags" in classApi.main and classApi.main["tags"] is not None and "main" in classApi.main["tags"]: if "doc" in classApi.main: destApi.main["doc"] = classApi.main["doc"] classApi.main["extension"] = True # Read existing data construct = getattr(classApi, "construct", None) statics = getattr(classApi, "statics", None) members = getattr(classApi, "members", None) if construct is not None: if hasattr(destApi, "construct"): warn("Overriding constructor in extension %s by %s", destName, className) destApi.construct = copy.copy(construct) if statics is not None: if not hasattr(destApi, "statics"): destApi.statics = {} for staticName in statics: destApi.statics[staticName] = copy.copy(statics[staticName]) destApi.statics[staticName]["from"] = className destApi.statics[staticName]["fromLink"] = "static:%s~%s" % (className, staticName) if members is not None: if not hasattr(destApi, "members"): destApi.members = {} for memberName in members: destApi.members[memberName] = copy.copy(members[memberName]) destApi.members[memberName]["from"] = className destApi.members[memberName]["fromLink"] = "member:%s~%s" % (className, memberName) outdent() # # Connecting Uses / UsedBy # info("Collecting Use Patterns...") # This matches all uses with the known classes and only keeps them if matched allClasses = set(list(apiData)) for className in apiData: uses = apiData[className].uses # Rebuild use list cleanUses = set() for use in uses: if use != className and use in allClasses: cleanUses.add(use) useEntry = apiData[use] if not hasattr(useEntry, "usedBy"): useEntry.usedBy = set() useEntry.usedBy.add(className) apiData[className].uses = cleanUses # # Collecting errors # info("Collecting Errors...") indent() for className in sorted(apiData): classApi = apiData[className] errors = [] if isErrornous(classApi.main): errors.append({ "kind": "Main", "name": None, "line": 1 }) if hasattr(classApi, "construct"): if isErrornous(classApi.construct): errors.append({ "kind": "Constructor", "name": None, "line": classApi.construct["line"] }) for section in ("statics", "members", "properties", "events"): items = getattr(classApi, section, {}) for itemName in items: item = items[itemName] if isErrornous(item): errors.append({ "kind": itemMap[section], "name": itemName, "line": item["line"] }) if errors: if printErrors: warn("Found errors in %s", className) errorsSorted = sorted(errors, key=lambda entry: entry["line"]) if printErrors: indent() for entry in errorsSorted: if entry["name"]: warn("%s: %s (line %s)", entry["kind"], entry["name"], entry["line"]) else: warn("%s (line %s)", entry["kind"], entry["line"]) outdent() classApi.errors = errorsSorted outdent() # # Building Search Index # info("Building Search Index...") search = {} def addSearch(classApi, field): data = getattr(classApi, field, None) if data: for name in data: if not name in search: search[name] = set() search[name].add(className) for className in apiData: classApi = apiData[className] addSearch(classApi, "statics") addSearch(classApi, "members") addSearch(classApi, "properties") addSearch(classApi, "events") # # Post Process (dict to sorted list) # info("Post Processing Data...") for className in sorted(apiData): classApi = apiData[className] convertTags(classApi.main) construct = getattr(classApi, "construct", None) if construct: convertFunction(construct) convertTags(construct) for section in ("statics", "members", "properties", "events"): items = getattr(classApi, section, None) if items: sortedList = [] for itemName in sorted(items): item = items[itemName] item["name"] = itemName if "type" in item and item["type"] == "Function": convertFunction(item) convertTags(item) sortedList.append(item) setattr(classApi, section, sortedList) # # Collecting Package Docs # info("Collecting Package Docs...") indent() # Inject existing package docs into api data for project in session.getProjects(): docs = project.getDocs() for packageName in docs: if self.isIncluded(packageName, classFilter): debug("Creating package documentation %s", packageName) apiData[packageName] = docs[packageName].getApi() # Fill missing package docs for className in sorted(apiData): splits = className.split(".") packageName = splits[0] for split in splits[1:]: if not packageName in apiData: warn("Missing package documentation %s", packageName) apiData[packageName] = ApiData(packageName) apiData[packageName].main = { "type" : "Package", "name" : packageName } packageName = "%s.%s" % (packageName, split) # Now register all classes in their parent namespace/package for className in sorted(apiData): splits = className.split(".") packageName = ".".join(splits[:-1]) if packageName: package = apiData[packageName] # debug("- Registering class %s in parent %s", className, packageName) entry = { "name" : splits[-1], "link" : className, } classMain = apiData[className].main if "doc" in classMain and classMain["doc"]: summary = extractSummary(classMain["doc"]) if summary: entry["summary"] = summary if "type" in classMain and classMain["type"]: entry["type"] = classMain["type"] if not hasattr(package, "content"): package.content = [entry] else: package.content.append(entry) outdent() # # Writing API Index # debug("Building Index...") index = {} for className in sorted(apiData): classApi = apiData[className] mainInfo = classApi.main # Create structure for className current = index for split in className.split("."): if not split in current: current[split] = {} current = current[split] # Store current type current["$type"] = mainInfo["type"] # Keep information if if hasattr(classApi, "content"): current["$content"] = True # # Return # return apiData, index, search
def write(self, distFolder, classFilter=None, callback="apiload", showInternals=False, showPrivates=False, printErrors=True, highlightCode=True): # # Collecting # header("Collecting API Data...") apiData = {} highlightedCode = {} for project in session.getProjects(): classes = project.getClasses() info("Loading API of project %s: %s...", colorize(project.getName(), "bold"), colorize("%s classes" % len(classes), "cyan")) indent() for className in classes: if self.isIncluded(className, classFilter): apiData[className] = classes[className].getApi(highlightCode) highlightedCode[className] = classes[className].getHighlightedCode() outdent() # # Processing # header("Processing API Data...") data, index, search = self.process(apiData, classFilter=classFilter, internals=showInternals, privates=showPrivates, printErrors=printErrors) # # Writing # header("Storing API data...") writeCounter = 0 extension = "js" if callback else "json" def encode(content, name): if callback: return "%s(%s,'%s');" % (callback, toJson(content), name) else: return toJson(content) info("Saving class data (%s files)...", len(data)) for className in data: try: classData = data[className] if type(classData) is dict: classExport = classData else: classExport = classData.export() writeFile(os.path.join(distFolder, "%s.%s" % (className, extension)), encode(classExport, className)) except TypeError as writeError: error("Could not write API data of: %s: %s", className, writeError) continue info("Saving highlighted code (%s files)...", len(highlightedCode)) for className in highlightedCode: try: writeFile(os.path.join(distFolder, "%s.html" % className), highlightedCode[className]) except TypeError as writeError: error("Could not write highlighted code of: %s: %s", className, writeError) continue info("Writing index...") writeFile(os.path.join(distFolder, "meta-index.%s" % extension), encode(index, "meta-index")) writeFile(os.path.join(distFolder, "meta-search.%s" % extension), encode(search, "meta-search"))