Skip to content
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

Add option to pluck from custom Vue block #4439

Merged
merged 4 commits into from
May 14, 2024
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
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
Loading