@servicenow/sdk-build-plugins 4.1.1 → 4.2.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 (108) hide show
  1. package/dist/acl-plugin.js +13 -4
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/application-menu-plugin.js +1 -0
  4. package/dist/application-menu-plugin.js.map +1 -1
  5. package/dist/atf/step-configs.d.ts +13 -12
  6. package/dist/atf/step-configs.js.map +1 -1
  7. package/dist/atf/test-plugin.d.ts +1 -1
  8. package/dist/atf/test-plugin.js +8 -5
  9. package/dist/atf/test-plugin.js.map +1 -1
  10. package/dist/basic-syntax-plugin.js +51 -13
  11. package/dist/basic-syntax-plugin.js.map +1 -1
  12. package/dist/business-rule-plugin.js.map +1 -1
  13. package/dist/claims-plugin.js +1 -1
  14. package/dist/claims-plugin.js.map +1 -1
  15. package/dist/client-script-plugin.js +5 -17
  16. package/dist/client-script-plugin.js.map +1 -1
  17. package/dist/column/column-helper.d.ts +1 -1
  18. package/dist/column/column-helper.js +46 -2
  19. package/dist/column/column-helper.js.map +1 -1
  20. package/dist/column/column-to-record.js +6 -4
  21. package/dist/column/column-to-record.js.map +1 -1
  22. package/dist/column-plugin.js +106 -27
  23. package/dist/column-plugin.js.map +1 -1
  24. package/dist/data-plugin.d.ts +3 -0
  25. package/dist/data-plugin.js +208 -0
  26. package/dist/data-plugin.js.map +1 -0
  27. package/dist/import-sets-plugin.d.ts +2 -0
  28. package/dist/import-sets-plugin.js +412 -0
  29. package/dist/import-sets-plugin.js.map +1 -0
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.js +4 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/json-plugin.d.ts +4 -4
  34. package/dist/json-plugin.js +21 -7
  35. package/dist/json-plugin.js.map +1 -1
  36. package/dist/list-plugin.js +83 -1
  37. package/dist/list-plugin.js.map +1 -1
  38. package/dist/now-attach-plugin.d.ts +35 -0
  39. package/dist/now-attach-plugin.js +317 -0
  40. package/dist/now-attach-plugin.js.map +1 -0
  41. package/dist/now-config-plugin.js +3 -0
  42. package/dist/now-config-plugin.js.map +1 -1
  43. package/dist/now-include-plugin.js +7 -1
  44. package/dist/now-include-plugin.js.map +1 -1
  45. package/dist/package-json-plugin.js +2 -2
  46. package/dist/package-json-plugin.js.map +1 -1
  47. package/dist/record-plugin.d.ts +6 -0
  48. package/dist/record-plugin.js +50 -23
  49. package/dist/record-plugin.js.map +1 -1
  50. package/dist/repack/lint/Rules.js.map +1 -1
  51. package/dist/rest-api-plugin.js +28 -31
  52. package/dist/rest-api-plugin.js.map +1 -1
  53. package/dist/role-plugin.js +1 -0
  54. package/dist/role-plugin.js.map +1 -1
  55. package/dist/server-module-plugin/index.js +15 -2
  56. package/dist/server-module-plugin/index.js.map +1 -1
  57. package/dist/service-portal/widget-plugin.js +4 -1
  58. package/dist/service-portal/widget-plugin.js.map +1 -1
  59. package/dist/static-content-plugin.d.ts +1 -0
  60. package/dist/static-content-plugin.js +4 -3
  61. package/dist/static-content-plugin.js.map +1 -1
  62. package/dist/table-plugin.js +33 -2
  63. package/dist/table-plugin.js.map +1 -1
  64. package/dist/ui-page-plugin.js +2 -1
  65. package/dist/ui-page-plugin.js.map +1 -1
  66. package/dist/ui-policy-plugin.d.ts +2 -0
  67. package/dist/ui-policy-plugin.js +407 -0
  68. package/dist/ui-policy-plugin.js.map +1 -0
  69. package/dist/utils.d.ts +10 -1
  70. package/dist/utils.js +24 -0
  71. package/dist/utils.js.map +1 -1
  72. package/dist/view-plugin.js +1 -1
  73. package/dist/view-plugin.js.map +1 -1
  74. package/package.json +6 -6
  75. package/src/_types/eslint-plugin-es-x.d.ts +17 -0
  76. package/src/_types/md5.js.d.ts +8 -0
  77. package/src/acl-plugin.ts +19 -9
  78. package/src/application-menu-plugin.ts +1 -0
  79. package/src/atf/step-configs.ts +14 -12
  80. package/src/atf/test-plugin.ts +40 -21
  81. package/src/basic-syntax-plugin.ts +61 -13
  82. package/src/business-rule-plugin.ts +7 -4
  83. package/src/claims-plugin.ts +1 -1
  84. package/src/client-script-plugin.ts +8 -22
  85. package/src/column/column-helper.ts +65 -3
  86. package/src/column/column-to-record.ts +6 -4
  87. package/src/column-plugin.ts +141 -39
  88. package/src/data-plugin.ts +266 -0
  89. package/src/import-sets-plugin.ts +542 -0
  90. package/src/index.ts +4 -0
  91. package/src/json-plugin.ts +31 -12
  92. package/src/list-plugin.ts +91 -1
  93. package/src/now-attach-plugin.ts +399 -0
  94. package/src/now-config-plugin.ts +6 -2
  95. package/src/now-include-plugin.ts +8 -1
  96. package/src/package-json-plugin.ts +3 -3
  97. package/src/record-plugin.ts +61 -30
  98. package/src/repack/lint/Rules.ts +1 -10
  99. package/src/rest-api-plugin.ts +45 -51
  100. package/src/role-plugin.ts +1 -0
  101. package/src/server-module-plugin/index.ts +21 -5
  102. package/src/service-portal/widget-plugin.ts +4 -1
  103. package/src/static-content-plugin.ts +2 -2
  104. package/src/table-plugin.ts +47 -7
  105. package/src/ui-page-plugin.ts +2 -1
  106. package/src/ui-policy-plugin.ts +509 -0
  107. package/src/utils.ts +27 -1
  108. package/src/view-plugin.ts +1 -1
