houdini 0.17.9 → 0.17.10

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 (121) hide show
  1. package/README.md +33 -0
  2. package/build/cmd-cjs/index.js +2 -2
  3. package/build/cmd-esm/index.js +2 -2
  4. package/package.json +16 -1
  5. package/.turbo/turbo-compile.log +0 -5
  6. package/.turbo/turbo-typedefs.log +0 -5
  7. package/CHANGELOG.md +0 -377
  8. package/src/cmd/generate.ts +0 -54
  9. package/src/cmd/index.ts +0 -60
  10. package/src/cmd/init.ts +0 -637
  11. package/src/cmd/pullSchema.ts +0 -40
  12. package/src/codegen/generators/artifacts/artifacts.test.ts +0 -3246
  13. package/src/codegen/generators/artifacts/fieldKey.ts +0 -60
  14. package/src/codegen/generators/artifacts/index.ts +0 -330
  15. package/src/codegen/generators/artifacts/indexFile.ts +0 -24
  16. package/src/codegen/generators/artifacts/inputs.ts +0 -81
  17. package/src/codegen/generators/artifacts/operations.ts +0 -281
  18. package/src/codegen/generators/artifacts/pagination.test.ts +0 -664
  19. package/src/codegen/generators/artifacts/policy.test.ts +0 -298
  20. package/src/codegen/generators/artifacts/selection.ts +0 -208
  21. package/src/codegen/generators/artifacts/utils.test.ts +0 -118
  22. package/src/codegen/generators/artifacts/utils.ts +0 -108
  23. package/src/codegen/generators/definitions/enums.test.ts +0 -61
  24. package/src/codegen/generators/definitions/enums.ts +0 -68
  25. package/src/codegen/generators/definitions/index.ts +0 -11
  26. package/src/codegen/generators/definitions/schema.test.ts +0 -236
  27. package/src/codegen/generators/index.ts +0 -6
  28. package/src/codegen/generators/indexFile/index.ts +0 -63
  29. package/src/codegen/generators/indexFile/indexFile.test.ts +0 -72
  30. package/src/codegen/generators/persistedQueries/index.ts +0 -55
  31. package/src/codegen/generators/persistedQueries/persistedQuery.test.ts +0 -26
  32. package/src/codegen/generators/runtime/index.test.ts +0 -74
  33. package/src/codegen/generators/runtime/index.ts +0 -64
  34. package/src/codegen/generators/runtime/runtime.test.ts +0 -25
  35. package/src/codegen/generators/typescript/addReferencedInputTypes.ts +0 -77
  36. package/src/codegen/generators/typescript/index.ts +0 -412
  37. package/src/codegen/generators/typescript/inlineType.ts +0 -409
  38. package/src/codegen/generators/typescript/typeReference.ts +0 -44
  39. package/src/codegen/generators/typescript/types.ts +0 -81
  40. package/src/codegen/generators/typescript/typescript.test.ts +0 -1434
  41. package/src/codegen/index.ts +0 -406
  42. package/src/codegen/transforms/addID.test.ts +0 -93
  43. package/src/codegen/transforms/addID.ts +0 -86
  44. package/src/codegen/transforms/composeQueries.test.ts +0 -50
  45. package/src/codegen/transforms/composeQueries.ts +0 -154
  46. package/src/codegen/transforms/fragmentVariables.test.ts +0 -636
  47. package/src/codegen/transforms/fragmentVariables.ts +0 -417
  48. package/src/codegen/transforms/index.ts +0 -7
  49. package/src/codegen/transforms/list.ts +0 -484
  50. package/src/codegen/transforms/lists.test.ts +0 -530
  51. package/src/codegen/transforms/paginate.test.ts +0 -1528
  52. package/src/codegen/transforms/paginate.ts +0 -770
  53. package/src/codegen/transforms/schema.test.ts +0 -136
  54. package/src/codegen/transforms/schema.ts +0 -109
  55. package/src/codegen/transforms/typename.test.ts +0 -125
  56. package/src/codegen/transforms/typename.ts +0 -55
  57. package/src/codegen/utils/commonjs.ts +0 -26
  58. package/src/codegen/utils/flattenSelections.ts +0 -179
  59. package/src/codegen/utils/graphql.test.ts +0 -35
  60. package/src/codegen/utils/graphql.ts +0 -79
  61. package/src/codegen/utils/index.ts +0 -5
  62. package/src/codegen/utils/moduleExport.ts +0 -27
  63. package/src/codegen/utils/murmur.ts +0 -79
  64. package/src/codegen/validators/index.ts +0 -4
  65. package/src/codegen/validators/noIDAlias.test.ts +0 -71
  66. package/src/codegen/validators/noIDAlias.ts +0 -39
  67. package/src/codegen/validators/plugins.ts +0 -25
  68. package/src/codegen/validators/typeCheck.test.ts +0 -960
  69. package/src/codegen/validators/typeCheck.ts +0 -1086
  70. package/src/codegen/validators/uniqueNames.test.ts +0 -59
  71. package/src/codegen/validators/uniqueNames.ts +0 -39
  72. package/src/lib/cleanupFiles.ts +0 -20
  73. package/src/lib/config.test.ts +0 -13
  74. package/src/lib/config.ts +0 -954
  75. package/src/lib/constants.ts +0 -11
  76. package/src/lib/error.ts +0 -24
  77. package/src/lib/fs.ts +0 -285
  78. package/src/lib/graphql.test.ts +0 -211
  79. package/src/lib/graphql.ts +0 -200
  80. package/src/lib/imports.ts +0 -82
  81. package/src/lib/index.ts +0 -17
  82. package/src/lib/introspection.ts +0 -39
  83. package/src/lib/parse.test.ts +0 -75
  84. package/src/lib/parse.ts +0 -23
  85. package/src/lib/path.ts +0 -49
  86. package/src/lib/pipeline.ts +0 -17
  87. package/src/lib/types.ts +0 -34
  88. package/src/lib/walk.ts +0 -104
  89. package/src/runtime/cache/cache.ts +0 -1026
  90. package/src/runtime/cache/gc.ts +0 -56
  91. package/src/runtime/cache/index.ts +0 -3
  92. package/src/runtime/cache/lists.ts +0 -516
  93. package/src/runtime/cache/storage.ts +0 -574
  94. package/src/runtime/cache/stuff.ts +0 -77
  95. package/src/runtime/cache/subscription.ts +0 -329
  96. package/src/runtime/cache/tests/availability.test.ts +0 -408
  97. package/src/runtime/cache/tests/gc.test.ts +0 -319
  98. package/src/runtime/cache/tests/keys.test.ts +0 -36
  99. package/src/runtime/cache/tests/list.test.ts +0 -3854
  100. package/src/runtime/cache/tests/readwrite.test.ts +0 -1201
  101. package/src/runtime/cache/tests/scalars.test.ts +0 -218
  102. package/src/runtime/cache/tests/storage.test.ts +0 -426
  103. package/src/runtime/cache/tests/subscriptions.test.ts +0 -1757
  104. package/src/runtime/index.ts +0 -29
  105. package/src/runtime/lib/config.ts +0 -211
  106. package/src/runtime/lib/constants.ts +0 -17
  107. package/src/runtime/lib/deepEquals.ts +0 -32
  108. package/src/runtime/lib/errors.ts +0 -8
  109. package/src/runtime/lib/index.ts +0 -8
  110. package/src/runtime/lib/log.ts +0 -69
  111. package/src/runtime/lib/network.ts +0 -303
  112. package/src/runtime/lib/networkUtils.ts +0 -151
  113. package/src/runtime/lib/scalars.test.ts +0 -877
  114. package/src/runtime/lib/scalars.ts +0 -195
  115. package/src/runtime/lib/types.ts +0 -195
  116. package/src/test/index.ts +0 -294
  117. package/src/vite/ast.ts +0 -107
  118. package/src/vite/houdini.ts +0 -113
  119. package/src/vite/imports.ts +0 -129
  120. package/src/vite/index.ts +0 -55
  121. package/src/vite/schema.ts +0 -80
