-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enums not tree shaking well #4253
Comments
Yeah, there are several issues with the TS enum like #3356. However converting to an object won't solve your issue here, that being said, this is a reference implementation and we have made tradeoffs for DX/readability for that matter. The minzipped size of this object when not using TS enums should not exceed 1kb. We could split this up between schema-kind and executable kind to improve this a bit more for web implementations. |
Right an object wouldn't solve tree shaking. Rather I defined every enum member as its own constant and then use the namespace to group them. That supports tree shaking and I see actually no DX issue here, since TS allows type names that match a namespace name so it ends up feeling identical to the enum before. |
Grouping them under a namespace won't allow them to be used in JS as follows right? I reckon that the use as type would be supported but use as value would not be. The issue is similar with using import { Kind } from 'graphql';
if (selection.kind === Kind.FIELD) My main concern would be to still allow this usage so v17 doesn't have too many breaking changes, in #4254 we move away from the I would be a huge fan of evolving into a tree-shakable model though, for now I would advise people that are concerned with that to leverage your approach. |
Does the following address your example or am I missing something? // @filename: node_modules/graphql/kind/kind.ts
export const FIELD = 'field'
export const B = 'b' // etc.
export const C = 'c' // etc.
export const D = 'd' // etc.
export type FIELD = typeof FIELD
export type B = typeof B
export type C = typeof C
export type D = typeof D
// @filename: node_modules/graphql/kind/__.ts
import type * as Kind_ from './kind.js'
type Kind__ = typeof Kind_
export type Kind = Kind__[keyof Kind__ & string]
export * as Kind from './kind.js'
// @filename: node_modules/graphql/index.ts
export * from './kind/__.js'
// @filename: app.ts
import { Kind } from 'graphql'
const foo = (kind: Kind) => {
if (kind === 'field') // do something
}
Kind.FIELD // "field"
// @ts-expect-error
foo('bad') // fail
foo('field') // ok
foo('b') // ok
foo('c') // ok
foo('d') // ok |
Here's another approach that might seem/be simpler: // @filename: node_modules/graphql/kind/kind.ts
export const FIELD = 'field'
export const B = 'b' // etc.
export const C = 'c' // etc.
export const D = 'd' // etc.
// @filename: node_modules/graphql/kind/__.ts
import { FIELD, B, C, D } from './kind.js'
export type Kind =
| typeof FIELD
| typeof B
| typeof C
| typeof D
export * as Kind from './kind.js'
// @filename: node_modules/graphql/index.ts
export * from './kind/__.js'
// @filename: app.ts
import { Kind } from 'graphql'
const foo = (kind: Kind) => {
if (kind === 'field') return // do something
}
Kind.FIELD // "field"
// @ts-expect-error
foo('bad') // fail
foo('field') // ok
foo('b') // ok
foo('c') // ok
foo('d') // ok |
It is worth another check but my understanding is that this allows |
Well you can't then use i.e. I reckon that the CJS equivalent might tree-shake less elegantly but the more reason for leveraging ESM. EDIT: I tried this out and the using the namespace as a type keeps failing as when we use |
Hey @JoviDeCroock, I'm not sure what you mean by this. In my example above for example we see that // @filename: app.ts
import { Kind } from 'graphql' // <-- `Kind` is at both type & term levels
Thanks will check it out! |
I will have a PR up today that we can discus further. |
Fixed it in #4268 |
@JoviDeCroock Ah beat me to it. PR is at fwiw #4269 but I'll close it now. Thanks! |
Reopening. Sorry didn't realize the PR is still open. I dropped some feedback on the PR. |
This gets rid of enums for two reasons - TypeScript does not really compile them size efficiently, see [here](https://www.runpkg.com/[email protected]/language/kinds.mjs) - Raw size kinds.js before: 2800bytes - Raw size kinds.js after: 2200 bytes - It's not kind to plains strings as seen in #3356 Resolves #3356 This does not intend to fix the tree-shaking issue as seen in #4253, for that we'd need to fully normalize these exports and sacrifice the DX of the namespaces. I do think that it becomes easier as plain strings will be "easier" compared to before so if you're not doing comparisons you can opt out of the `Kind` namespace. <details> <summary>See new output</summary> ```js /** * The set of allowed kind values for AST nodes. */ exports.Kind = { /** Name */ NAME: 'Name', /** Document */ DOCUMENT: 'Document', OPERATION_DEFINITION: 'OperationDefinition', VARIABLE_DEFINITION: 'VariableDefinition', SELECTION_SET: 'SelectionSet', FIELD: 'Field', ARGUMENT: 'Argument', FRAGMENT_ARGUMENT: 'FragmentArgument', /** Fragments */ FRAGMENT_SPREAD: 'FragmentSpread', INLINE_FRAGMENT: 'InlineFragment', FRAGMENT_DEFINITION: 'FragmentDefinition', /** Values */ VARIABLE: 'Variable', INT: 'IntValue', FLOAT: 'FloatValue', STRING: 'StringValue', BOOLEAN: 'BooleanValue', NULL: 'NullValue', ENUM: 'EnumValue', LIST: 'ListValue', OBJECT: 'ObjectValue', OBJECT_FIELD: 'ObjectField', /** Directives */ DIRECTIVE: 'Directive', /** Types */ NAMED_TYPE: 'NamedType', LIST_TYPE: 'ListType', NON_NULL_TYPE: 'NonNullType', /** Type System Definitions */ SCHEMA_DEFINITION: 'SchemaDefinition', OPERATION_TYPE_DEFINITION: 'OperationTypeDefinition', /** Type Definitions */ SCALAR_TYPE_DEFINITION: 'ScalarTypeDefinition', OBJECT_TYPE_DEFINITION: 'ObjectTypeDefinition', FIELD_DEFINITION: 'FieldDefinition', INPUT_VALUE_DEFINITION: 'InputValueDefinition', INTERFACE_TYPE_DEFINITION: 'InterfaceTypeDefinition', UNION_TYPE_DEFINITION: 'UnionTypeDefinition', ENUM_TYPE_DEFINITION: 'EnumTypeDefinition', ENUM_VALUE_DEFINITION: 'EnumValueDefinition', INPUT_OBJECT_TYPE_DEFINITION: 'InputObjectTypeDefinition', /** Directive Definitions */ DIRECTIVE_DEFINITION: 'DirectiveDefinition', /** Type System Extensions */ SCHEMA_EXTENSION: 'SchemaExtension', /** Type Extensions */ SCALAR_TYPE_EXTENSION: 'ScalarTypeExtension', OBJECT_TYPE_EXTENSION: 'ObjectTypeExtension', INTERFACE_TYPE_EXTENSION: 'InterfaceTypeExtension', UNION_TYPE_EXTENSION: 'UnionTypeExtension', ENUM_TYPE_EXTENSION: 'EnumTypeExtension', INPUT_OBJECT_TYPE_EXTENSION: 'InputObjectTypeExtension', }; ``` </details> Currently seeing whether we should disable the redeclare lint-rule and whether there is a better way to type our `nodeKinds`
I have redefined the
Kind
andOperationTypeNode
enums in Graffle because they do not seem to tree shake well. You can see the code and bundle visualization in this PR. With my approach already just those two enums are reduced by over 4kb in Graffle.Obviously it would be nice if
graphql
was already fully tree shake optimized from the start. Is there an appetite to adjust how the enums are defined ingraphql
? I think my approach would be a breaking change at the type level because TypeScript enum types, even if using string constants at runtime, are not equivalent to them at the type level.Feel free to close this issue if there's zero chance of change on the
graphql
package side of course. Convesely, happy to help out on this issue.The text was updated successfully, but these errors were encountered: