@servicenow/sdk-build-core 2.1.3 → 2.2.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 (66) hide show
  1. package/dist/BuildOptions.d.ts +0 -8
  2. package/dist/BuildOptions.js +2 -4
  3. package/dist/BuildOptions.js.map +1 -1
  4. package/dist/GUID.d.ts +1 -1
  5. package/dist/GUID.js +5 -33
  6. package/dist/GUID.js.map +1 -1
  7. package/dist/Keys.d.ts +3 -3
  8. package/dist/Keys.js +5 -6
  9. package/dist/Keys.js.map +1 -1
  10. package/dist/TypeScript.d.ts +1 -1
  11. package/dist/TypeScript.js +4 -27
  12. package/dist/TypeScript.js.map +1 -1
  13. package/dist/XML.js +1 -4
  14. package/dist/XML.js.map +1 -1
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.js +0 -14
  17. package/dist/index.js.map +1 -1
  18. package/dist/plugins/Context.d.ts +3 -8
  19. package/dist/plugins/Diagnostic.d.ts +2 -3
  20. package/dist/plugins/Diagnostic.js +1 -25
  21. package/dist/plugins/Diagnostic.js.map +1 -1
  22. package/dist/plugins/behaviors/Diagnostics.d.ts +1 -2
  23. package/dist/plugins/behaviors/extractors/Data.d.ts +1 -1
  24. package/dist/plugins/behaviors/extractors/Extractors.d.ts +1 -1
  25. package/dist/plugins/util/CallExpression.d.ts +1 -1
  26. package/dist/plugins/util/CallExpression.js +6 -32
  27. package/dist/plugins/util/CallExpression.js.map +1 -1
  28. package/dist/plugins/util/CodeTransformation.d.ts +12 -4
  29. package/dist/plugins/util/CodeTransformation.js +120 -56
  30. package/dist/plugins/util/CodeTransformation.js.map +1 -1
  31. package/dist/plugins/util/ConfigurationFunction.d.ts +3 -4
  32. package/dist/plugins/util/ConfigurationFunction.js +37 -37
  33. package/dist/plugins/util/ConfigurationFunction.js.map +1 -1
  34. package/dist/plugins/util/ObjectLiteral.d.ts +1 -1
  35. package/dist/plugins/util/ObjectLiteral.js +6 -29
  36. package/dist/plugins/util/ObjectLiteral.js.map +1 -1
  37. package/dist/util/Debug.js +5 -8
  38. package/dist/util/Debug.js.map +1 -1
  39. package/dist/util/Directive.d.ts +16 -0
  40. package/dist/util/Directive.js +107 -0
  41. package/dist/util/Directive.js.map +1 -0
  42. package/dist/util/Util.js +1 -25
  43. package/dist/util/Util.js.map +1 -1
  44. package/dist/util/index.d.ts +1 -0
  45. package/dist/util/index.js +1 -0
  46. package/dist/util/index.js.map +1 -1
  47. package/package.json +5 -10
  48. package/src/BuildOptions.ts +1 -3
  49. package/src/GUID.ts +3 -13
  50. package/src/Keys.ts +11 -12
  51. package/src/TypeScript.ts +3 -3
  52. package/src/XML.ts +1 -4
  53. package/src/index.ts +0 -1
  54. package/src/plugins/Context.ts +5 -9
  55. package/src/plugins/Diagnostic.ts +2 -3
  56. package/src/plugins/behaviors/Diagnostics.ts +1 -2
  57. package/src/plugins/behaviors/extractors/Data.ts +1 -1
  58. package/src/plugins/behaviors/extractors/Extractors.ts +1 -1
  59. package/src/plugins/util/CallExpression.ts +4 -7
  60. package/src/plugins/util/CodeTransformation.ts +92 -24
  61. package/src/plugins/util/ConfigurationFunction.ts +72 -81
  62. package/src/plugins/util/ObjectLiteral.ts +1 -1
  63. package/src/util/Debug.ts +7 -10
  64. package/src/util/Directive.ts +123 -0
  65. package/src/util/Util.ts +1 -2
  66. package/src/util/index.ts +1 -0
