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.
Files changed (59) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +21 -0
  3. package/package.json +57 -0
  4. package/src/plugin/codegen/adapter.ts +45 -0
  5. package/src/plugin/codegen/components/index.ts +149 -0
  6. package/src/plugin/codegen/index.ts +28 -0
  7. package/src/plugin/codegen/routes/index.ts +307 -0
  8. package/src/plugin/codegen/routes/kit.test.ts +276 -0
  9. package/src/plugin/codegen/stores/fragment.ts +83 -0
  10. package/src/plugin/codegen/stores/index.ts +55 -0
  11. package/src/plugin/codegen/stores/mutation.ts +56 -0
  12. package/src/plugin/codegen/stores/query.test.ts +504 -0
  13. package/src/plugin/codegen/stores/query.ts +97 -0
  14. package/src/plugin/codegen/stores/subscription.ts +57 -0
  15. package/src/plugin/extract.test.ts +290 -0
  16. package/src/plugin/extract.ts +127 -0
  17. package/src/plugin/extractLoadFunction.test.ts +247 -0
  18. package/src/plugin/extractLoadFunction.ts +249 -0
  19. package/src/plugin/fsPatch.ts +238 -0
  20. package/src/plugin/imports.ts +28 -0
  21. package/src/plugin/index.ts +165 -0
  22. package/src/plugin/kit.ts +382 -0
  23. package/src/plugin/transforms/index.ts +90 -0
  24. package/src/plugin/transforms/kit/index.ts +20 -0
  25. package/src/plugin/transforms/kit/init.test.ts +28 -0
  26. package/src/plugin/transforms/kit/init.ts +75 -0
  27. package/src/plugin/transforms/kit/load.test.ts +1234 -0
  28. package/src/plugin/transforms/kit/load.ts +506 -0
  29. package/src/plugin/transforms/kit/session.test.ts +268 -0
  30. package/src/plugin/transforms/kit/session.ts +161 -0
  31. package/src/plugin/transforms/query.test.ts +99 -0
  32. package/src/plugin/transforms/query.ts +263 -0
  33. package/src/plugin/transforms/reactive.ts +126 -0
  34. package/src/plugin/transforms/tags.ts +20 -0
  35. package/src/plugin/transforms/types.ts +9 -0
  36. package/src/plugin/validate.test.ts +95 -0
  37. package/src/plugin/validate.ts +50 -0
  38. package/src/preprocess/index.ts +33 -0
  39. package/src/runtime/adapter.ts +21 -0
  40. package/src/runtime/fragments.ts +86 -0
  41. package/src/runtime/index.ts +72 -0
  42. package/src/runtime/network.ts +6 -0
  43. package/src/runtime/session.ts +187 -0
  44. package/src/runtime/stores/fragment.ts +48 -0
  45. package/src/runtime/stores/index.ts +5 -0
  46. package/src/runtime/stores/mutation.ts +185 -0
  47. package/src/runtime/stores/pagination/cursor.ts +265 -0
  48. package/src/runtime/stores/pagination/fetch.ts +7 -0
  49. package/src/runtime/stores/pagination/fragment.ts +236 -0
  50. package/src/runtime/stores/pagination/index.ts +7 -0
  51. package/src/runtime/stores/pagination/offset.ts +162 -0
  52. package/src/runtime/stores/pagination/pageInfo.test.ts +39 -0
  53. package/src/runtime/stores/pagination/pageInfo.ts +67 -0
  54. package/src/runtime/stores/pagination/query.ts +132 -0
  55. package/src/runtime/stores/query.ts +524 -0
  56. package/src/runtime/stores/store.ts +13 -0
  57. package/src/runtime/stores/subscription.ts +107 -0
  58. package/src/runtime/types.ts +40 -0
  59. package/src/test/index.ts +208 -0
