biome-plugin-suitescript 0.1.0 → 0.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/LICENSE +21 -21
- package/README.md +208 -149
- package/biome-manifest.json +95 -3
- package/configs/suitescript2.json +5 -5
- package/dist/bin/biome-plugin-suitescript.js +23 -23
- package/dist/cli/analyze.js +33 -3
- package/dist/lib/module-script-types.js +70 -0
- package/dist/lib/script-side.js +83 -0
- package/dist/lib/script-types.js +12 -0
- package/dist/lib/suitescript.js +23 -0
- package/dist/rules/noAmdName.grit +9 -9
- package/dist/rules/noBeforeSubmitRecordReload.js +19 -0
- package/dist/rules/noBrowserGlobals.js +23 -0
- package/dist/rules/noClientLog.js +14 -0
- package/dist/rules/noDynamicSuiteQL.js +24 -0
- package/dist/rules/noEmptyEntryPoints.js +19 -0
- package/dist/rules/noEval.js +21 -0
- package/dist/rules/noGlobalShadowing.js +34 -0
- package/dist/rules/noHardcodedSecrets.js +38 -0
- package/dist/rules/noHeaderUnderscores.js +23 -0
- package/dist/rules/noInsecureHttpsUrl.js +20 -0
- package/dist/rules/noLogModule.js +4 -1
- package/dist/rules/noMapReduceSearchResults.js +33 -0
- package/dist/rules/noMissingModules.js +26 -0
- package/dist/rules/noModuleExtensions.grit +12 -12
- package/dist/rules/noModuleScopeApiCalls.js +39 -0
- package/dist/rules/noNativeIterators.js +16 -0
- package/dist/rules/noQueryColumnFieldAndFormula.js +19 -0
- package/dist/rules/noRecordInLoop.js +31 -0
- package/dist/rules/noSuiteScript1Api.js +10 -0
- package/dist/rules/noUserEventContextSave.js +12 -0
- package/dist/rules/noWrongScriptTypeModules.js +28 -0
- package/dist/rules/requireClientValidateReturn.js +27 -0
- package/dist/rules/requireCustpagePrefix.js +22 -0
- package/package.json +83 -83
- package/schema.json +21 -21
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Eric Nemchik
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eric Nemchik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,149 +1,208 @@
|
|
|
1
|
-
# biome-plugin-suitescript
|
|
2
|
-
|
|
3
|
-
[Biome](https://biomejs.dev) lint plugins and a tag checker for NetSuite **SuiteScript 2.1**, ported from
|
|
4
|
-
[acdvs/eslint-plugin-suitescript](https://github.com/acdvs/eslint-plugin-suitescript) and
|
|
5
|
-
[btonasse/eslint-plugin-suitescript](https://github.com/btonasse/eslint-plugin-suitescript).
|
|
6
|
-
|
|
7
|
-
Requires Biome **>= 2.6.4** (npm-distributed plugins use `biome-manifest.json`) and Node >= 22 for the CLI.
|
|
8
|
-
Rules work on both AMD `define()` and ES `import`/`export` source, in `.js` and `.ts`.
|
|
9
|
-
|
|
10
|
-
## Install
|
|
11
|
-
|
|
12
|
-
```sh
|
|
13
|
-
npm i -D @biomejs/biome biome-plugin-suitescript
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Use
|
|
17
|
-
|
|
18
|
-
```jsonc
|
|
19
|
-
// biome.json
|
|
20
|
-
{
|
|
21
|
-
"extends": ["biome-plugin-suitescript/configs/suitescript2"], // globals: define, require, log, util
|
|
22
|
-
"plugins": [
|
|
23
|
-
"biome-plugin-suitescript/presets/recommended",
|
|
24
|
-
"biome-plugin-suitescript/noLogModule" // add individual rules on top of a preset
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Run the tag checker beside `biome check` (see [Tag checker](#tag-checker)):
|
|
30
|
-
|
|
31
|
-
```sh
|
|
32
|
-
npx biome-plugin-suitescript check
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Rules
|
|
36
|
-
|
|
37
|
-
Each rule has one fixed severity: `error` for things that fail at runtime or on upload, `warn` for conventions.
|
|
38
|
-
Biome plugins cannot be reconfigured per project, so you choose by enabling rules, scoping them, or generating one.
|
|
39
|
-
|
|
40
|
-
| Rule | Severity | Description
|
|
41
|
-
| ------------------------------ | -------- |
|
|
42
|
-
| `noAmdName` (fixable) | error | No module id as the first `define` argument
|
|
43
|
-
| `noExtraModules` | error | Dependency array and callback parameters must match in count
|
|
44
|
-
| `noInvalidModules` | error | Unknown `N/...` modules
|
|
45
|
-
| `noModuleExtensions` (fixable) | error | No `.js` in `define` dependency names
|
|
46
|
-
| `moduleVars` | warn | Conventional identifier per `N/` module (`N/record` → `record`)
|
|
47
|
-
| `logRequireTitle` | warn | `log.debug/audit/error/emergency({...})` needs `title`
|
|
48
|
-
| `logRequireDetails` | warn | ...needs `details`
|
|
49
|
-
| `noLogModule` | warn |
|
|
50
|
-
| `entryPoints` | error | Returned entry points must belong to one script type and include its required ones
|
|
51
|
-
|
|
52
|
-
`
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
1
|
+
# biome-plugin-suitescript
|
|
2
|
+
|
|
3
|
+
[Biome](https://biomejs.dev) lint plugins and a tag checker for NetSuite **SuiteScript 2.1**, ported from
|
|
4
|
+
[acdvs/eslint-plugin-suitescript](https://github.com/acdvs/eslint-plugin-suitescript) and
|
|
5
|
+
[btonasse/eslint-plugin-suitescript](https://github.com/btonasse/eslint-plugin-suitescript).
|
|
6
|
+
|
|
7
|
+
Requires Biome **>= 2.6.4** (npm-distributed plugins use `biome-manifest.json`) and Node >= 22 for the CLI.
|
|
8
|
+
Rules work on both AMD `define()` and ES `import`/`export` source, in `.js` and `.ts`.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm i -D @biomejs/biome biome-plugin-suitescript
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Use
|
|
17
|
+
|
|
18
|
+
```jsonc
|
|
19
|
+
// biome.json
|
|
20
|
+
{
|
|
21
|
+
"extends": ["biome-plugin-suitescript/configs/suitescript2"], // globals: define, require, log, util
|
|
22
|
+
"plugins": [
|
|
23
|
+
"biome-plugin-suitescript/presets/recommended",
|
|
24
|
+
"biome-plugin-suitescript/noLogModule" // add individual rules on top of a preset
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Run the tag checker beside `biome check` (see [Tag checker](#tag-checker)):
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npx biome-plugin-suitescript check
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Rules
|
|
36
|
+
|
|
37
|
+
Each rule has one fixed severity: `error` for things that fail at runtime or on upload, `warn` for conventions.
|
|
38
|
+
Biome plugins cannot be reconfigured per project, so you choose by enabling rules, scoping them, or generating one.
|
|
39
|
+
|
|
40
|
+
| Rule | Severity | Description | ESLint equivalent |
|
|
41
|
+
| ------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------- |
|
|
42
|
+
| `noAmdName` (fixable) | error | No module id as the first `define` argument | `no-amd-name` |
|
|
43
|
+
| `noExtraModules` | error | Dependency array and callback parameters must match in count | `no-extra-modules` |
|
|
44
|
+
| `noInvalidModules` | error | Unknown `N/...` modules | `no-invalid-modules` |
|
|
45
|
+
| `noModuleExtensions` (fixable) | error | No `.js` in `define` dependency names | `no-module-extensions` |
|
|
46
|
+
| `moduleVars` | warn | Conventional identifier per `N/` module (`N/record` → `record`) | `module-vars` |
|
|
47
|
+
| `logRequireTitle` | warn | `log.debug/audit/error/emergency({...})` needs `title` | `log-args` `requireTitle` |
|
|
48
|
+
| `logRequireDetails` | warn | ...needs `details` | `log-args` `requireDetails` |
|
|
49
|
+
| `noLogModule` | warn | Opt-in opposite of `noMissingModules`: use the global `log`, not `N/log` | `no-log-module` |
|
|
50
|
+
| `entryPoints` | error | Returned entry points must belong to one script type and include its required ones | `entry-points` |
|
|
51
|
+
| `noMissingModules` | error | A module used (`record.load()`, `log.debug()`) must be loaded in `define`/`import` | none (from Oracle's skills) |
|
|
52
|
+
| `noGlobalShadowing` | warn | No variable named `log` or `util` other than the N/log, N/util module itself | none (from Oracle's skills) |
|
|
53
|
+
| `noEval` | error | No `eval`, `new Function` or string-form `setTimeout`/`setInterval` | none (OWASP skill) |
|
|
54
|
+
| `noDynamicSuiteQL` | warn | `runSuiteQL` queries use `?` placeholders, not concatenation or template literals | none (OWASP skill) |
|
|
55
|
+
| `noSuiteScript1Api` | error | No `nlapi*`/`nlobj*` (SuiteScript 1.0) leftovers | none (upgrade skill) |
|
|
56
|
+
| `noModuleScopeApiCalls` | warn | No `N/` API calls while the `define` callback runs; initialize from entry points | none (SAFE guide) |
|
|
57
|
+
| `noRecordInLoop` | warn | No `record.load`/`record.create` inside loops or `forEach`/`each`/`map` callbacks | none (SAFE guide) |
|
|
58
|
+
| `noNativeIterators` | warn | No generator functions or `Symbol.iterator` (GraalJS) | none (SAFE guide) |
|
|
59
|
+
| `noWrongScriptTypeModules` | error | No client-only module (`N/currentRecord`, `N/ui/dialog`) in a server script, or a server-only one in a client script | none (Oracle docs) |
|
|
60
|
+
| `noBrowserGlobals` | error | No `document`, `window`, `localStorage`, `XMLHttpRequest`, `alert` or `navigator` in server scripts | none (Oracle docs) |
|
|
61
|
+
| `noBeforeSubmitRecordReload` | error | No `record.load`/`submitFields`/`delete` of `context.newRecord.id` inside `beforeSubmit` | none (Oracle docs) |
|
|
62
|
+
| `noEmptyEntryPoints` | warn | A returned entry point with an empty body | none (Oracle docs) |
|
|
63
|
+
| `noMapReduceSearchResults` | warn | `getInputData` returns the search object, not `getRange()` results | none (Oracle docs) |
|
|
64
|
+
| `noHardcodedSecrets` | error | Opt-in only: no string literal assigned to `password`, `secret`, `apiKey`, ... | none (Oracle docs) |
|
|
65
|
+
| `noUserEventContextSave` | error | No `.save()` on `context.newRecord` or `context.oldRecord` in user events | none (Oracle docs) |
|
|
66
|
+
| `noInsecureHttpsUrl` | error | No `http://` URL passed to `https.*` (N/https refuses plain HTTP) | none (Oracle docs) |
|
|
67
|
+
| `requireCustpagePrefix` | warn | The `id` of a UI object added to a form (`addField`, `addTab`, ...) starts with `custpage` | none (Oracle docs) |
|
|
68
|
+
| `noHeaderUnderscores` | warn | No underscore in a custom `https.*` header name | none (Oracle docs) |
|
|
69
|
+
| `noQueryColumnFieldAndFormula` | error | A query column cannot set both `fieldId` and `formula` | none (Oracle docs) |
|
|
70
|
+
| `requireClientValidateReturn` | warn | `saveRecord` and `validate*` return a value (only a body with no `return <value>` is flagged) | none (Oracle docs) |
|
|
71
|
+
| `noClientLog` | warn | `log.*` is ignored in client scripts attached to a form; use `console` | none (Oracle docs) |
|
|
72
|
+
|
|
73
|
+
`noAmdName`, `noExtraModules` and `noModuleExtensions` only apply to `define()`. The rest cover `import` too.
|
|
74
|
+
|
|
75
|
+
| Preset | Contents |
|
|
76
|
+
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
77
|
+
| `presets/recommended` | every rule above except the opt-in `noLogModule` and `noHardcodedSecrets` and the ones `presets/all` adds |
|
|
78
|
+
| `presets/minimal` | `noExtraModules`, `noInvalidModules` |
|
|
79
|
+
| `presets/all` | `recommended` plus `entryPoints`, `noRecordInLoop`, `noNativeIterators`, `noBeforeSubmitRecordReload`, `noEmptyEntryPoints`, `noMapReduceSearchResults`, `requireCustpagePrefix`, `noHeaderUnderscores`, `noQueryColumnFieldAndFormula`, `requireClientValidateReturn` and `noClientLog` (never `noLogModule` or `noHardcodedSecrets`) |
|
|
80
|
+
|
|
81
|
+
Suppress a diagnostic with `// biome-ignore lint/plugin: reason`.
|
|
82
|
+
|
|
83
|
+
### Opt-in rules and typings
|
|
84
|
+
|
|
85
|
+
`noHardcodedSecrets` is in no preset. It guesses from names (`password`, `passwd`, `pwd`, `secret`, `apiKey`,
|
|
86
|
+
`clientSecret`, `tokenSecret`, `consumerSecret`, exact match only) rather than knowing a value is a credential, so it
|
|
87
|
+
can misfire on look-alike names, and you should decide whether that trade is worth it: add
|
|
88
|
+
`"biome-plugin-suitescript/noHardcodedSecrets"` to `plugins` yourself. Oracle says "Do not hard-code any passwords
|
|
89
|
+
in scripts."
|
|
90
|
+
|
|
91
|
+
The rules assume you type-check against [`@hitc/netsuite-types`](https://github.com/headintheclouddev/typings-suitescript-2.0),
|
|
92
|
+
which already rejects wrong option names, missing parameters and positional-argument calls to N/ methods (their
|
|
93
|
+
signatures take an options object). Plain JavaScript projects get none of that from these rules.
|
|
94
|
+
|
|
95
|
+
### Logging and `N/log`
|
|
96
|
+
|
|
97
|
+
Oracle's own agent skills disagree on whether `N/log` must be loaded: one says the global exists but loading is
|
|
98
|
+
recommended, another says it is not global. This package takes the consistent position that a module you use
|
|
99
|
+
should be loaded, so `noMissingModules` requires `N/log` when `log.` is called. `noLogModule` is the opposite style
|
|
100
|
+
for projects that prefer the global, so enable only one of the two (`presets/all` omits `noLogModule`).
|
|
101
|
+
|
|
102
|
+
### Rules drawn from Oracle's documentation
|
|
103
|
+
|
|
104
|
+
`noWrongScriptTypeModules`, `noBrowserGlobals`, `noBeforeSubmitRecordReload`, `noEmptyEntryPoints` and
|
|
105
|
+
`noMapReduceSearchResults`, plus the later additions listed in the table above marked Oracle docs, come from Oracle's SuiteScript help-center pages and cover what type checking against
|
|
106
|
+
`@hitc/netsuite-types` cannot (the typings accept every signature involved). The client-only and server-only module
|
|
107
|
+
lists are copied from Oracle's module reference table, and `noWrongScriptTypeModules` and `noBrowserGlobals` only fire
|
|
108
|
+
when the script type can be inferred from the returned entry points. `noBeforeSubmitRecordReload` only matches calls
|
|
109
|
+
whose `id` is `...newRecord.id`. `noMapReduceSearchResults` only detects `.getRange()` results.
|
|
110
|
+
|
|
111
|
+
### Rules drawn from Oracle's agent skills
|
|
112
|
+
|
|
113
|
+
`noMissingModules`, `noGlobalShadowing`, `noEval`, `noDynamicSuiteQL`, `noSuiteScript1Api`, `noModuleScopeApiCalls`,
|
|
114
|
+
`noRecordInLoop` and `noNativeIterators` come from Oracle's
|
|
115
|
+
[agent skills](https://github.com/oracle/netsuite-suitecloud-sdk/tree/master/packages/agent-skills) (SuiteScript
|
|
116
|
+
upgrade, OWASP secure coding and the SAFE guide). `noMissingModules` leaves `util` alone because it is a global.
|
|
117
|
+
`noDynamicSuiteQL` does not flag a bare variable, whose contents it cannot see. `noRecordInLoop` is opt-in because
|
|
118
|
+
a Map/Reduce `map` entry point legitimately handles one record per call.
|
|
119
|
+
|
|
120
|
+
### `entryPoints` and script types
|
|
121
|
+
|
|
122
|
+
Biome plugins cannot read comments, so the script type is **inferred from what the module returns**
|
|
123
|
+
(`beforeLoad` → User Event, `onRequest` → Suitelet, ...). The rule reports mixed entry points
|
|
124
|
+
(`pageInit` with `execute`) and missing required ones (Map/Reduce needs `getInputData` and `map` or `reduce`).
|
|
125
|
+
Modules that return no distinctive entry point are libraries and are ignored. Generic names such as
|
|
126
|
+
`get`, `post`, `map` and `run` alone never make a module a script.
|
|
127
|
+
|
|
128
|
+
### Choosing what to enforce
|
|
129
|
+
|
|
130
|
+
- **Pick rules.** `log-args`' two options are the separate `logRequireTitle` / `logRequireDetails` rules.
|
|
131
|
+
- **Scope with `includes`.** For example, the global `log` is not available in client scripts:
|
|
132
|
+
|
|
133
|
+
```jsonc
|
|
134
|
+
{ "path": "biome-plugin-suitescript/noLogModule", "includes": ["src/**", "!src/**/client/**"] }
|
|
135
|
+
```
|
|
136
|
+
- **Custom module identifiers.** Generate a rule with your own mapping (and omit `moduleVars`):
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
npx biome-plugin-suitescript generate module-vars --set N/record=nRecord --out plugins/module-vars.js
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
then add `"./plugins/module-vars.js"` to `plugins`.
|
|
143
|
+
|
|
144
|
+
## Tag checker
|
|
145
|
+
|
|
146
|
+
`@NApiVersion` and `@NScriptType` live in comments, which no Biome plugin can read, so a small CLI checks them.
|
|
147
|
+
It is **report-only**: it never edits files.
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
npx biome-plugin-suitescript check # uses biome-plugin-suitescript.json
|
|
151
|
+
npx biome-plugin-suitescript check "src/**/*.ts" # or explicit globs
|
|
152
|
+
npx biome-plugin-suitescript check --format github # text (default) | json | github
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
It reports:
|
|
156
|
+
|
|
157
|
+
- `@NApiVersion` other than `2.1` (2.0, 2.x, 1.0 and invalid values), or missing when the file is a script.
|
|
158
|
+
- `@NScriptType` with no or an invalid value, or missing when the returned entry points imply one.
|
|
159
|
+
- A module from the wrong side of the declared script type (for example `N/file` in a client script), or one the declared type does not support at all (`N/http`, `N/https`, `N/llm` and `N/sftp` in a custom tool; custom modules in a bundle installation script).
|
|
160
|
+
- A declared script type that does not match the returned entry points, or lacks its required ones.
|
|
161
|
+
|
|
162
|
+
Files with no tags and no implied entry points (libraries) are skipped. For scripts still on 1.0, 2.0 or 2.x, the
|
|
163
|
+
output points to Oracle's
|
|
164
|
+
[`netsuite-suitescript-upgrade`](https://github.com/oracle/netsuite-suitecloud-sdk/tree/master/packages/agent-skills/netsuite-suitescript-upgrade)
|
|
165
|
+
agent skill for the conversion.
|
|
166
|
+
|
|
167
|
+
Exit codes: `0` clean, `1` problems found, `2` usage or configuration error.
|
|
168
|
+
|
|
169
|
+
### Configuration
|
|
170
|
+
|
|
171
|
+
List only the **script sources**: files deployed to the file cabinet, or the sources they are transpiled from (not
|
|
172
|
+
the transpiled output, and not build files such as `swc.mts`). The CLI refuses to run without a list rather than
|
|
173
|
+
guessing from `deploy.xml`.
|
|
174
|
+
|
|
175
|
+
```jsonc
|
|
176
|
+
// biome-plugin-suitescript.json
|
|
177
|
+
{
|
|
178
|
+
"$schema": "./node_modules/biome-plugin-suitescript/schema.json",
|
|
179
|
+
"includes": ["src/TypeScripts/**/*.{js,ts}", "!**/*.d.ts", "!**/*.test.*"]
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`!` negation works as in Biome's `includes`. Reuse the same globs in the plugin `includes` in `biome.json` to scope the
|
|
184
|
+
Biome rules identically.
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
The source is TypeScript under `src/`, compiled by `tsc` to `dist/` (the only code that is published). Scripts and
|
|
189
|
+
tests are TypeScript too and run directly on Node. Node and npm versions come from `devEngines` in `package.json`
|
|
190
|
+
(also in `.nvmrc` and `.tool-versions`).
|
|
191
|
+
|
|
192
|
+
```sh
|
|
193
|
+
npm install
|
|
194
|
+
npm run build # tsc -> dist/, copies GritQL rules, regenerates biome-manifest.json
|
|
195
|
+
npm run typecheck # tsc --noEmit over src, scripts and tests
|
|
196
|
+
npm run check # biome check (npm run format applies fixes)
|
|
197
|
+
npm run sync-modules # refreshes the N/ module list from @hitc/netsuite-types
|
|
198
|
+
npm test # builds first, then tests the built package
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Git hooks (lefthook) run Biome on staged files before commit and reinstall dependencies after checkout or merge.
|
|
202
|
+
|
|
203
|
+
Commits follow [Conventional Commits](https://www.conventionalcommits.org); release-please opens a release PR and
|
|
204
|
+
publishes to npm when it is merged, using npm trusted publishing (OIDC, no token; the package settings on npmjs.com
|
|
205
|
+
allow this repo and `release.yml`). Dependencies are managed by Renovate: dev
|
|
206
|
+
dependencies are pinned, the `@biomejs/biome` peer range stays a caret, and Biome and `@biomejs/runtime` update together.
|
|
207
|
+
|
|
208
|
+
MIT licensed. Module and script-type data and rule behavior derive from the MIT-licensed ESLint plugins above.
|
package/biome-manifest.json
CHANGED
|
@@ -15,8 +15,27 @@
|
|
|
15
15
|
"moduleVars",
|
|
16
16
|
"logRequireTitle",
|
|
17
17
|
"logRequireDetails",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"noMissingModules",
|
|
19
|
+
"noGlobalShadowing",
|
|
20
|
+
"noEval",
|
|
21
|
+
"noDynamicSuiteQL",
|
|
22
|
+
"noSuiteScript1Api",
|
|
23
|
+
"noModuleScopeApiCalls",
|
|
24
|
+
"noWrongScriptTypeModules",
|
|
25
|
+
"noBrowserGlobals",
|
|
26
|
+
"noUserEventContextSave",
|
|
27
|
+
"noInsecureHttpsUrl",
|
|
28
|
+
"entryPoints",
|
|
29
|
+
"noRecordInLoop",
|
|
30
|
+
"noNativeIterators",
|
|
31
|
+
"noBeforeSubmitRecordReload",
|
|
32
|
+
"noEmptyEntryPoints",
|
|
33
|
+
"noMapReduceSearchResults",
|
|
34
|
+
"requireCustpagePrefix",
|
|
35
|
+
"noHeaderUnderscores",
|
|
36
|
+
"noQueryColumnFieldAndFormula",
|
|
37
|
+
"requireClientValidateReturn",
|
|
38
|
+
"noClientLog"
|
|
20
39
|
],
|
|
21
40
|
"minimal": ["noExtraModules", "noInvalidModules"],
|
|
22
41
|
"recommended": [
|
|
@@ -26,7 +45,17 @@
|
|
|
26
45
|
"noModuleExtensions",
|
|
27
46
|
"moduleVars",
|
|
28
47
|
"logRequireTitle",
|
|
29
|
-
"logRequireDetails"
|
|
48
|
+
"logRequireDetails",
|
|
49
|
+
"noMissingModules",
|
|
50
|
+
"noGlobalShadowing",
|
|
51
|
+
"noEval",
|
|
52
|
+
"noDynamicSuiteQL",
|
|
53
|
+
"noSuiteScript1Api",
|
|
54
|
+
"noModuleScopeApiCalls",
|
|
55
|
+
"noWrongScriptTypeModules",
|
|
56
|
+
"noBrowserGlobals",
|
|
57
|
+
"noUserEventContextSave",
|
|
58
|
+
"noInsecureHttpsUrl"
|
|
30
59
|
]
|
|
31
60
|
},
|
|
32
61
|
"rules": [
|
|
@@ -45,17 +74,80 @@
|
|
|
45
74
|
{
|
|
46
75
|
"noAmdName": "./dist/rules/noAmdName.grit"
|
|
47
76
|
},
|
|
77
|
+
{
|
|
78
|
+
"noBeforeSubmitRecordReload": "./dist/rules/noBeforeSubmitRecordReload.js"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"noBrowserGlobals": "./dist/rules/noBrowserGlobals.js"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"noClientLog": "./dist/rules/noClientLog.js"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"noDynamicSuiteQL": "./dist/rules/noDynamicSuiteQL.js"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"noEmptyEntryPoints": "./dist/rules/noEmptyEntryPoints.js"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"noEval": "./dist/rules/noEval.js"
|
|
94
|
+
},
|
|
48
95
|
{
|
|
49
96
|
"noExtraModules": "./dist/rules/noExtraModules.js"
|
|
50
97
|
},
|
|
98
|
+
{
|
|
99
|
+
"noGlobalShadowing": "./dist/rules/noGlobalShadowing.js"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"noHardcodedSecrets": "./dist/rules/noHardcodedSecrets.js"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"noHeaderUnderscores": "./dist/rules/noHeaderUnderscores.js"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"noInsecureHttpsUrl": "./dist/rules/noInsecureHttpsUrl.js"
|
|
109
|
+
},
|
|
51
110
|
{
|
|
52
111
|
"noInvalidModules": "./dist/rules/noInvalidModules.js"
|
|
53
112
|
},
|
|
54
113
|
{
|
|
55
114
|
"noLogModule": "./dist/rules/noLogModule.js"
|
|
56
115
|
},
|
|
116
|
+
{
|
|
117
|
+
"noMapReduceSearchResults": "./dist/rules/noMapReduceSearchResults.js"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"noMissingModules": "./dist/rules/noMissingModules.js"
|
|
121
|
+
},
|
|
57
122
|
{
|
|
58
123
|
"noModuleExtensions": "./dist/rules/noModuleExtensions.grit"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"noModuleScopeApiCalls": "./dist/rules/noModuleScopeApiCalls.js"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"noNativeIterators": "./dist/rules/noNativeIterators.js"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"noQueryColumnFieldAndFormula": "./dist/rules/noQueryColumnFieldAndFormula.js"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"noRecordInLoop": "./dist/rules/noRecordInLoop.js"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"noSuiteScript1Api": "./dist/rules/noSuiteScript1Api.js"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"noUserEventContextSave": "./dist/rules/noUserEventContextSave.js"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"noWrongScriptTypeModules": "./dist/rules/noWrongScriptTypeModules.js"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"requireClientValidateReturn": "./dist/rules/requireClientValidateReturn.js"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"requireCustpagePrefix": "./dist/rules/requireCustpagePrefix.js"
|
|
59
151
|
}
|
|
60
152
|
]
|
|
61
153
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
{
|
|
2
|
-
"javascript": {
|
|
3
|
-
"globals": ["define", "require", "log", "util"]
|
|
4
|
-
}
|
|
5
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"javascript": {
|
|
3
|
+
"globals": ["define", "require", "log", "util"]
|
|
4
|
+
}
|
|
5
|
+
}
|
|
@@ -5,11 +5,11 @@ import { glob } from "tinyglobby";
|
|
|
5
5
|
import { analyzeSource } from "../cli/analyze.js";
|
|
6
6
|
import { format } from "../cli/format.js";
|
|
7
7
|
import { MODULES } from "../lib/modules.js";
|
|
8
|
-
const USAGE = `Usage:
|
|
9
|
-
biome-plugin-suitescript check [globs...] [--config file] [--format text|json|github]
|
|
10
|
-
biome-plugin-suitescript generate module-vars [--set N/module=identifier]... [--out file.js]
|
|
11
|
-
|
|
12
|
-
check reads "includes" from biome-plugin-suitescript.json unless globs are given.
|
|
8
|
+
const USAGE = `Usage:
|
|
9
|
+
biome-plugin-suitescript check [globs...] [--config file] [--format text|json|github]
|
|
10
|
+
biome-plugin-suitescript generate module-vars [--set N/module=identifier]... [--out file.js]
|
|
11
|
+
|
|
12
|
+
check reads "includes" from biome-plugin-suitescript.json unless globs are given.
|
|
13
13
|
Exit codes: 0 clean, 1 problems found, 2 usage or configuration error.`;
|
|
14
14
|
function fail(message) {
|
|
15
15
|
console.error(`${message}\n\n${USAGE}`);
|
|
@@ -99,24 +99,24 @@ function generate(args) {
|
|
|
99
99
|
map[name] = id;
|
|
100
100
|
}
|
|
101
101
|
const out = flags.out?.[0] ?? "suitescript-module-vars.js";
|
|
102
|
-
writeFileSync(out, `// Generated by biome-plugin-suitescript. Add to biome.json "plugins".
|
|
103
|
-
import { defineRule, ast, registerDiagnostic } from "@biomejs/runtime/plugin";
|
|
104
|
-
|
|
105
|
-
${inline("define.js")}
|
|
106
|
-
${inline("biome-modules.js")}
|
|
107
|
-
const MODULES = ${JSON.stringify(map, null, 2)};
|
|
108
|
-
|
|
109
|
-
export const moduleVars = defineRule({
|
|
110
|
-
\tquery: ast("JS_CALL_EXPRESSION", "JS_IMPORT"),
|
|
111
|
-
\trun(node) {
|
|
112
|
-
\t\tfor (const { name, param } of dependencies(node)) {
|
|
113
|
-
\t\t\tconst expected = name && Object.hasOwn(MODULES, name) ? MODULES[name] : undefined;
|
|
114
|
-
\t\t\tif (expected && param && param.text !== expected) {
|
|
115
|
-
\t\t\t\tregisterDiagnostic(param, "warning", \`Identifier for \${name} should be '\${expected}'.\`);
|
|
116
|
-
\t\t\t}
|
|
117
|
-
\t\t}
|
|
118
|
-
\t},
|
|
119
|
-
});
|
|
102
|
+
writeFileSync(out, `// Generated by biome-plugin-suitescript. Add to biome.json "plugins".
|
|
103
|
+
import { defineRule, ast, registerDiagnostic } from "@biomejs/runtime/plugin";
|
|
104
|
+
|
|
105
|
+
${inline("define.js")}
|
|
106
|
+
${inline("biome-modules.js")}
|
|
107
|
+
const MODULES = ${JSON.stringify(map, null, 2)};
|
|
108
|
+
|
|
109
|
+
export const moduleVars = defineRule({
|
|
110
|
+
\tquery: ast("JS_CALL_EXPRESSION", "JS_IMPORT"),
|
|
111
|
+
\trun(node) {
|
|
112
|
+
\t\tfor (const { name, param } of dependencies(node)) {
|
|
113
|
+
\t\t\tconst expected = name && Object.hasOwn(MODULES, name) ? MODULES[name] : undefined;
|
|
114
|
+
\t\t\tif (expected && param && param.text !== expected) {
|
|
115
|
+
\t\t\t\tregisterDiagnostic(param, "warning", \`Identifier for \${name} should be '\${expected}'.\`);
|
|
116
|
+
\t\t\t}
|
|
117
|
+
\t\t}
|
|
118
|
+
\t},
|
|
119
|
+
});
|
|
120
120
|
`);
|
|
121
121
|
console.log(`Wrote ${out}`);
|
|
122
122
|
}
|