brighterscript 1.0.0-alpha.5 → 1.0.0-alpha.50

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 (715) hide show
  1. package/README.md +76 -138
  2. package/bsconfig.schema.json +121 -5
  3. package/dist/ActionPipeline.d.ts +10 -0
  4. package/dist/ActionPipeline.js +40 -0
  5. package/dist/ActionPipeline.js.map +1 -0
  6. package/dist/AstValidationSegmenter.d.ts +45 -0
  7. package/dist/AstValidationSegmenter.js +322 -0
  8. package/dist/AstValidationSegmenter.js.map +1 -0
  9. package/dist/BsConfig.d.ts +72 -39
  10. package/dist/BusyStatusTracker.d.ts +61 -0
  11. package/dist/BusyStatusTracker.js +148 -0
  12. package/dist/BusyStatusTracker.js.map +1 -0
  13. package/dist/Cache.d.ts +3 -8
  14. package/dist/Cache.js +9 -14
  15. package/dist/Cache.js.map +1 -1
  16. package/dist/CacheVerifier.d.ts +7 -0
  17. package/dist/CacheVerifier.js +20 -0
  18. package/dist/CacheVerifier.js.map +1 -0
  19. package/dist/CodeActionUtil.d.ts +12 -4
  20. package/dist/CodeActionUtil.js +22 -5
  21. package/dist/CodeActionUtil.js.map +1 -1
  22. package/dist/CommentFlagProcessor.d.ts +7 -6
  23. package/dist/CommentFlagProcessor.js +11 -8
  24. package/dist/CommentFlagProcessor.js.map +1 -1
  25. package/dist/CrossScopeValidator.d.ts +68 -0
  26. package/dist/CrossScopeValidator.js +642 -0
  27. package/dist/CrossScopeValidator.js.map +1 -0
  28. package/dist/DependencyGraph.d.ts +8 -3
  29. package/dist/DependencyGraph.js +49 -16
  30. package/dist/DependencyGraph.js.map +1 -1
  31. package/dist/DiagnosticCollection.d.ts +21 -5
  32. package/dist/DiagnosticCollection.js +77 -24
  33. package/dist/DiagnosticCollection.js.map +1 -1
  34. package/dist/DiagnosticFilterer.d.ts +27 -6
  35. package/dist/DiagnosticFilterer.js +273 -60
  36. package/dist/DiagnosticFilterer.js.map +1 -1
  37. package/dist/DiagnosticManager.d.ts +82 -0
  38. package/dist/DiagnosticManager.js +406 -0
  39. package/dist/DiagnosticManager.js.map +1 -0
  40. package/dist/DiagnosticMessages.d.ts +558 -196
  41. package/dist/DiagnosticMessages.js +870 -340
  42. package/dist/DiagnosticMessages.js.map +1 -1
  43. package/dist/DiagnosticSeverityAdjuster.d.ts +7 -0
  44. package/dist/DiagnosticSeverityAdjuster.js +45 -0
  45. package/dist/DiagnosticSeverityAdjuster.js.map +1 -0
  46. package/dist/FunctionScope.d.ts +28 -0
  47. package/dist/FunctionScope.js +52 -0
  48. package/dist/FunctionScope.js.map +1 -0
  49. package/dist/KeyedThrottler.d.ts +3 -3
  50. package/dist/KeyedThrottler.js +3 -3
  51. package/dist/KeyedThrottler.js.map +1 -1
  52. package/dist/LanguageServer.d.ts +100 -105
  53. package/dist/LanguageServer.js +444 -745
  54. package/dist/LanguageServer.js.map +1 -1
  55. package/dist/Logger.d.ts +17 -13
  56. package/dist/Logger.js +64 -34
  57. package/dist/Logger.js.map +1 -1
  58. package/dist/PluginInterface.d.ts +32 -10
  59. package/dist/PluginInterface.js +117 -7
  60. package/dist/PluginInterface.js.map +1 -1
  61. package/dist/Program.d.ts +241 -98
  62. package/dist/Program.js +1432 -717
  63. package/dist/Program.js.map +1 -1
  64. package/dist/ProgramBuilder.d.ts +47 -23
  65. package/dist/ProgramBuilder.js +224 -178
  66. package/dist/ProgramBuilder.js.map +1 -1
  67. package/dist/Scope.d.ts +149 -109
  68. package/dist/Scope.js +557 -550
  69. package/dist/Scope.js.map +1 -1
  70. package/dist/SemanticTokenUtils.js +5 -1
  71. package/dist/SemanticTokenUtils.js.map +1 -1
  72. package/dist/Stopwatch.d.ts +4 -0
  73. package/dist/Stopwatch.js +8 -1
  74. package/dist/Stopwatch.js.map +1 -1
  75. package/dist/SymbolTable.d.ts +136 -24
  76. package/dist/SymbolTable.js +565 -64
  77. package/dist/SymbolTable.js.map +1 -1
  78. package/dist/SymbolTypeFlag.d.ts +9 -0
  79. package/dist/SymbolTypeFlag.js +14 -0
  80. package/dist/SymbolTypeFlag.js.map +1 -0
  81. package/dist/Throttler.d.ts +12 -0
  82. package/dist/Throttler.js +39 -0
  83. package/dist/Throttler.js.map +1 -1
  84. package/dist/Watcher.d.ts +0 -3
  85. package/dist/Watcher.js +0 -3
  86. package/dist/Watcher.js.map +1 -1
  87. package/dist/XmlScope.d.ts +5 -15
  88. package/dist/XmlScope.js +34 -90
  89. package/dist/XmlScope.js.map +1 -1
  90. package/dist/astUtils/CachedLookups.d.ts +50 -0
  91. package/dist/astUtils/CachedLookups.js +334 -0
  92. package/dist/astUtils/CachedLookups.js.map +1 -0
  93. package/dist/astUtils/CachedLookups.spec.js +39 -0
  94. package/dist/astUtils/CachedLookups.spec.js.map +1 -0
  95. package/dist/astUtils/Editor.d.ts +69 -0
  96. package/dist/astUtils/Editor.js +245 -0
  97. package/dist/astUtils/Editor.js.map +1 -0
  98. package/dist/astUtils/Editor.spec.js +258 -0
  99. package/dist/astUtils/Editor.spec.js.map +1 -0
  100. package/dist/astUtils/creators.d.ts +54 -19
  101. package/dist/astUtils/creators.js +242 -42
  102. package/dist/astUtils/creators.js.map +1 -1
  103. package/dist/astUtils/creators.spec.js +5 -5
  104. package/dist/astUtils/creators.spec.js.map +1 -1
  105. package/dist/astUtils/reflection.d.ts +196 -85
  106. package/dist/astUtils/reflection.js +497 -144
  107. package/dist/astUtils/reflection.js.map +1 -1
  108. package/dist/astUtils/reflection.spec.js +267 -167
  109. package/dist/astUtils/reflection.spec.js.map +1 -1
  110. package/dist/astUtils/stackedVisitor.js.map +1 -1
  111. package/dist/astUtils/stackedVisitor.spec.js +14 -14
  112. package/dist/astUtils/stackedVisitor.spec.js.map +1 -1
  113. package/dist/astUtils/visitors.d.ts +116 -53
  114. package/dist/astUtils/visitors.js +95 -15
  115. package/dist/astUtils/visitors.js.map +1 -1
  116. package/dist/astUtils/visitors.spec.js +629 -51
  117. package/dist/astUtils/visitors.spec.js.map +1 -1
  118. package/dist/astUtils/xml.d.ts +9 -8
  119. package/dist/astUtils/xml.js +12 -7
  120. package/dist/astUtils/xml.js.map +1 -1
  121. package/dist/bscPlugin/BscPlugin.d.ts +24 -4
  122. package/dist/bscPlugin/BscPlugin.js +88 -4
  123. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  124. package/dist/bscPlugin/CallExpressionInfo.d.ts +36 -0
  125. package/dist/bscPlugin/CallExpressionInfo.js +143 -0
  126. package/dist/bscPlugin/CallExpressionInfo.js.map +1 -0
  127. package/dist/bscPlugin/FileWriter.d.ts +6 -0
  128. package/dist/bscPlugin/FileWriter.js +24 -0
  129. package/dist/bscPlugin/FileWriter.js.map +1 -0
  130. package/dist/bscPlugin/SignatureHelpUtil.d.ts +10 -0
  131. package/dist/bscPlugin/SignatureHelpUtil.js +137 -0
  132. package/dist/bscPlugin/SignatureHelpUtil.js.map +1 -0
  133. package/dist/bscPlugin/codeActions/CodeActionsProcessor.d.ts +6 -5
  134. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +173 -27
  135. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  136. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +138 -21
  137. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  138. package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +65 -0
  139. package/dist/bscPlugin/completions/CompletionsProcessor.js +633 -0
  140. package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -0
  141. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js +2512 -0
  142. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js.map +1 -0
  143. package/dist/bscPlugin/definition/DefinitionProvider.d.ts +13 -0
  144. package/dist/bscPlugin/definition/DefinitionProvider.js +212 -0
  145. package/dist/bscPlugin/definition/DefinitionProvider.js.map +1 -0
  146. package/dist/bscPlugin/definition/DefinitionProvider.spec.js +87 -0
  147. package/dist/bscPlugin/definition/DefinitionProvider.spec.js.map +1 -0
  148. package/dist/bscPlugin/fileProviders/FileProvider.d.ts +9 -0
  149. package/dist/bscPlugin/fileProviders/FileProvider.js +51 -0
  150. package/dist/bscPlugin/fileProviders/FileProvider.js.map +1 -0
  151. package/dist/bscPlugin/hover/HoverProcessor.d.ts +18 -0
  152. package/dist/bscPlugin/hover/HoverProcessor.js +230 -0
  153. package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -0
  154. package/dist/bscPlugin/hover/HoverProcessor.spec.js +991 -0
  155. package/dist/bscPlugin/hover/HoverProcessor.spec.js.map +1 -0
  156. package/dist/bscPlugin/references/ReferencesProvider.d.ts +12 -0
  157. package/dist/bscPlugin/references/ReferencesProvider.js +57 -0
  158. package/dist/bscPlugin/references/ReferencesProvider.js.map +1 -0
  159. package/dist/bscPlugin/references/ReferencesProvider.spec.d.ts +1 -0
  160. package/dist/bscPlugin/references/ReferencesProvider.spec.js +51 -0
  161. package/dist/bscPlugin/references/ReferencesProvider.spec.js.map +1 -0
  162. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +14 -0
  163. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +164 -0
  164. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -0
  165. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.d.ts +1 -0
  166. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js +564 -0
  167. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -0
  168. package/dist/bscPlugin/serialize/BslibInjector.spec.d.ts +1 -0
  169. package/dist/bscPlugin/serialize/BslibInjector.spec.js +33 -0
  170. package/dist/bscPlugin/serialize/BslibInjector.spec.js.map +1 -0
  171. package/dist/bscPlugin/serialize/BslibManager.d.ts +12 -0
  172. package/dist/bscPlugin/serialize/BslibManager.js +46 -0
  173. package/dist/bscPlugin/serialize/BslibManager.js.map +1 -0
  174. package/dist/bscPlugin/serialize/FileSerializer.d.ts +9 -0
  175. package/dist/bscPlugin/serialize/FileSerializer.js +75 -0
  176. package/dist/bscPlugin/serialize/FileSerializer.js.map +1 -0
  177. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.d.ts +7 -0
  178. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js +22 -0
  179. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js.map +1 -0
  180. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.d.ts +1 -0
  181. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.js +291 -0
  182. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.js.map +1 -0
  183. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.d.ts +7 -0
  184. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js +26 -0
  185. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js.map +1 -0
  186. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.d.ts +1 -0
  187. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.js +245 -0
  188. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.js.map +1 -0
  189. package/dist/bscPlugin/symbols/symbolUtils.d.ts +5 -0
  190. package/dist/bscPlugin/symbols/symbolUtils.js +141 -0
  191. package/dist/bscPlugin/symbols/symbolUtils.js.map +1 -0
  192. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.d.ts +27 -0
  193. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js +418 -0
  194. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +1 -0
  195. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.d.ts +1 -0
  196. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js +75 -0
  197. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js.map +1 -0
  198. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.d.ts +12 -0
  199. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js +99 -0
  200. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js.map +1 -0
  201. package/dist/bscPlugin/validation/BrsFileAfterValidator.d.ts +7 -0
  202. package/dist/bscPlugin/validation/BrsFileAfterValidator.js +18 -0
  203. package/dist/bscPlugin/validation/BrsFileAfterValidator.js.map +1 -0
  204. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +37 -0
  205. package/dist/bscPlugin/validation/BrsFileValidator.js +638 -0
  206. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -0
  207. package/dist/bscPlugin/validation/BrsFileValidator.spec.d.ts +1 -0
  208. package/dist/bscPlugin/validation/BrsFileValidator.spec.js +1517 -0
  209. package/dist/bscPlugin/validation/BrsFileValidator.spec.js.map +1 -0
  210. package/dist/bscPlugin/validation/ProgramValidator.d.ts +11 -0
  211. package/dist/bscPlugin/validation/ProgramValidator.js +33 -0
  212. package/dist/bscPlugin/validation/ProgramValidator.js.map +1 -0
  213. package/dist/bscPlugin/validation/ScopeValidator.d.ts +141 -0
  214. package/dist/bscPlugin/validation/ScopeValidator.js +1323 -0
  215. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -0
  216. package/dist/bscPlugin/validation/ScopeValidator.spec.d.ts +1 -0
  217. package/dist/bscPlugin/validation/ScopeValidator.spec.js +6135 -0
  218. package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +1 -0
  219. package/dist/bscPlugin/validation/XmlFileValidator.d.ts +8 -0
  220. package/dist/bscPlugin/validation/XmlFileValidator.js +36 -0
  221. package/dist/bscPlugin/validation/XmlFileValidator.js.map +1 -0
  222. package/dist/cli.js +126 -27
  223. package/dist/cli.js.map +1 -1
  224. package/dist/common/Sequencer.d.ts +53 -0
  225. package/dist/common/Sequencer.js +233 -0
  226. package/dist/common/Sequencer.js.map +1 -0
  227. package/dist/common/Sequencer.spec.d.ts +1 -0
  228. package/dist/common/Sequencer.spec.js +75 -0
  229. package/dist/common/Sequencer.spec.js.map +1 -0
  230. package/dist/deferred.d.ts +5 -3
  231. package/dist/deferred.js +10 -0
  232. package/dist/deferred.js.map +1 -1
  233. package/dist/diagnosticUtils.d.ts +10 -3
  234. package/dist/diagnosticUtils.js +64 -25
  235. package/dist/diagnosticUtils.js.map +1 -1
  236. package/dist/examples/plugins/removePrint.d.ts +2 -2
  237. package/dist/examples/plugins/removePrint.js +8 -12
  238. package/dist/examples/plugins/removePrint.js.map +1 -1
  239. package/dist/files/AssetFile.d.ts +24 -0
  240. package/dist/files/AssetFile.js +25 -0
  241. package/dist/files/AssetFile.js.map +1 -0
  242. package/dist/files/BrsFile.Class.spec.js +1213 -259
  243. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  244. package/dist/files/BrsFile.d.ts +145 -87
  245. package/dist/files/BrsFile.js +836 -934
  246. package/dist/files/BrsFile.js.map +1 -1
  247. package/dist/files/BrsFile.spec.js +4226 -902
  248. package/dist/files/BrsFile.spec.js.map +1 -1
  249. package/dist/files/BscFile.d.ts +102 -0
  250. package/dist/files/BscFile.js +15 -0
  251. package/dist/files/BscFile.js.map +1 -0
  252. package/dist/files/Factory.d.ts +25 -0
  253. package/dist/files/Factory.js +22 -0
  254. package/dist/files/Factory.js.map +1 -0
  255. package/dist/files/LazyFileData.d.ts +21 -0
  256. package/dist/files/LazyFileData.js +54 -0
  257. package/dist/files/LazyFileData.js.map +1 -0
  258. package/dist/files/LazyFileData.spec.d.ts +1 -0
  259. package/dist/files/LazyFileData.spec.js +27 -0
  260. package/dist/files/LazyFileData.spec.js.map +1 -0
  261. package/dist/files/XmlFile.d.ts +80 -41
  262. package/dist/files/XmlFile.js +161 -137
  263. package/dist/files/XmlFile.js.map +1 -1
  264. package/dist/files/XmlFile.spec.js +444 -336
  265. package/dist/files/XmlFile.spec.js.map +1 -1
  266. package/dist/files/tests/imports.spec.js +62 -52
  267. package/dist/files/tests/imports.spec.js.map +1 -1
  268. package/dist/files/tests/optionalChaning.spec.d.ts +1 -0
  269. package/dist/files/tests/optionalChaning.spec.js +152 -0
  270. package/dist/files/tests/optionalChaning.spec.js.map +1 -0
  271. package/dist/globalCallables.d.ts +3 -1
  272. package/dist/globalCallables.js +424 -184
  273. package/dist/globalCallables.js.map +1 -1
  274. package/dist/index.d.ts +32 -4
  275. package/dist/index.js +54 -7
  276. package/dist/index.js.map +1 -1
  277. package/dist/interfaces.d.ts +942 -125
  278. package/dist/interfaces.js +21 -0
  279. package/dist/interfaces.js.map +1 -1
  280. package/dist/lexer/Character.spec.js +5 -5
  281. package/dist/lexer/Character.spec.js.map +1 -1
  282. package/dist/lexer/Lexer.d.ts +51 -12
  283. package/dist/lexer/Lexer.js +215 -65
  284. package/dist/lexer/Lexer.js.map +1 -1
  285. package/dist/lexer/Lexer.spec.js +812 -568
  286. package/dist/lexer/Lexer.spec.js.map +1 -1
  287. package/dist/lexer/Token.d.ts +27 -11
  288. package/dist/lexer/Token.js +10 -2
  289. package/dist/lexer/Token.js.map +1 -1
  290. package/dist/lexer/TokenKind.d.ts +40 -2
  291. package/dist/lexer/TokenKind.js +147 -10
  292. package/dist/lexer/TokenKind.js.map +1 -1
  293. package/dist/logging.d.ts +14 -0
  294. package/dist/logging.js +29 -0
  295. package/dist/logging.js.map +1 -0
  296. package/dist/lsp/ActionQueue.d.ts +35 -0
  297. package/dist/lsp/ActionQueue.js +115 -0
  298. package/dist/lsp/ActionQueue.js.map +1 -0
  299. package/dist/lsp/ActionQueue.spec.d.ts +1 -0
  300. package/dist/lsp/ActionQueue.spec.js +80 -0
  301. package/dist/lsp/ActionQueue.spec.js.map +1 -0
  302. package/dist/lsp/DocumentManager.d.ts +63 -0
  303. package/dist/lsp/DocumentManager.js +122 -0
  304. package/dist/lsp/DocumentManager.js.map +1 -0
  305. package/dist/lsp/DocumentManager.spec.d.ts +1 -0
  306. package/dist/lsp/DocumentManager.spec.js +103 -0
  307. package/dist/lsp/DocumentManager.spec.js.map +1 -0
  308. package/dist/lsp/LspProject.d.ts +239 -0
  309. package/dist/lsp/LspProject.js +3 -0
  310. package/dist/lsp/LspProject.js.map +1 -0
  311. package/dist/lsp/PathFilterer.d.ts +75 -0
  312. package/dist/lsp/PathFilterer.js +196 -0
  313. package/dist/lsp/PathFilterer.js.map +1 -0
  314. package/dist/lsp/PathFilterer.spec.d.ts +1 -0
  315. package/dist/lsp/PathFilterer.spec.js +182 -0
  316. package/dist/lsp/PathFilterer.spec.js.map +1 -0
  317. package/dist/lsp/Project.d.ts +168 -0
  318. package/dist/lsp/Project.js +437 -0
  319. package/dist/lsp/Project.js.map +1 -0
  320. package/dist/lsp/Project.spec.d.ts +1 -0
  321. package/dist/lsp/Project.spec.js +267 -0
  322. package/dist/lsp/Project.spec.js.map +1 -0
  323. package/dist/lsp/ProjectManager.d.ts +242 -0
  324. package/dist/lsp/ProjectManager.js +824 -0
  325. package/dist/lsp/ProjectManager.js.map +1 -0
  326. package/dist/lsp/ProjectManager.spec.d.ts +1 -0
  327. package/dist/lsp/ProjectManager.spec.js +913 -0
  328. package/dist/lsp/ProjectManager.spec.js.map +1 -0
  329. package/dist/lsp/ReaderWriterManager.d.ts +21 -0
  330. package/dist/lsp/ReaderWriterManager.js +60 -0
  331. package/dist/lsp/ReaderWriterManager.js.map +1 -0
  332. package/dist/lsp/worker/MessageHandler.d.ts +99 -0
  333. package/dist/lsp/worker/MessageHandler.js +138 -0
  334. package/dist/lsp/worker/MessageHandler.js.map +1 -0
  335. package/dist/lsp/worker/MessageHandler.spec.d.ts +1 -0
  336. package/dist/lsp/worker/MessageHandler.spec.js +64 -0
  337. package/dist/lsp/worker/MessageHandler.spec.js.map +1 -0
  338. package/dist/lsp/worker/WorkerPool.d.ts +38 -0
  339. package/dist/lsp/worker/WorkerPool.js +78 -0
  340. package/dist/lsp/worker/WorkerPool.js.map +1 -0
  341. package/dist/lsp/worker/WorkerPool.spec.d.ts +1 -0
  342. package/dist/lsp/worker/WorkerPool.spec.js +59 -0
  343. package/dist/lsp/worker/WorkerPool.spec.js.map +1 -0
  344. package/dist/lsp/worker/WorkerThreadProject.d.ts +143 -0
  345. package/dist/lsp/worker/WorkerThreadProject.js +189 -0
  346. package/dist/lsp/worker/WorkerThreadProject.js.map +1 -0
  347. package/dist/lsp/worker/WorkerThreadProject.spec.d.ts +2 -0
  348. package/dist/lsp/worker/WorkerThreadProject.spec.js +71 -0
  349. package/dist/lsp/worker/WorkerThreadProject.spec.js.map +1 -0
  350. package/dist/lsp/worker/WorkerThreadProjectRunner.d.ts +15 -0
  351. package/dist/lsp/worker/WorkerThreadProjectRunner.js +58 -0
  352. package/dist/lsp/worker/WorkerThreadProjectRunner.js.map +1 -0
  353. package/dist/lsp/worker/run.d.ts +1 -0
  354. package/dist/lsp/worker/run.js +14 -0
  355. package/dist/lsp/worker/run.js.map +1 -0
  356. package/dist/parser/AstNode.d.ts +203 -0
  357. package/dist/parser/AstNode.js +303 -0
  358. package/dist/parser/AstNode.js.map +1 -0
  359. package/dist/parser/AstNode.spec.d.ts +1 -0
  360. package/dist/parser/AstNode.spec.js +1455 -0
  361. package/dist/parser/AstNode.spec.js.map +1 -0
  362. package/dist/parser/BrightScriptDocParser.d.ts +56 -0
  363. package/dist/parser/BrightScriptDocParser.js +294 -0
  364. package/dist/parser/BrightScriptDocParser.js.map +1 -0
  365. package/dist/parser/BrightScriptDocParser.spec.d.ts +1 -0
  366. package/dist/parser/BrightScriptDocParser.spec.js +310 -0
  367. package/dist/parser/BrightScriptDocParser.spec.js.map +1 -0
  368. package/dist/parser/BrsTranspileState.d.ts +22 -3
  369. package/dist/parser/BrsTranspileState.js +19 -0
  370. package/dist/parser/BrsTranspileState.js.map +1 -1
  371. package/dist/parser/Expression.d.ts +553 -221
  372. package/dist/parser/Expression.js +1414 -505
  373. package/dist/parser/Expression.js.map +1 -1
  374. package/dist/parser/Expression.spec.d.ts +1 -0
  375. package/dist/parser/Expression.spec.js +40 -0
  376. package/dist/parser/Expression.spec.js.map +1 -0
  377. package/dist/parser/Parser.Class.spec.js +255 -125
  378. package/dist/parser/Parser.Class.spec.js.map +1 -1
  379. package/dist/parser/Parser.d.ts +117 -124
  380. package/dist/parser/Parser.js +1669 -982
  381. package/dist/parser/Parser.js.map +1 -1
  382. package/dist/parser/Parser.spec.d.ts +3 -1
  383. package/dist/parser/Parser.spec.js +2111 -525
  384. package/dist/parser/Parser.spec.js.map +1 -1
  385. package/dist/parser/SGParser.d.ts +29 -13
  386. package/dist/parser/SGParser.js +85 -56
  387. package/dist/parser/SGParser.js.map +1 -1
  388. package/dist/parser/SGParser.spec.js +30 -45
  389. package/dist/parser/SGParser.spec.js.map +1 -1
  390. package/dist/parser/SGTypes.d.ts +134 -46
  391. package/dist/parser/SGTypes.js +206 -115
  392. package/dist/parser/SGTypes.js.map +1 -1
  393. package/dist/parser/Statement.d.ts +849 -267
  394. package/dist/parser/Statement.js +2412 -625
  395. package/dist/parser/Statement.js.map +1 -1
  396. package/dist/parser/Statement.spec.js +133 -36
  397. package/dist/parser/Statement.spec.js.map +1 -1
  398. package/dist/parser/TranspileState.d.ts +26 -12
  399. package/dist/parser/TranspileState.js +115 -24
  400. package/dist/parser/TranspileState.js.map +1 -1
  401. package/dist/parser/tests/Parser.spec.d.ts +3 -9
  402. package/dist/parser/tests/Parser.spec.js +7 -13
  403. package/dist/parser/tests/Parser.spec.js.map +1 -1
  404. package/dist/parser/tests/controlFlow/For.spec.js +83 -75
  405. package/dist/parser/tests/controlFlow/For.spec.js.map +1 -1
  406. package/dist/parser/tests/controlFlow/ForEach.spec.js +85 -51
  407. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  408. package/dist/parser/tests/controlFlow/If.spec.js +382 -239
  409. package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
  410. package/dist/parser/tests/controlFlow/While.spec.js +52 -45
  411. package/dist/parser/tests/controlFlow/While.spec.js.map +1 -1
  412. package/dist/parser/tests/expression/Additive.spec.js +51 -43
  413. package/dist/parser/tests/expression/Additive.spec.js.map +1 -1
  414. package/dist/parser/tests/expression/ArrayLiterals.spec.js +192 -142
  415. package/dist/parser/tests/expression/ArrayLiterals.spec.js.map +1 -1
  416. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +236 -160
  417. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
  418. package/dist/parser/tests/expression/Boolean.spec.js +41 -34
  419. package/dist/parser/tests/expression/Boolean.spec.js.map +1 -1
  420. package/dist/parser/tests/expression/Call.spec.js +173 -55
  421. package/dist/parser/tests/expression/Call.spec.js.map +1 -1
  422. package/dist/parser/tests/expression/Exponential.spec.js +20 -20
  423. package/dist/parser/tests/expression/Exponential.spec.js.map +1 -1
  424. package/dist/parser/tests/expression/Function.spec.js +291 -282
  425. package/dist/parser/tests/expression/Function.spec.js.map +1 -1
  426. package/dist/parser/tests/expression/Indexing.spec.js +193 -110
  427. package/dist/parser/tests/expression/Indexing.spec.js.map +1 -1
  428. package/dist/parser/tests/expression/Multiplicative.spec.js +42 -42
  429. package/dist/parser/tests/expression/Multiplicative.spec.js.map +1 -1
  430. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +260 -115
  431. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  432. package/dist/parser/tests/expression/PrefixUnary.spec.js +58 -52
  433. package/dist/parser/tests/expression/PrefixUnary.spec.js.map +1 -1
  434. package/dist/parser/tests/expression/Primary.spec.js +76 -60
  435. package/dist/parser/tests/expression/Primary.spec.js.map +1 -1
  436. package/dist/parser/tests/expression/RegexLiteralExpression.spec.d.ts +1 -0
  437. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +171 -0
  438. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -0
  439. package/dist/parser/tests/expression/Relational.spec.js +50 -50
  440. package/dist/parser/tests/expression/Relational.spec.js.map +1 -1
  441. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js +31 -31
  442. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js.map +1 -1
  443. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +281 -94
  444. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  445. package/dist/parser/tests/expression/TernaryExpression.spec.js +747 -192
  446. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  447. package/dist/parser/tests/expression/TypeExpression.spec.d.ts +1 -0
  448. package/dist/parser/tests/expression/TypeExpression.spec.js +126 -0
  449. package/dist/parser/tests/expression/TypeExpression.spec.js.map +1 -0
  450. package/dist/parser/tests/expression/UnaryExpression.spec.d.ts +1 -0
  451. package/dist/parser/tests/expression/UnaryExpression.spec.js +52 -0
  452. package/dist/parser/tests/expression/UnaryExpression.spec.js.map +1 -0
  453. package/dist/parser/tests/statement/AssignmentOperators.spec.js +44 -44
  454. package/dist/parser/tests/statement/AssignmentOperators.spec.js.map +1 -1
  455. package/dist/parser/tests/statement/ConstStatement.spec.d.ts +1 -0
  456. package/dist/parser/tests/statement/ConstStatement.spec.js +500 -0
  457. package/dist/parser/tests/statement/ConstStatement.spec.js.map +1 -0
  458. package/dist/parser/tests/statement/Continue.spec.d.ts +1 -0
  459. package/dist/parser/tests/statement/Continue.spec.js +119 -0
  460. package/dist/parser/tests/statement/Continue.spec.js.map +1 -0
  461. package/dist/parser/tests/statement/Declaration.spec.js +61 -55
  462. package/dist/parser/tests/statement/Declaration.spec.js.map +1 -1
  463. package/dist/parser/tests/statement/Dim.spec.js +29 -22
  464. package/dist/parser/tests/statement/Dim.spec.js.map +1 -1
  465. package/dist/parser/tests/statement/Enum.spec.d.ts +1 -0
  466. package/dist/parser/tests/statement/Enum.spec.js +744 -0
  467. package/dist/parser/tests/statement/Enum.spec.js.map +1 -0
  468. package/dist/parser/tests/statement/For.spec.d.ts +1 -0
  469. package/dist/parser/tests/statement/For.spec.js +45 -0
  470. package/dist/parser/tests/statement/For.spec.js.map +1 -0
  471. package/dist/parser/tests/statement/ForEach.spec.d.ts +1 -0
  472. package/dist/parser/tests/statement/ForEach.spec.js +36 -0
  473. package/dist/parser/tests/statement/ForEach.spec.js.map +1 -0
  474. package/dist/parser/tests/statement/Function.spec.js +226 -215
  475. package/dist/parser/tests/statement/Function.spec.js.map +1 -1
  476. package/dist/parser/tests/statement/Goto.spec.js +16 -15
  477. package/dist/parser/tests/statement/Goto.spec.js.map +1 -1
  478. package/dist/parser/tests/statement/Increment.spec.js +64 -61
  479. package/dist/parser/tests/statement/Increment.spec.js.map +1 -1
  480. package/dist/parser/tests/statement/InterfaceStatement.spec.d.ts +1 -0
  481. package/dist/parser/tests/statement/InterfaceStatement.spec.js +110 -0
  482. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -0
  483. package/dist/parser/tests/statement/LibraryStatement.spec.js +22 -22
  484. package/dist/parser/tests/statement/LibraryStatement.spec.js.map +1 -1
  485. package/dist/parser/tests/statement/Misc.spec.js +127 -168
  486. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  487. package/dist/parser/tests/statement/PrintStatement.spec.js +133 -114
  488. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  489. package/dist/parser/tests/statement/ReturnStatement.spec.js +57 -54
  490. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +1 -1
  491. package/dist/parser/tests/statement/Set.spec.js +131 -117
  492. package/dist/parser/tests/statement/Set.spec.js.map +1 -1
  493. package/dist/parser/tests/statement/Stop.spec.js +14 -13
  494. package/dist/parser/tests/statement/Stop.spec.js.map +1 -1
  495. package/dist/parser/tests/statement/Throw.spec.js +11 -8
  496. package/dist/parser/tests/statement/Throw.spec.js.map +1 -1
  497. package/dist/parser/tests/statement/TryCatch.spec.js +26 -15
  498. package/dist/parser/tests/statement/TryCatch.spec.js.map +1 -1
  499. package/dist/preprocessor/Manifest.d.ts +6 -6
  500. package/dist/preprocessor/Manifest.js +17 -38
  501. package/dist/preprocessor/Manifest.js.map +1 -1
  502. package/dist/preprocessor/Manifest.spec.d.ts +1 -0
  503. package/dist/preprocessor/Manifest.spec.js +78 -103
  504. package/dist/preprocessor/Manifest.spec.js.map +1 -1
  505. package/dist/roku-types/data.json +20347 -0
  506. package/dist/roku-types/index.d.ts +5726 -0
  507. package/dist/roku-types/index.js +11 -0
  508. package/dist/roku-types/index.js.map +1 -0
  509. package/dist/types/ArrayType.d.ts +12 -5
  510. package/dist/types/ArrayType.js +89 -24
  511. package/dist/types/ArrayType.js.map +1 -1
  512. package/dist/types/ArrayType.spec.js +39 -11
  513. package/dist/types/ArrayType.spec.js.map +1 -1
  514. package/dist/types/AssociativeArrayType.d.ts +15 -0
  515. package/dist/types/AssociativeArrayType.js +64 -0
  516. package/dist/types/AssociativeArrayType.js.map +1 -0
  517. package/dist/types/BaseFunctionType.d.ts +10 -0
  518. package/dist/types/BaseFunctionType.js +26 -0
  519. package/dist/types/BaseFunctionType.js.map +1 -0
  520. package/dist/types/BooleanType.d.ts +9 -5
  521. package/dist/types/BooleanType.js +19 -8
  522. package/dist/types/BooleanType.js.map +1 -1
  523. package/dist/types/BooleanType.spec.js +10 -4
  524. package/dist/types/BooleanType.spec.js.map +1 -1
  525. package/dist/types/BscType.d.ts +41 -3
  526. package/dist/types/BscType.js +152 -0
  527. package/dist/types/BscType.js.map +1 -1
  528. package/dist/types/BscTypeKind.d.ts +28 -0
  529. package/dist/types/BscTypeKind.js +33 -0
  530. package/dist/types/BscTypeKind.js.map +1 -0
  531. package/dist/types/BuiltInInterfaceAdder.d.ts +28 -0
  532. package/dist/types/BuiltInInterfaceAdder.js +212 -0
  533. package/dist/types/BuiltInInterfaceAdder.js.map +1 -0
  534. package/dist/types/BuiltInInterfaceAdder.spec.d.ts +1 -0
  535. package/dist/types/BuiltInInterfaceAdder.spec.js +115 -0
  536. package/dist/types/BuiltInInterfaceAdder.spec.js.map +1 -0
  537. package/dist/types/CallFuncableType.d.ts +24 -0
  538. package/dist/types/CallFuncableType.js +91 -0
  539. package/dist/types/CallFuncableType.js.map +1 -0
  540. package/dist/types/ClassType.d.ts +17 -0
  541. package/dist/types/ClassType.js +60 -0
  542. package/dist/types/ClassType.js.map +1 -0
  543. package/dist/types/ClassType.spec.d.ts +1 -0
  544. package/dist/types/ClassType.spec.js +76 -0
  545. package/dist/types/ClassType.spec.js.map +1 -0
  546. package/dist/types/ComponentType.d.ts +22 -0
  547. package/dist/types/ComponentType.js +107 -0
  548. package/dist/types/ComponentType.js.map +1 -0
  549. package/dist/types/DoubleType.d.ts +10 -5
  550. package/dist/types/DoubleType.js +21 -17
  551. package/dist/types/DoubleType.js.map +1 -1
  552. package/dist/types/DoubleType.spec.js +12 -4
  553. package/dist/types/DoubleType.spec.js.map +1 -1
  554. package/dist/types/DynamicType.d.ts +13 -5
  555. package/dist/types/DynamicType.js +26 -5
  556. package/dist/types/DynamicType.js.map +1 -1
  557. package/dist/types/DynamicType.spec.js +16 -5
  558. package/dist/types/DynamicType.spec.js.map +1 -1
  559. package/dist/types/EnumType.d.ts +42 -0
  560. package/dist/types/EnumType.js +98 -0
  561. package/dist/types/EnumType.js.map +1 -0
  562. package/dist/types/EnumType.spec.d.ts +1 -0
  563. package/dist/types/EnumType.spec.js +33 -0
  564. package/dist/types/EnumType.spec.js.map +1 -0
  565. package/dist/types/FloatType.d.ts +10 -5
  566. package/dist/types/FloatType.js +21 -17
  567. package/dist/types/FloatType.js.map +1 -1
  568. package/dist/types/FloatType.spec.js +4 -4
  569. package/dist/types/FloatType.spec.js.map +1 -1
  570. package/dist/types/FunctionType.d.ts +8 -22
  571. package/dist/types/FunctionType.js +25 -63
  572. package/dist/types/FunctionType.js.map +1 -1
  573. package/dist/types/InheritableType.d.ts +29 -0
  574. package/dist/types/InheritableType.js +173 -0
  575. package/dist/types/InheritableType.js.map +1 -0
  576. package/dist/types/InlineInterfaceType.d.ts +5 -0
  577. package/dist/types/InlineInterfaceType.js +17 -0
  578. package/dist/types/InlineInterfaceType.js.map +1 -0
  579. package/dist/types/IntegerType.d.ts +10 -5
  580. package/dist/types/IntegerType.js +21 -17
  581. package/dist/types/IntegerType.js.map +1 -1
  582. package/dist/types/IntegerType.spec.js +8 -4
  583. package/dist/types/IntegerType.spec.js.map +1 -1
  584. package/dist/types/InterfaceType.d.ts +14 -6
  585. package/dist/types/InterfaceType.js +30 -15
  586. package/dist/types/InterfaceType.js.map +1 -1
  587. package/dist/types/InterfaceType.spec.d.ts +1 -0
  588. package/dist/types/InterfaceType.spec.js +227 -0
  589. package/dist/types/InterfaceType.spec.js.map +1 -0
  590. package/dist/types/IntersectionType.d.ts +29 -0
  591. package/dist/types/IntersectionType.js +253 -0
  592. package/dist/types/IntersectionType.js.map +1 -0
  593. package/dist/types/IntersectionType.spec.d.ts +1 -0
  594. package/dist/types/IntersectionType.spec.js +150 -0
  595. package/dist/types/IntersectionType.spec.js.map +1 -0
  596. package/dist/types/InvalidType.d.ts +10 -5
  597. package/dist/types/InvalidType.js +21 -9
  598. package/dist/types/InvalidType.js.map +1 -1
  599. package/dist/types/InvalidType.spec.js +8 -4
  600. package/dist/types/InvalidType.spec.js.map +1 -1
  601. package/dist/types/LongIntegerType.d.ts +10 -5
  602. package/dist/types/LongIntegerType.js +21 -17
  603. package/dist/types/LongIntegerType.js.map +1 -1
  604. package/dist/types/LongIntegerType.spec.js +10 -4
  605. package/dist/types/LongIntegerType.spec.js.map +1 -1
  606. package/dist/types/NamespaceType.d.ts +12 -0
  607. package/dist/types/NamespaceType.js +28 -0
  608. package/dist/types/NamespaceType.js.map +1 -0
  609. package/dist/types/ObjectType.d.ts +12 -5
  610. package/dist/types/ObjectType.js +25 -8
  611. package/dist/types/ObjectType.js.map +1 -1
  612. package/dist/types/ObjectType.spec.js +3 -3
  613. package/dist/types/ObjectType.spec.js.map +1 -1
  614. package/dist/types/ReferenceType.d.ts +123 -0
  615. package/dist/types/ReferenceType.js +720 -0
  616. package/dist/types/ReferenceType.js.map +1 -0
  617. package/dist/types/ReferenceType.spec.d.ts +1 -0
  618. package/dist/types/ReferenceType.spec.js +151 -0
  619. package/dist/types/ReferenceType.spec.js.map +1 -0
  620. package/dist/types/StringType.d.ts +12 -5
  621. package/dist/types/StringType.js +23 -8
  622. package/dist/types/StringType.js.map +1 -1
  623. package/dist/types/StringType.spec.js +3 -3
  624. package/dist/types/StringType.spec.js.map +1 -1
  625. package/dist/types/TypeStatementType.d.ts +18 -0
  626. package/dist/types/TypeStatementType.js +45 -0
  627. package/dist/types/TypeStatementType.js.map +1 -0
  628. package/dist/types/TypedFunctionType.d.ts +34 -0
  629. package/dist/types/TypedFunctionType.js +147 -0
  630. package/dist/types/TypedFunctionType.js.map +1 -0
  631. package/dist/types/TypedFunctionType.spec.d.ts +1 -0
  632. package/dist/types/TypedFunctionType.spec.js +122 -0
  633. package/dist/types/TypedFunctionType.spec.js.map +1 -0
  634. package/dist/types/UninitializedType.d.ts +11 -6
  635. package/dist/types/UninitializedType.js +20 -11
  636. package/dist/types/UninitializedType.js.map +1 -1
  637. package/dist/types/UnionType.d.ts +27 -0
  638. package/dist/types/UnionType.js +193 -0
  639. package/dist/types/UnionType.js.map +1 -0
  640. package/dist/types/UnionType.spec.d.ts +1 -0
  641. package/dist/types/UnionType.spec.js +205 -0
  642. package/dist/types/UnionType.spec.js.map +1 -0
  643. package/dist/types/VoidType.d.ts +11 -5
  644. package/dist/types/VoidType.js +22 -8
  645. package/dist/types/VoidType.js.map +1 -1
  646. package/dist/types/VoidType.spec.js +3 -3
  647. package/dist/types/VoidType.spec.js.map +1 -1
  648. package/dist/types/helper.spec.d.ts +1 -0
  649. package/dist/types/helper.spec.js +174 -0
  650. package/dist/types/helper.spec.js.map +1 -0
  651. package/dist/types/helpers.d.ts +51 -0
  652. package/dist/types/helpers.js +323 -0
  653. package/dist/types/helpers.js.map +1 -0
  654. package/dist/types/index.d.ts +22 -0
  655. package/dist/types/index.js +39 -0
  656. package/dist/types/index.js.map +1 -0
  657. package/dist/types/roFunctionType.d.ts +11 -0
  658. package/dist/types/roFunctionType.js +37 -0
  659. package/dist/types/roFunctionType.js.map +1 -0
  660. package/dist/types/roFunctionType.spec.d.ts +1 -0
  661. package/dist/types/roFunctionType.spec.js +20 -0
  662. package/dist/types/roFunctionType.spec.js.map +1 -0
  663. package/dist/util.d.ts +288 -187
  664. package/dist/util.js +2018 -575
  665. package/dist/util.js.map +1 -1
  666. package/dist/validators/ClassValidator.d.ts +9 -15
  667. package/dist/validators/ClassValidator.js +93 -138
  668. package/dist/validators/ClassValidator.js.map +1 -1
  669. package/package.json +185 -138
  670. package/CHANGELOG.md +0 -1188
  671. package/dist/astUtils/index.d.ts +0 -7
  672. package/dist/astUtils/index.js +0 -26
  673. package/dist/astUtils/index.js.map +0 -1
  674. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.d.ts +0 -7
  675. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js +0 -63
  676. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js.map +0 -1
  677. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js +0 -45
  678. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +0 -1
  679. package/dist/lexer/index.d.ts +0 -3
  680. package/dist/lexer/index.js +0 -17
  681. package/dist/lexer/index.js.map +0 -1
  682. package/dist/parser/SGTypes.spec.js +0 -351
  683. package/dist/parser/SGTypes.spec.js.map +0 -1
  684. package/dist/parser/index.d.ts +0 -3
  685. package/dist/parser/index.js +0 -16
  686. package/dist/parser/index.js.map +0 -1
  687. package/dist/preprocessor/Chunk.d.ts +0 -82
  688. package/dist/preprocessor/Chunk.js +0 -77
  689. package/dist/preprocessor/Chunk.js.map +0 -1
  690. package/dist/preprocessor/Preprocessor.d.ts +0 -60
  691. package/dist/preprocessor/Preprocessor.js +0 -156
  692. package/dist/preprocessor/Preprocessor.js.map +0 -1
  693. package/dist/preprocessor/Preprocessor.spec.js +0 -152
  694. package/dist/preprocessor/Preprocessor.spec.js.map +0 -1
  695. package/dist/preprocessor/PreprocessorParser.d.ts +0 -61
  696. package/dist/preprocessor/PreprocessorParser.js +0 -194
  697. package/dist/preprocessor/PreprocessorParser.js.map +0 -1
  698. package/dist/preprocessor/PreprocessorParser.spec.js +0 -116
  699. package/dist/preprocessor/PreprocessorParser.spec.js.map +0 -1
  700. package/dist/preprocessor/index.d.ts +0 -3
  701. package/dist/preprocessor/index.js +0 -16
  702. package/dist/preprocessor/index.js.map +0 -1
  703. package/dist/types/CustomType.d.ts +0 -10
  704. package/dist/types/CustomType.js +0 -35
  705. package/dist/types/CustomType.js.map +0 -1
  706. package/dist/types/FunctionType.spec.js +0 -29
  707. package/dist/types/FunctionType.spec.js.map +0 -1
  708. package/dist/types/LazyType.d.ts +0 -15
  709. package/dist/types/LazyType.js +0 -32
  710. package/dist/types/LazyType.js.map +0 -1
  711. /package/dist/{bscPlugin/semanticTokens/SemanticTokensProcessor.spec.d.ts → astUtils/CachedLookups.spec.d.ts} +0 -0
  712. /package/dist/{parser/SGTypes.spec.d.ts → astUtils/Editor.spec.d.ts} +0 -0
  713. /package/dist/{preprocessor/Preprocessor.spec.d.ts → bscPlugin/completions/CompletionsProcessor.spec.d.ts} +0 -0
  714. /package/dist/{preprocessor/PreprocessorParser.spec.d.ts → bscPlugin/definition/DefinitionProvider.spec.d.ts} +0 -0
  715. /package/dist/{types/FunctionType.spec.d.ts → bscPlugin/hover/HoverProcessor.spec.d.ts} +0 -0
