Description
The idea is that you can have a package.json file that only contains dependencies, and that can be consumed directly inside ASP.NET Core via static web assets.
Currently we support integrating with NPM, but we assume that you are going to have a build step (which typically bundles/copies files to an output folder) and we are going to expose these over the wwwroot folder.
However, if you just want to consume the files in the package directly (as you would do within the bundle) it becomes hard to do, as typically you need an extra step to copy the files and define them as assets.
Instead of that, we can build a "node resolver" (similar to the one every bundler has) that can automatically expose the contents of a package as if they were part of the wwwroot. The way this works is as follows:
- We detect package.json files in your app.
- We parse the json file and read the "dependencies" node.
- For each dependency:
- We start by looking for a "node_modules" folder side by side with the package.json.
- If we find it:
- We search for the package name inside.
- If we find a folder with the package name:
- We read the package.json for the package.
- We search for the "main" property in the package.
- If present, we map the path "package-name" to the full path of the file pointed by main.
- If not present, we map the path "package-name" to the full path of the file
index.js
if it exists. - Additionally, if present, we map the contents of the "dist" folder of the package to "package-name/<>"
- We do not want to map the entire contents of the package because we do not have a way to trim them.
- We might alternatively, map the entire package, but use a pattern to filter that only covers "dist" by default.
- The strings we use to consume specific files from a package should match exactly those used when using an "import" in js.
- If we do not find a folder with the package name, we break
- If we do not find a node_modules folder, we try to find one in a directory above.
In addition to that, many packages ship source maps and minified versions of the files.
- We can have conventions that make sure that we pick the right file depending on whether we are publishing the app or developing.
- For example, if you add "jquery", the "jquery" package contains "jquery.js" and "jquery.min.js".
- Static web assets can automatically serve "jquery.js" (unminified) during development and copy "jquery.min.js" into wwwroot/jquery.js at publish time.
- In a similar way, it can automatically serve the source maps during development and avoid publishing those to the final output.
- This ensures that during development you get the best experience and during publish you get the minified asset.
A user can consume the file from a razor file by using <script src="jquery"></script>
or <script src="jquery/dist/jquery.js"></script>
in a very similar way as if they were using "unpkg" (https://unpkg.com/jquery@3.6.3/dist/jquery.js)
This would potentially all work without configuration (you drop a package.json into your project and we do the rest), or we could additionally provide some config options that can be specified inside package.json
or the csproj file (although it feels more natural to put it inside package.json). Some of this options could be:
- Common prefix to map the packages to, for example "vendors", "lib", etc.
- Configuration for what paths to include on a per package basis (everything else gets filtered out).
To give a full example. Let's say we have an MVC app and we want to consume the common JS libraries that MVC/Razor pages use. We drop a package.json
file:
{
"private": true,
"dependencies": {
"bootstrap": "^5.2.3",
"jquery": "^3.6.3",
"jquery-validation": "^1.19.5",
"jquery-validation-unobtrusive": "^4.0.0"
},
}
And we consume it from "_Layout.cshtml" as follows:
<!-- Mapping jquery from "main" inside package.json -->
<script src="jquery"></script>
<!-- Mapping explicitly selecting the file to consume from the package -->
<script src="bootstrap/dist/js/bootstrap.bundle.min.js"></script>