houdini 0.17.8 → 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.
- package/README.md +33 -0
- package/build/cmd-cjs/index.js +124 -38
- package/build/cmd-esm/index.js +124 -38
- package/build/codegen-cjs/index.js +112 -36
- package/build/codegen-esm/index.js +112 -36
- package/build/lib/config.d.ts +3 -0
- package/build/lib-cjs/index.js +31 -12
- package/build/lib-esm/index.js +31 -12
- package/build/runtime/cache/cache.d.ts +1 -1
- package/build/runtime/cache/lists.d.ts +1 -1
- package/build/runtime/lib/config.d.ts +10 -2
- package/build/runtime/lib/types.d.ts +1 -0
- package/build/runtime-cjs/cache/cache.d.ts +1 -1
- package/build/runtime-cjs/cache/cache.js +6 -6
- package/build/runtime-cjs/cache/lists.d.ts +1 -1
- package/build/runtime-cjs/cache/lists.js +15 -6
- package/build/runtime-cjs/cache/tests/list.test.js +160 -70
- package/build/runtime-cjs/lib/config.d.ts +10 -2
- package/build/runtime-cjs/lib/types.d.ts +1 -0
- package/build/runtime-esm/cache/cache.d.ts +1 -1
- package/build/runtime-esm/cache/cache.js +6 -6
- package/build/runtime-esm/cache/lists.d.ts +1 -1
- package/build/runtime-esm/cache/lists.js +15 -6
- package/build/runtime-esm/cache/tests/list.test.js +160 -70
- package/build/runtime-esm/lib/config.d.ts +10 -2
- package/build/runtime-esm/lib/types.d.ts +1 -0
- package/build/test-cjs/index.js +122 -36
- package/build/test-esm/index.js +122 -36
- package/build/vite-cjs/index.js +122 -36
- package/build/vite-esm/index.js +122 -36
- package/package.json +16 -1
- package/.turbo/turbo-compile.log +0 -5
- package/.turbo/turbo-typedefs.log +0 -5
- package/CHANGELOG.md +0 -367
- 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 -2978
- 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 -263
- 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 -227
- 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 -485
- package/src/codegen/transforms/lists.test.ts +0 -530
- package/src/codegen/transforms/paginate.test.ts +0 -1481
- package/src/codegen/transforms/paginate.ts +0 -750
- package/src/codegen/transforms/schema.test.ts +0 -136
- package/src/codegen/transforms/schema.ts +0 -104
- 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 -904
- package/src/codegen/validators/typeCheck.ts +0 -1031
- 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 -943
- 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 -1023
- package/src/runtime/cache/gc.ts +0 -56
- package/src/runtime/cache/index.ts +0 -3
- package/src/runtime/cache/lists.ts +0 -502
- 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 -3747
- 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 -201
- 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 -194
- 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,26 +0,0 @@
|
|
|
1
|
-
import { test, expect } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { runPipeline } from '../../../codegen'
|
|
4
|
-
import { CollectedGraphQLDocument, fs, path } from '../../../lib'
|
|
5
|
-
import { mockCollectedDoc, testConfig } from '../../../test'
|
|
6
|
-
|
|
7
|
-
test('generates an artifact for every document', async function () {
|
|
8
|
-
const config = testConfig()
|
|
9
|
-
config.persistedQueryPath = path.join(config.rootDir, 'hash.json')
|
|
10
|
-
|
|
11
|
-
// the documents to test
|
|
12
|
-
const docs: CollectedGraphQLDocument[] = [
|
|
13
|
-
mockCollectedDoc(`query TestQuery1 { version }`),
|
|
14
|
-
mockCollectedDoc(`query TestQuery2 { user { ...TestFragment } }`),
|
|
15
|
-
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
|
|
16
|
-
]
|
|
17
|
-
// execute the generator
|
|
18
|
-
await runPipeline(config, docs)
|
|
19
|
-
|
|
20
|
-
expect(JSON.parse((await fs.readFile(config.persistedQueryPath))!)).toMatchInlineSnapshot(`
|
|
21
|
-
{
|
|
22
|
-
"361432f464ed44eed788f3ea66c4dabc46437b88edbe7daccca87045fd31447f": "query TestQuery1 {\\n version\\n}\\n",
|
|
23
|
-
"17f12389123502b3d5d81202d0af249bdf0ec95cea480c9c12501ef627abd463": "query TestQuery2 {\\n user {\\n ...TestFragment\\n id\\n }\\n}\\n\\nfragment TestFragment on User {\\n firstName\\n}\\n"
|
|
24
|
-
}
|
|
25
|
-
`)
|
|
26
|
-
})
|
|
@@ -1,74 +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, path } from '../../../lib'
|
|
8
|
-
import { CollectedGraphQLDocument } from '../../../lib/types'
|
|
9
|
-
import { testConfig, mockCollectedDoc } from '../../../test'
|
|
10
|
-
|
|
11
|
-
// the documents to test
|
|
12
|
-
const docs: CollectedGraphQLDocument[] = [
|
|
13
|
-
mockCollectedDoc(`query TestQuery { version }`),
|
|
14
|
-
mockCollectedDoc(`fragment TestFragment on User { firstName }`),
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
test('runtime index file - sapper', async function () {
|
|
18
|
-
const config = testConfig({ module: 'commonjs' })
|
|
19
|
-
// execute the generator
|
|
20
|
-
await runPipeline(config, docs)
|
|
21
|
-
|
|
22
|
-
// open up the index file
|
|
23
|
-
const queryContents = await fs.readFile(path.join(config.rootDir, 'index.js'))
|
|
24
|
-
expect(queryContents).toBeTruthy()
|
|
25
|
-
// parse the contents
|
|
26
|
-
const parsedQuery: ProgramKind = recast.parse(queryContents!, {
|
|
27
|
-
parser: typeScriptParser,
|
|
28
|
-
}).program
|
|
29
|
-
// verify contents
|
|
30
|
-
expect(parsedQuery).toMatchInlineSnapshot(`
|
|
31
|
-
"use strict";
|
|
32
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
33
|
-
if (k2 === undefined) k2 = k;
|
|
34
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
35
|
-
}) : (function(o, m, k, k2) {
|
|
36
|
-
if (k2 === undefined) k2 = k;
|
|
37
|
-
o[k2] = m[k];
|
|
38
|
-
}));
|
|
39
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
40
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
41
|
-
};
|
|
42
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
43
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
44
|
-
};
|
|
45
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
__exportStar(require("./runtime"), exports);
|
|
47
|
-
|
|
48
|
-
__exportStar(require("./artifacts"), exports);
|
|
49
|
-
|
|
50
|
-
__exportStar(require("./graphql"), exports);
|
|
51
|
-
`)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('runtime index file - kit', async function () {
|
|
55
|
-
const config = testConfig({ module: 'esm', framework: 'kit' })
|
|
56
|
-
// execute the generator
|
|
57
|
-
await runPipeline(config, docs)
|
|
58
|
-
|
|
59
|
-
// open up the index file
|
|
60
|
-
const queryContents = await fs.readFile(path.join(config.rootDir, 'index.js'))
|
|
61
|
-
expect(queryContents).toBeTruthy()
|
|
62
|
-
// parse the contents
|
|
63
|
-
const parsedQuery: ProgramKind = recast.parse(queryContents!, {
|
|
64
|
-
parser: typeScriptParser,
|
|
65
|
-
}).program
|
|
66
|
-
// verify contents
|
|
67
|
-
expect(parsedQuery).toMatchInlineSnapshot(`
|
|
68
|
-
export * from "./runtime"
|
|
69
|
-
|
|
70
|
-
export * from "./artifacts"
|
|
71
|
-
|
|
72
|
-
export * from "./graphql"
|
|
73
|
-
`)
|
|
74
|
-
})
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { Config, siteURL as SITE_URL, fs, HoudiniError, path, houdini_mode } from '../../../lib'
|
|
2
|
-
|
|
3
|
-
export default async function runtimeGenerator(config: Config) {
|
|
4
|
-
// generate the adapter to normalize interactions with the framework
|
|
5
|
-
// update the generated runtime to point to the client
|
|
6
|
-
await Promise.all([
|
|
7
|
-
fs.recursiveCopy(config.runtimeSource, config.runtimeDirectory, {
|
|
8
|
-
// transform the files while we are copying so we don't trigger unnecessary changes
|
|
9
|
-
[path.join(config.runtimeSource, 'lib', 'config.js')]: (content) => {
|
|
10
|
-
// the path to the config file
|
|
11
|
-
const configFilePath = path.join(config.runtimeDirectory, 'lib', 'config.js')
|
|
12
|
-
// the relative path
|
|
13
|
-
const relativePath = path.relative(path.dirname(configFilePath), config.filepath)
|
|
14
|
-
|
|
15
|
-
return content.replace('HOUDINI_CONFIG_PATH', relativePath)
|
|
16
|
-
},
|
|
17
|
-
[path.join(config.runtimeSource, 'lib', 'constants.js')]: (content) => {
|
|
18
|
-
return content.replace('SITE_URL', SITE_URL)
|
|
19
|
-
},
|
|
20
|
-
}),
|
|
21
|
-
...config.plugins
|
|
22
|
-
.filter((plugin) => plugin.include_runtime)
|
|
23
|
-
.map((plugin) => generatePluginRuntime(config, plugin)),
|
|
24
|
-
])
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function generatePluginRuntime(config: Config, plugin: Config['plugins'][number]) {
|
|
28
|
-
if (houdini_mode.is_testing) {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// a plugin with a generated runtime has something at <dir>/build/runtime-{esm,cjs}
|
|
33
|
-
|
|
34
|
-
// find the location of the plugin
|
|
35
|
-
const source = path.join(
|
|
36
|
-
plugin.directory,
|
|
37
|
-
'build',
|
|
38
|
-
'runtime-' + (config.module === 'esm' ? 'esm' : 'cjs')
|
|
39
|
-
)
|
|
40
|
-
try {
|
|
41
|
-
await fs.stat(source)
|
|
42
|
-
} catch {
|
|
43
|
-
throw new HoudiniError({
|
|
44
|
-
message: name + ' does not have a runtime to generate',
|
|
45
|
-
description: 'please use the houdini-scripts command to bundle your plugin',
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const which = config.module === 'esm' ? 'esm' : 'cjs'
|
|
50
|
-
|
|
51
|
-
// copy the runtime
|
|
52
|
-
const pluginDir = config.pluginRuntimeDirectory(plugin.name)
|
|
53
|
-
await fs.mkdirp(pluginDir)
|
|
54
|
-
await fs.recursiveCopy(
|
|
55
|
-
source,
|
|
56
|
-
pluginDir,
|
|
57
|
-
Object.fromEntries(
|
|
58
|
-
Object.entries(plugin.transform_runtime ?? {}).map(([key, value]) => [
|
|
59
|
-
path.join(plugin.directory, 'build', `runtime-${which}`, key),
|
|
60
|
-
(content) => value({ config, content }),
|
|
61
|
-
])
|
|
62
|
-
)
|
|
63
|
-
)
|
|
64
|
-
}
|
|
@@ -1,25 +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, path } from '../../../lib'
|
|
8
|
-
import { testConfig } from '../../../test'
|
|
9
|
-
|
|
10
|
-
test('updates the config file with import path', async function () {
|
|
11
|
-
const config = testConfig({ module: 'esm' })
|
|
12
|
-
// execute the generator
|
|
13
|
-
await runPipeline(config, [])
|
|
14
|
-
|
|
15
|
-
// open up the index file
|
|
16
|
-
const fileContents = await fs.readFile(path.join(config.runtimeDirectory, 'lib', 'config.js'))
|
|
17
|
-
expect(fileContents).toBeTruthy()
|
|
18
|
-
|
|
19
|
-
// parse the contents
|
|
20
|
-
const parsedQuery: ProgramKind = recast.parse(fileContents!, {
|
|
21
|
-
parser: typeScriptParser,
|
|
22
|
-
}).program
|
|
23
|
-
// verify contents
|
|
24
|
-
expect(recast.print(parsedQuery).code).toContain('import("../../../config.cjs")')
|
|
25
|
-
})
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { StatementKind, TSPropertySignatureKind } from 'ast-types/gen/kinds'
|
|
2
|
-
import * as graphql from 'graphql'
|
|
3
|
-
import * as recast from 'recast'
|
|
4
|
-
|
|
5
|
-
import { Config, ensureImports, HoudiniError } from '../../../lib'
|
|
6
|
-
import { unwrapType } from '../../utils'
|
|
7
|
-
import { tsTypeReference } from './typeReference'
|
|
8
|
-
|
|
9
|
-
const AST = recast.types.builders
|
|
10
|
-
|
|
11
|
-
// add any object types found in the input
|
|
12
|
-
export function addReferencedInputTypes(
|
|
13
|
-
config: Config,
|
|
14
|
-
filepath: string,
|
|
15
|
-
body: StatementKind[],
|
|
16
|
-
visitedTypes: Set<string>,
|
|
17
|
-
missingScalars: Set<string>,
|
|
18
|
-
rootType: graphql.TypeNode
|
|
19
|
-
) {
|
|
20
|
-
// try to find the name of the type
|
|
21
|
-
const { type } = unwrapType(config, rootType)
|
|
22
|
-
|
|
23
|
-
// if we are looking at a scalar
|
|
24
|
-
if (graphql.isScalarType(type)) {
|
|
25
|
-
// we're done
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// if we have already processed this type, don't do anything
|
|
30
|
-
if (visitedTypes.has(type.name)) {
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// if we ran into a union
|
|
35
|
-
if (graphql.isUnionType(type)) {
|
|
36
|
-
// we don't support them yet
|
|
37
|
-
throw new HoudiniError({ filepath, message: 'Input Unions are not supported yet. Sorry!' })
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// track that we are processing the type
|
|
41
|
-
visitedTypes.add(type.name)
|
|
42
|
-
|
|
43
|
-
// if we ran into an enum, add its definition to the file
|
|
44
|
-
if (graphql.isEnumType(type)) {
|
|
45
|
-
// we need to add an import for the enum
|
|
46
|
-
ensureImports({
|
|
47
|
-
config,
|
|
48
|
-
// @ts-ignore
|
|
49
|
-
body,
|
|
50
|
-
import: [type.name],
|
|
51
|
-
sourceModule: '$houdini/graphql/enums',
|
|
52
|
-
importKind: 'type',
|
|
53
|
-
})
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// we found an object type so build up the list of fields (and walk down any object fields)
|
|
58
|
-
const members: TSPropertySignatureKind[] = []
|
|
59
|
-
|
|
60
|
-
for (const field of Object.values(type.getFields())) {
|
|
61
|
-
// walk down the referenced fields and build stuff back up
|
|
62
|
-
addReferencedInputTypes(config, filepath, body, visitedTypes, missingScalars, field.type)
|
|
63
|
-
|
|
64
|
-
// check if the type is optional so we can label the value as optional
|
|
65
|
-
|
|
66
|
-
members.push(
|
|
67
|
-
AST.tsPropertySignature(
|
|
68
|
-
AST.identifier(field.name),
|
|
69
|
-
AST.tsTypeAnnotation(tsTypeReference(config, missingScalars, field)),
|
|
70
|
-
graphql.isNullableType(field.type)
|
|
71
|
-
)
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// add the type def to the body
|
|
76
|
-
body.push(AST.tsTypeAliasDeclaration(AST.identifier(type.name), AST.tsTypeLiteral(members)))
|
|
77
|
-
}
|
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
import { logCyan, logGreen } from '@kitql/helper'
|
|
2
|
-
import type { StatementKind } from 'ast-types/gen/kinds'
|
|
3
|
-
import * as graphql from 'graphql'
|
|
4
|
-
import * as recast from 'recast'
|
|
5
|
-
|
|
6
|
-
import { Config, HoudiniError, siteURL, fs, CollectedGraphQLDocument, path } from '../../../lib'
|
|
7
|
-
import { flattenSelections } from '../../utils'
|
|
8
|
-
import { addReferencedInputTypes } from './addReferencedInputTypes'
|
|
9
|
-
import { fragmentKey, inlineType } from './inlineType'
|
|
10
|
-
import { tsTypeReference } from './typeReference'
|
|
11
|
-
import { readonlyProperty } from './types'
|
|
12
|
-
|
|
13
|
-
const AST = recast.types.builders
|
|
14
|
-
|
|
15
|
-
// typescriptGenerator generates typescript definitions for the artifacts
|
|
16
|
-
export default async function typescriptGenerator(
|
|
17
|
-
config: Config,
|
|
18
|
-
docs: CollectedGraphQLDocument[]
|
|
19
|
-
) {
|
|
20
|
-
// build up a list of paths we have types in (to export from index.d.ts)
|
|
21
|
-
const typePaths: string[] = []
|
|
22
|
-
|
|
23
|
-
// we need every fragment definition
|
|
24
|
-
const fragmentDefinitions: { [name: string]: graphql.FragmentDefinitionNode } = {}
|
|
25
|
-
for (const document of docs) {
|
|
26
|
-
for (const defn of document.originalDocument.definitions.filter(
|
|
27
|
-
({ kind }) => kind === 'FragmentDefinition'
|
|
28
|
-
) as graphql.FragmentDefinitionNode[]) {
|
|
29
|
-
fragmentDefinitions[defn.name.value] = defn
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const missingScalars = new Set<string>()
|
|
34
|
-
|
|
35
|
-
// every document needs a generated type
|
|
36
|
-
await Promise.all(
|
|
37
|
-
// the generated types depend solely on user-provided information
|
|
38
|
-
// so we need to use the original document that we haven't mutated
|
|
39
|
-
// as part of the compiler
|
|
40
|
-
docs.map(async ({ originalDocument, name, filename, generateArtifact }) => {
|
|
41
|
-
if (!generateArtifact) {
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// the place to put the artifact's type definition
|
|
46
|
-
const typeDefPath = config.artifactTypePath(originalDocument)
|
|
47
|
-
|
|
48
|
-
// build up the program
|
|
49
|
-
const program = AST.program([])
|
|
50
|
-
|
|
51
|
-
// if we have to define any types along the way, make sure we only do it once
|
|
52
|
-
const visitedTypes = new Set<string>()
|
|
53
|
-
|
|
54
|
-
// if there's an operation definition
|
|
55
|
-
let definition = originalDocument.definitions.find(
|
|
56
|
-
(def) =>
|
|
57
|
-
(def.kind === 'OperationDefinition' || def.kind === 'FragmentDefinition') &&
|
|
58
|
-
def.name?.value === name
|
|
59
|
-
) as graphql.OperationDefinitionNode | graphql.FragmentDefinitionNode
|
|
60
|
-
|
|
61
|
-
const selections = flattenSelections({
|
|
62
|
-
config,
|
|
63
|
-
filepath: filename,
|
|
64
|
-
selections: definition.selectionSet.selections,
|
|
65
|
-
fragmentDefinitions,
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
if (definition?.kind === 'OperationDefinition') {
|
|
69
|
-
// treat it as an operation document
|
|
70
|
-
await generateOperationTypeDefs(
|
|
71
|
-
config,
|
|
72
|
-
filename,
|
|
73
|
-
program.body,
|
|
74
|
-
definition,
|
|
75
|
-
selections,
|
|
76
|
-
visitedTypes,
|
|
77
|
-
missingScalars
|
|
78
|
-
)
|
|
79
|
-
} else {
|
|
80
|
-
// treat it as a fragment document
|
|
81
|
-
await generateFragmentTypeDefs(
|
|
82
|
-
config,
|
|
83
|
-
filename,
|
|
84
|
-
program.body,
|
|
85
|
-
selections,
|
|
86
|
-
originalDocument.definitions,
|
|
87
|
-
visitedTypes,
|
|
88
|
-
missingScalars
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// write the file contents
|
|
93
|
-
await fs.writeFile(typeDefPath, recast.print(program).code)
|
|
94
|
-
|
|
95
|
-
typePaths.push(typeDefPath)
|
|
96
|
-
})
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
// now that we have every type generated, create an index file in the runtime root that exports the types
|
|
100
|
-
const typeIndex = AST.program(
|
|
101
|
-
typePaths
|
|
102
|
-
.sort((a, b) => a.localeCompare(b))
|
|
103
|
-
.map((typePath) => {
|
|
104
|
-
return AST.exportAllDeclaration(
|
|
105
|
-
AST.literal(
|
|
106
|
-
'./' +
|
|
107
|
-
path
|
|
108
|
-
.relative(path.resolve(config.typeIndexPath, '..'), typePath)
|
|
109
|
-
// remove the .d.ts from the end of the path
|
|
110
|
-
.replace(/\.[^/.]+\.[^/.]+$/, '')
|
|
111
|
-
),
|
|
112
|
-
null
|
|
113
|
-
)
|
|
114
|
-
})
|
|
115
|
-
.concat([
|
|
116
|
-
AST.exportAllDeclaration(AST.literal('./runtime'), null),
|
|
117
|
-
AST.exportAllDeclaration(AST.literal('./graphql'), null),
|
|
118
|
-
])
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
// stringify the value so we can push it through the plugins
|
|
122
|
-
const export_default_as = ({ module, as }: { module: string; as: string }) =>
|
|
123
|
-
`\nexport { default as ${as} } from "${module}"\n`
|
|
124
|
-
const export_star_from = ({ module }: { module: string }) => `\nexport * from "${module}"\n`
|
|
125
|
-
let indexContent = recast.print(typeIndex).code
|
|
126
|
-
for (const plugin of config.plugins) {
|
|
127
|
-
if (!plugin.index_file) {
|
|
128
|
-
continue
|
|
129
|
-
}
|
|
130
|
-
indexContent = plugin.index_file({
|
|
131
|
-
config,
|
|
132
|
-
content: indexContent,
|
|
133
|
-
export_default_as,
|
|
134
|
-
export_star_from,
|
|
135
|
-
plugin_root: config.pluginDirectory(plugin.name),
|
|
136
|
-
typedef: true,
|
|
137
|
-
documents: docs,
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
// if the plugin generated a runtime
|
|
141
|
-
if (plugin.include_runtime) {
|
|
142
|
-
indexContent += export_star_from({
|
|
143
|
-
module:
|
|
144
|
-
'./' +
|
|
145
|
-
path.relative(config.rootDir, config.pluginRuntimeDirectory(plugin.name)),
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// write the contents
|
|
151
|
-
await fs.writeFile(config.typeIndexPath, indexContent)
|
|
152
|
-
|
|
153
|
-
// if we were missing scalars, we need to warn the user and tell them
|
|
154
|
-
if (missingScalars.size > 0) {
|
|
155
|
-
console.warn(`⚠️ Missing definitions for the following scalars: ${[...missingScalars].join(
|
|
156
|
-
', '
|
|
157
|
-
)}
|
|
158
|
-
Generated types will contain an any type in place of these values. To fix this, provide an equivalent
|
|
159
|
-
type in your config file:
|
|
160
|
-
|
|
161
|
-
{
|
|
162
|
-
scalars: {
|
|
163
|
-
${logCyan(`/* in your case, something like */`)}
|
|
164
|
-
${[...missingScalars]
|
|
165
|
-
.map(
|
|
166
|
-
(c) =>
|
|
167
|
-
` ${c}: { ${logGreen(`// <- The GraphQL Scalar`)}
|
|
168
|
-
type: "${logCyan(`YourType_${c}`)}" ${logGreen(`// <- The TypeScript type`)}
|
|
169
|
-
}`
|
|
170
|
-
)
|
|
171
|
-
.join(
|
|
172
|
-
`,
|
|
173
|
-
`
|
|
174
|
-
)}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
For more information, please visit this link: ${siteURL}/api/config#custom-scalars`)
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async function generateOperationTypeDefs(
|
|
183
|
-
config: Config,
|
|
184
|
-
filepath: string,
|
|
185
|
-
body: StatementKind[],
|
|
186
|
-
definition: graphql.OperationDefinitionNode,
|
|
187
|
-
selections: readonly graphql.SelectionNode[],
|
|
188
|
-
visitedTypes: Set<string>,
|
|
189
|
-
missingScalars: Set<string>
|
|
190
|
-
) {
|
|
191
|
-
let parentType: graphql.GraphQLCompositeType | null = null
|
|
192
|
-
if (definition.operation === 'query') {
|
|
193
|
-
parentType = config.schema.getQueryType()!
|
|
194
|
-
} else if (definition.operation === 'mutation') {
|
|
195
|
-
parentType = config.schema.getMutationType()!
|
|
196
|
-
} else if (definition.operation === 'subscription') {
|
|
197
|
-
parentType = config.schema.getSubscriptionType()!
|
|
198
|
-
}
|
|
199
|
-
if (!parentType) {
|
|
200
|
-
throw new HoudiniError({ filepath, message: 'Could not find root type for document' })
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// the name of the types we will define
|
|
204
|
-
const inputTypeName = `${definition.name!.value}$input`
|
|
205
|
-
const shapeTypeName = `${definition.name!.value}$result`
|
|
206
|
-
const optimisticTypeName = `${definition.name!.value}$optimistic`
|
|
207
|
-
|
|
208
|
-
// dry
|
|
209
|
-
const hasInputs = definition.variableDefinitions && definition.variableDefinitions.length > 0
|
|
210
|
-
|
|
211
|
-
// add our types to the body
|
|
212
|
-
body.push(
|
|
213
|
-
// add the root type named after the document that links the input and result types
|
|
214
|
-
AST.exportNamedDeclaration(
|
|
215
|
-
AST.tsTypeAliasDeclaration(
|
|
216
|
-
AST.identifier(definition.name!.value),
|
|
217
|
-
AST.tsTypeLiteral([
|
|
218
|
-
readonlyProperty(
|
|
219
|
-
AST.tsPropertySignature(
|
|
220
|
-
AST.stringLiteral('input'),
|
|
221
|
-
AST.tsTypeAnnotation(AST.tsTypeReference(AST.identifier(inputTypeName)))
|
|
222
|
-
)
|
|
223
|
-
),
|
|
224
|
-
readonlyProperty(
|
|
225
|
-
AST.tsPropertySignature(
|
|
226
|
-
AST.stringLiteral('result'),
|
|
227
|
-
AST.tsTypeAnnotation(
|
|
228
|
-
definition.operation === 'mutation'
|
|
229
|
-
? AST.tsTypeReference(AST.identifier(shapeTypeName))
|
|
230
|
-
: AST.tsUnionType([
|
|
231
|
-
AST.tsTypeReference(AST.identifier(shapeTypeName)),
|
|
232
|
-
AST.tsUndefinedKeyword(),
|
|
233
|
-
])
|
|
234
|
-
)
|
|
235
|
-
)
|
|
236
|
-
),
|
|
237
|
-
])
|
|
238
|
-
)
|
|
239
|
-
),
|
|
240
|
-
// export the type that describes the result
|
|
241
|
-
AST.exportNamedDeclaration(
|
|
242
|
-
AST.tsTypeAliasDeclaration(
|
|
243
|
-
AST.identifier(shapeTypeName),
|
|
244
|
-
inlineType({
|
|
245
|
-
config,
|
|
246
|
-
filepath,
|
|
247
|
-
rootType: parentType,
|
|
248
|
-
selections,
|
|
249
|
-
root: true,
|
|
250
|
-
allowReadonly: true,
|
|
251
|
-
visitedTypes,
|
|
252
|
-
body,
|
|
253
|
-
missingScalars,
|
|
254
|
-
includeFragments: true,
|
|
255
|
-
})
|
|
256
|
-
)
|
|
257
|
-
)
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
// if there are variables in this query
|
|
261
|
-
if (hasInputs && definition.variableDefinitions && definition.variableDefinitions.length > 0) {
|
|
262
|
-
for (const variableDefinition of definition.variableDefinitions) {
|
|
263
|
-
addReferencedInputTypes(
|
|
264
|
-
config,
|
|
265
|
-
filepath,
|
|
266
|
-
body,
|
|
267
|
-
visitedTypes,
|
|
268
|
-
missingScalars,
|
|
269
|
-
variableDefinition.type
|
|
270
|
-
)
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// merge all of the variables into a single object
|
|
274
|
-
body.push(
|
|
275
|
-
AST.exportNamedDeclaration(
|
|
276
|
-
AST.tsTypeAliasDeclaration(
|
|
277
|
-
AST.identifier(inputTypeName),
|
|
278
|
-
AST.tsTypeLiteral(
|
|
279
|
-
(definition.variableDefinitions || []).map(
|
|
280
|
-
(definition: graphql.VariableDefinitionNode) => {
|
|
281
|
-
// add a property describing the variable to the root object
|
|
282
|
-
return AST.tsPropertySignature(
|
|
283
|
-
AST.identifier(definition.variable.name.value),
|
|
284
|
-
AST.tsTypeAnnotation(
|
|
285
|
-
tsTypeReference(config, missingScalars, definition)
|
|
286
|
-
),
|
|
287
|
-
definition.type.kind !== 'NonNullType'
|
|
288
|
-
)
|
|
289
|
-
}
|
|
290
|
-
)
|
|
291
|
-
)
|
|
292
|
-
)
|
|
293
|
-
)
|
|
294
|
-
)
|
|
295
|
-
} else {
|
|
296
|
-
body.push(
|
|
297
|
-
AST.exportNamedDeclaration(
|
|
298
|
-
AST.tsTypeAliasDeclaration(AST.identifier(inputTypeName), AST.tsNullKeyword())
|
|
299
|
-
)
|
|
300
|
-
)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// mutations need to have an optimistic response type defined
|
|
304
|
-
if (definition.operation === 'mutation') {
|
|
305
|
-
body.push(
|
|
306
|
-
AST.exportNamedDeclaration(
|
|
307
|
-
AST.tsTypeAliasDeclaration(
|
|
308
|
-
AST.identifier(optimisticTypeName),
|
|
309
|
-
inlineType({
|
|
310
|
-
config,
|
|
311
|
-
filepath,
|
|
312
|
-
rootType: parentType,
|
|
313
|
-
selections,
|
|
314
|
-
root: true,
|
|
315
|
-
allowReadonly: true,
|
|
316
|
-
visitedTypes,
|
|
317
|
-
body,
|
|
318
|
-
missingScalars,
|
|
319
|
-
includeFragments: false,
|
|
320
|
-
allOptional: true,
|
|
321
|
-
})
|
|
322
|
-
)
|
|
323
|
-
)
|
|
324
|
-
)
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
async function generateFragmentTypeDefs(
|
|
329
|
-
config: Config,
|
|
330
|
-
filepath: string,
|
|
331
|
-
body: StatementKind[],
|
|
332
|
-
selections: readonly graphql.SelectionNode[],
|
|
333
|
-
definitions: readonly graphql.DefinitionNode[],
|
|
334
|
-
visitedTypes: Set<string>,
|
|
335
|
-
missingScalars: Set<string>
|
|
336
|
-
) {
|
|
337
|
-
// every definition will contribute the same thing to the typedefs
|
|
338
|
-
for (const definition of definitions) {
|
|
339
|
-
// if its not a fragment definition
|
|
340
|
-
if (definition.kind !== 'FragmentDefinition') {
|
|
341
|
-
// we don't know what to do
|
|
342
|
-
continue
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// the name of the prop type
|
|
346
|
-
const propTypeName = definition.name.value
|
|
347
|
-
// the name of the shape type
|
|
348
|
-
const shapeTypeName = `${definition.name.value}$data`
|
|
349
|
-
|
|
350
|
-
// look up the root type of the document
|
|
351
|
-
const type = config.schema.getType(definition.typeCondition.name.value)
|
|
352
|
-
if (!type) {
|
|
353
|
-
throw new Error('Should not get here')
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
body.push(
|
|
357
|
-
// we need to add a type that will act as the entry point for the fragment
|
|
358
|
-
// and be assigned to the prop that holds the reference passed from
|
|
359
|
-
// the fragment's parent
|
|
360
|
-
AST.exportNamedDeclaration(
|
|
361
|
-
AST.tsTypeAliasDeclaration(
|
|
362
|
-
AST.identifier(propTypeName),
|
|
363
|
-
AST.tsTypeLiteral([
|
|
364
|
-
readonlyProperty(
|
|
365
|
-
AST.tsPropertySignature(
|
|
366
|
-
AST.stringLiteral('shape'),
|
|
367
|
-
AST.tsTypeAnnotation(
|
|
368
|
-
AST.tsTypeReference(AST.identifier(shapeTypeName))
|
|
369
|
-
),
|
|
370
|
-
true
|
|
371
|
-
)
|
|
372
|
-
),
|
|
373
|
-
readonlyProperty(
|
|
374
|
-
AST.tsPropertySignature(
|
|
375
|
-
AST.stringLiteral(fragmentKey),
|
|
376
|
-
AST.tsTypeAnnotation(
|
|
377
|
-
AST.tsTypeLiteral([
|
|
378
|
-
AST.tsPropertySignature(
|
|
379
|
-
AST.stringLiteral(propTypeName),
|
|
380
|
-
AST.tsTypeAnnotation(
|
|
381
|
-
AST.tsLiteralType(AST.booleanLiteral(true))
|
|
382
|
-
)
|
|
383
|
-
),
|
|
384
|
-
])
|
|
385
|
-
)
|
|
386
|
-
)
|
|
387
|
-
),
|
|
388
|
-
])
|
|
389
|
-
)
|
|
390
|
-
),
|
|
391
|
-
|
|
392
|
-
// export the type that describes the fragments response data
|
|
393
|
-
AST.exportNamedDeclaration(
|
|
394
|
-
AST.tsTypeAliasDeclaration(
|
|
395
|
-
AST.identifier(shapeTypeName),
|
|
396
|
-
inlineType({
|
|
397
|
-
config,
|
|
398
|
-
filepath,
|
|
399
|
-
rootType: type,
|
|
400
|
-
selections,
|
|
401
|
-
root: true,
|
|
402
|
-
allowReadonly: true,
|
|
403
|
-
body,
|
|
404
|
-
visitedTypes,
|
|
405
|
-
missingScalars,
|
|
406
|
-
includeFragments: true,
|
|
407
|
-
})
|
|
408
|
-
)
|
|
409
|
-
)
|
|
410
|
-
)
|
|
411
|
-
}
|
|
412
|
-
}
|