brighterscript 0.66.0-alpha.6 → 0.66.0-alpha.8

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 (288) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/README.md +16 -0
  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 +15 -1
  11. package/dist/CommentFlagProcessor.d.ts +4 -3
  12. package/dist/CommentFlagProcessor.js.map +1 -1
  13. package/dist/DiagnosticMessages.d.ts +8 -1
  14. package/dist/DiagnosticMessages.js +30 -13
  15. package/dist/DiagnosticMessages.js.map +1 -1
  16. package/dist/LanguageServer.js +7 -1
  17. package/dist/LanguageServer.js.map +1 -1
  18. package/dist/PluginInterface.d.ts +11 -2
  19. package/dist/PluginInterface.js +69 -10
  20. package/dist/PluginInterface.js.map +1 -1
  21. package/dist/Program.d.ts +107 -38
  22. package/dist/Program.js +502 -270
  23. package/dist/Program.js.map +1 -1
  24. package/dist/ProgramBuilder.d.ts +10 -4
  25. package/dist/ProgramBuilder.js +44 -54
  26. package/dist/ProgramBuilder.js.map +1 -1
  27. package/dist/Scope.d.ts +26 -38
  28. package/dist/Scope.js +153 -174
  29. package/dist/Scope.js.map +1 -1
  30. package/dist/SymbolTable.d.ts +4 -1
  31. package/dist/SymbolTable.js +19 -7
  32. package/dist/SymbolTable.js.map +1 -1
  33. package/dist/XmlScope.d.ts +5 -4
  34. package/dist/XmlScope.js +16 -14
  35. package/dist/XmlScope.js.map +1 -1
  36. package/dist/astUtils/{AstEditor.d.ts → Editor.d.ts} +6 -1
  37. package/dist/astUtils/{AstEditor.js → Editor.js} +9 -3
  38. package/dist/astUtils/Editor.js.map +1 -0
  39. package/dist/astUtils/{AstEditor.spec.js → Editor.spec.js} +10 -6
  40. package/dist/astUtils/Editor.spec.js.map +1 -0
  41. package/dist/astUtils/reflection.d.ts +9 -4
  42. package/dist/astUtils/reflection.js +23 -7
  43. package/dist/astUtils/reflection.js.map +1 -1
  44. package/dist/astUtils/reflection.spec.js +2 -2
  45. package/dist/astUtils/reflection.spec.js.map +1 -1
  46. package/dist/astUtils/visitors.d.ts +14 -3
  47. package/dist/astUtils/visitors.js +22 -2
  48. package/dist/astUtils/visitors.js.map +1 -1
  49. package/dist/astUtils/visitors.spec.js +58 -7
  50. package/dist/astUtils/visitors.spec.js.map +1 -1
  51. package/dist/bscPlugin/BscPlugin.d.ts +10 -2
  52. package/dist/bscPlugin/BscPlugin.js +24 -4
  53. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  54. package/dist/bscPlugin/FileWriter.d.ts +6 -0
  55. package/dist/bscPlugin/FileWriter.js +24 -0
  56. package/dist/bscPlugin/FileWriter.js.map +1 -0
  57. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +8 -8
  58. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  59. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  60. package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +7 -2
  61. package/dist/bscPlugin/completions/CompletionsProcessor.js +112 -44
  62. package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -1
  63. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js +212 -6
  64. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js.map +1 -1
  65. package/dist/bscPlugin/fileProviders/FileProvider.d.ts +9 -0
  66. package/dist/bscPlugin/fileProviders/FileProvider.js +51 -0
  67. package/dist/bscPlugin/fileProviders/FileProvider.js.map +1 -0
  68. package/dist/bscPlugin/hover/HoverProcessor.d.ts +1 -7
  69. package/dist/bscPlugin/hover/HoverProcessor.js +10 -8
  70. package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -1
  71. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +1 -0
  72. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +43 -0
  73. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -1
  74. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js +22 -0
  75. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -1
  76. package/dist/bscPlugin/serialize/BslibInjector.spec.js +19 -0
  77. package/dist/bscPlugin/serialize/BslibInjector.spec.js.map +1 -0
  78. package/dist/bscPlugin/serialize/BslibManager.d.ts +9 -0
  79. package/dist/bscPlugin/serialize/BslibManager.js +40 -0
  80. package/dist/bscPlugin/serialize/BslibManager.js.map +1 -0
  81. package/dist/bscPlugin/serialize/FileSerializer.d.ts +9 -0
  82. package/dist/bscPlugin/serialize/FileSerializer.js +72 -0
  83. package/dist/bscPlugin/serialize/FileSerializer.js.map +1 -0
  84. package/dist/bscPlugin/transpile/{BrsFilePreTranspileProcessor.d.ts → BrsFileTranspileProcessor.d.ts} +4 -2
  85. package/dist/bscPlugin/transpile/{BrsFilePreTranspileProcessor.js → BrsFileTranspileProcessor.js} +29 -5
  86. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +1 -0
  87. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.d.ts +1 -0
  88. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js +41 -0
  89. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js.map +1 -0
  90. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.d.ts +2 -2
  91. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js.map +1 -1
  92. package/dist/bscPlugin/validation/BrsFileValidator.js +8 -3
  93. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -1
  94. package/dist/bscPlugin/validation/BrsFileValidator.spec.js +1 -1
  95. package/dist/bscPlugin/validation/BrsFileValidator.spec.js.map +1 -1
  96. package/dist/bscPlugin/validation/ScopeValidator.d.ts +5 -9
  97. package/dist/bscPlugin/validation/ScopeValidator.js +214 -222
  98. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -1
  99. package/dist/bscPlugin/validation/ScopeValidator.spec.js +669 -0
  100. package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +1 -1
  101. package/dist/bscPlugin/validation/XmlFileValidator.js +2 -2
  102. package/dist/bscPlugin/validation/XmlFileValidator.js.map +1 -1
  103. package/dist/cli.js +1 -0
  104. package/dist/cli.js.map +1 -1
  105. package/dist/deferred.d.ts +2 -2
  106. package/dist/deferred.js.map +1 -1
  107. package/dist/diagnosticUtils.d.ts +1 -0
  108. package/dist/diagnosticUtils.js +4 -3
  109. package/dist/diagnosticUtils.js.map +1 -1
  110. package/dist/examples/plugins/removePrint.js +1 -1
  111. package/dist/examples/plugins/removePrint.js.map +1 -1
  112. package/dist/files/AssetFile.d.ts +26 -0
  113. package/dist/files/AssetFile.js +26 -0
  114. package/dist/files/AssetFile.js.map +1 -0
  115. package/dist/files/BrsFile.Class.spec.js +241 -40
  116. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  117. package/dist/files/BrsFile.d.ts +66 -16
  118. package/dist/files/BrsFile.js +330 -80
  119. package/dist/files/BrsFile.js.map +1 -1
  120. package/dist/files/BrsFile.spec.js +1134 -167
  121. package/dist/files/BrsFile.spec.js.map +1 -1
  122. package/dist/files/Factory.d.ts +25 -0
  123. package/dist/files/Factory.js +22 -0
  124. package/dist/files/Factory.js.map +1 -0
  125. package/dist/files/File.d.ts +106 -0
  126. package/dist/files/File.js +16 -0
  127. package/dist/files/File.js.map +1 -0
  128. package/dist/files/LazyFileData.d.ts +20 -0
  129. package/dist/files/LazyFileData.js +54 -0
  130. package/dist/files/LazyFileData.js.map +1 -0
  131. package/dist/files/LazyFileData.spec.d.ts +1 -0
  132. package/dist/files/LazyFileData.spec.js +27 -0
  133. package/dist/files/LazyFileData.spec.js.map +1 -0
  134. package/dist/files/XmlFile.d.ts +55 -17
  135. package/dist/files/XmlFile.js +88 -47
  136. package/dist/files/XmlFile.js.map +1 -1
  137. package/dist/files/XmlFile.spec.js +64 -57
  138. package/dist/files/XmlFile.spec.js.map +1 -1
  139. package/dist/files/tests/imports.spec.js +21 -8
  140. package/dist/files/tests/imports.spec.js.map +1 -1
  141. package/dist/files/tests/optionalChaning.spec.js +14 -14
  142. package/dist/files/tests/optionalChaning.spec.js.map +1 -1
  143. package/dist/globalCallables.js +1 -1
  144. package/dist/globalCallables.js.map +1 -1
  145. package/dist/index.d.ts +6 -1
  146. package/dist/index.js +6 -1
  147. package/dist/index.js.map +1 -1
  148. package/dist/interfaces.d.ts +357 -89
  149. package/dist/interfaces.js +10 -2
  150. package/dist/interfaces.js.map +1 -1
  151. package/dist/lexer/Lexer.js +1 -1
  152. package/dist/lexer/TokenKind.d.ts +1 -0
  153. package/dist/lexer/TokenKind.js +4 -1
  154. package/dist/lexer/TokenKind.js.map +1 -1
  155. package/dist/parser/AstNode.d.ts +2 -2
  156. package/dist/parser/AstNode.js +1 -1
  157. package/dist/parser/AstNode.js.map +1 -1
  158. package/dist/parser/BrsTranspileState.d.ts +3 -2
  159. package/dist/parser/BrsTranspileState.js +3 -2
  160. package/dist/parser/BrsTranspileState.js.map +1 -1
  161. package/dist/parser/Expression.d.ts +2 -2
  162. package/dist/parser/Expression.js +23 -19
  163. package/dist/parser/Expression.js.map +1 -1
  164. package/dist/parser/Parser.Class.spec.js +103 -0
  165. package/dist/parser/Parser.Class.spec.js.map +1 -1
  166. package/dist/parser/Parser.js +61 -13
  167. package/dist/parser/Parser.js.map +1 -1
  168. package/dist/parser/Parser.spec.js +227 -1
  169. package/dist/parser/Parser.spec.js.map +1 -1
  170. package/dist/parser/SGParser.d.ts +2 -2
  171. package/dist/parser/SGParser.js +3 -3
  172. package/dist/parser/SGParser.js.map +1 -1
  173. package/dist/parser/SGParser.spec.js +2 -2
  174. package/dist/parser/SGParser.spec.js.map +1 -1
  175. package/dist/parser/SGTypes.d.ts +1 -1
  176. package/dist/parser/Statement.d.ts +12 -5
  177. package/dist/parser/Statement.js +56 -26
  178. package/dist/parser/Statement.js.map +1 -1
  179. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +16 -16
  180. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  181. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +10 -10
  182. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  183. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js +24 -24
  184. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js.map +1 -1
  185. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +64 -36
  186. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  187. package/dist/parser/tests/expression/TernaryExpression.spec.js +34 -34
  188. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  189. package/dist/parser/tests/expression/UnaryExpression.spec.d.ts +1 -0
  190. package/dist/parser/tests/expression/UnaryExpression.spec.js +52 -0
  191. package/dist/parser/tests/expression/UnaryExpression.spec.js.map +1 -0
  192. package/dist/parser/tests/statement/ConstStatement.spec.js +90 -16
  193. package/dist/parser/tests/statement/ConstStatement.spec.js.map +1 -1
  194. package/dist/parser/tests/statement/Continue.spec.js +2 -2
  195. package/dist/parser/tests/statement/Continue.spec.js.map +1 -1
  196. package/dist/parser/tests/statement/Enum.spec.js +35 -26
  197. package/dist/parser/tests/statement/Enum.spec.js.map +1 -1
  198. package/dist/parser/tests/statement/For.spec.js +6 -6
  199. package/dist/parser/tests/statement/For.spec.js.map +1 -1
  200. package/dist/parser/tests/statement/ForEach.spec.js +4 -4
  201. package/dist/parser/tests/statement/ForEach.spec.js.map +1 -1
  202. package/dist/parser/tests/statement/InterfaceStatement.spec.js +20 -12
  203. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  204. package/dist/parser/tests/statement/PrintStatement.spec.js +10 -10
  205. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  206. package/dist/preprocessor/Manifest.d.ts +1 -1
  207. package/dist/preprocessor/Manifest.js +2 -2
  208. package/dist/preprocessor/Manifest.js.map +1 -1
  209. package/dist/roku-types/data.json +98 -193
  210. package/dist/roku-types/index.d.ts +15 -11
  211. package/dist/types/ArrayType.d.ts +1 -1
  212. package/dist/types/ArrayType.js +4 -0
  213. package/dist/types/ArrayType.js.map +1 -1
  214. package/dist/types/ArrayType.spec.js +1 -1
  215. package/dist/types/ArrayType.spec.js.map +1 -1
  216. package/dist/types/AssociativeArrayType.d.ts +1 -1
  217. package/dist/types/AssociativeArrayType.js +1 -1
  218. package/dist/types/AssociativeArrayType.js.map +1 -1
  219. package/dist/types/BooleanType.d.ts +1 -1
  220. package/dist/types/BooleanType.js +2 -1
  221. package/dist/types/BooleanType.js.map +1 -1
  222. package/dist/types/BscType.d.ts +2 -2
  223. package/dist/types/BscType.js +30 -9
  224. package/dist/types/BscType.js.map +1 -1
  225. package/dist/types/BuiltInInterfaceAdder.d.ts +3 -0
  226. package/dist/types/BuiltInInterfaceAdder.js +37 -16
  227. package/dist/types/BuiltInInterfaceAdder.js.map +1 -1
  228. package/dist/types/BuiltInInterfaceAdder.spec.js +7 -0
  229. package/dist/types/BuiltInInterfaceAdder.spec.js.map +1 -1
  230. package/dist/types/ClassType.d.ts +4 -3
  231. package/dist/types/ClassType.js +6 -3
  232. package/dist/types/ClassType.js.map +1 -1
  233. package/dist/types/ClassType.spec.js +5 -3
  234. package/dist/types/ClassType.spec.js.map +1 -1
  235. package/dist/types/ComponentType.d.ts +1 -1
  236. package/dist/types/ComponentType.js +3 -0
  237. package/dist/types/ComponentType.js.map +1 -1
  238. package/dist/types/DoubleType.js +3 -1
  239. package/dist/types/DoubleType.js.map +1 -1
  240. package/dist/types/EnumType.d.ts +1 -1
  241. package/dist/types/EnumType.js +7 -2
  242. package/dist/types/EnumType.js.map +1 -1
  243. package/dist/types/FloatType.js +3 -1
  244. package/dist/types/FloatType.js.map +1 -1
  245. package/dist/types/InheritableType.d.ts +7 -4
  246. package/dist/types/InheritableType.js +67 -3
  247. package/dist/types/InheritableType.js.map +1 -1
  248. package/dist/types/IntegerType.js +3 -1
  249. package/dist/types/IntegerType.js.map +1 -1
  250. package/dist/types/InterfaceType.d.ts +5 -4
  251. package/dist/types/InterfaceType.js +5 -12
  252. package/dist/types/InterfaceType.js.map +1 -1
  253. package/dist/types/InterfaceType.spec.js +23 -0
  254. package/dist/types/InterfaceType.spec.js.map +1 -1
  255. package/dist/types/LongIntegerType.js +3 -1
  256. package/dist/types/LongIntegerType.js.map +1 -1
  257. package/dist/types/NamespaceType.d.ts +2 -1
  258. package/dist/types/NamespaceType.js +3 -0
  259. package/dist/types/NamespaceType.js.map +1 -1
  260. package/dist/types/ObjectType.d.ts +1 -1
  261. package/dist/types/ReferenceType.js +40 -6
  262. package/dist/types/ReferenceType.js.map +1 -1
  263. package/dist/types/StringType.js +2 -2
  264. package/dist/types/StringType.js.map +1 -1
  265. package/dist/types/TypedFunctionType.d.ts +6 -1
  266. package/dist/types/TypedFunctionType.js +46 -16
  267. package/dist/types/TypedFunctionType.js.map +1 -1
  268. package/dist/types/TypedFunctionType.spec.js +99 -0
  269. package/dist/types/TypedFunctionType.spec.js.map +1 -1
  270. package/dist/types/UnionType.js +8 -0
  271. package/dist/types/UnionType.js.map +1 -1
  272. package/dist/types/helper.spec.js +15 -0
  273. package/dist/types/helper.spec.js.map +1 -1
  274. package/dist/types/helpers.d.ts +3 -0
  275. package/dist/types/helpers.js +33 -1
  276. package/dist/types/helpers.js.map +1 -1
  277. package/dist/util.d.ts +25 -9
  278. package/dist/util.js +165 -72
  279. package/dist/util.js.map +1 -1
  280. package/dist/validators/ClassValidator.js.map +1 -1
  281. package/package.json +2 -2
  282. package/dist/astUtils/AstEditor.js.map +0 -1
  283. package/dist/astUtils/AstEditor.spec.js.map +0 -1
  284. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js.map +0 -1
  285. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.spec.js +0 -31
  286. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.spec.js.map +0 -1
  287. /package/dist/astUtils/{AstEditor.spec.d.ts → Editor.spec.d.ts} +0 -0
  288. /package/dist/bscPlugin/{transpile/BrsFilePreTranspileProcessor.spec.d.ts → serialize/BslibInjector.spec.d.ts} +0 -0
