ChatGPT解决这个技术问题 Extra ChatGPT

How to use npm with ASP.NET Core

I'm using npm to manage the jQuery, Bootstrap, Font Awesome and similar client libraries I need for my ASP.NET Core application.

The approach that worked for me started by adding a package.json file to the project, that looks like this:

{
    "version": "1.0.0",
    "name": "myapp",
    "private": true,
    "devDependencies": {
  },
  "dependencies": {
    "bootstrap": "^3.3.6",
    "font-awesome": "^4.6.1",
    "jquery": "^2.2.3"
  }
}

npm restores these packages into the node_modules folder which is on the same level as wwwroot in the project directory:

https://i.stack.imgur.com/5Xlw7.png

As ASP.NET Core serves the static files from the wwwroot folder, and node_modules is not there, I had to make a couple of changes to make this work, the first one: adding app.UseFileServer right before app.UseStaticFiles in my Startup.cs file:

app.UseFileServer(new FileServerOptions()
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(Directory.GetCurrentDirectory(), @"node_modules")), 
    RequestPath = new PathString("/node_modules"),
    EnableDirectoryBrowsing = true
});

app.UseStaticFiles();

and the second one, including node_modules in my publishOptions in the project.json file:

"publishOptions": {
  "include": [
    "web.config",
    "wwwroot",
    "Views",
    "node_modules"
  ]
},

This works in my development environment and it also works when I deploy it to my Azure App Service instance, the jquery, bootstrap and font-awesome static files get served well, but I'm not sure about this implementation.

What is the right approach for doing this?

This solution came after collecting lots of bits of info from several sources and trying some that didn't work, and it seems a bit odd having to serve these files from outside wwwroot.

Any advice will be greatly appreciated.

This link has a working example on ASP.NET Core w/ npm: ievangelistblog.wordpress.com/2016/01/13/…
One thing that occured to me is to use the Bundler and Minifier - Specify the source is Outside wwwroot and when you build it builds the JS in to wwwroot. That is the proper way.. You should not be serving content from node_modules
I would highly discourage anyone from statically serving the node_modules folder. a) that's not how the eco-system is designed b) it's a security risk, one of your installed packages might leak sensitive information. The proper way is to set-up a building pipeline (grunt/gulp/node/webpack) that publishes files to a src or whatever folder dedicated to serving static front-end files
@PiotrKula, using Bundler & Minifier to pull files out of node_modules has one fatal problem: Some CSS code contains relative folder references via url statements in the CSS. For example, the Font Awesome CSS has references to assets in other folders in the Font Awesome distribution. The bundled/minified CSS file that shows up under your wwwroot folder will try to reference stuff that Bundler & Minifier didn't know about and didn't copy to the correct place relative to the bundled file.

L
Leniel Maccaferri

By publishing your whole node_modules folder you are deploying far more files than you will actually need in production.

Instead, use a task runner as part of your build process to package up those files you require, and deploy them to your wwwroot folder. This will also allow you to concat and minify your assets at the same time, rather than having to serve each individual library separately.

You can then also completely remove the FileServer configuration and rely on UseStaticFiles instead.

Currently, gulp is the VS task runner of choice. Add a gulpfile.js to the root of your project, and configure it to process your static files on publish.

For example, you can add the following scripts section to your project.json:

 "scripts": {
    "prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ]
  },

Which would work with the following gulpfile (the default when scaffolding with yo):

/// <binding Clean='clean'/>
"use strict";

