public static void AddDependencyImports <T>(this TypeScriptFile file, TypeScriptTemplateBase <T> template)
        {
            var dependencies = template.GetTemplateDependencies().Select(template.ExecutionContext.FindTemplateInstance <ITemplate>).Distinct();

            foreach (var dependency in dependencies)
            {
                if (!(dependency is IClassProvider))
                {
                    continue;
                }

                if (string.IsNullOrWhiteSpace(((IClassProvider)dependency).ClassName))
                {
                    continue;
                }

                file.AddImportIfNotExists(
                    className: ((IClassProvider)dependency).ClassName,
                    location: GetRelativePath(template, dependency));
            }

            foreach (var import in template.Imports)
            {
                file.AddImportIfNotExists(
                    className: import.Type,
                    location: import.Location);
            }
            file.NormalizeImports();
        }
Beispiel #2
0
    private Task generateEntities(AppApiTemplate appTemplate)
    {
        var tsFile = new TypeScriptFile($"{appTemplate.Name}Entities.d.ts", createStream);

        tsFile.AddLine();
        foreach (var objectTemplate in appTemplate.ObjectTemplates(ApiCodeGenerators.TypeScript))
        {
            tsFile.AddLine($"interface I{objectTemplate.DataType.Name} {{");
            tsFile.Indent();
            foreach (var property in objectTemplate.PropertyTemplates)
            {
                var tsType = getTsType(property.ValueTemplate);
                tsFile.AddLine($"{property.Name}: {tsType};");
            }
            tsFile.Outdent();
            tsFile.AddLine("}");
        }
        foreach (var numericValueTemplate in appTemplate.NumericValueTemplates(ApiCodeGenerators.TypeScript))
        {
            tsFile.AddLine($"interface I{numericValueTemplate.DataType.Name} {{");
            tsFile.Indent();
            tsFile.AddLine($"Value: number;");
            tsFile.AddLine($"DisplayText: string;");
            tsFile.Outdent();
            tsFile.AddLine("}");
        }
        return(tsFile.Output());
    }
Beispiel #3
0
    private Task generateNumericValue(NumericValueTemplate numericValueTemplate)
    {
        var tsFile    = new TypeScriptFile($"{numericValueTemplate.DataType.Name}.ts", createStream);
        var className = numericValueTemplate.DataType.Name;

        tsFile.AddLine("import { NumericValue } from '@jasonbenfield/sharedwebapp/NumericValue';");
        tsFile.AddLine("import { NumericValues } from '@jasonbenfield/sharedwebapp/NumericValues';");
        tsFile.AddLine();
        tsFile.AddLine($"export class {className}s extends NumericValues<{className}> {{");
        tsFile.Indent();
        tsFile.AddLine("constructor(");
        tsFile.Indent();
        var valueNames    = numericValueTemplate.Values.Select(v => whitespaceRegex.Replace(v.DisplayText, ""));
        var lastValueName = valueNames.Last();

        foreach (var valueName in valueNames)
        {
            tsFile.AddLine($"public readonly {valueName}: {className}");
            if (valueName != lastValueName)
            {
                tsFile.Append(",");
            }
        }
        tsFile.Outdent();
        tsFile.AddLine(") {");
        tsFile.Indent();
        var joinedValueNames = string.Join(",", valueNames);

        tsFile.AddLine($"super([{joinedValueNames}]);");
        tsFile.Outdent();
        tsFile.AddLine("}");
        tsFile.Outdent();
        tsFile.AddLine("}");
        tsFile.AddLine();
        tsFile.AddLine($"export class {className} extends NumericValue implements I{className} {{");
        tsFile.Indent();
        tsFile.AddLine($"public static readonly values = new {className}s(");
        tsFile.Indent();
        var lastValue = numericValueTemplate.Values.Last();

        foreach (var numericValue in numericValueTemplate.Values)
        {
            tsFile.AddLine($"new {className}({numericValue.Value}, '{numericValue.DisplayText.Replace("'", "\'")}')");
            if (numericValue != lastValue)
            {
                tsFile.Append(",");
            }
        }
        tsFile.Outdent();
        tsFile.AddLine(");");
        tsFile.AddLine();
        tsFile.AddLine("private constructor(Value: number, DisplayText: string) {");
        tsFile.Indent();
        tsFile.AddLine("super(Value, DisplayText);");
        tsFile.Outdent();
        tsFile.AddLine("}");
        tsFile.Outdent();
        tsFile.AddLine("}");
        return(tsFile.Output());
    }
        public void TestFileWithManyComponentOfDifferentTypesInterleaved()
        {
            // arrange
            var typescriptFile =
                new TypeScriptFile
            {
                Contents = new List <TypeScriptDeclarationOrStatement>
                {
                    new Mocks.TypeScriptDeclarationOrStatementMock1("componentT1A;"),
                    new Mocks.TypeScriptDeclarationOrStatementMock2("componentT2A;"),
                    new Mocks.TypeScriptDeclarationOrStatementMock1("componentT1B;")
                }
            };
            var expectedOutput =
                "componentT1A;" + Environment.NewLine +
                Environment.NewLine +
                "componentT2A;" + Environment.NewLine +
                Environment.NewLine +
                "componentT1B;" + Environment.NewLine;
            var actualOutput = new StringWriter();

            // act
            typescriptFile.Generate(actualOutput);

            // assert
            Assert.Equal(expectedOutput, actualOutput.ToString());
        }
        public void TestEmptyFile()
        {
            // arrange
            var typescriptFile = new TypeScriptFile();
            var expectedOutput = "";
            var actualOutput   = new StringWriter();

            // act
            typescriptFile.Generate(actualOutput);

            // assert
            Assert.Equal(expectedOutput, actualOutput.ToString());
        }
        public void TestFileWithOneComponent()
        {
            // arrange
            var typescriptFile =
                new TypeScriptFile
            {
                Contents = new List <TypeScriptDeclarationOrStatement>
                {
                    new Mocks.TypeScriptDeclarationOrStatementMock1("component1;")
                }
            };
            var expectedOutput = "component1;" + Environment.NewLine;
            var actualOutput   = new StringWriter();

            // act
            typescriptFile.Generate(actualOutput);

            // assert
            Assert.Equal(expectedOutput, actualOutput.ToString());
        }
