How dynamically imports images from my assets folder? - reactjs

I've follow this tutorial to get the boiler plate code for my project.
The problem: my program is some kind of gallery of images that reads a folder to get the assets, so i don't have acces to the name of the files and i can't make a static import and the dynamic import did'nt find the assets.
My code:
import React from "react";
interface Props {
mock?: false;
src: string;
title: string;
compiler: string;
}
interface MockProps {
mock: true;
[x: string]: any;
}
const mockProps: Props = {
mock: false,
src: "../../public/assets/images/asd.png",
title: "./",
compiler: "",
};
export default function AlbumCard(props: Props | MockProps): JSX.Element {
const { mock } = props;
const realProps = mock ? mockProps : props;
return (
<div>
<div>
<img src={realProps.src} />
</div>
</div>
);
}
My webpack file:
module.exports = {
module: {
rules:[ {
test: /\.node$/,
use: "node-loader",
},
{
test: /\.(m?js|node)$/,
parser: { amd: false },
use: {
loader: "#marshallofsound/webpack-asset-relocator-loader",
options: {
outputAssetBase: "native_modules",
},
},
},
{
test: /\.tsx?$/,
exclude: /(node_modules|\.webpack)/,
use: {
loader: "ts-loader",
options: {
transpileOnly: true
}
}
},
{
test: /\.(png|jpe?g|gif|jp2|webp)$/,
loader: "file-loader",
options: {
name: "[name].[ext]",
},
},
{
test: /\.css$/,
use: [{ loader: "style-loader" }, { loader: "css-loader" }],
}]
},
plugins: plugins,
resolve: {
extensions: [".js", ".ts", ".jsx", ".tsx", ".css", ".png"]
},
};
What i get:
Failed to load resource: the server responded with a status of 404 (Not Found)
What am I missing here?

Related

Issues with style-loader lazyloading for multiple elements in shadow-dom

