biome-plugin-suitescript 0.2.0 → 0.4.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.
Files changed (69) hide show
  1. package/README.md +154 -78
  2. package/biome-manifest.json +179 -17
  3. package/configs/all.json +29 -0
  4. package/configs/minimal.json +14 -0
  5. package/configs/recommended.json +20 -0
  6. package/dist/lib/define.js +35 -5
  7. package/dist/lib/headers.js +12 -0
  8. package/dist/lib/module-calls.js +65 -0
  9. package/dist/lib/module-script-types.js +7 -1
  10. package/dist/lib/options.js +35 -0
  11. package/dist/lib/required-options.js +14 -0
  12. package/dist/lib/script-types.js +25 -14
  13. package/dist/lib/suiteql.js +16 -0
  14. package/dist/lib/typings-data.js +4327 -0
  15. package/dist/lib/typings.js +2 -0
  16. package/dist/rules/logRequireDetails.js +4 -0
  17. package/dist/rules/logRequireTitle.js +1 -0
  18. package/dist/rules/noAuthorizationHeaderInInternalRequest.js +24 -0
  19. package/dist/rules/noBlockedHttpsHeader.js +32 -0
  20. package/dist/rules/noBlockedResponseHeader.js +35 -0
  21. package/dist/rules/noBrowserGlobals.js +2 -0
  22. package/dist/rules/noConflictingClientScriptProps.js +36 -0
  23. package/dist/rules/noDynamicRequire.js +23 -0
  24. package/dist/rules/noDynamicSuiteQL.js +11 -6
  25. package/dist/rules/noExtraDeps.js +16 -0
  26. package/dist/rules/noExtraParams.js +17 -0
  27. package/dist/rules/noGlobalShadowing.js +6 -2
  28. package/dist/rules/noHardcodedAuthPasswords.js +27 -0
  29. package/dist/rules/noHardcodedSecrets.js +7 -2
  30. package/dist/rules/noInnerHtml.js +41 -0
  31. package/dist/rules/noInsecureCookie.js +27 -0
  32. package/dist/rules/noInvalidDefine.js +47 -0
  33. package/dist/rules/noInvalidEnumMember.js +24 -0
  34. package/dist/rules/noLocationHeader.js +20 -0
  35. package/dist/rules/noLogModule.js +8 -5
  36. package/dist/rules/noMissingModules.js +6 -3
  37. package/dist/rules/noModuleScopeApiCalls.js +14 -4
  38. package/dist/rules/noMultipleDefine.js +14 -0
  39. package/dist/rules/noNativeIterators.js +8 -5
  40. package/dist/rules/noPositionalOptions.js +26 -0
  41. package/dist/rules/noPostMessageWildcard.js +15 -0
  42. package/dist/rules/noPublicFileUpload.js +20 -0
  43. package/dist/rules/noRecordInLoop.js +8 -5
  44. package/dist/rules/noRequireInsteadOfDefine.js +25 -0
  45. package/dist/rules/noSecretKeyGuidAndSecret.js +16 -0
  46. package/dist/rules/noSensitiveBrowserStorage.js +24 -0
  47. package/dist/rules/noStackInResponse.js +21 -0
  48. package/dist/rules/noSuiteQLSelectStar.js +18 -0
  49. package/dist/rules/noSuiteQLSystemNoteJoin.js +19 -0
  50. package/dist/rules/noSuiteQLTypeInSearch.js +41 -0
  51. package/dist/rules/noSuiteScript1Api.js +77 -3
  52. package/dist/rules/noUnknownModuleMember.js +23 -0
  53. package/dist/rules/noUnknownOptions.js +31 -0
  54. package/dist/rules/noUnsupportedServerWidgetFieldType.js +40 -0
  55. package/dist/rules/noWeakHash.js +17 -0
  56. package/dist/rules/noWildcardCors.js +16 -0
  57. package/dist/rules/noWrongScriptTypeModules.js +3 -1
  58. package/dist/rules/requireCustpagePrefix.js +14 -13
  59. package/dist/rules/requireEachReturn.js +38 -0
  60. package/dist/rules/requireLogModule.js +25 -0
  61. package/dist/rules/requireOptions.js +26 -0
  62. package/dist/rules/requireOrderByForPagedSuiteQL.js +17 -0
  63. package/dist/rules/requireQueryOptions.js +53 -0
  64. package/dist/rules/requireServerResponseOptions.js +33 -0
  65. package/dist/rules/requireServerWidgetOptions.js +37 -0
  66. package/package.json +10 -1
  67. package/dist/rules/noEval.js +0 -21
  68. package/dist/rules/noExtraModules.js +0 -23
  69. package/dist/rules/noUserEventContextSave.js +0 -12
