Using node's fs results in empty object - node.js

I'm building a simple project with Angular 6 and Electron 4.
In my project I'd want to use fs from node but it results in an empty object when I try to output it. Here's my code:
import { Component } from '#angular/core';
import * as fs from 'fs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
constructor() {
console.log(fs); // empty object
}
}
I've added "browser": {"fs": false} as suggested in this issue, and I've installed #types/node and added "types": ["./../node_modules/#types/node"] in tsconfig.app.json.
I googled a lot to find a solution but I can't get it to work. Any suggestions?

Finally found a solution to use Node.js API in Angular 6.
Installed electron-builder and follwed the instruction, I can now use regular import statement.
The configuration I mentioned above helps to suppress errors like Module 'fs' not found and to help IDEs to recognize fs module

fs module uses the os and path under the hood.
According to this answer you need to assign false to all of them in the config object.
"browser": {
"fs": false,
"path": false,
"os": false
}

Related

The injectable 'PlatformLocation' needs to be compiled using the JIT compiler, but '#angular/compiler' is not available

My Angular application is served via Node 16.13.0. After updating to Angular 13, I'm receiving the following error:
JIT compilation failed for injectable [class PlatformLocation]
file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/core/fesm2015/core.mjs:4058
throw new Error(message);
^
Error: The injectable 'PlatformLocation' needs to be compiled using the JIT compiler, but '#angular/compiler' is not available.
The injectable is part of a library that has been partially compiled.
However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.
Ideally, the library is processed using the Angular Linker to become fully AOT compiled.
Alternatively, the JIT compiler should be loaded by bootstrapping using '#angular/platform-browser-dynamic' or '#angular/platform-server',
or manually provide the compiler with 'import "#angular/compiler";' before bootstrapping.
at getCompilerFacade (file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/core/fesm2015/core.mjs:4058:15)
at Module.ɵɵngDeclareFactory (file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/core/fesm2015/core.mjs:32999:22)
at file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/common/fesm2015/common.mjs:90:28
at ModuleJob.run (node:internal/modules/esm/module_job:185:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:281:24)
at async loadESM (node:internal/process/esm_loader:88:5)
at async handleMainPromise (node:internal/modules/run_main:65:12)
I have tried numerous solutions, such as: Angular JIT compilation failed: '#angular/compiler' not loaded
Currently, I have "type": "module" in my package.json
I have updated my postinstall command to: ngcc --properties es2020 browser module main --first-only --create-ivy-entry-points
I also added import '#angular/compiler'; to my main.ts file.
The project will compile, but won't run via Node.
I believe I have found the solution (presuming you are using jest as your test runner). In the test-setup.ts file my project still was using the outdated import for jest-preset-angular. Instead of import 'jest-preset-angular'; try using import 'jest-preset-angular/setup-jest';.
This addressed the issue for me.
It seems angular 13 made babel-loader a mandatory requirement to link Ivy-native packages. https://github.com/angular/angular/issues/44026#issuecomment-974137408 - and the issue is happening as core Angular libraries are already compiled with the Ivy package format.
I haven't yet made the change in my own projects, but processing .mjs files with babel and a special linker should be enough.
import { dynamicImport } from 'tsimportlib';
/**
* Webpack configuration
*
* See: http://webpack.github.io/docs/configuration.html#cli
*/
export default async (options: IWebpackOptions) => {
const linkerPlugin = await dynamicImport('#angular/compiler-cli/linker/babel', module);
const config: any = {
module: {
rules: [{
test: /\.mjs$/,
loader: 'babel-loader',
options: {
compact: false,
plugins: [linkerPlugin.default],
},
resolve: {
fullySpecified: false
}
}
}
}
}
I'll make more updated to this post once I am able to test it myself.
If I correctly understand, you use Angular Universal also.
I want to share my experience with the same problem which came to my project after updating Angular from v8 to v13.
First of all, I want to say that the main problem was with the incorrect started file for SSR. In my project, it was server.js and now it's main.js.
So, maybe your project also tries to start from the incorrect file which doesn't have the necessary code to start it.
More details:
I updated the project step by step, increasing the version as recommended by the Angular team and according to https://update.angular.io/?l=3&v=8.2-13.0
Unfortunately, at every step, I only checked the version of SPA without SSR and only compiled the project to check that all is OK, but didn't start it with SSR. Now I can't say when the problem with the JIT compiler started.
And when I finished updating and fixing bugs with SPA, compiled the project with SSR, and tried to start it I saw this problem:
Error: The injectable 'PlatformLocation' needs to be compiled using the JIT compiler, but '#angular/compiler' is not available.
I tried different solutions like you, but nothing helped. After that, I created a new clean project with ng13 and added Angular Universal. And compare my project with new, generated by Angular-CLI.
What I changed in my project(sorry, I can't show the project - NDA):
angular.json
"projects": {
"projectName": {
...
"architect": {
...
"server": {
...
"options": {
"outputPath": "dist/server",
//"main": "src/main.server.ts", //removed
"main": "server.ts", //added
...
}
}
}
}
}
package.json
"scripts": {
...
//"serve:production:ssr": "node dist/server --production" // removed. It was incorrect file server.js which gave the error from this case
"serve:production:ssr": "node dist/server/main --production", // added
...
"postinstall": "ngcc --properties es5 browser module main --first-only" // added. In my case, actual version is es5
}
3.server.ts
import 'zone.js/node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
...
// after all imports I inserted previous code into function run()
...
export function run() {
// previous code from project with some small changes
}
//and added next code from clean project:
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from '../src/main.server';
src/main.server.ts
// was only:
export { AppServerModule } from './app/app.server.module';
// replaced by code from clean project:
/***************************************************************************************************
* Initialize the server environment - for example, adding DOM built-in types to the global scope.
*
* NOTE:
* This import must come before any imports (direct or transitive) that rely on DOM built-ins being
* available, such as `#angular/elements`.
*/
import '#angular/platform-server/init';
import { enableProdMode } from '#angular/core';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
export { AppServerModule } from './app/app.server.module';
export { renderModule, renderModuleFactory } from '#angular/platform-server';
src/main.ts
...
//this part of code
document.addEventListener("DOMContentLoaded", () => {
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.log(err));
});
// replaced by
function bootstrap() {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
};
if (document.readyState === 'complete') {
bootstrap();
} else {
document.addEventListener('DOMContentLoaded', bootstrap);
}
//but I don't think it plays a role in solving the JIT problem. Wrote just in case
tsconfig.server.json
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "./out-tsc/app-server",
"module": "commonjs",// it's only in my case, I don't have time for rewrote server.ts now
"types": ["node"],
},
"files": [
"src/main.server.ts",
"server.ts"
],
"include":["server/**/*.ts","node/*.ts"],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}
Ok, I think that's all. After all these edits I don't have file /dist/server.js and start the project from /dist/server/main.js without error from this case.
N.B.: When I was updating the project step-by-step I noticed that the process of updating nguniversal by Angular-CLI didn't change anything in the project. Only the version of packages. That's why I recommend comparing your project with an actual clean project manually

Nestjs: import modules undefined, but methods and functions from modules can be imported

I am using Nestjs with WebStorm & TS 4.2.3^latest.
The problem that I am facing is a bit strange. For example, some modules, like axios can be installed, imported, and used as usual. But some modules, especially Nodejs Core, like fs or path, can't be imported as modules. BUT their methods can be imported and used just fine!
//ERROR: Module undefined on run:dev, but no error in IDE
import path from 'path';
import fs from 'fs';
//Working fine
import { join } from 'path';
import { readFileSync } from 'path';
I am sure, they have correct TS types, even installed manually. For example:
import axios from 'axios';
import path from 'path'; //path is undefined
import { join } from 'path'; // working fine
import { Injectable } from '#nestjs/common';
#Injectable()
export class AppService {
async test(input: string): Promise<void> {
await axios.get() // working fine
await path.join() // Cannot read property 'join' of undefined
//BUT await join() // Works fine!
}
}
I have only one tsconfig.json which is generated by Nest Cli. I am starting my apps via npm start:dev -name and IDE don't show any errors in code, until I ran code.
tsconfig.json module part, just to be sure: "module": "commonjs", package.json doesn't have module part at all.
IDE in this case, misdirect me a bit. Almost forgot, that I am dealing with TS now. Some modules seem to have no default exports, so:
You should import as with them: import * as fs from 'fs';
Or, another option is enabling: "esModuleInterop": true, in your tsconfig.json

Airtable not defined after import

