diff --git a/README.md b/README.md
index 56e2f8b92..f54ccaf75 100644
--- a/README.md
+++ b/README.md
@@ -262,16 +262,84 @@ If not set, this plugin treats all template strings in your .ts as GraphQL query
For example:
+```js
+/* tsconfig.json */
+
+{
+ "compilerOptions": {
+ "plugins": [
+ {
+ "name": "ts-graphql-plugin",
+ "tag": "gql",
+ }
+ ]
+ }
+}
+```
+
```ts
-import gql from 'graphql-tag';
+/* yourApp.ts */
-// when tag paramter is 'gql'
-const str1 = gql`query { }`; // work
-const str2 = `
`; // don't work
-const str3 = otherTagFn`foooo`; // don't work
+import { gql } from '@apollo/client';
+
+// Recognized as GraphQL document
+const str1 = gql`
+ query AppQuery {
+ __typename
+ }
+`;
+
+// Not recognized as GraphQL document
+const str2 = ``;
+const str3 = otherTagFn`foooo`;
```
-It's useful to write multiple kinds template strings(e.g. one is Angular Component template, another is Apollo GraphQL query).
+Sometimes you want to consider the arguments of a particular function calling as a GraphQL document, such as:
+
+```ts
+import { graphql } from '@octokit/graphql';
+
+const { viewer } = await graphql(`
+ query MyQuery {
+ viewer {
+ name
+ }
+ }
+`);
+```
+
+Configure as the following:
+
+```js
+/* tsconfig.json */
+
+{
+ "compilerOptions": {
+ "plugins": [
+ {
+ "name": "ts-graphql-plugin",
+ "tag": {
+ "name": "graphql",
+ "ignoreFunctionCallExpression: false,
+ }
+ }
+ ]
+ }
+}
+```
+
+The `tag` option accepts the following type:
+
+```ts
+type TagConfig =
+ | undefined
+ | string
+ | string[]
+ | {
+ name?: string | string[];
+ ignoreFunctionCallExpression?: boolean;
+ };
+```
### `exclude`
diff --git a/src/analyzer/analyzer.ts b/src/analyzer/analyzer.ts
index ac15acd1c..e3c811b84 100644
--- a/src/analyzer/analyzer.ts
+++ b/src/analyzer/analyzer.ts
@@ -4,10 +4,11 @@ import { ScriptSourceHelper } from '../ts-ast-util/types';
import { Extractor } from './extractor';
import {
createScriptSourceHelper,
- hasTagged,
+ getTemplateNodeUnder,
findAllNodes,
registerDocumentChangeEvent,
getSanitizedTemplateText,
+ parseTagConfig,
} from '../ts-ast-util';
import { FragmentRegistry } from '../gql-ast-util';
import { SchemaManager, SchemaBuildErrorInfo } from '../schema-manager/schema-manager';
@@ -79,25 +80,21 @@ export class Analyzer {
this._typeGenerator = new TypeGenerator({
prjRootPath: this._prjRootPath,
extractor: this._extractor,
- tag: this._pluginConfig.tag,
+ tag: parseTagConfig(this._pluginConfig.tag),
addonFactories: this._pluginConfig.typegen.addonFactories,
debug: this._debug,
});
if (this._pluginConfig.enabledGlobalFragments === true) {
- const tag = this._pluginConfig.tag;
+ const tag = parseTagConfig(this._pluginConfig.tag);
registerDocumentChangeEvent(documentRegistry, {
onAcquire: (fileName, sourceFile, version) => {
if (!isExcluded(fileName) && this._languageServiceHost.getScriptFileNames().includes(fileName)) {
fragmentRegistry.registerDocuments(
fileName,
version,
- findAllNodes(sourceFile, node => {
- if (tag && ts.isTaggedTemplateExpression(node) && hasTagged(node, tag, sourceFile)) {
- return node.template;
- } else if (ts.isNoSubstitutionTemplateLiteral(node) || ts.isTemplateExpression(node)) {
- return node;
- }
- }).map(node => getSanitizedTemplateText(node, sourceFile)),
+ findAllNodes(sourceFile, node => getTemplateNodeUnder(node, tag)).map(node =>
+ getSanitizedTemplateText(node, sourceFile),
+ ),
);
}
},
@@ -112,7 +109,7 @@ export class Analyzer {
extract(fileNameList?: string[]) {
const results = this._extractor.extract(
fileNameList || this._languageServiceHost.getScriptFileNames(),
- this._pluginConfig.tag,
+ parseTagConfig(this._pluginConfig.tag),
);
const errors = this._extractor.pickupErrors(results);
return [errors, results] as const;
@@ -120,7 +117,7 @@ export class Analyzer {
extractToManifest() {
const [errors, results] = this.extract();
- const manifest = this._extractor.toManifest(results, this._pluginConfig.tag);
+ const manifest = this._extractor.toManifest(results, parseTagConfig(this._pluginConfig.tag));
return [errors, manifest] as const;
}
diff --git a/src/analyzer/extractor.test.ts b/src/analyzer/extractor.test.ts
index b159c4f91..bf68edef2 100644
--- a/src/analyzer/extractor.test.ts
+++ b/src/analyzer/extractor.test.ts
@@ -1,6 +1,7 @@
import { print } from 'graphql';
import { Extractor, ExtractSucceededResult } from './extractor';
import { createTesintExtractor } from './testing/testing-extractor';
+import { parseTagConfig } from '../ts-ast-util';
describe(Extractor, () => {
it('should extract GraphQL documents', () => {
@@ -24,7 +25,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql');
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql'));
expect(result.fileEntries.map(r => print(r.documentNode!))).toMatchSnapshot();
});
@@ -51,7 +52,7 @@ describe(Extractor, () => {
],
true,
);
- const result = extractor.extract(['main.ts'], 'gql');
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql'));
expect(result.fileEntries.map(r => print(r.documentNode!))).toMatchSnapshot();
});
@@ -78,7 +79,7 @@ describe(Extractor, () => {
],
false,
);
- const result = extractor.extract(['main.ts'], 'gql');
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql'));
expect(result.fileEntries.map(r => print(r.documentNode!))).toMatchSnapshot();
});
@@ -102,7 +103,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql');
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql'));
expect(result.fileEntries[0].resolevedTemplateInfo).toBeTruthy();
expect(result.fileEntries[1].resolevedTemplateInfo).toBeFalsy();
expect(result.fileEntries[1].resolveTemplateError).toMatchSnapshot();
@@ -121,7 +122,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql');
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql'));
expect(result.fileEntries[0].graphqlError).toBeTruthy();
});
@@ -146,8 +147,8 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql');
- expect(extractor.toManifest(result)).toMatchSnapshot();
+ const result = extractor.extract(['main.ts'], parseTagConfig(''));
+ expect(extractor.toManifest(result, parseTagConfig(''))).toMatchSnapshot();
});
describe('getDominantDefinition', () => {
@@ -168,7 +169,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql') as { fileEntries: ExtractSucceededResult[] };
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql')) as { fileEntries: ExtractSucceededResult[] };
const { type, operationName } = extractor.getDominantDefinition(result.fileEntries[0]);
expect(type).toBe('query');
expect(operationName).toBe('MyQuery');
@@ -190,7 +191,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql') as { fileEntries: ExtractSucceededResult[] };
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql')) as { fileEntries: ExtractSucceededResult[] };
const { type, operationName } = extractor.getDominantDefinition(result.fileEntries[0]);
expect(type).toBe('mutation');
expect(operationName).toBe('MyMutation');
@@ -212,7 +213,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql') as { fileEntries: ExtractSucceededResult[] };
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql')) as { fileEntries: ExtractSucceededResult[] };
const { type, operationName } = extractor.getDominantDefinition(result.fileEntries[0]);
expect(type).toBe('subscription');
expect(operationName).toBe('MySubscription');
@@ -239,7 +240,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql') as { fileEntries: ExtractSucceededResult[] };
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql')) as { fileEntries: ExtractSucceededResult[] };
const { type, operationName } = extractor.getDominantDefinition(result.fileEntries[0]);
expect(type).toBe('complex');
expect(operationName).toBe('MULTIPLE_OPERATIONS');
@@ -259,7 +260,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql') as { fileEntries: ExtractSucceededResult[] };
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql')) as { fileEntries: ExtractSucceededResult[] };
const { type, fragmentName } = extractor.getDominantDefinition(result.fileEntries[0]);
expect(type).toBe('fragment');
expect(fragmentName).toBe('MyFragment');
@@ -283,7 +284,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql') as { fileEntries: ExtractSucceededResult[] };
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql')) as { fileEntries: ExtractSucceededResult[] };
const { type, fragmentName } = extractor.getDominantDefinition(result.fileEntries[0]);
expect(type).toBe('fragment');
expect(fragmentName).toBe('MyFragment');
@@ -311,7 +312,7 @@ describe(Extractor, () => {
`,
},
]);
- const result = extractor.extract(['main.ts'], 'gql') as { fileEntries: ExtractSucceededResult[] };
+ const result = extractor.extract(['main.ts'], parseTagConfig('gql')) as { fileEntries: ExtractSucceededResult[] };
const { type, fragmentName } = extractor.getDominantDefinition(result.fileEntries[0]);
expect(type).toBe('fragment');
expect(fragmentName).toBe('MyFragment2');
diff --git a/src/analyzer/extractor.ts b/src/analyzer/extractor.ts
index 3a1b980b0..fa3288827 100644
--- a/src/analyzer/extractor.ts
+++ b/src/analyzer/extractor.ts
@@ -10,7 +10,13 @@ import {
type FragmentDefinitionNode,
} from 'graphql';
import { getFragmentDependenciesForAST } from 'graphql-language-service';
-import { isTagged, ScriptSourceHelper, ResolvedTemplateInfo } from '../ts-ast-util';
+import {
+ getTemplateNodeUnder,
+ getTagName,
+ ScriptSourceHelper,
+ ResolvedTemplateInfo,
+ StrictTagCondition,
+} from '../ts-ast-util';
import { ManifestOutput, ManifestDocumentEntry, OperationType } from './types';
import { ErrorWithLocation, ERROR_CODES } from '../errors';
import {
@@ -84,19 +90,14 @@ export class Extractor {
this._debug = debug;
}
- extract(files: string[], tagName?: string): ExtractResult {
+ extract(files: string[], tag: StrictTagCondition): ExtractResult {
const results: ExtractFileResult[] = [];
const targetFiles = files.filter(fileName => !this._helper.isExcluded(fileName));
this._debug('Extract template literals from: ');
this._debug(targetFiles.map(f => ' ' + f).join(',\n'));
targetFiles.forEach(fileName => {
if (this._helper.isExcluded(fileName)) return;
- const nodes = this._helper
- .getAllNodes(fileName, node => ts.isTemplateExpression(node) || ts.isNoSubstitutionTemplateLiteral(node))
- .filter(node => (tagName ? isTagged(node, tagName) : true)) as (
- | ts.TemplateExpression
- | ts.NoSubstitutionTemplateLiteral
- )[];
+ const nodes = this._helper.getAllNodes(fileName, node => getTemplateNodeUnder(node, tag));
nodes.forEach(node => {
const { resolvedInfo, resolveErrors } = this._helper.resolveTemplateLiteral(fileName, node);
if (!resolvedInfo) {
@@ -287,7 +288,7 @@ export class Extractor {
};
}
- toManifest({ fileEntries: extractResults, globalFragments }: ExtractResult, tagName: string = ''): ManifestOutput {
+ toManifest({ fileEntries: extractResults, globalFragments }: ExtractResult, tag: StrictTagCondition): ManifestOutput {
const documents = extractResults
.filter(r => !!r.documentNode)
.map(result => {
@@ -299,7 +300,7 @@ export class Extractor {
operationName,
fragmentName,
body: print(this.inflateDocument(r, { globalFragments }).inflatedDocumentNode),
- tag: tagName,
+ tag: getTagName(r.templateNode, tag),
templateLiteralNodeStart: this._helper.getLineAndChar(r.fileName, r.templateNode.getStart()),
templateLiteralNodeEnd: this._helper.getLineAndChar(r.fileName, r.templateNode.getEnd()),
documentStart: this._helper.getLineAndChar(r.fileName, r.templateNode.getStart() + 1),
diff --git a/src/analyzer/markdown-reporter.test.ts b/src/analyzer/markdown-reporter.test.ts
index f45d8a856..9e1659a74 100644
--- a/src/analyzer/markdown-reporter.test.ts
+++ b/src/analyzer/markdown-reporter.test.ts
@@ -1,5 +1,6 @@
import { MarkdownReporter } from './markdown-reporter';
import { createTesintExtractor } from './testing/testing-extractor';
+import { parseTagConfig } from '../ts-ast-util';
describe(MarkdownReporter, () => {
it('should convert from manifest to markdown content', () => {
@@ -29,7 +30,10 @@ describe(MarkdownReporter, () => {
`,
},
]);
- const manifest = extractor.toManifest(extractor.extract(['/prj-root/src/main.ts'], 'gql'));
+ const manifest = extractor.toManifest(
+ extractor.extract(['/prj-root/src/main.ts'], parseTagConfig('gql')),
+ parseTagConfig('gql'),
+ );
const content = new MarkdownReporter().toMarkdownConntent(manifest, {
baseDir: '/prj-root',
outputDir: '/prj-root/dist',
@@ -64,7 +68,10 @@ describe(MarkdownReporter, () => {
`,
},
]);
- const manifest = extractor.toManifest(extractor.extract(['/prj-root/src/main.ts'], 'gql'));
+ const manifest = extractor.toManifest(
+ extractor.extract(['/prj-root/src/main.ts'], parseTagConfig('gql')),
+ parseTagConfig('gql'),
+ );
const content = new MarkdownReporter().toMarkdownConntent(manifest, {
ignoreFragments: false,
baseDir: '/prj-root',
diff --git a/src/analyzer/type-generator.test.ts b/src/analyzer/type-generator.test.ts
index 7113c2341..be385e4ac 100644
--- a/src/analyzer/type-generator.test.ts
+++ b/src/analyzer/type-generator.test.ts
@@ -3,7 +3,7 @@ import { TypeGenerator } from './type-generator';
import { createTesintExtractor } from './testing/testing-extractor';
import { ExtractSucceededResult } from './extractor';
import { TypeGenAddonFactory } from '../typegen/addon/types';
-import { createOutputSource } from '../ts-ast-util';
+import { createOutputSource, DEFAULT_TAG_CONDITION } from '../ts-ast-util';
function createTestingTypeGenerator({
files = [],
@@ -15,7 +15,7 @@ function createTestingTypeGenerator({
const extractor = createTesintExtractor(files, true);
const generator = new TypeGenerator({
prjRootPath: '',
- tag: undefined,
+ tag: DEFAULT_TAG_CONDITION,
addonFactories,
extractor,
debug: () => {},
@@ -37,7 +37,7 @@ describe(TypeGenerator, () => {
});
const {
fileEntries: [fileEntry],
- } = extractor.extract(['main.ts']) as { fileEntries: ExtractSucceededResult[] };
+ } = extractor.extract(['main.ts'], DEFAULT_TAG_CONDITION) as { fileEntries: ExtractSucceededResult[] };
const { addon, context } = generator.createAddon({
fileEntry,
schema,
diff --git a/src/analyzer/type-generator.ts b/src/analyzer/type-generator.ts
index ed1c3f414..78fed41bf 100644
--- a/src/analyzer/type-generator.ts
+++ b/src/analyzer/type-generator.ts
@@ -5,21 +5,21 @@ import { GraphQLSchema } from 'graphql';
import { TsGqlError, ErrorWithLocation } from '../errors';
import { mergeAddons, TypeGenVisitor, TypeGenError, TypeGenAddonFactory, TypeGenVisitorAddonContext } from '../typegen';
import { dasherize } from '../string-util';
-import { OutputSource, createOutputSource } from '../ts-ast-util';
+import { OutputSource, createOutputSource, type StrictTagCondition } from '../ts-ast-util';
import { Extractor, ExtractSucceededResult } from './extractor';
export type TypeGeneratorOptions = {
prjRootPath: string;
extractor: Extractor;
debug: (msg: string) => void;
- tag: string | undefined;
+ tag: StrictTagCondition;
addonFactories: TypeGenAddonFactory[];
};
export class TypeGenerator {
private readonly _prjRootPath: string;
private readonly _extractor: Extractor;
- private readonly _tag: string | undefined;
+ private readonly _tag: StrictTagCondition;
private readonly _addonFactories: TypeGenAddonFactory[];
private readonly _debug: (msg: string) => void;
private readonly _printer: ts.Printer;
diff --git a/src/graphql-language-service-adapter/get-completion-at-position.ts b/src/graphql-language-service-adapter/get-completion-at-position.ts
index 543f3a4a7..2c8aef521 100644
--- a/src/graphql-language-service-adapter/get-completion-at-position.ts
+++ b/src/graphql-language-service-adapter/get-completion-at-position.ts
@@ -33,7 +33,7 @@ export function getCompletionAtPosition(
if (ctx.getScriptSourceHelper().isExcluded(fileName)) return delegate(fileName, position, options);
const schema = ctx.getSchema();
if (!schema) return delegate(fileName, position, options);
- const node = ctx.findTemplateNode(fileName, position);
+ const node = ctx.findAscendantTemplateNode(fileName, position);
if (!node) return delegate(fileName, position, options);
const { resolvedInfo } = ctx.resolveTemplateInfo(fileName, node);
if (!resolvedInfo) {
diff --git a/src/graphql-language-service-adapter/get-quick-info-at-position.ts b/src/graphql-language-service-adapter/get-quick-info-at-position.ts
index 1aec9f3fd..7fb0bc297 100644
--- a/src/graphql-language-service-adapter/get-quick-info-at-position.ts
+++ b/src/graphql-language-service-adapter/get-quick-info-at-position.ts
@@ -12,7 +12,7 @@ export function getQuickInfoAtPosition(
if (ctx.getScriptSourceHelper().isExcluded(fileName)) return delegate(fileName, position);
const schema = ctx.getSchema();
if (!schema) return delegate(fileName, position);
- const node = ctx.findTemplateNode(fileName, position);
+ const node = ctx.findAscendantTemplateNode(fileName, position);
if (!node) return delegate(fileName, position);
const { resolvedInfo } = ctx.resolveTemplateInfo(fileName, node);
if (!resolvedInfo) return delegate(fileName, position);
diff --git a/src/graphql-language-service-adapter/graphql-language-service-adapter.ts b/src/graphql-language-service-adapter/graphql-language-service-adapter.ts
index 64092a6ea..28c623bb3 100644
--- a/src/graphql-language-service-adapter/graphql-language-service-adapter.ts
+++ b/src/graphql-language-service-adapter/graphql-language-service-adapter.ts
@@ -1,6 +1,12 @@
import ts from 'typescript';
import { GraphQLSchema, parse, type DocumentNode } from 'graphql';
-import { isTagged, ScriptSourceHelper, TagCondition, isTemplateLiteralTypeNode } from '../ts-ast-util';
+import {
+ getTemplateNodeUnder,
+ isTaggedTemplateNode,
+ ScriptSourceHelper,
+ StrictTagCondition,
+ isTemplateLiteralTypeNode,
+} from '../ts-ast-util';
import { SchemaBuildErrorInfo } from '../schema-manager/schema-manager';
import { getFragmentNamesInDocument, detectDuplicatedFragments, type FragmentRegistry } from '../gql-ast-util';
import { AnalysisContext, GetCompletionAtPosition, GetSemanticDiagnostics, GetQuickInfoAtPosition } from './types';
@@ -13,7 +19,7 @@ export interface GraphQLLanguageServiceAdapterCreateOptions {
schema?: GraphQLSchema | null;
schemaErrors?: SchemaBuildErrorInfo[] | null;
logger?: (msg: string) => void;
- tag?: string;
+ tag: StrictTagCondition;
removeDuplicatedFragments: boolean;
fragmentRegistry: FragmentRegistry;
}
@@ -23,7 +29,7 @@ type Args = T extends (...args: infer A) => any ? A : never;
export class GraphQLLanguageServiceAdapter {
private _schemaErrors?: SchemaBuildErrorInfo[] | null;
private _schema?: GraphQLSchema | null;
- private readonly _tagCondition?: TagCondition;
+ private readonly _tagCondition: StrictTagCondition;
private readonly _removeDuplicatedFragments: boolean;
private readonly _analysisContext: AnalysisContext;
private readonly _fragmentRegisry: FragmentRegistry;
@@ -36,7 +42,7 @@ export class GraphQLLanguageServiceAdapter {
if (opt.logger) this._logger = opt.logger;
if (opt.schemaErrors) this.updateSchema(opt.schemaErrors, null);
if (opt.schema) this.updateSchema(null, opt.schema);
- if (opt.tag) this._tagCondition = opt.tag;
+ this._tagCondition = opt.tag;
this._removeDuplicatedFragments = opt.removeDuplicatedFragments;
this._analysisContext = this._createAnalysisContext();
this._fragmentRegisry = opt.fragmentRegistry;
@@ -81,45 +87,39 @@ export class GraphQLLanguageServiceAdapter {
getExternalFragmentDefinitions: (documentStr, fileName, sourcePosition) =>
this._fragmentRegisry.getExternalFragments(documentStr, fileName, sourcePosition),
getDuplicaterdFragmentDefinitions: () => this._fragmentRegisry.getDuplicaterdFragmentDefinitions(),
- findTemplateNode: (fileName, position) => this._findTemplateNode(fileName, position),
+ findAscendantTemplateNode: (fileName, position) => this._findAscendantTemplateNode(fileName, position),
findTemplateNodes: fileName => this._findTemplateNodes(fileName),
resolveTemplateInfo: (fileName, node) => this._resolveTemplateInfo(fileName, node),
};
return ctx;
}
- private _findTemplateNode(fileName: string, position: number) {
- const foundNode = this._helper.getNode(fileName, position);
- if (!foundNode) return;
- let node: ts.NoSubstitutionTemplateLiteral | ts.TemplateExpression;
- if (ts.isNoSubstitutionTemplateLiteral(foundNode)) {
- node = foundNode;
- } else if (ts.isTemplateHead(foundNode) && !isTemplateLiteralTypeNode(foundNode.parent)) {
- node = foundNode.parent;
+ private _findAscendantTemplateNode(fileName: string, position: number) {
+ const nodeUnderCursor = this._helper.getNode(fileName, position);
+ if (!nodeUnderCursor) return;
+
+ let templateNode: ts.NoSubstitutionTemplateLiteral | ts.TemplateExpression;
+
+ if (ts.isNoSubstitutionTemplateLiteral(nodeUnderCursor)) {
+ templateNode = nodeUnderCursor;
+ } else if (ts.isTemplateHead(nodeUnderCursor) && !isTemplateLiteralTypeNode(nodeUnderCursor.parent)) {
+ templateNode = nodeUnderCursor.parent;
} else if (
- (ts.isTemplateMiddle(foundNode) || ts.isTemplateTail(foundNode)) &&
- !isTemplateLiteralTypeNode(foundNode.parent.parent)
+ (ts.isTemplateMiddle(nodeUnderCursor) || ts.isTemplateTail(nodeUnderCursor)) &&
+ !isTemplateLiteralTypeNode(nodeUnderCursor.parent.parent)
) {
- node = foundNode.parent.parent;
+ templateNode = nodeUnderCursor.parent.parent;
} else {
return;
}
- if (this._tagCondition && !isTagged(node, this._tagCondition)) {
+ if (!isTaggedTemplateNode(templateNode, this._tagCondition)) {
return;
}
- return node;
+ return templateNode;
}
private _findTemplateNodes(fileName: string) {
- const allTemplateStringNodes = this._helper.getAllNodes(
- fileName,
- (n: ts.Node) => ts.isNoSubstitutionTemplateLiteral(n) || ts.isTemplateExpression(n),
- );
- const nodes = allTemplateStringNodes.filter(n => {
- if (!this._tagCondition) return true;
- return isTagged(n, this._tagCondition);
- }) as (ts.NoSubstitutionTemplateLiteral | ts.TemplateExpression)[];
- return nodes;
+ return this._helper.getAllNodes(fileName, node => getTemplateNodeUnder(node, this._tagCondition));
}
private _parse(text: string) {
diff --git a/src/graphql-language-service-adapter/testing/adapter-fixture.ts b/src/graphql-language-service-adapter/testing/adapter-fixture.ts
index 8f2660403..f80e724d8 100644
--- a/src/graphql-language-service-adapter/testing/adapter-fixture.ts
+++ b/src/graphql-language-service-adapter/testing/adapter-fixture.ts
@@ -32,6 +32,12 @@ export class AdapterFixture {
schema: schema || null,
removeDuplicatedFragments: true,
fragmentRegistry: this._fragmentRegistry,
+ tag: {
+ names: [],
+ allowNotTaggedTemplate: true,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: true,
+ },
},
);
}
diff --git a/src/graphql-language-service-adapter/types.ts b/src/graphql-language-service-adapter/types.ts
index 7c1e54967..0586340f4 100644
--- a/src/graphql-language-service-adapter/types.ts
+++ b/src/graphql-language-service-adapter/types.ts
@@ -20,7 +20,7 @@ export interface AnalysisContext {
): FragmentDefinitionNode[];
getDuplicaterdFragmentDefinitions(): Set;
getGraphQLDocumentNode(text: string): DocumentNode | undefined;
- findTemplateNode(
+ findAscendantTemplateNode(
fileName: string,
position: number,
): ts.NoSubstitutionTemplateLiteral | ts.TemplateExpression | undefined;
diff --git a/src/language-service-plugin/plugin-module-factory.ts b/src/language-service-plugin/plugin-module-factory.ts
index 81fc3ac62..f845b6b6c 100644
--- a/src/language-service-plugin/plugin-module-factory.ts
+++ b/src/language-service-plugin/plugin-module-factory.ts
@@ -6,10 +6,11 @@ import { FragmentRegistry } from '../gql-ast-util';
import {
createScriptSourceHelper,
registerDocumentChangeEvent,
- hasTagged,
+ getTemplateNodeUnder,
findAllNodes,
getSanitizedTemplateText,
createFileNameFilter,
+ parseTagConfig,
} from '../ts-ast-util';
import { LanguageServiceProxyBuilder } from './language-service-proxy-builder';
@@ -19,7 +20,7 @@ function create(info: ts.server.PluginCreateInfo): ts.LanguageService {
const schemaManager = new SchemaManagerFactory(createSchemaManagerHostFromLSPluginInfo(info)).create();
const { schema, errors: schemaErrors } = schemaManager.getSchema();
const config = info.config as TsGraphQLPluginConfigOptions;
- const tag = config.tag;
+ 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() });
@@ -30,13 +31,9 @@ function create(info: ts.server.PluginCreateInfo): ts.LanguageService {
fragmentRegistry.registerDocuments(
fileName,
version,
- findAllNodes(sourceFile, node => {
- if (tag && ts.isTaggedTemplateExpression(node) && hasTagged(node, tag, sourceFile)) {
- return node.template;
- } else if (ts.isNoSubstitutionTemplateLiteral(node) || ts.isTemplateExpression(node)) {
- return node;
- }
- }).map(node => getSanitizedTemplateText(node, sourceFile)),
+ findAllNodes(sourceFile, node => getTemplateNodeUnder(node, tag)).map(node =>
+ getSanitizedTemplateText(node, sourceFile),
+ ),
);
};
diff --git a/src/transformer/transformer-host.ts b/src/transformer/transformer-host.ts
index a39232d3b..6b957854f 100644
--- a/src/transformer/transformer-host.ts
+++ b/src/transformer/transformer-host.ts
@@ -2,6 +2,7 @@ import ts from 'typescript';
import { DocumentNode } from 'graphql';
import { Analyzer, AnalyzerFactory, ExtractFileResult } from '../analyzer';
import { getTransformer, DocumentTransformer } from './transformer';
+import { parseTagConfig } from '../ts-ast-util';
class DocumentNodeRegistory {
protected readonly _map = new Map>();
@@ -92,7 +93,7 @@ export class TransformerHost {
});
return getTransformer({
getEnabled,
- tag,
+ tag: parseTagConfig(tag),
target,
removeFragmentDefinitions,
getDocumentNode: node => this._documentNodeRegistory.getDocumentNode(node),
diff --git a/src/transformer/transformer.test.ts b/src/transformer/transformer.test.ts
index 44bdf83d2..a21ab7a23 100644
--- a/src/transformer/transformer.test.ts
+++ b/src/transformer/transformer.test.ts
@@ -1,6 +1,7 @@
import ts from 'typescript';
import { DocumentNode, parse, visit } from 'graphql';
+import { parseTagConfig } from '../ts-ast-util';
import { getTransformer } from './transformer';
function transformAndPrint({
@@ -23,7 +24,7 @@ function transformAndPrint({
const getDocumentNode = () => parse(docContent);
const source = ts.createSourceFile('main.ts', tsContent, ts.ScriptTarget.Latest, true);
const transformer = getTransformer({
- tag,
+ tag: parseTagConfig(tag),
target,
getDocumentNode,
removeFragmentDefinitions,
diff --git a/src/transformer/transformer.ts b/src/transformer/transformer.ts
index cd30e6182..e0c3271c1 100644
--- a/src/transformer/transformer.ts
+++ b/src/transformer/transformer.ts
@@ -1,11 +1,11 @@
import ts from 'typescript';
import { DocumentNode, print } from 'graphql';
-import { astf, hasTagged, removeAliasFromImportDeclaration } from '../ts-ast-util';
+import { astf, getTemplateNodeUnder, removeAliasFromImportDeclaration, type StrictTagCondition } from '../ts-ast-util';
export type DocumentTransformer = (documentNode: DocumentNode) => DocumentNode;
export type TransformOptions = {
- tag?: string;
+ tag: StrictTagCondition;
documentTransformers: DocumentTransformer[];
removeFragmentDefinitions: boolean;
target: 'text' | 'object';
@@ -44,17 +44,21 @@ export function getTransformer({
return (ctx: ts.TransformationContext) => {
const visit = (node: ts.Node): ts.Node | undefined => {
if (!getEnabled()) return node;
+ if (tag.names.length > 1) return node;
let templateNode: ts.NoSubstitutionTemplateLiteral | ts.TemplateExpression | undefined = undefined;
- if (tag && ts.isImportDeclaration(node)) {
- return removeAliasFromImportDeclaration(node, tag);
+ if (ts.isImportDeclaration(node) && tag.names[0]) {
+ return removeAliasFromImportDeclaration(node, tag.names[0]);
}
- if (ts.isTaggedTemplateExpression(node) && (!tag || hasTagged(node, tag))) {
+ if (
+ ts.isTaggedTemplateExpression(node) &&
+ (!tag.names.length || !!getTemplateNodeUnder(node, { ...tag, allowFunctionCallExpression: false }))
+ ) {
templateNode = node.template;
- } else if (!tag && ts.isNoSubstitutionTemplateLiteral(node)) {
+ } else if (tag.allowNotTaggedTemplate && ts.isNoSubstitutionTemplateLiteral(node)) {
templateNode = node;
- } else if (!tag && ts.isTemplateExpression(node)) {
+ } else if (tag.allowNotTaggedTemplate && ts.isTemplateExpression(node)) {
templateNode = node;
}
diff --git a/src/ts-ast-util/index.ts b/src/ts-ast-util/index.ts
index 66f2cc5d6..1435e2bfb 100644
--- a/src/ts-ast-util/index.ts
+++ b/src/ts-ast-util/index.ts
@@ -3,6 +3,7 @@ export * from './ast-factory-alias';
export * from './utilily-functions';
export * from './template-expression-resolver';
export * from './register-document-change-event';
+export * from './tag-utils';
export { ScriptHost } from './script-host';
export { createScriptSourceHelper } from './script-source-helper';
diff --git a/src/ts-ast-util/script-source-helper.ts b/src/ts-ast-util/script-source-helper.ts
index 0beb8916e..7beb3fea6 100644
--- a/src/ts-ast-util/script-source-helper.ts
+++ b/src/ts-ast-util/script-source-helper.ts
@@ -35,7 +35,7 @@ export function createScriptSourceHelper(
const getNode = (fileName: string, position: number) => {
return findNode(getSourceFile(fileName), position);
};
- const getAllNodes = (fileName: string, cond: (n: ts.Node) => boolean) => {
+ const getAllNodes = (fileName: string, cond: (n: ts.Node) => undefined | boolean | S) => {
const s = getSourceFile(fileName);
return findAllNodes(s, cond);
};
diff --git a/src/ts-ast-util/tag-utils.test.ts b/src/ts-ast-util/tag-utils.test.ts
new file mode 100644
index 000000000..eeab862fc
--- /dev/null
+++ b/src/ts-ast-util/tag-utils.test.ts
@@ -0,0 +1,192 @@
+import ts from 'typescript';
+import { findAllNodes } from './utilily-functions';
+import type { TagConfig, StrictTagCondition } from './types';
+import { parseTagConfig, getTemplateNodeUnder, getTagName } from './tag-utils';
+
+describe(parseTagConfig, () => {
+ test.each([
+ {
+ config: undefined,
+ expected: {
+ names: [],
+ allowNotTaggedTemplate: true,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: '',
+ expected: {
+ names: [],
+ allowNotTaggedTemplate: true,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: 'gql',
+ expected: {
+ names: ['gql'],
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: ['gql', 'graphql'],
+ expected: {
+ names: ['gql', 'graphql'],
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: {},
+ expected: {
+ names: [],
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: {
+ name: 'gql',
+ } satisfies TagConfig,
+ expected: {
+ names: ['gql'],
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: {
+ name: ['gql', 'graphql'],
+ } satisfies TagConfig,
+ expected: {
+ names: ['gql', 'graphql'],
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: {
+ name: ['graphql'],
+ ignoreFunctionCallExpression: true,
+ } satisfies TagConfig,
+ expected: {
+ names: ['graphql'],
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ } as StrictTagCondition,
+ },
+ {
+ config: {
+ name: ['graphql'],
+ ignoreFunctionCallExpression: false,
+ } satisfies TagConfig,
+ expected: {
+ names: ['graphql'],
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: true,
+ } as StrictTagCondition,
+ },
+ ])('input: $config', ({ config, expected }) => {
+ expect(parseTagConfig(config)).toEqual(expected);
+ });
+});
+
+describe(getTemplateNodeUnder, () => {
+ const createFixture = (sourceText: string, tagConfig: TagConfig) => {
+ const source = ts.createSourceFile('input.ts', sourceText, ts.ScriptTarget.Latest, false);
+ return {
+ exec: () => findAllNodes(source, node => getTemplateNodeUnder(node, parseTagConfig(tagConfig))),
+ };
+ };
+
+ it.each([
+ { source: '`query { }`', config: undefined },
+ { source: 'gql`query { }`', config: 'gql' },
+ { source: 'gql`query { }`', config: ['gql', 'graphql'] },
+ {
+ source: 'graphql(`query { }`)',
+ config: { name: 'graphql', ignoreFunctionCallExpression: false } satisfies TagConfig,
+ },
+ ])('should return TemplateLiteralNode from $source with tagCongig: $config', ({ source, config }) => {
+ const [found] = createFixture(source, config).exec();
+ expect(found).toBeTruthy();
+ });
+
+ test.each([
+ { source: '`query { }`', config: 'gql' },
+ { source: 'graphql`query { }`', config: 'gql' },
+ {
+ source: 'fn(`query { }`)',
+ config: { name: 'graphql', ignoreFunctionCallExpression: false } satisfies TagConfig,
+ },
+ {
+ source: 'graphql(`query { }`)',
+ config: { name: 'graphql', ignoreFunctionCallExpression: true } satisfies TagConfig,
+ },
+ ])('should not return undefind from $source with tagCongig: $config', ({ source, config }) => {
+ const actual = createFixture(source, config).exec();
+ expect(actual).toEqual([]);
+ });
+});
+
+describe(getTagName, () => {
+ const createFixture = (sourceText: string, tagConfig: TagConfig) => {
+ const source = ts.createSourceFile('input.ts', sourceText, ts.ScriptTarget.Latest, true);
+ return {
+ exec: () =>
+ findAllNodes(source, node => getTemplateNodeUnder(node, parseTagConfig(tagConfig))).map(n =>
+ getTagName(n, parseTagConfig(tagConfig)),
+ ),
+ };
+ };
+
+ it.each([
+ { source: '`query { }`', expected: '', config: undefined },
+ { source: 'gql`query { }`', expected: 'gql', config: 'gql' },
+ { source: 'gql`query { }`', expected: 'gql', config: ['gql', 'graphql'] },
+ {
+ source: 'graphql(`query { }`)',
+ expected: 'graphql',
+ config: { name: 'graphql', ignoreFunctionCallExpression: false } satisfies TagConfig,
+ },
+ ])('should return TemplateLiteralNode from $source with tagCongig: $config', ({ source, expected, config }) => {
+ const actual = createFixture(source, config).exec();
+ expect(actual).toEqual([expected]);
+ });
+
+ it('return empty string when node is parsed without parent', () => {
+ const sourceText = 'gql`query { }`';
+ const source = ts.createSourceFile('input.ts', sourceText, ts.ScriptTarget.Latest, false);
+ const actual = findAllNodes(source, node => getTemplateNodeUnder(node, parseTagConfig('gql'))).map(n =>
+ getTagName(n, parseTagConfig('gql')),
+ );
+ expect(actual).toEqual([undefined]);
+ });
+
+ it('return empty string when node does not match condition', () => {
+ const sourceText = '`hoge`';
+ const source = ts.createSourceFile('input.ts', sourceText, ts.ScriptTarget.Latest, true);
+ const actual = getTagName(source.statements[0] as unknown as ts.TemplateLiteral, parseTagConfig('gql'));
+ expect(actual).toBe(undefined);
+ });
+
+ it('return empty string when node parent is not TaggedTemplateExpression nor CallExpression', () => {
+ const sourceText = 'const obj = { x: `hoge` }';
+ const source = ts.createSourceFile('input.ts', sourceText, ts.ScriptTarget.Latest, true);
+ const actual = getTagName(
+ findAllNodes(source, node => ts.isNoSubstitutionTemplateLiteral(node) && node)[0],
+ parseTagConfig('gql'),
+ );
+ expect(actual).toBe(undefined);
+ });
+});
diff --git a/src/ts-ast-util/tag-utils.ts b/src/ts-ast-util/tag-utils.ts
new file mode 100644
index 000000000..573f92475
--- /dev/null
+++ b/src/ts-ast-util/tag-utils.ts
@@ -0,0 +1,94 @@
+import ts from 'typescript';
+import type { TagConfig, StrictTagCondition } from './types';
+
+// TODO Change default at v4
+export const DEFAULT_TAG_CONDITION = {
+ names: [],
+ allowNotTaggedTemplate: true,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+} satisfies StrictTagCondition;
+
+export function parseTagConfig(tagConfig: TagConfig | undefined): StrictTagCondition {
+ const parseName = (name: unknown) => {
+ if (typeof name === 'string') {
+ return [name];
+ } else if (Array.isArray(name)) {
+ return name.map(n => n.toString());
+ } else {
+ return [] as string[];
+ }
+ };
+
+ if (!tagConfig) {
+ return DEFAULT_TAG_CONDITION;
+ }
+
+ if (typeof tagConfig === 'string' || Array.isArray(tagConfig)) {
+ return {
+ names: parseName(tagConfig),
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression: false,
+ };
+ }
+
+ const names = parseName(tagConfig.name);
+ return {
+ names,
+ allowNotTaggedTemplate: false,
+ allowTaggedTemplateExpression: true,
+ allowFunctionCallExpression:
+ tagConfig.ignoreFunctionCallExpression == null
+ ? false // Change behavior at v4
+ : tagConfig.ignoreFunctionCallExpression === false
+ ? true
+ : false,
+ };
+}
+
+export function getTemplateNodeUnder(
+ node: ts.Node | undefined,
+ { allowFunctionCallExpression, allowNotTaggedTemplate, allowTaggedTemplateExpression, names }: StrictTagCondition,
+) {
+ if (!node) return undefined;
+ if (allowTaggedTemplateExpression && ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag)) {
+ if (names.includes(node.tag.escapedText as string)) {
+ return node.template;
+ }
+ }
+ if (allowFunctionCallExpression && ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
+ const firstArg = node.arguments[0];
+ if (!ts.isTemplateLiteral(firstArg)) return;
+ if (names.includes(node.expression.escapedText as string)) {
+ return firstArg;
+ }
+ }
+ if (allowNotTaggedTemplate && ts.isTemplateLiteral(node)) {
+ return node;
+ }
+}
+
+export function isTaggedTemplateNode(node: ts.TemplateLiteral, tagCondition: StrictTagCondition) {
+ if (tagCondition.allowNotTaggedTemplate) return true;
+ return !!getTemplateNodeUnder(node.parent, tagCondition);
+}
+
+export function getTagName(
+ node: ts.TemplateLiteral,
+ { allowNotTaggedTemplate, allowFunctionCallExpression, allowTaggedTemplateExpression, names }: StrictTagCondition,
+) {
+ if (!node.parent) return undefined;
+ if (!ts.isCallExpression(node.parent) && !ts.isTaggedTemplateExpression(node.parent)) {
+ return allowNotTaggedTemplate ? '' : undefined;
+ }
+ if (allowFunctionCallExpression && ts.isCallExpression(node.parent) && ts.isIdentifier(node.parent.expression)) {
+ const name = node.parent.expression.escapedText as string;
+ return names.includes(name) ? name : allowNotTaggedTemplate ? '' : undefined;
+ }
+ if (allowTaggedTemplateExpression && ts.isTaggedTemplateExpression(node.parent) && ts.isIdentifier(node.parent.tag)) {
+ const name = node.parent.tag.escapedText as string;
+ return names.includes(name) ? name : allowNotTaggedTemplate ? '' : undefined;
+ }
+ return undefined;
+}
diff --git a/src/ts-ast-util/types.ts b/src/ts-ast-util/types.ts
index fd3692464..45a8d5fc7 100644
--- a/src/ts-ast-util/types.ts
+++ b/src/ts-ast-util/types.ts
@@ -120,6 +120,22 @@ export type ComputePosition = (innerPosition: number) => {
isInOtherExpression?: boolean;
};
+export type TagConfig =
+ | undefined
+ | string
+ | string[]
+ | {
+ name?: string | string[];
+ ignoreFunctionCallExpression?: boolean;
+ };
+
+export type StrictTagCondition = {
+ names: string[];
+ allowNotTaggedTemplate: boolean;
+ allowTaggedTemplateExpression: boolean;
+ allowFunctionCallExpression: boolean;
+};
+
/**
*
* Serves the following information.
@@ -154,7 +170,7 @@ export interface ResolveResult {
}
export interface ScriptSourceHelper {
- getAllNodes: (fileName: string, condition: (n: ts.Node) => boolean) => ts.Node[];
+ getAllNodes: (fileName: string, condition: (n: ts.Node) => undefined | boolean | S) => S[];
getNode: (fileName: string, position: number) => ts.Node | undefined;
getLineAndChar: (fileName: string, position: number) => ts.LineAndCharacter;
isExcluded: (fileName: string) => boolean;
diff --git a/src/ts-ast-util/utilily-functions.test.ts b/src/ts-ast-util/utilily-functions.test.ts
index 893014f11..174cc1f4a 100644
--- a/src/ts-ast-util/utilily-functions.test.ts
+++ b/src/ts-ast-util/utilily-functions.test.ts
@@ -1,35 +1,13 @@
import ts from 'typescript';
import {
findAllNodes,
- findNode,
getSanitizedTemplateText,
- isTagged,
isImportDeclarationWithCondition,
mergeImportDeclarationsWithSameModules,
removeAliasFromImportDeclaration,
} from './utilily-functions';
import { printNode } from './testing/print-node';
-describe(isTagged, () => {
- it('should return true when the tag condition is matched', () => {
- // prettier-ignore
- const text = 'function myTag(...args: any[]) { return "" }' + '\n'
- + 'const x = myTag`query { }`';
- const s = ts.createSourceFile('input.ts', text, ts.ScriptTarget.Latest, true);
- const node = findNode(s, text.length - 3) as ts.Node;
- expect(isTagged(node, 'myTag')).toBeTruthy();
- });
-
- it('should return true when the tag condition is not matched', () => {
- // prettier-ignore
- const text = 'function myTag(...args: any[]) { return "" }' + '\n'
- + 'const x = myTag`query { }`';
- const s = ts.createSourceFile('input.ts', text, ts.ScriptTarget.Latest, true);
- const node = findNode(s, text.length - 3) as ts.Node;
- expect(isTagged(node, 'MyTag')).toBeFalsy();
- });
-});
-
describe(findAllNodes, () => {
it('should return nodes which match given condition', () => {
// prettier-ignore
diff --git a/src/ts-ast-util/utilily-functions.ts b/src/ts-ast-util/utilily-functions.ts
index 34826b65b..68a284609 100644
--- a/src/ts-ast-util/utilily-functions.ts
+++ b/src/ts-ast-util/utilily-functions.ts
@@ -37,8 +37,6 @@ function removeFromImportClause(base: ts.ImportClause | undefined, name: string)
return astf.updateImportClause(base, base.isTypeOnly, nameId, namedBindings);
}
-export type TagCondition = string;
-
export function findNode(sourceFile: ts.SourceFile, position: number): ts.Node | undefined {
function find(node: ts.Node): ts.Node | undefined {
if (position >= node.getStart() && position < node.getEnd()) {
@@ -66,18 +64,6 @@ export function findAllNodes(
return result as S[];
}
-export function hasTagged(node: ts.Node | undefined, condition: TagCondition, source?: ts.SourceFile) {
- if (!node) return;
- if (!ts.isTaggedTemplateExpression(node)) return false;
- const tagNode = node;
- return tagNode.tag.getText(source) === condition;
-}
-
-export function isTagged(node: ts.Node | undefined, condition: TagCondition, source?: ts.SourceFile) {
- if (!node) return false;
- return hasTagged(node.parent, condition, source);
-}
-
export function getSanitizedTemplateText(
node: ts.NoSubstitutionTemplateLiteral | ts.TemplateExpression,
source?: ts.SourceFile,
diff --git a/src/typegen-addons/testing/addon-tester.ts b/src/typegen-addons/testing/addon-tester.ts
index acdc23a3f..81288de4f 100644
--- a/src/typegen-addons/testing/addon-tester.ts
+++ b/src/typegen-addons/testing/addon-tester.ts
@@ -3,6 +3,7 @@ import { buildSchema } from 'graphql';
import { TypeGenAddonFactory } from '../../typegen';
import { createTesintExtractor } from '../../analyzer/testing/testing-extractor';
import { TypeGenerator } from '../../analyzer/type-generator';
+import { parseTagConfig, type TagConfig } from '../../ts-ast-util';
function createTestingTypeGenerator({
files = [],
@@ -10,13 +11,13 @@ function createTestingTypeGenerator({
addonFactories = [],
}: {
files?: { fileName: string; content: string }[];
- tag?: string;
+ tag?: TagConfig;
addonFactories?: TypeGenAddonFactory[];
}) {
const extractor = createTesintExtractor(files, true);
const generator = new TypeGenerator({
prjRootPath: '',
- tag,
+ tag: parseTagConfig(tag),
addonFactories,
extractor,
debug: () => {},
diff --git a/src/types.ts b/src/types.ts
index 5129f8ca7..690c8a666 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,11 +1,12 @@
-import { SchemaConfig } from './schema-manager';
+import type { SchemaConfig } from './schema-manager';
+import type { TagConfig } from './ts-ast-util';
export type TsGraphQLPluginConfigOptions = SchemaConfig & {
name: string;
exclude?: string[];
enabledGlobalFragments?: boolean;
removeDuplicatedFragments?: boolean;
- tag?: string;
+ tag?: TagConfig;
typegen?: {
addons?: string[];
};