@@ -3,59 +3,122 @@
3
3
  "configs": [
4
4
  {
5
5
  "suitescript2": "./configs/suitescript2.json"
6
+ },
7
+ {
8
+ "minimal": "./configs/minimal.json"
9
+ },
10
+ {
11
+ "recommended": "./configs/recommended.json"
12
+ },
13
+ {
14
+ "all": "./configs/all.json"
6
15
  }
7
16
  ],
8
17
  "plugins": {
9
18
  "presets": {
10
19
  "all": [
11
20
  "noAmdName",
12
- "noExtraModules",
21
+ "noExtraDeps",
22
+ "noExtraParams",
23
+ "noInvalidDefine",
13
24
  "noInvalidModules",
14
25
  "noModuleExtensions",
26
+ "noMultipleDefine",
27
+ "noRequireInsteadOfDefine",
28
+ "noDynamicRequire",
15
29
  "moduleVars",
16
30
  "logRequireTitle",
17
- "logRequireDetails",
31
+ "entryPoints",
18
32
  "noMissingModules",
19
33
  "noGlobalShadowing",
20
- "noEval",
21
34
  "noDynamicSuiteQL",
22
35
  "noSuiteScript1Api",
23
36
  "noModuleScopeApiCalls",
37
+ "noNativeIterators",
24
38
  "noWrongScriptTypeModules",
25
39
  "noBrowserGlobals",
26
- "noUserEventContextSave",
27
40
  "noInsecureHttpsUrl",
28
- "entryPoints",
29
- "noRecordInLoop",
30
- "noNativeIterators",
31
41
  "noBeforeSubmitRecordReload",
32
42
  "noEmptyEntryPoints",
33
- "noMapReduceSearchResults",
34
43
  "requireCustpagePrefix",
35
44
  "noHeaderUnderscores",
36
45
  "noQueryColumnFieldAndFormula",
37
46
  "requireClientValidateReturn",
47
+ "noPostMessageWildcard",
48
+ "noInnerHtml",
49
+ "noWildcardCors",
50
+ "noSuiteQLTypeInSearch",
51
+ "requireEachReturn",
52
+ "noAuthorizationHeaderInInternalRequest",
53
+ "noBlockedResponseHeader",
54
+ "noBlockedHttpsHeader",
55
+ "requireOptions",
56
+ "noPositionalOptions",
57
+ "noUnknownModuleMember",
58
+ "requireServerWidgetOptions",
59
+ "requireQueryOptions",
60
+ "requireServerResponseOptions",
61
+ "noConflictingClientScriptProps",
62
+ "noUnsupportedServerWidgetFieldType",
63
+ "noHardcodedAuthPasswords",
64
+ "noSecretKeyGuidAndSecret",
65
+ "requireOrderByForPagedSuiteQL",
66
+ "logRequireDetails",
67
+ "noUnknownOptions",
68
+ "noInvalidEnumMember",
69
+ "requireLogModule",
70
+ "noRecordInLoop",
71
+ "noMapReduceSearchResults",
38
72
  "noClientLog"
39
73
  ],
40
- "minimal": ["noExtraModules", "noInvalidModules"],
74
+ "minimal": ["noExtraDeps", "noExtraParams", "noInvalidModules"],
41
75
  "recommended": [
42
76
  "noAmdName",
43
- "noExtraModules",
77
+ "noExtraDeps",
78
+ "noExtraParams",
79
+ "noInvalidDefine",
44
80
  "noInvalidModules",
45
81
  "noModuleExtensions",
82
+ "noMultipleDefine",
83
+ "noRequireInsteadOfDefine",
84
+ "noDynamicRequire",
46
85
  "moduleVars",
47
86
  "logRequireTitle",
48
- "logRequireDetails",
87
+ "entryPoints",
49
88
  "noMissingModules",
50
89
  "noGlobalShadowing",
51
- "noEval",
52
90
  "noDynamicSuiteQL",
53
91
  "noSuiteScript1Api",
54
92
  "noModuleScopeApiCalls",
93
+ "noNativeIterators",
55
94
  "noWrongScriptTypeModules",
56
95
  "noBrowserGlobals",
57
- "noUserEventContextSave",
58
- "noInsecureHttpsUrl"
96
+ "noInsecureHttpsUrl",
97
+ "noBeforeSubmitRecordReload",
98
+ "noEmptyEntryPoints",
99
+ "requireCustpagePrefix",
100
+ "noHeaderUnderscores",
101
+ "noQueryColumnFieldAndFormula",
102
+ "requireClientValidateReturn",
103
+ "noPostMessageWildcard",
104
+ "noInnerHtml",
105
+ "noWildcardCors",
106
+ "noSuiteQLTypeInSearch",
107
+ "requireEachReturn",
108
+ "noAuthorizationHeaderInInternalRequest",
109
+ "noBlockedResponseHeader",
110
+ "noBlockedHttpsHeader",
111
+ "requireOptions",
112
+ "noPositionalOptions",
113
+ "noUnknownModuleMember",
114
+ "requireServerWidgetOptions",
115
+ "requireQueryOptions",
116
+ "requireServerResponseOptions",
117
+ "noConflictingClientScriptProps",
118
+ "noUnsupportedServerWidgetFieldType",
119
+ "noHardcodedAuthPasswords",
120
+ "noSecretKeyGuidAndSecret",
121
+ "requireOrderByForPagedSuiteQL"
59
122
  ]
60
123
  },
61
124
  "rules": [
@@ -74,15 +137,30 @@
74
137
  {
75
138
  "noAmdName": "./dist/rules/noAmdName.grit"
76
139
  },
140
+ {
141
+ "noAuthorizationHeaderInInternalRequest": "./dist/rules/noAuthorizationHeaderInInternalRequest.js"
142
+ },
77
143
  {
78
144
  "noBeforeSubmitRecordReload": "./dist/rules/noBeforeSubmitRecordReload.js"
79
145
  },
146
+ {
147
+ "noBlockedHttpsHeader": "./dist/rules/noBlockedHttpsHeader.js"
148
+ },
149
+ {
150
+ "noBlockedResponseHeader": "./dist/rules/noBlockedResponseHeader.js"
151
+ },
80
152
  {
81
153
  "noBrowserGlobals": "./dist/rules/noBrowserGlobals.js"
82
154
  },
83
155
  {
84
156
  "noClientLog": "./dist/rules/noClientLog.js"
85
157
  },
158
+ {
159
+ "noConflictingClientScriptProps": "./dist/rules/noConflictingClientScriptProps.js"
160
+ },
161
+ {
162
+ "noDynamicRequire": "./dist/rules/noDynamicRequire.js"
163
+ },
86
164
  {
87
165
  "noDynamicSuiteQL": "./dist/rules/noDynamicSuiteQL.js"
88
166
  },
@@ -90,26 +168,44 @@
90
168
  "noEmptyEntryPoints": "./dist/rules/noEmptyEntryPoints.js"
91
169
  },
92
170
  {
93
- "noEval": "./dist/rules/noEval.js"
171
+ "noExtraDeps": "./dist/rules/noExtraDeps.js"
94
172
  },
95
173
  {
96
- "noExtraModules": "./dist/rules/noExtraModules.js"
174
+ "noExtraParams": "./dist/rules/noExtraParams.js"
97
175
  },
98
176
  {
99
177
  "noGlobalShadowing": "./dist/rules/noGlobalShadowing.js"
100
178
  },
179
+ {
180
+ "noHardcodedAuthPasswords": "./dist/rules/noHardcodedAuthPasswords.js"
181
+ },
101
182
  {
102
183
  "noHardcodedSecrets": "./dist/rules/noHardcodedSecrets.js"
103
184
  },
104
185
  {
105
186
  "noHeaderUnderscores": "./dist/rules/noHeaderUnderscores.js"
106
187
  },
188
+ {
189
+ "noInnerHtml": "./dist/rules/noInnerHtml.js"
190
+ },
191
+ {
192
+ "noInsecureCookie": "./dist/rules/noInsecureCookie.js"
193
+ },
107
194
  {
108
195
  "noInsecureHttpsUrl": "./dist/rules/noInsecureHttpsUrl.js"
109
196
  },
197
+ {
198
+ "noInvalidDefine": "./dist/rules/noInvalidDefine.js"
199
+ },
200
+ {
201
+ "noInvalidEnumMember": "./dist/rules/noInvalidEnumMember.js"
202
+ },
110
203
  {
111
204
  "noInvalidModules": "./dist/rules/noInvalidModules.js"
112
205
  },
206
+ {
207
+ "noLocationHeader": "./dist/rules/noLocationHeader.js"
208
+ },
113
209
  {
114
210
  "noLogModule": "./dist/rules/noLogModule.js"
115
211
  },
@@ -125,20 +221,65 @@
125
221
  {
126
222
  "noModuleScopeApiCalls": "./dist/rules/noModuleScopeApiCalls.js"
127
223
  },
224
+ {
225
+ "noMultipleDefine": "./dist/rules/noMultipleDefine.js"
226
+ },
128
227
  {
129
228
  "noNativeIterators": "./dist/rules/noNativeIterators.js"
130
229
  },
230
+ {
231
+ "noPositionalOptions": "./dist/rules/noPositionalOptions.js"
232
+ },
233
+ {
234
+ "noPostMessageWildcard": "./dist/rules/noPostMessageWildcard.js"
235
+ },
236
+ {
237
+ "noPublicFileUpload": "./dist/rules/noPublicFileUpload.js"
238
+ },
131
239
  {
132
240
  "noQueryColumnFieldAndFormula": "./dist/rules/noQueryColumnFieldAndFormula.js"
133
241
  },
134
242
  {
135
243
  "noRecordInLoop": "./dist/rules/noRecordInLoop.js"
136
244
  },
245
+ {
246
+ "noRequireInsteadOfDefine": "./dist/rules/noRequireInsteadOfDefine.js"
247
+ },
248
+ {
249
+ "noSecretKeyGuidAndSecret": "./dist/rules/noSecretKeyGuidAndSecret.js"
250
+ },
251
+ {
252
+ "noSensitiveBrowserStorage": "./dist/rules/noSensitiveBrowserStorage.js"
253
+ },
254
+ {
255
+ "noStackInResponse": "./dist/rules/noStackInResponse.js"
256
+ },
257
+ {
258
+ "noSuiteQLSelectStar": "./dist/rules/noSuiteQLSelectStar.js"
259
+ },
260
+ {
261
+ "noSuiteQLSystemNoteJoin": "./dist/rules/noSuiteQLSystemNoteJoin.js"
262
+ },
263
+ {
264
+ "noSuiteQLTypeInSearch": "./dist/rules/noSuiteQLTypeInSearch.js"
265
+ },
137
266
  {
138
267
  "noSuiteScript1Api": "./dist/rules/noSuiteScript1Api.js"
139
268
  },
140
269
  {
141
- "noUserEventContextSave": "./dist/rules/noUserEventContextSave.js"
270
+ "noUnknownModuleMember": "./dist/rules/noUnknownModuleMember.js"
271
+ },
272
+ {
273
+ "noUnknownOptions": "./dist/rules/noUnknownOptions.js"
274
+ },
275
+ {
276
+ "noUnsupportedServerWidgetFieldType": "./dist/rules/noUnsupportedServerWidgetFieldType.js"
277
+ },
278
+ {
279
+ "noWeakHash": "./dist/rules/noWeakHash.js"
280
+ },
281
+ {
282
+ "noWildcardCors": "./dist/rules/noWildcardCors.js"
142
283
  },
143
284
  {
144
285
  "noWrongScriptTypeModules": "./dist/rules/noWrongScriptTypeModules.js"
@@ -148,6 +289,27 @@
148
289
  },
149
290
  {
150
291
  "requireCustpagePrefix": "./dist/rules/requireCustpagePrefix.js"
292
+ },
293
+ {
294
+ "requireEachReturn": "./dist/rules/requireEachReturn.js"
295
+ },
296
+ {
297
+ "requireLogModule": "./dist/rules/requireLogModule.js"
298
+ },
299
+ {
300
+ "requireOptions": "./dist/rules/requireOptions.js"
301
+ },
302
+ {
303
+ "requireOrderByForPagedSuiteQL": "./dist/rules/requireOrderByForPagedSuiteQL.js"
304
+ },
305
+ {
306
+ "requireQueryOptions": "./dist/rules/requireQueryOptions.js"
307
+ },
308
+ {
309
+ "requireServerResponseOptions": "./dist/rules/requireServerResponseOptions.js"
310
+ },
311
+ {
312
+ "requireServerWidgetOptions": "./dist/rules/requireServerWidgetOptions.js"
151
313
  }
152
314
  ]