Beispiel #7
0
    private async Task generateApp(AppApiTemplate appTemplate)
    {
        var appClassName = $"{appTemplate.Name}AppApi";
        var tsFile       = new TypeScriptFile(appClassName, createStream);

        tsFile.AddLine();
        tsFile.AddLine("import { AppApi } from \"@jasonbenfield/sharedwebapp/Api/AppApi\";");
        tsFile.AddLine("import { AppApiEvents } from \"@jasonbenfield/sharedwebapp/Api/AppApiEvents\";");
        foreach (var groupTemplate in appTemplate.GroupTemplates)
        {
            var groupClassName = getGroupClassName(groupTemplate);
            tsFile.AddLine($"import {{ {groupClassName} }} from \"./{groupClassName}\";");
        }
        tsFile.AddLine();
        tsFile.AddLine($"\r\nexport class {appClassName} extends AppApi {{");
        tsFile.Indent();
        //tsFile.AddLine($"public static readonly DefaultVersion = '{versionKey.Value}';");
        //tsFile.AddLine();
        tsFile.AddLine($"constructor(events: AppApiEvents) {{");
        tsFile.Indent();
        tsFile.AddLine($"super(events, '{appTemplate.Name}');");
        foreach (var groupTemplate in appTemplate.GroupTemplates)
        {
            tsFile.AddLine($"this.{groupTemplate.Name} = this.addGroup((evts, resourceUrl) => new {groupTemplate.Name}Group(evts, resourceUrl));");
        }
        tsFile.Outdent();
        tsFile.AddLine("}");
        tsFile.AddLine();
        foreach (var group in appTemplate.GroupTemplates)
        {
            tsFile.AddLine($"readonly {group.Name}: {group.Name}Group;");
        }
        tsFile.Outdent();
        tsFile.AddLine("}");
        await tsFile.Output();
    }
