@servicenow/sdk-build-core 3.0.3 → 4.0.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 (252) hide show
  1. package/dist/app.d.ts +25 -0
  2. package/dist/app.js +8 -0
  3. package/dist/app.js.map +1 -0
  4. package/dist/compiler.d.ts +60 -0
  5. package/dist/compiler.js +320 -0
  6. package/dist/compiler.js.map +1 -0
  7. package/dist/compression.d.ts +7 -0
  8. package/dist/compression.js +79 -0
  9. package/dist/compression.js.map +1 -0
  10. package/dist/crypto.d.ts +1 -0
  11. package/dist/crypto.js +9 -0
  12. package/dist/crypto.js.map +1 -0
  13. package/dist/diagnostic.d.ts +41 -0
  14. package/dist/diagnostic.js +130 -0
  15. package/dist/diagnostic.js.map +1 -0
  16. package/dist/{plugins/Diagnostic.d.ts → fluent-diagnostic.d.ts} +3 -2
  17. package/dist/fluent-diagnostic.js +23 -0
  18. package/dist/fluent-diagnostic.js.map +1 -0
  19. package/dist/fluent-directive.d.ts +8 -0
  20. package/dist/fluent-directive.js +54 -0
  21. package/dist/fluent-directive.js.map +1 -0
  22. package/dist/fluent-file.d.ts +5 -0
  23. package/dist/fluent-file.js +15 -0
  24. package/dist/fluent-file.js.map +1 -0
  25. package/dist/formatter.d.ts +11 -0
  26. package/dist/formatter.js +77 -0
  27. package/dist/formatter.js.map +1 -0
  28. package/dist/fs.d.ts +174 -0
  29. package/dist/fs.js +313 -0
  30. package/dist/fs.js.map +1 -0
  31. package/dist/guid.d.ts +2 -0
  32. package/dist/{GUID.js → guid.js} +3 -6
  33. package/dist/guid.js.map +1 -0
  34. package/dist/index.d.ts +19 -5
  35. package/dist/index.js +19 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/json.d.ts +5 -0
  38. package/dist/json.js +43 -0
  39. package/dist/json.js.map +1 -0
  40. package/dist/keys-registry.d.ts +64 -0
  41. package/dist/keys-registry.js +339 -0
  42. package/dist/keys-registry.js.map +1 -0
  43. package/dist/logger.d.ts +8 -0
  44. package/dist/logger.js +17 -0
  45. package/dist/logger.js.map +1 -0
  46. package/dist/now-config.d.ts +348 -0
  47. package/dist/now-config.js +283 -0
  48. package/dist/now-config.js.map +1 -0
  49. package/dist/path.d.ts +3 -0
  50. package/dist/path.js +12 -0
  51. package/dist/path.js.map +1 -0
  52. package/dist/plugins/cache.d.ts +20 -0
  53. package/dist/plugins/cache.js +46 -0
  54. package/dist/plugins/cache.js.map +1 -0
  55. package/dist/plugins/context.d.ts +85 -0
  56. package/dist/plugins/{Context.js → context.js} +1 -1
  57. package/dist/plugins/context.js.map +1 -0
  58. package/dist/plugins/database.d.ts +27 -0
  59. package/dist/plugins/database.js +102 -0
  60. package/dist/plugins/database.js.map +1 -0
  61. package/dist/plugins/file.d.ts +10 -0
  62. package/dist/plugins/{behaviors/Arranger.js → file.js} +1 -1
  63. package/dist/plugins/file.js.map +1 -0
  64. package/dist/plugins/index.d.ts +9 -5
  65. package/dist/plugins/index.js +9 -6
  66. package/dist/plugins/index.js.map +1 -1
  67. package/dist/plugins/plugin.d.ts +478 -0
  68. package/dist/plugins/plugin.js +533 -0
  69. package/dist/plugins/plugin.js.map +1 -0
  70. package/dist/plugins/product.d.ts +15 -0
  71. package/dist/plugins/product.js +38 -0
  72. package/dist/plugins/product.js.map +1 -0
  73. package/dist/plugins/project.d.ts +25 -0
  74. package/dist/plugins/{behaviors/Generator.js → project.js} +1 -1
  75. package/dist/plugins/project.js.map +1 -0
  76. package/dist/plugins/shape.d.ts +424 -0
  77. package/dist/plugins/shape.js +1181 -0
  78. package/dist/plugins/shape.js.map +1 -0
  79. package/dist/plugins/time.d.ts +12 -0
  80. package/dist/plugins/time.js +84 -0
  81. package/dist/plugins/time.js.map +1 -0
  82. package/dist/plugins/usage.d.ts +11 -0
  83. package/dist/plugins/usage.js +26 -0
  84. package/dist/plugins/usage.js.map +1 -0
  85. package/dist/prettier/config-loader.d.ts +13 -0
  86. package/dist/prettier/config-loader.js +105 -0
  87. package/dist/prettier/config-loader.js.map +1 -0
  88. package/dist/telemetry/index.d.ts +25 -0
  89. package/dist/telemetry/index.js +18 -0
  90. package/dist/telemetry/index.js.map +1 -0
  91. package/dist/typescript.d.ts +293 -0
  92. package/dist/typescript.js +454 -0
  93. package/dist/typescript.js.map +1 -0
  94. package/dist/util/get-file-type.d.ts +2 -0
  95. package/dist/util/get-file-type.js +13 -0
  96. package/dist/util/get-file-type.js.map +1 -0
  97. package/dist/util/index.d.ts +2 -6
  98. package/dist/util/index.js +2 -6
  99. package/dist/util/index.js.map +1 -1
  100. package/dist/util/{Scope.js → is-sn-scope.js} +1 -1
  101. package/dist/util/is-sn-scope.js.map +1 -0
  102. package/dist/xml.d.ts +24 -0
  103. package/dist/xml.js +71 -0
  104. package/dist/xml.js.map +1 -0
  105. package/now.config.schema.json +336 -0
  106. package/package.json +22 -12
  107. package/src/app.ts +33 -0
  108. package/src/compiler.ts +384 -0
  109. package/src/compression.ts +93 -0
  110. package/src/crypto.ts +5 -0
  111. package/src/diagnostic.ts +108 -0
  112. package/src/{plugins/Diagnostic.ts → fluent-diagnostic.ts} +3 -10
  113. package/src/fluent-directive.ts +63 -0
  114. package/src/fluent-file.ts +13 -0
  115. package/src/formatter.ts +58 -0
  116. package/src/fs.ts +438 -0
  117. package/src/{GUID.ts → guid.ts} +2 -6
  118. package/src/index.ts +19 -5
  119. package/src/json.ts +20 -0
  120. package/src/keys-registry.ts +384 -0
  121. package/src/logger.ts +20 -0
  122. package/src/now-config.ts +337 -0
  123. package/src/path.ts +9 -0
  124. package/src/plugins/cache.ts +45 -0
  125. package/src/plugins/context.ts +93 -0
  126. package/src/plugins/database.ts +121 -0
  127. package/src/plugins/file.ts +19 -0
  128. package/src/plugins/index.ts +9 -5
  129. package/src/plugins/plugin.ts +995 -0
  130. package/src/plugins/product.ts +44 -0
  131. package/src/plugins/project.ts +39 -0
  132. package/src/plugins/shape.ts +1532 -0
  133. package/src/plugins/time.ts +108 -0
  134. package/src/plugins/usage.ts +26 -0
  135. package/src/prettier/config-loader.ts +130 -0
  136. package/src/telemetry/index.ts +27 -0
  137. package/src/typescript.ts +502 -0
  138. package/src/util/get-file-type.ts +11 -0
  139. package/src/util/index.ts +2 -6
  140. package/src/xml.ts +86 -0
  141. package/dist/GUID.d.ts +0 -2
  142. package/dist/GUID.js.map +0 -1
  143. package/dist/IncludePaths.d.ts +0 -25
  144. package/dist/IncludePaths.js +0 -97
  145. package/dist/IncludePaths.js.map +0 -1
  146. package/dist/Keys.d.ts +0 -32
  147. package/dist/Keys.js +0 -245
  148. package/dist/Keys.js.map +0 -1
  149. package/dist/TypeScript.d.ts +0 -5
  150. package/dist/TypeScript.js +0 -58
  151. package/dist/TypeScript.js.map +0 -1
  152. package/dist/XML.d.ts +0 -32
  153. package/dist/XML.js +0 -83
  154. package/dist/XML.js.map +0 -1
  155. package/dist/plugins/Context.d.ts +0 -190
  156. package/dist/plugins/Context.js.map +0 -1
  157. package/dist/plugins/Diagnostic.js +0 -28
  158. package/dist/plugins/Diagnostic.js.map +0 -1
  159. package/dist/plugins/Plugin.d.ts +0 -175
  160. package/dist/plugins/Plugin.js +0 -15
  161. package/dist/plugins/Plugin.js.map +0 -1
  162. package/dist/plugins/behaviors/Arranger.d.ts +0 -26
  163. package/dist/plugins/behaviors/Arranger.js.map +0 -1
  164. package/dist/plugins/behaviors/Composer.d.ts +0 -102
  165. package/dist/plugins/behaviors/Composer.js +0 -15
  166. package/dist/plugins/behaviors/Composer.js.map +0 -1
  167. package/dist/plugins/behaviors/Diagnostics.d.ts +0 -7
  168. package/dist/plugins/behaviors/Diagnostics.js +0 -3
  169. package/dist/plugins/behaviors/Diagnostics.js.map +0 -1
  170. package/dist/plugins/behaviors/Generator.d.ts +0 -21
  171. package/dist/plugins/behaviors/Generator.js.map +0 -1
  172. package/dist/plugins/behaviors/OwnedTables.d.ts +0 -6
  173. package/dist/plugins/behaviors/OwnedTables.js +0 -3
  174. package/dist/plugins/behaviors/OwnedTables.js.map +0 -1
  175. package/dist/plugins/behaviors/PostProcessor.d.ts +0 -5
  176. package/dist/plugins/behaviors/PostProcessor.js +0 -3
  177. package/dist/plugins/behaviors/PostProcessor.js.map +0 -1
  178. package/dist/plugins/behaviors/Serializer.d.ts +0 -30
  179. package/dist/plugins/behaviors/Serializer.js +0 -3
  180. package/dist/plugins/behaviors/Serializer.js.map +0 -1
  181. package/dist/plugins/behaviors/Transformer.d.ts +0 -23
  182. package/dist/plugins/behaviors/Transformer.js +0 -3
  183. package/dist/plugins/behaviors/Transformer.js.map +0 -1
  184. package/dist/plugins/behaviors/extractors/Data.d.ts +0 -119
  185. package/dist/plugins/behaviors/extractors/Data.js +0 -244
  186. package/dist/plugins/behaviors/extractors/Data.js.map +0 -1
  187. package/dist/plugins/behaviors/extractors/Extractors.d.ts +0 -63
  188. package/dist/plugins/behaviors/extractors/Extractors.js +0 -3
  189. package/dist/plugins/behaviors/extractors/Extractors.js.map +0 -1
  190. package/dist/plugins/behaviors/extractors/index.d.ts +0 -2
  191. package/dist/plugins/behaviors/extractors/index.js +0 -19
  192. package/dist/plugins/behaviors/extractors/index.js.map +0 -1
  193. package/dist/plugins/behaviors/index.d.ts +0 -9
  194. package/dist/plugins/behaviors/index.js +0 -26
  195. package/dist/plugins/behaviors/index.js.map +0 -1
  196. package/dist/plugins/util/CallExpression.d.ts +0 -5
  197. package/dist/plugins/util/CallExpression.js +0 -88
  198. package/dist/plugins/util/CallExpression.js.map +0 -1
  199. package/dist/plugins/util/CodeTransformation.d.ts +0 -95
  200. package/dist/plugins/util/CodeTransformation.js +0 -624
  201. package/dist/plugins/util/CodeTransformation.js.map +0 -1
  202. package/dist/plugins/util/ObjectLiteral.d.ts +0 -9
  203. package/dist/plugins/util/ObjectLiteral.js +0 -37
  204. package/dist/plugins/util/ObjectLiteral.js.map +0 -1
  205. package/dist/plugins/util/index.d.ts +0 -3
  206. package/dist/plugins/util/index.js +0 -20
  207. package/dist/plugins/util/index.js.map +0 -1
  208. package/dist/util/Debug.d.ts +0 -4
  209. package/dist/util/Debug.js +0 -20
  210. package/dist/util/Debug.js.map +0 -1
  211. package/dist/util/Directive.d.ts +0 -16
  212. package/dist/util/Directive.js +0 -107
  213. package/dist/util/Directive.js.map +0 -1
  214. package/dist/util/RuntimeTableSchema.d.ts +0 -5
  215. package/dist/util/RuntimeTableSchema.js +0 -58
  216. package/dist/util/RuntimeTableSchema.js.map +0 -1
  217. package/dist/util/Scope.js.map +0 -1
  218. package/dist/util/Util.d.ts +0 -1
  219. package/dist/util/Util.js +0 -12
  220. package/dist/util/Util.js.map +0 -1
  221. package/dist/util/XMLUploadParser.d.ts +0 -22
  222. package/dist/util/XMLUploadParser.js +0 -67
  223. package/dist/util/XMLUploadParser.js.map +0 -1
  224. package/src/IncludePaths.ts +0 -122
  225. package/src/Keys.ts +0 -274
  226. package/src/TypeScript.ts +0 -65
  227. package/src/XML.ts +0 -98
  228. package/src/plugins/Context.ts +0 -239
  229. package/src/plugins/Plugin.ts +0 -278
  230. package/src/plugins/behaviors/Arranger.ts +0 -42
  231. package/src/plugins/behaviors/Composer.ts +0 -125
  232. package/src/plugins/behaviors/Diagnostics.ts +0 -12
  233. package/src/plugins/behaviors/Generator.ts +0 -31
  234. package/src/plugins/behaviors/OwnedTables.ts +0 -5
  235. package/src/plugins/behaviors/PostProcessor.ts +0 -6
  236. package/src/plugins/behaviors/Serializer.ts +0 -40
  237. package/src/plugins/behaviors/Transformer.ts +0 -32
  238. package/src/plugins/behaviors/extractors/Data.ts +0 -332
  239. package/src/plugins/behaviors/extractors/Extractors.ts +0 -73
  240. package/src/plugins/behaviors/extractors/index.ts +0 -2
  241. package/src/plugins/behaviors/index.ts +0 -9
  242. package/src/plugins/util/CallExpression.ts +0 -110
  243. package/src/plugins/util/CodeTransformation.ts +0 -731
  244. package/src/plugins/util/ObjectLiteral.ts +0 -37
  245. package/src/plugins/util/index.ts +0 -3
  246. package/src/util/Debug.ts +0 -24
  247. package/src/util/Directive.ts +0 -123
  248. package/src/util/RuntimeTableSchema.ts +0 -44
  249. package/src/util/Util.ts +0 -7
  250. package/src/util/XMLUploadParser.ts +0 -90
  251. /package/dist/util/{Scope.d.ts → is-sn-scope.d.ts} +0 -0
  252. /package/src/util/{Scope.ts → is-sn-scope.ts} +0 -0
