@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.
- package/dist/BuildOptions.d.ts +50 -0
- package/dist/BuildOptions.js +46 -0
- package/dist/BuildOptions.js.map +1 -0
- package/dist/GUID.d.ts +2 -0
- package/dist/GUID.js +48 -0
- package/dist/GUID.js.map +1 -0
- package/dist/Keys.d.ts +29 -0
- package/dist/Keys.js +258 -0
- package/dist/Keys.js.map +1 -0
- package/dist/TypeScript.d.ts +5 -0
- package/dist/TypeScript.js +81 -0
- package/dist/TypeScript.js.map +1 -0
- package/dist/XML.d.ts +25 -0
- package/dist/XML.js +72 -0
- package/dist/XML.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/Context.d.ts +198 -0
- package/dist/plugins/Context.js +3 -0
- package/dist/plugins/Context.js.map +1 -0
- package/dist/plugins/Diagnostic.d.ts +10 -0
- package/dist/plugins/Diagnostic.js +52 -0
- package/dist/plugins/Diagnostic.js.map +1 -0
- package/dist/plugins/Plugin.d.ts +175 -0
- package/dist/plugins/Plugin.js +15 -0
- package/dist/plugins/Plugin.js.map +1 -0
- package/dist/plugins/behaviors/Arranger.d.ts +26 -0
- package/dist/plugins/behaviors/Arranger.js +3 -0
- package/dist/plugins/behaviors/Arranger.js.map +1 -0
- package/dist/plugins/behaviors/Composer.d.ts +101 -0
- package/dist/plugins/behaviors/Composer.js +15 -0
- package/dist/plugins/behaviors/Composer.js.map +1 -0
- package/dist/plugins/behaviors/Diagnostics.d.ts +8 -0
- package/dist/plugins/behaviors/Diagnostics.js +3 -0
- package/dist/plugins/behaviors/Diagnostics.js.map +1 -0
- package/dist/plugins/behaviors/Generator.d.ts +21 -0
- package/dist/plugins/behaviors/Generator.js +3 -0
- package/dist/plugins/behaviors/Generator.js.map +1 -0
- package/dist/plugins/behaviors/OwnedTables.d.ts +6 -0
- package/dist/plugins/behaviors/OwnedTables.js +3 -0
- package/dist/plugins/behaviors/OwnedTables.js.map +1 -0
- package/dist/plugins/behaviors/PostProcessor.d.ts +5 -0
- package/dist/plugins/behaviors/PostProcessor.js +3 -0
- package/dist/plugins/behaviors/PostProcessor.js.map +1 -0
- package/dist/plugins/behaviors/Serializer.d.ts +29 -0
- package/dist/plugins/behaviors/Serializer.js +3 -0
- package/dist/plugins/behaviors/Serializer.js.map +1 -0
- package/dist/plugins/behaviors/Transformer.d.ts +23 -0
- package/dist/plugins/behaviors/Transformer.js +3 -0
- package/dist/plugins/behaviors/Transformer.js.map +1 -0
- package/dist/plugins/behaviors/extractors/Data.d.ts +107 -0
- package/dist/plugins/behaviors/extractors/Data.js +191 -0
- package/dist/plugins/behaviors/extractors/Data.js.map +1 -0
- package/dist/plugins/behaviors/extractors/Extractors.d.ts +41 -0
- package/dist/plugins/behaviors/extractors/Extractors.js +3 -0
- package/dist/plugins/behaviors/extractors/Extractors.js.map +1 -0
- package/dist/plugins/behaviors/extractors/index.d.ts +2 -0
- package/dist/plugins/behaviors/extractors/index.js +19 -0
- package/dist/plugins/behaviors/extractors/index.js.map +1 -0
- package/dist/plugins/behaviors/index.d.ts +9 -0
- package/dist/plugins/behaviors/index.js +26 -0
- package/dist/plugins/behaviors/index.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.js +23 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/util/CallExpression.d.ts +6 -0
- package/dist/plugins/util/CallExpression.js +93 -0
- package/dist/plugins/util/CallExpression.js.map +1 -0
- package/dist/plugins/util/CodeTransformation.d.ts +74 -0
- package/dist/plugins/util/CodeTransformation.js +421 -0
- package/dist/plugins/util/CodeTransformation.js.map +1 -0
- package/dist/plugins/util/ConfigurationFunction.d.ts +106 -0
- package/dist/plugins/util/ConfigurationFunction.js +377 -0
- package/dist/plugins/util/ConfigurationFunction.js.map +1 -0
- package/dist/plugins/util/ObjectLiteral.d.ts +9 -0
- package/dist/plugins/util/ObjectLiteral.js +60 -0
- package/dist/plugins/util/ObjectLiteral.js.map +1 -0
- package/dist/plugins/util/index.d.ts +4 -0
- package/dist/plugins/util/index.js +21 -0
- package/dist/plugins/util/index.js.map +1 -0
- package/dist/util/Debug.d.ts +8 -0
- package/dist/util/Debug.js +39 -0
- package/dist/util/Debug.js.map +1 -0
- package/dist/util/Util.d.ts +4 -0
- package/dist/util/Util.js +41 -0
- package/dist/util/Util.js.map +1 -0
- package/dist/util/XMLJsonBuilder.d.ts +18 -0
- package/dist/util/XMLJsonBuilder.js +59 -0
- package/dist/util/XMLJsonBuilder.js.map +1 -0
- package/dist/util/XMLUploadParser.d.ts +22 -0
- package/dist/util/XMLUploadParser.js +67 -0
- package/dist/util/XMLUploadParser.js.map +1 -0
- package/dist/util/index.d.ts +4 -0
- package/dist/util/index.js +21 -0
- package/dist/util/index.js.map +1 -0
- package/license +9 -0
- package/package.json +42 -0
- package/src/BuildOptions.ts +27 -0
- package/src/GUID.ts +26 -0
- package/src/Keys.ts +287 -0
- package/src/TypeScript.ts +65 -0
- package/src/XML.ts +85 -0
- package/src/index.ts +8 -0
- package/src/plugins/Context.ts +249 -0
- package/src/plugins/Diagnostic.ts +31 -0
- package/src/plugins/Plugin.ts +246 -0
- package/src/plugins/behaviors/Arranger.ts +42 -0
- package/src/plugins/behaviors/Composer.ts +124 -0
- package/src/plugins/behaviors/Diagnostics.ts +13 -0
- package/src/plugins/behaviors/Generator.ts +31 -0
- package/src/plugins/behaviors/OwnedTables.ts +5 -0
- package/src/plugins/behaviors/PostProcessor.ts +6 -0
- package/src/plugins/behaviors/Serializer.ts +39 -0
- package/src/plugins/behaviors/Transformer.ts +32 -0
- package/src/plugins/behaviors/extractors/Data.ts +247 -0
- package/src/plugins/behaviors/extractors/Extractors.ts +57 -0
- package/src/plugins/behaviors/extractors/index.ts +2 -0
- package/src/plugins/behaviors/index.ts +9 -0
- package/src/plugins/index.ts +5 -0
- package/src/plugins/util/CallExpression.ts +83 -0
- package/src/plugins/util/CodeTransformation.ts +500 -0
- package/src/plugins/util/ConfigurationFunction.ts +477 -0
- package/src/plugins/util/ObjectLiteral.ts +37 -0
- package/src/plugins/util/index.ts +4 -0
- package/src/util/Debug.ts +46 -0
- package/src/util/Util.ts +21 -0
- package/src/util/XMLJsonBuilder.ts +64 -0
- package/src/util/XMLUploadParser.ts +90 -0
- 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,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
|
+
}
|