npm 8.19.2 overrides nested split tree - node.js

I have an angular 13 app and some dependencies are blocked due to security issues. Thats why I'm using the overrides feature to force versions for node-forge and async.
Both are transitive dependencies of #angular-devkit/build-angular as $ npm ls shows:
├─┬ #angular-devkit/build-angular#13.3.9
│ └─┬ webpack-dev-server#4.7.3
│ └─┬ portfinder#1.0.32
│ └── async#3.2.4 overridden
└─┬ #angular-eslint/builder#13.5.0
└─┬ #nrwl/devkit#13.1.3
└─┬ ejs#3.1.8
└─┬ jake#10.8.5
└── async#3.2.4 deduped
and:
─┬ #angular-devkit/build-angular#13.3.9
└─┬ webpack-dev-server#4.7.3
└─┬ selfsigned#2.1.1
└── node-forge#1.3.1
For better traceability, the dependencies should be overwritten in nested form! But they are both packages in subpaths of the same package dependency tree.
So how can I override multiple Branches of that nested tree with npm overrides?
I can, of course include only the package name into the overrides section with the desired version and it works. But this way it is not clear for future developers where this override comes from and why it is necessary.
So I tried:
[...]
"overrides": {
"#angular-devkit/build-angular": {
"webpack-dev-server": {
"selfsigned": {
"node-forge": "1.3.1"
},
"portfinder": {
"async": "3.2.4"
}
}
}
}
[...]
As the docs suggest it might be possible!?
But npm install is pulling async#2.6.4 as a dependency of portfinder.
I also tried:
[...]
"overrides": {
"#angular-devkit/build-angular": {
"webpack-dev-server": {
"selfsigned": {
"node-forge": "1.3.1"
}
}
},
"#angular-devkit/build-angular": {
"webpack-dev-server": {
"portfinder": {
"async": "3.2.4"
}
}
}
}
[...]
But it still attempts do download async#2.6.4.

Related

How do I export a TypeScript ESM Module as an NPM package?

Recently, I had to fork an open source NPM package that was built using TypeScript and exported using CommonJS. The package hasn't been maintained for a couple of years, so I had to update all of the dependencies, which made the code break.
One of the dependencies, unified, was being imported using const unified = require("unified"); which is CommonJS syntax, but in a newer version of unified that I updated to, they started forcing unified to be imported using ESM syntax like import { unified } from "unified". So I decided to convert my entire package to ESM.
I added "type": "module" to my package.json and updated all of the code to use ESM style imports/exports, but now that I'm ready to publish the package, I'm confused on a couple of things.
The original developer had the project setup like this:
├── src/
│ ├── index.ts
│ ├── options.ts
│ ├── toc.ts // where most of the code logic lives. This is the entry point to the application and the main function I want to export
│ └── ... // whole bunch of other typescript files
├── lib/
│ ├── index.d.ts
│ ├── index.js
│ ├── index.js.map
│ └── ... // this repeats for every typescript file in src/
├── dist/
│ ├── index.d.ts
│ ├── index.js
└── └── package.json
In tsconfig.json, he had "outDir": "lib", so all of the TypeScript files inside src/ get exported with their type stuff to the lib folder. That makes sense to me.
But what I'm confused on is what am I supposed to put in src/index.ts, dist/index.js and index.d.ts ?
This what the developer originally had:
src/index.ts
import { toc } from "./my-package";
export { CssClasses, Options } from "./options";
export * from "./types";
export { toc };
// Export `toc` as the default export
export default toc;
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = Object.assign(module.exports.default, module.exports);
}
dist/index.d.ts
import toc from "#username/my-package";
export * from "#username/my-package";
export default toc;
dist/index.js
"use strict";
module.exports = require("#username/my-package");
dist/package.json
"name": "#username/my-package",
"version": "3.0.0",
// ...
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.js",
"index.d.ts"
],
"engines": {
"node": ">=14"
},
"dependencies": {
"#username/my-package": "3.0.0"
}
So my questions are:
What goes inside dist/index.js and dist/index.d.ts ? Do I just make a copy of those files from lib and paste them in dist?
To make my package fully ESM, What should I replace the module.exports lines with in src/index.ts and dist/index.js ?
Is there anything else I need to add to my dist/package.json, besides "type": "module"? Is it really that simple, that I only need 3 files in my distribution folder?

Node - Search for dependancy recursively

I want to search recursively and find if I'm using a certain Node module. Is this possible?
If you mean not at runtime you can do:
npm ls <module name to search for>
For example:
$ npm ls async
/Users/justin/code/example
├─┬ aws-cdk#0.19.0
│ └─┬ archiver#2.1.1
│ └── async#2.1.4 deduped
├─┬ mongooster#6.0.2
│ └─┬ mongoose#5.3.5
│ └── async#2.6.1
└─┬ sqs-consumer#3.8.0
└── async#2.1.4
At runtime you can use require.resolve
> require.resolve('async')
'/Users/justin/code/example/node_modules/async/dist/async.js'
Or you can require the module then look in the cache to get details about the module, including its parent chain.
> require.cache[require.resolve('async')]
Module {
id: '/Users/justin/code/example/node_modules/async/dist/async.js',
exports: { ... },
parent: Module { ... },
filename: '/Users/justin/code/example/node_modules/async/dist/async.js',
loaded: true,
children: [],
paths: [ ... ]
}