I'm currently working with webpack and style-loader to inject my styles into components that use shadow-dom. However, the issue happens when I try to use multiple instances of the element (the styles stop injecting). I was able to properly solve this issue with another component by adding the unuse function to my disconnectCallback for that component. Unfortunately, for this component below, I expect it to appear multiple times at once on a page. Am I doing something wrong?
Component.tsx:
import React from 'react';
import { createRoot } from 'react-dom/client';
// Styles
import styles from '../styles/contentScript.css';
// Images
const icon = chrome.runtime.getURL('assets/icon.png');
export default class CustomButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// Functions
this.handleShow = this.handleShow.bind(this);
}
handleShow = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
...
};
connectedCallback() {
// Inject Styles
styles.use({ target: this.shadowRoot });
// Inject Component into ShadowRoot
createRoot(this.shadowRoot).render(this.render());
}
render() {
return (
<div className='absolute inset-0'>
<button className='main-button group' onClick={this.handleShow}>
<img src={icon} className='w-7' />
<span className='main-button-tooltip group-hover:scale-100'>
Open Popup
</span>
</button>
</div>
);
}
disconnectedCallback() {
styles.unuse();
}
}
customElements.define('custom-button', CustomButton);
webpack.config.js
// Imports...
module.exports = {
entry: {
...
},
module: {
rules: [
{
use: 'ts-loader',
test: /\.tsx?$/,
exclude: /node_modules/,
},
{
use: [
{
loader: 'style-loader',
options: {
injectType: 'lazyStyleTag',
insert: function insertIntoTarget(element, options) {
var parent = options.target || document.head;
parent.appendChild(element);
},
},
},
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
indent: 'postcss',
plugins: [tailwindcss, autoprefixer],
},
},
},
],
test: /\.css$/i,
},
{
type: 'asset/resource',
test: /\.(png|jpg|jpeg|gif)$/i,
},
]
},
resolve: {
...
},
plugins: [
...
],
output: {
filename: '[name].js',
clean: true,
},
optimization: {
...
},
}
I should also note (in case it's important) I am using tailwind for styling so I've included postcss and autoprefixer. This is also for a chrome extension so I'm creating this component in my contentScript. I have also tried it without the unuse call in my disconnectCallback and faced the same issue.
to follow the discussion on github, if you're only browser target is Chrome, then I really suggest you to use the CSSStyleSheet class.
In that case, you should drop from your webpack configuration the style-loader, as it's not needed anymore:
{
loader: 'style-loader',
options: {
injectType: 'lazyStyleTag',
insert: function insertIntoTarget(element, options) {
var parent = options.target || document.head;
parent.appendChild(element);
},
},
},
Then modify the configuration of the css-loader to have the option exportType = "css-style-sheet" (https://webpack.js.org/loaders/css-loader/#exporttype).
In this way, the exported element is already an object of type CSSStyleSheet, and you can use it directly on your web component:
import sheet from "./styles.css" assert { type: "css" };
document.adoptedStyleSheets = [sheet];
shadowRoot.adoptedStyleSheets = [sheet];
Normally, the 'postcss' step should not pose problems.
No need anymore to use "use / unuse" then (because that's an API of style-loder that you should remove with this solution)

React does not load local svg

I need to load a local SVG calling as a string './MySvg.svg', but it just works if i call it as a component, like <MySvg />.
I followed this tutorial https://blog.logrocket.com/how-to-use-svgs-in-react/, installed file-loader but the problem persists.
I'm trying to put a custom svg on the nodes of a graph with react-d3-graph, but it needs the svg locally as string and will not accept as a component.
Svgteste just exports the svg tag of svgFile.svg
An example with a component
import * as React from 'react';
import { PageSection, Title } from '#patternfly/react-core';
import { Svgteste } from './Svgteste'
import { Test } from './svgFile.svg'
const Dashboard: React.FunctionComponent = () => {
return(
<PageSection>
<Svgteste /> {/*this works*/}
<img src={'./svgFile.svg'} /> {/*this won't work*/}
<img src={Test} /> {/*neither this*/}
</PageSection>
)
}
export { Dashboard };
My webpack
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const BG_IMAGES_DIRNAME = 'bgimages';
module.exports = env => {
return {
entry: {
app: path.resolve(__dirname, 'src', 'index.tsx')
},
module: {
rules: [
{
test: /\.(tsx|ts|jsx)?$/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true,
experimentalWatchApi: true,
}
}
]
},
{
test: /\.(svg|ttf|eot|woff|woff2)$/,
// only process modules with this loader
// if they live under a 'fonts' or 'pficon' directory
use: {
loader: 'file-loader',
options: {
// Limit at 50k. larger files emited into separate files
limit: 5000,
outputPath: 'fonts',
name: '[name].[ext]',
}
}
},
{
test: /\.svg$/,
include: input => input.indexOf('background-filter.svg') > 1,
use: [
{
loader: 'url-loader',
options: {
limit: 5000,
outputPath: 'svgs',
name: '[name].[ext]',
}
}
]
},
{
test: /\.svg$/,
// only process SVG modules with this loader if they live under a 'bgimages' directory
// this is primarily useful when applying a CSS background using an SVG
include: input => input.indexOf(BG_IMAGES_DIRNAME) > -1,
use: {
loader: 'svg-url-loader',
options: {}
}
},
{
test: /\.svg$/,
// only process SVG modules with this loader when they don't live under a 'bgimages',
// 'fonts', or 'pficon' directory, those are handled with other loaders
include: input => (
(input.indexOf(BG_IMAGES_DIRNAME) === -1) &&
(input.indexOf('fonts') === -1) &&
(input.indexOf('background-filter') === -1) &&
(input.indexOf('pficon') === -1)
),
use: {
loader: 'raw-loader',
options: {}
}
},
{
test: /\.(jpg|jpeg|png|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 5000,
outputPath: 'images',
name: '[name].[ext]',
}
}
]
},
{
test: /\.svg$/,
use: ['#svgr/webpack'],
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
//name: 'images/[hash]-[name].[ext]',
},
},
],
},
]
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src', 'index.html')
}),
new Dotenv({
systemvars: true,
silent: true
})
],
resolve: {
extensions: ['.js', '.ts', '.tsx', '.jsx'],
plugins: [
new TsconfigPathsPlugin({
configFile: path.resolve(__dirname, './tsconfig.json')
})
],
symlinks: false,
cacheWithContext: false
}
}
};
I think, you have to remove the curly braces in the import. The curly braces indicate that you want to import a component that is exported with the given name, which is not the case when you import an svg.
import Test from './svgFile.svg'