@@ -0,0 +1,384 @@
1
+ import { path } from './path'
2
+ import { isSupportedNode, type SupportedNode, ts } from './typescript'
3
+ import { Diagnostic } from './diagnostic'
4
+ import { FileSystem, TsMorphFileSystemWrapper } from './fs'
5
+ import { NOW_FILE_EXTENSION } from './fluent-file'
6
+ import type { Diagnostics } from './plugins/context'
7
+
8
+ export type CompilerOptions = ts.CompilerOptions
9
+ export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = {
10
+ noEmit: true,
11
+ lib: ['lib.es5.d.ts'],
12
+ target: ts.ScriptTarget.ES2023,
13
+ module: ts.ModuleKind.NodeNext,
14
+ esModuleInterop: true,
15
+ isolatedModules: true,
16
+ allowJs: true,
17
+ checkJs: false,
18
+ strict: true,
19
+ alwaysStrict: true,
20
+ exactOptionalPropertyTypes: true,
21
+ noEmitOnError: true,
22
+ noFallthroughCasesInSwitch: true,
23
+ noImplicitOverride: true,
24
+ noImplicitReturns: true,
25
+ noImplicitThis: true,
26
+ noImplicitAny: false,
27
+ noUncheckedIndexedAccess: true,
28
+ noUnusedLocals: true,
29
+ noUnusedParameters: true,
30
+ noPropertyAccessFromIndexSignature: true,
31
+ forceConsistentCasingInFileNames: true,
32
+ resolveJsonModule: true,
33
+ skipLibCheck: true,
34
+ skipDefaultLibCheck: true,
35
+ allowImportingTsExtensions: true,
36
+ }
37
+
38
+ const SUPPORTED_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.cts', '.cjs', '.mts', '.mjs', '.json']
39
+ const UNSUPPORTED_EXTENSIONS = ['.test.ts', '.test.js', '.d.ts']
40
+
41
+ export class Compiler extends ts.Project {
42
+ private readonly rootDir: string
43
+ private readonly sourceFileToRelativeModuleSpecifier: Record<string, string> = {}
44
+ private readonly generatedTableFilePath: string
45
+ private moduleProject: ts.Project | undefined
46
+
47
+ constructor(
48
+ private readonly fs: FileSystem,
49
+ {
50
+ rootDir = '/',
51
+ sourceFilePaths = [],
52
+ compilerOptions = DEFAULT_COMPILER_OPTIONS,
53
+ }: {
54
+ rootDir?: string
55
+ sourceFilePaths?: string[]
56
+ compilerOptions?: CompilerOptions
57
+ } = {}
58
+ ) {
59
+ super({
60
+ compilerOptions,
61
+ fileSystem: new TsMorphFileSystemWrapper(fs),
62
+ })
63
+
64
+ this.rootDir = rootDir
65
+ this.generatedTableFilePath = path.join(this.rootDir, '$$GENERATED$$_common_table.ts')
66
+ this.addGlobalTableDefinitionFile()
67
+ // TODO: move this somewhere better, it doesn't work when I put the file contents in packages/core/src/global
68
+ this.addGlobalModuleExtensions()
69
+ this.addFluentSourceFilesAtPaths(sourceFilePaths)
70
+ }
71
+
72
+ addGlobalTableDefinitionFile() {
73
+ const globalTableDefinitionContent = `
74
+ import '@servicenow/sdk/global'
75
+ import { Table } from '@servicenow/sdk/core'
76
+
77
+ declare global {
78
+ namespace Now {
79
+ namespace Internal {
80
+ namespace TableSchemas {
81
+ }
82
+
83
+ interface Tables {
84
+ }
85
+ }
86
+ }
87
+ }
88
+ `
89
+ this.createSourceFile(this.generatedTableFilePath, globalTableDefinitionContent, {
90
+ overwrite: true,
91
+ scriptKind: ts.ScriptKind.TS,
92
+ })
93
+ }
94
+
95
+ addGlobalModuleExtensions() {
96
+ const globalModuleDefinition = `
97
+ declare module '*.html' {
98
+ const content: string;
99
+ export default content;
100
+ }`
101
+
102
+ this.createSourceFile(path.join(this.rootDir, '$$GENERATED$$_module_extensions.ts'), globalModuleDefinition, {
103
+ overwrite: true,
104
+ scriptKind: ts.ScriptKind.TS,
105
+ })
106
+ }
107
+
108
+ /**
109
+ * Recursively adds any source files in the specified directory, and returns the added
110
+ * files. Glob patterns can be supplied to control which files are added. This will also
111
+ * resolve the dependencies of the added files. If the directory does not exist, an empty
112
+ * array will be returned.
113
+ */
114
+ addSourceFilesFromDirectory(
115
+ dir: string,
116
+ extensions: string[] = SUPPORTED_EXTENSIONS,
117
+ ignore_extensions: string[] = UNSUPPORTED_EXTENSIONS
118
+ ): ts.SourceFile[] {
119
+ const files = [] as ts.SourceFile[]
120
+
121
+ // Using the filesystem API directly instead of using ts-morph's directory APIs is a
122
+ // workaround for this issue: https://github.com/dsherret/ts-morph/issues/1554
123
+ FileSystem.traverseDirectory(this.fs, dir, {
124
+ extensions,
125
+ ignore_extensions,
126
+ visitor: (filePath: string) => {
127
+ files.push(this.addSourceFileAtPath(filePath))
128
+ },
129
+ })
130
+
131
+ return files
132
+ }
133
+
134
+ /**
135
+ * Recursively adds any .now.ts files in the specified directory, and returns the added
136
+ * files. Glob patterns can be supplied to control which files are added. This will also
137
+ * resolve the dependencies of the added files. If the directory does not exist, an empty
138
+ * array will be returned.
139
+ */
140
+ addFluentSourceFilesFromDirectory(dir: string): ts.SourceFile[] {
141
+ return this.addSourceFilesFromDirectory(dir, [NOW_FILE_EXTENSION])
142
+ }
143
+
144
+ addFluentSourceFilesAtPaths(paths: string[]): ts.SourceFile[] {
145
+ return this.addSourceFilesAtPaths(paths).filter((file) => file.getBaseName().endsWith(NOW_FILE_EXTENSION))
146
+ }
147
+
148
+ getFluentSourceFilesFromDirectory(dir: string) {
149
+ const directory = this.getDirectoryOrThrow(dir)
150
+ return directory.getDescendantSourceFiles().filter((file) => file.getBaseName().endsWith(NOW_FILE_EXTENSION))
151
+ }
152
+
153
+ getDiagnosticsForFluentFile(fileOrPath: string | ts.SourceFile): TypeScriptDiagnostic[] {
154
+ const file = fileOrPath instanceof ts.SourceFile ? fileOrPath : this.getSourceFileOrThrow(fileOrPath)
155
+ if (!file.getFilePath().endsWith(NOW_FILE_EXTENSION)) {
156
+ return []
157
+ }
158
+ return file
159
+ .getPreEmitDiagnostics()
160
+ .map((diagnostic) => {
161
+ try {
162
+ return new TypeScriptDiagnostic(diagnostic)
163
+ } catch (e) {
164
+ return undefined
165
+ }
166
+ })
167
+ .filter((d) => d) as TypeScriptDiagnostic[]
168
+ }
169
+
170
+ getOrCreateSourceFile(path: string, options?: ts.SourceFileCreateOptions): ts.SourceFile {
171
+ return this.getSourceFile(path) ?? this.createSourceFile(path, '', options)
172
+ }
173
+
174
+ async visitSupportedNodes(
175
+ node: ts.Node,
176
+ visitor: (node: SupportedNode) => void | Promise<void>,
177
+ unsupportedNodeVisitor?: (node: ts.Node) => void | Promise<void>
178
+ ): Promise<void> {
179
+ if (node.wasForgotten()) {
180
+ return
181
+ }
182
+
183
+ if (!isSupportedNode(node)) {
184
+ await unsupportedNodeVisitor?.(node)
185
+ return
186
+ }
187
+
188
+ // Surprisingly, forEachChild() is not the same as getChildren().forEach() so we need to
189
+ // collect all the children using forEachChild() and then iterate
190
+ const children: ts.Node[] = []
191
+ node.forEachChild((child) => {
192
+ children.push(child)
193
+ })
194
+
195
+ for (const child of children) {
196
+ await this.visitSupportedNodes(child, visitor, unsupportedNodeVisitor)
197
+ }
198
+
199
+ await visitor(node)
200
+ }
201
+
202
+ private getTableSchemaModule(sourceFile: ts.SourceFile) {
203
+ return sourceFile?.getModule('global')?.getModule('Now')?.getModule('Internal')?.getModule('TableSchemas')
204
+ }
205
+
206
+ private getTableInterface(sourceFile: ts.SourceFile) {
207
+ return sourceFile?.getModule('global')?.getModule('Now')?.getModule('Internal')?.getInterface('Tables')
208
+ }
209
+
210
+ interfaceExistsInGlobalDeclaration(tableName: string) {
211
+ const generatedTableFile = this.getSourceFile(this.generatedTableFilePath)
212
+ const tableSchema = this.getTableSchemaModule(generatedTableFile!)
213
+ return tableSchema?.getInterfaces().find((value) => value.getName() === tableName)
214
+ }
215
+
216
+ propertyExistsInGlobalDeclaration(tableName: string) {
217
+ const generatedTableFile = this.getSourceFile(this.generatedTableFilePath)
218
+ const tableInterface = this.getTableInterface(generatedTableFile!)
219
+ return tableInterface?.getProperties().find((value) => value.getName() === tableName)
220
+ }
221
+
222
+ importExistsInGlobalDeclaration(resolvedModuleSpecifier: string) {
223
+ const generatedTableFile = this.getSourceFile(this.generatedTableFilePath)
224
+ return generatedTableFile
225
+ ?.getImportDeclarations()
226
+ .find((value) => value.getModuleSpecifierValue() === resolvedModuleSpecifier)
227
+ }
228
+
229
+ getGeneratedTableFile() {
230
+ return this.getSourceFile(this.generatedTableFilePath)
231
+ }
232
+
233
+ addTableInterfacesToGlobalDeclaration(data: { interfaces; properties; imports; namedImports }) {
234
+ const { interfaces, properties, imports, namedImports } = data
235
+
236
+ const generatedTableFile = this.getSourceFile(this.generatedTableFilePath)
237
+ const tableSchema = this.getTableSchemaModule(generatedTableFile!)
238
+ const tableInterface = this.getTableInterface(generatedTableFile!)
239
+
240
+ tableSchema?.addInterfaces(interfaces)
241
+ tableInterface?.addProperties(properties)
242
+ generatedTableFile?.addImportDeclarations(Object.values(imports))
243
+
244
+ namedImports.forEach((i) => {
245
+ i.existingImport.addNamedImport({
246
+ name: i.name,
247
+ })
248
+ })
249
+
250
+ Object.keys(imports).forEach((filePath) => {
251
+ this.sourceFileToRelativeModuleSpecifier[filePath] = imports[filePath].moduleSpecifier
252
+ })
253
+ }
254
+
255
+ removeTableInterfaceFromGlobalDeclaration(sourceFile: string | ts.SourceFile) {
256
+ const filePath = sourceFile instanceof ts.SourceFile ? sourceFile.getFilePath() : sourceFile
257
+ const generatedTableFile = this.getSourceFile(this.generatedTableFilePath)
258
+ const relativeModuleSpecifier = this.sourceFileToRelativeModuleSpecifier[filePath]
259
+ if (relativeModuleSpecifier) {
260
+ const foundDecl = generatedTableFile
261
+ ?.getImportDeclarations()
262
+ .find((v) => v.getModuleSpecifierValue() === relativeModuleSpecifier)
263
+ if (foundDecl) {
264
+ const tables = foundDecl.getNamedImports().map((v) => v.getName())
265
+ const tableSchema = this.getTableSchemaModule(generatedTableFile!)
266
+ const tableInterface = this.getTableInterface(generatedTableFile!)
267
+ tables.forEach((table) => {
268
+ tableSchema?.getInterface(table)?.remove()
269
+ tableInterface?.getProperty(table)?.remove()
270
+ })
271
+ foundDecl.remove()
272
+ }
273
+ }
274
+ }
275
+
276
+ isGeneratedTableFile(sourceFile: string | ts.SourceFile): boolean {
277
+ const sourcePath = sourceFile instanceof ts.SourceFile ? sourceFile.getFilePath() : sourceFile
278
+ return this.generatedTableFilePath === sourcePath
279
+ }
280
+
281
+ private createModuleProject(fs: FileSystem, tsConfigPath?: string) {
282
+ return new ts.Project({
283
+ fileSystem: new TsMorphFileSystemWrapper(fs),
284
+ ...(tsConfigPath
285
+ ? {
286
+ tsConfigFilePath: tsConfigPath,
287
+ // We always want to emit files even if the user config says not to
288
+ compilerOptions: { noEmit: false },
289
+ }
290
+ : {
291
+ compilerOptions: {
292
+ target: ts.ScriptTarget.ES2022,
293
+ module: ts.ModuleKind.ES2022,
294
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
295
+ allowImportingTsExtensions: true,
296
+ declaration: false,
297
+ sourceMap: false,
298
+ skipDefaultLibCheck: true,
299
+ skipLibCheck: true,
300
+ },
301
+ }),
302
+ })
303
+ }
304
+
305
+ compileModule(
306
+ moduleFile: ts.SourceFile,
307
+ fs: FileSystem,
308
+ diagnostics: Diagnostics,
309
+ tsConfigPath?: string
310
+ ): string | undefined {
311
+ if (!this.moduleProject) {
312
+ this.moduleProject = this.createModuleProject(fs, tsConfigPath)
313
+ }
314
+ const modulePath = moduleFile.getFilePath()
315
+ let sourceFile: ts.SourceFile | undefined
316
+ if (tsConfigPath) {
317
+ sourceFile = this.moduleProject.getSourceFile(modulePath)
318
+
319
+ if (!sourceFile) {
320
+ diagnostics.error(moduleFile, `${path} not included in project. Check your tsconfig: ${tsConfigPath}`)
321
+ return undefined
322
+ }
323
+ } else {
324
+ sourceFile = this.moduleProject.createSourceFile(modulePath, moduleFile.getFullText(), {
325
+ overwrite: true,
326
+ scriptKind: ts.ScriptKind.TS,
327
+ })
328
+ }
329
+
330
+ sourceFile.getPreEmitDiagnostics().forEach((diagnostic) => {
331
+ try {
332
+ diagnostics.add(new TypeScriptDiagnostic(diagnostic))
333
+ } catch (e) {
334
+ //ignore
335
+ }
336
+ })
337
+
338
+ const emitResult = sourceFile.getEmitOutput()
339
+ emitResult.getDiagnostics().forEach((d) => {
340
+ try {
341
+ const diagnostic = new TypeScriptDiagnostic(d)
342
+ if (diagnostic.level === Diagnostic.Level.Error) {
343
+ diagnostics.add(diagnostic)
344
+ }
345
+ } catch (e) {
346
+ //ignore
347
+ }
348
+ })
349
+
350
+ const output = emitResult.getOutputFiles()[0]
351
+ if (!output) {
352
+ diagnostics.error(moduleFile, `Failed to get emit output for module: ${modulePath}`)
353
+ }
354
+
355
+ return output?.getText()
356
+ }
357
+ }
358
+
359
+ export class TypeScriptDiagnostic extends Diagnostic {
360
+ constructor(private readonly diagnostic: ts.Diagnostic) {
361
+ const message = diagnostic.getMessageText()
362
+ const messageText = typeof message === 'string' ? message : message.getMessageText()
363
+ const start = diagnostic.getStart() ?? 0
364
+ const end = start + (diagnostic.getLength() ?? 0)
365
+ const file = diagnostic.getSourceFile()
366
+
367
+ if (!file) {
368
+ throw new Error(`TypeScript diagnostic does not have a source file (Message: ${messageText})`)
369
+ }
370
+
371
+ super(
372
+ messageText,
373
+ file,
374
+ { start, end },
375
+ diagnostic.getCode(),
376
+ 'ts',
377
+ Diagnostic.Level.fromCategory(diagnostic.getCategory())
378
+ )
379
+ }
380
+
381
+ public override asTypeScriptDiagnostic(): ts.ts.Diagnostic {
382
+ return this.diagnostic.compilerObject
383
+ }
384
+ }
@@ -0,0 +1,93 @@
1
+ import { ZipDeflate, unzipSync, Zip, gzipSync } from 'fflate'
2
+ import { FileSystem, TsMorphFileSystemWrapper } from './fs'
3
+ import { path } from './path'
4
+
5
+ const ZIP_STATUS_FILE = 'status.json'
6
+
7
+ export const unzipAppPackage = async (
8
+ fs: FileSystem,
9
+ zipPath: string,
10
+ targetPath: string
11
+ ): Promise<{ files: string[] }> => {
12
+ const zipArrBuf = new Uint8Array(fs.readFileSync(zipPath))
13
+ const unzipResult = unzipSync(zipArrBuf)
14
+
15
+ //remove unnecessary package inventory file if present
16
+ if (unzipResult['package_inventory.csv']) {
17
+ delete unzipResult['package_inventory.csv']
18
+ }
19
+
20
+ // check the status of download operation
21
+ if (ZIP_STATUS_FILE in unzipResult) {
22
+ const response = JSON.parse(Buffer.from(unzipResult[ZIP_STATUS_FILE]).toString('utf8'))
23
+ if (response.status === 'error') {
24
+ throw new Error('Application download failed with error, ' + response.message)
25
+ }
26
+ delete unzipResult[ZIP_STATUS_FILE]
27
+ }
28
+
29
+ const files = Object.entries(unzipResult).map(([filePath, data]) => {
30
+ const fullPath = path.join(targetPath, filePath)
31
+
32
+ // Create directories if necessary
33
+ const dir = path.dirname(fullPath)
34
+ FileSystem.ensureDirSync(fs, dir)
35
+
36
+ // Write file content to the filesystem
37
+ fs.writeFileSync(fullPath, data)
38
+
39
+ return fullPath
40
+ })
41
+
42
+ return { files }
43
+ }
44
+
45
+ export const zipDirectory = async (fs: FileSystem, source: string, destination: string): Promise<string> => {
46
+ return new Promise<string>((resolve, reject) => {
47
+ try {
48
+ const z = new Zip()
49
+
50
+ const outputStream = FileSystem.createWriteStream(fs, destination)
51
+
52
+ z.ondata = (err, data, final) => {
53
+ if (err) {
54
+ outputStream.destroy()
55
+ reject(err)
56
+ } else {
57
+ outputStream.write(data)
58
+ if (final) {
59
+ outputStream.end()
60
+ }
61
+ }
62
+ }
63
+
64
+ outputStream.on('finish', () => {
65
+ resolve(destination)
66
+ })
67
+
68
+ outputStream.on('error', (error) => {
69
+ reject(error)
70
+ })
71
+
72
+ // Using our own glob for browser compatibility
73
+ const wrappedFs = new TsMorphFileSystemWrapper(fs)
74
+ const files = wrappedFs.globSync(['**'], {
75
+ cwd: source,
76
+ })
77
+
78
+ for (const file of files) {
79
+ const fp = path.normalize(file)
80
+ const data = fs.readFileSync(fp)
81
+ const zf = new ZipDeflate(fp.replace(source, ''), { level: 6 })
82
+ z.add(zf)
83
+ zf.push(data, true)
84
+ }
85
+
86
+ z.end()
87
+ } catch (err) {
88
+ reject(err)
89
+ }
90
+ })
91
+ }
92
+
93
+ export { gzipSync }
package/src/crypto.ts ADDED
@@ -0,0 +1,5 @@
1
+ // Node 20+ globally available Crypto Module -OR- Browser Crypto Module
2
+ const globalCrypto = <Crypto>(<any>globalThis.crypto)?.webcrypto || globalThis.crypto
3
+ // Globally available crypto (Node or Browser) -OR- Node Web Crypto (We can get rid of this in April 2025 after Node 18 is EOL)
4
+ // biome-ignore lint/style/noRestrictedImports: This is where we re-export our version of the module
5
+ export const crypto = globalCrypto || require('node:crypto').webcrypto
@@ -0,0 +1,108 @@
1
+ import * as lsp from 'vscode-languageserver'
2
+ import * as os from 'os'
3
+ import { ts } from './typescript'
4
+ import type { Logger } from './logger'
5
+
6
+ type PrintOptions = {
7
+ colors?: boolean
8
+ noThrow?: boolean
9
+ }
10
+
11
+ export abstract class Diagnostic {
12
+ constructor(
13
+ public readonly message: string,
14
+ public readonly file: ts.SourceFile,
15
+ public readonly position: { start: number; end: number },
16
+ public readonly code: ts.SyntaxKind,
17
+ public readonly source: 'fluent' | 'ts',
18
+ public readonly level: Diagnostic.Level = Diagnostic.Level.Error
19
+ ) {}
20
+
21
+ public abstract asTypeScriptDiagnostic(): ts.ts.Diagnostic
22
+
23
+ public asLspDiagnostic(): lsp.Diagnostic {
24
+ const start = this.file.compilerNode.getLineAndCharacterOfPosition(this.position.start)
25
+ const end = this.file.compilerNode.getLineAndCharacterOfPosition(this.position.end)
26
+
27
+ return {
28
+ message: this.message,
29
+ range: { start, end },
30
+ severity: Diagnostic.Level.toSeverity(this.level),
31
+ code: this.code,
32
+ source: this.source,
33
+ }
34
+ }
35
+
36
+ public getFormattedText(colors = true) {
37
+ const host: ts.ts.FormatDiagnosticsHost = {
38
+ getCurrentDirectory: () => this.file.getProject().getFileSystem().getCurrentDirectory(),
39
+ getCanonicalFileName: (fileName) => fileName,
40
+ getNewLine: () => os.EOL,
41
+ }
42
+
43
+ return colors
44
+ ? ts.ts.formatDiagnosticsWithColorAndContext([this.asTypeScriptDiagnostic()], host)
45
+ : ts.ts.formatDiagnostic(this.asTypeScriptDiagnostic(), host)
46
+ }
47
+
48
+ public print(logger: Logger, { noThrow = false, colors = true }: PrintOptions = {}): void {
49
+ if (this.level === Diagnostic.Level.Error) {
50
+ const errorMessage = this.getFormattedText(colors)
51
+ if (noThrow) {
52
+ logger.error(errorMessage)
53
+ } else {
54
+ logger.error('Diagnostic errors found in source files, fix errors and re-run transform/build')
55
+ throw new Error(errorMessage)
56
+ }
57
+ } else if (this.level === Diagnostic.Level.Warn) {
58
+ logger.warn(this.getFormattedText(colors))
59
+ }
60
+ }
61
+ }
62
+
63
+ export namespace Diagnostic {
64
+ export enum Level {
65
+ Error = 1,
66
+ Warn = 2,
67
+ Info = 3,
68
+ Hint = 4,
69
+ }
70
+
71
+ export namespace Level {
72
+ export function fromCategory(category: ts.DiagnosticCategory): Level {
73
+ return {
74
+ [ts.DiagnosticCategory.Error]: Diagnostic.Level.Error,
75
+ [ts.DiagnosticCategory.Warning]: Diagnostic.Level.Warn,
76
+ [ts.DiagnosticCategory.Message]: Diagnostic.Level.Info,
77
+ [ts.DiagnosticCategory.Suggestion]: Diagnostic.Level.Hint,
78
+ }[category]
79
+ }
80
+
81
+ export function toCategory(level: Level): ts.DiagnosticCategory {
82
+ return {
83
+ [Diagnostic.Level.Error]: ts.DiagnosticCategory.Error,
84
+ [Diagnostic.Level.Warn]: ts.DiagnosticCategory.Warning,
85
+ [Diagnostic.Level.Info]: ts.DiagnosticCategory.Message,
86
+ [Diagnostic.Level.Hint]: ts.DiagnosticCategory.Suggestion,
87
+ }[level]
88
+ }
89
+
90
+ export function fromSeverity(severity: lsp.DiagnosticSeverity): Level {
91
+ return {
92
+ [lsp.DiagnosticSeverity.Error]: Diagnostic.Level.Error,
93
+ [lsp.DiagnosticSeverity.Warning]: Diagnostic.Level.Warn,
94
+ [lsp.DiagnosticSeverity.Information]: Diagnostic.Level.Info,
95
+ [lsp.DiagnosticSeverity.Hint]: Diagnostic.Level.Hint,
96
+ }[severity]
97
+ }
98
+
99
+ export function toSeverity(level: Level): lsp.DiagnosticSeverity {
100
+ return {
101
+ [Diagnostic.Level.Error]: lsp.DiagnosticSeverity.Error,
102
+ [Diagnostic.Level.Warn]: lsp.DiagnosticSeverity.Warning,
103
+ [Diagnostic.Level.Info]: lsp.DiagnosticSeverity.Information,
104
+ [Diagnostic.Level.Hint]: lsp.DiagnosticSeverity.Hint,
105
+ }[level]
106
+ }
107
+ }
108
+ }
@@ -1,8 +1,9 @@
1
- import { Diagnostic, ts, tsc } from '@servicenow/sdk-project'
1
+ import { Diagnostic } from './diagnostic'
2
+ import type { ts, tsc } from './typescript'
2
3
 