var gulp = require("gulp"),
    rimraf = require("rimraf"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");

var webroot = "./wwwroot/";

var paths = {
    js: webroot + "js/**/*.js",
    minJs: webroot + "js/**/*.min.js",
    css: webroot + "css/**/*.css",
    minCss: webroot + "css/**/*.min.css",
    concatJsDest: webroot + "js/site.min.js",
    concatCssDest: webroot + "css/site.min.css"
};

gulp.task("clean:js", function (cb) {
    rimraf(paths.concatJsDest, cb);
});

gulp.task("clean:css", function (cb) {
    rimraf(paths.concatCssDest, cb);
});

gulp.task("clean", ["clean:js", "clean:css"]);

gulp.task("min:js", function () {
    return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
        .pipe(concat(paths.concatJsDest))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});

gulp.task("min:css", function () {
    return gulp.src([paths.css, "!" + paths.minCss])
        .pipe(concat(paths.concatCssDest))
        .pipe(cssmin())
        .pipe(gulp.dest("."));
});

gulp.task("min", ["min:js", "min:css"]);

I'm a little confused as to how this is the answer to the question. This is almost exactly what Microsoft have for configuring Gulp here (docs.microsoft.com/en-us/aspnet/core/client-side/using-gulp). However as far as I can tell this doesn't take content from my node_modules directory and add it to 'lib' or make it usable in any of my files... I'm very new to this so incredibly confused by the seemingly incredibly complicated new world of Web Development...
Complicated alright. I'm ready to abandon front-end altogether and just work on APIs.
This is the right approach in general, but the answer leaves out a crucial step: copying the files from the node_modules folder into the wwwroot folder as a Gulp task. Start with var nodeRoot = './node_modules/'; and add a task that copies the desired subfolder from nodeRoot into the appropriate subfolder of webroot. No time now to elaborate, but if there is interest, I can add details later.
Why has no one raised the obvious: "Why do we have to ask this question!" Why do we intentionally install files in a location where they cannot be used? Then, because our files are not accessible, we install and configure an elaborate utility to copy them to a location where they can be used. Its really ridiculous.
Because the tooling here is crap, basically. Using npm is just using npm, just as you would for anything. There's nothing specific to ASP.NET Core. Everything goes into node_modules because that's what npm does. The gulp task is necessary to move things into the right place, i.e. where you actually need them. I think the problem is that Microsoft provided such a beautiful integration with Bower, but now Bower is dead (or at least dying) and Microsoft hasn't provided any alternative tooling.
I
InteXX

https://i.stack.imgur.com/UgxXD.png

Using npm for managing client-side libraries is a good choice (as opposed to Bower or NuGet), you're thinking in the right direction :)

Split server-side (ASP.NET Core) and client-side (e.g. Angular 2, Ember, React) projects into separate folders (otherwise your ASP.NET project may have lots of noise - unit tests for the client-side code, node_modules folder, build artifacts, etc.). Front-end developers working in the same team with you will thank you for that :)

Restore npm modules at the solution level (similarly how you restore packages via NuGet - not into the project's folder), this way you can have unit and integration tests in a separate folder as well (as opposed to having client-side JavaScript tests inside your ASP.NET Core project).

Use might not need FileServer, having StaticFiles should suffice for serving static files (.js, images, etc.)

Use Webpack to bundle your client-side code into one or more chunks (bundles)

You might not need Gulp/Grunt if you're using a module bundler such as Webpack

Write build automation scripts in ES2015+ JavaScript (as opposed to Bash or PowerShell), they will work cross-platform and be more accessible to a variety of web developers (everyone speaks JavaScript nowadays)

Rename wwwroot to public, otherwise the folder structure in Azure Web Apps will be confusing (D:\Home\site\wwwroot\wwwroot vs D:\Home\site\wwwroot\public)

Publish only the compiled output to Azure Web Apps (you should never push node_modules to a web hosting server). See tools/deploy.js as an example.

Visit ASP.NET Core Starter Kit on GitHub (disclaimer: I'm the author)


Nice answer, kudos for your work on providing the community with a starter kit!
I successfully built a huge public Angular app for a major UK brand earlier this year, and yet I couldn't even understand most of this answer. I can't even begin to learn it as its all over the place. Literally having a career crisis.
this is a good summary of Do's and Dont's while writing and managing client side scripts and files. Saved my time and research. Kudos to you!
Unfortunately, Visual Studio (stupidly IMHO) treats the "wwwroot" directory specially, based on its name alone, giving it a special "web root" icon in the solution explorer and marking its contents as "None" by default rather than "Content". If the intention is to put statically-served files in there, and you're using Visual Studio, you should probably leave the name as "wwwroot". I agree it's a bad name though.
P
Piotr Kula

Install the Bundler and Minifier into Visual Studio Extensions

Then you create a bundleconfig.json and enter the following like :

// Configure bundling and minification for the project.
// More info at https://go.microsoft.com/fwlink/?LinkId=808241
[
 {
    "outputFileName": "wwwroot/js/jquery.min.js",
    "inputFiles": [
      "node_modules/jquery/dist/jquery.js"
    ],
    // Optionally specify minification options
    "minify": {
      "enabled": true,
      "renameLocals": false
    },
    // Optionally generate .map file
    "sourceMap": false
  }
]

So the bundler and minifier (gulp based) has access to the source files (which should be excluded from Visual Studio and also excluded from GIT) and puts them into the wwwroot as specified

only side effect every time you save it will run this (but you can set it to run it manually)


After seeing Bower is dead and I can no longer update Bootstrap without switching to NPM, this appears to be my favorite approach. Gulp or Webpack seem overkill compared to this simple solution which is already in the latest MVC project templates. Thank you for sharing!
How images referenced in css be handled here ? I'm facing a problem with images still in node_modules folder where as css and js were moved to www. Any idea how to fix this ?
Is this IDE-agnostic? How will this work if some of your team is using VS Code, and how does this play into a CD build pipeline?
When you install the Bundler and Minifier NuGet package, the docs say it injects build targets which run at build and clean time. I suppose once that's in place, it will work fine regardless of which IDE is used, right? See more: docs.microsoft.com/en-gb/aspnet/core/client-side/…
The bundler is only useful for specifying these scripts outside of the Development environment. From my _Layout page I still have to manually reference the JS and CSS files I need, but the node_modules folder is outside of the website... so I don't think this addresses the problem properly, you still need a Gulp script to copy over the necessary files to my wwwroot folder.
C
Community

I give you two answers. npm combined with other tools is powerful but requires some work to setup. If you just want to download some libraries, you might want to use Library Manager instead (released in Visual Studio 15.8).

NPM (Advanced)

First add package.json in the root of you project. Add the following content:

{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "devDependencies": {
    "gulp": "3.9.1",
    "del": "3.0.0"
  },
  "dependencies": {
    "jquery": "3.3.1",
    "jquery-validation": "1.17.0",
    "jquery-validation-unobtrusive": "3.2.10",
    "bootstrap": "3.3.7"
  }
}

This will make NPM download Bootstrap, JQuery and other libraries that is used in a new asp.net core project to a folder named node_modules. Next step is to copy the files to an appropriate place. To do this we will use gulp, which also was downloaded by NPM. Then add a new file in the root of you project named gulpfile.js. Add the following content:

/// <binding AfterBuild='default' Clean='clean' />
/*
This file is the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');
var del = require('del');

var nodeRoot = './node_modules/';
var targetPath = './wwwroot/lib/';

gulp.task('clean', function () {
    return del([targetPath + '/**/*']);
});