package/dist/Program.js CHANGED
@@ -6,8 +6,6 @@ const fsExtra = require("fs-extra");
6
6
  const path = require("path");
7
7
  const Scope_1 = require("./Scope");
8
8
  const DiagnosticMessages_1 = require("./DiagnosticMessages");
9
- const BrsFile_1 = require("./files/BrsFile");
10
- const XmlFile_1 = require("./files/XmlFile");
11
9
  const util_1 = require("./util");
12
10
  const XmlScope_1 = require("./XmlScope");
13
11
  const DiagnosticFilterer_1 = require("./DiagnosticFilterer");
@@ -20,8 +18,7 @@ const vscode_uri_1 = require("vscode-uri");
20
18
  const PluginInterface_1 = require("./PluginInterface");
21
19
  const reflection_1 = require("./astUtils/reflection");
22
20
  const BscPlugin_1 = require("./bscPlugin/BscPlugin");
23
- const AstEditor_1 = require("./astUtils/AstEditor");
24
- const roku_deploy_1 = require("roku-deploy");
21
+ const Editor_1 = require("./astUtils/Editor");
25
22
  const CallExpressionInfo_1 = require("./bscPlugin/CallExpressionInfo");
26
23
  const SignatureHelpUtil_1 = require("./bscPlugin/SignatureHelpUtil");
