biome-plugin-suitescript 0.1.1 → 0.3.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/README.md +157 -27
- package/biome-manifest.json +261 -7
- package/configs/all.json +29 -0
- package/configs/minimal.json +14 -0
- package/configs/recommended.json +20 -0
- package/dist/cli/analyze.js +33 -3
- package/dist/lib/define.js +35 -5
- package/dist/lib/headers.js +12 -0
- package/dist/lib/module-calls.js +65 -0
- package/dist/lib/module-script-types.js +76 -0
- package/dist/lib/options.js +35 -0
- package/dist/lib/required-options.js +14 -0
- package/dist/lib/script-side.js +83 -0
- package/dist/lib/script-types.js +37 -14
- package/dist/lib/suiteql.js +16 -0
- package/dist/lib/suitescript.js +23 -0
- package/dist/lib/typings-data.js +4327 -0
- package/dist/lib/typings.js +2 -0
- package/dist/rules/logRequireDetails.js +4 -0
- package/dist/rules/logRequireTitle.js +1 -0
- package/dist/rules/noAuthorizationHeaderInInternalRequest.js +24 -0
- package/dist/rules/noBeforeSubmitRecordReload.js +19 -0
- package/dist/rules/noBlockedHttpsHeader.js +32 -0
- package/dist/rules/noBlockedResponseHeader.js +35 -0
- package/dist/rules/noBrowserGlobals.js +25 -0
- package/dist/rules/noClientLog.js +14 -0
- package/dist/rules/noConflictingClientScriptProps.js +36 -0
- package/dist/rules/noDynamicRequire.js +23 -0
- package/dist/rules/noDynamicSuiteQL.js +29 -0
- package/dist/rules/noEmptyEntryPoints.js +19 -0
- package/dist/rules/noExtraDeps.js +16 -0
- package/dist/rules/noExtraParams.js +17 -0
- package/dist/rules/noGlobalShadowing.js +38 -0
- package/dist/rules/noHardcodedAuthPasswords.js +27 -0
- package/dist/rules/noHardcodedSecrets.js +43 -0
- package/dist/rules/noHeaderUnderscores.js +23 -0
- package/dist/rules/noInnerHtml.js +41 -0
- package/dist/rules/noInsecureCookie.js +27 -0
- package/dist/rules/noInsecureHttpsUrl.js +20 -0
- package/dist/rules/noInvalidDefine.js +47 -0
- package/dist/rules/noInvalidEnumMember.js +24 -0
- package/dist/rules/noLocationHeader.js +20 -0
- package/dist/rules/noLogModule.js +8 -2
- package/dist/rules/noMapReduceSearchResults.js +33 -0
- package/dist/rules/noMissingModules.js +29 -0
- package/dist/rules/noModuleScopeApiCalls.js +49 -0
- package/dist/rules/noMultipleDefine.js +14 -0
- package/dist/rules/noNativeIterators.js +19 -0
- package/dist/rules/noPositionalOptions.js +26 -0
- package/dist/rules/noPostMessageWildcard.js +15 -0
- package/dist/rules/noPublicFileUpload.js +20 -0
- package/dist/rules/noQueryColumnFieldAndFormula.js +19 -0
- package/dist/rules/noRecordInLoop.js +34 -0
- package/dist/rules/noRequireInsteadOfDefine.js +25 -0
- package/dist/rules/noSecretKeyGuidAndSecret.js +16 -0
- package/dist/rules/noSensitiveBrowserStorage.js +24 -0
- package/dist/rules/noStackInResponse.js +21 -0
- package/dist/rules/noSuiteQLSelectStar.js +18 -0
- package/dist/rules/noSuiteQLSystemNoteJoin.js +19 -0
- package/dist/rules/noSuiteQLTypeInSearch.js +41 -0
- package/dist/rules/noSuiteScript1Api.js +84 -0
- package/dist/rules/noUnknownModuleMember.js +23 -0
- package/dist/rules/noUnknownOptions.js +31 -0
- package/dist/rules/noUnsupportedServerWidgetFieldType.js +40 -0
- package/dist/rules/noWeakHash.js +17 -0
- package/dist/rules/noWildcardCors.js +16 -0
- package/dist/rules/noWrongScriptTypeModules.js +30 -0
- package/dist/rules/requireClientValidateReturn.js +27 -0
- package/dist/rules/requireCustpagePrefix.js +23 -0
- package/dist/rules/requireEachReturn.js +38 -0
- package/dist/rules/requireLogModule.js +25 -0
- package/dist/rules/requireOptions.js +26 -0
- package/dist/rules/requireOrderByForPagedSuiteQL.js +17 -0
- package/dist/rules/requireQueryOptions.js +53 -0
- package/dist/rules/requireServerResponseOptions.js +33 -0
- package/dist/rules/requireServerWidgetOptions.js +37 -0
- package/package.json +3 -1
- package/dist/rules/noExtraModules.js +0 -23
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { esExportNames } from "./biome-modules.js";
|
|
2
|
+
import { parseDefine } from "./define.js";
|
|
3
|
+
import { amdReturnedNames } from "./entry-points.js";
|
|
4
|
+
import { inferScriptType } from "./inference.js";
|
|
5
|
+
import { sideOf } from "./module-script-types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Script types the module containing `node` (a define() call or an ES module)
|
|
8
|
+
* could be, judged from the entry points it returns. Undefined for libraries
|
|
9
|
+
* and for entry points that cannot be analysed.
|
|
10
|
+
*/
|
|
11
|
+
export function inferredTypes(node) {
|
|
12
|
+
for (const candidate of [node, ...node.ancestors()]) {
|
|
13
|
+
let names;
|
|
14
|
+
if (candidate.kind === "JS_CALL_EXPRESSION") {
|
|
15
|
+
const def = parseDefine(candidate);
|
|
16
|
+
if (!def)
|
|
17
|
+
continue;
|
|
18
|
+
names = amdReturnedNames(def);
|
|
19
|
+
}
|
|
20
|
+
else if (candidate.kind === "JS_MODULE") {
|
|
21
|
+
names = esExportNames(candidate);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
return (names && inferScriptType(names))?.types;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/** Whether that module is a client or server script. Undefined when unknown or mixed. */
|
|
31
|
+
export function scriptSide(node) {
|
|
32
|
+
const types = inferredTypes(node);
|
|
33
|
+
return types && sideOf(types);
|
|
34
|
+
}
|
|
35
|
+
const FUNCTION_KINDS = [
|
|
36
|
+
"JS_FUNCTION_DECLARATION",
|
|
37
|
+
"JS_FUNCTION_EXPRESSION",
|
|
38
|
+
"JS_ARROW_FUNCTION_EXPRESSION",
|
|
39
|
+
"JS_METHOD_OBJECT_MEMBER",
|
|
40
|
+
];
|
|
41
|
+
const unquote = (text) => text.replace(/^['"]|['"]$/g, "");
|
|
42
|
+
/** The name a function is bound to: its declaration, variable, object key or `o.name =` target. */
|
|
43
|
+
export function functionName(fn) {
|
|
44
|
+
if (fn.kind === "JS_FUNCTION_DECLARATION")
|
|
45
|
+
return fn.children().find((c) => c.kind === "JS_IDENTIFIER_BINDING")?.text;
|
|
46
|
+
if (fn.kind === "JS_METHOD_OBJECT_MEMBER") {
|
|
47
|
+
const name = fn.children()[0]?.text;
|
|
48
|
+
return name ? unquote(name) : undefined;
|
|
49
|
+
}
|
|
50
|
+
const parent = fn.parent;
|
|
51
|
+
if (parent?.kind === "JS_PROPERTY_OBJECT_MEMBER") {
|
|
52
|
+
const name = parent.children()[0]?.text;
|
|
53
|
+
return name ? unquote(name) : undefined;
|
|
54
|
+
}
|
|
55
|
+
if (parent?.kind === "JS_INITIALIZER_CLAUSE")
|
|
56
|
+
return parent.parent
|
|
57
|
+
?.children()
|
|
58
|
+
.find((c) => c.kind === "JS_IDENTIFIER_BINDING")?.text;
|
|
59
|
+
if (parent?.kind === "JS_ASSIGNMENT_EXPRESSION") {
|
|
60
|
+
const target = parent.children()[0];
|
|
61
|
+
if (target?.kind === "JS_STATIC_MEMBER_ASSIGNMENT")
|
|
62
|
+
return target.children()[1]?.text;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/** Names of the functions enclosing `node`, innermost first. */
|
|
67
|
+
export function enclosingFunctionNames(node) {
|
|
68
|
+
return node
|
|
69
|
+
.ancestors()
|
|
70
|
+
.filter((a) => FUNCTION_KINDS.includes(a.kind))
|
|
71
|
+
.map(functionName)
|
|
72
|
+
.filter((n) => !!n);
|
|
73
|
+
}
|
|
74
|
+
export function isFunction(node) {
|
|
75
|
+
return FUNCTION_KINDS.includes(node.kind);
|
|
76
|
+
}
|
|
77
|
+
/** Every node below `node`, depth first. */
|
|
78
|
+
export function* descendants(node) {
|
|
79
|
+
for (const child of node.children()) {
|
|
80
|
+
yield child;
|
|
81
|
+
yield* descendants(child);
|
|
82
|
+
}
|
|
83
|
+
}
|
package/dist/lib/script-types.js
CHANGED
|
@@ -33,30 +33,29 @@ 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",
|
|
39
43
|
},
|
|
44
|
+
{
|
|
45
|
+
entryPoints: [],
|
|
46
|
+
name: "CustomTool",
|
|
47
|
+
},
|
|
40
48
|
{
|
|
41
49
|
entryPoints: ["createDataset"],
|
|
42
50
|
name: "DatasetBuilderPlugin",
|
|
43
51
|
},
|
|
44
52
|
{
|
|
45
|
-
entryPoints: [
|
|
46
|
-
|
|
47
|
-
"getAccounts",
|
|
48
|
-
"getTransactionData",
|
|
49
|
-
],
|
|
50
|
-
name: "FiConnectivityPlugin",
|
|
53
|
+
entryPoints: [],
|
|
54
|
+
name: "EmailCapturePlugin",
|
|
51
55
|
},
|
|
52
56
|
{
|
|
53
|
-
entryPoints: [
|
|
54
|
-
|
|
55
|
-
"getStandardTransactionCodes",
|
|
56
|
-
"getExpenseCodes",
|
|
57
|
-
"getConfigurationPageUrl",
|
|
58
|
-
],
|
|
59
|
-
name: "FiParserPlugin",
|
|
57
|
+
entryPoints: ["handle"],
|
|
58
|
+
name: "EventSubscriber",
|
|
60
59
|
},
|
|
61
60
|
{
|
|
62
61
|
entryPoints: ["getInputData", "map", "reduce", "summarize"],
|
|
@@ -66,6 +65,10 @@ export const SCRIPT_TYPES = [
|
|
|
66
65
|
entryPoints: ["each"],
|
|
67
66
|
name: "MassUpdateScript",
|
|
68
67
|
},
|
|
68
|
+
{
|
|
69
|
+
entryPoints: [],
|
|
70
|
+
name: "PaymentGatewayPlugin",
|
|
71
|
+
},
|
|
69
72
|
{
|
|
70
73
|
entryPoints: [],
|
|
71
74
|
name: "PluginTypeImpl",
|
|
@@ -74,6 +77,10 @@ export const SCRIPT_TYPES = [
|
|
|
74
77
|
entryPoints: ["render"],
|
|
75
78
|
name: "Portlet",
|
|
76
79
|
},
|
|
80
|
+
{
|
|
81
|
+
entryPoints: [],
|
|
82
|
+
name: "PromotionsPlugin",
|
|
83
|
+
},
|
|
77
84
|
{
|
|
78
85
|
entryPoints: ["delete", "get", "post", "put"],
|
|
79
86
|
name: "Restlet",
|
|
@@ -86,17 +93,33 @@ export const SCRIPT_TYPES = [
|
|
|
86
93
|
entryPoints: ["run"],
|
|
87
94
|
name: "SDFInstallationScript",
|
|
88
95
|
},
|
|
96
|
+
{
|
|
97
|
+
entryPoints: [],
|
|
98
|
+
name: "ShippingPartnersPlugin",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
entryPoints: ["run"],
|
|
102
|
+
name: "SpaClientScript",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
entryPoints: ["initializeSpa"],
|
|
106
|
+
name: "SpaServerScript",
|
|
107
|
+
},
|
|
89
108
|
{
|
|
90
109
|
entryPoints: ["onRequest"],
|
|
91
110
|
name: "Suitelet",
|
|
92
111
|
},
|
|
112
|
+
{
|
|
113
|
+
entryPoints: [],
|
|
114
|
+
name: "TaxCalculationPlugin",
|
|
115
|
+
},
|
|
93
116
|
{
|
|
94
117
|
entryPoints: ["afterSubmit", "beforeLoad", "beforeSubmit"],
|
|
95
118
|
name: "UserEventScript",
|
|
96
119
|
},
|
|
97
120
|
{
|
|
98
121
|
entryPoints: ["createWorkbook"],
|
|
99
|
-
name: "
|
|
122
|
+
name: "WorkbookBuilderPlugin",
|
|
100
123
|
},
|
|
101
124
|
{
|
|
102
125
|
entryPoints: ["onAction"],
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { optionsObject, propertyValue } from "./options.js";
|
|
2
|
+
import { isStaticString } from "./suitescript.js";
|
|
3
|
+
/** Matches query.runSuiteQL, runSuiteQLPaged and their .promise forms. */
|
|
4
|
+
export const SUITEQL = /(^|\.)runSuiteQL(Paged)?(\.promise)?$/;
|
|
5
|
+
/** The query argument of a runSuiteQL call, from an options object or a bare argument. */
|
|
6
|
+
export function suiteqlQuery(call) {
|
|
7
|
+
const options = optionsObject(call);
|
|
8
|
+
return options
|
|
9
|
+
? propertyValue(options, "query")
|
|
10
|
+
: call.arguments?.children()[0]?.children()[0];
|
|
11
|
+
}
|
|
12
|
+
/** The SQL of a runSuiteQL call when it is built only from literals. */
|
|
13
|
+
export function staticSuiteql(call) {
|
|
14
|
+
const sql = suiteqlQuery(call);
|
|
15
|
+
return sql && isStaticString(sql) ? sql.text : undefined;
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { importsNetSuite } from "./biome-modules.js";
|
|
2
|
+
import { parseDefine } from "./define.js";
|
|
3
|
+
/** Whether a node is inside an AMD `define([...], fn)` or an ES module importing `N/` modules. */
|
|
4
|
+
export function inSuiteScriptModule(node) {
|
|
5
|
+
return node.ancestors().some((a) => {
|
|
6
|
+
if (a.kind === "JS_CALL_EXPRESSION")
|
|
7
|
+
return !!parseDefine(a);
|
|
8
|
+
return a.kind === "JS_MODULE" && importsNetSuite(a);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
/** Whether an expression is built only from string literals (and `+`). */
|
|
12
|
+
export function isStaticString(node) {
|
|
13
|
+
if (!node)
|
|
14
|
+
return false;
|
|
15
|
+
if (node.kind === "JS_STRING_LITERAL_EXPRESSION")
|
|
16
|
+
return true;
|
|
17
|
+
if (node.kind === "JS_TEMPLATE_EXPRESSION")
|
|
18
|
+
return !node.text.includes("${");
|
|
19
|
+
if (node.kind === "JS_BINARY_EXPRESSION" ||
|
|
20
|
+
node.kind === "JS_PARENTHESIZED_EXPRESSION")
|
|
21
|
+
return node.children().every(isStaticString);
|
|
22
|
+
return false;
|
|
23
|
+
}
|