@servicenow/sdk-build-plugins 2.2.9 → 3.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 (128) hide show
  1. package/dist/AttachmentPlugin.d.ts +7 -7
  2. package/dist/AttachmentPlugin.js +2 -2
  3. package/dist/AttachmentPlugin.js.map +1 -1
  4. package/dist/BusinessRulePlugin.js +6 -1
  5. package/dist/BusinessRulePlugin.js.map +1 -1
  6. package/dist/CrossScopePrivilegePlugin.d.ts +2 -2
  7. package/dist/CrossScopePrivilegePlugin.js +7 -3
  8. package/dist/CrossScopePrivilegePlugin.js.map +1 -1
  9. package/dist/DefaultPlugin.d.ts +5 -1
  10. package/dist/DefaultPlugin.js +12 -1
  11. package/dist/DefaultPlugin.js.map +1 -1
  12. package/dist/IncludePlugin.d.ts +34 -0
  13. package/dist/IncludePlugin.js +153 -0
  14. package/dist/IncludePlugin.js.map +1 -0
  15. package/dist/ListPlugin.d.ts +4 -4
  16. package/dist/ListPlugin.js +5 -5
  17. package/dist/ListPlugin.js.map +1 -1
  18. package/dist/PackageJsonPlugin.d.ts +15 -1
  19. package/dist/PackageJsonPlugin.js +39 -0
  20. package/dist/PackageJsonPlugin.js.map +1 -1
  21. package/dist/PropertyPlugin.d.ts +20 -4
  22. package/dist/PropertyPlugin.js +6 -5
  23. package/dist/PropertyPlugin.js.map +1 -1
  24. package/dist/ScriptTemplatePlugin.d.ts +0 -1
  25. package/dist/ScriptTemplatePlugin.js +7 -42
  26. package/dist/ScriptTemplatePlugin.js.map +1 -1
  27. package/dist/ServerModulePlugin.d.ts +19 -5
  28. package/dist/ServerModulePlugin.js +186 -30
  29. package/dist/ServerModulePlugin.js.map +1 -1
  30. package/dist/aclAndRole/AclPlugin.d.ts +9 -23
  31. package/dist/aclAndRole/AclPlugin.js +7 -5
  32. package/dist/aclAndRole/AclPlugin.js.map +1 -1
  33. package/dist/aclAndRole/RolePlugin.d.ts +3 -3
  34. package/dist/aclAndRole/RolePlugin.js +2 -2
  35. package/dist/aclAndRole/RolePlugin.js.map +1 -1
  36. package/dist/aclAndRole/Util.js +26 -19
  37. package/dist/aclAndRole/Util.js.map +1 -1
  38. package/dist/app/ApplicationMenuPlugin.d.ts +2 -2
  39. package/dist/app/ApplicationMenuPlugin.js +12 -10
  40. package/dist/app/ApplicationMenuPlugin.js.map +1 -1
  41. package/dist/db/ColumnPlugins.d.ts +12 -2
  42. package/dist/db/DocumentationPlugin.d.ts +9 -8
  43. package/dist/db/DocumentationPlugin.js +11 -5
  44. package/dist/db/DocumentationPlugin.js.map +1 -1
  45. package/dist/db/LicensingPlugin.d.ts +60 -0
  46. package/dist/db/LicensingPlugin.js +117 -0
  47. package/dist/db/LicensingPlugin.js.map +1 -0
  48. package/dist/db/RecordPlugin.d.ts +13 -10
  49. package/dist/db/RecordPlugin.js +54 -17
  50. package/dist/db/RecordPlugin.js.map +1 -1
  51. package/dist/db/TablePlugin.d.ts +161 -4
  52. package/dist/db/TablePlugin.js +313 -154
  53. package/dist/db/TablePlugin.js.map +1 -1
  54. package/dist/db/index.d.ts +1 -0
  55. package/dist/db/index.js +3 -1
  56. package/dist/db/index.js.map +1 -1
  57. package/dist/index.d.ts +1 -1
  58. package/dist/index.js +3 -2
  59. package/dist/index.js.map +1 -1
  60. package/dist/repack/index.d.ts +14 -0
  61. package/dist/repack/index.js +32 -0
  62. package/dist/repack/index.js.map +1 -0
  63. package/dist/repack/lint/Rules.d.ts +5 -0
  64. package/dist/repack/lint/Rules.js +128 -0
  65. package/dist/repack/lint/Rules.js.map +1 -0
  66. package/dist/repack/lint/index.d.ts +7 -0
  67. package/dist/repack/lint/index.js +66 -0
  68. package/dist/repack/lint/index.js.map +1 -0
  69. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +1 -0
  70. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +1 -1
  71. package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +3 -54
  72. package/dist/scriptedRESTAPI/RestApiPlugin.js +42 -6
  73. package/dist/scriptedRESTAPI/RestApiPlugin.js.map +1 -1
  74. package/dist/scriptedRESTAPI/RestSchemaUtils.d.ts +12 -12
  75. package/dist/scriptedRESTAPI/RestUtils.d.ts +2 -1
  76. package/dist/scriptedRESTAPI/RestUtils.js +24 -0
  77. package/dist/scriptedRESTAPI/RestUtils.js.map +1 -1
  78. package/dist/scripts/ClientScriptPlugin.d.ts +21 -1
  79. package/dist/scripts/ClientScriptPlugin.js +6 -3
  80. package/dist/scripts/ClientScriptPlugin.js.map +1 -1
  81. package/dist/uxf/ExperiencePlugin.d.ts +5 -10
  82. package/dist/uxf/ExperiencePlugin.js +4 -4
  83. package/dist/uxf/ExperiencePlugin.js.map +1 -1
  84. package/dist/uxf/RoutesPlugin.d.ts +3 -3
  85. package/dist/uxf/RoutesPlugin.js +2 -2
  86. package/dist/uxf/RoutesPlugin.js.map +1 -1
  87. package/package.json +12 -4
  88. package/src/AttachmentPlugin.ts +2 -2
  89. package/src/BusinessRulePlugin.ts +9 -3
  90. package/src/CrossScopePrivilegePlugin.ts +5 -3
  91. package/src/DefaultPlugin.ts +13 -1
  92. package/src/IncludePlugin.ts +204 -0
  93. package/src/ListPlugin.ts +6 -6
  94. package/src/PackageJsonPlugin.ts +46 -1
  95. package/src/PropertyPlugin.ts +8 -5
  96. package/src/ScriptTemplatePlugin.ts +7 -20
  97. package/src/ServerModulePlugin.ts +233 -29
  98. package/src/aclAndRole/AclPlugin.ts +23 -14
  99. package/src/aclAndRole/RolePlugin.ts +2 -2
  100. package/src/aclAndRole/Util.ts +30 -21
  101. package/src/app/ApplicationMenuPlugin.ts +15 -11
  102. package/src/db/DocumentationPlugin.ts +22 -5
  103. package/src/db/LicensingPlugin.ts +138 -0
  104. package/src/db/RecordPlugin.ts +70 -17
  105. package/src/db/TablePlugin.ts +394 -171
  106. package/src/db/index.ts +1 -0
  107. package/src/index.ts +1 -1
  108. package/src/repack/index.ts +32 -0
  109. package/src/repack/lint/Rules.ts +139 -0
  110. package/src/repack/lint/index.ts +71 -0
  111. package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +3 -1
  112. package/src/scriptedRESTAPI/RestApiPlugin.ts +30 -6
  113. package/src/scriptedRESTAPI/RestUtils.ts +29 -0
  114. package/src/scripts/ClientScriptPlugin.ts +15 -3
  115. package/src/uxf/ExperiencePlugin.ts +10 -8
  116. package/src/uxf/RoutesPlugin.ts +8 -6
  117. package/dist/atf/ATFComposer.d.ts +0 -492
  118. package/dist/atf/ATFComposer.js +0 -2722
  119. package/dist/atf/ATFComposer.js.map +0 -1
  120. package/dist/atf/TestPlugin.d.ts +0 -33
  121. package/dist/atf/TestPlugin.js +0 -97
  122. package/dist/atf/TestPlugin.js.map +0 -1
  123. package/dist/atf/index.d.ts +0 -1
  124. package/dist/atf/index.js +0 -9
  125. package/dist/atf/index.js.map +0 -1
  126. package/src/atf/ATFComposer.ts +0 -3361
  127. package/src/atf/TestPlugin.ts +0 -121
  128. package/src/atf/index.ts +0 -1