How do I correctly require and declare node library typings in TypeScript?

I am trying to write a package for node in TypeScript that uses standard node libraries, for example fs, path, stream, http, and so on.
When I try to import libraries in a .ts file, VS Code marks the corresponding line with an error:
[ts] Cannot find module 'fs'.
This happens no matter how I try to import the library:
import * as fs from 'fs'; // [ts] Cannot find module 'fs'
import fs = require('fs'); // [ts] Cannot find module 'fs'
const fs = require('fs'); // [ts] Cannot find name 'require'
I (should) have the correct definitions installed with typings install --save --ambient node.
When I compile to JavaScript (using gulp and gulp-typescript, not tsc), the compliation works and the syntax highlighting shows no errors until I type in 1 character again:
How can I correctly define the node libraries for TypeScript?
I use VS Code for code highlighting and autocompletion, gulp & gulp-typescript to compile and typings for the typescript library declarations.
The project's directory structure:
├─ build/
│ └─ (output files)
├─ src/
│ └─ myfile.ts
├─ typings/
│ ├─ browser/
│ │ └─ ambient/
│ │ └─ node/
│ │ └─ index.d.ts
│ ├─ main/
│ │ └─ ambient/
│ │ └─ node/
│ │ └─ index.d.ts
│ ├─ browser.d.ts
│ └─ main.d.ts
├─ gulpfile.js
├─ package.json
├─ tsconfig.json
└─ typings.json
My tsconfig.json:
{
"compileOnSave": false,
"compilerOptions": {
"declaration": true,
"module": "system",
"moduleResolution": "node",
"noEmitOnError": true,
"noImplicitAny": true,
"target": "es5"
},
"exclude": [
"node_modules",
"typings/browser",
"typings/browser.d.ts"
]
}
My typings.json:
{
"ambientDependencies": {
"node": "registry:dt/node#4.0.0+20160412142033"
}
}
And my gulp task:
gulp.task('build-typescript', () => {
const gulpts = require('gulp-typescript');
const tsProject = gulpts.createProject('tsconfig.json', {
typescript: require('typescript'),
outFile: 'mylibrary.js',
noLib: true
});
let tsstream = (
gulp.src([
'node_modules/typescript/lib/lib.es6.d.ts',
'typings/main.d.ts',
'src/sharpscript.ts'])
.pipe(sourcemaps.init())
.pipe(gulpts(tsProject))
);
return require('merge2')(
tsstream.dts.pipe(gulp.dest('build')),
tsstream.js
.pipe(sourcemaps.write('.', { includeContent: true }))
.pipe(gulp.dest('build'))
);
});
In case anyone has experienced the same problem, I am thankful for any insights.
the installation of type declarations with 'typings install ...' is obsolete since a few months. The new way is to install it directly via npm and the #types namespace. to install the node type declarations just use npm install #types/node --save-dev.

How does npm draw the dependency tree?

npm does a nifty job of drawing a package's dependency hierarchy as a tree in the console:
$ npm ls
my-awesome-project#0.0.1
├── colors#0.6.0-1
├─┬ express#2.5.11
│ ├─┬ connect#1.9.2
│ │ └── formidable#1.0.11
│ ├── mime#1.2.4
│ ├── mkdirp#0.3.0
│ └── qs#0.4.2
└── uglify-js#1.2.6
How does npm do this?
npm uses the Unicode box drawing characters (U+2500-2800) to draw the pretty lines of the tree.
To draw a similar tree in your own application, the best route is probably to use the same module that npm itself uses – archy.
var archy = require('archy');
var s = archy({
label : 'beep',
nodes : [
'ity',
{
label : 'boop',
nodes : [
{
label : 'o_O',
nodes : [
{
label : 'oh',
nodes : [ 'hello', 'puny' ]
},
'human'
]
},
'party\ntime!'
]
}
]
});
console.log(s);
Outputs
beep
├── ity
└─┬ boop
├─┬ o_O
│ ├─┬ oh
│ │ ├── hello
│ │ └── puny
│ └── human
└── party
time!
For list your folders and files you can use tree-cli:
https://www.npmjs.com/package/tree-cli
Just install:
npm install -g tree-cli
And use inside your folder:
tree -L 2, -d
You could also use console2 which does almost the same thing as archy does, but gives you useful additional features like improved stack traces, object inspection and more:
Feature screenshot
Full disclosure: I'm the author of the repository

