module ArSync::TypeScript

Public Class Methods

data_type_definition(type) click to toggle source
# 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_script() click to toggle source
# 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_script() click to toggle source
# 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_script() click to toggle source
# 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) click to toggle source
# 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_script() click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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