brighterscript 1.0.0-alpha.4 → 1.0.0-alpha.40

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 (620) hide show
  1. package/CHANGELOG.md +1387 -291
  2. package/README.md +72 -131
  3. package/bsconfig.schema.json +85 -1
  4. package/dist/ActionPipeline.d.ts +10 -0
  5. package/dist/ActionPipeline.js +40 -0
  6. package/dist/ActionPipeline.js.map +1 -0
  7. package/dist/AstValidationSegmenter.d.ts +42 -0
  8. package/dist/AstValidationSegmenter.js +237 -0
  9. package/dist/AstValidationSegmenter.js.map +1 -0
  10. package/dist/BsConfig.d.ts +56 -6
  11. package/dist/BusyStatusTracker.d.ts +31 -0
  12. package/dist/BusyStatusTracker.js +83 -0
  13. package/dist/BusyStatusTracker.js.map +1 -0
  14. package/dist/Cache.d.ts +5 -6
  15. package/dist/Cache.js +12 -11
  16. package/dist/Cache.js.map +1 -1
  17. package/dist/CacheVerifier.d.ts +7 -0
  18. package/dist/CacheVerifier.js +20 -0
  19. package/dist/CacheVerifier.js.map +1 -0
  20. package/dist/CodeActionUtil.d.ts +6 -3
  21. package/dist/CodeActionUtil.js +19 -5
  22. package/dist/CodeActionUtil.js.map +1 -1
  23. package/dist/CommentFlagProcessor.d.ts +7 -6
  24. package/dist/CommentFlagProcessor.js +11 -8
  25. package/dist/CommentFlagProcessor.js.map +1 -1
  26. package/dist/CrossScopeValidator.d.ts +67 -0
  27. package/dist/CrossScopeValidator.js +617 -0
  28. package/dist/CrossScopeValidator.js.map +1 -0
  29. package/dist/DependencyGraph.d.ts +8 -3
  30. package/dist/DependencyGraph.js +49 -16
  31. package/dist/DependencyGraph.js.map +1 -1
  32. package/dist/DiagnosticCollection.d.ts +5 -3
  33. package/dist/DiagnosticCollection.js +21 -16
  34. package/dist/DiagnosticCollection.js.map +1 -1
  35. package/dist/DiagnosticFilterer.d.ts +8 -4
  36. package/dist/DiagnosticFilterer.js +90 -54
  37. package/dist/DiagnosticFilterer.js.map +1 -1
  38. package/dist/DiagnosticManager.d.ts +61 -0
  39. package/dist/DiagnosticManager.js +238 -0
  40. package/dist/DiagnosticManager.js.map +1 -0
  41. package/dist/DiagnosticMessages.d.ts +204 -24
  42. package/dist/DiagnosticMessages.js +266 -33
  43. package/dist/DiagnosticMessages.js.map +1 -1
  44. package/dist/DiagnosticSeverityAdjuster.d.ts +7 -0
  45. package/dist/DiagnosticSeverityAdjuster.js +41 -0
  46. package/dist/DiagnosticSeverityAdjuster.js.map +1 -0
  47. package/dist/FunctionScope.d.ts +28 -0
  48. package/dist/FunctionScope.js +52 -0
  49. package/dist/FunctionScope.js.map +1 -0
  50. package/dist/KeyedThrottler.d.ts +3 -3
  51. package/dist/KeyedThrottler.js +3 -3
  52. package/dist/KeyedThrottler.js.map +1 -1
  53. package/dist/LanguageServer.d.ts +72 -47
  54. package/dist/LanguageServer.js +545 -314
  55. package/dist/LanguageServer.js.map +1 -1
  56. package/dist/Logger.d.ts +9 -10
  57. package/dist/Logger.js +36 -30
  58. package/dist/Logger.js.map +1 -1
  59. package/dist/PluginInterface.d.ts +29 -7
  60. package/dist/PluginInterface.js +90 -7
  61. package/dist/PluginInterface.js.map +1 -1
  62. package/dist/Program.d.ts +201 -100
  63. package/dist/Program.js +1079 -700
  64. package/dist/Program.js.map +1 -1
  65. package/dist/ProgramBuilder.d.ts +29 -18
  66. package/dist/ProgramBuilder.js +178 -141
  67. package/dist/ProgramBuilder.js.map +1 -1
  68. package/dist/Scope.d.ts +144 -109
  69. package/dist/Scope.js +533 -552
  70. package/dist/Scope.js.map +1 -1
  71. package/dist/SemanticTokenUtils.d.ts +14 -0
  72. package/dist/SemanticTokenUtils.js +85 -0
  73. package/dist/SemanticTokenUtils.js.map +1 -0
  74. package/dist/Stopwatch.d.ts +4 -0
  75. package/dist/Stopwatch.js +8 -1
  76. package/dist/Stopwatch.js.map +1 -1
  77. package/dist/SymbolTable.d.ts +91 -24
  78. package/dist/SymbolTable.js +291 -64
  79. package/dist/SymbolTable.js.map +1 -1
  80. package/dist/SymbolTypeFlag.d.ts +9 -0
  81. package/dist/SymbolTypeFlag.js +14 -0
  82. package/dist/SymbolTypeFlag.js.map +1 -0
  83. package/dist/Throttler.d.ts +12 -0
  84. package/dist/Throttler.js +39 -0
  85. package/dist/Throttler.js.map +1 -1
  86. package/dist/Watcher.d.ts +0 -3
  87. package/dist/Watcher.js +0 -3
  88. package/dist/Watcher.js.map +1 -1
  89. package/dist/XmlScope.d.ts +5 -15
  90. package/dist/XmlScope.js +35 -87
  91. package/dist/XmlScope.js.map +1 -1
  92. package/dist/astUtils/CachedLookups.d.ts +50 -0
  93. package/dist/astUtils/CachedLookups.js +335 -0
  94. package/dist/astUtils/CachedLookups.js.map +1 -0
  95. package/dist/astUtils/CachedLookups.spec.js +39 -0
  96. package/dist/astUtils/CachedLookups.spec.js.map +1 -0
  97. package/dist/astUtils/Editor.d.ts +69 -0
  98. package/dist/astUtils/Editor.js +245 -0
  99. package/dist/astUtils/Editor.js.map +1 -0
  100. package/dist/astUtils/Editor.spec.js +258 -0
  101. package/dist/astUtils/Editor.spec.js.map +1 -0
  102. package/dist/astUtils/creators.d.ts +36 -19
  103. package/dist/astUtils/creators.js +222 -43
  104. package/dist/astUtils/creators.js.map +1 -1
  105. package/dist/astUtils/creators.spec.js +5 -5
  106. package/dist/astUtils/creators.spec.js.map +1 -1
  107. package/dist/astUtils/reflection.d.ts +148 -82
  108. package/dist/astUtils/reflection.js +324 -137
  109. package/dist/astUtils/reflection.js.map +1 -1
  110. package/dist/astUtils/reflection.spec.js +267 -167
  111. package/dist/astUtils/reflection.spec.js.map +1 -1
  112. package/dist/astUtils/stackedVisitor.js.map +1 -1
  113. package/dist/astUtils/stackedVisitor.spec.js +14 -14
  114. package/dist/astUtils/stackedVisitor.spec.js.map +1 -1
  115. package/dist/astUtils/visitors.d.ts +114 -53
  116. package/dist/astUtils/visitors.js +70 -13
  117. package/dist/astUtils/visitors.js.map +1 -1
  118. package/dist/astUtils/visitors.spec.js +463 -51
  119. package/dist/astUtils/visitors.spec.js.map +1 -1
  120. package/dist/astUtils/xml.d.ts +9 -8
  121. package/dist/astUtils/xml.js +10 -5
  122. package/dist/astUtils/xml.js.map +1 -1
  123. package/dist/bscPlugin/BscPlugin.d.ts +22 -1
  124. package/dist/bscPlugin/BscPlugin.js +88 -0
  125. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  126. package/dist/bscPlugin/CallExpressionInfo.d.ts +36 -0
  127. package/dist/bscPlugin/CallExpressionInfo.js +135 -0
  128. package/dist/bscPlugin/CallExpressionInfo.js.map +1 -0
  129. package/dist/bscPlugin/FileWriter.d.ts +6 -0
  130. package/dist/bscPlugin/FileWriter.js +24 -0
  131. package/dist/bscPlugin/FileWriter.js.map +1 -0
  132. package/dist/bscPlugin/SignatureHelpUtil.d.ts +10 -0
  133. package/dist/bscPlugin/SignatureHelpUtil.js +137 -0
  134. package/dist/bscPlugin/SignatureHelpUtil.js.map +1 -0
  135. package/dist/bscPlugin/codeActions/CodeActionsProcessor.d.ts +1 -1
  136. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +30 -18
  137. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  138. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +94 -21
  139. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  140. package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +64 -0
  141. package/dist/bscPlugin/completions/CompletionsProcessor.js +626 -0
  142. package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -0
  143. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js +2188 -0
  144. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js.map +1 -0
  145. package/dist/bscPlugin/definition/DefinitionProvider.d.ts +13 -0
  146. package/dist/bscPlugin/definition/DefinitionProvider.js +212 -0
  147. package/dist/bscPlugin/definition/DefinitionProvider.js.map +1 -0
  148. package/dist/bscPlugin/definition/DefinitionProvider.spec.d.ts +1 -0
  149. package/dist/bscPlugin/definition/DefinitionProvider.spec.js +87 -0
  150. package/dist/bscPlugin/definition/DefinitionProvider.spec.js.map +1 -0
  151. package/dist/bscPlugin/fileProviders/FileProvider.d.ts +9 -0
  152. package/dist/bscPlugin/fileProviders/FileProvider.js +51 -0
  153. package/dist/bscPlugin/fileProviders/FileProvider.js.map +1 -0
  154. package/dist/bscPlugin/hover/HoverProcessor.d.ts +18 -0
  155. package/dist/bscPlugin/hover/HoverProcessor.js +218 -0
  156. package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -0
  157. package/dist/bscPlugin/hover/HoverProcessor.spec.d.ts +1 -0
  158. package/dist/bscPlugin/hover/HoverProcessor.spec.js +786 -0
  159. package/dist/bscPlugin/hover/HoverProcessor.spec.js.map +1 -0
  160. package/dist/bscPlugin/references/ReferencesProvider.d.ts +12 -0
  161. package/dist/bscPlugin/references/ReferencesProvider.js +57 -0
  162. package/dist/bscPlugin/references/ReferencesProvider.js.map +1 -0
  163. package/dist/bscPlugin/references/ReferencesProvider.spec.d.ts +1 -0
  164. package/dist/bscPlugin/references/ReferencesProvider.spec.js +51 -0
  165. package/dist/bscPlugin/references/ReferencesProvider.spec.js.map +1 -0
  166. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +14 -0
  167. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +154 -0
  168. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -0
  169. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.d.ts +1 -0
  170. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js +530 -0
  171. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -0
  172. package/dist/bscPlugin/serialize/BslibInjector.spec.d.ts +1 -0
  173. package/dist/bscPlugin/serialize/BslibInjector.spec.js +33 -0
  174. package/dist/bscPlugin/serialize/BslibInjector.spec.js.map +1 -0
  175. package/dist/bscPlugin/serialize/BslibManager.d.ts +12 -0
  176. package/dist/bscPlugin/serialize/BslibManager.js +46 -0
  177. package/dist/bscPlugin/serialize/BslibManager.js.map +1 -0
  178. package/dist/bscPlugin/serialize/FileSerializer.d.ts +9 -0
  179. package/dist/bscPlugin/serialize/FileSerializer.js +75 -0
  180. package/dist/bscPlugin/serialize/FileSerializer.js.map +1 -0
  181. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.d.ts +7 -0
  182. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js +22 -0
  183. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js.map +1 -0
  184. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.d.ts +1 -0
  185. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.js +291 -0
  186. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.spec.js.map +1 -0
  187. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.d.ts +7 -0
  188. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js +26 -0
  189. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js.map +1 -0
  190. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.d.ts +1 -0
  191. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.js +245 -0
  192. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.spec.js.map +1 -0
  193. package/dist/bscPlugin/symbols/symbolUtils.d.ts +5 -0
  194. package/dist/bscPlugin/symbols/symbolUtils.js +141 -0
  195. package/dist/bscPlugin/symbols/symbolUtils.js.map +1 -0
  196. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.d.ts +21 -0
  197. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js +201 -0
  198. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +1 -0
  199. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.d.ts +1 -0
  200. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js +75 -0
  201. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js.map +1 -0
  202. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.d.ts +12 -0
  203. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js +99 -0
  204. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js.map +1 -0
  205. package/dist/bscPlugin/validation/BrsFileAfterValidator.d.ts +7 -0
  206. package/dist/bscPlugin/validation/BrsFileAfterValidator.js +18 -0
  207. package/dist/bscPlugin/validation/BrsFileAfterValidator.js.map +1 -0
  208. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +36 -0
  209. package/dist/bscPlugin/validation/BrsFileValidator.js +534 -0
  210. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -0
  211. package/dist/bscPlugin/validation/BrsFileValidator.spec.d.ts +1 -0
  212. package/dist/bscPlugin/validation/BrsFileValidator.spec.js +1118 -0
  213. package/dist/bscPlugin/validation/BrsFileValidator.spec.js.map +1 -0
  214. package/dist/bscPlugin/validation/ProgramValidator.d.ts +11 -0
  215. package/dist/bscPlugin/validation/ProgramValidator.js +33 -0
  216. package/dist/bscPlugin/validation/ProgramValidator.js.map +1 -0
  217. package/dist/bscPlugin/validation/ScopeValidator.d.ts +126 -0
  218. package/dist/bscPlugin/validation/ScopeValidator.js +1039 -0
  219. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -0
  220. package/dist/bscPlugin/validation/ScopeValidator.spec.d.ts +1 -0
  221. package/dist/bscPlugin/validation/ScopeValidator.spec.js +3346 -0
  222. package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +1 -0
  223. package/dist/bscPlugin/validation/XmlFileValidator.d.ts +8 -0
  224. package/dist/bscPlugin/validation/XmlFileValidator.js +44 -0
  225. package/dist/bscPlugin/validation/XmlFileValidator.js.map +1 -0
  226. package/dist/cli.js +117 -11
  227. package/dist/cli.js.map +1 -1
  228. package/dist/deferred.d.ts +3 -3
  229. package/dist/deferred.js.map +1 -1
  230. package/dist/diagnosticUtils.d.ts +10 -3
  231. package/dist/diagnosticUtils.js +62 -24
  232. package/dist/diagnosticUtils.js.map +1 -1
  233. package/dist/examples/plugins/removePrint.js +8 -12
  234. package/dist/examples/plugins/removePrint.js.map +1 -1
  235. package/dist/files/AssetFile.d.ts +24 -0
  236. package/dist/files/AssetFile.js +25 -0
  237. package/dist/files/AssetFile.js.map +1 -0
  238. package/dist/files/BrsFile.Class.spec.js +912 -153
  239. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  240. package/dist/files/BrsFile.d.ts +142 -85
  241. package/dist/files/BrsFile.js +845 -935
  242. package/dist/files/BrsFile.js.map +1 -1
  243. package/dist/files/BrsFile.spec.js +3778 -862
  244. package/dist/files/BrsFile.spec.js.map +1 -1
  245. package/dist/files/BscFile.d.ts +101 -0
  246. package/dist/files/BscFile.js +15 -0
  247. package/dist/files/BscFile.js.map +1 -0
  248. package/dist/files/Factory.d.ts +25 -0
  249. package/dist/files/Factory.js +22 -0
  250. package/dist/files/Factory.js.map +1 -0
  251. package/dist/files/LazyFileData.d.ts +20 -0
  252. package/dist/files/LazyFileData.js +54 -0
  253. package/dist/files/LazyFileData.js.map +1 -0
  254. package/dist/files/LazyFileData.spec.d.ts +1 -0
  255. package/dist/files/LazyFileData.spec.js +27 -0
  256. package/dist/files/LazyFileData.spec.js.map +1 -0
  257. package/dist/files/XmlFile.d.ts +73 -41
  258. package/dist/files/XmlFile.js +128 -138
  259. package/dist/files/XmlFile.js.map +1 -1
  260. package/dist/files/XmlFile.spec.js +451 -324
  261. package/dist/files/XmlFile.spec.js.map +1 -1
  262. package/dist/files/tests/imports.spec.js +62 -52
  263. package/dist/files/tests/imports.spec.js.map +1 -1
  264. package/dist/files/tests/optionalChaning.spec.d.ts +1 -0
  265. package/dist/files/tests/optionalChaning.spec.js +152 -0
  266. package/dist/files/tests/optionalChaning.spec.js.map +1 -0
  267. package/dist/globalCallables.d.ts +3 -1
  268. package/dist/globalCallables.js +417 -163
  269. package/dist/globalCallables.js.map +1 -1
  270. package/dist/index.d.ts +26 -3
  271. package/dist/index.js +44 -5
  272. package/dist/index.js.map +1 -1
  273. package/dist/interfaces.d.ts +783 -122
  274. package/dist/interfaces.js +21 -0
  275. package/dist/interfaces.js.map +1 -1
  276. package/dist/lexer/Character.spec.js +5 -5
  277. package/dist/lexer/Character.spec.js.map +1 -1
  278. package/dist/lexer/Lexer.d.ts +51 -12
  279. package/dist/lexer/Lexer.js +201 -57
  280. package/dist/lexer/Lexer.js.map +1 -1
  281. package/dist/lexer/Lexer.spec.js +781 -565
  282. package/dist/lexer/Lexer.spec.js.map +1 -1
  283. package/dist/lexer/Token.d.ts +27 -11
  284. package/dist/lexer/Token.js +10 -2
  285. package/dist/lexer/Token.js.map +1 -1
  286. package/dist/lexer/TokenKind.d.ts +31 -2
  287. package/dist/lexer/TokenKind.js +134 -10
  288. package/dist/lexer/TokenKind.js.map +1 -1
  289. package/dist/logging.d.ts +9 -0
  290. package/dist/logging.js +16 -0
  291. package/dist/logging.js.map +1 -0
  292. package/dist/parser/AstNode.d.ts +191 -0
  293. package/dist/parser/AstNode.js +269 -0
  294. package/dist/parser/AstNode.js.map +1 -0
  295. package/dist/parser/AstNode.spec.d.ts +1 -0
  296. package/dist/parser/AstNode.spec.js +1455 -0
  297. package/dist/parser/AstNode.spec.js.map +1 -0
  298. package/dist/parser/BrightScriptDocParser.d.ts +56 -0
  299. package/dist/parser/BrightScriptDocParser.js +294 -0
  300. package/dist/parser/BrightScriptDocParser.js.map +1 -0
  301. package/dist/parser/BrightScriptDocParser.spec.d.ts +1 -0
  302. package/dist/parser/BrightScriptDocParser.spec.js +310 -0
  303. package/dist/parser/BrightScriptDocParser.spec.js.map +1 -0
  304. package/dist/parser/BrsTranspileState.d.ts +22 -3
  305. package/dist/parser/BrsTranspileState.js +19 -0
  306. package/dist/parser/BrsTranspileState.js.map +1 -1
  307. package/dist/parser/Expression.d.ts +489 -221
  308. package/dist/parser/Expression.js +1206 -506
  309. package/dist/parser/Expression.js.map +1 -1
  310. package/dist/parser/Expression.spec.d.ts +1 -0
  311. package/dist/parser/Expression.spec.js +40 -0
  312. package/dist/parser/Expression.spec.js.map +1 -0
  313. package/dist/parser/Parser.Class.spec.js +230 -125
  314. package/dist/parser/Parser.Class.spec.js.map +1 -1
  315. package/dist/parser/Parser.d.ts +113 -124
  316. package/dist/parser/Parser.js +1510 -983
  317. package/dist/parser/Parser.js.map +1 -1
  318. package/dist/parser/Parser.spec.d.ts +3 -1
  319. package/dist/parser/Parser.spec.js +1533 -517
  320. package/dist/parser/Parser.spec.js.map +1 -1
  321. package/dist/parser/SGParser.d.ts +60 -7
  322. package/dist/parser/SGParser.js +225 -185
  323. package/dist/parser/SGParser.js.map +1 -1
  324. package/dist/parser/SGParser.spec.js +29 -32
  325. package/dist/parser/SGParser.spec.js.map +1 -1
  326. package/dist/parser/SGTypes.d.ts +295 -52
  327. package/dist/parser/SGTypes.js +540 -187
  328. package/dist/parser/SGTypes.js.map +1 -1
  329. package/dist/parser/Statement.d.ts +808 -261
  330. package/dist/parser/Statement.js +2232 -588
  331. package/dist/parser/Statement.js.map +1 -1
  332. package/dist/parser/Statement.spec.js +133 -36
  333. package/dist/parser/Statement.spec.js.map +1 -1
  334. package/dist/parser/TranspileState.d.ts +26 -12
  335. package/dist/parser/TranspileState.js +114 -15
  336. package/dist/parser/TranspileState.js.map +1 -1
  337. package/dist/parser/tests/Parser.spec.d.ts +3 -9
  338. package/dist/parser/tests/Parser.spec.js +7 -13
  339. package/dist/parser/tests/Parser.spec.js.map +1 -1
  340. package/dist/parser/tests/controlFlow/For.spec.js +83 -75
  341. package/dist/parser/tests/controlFlow/For.spec.js.map +1 -1
  342. package/dist/parser/tests/controlFlow/ForEach.spec.js +58 -51
  343. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  344. package/dist/parser/tests/controlFlow/If.spec.js +382 -239
  345. package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
  346. package/dist/parser/tests/controlFlow/While.spec.js +52 -45
  347. package/dist/parser/tests/controlFlow/While.spec.js.map +1 -1
  348. package/dist/parser/tests/expression/Additive.spec.js +51 -43
  349. package/dist/parser/tests/expression/Additive.spec.js.map +1 -1
  350. package/dist/parser/tests/expression/ArrayLiterals.spec.js +192 -142
  351. package/dist/parser/tests/expression/ArrayLiterals.spec.js.map +1 -1
  352. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +236 -160
  353. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
  354. package/dist/parser/tests/expression/Boolean.spec.js +41 -34
  355. package/dist/parser/tests/expression/Boolean.spec.js.map +1 -1
  356. package/dist/parser/tests/expression/Call.spec.js +173 -55
  357. package/dist/parser/tests/expression/Call.spec.js.map +1 -1
  358. package/dist/parser/tests/expression/Exponential.spec.js +20 -20
  359. package/dist/parser/tests/expression/Exponential.spec.js.map +1 -1
  360. package/dist/parser/tests/expression/Function.spec.js +291 -282
  361. package/dist/parser/tests/expression/Function.spec.js.map +1 -1
  362. package/dist/parser/tests/expression/Indexing.spec.js +193 -110
  363. package/dist/parser/tests/expression/Indexing.spec.js.map +1 -1
  364. package/dist/parser/tests/expression/Multiplicative.spec.js +42 -42
  365. package/dist/parser/tests/expression/Multiplicative.spec.js.map +1 -1
  366. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +213 -115
  367. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  368. package/dist/parser/tests/expression/PrefixUnary.spec.js +58 -52
  369. package/dist/parser/tests/expression/PrefixUnary.spec.js.map +1 -1
  370. package/dist/parser/tests/expression/Primary.spec.js +76 -60
  371. package/dist/parser/tests/expression/Primary.spec.js.map +1 -1
  372. package/dist/parser/tests/expression/RegexLiteralExpression.spec.d.ts +1 -0
  373. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +171 -0
  374. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -0
  375. package/dist/parser/tests/expression/Relational.spec.js +50 -50
  376. package/dist/parser/tests/expression/Relational.spec.js.map +1 -1
  377. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js +31 -31
  378. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js.map +1 -1
  379. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +235 -94
  380. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  381. package/dist/parser/tests/expression/TernaryExpression.spec.js +403 -174
  382. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  383. package/dist/parser/tests/expression/TypeExpression.spec.d.ts +1 -0
  384. package/dist/parser/tests/expression/TypeExpression.spec.js +126 -0
  385. package/dist/parser/tests/expression/TypeExpression.spec.js.map +1 -0
  386. package/dist/parser/tests/expression/UnaryExpression.spec.d.ts +1 -0
  387. package/dist/parser/tests/expression/UnaryExpression.spec.js +52 -0
  388. package/dist/parser/tests/expression/UnaryExpression.spec.js.map +1 -0
  389. package/dist/parser/tests/statement/AssignmentOperators.spec.js +44 -44
  390. package/dist/parser/tests/statement/AssignmentOperators.spec.js.map +1 -1
  391. package/dist/parser/tests/statement/ConstStatement.spec.d.ts +1 -0
  392. package/dist/parser/tests/statement/ConstStatement.spec.js +262 -0
  393. package/dist/parser/tests/statement/ConstStatement.spec.js.map +1 -0
  394. package/dist/parser/tests/statement/Continue.spec.d.ts +1 -0
  395. package/dist/parser/tests/statement/Continue.spec.js +119 -0
  396. package/dist/parser/tests/statement/Continue.spec.js.map +1 -0
  397. package/dist/parser/tests/statement/Declaration.spec.js +61 -55
  398. package/dist/parser/tests/statement/Declaration.spec.js.map +1 -1
  399. package/dist/parser/tests/statement/Dim.spec.js +22 -22
  400. package/dist/parser/tests/statement/Dim.spec.js.map +1 -1
  401. package/dist/parser/tests/statement/Enum.spec.d.ts +1 -0
  402. package/dist/parser/tests/statement/Enum.spec.js +744 -0
  403. package/dist/parser/tests/statement/Enum.spec.js.map +1 -0
  404. package/dist/parser/tests/statement/For.spec.d.ts +1 -0
  405. package/dist/parser/tests/statement/For.spec.js +45 -0
  406. package/dist/parser/tests/statement/For.spec.js.map +1 -0
  407. package/dist/parser/tests/statement/ForEach.spec.d.ts +1 -0
  408. package/dist/parser/tests/statement/ForEach.spec.js +36 -0
  409. package/dist/parser/tests/statement/ForEach.spec.js.map +1 -0
  410. package/dist/parser/tests/statement/Function.spec.js +226 -215
  411. package/dist/parser/tests/statement/Function.spec.js.map +1 -1
  412. package/dist/parser/tests/statement/Goto.spec.js +16 -15
  413. package/dist/parser/tests/statement/Goto.spec.js.map +1 -1
  414. package/dist/parser/tests/statement/Increment.spec.js +64 -59
  415. package/dist/parser/tests/statement/Increment.spec.js.map +1 -1
  416. package/dist/parser/tests/statement/InterfaceStatement.spec.d.ts +1 -0
  417. package/dist/parser/tests/statement/InterfaceStatement.spec.js +110 -0
  418. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -0
  419. package/dist/parser/tests/statement/LibraryStatement.spec.js +22 -22
  420. package/dist/parser/tests/statement/LibraryStatement.spec.js.map +1 -1
  421. package/dist/parser/tests/statement/Misc.spec.js +127 -168
  422. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  423. package/dist/parser/tests/statement/PrintStatement.spec.js +133 -114
  424. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  425. package/dist/parser/tests/statement/ReturnStatement.spec.js +57 -54
  426. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +1 -1
  427. package/dist/parser/tests/statement/Set.spec.js +131 -117
  428. package/dist/parser/tests/statement/Set.spec.js.map +1 -1
  429. package/dist/parser/tests/statement/Stop.spec.js +14 -13
  430. package/dist/parser/tests/statement/Stop.spec.js.map +1 -1
  431. package/dist/parser/tests/statement/Throw.spec.js +11 -8
  432. package/dist/parser/tests/statement/Throw.spec.js.map +1 -1
  433. package/dist/parser/tests/statement/TryCatch.spec.js +26 -15
  434. package/dist/parser/tests/statement/TryCatch.spec.js.map +1 -1
  435. package/dist/preprocessor/Manifest.d.ts +6 -6
  436. package/dist/preprocessor/Manifest.js +17 -38
  437. package/dist/preprocessor/Manifest.js.map +1 -1
  438. package/dist/preprocessor/Manifest.spec.d.ts +1 -0
  439. package/dist/preprocessor/Manifest.spec.js +78 -103
  440. package/dist/preprocessor/Manifest.spec.js.map +1 -1
  441. package/dist/roku-types/data.json +19452 -0
  442. package/dist/roku-types/index.d.ts +5533 -0
  443. package/dist/roku-types/index.js +11 -0
  444. package/dist/roku-types/index.js.map +1 -0
  445. package/dist/types/ArrayType.d.ts +9 -5
  446. package/dist/types/ArrayType.js +73 -24
  447. package/dist/types/ArrayType.js.map +1 -1
  448. package/dist/types/ArrayType.spec.js +39 -11
  449. package/dist/types/ArrayType.spec.js.map +1 -1
  450. package/dist/types/AssociativeArrayType.d.ts +14 -0
  451. package/dist/types/AssociativeArrayType.js +60 -0
  452. package/dist/types/AssociativeArrayType.js.map +1 -0
  453. package/dist/types/BaseFunctionType.d.ts +9 -0
  454. package/dist/types/BaseFunctionType.js +25 -0
  455. package/dist/types/BaseFunctionType.js.map +1 -0
  456. package/dist/types/BooleanType.d.ts +10 -5
  457. package/dist/types/BooleanType.js +21 -9
  458. package/dist/types/BooleanType.js.map +1 -1
  459. package/dist/types/BooleanType.spec.js +10 -4
  460. package/dist/types/BooleanType.spec.js.map +1 -1
  461. package/dist/types/BscType.d.ts +29 -3
  462. package/dist/types/BscType.js +121 -0
  463. package/dist/types/BscType.js.map +1 -1
  464. package/dist/types/BscTypeKind.d.ts +25 -0
  465. package/dist/types/BscTypeKind.js +30 -0
  466. package/dist/types/BscTypeKind.js.map +1 -0
  467. package/dist/types/BuiltInInterfaceAdder.d.ts +25 -0
  468. package/dist/types/BuiltInInterfaceAdder.js +201 -0
  469. package/dist/types/BuiltInInterfaceAdder.js.map +1 -0
  470. package/dist/types/BuiltInInterfaceAdder.spec.d.ts +1 -0
  471. package/dist/types/BuiltInInterfaceAdder.spec.js +115 -0
  472. package/dist/types/BuiltInInterfaceAdder.spec.js.map +1 -0
  473. package/dist/types/ClassType.d.ts +16 -0
  474. package/dist/types/ClassType.js +57 -0
  475. package/dist/types/ClassType.js.map +1 -0
  476. package/dist/types/ClassType.spec.d.ts +1 -0
  477. package/dist/types/ClassType.spec.js +76 -0
  478. package/dist/types/ClassType.spec.js.map +1 -0
  479. package/dist/types/ComponentType.d.ts +27 -0
  480. package/dist/types/ComponentType.js +83 -0
  481. package/dist/types/ComponentType.js.map +1 -0
  482. package/dist/types/DoubleType.d.ts +10 -5
  483. package/dist/types/DoubleType.js +25 -18
  484. package/dist/types/DoubleType.js.map +1 -1
  485. package/dist/types/DoubleType.spec.js +12 -4
  486. package/dist/types/DoubleType.spec.js.map +1 -1
  487. package/dist/types/DynamicType.d.ts +12 -5
  488. package/dist/types/DynamicType.js +22 -6
  489. package/dist/types/DynamicType.js.map +1 -1
  490. package/dist/types/DynamicType.spec.js +16 -5
  491. package/dist/types/DynamicType.spec.js.map +1 -1
  492. package/dist/types/EnumType.d.ts +40 -0
  493. package/dist/types/EnumType.js +80 -0
  494. package/dist/types/EnumType.js.map +1 -0
  495. package/dist/types/EnumType.spec.d.ts +1 -0
  496. package/dist/types/EnumType.spec.js +33 -0
  497. package/dist/types/EnumType.spec.js.map +1 -0
  498. package/dist/types/FloatType.d.ts +10 -5
  499. package/dist/types/FloatType.js +25 -18
  500. package/dist/types/FloatType.js.map +1 -1
  501. package/dist/types/FloatType.spec.js +4 -4
  502. package/dist/types/FloatType.spec.js.map +1 -1
  503. package/dist/types/FunctionType.d.ts +10 -22
  504. package/dist/types/FunctionType.js +26 -63
  505. package/dist/types/FunctionType.js.map +1 -1
  506. package/dist/types/InheritableType.d.ts +28 -0
  507. package/dist/types/InheritableType.js +157 -0
  508. package/dist/types/InheritableType.js.map +1 -0
  509. package/dist/types/IntegerType.d.ts +10 -5
  510. package/dist/types/IntegerType.js +25 -18
  511. package/dist/types/IntegerType.js.map +1 -1
  512. package/dist/types/IntegerType.spec.js +8 -4
  513. package/dist/types/IntegerType.spec.js.map +1 -1
  514. package/dist/types/InterfaceType.d.ts +14 -6
  515. package/dist/types/InterfaceType.js +26 -15
  516. package/dist/types/InterfaceType.js.map +1 -1
  517. package/dist/types/InterfaceType.spec.d.ts +1 -0
  518. package/dist/types/InterfaceType.spec.js +227 -0
  519. package/dist/types/InterfaceType.spec.js.map +1 -0
  520. package/dist/types/InvalidType.d.ts +9 -5
  521. package/dist/types/InvalidType.js +20 -9
  522. package/dist/types/InvalidType.js.map +1 -1
  523. package/dist/types/InvalidType.spec.js +8 -4
  524. package/dist/types/InvalidType.spec.js.map +1 -1
  525. package/dist/types/LongIntegerType.d.ts +10 -5
  526. package/dist/types/LongIntegerType.js +25 -18
  527. package/dist/types/LongIntegerType.js.map +1 -1
  528. package/dist/types/LongIntegerType.spec.js +10 -4
  529. package/dist/types/LongIntegerType.spec.js.map +1 -1
  530. package/dist/types/NamespaceType.d.ts +12 -0
  531. package/dist/types/NamespaceType.js +28 -0
  532. package/dist/types/NamespaceType.js.map +1 -0
  533. package/dist/types/ObjectType.d.ts +10 -5
  534. package/dist/types/ObjectType.js +23 -9
  535. package/dist/types/ObjectType.js.map +1 -1
  536. package/dist/types/ObjectType.spec.js +3 -3
  537. package/dist/types/ObjectType.spec.js.map +1 -1
  538. package/dist/types/ReferenceType.d.ts +79 -0
  539. package/dist/types/ReferenceType.js +522 -0
  540. package/dist/types/ReferenceType.js.map +1 -0
  541. package/dist/types/ReferenceType.spec.d.ts +1 -0
  542. package/dist/types/ReferenceType.spec.js +151 -0
  543. package/dist/types/ReferenceType.spec.js.map +1 -0
  544. package/dist/types/StringType.d.ts +13 -5
  545. package/dist/types/StringType.js +25 -9
  546. package/dist/types/StringType.js.map +1 -1
  547. package/dist/types/StringType.spec.js +3 -3
  548. package/dist/types/StringType.spec.js.map +1 -1
  549. package/dist/types/TypedFunctionType.d.ts +33 -0
  550. package/dist/types/TypedFunctionType.js +106 -0
  551. package/dist/types/TypedFunctionType.js.map +1 -0
  552. package/dist/types/TypedFunctionType.spec.d.ts +1 -0
  553. package/dist/types/TypedFunctionType.spec.js +122 -0
  554. package/dist/types/TypedFunctionType.spec.js.map +1 -0
  555. package/dist/types/UninitializedType.d.ts +8 -6
  556. package/dist/types/UninitializedType.js +15 -9
  557. package/dist/types/UninitializedType.js.map +1 -1
  558. package/dist/types/UnionType.d.ts +20 -0
  559. package/dist/types/UnionType.js +127 -0
  560. package/dist/types/UnionType.js.map +1 -0
  561. package/dist/types/UnionType.spec.d.ts +1 -0
  562. package/dist/types/UnionType.spec.js +129 -0
  563. package/dist/types/UnionType.spec.js.map +1 -0
  564. package/dist/types/VoidType.d.ts +10 -5
  565. package/dist/types/VoidType.js +20 -9
  566. package/dist/types/VoidType.js.map +1 -1
  567. package/dist/types/VoidType.spec.js +3 -3
  568. package/dist/types/VoidType.spec.js.map +1 -1
  569. package/dist/types/helper.spec.d.ts +1 -0
  570. package/dist/types/helper.spec.js +144 -0
  571. package/dist/types/helper.spec.js.map +1 -0
  572. package/dist/types/helpers.d.ts +26 -0
  573. package/dist/types/helpers.js +191 -0
  574. package/dist/types/helpers.js.map +1 -0
  575. package/dist/types/index.d.ts +22 -0
  576. package/dist/types/index.js +39 -0
  577. package/dist/types/index.js.map +1 -0
  578. package/dist/util.d.ts +272 -117
  579. package/dist/util.js +1583 -343
  580. package/dist/util.js.map +1 -1
  581. package/dist/validators/ClassValidator.d.ts +9 -15
  582. package/dist/validators/ClassValidator.js +85 -138
  583. package/dist/validators/ClassValidator.js.map +1 -1
  584. package/package.json +174 -138
  585. package/dist/astUtils/index.d.ts +0 -7
  586. package/dist/astUtils/index.js +0 -26
  587. package/dist/astUtils/index.js.map +0 -1
  588. package/dist/lexer/index.d.ts +0 -3
  589. package/dist/lexer/index.js +0 -17
  590. package/dist/lexer/index.js.map +0 -1
  591. package/dist/parser/index.d.ts +0 -3
  592. package/dist/parser/index.js +0 -16
  593. package/dist/parser/index.js.map +0 -1
  594. package/dist/preprocessor/Chunk.d.ts +0 -82
  595. package/dist/preprocessor/Chunk.js +0 -77
  596. package/dist/preprocessor/Chunk.js.map +0 -1
  597. package/dist/preprocessor/Preprocessor.d.ts +0 -60
  598. package/dist/preprocessor/Preprocessor.js +0 -156
  599. package/dist/preprocessor/Preprocessor.js.map +0 -1
  600. package/dist/preprocessor/Preprocessor.spec.js +0 -152
  601. package/dist/preprocessor/Preprocessor.spec.js.map +0 -1
  602. package/dist/preprocessor/PreprocessorParser.d.ts +0 -61
  603. package/dist/preprocessor/PreprocessorParser.js +0 -194
  604. package/dist/preprocessor/PreprocessorParser.js.map +0 -1
  605. package/dist/preprocessor/PreprocessorParser.spec.js +0 -116
  606. package/dist/preprocessor/PreprocessorParser.spec.js.map +0 -1
  607. package/dist/preprocessor/index.d.ts +0 -3
  608. package/dist/preprocessor/index.js +0 -16
  609. package/dist/preprocessor/index.js.map +0 -1
  610. package/dist/types/CustomType.d.ts +0 -10
  611. package/dist/types/CustomType.js +0 -35
  612. package/dist/types/CustomType.js.map +0 -1
  613. package/dist/types/FunctionType.spec.js +0 -29
  614. package/dist/types/FunctionType.spec.js.map +0 -1
  615. package/dist/types/LazyType.d.ts +0 -15
  616. package/dist/types/LazyType.js +0 -32
  617. package/dist/types/LazyType.js.map +0 -1
  618. /package/dist/{preprocessor/Preprocessor.spec.d.ts → astUtils/CachedLookups.spec.d.ts} +0 -0
  619. /package/dist/{preprocessor/PreprocessorParser.spec.d.ts → astUtils/Editor.spec.d.ts} +0 -0
  620. /package/dist/{types/FunctionType.spec.d.ts → bscPlugin/completions/CompletionsProcessor.spec.d.ts} +0 -0
