@servicenow/sdk-build-plugins 3.0.3 → 4.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 (359) hide show
  1. package/dist/acl-plugin.d.ts +19 -0
  2. package/dist/acl-plugin.js +210 -0
  3. package/dist/acl-plugin.js.map +1 -0
  4. package/dist/application-menu-plugin.d.ts +18 -0
  5. package/dist/application-menu-plugin.js +104 -0
  6. package/dist/application-menu-plugin.js.map +1 -0
  7. package/dist/arrow-function-plugin.d.ts +16 -0
  8. package/dist/arrow-function-plugin.js +107 -0
  9. package/dist/arrow-function-plugin.js.map +1 -0
  10. package/dist/atf/step-configs.d.ts +39 -0
  11. package/dist/atf/step-configs.js +2334 -0
  12. package/dist/atf/step-configs.js.map +1 -0
  13. package/dist/atf/test-plugin.d.ts +4 -0
  14. package/dist/atf/test-plugin.js +600 -0
  15. package/dist/atf/test-plugin.js.map +1 -0
  16. package/dist/basic-syntax-plugin.d.ts +2 -0
  17. package/dist/basic-syntax-plugin.js +636 -0
  18. package/dist/basic-syntax-plugin.js.map +1 -0
  19. package/dist/business-rule-plugin.d.ts +3 -0
  20. package/dist/business-rule-plugin.js +205 -0
  21. package/dist/business-rule-plugin.js.map +1 -0
  22. package/dist/call-expression-plugin.d.ts +2 -0
  23. package/dist/call-expression-plugin.js +175 -0
  24. package/dist/call-expression-plugin.js.map +1 -0
  25. package/dist/client-script-plugin.d.ts +29 -0
  26. package/dist/client-script-plugin.js +164 -0
  27. package/dist/client-script-plugin.js.map +1 -0
  28. package/dist/column/column-helper.d.ts +12 -0
  29. package/dist/column/column-helper.js +84 -0
  30. package/dist/column/column-helper.js.map +1 -0
  31. package/dist/column/column-to-record.d.ts +3 -0
  32. package/dist/column/column-to-record.js +105 -0
  33. package/dist/column/column-to-record.js.map +1 -0
  34. package/dist/column-plugin.d.ts +8 -0
  35. package/dist/column-plugin.js +453 -0
  36. package/dist/column-plugin.js.map +1 -0
  37. package/dist/cross-scope-privilege-plugin.d.ts +15 -0
  38. package/dist/cross-scope-privilege-plugin.js +82 -0
  39. package/dist/cross-scope-privilege-plugin.js.map +1 -0
  40. package/dist/html-import-plugin.d.ts +2 -0
  41. package/dist/html-import-plugin.js +72 -0
  42. package/dist/html-import-plugin.js.map +1 -0
  43. package/dist/index.d.ts +37 -21
  44. package/dist/index.js +40 -45
  45. package/dist/index.js.map +1 -1
  46. package/dist/json-plugin.d.ts +15 -0
  47. package/dist/json-plugin.js +104 -0
  48. package/dist/json-plugin.js.map +1 -0
  49. package/dist/list-plugin.d.ts +2 -0
  50. package/dist/list-plugin.js +191 -0
  51. package/dist/list-plugin.js.map +1 -0
  52. package/dist/now-config-plugin.d.ts +4 -0
  53. package/dist/now-config-plugin.js +154 -0
  54. package/dist/now-config-plugin.js.map +1 -0
  55. package/dist/now-id-plugin.d.ts +11 -0
  56. package/dist/now-id-plugin.js +71 -0
  57. package/dist/now-id-plugin.js.map +1 -0
  58. package/dist/now-include-plugin.d.ts +16 -0
  59. package/dist/now-include-plugin.js +118 -0
  60. package/dist/now-include-plugin.js.map +1 -0
  61. package/dist/now-ref-plugin.d.ts +2 -0
  62. package/dist/now-ref-plugin.js +43 -0
  63. package/dist/now-ref-plugin.js.map +1 -0
  64. package/dist/now-unresolved-plugin.d.ts +2 -0
  65. package/dist/now-unresolved-plugin.js +32 -0
  66. package/dist/now-unresolved-plugin.js.map +1 -0
  67. package/dist/package-json-plugin.d.ts +2 -0
  68. package/dist/package-json-plugin.js +70 -0
  69. package/dist/package-json-plugin.js.map +1 -0
  70. package/dist/property-plugin.d.ts +2 -0
  71. package/dist/property-plugin.js +105 -0
  72. package/dist/property-plugin.js.map +1 -0
  73. package/dist/record-plugin.d.ts +43 -0
  74. package/dist/record-plugin.js +147 -0
  75. package/dist/record-plugin.js.map +1 -0
  76. package/dist/repack/index.d.ts +1 -0
  77. package/dist/repack/index.js +10 -2
  78. package/dist/repack/index.js.map +1 -1
  79. package/dist/repack/lint/Rules.d.ts +1 -1
  80. package/dist/repack/lint/Rules.js +7 -3
  81. package/dist/repack/lint/Rules.js.map +1 -1
  82. package/dist/repack/lint/index.js +1 -1
  83. package/dist/rest-api-plugin.d.ts +2 -0
  84. package/dist/rest-api-plugin.js +469 -0
  85. package/dist/rest-api-plugin.js.map +1 -0
  86. package/dist/role-plugin.d.ts +2 -0
  87. package/dist/role-plugin.js +117 -0
  88. package/dist/role-plugin.js.map +1 -0
  89. package/dist/script-action-plugin.d.ts +2 -0
  90. package/dist/script-action-plugin.js +62 -0
  91. package/dist/script-action-plugin.js.map +1 -0
  92. package/dist/script-include-plugin.d.ts +2 -0
  93. package/dist/script-include-plugin.js +102 -0
  94. package/dist/script-include-plugin.js.map +1 -0
  95. package/dist/server-module-plugin/index.d.ts +14 -0
  96. package/dist/server-module-plugin/index.js +378 -0
  97. package/dist/server-module-plugin/index.js.map +1 -0
  98. package/dist/server-module-plugin/sbom-builder.d.ts +45 -0
  99. package/dist/server-module-plugin/sbom-builder.js +179 -0
  100. package/dist/server-module-plugin/sbom-builder.js.map +1 -0
  101. package/dist/service-portal/angular-provider-plugin.d.ts +2 -0
  102. package/dist/service-portal/angular-provider-plugin.js +78 -0
  103. package/dist/service-portal/angular-provider-plugin.js.map +1 -0
  104. package/dist/service-portal/dependency-plugin.d.ts +2 -0
  105. package/dist/service-portal/dependency-plugin.js +235 -0
  106. package/dist/service-portal/dependency-plugin.js.map +1 -0
  107. package/dist/service-portal/widget-plugin.d.ts +6 -0
  108. package/dist/service-portal/widget-plugin.js +230 -0
  109. package/dist/service-portal/widget-plugin.js.map +1 -0
  110. package/dist/static-content-plugin.d.ts +2 -0
  111. package/dist/static-content-plugin.js +272 -0
  112. package/dist/static-content-plugin.js.map +1 -0
  113. package/dist/table-plugin.d.ts +3 -0
  114. package/dist/table-plugin.js +1324 -0
  115. package/dist/table-plugin.js.map +1 -0
  116. package/dist/ui-action-plugin.d.ts +2 -0
  117. package/dist/ui-action-plugin.js +291 -0
  118. package/dist/ui-action-plugin.js.map +1 -0
  119. package/dist/ui-page-plugin.d.ts +2 -0
  120. package/dist/ui-page-plugin.js +165 -0
  121. package/dist/ui-page-plugin.js.map +1 -0
  122. package/dist/user-preference-plugin.d.ts +15 -0
  123. package/dist/user-preference-plugin.js +63 -0
  124. package/dist/user-preference-plugin.js.map +1 -0
  125. package/dist/utils.d.ts +17 -0
  126. package/dist/utils.js +72 -0
  127. package/dist/utils.js.map +1 -0
  128. package/dist/view-plugin.d.ts +2 -0
  129. package/dist/view-plugin.js +45 -0
  130. package/dist/view-plugin.js.map +1 -0
  131. package/package.json +17 -13
  132. package/src/acl-plugin.ts +256 -0
  133. package/src/application-menu-plugin.ts +109 -0
  134. package/src/arrow-function-plugin.ts +127 -0
  135. package/src/atf/step-configs.ts +2384 -0
  136. package/src/atf/test-plugin.ts +739 -0
  137. package/src/basic-syntax-plugin.ts +729 -0
  138. package/src/business-rule-plugin.ts +266 -0
  139. package/src/call-expression-plugin.ts +202 -0
  140. package/src/client-script-plugin.ts +179 -0
  141. package/src/column/column-helper.ts +119 -0
  142. package/src/column/column-to-record.ts +131 -0
  143. package/src/column-plugin.ts +506 -0
  144. package/src/cross-scope-privilege-plugin.ts +82 -0
  145. package/src/html-import-plugin.ts +79 -0
  146. package/src/index.ts +39 -21
  147. package/src/json-plugin.ts +128 -0
  148. package/src/list-plugin.ts +222 -0
  149. package/src/now-config-plugin.ts +194 -0
  150. package/src/now-id-plugin.ts +78 -0
  151. package/src/now-include-plugin.ts +140 -0
  152. package/src/now-ref-plugin.ts +48 -0
  153. package/src/now-unresolved-plugin.ts +30 -0
  154. package/src/package-json-plugin.ts +87 -0
  155. package/src/property-plugin.ts +118 -0
  156. package/src/record-plugin.ts +171 -0
  157. package/src/repack/index.ts +10 -1
  158. package/src/repack/lint/Rules.ts +5 -4
  159. package/src/repack/lint/index.ts +1 -1
  160. package/src/rest-api-plugin.ts +658 -0
  161. package/src/role-plugin.ts +128 -0
  162. package/src/script-action-plugin.ts +63 -0
  163. package/src/script-include-plugin.ts +110 -0
  164. package/src/server-module-plugin/index.ts +470 -0
  165. package/src/server-module-plugin/sbom-builder.ts +183 -0
  166. package/src/service-portal/angular-provider-plugin.ts +83 -0
  167. package/src/service-portal/dependency-plugin.ts +284 -0
  168. package/src/service-portal/widget-plugin.ts +263 -0
  169. package/src/static-content-plugin.ts +254 -0
  170. package/src/table-plugin.ts +1698 -0
  171. package/src/ui-action-plugin.ts +324 -0
  172. package/src/ui-page-plugin.ts +168 -0
  173. package/src/user-preference-plugin.ts +62 -0
  174. package/src/utils.ts +69 -0
  175. package/src/view-plugin.ts +46 -0
  176. package/dist/AttachmentPlugin.d.ts +0 -254
  177. package/dist/AttachmentPlugin.js +0 -220
  178. package/dist/AttachmentPlugin.js.map +0 -1
  179. package/dist/BusinessRulePlugin.d.ts +0 -30
  180. package/dist/BusinessRulePlugin.js +0 -149
  181. package/dist/BusinessRulePlugin.js.map +0 -1
  182. package/dist/CrossScopePrivilegePlugin.d.ts +0 -35
  183. package/dist/CrossScopePrivilegePlugin.js +0 -80
  184. package/dist/CrossScopePrivilegePlugin.js.map +0 -1
  185. package/dist/DefaultPlugin.d.ts +0 -86
  186. package/dist/DefaultPlugin.js +0 -226
  187. package/dist/DefaultPlugin.js.map +0 -1
  188. package/dist/HtmlTemplatePlugin.d.ts +0 -21
  189. package/dist/HtmlTemplatePlugin.js +0 -29
  190. package/dist/HtmlTemplatePlugin.js.map +0 -1
  191. package/dist/IdPlugin.d.ts +0 -28
  192. package/dist/IdPlugin.js +0 -68
  193. package/dist/IdPlugin.js.map +0 -1
  194. package/dist/IncludePlugin.d.ts +0 -34
  195. package/dist/IncludePlugin.js +0 -155
  196. package/dist/IncludePlugin.js.map +0 -1
  197. package/dist/JsonPlugin.d.ts +0 -28
  198. package/dist/JsonPlugin.js +0 -69
  199. package/dist/JsonPlugin.js.map +0 -1
  200. package/dist/ListPlugin.d.ts +0 -93
  201. package/dist/ListPlugin.js +0 -456
  202. package/dist/ListPlugin.js.map +0 -1
  203. package/dist/NowConfigPlugin.d.ts +0 -45
  204. package/dist/NowConfigPlugin.js +0 -64
  205. package/dist/NowConfigPlugin.js.map +0 -1
  206. package/dist/PackageJsonPlugin.d.ts +0 -34
  207. package/dist/PackageJsonPlugin.js +0 -63
  208. package/dist/PackageJsonPlugin.js.map +0 -1
  209. package/dist/PropertyPlugin.d.ts +0 -51
  210. package/dist/PropertyPlugin.js +0 -181
  211. package/dist/PropertyPlugin.js.map +0 -1
  212. package/dist/ScriptTemplatePlugin.d.ts +0 -13
  213. package/dist/ScriptTemplatePlugin.js +0 -55
  214. package/dist/ScriptTemplatePlugin.js.map +0 -1
  215. package/dist/ServerModulePlugin.d.ts +0 -75
  216. package/dist/ServerModulePlugin.js +0 -368
  217. package/dist/ServerModulePlugin.js.map +0 -1
  218. package/dist/UserPreferencePlugin.d.ts +0 -19
  219. package/dist/UserPreferencePlugin.js +0 -32
  220. package/dist/UserPreferencePlugin.js.map +0 -1
  221. package/dist/aclAndRole/AclPlugin.d.ts +0 -82
  222. package/dist/aclAndRole/AclPlugin.js +0 -262
  223. package/dist/aclAndRole/AclPlugin.js.map +0 -1
  224. package/dist/aclAndRole/RolePlugin.d.ts +0 -66
  225. package/dist/aclAndRole/RolePlugin.js +0 -179
  226. package/dist/aclAndRole/RolePlugin.js.map +0 -1
  227. package/dist/aclAndRole/Util.d.ts +0 -3
  228. package/dist/aclAndRole/Util.js +0 -90
  229. package/dist/aclAndRole/Util.js.map +0 -1
  230. package/dist/app/ApplicationMenuPlugin.d.ts +0 -34
  231. package/dist/app/ApplicationMenuPlugin.js +0 -112
  232. package/dist/app/ApplicationMenuPlugin.js.map +0 -1
  233. package/dist/db/ColumnPlugins.d.ts +0 -750
  234. package/dist/db/ColumnPlugins.js +0 -114
  235. package/dist/db/ColumnPlugins.js.map +0 -1
  236. package/dist/db/DBUtils.d.ts +0 -2
  237. package/dist/db/DBUtils.js +0 -27
  238. package/dist/db/DBUtils.js.map +0 -1
  239. package/dist/db/DocumentationPlugin.d.ts +0 -67
  240. package/dist/db/DocumentationPlugin.js +0 -258
  241. package/dist/db/DocumentationPlugin.js.map +0 -1
  242. package/dist/db/LicensingPlugin.d.ts +0 -60
  243. package/dist/db/LicensingPlugin.js +0 -117
  244. package/dist/db/LicensingPlugin.js.map +0 -1
  245. package/dist/db/RecordPlugin.d.ts +0 -133
  246. package/dist/db/RecordPlugin.js +0 -337
  247. package/dist/db/RecordPlugin.js.map +0 -1
  248. package/dist/db/TablePlugin.d.ts +0 -231
  249. package/dist/db/TablePlugin.js +0 -1630
  250. package/dist/db/TablePlugin.js.map +0 -1
  251. package/dist/db/index.d.ts +0 -6
  252. package/dist/db/index.js +0 -32
  253. package/dist/db/index.js.map +0 -1
  254. package/dist/scriptedRESTAPI/RESTDeserializationUtils.d.ts +0 -10
  255. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +0 -373
  256. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +0 -1
  257. package/dist/scriptedRESTAPI/RESTSerializationUtils.d.ts +0 -13
  258. package/dist/scriptedRESTAPI/RESTSerializationUtils.js +0 -177
  259. package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +0 -1
  260. package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +0 -81
  261. package/dist/scriptedRESTAPI/RestApiPlugin.js +0 -345
  262. package/dist/scriptedRESTAPI/RestApiPlugin.js.map +0 -1
  263. package/dist/scriptedRESTAPI/RestSchemaUtils.d.ts +0 -190
  264. package/dist/scriptedRESTAPI/RestSchemaUtils.js +0 -53
  265. package/dist/scriptedRESTAPI/RestSchemaUtils.js.map +0 -1
  266. package/dist/scriptedRESTAPI/RestUtils.d.ts +0 -69
  267. package/dist/scriptedRESTAPI/RestUtils.js +0 -497
  268. package/dist/scriptedRESTAPI/RestUtils.js.map +0 -1
  269. package/dist/scripts/ClientScriptPlugin.d.ts +0 -64
  270. package/dist/scripts/ClientScriptPlugin.js +0 -170
  271. package/dist/scripts/ClientScriptPlugin.js.map +0 -1
  272. package/dist/scripts/scriptUtils.d.ts +0 -1
  273. package/dist/scripts/scriptUtils.js +0 -9
  274. package/dist/scripts/scriptUtils.js.map +0 -1
  275. package/dist/uxf/ExperiencePlugin.d.ts +0 -45
  276. package/dist/uxf/ExperiencePlugin.js +0 -61
  277. package/dist/uxf/ExperiencePlugin.js.map +0 -1
  278. package/dist/uxf/RoutesPlugin.d.ts +0 -29
  279. package/dist/uxf/RoutesPlugin.js +0 -181
  280. package/dist/uxf/RoutesPlugin.js.map +0 -1
  281. package/dist/uxf/UxfFormulaParser/cleanUxValue.d.ts +0 -4
  282. package/dist/uxf/UxfFormulaParser/cleanUxValue.js +0 -65
  283. package/dist/uxf/UxfFormulaParser/cleanUxValue.js.map +0 -1
  284. package/dist/uxf/UxfFormulaParser/grammerParser/api.d.ts +0 -189
  285. package/dist/uxf/UxfFormulaParser/grammerParser/api.js +0 -158
  286. package/dist/uxf/UxfFormulaParser/grammerParser/api.js.map +0 -1
  287. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.d.ts +0 -13
  288. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +0 -604
  289. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js.map +0 -1
  290. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.d.ts +0 -12
  291. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js +0 -551
  292. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js.map +0 -1
  293. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.d.ts +0 -31
  294. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +0 -64
  295. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js.map +0 -1
  296. package/dist/uxf/UxfFormulaParser/index.d.ts +0 -3
  297. package/dist/uxf/UxfFormulaParser/index.js +0 -11
  298. package/dist/uxf/UxfFormulaParser/index.js.map +0 -1
  299. package/dist/uxf/UxfFormulaParser/parser.d.ts +0 -8
  300. package/dist/uxf/UxfFormulaParser/parser.js +0 -87
  301. package/dist/uxf/UxfFormulaParser/parser.js.map +0 -1
  302. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.d.ts +0 -8
  303. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js +0 -17
  304. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js.map +0 -1
  305. package/dist/uxf/constants.d.ts +0 -2
  306. package/dist/uxf/constants.js +0 -8
  307. package/dist/uxf/constants.js.map +0 -1
  308. package/dist/uxf/index.d.ts +0 -2
  309. package/dist/uxf/index.js +0 -11
  310. package/dist/uxf/index.js.map +0 -1
  311. package/dist/uxf/tectonicIdGenerator.d.ts +0 -12
  312. package/dist/uxf/tectonicIdGenerator.js +0 -79
  313. package/dist/uxf/tectonicIdGenerator.js.map +0 -1
  314. package/src/AttachmentPlugin.ts +0 -268
  315. package/src/BusinessRulePlugin.ts +0 -238
  316. package/src/CrossScopePrivilegePlugin.ts +0 -115
  317. package/src/DefaultPlugin.ts +0 -288
  318. package/src/HtmlTemplatePlugin.ts +0 -31
  319. package/src/IdPlugin.ts +0 -74
  320. package/src/IncludePlugin.ts +0 -206
  321. package/src/JsonPlugin.ts +0 -76
  322. package/src/ListPlugin.ts +0 -570
  323. package/src/NowConfigPlugin.ts +0 -69
  324. package/src/PackageJsonPlugin.ts +0 -68
  325. package/src/PropertyPlugin.ts +0 -237
  326. package/src/ScriptTemplatePlugin.ts +0 -53
  327. package/src/ServerModulePlugin.ts +0 -480
  328. package/src/UserPreferencePlugin.ts +0 -45
  329. package/src/aclAndRole/AclPlugin.ts +0 -420
  330. package/src/aclAndRole/RolePlugin.ts +0 -254
  331. package/src/aclAndRole/Util.ts +0 -113
  332. package/src/app/ApplicationMenuPlugin.ts +0 -165
  333. package/src/db/ColumnPlugins.ts +0 -114
  334. package/src/db/DBUtils.ts +0 -36
  335. package/src/db/DocumentationPlugin.ts +0 -316
  336. package/src/db/LicensingPlugin.ts +0 -138
  337. package/src/db/RecordPlugin.ts +0 -459
  338. package/src/db/TablePlugin.ts +0 -2072
  339. package/src/db/index.ts +0 -6
  340. package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +0 -419
  341. package/src/scriptedRESTAPI/RESTSerializationUtils.ts +0 -228
  342. package/src/scriptedRESTAPI/RestApiPlugin.ts +0 -469
  343. package/src/scriptedRESTAPI/RestSchemaUtils.ts +0 -72
  344. package/src/scriptedRESTAPI/RestUtils.ts +0 -569
  345. package/src/scripts/ClientScriptPlugin.ts +0 -257
  346. package/src/scripts/scriptUtils.ts +0 -5
  347. package/src/uxf/ExperiencePlugin.ts +0 -67
  348. package/src/uxf/RoutesPlugin.ts +0 -211
  349. package/src/uxf/UxfFormulaParser/cleanUxValue.ts +0 -73
  350. package/src/uxf/UxfFormulaParser/grammerParser/api.js +0 -166
  351. package/src/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +0 -606
  352. package/src/uxf/UxfFormulaParser/grammerParser/grammarParser.js +0 -551
  353. package/src/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +0 -65
  354. package/src/uxf/UxfFormulaParser/index.ts +0 -4
  355. package/src/uxf/UxfFormulaParser/parser.ts +0 -64
  356. package/src/uxf/UxfFormulaParser/utils/getErrorMsg.ts +0 -13
  357. package/src/uxf/constants.ts +0 -4
  358. package/src/uxf/index.ts +0 -2
  359. package/src/uxf/tectonicIdGenerator.ts +0 -78
