UNPKG

@graphql-tools/graphql-tag-pluck

Version:
308 lines (291 loc) • 10.3 kB
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); /* eslint-disable @typescript-eslint/no-require-imports */ import { Source } from 'graphql'; import { parse } from '@babel/parser'; import traversePkg from '@babel/traverse'; import generateConfig from './config.js'; import { getExtNameFromFilePath } from './libs/extname.js'; import { freeText } from './utils.js'; import createVisitor from './visitor.js'; function getDefault(module) { return module.default || module; } const traverse = getDefault(traversePkg); const supportedExtensions = [ '.js', '.mjs', '.cjs', '.jsx', '.ts', '.mts', '.cts', '.tsx', '.flow', '.flow.js', '.flow.jsx', '.vue', '.svelte', '.astro', '.gts', '.gjs', ]; // tslint:disable-next-line: no-implicit-dependencies function parseWithVue(vueTemplateCompiler, fileData) { const { descriptor } = vueTemplateCompiler.parse(fileData); return descriptor.script || descriptor.scriptSetup ? vueTemplateCompiler.compileScript(descriptor, { id: Date.now().toString() }).content : ''; } function customBlockFromVue( // tslint:disable-next-line: no-implicit-dependencies vueTemplateCompiler, fileData, filePath, blockType) { 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, fileData) { const fileInTsx = svelte2tsx.svelte2tsx(fileData); return fileInTsx.code; } // tslint:disable-next-line: no-implicit-dependencies async function parseWithAstro(astroCompiler, fileData) { const fileInTsx = await astroCompiler.transform(fileData); return fileInTsx.code; } function parseWithAstroSync( // tslint:disable-next-line: no-implicit-dependencies astroCompiler, fileData) { const fileInTsx = astroCompiler.transform(fileData, undefined); return fileInTsx.code; } function transformGlimmerFile(glimmerSyntax, fileData) { const processor = new glimmerSyntax.Preprocessor(); return processor.process(fileData); } /** * 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`, `.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. * @param options Additional options for determining how a file is parsed. */ export const gqlPluckFromCodeString = async (filePath, code, options = {}) => { 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); } else if (fileExt === '.gts' || fileExt === '.gjs') { code = await pluckGlimmerFileScript(code); } const sources = parseCode({ code, filePath, options }).map(t => new Source(t.content, filePath, t.loc.start)); if (blockSource) { sources.push(blockSource); } return sources; }; /** * 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`, `.astro`, `.gts`, `.gjs` * * @param filePath Path to the file containing the code. Required to detect the file type * @param code The contents of the file being parsed. * @param options Additional options for determining how a file is parsed. */ export const gqlPluckFromCodeStringSync = (filePath, code, options = {}) => { 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); } else if (fileExt === '.gts' || fileExt === '.gjs') { code = pluckGlimmerFileScriptSync(code); } 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({ code, filePath, options, }) { const out = { returnValue: null }; const ast = parse(code, generateConfig(filePath, code, options)); const visitor = createVisitor(code, out, options); traverse(ast, visitor); return out.returnValue || []; } function validate({ code, options }) { if (typeof code !== 'string') { throw TypeError('Provided code must be a string'); } if (!(options instanceof Object)) { throw TypeError(`Options arg must be an object`); } } function extractExtension(filePath) { const fileExt = getExtNameFromFilePath(filePath); if (fileExt) { if (!supportedExtensions.includes(fileExt)) { throw TypeError(`Provided file type must be one of ${supportedExtensions.join(', ')} `); } } return fileExt; } const MissingVueTemplateCompilerError = new Error(freeText(` GraphQL template literals cannot be plucked from a Vue template code without having the "@vue/compiler-sfc" package installed. Please install it and try again. Via NPM: $ npm install @vue/compiler-sfc Via Yarn: $ yarn add @vue/compiler-sfc `)); const MissingSvelteTemplateCompilerError = new Error(freeText(` GraphQL template literals cannot be plucked from a Svelte template code without having the "svelte2tsx" & "svelte" package installed. Please install it and try again. Via NPM: $ npm install svelte2tsx svelte Via Yarn: $ yarn add svelte2tsx svelte `)); 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 `)); const MissingGlimmerCompilerError = new Error(freeText(` GraphQL template literals cannot be plucked from a Glimmer template code without having the "content-tag" package installed. Please install it and try again. Via NPM: $ npm install content-tag Via Yarn: $ yarn add content-tag `)); async function loadVueCompilerAsync() { try { // eslint-disable-next-line import/no-extraneous-dependencies return await import('@vue/compiler-sfc'); } catch { throw MissingVueTemplateCompilerError; } } function loadVueCompilerSync() { try { // eslint-disable-next-line import/no-extraneous-dependencies return require('@vue/compiler-sfc'); } catch { throw MissingVueTemplateCompilerError; } } async function pluckVueFileScript(fileData) { const vueTemplateCompiler = await loadVueCompilerAsync(); return parseWithVue(vueTemplateCompiler, fileData); } function pluckVueFileScriptSync(fileData) { const vueTemplateCompiler = loadVueCompilerSync(); return parseWithVue(vueTemplateCompiler, fileData); } async function pluckVueFileCustomBlock(fileData, filePath, blockType) { const vueTemplateCompiler = await loadVueCompilerSync(); return customBlockFromVue(vueTemplateCompiler, fileData, filePath, blockType); } function pluckVueFileCustomBlockSync(fileData, filePath, blockType) { const vueTemplateCompiler = loadVueCompilerSync(); return customBlockFromVue(vueTemplateCompiler, fileData, filePath, blockType); } async function pluckSvelteFileScript(fileData) { let svelte2tsx; try { // eslint-disable-next-line import/no-extraneous-dependencies svelte2tsx = await import('svelte2tsx'); } catch { throw MissingSvelteTemplateCompilerError; } return parseWithSvelte(svelte2tsx, fileData); } function pluckSvelteFileScriptSync(fileData) { let svelte2tsx; try { // eslint-disable-next-line import/no-extraneous-dependencies svelte2tsx = require('svelte2tsx'); } catch { throw MissingSvelteTemplateCompilerError; } return parseWithSvelte(svelte2tsx, fileData); } async function pluckAstroFileScript(fileData) { let astroCompiler; try { // eslint-disable-next-line import/no-extraneous-dependencies astroCompiler = await import('@astrojs/compiler'); } catch { throw MissingAstroCompilerError; } return parseWithAstro(astroCompiler, fileData); } function pluckAstroFileScriptSync(fileData) { let astroCompiler; try { // eslint-disable-next-line import/no-extraneous-dependencies astroCompiler = require('astrojs-compiler-sync'); } catch { throw MissingAstroCompilerError; } return parseWithAstroSync(astroCompiler, fileData); } async function pluckGlimmerFileScript(fileData) { let contentTag; try { contentTag = await import('content-tag'); } catch { throw MissingGlimmerCompilerError; } return transformGlimmerFile(contentTag, fileData); } function pluckGlimmerFileScriptSync(fileData) { let contentTag; try { contentTag = require('content-tag'); } catch { throw MissingGlimmerCompilerError; } return transformGlimmerFile(contentTag, fileData); }