What's best way to config React components library with Webpack, typescript, css modules and SASS

I'm config a React components library for reuses in other our projects. I want to use these:
Typescript for components
Css modules
Sass
Webpack for bundle
So this is my webpack config:
const childProcess = require('child_process');
const path = require('path');
const url = require('url');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const loadersConf = require('./webpack.loaders');
const NPM_TARGET = process.env.npm_lifecycle_event; //eslint-disable-line no-process-env
const targetIsRun = NPM_TARGET === 'run';
const targetIsTest = NPM_TARGET === 'test';
const targetIsStats = NPM_TARGET === 'stats';
const targetIsDevServer = NPM_TARGET === 'dev-server';
const DEV = targetIsRun || targetIsStats || targetIsDevServer;
const STANDARD_EXCLUDE = [
path.join(__dirname, 'node_modules'),
];
// These files will be imported in every sass file
const sassResourcesPaths = [
path.resolve(__dirname, 'src/styles/abstracts/_variables.sass'),
path.resolve(__dirname, 'src/styles/abstracts/_mixins.sass'),
];
const config = {
module: {
rules: loadersConf,
},
resolve: {
modules: [
'node_modules',
path.resolve(__dirname),
],
extensions: ['.js', '.jsx', '.ts', '.tsx', '.css', '.scss'],
},
performance: {
hints: 'warning',
},
target: 'web',
plugins: [
new webpack.DefinePlugin({
COMMIT_HASH: JSON.stringify(childProcess.execSync('git rev-parse HEAD || echo dev').toString()),
}),
new MiniCssExtractPlugin({
filename: '[name].[contentHash].css',
chunkFilename: '[name].[contentHash].css',
}),
],
};
if (DEV) {
config.mode = 'development';
config.devtool = 'source-map';
}
const env = {};
if (DEV) {
} else {
env.NODE_ENV = JSON.stringify('production');
}
config.plugins.push(new webpack.DefinePlugin({
'process.env': env,
}));
module.exports = config;
And this is loaders for webpack:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const STANDARD_EXCLUDE = [
path.join(__dirname, 'node_modules'),
];
const NPM_TARGET = process.env.npm_lifecycle_event; //eslint-disable-line no-process-env
const targetIsRun = NPM_TARGET === 'run';
const targetIsTest = NPM_TARGET === 'test';
const targetIsStats = NPM_TARGET === 'stats';
const targetIsDevServer = NPM_TARGET === 'dev-server';
const DEV = targetIsRun || targetIsStats || targetIsDevServer;
// noinspection WebpackConfigHighlighting
module.exports = [
{
test: /\.(js|jsx|ts|tsx)?$/,
exclude: STANDARD_EXCLUDE,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// Babel configuration is in .babelrc because jest requires it to be there.
},
},
},
{
type: 'javascript/auto',
test: /\.json$/,
include: [
path.resolve(__dirname, 'i18n'),
],
exclude: [/en\.json$/],
use: [
{
loader: 'file-loader?name=i18n/[name].[hash].[ext]',
},
],
},
// ==========
// = Styles =
// ==========
// Global CSS (from node_modules)
// ==============================
{
test: /\.css/,
include: path.resolve(__dirname, "node_modules"),
use: [
MiniCssExtractPlugin.loader,
{
loader: "style-loader"
},
{
loader: 'css-loader'
}
]
},
{
test: /\.(sc|sa|c)ss$/,
exclude: /\.st.css$/, //This must appear before the "oneOf" property
use: [
MiniCssExtractPlugin.loader,
'style-loader',
'css-modules-typescript-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true,
camelCase: "dashes",
localIdentName: DEV
? '[name]__[local]___[hash:base64:5]'
: '_[hash:base64:5]',
},
},
{
loader: "postcss-loader",
options: {
sourceMap: "inline",
extract: true,
}
},
"sass-loader",
],
},
{
test: /\.(png|eot|tiff|svg|woff2|woff|ttf|gif|mp3|jpg)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'files/[hash].[ext]',
},
},
{
loader: 'image-webpack-loader',
options: {},
},
],
},
];
Babel config:
const config = {
presets: [
['#babel/preset-env', {
targets: {
chrome: 66,
firefox: 60,
edge: 42,
safari: 12,
},
modules: false,
corejs: 3,
useBuiltIns: 'usage',
shippedProposals: true,
}],
['#babel/preset-react', {
useBuiltIns: true,
}],
['#babel/typescript', {
allExtensions: true,
isTSX: true,
}],
],
plugins: [
'#babel/plugin-transform-runtime',
'#babel/plugin-syntax-dynamic-import',
'#babel/proposal-class-properties',
'#babel/proposal-object-rest-spread',
'#babel/plugin-proposal-optional-chaining',
['module-resolver', {
root: ['./src', './test'],
}],
],
};
// Jest needs module transformation
config.env = {
test: {
presets: config.presets,
plugins: config.plugins,
},
};
config.env.test.presets[0][1].modules = 'auto';
module.exports = config;
This is a demo component of this library:
import React from 'react';
const styles = require('./style.scss');
type Props = {
children?: React.ReactNode;
openLeft?: boolean;
openUp?: boolean;
id?: string;
ariaLabel: string;
customStyles?: object;
}
export default class Button extends React.PureComponent<Props> {
public render() {
return (
<button className={styles.test}>
{this.props.children}
</button>
);
}
}
So, this is develop build command:
"build": "cross-env NODE_ENV=production webpack --display-error-details --verbose",
"run": "webpack --progress --watch",
when I using this library:
import ExampleComponent from 'mylibrary';
When I run BUILD or RUN command, the javascript is built, but not SCSS. So in my other project, the build throw an error:
can not find module './style.scss'
This error occur in ExampleComponent
Please tell me how to resolve it. Thanks you very much!
This is my webpack config for the styles part:
{
test: /\.s?css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader'
},
{
loader: 'postcss-loader',
options: {
config: {
path: 'postcss.config.js'
}
}
},
{
loader: 'sass-loader'
},
]
}
and in my postcss.config.js:
module.exports = {
plugins: [
require('autoprefixer')
]
}

