houdini 0.17.4 → 0.17.6

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 (55) hide show
  1. package/.turbo/turbo-compile.log +2 -2
  2. package/.turbo/turbo-typedefs.log +2 -2
  3. package/CHANGELOG.md +18 -0
  4. package/build/cmd/init.d.ts +2 -2
  5. package/build/cmd-cjs/index.js +9344 -10447
  6. package/build/cmd-esm/index.js +9344 -10447
  7. package/build/codegen/transforms/paginate.d.ts +10 -11
  8. package/build/codegen-cjs/index.js +9143 -10227
  9. package/build/codegen-esm/index.js +9143 -10227
  10. package/build/lib/constants.d.ts +7 -0
  11. package/build/lib/fs.d.ts +2 -0
  12. package/build/lib-cjs/index.js +9265 -10347
  13. package/build/lib-esm/index.js +9264 -10347
  14. package/build/runtime/cache/subscription.d.ts +2 -1
  15. package/build/runtime/lib/network.d.ts +5 -2
  16. package/build/runtime/lib/networkUtils.d.ts +8 -0
  17. package/build/runtime-cjs/cache/cache.js +5 -3
  18. package/build/runtime-cjs/cache/subscription.d.ts +2 -1
  19. package/build/runtime-cjs/cache/subscription.js +6 -4
  20. package/build/runtime-cjs/cache/tests/subscriptions.test.js +101 -0
  21. package/build/runtime-cjs/lib/network.d.ts +5 -2
  22. package/build/runtime-cjs/lib/network.js +39 -3
  23. package/build/runtime-cjs/lib/networkUtils.d.ts +8 -0
  24. package/build/runtime-cjs/lib/networkUtils.js +85 -0
  25. package/build/runtime-esm/cache/cache.js +5 -3
  26. package/build/runtime-esm/cache/subscription.d.ts +2 -1
  27. package/build/runtime-esm/cache/subscription.js +6 -4
  28. package/build/runtime-esm/cache/tests/subscriptions.test.js +101 -0
  29. package/build/runtime-esm/lib/network.d.ts +5 -2
  30. package/build/runtime-esm/lib/network.js +39 -3
  31. package/build/runtime-esm/lib/networkUtils.d.ts +8 -0
  32. package/build/runtime-esm/lib/networkUtils.js +60 -0
  33. package/build/test-cjs/index.js +9144 -10228
  34. package/build/test-esm/index.js +9144 -10228
  35. package/build/vite-cjs/index.js +9283 -10367
  36. package/build/vite-esm/index.js +9283 -10367
  37. package/package.json +2 -2
  38. package/src/cmd/init.ts +169 -187
  39. package/src/codegen/generators/artifacts/artifacts.test.ts +99 -66
  40. package/src/codegen/generators/artifacts/pagination.test.ts +12 -8
  41. package/src/codegen/generators/artifacts/policy.test.ts +12 -8
  42. package/src/codegen/generators/definitions/schema.test.ts +12 -36
  43. package/src/codegen/generators/persistedQueries/persistedQuery.test.ts +2 -2
  44. package/src/codegen/generators/runtime/index.ts +2 -2
  45. package/src/codegen/transforms/fragmentVariables.test.ts +24 -16
  46. package/src/codegen/transforms/paginate.test.ts +9 -6
  47. package/src/codegen/transforms/paginate.ts +2 -2
  48. package/src/lib/config.ts +3 -2
  49. package/src/lib/constants.ts +10 -0
  50. package/src/lib/fs.ts +59 -12
  51. package/src/runtime/cache/cache.ts +3 -1
  52. package/src/runtime/cache/subscription.ts +6 -4
  53. package/src/runtime/cache/tests/subscriptions.test.ts +115 -0
  54. package/src/runtime/lib/network.ts +65 -2
  55. package/src/runtime/lib/networkUtils.ts +151 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "houdini",
