Is it correct to think that loaders actually transform other types of code into javascript code? So a CSS-loader actually transforms CSS code into Javascript so that the JS file that imports CSS actually gets CSS transformed and then injected into that JS file when you webpack builds out your bundle?
This isn't true for all loaders. The idea behind Webpack loaders is to provide multiple ways to handle some resource or asset - whether that's a javascript module, a CSS stylesheet, or an image. The loader is responsible for parsing/processing the resource, which might do any of the following:
Transpile it into another language (e.g. babel-loader)
Copy the resource to a specified location and provide the new location as the imported value (e.g. file-loader)
Ignore the resource entirely (e.g. ignore-loader)
One thing that makes webpack so powerful is that these loaders can find more dependent resources inside the resource they're processing, and hand off to other loaders. So it's plausible to import an HTML file within your javascript, and that HTML file could reference another javascript file, which could then be loaded too.
Related
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?
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?
So I'm using Webpack to bundle up my js and css.
I've added the Webpack-plugin-critical to output an external css file featuring critical styles. This file gets added to my (twig) template.
It works fine if I run Webpack a second time, though the initial build fails and I get an error saying it can't find the primary generated css file. How do I expose the primary css file output from ExtractTextPlugin to Webpack-plugin-critical allowing it to consume the file and produce a critical css file on initial build?
I think it is impossible.
https://github.com/nrwl/webpack-plugin-critical/blob/master/src/critical.ts#L129
You can see that webpack-plugin-critical use the event hook "emit" which assets files are not generated by webpack compiler yet.
In the other hand, I recommend that you could use an other independent webpack config for generate critical css specifically.
For any one stuck on this. Use this plugin; https://www.npmjs.com/package/html-critical-webpack-plugin
It builds the critical styles AFTER the sass has been compiled.
I am refactoring a extremely large javascript file into multiple files with es6 modules / webpack. To start with, I am moving a single function out of giantFile.js into singleFunction.js, and then importing this new function file into index.js, which is the entry point for webpack to create bundle.js, which is then included in my template.html file as a script tag. In my template file, I also include giantFile.js as a script tag, which calls the function in singleFunction.js.
Is it simply a case of getting the script's imported in the correct order, or am i mistaken in my understanding of how giantFile.js can access the newly created modules.
Currently, within the console, when I type singleFunction(), i receive 'is not defined' error message', and so it would be good to check my understanding is correct of how I can use modules before further debugging. If anyone can point me towards some good resources on refactoring front end javascript and best practives that would be much appreciated too. Many thanks.
in singleFunction.js
`export default function singleFunction() {...}`
in index.js
import singleFunction from './components/singleFunction'
in template.html
<script src="/frontendHotness/components/singleFunction.js"></script>
<script src="/unstructuredMess/js/giantFile.js"></script>
The webpack compiled version of your giantFile.js should still be your application's entry point and the only file that is embedded in your HTML file using the <script> tag.
During your refactoring, you should gradually move well-encapsulated bits of functionality into separate files, or modules. Those modules export the functionality, to be used by dependent modules.
Your parent module, in this case giantFile.js can now import the different modules it depends on. These dependencies will be resolved by webpack, which moves your parent module together with all its dependencies into one JavaScript file that you can load from your HTML page.
Note that this dependency tree can be arbitrarily deep - your submodules can itself depend on other modules. You should however ensure that your modules encapsulate the functionality to do one particular job while being loosely coupled with other modules. Also avoid circular dependencies.
after optimizing a require.js project (pushing everything into one "big" .js file), is there any way to circumstance the need to include the require.js file (which then only loads one single .js file) and load the compiled file directly instead?
You can use a light weight AMD loader like Almond. You will still need a loader anyway as your compiled JavaScript still use define and require.