@@ -1,17 +1,4 @@
1
- import { Logger } from '@servicenow/sdk-project'
2
- import {
3
- ArrowFunction,
4
- CallExpression,
5
- FunctionExpression,
6
- Identifier,
7
- Node,
8
- ObjectLiteralExpression,
9
- PropertyAccessExpression,
10
- PropertyAssignment,
11
- SyntaxKind,
12
- TemplateExpression,
13
- ts,
14
- } from 'ts-morph'
1
+ import { Logger, ts } from '@servicenow/sdk-project'
15
2
  import { Context } from '../Context'
16
3
  import { EntityData, ObjectData } from '../behaviors'
17
4
  import { getCallExpressionName } from './CallExpression'
@@ -45,7 +32,7 @@ export type ComposerClass = {
45
32
  export class ConfigurationFunctionEntityData extends EntityData {
46
33
  constructor(
47
34
  private readonly config: ConfigEntity,
48
- node: CallExpression
35
+ node: ts.CallExpression
49
36
  ) {
50
37
  super(
51
38
  'test',
@@ -73,10 +60,10 @@ export class ConfigurationFunctionEntityData extends EntityData {
73
60
  * ```
74
61
  */
75
62
  export class ConfigurationFunctionExtractor {
76
- private rootNode: CallExpression
63
+ private rootNode: ts.CallExpression
77
64
  private context: Context
78
65
  private composerClass: ComposerClass
79
- private identifiers: (Identifier | null)[]
66
+ private identifiers: (ts.Identifier | null)[]
80
67
 
81
68
  /**
82
69
  *
@@ -84,7 +71,7 @@ export class ConfigurationFunctionExtractor {
84
71
  * @param context context from the plugin this is called in
85
72
  * @param composerClass A class whose prototype will be used to validate the allowed functions within the configuration function
86
73
  */
87
- constructor(rootNode: CallExpression, context: Context, composerClass: ComposerClass) {
74
+ constructor(rootNode: ts.CallExpression, context: Context, composerClass: ComposerClass) {
88
75
  this.rootNode = rootNode
89
76
  this.context = context
90
77
  this.composerClass = composerClass
@@ -94,11 +81,11 @@ export class ConfigurationFunctionExtractor {
94
81
  /**
95
82
  * Grabs the property access expression chain of a call expression, and protects against accidentally grabbing property access expressions in arguments of the call expression
96
83
  */
97
- private getPropChain(ce: CallExpression): PropertyAccessExpression[] {
84
+ private getPropChain(ce: ts.CallExpression): ts.PropertyAccessExpression[] {
98
85
  // Do this to ensure we are only considering property access expressions that are apart of the chain and not potentially in the arguments of the function
99
- const props = [ce.getFirstDescendantByKindOrThrow(SyntaxKind.PropertyAccessExpression)]
100
- const childProps = (props[0] as PropertyAccessExpression).getDescendantsOfKind(
101
- SyntaxKind.PropertyAccessExpression
86
+ const props = [ce.getFirstDescendantByKindOrThrow(ts.SyntaxKind.PropertyAccessExpression)]
87
+ const childProps = (props[0] as ts.PropertyAccessExpression).getDescendantsOfKind(
88
+ ts.SyntaxKind.PropertyAccessExpression
102
89
  )
103
90
  if (childProps.length > 0) {
104
91
  props.push(...childProps)
@@ -111,15 +98,15 @@ export class ConfigurationFunctionExtractor {
111
98
  * v
112
99
  * a.b.c.d({})
113
100
  */
114
- private getFirstProp(ce: CallExpression): Identifier {
101
+ private getFirstProp(ce: ts.CallExpression): ts.Identifier {
115
102
  // Need to use this instead of just grabbing descendents cause the arguments of the call expression can also potentially have property access expressions which we don't want to consider
116
103
  const props = this.getPropChain(ce)
117
104
  if (props.length === 1) {
118
- const ids = props[0]?.getChildrenOfKind(SyntaxKind.Identifier) as Identifier[]
119
- return ids[0] as Identifier
105
+ const ids = props[0]?.getChildrenOfKind(ts.SyntaxKind.Identifier) as ts.Identifier[]
106
+ return ids[0] as ts.Identifier
120
107
  }
121
- const lastProp = props[props.length - 1] as PropertyAccessExpression
122
- return lastProp.getChildrenOfKind(SyntaxKind.Identifier)[0] as Identifier
108
+ const lastProp = props[props.length - 1] as ts.PropertyAccessExpression
109
+ return lastProp.getChildrenOfKind(ts.SyntaxKind.Identifier)[0] as ts.Identifier
123
110
  }
124
111
 
125
112
  /**
@@ -128,11 +115,11 @@ export class ConfigurationFunctionExtractor {
128
115
  * v
129
116
  * a.b.c.d({})
130
117
  */
131
- private getLastProp(ce: CallExpression): Identifier {
118
+ private getLastProp(ce: ts.CallExpression): ts.Identifier {
132
119
  // Do not need special chain handling here like in getFirstProp cause we are always grabbing the property access expression from the top level regardless of chain length
133
- const props = ce.getFirstDescendantByKindOrThrow(SyntaxKind.PropertyAccessExpression)
134
- const ids = props.getChildrenOfKind(SyntaxKind.Identifier) as Identifier[]
135
- return ids[ids.length - 1] as Identifier // Identifier is either the only one present or is the last of 2 so this should fetch it in either scenario
120
+ const props = ce.getFirstDescendantByKindOrThrow(ts.SyntaxKind.PropertyAccessExpression)
121
+ const ids = props.getChildrenOfKind(ts.SyntaxKind.Identifier) as ts.Identifier[]
122
+ return ids[ids.length - 1] as ts.Identifier // Identifier is either the only one present or is the last of 2 so this should fetch it in either scenario
136
123
  }
137
124
 
138
125
  /**
@@ -140,7 +127,7 @@ export class ConfigurationFunctionExtractor {
140
127
  * @param ce
141
128
  * @param parameterName
142
129
  */
143
- private isValidCallExpression(ce: CallExpression, paramId: Identifier) {
130
+ private isValidCallExpression(ce: ts.CallExpression, paramId: ts.Identifier) {
144
131
  if (!paramId) {
145
132
  throw Error('No parameter provided to configuration function')
146
133
  }
@@ -165,7 +152,7 @@ export class ConfigurationFunctionExtractor {
165
152
  }
166
153
 
167
154
  // Verify that call expression has the expected number of arguments
168
- const funcArgs = ce.getFirstDescendantByKind(SyntaxKind.SyntaxList)?.getChildren() as Node[]
155
+ const funcArgs = ce.getFirstDescendantByKind(ts.SyntaxKind.SyntaxList)?.getChildren() as ts.Node[]
169
156
  if (!funcArgs) {
170
157
  throw Error('Call expression had no arguments at all')
171
158
  }
@@ -174,7 +161,7 @@ export class ConfigurationFunctionExtractor {
174
161
  throw Error('Call expressions only support a single argument')
175
162
  }
176
163
 
177
- if ((funcArgs[0] as Node).getKind() != SyntaxKind.ObjectLiteralExpression) {
164
+ if ((funcArgs[0] as ts.Node).getKind() != ts.SyntaxKind.ObjectLiteralExpression) {
178
165
  throw Error('Call expression must have an object as its argument')
179
166
  }
180
167
  }
@@ -186,7 +173,7 @@ export class ConfigurationFunctionExtractor {
186
173
  * @param identifier
187
174
  * @param data
188
175
  */
189
- private findReferencedIdentifier(identifier: Identifier) {
176
+ private findReferencedIdentifier(identifier: ts.Identifier) {
190
177
  const idName = identifier.getSymbol()?.getName()
191
178
  // Identifier index lines up with step index, removed from step object so identifiers are not returned in extracted object
192
179
  for (let i = 0; i < this.identifiers.length; i++) {
@@ -200,29 +187,29 @@ export class ConfigurationFunctionExtractor {
200
187
  return -1
201
188
  }
202
189
 
203
- private getRootIdentifier(prop: PropertyAccessExpression): Identifier {
204
- const children = prop.getChildrenOfKind(SyntaxKind.PropertyAccessExpression)
190
+ private getRootIdentifier(prop: ts.PropertyAccessExpression): ts.Identifier {
191
+ const children = prop.getChildrenOfKind(ts.SyntaxKind.PropertyAccessExpression)
205
192
  if (children.length == 0) {
206
- const id = prop.getFirstChildByKind(SyntaxKind.Identifier)
193
+ const id = prop.getFirstChildByKind(ts.SyntaxKind.Identifier)
207
194
  if (!id) {
208
195
  throw Error('Could not find identifier in property access expression')
209
196
  }
210
197
  return id
211
198
  }
212
- return this.getRootIdentifier(children[0] as PropertyAccessExpression)
199
+ return this.getRootIdentifier(children[0] as ts.PropertyAccessExpression)
213
200
  }
214
201
 
215
- private parseValue(valueNode: Node): any {
216
- if (valueNode.getKind() == SyntaxKind.ArrayLiteralExpression) {
202
+ private parseValue(valueNode: ts.Node): any {
203
+ if (valueNode.getKind() == ts.SyntaxKind.ArrayLiteralExpression) {
217
204
  const values: any[] = []
218
- const syntaxList = valueNode.getFirstDescendantByKind(SyntaxKind.SyntaxList)
205
+ const syntaxList = valueNode.getFirstDescendantByKind(ts.SyntaxKind.SyntaxList)
219
206
  if (!syntaxList) {
220
207
  return
221
208
  }
222
209
  const children = syntaxList.getChildren()
223
210
 
224
211
  for (const child of children) {
225
- if (child.getKind() == SyntaxKind.CommaToken) {
212
+ if (child.getKind() == ts.SyntaxKind.CommaToken) {
226
213
  continue
227
214
  }
228
215
  const value = this.parseValue(child)
@@ -231,13 +218,13 @@ export class ConfigurationFunctionExtractor {
231
218
  return values
232
219
  }
233
220
 
234
- if (valueNode.getKind() == SyntaxKind.ObjectLiteralExpression) {
235
- const objNode = valueNode as ObjectLiteralExpression
221
+ if (valueNode.getKind() == ts.SyntaxKind.ObjectLiteralExpression) {
222
+ const objNode = valueNode as ts.ObjectLiteralExpression
236
223
  return this.objNodeToJSON(objNode)
237
224
  }
238
225
 
239
- if (valueNode.getKind() == SyntaxKind.PropertyAccessExpression) {
240
- const propNode = valueNode as PropertyAccessExpression
226
+ if (valueNode.getKind() == ts.SyntaxKind.PropertyAccessExpression) {
227
+ const propNode = valueNode as ts.PropertyAccessExpression
241
228
  const identifier = this.getRootIdentifier(propNode)
242
229
  const refIdentifierIndex = this.findReferencedIdentifier(identifier)
243
230
  if (refIdentifierIndex == -1) {
@@ -249,9 +236,9 @@ export class ConfigurationFunctionExtractor {
249
236
  return { __references: refIdentifierIndex, __value: parsedValue[parsedValue.length - 1] }
250
237
  }
251
238
 
252
- if (valueNode.getKind() == SyntaxKind.TemplateExpression) {
253
- // console.info(`literal: ${(valueNode as TemplateExpression).getVal}`)
254
- const templateNode = valueNode as TemplateExpression
239
+ if (valueNode.getKind() == ts.SyntaxKind.TemplateExpression) {
240
+ // console.info(`literal: ${(valueNode as ts.TemplateExpression).getVal}`)
241
+ const templateNode = valueNode as ts.TemplateExpression
255
242
  const templateValue = [templateNode.getHead().getLiteralText()]
256
243
  templateNode.getTemplateSpans().forEach((s) => {
257
244
  templateValue.push(this.parseValue(s.getExpression()))
@@ -262,13 +249,13 @@ export class ConfigurationFunctionExtractor {
262
249
 
263
250
  // A variety of Identifiers. Will be handled here
264
251
  // TODO: This is swallowing diagnostics and not handling the case where no data was extracted
265
- return this.context.extractAst(valueNode)['data'][0]?.getValue()
252
+ return this.context.extractAst(valueNode)['data']?.getValue()
266
253
  }
267
254
 
268
- private getKVPair(prop: PropertyAssignment) {
255
+ private getKVPair(prop: ts.PropertyAssignment) {
269
256
  const props = prop.getChildren()
270
- const keyNode = props[0] as Node<ts.Node>
271
- const valueNode = props[2] as Node<ts.Node>
257
+ const keyNode = props[0] as ts.Node
258
+ const valueNode = props[2] as ts.Node
272
259
 
273
260
  const key = (keyNode as any).getLiteralValue ? (keyNode as any).getLiteralValue() : keyNode.getText()
274
261
  const value = this.parseValue(valueNode)
@@ -279,18 +266,18 @@ export class ConfigurationFunctionExtractor {
279
266
  return { key: undefined, value: undefined }
280
267
  }
281
268
 
282
- private objNodeToJSON(obj: ObjectLiteralExpression | undefined): { [name: string]: any } {
269
+ private objNodeToJSON(obj: ts.ObjectLiteralExpression | undefined): { [name: string]: any } {
283
270
  if (!obj) {
284
271
  throw Error('objNodeToJSON received an undefined object node')
285
272
  }
286
- const syntaxList = obj.getFirstDescendantByKind(SyntaxKind.SyntaxList)
273
+ const syntaxList = obj.getFirstDescendantByKind(ts.SyntaxKind.SyntaxList)
287
274
 
288
275
  if (!syntaxList) {
289
276
  throw Error('objNodeToJSON could not find a syntax list in the ObjectLiteralExpression')
290
277
  }
291
278
 
292
279
  const json = {}
293
- syntaxList.getChildrenOfKind(SyntaxKind.PropertyAssignment).forEach((arg) => {
280
+ syntaxList.getChildrenOfKind(ts.SyntaxKind.PropertyAssignment).forEach((arg) => {
294
281
  const propArg = arg
295
282
  const { key, value } = this.getKVPair(propArg)
296
283
  // TODO: What if the value is actually undefined?
@@ -301,35 +288,35 @@ export class ConfigurationFunctionExtractor {
301
288
  return json
302
289
  }
303
290
 
304
- private getName(node: CallExpression) {
291
+ private getName(node: ts.CallExpression) {
305
292
  // This may not be robust but should be a good enough
306
293
  const lastProp = this.getLastProp(node)
307
294
  return lastProp.getText()
308
295
  }
309
296
 
310
- private getConfigFunctionComponents(kind: string, node: CallExpression<ts.CallExpression>) {
297
+ private getConfigFunctionComponents(kind: string, node: ts.CallExpression) {
311
298
  kind
312
299
  const configArgs = node.getArguments()
313
300
  if (configArgs.length != 2) {
314
301
  throw Error('The plugin must have exactly 2 arguments')
315
302
  }
316
303
 
317
- if ((configArgs[0] as any).getKind() != SyntaxKind.ObjectLiteralExpression) {
304
+ if ((configArgs[0] as any).getKind() != ts.SyntaxKind.ObjectLiteralExpression) {
318
305
  throw Error("The plugin's first argument was not an object")
319
306
  }
320
307
 
321
308
  if (
322
- (configArgs[1] as any).getKind() != SyntaxKind.ArrowFunction &&
323
- (configArgs[1] as any).getKind() != SyntaxKind.FunctionExpression
309
+ (configArgs[1] as any).getKind() != ts.SyntaxKind.ArrowFunction &&
310
+ (configArgs[1] as any).getKind() != ts.SyntaxKind.FunctionExpression
324
311
  ) {
325
312
  throw Error("The plugin's second argument was not a function")
326
313
  }
327
314
 
328
- const objectArg = configArgs[0] as ObjectLiteralExpression
329
- const configFunction = configArgs[1] as ArrowFunction | FunctionExpression
315
+ const objectArg = configArgs[0] as ts.ObjectLiteralExpression
316
+ const configFunction = configArgs[1] as ts.ArrowFunction | ts.FunctionExpression
330
317
 
331
318
  // TODO: This is swallowing diagnostics and not handling the case where no data was extracted
332
- const objectArgValue = this.context.extractAst(objectArg!)['data'][0]?.getValue()
319
+ const objectArgValue = this.context.extractAst(objectArg!)['data']?.getValue()
333
320
 
334
321
  if (!('$id' in objectArgValue)) {
335
322
  throw Error('No $id defined in first argument')
@@ -337,25 +324,25 @@ export class ConfigurationFunctionExtractor {
337
324
 
338
325
  const parameters = configFunction.getParameters()
339
326
  // Checking the type is parameter is probably redundant but can not hurt
340
- if (parameters.length != 1 || !parameters[0] || parameters[0].getKind() != SyntaxKind.Parameter) {
327
+ if (parameters.length != 1 || !parameters[0] || parameters[0].getKind() != ts.SyntaxKind.Parameter) {
341
328
  throw Error('Configuration function must have an parameter as its only argument')
342
329
  }
343
330
 
344
- const paramId = parameters[0].getChildrenOfKind(SyntaxKind.Identifier)[0] as Identifier
345
- const block = configFunction.getFirstChildByKind(SyntaxKind.Block)
331
+ const paramId = parameters[0].getChildrenOfKind(ts.SyntaxKind.Identifier)[0] as ts.Identifier
332
+ const block = configFunction.getFirstChildByKind(ts.SyntaxKind.Block)
346
333
  if (!block) {
347
334
  throw Error(`Failed to find block in configuration function`)
348
335
  }
349
336
 
350
- const syntaxList = block.getFirstChildByKind(SyntaxKind.SyntaxList)
337
+ const syntaxList = block.getFirstChildByKind(ts.SyntaxKind.SyntaxList)
351
338
  if (!syntaxList) {
352
339
  throw Error(`Failed to find syntax list in configuration function`)
353
340
  }
354
341
  return { paramId, syntaxList, objectArgValue }
355
342
  }
356
343
 
357
- private extractCallExpression(node: Node, paramId: Identifier) {
358
- const callExpressions = node.getDescendantsOfKind(SyntaxKind.CallExpression)
344
+ private extractCallExpression(node: ts.Node, paramId: ts.Identifier) {
345
+ const callExpressions = node.getDescendantsOfKind(ts.SyntaxKind.CallExpression)
359
346
 
360
347
  if (callExpressions.length == 0) {
361
348
  throw Error(`Expression statement did not have any call expression`)
@@ -365,7 +352,7 @@ export class ConfigurationFunctionExtractor {
365
352
  throw Error(`Found more than one call expression inside of an expression statement`)
366
353
  }
367
354
 
368
- const callExpression = callExpressions[0] as CallExpression
355
+ const callExpression = callExpressions[0] as ts.CallExpression
369
356
  this.isValidCallExpression(callExpression, paramId)
370
357
  return callExpression
371
358
  }
@@ -410,7 +397,7 @@ export class ConfigurationFunctionExtractor {
410
397
  syntaxList.getChildren().forEach((statement) => {
411
398
  try {
412
399
  switch (statement.getKind()) {
413
- case SyntaxKind.ExpressionStatement: {
400
+ case ts.SyntaxKind.ExpressionStatement: {
414
401
  /**
415
402
  * (param) => {
416
403
  * param.foo({arg: 'value'}) <- Expression statement
@@ -422,41 +409,45 @@ export class ConfigurationFunctionExtractor {
422
409
  const ce = this.extractCallExpression(statement, paramId)
423
410
  data[stepNumber++] = {
424
411
  name: this.getName(ce),
425
- info: this.objNodeToJSON(ce.getFirstDescendantByKind(SyntaxKind.ObjectLiteralExpression)),
412
+ info: this.objNodeToJSON(
413
+ ce.getFirstDescendantByKind(ts.SyntaxKind.ObjectLiteralExpression)
414
+ ),
426
415
  }
427
416
  this.identifiers.push(null)
428
417
  break
429
418
  }
430
- case SyntaxKind.VariableStatement: {
419
+ case ts.SyntaxKind.VariableStatement: {
431
420
  /**
432
421
  * (param) => {
433
422
  * const output = param.foo({arg: 'value'}) <- Variable statement
434
423
  * }
435
424
  */
436
425
  const ce = this.extractCallExpression(statement, paramId)
437
- const variableDeclaration = statement.getFirstDescendantByKind(SyntaxKind.VariableDeclaration)
426
+ const variableDeclaration = statement.getFirstDescendantByKind(
427
+ ts.SyntaxKind.VariableDeclaration
428
+ )
438
429
 
439
430
  if (!variableDeclaration) {
440
431
  throw Error('Could not find variable declaration in variable statement')
441
432
  }
442
433
 
443
434
  const assignment = variableDeclaration.getChildren() // Format: identifier, =, value
444
- const identifier = assignment[0] as Identifier
445
- const value = assignment[2] as CallExpression // Already been validated above to be a valid call expression
435
+ const identifier = assignment[0] as ts.Identifier
436
+ const value = assignment[2] as ts.CallExpression // Already been validated above to be a valid call expression
446
437
 
447
438
  data[stepNumber++] = {
448
439
  name: this.getName(ce),
449
440
  info: this.objNodeToJSON(
450
- value.getFirstDescendantByKind(SyntaxKind.ObjectLiteralExpression)
441
+ value.getFirstDescendantByKind(ts.SyntaxKind.ObjectLiteralExpression)
451
442
  ),
452
443
  }
453
444
  this.identifiers.push(identifier)
454
445
  break
455
446
  }
456
- case SyntaxKind.MultiLineCommentTrivia:
447
+ case ts.SyntaxKind.MultiLineCommentTrivia:
457
448
  /** This is a multiline comment */
458
449
  break
459
- case SyntaxKind.SingleLineCommentTrivia:
450
+ case ts.SyntaxKind.SingleLineCommentTrivia:
460
451
  // This is a single line comment
461
452
  break
462
453
  default:
@@ -1,4 +1,4 @@
1
- import * as ts from 'ts-morph'
1
+ import { ts } from '@servicenow/sdk-project'
2
2
 
3
3
  /**
4
4
  * Retrieves the property assignment with the specified name and subpath from the given node.
package/src/util/Debug.ts CHANGED
@@ -11,9 +11,8 @@ export function inspect(val: unknown, depth = 2) {
11
11
  }
12
12
 
13
13
  export function debugData(data: ({ data: unknown } | Data)[], logger: Logger, message = 'DATA:') {
14
- logger.info(message)
15
- logger.info(
16
- inspect(
14
+ logger.debug(
15
+ `${message} ${inspect(
17
16
  data.map((d) => {
18
17
  if (d instanceof Data) {
19
18
  return d.getValue()
@@ -22,25 +21,23 @@ export function debugData(data: ({ data: unknown } | Data)[], logger: Logger, me
22
21
  }
23
22
  }),
24
23
  5
25
- )
24
+ )}`
26
25
  )
27
26
  }
28
27
 
29
28
  export function debugDocuments(documents: Document[], logger: Logger, message = 'DOCUMENTS:') {
30
- logger.info(message)
31
- logger.info(
32
- inspect(
29
+ logger.debug(
30
+ `${message} ${inspect(
33
31
  documents.map((d) => {
34
32
  const { node, ...rest } = d
35
33
  node
36
34
  return rest
37
35
  }),
38
36
  5
39
- )
37
+ )}`
40
38
  )
41
39
  }
42
40
 
43
41
  export function debugFiles(files: File[], logger: Logger, message = 'FILES:') {
44
- logger.info(message)
45
- logger.info(inspect(files, 5))
42
+ logger.debug(`${message}: ${inspect(files, 5)}`)
46
43
  }
@@ -0,0 +1,123 @@
1
+ import { ts, tsc, Diagnostic } from '@servicenow/sdk-project'
2
+ import { Context } from '../plugins/Context'
3
+ import { FluentDiagnostic } from '../plugins'
4
+
5
+ const FluentDisableSyncDirective = '@fluent-disable-sync'
6
+ const FluentDisableSyncForFileDirective = '@fluent-disable-sync-for-file'
7
+ const fluentCommentDirectiveRegEx = /^\/\/\/?\s*@fluent-ignore\b/
8
+
9
+ enum FluentDirectiveType {
10
+ Ignore = 'ignore',
11
+ }
12
+ export type FluentDirective = {
13
+ filePath: string
14
+ start: number
15
+ type: FluentDirectiveType
16
+ }
17
+
18
+ function getDirectiveFromCommentText(text: string) {
19
+ const directiveRegex = /^.*(\/{2,}|\/\*{1,})*\s*(@fluent-[-_a-zA-Z0-9]+).*$/m
20
+ return text.match(directiveRegex)?.[2]
21
+ }
22
+
23
+ function getDirectivesFromAncestors(node: ts.Node) {
24
+ return [node].concat(node.getAncestors()).flatMap(
25
+ (ancestor) =>
26
+ ancestor
27
+ .getLeadingCommentRanges()
28
+ .map((comment) => getDirectiveFromCommentText(comment.getText()))
29
+ .filter((directive) => directive) as string[]
30
+ )
31
+ }
32
+
33
+ function isFluentDirective(text: string, kind: tsc.CommentKind) {
34
+ text = text.trim()
35
+ if (kind === tsc.SyntaxKind.SingleLineCommentTrivia) {
36
+ return fluentCommentDirectiveRegEx.test(text)
37
+ }
38
+ text = text.substring(2, text.length - 2).trim()
39
+ return /^@fluent-ignore\b/.test(text)
40
+ }
41
+
42
+ function parseFluentDirectives(filePath: string, context: Context) {
43
+ const fluentDirectives: FluentDirective[] = []
44
+ const visitedRange = new Set<number>()
45
+ const sourceFile = context.compiler.getSourceFile(filePath)
46
+ if (sourceFile) {
47
+ const sourceCode = sourceFile.getFullText()
48
+ context.compiler.visitNodeTree(sourceFile, (node) => {
49
+ ;[
50
+ ...(tsc.getLeadingCommentRanges(sourceCode, node.getFullStart()) ?? []),
51
+ ...(tsc.getTrailingCommentRanges(sourceCode, node.getFullStart()) ?? []),
52
+ ].forEach((range) => {
53
+ if (
54
+ !visitedRange.has(range.pos) &&
55
+ isFluentDirective(sourceCode.substring(range.pos, range.end), range.kind)
56
+ ) {
57
+ fluentDirectives.push({
58
+ filePath,
59
+ start: range.pos,
60
+ type: FluentDirectiveType.Ignore,
61
+ })
62
+ visitedRange.add(range.pos)
63
+ }
64
+ })
65
+ })
66
+ }
67
+ return fluentDirectives
68
+ }
69
+
70
+ export function syncDisabledForNode(node: ts.Node) {
71
+ return getDirectivesFromAncestors(node).some((directive) => directive === FluentDisableSyncDirective)
72
+ }
73
+
74
+ export function syncDisabledForFile(sourceFile: ts.SourceFile) {
75
+ return sourceFile
76
+ .getStatements()?.[0]
77
+ ?.getLeadingCommentRanges()
78
+ .map((comment) => getDirectiveFromCommentText(comment.getText()))
79
+ .some((directive) => directive === FluentDisableSyncForFileDirective)
80
+ }
81
+
82
+ export function applyFluentDirectives(context: Context, diagnostics: Diagnostic[]) {
83
+ const directiveCache = new Map<string, FluentDirective[]>()
84
+ const filteredDiagnostics = diagnostics.filter((diagnostic) => {
85
+ const filePath = diagnostic.file.getFilePath()
86
+ let fluentDirectives = directiveCache.get(filePath)
87
+ if (!fluentDirectives) {
88
+ fluentDirectives = parseFluentDirectives(filePath, context)
89
+ directiveCache.set(filePath, fluentDirectives)
90
+ }
91
+ let allowDiagnostic = true
92
+ for (const directive of fluentDirectives) {
93
+ const { line: direcLine } = diagnostic.file.getLineAndColumnAtPos(directive.start)
94
+ const { line: diagLine } = diagnostic.file.getLineAndColumnAtPos(diagnostic.position.start)
95
+ if (direcLine + 1 === diagLine) {
96
+ allowDiagnostic = false
97
+ break
98
+ }
99
+ }
100
+ return allowDiagnostic
101
+ })
102
+
103
+ return filteredDiagnostics
104
+ }
105
+
106
+ export function getDirectiveDiagnostics(node: ts.Node): FluentDiagnostic[] {
107
+ // Currently, the @fluent-disable-sync directive is only allowed before a CallExpression
108
+ // If we see it, within one (say, in a PropertyAssignment), we should flag it
109
+ if (node.isKind(ts.SyntaxKind.CallExpression)) {
110
+ return node
111
+ .getDescendantsOfKind(ts.SyntaxKind.SingleLineCommentTrivia)
112
+ .filter((commentNode) => getDirectiveFromCommentText(commentNode.getText()) === FluentDisableSyncDirective)
113
+ .map((n) => {
114
+ return new FluentDiagnostic(
115
+ n,
116
+ `${FluentDisableSyncDirective} directive only allowed before a call expression`,
117
+ { level: Diagnostic.Level.Warn }
118
+ )
119
+ })
120
+ } else {
121
+ return []
122
+ }
123
+ }
package/src/util/Util.ts CHANGED
@@ -1,5 +1,4 @@
1
- import * as path from 'path'
2
- import { FileSystem } from '@servicenow/sdk-project'
1
+ import { FileSystem, path } from '@servicenow/sdk-project'
3
2
 
4
3
  export const NOW_DIR = '.now'
5
4
 
package/src/util/index.ts CHANGED
@@ -3,3 +3,4 @@ export * from './Util'
3
3
  export * from './XMLUploadParser'
4
4
  export * from './XMLJsonBuilder'
5
5
  export * from './Scope'
6
+ export * from './Directive'