How to write a package.json file so that all dependencies are downloaded with "npm install"

I wrote a simple application using node. It depends on express, mongodb and mongoose (easy).
So, I created a file called package.json and put this in it:
{
"name": "booking-dojo",
"description": "Booking dojo app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "3.x",
"mongodb": "*",
"mongoose": "*"
}
}
I then ran npm install, expecting npm to install those modules and their dependencies.
The result was disappointing:
booking-dojo#0.0.1 /home/merc/Synced/Development/Bookings/app/server
├─┬ express#3.0.0rc3
│ ├── commander#0.6.1
│ ├─┬ connect#2.4.3
│ │ ├── bytes#0.1.0
│ │ ├── formidable#1.0.11
│ │ ├── pause#0.0.1
│ │ └── qs#0.4.2
│ ├── cookie#0.0.4
│ ├── crc#0.2.0
│ ├── debug#0.7.0
│ ├── fresh#0.1.0
│ ├── methods#0.0.1
│ ├── mkdirp#0.3.3
│ ├── range-parser#0.0.4
│ └─┬ send#0.0.3
│ └── mime#1.2.6
├─┬ mongodb#1.1.4
│ └── bson#0.1.1
└─┬ mongoose#3.0.3
├── hooks#0.2.1
└── ms#0.1.0
I am confused by this, as I know that express needs jade (and much more), and mongoose needs mongodb.
If I go into node_modules/jade and run npm install, the result from the main tree is very different:
booking-dojo#0.0.1 /home/merc/Synced/Development/Bookings/app/server
├─┬ express#3.0.0rc3
│ ├── commander#0.6.1
│ ├─┬ connect#2.4.3
│ │ ├── bytes#0.1.0
│ │ ├── formidable#1.0.11
│ │ ├── pause#0.0.1
│ │ └── qs#0.4.2
│ ├─┬ connect-redis#1.4.1
│ │ └─┬ redis#0.7.2
│ │ └── hiredis#0.1.14
│ ├── cookie#0.0.4
│ ├── crc#0.2.0
│ ├── debug#0.7.0
│ ├── ejs#0.8.2
│ ├── fresh#0.1.0
│ ├── github-flavored-markdown#1.0.1
│ ├─┬ hjs#0.0.4
│ │ └── hogan.js#2.0.0
│ ├─┬ jade#0.27.2
│ │ └── mkdirp#0.3.0
│ ├── methods#0.0.1
│ ├── mkdirp#0.3.3
│ ├─┬ mocha#1.4.0
│ │ ├── diff#1.0.2
│ │ ├── growl#1.5.1
│ │ └─┬ jade#0.26.3
│ │ └── mkdirp#0.3.0
│ ├── range-parser#0.0.4
│ ├─┬ send#0.0.3
│ │ └── mime#1.2.6
│ ├── should#1.1.0
│ ├─┬ stylus#0.29.0
│ │ └── cssom#0.2.5
│ └─┬ supertest#0.0.1
│ └─┬ superagent#0.5.0
│ ├── emitter-component#0.0.1
│ ├── formidable#1.0.9
│ ├── mime#1.2.5
│ └── qs#0.4.2
├─┬ mongodb#1.1.4
│ └── bson#0.1.1
└─┬ mongoose#3.0.3
├── hooks#0.2.1
└── ms#0.1.0
So, express has grown a lot. It looks like npm install is only loading some of the dependencies of the sub-modules.
Can somebody please shed some light on this? Why are some dependencies missing? Am I doing something wrong? (likely)
Thanks!
Merc.
You are confused about at least 2 points.
First, express does not depend on jade, as you can see by reading the node_modules/express/package.json file:
"dependencies": {
"connect": "2.4.2",
"commander": "0.6.1",
"range-parser": "0.0.4",
"mkdirp": "0.3.3",
"cookie": "0.0.4",
"crc": "0.2.0",
"fresh": "0.1.0",
"methods": "0.0.1",
"send": "0.0.3",
"debug": "*"
}
Express does, however, work with jade if it is available, as well as many other template engines. So to fix this list jade as a dependency in your package.json file and you'll be fine.
Second, npm only installs node packages, not third party things like mongodb. You need to install mongodb and any other dependencies that are not npm modules using other means (apt-get, yum, manual install, etc).
So npm DOES install dependencies recursively, but only npm modules.
The answer was provided by Brandon in a comment to another answer:
"Another thing to note is that if a package depends on a module that can be resolved further up in the dependency chain, it will. For example, since you have mongodb in your package.json, Mongoose doesn't need to install its own mongodb. – Brandon Tilley 2 days ago
Thank you Brandon! (And this is the answer...)
use this sample
{
"name": "app",
"version": "0.0.1",
"main":"test.js",
"author":"Test",
"description": "For test ",
"dependencies": {
"express": "latest",
"mongoose": "latest"
}
}

Resources