Use node Path module with angular 6 - node.js

I'm trying to use the module Path in an Angular 6 project.
I found this post to fix the issue :
https://gist.github.com/niespodd/1fa82da6f8c901d1c33d2fcbb762947d
it says to add a script :
const fs = require('fs');
const f = 'node_modules/#angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
fs.readFile(f, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
var result = data.replace(/node: false/g, 'node: {crypto: true, stream: true}');
fs.writeFile(f, result, 'utf8', function (err) {
if (err) return console.log(err);
});
});
And declare it in package.json :
{...
"scripts": {
"postinstall": "node patch.js",
...
}
}
But when I'm trying to use it in a service, just import it like this :
import {join} from 'path';
It says that the module Path cannot be found.
How can I correct this ?

Interesting problem.
I managed to get Path module work on my Angular project.
Here are the steps.I use node 8, angular 6.
1: install path.
npm install path
This is an exact copy of the NodeJS ’path’ module published to the NPM registry.
2, I also installed #types/node as in Angular we are using typescript.
Although later I removed this module and path module seems still works.
3, run the above script using
node patch.js
I manually run it and go to 'node_modules/#angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js' to check the file actually changed.
4, I put
import {join} from 'path';
in one of my component.ts file
let x = join('Users', 'Refsnes', '..', 'demo_path.js');
console.log("-------------------------------------------------");
console.log(x);
in a Component's onInit() function.
and run "ng serve"
I saw the expected output in my console when loading the webpage.
-------------------------------------------------
Users/demo_path.js
So this method does work. I am not sure which step you did wrong. My guess would be the first step as I tried if not do step 3 there's different error message. Please check your node_modules folder and verify path folder exists and reinstall it if necessary.

Be sure to have node types installed: npm install --save-dev #types/node
Import path: import * as path from 'path';

Related

How to deal with node modules in the browser?

My title is a bit vague, here is what I'm trying to do:
I have a typescript npm package
I want it to be useable on both node and browser.
I'm building it using a simple tsc command (no bundling), in order to get proper typings
My module has 1 entry point, an index.ts file, which exposes (re-exports) everything.
Some functions in this module are meant to be used on node-only, so there are node imports in some files, like:
import { fileURLToPath } from 'url'
import { readFile } from 'fs/promises'
import { resolve } from 'path'
// ...
I would like to find a way to:
Not trip-up bundlers with this
Not force users of this package to add "hacks" to their bundler config, like mentioned here: Node cannot find module "fs" when using webpack
Throw sensible Errors in case they are trying to use node-only features
Use proper typings inside my module, utilizing #types/node in my code
My main problem is, that no matter what, I have to import or require the node-only modules, which breaks requirement 1 (trips up bundlers, or forces the user to add some polyfill).
The only way I found that's working, is what isomorphic packages use, which is to have 2 different entry points, and mark it in my package.json like so:
{
// The entry point for node modules
"main": "lib/index.node.js",
// The entry point for bundlers
"browser": "lib/index.browser.js",
// Common typings
"typings": "lib/index.browser.d.ts"
}
This is however very impractical, and forces me to do a lots of repetition, as I don't have 2 different versions of the package, just some code that should throw in the browser when used.
Is there a way to make something like this work?
// create safe-fs.ts locally and use it instead of the real "fs" module
import * as fs from 'fs'
function createModuleProxy(moduleName: string): any {
return new Proxy(
{},
{
get(target, property) {
return () => {
throw new Error(`Function "${String(property)}" from module "${moduleName}" should only be used on node.js`)
}
},
},
)
}
const isNode = typeof window === undefined && typeof process === 'object'
const safeFs: typeof fs = isNode ? fs : createModuleProxy('fs')
export default safeFs
As it stands, this trips up bundlers, as I'm still importing fs.

Accessing filesystem in Angular 2 app using Electron

