Webpack ContextReplacementPlugin get packages from node_modules - node.js

We are trying to make a webpack bundle, which will include all of our dependencies inside a single bundle.
There is a specific package, which has dynamic requires from node_modules, and webpack can't resolve the packages correctly at compile time.
The code that generates the error is:
// config.middlewares is a list of packages inside node_modules
Object.keys(config.middlewares).forEach(function (moduleName) {
var pkg = require(moduleName)
var alias = config.middlewares[moduleName]
helmet[alias] = pkg
})
Webpack compiles it into:
Object.keys(config.middlewares).forEach(function (moduleName) {
var pkg = !(function webpackMissingModule() { var e = new Error("Cannot find module \".\""); e.code = 'MODULE_NOT_FOUND'; throw e; }())
var alias = config.middlewares[moduleName]
helmet[alias] = pkg
})
So we tried using Webpack's ContextReplacementPlugin, but whatever we tried we couldn't configure it to change the context to fetch a file from node_modules.
Somethings we tried (we first tried to just get one specific package, called hsts):
new ContextReplacementPlugin(/helmet.*/, /\.\.\/hsts/)
new ContextReplacementPlugin(/helmet.*/, /\.\/\.\.\/hsts/)
new ContextReplacementPlugin(/helmet.*/, /\.\/\.\.\/hsts\/index\.js/)
```

Related

Is there a way to extract package.json from package-lock.json?

I'm working on a project in which the package.json file is missing. The developer has pushed the package-lock.json file without the package.json file.
How can I create a clean package.json from the package-lock.json file in case it is at all possible?
It's not possible to generate full package.json from package-lock.json because the latter doesn't contain all necessary data. It contains only a list of dependencies with specific versions without original semvers. Production and development dependencies are mixed up along with nested dependencies.
Fresh package.json could be generated, then augmented with these dependencies with something like:
const fs = require('fs');
const packageLock = require('./package-lock.json');
const package = require('./package.json');
package.dependencies = Object.entries(packageLock.dependencies)
.reduce((deps, [dep, { version }]) => Object.assign(deps, { [dep]: version }), {});
fs.writeFileSync('./package-new.json', JSON.stringify(package, null, 2));
Nested dependencies could be filtered out by checking requires key, but this can affect project's own dependencies.
Simply run npm init and it will pull all of the current dependencies from package-lock.json if you already have node_modules/ generated. If not, run npm ci to generate the node modules from the package-lock.json and then run npm init to generate the package.json file.
Slightly improved version of accepted answer script. Will pull locked versions out of the package-lock.
const fs = require('fs');
const packageLock = require('./package-lock.json');
const package = require('./package.json');
package.dependencies = Object.keys(package.dependencies)
.reduce((deps, dep) => Object.assign(deps, { [dep]: packageLock.dependencies[dep].version }), {});
package.devDependencies = Object.keys(package.devDependencies)
.reduce((deps, dep) => Object.assign(deps, { [dep]: packageLock.dependencies[dep].version }), {});
fs.writeFileSync('./package-new.json', JSON.stringify(package, null, 2));

Error: Cannot find module 'togeojson'

I am attempting to use this module in node.js and am running into an "Error: Cannot find module 'togeojson'" error when I attempt to use the documented example code:
// using togeojson in nodejs
var tj = require('togeojson'),
fs = require('fs'),
// node doesn't have xml parsing or a dom. use xmldom
DOMParser = require('xmldom').DOMParser;
var kml = new DOMParser().parseFromString(fs.readFileSync('foo.kml', 'utf8'));
var converted = tj.kml(kml);
var convertedWithStyles = tj.kml(kml, { styles: true });
I ran npm init in the same directory that my app.js file (where the above code resides) is stored and I used the --save flag when installing the #mapbox/togeojson package to my application.
I am running node version 8.11.2 and npm v 6.1.0.
How do I go about debugging an issue like this in node/npm?
It is #mapbox/togeojson package, not togeojson, so it should be required like:
var tj = require('#mapbox/togeojson');

gulp can't find module source-map

I'm having an issue with sourcemaps in gulp: when I try to implement gulp-sourcemaps directly through a pipe, but also when I try to feed it in via webpack, which if I've understood correctly, has sourcemaps by default, I keep getting the same confusing error:
Error: Cannot find module 'source-map'
I can see the #gulp-sourcemaps folder in my node_modules, which contains within it an identity-map folder containing a bunch more node_modules including the source-map module in question, so I think everything is hooked up as it should be. That being said, I'm new to gulp, so I might be missing something really obvious.
Can anyone offer me some guidance on how to help gulp find the module?
// package vars
const pkg = require("./package.json");
// gulp
const gulp = require("gulp");
const gulpIf = require("gulp-if");
// webpack
const webpack_config = require("./webpack.config");
// load all plugins in "devDependencies" into the variable $
const $ = require("gulp-load-plugins")({
pattern: ["*"],
scope: ["devDependencies"]
});
// ...
gulp.task("sass", function() {
return gulp
.src(pkg.paths.app.scss + "**/*.scss")
.pipe($.sourcemaps.init())
.pipe($.sass())
.pipe($.autoprefixer())
.pipe($.sourcemaps.write("./maps"))
.pipe(gulp.dest(pkg.paths.app.css))
.pipe(
$.browserSync.reload({
stream: true
})
);
});
// ...
gulp.task("webpack", function() {
return gulp
.src(pkg.paths.app.js + "**/*.js")
.pipe($.webpack(webpack_config))
.pipe(gulp.dest(pkg.paths.public.js));
});
// ...

Importing Sass through npm

Currently in our Sass files we have something like the following:
#import "../../node_modules/some-module/sass/app";
This is bad, because we're not actually sure of the path: it could be ../node_modules, it could be ../../../../../node_modules, because of how npm installs stuff.
Is there a way in Sass that we can search up until we find node_modules? Or even a proper way of including Sass through npm?
If you are looking for a handy answer in 2017 and are using Webpack, this was the easiest I found.
Suppose your module path is like:
node_modules/some-module/sass/app
Then in your main scss file you can use:
#import "~some-module/sass/app";
Tilde operator shall resolve any import as a module.
As Oncle Tom mentioned, the new version of Sass has this new importer option, where every "import" you do on your Sass file will go first through this method. That means that you can then modify the actual url of this method.
I've used require.resolve to locate the actual module entry file.
Have a look at my gulp task and see if it helps you:
'use strict';
var path = require('path'),
gulp = require('gulp'),
sass = require('gulp-sass');
var aliases = {};
/**
* Will look for .scss|sass files inside the node_modules folder
*/
function npmModule(url, file, done) {
// check if the path was already found and cached
if(aliases[url]) {
return done({ file:aliases[url] });
}
// look for modules installed through npm
try {
var newPath = path.relative('./css', require.resolve(url));
aliases[url] = newPath; // cache this request
return done({ file:newPath });
} catch(e) {
// if your module could not be found, just return the original url
aliases[url] = url;
return done({ file:url });
}
}
gulp.task("style", function() {
return gulp.src('./css/app.scss')
.pipe(sass({ importer:npmModule }))
.pipe(gulp.dest('./css'));
});
Now let's say you installed inuit-normalize using node. You can simply "require" it on your Sass file:
#import "inuit-normalize";
I hope that helps you and others. Because adding relative paths is always a pain in the ass :)
You can add another includePaths to your render options.
Plain example
Snippet based on example from Oncle Tom.
var options = {
file: './sample.scss',
includePaths: [
path.join(__dirname, 'bower_components'), // bower
path.join(__dirname, 'node_modules') // npm
]
};
sass.render(options, function(err, result){
console.log(result.css.toString());
});
That should do. You can include the files from package using #import "my-cool-package/super-grid
Webpack and scss-loader example
{
test: /\.scss$/,
loader: 'style!css!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap=true&sourceMapContents=true&includePaths[]=./node_modules'
},
Notice the last argument, includePaths has to be array. Keep in mind to use right format
You can use a Sass importer function to do so. Cf. https://github.com/sass/node-sass#importer--v200.
The following example illustrates node-sass#3.0.0 with node#0.12.2:
Install the bower dependency:
$ bower install sass-mq
$ npm install sass/node-sass#3.0.0-pre
The Sass file:
#import 'sass-mq/mq';
body {
#include mq($from: mobile) {
color: red;
}
#include mq($until: tablet) {
color: blue;
}
}
The node renderer file:
'use strict';
var sass = require('node-sass');
var path = require('path');
var fs = require('fs');
var options = {
file: './sample.scss',
importer: function bowerModule(url, file, done){
var bowerComponent = url.split(path.sep)[0];
if (bowerComponent !== url) {
fs.access(path.join(__dirname, 'bower_components', bowerComponent), fs.R_OK, function(err){
if (err) {
return done({ file: url });
}
var newUrl = path.join(__dirname, 'bower_components', url);
done({ file: newUrl });
})
}
else {
done({ file: url });
}
}
};
sass.render(options, function(err, result){
if (err) {
console.error(err);
return;
}
console.log(result.css.toString());
});
This one is simple and not recursive. The require.resolve function could help to deal with the tree – or wait until npm#3.0.0 to benefit from the flat dependency tree.
I made the sass-npm module specifically for this.
npm install sass-npm
In your SASS:
// Since node_modules/npm-module-name/style.scss exists, this will be imported.
#import "npm-module-name";
// Since just-a-sass-file isn't an installed npm module, it will be imported as a regular SCSS file.
#import "just-a-sass-file";
I normally use gulp-sass (which has the same 'importer' option as regular SASS)
var gulp = require('gulp'),
sass = require('gulp-sass'),
sassNpm = require('sass-npm')();
Then, in your .pipe(sass()), add the importer as an option:
.pipe(sass({
paths: ['public/scss'],
importer: sassNpm.importer,
}))
For dart-sass and commandline user at 2022, just use the --load-path option:
$ npx sass --load-path=node_modules
Important: the whole node_modules folder contains so much, just set it launch extremely slow in watch mode. Your should only set your package paths, eg:
$npx sass -w --load-path=node_modules/foo --load-path=node_modules/bar/scss
From offical docuumentation of Sass, adding ~ to imports should do the job.
However, for some reason it did'nt work for me, and sass compiler still complains that the module cannot be found.
Hence, I tried another method which worked for me without any issues. Here's the solution:
If you are compiling sass files directly from CLI try this:
sass src/main.scss dist/main.css --load-path=node_modules
If you are using npm and/or webpack for compiling sass files, add something like this to the scripts of package.json:
"scripts": {
...
"build": "sass src/main.scss dist/main.css --load-path=node_modules",
...
}
Then Run:
npm run build
Finally, import your modules like this:
#import "some-module/sass/app";
To wrap it up, adding --load-path=node_modules flag solved the issue permanently. For more information you can check:
sass --help

Installing bower packages after running yeoman generator

I'm having trouble getting this to work and even finding solutions through google on how to make it work.
Going to Bowers site shows they have a programatic API that looks like I should be able to run it in node, of course I can however it's not obeying my .bowerrc file and installing them into my dev folder created by yeoman.
Does this have something to do with the way yeoman works? Are the files and directories not quite available yet until after it's logged done()?
Here is my index.js
'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var bower = require('bower');
var FoprojectGenerator = yeoman.generators.Base.extend({
sayHello: function(){
console.log(this.yeoman);
},
scaffoldFolders: function(){
this.mkdir("working");
this.mkdir("working/assets");
this.mkdir("working/assets/sass");
this.mkdir("working/assets/coffee");
this.mkdir('dev');
},
copyMainFiles: function(){
this.copy("_index.html", "working/index.html");
this.copy("_gruntfile.js", "Gruntfile.js");
this.copy("_package.json", "package.json");
this.copy("_bower.json", "bower.json");
this.copy("_.bowerrc", ".bowerrc");
this.copy("assets/sass/_site.sass", "working/assets/sass/site.sass");
this.copy("assets/sass/_mixins.sass", "working/assets/sass/_mixins.sass");
this.copy("assets/sass/_normalize.sass", "working/assets/sass/_normalize.sass");
this.copy("assets/coffee/_scripts.coffee", "working/assets/coffee/scripts.coffee");
},
installDependencies: function(){
var done = this.async();
console.log("\nInstalling Node Dependencies\n");
this.npmInstall("", function(){
console.log("\nInstalling Bower Packages\n");
bower.commands
.install()
.on('end', function(){
done();
});
});
}
});
module.exports = FoprojectGenerator;
Like I said it runs great, but it installs it next to the bower.json as apposed to in the dev folder like I've defined in the .bowerrc file like so
{
"directory": "dev/bower_components"
}
When I run bower install after yeoman is done it installs the bower_components folder in the dev folder like it should.
Any guidance would be greatly appreciated!
Maybe the Yeoman generator you use could force you to install deps on a certian specific path.
Open Terminal and go to the root directory of your app.
type ls -la.
if you see .yo-rc.json, type cat .yo-rc.json.
Does it show any particular config for the path like below?
{
"generator-backbone-laser": {
"appPath": "app"
}
}
if so, delete .yo-rc.json.
a creator of Yoeman's generator sometimes set app path on that file.

Resources