Module: ArSync::TypeScript

Defined in:
lib/ar_sync/type_script.rb

Class Method Summary collapse

Class Method Details

.data_type_definition(type) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/ar_sync/type_script.rb', line 125

def self.data_type_definition(type)
  field_definitions = []
  type.fields.each do |field|
    field_definitions << "#{field.name}: #{field.type.ts_type}"
  end
  field_definitions << "_meta?: { name: '#{type.name}'; query: Type#{type.name}QueryBase }"
  <<~TYPE
    export interface Type#{type.name} {
    #{field_definitions.map { |line| "  #{line}" }.join("\n")}
    }
  TYPE
end

.generate_api_scriptObject



63
64
65
66
67
68
69
70
71
72
# File 'lib/ar_sync/type_script.rb', line 63

def self.generate_api_script
  <<~CODE
    import { TypeRequest } from './types'
    import DataTypeFromRequest from './DataTypeFromRequest'
    import ArSyncApi from 'ar_sync/core/ArSyncApi'
    export function fetch<R extends TypeRequest>(request: R) {
      return ArSyncApi.fetch(request) as Promise<DataTypeFromRequest<R>>
    }
  CODE
end

.generate_hooks_scriptObject



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ar_sync/type_script.rb', line 84

def self.generate_hooks_script
  <<~CODE
    import { useState, useEffect, useMemo, useRef } from 'react'
    import { TypeRequest } from './types'
    import DataTypeFromRequest, { NeverMatchArgument } from './DataTypeFromRequest'
    import { initializeHooks, useArSyncModel as useArSyncModelBase, useArSyncFetch as useArSyncFetchBase } from 'ar_sync/core/hooks'
    initializeHooks({ useState, useEffect, useMemo, useRef })
    export function useArSyncModel<R extends TypeRequest | NeverMatchArgument>(request: R | null) {
      return useArSyncModelBase<DataTypeFromRequest<R>>(request as TypeRequest)
    }
    export function useArSyncFetch<R extends TypeRequest | NeverMatchArgument>(request: R | null) {
      return useArSyncFetchBase<DataTypeFromRequest<R>>(request as TypeRequest)
    }
  CODE
end

.generate_model_scriptObject



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/ar_sync/type_script.rb', line 51

def self.generate_model_script
  <<~CODE
    import { TypeRequest } from './types'
    import DataTypeFromRequest from './DataTypeFromRequest'
    import { default as ArSyncModelBase } from 'ar_sync/core/ArSyncModel'
    declare class ArSyncModel<R extends TypeRequest> extends ArSyncModelBase<DataTypeFromRequest<R>> {
      constructor(r: R, option?: { immutable: boolean })
    }
    export default ArSyncModelBase as typeof ArSyncModel
  CODE
end

.generate_type_definition(api_class) ⇒ Object



12
13
14
15
16
17
18
19
# File 'lib/ar_sync/type_script.rb', line 12

def self.generate_type_definition(api_class)
  types = ArSerializer::TypeScript.related_serializer_types([api_class]).reject { |t| t.type == api_class }
  [
    types.map { |t| data_type_definition t },
    types.map { |t| query_type_definition t },
    request_type_definition(api_class)
  ].join "\n"
end

.generate_type_util_scriptObject



74
75
76
77
78
79
80
81
82
# File 'lib/ar_sync/type_script.rb', line 74

def self.generate_type_util_script
  <<~CODE
    import { TypeRequest, ApiNameRequests } from './types'
    import { DataTypeFromRequest as DataTypeFromRequestPair } from 'ar_sync/core/DataType'
    export type NeverMatchArgument = { __nevermatch: never }
    type DataTypeFromRequest<R extends TypeRequest | NeverMatchArgument> = NeverMatchArgument extends R ? never : R extends TypeRequest ? DataTypeFromRequestPair<ApiNameRequests[R['api']], R> : never
    export default DataTypeFromRequest
  CODE
end

.generate_typed_files(api_class, dir:, comment: nil) ⇒ Object



2
3
4
5
6
7
8
9
10
# File 'lib/ar_sync/type_script.rb', line 2

def self.generate_typed_files(api_class, dir:, comment: nil)
  {
    'types.ts' => generate_type_definition(api_class),
    'ArSyncModel.ts' => generate_model_script,
    'ArSyncApi.ts' => generate_api_script,
    'hooks.ts' => generate_hooks_script,
    'DataTypeFromRequest.ts' => generate_type_util_script
  }.each { |file, code| File.write File.join(dir, file), "#{comment}#{code}" }
end

.query_type_definition(type) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ar_sync/type_script.rb', line 100

def self.query_type_definition(type)
  field_definitions = type.fields.map do |field|
    association_type = field.type.association_type
    if association_type
      qname = "Type#{association_type.name}Query"
      if field.args.empty?
        "#{field.name}?: true | #{qname} | { attributes?: #{qname} }"
      else
        "#{field.name}?: true | #{qname} | { params: #{field.args_ts_type}; attributes?: #{qname} }"
      end
    else
      "#{field.name}?: true"
    end
  end
  field_definitions << "'*'?: true"
  query_type_name = "Type#{type.name}Query"
  base_query_type_name = "Type#{type.name}QueryBase"
  <<~TYPE
    export type #{query_type_name} = keyof (#{base_query_type_name}) | Readonly<(keyof (#{base_query_type_name}))[]> | #{base_query_type_name}
    export interface #{base_query_type_name} {
    #{field_definitions.map { |line| "  #{line}" }.join("\n")}
    }
  TYPE
end

.request_type_definition(api_class) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ar_sync/type_script.rb', line 21

def self.request_type_definition(api_class)
  type = ArSerializer::GraphQL::TypeClass.from api_class
  definitions = []
  request_types = {}
  type.fields.each do |field|
    association_type = field.type.association_type
    next unless association_type
    prefix = 'Class' if field.name.match?(/\A[A-Z]/) # for class reload query
    request_type_name = "Type#{prefix}#{field.name.camelize}Request"
    request_types[field.name] = request_type_name
    multiple = field.type.is_a? ArSerializer::GraphQL::ListTypeClass
    definitions << <<~CODE
      export interface #{request_type_name} {
        api: '#{field.name}'
        params?: #{field.args_ts_type}
        query: Type#{association_type.name}Query
        _meta?: { data: Type#{field.type.association_type.name}#{'[]' if multiple} }
      }
    CODE
  end
  [
    'export type TypeRequest = ',
    request_types.values.map { |value| "  | #{value}" },
    'export type ApiNameRequests =  {',
    request_types.map { |key, value| "  #{key}: #{value}" },
    '}',
    definitions
  ].join("\n")
end