gulp.task('default', function () {
    gulp.src(nodeRoot + "bootstrap/dist/js/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/js"));
    gulp.src(nodeRoot + "bootstrap/dist/css/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/css"));
    gulp.src(nodeRoot + "bootstrap/dist/fonts/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/fonts"));

    gulp.src(nodeRoot + "jquery/dist/jquery.js").pipe(gulp.dest(targetPath + "/jquery/dist"));
    gulp.src(nodeRoot + "jquery/dist/jquery.min.js").pipe(gulp.dest(targetPath + "/jquery/dist"));
    gulp.src(nodeRoot + "jquery/dist/jquery.min.map").pipe(gulp.dest(targetPath + "/jquery/dist"));

    gulp.src(nodeRoot + "jquery-validation/dist/*.js").pipe(gulp.dest(targetPath + "/jquery-validation/dist"));

    gulp.src(nodeRoot + "jquery-validation-unobtrusive/dist/*.js").pipe(gulp.dest(targetPath + "/jquery-validation-unobtrusive"));
});

This file contains a JavaScript code that is executed when the project is build and cleaned. It’s will copy all necessary files to lib2 (not lib – you can easily change this). I have used the same structure as in a new project, but it’s easy to change files to a different location. If you move the files, make sure you also update _Layout.cshtml. Note that all files in the lib2-directory will be removed when the project is cleaned.

If you right click on gulpfile.js, you can select Task Runner Explorer. From here you can run gulp manually to copy or clean files.

Gulp could also be useful for other tasks like minify JavaScript and CSS-files:

https://docs.microsoft.com/en-us/aspnet/core/client-side/using-gulp?view=aspnetcore-2.1

Library Manager (Simple)

Right click on you project and select Manage client side-libraries. The file libman.json is now open. In this file you specify which library and files to use and where they should be stored locally. Really simple! The following file copies the default libraries that is used when creating a new ASP.NET Core 2.1 project:

{
  "version": "1.0",
  "defaultProvider": "cdnjs",
  "libraries": [
    {
      "library": "jquery@3.3.1",
      "files": [ "jquery.js", "jquery.min.map", "jquery.min.js" ],
      "destination": "wwwroot/lib/jquery/dist/"
    },
    {
      "library": "jquery-validate@1.17.0",
      "files": [ "additional-methods.js", "additional-methods.min.js", "jquery.validate.js", "jquery.validate.min.js" ],
      "destination": "wwwroot/lib/jquery-validation/dist/"
    },
    {
      "library": "jquery-validation-unobtrusive@3.2.10",
      "files": [ "jquery.validate.unobtrusive.js", "jquery.validate.unobtrusive.min.js" ],
      "destination": "wwwroot/lib/jquery-validation-unobtrusive/"
    },
    {
      "library": "twitter-bootstrap@3.3.7",
      "files": [
        "css/bootstrap.css",
        "css/bootstrap.css.map",
        "css/bootstrap.min.css",
        "css/bootstrap.min.css.map",
        "css/bootstrap-theme.css",
        "css/bootstrap-theme.css.map",
        "css/bootstrap-theme.min.css",
        "css/bootstrap-theme.min.css.map",
        "fonts/glyphicons-halflings-regular.eot",
        "fonts/glyphicons-halflings-regular.svg",
        "fonts/glyphicons-halflings-regular.ttf",
        "fonts/glyphicons-halflings-regular.woff",
        "fonts/glyphicons-halflings-regular.woff2",
        "js/bootstrap.js",
        "js/bootstrap.min.js",
        "js/npm.js"
      ],
      "destination": "wwwroot/lib/bootstrap/dist"
    },
    {
      "library": "list.js@1.5.0",
      "files": [ "list.js", "list.min.js" ],
      "destination": "wwwroot/lib/listjs"
    }
  ]
}

If you move the files, make sure you also update _Layout.cshtml.


Library Manager -- never heard of it, but is exactly what I need! This should be the correct answer.
To avoid a gulp error I had to change the first line of the default task in file gulpfile.js to gulp.task('default', function (done) { and then add as a last line in that function the following: done(); Otherwise I would get the error message The following tasks did not complete: Did you forget to signal async completion?
C
Community

Instead of trying to serve the node modules folder, you can also use Gulp to copy what you need to wwwroot.

https://docs.asp.net/en/latest/client-side/using-gulp.html

This might help too

Visual Studio 2015 ASP.NET 5, Gulp task not copying files from node_modules


I really like the second link, seems familiar somehow. ;)
It's not very convenient when you mix ASP.NET Core web app source files with npm modules and client-side code build output. That's the reason why ASP.NET team is removing Gulp, package.json from default ASP.NET MVC project templates.
I did not know that. When I was at VS Live in March, they were all about it, but it has changed a lot since then.
C
Community

What is the right approach for doing this?

There are a lot of "right" approaches, you just have decide which one best suites your needs. It appears as though you're misunderstanding how to use node_modules...

If you're familiar with NuGet you should think of npm as its client-side counterpart. Where the node_modules directory is like the bin directory for NuGet. The idea is that this directory is just a common location for storing packages, in my opinion it is better to take a dependency on the packages you need as you have done in the package.json. Then use a task runner like Gulp for example to copy the files you need into your desired wwwroot location.

I wrote a blog post about this back in January that details npm, Gulp and a whole bunch of other details that are still relevant today. Additionally, someone called attention to my SO question I asked and ultimately answered myself here, which is probably helpful.

I created a Gist that shows the gulpfile.js as an example.

In your Startup.cs it is still important to use static files:

app.UseStaticFiles();

This will ensure that your application can access what it needs.


"I wrote a blog post about this back in January that details npm, Gulp and a whole bunch of other details that are still relevant today." the fact that from Jan - June and you have to mention is still being relevant is exactly my problem with bothering to learn any of this fly-by-night stuff. I've too much to learn already without wasting my time on fashions. Not your fault David, you're being very helpful, but I don't like this ephemeral new world.
M
Mariusz Bialobrzeski

Much simpler approach is to use OdeToCode.UseNodeModules Nuget package. I just tested it with .Net Core 3.0. All you need to do is add the package to the solution and reference it in the Configure method of the Startup class:

app.UseNodeModules();

I learned about it from the excellent Building a Web App with ASP.NET Core, MVC, Entity Framework Core, Bootstrap, and Angular Pluralsight course by Shawn Wildermuth.


And how do you add npm packages?
There are several ways to add npm packages to your project. I prefer to just type them in the package.json file under dependencies node as explained here: stackoverflow.com/a/49264424/5301317
@MariuszBialobrzeski Thanks for sharing. This is a neat solution.
@MariuszBialobrzeski Did you have to do anything additional to deploy the static content from node_modules in your DevOps pipelines? Or did you check in the node_modules folder?
@Sau001 Unfortunately I never had a chance to work with DevOps pipelines. node_modules folder tends to get pretty big with so I would definitely avoid checking it in if possible.
C
Community

Shawn Wildermuth has a nice guide here: https://wildermuth.com/2017/11/19/ASP-NET-Core-2-0-and-the-End-of-Bower

The article links to the gulpfile on GitHub where he's implemented the strategy in the article. You could just copy and paste most of the gulpfile contents into yours, but be sure to add the appropriate packages in package.json under devDependencies: gulp gulp-uglify gulp-concat rimraf merge-stream


P
Peter Húbek

I've found a better way how to manage JS packages in my project with NPM Gulp/Grunt task runners. I don't like the idea to have a NPM with another layer of javascript library to handle the "automation", and my number one requirement is to simple run the npm update without any other worries about to if I need to run gulp stuff, if it successfully copied everything and vice versa.

The NPM way:

The JS minifier is already bundled in the ASP.net core, look for bundleconfig.json so this is not an issue for me (not compiling something custom) The good thing about NPM is that is have a good file structure so I can always find the pre-compiled/minified versions of the dependencies under the node_modules/module/dist I'm using an NPM node_modules/.hooks/{eventname} script which is handling the copy/update/delete of the Project/wwwroot/lib/module/dist/.js files, you can find the documentation here https://docs.npmjs.com/misc/scripts (I'll update the script that I'm using to git once it'll be more polished) I don't need additional task runners (.js tools which I don't like) what keeps my project clean and simple.