React-Monaco-Editor: Not finding workers produced by monaco-editor-webpack-plugin

I'm trying to integrate the monaco editor into an existing react application. I followed the installation steps from (react-monaco-editor), but I don't get any syntax highlighting or autocomplete for typescript, consistent with the FAQ as if I hadn't set up the monaco-editor-webpack-plugin.
I have setup the plugin though. I can see that the monaco-editor-webpack-plugin is dropping files along side my app.js, so I presume it is working:
It seems my issue is that react-monaco-editor is trying to pull these files from the wrong location. If I load my page and open my network tab, I can see the following:
There are two problems:
1 - the paths are full paths on my system but should be relative paths - e.g.
http://127.0.0.1/dist/app/secured/49.js
2 - there is a slash missing between "secured" and "49.js" (secured49.js) and also for the other numeric files, but the editor.worker.js has the slash (weird).
So the question I'm hoping to answer is - how does react-monaco-editor determine the paths it will pull these files from, and is there a way I can influence the pathing?
Any help or recommendations would be much appreciated! Some snippets of my code below in case helpful.
package.json:
"monaco-editor-webpack-plugin": "^1.7.0",
"react-monaco-editor": "^0.26.2",
weback.config.js
'use strict';
const path = require('path');
const webpack = require('webpack');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
process.noDeprecation = true;
const babel = {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: [
['#babel/plugin-proposal-decorators', { 'legacy': true }],
['jsx-control-statements'],
'#babel/plugin-proposal-function-bind',
['#babel/plugin-proposal-decorators', { 'legacy': true }],
['#babel/plugin-proposal-class-properties', { 'loose': true }]
],
compact: true
}
};
const config = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.js'
},
mode: 'development', // Change to production on checkin
context: settings.app(),
resolve: {
modules: [
settings.app(),
path.join(settings.project(), 'node_modules'),
path.join(__dirname, 'node_modules')
],
symlinks: true,
extensions: ['.js', '.jsx', '.json', '.css', 'scss', 'less']
},
resolveLoader: {
modules: [
path.join(settings.project(), 'node_modules'),
path.join(__dirname, 'node_modules')
],
symlinks: true
},
module: {
rules: [
{
test: /.(js|jsx)?$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'#babel/preset-env',
'#babel/preset-react'
],
plugins: [
'#babel/plugin-proposal-function-bind',
['jsx-control-statements'],
['#babel/plugin-proposal-decorators', {'legacy': true}],
['#babel/plugin-proposal-class-properties', { 'loose': true }]
],
compact: true
}
}
],
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.less$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'less-loader'
}
]
},
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 100000,
mimetype: 'application/font-woff'
}
}]
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 100000,
mimetype: 'application/octet-stream'
}
}]
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 100000,
mimetype: 'application/image/svg+xml'
}
}]
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: 'file-loader'
},
{
test: /-font\.otf(\?.+)?$/,
use: [
'file-loader?name=fonts/[name].[ext]'
]
}
]
},
devtool: 'source-map',
node: {
console: true,
fs: 'empty',
net: 'empty',
tls: 'empty'
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new CaseSensitivePathsPlugin(),
new MonacoWebpackPlugin()
]
};
module.exports = config;
index.js
import { observer } from 'mobx-react';
import React, { Component } from 'react';
import MonacoEditor from 'react-monaco-editor';
#observer
export default class TSEditor extends Component {
constructor(props) {
super(props);
this.state = {
code: 'var name : string = "kyle";'
};
this.editorDidMount = this.editorDidMount.bind(this);
this.onChange = this.onChange.bind(this);
}
onChange(newValue, e) {
console.log('onChange', newValue, e);
}
editorDidMount(editor, monaco) {
console.log('editorDidMount', editor);
editor.focus();
}
render() {
const code = this.state.code;
const options = {
selectOnLineNumbers: true
};
return (
<MonacoEditor
language="typescript"
theme="vs-dark"
value={code}
options={options}
onChange={this.onChange}
editorDidMount={this.editorDidMount}
/>
);
}
}
I my case... this did the trick... within webpack.config.js
devServer: {
https: true,
port: 3000,
publicPath: "/dist/"
},
...
..
.
plugins: [
...
..
.
new MonacoWebpackPlugin({
publicPath: "/dist/"
})
]
available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
This turned out to be a problem caused by an incorrect publicPath in the output portion of my webpack configuration.
For anyone else with a similar issue, add a publicPath in your webpack configuration that gives the correct location of your web worker JS files.