package/dist/Program.js CHANGED
@@ -4,35 +4,58 @@ exports.Program = void 0;
4
4
  const assert = require("assert");
5
5
  const fsExtra = require("fs-extra");
6
6
  const path = require("path");
7
- const vscode_languageserver_1 = require("vscode-languageserver");
8
7
  const Scope_1 = require("./Scope");
9
8
  const DiagnosticMessages_1 = require("./DiagnosticMessages");
10
- const BrsFile_1 = require("./files/BrsFile");
11
- const XmlFile_1 = require("./files/XmlFile");
12
9
  const util_1 = require("./util");
13
10
  const XmlScope_1 = require("./XmlScope");
14
- const DiagnosticFilterer_1 = require("./DiagnosticFilterer");
15
11
  const DependencyGraph_1 = require("./DependencyGraph");
16
- const Logger_1 = require("./Logger");
12
+ const logging_1 = require("./logging");
17
13
  const chalk_1 = require("chalk");
18
14
  const globalCallables_1 = require("./globalCallables");
19
15
  const Manifest_1 = require("./preprocessor/Manifest");
20
16
  const vscode_uri_1 = require("vscode-uri");
21
17
  const PluginInterface_1 = require("./PluginInterface");
22
18
  const reflection_1 = require("./astUtils/reflection");