153
315
  },
@@ -0,0 +1,29 @@
1
+ {
2
+ "javascript": {
3
+ "globals": ["define", "require", "log", "util"]
4
+ },
5
+ "linter": {
6
+ "rules": {
7
+ "correctness": {
8
+ "noUnusedFunctionParameters": "off"
9
+ },
10
+ "nursery": {
11
+ "noImpliedEval": "error",
12
+ "useIncludes": "warn"
13
+ },
14
+ "preset": "recommended",
15
+ "security": {
16
+ "noGlobalEval": "error"
17
+ },
18
+ "style": {
19
+ "useConst": "warn",
20
+ "useForOf": "warn",
21
+ "useTemplate": "warn"
22
+ },
23
+ "suspicious": {
24
+ "noVar": "warn"
25
+ }
26
+ }
27
+ },
28
+ "plugins": ["biome-plugin-suitescript/presets/all"]
29
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "javascript": {
3
+ "globals": ["define", "require", "log", "util"]
4
+ },
5
+ "linter": {
6
+ "rules": {
7
+ "correctness": {
8
+ "noUnusedFunctionParameters": "off"
9
+ },
10
+ "preset": "recommended"
11
+ }
12
+ },
13
+ "plugins": ["biome-plugin-suitescript/presets/minimal"]
14
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "javascript": {
3
+ "globals": ["define", "require", "log", "util"]
4
+ },
5
+ "linter": {
6
+ "rules": {
7
+ "correctness": {
8
+ "noUnusedFunctionParameters": "off"
9
+ },
10
+ "nursery": {
11
+ "noImpliedEval": "error"
12
+ },
13
+ "preset": "recommended",
14
+ "security": {
15
+ "noGlobalEval": "error"
16
+ }
17
+ }
18
+ },
19
+ "plugins": ["biome-plugin-suitescript/presets/recommended"]
20
+ }
@@ -8,11 +8,38 @@ export function stringValue(node) {
8
8
  return undefined;
9
9
  return node.text.slice(1, -1);
10
10
  }
