angular-cli: Can't resolve 'net'/'global' or other node globals

Versions

Angular CLI: 6.0.0-beta.4
Node: 8.9.3
OS: win32 x64
Angular: 6.0.0-beta.6
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

@angular/cdk: 5.2.3
@angular/cli: 6.0.0-beta.4
@angular/material: 5.2.3
@angular-devkit/build-optimizer: 0.4.3
@angular-devkit/core: 0.4.3
@angular-devkit/schematics: 0.4.3
@ngtools/json-schema: 1.2.0
@ngtools/webpack: 6.0.0-beta.4
@schematics/angular: 0.4.3
@schematics/package-update: 0.4.3
typescript: 2.6.2
webpack: 4.0.1

Repro steps

udpate from 1.7.2 to @next branch beta4

Observed problems:

1

it was working in 1.7.2 but not in @next branch I get in one module problem image But I fixed it after “npm i net”

2

after reinstalling 6beta4 I get:

Could not find local "typescript" package.The "@ngtools/webpack" package requires a local "typescript@^2.4.2" package to be installed.Error: Cannot find module 'typescript'
Error: Could not find local "typescript" package.The "@ngtools/webpack" package requires a local "typescript@^2.4.2" package to be installed.Error: Cannot find module 'typescript'
    at Object.<anonymous> (C:\...\i10\node_modules\@ngtools\webpack\src\index.js:18:11)
    at Module._compile (module.js:635:30)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Module.require (module.js:579:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (C:\...node_modules\@angular\cli\models\webpack-config.js:3:19)
    at Module._compile (module.js:635:30)

Fixed after “npm update --next” and “npm i”

3

Then I have an deprecation warning: image

4

Then I have strange long chunk name image

5

Another point is when running ng build --build-optimizer --prod It block at step : 70% building modules without never finish .

Mention any other details that might be useful (optional)

Maybe this is due to webpack4.0 resolution, any temp idea?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 39 (14 by maintainers)

Commits related to this issue

Most upvoted comments

Heya, first of all I want to thank you all for trying the 6.x versions. They are still pretty raw and getting feedback early is super important to address problems.

The build times and chunkname bugs are unexpected, probably to do with Webpack 4 interop. We expect those to get fixed soon.

The problem with net/global is not so straightforward. We are removing it for 6.x, yes (https://github.com/angular/angular-cli/pull/9812). And we do expect this to cause problems to some projects. But we’re not doing it because we want to break projects, we’re doing it because leaving it in also breaks other projects, and it is incorrect.

The incorrect part is libraries that are meant to run in the browser relying on node globals being available. Browser code runs in the browser, not in node, and shouldn’t expect things that are not available in a browser context to be there.

For instance, if you open up your browser and type require('fs') you will get an error saying require does not exist. require is only available in node. But if you tried to import it some other way, like import('fs') (chrome supports import at the moment), you will also get an error saying it doesn’t know what fs is. fs is also available only in node.

Some libraries use those constructs because they expect to be built in node, but to run in the browser. And they also expect the tooling that builds them to either provide a browser version of those node built-ins, or to pretend it’s there when it actually is not.

In Angular CLI we never provided a browser version of node built-ins. But we did:

  • provide a shim for global and process.
  • supply an empty module when fs, crypto, tls and net were requested.

This is a problematic situation because even that can break some libraries (https://github.com/angular/angular-cli/issues/5804), increase the size of others (https://github.com/angular/angular-cli/pull/8130#pullrequestreview-71174772), and just generally make for a situation where browser code that shouldn’t work at all works only when built in with very specific tooling. This is not a good situation. Browser code should not rely on things that are not available in browser environments.

We investigated the topic and reached this conclusion some time ago but did not remove support for this broken behaviour in CLI 1.x to avoid a breaking change. But now with 6.x it is a good time to make needed breaking changes.

You can find some more context in https://github.com/angular/angular-cli/pull/8250, https://github.com/angular/angular-cli/pull/8130, https://github.com/angular/angular-cli/issues/8160, https://github.com/angular/angular-cli/issues/5804 and https://github.com/angular/angular-cli/issues/1548

We understand that this isn’t great if your code relies, directly or indirectly, on a library that makes incorrect assumptions about browser environments. The best I can say is that you should bring this problem to their attention via an issue on their tracker. Maybe newer versions of that library don’t have this behaviour anymore.

But although it is inconvenient to address these problems, I hope we can agree that the current behaviour is incorrect. Browser code should not rely on things that are not available in browser environments.

On version 6 of Angular CLI we are removing the shim for global and other node built-ins as I mentioned in https://github.com/angular/angular-cli/issues/9827#issuecomment-369578814.

If you are using a library that assumes these globals are present, you can try manually shimming it inside your polyfills.ts file:

// Add global to window, assigning the value of window itself.
(window as any).global = window;

I ran a quick test with socket.io-client and this seemed to allow it to run.

@filipesilva I understand the reasoning behind this, but not providing a recommendation for dealing with it or a switch to disable it is unreasonable. The suggestion of submitting a ticket to all of the repos related to the dependencies my app has is almost laughable and unrealistic. We need a way to deal with this in the near term if anyone is going to be able to migrate their angular 5 apps to angular 6

@filipesilva is there any chance we could pass in an override for the node webpack config to override the false? Since the options for the node property are all booleans or strings, it’s completely encodable as json, and would be straightforward to add it to the .angular-cli.json config.

If you’ll consider a PR adding support for that possibility, I’d be happy to make it.

I am having hard time to understand this. Please correct me if I am missing anything.

“Browser code should not rely on things that are not available in browser environments.” sounds perfectly correct. But the situation can be phrased in another form,

“No library should support both browser and node”. This is what I am having trouble with.

As a library developer it is pretty normal to have both browser code and node code, then run according to environment.

Are we saying this is wrong? Any library in order to work with Angular has to be pure?

Sorry if I misunderstood something. Any clarification is appreciated.

@filipesilva :

The incorrect part is libraries that are meant to run in the browser relying on node globals being available. Browser code runs in the browser, not in node, and shouldn’t expect things that are not available in a browser context to be there.

That’s why some libraries have code that looks like this :

if (ENVIRONMENT_IS_NODE) {
  var fs = require("fs");
  var NODEJS_PATH = require("path");
  NODEFS.staticInit();
};

Such code used to run fine in Angular 5.

However, in Angular 6, this same code produces the following errors :

ERROR in ./lib/luciad/photon/photon_painter.js Module not found: Error: Can’t resolve ‘fs’ in ‘D:\RIA ANGULAR\ria-angular-cli-2\lib\luciad\photon’ ERROR in ./lib/luciad/photon/photon_painter.js Module not found: Error: Can’t resolve ‘path’ in ‘D:\RIA ANGULAR\ria-angular-cli-2\lib\luciad\photon’

This is simply ridiculous.


@clydin :

This is statement is not what is implied by the first:

“No library should support both browser and node”.

Rather, a library should not assume its environment if it intends to support both node and a browser.

Of course. And that’s why libraries have stuff like if (ENVIRONMENT_IS_NODE) { }, which now no longer seems possible with Angular 6. Thus, compatibility with Angular 6 prevents libraries from supporting both browser & Node.

None of those are truly wrong. They are all design decisions based on the complexity of the code, volume of platform-dependent APIs, and personal choice. The basic conditional, for instance, is quite acceptable if the API usage is minimal; or if combined with an abstraction layer that is then used throughout the package.

So are you saying I CAN use libraries that have stuff like if (ENVIRONMENT_IS_NODE) { } in them in Angular 6?

And if so, what would I need to do to get this to work?

For anyone still looking for an answer, here’s how I managed to require(‘fs’) in my angular 7 app. Or for that matter, any other node module.

Versions

Angular CLI: 7.0.4
Node: 10.13.0
OS: win32 x64
    "@angular/animations": "~7.0.0",
    "@angular/common": "~7.0.0",
    "@angular/compiler": "~7.0.0",
    "@angular/core": "~7.0.0",
    "@angular/forms": "~7.0.0",
    "@angular/http": "~7.0.0",
    "@angular/platform-browser": "~7.0.0",
    "@angular/platform-browser-dynamic": "~7.0.0",
    "@angular/router": "~7.0.0",
    "@angular-devkit/build-angular": "~0.10.0",
    "@angular/cli": "~7.0.4",
    "@angular/compiler-cli": "~7.0.0",
    "@angular/language-service": "~7.0.0",
    "electron": "^3.0.7",
    "typescript": "~3.1.1"

1. Install @types/node

npm install --save-dev @types/node

2. Modify tsconfig.json

Take note of “allowSyntheticDefaultImports” flag. It must be set to true.

{
  "compileOnSave": false,
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "es2015",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "types": [
      "node"
    ],
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ],
    "strict": false
  }
}

3. Require fs

import { Component } from '@angular/core';
import { } from 'electron';
import Fs from 'fs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  constructor() {
    //check if platform is electron
    let isElectron: boolean = window && window['process'] && window['process'].type;
    
    if (isElectron) {
      let fs: typeof Fs = window['require']('fs');
      let app: Electron.App = window['require']('electron').remote;
      console.log(fs, app, window['process']);
    }
  }
}

