private Task GenerateTypes(TsGenerationOptions option)
        {
            if (!environment.IsDevelopment())
            {
                Console.WriteLine("Skip TS model generation");
                return(Task.CompletedTask);
            }

            Console.WriteLine("Generate TS models");
            IEnumerable <Type> profileTypes = option.Assemblies
                                              .SelectMany(v => v.GetTypes())
                                              .Where(v => v.IsSubclassOf(typeof(TypeScriptProfile)));

            var builder = new TypeCollectionBuilder();

            // duplication?
            profileTypes.Select(v => Activator.CreateInstance(v) as TypeScriptProfile)
            .ForEach(type =>
            {
                builder.AddRange(type.Types);
            });

            IEnumerable <Type> additionalTypes = option.Assemblies
                                                 .SelectMany(v => v.GetExportedTypes()
                                                             .Where(x => x.GetCustomAttributes(typeof(GenerateTsInterface), true).Any()));

            var actions = option.Assemblies.GetActions();

            builder.AddRange(actions.Select(v => v.Input));
            builder.AddRange(actions.Select(v => v.Output));

            builder.AddRange(additionalTypes);

            if (builder.Count == 0)
            {
                Console.WriteLine("Skipping Ts generation, as there are not types");
            }
            else
            {
                //builder.Insert(0, "/* eslint-disable prettier/prettier */");

                var path = option.GetPath();

                Console.WriteLine(path);
                TypeCollection typeCollection = builder.Generate(option.Update);
                RenderTypes.ToDisk(typeCollection, path);
                RenderApi.Render(typeCollection, option);
            }

            return(Task.CompletedTask);
        }
Ejemplo n.º 2
0
        public static void ToDisk(TypeCollection types, string path)
        {
            if (!path.EndsWith("/"))
            {
                throw new ArgumentException("path must end with /");
            }

            Directory.CreateDirectory(path);

            IEnumerable <Module> modules = types.Modules;

            foreach (Module v in modules)
            {
                RenderModule(v, path);
            }
        }
