From 1a736fc23aefbfcc7b003d5d1d194ee37c8a8ecb Mon Sep 17 00:00:00 2001 From: Alec Aivazis Date: Sun, 5 Nov 2023 01:04:47 -0800 Subject: [PATCH] Fix plugin runtime search (#1226) --- .changeset/cool-bees-do.md | 5 ++ .github/workflows/canary.yml | 2 +- .../src/runtime/routing/Router.tsx | 6 -- .../src/codegen/generators/runtime/index.ts | 86 ++----------------- .../generators/runtime/pluginRuntime.ts | 67 +++++++++++++++ packages/houdini/src/codegen/index.ts | 5 -- packages/houdini/src/lib/config.test.ts | 5 +- packages/houdini/src/lib/config.ts | 19 +++- 8 files changed, 102 insertions(+), 93 deletions(-) create mode 100644 .changeset/cool-bees-do.md create mode 100644 packages/houdini/src/codegen/generators/runtime/pluginRuntime.ts diff --git a/.changeset/cool-bees-do.md b/.changeset/cool-bees-do.md new file mode 100644 index 000000000..ce2def600 --- /dev/null +++ b/.changeset/cool-bees-do.md @@ -0,0 +1,5 @@ +--- +'houdini': patch +--- + +Fix bug causing inifinite loops in vite dev server diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 666edc902..166587561 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -6,7 +6,7 @@ on: push: branches: - next - - component-fields + - plugin-runtime-search env: CI: true diff --git a/packages/houdini-react/src/runtime/routing/Router.tsx b/packages/houdini-react/src/runtime/routing/Router.tsx index c385ba688..9db465411 100644 --- a/packages/houdini-react/src/runtime/routing/Router.tsx +++ b/packages/houdini-react/src/runtime/routing/Router.tsx @@ -167,14 +167,11 @@ function usePageData({ last_variables.set(artifact, variables) } - console.log('loading query', artifact.name) - // TODO: AbortController on send() // TODO: we can read from cache here before making an asynchronous network call // if there is a pending request and we were asked to load, don't do anything if (ssr_signals.has(id)) { - console.log('using ssr signal', id) return ssr_signals.get(id)! } @@ -187,7 +184,6 @@ function usePageData({ resolve = res reject = rej - console.log('sending query', id, variables) observer .send({ variables: variables, @@ -195,7 +191,6 @@ function usePageData({ session, }) .then(() => { - console.log('resolved query', id, variables) data_cache.set(id, observer) // if we are building up a stream (on the server), we want to add something @@ -255,7 +250,6 @@ function usePageData({ } - console.log('clearing ssr signal', artifactName) // trigger the signal window.__houdini__nav_caches__.ssr_signals.get(artifactName).resolve() window.__houdini__nav_caches__.ssr_signals.delete(artifactName) diff --git a/packages/houdini/src/codegen/generators/runtime/index.ts b/packages/houdini/src/codegen/generators/runtime/index.ts index fa146c312..32919888e 100644 --- a/packages/houdini/src/codegen/generators/runtime/index.ts +++ b/packages/houdini/src/codegen/generators/runtime/index.ts @@ -1,9 +1,10 @@ import { exportDefault, exportStarFrom, importDefaultFrom } from '../../../codegen/utils' import type { Config, Document } from '../../../lib' -import { siteURL as SITE_URL, fs, HoudiniError, path, houdini_mode } from '../../../lib' +import { siteURL as SITE_URL, fs, path } from '../../../lib' import generateGraphqlReturnTypes from './graphqlFunction' import injectPlugins from './injectPlugins' import { generatePluginIndex } from './pluginIndex' +import { generatePluginRuntimes } from './pluginRuntime' import { injectConfig } from './runtimeConfig' export default async function runtimeGenerator(config: Config, docs: Document[]) { @@ -40,89 +41,18 @@ ${exportStatement('config')} content ) => injectPlugins({ config, content, importStatement, exportStatement }), }), - transformPluginRuntimes({ config, docs }), + + generatePluginRuntimes({ + config, + docs, + }), generatePluginIndex({ config, exportStatement: exportStar }), ]) await generateGraphqlReturnTypes(config, docs) } -export async function generatePluginRuntimes({ config }: { config: Config }) { - await Promise.all( - config.plugins - .filter((plugin) => plugin.includeRuntime) - .map(async (plugin) => { - if (houdini_mode.is_testing || !plugin.includeRuntime) { - return - } - - // a plugin has told us to include a runtime then the path is relative to the plugin file - const runtime_path = path.join( - path.dirname(plugin.filepath), - typeof plugin.includeRuntime === 'string' - ? plugin.includeRuntime - : plugin.includeRuntime[config.module] - ) - - try { - await fs.stat(runtime_path) - } catch { - throw new HoudiniError({ - message: 'Cannot find runtime to generate for ' + plugin.name, - description: 'Maybe it was bundled?', - }) - } - - // copy the runtime - const pluginDir = config.pluginRuntimeDirectory(plugin.name) - - await fs.mkdirp(pluginDir) - await fs.recursiveCopy(runtime_path, pluginDir) - }) - ) -} - -async function transformPluginRuntimes({ config, docs }: { config: Config; docs: Document[] }) { - const { importStatement, exportDefaultStatement, exportStarStatement } = moduleStatments(config) - - await Promise.all( - config.plugins - .filter((plugin) => plugin.includeRuntime) - .map(async (plugin) => { - // the transform map holds a map of files to transform functions - let transformMap = plugin.transformRuntime ?? {} - if (transformMap && typeof transformMap === 'function') { - transformMap = transformMap(docs, { config }) - } - - // the keys of the transform map are the files we have to transform - for (const [target, transform] of Object.entries(transformMap)) { - // the path to the file we're transforming - const targetPath = path.join(config.pluginRuntimeDirectory(plugin.name), target) - - // read the file - const content = await fs.readFile(targetPath) - if (!content) { - return - } - - // transform the file - const transformed = transform({ - config, - content, - importStatement, - exportDefaultStatement: exportDefaultStatement, - exportStarStatement: exportStarStatement, - }) - - // write the file back out - await fs.writeFile(targetPath, transformed) - } - }) - ) -} - -function moduleStatments(config: Config) { +export function moduleStatments(config: Config) { const importStatement = config.module === 'commonjs' ? importDefaultFrom diff --git a/packages/houdini/src/codegen/generators/runtime/pluginRuntime.ts b/packages/houdini/src/codegen/generators/runtime/pluginRuntime.ts new file mode 100644 index 000000000..b702698ee --- /dev/null +++ b/packages/houdini/src/codegen/generators/runtime/pluginRuntime.ts @@ -0,0 +1,67 @@ +import { moduleStatments } from '.' +import type { Config, Document } from '../../../lib' +import { fs, HoudiniError, path, houdini_mode } from '../../../lib' + +export async function generatePluginRuntimes({ + config, + docs, +}: { + config: Config + docs: Document[] +}) { + if (houdini_mode.is_testing) { + return + } + + // generate the import statements + const { importStatement, exportDefaultStatement, exportStarStatement } = moduleStatments(config) + + // generate the runtime for each plugin + await Promise.all( + config.plugins + .filter((plugin) => plugin.includeRuntime) + .map(async (plugin) => { + // a plugin has told us to include a runtime then the path is relative to the plugin file + const runtime_path = config.pluginRuntimeSource(plugin) + if (!runtime_path) { + return + } + + // make sure the source file exists + try { + await fs.stat(runtime_path) + } catch { + throw new HoudiniError({ + message: 'Cannot find runtime to generate for ' + plugin.name, + description: 'Maybe it was bundled?', + }) + } + + // copy the runtime + const pluginDir = config.pluginRuntimeDirectory(plugin.name) + let transformMap = plugin.transformRuntime ?? {} + if (transformMap && typeof transformMap === 'function') { + transformMap = transformMap(docs, { config }) + } + + await fs.mkdirp(pluginDir) + await fs.recursiveCopy( + runtime_path, + pluginDir, + Object.fromEntries( + Object.entries(transformMap).map(([key, value]) => [ + path.join(runtime_path, key), + (content) => + value({ + config, + content, + importStatement, + exportDefaultStatement, + exportStarStatement, + }), + ]) + ) + ) + }) + ) +} diff --git a/packages/houdini/src/codegen/index.ts b/packages/houdini/src/codegen/index.ts index 2550f21ec..59d39f0b6 100755 --- a/packages/houdini/src/codegen/index.ts +++ b/packages/houdini/src/codegen/index.ts @@ -4,16 +4,11 @@ import type { Config, PluginHooks, Document, LogLevels } from '../lib' import { runPipeline as run, LogLevel, find_graphql, parseJS, HoudiniError, fs, path } from '../lib' import { ArtifactKind, type ArtifactKinds } from '../runtime/lib/types' import * as generators from './generators' -import { generatePluginRuntimes } from './generators/runtime' import * as transforms from './transforms' import * as validators from './validators' // the main entry point of the compile script export default async function compile(config: Config) { - // before we collect the documents, we need to generate the plugin runtimes - // so that they can include documents in the user's project - await generatePluginRuntimes({ config }) - // grab the graphql documents const documents = await collectDocuments(config) diff --git a/packages/houdini/src/lib/config.test.ts b/packages/houdini/src/lib/config.test.ts index 4846124c1..0f39b00eb 100644 --- a/packages/houdini/src/lib/config.test.ts +++ b/packages/houdini/src/lib/config.test.ts @@ -119,6 +119,9 @@ test('Config.include includes plugin runtimes', () => { ] // make sure we are including the plugin runtime - const includePath = path.relative(config.projectRoot, config.pluginDirectory('test-plugin')) + const includePath = path.relative( + config.projectRoot, + config.pluginRuntimeSource(config.plugins[0])! + ) expect(config.include.some((path) => path.includes(includePath))).toBeTruthy() }) diff --git a/packages/houdini/src/lib/config.ts b/packages/houdini/src/lib/config.ts index 4168f9cfa..9c6be390f 100644 --- a/packages/houdini/src/lib/config.ts +++ b/packages/houdini/src/lib/config.ts @@ -202,13 +202,15 @@ export class Config { // if any of the plugins specify included runtimes then their paths might have // documents for (const plugin of this.plugins) { + const runtimeDir = this.pluginRuntimeSource(plugin) + // skip plugins that dont' include runtimes - if (!plugin.includeRuntime) { + if (!runtimeDir) { continue } // the include path is relative to root of the vite project - const includePath = path.relative(this.projectRoot, this.pluginDirectory(plugin.name)) + const includePath = path.relative(this.projectRoot, runtimeDir) // add the plugin's directory to the include pile include.push(`${includePath}/**/*{${extensions.join(',')}}`) @@ -280,6 +282,19 @@ export class Config { return headers } + pluginRuntimeSource(plugin: PluginMeta) { + if (!plugin.includeRuntime) { + return null + } + + return path.join( + path.dirname(plugin.filepath), + typeof plugin.includeRuntime === 'string' + ? plugin.includeRuntime + : plugin.includeRuntime?.[this.module] + ) + } + async sourceFiles() { return [ ...new Set(