Vite build gets path inside css file wrong if i set base to './' - rollup

i've set up a plain vanilla js Vite installation and changed the folder structure to include all my "working" files inside a "src" folder.
With the following rollup Option inside my vite.config.js file, the build process mimic my folder structure inside the dist folder as well.
rollupOptions: {
output: {
assetFileNames: (assetInfo) => {
let extType = assetInfo.name.split('.')[1];
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) {
extType = 'img';
}
if (/woff|woff2|ttf/i.test(extType)) {
extType = 'fonts';
}
return `assets/${extType}/[name]-[hash][extname]`;
},
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
},
},
Build process works as intended: all files are inside their specified folders and all links inside html & css files are rewritten correctly.
But as soon as i set 'base' to './' or '' inside my vite.config.js file and build the project the url links inside css files are corrupted. They are missing the path. Only base + filename are written. All urls inside html files are build correctly.
If i set the base to something like '/somename/' all urls (html&css) are build correctly.
How can i fix this? :)
Here is a stackblitz example, where the body background image shows this behaviour. https://stackblitz.com/edit/vitejs-vite-j6xd8y?file=dist/assets/css/index-1e183c12.css

This seems to be a bug in Vite 2 (as of 2.9.12). I recommend reporting the issue.
As a workaround, switch to Vite 3 (currently 3.0.0-beta.2), which has refactored the base configuration code and avoids the problem you observed:
npm i -D vite#beta
demo

Related

How to use environment variables with static JS in public folder

