@servicenow/sdk-build-core 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/BuildOptions.d.ts +50 -0
  2. package/dist/BuildOptions.js +46 -0
  3. package/dist/BuildOptions.js.map +1 -0
  4. package/dist/GUID.d.ts +2 -0
  5. package/dist/GUID.js +48 -0
  6. package/dist/GUID.js.map +1 -0
  7. package/dist/Keys.d.ts +29 -0
  8. package/dist/Keys.js +258 -0
  9. package/dist/Keys.js.map +1 -0
  10. package/dist/TypeScript.d.ts +5 -0
  11. package/dist/TypeScript.js +81 -0
  12. package/dist/TypeScript.js.map +1 -0
  13. package/dist/XML.d.ts +25 -0
  14. package/dist/XML.js +72 -0
  15. package/dist/XML.js.map +1 -0
  16. package/dist/index.d.ts +8 -0
  17. package/dist/index.js +38 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/plugins/Context.d.ts +198 -0
  20. package/dist/plugins/Context.js +3 -0
  21. package/dist/plugins/Context.js.map +1 -0
  22. package/dist/plugins/Diagnostic.d.ts +10 -0
  23. package/dist/plugins/Diagnostic.js +52 -0
  24. package/dist/plugins/Diagnostic.js.map +1 -0
  25. package/dist/plugins/Plugin.d.ts +175 -0
  26. package/dist/plugins/Plugin.js +15 -0
  27. package/dist/plugins/Plugin.js.map +1 -0
  28. package/dist/plugins/behaviors/Arranger.d.ts +26 -0
  29. package/dist/plugins/behaviors/Arranger.js +3 -0
  30. package/dist/plugins/behaviors/Arranger.js.map +1 -0
  31. package/dist/plugins/behaviors/Composer.d.ts +101 -0
  32. package/dist/plugins/behaviors/Composer.js +15 -0
  33. package/dist/plugins/behaviors/Composer.js.map +1 -0
  34. package/dist/plugins/behaviors/Diagnostics.d.ts +8 -0
  35. package/dist/plugins/behaviors/Diagnostics.js +3 -0
  36. package/dist/plugins/behaviors/Diagnostics.js.map +1 -0
  37. package/dist/plugins/behaviors/Generator.d.ts +21 -0
  38. package/dist/plugins/behaviors/Generator.js +3 -0
  39. package/dist/plugins/behaviors/Generator.js.map +1 -0
  40. package/dist/plugins/behaviors/OwnedTables.d.ts +6 -0
  41. package/dist/plugins/behaviors/OwnedTables.js +3 -0
  42. package/dist/plugins/behaviors/OwnedTables.js.map +1 -0
  43. package/dist/plugins/behaviors/PostProcessor.d.ts +5 -0
  44. package/dist/plugins/behaviors/PostProcessor.js +3 -0
  45. package/dist/plugins/behaviors/PostProcessor.js.map +1 -0
  46. package/dist/plugins/behaviors/Serializer.d.ts +29 -0
  47. package/dist/plugins/behaviors/Serializer.js +3 -0
  48. package/dist/plugins/behaviors/Serializer.js.map +1 -0
  49. package/dist/plugins/behaviors/Transformer.d.ts +23 -0
  50. package/dist/plugins/behaviors/Transformer.js +3 -0
  51. package/dist/plugins/behaviors/Transformer.js.map +1 -0
  52. package/dist/plugins/behaviors/extractors/Data.d.ts +107 -0
  53. package/dist/plugins/behaviors/extractors/Data.js +191 -0
  54. package/dist/plugins/behaviors/extractors/Data.js.map +1 -0
  55. package/dist/plugins/behaviors/extractors/Extractors.d.ts +41 -0
  56. package/dist/plugins/behaviors/extractors/Extractors.js +3 -0
  57. package/dist/plugins/behaviors/extractors/Extractors.js.map +1 -0
  58. package/dist/plugins/behaviors/extractors/index.d.ts +2 -0
  59. package/dist/plugins/behaviors/extractors/index.js +19 -0
  60. package/dist/plugins/behaviors/extractors/index.js.map +1 -0
  61. package/dist/plugins/behaviors/index.d.ts +9 -0
  62. package/dist/plugins/behaviors/index.js +26 -0
  63. package/dist/plugins/behaviors/index.js.map +1 -0
  64. package/dist/plugins/index.d.ts +5 -0
  65. package/dist/plugins/index.js +23 -0
  66. package/dist/plugins/index.js.map +1 -0
  67. package/dist/plugins/util/CallExpression.d.ts +6 -0
  68. package/dist/plugins/util/CallExpression.js +93 -0
  69. package/dist/plugins/util/CallExpression.js.map +1 -0
  70. package/dist/plugins/util/CodeTransformation.d.ts +74 -0
  71. package/dist/plugins/util/CodeTransformation.js +421 -0
  72. package/dist/plugins/util/CodeTransformation.js.map +1 -0
  73. package/dist/plugins/util/ConfigurationFunction.d.ts +106 -0
  74. package/dist/plugins/util/ConfigurationFunction.js +377 -0
  75. package/dist/plugins/util/ConfigurationFunction.js.map +1 -0
  76. package/dist/plugins/util/ObjectLiteral.d.ts +9 -0
  77. package/dist/plugins/util/ObjectLiteral.js +60 -0
  78. package/dist/plugins/util/ObjectLiteral.js.map +1 -0
  79. package/dist/plugins/util/index.d.ts +4 -0
  80. package/dist/plugins/util/index.js +21 -0
  81. package/dist/plugins/util/index.js.map +1 -0
  82. package/dist/util/Debug.d.ts +8 -0
  83. package/dist/util/Debug.js +39 -0
  84. package/dist/util/Debug.js.map +1 -0
  85. package/dist/util/Util.d.ts +4 -0
  86. package/dist/util/Util.js +41 -0
  87. package/dist/util/Util.js.map +1 -0
  88. package/dist/util/XMLJsonBuilder.d.ts +18 -0
  89. package/dist/util/XMLJsonBuilder.js +59 -0
  90. package/dist/util/XMLJsonBuilder.js.map +1 -0
  91. package/dist/util/XMLUploadParser.d.ts +22 -0
  92. package/dist/util/XMLUploadParser.js +67 -0
  93. package/dist/util/XMLUploadParser.js.map +1 -0
  94. package/dist/util/index.d.ts +4 -0
  95. package/dist/util/index.js +21 -0
  96. package/dist/util/index.js.map +1 -0
  97. package/license +9 -0
  98. package/package.json +42 -0
  99. package/src/BuildOptions.ts +27 -0
  100. package/src/GUID.ts +26 -0
  101. package/src/Keys.ts +287 -0
  102. package/src/TypeScript.ts +65 -0
  103. package/src/XML.ts +85 -0
  104. package/src/index.ts +8 -0
  105. package/src/plugins/Context.ts +249 -0
  106. package/src/plugins/Diagnostic.ts +31 -0
  107. package/src/plugins/Plugin.ts +246 -0
  108. package/src/plugins/behaviors/Arranger.ts +42 -0
  109. package/src/plugins/behaviors/Composer.ts +124 -0
  110. package/src/plugins/behaviors/Diagnostics.ts +13 -0
  111. package/src/plugins/behaviors/Generator.ts +31 -0
  112. package/src/plugins/behaviors/OwnedTables.ts +5 -0
  113. package/src/plugins/behaviors/PostProcessor.ts +6 -0
  114. package/src/plugins/behaviors/Serializer.ts +39 -0
  115. package/src/plugins/behaviors/Transformer.ts +32 -0
  116. package/src/plugins/behaviors/extractors/Data.ts +247 -0
  117. package/src/plugins/behaviors/extractors/Extractors.ts +57 -0
  118. package/src/plugins/behaviors/extractors/index.ts +2 -0
  119. package/src/plugins/behaviors/index.ts +9 -0
  120. package/src/plugins/index.ts +5 -0
  121. package/src/plugins/util/CallExpression.ts +83 -0
  122. package/src/plugins/util/CodeTransformation.ts +500 -0
  123. package/src/plugins/util/ConfigurationFunction.ts +477 -0
  124. package/src/plugins/util/ObjectLiteral.ts +37 -0
  125. package/src/plugins/util/index.ts +4 -0
  126. package/src/util/Debug.ts +46 -0
  127. package/src/util/Util.ts +21 -0
  128. package/src/util/XMLJsonBuilder.ts +64 -0
  129. package/src/util/XMLUploadParser.ts +90 -0
  130. package/src/util/index.ts +4 -0
@@ -0,0 +1,65 @@
1
+ import * as ts from 'ts-morph'
2
+
3
+ export function getValueDeclaration(node: ts.Node) {
4
+ const symbol = node.getSymbolOrThrow(`Expected identifier to have symbol: ${node.getText()}`)
5
+ const valueDeclaration = getValueDeclarationFromSymbolOrAlias(symbol)
6
+
7
+ if (!ts.Node.isShorthandPropertyAssignment(valueDeclaration)) {
8
+ return valueDeclaration
9
+ }
10
+
11
+ const valueSymbol = valueDeclaration.getValueSymbol()
12
+ if (!valueSymbol) {
13
+ throw `Expected value declaration for shorthand property assignment to have value symbol: ${valueDeclaration.getText()}`
14
+ }
15
+
16
+ return getValueDeclarationFromSymbolOrAlias(valueSymbol)
17
+ }
18
+
19
+ function getValueDeclarationFromSymbolOrAlias(symbol: ts.Symbol) {
20
+ const valueDeclaration = symbol.getValueDeclaration() ?? symbol.getAliasedSymbol()?.getValueDeclaration()
21
+ if (!valueDeclaration) {
22
+ throw Error(`No value declaration found for symbol: ${symbol.getName()}`)
23
+ }
24
+
25
+ return valueDeclaration
26
+ }
27
+
28
+ export function parseType(
29
+ type: ts.Type,
30
+ location: ts.Node,
31
+ unparsableTypeHandler: (unparsableType: ts.Type) => unknown = (unparsableType) => {
32
+ throw `Unparsable type: ${unparsableType.getText()}`
33
+ }
34
+ ): unknown {
35
+ if (type.isLiteral()) {
36
+ return type.getLiteralValueOrThrow()
37
+ } else if (type.isTuple()) {
38
+ return type.getTupleElements().map((e) => parseType(e, location, unparsableTypeHandler))
39
+ } else if (type.isObject() && !type.isArray()) {
40
+ return type.getProperties().reduce(
41
+ (result, property) => ({
42
+ ...result,
43
+ [property.getName()]: parseType(property.getTypeAtLocation(location), location, unparsableTypeHandler),
44
+ }),
45
+ {}
46
+ )
47
+ } else {
48
+ return unparsableTypeHandler(type)
49
+ }
50
+ }
51
+
52
+ export function traverseNode(node: ts.Node, visitor: (node: ts.Node) => void) {
53
+ node.forEachChild((child) => {
54
+ traverseNode(child, visitor)
55
+ })
56
+
57
+ visitor(node)
58
+ }
59
+
60
+ export function createPropertyIdentifier(name: string) {
61
+ //Test that this is going to be a valid javascript property name (aaa, _aaa, 1, aaa1). If so use as identifier (aaa: ...) or as a string literal property ('1aaa': ...)
62
+ return /^((?!\d)[\w$]+|\d+)$/i.test(name)
63
+ ? ts.ts.factory.createIdentifier(name)
64
+ : ts.ts.factory.createStringLiteral(name)
65
+ }
package/src/XML.ts ADDED
@@ -0,0 +1,85 @@
1
+ import { Action, type Context } from './plugins'
2
+ import { SdkError } from '@servicenow/sdk-metrics'
3
+ import { XMLBuilder } from 'fast-xml-parser'
4
+ import { XMLJsonElement, XMLJsonBuilder } from './util/XMLJsonBuilder'
5
+
6
+ export type XmlValue = string | number | boolean
7
+
8
+ export function isValidXmlValue(value: unknown): value is XmlValue {
9
+ return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'
10
+ }
11
+
12
+ export function unloadBuilder(context: Context) {
13
+ const builder = new XMLJsonBuilder('1.0')
14
+ const xml = builder.createRoot('record_update', undefined, undefined)
15
+
16
+ const end = () =>
17
+ new XMLBuilder({
18
+ ignoreAttributes: false,
19
+ format: true,
20
+ suppressBooleanAttributes: false,
21
+ suppressEmptyNode: true,
22
+ cdataPropName: '__cdata',
23
+ })
24
+ .build(builder.buildJsonObj())
25
+ .trim()
26
+
27
+ // Root Builder
28
+ return {
29
+ xml,
30
+ end,
31
+ record: (tableName: string, id: string | number, action: Action = 'INSERT_OR_UPDATE') => {
32
+ const rec = recordXml(xml, tableName, id, { attr: { action } })
33
+ rec.addSysScope(context)
34
+ return rec
35
+ },
36
+ }
37
+ }
38
+
39
+ export function recordXml(
40
+ xml: XMLJsonElement,
41
+ tableName: string,
42
+ id: string | number,
43
+ options: {
44
+ attr?: Record<string, string>
45
+ excludeScopeElement?: boolean
46
+ } = {}
47
+ ) {
48
+ const recordXml = xml.addJsonObj(tableName, undefined, options.attr || { action: 'INSERT_OR_UPDATE' })
49
+ recordXml.addJsonObj('sys_id', `${id}`, undefined)
50
+
51
+ // Record Builder
52
+ return {
53
+ fields(fields: Record<string, XmlValue>) {
54
+ for (const [columnName, value] of Object.entries(fields)) {
55
+ this.field(columnName, value)
56
+ }
57
+ },
58
+ field(columnName: string, value: unknown, attributes: Record<string, string> = {}) {
59
+ if (!isValidXmlValue(value)) {
60
+ throw new SdkError(`Invalid XML value for "${columnName}" column: ${value}`, {
61
+ type: 'xml_validation',
62
+ })
63
+ }
64
+
65
+ let fieldXml
66
+ if (columnName === 'script' || columnName === 'operation_script') {
67
+ fieldXml = recordXml.addJsonObj(columnName, value as string, attributes, true)
68
+ } else {
69
+ fieldXml = recordXml.addJsonObj(columnName, `${value}`, attributes)
70
+ }
71
+
72
+ // Field Builder
73
+ return {
74
+ choices(_choices: unknown) {
75
+ fieldXml // TODO: Implement
76
+ },
77
+ }
78
+ },
79
+ addSysScope(context: Context) {
80
+ return recordXml.addJsonObj('sys_scope', context.app.config.scopeId, {
81
+ display_value: context.app.config.scope,
82
+ })
83
+ },
84
+ }
85
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * as ts from 'ts-morph'
2
+ export * from './util'
3
+ export * from './TypeScript'
4
+ export * from './XML'
5
+ export * from './plugins'
6
+ export * from './GUID'
7
+ export * from './Keys'
8
+ export * from './BuildOptions'
@@ -0,0 +1,249 @@
1
+ import * as ts from 'ts-morph'
2
+ import {
3
+ Document,
4
+ File,
5
+ LinkedDocument,
6
+ UnlinkedDocument,
7
+ Xml,
8
+ XmlData,
9
+ EntityData,
10
+ Arranged,
11
+ DocumentMap,
12
+ ExtractionResult,
13
+ } from './behaviors'
14
+ import { Keys } from '../Keys'
15
+ import { Plugin } from './Plugin'
16
+ import { FluentDiagnostic } from './Diagnostic'
17
+ import { ProjectContext } from '@servicenow/sdk-project'
18
+ import type { Diagnostic } from '@servicenow/sdk-project'
19
+
20
+ // These are only imported so they can be referenced in JS docs
21
+ import type {
22
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
+ Extractors,
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ Composers,
26
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
+ Arrangers,
28
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29
+ Serializers,
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ Generators,
32
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
+ Transformers,
34
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
35
+ PostProcessors,
36
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
37
+ Diagnostics,
38
+ Data,
39
+ } from './behaviors'
40
+
41
+ /**
42
+ * The context object is a collection of contextual information and methods
43
+ * passed into all the various behaviors of a plugin when those behaviors are
44
+ * invoked. It is also used by the build system to orchestrate the build.
45
+ *
46
+ * Each behavioral method on the context can accept an array of plugins as
47
+ * an argument to control which plugins are used for that request. If no
48
+ * plugins are provided as input, all available plugins will be used.
49
+ */
50
+ export type Context = ProjectContext & {
51
+ /**
52
+ * Accepts a parsed XML object and returns an array of {@linkcode XmlData}
53
+ * extracted from that object.
54
+ *
55
+ * @see {@linkcode Extractors}
56
+ * @param xml The parsed XML to extract.
57
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
58
+ * @returns an array of extracted data.
59
+ */
60
+ extractXml(this: Context, xml: Xml, plugins?: Plugin[]): XmlData[]
61
+
62
+ /**
63
+ * Accepts an AST node and returns a result containing an array of
64
+ * {@linkcode RawData} or {@linkcode EntityData} extracted from that node.
65
+ *
66
+ * @see {@linkcode Extractors}
67
+ * @param node The node to extract.
68
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
69
+ * @returns a result object containing an array of extracted data.
70
+ */
71
+ extractAst(this: Context, node: ts.Node, plugins?: Plugin[]): ExtractionResult
72
+
73
+ /**
74
+ * Accepts an AST node and returns a result containing an array of
75
+ * {@linkcode RawData} extracted from that node.
76
+ *
77
+ * @see {@linkcode Extractors}
78
+ * @param node The node to extract.
79
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
80
+ * @returns a result object containing an array of extracted data.
81
+ */
82
+ extractRaw(this: Context, node: ts.Node, plugins?: Plugin[]): ExtractionResult<Data>
83
+
84
+ /**
85
+ * Accepts an AST node and returns a result containing an array of
86
+ * {@linkcode EntityData} extracted from that node.
87
+ *
88
+ * @see {@linkcode Extractors}
89
+ * @param node The node to extract.
90
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
91
+ * @returns a result object containing an array of extracted entities.
92
+ */
93
+ extractEntities(this: Context, node: ts.Node, plugins?: Plugin[]): ExtractionResult<EntityData>
94
+
95
+ /**
96
+ * Accepts an array of entity data and returns a {@linkcode LinkedDocument}
97
+ * array.
98
+ *
99
+ * @see {@linkcode Composers}
100
+ * @param data The {@linkcode EntityData} to compose.
101
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
102
+ * @returns a {@linkcode LinkedDocument} array.
103
+ */
104
+ composeEntities(this: Context, data: EntityData[], plugins?: Plugin[]): LinkedDocument[]
105
+
106
+ /**
107
+ * Accepts an array of XML data and returns an {@linkcode UnlinkedDocument}
108
+ * array.
109
+ *
110
+ * @see {@linkcode Composers}
111
+ * @param data The {@linkcode XmlData} to compose.
112
+ * @param context The {@linkcode Context} object.
113
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
114
+ * @returns an {@linkcode UnlinkedDocument} array.
115
+ */
116
+ composeXml(this: Context, data: XmlData[], plugins?: Plugin[]): UnlinkedDocument[]
117
+
118
+ /**
119
+ * Accepts an array of documents and returns the same documents
120
+ * as unresolved arranged documents.
121
+ *
122
+ * @see {@linkcode Arrangers}
123
+ * @param documents The {@linkcode Document}s to arrange.
124
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
125
+ * @returns a {@linkcode Document} & {@linkcode Arranged} array.
126
+ */
127
+ arrange(this: Context, documents: Document[], plugins?: Plugin[]): (Document & Arranged<'unresolved'>)[]
128
+
129
+ /**
130
+ * Accepts an array of documents and returns an array of files.
131
+ *
132
+ * @see {@linkcode Serializers}
133
+ * @param documents The {@linkcode Document}s to serialize.
134
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
135
+ * @returns a {@linkcode File} array.
136
+ */
137
+ serialize(this: Context, documents: (Document & Arranged)[], plugins?: Plugin[]): File[]
138
+
139
+ /**
140
+ * Accepts an array of documents, generates new AST nodes for those
141
+ * documents, and returns the same documents as linked documents.
142
+ *
143
+ * @see {@linkcode Generators}
144
+ * @param documents The {@linkcode Document}s to generate AST nodes for.
145
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
146
+ * @returns a {@linkcode LinkedDocument} array.
147
+ */
148
+ generate(this: Context, documents: (UnlinkedDocument & Arranged)[], plugins?: Plugin[]): LinkedDocument[]
149
+
150
+ /**
151
+ * Accepts an array of linked documents and performs transformations on
152
+ * their original nodes to reflect any changes that may have been made to
153
+ * those documents' data.
154
+ *
155
+ * @see {@linkcode Transformers}
156
+ * @param documents The {@linkcode LinkedDocument}s to transform.
157
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
158
+ */
159
+ transform(this: Context, documents: (LinkedDocument & Arranged)[], plugins?: Plugin[]): void
160
+
161
+ /**
162
+ * Accepts an array of extracted entities and runs post-processors on that
163
+ * data. Post-processors may perform a variety of arbitrary operations.
164
+ *
165
+ * @see {@linkcode PostProcessors}
166
+ * @param data The {@linkcode EntityData} to process.
167
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
168
+ */
169
+ postProcessEntities(this: Context, data: EntityData[], plugins?: Plugin[]): void
170
+
171
+ /**
172
+ * Accepts an AST node and returns an array of {@linkcode FluentDiagnostic}s
173
+ * for that node.
174
+ *
175
+ * @see {@linkcode Diagnostics}
176
+ * @param node The node to get diagnostics for.
177
+ * @param plugins An optional array of {@linkcode Plugin}s to use.
178
+ * @returns an array of diagnostics.
179
+ */
180
+ getAstDiagnostics(this: Context, node: ts.Node, plugins?: Plugin[]): FluentDiagnostic[]
181
+
182
+ /**
183
+ * An object with metadata related to plugin like list of all tables that are claimed to be handled by a specific plugin, and
184
+ * the level of enforcement they apply to that table
185
+ * and API that the plugin is responsible for.
186
+ */
187
+ getPluginForTable(table: string): { logLevel: Diagnostic.Level; api: string } | undefined
188
+
189
+ /**
190
+ * Indicates whether or not the build system is running in debug mode. This
191
+ * should be checked before printing verbose log messages.
192
+ */
193
+ debug: boolean
194
+
195
+ /**
196
+ * Indicates the current mode in which the build system is running.
197
+ */
198
+ mode: 'serialize' | 'transform'
199
+
200
+ /**
201
+ * An array of plugins that will be used (unless overridden) during various
202
+ * build process operations.
203
+ *
204
+ * @see {@link Plugin}
205
+ */
206
+ plugins: Plugin[]
207
+
208
+ /**
209
+ * An array of plugins which implement entity-handling behaviors which will
210
+ * be used during various build process operations. These plugins are always
211
+ * executed BEFORE the plugins in the {@link Context#plugins} array.
212
+ *
213
+ * @see {@link Plugin}
214
+ */
215
+ entityPlugins: Plugin[]
216
+
217
+ /**
218
+ * Record to store and access data across plugins
219
+ */
220
+ store: Record<string, unknown>
221
+
222
+ usageCount: { [key: string]: number }
223
+
224
+ /**
225
+ * Resets the store to initial state
226
+ */
227
+ resetStore(): void
228
+
229
+ /**
230
+ * An object of XML file names that was marked as handled or ignored by code transformation during the build process.
231
+ */
232
+ handledXmls: Record<string, HandledStates>
233
+
234
+ setAllDocuments: (documents: Document[]) => void
235
+ getAllDocuments: () => Document[]
236
+ getDocumentMap: () => DocumentMap
237
+ getDocument: (sysId: string, kind?: string) => Document | undefined
238
+
239
+ /**
240
+ * @deprecated Use the key methods on the context object instead.
241
+ */
242
+ keys: Keys
243
+
244
+ registerExplicitId: Keys['registerExplicitId']
245
+ registerCompositeId: Keys['registerCompositeId']
246
+ getDeletedAndUnusedIds: Keys['getDeletedAndUnusedIds']
247
+ }
248
+
249
+ export type HandledStates = 'handled' | 'ignored' | 'skipped' | 'unchanged'
@@ -0,0 +1,31 @@
1
+ import * as ts from 'ts-morph'
2
+ import { Diagnostic } from '@servicenow/sdk-project'
3
+
4
+ export class FluentDiagnostic extends Diagnostic {
5
+ constructor(
6
+ node: ts.Node,
7
+ message: string,
8
+ { level = Diagnostic.Level.Error, code = node.getKind() }: { level?: Diagnostic.Level; code?: number } = {}
9
+ ) {
10
+ super(message, node.getSourceFile(), { start: node.getStart(), end: node.getEnd() }, code, 'fluent', level)
11
+ }
12
+
13
+ public override asTypeScriptDiagnostic(): ts.ts.Diagnostic {
14
+ return {
15
+ messageText: this.message,
16
+ category: Diagnostic.Level.toCategory(this.level),
17
+ code: this.code,
18
+ file: this.file.compilerNode,
19
+ start: this.position.start,
20
+ length: this.position.end - this.position.start,
21
+ }
22
+ }
23
+ }
24
+
25
+ export function findObjectPropertyValue(callExpression: ts.CallExpression, field: string) {
26
+ const property = callExpression
27
+ .getFirstDescendantByKind(ts.SyntaxKind.ObjectLiteralExpression)
28
+ ?.getProperty(field) as ts.PropertyAssignment
29
+
30
+ return property?.getInitializer() || callExpression
31
+ }
@@ -0,0 +1,246 @@
1
+ import { SupportedKindName } from '@servicenow/sdk-project'
2
+ import {
3
+ Extractors,
4
+ Composers,
5
+ Serializers,
6
+ Transformers,
7
+ Generators,
8
+ Arrangers,
9
+ PostProcessors,
10
+ Diagnostics,
11
+ TableOwnership,
12
+ } from './behaviors'
13
+
14
+ import type {
15
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
+ EntityComposerFunction,
17
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
18
+ XmlComposerFunction,
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
+ ArrangerFunction,
21
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
+ SerializerFunction,
23
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
24
+ GeneratorFunction,
25
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
26
+ TransformerFunction,
27
+ } from './behaviors'
28
+
29
+ /**
30
+ * A plugin is an object that defines any number of behaviors that will
31
+ * be invoked by the build system during various operations.
32
+ *
33
+ * The order of build operations during a normal build (code to XML) is
34
+ * as follows:
35
+ *
36
+ * Extract -> Compose -> Arrange -> Serialize
37
+ *
38
+ * The order of build operations during a transform (XML to code, AKA
39
+ * "bi-directional sync") is as follows:
40
+ *
41
+ * Extract -> Compose -> Arrange -> Generate -> Transform
42
+ *
43
+ * During each operation, all plugins which define behaviors for that
44
+ * operation will be invoked. Therefore, plugins should always "fail
45
+ * fast" to minimize performance impact. For example, if a plugin defines
46
+ * an extractor for calls to a function named "exampleFunction", that
47
+ * plugin should first validate the function name and return immediately
48
+ * if the name is anything but "exampleFunction".
49
+ *
50
+ * @see {@linkcode Extractors}
51
+ * @see {@linkcode Composers}
52
+ * @see {@linkcode Arrangers}
53
+ * @see {@linkcode Serializers}
54
+ * @see {@linkcode Generators}
55
+ * @see {@linkcode Transformers}
56
+ */
57
+ export type Plugin<
58
+ DiagnosticsNodeKinds extends SupportedKindName | 'Node' = any,
59
+ RawNodeKinds extends SupportedKindName = any,
60
+ EntityNodeKinds extends SupportedKindName = any,
61
+ ArrangerDocumentKinds extends string = any,
62
+ SerializerDocumentKinds extends string = any,
63
+ GeneratorDocumentKinds extends string = any,
64
+ TransformerDocumentKinds extends string = any,
65
+ > = {
66
+ /**
67
+ * Name of the plugin
68
+ */
69
+ name: string
70
+
71
+ diagnostics?: Diagnostics<DiagnosticsNodeKinds>
72
+
73
+ /**
74
+ * ServiceNow tables that this plugin handles and should not be serialized
75
+ * by generic plugins, and the appropriate diagnostic level to enforce on
76
+ * this relationship
77
+ */
78
+ ownedTables?: TableOwnership
79
+
80
+ /**
81
+ * Plugins may define extractors which extract raw data or entity data from
82
+ * AST nodes, or XML data from parsed XML files. The build system will take
83
+ * any entity data produced by an extractor and pass it along to any plugin
84
+ * that implements a composer for that kind of entity.
85
+ *
86
+ * An extractors object is an object with any of the keys 'xml', 'raw', or
87
+ * 'entity', where the values of those keys are nested objects where each
88
+ * key is the name of a kind of AST node, and the value is one of the types
89
+ * of extractor functions.
90
+ *
91
+ * @example
92
+ * const extractors = {
93
+ * entity: {
94
+ * CallExpression(node, context) { ... }
95
+ * ClassDeclaration(node, context) { ... }
96
+ * }
97
+ * }
98
+ */
99
+ extractors?: Extractors<RawNodeKinds, EntityNodeKinds>
100
+
101
+ /**
102
+ * Plugins may define composers for both entity data (from the
103
+ * source code) and XML data (from XML files). The job of a
104
+ * composer is to turn a piece of extracted data into one or more
105
+ * documents which can then be serialized or transformed.
106
+ *
107
+ * A composers object that has two optional keys: 'entity' and 'xml'. The
108
+ * value of the 'xml' key is a {@linkcode XmlComposerFunction}. The value of
109
+ * the 'entity' key is another object where each key is the name of a kind
110
+ * of entity, and the value is a {@linkcode EntityComposerFunction}.
111
+ *
112
+ * @example
113
+ * const composers = {
114
+ * entity: {
115
+ * MyFirstEntity(data, context) { ... }
116
+ * MyOtherEntity(data, context) { ... }
117
+ * },
118
+ * xml: {
119
+ * record(data, context) { ... }
120
+ * }
121
+ * }
122
+ */
123
+ composers?: Composers
124
+
125
+ /**
126
+ * Plugins may define arrangers which take documents and return their parents
127
+ * so that the build system can arrange them in a tree structure.
128
+ *
129
+ * An arrangers object is an object where each key is the name of a kind of
130
+ * document, and the value is a {@linkcode ArrangerFunction}.
131
+ *
132
+ * @example
133
+ * const arrangers = {
134
+ * MyFirstDocument(document, context) { ... },
135
+ * MyOtherDocument(document, context) { ... }
136
+ * }
137
+ */
138
+ arrangers?: Arrangers<ArrangerDocumentKinds>
139
+
140
+ /**
141
+ * Plugins may define serializers which turn documents into files which the
142
+ * build system will write to the filesystem as output.
143
+ *
144
+ * A serializers object is an object where each key is the name of a kind of
145
+ * document, and the value is a {@linkcode SerializerFunction}.
146
+ *
147
+ * @example
148
+ * const serializers = {
149
+ * MyFirstDocument(document, context) { ... },
150
+ * MyOtherDocument(document, context) { ... }
151
+ * }
152
+ */
153
+ serializers?: Serializers<SerializerDocumentKinds>
154
+
155
+ /**
156
+ * Plugins may define generators which create new nodes for documents that
157
+ * don't have nodes, usually because they were extracted from XML files and
158
+ * aren't defined yet in the source code. The build system will pass each
159
+ * document without a node to any plugin which implements a generator for
160
+ * that kind of document. These documents are then passed along to the
161
+ * transformers along with all the other documents which already had nodes.
162
+ *
163
+ * A generators object is an object where each key is the name of a kind of
164
+ * document, and the value is a {@linkcode GeneratorFunction}.
165
+ *
166
+ * @example
167
+ * const generators = {
168
+ * MyFirstDocument(document, context) { ... },
169
+ * MyOtherDocument(document, context) { ... }
170
+ * }
171
+ */
172
+ generators?: Generators<GeneratorDocumentKinds>
173
+
174
+ /**
175
+ * Plugins may define transformers which take linked documents and perform
176
+ * transformations on their linked nodes to ensure each document is fully
177
+ * represented in the source code.
178
+ *
179
+ * A transformers object is an object where each key is the name of a kind of
180
+ * document, and the value is another object where each key is the name of a
181
+ * kind of AST node, and the value is a {@linkcode TransformerFunction}.
182
+ *
183
+ * @example
184
+ * const transformers = {
185
+ * MyFirstDocument: {
186
+ * CallExpression(document, context) { ... },
187
+ * ClassDeclaration(document, context) { ... }
188
+ * },
189
+ * MyOtherDocument: {
190
+ * CallExpression(document, context) { ... },
191
+ * ClassDeclaration(document, context) { ... }
192
+ * }
193
+ * }
194
+ */
195
+ transformers?: Transformers<TransformerDocumentKinds>
196
+
197
+ /**
198
+ * Plugins may define post-processors for data from certain stages of the build
199
+ * process. For example, a post-processor for entities will be called after the
200
+ * extraction stage and will receive all entities extracted by all plugins.
201
+ * These can be used for arbitrary operations based on extracted data.
202
+ *
203
+ * @example
204
+ * const postProcessors = {
205
+ * entities(entities, context) { ... }
206
+ * }
207
+ */
208
+ postProcessors?: PostProcessors
209
+ }
210
+
211
+ /**
212
+ * Utility function to create a new {@linkcode Plugin} object. Provides
213
+ * the simplest and most type-safe experience for defining a plugin, but
214
+ * is not required.
215
+ *
216
+ * @param plugin The {@linkcode Plugin} configuration
217
+ * @returns a {@linkcode Plugin} object
218
+ */
219
+ export function Plugin<
220
+ const DiagnosticsNodeKinds extends SupportedKindName | 'Node',
221
+ const RawNodeKinds extends SupportedKindName,
222
+ const EntityNodeKinds extends SupportedKindName,
223
+ const SerializerDocumentKinds extends string,
224
+ const GeneratorDocumentKinds extends string,
225
+ const TransformerDocumentKinds extends string,
226
+ const P extends Plugin<
227
+ DiagnosticsNodeKinds,
228
+ RawNodeKinds,
229
+ EntityNodeKinds,
230
+ SerializerDocumentKinds,
231
+ GeneratorDocumentKinds,
232
+ TransformerDocumentKinds
233
+ >,
234
+ >(
235
+ plugin: P &
236
+ Plugin<
237
+ DiagnosticsNodeKinds,
238
+ RawNodeKinds,
239
+ EntityNodeKinds,
240
+ SerializerDocumentKinds,
241
+ GeneratorDocumentKinds,
242
+ TransformerDocumentKinds
243
+ >
244
+ ) {
245
+ return plugin as P
246
+ }