Vue CLI with electron - Unexpected character (1:0) when using native modules - vuejs3

In some popular NodeJS libraries, e.g. ssh2 or node-pty, there is natively compiled code as part of the library.
Creating the project with
vue create my-project
vue add electron-builder
yarn add ssh2
then importing and using ssh2's Client in the background process results in following errors during
electron:build
ERROR Failed to compile with 1 errors 5:29:10 PM
error in ./node_modules/cpu-features/build/Release/cpufeatures.node
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
# ./node_modules/cpu-features/lib/index.js 1:16-60
# ./node_modules/ssh2/lib/protocol/constants.js
# ./node_modules/ssh2/lib/client.js
# ./node_modules/ssh2/lib/index.js
...
This error occurs with many other libs or transitive dependencies and the reason for it is absence of native-ext-loader on Webpack chain. I understand why it is not included by default, and I would like to see what is the best way to add it.

One solution I found is this:
Add yarn add -D native-ext-loader (my version is 2.3.0 and electron is at 13.x)
Adjust vue.config.js and add the chainWebpackMainProcess like this:
const path = require('path')
module.exports = {
pluginOptions: {
electronBuilder: {
builderOptions: {
// options placed here will be merged with default
mac: {
target: 'dmg',
icon: 'build/icon.icns',
asar: true
}
},
preload: 'src/preload.ts',
chainWebpackMainProcess(config) {
config.module
.rule("node")
.test(/\.node$/)
.use("native-ext-loader")
.loader("native-ext-loader")
.options(
process.env.NODE_ENV === "development"
? {
rewritePath: path.resolve(__dirname, "native"),
}
: {}
)
}
}
}
}
Both, electron:build and electron:serve are now working and ssh2 client is happily delivering the stdout to renderer via ipcMain. Not sure it is the most elegant way of solving it, though.

Related

Nextjs with Jest---No tests found, exiting with code 1 Run with `--passWithNoTests` to exit with code 0

import nextJest from 'next/jest'
const createJestConfig = nextJest({
dir: './',
})
// Add any custom config to be passed to Jest
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jest-environment-jsdom',
modulePathIgnorePatterns: ['./cypress'],
// testMatch: ['<rootDir>/**/*.test.js', '<rootDir>/**/*.test.jsx'],
}
module.exports = createJestConfig(customJestConfig)
In my project, we use Nextjs application with both Cypress and Jest. The latest jest.config.ts which is recommended is shown above.
If you are now owned this problem. you can maybe try to check your modulePathIgnorePatterns.
I added a ./ to ['cypress'], then it works well. So, I think maybe it just cann't recognize the path.

SQL with Prisma under Electron