package/src/acl-plugin.ts CHANGED
@@ -54,7 +54,7 @@ export const AclPlugin = Plugin.create({
54
54
  },
55
55
  },
56
56
  toShape(record, { descendants }) {
57
- const type = reverseLookup(AclTypes, record.get('type').getValue()) || record.get('type')
57
+ const type = reverseLookup(AclTypes, record.get('type').getValue() as string) || record.get('type')
58
58
  const roles = descendants.query('sys_security_acl_role').map((m2m) => m2m.get('sys_user_role'))
59
59
 
60
60
  return {
@@ -75,14 +75,14 @@ export const AclPlugin = Plugin.create({
75
75
  appliesTo: $.from('applies_to').def(''),
76
76
  type: $.val(type),
77
77
  securityAttribute: $.from('security_attribute')
78
- .map((v) => reverseLookup(AclAttributes, v.getValue()) || v)
78
+ .map((v) => reverseLookup(AclAttributes, v.getValue() as string) || v)
79
79
  .def(''),
80
- operation: $.map((v) => reverseLookup(AclOperations, v.getValue()) || v),
80
+ operation: $.map((v) => reverseLookup(AclOperations, v.getValue() as string) || v),
81
81
  script: $.map((v) => v.ifString()?.getValue()).def(''),
82
82
  roles: $.val(roles.length > 0 ? roles : undefined),
83
83
  [merge]: $.from('type', 'name').map((type, name) =>
84
84
  getTableOrName(
85
- type.pipe((t) => reverseLookup(AclTypes, t.getValue())),
85
+ type.pipe((t) => reverseLookup(AclTypes, t.getValue() as string)),
86
86
  name.asString().getValue()
87
87
  )
88
88
  ),
@@ -159,8 +159,14 @@ export const AclPlugin = Plugin.create({
159
159
  name: $.from('name', 'table', 'field').map(
160
160
  (n, t, f) => n.ifString() ?? (f.ifString() ? `${t.getValue()}.${f.getValue()}` : t)
161
161
  ),
162
- type: $.map((type) => AclTypes[type.asString().getValue()] ?? type),
163
- operation: $.map((op) => AclOperations[op.asString().getValue()] ?? op),
162
+ type: $.map((type) => {
163
+ const typeKey = type.asString().getValue()
164
+ return AclTypes[typeKey as keyof typeof AclTypes] ?? type
165
+ }),
166
+ operation: $.map((op) => {
167
+ const opKey = op.asString().getValue()
168
+ return AclOperations[opKey as keyof typeof AclOperations] ?? op
169
+ }),
164
170
  advanced: $.val(advanced),
165
171
  script: $.map(
166
172
  (v) =>
@@ -168,7 +174,11 @@ export const AclPlugin = Plugin.create({
168
174
  v
169
175
  ).toCdata(),
170
176
  security_attribute: $.from('securityAttribute').map(
171
- (v) => v.ifString()?.pipe((v) => AclAttributes[v.getValue()]) ?? v
177
+ (v) =>
178
+ v.ifString()?.pipe((v) => {
179
+ const attrKey = v.getValue()
180
+ return AclAttributes[attrKey as keyof typeof AclAttributes]
181
+ }) ?? v
172
182
  ),
173
183
  })),
174
184
  })
@@ -243,7 +253,7 @@ export const AclPlugin = Plugin.create({
243
253
  })
244
254
 
245
255
  function getTableOrName(type: keyof typeof AclTypes, name: string) {
246
- if (AclNamedTypes[type]) {
256
+ if (type in AclNamedTypes) {
247
257
  return { name }
248
258
  }
249
259
  const split = name.indexOf('.')
@@ -253,7 +263,7 @@ function getTableOrName(type: keyof typeof AclTypes, name: string) {
253
263
  return { table, ...(field ? { field } : {}) }
254
264
  }
255
265
 
256
- function reverseLookup<T extends object>(obj: T, sysId): keyof T {
266
+ function reverseLookup<T extends object>(obj: T, sysId: string): keyof T {
257
267
  return (Object.entries(obj)
258
268
  .filter(([_, id]) => id === sysId)
259
269
  .map(([key]) => key)[0] || '') as keyof T
@@ -51,6 +51,7 @@ export const ApplicationMenuPlugin = Plugin.create({
51
51
  value: new CallExpressionShape({
52
52
  source: record,
53
53
  callee: 'ApplicationMenu',
54
+ exportName: record.get('sys_name')?.ifString()?.getValue(),
54
55
  args: [
55
56
  record.transform(({ $ }) => ({
56
57
  $id: $.val(NowIdShape.from(record)),
@@ -5,28 +5,30 @@ export type ATFVariableInfo = {
5
5
  inputVariableId: string
6
6
  type: string
7
7
  order: number
8
- default?: any
8
+ default?: unknown
9
9
  mandatory?: boolean
10
10
  }
11
11
 
12
12
  export type Category = keyof ATFType
13
13
 
14
- type FunctionParameters<F> = F extends (inputs: infer P) => any
14
+ type FunctionParameters<F> = F extends (inputs: infer P) => unknown
15
15
  ? P extends object
16
16
  ? Exclude<keyof P, keyof StandardStepValues>
17
17
  : never
18
18
  : never
19
19
 
20
- type ToRecordStepMetadata = {
20
+ export type StepMetadata<T extends FunctionParameters<unknown> = FunctionParameters<unknown>> = {
21
+ name: string
22
+ stepConfigId: string
23
+ variables: {
24
+ [K in T]: ATFVariableInfo
25
+ }
26
+ defaultTimeout?: string
27
+ }
28
+
29
+ export type ToRecordStepMetadata = {
21
30
  [C in keyof ATFType]: {
22
- [FunctionName in keyof ATFType[C]]: {
23
- name: string
24
- stepConfigId: string
25
- variables: {
26
- [K in FunctionParameters<ATFType[C][FunctionName]>]: ATFVariableInfo
27
- }
28
- defaultTimeout?: string
29
- }
31
+ [FunctionName in keyof ATFType[C]]: StepMetadata<FunctionParameters<ATFType[C][FunctionName]>>
30
32
  }
31
33
  }
32
34
 
@@ -2355,7 +2357,7 @@ export type ToShapeStepMetadata = Record<
2355
2357
  field: string
2356
2358
  type: string
2357
2359
  mandatory: boolean
2358
- default?: any
2360
+ default?: unknown
2359
2361
  }
2360
2362
  >
2361
2363
  }
@@ -20,9 +20,15 @@ import { NowIdShape } from '../now-id-plugin'
20
20
  import { ArrowFunctionShape } from '../arrow-function-plugin'
21
21
  import { create } from 'xmlbuilder2'
22
22
  import { Test } from '@servicenow/sdk-core/runtime/app'
23
- import { type ATFVariableInfo, type Category, ToShapeStepConfigs, ToRecordStepConfigs } from './step-configs'
23
+ import {
24
+ type ATFVariableInfo,
25
+ type Category,
26
+ ToShapeStepConfigs,
27
+ ToRecordStepConfigs,
28
+ type StepMetadata,
29
+ } from './step-configs'
24
30
  import { durationFieldToXML, formatDateToPlatformFormat, parseGlideDuration } from '@servicenow/sdk-build-core'
25
- import type { TimeDuration } from '@servicenow/sdk/core'
31
+ import type { Duration } from '@servicenow/sdk/core'
26
32
 
27
33
  export const TestPlugin = Plugin.create({
28
34
  name: 'TestPlugin',
@@ -381,7 +387,7 @@ function getFieldValuesFromElementMappingRecords(elementMappingRecords: RecordSh
381
387
  }
382
388
 
383
389
  function convertTimeoutToSystemFormat(timeoutShape: Shape) {
384
- const timeoutDuration = timeoutShape.ifObject()?.getValue() as TimeDuration | undefined
390
+ const timeoutDuration = timeoutShape.ifObject()?.getValue() as Duration | undefined
385
391
  const timeoutDate = timeoutDuration && durationFieldToXML(timeoutDuration)
386
392
  return timeoutDate && formatDateToPlatformFormat(timeoutDate)
387
393
  }
@@ -389,8 +395,9 @@ function convertTimeoutToSystemFormat(timeoutShape: Shape) {
389
395
  function anyStepsAreUnknown(stepRecords: RecordShape[], testName: string) {
390
396
  return stepRecords.some((stepRecord) => {
391
397
  const stepConfigId = stepRecord.get('step_config').toString().getValue()
392
- if (!ToShapeStepConfigs[stepConfigId]) {
393
- const stepConfigName = UNSUPPORTED_STEP_CONFIGS[stepConfigId] ?? stepConfigId
398
+ if (!ToShapeStepConfigs[stepConfigId as keyof typeof ToShapeStepConfigs]) {
399
+ const stepConfigName =
400
+ UNSUPPORTED_STEP_CONFIGS[stepConfigId as keyof typeof UNSUPPORTED_STEP_CONFIGS] ?? stepConfigId
394
401
  console.error(
395
402
  `Unable to transform ATF test '${testName}' because of unsupported step_config: '${stepConfigName}'`
396
403
  )
@@ -436,7 +443,11 @@ function propertyAccessToGemExpression(
436
443
  return `{{step['${stepId}']${suffix}}}`
437
444
  }
438
445
 
439
- function createGemExpressionsFromShape(shape: Shape, stepRecords: RecordShape[], diagnostics: Diagnostics) {
446
+ function createGemExpressionsFromShape(
447
+ shape: Shape,
448
+ stepRecords: RecordShape[],
449
+ diagnostics: Diagnostics
450
+ ): string | Record<string, unknown> | undefined {
440
451
  if (shape instanceof PropertyAccessShape) {
441
452
  return propertyAccessToGemExpression(shape, stepRecords, diagnostics)
442
453
  }
@@ -452,7 +463,7 @@ function createGemExpressionsFromShape(shape: Shape, stepRecords: RecordShape[],
452
463
  }
453
464
 
454
465
  if (shape instanceof ObjectShape) {
455
- const obj = {}
466
+ const obj: Record<string, unknown> = {}
456
467
  let gemExpressionFound = false
457
468
  for (const key in shape.properties()) {
458
469
  const value = createGemExpressionsFromShape(shape.get(key, false), stepRecords, diagnostics)
@@ -499,23 +510,26 @@ function getStepInfoFromAtfCallExpression(
499
510
  return undefined
500
511
  }
501
512
 
502
- const [, category, funcName] = callExpression.getCallee().split('.')
513
+ const [, category, funcName] = callExpression.getCallee().split('.') as [never, Category?, string?]
514
+
503
515
  if (!category || !funcName) {
504
516
  diagnostics.info(callExpression, 'Invalid ATF step')
505
517
  return undefined
506
518
  }
507
519
 
508
- if (!ToRecordStepConfigs[category]) {
520
+ const categoryKey = category
521
+ if (!ToRecordStepConfigs[categoryKey]) {
509
522
  diagnostics.warn(callExpression, `Unknown category: ${category}`)
510
523
  return undefined
511
524
  }
512
525
 
513
- if (!ToRecordStepConfigs[category][funcName]) {
526
+ const categorySteps = ToRecordStepConfigs[categoryKey]
527
+ if (!(funcName in categorySteps)) {
514
528
  diagnostics.warn(callExpression, `Unknown step: ${funcName}`)
515
529
  return undefined
516
530
  }
517
531
 
518
- const stepConfig = ToRecordStepConfigs[category as Category][funcName]!
532
+ const stepConfig = categorySteps[funcName as keyof typeof categorySteps] as StepMetadata
519
533
  const values = callExpression.getArgument(0).asObject()
520
534
  return {
521
535
  displayName: stepConfig.name,
@@ -524,7 +538,7 @@ function getStepInfoFromAtfCallExpression(
524
538
  table: values.get('table')?.ifString(),
525
539
  source: callExpression,
526
540
  identifier,
527
- defaultTimeout: stepConfig.defaultTimeout,
541
+ defaultTimeout: stepConfig.defaultTimeout as string,
528
542
  values,
529
543
  }
530
544
  }
@@ -566,14 +580,19 @@ function variableElementPathToPropertyAccess(source: Source, propertyAccessPath:
566
580
  })
567
581
  }
568
582
 
569
- function getElementMappingValues(source: Source, value: any, stepIdToVarName: Map<string, string>) {
583
+ function getElementMappingValues(
584
+ source: Source,
585
+ value: unknown,
586
+ stepIdToVarName: Map<string, string>
587
+ ): object | object[] | string | undefined {
570
588
  if (typeof value === 'object' && value !== null) {
571
589
  if (Array.isArray(value)) {
572
590
  return value.map((v) => getElementMappingValues(source, v, stepIdToVarName))
573
591
  } else {
574
- const obj = {}
575
- for (const key in value) {
576
- obj[key] = getElementMappingValues(source, value[key], stepIdToVarName)
592
+ const obj: Record<string, unknown> = {}
593
+ const valueObj = value
594
+ for (const key in valueObj) {
595
+ obj[key] = getElementMappingValues(source, valueObj[key as keyof typeof valueObj], stepIdToVarName)
577
596
  }
578
597
  return obj
579
598
  }
@@ -581,13 +600,13 @@ function getElementMappingValues(source: Source, value: any, stepIdToVarName: Ma
581
600
 
582
601
  const potentialGemExpression = typeof value === 'string' ? value : undefined
583
602
  if (!potentialGemExpression) {
584
- return value
603
+ return value as string
585
604
  }
586
605
 
587
606
  const [matched] = Array.from(potentialGemExpression.matchAll(GemRegex))
588
607
  const simpleGemExpression = matched && matched[0] === potentialGemExpression
589
608
  if (!matched) {
590
- return value
609
+ return value as string
591
610
  }
592
611
 
593
612
  // If the GEM expression is simple, it only contains the GEM
@@ -643,7 +662,7 @@ function getElementMappingValues(source: Source, value: any, stepIdToVarName: Ma
643
662
  })
644
663
  }
645
664
 
646
- function objectToEncodedQuery(objectShape: Record<string, any>) {
665
+ function objectToEncodedQuery(objectShape: Record<string, object | string | number | boolean>) {
647
666
  return Object.entries(objectShape)
648
667
  .map(([key, value]) => {
649
668
  const escapedValue = value.toString().replaceAll('^', '^^')
@@ -653,7 +672,7 @@ function objectToEncodedQuery(objectShape: Record<string, any>) {
653
672
  .join('^')
654
673
  }
655
674
 
656
- export function serializeValueToRecord(value: any, type: string, defaultValue: any) {
675
+ export function serializeValueToRecord(value: unknown, type: string, defaultValue: unknown) {
657
676
  value = value === undefined ? defaultValue : value
658
677
 
659
678
  switch (type) {
@@ -670,7 +689,7 @@ export function serializeValueToRecord(value: any, type: string, defaultValue: a
670
689
  return JSON.stringify(value)
671
690
  }
672
691
  case 'template_value':
673
- return value && objectToEncodedQuery(value)
692
+ return value && objectToEncodedQuery(value as Record<string, object | string | number | boolean>)
674
693
  case 'boolean':
675
694
  return value ? 1 : 0
676
695
  default:
@@ -24,7 +24,11 @@ import {
24
24
  remove,
25
25
  isFluentFile,
26
26
  path as pathModule,
27
+ Record as DBRecord,
28
+ asDataHelper,
27
29
  } from '@servicenow/sdk-build-core'
30
+ import { isDataHelper } from './data-plugin'
31
+ import { getCallExpressionName } from './utils'
28
32
 
29
33
  export const BasicSyntaxPlugin = Plugin.create({
30
34
  name: 'BasicSyntaxPlugin',
@@ -70,8 +74,26 @@ export const BasicSyntaxPlugin = Plugin.create({
70
74
  return { success: false }
71
75
  }
72
76
 
73
- const name = pathModule.basename(source.path).replace(RegExp(`${pathModule.extname(source.path)}$`), '')
74
- const file = compiler.getOrCreateSourceFile(project.resolvePath(config.generatedDir, `${name}.now.ts`))
77
+ const { generatedDir, taxonomy } = config
78
+ const tableName =
79
+ shape.getSource() instanceof DBRecord ? (shape.getSource() as DBRecord).getTable() : undefined
80
+
81
+ const dir =
82
+ tableName && taxonomy.mapping[tableName]
83
+ ? pathModule.join(generatedDir, taxonomy.mapping[tableName])
84
+ : generatedDir
85
+
86
+ let name = pathModule.basename(source.path).replace(RegExp(`${pathModule.extname(source.path)}$`), '')
87
+ if (project.isTypesGenerationMode()) {
88
+ // In types generation mode, use the exported variable name as the file name
89
+ // Scope directory is already set via per-scope Project rootDir
90
+ if (shape.is(VariableStatementShape) && shape.isExported()) {
91
+ name = shape.getVariableName().getName()
92
+ }
93
+ }
94
+ const file = compiler.getOrCreateSourceFile(
95
+ project.resolvePath(project.getRootDir(), dir, `${name}.now.ts`)
96
+ )
75
97
  const statement = project
76
98
  .addFile(file.getFilePath(), { resolveDependencies: false })
77
99
  .addStatement(shape.getCode())
@@ -167,6 +189,16 @@ export const BasicSyntaxPlugin = Plugin.create({
167
189
  // Turn the number back into a string and compare with the original string to avoid
168
190
  // lossy conversions due to floating point precision or other factors
169
191
  return number.getValue().toString() === shape.getValue() ? number : undefined
192
+ } else if (isDataHelper(target)) {
193
+ if (!ts.Node.isCallExpression(target)) {
194
+ return undefined
195
+ }
196
+ const helperName = getCallExpressionName(target)
197
+ const timeZone = target
198
+ .getArguments()[1]
199
+ ?.asKind(ts.SyntaxKind.StringLiteral)
200
+ ?.getLiteralValue()
201
+ return asDataHelper(helperName, shape, timeZone)
170
202
  } else {
171
203
  return undefined
172
204
  }
@@ -272,7 +304,7 @@ export const BasicSyntaxPlugin = Plugin.create({
272
304
  const existingPropKey = [...shape.getAliases(name), name].find((aliasedKey) =>
273
305
  existingNames.has(aliasedKey)
274
306
  )
275
- if (existingPropKey) {
307
+ if (existingPropKey !== undefined) {
276
308
  if (existingPropKey !== name) {
277
309
  target
278
310
  .getPropertyOrThrow(existingPropKey)
@@ -280,6 +312,7 @@ export const BasicSyntaxPlugin = Plugin.create({
280
312
  .getNameNode()
281
313
  .replaceWithText(name)
282
314
  }
315
+
283
316
  await commit(value, existingNames.get(existingPropKey) as ts.Expression)
284
317
  } else if (!value.equals(shape.getDefault(name))) {
285
318
  propsToAdd.push({
@@ -318,35 +351,50 @@ export const BasicSyntaxPlugin = Plugin.create({
318
351
  {
319
352
  shape: VariableStatementShape,
320
353
  async commit(shape, target, { commit }) {
321
- if (!ts.Node.isVariableStatement(target)) {
322
- const parentStatement = target.getParentIfKind(ts.SyntaxKind.ExpressionStatement)
323
- if (ts.Node.isCallExpression(target) && parentStatement) {
324
- parentStatement.replaceWithText(shape.getCode())
325
- return { success: true }
326
- }
354
+ // If target is ExpressionStatement, replace it directly
355
+ if (ts.Node.isExpressionStatement(target)) {
327
356
  target.replaceWithText(shape.getCode())
328
357
  return { success: true }
329
358
  }
330
359
 
331
- if (target.isExported() !== shape.isExported()) {
332
- target.setIsExported(shape.isExported())
360
+ // If target is CallExpression, check parent type
361
+ if (ts.Node.isCallExpression(target)) {
362
+ const expressionStatement = target.getParentIfKind(ts.SyntaxKind.ExpressionStatement)
363
+ if (expressionStatement) {
364
+ expressionStatement.replaceWithText(shape.getCode())
365
+ return { success: true }
366
+ }
367
+ }
368
+
369
+ // Get VariableStatement for in-place updates (either from CallExpression ancestor or target itself)
370
+ const variableStatement = ts.Node.isVariableStatement(target)
371
+ ? target
372
+ : target.getFirstAncestorByKindOrThrow(ts.SyntaxKind.VariableStatement)
373
+
374
+ // Update export modifier if needed
375
+ if (variableStatement.isExported() !== shape.isExported()) {
376
+ variableStatement.setIsExported(shape.isExported())
333
377
  }
334
378
 
335
- const [declaration, otherDeclaration] = target.getDeclarations()
379
+ // Get the variable declaration
380
+ const [declaration, otherDeclaration] = variableStatement.getDeclarations()
336
381
  if (!declaration) {
337
- throw new Error(`Variable statement is missing a declaration: ${target.getFullText()}`)
382
+ throw new Error(`Variable statement is missing a declaration: ${variableStatement.getFullText()}`)
338
383
  }
339
384
 
385
+ // Remove duplicate declarations
340
386
  if (otherDeclaration) {
341
387
  removeReferences(otherDeclaration)
342
388
  remove(otherDeclaration)
343
389
  }
344
390
 
391
+ // Update variable name if changed
345
392
  const variableName = shape.getVariableName()
346
393
  if (declaration.getName() !== variableName.getName()) {
347
394
  await commit(variableName, declaration.getNameNode())
348
395
  }
349
396
 
397
+ // Update initializer
350
398
  await commit(shape.getInitializer(), declaration.getInitializerOrThrow())
351
399
  return { success: true }
352
400
  },
@@ -7,10 +7,13 @@ import { NowIncludeShape } from './now-include-plugin'
7
7
  // Similar to lodash's groupBy, but simply stores the last element of each group
8
8
  function groupByExistence<T>(collection: T[] | undefined, keyProvider: (element: T) => string): { [key: string]: T } {
9
9
  return (
10
- collection?.reduce((groups, e) => {
11
- groups[keyProvider(e)] = e
12
- return groups
13
- }, {}) ?? {}
10
+ collection?.reduce(
11
+ (groups, e) => {
12
+ groups[keyProvider(e)] = e
13
+ return groups
14
+ },
15
+ {} as { [key: string]: T }
16
+ ) ?? {}
14
17
  )
15
18
  }
16
19
 
@@ -59,7 +59,7 @@ export const ClaimsPlugin = Plugin.create({
59
59
  return { success: false }
60
60
  }
61
61
 
62
- const allClaims = file.getJson()
62
+ const allClaims = file.getJson().asObject()
63
63
  const records: Record[] = []
64
64
  for (const [updateName, claims] of allClaims.entries()) {
65
65
  for (const claim of claims
@@ -1,6 +1,7 @@
1
- import { type Compiler, ts, CallExpressionShape, Plugin } from '@servicenow/sdk-build-core'
1
+ import { CallExpressionShape, Plugin } from '@servicenow/sdk-build-core'
2
2
  import { NowIdShape } from './now-id-plugin'
3
- import { generateDeprecatedDiagnostics } from './utils'
3
+ import { generateDeprecatedDiagnostics, validateClientSideScript } from './utils'
4
+ import { NowIncludeShape } from './now-include-plugin'
4
5
 
5
6
  enum UITypeMapping {
6
7
  desktop = 0,
@@ -45,7 +46,8 @@ export const ClientScriptPlugin = Plugin.create({
45
46
  name: 'ClientScriptPlugin',
46
47
  records: {
47
48
  sys_script_client: {
48
- toShape(record) {
49
+ async toShape(record, { transform }) {
50
+ const script = await NowIncludeShape.fromRecord(record, record.get('script'), transform)
49
51
  return {
50
52
  success: true,
51
53
  value: new CallExpressionShape({
@@ -59,7 +61,7 @@ export const ClientScriptPlugin = Plugin.create({
59
61
  table: $,
60
62
  appliesExtended: $.from('applies_extended').toBoolean().def(false),
61
63
  isolateScript: $.from('isolate_script').toBoolean().def(false),
62
- script: $,
64
+ script: $.val(script),
63
65
  name: $,
64
66
  description: $.def(''),
65
67
  messages: $.def(''),
@@ -110,7 +112,7 @@ export const ClientScriptPlugin = Plugin.create({
110
112
  )
111
113
  }
112
114
 
113
- if (script && !validateClientSideScripts(script, compiler)) {
115
+ if (script && !validateClientSideScript(script, compiler)) {
114
116
  diagnostics.error(
115
117
  clientScript.get('script'),
116
118
  `Client side scripts cannot import or require modules.`
@@ -147,17 +149,6 @@ export const ClientScriptPlugin = Plugin.create({
147
149
  ],
148
150
  })
149
151
 
150
- function validateClientSideScripts(script: string, compiler: Compiler) {
151
- const source = compiler.createSourceFile('tmp-file.ts', script)
152
- const importDeclarations = source.getDescendantsOfKind(ts.SyntaxKind.ImportDeclaration)
153
- const requireCalls = source.getDescendantsOfKind(ts.SyntaxKind.CallExpression).filter((callExpression) => {
154
- return isRequire(callExpression)
155
- })
156
- const isValid = !(importDeclarations.length > 0 || requireCalls.length > 0)
157
- compiler.removeSourceFile(source)
158
- return isValid
159
- }
160
-
161
152
  function getUITypeFromId(id: number) {
162
153
  const type = UITypeMapping[id]
163
154
  if (!type) {
@@ -168,12 +159,7 @@ function getUITypeFromId(id: number) {
168
159
 
169
160
  function getUITypeId(value: string) {
170
161
  if (value in UITypeMapping) {
171
- return UITypeMapping[value]
162
+ return UITypeMapping[value as keyof typeof UITypeMapping]
172
163
  }
173
164
  throw Error('Invalid ui_type found in xml')
174
165
  }
175
-
176
- function isRequire(callExpression: ts.CallExpression): boolean {
177
- const expression = callExpression.getExpression()
178
- return ts.Node.isIdentifier(expression) && expression.getText() === 'require'
179
- }
@@ -1,5 +1,6 @@
1
1
  import { CallExpressionShape, Shape } from '@servicenow/sdk-build-core'
2
2
  import {
3
+ ApprovalRulesColumn,
3
4
  BasicDateTimeColumn,
4
5
  BasicImageColumn,
5
6
  BooleanColumn,
@@ -8,25 +9,42 @@ import {
8
9
  ConditionsColumn,
9
10
  DateColumn,
10
11
  DateTimeColumn,
12
+ DayOfWeekColumn,
13
+ DaysOfWeekColumn,
11
14
  DecimalColumn,
12
15
  DocumentIdColumn,
13
16
  DomainIdColumn,
14
17
  DomainPathColumn,
15
18
  DueDateColumn,
19
+ DurationColumn,
20
+ EmailColumn,
21
+ FieldListColumn,
16
22
  FieldNameColumn,
23
+ FloatColumn,
24
+ GuidColumn,
25
+ HtmlColumn,
17
26
  IntegerColumn,
18
27
  IntegerDateColumn,
28
+ JsonColumn,
19
29
  ListColumn,
30
+ MultiLineTextColumn,
31
+ NameValuePairsColumn,
20
32
  OtherDateColumn,
33
+ Password2Column,
21
34
  RadioColumn,
35
+ RecordsColumn,
22
36
  ReferenceColumn,
23
37
  ScheduleDateTimeColumn,
24
38
  ScriptColumn,
39
+ SlushBucketColumn,
25
40
  StringColumn,
26
41
  SystemClassNameColumn,
27
42
  TableNameColumn,
43
+ TemplateValueColumn,
44
+ TimeColumn,
28
45
  TranslatedFieldColumn,
29
46
  TranslatedTextColumn,
47
+ UrlColumn,
30
48
  UserRolesColumn,
31
49
  VersionColumn,
32
50
  } from '@servicenow/sdk-core/runtime/db'
@@ -61,6 +79,25 @@ export const COLUMN_TYPE_TO_API: Record<string, string> = {
61
79
  version: VersionColumn.name,
62
80
  glide_list: ListColumn.name,
63
81
  image: BasicImageColumn.name,
82
+ // New column types
83
+ GUID: GuidColumn.name,
84
+ password2: Password2Column.name,
85
+ json: JsonColumn.name,
86
+ url: UrlColumn.name,
87
+ email: EmailColumn.name,
88
+ html: HtmlColumn.name,
89
+ float: FloatColumn.name,
90
+ glide_duration: DurationColumn.name,
91
+ glide_time: TimeColumn.name,
92
+ field_list: FieldListColumn.name,
93
+ records: RecordsColumn.name,
94
+ slushbucket: SlushBucketColumn.name,
95
+ name_values: NameValuePairsColumn.name,
96
+ approval_rules: ApprovalRulesColumn.name,
97
+ multi_two_lines: MultiLineTextColumn.name,
98
+ template_value: TemplateValueColumn.name,
99
+ day_of_week: DayOfWeekColumn.name,
100
+ days_of_week: DaysOfWeekColumn.name,
64
101
  }
65
102
 
66
103
  export const COLUMN_API_TO_TYPE = Object.fromEntries(
@@ -87,25 +124,50 @@ export function addFieldsToColumn(
87
124
  })
88
125
  }
89
126
 
90
- export function getDefaultMaxLength(columnType: string): number {
127
+ export function getDefaultMaxLength(columnType: string | undefined): number {
91
128
  switch (columnType) {
92
129
  case 'conditions':
93
130
  case 'glide_list':
131
+ case 'json':
132
+ case 'name_values':
133
+ case 'multi_two_lines':
94
134
  return 4000
135
+ case 'template_value':
136
+ return 65000
137
+ case 'script':
138
+ return 8000
139
+ case 'html':
140
+ return 65536
95
141
  case 'decimal':
96
142
  return 20
143
+ case 'float':
144
+ return 15
97
145
  case 'domain_id':
98
146
  case 'document_id':
99
147
  case 'reference':
148
+ case 'GUID':
100
149
  return 32
150
+ case 'string_full_utf8':
101
151
  case 'domain_path':
102
152
  case 'user_roles':
153
+ case 'password2':
154
+ case 'email':
103
155
  return 255
104
- case 'script':
105
- return 8000
156
+ case 'url':
157
+ case 'records':
158
+ return 1024
106
159
  case 'table_name':
107
160
  case 'field_name':
108
161
  return 80
162
+ case 'glide_duration':
163
+ return 20
164
+ case 'glide_time':
165
+ return 8
166
+ case 'field_list':
167
+ case 'approval_rules':
168
+ return 1024
169
+ case 'slushbucket':
170
+ return 4000
109
171
  default:
110
172
  /**
111
173
  * For types mentioned below it default to 40 on instance