Beispiel #8
0
    private Task generateComplexField(IComplexField complexField, bool isForm)
    {
        var className = complexField.TypeName;
        var tsFile    = new TypeScriptFile(className, createStream);

        if (isForm)
        {
            tsFile.AddLine("import { BaseForm } from '@jasonbenfield/sharedwebapp/Forms/BaseForm';");
        }
        else
        {
            tsFile.AddLine("import { ComplexFieldFormGroup } from '@jasonbenfield/sharedwebapp/Forms/ComplexFieldFormGroup';");
        }
        tsFile.AddLine($"import {{ {className}View }} from './{className}View';");
        var fields = complexField.Fields;

        if (fields.OfType <DropDownFieldModel>().Any())
        {
            tsFile.AddLine("import { DropDownFieldItem } from \"@jasonbenfield/sharedwebapp/Forms/DropDownFieldItem\";");
        }
        foreach (var field in fields.OfType <ComplexFieldModel>().Select(f => f.TypeName).Distinct())
        {
            tsFile.AddLine($"import {{ {field} }} from './{field}';");
        }
        tsFile.AddLine();
        var baseClass = isForm ? "BaseForm" : "ComplexFieldFormGroup";

        tsFile.AddLine($"export class {className} extends {baseClass} {{");
        tsFile.Indent();
        tsFile.AddLine($"protected readonly view: {className}View;");
        tsFile.AddLine();
        var ctorArgs    = isForm ? "" : "prefix: string, name: string, ";
        var vmClassName = isForm ? "FormComponentViewModel" : "BlockViewModel";

        tsFile.AddLine($"constructor({ctorArgs}view: {className}View) {{");
        tsFile.Indent();
        var superArgs = isForm ? $"'{className}'" : "prefix, name";

        tsFile.AddLine($"super({superArgs}, view);");
        foreach (var field in fields)
        {
            if (!string.IsNullOrWhiteSpace(field.Caption))
            {
                tsFile.AddLine($"this.{field.Name}.setCaption('{field.Caption}');");
            }
            if (field is SimpleFieldModel simpleField)
            {
                if (!simpleField.IsNullAllowed)
                {
                    tsFile.AddLine($"this.{field.Name}.constraints.mustNotBeNull();");
                }
                foreach (var constraint in simpleField.Constraints)
                {
                    if (constraint is NotWhitespaceConstraintModel)
                    {
                        tsFile.AddLine($"this.{field.Name}.constraints.mustNotBeWhitespace('{constraint.FailureMessage}');");
                    }
                    else if (constraint is LowerRangeConstraintModel lowerRange)
                    {
                        var lowerValue = valueToJsLiteral(lowerRange.Value);
                        if (lowerRange.IsIncluded)
                        {
                            tsFile.AddLine($"this.{field.Name}.constraints.mustBeOnOrAbove({lowerRange.Value}, '{constraint.FailureMessage}');");
                        }
                        else
                        {
                            tsFile.AddLine($"this.{field.Name}.constraints.mustBeAbove({lowerRange.Value}, '{constraint.FailureMessage}');");
                        }
                    }
                    else if (constraint is UpperRangeConstraintModel upperRange)
                    {
                        var upperValue = valueToJsLiteral(upperRange.Value);
                        if (upperRange.IsIncluded)
                        {
                            tsFile.AddLine($"this.{field.Name}.constraints.mustBeOnOrBelow({upperRange.Value}, '{constraint.FailureMessage}');");
                        }
                        else
                        {
                            tsFile.AddLine($"this.{field.Name}.constraints.mustBeBelow({upperRange.Value}, '{constraint.FailureMessage}');");
                        }
                    }
                }
            }
            if (field is InputFieldModel inputField)
            {
                if (inputField.MaxLength.HasValue)
                {
                    tsFile.AddLine($"this.{field.Name}.setMaxLength({inputField.MaxLength});");
                }
                if (inputField.IsProtected)
                {
                    tsFile.AddLine($"this.{field.Name}.protect();");
                }
            }
            else if (field is DropDownFieldModel dropDownField)
            {
                if (!string.IsNullOrWhiteSpace(dropDownField.ItemCaption))
                {
                    tsFile.AddLine($"this.{field.Name}.setItemCaption('{dropDownField.ItemCaption}');");
                }
                var dropDownItems = dropDownField.Items;
                if (dropDownItems?.Any() == true)
                {
                    tsFile.AddLine($"this.{field.Name}.setItems(");
                    tsFile.Indent();
                    var inputType = dropDownField.InputDataType;
                    var lastItem  = dropDownItems.Last();
                    foreach (var dropDownItem in dropDownItems)
                    {
                        var itemValue = valueToJsLiteral(dropDownItem.Value);
                        tsFile.AddLine($"new DropDownFieldItem({itemValue}, '{dropDownItem.DisplayText}')");
                        if (dropDownItem != lastItem)
                        {
                            tsFile.Append(",");
                        }
                    }
                    tsFile.Outdent();
                    tsFile.AddLine(");");
                }
            }
        }
        tsFile.Outdent();
        tsFile.AddLine("}");
        foreach (var field in fields)
        {
            tsFile.AddLine("");
            tsFile.Append($"readonly {field.Name} = ");
            if (field is IComplexField complex)
            {
                tsFile.Append($"this.addFormGroup(new {complex.TypeName}(this.getName(), '{field.Name}', this.view.{field.Name}))");
            }
            else
            {
                string addFormGroup;
                if (field is InputFieldModel inputField)
                {
                    var inputType = inputField.InputDataType;
                    if (inputType == typeof(int?) || inputType == typeof(decimal?))
                    {
                        addFormGroup = "addNumberInputFormGroup";
                    }
                    else if (inputType == typeof(DateTimeOffset?))
                    {
                        addFormGroup = "addDateInputFormGroup";
                    }
                    else if (inputType == typeof(string))
                    {
                        addFormGroup = "addTextInputFormGroup";
                    }
                    else
                    {
                        throw new NotSupportedException($"Simple field with input type {inputType} is not supported");
                    }
                }
                else if (field is DropDownFieldModel dropDownField)
                {
                    var inputType   = dropDownField.InputDataType;
                    var tsInputType = getTsType(inputType);
                    if (inputType == typeof(int?) || inputType == typeof(decimal?))
                    {
                        addFormGroup = "addNumberDropDownFormGroup";
                    }
                    else if (inputType == typeof(DateTimeOffset?))
                    {
                        addFormGroup = "addDateDropDownFormGroup";
                    }
                    else if (inputType == typeof(string))
                    {
                        addFormGroup = "addTextDropDownFormGroup";
                    }
                    else if (inputType == typeof(bool?))
                    {
                        addFormGroup = "addBooleanDropDownFormGroup";
                    }
                    else
                    {
                        throw new NotSupportedException($"DropDown field with input type {inputType} is not supported");
                    }
                }
                else if (field is SimpleFieldModel hiddenField)
                {
                    var inputType = hiddenField.InputDataType;
                    if (inputType == typeof(int?) || inputType == typeof(decimal?))
                    {
                        addFormGroup = "addHiddenNumberFormGroup";
                    }
                    else if (inputType == typeof(DateTimeOffset?))
                    {
                        addFormGroup = "addHiddenDateFormGroup";
                    }
                    else if (inputType == typeof(string))
                    {
                        addFormGroup = "addHiddenTextFormGroup";
                    }
                    else
                    {
                        throw new NotSupportedException($"Simple field with input type {inputType} is not supported");
                    }
                }
                else
                {
                    throw new NotSupportedException($"Simple field of type {field.GetType()} is not supported");
                }
                tsFile.Append($"this.{addFormGroup}('{field.Name}', this.view.{field.Name})");
            }
            tsFile.Append(";");
        }
        tsFile.Outdent();
        tsFile.AddLine("}");
        return(tsFile.Output());
    }
