Creating an SVG Sprite component with ejected create-react-app - javascript

I'm using the create-react-app library and I've created an SVG component that works well in development. My problem is that when building the application to publish, the build process doesn't recognize my component's dynamic paths and therefore doesn't put the main sprite file into my /media folder.
Example SVG Component:
render() {
return (
<svg className={`icon ${this.props.id}`} fill={this.props.fill}>
<use xlinkHref={`/src/assets/images/svg-sprite/svg-sprite-${this.props.category}-symbol.svg#ic_${this.props.id}_24px`}></use>
</svg>
);
}
As you can see I'm referencing specific symbols in specific sprite files.

If you ejected before 0.5.0, importing assets is the only way to get them added into the build output. There are good reasons for this: for example, their filenames automatically include a hash because build system is aware of them, so you don't need to worry about busting browser caches when the file changes. You also don't need to worry about typos because a missing file will give you a compilation error.
Since 0.5.0, we also support a public folder as an escape hatch. You can put any files in the public folder, and they will be merged with the build output. The only gotcha is that to reference them, you need to add process.env.PUBLIC_URL to your links. This ensures that if you build a project for a non-root URL (like GitHub Pages), it still works correctly.
<use linkHref={process.env.PUBLIC_URL + `/assets/images/svg-sprite/svg-sprite-${this.props.category}-symbol.svg#ic_${this.props.id}_24px`}></use>
would work as long as your public folder contains assets/images/svg-sprite/svg-sprite-* files.
Please note again that this feature is only available since react-scripts#0.5.0 so if you ejected earlier, you might need to backport it to your project.
Reference:
Using the public Folder
Pull Request adding support for it

In case anyone runs into this issue... I looked into the webpack.config.prod.js and found a comment that says any files you import get built into the /media folder. Fixed my problem by importing all of my SVG sprite files which isn't ideal but got the job done.

Related

webpack resolve error with packaged css file referencing an image

I'm working on a Vue component library built via VueCLI (and using Storybook Js, Bulma, and Buefy) and I am having issues consuming the CSS downstream. Specifically when I import the CSS file from my package, I am getting Webpack errors with referenced images.
For example, in my upstream src scss files I have a file called "notice-badge.scss" and am referencing background images like so:
.notice-badge img {
background-image: url('#/assets/img/warning-dark.svg');
}
and my src directory structure looks like:
my-app/
|--src/
|--assets/
|--scss/
|-- notice-badge.scss
|--img/
|--warning-dark.svg
|--fonts/
|--vue-components/
and I build the packages with this command which produces no errors.
vue-cli-service build --target lib --name my-ui-components ./src/index.ts
This outputs my JS, a CSS file, and 2 directories (img and fonts) into my "dist" directory. The images listed in my errors are infact inthere.
So over in another Vue cli app (and later Nuxt) I will be importing the CSS file and Vue components but I am getting a "can't resolve" error on that warning-dark.svg file:
Can't resolve /img/warning-dark.a45b259b.svg in /Users/myname/sites/my-app/ui-components/dist. My package also contains font awesome font files too (a business decision to include this all up stream)
So how can I get my downstream Vue CLI app to resolve the images and fonts referenced inside my node_modules dir?
You have (at least) 3 options:
Inline the images/fonts as data URLs.
Use a relative path in the output and require apps that install your package to move the image directory to the same path as the built CSS file.
Don't ship built CSS, but instead source SCSS files. That way file loading/moving can be handled with WebPack configuration in the app that uses it (using file_loader. You can include example configuration in your package to make this easier.
If you're writing a Vue component library, it probably makes most sense to use method 3. However from your description it seems like this may not be an option (the business decision you mention). Method 2 might be viable but I didn't try it nor seen someone else suggest it.
Inline
This method probably is easiest and has best performance. If your other SVGs are similar to the examples, it seems like they should all be relatively small files. There's few reasons for a component library to ship big images, so this might be sufficient for your use case.
If you're using WebPack 5, you can inline assets using "Asset Modules".
module: {
rules: [
{
test: /\.svg/,
type: 'asset/inline'
}
]
}
If you tried this, you may have run into the following problem.
Since Sass implementations don't provide url rewriting, all linked
assets must be relative to the output.
If you pass the generated CSS on to the css-loader, all urls must be
relative to the entry-file (e.g. main.scss).
If you're just generating CSS without passing it to the css-loader, it must be relative to your web root.
You can try replacing url('#/assets/img/warning-dark.svg') with url('../img/warning-dark.svg') (or whatever the path relative to the entrypoint is). Does it now properly inline them?

