brighterscript 0.66.0-alpha.9 → 0.67.0

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 (472) hide show
  1. package/CHANGELOG.md +116 -73
  2. package/README.md +14 -418
  3. package/dist/BsConfig.d.ts +25 -2
  4. package/dist/Cache.js +3 -3
  5. package/dist/Cache.js.map +1 -1
  6. package/dist/CodeActionUtil.d.ts +3 -3
  7. package/dist/CodeActionUtil.js.map +1 -1
  8. package/dist/CommentFlagProcessor.d.ts +3 -4
  9. package/dist/CommentFlagProcessor.js +4 -3
  10. package/dist/CommentFlagProcessor.js.map +1 -1
  11. package/dist/DependencyGraph.js +8 -8
  12. package/dist/DependencyGraph.js.map +1 -1
  13. package/dist/DiagnosticFilterer.d.ts +8 -4
  14. package/dist/DiagnosticFilterer.js +71 -38
  15. package/dist/DiagnosticFilterer.js.map +1 -1
  16. package/dist/DiagnosticMessages.d.ts +15 -36
  17. package/dist/DiagnosticMessages.js +15 -61
  18. package/dist/DiagnosticMessages.js.map +1 -1
  19. package/dist/DiagnosticSeverityAdjuster.js +3 -0
  20. package/dist/DiagnosticSeverityAdjuster.js.map +1 -1
  21. package/dist/FunctionScope.d.ts +2 -3
  22. package/dist/FunctionScope.js +0 -3
  23. package/dist/FunctionScope.js.map +1 -1
  24. package/dist/LanguageServer.d.ts +1 -2
  25. package/dist/LanguageServer.js +29 -35
  26. package/dist/LanguageServer.js.map +1 -1
  27. package/dist/Logger.d.ts +5 -9
  28. package/dist/Logger.js +18 -22
  29. package/dist/Logger.js.map +1 -1
  30. package/dist/PluginInterface.d.ts +13 -15
  31. package/dist/PluginInterface.js +16 -70
  32. package/dist/PluginInterface.js.map +1 -1
  33. package/dist/Program.d.ts +105 -138
  34. package/dist/Program.js +479 -702
  35. package/dist/Program.js.map +1 -1
  36. package/dist/ProgramBuilder.d.ts +8 -19
  37. package/dist/ProgramBuilder.js +82 -87
  38. package/dist/ProgramBuilder.js.map +1 -1
  39. package/dist/Scope.d.ts +56 -46
  40. package/dist/Scope.js +281 -217
  41. package/dist/Scope.js.map +1 -1
  42. package/dist/Stopwatch.js +1 -1
  43. package/dist/Stopwatch.js.map +1 -1
  44. package/dist/SymbolTable.d.ts +12 -68
  45. package/dist/SymbolTable.js +28 -213
  46. package/dist/SymbolTable.js.map +1 -1
  47. package/dist/XmlScope.d.ts +5 -7
  48. package/dist/XmlScope.js +36 -76
  49. package/dist/XmlScope.js.map +1 -1
  50. package/dist/astUtils/{Editor.d.ts → AstEditor.d.ts} +1 -6
  51. package/dist/astUtils/{Editor.js → AstEditor.js} +3 -9
  52. package/dist/astUtils/AstEditor.js.map +1 -0
  53. package/dist/astUtils/{Editor.spec.js → AstEditor.spec.js} +6 -10
  54. package/dist/astUtils/AstEditor.spec.js.map +1 -0
  55. package/dist/astUtils/creators.d.ts +8 -19
  56. package/dist/astUtils/creators.js +22 -54
  57. package/dist/astUtils/creators.js.map +1 -1
  58. package/dist/astUtils/creators.spec.js +0 -10
  59. package/dist/astUtils/creators.spec.js.map +1 -1
  60. package/dist/astUtils/reflection.d.ts +45 -81
  61. package/dist/astUtils/reflection.js +157 -220
  62. package/dist/astUtils/reflection.js.map +1 -1
  63. package/dist/astUtils/reflection.spec.js +19 -96
  64. package/dist/astUtils/reflection.spec.js.map +1 -1
  65. package/dist/astUtils/stackedVisitor.spec.js.map +1 -1
  66. package/dist/astUtils/visitors.d.ts +14 -18
  67. package/dist/astUtils/visitors.js +9 -22
  68. package/dist/astUtils/visitors.js.map +1 -1
  69. package/dist/astUtils/visitors.spec.js +9 -62
  70. package/dist/astUtils/visitors.spec.js.map +1 -1
  71. package/dist/astUtils/xml.d.ts +9 -9
  72. package/dist/astUtils/xml.js +6 -6
  73. package/dist/astUtils/xml.js.map +1 -1
  74. package/dist/bscPlugin/BscPlugin.d.ts +8 -11
  75. package/dist/bscPlugin/BscPlugin.js +21 -29
  76. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  77. package/dist/bscPlugin/CallExpressionInfo.d.ts +6 -5
  78. package/dist/bscPlugin/CallExpressionInfo.js +2 -2
  79. package/dist/bscPlugin/CallExpressionInfo.js.map +1 -1
  80. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +11 -11
  81. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  82. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +4 -4
  83. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  84. package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +1 -49
  85. package/dist/bscPlugin/completions/CompletionsProcessor.js +23 -424
  86. package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -1
  87. package/dist/bscPlugin/definition/DefinitionProvider.d.ts +13 -0
  88. package/dist/bscPlugin/definition/DefinitionProvider.js +200 -0
  89. package/dist/bscPlugin/definition/DefinitionProvider.js.map +1 -0
  90. package/dist/bscPlugin/definition/DefinitionProvider.spec.js +87 -0
  91. package/dist/bscPlugin/definition/DefinitionProvider.spec.js.map +1 -0
  92. package/dist/bscPlugin/hover/HoverProcessor.d.ts +3 -7
  93. package/dist/bscPlugin/hover/HoverProcessor.js +88 -128
  94. package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -1
  95. package/dist/bscPlugin/hover/HoverProcessor.spec.js +24 -336
  96. package/dist/bscPlugin/hover/HoverProcessor.spec.js.map +1 -1
  97. package/dist/bscPlugin/references/ReferencesProvider.d.ts +12 -0
  98. package/dist/bscPlugin/references/ReferencesProvider.js +56 -0
  99. package/dist/bscPlugin/references/ReferencesProvider.js.map +1 -0
  100. package/dist/bscPlugin/references/ReferencesProvider.spec.js +51 -0
  101. package/dist/bscPlugin/references/ReferencesProvider.spec.js.map +1 -0
  102. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +0 -1
  103. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +5 -49
  104. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -1
  105. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js +0 -22
  106. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -1
  107. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.d.ts +7 -0
  108. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js +22 -0
  109. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js.map +1 -0
  110. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.js +290 -0
  111. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.js.map +1 -0
  112. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.d.ts +7 -0
  113. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js +26 -0
  114. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js.map +1 -0
  115. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.js +245 -0
  116. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.js.map +1 -0
  117. package/dist/bscPlugin/symbols/symbolUtils.d.ts +5 -0
  118. package/dist/bscPlugin/symbols/symbolUtils.js +140 -0
  119. package/dist/bscPlugin/symbols/symbolUtils.js.map +1 -0
  120. package/dist/bscPlugin/transpile/{BrsFileTranspileProcessor.d.ts → BrsFilePreTranspileProcessor.d.ts} +2 -4
  121. package/dist/bscPlugin/transpile/{BrsFileTranspileProcessor.js → BrsFilePreTranspileProcessor.js} +15 -36
  122. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js.map +1 -0
  123. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.spec.js +46 -0
  124. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.spec.js.map +1 -0
  125. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +1 -0
  126. package/dist/bscPlugin/validation/BrsFileValidator.js +30 -41
  127. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -1
  128. package/dist/bscPlugin/validation/BrsFileValidator.spec.js +2 -2
  129. package/dist/bscPlugin/validation/BrsFileValidator.spec.js.map +1 -1
  130. package/dist/bscPlugin/validation/ProgramValidator.d.ts +3 -3
  131. package/dist/bscPlugin/validation/ProgramValidator.js +6 -6
  132. package/dist/bscPlugin/validation/ProgramValidator.js.map +1 -1
  133. package/dist/bscPlugin/validation/ScopeValidator.d.ts +6 -28
  134. package/dist/bscPlugin/validation/ScopeValidator.js +166 -387
  135. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -1
  136. package/dist/bscPlugin/validation/XmlFileValidator.js +9 -9
  137. package/dist/bscPlugin/validation/XmlFileValidator.js.map +1 -1
  138. package/dist/diagnosticUtils.d.ts +2 -3
  139. package/dist/diagnosticUtils.js +5 -5
  140. package/dist/diagnosticUtils.js.map +1 -1
  141. package/dist/examples/plugins/removePrint.js +1 -1
  142. package/dist/examples/plugins/removePrint.js.map +1 -1
  143. package/dist/files/BrsFile.Class.spec.js +143 -114
  144. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  145. package/dist/files/BrsFile.d.ts +61 -83
  146. package/dist/files/BrsFile.js +552 -607
  147. package/dist/files/BrsFile.js.map +1 -1
  148. package/dist/files/BrsFile.spec.js +1365 -1201
  149. package/dist/files/BrsFile.spec.js.map +1 -1
  150. package/dist/files/XmlFile.d.ts +28 -56
  151. package/dist/files/XmlFile.js +103 -89
  152. package/dist/files/XmlFile.js.map +1 -1
  153. package/dist/files/XmlFile.spec.js +179 -122
  154. package/dist/files/XmlFile.spec.js.map +1 -1
  155. package/dist/files/tests/imports.spec.js +19 -29
  156. package/dist/files/tests/imports.spec.js.map +1 -1
  157. package/dist/files/tests/optionalChaning.spec.js +14 -14
  158. package/dist/files/tests/optionalChaning.spec.js.map +1 -1
  159. package/dist/globalCallables.js +83 -88
  160. package/dist/globalCallables.js.map +1 -1
  161. package/dist/index.d.ts +1 -9
  162. package/dist/index.js +1 -9
  163. package/dist/index.js.map +1 -1
  164. package/dist/interfaces.d.ts +173 -423
  165. package/dist/interfaces.js +0 -24
  166. package/dist/interfaces.js.map +1 -1
  167. package/dist/lexer/Lexer.d.ts +9 -15
  168. package/dist/lexer/Lexer.js +35 -46
  169. package/dist/lexer/Lexer.js.map +1 -1
  170. package/dist/lexer/Lexer.spec.js +48 -40
  171. package/dist/lexer/Lexer.spec.js.map +1 -1
  172. package/dist/lexer/Token.d.ts +1 -5
  173. package/dist/lexer/Token.js +1 -1
  174. package/dist/lexer/Token.js.map +1 -1
  175. package/dist/lexer/TokenKind.d.ts +0 -6
  176. package/dist/lexer/TokenKind.js +2 -14
  177. package/dist/lexer/TokenKind.js.map +1 -1
  178. package/dist/logging.d.ts +9 -0
  179. package/dist/logging.js +16 -0
  180. package/dist/logging.js.map +1 -0
  181. package/dist/parser/AstNode.d.ts +6 -90
  182. package/dist/parser/AstNode.js +5 -96
  183. package/dist/parser/AstNode.js.map +1 -1
  184. package/dist/parser/AstNode.spec.js.map +1 -1
  185. package/dist/parser/BrsTranspileState.d.ts +3 -4
  186. package/dist/parser/BrsTranspileState.js +2 -3
  187. package/dist/parser/BrsTranspileState.js.map +1 -1
  188. package/dist/parser/Expression.d.ts +114 -137
  189. package/dist/parser/Expression.js +244 -373
  190. package/dist/parser/Expression.js.map +1 -1
  191. package/dist/parser/Parser.Class.spec.js +19 -46
  192. package/dist/parser/Parser.Class.spec.js.map +1 -1
  193. package/dist/parser/Parser.d.ts +18 -14
  194. package/dist/parser/Parser.js +196 -175
  195. package/dist/parser/Parser.js.map +1 -1
  196. package/dist/parser/Parser.spec.d.ts +0 -2
  197. package/dist/parser/Parser.spec.js +10 -674
  198. package/dist/parser/Parser.spec.js.map +1 -1
  199. package/dist/parser/SGParser.d.ts +6 -44
  200. package/dist/parser/SGParser.js +198 -194
  201. package/dist/parser/SGParser.js.map +1 -1
  202. package/dist/parser/SGParser.spec.js +11 -14
  203. package/dist/parser/SGParser.spec.js.map +1 -1
  204. package/dist/parser/SGTypes.d.ts +52 -280
  205. package/dist/parser/SGTypes.js +185 -562
  206. package/dist/parser/SGTypes.js.map +1 -1
  207. package/dist/parser/Statement.d.ts +140 -172
  208. package/dist/parser/Statement.js +201 -337
  209. package/dist/parser/Statement.js.map +1 -1
  210. package/dist/parser/Statement.spec.js.map +1 -1
  211. package/dist/parser/TranspileState.d.ts +3 -2
  212. package/dist/parser/TranspileState.js +8 -10
  213. package/dist/parser/TranspileState.js.map +1 -1
  214. package/dist/parser/tests/Parser.spec.js +3 -5
  215. package/dist/parser/tests/Parser.spec.js.map +1 -1
  216. package/dist/parser/tests/controlFlow/For.spec.js +8 -16
  217. package/dist/parser/tests/controlFlow/For.spec.js.map +1 -1
  218. package/dist/parser/tests/controlFlow/ForEach.spec.js +6 -12
  219. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  220. package/dist/parser/tests/controlFlow/While.spec.js +4 -8
  221. package/dist/parser/tests/controlFlow/While.spec.js.map +1 -1
  222. package/dist/parser/tests/expression/Call.spec.js +4 -4
  223. package/dist/parser/tests/expression/Call.spec.js.map +1 -1
  224. package/dist/parser/tests/expression/Indexing.spec.js +25 -0
  225. package/dist/parser/tests/expression/Indexing.spec.js.map +1 -1
  226. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +73 -29
  227. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  228. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +10 -10
  229. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  230. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js +24 -24
  231. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js.map +1 -1
  232. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +47 -35
  233. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  234. package/dist/parser/tests/expression/TernaryExpression.spec.js +83 -36
  235. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  236. package/dist/parser/tests/expression/UnaryExpression.spec.js +2 -2
  237. package/dist/parser/tests/expression/UnaryExpression.spec.js.map +1 -1
  238. package/dist/parser/tests/statement/ConstStatement.spec.js +26 -27
  239. package/dist/parser/tests/statement/ConstStatement.spec.js.map +1 -1
  240. package/dist/parser/tests/statement/Continue.spec.js +2 -2
  241. package/dist/parser/tests/statement/Continue.spec.js.map +1 -1
  242. package/dist/parser/tests/statement/Dim.spec.js.map +1 -1
  243. package/dist/parser/tests/statement/Enum.spec.js +393 -90
  244. package/dist/parser/tests/statement/Enum.spec.js.map +1 -1
  245. package/dist/parser/tests/statement/For.spec.js +6 -6
  246. package/dist/parser/tests/statement/For.spec.js.map +1 -1
  247. package/dist/parser/tests/statement/ForEach.spec.js +4 -4
  248. package/dist/parser/tests/statement/ForEach.spec.js.map +1 -1
  249. package/dist/parser/tests/statement/Function.spec.js +1 -1
  250. package/dist/parser/tests/statement/Function.spec.js.map +1 -1
  251. package/dist/parser/tests/statement/InterfaceStatement.spec.js +18 -18
  252. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  253. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  254. package/dist/parser/tests/statement/PrintStatement.spec.js +13 -16
  255. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  256. package/dist/parser/tests/statement/ReturnStatement.spec.js +3 -5
  257. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +1 -1
  258. package/dist/parser/tests/statement/Set.spec.js +13 -26
  259. package/dist/parser/tests/statement/Set.spec.js.map +1 -1
  260. package/dist/parser/tests/statement/Throw.spec.js.map +1 -1
  261. package/dist/parser/tests/statement/TryCatch.spec.js.map +1 -1
  262. package/dist/preprocessor/Chunk.js +1 -2
  263. package/dist/preprocessor/Chunk.js.map +1 -1
  264. package/dist/preprocessor/Preprocessor.d.ts +3 -4
  265. package/dist/preprocessor/Preprocessor.js +3 -3
  266. package/dist/preprocessor/Preprocessor.js.map +1 -1
  267. package/dist/preprocessor/PreprocessorParser.js +8 -1
  268. package/dist/preprocessor/PreprocessorParser.js.map +1 -1
  269. package/dist/roku-types/data.json +293 -243
  270. package/dist/roku-types/index.d.ts +38 -17
  271. package/dist/types/ArrayType.d.ts +4 -9
  272. package/dist/types/ArrayType.js +24 -72
  273. package/dist/types/ArrayType.js.map +1 -1
  274. package/dist/types/ArrayType.spec.js +10 -39
  275. package/dist/types/ArrayType.spec.js.map +1 -1
  276. package/dist/types/BooleanType.d.ts +4 -8
  277. package/dist/types/BooleanType.js +8 -19
  278. package/dist/types/BooleanType.js.map +1 -1
  279. package/dist/types/BooleanType.spec.js +3 -9
  280. package/dist/types/BooleanType.spec.js.map +1 -1
  281. package/dist/types/BscType.d.ts +2 -29
  282. package/dist/types/BscType.js +0 -113
  283. package/dist/types/BscType.js.map +1 -1
  284. package/dist/types/CustomType.d.ts +9 -0
  285. package/dist/types/CustomType.js +32 -0
  286. package/dist/types/CustomType.js.map +1 -0
  287. package/dist/types/DoubleType.d.ts +4 -8
  288. package/dist/types/DoubleType.js +20 -23
  289. package/dist/types/DoubleType.js.map +1 -1
  290. package/dist/types/DoubleType.spec.js +3 -11
  291. package/dist/types/DoubleType.spec.js.map +1 -1
  292. package/dist/types/DynamicType.d.ts +3 -9
  293. package/dist/types/DynamicType.js +2 -18
  294. package/dist/types/DynamicType.js.map +1 -1
  295. package/dist/types/DynamicType.spec.js +4 -15
  296. package/dist/types/DynamicType.spec.js.map +1 -1
  297. package/dist/types/FloatType.d.ts +4 -8
  298. package/dist/types/FloatType.js +20 -23
  299. package/dist/types/FloatType.js.map +1 -1
  300. package/dist/types/FloatType.spec.js +3 -3
  301. package/dist/types/FloatType.spec.js.map +1 -1
  302. package/dist/types/FunctionType.d.ts +20 -10
  303. package/dist/types/FunctionType.js +52 -27
  304. package/dist/types/FunctionType.js.map +1 -1
  305. package/dist/types/FunctionType.spec.js +23 -0
  306. package/dist/types/FunctionType.spec.js.map +1 -0
  307. package/dist/types/IntegerType.d.ts +4 -8
  308. package/dist/types/IntegerType.js +20 -23
  309. package/dist/types/IntegerType.js.map +1 -1
  310. package/dist/types/IntegerType.spec.js +3 -7
  311. package/dist/types/IntegerType.spec.js.map +1 -1
  312. package/dist/types/InterfaceType.d.ts +10 -12
  313. package/dist/types/InterfaceType.js +48 -23
  314. package/dist/types/InterfaceType.js.map +1 -1
  315. package/dist/types/InterfaceType.spec.js +45 -82
  316. package/dist/types/InterfaceType.spec.js.map +1 -1
  317. package/dist/types/InvalidType.d.ts +4 -7
  318. package/dist/types/InvalidType.js +8 -18
  319. package/dist/types/InvalidType.js.map +1 -1
  320. package/dist/types/InvalidType.spec.js +3 -7
  321. package/dist/types/InvalidType.spec.js.map +1 -1
  322. package/dist/types/LongIntegerType.d.ts +4 -8
  323. package/dist/types/LongIntegerType.js +20 -23
  324. package/dist/types/LongIntegerType.js.map +1 -1
  325. package/dist/types/LongIntegerType.spec.js +3 -9
  326. package/dist/types/LongIntegerType.spec.js.map +1 -1
  327. package/dist/types/ObjectType.d.ts +4 -8
  328. package/dist/types/ObjectType.js +7 -21
  329. package/dist/types/ObjectType.js.map +1 -1
  330. package/dist/types/ObjectType.spec.js +2 -2
  331. package/dist/types/ObjectType.spec.js.map +1 -1
  332. package/dist/types/StringType.d.ts +4 -11
  333. package/dist/types/StringType.js +8 -23
  334. package/dist/types/StringType.js.map +1 -1
  335. package/dist/types/StringType.spec.js +2 -2
  336. package/dist/types/StringType.spec.js.map +1 -1
  337. package/dist/types/UninitializedType.d.ts +3 -7
  338. package/dist/types/UninitializedType.js +3 -14
  339. package/dist/types/UninitializedType.js.map +1 -1
  340. package/dist/types/VoidType.d.ts +4 -8
  341. package/dist/types/VoidType.js +8 -18
  342. package/dist/types/VoidType.js.map +1 -1
  343. package/dist/types/VoidType.spec.js +2 -2
  344. package/dist/types/VoidType.spec.js.map +1 -1
  345. package/dist/util.d.ts +43 -104
  346. package/dist/util.js +243 -640
  347. package/dist/util.js.map +1 -1
  348. package/dist/validators/ClassValidator.d.ts +6 -1
  349. package/dist/validators/ClassValidator.js +61 -20
  350. package/dist/validators/ClassValidator.js.map +1 -1
  351. package/package.json +13 -11
  352. package/dist/ActionPipeline.d.ts +0 -10
  353. package/dist/ActionPipeline.js +0 -40
  354. package/dist/ActionPipeline.js.map +0 -1
  355. package/dist/AstValidationSegmenter.d.ts +0 -25
  356. package/dist/AstValidationSegmenter.js +0 -150
  357. package/dist/AstValidationSegmenter.js.map +0 -1
  358. package/dist/CacheVerifier.d.ts +0 -7
  359. package/dist/CacheVerifier.js +0 -20
  360. package/dist/CacheVerifier.js.map +0 -1
  361. package/dist/astUtils/Editor.js.map +0 -1
  362. package/dist/astUtils/Editor.spec.js.map +0 -1
  363. package/dist/bscPlugin/FileWriter.d.ts +0 -6
  364. package/dist/bscPlugin/FileWriter.js +0 -24
  365. package/dist/bscPlugin/FileWriter.js.map +0 -1
  366. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js +0 -1658
  367. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js.map +0 -1
  368. package/dist/bscPlugin/fileProviders/FileProvider.d.ts +0 -9
  369. package/dist/bscPlugin/fileProviders/FileProvider.js +0 -51
  370. package/dist/bscPlugin/fileProviders/FileProvider.js.map +0 -1
  371. package/dist/bscPlugin/serialize/BslibInjector.spec.js +0 -19
  372. package/dist/bscPlugin/serialize/BslibInjector.spec.js.map +0 -1
  373. package/dist/bscPlugin/serialize/BslibManager.d.ts +0 -9
  374. package/dist/bscPlugin/serialize/BslibManager.js +0 -40
  375. package/dist/bscPlugin/serialize/BslibManager.js.map +0 -1
  376. package/dist/bscPlugin/serialize/FileSerializer.d.ts +0 -9
  377. package/dist/bscPlugin/serialize/FileSerializer.js +0 -72
  378. package/dist/bscPlugin/serialize/FileSerializer.js.map +0 -1
  379. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +0 -1
  380. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js +0 -41
  381. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js.map +0 -1
  382. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.d.ts +0 -11
  383. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js +0 -53
  384. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js.map +0 -1
  385. package/dist/bscPlugin/validation/ScopeValidator.spec.js +0 -2004
  386. package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +0 -1
  387. package/dist/files/AssetFile.d.ts +0 -26
  388. package/dist/files/AssetFile.js +0 -26
  389. package/dist/files/AssetFile.js.map +0 -1
  390. package/dist/files/Factory.d.ts +0 -25
  391. package/dist/files/Factory.js +0 -22
  392. package/dist/files/Factory.js.map +0 -1
  393. package/dist/files/File.d.ts +0 -106
  394. package/dist/files/File.js +0 -16
  395. package/dist/files/File.js.map +0 -1
  396. package/dist/files/LazyFileData.d.ts +0 -20
  397. package/dist/files/LazyFileData.js +0 -54
  398. package/dist/files/LazyFileData.js.map +0 -1
  399. package/dist/files/LazyFileData.spec.js +0 -27
  400. package/dist/files/LazyFileData.spec.js.map +0 -1
  401. package/dist/parser/tests/expression/TypeExpression.spec.js +0 -127
  402. package/dist/parser/tests/expression/TypeExpression.spec.js.map +0 -1
  403. package/dist/types/AssociativeArrayType.d.ts +0 -11
  404. package/dist/types/AssociativeArrayType.js +0 -52
  405. package/dist/types/AssociativeArrayType.js.map +0 -1
  406. package/dist/types/BaseFunctionType.d.ts +0 -9
  407. package/dist/types/BaseFunctionType.js +0 -25
  408. package/dist/types/BaseFunctionType.js.map +0 -1
  409. package/dist/types/BscTypeKind.d.ts +0 -25
  410. package/dist/types/BscTypeKind.js +0 -30
  411. package/dist/types/BscTypeKind.js.map +0 -1
  412. package/dist/types/BuiltInInterfaceAdder.d.ts +0 -23
  413. package/dist/types/BuiltInInterfaceAdder.js +0 -160
  414. package/dist/types/BuiltInInterfaceAdder.js.map +0 -1
  415. package/dist/types/BuiltInInterfaceAdder.spec.d.ts +0 -1
  416. package/dist/types/BuiltInInterfaceAdder.spec.js +0 -116
  417. package/dist/types/BuiltInInterfaceAdder.spec.js.map +0 -1
  418. package/dist/types/ClassType.d.ts +0 -17
  419. package/dist/types/ClassType.js +0 -58
  420. package/dist/types/ClassType.js.map +0 -1
  421. package/dist/types/ClassType.spec.d.ts +0 -1
  422. package/dist/types/ClassType.spec.js +0 -77
  423. package/dist/types/ClassType.spec.js.map +0 -1
  424. package/dist/types/ComponentType.d.ts +0 -26
  425. package/dist/types/ComponentType.js +0 -83
  426. package/dist/types/ComponentType.js.map +0 -1
  427. package/dist/types/EnumType.d.ts +0 -40
  428. package/dist/types/EnumType.js +0 -81
  429. package/dist/types/EnumType.js.map +0 -1
  430. package/dist/types/EnumType.spec.d.ts +0 -1
  431. package/dist/types/EnumType.spec.js +0 -33
  432. package/dist/types/EnumType.spec.js.map +0 -1
  433. package/dist/types/InheritableType.d.ts +0 -28
  434. package/dist/types/InheritableType.js +0 -152
  435. package/dist/types/InheritableType.js.map +0 -1
  436. package/dist/types/NamespaceType.d.ts +0 -12
  437. package/dist/types/NamespaceType.js +0 -28
  438. package/dist/types/NamespaceType.js.map +0 -1
  439. package/dist/types/ReferenceType.d.ts +0 -63
  440. package/dist/types/ReferenceType.js +0 -423
  441. package/dist/types/ReferenceType.js.map +0 -1
  442. package/dist/types/ReferenceType.spec.d.ts +0 -1
  443. package/dist/types/ReferenceType.spec.js +0 -137
  444. package/dist/types/ReferenceType.spec.js.map +0 -1
  445. package/dist/types/TypedFunctionType.d.ts +0 -33
  446. package/dist/types/TypedFunctionType.js +0 -106
  447. package/dist/types/TypedFunctionType.js.map +0 -1
  448. package/dist/types/TypedFunctionType.spec.d.ts +0 -1
  449. package/dist/types/TypedFunctionType.spec.js +0 -122
  450. package/dist/types/TypedFunctionType.spec.js.map +0 -1
  451. package/dist/types/UnionType.d.ts +0 -20
  452. package/dist/types/UnionType.js +0 -123
  453. package/dist/types/UnionType.js.map +0 -1
  454. package/dist/types/UnionType.spec.d.ts +0 -1
  455. package/dist/types/UnionType.spec.js +0 -130
  456. package/dist/types/UnionType.spec.js.map +0 -1
  457. package/dist/types/helper.spec.d.ts +0 -1
  458. package/dist/types/helper.spec.js +0 -145
  459. package/dist/types/helper.spec.js.map +0 -1
  460. package/dist/types/helpers.d.ts +0 -24
  461. package/dist/types/helpers.js +0 -178
  462. package/dist/types/helpers.js.map +0 -1
  463. package/dist/types/index.d.ts +0 -22
  464. package/dist/types/index.js +0 -39
  465. package/dist/types/index.js.map +0 -1
  466. /package/dist/astUtils/{Editor.spec.d.ts → AstEditor.spec.d.ts} +0 -0
  467. /package/dist/bscPlugin/{completions/CompletionsProcessor.spec.d.ts → definition/DefinitionProvider.spec.d.ts} +0 -0
  468. /package/dist/bscPlugin/{serialize/BslibInjector.spec.d.ts → references/ReferencesProvider.spec.d.ts} +0 -0
  469. /package/dist/bscPlugin/{transpile/BrsFileTranspileProcessor.spec.d.ts → symbols/DocumentSymbolProcessor.spec.d.ts} +0 -0
  470. /package/dist/bscPlugin/{validation/ScopeValidator.spec.d.ts → symbols/WorkspaceSymbolProcessor.spec.d.ts} +0 -0
  471. /package/dist/{files/LazyFileData.spec.d.ts → bscPlugin/transpile/BrsFilePreTranspileProcessor.spec.d.ts} +0 -0
  472. /package/dist/{parser/tests/expression/TypeExpression.spec.d.ts → types/FunctionType.spec.d.ts} +0 -0