Note: The import statements at the top of the file are just to provide for type information. The variable values are set using node require.

4. Result

electron-angular

Edit:

Turns out, that if your project has dependencies that require 'fs', 'path', 'child_process' etc. The angular compiler fails to compile the code. To get around this, as someone has already suggested, add (window as any).global = window; to your polyfills.ts.

In my case, I had chokidar, node-pty and electron as a dependency. This worker for me.

@alexzuza A path mapping in the app’s tsconfig should allow redirection of the jszip import to the jszip/dist/jszip.min.js file.

Untested example:

paths: {
      'jszip': [ '../node_modules/jszip/dist/jszip.min.js' ],
    }

Ideally, since the package appears to ship a browser bundled version it should point the package.json field browser to the file. The CLI would then pick up that file and use it directly.

@clydin Big thank’s for clarification.

It would be great if it was written under

BREAKING CHANGES @angular/cli: Libraries/Packages must contain a web version if not web specific.

Update

The paths option works

Now only one thing annoys me 😃

If we run cli as ng serve -o then new window will be opened after each rebuild.

@jskrzypek I don’t import jszip directly. It’s one of dependencies of my dependencies

in an angular 7 app the workaround (https://github.com/angular/angular-cli/issues/9827#issuecomment-386154063) only works for me if i put

(window as any).global = window;

in a .ts file and import it in polyfill.ts , e.g. import ‘cstm-polyfill.ts’;

well seems like someone else is even more frustrated than me ;D

@alexzuza I managed to fix my issue with jszip by changing the import from 'jszip' to 'jszip/dist/jszip.min', which is the more expected choice for web apps I guess…

@alexzuza @istiti I fairly certain your original issue with needing net is coming from this commit: https://github.com/angular/angular-cli/commit/e84baa1d4e65dba4c64ff4632f2e8ea1a964b1f8 (master) / https://github.com/angular/angular-cli/commit/c7a8b619fb67b42e346b98fb6e6629cf4aace850 (6.0.x) (they’re the same commit just in different branches form the release process afaik)

If you look, they’ve removed all of the node globals shimming. I’m currently trying to figure out how to overcome that, and put the node builtins back in the bundle.

We’re having the same issue with jszip: slack_-_yoobic

Why cli shows a warning from a library which requests a stream global inside the try/catch block? e.g. https://github.com/isaacs/sax-js/issues/237

@shinde-ha using https://github.com/manfredsteyer/ngx-build-plus to alter the webpack config is the most convenient option I think.

Hello, is there any lib for deflation that can be used with Angular 6 ? Since the upgrade I am having the following issues with these zlibs: https://github.com/devongovett/browserify-zlib/issues/36 https://github.com/imaya/zlib.js/issues/74 I’ve tried some workarounds, described in the issues above but to no avail. p.s. I use pako for the deflation without any issue, however, the above is still valid for the mentioned libs.

Any update how to solve those issues?

Thank you @clydin for the response. What I meant was not assuming, but detecting. Basically something like

if (is_browser) {
  // browser code
} else {
  // node code
}

So this is wrong. Instead should choose between these two options, 1) have separate packages; 2) specify browser field in package.json to indicate browser entry point.

correct?

@istiti we’re actually in the middle of moving a lot of things around and hope to have a more stable 6.0 beta soon, so some of those problems are not going to be solved in the next beta but should all be taken care of before 6.0 final.

@alexzuza You’re welcome. We are working on that last mentioned annoyance and should be fixed in the next release.