@prismalyst/core 0.0.0-stage → 0.1.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 -0
- package/README.md +209 -2
- package/dist/ast/parser/create-ast-parser.d.ts +8 -0
- package/dist/ast/parser/create-ast-parser.d.ts.map +1 -0
- package/dist/ast/parser/create-ast-parser.js +41 -0
- package/dist/ast/parser/create-ast-parser.js.map +1 -0
- package/dist/ast/prisma/convert-to-prisma-expression.d.ts +5 -0
- package/dist/ast/prisma/convert-to-prisma-expression.d.ts.map +1 -0
- package/dist/ast/prisma/convert-to-prisma-expression.js +58 -0
- package/dist/ast/prisma/convert-to-prisma-expression.js.map +1 -0
- package/dist/ast/prisma/is-prisma-expression.d.ts +5 -0
- package/dist/ast/prisma/is-prisma-expression.d.ts.map +1 -0
- package/dist/ast/prisma/is-prisma-expression.js +52 -0
- package/dist/ast/prisma/is-prisma-expression.js.map +1 -0
- package/dist/ast/prisma/utils/define-method.d.ts +4 -0
- package/dist/ast/prisma/utils/define-method.d.ts.map +1 -0
- package/dist/ast/prisma/utils/define-method.js +18 -0
- package/dist/ast/prisma/utils/define-method.js.map +1 -0
- package/dist/ast/prisma/utils/remove-quotes.d.ts +2 -0
- package/dist/ast/prisma/utils/remove-quotes.d.ts.map +1 -0
- package/dist/ast/prisma/utils/remove-quotes.js +9 -0
- package/dist/ast/prisma/utils/remove-quotes.js.map +1 -0
- package/dist/ast/utils/create-ast-utils.d.ts +30 -0
- package/dist/ast/utils/create-ast-utils.d.ts.map +1 -0
- package/dist/ast/utils/create-ast-utils.js +9 -0
- package/dist/ast/utils/create-ast-utils.js.map +1 -0
- package/dist/ast/utils/create-node-utils.d.ts +21 -0
- package/dist/ast/utils/create-node-utils.d.ts.map +1 -0
- package/dist/ast/utils/create-node-utils.js +315 -0
- package/dist/ast/utils/create-node-utils.js.map +1 -0
- package/dist/ast/utils/create-prisma-utils.d.ts +12 -0
- package/dist/ast/utils/create-prisma-utils.d.ts.map +1 -0
- package/dist/ast/utils/create-prisma-utils.js +130 -0
- package/dist/ast/utils/create-prisma-utils.js.map +1 -0
- package/dist/ast/utils/get-property.d.ts +34 -0
- package/dist/ast/utils/get-property.d.ts.map +1 -0
- package/dist/ast/utils/get-property.js +61 -0
- package/dist/ast/utils/get-property.js.map +1 -0
- package/dist/constants/prisma-methods.constants.d.ts +14 -0
- package/dist/constants/prisma-methods.constants.d.ts.map +1 -0
- package/dist/constants/prisma-methods.constants.js +43 -0
- package/dist/constants/prisma-methods.constants.js.map +1 -0
- package/dist/index.cjs +1261 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/list/limit-relation-loading.d.ts +4 -0
- package/dist/rules/list/limit-relation-loading.d.ts.map +1 -0
- package/dist/rules/list/limit-relation-loading.js +60 -0
- package/dist/rules/list/limit-relation-loading.js.map +1 -0
- package/dist/rules/list/max-take.d.ts +7 -0
- package/dist/rules/list/max-take.d.ts.map +1 -0
- package/dist/rules/list/max-take.js +32 -0
- package/dist/rules/list/max-take.js.map +1 -0
- package/dist/rules/list/no-deep-offset-pagination.d.ts +8 -0
- package/dist/rules/list/no-deep-offset-pagination.d.ts.map +1 -0
- package/dist/rules/list/no-deep-offset-pagination.js +26 -0
- package/dist/rules/list/no-deep-offset-pagination.js.map +1 -0
- package/dist/rules/list/no-disconnect-in-request-flow.d.ts +8 -0
- package/dist/rules/list/no-disconnect-in-request-flow.d.ts.map +1 -0
- package/dist/rules/list/no-disconnect-in-request-flow.js +17 -0
- package/dist/rules/list/no-disconnect-in-request-flow.js.map +1 -0
- package/dist/rules/list/no-dynamic-raw-query.d.ts +4 -0
- package/dist/rules/list/no-dynamic-raw-query.d.ts.map +1 -0
- package/dist/rules/list/no-dynamic-raw-query.js +13 -0
- package/dist/rules/list/no-dynamic-raw-query.js.map +1 -0
- package/dist/rules/list/no-floating-prisma-promise.d.ts +4 -0
- package/dist/rules/list/no-floating-prisma-promise.d.ts.map +1 -0
- package/dist/rules/list/no-floating-prisma-promise.js +10 -0
- package/dist/rules/list/no-floating-prisma-promise.js.map +1 -0
- package/dist/rules/list/no-prisma-client-in-function.d.ts +9 -0
- package/dist/rules/list/no-prisma-client-in-function.d.ts.map +1 -0
- package/dist/rules/list/no-prisma-client-in-function.js +30 -0
- package/dist/rules/list/no-prisma-client-in-function.js.map +1 -0
- package/dist/rules/list/no-query-in-loop.d.ts +4 -0
- package/dist/rules/list/no-query-in-loop.d.ts.map +1 -0
- package/dist/rules/list/no-query-in-loop.js +9 -0
- package/dist/rules/list/no-query-in-loop.js.map +1 -0
- package/dist/rules/list/no-root-client-in-transaction.d.ts +4 -0
- package/dist/rules/list/no-root-client-in-transaction.d.ts.map +1 -0
- package/dist/rules/list/no-root-client-in-transaction.js +12 -0
- package/dist/rules/list/no-root-client-in-transaction.js.map +1 -0
- package/dist/rules/list/no-unindexed-filter.d.ts +4 -0
- package/dist/rules/list/no-unindexed-filter.d.ts.map +1 -0
- package/dist/rules/list/no-unindexed-filter.js +62 -0
- package/dist/rules/list/no-unindexed-filter.js.map +1 -0
- package/dist/rules/list/no-unsafe-prisma-raw.d.ts +4 -0
- package/dist/rules/list/no-unsafe-prisma-raw.d.ts.map +1 -0
- package/dist/rules/list/no-unsafe-prisma-raw.js +19 -0
- package/dist/rules/list/no-unsafe-prisma-raw.js.map +1 -0
- package/dist/rules/list/no-unsafe-raw-sql.d.ts +4 -0
- package/dist/rules/list/no-unsafe-raw-sql.d.ts.map +1 -0
- package/dist/rules/list/no-unsafe-raw-sql.js +7 -0
- package/dist/rules/list/no-unsafe-raw-sql.js.map +1 -0
- package/dist/rules/list/require-where-delete-many.d.ts +4 -0
- package/dist/rules/list/require-where-delete-many.d.ts.map +1 -0
- package/dist/rules/list/require-where-delete-many.js +25 -0
- package/dist/rules/list/require-where-delete-many.js.map +1 -0
- package/dist/rules/list/require-where-update-many.d.ts +4 -0
- package/dist/rules/list/require-where-update-many.d.ts.map +1 -0
- package/dist/rules/list/require-where-update-many.js +25 -0
- package/dist/rules/list/require-where-update-many.js.map +1 -0
- package/dist/rules/list/unstable-pagination.d.ts +4 -0
- package/dist/rules/list/unstable-pagination.d.ts.map +1 -0
- package/dist/rules/list/unstable-pagination.js +59 -0
- package/dist/rules/list/unstable-pagination.js.map +1 -0
- package/dist/rules/list/warn-cascade-delete.d.ts +4 -0
- package/dist/rules/list/warn-cascade-delete.d.ts.map +1 -0
- package/dist/rules/list/warn-cascade-delete.js +24 -0
- package/dist/rules/list/warn-cascade-delete.js.map +1 -0
- package/dist/rules/rules-list.d.ts +3 -0
- package/dist/rules/rules-list.d.ts.map +1 -0
- package/dist/rules/rules-list.js +133 -0
- package/dist/rules/rules-list.js.map +1 -0
- package/dist/rules/types/rules-options.types.d.ts +34 -0
- package/dist/rules/types/rules-options.types.d.ts.map +1 -0
- package/dist/rules/types/rules-options.types.js +2 -0
- package/dist/rules/types/rules-options.types.js.map +1 -0
- package/dist/rules/utils/is-array.d.ts +2 -0
- package/dist/rules/utils/is-array.d.ts.map +1 -0
- package/dist/rules/utils/is-array.js +4 -0
- package/dist/rules/utils/is-array.js.map +1 -0
- package/dist/rules/utils/is-expression-in-allowed-function.d.ts +3 -0
- package/dist/rules/utils/is-expression-in-allowed-function.d.ts.map +1 -0
- package/dist/rules/utils/is-expression-in-allowed-function.js +5 -0
- package/dist/rules/utils/is-expression-in-allowed-function.js.map +1 -0
- package/dist/rules/utils/is-expression-in-ignored-files.d.ts +2 -0
- package/dist/rules/utils/is-expression-in-ignored-files.d.ts.map +1 -0
- package/dist/rules/utils/is-expression-in-ignored-files.js +5 -0
- package/dist/rules/utils/is-expression-in-ignored-files.js.map +1 -0
- package/dist/rules/utils/is-expression-in-test-file.d.ts +2 -0
- package/dist/rules/utils/is-expression-in-test-file.d.ts.map +1 -0
- package/dist/rules/utils/is-expression-in-test-file.js +4 -0
- package/dist/rules/utils/is-expression-in-test-file.js.map +1 -0
- package/dist/rules/utils/is-expression-inside-constructor.d.ts +3 -0
- package/dist/rules/utils/is-expression-inside-constructor.d.ts.map +1 -0
- package/dist/rules/utils/is-expression-inside-constructor.js +4 -0
- package/dist/rules/utils/is-expression-inside-constructor.js.map +1 -0
- package/dist/rules/utils/is-expression-inside-function-like.d.ts +3 -0
- package/dist/rules/utils/is-expression-inside-function-like.d.ts.map +1 -0
- package/dist/rules/utils/is-expression-inside-function-like.js +4 -0
- package/dist/rules/utils/is-expression-inside-function-like.js.map +1 -0
- package/dist/rules/utils/is-object-empty.d.ts +2 -0
- package/dist/rules/utils/is-object-empty.d.ts.map +1 -0
- package/dist/rules/utils/is-object-empty.js +4 -0
- package/dist/rules/utils/is-object-empty.js.map +1 -0
- package/dist/rules/utils/is-object.d.ts +2 -0
- package/dist/rules/utils/is-object.d.ts.map +1 -0
- package/dist/rules/utils/is-object.js +4 -0
- package/dist/rules/utils/is-object.js.map +1 -0
- package/dist/rules/utils/is-template-node.d.ts +3 -0
- package/dist/rules/utils/is-template-node.d.ts.map +1 -0
- package/dist/rules/utils/is-template-node.js +9 -0
- package/dist/rules/utils/is-template-node.js.map +1 -0
- package/dist/types/ast.types.d.ts +35 -0
- package/dist/types/ast.types.d.ts.map +1 -0
- package/dist/types/ast.types.js +2 -0
- package/dist/types/ast.types.js.map +1 -0
- package/dist/types/prisma.types.d.ts +46 -0
- package/dist/types/prisma.types.d.ts.map +1 -0
- package/dist/types/prisma.types.js +2 -0
- package/dist/types/prisma.types.js.map +1 -0
- package/dist/types/rule.eslint.types.d.ts +22 -0
- package/dist/types/rule.eslint.types.d.ts.map +1 -0
- package/dist/types/rule.eslint.types.js +2 -0
- package/dist/types/rule.eslint.types.js.map +1 -0
- package/package.json +57 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Prismalyst
|
|
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,3 +1,210 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Prismalyst Core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Internal analysis engine and shared TypeScript contracts for the
|
|
4
|
+
[Prismalyst ESLint plugin](https://github.com/Prismalyst/eslint-plugin).
|
|
5
|
+
|
|
6
|
+
The core identifies Prisma calls and client construction through the TypeScript compiler API,
|
|
7
|
+
extracts their arguments and context, and evaluates rules. Analysis does not execute Prisma queries
|
|
8
|
+
or require a database connection.
|
|
9
|
+
|
|
10
|
+
## Responsibilities
|
|
11
|
+
|
|
12
|
+
| Core | ESLint plugin |
|
|
13
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
14
|
+
| Recognize Prisma expressions using type information. | Obtain the TypeScript program from ESLint parser services. |
|
|
15
|
+
| Parse arguments, source locations, and expression context. | Map between TypeScript and ESLint AST nodes. |
|
|
16
|
+
| Define rule behavior, option types, and registration. | Define messages, severity, option validation, defaults, and presets. |
|
|
17
|
+
| Return whether a rule triggers and which node to highlight. | Load schema metadata and report diagnostics through ESLint. |
|
|
18
|
+
|
|
19
|
+
The package exports ESM, CommonJS, and TypeScript declarations from `dist`. Changes to exported
|
|
20
|
+
contracts must be coordinated with the plugin.
|
|
21
|
+
|
|
22
|
+
## Development setup
|
|
23
|
+
|
|
24
|
+
Use **Node.js 24** and **npm** to match [CI](.github/workflows/ci.yml). Run these commands from
|
|
25
|
+
`prismalyst-core`:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm ci
|
|
29
|
+
npm run prisma:v7:generate
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The generation step creates the ignored test client in `src/tests/schemas/v7/generated/prisma`.
|
|
33
|
+
Repeat it after changing the [test schema](src/tests/schemas/v7/schema.prisma). Tests analyze source
|
|
34
|
+
fixtures and use in-memory schema metadata; database setup and migrations are not required.
|
|
35
|
+
|
|
36
|
+
The declared TypeScript peer range is `>=5.0.0 <7`. The current development dependency is TypeScript
|
|
37
|
+
6, and the AST fixtures cover Prisma 7. The peer range alone does not establish compatibility with
|
|
38
|
+
every Prisma generator or TypeScript version.
|
|
39
|
+
|
|
40
|
+
## Checks and commands
|
|
41
|
+
|
|
42
|
+
Run the same checks as CI before submitting a change:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
npm run lint
|
|
46
|
+
npm run typecheck
|
|
47
|
+
npm run test:ci
|
|
48
|
+
npm run build
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`typecheck` checks production source; `tsconfig.json` excludes the tests. The build emits declarations
|
|
52
|
+
and ESM files through TypeScript, then creates the CommonJS entry with Rollup.
|
|
53
|
+
|
|
54
|
+
| Command | Purpose |
|
|
55
|
+
| ----------------------------------------- | -------------------------------------------------------- |
|
|
56
|
+
| `npm run test:watch` | Run Vitest in watch mode. |
|
|
57
|
+
| `npm run test:ci -- src/tests/unit/rules` | Run rule unit tests. |
|
|
58
|
+
| `npm run test:ci -- src/tests/ast/v7` | Run Prisma expression and parser fixture tests. |
|
|
59
|
+
| `npm run watch` | Recompile TypeScript on source changes. |
|
|
60
|
+
| `npm run pack:check` | Preview the package contents without creating a tarball. |
|
|
61
|
+
|
|
62
|
+
## Project structure
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
src/
|
|
66
|
+
ast/ Prisma expression detection, conversion, and parsing helpers
|
|
67
|
+
constants/ Shared Prisma method groups
|
|
68
|
+
rules/
|
|
69
|
+
list/ Rule implementations
|
|
70
|
+
types/ Rule names and their option types
|
|
71
|
+
utils/ Shared rule helpers
|
|
72
|
+
rules-list.ts Rule registration and dispatch metadata
|
|
73
|
+
tests/
|
|
74
|
+
unit/ Isolated rule and utility tests
|
|
75
|
+
ast/v7/ Type-aware tests backed by TypeScript fixtures
|
|
76
|
+
schemas/v7/ Prisma schema and generated test client
|
|
77
|
+
types/ AST, Prisma expression, and rule contracts
|
|
78
|
+
index.ts Package exports
|
|
79
|
+
documentation/ One Markdown page per rule
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Core API
|
|
83
|
+
|
|
84
|
+
The public entry point is [`src/index.ts`](src/index.ts).
|
|
85
|
+
|
|
86
|
+
| Export | Purpose |
|
|
87
|
+
| ---------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
88
|
+
| `createAstUtils(ts, checker)` | Create node and Prisma helpers for a TypeScript analysis context. |
|
|
89
|
+
| `isPrismaExpression(node, program, astUtils)` | Identify a supported Prisma call or client construction. |
|
|
90
|
+
| `convertToPrismaExpression(node, program, astUtils)` | Extract arguments, locations, method, model, and context from a recognized node. |
|
|
91
|
+
| `getProperty(argument, path)` | Read a nested parsed value while preserving its source node. |
|
|
92
|
+
| `RULES_LIST` | Access registered rules, their functions, and dispatch metadata. |
|
|
93
|
+
| `ParsedValue` | Type for a parsed value and its source nodes. |
|
|
94
|
+
| `OptionsByRuleName` | Type mapping rule names to their option objects. |
|
|
95
|
+
| `RegisteredRule` | Type linking a registered rule name to its function and options. |
|
|
96
|
+
|
|
97
|
+
### Example: evaluating one rule
|
|
98
|
+
|
|
99
|
+
This fragment assumes `program` is the host's `ts.Program` and `node` comes from that program's AST
|
|
100
|
+
traversal. Create `astUtils` once per analysis context and reuse it while visiting nodes.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import {
|
|
104
|
+
RULES_LIST,
|
|
105
|
+
convertToPrismaExpression,
|
|
106
|
+
createAstUtils,
|
|
107
|
+
isPrismaExpression,
|
|
108
|
+
} from '@prismalyst/core';
|
|
109
|
+
import * as ts from 'typescript';
|
|
110
|
+
|
|
111
|
+
const astUtils = createAstUtils(ts, program.getTypeChecker());
|
|
112
|
+
const rule = RULES_LIST.find((candidate) => candidate.name === 'max-take');
|
|
113
|
+
|
|
114
|
+
if (
|
|
115
|
+
rule?.name === 'max-take' &&
|
|
116
|
+
ts.isCallExpression(node) &&
|
|
117
|
+
isPrismaExpression(node, program, astUtils)
|
|
118
|
+
) {
|
|
119
|
+
const expression = convertToPrismaExpression(node, program, astUtils);
|
|
120
|
+
const result = rule.function(expression, rule, { max: 100 });
|
|
121
|
+
|
|
122
|
+
if (result.isTriggered) {
|
|
123
|
+
console.log(rule.name, result.nodeToHighlight.getText());
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Call `convertToPrismaExpression` only after recognition. For general rule dispatch, use each rule's
|
|
129
|
+
`astType`, `methods`, and `requireSchema` metadata. Match `callExpression` rules to call nodes and
|
|
130
|
+
`newExpression` rules to construction nodes.
|
|
131
|
+
|
|
132
|
+
Rules return `{ isTriggered: false }` or `{ isTriggered: true, nodeToHighlight }`. The host turns the
|
|
133
|
+
reported TypeScript node into its own diagnostic. The core does not supply diagnostic messages or
|
|
134
|
+
autofixes.
|
|
135
|
+
|
|
136
|
+
Pass the complete option object as the third argument to a rule function, or `{}` for a rule without
|
|
137
|
+
options. The core provides no option defaults or runtime validation. Schema-dependent rules accept
|
|
138
|
+
Prisma's Data Model Meta Format (DMMF) document as the fourth argument and return no report when it is
|
|
139
|
+
absent. Schema discovery and loading belong to the host integration.
|
|
140
|
+
|
|
141
|
+
## Rules
|
|
142
|
+
|
|
143
|
+
Each page documents the shipped behavior, reported and accepted examples, options, schema
|
|
144
|
+
requirements, and known limits.
|
|
145
|
+
|
|
146
|
+
| Rule | Purpose | Requires schema |
|
|
147
|
+
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------- | --------------- |
|
|
148
|
+
| [limit-relation-loading](documentation/limit-relation-loading.md) | Require a separate `take` for each loaded list relation. | Yes |
|
|
149
|
+
| [max-take](documentation/max-take.md) | Require `take` and enforce its upper bound on `findMany`. | No |
|
|
150
|
+
| [no-deep-offset-pagination](documentation/no-deep-offset-pagination.md) | Keep `skip` within a configured range. | No |
|
|
151
|
+
| [no-disconnect-in-request-flow](documentation/no-disconnect-in-request-flow.md) | Restrict `$disconnect` to explicitly allowed files. | No |
|
|
152
|
+
| [no-dynamic-raw-query](documentation/no-dynamic-raw-query.md) | Report interpolated templates passed to unsafe raw SQL methods. | No |
|
|
153
|
+
| [no-floating-prisma-promise](documentation/no-floating-prisma-promise.md) | Report Prisma promises without a recognized await or return. | No |
|
|
154
|
+
| [no-prisma-client-in-function](documentation/no-prisma-client-in-function.md) | Restrict client creation inside functions and methods. | No |
|
|
155
|
+
| [no-query-in-loop](documentation/no-query-in-loop.md) | Report Prisma calls inside loops and selected array callbacks. | No |
|
|
156
|
+
| [no-root-client-in-transaction](documentation/no-root-client-in-transaction.md) | Require the transaction client inside interactive transactions. | No |
|
|
157
|
+
| [no-unindexed-filter](documentation/no-unindexed-filter.md) | Check top-level filter keys against schema indexes. | Yes |
|
|
158
|
+
| [no-unsafe-prisma-raw](documentation/no-unsafe-prisma-raw.md) | Restrict `Prisma.raw` arguments to resolved strings. | No |
|
|
159
|
+
| [no-unsafe-raw-sql](documentation/no-unsafe-raw-sql.md) | Report all calls to unsafe raw SQL methods. | No |
|
|
160
|
+
| [require-where-delete-many](documentation/require-where-delete-many.md) | Require a nonempty `where` for `deleteMany`. | No |
|
|
161
|
+
| [require-where-update-many](documentation/require-where-update-many.md) | Require a nonempty `where` for `updateMany`. | No |
|
|
162
|
+
| [unstable-pagination](documentation/unstable-pagination.md) | Require a unique ordering when `take` and `orderBy` are present. | Yes |
|
|
163
|
+
| [warn-cascade-delete](documentation/warn-cascade-delete.md) | Warn when deleting a model with incoming cascade relations. | Yes |
|
|
164
|
+
|
|
165
|
+
Examples in the rule pages are TypeScript fragments. They assume a configured `prisma` client,
|
|
166
|
+
generated Prisma exports where used, and models with the shown fields. `clientOptions` represents
|
|
167
|
+
the application's client construction options. “Not reported” refers only to that page's rule;
|
|
168
|
+
another rule or Prisma's types may still reject the code.
|
|
169
|
+
|
|
170
|
+
### Adding or changing a rule
|
|
171
|
+
|
|
172
|
+
1. Implement the behavior in `src/rules/list/<rule-name>.ts`. Keep parsing helpers in `src/ast` and
|
|
173
|
+
shared rule helpers in `src/rules/utils`.
|
|
174
|
+
2. Add the rule's option contract in `src/rules/types/rules-options.types.ts`, including an entry for
|
|
175
|
+
rules with no options.
|
|
176
|
+
3. Register the rule in `src/rules/rules-list.ts` with its name, AST type, methods, and schema
|
|
177
|
+
requirement. Add shared method names to `src/constants` if needed.
|
|
178
|
+
4. Add focused tests in `src/tests/unit/rules`. Cover reports, accepted cases, method filtering,
|
|
179
|
+
options, and the highlighted node. For parser changes, add or update a fixture and its tests in
|
|
180
|
+
`src/tests/ast/v7`.
|
|
181
|
+
5. Write or update `documentation/<rule-name>.md` and this rule table. Describe current behavior and
|
|
182
|
+
limits, and link the implementation and tests.
|
|
183
|
+
6. Coordinate the plugin changes: messages, option schemas and defaults in `src/rules/rules.map.ts`,
|
|
184
|
+
preset entries in `src/config/create-configs.ts`, and integration tests in the plugin repository.
|
|
185
|
+
|
|
186
|
+
## Core behavior
|
|
187
|
+
|
|
188
|
+
Method groups come from
|
|
189
|
+
[`prisma-methods.constants.ts`](src/constants/prisma-methods.constants.ts):
|
|
190
|
+
|
|
191
|
+
- **CRUD methods:** `create`, `createMany`, `createManyAndReturn`, `findUnique`, `findUniqueOrThrow`,
|
|
192
|
+
`findFirst`, `findFirstOrThrow`, `findMany`, `update`, `updateMany`, `updateManyAndReturn`, `upsert`,
|
|
193
|
+
`delete`, and `deleteMany`.
|
|
194
|
+
- **Read methods:** `findUnique`, `findUniqueOrThrow`, `findFirst`, `findFirstOrThrow`, and `findMany`.
|
|
195
|
+
- **All registered Prisma methods:** CRUD methods plus `count`, `aggregate`, `groupBy`, `$queryRaw`,
|
|
196
|
+
`$executeRaw`, `$queryRawUnsafe`, `$executeRawUnsafe`, `$transaction`, `raw`, `sql`, `join`, `empty`,
|
|
197
|
+
`$db`, `$connect`, and `$disconnect`.
|
|
198
|
+
|
|
199
|
+
Recognition depends on declarations from generated Prisma files. The current detection checks for
|
|
200
|
+
`.prisma/client` or `generated/prisma` in declaration paths. Model resolution also depends on generated
|
|
201
|
+
files under a `prisma/models` directory. Custom output layouts can therefore affect detection.
|
|
202
|
+
|
|
203
|
+
The core handles TypeScript call and construction expressions. Tagged template expressions are not
|
|
204
|
+
recognized directly. Parsed values can come from syntax, declarations, or types, and can remain
|
|
205
|
+
unknown. Individual rules decide how to handle those values; analysis does not prove runtime query
|
|
206
|
+
safety or database performance.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type * as TS from 'typescript';
|
|
2
|
+
export declare function createAstParser(ts: typeof TS): {
|
|
3
|
+
findVariableStatements: (sourceFile: TS.SourceFile) => TS.VariableStatement[];
|
|
4
|
+
findCallExpressions: (sourceFile: TS.SourceFile) => TS.CallExpression[];
|
|
5
|
+
findNewExpressions: (sourceFile: TS.SourceFile) => TS.NewExpression[];
|
|
6
|
+
};
|
|
7
|
+
export type AstParser = ReturnType<typeof createAstParser>;
|
|
8
|
+
//# sourceMappingURL=create-ast-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-ast-parser.d.ts","sourceRoot":"","sources":["../../../src/ast/parser/create-ast-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtC,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE;yCACC,EAAE,CAAC,UAAU,KAAG,EAAE,CAAC,iBAAiB,EAAE;sCAgBzC,EAAE,CAAC,UAAU,KAAG,EAAE,CAAC,cAAc,EAAE;qCAgBpC,EAAE,CAAC,UAAU,KAAG,EAAE,CAAC,aAAa,EAAE;EAqB3E;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function createAstParser(ts) {
|
|
2
|
+
function findVariableStatements(sourceFile) {
|
|
3
|
+
const statements = [];
|
|
4
|
+
function visit(node) {
|
|
5
|
+
if (ts.isVariableStatement(node)) {
|
|
6
|
+
statements.push(node);
|
|
7
|
+
}
|
|
8
|
+
ts.forEachChild(node, visit);
|
|
9
|
+
}
|
|
10
|
+
visit(sourceFile);
|
|
11
|
+
return statements;
|
|
12
|
+
}
|
|
13
|
+
function findCallExpressions(sourceFile) {
|
|
14
|
+
const calls = [];
|
|
15
|
+
function visit(node) {
|
|
16
|
+
if (ts.isCallExpression(node)) {
|
|
17
|
+
calls.push(node);
|
|
18
|
+
}
|
|
19
|
+
ts.forEachChild(node, visit);
|
|
20
|
+
}
|
|
21
|
+
visit(sourceFile);
|
|
22
|
+
return calls;
|
|
23
|
+
}
|
|
24
|
+
function findNewExpressions(sourceFile) {
|
|
25
|
+
const calls = [];
|
|
26
|
+
function visit(node) {
|
|
27
|
+
if (ts.isNewExpression(node)) {
|
|
28
|
+
calls.push(node);
|
|
29
|
+
}
|
|
30
|
+
ts.forEachChild(node, visit);
|
|
31
|
+
}
|
|
32
|
+
visit(sourceFile);
|
|
33
|
+
return calls;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
findVariableStatements,
|
|
37
|
+
findCallExpressions,
|
|
38
|
+
findNewExpressions,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=create-ast-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-ast-parser.js","sourceRoot":"","sources":["../../../src/ast/parser/create-ast-parser.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,EAAa;IAC3C,SAAS,sBAAsB,CAAC,UAAyB;QACvD,MAAM,UAAU,GAA2B,EAAE,CAAC;QAE9C,SAAS,KAAK,CAAC,IAAa;YAC1B,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC;YAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,CAAC;QAElB,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,SAAS,mBAAmB,CAAC,UAAyB;QACpD,MAAM,KAAK,GAAwB,EAAE,CAAC;QAEtC,SAAS,KAAK,CAAC,IAAa;YAC1B,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;YAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,CAAC;QAElB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS,kBAAkB,CAAC,UAAyB;QACnD,MAAM,KAAK,GAAuB,EAAE,CAAC;QAErC,SAAS,KAAK,CAAC,IAAa;YAC1B,IAAI,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;YAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,CAAC;QAElB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO;QACL,sBAAsB;QACtB,mBAAmB;QACnB,kBAAkB;KACnB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type * as TS from 'typescript';
|
|
2
|
+
import { PrismaExpression, PrismaNodeExpression } from '../../types/prisma.types.js';
|
|
3
|
+
import { AstUtils } from '../utils/create-ast-utils.js';
|
|
4
|
+
export declare function convertToPrismaExpression(node: PrismaNodeExpression, program: TS.Program, { node: nodeUtils, prisma: prismaUtils }: AstUtils): PrismaExpression;
|
|
5
|
+
//# sourceMappingURL=convert-to-prisma-expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert-to-prisma-expression.d.ts","sourceRoot":"","sources":["../../../src/ast/prisma/convert-to-prisma-expression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAErF,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,EAAE,CAAC,OAAO,EACnB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,QAAQ,GACjD,gBAAgB,CA+DlB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { defineMethod } from './utils/define-method.js';
|
|
2
|
+
export function convertToPrismaExpression(node, program, { node: nodeUtils, prisma: prismaUtils }) {
|
|
3
|
+
const method = defineMethod(node, nodeUtils);
|
|
4
|
+
const { line: startLine, character: startCharacter } = node
|
|
5
|
+
.getSourceFile()
|
|
6
|
+
.getLineAndCharacterOfPosition(node.getStart());
|
|
7
|
+
const { line: endLine, character: endCharacter } = node
|
|
8
|
+
.getSourceFile()
|
|
9
|
+
.getLineAndCharacterOfPosition(node.getEnd());
|
|
10
|
+
const nodeLength = node.getWidth();
|
|
11
|
+
const filename = node.getSourceFile().fileName;
|
|
12
|
+
const args = node.arguments ?? [];
|
|
13
|
+
const checker = program.getTypeChecker();
|
|
14
|
+
const isFloatingPrismaPromise = prismaUtils.isFloatingPrismaPromise(node, node.parent, checker);
|
|
15
|
+
const isExpressionInsideTransaction = prismaUtils.isInsidePrismaTransaction(node);
|
|
16
|
+
const transactionType = prismaUtils.resolveTransactionType(node);
|
|
17
|
+
const clientType = prismaUtils.resolveClientType(node, checker);
|
|
18
|
+
const model = prismaUtils.resolveModelName(node, checker);
|
|
19
|
+
const isExpressionInsideLoop = nodeUtils.isInsideLoop(node);
|
|
20
|
+
const isExpressionInsideFunction = nodeUtils.isInsideFunction(node);
|
|
21
|
+
const isExpressionInsideClassMethod = nodeUtils.isInsideClassMethod(node);
|
|
22
|
+
const isExpressionInsideAwaitExpression = nodeUtils.isInsideAwaitExpression(node);
|
|
23
|
+
const parentFunctionName = nodeUtils.getParentFunctionName(node);
|
|
24
|
+
const parentMethodName = nodeUtils.getParentMethodName(node);
|
|
25
|
+
return {
|
|
26
|
+
node: node,
|
|
27
|
+
range: {
|
|
28
|
+
start: {
|
|
29
|
+
line: startLine,
|
|
30
|
+
character: startCharacter,
|
|
31
|
+
},
|
|
32
|
+
end: {
|
|
33
|
+
line: endLine,
|
|
34
|
+
character: endCharacter,
|
|
35
|
+
},
|
|
36
|
+
length: nodeLength,
|
|
37
|
+
},
|
|
38
|
+
filename,
|
|
39
|
+
expressionContext: {
|
|
40
|
+
isExpressionInsideLoop,
|
|
41
|
+
isExpressionInsideFunction,
|
|
42
|
+
isExpressionInsideClassMethod,
|
|
43
|
+
isExpressionInsideAwaitExpression,
|
|
44
|
+
parentFunctionName,
|
|
45
|
+
parentMethodName,
|
|
46
|
+
},
|
|
47
|
+
prismaContext: {
|
|
48
|
+
isFloatingPrismaPromise,
|
|
49
|
+
isExpressionInsideTransaction,
|
|
50
|
+
transactionType,
|
|
51
|
+
clientType,
|
|
52
|
+
model,
|
|
53
|
+
},
|
|
54
|
+
method,
|
|
55
|
+
args: args.map((argument) => nodeUtils.parseValue(argument, checker)),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=convert-to-prisma-expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert-to-prisma-expression.js","sourceRoot":"","sources":["../../../src/ast/prisma/convert-to-prisma-expression.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,MAAM,UAAU,yBAAyB,CACvC,IAA0B,EAC1B,OAAmB,EACnB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAY;IAElD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAE7C,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,IAAI;SACxD,aAAa,EAAE;SACf,6BAA6B,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAElD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI;SACpD,aAAa,EAAE;SACf,6BAA6B,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAEhD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC;IAE/C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;IAElC,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAEzC,MAAM,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChG,MAAM,6BAA6B,GAAG,WAAW,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAClF,MAAM,eAAe,GAAG,WAAW,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,WAAW,CAAC,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,WAAW,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE1D,MAAM,sBAAsB,GAAG,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,0BAA0B,GAAG,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,6BAA6B,GAAG,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC1E,MAAM,iCAAiC,GAAG,SAAS,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;IAClF,MAAM,kBAAkB,GAAG,SAAS,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,gBAAgB,GAAG,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAE7D,OAAO;QACL,IAAI,EAAE,IAAI;QACV,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS;gBACf,SAAS,EAAE,cAAc;aAC1B;YACD,GAAG,EAAE;gBACH,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,YAAY;aACxB;YACD,MAAM,EAAE,UAAU;SACnB;QACD,QAAQ;QACR,iBAAiB,EAAE;YACjB,sBAAsB;YACtB,0BAA0B;YAC1B,6BAA6B;YAC7B,iCAAiC;YACjC,kBAAkB;YAClB,gBAAgB;SACjB;QACD,aAAa,EAAE;YACb,uBAAuB;YACvB,6BAA6B;YAC7B,eAAe;YACf,UAAU;YACV,KAAK;SACN;QACD,MAAM;QACN,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;KACtE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type * as TS from 'typescript';
|
|
2
|
+
import { PrismaNodeExpression } from '../../types/prisma.types.js';
|
|
3
|
+
import { AstUtils } from '../utils/create-ast-utils.js';
|
|
4
|
+
export declare function isPrismaExpression(node: TS.Node, program: TS.Program, { node: nodeUtils, prisma: prismaUtils }: AstUtils): node is PrismaNodeExpression;
|
|
5
|
+
//# sourceMappingURL=is-prisma-expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-prisma-expression.d.ts","sourceRoot":"","sources":["../../../src/ast/prisma/is-prisma-expression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAKxD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,OAAO,EAAE,EAAE,CAAC,OAAO,EACnB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,QAAQ,GACjD,IAAI,IAAI,oBAAoB,CAgE9B"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { removeQuotes } from './utils/remove-quotes.js';
|
|
2
|
+
import { PRISMA_METHODS } from '../../constants/prisma-methods.constants.js';
|
|
3
|
+
export function isPrismaExpression(node, program, { node: nodeUtils, prisma: prismaUtils }) {
|
|
4
|
+
if (nodeUtils.isCallExpression(node)) {
|
|
5
|
+
const isPropertyAccess = nodeUtils.isPropertyAccessExpression(node.expression);
|
|
6
|
+
const isElementAccess = nodeUtils.isElementAccessExpression(node.expression);
|
|
7
|
+
if (isPropertyAccess) {
|
|
8
|
+
const methodNode = node.expression.name;
|
|
9
|
+
const method = methodNode.text;
|
|
10
|
+
if (!PRISMA_METHODS.has(method)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const checker = program.getTypeChecker();
|
|
14
|
+
const symbol = checker.getSymbolAtLocation(methodNode);
|
|
15
|
+
const declarations = symbol?.getDeclarations() ?? [];
|
|
16
|
+
const isGeneratedPrismaMethod = prismaUtils.isGeneratedPrismaFile(declarations);
|
|
17
|
+
if (isGeneratedPrismaMethod)
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (isElementAccess) {
|
|
21
|
+
const methodNode = node.expression.argumentExpression;
|
|
22
|
+
const method = removeQuotes(methodNode.getText());
|
|
23
|
+
if (!PRISMA_METHODS.has(method)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const checker = program.getTypeChecker();
|
|
27
|
+
const symbol = checker.getSymbolAtLocation(methodNode);
|
|
28
|
+
const declarations = symbol?.getDeclarations() ?? [];
|
|
29
|
+
const isGeneratedPrismaMethod = prismaUtils.isGeneratedPrismaFile(declarations);
|
|
30
|
+
if (isGeneratedPrismaMethod)
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (nodeUtils.isNewExpression(node)) {
|
|
35
|
+
const checker = program.getTypeChecker();
|
|
36
|
+
const symbolLocation = nodeUtils.isPropertyAccessExpression(node.expression)
|
|
37
|
+
? node.expression.name
|
|
38
|
+
: node.expression;
|
|
39
|
+
const symbol = checker.getSymbolAtLocation(symbolLocation);
|
|
40
|
+
if (!symbol)
|
|
41
|
+
return false;
|
|
42
|
+
const resolvedSymbol = nodeUtils.resolveAliasedSymbol(symbol, checker);
|
|
43
|
+
if (resolvedSymbol.getName() !== 'PrismaClient')
|
|
44
|
+
return false;
|
|
45
|
+
const declarations = resolvedSymbol.getDeclarations() ?? [];
|
|
46
|
+
const isGeneratedPrismaClient = prismaUtils.isGeneratedPrismaFile(declarations);
|
|
47
|
+
if (isGeneratedPrismaClient)
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=is-prisma-expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-prisma-expression.js","sourceRoot":"","sources":["../../../src/ast/prisma/is-prisma-expression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { PrismaNodeExpression } from '../../../types/prisma.types.js';
|
|
2
|
+
import { AstUtils } from '../../utils/create-ast-utils.js';
|
|
3
|
+
export declare function defineMethod(node: PrismaNodeExpression, nodeUtils: AstUtils['node']): string;
|
|
4
|
+
//# sourceMappingURL=define-method.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-method.d.ts","sourceRoot":"","sources":["../../../../src/ast/prisma/utils/define-method.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAEtE,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAkB3D,wBAAgB,YAAY,CAAC,IAAI,EAAE,oBAAoB,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,UAUnF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { removeQuotes } from './remove-quotes.js';
|
|
2
|
+
function isPropertyAccess(expression, nodeUtils) {
|
|
3
|
+
return nodeUtils.isPropertyAccessExpression(expression);
|
|
4
|
+
}
|
|
5
|
+
function isElementAccess(expression, nodeUtils) {
|
|
6
|
+
return nodeUtils.isElementAccessExpression(expression);
|
|
7
|
+
}
|
|
8
|
+
export function defineMethod(node, nodeUtils) {
|
|
9
|
+
const isNewExpression = nodeUtils.isNewExpression(node);
|
|
10
|
+
if (isNewExpression)
|
|
11
|
+
return 'PrismaClient';
|
|
12
|
+
if (isPropertyAccess(node.expression, nodeUtils))
|
|
13
|
+
return node.expression.name.text;
|
|
14
|
+
if (isElementAccess(node.expression, nodeUtils))
|
|
15
|
+
return removeQuotes(node.expression.argumentExpression.getText());
|
|
16
|
+
return 'unknown';
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=define-method.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-method.js","sourceRoot":"","sources":["../../../../src/ast/prisma/utils/define-method.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,SAAS,gBAAgB,CACvB,UAAoE,EACpE,SAA2B;IAE3B,OAAO,SAAS,CAAC,0BAA0B,CAAC,UAAU,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,eAAe,CACtB,UAAoE,EACpE,SAA2B;IAE3B,OAAO,SAAS,CAAC,yBAAyB,CAAC,UAAU,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAA0B,EAAE,SAA2B;IAClF,MAAM,eAAe,GAAG,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAExD,IAAI,eAAe;QAAE,OAAO,cAAc,CAAC;IAE3C,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;IACnF,IAAI,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC;QAC7C,OAAO,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC,CAAC;IAEpE,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-quotes.d.ts","sourceRoot":"","sources":["../../../../src/ast/prisma/utils/remove-quotes.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUlD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function removeQuotes(value) {
|
|
2
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
3
|
+
(value.startsWith("'") && value.endsWith("'")) ||
|
|
4
|
+
(value.startsWith('`') && value.endsWith('`'))) {
|
|
5
|
+
return value.slice(1, -1);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=remove-quotes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-quotes.js","sourceRoot":"","sources":["../../../../src/ast/prisma/utils/remove-quotes.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type * as TS from 'typescript';
|
|
2
|
+
export declare function createAstUtils(ts: typeof TS, checker: TS.TypeChecker): {
|
|
3
|
+
node: {
|
|
4
|
+
isCallExpression: (node: TS.Node) => node is TS.CallExpression;
|
|
5
|
+
isPropertyAccessExpression: (node: TS.Node) => node is TS.PropertyAccessExpression;
|
|
6
|
+
isElementAccessExpression: (node: TS.Node) => node is TS.ElementAccessExpression;
|
|
7
|
+
isNewExpression: (node: TS.Node) => node is TS.NewExpression;
|
|
8
|
+
getPropertyName: (name: TS.PropertyName) => string;
|
|
9
|
+
resolveAliasedSymbol: (symbol: TS.Symbol, checker: TS.TypeChecker) => TS.Symbol;
|
|
10
|
+
getParentFunctionName: (node: TS.Node) => string | null;
|
|
11
|
+
getParentMethodName: (node: TS.Node) => string | null;
|
|
12
|
+
isInsideLoop: (node: TS.Node) => boolean;
|
|
13
|
+
isInsideFunction: (node: TS.Node) => boolean;
|
|
14
|
+
isInsideClassMethod: (node: TS.Node) => boolean;
|
|
15
|
+
isInsideAwaitExpression: (node: TS.Node) => boolean;
|
|
16
|
+
parseArgument: (node: TS.CallExpression | TS.NewExpression, index?: number) => import("../../index.js").ParsedValue | undefined;
|
|
17
|
+
parseObject: (node: TS.ObjectLiteralExpression) => import("../../index.js").ParsedValue<import("../../types/ast.types.js").AstObject>;
|
|
18
|
+
parseValue: (node: TS.Expression, checker: TS.TypeChecker) => import("../../index.js").ParsedValue;
|
|
19
|
+
};
|
|
20
|
+
prisma: {
|
|
21
|
+
resolveTransactionType: (node: TS.Node) => import("../../types/prisma.types.js").TransactionType | null;
|
|
22
|
+
resolveClientType: (node: TS.Node, checker: TS.TypeChecker) => import("../../types/prisma.types.js").ClientType;
|
|
23
|
+
isGeneratedPrismaFile: (declarations: TS.Declaration[]) => boolean;
|
|
24
|
+
isFloatingPrismaPromise: (node: TS.Node, parent: TS.Node, checker: TS.TypeChecker) => boolean;
|
|
25
|
+
isInsidePrismaTransaction: (node: TS.Node) => boolean;
|
|
26
|
+
resolveModelName: (node: import("../../types/prisma.types.js").PrismaNodeExpression, checker: TS.TypeChecker) => string | null;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type AstUtils = ReturnType<typeof createAstUtils>;
|
|
30
|
+
//# sourceMappingURL=create-ast-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-ast-utils.d.ts","sourceRoot":"","sources":["../../../src/ast/utils/create-ast-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAKtC,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW;IAEjE,IAAI;;;;;;;;;;;;;;;;;IACJ,MAAM;;;;;;;;EAET;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createNodeUtils } from './create-node-utils.js';
|
|
2
|
+
import { createPrismaUtils } from './create-prisma-utils.js';
|
|
3
|
+
export function createAstUtils(ts, checker) {
|
|
4
|
+
return {
|
|
5
|
+
node: createNodeUtils(ts, checker),
|
|
6
|
+
prisma: createPrismaUtils(ts),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=create-ast-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-ast-utils.js","sourceRoot":"","sources":["../../../src/ast/utils/create-ast-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,UAAU,cAAc,CAAC,EAAa,EAAE,OAAuB;IACnE,OAAO;QACL,IAAI,EAAE,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC;QAClC,MAAM,EAAE,iBAAiB,CAAC,EAAE,CAAC;KAC9B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as TS from 'typescript';
|
|
2
|
+
import { AstObject, ParsedValue } from '../../types/ast.types.js';
|
|
3
|
+
export declare function createNodeUtils(ts: typeof TS, checker: TS.TypeChecker): {
|
|
4
|
+
isCallExpression: (node: TS.Node) => node is TS.CallExpression;
|
|
5
|
+
isPropertyAccessExpression: (node: TS.Node) => node is TS.PropertyAccessExpression;
|
|
6
|
+
isElementAccessExpression: (node: TS.Node) => node is TS.ElementAccessExpression;
|
|
7
|
+
isNewExpression: (node: TS.Node) => node is TS.NewExpression;
|
|
8
|
+
getPropertyName: (name: TS.PropertyName) => string;
|
|
9
|
+
resolveAliasedSymbol: (symbol: TS.Symbol, checker: TS.TypeChecker) => TS.Symbol;
|
|
10
|
+
getParentFunctionName: (node: TS.Node) => string | null;
|
|
11
|
+
getParentMethodName: (node: TS.Node) => string | null;
|
|
12
|
+
isInsideLoop: (node: TS.Node) => boolean;
|
|
13
|
+
isInsideFunction: (node: TS.Node) => boolean;
|
|
14
|
+
isInsideClassMethod: (node: TS.Node) => boolean;
|
|
15
|
+
isInsideAwaitExpression: (node: TS.Node) => boolean;
|
|
16
|
+
parseArgument: (node: TS.CallExpression | TS.NewExpression, index?: number) => ParsedValue | undefined;
|
|
17
|
+
parseObject: (node: TS.ObjectLiteralExpression) => ParsedValue<AstObject>;
|
|
18
|
+
parseValue: (node: TS.Expression, checker: TS.TypeChecker) => ParsedValue;
|
|
19
|
+
};
|
|
20
|
+
export type NodeUtils = ReturnType<typeof createNodeUtils>;
|
|
21
|
+
//# sourceMappingURL=create-node-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-node-utils.d.ts","sourceRoot":"","sources":["../../../src/ast/utils/create-node-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAgB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIhF,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW;6BAGpC,EAAE,CAAC,IAAI;uCAIG,EAAE,CAAC,IAAI;sCAIR,EAAE,CAAC,IAAI;4BAIjB,EAAE,CAAC,IAAI;4BAIP,EAAE,CAAC,YAAY,KAAG,MAAM;mCAQjB,EAAE,CAAC,MAAM,WAAW,EAAE,CAAC,WAAW,KAAG,EAAE,CAAC,MAAM;kCAI/C,EAAE,CAAC,IAAI,KAAG,MAAM,GAAG,IAAI;gCAczB,EAAE,CAAC,IAAI,KAAG,MAAM,GAAG,IAAI;yBAc9B,EAAE,CAAC,IAAI,KAAG,OAAO;6BAmCb,EAAE,CAAC,IAAI,KAAG,OAAO;gCAiBd,EAAE,CAAC,IAAI,KAAG,OAAO;oCAiBb,EAAE,CAAC,IAAI,KAAG,OAAO;0BAehD,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,aAAa,qBAEzC,WAAW,GAAG,SAAS;wBAUC,EAAE,CAAC,uBAAuB,KAAG,WAAW,CAAC,SAAS,CAAC;uBAwKpD,EAAE,CAAC,UAAU,WAAW,EAAE,CAAC,WAAW,KAAG,WAAW;EAoG/E;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|