I have VueJS app (Vue CLI 3) and additional static JS script in public folder. And I don't understand how I can use .env in this .js.
Let's say I have some specific environment variable, for example MY_URL and my JS file:
const myUrl = process.env.VUE_APP_MY_URL;
And it's not working, because static files from public folder don't processed by webpack as I understand.
Maybe someone knows good solution? Or maybe other solutions\workarounds?
In my case, I put .js to src and add new entry by chainWebpack:
config.entryPoints.delete('app')
config.entry('app')
.add('./src/main.ts')
.end()
.entry('myScript')
.add('./src/myScript.js')
.end()
And now webpack build the script as separate file, but injects to index.html with app.js. This is not what I really want.
So, main purpose - build separate static JS file with specific name without hash (for example, myScript.js) which would contain variable from .env (.env.production, .env.development)
Main fact about static files in public folder from docs:
Static assets placed in the public directory will simply be copied and not go through webpack
So, I cannot use .env with static files in public.
I haven't found a perfect solution, but at least 3 acceptable options:
JS file as entry, as Jesse Reza Khorasanee said in comments and gave a link to almost same question
The main idea:
configure vue.config.js for an additional entry and force webpack to process my file.
// vue.config.js
module.exports = {
configureWebpack: {
entry: {
public: "./public/main.js"
},
output: {
filename: "[name]/[name].main.js"
}
}
};
This solution would work with certain features:
at least two entry points: main entry for my SPA (main.js) and additional entry just for my static JS.
It's not good, because processed JS would contain a link to vendors.js chunk as one of the entries. But I need JS file processed by webpack only.
same output.filename and hash in filename with clear config (it's not work, because I use this script as 3rd party JS and load by static name), or different output.filename for my JS file but with dirty config:
configureWebpack: config => {
config.output.filename = (pathData) => {
return pathData.chunk.name === 'myScript'
? '[name].js' : '[name].[hash].js';
};
...
}
If I leave my JS in public folder I get two files after build: one in default js folder with other static assets and another in root folder near main.js
Multi-Page Application (configuration for Vue multi-page mode)
module.exports = {
pages: {
index: {
// entry for the page
entry: 'src/index/main.js',
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// when using the entry-only string format,
// template is inferred to be `public/myScript.html`
// and falls back to `public/index.html` if not found.
// Output filename is inferred to be `myScript.html`.
myScript: 'src/myScript.js'
}
}
This solution would work almost like the first solution and I get a clear config file. But still I have problem with vendors.js seems like pages option work directly with html-webpack-plugin and I can config chunks which would load with my page, and I tried different ways to setup this option but without success. Vendors is still part of myScript entry.
Build JS file as library
I chose this solution in my case. Because it's clear and short.
I put additional script to my package.json: vue-cli-service build --no-clean --target lib --name paysendPaymentLibrary src/payment.js and change main build script.
Final version of package.json:
...
"scripts": {
"build": "vue-cli-service build && npm run build-library",
"build-library": "vue-cli-service build --no-clean --target lib --name myScriptLibrary src/myScript.js"
},
...
After run npm run build I get static files for SPA and three files for my script:
myScriptLibrary.umd.min.js
myScriptLibrary.umd.js
myScriptLibrary.common.js
For 3rd party site I use myScriptLibrary.umd.js file.
If you choose this solution be careful when you build your application, because:
in Windows vue-cli-service build & npm run build-library scripts would run sequentially, but in Unix it runs in parallel. It can cause deletion of your SPA files. So be sure to use && instead of & (see discussions about environments and parallel\sequential script running)
size of processed files would be bigger than raw static JS. For example, in my case raw file size: 4 KiB, after build: 15.44 KiB, gzipped: 5.78 KiB.

Vue CLI deploy to a subfolder using relative paths

Using Vue CLI, and Vue 2.
Anyone knows how to build the project using relative paths, so I can place it in any subfolder in my server and it will work? (for example www.mysite.com/subfolder/)
I've tried with
// vue.config.js
module.exports = {
publicPath: "",
};
And it builds the paths relative (ie, js/app.js instead of /js/app.js), but the app wont' load. Nothing shows on the page.
Strangest thing is that all files are loaded correctly (I can check on network tab in Chrome devtools), no JS errors, etc. So the page is loading all the files but it seems like it's refusing to mount the app when using relative paths.
I know that I can add the absolute path to the build process but that's not what I need. My client needs to be able to move the files freely from one subfolder to another and the app should work without the need to recompile
PS: Also tried building the project with Vite and Vue 3, same problem.
Thanks!
Alright, looks like all that's needed is:
build with a relative publicPath (empty string)
// vue.config.js
module.exports = {
publicPath: "",
};
add a <base> tag to the final HTML...
<base href="/subfolder/" />
For Vite, the export should be
// vite.config.js
export default {
...
base: "",
};

How to uglify a js file upon building the vue project

I have a VUE2 project and in the public folder I created an iframe.html file that will be loaded in an iframe.
That iframe will also load a javascript.js file that I want encoded/uglified upon "npm run build" but I also want to be able to access it during dev.
How could I proceed?
Should this js file be placed inside the /src/assets/ folder and referenced from the iframe.html file? If yes, any advice?
Or should it stay in the public folder and upod the dist folder being built, encode it with something.
Any solution is welcome, thanks in advance!
Edit: Here are further details of how I use the iframe.
First, I'm referencing the .vue file in the router like so:
{
path: "/pages/:id/edit",
name: "edit",
component: () => import("../views/Edit.vue"),
},
Next, in the Edit.vue file, I add the iframe like so (note how it's referencing iframe.html that is in the public directory):
<iframe
id="iframe"
ref="iframe"
src="iframe.html"
/>
Next, in the iframe.html it's just normal html code, with this part including the javascript.js file (that actually is in the public folder as well for now)
<script src="javascript.js"></script>
You can explicitly include the .js file in your Webpack config by adding a rule for UglifyJsPlugin:
npm i -D uglifyjs-webpack-plugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
...
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
include: /\/regex-for-file/,
minimize: true
})
]
}
...
};
In Vue.config.js, this might look like:
configureWebpack: {
plugins : [
new webpack.optimize.UglifyJsPlugin({
uglifyOptions: {
include: /\/regex-for-file/,
minimize: true
}
)}
]
}
Another option is to use uglify-es; this would allow you to get even more explicit by specifying from where to copy the file during build (assuming you might want the file located outside of src/):
npm i -D uglify-es // CopyWebpackPlugin ships w/ Vue's Webpack conf by default
const UglifyJS = require('uglify-es');
const { resolve } = require('path');
const resolveAbs = (dir) => resolve(__dirname, dir);
new CopyWebpackPlugin([
{
from: resolveAbs('../external'),
to: config.build.assetsSubDirectory
},
{
from: resolveAbs('../src/custom-build-path'),
to: config.build.assetsServerDirectory,
transform: (content, path) => UglifyJS.minify(content.toString()).code;
}
]),
To be able to access it during dev, you can include the path of the js file (relative to your Vue src directory) using the resolve.alias option in the config (so you don't need to deal with possibly ridiculous relative paths in your project). Finally, you can look into webpack's HTML plugin docs for info on importing an external index.html file if needed
I would recommend not putting it in static; by default it will not be minified and built if placed in that directory.
Update/edit: Sorry, I saw a 'uglify' and just assumed you wanted uglify js. As long as the script is in your Vue project directory (or otherwise specified in the Webpack config) the file should be minified during build. Vue has pretty smart defaults for Webpack; assuming the iframe is being referenced somewhere in the app i.e. the dependency graph it will be built.

StencilJS: compile scss and copy to dist folder

My goal is to provide a css file in the distribution package which can be used by the consumer if needed.
For that I would like to create a kind of global scss file, but I want to avoid that this style is attached to each component + it also won't be used directly by the components. So for that I would like to create my-style.scss file somewhere in the /src folder.
What would be the best way to compile this file to my-style.css and copy it to the dist folder?
It is possible to specify a global style with Stencil. Simply add the globalStyle property to your stencil.config.js and provide the entry scss file. Your config should look something like this:
const sass = require('#stencil/sass');
exports.config = {
namespace: 'my-component-lib',
outputTargets:[
{
type: 'dist'
},
{
type: 'www',
serviceWorker: false
}
],
globalStyle: 'src/globals/app.scss',
plugins: [
sass()
]
};
exports.devServer = {
root: 'www',
watchGlob: '**/**'
}
The build will successfully compile the scss to dist/my-component-lib.css.
From the docs: https://stenciljs.com/docs/config/#copy
The copy config is an array of objects that defines any files or
folders that should be copied over to the build directory. Each object
in the array must include a src property which can be either an
absolute path, a relative path or a glob pattern. The config can also
provide an optional dest property which can be either an absolute
path or a path relative to the build directory. Also note that any
files within src/assets are automatically copied to www/assets for
convenience.
In the copy config below, it will copy the entire directory from
src/docs-content over to www/docs-content.
Within stencil.config.ts:
copy: [
{ src: 'docs-content' }
]
For example I am copying my css file from src to the build directory and it's totally standalone, e.g. https://github.com/BlazeUI/blaze/blob/master/packages/atoms/stencil.config.ts#L14

Server a static pdf - Vue CLI - Webpack - issue

I build a little front thanks to VueCLI (+ Vuetify).
I wanted to render a button where onClick open a new tab with a pdf located in the folder's tree of the project.
I have an error and after hours looking why, it's seems to be a webpack conf to modify.
I finally read this answer on S/O ; Serving static pdf with react webpack file loader
But I got an error saying include: paths -> paths is not defined
I have to admit that I have no clue how webpack works behind the scene so any help would be find.
You probably don't need Webpack for this: you can just put it in your static folder and link to it pdf. If you want to go the Webpack route, put it in your src/assets directory and import it and add it to data object:
import pdf from '../assets/mypdf.pdf'
//...
data: () => ({ pdf })
//...
Then use the link in your component:
<a :href="pdf">pdf</a>
You probably added include: paths to the loader config but you didn't define it, instead of modifying your image loader, add a new one for PDFs:
{
test: /\.(pdf)(\?.*)?$/,
loader: 'url-loader',
options: {
name: utils.assetsPath('[name].[hash:7].[ext]')
}
}
You can change [name].[hash:7].[ext] if you want, say you want to add a pdfs directory in assets instead of using the base assets directory you would use: pdfs/[name].[hash:7].[ext]