brighterscript 0.42.0 → 0.45.0

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 (201) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/Cache.d.ts +3 -8
  3. package/dist/Cache.js +9 -14
  4. package/dist/Cache.js.map +1 -1
  5. package/dist/DiagnosticMessages.d.ts +21 -1
  6. package/dist/DiagnosticMessages.js +20 -0
  7. package/dist/DiagnosticMessages.js.map +1 -1
  8. package/dist/LanguageServer.d.ts +1 -6
  9. package/dist/LanguageServer.js +3 -12
  10. package/dist/LanguageServer.js.map +1 -1
  11. package/dist/PluginInterface.d.ts +3 -3
  12. package/dist/PluginInterface.js +3 -0
  13. package/dist/PluginInterface.js.map +1 -1
  14. package/dist/Program.d.ts +68 -25
  15. package/dist/Program.js +169 -76
  16. package/dist/Program.js.map +1 -1
  17. package/dist/ProgramBuilder.js +6 -6
  18. package/dist/ProgramBuilder.js.map +1 -1
  19. package/dist/Scope.d.ts +18 -11
  20. package/dist/Scope.js +41 -14
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/XmlScope.d.ts +3 -3
  23. package/dist/astUtils/AstEditor.d.ts +6 -0
  24. package/dist/astUtils/AstEditor.js +10 -0
  25. package/dist/astUtils/AstEditor.js.map +1 -1
  26. package/dist/astUtils/AstEditor.spec.js +37 -0
  27. package/dist/astUtils/AstEditor.spec.js.map +1 -1
  28. package/dist/astUtils/reflection.d.ts +5 -2
  29. package/dist/astUtils/reflection.js +14 -2
  30. package/dist/astUtils/reflection.js.map +1 -1
  31. package/dist/astUtils/reflection.spec.js +6 -6
  32. package/dist/astUtils/reflection.spec.js.map +1 -1
  33. package/dist/astUtils/visitors.d.ts +3 -1
  34. package/dist/astUtils/visitors.js.map +1 -1
  35. package/dist/astUtils/visitors.spec.js +15 -18
  36. package/dist/astUtils/visitors.spec.js.map +1 -1
  37. package/dist/bscPlugin/BscPlugin.d.ts +4 -1
  38. package/dist/bscPlugin/BscPlugin.js +21 -2
  39. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  40. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +3 -3
  41. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  42. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +18 -16
  43. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  44. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +9 -0
  45. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +97 -0
  46. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -0
  47. package/dist/bscPlugin/semanticTokens/{SemanticTokensProcessor.spec.d.ts → BrsFileSemanticTokensProcessor.spec.d.ts} +0 -0
  48. package/dist/bscPlugin/semanticTokens/{SemanticTokensProcessor.spec.js → BrsFileSemanticTokensProcessor.spec.js} +32 -4
  49. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -0
  50. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.d.ts +8 -0
  51. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js +36 -0
  52. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js.map +1 -0
  53. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +9 -0
  54. package/dist/bscPlugin/validation/BrsFileValidator.js +66 -0
  55. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -0
  56. package/dist/bscPlugin/validation/ScopeValidator.d.ts +11 -0
  57. package/dist/bscPlugin/validation/ScopeValidator.js +94 -0
  58. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -0
  59. package/dist/files/BrsFile.Class.spec.js +218 -114
  60. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  61. package/dist/files/BrsFile.d.ts +27 -8
  62. package/dist/files/BrsFile.js +216 -95
  63. package/dist/files/BrsFile.js.map +1 -1
  64. package/dist/files/BrsFile.spec.js +338 -190
  65. package/dist/files/BrsFile.spec.js.map +1 -1
  66. package/dist/files/XmlFile.d.ts +11 -5
  67. package/dist/files/XmlFile.js +15 -9
  68. package/dist/files/XmlFile.js.map +1 -1
  69. package/dist/files/XmlFile.spec.js +118 -114
  70. package/dist/files/XmlFile.spec.js.map +1 -1
  71. package/dist/files/tests/imports.spec.js +29 -27
  72. package/dist/files/tests/imports.spec.js.map +1 -1
  73. package/dist/index.d.ts +12 -3
  74. package/dist/index.js +21 -4
  75. package/dist/index.js.map +1 -1
  76. package/dist/interfaces.d.ts +50 -9
  77. package/dist/lexer/Lexer.js +1 -2
  78. package/dist/lexer/Lexer.js.map +1 -1
  79. package/dist/lexer/Lexer.spec.js +470 -462
  80. package/dist/lexer/Lexer.spec.js.map +1 -1
  81. package/dist/lexer/TokenKind.d.ts +2 -0
  82. package/dist/lexer/TokenKind.js +5 -0
  83. package/dist/lexer/TokenKind.js.map +1 -1
  84. package/dist/parser/Expression.d.ts +1 -1
  85. package/dist/parser/Expression.js +10 -10
  86. package/dist/parser/Expression.js.map +1 -1
  87. package/dist/parser/Parser.Class.spec.js +32 -31
  88. package/dist/parser/Parser.Class.spec.js.map +1 -1
  89. package/dist/parser/Parser.d.ts +23 -2
  90. package/dist/parser/Parser.js +445 -254
  91. package/dist/parser/Parser.js.map +1 -1
  92. package/dist/parser/Parser.spec.js +86 -24
  93. package/dist/parser/Parser.spec.js.map +1 -1
  94. package/dist/parser/SGParser.spec.js +1 -1
  95. package/dist/parser/SGParser.spec.js.map +1 -1
  96. package/dist/parser/Statement.d.ts +54 -2
  97. package/dist/parser/Statement.js +162 -9
  98. package/dist/parser/Statement.js.map +1 -1
  99. package/dist/parser/Statement.spec.js +5 -5
  100. package/dist/parser/Statement.spec.js.map +1 -1
  101. package/dist/parser/tests/Parser.spec.d.ts +3 -3
  102. package/dist/parser/tests/Parser.spec.js +4 -4
  103. package/dist/parser/tests/Parser.spec.js.map +1 -1
  104. package/dist/parser/tests/controlFlow/For.spec.js +40 -40
  105. package/dist/parser/tests/controlFlow/For.spec.js.map +1 -1
  106. package/dist/parser/tests/controlFlow/ForEach.spec.js +22 -21
  107. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  108. package/dist/parser/tests/controlFlow/If.spec.js +100 -99
  109. package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
  110. package/dist/parser/tests/controlFlow/While.spec.js +25 -25
  111. package/dist/parser/tests/controlFlow/While.spec.js.map +1 -1
  112. package/dist/parser/tests/expression/Additive.spec.js +21 -21
  113. package/dist/parser/tests/expression/Additive.spec.js.map +1 -1
  114. package/dist/parser/tests/expression/ArrayLiterals.spec.js +91 -91
  115. package/dist/parser/tests/expression/ArrayLiterals.spec.js.map +1 -1
  116. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +102 -102
  117. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
  118. package/dist/parser/tests/expression/Boolean.spec.js +15 -15
  119. package/dist/parser/tests/expression/Boolean.spec.js.map +1 -1
  120. package/dist/parser/tests/expression/Call.spec.js +22 -21
  121. package/dist/parser/tests/expression/Call.spec.js.map +1 -1
  122. package/dist/parser/tests/expression/Exponential.spec.js +11 -11
  123. package/dist/parser/tests/expression/Exponential.spec.js.map +1 -1
  124. package/dist/parser/tests/expression/Function.spec.js +171 -171
  125. package/dist/parser/tests/expression/Function.spec.js.map +1 -1
  126. package/dist/parser/tests/expression/Indexing.spec.js +50 -50
  127. package/dist/parser/tests/expression/Indexing.spec.js.map +1 -1
  128. package/dist/parser/tests/expression/Multiplicative.spec.js +25 -25
  129. package/dist/parser/tests/expression/Multiplicative.spec.js.map +1 -1
  130. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +17 -17
  131. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  132. package/dist/parser/tests/expression/PrefixUnary.spec.js +26 -26
  133. package/dist/parser/tests/expression/PrefixUnary.spec.js.map +1 -1
  134. package/dist/parser/tests/expression/Primary.spec.js +27 -27
  135. package/dist/parser/tests/expression/Primary.spec.js.map +1 -1
  136. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +4 -3
  137. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  138. package/dist/parser/tests/expression/Relational.spec.js +25 -25
  139. package/dist/parser/tests/expression/Relational.spec.js.map +1 -1
  140. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +8 -8
  141. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  142. package/dist/parser/tests/expression/TernaryExpression.spec.js +7 -7
  143. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  144. package/dist/parser/tests/statement/AssignmentOperators.spec.js +15 -15
  145. package/dist/parser/tests/statement/AssignmentOperators.spec.js.map +1 -1
  146. package/dist/parser/tests/statement/Declaration.spec.js +20 -20
  147. package/dist/parser/tests/statement/Declaration.spec.js.map +1 -1
  148. package/dist/parser/tests/statement/Enum.spec.d.ts +1 -0
  149. package/dist/parser/tests/statement/Enum.spec.js +774 -0
  150. package/dist/parser/tests/statement/Enum.spec.js.map +1 -0
  151. package/dist/parser/tests/statement/Function.spec.js +121 -120
  152. package/dist/parser/tests/statement/Function.spec.js.map +1 -1
  153. package/dist/parser/tests/statement/Goto.spec.js +9 -8
  154. package/dist/parser/tests/statement/Goto.spec.js.map +1 -1
  155. package/dist/parser/tests/statement/Increment.spec.js +22 -22
  156. package/dist/parser/tests/statement/Increment.spec.js.map +1 -1
  157. package/dist/parser/tests/statement/InterfaceStatement.spec.js +12 -0
  158. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  159. package/dist/parser/tests/statement/LibraryStatement.spec.js +7 -7
  160. package/dist/parser/tests/statement/LibraryStatement.spec.js.map +1 -1
  161. package/dist/parser/tests/statement/Misc.spec.js +71 -70
  162. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  163. package/dist/parser/tests/statement/PrintStatement.spec.js +17 -17
  164. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  165. package/dist/parser/tests/statement/ReturnStatement.spec.js +33 -33
  166. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +1 -1
  167. package/dist/parser/tests/statement/Set.spec.js +53 -53
  168. package/dist/parser/tests/statement/Set.spec.js.map +1 -1
  169. package/dist/parser/tests/statement/Stop.spec.js +7 -6
  170. package/dist/parser/tests/statement/Stop.spec.js.map +1 -1
  171. package/dist/preprocessor/Chunk.d.ts +1 -1
  172. package/dist/preprocessor/Preprocessor.d.ts +1 -1
  173. package/dist/preprocessor/Preprocessor.js +7 -7
  174. package/dist/preprocessor/Preprocessor.js.map +1 -1
  175. package/dist/types/FunctionType.d.ts +2 -2
  176. package/dist/types/FunctionType.js +3 -3
  177. package/dist/types/FunctionType.js.map +1 -1
  178. package/dist/types/FunctionType.spec.js +2 -2
  179. package/dist/types/FunctionType.spec.js.map +1 -1
  180. package/dist/util.d.ts +27 -1
  181. package/dist/util.js +96 -29
  182. package/dist/util.js.map +1 -1
  183. package/dist/validators/ClassValidator.js +20 -27
  184. package/dist/validators/ClassValidator.js.map +1 -1
  185. package/package.json +2 -1
  186. package/dist/astUtils/index.d.ts +0 -7
  187. package/dist/astUtils/index.js +0 -26
  188. package/dist/astUtils/index.js.map +0 -1
  189. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.d.ts +0 -7
  190. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js +0 -63
  191. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js.map +0 -1
  192. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +0 -1
  193. package/dist/lexer/index.d.ts +0 -3
  194. package/dist/lexer/index.js +0 -18
  195. package/dist/lexer/index.js.map +0 -1
  196. package/dist/parser/index.d.ts +0 -3
  197. package/dist/parser/index.js +0 -16
  198. package/dist/parser/index.js.map +0 -1
  199. package/dist/preprocessor/index.d.ts +0 -3
  200. package/dist/preprocessor/index.js +0 -16
  201. package/dist/preprocessor/index.js.map +0 -1