27
24
  const DiagnosticSeverityAdjuster_1 = require("./DiagnosticSeverityAdjuster");
@@ -36,11 +33,14 @@ const LongIntegerType_1 = require("./types/LongIntegerType");
36
33
  const ObjectType_1 = require("./types/ObjectType");
37
34
  const VoidType_1 = require("./types/VoidType");
38
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");
39
40
  const roku_types_1 = require("./roku-types");
40
41
  const ComponentType_1 = require("./types/ComponentType");
41
42
  const types_1 = require("./types");
42
43
  const BuiltInInterfaceAdder_1 = require("./types/BuiltInInterfaceAdder");
43
- const startOfSourcePkgPath = `source${path.sep}`;
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,6 +50,10 @@ class Program {
50
50
  */
51
51
  options, logger, plugins) {
52
52
  this.options = options;
53
+ /**
54
+ * An editor that plugins can use to modify program-level things during the build flow. Don't use this to edit files (they have their own `.editor`)
55
+ */
56
+ this.editor = new Editor_1.Editor();
53
57
  /**
54
58
  * A graph of all files and their dependencies.
55
59
  * For example:
@@ -64,11 +68,21 @@ class Program {
64
68
  * Should only be set from `this.validate()`
65
69
  */
66
70
  this.diagnostics = [];
71
+ this.fileSymbolInformation = new Map();
67
72
  /**
68
73
  * A map of every file loaded into this program, indexed by its original file location
69
74
  */
70
75
  this.files = {};
71
- 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();
72
86
  this.scopes = {};
73
87
  /**
74
88
  * A map of every component currently loaded into the program, indexed by the component name.
@@ -81,6 +95,9 @@ class Program {
81
95
  * Keeps a set of all the components that need to have their types updated during the current validation cycle
82
96
  */
83
97
  this.componentSymbolsToUpdate = new Set();
98
+ this.lastValidationInfo = new Map();
99
+ this.getTranspiledFileContentsPipeline = new ActionPipeline_1.ActionPipeline();
100
+ this.buildPipeline = new ActionPipeline_1.ActionPipeline();
84
101
  this.options = util_1.util.normalizeConfig(options);
85
102
  this.logger = logger || new Logger_1.Logger(options.logLevel);
86
103
  this.plugins = plugins || new PluginInterface_1.default([], { logger: this.logger });
@@ -89,6 +106,7 @@ class Program {
89
106
  //normalize the root dir path
90
107
  this.options.rootDir = util_1.util.getRootDir(this.options);
91
108
  this.createGlobalScope();
109
+ this.fileFactory = new Factory_1.FileFactory(this);
92
110
  }
93
111
  createGlobalScope() {
94
112
  //create the 'global' scope
@@ -98,6 +116,7 @@ class Program {
98
116
  this.populateGlobalSymbolTable();
99
117
  //hardcode the files list for global scope to only contain the global file
100
118
  this.globalScope.getAllFiles = () => [globalCallables_1.globalFile];
119
+ globalCallables_1.globalFile.isValidated = true;
101
120
  this.globalScope.validate();
102
121
  //for now, disable validation of global scope because the global files have some duplicate method declarations
103
122
  this.globalScope.getDiagnostics = () => [];
@@ -126,7 +145,7 @@ class Program {
126
145
  this.globalScope.symbolTable.addSymbol(nodeName, { description: nodeData.description }, nodeType, SymbolTable_1.SymbolTypeFlag.typetime);
127
146
  }
128
147
  else {
129
- nodeType = this.globalScope.symbolTable.getSymbolType(nodeData.name, { flags: SymbolTable_1.SymbolTypeFlag.typetime });
148
+ nodeType = this.globalScope.symbolTable.getSymbolType(nodeName, { flags: SymbolTable_1.SymbolTypeFlag.typetime });
130
149
  }
131
150
  return nodeType;
132
151
  }
@@ -168,6 +187,15 @@ class Program {
168
187
  this.recursivelyAddNodeToSymbolTable(nodeData);
169
188
  }
170
189
  }
190
+ addFileSymbolInfo(file) {
191
+ this.fileSymbolInformation.set(file.pkgPath, {
192
+ provides: file.providedSymbols,
193
+ requires: file.requiredSymbols
194
+ });
195
+ }
196
+ getFileSymbolInfo(file) {
197
+ return this.fileSymbolInformation.get(file.pkgPath);
198
+ }
171
199
  /**
172
200
  * The path to bslib.brs (the BrightScript runtime for certain BrighterScript features)
173
201
  */
@@ -182,7 +210,7 @@ class Program {
182
210
  //default to the embedded version
183
211
  }
184
212
  else {
185
- return `source${path.sep}bslib.brs`;
213
+ return `${this.options.bslibDestinationDir}${path.sep}bslib.brs`;
186
214
  }
187
215
  }