package/dist/Program.js CHANGED
@@ -4,13 +4,16 @@ exports.Program = void 0;
4
4
  const assert = require("assert");
5
5
  const fsExtra = require("fs-extra");
6
6
  const path = require("path");
7
+ const vscode_languageserver_1 = require("vscode-languageserver");
7
8
  const Scope_1 = require("./Scope");
8
9
  const DiagnosticMessages_1 = require("./DiagnosticMessages");
10
+ const BrsFile_1 = require("./files/BrsFile");
11
+ const XmlFile_1 = require("./files/XmlFile");
9
12
  const util_1 = require("./util");
10
13
  const XmlScope_1 = require("./XmlScope");
11
14
  const DiagnosticFilterer_1 = require("./DiagnosticFilterer");
12
15
  const DependencyGraph_1 = require("./DependencyGraph");
13
- const Logger_1 = require("./Logger");
16
+ const logging_1 = require("./logging");
14
17
  const chalk_1 = require("chalk");
15
18
  const globalCallables_1 = require("./globalCallables");
16
19
  const Manifest_1 = require("./preprocessor/Manifest");
@@ -18,29 +21,12 @@ const vscode_uri_1 = require("vscode-uri");
18
21
  const PluginInterface_1 = require("./PluginInterface");
19
22
  const reflection_1 = require("./astUtils/reflection");
