@@ -39,22 +39,21 @@ import {
3939} from 'graphql' ;
4040import resolveFrom from 'resolve-from' ;
4141import { parseGraphQLSDL } from '@graphql-tools/utils' ;
42+ import { extractLinkImplementations } from '@theguild/federation-composition' ;
4243
4344const builtinTypes = [ 'String' , 'Float' , 'Int' , 'Boolean' , 'ID' , 'Upload' ] ;
4445
46+ const federationV1Directives = [ 'key' , 'provides' , 'requires' , 'external' ] ;
47+
4548const builtinDirectives = [
4649 'deprecated' ,
4750 'skip' ,
4851 'include' ,
4952 'cacheControl' ,
50- 'key' ,
51- 'external' ,
52- 'requires' ,
53- 'provides' ,
5453 'connection' ,
5554 'client' ,
5655 'specifiedBy' ,
57- 'link' ,
56+ ... federationV1Directives ,
5857] ;
5958
6059const IMPORT_FROM_REGEX = / ^ i m p o r t \s + ( \* | ( .* ) ) \s + f r o m \s + ( ' | " ) ( .* ) ( ' | " ) ; ? $ / ;
@@ -264,6 +263,66 @@ export function extractDependencies(
264263 } ;
265264}
266265
266+ function importFederatedSchemaLinks (
267+ definition : DefinitionNode ,
268+ definitionsByName : Map < string , Set < DefinitionNode > > ,
269+ ) {
270+ const addDefinition = ( name : string ) => {
271+ definitionsByName . set ( name . replace ( / ^ @ / g, '' ) , new Set ( ) ) ;
272+ } ;
273+
274+ // extract links from this definition
275+ const { links, matchesImplementation, resolveImportName } = extractLinkImplementations ( {
276+ kind : Kind . DOCUMENT ,
277+ definitions : [ definition ] ,
278+ } ) ;
279+
280+ if ( links . length ) {
281+ const federationUrl = 'https://specs.apollo.dev/federation' ;
282+ const linkUrl = 'https://specs.apollo.dev/link' ;
283+
284+ /**
285+ * Official Federated imports are special because they can be referenced without specifyin the import.
286+ * To handle this case, we must prepare a list of all the possible valid usages to check against.
287+ * Note that this versioning is not technically correct, since some definitions are after v2.0.
288+ * But this is enough information to be comfortable not blocking the imports at this phase. It's
289+ * the job of the composer to validate the versions.
290+ * */
291+ if ( matchesImplementation ( federationUrl , 'v2.0' ) ) {
292+ const federationImports = [
293+ '@composeDirective' ,
294+ '@extends' ,
295+ '@external' ,
296+ '@inaccessible' ,
297+ '@interfaceObject' ,
298+ '@key' ,
299+ '@override' ,
300+ '@provides' ,
301+ '@requires' ,
302+ '@shareable' ,
303+ '@tag' ,
304+ 'FieldSet' ,
305+ ] ;
306+ for ( const i of federationImports ) {
307+ addDefinition ( resolveImportName ( federationUrl , i ) ) ;
308+ }
309+ }
310+ if ( matchesImplementation ( linkUrl , 'v1.0' ) ) {
311+ const linkImports = [ 'Purpose' , 'Import' , '@link' ] ;
312+ for ( const i of linkImports ) {
313+ addDefinition ( resolveImportName ( linkUrl , i ) ) ;
314+ }
315+ }
316+
317+ const imported = links
318+ . filter ( l => ! [ linkUrl , federationUrl ] . includes ( l . identity ) )
319+ . flatMap ( l => l . imports . map ( i => i . as ?? i . name ) ) ;
320+ for ( const namedImport of imported ) {
321+ addDefinition ( namedImport ) ;
322+ }
323+ }
324+ }
325+
267326function visitDefinition (
268327 definition : DefinitionNode ,
269328 definitionsByName : Map < string , Set < DefinitionNode > > ,
@@ -288,6 +347,7 @@ function visitDefinition(
288347 dependencySet = new Set ( ) ;
289348 dependenciesByDefinitionName . set ( definitionName , dependencySet ) ;
290349 }
350+
291351 switch ( definition . kind ) {
292352 case Kind . OPERATION_DEFINITION :
293353 visitOperationDefinitionNode ( definition , dependencySet ) ;
@@ -317,9 +377,11 @@ function visitDefinition(
317377 visitScalarDefinitionNode ( definition , dependencySet ) ;
318378 break ;
319379 case Kind . SCHEMA_DEFINITION :
380+ importFederatedSchemaLinks ( definition , definitionsByName ) ;
320381 visitSchemaDefinitionNode ( definition , dependencySet ) ;
321382 break ;
322383 case Kind . SCHEMA_EXTENSION :
384+ importFederatedSchemaLinks ( definition , definitionsByName ) ;
323385 visitSchemaExtensionDefinitionNode ( definition , dependencySet ) ;
324386 break ;
325387 case Kind . OBJECT_TYPE_EXTENSION :
0 commit comments