package/dist/Program.js CHANGED
@@ -7,32 +7,61 @@ const path = require("path");
7
7
  const vscode_languageserver_1 = require("vscode-languageserver");
8
8
  const Scope_1 = require("./Scope");
9
9
  const DiagnosticMessages_1 = require("./DiagnosticMessages");
10
- const BrsFile_1 = require("./files/BrsFile");
11
- const XmlFile_1 = require("./files/XmlFile");
12
10
  const util_1 = require("./util");
13
11
  const XmlScope_1 = require("./XmlScope");
14
- const DiagnosticFilterer_1 = require("./DiagnosticFilterer");
15
12
  const DependencyGraph_1 = require("./DependencyGraph");
16
- const Logger_1 = require("./Logger");
13
+ const logging_1 = require("./logging");
17
14
  const chalk_1 = require("chalk");
18
15
  const globalCallables_1 = require("./globalCallables");
19
16
  const Manifest_1 = require("./preprocessor/Manifest");
20
17
  const vscode_uri_1 = require("vscode-uri");
21
18
  const PluginInterface_1 = require("./PluginInterface");
22
19
  const reflection_1 = require("./astUtils/reflection");
23
- const parser_1 = require("./parser");
24
- const lexer_1 = require("./lexer");
25
20
  const BscPlugin_1 = require("./bscPlugin/BscPlugin");