20
23
  const BscPlugin_1 = require("./bscPlugin/BscPlugin");
21
- const Editor_1 = require("./astUtils/Editor");
24
+ const AstEditor_1 = require("./astUtils/AstEditor");
25
+ const roku_deploy_1 = require("roku-deploy");
22
26
  const CallExpressionInfo_1 = require("./bscPlugin/CallExpressionInfo");
23
27
  const SignatureHelpUtil_1 = require("./bscPlugin/SignatureHelpUtil");
24
28
  const DiagnosticSeverityAdjuster_1 = require("./DiagnosticSeverityAdjuster");
25
- const IntegerType_1 = require("./types/IntegerType");
26
- const StringType_1 = require("./types/StringType");
27
- const SymbolTable_1 = require("./SymbolTable");
28
- const BooleanType_1 = require("./types/BooleanType");
29
- const DoubleType_1 = require("./types/DoubleType");
30
- const DynamicType_1 = require("./types/DynamicType");
31
- const FloatType_1 = require("./types/FloatType");
32
- const LongIntegerType_1 = require("./types/LongIntegerType");
33
- const ObjectType_1 = require("./types/ObjectType");
34
- const VoidType_1 = require("./types/VoidType");
35
- const FunctionType_1 = require("./types/FunctionType");
36
- const Factory_1 = require("./files/Factory");
37
- const ActionPipeline_1 = require("./ActionPipeline");
38
- const LazyFileData_1 = require("./files/LazyFileData");
39
- const roku_deploy_1 = require("roku-deploy");
40
- const roku_types_1 = require("./roku-types");
41
- const ComponentType_1 = require("./types/ComponentType");
42
- const types_1 = require("./types");
43
- const BuiltInInterfaceAdder_1 = require("./types/BuiltInInterfaceAdder");
29
+ const startOfSourcePkgPath = `source${path.sep}`;
44
30
  const bslibNonAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/rokucommunity_bslib/bslib.brs`;
45
31
  const bslibAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/bslib/bslib.brs`;
