@servicenow/sdk-build-plugins 2.1.4 → 2.2.2

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 (118) hide show
  1. package/dist/AttachmentPlugin.d.ts +6 -7
  2. package/dist/AttachmentPlugin.js +8 -6
  3. package/dist/AttachmentPlugin.js.map +1 -1
  4. package/dist/BusinessRulePlugin.d.ts +5 -5
  5. package/dist/BusinessRulePlugin.js +4 -4
  6. package/dist/BusinessRulePlugin.js.map +1 -1
  7. package/dist/CrossScopePrivilegePlugin.d.ts +5 -5
  8. package/dist/CrossScopePrivilegePlugin.js +1 -2
  9. package/dist/CrossScopePrivilegePlugin.js.map +1 -1
  10. package/dist/DefaultPlugin.d.ts +19 -11
  11. package/dist/DefaultPlugin.js +23 -51
  12. package/dist/DefaultPlugin.js.map +1 -1
  13. package/dist/HtmlTemplatePlugin.d.ts +2 -2
  14. package/dist/HtmlTemplatePlugin.js +3 -26
  15. package/dist/HtmlTemplatePlugin.js.map +1 -1
  16. package/dist/IdPlugin.d.ts +5 -4
  17. package/dist/IdPlugin.js +5 -9
  18. package/dist/IdPlugin.js.map +1 -1
  19. package/dist/JsonPlugin.d.ts +8 -3
  20. package/dist/JsonPlugin.js +6 -11
  21. package/dist/JsonPlugin.js.map +1 -1
  22. package/dist/ListPlugin.d.ts +6 -7
  23. package/dist/ListPlugin.js +17 -19
  24. package/dist/ListPlugin.js.map +1 -1
  25. package/dist/NowConfigPlugin.d.ts +1 -1
  26. package/dist/NowConfigPlugin.js +5 -31
  27. package/dist/NowConfigPlugin.js.map +1 -1
  28. package/dist/PackageJsonPlugin.d.ts +1 -1
  29. package/dist/PackageJsonPlugin.js +1 -1
  30. package/dist/PackageJsonPlugin.js.map +1 -1
  31. package/dist/PropertyPlugin.d.ts +5 -66
  32. package/dist/PropertyPlugin.js +6 -12
  33. package/dist/PropertyPlugin.js.map +1 -1
  34. package/dist/ScriptTemplatePlugin.d.ts +1 -1
  35. package/dist/ScriptTemplatePlugin.js +5 -5
  36. package/dist/ScriptTemplatePlugin.js.map +1 -1
  37. package/dist/ServerModulePlugin.d.ts +5 -6
  38. package/dist/ServerModulePlugin.js +10 -39
  39. package/dist/ServerModulePlugin.js.map +1 -1
  40. package/dist/UserPreferencePlugin.d.ts +1 -1
  41. package/dist/aclAndRole/AclPlugin.d.ts +44 -28
  42. package/dist/aclAndRole/AclPlugin.js +30 -33
  43. package/dist/aclAndRole/AclPlugin.js.map +1 -1
  44. package/dist/aclAndRole/RolePlugin.d.ts +5 -6
  45. package/dist/aclAndRole/RolePlugin.js +6 -7
  46. package/dist/aclAndRole/RolePlugin.js.map +1 -1
  47. package/dist/aclAndRole/Util.js +7 -30
  48. package/dist/aclAndRole/Util.js.map +1 -1
  49. package/dist/app/ApplicationMenuPlugin.d.ts +5 -5
  50. package/dist/app/ApplicationMenuPlugin.js +2 -3
  51. package/dist/app/ApplicationMenuPlugin.js.map +1 -1
  52. package/dist/atf/TestPlugin.d.ts +3 -3
  53. package/dist/atf/TestPlugin.js +1 -1
  54. package/dist/atf/TestPlugin.js.map +1 -1
  55. package/dist/db/ColumnPlugins.d.ts +85 -85
  56. package/dist/db/ColumnPlugins.js +17 -19
  57. package/dist/db/ColumnPlugins.js.map +1 -1
  58. package/dist/db/DocumentationPlugin.d.ts +6 -6
  59. package/dist/db/DocumentationPlugin.js +17 -49
  60. package/dist/db/DocumentationPlugin.js.map +1 -1
  61. package/dist/db/RecordPlugin.d.ts +7 -101
  62. package/dist/db/RecordPlugin.js +25 -24
  63. package/dist/db/RecordPlugin.js.map +1 -1
  64. package/dist/db/TablePlugin.d.ts +9 -610
  65. package/dist/db/TablePlugin.js +185 -84
  66. package/dist/db/TablePlugin.js.map +1 -1
  67. package/dist/scriptedRESTAPI/RESTDeserializationUtils.d.ts +0 -2
  68. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +17 -18
  69. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +1 -1
  70. package/dist/scriptedRESTAPI/RESTSerializationUtils.d.ts +1 -3
  71. package/dist/scriptedRESTAPI/RESTSerializationUtils.js +0 -1
  72. package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +1 -1
  73. package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +3 -4
  74. package/dist/scriptedRESTAPI/RestApiPlugin.js +7 -33
  75. package/dist/scriptedRESTAPI/RestApiPlugin.js.map +1 -1
  76. package/dist/scriptedRESTAPI/RestUtils.d.ts +1 -10
  77. package/dist/scriptedRESTAPI/RestUtils.js +11 -35
  78. package/dist/scriptedRESTAPI/RestUtils.js.map +1 -1
  79. package/dist/scripts/ClientScriptPlugin.d.ts +3 -4
  80. package/dist/scripts/ClientScriptPlugin.js +11 -37
  81. package/dist/scripts/ClientScriptPlugin.js.map +1 -1
  82. package/dist/uxf/ExperiencePlugin.d.ts +2 -2
  83. package/dist/uxf/RoutesPlugin.d.ts +2 -2
  84. package/dist/uxf/RoutesPlugin.js +2 -3
  85. package/dist/uxf/RoutesPlugin.js.map +1 -1
  86. package/dist/uxf/tectonicIdGenerator.d.ts +1 -1
  87. package/dist/uxf/tectonicIdGenerator.js +5 -28
  88. package/dist/uxf/tectonicIdGenerator.js.map +1 -1
  89. package/package.json +6 -8
  90. package/src/AttachmentPlugin.ts +5 -7
  91. package/src/BusinessRulePlugin.ts +12 -6
  92. package/src/CrossScopePrivilegePlugin.ts +2 -3
  93. package/src/DefaultPlugin.ts +22 -26
  94. package/src/HtmlTemplatePlugin.ts +2 -2
  95. package/src/IdPlugin.ts +6 -10
  96. package/src/JsonPlugin.ts +7 -12
  97. package/src/ListPlugin.ts +20 -22
  98. package/src/NowConfigPlugin.ts +10 -13
  99. package/src/PackageJsonPlugin.ts +1 -1
  100. package/src/PropertyPlugin.ts +8 -14
  101. package/src/ScriptTemplatePlugin.ts +1 -1
  102. package/src/ServerModulePlugin.ts +15 -20
  103. package/src/aclAndRole/AclPlugin.ts +37 -6
  104. package/src/aclAndRole/RolePlugin.ts +7 -9
  105. package/src/aclAndRole/Util.ts +2 -2
  106. package/src/app/ApplicationMenuPlugin.ts +3 -4
  107. package/src/atf/TestPlugin.ts +3 -3
  108. package/src/db/ColumnPlugins.ts +20 -22
  109. package/src/db/DocumentationPlugin.ts +7 -41
  110. package/src/db/RecordPlugin.ts +23 -26
  111. package/src/db/TablePlugin.ts +222 -115
  112. package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +18 -19
  113. package/src/scriptedRESTAPI/RESTSerializationUtils.ts +1 -1
  114. package/src/scriptedRESTAPI/RestApiPlugin.ts +15 -18
  115. package/src/scriptedRESTAPI/RestUtils.ts +7 -7
  116. package/src/scripts/ClientScriptPlugin.ts +10 -12
  117. package/src/uxf/RoutesPlugin.ts +2 -3
  118. package/src/uxf/tectonicIdGenerator.ts +3 -6
