React/Electron not loading static files located in public directory - reactjs

I've built a React app that works fine on Chrome. Now I wanted to convert it into an Electron app. The assets located in the /public directory (pictures, icons, JSON files) cannot be loaded in the electron app.
The browser console displays errors while loading these files:
Failed to load resource: net::ERR_FILE_NOT_FOUND.
When I insert an asset picture from the public directory directly in the generated index.html file like
<img src="pic.png" alt=" pic"/>
it works, but loading it the same way from react components doesn't work. The asset files are being loaded from the root (e.g. file://pic.png)
Here is the project structure:
package.json:
"homepage": "./",
"main": "src/electron-starter.js",
"build": {
"appId": "com.myapp",
"productName": "App",
"files": [
"build/**/*",
"src/electron-starter.js"
],
"directories": {
"buildResources": "public"
},
"win": {
"target": "NSIS"
},
"linux": {
"target": [
"AppImage",
"deb"
],
"category": "Audio"
},
"dmg": {
"contents": [
{
"x": 110,
"y": 150
},
{
"x": 240,
"y": 150,
"type": "link",
"path": "/Applications"
}
]
}
}
createWindow function inside electron-starter.js:
function createWindow () {
const mainWindow = new BrowserWindow({
width: 1200,
height: 700,
webPreferences: {
nodeIntegration: true
}
})
if (process.env.ELECTRON_START_URL) {
mainWindow.loadURL(process.env.ELECTRON_START_URL);
} else {
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, '../build/index.html'),
protocol: 'file',
slashes: true
}))
}
mainWindow.webContents.openDevTools()
}
Thank's in advance for your help!

It turns out that Electron doesn't use React's /public directory. So I moved the required assets into /src/assets and imported them directly inside the component classes. Example:
import logo from "../assets/logo.png";
class MyComponent extends React.Component {
render() {
<img src={logo} alt="my_logo"/>
}
}
I've also added <base href="./"/> to the header of the index.html file.

i have this example, and you charge an path with "../" this example if you can enter with path don't need include ".." i hope you can resolve
import { ipcMain, dialog } from 'electron'
import isImage from 'is-image'
import filesize from 'filesize'
import fs from 'fs'
import path from 'path'
function setMainIpc (win) {
ipcMain.on('open-directory', (event) => {
dialog.showOpenDialog(win, {
title: 'Select new location',
buttonLabel: 'open dir',
properties: ['openDirectory']
},
(dir) => {
if (dir) {
loadImages(event, dir[0])
}
})
})
ipcMain.on('load-directory', (event, dir) => {
loadImages(event, dir)
})
ipcMain.on('open-save-dialog', (event, ext) => {
dialog.showSaveDialog(win, {
title: 'save image modify',
buttonLabel: 'save imagen',
filters: [{name: 'Images', extensions: [ext.substr(1)]}]
}, (file) => {
if (file) {
event.sender.send('save-image', file)
}
})
})
ipcMain.on('show-dialog', (event, info) => {
dialog.showMessageBox(win, {
type: info.type,
title: info.title,
message: info.message
})
})
}
function loadImages (event, dir) {
const images = []
fs.readdir(dir, (err, files) => {
if (err) throw err
for (var i = 0, length1 = files.length; i < length1; i++) {
if (isImage(files[i])) {
let imageFile = path.join(dir, files[i])
let stats = fs.statSync(imageFile)
let size = filesize(stats.size, {round: 0})
images.push({filename: files[i], src: `plp://${imageFile}`, size: size})
}
}
event.sender.send('load-images', dir, images)
})
}
module.exports = setMainIpc

Related

Storybook fails to show stories

the problem we are encountering when trying to add storybook to our project is, that when we run development server, webpack builds every part of the app (resolves ~50,000 dependencies), therefore, when we run it, on network tab we can see unnecessary file downloaded (which contains entire app) that causes entire story (which is downloaded as separate file due to storyStoreV7 flag) to crash, because some lines of unnecessary bundle are raising errors (files aren't even required to display stories). Example error:
TypeError: Cannot read properties of undefined (reading 'CheckboxList')
at Module.CheckboxList (http://localhost:6007/js_components_index_tsx-node_modules_django-channels_dist_sync_recursive-node_modules_moment_-6a9914.iframe.bundle.js:35683:111)
at Module../js/forms/fields/checkbox-list/index.tsx (http://localhost:6007/js_components_index_tsx-node_modules_django-channels_dist_sync_recursive-node_modules_moment_-6a9914.iframe.bundle.js:41141:103)
at __webpack_require__ (http://localhost:6007/runtime~main.iframe.bundle.js:28:33)
at fn (http://localhost:6007/runtime~main.iframe.bundle.js:352:21)
at Module../js/forms/fields/index.js (http://localhost:6007/js_components_index_tsx-node_modules_django-channels_dist_sync_recursive-node_modules_moment_-6a9914.iframe.bundle.js:43187:73)
at __webpack_require__ (http://localhost:6007/runtime~main.iframe.bundle.js:28:33)
at fn (http://localhost:6007/runtime~main.iframe.bundle.js:352:21)
at Module../js/apps/admin/forms/add-reward-rule/index.tsx (http://localhost:6007/js_components_index_tsx-node_modules_django-channels_dist_sync_recursive-node_modules_moment_-6a9914.iframe.bundle.js:1726:71)
at __webpack_require__ (http://localhost:6007/runtime~main.iframe.bundle.js:28:33)
at fn (http://localhost:6007/runtime~main.iframe.bundle.js:352:21)
We found out, that when importing components with React.lazy issue is not present, we can use it this way, but it would be better to use it the "proper" way.
Storybook version: 6.5.0-alpha.42
.storybook/main.js
const webpack = require('webpack');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
const path = require('path');
const globImporter = require('node-sass-glob-importer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
"stories": [
"../js/**/*.stories.*",
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
"#storybook/addon-interactions",
{
"name": '#storybook/preset-scss',
"options": {
"sassLoaderOptions": {
"sourceMap": true,
"sassOptions": {
"includePaths": [path.resolve(__dirname, '../js')],
"importer": globImporter(),
}
},
"cssLoaderOptions": {
"url": false,
}
}
}
],
"features": {
"storyStoreV7": true,
},
"framework": "#storybook/react",
"core": {
"builder": "webpack5"
},
"staticDirs": [path.resolve(__dirname, '../../static')],
"webpackFinal": async (config) => {
config.entry.push(path.resolve(__dirname, '../scss/main.scss'))
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
];
config.resolve.alias = {
...(config.resolve.alias || {}),
'#js': path.resolve(__dirname, '../js'),
}
config.plugins = [
...(config.plugins || []),
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer'],
}),
new MiniCssExtractPlugin({
filename: 'style.css'
}),
]
config.module.rules.push(
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: [{ loader: '#svgr/webpack', options: { ref: true } }],
}
)
return config
}
}
.storybook/preview.tsx
import { ThemeProvider, StyledEngineProvider } from '#mui/material/styles';
import { Parameters } from '#storybook/react'
import { HistoryRouter } from '../js/routes/history-router';
import { browserHistory } from '../js/routes/history';
import '../scss/main.scss';
import theme from '../js/theme'
export const decorators = [
(Story) => {
return <StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<HistoryRouter history={browserHistory}>
{Story()}
</HistoryRouter>
</ThemeProvider>
</StyledEngineProvider>
}
]
export const parameters: Parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
export const argTypes = { children: { type: 'string' }}
example story: Button.stories.tsx:
import React from 'react';
import { ComponentMeta, ComponentStory } from '#storybook/react';
import { Button } from './index';
export default {
component: Button,
} as ComponentMeta<typeof Button>;
const Template: ComponentStory<typeof Button> = (args) => {
return <Button {...args}>{args.children || 'Button'}</Button>;
};
export const Common = Template.bind({});
Common.args = { variant: 'primary' };
In our case, the issue was actually a circular dependency between a few files that used the component we wanted to create stories for.

Monaco editor does not accept colors

I am using react-monaco-editor and for syntax highlighting like vscode, I am using vscode-oniguruma and vscode-textmate. When I run this, https://github.com/bolinfest/monaco-tm, everything goes perfectly(which is implemented using monaco-editor). But if I try this using react-monaco-editor in nextjs, highlighting works sometimes, and doesnt work sometimes.
Can anyone tell me what is the problem??
import React, { useState, useEffect, useRef } from "react";
import dynamic from "next/dynamic";
import { setformatter } from "../actions/getdataaction";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
import {
createOnigScanner,
createOnigString,
loadWASM,
} from "vscode-oniguruma";
import { SimpleLanguageInfoProvider } from "./providers";
import { registerLanguages } from "./register";
import { rehydrateRegexps } from "./configuration";
import VsCodeDarkTheme from "./vs-dark-plus-theme";
import { html, scss, css, javascript, typescript, python } from "./samplecode";
const MonacoEditor = dynamic(import("react-monaco-editor"), { ssr: false });
let changescontent = [];
let cursorposition = 1;
function Editor(props) {
var language = props.extension;
const editorRef = useRef(null);
const monacoRef = useRef(null);
var code =
language === "html"
? html
: language === "css"
? css
: language === "scss"
? scss
: language === "javascript"
? javascript
: language === "typescript"
? typescript
: language === "python"
? python
: "";
const [editorContent, setEditorContent] = useState(code);
useEffect(() => {
main();
}, []);
async function main() {
const data = await loadVSCodeOnigurumWASM();
if (props.openFiles.length === 1) {
loadWASM(data);
}
}
const languages = [
{
id: "python",
extensions: [".py"],
aliases: ["Python", "py"],
filenames: ["Snakefile", "BUILD", "BUCK", "TARGETS"],
firstLine: "^#!\\s*/?.*\\bpython[0-9.-]*\\b",
},
{
id: "javascript",
extensions: [".js", ".jsx"],
aliases: ["Javascript", "JS", "js"],
filenames: ["Snakefile", "BUILD", "BUCK", "TARGETS"],
},
{
id: "css",
extensions: [".css"],
aliases: ["CSS", "css"],
filenames: ["Snakefile", "BUILD", "BUCK", "TARGETS"],
},
{
id: "scss",
extensions: [".scss", ".sass"],
aliases: ["SCSS", "scss", "sass"],
filenames: ["Snakefile", "BUILD", "BUCK", "TARGETS"],
},
{
id: "html",
extensions: [".html"],
aliases: ["HTML", "html"],
filenames: ["Snakefile", "BUILD", "BUCK", "TARGETS"],
},
{
id: "typescript",
extensions: [".ts", ".tsx"],
aliases: ["Typescript", "TS", "ts"],
filenames: ["Snakefile", "BUILD", "BUCK", "TARGETS"],
},
{
id: "json",
extensions: [".json"],
aliases: ["JSON", "json"],
filenames: ["Snakefile", "BUILD", "BUCK", "TARGETS"],
},
];
const grammars = {
"source.py": {
language: "python",
path: "python.tmLanguage.json",
},
"source.js": {
language: "javascript",
path: "javascript.tmLanguage.json",
},
"source.css": {
language: "css",
path: "css.tmLanguage.json",
},
"source.css.scss": {
language: "scss",
path: "scss.tmLanguage.json",
},
"text.html.basic": {
language: "html",
path: "html.tmLanguage.json",
},
"source.ts": {
language: "typescript",
path: "typescript.tmLanguage.json",
},
"source.json": {
language: "json",
path: "json.tmLanguage.json",
},
};
const fetchGrammar = async (scopeName) => {
const { path } = grammars[scopeName];
const uri = `grammars/${path}`;
const response = await fetch(uri);
const grammar = await response.text();
const type = path.endsWith(".json") ? "json" : "plist";
return { type, grammar };
};
const fetchConfiguration = async (language) => {
const uri = `configurations/${language}.json`;
// console.log(uri, "uri fetchConfiguration");
const response = await fetch(uri);
const rawConfiguration = await response.text();
return rehydrateRegexps(rawConfiguration);
};
const onigLib = Promise.resolve({
createOnigScanner,
createOnigString,
});
const provider = new SimpleLanguageInfoProvider({
grammars,
fetchGrammar,
configurations: languages.map((language) => language.id),
fetchConfiguration,
theme: VsCodeDarkTheme,
onigLib,
monaco,
});
registerLanguages(
languages,
(language) => provider.fetchLanguageInfo(language),
monaco
);
async function loadVSCodeOnigurumWASM() {
const response = await fetch("release/onig.wasm");
const contentType = response.headers.get("content-type");
// console.log(response, contentType, "loadVSCodeOnigurumWASM()");
if (contentType === "application/wasm") {
return response;
}
return await response.arrayBuffer();
}
return (
<>
<div style={{ width: "100%", height: "100%" }}>
<MonacoEditor
editorDidMount={(editor, monaco) => {
editorRef.current = editor;
monacoRef.current = monaco;
provider.injectCSS();
}}
width="100%"
height="560"
language={language}
theme="vs-dark"
value={editorContent}
options={{
minimap: {
enabled: false,
},
}}
/>
</div>
</>
);
}
export default Editor;

Craco plugin not loading

I'm trying to add this plugin, which is uses this webpack plugin to my craco config file, followed the guide but it's not working.
const CracoAlias = require('craco-alias');
const imageOptimizer = require('craco-image-optimizer-plugin');
module.exports = function ({ env }) {
return {
reactScriptsVersion: 'react-scripts',
style: {
postcss: {
plugins: [
require('tailwindcss'),
require('postcss-focus-visible'),
require('autoprefixer'),
],
},
},
plugins: [
{
plugin: imageOptimizer,
// image-webpack-plugin options
options: {
mozjpeg: {
progressive: true,
quality: 65,
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.9],
speed: 4,
},
gifsicle: {
interlaced: false,
},
// the webp option will enable WEBP
webp: {
quality: 75,
},
},
},
{
plugin: CracoAlias,
options: {
//CracoAlias options
},
},
],
};
};
The plugin is supposed to optimize the images, but it's not happening. Any ideas? Is it something wrong with my config file? Thanks.
Seems like it was a problem with react-scripts. Had to add the plugin manually like this:
const { getLoaders, loaderByName } = require('#craco/craco');
module.exports = {
overrideWebpackConfig: ({ webpackConfig, cracoConfig, pluginOptions }) => {
const config = { ...webpackConfig };
const urlLoaderCandidates = getLoaders(config, loaderByName('url-loader'));
const urlLoader = urlLoaderCandidates.matches.find(
m =>
m.loader &&
m.loader.test &&
(Array.isArray(m.loader.test)
? m.loader.test.some(r => r.toString().indexOf('jpe?g') >= 0)
: m.loader.test.toString().indexOf('jpe?g') >= 0)
);
if (!urlLoader) {
throw Error(
'could not find correct url-loader. did you change react-scripts version?'
);
}
const loader = urlLoader.loader;
loader.use = [
{
loader: loader.loader,
options: Object.assign({}, loader.options),
},
{
/**
* #see https://github.com/tcoopman/image-webpack-loader
*/
loader: 'image-webpack-loader',
options: pluginOptions,
},
];
delete loader.loader;
delete loader.options;
return config;
},
};
And then import the file instead of the package.

path.resolve() was not pointing the templates/blogpost.js in gatsby application

I'm developing very basic blog application that display blog posts from markdown files. When i click each post, it will open on the new route with dynamically create page with createPages functionality. The problem is When i pointing the templates/blogpost.js in the gatsby-node.js.
It is showing like this.
Your site's "gatsby-node.js" created a page with a component that doesn't exist.
Error :
The path to the missing component is "C:\Users\viper\Desktop\react\gatsby\portfolio\imlohith\src\templates\blog.js"
The page object passed to createPage:
{
"path": "/post-four",
"component": "C:\\Users\\viper\\Desktop\\react\\gatsby\\portfolio\\imlohith\\src\\templates\\blog.js"
}
const postTemplate = path.resolve(`src/templates/blog.js`)
return graphql(`
{
allMarkdownRemark {
edges {
node {
html
id
frontmatter {
path
title
date
author
}
}
}
}
}
`).then(res => {
if (res.errors) {
return Promise.reject(res.errors)
}
res.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.frontmatter.path,
component: postTemplate,
})
})
})
}
Gatsby-congig.js file
const config = require('./config');
module.exports = {
pathPrefix: config.pathPrefix,
siteMetadata: {
title: config.siteTitle,
description: "This is awesome site awesome awesome"
},
plugins: [
'gatsby-plugin-react-helmet',
'gatsby-plugin-catch-links',
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/pages`,
name: 'pages',
},
},
'gatsby-transformer-remark',
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: config.manifestName,
short_name: config.manifestShortName,
start_url: config.pathPrefix || config.manifestStartUrl,
background_color: config.manifestBackgroundColor,
theme_color: config.manifestThemeColor,
display: config.manifestDisplay,
icon: config.manifestIcon, // This path is relative to the root of the site.
},
},
'gatsby-plugin-sass',
'gatsby-plugin-offline',
],
};

Get all images from specific directory via GraphQL and Gatsby JS

I have three folders with images that will be loaded in multiple carousel components. Here is the folder structure, I would get all the images that in the collection folders.
src/
images/
collection1/
collection2/
collection3/
randomImage.jpg
randomImage.svg
components/
pages/
I currently have this query:
export const pageQuery = graphql`
query IndexQuery {
site {
siteMetadata {
title
}
}
heroFeaturedImage: imageSharp(id: { regex: "/war-room.jpg/" }) {
sizes(maxWidth: 1250, quality: 90) {
...GatsbyImageSharpSizes
}
}
allImageSharp {
edges {
node {
... on ImageSharp {
sizes(maxWidth: 1250, quality: 90) {
src
srcSet
originalName
}
}
}
}
}
}
`;
Here is my gatsby-config.js:
module.exports = {
siteMetadata: {
title: 'Get all images in directory test',
},
plugins: [
'gatsby-plugin-react-helmet',
'gatsby-plugin-styled-components',
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/pages`,
name: 'pages',
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/images`,
name: 'images',
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `collection1`,
path: `${__dirname}/src/images/collection1`,
},
},
{
resolve: `gatsby-plugin-google-fonts`,
options: {
fonts: [`Roboto\:300,400,500,600`, `Montserrat\:300,400,600,800`],
},
},
],
};
The issue with it is that returns ALL of the images in the project without a great way of organizing the specific images I want.
So how can I only get all of the images out of a specific directory (e.g. collection1)?
Couple things jump out at me, although I'm not sure if this solves it:
1) Kinda weird in gatsby-config.js you're calling gatsby-source-filesystem twice on what amounts to the same set of images. I think you could lose that call to the collection1 set, it's redundant as best I understand.
2) The allImageSharp query looks to be doing what it should, returning everything. Have you tried a query like:
export const pageQuery = graphql`
query IndexQuery {
site {
siteMetadata {
title
}
}
collectionOneImages: imageSharp(id: { regex: "^\/collection1\/?(?:[^\/]+\/?)*$" }) {
sizes(maxWidth: 1250, quality: 90) {
...GatsbyImageSharpSizes
}
}
collectionTwoImages: imageSharp(id: { regex: "^\/collection2\/?(?:[^\/]+\/?)*$" }) {
sizes(maxWidth: 1250, quality: 90) {
...GatsbyImageSharpSizes
}
}
collectionThreeImages: imageSharp(id: { regex: "^\/collection3\/?(?:[^\/]+\/?)*$" }) {
sizes(maxWidth: 1250, quality: 90) {
...GatsbyImageSharpSizes
}
}
}
`;
I'm not the best with regex, but those regex queries essentially mean, "give me any file after the collection#".

Resources