@servicenow/sdk-build-plugins 2.0.1

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 (166) hide show
  1. package/dist/AttachmentPlugin.d.ts +253 -0
  2. package/dist/AttachmentPlugin.js +216 -0
  3. package/dist/AttachmentPlugin.js.map +1 -0
  4. package/dist/BusinessRulePlugin.d.ts +56 -0
  5. package/dist/BusinessRulePlugin.js +171 -0
  6. package/dist/BusinessRulePlugin.js.map +1 -0
  7. package/dist/CrossScopePrivilegePlugin.d.ts +22 -0
  8. package/dist/CrossScopePrivilegePlugin.js +42 -0
  9. package/dist/CrossScopePrivilegePlugin.js.map +1 -0
  10. package/dist/DefaultPlugin.d.ts +71 -0
  11. package/dist/DefaultPlugin.js +238 -0
  12. package/dist/DefaultPlugin.js.map +1 -0
  13. package/dist/IdPlugin.d.ts +17 -0
  14. package/dist/IdPlugin.js +45 -0
  15. package/dist/IdPlugin.js.map +1 -0
  16. package/dist/ListPlugin.d.ts +91 -0
  17. package/dist/ListPlugin.js +398 -0
  18. package/dist/ListPlugin.js.map +1 -0
  19. package/dist/PropertyPlugin.d.ts +122 -0
  20. package/dist/PropertyPlugin.js +165 -0
  21. package/dist/PropertyPlugin.js.map +1 -0
  22. package/dist/ScriptTemplatePlugin.d.ts +31 -0
  23. package/dist/ScriptTemplatePlugin.js +208 -0
  24. package/dist/ScriptTemplatePlugin.js.map +1 -0
  25. package/dist/UserPreferencePlugin.d.ts +16 -0
  26. package/dist/UserPreferencePlugin.js +30 -0
  27. package/dist/UserPreferencePlugin.js.map +1 -0
  28. package/dist/aclAndRole/AclPlugin.d.ts +117 -0
  29. package/dist/aclAndRole/AclPlugin.js +285 -0
  30. package/dist/aclAndRole/AclPlugin.js.map +1 -0
  31. package/dist/aclAndRole/RolePlugin.d.ts +58 -0
  32. package/dist/aclAndRole/RolePlugin.js +152 -0
  33. package/dist/aclAndRole/RolePlugin.js.map +1 -0
  34. package/dist/aclAndRole/Util.d.ts +3 -0
  35. package/dist/aclAndRole/Util.js +106 -0
  36. package/dist/aclAndRole/Util.js.map +1 -0
  37. package/dist/app/ApplicationMenuPlugin.d.ts +32 -0
  38. package/dist/app/ApplicationMenuPlugin.js +106 -0
  39. package/dist/app/ApplicationMenuPlugin.js.map +1 -0
  40. package/dist/atf/ATFComposer.d.ts +492 -0
  41. package/dist/atf/ATFComposer.js +2717 -0
  42. package/dist/atf/ATFComposer.js.map +1 -0
  43. package/dist/atf/TestPlugin.d.ts +31 -0
  44. package/dist/atf/TestPlugin.js +95 -0
  45. package/dist/atf/TestPlugin.js.map +1 -0
  46. package/dist/atf/index.d.ts +1 -0
  47. package/dist/atf/index.js +9 -0
  48. package/dist/atf/index.js.map +1 -0
  49. package/dist/db/ColumnPlugins.d.ts +278 -0
  50. package/dist/db/ColumnPlugins.js +112 -0
  51. package/dist/db/ColumnPlugins.js.map +1 -0
  52. package/dist/db/RecordPlugin.d.ts +208 -0
  53. package/dist/db/RecordPlugin.js +287 -0
  54. package/dist/db/RecordPlugin.js.map +1 -0
  55. package/dist/db/TablePlugin.d.ts +742 -0
  56. package/dist/db/TablePlugin.js +1249 -0
  57. package/dist/db/TablePlugin.js.map +1 -0
  58. package/dist/db/index.d.ts +3 -0
  59. package/dist/db/index.js +27 -0
  60. package/dist/db/index.js.map +1 -0
  61. package/dist/index.d.ts +16 -0
  62. package/dist/index.js +51 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/scriptedRESTAPI/RESTDeserializationUtils.d.ts +12 -0
  65. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +371 -0
  66. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +1 -0
  67. package/dist/scriptedRESTAPI/RESTSerializationUtils.d.ts +15 -0
  68. package/dist/scriptedRESTAPI/RESTSerializationUtils.js +177 -0
  69. package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +1 -0
  70. package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +144 -0
  71. package/dist/scriptedRESTAPI/RestApiPlugin.js +318 -0
  72. package/dist/scriptedRESTAPI/RestApiPlugin.js.map +1 -0
  73. package/dist/scriptedRESTAPI/RestSchemaUtils.d.ts +190 -0
  74. package/dist/scriptedRESTAPI/RestSchemaUtils.js +53 -0
  75. package/dist/scriptedRESTAPI/RestSchemaUtils.js.map +1 -0
  76. package/dist/scriptedRESTAPI/RestUtils.d.ts +75 -0
  77. package/dist/scriptedRESTAPI/RestUtils.js +469 -0
  78. package/dist/scriptedRESTAPI/RestUtils.js.map +1 -0
  79. package/dist/scripts/ClientScriptPlugin.d.ts +43 -0
  80. package/dist/scripts/ClientScriptPlugin.js +190 -0
  81. package/dist/scripts/ClientScriptPlugin.js.map +1 -0
  82. package/dist/scripts/scriptUtils.d.ts +15 -0
  83. package/dist/scripts/scriptUtils.js +83 -0
  84. package/dist/scripts/scriptUtils.js.map +1 -0
  85. package/dist/uxf/ExperiencePlugin.d.ts +22 -0
  86. package/dist/uxf/ExperiencePlugin.js +55 -0
  87. package/dist/uxf/ExperiencePlugin.js.map +1 -0
  88. package/dist/uxf/RoutesPlugin.d.ts +22 -0
  89. package/dist/uxf/RoutesPlugin.js +176 -0
  90. package/dist/uxf/RoutesPlugin.js.map +1 -0
  91. package/dist/uxf/UxfFormulaParser/cleanUxValue.d.ts +4 -0
  92. package/dist/uxf/UxfFormulaParser/cleanUxValue.js +65 -0
  93. package/dist/uxf/UxfFormulaParser/cleanUxValue.js.map +1 -0
  94. package/dist/uxf/UxfFormulaParser/grammerParser/api.d.ts +189 -0
  95. package/dist/uxf/UxfFormulaParser/grammerParser/api.js +158 -0
  96. package/dist/uxf/UxfFormulaParser/grammerParser/api.js.map +1 -0
  97. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.d.ts +13 -0
  98. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +604 -0
  99. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js.map +1 -0
  100. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.d.ts +12 -0
  101. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js +551 -0
  102. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js.map +1 -0
  103. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.d.ts +31 -0
  104. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +64 -0
  105. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js.map +1 -0
  106. package/dist/uxf/UxfFormulaParser/index.d.ts +3 -0
  107. package/dist/uxf/UxfFormulaParser/index.js +11 -0
  108. package/dist/uxf/UxfFormulaParser/index.js.map +1 -0
  109. package/dist/uxf/UxfFormulaParser/parser.d.ts +8 -0
  110. package/dist/uxf/UxfFormulaParser/parser.js +87 -0
  111. package/dist/uxf/UxfFormulaParser/parser.js.map +1 -0
  112. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.d.ts +8 -0
  113. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js +17 -0
  114. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js.map +1 -0
  115. package/dist/uxf/constants.d.ts +2 -0
  116. package/dist/uxf/constants.js +8 -0
  117. package/dist/uxf/constants.js.map +1 -0
  118. package/dist/uxf/index.d.ts +2 -0
  119. package/dist/uxf/index.js +11 -0
  120. package/dist/uxf/index.js.map +1 -0
  121. package/dist/uxf/tectonicIdGenerator.d.ts +12 -0
  122. package/dist/uxf/tectonicIdGenerator.js +102 -0
  123. package/dist/uxf/tectonicIdGenerator.js.map +1 -0
  124. package/license +9 -0
  125. package/package.json +42 -0
  126. package/src/AttachmentPlugin.ts +262 -0
  127. package/src/BusinessRulePlugin.ts +251 -0
  128. package/src/CrossScopePrivilegePlugin.ts +54 -0
  129. package/src/DefaultPlugin.ts +272 -0
  130. package/src/IdPlugin.ts +47 -0
  131. package/src/ListPlugin.ts +497 -0
  132. package/src/PropertyPlugin.ts +218 -0
  133. package/src/ScriptTemplatePlugin.ts +223 -0
  134. package/src/UserPreferencePlugin.ts +36 -0
  135. package/src/aclAndRole/AclPlugin.ts +410 -0
  136. package/src/aclAndRole/RolePlugin.ts +225 -0
  137. package/src/aclAndRole/Util.ts +104 -0
  138. package/src/app/ApplicationMenuPlugin.ts +158 -0
  139. package/src/atf/ATFComposer.ts +3356 -0
  140. package/src/atf/TestPlugin.ts +119 -0
  141. package/src/atf/index.ts +1 -0
  142. package/src/db/ColumnPlugins.ts +117 -0
  143. package/src/db/RecordPlugin.ts +391 -0
  144. package/src/db/TablePlugin.ts +1581 -0
  145. package/src/db/index.ts +3 -0
  146. package/src/index.ts +16 -0
  147. package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +410 -0
  148. package/src/scriptedRESTAPI/RESTSerializationUtils.ts +227 -0
  149. package/src/scriptedRESTAPI/RestApiPlugin.ts +438 -0
  150. package/src/scriptedRESTAPI/RestSchemaUtils.ts +72 -0
  151. package/src/scriptedRESTAPI/RestUtils.ts +507 -0
  152. package/src/scripts/ClientScriptPlugin.ts +251 -0
  153. package/src/scripts/scriptUtils.ts +81 -0
  154. package/src/uxf/ExperiencePlugin.ts +64 -0
  155. package/src/uxf/RoutesPlugin.ts +215 -0
  156. package/src/uxf/UxfFormulaParser/cleanUxValue.ts +73 -0
  157. package/src/uxf/UxfFormulaParser/grammerParser/api.js +166 -0
  158. package/src/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +606 -0
  159. package/src/uxf/UxfFormulaParser/grammerParser/grammarParser.js +551 -0
  160. package/src/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +65 -0
  161. package/src/uxf/UxfFormulaParser/index.ts +4 -0
  162. package/src/uxf/UxfFormulaParser/parser.ts +64 -0
  163. package/src/uxf/UxfFormulaParser/utils/getErrorMsg.ts +13 -0
  164. package/src/uxf/constants.ts +4 -0
  165. package/src/uxf/index.ts +2 -0
  166. package/src/uxf/tectonicIdGenerator.ts +81 -0
