@sailshq/language-server 0.3.2 → 0.5.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/SailsParser.js +125 -33
- package/completions/helper-inputs-completion.js +8 -3
- package/completions/model-attribute-props-completion.js +20 -19
- package/completions/model-attributes-completion.js +356 -0
- package/completions/model-methods-completion.js +24 -1
- package/go-to-definitions/go-to-action.js +82 -29
- package/go-to-definitions/go-to-helper-input.js +112 -0
- package/go-to-definitions/go-to-model-attribute.js +302 -0
- package/go-to-definitions/go-to-model.js +4 -3
- package/go-to-definitions/go-to-page.js +40 -26
- package/go-to-definitions/go-to-view.js +42 -24
- package/index.js +12 -4
- package/package.json +1 -1
- package/validators/validate-action-exist.js +54 -15
- package/validators/validate-data-type.js +88 -25
- package/validators/validate-helper-input-exist.js +98 -32
- package/validators/validate-model-attribute-exist.js +184 -52
- package/validators/validate-model-exist.js +14 -0
- package/validators/validate-required-helper-input.js +100 -39
|
@@ -1,49 +1,110 @@
|
|
|
1
1
|
const lsp = require('vscode-languageserver/node')
|
|
2
|
+
const acorn = require('acorn')
|
|
3
|
+
const walk = require('acorn-walk')
|
|
2
4
|
|
|
3
5
|
module.exports = function validateRequiredHelperInput(document, typeMap) {
|
|
4
6
|
const diagnostics = []
|
|
5
7
|
const text = document.getText()
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
9
|
+
try {
|
|
10
|
+
const ast = acorn.parse(text, {
|
|
11
|
+
ecmaVersion: 'latest',
|
|
12
|
+
sourceType: 'module'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
walk.simple(ast, {
|
|
16
|
+
CallExpression(node) {
|
|
17
|
+
// Match sails.helpers.foo.bar.with({ ... })
|
|
18
|
+
if (
|
|
19
|
+
node.callee &&
|
|
20
|
+
node.callee.type === 'MemberExpression' &&
|
|
21
|
+
node.callee.property.name === 'with' &&
|
|
22
|
+
node.callee.object &&
|
|
23
|
+
node.callee.object.type === 'MemberExpression'
|
|
24
|
+
) {
|
|
25
|
+
// Extract helper path from sails.helpers.foo.bar
|
|
26
|
+
const helperPath = extractHelperPath(node.callee.object)
|
|
27
|
+
if (!helperPath) return
|
|
28
|
+
|
|
29
|
+
const helperInfo = typeMap.helpers && typeMap.helpers[helperPath]
|
|
30
|
+
if (!helperInfo || !helperInfo.inputs) return
|
|
31
|
+
|
|
32
|
+
// Get the object argument to .with()
|
|
33
|
+
const objArg = node.arguments[0]
|
|
34
|
+
if (!objArg || objArg.type !== 'ObjectExpression') return
|
|
35
|
+
|
|
36
|
+
// Collect provided keys (handles both regular and shorthand properties)
|
|
37
|
+
const providedKeys = new Set()
|
|
38
|
+
for (const prop of objArg.properties) {
|
|
39
|
+
if (prop.type === 'Property') {
|
|
40
|
+
if (prop.key.type === 'Identifier') {
|
|
41
|
+
providedKeys.add(prop.key.name)
|
|
42
|
+
} else if (prop.key.type === 'Literal') {
|
|
43
|
+
providedKeys.add(prop.key.value)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check for missing required inputs
|
|
49
|
+
for (const [inputKey, inputDef] of Object.entries(
|
|
50
|
+
helperInfo.inputs
|
|
51
|
+
)) {
|
|
52
|
+
const requiredValue =
|
|
53
|
+
inputDef?.value?.required?.value ?? inputDef?.required
|
|
54
|
+
const isRequired =
|
|
55
|
+
requiredValue === true || requiredValue === 'true'
|
|
56
|
+
if (isRequired && !providedKeys.has(inputKey)) {
|
|
57
|
+
diagnostics.push(
|
|
58
|
+
lsp.Diagnostic.create(
|
|
59
|
+
lsp.Range.create(
|
|
60
|
+
document.positionAt(objArg.start),
|
|
61
|
+
document.positionAt(objArg.end)
|
|
62
|
+
),
|
|
63
|
+
`Missing required input '${inputKey}' for helper '${helperPath}'.`,
|
|
64
|
+
lsp.DiagnosticSeverity.Error,
|
|
65
|
+
'sails-lsp'
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
45
71
|
}
|
|
46
|
-
}
|
|
72
|
+
})
|
|
73
|
+
} catch (error) {
|
|
74
|
+
// Ignore parse errors
|
|
47
75
|
}
|
|
76
|
+
|
|
48
77
|
return diagnostics
|
|
49
78
|
}
|
|
79
|
+
|
|
80
|
+
function extractHelperPath(node) {
|
|
81
|
+
// Walk up the member expression to extract the full helper path
|
|
82
|
+
const segments = []
|
|
83
|
+
let current = node
|
|
84
|
+
|
|
85
|
+
// Collect all segments until we reach sails.helpers
|
|
86
|
+
while (current && current.type === 'MemberExpression') {
|
|
87
|
+
if (current.property && current.property.type === 'Identifier') {
|
|
88
|
+
const propName = current.property.name
|
|
89
|
+
// Stop when we reach 'helpers'
|
|
90
|
+
if (propName === 'helpers') {
|
|
91
|
+
// Check if the object is 'sails'
|
|
92
|
+
if (
|
|
93
|
+
current.object &&
|
|
94
|
+
current.object.type === 'Identifier' &&
|
|
95
|
+
current.object.name === 'sails'
|
|
96
|
+
) {
|
|
97
|
+
// Valid sails.helpers path found
|
|
98
|
+
const toKebab = (s) =>
|
|
99
|
+
s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
|
|
100
|
+
return segments.map(toKebab).join('/')
|
|
101
|
+
}
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
segments.unshift(propName)
|
|
105
|
+
}
|
|
106
|
+
current = current.object
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null
|
|
110
|
+
}
|