houdini 0.17.9 → 0.17.11
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/README.md +33 -0
- package/build/cmd-cjs/index.js +2 -2
- package/build/cmd-esm/index.js +2 -2
- package/package.json +16 -1
- package/.turbo/turbo-compile.log +0 -5
- package/.turbo/turbo-typedefs.log +0 -5
- package/CHANGELOG.md +0 -377
- package/src/cmd/generate.ts +0 -54
- package/src/cmd/index.ts +0 -60
- package/src/cmd/init.ts +0 -637
- package/src/cmd/pullSchema.ts +0 -40
- package/src/codegen/generators/artifacts/artifacts.test.ts +0 -3246
- package/src/codegen/generators/artifacts/fieldKey.ts +0 -60
- package/src/codegen/generators/artifacts/index.ts +0 -330
- package/src/codegen/generators/artifacts/indexFile.ts +0 -24
- package/src/codegen/generators/artifacts/inputs.ts +0 -81
- package/src/codegen/generators/artifacts/operations.ts +0 -281
- package/src/codegen/generators/artifacts/pagination.test.ts +0 -664
- package/src/codegen/generators/artifacts/policy.test.ts +0 -298
- package/src/codegen/generators/artifacts/selection.ts +0 -208
- package/src/codegen/generators/artifacts/utils.test.ts +0 -118
- package/src/codegen/generators/artifacts/utils.ts +0 -108
- package/src/codegen/generators/definitions/enums.test.ts +0 -61
- package/src/codegen/generators/definitions/enums.ts +0 -68
- package/src/codegen/generators/definitions/index.ts +0 -11
- package/src/codegen/generators/definitions/schema.test.ts +0 -236
- package/src/codegen/generators/index.ts +0 -6
- package/src/codegen/generators/indexFile/index.ts +0 -63
- package/src/codegen/generators/indexFile/indexFile.test.ts +0 -72
- package/src/codegen/generators/persistedQueries/index.ts +0 -55
- package/src/codegen/generators/persistedQueries/persistedQuery.test.ts +0 -26
- package/src/codegen/generators/runtime/index.test.ts +0 -74
- package/src/codegen/generators/runtime/index.ts +0 -64
- package/src/codegen/generators/runtime/runtime.test.ts +0 -25
- package/src/codegen/generators/typescript/addReferencedInputTypes.ts +0 -77
- package/src/codegen/generators/typescript/index.ts +0 -412
- package/src/codegen/generators/typescript/inlineType.ts +0 -409
- package/src/codegen/generators/typescript/typeReference.ts +0 -44
- package/src/codegen/generators/typescript/types.ts +0 -81
- package/src/codegen/generators/typescript/typescript.test.ts +0 -1434
- package/src/codegen/index.ts +0 -406
- package/src/codegen/transforms/addID.test.ts +0 -93
- package/src/codegen/transforms/addID.ts +0 -86
- package/src/codegen/transforms/composeQueries.test.ts +0 -50
- package/src/codegen/transforms/composeQueries.ts +0 -154
- package/src/codegen/transforms/fragmentVariables.test.ts +0 -636
- package/src/codegen/transforms/fragmentVariables.ts +0 -417
- package/src/codegen/transforms/index.ts +0 -7
- package/src/codegen/transforms/list.ts +0 -484
- package/src/codegen/transforms/lists.test.ts +0 -530
- package/src/codegen/transforms/paginate.test.ts +0 -1528
- package/src/codegen/transforms/paginate.ts +0 -770
- package/src/codegen/transforms/schema.test.ts +0 -136
- package/src/codegen/transforms/schema.ts +0 -109
- package/src/codegen/transforms/typename.test.ts +0 -125
- package/src/codegen/transforms/typename.ts +0 -55
- package/src/codegen/utils/commonjs.ts +0 -26
- package/src/codegen/utils/flattenSelections.ts +0 -179
- package/src/codegen/utils/graphql.test.ts +0 -35
- package/src/codegen/utils/graphql.ts +0 -79
- package/src/codegen/utils/index.ts +0 -5
- package/src/codegen/utils/moduleExport.ts +0 -27
- package/src/codegen/utils/murmur.ts +0 -79
- package/src/codegen/validators/index.ts +0 -4
- package/src/codegen/validators/noIDAlias.test.ts +0 -71
- package/src/codegen/validators/noIDAlias.ts +0 -39
- package/src/codegen/validators/plugins.ts +0 -25
- package/src/codegen/validators/typeCheck.test.ts +0 -960
- package/src/codegen/validators/typeCheck.ts +0 -1086
- package/src/codegen/validators/uniqueNames.test.ts +0 -59
- package/src/codegen/validators/uniqueNames.ts +0 -39
- package/src/lib/cleanupFiles.ts +0 -20
- package/src/lib/config.test.ts +0 -13
- package/src/lib/config.ts +0 -954
- package/src/lib/constants.ts +0 -11
- package/src/lib/error.ts +0 -24
- package/src/lib/fs.ts +0 -285
- package/src/lib/graphql.test.ts +0 -211
- package/src/lib/graphql.ts +0 -200
- package/src/lib/imports.ts +0 -82
- package/src/lib/index.ts +0 -17
- package/src/lib/introspection.ts +0 -39
- package/src/lib/parse.test.ts +0 -75
- package/src/lib/parse.ts +0 -23
- package/src/lib/path.ts +0 -49
- package/src/lib/pipeline.ts +0 -17
- package/src/lib/types.ts +0 -34
- package/src/lib/walk.ts +0 -104
- package/src/runtime/cache/cache.ts +0 -1026
- package/src/runtime/cache/gc.ts +0 -56
- package/src/runtime/cache/index.ts +0 -3
- package/src/runtime/cache/lists.ts +0 -516
- package/src/runtime/cache/storage.ts +0 -574
- package/src/runtime/cache/stuff.ts +0 -77
- package/src/runtime/cache/subscription.ts +0 -329
- package/src/runtime/cache/tests/availability.test.ts +0 -408
- package/src/runtime/cache/tests/gc.test.ts +0 -319
- package/src/runtime/cache/tests/keys.test.ts +0 -36
- package/src/runtime/cache/tests/list.test.ts +0 -3854
- package/src/runtime/cache/tests/readwrite.test.ts +0 -1201
- package/src/runtime/cache/tests/scalars.test.ts +0 -218
- package/src/runtime/cache/tests/storage.test.ts +0 -426
- package/src/runtime/cache/tests/subscriptions.test.ts +0 -1757
- package/src/runtime/index.ts +0 -29
- package/src/runtime/lib/config.ts +0 -211
- package/src/runtime/lib/constants.ts +0 -17
- package/src/runtime/lib/deepEquals.ts +0 -32
- package/src/runtime/lib/errors.ts +0 -8
- package/src/runtime/lib/index.ts +0 -8
- package/src/runtime/lib/log.ts +0 -69
- package/src/runtime/lib/network.ts +0 -303
- package/src/runtime/lib/networkUtils.ts +0 -151
- package/src/runtime/lib/scalars.test.ts +0 -877
- package/src/runtime/lib/scalars.ts +0 -195
- package/src/runtime/lib/types.ts +0 -195
- package/src/test/index.ts +0 -294
- package/src/vite/ast.ts +0 -107
- package/src/vite/houdini.ts +0 -113
- package/src/vite/imports.ts +0 -129
- package/src/vite/index.ts +0 -55
- package/src/vite/schema.ts +0 -80
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { ExpressionKind } from 'ast-types/gen/kinds'
|
|
2
|
-
import * as graphql from 'graphql'
|
|
3
|
-
import * as recast from 'recast'
|
|
4
|
-
|
|
5
|
-
import { HoudiniError } from '../../../lib'
|
|
6
|
-
|
|
7
|
-
const AST = recast.types.builders
|
|
8
|
-
|
|
9
|
-
export function serializeValue(value: any): ExpressionKind {
|
|
10
|
-
// if we are serializing a list
|
|
11
|
-
if (Array.isArray(value)) {
|
|
12
|
-
// return an array expression with every element serialize
|
|
13
|
-
return AST.arrayExpression(value.map(serializeValue))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// if we are serializing an object
|
|
17
|
-
if (typeof value === 'object' && value !== null) {
|
|
18
|
-
return AST.objectExpression(
|
|
19
|
-
Object.entries(value)
|
|
20
|
-
.filter(([, value]) => typeof value !== 'undefined')
|
|
21
|
-
.map(([key, value]) =>
|
|
22
|
-
AST.objectProperty(AST.identifier(key), serializeValue(value))
|
|
23
|
-
)
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// if we are serializing a string
|
|
28
|
-
if (typeof value === 'string') {
|
|
29
|
-
// if there are new lines, use a template. otherwise, just use a string
|
|
30
|
-
if (value.indexOf('\n') !== -1) {
|
|
31
|
-
return AST.templateLiteral(
|
|
32
|
-
[AST.templateElement({ raw: value, cooked: value }, true)],
|
|
33
|
-
[]
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
return AST.stringLiteral(value)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// anything else can just use its literal value
|
|
40
|
-
return AST.literal(value)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function deepMerge(filepath: string, ...targets: {}[]): {} {
|
|
44
|
-
// look at the first target to know what type we're merging
|
|
45
|
-
|
|
46
|
-
// if we aren't looking at an object
|
|
47
|
-
if (typeof targets[0] !== 'object') {
|
|
48
|
-
// make sure all of the values are the same
|
|
49
|
-
const matches = targets.filter((val) => val !== targets[0]).length === 0
|
|
50
|
-
if (!matches) {
|
|
51
|
-
throw new HoudiniError({ filepath, message: 'could not merge: ' + targets })
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// return the matching value
|
|
55
|
-
return targets[0]
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// if we are looking at a list of lists
|
|
59
|
-
if (Array.isArray(targets[0])) {
|
|
60
|
-
return (targets[0] as {}[]).concat(...targets.slice(1))
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// collect all of the fields that the targets specify and map them to their value
|
|
64
|
-
const fields: Record<string, any[]> = {}
|
|
65
|
-
|
|
66
|
-
for (const target of targets) {
|
|
67
|
-
// add every field of the target to the bag
|
|
68
|
-
for (const [key, value] of Object.entries(target)) {
|
|
69
|
-
// if we haven't seen the key before
|
|
70
|
-
if (!fields[key]) {
|
|
71
|
-
// save it as a list
|
|
72
|
-
fields[key] = []
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
fields[key].push(value)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return Object.fromEntries(
|
|
80
|
-
Object.entries(fields).map(([key, value]) => [key, deepMerge(filepath, ...value)])
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function convertValue(val: graphql.ValueNode) {
|
|
85
|
-
// figure out the value to use
|
|
86
|
-
let value
|
|
87
|
-
let kind
|
|
88
|
-
|
|
89
|
-
// the value of the arg is always going to be a
|
|
90
|
-
if (val.kind === graphql.Kind.INT) {
|
|
91
|
-
value = parseInt(val.value, 10)
|
|
92
|
-
kind = 'Int'
|
|
93
|
-
} else if (val.kind === graphql.Kind.FLOAT) {
|
|
94
|
-
value = parseFloat(val.value)
|
|
95
|
-
kind = 'Float'
|
|
96
|
-
} else if (val.kind === graphql.Kind.BOOLEAN) {
|
|
97
|
-
value = val.value
|
|
98
|
-
kind = 'Boolean'
|
|
99
|
-
} else if (val.kind === graphql.Kind.VARIABLE) {
|
|
100
|
-
value = val.name.value
|
|
101
|
-
kind = 'Variable'
|
|
102
|
-
} else if (val.kind === graphql.Kind.STRING) {
|
|
103
|
-
value = val.value
|
|
104
|
-
kind = 'String'
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return { kind, value }
|
|
108
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { ProgramKind } from 'ast-types/gen/kinds'
|
|
2
|
-
import * as recast from 'recast'
|
|
3
|
-
import * as typeScriptParser from 'recast/parsers/typescript'
|
|
4
|
-
import { test, expect } from 'vitest'
|
|
5
|
-
|
|
6
|
-
import { runPipeline } from '../..'
|
|
7
|
-
import { fs, CollectedGraphQLDocument, path } from '../../../lib'
|
|
8
|
-
import { mockCollectedDoc, testConfig } from '../../../test'
|
|
9
|
-
|
|
10
|
-
// the config to use in tests
|
|
11
|
-
const config = testConfig()
|
|
12
|
-
|
|
13
|
-
// the documents to test
|
|
14
|
-
const docs: CollectedGraphQLDocument[] = [
|
|
15
|
-
mockCollectedDoc(`query TestQuery { version }`),
|
|
16
|
-
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
test('generates runtime definitions for each enum', async function () {
|
|
20
|
-
// execute the generator
|
|
21
|
-
await runPipeline(config, [])
|
|
22
|
-
|
|
23
|
-
// load the contents of the type definitions file
|
|
24
|
-
let fileContents = await fs.readFile(path.join(config.enumTypesDefinitionsPath))
|
|
25
|
-
expect(fileContents).toBeTruthy()
|
|
26
|
-
let parsedQuery: ProgramKind = recast.parse(fileContents!.toString(), {
|
|
27
|
-
parser: typeScriptParser,
|
|
28
|
-
}).program
|
|
29
|
-
|
|
30
|
-
expect(parsedQuery).toMatchInlineSnapshot(`
|
|
31
|
-
export declare enum TestEnum1 {
|
|
32
|
-
Value1 = "Value1",
|
|
33
|
-
Value2 = "Value2"
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export declare enum TestEnum2 {
|
|
37
|
-
Value3 = "Value3",
|
|
38
|
-
Value2 = "Value2"
|
|
39
|
-
}
|
|
40
|
-
`)
|
|
41
|
-
|
|
42
|
-
// load the contents of the type definitions file
|
|
43
|
-
fileContents = await fs.readFile(path.join(config.enumRuntimeDefinitionsPath))
|
|
44
|
-
|
|
45
|
-
expect(fileContents).toBeTruthy()
|
|
46
|
-
parsedQuery = recast.parse(fileContents!.toString(), {
|
|
47
|
-
parser: typeScriptParser,
|
|
48
|
-
}).program
|
|
49
|
-
|
|
50
|
-
expect(parsedQuery).toMatchInlineSnapshot(`
|
|
51
|
-
export const TestEnum1 = {
|
|
52
|
-
"Value1": "Value1",
|
|
53
|
-
"Value2": "Value2"
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const TestEnum2 = {
|
|
57
|
-
"Value3": "Value3",
|
|
58
|
-
"Value2": "Value2"
|
|
59
|
-
};
|
|
60
|
-
`)
|
|
61
|
-
})
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import * as graphql from 'graphql'
|
|
2
|
-
import * as recast from 'recast'
|
|
3
|
-
|
|
4
|
-
import { Config, fs, path } from '../../../lib'
|
|
5
|
-
import { moduleExport } from '../../utils'
|
|
6
|
-
|
|
7
|
-
const AST = recast.types.builders
|
|
8
|
-
|
|
9
|
-
// the enum generator creates runtime definitions and centralizes the type definitions in a
|
|
10
|
-
// single place to avoid conflicting exported types
|
|
11
|
-
export default async function definitionsGenerator(config: Config) {
|
|
12
|
-
// grab every enum definition in the project's schema
|
|
13
|
-
const enums = (
|
|
14
|
-
graphql
|
|
15
|
-
.parse(graphql.printSchema(config.schema))
|
|
16
|
-
.definitions.filter(
|
|
17
|
-
(definition) => definition.kind === 'EnumTypeDefinition'
|
|
18
|
-
) as graphql.EnumTypeDefinitionNode[]
|
|
19
|
-
).filter((def) => !config.isInternalEnum(def))
|
|
20
|
-
|
|
21
|
-
// generate the runtime definitions
|
|
22
|
-
const runtimeDefinitions = recast.print(
|
|
23
|
-
AST.program(
|
|
24
|
-
enums.map((defn) => {
|
|
25
|
-
const name = defn.name.value
|
|
26
|
-
|
|
27
|
-
return moduleExport(
|
|
28
|
-
config,
|
|
29
|
-
name,
|
|
30
|
-
AST.objectExpression(
|
|
31
|
-
defn.values?.map((value) => {
|
|
32
|
-
const str = value.name.value
|
|
33
|
-
return AST.objectProperty(
|
|
34
|
-
AST.stringLiteral(str),
|
|
35
|
-
AST.stringLiteral(str)
|
|
36
|
-
)
|
|
37
|
-
}) || []
|
|
38
|
-
)
|
|
39
|
-
)
|
|
40
|
-
})
|
|
41
|
-
)
|
|
42
|
-
).code
|
|
43
|
-
|
|
44
|
-
// generate the type definitions
|
|
45
|
-
const typeDefinitions = enums
|
|
46
|
-
.sort((a, b) => a.name.value.localeCompare(b.name.value))
|
|
47
|
-
.map(
|
|
48
|
-
(definition) => `
|
|
49
|
-
export declare enum ${definition.name.value} {
|
|
50
|
-
${definition.values?.map((value) => ` ${value.name.value} = "${value.name.value}"`).join(',\n')}
|
|
51
|
-
}
|
|
52
|
-
`
|
|
53
|
-
)
|
|
54
|
-
.join('')
|
|
55
|
-
|
|
56
|
-
// the index file for the definitions directory
|
|
57
|
-
const definitionsIndex = `
|
|
58
|
-
export * from './enums.js'
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
// write the typedefinition to disk
|
|
62
|
-
await Promise.all([
|
|
63
|
-
fs.writeFile(config.enumTypesDefinitionsPath, typeDefinitions),
|
|
64
|
-
fs.writeFile(config.enumRuntimeDefinitionsPath, runtimeDefinitions),
|
|
65
|
-
fs.writeFile(path.join(config.definitionsDirectory, 'index.js'), definitionsIndex),
|
|
66
|
-
fs.writeFile(path.join(config.definitionsDirectory, 'index.d.ts'), definitionsIndex),
|
|
67
|
-
])
|
|
68
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Config, fs } from '../../../lib'
|
|
2
|
-
import enums from './enums'
|
|
3
|
-
|
|
4
|
-
// schemaGenerator updates the schema file to contain all of the generated
|
|
5
|
-
export default async function schemaGenerator(config: Config) {
|
|
6
|
-
await Promise.all([
|
|
7
|
-
fs.writeFile(config.definitionsSchemaPath, config.newSchema),
|
|
8
|
-
fs.writeFile(config.definitionsDocumentsPath, config.newDocuments),
|
|
9
|
-
enums(config),
|
|
10
|
-
])
|
|
11
|
-
}
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
// external
|
|
2
|
-
import * as graphql from 'graphql'
|
|
3
|
-
import { test, expect } from 'vitest'
|
|
4
|
-
|
|
5
|
-
import { runPipeline } from '../..'
|
|
6
|
-
import { CollectedGraphQLDocument, fs } from '../../../lib'
|
|
7
|
-
import { mockCollectedDoc, testConfig } from '../../../test'
|
|
8
|
-
|
|
9
|
-
const config = testConfig()
|
|
10
|
-
|
|
11
|
-
test('adds internal documents to schema', async function () {
|
|
12
|
-
const docs: CollectedGraphQLDocument[] = [
|
|
13
|
-
mockCollectedDoc(`query TestQuery { version }`),
|
|
14
|
-
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
// execute the generator
|
|
18
|
-
await runPipeline(config, docs)
|
|
19
|
-
|
|
20
|
-
// read the schema file and make sure it got the internal documents
|
|
21
|
-
expect(graphql.parse((await fs.readFile(config.definitionsSchemaPath))!))
|
|
22
|
-
.toMatchInlineSnapshot(`
|
|
23
|
-
enum CachePolicy {
|
|
24
|
-
CacheAndNetwork
|
|
25
|
-
CacheOnly
|
|
26
|
-
CacheOrNetwork
|
|
27
|
-
NetworkOnly
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
"""
|
|
31
|
-
@list is used to mark a field for the runtime as a place to add or remove
|
|
32
|
-
entities in mutations
|
|
33
|
-
"""
|
|
34
|
-
directive @list(name: String!, connection: Boolean) on FIELD
|
|
35
|
-
|
|
36
|
-
"""
|
|
37
|
-
@paginate is used to to mark a field for pagination.
|
|
38
|
-
More info in the [doc](https://houdinigraphql.com/guides/pagination).
|
|
39
|
-
"""
|
|
40
|
-
directive @paginate(name: String) on FIELD
|
|
41
|
-
|
|
42
|
-
"""@prepend is used to tell the runtime to add the result to the end of the list"""
|
|
43
|
-
directive @prepend(parentID: ID) on FRAGMENT_SPREAD
|
|
44
|
-
|
|
45
|
-
"""@append is used to tell the runtime to add the result to the start of the list"""
|
|
46
|
-
directive @append(parentID: ID) on FRAGMENT_SPREAD
|
|
47
|
-
|
|
48
|
-
"""@allLists is used to tell the runtime to add the result to all list"""
|
|
49
|
-
directive @allLists on FRAGMENT_SPREAD
|
|
50
|
-
|
|
51
|
-
"""
|
|
52
|
-
@parentID is used to provide a parentID without specifying position or in situations
|
|
53
|
-
where it doesn't make sense (eg when deleting a node.)
|
|
54
|
-
"""
|
|
55
|
-
directive @parentID(value: ID!) on FRAGMENT_SPREAD
|
|
56
|
-
|
|
57
|
-
"""@when is used to provide a conditional or in situations where it doesn't make sense (eg when removing or deleting a node.)"""
|
|
58
|
-
directive @when on FRAGMENT_SPREAD
|
|
59
|
-
|
|
60
|
-
"""@when_not is used to provide a conditional or in situations where it doesn't make sense (eg when removing or deleting a node.)"""
|
|
61
|
-
directive @when_not on FRAGMENT_SPREAD
|
|
62
|
-
|
|
63
|
-
"""@arguments is used to define the arguments of a fragment"""
|
|
64
|
-
directive @arguments on FRAGMENT_DEFINITION
|
|
65
|
-
|
|
66
|
-
"""@cache is used to specify cache rules for a query"""
|
|
67
|
-
directive @cache(policy: CachePolicy, partial: Boolean) on QUERY
|
|
68
|
-
|
|
69
|
-
"""@houdini is used to configure houdini's internal behavior"""
|
|
70
|
-
directive @houdini(
|
|
71
|
-
"""Opt-in to an automatic load function (only valid when used at queries)"""
|
|
72
|
-
load: Boolean! = true
|
|
73
|
-
"""Mask fragment fields (only valid when used at a fragment spread)"""
|
|
74
|
-
mask: Boolean! = true
|
|
75
|
-
) on QUERY | FRAGMENT_SPREAD
|
|
76
|
-
`)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test('list operations are included', async function () {
|
|
80
|
-
const docs: CollectedGraphQLDocument[] = [
|
|
81
|
-
mockCollectedDoc(
|
|
82
|
-
`query TestQuery { usersByCursor @list(name: "Friends") { edges { node { id } } } }`
|
|
83
|
-
),
|
|
84
|
-
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
|
|
85
|
-
]
|
|
86
|
-
|
|
87
|
-
// execute the generator
|
|
88
|
-
await runPipeline(config, docs)
|
|
89
|
-
|
|
90
|
-
// read the schema file
|
|
91
|
-
expect(graphql.parse((await fs.readFile(config.definitionsSchemaPath))!))
|
|
92
|
-
.toMatchInlineSnapshot(`
|
|
93
|
-
enum CachePolicy {
|
|
94
|
-
CacheAndNetwork
|
|
95
|
-
CacheOnly
|
|
96
|
-
CacheOrNetwork
|
|
97
|
-
NetworkOnly
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
"""
|
|
101
|
-
@list is used to mark a field for the runtime as a place to add or remove
|
|
102
|
-
entities in mutations
|
|
103
|
-
"""
|
|
104
|
-
directive @list(name: String!, connection: Boolean) on FIELD
|
|
105
|
-
|
|
106
|
-
"""
|
|
107
|
-
@paginate is used to to mark a field for pagination.
|
|
108
|
-
More info in the [doc](https://houdinigraphql.com/guides/pagination).
|
|
109
|
-
"""
|
|
110
|
-
directive @paginate(name: String) on FIELD
|
|
111
|
-
|
|
112
|
-
"""@prepend is used to tell the runtime to add the result to the end of the list"""
|
|
113
|
-
directive @prepend(parentID: ID) on FRAGMENT_SPREAD
|
|
114
|
-
|
|
115
|
-
"""@append is used to tell the runtime to add the result to the start of the list"""
|
|
116
|
-
directive @append(parentID: ID) on FRAGMENT_SPREAD
|
|
117
|
-
|
|
118
|
-
"""@allLists is used to tell the runtime to add the result to all list"""
|
|
119
|
-
directive @allLists on FRAGMENT_SPREAD
|
|
120
|
-
|
|
121
|
-
"""
|
|
122
|
-
@parentID is used to provide a parentID without specifying position or in situations
|
|
123
|
-
where it doesn't make sense (eg when deleting a node.)
|
|
124
|
-
"""
|
|
125
|
-
directive @parentID(value: ID!) on FRAGMENT_SPREAD
|
|
126
|
-
|
|
127
|
-
"""@when is used to provide a conditional or in situations where it doesn't make sense (eg when removing or deleting a node.)"""
|
|
128
|
-
directive @when on FRAGMENT_SPREAD
|
|
129
|
-
|
|
130
|
-
"""@when_not is used to provide a conditional or in situations where it doesn't make sense (eg when removing or deleting a node.)"""
|
|
131
|
-
directive @when_not on FRAGMENT_SPREAD
|
|
132
|
-
|
|
133
|
-
"""@arguments is used to define the arguments of a fragment"""
|
|
134
|
-
directive @arguments on FRAGMENT_DEFINITION
|
|
135
|
-
|
|
136
|
-
"""@cache is used to specify cache rules for a query"""
|
|
137
|
-
directive @cache(policy: CachePolicy, partial: Boolean) on QUERY
|
|
138
|
-
|
|
139
|
-
"""@houdini is used to configure houdini's internal behavior"""
|
|
140
|
-
directive @houdini(
|
|
141
|
-
"""Opt-in to an automatic load function (only valid when used at queries)"""
|
|
142
|
-
load: Boolean! = true
|
|
143
|
-
"""Mask fragment fields (only valid when used at a fragment spread)"""
|
|
144
|
-
mask: Boolean! = true
|
|
145
|
-
) on QUERY | FRAGMENT_SPREAD
|
|
146
|
-
|
|
147
|
-
directive @User_delete repeatable on FIELD
|
|
148
|
-
`)
|
|
149
|
-
|
|
150
|
-
// read the documents file
|
|
151
|
-
expect(graphql.parse((await fs.readFile(config.definitionsDocumentsPath))!))
|
|
152
|
-
.toMatchInlineSnapshot(`
|
|
153
|
-
fragment Friends_insert on User {
|
|
154
|
-
id
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
fragment Friends_toggle on User {
|
|
158
|
-
id
|
|
159
|
-
id
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
fragment Friends_remove on User {
|
|
163
|
-
id
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
`)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
test("writing twice doesn't duplicate definitions", async function () {
|
|
170
|
-
const docs: CollectedGraphQLDocument[] = [
|
|
171
|
-
mockCollectedDoc(`query TestQuery { version }`),
|
|
172
|
-
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
|
|
173
|
-
]
|
|
174
|
-
|
|
175
|
-
// execute the generator twice
|
|
176
|
-
await runPipeline(config, docs)
|
|
177
|
-
await runPipeline(config, docs)
|
|
178
|
-
|
|
179
|
-
// read the schema file and make sure it got the internal documents
|
|
180
|
-
expect(graphql.parse((await fs.readFile(config.definitionsSchemaPath))!))
|
|
181
|
-
.toMatchInlineSnapshot(`
|
|
182
|
-
enum CachePolicy {
|
|
183
|
-
CacheAndNetwork
|
|
184
|
-
CacheOnly
|
|
185
|
-
CacheOrNetwork
|
|
186
|
-
NetworkOnly
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
"""
|
|
190
|
-
@list is used to mark a field for the runtime as a place to add or remove
|
|
191
|
-
entities in mutations
|
|
192
|
-
"""
|
|
193
|
-
directive @list(name: String!, connection: Boolean) on FIELD
|
|
194
|
-
|
|
195
|
-
"""
|
|
196
|
-
@paginate is used to to mark a field for pagination.
|
|
197
|
-
More info in the [doc](https://houdinigraphql.com/guides/pagination).
|
|
198
|
-
"""
|
|
199
|
-
directive @paginate(name: String) on FIELD
|
|
200
|
-
|
|
201
|
-
"""@prepend is used to tell the runtime to add the result to the end of the list"""
|
|
202
|
-
directive @prepend(parentID: ID) on FRAGMENT_SPREAD
|
|
203
|
-
|
|
204
|
-
"""@append is used to tell the runtime to add the result to the start of the list"""
|
|
205
|
-
directive @append(parentID: ID) on FRAGMENT_SPREAD
|
|
206
|
-
|
|
207
|
-
"""@allLists is used to tell the runtime to add the result to all list"""
|
|
208
|
-
directive @allLists on FRAGMENT_SPREAD
|
|
209
|
-
|
|
210
|
-
"""
|
|
211
|
-
@parentID is used to provide a parentID without specifying position or in situations
|
|
212
|
-
where it doesn't make sense (eg when deleting a node.)
|
|
213
|
-
"""
|
|
214
|
-
directive @parentID(value: ID!) on FRAGMENT_SPREAD
|
|
215
|
-
|
|
216
|
-
"""@when is used to provide a conditional or in situations where it doesn't make sense (eg when removing or deleting a node.)"""
|
|
217
|
-
directive @when on FRAGMENT_SPREAD
|
|
218
|
-
|
|
219
|
-
"""@when_not is used to provide a conditional or in situations where it doesn't make sense (eg when removing or deleting a node.)"""
|
|
220
|
-
directive @when_not on FRAGMENT_SPREAD
|
|
221
|
-
|
|
222
|
-
"""@arguments is used to define the arguments of a fragment"""
|
|
223
|
-
directive @arguments on FRAGMENT_DEFINITION
|
|
224
|
-
|
|
225
|
-
"""@cache is used to specify cache rules for a query"""
|
|
226
|
-
directive @cache(policy: CachePolicy, partial: Boolean) on QUERY
|
|
227
|
-
|
|
228
|
-
"""@houdini is used to configure houdini's internal behavior"""
|
|
229
|
-
directive @houdini(
|
|
230
|
-
"""Opt-in to an automatic load function (only valid when used at queries)"""
|
|
231
|
-
load: Boolean! = true
|
|
232
|
-
"""Mask fragment fields (only valid when used at a fragment spread)"""
|
|
233
|
-
mask: Boolean! = true
|
|
234
|
-
) on QUERY | FRAGMENT_SPREAD
|
|
235
|
-
`)
|
|
236
|
-
})
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { default as artifacts } from './artifacts'
|
|
2
|
-
export { default as runtime } from './runtime'
|
|
3
|
-
export { default as typescript } from './typescript'
|
|
4
|
-
export { default as persistOutput } from './persistedQueries'
|
|
5
|
-
export { default as definitions } from './definitions'
|
|
6
|
-
export { default as indexFile } from './indexFile'
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// locals
|
|
2
|
-
import { Config, CollectedGraphQLDocument, fs, path } from '../../../lib'
|
|
3
|
-
import { cjsIndexFilePreamble, exportStarFrom, exportDefaultFrom } from '../../utils'
|
|
4
|
-
|
|
5
|
-
// every document in the application should be re-exported from the root. this allows the user to balance
|
|
6
|
-
// code-splitting concerns with the "cleanliness" of importing from a single location
|
|
7
|
-
export default async function writeIndexFile(config: Config, docs: CollectedGraphQLDocument[]) {
|
|
8
|
-
const relative = (target: string) => './' + path.relative(config.rootDir, target)
|
|
9
|
-
|
|
10
|
-
// the directories we want to export
|
|
11
|
-
const runtimeDir = relative(config.runtimeDirectory)
|
|
12
|
-
const artifactDir = relative(config.artifactDirectory)
|
|
13
|
-
const definitionsDir = relative(config.definitionsDirectory)
|
|
14
|
-
|
|
15
|
-
// if we are rendering an index file for sapper we need to compile it for commonjs
|
|
16
|
-
const cjs = config.module === 'commonjs'
|
|
17
|
-
let body = cjs ? cjsIndexFilePreamble : ''
|
|
18
|
-
|
|
19
|
-
// create the export functions
|
|
20
|
-
const export_star_from = ({ module }: { module: string }) =>
|
|
21
|
-
'\n' + (cjs ? exportStarFrom(module) : `export * from "${module}"`) + '\n'
|
|
22
|
-
const export_default_as = ({ module, as }: { module: string; as: string }) =>
|
|
23
|
-
'\n' +
|
|
24
|
-
(cjs ? exportDefaultFrom(module, as) : `export { default as ${as} } from "${module}"`) +
|
|
25
|
-
'\n'
|
|
26
|
-
|
|
27
|
-
// add the standard exports
|
|
28
|
-
body += [
|
|
29
|
-
export_star_from({ module: runtimeDir }),
|
|
30
|
-
export_star_from({ module: artifactDir }),
|
|
31
|
-
export_star_from({ module: definitionsDir }),
|
|
32
|
-
].join('')
|
|
33
|
-
|
|
34
|
-
// plugins can influence the index file
|
|
35
|
-
for (const plugin of config.plugins) {
|
|
36
|
-
// if they need to add stuff directly (from directories that were generated)
|
|
37
|
-
if (plugin.index_file) {
|
|
38
|
-
body = plugin.index_file({
|
|
39
|
-
config,
|
|
40
|
-
content: body,
|
|
41
|
-
export_default_as,
|
|
42
|
-
export_star_from,
|
|
43
|
-
plugin_root: config.pluginDirectory(plugin.name),
|
|
44
|
-
typedef: false,
|
|
45
|
-
documents: docs,
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// if the plugin generated a runtime
|
|
50
|
-
if (plugin.include_runtime) {
|
|
51
|
-
body += export_star_from({
|
|
52
|
-
module: relative(config.pluginRuntimeDirectory(plugin.name)),
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!plugin.index_file) {
|
|
57
|
-
continue
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// write the index file that exports the runtime
|
|
62
|
-
await fs.writeFile(path.join(config.rootDir, 'index.js'), body)
|
|
63
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { ProgramKind } from 'ast-types/gen/kinds'
|
|
2
|
-
import * as recast from 'recast'
|
|
3
|
-
import * as typeScriptParser from 'recast/parsers/typescript'
|
|
4
|
-
import { test, expect } from 'vitest'
|
|
5
|
-
|
|
6
|
-
import { runPipeline } from '../..'
|
|
7
|
-
import { fs, CollectedGraphQLDocument, path } from '../../../lib'
|
|
8
|
-
import { mockCollectedDoc, testConfig } from '../../../test'
|
|
9
|
-
|
|
10
|
-
// the config to use in tests
|
|
11
|
-
const config = testConfig()
|
|
12
|
-
|
|
13
|
-
// the documents to test
|
|
14
|
-
const docs: CollectedGraphQLDocument[] = [
|
|
15
|
-
mockCollectedDoc(`query TestQuery { version }`),
|
|
16
|
-
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
test('index file - esm', async function () {
|
|
20
|
-
const config = testConfig({ module: 'esm' })
|
|
21
|
-
|
|
22
|
-
// execute the generator
|
|
23
|
-
await runPipeline(config, docs)
|
|
24
|
-
|
|
25
|
-
// open up the index file
|
|
26
|
-
const queryContents = await fs.readFile(path.join(config.artifactDirectory, 'index.js'))
|
|
27
|
-
expect(queryContents).toBeTruthy()
|
|
28
|
-
// parse the contents
|
|
29
|
-
const parsedQuery: ProgramKind = recast.parse(queryContents!, {
|
|
30
|
-
parser: typeScriptParser,
|
|
31
|
-
}).program
|
|
32
|
-
// verify contents
|
|
33
|
-
expect(parsedQuery).toMatchInlineSnapshot(`
|
|
34
|
-
export { default as TestFragment} from './TestFragment'
|
|
35
|
-
export { default as TestQuery} from './TestQuery'
|
|
36
|
-
`)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test('index file - commonjs', async function () {
|
|
40
|
-
// execute the generator
|
|
41
|
-
await runPipeline(testConfig({ module: 'commonjs' }), docs)
|
|
42
|
-
|
|
43
|
-
// open up the index file
|
|
44
|
-
const queryContents = await fs.readFile(path.join(config.artifactDirectory, 'index.js'))
|
|
45
|
-
expect(queryContents).toBeTruthy()
|
|
46
|
-
// parse the contents
|
|
47
|
-
const parsedQuery: ProgramKind = recast.parse(queryContents!, {
|
|
48
|
-
parser: typeScriptParser,
|
|
49
|
-
}).program
|
|
50
|
-
// verify contents
|
|
51
|
-
expect(parsedQuery).toMatchInlineSnapshot(`
|
|
52
|
-
"use strict";
|
|
53
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
54
|
-
if (k2 === undefined) k2 = k;
|
|
55
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
56
|
-
}) : (function(o, m, k, k2) {
|
|
57
|
-
if (k2 === undefined) k2 = k;
|
|
58
|
-
o[k2] = m[k];
|
|
59
|
-
}));
|
|
60
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
61
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
62
|
-
};
|
|
63
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
64
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
65
|
-
};
|
|
66
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
67
|
-
var TestFragment = require("./TestFragment");
|
|
68
|
-
Object.defineProperty(exports, "TestFragment", { enumerable: true, get: function () { return __importDefault(TestFragment).default; } });
|
|
69
|
-
var TestQuery = require("./TestQuery");
|
|
70
|
-
Object.defineProperty(exports, "TestQuery", { enumerable: true, get: function () { return __importDefault(TestQuery).default; } });
|
|
71
|
-
`)
|
|
72
|
-
})
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
//externals
|
|
2
|
-
import * as graphql from 'graphql'
|
|
3
|
-
|
|
4
|
-
// internals
|
|
5
|
-
import { Config, hashDocument, fs, CollectedGraphQLDocument } from '../../../lib'
|
|
6
|
-
|
|
7
|
-
// the persist output generator is responsible for generating a queryMap.json
|
|
8
|
-
// to the provided path with the `hash` as key and the raw query as value.
|
|
9
|
-
export default async function persistOutputGenerator(
|
|
10
|
-
config: Config,
|
|
11
|
-
docs: CollectedGraphQLDocument[]
|
|
12
|
-
) {
|
|
13
|
-
if (typeof config.persistedQueryPath !== 'string' || config.persistedQueryPath.length === 0)
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
if (!config.persistedQueryPath.endsWith('.json')) {
|
|
17
|
-
console.log('Can only write the queryMap to a json file')
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const queryMap = docs.reduce<Record<string, string>>((acc, { document, generateArtifact }) => {
|
|
22
|
-
// if the document is generated, just return early since there is no operation
|
|
23
|
-
if (!generateArtifact) {
|
|
24
|
-
return acc
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Strip all references to internal directives
|
|
28
|
-
let rawString = graphql.print(
|
|
29
|
-
graphql.visit(document, {
|
|
30
|
-
Directive(node) {
|
|
31
|
-
// if the directive is one of the internal ones, remove it
|
|
32
|
-
if (config.isInternalDirective(node)) {
|
|
33
|
-
return null
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
const operations = document.definitions.filter(
|
|
40
|
-
({ kind }) => kind === graphql.Kind.OPERATION_DEFINITION
|
|
41
|
-
) as graphql.OperationDefinitionNode[]
|
|
42
|
-
|
|
43
|
-
// if there are operations in the document
|
|
44
|
-
if (operations.length > 0 && operations[0].kind === 'OperationDefinition') {
|
|
45
|
-
acc[hashDocument(rawString)] = rawString
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return acc
|
|
49
|
-
}, {})
|
|
50
|
-
|
|
51
|
-
if (Object.keys(queryMap).length === 0) return
|
|
52
|
-
|
|
53
|
-
// Write the queryMap to the provided path
|
|
54
|
-
await fs.writeFile(config.persistedQueryPath, JSON.stringify(queryMap, null, 4))
|
|
55
|
-
}
|