3
- "version": "0.17.4",
3
+ "version": "0.17.6",
4
4
  "description": "The disappearing GraphQL clients",
5
5
  "type": "module",
6
6
  "devDependencies": {
@@ -28,7 +28,7 @@
28
28
  "estree-walker": "^3.0.1",
29
29
  "fs-extra": "^10.1.0",
30
30
  "glob": "^8.0.3",
31
- "graphql": "^16.6.0",
31
+ "graphql": "^15.8.0",
32
32
  "memfs": "^3.4.7",
33
33
  "micromatch": "^4.0.5",
34
34
  "minimatch": "^5.1.0",
package/src/cmd/init.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { logGreen } from '@kitql/helper'
2
+ import { execSync } from 'child_process'
1
3
  import { getIntrospectionQuery } from 'graphql'
2
4
  import fetch from 'node-fetch'
3
5
  import prompts from 'prompts'
@@ -9,9 +11,10 @@ import { ConfigFile } from '../runtime/lib/config'
9
11
  // as well as pulling down the initial schema representation
10
12
  export default async function init(
11
13
  _path: string | undefined,
12
- args: { headers?: string[]; yes: boolean },
13
- withRunningCheck = true
14
+ args: { headers?: string[]; force_remote_endpoint?: boolean }
14
15
  ): Promise<void> {
16
+ const force_remote_endpoint = args.force_remote_endpoint || false
17
+
15
18
  // before we start anything, let's make sure they have initialized their project
16
19
  try {
17
20
  await fs.stat(path.resolve('./src'))
@@ -35,69 +38,128 @@ export default async function init(
35
38
  // if no path was given, we 'll use cwd
36
39
  const targetPath = _path ? path.resolve(_path) : process.cwd()
37
40
 
38
- // make sure its running
39
- let running = true
40
- if (withRunningCheck) {
41
- running = (
42
- await prompts({
43
- message: 'Is your GraphQL API running?',
44
- name: 'running',
45
- type: 'confirm',
46
- initial: true,
47
- })
48
- ).running
49
- }
50
- if (!running) {
51
- console.log('❌ Your API must be running order to continue')
52
- return
53
- }
41
+ // git check
42
+ if (!force_remote_endpoint) {
43
+ // from https://github.com/sveltejs/kit/blob/master/packages/migrate/migrations/routes/index.js#L60
44
+ let use_git = false
54
45
 
55
- let { url } = await prompts(
56
- {
57
- message: "What's the URL for your api?",
58
- name: 'url',
59
- type: 'text',
60
- initial: 'http://localhost:4000/graphql',
61
- },
62
- {
63
- onCancel() {
64
- process.exit(1)
65
- },
46
+ let dir = targetPath
47
+ do {
48
+ if (fs.existsSync(path.join(dir, '.git'))) {
49
+ use_git = true
50
+ break
51
+ }
52
+ } while (dir !== (dir = path.dirname(dir)))
53
+
54
+ if (use_git) {
55
+ const status = execSync('git status --porcelain', { stdio: 'pipe' }).toString()
56
+
57
+ if (status) {
58
+ const message =
59
+ 'Your git working directory is dirty — we recommend committing your changes before running this migration.\n'
60
+ console.error(message)
61
+
62
+ const { confirm } = await prompts({
63
+ message: 'Continue anyway?',
64
+ name: 'confirm',
65
+ type: 'confirm',
66
+ initial: false,
67
+ })
68
+
69
+ if (!confirm) {
70
+ process.exit(1)
71
+ }
72
+ }
66
73
  }
67
- )
74
+ }
68
75
 
69
- try {
70
- // verify we can send graphql queries to the server
71
- const response = await fetch(url, {
72
- method: 'POST',
73
- headers: {
74
- 'Content-Type': 'application/json',
75
- ...headers,
76
+ // Questions...
77
+ let url = 'http://localhost:5173/api/graphql'
78
+ const { is_remote_endpoint } = force_remote_endpoint
79
+ ? { is_remote_endpoint: true }
80
+ : await prompts(
81
+ {
82
+ message: 'Will you use a remote GraphQL API?',
83
+ name: 'is_remote_endpoint',
84
+ type: 'confirm',
85
+ initial: true,
86
+ },
87
+ {
88
+ onCancel() {
89
+ process.exit(1)
90
+ },
91
+ }
92
+ )
93
+
94
+ let schemaPath = is_remote_endpoint ? './schema.graphql' : 'path/to/src/lib/**/*.graphql'
95
+
96
+ if (is_remote_endpoint) {
97
+ const { url_remote } = await prompts(
98
+ {
99
+ message: "What's the URL for your api?",
100
+ name: 'url_remote',
101
+ type: 'text',
102
+ initial: 'http://localhost:4000/graphql',
76
103
  },
77
- body: JSON.stringify({
78
- query: getIntrospectionQuery(),
79
- }),
80
- })
104
+ {
105
+ onCancel() {
106
+ process.exit(1)
107
+ },
108
+ }
109
+ )
110
+
111
+ // set the url for later
112
+ url = url_remote
113
+ try {
114
+ // verify we can send graphql queries to the server
115
+ const response = await fetch(url, {
116
+ method: 'POST',
117
+ headers: {
118
+ 'Content-Type': 'application/json',
119
+ ...headers,
120
+ },
121
+ body: JSON.stringify({
122
+ query: getIntrospectionQuery(),
123
+ }),
124
+ })
81
125
 
82
- // if the response was not a 200, we have a problem
83
- if (response.status !== 200) {
84
- console.log('❌ That URL is not accepting GraphQL queries. Please try again.')
85
- return await init(_path, args, false)
126
+ // if the response was not a 200, we have a problem
127
+ if (response.status !== 200) {
128
+ console.log('❌ That URL is not accepting GraphQL queries. Please try again.')
129
+ return await init(_path, { ...args, force_remote_endpoint: true })
130
+ }
131
+
132
+ // make sure we can parse the response as json
133
+ await response.json()
134
+ } catch (e) {
135
+ console.log('❌ Something went wrong: ' + (e as Error).message)
136
+ return await init(_path, { ...args, force_remote_endpoint: true })
86
137
  }
138
+ } else {
139
+ // the schema is local so ask them for the path
140
+ const answers = await prompts(
141
+ {
142
+ message: 'Where is your schema located?',
143
+ name: 'schema_path',
144
+ type: 'text',
145
+ initial: schemaPath,
146
+ },
147
+ {
148
+ onCancel() {
149
+ process.exit(1)
150
+ },
151
+ }
152
+ )
87
153
 
88
- // make sure we can parse the response as json
89
- await response.json()
90
- } catch (e) {
91
- console.log('❌ Something went wrong: ' + (e as Error).message)
92
- return await init(_path, args, false)
154
+ schemaPath = answers.schema_path
93
155
  }
94
156
 
95
157
  // try to detect which tools they are using
96
- const { framework, typescript, module } = await detectTools(targetPath)
158
+ const { framework, typescript, module, package_manager } = await detectTools(targetPath)
97
159
 
98
160
  // notify the users of what we detected
99
161
  console.log()
100
- console.log('🔎 Heres what we found:')
162
+ console.log("🔎 Here's what we found:")
101
163
 
102
164
  // framework
103
165
  if (framework === 'kit') {
@@ -130,9 +192,6 @@ export default async function init(
130
192
  process.exit(1)
131
193
  }
132
194
 
133
- // the location for the schema
134
- const schemaPath = './schema.graphql'
135
-
136
195
  // the source directory
137
196
  const sourceDir = path.join(targetPath, 'src')
138
197
  // the config file path
@@ -148,14 +207,16 @@ export default async function init(
148
207
 
149
208
  await updatePackageJSON(targetPath)
150
209
 
151
- await pullSchema(url, path.join(targetPath, schemaPath), headers)
210
+ // let's pull the schema only when we are using a remote endpoint
211
+ if (is_remote_endpoint) {
212
+ await pullSchema(url, path.join(targetPath, schemaPath), headers)
213
+ }
214
+
152
215
  await writeConfigFile({
153
- targetPath,
154
216
  configPath,
155
217
  schemaPath,
156
- framework,
157
218
  module,
158
- url,
219
+ url: is_remote_endpoint ? url : null,
159
220
  houdiniClientImport,
160
221
  })
161
222
  await fs.writeFile(houdiniClientPath, networkFile(url, typescript))
@@ -177,10 +238,19 @@ export default async function init(
177
238
  // we're done!
178
239
  console.log()
179
240
  console.log('🎩 Welcome to Houdini!')
241
+ let cmd_install = 'npm i'
242
+ let cmd_run = 'npm run dev'
243
+ if (package_manager === 'pnpm') {
244
+ cmd_install = 'pnpm i'
245
+ cmd_run = 'pnpm dev'
246
+ } else if (package_manager === 'yarn') {
247
+ cmd_install = 'yarn'
248
+ cmd_run = 'yarn dev'
249
+ }
180
250
  console.log(`
181
251
  👉 Next Steps
182
- 1️⃣ Finalize your installation: npm/yarn/pnpm install
183
- 2️⃣ Start your application: npm run dev
252
+ 1️⃣ Finalize your installation: ${logGreen(cmd_install)}
253
+ 2️⃣ Start your application: ${logGreen(cmd_run)}
184
254
  `)
185
255
  }
186
256
 
@@ -212,30 +282,31 @@ export default new HoudiniClient(fetchQuery);
212
282
  `
213
283
 
214
284
  const writeConfigFile = async ({
215
- targetPath,
216
285
  configPath,
217
286
  schemaPath,
218
- framework,
219
287
  module,
220
288
  url,
221
289
  houdiniClientImport,
222
290
  }: {
223
- targetPath: string
224
291
  configPath: string
225
292
  schemaPath: string
226
- framework: 'kit' | 'svelte'
227
293
  module: 'esm' | 'commonjs'
228
- url: string
294
+ url: string | null
229
295
  houdiniClientImport: string
230
296
  }): Promise<boolean> => {
231
- const config: ConfigFile = {
232
- apiUrl: url,
297
+ const config: ConfigFile = {}
298
+
299
+ // if we have no url, we are using a local schema
300
+ if (url !== null) {
301
+ config.apiUrl = url
233
302
  }
234
303
 
235
304
  // if it's different for defaults, write it down
236
305
  if (schemaPath !== './schema.graphql') {
237
306
  config.schemaPath = schemaPath
238
307
  }
308
+
309
+ // if it's different for defaults, write it down
239
310
  if (module !== 'esm') {
240
311
  config.module = module
241
312
  }
@@ -249,23 +320,25 @@ const writeConfigFile = async ({
249
320
 
250
321
  // the actual config contents
251
322
  const configObj = JSON.stringify(config, null, 4)
323
+ const content_base = `/// <references types="houdini-svelte">
324
+
325
+ /** @type {import('houdini').ConfigFile} */
326
+ const config = ${configObj}`
327
+
252
328
  const content =
253
329
  module === 'esm'
254
330
  ? // ESM default config
255
- `/** @type {import('houdini').ConfigFile} */
256
- const config = ${configObj}
331
+ `${content_base}
257
332
 
258
333
  export default config
259
334
  `
260
335
  : // CommonJS default config
261
- `/** @type {import('houdini').ConfigFile} */
262
- const config = ${configObj}
336
+ `${content_base}}
263
337
 
264
338
  module.exports = config
265
339
  `
266
340
 
267
341
  await updateFile({
268
- projectPath: targetPath,
269
342
  filepath: configPath,
270
343
  content,
271
344
  })
@@ -327,26 +400,6 @@ async function updateViteConfig(
327
400
  ) {
328
401
  const viteConfigPath = path.join(targetPath, `vite.config${typescript ? '.ts' : '.js'}`)
329
402
 
330
- const oldViteConfig1 = `import { sveltekit } from '@sveltejs/kit/vite';
331
-
332
- /** @type {import('vite').UserConfig} */
333
- const config = {
334
- plugins: [sveltekit()]
335
- };
336
-
337
- export default config;
338
- `
339
-
340
- const oldViteConfig2 = `import { sveltekit } from '@sveltejs/kit/vite';
341
- import type { UserConfig } from 'vite';
342
-
343
- const config: UserConfig = {
344
- plugins: [sveltekit()]
345
- };
346
-
347
- export default config;
348
- `
349
-
350
403
  const viteConfigKit = `import { sveltekit } from '@sveltejs/kit/vite';
351
404
  import houdini from 'houdini/vite';
352
405
 
@@ -406,17 +459,13 @@ export default config;
406
459
 
407
460
  if (typescript) {
408
461
  await updateFile({
409
- projectPath: targetPath,
410
462
  filepath: viteConfigPath,
411
463
  content: framework === 'kit' ? viteConfigKitTs : viteConfigSvelteTs,
412
- old: [oldViteConfig1, oldViteConfig2],
413
464
  })
414
465
  } else {
415
466
  await updateFile({
416
- projectPath: targetPath,
417
467
  filepath: viteConfigPath,
418
468
  content: framework === 'kit' ? viteConfigKit : viteConfigSvelte,
419
- old: [oldViteConfig1, oldViteConfig2],
420
469
  })
421
470
  }
422
471
  }
@@ -441,43 +490,13 @@ const config = {
441
490
  }
442
491
  };
443
492
 
444
- export default config;
445
- `
446
-
447
- const oldSvelteConfig1 = `import adapter from '@sveltejs/adapter-auto';
448
- import preprocess from 'svelte-preprocess';
449
-
450
- /** @type {import('@sveltejs/kit').Config} */
451
- const config = {
452
- // Consult https://github.com/sveltejs/svelte-preprocess
453
- // for more information about preprocessors
454
- preprocess: preprocess(),
455
-
456
- kit: {
457
- adapter: adapter()
458
- }
459
- };
460
-
461
- export default config;
462
- `
463
- const oldSvelteConfig2 = `import adapter from '@sveltejs/adapter-auto';
464
-
465
- /** @type {import('@sveltejs/kit').Config} */
466
- const config = {
467
- kit: {
468
- adapter: adapter()
469
- }
470
- };
471
-
472
493
  export default config;
473
494
  `
474
495
 
475
496
  // write the svelte config file
476
497
  await updateFile({
477
- projectPath: targetPath,
478
498
  filepath: svelteConfigPath,
479
499
  content: newContent,
480
- old: [oldSvelteConfig1, oldSvelteConfig2],
481
500
  })
482
501
  }
483
502
 
@@ -487,6 +506,7 @@ async function updateSvelteMainJs(targetPath: string) {
487
506
  const newContent = `import client from "../client";
488
507
  import './app.css'
489
508
  import App from './App.svelte'
509
+ import { logGreen } from '@kitql/helper'
490
510
 
491
511
  client.init();
492
512
 
@@ -494,24 +514,12 @@ const app = new App({
494
514
  target: document.getElementById('app')
495
515
  })
496
516
 
497
- export default app
498
- `
499
-
500
- const oldContent = `import './app.css'
501
- import App from './App.svelte'
502
-
503
- const app = new App({
504
- target: document.getElementById('app')
505
- })
506
-
507
517
  export default app
508
518
  `
509
519
 
510
520
  await updateFile({
511
- projectPath: targetPath,
512
521
  filepath: svelteMainJsPath,
513
522
  content: newContent,
514
- old: [oldContent],
515
523
  })
516
524
  }
517
525
 
@@ -529,7 +537,7 @@ async function updatePackageJSON(targetPath: string) {
529
537
  ...packageJSON.devDependencies,
530
538
  houdini: '^PACKAGE_VERSION',
531
539
  'houdini-svelte': '^PACKAGE_VERSION',
532
- graphql: '^15.5.0',
540
+ graphql: '^15.8.0',
533
541
  }
534
542
 
535
543
  await fs.writeFile(packagePath, JSON.stringify(packageJSON, null, 4))
@@ -550,7 +558,6 @@ async function graphqlRCFile(targetPath: string) {
550
558
  `
551
559
 
552
560
  await updateFile({
553
- projectPath: targetPath,
554
561
  filepath: target,
555
562
  content,
556
563
  })
@@ -559,13 +566,17 @@ async function graphqlRCFile(targetPath: string) {
559
566
  async function gitIgnore(targetPath: string) {
560
567
  const filepath = path.join(targetPath, '.gitignore')
561
568
  const existing = (await fs.readFile(filepath)) || ''
562
- await fs.writeFile(filepath, existing + '\n$houdini\n')
569
+
570
+ if (!existing.includes('\n$houdini\n')) {
571
+ await fs.writeFile(filepath, existing + '\n$houdini\n')
572
+ }
563
573
  }
564
574
 
565
575
  type DetectedTools = {
566
576
  typescript: boolean
567
577
  framework: 'kit' | 'sapper' | 'svelte'
568
578
  module: 'esm' | 'commonjs'
579
+ package_manager: 'npm' | 'yarn' | 'pnpm'
569
580
  }
570
581
 
571
582
  async function detectTools(cwd: string): Promise<DetectedTools> {
@@ -599,57 +610,28 @@ async function detectTools(cwd: string): Promise<DetectedTools> {
599
610
  typescript = true
600
611
  } catch {}
601
612
 
613
+ // package manager?
614
+ let package_manager: 'npm' | 'yarn' | 'pnpm' = 'npm'
615
+ let dir = cwd
616
+ do {
617
+ if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) {
618
+ package_manager = 'pnpm'
619
+ break
620
+ }
621
+ if (fs.existsSync(path.join(dir, 'yarn.lock'))) {
622
+ package_manager = 'yarn'
623
+ break
624
+ }
625
+ } while (dir !== (dir = path.dirname(dir)))
626
+
602
627
  return {
603
628
  typescript,
604
629
  framework,
605
630
  module: packageJSON['type'] === 'module' ? 'esm' : 'commonjs',
631
+ package_manager,
606
632
  }
607
633
  }
608
634
 
609
- async function updateFile({
610
- projectPath,
611
- filepath,
612
- old = [],
613
- content,
614
- }: {
615
- projectPath: string
616
- filepath: string
617
- old?: string[]
618
- content: string
619
- }) {
620
- // look up the file contents
621
- const existingContents = await fs.readFile(filepath)
622
-
623
- // compare the existing contents to the approved overwrite list
624
- if (existingContents && !old.includes(existingContents)) {
625
- // show the filepath relative to the project path
626
- const relPath = path.relative(projectPath, filepath)
627
-
628
- // show a message before we prompt their response
629
- console.log()
630
- console.log(`⚠️ ${relPath} already exists. We'd like to replace it with:
631
-
632
- ${content}`)
633
-
634
- // ask the user if we should continue
635
- const { done } = await prompts(
636
- {
637
- name: 'done',
638
- type: 'confirm',
639
- message: 'Should we overwrite the file? If not, please update it manually.',
640
- },
641
- {
642
- onCancel() {
643
- process.exit(1)
644
- },
645
- }
646
- )
647
-
648
- if (!done) {
649
- return
650
- }
651
- }
652
-
653
- // if we got this far we are safe to write the file
635
+ async function updateFile({ filepath, content }: { filepath: string; content: string }) {
654
636
  await fs.writeFile(filepath, content)
655
637
  }