@@ -0,0 +1,268 @@
1
+ import { test, expect } from 'vitest'
2
+
3
+ import { test_transform_js, test_transform_svelte } from '../../../test'
4
+
5
+ test('modifies root +layout.svelte with data prop', async function () {
6
+ // run the test
7
+ const result = await test_transform_svelte(
8
+ 'src/routes/+layout.svelte',
9
+ `
10
+ <script>
11
+ export let data
12
+ </script>
13
+ `
14
+ )
15
+
16
+ expect(result).toMatchInlineSnapshot(`
17
+ import { page } from "$app/stores";
18
+ import { extractSession, setClientSession } from "$houdini/plugins/houdini-svelte/runtime/session";
19
+ import { onMount } from "svelte";
20
+ import { setClientStarted } from "$houdini/plugins/houdini-svelte/runtime/adapter";
21
+ export let data;
22
+ onMount(() => setClientStarted());
23
+
24
+ page.subscribe(val => {
25
+ setClientSession(extractSession(val.data));
26
+ });
27
+ `)
28
+ })
29
+
30
+ test('modifies root +layout.svelte without data prop', async function () {
31
+ // run the test
32
+ const result = await test_transform_svelte('src/routes/+layout.svelte', ``)
33
+
34
+ expect(result).toMatchInlineSnapshot(`
35
+ import { page } from "$app/stores";
36
+ import { extractSession, setClientSession } from "$houdini/plugins/houdini-svelte/runtime/session";
37
+ import { onMount } from "svelte";
38
+ import { setClientStarted } from "$houdini/plugins/houdini-svelte/runtime/adapter";
39
+ onMount(() => setClientStarted());
40
+
41
+ page.subscribe(val => {
42
+ setClientSession(extractSession(val.data));
43
+ });
44
+ `)
45
+ })
46
+
47
+ test('adds load to +layout.server.js', async function () {
48
+ const result = await test_transform_js('src/routes/+layout.server.js', ``)
49
+
50
+ expect(result).toMatchInlineSnapshot(`
51
+ import { buildSessionObject } from "$houdini/plugins/houdini-svelte/runtime/session";
52
+
53
+ export async function load(event) {
54
+ const __houdini__vite__plugin__return__value__ = {};
55
+
56
+ return {
57
+ ...buildSessionObject(event),
58
+ ...__houdini__vite__plugin__return__value__
59
+ };
60
+ }
61
+ `)
62
+ })
63
+
64
+ test('modifies existing load +layout.server.js', async function () {
65
+ const result = await test_transform_js(
66
+ 'src/routes/+layout.server.js',
67
+ `
68
+ export async function load() {
69
+ "some random stuff that's valid javascript"
70
+ return {
71
+ hello: "world",
72
+ }
73
+
74
+ }
75
+ `
76
+ )
77
+
78
+ expect(result).toMatchInlineSnapshot(`
79
+ import { buildSessionObject } from "$houdini/plugins/houdini-svelte/runtime/session";
80
+
81
+ export async function load(event) {
82
+ "some random stuff that's valid javascript";
83
+ const __houdini__vite__plugin__return__value__ = {
84
+ hello: "world"
85
+ };
86
+
87
+ return {
88
+ ...buildSessionObject(event),
89
+ ...__houdini__vite__plugin__return__value__
90
+ };
91
+ }
92
+ `)
93
+ })
94
+
95
+ test('modifies existing load +layout.server.js - no return', async function () {
96
+ const result = await test_transform_js(
97
+ 'src/routes/+layout.server.js',
98
+ `
99
+ export async function load() {
100
+ "some random stuff that's valid javascript"
101
+ }
102
+ `
103
+ )
104
+
105
+ expect(result).toMatchInlineSnapshot(`
106
+ import { buildSessionObject } from "$houdini/plugins/houdini-svelte/runtime/session";
107
+
108
+ export async function load(event) {
109
+ "some random stuff that's valid javascript";
110
+ const __houdini__vite__plugin__return__value__ = {};
111
+
112
+ return {
113
+ ...buildSessionObject(event),
114
+ ...__houdini__vite__plugin__return__value__
115
+ };
116
+ }
117
+ `)
118
+ })
119
+
120
+ test('modifies existing load +layout.server.js - rest params', async function () {
121
+ const result = await test_transform_js(
122
+ 'src/routes/+layout.server.js',
123
+ `
124
+ export async function load({ foo, bar, ...baz }) {
125
+ console.log(foo)
126
+ return {
127
+ some: 'value'
128
+ }
129
+ }
130
+ `
131
+ )
132
+
133
+ expect(result).toMatchInlineSnapshot(`
134
+ import { buildSessionObject } from "$houdini/plugins/houdini-svelte/runtime/session";
135
+
136
+ export async function load(event) {
137
+ let {
138
+ foo,
139
+ bar,
140
+ ...baz
141
+ } = event;
142
+
143
+ console.log(foo);
144
+
145
+ const __houdini__vite__plugin__return__value__ = {
146
+ some: "value"
147
+ };
148
+
149
+ return {
150
+ ...buildSessionObject(event),
151
+ ...__houdini__vite__plugin__return__value__
152
+ };
153
+ }
154
+ `)
155
+ })
156
+
157
+ test('modifies existing load +layout.server.js - const arrow function', async function () {
158
+ const result = await test_transform_js(
159
+ 'src/routes/+layout.server.js',
160
+ `
161
+ export const load = ({ foo, bar, ...baz }) => {
162
+ console.log(foo)
163
+ return {
164
+ some: 'value'
165
+ }
166
+ }
167
+ `
168
+ )
169
+
170
+ expect(result).toMatchInlineSnapshot(`
171
+ import { buildSessionObject } from "$houdini/plugins/houdini-svelte/runtime/session";
172
+
173
+ export const load = event => {
174
+ let {
175
+ foo,
176
+ bar,
177
+ ...baz
178
+ } = event;
179
+
180
+ console.log(foo);
181
+
182
+ const __houdini__vite__plugin__return__value__ = {
183
+ some: "value"
184
+ };
185
+
186
+ return {
187
+ ...buildSessionObject(event),
188
+ ...__houdini__vite__plugin__return__value__
189
+ };
190
+ };
191
+ `)
192
+ })
193
+
194
+ test('modifies existing load +layout.server.js - const function', async function () {
195
+ const result = await test_transform_js(
196
+ 'src/routes/+layout.server.js',
197
+ `
198
+ export const load = function({ foo, bar, ...baz }) {
199
+ console.log(foo)
200
+ return {
201
+ some: 'value'
202
+ }
203
+ }
204
+ `
205
+ )
206
+
207
+ expect(result).toMatchInlineSnapshot(`
208
+ import { buildSessionObject } from "$houdini/plugins/houdini-svelte/runtime/session";
209
+
210
+ export const load = function(event) {
211
+ let {
212
+ foo,
213
+ bar,
214
+ ...baz
215
+ } = event;
216
+
217
+ console.log(foo);
218
+
219
+ const __houdini__vite__plugin__return__value__ = {
220
+ some: "value"
221
+ };
222
+
223
+ return {
224
+ ...buildSessionObject(event),
225
+ ...__houdini__vite__plugin__return__value__
226
+ };
227
+ };
228
+ `)
229
+ })
230
+
231
+ test('modifies existing load +layout.server.js - implicit return', async function () {
232
+ const result = await test_transform_js(
233
+ 'src/routes/+layout.server.js',
234
+ `
235
+ export const load = () => ({ hello: 'world'})
236
+ `
237
+ )
238
+
239
+ expect(result).toMatchInlineSnapshot(`
240
+ import { buildSessionObject } from "$houdini/plugins/houdini-svelte/runtime/session";
241
+
242
+ export const load = event => {
243
+ const __houdini__vite__plugin__return__value__ = ({
244
+ hello: "world"
245
+ });
246
+
247
+ return {
248
+ ...buildSessionObject(event),
249
+ ...__houdini__vite__plugin__return__value__
250
+ };
251
+ };
252
+ `)
253
+ })
254
+
255
+ test('passes session from root client-side layout', async function () {
256
+ const result = await test_transform_js('src/routes/+layout.js', ``)
257
+
258
+ expect(result).toMatchInlineSnapshot(`
259
+ export async function load(event) {
260
+ const __houdini__vite__plugin__return__value__ = {};
261
+
262
+ return {
263
+ ...event.data,
264
+ ...__houdini__vite__plugin__return__value__
265
+ };
266
+ }
267
+ `)
268
+ })
@@ -0,0 +1,161 @@
1
+ import { find_exported_fn, find_insert_index } from 'houdini/vite'
2
+ import { ensure_imports } from 'houdini/vite'
3
+ import * as recast from 'recast'
4
+
5
+ import { is_root_layout_script, is_root_layout_server } from '../../kit'
6
+ import { SvelteTransformPage } from '../types'
7
+
8
+ const AST = recast.types.builders
9
+
10
+ type ReturnStatement = recast.types.namedTypes.ReturnStatement
11
+ type BlockStatement = recast.types.namedTypes.BlockStatement
12
+ type Identifier = recast.types.namedTypes.Identifier
13
+ type ObjectExpression = recast.types.namedTypes.ObjectExpression
14
+
15
+ export default function (page: SvelteTransformPage) {
16
+ if (is_root_layout_server(page.config, page.filepath)) {
17
+ process_root_layout_server(page)
18
+ } else if (is_root_layout_script(page.config, page.filepath)) {
19
+ process_root_layout_script(page)
20
+ }
21
+ }
22
+
23
+ // the root layout server file (src/routes/+layout.server.js) needs to define a load that's accessible
24
+ // to all routes that adds the session set in the application's hook file along with any existing values
25
+ // This is done in three steps:
26
+ // - define a load if there isn't one
27
+ // - set the current return value to some internal name
28
+ // - add a new return statement that includes the session data from event.locals
29
+ function process_root_layout_server(page: SvelteTransformPage) {
30
+ const build_session_object = ensure_imports({
31
+ script: page.script,
32
+ config: page.config,
33
+ import: ['buildSessionObject'],
34
+ sourceModule: '$houdini/plugins/houdini-svelte/runtime/session',
35
+ }).ids[0]
36
+
37
+ add_load_return(page, (event_id) => [
38
+ AST.spreadElement(AST.callExpression(build_session_object, [event_id])),
39
+ ])
40
+ }
41
+
42
+ // all we need to do is make sure the session gets passed down by
43
+ // threading the value through the return
44
+ function process_root_layout_script(page: SvelteTransformPage) {
45
+ add_load_return(page, (event_id) => [
46
+ AST.spreadElement(AST.memberExpression(event_id, AST.identifier('data'))),
47
+ ])
48
+ }
49
+
50
+ function add_load_return(
51
+ page: SvelteTransformPage,
52
+ properties: (id: Identifier) => ObjectExpression['properties']
53
+ ) {
54
+ modify_load(page, (body, event_id) => {
55
+ // we have a load function and `event` is guaranteed to resolve correctly
56
+
57
+ // now we need to find the return statement and replace it with a local variable
58
+ // that we will use later
59
+ let return_statement_index = body.body.findIndex(
60
+ (statement) => statement.type === 'ReturnStatement'
61
+ )
62
+ let return_statement: ReturnStatement
63
+ if (return_statement_index !== -1) {
64
+ return_statement = body.body[return_statement_index] as ReturnStatement
65
+ }
66
+ // there was no return statement so its safe to just push one at the end that sets an empty
67
+ // object
68
+ else {
69
+ return_statement = AST.returnStatement(AST.objectExpression([]))
70
+ body.body.push(return_statement)
71
+ return_statement_index = body.body.length - 1
72
+ }
73
+
74
+ // replace the return statement with the variable declaration
75
+ const local_return_var = AST.identifier('__houdini__vite__plugin__return__value__')
76
+ body.body[return_statement_index] = AST.variableDeclaration('const', [
77
+ AST.variableDeclarator(local_return_var, return_statement.argument),
78
+ ])
79
+
80
+ // its safe to insert a return statement after the declaration that references event
81
+ body.body.splice(
82
+ return_statement_index + 1,
83
+ 0,
84
+ AST.returnStatement(
85
+ AST.objectExpression([...properties(event_id), AST.spreadElement(local_return_var)])
86
+ )
87
+ )
88
+ })
89
+ }
90
+
91
+ function modify_load(
92
+ page: SvelteTransformPage,
93
+ cb: (body: BlockStatement, event_id: Identifier) => void
94
+ ) {
95
+ // before we do anything, we need to find the load function
96
+ let load_fn = find_exported_fn(page.script.body, 'load')
97
+ let event_id = AST.identifier('event')
98
+
99
+ // lets get a reference to the body of the function
100
+ let body: BlockStatement = AST.blockStatement([])
101
+ if (load_fn?.type === 'ArrowFunctionExpression') {
102
+ if (load_fn.body.type === 'BlockStatement') {
103
+ body = load_fn.body
104
+ } else {
105
+ body = AST.blockStatement([AST.returnStatement(load_fn.body)])
106
+ load_fn.body = body
107
+ }
108
+ } else if (load_fn) {
109
+ body = load_fn.body
110
+ }
111
+
112
+ // if there is no load function, then we have to add one
113
+ if (!load_fn) {
114
+ load_fn = AST.functionDeclaration(
115
+ AST.identifier('load'),
116
+ [event_id],
117
+ AST.blockStatement([])
118
+ )
119
+ load_fn.async = true
120
+ page.script.body.splice(
121
+ find_insert_index(page.script),
122
+ 0,
123
+ AST.exportNamedDeclaration(load_fn)
124
+ )
125
+ body = load_fn.body
126
+ }
127
+ // there is a load function, we need the event
128
+ else {
129
+ // if there are no identifiers, we need to add one
130
+ if (load_fn.params.length === 0) {
131
+ load_fn.params.push(event_id)
132
+ }
133
+ // if the first parameter of the function declaration is an identifier, we're in business
134
+ else if (load_fn.params[0]?.type === 'Identifier') {
135
+ event_id = load_fn.params[0]
136
+ }
137
+ // the first parameter is not an identifier so it's almost certainly an object pattern pulling parameters out
138
+ else if (load_fn.params[0].type === 'ObjectPattern') {
139
+ // hold onto the pattern so we can re-use it as the first
140
+ const pattern = load_fn.params[0]
141
+
142
+ // overwrite the parameter as event
143
+ load_fn.params[0] = event_id
144
+
145
+ // redefine the variables as let in the first statement of the function
146
+ body.body.unshift(
147
+ AST.variableDeclaration('let', [AST.variableDeclarator(pattern, event_id)])
148
+ )
149
+ }
150
+ // we can't work with this
151
+ else {
152
+ throw new Error(
153
+ 'Could not inject session data into load. Please open a ticket with the contents of ' +
154
+ page.filepath
155
+ )
156
+ }
157
+ }
158
+
159
+ // modify the body
160
+ cb(body, event_id)
161
+ }
@@ -0,0 +1,99 @@
1
+ import { test, expect } from 'vitest'
2
+
3
+ import { component_test } from '../../test'
4
+
5
+ test('no variables', async function () {
6
+ const route = await component_test(
7
+ `
8
+ const value = graphql\`
9
+ query TestQuery {
10
+ viewer {
11
+ id
12
+ }
13
+ }
14
+ \`
15
+ `
16
+ )
17
+
18
+ // make sure we added the right stuff
19
+ expect(route).toMatchInlineSnapshot(`
20
+ import { TestQueryStore } from "$houdini/plugins/houdini-svelte/stores/TestQuery";
21
+ import { isBrowser } from "$houdini/plugins/houdini-svelte/runtime/adapter";
22
+ import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session";
23
+ import { marshalInputs } from "$houdini/runtime/lib/scalars";
24
+ const _houdini_TestQuery = new TestQueryStore();
25
+
26
+ $:
27
+ value = _houdini_TestQuery;
28
+
29
+ $:
30
+ marshalInputs({
31
+ artifact: _houdini_TestQuery.artifact,
32
+ input: {}
33
+ }).then(_TestQuery_Input => isBrowser && _houdini_TestQuery.fetch({
34
+ variables: _TestQuery_Input
35
+ }));
36
+ `)
37
+ })
38
+
39
+ test('with variables', async function () {
40
+ const route = await component_test(
41
+ `
42
+ export function TestQueryVariables() {
43
+ return {
44
+ hello: 'world'
45
+ }
46
+ }
47
+
48
+ export let prop1 = 'hello'
49
+ export const prop2 = 'goodbye'
50
+ export let prop3, prop4
51
+
52
+ const result = graphql\`
53
+ query TestQuery($test: String!) {
54
+ users(stringValue: $test) {
55
+ id
56
+ }
57
+ }
58
+ \`
59
+ `
60
+ )
61
+
62
+ // make sure we added the right stuff
63
+ expect(route).toMatchInlineSnapshot(`
64
+ import { TestQueryStore } from "$houdini/plugins/houdini-svelte/stores/TestQuery";
65
+ import { isBrowser } from "$houdini/plugins/houdini-svelte/runtime/adapter";
66
+ import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session";
67
+ import { marshalInputs } from "$houdini/runtime/lib/scalars";
68
+ const _houdini_TestQuery = new TestQueryStore();
69
+
70
+ export function TestQueryVariables() {
71
+ return {
72
+ hello: "world"
73
+ };
74
+ }
75
+
76
+ export let prop1 = "hello";
77
+ export const prop2 = "goodbye";
78
+ export let prop3, prop4;
79
+
80
+ $:
81
+ result = _houdini_TestQuery;
82
+
83
+ $:
84
+ marshalInputs({
85
+ artifact: _houdini_TestQuery.artifact,
86
+
87
+ input: TestQueryVariables.call(new RequestContext(), {
88
+ props: {
89
+ prop1: prop1,
90
+ prop2: prop2,
91
+ prop3: prop3,
92
+ prop4: prop4
93
+ }
94
+ })
95
+ }).then(_TestQuery_Input => isBrowser && _houdini_TestQuery.fetch({
96
+ variables: _TestQuery_Input
97
+ }));
98
+ `)
99
+ })