Ejemplo n.º 3
0
        public static void Render(TypeCollection types, TsGenerationOptions options)
        {
            if (!options.GenerateApi)
            {
                Console.WriteLine("Skip generate api for " + options.Path);
                return;
            }

            var path = options.GetPath();
            IEnumerable <ActionMeta> actions = options.Assemblies.GetActions();

            var glowPath = options.Assemblies.FirstOrDefault()?.FullName.StartsWith("Glow.Core") == true
                ? ".."
                : "glow-react";
            var useSubmitPath = options.Assemblies.FirstOrDefault()?.FullName.StartsWith("Glow.Core") == true
                ? ".."
                : "glow-react/es";

            var imports = new StringBuilder();

            if (options.ApiOptions != null)
            {
                foreach (var v in options.ApiOptions?.ApiFileFirstLines)
                {
                    imports.AppendLine(v);
                }
            }

            imports.AppendLine(@"import * as React from ""react""");
            imports.AppendLine(@"import { QueryOptions } from ""react-query""");
            // allow adjusting?
            imports.AppendLine($@"import {{ useApi, ApiResult, notifySuccess, notifyError }} from ""{glowPath}""");
            imports.AppendLine(
                $@"import {{ useAction, useSubmit, UseSubmit, ProblemDetails }} from ""{useSubmitPath}/Forms/use-submit""");
            imports.AppendLine(@"import { Formik, FormikFormProps } from ""formik""");
            imports.AppendLine(@"import { Form } from ""formik-antd""");

            var modules = types.Modules;

            foreach (var v in modules)
            {
                if (v.Namespace == null)
                {
                    foreach (var t in v.TsTypes)
                    {
                        Console.WriteLine(t.Name);
                    }

                    throw new Exception("Namespace is null " + options.Path);
                    continue;
                }

                if (!v.Namespace.StartsWith("System."))
                {
                    imports.AppendLine(
                        $"import * as {v.Namespace.Replace(".", "_")} from \"./{v.Namespace}\"");
                }
            }

            IEnumerable <Dependency> dependencies = types
                                                    .Modules
                                                    .SelectMany(v => v.GetDependencies())
                                                    .DistinctBy(v => v.Id);

            imports.AppendLine("");

            var queryInputs   = new StringBuilder();
            var queryOutputs  = new StringBuilder();
            var actionInputs  = new StringBuilder();
            var actionOutputs = new StringBuilder();

            queryInputs.AppendLine("type QueryInputs = {");
            queryOutputs.AppendLine("type QueryOutputs = {");
            actionInputs.AppendLine("export type Actions = {");
            actionOutputs.AppendLine("export type Outputs = {");

            string GetTypeName(Type type)
            {
                if (!types.Exists(type))
                {
                    Console.WriteLine("Type does not exist " + type + " using any");
                    return("any");
                }

                var tsType    = types.Find(type);
                var name      = tsType.Match(v1 => v1.Name, v2 => v2.Name);
                var nameSpace = tsType.Match(v1 => v1.Namespace, v2 => v2.Namespace).Replace(".", "_");

                // Console.WriteLine("name=" + name);
                return(nameSpace + "." + name);
            }

            foreach (var v in actions)
            {
                // Console.WriteLine(v.ActionAttribute.Route);
                var outputTypeName = GetTypeName(v.Output);
                var inputTypeName  = GetTypeName(v.Input);

                if (v.Input.Name.StartsWith("Get"))
                {
                    queryOutputs.AppendLine($@"  ""/{v.ActionAttribute.Route}"": {outputTypeName},");
                    queryInputs.AppendLine($@"  ""/{v.ActionAttribute.Route}"": {inputTypeName},");
                }
                else
                {
                    actionOutputs.AppendLine($@"  ""/{v.ActionAttribute.Route}"": {outputTypeName},");
                    actionInputs.AppendLine($@"  ""/{v.ActionAttribute.Route}"": {inputTypeName},");
                }
            }

            queryInputs.AppendLine("}");
            queryOutputs.AppendLine("}");
            actionOutputs.AppendLine("}");
            actionInputs.AppendLine("}");

            actionInputs.AppendLine(@"
type TagWithKey<TagName extends string, T> = {
  [K in keyof T]: { [_ in TagName]: K } & T[K]
};

//export function useTypedAction<ActionName extends keyof ActionTable>(key: ActionName): UseSubmit<Actions[ActionName], Outputs[ActionName]>{
//  const s = useAction<Actions[ActionName], Outputs[ActionName]>(key)
//  return s
//}

export type ActionTable = TagWithKey<""url"", Actions>

export type TypedActionHookResult<
  ActionName extends keyof ActionTable
> = UseSubmit<Actions[ActionName], Outputs[ActionName]>

export type TypedActionHook = <ActionName extends keyof ActionTable>(
  key: ActionName,
) => TypedActionHookResult<ActionName>

export const useTypedAction: TypedActionHook = <
  ActionName extends keyof ActionTable
>(
  key: ActionName,
) => {
  const s = useAction<Actions[ActionName], Outputs[ActionName]>(key)
  return s
}

// export function useTypedAction<ActionName extends keyof ActionTable>(
//   key: ActionName,
// ): UseSubmit<Actions[ActionName], Outputs[ActionName]> {
//   const s = useAction<Actions[ActionName], Outputs[ActionName]>(key)
//   return s
// }

type QueryTable = TagWithKey<""url"", QueryInputs>;

export function useTypedQuery<ActionName extends keyof QueryTable>(key: ActionName, {
    placeholder,
    input,
    queryOptions
  }: {
    placeholder: QueryOutputs[ActionName],
    input:  QueryInputs[ActionName]
    queryOptions?: QueryOptions<QueryOutputs[ActionName]>
  }): ApiResult<QueryOutputs[ActionName]> {

  const { data, ...rest} = useApi({
    url: key,
    method:""POST"",
    payload: input,
    // todo: find defaultPlaceholder
    placeholder: placeholder,
    queryOptions: queryOptions
  })

  const result = data as QueryOutputs[ActionName]

  return { data: result, ...rest} as any
}

export function TypedForm<ActionName extends keyof ActionTable>({
  initialValues,
  actionName,
  formProps,
  children,
  onSuccess,
  onError,
}: React.PropsWithChildren<{
  actionName: ActionName
  initialValues: Actions[ActionName]
  formProps?: FormikFormProps
  onSuccess?: (payload: Outputs[ActionName]) => void
  onError?: (error: ProblemDetails) => void
}>) {
  const [submit, validate] = useTypedAction<ActionName>(actionName)
  return (
    <Formik
      validate={validate}
      validateOnBlur={true}
      enableReinitialize={true}
      validateOnChange={false}
      initialValues={initialValues}
      onSubmit={async (values) => {
        const response = await submit(values)
        if (response.ok) {
          onSuccess && onSuccess(response.payload)
        } else {
          onError && onError(response.error)
          !onError && notifyError(response.error)
        }
      }}
    >
      {(f) => (
        <Form {...formProps}>
          {typeof children === ""function"" ? children(f) : children}
        </Form>
      )}
    </Formik>)
}
");

            File.WriteAllText(path + "api.tsx", imports.ToString());
            File.AppendAllText(path + "api.tsx", queryInputs.ToString());
            File.AppendAllText(path + "api.tsx", queryOutputs.ToString());
            File.AppendAllText(path + "api.tsx", actionOutputs.ToString());
            File.AppendAllText(path + "api.tsx", actionInputs.ToString());

            Console.WriteLine("Rendered api " + options.Path);
        }