23
- const parser_1 = require("./parser");
24
- const lexer_1 = require("./lexer");
25
19
  const BscPlugin_1 = require("./bscPlugin/BscPlugin");
20
+ const Editor_1 = require("./astUtils/Editor");
21
+ const CallExpressionInfo_1 = require("./bscPlugin/CallExpressionInfo");
22
+ const SignatureHelpUtil_1 = require("./bscPlugin/SignatureHelpUtil");
23
+ const IntegerType_1 = require("./types/IntegerType");
24
+ const StringType_1 = require("./types/StringType");
25
+ const BooleanType_1 = require("./types/BooleanType");
26
+ const DoubleType_1 = require("./types/DoubleType");
27
+ const DynamicType_1 = require("./types/DynamicType");
28
+ const FloatType_1 = require("./types/FloatType");
29
+ const LongIntegerType_1 = require("./types/LongIntegerType");
30
+ const ObjectType_1 = require("./types/ObjectType");
31
+ const VoidType_1 = require("./types/VoidType");
32
+ const FunctionType_1 = require("./types/FunctionType");
33
+ const Factory_1 = require("./files/Factory");
34
+ const ActionPipeline_1 = require("./ActionPipeline");
35
+ const LazyFileData_1 = require("./files/LazyFileData");
26
36
  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`;
37
+ const roku_types_1 = require("./roku-types");
38
+ const ComponentType_1 = require("./types/ComponentType");
39
+ const InterfaceType_1 = require("./types/InterfaceType");
40
+ const BuiltInInterfaceAdder_1 = require("./types/BuiltInInterfaceAdder");
41
+ const visitors_1 = require("./astUtils/visitors");
42
+ const Stopwatch_1 = require("./Stopwatch");
43
+ const thenby_1 = require("thenby");
44
+ const CrossScopeValidator_1 = require("./CrossScopeValidator");
45
+ const DiagnosticManager_1 = require("./DiagnosticManager");
46
+ const ProgramValidator_1 = require("./bscPlugin/validation/ProgramValidator");
47
+ const bslibNonAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/rokucommunity_bslib/bslib.brs`;
48
+ const bslibAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/bslib/bslib.brs`;
29
49
  class Program {
30
50
  constructor(
31
51
  /**
32
52
  * The root directory for this program
33
53
  */
34
- options, logger, plugins) {
35
- this.options = options;
54
+ options, logger, plugins, diagnosticsManager) {
55
+ /**
56
+ * 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`)
57
+ */
58
+ this.editor = new Editor_1.Editor();
36
59
  /**
37
60
  * A graph of all files and their dependencies.
38
61
  * For example:
@@ -40,20 +63,26 @@ class Program {
40
63
  * lib2.brs -> [lib3.brs] //via an import statement
41
64
  */
42
65
  this.dependencyGraph = new DependencyGraph_1.DependencyGraph();
43
- this.diagnosticFilterer = new DiagnosticFilterer_1.DiagnosticFilterer();
44
66
  /**
45
- * A set of diagnostics. This does not include any of the scope diagnostics.
46
- * Should only be set from `this.validate()`
67
+ * A scope that contains all built-in global functions.
68
+ * All scopes should directly or indirectly inherit from this scope
47
69
  */
48
- this.diagnostics = [];
70
+ this.globalScope = undefined;
71
+ this.fileSymbolInformation = new Map();
49
72
  /**
50
- * A map of every file loaded ino this program, indexed by its lower-case pkgPath
73
+ * A map of every file loaded into this program, indexed by its original file location
51
74
  */
52
- this.pkgMap = {};
75
+ this.files = {};
53
76
  /**
54
- * A map of every file loaded into this program, indexed by its lower-case srcPath
77
+ * A map of every file loaded into this program, indexed by its destPath
55
78
  */
56
- this.files = {};
79
+ this.destMap = new Map();
80
+ /**
81
+ * Plugins can contribute multiple virtual files for a single physical file.
82
+ * This collection links the virtual files back to the physical file that produced them.
83
+ * The key is the standardized and lower-cased srcPath
84
+ */
85
+ this.fileClusters = new Map();
57
86
  this.scopes = {};
58
87
  /**
59
88
  * A map of every component currently loaded into the program, indexed by the component name.
@@ -62,28 +91,123 @@ class Program {
62
91
  * but if you do, only ever use the component at index 0.
63
92
  */
64
93
  this.components = {};
94
+ /**
95
+ * Keeps a set of all the components that need to have their types updated during the current validation cycle
96
+ */
97
+ this.componentSymbolsToUpdate = new Set();
98
+ this.crossScopeValidation = new CrossScopeValidator_1.CrossScopeValidator(this);
99
+ this.isFirstValidation = true;
100
+ this.sortedScopeNames = undefined;
101
+ this.getTranspiledFileContentsPipeline = new ActionPipeline_1.ActionPipeline();
102
+ this.buildPipeline = new ActionPipeline_1.ActionPipeline();
65
103
  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);
104
+ this.logger = logger !== null && logger !== void 0 ? logger : (0, logging_1.createLogger)(options);
105
+ this.plugins = plugins || new PluginInterface_1.default([], { logger: this.logger });
106
+ this.diagnostics = diagnosticsManager || new DiagnosticManager_1.DiagnosticManager();
107
+ // initialize the diagnostics Manager
108
+ this.diagnostics.logger = this.logger;
109
+ this.diagnostics.options = this.options;
110
+ this.diagnostics.program = this;
68
111
  //inject the bsc plugin as the first plugin in the stack.
69
112
  this.plugins.addFirst(new BscPlugin_1.BscPlugin());
70
113
  //normalize the root dir path
71
114
  this.options.rootDir = util_1.util.getRootDir(this.options);
72
115
  this.createGlobalScope();
116
+ this.fileFactory = new Factory_1.FileFactory(this);
73
117
  }
74
118
  createGlobalScope() {
75
119
  //create the 'global' scope
76
120
  this.globalScope = new Scope_1.Scope('global', this, 'scope:global');
77
121
  this.globalScope.attachDependencyGraph(this.dependencyGraph);
78
122
  this.scopes.global = this.globalScope;
123
+ this.populateGlobalSymbolTable();
79
124
  //hardcode the files list for global scope to only contain the global file
80
125
  this.globalScope.getAllFiles = () => [globalCallables_1.globalFile];
126
+ globalCallables_1.globalFile.isValidated = true;
81
127
  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
128
  //TODO we might need to fix this because the isValidated clears stuff now
85
129
  this.globalScope.isValidated = true;
86
130
  }