How to use static files with Custom Web Component

I have created a web component, uploaded it to npm, and imported it into another project.
I use webpack to bundle all of the code used within the web component, which creates a bundle file (in my case index.js).
This is the structure of my dist folder:
/dist
/static
/images
/fonts
index.js
Everything works, except loading images or fonts (static files). How can this issue be overcome? I have just added the static files to a public github repo at the moment, and load them from there. But I do not think this is the right way to do it.
I guess if the images are not that large, they can be converted into base64 and just bundled with the rest of the code?
Any thoughts
Found a similar question Paths in Web Components are Relative to Root.
The webcomponent specification defines that URLs are always relative
to the main document. This, of course, breaks web component
encapsulation, as you rightly concluded. In other words, the
specification is buggy, and lots of people are complaining about it.

Cannot create Vue application http-server - Error: css and js files 404 Not found

I want to dockerize my vue app, but when I run it in a docker container nothing is loaded in the browser.
Since I run CMD["http-server", "dist"] in my Dockerfile, I decided to test it out locally to troubleshoot the issue.
Running:
npm run serve
Works fine and I get:
Then I run
npm run build
I believe this is due to having a posters folder with 50,000+ jpeg images in the assets directory which I dynamically display in the app as follows:
<div v-for="movie in this.recommendations" :key="movie" class="movie-card col-md-2">
<img v-if="movie['poster']=='True'" :src="getImgUrl(movie['movieId'])" v-bind:alt="pic">
And the getImgUrl function is:
getImgUrl(imgName) {
var images = require.context('../assets/posters', false, /\.jpg$/)
return images('./' + imgName + ".jpg")
}
vue/cli suggests
webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
but I'm not sure how to implement either one of these or
if hosting the images on a public google drive and importing them from there would solve the issue?
Pretty new to vue so any help would be much appreciated!
By using the assets folder and using require you are bundling all of your images into your code, by encoding them as base64. So when it is compiled it is creating GIANT chunks. Because the images are compiled into the source.
What you should do is move your images from assets to the public directory. Then load the images via HTTP. This means that your code and images remain separate. When the page loads, the browser requests the images separately from your code, and loads them into the file.
For example
<img v-if="movie['poster']=='True'" :src="getImgUrl(movie['movieId'])" v-bind:alt="pic">
getImgUrl(imgName) {
return `/posters/${imgName}.jpg`
}
Thus your directory structure would become
-public
-|--posters
-|--|--Poster1.jpg
-|--|--Poster2.jpg
-|--|--Poster3.jpg
-|--|--Poster4.jpg
etc
The public directory overly simplified acts as a webserver. Anything that is in it can be accessed directly. For example, if you were to move your images over, to the public directory, with the directory structure above, and access localhost:8080/posters/Poster1.jpg, it would load just the image, without even needing a Vue router.
For a better, in-depth description of the public folder, and the role it serves check out the docs.

Using webpack and publicPath for static resources

