Reimagine dotnet-new (dotnet new enhanchements 2) #2052

Closed
blackdwarf opened this Issue Mar 25, 2016 · 35 comments

Projects

None yet
@blackdwarf
Contributor

As it currently stands, dotnet new is a good command for people to get started with simple projects. However, it suffers from several things:

  • Templates are coupled with the implementation, thus requiring a new release of the CLI when adding/changing templates
  • Extending templates is tough
  • No support for naming projects via the command line

This issue suggests a new form of dotnet new that solves the above problems. It also replaces the old #655 that will be closed in favor of this one.

/cc @DamianEdwards @piotrpMSFT @Eilon @enricosada

.NET Core CLI: new

The new command is used to create new .NET Core projects. Projects are created using templates installed via NuGet packages.

Templates are laid out in their NuGet packages in a hierarchical fashion that enables the creation of simple trees of templates
with categories that are then merged across the installed template packages to create an overall template tree. This tree allows
the display of an interactive menu when creating new projects, while also allowing for direct project creation using a template
node's full path, e.g. dotnet new -t aspnetcore/mvc/empty

As templates are in normal NuGet packages and installed into the standard NuGet packages folder, they can depend on other NuGet
packages that are used in their templates, such that when they're installed, all the required packages to create new projects using
the contained templates are installed too.

Using the Command Line

Displaying help:

~/code> dotnet new --help
    Usage:
        dotnet new
        dotnet new -t|--template <template/path> [-n|--name <ProjectName>] [[-v|--variable <Name> <Value>] [-v|--variable <Name> <Value>]]
        dotnet new <arguments>

    Arguments:
        -t|--template <template/path>                 The path of the template to use
        -n|--name <ProjectName>                       The name of the new project, created in current folder if not specified
        -v|--variable <Name> <Value>                  Variable to be passed to the template (can be specified more than once)

        -l|--list                                     Lists installed templates
        -i|--install <TemplatePackageId> [version]    Installs templates from a package with optional version
        -i|--install <TemplatePackagePath>            Installs templates from a package
        -u|--uninstall <TemplatePackageId>            Uninstalls a templates package
        -r|--restore                                  Restores installed template packages

Listing installed templates:

~/code> dotnet new --list

MS.DotNetCore.New.Templates 1.0.0
  - Console Application    [console]
  - Class Library          [classlib]

~/code> 

Installing new templates from a package:

~/code> dotnet new --install MS.DotNetCore.New.Templates.AspNetCore 1.0.0
Installing templates from MS.DotNetCore.New.Templates.AspNetCore

Installed 3 templates:
  - ASP.NET Core Empty Application          [aspnetcore/empty]
  - ASP.NET Core MVC Web Site               [aspnetcore/mvc/website]
  - ASP.NET Core MVC Web API Application    [aspnetcore/mvc/webapi]

~/code> dotnet new --list

MS.DotNetCore.New.Templates 1.0.0
  - Console Application    [console]
  - Class Library          [classlib]

MS.DotNetCore.New.Templates.AspNetCore 1.0.0
  - ASP.NET Core Empty Application          [aspnetcore/empty]
  - ASP.NET Core MVC Web Site               [aspnetcore/mvc/website]
  - ASP.NET Core MVC Web API Application    [aspnetcore/mvc/webapi]

~/code> 

Creating a new project with a specific template:

~/code> mkdir MyApp && cd MyApp
~/code/MyApp> dotnet new --template console

Created project "MyApp" in ~/code/MyApp

~/code/MyApp> cd ../
~/code> dotnet new -t console --name MyApp2

Created project "MyApp2" in ~/code/MyApp2

~/code> dotnet new -t aspnetcore/mvc/website --name MyWebApp

Created project "MyWebApp" in ~/code/MyWebApp

~/code> 

Creating a new project using the template menu:

~/code> dotnet new

  Templates
  -----------------------------------
  1. Console Application [console]
  2. Class Library       [classlib]
  3. ASP.NET Core        [aspnetcore]

