brighterscript 0.66.0-alpha.1 → 0.66.0-alpha.11

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