houdini-svelte 0.17.0-next.0
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/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/package.json +57 -0
- package/src/plugin/codegen/adapter.ts +45 -0
- package/src/plugin/codegen/components/index.ts +149 -0
- package/src/plugin/codegen/index.ts +28 -0
- package/src/plugin/codegen/routes/index.ts +307 -0
- package/src/plugin/codegen/routes/kit.test.ts +276 -0
- package/src/plugin/codegen/stores/fragment.ts +83 -0
- package/src/plugin/codegen/stores/index.ts +55 -0
- package/src/plugin/codegen/stores/mutation.ts +56 -0
- package/src/plugin/codegen/stores/query.test.ts +504 -0
- package/src/plugin/codegen/stores/query.ts +97 -0
- package/src/plugin/codegen/stores/subscription.ts +57 -0
- package/src/plugin/extract.test.ts +290 -0
- package/src/plugin/extract.ts +127 -0
- package/src/plugin/extractLoadFunction.test.ts +247 -0
- package/src/plugin/extractLoadFunction.ts +249 -0
- package/src/plugin/fsPatch.ts +238 -0
- package/src/plugin/imports.ts +28 -0
- package/src/plugin/index.ts +165 -0
- package/src/plugin/kit.ts +382 -0
- package/src/plugin/transforms/index.ts +90 -0
- package/src/plugin/transforms/kit/index.ts +20 -0
- package/src/plugin/transforms/kit/init.test.ts +28 -0
- package/src/plugin/transforms/kit/init.ts +75 -0
- package/src/plugin/transforms/kit/load.test.ts +1234 -0
- package/src/plugin/transforms/kit/load.ts +506 -0
- package/src/plugin/transforms/kit/session.test.ts +268 -0
- package/src/plugin/transforms/kit/session.ts +161 -0
- package/src/plugin/transforms/query.test.ts +99 -0
- package/src/plugin/transforms/query.ts +263 -0
- package/src/plugin/transforms/reactive.ts +126 -0
- package/src/plugin/transforms/tags.ts +20 -0
- package/src/plugin/transforms/types.ts +9 -0
- package/src/plugin/validate.test.ts +95 -0
- package/src/plugin/validate.ts +50 -0
- package/src/preprocess/index.ts +33 -0
- package/src/runtime/adapter.ts +21 -0
- package/src/runtime/fragments.ts +86 -0
- package/src/runtime/index.ts +72 -0
- package/src/runtime/network.ts +6 -0
- package/src/runtime/session.ts +187 -0
- package/src/runtime/stores/fragment.ts +48 -0
- package/src/runtime/stores/index.ts +5 -0
- package/src/runtime/stores/mutation.ts +185 -0
- package/src/runtime/stores/pagination/cursor.ts +265 -0
- package/src/runtime/stores/pagination/fetch.ts +7 -0
- package/src/runtime/stores/pagination/fragment.ts +236 -0
- package/src/runtime/stores/pagination/index.ts +7 -0
- package/src/runtime/stores/pagination/offset.ts +162 -0
- package/src/runtime/stores/pagination/pageInfo.test.ts +39 -0
- package/src/runtime/stores/pagination/pageInfo.ts +67 -0
- package/src/runtime/stores/pagination/query.ts +132 -0
- package/src/runtime/stores/query.ts +524 -0
- package/src/runtime/stores/store.ts +13 -0
- package/src/runtime/stores/subscription.ts +107 -0
- package/src/runtime/types.ts +40 -0
- package/src/test/index.ts +208 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# houdini-svelte
|
|
2
|
+
|
|
3
|
+
## 0.17.0-next.0
|
|
4
|
+
|
|
5
|
+
### ⚠️ Breaking Changes
|
|
6
|
+
|
|
7
|
+
- [#593](https://github.com/HoudiniGraphql/houdini/pull/593) [`c1363fe`](https://github.com/HoudiniGraphql/houdini/commit/c1363fe938ab94281272cad8939b892fd705a803) Thanks [@AlecAivazis](https://github.com/AlecAivazis)! - Split houdini into two packages: `houdini` and `houdini-svelte`
|
|
8
|
+
|
|
9
|
+
### ✨ Features
|
|
10
|
+
|
|
11
|
+
- [#593](https://github.com/HoudiniGraphql/houdini/pull/593) [`c1363fe`](https://github.com/HoudiniGraphql/houdini/commit/c1363fe938ab94281272cad8939b892fd705a803) Thanks [@AlecAivazis](https://github.com/AlecAivazis)! - Adding layout.gql special file
|
|
12
|
+
|
|
13
|
+
- [#610](https://github.com/HoudiniGraphql/houdini/pull/610) [`3168f7d`](https://github.com/HoudiniGraphql/houdini/commit/3168f7dffd06f5074d08652d2d2c459377bc73d6) Thanks [@AlecAivazis](https://github.com/AlecAivazis)! - Generate variable function definitions for non-route queries
|
|
14
|
+
|
|
15
|
+
### 🐛 Fixes
|
|
16
|
+
|
|
17
|
+
- [#613](https://github.com/HoudiniGraphql/houdini/pull/613) [`eb3ffe1`](https://github.com/HoudiniGraphql/houdini/commit/eb3ffe1fbf14180210464863cb7e2ca29892a1fe) Thanks [@AlecAivazis](https://github.com/AlecAivazis)! - Avoid unnecessary data prop being added to route
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Alec Aivazis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "houdini-svelte",
|
|
3
|
+
"version": "0.17.0-next.0",
|
|
4
|
+
"description": "The svelte plugin for houdini",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@kitql/helper": "^0.5.0",
|
|
8
|
+
"@sveltejs/kit": "1.0.0-next.505",
|
|
9
|
+
"ast-types": "^0.15.1",
|
|
10
|
+
"estree-walker": "^3.0.1",
|
|
11
|
+
"graphql": "^16.6.0",
|
|
12
|
+
"houdini": "^0.17.0-next.0",
|
|
13
|
+
"scripts": "^1.0.0",
|
|
14
|
+
"minimatch": "^5.1.0",
|
|
15
|
+
"recast": "^0.21.5",
|
|
16
|
+
"svelte": "^3.52.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/minimatch": "^5.1.2",
|
|
20
|
+
"vitest": "^0.23.4"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
"./package.json": "./package.json",
|
|
27
|
+
".": {
|
|
28
|
+
"import": "./build/plugin-esm/index.js",
|
|
29
|
+
"require": "./build/plugin-cjs/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./preprocess": {
|
|
32
|
+
"import": "./build/preprocess-esm/index.js",
|
|
33
|
+
"require": "./build/preprocess-cjs/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./test": {
|
|
36
|
+
"import": "./build/test-esm/index.js",
|
|
37
|
+
"require": "./build/test-cjs/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"typesVersions": {
|
|
41
|
+
"*": {
|
|
42
|
+
"preprocess": [
|
|
43
|
+
"build/preprocess/index.d.ts"
|
|
44
|
+
],
|
|
45
|
+
"test": [
|
|
46
|
+
"build/test/index.d.ts"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"main": "./build/plugin-cjs/index.js",
|
|
51
|
+
"types": "./build/plugin/index.d.ts",
|
|
52
|
+
"scripts": {
|
|
53
|
+
"tests": "vitest",
|
|
54
|
+
"compile": "scripts build --plugin",
|
|
55
|
+
"typedefs": "scripts typedefs --plugin"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { fs, path } from 'houdini'
|
|
2
|
+
|
|
3
|
+
import { PluginGenerateInput } from '.'
|
|
4
|
+
|
|
5
|
+
export default async function generateAdapter({ config, framework }: PluginGenerateInput) {
|
|
6
|
+
// we only need to generate an adapter for kit (the default one is fine for vanilla svelte)
|
|
7
|
+
if (framework !== 'kit') {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// the location of the adapter
|
|
12
|
+
const adapterLocation = path.join(config.pluginRuntimeDirectory('houdini-svelte'), 'adapter.js')
|
|
13
|
+
|
|
14
|
+
// figure out which adapter we need to lay down
|
|
15
|
+
const adapter = {
|
|
16
|
+
kit: sveltekitAdapter,
|
|
17
|
+
}[framework]
|
|
18
|
+
|
|
19
|
+
// write the index file that exports the runtime
|
|
20
|
+
await fs.mkdirp(path.dirname(adapterLocation))
|
|
21
|
+
await fs.writeFile(adapterLocation, adapter)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const sveltekitAdapter = `import { goto as go } from '$app/navigation'
|
|
25
|
+
import { get } from 'svelte/store';
|
|
26
|
+
import { browser, prerendering } from '$app/environment'
|
|
27
|
+
import { page } from '$app/stores'
|
|
28
|
+
import { error as svelteKitError } from '@sveltejs/kit'
|
|
29
|
+
|
|
30
|
+
export function goTo(location, options) {
|
|
31
|
+
go(location, options)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const isBrowser = browser
|
|
35
|
+
|
|
36
|
+
export let clientStarted = false;
|
|
37
|
+
|
|
38
|
+
export function setClientStarted() {
|
|
39
|
+
clientStarted = true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const isPrerender = prerendering
|
|
43
|
+
|
|
44
|
+
export const error = svelteKitError
|
|
45
|
+
`
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ArtifactKind, Config, fs, GenerateHookInput, path } from 'houdini'
|
|
2
|
+
|
|
3
|
+
import { Framework } from '../../kit'
|
|
4
|
+
|
|
5
|
+
export default async function componentTypesGenerator(
|
|
6
|
+
framework: Framework,
|
|
7
|
+
{ config, documents }: GenerateHookInput
|
|
8
|
+
) {
|
|
9
|
+
// if we treat the documents as the source of truth for files that match
|
|
10
|
+
// we can just filter out the ones that don't apply:t
|
|
11
|
+
// - in kit, exclude the route directory
|
|
12
|
+
// - group the files by directory
|
|
13
|
+
// - generate ./$houdini in the typeroot directory at the correct spot
|
|
14
|
+
|
|
15
|
+
// there could be many queries in a given component so we can't just think about filepaths
|
|
16
|
+
const queries: Record<string, { name: string; query: string }[]> = {}
|
|
17
|
+
for (const document of documents) {
|
|
18
|
+
if (document.kind !== ArtifactKind.Query) {
|
|
19
|
+
continue
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
queries[document.filename] = (queries[document.filename] ?? []).concat({
|
|
23
|
+
name: document.name,
|
|
24
|
+
query: document.originalString,
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
let matches = Object.keys(queries).filter((filepath) => filepath.endsWith('.svelte'))
|
|
28
|
+
|
|
29
|
+
// if we are in kit, don't consider the source directory
|
|
30
|
+
if (framework === 'kit') {
|
|
31
|
+
matches = matches.filter((match) => !match.startsWith(config.routesDir))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// group the files by directory
|
|
35
|
+
const files: ProjectDirs = {
|
|
36
|
+
dirs: {},
|
|
37
|
+
files: [],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// put every file we found in the right place
|
|
41
|
+
for (let file of matches) {
|
|
42
|
+
// only worry about things relative to the project root
|
|
43
|
+
file = path.relative(config.projectRoot, file)
|
|
44
|
+
|
|
45
|
+
// walk down the path
|
|
46
|
+
let target = files
|
|
47
|
+
const parts = file.split('/')
|
|
48
|
+
for (const [i, part] of parts.entries()) {
|
|
49
|
+
// if we are at the end of the path, we are looking at a file
|
|
50
|
+
if (i === parts.length - 1) {
|
|
51
|
+
target.files.push(part)
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// we are on a file
|
|
56
|
+
if (!target.dirs[part]) {
|
|
57
|
+
target.dirs[part] = {
|
|
58
|
+
dirs: {},
|
|
59
|
+
files: [],
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// there is guaranteed to be an entry for this particular filepath part
|
|
64
|
+
// focus on it and move onto the next one
|
|
65
|
+
target = target.dirs[part]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// now that we've grouped together all of the files together, we can just walk down the
|
|
70
|
+
// structure and generate the necessary types at the right place.
|
|
71
|
+
await walk_project(config, files, queries, config.projectRoot)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function walk_project(
|
|
75
|
+
config: Config,
|
|
76
|
+
dirs: ProjectDirs,
|
|
77
|
+
queries: Record<string, { name: string; query: string }[]>,
|
|
78
|
+
root: string
|
|
79
|
+
) {
|
|
80
|
+
// process every child directory
|
|
81
|
+
await Promise.all(
|
|
82
|
+
Object.entries(dirs.dirs).map(async ([path_part, child]) => {
|
|
83
|
+
// keep going with the new root
|
|
84
|
+
return walk_project(config, child, queries, path.join(root, path_part))
|
|
85
|
+
})
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
// if we don't have any files at this spot we're done
|
|
89
|
+
if (dirs.files.length === 0) {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// every query in this directory needs an entry in the file
|
|
94
|
+
let typeFile = "import type { ComponentProps } from 'svelte'"
|
|
95
|
+
for (const file of dirs.files) {
|
|
96
|
+
const no_ext = path.parse(file).name
|
|
97
|
+
const prop_type = no_ext + 'Props'
|
|
98
|
+
|
|
99
|
+
// figure out the full file path
|
|
100
|
+
const filepath = path.join(root, file)
|
|
101
|
+
|
|
102
|
+
// we need to figure out the props for this component
|
|
103
|
+
const contents = await fs.readFile(filepath)
|
|
104
|
+
// make typescript happy
|
|
105
|
+
if (!contents) {
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// define the prop types for the component
|
|
110
|
+
typeFile =
|
|
111
|
+
`
|
|
112
|
+
import ${no_ext} from './${file}'
|
|
113
|
+
` +
|
|
114
|
+
typeFile +
|
|
115
|
+
`
|
|
116
|
+
type ${prop_type} = ComponentProps<${no_ext}>
|
|
117
|
+
`
|
|
118
|
+
|
|
119
|
+
// a file can contain multiple queries
|
|
120
|
+
for (const query of queries[filepath]) {
|
|
121
|
+
// we can't generate actual type defs for props so let's just export a
|
|
122
|
+
// generic typedefinition
|
|
123
|
+
typeFile =
|
|
124
|
+
`
|
|
125
|
+
import type { ${query.name}$input } from '${path
|
|
126
|
+
.relative(filepath, path.join(config.artifactDirectory, query.name))
|
|
127
|
+
.replace('/$houdini', '')}'
|
|
128
|
+
` +
|
|
129
|
+
typeFile +
|
|
130
|
+
`
|
|
131
|
+
export type ${config.variableFunctionName(
|
|
132
|
+
query.name
|
|
133
|
+
)} = <_Props = ${prop_type}>(args: { props: _Props }) => FragmentQueryVars$input
|
|
134
|
+
`
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// we need to write this file in the correct location in the type root dir
|
|
139
|
+
const relative = path.join(config.typeRootDir, path.relative(config.projectRoot, root))
|
|
140
|
+
|
|
141
|
+
// write the file
|
|
142
|
+
await fs.mkdirp(relative)
|
|
143
|
+
await fs.writeFile(path.join(relative, '$houdini.d.ts'), typeFile)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
type ProjectDirs = {
|
|
147
|
+
dirs: Record<string, ProjectDirs>
|
|
148
|
+
files: string[]
|
|
149
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { GenerateHookInput, fs, Config } from 'houdini'
|
|
2
|
+
|
|
3
|
+
import { stores_directory, type_route_dir } from '../kit'
|
|
4
|
+
import adapter from './adapter'
|
|
5
|
+
import components from './components'
|
|
6
|
+
import kit from './routes'
|
|
7
|
+
import stores from './stores'
|
|
8
|
+
|
|
9
|
+
export default async function (input: PluginGenerateInput) {
|
|
10
|
+
// create the static directories
|
|
11
|
+
await Promise.all([
|
|
12
|
+
fs.mkdirp(type_route_dir(input.config)),
|
|
13
|
+
fs.mkdirp(stores_directory(input.plugin_root)),
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
// generate the files
|
|
17
|
+
await Promise.all([
|
|
18
|
+
adapter(input),
|
|
19
|
+
kit(input.framework, input),
|
|
20
|
+
stores(input),
|
|
21
|
+
components(input.framework, input),
|
|
22
|
+
])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type PluginGenerateInput = Omit<GenerateHookInput, 'config'> & {
|
|
26
|
+
config: Config
|
|
27
|
+
framework: 'kit' | 'svelte'
|
|
28
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { OperationDefinitionNode } from 'graphql'
|
|
2
|
+
import { Config, fs, GenerateHookInput, path } from 'houdini'
|
|
3
|
+
|
|
4
|
+
import { extract_load_function } from '../../extractLoadFunction'
|
|
5
|
+
import {
|
|
6
|
+
type_route_dir,
|
|
7
|
+
walk_routes,
|
|
8
|
+
stores_directory_name,
|
|
9
|
+
store_suffix,
|
|
10
|
+
Framework,
|
|
11
|
+
} from '../../kit'
|
|
12
|
+
|
|
13
|
+
export default async function svelteKitGenerator(
|
|
14
|
+
framework: Framework,
|
|
15
|
+
{ config }: GenerateHookInput
|
|
16
|
+
) {
|
|
17
|
+
// this generator creates the locally imported type definitions.
|
|
18
|
+
// the component type generator will handle
|
|
19
|
+
if (framework !== 'kit') {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// we need to walk down their route directory and create any variable definitions we need
|
|
24
|
+
await walk_routes(config, framework, {
|
|
25
|
+
async route({
|
|
26
|
+
dirpath,
|
|
27
|
+
pageScript,
|
|
28
|
+
layoutScript,
|
|
29
|
+
routePageQuery,
|
|
30
|
+
routeLayoutQuery,
|
|
31
|
+
inlineLayoutQueries,
|
|
32
|
+
inlineQueries,
|
|
33
|
+
}) {
|
|
34
|
+
// in order to create the variable definition we need to know every query that is being
|
|
35
|
+
// used in a specific route so we can generate versions of the variable functions with
|
|
36
|
+
// the Params type from './$types' provided by the sveltekit rootDir
|
|
37
|
+
|
|
38
|
+
let scriptPageExports: string[] = []
|
|
39
|
+
let scriptLayoutExports: string[] = []
|
|
40
|
+
|
|
41
|
+
const pageQueries = inlineQueries.concat(routePageQuery ?? [])
|
|
42
|
+
const layoutQueries = inlineLayoutQueries.concat(routeLayoutQuery ?? [])
|
|
43
|
+
|
|
44
|
+
// pageScript need to be imported so we can figure out if there is a houdini_load
|
|
45
|
+
// and what's inside.
|
|
46
|
+
if (pageScript) {
|
|
47
|
+
// import the houdini_load function
|
|
48
|
+
const { houdini_load, exports } = await extract_load_function(config, pageScript)
|
|
49
|
+
|
|
50
|
+
// add every load to the list
|
|
51
|
+
pageQueries.push(...(houdini_load ?? []))
|
|
52
|
+
scriptPageExports = exports
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// pageScript need to be imported so we can figure out if there is a houdini_load
|
|
56
|
+
// and what's inside.
|
|
57
|
+
if (layoutScript) {
|
|
58
|
+
// import the houdini_load function
|
|
59
|
+
const { houdini_load, exports } = await extract_load_function(config, layoutScript)
|
|
60
|
+
|
|
61
|
+
// add every load to the list
|
|
62
|
+
layoutQueries.push(...(houdini_load ?? []))
|
|
63
|
+
scriptLayoutExports = exports
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// if we have no queries, there's nothing to do
|
|
67
|
+
if (pageQueries.length === 0 && layoutQueries.length === 0) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// we need to write the type defs to the same route path relative to the type root
|
|
72
|
+
// const targetPath = path.join(config.typeRouteDir,
|
|
73
|
+
const relativePath = path.relative(config.routesDir, dirpath)
|
|
74
|
+
const target = path.join(type_route_dir(config), relativePath, config.typeRootFile)
|
|
75
|
+
|
|
76
|
+
// we can't import from $houdini so we need to compute the relative path from the import
|
|
77
|
+
const houdiniRelative = path.relative(target, config.typeRootDir)
|
|
78
|
+
|
|
79
|
+
// the unique set of query names
|
|
80
|
+
const queryNames: string[] = []
|
|
81
|
+
const uniqueQueries: OperationDefinitionNode[] = []
|
|
82
|
+
for (const query of pageQueries) {
|
|
83
|
+
if (!queryNames.includes(query.name!.value)) {
|
|
84
|
+
queryNames.push(query.name!.value)
|
|
85
|
+
uniqueQueries.push(query)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const layoutNames: string[] = []
|
|
90
|
+
const uniqueLayoutQueries: OperationDefinitionNode[] = []
|
|
91
|
+
for (const layout of layoutQueries) {
|
|
92
|
+
if (!layoutNames.includes(layout.name!.value)) {
|
|
93
|
+
layoutNames.push(layout.name!.value)
|
|
94
|
+
uniqueLayoutQueries.push(layout)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// we need to create a typescript file that has a definition of the variable and hook functions
|
|
99
|
+
const typeDefs = getTypeDefs(
|
|
100
|
+
houdiniRelative,
|
|
101
|
+
config,
|
|
102
|
+
uniqueQueries,
|
|
103
|
+
pageQueries,
|
|
104
|
+
uniqueLayoutQueries,
|
|
105
|
+
layoutQueries,
|
|
106
|
+
scriptPageExports,
|
|
107
|
+
scriptLayoutExports
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
// make sure we have a home for the directory
|
|
111
|
+
await fs.mkdirp(path.dirname(target))
|
|
112
|
+
|
|
113
|
+
// write the file
|
|
114
|
+
await fs.writeFile(target, typeDefs)
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getTypeDefs(
|
|
120
|
+
houdiniRelative: string,
|
|
121
|
+
config: Config,
|
|
122
|
+
uniqueQueries: OperationDefinitionNode[],
|
|
123
|
+
pageQueries: OperationDefinitionNode[],
|
|
124
|
+
uniqueLayoutQueries: OperationDefinitionNode[],
|
|
125
|
+
layoutQueries: OperationDefinitionNode[],
|
|
126
|
+
scriptPageExports: string[],
|
|
127
|
+
scriptLayoutExports: string[]
|
|
128
|
+
) {
|
|
129
|
+
const afterPageLoad = scriptPageExports.includes('afterLoad')
|
|
130
|
+
const beforePageLoad = scriptPageExports.includes('beforeLoad')
|
|
131
|
+
const onPageError = scriptPageExports.includes('onError')
|
|
132
|
+
|
|
133
|
+
const afterLayoutLoad = scriptLayoutExports.includes('afterLoad')
|
|
134
|
+
const beforeLayoutLoad = scriptLayoutExports.includes('beforeLoad')
|
|
135
|
+
const onLayoutError = scriptLayoutExports.includes('onError')
|
|
136
|
+
|
|
137
|
+
return `import type * as Kit from '@sveltejs/kit';
|
|
138
|
+
import type { VariableFunction, AfterLoadFunction, BeforeLoadFunction } from '${houdiniRelative}/plugins/houdini-svelte/runtime/types'
|
|
139
|
+
${
|
|
140
|
+
pageQueries.length > 0
|
|
141
|
+
? `import type { PageLoadEvent, PageData as KitPageData } from './$types'`
|
|
142
|
+
: ``
|
|
143
|
+
}
|
|
144
|
+
${
|
|
145
|
+
layoutQueries.length > 0
|
|
146
|
+
? `import type { LayoutLoadEvent, LayoutData as KitPageData } from './$types'`
|
|
147
|
+
: ``
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
${append_Store(houdiniRelative, config, uniqueQueries)}
|
|
151
|
+
${append_Store(houdiniRelative, config, uniqueLayoutQueries)}
|
|
152
|
+
|
|
153
|
+
${pageQueries.length > 0 ? `type PageParams = PageLoadEvent['params']` : ``}
|
|
154
|
+
${layoutQueries.length > 0 ? `type LayoutParams = LayoutLoadEvent['params']` : ``}
|
|
155
|
+
|
|
156
|
+
${append_VariablesFunction('Page', config, uniqueQueries)}
|
|
157
|
+
${append_VariablesFunction('Layout', config, uniqueLayoutQueries)}
|
|
158
|
+
|
|
159
|
+
${append_afterLoad('Page', afterPageLoad, pageQueries)}
|
|
160
|
+
${append_beforeLoad(beforePageLoad)}
|
|
161
|
+
${append_onError(onPageError)}
|
|
162
|
+
${append_afterLoad('Layout', afterLayoutLoad, layoutQueries)}
|
|
163
|
+
${append_beforeLoad(beforeLayoutLoad)}
|
|
164
|
+
${append_onError(onLayoutError)}
|
|
165
|
+
|
|
166
|
+
${append_TypeData(
|
|
167
|
+
config,
|
|
168
|
+
layoutQueries,
|
|
169
|
+
'Layout',
|
|
170
|
+
beforeLayoutLoad,
|
|
171
|
+
afterLayoutLoad,
|
|
172
|
+
onLayoutError
|
|
173
|
+
)}
|
|
174
|
+
${append_TypeData(config, pageQueries, 'Page', beforePageLoad, afterPageLoad, onPageError)}
|
|
175
|
+
`
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function append_Store(houdiniRelative: string, config: Config, queries: OperationDefinitionNode[]) {
|
|
179
|
+
return queries
|
|
180
|
+
.map((query) => {
|
|
181
|
+
const name = query.name!.value
|
|
182
|
+
|
|
183
|
+
return `import { ${name}$result, ${name}$input } from '${houdiniRelative}/${
|
|
184
|
+
config.artifactDirectoryName
|
|
185
|
+
}/${name}'
|
|
186
|
+
import { ${name}Store } from '${houdiniRelative}/${stores_directory_name()}/${name}'`
|
|
187
|
+
})
|
|
188
|
+
.join('\n')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function append_VariablesFunction(
|
|
192
|
+
type: `Page` | `Layout`,
|
|
193
|
+
config: Config,
|
|
194
|
+
queries: OperationDefinitionNode[]
|
|
195
|
+
) {
|
|
196
|
+
return queries
|
|
197
|
+
.map((query) => {
|
|
198
|
+
const name = query.name!.value
|
|
199
|
+
// if the query does not have any variables, don't include anything
|
|
200
|
+
if (!query.variableDefinitions?.length) {
|
|
201
|
+
return ''
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return `export type ${config.variableFunctionName(
|
|
205
|
+
name
|
|
206
|
+
)} = VariableFunction<${type}Params, ${name}$input>`
|
|
207
|
+
})
|
|
208
|
+
.join('\n')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function append_afterLoad(
|
|
212
|
+
type: `Page` | `Layout`,
|
|
213
|
+
afterLoad: boolean,
|
|
214
|
+
queries: OperationDefinitionNode[]
|
|
215
|
+
) {
|
|
216
|
+
return afterLoad
|
|
217
|
+
? `
|
|
218
|
+
type AfterLoadReturn = ReturnType<typeof import('./+${type.toLowerCase()}').afterLoad>;
|
|
219
|
+
|
|
220
|
+
type AfterLoadData = {
|
|
221
|
+
${internal_append_afterLoad(queries)}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
type LoadInput = {
|
|
225
|
+
${internal_append_afterLoadInput(queries)}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export type AfterLoadEvent = {
|
|
229
|
+
event: PageLoadEvent
|
|
230
|
+
data: AfterLoadData
|
|
231
|
+
input: LoadInput
|
|
232
|
+
}
|
|
233
|
+
`
|
|
234
|
+
: ''
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function internal_append_afterLoad(queries: OperationDefinitionNode[]) {
|
|
238
|
+
return `${queries
|
|
239
|
+
.map((query) => {
|
|
240
|
+
// if the query does not have any variables, don't include anything
|
|
241
|
+
const name = query.name!.value
|
|
242
|
+
|
|
243
|
+
return [name, name + '$result'].join(': ')
|
|
244
|
+
})
|
|
245
|
+
.join(', \n')}`
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function internal_append_afterLoadInput(queries: OperationDefinitionNode[]) {
|
|
249
|
+
return `${queries
|
|
250
|
+
.filter((query) => query.variableDefinitions?.length)
|
|
251
|
+
.map((query) => {
|
|
252
|
+
// if the query does not have any variables, don't include anything
|
|
253
|
+
const name = query.name!.value
|
|
254
|
+
|
|
255
|
+
return [name, name + '$input'].join(': ')
|
|
256
|
+
})
|
|
257
|
+
.join(', \n')}`
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function append_beforeLoad(beforeLoad: boolean) {
|
|
261
|
+
return beforeLoad
|
|
262
|
+
? `
|
|
263
|
+
export type BeforeLoadEvent = PageLoadEvent
|
|
264
|
+
|
|
265
|
+
type BeforeLoadReturn = ReturnType<typeof import('./+page').beforeLoad>;
|
|
266
|
+
`
|
|
267
|
+
: ''
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function append_onError(onError: boolean) {
|
|
271
|
+
return onError
|
|
272
|
+
? `
|
|
273
|
+
export type OnErrorEvent = { event: LoadEvent, input: LoadInput, error: Error | Error[] }
|
|
274
|
+
|
|
275
|
+
type OnErrorReturn = ReturnType<typeof import('./+page').onError>;
|
|
276
|
+
`
|
|
277
|
+
: ''
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function append_TypeData(
|
|
281
|
+
config: Config,
|
|
282
|
+
queries: OperationDefinitionNode[],
|
|
283
|
+
type: `Page` | `Layout`,
|
|
284
|
+
beforeLoad: boolean,
|
|
285
|
+
afterLoad: boolean,
|
|
286
|
+
onError: boolean
|
|
287
|
+
) {
|
|
288
|
+
if (queries.length === 0) {
|
|
289
|
+
return ''
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return `export type ${type}Data = {
|
|
293
|
+
${queries
|
|
294
|
+
.map((query) => {
|
|
295
|
+
const name = query.name!.value
|
|
296
|
+
|
|
297
|
+
return [name, name + store_suffix(config)].join(': ')
|
|
298
|
+
})
|
|
299
|
+
.join(', \n')}
|
|
300
|
+
} ${internal_append_TypeDataExtra(beforeLoad, afterLoad, onError)}`
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function internal_append_TypeDataExtra(beforeLoad: boolean, afterLoad: boolean, onError: boolean) {
|
|
304
|
+
return `${beforeLoad ? '& BeforeLoadReturn ' : ''} ${afterLoad ? '& AfterLoadReturn ' : ''} ${
|
|
305
|
+
onError ? '& OnErrorReturn ' : ''
|
|
306
|
+
}`
|
|
307
|
+
}
|