11
+ /**
12
+ * The array literal a variable is initialised with, for `var dependencies = [...]`
13
+ * followed by `define(dependencies, ...)`. The first matching declaration in the
14
+ * file wins; a variable that is not an array literal is not resolved.
15
+ */
16
+ function declaredArray(from, name) {
17
+ const root = [...from.ancestors()].at(-1);
18
+ const visit = (node) => {
19
+ if (node.kind === "JS_VARIABLE_DECLARATOR") {
20
+ const children = node.children();
21
+ const id = children.find((c) => c.kind === "JS_IDENTIFIER_BINDING");
22
+ const init = children.find((c) => c.kind === "JS_INITIALIZER_CLAUSE");
23
+ const value = init?.children()[0];
24
+ if (id?.text === name && value?.kind === "JS_ARRAY_EXPRESSION") {
25
+ return value;
26
+ }
27
+ }
28
+ for (const child of node.children()) {
29
+ const found = visit(child);
30
+ if (found)
31
+ return found;
32
+ }
33
+ return undefined;
34
+ };
35
+ return root ? visit(root) : undefined;
36
+ }
11
37
  /**
12
38
  * Parses an AMD `define([...], function (...) {})` call.
13
39
  *
14
- * Returns undefined unless `call` is `define(...)` with a dependency array
15
- * followed by a function callback (the optional leading module id is allowed).
40
+ * Returns undefined unless `call` is `define(...)` with a dependency array (a
41
+ * literal, or a variable initialised with one) followed by a function callback (the
42
+ * optional leading module id is allowed).
16
43
  */