Beispiel #9
0
    private Task generateGroup(AppApiGroupTemplate group)
    {
        var groupClassName = getGroupClassName(group);
        var tsFile         = new TypeScriptFile(groupClassName, createStream);

        tsFile.AddLine();
        tsFile.AddLine("import { AppApiGroup } from \"@jasonbenfield/sharedwebapp/Api/AppApiGroup\";");
        tsFile.AddLine("import { AppApiAction } from \"@jasonbenfield/sharedwebapp/Api/AppApiAction\";");
        tsFile.AddLine("import { AppApiView } from \"@jasonbenfield/sharedwebapp/Api/AppApiView\";");
        tsFile.AddLine("import { AppApiEvents } from \"@jasonbenfield/sharedwebapp/Api/AppApiEvents\";");
        tsFile.AddLine("import { AppResourceUrl } from \"@jasonbenfield/sharedwebapp/Api/AppResourceUrl\";");
        foreach (var form in group.FormTemplates())
        {
            tsFile.AddLine($"import {{ {form.Form.TypeName} }} from \"./{form.Form.TypeName}\";");
        }
        tsFile.AddLine();
        var implementsClause = group.IsUser()
            ? "implements IUserGroup "
            : "";

        tsFile.AddLine($"export class {groupClassName} extends AppApiGroup {implementsClause}{{");
        tsFile.Indent();
        tsFile.AddLine("constructor(events: AppApiEvents, resourceUrl: AppResourceUrl) {");
        tsFile.Indent();
        tsFile.AddLine($"super(events, resourceUrl, '{group.Name}');");
        foreach (var action in group.ActionTemplates)
        {
            if (action.IsView() || action.IsPartialView() || action.IsRedirect())
            {
                var modelType = getTsType(action.ModelTemplate);
                tsFile.AddLine($"this.{action.Name} = this.createView<{modelType}>('{action.Name}');");
            }
            else
            {
                var genericArgs = getGenericArguments(action);
                tsFile.AddLine($"this.{action.Name}Action = this.createAction{genericArgs}('{action.Name}', '{action.FriendlyName}');");
            }
        }
        tsFile.Outdent();
        tsFile.AddLine("}");
        tsFile.AddLine();
        foreach (var action in group.ActionTemplates)
        {
            if (action.IsView() || action.IsPartialView() || action.IsRedirect())
            {
                var modelType = getTsType(action.ModelTemplate);
                tsFile.AddLine($"readonly {action.Name}: AppApiView<{modelType}>;");
            }
            else
            {
                var genericArgs = getGenericArguments(action);
                tsFile.AddLine($"readonly {action.Name}Action: AppApiAction{genericArgs};");
            }
        }
        tsFile.AddLine();
        foreach (var action in group.ActionTemplates)
        {
            if (!action.IsView() && !action.IsPartialView() && !action.IsRedirect())
            {
                var modelType = getTsType(action.ModelTemplate.DataType);
                var modelDecl = action.HasEmptyModel() ? "" : $"model: {modelType}, ";
                var modelParm = action.HasEmptyModel() ? "{}" : "model";
                tsFile.AddLine($"{action.Name}({modelDecl}errorOptions?: IActionErrorOptions) {{");
                tsFile.Indent();
                tsFile.AddLine($"return this.{action.Name}Action.execute({modelParm}, errorOptions || {{}});");
                tsFile.Outdent();
                tsFile.AddLine("}");
            }
        }
        tsFile.Outdent();
        tsFile.AddLine("}");
        return(tsFile.Output());
    }
