Our current build process currently uses Grunt, vueify, and browserify to build our Single File Components and also pull Vue out of the SFC and into its own external file.
For various reasons (vueify no longer supported, async loading components, ...) we want to switch to Webpack.
However, I am failing at wrapping my head on how to make our current method work for Webpack. I've included our current build process below. I would love to figure out how make Webpack work for us. Any suggestions? I can't even seem to get started... How can I make Webpack compile our *.vue.js files into pre-rendered javascript files? At the bottom I've also included the contents of one of our SFC .vue.js files.
vueRuntime: {
expand: true,
cwd: 'node_modules/vue/dist/',
src: 'vue.runtime.min.js',
dest: 'js/rwd/libs',
ext: '.js',
extDot: 'first',
options: {
configure: b => b
.require('vue')
.transform(
// Required in order to process node_modules files
{global: true},
envify({NODE_ENV: 'production'})
)
.bundle(),
browserifyOptions: {
debug: false
}
}
},
vue: {
expand: true,
cwd: 'js/rwd/',
src: '**/*.vue.js',
dest: 'js/rwd',
ext: '.js',
extDot: 'first',
options: {
configure: b => b
.transform('vueify')
.transform(
// Required in order to process node_modules files
{
global: true
},
envify({NODE_ENV: 'production'})
)
.external('vue')
.bundle(),
browserifyOptions: {
debug: false
}
}
}
A sample *.vue.js file:
const Vue = require('vue');
const App = require('./something/components/Something.vue');
new Vue(App).$mount('#app-element-id');
Having done a similar migration to Vue + Webpack recently, I found this blog post extremely helpful: https://itnext.io/vue-js-and-webpack-4-from-scratch-part-3-3f68d2a3c127
Another source of examples is the vue-cli. Unfortunately, the produced boilerplate is extremely hard to decipher because it requires a ton of node modules that all contribute minuscule amounts of config and also depend on other modules. So if you want to build something customized or actually learn how it all works together, it's more trouble than it's worth.
Related
I have a file in my project called test.js
I don't import/require it anywhere which means my webpack won't call babel-loader for that js file.
Question: what I want is to move test.js into /dist folder, but as a compiled/transpiled. What's the best practice for it?
What I tried: I tried to use a copy-webpack-plugin and use its transform parameters before copying the file, but I can't seem to find the good babel package.
{
from: 'test.js',
to: '/dist/test.js',
transform(content, path) {
//what do I write here?
},
}
The simplest approach I could think about is to use several entry points like this:
{
entry: {
a: "./your-main-stuff",
b: "./your-single-file",
},
output: {
path: path.join(__dirname, "dist"),
filename: "[name].js"
}
}
which will create your a.js main bundle and b.js file in __dirname/dist folder both transpiled provided you used corresponding loader(s).
And from copy-webpack-plugin docs section:
webpack-copy-plugin is not designed to copy files generated from the
build process; rather, it is to copy files that already exist in the
source tree, as part of the build process.
so it seems to be difficult (if possible) making it move transpiled files.
Update. If you want to output files into different folders w/o changing your src folder, additonal tools needed. For your case (just 1 file) I would write a simple script and add it into package.json script section combined with webpack call like:
"scripts": {
"dev": "webpack && babel path-to-script.js --out-file path-to-script-compiled.js"
}
Just like in the previous answer, initially I went with the "scripts" entry in package.json that runs babel. But for a number of reasons I wanted to use webpack 5 to do the job. So after failing with webpack-copy-plugin and a good amount of digging around I came to this solution:
let config = {
entry: [
glob.sync(srcDir + '/**/*.js') // get all .js files from the source dir
],
output : {
filename : '[name].rem.js', // webpack wants to bundle - it can bundle here ;)
path: outDir
},
resolve: {
alias: {
'app': appDir
}
},
plugins: [
new RemoveEmptyScriptsPlugin({extensions: ['js'], scriptExtensions: /\.rem\.js/}) // for all .js source files that get bundled remove the bundle .rem.js file
],
module: {
rules:[{
test: /\.jsx?$/,
type: 'asset/resource', // get webpack to take it out instead of bundling
generator: {
filename: ({filename}) => filename // return full file name so directory structure is preserved
},
use: {
loader: 'babel-loader',
options: {
targets: { node: 16 },
presets: [
['#babel/preset-env', { modules: 'commonjs' /* transpile import/export */}],
]
}
}
}]
}
};
// Since the code does not go through the full pipeline and imports are not getting resolved, aliases will remain in the code.
// To resolve them it takes to hack the aliases object into the babel config
config.module.rules[0].use.options.plugins.push(['babel-plugin-webpack-alias-7', {config: {resolve: {alias: config.resolve.alias}}}];
And it does a good job but beware that it takes to use the patched versions of the two plugins (unless the patches have been merged already)!
I'm trying to build my project using Rollup. I got most of it working (including Angular 2), but I'm unable to transform some of my polyfills, since they have calls to require() in them. Even with the rollup-plugin-node-resolve and rollup-plugin-commonjs it won't touch the requires at all.
So how can I transform them? A workaround I could use is to Browserify the polyfills and then include the browserified bundle in the rollup process, but that requires an awfully lot of extra plugins and isn't exactly a clean solution, especially since I thought that Rollup should be able to understand CommonJS.
Here is some test code for Node.js:
"use strict";
let rollup = require("rollup");
let fs = require("fs");
let resolve = require("rollup-plugin-node-resolve");
let commonjs = require("rollup-plugin-commonjs");
rollup.rollup({
entry: "src/main.js",
plugins: [
resolve({
jsnext: true,
main: true
}),
commonjs({
include: '**', // also tried src/** node_modules/** and a lot of other stuff.
// Leaving this undefined stops rollup from working
}),
]
}).then(function(bundle) {
bundle.write({
format: "iife",
dest: "www/bundle.js"
})
});
Then using a simple file as src/main.js that just contains the line require("./some-file.js"); and a src/some-file.js that contains anything demonstrates the problem.
I solved it with the help of the nice guys at rollup-gitter.
I needed to add a moduleName to the bundle.write options like so:
bundle.write({
format: "iife",
dest: "www/bundle.js"
moduleName: "someModule"
})
I've tried a few things but can't come to a good solution.
I'm using grunt to compile my sass and minify my javascript.
Is it possible to autoload every .sass file and every .js file in a specific directory?
I've tried stuff like this
sass: {
options: {
sourceMap: false
},
dist: {
src: 'src/sass/**/*.sass',
dest: 'dist/css/style.css'
}
}
but this will only load the very first sass file.
I'm not sure what concat even does but I tried it aswell and didn't find the solution I was looking for.
Basicly all files in the folder sass/ should be compiled to 1 big style.css file the same with javascript.
Sure I could manually import each file in a main.sass file or so, but I would love a autoload function so I don't get lazy and don't create new files because I would have to add them.
edit:
So with this
files: [{
expand: true,
cwd: "src/sass/",
src: ["**/*.sass"],
dest: "dest/css",
ext: ".css"
}]
I can actually do what I want. The problem is my mixins get loaded too late and it's thorwing an error because it doesn't find the mixin I wanted to include.
This is a format to generate a single output file from multiple source files:
concat: {
whatever: {
options: { separator: '\n' },
src: ['src/sass/**/*.sass'],
dest: 'build/tmp.sass' // make sure the temporary build/ dir exists!
}
}
It only works on tasks supporting combining multiple files; in this case the grunt-contrib-concat task.
It produces dist/css/sass.tmp, which you will need to process with the sass task:
sass: {
dist: {
files: [{
src: 'build/tmp.sass',
dest: 'dist/css/style.css'
}]
}
}
You would make sure they are run in sequence with something like this:
grunt.registerTask( 'default', ['concat', 'sass'] ); // add your uglify/cssmin here
However, I don't recommend this, because the order of the sass files is uncontrollable, and it won't make mixin's available, even if they are compiled to css first - which is pointless, because they loose their 'mixin' format. It's best to run sass on a single source file and import your other files in that source file.
Update
Regarding auto-loading of mixin files,
the SASS Reference does mention you can use custom importers (written in Ruby) that take care of #import; I'm not sure whether this is only for top-level importing of files, or also for #import mixin-name(foo) within rules, but I don't think so.
There is an alternative approach you could use, based on concat, assuming that you have one main sass file. You would need to add a single #import 'all-mixins' to it. This all-mixins.sass is a generated file:
concat: {
all_mixins: {
options: { separator: '\n' },
src: ['src/sass/mixins/*.sass'],
dest: 'build/all-mixins.sass'
}
}
And you would specify the sass option loadPath to add build/ to the path:
sass: {
dist: {
options: {
loadPath: 'build/'
},
files: [{
src: 'build/tmp.sass',
dest: 'dist/css/style.css'
}]
}
}
This is as close to auto-loading as it gets without extending sass itself.
I have a working Gruntfile with less and autoprefixer. I also have 'grunt watch' working fine.
Before I was using autoprefixer, I was using less mixins for vendor prefixes. Running 'grunt less' would build working CSS with all my prefixes.
Now I have autoprefixer, but if I want to do a once-off build of my styles, I now have to run 'grunt less' then 'grunt autoprefixer' to get working CSS with prefixes.
How can I modify 'grunt less' so it build working, prefixes less again?
I've read the docs, and I know I could add an additional task to do both these things. However:
'grunt less' now doesn't have usable output. A task should always produce usable output.
I don't want to have to tell other people that 'grunt less' doesn't produce usable output
An additional task should not be required to replace one that doesn't work
I.e., I just want grunt less to produce working CSS (with prefixes).
module.exports = function(grunt) {
// Load Grunt tasks declared in the package.json file
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
// Configure Grunt
grunt.initConfig({
less: {
development: {
options: {
paths: ["./less"],
yuicompress: false
},
files: {
"./public/css/style.css": "./public/less/style.less"
}
}
},
autoprefixer: {
development: {
browsers: ['last 2 version', 'ie 9'],
expand: true,
flatten: true,
src: 'public/css/*.css',
dest: 'public/css'
}
},
watch: {
less: {
files: ["./public/less/*"],
tasks: ["less", "autoprefixer:development"],
options: {
livereload: true
}
}
},
});
};
For using autoprefixer as plugin for LESS, you must install npm-package less-plugin-autoprefix:
npm install grunt-contrib-less less-plugin-autoprefix --save-dev
Gruntfile.js
module.exports = function(grunt) {
"use strict";
var gruntConfig = {
less : {
options : {
plugins : [ new (require('less-plugin-autoprefix'))({browsers : [ "last 2 versions" ]}) ]
},
main : {
files: {
'src/css/desktop/bootstrap-theme.css' : 'src/less/desktop/bootstrap-theme.less',
'src/css/desktop/company.css' : 'src/less/desktop/company.less',
'src/css/desktop/index.css' : 'src/less/desktop/index.less',
'src/css/desktop/login.css' : 'src/less/desktop/login.less'
}
}
}
};
grunt.initConfig(gruntConfig);
grunt.loadNpmTasks('grunt-contrib-less');
grunt.registerTask('default', [ 'less' ]);
};
Grunt can't do what you describe in the question as tasks do not know about each other inherently. You have to glue tasks together using aliases, (easy) or functions (for when you want a bit more control), but there's no way of modifying the way one of these tasks behaves without changing the source.
As an example, you could fork grunt-contrib-less and add the code to run the auto prefixing directly into the task, around here: https://github.com/gruntjs/grunt-contrib-less/blob/master/tasks/less.js#L69 - inject this line here https://github.com/nDmitry/grunt-autoprefixer/blob/master/tasks/autoprefixer.js#L56 and then use your fork instead of the official plugin.
The easiest and best way is to register a new task that does the job of these two tasks but in the one command, i.e:
grunt.registerTask('buildless', ['less', 'autoprefixer']);
I do this with all my own tasks - SASS compilation, JS concat + uglify, webfont generation etc. Just tell the others in your team to run those tasks and not grunt less or grunt autoprefixer on their own.
Better still, use my Grunt plugin available tasks. With this (and the filter config) you will be able to produce a trimmed down list of tasks whenever someone runs grunt availabletasks although I prefer to alias this with tasks for quicker typing. If you're like me and have been bitten by the automation bug (and have registered loads of plugins in your Gruntfile), this can really help a newcomer to the project with which tasks should be run.
If I use RequireJS to optimize my whole project my main module will not get optimized/uglified if I use the setting skipDirOptimize: true. From my understanding everything should be optimized except the non-build layer JS files. Is this a bug or me not understanding the correct usage of this parameter?
Here is my requirejs config:
{
appDir: '../project',
mainConfigFile: '../project/assets/js/main.js',
dir: '../httpdocs',
optimize: "uglify",
//Introduced in 2.1.2: If using "dir" for an output directory, normally the
//optimize setting is used to optimize the build layers (the "modules"
//section of the config) and any other JS file in the directory. However, if
//the non-build layer JS files will not be loaded after a build, you can
//skip the optimization of those files, to speed up builds. Set this value
//to true if you want to skip optimizing those other non-build layer JS
//files.
skipDirOptimize: true,
generateSourceMaps: false,
normalizeDirDefines: "skip",
uglify: {
toplevel: true,
ascii_only: true,
beautify: false,
max_line_length: 1000,
defines: {
DEBUG: ['name', 'false']
},
no_mangle: false
},
optimizeCss: "standard",
removeCombined: true,
modules: [
{
name: '../main'
}
]
}
The use of the relative path in your module is probably causing r.js to not recognise it as a build bundle at the point where it decides whether or not to optimize it.
I had a similar problem (build bundles not being optimized), not with a relative module path but with a paths config to allow my modules to be named differently to my folder structure:
({
...
skipDirOptimize: true,
paths: {
'MyLibrary': ''
},
modules: [
{ name: 'MyLibrary/Main' }
],
...
})
This causes the module name in r.js (2.1.8) to become /Main, so when it builds its _buildPathToModuleIndex mapping, the key will be incorrect due to having two slashes (e.g. C:\dev\project\output\\Main).
The way that the optimization loop decides if a module is a build bundle (and hence needs optimization even when skipDirOptimize: true) is by looking it up in the _buildPathToModuleIndex mapping using its filename (e.g. C:\dev\project\output\Main). Due to it being in the map with two slashes, it won't find it. Therefore it won't be considered to be a build bundle and won't be optimized.
Try putting some console.logs in r.js where it builds and accesses _buildPathToModuleIndex to see what it's putting in and what it uses to look it up.
For my problem, the solution was to add a paths entry for 'MyLibrary/Main': 'Main' (repetition unfortunately). I'm not sure what your project structure is, but how about if you set baseUrl: '../ and then simply call your module main ?