maestro-cli-roku
Version:
command line tools for maestro-roku projects
288 lines (287 loc) • 13.9 kB
JavaScript
'use strict';
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
var FileType_1 = require("../fileProcessing/FileType");
var ProcessorSettings_1 = require("../fileProcessing/ProcessorSettings");
var ChangeExtension_1 = require("../utils/ChangeExtension");
var Feedback_1 = require("../utils/Feedback");
var Utils_1 = require("../utils/Utils");
/**
* Manages importing includes for a given brs file
*/
var ImportProcessor = /** @class */ (function () {
function ImportProcessor(projectProcessor) {
this.importRegex = new RegExp("^(?: *|\\t*)import\\s*\\\"(.*)\\\"", "gim");
this.importTemplate = "<script type=\"text/brightscript\" uri=\"$PATH$\" />";
this.endOfXmlFileRegex = new RegExp("</component>", "gim");
this.settings = new ProcessorSettings_1.ProcessorSettings();
this.config = projectProcessor.config;
this.fileMap = projectProcessor.fileMap;
this.projectProcessor = projectProcessor;
}
ImportProcessor.prototype.addImportsToXmlFile = function (file) {
if (!file || file.fileType !== FileType_1.FileType.Xml && file.fileType !== FileType_1.FileType.ViewXml) {
throw new Error('was given a non-xml file');
}
if (!file.hasProcessedImports) {
if (file.parentFile && !file.parentFile.hasProcessedImports) {
//we must process and update the parent first, otherwise we can't filter the imports
this.addImportsToXmlFile(file.parentFile);
}
this.identifyImports(file);
this.addImportCodeToFile(file);
file.hasProcessedImports = true;
}
};
ImportProcessor.prototype.parseFileImports = function (file) {
Utils_1.addSetItems(file.importedPaths, this.getFileImports(file, Utils_1.getRegexMatchesValues(file.getFileContents(), this.importRegex, 1)));
};
ImportProcessor.prototype.getFileImports = function (file, paths) {
var _this = this;
var importPaths = [];
var filePath = file.pkgPath.toLowerCase();
paths.forEach(function (path) {
if (path.toLowerCase().indexOf('build:/') === 0) {
_this.addBuildImportPaths(file, filePath, path, importPaths);
}
else {
_this.addImportPath(filePath, path.toLowerCase(), importPaths);
}
});
return importPaths;
};
ImportProcessor.prototype.addBuildImportPaths = function (file, filePath, buildKey, target) {
var _this = this;
var imports = this.config.buildTimeImports ? this.config.buildTimeImports[buildKey.substr(7)] : null;
if (imports) {
imports.forEach(function (p) { return _this.addImportPath(filePath, p.toLowerCase(), target); });
}
else {
Feedback_1.feedbackError(file, "xml file imports a build time import key that was not defined in your config:file that cannot be found " + buildKey, true);
}
};
ImportProcessor.prototype.addImportPath = function (filePath, path, target) {
var normalizedPath = Utils_1.getPkgPathFromTarget(filePath, path);
if (normalizedPath) {
//.bs imports become .brs imports, as they will be compiled prior to resolution
if (normalizedPath.toLowerCase().endsWith('.bs')) {
normalizedPath = ChangeExtension_1.changeExtension(normalizedPath, '.brs');
}
target.push(normalizedPath);
}
};
ImportProcessor.prototype.identifyImports = function (file) {
var e_1, _a, e_2, _b, e_3, _c;
if (!file || file.fileType !== FileType_1.FileType.Xml && file.fileType !== FileType_1.FileType.ViewXml) {
throw new Error('was given a non-xml file');
}
var rootImportPaths = new Set();
//1 add codebehind imports
if (file.associatedFile) {
Utils_1.addSetItems(rootImportPaths, file.associatedFile.importedPaths);
}
//2 add imported paths from all XML imported scripts
var xmlFile = file.programFile;
try {
for (var _d = __values(xmlFile.getOwnScriptImports()), _e = _d.next(); !_e.done; _e = _d.next()) {
var fileReference = _e.value;
var importedFile = this.fileMap.getFileByPkgPath(fileReference.pkgPath);
if (!importedFile) {
//theres's a bug in brs that can sometimes set the absolute path to pkg path
importedFile = this.fileMap.getFile(fileReference.pkgPath);
}
if (!importedFile) {
if (this.isImportSkipped(fileReference.pkgPath)) {
file.importedPaths.add(fileReference.pkgPath);
Feedback_1.feedbackWarning(file, "xml file imports nonchecked file that is not available: " + fileReference.pkgPath, false, 'import');
continue;
}
else {
Feedback_1.feedbackError(file, "xml file imports a file that cannot be found " + fileReference.pkgPath);
continue;
}
}
else if (importedFile === file.associatedFile) {
continue;
}
Utils_1.addSetItems(rootImportPaths, importedFile.importedPaths);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
}
finally { if (e_1) throw e_1.error; }
}
//3 identify if the file is using bindings
if (file.bindings.length > 0) {
rootImportPaths.add('source/maestro/observable/ObservableMixin.brs');
rootImportPaths.add('source/maestro/observable/BaseObservable.brs');
}
if (this.config.createRuntimeFiles) {
rootImportPaths.add('source/MRuntime.brs');
}
//5 get all nested dependencies
var allImportedPaths = new Set();
try {
for (var rootImportPaths_1 = __values(rootImportPaths), rootImportPaths_1_1 = rootImportPaths_1.next(); !rootImportPaths_1_1.done; rootImportPaths_1_1 = rootImportPaths_1.next()) {
var importPath = rootImportPaths_1_1.value;
this.addNestedImportPaths(file, importPath, allImportedPaths);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (rootImportPaths_1_1 && !rootImportPaths_1_1.done && (_b = rootImportPaths_1.return)) _b.call(rootImportPaths_1);
}
finally { if (e_2) throw e_2.error; }
}
//turn all imports into files
var requiredFiles = [];
var importedFiles = [];
var parentImportPaths = file.getAllParentImportPathsAsSet();
try {
for (var allImportedPaths_1 = __values(allImportedPaths), allImportedPaths_1_1 = allImportedPaths_1.next(); !allImportedPaths_1_1.done; allImportedPaths_1_1 = allImportedPaths_1.next()) {
var path = allImportedPaths_1_1.value;
var importedFile = this.fileMap.getFileByPkgPath(path);
if (importedFile) {
requiredFiles.push(importedFile);
if (!parentImportPaths.has(path.toLowerCase())) {
importedFiles.push(importedFile);
}
}
else if (!this.isImportSkipped(path)) {
this.failWithMissingImport(file, path);
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (allImportedPaths_1_1 && !allImportedPaths_1_1.done && (_c = allImportedPaths_1.return)) _c.call(allImportedPaths_1);
}
finally { if (e_3) throw e_3.error; }
}
file.importedFiles = importedFiles;
file.requiredFiles = requiredFiles;
};
ImportProcessor.prototype.addNestedImportPaths = function (sourceFile, parentPkgPath, importPaths, parentSet) {
var e_4, _a;
if (parentSet === void 0) { parentSet = null; }
var parentFile = this.fileMap.getFileByPkgPath(parentPkgPath);
if (!parentFile) {
if (this.isImportSkipped(parentPkgPath)) {
Feedback_1.feedbackWarning(sourceFile, "xml file imports nonchecked file that is not available: " + parentPkgPath, false, 'import');
importPaths.add(parentPkgPath);
return;
}
else {
this.failWithMissingImport(sourceFile, parentPkgPath);
return;
}
}
if (!parentSet) {
parentSet = new Set([parentPkgPath]);
}
var file = parentFile;
try {
for (var _b = __values(file.importedPaths), _c = _b.next(); !_c.done; _c = _b.next()) {
var importPath = _c.value;
if (!importPaths.has(importPath)) {
var importFile = this.fileMap.getFileByPkgPath(importPath);
if (!importFile) {
if (this.isImportSkipped(importPath)) {
Feedback_1.feedbackWarning(file, "xml file imports nonchecked file that is not available: " + importPath, false, 'import');
break;
}
else {
this.failWithMissingImport(file, importPath);
return;
}
}
if (parentSet.has(importPath)) {
this.failWithCyclicalImport(file, parentPkgPath, importPath);
}
var importStack = new Set(parentSet);
importStack.add(importPath);
this.addNestedImportPaths(file, importPath, importPaths, importStack);
}
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_4) throw e_4.error; }
}
importPaths.add(parentPkgPath);
};
ImportProcessor.prototype.failWithMissingImport = function (file, path) {
Feedback_1.feedbackError(file, "Missing import - could not find a file at path " + path);
};
ImportProcessor.prototype.failWithCyclicalImport = function (file, sourceImportPath, path) {
Feedback_1.feedbackError(file, "Cyclical import detected - an infinite import cycle\n was found on " + path + " when processing import for " + sourceImportPath, true);
};
/**
* Responsible for updating the codebehind and xml files with the required imports
*/
ImportProcessor.prototype.addImportCodeToFile = function (file) {
//for viewxml/codebehind files, we add the imports to xml - for pure brs files that's a moot point
if (file.fileType === FileType_1.FileType.Xml || file.fileType === FileType_1.FileType.ViewXml) {
this.addImportIncludesToXML(file);
}
};
ImportProcessor.prototype.addImportIncludesToXML = function (file) {
var _this = this;
if (file.fileType !== FileType_1.FileType.Xml && file.fileType !== FileType_1.FileType.ViewXml) {
Feedback_1.feedbackError(file, "Was passed a non xml file", true);
}
var imports = "";
var cwd = process.cwd();
file.importedFiles.forEach(function (file) {
imports += "\n" + _this.importTemplate.replace("$PATH$", file.pkgUri);
});
//if the codebehind is not imported yet, import it
var xmlFile = file.programFile;
if (file.associatedFile) {
var codeBehindPkgPath_1 = file.associatedFile.pkgPath.toLowerCase();
var ownScripts = xmlFile.getOwnScriptImports();
if (!ownScripts.find(function (i) { return i.pkgPath.toLowerCase() === codeBehindPkgPath_1; })) {
imports += "\n" + this.importTemplate.replace("$PATH$", file.associatedFile.pkgUri);
}
}
//we place imports at the end of the file to ensure we don't screw up error line number reporting
var xmlContents = file.getFileContents();
this.endOfXmlFileRegex.lastIndex = 0;
var result = this.endOfXmlFileRegex.exec(xmlContents);
if (imports.length > 0) {
if (result) {
var insertionIndex = result.index; //TODO - get the end tag location, and put it on the line before
xmlContents = Utils_1.spliceString(xmlContents, insertionIndex, imports);
xmlContents += '\n</component>';
file.setFileContents(xmlContents);
file.saveFileContents();
}
else {
Feedback_1.feedbackError(file, "xml file did not have end component tag");
}
}
};
ImportProcessor.prototype.isImportSkipped = function (pkgPath) {
return this.config.nonCheckedImports ? this.config.nonCheckedImports.some(function (i) { return i.toLowerCase().endsWith(pkgPath.toLowerCase()); }) : false;
};
return ImportProcessor;
}());
exports.default = ImportProcessor;