From a2195e0c369c07fcc922270482599cc58192681c Mon Sep 17 00:00:00 2001 From: XiNiHa Date: Sat, 16 Dec 2023 20:15:16 +0900 Subject: [PATCH] feat: let graphql-tag-pluck load from astro files --- .changeset/wise-dolphins-train.md | 6 + packages/graphql-tag-pluck/package.json | 4 +- packages/graphql-tag-pluck/src/config.ts | 3 + packages/graphql-tag-pluck/src/index.ts | 62 +++++++- .../tests/graphql-tag-pluck.test.ts | 132 ++++++++++++++++++ packages/loaders/code-file/src/index.ts | 3 +- yarn.lock | 5 + 7 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 .changeset/wise-dolphins-train.md diff --git a/.changeset/wise-dolphins-train.md b/.changeset/wise-dolphins-train.md new file mode 100644 index 00000000000..9e1325f7553 --- /dev/null +++ b/.changeset/wise-dolphins-train.md @@ -0,0 +1,6 @@ +--- +'@graphql-tools/code-file-loader': minor +'@graphql-tools/graphql-tag-pluck': minor +--- + +Add .astro file support diff --git a/packages/graphql-tag-pluck/package.json b/packages/graphql-tag-pluck/package.json index e026bbb6871..00a9e6cdb36 100644 --- a/packages/graphql-tag-pluck/package.json +++ b/packages/graphql-tag-pluck/package.json @@ -59,6 +59,7 @@ "tslib": "^2.4.0" }, "devDependencies": { + "@astrojs/compiler": "^2.3.4", "@babel/parser": "7.23.6", "@babel/traverse": "7.23.7", "@babel/types": "7.23.6", @@ -75,7 +76,8 @@ "buildOptions": { "external": [ "@vue/compiler-sfc", - "svelte2tsx" + "svelte2tsx", + "@astrojs/compiler" ] }, "typescript": { diff --git a/packages/graphql-tag-pluck/src/config.ts b/packages/graphql-tag-pluck/src/config.ts index a3e26eaea75..99be5ea61d6 100644 --- a/packages/graphql-tag-pluck/src/config.ts +++ b/packages/graphql-tag-pluck/src/config.ts @@ -76,6 +76,9 @@ export default function generateConfig( case '.svelte': plugins.push('typescript', 'svelte'); break; + case '.astro': + plugins.push('typescript', 'jsx'); + break; default: plugins.push('jsx', ...dynamicFlowPlugins); break; diff --git a/packages/graphql-tag-pluck/src/index.ts b/packages/graphql-tag-pluck/src/index.ts index c49d355a1aa..ee426c9f9e1 100644 --- a/packages/graphql-tag-pluck/src/index.ts +++ b/packages/graphql-tag-pluck/src/index.ts @@ -142,6 +142,7 @@ const supportedExtensions = [ '.flow.jsx', '.vue', '.svelte', + '.astro', ]; // tslint:disable-next-line: no-implicit-dependencies @@ -159,10 +160,25 @@ function parseWithSvelte(svelte2tsx: typeof import('svelte2tsx'), fileData: stri return fileInTsx.code; } +// tslint:disable-next-line: no-implicit-dependencies +async function parseWithAstro(astroCompiler: typeof import('@astrojs/compiler'), fileData: string) { + const fileInTsx = await astroCompiler.transform(fileData); + return fileInTsx.code; +} + +// tslint:disable-next-line: no-implicit-dependencies +function parseWithAstroSync( + astroCompiler: typeof import('@astrojs/compiler/sync'), + fileData: string, +) { + const fileInTsx = astroCompiler.transform(fileData, undefined); + return fileInTsx.code; +} + /** * Asynchronously plucks GraphQL template literals from a single file. * - * Supported file extensions include: `.js`, `.mjs`, `.cjs`, `.jsx`, `.ts`, `.mts`, `.cts`, `.tsx`, `.flow`, `.flow.js`, `.flow.jsx`, `.vue`, `.svelte` + * Supported file extensions include: `.js`, `.mjs`, `.cjs`, `.jsx`, `.ts`, `.mts`, `.cts`, `.tsx`, `.flow`, `.flow.js`, `.flow.jsx`, `.vue`, `.svelte`, `.astro` * * @param filePath Path to the file containing the code. Required to detect the file type * @param code The contents of the file being parsed. @@ -180,6 +196,8 @@ export const gqlPluckFromCodeString = async ( 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( @@ -190,7 +208,7 @@ export const gqlPluckFromCodeString = async ( /** * Synchronously plucks GraphQL template literals from a single file * - * Supported file extensions include: `.js`, `.mjs`, `.cjs`, `.jsx`, `.ts`, `.mjs`, `.cjs`, `.tsx`, `.flow`, `.flow.js`, `.flow.jsx`, `.vue`, `.svelte` + * Supported file extensions include: `.js`, `.mjs`, `.cjs`, `.jsx`, `.ts`, `.mjs`, `.cjs`, `.tsx`, `.flow`, `.flow.js`, `.flow.jsx`, `.vue`, `.svelte`, `.astro` * * @param filePath Path to the file containing the code. Required to detect the file type * @param code The contents of the file being parsed. @@ -208,6 +226,8 @@ export const gqlPluckFromCodeStringSync = ( code = pluckVueFileScriptSync(code); } else if (fileExt === '.svelte') { code = pluckSvelteFileScriptSync(code); + } else if (fileExt === '.astro') { + code = pluckAstroFileScriptSync(code); } return parseCode({ code, filePath, options }).map( @@ -285,6 +305,21 @@ const MissingSvelteTemplateCompilerError = new Error( `), ); +const MissingAstroCompilerError = new Error( + freeText(` + GraphQL template literals cannot be plucked from a Astro template code without having the "@astrojs/compiler" package installed. + Please install it and try again. + + Via NPM: + + $ npm install @astrojs/compiler + + Via Yarn: + + $ yarn add @astrojs/compiler + `), +); + async function pluckVueFileScript(fileData: string) { let vueTemplateCompiler: typeof import('@vue/compiler-sfc'); try { @@ -334,3 +369,26 @@ function pluckSvelteFileScriptSync(fileData: string) { return parseWithSvelte(svelte2tsx, fileData); } + +async function pluckAstroFileScript(fileData: string) { + let astroCompiler: typeof import('@astrojs/compiler'); + try { + // eslint-disable-next-line import/no-extraneous-dependencies + astroCompiler = await import('@astrojs/compiler'); + } catch (e: any) { + throw MissingAstroCompilerError; + } + + return parseWithAstro(astroCompiler, fileData); +} + +function pluckAstroFileScriptSync(fileData: string) { + let astroCompiler: typeof import('@astrojs/compiler/sync'); + try { + astroCompiler = require('@astrojs/compiler/sync'); + } catch (e: any) { + throw MissingAstroCompilerError; + } + + return parseWithAstroSync(astroCompiler, fileData); +} diff --git a/packages/graphql-tag-pluck/tests/graphql-tag-pluck.test.ts b/packages/graphql-tag-pluck/tests/graphql-tag-pluck.test.ts index 6713fbba2df..cda96cece82 100644 --- a/packages/graphql-tag-pluck/tests/graphql-tag-pluck.test.ts +++ b/packages/graphql-tag-pluck/tests/graphql-tag-pluck.test.ts @@ -1163,6 +1163,138 @@ describe('graphql-tag-pluck', () => { ); }); + it('should pluck graphql-tag template literals from .astro file', async () => { + const sources = await pluck( + 'tmp-XXXXXX.astro', + freeText(` + --- + import gql from 'graphql-tag'; + + let q = gql\` + query IndexQuery { + site { + siteMetadata { + title + } + } + } + \`; + --- + +
foo
+ `), + ); + + expect(sources.map(source => source.body).join('\n\n')).toEqual( + freeText(` + query IndexQuery { + site { + siteMetadata { + title + } + } + } + `), + ); + }); + + it('should pluck graphql-tag template literals from .astro file with 2 queries', async () => { + const sources = await pluck( + 'tmp-XXXXXX.astro', + freeText(` + --- + import gql from 'graphql-tag'; + + let q = gql\` + query IndexQuery { + site { + siteMetadata { + title + } + } + } + \`; + let q2 = gql\` + query IndexQuery2 { + site { + siteMetadata { + title + } + } + } + \`; + --- + +
foo
+ `), + ); + + expect(sources.map(source => source.body).join('\n\n')).toEqual( + freeText(` + query IndexQuery { + site { + siteMetadata { + title + } + } + } + + query IndexQuery2 { + site { + siteMetadata { + title + } + } + } + `), + ); + }); + + it('should pluck graphql-tag template literals from .astro removing comments', async () => { + const sources = await pluck( + 'tmp-XXXXXX.astro', + freeText(` + --- + import gql from 'graphql-tag'; + + let q = gql\` + query IndexQuery { + site { + siteMetadata { + title + } + } + } + \`; + + // let q2 = gql\` + // query IndexQuery2 { + // site { + // siteMetadata { + // title + // } + // } + // } + // \`; + --- + +
foo
+ `), + ); + + expect(sources.map(source => source.body).join('\n\n')).toEqual( + freeText(` + query IndexQuery { + site { + siteMetadata { + title + } + } + } + `), + ); + }); + it('should pluck graphql-tag template literals from .tsx file with generic jsx elements', async () => { const sources = await pluck( 'tmp-XXXXXX.tsx', diff --git a/packages/loaders/code-file/src/index.ts b/packages/loaders/code-file/src/index.ts index 514d7ea3afa..b5cc486a3ed 100644 --- a/packages/loaders/code-file/src/index.ts +++ b/packages/loaders/code-file/src/index.ts @@ -54,6 +54,7 @@ const FILE_EXTENSIONS = [ '.jsx', '.vue', '.svelte', + '.astro', ]; function createGlobbyOptions(options: CodeFileLoaderOptions): GlobbyOptions { @@ -75,7 +76,7 @@ const buildIgnoreGlob = (path: string) => `!${path}`; * ``` * * Supported extensions include: `.ts`, `.mts`, `.cts`, `.tsx`, `.js`, `.mjs`, - * `.cjs`, `.jsx`, `.vue`, `.svelte` + * `.cjs`, `.jsx`, `.vue`, `.svelte`, `.astro` */ export class CodeFileLoader implements Loader { private config: CodeFileLoaderConfig; diff --git a/yarn.lock b/yarn.lock index dbfdd1985fe..f4e38f1db0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -373,6 +373,11 @@ dependencies: node-fetch "^2.6.1" +"@astrojs/compiler@^2.3.4": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-2.3.4.tgz#4dbc169de1f071508bf30db390890f16cb266416" + integrity sha512-33/YtWoBCE0cBUNy1kh78FCDXBoBANX87ShgATlAHECYbG2+buNTAgq4Xgz4t5NgnEHPN21GIBC2Mvvwisoutw== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244"