Skip to content

Commit

Permalink
Add option to pluck from custom Vue block (#4439)
Browse files Browse the repository at this point in the history
* Add option to pluck from custom Vue block

* Changeset

* Go

* Prettier

---------

Co-authored-by: Arda TANRIKULU <[email protected]>
  • Loading branch information
jaschaephraim and ardatan authored May 14, 2024
1 parent bc88a56 commit ee0daab
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-lobsters-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-tools/graphql-tag-pluck": patch
---

Add option to pluck from custom Vue block
74 changes: 63 additions & 11 deletions packages/graphql-tag-pluck/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export interface GraphQLTagPluckOptions {
* The magic comment anchor to look for when parsing GraphQL strings. Defaults to `graphql`.
*/
gqlMagicComment?: string;
/**
* The name of a custom Vue block that contains raw GraphQL to be plucked.
*/
gqlVueBlock?: string;
/**
* Allows to use a global identifier instead of a module import.
* ```js
Expand Down Expand Up @@ -154,6 +158,23 @@ function parseWithVue(vueTemplateCompiler: typeof import('@vue/compiler-sfc'), f
: '';
}

function customBlockFromVue(
// tslint:disable-next-line: no-implicit-dependencies
vueTemplateCompiler: typeof import('@vue/compiler-sfc'),
fileData: string,
filePath: string,
blockType: string,
): Source | undefined {
const { descriptor } = vueTemplateCompiler.parse(fileData);

const block = descriptor.customBlocks.find(b => b.type === blockType);
if (block === undefined) {
return;
}

return new Source(block.content.trim(), filePath, block.loc.start);
}

// tslint:disable-next-line: no-implicit-dependencies
function parseWithSvelte(svelte2tsx: typeof import('svelte2tsx'), fileData: string) {
const fileInTsx = svelte2tsx.svelte2tsx(fileData);
Expand Down Expand Up @@ -192,17 +213,26 @@ export const gqlPluckFromCodeString = async (
validate({ code, options });

const fileExt = extractExtension(filePath);

let blockSource;
if (fileExt === '.vue') {
if (options.gqlVueBlock) {
blockSource = await pluckVueFileCustomBlock(code, filePath, options.gqlVueBlock);
}
code = await pluckVueFileScript(code);
} else if (fileExt === '.svelte') {
code = await pluckSvelteFileScript(code);
} else if (fileExt === '.astro') {
code = await pluckAstroFileScript(code);
}

return parseCode({ code, filePath, options }).map(
const sources = parseCode({ code, filePath, options }).map(
t => new Source(t.content, filePath, t.loc.start),
);
if (blockSource) {
sources.push(blockSource);
}
return sources;
};

/**
Expand All @@ -222,17 +252,26 @@ export const gqlPluckFromCodeStringSync = (
validate({ code, options });

const fileExt = extractExtension(filePath);

let blockSource;
if (fileExt === '.vue') {
if (options.gqlVueBlock) {
blockSource = pluckVueFileCustomBlockSync(code, filePath, options.gqlVueBlock);
}
code = pluckVueFileScriptSync(code);
} else if (fileExt === '.svelte') {
code = pluckSvelteFileScriptSync(code);
} else if (fileExt === '.astro') {
code = pluckAstroFileScriptSync(code);
}

return parseCode({ code, filePath, options }).map(
const sources = parseCode({ code, filePath, options }).map(
t => new Source(t.content, filePath, t.loc.start),
);
if (blockSource) {
sources.push(blockSource);
}
return sources;
};

export function parseCode({
Expand Down Expand Up @@ -320,31 +359,44 @@ const MissingAstroCompilerError = new Error(
`),
);

async function pluckVueFileScript(fileData: string) {
let vueTemplateCompiler: typeof import('@vue/compiler-sfc');
async function loadVueCompilerSync() {
try {
// eslint-disable-next-line import/no-extraneous-dependencies
vueTemplateCompiler = await import('@vue/compiler-sfc');
return await import('@vue/compiler-sfc');
} catch (e: any) {
throw MissingVueTemplateCompilerError;
}

return parseWithVue(vueTemplateCompiler, fileData);
}

function pluckVueFileScriptSync(fileData: string) {
let vueTemplateCompiler: typeof import('@vue/compiler-sfc');

function loadVueCompilerAsync() {
try {
// eslint-disable-next-line import/no-extraneous-dependencies
vueTemplateCompiler = require('@vue/compiler-sfc');
return require('@vue/compiler-sfc');
} catch (e: any) {
throw MissingVueTemplateCompilerError;
}
}

async function pluckVueFileScript(fileData: string) {
const vueTemplateCompiler = await loadVueCompilerSync();
return parseWithVue(vueTemplateCompiler, fileData);
}

function pluckVueFileScriptSync(fileData: string) {
const vueTemplateCompiler = loadVueCompilerAsync();
return parseWithVue(vueTemplateCompiler, fileData);
}

async function pluckVueFileCustomBlock(fileData: string, filePath: string, blockType: string) {
const vueTemplateCompiler = await loadVueCompilerSync();
return customBlockFromVue(vueTemplateCompiler, fileData, filePath, blockType);
}

function pluckVueFileCustomBlockSync(fileData: string, filePath: string, blockType: string) {
const vueTemplateCompiler = loadVueCompilerAsync();
return customBlockFromVue(vueTemplateCompiler, fileData, filePath, blockType);
}

async function pluckSvelteFileScript(fileData: string) {
let svelte2tsx: typeof import('svelte2tsx');
try {
Expand Down
82 changes: 81 additions & 1 deletion packages/graphql-tag-pluck/tests/graphql-tag-pluck.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ describe('graphql-tag-pluck', () => {
);
});

it.only("should pluck graphql-tag template literals from .ts that use 'using' keyword", async () => {
it("should pluck graphql-tag template literals from .ts that use 'using' keyword", async () => {
const sources = await pluck(
'tmp-XXXXXX.ts',
freeText(`
Expand Down Expand Up @@ -1986,6 +1986,86 @@ describe('graphql-tag-pluck', () => {
);
});

it('should be able to specify a custom Vue block to pluck from', async () => {
const sources = await pluck(
'tmp-XXXXXX.vue',
freeText(`
<template lang="pug">
<div>test</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import gql from 'graphql-tag';
export default defineComponent({
name: 'TestComponent',
setup(){
return {
pageQuery: gql\`
query IndexQuery {
site {
siteMetadata {
title
}
}
}
\`
}
}
})
// export const pageQuery = gql\`
// query OtherQuery {
// site {
// siteMetadata {
// title
// }
// }
// }
// \`;
</script>
<style lang="scss">
.test { color: red };
</style>
<graphql lang="gql">
query CustomBlockQuery {
site {
siteMetadata {
title
}
}
}
</graphql>
`),
{
gqlVueBlock: 'graphql',
},
);

expect(sources.map(source => source.body).join('\n\n')).toEqual(
freeText(`
query IndexQuery {
site {
siteMetadata {
title
}
}
}
query CustomBlockQuery {
site {
siteMetadata {
title
}
}
}
`),
);
});

it('should be able to specify the package name of which the GraphQL identifier should be imported from', async () => {
const sources = await pluck(
'tmp-XXXXXX.js',
Expand Down
64 changes: 44 additions & 20 deletions packages/load/tests/loaders/schema/schema-from-export.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,53 +21,77 @@ describe('Schema From Export', () => {
sync: loadSchemaSync,
})((load, mode) => {
test('should load the schema correctly from module.exports', async () => {
const result = await load('./tests/loaders/schema/test-files/loaders/module-exports.js', {
loaders: [new CodeFileLoader()],
});
const result = await load(
'../../../../loaders/code-file/tests/test-files/loaders/module-exports.js',
{
loaders: [new CodeFileLoader()],
cwd: __dirname,
},
);
expect(isSchema(result)).toBeTruthy();
});

test('should load the schema (with extend) correctly from module.exports', async () => {
const result = await load('./tests/loaders/schema/test-files/schema-dir/with-extend.js', {
loaders: [new CodeFileLoader()],
});
const result = await load(
'../../../../loaders/code-file/tests/test-files/loaders/with-extend.js',
{
loaders: [new CodeFileLoader()],
cwd: __dirname,
},
);
expect(isSchema(result)).toBeTruthy();
const QueryType = result.getQueryType();
assertNonMaybe(QueryType);
expect(QueryType.getFields()['hello']).toBeDefined();
});

test('should load the schema correctly from variable export', async () => {
const result = await load('./tests/loaders/schema/test-files/loaders/schema-export.js', {
loaders: [new CodeFileLoader()],
});
const result = await load(
'../../../../loaders/code-file/tests/test-files/loaders/schema-export.js',
{
loaders: [new CodeFileLoader()],
cwd: __dirname,
},
);
expect(isSchema(result)).toBeTruthy();
});

test('should load the schema correctly from default export', async () => {
const result = await load('./tests/loaders/schema/test-files/loaders/default-export.js', {
loaders: [new CodeFileLoader()],
});
const result = await load(
'../../../../loaders/code-file/tests/test-files/loaders/default-export.js',
{
loaders: [new CodeFileLoader()],
cwd: __dirname,
},
);
expect(isSchema(result)).toBeTruthy();
});

if (mode === 'async') {
test('should load the schema correctly from promise export', async () => {
const result = await load('./tests/loaders/schema/test-files/loaders/promise-export.js', {
loaders: [new CodeFileLoader()],
});
const result = await load(
'../../../../loaders/code-file/tests/test-files/loaders/promise-export.js',
{
loaders: [new CodeFileLoader()],
cwd: __dirname,
},
);
expect(isSchema(result)).toBeTruthy();
});

test('should load the schema correctly from promise export', async () => {
const result = await load('./tests/loaders/schema/test-files/loaders/promise-export.js', {
loaders: [new CodeFileLoader()],
});
const result = await load(
'../../../../loaders/code-file/tests/test-files/loaders/promise-export.js',
{
loaders: [new CodeFileLoader()],
cwd: __dirname,
},
);
expect(isSchema(result)).toBeTruthy();
});
}

test.only('should work with extensions (without schema definition)', async () => {
test('should work with extensions (without schema definition)', async () => {
const schemaPath = './tests/loaders/schema/test-files/schema-dir/extensions/export-schema.js';
const schema = await load(schemaPath, {
loaders: [new CodeFileLoader()],
Expand All @@ -80,7 +104,7 @@ describe('Schema From Export', () => {
expect(queryFields).toContain('bar');
});

test.only('should work with extensions (with schema definition)', async () => {
test('should work with extensions (with schema definition)', async () => {
const schemaPath =
'./tests/loaders/schema/test-files/schema-dir/extensions/export-schema-with-def.js';
const schema = await load(schemaPath, {
Expand Down

0 comments on commit ee0daab

Please sign in to comment.