188
216
  get bslibPrefix() {
@@ -203,7 +231,7 @@ class Program {
203
231
  var _a;
204
232
  if (componentName) {
205
233
  //return the first compoment in the list with this name
206
- //(components are ordered in this list by pkgPath to ensure consistency)
234
+ //(components are ordered in this list by destPath to ensure consistency)
207
235
  return (_a = this.components[componentName.toLowerCase()]) === null || _a === void 0 ? void 0 : _a[0];
208
236
  }
209
237
  else {
@@ -223,8 +251,8 @@ class Program {
223
251
  scope: scope
224
252
  });
225
253
  this.components[key].sort((a, b) => {
226
- const pathA = a.file.pkgPath.toLowerCase();
227
- const pathB = b.file.pkgPath.toLowerCase();
254
+ const pathA = a.file.destPath.toLowerCase();
255
+ const pathB = b.file.destPath.toLowerCase();
228
256
  if (pathA < pathB) {
229
257
  return -1;
230
258
  }
@@ -300,6 +328,7 @@ class Program {
300
328
  //attach (or re-attach) the dependencyGraph for every component whose position changed
301
329
  if (file.dependencyGraphIndex !== i) {
302
330
  file.dependencyGraphIndex = i;
331
+ this.dependencyGraph.addOrReplace(file.dependencyGraphKey, file.dependencies);
303
332
  file.attachDependencyGraph(this.dependencyGraph);
304
333
  scope.attachDependencyGraph(this.dependencyGraph);
305
334
  }
@@ -336,7 +365,7 @@ class Program {
336
365
  //get the diagnostics from all unreferenced files
337
366
  let unreferencedFiles = this.getUnreferencedFiles();
338
367
  for (let file of unreferencedFiles) {
339
- diagnostics.push(...file.getDiagnostics());
368
+ diagnostics.push(...file.diagnostics);
340
369
  }
341
370
  const filteredDiagnostics = this.logger.time(Logger_1.LogLevel.debug, ['filter diagnostics'], () => {
342
371
  //filter out diagnostics based on our diagnostic filters
@@ -361,11 +390,9 @@ class Program {
361
390
  hasFile(filePath, normalizePath = true) {
362
391
  return !!this.getFile(filePath, normalizePath);
363
392
  }
364
- getPkgPath(...args) {
365
- throw new Error('Not implemented');
366
- }
367
393
  /**
368
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"`
369
396
  */
370
397
  getScopeByName(scopeName) {
371
398
  if (!scopeName) {
@@ -394,8 +421,14 @@ class Program {
394
421
  * Update internal maps with this file reference
395
422
  */
396
423
  assignFile(file) {
424
+ const fileAddEvent = {
425
+ file: file,
426
+ program: this
427
+ };
428
+ this.plugins.emit('beforeFileAdd', fileAddEvent);
397
429
  this.files[file.srcPath.toLowerCase()] = file;
398
- this.pkgMap[file.pkgPath.toLowerCase()] = file;
430
+ this.destMap.set(file.destPath.toLowerCase(), file);
431
+ this.plugins.emit('afterFileAdd', fileAddEvent);
399
432
  return file;
400
433
  }
401
434
  /**
@@ -403,104 +436,102 @@ class Program {
403
436
  */
404
437
  unassignFile(file) {
405
438
  delete this.files[file.srcPath.toLowerCase()];
406
- delete this.pkgMap[file.pkgPath.toLowerCase()];
439
+ this.destMap.delete(file.destPath.toLowerCase());
407
440
  return file;
408
441
  }
409
- setFile(fileParam, fileContents) {
442
+ setFile(fileParam, fileData) {
410
443
  //normalize the file paths
411
- const { srcPath, pkgPath } = this.getPaths(fileParam, this.options.rootDir);
444
+ const { srcPath, destPath } = this.getPaths(fileParam, this.options.rootDir);
412
445
  let file = this.logger.time(Logger_1.LogLevel.debug, ['Program.setFile()', chalk_1.default.green(srcPath)], () => {
446
+ var _a, _b, _c;
413
447
  //if the file is already loaded, remove it
414
448
  if (this.hasFile(srcPath)) {
415
- this.removeFile(srcPath);
449
+ this.removeFile(srcPath, true, true);
416
450
  }
417
- let fileExtension = path.extname(srcPath).toLowerCase();
418
- let file;
419
- if (fileExtension === '.brs' || fileExtension === '.bs') {
420
- //add the file to the program
421
- const brsFile = this.assignFile(new BrsFile_1.BrsFile(srcPath, pkgPath, this));
422
- //add file to the `source` dependency list
423
- if (brsFile.pkgPath.startsWith(startOfSourcePkgPath)) {
424
- this.createSourceScope();
425
- this.dependencyGraph.addDependency('scope:source', brsFile.dependencyGraphKey);
426
- }
427
- let beforeFileParseEvent = {
428
- program: this,
429
- srcPath: srcPath,
430
- source: fileContents
431
- };
432
- this.plugins.emit('beforeFileParse', beforeFileParseEvent);
433
- this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
434
- brsFile.parse(beforeFileParseEvent.source);
435
- });
436
- //notify plugins that this file has finished parsing
437
- this.plugins.emit('afterFileParse', {
438
- program: this,
439
- file: brsFile
440
- });
441
- file = brsFile;
442
- 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
+ }));
443
464
  }
444
- else if (
445
- //is xml file
446
- fileExtension === '.xml' &&
447
- //resides in the components folder (Roku will only parse xml files in the components folder)
448
- pkgPath.toLowerCase().startsWith(util_1.util.pathSepNormalize(`components/`))) {
449
- //add the file to the program
450
- const xmlFile = this.assignFile(new XmlFile_1.XmlFile(srcPath, pkgPath, this));
451
- let event = {
452
- program: this,
453
- srcPath: srcPath,
454
- source: fileContents
455
- };
456
- this.plugins.emit('beforeFileParse', event);
457
- this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
458
- xmlFile.parse(event.source);
459
- });
460
- //notify plugins that this file has finished parsing
461
- this.plugins.emit('afterFileParse', {
462
- program: this,
463
- file: xmlFile
464
- });
465
- file = xmlFile;
466
- //create a new scope for this xml file
467
- let scope = new XmlScope_1.XmlScope(xmlFile, this);
468
- this.addScope(scope);
469
- //register this component now that we have parsed it and know its component name
470
- this.registerComponent(xmlFile, scope);
471
- //notify plugins that the scope is created and the component is registered
472
- this.plugins.emit('afterScopeCreate', {
473
- program: this,
474
- scope: scope
475
- });
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
+ })))}`);
476
473
  }
477
- else {
478
- //TODO do we actually need to implement this? Figure out how to handle img paths
479
- // let genericFile = this.files[srcPath] = <any>{
480
- // srcPath: srcPath,
481
- // pkgPath: pkgPath,
482
- // wasProcessed: true
483
- // } as File;
484
- // 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
+ }
485
516
  }
486
- return file;
517
+ return primaryFile;
487
518
  });
488
519
  return file;
489
520
  }
490
521
  /**
491
- * 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.
492
523
  * @param fileParam an object representing file paths
493
524
  * @param rootDir must be a pre-normalized path
494
525
  */
495
526
  getPaths(fileParam, rootDir) {
496
527
  let srcPath;
497
- let pkgPath;
528
+ let destPath;
498
529
  assert.ok(fileParam, 'fileParam is required');
499
- //lift the srcPath and pkgPath vars from the incoming param
530
+ //lift the path vars from the incoming param
500
531
  if (typeof fileParam === 'string') {
501
532
  fileParam = this.removePkgPrefix(fileParam);
502
533
  srcPath = (0, util_1.standardizePath) `${path.resolve(rootDir, fileParam)}`;
503
- pkgPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
534
+ destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
504
535
  }
505
536
  else {
506
537
  let param = fileParam;
@@ -511,30 +542,30 @@ class Program {
511
542
  srcPath = (0, util_1.standardizePath) `${param.srcPath}`;
512
543
  }
513
544
  if (param.dest) {
514
- pkgPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.dest)}`;
545
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.dest)}`;
515
546
  }
516
547
  if (param.pkgPath) {
517
- pkgPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.pkgPath)}`;
548
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.pkgPath)}`;
518
549
  }
519
550
  }
520
- //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
521
552
  if (!srcPath) {
522
- srcPath = (0, util_1.standardizePath) `${rootDir}/${pkgPath}`;
553
+ srcPath = (0, util_1.standardizePath) `${rootDir}/${destPath}`;
523
554
  }
524
555
  //coerce srcPath to an absolute path
525
556
  if (!path.isAbsolute(srcPath)) {
526
557
  srcPath = util_1.util.standardizePath(srcPath);
527
558
  }
528
- //if there's no pkgPath, compute relative path from rootDir
529
- if (!pkgPath) {
530
- 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, '')}`;
531
562
  }