@@ -0,0 +1,470 @@
1
+ import {
2
+ Plugin,
3
+ Shape,
4
+ type Diagnostics,
5
+ type Transform,
6
+ type Result,
7
+ SourceFileShape,
8
+ StringShape,
9
+ type Source,
10
+ type Project,
11
+ type RecordContext,
12
+ type Compiler,
13
+ } from '@servicenow/sdk-build-core'
14
+ import { path as pathModule, ts, type FileSystem, type NowConfig } from '@servicenow/sdk-build-core'
15
+ import { RepackService } from '../repack'
16
+ import { SBOMBuilder } from './sbom-builder'
17
+ import isEqual from 'lodash/isEqual'
18
+ import { INVALID_XML_CHARACTERS, applyPathMappings } from '../utils'
19
+ import zip from 'lodash/zip'
20
+
21
+ const GLUE_CODE_PREFIX = '// @fluent-module'
22
+ const GLUE_CODE_META_REGEX = new RegExp(`^${GLUE_CODE_PREFIX} (.*);(true|false);(.*)`) // name;isDefault;path
23
+ const GLUE_CODE_WARNING = `
24
+ // WARNING: This code is generated by the ServiceNow SDK in order to provide
25
+ // support for modular JavaScript. Modifications of any kind are likely to
26
+ // result in unintended behavior. In most cases, you should edit the source
27
+ // file of the module imported below. If you are absolutely certain you want
28
+ // to take control of this code, you can remove this comment to prevent the
29
+ // SDK from regenerating it. However, you will then be responsible for the
30
+ // management of this code in your Fluent file.`
31
+
32
+ type GlueCodeMeta = {
33
+ name: string
34
+ path: string
35
+ isDefault: boolean
36
+ }
37
+
38
+ function isGlueCode(string: StringShape): boolean {
39
+ return string.startsWith(GLUE_CODE_PREFIX)
40
+ }
41
+
42
+ function parseMeta(string: StringShape): GlueCodeMeta {
43
+ const result = string.getValue().match(GLUE_CODE_META_REGEX)
44
+ const [, name, isDefault, path] = result ?? []
45
+
46
+ if (typeof name !== 'string' || typeof isDefault !== 'string' || typeof path !== 'string') {
47
+ throw new Error(`Invalid glue code format: ${string.getValue()}`)
48
+ }
49
+
50
+ return { name, path, isDefault: isDefault === 'true' }
51
+ }
52
+
53
+ export class ModuleFunctionShape extends Shape {
54
+ constructor(
55
+ source: Source,
56
+ private readonly meta: GlueCodeMeta
57
+ ) {
58
+ super({ source })
59
+ }
60
+
61
+ override toString(
62
+ callExpressionProvider: (functionName: string) => string = (name) => `${name}()`,
63
+ defaultParams?: string[]
64
+ ): StringShape {
65
+ const { name, path, isDefault } = this.meta
66
+ const moduleParamNames = getFunctionParameters(this.getSource())
67
+ const variables = zip(defaultParams ?? [], moduleParamNames).map(
68
+ ([defaultParam, moduleParamName]) => defaultParam ?? moduleParamName
69
+ )
70
+ const moduleCallExpression = callExpressionProvider(name).replace('{{PARAMS}}', variables.join(', '))
71
+
72
+ const code = isDefault
73
+ ? `const ${name} = require('${path}').default;\n${moduleCallExpression};`
74
+ : `const { ${name} } = require('${path}');\n${moduleCallExpression};`
75
+
76
+ return Shape.from(this, `${GLUE_CODE_PREFIX} ${name};${isDefault};${path}${GLUE_CODE_WARNING}\n${code}`)
77
+ .asString()
78
+ .withContentType('cdata')
79
+ }
80
+
81
+ override equals(other: Shape): boolean {
82
+ if (other.is(ModuleFunctionShape)) {
83
+ return isEqual(this.meta, other.meta)
84
+ } else if (other.isString()) {
85
+ if (!isGlueCode(other)) {
86
+ return false
87
+ }
88
+
89
+ return isEqual(this.meta, parseMeta(other))
90
+ } else {
91
+ return super.equals(other)
92
+ }
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Removes invalid control characters that would cause XML parsing errors.
98
+ */
99
+ function sanitizeModuleContent(content: string): string {
100
+ return content.replace(INVALID_XML_CHARACTERS, '')
101
+ }
102
+
103
+ function getEmitOutput(
104
+ file: ts.SourceFile,
105
+ {
106
+ fs,
107
+ diagnostics,
108
+ config,
109
+ project,
110
+ compiler,
111
+ }: {
112
+ fs: FileSystem
113
+ diagnostics: Diagnostics
114
+ config: NowConfig
115
+ project: Project
116
+ compiler: Compiler
117
+ }
118
+ ): string | undefined {
119
+ const path = file.getFilePath()
120
+ if (!path.endsWith('.ts')) {
121
+ return file.getFullText()
122
+ }
123
+
124
+ const tsConfigPath = config.tsconfigPath ? project.resolvePath(config.tsconfigPath) : undefined
125
+ return compiler.compileModule(file, fs, diagnostics, tsConfigPath)
126
+ }
127
+
128
+ function getFunctionParameters(source: Source): string[] {
129
+ if (!(source instanceof ts.VariableDeclaration || source instanceof ts.FunctionDeclaration)) {
130
+ return []
131
+ }
132
+ const node = source as ts.VariableDeclaration | ts.VariableDeclaration
133
+
134
+ let functionNode: ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction | undefined
135
+ if (ts.Node.isFunctionDeclaration(node)) {
136
+ functionNode = node
137
+ } else if (ts.Node.isVariableDeclaration(node) && node.getInitializerIfKind(ts.SyntaxKind.FunctionExpression)) {
138
+ functionNode = node.getInitializerIfKindOrThrow(ts.SyntaxKind.FunctionExpression)
139
+ } else if (ts.Node.isVariableDeclaration(node) && node.getInitializerIfKind(ts.SyntaxKind.ArrowFunction)) {
140
+ functionNode = node.getInitializerIfKindOrThrow(ts.SyntaxKind.ArrowFunction)
141
+ } else {
142
+ return []
143
+ }
144
+
145
+ return functionNode.getParameters().map((param) => param.getName()) ?? []
146
+ }
147
+
148
+ async function parseDeclaration(
149
+ node: ts.FunctionDeclaration | ts.VariableDeclaration,
150
+ { transform }: { transform: Transform }
151
+ ): Promise<Result<ModuleFunctionShape>> {
152
+ const result = await transform.toRecord(node.getSourceFile())
153
+ if (!result.success) {
154
+ return result
155
+ }
156
+
157
+ const record = result.value
158
+ if (record.getTable() !== 'sys_module') {
159
+ return { success: false }
160
+ }
161
+
162
+ return {
163
+ success: true,
164
+ value: new ModuleFunctionShape(node, {
165
+ name: node.getName() ?? 'functionModule',
166
+ path: record.get('path').asString().getValue(),
167
+ isDefault: node.isDefaultExport(),
168
+ }),
169
+ }
170
+ }
171
+
172
+ const dependencyIgnoreList = ['@servicenow/glide', '@servicenow/sdk']
173
+ function isIgnoredDependency(name: string): boolean {
174
+ return dependencyIgnoreList.some((dependency) => name.startsWith(dependency))
175
+ }
176
+
177
+ function validateAndGetModuleSpecifier(name: string): { name: string; entry?: string } {
178
+ const scopedRegex = /@[a-z\d][\w\-.]+\/[a-z\d][\w\-.]*/gi
179
+ scopedRegex.test(name)
180
+ const idx = scopedRegex.lastIndex
181
+
182
+ const isSubPathImport = name.indexOf('/', idx)
183
+ if (isSubPathImport > 0) {
184
+ return { name: name.slice(0, isSubPathImport), entry: name.slice(isSubPathImport + 1) }
185
+ }
186
+ return { name }
187
+ }
188
+
189
+ function isValidRequireCall(callExpression: ts.CallExpression, requirePath: ts.StringLiteral): boolean {
190
+ const expression = callExpression.getExpression()
191
+ const isRequire = ts.Node.isIdentifier(expression) && expression.getText() === 'require'
192
+ const isRelativePath =
193
+ requirePath.getLiteralText().startsWith('../') || requirePath.getLiteralText().startsWith('./')
194
+ return isRequire && !isRelativePath
195
+ }
196
+
197
+ class ModuleDependencyShape extends Shape {
198
+ private readonly moduleName: string
199
+
200
+ constructor({
201
+ node,
202
+ moduleName,
203
+ }: {
204
+ node: ts.ImportDeclaration | ts.ExportDeclaration | ts.CallExpression
205
+ moduleName: string
206
+ }) {
207
+ super({ source: node })
208
+ this.moduleName = moduleName
209
+ }
210
+
211
+ getModuleName(): string {
212
+ return this.moduleName
213
+ }
214
+ }
215
+
216
+ // TODO: Need to have some invalidation mechanism. Maybe the plugin framework can provide plugins with a managed cache to use for stuff like this?
217
+ const DEPENDENCY_CACHE: globalThis.Record<string, Result<ModuleDependencyShape>> = {}
218
+
219
+ function parseModuleDependency(
220
+ node: ts.ImportDeclaration | ts.ExportDeclaration | ts.CallExpression
221
+ ): Result<ModuleDependencyShape> {
222
+ let moduleName: string | undefined
223
+ if (ts.Node.isImportDeclaration(node) && !node.isModuleSpecifierRelative()) {
224
+ moduleName = node.getModuleSpecifierValue()
225
+ } else if (ts.Node.isExportDeclaration(node) && node.hasModuleSpecifier() && !node.isModuleSpecifierRelative()) {
226
+ moduleName = node.getModuleSpecifierValue()
227
+ } else if (ts.Node.isCallExpression(node)) {
228
+ const args = node.getArguments()
229
+ const requirePath = args[0]
230
+ if (ts.Node.isStringLiteral(requirePath) && isValidRequireCall(node, requirePath)) {
231
+ moduleName = requirePath.getLiteralValue()
232
+ }
233
+ }
234
+
235
+ if (!moduleName || isIgnoredDependency(moduleName)) {
236
+ return { success: false }
237
+ }
238
+
239
+ const cached = DEPENDENCY_CACHE[moduleName]
240
+ if (cached && (!cached.success || !cached.value.getOriginalNode().wasForgotten())) {
241
+ return cached
242
+ }
243
+
244
+ return (DEPENDENCY_CACHE[moduleName] = {
245
+ success: true,
246
+ value: new ModuleDependencyShape({ node, moduleName }),
247
+ })
248
+ }
249
+
250
+ function generateSBOMContent(context: RecordContext) {
251
+ const { database } = context
252
+ const moduleRecords = database.query('sys_module', { external_source: true })
253
+ const sbomBuilder = new SBOMBuilder()
254
+ for (const mod of moduleRecords) {
255
+ const modulePath = mod.get('path').asString().getValue()
256
+ if (modulePath.endsWith('/package.json')) {
257
+ const moduleContent = mod.get('content').asString().getValue()
258
+ sbomBuilder.addPackageJson(modulePath, moduleContent)
259
+ }
260
+ }
261
+ return sbomBuilder.generateSBOM(context)
262
+ }
263
+
264
+ export const ServerModulePlugin = Plugin.create({
265
+ name: 'ServerModulePlugin',
266
+ files: [
267
+ {
268
+ entryPoint: true,
269
+ matcher: /[/\\]package\.json$/,
270
+ },
271
+ {
272
+ entryPoint: true,
273
+ matcher: (path, { config, project }) =>
274
+ !path.endsWith('.test.ts') &&
275
+ !path.endsWith('.test.js') &&
276
+ project.isInDir(config.serverModulesDir, path),
277
+ },
278
+ ],
279
+ records: {
280
+ sys_module: {
281
+ toFile(record, context) {
282
+ const { config } = context
283
+ // If the record is not the sbom, we defer to using the record plugin
284
+ if (!record.get('path').ifString()?.endsWith('/bom.json')) {
285
+ return {
286
+ success: false,
287
+ }
288
+ }
289
+ const sbomContent = generateSBOMContent(context)
290
+
291
+ return {
292
+ success: true,
293
+ value: {
294
+ name: `sys_module_${record.getId().getValue()}.xml`,
295
+ category: record.getInstallCategory(),
296
+ content: `<?xml version="1.0"?>
297
+ <record_update>
298
+ <sys_module action="INSERT_OR_UPDATE">
299
+ <sys_id>${record.getId().getValue()}</sys_id>
300
+ <sys_scope display_value="${config.scope}">${config.scopeId}</sys_scope>
301
+ <path>${record.get('path').getValue()}</path>
302
+ <external_source>${record.get('external_source').getValue()}</external_source>
303
+ <content><![CDATA[${StringShape.escapeCdataTags(sanitizeModuleContent(sbomContent))}]]></content>
304
+ </sys_module>
305
+ </record_update>`,
306
+ },
307
+ }
308
+ },
309
+ toShape(record) {
310
+ return { success: true, value: Shape.noOp(record) }
311
+ },
312
+ },
313
+ },
314
+ shapes: [
315
+ {
316
+ shape: SourceFileShape,
317
+ fileTypes: ['module'],
318
+ toRecord(file, { factory, fs, diagnostics, project, config, packageJson, compiler }) {
319
+ const path = file.getPath()
320
+ if (!path.startsWith(project.resolvePath(config.serverModulesDir))) {
321
+ return { success: false }
322
+ }
323
+
324
+ const mappedPath = applyPathMappings(path, config.modulePaths)
325
+ const resolvedPath = project.resolvePath(mappedPath)
326
+ const mappedFile =
327
+ compiler.getSourceFile(resolvedPath) ?? compiler.addSourceFileAtPathIfExists(resolvedPath)
328
+
329
+ if (!mappedFile) {
330
+ diagnostics.error(
331
+ file,
332
+ `Module path was mapped from '${path}' to '${resolvedPath}' but no file exists at the mapped location.`
333
+ )
334
+ return { success: false }
335
+ }
336
+
337
+ const content = getEmitOutput(mappedFile, { fs, diagnostics, config, project, compiler })
338
+ if (!content) {
339
+ return { success: false }
340
+ }
341
+
342
+ const relativePath = pathModule.relative(project.getRootDir(), resolvedPath)
343
+ const sysModulePath = pathModule.join(config.scope, packageJson.name, packageJson.version, relativePath)
344
+
345
+ return {
346
+ success: true,
347
+ value: factory.createRecord({
348
+ source: file,
349
+ table: 'sys_module',
350
+ explicitId: relativePath.replaceAll(/[./\\]/g, '_'),
351
+ properties: {
352
+ // Module resolution at runtime requires this format
353
+ path: sysModulePath,
354
+ content: Shape.from(file, sanitizeModuleContent(content))
355
+ .asString()
356
+ .withContentType('cdata'),
357
+ external_source: false,
358
+ sys_name: sysModulePath,
359
+ },
360
+ }),
361
+ }
362
+ },
363
+ },
364
+ {
365
+ shape: ModuleDependencyShape,
366
+ fileTypes: ['module'],
367
+ // TODO: When managed cache is provided to plugins, cache dependencies that were already handled to avoid reprocessing
368
+ async toRecord(shape, { packageJson, diagnostics, fs, logger, project, factory, config }) {
369
+ const dependencies = packageJson.dependencies ?? {}
370
+ const { name, entry } = validateAndGetModuleSpecifier(shape.getModuleName())
371
+
372
+ const version = dependencies[name]
373
+ if (!version) {
374
+ diagnostics.error(shape, `Dependency ${name} is not found in package.json`)
375
+ return { success: false }
376
+ }
377
+
378
+ const id = `${name}@${version}`
379
+ const repack = await RepackService.create(logger, fs, project.getRootDir())
380
+ const dependencyNodes = await repack.execute({
381
+ id,
382
+ entry: entry ? [entry] : ['.'],
383
+ })
384
+
385
+ if (!dependencyNodes) {
386
+ throw new Error(`Failed to build dependency ${id}`)
387
+ }
388
+
389
+ const modules: { id: string; path: string; content: string }[] = []
390
+ const { Lint } = await import('../repack/lint/index.js')
391
+ for (const node of dependencyNodes) {
392
+ const { packagePath, files, updatedManifest } = node
393
+ const { name, version } = updatedManifest
394
+
395
+ for (const file of files) {
396
+ const fileContent = fs.readFileSync(pathModule.join(packagePath, file)).toString('utf-8')
397
+ if (/(.js|.cjs|.mjs)$/.test(pathModule.extname(file))) {
398
+ const result = await new Lint().check(fileContent)
399
+ if (result) {
400
+ logger.warn(`Use of unsupported APIs detected in npm dependency ${name}`)
401
+ logger.warn(result)
402
+ }
403
+ }
404
+
405
+ modules.push({
406
+ id: `${name}@${version}/${file}`,
407
+ path: pathModule.join(config.scope, name, version, file),
408
+ content: fileContent,
409
+ })
410
+ }
411
+ }
412
+
413
+ const [first, ...rest] = modules.map((m) => {
414
+ return factory.createRecord({
415
+ source: shape,
416
+ table: 'sys_module',
417
+ explicitId: m.id,
418
+ properties: {
419
+ path: m.path,
420
+ content: Shape.from(shape, sanitizeModuleContent(m.content))
421
+ .asString()
422
+ .withContentType('cdata'),
423
+ external_source: true,
424
+ sys_name: m.path,
425
+ },
426
+ })
427
+ })
428
+
429
+ if (!first) {
430
+ return { success: false }
431
+ }
432
+
433
+ return {
434
+ success: true,
435
+ value: first.with(...rest),
436
+ }
437
+ },
438
+ },
439
+ ],
440
+ nodes: [
441
+ { node: 'CallExpression', fileTypes: ['module'], entryPoint: true, toShape: parseModuleDependency },
442
+ { node: 'ImportDeclaration', fileTypes: ['module'], entryPoint: true, toShape: parseModuleDependency },
443
+ { node: 'ExportDeclaration', fileTypes: ['module'], entryPoint: true, toShape: parseModuleDependency },
444
+ { node: 'FunctionDeclaration', fileTypes: ['module'], toShape: parseDeclaration },
445
+ {
446
+ node: 'FunctionExpression',
447
+ fileTypes: ['module'],
448
+ toShape(node, context) {
449
+ const parent = node.getParentIfKind(ts.SyntaxKind.VariableDeclaration)
450
+ if (!parent) {
451
+ return { success: false }
452
+ }
453
+
454
+ return parseDeclaration(parent, context)
455
+ },
456
+ },
457
+ {
458
+ node: 'ArrowFunction',
459
+ fileTypes: ['module'],
460
+ toShape(node, context) {
461
+ const parent = node.getParentIfKind(ts.SyntaxKind.VariableDeclaration)
462
+ if (!parent) {
463
+ return { success: false }
464
+ }
465
+
466
+ return parseDeclaration(parent, context)
467
+ },
468
+ },
469
+ ],
470
+ })
@@ -0,0 +1,183 @@
1
+ import { path, FileSystem } from '@servicenow/sdk-build-core'
2
+ import type { RecordContext } from '@servicenow/sdk-build-core'
3
+ import { JsonSerializer, JSON as CycloneDXJSON } from '@cyclonedx/cyclonedx-library/Serialize'
4
+ import { ExternalReference, type Metadata } from '@cyclonedx/cyclonedx-library/Models'
5
+ import { ExternalReferenceType } from '@cyclonedx/cyclonedx-library/Enums'
6
+ import * as CDXUtils from '@cyclonedx/cyclonedx-library/Utils'
7
+ import * as Builders from '@cyclonedx/cyclonedx-library/Builders'
8
+ import * as Enums from '@cyclonedx/cyclonedx-library/Enums'
9
+ import * as Factories from '@cyclonedx/cyclonedx-library/Factories'
10
+ import * as Models from '@cyclonedx/cyclonedx-library/Models'
11
+ import * as Spec from '@cyclonedx/cyclonedx-library/Spec'
12
+ import { PackageURL } from 'packageurl-js'
13
+
14
+ export class SBOMBuilder {
15
+ /**
16
+ * The set of package.json files that are used to generate the SBOM.
17
+ * @private
18
+ */
19
+ private packageJsonFiles: Map<string, string> = new Map()
20
+
21
+ /**
22
+ * The package.json of the SDK build plugins. This is used to generate the tool component in the SBOM.
23
+ */
24
+ private toolPkgJsonDefault = {
25
+ name: '@servicenow/sdk-build-plugins',
26
+ version: '0.0.1',
27
+ author: 'ServiceNow',
28
+ }
29
+
30
+ /**
31
+ * Add a package.json file to the set of package.json files that are used to generate the SBOM for the application.
32
+ * @param file the path to the package.json file
33
+ */
34
+ public addPackageJson(file: string, content: string) {
35
+ //We don't care about the package.json path but it least keeps the content unique
36
+ this.packageJsonFiles.set(file, content)
37
+ }
38
+
39
+ /**
40
+ * Generate a CycloneDX SBOM for the application.
41
+ * @param context the build context
42
+ */
43
+ public generateSBOM(context: RecordContext): string {
44
+ const cdxExternalReferenceFactory = new Factories.FromNodePackageJson.ExternalReferenceFactory()
45
+ const cdxLicenseFactory = new Factories.LicenseFactory()
46
+ const cdxPurlFactory = new Factories.FromNodePackageJson.PackageUrlFactory('npm')
47
+ const cdxComponentBuilder = new Builders.FromNodePackageJson.ComponentBuilder(
48
+ cdxExternalReferenceFactory,
49
+ cdxLicenseFactory
50
+ )
51
+
52
+ const jsonSerializer = new JsonSerializer(new CycloneDXJSON.Normalize.Factory(Spec.Spec1dot6))
53
+ const sdkJson = this.getSdkInfo(context)
54
+ const toolComponent = cdxComponentBuilder.makeComponent(sdkJson, Enums.ComponentType.Library)
55
+
56
+ const rootComponent = cdxComponentBuilder.makeComponent(context.packageJson, Enums.ComponentType.Application)
57
+
58
+ let qualifiers: Record<string, string> | undefined
59
+ if (context.config.scopeId) {
60
+ qualifiers = {
61
+ sys_scope_id: context.config.scopeId,
62
+ }
63
+ }
64
+ const purl = new PackageURL(
65
+ 'servicenow',
66
+ undefined,
67
+ context.packageJson.name,
68
+ context.packageJson.version,
69
+ qualifiers,
70
+ undefined
71
+ )
72
+ if (rootComponent && purl) {
73
+ rootComponent.purl = purl
74
+ rootComponent.bomRef.value = purl.toString()
75
+ }
76
+ const metadata = new Models.Metadata({
77
+ timestamp: new Date(),
78
+ component: rootComponent,
79
+ })
80
+ toolComponent && metadata.tools.components.add(toolComponent)
81
+ this.addExternalReferences(metadata)
82
+
83
+ const bom = new Models.Bom({
84
+ metadata,
85
+ serialNumber: CDXUtils.BomUtility.randomSerialNumber(),
86
+ })
87
+
88
+ for (const [, content] of this.packageJsonFiles.entries()) {
89
+ const data = content
90
+ const json = JSON.parse(data)
91
+ if (json) {
92
+ const { name, version } = json
93
+ if (!(context.packageJson.name === name && context.packageJson.version === version)) {
94
+ const component = cdxComponentBuilder.makeComponent(json, Enums.ComponentType.Application)
95
+ if (component) {
96
+ this.registerPackageUrlOnComponent(component, cdxPurlFactory)
97
+ bom.components.add(component)
98
+ rootComponent?.dependencies.add(component.bomRef)
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ const nowDir: string = path.resolve(context.project.getRootDir(), '.now')
105
+ FileSystem.ensureDirSync(context.fs, nowDir)
106
+ const bomPath = path.resolve(nowDir, 'bom.json')
107
+ const bomContent: string = jsonSerializer.serialize(bom, { space: 2, sortLists: true })
108
+ context.fs.writeFileSync(bomPath, bomContent)
109
+
110
+ return bomContent
111
+ }
112
+
113
+ /**
114
+ * Helper method to get the JSON needed to build the tool component for the @servicenow/sdk-build-plugins package.
115
+ * @param context the build context
116
+ * @private
117
+ */
118
+ public getSdkInfo(context: RecordContext): { name: string; version: string; author: string } {
119
+ try {
120
+ //TODO can we retrieve the package.json's name and version from the context for the sdk-build-plugins package?
121
+ const packageJsonPath = path.join(
122
+ context.project.getRootDir(),
123
+ 'node_modules',
124
+ '@servicenow/sdk-build-plugins',
125
+ 'package.json'
126
+ )
127
+ const packageJson = JSON.parse(context.fs.readFileSync(packageJsonPath, { encoding: 'utf-8' }))
128
+ return {
129
+ name: packageJson.name,
130
+ version: packageJson.version,
131
+ author: 'ServiceNow',
132
+ }
133
+ } catch (error) {
134
+ // ignore
135
+ }
136
+ return this.toolPkgJsonDefault
137
+ }
138
+
139
+ /**
140
+ * Helper method to generate a package url from a component and register it on the component. No idea why the base
141
+ * component builder doesn't do this automatically.
142
+ * @param component the component to register the package url on
143
+ * @param purlFactory the package url factory
144
+ */
145
+ private registerPackageUrlOnComponent(
146
+ component: Models.Component | undefined,
147
+ purlFactory: Factories.FromNodePackageJson.PackageUrlFactory
148
+ ) {
149
+ if (component) {
150
+ const purl = purlFactory.makeFromComponent(component)
151
+ if (purl) {
152
+ component.purl = purl
153
+ component.bomRef.value = purl.toString()
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Add external references to the root component that are retrieved from common Jenkins environment variables.
160
+ * @param metadata the bom metadata reference
161
+ * @private
162
+ */
163
+ private addExternalReferences(metadata: Metadata) {
164
+ const buildUrl = process.env['BUILD_URL']
165
+ if (buildUrl) {
166
+ const buildSystemExRef = new ExternalReference(buildUrl, ExternalReferenceType.BuildSystem, {
167
+ comment: 'As obtained from the environment variable BUILD_URL',
168
+ })
169
+ if (metadata.component && metadata.component.externalReferences) {
170
+ metadata.component.externalReferences.add(buildSystemExRef)
171
+ }
172
+ }
173
+ const vcsUrl = process.env['GIT_URL']
174
+ if (vcsUrl) {
175
+ const vcsExRef = new ExternalReference(vcsUrl, ExternalReferenceType.VCS, {
176
+ comment: 'As obtained from the environment variable GIT_URL',
177
+ })
178
+ if (metadata.component && metadata.component.externalReferences) {
179
+ metadata.component.externalReferences.add(vcsExRef)
180
+ }
181
+ }
182
+ }
183
+ }