I know that Angular 2 is run on a web browser, which does not have access to the file system.
However, I'm using Electron as my front-end, and also running the app via electron:
"build-electron": "ng build --base-href . && cp src/electron/* dist",
"electron": "npm run build-electron && electron dist"
Therefore, I run it with npm run electron which at the very end runs electron dist.
Since I'm running through electron and not ng I would think that I should be able to access the filesystem. However, when I do:
import * as fs from 'fs'
I get an error:
ng:///AppModule/AppComponent_Host.ngfactory.js:5 ERROR TypeError: __WEBPACK_IMPORTED_MODULE_0_fs__.readFileSync is not a function(…)
Similarly, when I try: var fs = require('fs');
I get:
ng:///AppModule/AppComponent_Host.ngfactory.js:5 ERROR TypeError: fs.readFileSync is not a function
This is the call resulting in the error:
this.config = ini.parse(fs.readFileSync('../../CONFIG.ini', 'utf-8'))
Does anyone have any idea what's causing this?
Thanks.
Solved it by:
1) Eject webpack: ng eject
2) Add target: 'electron-renderer' to the module.exports array inside webpack.config.js
3) Require remote, since we're in the renderer, but fs is only available in the main process (Read more): var remote = require('electron').remote;
4) Require fs (this time using remotes implementation of require): var fs = remote.require('fs');
And now it works!
I am using
Angular CLI: 7.0.7
Node: 8.12.0
OS: win32 x64
Angular: 7.0.4
I tried the ng eject method it didn't work in my case, it is disabled by default and will be removed completely in Angular 8.0
Error message: The 'eject' command has been disabled and will be removed completely in 8.0.
It worked for me by creating a file called native.js in the src folder and insert the following:
`window.fs = require('fs');
Add this file to the angular-cli.json scripts array:
"scripts": [
"native.js"
]
Add the following lines to polyfills.ts:
`declare global {
interface Window {
fs: any;
}
}`
After that you can access the filesystem with:
`window.fs.writeFileSync('sample.txt', 'my data');`
credits
As I understand it, you build the application with Webpack.
You can expose all Node modules via the externals array in your webpack config.
module.exports = {
"externals": {
"electron": "require('electron')",
"child_process": "require('child_process')",
"fs": "require('fs')",
"path": "require('path')",
...
}
}
Since they are provided through the Webpack externals, one does not have to require them but use them with imports.
import * as fs from 'fs'
You can read more about this problem in my article.
I'm late to the party but I also stumbled upon this problem recently. To the late comers, you can use ngx-fs
https://github.com/Inoverse/ngx-fs
Usage:
const fs = this._fsService.fs as any;
fs.readdir("\\", function (err, items) {
if (err) {
return;
}
for (let i = 0; i < items.length; i++) {
console.log(items[i]);
}
});
I had the same problem and could solve it in an easier way:
Just download this project as start, the 'require'-s are already in the webpack.config.js file (along with the integration of angular, electron and so on):
https://github.com/maximegris/angular-electron
import 'fs' into home.ts (or into any other component) as mentioned by #Matthias Sommer above:
import * as fs from 'fs'
Use 'fs' :)

Cannot find module './lib/BufferMaker' when use buffermaker in Meteor 1.5.1

I have encountered a problem when use some npm package in Meteor (version 1.5.1), any help on it will be much appreciated.
My Environment:
meteor: 1.5.1
buffermaker: 1.2.0
What I Did:
Create a sample Meteor app.
meteor create test
Install buffermaker
meteor npm install --save buffermaker
Import buffermaker in Meteor app by editing test/client/main.js, add line:
import { BufferMaker } from 'buffermaker';
Full content of test/client/main.js:
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { BufferMaker } from 'buffermaker';
import './main.html';
Template.hello.onCreated(function helloOnCreated() {
// counter starts at 0
this.counter = new ReactiveVar(0);
});
Template.hello.helpers({
counter() {
return Template.instance().counter.get();
},
});
Template.hello.events({
'click button'(event, instance) {
// increment the counter when button is clicked
instance.counter.set(instance.counter.get() + 1);
},
});
Run the Meteor app
meteor npm install
meteor
I got this error in the console of browser (Chrome).
modules-runtime.js?hash=8587d18…:231 Uncaught Error: Cannot find module './lib/BufferMaker'
at makeMissingError (modules-runtime.js?hash=8587d18…:231)
at require (modules-runtime.js?hash=8587d18…:241)
at index.js (modules.js?hash=e9fc8db…:1016)
at fileEvaluate (modules-runtime.js?hash=8587d18…:343)
at require (modules-runtime.js?hash=8587d18…:238)
at main.js (main.js:1)
at fileEvaluate (modules-runtime.js?hash=8587d18…:343)
at require (modules-runtime.js?hash=8587d18…:238)
at app.js?hash=3f48780…:101
Did you try:
import BufferMaker from 'buffermaker';
Some if not most modules do a default export meaning that you don't need the curley braces in your import statement
Turns out buffermaker re-exports it’s main module in a strange way, so first step is to bypass it by importing BufferMaker directly:
import BufferMaker from 'buffermaker/lib/BufferMaker';
Then you’ll find when you call .make(), it will complain about Buffer not existing. To get Buffer on the client, first install meteor-node-stubs
$ meteor npm install --save meteor-node-stubs
Then load the buffer module and stick it on the window so BufferMaker can access it
import { Buffer } from 'buffer';
window.Buffer = Buffer;
/* OR do it with require */
window.Buffer = require('buffer').Buffer;

Angular 2 - Import html2canvas

I have installed html2canvas on my angular 2 project using npm install html2canvas --save. If I now go to any file and write import * as html2canvas from 'html2canvas' it gives the error:
Cannot find module 'html2canvas'
My package file looks like this:
{
...
"scripts": {
...
},
"dependencies": {
...
"html2canvas": "^0.5.0-beta4",
...
},
"devDependencies": {
...
}
}
The file on which I'm trying to import the html2canvas is:
import { Injectable } from '#angular/core';
import * as jsPDF from 'jspdf';
import * as html2canvas from 'html2canvas';
#Injectable ()
export class pdfGeneratorService {
...
}
Since Angular2 uses typescript, you need to install the typescript definition files for that module.
It can be installed from #types (if it exists). If it doesn't you can create your own definition file and include it in your project.
in angular 9 i use it this way:
import html2canvas from 'html2canvas';
....
html2canvas(this.head2print.nativeElement).then(_canvas => {
hdr = _canvas.toDataURL("image/png");
});
Also, the onrendered option for callback function may not work. Instead, you may use "then" as below:
html2canvas(document.body).then((canvas) => {
document.body.appendChild(canvas);
});
https://stackoverflow.com/a/45366038/3119507
Ran into the same issue running Angular 8. It still didn't work after installing the #types. What worked for me was to include the html2canvas library using require instead.
const html2canvas = require('../../../node_modules/html2canvas');
Then to take the screenshot:
#ViewChild('screenshotCanvas') screenCanvas: ElementRef;
html2canvas(this.screenCanvas.nativeElement).then(canvas => {
var imgData = canvas.toDataURL("image/png");
console.log("ENTER takeScreenshot: ",imgData )
document.body.appendChild(imgData);
})

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

Resources