The python way:

https://pypi.python.org/pyp... but in this case you need to maintain the sources manually


b
b01

Please excuse the length of this post.

This is a working example using ASP.NET Core version 2.5.

Something of note is that the project.json is obsolete (see here) in favor of .csproj. An issue with .csproj. file is the large amount of features and the fact there is no central location for its documentation (see here).

One more thing, this example is running ASP.NET core in a Docker Linux (alpine 3.9) container; so the paths will reflect that. It also uses gulp ^4.0. However, with some modification, it should work with older versions of ASP.NET Core, Gulp, NodeJS, and also without Docker.

But here's an answer:

gulpfile.js see the real working exmple here

// ROOT and OUT_DIR are defined in the file above. The OUT_DIR value comes from .NET Core when ASP.net us built.
const paths = {
    styles: {
        src: `${ROOT}/scss/**/*.scss`,
        dest: `${OUT_DIR}/css`
    },
    bootstrap: {
        src: [
            `${ROOT}/node_modules/bootstrap/dist/css/bootstrap.min.css`,
            `${ROOT}/node_modules/startbootstrap-creative/css/creative.min.css`
        ],
        dest: `${OUT_DIR}/css`
    },
    fonts: {// enter correct paths for font-awsome here.
        src: [
            `${ROOT}/node_modules/fontawesome/...`,
        ],
        dest: `${OUT_DIR}/fonts`
    },
    js: {
        src: `${ROOT}/js/**/*.js`,
        dest: `${OUT_DIR}/js`
    },
    vendorJs: {
        src: [
            `${ROOT}/node_modules/jquery/dist/jquery.min.js`
            `${ROOT}/node_modules/bootstrap/dist/js/bootstrap.min.js`
        ],
        dest: `${OUT_DIR}/js`
    }
};

// Copy files from node_modules folder to the OUT_DIR.
let fonts = () => {
    return gulp
        .src(paths.styles.src)
        .pipe(gulp.dest(paths.styles.dest));
};

// This compiles all the vendor JS files into one, jsut remove the concat to keep them seperate.
let vendorJs = () => {
    return gulp
        .src(paths.vendorJs.src)
        .pipe(concat('vendor.js'))
        .pipe(gulp.dest(paths.vendorJs.dest));
}

// Build vendorJs before my other files, then build all other files in parallel to save time.
let build = gulp.series(vendorJs, gulp.parallel(js, styles, bootstrap));

module.exports = {// Only add what we intend to use externally.
    default: build,
    watch
};

Add a Target in .csproj file. Notice we also added a Watch to watch and exclude if we take advantage of dotnet run watch command.

app.csprod

  <ItemGroup>
    <Watch Include="gulpfile.js;js/**/*.js;scss/**/*.scss" Exclude="node_modules/**/*;bin/**/*;obj/**/*" />
  </ItemGroup>

  <Target Name="BuildFrontend" BeforeTargets="Build">
    <Exec Command="yarn install" />
    <Exec Command="yarn run build -o $(OutputPath)" />
  </Target>

Now when dotnet run build is run it will also install and build node modules.