17
44
  export function parseDefine(call) {
18
45
  if (call.callee?.text !== "define")
@@ -20,11 +47,14 @@ export function parseDefine(call) {
20
47
  const args = call.arguments?.children()[0]?.children() ?? [];
21
48
  if (args.length < 2 || args.length > 3)
22
49
  return undefined;
23
- const deps = args[args.length - 2];
50
+ const given = args[args.length - 2];
24
51
  const callback = args[args.length - 1];
25
- if (!deps || !callback)
52
+ if (!given || !callback)
26
53
  return undefined;
27
- if (deps.kind !== "JS_ARRAY_EXPRESSION")
54
+ const deps = given.kind === "JS_IDENTIFIER_EXPRESSION"
55
+ ? declaredArray(call, given.text)
56
+ : given;
57
+ if (deps?.kind !== "JS_ARRAY_EXPRESSION")
28
58
  return undefined;
29
59
  if (!FUNCTION_KINDS.includes(callback.kind))
30
60
  return undefined;
@@ -0,0 +1,12 @@
1
+ import { optionsObject, propertyValue, stringValue } from "./options.js";
2
+ /** The header name and value nodes of `x.setHeader({ name, value })` or `addHeader`. */
3
+ export function headerCall(call) {
4
+ if (!/\.(setHeader|addHeader)$/.test(call.callee?.text ?? ""))
5
+ return undefined;
6
+ const options = optionsObject(call);
7
+ const nameNode = options && propertyValue(options, "name");
8
+ const name = stringValue(nameNode)?.toLowerCase();
9
+ if (!options || !nameNode || !name)
10
+ return undefined;
11
+ return { name, nameNode, value: propertyValue(options, "value") };
12
+ }
@@ -0,0 +1,65 @@
1
+ import { dependencies } from "./biome-modules.js";
2
+ import { parseDefine, stringValue } from "./define.js";
3
+ import { isFunction } from "./script-side.js";
4
+ import { TYPINGS } from "./typings-data.js";
5
+ const has = (object, key) => Object.hasOwn(object, key);
6
+ /**
7
+ * The N/ module a receiver expression (`record` in `record.load`) is bound to by a
8
+ * define() callback parameter or an import. A local variable or parameter of the
9
+ * same name shadows it, and a global resolves to nothing.
10
+ */
11
+ export function moduleOfReceiver(receiver, model) {
12
+ if (receiver.kind !== "JS_IDENTIFIER_EXPRESSION")
13
+ return undefined;
14
+ const reference = receiver.children()[0];
15
+ if (reference?.kind !== "JS_REFERENCE_IDENTIFIER")
16
+ return undefined;
17
+ const declaration = model.binding(reference)?.syntax();
18
+ if (!declaration)
19
+ return undefined;
20
+ const nearestFunction = declaration.ancestors().find(isFunction);
21
+ for (const ancestor of declaration.ancestors()) {
22
+ if (ancestor.kind === "JS_IMPORT") {
23
+ return dependencies(ancestor).find((d) => d.param?.text === declaration.text)?.name;
24
+ }
25
+ if (ancestor.kind === "JS_CALL_EXPRESSION") {
26
+ const def = parseDefine(ancestor);
27
+ if (def && nearestFunction?.text === def.callback.text) {
28
+ const index = def.params.findIndex((p) => p?.text === declaration.text);
29
+ return index >= 0 ? stringValue(def.elements[index]) : undefined;
30
+ }
31
+ }
32
+ }
33
+ return undefined;
34
+ }
35
+ /** The generated data for a module, if there is any. */
36
+ export function typingsOf(module) {
37
+ return module && has(TYPINGS, module) ? TYPINGS[module] : undefined;
38
+ }
39
+ /** A call to a function of a loaded N/ module that the type definitions describe. */
40
+ export function moduleCall(call, model) {
41
+ let callee = call.callee;
42
+ if (callee?.kind !== "JS_STATIC_MEMBER_EXPRESSION")
43
+ return undefined;
44
+ if (callee.children()[1]?.text === "promise") {
45
+ const inner = callee.children()[0];
46
+ if (inner?.kind !== "JS_STATIC_MEMBER_EXPRESSION")
47
+ return undefined;
48
+ callee = inner;
49
+ }
50
+ const [object, member] = callee.children();
51
+ if (!object || !member)
52
+ return undefined;
53
+ const module = moduleOfReceiver(object, model);
54
+ const typings = typingsOf(module);
55
+ if (!module || !typings || !has(typings.functions, member.text)) {
56
+ return undefined;
57
+ }
58
+ return {
59
+ alternatives: typings.functions[member.text],
60
+ callee: callee.text,
61
+ fn: member.text,
62
+ module,
63
+ typings,
64
+ };
65
+ }
@@ -1,12 +1,18 @@
1
1
  // Which side of NetSuite a script type runs on, and the N/ modules that only
2
2
  // work on one side. From the "Supported Script Types" column of Oracle's SuiteScript
3
3
  // module reference; loading a module on the wrong side fails at runtime.
4
+ // N/ui/message is not listed although the table marks it client-only. No agent
5
+ // skill requires it to be client-only: the SAFE guide's complete Suitelet sample
6
+ // (references/appendices/appendix-ncache-sample.md) loads it and passes
7
+ // message.Type to Form.addPageInitMessage. The type definitions accept a message
8
+ // there too (N/ui/serverWidget.d.ts). And Oracle's Form.addPageInitMessage page
9
+ // (Suitelets and user event beforeLoad) and FAQ sample build the message on the
10
+ // server. Only show() and hide() need a browser.
4
11
  // Pure logic shared by the Biome rules and the CLI.
5
12
  export const CLIENT_ONLY_MODULES = new Set([
6
13
  "N/currentRecord",
7
14
  "N/portlet",
8
15
  "N/ui/dialog",
9
- "N/ui/message",
10
16
  ]);
11
17
  export const SERVER_ONLY_MODULES = new Set([
12
18
  "N/auth",
@@ -0,0 +1,35 @@
1
+ const unquote = (text) => text.replace(/^['"]|['"]$/g, "");
2
+ /** The arguments of a call. */
3
+ export function callArguments(call) {
4
+ return call.arguments?.children()[0]?.children() ?? [];
5
+ }
6
+ /** The members of an object literal (properties, methods and spreads). */
7
+ export function objectMembers(object) {
8
+ return object.children()[0]?.children() ?? [];
9
+ }
10
+ /** The object literal passed as argument `index`, if it is one. */
11
+ export function optionsObject(call, index = 0) {
12
+ const argument = callArguments(call)[index];
13
+ return argument?.kind === "JS_OBJECT_EXPRESSION" ? argument : undefined;
14
+ }
15
+ /** The value node of property `key` in an object literal. */
16
+ export function propertyValue(object, key) {
17
+ for (const member of objectMembers(object)) {
18
+ if (member.kind !== "JS_PROPERTY_OBJECT_MEMBER")
19
+ continue;
20
+ const [name, value] = member.children();
21
+ if (name && unquote(name.text) === key)
22
+ return value;
23
+ }
24
+ return undefined;
25
+ }
26
+ /** Whether an object literal contains a spread, so its keys are not all known. */
27
+ export function hasSpread(object) {
28
+ return objectMembers(object).some((m) => m.kind === "JS_SPREAD");
29
+ }
30
+ /** The text of a string literal without its quotes, or undefined for anything else. */
31
+ export function stringValue(node) {
32
+ return node?.kind === "JS_STRING_LITERAL_EXPRESSION"
33
+ ? unquote(node.text)
34
+ : undefined;
35
+ }
@@ -0,0 +1,14 @@
1
+ import { hasSpread, objectMembers, optionsObject } from "./options.js";
2
+ /** Names of the properties an options object literal defines, or undefined if a spread hides some. */
3
+ export function definedOptions(call) {
4
+ const options = optionsObject(call);
5
+ if (!options || hasSpread(options))
6
+ return undefined;
7
+ const names = new Set();
8
+ for (const member of objectMembers(options)) {
9
+ const name = member.children()[0]?.text.replace(/^['"]|['"]$/g, "");
10
+ if (name)
11
+ names.add(name);
12
+ }
13
+ return names;
14
+ }
@@ -33,6 +33,10 @@ export const SCRIPT_TYPES = [
33
33
  ],
34
34
  name: "ClientScript",
35
35
  },
36
+ {
37
+ entryPoints: [],
38
+ name: "ConsolidatedRateAdjustorPlugin",
39
+ },
36
40
  {
37
41
  entryPoints: ["customizeGlImpact"],
38
42
  name: "CustomGLPlugin",
@@ -46,21 +50,12 @@ export const SCRIPT_TYPES = [
46
50
  name: "DatasetBuilderPlugin",
47
51
  },
48
52
  {
49
- entryPoints: [
50
- "getConfigurationIFrameUrl",
51
- "getAccounts",
52
- "getTransactionData",
53
- ],
54
- name: "FiConnectivityPlugin",
53
+ entryPoints: [],
54
+ name: "EmailCapturePlugin",
55
55
  },
56
56
  {
57
- entryPoints: [
58
- "parseData",
59
- "getStandardTransactionCodes",
60
- "getExpenseCodes",
61
- "getConfigurationPageUrl",
62
- ],
63
- name: "FiParserPlugin",
57
+ entryPoints: ["handle"],
58
+ name: "EventSubscriber",
64
59
  },
65
60
  {
66
61
  entryPoints: ["getInputData", "map", "reduce", "summarize"],
@@ -70,6 +65,10 @@ export const SCRIPT_TYPES = [
70
65
  entryPoints: ["each"],
71
66
  name: "MassUpdateScript",
72
67
  },
68
+ {
69
+ entryPoints: [],
70
+ name: "PaymentGatewayPlugin",
71
+ },
73
72
  {
74
73
  entryPoints: [],
75
74
  name: "PluginTypeImpl",
@@ -78,6 +77,10 @@ export const SCRIPT_TYPES = [
78
77
  entryPoints: ["render"],
79
78
  name: "Portlet",
80
79
  },
80
+ {
81
+ entryPoints: [],
82
+ name: "PromotionsPlugin",
83
+ },
81
84
  {
82
85
  entryPoints: ["delete", "get", "post", "put"],
83
86
  name: "Restlet",
@@ -90,6 +93,10 @@ export const SCRIPT_TYPES = [
90
93
  entryPoints: ["run"],
91
94
  name: "SDFInstallationScript",
92
95
  },
96
+ {
97
+ entryPoints: [],
98
+ name: "ShippingPartnersPlugin",
99
+ },
93
100
  {
94
101
  entryPoints: ["run"],
95
102
  name: "SpaClientScript",
@@ -102,13 +109,17 @@ export const SCRIPT_TYPES = [
102
109
  entryPoints: ["onRequest"],
103
110
  name: "Suitelet",
104
111
  },
112
+ {
113
+ entryPoints: [],
114
+ name: "TaxCalculationPlugin",
115
+ },
105
116
  {
106
117
  entryPoints: ["afterSubmit", "beforeLoad", "beforeSubmit"],
107
118
  name: "UserEventScript",
108
119
  },
109
120
  {
110
121
  entryPoints: ["createWorkbook"],
111
- name: "WorkbookBbuilderPlugin",
122
+ name: "WorkbookBuilderPlugin",
112
123
  },
113
124
  {
114
125
  entryPoints: ["onAction"],