houdini-react 2.0.0-go.19 → 2.0.0-go.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -8
- package/postInstall.js +1 -1
- package/runtime/client.ts +1 -1
- package/runtime/hooks/useFragment.ts +3 -3
- package/runtime/hooks/useFragmentHandle.ts +2 -2
- package/runtime/hooks/useQuery.ts +4 -9
- package/runtime/hooks/useQueryHandle.ts +1 -1
- package/runtime/hydration.tsx +155 -0
- package/runtime/routing/Router.tsx +1 -6
- package/server/index.d.ts +1 -1
- package/server/index.js +1 -1
- package/vite/index.d.ts +2 -2
- package/vite/index.js +275 -2
- package/vite/transform.d.ts +11 -0
- package/vite/transform.js +92 -0
- /package/server/{react-streaming-compat.d.js → react-streaming.d.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "houdini-react",
|
|
3
|
-
"version": "2.0.0-go.
|
|
3
|
+
"version": "2.0.0-go.20",
|
|
4
4
|
"description": "The React plugin for houdini",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"graphql-yoga": "^4.0.4",
|
|
37
37
|
"react": "^19.0.0",
|
|
38
38
|
"react-dom": "^19.0.0",
|
|
39
|
-
"react-streaming
|
|
39
|
+
"react-streaming": "^0.4.17",
|
|
40
40
|
"recast": "^0.23.1",
|
|
41
41
|
"rollup": "^4.28.1",
|
|
42
42
|
"use-deep-compare-effect": "^1.8.1"
|
|
@@ -81,12 +81,12 @@
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"optionalDependencies": {
|
|
84
|
-
"houdini-react-darwin-x64": "2.0.0-go.
|
|
85
|
-
"houdini-react-darwin-arm64": "2.0.0-go.
|
|
86
|
-
"houdini-react-linux-x64": "2.0.0-go.
|
|
87
|
-
"houdini-react-linux-arm64": "2.0.0-go.
|
|
88
|
-
"houdini-react-win32-x64": "2.0.0-go.
|
|
89
|
-
"houdini-react-win32-arm64": "2.0.0-go.
|
|
84
|
+
"houdini-react-darwin-x64": "2.0.0-go.20",
|
|
85
|
+
"houdini-react-darwin-arm64": "2.0.0-go.20",
|
|
86
|
+
"houdini-react-linux-x64": "2.0.0-go.20",
|
|
87
|
+
"houdini-react-linux-arm64": "2.0.0-go.20",
|
|
88
|
+
"houdini-react-win32-x64": "2.0.0-go.20",
|
|
89
|
+
"houdini-react-win32-arm64": "2.0.0-go.20"
|
|
90
90
|
},
|
|
91
91
|
"bin": "bin/houdini-react",
|
|
92
92
|
"scripts": {
|
package/postInstall.js
CHANGED
|
@@ -5,7 +5,7 @@ const https = require('https')
|
|
|
5
5
|
const child_process = require('child_process')
|
|
6
6
|
|
|
7
7
|
// Adjust the version you want to install. You can also make this dynamic.
|
|
8
|
-
const BINARY_DISTRIBUTION_VERSION = '2.0.0-go.
|
|
8
|
+
const BINARY_DISTRIBUTION_VERSION = '2.0.0-go.20'
|
|
9
9
|
|
|
10
10
|
// Windows binaries end with .exe so we need to special case them.
|
|
11
11
|
const binaryName = process.platform === 'win32' ? 'houdini-react.exe' : 'houdini-react'
|
package/runtime/client.ts
CHANGED
|
@@ -12,9 +12,9 @@ export function useFragment<
|
|
|
12
12
|
_ReferenceType extends {},
|
|
13
13
|
_Input extends GraphQLVariables = GraphQLVariables
|
|
14
14
|
>(
|
|
15
|
-
reference: _Data | {
|
|
15
|
+
reference: _Data | { ' $fragments': _ReferenceType } | null,
|
|
16
16
|
document: { artifact: FragmentArtifact }
|
|
17
|
-
) {
|
|
17
|
+
): _Data | null {
|
|
18
18
|
const { cache } = useRouterContext()
|
|
19
19
|
|
|
20
20
|
// get the fragment reference info
|
|
@@ -82,7 +82,7 @@ export function useFragment<
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export function fragmentReference<_Data extends GraphQLObject, _Input, _ReferenceType extends {}>(
|
|
85
|
-
reference: _Data | {
|
|
85
|
+
reference: _Data | { ' $fragments': _ReferenceType } | null,
|
|
86
86
|
document: { artifact: FragmentArtifact }
|
|
87
87
|
): { variables: _Input; parent: string; loading: boolean } {
|
|
88
88
|
// @ts-expect-error: typescript can't guarantee that the fragment key is defined
|
|
@@ -19,9 +19,9 @@ export function useFragmentHandle<
|
|
|
19
19
|
_PaginationArtifact extends QueryArtifact,
|
|
20
20
|
_Input extends GraphQLVariables = GraphQLVariables
|
|
21
21
|
>(
|
|
22
|
-
reference: _Data | {
|
|
22
|
+
reference: _Data | { ' $fragments': _ReferenceType } | null,
|
|
23
23
|
document: { artifact: FragmentArtifact; refetchArtifact?: QueryArtifact }
|
|
24
|
-
):
|
|
24
|
+
): any {
|
|
25
25
|
// get the fragment values
|
|
26
26
|
const data = useFragment<_Data, _ReferenceType, _Input>(reference, document)
|
|
27
27
|
|
|
@@ -5,13 +5,8 @@ import { useQueryHandle } from './useQueryHandle'
|
|
|
5
5
|
|
|
6
6
|
export function useQuery<
|
|
7
7
|
_Artifact extends QueryArtifact,
|
|
8
|
-
_Data extends GraphQLObject = GraphQLObject
|
|
9
|
-
|
|
10
|
-
>(
|
|
11
|
-
|
|
12
|
-
variables: any = null,
|
|
13
|
-
config: UseQueryConfig = {}
|
|
14
|
-
): _Data {
|
|
15
|
-
const { data } = useQueryHandle<_Artifact, _Data, _Input>(document, variables, config)
|
|
16
|
-
return data
|
|
8
|
+
_Data extends GraphQLObject = GraphQLObject
|
|
9
|
+
>(document: { artifact: _Artifact }, variables: any = null, config: UseQueryConfig = {}): _Data {
|
|
10
|
+
const { data } = useQueryHandle<_Artifact, _Data>(document, variables, config)
|
|
11
|
+
return data as unknown as _Data
|
|
17
12
|
}
|
|
@@ -33,7 +33,7 @@ export function useQueryHandle<
|
|
|
33
33
|
{ artifact }: { artifact: QueryArtifact },
|
|
34
34
|
variables: any = null,
|
|
35
35
|
config: UseQueryConfig = {}
|
|
36
|
-
):
|
|
36
|
+
): any {
|
|
37
37
|
// figure out the identifier so we know what to look for
|
|
38
38
|
const identifier = queryIdentifier({ artifact, variables, config })
|
|
39
39
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { hydrateRoot, createRoot } from 'react-dom/client'
|
|
3
|
+
import type { QueryArtifact, GraphQLVariables } from '$houdini/runtime'
|
|
4
|
+
import type { Cache } from '$houdini/runtime/cache'
|
|
5
|
+
import type { HoudiniClient } from '$houdini/runtime/client'
|
|
6
|
+
import cacheRef from '$houdini/runtime/cache'
|
|
7
|
+
|
|
8
|
+
import { injectComponents } from './componentFields'
|
|
9
|
+
import { router_cache, type RouterCache } from './routing'
|
|
10
|
+
import clientFactory from './client'
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface Window {
|
|
14
|
+
__houdini__client__?: HoudiniClient
|
|
15
|
+
__houdini__pending_components__?: Record<string, any>
|
|
16
|
+
__houdini__cache__?: Cache
|
|
17
|
+
__houdini__hydration__layer__?: any
|
|
18
|
+
__houdini__initial__cache__?: any
|
|
19
|
+
__houdini__initial__session__?: any
|
|
20
|
+
__houdini__pending_artifacts__?: Record<string, QueryArtifact>
|
|
21
|
+
__houdini__pending_data__?: Record<string, any>
|
|
22
|
+
__houdini__pending_variables__?: Record<string, GraphQLVariables>
|
|
23
|
+
__houdini__nav_caches__?: RouterCache
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function hydrate_page(
|
|
28
|
+
App: React.ComponentType<any>,
|
|
29
|
+
Component: React.ComponentType<any>,
|
|
30
|
+
pageName: string,
|
|
31
|
+
pendingQueries: string[]
|
|
32
|
+
) {
|
|
33
|
+
// set up the client using its internally-managed singleton cache (cacheRef).
|
|
34
|
+
// the client's cachePolicy and queryPlugin are closure-bound to cacheRef at
|
|
35
|
+
// construction time, so we must use that same instance as window.__houdini__cache__
|
|
36
|
+
// rather than creating a second cache and trying to wire them together afterward.
|
|
37
|
+
window.__houdini__client__ ??= clientFactory()
|
|
38
|
+
window.__houdini__cache__ ??= cacheRef
|
|
39
|
+
|
|
40
|
+
// configure the singleton for React component field support
|
|
41
|
+
if (window.__houdini__pending_components__) {
|
|
42
|
+
window.__houdini__client__.componentCache = window.__houdini__pending_components__
|
|
43
|
+
}
|
|
44
|
+
window.__houdini__cache__._internal_unstable.componentCache =
|
|
45
|
+
window.__houdini__client__.componentCache
|
|
46
|
+
window.__houdini__cache__._internal_unstable.createComponent = (
|
|
47
|
+
fn: React.ComponentType<any>,
|
|
48
|
+
props: any
|
|
49
|
+
) => React.createElement(fn, props)
|
|
50
|
+
|
|
51
|
+
window.__houdini__hydration__layer__ ??=
|
|
52
|
+
window.__houdini__cache__._internal_unstable.storage.createLayer()
|
|
53
|
+
|
|
54
|
+
// rehydrate the cache from the server-serialized snapshot
|
|
55
|
+
window.__houdini__cache__?.hydrate(
|
|
56
|
+
window.__houdini__initial__cache__,
|
|
57
|
+
window.__houdini__hydration__layer__
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// prime the data/artifact caches from anything the server streamed
|
|
61
|
+
const initialData: Record<string, any> = {}
|
|
62
|
+
const initialArtifacts: Record<string, QueryArtifact> = {}
|
|
63
|
+
|
|
64
|
+
for (const [artifactName, artifact] of Object.entries(
|
|
65
|
+
window.__houdini__pending_artifacts__ ?? {}
|
|
66
|
+
)) {
|
|
67
|
+
initialArtifacts[artifactName] = artifact
|
|
68
|
+
|
|
69
|
+
if (window.__houdini__pending_data__?.[artifactName]) {
|
|
70
|
+
const variables = window.__houdini__pending_variables__![artifactName]
|
|
71
|
+
|
|
72
|
+
if ((artifact as any).hasComponents) {
|
|
73
|
+
injectComponents({
|
|
74
|
+
cache: window.__houdini__cache__!,
|
|
75
|
+
selection: (artifact as any).selection,
|
|
76
|
+
data: window.__houdini__pending_data__[artifactName],
|
|
77
|
+
variables,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const observer = window.__houdini__client__!.observe({
|
|
82
|
+
artifact,
|
|
83
|
+
cache: window.__houdini__cache__,
|
|
84
|
+
initialValue: window.__houdini__cache__!.read({
|
|
85
|
+
selection: (artifact as any).selection,
|
|
86
|
+
variables,
|
|
87
|
+
}).data,
|
|
88
|
+
initialVariables: variables,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
observer.send({
|
|
92
|
+
setup: true,
|
|
93
|
+
variables,
|
|
94
|
+
session: window.__houdini__initial__session__,
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
initialData[artifactName] = observer
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!window.__houdini__nav_caches__) {
|
|
102
|
+
window.__houdini__nav_caches__ = router_cache({
|
|
103
|
+
pending_queries: pendingQueries,
|
|
104
|
+
initialData,
|
|
105
|
+
initialVariables: window.__houdini__pending_variables__,
|
|
106
|
+
initialArtifacts,
|
|
107
|
+
components: { [pageName]: Component },
|
|
108
|
+
})
|
|
109
|
+
_flush_pending_artifacts()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
hydrateRoot(
|
|
113
|
+
document,
|
|
114
|
+
<App
|
|
115
|
+
initialURL={window.location.pathname}
|
|
116
|
+
cache={window.__houdini__cache__}
|
|
117
|
+
session={window.__houdini__initial__session__}
|
|
118
|
+
{...window.__houdini__nav_caches__}
|
|
119
|
+
/>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const _pendingArtifacts: Array<[string, QueryArtifact]> = []
|
|
124
|
+
|
|
125
|
+
export function register_artifact(name: string, artifact: QueryArtifact) {
|
|
126
|
+
const caches = window.__houdini__nav_caches__
|
|
127
|
+
if (!caches?.artifact_cache) {
|
|
128
|
+
_pendingArtifacts.push([name, artifact])
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
if (!caches.artifact_cache.has(name)) {
|
|
132
|
+
caches.artifact_cache.set(name, artifact)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function _flush_pending_artifacts() {
|
|
137
|
+
while (_pendingArtifacts.length > 0) {
|
|
138
|
+
const [name, artifact] = _pendingArtifacts.shift()!
|
|
139
|
+
register_artifact(name, artifact)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function mount_static_app(App: React.ComponentType<any>, manifest: any) {
|
|
144
|
+
const root = createRoot(document.getElementById('app')!)
|
|
145
|
+
|
|
146
|
+
root.render(
|
|
147
|
+
React.createElement(App, {
|
|
148
|
+
initialURL: window.location.pathname,
|
|
149
|
+
cache: cacheRef,
|
|
150
|
+
session: null,
|
|
151
|
+
manifest,
|
|
152
|
+
...router_cache(),
|
|
153
|
+
})
|
|
154
|
+
)
|
|
155
|
+
}
|
|
@@ -246,7 +246,7 @@ function usePageData({
|
|
|
246
246
|
injectToStream?.(`
|
|
247
247
|
<script>
|
|
248
248
|
{
|
|
249
|
-
window.__houdini__cache__?.hydrate(${cache.serialize()}, window.
|
|
249
|
+
window.__houdini__cache__?.hydrate(${cache.serialize()}, window.__houdini__hydration__layer__)
|
|
250
250
|
|
|
251
251
|
const artifactName = "${artifact.name}"
|
|
252
252
|
const value = ${JSON.stringify(
|
|
@@ -403,11 +403,6 @@ function usePageData({
|
|
|
403
403
|
// save the artifact in the cache
|
|
404
404
|
artifact_cache.set(artifact_id, artifact)
|
|
405
405
|
|
|
406
|
-
// add a script to load the artifact
|
|
407
|
-
injectToStream?.(`
|
|
408
|
-
<script type="module" src="${assetPrefix}/artifacts/${artifact.name}.js" async=""></script>
|
|
409
|
-
`)
|
|
410
|
-
|
|
411
406
|
// now that we have the artifact, we can load the query too
|
|
412
407
|
load_query({ id: artifact.name, artifact, variables })
|
|
413
408
|
})
|
package/server/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { renderToStream } from 'react-streaming
|
|
1
|
+
export { renderToStream } from 'react-streaming/server';
|
package/server/index.js
CHANGED
package/vite/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { VitePluginContext } from 'houdini/vite';
|
|
2
|
+
import { PluginOption } from 'vite';
|
|
3
3
|
export default function (ctx: VitePluginContext): PluginOption;
|
package/vite/index.js
CHANGED
|
@@ -1,11 +1,284 @@
|
|
|
1
|
+
import { fs, path } from "houdini";
|
|
2
|
+
import {
|
|
3
|
+
app_component_path,
|
|
4
|
+
adapter_config_path,
|
|
5
|
+
plugin_dir,
|
|
6
|
+
client_build_directory
|
|
7
|
+
} from "houdini/router/conventions";
|
|
8
|
+
import { load_manifest } from "houdini/router/manifest";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { build } from "vite";
|
|
11
|
+
import { transform_file } from "./transform.js";
|
|
12
|
+
const _require = createRequire(import.meta.url);
|
|
13
|
+
let reactStreamingServerPath = "";
|
|
14
|
+
try {
|
|
15
|
+
const main = _require.resolve("react-streaming");
|
|
16
|
+
const pkgDir = main.replace(/\/dist\/.*$/, "");
|
|
17
|
+
reactStreamingServerPath = path.join(pkgDir, "dist/server/index.node-and-web.js");
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
1
20
|
function vite_default(ctx) {
|
|
21
|
+
let manifest;
|
|
22
|
+
let viteEnv;
|
|
23
|
+
let devServer = false;
|
|
24
|
+
let isSSRBuild = false;
|
|
25
|
+
let cfCache = null;
|
|
2
26
|
return {
|
|
3
27
|
name: "houdini-react",
|
|
4
|
-
|
|
5
|
-
|
|
28
|
+
configResolved(config) {
|
|
29
|
+
isSSRBuild = !!config.build.ssr;
|
|
30
|
+
},
|
|
31
|
+
async config(userConfig, env) {
|
|
32
|
+
viteEnv = env;
|
|
33
|
+
if (userConfig.build?.ssr) {
|
|
34
|
+
return reactStreamingServerPath ? { resolve: { alias: { "react-streaming/server": reactStreamingServerPath } } } : {};
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
manifest = await load_manifest({ config: ctx.config });
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.log(
|
|
40
|
+
"something went wrong. please try again. \n error: " + e.message
|
|
41
|
+
);
|
|
42
|
+
manifest = {
|
|
43
|
+
pages: {},
|
|
44
|
+
layouts: {},
|
|
45
|
+
page_queries: {},
|
|
46
|
+
layout_queries: {},
|
|
47
|
+
artifacts: [],
|
|
48
|
+
local_schema: false,
|
|
49
|
+
local_yoga: false,
|
|
50
|
+
component_fields: {}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
let conf = {
|
|
54
|
+
build: { rollupOptions: {} }
|
|
55
|
+
};
|
|
56
|
+
if (env.command === "build") {
|
|
57
|
+
conf.base = "/assets";
|
|
58
|
+
}
|
|
59
|
+
const compiledAssetsDir = client_build_directory(ctx.config);
|
|
60
|
+
await fs.mkdirp(compiledAssetsDir);
|
|
61
|
+
conf.build = {
|
|
62
|
+
outDir: compiledAssetsDir,
|
|
63
|
+
rollupOptions: {
|
|
64
|
+
output: {
|
|
65
|
+
assetFileNames: "assets/[name].js",
|
|
66
|
+
entryFileNames: "[name].js"
|
|
67
|
+
},
|
|
68
|
+
input: {
|
|
69
|
+
"entries/app": app_component_path(ctx.config),
|
|
70
|
+
...ctx.adapter ? { "entries/adapter": adapter_config_path(ctx.config) } : {}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
if (env.command === "build" && ctx.adapter && ctx.adapter.includePaths) {
|
|
75
|
+
const extra = typeof ctx.adapter.includePaths === "function" ? ctx.adapter.includePaths({ config: ctx.config }) : ctx.adapter.includePaths;
|
|
76
|
+
Object.assign(conf.build.rollupOptions.input, extra);
|
|
77
|
+
}
|
|
78
|
+
for (const [id, page] of Object.entries(manifest.pages)) {
|
|
79
|
+
;
|
|
80
|
+
conf.build.rollupOptions.input[`pages/${id}`] = `virtual:houdini/pages/${page.id}.jsx`;
|
|
81
|
+
}
|
|
82
|
+
return reactStreamingServerPath ? {
|
|
83
|
+
...conf,
|
|
84
|
+
resolve: { alias: { "react-streaming/server": reactStreamingServerPath } }
|
|
85
|
+
} : conf;
|
|
86
|
+
},
|
|
87
|
+
resolveId(id) {
|
|
88
|
+
if (!id.includes("virtual:houdini")) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
return id.substring(id.indexOf("virtual:houdini"));
|
|
92
|
+
},
|
|
93
|
+
async transform(code, filepath) {
|
|
94
|
+
filepath = path.posixify(filepath);
|
|
95
|
+
if (filepath.startsWith("/src/")) {
|
|
96
|
+
filepath = path.join(process.cwd(), filepath);
|
|
97
|
+
}
|
|
98
|
+
if (!ctx.config.includeFile(filepath)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (cfCache === null) {
|
|
102
|
+
try {
|
|
103
|
+
cfCache = ctx.db.prepare("SELECT type, field, fragment FROM component_fields").all();
|
|
104
|
+
} catch {
|
|
105
|
+
cfCache = [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return transform_file(
|
|
109
|
+
{
|
|
110
|
+
config: ctx.config,
|
|
111
|
+
content: code,
|
|
112
|
+
filepath: path.posixify(filepath),
|
|
113
|
+
watch_file: this.addWatchFile.bind(this)
|
|
114
|
+
},
|
|
115
|
+
cfCache
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
async closeBundle() {
|
|
119
|
+
if (viteEnv.mode !== "production" || devServer || isSSRBuild) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!ctx.adapter || ctx.adapter?.disableServer) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const compiledAssetsDir = client_build_directory(ctx.config);
|
|
126
|
+
await build({
|
|
127
|
+
build: {
|
|
128
|
+
ssr: true,
|
|
129
|
+
outDir: path.join(compiledAssetsDir, "ssr"),
|
|
130
|
+
rollupOptions: {
|
|
131
|
+
output: {
|
|
132
|
+
assetFileNames: "assets/[name].js",
|
|
133
|
+
entryFileNames: "[name].js"
|
|
134
|
+
},
|
|
135
|
+
input: {
|
|
136
|
+
"entries/adapter": adapter_config_path(ctx.config)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
async load(id) {
|
|
143
|
+
if (!id.startsWith("virtual:houdini")) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!manifest) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let [, which, arg] = id.split("/");
|
|
150
|
+
const parsedPath = arg ? path.parse(arg) : "";
|
|
151
|
+
const pageName = parsedPath ? parsedPath.name : "";
|
|
152
|
+
if (which === "pages") {
|
|
153
|
+
const page = manifest.pages[pageName];
|
|
154
|
+
if (!page) {
|
|
155
|
+
throw new Error("unknown page" + pageName);
|
|
156
|
+
}
|
|
157
|
+
const pendingQueries = page.queries.filter((query) => {
|
|
158
|
+
const pg = Object.values(manifest.page_queries).find((q) => q.name === query);
|
|
159
|
+
if (pg) {
|
|
160
|
+
return pg.loading;
|
|
161
|
+
}
|
|
162
|
+
const layout = Object.values(manifest.layout_queries).find(
|
|
163
|
+
(q) => q.name === query
|
|
164
|
+
);
|
|
165
|
+
return layout?.loading;
|
|
166
|
+
});
|
|
167
|
+
return `
|
|
168
|
+
import App from '$houdini/plugins/houdini-react/units/render/App'
|
|
169
|
+
import Component from '$houdini/plugins/houdini-react/units/entries/${pageName}.jsx'
|
|
170
|
+
import { hydrate_page } from '$houdini/plugins/houdini-react/runtime/hydration'
|
|
171
|
+
hydrate_page(App, Component, '${pageName}', ${JSON.stringify(pendingQueries)})
|
|
172
|
+
`;
|
|
173
|
+
}
|
|
174
|
+
if (which === "artifacts") {
|
|
175
|
+
return `
|
|
176
|
+
import artifact from '$houdini/artifacts/${pageName}'
|
|
177
|
+
import { register_artifact } from '$houdini/plugins/houdini-react/runtime/hydration'
|
|
178
|
+
register_artifact('${pageName}', artifact)
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
if (which === "static-entry") {
|
|
182
|
+
return `
|
|
183
|
+
import App from '$houdini/plugins/houdini-react/units/render/App'
|
|
184
|
+
import manifest from '$houdini/plugins/houdini-react/runtime/manifest'
|
|
185
|
+
import { mount_static_app } from '$houdini/plugins/houdini-react/runtime/hydration'
|
|
186
|
+
mount_static_app(App, manifest)
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
async configureServer(server) {
|
|
191
|
+
devServer = true;
|
|
192
|
+
server.middlewares.use(async (req, res, next) => {
|
|
193
|
+
if (!req.url) {
|
|
194
|
+
next();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const { default: router_manifest } = await server.ssrLoadModule(
|
|
198
|
+
path.join(plugin_dir(ctx.config, "houdini-react"), "runtime", "manifest.ts")
|
|
199
|
+
);
|
|
200
|
+
const { createServerAdapter } = await server.ssrLoadModule(
|
|
201
|
+
adapter_config_path(ctx.config)
|
|
202
|
+
);
|
|
203
|
+
const requestHeaders = new Headers();
|
|
204
|
+
for (const header of Object.entries(req.headers ?? {})) {
|
|
205
|
+
requestHeaders.set(header[0], header[1]);
|
|
206
|
+
}
|
|
207
|
+
const port = server.config.server.port ?? 5173;
|
|
208
|
+
const request = new Request(
|
|
209
|
+
`http://localhost:${port}` + req.url,
|
|
210
|
+
req.method === "POST" ? {
|
|
211
|
+
method: req.method,
|
|
212
|
+
headers: requestHeaders,
|
|
213
|
+
body: await getBody(req)
|
|
214
|
+
} : void 0
|
|
215
|
+
);
|
|
216
|
+
let documentPremable = `<script type="module" src="/@vite/client" async=""><\/script>`;
|
|
217
|
+
try {
|
|
218
|
+
const transformed = await server.transformIndexHtml(
|
|
219
|
+
req.url,
|
|
220
|
+
"<!DOCTYPE html><html><head></head><body></body></html>"
|
|
221
|
+
);
|
|
222
|
+
const headMatch = transformed.match(/<head>([\s\S]*?)<\/head>/);
|
|
223
|
+
if (headMatch?.[1]?.trim()) {
|
|
224
|
+
documentPremable = headMatch[1].trim();
|
|
225
|
+
}
|
|
226
|
+
} catch {
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const result = await createServerAdapter({
|
|
230
|
+
production: false,
|
|
231
|
+
manifest: router_manifest,
|
|
232
|
+
assetPrefix: "/virtual:houdini",
|
|
233
|
+
pipe: res,
|
|
234
|
+
documentPremable
|
|
235
|
+
})(request);
|
|
236
|
+
if (result && result.status === 404) {
|
|
237
|
+
return next();
|
|
238
|
+
}
|
|
239
|
+
if (result && typeof result !== "boolean") {
|
|
240
|
+
if (res.closed) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
for (const header of result.headers ?? []) {
|
|
244
|
+
res.setHeader(header[0], header[1]);
|
|
245
|
+
}
|
|
246
|
+
if (result.status >= 300 && result.status < 400) {
|
|
247
|
+
res.writeHead(result.status, {
|
|
248
|
+
Location: result.headers.get("Location") ?? "",
|
|
249
|
+
...[...result.headers].reduce(
|
|
250
|
+
(headers, [key, value]) => ({
|
|
251
|
+
...headers,
|
|
252
|
+
[key]: value
|
|
253
|
+
}),
|
|
254
|
+
{}
|
|
255
|
+
)
|
|
256
|
+
});
|
|
257
|
+
} else {
|
|
258
|
+
res.write(await result.text());
|
|
259
|
+
}
|
|
260
|
+
res.end();
|
|
261
|
+
}
|
|
262
|
+
} catch (e) {
|
|
263
|
+
console.error(e);
|
|
264
|
+
res.end();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
6
267
|
}
|
|
7
268
|
};
|
|
8
269
|
}
|
|
270
|
+
function getBody(request) {
|
|
271
|
+
return new Promise((resolve) => {
|
|
272
|
+
const bodyParts = [];
|
|
273
|
+
let body;
|
|
274
|
+
request.on("data", (chunk) => {
|
|
275
|
+
bodyParts.push(chunk);
|
|
276
|
+
}).on("end", () => {
|
|
277
|
+
body = Buffer.concat(bodyParts).toString();
|
|
278
|
+
resolve(body);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
9
282
|
export {
|
|
10
283
|
vite_default as default
|
|
11
284
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TransformPage } from 'houdini';
|
|
2
|
+
import type { SourceMapInput } from 'rollup';
|
|
3
|
+
export type ComponentFieldRow = {
|
|
4
|
+
type: string;
|
|
5
|
+
field: string;
|
|
6
|
+
fragment: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function transform_file(page: TransformPage, cfRows: ComponentFieldRow[]): Promise<{
|
|
9
|
+
code: string;
|
|
10
|
+
map?: SourceMapInput;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as graphql from "graphql";
|
|
2
|
+
import {
|
|
3
|
+
ArtifactKind,
|
|
4
|
+
artifact_import,
|
|
5
|
+
ensure_imports,
|
|
6
|
+
find_graphql,
|
|
7
|
+
parseJS,
|
|
8
|
+
path,
|
|
9
|
+
printJS
|
|
10
|
+
} from "houdini";
|
|
11
|
+
import { componentField_unit_path, houdini_root } from "houdini/router/conventions";
|
|
12
|
+
import * as recast from "recast";
|
|
13
|
+
const AST = recast.types.builders;
|
|
14
|
+
async function transform_file(page, cfRows) {
|
|
15
|
+
const isJSX = page.filepath.endsWith(".tsx") || page.filepath.endsWith(".jsx");
|
|
16
|
+
if (!isJSX && !page.filepath.endsWith(".ts") && !page.filepath.endsWith(".js")) {
|
|
17
|
+
return { code: page.content, map: page.map };
|
|
18
|
+
}
|
|
19
|
+
const script = parseJS(page.content, isJSX ? { plugins: ["jsx"] } : {});
|
|
20
|
+
const cfMap = {};
|
|
21
|
+
for (const row of cfRows) {
|
|
22
|
+
if (row.type && row.field && row.fragment) {
|
|
23
|
+
cfMap[row.type] ??= {};
|
|
24
|
+
cfMap[row.type][row.field] = row.fragment;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
await find_graphql(page.config, script, {
|
|
28
|
+
skipGraphqlType: true,
|
|
29
|
+
tag({ node, artifact, parsedDocument }) {
|
|
30
|
+
const { id: artifactRef } = artifact_import({ page, script, artifact });
|
|
31
|
+
const properties = [AST.objectProperty(AST.stringLiteral("artifact"), artifactRef)];
|
|
32
|
+
if (is_paginated(parsedDocument)) {
|
|
33
|
+
if (artifact.kind !== ArtifactKind.Query) {
|
|
34
|
+
const refetchName = artifact.name + "_Pagination_Query";
|
|
35
|
+
const { id: refetchRef } = artifact_import({
|
|
36
|
+
page,
|
|
37
|
+
script,
|
|
38
|
+
artifact: { name: refetchName }
|
|
39
|
+
});
|
|
40
|
+
properties.push(
|
|
41
|
+
AST.objectProperty(AST.stringLiteral("refetchArtifact"), refetchRef)
|
|
42
|
+
);
|
|
43
|
+
} else {
|
|
44
|
+
properties.push(
|
|
45
|
+
AST.objectProperty(AST.stringLiteral("refetchArtifact"), artifactRef)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (Object.keys(cfMap).length > 0) {
|
|
50
|
+
const typeInfo = new graphql.TypeInfo(page.config.schema);
|
|
51
|
+
graphql.visit(
|
|
52
|
+
parsedDocument,
|
|
53
|
+
graphql.visitWithTypeInfo(typeInfo, {
|
|
54
|
+
Field(fieldNode) {
|
|
55
|
+
const parentType = typeInfo.getParentType();
|
|
56
|
+
const typeName = parentType?.name;
|
|
57
|
+
if (!typeName)
|
|
58
|
+
return;
|
|
59
|
+
const fragmentName = cfMap[typeName]?.[fieldNode.name.value];
|
|
60
|
+
if (!fragmentName)
|
|
61
|
+
return;
|
|
62
|
+
const entryPointPath = componentField_unit_path(
|
|
63
|
+
page.config,
|
|
64
|
+
fragmentName
|
|
65
|
+
);
|
|
66
|
+
ensure_imports({
|
|
67
|
+
script,
|
|
68
|
+
sourceModule: "$houdini/" + path.relative(houdini_root(page.config), entryPointPath)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
node.replaceWith(AST.objectExpression(properties));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return printJS(script);
|
|
78
|
+
}
|
|
79
|
+
function is_paginated(doc) {
|
|
80
|
+
let paginated = false;
|
|
81
|
+
graphql.visit(doc, {
|
|
82
|
+
Directive(node) {
|
|
83
|
+
if (node.name.value === "paginate") {
|
|
84
|
+
paginated = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return paginated;
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
transform_file
|
|
92
|
+
};
|
|
File without changes
|