@@ -0,0 +1,497 @@
1
+ import { List } from '@servicenow/sdk-core/runtime/ui'
2
+ import {
3
+ Plugin,
4
+ PrimitiveData,
5
+ extractCallExpression,
6
+ generateCallExpressionExportForDocument,
7
+ getCallExpressionName,
8
+ getOrCreateEntitySourceFile,
9
+ linkDocument,
10
+ recordXml,
11
+ removeNode,
12
+ unloadBuilder,
13
+ getOrCreatePropertyAssignment,
14
+ setPropertyAssignmentValue,
15
+ getSysUpdateName,
16
+ XmlData,
17
+ writePropertyAsReference,
18
+ getPropertyAssignment,
19
+ addDefaultImportIfAbsent,
20
+ addNamedImportIfAbsent,
21
+ } from '@servicenow/sdk-build-core'
22
+ import { RecordXmlSchema } from './db/RecordPlugin'
23
+ import * as z from 'zod'
24
+ import { SyntaxKind, Node, PropertyAssignment, CallExpression, VariableDeclaration } from 'ts-morph'
25
+ import * as path from 'path'
26
+ import { Diagnostic } from '@servicenow/sdk-project'
27
+
28
+ const DEFAULT_VIEW = 'Default view'
29
+
30
+ type View = {
31
+ id: string
32
+ name: string
33
+ title: string
34
+ }
35
+
36
+ type TransformData = {
37
+ sys_id: string
38
+ view: View
39
+ list: Omit<List, 'columns'>
40
+ table: string
41
+ elements: SysUIListElementXml
42
+ }
43
+
44
+ export default Plugin({
45
+ name: 'List',
46
+ ownedTables: {
47
+ sys_ui_list: { diagnosticLevel: Diagnostic.Level.Warn },
48
+ },
49
+ extractors: {
50
+ entity: {
51
+ CallExpression(node, context) {
52
+ return extractCallExpression(List, 'list', node, context, (list) =>
53
+ context.keys.registerExplicitId('sys_ui_list', list.$id as string)
54
+ )
55
+ },
56
+ },
57
+ xml(xml) {
58
+ const result = SysUIListXmlSchema.safeParse(xml.data['record_update'])
59
+ if (!result.success) {
60
+ return undefined
61
+ }
62
+
63
+ const {
64
+ data: {
65
+ sys_ui_list: { sys_ui_list, sys_ui_list_element },
66
+ sys_metadata_delete,
67
+ },
68
+ } = result
69
+
70
+ //Delete only gets set on the top level sys_ui_list action attribute, not on sub nodes
71
+ const isDelete = result.data.sys_ui_list['@_action'] === 'DELETE'
72
+ const list = xmlElementToObject(sys_ui_list)
73
+ const elements = toArray(sys_ui_list_element).map(xmlElementToObject)
74
+ let updateName = list['sys_update_name']
75
+ // sys_update_name is not present on the list element, but is present on the delete element on delete payload
76
+ if (isDelete && sys_metadata_delete) {
77
+ const metadataDelete = xmlElementToObject(sys_metadata_delete)
78
+ updateName = metadataDelete['sys_update_name']
79
+ }
80
+
81
+ const viewName = sys_ui_list['view']['@_name']
82
+ const view = {
83
+ title: sys_ui_list['view']['@_display_value'],
84
+ name: viewName === 'NULL' ? 'default_view' : viewName,
85
+ id: sys_ui_list['view']['#text'],
86
+ }
87
+
88
+ return new XmlData(
89
+ {
90
+ updateName,
91
+ sys_id: list['sys_id'],
92
+ view,
93
+ elements,
94
+ table: list['name'],
95
+ },
96
+ xml.filePath,
97
+ 'list',
98
+ isDelete ? 'DELETE' : list['@_action']
99
+ )
100
+ },
101
+ },
102
+ composers: {
103
+ entity: {
104
+ list(entity, context) {
105
+ const node = entity.getNode()
106
+ const guid = entity.getGuid()
107
+ const data = entity.getValue()
108
+ const { view, columns, table } = data as List
109
+
110
+ const viewInfo: View = {
111
+ id:
112
+ view.$id === DEFAULT_VIEW
113
+ ? view.$id
114
+ : context.keys.registerExplicitId('sys_ui_view', view.$id as unknown as string),
115
+ name: view.data.name,
116
+ title: view.data.title,
117
+ }
118
+
119
+ return {
120
+ kind: 'list',
121
+ node,
122
+ guid: getListUpdateName(viewInfo, table),
123
+ data: {
124
+ id: guid,
125
+ table,
126
+ view: viewInfo,
127
+ elements: columns,
128
+ },
129
+ }
130
+ },
131
+ },
132
+ xml: {
133
+ list(xml) {
134
+ const { sys_id, view, elements, table } = xml.data as TransformData
135
+ return {
136
+ action: xml.action,
137
+ kind: xml.kind,
138
+ guid: xml.data['updateName'] as string,
139
+ data: {
140
+ id: sys_id,
141
+ table,
142
+ view,
143
+ elements,
144
+ },
145
+ }
146
+ },
147
+ },
148
+ },
149
+ transformers: {
150
+ list: {
151
+ CallExpression(document, context) {
152
+ const expressionName = getCallExpressionName(document.node)
153
+ if (expressionName !== List.name) {
154
+ return false
155
+ }
156
+
157
+ if (document.action === 'DELETE') {
158
+ removeNode(document.node)
159
+ return true
160
+ }
161
+ if (document.action !== 'INSERT_OR_UPDATE') {
162
+ return false
163
+ }
164
+ const [listArgument] = document.node.getArguments()
165
+ if (!Node.isObjectLiteralExpression(listArgument)) {
166
+ return false
167
+ }
168
+
169
+ const { elements, view, table } = document.xmlData as TransformData
170
+ updateView(context, listArgument, view)
171
+
172
+ //create the table reference for now, user will need to fix to proper import
173
+ const tableProperty = getOrCreatePropertyAssignment(listArgument, 'table', '')
174
+ setPropertyAssignmentValue(tableProperty, table)
175
+
176
+ const arrayElements = toArray(elements)
177
+
178
+ //update the columns
179
+ const columnsProperty = getOrCreatePropertyAssignment(listArgument, 'columns', '[]')
180
+ const columns = arrayElements.sort((a, b) => (a['position'] as number) - (b['position'] as number))
181
+ setColumnsList(columnsProperty, columns)
182
+
183
+ return true
184
+ },
185
+ },
186
+ },
187
+ generators: {
188
+ list(document, context) {
189
+ const { id } = document.data as any
190
+ return linkDocument(
191
+ document,
192
+ generateCallExpressionExportForDocument(
193
+ context,
194
+ {
195
+ sourceFile: getOrCreateEntitySourceFile(context, getSysUpdateName(document, 'sys_ui_list')),
196
+ moduleSpecifier: '@servicenow/sdk/core',
197
+ },
198
+ List,
199
+ { $id: id }
200
+ ).getExpressionIfKindOrThrow(SyntaxKind.CallExpression)
201
+ )
202
+ },
203
+ },
204
+ serializers: {
205
+ list(document, context) {
206
+ const {
207
+ id: listId,
208
+ elements: columns,
209
+ table,
210
+ view,
211
+ } = document.data as {
212
+ id: string
213
+ elements: Array<any>
214
+ table: string
215
+ view: View
216
+ }
217
+ const xmlBuilder = unloadBuilder(context)
218
+ /**
219
+ * Structure that needs to be built for sys_ui_list
220
+ * <sys_ui_list>
221
+ * <sys_ui_list_element>
222
+ * <sys_id/>
223
+ * </sys_ui_list_element>
224
+ * <sys_ui_list>
225
+ * <sys_id/>
226
+ * <view/>
227
+ * </sys_ui_list>
228
+ * </sys_ui_list>
229
+ */
230
+
231
+ const listBuilder = xmlBuilder.xml.addJsonObj('sys_ui_list', undefined, {
232
+ table: table,
233
+ view: view.id === DEFAULT_VIEW ? '' : view.name,
234
+ })
235
+
236
+ columns.forEach((column, index) => {
237
+ let elementProperties = column
238
+
239
+ if (typeof column === 'string') {
240
+ elementProperties = {
241
+ element: column,
242
+ position: index,
243
+ }
244
+ }
245
+
246
+ if (elementProperties.position === undefined) {
247
+ elementProperties.position = index
248
+ }
249
+
250
+ const childNode = recordXml(
251
+ listBuilder,
252
+ 'sys_ui_list_element',
253
+ context.keys.registerCompositeId('sys_ui_list_element', {
254
+ list: listId,
255
+ element: elementProperties.element,
256
+ })
257
+ )
258
+
259
+ childNode.field('list_id', listId, {
260
+ display_value: table,
261
+ view: view.id,
262
+ })
263
+
264
+ Object.entries(elementProperties)
265
+ .filter(([, v]) => typeof v !== 'undefined')
266
+ .forEach(([n, v]) => childNode.field(n, v))
267
+ })
268
+
269
+ const sysUIList = recordXml(listBuilder, 'sys_ui_list', listId)
270
+ sysUIList.addSysScope(context)
271
+ sysUIList.field('view', view.id, {
272
+ display_value: view.title,
273
+ name: view.name,
274
+ })
275
+ sysUIList.field('name', table)
276
+
277
+ return {
278
+ name: `${document.guid}.xml`,
279
+ directory: 'update',
280
+ content: xmlBuilder.end(),
281
+ }
282
+ },
283
+ },
284
+ })
285
+
286
+ /*
287
+ * Update the view property of the list argument
288
+ * If the view is the default view, write view as a reference from the core package
289
+ * If the view is a record, look for a record entity and write the view as a reference
290
+ *
291
+ * **Note:** View Cannot be updated using list layout on the instance,
292
+ * if the view is updated directly from sys_ui_list table, then update name changes and a new List entity is created
293
+ *
294
+ */
295
+ const updateView = (context, listArgument, view) => {
296
+ const viewAssignment = getPropertyAssignment(listArgument, 'view')
297
+ if (viewAssignment) {
298
+ return
299
+ }
300
+ const viewRecord = context.getDocument(view.id)
301
+
302
+ if (view.id === DEFAULT_VIEW) {
303
+ writePropertyAsReference(listArgument, 'view', '', view.name)
304
+ } else if (viewRecord) {
305
+ const nodeSource = listArgument.getSourceFile()
306
+ const viewNode = viewRecord.node
307
+ const viewPath = getImportPath(nodeSource.getFilePath(), viewNode.getSourceFile().getFilePath())
308
+
309
+ if (!viewPath) {
310
+ const identifier = getIdentifier(viewNode)
311
+ if (identifier) {
312
+ listArgument.addPropertyAssignment({ name: 'view', initializer: identifier })
313
+ return
314
+ }
315
+ context.logger.warn(
316
+ `Could not find identifier for view ${view.name}, consider adding one to use as a reference`
317
+ )
318
+ listArgument.addPropertyAssignment({ name: 'view', initializer: view.name })
319
+ return
320
+ }
321
+
322
+ // If the view record is not in the same file, add a reference to the view record
323
+ const viewExport = getExportTypeAndName(viewNode)
324
+
325
+ if (!viewExport.exported) {
326
+ context.logger.warn(
327
+ `View record is defined in ${path.normalize(viewNode.getSourceFile().getFilePath())} and is not exported`
328
+ )
329
+ listArgument.addPropertyAssignment({ name: 'view', initializer: view.name })
330
+ return
331
+ }
332
+
333
+ // Add a reference to the view record and add an import statement
334
+ if (viewExport.type === 'Default') {
335
+ listArgument.addPropertyAssignment({ name: 'view', initializer: view.name })
336
+ addDefaultImportIfAbsent(nodeSource, viewPath, view.name)
337
+ } else {
338
+ listArgument.addPropertyAssignment({ name: 'view', initializer: viewExport.identifier })
339
+ addNamedImportIfAbsent(context, nodeSource, viewPath, viewExport.identifier)
340
+ }
341
+ } else {
342
+ listArgument.addPropertyAssignment({ name: 'view', initializer: view.name })
343
+ }
344
+ }
345
+
346
+ type ExportsInfo =
347
+ | { exported: false; identifier?: string }
348
+ | { exported: true; identifier: string; type: 'Named' }
349
+ | { exported: true; type: 'Default' }
350
+
351
+ const getExportTypeAndName = (sourceNode: CallExpression): ExportsInfo => {
352
+ // const defaultExports = sourceNode.getSourceFile().getExportSymbols()
353
+ const parentNode = sourceNode.getParent()
354
+
355
+ // export declarations are blocked by fluent-diagnostics
356
+ switch (parentNode?.getKind()) {
357
+ case SyntaxKind.ExportAssignment:
358
+ return { exported: true, type: 'Default' }
359
+ case SyntaxKind.VariableDeclaration: {
360
+ const identifier = getIdentifier(parentNode as VariableDeclaration)!
361
+
362
+ const variableStmt = parentNode.getParentIfKind(SyntaxKind.VariableStatement)
363
+ const exportKeyword = variableStmt?.getFirstChildByKind(SyntaxKind.ExportKeyword)
364
+
365
+ if (exportKeyword) {
366
+ return { exported: true, type: 'Named', identifier }
367
+ }
368
+ return { exported: false, identifier }
369
+ }
370
+
371
+ case SyntaxKind.ExpressionStatement:
372
+ default:
373
+ return { exported: false }
374
+ }
375
+ }
376
+
377
+ const getIdentifier = (sourceNode: CallExpression | VariableDeclaration): string | undefined => {
378
+ const variableDeclaration =
379
+ sourceNode.getKind() === SyntaxKind.CallExpression
380
+ ? sourceNode.getParentIfKind(SyntaxKind.VariableDeclaration)
381
+ : sourceNode
382
+
383
+ return variableDeclaration?.getFirstChildByKind(SyntaxKind.Identifier)?.getText()
384
+ }
385
+
386
+ const getImportPath = (listSourcePath, viewSourcePath): string | undefined => {
387
+ if (listSourcePath === viewSourcePath) {
388
+ return
389
+ }
390
+
391
+ const viewFilePath = path.parse(viewSourcePath)
392
+ const listFilePath = path.parse(listSourcePath)
393
+ const relativePath = path.posix.join(path.relative(listFilePath.dir, viewFilePath.dir), viewFilePath.name)
394
+ // relativePath = relativePath.replace(path.extname(relativePath), '')
395
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`
396
+ }
397
+
398
+ const setColumnsList = (columnsProperty: PropertyAssignment, elements: SysUIListElementXml) => {
399
+ const [array] = columnsProperty.getChildrenOfKind(SyntaxKind.ArrayLiteralExpression)
400
+ let serializeValue: string = '[]'
401
+ if (array) {
402
+ const elementSyntaxList = array.getChildAtIndexIfKind(1, SyntaxKind.SyntaxList)
403
+
404
+ //If the columns are defined as objects or need to be expressed that way due to additional properties set on instance
405
+ if (
406
+ !!elementSyntaxList?.getChildrenOfKind(SyntaxKind.ObjectLiteralExpression).length ||
407
+ columnsNeedObjectLiterals(elements)
408
+ ) {
409
+ const newElements = elements.map((element, index) => {
410
+ let newValue: Record<string, unknown>
411
+
412
+ if (typeof element === 'string') {
413
+ newValue = element
414
+ } else {
415
+ newValue = {
416
+ element: element['element'],
417
+ position: element['position'] ?? index,
418
+ }
419
+ if (element['average_value']) {
420
+ newValue['average_value'] = true
421
+ }
422
+ if (element['max_value']) {
423
+ newValue['max_value'] = true
424
+ }
425
+ if (element['min_value']) {
426
+ newValue['min_value'] = true
427
+ }
428
+ if (element['sum']) {
429
+ newValue['sum'] = true
430
+ }
431
+ }
432
+
433
+ return newValue
434
+ })
435
+
436
+ serializeValue = JSON.stringify(newElements)
437
+ } else {
438
+ serializeValue = JSON.stringify(elements.map((e) => e['element']))
439
+ }
440
+ }
441
+ columnsProperty?.replaceWithText(`columns: ${serializeValue}`)
442
+ }
443
+
444
+ const columnsNeedObjectLiterals = (elements: SysUIListElementXml) => {
445
+ return elements.some((element) => {
446
+ return Object.keys(element)
447
+ .filter((key) => !key.startsWith('sys_') && !key.startsWith('@_'))
448
+ .some((key) => {
449
+ switch (key) {
450
+ case 'position':
451
+ case 'list_id':
452
+ case 'element':
453
+ return false
454
+ case 'average_value':
455
+ case 'max_value':
456
+ case 'sum':
457
+ case 'min_value':
458
+ return !!element[key]
459
+ default: //Non default properties are unknown so just return true
460
+ return true
461
+ }
462
+ })
463
+ })
464
+ }
465
+
466
+ const xmlElementToObject = (element: PrimitiveData) => {
467
+ return Object.entries(element).reduce((out, [k, v]) => {
468
+ out[k] = typeof v === 'string' ? v : v ? v['#text'] : ''
469
+ return out
470
+ }, {})
471
+ }
472
+
473
+ const getListUpdateName = (view: View, table: string) => {
474
+ const viewName = view.title === DEFAULT_VIEW ? 'null' : view.name
475
+ return `sys_ui_list_${table}_${viewName}` //This name matches the generated
476
+ }
477
+
478
+ const SysUIListXmlSchema = z.object({
479
+ sys_ui_list: z.object({
480
+ '@_action': z.union([z.literal('DELETE'), z.literal('INSERT_OR_UPDATE')]).optional(),
481
+ '@_table': z.string(),
482
+ '@_view': z.string(),
483
+ sys_ui_list: RecordXmlSchema,
484
+ sys_ui_list_element: z.union([z.array(RecordXmlSchema), RecordXmlSchema]),
485
+ }),
486
+ sys_metadata_delete: z
487
+ .object({
488
+ sys_update_name: z.object({
489
+ '#text': z.string(),
490
+ }),
491
+ })
492
+ .optional(),
493
+ })
494
+
495
+ const toArray = (value: unknown) => (Array.isArray(value) ? value : [value])
496
+
497
+ type SysUIListElementXml = z.infer<typeof RecordXmlSchema>[]