Skip to content

Commit

Permalink
Add option to pluck from custom Vue block
Browse files Browse the repository at this point in the history
  • Loading branch information
jaschaephraim committed May 4, 2022
1 parent 27c0e41 commit c98d5c9
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 12 deletions.
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 @@ -90,6 +90,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 @@ -122,6 +126,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 All @@ -145,13 +166,22 @@ 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);
}

return parseCode({ code, filePath, options }).map(t => new Source(t.content, filePath, t.loc.start));
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 @@ -171,13 +201,22 @@ 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);
}

return parseCode({ code, filePath, options }).map(t => new Source(t.content, filePath, t.loc.start));
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 @@ -250,31 +289,44 @@ const MissingSvelteTemplateCompilerError = 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
80 changes: 80 additions & 0 deletions packages/graphql-tag-pluck/tests/graphql-tag-pluck.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1733,6 +1733,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
6 changes: 5 additions & 1 deletion website/docs/graphql-tag-pluck.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Template literals leaded by magic comments will also be extracted :-)
`
```

supported file extensions are: `.js`, `.jsx`, `.ts`, `.tsx`, `.flow`, `.flow.js`, `.flow.jsx`, `.graphqls`, `.graphql`, `.gqls`, `.gql`.
supported file extensions are: `.js`, `.jsx`, `.ts`, `.tsx`, `.flow`, `.flow.js`, `.flow.jsx`, `.vue`, `.graphqls`, `.graphql`, `.gqls`, `.gql`.

### Options

Expand All @@ -89,6 +89,10 @@ It is recommended to look at the [source code](https://github.com/ardatan/graphq

The magic comment anchor to look for when parsing GraphQL strings. Defaults to `graphql`, which may be translated into `/* GraphQL */` in code.

- **`gqlVueBlock`**

The name of a custom Vue block that contains raw GraphQL to be plucked.

- **`globalGqlIdentifierName`**

Allows to use a global identifier instead of a module import.
Expand Down

0 comments on commit c98d5c9

Please sign in to comment.