Skip to content

(svelte2tsx)blank possibly operator or property access near end of mustache before svelte compile #786

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

Merged
merged 9 commits into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export interface SnapshotFragment extends DocumentMapper {
*/
export interface SvelteSnapshotOptions {
strictMode: boolean;
transformOnTemplateError: boolean;
}

export namespace DocumentSnapshot {
Expand Down Expand Up @@ -142,7 +143,8 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions
const tsx = svelte2tsx(text, {
strictMode: options.strictMode,
filename: document.getFilePath() ?? undefined,
isTsFile: scriptKind === ts.ScriptKind.TSX
isTsFile: scriptKind === ts.ScriptKind.TSX,
emitOnTemplateError: options.transformOnTemplateError
});
text = tsx.code;
tsxMap = tsx.map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import {
getLanguageServiceForDocument,
getLanguageServiceForPath,
getService,
LanguageServiceContainer
LanguageServiceContainer,
LanguageServiceDocumentContext
} from './service';
import { SnapshotManager } from './SnapshotManager';

export class LSAndTSDocResolver {
constructor(
private readonly docManager: DocumentManager,
private readonly workspaceUris: string[],
private readonly configManager: LSConfigManager
private readonly configManager: LSConfigManager,
private readonly transformOnTemplateError = true
) {
docManager.on(
'documentChange',
Expand Down Expand Up @@ -43,8 +45,15 @@ export class LSAndTSDocResolver {
return document;
};

private get lsDocumentContext(): LanguageServiceDocumentContext {
return {
createDocument: this.createDocument,
transformOnTemplateError: this.transformOnTemplateError
};
}

getLSForPath(path: string) {
return getLanguageServiceForPath(path, this.workspaceUris, this.createDocument);
return getLanguageServiceForPath(path, this.workspaceUris, this.lsDocumentContext);
}

getLSAndTSDoc(
Expand All @@ -57,7 +66,7 @@ export class LSAndTSDocResolver {
const lang = getLanguageServiceForDocument(
document,
this.workspaceUris,
this.createDocument
this.lsDocumentContext
);
const filePath = document.getFilePath()!;
const tsDoc = this.getSnapshot(filePath, document);
Expand All @@ -74,7 +83,10 @@ export class LSAndTSDocResolver {

let tsDoc = snapshotManager.get(filePath);
if (!tsDoc) {
const options = { strictMode: !!tsService.compilerOptions.strict };
const options = {
strictMode: !!tsService.compilerOptions.strict,
transformOnTemplateError: this.transformOnTemplateError
};
tsDoc = document
? DocumentSnapshot.fromDocument(document, options)
: DocumentSnapshot.fromFilePath(filePath, options);
Expand All @@ -99,7 +111,7 @@ export class LSAndTSDocResolver {
}

private getTSService(filePath: string): LanguageServiceContainer {
return getService(filePath, this.workspaceUris, this.createDocument);
return getService(filePath, this.workspaceUris, this.lsDocumentContext);
}

private getUserPreferences(scriptKind: ts.ScriptKind): ts.UserPreferences {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,16 @@ export class TypeScriptPlugin
constructor(
docManager: DocumentManager,
configManager: LSConfigManager,
workspaceUris: string[]
workspaceUris: string[],
isEditor = true
) {
this.configManager = configManager;
this.lsAndTsDocResolver = new LSAndTSDocResolver(docManager, workspaceUris, configManager);
this.lsAndTsDocResolver = new LSAndTSDocResolver(
docManager,
workspaceUris,
configManager,
/**transformOnTemplateError */ isEditor
);
this.completionProvider = new CompletionsProviderImpl(this.lsAndTsDocResolver);
this.codeActionsProvider = new CodeActionsProviderImpl(
this.lsAndTsDocResolver,
Expand Down Expand Up @@ -381,7 +387,10 @@ export class TypeScriptPlugin

// Since the options parameter only applies to svelte snapshots, and this is not
// a svelte file, we can just set it to false without having any effect.
snapshotManager.updateByFileName(fileName, { strictMode: false });
snapshotManager.updateByFileName(fileName, {
strictMode: false,
transformOnTemplateError: false
});
}
}

Expand Down
40 changes: 25 additions & 15 deletions packages/language-server/src/plugins/typescript/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,42 @@ export interface LanguageServiceContainer {

const services = new Map<string, LanguageServiceContainer>();

export type CreateDocument = (fileName: string, content: string) => Document;
export interface LanguageServiceDocumentContext {
transformOnTemplateError: boolean;
createDocument: (fileName: string, content: string) => Document;
}

export function getLanguageServiceForPath(
path: string,
workspaceUris: string[],
createDocument: CreateDocument
docContext: LanguageServiceDocumentContext
): ts.LanguageService {
return getService(path, workspaceUris, createDocument).getService();
return getService(path, workspaceUris, docContext).getService();
}

export function getLanguageServiceForDocument(
document: Document,
workspaceUris: string[],
createDocument: CreateDocument
docContext: LanguageServiceDocumentContext
): ts.LanguageService {
return getService(document.getFilePath() || '', workspaceUris, createDocument).updateDocument(
return getService(document.getFilePath() || '', workspaceUris, docContext).updateDocument(
document
);
}

export function getService(path: string, workspaceUris: string[], createDocument: CreateDocument) {
export function getService(
path: string,
workspaceUris: string[],
docContext: LanguageServiceDocumentContext
) {
const tsconfigPath = findTsConfigPath(path, workspaceUris);

let service: LanguageServiceContainer;
if (services.has(tsconfigPath)) {
service = services.get(tsconfigPath)!;
} else {
Logger.log('Initialize new ts service at ', tsconfigPath);
service = createLanguageService(tsconfigPath, createDocument);
service = createLanguageService(tsconfigPath, docContext);
services.set(tsconfigPath, service);
}

Expand All @@ -56,7 +63,7 @@ export function getService(path: string, workspaceUris: string[], createDocument

export function createLanguageService(
tsconfigPath: string,
createDocument: CreateDocument
docContext: LanguageServiceDocumentContext
): LanguageServiceContainer {
const workspacePath = tsconfigPath ? dirname(tsconfigPath) : '';

Expand Down Expand Up @@ -105,6 +112,10 @@ export function createLanguageService(
getScriptKind: (fileName: string) => getSnapshot(fileName).scriptKind
};
let languageService = ts.createLanguageService(host);
const transformationConfig = {
strictMode: !!compilerOptions.strict,
transformOnTemplateError: docContext.transformOnTemplateError
};

return {
tsconfigPath,
Expand All @@ -128,9 +139,7 @@ export function createLanguageService(
return languageService;
}

const newSnapshot = DocumentSnapshot.fromDocument(document, {
strictMode: !!compilerOptions.strict
});
const newSnapshot = DocumentSnapshot.fromDocument(document, transformationConfig);
if (preSnapshot && preSnapshot.scriptKind !== newSnapshot.scriptKind) {
// Restart language service as it doesn't handle script kind changes.
languageService.dispose();
Expand All @@ -151,11 +160,12 @@ export function createLanguageService(

if (isSvelteFilePath(fileName)) {
const file = ts.sys.readFile(fileName) || '';
doc = DocumentSnapshot.fromDocument(createDocument(fileName, file), {
strictMode: !!compilerOptions.strict
});
doc = DocumentSnapshot.fromDocument(
docContext.createDocument(fileName, file),
transformationConfig
);
} else {
doc = DocumentSnapshot.fromFilePath(fileName, { strictMode: !!compilerOptions.strict });
doc = DocumentSnapshot.fromFilePath(fileName, transformationConfig);
}

snapshotManager.set(fileName, doc);
Expand Down
9 changes: 6 additions & 3 deletions packages/language-server/src/svelte-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ export class SvelteCheck {
}
if (shouldRegister('js')) {
this.pluginHost.register(
new TypeScriptPlugin(this.docManager, this.configManager, [
pathToUrl(workspacePath)
])
new TypeScriptPlugin(
this.docManager,
this.configManager,
[pathToUrl(workspacePath)],
/**isEditor */ false
)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ describe('CompletionProviderImpl', () => {
);
assert.ok(completions!.items.length > 0, 'Expected completions to have length');

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { data, ...withoutData } = completions!.items[0];
const first = completions!.items[0];
delete first.data;

assert.deepStrictEqual(withoutData, <CompletionItem>{
assert.deepStrictEqual(first, <CompletionItem>{
label: 'b',
insertText: undefined,
kind: CompletionItemKind.Method,
Expand All @@ -80,6 +80,31 @@ describe('CompletionProviderImpl', () => {
});
});

it('provides completions on simple property access in mustache', async () => {
const { completionProvider, document } = setup('mustache.svelte');

const completions = await completionProvider.getCompletions(
document,
Position.create(5, 3),
{
triggerKind: CompletionTriggerKind.TriggerCharacter,
triggerCharacter: '.'
}
);

const first = completions!.items[0];
delete first.data;

assert.deepStrictEqual(first, <CompletionItem>{
label: 'b',
insertText: undefined,
kind: CompletionItemKind.Field,
sortText: '1',
commitCharacters: ['.', ',', '('],
preselect: undefined
});
});

it('provides event completions', async () => {
const { completionProvider, document } = setup('component-events-completion.svelte');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script>
let a = 1;
let b = { b: 1 };
</script>
{a?.}
{b.}
4 changes: 4 additions & 0 deletions packages/svelte2tsx/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,9 @@ export default function svelte2tsx(
* `svelte-preprocess`.
*/
isTsFile?: boolean;
/**
* Whether to try emitting result when there's a syntax error in the template
*/
emitOnTemplateError?: boolean;
}
): SvelteCompiledToTsx
4 changes: 2 additions & 2 deletions packages/svelte2tsx/src/htmlxtojsx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ export function convertHtmlxToJsx(
/**
* @internal For testing only
*/
export function htmlx2jsx(htmlx: string) {
const ast = parseHtmlx(htmlx);
export function htmlx2jsx(htmlx: string, options?: { emitOnTemplateError?: boolean }) {
const ast = parseHtmlx(htmlx, options);
const str = new MagicString(htmlx);

convertHtmlxToJsx(str, ast);
Expand Down
16 changes: 12 additions & 4 deletions packages/svelte2tsx/src/svelte2tsx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@ type TemplateProcessResult = {
*/
const COMPONENT_SUFFIX = '__SvelteComponent_';

function processSvelteTemplate(str: MagicString): TemplateProcessResult {
const htmlxAst = parseHtmlx(str.original);
function processSvelteTemplate(
str: MagicString,
options?: { emitOnTemplateError?: boolean }
): TemplateProcessResult {
const htmlxAst = parseHtmlx(str.original, options);

let uses$$props = false;
let uses$$restProps = false;
Expand Down Expand Up @@ -407,7 +410,12 @@ function createRenderFunction({

export function svelte2tsx(
svelte: string,
options?: { filename?: string; strictMode?: boolean; isTsFile?: boolean }
options?: {
filename?: string;
strictMode?: boolean;
isTsFile?: boolean;
emitOnTemplateError?: boolean;
}
) {
const str = new MagicString(svelte);
// process the htmlx as a svelte template
Expand All @@ -420,7 +428,7 @@ export function svelte2tsx(
uses$$restProps,
events,
componentDocumentation
} = processSvelteTemplate(str);
} = processSvelteTemplate(str, options);

/* Rearrange the script tags so that module is first, and instance second followed finally by the template
* This is a bit convoluted due to some trouble I had with magic string. A simple str.move(start,end,0) for each script wasn't enough
Expand Down
Loading