-
-
Notifications
You must be signed in to change notification settings - Fork 594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Type checking for template expressions #681
Changes from 44 commits
e98740d
e4b1549
dc9d8c1
912b134
a68726e
a66b78c
a10d492
6865c30
34a58dc
3b852e4
de2e859
c6a38a0
b90f96a
1f02005
deaa8a7
68b8200
6fc1e52
60562ef
3138243
7696040
b7a805b
a8ef51e
0c97e75
1642733
ce43c75
55191cb
bd98089
9f5304d
f2f3236
bca4ad4
5d6d62f
0fd53f0
0542c1a
653bf3d
aabba0f
31d88bf
bec63eb
71fd337
4e73004
b8be711
889d1ce
39c807b
30ddc84
e26d5d9
79dc034
ef7978a
c143dfa
d0713d1
eff560a
f428765
2923e08
96797c0
de998c6
532074a
ab4a5cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,11 +44,14 @@ | |
"vscode-languageserver": "^5.3.0-next.4", | ||
"vscode-languageserver-types": "^3.15.0-next.1", | ||
"vscode-uri": "^1.0.1", | ||
"vue-eslint-parser": "^6.0.3", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm using v6 now. There are a few changes in the AST format. Take a look at the transformer for Todo comments. |
||
"vue-onsenui-helper-json": "^1.0.2", | ||
"vuetify-helper-json": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"@types/eslint": "^4.16.5", | ||
"@types/eslint-scope": "^3.7.0", | ||
"@types/eslint-visitor-keys": "^1.0.0", | ||
"@types/glob": "^7.1.0", | ||
"@types/js-beautify": "^1.8.0", | ||
"@types/lodash": "^4.14.118", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,41 @@ | ||
// this bridge file will be injected into TypeScript service | ||
import { renderHelperName, componentHelperName, iterationHelperName } from './transformTemplate'; | ||
|
||
// This bridge file will be injected into TypeScript language service | ||
// it enable type checking and completion, yet still preserve precise option type | ||
|
||
export const moduleName = 'vue-editor-bridge'; | ||
|
||
export const fileName = 'vue-temp/vue-editor-bridge.ts'; | ||
|
||
export const oldContent = ` | ||
const renderHelpers = ` | ||
export declare const ${renderHelperName}: { | ||
<T>(Component: (new (...args: any[]) => T), fn: (this: T) => any): any; | ||
}; | ||
export declare const ${componentHelperName}: { | ||
(tag: string, data: any, children: any[]): any; | ||
}; | ||
export declare const ${iterationHelperName}: { | ||
<T>(list: T[], fn: (value: T, index: number) => any): any; | ||
<T>(obj: { [key: string]: T }, fn: (value: T, key: string, index: number) => any): any; | ||
(num: number, fn: (value: number) => any): any; | ||
<T>(obj: object, fn: (value: any, key: string, index: number) => any): any; | ||
}; | ||
`; | ||
|
||
export const oldContent = | ||
` | ||
import Vue from 'vue'; | ||
export interface GeneralOption extends Vue.ComponentOptions<Vue> { | ||
[key: string]: any; | ||
} | ||
export default function bridge<T>(t: T & GeneralOption): T { | ||
return t; | ||
}`; | ||
} | ||
` + renderHelpers; | ||
|
||
export const content = ` | ||
export const content = | ||
` | ||
import Vue from 'vue'; | ||
const func = Vue.extend; | ||
export default func; | ||
`; | ||
` + renderHelpers; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,35 +102,73 @@ export async function getJavascriptMode( | |
}, | ||
|
||
doValidation(doc: TextDocument): Diagnostic[] { | ||
const { scriptDoc, service } = updateCurrentTextDocument(doc); | ||
if (!languageServiceIncludesFile(service, doc.uri)) { | ||
return []; | ||
const templateDiags = getTemplateDiagnostics(); | ||
const scriptDiags = getScriptDiagnostics(); | ||
return [...templateDiags, ...scriptDiags]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Decided to keep the diagnostics calculation all in JS mode. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @octref I realized this might cause an edge case. When we do not have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ktsn When you don't have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mainly detecting some typos. For example, sometimes I have some typo in template expression like: <template>
<ChildComp @click="$('click')" /> <!-- <= typo of $emit('click') -->
</template> Another example is when we use scoped slot component (scoped slot variables not handled currently though): <template>
<apollo-query :query="..." v-slot="{ isLoading, result }">
<-- typo of v-if="isLoading" -->
<div v-if="loading">Loading...</div>
<div v-else-if="result.data">{{ result.data }}</div>
</apollo-query>
</template> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll open a new issue for this. Let's keep these two things separate:
|
||
|
||
function getScriptDiagnostics(): Diagnostic[] { | ||
const { scriptDoc, service } = updateCurrentTextDocument(doc); | ||
if (!languageServiceIncludesFile(service, doc.uri)) { | ||
return []; | ||
} | ||
|
||
const fileFsPath = getFileFsPath(doc.uri); | ||
const rawScriptDiagnostics = [ | ||
...service.getSyntacticDiagnostics(fileFsPath), | ||
...service.getSemanticDiagnostics(fileFsPath) | ||
]; | ||
|
||
return rawScriptDiagnostics.map(diag => { | ||
const tags: DiagnosticTag[] = []; | ||
|
||
if (diag.reportsUnnecessary) { | ||
tags.push(DiagnosticTag.Unnecessary); | ||
} | ||
|
||
// syntactic/semantic diagnostic always has start and length | ||
// so we can safely cast diag to TextSpan | ||
return <Diagnostic>{ | ||
range: convertRange(scriptDoc, diag as ts.TextSpan), | ||
severity: DiagnosticSeverity.Error, | ||
message: tsModule.flattenDiagnosticMessageText(diag.messageText, '\n'), | ||
tags, | ||
code: diag.code, | ||
source: 'Vetur' | ||
}; | ||
}); | ||
} | ||
|
||
const fileFsPath = getFileFsPath(doc.uri); | ||
const diagnostics = [ | ||
...service.getSyntacticDiagnostics(fileFsPath), | ||
...service.getSemanticDiagnostics(fileFsPath) | ||
]; | ||
function getTemplateDiagnostics(): Diagnostic[] { | ||
const enabledTemplateValidation = config.vetur.experimental.templateTypeCheck; | ||
if (!enabledTemplateValidation) { | ||
return []; | ||
} | ||
|
||
return diagnostics.map(diag => { | ||
const tags: DiagnosticTag[] = []; | ||
// Add suffix to process this doc as vue template. | ||
const templateDoc = TextDocument.create(doc.uri + '.template', doc.languageId, doc.version, doc.getText()); | ||
|
||
if (diag.reportsUnnecessary) { | ||
tags.push(DiagnosticTag.Unnecessary); | ||
const { templateService } = updateCurrentTextDocument(templateDoc); | ||
if (!languageServiceIncludesFile(templateService, templateDoc.uri)) { | ||
return []; | ||
} | ||
|
||
// syntactic/semantic diagnostic always has start and length | ||
// so we can safely cast diag to TextSpan | ||
return <Diagnostic>{ | ||
range: convertRange(scriptDoc, diag as ts.TextSpan), | ||
severity: DiagnosticSeverity.Error, | ||
message: tsModule.flattenDiagnosticMessageText(diag.messageText, '\n'), | ||
tags, | ||
code: diag.code, | ||
source: 'Vetur' | ||
}; | ||
}); | ||
const templateFileFsPath = getFileFsPath(templateDoc.uri); | ||
// We don't need syntactic diagnostics because | ||
// compiled template is always valid JavaScript syntax. | ||
const rawTemplateDiagnostics = templateService.getSemanticDiagnostics(templateFileFsPath); | ||
|
||
return rawTemplateDiagnostics.map(diag => { | ||
// syntactic/semantic diagnostic always has start and length | ||
// so we can safely cast diag to TextSpan | ||
return { | ||
range: convertRange(templateDoc, diag as ts.TextSpan), | ||
severity: DiagnosticSeverity.Error, | ||
message: ts.flattenDiagnosticMessageText(diag.messageText, '\n'), | ||
code: diag.code, | ||
source: 'Vetur' | ||
}; | ||
}); | ||
} | ||
}, | ||
doComplete(doc: TextDocument, position: Position): CompletionList { | ||
const { scriptDoc, service } = updateCurrentTextDocument(doc); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can enable it by default to get more feedback.