Webpack 'publicPath' with express static - express

I'm trying to understand webpack output.publicPath now. I'm reading webpack official docs now, but it dosen't help me.
So here is webpack.config.js
import webpack from 'webpack';
export default {
output: {
filename: 'bundle.js',
path: '/dist',
publicPath: '/assets' // what's this for?
},
plugins: [
// ...
]
};
So i guess, this makes all files reference /assets which is set to publicPath. like prefix.
When if i want to server static file in /assets with express server, i should makes /assets as static like app.use('/assets', express.static(__dirname + '/assets')) .
Then what is the purpose of publicPath ? is it just prefix for path?

I'm a bit late, but in case anyone else looked at this question like me and wished it had an answer -
This publicPath is used by webpack as an alias by which you can access your built files. When you also attach it to express static file serving as you've done above, you allow any files placed there by webpack to be requested via that path.
For example, if you have /assets as your publicPath and a file foo.js that is built during your webpack build, you can then access it by hitting localhost:[port]/assets/foo.js

I've spent so much time trying to wrap my head around path vs publicPath that my head nearly explodes. Here is what I understand (correct me if I'm wrong):
publicPath specifies the URL that Webpack needs to reference from the perspective of the index.html file.
For example:
module.exports = {
output: {
path: path.resolve(__dirname, 'dist/assets')
publicPath: '/assets/
}
}
This means the bundle is located at the dist/assets directory in your file system, but a request to it will look like localhost:3000/assets/bundle.js.
Inside the index.html, the script tag will look like this:
<script type="text/javascript" src="assets/bundle.js"></script>
It is even more important when you serve assets from external resources like a CDN.
What you do with Express static middleware is irrelevant AFAIK. But I still set the assets folder as the root directory from which to serve static files.

Related

Static site generation with Nuxt.JS using relative paths

