UNPKG

maestro-roku-bsc-plugin

Version:

Visual studio plugin for maestro brightscript development

370 lines 16.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BindingProcessor = void 0; const brighterscript_1 = require("brighterscript"); const undent_1 = require("undent"); const FileType_1 = require("../files/FileType"); const Diagnostics_1 = require("../utils/Diagnostics"); const Utils_1 = require("../utils/Utils"); const BindingType_1 = require("./BindingType"); const XMLTag_1 = require("./XMLTag"); const RawCodeStatement_1 = require("../utils/RawCodeStatement"); const SGTypes_1 = require("brighterscript/dist/parser/SGTypes"); const Utils_2 = require("../Utils"); // eslint-disable-next-line const fsExtra = require("fs-extra"); const BrsTranspileState_1 = require("brighterscript/dist/parser/BrsTranspileState"); const source_map_1 = require("source-map"); class BindingProcessor { constructor(fileMap, fileFactory, config) { this.fileMap = fileMap; this.fileFactory = fileFactory; this.config = config; } generateCodeForXMLFile(file, program, entry) { if (!file || (file.fileType !== FileType_1.FileType.Xml)) { throw new Error('was given a non-xml file'); } if (!file.associatedFile && file.vmClassName) { if (this.config.mvvm.createCodeBehindFilesWhenNeeded) { if (entry) { let vmFile = this.fileMap.getFileForClass(file.vmClassName); if (vmFile) { let xmlFile = file.bscFile; // eslint-disable-next-line @typescript-eslint/dot-notation let dg = program['dependencyGraph']; dg.addDependency(xmlFile.dependencyGraphKey, vmFile.bscFile.dependencyGraphKey); xmlFile.ast.component.scripts.push(this.createSGScript(xmlFile.pkgPath.replace('.xml', '.brs'))); fsExtra.outputFileSync(entry.outputPath.replace('.xml', '.brs'), this.getCodeBehindText(file, vmFile.bscFile)); } else { console.error('missing vm file ' + file.vmClassName); } } else { console.error('cannot generated codebehind file transpile without entry'); } } } else { file.bscFile.parser.invalidateReferences(); this.addFindNodeVarsMethodForFile(file); if (this.config.mvvm.callCreateNodeVarsInInit) { this.addInitCreateNodeVarsCall(file.associatedFile.bscFile); } if (this.config.mvvm.insertCreateVMMethod) { this.addVMConstructor(file); } if (file.bindings.length > 0) { this.addBindingMethodsForFile(file); } file.associatedFile.bscFile.parser.invalidateReferences(); } } getCodeBehindText(file, brsFile) { let text = (0, undent_1.default) ` 'generated by maestro-bsc-plugin function init() m_createNodeVars() end function ${this.getNodeVarMethodText(file)} function m_createVM() m.vm = ${file.vmClassName.replace(/\./gim, '_')}() m.vm.initialize() mx_initializeBindings() end function `; if (file.bindings.length > 0) { text += this.getBindingMethodsText(file, brsFile); } return text; } getBindingMethodsText(file, brsFile) { let bindings = file.bindings.concat(file.getAllParentBindings()); if (bindings.length > 0) { //TODO convert to pure AST let bindingInitStatement = this.getBindingInitMethod(bindings.filter((b) => b.properties.type !== BindingType_1.BindingType.static && b.properties.type !== BindingType_1.BindingType.code), file.bscFile); let staticBindingStatement = this.getStaticBindingsMethod(bindings.filter((b) => b.properties.type === BindingType_1.BindingType.static || b.properties.type === BindingType_1.BindingType.code), file.bscFile); let text = ''; let state = new BrsTranspileState_1.BrsTranspileState(brsFile); text += new source_map_1.SourceNode(null, null, state.srcPath, bindingInitStatement.transpile(state)).toString() + '\n'; text += new source_map_1.SourceNode(null, null, state.srcPath, staticBindingStatement.transpile(state)).toString() + '\n'; return text; } } createSGScript(uri) { return new SGTypes_1.SGScript({ text: 'script' }, [(0, brighterscript_1.createSGAttribute)('uri', `pkg:/${uri}`)], { text: '' }); } /** * given a file, will load it's xml, identify bindings and clear out binding text. * @param file - file to parse bindings for */ parseBindings(file) { var _a; if (!file || file.fileType !== FileType_1.FileType.Xml) { throw new Error('was given a non-xml file'); } file.resetBindings(); let xmlFile = file.bscFile; //we have to reparse the xml each time we do this.. xmlFile.parse(file.bscFile.fileContents); // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression (_a = xmlFile.ast.component) === null || _a === void 0 ? void 0 : _a.setAttribute('vm', undefined); xmlFile.needsTranspiled = true; file.bindings = this.processElements(file); } addNodeVarsMethodForRegularXMLFile(file) { if (!file || file.fileType !== FileType_1.FileType.Xml) { throw new Error('was given a non-xml file'); } if (file.associatedFile) { file.resetBindings(); //we have to reparse the xml each time we do this.. file.bscFile.parse(file.bscFile.fileContents); this.processElementsForTagIds(file); if (file.tagIds.size > 0) { this.addFindNodeVarsMethodForFile(file); if (this.config.mvvm.callCreateNodeVarsInInit) { this.addInitCreateNodeVarsCall(file.associatedFile.bscFile); } } } } processElementsForTagIds(file) { let xmlFile = file.bscFile; file.componentTag = xmlFile.ast.component; for (let sgNode of this.getAllChildren(file.componentTag)) { let id = sgNode.getAttributeValue('id'); if (id) { file.tagIds.add(id); } } // console.log('got tagids', file.tagIds); } addInitCreateNodeVarsCall(file) { let initFunc = file.parser.references.functionStatements.find((f) => f.name.text.toLowerCase() === 'init'); if (initFunc) { initFunc.func.body.statements.splice(0, 0, new RawCodeStatement_1.RawCodeStatement((0, undent_1.default) ` m_createNodeVars() `)); } if (!initFunc && this.config.mvvm.callCreateNodeVarsInInit) { console.log('init func was not present in ', file.pkgPath, ' adding init function'); let initFunc = (0, Utils_1.makeASTFunction)((0, undent_1.default) ` function init() m_createNodeVars() end function `); file.parser.references.functionStatements.push(initFunc); file.parser.references.functionStatementLookup.set('init', initFunc); file.parser.ast.statements.push(initFunc); } } getAllChildren(component) { let result = []; this.getNodeChildren(component.children, result); return result; } getNodeChildren(node, results = []) { if (node) { results.push(node); if (node.children) { for (let child of node.children) { this.getNodeChildren(child, results); } } } } processElements(file) { let xmlFile = file.bscFile; file.componentTag = xmlFile.ast.component; const allTags = this.getAllChildren(file.componentTag).map((c) => new XMLTag_1.XMLTag(c, file, false)); let interfaceFields = file.componentTag.api.fields.map((c) => new XMLTag_1.XMLTag(c, file, true)); allTags.push(...interfaceFields); for (let tag of allTags) { if (tag.id) { (tag.isTopTag ? file.fieldIds : file.tagIds).add(tag.id); } } let tagsWithBindings = allTags.filter((t) => t.hasBindings); return brighterscript_1.util.flatMap(tagsWithBindings, (t) => t.bindings); } validateBindings(file) { if (!file || (file.fileType !== FileType_1.FileType.Xml)) { throw new Error('was given a non-xml file'); } let errorCount = 0; try { let allParentIds = file.getAllParentTagIds(); let allParentFieldIds = file.getAllParentFieldIds(); for (let id of file.fieldIds) { if (allParentFieldIds.has(id)) { (0, Diagnostics_1.addXmlBindingParentHasDuplicateField)(file, id, 1); errorCount++; } } for (let id of file.tagIds) { if (allParentIds.has(id)) { (0, Diagnostics_1.addXmlBindingParentHasDuplicateField)(file, id, 1); errorCount++; } } } catch (e) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument (0, Diagnostics_1.addXmlBindingErrorValidatingBindings)(file, e.message); errorCount++; } if (file.bindings.length > 0) { // if (!file.associatedFile) { // addXmlBindingNoCodeBehind(file); // } if (!file.vmClassName) { if (errorCount === 0) { (0, Diagnostics_1.addXmlBindingNoVMClassDefined)(file); errorCount++; } } else { file.bindingClass = this.fileMap.allClasses[file.vmClassName]; if (!file.bindingClass) { (0, Diagnostics_1.addXmlBindingVMClassNotFound)(file); errorCount++; } else { for (let binding of file.bindings.filter((b) => b.isValid)) { binding.validateAgainstClass(); errorCount += binding.isValid ? 0 : 1; } let bindingFile = this.fileMap.getFileForClass(file.vmClassName); if (bindingFile) { bindingFile.bindingTargetFiles.add(file.bscFile); } } } } file.isValid = errorCount === 0; } addBindingMethodsForFile(file) { //TODO - use AST for this. let associatedMFile = file.associatedFile.bscFile; let bindings = file.bindings.concat(file.getAllParentBindings()); if (bindings.length > 0) { //TODO convert to pure AST let bindingInitStatement = this.getBindingInitMethod(bindings.filter((b) => b.properties.type !== BindingType_1.BindingType.static && b.properties.type !== BindingType_1.BindingType.code), file.bscFile); let staticBindingStatement = this.getStaticBindingsMethod(bindings.filter((b) => b.properties.type === BindingType_1.BindingType.static || b.properties.type === BindingType_1.BindingType.code), file.bscFile); if (bindingInitStatement) { associatedMFile.parser.statements.push(bindingInitStatement); file.associatedFile.isASTChanged = true; } if (staticBindingStatement) { associatedMFile.parser.statements.push(staticBindingStatement); file.associatedFile.isASTChanged = true; } } } makeASTFunction(source) { let tokens = brighterscript_1.Lexer.scan(source).tokens; let { statements } = brighterscript_1.Parser.parse(tokens, { mode: brighterscript_1.ParseMode.BrighterScript }); if (statements && statements.length > 0) { return statements[0]; } return undefined; } getBindingInitMethod(bindings, file) { let func = (0, Utils_1.makeASTFunction)((0, undent_1.default) ` function m_initBindings() if m.vm <> invalid vm = m.vm end if end function `); if (func) { let ifStatement = func.func.body.statements[0]; for (let binding of bindings) { ifStatement.thenBranch.statements.push(new RawCodeStatement_1.RawCodeStatement(binding.getInitText(), file, binding.range)); } ifStatement.thenBranch.statements.push(new RawCodeStatement_1.RawCodeStatement((0, undent_1.default) ` if vm.onBindingsConfigured <> invalid vm.onBindingsConfigured() end if `)); } return func; } getStaticBindingsMethod(bindings, file) { let func = (0, Utils_1.makeASTFunction)((0, undent_1.default) ` function m_initStaticBindings() if m.vm <> invalid vm = m.vm end if end function `); if (func) { let ifStatement = func.func.body.statements[0]; for (let binding of bindings) { ifStatement.thenBranch.statements.push(new RawCodeStatement_1.RawCodeStatement(binding.getStaticText(), file, binding.range)); } } return func; } addFindNodeVarsMethodForFile(file) { var _a, _b; let createNodeVarsFunction = this.makeASTFunction(this.getNodeVarMethodText(file)); let brsFile = file.associatedFile.bscFile; if (createNodeVarsFunction && ((_b = (_a = file.associatedFile) === null || _a === void 0 ? void 0 : _a.bscFile) === null || _b === void 0 ? void 0 : _b.parser)) { brsFile.parser.statements.push(createNodeVarsFunction); file.associatedFile.isASTChanged = true; } } getNodeVarMethodText(file) { let tagIds = Array.from(file.getAllParentTagIds().values()).concat(Array.from(file.tagIds.values())); if (tagIds.length > 0) { return (0, undent_1.default) ` function m_createNodeVars() for each id in [ ${tagIds.map((id) => `"${id}"`).join(',')}] m[id] = m.top.findNode(id) end for end function `; } else { return (0, undent_1.default) ` function m_createNodeVars() end function `; } } addVMConstructor(file) { console.log('addVM ', file.fullPath, file.bscFile === undefined); console.log('no initialize function, adding one'); let func = (0, Utils_1.makeASTFunction)(this.getVMInitializeText(file)); if (func) { let vmFile = this.fileMap.getFileForClass(file.vmClassName); if (vmFile) { (0, Utils_2.addImport)(file.associatedFile.bscFile, vmFile.bscFile.pkgPath); file.associatedFile.bscFile.parser.statements.push(func); file.associatedFile.isASTChanged = true; } else { console.error(`file for vm class ${file.vmClassName} was not found!`); } } } getVMInitializeText(file) { return (0, undent_1.default) ` function m_createVM() m.vm = new ${file.vmClassName}() m.vm.initialize() mx_initializeBindings() end function `; } } exports.BindingProcessor = BindingProcessor; //# sourceMappingURL=BindingProcessor.js.map