46
32
  class Program {
@@ -49,11 +35,6 @@ class Program {
49
35
  * The root directory for this program
50
36
  */
51
37
  options, logger, plugins) {
52
- this.options = options;
53
- /**
54
- * An editor that plugins can use to modify program-level things during the build flow. Don't use this to edit files (they have their own `.editor`)
55
- */
56
- this.editor = new Editor_1.Editor();
57
38
  /**
58
39
  * A graph of all files and their dependencies.
59
40
  * For example:
@@ -63,26 +44,21 @@ class Program {
63
44
  this.dependencyGraph = new DependencyGraph_1.DependencyGraph();
64
45
  this.diagnosticFilterer = new DiagnosticFilterer_1.DiagnosticFilterer();
65
46
  this.diagnosticAdjuster = new DiagnosticSeverityAdjuster_1.DiagnosticSeverityAdjuster();
47
+ /**
48
+ * A scope that contains all built-in global functions.
49
+ * All scopes should directly or indirectly inherit from this scope
50
+ */
51
+ this.globalScope = undefined;
66
52
  /**
67
53
  * A set of diagnostics. This does not include any of the scope diagnostics.
68
54
  * Should only be set from `this.validate()`
69
55
  */
70
56
  this.diagnostics = [];
71
- this.fileSymbolInformation = new Map();
72
57
  /**
73
58
  * A map of every file loaded into this program, indexed by its original file location
74
59
  */
75
60
  this.files = {};
76
- /**
77
- * A map of every file loaded into this program, indexed by its destPath
78
- */
79
- this.destMap = new Map();
80
- /**
81
- * Plugins can contribute multiple virtual files for a single physical file.
82
- * This collection links the virtual files back to the physical file that produced them.
83
- * The key is the standardized and lower-cased srcPath
84
- */
85
- this.fileClusters = new Map();
61
+ this.pkgMap = {};
86
62
  this.scopes = {};
87
63
  /**
88
64
  * A map of every component currently loaded into the program, indexed by the component name.
@@ -91,111 +67,28 @@ class Program {
91
67
  * but if you do, only ever use the component at index 0.
92
68
  */
93
69
  this.components = {};
94
- /**
95
- * Keeps a set of all the components that need to have their types updated during the current validation cycle
96
- */
97
- this.componentSymbolsToUpdate = new Set();
98
- this.lastValidationInfo = new Map();
99
- this.getTranspiledFileContentsPipeline = new ActionPipeline_1.ActionPipeline();
100
- this.buildPipeline = new ActionPipeline_1.ActionPipeline();
101
70
  this.options = util_1.util.normalizeConfig(options);
102
- this.logger = logger || new Logger_1.Logger(options.logLevel);
71
+ this.logger = logger !== null && logger !== void 0 ? logger : (0, logging_1.createLogger)(options);
103
72
  this.plugins = plugins || new PluginInterface_1.default([], { logger: this.logger });
104
73
  //inject the bsc plugin as the first plugin in the stack.
105
74
  this.plugins.addFirst(new BscPlugin_1.BscPlugin());
106
75
  //normalize the root dir path
107
76
  this.options.rootDir = util_1.util.getRootDir(this.options);
108
77
  this.createGlobalScope();
109
- this.fileFactory = new Factory_1.FileFactory(this);
110
78
  }
111
79
  createGlobalScope() {
112
80
  //create the 'global' scope
113
81
  this.globalScope = new Scope_1.Scope('global', this, 'scope:global');
114
82
  this.globalScope.attachDependencyGraph(this.dependencyGraph);
115
83
  this.scopes.global = this.globalScope;
116
- this.populateGlobalSymbolTable();
117
84
  //hardcode the files list for global scope to only contain the global file
118
85
  this.globalScope.getAllFiles = () => [globalCallables_1.globalFile];
119
- globalCallables_1.globalFile.isValidated = true;
120
86
  this.globalScope.validate();
121
87
  //for now, disable validation of global scope because the global files have some duplicate method declarations
122
88
  this.globalScope.getDiagnostics = () => [];
123
89
  //TODO we might need to fix this because the isValidated clears stuff now
124
90
  this.globalScope.isValidated = true;
125
91
  }
126
- recursivelyAddNodeToSymbolTable(nodeData) {
127
- if (!nodeData) {
128
- return;
129
- }
130
- let nodeType;
131
- const nodeName = util_1.util.getSgNodeTypeName(nodeData.name);
132
- if (!this.globalScope.symbolTable.hasSymbol(nodeName, SymbolTable_1.SymbolTypeFlag.typetime)) {
133
- let parentNode;
134
- if (nodeData.extends) {
135
- const parentNodeData = roku_types_1.nodes[nodeData.extends.name.toLowerCase()];
136
- try {
137
- parentNode = this.recursivelyAddNodeToSymbolTable(parentNodeData);
138
- }
139
- catch (error) {
140
- console.log(error, nodeData);
141
- }
142
- }
143
- nodeType = new ComponentType_1.ComponentType(nodeData.name, parentNode);
144
- nodeType.addBuiltInInterfaces();
145
- this.globalScope.symbolTable.addSymbol(nodeName, { description: nodeData.description }, nodeType, SymbolTable_1.SymbolTypeFlag.typetime);
146
- }
147
- else {
148
- nodeType = this.globalScope.symbolTable.getSymbolType(nodeName, { flags: SymbolTable_1.SymbolTypeFlag.typetime });
149
- }
150
- return nodeType;
151
- }
152
- /**
153
- * Do all setup required for the global symbol table.
154
- */
155
- populateGlobalSymbolTable() {
156
- //Setup primitive types in global symbolTable
157
- this.globalScope.symbolTable.addSymbol('boolean', undefined, BooleanType_1.BooleanType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
158
- this.globalScope.symbolTable.addSymbol('double', undefined, DoubleType_1.DoubleType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
159
- this.globalScope.symbolTable.addSymbol('dynamic', undefined, DynamicType_1.DynamicType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
160
- this.globalScope.symbolTable.addSymbol('float', undefined, FloatType_1.FloatType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
161
- this.globalScope.symbolTable.addSymbol('function', undefined, new FunctionType_1.FunctionType(), SymbolTable_1.SymbolTypeFlag.typetime);
162
- this.globalScope.symbolTable.addSymbol('integer', undefined, IntegerType_1.IntegerType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
163
- this.globalScope.symbolTable.addSymbol('longinteger', undefined, LongIntegerType_1.LongIntegerType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
164
- this.globalScope.symbolTable.addSymbol('object', undefined, new ObjectType_1.ObjectType(), SymbolTable_1.SymbolTypeFlag.typetime);
165
- this.globalScope.symbolTable.addSymbol('string', undefined, StringType_1.StringType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
166
- this.globalScope.symbolTable.addSymbol('void', undefined, VoidType_1.VoidType.instance, SymbolTable_1.SymbolTypeFlag.typetime);
167
- BuiltInInterfaceAdder_1.BuiltInInterfaceAdder.getLookupTable = () => this.globalScope.symbolTable;
168
- for (const callable of globalCallables_1.globalCallables) {
169
- this.globalScope.symbolTable.addSymbol(callable.name, { description: callable.shortDescription }, callable.type, SymbolTable_1.SymbolTypeFlag.runtime);
170
- }
171
- for (const componentData of Object.values(roku_types_1.components)) {
172
- const nodeType = new types_1.InterfaceType(componentData.name);
173
- nodeType.addBuiltInInterfaces();
174
- this.globalScope.symbolTable.addSymbol(componentData.name, { description: componentData.description }, nodeType, SymbolTable_1.SymbolTypeFlag.typetime);
175
- }
176
- for (const ifaceData of Object.values(roku_types_1.interfaces)) {
177
- const nodeType = new types_1.InterfaceType(ifaceData.name);
178
- nodeType.addBuiltInInterfaces();
179
- this.globalScope.symbolTable.addSymbol(ifaceData.name, { description: ifaceData.description }, nodeType, SymbolTable_1.SymbolTypeFlag.typetime);
180
- }
181
- for (const eventData of Object.values(roku_types_1.events)) {
182
- const nodeType = new types_1.InterfaceType(eventData.name);
183
- nodeType.addBuiltInInterfaces();
184
- this.globalScope.symbolTable.addSymbol(eventData.name, { description: eventData.description }, nodeType, SymbolTable_1.SymbolTypeFlag.typetime);
185
- }
186
- for (const nodeData of Object.values(roku_types_1.nodes)) {
187
- this.recursivelyAddNodeToSymbolTable(nodeData);
188
- }
189
- }
190
- addFileSymbolInfo(file) {
191
- this.fileSymbolInformation.set(file.pkgPath, {
192
- provides: file.providedSymbols,
193
- requires: file.requiredSymbols
194
- });
195
- }
196
- getFileSymbolInfo(file) {
197
- return this.fileSymbolInformation.get(file.pkgPath);
198
- }
199
92
  /**
200
93
  * The path to bslib.brs (the BrightScript runtime for certain BrighterScript features)
201
94
  */
@@ -231,7 +124,7 @@ class Program {
231
124
  var _a;
232
125
  if (componentName) {
233
126
  //return the first compoment in the list with this name
234
- //(components are ordered in this list by destPath to ensure consistency)
127
+ //(components are ordered in this list by pkgPath to ensure consistency)
235
128
  return (_a = this.components[componentName.toLowerCase()]) === null || _a === void 0 ? void 0 : _a[0];
236
129
  }
237
130
  else {
@@ -242,7 +135,8 @@ class Program {
242
135
  * Register (or replace) the reference to a component in the component map
243
136
  */
244
137
  registerComponent(xmlFile, scope) {
245
- const key = this.getComponentKey(xmlFile);
138
+ var _a, _b;
139
+ const key = ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
246
140
  if (!this.components[key]) {
247
141
  this.components[key] = [];
248
142
  }
@@ -251,8 +145,8 @@ class Program {
251
145
  scope: scope
252
146
  });
253
147
  this.components[key].sort((a, b) => {
254
- const pathA = a.file.destPath.toLowerCase();
255
- const pathB = b.file.destPath.toLowerCase();
148
+ const pathA = a.file.pkgPath.toLowerCase();
149
+ const pathB = b.file.pkgPath.toLowerCase();
256
150
  if (pathA < pathB) {
257
151
  return -1;
258
152
  }
@@ -262,13 +156,13 @@ class Program {
262
156
  return 0;
263
157
  });
264
158
  this.syncComponentDependencyGraph(this.components[key]);
265
- this.addDeferredComponentTypeSymbolCreation(xmlFile);
266
159
  }
267
160
  /**
268
161
  * Remove the specified component from the components map
269
162
  */
270
163
  unregisterComponent(xmlFile) {
271
- const key = this.getComponentKey(xmlFile);
164
+ var _a, _b;
165
+ const key = ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
272
166
  const arr = this.components[key] || [];
273
167
  for (let i = 0; i < arr.length; i++) {
274
168
  if (arr[i].file === xmlFile) {
@@ -277,44 +171,6 @@ class Program {
277
171
  }
278
172
  }
279
173
  this.syncComponentDependencyGraph(arr);
280
- this.addDeferredComponentTypeSymbolCreation(xmlFile);
281
- }
282
- /**
283
- * Adds a component described in an XML to the set of components that needs to be updated this validation cycle.
284
- * @param xmlFile XML file with <component> tag
285
- */
286
- addDeferredComponentTypeSymbolCreation(xmlFile) {
287
- var _a;
288
- this.componentSymbolsToUpdate.add({ componentKey: this.getComponentKey(xmlFile), componentName: (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text });
289
- }
290
- getComponentKey(xmlFile) {
291
- var _a, _b;
292
- return ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
293
- }
294
- /**
295
- * Updates the global symbol table with the first component in this.components to have the same name as the component in the file
296
- * @param componentKey key getting a component from `this.components`
297
- * @param componentName the unprefixed name of the component that will be added (e.g. 'MyLabel' NOT 'roSgNodeMyLabel')
298
- */
299
- updateComponentSymbolInGlobalScope(componentKey, componentName) {
300
- const symbolName = componentName ? util_1.util.getSgNodeTypeName(componentName) : undefined;
301
- if (!symbolName) {
302
- return;
303
- }
304
- const components = this.components[componentKey] || [];
305
- // Remove any existing symbols that match
306
- this.globalScope.symbolTable.removeSymbol(symbolName);
307
- // There is a component that can be added - use it.
308
- if (components.length > 0) {
309
- const componentScope = components[0].scope;
310
- // TODO: May need to link symbol tables to get correct types for callfuncs
311
- // componentScope.linkSymbolTable();
312
- const componentType = componentScope.getComponentType();
313
- if (componentType) {
314
- this.globalScope.symbolTable.addSymbol(symbolName, {}, componentType, SymbolTable_1.SymbolTypeFlag.typetime);
315
- }
316
- // TODO: Remember to unlink! componentScope.unlinkSymbolTable();
317
- }
318
174
  }
319
175
  /**
320
176
  * re-attach the dependency graph with a new key for any component who changed
@@ -328,7 +184,6 @@ class Program {
328
184
  //attach (or re-attach) the dependencyGraph for every component whose position changed
329
185
  if (file.dependencyGraphIndex !== i) {
330
186
  file.dependencyGraphIndex = i;
331
- this.dependencyGraph.addOrReplace(file.dependencyGraphKey, file.dependencies);
332
187
  file.attachDependencyGraph(this.dependencyGraph);
333
188
  scope.attachDependencyGraph(this.dependencyGraph);
334
189
  }
@@ -355,7 +210,7 @@ class Program {
355
210
  * by walking through every file, so call this sparingly.
356
211
  */
357
212
  getDiagnostics() {
358
- return this.logger.time(Logger_1.LogLevel.info, ['Program.getDiagnostics()'], () => {
213
+ return this.logger.time(logging_1.LogLevel.info, ['Program.getDiagnostics()'], () => {
359
214
  let diagnostics = [...this.diagnostics];
360
215
  //get the diagnostics from all scopes
361
216
  for (let scopeName in this.scopes) {
@@ -365,14 +220,14 @@ class Program {
365
220
  //get the diagnostics from all unreferenced files
366
221
  let unreferencedFiles = this.getUnreferencedFiles();
367
222
  for (let file of unreferencedFiles) {
368
- diagnostics.push(...file.diagnostics);
223
+ diagnostics.push(...file.getDiagnostics());
369
224
  }
370
- const filteredDiagnostics = this.logger.time(Logger_1.LogLevel.debug, ['filter diagnostics'], () => {
225
+ const filteredDiagnostics = this.logger.time(logging_1.LogLevel.debug, ['filter diagnostics'], () => {
371
226
  //filter out diagnostics based on our diagnostic filters
372
227
  let finalDiagnostics = this.diagnosticFilterer.filter(Object.assign(Object.assign({}, this.options), { rootDir: this.options.rootDir }), diagnostics);
373
228
  return finalDiagnostics;
374
229
  });
375
- this.logger.time(Logger_1.LogLevel.debug, ['adjust diagnostics severity'], () => {
230
+ this.logger.time(logging_1.LogLevel.debug, ['adjust diagnostics severity'], () => {
376
231
  this.diagnosticAdjuster.adjust(this.options, diagnostics);
377
232
  });
378
233
  this.logger.info(`diagnostic counts: total=${chalk_1.default.yellow(diagnostics.length.toString())}, after filter=${chalk_1.default.yellow(filteredDiagnostics.length.toString())}`);
@@ -390,9 +245,11 @@ class Program {
390
245
  hasFile(filePath, normalizePath = true) {
391
246
  return !!this.getFile(filePath, normalizePath);
392
247
  }
248
+ getPkgPath(...args) {
249
+ throw new Error('Not implemented');
250
+ }
393
251
  /**
394
252
  * roku filesystem is case INsensitive, so find the scope by key case insensitive
395
- * @param scopeName xml scope names are their `destPath`. Source scope is stored with the key `"source"`
396
253
  */
397
254
  getScopeByName(scopeName) {
398
255
  if (!scopeName) {
@@ -400,8 +257,8 @@ class Program {
400
257
  }
401
258
  //most scopes are xml file pkg paths. however, the ones that are not are single names like "global" and "scope",
402
259
  //so it's safe to run the standardizePkgPath method
403
- scopeName = (0, util_1.standardizePath) `${scopeName.toLowerCase()}`;
404
- let key = Object.keys(this.scopes).find(x => x.toLowerCase() === scopeName);
260
+ scopeName = (0, util_1.standardizePath) `${scopeName}`;
261
+ let key = Object.keys(this.scopes).find(x => x.toLowerCase() === scopeName.toLowerCase());
405
262
  return this.scopes[key];
406
263
  }
407
264
  /**
@@ -421,14 +278,8 @@ class Program {
421
278
  * Update internal maps with this file reference
422
279
  */
423
280
  assignFile(file) {
424
- const fileAddEvent = {
425
- file: file,
426
- program: this
427
- };
428
- this.plugins.emit('beforeFileAdd', fileAddEvent);
429
281
  this.files[file.srcPath.toLowerCase()] = file;
430
- this.destMap.set(file.destPath.toLowerCase(), file);
431
- this.plugins.emit('afterFileAdd', fileAddEvent);
282
+ this.pkgMap[file.pkgPath.toLowerCase()] = file;
432
283
  return file;
433
284
  }
434
285
  /**
@@ -436,102 +287,100 @@ class Program {
436
287
  */
437
288
  unassignFile(file) {
438
289
  delete this.files[file.srcPath.toLowerCase()];
439
- this.destMap.delete(file.destPath.toLowerCase());
290
+ delete this.pkgMap[file.pkgPath.toLowerCase()];
440
291
  return file;
441
292
  }
442
- setFile(fileParam, fileData) {
293
+ addOrReplaceFile(fileParam, fileContents) {
294
+ return this.setFile(fileParam, fileContents);
295
+ }
296
+ setFile(fileParam, fileContents) {
443
297
  //normalize the file paths
444
- const { srcPath, destPath } = this.getPaths(fileParam, this.options.rootDir);
445
- let file = this.logger.time(Logger_1.LogLevel.debug, ['Program.setFile()', chalk_1.default.green(srcPath)], () => {
446
- var _a, _b, _c;
298
+ const { srcPath, pkgPath } = this.getPaths(fileParam, this.options.rootDir);
299
+ let file = this.logger.time(logging_1.LogLevel.debug, ['Program.setFile()', chalk_1.default.green(srcPath)], () => {
447
300
  //if the file is already loaded, remove it
448
301
  if (this.hasFile(srcPath)) {
449
- this.removeFile(srcPath, true, true);
302
+ this.removeFile(srcPath);
450
303
  }
451
- const data = new LazyFileData_1.LazyFileData(fileData);
452
- const event = new ProvideFileEventInternal(this, srcPath, destPath, data, this.fileFactory);
453
- this.plugins.emit('beforeProvideFile', event);
454
- this.plugins.emit('provideFile', event);
455
- this.plugins.emit('afterProvideFile', event);
456
- //if no files were provided, create a AssetFile to represent it.
457
- if (event.files.length === 0) {
458
- event.files.push(this.fileFactory.AssetFile({
459
- srcPath: event.srcPath,
460
- destPath: event.destPath,
461
- pkgPath: event.destPath,
462
- data: data
463
- }));
464
- }
465
- //find the file instance for the srcPath that triggered this action.
466
- const primaryFile = event.files.find(x => x.srcPath === srcPath);
467
- if (!primaryFile) {
468
- throw new Error(`No file provided for srcPath '${srcPath}'. Instead, received ${JSON.stringify(event.files.map(x => ({
469
- type: x.type,
470
- srcPath: x.srcPath,
471
- destPath: x.destPath
472
- })))}`);
473
- }
474
- //link the virtual files to the primary file
475
- this.fileClusters.set((_a = primaryFile.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase(), event.files);
476
- for (const file of event.files) {
477
- file.srcPath = (0, util_1.standardizePath)(file.srcPath);
478
- if (file.destPath) {
479
- file.destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.destPath, this.options.rootDir, '')}`;
480
- }
481
- if (file.pkgPath) {
482
- file.pkgPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.pkgPath, this.options.rootDir, '')}`;
483
- }
484
- else {
485
- file.pkgPath = file.destPath;
486
- }
487
- file.excludeFromOutput = file.excludeFromOutput === true;
488
- //set the dependencyGraph key for every file to its destPath
489
- file.dependencyGraphKey = file.destPath.toLowerCase();
490
- this.assignFile(file);
491
- //register a callback anytime this file's dependencies change
492
- if (typeof file.onDependenciesChanged === 'function') {
493
- (_b = file.disposables) !== null && _b !== void 0 ? _b : (file.disposables = []);
494
- file.disposables.push(this.dependencyGraph.onchange(file.dependencyGraphKey, file.onDependenciesChanged.bind(file)));
495
- }
496
- //register this file (and its dependencies) with the dependency graph
497
- this.dependencyGraph.addOrReplace(file.dependencyGraphKey, (_c = file.dependencies) !== null && _c !== void 0 ? _c : []);
498
- //if this is a `source` file, add it to the source scope's dependency list
499
- if (this.isSourceBrsFile(file)) {
304
+ let fileExtension = path.extname(srcPath).toLowerCase();
305
+ let file;
306
+ if (fileExtension === '.brs' || fileExtension === '.bs') {
307
+ //add the file to the program
308
+ const brsFile = this.assignFile(new BrsFile_1.BrsFile(srcPath, pkgPath, this));
309
+ //add file to the `source` dependency list
310
+ if (brsFile.pkgPath.startsWith(startOfSourcePkgPath)) {
500
311
  this.createSourceScope();
501
- this.dependencyGraph.addDependency('scope:source', file.dependencyGraphKey);
502
- }
503
- //if this is an xml file in the components folder, register it as a component
504
- if (this.isComponentsXmlFile(file)) {
505
- //create a new scope for this xml file
506
- let scope = new XmlScope_1.XmlScope(file, this);
507
- this.addScope(scope);
508
- //register this compoent now that we have parsed it and know its component name
509
- this.registerComponent(file, scope);
510
- //notify plugins that the scope is created and the component is registered
511
- this.plugins.emit('afterScopeCreate', {
512
- program: this,
513
- scope: scope
514
- });
312
+ this.dependencyGraph.addDependency('scope:source', brsFile.dependencyGraphKey);
515
313
  }
314
+ let sourceObj = {
315
+ //TODO remove `pathAbsolute` in v1
316
+ pathAbsolute: srcPath,
317
+ srcPath: srcPath,
318
+ source: fileContents
319
+ };
320
+ this.plugins.emit('beforeFileParse', sourceObj);
321
+ this.logger.time(logging_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
322
+ brsFile.parse(sourceObj.source);
323
+ });
324
+ //notify plugins that this file has finished parsing
325
+ this.plugins.emit('afterFileParse', brsFile);
326
+ file = brsFile;
327
+ brsFile.attachDependencyGraph(this.dependencyGraph);
328
+ }
329
+ else if (
330
+ //is xml file
331
+ fileExtension === '.xml' &&
332
+ //resides in the components folder (Roku will only parse xml files in the components folder)
333
+ pkgPath.toLowerCase().startsWith(util_1.util.pathSepNormalize(`components/`))) {
334
+ //add the file to the program
335
+ const xmlFile = this.assignFile(new XmlFile_1.XmlFile(srcPath, pkgPath, this));
336
+ let sourceObj = {
337
+ //TODO remove `pathAbsolute` in v1
338
+ pathAbsolute: srcPath,
339
+ srcPath: srcPath,
340
+ source: fileContents
341
+ };
342
+ this.plugins.emit('beforeFileParse', sourceObj);
343
+ this.logger.time(logging_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
344
+ xmlFile.parse(sourceObj.source);
345
+ });
346
+ //notify plugins that this file has finished parsing
347
+ this.plugins.emit('afterFileParse', xmlFile);
348
+ file = xmlFile;
349
+ //create a new scope for this xml file
350
+ let scope = new XmlScope_1.XmlScope(xmlFile, this);
351
+ this.addScope(scope);
352
+ //register this compoent now that we have parsed it and know its component name
353
+ this.registerComponent(xmlFile, scope);
354
+ //notify plugins that the scope is created and the component is registered
355
+ this.plugins.emit('afterScopeCreate', scope);
356
+ }
357
+ else {
358
+ //TODO do we actually need to implement this? Figure out how to handle img paths
359
+ // let genericFile = this.files[srcPath] = <any>{
360
+ // srcPath: srcPath,
361
+ // pkgPath: pkgPath,
362
+ // wasProcessed: true
363
+ // } as File;
364
+ // file = <any>genericFile;
516
365
  }
517
- return primaryFile;
366
+ return file;
518
367
  });
519
368
  return file;
520
369
  }
521
370
  /**
522
- * Given a srcPath, a destPath, or both, resolve whichever is missing, relative to rootDir.
371
+ * Given a srcPath, a pkgPath, or both, resolve whichever is missing, relative to rootDir.
523
372
  * @param fileParam an object representing file paths
524
373
  * @param rootDir must be a pre-normalized path
525
374
  */
526
375
  getPaths(fileParam, rootDir) {
527
376
  let srcPath;
528
- let destPath;
377
+ let pkgPath;
529
378
  assert.ok(fileParam, 'fileParam is required');
530
- //lift the path vars from the incoming param
379
+ //lift the srcPath and pkgPath vars from the incoming param
531
380
  if (typeof fileParam === 'string') {
532
381
  fileParam = this.removePkgPrefix(fileParam);
533
382
  srcPath = (0, util_1.standardizePath) `${path.resolve(rootDir, fileParam)}`;
534
- destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
383
+ pkgPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
535
384
  }
536
385
  else {
537
386
  let param = fileParam;
@@ -542,30 +391,30 @@ class Program {
542
391
  srcPath = (0, util_1.standardizePath) `${param.srcPath}`;
543
392
  }
544
393
  if (param.dest) {
545
- destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.dest)}`;
394
+ pkgPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.dest)}`;
546
395
  }
547
396
  if (param.pkgPath) {
548
- destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.pkgPath)}`;
397
+ pkgPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.pkgPath)}`;
549
398
  }
550
399
  }
551
- //if there's no srcPath, use the destPath to build an absolute srcPath
400
+ //if there's no srcPath, use the pkgPath to build an absolute srcPath
552
401
  if (!srcPath) {
553
- srcPath = (0, util_1.standardizePath) `${rootDir}/${destPath}`;
402
+ srcPath = (0, util_1.standardizePath) `${rootDir}/${pkgPath}`;
554
403
  }
555
404
  //coerce srcPath to an absolute path
556
405
  if (!path.isAbsolute(srcPath)) {
557
406
  srcPath = util_1.util.standardizePath(srcPath);
558
407
  }
559
- //if destPath isn't set, compute it from the other paths
560
- if (!destPath) {
561
- destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
408
+ //if there's no pkgPath, compute relative path from rootDir
409
+ if (!pkgPath) {
410
+ pkgPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
562
411
  }
563
412
  assert.ok(srcPath, 'fileEntry.src is required');
564
- assert.ok(destPath, 'fileEntry.dest is required');
413
+ assert.ok(pkgPath, 'fileEntry.dest is required');
565
414
  return {
566
415
  srcPath: srcPath,
567
- //remove leading slash
568
- destPath: destPath.replace(/^[\/\\]+/, '')
416
+ //remove leading slash from pkgPath
417
+ pkgPath: pkgPath.replace(/^[\/\\]+/, '')
569
418
  };
570
419
  }
571
420
  /**
@@ -574,18 +423,6 @@ class Program {
574
423
  removePkgPrefix(path) {
575
424
  return path.replace(/^pkg:\//i, '');
576
425
  }
577
- /**
578
- * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
579
- */
580
- isSourceBrsFile(file) {
581
- return !!/^(pkg:\/)?source[\/\\]/.exec(file.destPath);
582
- }
583
- /**
584
- * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
585
- */
586
- isComponentsXmlFile(file) {
587
- return (0, reflection_1.isXmlFile)(file) && !!/^(pkg:\/)?components[\/\\]/.exec(file.destPath);
588
- }
589
426
  /**
590
427
  * Ensure source scope is created.
591
428
  * Note: automatically called internally, and no-op if it exists already.
@@ -595,12 +432,42 @@ class Program {
595
432
  const sourceScope = new Scope_1.Scope('source', this, 'scope:source');
596
433
  sourceScope.attachDependencyGraph(this.dependencyGraph);
597
434
  this.addScope(sourceScope);
598
- this.plugins.emit('afterScopeCreate', {
599
- program: this,
600
- scope: sourceScope
601
- });
435
+ this.plugins.emit('afterScopeCreate', sourceScope);
602
436
  }
603
437
  }
438
+ /**
439
+ * Find the file by its absolute path. This is case INSENSITIVE, since
440
+ * Roku is a case insensitive file system. It is an error to have multiple files
441
+ * with the same path with only case being different.
442
+ * @param srcPath the absolute path to the file
443
+ * @deprecated use `getFile` instead, which auto-detects the path type
444
+ */
445
+ getFileByPathAbsolute(srcPath) {
446
+ srcPath = (0, util_1.standardizePath) `${srcPath}`;
447
+ for (let filePath in this.files) {
448
+ if (filePath.toLowerCase() === srcPath.toLowerCase()) {
449
+ return this.files[filePath];
450
+ }
451
+ }
452
+ }
453
+ /**
454
+ * Get a list of files for the given (platform-normalized) pkgPath array.
455
+ * Missing files are just ignored.
456
+ * @deprecated use `getFiles` instead, which auto-detects the path types
457
+ */
458
+ getFilesByPkgPaths(pkgPaths) {
459
+ return pkgPaths
460
+ .map(pkgPath => this.getFileByPkgPath(pkgPath))
461
+ .filter(file => file !== undefined);
462
+ }
463
+ /**
464
+ * Get a file with the specified (platform-normalized) pkg path.
465
+ * If not found, return undefined
466
+ * @deprecated use `getFile` instead, which auto-detects the path type
467
+ */
468
+ getFileByPkgPath(pkgPath) {
469
+ return this.pkgMap[pkgPath.toLowerCase()];
470
+ }
604
471
  /**
605
472
  * Remove a set of files from the program
606
473
  * @param srcPaths can be an array of srcPath or destPath strings
@@ -613,197 +480,79 @@ class Program {
613
480
  }
614
481
  /**
615
482
  * Remove a file from the program
616
- * @param filePath can be a srcPath, a destPath, or a destPath with leading `pkg:/`
483
+ * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
617
484
  * @param normalizePath should this function repair and standardize the path? Passing false should have a performance boost if you can guarantee your path is already sanitized
618
485
  */
619
- removeFile(filePath, normalizePath = true, keepSymbolInformation = false) {
620
- var _a, _b, _c, _d;
486
+ removeFile(filePath, normalizePath = true) {
621
487
  this.logger.debug('Program.removeFile()', filePath);
622
- const paths = this.getPaths(filePath, this.options.rootDir);
623
- //there can be one or more File entries for a single srcPath, so get all of them and remove them all
624
- const files = (_b = this.fileClusters.get((_a = paths.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase())) !== null && _b !== void 0 ? _b : [this.getFile(filePath, normalizePath)];
625
- for (const file of files) {
626
- //if a file has already been removed, nothing more needs to be done here
627
- if (!file || !this.hasFile(file.srcPath)) {
628
- continue;
629
- }
630
- const event = { file: file, program: this };
631
- this.plugins.emit('beforeFileRemove', event);
488
+ let file = this.getFile(filePath, normalizePath);
489
+ if (file) {
490
+ this.plugins.emit('beforeFileDispose', file);
632
491
  //if there is a scope named the same as this file's path, remove it (i.e. xml scopes)
633
- let scope = this.scopes[file.destPath];
492
+ let scope = this.scopes[file.pkgPath];
634
493
  if (scope) {
635
- const scopeDisposeEvent = {
636
- program: this,
637
- scope: scope
638
- };
639
- this.plugins.emit('beforeScopeDispose', scopeDisposeEvent);
640
- this.plugins.emit('onScopeDispose', scopeDisposeEvent);
494
+ this.plugins.emit('beforeScopeDispose', scope);
641
495
  scope.dispose();
642
496
  //notify dependencies of this scope that it has been removed
643
497
  this.dependencyGraph.remove(scope.dependencyGraphKey);
644
- delete this.scopes[file.destPath];
645
- this.plugins.emit('afterScopeDispose', scopeDisposeEvent);
498
+ delete this.scopes[file.pkgPath];
499
+ this.plugins.emit('afterScopeDispose', scope);
646
500
  }
647
501
  //remove the file from the program
648
502
  this.unassignFile(file);
649
503
  this.dependencyGraph.remove(file.dependencyGraphKey);
650
504
  //if this is a pkg:/source file, notify the `source` scope that it has changed
651
- if (this.isSourceBrsFile(file)) {
505
+ if (file.pkgPath.startsWith(startOfSourcePkgPath)) {
652
506
  this.dependencyGraph.removeDependency('scope:source', file.dependencyGraphKey);
653
- if (!keepSymbolInformation) {
654
- this.fileSymbolInformation.delete(file.pkgPath);
655
- }
656
507
  }
657
508
  //if this is a component, remove it from our components map
658
509
  if ((0, reflection_1.isXmlFile)(file)) {
659
510
  this.unregisterComponent(file);
660
511
  }
661
- //dispose any disposable things on the file
662
- for (const disposable of (_c = file === null || file === void 0 ? void 0 : file.disposables) !== null && _c !== void 0 ? _c : []) {
663
- disposable();
664
- }
665
512
  //dispose file
666
- (_d = file === null || file === void 0 ? void 0 : file.dispose) === null || _d === void 0 ? void 0 : _d.call(file);
667
- this.plugins.emit('afterFileRemove', event);
513
+ file === null || file === void 0 ? void 0 : file.dispose();
514
+ this.plugins.emit('afterFileDispose', file);
668
515
  }
669
516
  }
670
517
  /**
671
518
  * Traverse the entire project, and validate all scopes
672
519
  */
673
520
  validate() {
674
- this.logger.time(Logger_1.LogLevel.log, ['Validating project'], () => {
675
- var _a, _b, _c, _d, _e, _f, _g;
521
+ this.logger.time(logging_1.LogLevel.log, ['Validating project'], () => {
522
+ var _a;
676
523
  this.diagnostics = [];
677
- const programValidateEvent = {
678
- program: this
679
- };
680
- this.plugins.emit('beforeProgramValidate', programValidateEvent);
681
- this.plugins.emit('onProgramValidate', programValidateEvent);
524
+ this.plugins.emit('beforeProgramValidate', this);
682
525
  //validate every file
683
- const brsFilesValidated = [];
684
526
  for (const file of Object.values(this.files)) {
685
527
  //for every unvalidated file, validate it
686
528
  if (!file.isValidated) {
687
- const validateFileEvent = {
529
+ this.plugins.emit('beforeFileValidate', {
688
530
  program: this,
689
531
  file: file
690
- };
691
- this.plugins.emit('beforeFileValidate', validateFileEvent);
532
+ });
692
533
  //emit an event to allow plugins to contribute to the file validation process
693
- this.plugins.emit('onFileValidate', validateFileEvent);
534
+ this.plugins.emit('onFileValidate', {
535
+ program: this,
536
+ file: file
537
+ });
538
+ //call file.validate() IF the file has that function defined
539
+ (_a = file.validate) === null || _a === void 0 ? void 0 : _a.call(file);
694
540
  file.isValidated = true;
695
- if ((0, reflection_1.isBrsFile)(file)) {
696
- brsFilesValidated.push(file);
697
- }
698
- this.plugins.emit('afterFileValidate', validateFileEvent);
699
- }
700
- }
701
- // build list of all changed symbols in each file that changed
702
- this.lastValidationInfo.clear();
703
- for (const file of brsFilesValidated) {
704
- const fileInfo = {
705
- symbolsNotDefinedInEveryScope: [],
706
- duplicateSymbolsInSameScope: [],
707
- symbolsNotConsistentAcrossScopes: []
708
- };
709
- const scopesToCheckForConsistency = this.getScopesForFile(file);
710
- for (const symbol of file.requiredSymbols) {
711
- let providedSymbolType;
712
- let scopesDefiningSymbol = [];
713
- let scopesAreInconsistent = false;
714
- for (const scope of scopesToCheckForConsistency) {
715
- let symbolFoundInScope = false;
716
- for (const scopeFile of scope.getAllFiles()) {
717
- if (!(0, reflection_1.isBrsFile)(scopeFile) || scopeFile.isTypedef || scopeFile.hasTypedef) {
718
- continue;
719
- }
720
- const lowerFirstSymbolName = (_b = (_a = symbol.typeChain) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.name.toLowerCase();
721
- let symbolInThisScope = (_d = (_c = scopeFile.providedSymbols.symbolMap) === null || _c === void 0 ? void 0 : _c.get(symbol.flags)) === null || _d === void 0 ? void 0 : _d.get(lowerFirstSymbolName);
722
- if (!symbolInThisScope && ((_e = symbol.containingNamespaces) === null || _e === void 0 ? void 0 : _e.length) > 0) {
723
- const fullNameWithNamespaces = (symbol.containingNamespaces.join('.') + '.' + lowerFirstSymbolName).toLowerCase();
724
- symbolInThisScope = (_g = (_f = scopeFile.providedSymbols.symbolMap) === null || _f === void 0 ? void 0 : _f.get(symbol.flags)) === null || _g === void 0 ? void 0 : _g.get(fullNameWithNamespaces);
725
- }
726
- if (symbolInThisScope) {
727
- if (symbolFoundInScope) {
728
- // this is duplicately defined!
729
- fileInfo.duplicateSymbolsInSameScope.push({ symbol: symbol, scope: scope });
730
- }
731
- else {
732
- symbolFoundInScope = true;
733
- scopesDefiningSymbol.push(scope);
734
- //check for consistency across scopes
735
- if (!providedSymbolType) {
736
- providedSymbolType = symbolInThisScope.type;
737
- }
738
- else {
739
- //get more general type
740
- if (providedSymbolType.isEqual(symbolInThisScope.type)) {
741
- //type in this scope is the same as one we're already checking
742
- }
743
- else if (providedSymbolType.isTypeCompatible(symbolInThisScope.type)) {
744
- //type in this scope is compatible with one we're storing. use most generic
745
- providedSymbolType = symbolInThisScope.type;
746
- }
747
- else if (symbolInThisScope.type.isTypeCompatible(providedSymbolType)) {
748
- // type we're storing is more generic that the type in this scope
749
- }
750
- else {
751
- // type in this scope is not compatible with other types for this symbol
752
- scopesAreInconsistent = true;
753
- }
754
- }
755
- }
756
- }
757
- }
758
- if (!symbolFoundInScope) {
759
- fileInfo.symbolsNotDefinedInEveryScope.push({ symbol: symbol, scope: scope });
760
- }
761
- }
762
- if (scopesAreInconsistent) {
763
- fileInfo.symbolsNotConsistentAcrossScopes.push({ symbol: symbol, scopes: scopesDefiningSymbol });
764
- }
541
+ this.plugins.emit('afterFileValidate', file);
765
542
  }
766
- this.lastValidationInfo.set(file.srcPath.toLowerCase(), fileInfo);
767
543
  }
768
- this.detectIncompatibleSymbolsAcrossScopes();
769
- // Build component types for any component that changes
770
- this.logger.time(Logger_1.LogLevel.info, ['Build component types'], () => {
771
- for (let { componentKey, componentName } of this.componentSymbolsToUpdate) {
772
- this.updateComponentSymbolInGlobalScope(componentKey, componentName);
773
- }
774
- this.componentSymbolsToUpdate.clear();
775
- });
776
- const changedSymbolsMapArr = brsFilesValidated === null || brsFilesValidated === void 0 ? void 0 : brsFilesValidated.map(f => {
777
- if ((0, reflection_1.isBrsFile)(f)) {
778
- return f.providedSymbols.changes;
779
- }
780
- return null;
781
- }).filter(x => x);
782
- const changedSymbols = new Map();
783
- for (const flag of [SymbolTable_1.SymbolTypeFlag.runtime, SymbolTable_1.SymbolTypeFlag.typetime]) {
784
- const changedSymbolsSetArr = changedSymbolsMapArr.map(symMap => symMap.get(flag));
785
- changedSymbols.set(flag, new Set(...changedSymbolsSetArr));
786
- }
787
- this.logger.time(Logger_1.LogLevel.info, ['Validate all scopes'], () => {
544
+ this.logger.time(logging_1.LogLevel.info, ['Validate all scopes'], () => {
788
545
  for (let scopeName in this.scopes) {
789
546
  let scope = this.scopes[scopeName];
790
- scope.validate({ changedFiles: brsFilesValidated, changedSymbols: changedSymbols });
547
+ scope.linkSymbolTable();
548
+ scope.validate();
549
+ scope.unlinkSymbolTable();
791
550
  }
792
551
  });
793
552
  this.detectDuplicateComponentNames();
794
- this.plugins.emit('afterProgramValidate', programValidateEvent);
553
+ this.plugins.emit('afterProgramValidate', this);
795
554
  });
796
555
  }
797
- detectIncompatibleSymbolsAcrossScopes() {
798
- for (const [lowerFilePath, fileInfo] of this.lastValidationInfo.entries()) {
799
- const file = this.files[lowerFilePath];
800
- for (const symbolAndScopes of fileInfo.symbolsNotConsistentAcrossScopes) {
801
- const typeChainResult = util_1.util.processTypeChain(symbolAndScopes.symbol.typeChain);
802
- const scopeListName = symbolAndScopes.scopes.map(s => s.name).join(', ');
803
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.incompatibleSymbolDefinition(typeChainResult.fullNameOfItem, scopeListName)), { file: file, range: typeChainResult.range }));
804
- }
805
- }
806
- }
807
556
  /**
808
557
  * Flag all duplicate component names
809
558
  */
@@ -856,13 +605,12 @@ class Program {
856
605
  getFile(filePath, normalizePath = true) {
857
606
  if (typeof filePath !== 'string') {
858
607
  return undefined;
859
- //is the path absolute (or the `virtual:` prefix)
860
608
  }
861
- else if (/^(?:(?:virtual:[\/\\])|(?:\w:)|(?:[\/\\]))/gmi.exec(filePath)) {
609
+ else if (path.isAbsolute(filePath)) {
862
610
  return this.files[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
863
611
  }
864
612
  else {
865
- return this.destMap.get((normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase());
613
+ return this.pkgMap[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
866
614
  }
867
615
  }
868
616
  /**
@@ -870,14 +618,12 @@ class Program {
870
618
  * @param file the file
871
619
  */
872
620
  getScopesForFile(file) {
873
- if (typeof file === 'string') {
874
- file = this.getFile(file);
875
- }
621
+ const resolvedFile = typeof file === 'string' ? this.getFile(file) : file;
876
622
  let result = [];
877
- if (file) {
623
+ if (resolvedFile) {
878
624
  for (let key in this.scopes) {
879
625
  let scope = this.scopes[key];
880
- if (scope.hasFile(file)) {
626
+ if (scope.hasFile(resolvedFile)) {
881
627
  result.push(scope);
882
628
  }
883
629
  }
@@ -904,8 +650,7 @@ class Program {
904
650
  //look through all files in scope for matches
905
651
  for (const scope of this.getScopesForFile(originFile)) {
906
652
  for (const file of scope.getAllFiles()) {
907
- //skip non-brs files, or files we've already processed
908
- if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
653
+ if ((0, reflection_1.isXmlFile)(file) || filesSearched.has(file)) {
909
654
  continue;
910
655
  }
911
656
  filesSearched.add(file);
@@ -929,7 +674,7 @@ class Program {
929
674
  let funcNames = new Set();
930
675
  let currentScope = scope;
931
676
  while ((0, reflection_1.isXmlScope)(currentScope)) {
932
- for (let name of (_b = (_a = currentScope.xmlFile.ast.componentElement.interfaceElement) === null || _a === void 0 ? void 0 : _a.functions.map((f) => f.name)) !== null && _b !== void 0 ? _b : []) {
677
+ for (let name of (_b = (_a = currentScope.xmlFile.ast.component.api) === null || _a === void 0 ? void 0 : _a.functions.map((f) => f.name)) !== null && _b !== void 0 ? _b : []) {
933
678
  if (!filterName || name === filterName) {
934
679
  funcNames.add(name);
935
680
  }
@@ -938,8 +683,7 @@ class Program {
938
683
  }
939
684
  //look through all files in scope for matches
940
685
  for (const file of scope.getOwnFiles()) {
941
- //skip non-brs files, or files we've already processed
942
- if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
686
+ if ((0, reflection_1.isXmlFile)(file) || filesSearched.has(file)) {
943
687
  continue;
944
688
  }
945
689
  filesSearched.add(file);
@@ -983,14 +727,14 @@ class Program {
983
727
  * Goes through each file and builds a list of workspace symbols for the program. Used by LanguageServer's onWorkspaceSymbol functionality
984
728
  */
985
729
  getWorkspaceSymbols() {
986
- const results = Object.keys(this.files).map(key => {
987
- const file = this.files[key];
988
- if ((0, reflection_1.isBrsFile)(file)) {
989
- return file.getWorkspaceSymbols();
990
- }
991
- return [];
992
- });
993
- return util_1.util.flatMap(results, c => c);
730
+ const event = {
731
+ program: this,
732
+ workspaceSymbols: []
733
+ };
734
+ this.plugins.emit('beforeProvideWorkspaceSymbols', event);
735
+ this.plugins.emit('provideWorkspaceSymbols', event);
736
+ this.plugins.emit('afterProvideWorkspaceSymbols', event);
737
+ return event.workspaceSymbols;
994
738
  }
995
739
  /**
996
740
  * Given a position in a file, if the position is sitting on some type of identifier,
@@ -1001,17 +745,16 @@ class Program {
1001
745
  if (!file) {
1002
746
  return [];
1003
747
  }
1004
- if ((0, reflection_1.isBrsFile)(file)) {
1005
- return file.getDefinition(position);
1006
- }
1007
- else {
1008
- let results = [];
1009
- const scopes = this.getScopesForFile(file);
1010
- for (const scope of scopes) {
1011
- results = results.concat(...scope.getDefinition(file, position));
1012
- }
1013
- return results;
1014
- }
748
+ const event = {
749
+ program: this,
750
+ file: file,
751
+ position: position,
752
+ definitions: []
753
+ };
754
+ this.plugins.emit('beforeProvideDefinition', event);
755
+ this.plugins.emit('provideDefinition', event);
756
+ this.plugins.emit('afterProvideDefinition', event);
757
+ return event.definitions;
1015
758
  }
1016
759
  /**
1017
760
  * Get hover information for a file and position
@@ -1034,6 +777,27 @@ class Program {
1034
777
  }
1035
778
  return result !== null && result !== void 0 ? result : [];
1036
779
  }
780
+ /**
781
+ * Get full list of document symbols for a file
782
+ * @param srcPath path to the file
783
+ */
784
+ getDocumentSymbols(srcPath) {
785
+ let file = this.getFile(srcPath);
786
+ if (file) {
787
+ const event = {
788
+ program: this,
789
+ file: file,
790
+ documentSymbols: []
791
+ };
792
+ this.plugins.emit('beforeProvideDocumentSymbols', event);
793
+ this.plugins.emit('provideDocumentSymbols', event);
794
+ this.plugins.emit('afterProvideDocumentSymbols', event);
795
+ return event.documentSymbols;
796
+ }
797
+ else {
798
+ return undefined;
799
+ }
800
+ }
1037
801
  /**
1038
802
  * Compute code actions for the given file and range
1039
803
  */
@@ -1088,12 +852,69 @@ class Program {
1088
852
  getReferences(srcPath, position) {
1089
853
  //find the file
1090
854
  let file = this.getFile(srcPath);
1091
- if ((0, reflection_1.isBrsFile)(file) || (0, reflection_1.isXmlFile)(file)) {
1092
- return file.getReferences(position);
1093
- }
1094
- else {
855
+ if (!file) {
1095
856
  return null;
1096
857
  }
858
+ const event = {
859
+ program: this,
860
+ file: file,
861
+ position: position,
862
+ references: []
863
+ };
864
+ this.plugins.emit('beforeProvideReferences', event);
865
+ this.plugins.emit('provideReferences', event);
866
+ this.plugins.emit('afterProvideReferences', event);
867
+ return event.references;
868
+ }
869
+ /**
870
+ * Get a list of all script imports, relative to the specified pkgPath
871
+ * @param sourcePkgPath - the pkgPath of the source that wants to resolve script imports.
872
+ */
873
+ getScriptImportCompletions(sourcePkgPath, scriptImport) {
874
+ let lowerSourcePkgPath = sourcePkgPath.toLowerCase();
875
+ let result = [];
876
+ /**
877
+ * hashtable to prevent duplicate results
878
+ */
879
+ let resultPkgPaths = {};
880
+ //restrict to only .brs files
881
+ for (let key in this.files) {
882
+ let file = this.files[key];
883
+ if (
884
+ //is a BrightScript or BrighterScript file
885
+ (file.extension === '.bs' || file.extension === '.brs') &&
886
+ //this file is not the current file
887
+ lowerSourcePkgPath !== file.pkgPath.toLowerCase()) {
888
+ //add the relative path
889
+ let relativePath = util_1.util.getRelativePath(sourcePkgPath, file.pkgPath).replace(/\\/g, '/');
890
+ let pkgPathStandardized = file.pkgPath.replace(/\\/g, '/');
891
+ let filePkgPath = `pkg:/${pkgPathStandardized}`;
892
+ let lowerFilePkgPath = filePkgPath.toLowerCase();
893
+ if (!resultPkgPaths[lowerFilePkgPath]) {
894
+ resultPkgPaths[lowerFilePkgPath] = true;
895
+ result.push({
896
+ label: relativePath,
897
+ detail: file.srcPath,
898
+ kind: vscode_languageserver_1.CompletionItemKind.File,
899
+ textEdit: {
900
+ newText: relativePath,
901
+ range: scriptImport.filePathRange
902
+ }
903
+ });
904
+ //add the absolute path
905
+ result.push({
906
+ label: filePkgPath,
907
+ detail: file.srcPath,
908
+ kind: vscode_languageserver_1.CompletionItemKind.File,
909
+ textEdit: {
910
+ newText: filePkgPath,
911
+ range: scriptImport.filePathRange
912
+ }
913
+ });
914
+ }
915
+ }
916
+ }
917
+ return result;
1097
918
  }
1098
919
  /**
1099
920
  * Transpile a single file and get the result as a string.
@@ -1104,211 +925,162 @@ class Program {
1104
925
  * @param filePath can be a srcPath or a destPath
1105
926
  */
1106
927
  async getTranspiledFileContents(filePath) {
1107
- const file = this.getFile(filePath);
1108
- return this.getTranspiledFileContentsPipeline.run(async () => {
1109
- const result = {
1110
- destPath: file.destPath,
1111
- pkgPath: file.pkgPath,
1112
- srcPath: file.srcPath
1113
- };
1114
- const expectedPkgPath = file.pkgPath.toLowerCase();
1115
- const expectedMapPath = `${expectedPkgPath}.map`;
1116
- const expectedTypedefPkgPath = expectedPkgPath.replace(/\.brs$/i, '.d.bs');
1117
- //add a temporary plugin to tap into the file writing process
1118
- const plugin = this.plugins.addFirst({
1119
- name: 'getTranspiledFileContents',
1120
- beforeWriteFile: (event) => {
1121
- const pkgPath = event.file.pkgPath.toLowerCase();
1122
- switch (pkgPath) {
1123
- //this is the actual transpiled file
1124
- case expectedPkgPath:
1125
- result.code = event.file.data.toString();
1126
- break;
1127
- //this is the sourcemap
1128
- case expectedMapPath:
1129
- result.map = event.file.data.toString();
1130
- break;
1131
- //this is the typedef
1132
- case expectedTypedefPkgPath:
1133
- result.typedef = event.file.data.toString();
1134
- break;
1135
- default:
1136
- //no idea what this file is. just ignore it
1137
- }
1138
- //mark every file as processed so it they don't get written to the output directory
1139
- event.processedFiles.add(event.file);
1140
- }
1141
- });
1142
- try {
1143
- //now that the plugin has been registered, run the build with just this file
1144
- await this.build({
1145
- files: [file]
1146
- });
1147
- }
1148
- finally {
1149
- this.plugins.remove(plugin);
928
+ let fileMap = await roku_deploy_1.rokuDeploy.getFilePaths(this.options.files, this.options.rootDir);
929
+ //remove files currently loaded in the program, we will transpile those instead (even if just for source maps)
930
+ let filteredFileMap = [];
931
+ for (let fileEntry of fileMap) {
932
+ if (this.hasFile(fileEntry.src) === false) {
933
+ filteredFileMap.push(fileEntry);
1150
934
  }
1151
- return result;
1152
- });
1153
- }
1154
- /**
1155
- * Get the absolute output path for a file
1156
- */
1157
- getOutputPath(file, stagingDir = this.getStagingDir()) {
1158
- return (0, util_1.standardizePath) `${stagingDir}/${file.pkgPath}`;
1159
- }
1160
- getStagingDir(stagingDir) {
1161
- var _a, _b;
1162
- let result = (_a = stagingDir !== null && stagingDir !== void 0 ? stagingDir : this.options.stagingDir) !== null && _a !== void 0 ? _a : this.options.stagingDir;
1163
- if (!result) {
1164
- result = roku_deploy_1.rokuDeploy.getOptions(this.options).stagingDir;
1165
935
  }
1166
- result = (0, util_1.standardizePath) `${path.resolve((_b = this.options.cwd) !== null && _b !== void 0 ? _b : process.cwd(), result !== null && result !== void 0 ? result : '/')}`;
936
+ const { entries, astEditor } = this.beforeProgramTranspile(fileMap, this.options.stagingDir);
937
+ const result = this._getTranspiledFileContents(this.getFile(filePath));
938
+ this.afterProgramTranspile(entries, astEditor);
1167
939
  return result;
1168
940
  }
1169
941
  /**
1170
- * Prepare the program for building
1171
- * @param files the list of files that should be prepared
942
+ * Internal function used to transpile files.
943
+ * This does not write anything to the file system
1172
944
  */
1173
- async prepare(files) {
1174
- const programEvent = {
945
+ _getTranspiledFileContents(file, outputPath) {
946
+ const editor = new AstEditor_1.AstEditor();
947
+ this.plugins.emit('beforeFileTranspile', {
1175
948
  program: this,
1176
- editor: this.editor,
1177
- files: files
1178
- };
1179
- //assign an editor to every file
1180
- for (const file of files) {
1181
- //if the file doesn't have an editor yet, assign one now
1182
- if (!file.editor) {
1183
- file.editor = new Editor_1.Editor();
1184
- }
1185
- }
1186
- files.sort((a, b) => {
1187
- if (a.pkgPath < b.pkgPath) {
1188
- return -1;
1189
- }
1190
- else if (a.pkgPath > b.pkgPath) {
1191
- return 1;
1192
- }
1193
- else {
1194
- return 1;
1195
- }
949
+ file: file,
950
+ outputPath: outputPath,
951
+ editor: editor
1196
952
  });
1197
- await this.plugins.emitAsync('beforePrepareProgram', programEvent);
1198
- await this.plugins.emitAsync('prepareProgram', programEvent);
1199
- const stagingDir = this.getStagingDir();
1200
- const entries = [];
1201
- for (const file of files) {
1202
- //if the file doesn't have an editor yet, assign one now
1203
- if (!file.editor) {
1204
- file.editor = new Editor_1.Editor();
1205
- }
1206
- const event = {
1207
- program: this,
1208
- file: file,
1209
- editor: file.editor,
1210
- outputPath: this.getOutputPath(file, stagingDir)
1211
- };
1212
- await this.plugins.emitAsync('beforePrepareFile', event);
1213
- await this.plugins.emitAsync('prepareFile', event);
1214
- await this.plugins.emitAsync('afterPrepareFile', event);
1215
- //TODO remove this in v1
1216
- entries.push(event);
953
+ //if we have any edits, assume the file needs to be transpiled
954
+ if (editor.hasChanges) {
955
+ //use the `editor` because it'll track the previous value for us and revert later on
956
+ editor.setProperty(file, 'needsTranspiled', true);
1217
957
  }
1218
- await this.plugins.emitAsync('afterPrepareProgram', programEvent);
1219
- return files;
1220
- }
1221
- /**
1222
- * Generate the contents of every file
1223
- */
1224
- async serialize(files) {
1225
- const allFiles = new Map();
1226
- const serializeProgramEvent = await this.plugins.emitAsync('beforeSerializeProgram', {
1227
- program: this,
1228
- files: files,
1229
- result: allFiles
1230
- });
1231
- await this.plugins.emitAsync('onSerializeProgram', {
1232
- program: this,
1233
- files: files,
1234
- result: allFiles
1235
- });
1236
- //sort the entries to make transpiling more deterministic
1237
- files = serializeProgramEvent.files.sort((a, b) => {
1238
- return a.srcPath < b.srcPath ? -1 : 1;
1239
- });
1240
- // serialize each file
1241
- for (const file of files) {
1242
- const event = {
1243
- program: this,
1244
- file: file,
1245
- result: allFiles
1246
- };
1247
- await this.plugins.emitAsync('beforeSerializeFile', event);
1248
- await this.plugins.emitAsync('serializeFile', event);
1249
- await this.plugins.emitAsync('afterSerializeFile', event);
958
+ //transpile the file
959
+ const result = file.transpile();
960
+ //generate the typedef if enabled
961
+ let typedef;
962
+ if ((0, reflection_1.isBrsFile)(file) && this.options.emitDefinitions) {
963
+ typedef = file.getTypedef();
1250
964
  }
1251
- this.plugins.emit('afterSerializeProgram', {
965
+ const event = {
1252
966
  program: this,
1253
- files: files,
1254
- result: allFiles
1255
- });
1256
- return allFiles;
967
+ file: file,
968
+ outputPath: outputPath,
969
+ editor: editor,
970
+ code: result.code,
971
+ map: result.map,
972
+ typedef: typedef
973
+ };
974
+ this.plugins.emit('afterFileTranspile', event);
975
+ //undo all `editor` edits that may have been applied to this file.
976
+ editor.undoAll();
977
+ return {
978
+ srcPath: file.srcPath,
979
+ pkgPath: file.pkgPath,
980
+ code: event.code,
981
+ map: event.map,
982
+ typedef: event.typedef
983
+ };
1257
984
  }
1258
- /**
1259
- * Write the entire project to disk
1260
- */
1261
- async write(stagingDir, files) {
1262
- const programEvent = await this.plugins.emitAsync('beforeWriteProgram', {
1263
- program: this,
1264
- files: files,
1265
- stagingDir: stagingDir
1266
- });
1267
- //empty the staging directory
1268
- await fsExtra.emptyDir(stagingDir);
1269
- const serializedFiles = [...files]
1270
- .map(([, serializedFiles]) => serializedFiles)
1271
- .flat();
1272
- //write all the files to disk (asynchronously)
1273
- await Promise.all(serializedFiles.map(async (file) => {
1274
- const event = await this.plugins.emitAsync('beforeWriteFile', {
1275
- program: this,
985
+ beforeProgramTranspile(fileEntries, stagingDir) {
986
+ // map fileEntries using their path as key, to avoid excessive "find()" operations
987
+ const mappedFileEntries = fileEntries.reduce((collection, entry) => {
988
+ collection[(0, util_1.standardizePath) `${entry.src}`] = entry;
989
+ return collection;
990
+ }, {});
991
+ const getOutputPath = (file) => {
992
+ let filePathObj = mappedFileEntries[(0, util_1.standardizePath) `${file.srcPath}`];
993
+ if (!filePathObj) {
994
+ //this file has been added in-memory, from a plugin, for example
995
+ filePathObj = {
996
+ //add an interpolated src path (since it doesn't actually exist in memory)
997
+ src: `bsc:/${file.pkgPath}`,
998
+ dest: file.pkgPath
999
+ };
1000
+ }
1001
+ //replace the file extension
1002
+ let outputPath = filePathObj.dest.replace(/\.bs$/gi, '.brs');
1003
+ //prepend the staging folder path
1004
+ outputPath = (0, util_1.standardizePath) `${stagingDir}/${outputPath}`;
1005
+ return outputPath;
1006
+ };
1007
+ const entries = Object.values(this.files).map(file => {
1008
+ return {
1276
1009
  file: file,
1277
- outputPath: this.getOutputPath(file, stagingDir),
1278
- processedFiles: new Set()
1279
- });
1280
- await this.plugins.emitAsync('writeFile', event);
1281
- await this.plugins.emitAsync('afterWriteFile', event);
1282
- }));
1283
- await this.plugins.emitAsync('afterWriteProgram', programEvent);
1010
+ outputPath: getOutputPath(file)
1011
+ };
1012
+ //sort the entries to make transpiling more deterministic
1013
+ }).sort((a, b) => {
1014
+ return a.file.srcPath < b.file.srcPath ? -1 : 1;
1015
+ });
1016
+ const astEditor = new AstEditor_1.AstEditor();
1017
+ this.plugins.emit('beforeProgramTranspile', this, entries, astEditor);
1018
+ return {
1019
+ entries: entries,
1020
+ getOutputPath: getOutputPath,
1021
+ astEditor: astEditor
1022
+ };
1284
1023
  }
1285
- /**
1286
- * Build the project. This transpiles/transforms/copies all files and moves them to the staging directory
1287
- * @param options the list of options used to build the program
1288
- */
1289
- async build(options) {
1290
- //run a single build at a time
1291
- await this.buildPipeline.run(async () => {
1292
- var _a;
1293
- const stagingDir = this.getStagingDir(options === null || options === void 0 ? void 0 : options.stagingDir);
1294
- const event = await this.plugins.emitAsync('beforeBuildProgram', {
1295
- program: this,
1296
- editor: this.editor,
1297
- files: (_a = options === null || options === void 0 ? void 0 : options.files) !== null && _a !== void 0 ? _a : Object.values(this.files)
1298
- });
1299
- //prepare the program (and files) for building
1300
- event.files = await this.prepare(event.files);
1301
- //stage the entire program
1302
- const serializedFilesByFile = await this.serialize(event.files);
1303
- await this.write(stagingDir, serializedFilesByFile);
1304
- await this.plugins.emitAsync('afterBuildProgram', event);
1305
- //undo all edits for the program
1306
- this.editor.undoAll();
1307
- //undo all edits for each file
1308
- for (const file of event.files) {
1309
- file.editor.undoAll();
1024
+ async transpile(fileEntries, stagingDir) {
1025
+ const { entries, getOutputPath, astEditor } = this.beforeProgramTranspile(fileEntries, stagingDir);
1026
+ const processedFiles = new Set();
1027
+ const transpileFile = async (srcPath, outputPath) => {
1028
+ //find the file in the program
1029
+ const file = this.getFile(srcPath);
1030
+ //mark this file as processed so we don't process it more than once
1031
+ processedFiles.add(outputPath === null || outputPath === void 0 ? void 0 : outputPath.toLowerCase());
1032
+ if (!this.options.pruneEmptyCodeFiles || !file.canBePruned) {
1033
+ //skip transpiling typedef files
1034
+ if ((0, reflection_1.isBrsFile)(file) && file.isTypedef) {
1035
+ return;
1036
+ }
1037
+ const fileTranspileResult = this._getTranspiledFileContents(file, outputPath);
1038
+ //make sure the full dir path exists
1039
+ await fsExtra.ensureDir(path.dirname(outputPath));
1040
+ if (await fsExtra.pathExists(outputPath)) {
1041
+ throw new Error(`Error while transpiling "${file.srcPath}". A file already exists at "${outputPath}" and will not be overwritten.`);
1042
+ }
1043
+ const writeMapPromise = fileTranspileResult.map ? fsExtra.writeFile(`${outputPath}.map`, fileTranspileResult.map.toString()) : null;
1044
+ await Promise.all([
1045
+ fsExtra.writeFile(outputPath, fileTranspileResult.code),
1046
+ writeMapPromise
1047
+ ]);
1048
+ if (fileTranspileResult.typedef) {
1049
+ const typedefPath = outputPath.replace(/\.brs$/i, '.d.bs');
1050
+ await fsExtra.writeFile(typedefPath, fileTranspileResult.typedef);
1051
+ }
1310
1052
  }
1053
+ };
1054
+ let promises = entries.map(async (entry) => {
1055
+ var _a;
1056
+ return transpileFile((_a = entry === null || entry === void 0 ? void 0 : entry.file) === null || _a === void 0 ? void 0 : _a.srcPath, entry.outputPath);
1311
1057
  });
1058
+ //if there's no bslib file already loaded into the program, copy it to the staging directory
1059
+ if (!this.getFile(bslibAliasedRokuModulesPkgPath) && !this.getFile((0, util_1.standardizePath) `source/bslib.brs`)) {
1060
+ promises.push(util_1.util.copyBslibToStaging(stagingDir, this.options.bslibDestinationDir));
1061
+ }
1062
+ await Promise.all(promises);
1063
+ //transpile any new files that plugins added since the start of this transpile process
1064
+ do {
1065
+ promises = [];
1066
+ for (const key in this.files) {
1067
+ const file = this.files[key];
1068
+ //this is a new file
1069
+ const outputPath = getOutputPath(file);
1070
+ if (!processedFiles.has(outputPath === null || outputPath === void 0 ? void 0 : outputPath.toLowerCase())) {
1071
+ promises.push(transpileFile(file === null || file === void 0 ? void 0 : file.srcPath, outputPath));
1072
+ }
1073
+ }
1074
+ if (promises.length > 0) {
1075
+ this.logger.info(`Transpiling ${promises.length} new files`);
1076
+ await Promise.all(promises);
1077
+ }
1078
+ } while (promises.length > 0);
1079
+ this.afterProgramTranspile(entries, astEditor);
1080
+ }
1081
+ afterProgramTranspile(entries, astEditor) {
1082
+ this.plugins.emit('afterProgramTranspile', this, entries, astEditor);
1083
+ astEditor.undoAll();
1312
1084
  }
1313
1085
  /**
1314
1086
  * Find a list of files in the program that have a function with the given name (case INsensitive)
@@ -1380,70 +1152,75 @@ class Program {
1380
1152
  return files;
1381
1153
  }
1382
1154
  /**
1383
- * Get a map of the manifest information
1155
+ * Modify a parsed manifest map by reading `bs_const` and injecting values from `options.manifest.bs_const`
1156
+ * @param parsedManifest The manifest map to read from and modify
1384
1157
  */
1385
- getManifest() {
1158
+ buildBsConstsIntoParsedManifest(parsedManifest) {
1386
1159
  var _a, _b;
1387
- if (!this._manifest) {
1388
- //load the manifest file.
1389
- //TODO update this to get the manifest from the files array or require it in the options...we shouldn't assume the location of the manifest
1390
- let manifestPath = path.join(this.options.rootDir, 'manifest');
1391
- let contents;
1392
- try {
1393
- //we only load this manifest once, so do it sync to improve speed downstream
1394
- contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1395
- let parsedManifest = (0, Manifest_1.parseManifest)(contents);
1396
- // Lift the bs_consts defined in the manifest
1397
- let bsConsts = (0, Manifest_1.getBsConst)(parsedManifest, false);
1398
- // Override or delete any bs_consts defined in the bs config
1399
- for (const key in (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.manifest) === null || _b === void 0 ? void 0 : _b.bs_const) {
1400
- const value = this.options.manifest.bs_const[key];
1401
- if (value === null) {
1402
- bsConsts.delete(key);
1403
- }
1404
- else {
1405
- bsConsts.set(key, value);
1406
- }
1407
- }
1408
- // convert the new list of bs consts back into a string for the rest of the down stream systems to use
1409
- let constString = '';
1410
- for (const [key, value] of bsConsts) {
1411
- constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
1412
- }
1413
- // Set the updated bs_const value
1414
- parsedManifest.set('bs_const', constString);
1415
- this._manifest = parsedManifest;
1160
+ // Lift the bs_consts defined in the manifest
1161
+ let bsConsts = (0, Manifest_1.getBsConst)(parsedManifest, false);
1162
+ // Override or delete any bs_consts defined in the bs config
1163
+ for (const key in (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.manifest) === null || _b === void 0 ? void 0 : _b.bs_const) {
1164
+ const value = this.options.manifest.bs_const[key];
1165
+ if (value === null) {
1166
+ bsConsts.delete(key);
1416
1167
  }
1417
- catch (err) {
1418
- this._manifest = new Map();
1168
+ else {
1169
+ bsConsts.set(key, value);
1419
1170
  }
1420
1171
  }
1172
+ // convert the new list of bs consts back into a string for the rest of the down stream systems to use
1173
+ let constString = '';
1174
+ for (const [key, value] of bsConsts) {
1175
+ constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
1176
+ }
1177
+ // Set the updated bs_const value
1178
+ parsedManifest.set('bs_const', constString);
1179
+ }
1180
+ /**
1181
+ * Try to find and load the manifest into memory
1182
+ * @param manifestFileObj A pointer to a potential manifest file object found during loading
1183
+ * @param replaceIfAlreadyLoaded should we overwrite the internal `_manifest` if it already exists
1184
+ */
1185
+ loadManifest(manifestFileObj, replaceIfAlreadyLoaded = true) {
1186
+ //if we already have a manifest instance, and should not replace...then don't replace
1187
+ if (!replaceIfAlreadyLoaded && this._manifest) {
1188
+ return;
1189
+ }
1190
+ let manifestPath = manifestFileObj
1191
+ ? manifestFileObj.src
1192
+ : path.join(this.options.rootDir, 'manifest');
1193
+ try {
1194
+ // we only load this manifest once, so do it sync to improve speed downstream
1195
+ const contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1196
+ const parsedManifest = (0, Manifest_1.parseManifest)(contents);
1197
+ this.buildBsConstsIntoParsedManifest(parsedManifest);
1198
+ this._manifest = parsedManifest;
1199
+ }
1200
+ catch (e) {
1201
+ this._manifest = new Map();
1202
+ }
1203
+ }
1204
+ /**
1205
+ * Get a map of the manifest information
1206
+ */
1207
+ getManifest() {
1208
+ if (!this._manifest) {
1209
+ this.loadManifest();
1210
+ }
1421
1211
  return this._manifest;
1422
1212
  }
1423
1213
  dispose() {
1424
- var _a, _b, _c, _d, _e, _f, _g, _h;
1425
1214
  this.plugins.emit('beforeProgramDispose', { program: this });
1426
1215
  for (let filePath in this.files) {
1427
- (_b = (_a = this.files[filePath]) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
1216
+ this.files[filePath].dispose();
1428
1217
  }
1429
1218
  for (let name in this.scopes) {
1430
- (_d = (_c = this.scopes[name]) === null || _c === void 0 ? void 0 : _c.dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
1219
+ this.scopes[name].dispose();
1431
1220
  }
1432
- (_f = (_e = this.globalScope) === null || _e === void 0 ? void 0 : _e.dispose) === null || _f === void 0 ? void 0 : _f.call(_e);
1433
- (_h = (_g = this.dependencyGraph) === null || _g === void 0 ? void 0 : _g.dispose) === null || _h === void 0 ? void 0 : _h.call(_g);
1221
+ this.globalScope.dispose();
1222
+ this.dependencyGraph.dispose();
1434
1223
  }
1435
1224
  }
1436
1225
  exports.Program = Program;
1437
- class ProvideFileEventInternal {
1438
- constructor(program, srcPath, destPath, data, fileFactory) {
1439
- var _a;
1440
- this.program = program;
1441
- this.srcPath = srcPath;
1442
- this.destPath = destPath;
1443
- this.data = data;
1444
- this.fileFactory = fileFactory;
1445
- this.files = [];
1446
- this.srcExtension = (_a = path.extname(srcPath)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
1447
- }
1448
- }
1449
1226
  //# sourceMappingURL=Program.js.map