brighterscript 0.66.0-alpha.0 → 0.66.0-alpha.10

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