/
plugin-module-factory.ts
88 lines (80 loc) · 3.66 KB
/
plugin-module-factory.ts
1
2
3
4
5
6
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
45
46
47
48
49
50
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
import ts from 'typescript/lib/tsserverlibrary';
import type { TsGraphQLPluginConfigOptions } from '../types';
import { GraphQLLanguageServiceAdapter } from '../graphql-language-service-adapter';
import { SchemaManagerFactory, createSchemaManagerHostFromLSPluginInfo } from '../schema-manager';
import { FragmentRegistry } from '../gql-ast-util';
import {
createScriptSourceHelper,
registerDocumentChangeEvent,
getTemplateNodeUnder,
findAllNodes,
getSanitizedTemplateText,
createFileNameFilter,
parseTagConfig,
} from '../ts-ast-util';
import { LanguageServiceProxyBuilder } from './language-service-proxy-builder';
function create(info: ts.server.PluginCreateInfo): ts.LanguageService {
const logger = (msg: string) => info.project.projectService.logger.info(`[ts-graphql-plugin] ${msg}`);
logger('config: ' + JSON.stringify(info.config));
const schemaManager = new SchemaManagerFactory(createSchemaManagerHostFromLSPluginInfo(info)).create();
const { schema, errors: schemaErrors } = schemaManager.getSchema();
const config = info.config as TsGraphQLPluginConfigOptions;
const tag = parseTagConfig(config.tag);
const removeDuplicatedFragments = config.removeDuplicatedFragments === false ? false : true;
const enabledGlobalFragments = config.enabledGlobalFragments === true;
const isExcluded = createFileNameFilter({ specs: config.exclude, projectName: info.project.getProjectName() });
const fragmentRegistry = new FragmentRegistry({ logger });
if (enabledGlobalFragments) {
const handleAcquireOrUpdate = (fileName: string, sourceFile: ts.SourceFile, version: string) => {
fragmentRegistry.registerDocuments(
fileName,
version,
findAllNodes(sourceFile, node => getTemplateNodeUnder(node, tag)).map(node =>
getSanitizedTemplateText(node, sourceFile),
),
);
};
registerDocumentChangeEvent(
// Note:
// documentRegistry in ts.server.Project is annotated @internal
(info.project as any).documentRegistry as ts.DocumentRegistry,
{
onAcquire: (fileName, sourceFile, version) => {
if (!isExcluded(fileName) && info.languageServiceHost.getScriptFileNames().includes(fileName)) {
handleAcquireOrUpdate(fileName, sourceFile, version);
}
},
onUpdate: (fileName, sourceFile, version) => {
if (!isExcluded(fileName) && info.languageServiceHost.getScriptFileNames().includes(fileName)) {
if (fragmentRegistry.getFileCurrentVersion(fileName) === version) return;
handleAcquireOrUpdate(fileName, sourceFile, version);
}
},
onRelease: fileName => {
fragmentRegistry.removeDocument(fileName);
},
},
);
}
const scriptSourceHelper = createScriptSourceHelper(info, { exclude: config.exclude });
const adapter = new GraphQLLanguageServiceAdapter(scriptSourceHelper, {
schema,
schemaErrors,
logger,
tag,
fragmentRegistry,
removeDuplicatedFragments,
});
const proxy = new LanguageServiceProxyBuilder(info)
.wrap('getCompletionsAtPosition', delegate => adapter.getCompletionAtPosition.bind(adapter, delegate))
.wrap('getSemanticDiagnostics', delegate => adapter.getSemanticDiagnostics.bind(adapter, delegate))
.wrap('getQuickInfoAtPosition', delegate => adapter.getQuickInfoAtPosition.bind(adapter, delegate))
.wrap('getDefinitionAtPosition', delegate => adapter.getDefinitionAtPosition.bind(adapter, delegate))
.build();
schemaManager.registerOnChange(adapter.updateSchema.bind(adapter));
schemaManager.start();
return proxy;
}
export const pluginModuleFactory: ts.server.PluginModuleFactory = () => {
return { create };
};