Select a template [1]: 1
Enter a project name [Project1]: MyApp

Created project "MyApp" in ~/code/MyApp

~/code/MyApp> cd../
~/code> dotnet new

  Templates
  -----------------------------------
  1. Console Application [console]
  2. Class Library       [classlib]
  3. ASP.NET Core        [aspnetcore]

Select a template [1]: 3

  ASP.NET Core Templates
  -----------------------------------
  1. ASP.NET Core Empty Application [aspnetcore/empty]
  2. ASP.NET Core MVC               [aspnetcore/mvc]

Select an ASP.NET Core template [1]: 2

  ASP.NET Core MVC Templates
  -----------------------------------
  1. ASP.NET Core MVC Empty               [aspnetcore/mvc/empty]
  2. ASP.NET Core MVC Web Site            [aspnetcore/mvc/website]
  3. ASP.NET Core MVC Web API Application [aspnetcore/mvc/webapi]

Select an ASP.NET Core MVC template [1]: 2
Enter a project name [Project1]: MyWebApp

Created "MyWebApp" in ~/code/MyWebApp

~/code/MyWebApp> 

Authoring Templates

Templates are placed in NuGet packages in the content/templates folder. Optionally, in the root of the folder is a templates.json file
that describes the templates contained and if present is used to enhance the project creation experience.

The layout allows for multi-level category nodes with template nodes as the leaves. In the case a category node contains just a single template
node, that template will be automatically selected when the category is chosen.

Example templates.json

Example templates.json for the default templates:

{
    "projectTemplates": {
        "console": {
            "title": "Console Application",
            "children": {
                "csharp": {
                    "title": "Console Application (C#)",
                    "variables": {
                        "framework": {
                            "question": "Please select a target framework",
                            "default": "netstandardapp1.5",
                            "conditions": {
                                "rid": ["win7"]
                            }
                        }
                    }
                }
            }
        },
        "classlib": {
            "title": "Class Library",
            "children": {
                "csharp": {
                    "title": "Class Library (C#)",
                    "variables": {
                        "framework": {
                            "question": "Please select a target framework",
                            "default": "netstandardapp1.5",
                            "conditions": {
                                "rid": ["win7"]   
                            }
                        }
                    }
                }
            }
        }
    }
}

Package Files Layout

The following demonstrates the layout of template NuGet packages. Note how templates from separate packages can share their structure
such that they're templates can occupy the same parts of the resultant installed template "tree".

~/.nuget/
    packages/
        Microsoft.NETCore.New.Templates/
            templates/
                templates.json
                console/
                    csharp/
                        files/
                            Program.cs
                classlib/
                    csharp/
                        files/
                            project.json
                            Program.cs
        Microsoft.NETCore.New.Templates.VB/
            templates/
                templates.json
                console/
                    vb/
                        files/
                            project.json
                            Program.vb
                classlib/
                    vb/
                        files/
                            projext.json
                            Program.vb
        Microsoft.NETCore.New.Templates.AspNetCore/
            templates/
                templates.json
                aspnetcore/
                    empty/
                        csharp/
                            files/
                                project.json
                                Program.cs
                    mvc/
                        empty/
                            csharp/
                                files/
                                    project.json
                                    Program.cs
                                    Startup.cs
                                    Controllers/
                                        HomeController.cs
                                    wwwroot/
                        website/
                            csharp/
                        webapi/
                            csharp/
@blackdwarf blackdwarf added this to the RC2-Feb milestone Mar 25, 2016
@DamianEdwards
Collaborator

There is a functional prototype at https://github.com/DamianEdwards/dotnet-new2

@Eilon
Member
Eilon commented Mar 25, 2016

@ajaybhargavb this is for you.

@enricosada
Collaborator

nice having a way for templates outside instead of embedded resources πŸ‘

I'd like to add the feedback from old thread #655:

Make the common use case short using positional arguments (in addition to named arg)

the most common use case: dotnet new name type ( about --lang, see next comment )

so these are equivalent:

  • dotnet new hello_world console
  • dotnet new --name hello_world --type console