package/dist/Program.d.ts CHANGED
@@ -4,12 +4,13 @@ import type { BsConfig } from './BsConfig';
4
4
  import { Scope } from './Scope';
5
5
  import { BrsFile } from './files/BrsFile';
6
6
  import { XmlFile } from './files/XmlFile';
7
- import type { BsDiagnostic, File, FileReference, FileObj, BscFile, SemanticToken } from './interfaces';
7
+ import type { BsDiagnostic, File, FileReference, FileObj, BscFile, SemanticToken, FileLink } from './interfaces';
8
8
  import { XmlScope } from './XmlScope';
9
9
  import { Logger } from './Logger';
10
10
  import type { ManifestValue } from './preprocessor/Manifest';
11
11
  import PluginInterface from './PluginInterface';
12
12
  import type { FunctionStatement, Statement } from './parser/Statement';
13
+ import type { SourceMapGenerator } from 'source-map';
13
14
  export interface SourceObj {
14
15
  pathAbsolute: string;
15
16
  source: string;
@@ -24,10 +25,6 @@ export interface SignatureInfoObj {
24
25
  key: string;
25
26
  signature: SignatureInformation;
26
27
  }
27
- export interface FileLink<T> {
28
- item: T;
29
- file: BrsFile;
30
- }
31
28
  export declare class Program {
32
29
  /**
33
30
  * The root directory for this program
@@ -116,8 +113,9 @@ export declare class Program {
116
113
  /**
117
114
  * Determine if the specified file is loaded in this program right now.
118
115
  * @param filePath
116
+ * @param normalizePath should the provided path be normalized before use
119
117
  */
120
- hasFile(filePath: string): boolean;
118
+ hasFile(filePath: string, normalizePath?: boolean): boolean;
121
119
  getPkgPath(...args: any[]): any;
122
120
  /**
123
121
  * roku filesystem is case INsensitive, so find the scope by key case insensitive
@@ -132,19 +130,42 @@ export declare class Program {
132
130
  * Find the scope for the specified component
133
131
  */
134
132
  getComponentScope(componentName: string): XmlScope;
133
+ /**
134
+ * Update internal maps with this file reference
135
+ */
136
+ private assignFile;
137
+ /**
138
+ * Remove this file from internal maps
139
+ */
140
+ private unassignFile;
135
141
  /**
136
142
  * Load a file into the program. If that file already exists, it is replaced.
137
143
  * If file contents are provided, those are used, Otherwise, the file is loaded from the file system
138
- * @param relativePath the file path relative to the root dir
144
+ * @param srcPath the file path relative to the root dir
139
145
  * @param fileContents the file contents
146
+ * @deprecated use `setFile` instead
140
147
  */
141
- addOrReplaceFile<T extends BscFile>(relativePath: string, fileContents: string): T;
148
+ addOrReplaceFile<T extends BscFile>(srcPath: string, fileContents: string): T;
142
149
  /**
143
150
  * Load a file into the program. If that file already exists, it is replaced.
144
151
  * @param fileEntry an object that specifies src and dest for the file.
145
152
  * @param fileContents the file contents. If not provided, the file will be loaded from disk
153
+ * @deprecated use `setFile` instead
146
154
  */
147
155
  addOrReplaceFile<T extends BscFile>(fileEntry: FileObj, fileContents: string): T;
156
+ /**
157
+ * Load a file into the program. If that file already exists, it is replaced.
158
+ * If file contents are provided, those are used, Otherwise, the file is loaded from the file system
159
+ * @param srcDestOrPkgPath the absolute path, the pkg path (i.e. `pkg:/path/to/file.brs`), or the destPath (i.e. `path/to/file.brs` relative to `pkg:/`)
160
+ * @param fileContents the file contents
161
+ */
162
+ setFile<T extends BscFile>(srcDestOrPkgPath: string, fileContents: string): T;
163
+ /**
164
+ * Load a file into the program. If that file already exists, it is replaced.
165
+ * @param fileEntry an object that specifies src and dest for the file.
166
+ * @param fileContents the file contents. If not provided, the file will be loaded from disk
167
+ */
168
+ setFile<T extends BscFile>(fileEntry: FileObj, fileContents: string): T;
148
169
  /**
149
170
  * Ensure source scope is created.
150
171
  * Note: automatically called internally, and no-op if it exists already.
@@ -155,28 +176,33 @@ export declare class Program {
155
176
  * Roku is a case insensitive file system. It is an error to have multiple files
156
177
  * with the same path with only case being different.
157
178
  * @param pathAbsolute
179
+ * @deprecated use `getFile` instead, which auto-detects the path type
158
180
  */
159
181
  getFileByPathAbsolute<T extends BrsFile | XmlFile>(pathAbsolute: string): T;
160
182
  /**
161
183
  * Get a list of files for the given (platform-normalized) pkgPath array.
162
184
  * Missing files are just ignored.
185
+ * @deprecated use `getFiles` instead, which auto-detects the path types
163
186
  */
164
187
  getFilesByPkgPaths<T extends BscFile[]>(pkgPaths: string[]): T;
165
188
  /**
166
189
  * Get a file with the specified (platform-normalized) pkg path.
167
190
  * If not found, return undefined
191
+ * @deprecated use `getFile` instead, which auto-detects the path type
168
192
  */
169
193
  getFileByPkgPath<T extends BscFile>(pkgPath: string): T;
170
194
  /**
171
195
  * Remove a set of files from the program
172
- * @param absolutePaths
196
+ * @param filePaths can be an array of srcPath or destPath strings
197
+ * @param normalizePath should this function repair and standardize the filePaths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
173
198
  */
174
- removeFiles(absolutePaths: string[]): void;
199
+ removeFiles(srcPaths: string[], normalizePath?: boolean): void;
175
200
  /**
176
201
  * Remove a file from the program
177
- * @param pathAbsolute
202
+ * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
203
+ * @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
178
204
  */
179
- removeFile(pathAbsolute: string): void;
205
+ removeFile(filePath: string, normalizePath?: boolean): void;
180
206
  /**
181
207
  * Traverse the entire project, and validate all scopes
182
208
  * @param force - if true, then all scopes are force to validate, even if they aren't marked as dirty
@@ -190,25 +216,35 @@ export declare class Program {
190
216
  * Determine if the given file is included in at least one scope in this program
191
217
  */
192
218
  private fileIsIncludedInAnyScope;
219
+ /**
220
+ * Get the files for a list of filePaths
221
+ * @param filePaths can be an array of srcPath or a destPath strings
222
+ * @param normalizePath should this function repair and standardize the paths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
223
+ */
224
+ getFiles<T extends BscFile>(filePaths: string[], normalizePath?: boolean): T[];
193
225
  /**
194
226
  * Get the file at the given path
195
- * @param pathAbsolute
227
+ * @param filePath can be a srcPath or a destPath
228
+ * @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
196
229
  */
197
- private getFile;
230
+ getFile<T extends BscFile>(filePath: string, normalizePath?: boolean): T;
198
231
  /**
199
232
  * Get a list of all scopes the file is loaded into
200
233
  * @param file
201
234
  */
202
235
  getScopesForFile(file: XmlFile | BrsFile): Scope[];
236
+ /**
237
+ * Get the first found scope for a file.
238
+ */
239
+ getFirstScopeForFile(file: XmlFile | BrsFile): Scope;
203
240
  getStatementsByName(name: string, originFile: BrsFile, namespaceName?: string): FileLink<Statement>[];
204
241
  getStatementsForXmlFile(scope: XmlScope, filterName?: string): FileLink<FunctionStatement>[];
205
242
  /**
206
243
  * Find all available completion items at the given position
207
- * @param pathAbsolute
208
- * @param lineIndex
209
- * @param columnIndex
244
+ * @param filePath can be a srcPath or a destPath
245
+ * @param position the position (line & column) where completions should be found
210
246
  */
211
- getCompletions(pathAbsolute: string, position: Position): CompletionItem[];
247
+ getCompletions(filePath: string, position: Position): CompletionItem[];
212
248
  /**
213
249
  * Goes through each file and builds a list of workspace symbols for the program. Used by LanguageServer's onWorkspaceSymbol functionality
214
250
  */
@@ -240,19 +276,19 @@ export declare class Program {
240
276
  * Transpile a single file and get the result as a string.
241
277
  * This does not write anything to the file system.
242
278
  */
243
- getTranspiledFileContents(pathAbsolute: string): {
244
- pathAbsolute: string;
245
- pkgPath: string;
246
- code: string;
247
- map: import("source-map").SourceMapGenerator;
248
- };
279
+ getTranspiledFileContents(pathAbsolute: string): FileTranspileResult;
280
+ /**
281
+ * Internal function used to transpile files.
282
+ * This does not write anything to the file system
283
+ */
284
+ private _getTranspiledFileContents;
249
285
  transpile(fileEntries: FileObj[], stagingFolderPath: string): Promise<void>;
250
286
  /**
251
287
  * Find a list of files in the program that have a function with the given name (case INsensitive)
252
288
  */
253
289
  findFilesForFunction(functionName: string): BscFile[];
254
290
  /**
255
- * Find a list of files in the program that have a function with the given name (case INsensitive)
291
+ * Find a list of files in the program that have a class with the given name (case INsensitive)
256
292
  */
257
293
  findFilesForClass(className: string): BscFile[];
258
294
  /**
@@ -262,3 +298,10 @@ export declare class Program {
262
298
  private _manifest;
263
299
  dispose(): void;
264
300
  }
301
+ export interface FileTranspileResult {
302
+ pathAbsolute: string;
303
+ pkgPath: string;
304
+ code: string;
305
+ map: SourceMapGenerator;
306
+ typedef: string;
307
+ }
package/dist/Program.js CHANGED
@@ -20,8 +20,8 @@ const Manifest_1 = require("./preprocessor/Manifest");
20
20
  const vscode_uri_1 = require("vscode-uri");
21
21
  const PluginInterface_1 = require("./PluginInterface");
22
22
  const reflection_1 = require("./astUtils/reflection");
23
- const parser_1 = require("./parser");
24
- const lexer_1 = require("./lexer");
23
+ const Parser_1 = require("./parser/Parser");
24
+ const TokenKind_1 = require("./lexer/TokenKind");
25
25
  const BscPlugin_1 = require("./bscPlugin/BscPlugin");
26
26
  const AstEditor_1 = require("./astUtils/AstEditor");
27
27
  const startOfSourcePkgPath = `source${path.sep}`;
@@ -87,11 +87,11 @@ class Program {
87
87
  */
88
88
  get bslibPkgPath() {
89
89
  //if there's an aliased (preferred) version of bslib from roku_modules loaded into the program, use that
90
- if (this.getFileByPkgPath(bslibAliasedRokuModulesPkgPath)) {
90
+ if (this.getFile(bslibAliasedRokuModulesPkgPath)) {
91
91
  return bslibAliasedRokuModulesPkgPath;
92
92
  //if there's a non-aliased version of bslib from roku_modules, use that
93
93
  }
94
- else if (this.getFileByPkgPath(bslibNonAliasedRokuModulesPkgPath)) {
94
+ else if (this.getFile(bslibNonAliasedRokuModulesPkgPath)) {
95
95
  return bslibNonAliasedRokuModulesPkgPath;
96
96
  //default to the embedded version
97
97
  }
@@ -220,10 +220,10 @@ class Program {
220
220
  /**
221
221
  * Determine if the specified file is loaded in this program right now.
222
222
  * @param filePath
223
+ * @param normalizePath should the provided path be normalized before use
223
224
  */
224
- hasFile(filePath) {
225
- filePath = (0, util_1.standardizePath) `${filePath}`;
226
- return this.files[filePath] !== undefined;
225
+ hasFile(filePath, normalizePath = true) {
226
+ return !!this.getFile(filePath, normalizePath);
227
227
  }
228
228
  getPkgPath(...args) {
229
229
  throw new Error('Not implemented');
@@ -255,8 +255,27 @@ class Program {
255
255
  var _a;
256
256
  return (_a = this.getComponent(componentName)) === null || _a === void 0 ? void 0 : _a.scope;
257
257
  }
258
+ /**
259
+ * Update internal maps with this file reference
260
+ */
261
+ assignFile(file) {
262
+ this.files[file.pathAbsolute.toLowerCase()] = file;
263
+ this.pkgMap[file.pkgPath.toLowerCase()] = file;
264
+ return file;
265
+ }
266
+ /**
267
+ * Remove this file from internal maps
268
+ */
269
+ unassignFile(file) {
270
+ delete this.files[file.pathAbsolute.toLowerCase()];
271
+ delete this.pkgMap[file.pkgPath.toLowerCase()];
272
+ return file;
273
+ }
258
274
  addOrReplaceFile(fileParam, fileContents) {
259
- assert.ok(fileParam, 'fileEntry is required');
275
+ return this.setFile(fileParam, fileContents);
276
+ }
277
+ setFile(fileParam, fileContents) {
278
+ assert.ok(fileParam, 'fileParam is required');
260
279
  let srcPath;
261
280
  let pkgPath;
262
281
  if (typeof fileParam === 'string') {
@@ -267,7 +286,7 @@ class Program {
267
286
  srcPath = (0, util_1.standardizePath) `${fileParam.src}`;
268
287
  pkgPath = (0, util_1.standardizePath) `${fileParam.dest}`;
269
288
  }
270
- let file = this.logger.time(Logger_1.LogLevel.debug, ['Program.addOrReplaceFile()', chalk_1.default.green(srcPath)], () => {
289
+ let file = this.logger.time(Logger_1.LogLevel.debug, ['Program.setFile()', chalk_1.default.green(srcPath)], () => {
271
290
  assert.ok(srcPath, 'fileEntry.src is required');
272
291
  assert.ok(pkgPath, 'fileEntry.dest is required');
273
292
  //if the file is already loaded, remove it
@@ -277,15 +296,13 @@ class Program {
277
296
  let fileExtension = path.extname(srcPath).toLowerCase();
278
297
  let file;
279
298
  if (fileExtension === '.brs' || fileExtension === '.bs') {
280
- let brsFile = new BrsFile_1.BrsFile(srcPath, pkgPath, this);
299
+ //add the file to the program
300
+ const brsFile = this.assignFile(new BrsFile_1.BrsFile(srcPath, pkgPath, this));
281
301
  //add file to the `source` dependency list
282
302
  if (brsFile.pkgPath.startsWith(startOfSourcePkgPath)) {
283
303
  this.createSourceScope();
284
304
  this.dependencyGraph.addDependency('scope:source', brsFile.dependencyGraphKey);
285
305
  }
286
- //add the file to the program
287
- this.files[srcPath] = brsFile;
288
- this.pkgMap[brsFile.pkgPath.toLowerCase()] = brsFile;
289
306
  let sourceObj = {
290
307
  pathAbsolute: srcPath,
291
308
  source: fileContents
@@ -294,19 +311,18 @@ class Program {
294
311
  this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
295
312
  brsFile.parse(sourceObj.source);
296
313
  });
314
+ //notify plugins that this file has finished parsing
315
+ this.plugins.emit('afterFileParse', brsFile);
297
316
  file = brsFile;
298
317
  brsFile.attachDependencyGraph(this.dependencyGraph);
299
- this.plugins.emit('afterFileValidate', brsFile);
300
318
  }
301
319
  else if (
302
320
  //is xml file
303
321
  fileExtension === '.xml' &&
304
322
  //resides in the components folder (Roku will only parse xml files in the components folder)
305
323
  pkgPath.toLowerCase().startsWith(util_1.util.pathSepNormalize(`components/`))) {
306
- let xmlFile = new XmlFile_1.XmlFile(srcPath, pkgPath, this);
307
324
  //add the file to the program
308
- this.files[srcPath] = xmlFile;
309
- this.pkgMap[xmlFile.pkgPath.toLowerCase()] = xmlFile;
325
+ const xmlFile = this.assignFile(new XmlFile_1.XmlFile(srcPath, pkgPath, this));
310
326
  let sourceObj = {
311
327
  pathAbsolute: srcPath,
312
328
  source: fileContents
@@ -315,13 +331,14 @@ class Program {
315
331
  this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
316
332
  xmlFile.parse(sourceObj.source);
317
333
  });
334
+ //notify plugins that this file has finished parsing
335
+ this.plugins.emit('afterFileParse', xmlFile);
318
336
  file = xmlFile;
319
337
  //create a new scope for this xml file
320
338
  let scope = new XmlScope_1.XmlScope(xmlFile, this);
321
339
  this.addScope(scope);
322
340
  //register this compoent now that we have parsed it and know its component name
323
341
  this.registerComponent(xmlFile, scope);
324
- this.plugins.emit('afterFileValidate', xmlFile);
325
342
  }
326
343
  else {
327
344
  //TODO do we actually need to implement this? Figure out how to handle img paths
@@ -352,6 +369,7 @@ class Program {
352
369
  * Roku is a case insensitive file system. It is an error to have multiple files
353
370
  * with the same path with only case being different.
354
371
  * @param pathAbsolute
372
+ * @deprecated use `getFile` instead, which auto-detects the path type
355
373
  */
356
374
  getFileByPathAbsolute(pathAbsolute) {
357
375
  pathAbsolute = (0, util_1.standardizePath) `${pathAbsolute}`;
@@ -364,6 +382,7 @@ class Program {
364
382
  /**
365
383
  * Get a list of files for the given (platform-normalized) pkgPath array.
366
384
  * Missing files are just ignored.
385
+ * @deprecated use `getFiles` instead, which auto-detects the path types
367
386
  */
368
387
  getFilesByPkgPaths(pkgPaths) {
369
388
  return pkgPaths
@@ -373,29 +392,29 @@ class Program {
373
392
  /**
374
393
  * Get a file with the specified (platform-normalized) pkg path.
375
394
  * If not found, return undefined
395
+ * @deprecated use `getFile` instead, which auto-detects the path type
376
396
  */
377
397
  getFileByPkgPath(pkgPath) {
378
398
  return this.pkgMap[pkgPath.toLowerCase()];
379
399
  }
380
400
  /**
381
401
  * Remove a set of files from the program
382
- * @param absolutePaths
402
+ * @param filePaths can be an array of srcPath or destPath strings
403
+ * @param normalizePath should this function repair and standardize the filePaths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
383
404
  */
384
- removeFiles(absolutePaths) {
385
- for (let pathAbsolute of absolutePaths) {
386
- this.removeFile(pathAbsolute);
405
+ removeFiles(srcPaths, normalizePath = true) {
406
+ for (let srcPath of srcPaths) {
407
+ this.removeFile(srcPath, normalizePath);
387
408
  }
388
409
  }
389
410
  /**
390
411
  * Remove a file from the program
391
- * @param pathAbsolute
412
+ * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
413
+ * @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
392
414
  */
393
- removeFile(pathAbsolute) {
394
- this.logger.debug('Program.removeFile()', pathAbsolute);
395
- if (!path.isAbsolute(pathAbsolute)) {
396
- throw new Error(`Path must be absolute: "${pathAbsolute}"`);
397
- }
398
- let file = this.getFile(pathAbsolute);
415
+ removeFile(filePath, normalizePath = true) {
416
+ this.logger.debug('Program.removeFile()', filePath);
417
+ let file = this.getFile(filePath, normalizePath);
399
418
  if (file) {
400
419
  this.plugins.emit('beforeFileDispose', file);
401
420
  //if there is a scope named the same as this file's path, remove it (i.e. xml scopes)
@@ -409,8 +428,7 @@ class Program {
409
428
  this.plugins.emit('afterScopeDispose', scope);
410
429
  }
411
430
  //remove the file from the program
412
- delete this.files[file.pathAbsolute];
413
- delete this.pkgMap[file.pkgPath.toLowerCase()];
431
+ this.unassignFile(file);
414
432
  this.dependencyGraph.remove(file.dependencyGraphKey);
415
433
  //if this is a pkg:/source file, notify the `source` scope that it has changed
416
434
  if (file.pkgPath.startsWith(startOfSourcePkgPath)) {
@@ -429,23 +447,40 @@ class Program {
429
447
  */
430
448
  validate() {
431
449
  this.logger.time(Logger_1.LogLevel.log, ['Validating project'], () => {
450
+ var _a;
432
451
  this.diagnostics = [];
433
452
  this.plugins.emit('beforeProgramValidate', this);
434
- this.logger.time(Logger_1.LogLevel.info, ['Validate all scopes'], () => {
435
- for (let scopeName in this.scopes) {
436
- let scope = this.scopes[scopeName];
437
- scope.validate();
438
- }
439
- });
440
- //find any files NOT loaded into a scope
441
- for (let filePath in this.files) {
442
- let file = this.files[filePath];
453
+ //validate every file
454
+ for (const file of Object.values(this.files)) {
455
+ //find any files NOT loaded into a scope
443
456
  if (!this.fileIsIncludedInAnyScope(file)) {
444
457
  this.logger.debug('Program.validate(): fileNotReferenced by any scope', () => chalk_1.default.green(file === null || file === void 0 ? void 0 : file.pkgPath));
445
458
  //the file is not loaded in any scope
446
459
  this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.fileNotReferencedByAnyOtherFile()), { file: file, range: util_1.util.createRange(0, 0, 0, Number.MAX_VALUE) }));
447
460
  }
461
+ //for every unvalidated file, validate it
462
+ if (!file.isValidated) {
463
+ this.plugins.emit('beforeFileValidate', {
464
+ program: this,
465
+ file: file
466
+ });
467
+ //emit an event to allow plugins to contribute to the file validation process
468
+ this.plugins.emit('onFileValidate', {
469
+ program: this,
470
+ file: file
471
+ });
472
+ //call file.validate() IF the file has that function defined
473
+ (_a = file.validate) === null || _a === void 0 ? void 0 : _a.call(file);
474
+ file.isValidated = true;
475
+ this.plugins.emit('afterFileValidate', file);
476
+ }
448
477
  }
478
+ this.logger.time(Logger_1.LogLevel.info, ['Validate all scopes'], () => {
479
+ for (let scopeName in this.scopes) {
480
+ let scope = this.scopes[scopeName];
481
+ scope.validate();
482
+ }
483
+ });
449
484
  this.detectDuplicateComponentNames();
450
485
  this.plugins.emit('afterProgramValidate', this);
451
486
  });
@@ -494,13 +529,31 @@ class Program {
494
529
  }
495
530
  return false;
496
531
  }
532
+ /**
533
+ * Get the files for a list of filePaths
534
+ * @param filePaths can be an array of srcPath or a destPath strings
535
+ * @param normalizePath should this function repair and standardize the paths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
536
+ */
537
+ getFiles(filePaths, normalizePath = true) {
538
+ return filePaths
539
+ .map(filePath => this.getFile(filePath, normalizePath))
540
+ .filter(file => file !== undefined);
541
+ }
497
542
  /**
498
543
  * Get the file at the given path
499
- * @param pathAbsolute
544
+ * @param filePath can be a srcPath or a destPath
545
+ * @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
500
546
  */
501
- getFile(pathAbsolute) {
502
- pathAbsolute = (0, util_1.standardizePath) `${pathAbsolute}`;
503
- return this.files[pathAbsolute];
547
+ getFile(filePath, normalizePath = true) {
548
+ if (typeof filePath !== 'string') {
549
+ return undefined;
550
+ }
551
+ else if (path.isAbsolute(filePath)) {
552
+ return this.files[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
553
+ }
554
+ else {
555
+ return this.pkgMap[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
556
+ }
504
557
  }
505
558
  /**
506
559
  * Get a list of all scopes the file is loaded into
@@ -516,6 +569,17 @@ class Program {
516
569
  }
517
570
  return result;
518
571
  }
572
+ /**
573
+ * Get the first found scope for a file.
574
+ */
575
+ getFirstScopeForFile(file) {
576
+ for (let key in this.scopes) {
577
+ let scope = this.scopes[key];
578
+ if (scope.hasFile(file)) {
579
+ return scope;
580
+ }
581
+ }
582
+ }
519
583
  getStatementsByName(name, originFile, namespaceName) {
520
584
  var _a, _b;
521
585
  let results = new Map();
@@ -574,17 +638,16 @@ class Program {
574
638
  }
575
639
  /**
576
640
  * Find all available completion items at the given position
577
- * @param pathAbsolute
578
- * @param lineIndex
579
- * @param columnIndex
641
+ * @param filePath can be a srcPath or a destPath
642
+ * @param position the position (line & column) where completions should be found
580
643
  */
581
- getCompletions(pathAbsolute, position) {
582
- let file = this.getFile(pathAbsolute);
644
+ getCompletions(filePath, position) {
645
+ let file = this.getFile(filePath);
583
646
  if (!file) {
584
647
  return [];
585
648
  }
586
649
  let result = [];
587
- if ((0, reflection_1.isBrsFile)(file) && file.isPositionNextToTokenKind(position, lexer_1.TokenKind.Callfunc)) {
650
+ if ((0, reflection_1.isBrsFile)(file) && file.isPositionNextToTokenKind(position, TokenKind_1.TokenKind.Callfunc)) {
588
651
  // is next to a @. callfunc invocation - must be an interface method
589
652
  for (const scope of this.getScopes().filter((s) => (0, reflection_1.isXmlScope)(s))) {
590
653
  let fileLinks = this.getStatementsForXmlFile(scope);
@@ -652,7 +715,8 @@ class Program {
652
715
  if (!file) {
653
716
  return null;
654
717
  }
655
- return Promise.resolve(file.getHover(position));
718
+ const hover = file.getHover(position);
719
+ return Promise.resolve(hover);
656
720
  }
657
721
  /**
658
722
  * Compute code actions for the given file and range
@@ -725,7 +789,7 @@ class Program {
725
789
  let myClass = file.getClassFromMReference(position, file.getTokenAt(position), functionScope);
726
790
  if (myClass) {
727
791
  for (let scope of this.getScopesForFile(myClass.file)) {
728
- let classes = scope.getClassHierarchy(myClass.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
792
+ let classes = scope.getClassHierarchy(myClass.item.getName(Parser_1.ParseMode.BrighterScript).toLowerCase());
729
793
  //and anything from any class in scope to a non m class
730
794
  for (let statement of [...classes].filter((i) => (0, reflection_1.isClassMethodStatement)(i.item))) {
731
795
  let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
@@ -796,12 +860,12 @@ class Program {
796
860
  //try to get sig help based on the name
797
861
  index = position.character;
798
862
  let currentToken = file.getTokenAt(position);
799
- if (currentToken && currentToken.kind !== lexer_1.TokenKind.Comment) {
800
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
863
+ if (currentToken && currentToken.kind !== TokenKind_1.TokenKind.Comment) {
864
+ name = file.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New]);
801
865
  if (!name) {
802
866
  //try the previous token, incase we're on a bracket
803
867
  currentToken = file.getPreviousToken(currentToken);
804
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
868
+ name = file.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New]);
805
869
  }
806
870
  if (name === null || name === void 0 ? void 0 : name.indexOf('.')) {
807
871
  let parts = name.split('.');
@@ -970,9 +1034,49 @@ class Program {
970
1034
  * This does not write anything to the file system.
971
1035
  */
972
1036
  getTranspiledFileContents(pathAbsolute) {
973
- let file = this.getFile(pathAbsolute);
974
- let result = file.transpile();
975
- return Object.assign(Object.assign({}, result), { pathAbsolute: file.pathAbsolute, pkgPath: file.pkgPath });
1037
+ return this._getTranspiledFileContents(this.getFile(pathAbsolute));
1038
+ }
1039
+ /**
1040
+ * Internal function used to transpile files.
1041
+ * This does not write anything to the file system
1042
+ */
1043
+ _getTranspiledFileContents(file, outputPath) {
1044
+ const editor = new AstEditor_1.AstEditor();
1045
+ this.plugins.emit('beforeFileTranspile', {
1046
+ file: file,
1047
+ outputPath: outputPath,
1048
+ editor: editor
1049
+ });
1050
+ //if we have any edits, assume the file needs to be transpiled
1051
+ if (editor.hasChanges) {
1052
+ //use the `editor` because it'll track the previous value for us and revert later on
1053
+ editor.setProperty(file, 'needsTranspiled', true);
1054
+ }
1055
+ //transpile the file
1056
+ const result = file.transpile();
1057
+ //generate the typedef if enabled
1058
+ let typedef;
1059
+ if ((0, reflection_1.isBrsFile)(file) && this.options.emitDefinitions) {
1060
+ typedef = file.getTypedef();
1061
+ }
1062
+ const event = {
1063
+ file: file,
1064
+ outputPath: outputPath,
1065
+ editor: editor,
1066
+ code: result.code,
1067
+ map: result.map,
1068
+ typedef: typedef
1069
+ };
1070
+ this.plugins.emit('afterFileTranspile', event);
1071
+ //undo all `editor` edits that may have been applied to this file.
1072
+ editor.undoAll();
1073
+ return {
1074
+ pathAbsolute: file.pathAbsolute,
1075
+ pkgPath: file.pkgPath,
1076
+ code: event.code,
1077
+ map: event.map,
1078
+ typedef: event.typedef
1079
+ };
976
1080
  }
977
1081
  async transpile(fileEntries, stagingFolderPath) {
978
1082
  // map fileEntries using their path as key, to avoid excessive "find()" operations
@@ -996,8 +1100,7 @@ class Program {
996
1100
  outputPath = (0, util_1.standardizePath) `${stagingFolderPath}/${outputPath}`;
997
1101
  return {
998
1102
  file: file,
999
- outputPath: outputPath,
1000
- editor: new AstEditor_1.AstEditor()
1103
+ outputPath: outputPath
1001
1104
  };
1002
1105
  });
1003
1106
  this.plugins.emit('beforeProgramTranspile', this, entries);
@@ -1006,32 +1109,22 @@ class Program {
1006
1109
  if ((0, reflection_1.isBrsFile)(entry.file) && entry.file.isTypedef) {
1007
1110
  return;
1008
1111
  }
1009
- this.plugins.emit('beforeFileTranspile', entry);
1010
1112
  const { file, outputPath } = entry;
1011
- //if we have any edits, assume the file needs to be transpiled
1012
- if (entry.editor.hasChanges) {
1013
- //use the `editor` because it'll track the previous value for us and revert later on
1014
- entry.editor.setProperty(file, 'needsTranspiled', true);
1015
- }
1016
- const result = file.transpile();
1113
+ const fileTranspileResult = this._getTranspiledFileContents(file, outputPath);
1017
1114
  //make sure the full dir path exists
1018
1115
  await fsExtra.ensureDir(path.dirname(outputPath));
1019
1116
  if (await fsExtra.pathExists(outputPath)) {
1020
1117
  throw new Error(`Error while transpiling "${file.pathAbsolute}". A file already exists at "${outputPath}" and will not be overwritten.`);
1021
1118
  }
1022
- const writeMapPromise = result.map ? fsExtra.writeFile(`${outputPath}.map`, result.map.toString()) : null;
1119
+ const writeMapPromise = fileTranspileResult.map ? fsExtra.writeFile(`${outputPath}.map`, fileTranspileResult.map.toString()) : null;
1023
1120
  await Promise.all([
1024
- fsExtra.writeFile(outputPath, result.code),
1121
+ fsExtra.writeFile(outputPath, fileTranspileResult.code),
1025
1122
  writeMapPromise
1026
1123
  ]);
1027
- if ((0, reflection_1.isBrsFile)(file) && this.options.emitDefinitions) {
1028
- const typedef = file.getTypedef();
1124
+ if (fileTranspileResult.typedef) {
1029
1125
  const typedefPath = outputPath.replace(/\.brs$/i, '.d.bs');
1030
- await fsExtra.writeFile(typedefPath, typedef);
1126
+ await fsExtra.writeFile(typedefPath, fileTranspileResult.typedef);
1031
1127
  }
1032
- this.plugins.emit('afterFileTranspile', entry);
1033
- //undo all `editor` edits that may have been applied to this file.
1034
- entry.editor.undoAll();
1035
1128
  });
1036
1129
  //if there's no bslib file already loaded into the program, copy it to the staging directory
1037
1130
  if (!this.getFileByPkgPath(bslibAliasedRokuModulesPkgPath) && !this.getFileByPkgPath((0, util_1.standardizePath) `source/bslib.brs`)) {
@@ -1059,7 +1152,7 @@ class Program {
1059
1152
  return files;
1060
1153
  }
1061
1154
  /**
1062
- * Find a list of files in the program that have a function with the given name (case INsensitive)
1155
+ * Find a list of files in the program that have a class with the given name (case INsensitive)
1063
1156
  */
1064
1157
  findFilesForClass(className) {
1065
1158
  const files = [];