131
+ recursivelyAddNodeToSymbolTable(nodeData) {
132
+ if (!nodeData) {
133
+ return;
134
+ }
135
+ let nodeType;
136
+ const nodeName = util_1.util.getSgNodeTypeName(nodeData.name);
137
+ if (!this.globalScope.symbolTable.hasSymbol(nodeName, 2 /* SymbolTypeFlag.typetime */)) {
138
+ let parentNode;
139
+ if (nodeData.extends) {
140
+ const parentNodeData = roku_types_1.nodes[nodeData.extends.name.toLowerCase()];
141
+ try {
142
+ parentNode = this.recursivelyAddNodeToSymbolTable(parentNodeData);
143
+ }
144
+ catch (error) {
145
+ this.logger.error(error, nodeData);
146
+ }
147
+ }
148
+ nodeType = new ComponentType_1.ComponentType(nodeData.name, parentNode);
149
+ nodeType.addBuiltInInterfaces();
150
+ if (nodeData.name === 'Node') {
151
+ // Add `roSGNode` as shorthand for `roSGNodeNode`
152
+ this.globalScope.symbolTable.addSymbol('roSGNode', { description: nodeData.description }, nodeType, 2 /* SymbolTypeFlag.typetime */);
153
+ }
154
+ this.globalScope.symbolTable.addSymbol(nodeName, { description: nodeData.description }, nodeType, 2 /* SymbolTypeFlag.typetime */);
155
+ }
156
+ else {
157
+ nodeType = this.globalScope.symbolTable.getSymbolType(nodeName, { flags: 2 /* SymbolTypeFlag.typetime */ });
158
+ }
159
+ return nodeType;
160
+ }
161
+ /**
162
+ * Do all setup required for the global symbol table.
163
+ */
164
+ populateGlobalSymbolTable() {
165
+ //Setup primitive types in global symbolTable
166
+ this.globalScope.symbolTable.addSymbol('boolean', undefined, BooleanType_1.BooleanType.instance, 2 /* SymbolTypeFlag.typetime */);
167
+ this.globalScope.symbolTable.addSymbol('double', undefined, DoubleType_1.DoubleType.instance, 2 /* SymbolTypeFlag.typetime */);
168
+ this.globalScope.symbolTable.addSymbol('dynamic', undefined, DynamicType_1.DynamicType.instance, 2 /* SymbolTypeFlag.typetime */);
169
+ this.globalScope.symbolTable.addSymbol('float', undefined, FloatType_1.FloatType.instance, 2 /* SymbolTypeFlag.typetime */);
170
+ this.globalScope.symbolTable.addSymbol('function', undefined, new FunctionType_1.FunctionType(), 2 /* SymbolTypeFlag.typetime */);
171
+ this.globalScope.symbolTable.addSymbol('integer', undefined, IntegerType_1.IntegerType.instance, 2 /* SymbolTypeFlag.typetime */);
172
+ this.globalScope.symbolTable.addSymbol('longinteger', undefined, LongIntegerType_1.LongIntegerType.instance, 2 /* SymbolTypeFlag.typetime */);
173
+ this.globalScope.symbolTable.addSymbol('object', undefined, new ObjectType_1.ObjectType(), 2 /* SymbolTypeFlag.typetime */);
174
+ this.globalScope.symbolTable.addSymbol('string', undefined, StringType_1.StringType.instance, 2 /* SymbolTypeFlag.typetime */);
175
+ this.globalScope.symbolTable.addSymbol('void', undefined, VoidType_1.VoidType.instance, 2 /* SymbolTypeFlag.typetime */);
176
+ BuiltInInterfaceAdder_1.BuiltInInterfaceAdder.getLookupTable = () => this.globalScope.symbolTable;
177
+ for (const callable of globalCallables_1.globalCallables) {
178
+ this.globalScope.symbolTable.addSymbol(callable.name, { description: callable.shortDescription }, callable.type, 1 /* SymbolTypeFlag.runtime */);
179
+ }
180
+ for (const ifaceData of Object.values(roku_types_1.interfaces)) {
181
+ const nodeType = new InterfaceType_1.InterfaceType(ifaceData.name);
182
+ nodeType.addBuiltInInterfaces();
183
+ this.globalScope.symbolTable.addSymbol(ifaceData.name, { description: ifaceData.description }, nodeType, 2 /* SymbolTypeFlag.typetime */);
184
+ }
185
+ for (const componentData of Object.values(roku_types_1.components)) {
186
+ const nodeType = new InterfaceType_1.InterfaceType(componentData.name);
187
+ nodeType.addBuiltInInterfaces();
188
+ if (componentData.name !== 'roSGNode') {
189
+ // we will add `roSGNode` as shorthand for `roSGNodeNode`, since all roSgNode components are SceneGraph nodes
190
+ this.globalScope.symbolTable.addSymbol(componentData.name, { description: componentData.description }, nodeType, 2 /* SymbolTypeFlag.typetime */);
191
+ }
192
+ }
193
+ for (const nodeData of Object.values(roku_types_1.nodes)) {
194
+ this.recursivelyAddNodeToSymbolTable(nodeData);
195
+ }
196
+ for (const eventData of Object.values(roku_types_1.events)) {
197
+ const nodeType = new InterfaceType_1.InterfaceType(eventData.name);
198
+ nodeType.addBuiltInInterfaces();
199
+ this.globalScope.symbolTable.addSymbol(eventData.name, { description: eventData.description }, nodeType, 2 /* SymbolTypeFlag.typetime */);
200
+ }
201
+ }
202
+ addFileSymbolInfo(file) {
203
+ this.fileSymbolInformation.set(file.pkgPath, {
204
+ provides: file.providedSymbols,
205
+ requires: file.requiredSymbols
206
+ });
207
+ }
208
+ getFileSymbolInfo(file) {
209
+ return this.fileSymbolInformation.get(file.pkgPath);
210
+ }
87
211
  /**
88
212
  * The path to bslib.brs (the BrightScript runtime for certain BrighterScript features)
89
213
  */
@@ -98,7 +222,7 @@ class Program {
98
222
  //default to the embedded version
99
223
  }
100
224
  else {
101
- return `pkg:/source/bslib.brs`;
225
+ return `${this.options.bslibDestinationDir}${path.sep}bslib.brs`;
102
226
  }
103
227
  }
104
228
  get bslibPrefix() {
@@ -109,18 +233,15 @@ class Program {
109
233
  return 'bslib';
110
234
  }
111
235
  }
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
236
  addScope(scope) {
119
237
  this.scopes[scope.name] = scope;
120
- this.plugins.emit('afterScopeCreate', {
121
- program: this,
122
- scope: scope
123
- });
238
+ delete this.sortedScopeNames;
239
+ }
240
+ removeScope(scope) {
241
+ if (this.scopes[scope.name]) {
242
+ delete this.scopes[scope.name];
243
+ delete this.sortedScopeNames;
244
+ }
124
245
  }
125
246
  /**
126
247
  * Get the component with the specified name
@@ -129,19 +250,34 @@ class Program {
129
250
  var _a;
130
251
  if (componentName) {
131
252
  //return the first compoment in the list with this name
132
- //(components are ordered in this list by pkgPath to ensure consistency)
253
+ //(components are ordered in this list by destPath to ensure consistency)
133
254
  return (_a = this.components[componentName.toLowerCase()]) === null || _a === void 0 ? void 0 : _a[0];
134
255
  }
135
256
  else {
136
257
  return undefined;
137
258
  }
138
259
  }
260
+ /**
261
+ * Get the sorted names of custom components
262
+ */
263
+ getSortedComponentNames() {
264
+ const componentNames = Object.keys(this.components);
265
+ componentNames.sort((a, b) => {
266
+ if (a < b) {
267
+ return -1;
268
+ }
269
+ else if (b < a) {
270
+ return 1;
271
+ }
272
+ return 0;
273
+ });
274
+ return componentNames;
275
+ }
139
276
  /**
140
277
  * Register (or replace) the reference to a component in the component map
141
278
  */
142
279
  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();
280
+ const key = this.getComponentKey(xmlFile);
145
281
  if (!this.components[key]) {
146
282
  this.components[key] = [];
147
283
  }
@@ -149,15 +285,25 @@ class Program {
149
285
  file: xmlFile,
150
286
  scope: scope
151
287
  });
152
- this.components[key].sort((x, y) => x.file.pkgPath.toLowerCase().localeCompare(y.file.pkgPath.toLowerCase()));
288
+ this.components[key].sort((a, b) => {
289
+ const pathA = a.file.destPath.toLowerCase();
290
+ const pathB = b.file.destPath.toLowerCase();
291
+ if (pathA < pathB) {
292
+ return -1;
293
+ }
294
+ else if (pathA > pathB) {
295
+ return 1;
296
+ }
297
+ return 0;
298
+ });
153
299
  this.syncComponentDependencyGraph(this.components[key]);
300
+ this.addDeferredComponentTypeSymbolCreation(xmlFile);
154
301
  }
155
302
  /**
156
303
  * Remove the specified component from the components map
157
304
  */
158
305
  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();
306
+ const key = this.getComponentKey(xmlFile);
161
307
  const arr = this.components[key] || [];