package/src/lib/config.ts DELETED
@@ -1,954 +0,0 @@
1
- import { mergeSchemas } from '@graphql-tools/schema'
2
- import * as graphql from 'graphql'
3
- import minimatch from 'minimatch'
4
- import type {
5
- CustomPluginOptions,
6
- LoadResult,
7
- ObjectHook,
8
- PluginContext,
9
- ResolveIdResult,
10
- } from 'rollup'
11
- import { fileURLToPath, pathToFileURL } from 'url'
12
-
13
- import { ConfigFile, CachePolicy } from '../runtime/lib'
14
- import { computeID, defaultConfigValues, keyFieldsForType } from '../runtime/lib/config'
15
- import { TransformPage } from '../vite/houdini'
16
- import { houdini_mode } from './constants'
17
- import { HoudiniError } from './error'
18
- import * as fs from './fs'
19
- import { pullSchema } from './introspection'
20
- import * as path from './path'
21
- import { CollectedGraphQLDocument } from './types'
22
-
23
- // @ts-ignore
24
- const currentDir = global.__dirname || path.dirname(fileURLToPath(import.meta.url))
25
-
26
- // a place to hold conventions and magic strings
27
- export class Config {
28
- filepath: string
29
- rootDir: string
30
- projectRoot: string
31
- schema: graphql.GraphQLSchema
32
- apiUrl?: string
33
- schemaPath?: string
34
- persistedQueryPath?: string
35
- exclude: string[]
36
- scalars?: ConfigFile['scalars']
37
- module: 'commonjs' | 'esm' = 'esm'
38
- cacheBufferSize?: number
39
- defaultCachePolicy: CachePolicy
40
- defaultPartial: boolean
41
- internalListPosition: 'first' | 'last'
42
- defaultListTarget: 'all' | null = null
43
- definitionsFolder?: string
44
- newSchema: string = ''
45
- newDocuments: string = ''
46
- defaultKeys: string[] = ['id']
47
- typeConfig: ConfigFile['types']
48
- configFile: ConfigFile
49
- logLevel: LogLevel
50
- disableMasking: boolean
51
- configIsRoute: ((filepath: string) => boolean) | null = null
52
- routesDir: string
53
- schemaPollInterval: number | null
54
- schemaPollHeaders: Record<string, string | ((env: any) => string)>
55
- pluginMode: boolean = false
56
- plugins: (Plugin & {
57
- name: string
58
- include_runtime: boolean
59
- version: string
60
- directory: string
61
- })[] = []
62
-
63
- constructor({
64
- filepath,
65
- loadFrameworkConfig,
66
- ...configFile
67
- }: ConfigFile & { filepath: string; loadFrameworkConfig?: boolean }) {
68
- this.configFile = defaultConfigValues(configFile)
69
-
70
- // apply defaults and pull out the values
71
- let {
72
- schema,
73
- schemaPath = './schema.graphql',
74
- exclude = [],
75
- apiUrl,
76
- module = 'esm',
77
- scalars,
78
- cacheBufferSize,
79
- definitionsPath,
80
- defaultCachePolicy = CachePolicy.CacheOrNetwork,
81
- defaultPartial = false,
82
- defaultListPosition = 'append',
83
- defaultListTarget = null,
84
- defaultKeys,
85
- types = {},
86
- logLevel,
87
- disableMasking = false,
88
- schemaPollInterval = 2000,
89
- schemaPollHeaders = {},
90
- projectDir,
91
- } = this.configFile
92
-
93
- // if we're given a schema string
94
- if (typeof schema === 'string') {
95
- this.schema = graphql.buildSchema(schema)
96
- } else {
97
- this.schema = schema!
98
- }
99
-
100
- // validate the log level value
101
- if (logLevel && !Object.values(LogLevel).includes(logLevel.toLowerCase() as LogLevel)) {
102
- console.warn(
103
- `⚠️ Invalid log level provided. Valid values are: ${JSON.stringify(
104
- Object.values(LogLevel)
105
- )}`
106
- )
107
- logLevel = LogLevel.Summary
108
- }
109
-
110
- // save the values we were given
111
- this.schemaPath = schemaPath
112
- this.apiUrl = apiUrl
113
- this.filepath = filepath
114
- this.exclude = Array.isArray(exclude) ? exclude : [exclude]
115
- this.module = module
116
- this.projectRoot = path.dirname(
117
- projectDir ? path.join(process.cwd(), projectDir) : filepath
118
- )
119
- this.scalars = scalars
120
- this.cacheBufferSize = cacheBufferSize
121
- this.defaultCachePolicy = defaultCachePolicy
122
- this.defaultPartial = defaultPartial
123
- this.internalListPosition = defaultListPosition === 'append' ? 'last' : 'first'
124
- this.defaultListTarget == defaultListTarget
125
- this.definitionsFolder = definitionsPath
126
- this.logLevel = ((logLevel as LogLevel) || LogLevel.Summary).toLowerCase() as LogLevel
127
- this.disableMasking = disableMasking
128
- this.routesDir = path.join(this.projectRoot, 'src', 'routes')
129
- this.schemaPollInterval = schemaPollInterval
130
- this.schemaPollHeaders = schemaPollHeaders
131
- this.rootDir = path.join(this.projectRoot, '$houdini')
132
-
133
- // hold onto the key config
134
- if (defaultKeys) {
135
- this.defaultKeys = defaultKeys
136
- }
137
- if (types) {
138
- this.typeConfig = {
139
- ...this.typeConfig,
140
- ...types,
141
- }
142
- }
143
- }
144
-
145
- get include() {
146
- // if the config file has one, use it
147
- if (this.configFile.include) {
148
- return Array.isArray(this.configFile.include)
149
- ? this.configFile.include
150
- : [this.configFile.include]
151
- }
152
-
153
- // we have to figure out a reasonable default so start with the normal extensions
154
- const extensions = ['.graphql', '.gql', '.ts', '.js'].concat(
155
- this.plugins.flatMap((plugin) => plugin.extensions ?? [])
156
- )
157
-
158
- // any file of a valid extension in src is good enough
159
- return [`src/**/*{${extensions.join(',')}}`]
160
- }
161
-
162
- pluginConfig<ConfigType extends {}>(name: string): ConfigType {
163
- // @ts-ignore
164
- return this.configFile.plugins?.[name] ?? {}
165
- }
166
-
167
- get pullHeaders() {
168
- return Object.fromEntries(
169
- Object.entries(this.schemaPollHeaders || {}).map(([key, value]) => {
170
- let headerValue
171
- if (typeof value === 'function') {
172
- headerValue = value(process.env)
173
- } else if (value.startsWith('env:')) {
174
- headerValue = process.env[value.slice('env:'.length)]
175
- } else {
176
- headerValue = value
177
- }
178
-
179
- // if there was no value, dont add anything
180
- if (!headerValue) {
181
- return []
182
- }
183
-
184
- return [key, headerValue]
185
- })
186
- )
187
- }
188
-
189
- async sourceFiles() {
190
- return [
191
- ...new Set(
192
- (
193
- await Promise.all(
194
- this.include.map((filepath) =>
195
- fs.glob(path.join(this.projectRoot, filepath))
196
- )
197
- )
198
- )
199
- .flat()
200
- .filter((filepath) => this.includeFile(filepath))
201
- // don't include the schema path as a source file
202
- .filter((filepath) => {
203
- const prefix = this.schemaPath?.startsWith('./') ? './' : ''
204
-
205
- return (
206
- !this.schemaPath ||
207
- !minimatch(
208
- prefix +
209
- path.relative(this.projectRoot, filepath).replaceAll('\\', '/'),
210
- this.schemaPath
211
- )
212
- )
213
- })
214
- ),
215
- ]
216
- }
217
-
218
- /*
219
-
220
- Directory structure
221
-
222
- */
223
-
224
- // the directory where we put all of the artifacts
225
- get artifactDirectory() {
226
- return path.join(this.rootDir, this.artifactDirectoryName)
227
- }
228
-
229
- get artifactDirectoryName() {
230
- return 'artifacts'
231
- }
232
-
233
- // the directory where artifact types live
234
- get artifactTypeDirectory() {
235
- return this.artifactDirectory
236
- }
237
-
238
- // where we will place the runtime
239
- get runtimeDirectory() {
240
- return path.join(this.rootDir, 'runtime')
241
- }
242
-
243
- // Default to => $houdini/graphql
244
- get definitionsDirectory() {
245
- return this.definitionsFolder
246
- ? path.join(this.projectRoot, this.definitionsFolder)
247
- : path.join(this.rootDir, 'graphql')
248
- }
249
-
250
- get enumRuntimeDefinitionsPath() {
251
- return path.join(this.definitionsDirectory, 'enums.js')
252
- }
253
-
254
- get enumTypesDefinitionsPath() {
255
- return path.join(this.definitionsDirectory, 'enums.d.ts')
256
- }
257
-
258
- get definitionsSchemaPath() {
259
- return path.join(this.definitionsDirectory, 'schema.graphql')
260
- }
261
-
262
- get definitionsDocumentsPath() {
263
- return path.join(this.definitionsDirectory, 'documents.gql')
264
- }
265
-
266
- get typeIndexPath() {
267
- return path.join(this.rootDir, 'index.d.ts')
268
- }
269
-
270
- get typeRootDir() {
271
- return path.join(this.rootDir, 'types')
272
- }
273
-
274
- get typeRootFile() {
275
- return '$houdini.d.ts'
276
- }
277
-
278
- findModule(
279
- pkg: string = 'houdini',
280
- currentLocation: string = path.join(path.dirname(this.filepath))
281
- ) {
282
- const pathEndingBy = ['node_modules', pkg]
283
-
284
- // Build the first possible location
285
- let locationFound = path.join(currentLocation, ...pathEndingBy)
286
-
287
- // previousLocation is nothing
288
- let previousLocation = ''
289
- const backFolder: string[] = []
290
-
291
- // if previousLocation !== locationFound that mean that we can go upper
292
- // if the directory doesn't exist, let's go upper.
293
- while (previousLocation !== locationFound && !fs.existsSync(locationFound)) {
294
- // save the previous path
295
- previousLocation = locationFound
296
-
297
- // add a back folder
298
- backFolder.push('../')
299
-
300
- // set the new location
301
- locationFound = path.join(currentLocation, ...backFolder, ...pathEndingBy)
302
- }
303
-
304
- if (previousLocation === locationFound) {
305
- throw new Error('Could not find any node_modules/houdini folder')
306
- }
307
-
308
- return locationFound
309
- }
310
-
311
- get runtimeSource() {
312
- // when running in the real world, scripts are nested in a sub directory of build, in tests they aren't nested
313
- // under /src so we need to figure out how far up to go to find the appropriately compiled runtime
314
- const relative = houdini_mode.is_testing
315
- ? path.join(currentDir, '..', '..')
316
- : // start here and go to parent until we find the node_modules/houdini folder
317
- this.findModule()
318
-
319
- const which = this.module === 'esm' ? 'esm' : 'cjs'
320
-
321
- // we want to copy the typescript source code for the templates and then compile the files according
322
- // to the requirements of the platform
323
- return path.resolve(relative, 'build', `runtime-${which}`)
324
- }
325
-
326
- artifactTypePath(document: graphql.DocumentNode) {
327
- return path.join(this.artifactTypeDirectory, `${this.documentName(document)}.d.ts`)
328
- }
329
-
330
- // the location of the artifact generated corresponding to the provided documents
331
- artifactPath(document: graphql.DocumentNode): string {
332
- // use the operation name for the artifact
333
- return path.join(this.artifactDirectory, this.documentName(document) + '.js')
334
- }
335
-
336
- // the path that the runtime can use to import an artifact
337
- artifactImportPath(name: string): string {
338
- return `$houdini/${this.artifactDirectoryName}/${name}`
339
- }
340
-
341
- keyFieldsForType(type: string) {
342
- return keyFieldsForType(this.configFile, type)
343
- }
344
-
345
- computeID(type: string, data: any): string {
346
- return computeID(this.configFile, type, data)
347
- }
348
-
349
- // a string identifier for the document (must be unique)
350
- documentName(document: graphql.DocumentNode): string {
351
- // if there is an operation in the document
352
- const operation = document.definitions.find(
353
- ({ kind }) => kind === graphql.Kind.OPERATION_DEFINITION
354
- ) as graphql.OperationDefinitionNode
355
- if (operation) {
356
- // if the operation does not have a name
357
- if (!operation.name) {
358
- // we can't give them a file
359
- throw new Error('encountered operation with no name: ' + graphql.print(document))
360
- }
361
-
362
- // use the operation name for the artifact
363
- return operation.name.value
364
- }
365
-
366
- // look for a fragment definition
367
- const fragmentDefinitions = document.definitions.filter(
368
- ({ kind }) => kind === graphql.Kind.FRAGMENT_DEFINITION
369
- ) as graphql.FragmentDefinitionNode[]
370
- if (fragmentDefinitions.length) {
371
- // join all of the fragment definitions into one
372
- return fragmentDefinitions.map((fragment) => fragment.name.value).join('_')
373
- }
374
-
375
- // we don't know how to generate a name for this document
376
- throw new Error('Could not generate artifact name for document: ' + graphql.print(document))
377
- }
378
-
379
- isSelectionScalar(type: string) {
380
- return ['String', 'Boolean', 'Float', 'ID', 'Int']
381
- .concat(Object.keys(this.scalars || {}))
382
- .includes(type)
383
- }
384
-
385
- createDirectories() {
386
- fs.mkdirpSync(this.artifactDirectory)
387
- fs.mkdirpSync(this.artifactTypeDirectory)
388
- fs.mkdirpSync(this.runtimeDirectory)
389
- fs.mkdirpSync(this.definitionsDirectory)
390
- }
391
-
392
- get compiledAssetsDir() {
393
- return path.join(this.rootDir, '.build')
394
- }
395
-
396
- compiledAssetPath(filepath: string) {
397
- return path.join(
398
- this.compiledAssetsDir,
399
- path.relative(process.cwd(), filepath).replaceAll(path.sep, '_').replace('.ts', '.js')
400
- )
401
- }
402
-
403
- includeFile(
404
- filepath: string,
405
- {
406
- root = this.projectRoot,
407
- ignore_plugins = false,
408
- }: { root?: string; ignore_plugins?: boolean } = {}
409
- ) {
410
- let included = false
411
- // plugins might define custom include logic
412
- for (const plugin of ignore_plugins ? [] : this.plugins) {
413
- if (!plugin.include) {
414
- continue
415
- }
416
-
417
- if (plugin.include(this, filepath)) {
418
- included = true
419
- break
420
- }
421
- }
422
-
423
- // if the filepath doesn't match the include we're done
424
- if (
425
- !included &&
426
- !this.include.some((pattern) => minimatch(filepath, path.join(root, pattern)))
427
- ) {
428
- return false
429
- }
430
-
431
- // if there is an exclude, make sure the path doesn't match any of the exclude patterns
432
- return (
433
- !this.exclude ||
434
- this.exclude.length === 0 ||
435
- !this.exclude.some((pattern) => minimatch(filepath, pattern))
436
- )
437
- }
438
-
439
- pluginRuntimeDirectory(name: string) {
440
- return path.join(this.pluginDirectory(name), 'runtime')
441
- }
442
-
443
- pluginDirectory(name: string) {
444
- return houdini_mode.is_testing
445
- ? path.resolve('../../../', name)
446
- : path.join(this.rootDir, 'plugins', name)
447
- }
448
-
449
- /*
450
-
451
- GraphqQL conventions
452
-
453
- */
454
-
455
- get houdiniDirective() {
456
- return 'houdini'
457
- }
458
-
459
- get listDirective() {
460
- return 'list'
461
- }
462
-
463
- get listPrependDirective() {
464
- return 'prepend'
465
- }
466
-
467
- get listAppendDirective() {
468
- return 'append'
469
- }
470
-
471
- get listParentDirective() {
472
- return this.listDirectiveParentIDArg
473
- }
474
-
475
- get listDirectiveParentIDArg() {
476
- return 'parentID'
477
- }
478
-
479
- get listAllListsDirective() {
480
- return 'allLists'
481
- }
482
-
483
- get listNameArg() {
484
- return 'name'
485
- }
486
-
487
- get insertFragmentSuffix() {
488
- return `_insert`
489
- }
490
-
491
- get removeFragmentSuffix() {
492
- return `_remove`
493
- }
494
-
495
- get toggleFragmentSuffix() {
496
- return `_toggle`
497
- }
498
-
499
- get deleteDirectiveSuffix() {
500
- return `_delete`
501
- }
502
-
503
- get whenDirective() {
504
- return 'when'
505
- }
506
-
507
- get whenNotDirective() {
508
- return this.whenDirective + '_not'
509
- }
510
-
511
- get argumentsDirective() {
512
- return 'arguments'
513
- }
514
-
515
- get withDirective() {
516
- return 'with'
517
- }
518
-
519
- get paginateDirective() {
520
- return 'paginate'
521
- }
522
-
523
- get paginateNameArg() {
524
- return 'name'
525
- }
526
-
527
- get cacheDirective() {
528
- return 'cache'
529
- }
530
-
531
- get cachePartialArg() {
532
- return 'partial'
533
- }
534
-
535
- get cachePolicyArg() {
536
- return 'policy'
537
- }
538
-
539
- paginationQueryName(documentName: string) {
540
- return documentName + '_Pagination_Query'
541
- }
542
-
543
- isDeleteDirective(name: string) {
544
- return name.endsWith(this.deleteDirectiveSuffix)
545
- }
546
-
547
- listDeleteDirective(name: string): string {
548
- return name + this.deleteDirectiveSuffix
549
- }
550
-
551
- deleteDirectiveType(name: string) {
552
- return name.slice(0, name.length - this.deleteDirectiveSuffix.length)
553
- }
554
-
555
- isInsertFragment(name: string) {
556
- return name.endsWith(this.insertFragmentSuffix)
557
- }
558
-
559
- listInsertFragment(name: string): string {
560
- return name + this.insertFragmentSuffix
561
- }
562
-
563
- listToggleFragment(name: string): string {
564
- return name + this.toggleFragmentSuffix
565
- }
566
-
567
- isRemoveFragment(name: string) {
568
- return name.endsWith(this.removeFragmentSuffix)
569
- }
570
-
571
- isToggleFragment(name: string) {
572
- return name.endsWith(this.toggleFragmentSuffix)
573
- }
574
-
575
- listRemoveFragment(name: string): string {
576
- return name + this.removeFragmentSuffix
577
- }
578
-
579
- isInternalEnum(node: graphql.EnumTypeDefinitionNode): boolean {
580
- // if we are looking at an enum, it could be CachePolicy
581
- return node.name.value === 'CachePolicy'
582
- }
583
-
584
- isInternalDirective({ name }: graphql.DirectiveNode): boolean {
585
- return (
586
- [
587
- this.listDirective,
588
- this.listPrependDirective,
589
- this.listAppendDirective,
590
- this.listDirectiveParentIDArg,
591
- this.listAllListsDirective,
592
- this.whenDirective,
593
- this.whenNotDirective,
594
- this.argumentsDirective,
595
- this.withDirective,
596
- this.paginateDirective,
597
- this.cacheDirective,
598
- this.houdiniDirective,
599
- ].includes(name.value) || this.isDeleteDirective(name.value)
600
- )
601
- }
602
-
603
- isListFragment(name: string): boolean {
604
- return (
605
- name.endsWith(this.insertFragmentSuffix) ||
606
- name.endsWith(this.removeFragmentSuffix) ||
607
- name.endsWith(this.toggleFragmentSuffix)
608
- )
609
- }
610
-
611
- isListOperationDirective(name: string): boolean {
612
- return name.endsWith(this.deleteDirectiveSuffix)
613
- }
614
-
615
- isFragmentForList(listName: string, fragmentName: string) {
616
- return fragmentName.startsWith(listName)
617
- }
618
-
619
- // return 'insert' for All_Users_insert
620
- listOperationFromFragment(fragmentName: string): 'insert' | 'remove' | 'toggle' {
621
- // check the name against the fragment patterns
622
- if (this.isInsertFragment(fragmentName)) {
623
- return 'insert'
624
- } else if (this.isRemoveFragment(fragmentName)) {
625
- return 'remove'
626
- } else if (this.isToggleFragment(fragmentName)) {
627
- return 'toggle'
628
- }
629
-
630
- throw new Error('Could not determine list operation from fragment name: ' + fragmentName)
631
- }
632
-
633
- listNameFromDirective(directiveName: string): string {
634
- try {
635
- return this.listNameFromFragment(directiveName)
636
- } catch (e) {
637
- throw new Error('Could not find list name from directive: ' + directiveName)
638
- }
639
- }
640
-
641
- listNameFromFragment(fragmentName: string): string {
642
- // starting at the end of the fragment name going left, look for a _
643
- for (let i = fragmentName.length - 1; i >= 0; i--) {
644
- // if we hit a _
645
- if (fragmentName[i] === '_') {
646
- return fragmentName.slice(0, i)
647
- }
648
- }
649
-
650
- throw new Error('Could not find list name from fragment: ' + fragmentName)
651
- }
652
-
653
- extractDefinition(document: graphql.DocumentNode): graphql.ExecutableDefinitionNode {
654
- // make sure there's only one definition
655
- if (document.definitions.length !== 1) {
656
- throw new Error('Encountered document with multiple definitions')
657
- }
658
-
659
- // get the definition
660
- const definition = document.definitions[0]
661
-
662
- // make sure that it's an operation definition or a fragment definition
663
- if (definition.kind !== 'OperationDefinition' && definition.kind !== 'FragmentDefinition') {
664
- throw new Error('Encountered document without a fragment or operation definition')
665
- }
666
-
667
- return definition
668
- }
669
-
670
- extractQueryDefinition(document: graphql.DocumentNode): graphql.OperationDefinitionNode {
671
- const definition = this.extractDefinition(document)
672
- if (definition.kind !== 'OperationDefinition' || definition.operation !== 'query') {
673
- throw new Error('Encountered document with non query definition')
674
- }
675
-
676
- return definition
677
- }
678
-
679
- variableFunctionName(name: string) {
680
- return name + 'Variables'
681
- }
682
- }
683
-
684
- const DEFAULT_CONFIG_PATH = path.join(process.cwd(), 'houdini.config.js')
685
-
686
- // helper function to load the config file
687
- export async function readConfigFile(
688
- configPath: string = DEFAULT_CONFIG_PATH
689
- ): Promise<ConfigFile> {
690
- // on windows, we need to prepend the right protocol before we
691
- // can import from an absolute path
692
- let importPath = path.importPath(configPath)
693
-
694
- let imported: any
695
- try {
696
- imported = await import(importPath)
697
- } catch (e: any) {
698
- throw new Error(`Could not load config file at file://${configPath}.\n${e.message}`)
699
- }
700
-
701
- // if this is wrapped in a default, use it
702
- const config = imported.default || imported
703
- return config
704
- }
705
-
706
- // a place to store the current configuration
707
- let _config: Config
708
-
709
- async function loadSchemaFile(schemaPath: string): Promise<graphql.GraphQLSchema> {
710
- // if the schema is not a relative path, the config file is out of date
711
- if (path.isAbsolute(schemaPath)) {
712
- // compute the new value for schema
713
- const relPath = path.relative(process.cwd(), schemaPath)
714
-
715
- // build up an error with no stack trace so the message isn't so noisy
716
- const error = new Error(
717
- `Invalid config value: 'schemaPath' must now be passed as a relative directory. Please change ` +
718
- `its value to "./${relPath}".`
719
- )
720
- error.stack = ''
721
-
722
- // don't let anything continue
723
- throw error
724
- }
725
-
726
- // if the path is a glob, load each file
727
- if (fs.glob.hasMagic(schemaPath)) {
728
- // the first step we have to do is grab a list of every file in the source tree
729
- const sourceFiles = await fs.glob(schemaPath)
730
-
731
- return mergeSchemas({
732
- typeDefs: await Promise.all(
733
- sourceFiles.map(async (filepath) => (await fs.readFile(filepath))!)
734
- ),
735
- })
736
- }
737
-
738
- // the path has no glob magic, make sure its a real file
739
- try {
740
- await fs.stat(schemaPath)
741
- } catch {
742
- throw new HoudiniError({
743
- message: `Schema file does not exist! Create it using houdini pull-schema`,
744
- })
745
- }
746
-
747
- const contents = (await fs.readFile(schemaPath))!
748
-
749
- // if the schema points to an sdl file
750
- if (schemaPath.endsWith('gql') || schemaPath.endsWith('graphql')) {
751
- return graphql.buildSchema(contents)
752
- }
753
-
754
- // the schema must point to a json blob (with data level or content of data directly)
755
- const jsonContents = JSON.parse(contents)
756
- if (jsonContents.data) {
757
- return graphql.buildClientSchema(jsonContents.data)
758
- }
759
- return graphql.buildClientSchema(jsonContents)
760
- }
761
-
762
- // if multiple calls to getConfig happen simultaneously, we want to only load the
763
- // schema once (if it needs to happen, ie the file doesn't exist).
764
- let pendingConfigPromise: Promise<Config> | null = null
765
-
766
- // get the project's current configuration
767
- export async function getConfig({
768
- configPath = DEFAULT_CONFIG_PATH,
769
- noSchema,
770
- ...extraConfig
771
- }: PluginConfig & { noSchema?: boolean } = {}): Promise<Config> {
772
- if (_config) {
773
- return _config
774
- }
775
-
776
- // if we have a pending promise, return the result of that
777
- if (pendingConfigPromise) {
778
- return await pendingConfigPromise
779
- }
780
-
781
- // there isn't a pending config so let's make one to claim
782
- let resolve: (cfg: Config | PromiseLike<Config>) => void = () => {}
783
- let reject = (message?: any) => {}
784
- pendingConfigPromise = new Promise((res, rej) => {
785
- resolve = res
786
- reject = rej
787
- })
788
-
789
- // look up the current config file
790
- let configFile = await readConfigFile(configPath)
791
-
792
- // if there is a framework specified, tell them they need to change things
793
- if (!configFile.plugins) {
794
- throw new HoudiniError({
795
- message:
796
- 'Welcome to 0.17.0! Please following the migration guide here: http://www.houdinigraphql.com/guides/release-notes#0170',
797
- })
798
- }
799
-
800
- try {
801
- _config = new Config({
802
- ...configFile,
803
- ...extraConfig,
804
- filepath: configPath,
805
- })
806
-
807
- // look up the schema if we need to
808
- if (_config.schemaPath && !_config.schema) {
809
- let schemaOk = true
810
- // we might have to pull the schema first
811
- if (_config.apiUrl) {
812
- // make sure we don't have a pattern pointing to multiple files and a remove URL
813
- if (fs.glob.hasMagic(_config.schemaPath)) {
814
- console.log(
815
- `⚠️ Your houdini configuration contains an apiUrl and a path pointing to multiple files.
816
- This will prevent your schema from being pulled.`
817
- )
818
- }
819
- // we might have to create the file
820
- else if (!(await fs.readFile(_config.schemaPath))) {
821
- console.log('⌛ Pulling schema from api')
822
- schemaOk = await pullSchema(_config.apiUrl, _config.schemaPath)
823
- }
824
- }
825
-
826
- // the schema is safe to load
827
- if (schemaOk && !noSchema) {
828
- _config.schema = await loadSchemaFile(_config.schemaPath)
829
- }
830
- }
831
- } catch (e) {
832
- reject(e)
833
- throw e
834
- }
835
-
836
- // load the specified plugins
837
- for (const [pluginName, plugin_config] of Object.entries(_config.configFile.plugins ?? {})) {
838
- try {
839
- // look for the houdini-svelte module
840
- const pluginDirectory = _config.findModule(pluginName)
841
- const { default: sveltePlugin }: { default: PluginFactory } = await import(
842
- pathToFileURL(pluginDirectory).toString() + '/build/plugin-esm/index.js'
843
- )
844
- let include_runtime = false
845
- try {
846
- await fs.stat(path.join(pluginDirectory, 'build', 'runtime-esm'))
847
- include_runtime = true
848
- } catch {}
849
-
850
- // figure out the current version
851
- let version = ''
852
- try {
853
- const packageJsonSrc = await fs.readFile(path.join(pluginDirectory, 'package.json'))
854
- if (!packageJsonSrc) {
855
- throw new Error('skip')
856
- }
857
- const packageJSON = JSON.parse(packageJsonSrc)
858
- version = packageJSON.version
859
- } catch {}
860
-
861
- // add the plugin to the list
862
- _config.plugins.push({
863
- ...(await sveltePlugin(plugin_config)),
864
- name: pluginName,
865
- include_runtime,
866
- version,
867
- directory: pluginDirectory,
868
- })
869
- } catch (e) {
870
- throw new Error(
871
- `Could not find plugin: ${pluginName}. Are you sure its installed? If so, please open a ticket on GitHub.`
872
- )
873
- }
874
- }
875
-
876
- // look for any plugins with a loaded hook
877
- await Promise.all(_config.plugins.map((plugin) => plugin.after_load?.(_config)))
878
-
879
- // we're done and have a valid config
880
- resolve(_config)
881
- return _config
882
- }
883
-
884
- export enum LogLevel {
885
- Full = 'full',
886
- Summary = 'summary',
887
- ShortSummary = 'short-summary',
888
- Quiet = 'quiet',
889
- }
890
-
891
- export type PluginFactory = (args?: PluginConfig) => Promise<Plugin>
892
-
893
- export type Plugin = {
894
- extensions?: string[]
895
- transform_runtime?: Record<string, (args: { config: Config; content: string }) => string>
896
- after_load?: (config: Config) => Promise<void> | void
897
- extract_documents?: (filepath: string, content: string) => Promise<string[]> | string[]
898
- generate?: GenerateHook
899
- transform_file?: (page: TransformPage) => Promise<{ code: string }> | { code: string }
900
- index_file?: ModuleIndexTransform
901
- validate?: (args: {
902
- config: Config
903
- documents: CollectedGraphQLDocument[]
904
- }) => Promise<void> | void
905
- vite?: {
906
- // these type definitions are copy and pasted from the vite ones
907
- // with config added to the appropriate options object
908
- resolveId?: ObjectHook<
909
- (
910
- this: PluginContext,
911
- source: string,
912
- importer: string | undefined,
913
- options: {
914
- config: Config
915
- custom?: CustomPluginOptions
916
- ssr?: boolean
917
- /* Excluded from this release type: scan */
918
- isEntry: boolean
919
- }
920
- ) => Promise<ResolveIdResult> | ResolveIdResult
921
- >
922
- load?: ObjectHook<
923
- (
924
- this: PluginContext,
925
- id: string,
926
- options: {
927
- config: Config
928
- ssr?: boolean
929
- }
930
- ) => Promise<LoadResult> | LoadResult
931
- >
932
- }
933
- include?: (config: Config, filepath: string) => boolean | null | undefined
934
- }
935
-
936
- type ModuleIndexTransform = (arg: {
937
- config: Config
938
- content: string
939
- export_default_as(args: { module: string; as: string }): string
940
- export_star_from(args: { module: string }): string
941
- plugin_root: string
942
- typedef: boolean
943
- documents: CollectedGraphQLDocument[]
944
- }) => string
945
-
946
- export type GenerateHook = (args: GenerateHookInput) => Promise<void> | void
947
-
948
- export type GenerateHookInput = {
949
- config: Config
950
- documents: CollectedGraphQLDocument[]
951
- plugin_root: string
952
- }
953
-
954
- export type PluginConfig = { configPath?: string } & Partial<ConfigFile>