brighterscript 0.41.2 → 1.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +2 -2
  3. package/dist/DiagnosticCollection.js +2 -2
  4. package/dist/DiagnosticCollection.js.map +1 -1
  5. package/dist/DiagnosticFilterer.js +2 -2
  6. package/dist/DiagnosticFilterer.js.map +1 -1
  7. package/dist/DiagnosticMessages.d.ts +6 -1
  8. package/dist/DiagnosticMessages.js +5 -0
  9. package/dist/DiagnosticMessages.js.map +1 -1
  10. package/dist/LanguageServer.d.ts +8 -2
  11. package/dist/LanguageServer.js +50 -44
  12. package/dist/LanguageServer.js.map +1 -1
  13. package/dist/Program.d.ts +61 -45
  14. package/dist/Program.js +305 -188
  15. package/dist/Program.js.map +1 -1
  16. package/dist/ProgramBuilder.d.ts +7 -7
  17. package/dist/ProgramBuilder.js +60 -51
  18. package/dist/ProgramBuilder.js.map +1 -1
  19. package/dist/Scope.d.ts +42 -19
  20. package/dist/Scope.js +261 -129
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/SymbolTable.d.ts +73 -0
  23. package/dist/SymbolTable.js +157 -0
  24. package/dist/SymbolTable.js.map +1 -0
  25. package/dist/XmlScope.d.ts +5 -0
  26. package/dist/XmlScope.js +66 -28
  27. package/dist/XmlScope.js.map +1 -1
  28. package/dist/astUtils/creators.d.ts +15 -1
  29. package/dist/astUtils/creators.js +39 -9
  30. package/dist/astUtils/creators.js.map +1 -1
  31. package/dist/astUtils/reflection.d.ts +28 -16
  32. package/dist/astUtils/reflection.js +52 -30
  33. package/dist/astUtils/reflection.js.map +1 -1
  34. package/dist/astUtils/reflection.spec.js +3 -3
  35. package/dist/astUtils/reflection.spec.js.map +1 -1
  36. package/dist/astUtils/visitors.spec.js +12 -13
  37. package/dist/astUtils/visitors.spec.js.map +1 -1
  38. package/dist/astUtils/xml.d.ts +3 -3
  39. package/dist/astUtils/xml.js +2 -2
  40. package/dist/astUtils/xml.js.map +1 -1
  41. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +5 -6
  42. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  43. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +24 -22
  44. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  45. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js +2 -2
  46. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +1 -1
  47. package/dist/examples/plugins/removePrint.js +1 -1
  48. package/dist/examples/plugins/removePrint.js.map +1 -1
  49. package/dist/files/BrsFile.Class.spec.js +356 -41
  50. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  51. package/dist/files/BrsFile.d.ts +55 -37
  52. package/dist/files/BrsFile.js +430 -399
  53. package/dist/files/BrsFile.js.map +1 -1
  54. package/dist/files/BrsFile.spec.js +199 -158
  55. package/dist/files/BrsFile.spec.js.map +1 -1
  56. package/dist/files/XmlFile.d.ts +20 -9
  57. package/dist/files/XmlFile.js +36 -31
  58. package/dist/files/XmlFile.js.map +1 -1
  59. package/dist/files/XmlFile.spec.js +113 -113
  60. package/dist/files/XmlFile.spec.js.map +1 -1
  61. package/dist/files/tests/imports.spec.js +32 -32
  62. package/dist/files/tests/imports.spec.js.map +1 -1
  63. package/dist/globalCallables.js +17 -6
  64. package/dist/globalCallables.js.map +1 -1
  65. package/dist/interfaces.d.ts +155 -39
  66. package/dist/parser/BrsTranspileState.d.ts +7 -0
  67. package/dist/parser/BrsTranspileState.js +10 -1
  68. package/dist/parser/BrsTranspileState.js.map +1 -1
  69. package/dist/parser/Expression.d.ts +23 -12
  70. package/dist/parser/Expression.js +45 -30
  71. package/dist/parser/Expression.js.map +1 -1
  72. package/dist/parser/Parser.Class.spec.js +100 -1
  73. package/dist/parser/Parser.Class.spec.js.map +1 -1
  74. package/dist/parser/Parser.d.ts +118 -5
  75. package/dist/parser/Parser.js +398 -37
  76. package/dist/parser/Parser.js.map +1 -1
  77. package/dist/parser/Parser.spec.js +404 -7
  78. package/dist/parser/Parser.spec.js.map +1 -1
  79. package/dist/parser/SGParser.d.ts +41 -4
  80. package/dist/parser/SGParser.js +185 -174
  81. package/dist/parser/SGParser.js.map +1 -1
  82. package/dist/parser/SGParser.spec.js +17 -4
  83. package/dist/parser/SGParser.spec.js.map +1 -1
  84. package/dist/parser/SGTypes.d.ts +203 -38
  85. package/dist/parser/SGTypes.js +464 -160
  86. package/dist/parser/SGTypes.js.map +1 -1
  87. package/dist/parser/SGTypes.spec.d.ts +1 -0
  88. package/dist/parser/SGTypes.spec.js +351 -0
  89. package/dist/parser/SGTypes.spec.js.map +1 -0
  90. package/dist/parser/Statement.d.ts +37 -26
  91. package/dist/parser/Statement.js +81 -20
  92. package/dist/parser/Statement.js.map +1 -1
  93. package/dist/parser/Statement.spec.js +5 -5
  94. package/dist/parser/Statement.spec.js.map +1 -1
  95. package/dist/parser/TranspileState.d.ts +1 -1
  96. package/dist/parser/TranspileState.js +15 -7
  97. package/dist/parser/TranspileState.js.map +1 -1
  98. package/dist/parser/tests/controlFlow/ForEach.spec.js +5 -4
  99. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  100. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +1 -1
  101. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  102. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +1 -1
  103. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  104. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +1 -1
  105. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  106. package/dist/parser/tests/expression/TernaryExpression.spec.js +1 -1
  107. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  108. package/dist/types/ArrayType.d.ts +1 -0
  109. package/dist/types/ArrayType.js +23 -19
  110. package/dist/types/ArrayType.js.map +1 -1
  111. package/dist/types/BooleanType.d.ts +3 -2
  112. package/dist/types/BooleanType.js +6 -3
  113. package/dist/types/BooleanType.js.map +1 -1
  114. package/dist/types/BscType.d.ts +19 -4
  115. package/dist/types/BscType.js +9 -0
  116. package/dist/types/BscType.js.map +1 -1
  117. package/dist/types/CustomType.d.ts +8 -5
  118. package/dist/types/CustomType.js +12 -12
  119. package/dist/types/CustomType.js.map +1 -1
  120. package/dist/types/DoubleType.d.ts +1 -0
  121. package/dist/types/DoubleType.js +11 -11
  122. package/dist/types/DoubleType.js.map +1 -1
  123. package/dist/types/DynamicType.d.ts +1 -0
  124. package/dist/types/DynamicType.js +4 -0
  125. package/dist/types/DynamicType.js.map +1 -1
  126. package/dist/types/FloatType.d.ts +2 -1
  127. package/dist/types/FloatType.js +11 -11
  128. package/dist/types/FloatType.js.map +1 -1
  129. package/dist/types/FunctionType.d.ts +13 -10
  130. package/dist/types/FunctionType.js +34 -18
  131. package/dist/types/FunctionType.js.map +1 -1
  132. package/dist/types/FunctionType.spec.js +8 -2
  133. package/dist/types/FunctionType.spec.js.map +1 -1
  134. package/dist/types/IntegerType.d.ts +2 -1
  135. package/dist/types/IntegerType.js +11 -11
  136. package/dist/types/IntegerType.js.map +1 -1
  137. package/dist/types/InvalidType.d.ts +3 -2
  138. package/dist/types/InvalidType.js +7 -4
  139. package/dist/types/InvalidType.js.map +1 -1
  140. package/dist/types/LazyType.d.ts +17 -0
  141. package/dist/types/LazyType.js +44 -0
  142. package/dist/types/LazyType.js.map +1 -0
  143. package/dist/types/LongIntegerType.d.ts +2 -1
  144. package/dist/types/LongIntegerType.js +11 -11
  145. package/dist/types/LongIntegerType.js.map +1 -1
  146. package/dist/types/ObjectType.d.ts +6 -2
  147. package/dist/types/ObjectType.js +9 -3
  148. package/dist/types/ObjectType.js.map +1 -1
  149. package/dist/types/StringType.d.ts +3 -2
  150. package/dist/types/StringType.js +6 -3
  151. package/dist/types/StringType.js.map +1 -1
  152. package/dist/types/UninitializedType.d.ts +4 -2
  153. package/dist/types/UninitializedType.js +8 -3
  154. package/dist/types/UninitializedType.js.map +1 -1
  155. package/dist/types/VoidType.d.ts +3 -2
  156. package/dist/types/VoidType.js +6 -3
  157. package/dist/types/VoidType.js.map +1 -1
  158. package/dist/types/helpers.d.ts +42 -0
  159. package/dist/types/helpers.js +113 -0
  160. package/dist/types/helpers.js.map +1 -0
  161. package/dist/util.d.ts +68 -15
  162. package/dist/util.js +193 -45
  163. package/dist/util.js.map +1 -1
  164. package/dist/validators/ClassValidator.d.ts +5 -1
  165. package/dist/validators/ClassValidator.js +31 -17
  166. package/dist/validators/ClassValidator.js.map +1 -1
  167. package/package.json +1 -1
  168. package/dist/FunctionScope.d.ts +0 -27
  169. package/dist/FunctionScope.js +0 -49
  170. package/dist/FunctionScope.js.map +0 -1