Beispiel #10
0
    private Task generateComplexFieldView(IComplexField complexField, bool isForm)
    {
        var className = $"{complexField.TypeName}View";
        var tsFile    = new TypeScriptFile(className, createStream);

        if (isForm)
        {
            tsFile.AddLine("import { BaseFormView } from '@jasonbenfield/sharedwebapp/Forms/BaseFormView';");
        }
        else
        {
            tsFile.AddLine("import { ComplexFieldFormGroupView } from '@jasonbenfield/sharedwebapp/Forms/ComplexFieldFormGroupView';");
        }
        var fields = complexField.Fields;

        foreach (var field in fields.OfType <ComplexFieldModel>().Select(f => f.TypeName).Distinct())
        {
            tsFile.AddLine($"import {{ {field}View }} from './{field}View';");
        }
        tsFile.AddLine();
        var baseClass = isForm ? "BaseFormView" : "ComplexFieldFormGroupView";

        tsFile.AddLine($"export class {className} extends {baseClass} {{");
        tsFile.Indent();
        foreach (var field in fields)
        {
            tsFile.AddLine("");
            tsFile.Append($"readonly {field.Name} = ");
            if (field is IComplexField complex)
            {
                tsFile.Append($"this.addFormGroup(new {complex.TypeName}View())");
            }
            else
            {
                string addFormGroup;
                if (field is InputFieldModel inputField)
                {
                    addFormGroup = "addInputFormGroup";
                }
                else if (field is DropDownFieldModel dropDownField)
                {
                    var inputType   = dropDownField.InputDataType;
                    var tsInputType = getTsType(inputType);
                    addFormGroup = $"addDropDownFormGroup<{tsInputType}>";
                }
                else if (field is SimpleFieldModel hiddenField)
                {
                    addFormGroup = "addInputFormGroup";
                }
                else
                {
                    throw new NotSupportedException($"Simple field of type {field.GetType()} is not supported");
                }
                tsFile.Append($"this.{addFormGroup}()");
            }
            tsFile.Append(";");
        }
        tsFile.Outdent();
        tsFile.AddLine("}");
        return(tsFile.Output());
    }