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

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 }
});

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

Is there a way to use Vite with HMR and still generate the files in the /dist folder?

First of all, I wanna say that I've started using Vite awhile ago and I'm no Vite expert in any shape or form.
Now, about my problem: I'm working on a Chrome Extension which requires me to have the files generated in the /dist folder. That works excellent using vite build. But, if I try to use only vite (to get the benefits of HMR), no files get generated in the /dist folder. So I have no way to load the Chrome Extension.
If anyone has faced similar issues, or knows a config that I've overlooked, feel free to share it here.
Thanks!
With this small plugin you will get a build after each hot module reload event :
In a file hot-build.ts :
/**
* Custom Hot Reloading Plugin
* Start `vite build` on Hot Module Reload
*/
import { build } from 'vite'
export default function HotBuild() {
let bundling = false
const hmrBuild = async () => {
bundling = true
await build({'build': { outDir: './hot-dist'}}) // <--- you can give a custom config here or remove it to use default options
};
return {
name: 'hot-build',
enforce: "pre",
// HMR
handleHotUpdate({ file, server }) {
if (!bundling) {
console.log(`hot vite build starting...`)
hmrBuild()
.then(() => {
bundling = false
console.log(`hot vite build finished`)
})
}
return []
}
}
}
then in vite.config.js :
import HotBuild from './hot-build'
// vite config
{
plugins: [
HotBuild()
],
}

Using node's fs results in empty object

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
}

How can I use node "fs" in electron within angular 5

I try to use Electron and Angular5 to write my first desktop App but unfortunately i am stuck in using the fs module. It seems that I have imported fs correctly (no errors within Visual Studio Code and code completion) but when i tried using "fs.readFile" the console prints out this error:
Uncaught TypeError: __WEBPACK_IMPORTED_MODULE_2_fs__.readFile is not a function
This is the code of my service so far:
import { Injectable } from '#angular/core';
import { ElectronService } from 'ngx-electron';
import * as fs from 'fs';
import { OpenDialogOptions } from 'electron';
#Injectable()
export class FileService {
dialog = this._electronService.remote.dialog;
window = this._electronService.remote.getCurrentWindow();
constructor(private _electronService: ElectronService) { }
loadFileContent(): void{
this.dialog.showOpenDialog(this.window, {},(fileNames) => {
if(fileNames === undefined){
console.error("no files selected!");
return;
}
fs.readFile(fileNames[0], "utf-8", (err, data) => {
if(err){
console.error("Cannot read file ",err);
return;
}
console.log("The content of the file is : ");
console.log(data);
});
});
}
}
Do I miss something here? Seems that fs is not loaded or something? Thanks for your help everyone!
You can also use remote.require to load native node modules from ngx-electron.
fs;
constructor(private _electronService: ElectronService) {
this.fs = this._electronService.remote.require('fs');
}
I found the answer with the help of the comments from kimy82!
First i needed to get the Angular5 webpack.config.js by simply using:
ng eject
After that i opened up the webpack.config.js and added the following:
"target": "node-webkit"
Simply "node" did not work out for me and since electron uses a Chromium this should be ok.
Thanks everyone!
Your browser cannot access the file system on the server. fs should not be loaded in the browser

Require node module in angular2 component

I can not figure out how to require node modules in my angular2 components - especially in my case on how to open a new electron window within an angular2 component.
My component.html has something like this
<button class="btn btn-success" (click)="buttonLoginClick()">Login</button>
And within the component.ts I use the following
export class LoginComponent {
constructor() {}
buttonLoginClick(): void {
alert("just a test");
const remote = require('electron').remote;
const BrowserWindow = remote.BrowserWindow;
var win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL('./test.html');
}
}
The error at compiling is saying
Cannot find name 'require'.
I know the question's been asked over two months ago. But, since this is ranking quite high on Google for some keywords. I'll explain how I got it working...
Option 1
In your index.htm file add the following block inside the head element
<script>
var electron = require('electron');
</script>
Then you can declar the electron variable inside any Typescript file, for example, a component...
import { Component } from "#angular/core";
declare var electron: any;
#Component({
...
})
export class FooComponent {
bar() {
var win = new electron.remote.BrowserWindow({ width: 800, height: 600 });
win.loadURL('https://google.com.au');
}
}
when you called that function, it'll open an electron window pointing to google
Option 2
This option should be a bit cleaner. If you have a src\typings.d.ts file. Then you can simply tell the typescript compiler to register the require global function...
declare var require: any;
And then you can use the require function as you'd normally do

Resources