some examples in #1124 (comment)

Using another language is ok with --lang ( or -l )

dotnet new hello_world console --lang f#

or if you want, the old proposal was [lang]/[type] like

dotnet new hello_world fsharp/console

@enricosada
Collaborator

About other languages support ( just to check my use case ):

  • templates support multiple languages using subdirectory, nice, πŸ‘ βœ…
  • --list should output all templates installed, --list --lang f# only the templates who support f#
  • --list --lang vb should show grouped templates ( like aspnetcore ) if at least one of aspnetcore subtemplates support vb (let's filter out useless groups, but i'd like aspnet to support f# πŸ˜„ )

That's useful not only for people who use only one language, but for poliglot who have multiple languages installed (i use c# and f#, both a lot)

Now the bonus points (suggestions) for a better cross language experience.
The c# is the default language, np about that, there is --lang and it's ok (was already discussed and it's ok).
But i think we can improve experience for others without changing c# experience:

  • support something (env var, global settings) to have a default language (#1118) for dotnet-new
    for example an env var DOTNET_DEFAULT_LANGUAGE (see #1118 (comment) )
  • --list write name of supported languages near template name
~/code> dotnet new --list

MS.DotNetCore.New.Templates 1.0.0
  - Console Application    [console] (C#, F#)
  - Class Library              [classlib]  (C#, F#)

MS.DotNetCore.New.Templates.AspNetCore 1.0.0
  - ASP.NET Core Empty Application          [aspnetcore/empty]   (C#, VB)
  • write language name on output, it's easier to see the default or chosen language
Created C# project "MyApp" in ~/code/MyApp
Created F# project "MyApp" in ~/code/MyApp
@enricosada
Collaborator

about the issue load templates from nuget (last feedback i promise).

  • when i install a template, it's global or per .net cli? i dont want to mess the global config when i install a package, for example in a ci scenario with multiple .net cli installed by directory.
  • if i want to add more nuget feeds (personal/work templates), i need to modify global nuget.config? or add a local nuget.config in working directory?
  • it's possibile to install multiple version of same package? ( TemplatePackageId? )
  • -u|--uninstall <TemplatePackageId> should have a [version] optional argument, because maybe i want to have multiple version of same package (bugfix, stable vs dev) and i want to install/uninstall specific versions

The following is more a request, and maybe it's already like that.

Not everyone use nuget always, or publish to default nuget.org feed
I understand the pro, it's nice for discoverability/versioning/tag/author etc but it's a big requirement a zip files with some static template files inside (and no dependencies).

i see --install <TemplatePackagePath> i hope that mean i can install from a package directly (using url or file path).

It's possibile to not bind the implementation to nuget packages only? templates are only a zip file with a structure and a template.json, we can consume it as zip file, no need to parse as nuget packages.

It's nice if i can just install a zip from url (from example from a github repo zip like https://github.com/dotnet/cli/archive/rel/1.0.0.zip ) or a local directory zipped.
Offline help a lot too, no need for directory + project.json + dotnet pack.

@blackdwarf blackdwarf modified the milestone: 1.0.0-rtm, 1.0.0-rc2 Mar 25, 2016
@jchannon
Contributor

Pinging @sayedihashimi just in case he hasn't seen this...(although I'm sure he has)

@muratg
Member
muratg commented Mar 29, 2016

πŸ‘€

@sayedihashimi
Member

Thoughts on command line project creation

With dotnew new we are interested in enabling an experience for users to create new projects from the command line. Creating projects from the command line is not a new concept, it's been around for a while especially on the web development side. One example of a successful command line project generator is yeoman. Yeoman is a super popular tool for developers, and if dotnet new is as successful as yeoman, IMO, that would be a really wonderful thing. To enable a meaningful discussion on dotnet new let's start by taking a look at a typical user experience when using yeoman to create a new project. Below are typical steps a user would take to create a new project from the command line.

Steps to create a new project from yeoman

  1. Install yeoman and it's dependencies
  2. Search for generator
  3. Install desired generator
  4. Invoke generator
  5. Enter info about the project; i.e. project name, dest dir and other options
  6. Generate the project (happens automatically after the previous step)

From the set of steps above, we can break this down into the following categories.

  1. Template discovery (local and remote)
  2. Command line user interaction - For example; selecting a generator, project name, dest dir, other project options, displaying help, etc.
  3. Going from template -> generated project

I think that dotnet new should focus on the overall end user experience. Which consists of the following, Template discovery and Command line interaction. When the user selects and invokes a template, dotnet new will handle prompting for project properties and then call the generator (with a consistent way to pass project properties). The template should be able to use any "template generator" that it desires. dotnet new will have a template generator available out of the box which can be used. For scenarios in which the default template generator doesn't meet the needs, it can always be switched out for a different implementation.

In my case I work on delivering templates in the following areas.

  • ASP.NET templates in Visual Studio
  • ASP.NET templates in yo aspnet
  • ASP.NET templates in dotnet new (future)

Today for VS and yo aspnet we have to author templates once for VS and once for yo aspnet. In addition the template infrastructure that we use for the VS side is very fragile and difficult to maintain. Once we enable templates in dotnet new it's yet another place for us to invest in creating templates. Instead of maintaining three different templates I'd like to have one way to create templates from the command line, and then reuse that tool everywhere. So we author templates once in dotnet new which enables the command line experience with dotnet new. Then in Visual Studio we create "shim templates" which will allow us to present the UI to gather parameters and then call into dotnet new for the project creation process. For yo aspnet we would do a similar thing.

Ideas for implementation

diagram

Brokers are decoupled from specific generators via a set of C# interfaces. This will allow us to maximize reusability of generators in different scenarios including; command line, Visual Studio, yeoman, etc.

Here is a typical user session when creating a new project and a high level of what happens.

Setup

When dotnet-new or Visual Studio are installed the following happens.

  1. Broker add template sources
  2. Broker adds generators

Users can add additional sources to enable using additional generators.

Project Creation

  1. Use uses broker to list templates
    1. Broker "collects" template sources
    2. Broker asks each generator for info on available templates for each template source
  2. User asks broker to create using a selected template (X)
    1. Broker "collects" template sources
    2. Broker finds generator for X
      1. Broker asks each generator if it supports X and stops when a match is found (or some other heuristic to find the single generator to use)
    3. Broker gets info on the given template from the generator
      • Contains metadata to display about the template
      • Contains info about template parameters
  3. Broker gathers info for generation
    1. Get info from the user (i.e. project name, destination, etc.)
    2. Get info from the environment and target
  4. Broker asks the generator to create using a given template
    1. Broker passes the properties acquired from the previous step to the generator to start generation
  5. Broker completes experience
    • VS Broker opens the project in Visual Studio
    • Command line broker displays a success message and more info on next steps

FAQ

Q: Does this mean yo aspnet will go away?

A: Absolutely not. yo aspnet has had some wonderful success it's not going anywhere. The idea is that we will create a self-contained template tool and then plug that into: VS, dotnet new, yo aspnet, etc. By allowing us to author templates once it will allow us to bring a better experience in yo aspnet. And it will enable us to keep templates in sync at all times across all end-user experiences.

Q: Why not just use yeoman here?

A: Yeoman has a lot of dependencies which are required to use it. It makes sense to have an entry point in yeoman because many people already have it installed (or some of it's dependencies). There are drawbacks from shipping/using yeoman directly by Visual Studio include the following.

  • Yeoman has a lot of dependencies. This increases the download size, install time, and also introduces issues where each dependency needs to be reviewed to ensure we don't violate any licenses.
  • If a bug is found in a dependency it may take a while for that to get fixed, especially if it's a platform specific bug. For example OmniSharp/generator-aspnet#351, which was opened in Sept 2015, and we are just now able to get the fix out.
  • Since the dependency graph for yeoman is deep there are issues when different versions of dependencies don't work together well. For example: OmniSharp/generator-aspnet#582 and OmniSharp/generator-aspnet#580
  • In addition to this, authoring templates can be made a lot easier than what's required with yeoman today.
@rschiefer

@sayedihashimi I like this approach, supporting existing solutions with a new abstraction (dotnet new) and making VS templates easier by providing a shim to dotnet new templates. Also agree Yeoman has too much baggage to be the official solution. Well documented and thought out!

@blackdwarf
Contributor

@sayedihashimi can you add some examples of invocation of this new dotnet new you imagined on the command line? I'm fine with VS, but would love to see how complex/simple this would be in the shell.

@sayedihashimi
Member

@blackdwarf the specific user interaction is orthogonal to my ideas. What you have above should work with my proposal. Since there is an abstraction between the broker (dotnet new) and the template generator, you should be able to define the end user experience completely.

I'd like to make sure the local template story is good, so we may need to add more to what you have.

@blackdwarf
Contributor

@sayedihashimi ah, I see. When you say a "local template story", you mean templates that are installed with the SDK/other?

@sayedihashimi
Member

@blackdwarf

@sayedihashimi ah, I see. When you say a "local template story", you mean templates that are installed with the SDK/other?

I should have said "local template dev story". Let me explain by example.

When developing templates in Visual Studio with SideWaffle/TemplateBuilder I can CTRL+F5 (or F5) to start the VS experimental instance with my template vsix loaded.

When developing templates with yeoman I can use npm link to point to my local copy of yo aspnet so that I can try the templates via yo aspnet during development.

When developing templates for dotnet new there should be a similar story. Requiring users to create a .nupkg to share their templates widely is fine (similar to yo with npm packages), but users shouldn't have to create templates from .nupkg files locally during template development.

I think what we have here is a good start but for a complete template story we need more details like.

  • What's the local template development story?
  • What's the process of shipping a security fix to a template (ideally publish a new nuget package, and flag old versions of the package as containing a security issue then dotnet new picks up the new version)?
  • If NuGet packages are used how do users point to specific NuGet servers for templates? Do we rely on the global nuget.config here or provide users the option of adding specific feeds for dotnet new?
  • How are replacements handled, and what specific features do we need for replacements? From my prototypes and previous experience I have a good sense for what's needed. I also have OSS code from @mlorbetske which we likely can directly use. He owns the dev side of One ASP.NET so he's very interested in this.
  • What specific command line features do we need to realize scenarios from the One ASP.NET dialog (for example displaying a select one list or select many list)?
@shanselman
Contributor

What about "dotnet new http://github/shanselman/whatever/something.git" where a repo has the template

@svick
Contributor
svick commented Jun 21, 2016

@shanselman Would that effectively be just git clone http://github/shanselman/whatever/something.git && rm .git?

@shanselman
Contributor

@svick I'm thinking it'd be more like the Uppercut project years back. You'd say dotnet new "foo" gitrepo and then {{classname}} would be replaced with foo. So a clone, then a global search and replace. Brainstorming.

@blackdwarf
Contributor
blackdwarf commented Jun 21, 2016 edited

@shanselman @sayedihashimi's design actually allows for that, I think. Honestly, the more I think about it, the more it sounds like a good idea. But then why not go the full monty here and allow something like dotnet new http://tempuri.org/template/thing or similar for file shares? Brainstorming (I like this notation :))

@enricosada
Collaborator

@shanselman we can directly use github zip link as url, instead of git repo so is a dumb zip (no deps on git or github). also easier to host in S3.
The proposed format define arguments/parameters/questions, so can be used by gui or command line. That's nice (arguments) because it's not bound on a specific language.

@blackdwarf it's something someone is implementing now or can be up-for-grab from community? last time i think i read about was in progress.
Maybe a simple iteration first (zip+url) because it can help a lot to show new developer (new of .netcore) how to do something. atm are embedded in dotnet new , so each new tempalte is waiting merge (i'd like to merge F# lib for example, it's confusing for new users)

@blackdwarf
Contributor
blackdwarf commented Jun 21, 2016 edited

@enricosada at this point we are hashing out the design. Once design is in place, we will figure out the next level of details. πŸ˜ƒ

But it is interesting to do the ZIP thing. However, do take into account that not all people use GH for hosting, that is, maybe someone would like to point this to a random git repo hosted somewhere/somehow and have it work.

@enricosada
Collaborator

do take into account that not all people use GH for hosting, that is ..

@blackdwarf i was proposing zip from github repo, instead of git repo. I dont understand why we need to add git, because we dont need to manage versions or branch. Just download a zip, unzip, done.
If someone doesnt want to use github, can just upload a zip somewhere.
Really useful for bootstrap things for new users (i'd like to add http://suave.io template for example).

in v1 we can do unzip only of package. In v2 add arguments/replacements. etc. So it's working asap (i really love do have it πŸ˜„ )

@shanselman
Contributor

We should only do the Zip thing if it's a GIT thing and not a GitHub thing. We don't want a design that is tied to a single provider.

@enricosada
Collaborator

@shanselman it's a github thing ( but git archive exists). I dont see how a design tied to git, a versioning tool, instead of a dump zip files may be more complex. btw it's easier to support both scenarios.

dotnet new --from-zip "http://example.com/fable-template.zip"

and

dotnet new --from-git-repo "http://example.com/fable-templates.git"
@factormystic
Contributor

πŸ‘Ž on zip simply because it might accidentally imply that someone should throw their code up on a server somewhere in a zip file. This behavior is awful. Use of source control should be encouraged at every opportunity. Free source control providers exist, so this really isn't a test of means.

@enricosada
Collaborator

@factormystic the nuget packages are zip files, the .docx are zip files. It's an artifact. i can create it from git archive.
clone a git repo mean you need a git command line, and usually you need to specifiy a branch or tag.
I am not against it, but not as the only option.

@sayedihashimi
Member
sayedihashimi commented Jun 23, 2016 edited

Thanks for all the great comments. I'm loving the discussions here.
We are designing this so that we can accept templates from different sources. We will likely start with:

  • NuGet package
  • .zip file
  • Folder

Hopefully in the long term we can come with a story where 3rd parties can implement different template sources. We have discussed git, and I think that's a very interesting idea, which likely has a lot of potential. With that said we have to make progress in some other areas before we tackle the sources problem fully.

@enricosada

@blackdwarf it's something someone is implementing now or can be up-for-grab from community? last time i think i read about was in progress.

We are just now getting started (we are meeting today to take a deep dive on design) with the RTM (tooling) version of dotnet new. There is nothing to send a PR to at this time, but hopefully in a few weeks we will have something.

@theBoringCoder

We are designing this so that we can accept templates from different sources. We will likely start with:
β€’NuGet package
β€’.zip file
β€’Folder

Will the NuGet package support include the ability to pass a Uri to a package? My organization (county government), like many I am sure, has its' own internal NuGet server. We already get some benefit by sharing our packaged code internally between dev teams. The ability to post our project & item templates there as well would be a great benefit.

@mlorbetske
Member

@theBoringCoder the current design relies on the package existing on one of the configured feeds (managable via a nuget.config file). In your case, the private feed containing the packages of interest would just be present in that file. When the template gets "installed", the .nupkg matching the package id (maximum version) would get copied into the local cache and the contents would be usable from that location for the purposes of template creation. Updates to the templates would work in a similar way - you'd either be notified (or just have the option to update) about newer versions of the template package being discovered on the feed and have the ability to acquire and use the new template content instead of the currently cached version.

@RehanSaeed
RehanSaeed commented Jul 7, 2016 edited

I work on the ASP.NET MVC Boilerplate project template which:

  1. Shows a feature selection UI to enable/disable things like 'HTTPS Everywhere' and 'TypeScript' etc.
  2. Heavily modifies the project based on the features selected.

All of the above is done after project creation using hooks in VS. I'd love to bring this to dotnet new but:

  1. I needs hooks post project creation to run something to modify the project. I'm not clear on whether this would be possible with dotnet new.
  2. Instead of a UI for feature selection, I could switch to a console app but is that a hard requirement? Could I write a cross-platform electron shell app or even a ASP.NET Core app for feature selection instead? Given that I have a few dozen features being offered, a console app experience would be sub-optimal.
  3. Eventually I expect that VS would just call into dotnet new. There must be some sort of UI story there?
  4. It looks like the template.json file is being used to ask the user questions about which target framework they want to use. Is this file essentially acting as a manifest of features in the template?
@factormystic
Contributor

@RehanSaeed look at eslint for an example of how to implement multi step configuration decisions on the command line. Not having a GUI is fine.

@sayedihashimi
Member

@RehanSaeed good to see you here.

@RehanSaeed your needs are very similar to our own (ASP.NET team). I think everything that you mentioned will be covered, but it may be difficult for me to explain without the code samples to go along with it. We should have that in a few weeks I think but not totally sure. I'll explain how we are planning to approach this from the One ASP.NET side and then you can comment if it makes sense for you.

From a design perspective gathering inputs is on the broker side of the house. For the CLI there will be a common broker that will be used. It will run through the parameters defined by the template and then prompt the user for the values. This can be done in a generic fashion. For the same template when used in VS, the broker will be in the form of a custom wizard (we may have one that can be used but miminally we'll provide code samples for the short term and a better plan long term). The custom wizard will be responsible for showing the right UI and getting input from the user. After input is gathered it will call the generator.

Now, how does this work when a template support options like:

  • Auth
    • No Auth
    • Individual Auth
    • Org Auth
    • Win Auth

So the UI will gather the input. After that it will invoke a generator. In the One ASP.NET case we are planning to create a single template which covers the different auth options. Then in the code we will add sections to indicate whether it should apply for each auth option. For example you may see something like the following in .cs files.

#if NO_AUTH
// code here for no auth
#endif NO_AUTH
#if ORG_AUTH
// code here for org auth
#endif ORG_AUTH
#if WIN_AUTH
// code here for win auth
#endif WIN_AUTH

We will have similar expressions for files that are not code files, so you'll be able to use this same technique across all file types.

Then when you invoke the generator and pass the parameter for Individual Auth the resulting code in the template will be whatever is in the #if for IND_AUTH (minus the #if and #endif lines of course).

We will also support a model where you can have files that are dropped for a specific set of options.

Regarding shared UI code, I'm not sure if that makes sense. For example when plugging in a template into yeoman, you need to run through normal yeoman prompts for gathering input. When running in VS we need to implement with a wizard and maybe custom UI with WPF, when adding it to VS Code you'll need to create an extension which uses HTML/JS/CSS for the UI. So I don't see much benefit in trying to create a shared UI experience. There are technical challanges there as well as user experience consistency issues.

I'm confident that what we are creating will meet your needs.

@RehanSaeed

Hi @sayedihashimi. Agreed it's difficult to discuss without a code sample but a few comments anyway:

CLI

If the CLI broker component which asks the user questions is common, it needs to be flexible enough to support Yes/No questions, Multiple choice questions (Looks to be supported according to your Auth example) and perhaps free text questions (I don't use this one yet but I have considered adding something like this to ask for the site name as an example).

Conditional Logic

So you are using pre-processor directives for conditionally adding/removing logic. I do a lot of this and have some comments. I think it is very important that the project should still compile and be usable even though you have conditional logic in there.

  • What about other file types e.g. .js or .css are you using pre-processor directives there too? I use comments specific to the file type so everything still compiles e.g.:

Using your example, a C# or JavaScript file uses:

// $Start-Auth-NoAuth$
// code here for no auth
// $End-Auth-NoAuth$
// $Start-Auth-Org$
// code here for org auth
// $End-Auth-Org$
// $Start-Auth-Windows$
// code here for win auth
// $End-Auth-Windows$

However, a Razor file would oook like this:

@* $Start-Auth-NoAuth$ *@
code here for no auth
@* $End-Auth-NoAuth$ *@
@* $Start-Auth-Org$ *@
code here for org auth 
@* $End-Auth-Org$ *@
@* $Start-Auth-Windows$ *@
code here for win auth
@* $End-Auth-Windows$ *@
  • In the above examples, if you have three copies of the same code, it often won't compile. To get around this, I also uncomment code like so:
// $Start-Auth-NoAuth$
int i = 1;          // Not commented and used in the development time compilation of the template.
// $End-Auth-NoAuth$
// $Start-Auth-Org$
// int i = 2          // This code is uncommented if Org is selected.
// $End-Auth-Org$
// $Start-Auth-Windows$
int i = 3;          // This code is uncommented if Windows is selected.
// $End-Auth-Windows$
  • There also needs to be support for deleting files and folders based on a feature selection.
  • I have features which do Regex find and replace in certain files.
  • Sometimes you need to conditionally add/remove code based on multiple feature selections e.g. I have cases where I have to remove a namespace if all features using that namespace are de-selected.
  • I have custom features require the use of arbitrary code e.g.
    • Opening random ports until a free one is found and then using that port for the application.
    • Setting the AssembyCopyright to the current users name and current year.
    • Setting the name property in a package.json or bower.json file. These are lower-kebab case.

UI

If you are creating a manifest of features and the CLI is a shared component, then I suppose that means UI can also be a standard component provided by Microsoft. I'm not sure how I feel about that, it would be nice to have some UI customization:

  • The ability to add a custom look and feel. Maybe a theme background, foreground and accent colour.
  • Optional icons for each feature.
@SteveSandersonMS

This all looks great!

One further (but minor) point that you're probably already considered but I didn't see mentioned: restoring dependencies.

Obviously, after creating the initial project file layout, templates will need to invoke dotnet restore. But also they'll often need to invoke other tools like npm install or webpack --config <somefile> so that the generated app will run correctly.

If the templates can execute arbitrary C# code (as in @RehanSaeed's comment above) then that's sufficient. I'm guessing this is pretty inevitable because you couldn't configure something like this declaratively (the actions you need to take might vary according to options chosen by the user).

@RehanSaeed
RehanSaeed commented Aug 16, 2016 edited

As @SteveSandersonMS mentions, running arbitrary code is essential.

This ligershark/microsoft-templateengine#56 (comment) issue is also relevant to this topic. In it, I add two more issues that a templating engine must support:

  • The case where the code block contains a comment or maybe a commented pre-processor directive should also be handled. I do this with the use of a double comment:
//#if FOO
// Some comment
int i = 0;
//#else
// // Some comment
// int i = 0
//#endif
  • Support for multiple file formats is essential. I use:
    • // as the default.
    • <!-- --> for HTML and XML based formats.
    • @* *@ for Razor.
    • # for text based formats e.g. nginx.conf, robots.txt etc.
@mlorbetske
Member

@RehanSaeed @SteveSandersonMS we're still looking at if and how running arbitrary code during template instantiation specifically should work - in almost all cases, it's a step before the creation (collect some information to supply as parameters to the actual create operation) or a step after (to install dependencies or configure things on the user's machine to give a better development experience). Both of these often would require dependencies to be installed/present on the machine running the command without a particularly clear way of expressing that they're required (or how to obtain/install them - performing some npm commands after the template has been instantiated for example requires npm and would have to either detect it/install it before running it or fail gracefully and provide instructions no matter what OS you're on). That's not to say it won't happen, just that it needs a bit more design before it does.

With regard to the conditional syntaxes, these can be accomplished without running arbitrary code per se. The update that brings the automatic uncommenting of blocks is nearly ready (first commit for it was seancpeters/microsoft-templateengine@60f7f14 more tests are being added to make sure it's all right, the default preprocessor markers will be added/updated before it's pulled in)

@sayedihashimi sayedihashimi referenced this issue in OmniSharp/generator-aspnet Jan 6, 2017
Closed

"dotnet new" versus "yo aspnet" #858

@piotrpMSFT
Collaborator

dupe of #5387

@piotrpMSFT piotrpMSFT closed this Jan 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment