DEV Community

Cover image for Including files created by Node.js into .Net project 🛠
John Kapantzakis
John Kapantzakis

Posted on • Updated on

Including files created by Node.js into .Net project 🛠

This is the continuation of a previous post of mine

In that post we had described the process of creating a set of new files, that contain boilerplate code, using the Handlebars library. Those files are created within the folder structure of a .Net project and they, somehow, have to be included in the project.

We can find them one by one and include them manually, but it would be better if they could automatically get included into the project.

The project file

Each .Net project is built by the Microsoft build engine (MSBuild). In order for the engine to know what files to include into the build process, there is a special XML file called the project file that has a language soecific extension. For C# is .csproj.

The project file is created automatically by Visual Studio, or it can be created manually if we want to build a project without using Visual Studio. Here is a sample project file for a .Net project written in C#.

Each time we create a new file, Visual Studio includes it to the project, by adding a new entry to the XML document like the following:

<Content Include="file1.ascx"/>
Enter fullscreen mode Exit fullscreen mode

In our case, the new files are created by a Node.js app. They are at the right place but they are not yet part of the build process. We have to add a new entry to the project file for each one of them. We can do it manually, by right clicking on each file and selecting Include In Project, or we can somehow automate this process.

Attempt #1 (xml-js)

As we said at the beginning, this post describes the process of including files generated from a Node.js cli to specific .Net project. We are now going to describe the first attempt of creating an automated process of including those files to the desired project file.

The first idea that came to my mind was to read the .csproj file in javascript, add the desired entries and recreate it. So, I found xml-js, a powerfull tool that lets you convert xml to js/JSON and vice versa.

The flow was something like this:

Alt Text

After the new files are created, we read the .csproj file

const convert = require("xml-js");

const xml = fs.readFileSync(path, "utf8");
const js = convert.xml2js(xml);
Enter fullscreen mode Exit fullscreen mode

Then, we manipulate the js object accordingly and we recreate a new xml structure:

const xml = convert.js2xml(js, {
    compact: true,
    ignoreComment: true,
    spaces: 2
});
Enter fullscreen mode Exit fullscreen mode

Finally, we replace the contents of the .csproj file with the new xml structure:

fs.writeFileSync(filePath, xml);
Enter fullscreen mode Exit fullscreen mode

After the creation of the new .cproj file, I could not build the project at all (oops!)

oops

It looks that the project file is broken, and, eventually it seems logical since we are trying to manipulate a specific type of XML file in a way that is not recommended. I have never used xml-js before and I might could have achieved a better result if I tried different conifgurations.

Attempt #2 (Project class)

After the previous failed attempt, I searched about how to programmatically include files in .net projects and found an answer to this question on stackoverflow:

Background

I'm making a helper application that reformats some code files and creates new code files, which are to be added to my other project, so I could use the new code right away, but I'm having serious trouble adding that new code file into my project automatically. By the…

People suggested to use the class Microsoft.Build.Evaluation.Project from the .Net framework and its AddItem method that does exactly what we are looking for.

The problem is that we have to use C# (or VB or whatever .Net compatible language you are working with) in order to use the Project class. So, we have to write a console application that uses the Project class and its methods in order to add new entries to the project file.

As soon as we write the console appication, we must find a way to execute it from Node.js, since our new files are created from a Node.js cli.

Console application

Lets now build a simple (C#) console application that will load the project file, add the new items and save the changes. We are not going to cover the whole process in detail, instead we are going to highlight the major points.

If you want to view the whole code you can check the following repo:

GitHub logo kapantzak / csproj-include

Programmatically include items to csproj file

csproj-include

Programmatically include items to csproj file




Inside our Main method, we check if the desired project is already loaded and if not, we load it.

var p = ProjectCollection
          .GlobalProjectCollection
          .LoadedProjects
          .FirstOrDefault(
              x => x.FullPath == projectFullPath
          );
if (p == null)
    p = new Project(projectFullPath);
Enter fullscreen mode Exit fullscreen mode

As soon as we have a loaded project file, we can iterate over a collection of items that represents the new entries and add them one by one to the loaded file:

items.ForEach(x =>
{                                
    p.AddItemFast(x.itemType, x.unevaluatedInclude, x.metadata);    
});
p.Save();
Enter fullscreen mode Exit fullscreen mode

We assume that items is a collection of objects of type Item which is defined bellow:

class Item
{
    public string itemType { get; set; }
    public string unevaluatedInclude { get; set; }
    public Dictionary<string, string> metadata { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Inputs

The program needs some input in order to work properly, like:

  • The project file to load
  • The entries collection to be inserted

We expect the first argument to be the project file and the second argument to be a JSON string that is going to be deserialised into a list of Item objects.

Execution

We can now call the executable like this:

> app.exe file.csproj [{"itemType": "Content", "unevaluatedInclude": "myFile.ts"}, {"metadata": null}]
Enter fullscreen mode Exit fullscreen mode

But when I tested, I got the following error.

Alt Text

It seems that there is a missing file or directory! After a lot of searching, I found another stackoverflow question:

I built a Web Forms website using VS2015 where I user Microsoft.Build.Evaluation so that I can grammatically go through the files in my project When using VS2017 I get this error:

Microsoft.Build.Exceptions.InvalidProjectFileException: 'The imported project "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v15.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the declaration is correct…

I finally found out that $(MSBuildExtensionsPath32) and $(VisualStudioVersion) variables of the project file was not correctly set, and that I could apply the desired settings, using an overload of the Project class constructor that accepts this kind of settings.

So I added the following lines:

var glob = new Dictionary<string, string>();
glob.Add("VisualStudioVersion", "16.0");
glob.Add("MSBuildExtensionsPath32", @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild");

p = new Project(projectFullPath, glob, null);
Enter fullscreen mode Exit fullscreen mode

And it worked just fine!

hooray

Execute from Node.js

Our final step is to call the console application's executable from inside our Node.js cli. We can achieve that using the execFile() method of the child_process module like that:

const exec = require("child_process").execFile;
const child = exec(projectFile, args, (err, data) => {
    // ...
});
Enter fullscreen mode Exit fullscreen mode

Workflow

Now lets overview the workflow after our second attempt

Alt Text

As we described earlier, the new files are created by a Node.js cli. As soon as the files have been created, we use execFile to call the console app we have created in order to add the new items to the desired project file.

Conclusion

During the development process of this Node.js app and the console application, I came accross various problems that I had never faced before. I had to search a lot for the details so, I thought I could write a post about my experience in order to help other people who may face the same or similar problems. I hope you enjoyed reading! 😄

Resources

Top comments (0)