@servicenow/sdk-build-plugins 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acl-plugin.js +13 -4
- package/dist/acl-plugin.js.map +1 -1
- package/dist/application-menu-plugin.js +1 -0
- package/dist/application-menu-plugin.js.map +1 -1
- package/dist/atf/step-configs.d.ts +13 -12
- package/dist/atf/step-configs.js.map +1 -1
- package/dist/atf/test-plugin.d.ts +1 -1
- package/dist/atf/test-plugin.js +8 -5
- package/dist/atf/test-plugin.js.map +1 -1
- package/dist/basic-syntax-plugin.js +51 -13
- package/dist/basic-syntax-plugin.js.map +1 -1
- package/dist/business-rule-plugin.js.map +1 -1
- package/dist/claims-plugin.js +1 -1
- package/dist/claims-plugin.js.map +1 -1
- package/dist/client-script-plugin.js +5 -17
- package/dist/client-script-plugin.js.map +1 -1
- package/dist/column/column-helper.d.ts +1 -1
- package/dist/column/column-helper.js +46 -2
- package/dist/column/column-helper.js.map +1 -1
- package/dist/column/column-to-record.js +6 -4
- package/dist/column/column-to-record.js.map +1 -1
- package/dist/column-plugin.js +106 -27
- package/dist/column-plugin.js.map +1 -1
- package/dist/data-plugin.d.ts +3 -0
- package/dist/data-plugin.js +208 -0
- package/dist/data-plugin.js.map +1 -0
- package/dist/import-sets-plugin.d.ts +2 -0
- package/dist/import-sets-plugin.js +412 -0
- package/dist/import-sets-plugin.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/json-plugin.d.ts +4 -4
- package/dist/json-plugin.js +21 -7
- package/dist/json-plugin.js.map +1 -1
- package/dist/list-plugin.js +83 -1
- package/dist/list-plugin.js.map +1 -1
- package/dist/now-attach-plugin.d.ts +35 -0
- package/dist/now-attach-plugin.js +317 -0
- package/dist/now-attach-plugin.js.map +1 -0
- package/dist/now-config-plugin.js +3 -0
- package/dist/now-config-plugin.js.map +1 -1
- package/dist/now-include-plugin.js +7 -1
- package/dist/now-include-plugin.js.map +1 -1
- package/dist/package-json-plugin.js +2 -2
- package/dist/package-json-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +6 -0
- package/dist/record-plugin.js +50 -23
- package/dist/record-plugin.js.map +1 -1
- package/dist/repack/lint/Rules.js.map +1 -1
- package/dist/rest-api-plugin.js +28 -31
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/role-plugin.js +1 -0
- package/dist/role-plugin.js.map +1 -1
- package/dist/server-module-plugin/index.js +15 -2
- package/dist/server-module-plugin/index.js.map +1 -1
- package/dist/service-portal/widget-plugin.js +4 -1
- package/dist/service-portal/widget-plugin.js.map +1 -1
- package/dist/static-content-plugin.d.ts +1 -0
- package/dist/static-content-plugin.js +4 -3
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +33 -2
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +2 -1
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/ui-policy-plugin.d.ts +2 -0
- package/dist/ui-policy-plugin.js +407 -0
- package/dist/ui-policy-plugin.js.map +1 -0
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +24 -0
- package/dist/utils.js.map +1 -1
- package/dist/view-plugin.js +1 -1
- package/dist/view-plugin.js.map +1 -1
- package/package.json +11 -7
- package/src/_types/eslint-plugin-es-x.d.ts +17 -0
- package/src/_types/md5.js.d.ts +8 -0
- package/src/acl-plugin.ts +19 -9
- package/src/application-menu-plugin.ts +1 -0
- package/src/atf/step-configs.ts +14 -12
- package/src/atf/test-plugin.ts +40 -21
- package/src/basic-syntax-plugin.ts +61 -13
- package/src/business-rule-plugin.ts +7 -4
- package/src/claims-plugin.ts +1 -1
- package/src/client-script-plugin.ts +8 -22
- package/src/column/column-helper.ts +65 -3
- package/src/column/column-to-record.ts +6 -4
- package/src/column-plugin.ts +141 -39
- package/src/data-plugin.ts +266 -0
- package/src/import-sets-plugin.ts +542 -0
- package/src/index.ts +4 -0
- package/src/json-plugin.ts +31 -12
- package/src/list-plugin.ts +91 -1
- package/src/now-attach-plugin.ts +399 -0
- package/src/now-config-plugin.ts +6 -2
- package/src/now-include-plugin.ts +8 -1
- package/src/package-json-plugin.ts +3 -3
- package/src/record-plugin.ts +61 -30
- package/src/repack/lint/Rules.ts +1 -10
- package/src/rest-api-plugin.ts +45 -51
- package/src/role-plugin.ts +1 -0
- package/src/server-module-plugin/index.ts +21 -5
- package/src/service-portal/widget-plugin.ts +4 -1
- package/src/static-content-plugin.ts +2 -2
- package/src/table-plugin.ts +47 -7
- package/src/ui-page-plugin.ts +2 -1
- package/src/ui-policy-plugin.ts +509 -0
- package/src/utils.ts +27 -1
- package/src/view-plugin.ts +1 -1
package/src/record-plugin.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { CallExpressionShape, Plugin } from '@servicenow/sdk-build-core'
|
|
2
|
-
import { unloadBuilder } from '@servicenow/sdk-build-core'
|
|
1
|
+
import { CallExpressionShape, Database, Plugin, unloadBuilder, type Record } from '@servicenow/sdk-build-core'
|
|
3
2
|
import { NowIdShape } from './now-id-plugin'
|
|
4
3
|
import {
|
|
5
4
|
Acl,
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
Role,
|
|
11
10
|
Test,
|
|
12
11
|
UserPreference,
|
|
12
|
+
ImportSet,
|
|
13
13
|
} from '@servicenow/sdk-core/runtime/app'
|
|
14
14
|
import {
|
|
15
15
|
SPWidget,
|
|
@@ -23,7 +23,7 @@ import { ScriptAction, ScriptInclude } from '@servicenow/sdk-core/runtime/sys'
|
|
|
23
23
|
import { List } from '@servicenow/sdk-core/runtime/ui'
|
|
24
24
|
import { Table } from '@servicenow/sdk-core/runtime/db'
|
|
25
25
|
import { RestApi } from '@servicenow/sdk-core/runtime/rest'
|
|
26
|
-
import { UiAction, UiPage } from '@servicenow/sdk-core/runtime/ui'
|
|
26
|
+
import { UiAction, UiPage, UiPolicy } from '@servicenow/sdk-core/runtime/ui'
|
|
27
27
|
|
|
28
28
|
export const RecordPlugin = Plugin.create({
|
|
29
29
|
name: 'RecordPlugin',
|
|
@@ -32,31 +32,46 @@ export const RecordPlugin = Plugin.create({
|
|
|
32
32
|
getUpdateName(record) {
|
|
33
33
|
return { success: true, value: `${record.getTable()}_${record.getId().getValue()}` }
|
|
34
34
|
},
|
|
35
|
-
|
|
35
|
+
async diff(existing, incoming) {
|
|
36
|
+
const changeDatabase = existing.compare(incoming)
|
|
36
37
|
return {
|
|
37
38
|
success: true,
|
|
38
|
-
value: new
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
39
|
+
value: changeDatabase.hasChanges() ? new Database(changeDatabase.query()) : new Database(),
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
async toShape(record) {
|
|
43
|
+
const value = new CallExpressionShape({
|
|
44
|
+
source: record,
|
|
45
|
+
callee: 'Record',
|
|
46
|
+
exportName: record.get('sys_name')?.ifDefined()?.asString().getValue(),
|
|
47
|
+
args: [
|
|
48
|
+
{
|
|
49
|
+
$id: NowIdShape.from(record),
|
|
50
|
+
table: record.getTable(),
|
|
51
|
+
data: record.transform(
|
|
52
|
+
({ $ }) =>
|
|
53
|
+
Object.fromEntries(
|
|
54
|
+
record
|
|
55
|
+
.keys()
|
|
56
|
+
.filter((key) => key !== 'sys_name')
|
|
57
|
+
.map((key) => [key, record.get(key).isString() ? $.def('') : $])
|
|
58
|
+
) //to avoid writing empty string values to the code
|
|
59
|
+
),
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
success: true,
|
|
66
|
+
value,
|
|
56
67
|
}
|
|
57
68
|
},
|
|
58
69
|
async toFile(record, { config, database, transform }) {
|
|
59
|
-
const recordBuilder = unloadBuilder(
|
|
70
|
+
const recordBuilder = unloadBuilder({
|
|
71
|
+
scope: config.scope,
|
|
72
|
+
scopeId: config.scopeId,
|
|
73
|
+
table: record.getTable(),
|
|
74
|
+
})
|
|
60
75
|
const updateName = await transform.getUpdateName(record)
|
|
61
76
|
const builder = recordBuilder.record(record, updateName)
|
|
62
77
|
|
|
@@ -101,13 +116,14 @@ export const RecordPlugin = Plugin.create({
|
|
|
101
116
|
|
|
102
117
|
const record = callExpression.getArgument(0).asObject()
|
|
103
118
|
const table = record.get('table').asString().getValue()
|
|
104
|
-
const tableOwningPlugin = TableOwnership[table]
|
|
119
|
+
const tableOwningPlugin = TableOwnership[table as keyof typeof TableOwnership]
|
|
105
120
|
if (tableOwningPlugin) {
|
|
106
121
|
diagnostics.hint(
|
|
107
122
|
callExpression,
|
|
108
123
|
`For a better experience, consider using the ${tableOwningPlugin} API`
|
|
109
124
|
)
|
|
110
125
|
}
|
|
126
|
+
|
|
111
127
|
return {
|
|
112
128
|
success: true,
|
|
113
129
|
value: await factory.createRecord({
|
|
@@ -125,16 +141,25 @@ export const RecordPlugin = Plugin.create({
|
|
|
125
141
|
matcher: /\.xml$/,
|
|
126
142
|
async toRecord(file, { parser, logger }) {
|
|
127
143
|
try {
|
|
128
|
-
const
|
|
144
|
+
const records = await parser.parsePayload(file)
|
|
145
|
+
const recordMap = new Map<string, Record>()
|
|
146
|
+
for (const record of records) {
|
|
147
|
+
const key = `${record.getTable()}::${record.getId().getValue()}`
|
|
148
|
+
const existing = recordMap.get(key)
|
|
129
149
|
|
|
130
|
-
|
|
131
|
-
|
|
150
|
+
const merged = existing
|
|
151
|
+
? existing.merge(record.properties()) // merge properties only to retain the action
|
|
152
|
+
: record
|
|
153
|
+
|
|
154
|
+
recordMap.set(key, merged)
|
|
132
155
|
}
|
|
133
156
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
157
|
+
const mergedRecords = Array.from(recordMap.values())
|
|
158
|
+
const [mergedFirst, ...mergedRest] = mergedRecords
|
|
159
|
+
if (!mergedFirst) {
|
|
160
|
+
return { success: false }
|
|
137
161
|
}
|
|
162
|
+
return { success: true, value: mergedFirst.with(...mergedRest) }
|
|
138
163
|
} catch (e) {
|
|
139
164
|
logger.debug(e)
|
|
140
165
|
return { success: false }
|
|
@@ -181,7 +206,13 @@ export const TableOwnership = {
|
|
|
181
206
|
sys_ui_action: UiAction.name,
|
|
182
207
|
sys_ui_action_role: UiAction.name,
|
|
183
208
|
sys_ui_action_view: UiAction.name,
|
|
209
|
+
sys_ui_policy: UiPolicy.name,
|
|
210
|
+
sys_ui_policy_action: UiPolicy.name,
|
|
211
|
+
sys_ui_policy_rl_action: UiPolicy.name,
|
|
184
212
|
sysevent_script_action: ScriptAction.name,
|
|
185
213
|
sys_db_object: Table.name,
|
|
186
214
|
sys_dictionary: Table.name,
|
|
215
|
+
sys_transform_map: ImportSet.name,
|
|
216
|
+
sys_transform_entry: ImportSet.name,
|
|
217
|
+
sys_transform_script: ImportSet.name,
|
|
187
218
|
}
|
package/src/repack/lint/Rules.ts
CHANGED
|
@@ -10,15 +10,6 @@ const NO_GLOBAL_THIS = 'ES2020 `globalThis` variable is not supported by the now
|
|
|
10
10
|
|
|
11
11
|
type LogLevel = 'warn' | 'error'
|
|
12
12
|
|
|
13
|
-
type ClassMethods = {
|
|
14
|
-
[className: string]: string[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type GlideAPIMapping = {
|
|
18
|
-
defaults: ClassMethods
|
|
19
|
-
[namespace: string]: ClassMethods
|
|
20
|
-
}
|
|
21
|
-
|
|
22
13
|
type ESLintPropertyRule = {
|
|
23
14
|
message: string
|
|
24
15
|
object?: string
|
|
@@ -26,7 +17,7 @@ type ESLintPropertyRule = {
|
|
|
26
17
|
name?: string
|
|
27
18
|
}
|
|
28
19
|
|
|
29
|
-
const glideAPIs
|
|
20
|
+
const glideAPIs = GlideAPIs
|
|
30
21
|
|
|
31
22
|
const globalsAllowList: string[] = ['console', 'fetch']
|
|
32
23
|
|
package/src/rest-api-plugin.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '@servicenow/sdk-build-core'
|
|
12
12
|
import { ModuleFunctionShape } from './server-module-plugin'
|
|
13
13
|
import { NowIdShape } from './now-id-plugin'
|
|
14
|
+
import { NowIncludeShape } from './now-include-plugin'
|
|
14
15
|
import { generateDeprecatedDiagnostics } from './utils'
|
|
15
16
|
|
|
16
17
|
const DEFAULT_MEDIA_TYPE = 'application/json,application/xml,text/xml'
|
|
@@ -453,7 +454,7 @@ export const RestApiPlugin = Plugin.create({
|
|
|
453
454
|
descendant: true,
|
|
454
455
|
},
|
|
455
456
|
},
|
|
456
|
-
toShape(record, { descendants }) {
|
|
457
|
+
async toShape(record, { descendants, transform }) {
|
|
457
458
|
const versions = descendants.query('sys_ws_version')
|
|
458
459
|
const routes = descendants.query('sys_ws_operation')
|
|
459
460
|
const headers = descendants.query('sys_ws_header')
|
|
@@ -476,6 +477,48 @@ export const RestApiPlugin = Plugin.create({
|
|
|
476
477
|
validateAttributesAndAssociations(headers, headerRouteAssociations, 'header')
|
|
477
478
|
validateAttributesAndAssociations(parameters, paramRouteAssociations, 'query_parameter')
|
|
478
479
|
|
|
480
|
+
const routesWithScript = await Promise.all(
|
|
481
|
+
routes.map(async (r) => {
|
|
482
|
+
const script = await NowIncludeShape.fromRecord(r, r.get('operation_script'), transform)
|
|
483
|
+
return r
|
|
484
|
+
.transform(({ $ }) => ({
|
|
485
|
+
$id: $.val(NowIdShape.from(r)),
|
|
486
|
+
name: $,
|
|
487
|
+
active: $.toBoolean().def(true),
|
|
488
|
+
consumes: $,
|
|
489
|
+
method: $.from('http_method').def('GET'),
|
|
490
|
+
script: $.val(script),
|
|
491
|
+
produces: $,
|
|
492
|
+
path: $.from('relative_path').def('/'),
|
|
493
|
+
enforceAcl: $.val(splitAcls(r.get('enforce_acl'))).def([DEFAULT_REST_ENFORCED_ACL]),
|
|
494
|
+
authorization: $.from('requires_acl_authorization').toBoolean().def(true),
|
|
495
|
+
authentication: $.from('requires_authentication').toBoolean().def(true),
|
|
496
|
+
internalRole: $.from('requires_snc_internal_role').toBoolean().def(true),
|
|
497
|
+
shortDescription: $.from('short_description').def(''),
|
|
498
|
+
requestExample: $.from('request_example').def(''),
|
|
499
|
+
policy: $.from('sys_policy').def(''),
|
|
500
|
+
parameters: $.val(
|
|
501
|
+
routeAttributeTransform(
|
|
502
|
+
paramRouteAssociations,
|
|
503
|
+
parameters,
|
|
504
|
+
r.getId().getValue(),
|
|
505
|
+
'query_parameter'
|
|
506
|
+
)
|
|
507
|
+
).def([]),
|
|
508
|
+
headers: $.val(
|
|
509
|
+
routeAttributeTransform(
|
|
510
|
+
headerRouteAssociations,
|
|
511
|
+
headers,
|
|
512
|
+
r.getId().getValue(),
|
|
513
|
+
'header'
|
|
514
|
+
)
|
|
515
|
+
).def([]),
|
|
516
|
+
version: $.from('web_service_version').map((v) => getVersionNumber(v, versions)),
|
|
517
|
+
}))
|
|
518
|
+
.withAliasedKeys(routeAliases)
|
|
519
|
+
})
|
|
520
|
+
)
|
|
521
|
+
|
|
479
522
|
return {
|
|
480
523
|
success: true,
|
|
481
524
|
value: new CallExpressionShape({
|
|
@@ -496,56 +539,7 @@ export const RestApiPlugin = Plugin.create({
|
|
|
496
539
|
shortDescription: $.from('short_description').def(''),
|
|
497
540
|
policy: $.from('sys_policy').def(''),
|
|
498
541
|
docLink: $.from('doc_link').def(''),
|
|
499
|
-
routes: $.val(
|
|
500
|
-
routes.map((r) =>
|
|
501
|
-
r
|
|
502
|
-
.transform(({ $ }) => ({
|
|
503
|
-
$id: $.val(NowIdShape.from(r)),
|
|
504
|
-
name: $,
|
|
505
|
-
active: $.toBoolean().def(true),
|
|
506
|
-
consumes: $,
|
|
507
|
-
method: $.from('http_method').def('GET'),
|
|
508
|
-
script: $.from('operation_script'),
|
|
509
|
-
produces: $,
|
|
510
|
-
path: $.from('relative_path').def('/'),
|
|
511
|
-
enforceAcl: $.val(splitAcls(r.get('enforce_acl'))).def([
|
|
512
|
-
DEFAULT_REST_ENFORCED_ACL,
|
|
513
|
-
]),
|
|
514
|
-
authorization: $.from('requires_acl_authorization')
|
|
515
|
-
.toBoolean()
|
|
516
|
-
.def(true),
|
|
517
|
-
authentication: $.from('requires_authentication')
|
|
518
|
-
.toBoolean()
|
|
519
|
-
.def(true),
|
|
520
|
-
internalRole: $.from('requires_snc_internal_role')
|
|
521
|
-
.toBoolean()
|
|
522
|
-
.def(true),
|
|
523
|
-
shortDescription: $.from('short_description').def(''),
|
|
524
|
-
requestExample: $.from('request_example').def(''),
|
|
525
|
-
policy: $.from('sys_policy').def(''),
|
|
526
|
-
parameters: $.val(
|
|
527
|
-
routeAttributeTransform(
|
|
528
|
-
paramRouteAssociations,
|
|
529
|
-
parameters,
|
|
530
|
-
r.getId().getValue(),
|
|
531
|
-
'query_parameter'
|
|
532
|
-
)
|
|
533
|
-
).def([]),
|
|
534
|
-
headers: $.val(
|
|
535
|
-
routeAttributeTransform(
|
|
536
|
-
headerRouteAssociations,
|
|
537
|
-
headers,
|
|
538
|
-
r.getId().getValue(),
|
|
539
|
-
'header'
|
|
540
|
-
)
|
|
541
|
-
).def([]),
|
|
542
|
-
version: $.from('web_service_version').map((v) =>
|
|
543
|
-
getVersionNumber(v, versions)
|
|
544
|
-
),
|
|
545
|
-
}))
|
|
546
|
-
.withAliasedKeys(routeAliases)
|
|
547
|
-
)
|
|
548
|
-
).def([]),
|
|
542
|
+
routes: $.val(routesWithScript).def([]),
|
|
549
543
|
versions: $.val(versionsTransform(versions)).def([]),
|
|
550
544
|
}))
|
|
551
545
|
.withAliasedKeys(restDefAliases),
|
package/src/role-plugin.ts
CHANGED
|
@@ -223,8 +223,8 @@ function isValidRequireCall(callExpression: ts.CallExpression, requirePath: ts.S
|
|
|
223
223
|
return isRequire && !isRelativePath
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
function buildParentPathMap(dependencyNodes: DependencyNode[]) {
|
|
227
|
-
const importerMap = {}
|
|
226
|
+
function buildParentPathMap(dependencyNodes: DependencyNode[]): { [key: string]: string[] } {
|
|
227
|
+
const importerMap: { [key: string]: string[] } = {}
|
|
228
228
|
for (const node of dependencyNodes) {
|
|
229
229
|
let parent = node.parentPackage
|
|
230
230
|
|
|
@@ -233,7 +233,9 @@ function buildParentPathMap(dependencyNodes: DependencyNode[]) {
|
|
|
233
233
|
if (!importerMap[importerMapKey]) {
|
|
234
234
|
importerMap[importerMapKey] = []
|
|
235
235
|
}
|
|
236
|
-
|
|
236
|
+
if (parent.pkgName) {
|
|
237
|
+
importerMap[importerMapKey].push(parent.pkgName)
|
|
238
|
+
}
|
|
237
239
|
|
|
238
240
|
const newParent = dependencyNodes.find((potentialParent) => {
|
|
239
241
|
if (!parent || !parent.pkgName) {
|
|
@@ -284,6 +286,11 @@ export function clearDependencyCache() {
|
|
|
284
286
|
function parseModuleDependency(
|
|
285
287
|
node: ts.ImportDeclaration | ts.ExportDeclaration | ts.CallExpression
|
|
286
288
|
): Result<ModuleDependencyShape> {
|
|
289
|
+
//Check if this is a type only import and skip it
|
|
290
|
+
if (node.asKind(ts.SyntaxKind.ImportDeclaration)?.getImportClause()?.isTypeOnly()) {
|
|
291
|
+
return { success: false }
|
|
292
|
+
}
|
|
293
|
+
|
|
287
294
|
let moduleName: string | undefined
|
|
288
295
|
if (ts.Node.isImportDeclaration(node) && !node.isModuleSpecifierRelative()) {
|
|
289
296
|
moduleName = node.getModuleSpecifierValue()
|
|
@@ -386,6 +393,7 @@ export const ServerModulePlugin = Plugin.create({
|
|
|
386
393
|
success: false,
|
|
387
394
|
}
|
|
388
395
|
}
|
|
396
|
+
|
|
389
397
|
const sbomContent = generateSBOMContent(context)
|
|
390
398
|
|
|
391
399
|
return {
|
|
@@ -417,6 +425,9 @@ export const ServerModulePlugin = Plugin.create({
|
|
|
417
425
|
shape: SourceFileShape,
|
|
418
426
|
fileTypes: ['module'],
|
|
419
427
|
async toRecord(file, { factory, fs, diagnostics, project, config, packageJson, compiler }) {
|
|
428
|
+
if (config.type === 'configuration') {
|
|
429
|
+
throw new Error(`Modules cannot be used in a configuration project`)
|
|
430
|
+
}
|
|
420
431
|
const path = file.getPath()
|
|
421
432
|
if (!path.startsWith(project.resolvePath(config.serverModulesDir))) {
|
|
422
433
|
return { success: false }
|
|
@@ -467,6 +478,10 @@ export const ServerModulePlugin = Plugin.create({
|
|
|
467
478
|
fileTypes: ['module'],
|
|
468
479
|
// TODO: When managed cache is provided to plugins, cache dependencies that were already handled to avoid reprocessing
|
|
469
480
|
async toRecord(shape, { packageJson, diagnostics, fs, logger, project, factory, config }) {
|
|
481
|
+
if (config.type === 'configuration') {
|
|
482
|
+
throw new Error(`Modules cannot be used in a configuration project`)
|
|
483
|
+
}
|
|
484
|
+
|
|
470
485
|
const dependencies = packageJson.dependencies ?? {}
|
|
471
486
|
const { name: parentName, entry } = validateAndGetModuleSpecifier(shape.getModuleName())
|
|
472
487
|
|
|
@@ -486,7 +501,7 @@ export const ServerModulePlugin = Plugin.create({
|
|
|
486
501
|
throw new Error(`Failed to build dependency ${id}`)
|
|
487
502
|
}
|
|
488
503
|
|
|
489
|
-
let importerMap = {}
|
|
504
|
+
let importerMap: { [key: string]: string[] } = {}
|
|
490
505
|
if (!config.hoistDependencies) {
|
|
491
506
|
importerMap = buildParentPathMap(dependencyNodes)
|
|
492
507
|
}
|
|
@@ -507,6 +522,7 @@ export const ServerModulePlugin = Plugin.create({
|
|
|
507
522
|
}
|
|
508
523
|
}
|
|
509
524
|
|
|
525
|
+
const importerPath = importerMap[`${pkgName}@${version}`]
|
|
510
526
|
modules.push({
|
|
511
527
|
id: `${name}@${version}/${file}`,
|
|
512
528
|
path: getModuleDependencyPath(config, {
|
|
@@ -514,7 +530,7 @@ export const ServerModulePlugin = Plugin.create({
|
|
|
514
530
|
file,
|
|
515
531
|
version,
|
|
516
532
|
packageJson,
|
|
517
|
-
importerPath
|
|
533
|
+
...(importerPath ? { importerPath } : {}),
|
|
518
534
|
}),
|
|
519
535
|
content: fileContent,
|
|
520
536
|
})
|
|
@@ -171,7 +171,10 @@ export const SPWidgetPlugin = Plugin.create({
|
|
|
171
171
|
explicitId: widget.get('$id'),
|
|
172
172
|
properties: widget.transform(({ $ }) => ({
|
|
173
173
|
name: $,
|
|
174
|
-
category: $.map((v) =>
|
|
174
|
+
category: $.map((v) => {
|
|
175
|
+
const catKey = v.ifString()?.getValue() || ''
|
|
176
|
+
return WidgetCategories[catKey as keyof typeof WidgetCategories]
|
|
177
|
+
}).def('custom'),
|
|
175
178
|
client_script: $.from('clientScript').def(getDefaultClientScript(controller)),
|
|
176
179
|
script: $.from('serverScript').def(SP_WIDGET_DEFAULT_SERVER_SCRIPT),
|
|
177
180
|
controller_as: $.from('controllerAs').def('c'),
|
|
@@ -22,7 +22,7 @@ export const chunkData = (data: string): string[] => {
|
|
|
22
22
|
return chunks
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const generateId = (...parts: Array<string | number>): string => new MD5().update(parts.join(':')).digest('hex')
|
|
25
|
+
export const generateId = (...parts: Array<string | number>): string => new MD5().update(parts.join(':')).digest('hex')
|
|
26
26
|
|
|
27
27
|
// based on tectonic code for validating XML content
|
|
28
28
|
// https://code.devsnc.com/dev/sn-tectonic/blob/b3ab42ce742158cb5a0d00efd540b97eeafcbdd7/core/metadata-transform-san-diego/utils/index.js#L51-L63
|
|
@@ -71,7 +71,7 @@ const attachmentRelationships = {
|
|
|
71
71
|
},
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const toNoOpShape = (record) => {
|
|
74
|
+
const toNoOpShape = (record: Record) => {
|
|
75
75
|
return { success: true, value: Shape.noOp(record) }
|
|
76
76
|
}
|
|
77
77
|
|
package/src/table-plugin.ts
CHANGED
|
@@ -63,6 +63,7 @@ const ColumnSchema = z
|
|
|
63
63
|
'@_max_length': z.coerce.number().or(z.string()).optional(),
|
|
64
64
|
'@_mandatory': BooleanFromString.optional(),
|
|
65
65
|
'@_read_only': BooleanFromString.optional(),
|
|
66
|
+
'@_read_only_option': z.string().optional(),
|
|
66
67
|
'@_reference_table': z.string().optional(),
|
|
67
68
|
'@_reference_qual': z.string().optional(),
|
|
68
69
|
'@_label': z.string().optional(),
|
|
@@ -150,6 +151,7 @@ type ColumnDefinition = {
|
|
|
150
151
|
maxLength: number | string | undefined
|
|
151
152
|
isMandatory: boolean | undefined
|
|
152
153
|
isReadOnly: boolean | undefined
|
|
154
|
+
readOnlyOption: string | undefined
|
|
153
155
|
referenceTable: string | undefined
|
|
154
156
|
referenceQual: string | undefined
|
|
155
157
|
columnLabel: string | undefined
|
|
@@ -245,6 +247,7 @@ type SysDictionaryProperties = {
|
|
|
245
247
|
max_length: number | string | undefined
|
|
246
248
|
mandatory: boolean | undefined
|
|
247
249
|
read_only: boolean | undefined
|
|
250
|
+
read_only_option: string | undefined
|
|
248
251
|
reference_table: string | undefined
|
|
249
252
|
reference_qual: string | undefined
|
|
250
253
|
column_label: string | undefined
|
|
@@ -461,7 +464,7 @@ export const TablePlugin = Plugin.create({
|
|
|
461
464
|
},
|
|
462
465
|
},
|
|
463
466
|
toShape(record, { descendants }) {
|
|
464
|
-
const schema = {}
|
|
467
|
+
const schema: { [key: string]: CallExpressionShape } = {}
|
|
465
468
|
let displayColumn: string | undefined
|
|
466
469
|
const columns = descendants.query('sys_dictionary')
|
|
467
470
|
let collectionRecord: Record
|
|
@@ -497,6 +500,7 @@ export const TablePlugin = Plugin.create({
|
|
|
497
500
|
const callExpression = new CallExpressionShape({
|
|
498
501
|
source: record,
|
|
499
502
|
callee: 'Table',
|
|
503
|
+
exportName: record.get('name').ifString()?.getValue(),
|
|
500
504
|
args: [
|
|
501
505
|
record
|
|
502
506
|
.transform(({ $ }) => ({
|
|
@@ -545,7 +549,7 @@ export const TablePlugin = Plugin.create({
|
|
|
545
549
|
if (!attributes?.isString()) {
|
|
546
550
|
return undefined
|
|
547
551
|
}
|
|
548
|
-
const result = {}
|
|
552
|
+
const result: { [key: string]: string | boolean } = {}
|
|
549
553
|
attributes
|
|
550
554
|
.toString()
|
|
551
555
|
.getValue()
|
|
@@ -555,12 +559,15 @@ export const TablePlugin = Plugin.create({
|
|
|
555
559
|
return
|
|
556
560
|
}
|
|
557
561
|
const [key, value] = attr.split('=').map((s) => s.trim())
|
|
562
|
+
if (!key || value === undefined) {
|
|
563
|
+
return
|
|
564
|
+
}
|
|
558
565
|
if (value === 'true') {
|
|
559
|
-
result[key
|
|
566
|
+
result[key] = true
|
|
560
567
|
} else if (value === 'false') {
|
|
561
|
-
result[key
|
|
568
|
+
result[key] = false
|
|
562
569
|
} else {
|
|
563
|
-
result[key
|
|
570
|
+
result[key] = value
|
|
564
571
|
}
|
|
565
572
|
})
|
|
566
573
|
return result
|
|
@@ -688,7 +695,7 @@ export const TablePlugin = Plugin.create({
|
|
|
688
695
|
}
|
|
689
696
|
},
|
|
690
697
|
async toFile(record, { descendants, config, transform }) {
|
|
691
|
-
if (config.tableOutputFormat !== 'bootstrap' || record.isDeleted()) {
|
|
698
|
+
if (config.tableOutputFormat !== 'bootstrap' || config.type === 'configuration' || record.isDeleted()) {
|
|
692
699
|
// Defer to record plugin
|
|
693
700
|
return { success: false }
|
|
694
701
|
}
|
|
@@ -739,6 +746,7 @@ export const TablePlugin = Plugin.create({
|
|
|
739
746
|
['max_length', maxLength.ifNumber()?.getValue().toString() ?? maxLength.ifString()?.getValue()],
|
|
740
747
|
['mandatory', column.get('mandatory').ifBoolean()?.getValue().toString()],
|
|
741
748
|
['read_only', column.get('read_only').ifBoolean()?.getValue().toString()],
|
|
749
|
+
['read_only_option', column.get('read_only_option').ifString()?.getValue()],
|
|
742
750
|
['reference_cascade_rule', column.get('reference_cascade_rule').ifString()?.getValue()],
|
|
743
751
|
['calculation', column.get('calculation').ifString()?.getValue()],
|
|
744
752
|
['choice', column.get('choice').ifNumber()?.getValue().toString()],
|
|
@@ -947,6 +955,7 @@ export const TablePlugin = Plugin.create({
|
|
|
947
955
|
let ignoreColumnNameCheck = false
|
|
948
956
|
const scopeName = config.scope
|
|
949
957
|
const scopeRegex = new RegExp(`^${scopeName}_`)
|
|
958
|
+
const globalRegex = /^u_/
|
|
950
959
|
const tableNameMatch = tableName.getValue().match(scopeRegex)
|
|
951
960
|
if (!tableNameMatch && !isSNScope(scopeName) && scopeName !== 'global') {
|
|
952
961
|
const nameNode = tableName.getOriginalNode()
|
|
@@ -973,6 +982,29 @@ export const TablePlugin = Plugin.create({
|
|
|
973
982
|
)
|
|
974
983
|
}
|
|
975
984
|
|
|
985
|
+
const globalTableNameMatch = tableName.getValue().match(globalRegex)
|
|
986
|
+
let anyNonPrefixedGlobalColumn = false
|
|
987
|
+
|
|
988
|
+
if (scopeName === 'global' && !globalTableNameMatch) {
|
|
989
|
+
const schema = table.get('schema').asObject()
|
|
990
|
+
for (const [name, _] of schema.entries()) {
|
|
991
|
+
if (!name.match(globalRegex)) {
|
|
992
|
+
anyNonPrefixedGlobalColumn = true
|
|
993
|
+
break
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (anyNonPrefixedGlobalColumn) {
|
|
998
|
+
diagnostics.error(
|
|
999
|
+
table.get('name'),
|
|
1000
|
+
`Global table 'name' property should start with custom prefix 'u_'`
|
|
1001
|
+
)
|
|
1002
|
+
}
|
|
1003
|
+
} else if (scopeName === 'global') {
|
|
1004
|
+
// Global table starts with custom prefix `u_`, allow any column name prefix
|
|
1005
|
+
ignoreColumnNameCheck = true
|
|
1006
|
+
}
|
|
1007
|
+
|
|
976
1008
|
// sys_dictionary
|
|
977
1009
|
const schema = table.get('schema').asObject()
|
|
978
1010
|
for (const [name, column] of schema.entries()) {
|
|
@@ -994,6 +1026,11 @@ export const TablePlugin = Plugin.create({
|
|
|
994
1026
|
column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
|
|
995
1027
|
`Column name should be prefixed with scope '${scopeName}_' if table name does not contain prefix`
|
|
996
1028
|
)
|
|
1029
|
+
} else if (scopeName === 'global' && !globalTableNameMatch && !name.match(globalRegex)) {
|
|
1030
|
+
diagnostics.error(
|
|
1031
|
+
column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
|
|
1032
|
+
`Column name should be prefixed with 'u_' custom prefix if table name does not contain this prefix, such as when adding columns to an existing global table`
|
|
1033
|
+
)
|
|
997
1034
|
}
|
|
998
1035
|
const display = table.get('display').ifString()?.getValue() === name
|
|
999
1036
|
const result = await transform.toRecord(
|
|
@@ -1279,6 +1316,7 @@ function parseTableBootstrapXml(xml: unknown): TableDefinition | null {
|
|
|
1279
1316
|
maxLength: column['@_max_length'],
|
|
1280
1317
|
isMandatory: column['@_mandatory'],
|
|
1281
1318
|
isReadOnly: column['@_read_only'],
|
|
1319
|
+
readOnlyOption: column['@_read_only_option'],
|
|
1282
1320
|
referenceTable: column['@_reference_table'],
|
|
1283
1321
|
referenceQual: column['@_reference_qual'],
|
|
1284
1322
|
columnLabel: column['@_label'],
|
|
@@ -1441,6 +1479,7 @@ function tableDefToRecordProperties(tableDef: TableDefinition): {
|
|
|
1441
1479
|
max_length: column.maxLength,
|
|
1442
1480
|
mandatory: column.isMandatory,
|
|
1443
1481
|
read_only: column.isReadOnly,
|
|
1482
|
+
read_only_option: column.readOnlyOption,
|
|
1444
1483
|
reference_table: column.referenceTable,
|
|
1445
1484
|
reference_qual: column.referenceQual,
|
|
1446
1485
|
column_label: column.columnLabel,
|
|
@@ -1522,6 +1561,7 @@ function tableDefToRecordProperties(tableDef: TableDefinition): {
|
|
|
1522
1561
|
default_value: undefined,
|
|
1523
1562
|
max_length: undefined,
|
|
1524
1563
|
mandatory: undefined,
|
|
1564
|
+
read_only_option: undefined,
|
|
1525
1565
|
reference_table: undefined,
|
|
1526
1566
|
reference_qual: undefined,
|
|
1527
1567
|
column_label: undefined,
|
|
@@ -1673,7 +1713,7 @@ async function generateRecordXml(
|
|
|
1673
1713
|
): Promise<OutputFile[]> {
|
|
1674
1714
|
const files: OutputFile[] = []
|
|
1675
1715
|
for (const record of records) {
|
|
1676
|
-
const recordBuilder = unloadBuilder({ scope: config.scope, scopeId: config.scopeId })
|
|
1716
|
+
const recordBuilder = unloadBuilder({ scope: config.scope, scopeId: config.scopeId, table: record.getTable() })
|
|
1677
1717
|
const updateName = await transform.getUpdateName(record)
|
|
1678
1718
|
const builder = recordBuilder.record(record, updateName)
|
|
1679
1719
|
|
package/src/ui-page-plugin.ts
CHANGED
|
@@ -22,7 +22,7 @@ const builderOptions: XmlBuilderOptions = {
|
|
|
22
22
|
processEntities: true,
|
|
23
23
|
// A valid but undocumented option
|
|
24
24
|
// https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/src/xmlbuilder/json2xml.js#L26
|
|
25
|
-
// @ts-
|
|
25
|
+
// @ts-expect-error
|
|
26
26
|
entities: [
|
|
27
27
|
// Match on &, &test, but not &, <, >, ', and "
|
|
28
28
|
// See: https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Standard_public_entity_sets_for_characters for the list of default XML entities
|
|
@@ -70,6 +70,7 @@ window.g_ck = "$[gs.getSession().getSessionToken() || gs.getSessionToken()]";
|
|
|
70
70
|
* @param nodes - parsed XML nodes
|
|
71
71
|
* @returns - parsed XML nodes and any replacements of synthetic tags
|
|
72
72
|
*/
|
|
73
|
+
// biome-ignore lint/suspicious/noExplicitAny: Fast-xml-parser types are not defined
|
|
73
74
|
const nodeTransformer = (nodes: any[]) => {
|
|
74
75
|
for (let i = 0; i < nodes.length; i++) {
|
|
75
76
|
const node = nodes[i]
|