How to uglify a js file upon building the vue project - vue.js

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.

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.js / electron How to list all folder names in public folder

I'm quite new in the Vue / electron...
I need to list all folder names that are in the /public directory.
Inside of these folders may be other subfolders with .pdf files.
So far i try it with:
require.context('#/../public/pdf/moreFolders', true, /^.*\.*$/)
but this seems more for displaying files inside a folder.
In the end, I want to show a menu that has the tree structure of that public folder.
Any idea of how I can display such a structure in Vue?
You can list files and read the content simply by using nodejs modules:
const fs = require('fs')
...
If the build should contain those files you have to add an entry in the vue.config.js
module.exports = {
transpileDependencies: [
'vuetify'
],
pluginOptions: {
electronBuilder: {
builderOptions: {
...
extraFiles: [
{
from: './data',
to: 'data',
filter: [
'**/*'
]
But that is only needed, if you want to use another directory, I would suggest. Public is always copied, as I know.

how to override vue cli-service entry settings

I'm trying to integrate a vue project that I built with the vue cli into an existing .net app. I'm very new to vue, so I'm trying to follow guides and such, but am left with lots of questions.
While trying to compile this, I found that the vue cli-service node module has the following for setting the main.js file located in it's base.js file.
webpackConfig
.mode('development')
.context(api.service.context)
.entry('app')
.add('./src/main.js')
.end()
.output
.path(api.resolve(options.outputDir))
.filename(isLegacyBundle ? '[name]-legacy.js' : '[name].js')
.publicPath(options.publicPath)
I need to override this since my .net app doesn't have a src directory and the usage of this vue app won't follow that path structure. I'm not seeing a way to do it in my vue.config.js file. I would expect that if I can override it, that would be the spot.
I could overwrite the base.js file where this exists, but when a co-worker runs npm install, they would get the default value rather than what I have. The only option I see there is checking in all the node modules to git which we really don't want to do.
For anyone in a similar situation, I found what worked for me. It's not the ideal solution due to the fact that it forces you to build into a js folder. That resulted in the file being put in Scripts\build\vue\js. Would be nice to be able to just dump it in the vue folder, but at least this works. Code below.
vue.config.js
module.exports = {
publicPath : "/",
outputDir: "Scripts/build/vue", //where to put the files
// Modify Webpack config
// https://cli.vuejs.org/config/#chainwebpack
chainWebpack: config => {
// Not naming bundle 'app'
config.entryPoints.delete('app'); //removes what base.js added
},
// Overriding webpack config
configureWebpack: {
// Naming bundle 'bundleName'
entry: {
quote: './Scripts/Quote/index.js' //where to get the main vue app js file
},
optimization: {
splitChunks: false
}
},
filenameHashing: false,
pages: {
quoteApp: { //by using pages, it allowed me to name the output file quoteApp.js
entry: './Scripts/Quote/index.js',
filename: 'index.html'
}
}
}

Vue CLI v3 - How does it know what JS files should be chunked in a different file(s)

I am having hard times wrapping my mind around new CLI and configuration.
In the official documentation, I couldn't really find anything about CSS and how to add it as an entry point and not import it directly into an component or main.js file.
I realized that some JS files are being chunked into separate file, from main.js and the rest gets compiled where supposed to - into the app.js.
I was wondering, how does it know in the background what should be stored as "vendor" for the JS, but when I try to import some "vendor" SASS files into main.js it does not and it merges all within a single app.css file.
Can anyone tell me, how does one create/modify the vue.config.js and tell the bundler that I also want app.scss to be an entry point and vendor.scss to be another entry point.
I am unsure what are best practices for such purpose, but I always did it this way with my own webpack config...
Partial example below:
entry: {
vendor: [
'./styles/vendor.scss',
'./scripts/vendor.js'
],
app: [
'./styles/app.scss',
'./scripts/app.js'
]
}
EDIT #1
I think I got the first one...
"How does it know what should be chunked in "vendor" files?
Whatever gets imported from node_modules, it is being chunked.
What I did not figure out yet is... What if I am having my personal assets/styles/vendor directory where I #import those SASS files from NPM and do some modifications of variables or whatever.
Importing this file to main.js does not get chunked in this case... So there must be a way to tell bundler that I want everything within that directory or everything within vendor.scss file where everything is being imported, to be chunked out.
EDIT #2
I figured I can use WebPack's magical comments to import the main vendor SCSS file, such as:
import(/* webpackChunkName: "vendor" */ './assets/styles/vendor.scss')
I don't have a problem with this, but apparently the bundler does. It generates an empty vendor.[hash].js file as well.
EDIT #3
I did further research and learned that there's a command vue inspect which would output the webpack configuration.
So when making tweaks to vue.config.js, we can look a the output with this command if there's a bug or something is not working as expected.
Further more, I learned that if we specify entry directly in our vue.config.js file, that we will get an error that entry cannot be specified within our configuration file.
The following is forbidden to do so, but it's what I actually want to achieve...
// vue.config.js
module.exports = {
entry: {
app: [
'./src/main.js',
'./src/assets/styles/app.scss'
],
vendor: [
'./src/assets/styles/vendor.scss'
]
}
}
The actual proper way to do this will be an answer to my own question...
The way to achieve this is by using WebPack's Chain API.
However, if I did everything correctly, I still see a problem of generated vendor.[hash].js file with some WebPack module boilerplate. This JS file is also being injected to the index.html template.
Which leads to the same outcome as the attempt of my EDIT #2, except that we're no longer importing our Sass files within main.js
To modify entry points for my purpose of this question, we can do it the following way:
// vue.config.js
module.exports = {
chainWebpack: config => {
config
.entry('app')
.add('./src/assets/styles/app.scss')
.end()
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
}
}
Note
We're not specifying the app entry JS file, which would be main.js by default, because we're not overriding the current entry point. Instead, we're extending it, so everything works as expected.
UPDATE
Until WebPack resolves this in future major releases, I found a great package - fqborges/webpack-fix-style-only-entries. It solves this issue that I was having and I'd suggest you to use it.
Final configuration would look like this:
const FixStyleOnlyEntries = require('webpack-fix-style-only-entries')
module.exports = {
chainWebpack: config => {
config
.entry('app')
.add('./src/assets/styles/app.scss')
.end()
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
},
configureWebpack: {
plugins: [
new FixStyleOnlyEntries()
]
}
}
UPDATE #2
After further investigation and use of such configuration for projects, I realized that I had to use !important in styles where I had a need to override anything vendor related.
This is simply because WebPack will inject app, before vendor (both JS and CSS) and it will cause such issue.
Even if we modify the configuration from above and move app entry, below the vendor entry, it will still fail. Reason being, because we're modifying the entry point which already exists by default within vue-cli config. We're adding more entries to the app and we're adding new vendor entry.
To fix this issue of ordering, we must delete the app entirely and then create it ourselves.
const FixStyleOnlyEntries = require('webpack-fix-style-only-entries')
module.exports = {
chainWebpack: config => {
config.entryPoints.delete('app')
config
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
.entry('app')
.add('./src/main.js')
.add('./src/assets/styles/app.scss')
.end()
},
configureWebpack: {
plugins: [
new FixStyleOnlyEntries()
]
}
}

VueJS 2: Local assets

I made a project with the cli and I have some css and js files in the assets folder that was generated. I am trying to add them to the index.html file but it keeps coming back as a 404. Is there a different way I need to add my local css and javascript files?
Assuming you're using the webpack template, try /src/assets/... in the index.html
In your webpack config file, you can add an alias for your assets folder:
module.exports = {
...
resolve: {
alias: {
'assets': path.resolve(__dirname, '../src/assets'),
},
...
},
...
},
Then, in your application, you can reference this folder like so:
// in a .less file
#import "~assets/css/vars"
// in a .js file
require("assets/js/utils")