532
563
  assert.ok(srcPath, 'fileEntry.src is required');
533
- assert.ok(pkgPath, 'fileEntry.dest is required');
564
+ assert.ok(destPath, 'fileEntry.dest is required');
534
565
  return {
535
566
  srcPath: srcPath,
536
- //remove leading slash from pkgPath
537
- pkgPath: pkgPath.replace(/^[\/\\]+/, '')
567
+ //remove leading slash
568
+ destPath: destPath.replace(/^[\/\\]+/, '')
538
569
  };
539
570
  }
540
571
  /**
@@ -543,6 +574,18 @@ class Program {
543
574
  removePkgPrefix(path) {
544
575
  return path.replace(/^pkg:\//i, '');
545
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
+ }
546
589
  /**
547
590
  * Ensure source scope is created.
548
591
  * Note: automatically called internally, and no-op if it exists already.
@@ -570,46 +613,58 @@ class Program {
570
613
  }
571
614
  /**
572
615
  * Remove a file from the program
573
- * @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:/`
574
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
575
618
  */
576
- removeFile(filePath, normalizePath = true) {
619
+ removeFile(filePath, normalizePath = true, keepSymbolInformation = false) {
620
+ var _a, _b, _c, _d;
577
621
  this.logger.debug('Program.removeFile()', filePath);
578
- let file = this.getFile(filePath, normalizePath);
579
- if (file) {
580
- const fileDisposeEvent = {
581
- program: this,
582
- file: file
583
- };
584
- this.plugins.emit('beforeFileDispose', fileDisposeEvent);
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);
585
632
  //if there is a scope named the same as this file's path, remove it (i.e. xml scopes)
586
- let scope = this.scopes[file.pkgPath];
633
+ let scope = this.scopes[file.destPath];
587
634
  if (scope) {
588
635
  const scopeDisposeEvent = {
589
636
  program: this,
590
637
  scope: scope
591
638
  };
592
639
  this.plugins.emit('beforeScopeDispose', scopeDisposeEvent);
640
+ this.plugins.emit('onScopeDispose', scopeDisposeEvent);
593
641
  scope.dispose();
594
642
  //notify dependencies of this scope that it has been removed
595
643
  this.dependencyGraph.remove(scope.dependencyGraphKey);
596
- delete this.scopes[file.pkgPath];
644
+ delete this.scopes[file.destPath];
597
645
  this.plugins.emit('afterScopeDispose', scopeDisposeEvent);
598
646
  }
599
647
  //remove the file from the program
600
648
  this.unassignFile(file);
601
649
  this.dependencyGraph.remove(file.dependencyGraphKey);
602
650
  //if this is a pkg:/source file, notify the `source` scope that it has changed
603
- if (file.pkgPath.startsWith(startOfSourcePkgPath)) {
651
+ if (this.isSourceBrsFile(file)) {
604
652
  this.dependencyGraph.removeDependency('scope:source', file.dependencyGraphKey);
653
+ if (!keepSymbolInformation) {
654
+ this.fileSymbolInformation.delete(file.pkgPath);
655
+ }
605
656
  }
606
657
  //if this is a component, remove it from our components map
607
658
  if ((0, reflection_1.isXmlFile)(file)) {
608
659
  this.unregisterComponent(file);
609
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
+ }
610
665
  //dispose file
611
- file === null || file === void 0 ? void 0 : file.dispose();
612
- this.plugins.emit('afterFileDispose', fileDisposeEvent);
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);
613
668
  }
614
669
  }
615
670
  /**
@@ -617,12 +672,15 @@ class Program {
617
672
  */
618
673
  validate() {
619
674
  this.logger.time(Logger_1.LogLevel.log, ['Validating project'], () => {
675
+ var _a, _b, _c, _d, _e, _f, _g;
620
676
  this.diagnostics = [];
621
677
  const programValidateEvent = {
622
678
  program: this
623
679
  };
624
680
  this.plugins.emit('beforeProgramValidate', programValidateEvent);
681
+ this.plugins.emit('onProgramValidate', programValidateEvent);
625
682
  //validate every file
683
+ const brsFilesValidated = [];
626
684
  for (const file of Object.values(this.files)) {
627
685
  //for every unvalidated file, validate it
628
686
  if (!file.isValidated) {
@@ -634,9 +692,80 @@ class Program {
634
692
  //emit an event to allow plugins to contribute to the file validation process
635
693
  this.plugins.emit('onFileValidate', validateFileEvent);
636
694
  file.isValidated = true;
695
+ if ((0, reflection_1.isBrsFile)(file)) {
696
+ brsFilesValidated.push(file);
697
+ }
637
698
  this.plugins.emit('afterFileValidate', validateFileEvent);
638
699
  }
639
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();
640
769
  // Build component types for any component that changes
641
770
  this.logger.time(Logger_1.LogLevel.info, ['Build component types'], () => {
642
771
  for (let { componentKey, componentName } of this.componentSymbolsToUpdate) {
@@ -644,16 +773,37 @@ class Program {
644
773
  }
645
774
  this.componentSymbolsToUpdate.clear();
646
775
  });
776
+ const changedSymbolsMapArr = brsFilesValidated === null || brsFilesValidated === void 0 ? void 0 : brsFilesValidated.map(f => {
777
+ if ((0, reflection_1.isBrsFile)(f)) {
778
+ return f.providedSymbols.changes;
779
+ }
780
+ return null;
781
+ }).filter(x => x);
782
+ const changedSymbols = new Map();
783
+ for (const flag of [SymbolTable_1.SymbolTypeFlag.runtime, SymbolTable_1.SymbolTypeFlag.typetime]) {
784
+ const changedSymbolsSetArr = changedSymbolsMapArr.map(symMap => symMap.get(flag));
785
+ changedSymbols.set(flag, new Set(...changedSymbolsSetArr));
786
+ }
647
787
  this.logger.time(Logger_1.LogLevel.info, ['Validate all scopes'], () => {
648
788
  for (let scopeName in this.scopes) {
649
789
  let scope = this.scopes[scopeName];
650
- scope.validate();
790
+ scope.validate({ changedFiles: brsFilesValidated, changedSymbols: changedSymbols });
651
791
  }
652
792
  });
653
793
  this.detectDuplicateComponentNames();
654
794
  this.plugins.emit('afterProgramValidate', programValidateEvent);
655
795
  });
656
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
+ }
657
807
  /**
658
808
  * Flag all duplicate component names
659
809
  */
@@ -706,12 +856,13 @@ class Program {
706
856
  getFile(filePath, normalizePath = true) {
707
857
  if (typeof filePath !== 'string') {
708
858
  return undefined;
859
+ //is the path absolute (or the `virtual:` prefix)
709
860
  }
710
- else if (path.isAbsolute(filePath)) {
861
+ else if (/^(?:(?:virtual:[\/\\])|(?:\w:)|(?:[\/\\]))/gmi.exec(filePath)) {
711
862
  return this.files[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
712
863
  }
713
864
  else {
714
- return this.pkgMap[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
865
+ return this.destMap.get((normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase());
715
866
  }
716
867
  }
717
868
  /**
@@ -753,7 +904,8 @@ class Program {
753
904
  //look through all files in scope for matches
754
905
  for (const scope of this.getScopesForFile(originFile)) {
755
906
  for (const file of scope.getAllFiles()) {
756
- 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)) {
757
909
  continue;
758
910
  }
759
911
  filesSearched.add(file);
@@ -786,7 +938,8 @@ class Program {
786
938
  }
787
939
  //look through all files in scope for matches
788
940
  for (const file of scope.getOwnFiles()) {
789
- 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)) {
790
943
  continue;
791
944
  }
792
945
  filesSearched.add(file);
@@ -935,10 +1088,12 @@ class Program {
935
1088
  getReferences(srcPath, position) {
936
1089
  //find the file
937
1090
  let file = this.getFile(srcPath);
938
- if (!file) {
1091
+ if ((0, reflection_1.isBrsFile)(file) || (0, reflection_1.isXmlFile)(file)) {
1092
+ return file.getReferences(position);
1093
+ }
1094
+ else {
939
1095
  return null;
940
1096
  }
941
- return file.getReferences(position);
942
1097
  }
943
1098
  /**
944
1099
  * Transpile a single file and get the result as a string.
@@ -949,168 +1104,211 @@ class Program {
949
1104
  * @param filePath can be a srcPath or a destPath
950
1105
  */
951
1106
  async getTranspiledFileContents(filePath) {
952
- let fileMap = await roku_deploy_1.rokuDeploy.getFilePaths(this.options.files, this.options.rootDir);
953
- //remove files currently loaded in the program, we will transpile those instead (even if just for source maps)
954
- let filteredFileMap = [];
955
- for (let fileEntry of fileMap) {
956
- if (this.hasFile(fileEntry.src) === false) {
957
- 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
+ });
958
1147
  }
1148
+ finally {
1149
+ this.plugins.remove(plugin);
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;
959
1165
  }
960
- const { entries, astEditor } = this.beforeProgramTranspile(fileMap, this.options.stagingDir);
961
- const result = this._getTranspiledFileContents(this.getFile(filePath));
962
- 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 : '/')}`;
963
1167
  return result;
964
1168
  }
965
1169
  /**
966
- * Internal function used to transpile files.
967
- * 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
968
1172
  */
969
- _getTranspiledFileContents(file, outputPath) {
970
- const editor = new AstEditor_1.AstEditor();
971
- this.plugins.emit('beforeFileTranspile', {
1173
+ async prepare(files) {
1174
+ const programEvent = {
972
1175
  program: this,
973
- file: file,
974
- outputPath: outputPath,
975
- editor: editor
976
- });
977
- //if we have any edits, assume the file needs to be transpiled
978
- if (editor.hasChanges) {
979
- //use the `editor` because it'll track the previous value for us and revert later on
980
- editor.setProperty(file, 'needsTranspiled', true);
981
- }
982
- //transpile the file
983
- const result = file.transpile();
984
- //generate the typedef if enabled
985
- let typedef;
986
- if ((0, reflection_1.isBrsFile)(file) && this.options.emitDefinitions) {
987
- typedef = file.getTypedef();
988
- }
989
- const event = {
990
- program: this,
991
- file: file,
992
- outputPath: outputPath,
993
- editor: editor,
994
- code: result.code,
995
- map: result.map,
996
- typedef: typedef
997
- };
998
- this.plugins.emit('afterFileTranspile', event);
999
- //undo all `editor` edits that may have been applied to this file.
1000
- editor.undoAll();
1001
- return {
1002
- srcPath: file.srcPath,
1003
- pkgPath: file.pkgPath,
1004
- code: event.code,
1005
- map: event.map,
1006
- typedef: event.typedef
1176
+ editor: this.editor,
1177
+ files: files
1007
1178
  };
1008
- }
1009
- beforeProgramTranspile(fileEntries, stagingDir) {
1010
- // map fileEntries using their path as key, to avoid excessive "find()" operations
1011
- const mappedFileEntries = fileEntries.reduce((collection, entry) => {
1012
- collection[(0, util_1.standardizePath) `${entry.src}`] = entry;
1013
- return collection;
1014
- }, {});
1015
- const getOutputPath = (file) => {
1016
- let filePathObj = mappedFileEntries[(0, util_1.standardizePath) `${file.srcPath}`];
1017
- if (!filePathObj) {
1018
- //this file has been added in-memory, from a plugin, for example
1019
- filePathObj = {
1020
- //add an interpolated src path (since it doesn't actually exist in memory)
1021
- src: `bsc:/${file.pkgPath}`,
1022
- dest: file.pkgPath
1023
- };
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();
1024
1184
  }
1025
- //replace the file extension
1026
- let outputPath = filePathObj.dest.replace(/\.bs$/gi, '.brs');
1027
- //prepend the staging folder path
1028
- outputPath = (0, util_1.standardizePath) `${stagingDir}/${outputPath}`;
1029
- return outputPath;
1030
- };
1031
- const entries = Object.values(this.files).map(file => {
1032
- return {
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
+ }
1196
+ });
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,
1033
1208
  file: file,
1034
- outputPath: getOutputPath(file)
1209
+ editor: file.editor,
1210
+ outputPath: this.getOutputPath(file, stagingDir)
1035
1211
  };
1036
- //sort the entries to make transpiling more deterministic
1037
- }).sort((a, b) => {
1038
- return a.file.srcPath < b.file.srcPath ? -1 : 1;
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);
1217
+ }
1218
+ await this.plugins.emitAsync('afterPrepareProgram', programEvent);
1219
+ return files;
1220
+ }
1221
+ /**
1222
+ * Generate the contents of every file
1223
+ */
1224
+ async serialize(files) {
1225
+ const allFiles = new Map();
1226
+ const serializeProgramEvent = await this.plugins.emitAsync('beforeSerializeProgram', {
1227
+ program: this,
1228
+ files: files,
1229
+ result: allFiles
1039
1230
  });
1040
- const astEditor = new AstEditor_1.AstEditor();
1041
- this.plugins.emit('beforeProgramTranspile', {
1231
+ await this.plugins.emitAsync('onSerializeProgram', {
1042
1232
  program: this,
1043
- entries: entries,
1044
- editor: astEditor
1233
+ files: files,
1234
+ result: allFiles
1045
1235
  });
1046
- return {
1047
- entries: entries,
1048
- getOutputPath: getOutputPath,
1049
- astEditor: astEditor
1050
- };
1051
- }
1052
- async transpile(fileEntries, stagingDir) {
1053
- const { entries, getOutputPath, astEditor } = this.beforeProgramTranspile(fileEntries, stagingDir);
1054
- const processedFiles = new Set();
1055
- const transpileFile = async (srcPath, outputPath) => {
1056
- //find the file in the program
1057
- const file = this.getFile(srcPath);
1058
- //mark this file as processed so we don't process it more than once
1059
- processedFiles.add(outputPath === null || outputPath === void 0 ? void 0 : outputPath.toLowerCase());
1060
- //skip transpiling typedef files
1061
- if ((0, reflection_1.isBrsFile)(file) && file.isTypedef) {
1062
- return;
1063
- }
1064
- const fileTranspileResult = this._getTranspiledFileContents(file, outputPath);
1065
- //make sure the full dir path exists
1066
- await fsExtra.ensureDir(path.dirname(outputPath));
1067
- if (await fsExtra.pathExists(outputPath)) {
1068
- throw new Error(`Error while transpiling "${file.srcPath}". A file already exists at "${outputPath}" and will not be overwritten.`);
1069
- }
1070
- const writeMapPromise = fileTranspileResult.map ? fsExtra.writeFile(`${outputPath}.map`, fileTranspileResult.map.toString()) : null;
1071
- await Promise.all([
1072
- fsExtra.writeFile(outputPath, fileTranspileResult.code),
1073
- writeMapPromise
1074
- ]);
1075
- if (fileTranspileResult.typedef) {
1076
- const typedefPath = outputPath.replace(/\.brs$/i, '.d.bs');
1077
- await fsExtra.writeFile(typedefPath, fileTranspileResult.typedef);
1078
- }
1079
- };
1080
- let promises = entries.map(async (entry) => {
1081
- var _a;
1082
- return transpileFile((_a = entry === null || entry === void 0 ? void 0 : entry.file) === null || _a === void 0 ? void 0 : _a.srcPath, entry.outputPath);
1236
+ //sort the entries to make transpiling more deterministic
1237
+ files = serializeProgramEvent.files.sort((a, b) => {
1238
+ return a.srcPath < b.srcPath ? -1 : 1;
1083
1239
  });
1084
- //if there's no bslib file already loaded into the program, copy it to the staging directory
1085
- if (!this.getFile(bslibAliasedRokuModulesPkgPath) && !this.getFile((0, util_1.standardizePath) `source/bslib.brs`)) {
1086
- promises.push(util_1.util.copyBslibToStaging(stagingDir));
1240
+ // serialize each file
1241
+ for (const file of files) {
1242
+ const event = {
1243
+ program: this,
1244
+ file: file,
1245
+ result: allFiles
1246
+ };
1247
+ await this.plugins.emitAsync('beforeSerializeFile', event);
1248
+ await this.plugins.emitAsync('serializeFile', event);
1249
+ await this.plugins.emitAsync('afterSerializeFile', event);
1087
1250
  }
1088
- await Promise.all(promises);
1089
- //transpile any new files that plugins added since the start of this transpile process
1090
- do {
1091
- promises = [];
1092
- for (const key in this.files) {
1093
- const file = this.files[key];
1094
- //this is a new file
1095
- const outputPath = getOutputPath(file);
1096
- if (!processedFiles.has(outputPath === null || outputPath === void 0 ? void 0 : outputPath.toLowerCase())) {
1097
- promises.push(transpileFile(file === null || file === void 0 ? void 0 : file.srcPath, outputPath));
1098
- }
1099
- }
1100
- if (promises.length > 0) {
1101
- this.logger.info(`Transpiling ${promises.length} new files`);
1102
- await Promise.all(promises);
1103
- }
1104
- } while (promises.length > 0);
1105
- this.afterProgramTranspile(entries, astEditor);
1251
+ this.plugins.emit('afterSerializeProgram', {
1252
+ program: this,
1253
+ files: files,
1254
+ result: allFiles
1255
+ });
1256
+ return allFiles;
1106
1257
  }
1107
- afterProgramTranspile(entries, astEditor) {
1108
- this.plugins.emit('afterProgramTranspile', {
1258
+ /**
1259
+ * Write the entire project to disk
1260
+ */
1261
+ async write(stagingDir, files) {
1262
+ const programEvent = await this.plugins.emitAsync('beforeWriteProgram', {
1109
1263
  program: this,
1110
- entries: entries,
1111
- editor: astEditor
1264
+ files: files,
1265
+ stagingDir: stagingDir
1266
+ });
1267
+ //empty the staging directory
1268
+ await fsExtra.emptyDir(stagingDir);
1269
+ const serializedFiles = [...files]
1270
+ .map(([, serializedFiles]) => serializedFiles)
1271
+ .flat();
1272
+ //write all the files to disk (asynchronously)
1273
+ await Promise.all(serializedFiles.map(async (file) => {
1274
+ const event = await this.plugins.emitAsync('beforeWriteFile', {
1275
+ program: this,
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);
1284
+ }
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
+ }
1112
1311
  });
1113
- astEditor.undoAll();
1114
1312
  }
1115
1313
  /**
1116
1314
  * Find a list of files in the program that have a function with the given name (case INsensitive)
@@ -1185,6 +1383,7 @@ class Program {
1185
1383
  * Get a map of the manifest information
1186
1384
  */
1187
1385
  getManifest() {
1386
+ var _a, _b;
1188
1387
  if (!this._manifest) {
1189
1388
  //load the manifest file.
1190
1389
  //TODO update this to get the manifest from the files array or require it in the options...we shouldn't assume the location of the manifest
@@ -1193,7 +1392,27 @@ class Program {
1193
1392
  try {
1194
1393
  //we only load this manifest once, so do it sync to improve speed downstream
1195
1394
  contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1196
- this._manifest = (0, Manifest_1.parseManifest)(contents);
1395
+ let parsedManifest = (0, Manifest_1.parseManifest)(contents);
1396
+ // Lift the bs_consts defined in the manifest
1397
+ let bsConsts = (0, Manifest_1.getBsConst)(parsedManifest, false);
1398
+ // Override or delete any bs_consts defined in the bs config
1399
+ for (const key in (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.manifest) === null || _b === void 0 ? void 0 : _b.bs_const) {
1400
+ const value = this.options.manifest.bs_const[key];
1401
+ if (value === null) {
1402
+ bsConsts.delete(key);
1403
+ }
1404
+ else {
1405
+ bsConsts.set(key, value);
1406
+ }
1407
+ }
1408
+ // convert the new list of bs consts back into a string for the rest of the down stream systems to use
1409
+ let constString = '';
1410
+ for (const [key, value] of bsConsts) {
1411
+ constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
1412
+ }
1413
+ // Set the updated bs_const value
1414
+ parsedManifest.set('bs_const', constString);
1415
+ this._manifest = parsedManifest;
1197
1416
  }
1198
1417
  catch (err) {
1199
1418
  this._manifest = new Map();
@@ -1202,16 +1421,29 @@ class Program {
1202
1421
  return this._manifest;
1203
1422
  }
1204
1423
  dispose() {
1424
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1205
1425
  this.plugins.emit('beforeProgramDispose', { program: this });
1206
1426
  for (let filePath in this.files) {
1207
- this.files[filePath].dispose();
1427
+ (_b = (_a = this.files[filePath]) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
1208
1428
  }
1209
1429
  for (let name in this.scopes) {
1210
- this.scopes[name].dispose();
1430
+ (_d = (_c = this.scopes[name]) === null || _c === void 0 ? void 0 : _c.dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
1211
1431
  }
1212
- this.globalScope.dispose();
1213
- this.dependencyGraph.dispose();
1432
+ (_f = (_e = this.globalScope) === null || _e === void 0 ? void 0 : _e.dispose) === null || _f === void 0 ? void 0 : _f.call(_e);
1433
+ (_h = (_g = this.dependencyGraph) === null || _g === void 0 ? void 0 : _g.dispose) === null || _h === void 0 ? void 0 : _h.call(_g);
1214
1434
  }
1215
1435
  }
1216
1436
  exports.Program = Program;
1437
+ class ProvideFileEventInternal {
1438
+ constructor(program, srcPath, destPath, data, fileFactory) {
1439
+ var _a;
1440
+ this.program = program;
1441
+ this.srcPath = srcPath;
1442
+ this.destPath = destPath;
1443
+ this.data = data;
1444
+ this.fileFactory = fileFactory;
1445
+ this.files = [];
1446
+ this.srcExtension = (_a = path.extname(srcPath)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
1447
+ }
1448
+ }
1217
1449
  //# sourceMappingURL=Program.js.map