3
4
  export class FluentDiagnostic extends Diagnostic {
4
5
  constructor(
5
- node: ts.Node,
6
+ public readonly node: ts.Node,
6
7
  message: string,
7
8
  { level = Diagnostic.Level.Error, code = node.getKind() }: { level?: Diagnostic.Level; code?: number } = {}
8
9
  ) {
@@ -20,11 +21,3 @@ export class FluentDiagnostic extends Diagnostic {
20
21
  }
21
22
  }
22
23
  }
23
-
24
- export function findObjectPropertyValue(callExpression: ts.CallExpression, field: string) {
25
- const property = callExpression
26
- .getFirstDescendantByKind(ts.SyntaxKind.ObjectLiteralExpression)
27
- ?.getProperty(field) as ts.PropertyAssignment
28
-
29
- return property?.getInitializer() || callExpression
30
- }
@@ -0,0 +1,63 @@
1
+ import { ts } from './typescript'
2
+
3
+ type FluentDirective = '@fluent-ignore' | '@fluent-disable-sync' | '@fluent-disable-sync-for-file'
4
+
5
+ type CacheEntry = Partial<Record<FluentDirective, boolean>>
6
+ const CACHE = new WeakMap<ts.Node, CacheEntry>()
7
+ function cacheGet(node: ts.Node): CacheEntry {
8
+ return CACHE.get(node) ?? CACHE.set(node, {}).get(node)!
9
+ }
10
+
11
+ /**
12
+ * Determines if the specified node or any of its ancestors have the specified directive in
13
+ * one of their leading comment ranges. Avoids regex for optimal performance.
14
+ */
15
+ export function hasDirective(node: ts.Node, directive: FluentDirective): boolean {
16
+ const cache = cacheGet(node)
17
+ if (cache[directive] !== undefined) {
18
+ return cache[directive]
19
+ }
20
+
21
+ for (const comment of getComments(node)) {
22
+ const text = comment.getText()
23
+ const idx = text.indexOf(directive)
24
+ if (idx >= 0) {
25
+ return (cache[directive] =
26
+ isDirectiveBoundary(text.charCodeAt(idx - 1)) &&
27
+ isDirectiveBoundary(text.charCodeAt(idx + directive.length)))
28
+ }
29
+ }
30
+
31
+ const parent = node.getParent()
32
+ return (cache[directive] = !!parent && hasDirective(parent, directive))
33
+ }
34
+
35
+ function getComments(node: ts.Node): ts.CommentRange[] | ts.CommentStatement[] {
36
+ if (!ts.Node.isSourceFile(node)) {
37
+ return node.getLeadingCommentRanges()
38
+ }
39
+
40
+ const top = node.getFirstChildIfKind(ts.SyntaxKind.SyntaxList)?.getFirstChild()
41
+ return ts.Node.isCommentStatement(top) ? [top] : []
42
+ }
43
+
44
+ const DIRECTIVE_CHARS = {
45
+ '-': (c: number) => c === 45,
46
+ '@': (c: number) => c === 64,
47
+ 'A-Z': (c: number) => c >= 65 && c <= 90,
48
+ 'a-z': (c: number) => c >= 97 && c <= 122,
49
+ }
50
+
51
+ function isDirectiveBoundary(code: number): boolean {
52
+ if (isNaN(code)) {
53
+ return true
54
+ }
55
+
56
+ for (const test of Object.values(DIRECTIVE_CHARS)) {
57
+ if (test(code)) {
58
+ return false
59
+ }
60
+ }
61
+
62
+ return true
63
+ }
@@ -0,0 +1,13 @@
1
+ import { ts } from './typescript'
2
+ import type { ProjectFile } from './plugins'
3
+
4
+ export const NOW_FILE_EXTENSION = '.now.ts'
5
+
6
+ export function isFluentNode(node: ts.Node): boolean {
7
+ return isFluentFile(node.getSourceFile())
8
+ }
9
+
10
+ export function isFluentFile(file: ts.SourceFile | ProjectFile | string): boolean {
11
+ const path = ts.Node.isNode(file) ? file.getFilePath() : typeof file === 'string' ? file : file.getPath()
12
+ return path.endsWith(NOW_FILE_EXTENSION)
13
+ }