21
+ const Editor_1 = require("./astUtils/Editor");
22
+ const IntegerType_1 = require("./types/IntegerType");
23
+ const StringType_1 = require("./types/StringType");
24
+ const BooleanType_1 = require("./types/BooleanType");
25
+ const DoubleType_1 = require("./types/DoubleType");
26
+ const DynamicType_1 = require("./types/DynamicType");
27
+ const FloatType_1 = require("./types/FloatType");
28
+ const LongIntegerType_1 = require("./types/LongIntegerType");
29
+ const ObjectType_1 = require("./types/ObjectType");
30
+ const VoidType_1 = require("./types/VoidType");
31
+ const FunctionType_1 = require("./types/FunctionType");
32
+ const Factory_1 = require("./files/Factory");
33
+ const ActionPipeline_1 = require("./ActionPipeline");
34
+ const LazyFileData_1 = require("./files/LazyFileData");
26
35
  const roku_deploy_1 = require("roku-deploy");
27
- const bslibNonAliasedRokuModulesPkgPath = `pkg:/source/roku_modules/rokucommunity_bslib/bslib.brs`;
28
- const bslibAliasedRokuModulesPkgPath = `pkg:/source/roku_modules/bslib/bslib.brs`;
36
+ const roku_types_1 = require("./roku-types");
37
+ const ComponentType_1 = require("./types/ComponentType");
38
+ const InterfaceType_1 = require("./types/InterfaceType");
39
+ const BuiltInInterfaceAdder_1 = require("./types/BuiltInInterfaceAdder");
40
+ const visitors_1 = require("./astUtils/visitors");
41
+ const thenby_1 = require("thenby");
42
+ const CrossScopeValidator_1 = require("./CrossScopeValidator");
43
+ const DiagnosticManager_1 = require("./DiagnosticManager");
44
+ const ProgramValidator_1 = require("./bscPlugin/validation/ProgramValidator");
45
+ const SymbolTable_1 = require("./SymbolTable");
46
+ const ReferenceType_1 = require("./types/ReferenceType");
47
+ const helpers_1 = require("./types/helpers");
48
+ const CallExpressionInfo_1 = require("./bscPlugin/CallExpressionInfo");
49
+ const SignatureHelpUtil_1 = require("./bscPlugin/SignatureHelpUtil");
50
+ const Sequencer_1 = require("./common/Sequencer");
51
+ const deferred_1 = require("./deferred");
52
+ const roFunctionType_1 = require("./types/roFunctionType");
53
+ const bslibNonAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/rokucommunity_bslib/bslib.brs`;
54
+ const bslibAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/bslib/bslib.brs`;
29
55
  class Program {
30
56
  constructor(
31
57
  /**
32
58
  * The root directory for this program
33
59
  */
34
- options, logger, plugins) {
35
- this.options = options;
60
+ options, logger, plugins, diagnosticsManager) {
61
+ /**
62
+ * 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`)
63
+ */
64
+ this.editor = new Editor_1.Editor();
36
65
  /**
37
66
  * A graph of all files and their dependencies.
38
67
  * For example:
@@ -40,20 +69,37 @@ class Program {
40
69
  * lib2.brs -> [lib3.brs] //via an import statement
41
70
  */
42
71
  this.dependencyGraph = new DependencyGraph_1.DependencyGraph();
43
- this.diagnosticFilterer = new DiagnosticFilterer_1.DiagnosticFilterer();
44
72
  /**
45
- * A set of diagnostics. This does not include any of the scope diagnostics.
46
- * Should only be set from `this.validate()`
73
+ * A scope that contains all built-in global functions.
74
+ * All scopes should directly or indirectly inherit from this scope
75
+ */
76
+ this.globalScope = undefined;
77
+ this.fileSymbolInformation = new Map();
78
+ /**
79
+ * Map of typetime symbols which depend upon the key symbol
47
80
  */
48
- this.diagnostics = [];
81
+ this.symbolDependencies = new Map();
49
82
  /**
50
- * A map of every file loaded ino this program, indexed by its lower-case pkgPath
83
+ * Symbol Table for storing custom component types
84
+ * This is a sibling to the global table (as Components can be used/referenced anywhere)
85
+ * Keeping custom components out of the global table and in a specific symbol table
86
+ * compartmentalizes their use
51
87
  */
52
- this.pkgMap = {};
88
+ this.componentsTable = new SymbolTable_1.SymbolTable('Custom Components');
53
89
  /**
54
- * A map of every file loaded into this program, indexed by its lower-case srcPath
90
+ * A map of every file loaded into this program, indexed by its original file location
55
91
  */
56
92
  this.files = {};
93
+ /**
94
+ * A map of every file loaded into this program, indexed by its destPath
95
+ */
96
+ this.destMap = new Map();
97
+ /**
98
+ * Plugins can contribute multiple virtual files for a single physical file.
99
+ * This collection links the virtual files back to the physical file that produced them.
100
+ * The key is the standardized and lower-cased srcPath
101
+ */
102
+ this.fileClusters = new Map();
57
103
  this.scopes = {};
58
104
  /**
59
105
  * A map of every component currently loaded into the program, indexed by the component name.
@@ -62,28 +108,184 @@ class Program {
62
108
  * but if you do, only ever use the component at index 0.
63
109
  */
64
110
  this.components = {};
111
+ /**
112
+ * Keeps a set of all the components that need to have their types updated during the current validation cycle
113
+ * Map <componentKey, componentName>
114
+ */
115
+ this.componentSymbolsToUpdate = new Map();
116
+ this.crossScopeValidation = new CrossScopeValidator_1.CrossScopeValidator(this);
117
+ this.isFirstValidation = true;
118
+ this.validationDetails = {
119
+ brsFilesValidated: [],
120
+ xmlFilesValidated: [],
121
+ changedSymbols: new Map(),
122
+ changedComponentTypes: [],
123
+ scopesToValidate: [],
124
+ filesToBeValidatedInScopeContext: new Set()
125
+ };
126
+ this.lastValidationInfo = {
127
+ brsFilesSrcPath: new Set(),
128
+ xmlFilesSrcPath: new Set(),
129
+ scopeNames: new Set(),
130
+ componentsRebuilt: new Set()
131
+ };
132
+ /**
133
+ * Counter used to track which validation run is being logged
134
+ */
135
+ this.validationRunSequence = 1;
136
+ /**
137
+ * How many milliseconds can pass while doing synchronous operations in validate before we register a short timeout (i.e. yield to the event loop)
138
+ */
139
+ this.validationMinSyncDuration = 75;
140
+ this.getFilePathCache = new Map();
141
+ this.sortedScopeNames = undefined;
142
+ this.getTranspiledFileContentsPipeline = new ActionPipeline_1.ActionPipeline();
143
+ this.buildPipeline = new ActionPipeline_1.ActionPipeline();
65
144
  this.options = util_1.util.normalizeConfig(options);
66
- this.logger = logger || new Logger_1.Logger(options.logLevel);
67
- this.plugins = plugins || new PluginInterface_1.default([], this.logger);
145
+ this.logger = logger !== null && logger !== void 0 ? logger : (0, logging_1.createLogger)(options);
146
+ this.plugins = plugins || new PluginInterface_1.default([], { logger: this.logger });
147
+ this.diagnostics = diagnosticsManager || new DiagnosticManager_1.DiagnosticManager();
148
+ //try to find a location for the diagnostic if it doesn't have one
149
+ this.diagnostics.locationResolver = (args) => {
150
+ //find the first xml scope for this diagnostic
151
+ for (let context of args.contexts) {
152
+ if ((0, reflection_1.isXmlScope)(context.scope) && (0, reflection_1.isXmlFile)(context.scope.xmlFile)) {
153
+ return util_1.util.createLocation(0, 0, 0, 100, context.scope.xmlFile.srcPath);
154
+ }
155
+ }
156
+ //we couldn't find an xml scope for this, so try to find the manifest file instead
157
+ const manifest = this.getFile('manifest', false);
158
+ if (manifest) {
159
+ return util_1.util.createLocation(0, 0, 0, 100, manifest.srcPath);
160
+ }
161
+ //if we still don't have a manifest, try to find the first file in the program
162
+ for (const key in this.files) {
163
+ if ((0, reflection_1.isBrsFile)(this.files[key]) || (0, reflection_1.isXmlFile)(this.files[key])) {
164
+ return util_1.util.createLocation(0, 0, 0, 100, this.files[key].srcPath);
165
+ }
166
+ }
167
+ this.logger.warn(`Unable to find a location for the diagnostic.`, args);
168
+ //we couldn't find any locations for the file, so just return undefined
169
+ return undefined;
170
+ };
171
+ // initialize the diagnostics Manager
172
+ this.diagnostics.logger = this.logger;
173
+ this.diagnostics.options = this.options;
174
+ this.diagnostics.program = this;
68
175
  //inject the bsc plugin as the first plugin in the stack.
69
176
  this.plugins.addFirst(new BscPlugin_1.BscPlugin());
70
177
  //normalize the root dir path
71
178
  this.options.rootDir = util_1.util.getRootDir(this.options);
72
179
  this.createGlobalScope();
180
+ this.fileFactory = new Factory_1.FileFactory(this);
73
181
  }
74
182
  createGlobalScope() {
75
183
  //create the 'global' scope
76
184
  this.globalScope = new Scope_1.Scope('global', this, 'scope:global');
77
185
  this.globalScope.attachDependencyGraph(this.dependencyGraph);
78
186
  this.scopes.global = this.globalScope;
187
+ this.populateGlobalSymbolTable();
188
+ this.globalScope.symbolTable.addSibling(this.componentsTable);
79
189
  //hardcode the files list for global scope to only contain the global file
80
190
  this.globalScope.getAllFiles = () => [globalCallables_1.globalFile];
191
+ globalCallables_1.globalFile.isValidated = true;
81
192
  this.globalScope.validate();
82
- //for now, disable validation of global scope because the global files have some duplicate method declarations
83
- this.globalScope.getDiagnostics = () => [];
84
193
  //TODO we might need to fix this because the isValidated clears stuff now
85
194
  this.globalScope.isValidated = true;
86
195
  }
196
+ recursivelyAddNodeToSymbolTable(nodeData) {
197
+ if (!nodeData) {
198
+ return;
199
+ }
200
+ let nodeType;
201
+ const nodeName = util_1.util.getSgNodeTypeName(nodeData.name);
202
+ if (!this.globalScope.symbolTable.hasSymbol(nodeName, 2 /* SymbolTypeFlag.typetime */)) {
203
+ let parentNode;
204
+ if (nodeData.extends) {
205
+ const parentNodeData = roku_types_1.nodes[nodeData.extends.name.toLowerCase()];
206
+ try {
207
+ parentNode = this.recursivelyAddNodeToSymbolTable(parentNodeData);
208
+ }
209
+ catch (error) {
210
+ this.logger.error(error, nodeData);
211
+ }
212
+ }
213
+ nodeType = new ComponentType_1.ComponentType(nodeData.name, parentNode);
214
+ nodeType.addBuiltInInterfaces();
215
+ nodeType.isBuiltIn = true;
216
+ if (nodeData.name === 'Node') {
217
+ // Add `roSGNode` as shorthand for `roSGNodeNode`
218
+ this.globalScope.symbolTable.addSymbol('roSGNode', { description: nodeData.description, isBuiltIn: true }, nodeType, 2 /* SymbolTypeFlag.typetime */);
219
+ }
220
+ this.globalScope.symbolTable.addSymbol(nodeName, { description: nodeData.description, isBuiltIn: true }, nodeType, 2 /* SymbolTypeFlag.typetime */);
221
+ }
222
+ else {
223
+ nodeType = this.globalScope.symbolTable.getSymbolType(nodeName, { flags: 2 /* SymbolTypeFlag.typetime */ });
224
+ }
225
+ return nodeType;
226
+ }
227
+ /**
228
+ * Do all setup required for the global symbol table.
229
+ */
230
+ populateGlobalSymbolTable() {
231
+ //Setup primitive types in global symbolTable
232
+ const builtInSymbolData = { isBuiltIn: true };
233
+ this.globalScope.symbolTable.addSymbol('boolean', builtInSymbolData, BooleanType_1.BooleanType.instance, 2 /* SymbolTypeFlag.typetime */);
234
+ this.globalScope.symbolTable.addSymbol('double', builtInSymbolData, DoubleType_1.DoubleType.instance, 2 /* SymbolTypeFlag.typetime */);
235
+ this.globalScope.symbolTable.addSymbol('dynamic', builtInSymbolData, DynamicType_1.DynamicType.instance, 2 /* SymbolTypeFlag.typetime */);
236
+ this.globalScope.symbolTable.addSymbol('float', builtInSymbolData, FloatType_1.FloatType.instance, 2 /* SymbolTypeFlag.typetime */);
237
+ this.globalScope.symbolTable.addSymbol('function', builtInSymbolData, FunctionType_1.FunctionType.instance, 2 /* SymbolTypeFlag.typetime */);
238
+ this.globalScope.symbolTable.addSymbol('integer', builtInSymbolData, IntegerType_1.IntegerType.instance, 2 /* SymbolTypeFlag.typetime */);
239
+ this.globalScope.symbolTable.addSymbol('longinteger', builtInSymbolData, LongIntegerType_1.LongIntegerType.instance, 2 /* SymbolTypeFlag.typetime */);
240
+ this.globalScope.symbolTable.addSymbol('object', builtInSymbolData, ObjectType_1.ObjectType.instance, 2 /* SymbolTypeFlag.typetime */);
241
+ this.globalScope.symbolTable.addSymbol('string', builtInSymbolData, StringType_1.StringType.instance, 2 /* SymbolTypeFlag.typetime */);
242
+ this.globalScope.symbolTable.addSymbol('void', builtInSymbolData, VoidType_1.VoidType.instance, 2 /* SymbolTypeFlag.typetime */);
243
+ BuiltInInterfaceAdder_1.BuiltInInterfaceAdder.getLookupTable = () => this.globalScope.symbolTable;
244
+ for (const callable of globalCallables_1.globalCallables) {
245
+ this.globalScope.symbolTable.addSymbol(callable.name, Object.assign(Object.assign({}, builtInSymbolData), { description: callable.shortDescription }), callable.type, 1 /* SymbolTypeFlag.runtime */);
246
+ }
247
+ for (const ifaceData of Object.values(roku_types_1.interfaces)) {
248
+ const ifaceType = new InterfaceType_1.InterfaceType(ifaceData.name);
249
+ ifaceType.addBuiltInInterfaces();
250
+ ifaceType.isBuiltIn = true;
251
+ this.globalScope.symbolTable.addSymbol(ifaceData.name, Object.assign(Object.assign({}, builtInSymbolData), { description: ifaceData.description }), ifaceType, 2 /* SymbolTypeFlag.typetime */);
252
+ }
253
+ for (const componentData of Object.values(roku_types_1.components)) {
254
+ let roComponentType;
255
+ const lowerComponentName = componentData.name.toLowerCase();
256
+ if (lowerComponentName === 'rosgnode') {
257
+ // we will add `roSGNode` as shorthand for `roSGNodeNode`, since all roSgNode components are SceneGraph nodes
258
+ continue;
259
+ }
260
+ if (lowerComponentName === 'rofunction') {
261
+ roComponentType = new roFunctionType_1.roFunctionType();
262
+ }
263
+ else {
264
+ roComponentType = new InterfaceType_1.InterfaceType(componentData.name);
265
+ }
266
+ roComponentType.addBuiltInInterfaces();
267
+ roComponentType.isBuiltIn = true;
268
+ this.globalScope.symbolTable.addSymbol(componentData.name, Object.assign(Object.assign({}, builtInSymbolData), { description: componentData.description }), roComponentType, 2 /* SymbolTypeFlag.typetime */);
269
+ }
270
+ for (const nodeData of Object.values(roku_types_1.nodes)) {
271
+ this.recursivelyAddNodeToSymbolTable(nodeData);
272
+ }
273
+ for (const eventData of Object.values(roku_types_1.events)) {
274
+ const eventType = new InterfaceType_1.InterfaceType(eventData.name);
275
+ eventType.addBuiltInInterfaces();
276
+ eventType.isBuiltIn = true;
277
+ this.globalScope.symbolTable.addSymbol(eventData.name, Object.assign(Object.assign({}, builtInSymbolData), { description: eventData.description }), eventType, 2 /* SymbolTypeFlag.typetime */);
278
+ }
279
+ }
280
+ addFileSymbolInfo(file) {
281
+ this.fileSymbolInformation.set(file.pkgPath, {
282
+ provides: file.providedSymbols,
283
+ requires: file.requiredSymbols
284
+ });
285
+ }
286
+ getFileSymbolInfo(file) {
287
+ return this.fileSymbolInformation.get(file.pkgPath);
288
+ }
87
289
  /**
88
290
  * The path to bslib.brs (the BrightScript runtime for certain BrighterScript features)
89
291
  */
@@ -98,7 +300,7 @@ class Program {
98
300
  //default to the embedded version
99
301
  }
100
302
  else {
101
- return `pkg:/source/bslib.brs`;
303
+ return `${this.options.bslibDestinationDir}${path.sep}bslib.brs`;
102
304
  }
103
305
  }