Retrieving data from the controllerApi method in the tsx file [Module Parse failed error]

I am trying to fetch some records from the database (through ControllerApi) and display them through React. Below is the code in the *.tsx file:
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
class CourseData {
courseId: number = 0;
title: string = "";
}
const FetchCourses = () => {
const [courses, setCourses] = useState(0);
fetch('api/Course/Index')
.then(response => response.json() as Promise<CourseData[]>)
.then(data => {
this.setState({ empList: data, loading: false });
});
let contents = this.state.loading ? <p><em>Loading...</em></p> : this.renderCourseTable(this.state.empList);
return <div>
<h1>Courses</h1>
{contents}
</div>;
}
This code generates the following error:
I adopted this code from this example.
I cannot see any syntax error, any ideas?
Below, I also share the webpack.config.js file
const path = require('path');
module.exports = {
mode: 'development',
entry: { 'main': './wwwroot/components/Main.tsx' },
output: {
path: path.resolve(__dirname, 'wwwroot/dist'),
filename: 'bundle.js',
publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-react', '#babel/preset-env']
}
}
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
}
};
Seems that you are missing a webpack loader to handle the tsx files
module: {
rules: [
{ test: /\.tsx?$/, include: /wwwroot/components/, use: 'awesome-typescript-loader?silent=true' },
{ test: /\.css$/, use: isDevBuild ? ['style-loader', 'css-loader'] : ExtractTextPlugin.extract({ use: 'css-loader?minimize' }) },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
]
},

Resources