162
308
  for (let i = 0; i < arr.length; i++) {
163
309
  if (arr[i].file === xmlFile) {
@@ -166,6 +312,44 @@ class Program {
166
312
  }
167
313
  }
168
314
  this.syncComponentDependencyGraph(arr);
315
+ this.addDeferredComponentTypeSymbolCreation(xmlFile);
316
+ }
317
+ /**
318
+ * Adds a component described in an XML to the set of components that needs to be updated this validation cycle.
319
+ * @param xmlFile XML file with <component> tag
320
+ */
321
+ addDeferredComponentTypeSymbolCreation(xmlFile) {
322
+ var _a;
323
+ this.componentSymbolsToUpdate.add({ componentKey: this.getComponentKey(xmlFile), componentName: (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text });
324
+ }
325
+ getComponentKey(xmlFile) {
326
+ var _a, _b;
327
+ return ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
328
+ }
329
+ /**
330
+ * Updates the global symbol table with the first component in this.components to have the same name as the component in the file
331
+ * @param componentKey key getting a component from `this.components`
332
+ * @param componentName the unprefixed name of the component that will be added (e.g. 'MyLabel' NOT 'roSgNodeMyLabel')
333
+ */
334
+ updateComponentSymbolInGlobalScope(componentKey, componentName) {
335
+ const symbolName = componentName ? util_1.util.getSgNodeTypeName(componentName) : undefined;
336
+ if (!symbolName) {
337
+ return;
338
+ }
339
+ const components = this.components[componentKey] || [];
340
+ // Remove any existing symbols that match
341
+ this.globalScope.symbolTable.removeSymbol(symbolName);
342
+ // There is a component that can be added - use it.
343
+ if (components.length > 0) {
344
+ const componentScope = components[0].scope;
345
+ // TODO: May need to link symbol tables to get correct types for callfuncs
346
+ // componentScope.linkSymbolTable();
347
+ const componentType = componentScope.getComponentType();
348
+ if (componentType) {
349
+ this.globalScope.symbolTable.addSymbol(symbolName, {}, componentType, 2 /* SymbolTypeFlag.typetime */);
350
+ }
351
+ // TODO: Remember to unlink! componentScope.unlinkSymbolTable();
352
+ }
169
353
  }
170
354
  /**
171
355
  * re-attach the dependency graph with a new key for any component who changed
@@ -179,6 +363,7 @@ class Program {
179
363
  //attach (or re-attach) the dependencyGraph for every component whose position changed
180
364
  if (file.dependencyGraphIndex !== i) {
181
365
  file.dependencyGraphIndex = i;
366
+ this.dependencyGraph.addOrReplace(file.dependencyGraphKey, file.dependencies);
182
367
  file.attachDependencyGraph(this.dependencyGraph);
183
368
  scope.attachDependencyGraph(this.dependencyGraph);
184
369
  }
@@ -190,9 +375,10 @@ class Program {
190
375
  */
191
376
  getUnreferencedFiles() {
192
377
  let result = [];
193
- for (let key in this.files) {
194
- const file = this.files[key];
195
- if (!this.fileIsIncludedInAnyScope(file)) {
378
+ for (let filePath in this.files) {
379
+ let file = this.files[filePath];
380
+ //is this file part of a scope
381
+ if (!this.getFirstScopeForFile(file)) {
196
382
  //no scopes reference this file. add it to the list
197
383
  result.push(file);
198
384
  }
@@ -200,37 +386,14 @@ class Program {
200
386
  return result;
201
387
  }
202
388
  /**
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.
389
+ * Get the list of errors for the entire program.
205
390
  */
206
391
  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);
392
+ return this.diagnostics.getDiagnostics();
230
393
  }
231
394
  /**
232
395
  * Determine if the specified file is loaded in this program right now.
233
- * @param filePath
396
+ * @param filePath the absolute or relative path to the file
234
397
  * @param normalizePath should the provided path be normalized before use
235
398
  */
236
399
  hasFile(filePath, normalizePath = true) {
@@ -238,12 +401,15 @@ class Program {
238
401
  }
239
402
  /**
240
403
  * roku filesystem is case INsensitive, so find the scope by key case insensitive
241
- * @param scopeName
404
+ * @param scopeName xml scope names are their `destPath`. Source scope is stored with the key `"source"`
242
405
  */
243
406
  getScopeByName(scopeName) {
244
407
  if (!scopeName) {
245
408
  return undefined;
246
409
  }
410
+ //most scopes are xml file pkg paths. however, the ones that are not are single names like "global" and "scope",
411
+ //so it's safe to run the standardizePkgPath method
412
+ scopeName = (0, util_1.standardizePath) `${scopeName}`;
247
413
  let key = Object.keys(this.scopes).find(x => x.toLowerCase() === scopeName.toLowerCase());
248
414
  return this.scopes[key];
249
415
  }
@@ -264,113 +430,170 @@ class Program {
264
430
  * Update internal maps with this file reference
265
431
  */
266
432
  assignFile(file) {
433
+ const fileAddEvent = {
434
+ file: file,
435
+ program: this
436
+ };
437
+ this.plugins.emit('beforeFileAdd', fileAddEvent);
267
438
  this.files[file.srcPath.toLowerCase()] = file;
268
- this.pkgMap[file.pkgPath.toLowerCase()] = file;
439
+ this.destMap.set(file.destPath.toLowerCase(), file);
440
+ this.plugins.emit('afterFileAdd', fileAddEvent);
441
+ return file;
269
442
  }
270
443
  /**
271
444
  * Remove this file from internal maps
272
445
  */
273
446
  unassignFile(file) {
274
447
  delete this.files[file.srcPath.toLowerCase()];
275
- delete this.pkgMap[file.pkgPath.toLowerCase()];
448
+ this.destMap.delete(file.destPath.toLowerCase());
449
+ return file;
276
450
  }
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)
451
+ setFile(fileParam, fileData) {
452
+ //normalize the file paths
453
+ const { srcPath, destPath } = this.getPaths(fileParam, this.options.rootDir);
454
+ let file = this.logger.time(logging_1.LogLevel.debug, ['Program.setFile()', chalk_1.default.green(srcPath)], () => {
455
+ var _a, _b, _c;
456
+ //if the file is already loaded, remove it
457
+ if (this.hasFile(srcPath)) {
458
+ this.removeFile(srcPath, true, true);
288
459
  }
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:/`)
460
+ const data = new LazyFileData_1.LazyFileData(fileData);
461
+ const event = new ProvideFileEventInternal(this, srcPath, destPath, data, this.fileFactory);
462
+ this.plugins.emit('beforeProvideFile', event);
463
+ this.plugins.emit('provideFile', event);
464
+ this.plugins.emit('afterProvideFile', event);
465
+ //if no files were provided, create a AssetFile to represent it.
466
+ if (event.files.length === 0) {
467
+ event.files.push(this.fileFactory.AssetFile({
468
+ srcPath: event.srcPath,
469
+ destPath: event.destPath,
470
+ pkgPath: event.destPath,
471
+ data: data
472
+ }));
294
473
  }
295
- else {
296
- srcPath = util_1.standardizePath `${this.options.rootDir}/${fileParam}`;
297
- pkgPath = util_1.util.sanitizePkgPath(fileParam);
474
+ //find the file instance for the srcPath that triggered this action.
475
+ const primaryFile = event.files.find(x => x.srcPath === srcPath);
476
+ if (!primaryFile) {
477
+ throw new Error(`No file provided for srcPath '${srcPath}'. Instead, received ${JSON.stringify(event.files.map(x => ({
478
+ type: x.type,
479
+ srcPath: x.srcPath,
480
+ destPath: x.destPath
481
+ })))}`);
298
482
  }
299
- //is a FileObj
483
+ //link the virtual files to the primary file
484
+ this.fileClusters.set((_a = primaryFile.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase(), event.files);
485
+ for (const file of event.files) {
486
+ file.srcPath = (0, util_1.standardizePath)(file.srcPath);
487
+ if (file.destPath) {
488
+ file.destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.destPath, this.options.rootDir, '')}`;
489
+ }
490
+ if (file.pkgPath) {
491
+ file.pkgPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.pkgPath, this.options.rootDir, '')}`;
492
+ }
493
+ else {
494
+ file.pkgPath = file.destPath;
495
+ }
496
+ file.excludeFromOutput = file.excludeFromOutput === true;
497
+ //set the dependencyGraph key for every file to its destPath
498
+ file.dependencyGraphKey = file.destPath.toLowerCase();
499
+ this.assignFile(file);
500
+ //register a callback anytime this file's dependencies change
501
+ if (typeof file.onDependenciesChanged === 'function') {
502
+ (_b = file.disposables) !== null && _b !== void 0 ? _b : (file.disposables = []);
503
+ file.disposables.push(this.dependencyGraph.onchange(file.dependencyGraphKey, file.onDependenciesChanged.bind(file)));
504
+ }
505
+ //register this file (and its dependencies) with the dependency graph
506
+ this.dependencyGraph.addOrReplace(file.dependencyGraphKey, (_c = file.dependencies) !== null && _c !== void 0 ? _c : []);
507
+ //if this is a `source` file, add it to the source scope's dependency list
508
+ if (this.isSourceBrsFile(file)) {
509
+ this.createSourceScope();
510
+ this.dependencyGraph.addDependency('scope:source', file.dependencyGraphKey);
511
+ }
512
+ //if this is an xml file in the components folder, register it as a component
513
+ if (this.isComponentsXmlFile(file)) {
514
+ //create a new scope for this xml file
515
+ let scope = new XmlScope_1.XmlScope(file, this);
516
+ this.addScope(scope);
517
+ //register this compoent now that we have parsed it and know its component name
518
+ this.registerComponent(file, scope);
519
+ //notify plugins that the scope is created and the component is registered
520
+ this.plugins.emit('afterScopeCreate', {
521
+ program: this,
522
+ scope: scope
523
+ });
524
+ }
525
+ }
526
+ return primaryFile;
527
+ });
528
+ return file;
529
+ }
530
+ /**
531
+ * Given a srcPath, a destPath, or both, resolve whichever is missing, relative to rootDir.
532
+ * @param fileParam an object representing file paths
533
+ * @param rootDir must be a pre-normalized path
534
+ */
535
+ getPaths(fileParam, rootDir) {
536
+ let srcPath;
537
+ let destPath;
538
+ assert.ok(fileParam, 'fileParam is required');
539
+ //lift the path vars from the incoming param
540
+ if (typeof fileParam === 'string') {
541
+ fileParam = this.removePkgPrefix(fileParam);
542
+ srcPath = (0, util_1.standardizePath) `${path.resolve(rootDir, fileParam)}`;
543
+ destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
300
544
  }
301
545
  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');
309
- //if the file is already loaded, remove it
310
- if (this.hasFile(srcPath)) {
311
- this.removeFile(srcPath);
546
+ let param = fileParam;
547
+ if (param.src) {
548
+ srcPath = (0, util_1.standardizePath) `${param.src}`;
312
549
  }
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/')) {
324
- this.createSourceScope();
325
- this.dependencyGraph.addDependency('scope:source', brsFile.dependencyGraphKey);
326
- }
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
- });
550
+ if (param.srcPath) {
551
+ srcPath = (0, util_1.standardizePath) `${param.srcPath}`;
339
552
  }
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
- });
553
+ if (param.dest) {
554
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.dest)}`;
362
555
  }
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;
556
+ if (param.pkgPath) {
557
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.pkgPath)}`;
371
558
  }
372
- return file;
373
- });
559
+ }
560
+ //if there's no srcPath, use the destPath to build an absolute srcPath
561
+ if (!srcPath) {
562
+ srcPath = (0, util_1.standardizePath) `${rootDir}/${destPath}`;
563
+ }
564
+ //coerce srcPath to an absolute path
565
+ if (!path.isAbsolute(srcPath)) {
566
+ srcPath = util_1.util.standardizePath(srcPath);
567
+ }
568
+ //if destPath isn't set, compute it from the other paths
569
+ if (!destPath) {
570
+ destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
571
+ }
572
+ assert.ok(srcPath, 'fileEntry.src is required');
573
+ assert.ok(destPath, 'fileEntry.dest is required');
574
+ return {
575
+ srcPath: srcPath,
576
+ //remove leading slash
577
+ destPath: destPath.replace(/^[\/\\]+/, '')
578
+ };
579
+ }
580
+ /**
581
+ * Remove any leading `pkg:/` found in the path
582
+ */
583
+ removePkgPrefix(path) {
584
+ return path.replace(/^pkg:\//i, '');
585
+ }
586
+ /**
587
+ * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
588
+ */
589
+ isSourceBrsFile(file) {
590
+ return !!/^(pkg:\/)?source[\/\\]/.exec(file.destPath);
591
+ }
592
+ /**
593
+ * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
594
+ */
595
+ isComponentsXmlFile(file) {
596
+ return (0, reflection_1.isXmlFile)(file) && !!/^(pkg:\/)?components[\/\\]/.exec(file.destPath);
374
597
  }
375
598
  /**
376
599
  * Ensure source scope is created.
@@ -381,238 +604,379 @@ class Program {
381
604
  const sourceScope = new Scope_1.Scope('source', this, 'scope:source');
382
605
  sourceScope.attachDependencyGraph(this.dependencyGraph);
383
606
  this.addScope(sourceScope);
607
+ this.plugins.emit('afterScopeCreate', {
608
+ program: this,
609
+ scope: sourceScope
610
+ });
384
611
  }
385
612
  }
386
613
  /**
387
614
  * Remove a set of files from the program
388
- * @param srcPaths
615
+ * @param srcPaths can be an array of srcPath or destPath strings
616
+ * @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
617
  */
390
- removeFiles(srcPaths) {
618
+ removeFiles(srcPaths, normalizePath = true) {
391
619
  for (let srcPath of srcPaths) {
392
- this.removeFile(srcPath);
620
+ this.removeFile(srcPath, normalizePath);
393
621
  }
394
622
  }
395
623
  /**
396
624
  * Remove a file from the program
397
- * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
625
+ * @param filePath can be a srcPath, a destPath, or a destPath with leading `pkg:/`
398
626
  * @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
627
  */
401
- removeFile(filePath, normalizePath = true) {
628
+ removeFile(filePath, normalizePath = true, keepSymbolInformation = false) {
629
+ var _a, _b, _c, _d;
402
630
  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
- });
631
+ const paths = this.getPaths(filePath, this.options.rootDir);
632
+ //there can be one or more File entries for a single srcPath, so get all of them and remove them all
633
+ 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)];
634
+ for (const file of files) {
635
+ //if a file has already been removed, nothing more needs to be done here
636
+ if (!file || !this.hasFile(file.srcPath)) {
637
+ continue;
638
+ }
639
+ this.diagnostics.clearForFile(file.srcPath);
640
+ const event = { file: file, program: this };
641
+ this.plugins.emit('beforeFileRemove', event);
409
642
  //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];
643
+ let scope = this.scopes[file.destPath];
411
644
  if (scope) {
412
- this.plugins.emit('beforeScopeDispose', {
645
+ const scopeDisposeEvent = {
413
646
  program: this,
414
647
  scope: scope
415
- });
648
+ };
649
+ this.plugins.emit('beforeScopeDispose', scopeDisposeEvent);
650
+ this.plugins.emit('onScopeDispose', scopeDisposeEvent);
416
651
  scope.dispose();
417
652
  //notify dependencies of this scope that it has been removed
418
653
  this.dependencyGraph.remove(scope.dependencyGraphKey);
419
- delete this.scopes[file.pkgPath];
420
- this.plugins.emit('afterScopeDispose', {
421
- program: this,
422
- scope: scope
423
- });
654
+ this.removeScope(this.scopes[file.destPath]);
655
+ this.plugins.emit('afterScopeDispose', scopeDisposeEvent);
424
656
  }
425
657
  //remove the file from the program
426
658
  this.unassignFile(file);
427
659
  this.dependencyGraph.remove(file.dependencyGraphKey);
428
660
  //if this is a pkg:/source file, notify the `source` scope that it has changed
429
- if (file.pkgPath.startsWith('pkg:/source/')) {
661
+ if (this.isSourceBrsFile(file)) {
430
662
  this.dependencyGraph.removeDependency('scope:source', file.dependencyGraphKey);
431
663
  }
664
+ if ((0, reflection_1.isBrsFile)(file)) {
665
+ if (!keepSymbolInformation) {
666
+ this.fileSymbolInformation.delete(file.pkgPath);
667
+ }
668
+ this.crossScopeValidation.clearResolutionsForFile(file);
669
+ }
432
670
  //if this is a component, remove it from our components map
433
- if (reflection_1.isXmlFile(file)) {
671
+ if ((0, reflection_1.isXmlFile)(file)) {
434
672
  this.unregisterComponent(file);
435
673
  }
436
- this.plugins.emit('afterFileDispose', {
437
- program: this,
438
- file: file
439
- });
440
- }
441
- }
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);
450
- }
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);
674
+ //dispose any disposable things on the file
675
+ for (const disposable of (_c = file === null || file === void 0 ? void 0 : file.disposables) !== null && _c !== void 0 ? _c : []) {
676
+ disposable();
458
677
  }
678
+ //dispose file
679
+ (_d = file === null || file === void 0 ? void 0 : file.dispose) === null || _d === void 0 ? void 0 : _d.call(file);
680
+ this.plugins.emit('afterFileRemove', event);
459
681
  }
460
682
  }
461
683
  /**
462
684
  * 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
685
  */
465
686
  validate() {
466
- this.logger.time(Logger_1.LogLevel.log, ['Validating project'], () => {
467
- var _a;
468
- this.diagnostics = [];
469
- this.plugins.emit('beforeProgramValidate', {
687
+ this.logger.time(logging_1.LogLevel.log, ['Validating project'], () => {
688
+ this.diagnostics.clearForTag(ProgramValidator_1.ProgramValidatorDiagnosticsTag);
689
+ const programValidateEvent = {
470
690
  program: this
471
- });
691
+ };
692
+ this.plugins.emit('beforeProgramValidate', programValidateEvent);
693
+ this.plugins.emit('onProgramValidate', programValidateEvent);
694
+ const metrics = {
695
+ filesChanged: 0,
696
+ filesValidated: 0,
697
+ fileValidationTime: '',
698
+ crossScopeValidationTime: '',
699
+ scopesValidated: 0,
700
+ totalLinkTime: '',
701
+ totalScopeValidationTime: '',
702
+ componentValidationTime: ''
703
+ };
704
+ const validationStopwatch = new Stopwatch_1.Stopwatch();
472
705
  //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) }));
706
+ const brsFilesValidated = [];
707
+ const afterValidateFiles = [];
708
+ metrics.fileValidationTime = validationStopwatch.getDurationTextFor(() => {
709
+ //sort files by path so we get consistent results
710
+ const files = Object.values(this.files).sort((0, thenby_1.firstBy)(x => x.srcPath));
711
+ for (const file of files) {
712
+ //for every unvalidated file, validate it
713
+ if (!file.isValidated) {
714
+ const validateFileEvent = {
715
+ program: this,
716
+ file: file
717
+ };
718
+ this.plugins.emit('beforeFileValidate', validateFileEvent);
719
+ //emit an event to allow plugins to contribute to the file validation process
720
+ this.plugins.emit('onFileValidate', validateFileEvent);
721
+ file.isValidated = true;
722
+ if ((0, reflection_1.isBrsFile)(file)) {
723
+ brsFilesValidated.push(file);
724
+ }
725
+ afterValidateFiles.push(file);
726
+ }
479
727
  }
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', {
728
+ // AfterFileValidate is after all files have been validated
729
+ for (const file of afterValidateFiles) {
730
+ const validateFileEvent = {
490
731
  program: this,
491
732
  file: file
492
- });
733
+ };
734
+ this.plugins.emit('afterFileValidate', validateFileEvent);
735
+ }
736
+ }).durationText;
737
+ metrics.filesChanged = afterValidateFiles.length;
738
+ // Build component types for any component that changes
739
+ this.logger.time(logging_1.LogLevel.info, ['Build component types'], () => {
740
+ for (let { componentKey, componentName } of this.componentSymbolsToUpdate) {
741
+ this.updateComponentSymbolInGlobalScope(componentKey, componentName);
493
742
  }
743
+ this.componentSymbolsToUpdate.clear();
744
+ });
745
+ const changedSymbolsMapArr = brsFilesValidated === null || brsFilesValidated === void 0 ? void 0 : brsFilesValidated.map(f => {
746
+ if ((0, reflection_1.isBrsFile)(f)) {
747
+ return f.providedSymbols.changes;
748
+ }
749
+ return null;
750
+ }).filter(x => x);
751
+ const changedSymbols = new Map();
752
+ for (const flag of [1 /* SymbolTypeFlag.runtime */, 2 /* SymbolTypeFlag.typetime */]) {
753
+ const changedSymbolsSetArr = changedSymbolsMapArr.map(symMap => symMap.get(flag));
754
+ changedSymbols.set(flag, new Set(...changedSymbolsSetArr));
494
755
  }
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
- });
756
+ const filesToBeValidatedInScopeContext = new Set(afterValidateFiles);
757
+ metrics.crossScopeValidationTime = validationStopwatch.getDurationTextFor(() => {
758
+ const scopesToCheck = this.getScopesForCrossScopeValidation();
759
+ this.crossScopeValidation.buildComponentsMap();
760
+ this.crossScopeValidation.addDiagnosticsForScopes(scopesToCheck);
761
+ const filesToRevalidate = this.crossScopeValidation.getFilesRequiringChangedSymbol(scopesToCheck, changedSymbols);
762
+ for (const file of filesToRevalidate) {
763
+ filesToBeValidatedInScopeContext.add(file);
764
+ }
765
+ }).durationText;
766
+ metrics.filesValidated = filesToBeValidatedInScopeContext.size;
767
+ let linkTime = 0;
768
+ let validationTime = 0;
769
+ let scopesValidated = 0;
770
+ let changedFiles = new Set(afterValidateFiles);
771
+ this.logger.time(logging_1.LogLevel.info, ['Validate all scopes'], () => {
772
+ //sort the scope names so we get consistent results
773
+ const scopeNames = this.getSortedScopeNames();
774
+ for (const file of filesToBeValidatedInScopeContext) {
775
+ if ((0, reflection_1.isBrsFile)(file)) {
776
+ file.validationSegmenter.unValidateAllSegments();
509
777
  }
510
778
  }
779
+ for (let scopeName of scopeNames) {
780
+ let scope = this.scopes[scopeName];
781
+ const scopeValidated = scope.validate({
782
+ filesToBeValidatedInScopeContext: filesToBeValidatedInScopeContext,
783
+ changedSymbols: changedSymbols,
784
+ changedFiles: changedFiles,
785
+ initialValidation: this.isFirstValidation
786
+ });
787
+ if (scopeValidated) {
788
+ scopesValidated++;
789
+ }
790
+ linkTime += scope.validationMetrics.linkTime;
791
+ validationTime += scope.validationMetrics.validationTime;
792
+ }
511
793
  });
512
- this.detectDuplicateComponentNames();
513
- this.plugins.emit('afterProgramValidate', {
514
- program: this
515
- });
794
+ metrics.scopesValidated = scopesValidated;
795
+ validationStopwatch.totalMilliseconds = linkTime;
796
+ metrics.totalLinkTime = validationStopwatch.getDurationText();
797
+ validationStopwatch.totalMilliseconds = validationTime;
798
+ metrics.totalScopeValidationTime = validationStopwatch.getDurationText();
799
+ metrics.componentValidationTime = validationStopwatch.getDurationTextFor(() => {
800
+ this.detectDuplicateComponentNames();
801
+ }).durationText;
802
+ this.logValidationMetrics(metrics);
803
+ this.isFirstValidation = false;
804
+ this.plugins.emit('afterProgramValidate', programValidateEvent);
516
805
  });
517
806
  }
807
+ // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
808
+ logValidationMetrics(metrics) {
809
+ let logs = [];
810
+ for (const key in metrics) {
811
+ logs.push(`${key}=${chalk_1.default.yellow(metrics[key].toString())}`);
812
+ }
813
+ this.logger.info(`Validation Metrics: ${logs.join(', ')}`);
814
+ }
815
+ getScopesForCrossScopeValidation() {
816
+ const scopesForCrossScopeValidation = [];
817
+ for (let scopeName of this.getSortedScopeNames()) {
818
+ let scope = this.scopes[scopeName];
819
+ if (this.globalScope !== scope && !scope.isValidated) {
820
+ scopesForCrossScopeValidation.push(scope);
821
+ }
822
+ }
823
+ return scopesForCrossScopeValidation;
824
+ }
518
825
  /**
519
826
  * Flag all duplicate component names
520
827
  */
521
828
  detectDuplicateComponentNames() {
522
- var _a;
523
- const componentsByName = new Map();
524
- for (const key in this.files) {
525
- const file = this.files[key];
829
+ const componentsByName = Object.keys(this.files).reduce((map, filePath) => {
830
+ var _a;
831
+ const file = this.files[filePath];
526
832
  //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
- }
833
+ if ((0, reflection_1.isXmlFile)(file) && ((_a = file.componentName) === null || _a === void 0 ? void 0 : _a.text)) {
834
+ let lowerName = file.componentName.text.toLowerCase();
835
+ if (!map[lowerName]) {
836
+ map[lowerName] = [];
536
837
  }
838
+ map[lowerName].push(file);
537
839
  }
538
- }
539
- for (const xmlFiles of componentsByName.values()) {
840
+ return map;
841
+ }, {});
842
+ for (let name in componentsByName) {
843
+ const xmlFiles = componentsByName[name];
540
844
  //add diagnostics for every duplicate component with this name
541
845
  if (xmlFiles.length > 1) {
542
846
  for (let xmlFile of xmlFiles) {
543
847
  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 => {
848
+ 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
849
  return {
546
- location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(xmlFile.srcPath).toString(), x.componentName.range),
850
+ location: x.componentName.location,
547
851
  message: 'Also defined here'
548
852
  };
549
- }) }));
853
+ }) }), { tags: [ProgramValidator_1.ProgramValidatorDiagnosticsTag] });
550
854
  }
551
855
  }
552
856
  }
553
857
  }
554
858
  /**
555
- * Determine at least one scope has the file
859
+ * Get the files for a list of filePaths
860
+ * @param filePaths can be an array of srcPath or a destPath strings
861
+ * @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
862
  */
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;
863
+ getFiles(filePaths, normalizePath = true) {
864
+ return filePaths
865
+ .map(filePath => this.getFile(filePath, normalizePath))
866
+ .filter(file => file !== undefined);
564
867
  }
565
868
  /**
566
869
  * 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:/`)
870
+ * @param filePath can be a srcPath or a destPath
568
871
  * @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
872
  */
570
873
  getFile(filePath, normalizePath = true) {
571
874
  if (typeof filePath !== 'string') {
572
875
  return undefined;
876
+ //is the path absolute (or the `virtual:` prefix)
573
877
  }
574
- else if (path.isAbsolute(filePath)) {
878
+ else if (/^(?:(?:virtual:[\/\\])|(?:\w:)|(?:[\/\\]))/gmi.exec(filePath)) {
575
879
  return this.files[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
576
880
  }
881
+ else if (util_1.util.isUriLike(filePath)) {
882
+ const path = vscode_uri_1.URI.parse(filePath).fsPath;
883
+ return this.files[(normalizePath ? util_1.util.standardizePath(path) : path).toLowerCase()];
884
+ }
577
885
  else {
578
- return this.pkgMap[(normalizePath ? util_1.util.sanitizePkgPath(filePath) : filePath).toLowerCase()];
886
+ return this.destMap.get((normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase());
579
887
  }
580
888
  }
889
+ /**
890
+ * Gets a sorted list of all scopeNames, always beginning with "global", "source", then any others in alphabetical order
891
+ */
892
+ getSortedScopeNames() {
893
+ if (!this.sortedScopeNames) {
894
+ this.sortedScopeNames = Object.keys(this.scopes).sort((a, b) => {
895
+ if (a === 'global') {
896
+ return -1;
897
+ }
898
+ else if (b === 'global') {
899
+ return 1;
900
+ }
901
+ if (a === 'source') {
902
+ return -1;
903
+ }
904
+ else if (b === 'source') {
905
+ return 1;
906
+ }
907
+ if (a < b) {
908
+ return -1;
909
+ }
910
+ else if (b < a) {
911
+ return 1;
912
+ }
913
+ return 0;
914
+ });
915
+ }
916
+ return this.sortedScopeNames;
917
+ }
581
918
  /**
582
919
  * Get a list of all scopes the file is loaded into
583
- * @param file
920
+ * @param file the file
584
921
  */
585
922
  getScopesForFile(file) {
923
+ const resolvedFile = typeof file === 'string' ? this.getFile(file) : file;
586
924
  let result = [];
587
- for (let key in this.scopes) {
925
+ if (resolvedFile) {
926
+ const scopeKeys = this.getSortedScopeNames();
927
+ for (let key of scopeKeys) {
928
+ let scope = this.scopes[key];
929
+ if (scope.hasFile(resolvedFile)) {
930
+ result.push(scope);
931
+ }
932
+ }
933
+ }
934
+ return result;
935
+ }
936
+ /**
937
+ * Get the first found scope for a file.
938
+ */
939
+ getFirstScopeForFile(file) {
940
+ const scopeKeys = this.getSortedScopeNames();
941
+ for (let key of scopeKeys) {
588
942
  let scope = this.scopes[key];
589
943
  if (scope.hasFile(file)) {
590
- result.push(scope);
944
+ return scope;
591
945
  }
592
946
  }
593
- return result;
594
947
  }
595
948
  getStatementsByName(name, originFile, namespaceName) {
596
- var _a, _b;
597
949
  let results = new Map();
598
950
  const filesSearched = new Set();
599
951
  let lowerNamespaceName = namespaceName === null || namespaceName === void 0 ? void 0 : namespaceName.toLowerCase();
600
952
  let lowerName = name === null || name === void 0 ? void 0 : name.toLowerCase();
953
+ function addToResults(statement, file) {
954
+ var _a, _b;
955
+ 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();
956
+ if (statement.tokens.name.text.toLowerCase() === lowerName && (!lowerNamespaceName || parentNamespaceName === lowerNamespaceName)) {
957
+ if (!results.has(statement)) {
958
+ results.set(statement, { item: statement, file: file });
959
+ }
960
+ }
961
+ }
601
962
  //look through all files in scope for matches
602
963
  for (const scope of this.getScopesForFile(originFile)) {
603
964
  for (const file of scope.getAllFiles()) {
604
- if (reflection_1.isXmlFile(file) || filesSearched.has(file)) {
965
+ //skip non-brs files, or files we've already processed
966
+ if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
605
967
  continue;
606
968
  }
607
969
  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
- }
970
+ file.ast.walk((0, visitors_1.createVisitor)({
971
+ FunctionStatement: (statement) => {
972
+ addToResults(statement, file);
973
+ },
974
+ MethodStatement: (statement) => {
975
+ addToResults(statement, file);
614
976
  }
615
- }
977
+ }), {
978
+ walkMode: visitors_1.WalkMode.visitStatements
979
+ });
616
980
  }
617
981
  }
618
982
  return [...results.values()];
@@ -624,8 +988,8 @@ class Program {
624
988
  //get all function names for the xml file and parents
625
989
  let funcNames = new Set();
626
990
  let currentScope = scope;
627
- while (reflection_1.isXmlScope(currentScope)) {
628
- for (let name of (_b = (_a = currentScope.xmlFile.ast.component.api) === null || _a === void 0 ? void 0 : _a.functions.map((f) => f.name)) !== null && _b !== void 0 ? _b : []) {
991
+ while ((0, reflection_1.isXmlScope)(currentScope)) {
992
+ 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 : []) {
629
993
  if (!filterName || name === filterName) {
630
994
  funcNames.add(name);
631
995
  }
@@ -634,121 +998,141 @@ class Program {
634
998
  }
635
999
  //look through all files in scope for matches
636
1000
  for (const file of scope.getOwnFiles()) {
637
- if (reflection_1.isXmlFile(file) || filesSearched.has(file)) {
1001
+ //skip non-brs files, or files we've already processed
1002
+ if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
638
1003
  continue;
639
1004
  }
640
1005
  filesSearched.add(file);
641
- for (const statement of file.parser.references.functionStatements) {
642
- if (funcNames.has(statement.name.text)) {
643
- if (!results.has(statement)) {
644
- results.set(statement, { item: statement, file: file });
1006
+ file.ast.walk((0, visitors_1.createVisitor)({
1007
+ FunctionStatement: (statement) => {
1008
+ if (funcNames.has(statement.tokens.name.text)) {
1009
+ if (!results.has(statement)) {
1010
+ results.set(statement, { item: statement, file: file });
1011
+ }
645
1012
  }
646
1013
  }
647
- }
1014
+ }), {
1015
+ walkMode: visitors_1.WalkMode.visitStatements
1016
+ });
648
1017
  }
649
1018
  return [...results.values()];
650
1019
  }
651
1020
  /**
652
1021
  * Find all available completion items at the given position
653
- * @param srcPath The absolute path to the source file on disk
654
- * @param lineIndex
655
- * @param columnIndex
1022
+ * @param filePath can be a srcPath or a destPath
1023
+ * @param position the position (line & column) where completions should be found
656
1024
  */
657
- getCompletions(srcPath, position) {
658
- let file = this.getFile(srcPath);
1025
+ getCompletions(filePath, position) {
1026
+ let file = this.getFile(filePath);
659
1027
  if (!file) {
660
1028
  return [];
661
1029
  }
662
- let result = [];
663
- if (reflection_1.isBrsFile(file) && file.parser.isPositionNextToTokenKind(position, lexer_1.TokenKind.Callfunc)) {
664
- // is next to a @. callfunc invocation - must be an interface method
665
- for (const scope of this.getScopes().filter((s) => reflection_1.isXmlScope(s))) {
666
- let fileLinks = this.getStatementsForXmlFile(scope);
667
- for (let fileLink of fileLinks) {
668
- result.push(scope.createCompletionFromFunctionStatement(fileLink.item));
669
- }
670
- }
671
- //no other result is possible in this case
672
- return result;
673
- }
674
1030
  //find the scopes for this file
675
1031
  let scopes = this.getScopesForFile(file);
676
1032
  //if there are no scopes, include the global scope so we at least get the built-in functions
677
1033
  scopes = scopes.length > 0 ? scopes : [this.globalScope];
678
- //get the completions from all scopes for this file
679
- let allCompletions = util_1.util.flatMap(scopes.map(ctx => file.getCompletions(position, ctx)), c => c);
680
- //only keep completions common to every scope for this file
681
- let keyCounts = {};
682
- for (let completion of allCompletions) {
683
- let key = `${completion.label}-${completion.kind}`;
684
- keyCounts[key] = keyCounts[key] ? keyCounts[key] + 1 : 1;
685
- if (keyCounts[key] === scopes.length) {
686
- result.push(completion);
687
- }
688
- }
689
- return result;
1034
+ const event = {
1035
+ program: this,
1036
+ file: file,
1037
+ scopes: scopes,
1038
+ position: position,
1039
+ completions: []
1040
+ };
1041
+ this.plugins.emit('beforeProvideCompletions', event);
1042
+ this.plugins.emit('provideCompletions', event);
1043
+ this.plugins.emit('afterProvideCompletions', event);
1044
+ return event.completions;
690
1045
  }
691
1046
  /**
692
1047
  * Goes through each file and builds a list of workspace symbols for the program. Used by LanguageServer's onWorkspaceSymbol functionality
693
1048
  */
694
1049
  getWorkspaceSymbols() {
695
- const result = [];
696
- for (const key in this.files) {
697
- const file = this.files[key];
698
- if (reflection_1.isBrsFile(file)) {
699
- result.push(...file.getWorkspaceSymbols());
700
- }
701
- }
702
- return result;
1050
+ const event = {
1051
+ program: this,
1052
+ workspaceSymbols: []
1053
+ };
1054
+ this.plugins.emit('beforeProvideWorkspaceSymbols', event);
1055
+ this.plugins.emit('provideWorkspaceSymbols', event);
1056
+ this.plugins.emit('afterProvideWorkspaceSymbols', event);
1057
+ return event.workspaceSymbols;
703
1058
  }
704
1059
  /**
705
1060
  * Given a position in a file, if the position is sitting on some type of identifier,
706
1061
  * go to the definition of that identifier (where this thing was first defined)
707
- * @param srcPath The absolute path to the source file on disk
708
1062
  */
709
1063
  getDefinition(srcPath, position) {
710
1064
  let file = this.getFile(srcPath);
711
1065
  if (!file) {
712
1066
  return [];
713
1067
  }
714
- if (reflection_1.isBrsFile(file)) {
715
- return file.getDefinition(position);
716
- }
717
- else {
718
- let results = [];
719
- const scopes = this.getScopesForFile(file);
720
- for (const scope of scopes) {
721
- results = results.concat(...scope.getDefinition(file, position));
722
- }
723
- return results;
724
- }
1068
+ const event = {
1069
+ program: this,
1070
+ file: file,
1071
+ position: position,
1072
+ definitions: []
1073
+ };
1074
+ this.plugins.emit('beforeProvideDefinition', event);
1075
+ this.plugins.emit('provideDefinition', event);
1076
+ this.plugins.emit('afterProvideDefinition', event);
1077
+ return event.definitions;
725
1078
  }
726
1079
  /**
727
- * @param srcPath The absolute path to the source file on disk
1080
+ * Get hover information for a file and position
728
1081
  */
729
1082
  getHover(srcPath, position) {
730
- //find the file
731
1083
  let file = this.getFile(srcPath);
732
- if (!file) {
733
- return null;
1084
+ let result;
1085
+ if (file) {
1086
+ const event = {
1087
+ program: this,
1088
+ file: file,
1089
+ position: position,
1090
+ scopes: this.getScopesForFile(file),
1091
+ hovers: []
1092
+ };
1093
+ this.plugins.emit('beforeProvideHover', event);
1094
+ this.plugins.emit('provideHover', event);
1095
+ this.plugins.emit('afterProvideHover', event);
1096
+ result = event.hovers;
1097
+ }
1098
+ return result !== null && result !== void 0 ? result : [];
1099
+ }
1100
+ /**
1101
+ * Get full list of document symbols for a file
1102
+ * @param srcPath path to the file
1103
+ */
1104
+ getDocumentSymbols(srcPath) {
1105
+ let file = this.getFile(srcPath);
1106
+ if (file) {
1107
+ const event = {
1108
+ program: this,
1109
+ file: file,
1110
+ documentSymbols: []
1111
+ };
1112
+ this.plugins.emit('beforeProvideDocumentSymbols', event);
1113
+ this.plugins.emit('provideDocumentSymbols', event);
1114
+ this.plugins.emit('afterProvideDocumentSymbols', event);
1115
+ return event.documentSymbols;
1116
+ }
1117
+ else {
1118
+ return undefined;
734
1119
  }
735
- return Promise.resolve(file.getHover(position));
736
1120
  }
737
1121
  /**
738
1122
  * Compute code actions for the given file and range
739
- * @param srcPath The absolute path to the source file on disk
740
1123
  */
741
1124
  getCodeActions(srcPath, range) {
742
1125
  const codeActions = [];
743
1126
  const file = this.getFile(srcPath);
744
1127
  if (file) {
1128
+ const fileUri = util_1.util.pathToUri(file === null || file === void 0 ? void 0 : file.srcPath);
745
1129
  const diagnostics = this
746
1130
  //get all current diagnostics (filtered by diagnostic filters)
747
1131
  .getDiagnostics()
748
1132
  //only keep diagnostics related to this file
749
- .filter(x => x.file === file)
1133
+ .filter(x => { var _a; return ((_a = x.location) === null || _a === void 0 ? void 0 : _a.uri) === fileUri; })
750
1134
  //only keep diagnostics that touch this range
751
- .filter(x => util_1.util.rangesIntersect(x.range, range));
1135
+ .filter(x => util_1.util.rangesIntersectOrTouch(x.location.range, range));
752
1136
  const scopes = this.getScopesForFile(file);
753
1137
  this.plugins.emit('onGetCodeActions', {
754
1138
  program: this,
@@ -761,362 +1145,269 @@ class Program {
761
1145
  }
762
1146
  return codeActions;
763
1147
  }
1148
+ /**
1149
+ * Get semantic tokens for the specified file
1150
+ */
1151
+ getSemanticTokens(srcPath) {
1152
+ const file = this.getFile(srcPath);
1153
+ if (file) {
1154
+ const result = [];
1155
+ this.plugins.emit('onGetSemanticTokens', {
1156
+ program: this,
1157
+ file: file,
1158
+ scopes: this.getScopesForFile(file),
1159
+ semanticTokens: result
1160
+ });
1161
+ return result;
1162
+ }
1163
+ }
764
1164
  getSignatureHelp(filepath, position) {
765
- var _a;
766
1165
  let file = this.getFile(filepath);
767
- if (!file || !reflection_1.isBrsFile(file)) {
1166
+ if (!file || !(0, reflection_1.isBrsFile)(file)) {
768
1167
  return [];
769
1168
  }
770
- const results = new Map();
771
- let functionExpression = file.getFunctionExpressionAtPosition(position);
772
- let identifierInfo = this.getPartialStatementInfo(file, position);
773
- if (identifierInfo.statementType === '') {
774
- // just general function calls
775
- let statements = file.program.getStatementsByName(identifierInfo.name, file);
776
- for (let statement of statements) {
777
- //TODO better handling of collisions - if it's a namespace, then don't show any other overrides
778
- //if we're on m - then limit scope to the current class, if present
779
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
780
- if (sigHelp && !results.has[sigHelp.key]) {
781
- sigHelp.index = identifierInfo.commaCount;
782
- results.set(sigHelp.key, sigHelp);
783
- }
784
- }
785
- }
786
- else if (identifierInfo.statementType === '.') {
787
- //if m class reference.. then
788
- //only get statements from the class I am in..
789
- if (functionExpression) {
790
- let myClass = file.getClassFromMReference(position, file.parser.getTokenAt(position), functionExpression);
791
- if (myClass) {
792
- for (let scope of this.getScopesForFile(myClass.file)) {
793
- let classes = scope.getClassHierarchy(myClass.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
794
- //and anything from any class in scope to a non m class
795
- for (let statement of [...classes].filter((i) => reflection_1.isClassMethodStatement(i.item))) {
796
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
797
- if (sigHelp && !results.has[sigHelp.key]) {
798
- results.set(sigHelp.key, sigHelp);
799
- return;
800
- }
801
- }
802
- }
803
- }
804
- }
805
- if (identifierInfo.dotPart) {
806
- //potential namespaces
807
- let statements = file.program.getStatementsByName(identifierInfo.name, file, identifierInfo.dotPart);
808
- if (statements.length === 0) {
809
- //was not a namespaced function, it could be any method on any class now
810
- statements = file.program.getStatementsByName(identifierInfo.name, file);
811
- }
812
- for (let statement of statements) {
813
- //TODO better handling of collisions - if it's a namespace, then don't show any other overrides
814
- //if we're on m - then limit scope to the current class, if present
815
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
816
- if (sigHelp && !results.has[sigHelp.key]) {
817
- sigHelp.index = identifierInfo.commaCount;
818
- results.set(sigHelp.key, sigHelp);
819
- }
820
- }
821
- }
822
- }
823
- else if (identifierInfo.statementType === '@.') {
824
- for (const scope of this.getScopes().filter((s) => reflection_1.isXmlScope(s))) {
825
- let fileLinks = this.getStatementsForXmlFile(scope, identifierInfo.name);
826
- for (let fileLink of fileLinks) {
827
- let sigHelp = fileLink.file.getSignatureHelpForStatement(fileLink.item);
828
- if (sigHelp && !results.has[sigHelp.key]) {
829
- sigHelp.index = identifierInfo.commaCount;
830
- results.set(sigHelp.key, sigHelp);
831
- }
832
- }
833
- }
834
- }
835
- else if (identifierInfo.statementType === 'new') {
836
- let classItem = file.getClassFileLink(identifierInfo.dotPart ? `${identifierInfo.dotPart}.${identifierInfo.name}` : identifierInfo.name);
837
- 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);
838
- if (sigHelp && !results.has(sigHelp.key)) {
839
- sigHelp.index = identifierInfo.commaCount;
840
- results.set(sigHelp.key, sigHelp);
841
- }
842
- }
843
- return [...results.values()];
1169
+ let callExpressionInfo = new CallExpressionInfo_1.CallExpressionInfo(file, position);
1170
+ let signatureHelpUtil = new SignatureHelpUtil_1.SignatureHelpUtil();
1171
+ return signatureHelpUtil.getSignatureHelpItems(callExpressionInfo);
844
1172
  }
845
- getPartialStatementInfo(file, position) {
846
- let lines = util_1.util.splitIntoLines(file.fileContents);
847
- let line = lines[position.line];
848
- let index = position.character;
849
- let itemCounts = this.getPartialItemCounts(line, index);
850
- if (!itemCounts.isArgStartFound && line.charAt(index) === ')') {
851
- //try previous char, in case we were on a close bracket..
852
- index--;
853
- itemCounts = this.getPartialItemCounts(line, index);
854
- }
855
- let argStartIndex = itemCounts.argStartIndex;
856
- index = itemCounts.argStartIndex - 1;
857
- let statementType = '';
858
- let name;
859
- let dotPart;
860
- if (!itemCounts.isArgStartFound) {
861
- //try to get sig help based on the name
862
- index = position.character;
863
- let currentToken = file.parser.getTokenAt(position);
864
- if (currentToken && currentToken.kind !== lexer_1.TokenKind.Comment) {
865
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
866
- if (!name) {
867
- //try the previous token, incase we're on a bracket
868
- currentToken = file.parser.getPreviousToken(currentToken);
869
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
870
- }
871
- if (name === null || name === void 0 ? void 0 : name.indexOf('.')) {
872
- let parts = name.split('.');
873
- name = parts[parts.length - 1];
874
- }
875
- index = currentToken.range.start.character;
876
- argStartIndex = index;
877
- }
878
- else {
879
- // invalid location
880
- index = 0;
881
- itemCounts.comma = 0;
882
- }
883
- }
884
- //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).
885
- while (index >= -1) {
886
- if (!(/[a-z0-9_\.\@]/i).test(line.charAt(index))) {
887
- if (!name) {
888
- name = line.substring(index + 1, argStartIndex);
889
- }
890
- else {
891
- dotPart = line.substring(index + 1, argStartIndex);
892
- if (dotPart.endsWith('.')) {
893
- dotPart = dotPart.substr(0, dotPart.length - 1);
1173
+ getReferences(srcPath, position) {
1174
+ //find the file
1175
+ let file = this.getFile(srcPath);
1176
+ const event = {
1177
+ program: this,
1178
+ file: file,
1179
+ position: position,
1180
+ references: []
1181
+ };
1182
+ this.plugins.emit('beforeProvideReferences', event);
1183
+ this.plugins.emit('provideReferences', event);
1184
+ this.plugins.emit('afterProvideReferences', event);
1185
+ return event.references;
1186
+ }
1187
+ /**
1188
+ * Transpile a single file and get the result as a string.
1189
+ * This does not write anything to the file system.
1190
+ *
1191
+ * This should only be called by `LanguageServer`.
1192
+ * Internal usage should call `_getTranspiledFileContents` instead.
1193
+ * @param filePath can be a srcPath or a destPath
1194
+ */
1195
+ async getTranspiledFileContents(filePath) {
1196
+ const file = this.getFile(filePath);
1197
+ return this.getTranspiledFileContentsPipeline.run(async () => {
1198
+ const result = {
1199
+ destPath: file.destPath,
1200
+ pkgPath: file.pkgPath,
1201
+ srcPath: file.srcPath
1202
+ };
1203
+ const expectedPkgPath = file.pkgPath.toLowerCase();
1204
+ const expectedMapPath = `${expectedPkgPath}.map`;
1205
+ const expectedTypedefPkgPath = expectedPkgPath.replace(/\.brs$/i, '.d.bs');
1206
+ //add a temporary plugin to tap into the file writing process
1207
+ const plugin = this.plugins.addFirst({
1208
+ name: 'getTranspiledFileContents',
1209
+ beforeWriteFile: (event) => {
1210
+ const pkgPath = event.file.pkgPath.toLowerCase();
1211
+ switch (pkgPath) {
1212
+ //this is the actual transpiled file
1213
+ case expectedPkgPath:
1214
+ result.code = event.file.data.toString();
1215
+ break;
1216
+ //this is the sourcemap
1217
+ case expectedMapPath:
1218
+ result.map = event.file.data.toString();
1219
+ break;
1220
+ //this is the typedef
1221
+ case expectedTypedefPkgPath:
1222
+ result.typedef = event.file.data.toString();
1223
+ break;
1224
+ default:
1225
+ //no idea what this file is. just ignore it
894
1226
  }
1227
+ //mark every file as processed so it they don't get written to the output directory
1228
+ event.processedFiles.add(event.file);
895
1229
  }
896
- break;
897
- }
898
- if (line.substr(index - 2, 2) === '@.') {
899
- statementType = '@.';
900
- name = name || line.substring(index, argStartIndex);
901
- break;
1230
+ });
1231
+ try {
1232
+ //now that the plugin has been registered, run the build with just this file
1233
+ await this.build({
1234
+ files: [file]
1235
+ });
902
1236
  }
903
- else if (line.charAt(index - 1) === '.' && statementType === '') {
904
- statementType = '.';
905
- name = name || line.substring(index, argStartIndex);
906
- argStartIndex = index;
1237
+ finally {
1238
+ this.plugins.remove(plugin);
907
1239
  }
908
- index--;
909
- }
910
- if (line.substring(0, index).trim().endsWith('new')) {
911
- statementType = 'new';
1240
+ return result;
1241
+ });
1242
+ }
1243
+ /**
1244
+ * Get the absolute output path for a file
1245
+ */
1246
+ getOutputPath(file, stagingDir = this.getStagingDir()) {
1247
+ return (0, util_1.standardizePath) `${stagingDir}/${file.pkgPath}`;
1248
+ }
1249
+ getStagingDir(stagingDir) {
1250
+ var _a, _b;
1251
+ let result = (_a = stagingDir !== null && stagingDir !== void 0 ? stagingDir : this.options.stagingDir) !== null && _a !== void 0 ? _a : this.options.stagingDir;
1252
+ if (!result) {
1253
+ result = roku_deploy_1.rokuDeploy.getOptions(this.options).stagingDir;
912
1254
  }
913
- return {
914
- commaCount: itemCounts.comma,
915
- statementType: statementType,
916
- name: name,
917
- dotPart: dotPart
918
- };
1255
+ result = (0, util_1.standardizePath) `${path.resolve((_b = this.options.cwd) !== null && _b !== void 0 ? _b : process.cwd(), result !== null && result !== void 0 ? result : '/')}`;
1256
+ return result;
919
1257
  }
920
- getPartialItemCounts(line, index) {
921
- let isArgStartFound = false;
922
- let itemCounts = {
923
- normal: 0,
924
- square: 0,
925
- curly: 0,
926
- comma: 0,
927
- endIndex: 0,
928
- argStartIndex: index,
929
- isArgStartFound: false
1258
+ /**
1259
+ * Prepare the program for building
1260
+ * @param files the list of files that should be prepared
1261
+ */
1262
+ async prepare(files) {
1263
+ const programEvent = {
1264
+ program: this,
1265
+ editor: this.editor,
1266
+ files: files
930
1267
  };
931
- while (index >= 0) {
932
- const currentChar = line.charAt(index);
933
- if (currentChar === '\'') { //found comment, invalid index
934
- itemCounts.isArgStartFound = false;
935
- break;
1268
+ //assign an editor to every file
1269
+ for (const file of programEvent.files) {
1270
+ //if the file doesn't have an editor yet, assign one now
1271
+ if (!file.editor) {
1272
+ file.editor = new Editor_1.Editor();
936
1273
  }
937
- if (isArgStartFound) {
938
- if (currentChar !== ' ') {
939
- break;
940
- }
1274
+ }
1275
+ //sort the entries to make transpiling more deterministic
1276
+ programEvent.files.sort((a, b) => {
1277
+ if (a.pkgPath < b.pkgPath) {
1278
+ return -1;
1279
+ }
1280
+ else if (a.pkgPath > b.pkgPath) {
1281
+ return 1;
941
1282
  }
942
1283
  else {
943
- if (currentChar === ')') {
944
- itemCounts.normal++;
945
- }
946
- if (currentChar === ']') {
947
- itemCounts.square++;
948
- }
949
- if (currentChar === '}') {
950
- itemCounts.curly++;
951
- }
952
- if (currentChar === ',' && itemCounts.normal <= 0 && itemCounts.curly <= 0 && itemCounts.square <= 0) {
953
- itemCounts.comma++;
954
- }
955
- if (currentChar === '(') {
956
- if (itemCounts.normal === 0) {
957
- itemCounts.isArgStartFound = true;
958
- itemCounts.argStartIndex = index;
959
- }
960
- else {
961
- itemCounts.normal--;
962
- }
963
- }
964
- if (currentChar === '[') {
965
- itemCounts.square--;
966
- }
967
- if (currentChar === '{') {
968
- itemCounts.curly--;
969
- }
1284
+ return 1;
1285
+ }
1286
+ });
1287
+ await this.plugins.emitAsync('beforePrepareProgram', programEvent);
1288
+ await this.plugins.emitAsync('prepareProgram', programEvent);
1289
+ const stagingDir = this.getStagingDir();
1290
+ const entries = [];
1291
+ for (const file of files) {
1292
+ const scope = this.getFirstScopeForFile(file);
1293
+ //link the symbol table for all the files in this scope
1294
+ scope === null || scope === void 0 ? void 0 : scope.linkSymbolTable();
1295
+ //if the file doesn't have an editor yet, assign one now
1296
+ if (!file.editor) {
1297
+ file.editor = new Editor_1.Editor();
970
1298
  }
971
- index--;
1299
+ const event = {
1300
+ program: this,
1301
+ file: file,
1302
+ editor: file.editor,
1303
+ scope: scope,
1304
+ outputPath: this.getOutputPath(file, stagingDir)
1305
+ };
1306
+ await this.plugins.emitAsync('beforePrepareFile', event);
1307
+ await this.plugins.emitAsync('prepareFile', event);
1308
+ await this.plugins.emitAsync('afterPrepareFile', event);
1309
+ //TODO remove this in v1
1310
+ entries.push(event);
1311
+ //unlink the symbolTable so the next loop iteration can link theirs
1312
+ scope === null || scope === void 0 ? void 0 : scope.unlinkSymbolTable();
972
1313
  }
973
- return itemCounts;
1314
+ await this.plugins.emitAsync('afterPrepareProgram', programEvent);
1315
+ return files;
974
1316
  }
975
1317
  /**
976
- * @param srcPath The absolute path to the source file on disk
1318
+ * Generate the contents of every file
977
1319
  */
978
- getReferences(srcPath, position) {
979
- //find the file
980
- let file = this.getFile(srcPath);
981
- if (!file) {
982
- return null;
1320
+ async serialize(files) {
1321
+ const allFiles = new Map();
1322
+ //exclude prunable files if that option is enabled
1323
+ if (this.options.pruneEmptyCodeFiles === true) {
1324
+ files = files.filter(x => x.canBePruned !== true);
983
1325
  }
984
- return file.getReferences(position);
985
- }
986
- /**
987
- * Get a list of all script imports, relative to the specified pkgPath
988
- * @param sourcePkgPath - the pkgPath of the source that wants to resolve script imports.
989
- */
990
- getScriptImportCompletions(sourcePkgPath, scriptImport) {
991
- let lowerSourcePkgPath = sourcePkgPath.toLowerCase();
992
- let result = [];
993
- /**
994
- * hashtable to prevent duplicate results
995
- */
996
- let resultPkgPaths = {};
997
- //restrict to only .brs files
998
- for (const key in this.files) {
999
- const file = this.files[key];
1000
- if (
1001
- //is a BrightScript or BrighterScript file
1002
- (file.extension === '.bs' || file.extension === '.brs') &&
1003
- //this file is not the current file
1004
- lowerSourcePkgPath !== file.pkgPath.toLowerCase()) {
1005
- //add the relative path
1006
- let relativePath = util_1.util.getRelativePath(sourcePkgPath, file.pkgPath).replace(/\\/g, '/');
1007
- const lowerPkgPath = file.pkgPath.toLowerCase();
1008
- if (!resultPkgPaths[lowerPkgPath]) {
1009
- resultPkgPaths[lowerPkgPath] = true;
1010
- result.push({
1011
- label: relativePath,
1012
- detail: file.srcPath,
1013
- kind: vscode_languageserver_1.CompletionItemKind.File,
1014
- textEdit: {
1015
- newText: relativePath,
1016
- range: scriptImport.filePathRange
1017
- }
1018
- });
1019
- //add the absolute path
1020
- result.push({
1021
- label: file.pkgPath,
1022
- detail: file.srcPath,
1023
- kind: vscode_languageserver_1.CompletionItemKind.File,
1024
- textEdit: {
1025
- newText: file.pkgPath,
1026
- range: scriptImport.filePathRange
1027
- }
1028
- });
1029
- }
1326
+ const serializeProgramEvent = await this.plugins.emitAsync('beforeSerializeProgram', {
1327
+ program: this,
1328
+ files: files,
1329
+ result: allFiles
1330
+ });
1331
+ await this.plugins.emitAsync('onSerializeProgram', serializeProgramEvent);
1332
+ // serialize each file
1333
+ for (const file of files) {
1334
+ let scope = this.getFirstScopeForFile(file);
1335
+ //if the file doesn't have a scope, create a temporary scope for the file so it can depend on scope-level items
1336
+ if (!scope) {
1337
+ scope = new Scope_1.Scope(`temporary-for-${file.pkgPath}`, this);
1338
+ scope.getAllFiles = () => [file];
1339
+ scope.getOwnFiles = scope.getAllFiles;
1030
1340
  }
1341
+ //link the symbol table for all the files in this scope
1342
+ scope === null || scope === void 0 ? void 0 : scope.linkSymbolTable();
1343
+ const event = {
1344
+ program: this,
1345
+ file: file,
1346
+ scope: scope,
1347
+ result: allFiles
1348
+ };
1349
+ await this.plugins.emitAsync('beforeSerializeFile', event);
1350
+ await this.plugins.emitAsync('serializeFile', event);
1351
+ await this.plugins.emitAsync('afterSerializeFile', event);
1352
+ //unlink the symbolTable so the next loop iteration can link theirs
1353
+ scope === null || scope === void 0 ? void 0 : scope.unlinkSymbolTable();
1031
1354
  }
1032
- return result;
1355
+ this.plugins.emit('afterSerializeProgram', serializeProgramEvent);
1356
+ return allFiles;
1033
1357
  }
1034
1358
  /**
1035
- * Transpile a single file and get the result as a string.
1036
- * This does not write anything to the file system.
1037
- * @param srcPath The absolute path to the source file on disk
1359
+ * Write the entire project to disk
1038
1360
  */
1039
- getTranspiledFileContents(srcPath) {
1040
- let file = this.getFile(srcPath);
1041
- let result = file.transpile();
1042
- return Object.assign(Object.assign({}, result), { srcPath: file.srcPath, pkgPath: file.pkgPath });
1043
- }
1044
- async transpile(fileEntries, stagingFolderPath) {
1045
- // map fileEntries using their path as key to avoid excessive "find()" operations
1046
- const mappedFileEntries = fileEntries.reduce((collection, entry) => {
1047
- collection[util_1.standardizePath `${entry.src}`] = entry;
1048
- return collection;
1049
- }, {});
1050
- const entries = [];
1051
- for (const key in this.files) {
1052
- const file = this.files[key];
1053
- let filePathObj = mappedFileEntries[util_1.standardizePath `${file.srcPath}`];
1054
- if (!filePathObj) {
1055
- //this file has been added in-memory, from a plugin, for example
1056
- filePathObj = {
1057
- //add an interpolated src path (since it doesn't actually exist in memory)
1058
- src: `bsc-in-memory:/${util_1.util.removeProtocol(file.pkgPath)}`,
1059
- dest: file.pkgPath
1060
- };
1061
- }
1062
- //prep the output path
1063
- let outputPath = filePathObj.dest
1064
- //replace any leading protocol
1065
- .replace(/^[-a-z_]+:\//, '')
1066
- //change any .bs file extension to .brs
1067
- .replace(/\.bs$/gi, '.brs');
1068
- //prepend the staging folder path
1069
- outputPath = util_1.standardizePath `${stagingFolderPath}/${outputPath}`;
1070
- entries.push({
1071
- file: file,
1072
- outputPath: outputPath
1073
- });
1074
- }
1075
- this.plugins.emit('beforeProgramTranspile', {
1361
+ async write(stagingDir, files) {
1362
+ const programEvent = await this.plugins.emitAsync('beforeWriteProgram', {
1076
1363
  program: this,
1077
- entries: entries
1364
+ files: files,
1365
+ stagingDir: stagingDir
1078
1366
  });
1079
- const promises = entries.map(async (entry) => {
1080
- //skip transpiling typedef files
1081
- if (reflection_1.isBrsFile(entry.file) && entry.file.isTypedef) {
1082
- return;
1083
- }
1084
- this.plugins.emit('beforeFileTranspile', {
1367
+ //empty the staging directory
1368
+ await fsExtra.emptyDir(stagingDir);
1369
+ const serializedFiles = [...files]
1370
+ .map(([, serializedFiles]) => serializedFiles)
1371
+ .flat();
1372
+ //write all the files to disk (asynchronously)
1373
+ await Promise.all(serializedFiles.map(async (file) => {
1374
+ const event = await this.plugins.emitAsync('beforeWriteFile', {
1085
1375
  program: this,
1086
- file: entry.file,
1087
- outputPath: entry.outputPath
1376
+ file: file,
1377
+ outputPath: this.getOutputPath(file, stagingDir),
1378
+ processedFiles: new Set()
1088
1379
  });
1089
- const { file, outputPath } = entry;
1090
- const result = file.transpile();
1091
- //make sure the full dir path exists
1092
- await fsExtra.ensureDir(path.dirname(outputPath));
1093
- if (await fsExtra.pathExists(outputPath)) {
1094
- throw new Error(`Error while transpiling "${file.srcPath}". A file already exists at "${outputPath}" and will not be overwritten.`);
1095
- }
1096
- const writeMapPromise = result.map ? fsExtra.writeFile(`${outputPath}.map`, result.map.toString()) : null;
1097
- await Promise.all([
1098
- fsExtra.writeFile(outputPath, result.code),
1099
- writeMapPromise
1100
- ]);
1101
- if (reflection_1.isBrsFile(file) && this.options.emitDefinitions) {
1102
- const typedef = file.getTypedef();
1103
- const typedefPath = outputPath.replace(/\.brs$/i, '.d.bs');
1104
- await fsExtra.writeFile(typedefPath, typedef);
1105
- }
1106
- this.plugins.emit('afterFileTranspile', {
1380
+ await this.plugins.emitAsync('writeFile', event);
1381
+ await this.plugins.emitAsync('afterWriteFile', event);
1382
+ }));
1383
+ await this.plugins.emitAsync('afterWriteProgram', programEvent);
1384
+ }
1385
+ /**
1386
+ * Build the project. This transpiles/transforms/copies all files and moves them to the staging directory
1387
+ * @param options the list of options used to build the program
1388
+ */
1389
+ async build(options) {
1390
+ //run a single build at a time
1391
+ await this.buildPipeline.run(async () => {
1392
+ var _a;
1393
+ const stagingDir = this.getStagingDir(options === null || options === void 0 ? void 0 : options.stagingDir);
1394
+ const event = await this.plugins.emitAsync('beforeBuildProgram', {
1107
1395
  program: this,
1108
- file: entry.file,
1109
- outputPath: entry.outputPath
1396
+ editor: this.editor,
1397
+ files: (_a = options === null || options === void 0 ? void 0 : options.files) !== null && _a !== void 0 ? _a : Object.values(this.files)
1110
1398
  });
1111
- });
1112
- //if there's no bslib file already loaded into the program, copy it to the staging directory
1113
- if (!this.getFile(bslibAliasedRokuModulesPkgPath) && !this.getFile(`pkg:/source/bslib.brs`)) {
1114
- promises.push(util_1.util.copyBslibToStaging(stagingFolderPath));
1115
- }
1116
- await Promise.all(promises);
1117
- this.plugins.emit('afterProgramTranspile', {
1118
- program: this,
1119
- entries: entries
1399
+ //prepare the program (and files) for building
1400
+ event.files = await this.prepare(event.files);
1401
+ //stage the entire program
1402
+ const serializedFilesByFile = await this.serialize(event.files);
1403
+ await this.write(stagingDir, serializedFilesByFile);
1404
+ await this.plugins.emitAsync('afterBuildProgram', event);
1405
+ //undo all edits for the program
1406
+ this.editor.undoAll();
1407
+ //undo all edits for each file
1408
+ for (const file of event.files) {
1409
+ file.editor.undoAll();
1410
+ }
1120
1411
  });
1121
1412
  }
1122
1413
  /**
@@ -1127,10 +1418,11 @@ class Program {
1127
1418
  const lowerFunctionName = functionName.toLowerCase();
1128
1419
  //find every file with this function defined
1129
1420
  for (const file of Object.values(this.files)) {
1130
- if (reflection_1.isBrsFile(file)) {
1421
+ if ((0, reflection_1.isBrsFile)(file)) {
1131
1422
  //TODO handle namespace-relative function calls
1132
1423
  //if the file has a function with this name
1133
- if (file.parser.references.functionStatementLookup.get(lowerFunctionName) !== undefined) {
1424
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1425
+ if (file['_cachedLookups'].functionStatementMap.get(lowerFunctionName)) {
1134
1426
  files.push(file);
1135
1427
  }
1136
1428
  }
@@ -1138,55 +1430,142 @@ class Program {
1138
1430
  return files;
1139
1431
  }
1140
1432
  /**
1141
- * Find a list of files in the program that have a function with the given name (case INsensitive)
1433
+ * Find a list of files in the program that have a class with the given name (case INsensitive)
1142
1434
  */
1143
1435
  findFilesForClass(className) {
1144
1436
  const files = [];
1145
1437
  const lowerClassName = className.toLowerCase();
1146
1438
  //find every file with this class defined
1147
1439
  for (const file of Object.values(this.files)) {
1148
- if (reflection_1.isBrsFile(file)) {
1440
+ if ((0, reflection_1.isBrsFile)(file)) {
1149
1441
  //TODO handle namespace-relative classes
1150
1442
  //if the file has a function with this name
1151
- if (file.parser.references.classStatementLookup.get(lowerClassName) !== undefined) {
1443
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1444
+ if (file['_cachedLookups'].classStatementMap.get(lowerClassName) !== undefined) {
1445
+ files.push(file);
1446
+ }
1447
+ }
1448
+ }
1449
+ return files;
1450
+ }
1451
+ findFilesForNamespace(name) {
1452
+ const files = [];
1453
+ const lowerName = name.toLowerCase();
1454
+ //find every file with this class defined
1455
+ for (const file of Object.values(this.files)) {
1456
+ if ((0, reflection_1.isBrsFile)(file)) {
1457
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1458
+ if (file['_cachedLookups'].namespaceStatements.find((x) => {
1459
+ const namespaceName = x.name.toLowerCase();
1460
+ return (
1461
+ //the namespace name matches exactly
1462
+ namespaceName === lowerName ||
1463
+ //the full namespace starts with the name (honoring the part boundary)
1464
+ namespaceName.startsWith(lowerName + '.'));
1465
+ })) {
1466
+ files.push(file);
1467
+ }
1468
+ }
1469
+ }
1470
+ return files;
1471
+ }
1472
+ findFilesForEnum(name) {
1473
+ const files = [];
1474
+ const lowerName = name.toLowerCase();
1475
+ //find every file with this enum defined
1476
+ for (const file of Object.values(this.files)) {
1477
+ if ((0, reflection_1.isBrsFile)(file)) {
1478
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1479
+ if (file['_cachedLookups'].enumStatementMap.get(lowerName)) {
1152
1480
  files.push(file);
1153
1481
  }
1154
1482
  }
1155
1483
  }
1156
1484
  return files;
1157
1485
  }
1486
+ /**
1487
+ * Modify a parsed manifest map by reading `bs_const` and injecting values from `options.manifest.bs_const`
1488
+ * @param parsedManifest The manifest map to read from and modify
1489
+ */
1490
+ buildBsConstsIntoParsedManifest(parsedManifest) {
1491
+ var _a, _b;
1492
+ // Lift the bs_consts defined in the manifest
1493
+ let bsConsts = (0, Manifest_1.getBsConst)(parsedManifest, false);
1494
+ // Override or delete any bs_consts defined in the bs config
1495
+ 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) {
1496
+ const value = this.options.manifest.bs_const[key];
1497
+ if (value === null) {
1498
+ bsConsts.delete(key);
1499
+ }
1500
+ else {
1501
+ bsConsts.set(key, value);
1502
+ }
1503
+ }
1504
+ // convert the new list of bs consts back into a string for the rest of the down stream systems to use
1505
+ let constString = '';
1506
+ for (const [key, value] of bsConsts) {
1507
+ constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
1508
+ }
1509
+ // Set the updated bs_const value
1510
+ parsedManifest.set('bs_const', constString);
1511
+ }
1512
+ /**
1513
+ * Try to find and load the manifest into memory
1514
+ * @param manifestFileObj A pointer to a potential manifest file object found during loading
1515
+ * @param replaceIfAlreadyLoaded should we overwrite the internal `_manifest` if it already exists
1516
+ */
1517
+ loadManifest(manifestFileObj, replaceIfAlreadyLoaded = true) {
1518
+ //if we already have a manifest instance, and should not replace...then don't replace
1519
+ if (!replaceIfAlreadyLoaded && this._manifest) {
1520
+ return;
1521
+ }
1522
+ let manifestPath = manifestFileObj
1523
+ ? manifestFileObj.src
1524
+ : path.join(this.options.rootDir, 'manifest');
1525
+ try {
1526
+ // we only load this manifest once, so do it sync to improve speed downstream
1527
+ const contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1528
+ const parsedManifest = (0, Manifest_1.parseManifest)(contents);
1529
+ this.buildBsConstsIntoParsedManifest(parsedManifest);
1530
+ this._manifest = parsedManifest;
1531
+ }
1532
+ catch (e) {
1533
+ this._manifest = new Map();
1534
+ }
1535
+ }
1158
1536
  /**
1159
1537
  * Get a map of the manifest information
1160
1538
  */
1161
1539
  getManifest() {
1162
1540
  if (!this._manifest) {
1163
- //load the manifest file.
1164
- //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
1165
- let manifestPath = path.join(this.options.rootDir, 'manifest');
1166
- let contents;
1167
- try {
1168
- //we only load this manifest once, so do it sync to improve speed downstream
1169
- contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1170
- this._manifest = Manifest_1.parseManifest(contents);
1171
- }
1172
- catch (err) {
1173
- this._manifest = new Map();
1174
- }
1541
+ this.loadManifest();
1175
1542
  }
1176
1543
  return this._manifest;
1177
1544
  }
1178
1545
  dispose() {
1179
- var _a, _b;
1180
- for (const key in this.files) {
1181
- const file = this.files[key];
1182
- (_a = file.dispose) === null || _a === void 0 ? void 0 : _a.call(file);
1546
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1547
+ this.plugins.emit('beforeProgramDispose', { program: this });
1548
+ for (let filePath in this.files) {
1549
+ (_b = (_a = this.files[filePath]) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
1183
1550
  }
1184
1551
  for (let name in this.scopes) {
1185
- (_b = this.scopes[name]) === null || _b === void 0 ? void 0 : _b.dispose();
1552
+ (_d = (_c = this.scopes[name]) === null || _c === void 0 ? void 0 : _c.dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
1186
1553
  }
1187
- this.globalScope.dispose();
1188
- this.dependencyGraph.dispose();
1554
+ (_f = (_e = this.globalScope) === null || _e === void 0 ? void 0 : _e.dispose) === null || _f === void 0 ? void 0 : _f.call(_e);
1555
+ (_h = (_g = this.dependencyGraph) === null || _g === void 0 ? void 0 : _g.dispose) === null || _h === void 0 ? void 0 : _h.call(_g);
1189
1556
  }
1190
1557
  }
1191
1558
  exports.Program = Program;
1559
+ class ProvideFileEventInternal {
1560
+ constructor(program, srcPath, destPath, data, fileFactory) {
1561
+ var _a;
1562
+ this.program = program;
1563
+ this.srcPath = srcPath;
1564
+ this.destPath = destPath;
1565
+ this.data = data;
1566
+ this.fileFactory = fileFactory;
1567
+ this.files = [];
1568
+ this.srcExtension = (_a = path.extname(srcPath)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
1569
+ }
1570
+ }
1192
1571
  //# sourceMappingURL=Program.js.map