@@ -1,150 +1,291 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SGAst = exports.SGComponent = exports.SGInterface = exports.SGFunction = exports.SGFieldTypes = exports.SGField = exports.SGScript = exports.SGChildren = exports.SGNode = exports.SGProlog = exports.SGTag = void 0;
3
+ exports.SGAst = exports.SGComponent = exports.SGInterface = exports.SGInterfaceFunction = exports.getBscTypeFromSGFieldType = exports.SGFieldTypes = exports.SGInterfaceField = exports.SGScript = exports.SGChildren = exports.SGNode = exports.SGProlog = exports.SGTag = exports.SGAttribute = void 0;
4
4
  const source_map_1 = require("source-map");
5
+ const astUtils_1 = require("../astUtils");
5
6
  const creators_1 = require("../astUtils/creators");
6
- const xml_1 = require("../astUtils/xml");
7
+ const BooleanType_1 = require("../types/BooleanType");
8
+ const DynamicType_1 = require("../types/DynamicType");
9
+ const FloatType_1 = require("../types/FloatType");
10
+ const FunctionType_1 = require("../types/FunctionType");
11
+ const IntegerType_1 = require("../types/IntegerType");
12
+ const LongIntegerType_1 = require("../types/LongIntegerType");
13
+ const StringType_1 = require("../types/StringType");
7
14
  const util_1 = require("../util");