@@ -48,7 +48,6 @@ import {
48
48
  removeNode,
49
49
  stringify,
50
50
  transformFunctionArguments,
51
- writeCustomProperty,
52
51
  XmlData,
53
52
  FluentDiagnostic,
54
53
  PartialElements,
@@ -58,30 +57,17 @@ import {
58
57
  ObjectData,
59
58
  isSNScope,
60
59
  GUID,
60
+ mergeDataIntoObjectLiteral,
61
+ getPropertyAssignment,
62
+ mergeDataIntoObjectLiteralArray,
61
63
  } from '@servicenow/sdk-build-core'
62
64
  import { create } from 'xmlbuilder2'
63
65
  import { isArray, isEmpty, isObject, parseInt } from 'lodash'
64
66
  import { z } from 'zod'
65
67
  import { RecordPlugin, TextBooleanSchema, TextNumberSchema, TextStringSchema } from './RecordPlugin'
66
- import {
67
- SyntaxKind,
68
- Node,
69
- ts,
70
- ObjectLiteralExpression,
71
- CallExpression,
72
- Structure,
73
- SourceFile,
74
- ImportDeclarationStructure,
75
- PropertySignatureStructure,
76
- InterfaceDeclarationStructure,
77
- ImportSpecifierStructure,
78
- OptionalKind,
79
- ImportDeclaration,
80
- } from 'ts-morph'
81
- import { Diagnostic } from '@servicenow/sdk-project'
68
+ import { Diagnostic, ts, tsc } from '@servicenow/sdk-project'
82
69
  import { formatScript } from '../ScriptTemplatePlugin'
83
70
  import * as os from 'os'
84
- import * as path from 'path'
85
71
  import { XMLBuilder } from 'xmlbuilder2/lib/interfaces'
86
72
  import { getLabelDiagnostics } from './DBUtils'
87
73
  import { ModuleFunctionData } from '../ServerModulePlugin'
@@ -94,10 +80,10 @@ import { ModuleFunctionData } from '../ServerModulePlugin'
94
80
  * - `restricted`: 2
95
81
  *
96
82
  */
97
- export const callerAccess = ['none', 'tracking', 'restricted'] as const
83
+ const callerAccess = ['none', 'tracking', 'restricted'] as const
98
84
 
99
85
  // Basic info needed for table references
100
- export const TableSchemaFull = z.object({
86
+ const TableSchemaFull = z.object({
101
87
  /** lowercase, one word name. Numbers, letters and underscores only */
102
88
  name: z.string(),
103
89
  /** Object literal of column names with <type>Column() calls as values.
@@ -158,7 +144,10 @@ export const TableSchemaFull = z.object({
158
144
  z.object({
159
145
  name: z.string(),
160
146
  unique: z.boolean(),
161
- element: z.string(),
147
+ element: z
148
+ .string()
149
+ .or(z.array(z.string()))
150
+ .transform((el) => (isArray(el) ? el : [el])),
162
151
  })
163
152
  )
164
153
  .optional(),
@@ -169,7 +158,7 @@ const componentTables = ['sys_dictionary', 'sys_choice', 'sys_documentation']
169
158
  type IndexType = {
170
159
  name: string
171
160
  unique: boolean
172
- element: string
161
+ element: string[] | string
173
162
  }
174
163
 
175
164
  // Everything that goes into a table
@@ -248,7 +237,7 @@ const DBObjectIncomingAttributes = z.object({
248
237
  })
249
238
 
250
239
  // Attributes in the element='collection' tag of bootstrap xml
251
- export const TableSchemaBootstrap = z.object({
240
+ const TableSchemaBootstrap = z.object({
252
241
  name: z.string(),
253
242
  extends: z.string().optional(),
254
243
  label: z.string().max(80, 'Cannot exceed 80 characters').or(z.array(z.any())).optional(),
@@ -325,7 +314,7 @@ const DBObjectIncoming = z.object({
325
314
  sys_db_object: z.record(z.any()),
326
315
  })
327
316
 
328
- export const SysDocumentation = z.object({
317
+ const SysDocumentation = z.object({
329
318
  sys_documentation: z.object({
330
319
  element: TextStringSchema.optional(),
331
320
  label: TextStringSchema.optional(),
@@ -341,7 +330,7 @@ export const SysDocumentation = z.object({
341
330
  })
342
331
 
343
332
  // Attributes from element=table in bootstrap.xml
344
- export const TableSchemaBootstrapAttributes = z
333
+ const TableSchemaBootstrapAttributes = z
345
334
  .object({
346
335
  '@_name': z.string(),
347
336
  '@_extends': z.string().optional(),
@@ -391,12 +380,24 @@ export const TableSchemaBootstrapAttributes = z
391
380
  const IndexSchemaBootstrapAttributes = z.object({
392
381
  '@_name': z.string(),
393
382
  '@_unique': BooleanFromString,
394
- element: z.object({
395
- '@_name': z.string(),
396
- }),
383
+ element: z
384
+ .object({ '@_name': z.string() })
385
+ .or(
386
+ z.array(
387
+ z.object({
388
+ '@_name': z.string(),
389
+ })
390
+ )
391
+ )
392
+ .transform((val) => {
393
+ if (isArray(val)) {
394
+ return val
395
+ }
396
+ return [val]
397
+ }),
397
398
  })
398
399
 
399
- export const ColumnSchemaBootstrapAttributes = TableSchemaBootstrapAttributes.omit({ '@_extends': true })
400
+ const ColumnSchemaBootstrapAttributes = TableSchemaBootstrapAttributes.omit({ '@_extends': true })
400
401
  .extend({
401
402
  '@_choice': z.coerce.number().optional(),
402
403
  '@_active': BooleanFromString,
@@ -446,11 +447,11 @@ export default Plugin({
446
447
  // TODO: Cast to `any` is temporary workaround for the circular nature of Table entities
447
448
  CallExpression: (node, context) => {
448
449
  const result = extractCallExpression(Table, 'table', node, context, (table) => table.name)
449
- if (!result.handled || !(0 in result.data)) {
450
+ if (!result.handled || !result.data) {
450
451
  return result
451
452
  }
452
453
 
453
- const data = result.data[0]
454
+ const data = result.data
454
455
  const diagnostics = result.diagnostics
455
456
  const scopeName = context.app.config.scope
456
457
  const scopeRegex = new RegExp(`^${scopeName}_`)
@@ -470,7 +471,7 @@ export default Plugin({
470
471
  }
471
472
 
472
473
  if (!scopeMatches) {
473
- const nameNode = tableName.getNode().asKindOrThrow(SyntaxKind.StringLiteral)
474
+ const nameNode = tableName.getNode().asKindOrThrow(ts.SyntaxKind.StringLiteral)
474
475
  if (nameNode.getParent().getKindName() !== 'AsExpression') {
475
476
  diagnostics.push(
476
477
  new FluentDiagnostic(
@@ -484,7 +485,7 @@ export default Plugin({
484
485
  }
485
486
 
486
487
  const schema = data.getProperty('schema')
487
- const schemaNode = schema.getNode().asKindOrThrow(SyntaxKind.ObjectLiteralExpression)
488
+ const schemaNode = schema.getNode().asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
488
489
  for (const columnName in schema.getValue()) {
489
490
  const columnNode = schemaNode.getPropertyOrThrow(columnName)
490
491
  if (
@@ -511,7 +512,7 @@ export default Plugin({
511
512
  }
512
513
  }
513
514
 
514
- const declaration = node.getParentIfKind(SyntaxKind.VariableDeclaration)
515
+ const declaration = node.getParentIfKind(ts.SyntaxKind.VariableDeclaration)
515
516
  if (
516
517
  !declaration ||
517
518
  !declaration.isExported() ||
@@ -528,7 +529,7 @@ export default Plugin({
528
529
  }
529
530
 
530
531
  diagnostics.push(...getLabelDiagnostics(data))
531
- return { handled: true, data: [data], diagnostics }
532
+ return { handled: true, data, diagnostics }
532
533
  },
533
534
  },
534
535
  },
@@ -622,7 +623,10 @@ export default Plugin({
622
623
  if (idxData.success) {
623
624
  idxElements.push({
624
625
  name: idxData.data['@_name'],
625
- element: idxData.data.element['@_name'],
626
+ element: idxData.data.element.reduce((arr, curr) => {
627
+ arr.push(curr['@_name'])
628
+ return arr
629
+ }, [] as string[]),
626
630
  unique: idxData.data['@_unique'] ?? false,
627
631
  })
628
632
  }
@@ -740,15 +744,12 @@ export default Plugin({
740
744
  },
741
745
  Table,
742
746
  {}
743
- ).getInitializerIfKindOrThrow(SyntaxKind.CallExpression)
747
+ ).getInitializerIfKindOrThrow(ts.SyntaxKind.CallExpression)
744
748
  )
745
749
  },
746
750
 
747
751
  record(document, context) {
748
- if (
749
- document.data?.['table'] !== 'sys_documentation' &&
750
- document.data?.['table'] !== 'ua_table_licensing_config'
751
- ) {
752
+ if (document.data?.['table'] !== 'sys_documentation') {
752
753
  return
753
754
  }
754
755
 
@@ -803,7 +804,7 @@ export default Plugin({
803
804
  return false
804
805
  }
805
806
  const [args] = document.node.getArguments()
806
- if (!Node.isObjectLiteralExpression(args)) {
807
+ if (!ts.Node.isObjectLiteralExpression(args)) {
807
808
  return false
808
809
  }
809
810
 
@@ -850,34 +851,21 @@ export default Plugin({
850
851
  }
851
852
 
852
853
  if (!isEmpty(db_data)) {
853
- const actions = getActionsArray({ create_access, delete_access, update_access, read_access })
854
- if (actions.length > 0) {
855
- writeCustomProperty(args, 'actions', '[]', stringify(actions))
856
- } else {
857
- const actionsProp = getOrCreatePropertyAssignment(args, 'actions', '[]')
858
- actionsProp.remove()
859
- }
854
+ rest.actions = getActionsArray(xmlData.dbObjectData)
860
855
  }
861
856
 
862
857
  if (!rest.attributes) {
863
858
  delete rest.attributes
864
859
  }
865
860
 
866
- if (xmlData.bootstrapData && xmlData.bootstrapData.index) {
867
- const indexProp = getOrCreatePropertyAssignment(args, 'index', '[]')
868
- indexProp.setInitializer(stringify(xmlData.bootstrapData.index))
869
- if (xmlData.bootstrapData.index.length === 0) {
870
- indexProp.remove()
871
- }
872
- }
873
-
874
861
  transformFunctionArguments(document.node, Table as any, rest)
875
862
 
876
863
  // Handle columns
877
864
  const incomingColumns = xmlData.bootstrapData ? xmlData.bootstrapData.columns : undefined
878
865
 
879
866
  updateDisplayValue(document.node, columns, incomingColumns)
880
- transformColumns(incomingColumns, args)
867
+ transformColumns(columns, incomingColumns, args)
868
+ transformIndexes(xmlData, args)
881
869
  return true
882
870
  },
883
871
  },
@@ -885,14 +873,14 @@ export default Plugin({
885
873
  postProcessors: {
886
874
  entities(entities, context) {
887
875
  const { interfaces, properties, imports, namedImports } = {
888
- interfaces: [] as OptionalKind<InterfaceDeclarationStructure>[],
889
- properties: [] as OptionalKind<PropertySignatureStructure>[],
890
- imports: {} as Record<string, OptionalKind<ImportDeclarationStructure>>,
891
- namedImports: [] as (OptionalKind<ImportSpecifierStructure> & {
892
- existingImport: ImportDeclaration
876
+ interfaces: [] as ts.OptionalKind<ts.InterfaceDeclarationStructure>[],
877
+ properties: [] as ts.OptionalKind<ts.PropertySignatureStructure>[],
878
+ imports: {} as Record<string, ts.OptionalKind<ts.ImportDeclarationStructure>>,
879
+ namedImports: [] as (ts.OptionalKind<ts.ImportSpecifierStructure> & {
880
+ existingImport: ts.ImportDeclaration
893
881
  })[],
894
882
  }
895
- const generatedTableFile: SourceFile | undefined = context.compiler.getGeneratedTableFile()
883
+ const generatedTableFile: ts.SourceFile | undefined = context.compiler.getGeneratedTableFile()
896
884
  entities
897
885
  .filter((entity) => entity.getKind() === 'table')
898
886
  .forEach((table) => {
@@ -916,7 +904,7 @@ export default Plugin({
916
904
  })
917
905
  }
918
906
 
919
- const filePath = path.normalize(table.getNode().getSourceFile().getFilePath())
907
+ const filePath = table.getNode().getSourceFile().getFilePath()
920
908
  const resolvedModuleSpecifier = generatedTableFile?.getRelativePathAsModuleSpecifierTo(
921
909
  filePath
922
910
  ) as string
@@ -954,10 +942,32 @@ export default Plugin({
954
942
  },
955
943
  })
956
944
 
945
+ function transformIndexes(xmlData, args) {
946
+ if (xmlData.bootstrapData?.index?.length > 0) {
947
+ // find the correct object literal
948
+ const indexProp = getPropertyAssignment(args, 'index')
949
+ if (indexProp) {
950
+ mergeDataIntoObjectLiteralArray(
951
+ indexProp.getFirstChildByKindOrThrow(ts.SyntaxKind.ArrayLiteralExpression),
952
+ Object.values(xmlData.bootstrapData?.index),
953
+ 'name'
954
+ )
955
+ } else {
956
+ const newIndexProp = getOrCreatePropertyAssignment(args, 'index', '[]')
957
+ newIndexProp.setInitializer(stringify(xmlData.bootstrapData.index))
958
+ }
959
+ } else {
960
+ const indexProp = getPropertyAssignment(args, 'index')
961
+ if (indexProp) {
962
+ indexProp.remove()
963
+ }
964
+ }
965
+ }
966
+
957
967
  function generateCallExpressionExportForDocument<const A extends unknown[]>(
958
968
  context: Context,
959
969
  info: {
960
- sourceFile: SourceFile
970
+ sourceFile: ts.SourceFile
961
971
  moduleSpecifier: string
962
972
  exportName: string
963
973
  },
@@ -978,7 +988,7 @@ function composeAttributes(attributes: Record<string, any> | undefined) {
978
988
  .join(',')
979
989
  }
980
990
 
981
- function updateDisplayValue(node: CallExpression, columns: ColumnSchema, incomingColumns: ColumnSchema) {
991
+ function updateDisplayValue(node: ts.CallExpression, columns: ColumnSchema, incomingColumns: ColumnSchema) {
982
992
  const display = Object.values(columns ?? {}).filter((col: any) => col.display)
983
993
  if (display) {
984
994
  const displayName = Object.values(incomingColumns ?? {}).find((c) => c.display)
@@ -1159,40 +1169,131 @@ function mapNames(schema: any) {
1159
1169
 
1160
1170
  let schemaPosition = 4
1161
1171
 
1162
- function transformColumns(incoming_schema: ColumnSchema, args: ObjectLiteralExpression) {
1172
+ function transformColumns(columns: ColumnSchema, incomingSchema: ColumnSchema, args: ts.ObjectLiteralExpression) {
1163
1173
  const schemaProperty = getOrCreatePropertyAssignment(args, 'schema', '{}')
1164
1174
  const schemaLiteral = schemaProperty.getChildrenOfKind(ts.SyntaxKind.ObjectLiteralExpression)[0]
1165
1175
  if (!schemaLiteral) {
1166
1176
  return
1167
1177
  }
1168
1178
 
1169
- const keys = Object.keys(incoming_schema ?? {})
1179
+ const keys = Object.keys(columns ?? {})
1170
1180
  if (keys.length === 0) {
1171
1181
  return
1172
1182
  }
1173
1183
 
1174
1184
  //TODO:: need to come up with a way to format script more accurately
1175
1185
  schemaPosition = schemaLiteral.getSourceFile().getLineAndColumnAtPos(schemaLiteral.getStart()).column
1176
- const assignments: ts.PropertyAssignment[] = []
1186
+ const existingColumns = {}
1187
+ schemaLiteral.getProperties().forEach((property) => {
1188
+ const propertyAssignment = property.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
1189
+ const name = propertyAssignment.getFirstChildByKindOrThrow(ts.SyntaxKind.Identifier).getText()
1190
+ const callExp = propertyAssignment.getFirstChildByKindOrThrow(ts.SyntaxKind.CallExpression)
1191
+ const callExpType = callExp.getFirstChildByKindOrThrow(ts.SyntaxKind.Identifier).getText()
1192
+ const objectLiteral = callExp.getFirstChildByKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
1193
+ existingColumns[name] = {
1194
+ node: propertyAssignment,
1195
+ type: callExpType,
1196
+ objectLiteral,
1197
+ }
1198
+
1199
+ if (!incomingSchema[name]) {
1200
+ // Full bootstrap.xml does not have this existing column so remove it
1201
+ removeNode(propertyAssignment)
1202
+ }
1203
+ })
1204
+
1177
1205
  keys.forEach((key) => {
1178
- const data = incoming_schema[key]
1179
- const callExpFromType = getCallExpressionFromType(data.type)
1180
- const transformedColumn = transformColumnData(data, callExpFromType)
1181
- const callExp = generateColumnExpression(callExpFromType, {
1182
- ...transformedColumn,
1183
- choices: data.choice_elements,
1184
- })
1185
- assignments.push(ts.factory.createPropertyAssignment(createPropertyIdentifier(data.name), callExp))
1206
+ const partialData = columns[key]
1207
+ if (!existingColumns[key]) {
1208
+ // This column doesn't exist so let's make it
1209
+ const callExpFromType = getCallExpressionFromType(partialData.type)
1210
+ createNewColumn(schemaLiteral, callExpFromType.name, partialData)
1211
+ } else {
1212
+ const existingColumn = existingColumns[key]
1213
+ if (partialData.type && existingColumn.type !== partialData.type) {
1214
+ // Switching the column type so regenerate the CallExpression and add all incoming data
1215
+ removeNode(existingColumn.node)
1216
+ const callExpFromType = getCallExpressionFromType(partialData.type)
1217
+ createNewColumn(schemaLiteral, callExpFromType.name, incomingSchema[key])
1218
+ } else {
1219
+ // Update the existing column with changed data only
1220
+ const transformedColumnData = transformColumnData(partialData, existingColumn.type)
1221
+ mergeDataIntoObjectLiteral(existingColumn.objectLiteral, {
1222
+ ...transformedColumnData,
1223
+ choices: partialData.choice_elements,
1224
+ })
1225
+
1226
+ removeChoicesIfNecessary(existingColumn, incomingSchema[key])
1227
+ removeAttributesIfNecessary(existingColumn, incomingSchema[key])
1228
+ }
1229
+ }
1186
1230
  })
1231
+ }
1232
+
1233
+ function removeChoicesIfNecessary(existingColumn: any, columnFromBootstrap: any) {
1234
+ const existingProperties = existingColumn.objectLiteral
1235
+ .getProperties()
1236
+ .reduce((a, v) => ({ ...a, [(v as ts.PropertyAssignment).getName()]: v }), {})
1187
1237
 
1188
- schemaLiteral.transform((traversal) => {
1189
- if (ts.isObjectLiteralExpression(traversal.currentNode)) {
1190
- return traversal.factory.updateObjectLiteralExpression(traversal.currentNode, assignments)
1238
+ if (!existingProperties['choices']) {
1239
+ return
1240
+ }
1241
+
1242
+ if (!columnFromBootstrap['choice_elements']) {
1243
+ removeNode(existingProperties['choices'])
1244
+ return
1245
+ }
1246
+
1247
+ const choicesFromBootstrap = Object.keys(columnFromBootstrap['choice_elements'])
1248
+ const existingChoices = existingProperties['choices']
1249
+ .getFirstChildByKind(ts.SyntaxKind.ObjectLiteralExpression)
1250
+ .getProperties()
1251
+ .reduce((a, v) => ({ ...a, [v.getName()]: v }), {})
1252
+ Object.keys(existingChoices).forEach((choice) => {
1253
+ if (!choicesFromBootstrap.includes(choice)) {
1254
+ removeNode(existingChoices[choice])
1191
1255
  }
1192
- return traversal.currentNode
1193
1256
  })
1194
1257
  }
1195
1258
 
1259
+ function removeAttributesIfNecessary(existingColumn: any, columnFromBootstrap: any) {
1260
+ const existingProperties = existingColumn.objectLiteral
1261
+ .getProperties()
1262
+ .reduce((a, v) => ({ ...a, [(v as ts.PropertyAssignment).getName()]: v }), {})
1263
+
1264
+ if (!existingProperties['attributes']) {
1265
+ return
1266
+ }
1267
+
1268
+ if (!columnFromBootstrap['attributes']) {
1269
+ removeNode(existingProperties['attributes'])
1270
+ return
1271
+ }
1272
+
1273
+ const attributesFromBootstrap = Object.keys(columnFromBootstrap['attributes'])
1274
+ const existingChoices = existingProperties['attributes']
1275
+ .getFirstChildByKind(ts.SyntaxKind.ObjectLiteralExpression)
1276
+ .getProperties()
1277
+ .reduce((a, v) => ({ ...a, [v.getName()]: v }), {})
1278
+ Object.keys(existingChoices).forEach((choice) => {
1279
+ if (!(choice in attributesFromBootstrap)) {
1280
+ removeNode(existingChoices[choice])
1281
+ }
1282
+ })
1283
+ }
1284
+
1285
+ function createNewColumn(schemaLiteral: ts.ObjectLiteralExpression, callExpName: string, data: any) {
1286
+ const propertyAssignment = schemaLiteral.addPropertyAssignment({
1287
+ name: data.name,
1288
+ initializer: `${callExpName}({})`,
1289
+ })
1290
+ const objectLiteral = propertyAssignment
1291
+ .getInitializer()!
1292
+ .getFirstChildByKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
1293
+ const transformedColumnData = transformColumnData(data, callExpName)
1294
+ mergeDataIntoObjectLiteral(objectLiteral, { ...transformedColumnData, choices: data.choice_elements })
1295
+ }
1296
+
1196
1297
  type DB_ACTIONS = 'read' | 'delete' | 'create' | 'update'
1197
1298
 
1198
1299
  function getActionsArray(data) {
@@ -1223,7 +1324,13 @@ function getDynamicDefault(val: string | undefined) {
1223
1324
  function buildIndexes(indexes: IndexType[] = [], collection: XMLBuilder) {
1224
1325
  indexes.forEach((index) => {
1225
1326
  const indexElement = collection.ele('index', { name: index.name, unique: index.unique })
1226
- indexElement.ele('element', { name: index.element })
1327
+ if (isArray(index.element)) {
1328
+ index.element.forEach((element) => {
1329
+ indexElement.ele('element', { name: element })
1330
+ })
1331
+ } else {
1332
+ indexElement.ele('element', { name: index.element })
1333
+ }
1227
1334
  })
1228
1335
  }
1229
1336
 
@@ -1259,7 +1366,7 @@ function buildChoices(column: ColumnSchema, element: XMLBuilder) {
1259
1366
  }
1260
1367
  }
1261
1368
 
1262
- export function transformColumnData(col: any, callExpFromType: any) {
1369
+ function transformColumnData(col: any, callExpFromType: string) {
1263
1370
  const {
1264
1371
  type: internal_type,
1265
1372
  choice,
@@ -1284,10 +1391,10 @@ export function transformColumnData(col: any, callExpFromType: any) {
1284
1391
  } = col
1285
1392
 
1286
1393
  if (max_length !== undefined) {
1287
- column.maxLength = max_length
1394
+ column.maxLength = parseInt(max_length, 10)
1288
1395
  }
1289
1396
 
1290
- if (internal_type && (callExpFromType.entityKind === 'generic' || callExpFromType.name === 'GenericColumn')) {
1397
+ if (internal_type && callExpFromType === 'GenericColumn') {
1291
1398
  column.column_type = internal_type
1292
1399
  }
1293
1400
 
@@ -1362,7 +1469,7 @@ function removeAttributeTag(obj: any) {
1362
1469
  return obj
1363
1470
  }
1364
1471
 
1365
- export function SysNumber(
1472
+ function SysNumber(
1366
1473
  category: TableName,
1367
1474
  maximum_digits: number | undefined,
1368
1475
  number?: number | undefined,
@@ -1381,7 +1488,7 @@ export function SysNumber(
1381
1488
  }
1382
1489
 
1383
1490
  // TODO should this reference sys_filter_option_dynamic instead?
1384
- export const dynamic_value_mapping = {
1491
+ const dynamic_value_mapping = {
1385
1492
  'Current Name': `current.name`,
1386
1493
  'Next ECC Sequence Number': `GlideCounter.next('ecc:sequence');`,
1387
1494
  'Get Module View': `var viewDefault='';if(current.device_type=='mobile') viewDefault='Mobile';else if(!current.application.nil() && current.application.device_type=='mobile' && current.device_type.nil()) viewDefault='Mobile';viewDefault`,
@@ -1406,7 +1513,7 @@ export function generateColumnExpression<Type extends (...args: any) => any>(
1406
1513
  expression: Type,
1407
1514
  args: Parameters<Type>[0],
1408
1515
  argsSetIfDefined?: Undefined<Parameters<Type>[0]>
1409
- ): ts.CallExpression {
1516
+ ): tsc.CallExpression {
1410
1517
  const setArgs = { ...args }
1411
1518
  if (argsSetIfDefined) {
1412
1519
  Object.keys(argsSetIfDefined).forEach((key) => {
@@ -1416,56 +1523,56 @@ export function generateColumnExpression<Type extends (...args: any) => any>(
1416
1523
  })
1417
1524
  }
1418
1525
 
1419
- return ts.factory.createCallExpression(ts.factory.createIdentifier(expression.name), undefined, [
1526
+ return tsc.factory.createCallExpression(tsc.factory.createIdentifier(expression.name), undefined, [
1420
1527
  createObjectExpression(setArgs),
1421
1528
  ])
1422
1529
  }
1423
1530
 
1424
- export function createObjectExpression(firstArg: object): ts.Expression {
1425
- return ts.factory.createObjectLiteralExpression(
1531
+ export function createObjectExpression(firstArg: object): tsc.Expression {
1532
+ return tsc.factory.createObjectLiteralExpression(
1426
1533
  Object.entries(firstArg)
1427
1534
  .filter(([_key, value]) => value !== undefined)
1428
1535
  .map(([key, value]) => {
1429
1536
  if (key === 'referenceTable') {
1430
- return ts.factory.createPropertyAssignment(
1431
- ts.factory.createIdentifier('referenceTable'),
1432
- ts.factory.createStringLiteral(value)
1537
+ return tsc.factory.createPropertyAssignment(
1538
+ tsc.factory.createIdentifier('referenceTable'),
1539
+ tsc.factory.createStringLiteral(value)
1433
1540
  )
1434
1541
  } else if (key === 'calculated_value') {
1435
1542
  const calVal = `${os.EOL}${formatScript(value, schemaPosition)}`
1436
- return ts.factory.createPropertyAssignment(
1437
- ts.factory.createIdentifier(key),
1438
- ts.factory.createTaggedTemplateExpression(
1439
- ts.factory.createIdentifier('script'),
1543
+ return tsc.factory.createPropertyAssignment(
1544
+ tsc.factory.createIdentifier(key),
1545
+ tsc.factory.createTaggedTemplateExpression(
1546
+ tsc.factory.createIdentifier('script'),
1440
1547
  undefined,
1441
- ts.factory.createNoSubstitutionTemplateLiteral(calVal, calVal)
1548
+ tsc.factory.createNoSubstitutionTemplateLiteral(calVal, calVal)
1442
1549
  )
1443
1550
  )
1444
1551
  } else if (key === 'maxLength') {
1445
1552
  if (isNaN(+value)) {
1446
- return ts.factory.createPropertyAssignment(
1447
- ts.factory.createIdentifier(key),
1448
- ts.factory.createAsExpression(
1449
- ts.factory.createStringLiteral(value),
1450
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
1553
+ return tsc.factory.createPropertyAssignment(
1554
+ tsc.factory.createIdentifier(key),
1555
+ tsc.factory.createAsExpression(
1556
+ tsc.factory.createStringLiteral(value),
1557
+ tsc.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
1451
1558
  )
1452
1559
  )
1453
1560
  }
1454
1561
 
1455
- return ts.factory.createPropertyAssignment(
1456
- ts.factory.createIdentifier(key),
1457
- ts.factory.createNumericLiteral(value)
1562
+ return tsc.factory.createPropertyAssignment(
1563
+ tsc.factory.createIdentifier(key),
1564
+ tsc.factory.createNumericLiteral(value)
1458
1565
  )
1459
1566
  }
1460
- return ts.factory.createPropertyAssignment(
1567
+ return tsc.factory.createPropertyAssignment(
1461
1568
  createPropertyIdentifier(key),
1462
- factoryCreateValue(value, ts.factory)
1569
+ factoryCreateValue(value, tsc.factory)
1463
1570
  )
1464
1571
  })
1465
1572
  )
1466
1573
  }
1467
1574
 
1468
- export function factoryCreateValue(value: unknown, factory: ts.NodeFactory) {
1575
+ function factoryCreateValue(value: unknown, factory: tsc.NodeFactory) {
1469
1576
  if (typeof value === 'string') {
1470
1577
  return factory.createStringLiteral(value)
1471
1578
  }
@@ -1475,7 +1582,7 @@ export function factoryCreateValue(value: unknown, factory: ts.NodeFactory) {
1475
1582
  return factory.createNumericLiteral(value)
1476
1583
  } else {
1477
1584
  return factory.createPrefixUnaryExpression(
1478
- ts.SyntaxKind.MinusToken,
1585
+ tsc.SyntaxKind.MinusToken,
1479
1586
  factory.createNumericLiteral(Math.abs(value))
1480
1587
  )
1481
1588
  }
@@ -1490,14 +1597,14 @@ export function factoryCreateValue(value: unknown, factory: ts.NodeFactory) {
1490
1597
  return factory.createNull()
1491
1598
  }
1492
1599
  if (isArray(value)) {
1493
- const newvals: ts.Expression[] = []
1600
+ const newvals: tsc.Expression[] = []
1494
1601
 
1495
1602
  Object.values(value).forEach((v) => {
1496
1603
  newvals.push(factoryCreateValue(v, factory))
1497
1604
  })
1498
1605
 
1499
1606
  return factory.createArrayLiteralExpression(newvals)
1500
- } else if (Structure.isImportSpecifier(value)) {
1607
+ } else if (ts.Structure.isImportSpecifier(value)) {
1501
1608
  return factory.createIdentifier(value['name'])
1502
1609
  } else {
1503
1610
  return createObjectExpression(value)
@@ -1512,7 +1619,7 @@ export function factoryCreateValue(value: unknown, factory: ts.NodeFactory) {
1512
1619
  type Undefined<T> = {
1513
1620
  [P in keyof T]: T[P] | undefined
1514
1621
  }
1515
- export function getCallExpressionFromType(internal_type: string) {
1622
+ function getCallExpressionFromType(internal_type: string) {
1516
1623
  switch (internal_type) {
1517
1624
  case undefined:
1518
1625
  case 'string':