104
306
  get bslibPrefix() {
@@ -109,18 +311,15 @@ class Program {
109
311
  return 'bslib';
110
312
  }
111
313
  }
112
- /**
113
- * Get a copy of the list of files currently loaded in the program
114
- */
115
- getFiles() {
116
- return Object.values(this.files);
117
- }
118
314
  addScope(scope) {
119
315
  this.scopes[scope.name] = scope;
120
- this.plugins.emit('afterScopeCreate', {
121
- program: this,
122
- scope: scope
123
- });
316
+ delete this.sortedScopeNames;
317
+ }
318
+ removeScope(scope) {
319
+ if (this.scopes[scope.name]) {
320
+ delete this.scopes[scope.name];
321
+ delete this.sortedScopeNames;
322
+ }
124
323
  }
125
324
  /**
126
325
  * Get the component with the specified name
@@ -129,19 +328,34 @@ class Program {
129
328
  var _a;
130
329
  if (componentName) {
131
330
  //return the first compoment in the list with this name
132
- //(components are ordered in this list by pkgPath to ensure consistency)
331
+ //(components are ordered in this list by destPath to ensure consistency)
133
332
  return (_a = this.components[componentName.toLowerCase()]) === null || _a === void 0 ? void 0 : _a[0];
134
333
  }
135
334
  else {
136
335
  return undefined;
137
336
  }
138
337
  }
338
+ /**
339
+ * Get the sorted names of custom components
340
+ */
341
+ getSortedComponentNames() {
342
+ const componentNames = Object.keys(this.components);
343
+ componentNames.sort((a, b) => {
344
+ if (a < b) {
345
+ return -1;
346
+ }
347
+ else if (b < a) {
348
+ return 1;
349
+ }
350
+ return 0;
351
+ });
352
+ return componentNames;
353
+ }
139
354
  /**
140
355
  * Register (or replace) the reference to a component in the component map
141
356
  */
142
357
  registerComponent(xmlFile, scope) {
143
- var _a, _b;
144
- const key = ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
358
+ const key = this.getComponentKey(xmlFile);
145
359
  if (!this.components[key]) {
146
360
  this.components[key] = [];
147
361
  }
@@ -149,15 +363,25 @@ class Program {
149
363
  file: xmlFile,
150
364
  scope: scope
151
365
  });
152
- this.components[key].sort((x, y) => x.file.pkgPath.toLowerCase().localeCompare(y.file.pkgPath.toLowerCase()));
366
+ this.components[key].sort((a, b) => {
367
+ const pathA = a.file.destPath.toLowerCase();
368
+ const pathB = b.file.destPath.toLowerCase();
369
+ if (pathA < pathB) {
370
+ return -1;
371
+ }
372
+ else if (pathA > pathB) {
373
+ return 1;
374
+ }
375
+ return 0;
376
+ });
153
377
  this.syncComponentDependencyGraph(this.components[key]);
378
+ this.addDeferredComponentTypeSymbolCreation(xmlFile);
154
379
  }
155
380
  /**
156
381
  * Remove the specified component from the components map
157
382
  */