The following question was rewritten, because I have now a working solution, but no answer to the question above.
The repository that shows different scenarios how to use resources packed with webpack is named example-webpack-dynamic-resources. It contains 3 modules:
inline: a solution, but not useful in my context (many resource files)
file: a solution by using the plugin webpack-require-from
public-path: no solution yet, shows how I would like to use __webpack?public_path__.
I think I have read any resource about webpack and publicPath and __webpack_public_path__, but I don't get it to work. I try to dynamically change the path to static resources, but it fails.
Here is my context:
I build a Javascript library that will be used on web pages (HTML, CSS, Javascript).
It provides a lot (>100) static resources to small image files, combined > 500 KB. Only a fraction of it will be used by the user looking at the web site.
Therefore I would like to pack the CSS into the bundle, but keep the image resources in a directory located on the server somewhere. The default path to it will be /img.
As long as I use the same structure (which means, images only under ROOT/img/**, everything is ok.
But the users of the library should be able to configure the path to the image resources on their will.
You will find all relevant files in my example repository example-webpack-dynamic-resources in the module public-path-resources.
webpack.js: Use file-loader for images, which are referenced in CSS files. CSS will be inlined by style-loader and css-loader.
src/public-path.js: Define the global variable with a default (no environment variable).
src/index.js: require first public-path, then the logic.
examples/exam1-root/index.html: Tries to use the assets in the sub directory lib, sets the value therefore to __webpack_public_path__ = '/lib/. Not working.
examples/exam2-different-dirs/index.html: Moves the library to a different dir (not relevant), but uses the originally defined directory pgnv-assets for the assets. Working.
examples/exam3-non-standard-dirs/index.html: Try to use instead my-assets as directory for the assets. Not working.
How could the __webpack_public_path__ defined at runtime in the index.html file?

How to download the complete project structure from cdnjs using Python

I want to download the complete project from the cdnjs cloud to local folder.
I have tried this:
import requests
files = requests.get("https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML%2CSafe.js&ver=4.1")
with open("mathjax.js","w") as file:
file.write(files.text)
Now this download the js file. When I tried using the same code to get the project instead of the js file, the output was weird.
So I tried using the cdnjs and check what happens when I use cdnjs cloud and when I use local file.
I have got this difference as shown in the images:
Using cdnjs:
Using Local file:
How I can get the similar structure as I get when I use cdnjs?
Kindly, advise me.
The URL you are providing to requests module is just the URL of one file MathJax.js, that is why you are getting only that file as output.
What you want is to download the complete directory mathjax/2.7.5/. However, if we request the whole directory, the server forbids such requests.
An alternate approach is to get relative paths of all the files from the main directory, which you already have as you showed in image. You can then download each of the file independently and store it into its respective folder. You'll have the whole directory ready at the end.
Try the following code for this purpose.
import requests
import os
baseUrl="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/" #Base URL for the main directory
#List containing relative paths of all required files
relativePaths=['config/Safe.js?V=2.7.5',
'config/TeX-AMS-MML_HTMLorMML.js?V=2.7.5',
'extensions/Safe.js?V=2.7.5',
'jax/output/SVG/fonts/TeX/fontdata.js?V=2.7.5',
'jax/output/SVG/jax.js?V=2.7.5',
'MathJax.js?config=TeX-AMS-MML_HTMLorMML%2CSafe.js&ver=4.1']
parentDir='\\'.join(baseUrl.split('/')[-3:]) #Parent directory from URL
for path in relativePaths: #For all files
req=requests.get(baseUrl+path) #forming url
filename=path.split("/")[-1].split("?")[0] #extracting filename out of url
directory=os.path.join(parentDir,"\\".join(path.split('/')[:-1])) #Extracting directories path for local path formation
if not os.path.exists(directory): #Creating local direcories if they do not exist
os.makedirs(directory)
with open(os.path.join(directory,filename),"wb+") as file: #Storing results into files
file.write(req.content)
Local Directory Structure Output:
Beyond iterating over a defined list of files, you could also look at a couple of other options that could take a more dynamic approach to fetch files from the CDN.
cdnjs is powered by a GitHub repository, so you could explore cloning it and extracting files (I'd recommend use sparse-checkout if you do this due to repo size) or you could look at using the GitHub API to navigate the repository an extract files: github.com/cdnjs/cdnjs/tree/master/ajax/libs/mathjax/2.7.5
We actually have an API available for cdnjs, which allows you to rather easily get all the files within a version of a library. Using that list, you could then perform a similar iterative solution to what Hamza suggested to get a copy of all the files locally: https://api.cdnjs.com/libraries/mathjax?fields=assets (annoyingly we've not yet implemented API navigation per version)
Hope that helps!
Matt, cdnjs maintainer.

Categories

Resources