15
+ class SGAttribute {
16
+ constructor(key, equals, openingQuote, value, closingQuote) {
17
+ this.tokens = {};
18
+ this._range = null;
19
+ this.tokens.key = key;
20
+ this.tokens.equals = equals;
21
+ this.tokens.openingQuote = openingQuote;
22
+ this.tokens.value = value;
23
+ this.tokens.closingQuote = closingQuote;
24
+ }
25
+ get key() {
26
+ return this.tokens.key.text;
27
+ }
28
+ /**
29
+ * The value of this attribute. This does not including the opening or closing quote
30
+ */
31
+ get value() {
32
+ var _a;
33
+ return (_a = this.tokens.value) === null || _a === void 0 ? void 0 : _a.text;
34
+ }
35
+ set value(val) {
36
+ if (val === null || val === undefined) {
37
+ val = '';
38
+ }
39
+ if (!this.tokens.equals) {
40
+ this.tokens.equals = { text: '=' };
41
+ }
42
+ if (this.tokens.value) {
43
+ this.tokens.value.text = val;
44
+ }
45
+ else {
46
+ this.tokens.value = { text: val };
47
+ }
48
+ }
49
+ get range() {
50
+ if (!this._range) {
51
+ this._range = util_1.default.createBoundingRange(this.tokens.key, this.tokens.equals, this.tokens.openingQuote, this.tokens.value, this.tokens.closingQuote);
52
+ }
53
+ return this._range;
54
+ }
55
+ transpile(state) {
56
+ const result = [
57
+ state.transpileToken(this.tokens.key)
58
+ ];
59
+ if (this.tokens.value) {
60
+ result.push(state.transpileToken(this.tokens.equals, '='), state.transpileToken(this.tokens.openingQuote, '"'), state.transpileToken(this.tokens.value), state.transpileToken(this.tokens.closingQuote, '"'));
61
+ }
62
+ return new source_map_1.SourceNode(null, null, null, result);
63
+ }
64
+ }
65
+ exports.SGAttribute = SGAttribute;
8
66
  class SGTag {
9
- constructor(tag, attributes = [], range) {
10
- this.tag = tag;
67
+ constructor(startTagOpen, startTagName, attributes = [], startTagClose, childNodes = [], endTagOpen, endTagName, endTagClose) {
68
+ this.tokens = {};
69
+ /**
70
+ * Array of attributes found on this tag
71
+ */
72
+ this.attributes = [];
73
+ /**
74
+ * The array of direct children AST elements of this AST node
75
+ */
76
+ this.childNodes = [];
77
+ this._range = null;
78
+ this.tokens.startTagOpen = startTagOpen;
79
+ this.tokens.startTagName = startTagName;
11
80
  this.attributes = attributes;
12
- this.range = range;
81
+ this.tokens.startTagClose = startTagClose;
82
+ this.childNodes = childNodes;
83
+ this.tokens.endTagOpen = endTagOpen;
84
+ this.tokens.endTagName = endTagName;
85
+ this.tokens.endTagClose = endTagClose;
86
+ }
87
+ get range() {
88
+ var _a, _b, _c, _d;
89
+ if (!this._range) {
90
+ this._range = util_1.default.createBoundingRange(this.tokens.startTagOpen, this.tokens.startTagName, (_a = this.attributes) === null || _a === void 0 ? void 0 : _a[((_b = this.attributes) === null || _b === void 0 ? void 0 : _b.length) - 1], this.tokens.startTagClose, (_c = this.childNodes) === null || _c === void 0 ? void 0 : _c[((_d = this.childNodes) === null || _d === void 0 ? void 0 : _d.length) - 1], this.tokens.endTagOpen, this.tokens.endTagName, this.tokens.endTagClose);
91
+ }
92
+ return this._range;
93
+ }
94
+ /**
95
+ * Is this a self-closing tag?
96
+ */
97
+ get isSelfClosing() {
98
+ return !this.tokens.endTagName;
13
99
  }
14
100
  get id() {
15
101
  return this.getAttributeValue('id');
16
102
  }
17
103
  set id(value) {
18
- this.setAttribute('id', value);
104
+ this.setAttributeValue('id', value);
105
+ }
106
+ /**
107
+ * Get the lower-case name of this tag.
108
+ */
109
+ get tagName() {
110
+ var _a;
111
+ return (_a = this.tokens.startTagName) === null || _a === void 0 ? void 0 : _a.text;
112
+ }
113
+ /**
114
+ * Find all direct children by their tag name (case insensitive).
115
+ * This does not step into children's children.
116
+ *
117
+ */
118
+ getChildNodesByTagName(tagName) {
119
+ const result = [];
120
+ const lowerTagName = tagName.toLowerCase();
121
+ for (const el of this.childNodes) {
122
+ if (el.tokens.startTagName.text.toLowerCase() === lowerTagName) {
123
+ result.push(el);
124
+ }
125
+ }
126
+ return result;
19
127
  }
128
+ /**
129
+ * Add a child to the end of the children array
130
+ */
131
+ addChild(tag) {
132
+ this.childNodes.push(tag);
133
+ return tag;
134
+ }
135
+ /**
136
+ * Remove a child from the children array.
137
+ * @returns true if node was found and removed, false if the node wasn't there and thus nothing was done
138
+ */
139
+ removeChild(tag) {
140
+ const idx = this.childNodes.indexOf(tag);
141
+ if (idx > -1) {
142
+ this.childNodes.splice(idx, 1);
143
+ return true;
144
+ }
145
+ return false;
146
+ }
147
+ /**
148
+ * Does this node have the specified attribute?
149
+ */
150
+ hasAttribute(name) {
151
+ return !!this.getAttribute(name);
152
+ }
153
+ /**
154
+ * Get an SGAttribute by its name
155
+ */
20
156
  getAttribute(name) {
21
- return this.attributes.find(att => att.key.text.toLowerCase() === name);
157
+ var _a;
158
+ const nameLower = name.toLowerCase();
159
+ for (const attr of this.attributes) {
160
+ if (((_a = attr.tokens.key) === null || _a === void 0 ? void 0 : _a.text.toLowerCase()) === nameLower) {
161
+ return attr;
162
+ }
163
+ }
22
164
  }
165
+ /**
166
+ * Get an attribute value by its name
167
+ */
23
168
  getAttributeValue(name) {
24
169
  var _a, _b;
25
- return (_b = (_a = this.getAttribute(name)) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.text;
26
- }
27
- setAttribute(name, value) {
28
- const attr = this.getAttribute(name);
29
- if (attr) {
30
- if (value) {
31
- attr.value = { text: value };
32
- attr.range = undefined;
33
- }
34
- else {
35
- this.attributes.splice(this.attributes.indexOf(attr), 1);
36
- }
170
+ return (_b = (_a = this.getAttribute(name)) === null || _a === void 0 ? void 0 : _a.tokens.value) === null || _b === void 0 ? void 0 : _b.text;
171
+ }
172
+ /**
173
+ * Set an attribute value by its name. If no attribute exists with this name, it is created
174
+ */
175
+ setAttributeValue(name, value) {
176
+ let attr = this.getAttribute(name);
177
+ //create an attribute with this name if we don't have one yet
178
+ if (!attr) {
179
+ attr = (0, creators_1.createSGAttribute)(name, value);
180
+ this.attributes.push(attr);
37
181
  }
38
- else if (value) {
39
- this.attributes.push({
40
- key: { text: name },
41
- value: { text: value }
42
- });
182
+ attr.value = value;
183
+ }
184
+ /**
185
+ * Remove an attribute by its name
186
+ * @returns true if an attribute was found and removed. False if no attribute was found
187
+ */
188
+ removeAttribute(name) {
189
+ var _a;
190
+ const nameLower = name.toLowerCase();
191
+ for (let i = 0; i < this.attributes.length; i++) {
192
+ if (((_a = this.attributes[i].key) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === nameLower) {
193
+ this.attributes.splice(i, 1);
194
+ return true;
195
+ }
43
196
  }
197
+ return false;
44
198
  }
45
199
  transpile(state) {
46
- return new source_map_1.SourceNode(null, null, state.srcPath, [
47
- state.indentText,
48
- '<',
49
- state.transpileToken(this.tag),
50
- ...this.transpileAttributes(state, this.attributes),
51
- ...this.transpileBody(state)
200
+ return new source_map_1.SourceNode(null, null, null, [
201
+ state.transpileToken(this.tokens.startTagOpen, '<'),
202
+ state.transpileToken(this.tokens.startTagName),
203
+ this.transpileAttributes(state, this.attributes),
204
+ this.transpileBody(state)
52
205
  ]);
53
206
  }
54
207
  transpileBody(state) {
55
- return [' />\n'];
208
+ var _a;
209
+ if (this.isSelfClosing) {
210
+ return new source_map_1.SourceNode(null, null, null, [
211
+ ' ',
212
+ state.transpileToken(this.tokens.startTagClose, '/>'),
213
+ state.newline
214
+ ]);
215
+ }
216
+ else {
217
+ const chunks = [
218
+ state.transpileToken(this.tokens.startTagClose, '>'),
219
+ state.newline
220
+ ];
221
+ state.blockDepth++;
222
+ for (const child of this.childNodes) {
223
+ chunks.push(state.indentText, child.transpile(state));
224
+ }
225
+ state.blockDepth--;
226
+ chunks.push(state.indentText, state.transpileToken(this.tokens.endTagOpen, '</'), state.transpileToken((_a = this.tokens.endTagName) !== null && _a !== void 0 ? _a : this.tokens.startTagName), state.transpileToken(this.tokens.endTagClose, '>'), state.newline);
227
+ return new source_map_1.SourceNode(null, null, null, chunks);
228
+ }
56
229
  }
57
230
  transpileAttributes(state, attributes) {
58
- const result = [];
231
+ const chunks = [];
59
232
  for (const attr of attributes) {
60
- result.push(' ', state.transpileToken(attr.key), '="', state.transpileToken(attr.value), '"');
233
+ chunks.push(' ', attr.transpile(state));
61
234
  }
62
- return result;
235
+ return new source_map_1.SourceNode(null, null, null, chunks);
63
236
  }
64
237
  }
65
238
  exports.SGTag = SGTag;
66
239
  class SGProlog extends SGTag {
67
- transpile(state) {
68
- return new source_map_1.SourceNode(null, null, state.srcPath, [
69
- '<?xml',
70
- ...this.transpileAttributes(state, this.attributes),
71
- ' ?>\n'
72
- ]);
73
- }
74
240
  }
75
241
  exports.SGProlog = SGProlog;
76
242
  class SGNode extends SGTag {
77
- constructor(tag, attributes, children = [], range) {
78
- super(tag, attributes, range);
79
- this.children = children;
80
- }
81
- transpileBody(state) {
82
- if (this.children.length > 0) {
83
- const body = ['>\n'];
84
- state.blockDepth++;
85
- body.push(...this.children.map(node => node.transpile(state)));
86
- state.blockDepth--;
87
- body.push(state.indentText, '</', this.tag.text, '>\n');
88
- return body;
89
- }
90
- else {
91
- return super.transpileBody(state);
92
- }
93
- }
94
243
  }
95
244
  exports.SGNode = SGNode;
96
- class SGChildren extends SGNode {
97
- constructor(tag = { text: 'children' }, children = [], range) {
98
- super(tag, [], children, range);
99
- }
245
+ class SGChildren extends SGTag {
100
246
  }
101
247
  exports.SGChildren = SGChildren;
102
248
  class SGScript extends SGTag {
103
- constructor(tag = { text: 'script' }, attributes, cdata, range) {
104
- super(tag, attributes, range);
105
- this.cdata = cdata;
106
- if (!attributes) {
107
- this.type = 'text/brightscript';
108
- }
109
- }
110
249
  get type() {
111
250
  return this.getAttributeValue('type');
112
251
  }
113
252
  set type(value) {
114
- this.setAttribute('type', value);
253
+ this.setAttributeValue('type', value);
115
254
  }
116
255
  get uri() {
117
256
  return this.getAttributeValue('uri');
118
257
  }
119
258
  set uri(value) {
120
- this.setAttribute('uri', value);
259
+ this.setAttributeValue('uri', value);
121
260
  }
122
261
  transpileBody(state) {
123
262
  if (this.cdata) {
124
- return [
263
+ return new source_map_1.SourceNode(null, null, null, [
125
264
  '>',
126
265
  state.transpileToken(this.cdata),
127
266
  '</',
128
- this.tag.text,
129
- '>\n'
130
- ];
267
+ this.tokens.startTagName.text,
268
+ '>',
269
+ state.newline
270
+ ]);
131
271
  }
132
272
  else {
133
273
  return super.transpileBody(state);
134
274
  }
135
275
  }
136
276
  transpileAttributes(state, attributes) {
277
+ var _a, _b, _c;
137
278
  const modifiedAttributes = [];
138
279
  let foundType = false;
139
280
  const bsExtensionRegexp = /\.bs$/i;
140
281
  for (const attr of attributes) {
141
- const lowerKey = attr.key.text.toLowerCase();
142
- if (lowerKey === 'uri' && bsExtensionRegexp.exec(attr.value.text)) {
143
- modifiedAttributes.push(util_1.default.cloneSGAttribute(attr, attr.value.text.replace(bsExtensionRegexp, '.brs')));
282
+ const lowerKey = (_a = attr.tokens.key) === null || _a === void 0 ? void 0 : _a.text.toLowerCase();
283
+ if (lowerKey === 'uri' && bsExtensionRegexp.exec((_b = attr.tokens.value) === null || _b === void 0 ? void 0 : _b.text)) {
284
+ modifiedAttributes.push(util_1.default.cloneSGAttribute(attr, attr.tokens.value.text.replace(bsExtensionRegexp, '.brs')));
144
285
  }
145
286
  else if (lowerKey === 'type') {
146
287
  foundType = true;
147
- if (attr.value.text.toLowerCase().endsWith('brighterscript')) {
288
+ if ((_c = attr.tokens.value) === null || _c === void 0 ? void 0 : _c.text.toLowerCase().endsWith('brighterscript')) {
148
289
  modifiedAttributes.push(util_1.default.cloneSGAttribute(attr, 'text/brightscript'));
149
290
  }
150
291
  else {
@@ -162,88 +303,146 @@ class SGScript extends SGTag {
162
303
  }
163
304
  }
164
305
  exports.SGScript = SGScript;
165
- class SGField extends SGTag {
166
- constructor(tag = { text: 'field' }, attributes = [], range) {
167
- super(tag, attributes, range);
168
- }
306
+ class SGInterfaceField extends SGTag {
169
307
  get type() {
170
308
  return this.getAttributeValue('type');
171
309
  }
172
310
  set type(value) {
173
- this.setAttribute('type', value);
311
+ this.setAttributeValue('type', value);
174
312
  }
175
313
  get alias() {
176
314
  return this.getAttributeValue('alias');
177
315
  }
178
316
  set alias(value) {
179
- this.setAttribute('alias', value);
317
+ this.setAttributeValue('alias', value);
180
318
  }
181
319
  get value() {
182
320
  return this.getAttributeValue('value');
183
321
  }
184
322
  set value(value) {
185
- this.setAttribute('value', value);
323
+ this.setAttributeValue('value', value);
186
324
  }
187
325
  get onChange() {
188
326
  return this.getAttributeValue('onChange');
189
327
  }
190
328
  set onChange(value) {
191
- this.setAttribute('onChange', value);
329
+ this.setAttributeValue('onChange', value);
192
330
  }
193
331
  get alwaysNotify() {
194
332
  return this.getAttributeValue('alwaysNotify');
195
333
  }
196
334
  set alwaysNotify(value) {
197
- this.setAttribute('alwaysNotify', value);
335
+ this.setAttributeValue('alwaysNotify', value);
336
+ }
337
+ get bscType() {
338
+ return getBscTypeFromSGFieldType(this.type);
198
339
  }
199
340
  }
200
- exports.SGField = SGField;
341
+ exports.SGInterfaceField = SGInterfaceField;
201
342
  exports.SGFieldTypes = [
202
343
  'integer', 'int', 'longinteger', 'float', 'string', 'str', 'boolean', 'bool',
203
344
  'vector2d', 'color', 'time', 'uri', 'node', 'floatarray', 'intarray', 'boolarray',
204
345
  'stringarray', 'vector2darray', 'colorarray', 'timearray', 'nodearray', 'assocarray',
205
346
  'array', 'roarray', 'rect2d', 'rect2darray'
206
347
  ];
207
- class SGFunction extends SGTag {
208
- constructor(tag = { text: 'function' }, attributes = [], range) {
209
- super(tag, attributes, range);
348
+ function getBscTypeFromSGFieldType(sgFieldType) {
349
+ switch (sgFieldType) {
350
+ case 'integer':
351
+ case 'int': {
352
+ return new IntegerType_1.IntegerType();
353
+ }
354
+ case 'longinteger': {
355
+ return new LongIntegerType_1.LongIntegerType();
356
+ }
357
+ case 'float': {
358
+ return new FloatType_1.FloatType();
359
+ }
360
+ case 'string':
361
+ case 'str': {
362
+ return new StringType_1.StringType();
363
+ }
364
+ case 'boolean':
365
+ case 'bool': {
366
+ return new BooleanType_1.BooleanType();
367
+ }
368
+ default: {
369
+ return new DynamicType_1.DynamicType();
370
+ }
210
371
  }
372
+ }
373
+ exports.getBscTypeFromSGFieldType = getBscTypeFromSGFieldType;
374
+ class SGInterfaceFunction extends SGTag {
211
375
  get name() {
212
376
  return this.getAttributeValue('name');
213
377
  }
214
378
  set name(value) {
215
- this.setAttribute('name', value);
379
+ this.setAttributeValue('name', value);
380
+ }
381
+ get functionType() {
382
+ const funcType = new FunctionType_1.FunctionType(new DynamicType_1.DynamicType());
383
+ funcType.name = this.name;
384
+ // TODO TYPES: Need to get parameter count/type for these functions for @callFunc usage
385
+ return funcType;
216
386
  }
217
387
  }
218
- exports.SGFunction = SGFunction;
388
+ exports.SGInterfaceFunction = SGInterfaceFunction;
219
389
  class SGInterface extends SGTag {
220
- constructor(tag = { text: 'interface' }, content, range) {
221
- super(tag, [], range);
222
- this.fields = [];
223
- this.functions = [];
224
- if (content) {
225
- for (const tag of content) {
226
- if ((0, xml_1.isSGField)(tag)) {
227
- this.fields.push(tag);
228
- }
229
- else if ((0, xml_1.isSGFunction)(tag)) {
230
- this.functions.push(tag);
231
- }
232
- else {
233
- throw new Error(`Unexpected tag ${tag.tag.text}`);
234
- }
390
+ get fields() {
391
+ return this.getChildNodesByTagName('field');
392
+ }
393
+ get functions() {
394
+ return this.getChildNodesByTagName('function');
395
+ }
396
+ get members() {
397
+ var _a;
398
+ const result = [];
399
+ for (const node of this.childNodes) {
400
+ const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
401
+ if (tagName === 'field' || tagName === 'function') {
402
+ result.push(node);
235
403
  }
236
404
  }
405
+ return result;
237
406
  }
407
+ /**
408
+ * Check if there's an SGField with the specified name
409
+ */
410
+ hasField(id) {
411
+ var _a;
412
+ for (const node of this.childNodes) {
413
+ const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
414
+ if (tagName === 'field' && node.id === id) {
415
+ return true;
416
+ }
417
+ }
418
+ return false;
419
+ }
420
+ /**
421
+ * Check if there's an SGFunction with the specified name
422
+ */
423
+ hasFunction(name) {
424
+ var _a;
425
+ for (const node of this.childNodes) {
426
+ const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
427
+ if (tagName === 'function' && node.name === name) {
428
+ return true;
429
+ }
430
+ }
431
+ return false;
432
+ }
433
+ /**
434
+ * Find a field by its ID
435
+ */
238
436
  getField(id) {
239
437
  return this.fields.find(field => field.id === id);
240
438
  }
439
+ /**
440
+ * Set the value of a field. Creates a new field if one does not already exist with this ID
441
+ */
241
442
  setField(id, type, onChange, alwaysNotify, alias) {
242
443
  let field = this.getField(id);
243
444
  if (!field) {
244
- field = new SGField();
245
- field.id = id;
246
- this.fields.push(field);
445
+ field = this.addChild((0, creators_1.createSGInterfaceField)(id));
247
446
  }
248
447
  field.type = type;
249
448
  field.onChange = onChange;
@@ -254,81 +453,186 @@ class SGInterface extends SGTag {
254
453
  field.alwaysNotify = alwaysNotify ? 'true' : 'false';
255
454
  }
256
455
  field.alias = alias;
456
+ return field;
457
+ }
458
+ /**
459
+ * Remove a field from the interface
460
+ * @returns true if a field was found and removed. Returns false if no field was found with that name
461
+ */
462
+ removeField(id) {
463
+ var _a;
464
+ for (let i = 0; i < this.childNodes.length; i++) {
465
+ const node = this.childNodes[i];
466
+ const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
467
+ if (tagName === 'field' && node.id === id) {
468
+ this.childNodes.splice(i, 1);
469
+ return true;
470
+ }
471
+ }
472
+ return false;
257
473
  }
474
+ /**
475
+ * Get the interface function with the specified name
476
+ */
258
477
  getFunction(name) {
259
478
  return this.functions.find(field => field.name === name);
260
479
  }
480
+ /**
481
+ * Add or replace a function on the interface
482
+ */
261
483
  setFunction(name) {
262
484
  let func = this.getFunction(name);
263
485
  if (!func) {
264
- func = new SGFunction();
265
- func.name = name;
266
- this.functions.push(func);
267
- }
268
- }
269
- transpileBody(state) {
270
- const body = ['>\n'];
271
- state.blockDepth++;
272
- if (this.fields.length > 0) {
273
- body.push(...this.fields.map(node => node.transpile(state)));
486
+ func = this.addChild((0, creators_1.createSGInterfaceFunction)(name));
274
487
  }
275
- if (this.functions.length > 0) {
276
- body.push(...this.functions.map(node => node.transpile(state)));
488
+ return func;
489
+ }
490
+ /**
491
+ * Remove a function from the interface
492
+ * @returns true if a function was found and removed. Returns false if no function was found with that name
493
+ */
494
+ removeFunction(name) {
495
+ var _a;
496
+ for (let i = 0; i < this.childNodes.length; i++) {
497
+ const node = this.childNodes[i];
498
+ const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
499
+ if (tagName === 'function' && node.getAttributeValue('name') === name) {
500
+ this.childNodes.splice(i, 1);
501
+ return true;
502
+ }
277
503
  }
278
- state.blockDepth--;
279
- body.push(state.indentText, '</', this.tag.text, '>\n');
280
- return body;
504
+ return false;
281
505
  }
282
506
  }
283
507
  exports.SGInterface = SGInterface;
284
508
  class SGComponent extends SGTag {
285
- constructor(tag = { text: 'component' }, attributes, content, range) {
286
- super(tag, attributes, range);
287
- this.scripts = [];
288
- if (content) {
289
- for (const tag of content) {
290
- if ((0, xml_1.isSGInterface)(tag)) {
291
- this.api = tag;
292
- }
293
- else if ((0, xml_1.isSGScript)(tag)) {
294
- this.scripts.push(tag);
295
- }
296
- else if ((0, xml_1.isSGChildren)(tag)) {
297
- this.children = tag;
298
- }
299
- else {
300
- throw new Error(`Unexpected tag ${tag.tag.text}`);
301
- }
302
- }
509
+ /**
510
+ * Get all the <Field> and <Function> elements across all <Interface> nodes in this component
511
+ */
512
+ get interfaceMembers() {
513
+ const members = [];
514
+ for (const ifaceNode of this.getChildNodesByTagName('interface')) {
515
+ members.push(...ifaceNode.members);
303
516
  }
517
+ return members;
518
+ }
519
+ get scripts() {
520
+ return this.getChildNodesByTagName('script');
521
+ }
522
+ /**
523
+ * Get the `<children>` element of this component. (not to be confused with the AST `childNodes` property).
524
+ * If there are multiope `<children>` elements, this function will return the last `<children>` tag because that's what Roku devices do.
525
+ */
526
+ get children() {
527
+ const children = this.getChildNodesByTagName('children');
528
+ return children[children.length - 1];
304
529
  }
305
530
  get name() {
306
531
  return this.getAttributeValue('name');
307
532
  }
308
533
  set name(value) {
309
- this.setAttribute('name', value);
534
+ this.setAttributeValue('name', value);
310
535
  }
311
536
  get extends() {
312
537
  return this.getAttributeValue('extends');
313
538
  }
314
539
  set extends(value) {
315
- this.setAttribute('extends', value);
540
+ this.setAttributeValue('extends', value);
541
+ }
542
+ /**
543
+ * Does the specified field exist in the component interface?
544
+ */
545
+ hasInterfaceField(id) {
546
+ for (const ifaceNode of this.getChildNodesByTagName('interface')) {
547
+ if (ifaceNode.hasField(id)) {
548
+ return true;
549
+ }
550
+ }
551
+ return false;
552
+ }
553
+ /**
554
+ * Does the specified function exist in the component interface?
555
+ */
556
+ hasInterfaceFunction(name) {
557
+ for (const ifaceNode of this.getChildNodesByTagName('interface')) {
558
+ if (ifaceNode.hasFunction(name)) {
559
+ return true;
560
+ }
561
+ }
562
+ return false;
563
+ }
564
+ /**
565
+ * Get an interface field with the specified name
566
+ */
567
+ getInterfaceField(name) {
568
+ for (const ifaceNode of this.getChildNodesByTagName('interface')) {
569
+ const field = ifaceNode.getField(name);
570
+ if (field) {
571
+ return field;
572
+ }
573
+ }
316
574
  }
317
- transpileBody(state) {
318
- const body = ['>\n'];
319
- state.blockDepth++;
320
- if (this.api) {
321
- body.push(this.api.transpile(state));
575
+ /**
576
+ * Return the first SGInterface node found, or insert a new one then return it
577
+ */
578
+ ensureInterfaceNode() {
579
+ for (const el of this.childNodes) {
580
+ if (el.tokens.startTagName.text.toLowerCase() === 'interface') {
581
+ return el;
582
+ }
583
+ }
584
+ return this.addChild((0, astUtils_1.createSGInterface)());
585
+ }
586
+ /**
587
+ * Create or update a <field> interface element.
588
+ * This will create a new `<interface>` element if there are none on the component already
589
+ */
590
+ setInterfaceField(id, type, onChange, alwaysNotify, alias) {
591
+ let ifaceNode = this.ensureInterfaceNode();
592
+ return ifaceNode.setField(id, type, onChange, alwaysNotify, alias);
593
+ }
594
+ /**
595
+ * Create or update a <function> interface element.
596
+ * This will create a new `<interface>` element if there are none on the component already
597
+ */
598
+ setInterfaceFunction(name) {
599
+ let ifaceNode = this.ensureInterfaceNode();
600
+ return ifaceNode.setFunction(name);
601
+ }
602
+ /**
603
+ * Remove an interface field.
604
+ * @returns true if a field was found and removed. Returns false if no field was found with that name
605
+ */
606
+ removeInterfaceField(id) {
607
+ for (const ifaceNode of this.getChildNodesByTagName('interface')) {
608
+ if (ifaceNode.removeField(id)) {
609
+ return true;
610
+ }
322
611
  }
323
- if (this.scripts.length > 0) {
324
- body.push(...this.scripts.map(node => node.transpile(state)));
612
+ return false;
613
+ }
614
+ /**
615
+ * Get an interface field with the specified name
616
+ */
617
+ getInterfaceFunction(name) {
618
+ for (const ifaceNode of this.getChildNodesByTagName('interface')) {
619
+ const func = ifaceNode.getFunction(name);
620
+ if (func) {
621
+ return func;
622
+ }
325
623
  }
326
- if (this.children) {
327
- body.push(this.children.transpile(state));
624
+ }
625
+ /**
626
+ * Remove an interface function.
627
+ * @returns true if a function was found and removed. Returns false if no function was found with that name
628
+ */
629
+ removeInterfaceFunction(name) {
630
+ for (const ifaceNode of this.getChildNodesByTagName('interface')) {
631
+ if (ifaceNode.removeFunction(name)) {
632
+ return true;
633
+ }
328
634
  }
329
- state.blockDepth--;
330
- body.push(state.indentText, '</', this.tag.text, '>\n');
331
- return body;
635
+ return false;
332
636
  }
333
637
  }
334
638
  exports.SGComponent = SGComponent;
@@ -348,7 +652,7 @@ class SGAst {
348
652
  //write content
349
653
  chunks.push(this.component.transpile(state));
350
654
  }
351
- return chunks;
655
+ return new source_map_1.SourceNode(null, null, null, chunks);
352
656
  }
353
657
  }
354
658
  exports.SGAst = SGAst;