I am given this task to perform some stuff with data obtained from an AirTable API with NodeJS. I am using the AirTableJS node library with the DefinitelyTyped airtable definitions (I am using NestJS).
So, naturally, I do the following to import the necessary packages.
yarn add airtable #types/airtable
Then, in my repository that interacts with the AirTable API, I have the following.
import { User } from "../entities/user";
import { ConfigService } from "#nestjs/config";
import { Inject, Injectable } from "#nestjs/common";
import { LogService } from "src/log/services/log/log.service";
import { Base } from "airtable";
#Injectable()
export class UsersRepository {
private readonly apiKey: string;
private readonly baseId: string;
constructor(#Inject(ConfigService) private readonly config: ConfigService, #Inject(LogService) private readonly log: LogService) {
this.apiKey = this.config.get<string>('airtable.apiKey');
this.baseId = this.config.get<string>('airtable.baseId');
}
async getUnmatchedUsers(): Promise<User[]> {
const base: Base = new Airtable({apiKey: this.apiKey}).base(this.baseId);
// other code here
}
}
But, when running it, I get the following error relating to the repository function:
ReferenceError: Airtable is not defined
Am I missing anything here or did I not import the Airtable package correctly?
Thanks.
It's not defined because you haven't imported Airtable.
That's probably going to look like:
import Airtable, { Base } from "airtable";
Currently using Airtable in a Typescript project (tsconfig below) and the following allowed me to make api calls against my Airtable base.
Node v16.5.1
Typescript v4.7.4
Airtable v0.11.4
import Airtable from 'airtable';
Airtable.configure({
endpointUrl: "https://api.airtable.com",
apiKey: `${process.env.AIRTABLE_API_KEY}`,
});
const base = Airtable.base(AIRTABLE_BASE);
base('myTableKey')
.select({
fields: [
'fieldA', 'fieldB'
]
})
{
"compilerOptions": {
// Enable top-level await, and other modern ESM features.
"target": "ESNext",
"module": "ESNext",
// Enable node-style module resolution, for things like npm package imports.
"moduleResolution": "node",
// Enable JSON imports.
"resolveJsonModule": true,
// Enable stricter transpilation for better output.
"isolatedModules": true,
// Add type definitions for our Vite runtime.
"types": ["node","vite/client"],
"outDir": "dist",
"allowSyntheticDefaultImports": true
}
}
You don't have the Airtable class imported from anywhere, so Node and Tyepscript don't have any idea what it is. The closest you have is the Base type imported from the airtable pacakge. As their documentation isn't public, it's hard to say how to fix it.

Requiring node modules in ionic + electron (5.0.0) desktop application

I'm building a desktop application using ionic and electron.
I started using electron v4.1.3 and i was able to require node modules in the "ionic part" of the application, for example in the home.ts file by using:
import { Component } from '#angular/core';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
ngOnInit () {
console.log ((<any> window).require ("fs"));
}
}
and this is what i get:
As you can see i can access all fs methods, so i can read, write, copy files and whatever else.
Now i have installed electron v5.0.0, i created the same application but i get an error when i try to require fs module in the ngOnInit method:
window.require is not a function
How can i fix this? If you need more details on installation or envinronment just tell me, thank you!
nodeIntegration is now disabled by default in 5.0.0 as per breaking changes document.
https://github.com/electron/electron/blob/master/docs/api/breaking-changes.md#planned-breaking-api-changes-50
and the release notes
https://github.com/electron/electron/releases/tag/v5.0.0
So you need to enable it :
const mainWindow = new BrowserWindow({
webPreferences: { nodeIntegration: true }
});

How to use remote.require() in Electron, using TypeScript

Currently, I'm trying to use the opencv4nodejs module within an Electron/Angular application that also uses TypeScript. I've tried several ways to do this. The following codeblock shows what I tried and what error message I get.
// This says cv is undefined:
const cv = window.require('electron').opencv4nodejs;
const img = cv.imread('../assets/poop.jpg');
// Same here:
const cv = window.require('electron').remote.opencv4nodejs;
const img = cv.imread('../assets/poop.jpg');
// Uncaught Error: The specified module could not be found. (Though the module does exist at that location)
const cv = window.require('electron').remote.require('opencv4nodejs');
const img = cv.imread('../assets/poop.jpg');
// Without window I get "Uncaught TypeError: fs.existsSync is not a function"
const remote = require('electron').remote;
const cv = remote.opencv4nodejs;
const img = cv.imread('../assets/poop.jpg');
I had the fs.existSync error before, trying to require something else. I fixed that by using the following tsconfig.app.json:
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "es2015",
"types": ["node"] // Included this line
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
As far as I understand, the remote require is needed to load modules that normally only run on the node.js server. Still I can't seem to figure out how to require the module in my application. The author of the module was very helpful with build problems and other problems, but he never used his module together with TypeScript.
How do I remote require a module in a TypeScript/Angular/Electron based application?
[edit]
I also tried the following:
import { Injectable } from '#angular/core';
// If you import a module but never use any of the imported values other than as TypeScript types,
// the resulting javascript file will look as if you never imported the module at all.
import { ipcRenderer } from 'electron';
import * as childProcess from 'child_process';
#Injectable()
export class ElectronService {
ipcRenderer: typeof ipcRenderer;
childProcess: typeof childProcess;
constructor() {
// Conditional imports
if (this.isElectron()) {
this.ipcRenderer = window.require('electron').ipcRenderer;
this.childProcess = window.require('child_process');
}
}
isElectron = () => {
return window && window.process && window.process.type;
}
require = (module: string) => {
return window.require('electron').remote.require(module);
}
}
Injecting this service into my component and calling electronService.require('opencv4nodejs') also did not work.
Since Electron v1.6.10, Electron ships with TypeScript definitions included.
In order to use remote via TypeScript, you can use the following import statement:
import {remote} from 'electron';

Resources