I'm using Nuxt 2.15.8 to generate static pages (migrating to Nuxt 3 is also an option for me if it solves the problem).
It works great when deployed in the root folder of the server but I need it to be served in a subdirectory, like:
https://my.domain.com/folder/subfolder
The problem is that the compiled HTML includes nuxt related assets like:
/_nuxt/123456789.js
which translates to:
https://my.domain.com/_nuxt/123456789.js which obviously fails as the file is in a subfolder, not in the root.
I tried using publicPath config and absolute paths but it is not an option for me as I have several environments with different URLs.
I need to generate static HTML files with relative paths in order to make sure my site works as expected in all the environments, agnostically from the server URL.
I was able to achieve it using Vite + Vue 3 but migrating to a new implementation is not an option, I need to achieve it using the current Nuxt implementation.
I tried using nuxt-vite https://vite.nuxtjs.org/ but was not able to achieve relative paths, I still get
/_nuxt/123456789.js
instead of
./_nuxt/123456789.js
../_nuxt/123456789.js
../../_nuxt/123456789.js
, etc
It seems like it's not supported in plain nuxt 2 but if you use nuxt-vite you can set vite.base to '' or './' in nuxt.config to make the paths relative.
Try this out:
export default defineNuxtConfig({
app: {
baseURL: '/mydir',
buildAssetsDir: '/mydir/_nuxt/',
},
Or just edit index.html manually...

Vue js problems when building outside the src folder

I am working on an MPA with Vue and codeigniter. I have the following architecture:
-htdocs(root)
- application
- src
- views
- models
- controllers
I work with my frontend basically in the src directory and the others are codeigniter MVC model directories. What I am doing is configuring the webpack to build directly in the views directory so that my codeigniter can consume the generated htmls, configured as follows: (in this case I set it up in the vue.config.js file
)
outputDir: './views'
Up to this point everything works fine, wepback does the bundles and generates all the necessary files inside my views directory.The problem now is that the path to the files is the root of the project. Then he tries to fetch the files like this:
<link rel="preaload" href="/css/chunk-common.45fe69c2.css" as="style">
So that it points to the correct path (application/views) I made the following configuration in the vue.config.js:
assetsDir: './views'
But now when he is going to do the build I have the following warning and the files bundle is not completed.
Does anyone know why this happens?
Assuming you want to access static assets under the base URL http://example.com/application/views/..., you'll want to set the publicPath property in your config
// vue.config.js
module.exports = {
outputDir: 'views',
publicPath: '/application/views/',
// ...
}
FYI, if you ever do want to use assetsDir, don't prefix it with ./
A directory (relative to outputDir) to nest generated static assets (js, css, img, fonts) under.
An example would be
assetsDir: 'assets'
which would dump files in views/assets/css, views/assets/js, etc.

Vue + Webpack: exclude config.js from being packed works, but it does not load

What I want:
In my Vue project, when doing a vue run build, everything is packed (webpack) into the dist directory.
I could deploy this entire package to the test server, but since the test server needs another set of credentials (for database etc.), one file, namely config.js, must be different on each environment.
My strategy:
exclude config.js from being packed into the app.12314...js bundle
define config.js as being emitted unaltered as a file (via webpack's file-loader)
What I did:
In the Vue component files which need config data, config is included via:
<script>
import config from '#/config/config.js';
</script>
I created a vue.config.js file for modifying the default webpack settings like this:
const path = require("path");
// vue.config.js
module.exports = {
publicPath: '/',
configureWebpack: {
module: {
rules: [
{
test: /config.*config\.js$/,
use: [
{
// exclude config file from being packed. The config file should simply reside as a plain file on the
// file system, since it must be replaced with the target environoment specific config file on the server,
// e.g. for setting the correct database connection
loader: 'file-loader',
options: {
name: 'config/config.js'
},
}
]
}
]
}
}
}
What I expected:
config.js will be emitted as a plain file, not bundled => dist folder will contain a bundled app....js file and a separate config.js file
I can deploy the content of the dist folder to the test server and the generated code will take care of loading config.js adequately, so that the components can use the config data
Problem:
Granted, config.js is now emitted as a file, so my dist folder looks like this:
.
├── config
│   └── config.js
├── css
│   ├── app.eb377513.css
│   └── chunk-vendors.2da46af1.css
├── favicon.png
├── index.html
└── js
├── app.724607ed.js
├── app.724607ed.js.map
├── chunk-vendors.426dad42.js
└── chunk-vendors.426dad42.js.map
As you can see, a separate file config.js! All other JS code has been bundled into app.724607ed.js. So far, so good.
However, the application on the test server would not load config.js, so every component which tries to use the variables in it, fails.
I noticed that in Chrome's developer tools, there is no network traffic for config.js. Apparently, the webpack code does not try to actually load the file from the file system. I expected this to be the case automatically.
what am I doing wrong, what am I missing?
is this the best practice to exclude config file from being packed?
Apparently, Webpack's file-loader does emit the file separately (good), but does not add code for loading it in the webapp (bad). I assumed that file-loader would take care of inserting the correct <script> tag in the index.html file, so that:
the separately emitted config.js file will be loaded
on the dev side, the syntax for using the config.js is the same as for the other (packed) artifacts, like this:
(in the Vue component file:)
<script>
import backend from '#/utils/backend.js'; // <-- normal way of requiring modules in Vue component
import config from '#/config/config.js'; // <-- would work the same, but actually will load emitted file
</script>
Unfortunately, seems I was wrong. Since Webpack does not care about loading the file in the webapp, I switch to a manual way, like this:
Step 1: Emit as a separate file
This one is important, since I want the config.js file to be separate (not packed and minified along with the rest of the Vue app). I already described how to do this in the question (define a separate rule for config.js and use file-loader for it).
Step 2: Take care yourself of loading the config file in the webapp
Since the file-loader does not add loading code, make it manually. In index.html, insert the following line:
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.png">
<script type="text/javascript" src="config/config.js" ></script> <-------- insert this line!
<title>replayer-gui4</title>
</head>
Step 3: change config.js to be compatible with legacy importing method (without ES2015 module)
Now, the commonJS syntax is not viable anymore because config.js should be loaded by the browser. And I am not sure how to mix ES2015 type module loading (with native import syntax) with Webpack code (which would replace all import's with Webpack loading code). So I decided to switch to a legacy method: let config.js simply register a global (yuck!) variable like in old times:
var config = (() => {
return {
databaseCredentials: ...
};
})();
Consequently, in each Vue module consuming config settings, adapt the "import" code - simply remove the imports because the variable config will be avalailable globally anyway (after our changes in config.js in this step):
<script>
// import config from '#/config/config.js'; // <-- commented out - or just REMOVE this line
</script>
Step 4: Trigger Webpack to consider config.js at all
The only caveat now is that since config.js is never referenced in a Javascript file in an import statement, Webpack would not "find" the file and would never take it into consideration. Which means that config.js also never would get emitted.
So, force Webpack to handle it, in App.vue:
<script>
import configDummy from '#/config/config.js';
</script>
Note that we never use the configDummy reference (variable), this is just that Webpack finds it - and applies its rules to the file. The rule in this case would be: emit it as a separate file in the dist folder (i.e. produce a dist/config/config.js file)
That's it! We used Webpack's file-loader so that config.js is not minified and bundled into the application bundle, but kept as a separate file. Further we took care of loading it via index.html which means the config settings are globally available.
What I do not like about this solution is that the nice methodology with import methods is corrupted for this special file. But I found no simple solution which just would make Webpack take care of these things.
If somebody has a suggestion, I would be glad to hear!
Update 09.04.2019: No, this is NOT an ideal solution. This works for the packaging/deploying (the npm run build part), but not for the development server (the npm run serve part). In order for the dev server to work, I had to copy config.js into the path public/config/config.js, so that the dev server can find it at the place which index.html is telling it, i.e. config/config.js (this is resolved relative to the index.html file).
So, again: it works, but sort of clumsy. And I do not like this solution.

Configure nuxt.js application to work in a subdirectory on a webserver

I want to deploy a static nuxt.js application (built with nuxt generate) to a subdirectory of a webserver. nuxt places the generated files in the dist directory by default:
If I start a webserver on the parent directory of the dist folder and open the page with:
http://localhost:34360/dist/
the site fails to load the script files from the domain root directory:
I've tried setting the publicPath property in the nuxt config:
build: {
publicPath: '/dist/'
}
The appplication compiles to:
Now, nuxt moves the script files one level lower (/dist/dist) and searches on root level again (/dist), thus still not finding the files
How can I configure the site, such that scripts and assets are loaded and it is self contained, no matter on which directory on my server I put it?
The issue has been covered on GitHub but the suggested hints (using publicPath) didn't work, as shown above.
Sidenote: I do not want to specify the publicPath absolut (i.e. http://localhost:8080/dist), which would work but creates new problems.
You need router/base
router: {
base: '/dist/'
}
To complete #Aldarund answer, I use :
export default {
[…] // some code
router: {
base:
process.env.NODE_ENV === "development" ? process.env.BASE_URL : "/<subfolder>/"
} // where <subfolder> is the subfolder!
};
In Nuxt 3.0 use config's app.baseURL or NUXT_APP_BASE_URL environment variable.

Add _redirects file to root path for Vue SPA hosted on Netlify

I'm developing a Single Page App using Vue CLI and want history pushstate to work so I get clean URLs.
I have to follow this: https://www.netlify.com/docs/redirects/#history-pushstate-and-single-page-apps and add a _redirects file to the root of my site folder with the following:
/* /index.html 200
The problem is I don't know how to add this _redirects file to the root of my dist folder. I tried adding it to the static folder but it ends up in a subfolder and not in root. How can I include this file so that history mode works after deploying on Netlify ?
// config/index.js
build: {
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
vue-cli created app 3.x
For the new build setup using the vue-cli version 3.0.0-beta.x there is a public folder now and you do not need the following setup. Just put your _redirects file under the public folder root. When you build it will make a copy to the dist folder which will be used for your deploy.
vue-cli created app prior to 3.x
Vue.js uses webpack to copy the static assets. This is maintained in webpack.prod.conf.js for the production build which is what you will need in this case for Netlify. I believe the best and cleanest configuration is based on this solution.
Search the file for the new CopyWebpackPlugin in webpack.prod.conf.js.
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
Create a root ( folder in your project same level of the static folder )
You could name this anything you like, but I will use root for the example.
You would then make sure the _redirects file is in the new root directory or whatever you called it. In this case it is named root
Now modify the webpack.prod.conf.js CopyWebpackPlugin section to look like the following:
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
},
{
from: path.resolve(__dirname, '../root'),
to: config.build.assetsRoot,
ignore: ['.*']
}
])
You could also just use the netlify.toml file which tends to be a bit cleaner. Just put this in the file to get the redirect you were looking for:
# The following redirect is intended for use with most SPA's that handles routing internally.
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
netlify.toml is normally stored in the root of your site repository.
You can find more info about this file here.
I've tried Rutger Willems's snippet without the last line and it works. Credit goes to Hamish Moffatt.
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Just in case you're looking for an answer on Nuxt instead of Vue, add the _redirects file to the static/ directory.
You can simply add the _redirects file to your /public directory in your vue app