My Main goal is to create an Electron App (Windows) that locally stores data in an SQLite Database. And because of type safety I choose to use the Prisma framework instead of other SQLite Frameworks.
I took this Electron Sample Project and now try to include Prisma. Depending on what I try different problems do arrise.
1. PrismaClient is unable to be run in the Browser
I executed npx prisma generate and then try to execute this function via a button:
import { PrismaClient } from '#prisma/client';
onSqlTestAction(): void {
const prisma = new PrismaClient();
const newTestObject = prisma.testTable.create(
{
data: {
value: "TestValue"
}
}
);
}
When executing this in Electron I get this:
core.js:6456 ERROR Error: PrismaClient is unable to be run in the browser.
In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues
at new PrismaClient (index-browser.js:93)
at HomeComponent.onSqlTestAction (home.component.ts:19)
at HomeComponent_Template_button_click_7_listener (template.html:7)
at executeListenerWithErrorHandling (core.js:15281)
at wrapListenerIn_markDirtyAndPreventDefault (core.js:15319)
at HTMLButtonElement.<anonymous> (platform-browser.js:568)
at ZoneDelegate.invokeTask (zone.js:406)
at Object.onInvokeTask (core.js:28666)
at ZoneDelegate.invokeTask (zone.js:405)
at Zone.runTask (zone.js:178)
It somehow seems logical that Prisma cannot run in a browser. But I actually build a native app - with Electron that embeds a Browser. It seems to be a loophole.
2. BREAKING CHANGE: webpack < 5 used to include polyfills
So i found this Question: How to use Prisma with Electron
Seemed to be exactly what I looked for. But the error message is different (Debian binaries were not found).
The solution provided is to generate the prisma artifacts into the src folder instead of node_modules - and this leads to 19 polyfills errors. One for example:
./src/database/generated/index.js:20:11-26 - Error: Module not found: Error: Can't resolve 'path' in '[PATH_TO_MY_PROJECT]\src\database\generated'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
And this repeats with 18 other modules. Since the error message to begin with was different I also doubt that this is the way to go.
I finally figured this out. What I needed to understand was, that all Electron apps consist of 2 parts: The Frontend Webapp (running in embedded Chromium) and a Node backend server. Those 2 parts are called IPC Main and IPC Renderer and they can communicate with each other. And since Prisma can only run on the main process which is the backend I had to send my SQL actions to the Electron backend and execute them there.
My minimal example
In the frontend (I use Angular)
// This refers to the node_modules folder of the Electron Backend, the folder where the main.ts file is located.
// I just use this import so that I can use the prisma generated classes for type safety.
import { TestTable } from '../../../app/node_modules/.prisma/client';
// Button action
onSqlTestAction(): void {
this.electronService.ipcRenderer.invoke("prisma-channel", 'Test input').then((value) => {
const testObject: TestTable = JSON.parse(value);
console.log(testObject);
});
The sample project I used already had this service to provide the IPC Renderer:
#Injectable({
providedIn: 'root'
})
export class ElectronService {
ipcRenderer: typeof ipcRenderer;
webFrame: typeof webFrame;
remote: typeof remote;
childProcess: typeof childProcess;
fs: typeof fs;
get isElectron(): boolean {
return !!(window && window.process && window.process.type);
}
constructor() {
// Conditional imports
if (this.isElectron) {
this.ipcRenderer = window.require('electron').ipcRenderer;
this.webFrame = window.require('electron').webFrame;
this.childProcess = window.require('child_process');
this.fs = window.require('fs');
// If you want to use a NodeJS 3rd party deps in Renderer process (like #electron/remote),
// it must be declared in dependencies of both package.json (in root and app folders)
// If you want to use remote object in renderer process, please set enableRemoteModule to true in main.ts
this.remote = window.require('#electron/remote');
}
}
And then in the Electron backend I first added "#prisma/client": "^3.0.1" to the package.json (for the Electron backend not the frontend). Then I added to the main.ts this function to handle the requests from the renderer:
// main.ts
ipcMain.handle("prisma-channel", async (event, args) => {
const prisma = new PrismaClient();
await prisma.testTable.create(
{
data: {
value: args
}
}
);
const readValue = await prisma.testTable.findMany();
return JSON.stringify(readValue);
})
This way of simply adding the IPC Main handler in the main.ts file of course is a big code smell but usefull as minimal example. I think I will move on with the achitecture concept presented in this article.

Angular 2 submodule routing with AOT and Rollup

What is the correct way to configure a submodule in Angular 2 so that it will work after AOT and rollup? I'm not concerned about lazy loading and will be happy for all submodules to be bundled together, but loadChildren is the cleanest way of referencing a submodule and having it use the correct <router-outlet>, and although I've tried various methods, none will work in both development and production.
I followed the instructions in the AOT cookbook to prepare my app for deployment. My module structure is root > account > admin, and I want the admin routes to load in the outlet defined by the account component.
Here's the router config for the account module:
import { NgModule } from '#angular/core';
import { AdminModule } from './admin/admin.module';
const accountRoutes: Routes = [{
path: 'account',
component: AccountComponent,
children: [
{ path: 'setup', component: SetupComponent },
{ path: 'admin', loadChildren: () => AdminModule }
]
}];
This works in development, but NGC compilation fails with Error: Error encountered resolving symbol values statically. Reference to a local (non-exported) symbol 'accountRoutes'.
I added an export to accountRoutes but this also fails to compile with Error: Error encountered resolving symbol values statically. Function calls are not supported..
One suggestion was to use a string instead of a function so that NGC can compile the code:
loadChildren: 'app/account/admin/admin.module#AdminModule'
This works in development and compiles successfully but the compiled app will not run, because SystemJS is unavailable. The error is:
ERROR Error: Uncaught (in promise): TypeError: System.import is not a function
TypeError: System.import is not a function
at t.loadFactory (build.js:6)
at t.load (build.js:6)
at t.loadModuleFactory (build.js:12)
I tried including SystemJS in the production build but it could not find the separate module file app/account/admin/admin.module.ngfactory - after rollup everything is in one build.js. There may be a way to have separate rollups build each submodule, but that's a lot of work.
I found the suggestion of referring to an exported function:
export function loadAdminModule() {
return AdminModule;
}
loadChildren: loadAdminModule
This works in development, but in production the runtime compiler is unavailable so it logs build.js:1 ERROR Error: Uncaught (in promise): Error: Runtime compiler is not loaded.
The helper function can be modified so it works in production by referencing the NgFactory instead, but this won't work in development.
import { AdminModuleNgFactory } from '../../../aot/src/app/account/admin/admin.module.ngfactory';
export function loadAdminModule() {
return AdminModuleNgFactory;
}
loadChildren: loadAdminModule
Is there a supported way of using loadChildren so that it will work with the instructions in the AOT cookbook? Is it better to use webpack instead?
For anyone having the same problem, here's the temporary workaround I'm using. Hopefully a better answer will come along soon.
I now define my submodules in a dedicated file, submodules.ts. I have two versions of this file, submodules-jit.ts for development, and submodules-aot.ts for AOT/rollup deployment to production. I gitignore submodules.ts and have added commands to my npm start and npm build:aot scripts that substitute in the right one.
// submodules-jit.ts
import { AdminModule } from './account/admin/admin.module'
export function adminModule(): any { return AdminModule; }
// submodules-aot.ts
import { AdminModuleNgFactory } from '../../aot/src/app/account/admin/admin.module.ngfactory'
import { AdminModule } from './account/admin/admin.module'
export function adminModule(): any { return AdminModuleNgFactory; }
export function adminModuleKeep(): any { return AdminModule; }
AdminModule must be referenced in the AOT file so it is preserved.
Then I use the adminModule function in my routes:
import { adminModule } from '../submodules'
{ path: 'admin', loadChildren: adminModule }
Finally for convenience, npm scripts drop in the correct file.
"build:aot": "cp src/submodules-aot.ts src/app/submodules.ts && ngc -p tsconfig-aot.json && rollup -c rollup-config.js",
"start": "cp src/submodules-jit.ts src/app/submodules.ts && concurrently \"npm run build:watch\" \"npm run serve\""
Needless to say this is a horrible hack, but it keeps the routing files clean and most of the evil in one place.
In my case I solved with your hint on loading routes from an external module, then reading this issue on angular/cli issue page.
So, I came out refactoring my routes from:
import { TestModule } from './pathToModule/test.module';
export function exportTestModule() {
return TestModule;
}
. . .
{
path: 'test',
loadChildren: exportTestModule
}
To:
. . .
{
path: 'test',
loadChildren: './pathToModule#TestModule'
}

Grunt relative file path globbing

Is it possible to use Globbing partially on a directory in a file path?
I have a grunt-contrib-less task set up, the file path for my task looks something like this:
files: {
"../../application/user/themes/some-theme-5.1.1.5830/css/main.css": "less/base.less",
}
However the version number in the relative path may sometime change, such as:
files: {
"../../application/user/themes/some-theme-5.1.1.5831/css/main.css": "less/base.less",
}
Ideally I'd like to something like this:
files: {
"../../application/user/themes/some-theme-*/css/main.css": "less/base.less",
}
Is there a way of doing this? With the above syntax it stops searching after the asterisk.
One potential solution to achieve this is to utilize grunts --options feature.
When running a grunt task via the command line it is possible to specify an additional options value.
In your scenario you could pass in the version number of the folder name that is going to change. (I.e. In your case the part that you tried to specify using the asterisk character (*) E.g. '5.1.1.5830'
Caveat: For this solution to be of any use it does require knowing what that value, (the version number), of the destination folder is upfront prior to running the task via the command line.
Example Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
themesFolder: {
namePart: '0.0.0.0' // <-- If no option is passed via the CLI this name will be used.
},
less: {
production: {
options: {
// ...
},
files: {
// The destination path below utilizes a grunt template for the part
// of the folder name that will change. E.g. '5.1.1.0'
'../../application/user/themes/some-theme-<%= themesFolder.name %>/css/main.css': 'less/base.less'
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.registerTask('saveFolderNameFromOption', 'Uses the option provided to configure name part.', function(n) {
var themesFolder = grunt.option('themesFolder');
if (themesFolder) {
// Store the option value so it can be referenced in the less task.
grunt.config('themesFolder.namePart', themesFolder);
}
});
grunt.registerTask('processLess', ['saveFolderNameFromOption', 'less:production']);
};
Running the ProcessLess task
Run the task via the command line as follows:
$ grunt processLess --themesFolder=5.1.1.5830
Note: The additional option that is specified. Namely: --themesFolder=5.1.1.5830
When using the above command the .css output will be directed to the following path:
'../../application/user/themes/some-theme-5.1.1.5830/css/main.css': 'less/base.less'
Now, each time you run the task you modify the options accordingly.
Benefits: By providing the version number as an option via the CLI will avoid having to reconfigure your Gruntfile.js each time it is run.

Grunt-complexity on all the files in a directory

I'd like to run Grunt-Complexity on all the files in a directory?
I'd like to get this kind of output.
Is there a way?
My js files are all under a subdirectory called "js".
Here's my gruntfile:
/*global module:false*/
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// Task configuration.
complexity: {
generic: {
src: ['grunt.js', 'js/*'],
//exclude: ['doNotTest.js'],
options: {
breakOnErrors: false,
jsLintXML: 'report.xml', // create XML JSLint-like report
checkstyleXML: 'checkstyle.xml', // create checkstyle report
pmdXML: 'pmd.xml', // create pmd report
errorsOnly: false, // show only maintainability errors
cyclomatic: [3, 7, 12], // or optionally a single value, like 3
halstead: [8, 13, 20], // or optionally a single value, like 8
maintainability: 100,
hideComplexFunctions: false, // only display maintainability
broadcast: false // broadcast data over event-bus
}
}
}
});
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-complexity');
// Default task.
grunt.registerTask('default', 'complexity');
};
I'm simply calling this by typing
grunt
from the command line.
then if I type this
grunt complexity js/*
I get
Warning: Task "js/AgencyMediaController.js" not found. Use --force to continue.
Aborted due to warnings.
And AgencyMediaController.js is the first file in my js directory. So it's having a look and listing the files, but then it crashes.
Thanx!
example:
for all js file in JS folder:
src: ['js/**/*.js']
for ass .scss files in scss folder:
src: ['scss/**/*.scss']
I suggest for you create a config for your src folder can be easy in future folder changes in future projects:
sample:
var src;
config.src = src = {
sassMain : 'scss/main.scss',
distFolder : 'public/stylesheets/lovelycss.dist.css',
devFolder : 'public/stylesheets/lovelycss.dev.css',
libFolder : 'lib/**/*.js',
sassFolder : 'scss/**/*.scss',
spriteCssFolder : 'scss/helpers/_sprite.scss',
spriteDestImg : 'public/images/sprite/spritesheet.png',
spriteSrc : 'public/images/min/*.{png,jpg,gif}',
imageminCwd : 'public/images/',
imageminDest : 'public/images/min'
};
//grunt Watch ===============================
config.watch = {
scripts: {
files: ["<%= src.libFolder %>", "<%= src.sassFolder %>"]
,tasks: ["dev", "sass:dist"]
//,tasks: ["dev",'sass:dist']
}
}
I hope that helped you.
It's been quite a long while since I asked this question. I just ran into the same issue again and found the answer so here it is:
In the end it turned out to be that one of the files I was trying to analyse was causing the crash. This particular Javascript environment allows for C-like preprocessor directives and the Javascript file had something like this:
var mySettings = {
//#ifdef FOO_CONSTANT
setting : constants.FOO_SETTING
//#endif
//#ifdef BAR_CONSTANT
setting : constants.BAR_SETTING
//#endif
};
I guess the problem is that if this is read as strictly Javascript, the preprocessor directives are just plane comments, and there's a comma missing between the two properties, so Grunt complexity is unable to read this because of a syntax error. Using --force makes no difference BTW.
The annoying part is that this is all the error shows:
$ grunt --force
Running "complexity:generic" (complexity) task
Warning: undefined: Unexpected token, expected , (17570:1) Used --force, continuing.
Done, but with warnings.
So while it does say expected , (175:1) it doesn't say in which of the several Javascript files in this project the problem was found!
Just adding exclude: ['path/to/MyFileWithPreprocessorDirectives.js'] to Gruntfile.js in order to exclude this file from the analysis gets me around the problem.

Resources