React : setting background image for a file present on the server - javascript

I want to use a background image for my website built using React.
I include in the .scss file :
html {
background-color: #FFFCB2;
background-image: url(https://mdn.mozillademos.org/files/11991/startransparent.gif);
}
and I just import the .scss file in my index.jsx main page :
import "styles/index.scss"
And it works perfectly
But when I use the same image saved on my folder, like this :
html {
background-color: #FFFCB2;
background-image: url(../static/images/startransparent.gif);
}
I obtain the following error message in my web client :
./styles/index.scss
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
ModuleParseError: Module parse failed: Unexpected character '' (1:6)
You may need an appropriate loader to handle this file type.
(Source code omitted for this binary file)
Most of the references to this error message (like in this post) refer to including a series of instructions in my webpack.config.
However I have no webpack.config in my main architecture, I am using babel (I'm not sure how these two facts are connected, I am not proficient in JS). So I wouldn't know where to apply this.
How to make images load properly from local? What if I don't have a webpack.config file in my project?
**Edit : It seems my lack of webpack.config comes from the fact that I'm using next. **
My current next.config.js file :
const withTranspileModules = require("next-transpile-modules")
const withTypescript = require("#zeit/next-typescript")
const withSass = require("#zeit/next-sass")
const _ = require("lodash")
const config = {
exportPathMap() {
return {
"/": { page: "/" },
}
},
distDir: "build",
transpileModules: ["#iconify/react"],
}
module.exports = _.flow([,withTranspileModules, withTypescript, withSass])(config)

Please add this to your webpack configuration file . It can solve gif loader problem. Please find this link for more reference
{
test: /\.(ttf|eot|svg|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
include: SRC,
use: [{
loader: 'file-loader'
}]
}]
},

The problem originated from the fact that I was using next and not webpack, which makes it somehow easier to install the loader, but less documented on the Internet.
I installed the "next-images" library, and changed my next.config.js to the following :
const withTranspileModules = require("next-transpile-modules")
const withTypescript = require("#zeit/next-typescript")
const withSass = require("#zeit/next-sass")
const withImages = require('next-images');
const _ = require("lodash")
const config = {
exportPathMap() {
return {
"/": { page: "/" },
}
},
distDir: "build",
transpileModules: ["#iconify/react"],
}
module.exports = _.flow([withTranspileModules, withTypescript, withSass,withImages])(config)
I was inspired by the solution appearing in this github issue

Related

Vue 3 Vite load fonts from assets with Font Loading API results in 404

I am trying to dynamically load fonts from the assets directory in Vue 3 (Typescript) using Vite. But the whole thing results in a 404 error.
const fonts = import.meta.glob('#/assets/fonts/*.otf')
console.log(fonts)
async function loadFonts() {
for (const path in fonts) {
const fontName = path.split('/')[3].split('-')[0]
console.log(fontName)
const myFont = new FontFace(fontName, `url('${path}')`)
await myFont.load()
document.fonts.add(myFont)
console.log(myFont)
}
}
loadFonts()
I've tried your codes in my configuration, and it works fine.
So, I think you may have wrong path alias of # in your vite.config.ts file.
You can see the error says that the source path is http://localhost:3000/assets/fonts/Acumin-Bold.otf, but the real fonts path is src/assets/fonts/Acumin-Bold.otf. Your src is missing.
Below is my vite.config.ts, you can take a look.
resolve: {
alias: {
'#': path.resolve(__dirname, './src')
}
}

Converting legacy namespace code to modules from the top down

I'm trying to convert out codebase which is comprised of multiple repositories to use modules instead of namespaces.
Sorry ahead of time if my understanding is fundamentally wrong, my main expirience in javascript is in this company so this is all I've ever known.
The codebase has heiharchies where certain repositories inherit from the base one so for instance if we have A,B,C,D,E then B,C,D,E all know A.
C might know B, but D only knows A, while E knows D and C. (Attached image to be clear)
Right now we have every repository compile into 1 js file using tsconfig outFile, and we load all of it in our html file one by one in order in script tags.
I've started converting the "upper" repos as modules can use namespaces, (so in my example E and D). I changed all of the code inside the repositories themselves to be modular and not use namespaces.
It now compiles properly and works (individually - the issue I'm having is with making them import from one another).
Since I'm trying to preserve the behavior it seems like I need to use a bundler.
I'm trying to use webpack but I'm having some problems with it.
I created a package.json (we didn't have one because we just directly made one js file and put it in the html until now yes that means we couldn't import things properly it's a nightmare it's why I'm trying to change it).
Installed webpack ts-loader and yarg.
I've then made the webpack.config.js I linked below and tried compiling.
Some of my repos don't have good entry files because they are initialized by other repos so I have to make a massive list and check that all files are at least written in the JS (there has to be a solution for this i'm missing right?)
The other issue is I don't understand how to make the other repos import from the repo I just bundled.
I put the bundled.js in the node_modules but when I attempt to import a class from it I get the following error:
"TS2305: Module '"../../node_modules/#types/hybridPanel.js"' has no exported member 'test'.
(I am trying to load D in E in this instance both have been converted to modules and are not using namespaces so this should work).
Do I need to publish the repo somewhere and npm install it in repo2 (I thought that's the same thing as moving the js over).
So the questions I have are:
How do I make this work?
What do I do about the entry files issue?
Am I even going about this the right way (will I even get types if I just import the js)?
Will we need to change our tags in the html to be type=module instead of javascript?
If I were to add a module (using npm or similar) will I then need to add it to the externals tag in the webpack? I'm not confident in my understanding just yet.
Is this the correct way of converting to modules from namespaces? Our plan is if I can get this to work to convert the rest of the higher ones next and then do the lower ones all at once (the base ones multiple people use) and fix the imports in the higher ones once we get there.
webpack config link :
https://pastebin.com/iNqV1dEV
const webpack = require("webpack");
const path = require("path");
const yargs = require("yargs");
const env = yargs.argv.env; // use --env with webpack 2
const pkg = require("./package.json");
const shouldExportToAMD = yargs.argv.amd;
let libraryName = pkg.name;
let outputFile, mode;
if (shouldExportToAMD) {
libraryName += ".amd";
}
if (env === "build") {
mode = "production";
outputFile = libraryName + ".min.js";
} else {
mode = "development";
outputFile = libraryName + ".js";
}
const config = {
mode: mode,
entry: [__dirname + "/src/panel/MoPanelManager.ts", __dirname + "/src/panel/chat/MoSingleChat.ts", __dirname + "/src/panel/booth/MoBoothDisplays.ts", __dirname + "/src/panel/chat/MoGifs.ts", __dirname + "/src/settings/MoSettings.ts"],
devtool: "source-map",
output: {
path: __dirname + "/www/module",
filename: outputFile,
library: libraryName,
libraryTarget: "umd",
libraryExport: "default",
umdNamedDefine: true,
globalObject: "typeof self !== 'undefined' ? self : this",
},
module: {
rules: [
{
test: /\.ts?$/,
use: {
loader: 'ts-loader',
},
exclude: /(node_modules|bower_components)/,
},
],
},
resolve: {
modules: [path.resolve("./node_modules"), path.resolve("./src")],
extensions: [".ts", ".js"]
},
};
module.exports = config;
Image of the repo example

How to dynamically import markdown files

I'm building a blog using Vue, where I'd like my posts to be written as markdown files.
Currently I have the following structure
src/
posts/
blogpost1.md
blogpost2.md
view/
myComponent.vue
I want myComponent.vue to dynamically load a markdown post based on the route params.
For instance if we visited .../blog/blogpost2, then we would dynamically load in blogpost2.md
My current implementation is as follows:
<template>
<div
v-html="md"
></div>
</template>
<script>
import marked from "marked";
export default {
data () {
return {
md: undefined,
};
},
created () {
const importRoute = `posts/blog/${this.$route.params.postName}.md`;
const md = require(importRoute);
this.md = marked(md);
},
};
</script>
To load the markdown file I'm using markdown-loader in webpack.
I'm also using copy-webpack-plugin to try to copy my entire /posts dir into my build folder.
configureWebpack: {
module: {
rules: [{
test: /\.md$/,
use: [
{ loader: "html-loader", options: {} },
{ loader: "markdown-loader", options: {} },
],
},
],
},
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/posts", to: "posts" },
],
}),
],
},
When I run npm run serve however, I receive
[Vue warn]: Error in created hook: "Error: Cannot find module './posts/blogpost2.md'"
And when I look at the pages in my browser I don't see the posts directory (see image below)
However, when running npm run build:staging I do see the posts directory in /dist
Why am I unable to import import blogpost2.mddynamically?
My attempt at using a dynamic import that changes based on the route will not work because webpack is unable to properly bundle the imported files. This is because webpack needs to know what it will be importing in order to bundle them correctly.
Instead we can use a different approach of copying our markdown directory as it is into /dist and sending a request to fetch each markdown file.
We end up with this in our webpack config (or in my case vue.config.js):
const CopyPlugin = require("copy-webpack-plugin");
configureWebpack: {
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/posts", to: "posts" },
],
}),
],
},
Note that we don't need the webpack loaders shown in my initial question because webpack itself isn't handling the loading of these files.
This ensures that posts/ gets copied as it is when we build this project.
Then in our component we can dynamically retrieve Markdown files like so:
<template>
<div
v-html="md"
></div>
</template>
<script>
import marked from "marked";
export default {
data () {
return {
md: undefined,
};
},
async created () {
const res = await fetch(`http://localhost:8081/posts/${this.$route.params.postName}.md`);
const md = await res.text();
this.md = marked(md);
},
};
</script>
// I once need to do a similar thing and faced similar challenges but it was built with Create React App, so please take my answer with a pinch of salt.
Try placing your import path inside require(…) (or import(…), if you ever use it) instead of declaring a variable then passing it in. I remember WebPack has trouble finding files when the paths are not static strings (hard-coded in this way). Whatever dynamic path it may be, your path needs to start with a static string, but you can concatenate dynamic values behind it.
Also, notice the ./ I added to the front of the path. Ignore it if you're using absolute imports, otherwise, this might also be a reason why the code failed?
created () {
// Instead of:
// const importRoute = `posts/blog/${this.$route.params.postName}.md`;
// const md = require(importRoute);
// Try:
const md = require(
`./posts/blog/${this.$route.params.postName}.md`
);
this.md = marked(md);
},
Extra: As much as I know, that the created hook in Vue is supposed only be synchronous, but after stumbling upon this answer I decided to include this snippet too. This one uses dynamic import. If all else fails perhaps you can give this a try?
async created () {
const { default: url } = await import(
`posts/blog/${this.$route.params.postName}.md`
);
const res = await fetch(url);
const md = res.text();
this.md = marked(md);
},

How to get React Native variant in Metro?

I am developing React Native application that includes different configurations for different possible clients, in a file such as src/config/config.js. These configurations are quite complex. The file is structured based on the client name as key, and the values as the object entries, e.g.:
export default {
fooClient: {
apiUrl: "https://foo.example.com/",
barClient: {
apiUrl: "https://bar.example.com/"
}
}
Of course, there are many other option keys.
When building the app, I know for which client I want to do this, by specifying an Android build variant, e.g.:
ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
For security reasons, I don't want keys of other clients to be included in the config file though. What are my options to remove all other client configs from this file before I build the app and ship it to a client?
I thought about the following: I modify the packager so that it strips out the keys that do not correspond to the current build variant.
I now have a transformer plugin for Metro that does the following:
const upstreamTransformer = require('metro-react-native-babel-transformer');
module.exports.transform = function(src, filename, options) {
if (typeof src === 'object') {
// handle RN >= 0.46
({ src, filename, options } = src);
}
if (filename.endsWith('config.js')) {
console.log('Transforming ' + filename);
let srcStripped = src.replace(';', '').replace('export default ', '');
let configObj = JSON.parse(srcStripped);
// TODO: get the build variant and strip all keys that we do not need from configObj
return upstreamTransformer.transform({
src: 'export default ' + JSON.stringify(configObj) + ';',
filename: filename,
options
});
} else {
return upstreamTransformer.transform({ src, filename, options });
}
};
But how do I know which build variant is being used?
If this seems like an XY problem, I am happy to explore alternatives to building the configuration dynamically. I cannot, however, use environment variables, since the configuration will be too complex for it to be just a list of .env keys.
You shouldn't use Metro transform this way. It's not clean and it may lead to missing configuration and/or damaged syntax sooner or later.
What I have done and suggest you, is to create 3 different configuration files under src/config/; one file for fooClient.js, one file for barClient.js and one last file with common configuration client.js. All files will export default configuration objects, but inside each fooClient and barClient, you'll use deepmerge module to merge client.js config:
client.js:
export default {
commonSettingA: "...",
commonSettings: {
...
}
...
}
fooClient.js:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://foo.example.com/",
}
]);
barClient.js:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://bar.example.com/",
}
]);
Then you can use an environment variable to pass the needed configuration and create a propriate metro resolve; #react-native-community/cli does not pass command line arguments to metro config script. You can use process.argv to parse it by yourself, but it's not worth it.
Here is how you can create a resolve inside metro.config.js using environment variable:
const path = require("path");
module.exports = {
projectRoot: path.resolve(__dirname),
resolver: {
sourceExts: ['js', 'jsx', 'ts', 'tsx'],
extraNodeModules: {
// Local aliases
"#config": path.resolve(__dirname, "src/config", `${process.env.METRO_VARIANT}Client.js`)
}
}
};
Using this resolve, you'll have to import the configuration like this:
import config from '#config';
Then you add 2 package.json scripts, one for fooClient and one for barClient:
{
...
"scripts": {
"run:android:foo": "METRO_VARIANT=foo ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo",
"run:android:bar": "METRO_VARIANT=bar ENVFILE=.env npx react-native run-android --variant barDebug --appIdSuffix bar",
...
}
...
}
Then you just run the needed script:
yarn run:android:foo # will build with fooClient.js
yarn run:android:bar # will build with barClient.js
Can't you just add a "duplicated" parameter to the command?
Like so
METRO_VARIANT=fooDebug ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
And get it with process.env.METRO_VARIANT
You might not want to make your setup much more complex. Depending on the number of the number of different clients, you might wanna go with multiple schemas/variants per client, but that is probably way overkill.