@@ -117,7 +117,7 @@ export default Plugin({
117
117
 
118
118
  const role = roleSchema.safeParse(entity.getValue())
119
119
  if (!role.success) {
120
- return undefined
120
+ return Promise.resolve(undefined)
121
121
  }
122
122
 
123
123
  const { $id: _, name, contains_roles, ...rest } = role.data
@@ -158,7 +158,7 @@ export default Plugin({
158
158
  },
159
159
  }
160
160
 
161
- return [roleDocument, ...roleMappingDocuments]
161
+ return Promise.resolve([roleDocument, ...roleMappingDocuments])
162
162
  },
163
163
  },
164
164
  },
@@ -7,43 +7,52 @@ import {
7
7
  getOrCreatePropertyAssignment,
8
8
  ArrayIterator,
9
9
  stringify,
10
+ Data,
11
+ isGUID,
10
12
  } from '@servicenow/sdk-build-core'
11
13
 
12
- const getElementKey = (element: ts.Expression) => {
13
- if (element.isKind(ts.SyntaxKind.StringLiteral)) {
14
- return element.getLiteralValue()
14
+ const getElementKey = (element: ts.Expression, context: Context): string => {
15
+ const result = context.extractAst(element)
16
+ if (result.handled && result.data) {
17
+ const data = result.data
18
+ if (Data.isString(data)) {
19
+ return data.getValue()
20
+ }
21
+ if (Data.isEntity(data) && data.getKind() === 'role') {
22
+ return data.getProperty('$id') ? `${data.getProperty('$id').getValue()}` : ''
23
+ }
15
24
  }
16
- const varDec = (element.getSymbol()?.getValueDeclaration() ??
17
- element.getSymbol()?.getAliasedSymbol()?.getValueDeclaration()) as ts.VariableDeclaration
18
- return varDec
19
- ? getNodeId(
20
- varDec.getInitializerIfKind(ts.SyntaxKind.CallExpression)?.getArguments()[0] as ts.ObjectLiteralExpression
21
- )!
22
- : ''
25
+ return ''
23
26
  }
24
27
 
25
28
  export function addObjectToArrayById(document: Document, name: string, roleId: string, context: Context) {
26
29
  const arg = document.node?.asKind(ts.SyntaxKind.CallExpression)?.getArguments()[0] as ts.ObjectLiteralExpression
27
30
  const table = document.data!['table']
28
- const argId = getNodeId(arg)
31
+ const primaryKey = getNodeId(arg)
29
32
  const propertyAssignment = getOrCreatePropertyAssignment(arg, name, '[]')
30
33
 
31
34
  let handled = false
32
35
  const rolesIterator = new ArrayIterator(propertyAssignment)
33
- const primaryKey = table === 'sys_security_acl_role' ? 'sys_security_acl' : 'role'
34
- const secondKey = table === 'sys_security_acl_role' ? 'sys_user_role' : 'contains'
36
+ const primaryKeyName = table === 'sys_security_acl_role' ? 'sys_security_acl' : 'role'
37
+ const secondaryKeyName = table === 'sys_security_acl_role' ? 'sys_user_role' : 'contains'
38
+
35
39
  while (rolesIterator.hasNext()) {
36
40
  const element = rolesIterator.next()
37
- const secKey = getElementKey(element)
41
+ const secondaryKey = getElementKey(element, context)
42
+ const secondaryKeySysId =
43
+ secondaryKey && isGUID(secondaryKey) ? secondaryKey : context.getIdUsingExplicitKey(secondaryKey) || ''
44
+ if (secondaryKeySysId === roleId) {
45
+ return
46
+ }
38
47
 
39
48
  const foundIdx = context.keys.composite.findIndex((k) =>
40
49
  isEqual(k.key, {
41
- [primaryKey]: argId,
42
- [secondKey]: secKey,
50
+ [primaryKeyName]: primaryKey,
51
+ [secondaryKeyName]: secondaryKey,
43
52
  })
44
53
  )
45
- if (foundIdx !== -1 && context.keys.composite[foundIdx]?.id === document.guid) {
46
- if (context.keys.explicit[secKey]?.id === roleId || secKey === roleId) {
54
+ if (foundIdx !== -1 && context.keys.composite[foundIdx]?.id == document.guid) {
55
+ if (context.keys.explicit[secondaryKey]?.id === roleId || secondaryKey === roleId) {
47
56
  return
48
57
  }
49
58
  element?.replaceWithText(stringify(roleId))
@@ -59,8 +68,8 @@ export function addObjectToArrayById(document: Document, name: string, roleId: s
59
68
  context.keys.registerCompositeId(
60
69
  table,
61
70
  {
62
- [primaryKey]: argId!,
63
- [secondKey]: roleId,
71
+ [primaryKeyName]: primaryKey!,
72
+ [secondaryKeyName]: roleId,
64
73
  },
65
74
  document.guid
66
75
  )
@@ -83,7 +92,7 @@ export function removeObjectFromArrayById(document: Document, name: string, cont
83
92
  const rolesIterator = new ArrayIterator(propertyAssignment)
84
93
  while (rolesIterator.hasNext()) {
85
94
  const element = rolesIterator.next()
86
- const secKey = getElementKey(element)
95
+ const secKey = getElementKey(element, context)
87
96
  const primaryKey = table === 'sys_security_acl_role' ? 'sys_security_acl' : 'role'
88
97
  const secondKey = table === 'sys_security_acl_role' ? 'sys_user_role' : 'contains'
89
98
  const foundIdx = context.keys.composite.findIndex((k) =>
@@ -11,9 +11,9 @@ import {
11
11
  removeNode,
12
12
  transformFunctionArguments,
13
13
  writeArrayPropertyAsReference,
14
- writePropertyAsReference,
15
14
  EntityData,
16
15
  ObjectData,
16
+ transformCustomizedProperties,
17
17
  } from '@servicenow/sdk-build-core'
18
18
  import { RecordPlugin } from '../db/RecordPlugin'
19
19
  import { Record } from '@servicenow/sdk-core/runtime/db'
@@ -60,9 +60,9 @@ export default Plugin({
60
60
  composers: {
61
61
  entity: {
62
62
  appMenu(entity, context) {
63
- const appMenuData = ApplicationMenuSchema.safeParse(entity.getValue())
63
+ const appMenuData = ApplicationMenuSchema.and(z.any()).safeParse(entity.getValue())
64
64
  if (!appMenuData.success) {
65
- return undefined
65
+ return Promise.resolve(undefined)
66
66
  }
67
67
  const { $id, category, ...props } = appMenuData.data
68
68
  const roles = entity.getProperty('roles', 'array')
@@ -85,7 +85,8 @@ export default Plugin({
85
85
  'record',
86
86
  entity.getGuid(),
87
87
  ObjectData.fromObjectValue(recordEntity, entity.getNode()),
88
- entity.getNode()
88
+ entity.getNode(),
89
+ entity.getInstallMethod()
89
90
  ),
90
91
  ],
91
92
  [RecordPlugin]
@@ -119,7 +120,7 @@ export default Plugin({
119
120
  },
120
121
  transformers: {
121
122
  record: {
122
- CallExpression(document) {
123
+ CallExpression(document, _context) {
123
124
  if (getCallExpressionName(document.node) !== ApplicationMenu.name) {
124
125
  return false
125
126
  }
@@ -131,29 +132,32 @@ export default Plugin({
131
132
  if (!document.changedData) {
132
133
  return false
133
134
  }
135
+
134
136
  const menu = AppMenuDocumentTransformer.safeParse(document.data)
135
137
  if (!menu.success) {
136
138
  return false
137
139
  }
138
- const { roles, category } = (document.xmlData as AppMenuDocumentSchema).data
140
+ const { roles } = (document.xmlData as AppMenuDocumentSchema).data
139
141
  const [args] = document.node.getArguments()
140
142
  if (!ts.Node.isObjectLiteralExpression(args)) {
141
143
  return false
142
144
  }
143
145
 
144
- if (roles) {
146
+ if (roles && document.changedData?.['data']['roles']) {
145
147
  writeArrayPropertyAsReference(args, 'roles', '[]', getRolesArray(roles))
146
148
  }
147
- if (category) {
148
- writePropertyAsReference(args, 'category', '{}', category)
149
- }
150
149
 
151
150
  const {
152
151
  roles: _roles,
153
- category: _category,
154
152
  device_type: _device_type,
155
153
  ...data
156
154
  } = (AppMenuDocumentSchema.deepPartial().parse(document.changedData) as AppMenuDocumentSchema).data
155
+
156
+ transformCustomizedProperties(ApplicationMenu, document.node, (document.changedData as any).data, {
157
+ roles: _roles,
158
+ device_type: _device_type,
159
+ ...data,
160
+ })
157
161
  return transformFunctionArguments(document.node, ApplicationMenu, data as any)
158
162
  },
159
163
  },
@@ -7,6 +7,9 @@ import {
7
7
  getOrCreatePropertyAssignment,
8
8
  mergeDataIntoObjectLiteralArray,
9
9
  filterEmpty,
10
+ generateCallExpressionExportForDocument,
11
+ getOrCreateEntitySourceFile,
12
+ linkDocument,
10
13
  } from '@servicenow/sdk-build-core'
11
14
  import { Documentation, Record, Table } from '@servicenow/sdk-core/runtime/db'
12
15
  import { createObjectExpression } from './TablePlugin'
@@ -47,17 +50,31 @@ export default Plugin({
47
50
  },
48
51
  },
49
52
  generators: {
50
- sys_documentation(document, _context: Context, linkedDocuments) {
51
- const table = linkedDocuments.find((doc) => doc.guid === (document.data as any).data.name)
52
- return table && table.node ? { ...document, node: table.node } : undefined
53
+ sys_documentation(document, context: Context, linkedDocuments) {
54
+ const tableName = (document.data as any).data.name
55
+ const table = linkedDocuments.find((doc) => doc.guid === tableName)
56
+ return table && table.node
57
+ ? { ...document, node: table.node }
58
+ : linkDocument(
59
+ document,
60
+ generateCallExpressionExportForDocument(
61
+ context,
62
+ {
63
+ sourceFile: getOrCreateEntitySourceFile(context, `${document.guid}`),
64
+ moduleSpecifier: '@servicenow/sdk/core',
65
+ },
66
+ Record,
67
+ { $id: document.guid }
68
+ ).getExpressionIfKindOrThrow(ts.SyntaxKind.CallExpression)
69
+ )
53
70
  },
54
71
  },
55
72
  transformers: {
56
73
  sys_documentation: {
57
- CallExpression(document) {
74
+ CallExpression(document, context) {
58
75
  const expressionName = getCallExpressionName(document.node)
59
76
  if (expressionName !== Table.name) {
60
- return false
77
+ return RecordPlugin.transformers!.record!.CallExpression(document, context)
61
78
  }
62
79
 
63
80
  const { element: column } = (document.data as any).data
@@ -0,0 +1,138 @@
1
+ import {
2
+ getCallExpressionName,
3
+ Plugin,
4
+ Context,
5
+ getPropertyAssignment,
6
+ mergeDataIntoObjectLiteral,
7
+ linkDocument,
8
+ generateCallExpressionExportForDocument,
9
+ getOrCreateEntitySourceFile,
10
+ GUID,
11
+ } from '@servicenow/sdk-build-core'
12
+ import { Table, Record } from '@servicenow/sdk-core/runtime/db'
13
+ import { RecordPlugin } from './RecordPlugin'
14
+ import { ts } from '@servicenow/sdk-project'
15
+ import { isEqual } from 'lodash'
16
+
17
+ export const ua_table_licensing_config = 'ua_table_licensing_config'
18
+
19
+ export default Plugin({
20
+ name: 'LicensingConfig',
21
+ composers: {
22
+ xml: {
23
+ ua_table_licensing_config(xml) {
24
+ return {
25
+ kind: ua_table_licensing_config,
26
+ guid: (xml.data['id'] as string) ?? GUID(),
27
+ data: xml.data,
28
+ action: xml.action,
29
+ }
30
+ },
31
+ },
32
+ },
33
+ serializers: {
34
+ ua_table_licensing_config(document, context) {
35
+ return RecordPlugin.serializers.record(document, context)
36
+ },
37
+ },
38
+ arrangers: {
39
+ ua_table_licensing_config(document) {
40
+ if ((document.data as any).table !== ua_table_licensing_config) {
41
+ return { handled: false }
42
+ }
43
+ const tableName = (document.data as any).data.name
44
+ return {
45
+ handled: true,
46
+ result: tableName ? { kind: 'bootstrap', guid: tableName } : undefined,
47
+ }
48
+ },
49
+ },
50
+ generators: {
51
+ ua_table_licensing_config(document, context: Context, linkedDocuments) {
52
+ const table = linkedDocuments.find((doc) => doc.guid === (document.data as any).data.name)
53
+ return table && table.node
54
+ ? { ...document, node: table.node }
55
+ : linkDocument(
56
+ document,
57
+ generateCallExpressionExportForDocument(
58
+ context,
59
+ {
60
+ sourceFile: getOrCreateEntitySourceFile(context, `${document.kind}_${document.guid}`),
61
+ moduleSpecifier: '@servicenow/sdk/core',
62
+ },
63
+ Record,
64
+ { $id: document.guid }
65
+ ).getExpressionIfKindOrThrow(ts.SyntaxKind.CallExpression)
66
+ )
67
+ },
68
+ },
69
+ transformers: {
70
+ ua_table_licensing_config: {
71
+ CallExpression(document, context) {
72
+ const expressionName = getCallExpressionName(document.node)
73
+ if (expressionName !== Table.name) {
74
+ return RecordPlugin.transformers!.record!.CallExpression(document, context)
75
+ }
76
+
77
+ const [baseArgs] = document.node.getArguments()
78
+ if (!baseArgs) {
79
+ return false
80
+ }
81
+
82
+ const changedData = (document.changedData as any).data
83
+ const data = (document.data as any).data
84
+
85
+ let objLit, dataToMerge
86
+ const licensingProp = getPropertyAssignment(baseArgs, 'licensing_config')
87
+ if (!licensingProp) {
88
+ if (isDefaultLicensingConfig(data)) {
89
+ return true
90
+ }
91
+ const defaultValues = defaultLicensingConfigRecord(data.name).data
92
+ dataToMerge = {}
93
+ Object.entries(data)
94
+ .filter(([prop]) => {
95
+ return data[prop] !== defaultValues[prop]
96
+ })
97
+ .map(([k, v]) => {
98
+ dataToMerge[k] = v
99
+ })
100
+ const callExpLit = document.node.getFirstChildByKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
101
+ const newProp = callExpLit.addPropertyAssignment({ name: 'licensing_config', initializer: '{}' })
102
+ objLit = newProp!.getFirstChildByKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
103
+ } else {
104
+ dataToMerge = changedData
105
+ objLit = licensingProp!.getFirstChildByKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
106
+ }
107
+
108
+ if (dataToMerge.license_roles) {
109
+ dataToMerge.license_roles = dataToMerge.license_roles.split(',')
110
+ }
111
+
112
+ mergeDataIntoObjectLiteral(objLit, dataToMerge)
113
+ return true
114
+ },
115
+ },
116
+ },
117
+ })
118
+ function isDefaultLicensingConfig(data) {
119
+ return isEqual(data, defaultLicensingConfigRecord(data.name).data)
120
+ }
121
+
122
+ function defaultLicensingConfigRecord(tableName: string) {
123
+ return Record({
124
+ table: ua_table_licensing_config,
125
+ $id: `${tableName}_license`,
126
+ data: {
127
+ name: tableName,
128
+ op_insert: true,
129
+ op_update: true,
130
+ op_delete: true,
131
+ is_fulfillment: false,
132
+ license_model: 'none',
133
+ owner_condition: '',
134
+ license_condition: '',
135
+ license_roles: '',
136
+ },
137
+ })
138
+ }
@@ -17,9 +17,13 @@ import {
17
17
  extractCallExpression,
18
18
  Data,
19
19
  ObjectData,
20
+ Document,
21
+ type Directories,
20
22
  } from '@servicenow/sdk-build-core'
21
23
  import { z } from 'zod'
22
24
  import { ts } from '@servicenow/sdk-project'
25
+ import { except } from '../scripts/scriptUtils'
26
+ import { fieldHasNowIncludeCallExpression } from '../IncludePlugin'
23
27
 
24
28
  // TODO: This schema should live with the Record entity function itself
25
29
  const RecordEntitySchema = z.object({
@@ -42,15 +46,7 @@ export const TextStringSchema = z.object({ '#text': z.string() }).transform((val
42
46
  export const TextBooleanSchema = z.object({ '#text': z.boolean() }).transform((val) => val['#text'])
43
47
  export const TextNumberSchema = z.object({ '#text': z.number().or(z.literal('')) }).transform((val) => val['#text'])
44
48
 
45
- const ignoreTables = [
46
- 'sys_metadata_link',
47
- 'sys_module',
48
- 'sys_ux_lib_asset',
49
- 'sys_app',
50
- 'sys_store_app',
51
- 'ua_table_licensing_config',
52
- 'sys_index',
53
- ]
49
+ const ignoreTables = ['sys_metadata_link', 'sys_module', 'sys_ux_lib_asset', 'sys_app', 'sys_store_app', 'sys_index']
54
50
  const ignoreFields = [
55
51
  'sys_created_by',
56
52
  'sys_created_on',
@@ -91,11 +87,45 @@ const RecordDocumentSchema = z.object({
91
87
  table: z.string(),
92
88
  })
93
89
 
90
+ const InstallDir = z
91
+ .union([
92
+ z.literal('update'),
93
+ z.literal('dictionary'),
94
+ z.literal('scope'),
95
+ z.literal('demo'),
96
+ z.literal('first install'),
97
+ ])
98
+ .default('update')
99
+ .transform((directory) => {
100
+ if (directory === 'demo') {
101
+ return 'unload.demo'
102
+ } else if (directory === 'first install') {
103
+ return 'unload'
104
+ }
105
+ return directory
106
+ })
107
+
108
+ export function getOutputDirectoryForDocument(document: Document, table: string): Directories {
109
+ if (table === 'sys_app') {
110
+ return 'scope'
111
+ }
112
+ return InstallDir.parse(document['directory'])
113
+ }
114
+
94
115
  function cleanRecordUpdate(recordUpdate: globalThis.Record<string, unknown>) {
95
116
  ignoreXMLElement.forEach((element) => delete recordUpdate[element])
96
117
  return recordUpdate
97
118
  }
98
119
 
120
+ export function propertyAssignmentIsInlineScript(pa: ts.PropertyAssignment) {
121
+ return (
122
+ pa.getInitializerIfKind(ts.SyntaxKind.StringLiteral) ||
123
+ pa.getInitializerIfKind(ts.SyntaxKind.TaggedTemplateExpression) ||
124
+ pa.getInitializerIfKind(ts.SyntaxKind.TemplateExpression) ||
125
+ pa.getInitializerIfKind(ts.SyntaxKind.NoSubstitutionTemplateLiteral)
126
+ )
127
+ }
128
+
99
129
  export const RecordPlugin = Plugin({
100
130
  name: 'Record',
101
131
  extractors: {
@@ -244,29 +274,30 @@ export const RecordPlugin = Plugin({
244
274
  record(entity, _context) {
245
275
  const $id = entity.getProperty('$id')
246
276
  if (!Data.isString($id) && !Data.isNumber($id)) {
247
- throw new Error(`Record ID must be a string or number`)
277
+ Promise.reject(`Record ID must be a string or number`)
248
278
  }
249
279
 
250
280
  const table = entity.getProperty('table')
251
281
  if (!Data.isString(table)) {
252
- throw new Error(`Record table must be a string`)
282
+ Promise.reject(`Record table must be a string`)
253
283
  }
254
284
 
255
285
  const data = entity.getProperty('data')
256
286
  if (!Data.isObject(data)) {
257
- throw new Error(`Record data must be an object`)
287
+ Promise.reject(`Record data must be an object`)
258
288
  }
259
289
 
260
- return {
290
+ return Promise.resolve({
261
291
  kind: 'record',
262
292
  guid: entity.getGuid(),
263
293
  node: entity.getNode(),
294
+ directory: entity.getInstallMethod(),
264
295
  data: {
265
296
  $id: $id.getValue(),
266
297
  table: table.getValue(),
267
298
  data: data.getValue(),
268
299
  },
269
- }
300
+ })
270
301
  },
271
302
  },
272
303
  xml: {
@@ -295,7 +326,7 @@ export const RecordPlugin = Plugin({
295
326
 
296
327
  return {
297
328
  name: `${table}_${document.guid}.xml`,
298
- directory: table === 'sys_app' ? 'scope' : 'update',
329
+ directory: getOutputDirectoryForDocument(document, table),
299
330
  content: recordBuilder.end(),
300
331
  }
301
332
  },
@@ -335,6 +366,11 @@ export const RecordPlugin = Plugin({
335
366
  return false
336
367
  }
337
368
 
369
+ const [args] = document.node.getArguments()
370
+ if (!ts.Node.isObjectLiteralExpression(args)) {
371
+ return false
372
+ }
373
+
338
374
  const { table } = document.data as RecordData
339
375
  const data = (document.changedData as any).data
340
376
 
@@ -350,9 +386,17 @@ export const RecordPlugin = Plugin({
350
386
  return out
351
387
  }, {} as Record)
352
388
 
389
+ const preexistingData = args
390
+ .getProperty('data')
391
+ ?.asKind(ts.SyntaxKind.PropertyAssignment)
392
+ ?.getInitializerIfKind(ts.SyntaxKind.ObjectLiteralExpression)
393
+
353
394
  transformFunctionArguments(document.node, Record, {
354
395
  table,
355
- data: properties,
396
+ data: except(
397
+ properties,
398
+ (field) => !!preexistingData && fieldHasNowIncludeCallExpression(preexistingData, field)
399
+ ),
356
400
  })
357
401
 
358
402
  context.argTypesToFix.push(document.node)
@@ -381,6 +425,15 @@ export function extractCallExpressionAsRecord<
381
425
  return {
382
426
  handled: true,
383
427
  diagnostics: result.diagnostics,
384
- data: result.data ? new EntityData('record', result.data.getGuid(), asRecord(result.data), node) : undefined,
428
+ data: result.data
429
+ ? new EntityData(
430
+ 'record',
431
+ result.data.getGuid(),
432
+ asRecord(result.data),
433
+ node,
434
+ result.data.getInstallMethod(),
435
+ result.data.getExplicitIds()
436
+ )
437
+ : undefined,
385
438
  }
386
439
  }