158
383
  unregisterComponent(xmlFile) {
159
- var _a, _b;
160
- const key = ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
384
+ const key = this.getComponentKey(xmlFile);
161
385
  const arr = this.components[key] || [];
162
386
  for (let i = 0; i < arr.length; i++) {
163
387
  if (arr[i].file === xmlFile) {
@@ -166,6 +390,84 @@ class Program {
166
390
  }
167
391
  }
168
392
  this.syncComponentDependencyGraph(arr);
393
+ this.addDeferredComponentTypeSymbolCreation(xmlFile);
394
+ }
395
+ /**
396
+ * Adds a component described in an XML to the set of components that needs to be updated this validation cycle.
397
+ * @param xmlFile XML file with <component> tag
398
+ */
399
+ addDeferredComponentTypeSymbolCreation(xmlFile) {
400
+ var _a;
401
+ const componentKey = this.getComponentKey(xmlFile);
402
+ const componentName = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text;
403
+ if (this.componentSymbolsToUpdate.has(componentKey)) {
404
+ return;
405
+ }
406
+ this.componentSymbolsToUpdate.set(componentKey, componentName);
407
+ }
408
+ getComponentKey(xmlFile) {
409
+ var _a, _b;
410
+ return ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
411
+ }
412
+ /**
413
+ * Resolves symbol table with the first component in this.components to have the same name as the component in the file
414
+ * @param componentKey key getting a component from `this.components`
415
+ * @param componentName the unprefixed name of the component that will be added (e.g. 'MyLabel' NOT 'roSgNodeMyLabel')
416
+ */
417
+ updateComponentSymbolInGlobalScope(componentKey, componentName) {
418
+ const symbolName = componentName ? util_1.util.getSgNodeTypeName(componentName) : undefined;
419
+ if (!symbolName) {
420
+ return;
421
+ }
422
+ const components = this.components[componentKey] || [];
423
+ const previousComponentType = this.componentsTable.getSymbolType(symbolName, { flags: 2 /* SymbolTypeFlag.typetime */ });
424
+ // Remove any existing symbols that match
425
+ this.componentsTable.removeSymbol(symbolName);
426
+ if (components.length > 0) {
427
+ // There is a component that can be added - use it.
428
+ const componentScope = components[0].scope;
429
+ this.componentsTable.removeSymbol(symbolName);
430
+ componentScope.linkSymbolTable();
431
+ const componentType = componentScope.getComponentType();
432
+ if (componentType) {
433
+ this.componentsTable.addSymbol(symbolName, {}, componentType, 2 /* SymbolTypeFlag.typetime */);
434
+ }
435
+ const typeData = {};
436
+ const isSameAsPrevious = previousComponentType && componentType.isEqual(previousComponentType, typeData);
437
+ const isComponentTypeDifferent = !previousComponentType || (0, reflection_1.isReferenceType)(previousComponentType) || !isSameAsPrevious;
438
+ componentScope.unlinkSymbolTable();
439
+ return isComponentTypeDifferent;
440
+ }
441
+ // There was a previous component type, but no new one, so it's different
442
+ return !!previousComponentType;
443
+ }
444
+ /**
445
+ * Adds a reference type to the global symbol table with the first component in this.components to have the same name as the component in the file
446
+ * This is so on a first validation, these types can be resolved in teh future (eg. when the actual component is created)
447
+ * If we don't add reference types at this top level, they will be created at the file level, and will never get resolved
448
+ * @param componentKey key getting a component from `this.components`
449
+ * @param componentName the unprefixed name of the component that will be added (e.g. 'MyLabel' NOT 'roSgNodeMyLabel')
450
+ */
451
+ addComponentReferenceType(componentKey, componentName) {
452
+ const symbolName = componentName ? util_1.util.getSgNodeTypeName(componentName) : undefined;
453
+ if (!symbolName) {
454
+ return;
455
+ }
456
+ const components = this.components[componentKey] || [];
457
+ if (components.length > 0) {
458
+ // There is a component that can be added,
459
+ if (!this.componentsTable.hasSymbol(symbolName, 2 /* SymbolTypeFlag.typetime */)) {
460
+ // it doesn't already exist in the table
461
+ const componentRefType = new ReferenceType_1.ReferenceType(symbolName, symbolName, 2 /* SymbolTypeFlag.typetime */, () => this.componentsTable);
462
+ if (componentRefType) {
463
+ this.componentsTable.addSymbol(symbolName, {}, componentRefType, 2 /* SymbolTypeFlag.typetime */);
464
+ }
465
+ }
466
+ }
467
+ else {
468
+ // there is no component. remove from table
469
+ this.componentsTable.removeSymbol(symbolName);
470
+ }
169
471
  }
170
472
  /**
171
473
  * re-attach the dependency graph with a new key for any component who changed
@@ -179,6 +481,7 @@ class Program {
179
481
  //attach (or re-attach) the dependencyGraph for every component whose position changed
180
482
  if (file.dependencyGraphIndex !== i) {
181
483
  file.dependencyGraphIndex = i;
484
+ this.dependencyGraph.addOrReplace(file.dependencyGraphKey, file.dependencies);
182
485
  file.attachDependencyGraph(this.dependencyGraph);
183
486
  scope.attachDependencyGraph(this.dependencyGraph);
184
487
  }
@@ -190,9 +493,10 @@ class Program {
190
493
  */
191
494
  getUnreferencedFiles() {
192
495
  let result = [];
193
- for (let key in this.files) {
194
- const file = this.files[key];
195
- if (!this.fileIsIncludedInAnyScope(file)) {
496
+ for (let filePath in this.files) {
497
+ let file = this.files[filePath];
498
+ //is this file part of a scope
499
+ if (!this.getFirstScopeForFile(file)) {
196
500
  //no scopes reference this file. add it to the list
197
501
  result.push(file);
198
502
  }
@@ -200,37 +504,14 @@ class Program {
200
504
  return result;
201
505
  }
202
506
  /**
203
- * Get the list of errors for the entire program. It's calculated on the fly
204
- * by walking through every file, so call this sparingly.
507
+ * Get the list of errors for the entire program.
205
508
  */
206
509
  getDiagnostics() {
207
- return this.logger.time(Logger_1.LogLevel.info, ['Program.getDiagnostics()'], () => {
208
- let diagnostics = [...this.diagnostics];
209
- //get the diagnostics from all scopes
210
- for (let scopeName in this.scopes) {
211
- let scope = this.scopes[scopeName];
212
- diagnostics.push(...scope.getDiagnostics());
213
- }
214
- //get the diagnostics from all unreferenced files
215
- let unreferencedFiles = this.getUnreferencedFiles();
216
- for (let file of unreferencedFiles) {
217
- diagnostics.push(...file.getDiagnostics());
218
- }
219
- const filteredDiagnostics = this.logger.time(Logger_1.LogLevel.debug, ['filter diagnostics'], () => {
220
- //filter out diagnostics based on our diagnostic filters
221
- let finalDiagnostics = this.diagnosticFilterer.filter(Object.assign(Object.assign({}, this.options), { rootDir: this.options.rootDir }), diagnostics);
222
- return finalDiagnostics;
223
- });
224
- this.logger.info(`diagnostic counts: total=${chalk_1.default.yellow(diagnostics.length.toString())}, after filter=${chalk_1.default.yellow(filteredDiagnostics.length.toString())}`);
225
- return filteredDiagnostics;
226
- });
227
- }
228
- addDiagnostics(diagnostics) {
229
- this.diagnostics.push(...diagnostics);
510
+ return this.diagnostics.getDiagnostics();
230
511
  }
231
512
  /**
232
513
  * Determine if the specified file is loaded in this program right now.
233
- * @param filePath
514
+ * @param filePath the absolute or relative path to the file
234
515
  * @param normalizePath should the provided path be normalized before use
235
516
  */
236
517
  hasFile(filePath, normalizePath = true) {
@@ -238,12 +519,15 @@ class Program {
238
519
  }
239
520
  /**
240
521
  * roku filesystem is case INsensitive, so find the scope by key case insensitive
241
- * @param scopeName
522
+ * @param scopeName xml scope names are their `destPath`. Source scope is stored with the key `"source"`
242
523
  */
243
524
  getScopeByName(scopeName) {
244
525
  if (!scopeName) {
245
526
  return undefined;
246
527
  }
528
+ //most scopes are xml file pkg paths. however, the ones that are not are single names like "global" and "scope",
529
+ //so it's safe to run the standardizePkgPath method
530
+ scopeName = (0, util_1.standardizePath) `${scopeName}`;
247
531
  let key = Object.keys(this.scopes).find(x => x.toLowerCase() === scopeName.toLowerCase());
248
532
  return this.scopes[key];
249
533
  }
@@ -264,113 +548,178 @@ class Program {
264
548
  * Update internal maps with this file reference
265
549
  */
266
550
  assignFile(file) {
551
+ const fileAddEvent = {
552
+ file: file,
553
+ program: this
554
+ };
555
+ this.plugins.emit('beforeAddFile', fileAddEvent);
267
556
  this.files[file.srcPath.toLowerCase()] = file;
268
- this.pkgMap[file.pkgPath.toLowerCase()] = file;
557
+ this.destMap.set(file.destPath.toLowerCase(), file);
558
+ this.plugins.emit('afterAddFile', fileAddEvent);
559
+ return file;
269
560
  }
270
561
  /**
271
562
  * Remove this file from internal maps
272
563
  */
273
564
  unassignFile(file) {
274
565
  delete this.files[file.srcPath.toLowerCase()];
275
- delete this.pkgMap[file.pkgPath.toLowerCase()];
566
+ this.destMap.delete(file.destPath.toLowerCase());
567
+ return file;
276
568
  }
277
- setFile(fileParam, fileContents) {
278
- assert.ok(fileParam, 'fileParam is required');
279
- let srcPath;
280
- let pkgPath;
281
- if (typeof fileParam === 'string') {
282
- //is a pkg path
283
- if (fileParam.startsWith('pkg:/')) {
284
- //srcPath is the pkgPath relative to the rootDir
285
- srcPath = util_1.standardizePath `${this.options.rootDir}/${fileParam.substring(5)}`;
286
- pkgPath = fileParam;
287
- //is a srcPath (absolute path to src file location)
288
- }
289
- else if (path.isAbsolute(fileParam)) {
290
- srcPath = util_1.util.standardizePath(fileParam);
291
- //assume the file path is a sub path of rootDir
292
- pkgPath = util_1.util.sanitizePkgPath(roku_deploy_1.util.stringReplaceInsensitive(srcPath, this.options.rootDir, ''));
293
- //is destPath (path relative to rootDir and `pkg:/`)
294
- }
295
- else {
296
- srcPath = util_1.standardizePath `${this.options.rootDir}/${fileParam}`;
297
- pkgPath = util_1.util.sanitizePkgPath(fileParam);
298
- }
299
- //is a FileObj
300
- }
301
- else {
302
- srcPath = util_1.standardizePath `${fileParam.src}`;
303
- pkgPath = util_1.util.sanitizePkgPath(fileParam.dest);
304
- }
305
- const lowerPkgPath = pkgPath.toLowerCase();
306
- return this.logger.time(Logger_1.LogLevel.debug, ['Program.addOrReplaceFile()', chalk_1.default.green(srcPath)], () => {
307
- assert.ok(srcPath, 'srcPath is required');
308
- assert.ok(pkgPath, 'pkgPath is required');
569
+ setFile(fileParam, fileData) {
570
+ //normalize the file paths
571
+ const { srcPath, destPath } = this.getPaths(fileParam, this.options.rootDir);
572
+ let file = this.logger.time(logging_1.LogLevel.debug, ['Program.setFile()', chalk_1.default.green(srcPath)], () => {
573
+ var _a, _b, _c;
309
574
  //if the file is already loaded, remove it
310
575
  if (this.hasFile(srcPath)) {
311
- this.removeFile(srcPath);
576
+ this.removeFile(srcPath, true, true);
312
577
  }
313
- let fileExtension = path.extname(srcPath).toLowerCase();
314
- let file;
315
- const beforeFileParseEvent = {
316
- program: this,
317
- srcPath: srcPath,
318
- source: fileContents
319
- };
320
- if (fileExtension === '.brs' || fileExtension === '.bs') {
321
- let brsFile = new BrsFile_1.BrsFile(srcPath, pkgPath, this);
322
- //add file to the `source` dependency list
323
- if (brsFile.pkgPath.startsWith('pkg:/source/')) {
578
+ const data = new LazyFileData_1.LazyFileData(fileData);
579
+ const event = new ProvideFileEventInternal(this, srcPath, destPath, data, this.fileFactory);
580
+ this.plugins.emit('beforeProvideFile', event);
581
+ this.plugins.emit('provideFile', event);
582
+ this.plugins.emit('afterProvideFile', event);
583
+ //if no files were provided, create a AssetFile to represent it.
584
+ if (event.files.length === 0) {
585
+ event.files.push(this.fileFactory.AssetFile({
586
+ srcPath: event.srcPath,
587
+ destPath: event.destPath,
588
+ pkgPath: event.destPath,
589
+ data: data
590
+ }));
591
+ }
592
+ //find the file instance for the srcPath that triggered this action.
593
+ const primaryFile = event.files.find(x => x.srcPath === srcPath);
594
+ if (!primaryFile) {
595
+ throw new Error(`No file provided for srcPath '${srcPath}'. Instead, received ${JSON.stringify(event.files.map(x => ({
596
+ type: x.type,
597
+ srcPath: x.srcPath,
598
+ destPath: x.destPath
599
+ })))}`);
600
+ }
601
+ //link the virtual files to the primary file
602
+ this.fileClusters.set((_a = primaryFile.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase(), event.files);
603
+ for (const file of event.files) {
604
+ file.srcPath = (0, util_1.standardizePath)(file.srcPath);
605
+ if (file.destPath) {
606
+ file.destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.destPath, this.options.rootDir, '')}`;
607
+ }
608
+ if (file.pkgPath) {
609
+ file.pkgPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.pkgPath, this.options.rootDir, '')}`;
610
+ }
611
+ else {
612
+ file.pkgPath = file.destPath;
613
+ }
614
+ file.excludeFromOutput = file.excludeFromOutput === true;
615
+ //set the dependencyGraph key for every file to its destPath
616
+ file.dependencyGraphKey = file.destPath.toLowerCase();
617
+ this.assignFile(file);
618
+ //register a callback anytime this file's dependencies change
619
+ if (typeof file.onDependenciesChanged === 'function') {
620
+ (_b = file.disposables) !== null && _b !== void 0 ? _b : (file.disposables = []);
621
+ file.disposables.push(this.dependencyGraph.onchange(file.dependencyGraphKey, file.onDependenciesChanged.bind(file)));
622
+ }
623
+ //register this file (and its dependencies) with the dependency graph
624
+ this.dependencyGraph.addOrReplace(file.dependencyGraphKey, (_c = file.dependencies) !== null && _c !== void 0 ? _c : []);
625
+ //if this is a `source` file, add it to the source scope's dependency list
626
+ if (this.isSourceBrsFile(file)) {
324
627
  this.createSourceScope();
325
- this.dependencyGraph.addDependency('scope:source', brsFile.dependencyGraphKey);
628
+ this.dependencyGraph.addDependency('scope:source', file.dependencyGraphKey);
629
+ }
630
+ //if this is an xml file in the components folder, register it as a component
631
+ if (this.isComponentsXmlFile(file)) {
632
+ this.plugins.emit('beforeProvideScope', {
633
+ program: this,
634
+ scope: undefined
635
+ });
636
+ //create a new scope for this xml file
637
+ let scope = new XmlScope_1.XmlScope(file, this);
638
+ this.addScope(scope);
639
+ //register this componet now that we have parsed it and know its component name
640
+ this.registerComponent(file, scope);
641
+ this.plugins.emit('provideScope', {
642
+ program: this,
643
+ scope: scope
644
+ });
645
+ //notify plugins that the scope is created and the component is registered
646
+ this.plugins.emit('afterProvideScope', {
647
+ program: this,
648
+ scope: scope
649
+ });
326
650
  }
327
- //add the file to the program
328
- this.assignFile(brsFile);
329
- this.plugins.emit('beforeFileParse', beforeFileParseEvent);
330
- this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
331
- brsFile.parse(beforeFileParseEvent.source);
332
- });
333
- file = brsFile;
334
- brsFile.attachDependencyGraph(this.dependencyGraph);
335
- this.plugins.emit('afterFileParse', {
336
- program: this,
337
- file: brsFile
338
- });
339
- }
340
- else if (
341
- //is xml file
342
- fileExtension === '.xml' &&
343
- //resides in the components folder (Roku will only parse xml files in the components folder)
344
- lowerPkgPath.startsWith('pkg:/components/')) {
345
- let xmlFile = new XmlFile_1.XmlFile(srcPath, pkgPath, this);
346
- this.assignFile(xmlFile);
347
- //add the file to the program
348
- this.plugins.emit('beforeFileParse', beforeFileParseEvent);
349
- this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
350
- xmlFile.parse(beforeFileParseEvent.source);
351
- });
352
- file = xmlFile;
353
- //create a new scope for this xml file
354
- let scope = new XmlScope_1.XmlScope(xmlFile, this);
355
- this.addScope(scope);
356
- //register this compoent now that we have parsed it and know its component name
357
- this.registerComponent(xmlFile, scope);
358
- this.plugins.emit('afterFileParse', {
359
- program: this,
360
- file: xmlFile
361
- });
362
651
  }
363
- else {
364
- //TODO do we actually need to implement this? Figure out how to handle img paths
365
- // let genericFile = this.files[srcPath] = <any>{
366
- // srcPath: srcPath,
367
- // pkgPath: pkgPath,
368
- // wasProcessed: true
369
- // } as File;
370
- // file = <any>genericFile;
371
- }
372
- return file;
652
+ return primaryFile;
373
653
  });
654
+ return file;
655
+ }
656
+ /**
657
+ * Given a srcPath, a destPath, or both, resolve whichever is missing, relative to rootDir.
658
+ * @param fileParam an object representing file paths
659
+ * @param rootDir must be a pre-normalized path
660
+ */
661
+ getPaths(fileParam, rootDir) {
662
+ let srcPath;
663
+ let destPath;
664
+ assert.ok(fileParam, 'fileParam is required');
665
+ //lift the path vars from the incoming param
666
+ if (typeof fileParam === 'string') {
667
+ fileParam = this.removePkgPrefix(fileParam);
668
+ srcPath = (0, util_1.standardizePath) `${path.resolve(rootDir, fileParam)}`;
669
+ destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
670
+ }
671
+ else {
672
+ let param = fileParam;
673
+ if (param.src) {
674
+ srcPath = (0, util_1.standardizePath) `${param.src}`;
675
+ }
676
+ if (param.srcPath) {
677
+ srcPath = (0, util_1.standardizePath) `${param.srcPath}`;
678
+ }
679
+ if (param.dest) {
680
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.dest)}`;
681
+ }
682
+ if (param.pkgPath) {
683
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.pkgPath)}`;
684
+ }
685
+ }
686
+ //if there's no srcPath, use the destPath to build an absolute srcPath
687
+ if (!srcPath) {
688
+ srcPath = (0, util_1.standardizePath) `${rootDir}/${destPath}`;
689
+ }
690
+ //coerce srcPath to an absolute path
691
+ if (!path.isAbsolute(srcPath)) {
692
+ srcPath = util_1.util.standardizePath(srcPath);
693
+ }
694
+ //if destPath isn't set, compute it from the other paths
695
+ if (!destPath) {
696
+ destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
697
+ }
698
+ assert.ok(srcPath, 'fileEntry.src is required');
699
+ assert.ok(destPath, 'fileEntry.dest is required');
700
+ return {
701
+ srcPath: srcPath,
702
+ //remove leading slash
703
+ destPath: destPath.replace(/^[\/\\]+/, '')
704
+ };
705
+ }
706
+ /**
707
+ * Remove any leading `pkg:/` found in the path
708
+ */
709
+ removePkgPrefix(path) {
710
+ return path.replace(/^pkg:\//i, '');
711
+ }
712
+ /**
713
+ * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
714
+ */
715
+ isSourceBrsFile(file) {
716
+ return !!/^(pkg:\/)?source[\/\\]/.exec(file.destPath);
717
+ }
718
+ /**
719
+ * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
720
+ */
721
+ isComponentsXmlFile(file) {
722
+ return (0, reflection_1.isXmlFile)(file) && !!/^(pkg:\/)?components[\/\\]/.exec(file.destPath);
374
723
  }
375
724
  /**
376
725
  * Ensure source scope is created.
@@ -381,238 +730,574 @@ class Program {
381
730
  const sourceScope = new Scope_1.Scope('source', this, 'scope:source');
382
731
  sourceScope.attachDependencyGraph(this.dependencyGraph);
383
732
  this.addScope(sourceScope);
733
+ this.plugins.emit('afterProvideScope', {
734
+ program: this,
735
+ scope: sourceScope
736
+ });
384
737
  }
385
738
  }
386
739
  /**
387
740
  * Remove a set of files from the program
388
- * @param srcPaths
741
+ * @param srcPaths can be an array of srcPath or destPath strings
742
+ * @param normalizePath should this function repair and standardize the filePaths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
389
743
  */
390
- removeFiles(srcPaths) {
744
+ removeFiles(srcPaths, normalizePath = true) {
391
745
  for (let srcPath of srcPaths) {
392
- this.removeFile(srcPath);
746
+ this.removeFile(srcPath, normalizePath);
393
747
  }
394
748
  }
395
749
  /**
396
750
  * Remove a file from the program
397
- * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
751
+ * @param filePath can be a srcPath, a destPath, or a destPath with leading `pkg:/`
398
752
  * @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
399
-
400
753
  */
401
- removeFile(filePath, normalizePath = true) {
754
+ removeFile(filePath, normalizePath = true, keepSymbolInformation = false) {
755
+ var _a, _b, _c, _d;
402
756
  this.logger.debug('Program.removeFile()', filePath);
403
- let file = this.getFile(filePath, normalizePath);
404
- if (file) {
405
- this.plugins.emit('beforeFileDispose', {
406
- program: this,
407
- file: file
408
- });
757
+ const paths = this.getPaths(filePath, this.options.rootDir);
758
+ //there can be one or more File entries for a single srcPath, so get all of them and remove them all
759
+ 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)];
760
+ for (const file of files) {
761
+ //if a file has already been removed, nothing more needs to be done here
762
+ if (!file || !this.hasFile(file.srcPath)) {
763
+ continue;
764
+ }
765
+ this.diagnostics.clearForFile(file.srcPath);
766
+ const event = { file: file, program: this };
767
+ this.plugins.emit('beforeRemoveFile', event);
409
768
  //if there is a scope named the same as this file's path, remove it (i.e. xml scopes)
410
- let scope = this.scopes[file.pkgPath];
769
+ let scope = this.scopes[file.destPath];
411
770
  if (scope) {
412
- this.plugins.emit('beforeScopeDispose', {
771
+ this.logger.debug('Removing associated scope', scope.name);
772
+ const scopeRemoveEvent = {
413
773
  program: this,
414
774
  scope: scope
415
- });
775
+ };
776
+ this.plugins.emit('beforeRemoveScope', scopeRemoveEvent);
777
+ this.plugins.emit('removeScope', scopeRemoveEvent);
416
778
  scope.dispose();
417
779
  //notify dependencies of this scope that it has been removed
418
780
  this.dependencyGraph.remove(scope.dependencyGraphKey);
419
- delete this.scopes[file.pkgPath];
420
- this.plugins.emit('afterScopeDispose', {
421
- program: this,
422
- scope: scope
423
- });
781
+ this.removeScope(this.scopes[file.destPath]);
782
+ this.plugins.emit('afterRemoveScope', scopeRemoveEvent);
424
783
  }
425
784
  //remove the file from the program
426
785
  this.unassignFile(file);
427
786
  this.dependencyGraph.remove(file.dependencyGraphKey);
428
787
  //if this is a pkg:/source file, notify the `source` scope that it has changed
429
- if (file.pkgPath.startsWith('pkg:/source/')) {
788
+ if (this.isSourceBrsFile(file)) {
430
789
  this.dependencyGraph.removeDependency('scope:source', file.dependencyGraphKey);
431
790
  }
791
+ if ((0, reflection_1.isBrsFile)(file)) {
792
+ this.logger.debug('Removing file symbol info', file.srcPath);
793
+ if (!keepSymbolInformation) {
794
+ this.fileSymbolInformation.delete(file.pkgPath);
795
+ }
796
+ this.crossScopeValidation.clearResolutionsForFile(file);
797
+ }
798
+ this.diagnostics.clearForFile(file.srcPath);
432
799
  //if this is a component, remove it from our components map
433
- if (reflection_1.isXmlFile(file)) {
800
+ if ((0, reflection_1.isXmlFile)(file)) {
801
+ this.logger.debug('Unregistering component', file.srcPath);
434
802
  this.unregisterComponent(file);
435
803
  }
436
- this.plugins.emit('afterFileDispose', {
437
- program: this,
438
- file: file
439
- });
804
+ this.logger.debug('Disposing file', file.srcPath);
805
+ //dispose any disposable things on the file
806
+ for (const disposable of (_c = file === null || file === void 0 ? void 0 : file.disposables) !== null && _c !== void 0 ? _c : []) {
807
+ disposable();
808
+ }
809
+ //dispose file
810
+ (_d = file === null || file === void 0 ? void 0 : file.dispose) === null || _d === void 0 ? void 0 : _d.call(file);
811
+ this.plugins.emit('afterRemoveFile', event);
440
812
  }
441
813
  }
442
- /**
443
- * Remove all files from the program that are in the specified folder path (recursive)
444
- * @param folderSrcPath The absolute path to the folder on disk
445
- * @param normalizePath should the provided path be normalized before use?
446
- */
447
- removeFilesInFolder(folderSrcPath, normalizePath = true) {
448
- if (normalizePath) {
449
- folderSrcPath = util_1.util.standardizePath(folderSrcPath);
814
+ validate(options) {
815
+ var _a;
816
+ const validationRunId = this.validationRunSequence++;
817
+ let previousValidationPromise = this.validatePromise;
818
+ const deferred = new deferred_1.Deferred();
819
+ if (options === null || options === void 0 ? void 0 : options.async) {
820
+ //we're async, so create a new promise chain to resolve after this validation is done
821
+ this.validatePromise = Promise.resolve(previousValidationPromise).then(() => {
822
+ return deferred.promise;
823
+ });
824
+ //we are not async but there's a pending promise, then we cannot run this validation
450
825
  }
451
- const lowerFolderSrcPath = folderSrcPath.toLowerCase();
452
- for (const key in this.files) {
453
- const file = this.files[key];
454
- const lowerSrcPath = file.srcPath.toLowerCase();
455
- //if the file path starts with the parent path and the file path does not exactly match the folder path
456
- if (lowerSrcPath.toLowerCase().startsWith(lowerFolderSrcPath) && lowerSrcPath !== lowerFolderSrcPath) {
457
- this.removeFile(file.srcPath, false);
458
- }
826
+ else if (previousValidationPromise !== undefined) {
827
+ throw new Error('Cannot run synchronous validation while an async validation is in progress');
459
828
  }
460
- }
461
- /**
462
- * Traverse the entire project, and validate all scopes
463
- * @param force - if true, then all scopes are force to validate, even if they aren't marked as dirty
464
- */
465
- validate() {
466
- this.logger.time(Logger_1.LogLevel.log, ['Validating project'], () => {
467
- var _a;
468
- this.diagnostics = [];
469
- this.plugins.emit('beforeProgramValidate', {
829
+ let beforeValidateProgramWasEmitted = false;
830
+ const brsFilesValidated = this.validationDetails.brsFilesValidated;
831
+ const xmlFilesValidated = this.validationDetails.xmlFilesValidated;
832
+ const changedSymbols = this.validationDetails.changedSymbols;
833
+ const changedComponentTypes = this.validationDetails.changedComponentTypes;
834
+ const scopesToValidate = this.validationDetails.scopesToValidate;
835
+ const filesToBeValidatedInScopeContext = this.validationDetails.filesToBeValidatedInScopeContext;
836
+ //validate every file
837
+ let logValidateEnd = (status) => { };
838
+ //will be populated later on during the correspnding sequencer event
839
+ let filesToProcess;
840
+ const sequencer = new Sequencer_1.Sequencer({
841
+ name: 'program.validate',
842
+ cancellationToken: (_a = options === null || options === void 0 ? void 0 : options.cancellationToken) !== null && _a !== void 0 ? _a : new vscode_languageserver_1.CancellationTokenSource().token,
843
+ minSyncDuration: this.validationMinSyncDuration
844
+ });
845
+ //this sequencer allows us to run in both sync and async mode, depending on whether options.async is enabled.
846
+ //We use this to prevent starving the CPU during long validate cycles when running in a language server context
847
+ sequencer
848
+ .once('wait for previous run', () => {
849
+ //if running in async mode, return the previous validation promise to ensure we're only running one at a time
850
+ if (options === null || options === void 0 ? void 0 : options.async) {
851
+ return previousValidationPromise;
852
+ }
853
+ })
854
+ .once('before and on programValidate', () => {
855
+ logValidateEnd = this.logger.timeStart(logging_1.LogLevel.log, `Validating project${this.logger.logLevel > logging_1.LogLevel.log ? ` (run ${validationRunId})` : ''}`);
856
+ this.diagnostics.clearForTag(ProgramValidator_1.ProgramValidatorDiagnosticsTag);
857
+ this.plugins.emit('beforeValidateProgram', {
858
+ program: this
859
+ });
860
+ beforeValidateProgramWasEmitted = true;
861
+ this.plugins.emit('validateProgram', {
470
862
  program: this
471
863
  });
472
- //validate every file
473
- for (const file of Object.values(this.files)) {
474
- //find any files NOT loaded into a scope
475
- if (!this.fileIsIncludedInAnyScope(file)) {
476
- this.logger.debug('Program.validate(): fileNotReferenced by any scope', () => chalk_1.default.green(file === null || file === void 0 ? void 0 : file.pkgPath));
477
- //the file is not loaded in any scope
478
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.fileNotReferencedByAnyOtherFile()), { file: file, range: util_1.util.createRange(0, 0, 0, Number.MAX_VALUE) }));
864
+ })
865
+ .once('get files to be validated', () => {
866
+ filesToProcess = Object.values(this.files).sort((0, thenby_1.firstBy)(x => x.srcPath)).filter(x => !x.isValidated);
867
+ for (const file of filesToProcess) {
868
+ filesToBeValidatedInScopeContext.add(file);
869
+ }
870
+ })
871
+ .once('add component reference types', () => {
872
+ // Create reference component types for any component that changes
873
+ for (let [componentKey, componentName] of this.componentSymbolsToUpdate.entries()) {
874
+ this.addComponentReferenceType(componentKey, componentName);
875
+ }
876
+ })
877
+ .forEach('beforeValidateFile', () => filesToProcess, (file) => {
878
+ //run the beforeFilevalidate event for every unvalidated file
879
+ this.plugins.emit('beforeValidateFile', {
880
+ program: this,
881
+ file: file
882
+ });
883
+ })
884
+ .forEach('validateFile', () => filesToProcess, (file) => {
885
+ //run the validateFile event for every unvalidated file
886
+ this.plugins.emit('validateFile', {
887
+ program: this,
888
+ file: file
889
+ });
890
+ file.isValidated = true;
891
+ if ((0, reflection_1.isBrsFile)(file)) {
892
+ brsFilesValidated.push(file);
893
+ }
894
+ else if ((0, reflection_1.isXmlFile)(file)) {
895
+ xmlFilesValidated.push(file);
896
+ }
897
+ })
898
+ .forEach('afterValidateFile', () => filesToProcess, (file) => {
899
+ //run the validateFile event for every unvalidated file
900
+ this.plugins.emit('afterValidateFile', {
901
+ program: this,
902
+ file: file
903
+ });
904
+ })
905
+ .forEach('do deferred component creation', () => [...brsFilesValidated, ...xmlFilesValidated], (file) => {
906
+ if ((0, reflection_1.isXmlFile)(file)) {
907
+ this.addDeferredComponentTypeSymbolCreation(file);
908
+ }
909
+ else if ((0, reflection_1.isBrsFile)(file)) {
910
+ const fileHasChanges = file.providedSymbols.changes.get(1 /* SymbolTypeFlag.runtime */).size > 0 || file.providedSymbols.changes.get(2 /* SymbolTypeFlag.typetime */).size > 0;
911
+ if (fileHasChanges) {
912
+ for (const scope of this.getScopesForFile(file)) {
913
+ if ((0, reflection_1.isXmlScope)(scope) && this.doesXmlFileRequireProvidedSymbols(scope.xmlFile, file.providedSymbols.changes)) {
914
+ this.addDeferredComponentTypeSymbolCreation(scope.xmlFile);
915
+ }
916
+ }
479
917
  }
480
- //for every unvalidated file, validate it
481
- if (!file.isValidated) {
482
- this.plugins.emit('beforeFileValidate', {
483
- program: this,
484
- file: file
485
- });
486
- //call file.validate() IF the file has that function defined
487
- (_a = file.validate) === null || _a === void 0 ? void 0 : _a.call(file);
488
- file.isValidated = true;
489
- this.plugins.emit('afterFileValidate', {
490
- program: this,
491
- file: file
492
- });
918
+ }
919
+ })
920
+ .once('build component types for any component that changes', () => {
921
+ this.logger.time(logging_1.LogLevel.info, ['Build component types'], () => {
922
+ this.logger.debug(`Component Symbols to update:`, [...this.componentSymbolsToUpdate.entries()].sort());
923
+ this.lastValidationInfo.componentsRebuilt = new Set();
924
+ for (let [componentKey, componentName] of this.componentSymbolsToUpdate.entries()) {
925
+ this.lastValidationInfo.componentsRebuilt.add(componentName === null || componentName === void 0 ? void 0 : componentName.toLowerCase());
926
+ if (this.updateComponentSymbolInGlobalScope(componentKey, componentName)) {
927
+ changedComponentTypes.push(util_1.util.getSgNodeTypeName(componentName).toLowerCase());
928
+ }
929
+ }
930
+ this.componentSymbolsToUpdate.clear();
931
+ });
932
+ })
933
+ .once('track and update type-time and runtime symbol dependencies and changes', () => {
934
+ var _a, _b, _c, _d;
935
+ const changedSymbolsMapArr = (_a = [...brsFilesValidated, ...xmlFilesValidated]) === null || _a === void 0 ? void 0 : _a.map(f => {
936
+ if ((0, reflection_1.isBrsFile)(f)) {
937
+ return f.providedSymbols.changes;
938
+ }
939
+ return null;
940
+ }).filter(x => x);
941
+ // update the map of typetime dependencies
942
+ for (const file of brsFilesValidated) {
943
+ for (const [symbolName, provided] of file.providedSymbols.symbolMap.get(2 /* SymbolTypeFlag.typetime */).entries()) {
944
+ // clear existing dependencies
945
+ for (const values of this.symbolDependencies.values()) {
946
+ values.delete(symbolName);
947
+ }
948
+ // map types to the set of types that depend upon them
949
+ for (const dependentSymbol of (_c = (_b = provided.requiredSymbolNames) === null || _b === void 0 ? void 0 : _b.values()) !== null && _c !== void 0 ? _c : []) {
950
+ const dependentSymbolLower = dependentSymbol.toLowerCase();
951
+ if (!this.symbolDependencies.has(dependentSymbolLower)) {
952
+ this.symbolDependencies.set(dependentSymbolLower, new Set());
953
+ }
954
+ const symbolsDependentUpon = this.symbolDependencies.get(dependentSymbolLower);
955
+ symbolsDependentUpon.add(symbolName);
956
+ }
493
957
  }
494
958
  }
495
- this.logger.time(Logger_1.LogLevel.info, ['Validate all scopes'], () => {
496
- for (let scope of Object.values(this.scopes)) {
497
- //only validate unvalidated scopes
498
- if (!scope.isValidated) {
499
- this.plugins.emit('beforeScopeValidate', {
500
- program: this,
501
- scope: scope
502
- });
503
- scope.validate();
504
- scope.isValidated = true;
505
- this.plugins.emit('afterScopeValidate', {
506
- program: this,
507
- scope: scope
508
- });
959
+ for (const flag of [1 /* SymbolTypeFlag.runtime */, 2 /* SymbolTypeFlag.typetime */]) {
960
+ const changedSymbolsSetArr = changedSymbolsMapArr.map(symMap => symMap.get(flag));
961
+ const changedSymbolSet = new Set();
962
+ for (const changeSet of changedSymbolsSetArr) {
963
+ for (const change of changeSet) {
964
+ changedSymbolSet.add(change);
965
+ }
966
+ }
967
+ if (!changedSymbols.has(flag)) {
968
+ changedSymbols.set(flag, changedSymbolSet);
969
+ }
970
+ else {
971
+ changedSymbols.set(flag, new Set([...changedSymbols.get(flag), ...changedSymbolSet]));
972
+ }
973
+ }
974
+ // update changed symbol set with any changed component
975
+ for (const changedComponentType of changedComponentTypes) {
976
+ changedSymbols.get(2 /* SymbolTypeFlag.typetime */).add(changedComponentType);
977
+ }
978
+ // Add any additional types that depend on a changed type
979
+ // as each iteration of the loop might add new types, need to keep checking until nothing new is added
980
+ const dependentTypesChanged = new Set();
981
+ let foundDependentTypes = false;
982
+ const changedTypeSymbols = changedSymbols.get(2 /* SymbolTypeFlag.typetime */);
983
+ do {
984
+ foundDependentTypes = false;
985
+ const allChangedTypesSofar = [...Array.from(changedTypeSymbols), ...Array.from(dependentTypesChanged)];
986
+ for (const changedSymbol of allChangedTypesSofar) {
987
+ const symbolsDependentUponChangedSymbol = (_d = this.symbolDependencies.get(changedSymbol)) !== null && _d !== void 0 ? _d : [];
988
+ for (const symbolName of symbolsDependentUponChangedSymbol) {
989
+ if (!changedTypeSymbols.has(symbolName) && !dependentTypesChanged.has(symbolName)) {
990
+ foundDependentTypes = true;
991
+ dependentTypesChanged.add(symbolName);
992
+ }
509
993
  }
510
994
  }
995
+ } while (foundDependentTypes);
996
+ changedSymbols.set(2 /* SymbolTypeFlag.typetime */, new Set([...changedSymbols.get(2 /* SymbolTypeFlag.typetime */), ...changedTypeSymbols, ...dependentTypesChanged]));
997
+ this.lastValidationInfo.brsFilesSrcPath = new Set(this.validationDetails.brsFilesValidated.map(f => { var _a, _b; return (_b = (_a = f.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; }));
998
+ this.lastValidationInfo.xmlFilesSrcPath = new Set(this.validationDetails.xmlFilesValidated.map(f => { var _a, _b; return (_b = (_a = f.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; }));
999
+ // can reset filesValidatedList, because they are no longer needed
1000
+ this.validationDetails.brsFilesValidated = [];
1001
+ this.validationDetails.xmlFilesValidated = [];
1002
+ })
1003
+ .once('tracks changed symbols and prepares files and scopes for validation', () => {
1004
+ if (this.options.logLevel === logging_1.LogLevel.debug) {
1005
+ const changedRuntime = Array.from(changedSymbols.get(1 /* SymbolTypeFlag.runtime */)).sort();
1006
+ this.logger.debug('Changed Symbols (runTime):', changedRuntime.join(', '));
1007
+ const changedTypetime = Array.from(changedSymbols.get(2 /* SymbolTypeFlag.typetime */)).sort();
1008
+ this.logger.debug('Changed Symbols (typeTime):', changedTypetime.join(', '));
1009
+ }
1010
+ const didComponentChange = changedComponentTypes.length > 0;
1011
+ const didProvidedSymbolChange = changedSymbols.get(1 /* SymbolTypeFlag.runtime */).size > 0 || changedSymbols.get(2 /* SymbolTypeFlag.typetime */).size > 0;
1012
+ const scopesToCheck = this.getScopesForCrossScopeValidation(didComponentChange, didProvidedSymbolChange);
1013
+ this.crossScopeValidation.buildComponentsMap();
1014
+ this.logger.time(logging_1.LogLevel.info, ['addDiagnosticsForScopes'], () => {
1015
+ this.crossScopeValidation.addDiagnosticsForScopes(scopesToCheck);
511
1016
  });
512
- this.detectDuplicateComponentNames();
513
- this.plugins.emit('afterProgramValidate', {
514
- program: this
1017
+ const filesToRevalidate = this.crossScopeValidation.getFilesRequiringChangedSymbol(scopesToCheck, changedSymbols);
1018
+ for (const file of filesToRevalidate) {
1019
+ filesToBeValidatedInScopeContext.add(file);
1020
+ }
1021
+ this.currentScopeValidationOptions = {
1022
+ filesToBeValidatedInScopeContext: filesToBeValidatedInScopeContext,
1023
+ changedSymbols: changedSymbols,
1024
+ changedFiles: Array.from(filesToBeValidatedInScopeContext),
1025
+ initialValidation: this.isFirstValidation
1026
+ };
1027
+ //can reset changedComponent types
1028
+ this.validationDetails.changedComponentTypes = [];
1029
+ })
1030
+ .forEach('invalidate affected scopes', () => filesToBeValidatedInScopeContext, (file) => {
1031
+ if ((0, reflection_1.isBrsFile)(file)) {
1032
+ file.validationSegmenter.unValidateAllSegments();
1033
+ for (const scope of this.getScopesForFile(file)) {
1034
+ scope.invalidate();
1035
+ }
1036
+ }
1037
+ })
1038
+ .once('checking scopes to validate', () => {
1039
+ //sort the scope names so we get consistent results
1040
+ for (const scopeName of this.getSortedScopeNames()) {
1041
+ let scope = this.scopes[scopeName];
1042
+ if (scope.shouldValidate(this.currentScopeValidationOptions)) {
1043
+ scopesToValidate.push(scope);
1044
+ }
1045
+ }
1046
+ this.lastValidationInfo.scopeNames = new Set(scopesToValidate.map(s => { var _a, _b; return (_b = (_a = s.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; }));
1047
+ })
1048
+ .forEach('beforeScopeValidate', () => scopesToValidate, (scope) => {
1049
+ this.plugins.emit('beforeValidateScope', {
1050
+ program: this,
1051
+ scope: scope
1052
+ });
1053
+ })
1054
+ .forEach('validate scope', () => scopesToValidate, (scope) => {
1055
+ scope.validate(this.currentScopeValidationOptions);
1056
+ })
1057
+ .forEach('afterValidateScope', () => scopesToValidate, (scope) => {
1058
+ this.plugins.emit('afterValidateScope', {
1059
+ program: this,
1060
+ scope: scope
515
1061
  });
1062
+ })
1063
+ .once('detect duplicate component names', () => {
1064
+ this.detectDuplicateComponentNames();
1065
+ this.isFirstValidation = false;
1066
+ // can reset other validation details
1067
+ this.validationDetails.changedSymbols = new Map();
1068
+ this.validationDetails.scopesToValidate = [];
1069
+ this.validationDetails.filesToBeValidatedInScopeContext = new Set();
1070
+ })
1071
+ .onCancel(() => {
1072
+ logValidateEnd('cancelled');
1073
+ })
1074
+ .onSuccess(() => {
1075
+ logValidateEnd();
1076
+ })
1077
+ .onComplete(() => {
1078
+ var _a, _b;
1079
+ //if we emitted the beforeValidateProgram hook, emit the afterValidateProgram hook as well
1080
+ if (beforeValidateProgramWasEmitted) {
1081
+ const wasCancelled = (_b = (_a = options === null || options === void 0 ? void 0 : options.cancellationToken) === null || _a === void 0 ? void 0 : _a.isCancellationRequested) !== null && _b !== void 0 ? _b : false;
1082
+ this.plugins.emit('afterValidateProgram', {
1083
+ program: this,
1084
+ wasCancelled: wasCancelled
1085
+ });
1086
+ }
1087
+ //log all the sequencer timing metrics if `info` logging is enabled
1088
+ this.logger.info(sequencer.formatMetrics({
1089
+ header: 'Program.validate metrics:',
1090
+ //only include loop iterations if `debug` logging is enabled
1091
+ includeLoopIterations: this.logger.isLogLevelEnabled(logging_1.LogLevel.debug)
1092
+ }));
1093
+ //regardless of the success of the validation, mark this run as complete
1094
+ deferred.resolve();
1095
+ //clear the validatePromise which means we're no longer running a validation
1096
+ this.validatePromise = undefined;
516
1097
  });
1098
+ //run the sequencer in async mode if enabled
1099
+ if (options === null || options === void 0 ? void 0 : options.async) {
1100
+ return sequencer.run();
1101
+ //run the sequencer in sync mode
1102
+ }
1103
+ else {
1104
+ return sequencer.runSync();
1105
+ }
1106
+ }
1107
+ getScopesForCrossScopeValidation(someComponentTypeChanged, didProvidedSymbolChange) {
1108
+ const scopesForCrossScopeValidation = [];
1109
+ for (let scopeName of this.getSortedScopeNames()) {
1110
+ let scope = this.scopes[scopeName];
1111
+ if (this.globalScope === scope) {
1112
+ continue;
1113
+ }
1114
+ if (someComponentTypeChanged) {
1115
+ scopesForCrossScopeValidation.push(scope);
1116
+ }
1117
+ if (didProvidedSymbolChange && !scope.isValidated) {
1118
+ scopesForCrossScopeValidation.push(scope);
1119
+ }
1120
+ }
1121
+ return scopesForCrossScopeValidation;
1122
+ }
1123
+ doesXmlFileRequireProvidedSymbols(file, providedSymbolsByFlag) {
1124
+ for (const required of file.requiredSymbols) {
1125
+ const symbolNameLower = required.name.toLowerCase();
1126
+ const requiredSymbolIsProvided = providedSymbolsByFlag.get(required.flags).has(symbolNameLower);
1127
+ if (requiredSymbolIsProvided) {
1128
+ return true;
1129
+ }
1130
+ }
1131
+ return false;
517
1132
  }
518
1133
  /**
519
1134
  * Flag all duplicate component names
520
1135
  */
521
1136
  detectDuplicateComponentNames() {
522
- var _a;
523
- const componentsByName = new Map();
524
- for (const key in this.files) {
525
- const file = this.files[key];
1137
+ const componentsByName = Object.keys(this.files).reduce((map, filePath) => {
1138
+ var _a;
1139
+ const file = this.files[filePath];
526
1140
  //if this is an XmlFile, and it has a valid `componentName` property
527
- if (reflection_1.isXmlFile(file)) {
528
- const componentNameLower = (_a = file.componentName) === null || _a === void 0 ? void 0 : _a.text.toLowerCase();
529
- if (componentNameLower) {
530
- if (!componentsByName.has(componentNameLower)) {
531
- componentsByName.set(componentNameLower, [file]);
532
- }
533
- else {
534
- componentsByName.get(componentNameLower).push(file);
535
- }
1141
+ if ((0, reflection_1.isXmlFile)(file) && ((_a = file.componentName) === null || _a === void 0 ? void 0 : _a.text)) {
1142
+ let lowerName = file.componentName.text.toLowerCase();
1143
+ if (!map[lowerName]) {
1144
+ map[lowerName] = [];
536
1145
  }
1146
+ map[lowerName].push(file);
537
1147
  }
538
- }
539
- for (const xmlFiles of componentsByName.values()) {
1148
+ return map;
1149
+ }, {});
1150
+ for (let name in componentsByName) {
1151
+ const xmlFiles = componentsByName[name];
540
1152
  //add diagnostics for every duplicate component with this name
541
1153
  if (xmlFiles.length > 1) {
542
1154
  for (let xmlFile of xmlFiles) {
543
1155
  const { componentName } = xmlFile;
544
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.duplicateComponentName(componentName.text)), { range: xmlFile.componentName.range, file: xmlFile, relatedInformation: xmlFiles.filter(x => x !== xmlFile).map(x => {
1156
+ this.diagnostics.register(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.duplicateComponentName(componentName.text)), { location: xmlFile.componentName.location, relatedInformation: xmlFiles.filter(x => x !== xmlFile).map(x => {
545
1157
  return {
546
- location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(xmlFile.srcPath).toString(), x.componentName.range),
1158
+ location: x.componentName.location,
547
1159
  message: 'Also defined here'
548
1160
  };
549
- }) }));
1161
+ }) }), { tags: [ProgramValidator_1.ProgramValidatorDiagnosticsTag] });
550
1162
  }
551
1163
  }
552
1164
  }
553
1165
  }
554
1166
  /**
555
- * Determine at least one scope has the file
1167
+ * Get the files for a list of filePaths
1168
+ * @param filePaths can be an array of srcPath or a destPath strings
1169
+ * @param normalizePath should this function repair and standardize the paths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
556
1170
  */
557
- fileIsIncludedInAnyScope(file) {
558
- for (let scope of Object.values(this.scopes)) {
559
- if (scope.hasFile(file)) {
560
- return true;
561
- }
562
- }
563
- return false;
1171
+ getFiles(filePaths, normalizePath = true) {
1172
+ return filePaths
1173
+ .map(filePath => this.getFile(filePath, normalizePath))
1174
+ .filter(file => file !== undefined);
564
1175
  }
565
1176
  /**
566
1177
  * Get the file at the given path
567
- * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
1178
+ * @param filePath can be a srcPath or a destPath
568
1179
  * @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
569
1180
  */
570
1181
  getFile(filePath, normalizePath = true) {
1182
+ if (this.getFilePathCache.has(filePath)) {
1183
+ const cachedFilePath = this.getFilePathCache.get(filePath);
1184
+ if (cachedFilePath.isDestMap) {
1185
+ return this.destMap.get(cachedFilePath.path);
1186
+ }
1187
+ return this.files[cachedFilePath.path];
1188
+ }
571
1189
  if (typeof filePath !== 'string') {
572
1190
  return undefined;
1191
+ //is the path absolute (or the `virtual:` prefix)
1192
+ }
1193
+ else if (/^(?:(?:virtual:[\/\\])|(?:\w:)|(?:[\/\\]))/gmi.exec(filePath)) {
1194
+ const standardizedPath = (normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase();
1195
+ this.getFilePathCache.set(filePath, { path: standardizedPath });
1196
+ return this.files[standardizedPath];
573
1197
  }
574
- else if (path.isAbsolute(filePath)) {
575
- return this.files[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
1198
+ else if (util_1.util.isUriLike(filePath)) {
1199
+ const path = vscode_uri_1.URI.parse(filePath).fsPath;
1200
+ const standardizedPath = (normalizePath ? util_1.util.standardizePath(path) : path).toLowerCase();
1201
+ this.getFilePathCache.set(filePath, { path: standardizedPath });
1202
+ return this.files[standardizedPath];
576
1203
  }
577
1204
  else {
578
- return this.pkgMap[(normalizePath ? util_1.util.sanitizePkgPath(filePath) : filePath).toLowerCase()];
1205
+ const standardizedPath = (normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase();
1206
+ this.getFilePathCache.set(filePath, { path: standardizedPath, isDestMap: true });
1207
+ return this.destMap.get(standardizedPath);
1208
+ }
1209
+ }
1210
+ /**
1211
+ * Gets a sorted list of all scopeNames, always beginning with "global", "source", then any others in alphabetical order
1212
+ */
1213
+ getSortedScopeNames() {
1214
+ if (!this.sortedScopeNames) {
1215
+ this.sortedScopeNames = Object.keys(this.scopes).sort((a, b) => {
1216
+ if (a === 'global') {
1217
+ return -1;
1218
+ }
1219
+ else if (b === 'global') {
1220
+ return 1;
1221
+ }
1222
+ if (a === 'source') {
1223
+ return -1;
1224
+ }
1225
+ else if (b === 'source') {
1226
+ return 1;
1227
+ }
1228
+ if (a < b) {
1229
+ return -1;
1230
+ }
1231
+ else if (b < a) {
1232
+ return 1;
1233
+ }
1234
+ return 0;
1235
+ });
579
1236
  }
1237
+ return this.sortedScopeNames;
580
1238
  }
581
1239
  /**
582
1240
  * Get a list of all scopes the file is loaded into
583
- * @param file
1241
+ * @param file the file
584
1242
  */
585
1243
  getScopesForFile(file) {
1244
+ const resolvedFile = typeof file === 'string' ? this.getFile(file) : file;
586
1245
  let result = [];
587
- for (let key in this.scopes) {
1246
+ if (resolvedFile) {
1247
+ const scopeKeys = this.getSortedScopeNames();
1248
+ for (let key of scopeKeys) {
1249
+ let scope = this.scopes[key];
1250
+ if (scope.hasFile(resolvedFile)) {
1251
+ result.push(scope);
1252
+ }
1253
+ }
1254
+ }
1255
+ return result;
1256
+ }
1257
+ /**
1258
+ * Get the first found scope for a file.
1259
+ */
1260
+ getFirstScopeForFile(file) {
1261
+ const scopeKeys = this.getSortedScopeNames();
1262
+ for (let key of scopeKeys) {
588
1263
  let scope = this.scopes[key];
589
1264
  if (scope.hasFile(file)) {
590
- result.push(scope);
1265
+ return scope;
591
1266
  }
592
1267
  }
593
- return result;
594
1268
  }
595
1269
  getStatementsByName(name, originFile, namespaceName) {
596
- var _a, _b;
597
1270
  let results = new Map();
598
1271
  const filesSearched = new Set();
599
1272
  let lowerNamespaceName = namespaceName === null || namespaceName === void 0 ? void 0 : namespaceName.toLowerCase();
600
1273
  let lowerName = name === null || name === void 0 ? void 0 : name.toLowerCase();
1274
+ function addToResults(statement, file) {
1275
+ var _a, _b;
1276
+ let parentNamespaceName = (_b = (_a = statement.findAncestor(reflection_1.isNamespaceStatement)) === null || _a === void 0 ? void 0 : _a.getName(originFile.parseMode)) === null || _b === void 0 ? void 0 : _b.toLowerCase();
1277
+ if (statement.tokens.name.text.toLowerCase() === lowerName && (!lowerNamespaceName || parentNamespaceName === lowerNamespaceName)) {
1278
+ if (!results.has(statement)) {
1279
+ results.set(statement, { item: statement, file: file });
1280
+ }
1281
+ }
1282
+ }
601
1283
  //look through all files in scope for matches
602
1284
  for (const scope of this.getScopesForFile(originFile)) {
603
1285
  for (const file of scope.getAllFiles()) {
604
- if (reflection_1.isXmlFile(file) || filesSearched.has(file)) {
1286
+ //skip non-brs files, or files we've already processed
1287
+ if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
605
1288
  continue;
606
1289
  }
607
1290
  filesSearched.add(file);
608
- for (const statement of [...file.parser.references.functionStatements, ...file.parser.references.classStatements.flatMap((cs) => cs.methods)]) {
609
- let parentNamespaceName = (_b = (_a = statement.namespaceName) === null || _a === void 0 ? void 0 : _a.getName(originFile.parseMode)) === null || _b === void 0 ? void 0 : _b.toLowerCase();
610
- if (statement.name.text.toLowerCase() === lowerName && (!parentNamespaceName || parentNamespaceName === lowerNamespaceName)) {
611
- if (!results.has(statement)) {
612
- results.set(statement, { item: statement, file: file });
613
- }
1291
+ file.ast.walk((0, visitors_1.createVisitor)({
1292
+ FunctionStatement: (statement) => {
1293
+ addToResults(statement, file);
1294
+ },
1295
+ MethodStatement: (statement) => {
1296
+ addToResults(statement, file);
614
1297
  }
615
- }
1298
+ }), {
1299
+ walkMode: visitors_1.WalkMode.visitStatements
1300
+ });
616
1301
  }
617
1302
  }
618
1303
  return [...results.values()];
@@ -624,136 +1309,165 @@ class Program {
624
1309
  //get all function names for the xml file and parents
625
1310
  let funcNames = new Set();
626
1311
  let currentScope = scope;
627
- while (reflection_1.isXmlScope(currentScope)) {
628
- for (let member of (_b = (_a = currentScope.xmlFile.ast.component) === null || _a === void 0 ? void 0 : _a.interfaceMembers) !== null && _b !== void 0 ? _b : []) {
629
- if (reflection_1.isSGInterfaceFunction(member)) {
630
- const name = member.name;
631
- if (!filterName || name === filterName) {
632
- funcNames.add(name);
633
- }
1312
+ while ((0, reflection_1.isXmlScope)(currentScope)) {
1313
+ 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 : []) {
1314
+ if (!filterName || name === filterName) {
1315
+ funcNames.add(name);
634
1316
  }
635
1317
  }
636
1318
  currentScope = currentScope.getParentScope();
637
1319
  }
638
1320
  //look through all files in scope for matches
639
1321
  for (const file of scope.getOwnFiles()) {
640
- if (reflection_1.isXmlFile(file) || filesSearched.has(file)) {
1322
+ //skip non-brs files, or files we've already processed
1323
+ if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
641
1324
  continue;
642
1325
  }
643
1326
  filesSearched.add(file);
644
- for (const statement of file.parser.references.functionStatements) {
645
- if (funcNames.has(statement.name.text)) {
646
- if (!results.has(statement)) {
647
- results.set(statement, { item: statement, file: file });
1327
+ file.ast.walk((0, visitors_1.createVisitor)({
1328
+ FunctionStatement: (statement) => {
1329
+ if (funcNames.has(statement.tokens.name.text)) {
1330
+ if (!results.has(statement)) {
1331
+ results.set(statement, { item: statement, file: file });
1332
+ }
648
1333
  }
649
1334
  }
650
- }
1335
+ }), {
1336
+ walkMode: visitors_1.WalkMode.visitStatements
1337
+ });
651
1338
  }
652
1339
  return [...results.values()];
653
1340
  }
654
1341
  /**
655
1342
  * Find all available completion items at the given position
656
- * @param srcPath The absolute path to the source file on disk
657
- * @param lineIndex
658
- * @param columnIndex
1343
+ * @param filePath can be a srcPath or a destPath
1344
+ * @param position the position (line & column) where completions should be found
659
1345
  */
660
- getCompletions(srcPath, position) {
661
- let file = this.getFile(srcPath);
1346
+ getCompletions(filePath, position) {
1347
+ let file = this.getFile(filePath);
662
1348
  if (!file) {
663
1349
  return [];
664
1350
  }
665
- let result = [];
666
- if (reflection_1.isBrsFile(file) && file.parser.isPositionNextToTokenKind(position, lexer_1.TokenKind.Callfunc)) {
667
- // is next to a @. callfunc invocation - must be an interface method
668
- for (const scope of this.getScopes().filter((s) => reflection_1.isXmlScope(s))) {
669
- let fileLinks = this.getStatementsForXmlFile(scope);
670
- for (let fileLink of fileLinks) {
671
- result.push(scope.createCompletionFromFunctionStatement(fileLink.item));
672
- }
673
- }
674
- //no other result is possible in this case
675
- return result;
676
- }
677
- //find the scopes for this file
678
- let scopes = this.getScopesForFile(file);
679
- //if there are no scopes, include the global scope so we at least get the built-in functions
680
- scopes = scopes.length > 0 ? scopes : [this.globalScope];
681
- //get the completions from all scopes for this file
682
- let allCompletions = util_1.util.flatMap(scopes.map(ctx => file.getCompletions(position, ctx)), c => c);
683
- //only keep completions common to every scope for this file
684
- let keyCounts = {};
685
- for (let completion of allCompletions) {
686
- let key = `${completion.label}-${completion.kind}`;
687
- keyCounts[key] = keyCounts[key] ? keyCounts[key] + 1 : 1;
688
- if (keyCounts[key] === scopes.length) {
689
- result.push(completion);
690
- }
691
- }
692
- return result;
1351
+ const event = {
1352
+ program: this,
1353
+ file: file,
1354
+ scopes: this.getScopesForFile(file),
1355
+ position: position,
1356
+ completions: []
1357
+ };
1358
+ this.plugins.emit('beforeProvideCompletions', event);
1359
+ this.plugins.emit('provideCompletions', event);
1360
+ this.plugins.emit('afterProvideCompletions', event);
1361
+ return event.completions;
693
1362
  }
694
1363
  /**
695
1364
  * Goes through each file and builds a list of workspace symbols for the program. Used by LanguageServer's onWorkspaceSymbol functionality
696
1365
  */
697
1366
  getWorkspaceSymbols() {
698
- const result = [];
699
- for (const key in this.files) {
700
- const file = this.files[key];
701
- if (reflection_1.isBrsFile(file)) {
702
- result.push(...file.getWorkspaceSymbols());
703
- }
704
- }
705
- return result;
1367
+ const event = {
1368
+ program: this,
1369
+ workspaceSymbols: []
1370
+ };
1371
+ this.plugins.emit('beforeProvideWorkspaceSymbols', event);
1372
+ this.plugins.emit('provideWorkspaceSymbols', event);
1373
+ this.plugins.emit('afterProvideWorkspaceSymbols', event);
1374
+ return event.workspaceSymbols;
706
1375
  }
707
1376
  /**
708
1377
  * Given a position in a file, if the position is sitting on some type of identifier,
709
1378
  * go to the definition of that identifier (where this thing was first defined)
710
- * @param srcPath The absolute path to the source file on disk
711
1379
  */
712
1380
  getDefinition(srcPath, position) {
713
1381
  let file = this.getFile(srcPath);
714
1382
  if (!file) {
715
1383
  return [];
716
1384
  }
717
- if (reflection_1.isBrsFile(file)) {
718
- return file.getDefinition(position);
719
- }
720
- else {
721
- let results = [];
722
- const scopes = this.getScopesForFile(file);
723
- for (const scope of scopes) {
724
- results = results.concat(...scope.getDefinition(file, position));
725
- }
726
- return results;
727
- }
1385
+ const event = {
1386
+ program: this,
1387
+ file: file,
1388
+ position: position,
1389
+ definitions: []
1390
+ };
1391
+ this.plugins.emit('beforeProvideDefinition', event);
1392
+ this.plugins.emit('provideDefinition', event);
1393
+ this.plugins.emit('afterProvideDefinition', event);
1394
+ return event.definitions;
728
1395
  }
729
1396
  /**
730
- * @param srcPath The absolute path to the source file on disk
1397
+ * Get hover information for a file and position
731
1398
  */
732
1399
  getHover(srcPath, position) {
733
- //find the file
734
1400
  let file = this.getFile(srcPath);
735
- if (!file) {
736
- return null;
1401
+ let result;
1402
+ if (file) {
1403
+ const event = {
1404
+ program: this,
1405
+ file: file,
1406
+ position: position,
1407
+ scopes: this.getScopesForFile(file),
1408
+ hovers: []
1409
+ };
1410
+ this.plugins.emit('beforeProvideHover', event);
1411
+ this.plugins.emit('provideHover', event);
1412
+ this.plugins.emit('afterProvideHover', event);
1413
+ result = event.hovers;
1414
+ }
1415
+ return result !== null && result !== void 0 ? result : [];
1416
+ }
1417
+ /**
1418
+ * Get full list of document symbols for a file
1419
+ * @param srcPath path to the file
1420
+ */
1421
+ getDocumentSymbols(srcPath) {
1422
+ let file = this.getFile(srcPath);
1423
+ if (file) {
1424
+ const event = {
1425
+ program: this,
1426
+ file: file,
1427
+ documentSymbols: []
1428
+ };
1429
+ this.plugins.emit('beforeProvideDocumentSymbols', event);
1430
+ this.plugins.emit('provideDocumentSymbols', event);
1431
+ this.plugins.emit('afterProvideDocumentSymbols', event);
1432
+ return event.documentSymbols;
1433
+ }
1434
+ else {
1435
+ return undefined;
737
1436
  }
738
- return Promise.resolve(file.getHover(position));
739
1437
  }
740
1438
  /**
741
1439
  * Compute code actions for the given file and range
742
- * @param srcPath The absolute path to the source file on disk
743
1440
  */
744
1441
  getCodeActions(srcPath, range) {
745
1442
  const codeActions = [];
746
1443
  const file = this.getFile(srcPath);
747
1444
  if (file) {
1445
+ const fileUri = util_1.util.pathToUri(file === null || file === void 0 ? void 0 : file.srcPath);
748
1446
  const diagnostics = this
749
1447
  //get all current diagnostics (filtered by diagnostic filters)
750
1448
  .getDiagnostics()
751
1449
  //only keep diagnostics related to this file
752
- .filter(x => x.file === file)
1450
+ .filter(x => { var _a; return ((_a = x.location) === null || _a === void 0 ? void 0 : _a.uri) === fileUri; })
753
1451
  //only keep diagnostics that touch this range
754
- .filter(x => util_1.util.rangesIntersect(x.range, range));
1452
+ .filter(x => util_1.util.rangesIntersectOrTouch(x.location.range, range));
755
1453
  const scopes = this.getScopesForFile(file);
756
- this.plugins.emit('onGetCodeActions', {
1454
+ this.plugins.emit('beforeProvideCodeActions', {
1455
+ program: this,
1456
+ file: file,
1457
+ range: range,
1458
+ diagnostics: diagnostics,
1459
+ scopes: scopes,
1460
+ codeActions: codeActions
1461
+ });
1462
+ this.plugins.emit('provideCodeActions', {
1463
+ program: this,
1464
+ file: file,
1465
+ range: range,
1466
+ diagnostics: diagnostics,
1467
+ scopes: scopes,
1468
+ codeActions: codeActions
1469
+ });
1470
+ this.plugins.emit('afterProvideCodeActions', {
757
1471
  program: this,
758
1472
  file: file,
759
1473
  range: range,
@@ -770,8 +1484,20 @@ class Program {
770
1484
  getSemanticTokens(srcPath) {
771
1485
  const file = this.getFile(srcPath);
772
1486
  if (file) {
1487
+ this.plugins.emit('beforeProvideSemanticTokens', {
1488
+ program: this,
1489
+ file: file,
1490
+ scopes: this.getScopesForFile(file),
1491
+ semanticTokens: undefined
1492
+ });
773
1493
  const result = [];
774
- this.plugins.emit('onGetSemanticTokens', {
1494
+ this.plugins.emit('provideSemanticTokens', {
1495
+ program: this,
1496
+ file: file,
1497
+ scopes: this.getScopesForFile(file),
1498
+ semanticTokens: result
1499
+ });
1500
+ this.plugins.emit('afterProvideSemanticTokens', {
775
1501
  program: this,
776
1502
  file: file,
777
1503
  scopes: this.getScopesForFile(file),
@@ -780,363 +1506,262 @@ class Program {
780
1506
  return result;
781
1507
  }
782
1508
  }
783
- getSignatureHelp(filepath, position) {
784
- var _a;
785
- let file = this.getFile(filepath);
786
- if (!file || !reflection_1.isBrsFile(file)) {
1509
+ getSignatureHelp(filePath, position) {
1510
+ let file = this.getFile(filePath);
1511
+ if (!file || !(0, reflection_1.isBrsFile)(file)) {
787
1512
  return [];
788
1513
  }
789
- const results = new Map();
790
- let functionExpression = file.getFunctionExpressionAtPosition(position);
791
- let identifierInfo = this.getPartialStatementInfo(file, position);
792
- if (identifierInfo.statementType === '') {
793
- // just general function calls
794
- let statements = file.program.getStatementsByName(identifierInfo.name, file);
795
- for (let statement of statements) {
796
- //TODO better handling of collisions - if it's a namespace, then don't show any other overrides
797
- //if we're on m - then limit scope to the current class, if present
798
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
799
- if (sigHelp && !results.has[sigHelp.key]) {
800
- sigHelp.index = identifierInfo.commaCount;
801
- results.set(sigHelp.key, sigHelp);
802
- }
803
- }
804
- }
805
- else if (identifierInfo.statementType === '.') {
806
- //if m class reference.. then
807
- //only get statements from the class I am in..
808
- if (functionExpression) {
809
- let myClass = file.getClassFromMReference(position, file.parser.getTokenAt(position), functionExpression);
810
- if (myClass) {
811
- for (let scope of this.getScopesForFile(myClass.file)) {
812
- let classes = scope.getClassHierarchy(myClass.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
813
- //and anything from any class in scope to a non m class
814
- for (let statement of [...classes].filter((i) => reflection_1.isClassMethodStatement(i.item))) {
815
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
816
- if (sigHelp && !results.has[sigHelp.key]) {
817
- results.set(sigHelp.key, sigHelp);
818
- return;
819
- }
820
- }
821
- }
822
- }
823
- }
824
- if (identifierInfo.dotPart) {
825
- //potential namespaces
826
- let statements = file.program.getStatementsByName(identifierInfo.name, file, identifierInfo.dotPart);
827
- if (statements.length === 0) {
828
- //was not a namespaced function, it could be any method on any class now
829
- statements = file.program.getStatementsByName(identifierInfo.name, file);
830
- }
831
- for (let statement of statements) {
832
- //TODO better handling of collisions - if it's a namespace, then don't show any other overrides
833
- //if we're on m - then limit scope to the current class, if present
834
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
835
- if (sigHelp && !results.has[sigHelp.key]) {
836
- sigHelp.index = identifierInfo.commaCount;
837
- results.set(sigHelp.key, sigHelp);
838
- }
839
- }
840
- }
841
- }
842
- else if (identifierInfo.statementType === '@.') {
843
- for (const scope of this.getScopes().filter((s) => reflection_1.isXmlScope(s))) {
844
- let fileLinks = this.getStatementsForXmlFile(scope, identifierInfo.name);
845
- for (let fileLink of fileLinks) {
846
- let sigHelp = fileLink.file.getSignatureHelpForStatement(fileLink.item);
847
- if (sigHelp && !results.has[sigHelp.key]) {
848
- sigHelp.index = identifierInfo.commaCount;
849
- results.set(sigHelp.key, sigHelp);
850
- }
851
- }
852
- }
853
- }
854
- else if (identifierInfo.statementType === 'new') {
855
- let classItem = file.getClassFileLink(identifierInfo.dotPart ? `${identifierInfo.dotPart}.${identifierInfo.name}` : identifierInfo.name);
856
- let sigHelp = (_a = classItem === null || classItem === void 0 ? void 0 : classItem.file) === null || _a === void 0 ? void 0 : _a.getClassSignatureHelp(classItem === null || classItem === void 0 ? void 0 : classItem.item);
857
- if (sigHelp && !results.has(sigHelp.key)) {
858
- sigHelp.index = identifierInfo.commaCount;
859
- results.set(sigHelp.key, sigHelp);
860
- }
861
- }
862
- return [...results.values()];
1514
+ let callExpressionInfo = new CallExpressionInfo_1.CallExpressionInfo(file, position);
1515
+ let signatureHelpUtil = new SignatureHelpUtil_1.SignatureHelpUtil();
1516
+ return signatureHelpUtil.getSignatureHelpItems(callExpressionInfo);
863
1517
  }
864
- getPartialStatementInfo(file, position) {
865
- let lines = util_1.util.splitIntoLines(file.fileContents);
866
- let line = lines[position.line];
867
- let index = position.character;
868
- let itemCounts = this.getPartialItemCounts(line, index);
869
- if (!itemCounts.isArgStartFound && line.charAt(index) === ')') {
870
- //try previous char, in case we were on a close bracket..
871
- index--;
872
- itemCounts = this.getPartialItemCounts(line, index);
873
- }
874
- let argStartIndex = itemCounts.argStartIndex;
875
- index = itemCounts.argStartIndex - 1;
876
- let statementType = '';
877
- let name;
878
- let dotPart;
879
- if (!itemCounts.isArgStartFound) {
880
- //try to get sig help based on the name
881
- index = position.character;
882
- let currentToken = file.parser.getTokenAt(position);
883
- if (currentToken && currentToken.kind !== lexer_1.TokenKind.Comment) {
884
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
885
- if (!name) {
886
- //try the previous token, incase we're on a bracket
887
- currentToken = file.parser.getPreviousToken(currentToken);
888
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
889
- }
890
- if (name === null || name === void 0 ? void 0 : name.indexOf('.')) {
891
- let parts = name.split('.');
892
- name = parts[parts.length - 1];
893
- }
894
- index = currentToken.range.start.character;
895
- argStartIndex = index;
896
- }
897
- else {
898
- // invalid location
899
- index = 0;
900
- itemCounts.comma = 0;
901
- }
902
- }
903
- //this loop is quirky. walk to -1 (which will result in the last char being '' thus satisfying the situation where there is no leading whitespace).
904
- while (index >= -1) {
905
- if (!(/[a-z0-9_\.\@]/i).test(line.charAt(index))) {
906
- if (!name) {
907
- name = line.substring(index + 1, argStartIndex);
908
- }
909
- else {
910
- dotPart = line.substring(index + 1, argStartIndex);
911
- if (dotPart.endsWith('.')) {
912
- dotPart = dotPart.substr(0, dotPart.length - 1);
1518
+ getReferences(srcPath, position) {
1519
+ //find the file
1520
+ let file = this.getFile(srcPath);
1521
+ const event = {
1522
+ program: this,
1523
+ file: file,
1524
+ position: position,
1525
+ references: []
1526
+ };
1527
+ this.plugins.emit('beforeProvideReferences', event);
1528
+ this.plugins.emit('provideReferences', event);
1529
+ this.plugins.emit('afterProvideReferences', event);
1530
+ return event.references;
1531
+ }
1532
+ /**
1533
+ * Transpile a single file and get the result as a string.
1534
+ * This does not write anything to the file system.
1535
+ *
1536
+ * This should only be called by `LanguageServer`.
1537
+ * Internal usage should call `_getTranspiledFileContents` instead.
1538
+ * @param filePath can be a srcPath or a destPath
1539
+ */
1540
+ async getTranspiledFileContents(filePath) {
1541
+ const file = this.getFile(filePath);
1542
+ return this.getTranspiledFileContentsPipeline.run(async () => {
1543
+ const result = {
1544
+ destPath: file.destPath,
1545
+ pkgPath: file.pkgPath,
1546
+ srcPath: file.srcPath
1547
+ };
1548
+ const expectedPkgPath = file.pkgPath.toLowerCase();
1549
+ const expectedMapPath = `${expectedPkgPath}.map`;
1550
+ const expectedTypedefPkgPath = expectedPkgPath.replace(/\.brs$/i, '.d.bs');
1551
+ //add a temporary plugin to tap into the file writing process
1552
+ const plugin = this.plugins.addFirst({
1553
+ name: 'getTranspiledFileContents',
1554
+ beforeWriteFile: (event) => {
1555
+ const pkgPath = event.file.pkgPath.toLowerCase();
1556
+ switch (pkgPath) {
1557
+ //this is the actual transpiled file
1558
+ case expectedPkgPath:
1559
+ result.code = event.file.data.toString();
1560
+ break;
1561
+ //this is the sourcemap
1562
+ case expectedMapPath:
1563
+ result.map = event.file.data.toString();
1564
+ break;
1565
+ //this is the typedef
1566
+ case expectedTypedefPkgPath:
1567
+ result.typedef = event.file.data.toString();
1568
+ break;
1569
+ default:
1570
+ //no idea what this file is. just ignore it
913
1571
  }
1572
+ //mark every file as processed so it they don't get written to the output directory
1573
+ event.processedFiles.add(event.file);
914
1574
  }
915
- break;
916
- }
917
- if (line.substr(index - 2, 2) === '@.') {
918
- statementType = '@.';
919
- name = name || line.substring(index, argStartIndex);
920
- break;
1575
+ });
1576
+ try {
1577
+ //now that the plugin has been registered, run the build with just this file
1578
+ await this.build({
1579
+ files: [file]
1580
+ });
921
1581
  }
922
- else if (line.charAt(index - 1) === '.' && statementType === '') {
923
- statementType = '.';
924
- name = name || line.substring(index, argStartIndex);
925
- argStartIndex = index;
1582
+ finally {
1583
+ this.plugins.remove(plugin);
926
1584
  }
927
- index--;
928
- }
929
- if (line.substring(0, index).trim().endsWith('new')) {
930
- statementType = 'new';
1585
+ return result;
1586
+ });
1587
+ }
1588
+ /**
1589
+ * Get the absolute output path for a file
1590
+ */
1591
+ getOutputPath(file, outDir = this.getOutDir()) {
1592
+ return (0, util_1.standardizePath) `${outDir}/${file.pkgPath}`;
1593
+ }
1594
+ getOutDir(outDir) {
1595
+ var _a, _b;
1596
+ let result = (_a = outDir !== null && outDir !== void 0 ? outDir : this.options.outDir) !== null && _a !== void 0 ? _a : this.options.outDir;
1597
+ if (!result) {
1598
+ result = roku_deploy_1.rokuDeploy.getOptions(this.options).outDir;
931
1599
  }
932
- return {
933
- commaCount: itemCounts.comma,
934
- statementType: statementType,
935
- name: name,
936
- dotPart: dotPart
937
- };
1600
+ result = (0, util_1.standardizePath) `${path.resolve((_b = this.options.cwd) !== null && _b !== void 0 ? _b : process.cwd(), result !== null && result !== void 0 ? result : '/')}`;
1601
+ return result;
938
1602
  }
939
- getPartialItemCounts(line, index) {
940
- let isArgStartFound = false;
941
- let itemCounts = {
942
- normal: 0,
943
- square: 0,
944
- curly: 0,
945
- comma: 0,
946
- endIndex: 0,
947
- argStartIndex: index,
948
- isArgStartFound: false
1603
+ /**
1604
+ * Prepare the program for building
1605
+ * @param files the list of files that should be prepared
1606
+ */
1607
+ async prepare(files) {
1608
+ const programEvent = {
1609
+ program: this,
1610
+ editor: this.editor,
1611
+ files: files
949
1612
  };
950
- while (index >= 0) {
951
- const currentChar = line.charAt(index);
952
- if (currentChar === '\'') { //found comment, invalid index
953
- itemCounts.isArgStartFound = false;
954
- break;
1613
+ //assign an editor to every file
1614
+ for (const file of programEvent.files) {
1615
+ //if the file doesn't have an editor yet, assign one now
1616
+ if (!file.editor) {
1617
+ file.editor = new Editor_1.Editor();
955
1618
  }
956
- if (isArgStartFound) {
957
- if (currentChar !== ' ') {
958
- break;
959
- }
1619
+ }
1620
+ //sort the entries to make transpiling more deterministic
1621
+ programEvent.files.sort((a, b) => {
1622
+ if (a.pkgPath < b.pkgPath) {
1623
+ return -1;
1624
+ }
1625
+ else if (a.pkgPath > b.pkgPath) {
1626
+ return 1;
960
1627
  }
961
1628
  else {
962
- if (currentChar === ')') {
963
- itemCounts.normal++;
964
- }
965
- if (currentChar === ']') {
966
- itemCounts.square++;
967
- }
968
- if (currentChar === '}') {
969
- itemCounts.curly++;
970
- }
971
- if (currentChar === ',' && itemCounts.normal <= 0 && itemCounts.curly <= 0 && itemCounts.square <= 0) {
972
- itemCounts.comma++;
973
- }
974
- if (currentChar === '(') {
975
- if (itemCounts.normal === 0) {
976
- itemCounts.isArgStartFound = true;
977
- itemCounts.argStartIndex = index;
978
- }
979
- else {
980
- itemCounts.normal--;
981
- }
982
- }
983
- if (currentChar === '[') {
984
- itemCounts.square--;
985
- }
986
- if (currentChar === '{') {
987
- itemCounts.curly--;
988
- }
1629
+ return 1;
1630
+ }
1631
+ });
1632
+ await this.plugins.emitAsync('beforePrepareProgram', programEvent);
1633
+ await this.plugins.emitAsync('prepareProgram', programEvent);
1634
+ const outDir = this.getOutDir();
1635
+ const entries = [];
1636
+ for (const file of files) {
1637
+ const scope = this.getFirstScopeForFile(file);
1638
+ //link the symbol table for all the files in this scope
1639
+ scope === null || scope === void 0 ? void 0 : scope.linkSymbolTable();
1640
+ //if the file doesn't have an editor yet, assign one now
1641
+ if (!file.editor) {
1642
+ file.editor = new Editor_1.Editor();
989
1643
  }
990
- index--;
1644
+ const event = {
1645
+ program: this,
1646
+ file: file,
1647
+ editor: file.editor,
1648
+ scope: scope,
1649
+ outputPath: this.getOutputPath(file, outDir)
1650
+ };
1651
+ await this.plugins.emitAsync('beforePrepareFile', event);
1652
+ await this.plugins.emitAsync('prepareFile', event);
1653
+ await this.plugins.emitAsync('afterPrepareFile', event);
1654
+ //TODO remove this in v1
1655
+ entries.push(event);
1656
+ //unlink the symbolTable so the next loop iteration can link theirs
1657
+ scope === null || scope === void 0 ? void 0 : scope.unlinkSymbolTable();
991
1658
  }
992
- return itemCounts;
1659
+ await this.plugins.emitAsync('afterPrepareProgram', programEvent);
1660
+ return files;
993
1661
  }
994
1662
  /**
995
- * @param srcPath The absolute path to the source file on disk
1663
+ * Generate the contents of every file
996
1664
  */
997
- getReferences(srcPath, position) {
998
- //find the file
999
- let file = this.getFile(srcPath);
1000
- if (!file) {
1001
- return null;
1665
+ async serialize(files) {
1666
+ const allFiles = new Map();
1667
+ //exclude prunable files if that option is enabled
1668
+ if (this.options.pruneEmptyCodeFiles === true) {
1669
+ files = files.filter(x => x.canBePruned !== true);
1002
1670
  }
1003
- return file.getReferences(position);
1004
- }
1005
- /**
1006
- * Get a list of all script imports, relative to the specified pkgPath
1007
- * @param sourcePkgPath - the pkgPath of the source that wants to resolve script imports.
1008
- */
1009
- getScriptImportCompletions(sourcePkgPath, scriptImport) {
1010
- let lowerSourcePkgPath = sourcePkgPath.toLowerCase();
1011
- let result = [];
1012
- /**
1013
- * hashtable to prevent duplicate results
1014
- */
1015
- let resultPkgPaths = {};
1016
- //restrict to only .brs files
1017
- for (const key in this.files) {
1018
- const file = this.files[key];
1019
- if (
1020
- //is a BrightScript or BrighterScript file
1021
- (file.extension === '.bs' || file.extension === '.brs') &&
1022
- //this file is not the current file
1023
- lowerSourcePkgPath !== file.pkgPath.toLowerCase()) {
1024
- //add the relative path
1025
- let relativePath = util_1.util.getRelativePath(sourcePkgPath, file.pkgPath).replace(/\\/g, '/');
1026
- const lowerPkgPath = file.pkgPath.toLowerCase();
1027
- if (!resultPkgPaths[lowerPkgPath]) {
1028
- resultPkgPaths[lowerPkgPath] = true;
1029
- result.push({
1030
- label: relativePath,
1031
- detail: file.srcPath,
1032
- kind: vscode_languageserver_1.CompletionItemKind.File,
1033
- textEdit: {
1034
- newText: relativePath,
1035
- range: scriptImport.filePathRange
1036
- }
1037
- });
1038
- //add the absolute path
1039
- result.push({
1040
- label: file.pkgPath,
1041
- detail: file.srcPath,
1042
- kind: vscode_languageserver_1.CompletionItemKind.File,
1043
- textEdit: {
1044
- newText: file.pkgPath,
1045
- range: scriptImport.filePathRange
1046
- }
1047
- });
1048
- }
1671
+ const serializeProgramEvent = await this.plugins.emitAsync('beforeSerializeProgram', {
1672
+ program: this,
1673
+ files: files,
1674
+ result: allFiles
1675
+ });
1676
+ await this.plugins.emitAsync('serializeProgram', serializeProgramEvent);
1677
+ // serialize each file
1678
+ for (const file of files) {
1679
+ let scope = this.getFirstScopeForFile(file);
1680
+ //if the file doesn't have a scope, create a temporary scope for the file so it can depend on scope-level items
1681
+ if (!scope) {
1682
+ scope = new Scope_1.Scope(`temporary-for-${file.pkgPath}`, this);
1683
+ scope.getAllFiles = () => [file];
1684
+ scope.getOwnFiles = scope.getAllFiles;
1049
1685
  }
1686
+ //link the symbol table for all the files in this scope
1687
+ scope === null || scope === void 0 ? void 0 : scope.linkSymbolTable();
1688
+ const event = {
1689
+ program: this,
1690
+ file: file,
1691
+ scope: scope,
1692
+ result: allFiles
1693
+ };
1694
+ await this.plugins.emitAsync('beforeSerializeFile', event);
1695
+ await this.plugins.emitAsync('serializeFile', event);
1696
+ await this.plugins.emitAsync('afterSerializeFile', event);
1697
+ //unlink the symbolTable so the next loop iteration can link theirs
1698
+ scope === null || scope === void 0 ? void 0 : scope.unlinkSymbolTable();
1050
1699
  }
1051
- return result;
1700
+ this.plugins.emit('afterSerializeProgram', serializeProgramEvent);
1701
+ return allFiles;
1052
1702
  }
1053
1703
  /**
1054
- * Transpile a single file and get the result as a string.
1055
- * This does not write anything to the file system.
1056
- * @param srcPath The absolute path to the source file on disk
1704
+ * Write the entire project to disk
1057
1705
  */
1058
- getTranspiledFileContents(srcPath) {
1059
- let file = this.getFile(srcPath);
1060
- let result = file.transpile();
1061
- return Object.assign(Object.assign({}, result), { srcPath: file.srcPath, pkgPath: file.pkgPath });
1062
- }
1063
- async transpile(fileEntries, stagingFolderPath) {
1064
- // map fileEntries using their path as key to avoid excessive "find()" operations
1065
- const mappedFileEntries = fileEntries.reduce((collection, entry) => {
1066
- collection[util_1.standardizePath `${entry.src}`] = entry;
1067
- return collection;
1068
- }, {});
1069
- const entries = [];
1070
- for (const key in this.files) {
1071
- const file = this.files[key];
1072
- let filePathObj = mappedFileEntries[util_1.standardizePath `${file.srcPath}`];
1073
- if (!filePathObj) {
1074
- //this file has been added in-memory, from a plugin, for example
1075
- filePathObj = {
1076
- //add an interpolated src path (since it doesn't actually exist in memory)
1077
- src: `bsc-in-memory:/${util_1.util.removeProtocol(file.pkgPath)}`,
1078
- dest: file.pkgPath
1079
- };
1080
- }
1081
- //prep the output path
1082
- let outputPath = filePathObj.dest
1083
- //replace any leading protocol
1084
- .replace(/^[-a-z_]+:\//, '')
1085
- //change any .bs file extension to .brs
1086
- .replace(/\.bs$/gi, '.brs');
1087
- //prepend the staging folder path
1088
- outputPath = util_1.standardizePath `${stagingFolderPath}/${outputPath}`;
1089
- entries.push({
1090
- file: file,
1091
- outputPath: outputPath
1092
- });
1093
- }
1094
- this.plugins.emit('beforeProgramTranspile', {
1706
+ async write(outDir, files) {
1707
+ const programEvent = await this.plugins.emitAsync('beforeWriteProgram', {
1095
1708
  program: this,
1096
- entries: entries
1709
+ files: files,
1710
+ outDir: outDir
1097
1711
  });
1098
- const promises = entries.map(async (entry) => {
1099
- //skip transpiling typedef files
1100
- if (reflection_1.isBrsFile(entry.file) && entry.file.isTypedef) {
1101
- return;
1102
- }
1103
- this.plugins.emit('beforeFileTranspile', {
1712
+ //empty the out directory
1713
+ await fsExtra.emptyDir(outDir);
1714
+ const serializedFiles = [...files]
1715
+ .map(([, serializedFiles]) => serializedFiles)
1716
+ .flat();
1717
+ //write all the files to disk (asynchronously)
1718
+ await Promise.all(serializedFiles.map(async (file) => {
1719
+ const event = await this.plugins.emitAsync('beforeWriteFile', {
1104
1720
  program: this,
1105
- file: entry.file,
1106
- outputPath: entry.outputPath
1721
+ file: file,
1722
+ outputPath: this.getOutputPath(file, outDir),
1723
+ processedFiles: new Set()
1107
1724
  });
1108
- const { file, outputPath } = entry;
1109
- const result = file.transpile();
1110
- //make sure the full dir path exists
1111
- await fsExtra.ensureDir(path.dirname(outputPath));
1112
- if (await fsExtra.pathExists(outputPath)) {
1113
- throw new Error(`Error while transpiling "${file.srcPath}". A file already exists at "${outputPath}" and will not be overwritten.`);
1114
- }
1115
- const writeMapPromise = result.map ? fsExtra.writeFile(`${outputPath}.map`, result.map.toString()) : null;
1116
- await Promise.all([
1117
- fsExtra.writeFile(outputPath, result.code),
1118
- writeMapPromise
1119
- ]);
1120
- if (reflection_1.isBrsFile(file) && this.options.emitDefinitions) {
1121
- const typedef = file.getTypedef();
1122
- const typedefPath = outputPath.replace(/\.brs$/i, '.d.bs');
1123
- await fsExtra.writeFile(typedefPath, typedef);
1124
- }
1125
- this.plugins.emit('afterFileTranspile', {
1725
+ await this.plugins.emitAsync('writeFile', event);
1726
+ await this.plugins.emitAsync('afterWriteFile', event);
1727
+ }));
1728
+ await this.plugins.emitAsync('afterWriteProgram', programEvent);
1729
+ }
1730
+ /**
1731
+ * Build the project. This transpiles/transforms/copies all files and moves them to the staging directory
1732
+ * @param options the list of options used to build the program
1733
+ */
1734
+ async build(options) {
1735
+ //run a single build at a time
1736
+ await this.buildPipeline.run(async () => {
1737
+ var _a;
1738
+ const outDir = this.getOutDir(options === null || options === void 0 ? void 0 : options.outDir);
1739
+ const event = await this.plugins.emitAsync('beforeBuildProgram', {
1126
1740
  program: this,
1127
- file: entry.file,
1128
- outputPath: entry.outputPath
1741
+ editor: this.editor,
1742
+ files: (_a = options === null || options === void 0 ? void 0 : options.files) !== null && _a !== void 0 ? _a : Object.values(this.files)
1129
1743
  });
1744
+ //prepare the program (and files) for building
1745
+ event.files = await this.prepare(event.files);
1746
+ //stage the entire program
1747
+ const serializedFilesByFile = await this.serialize(event.files);
1748
+ await this.write(outDir, serializedFilesByFile);
1749
+ await this.plugins.emitAsync('afterBuildProgram', event);
1750
+ //undo all edits for the program
1751
+ this.editor.undoAll();
1752
+ //undo all edits for each file
1753
+ for (const file of event.files) {
1754
+ file.editor.undoAll();
1755
+ }
1130
1756
  });
1131
- //if there's no bslib file already loaded into the program, copy it to the staging directory
1132
- if (!this.getFile(bslibAliasedRokuModulesPkgPath) && !this.getFile(`pkg:/source/bslib.brs`)) {
1133
- promises.push(util_1.util.copyBslibToStaging(stagingFolderPath));
1757
+ this.logger.debug('Types Created', helpers_1.TypesCreated);
1758
+ let totalTypesCreated = 0;
1759
+ for (const key in helpers_1.TypesCreated) {
1760
+ if (helpers_1.TypesCreated.hasOwnProperty(key)) {
1761
+ totalTypesCreated += helpers_1.TypesCreated[key];
1762
+ }
1134
1763
  }
1135
- await Promise.all(promises);
1136
- this.plugins.emit('afterProgramTranspile', {
1137
- program: this,
1138
- entries: entries
1139
- });
1764
+ this.logger.info('Total Types Created', totalTypesCreated);
1140
1765
  }
1141
1766
  /**
1142
1767
  * Find a list of files in the program that have a function with the given name (case INsensitive)
@@ -1146,10 +1771,11 @@ class Program {
1146
1771
  const lowerFunctionName = functionName.toLowerCase();
1147
1772
  //find every file with this function defined
1148
1773
  for (const file of Object.values(this.files)) {
1149
- if (reflection_1.isBrsFile(file)) {
1774
+ if ((0, reflection_1.isBrsFile)(file)) {
1150
1775
  //TODO handle namespace-relative function calls
1151
1776
  //if the file has a function with this name
1152
- if (file.parser.references.functionStatementLookup.get(lowerFunctionName) !== undefined) {
1777
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1778
+ if (file['_cachedLookups'].functionStatementMap.get(lowerFunctionName)) {
1153
1779
  files.push(file);
1154
1780
  }
1155
1781
  }
@@ -1157,55 +1783,144 @@ class Program {
1157
1783
  return files;
1158
1784
  }
1159
1785
  /**
1160
- * Find a list of files in the program that have a function with the given name (case INsensitive)
1786
+ * Find a list of files in the program that have a class with the given name (case INsensitive)
1161
1787
  */
1162
1788
  findFilesForClass(className) {
1163
1789
  const files = [];
1164
1790
  const lowerClassName = className.toLowerCase();
1165
1791
  //find every file with this class defined
1166
1792
  for (const file of Object.values(this.files)) {
1167
- if (reflection_1.isBrsFile(file)) {
1793
+ if ((0, reflection_1.isBrsFile)(file)) {
1168
1794
  //TODO handle namespace-relative classes
1169
1795
  //if the file has a function with this name
1170
- if (file.parser.references.classStatementLookup.get(lowerClassName) !== undefined) {
1796
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1797
+ if (file['_cachedLookups'].classStatementMap.get(lowerClassName) !== undefined) {
1798
+ files.push(file);
1799
+ }
1800
+ }
1801
+ }
1802
+ return files;
1803
+ }
1804
+ findFilesForNamespace(name) {
1805
+ const files = [];
1806
+ const lowerName = name.toLowerCase();
1807
+ //find every file with this class defined
1808
+ for (const file of Object.values(this.files)) {
1809
+ if ((0, reflection_1.isBrsFile)(file)) {
1810
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1811
+ if (file['_cachedLookups'].namespaceStatements.find((x) => {
1812
+ const namespaceName = x.name.toLowerCase();
1813
+ return (
1814
+ //the namespace name matches exactly
1815
+ namespaceName === lowerName ||
1816
+ //the full namespace starts with the name (honoring the part boundary)
1817
+ namespaceName.startsWith(lowerName + '.'));
1818
+ })) {
1171
1819
  files.push(file);
1172
1820
  }
1173
1821
  }
1174
1822
  }
1175
1823
  return files;
1176
1824
  }
1825
+ findFilesForEnum(name) {
1826
+ const files = [];
1827
+ const lowerName = name.toLowerCase();
1828
+ //find every file with this enum defined
1829
+ for (const file of Object.values(this.files)) {
1830
+ if ((0, reflection_1.isBrsFile)(file)) {
1831
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1832
+ if (file['_cachedLookups'].enumStatementMap.get(lowerName)) {
1833
+ files.push(file);
1834
+ }
1835
+ }
1836
+ }
1837
+ return files;
1838
+ }
1839
+ /**
1840
+ * Modify a parsed manifest map by reading `bs_const` and injecting values from `options.manifest.bs_const`
1841
+ * @param parsedManifest The manifest map to read from and modify
1842
+ */
1843
+ buildBsConstsIntoParsedManifest(parsedManifest) {
1844
+ var _a, _b;
1845
+ // Lift the bs_consts defined in the manifest
1846
+ let bsConsts = (0, Manifest_1.getBsConst)(parsedManifest, false);
1847
+ // Override or delete any bs_consts defined in the bs config
1848
+ 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) {
1849
+ const value = this.options.manifest.bs_const[key];
1850
+ if (value === null) {
1851
+ bsConsts.delete(key);
1852
+ }
1853
+ else {
1854
+ bsConsts.set(key, value);
1855
+ }
1856
+ }
1857
+ // convert the new list of bs consts back into a string for the rest of the down stream systems to use
1858
+ let constString = '';
1859
+ for (const [key, value] of bsConsts) {
1860
+ constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
1861
+ }
1862
+ // Set the updated bs_const value
1863
+ parsedManifest.set('bs_const', constString);
1864
+ }
1865
+ /**
1866
+ * Try to find and load the manifest into memory
1867
+ * @param manifestFileObj A pointer to a potential manifest file object found during loading
1868
+ * @param replaceIfAlreadyLoaded should we overwrite the internal `_manifest` if it already exists
1869
+ */
1870
+ loadManifest(manifestFileObj, replaceIfAlreadyLoaded = true) {
1871
+ //if we already have a manifest instance, and should not replace...then don't replace
1872
+ if (!replaceIfAlreadyLoaded && this._manifest) {
1873
+ return;
1874
+ }
1875
+ let manifestPath = manifestFileObj
1876
+ ? manifestFileObj.src
1877
+ : path.join(this.options.rootDir, 'manifest');
1878
+ try {
1879
+ // we only load this manifest once, so do it sync to improve speed downstream
1880
+ const contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1881
+ const parsedManifest = (0, Manifest_1.parseManifest)(contents);
1882
+ this.buildBsConstsIntoParsedManifest(parsedManifest);
1883
+ this._manifest = parsedManifest;
1884
+ }
1885
+ catch (e) {
1886
+ this._manifest = new Map();
1887
+ }
1888
+ }
1177
1889
  /**
1178
1890
  * Get a map of the manifest information
1179
1891
  */
1180
1892
  getManifest() {
1181
1893
  if (!this._manifest) {
1182
- //load the manifest file.
1183
- //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
1184
- let manifestPath = path.join(this.options.rootDir, 'manifest');
1185
- let contents;
1186
- try {
1187
- //we only load this manifest once, so do it sync to improve speed downstream
1188
- contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1189
- this._manifest = Manifest_1.parseManifest(contents);
1190
- }
1191
- catch (err) {
1192
- this._manifest = new Map();
1193
- }
1894
+ this.loadManifest();
1194
1895
  }
1195
1896
  return this._manifest;
1196
1897
  }
1197
1898
  dispose() {
1198
- var _a, _b;
1199
- for (const key in this.files) {
1200
- const file = this.files[key];
1201
- (_a = file.dispose) === null || _a === void 0 ? void 0 : _a.call(file);
1899
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1900
+ this.plugins.emit('beforeRemoveProgram', { program: this });
1901
+ for (let filePath in this.files) {
1902
+ (_b = (_a = this.files[filePath]) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
1202
1903
  }
1203
1904
  for (let name in this.scopes) {
1204
- (_b = this.scopes[name]) === null || _b === void 0 ? void 0 : _b.dispose();
1905
+ (_d = (_c = this.scopes[name]) === null || _c === void 0 ? void 0 : _c.dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
1205
1906
  }
1206
- this.globalScope.dispose();
1207
- this.dependencyGraph.dispose();
1907
+ (_f = (_e = this.globalScope) === null || _e === void 0 ? void 0 : _e.dispose) === null || _f === void 0 ? void 0 : _f.call(_e);
1908
+ (_h = (_g = this.dependencyGraph) === null || _g === void 0 ? void 0 : _g.dispose) === null || _h === void 0 ? void 0 : _h.call(_g);
1909
+ this.plugins.emit('removeProgram', { program: this });
1910
+ this.plugins.emit('afterRemoveProgram', { program: this });
1208
1911
  }
1209
1912
  }
1210
1913
  exports.Program = Program;
1914
+ class ProvideFileEventInternal {
1915
+ constructor(program, srcPath, destPath, data, fileFactory) {
1916
+ var _a;
1917
+ this.program = program;
1918
+ this.srcPath = srcPath;
1919
+ this.destPath = destPath;
1920
+ this.data = data;
1921
+ this.fileFactory = fileFactory;
1922
+ this.files = [];
1923
+ this.srcExtension = (_a = path.extname(srcPath)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
1924
+ }
1925
+ }
1211
1926
  //# sourceMappingURL=Program.js.map