Webpack: How can I create a loader for "webpack" which takes an array of dependencies?

For example, I use AMD definition in my project, and use "webpack" for project building. It's possible to create some loader which will take a dependencies in array format?
define(
[
'mySuperLoader![./path/dependency-1, ./path/dependency-2, ...]'
],
function() {
// ... some logic here
}
)
Project example: gitHub
If you want to port the load-plugin's behavior to webpack, you need to do this:
1. Create a custom resolver
This is because mySuperLoader![./path/dependency-1, ./path/dependency-2, ...] does not point to a single file. When webpack tries to load a file, it first:
resolves the file path
loads the file content
matches and resolves all loaders
passes the file content to the loader chain
Since [./path/dependency-1, ./path/dependency-2, ...] is not a proper file path, there is some work to do. It is even not a proper JSON.
So, our first goal is to turn this into mySuperLoader!some/random/file?["./path/dependency-1", "./path/dependency-2", ...]. This is usually done by creating a custom resolver:
// webpack.config.js
var customResolverPlugin = {
apply: function (resolver) {
resolver.plugin("resolve", function (context, request) {
const matchLoadRequest = /^\[(.+)]$/.exec(request.path);
if (matchLoadRequest) {
request.query = '?' + JSON.stringify(
matchLoadRequest[1]
.split(", ")
);
request.path = __filename;
}
});
}
};
module.exports = {
...
plugins: [
{
apply: function (compiler) {
compiler.resolvers.normal.apply(customResolverPlugin);
}
}
]
};
Notice request.path = __filename;? We just need to give webpack an existing file so that it does not throw an error. We will generate all the content anyway. Probably not the most elegant solution, but it works.
2. Create our own load-loader (yeah!)
// loadLoader.js
const path = require("path");
function loadLoader() {
return JSON.parse(this.request.match(/\?(.+?)$/)[1])
.map(module =>
`exports['${path.basename(module, '.js')}'] = require('${module}');`
)
.join('\n');
}
module.exports = loadLoader;
This loader parses the request's query we have re-written with our custom resolver and creates a CommonJS module that looks like this
exports['dependency-1'] = require('path/to/dependency-1');
exports['dependency-2'] = require('path/to/dependency-2');
3. Alias our own load-loader
// webpack.config.js
...
resolveLoader: {
alias: {
load: require.resolve('./loadLoader.js')
}
},
4. Configure root
Since /path/to/dependency-1 is root-relative, we need to add the root to the webpack config
// webpack.config.js
resolve: {
root: '/absolute/path/to/root' // usually just __dirname
},
This is neither a beautiful nor an ideal solution, but should work as a makeshift until you've ported your modules.
I don't think that you should use a loader for that. Why don't you just write:
require("./path/dependency-1");
require("./path/dependency-2");
require("./path/dependency-3");
It accomplishes the same thing, is much more expressive and requires no extra code/loader/hack/configuration.
If you're still not satisfied, you might be interested in webpack contexts which allow you to require a bulk of files that match a given filter. So, if you write
require("./template/" + name + ".jade");
webpack includes all modules that could be accessed by this expression without accessing parent directories. It's basically the same like writing
require("./table.jade");
require("./table-row.jade");
require("./directory/folder.jade")
You can also create contexts manually like this
var myRequire = require.context(
"./template", // search inside this directory
false, // false excludes sub-directories
/\.jade$/ // use this regex to filter files
);
var table = myRequire("./table.jade");

Categories

Resources