browserify for mixed-style project (grunt, reactjs + plain js) - javascript

What is the best way to configure browserify plugin in grunt for a project where reactjs is used along with the plain javascript?
I.e. assuming that I want to use require(...) in every js file in my project and directory structure looks as follows:
/js/model/foo.js (plain javascript)
/js/model/... (plain javascript)
/js/services/foo-service.js (plain javascript)
/js/view/foo-item-view.jsx (reactjs)
/js/view/... (reactjs)
/js/main.js (plain javascript, entry point)
etc.
I want to apply reactjs transform for all the files within /js/view/... dir and then transform everything with browserify. I found no clean way to do that.
Is it possible at all?
UPDATE:
All right, I think my question was a little bit unclear. I understand that this is definitely doable and here is how I'm doing it right now (it looks like a hack to me):
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: {
scripts: {
files: ['web/js/**/*.js', 'web/html/index.html'],
tasks: ['copy', 'react', 'browserify']
}
},
copy: {
main: {
files: [
{
src: 'web/html/index.html',
dest: 'target/web/index.html'
},
{
cwd: 'web/js', // source js dir
src: ['**', '!**/*.jsx'], // copy all files and subfolders to the temporary dor, except for jsx
dest: 'target/web/tmp/js', // destination folder, used by browserify
expand: true // required when using cwd
}
]
}
},
react: {
dynamic_mappings: {
files: [
{
expand: true,
cwd: 'web/js/view',
src: ['**/*.jsx'],
dest: 'target/web/tmp/js/view',
ext: '.js'
}
]
}
},
browserify: {
dist: {
files: {
'target/web/js/app.js': ['target/web/tmp/js/main.js'],
}
}
}
});
This doesn't look elegant to me, because of extra copy instead of 'reactifying' sources in browserify task. May be I'm missing something and this solution can be simplified?

You can just run the jsx transform over everything. It won't do anything at all to regular JavaScript code.
Anything beyond that is an optimization, but just use watchify (or the watchify grunt plugin) and you won't notice.
In response to the comment and edit... do you really need grunt here?
npm i -D browserify watchify reactify
And then in your package.json:
"scripts": {
"build": "browserify src/main.js -o dist/bundle.js",
"watch": "watchify src/main.js -o dist/bundle.js"
},
"browserify": {
"transform": ["reactify"]
}
You can also just have everything be .js, it's much simpler imo. If you need to some other things like css preprocessors, image compression, sprite sheets, running tests, etc. you should use a tool like grunt/gulp for that.
For browserify, I find it's much easier to just use watchify on its own.

Related

Public file is missing from build/public in Webpack [duplicate]

I'm trying to move from Gulp to Webpack. In Gulp I have task which copies all files and folders from /static/ folder to /build/ folder. How to do the same with Webpack? Do I need some plugin?
Requiring assets using the file-loader module is the way webpack is intended to be used (source). However, if you need greater flexibility or want a cleaner interface, you can also copy static files directly using my copy-webpack-plugin (npm, Github). For your static to build example:
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
context: path.join(__dirname, 'your-app'),
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: 'static' }
]
})
]
};
Compatibility note: If you're using an old version of webpack like webpack#4.x.x, use copy-webpack-plugin#6.x.x. Otherwise use latest.
You don't need to copy things around, webpack works different than gulp. Webpack is a module bundler and everything you reference in your files will be included. You just need to specify a loader for that.
So if you write:
var myImage = require("./static/myImage.jpg");
Webpack will first try to parse the referenced file as JavaScript (because that's the default). Of course, that will fail. That's why you need to specify a loader for that file type. The file- or url-loader for instance take the referenced file, put it into webpack's output folder (which should be build in your case) and return the hashed url for that file.
var myImage = require("./static/myImage.jpg");
console.log(myImage); // '/build/12as7f9asfasgasg.jpg'
Usually loaders are applied via the webpack config:
// webpack.config.js
module.exports = {
...
module: {
loaders: [
{ test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/, loader: "file" }
]
}
};
Of course you need to install the file-loader first to make this work.
If you want to copy your static files you can use the file-loader in this way :
for html files :
in webpack.config.js :
module.exports = {
...
module: {
loaders: [
{ test: /\.(html)$/,
loader: "file?name=[path][name].[ext]&context=./app/static"
}
]
}
};
in your js file :
require.context("./static/", true, /^\.\/.*\.html/);
./static/ is relative to where your js file is.
You can do the same with images or whatever.
The context is a powerful method to explore !!
One advantage that the aforementioned copy-webpack-plugin brings that hasn't been explained before is that all the other methods mentioned here still bundle the resources into your bundle files (and require you to "require" or "import" them somewhere). If I just want to move some images around or some template partials, I don't want to clutter up my javascript bundle file with useless references to them, I just want the files emitted in the right place. I haven't found any other way to do this in webpack. Admittedly it's not what webpack originally was designed for, but it's definitely a current use case.
(#BreakDS I hope this answers your question - it's only a benefit if you want it)
Webpack 5 adds Asset Modules which are essentially replacements for common file loaders. I've copied a relevant portion of the documentation below:
asset/resource emits a separate file and exports the URL. Previously achievable by using file-loader.
asset/inline exports a data URI of the asset. Previously achievable by using url-loader.
asset/source exports the source code of the asset. Previously achievable by using raw-loader.
asset automatically chooses between exporting a data URI and emitting a separate file. Previously achievable by using url-loader with asset size limit.
To add one in you can make your config look like so:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/,
type: "asset/resource"
}
]
}
};
To control how the files get output, you can use templated paths.
In the config you can set the global template here:
// webpack.config.js
module.exports = {
...
output: {
...
assetModuleFilename: '[path][name].[hash][ext][query]'
}
}
To override for a specific set of assets, you can do this:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/,
type: "asset/resource"
generator: {
filename: '[path][name].[hash][ext][query]'
}
}
]
}
};
The provided templating will result in filenames that look like build/images/img.151cfcfa1bd74779aadb.png. The hash can be useful for cache busting etc. You should modify to your needs.
Above suggestions are good. But to try to answer your question directly I'd suggest using cpy-cli in a script defined in your package.json.
This example expects node to somewhere on your path. Install cpy-cli as a development dependency:
npm install --save-dev cpy-cli
Then create a couple of nodejs files. One to do the copy and the other to display a checkmark and message.
copy.js
#!/usr/bin/env node
var shelljs = require('shelljs');
var addCheckMark = require('./helpers/checkmark');
var path = require('path');
var cpy = path.join(__dirname, '../node_modules/cpy-cli/cli.js');
shelljs.exec(cpy + ' /static/* /build/', addCheckMark.bind(null, callback));
function callback() {
process.stdout.write(' Copied /static/* to the /build/ directory\n\n');
}
checkmark.js
var chalk = require('chalk');
/**
* Adds mark check symbol
*/
function addCheckMark(callback) {
process.stdout.write(chalk.green(' ✓'));
callback();
}
module.exports = addCheckMark;
Add the script in package.json. Assuming scripts are in <project-root>/scripts/
...
"scripts": {
"copy": "node scripts/copy.js",
...
To run the sript:
npm run copy
The way I load static images and fonts:
module: {
rules: [
....
{
test: /\.(jpe?g|png|gif|svg)$/i,
/* Exclude fonts while working with images, e.g. .svg can be both image or font. */
exclude: path.resolve(__dirname, '../src/assets/fonts'),
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}]
},
{
test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=\d+\.\d+\.\d+)?$/,
/* Exclude images while working with fonts, e.g. .svg can be both image or font. */
exclude: path.resolve(__dirname, '../src/assets/images'),
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
},
}
]
}
Don't forget to install file-loader to have that working.
You can write bash in your package.json:
# package.json
{
"name": ...,
"version": ...,
"scripts": {
"build": "NODE_ENV=production npm run webpack && cp -v <this> <that> && echo ok",
...
}
}
Most likely you should use CopyWebpackPlugin which was mentioned in kevlened answer. Alternativly for some kind of files like .html or .json you can also use raw-loader or json-loader. Install it via npm install -D raw-loader and then what you only need to do is to add another loader to our webpack.config.js file.
Like:
{
test: /\.html/,
loader: 'raw'
}
Note: Restart the webpack-dev-server for any config changes to take effect.
And now you can require html files using relative paths, this makes it much easier to move folders around.
template: require('./nav.html')
I was stuck here too. copy-webpack-plugin worked for me.
However, 'copy-webpack-plugin' was not necessary in my case (i learned later).
webpack ignores root paths
example
<img src="/images/logo.png'>
Hence, to make this work without using 'copy-webpack-plugin'
use '~' in paths
<img src="~images/logo.png'>
'~' tells webpack to consider 'images' as a module
note:
you might have to add the parent directory of images directory in
resolve: {
modules: [
'parent-directory of images',
'node_modules'
]
}
Visit https://vuejs-templates.github.io/webpack/static.html
The webpack config file (in webpack 2) allows you to export a promise chain, so long as the last step returns a webpack config object. See promise configuration docs. From there:
webpack now supports returning a Promise from the configuration file. This allows to do async processing in you configuration file.
You could create a simple recursive copy function that copies your file, and only after that triggers webpack. E.g.:
module.exports = function(){
return copyTheFiles( inpath, outpath).then( result => {
return { entry: "..." } // Etc etc
} )
}
lets say all your static assets are in a folder "static" at the root level and you want copy them to the build folder maintaining the structure of subfolder, then
in your entry file) just put
//index.js or index.jsx
require.context("!!file?name=[path][name].[ext]&context=./static!../static/", true, /^\.\/.*\.*/);
In my case I used webpack for a wordpress plugin to compress js files, where the plugin files are already compressed and need to skip from the process.
optimization: {
minimize: false,
},
externals: {
"jquery": "jQuery",
},
entry: glob.sync('./js/plugin/**.js').reduce(function (obj, el) {
obj[path.parse(el).name] = el;
return obj
}, {}),
output: {
path: path.resolve(__dirname, './js/dist/plugin'),
filename: "[name].js",
clean: true,
},
That used to copy the js file as it is to the build folder. Using any other methods like file-loader and copy-webpack create issues with that.
Hope it will help someone.

Configuration of stylelint in Gruntfile

I'm trying to create a separate task called stylelint because for reasons I do not want it to be part of the postcss task.
In the gruntfile I'm writing:
stylelint: {
options: {},
src: './assets/css/precss/**'
}
When I run grunt stylelint it lints my code but when it finds an issue then I get Warning: Task "stylelint:src" failed. Use --force to continue.
Am I omitting something?
You should use grunt-stylelint rather than PostCSS as it supports the native stylelint format reporters. Your Gruntfile config should then look something like this:
stylelint: {
css: {
options: {
configFile: '.stylelintrc',
format: 'css'
},
src: [ 'assets/*.css' ]
},
},
I just realised that this is not possible. You have to run grunt with --force

Typescript: Compile files without mirroring the directory hierarchy

I am using VS Code for making an HTML5 game with TypeScript (JS). The project is getting a little bigger and I want to store the output in a different directory.
The problem is that whenever I compile everything, it mirrors the original directory hierarchy. So for example:
-dir1
--dir2
--dir3
---dir4
outputs:
-dir1
--dir2
--dir3
---dir4
(the same)
and I want:
-dir1
*.js
I've tried Grunt/Gulp/VSCode's own TaskRunner but nothing works and "keepDirectoryHierarchy" seems depricated..
VS Code support two way of typescript compilation:
Native compilation using tsconfig
Using JavaScript Task Runner such as Gulp or Grunt
Native compilation using tsconfig
Create file tsconfig.json in root dir
Put next configuration in it
{
"version": "1.6.0-beta",
"compilerOptions": {
"target": "es5",
"declaration": true,
"noImplicitAny": false,
"removeComments": true,
"noLib": false,
"emitDecoratorMetadata": true,
"sourceMap": true,
"listFiles": true,
"outDir": "",
"out": "./Compiled/mycompiled.js", // here specify your output file( it would be contain all your compiled ts file in one)
"experimentalDecorators": true
},
"files": [ // file list (optional)
"somefile.ts"
]
}
Configure VS Code task runner
Using JavaScript Task Runner such as Gulp or Grunt
Current example show how you should modify your gulpfile.js to compile your project using gulp-typescript
gulp.task('build', function () {
var tsResult = gulp.src('src/**/*.ts') // here specify your file location or folders
.pipe(ts({ // gulp-typescript configuration
noImplicitAny: true,
out: 'output.js'// here specify your output file( it would be contain all your compiled ts file in one)
}));
return
tsResult.js
.pipe(gulp.dest('./')); // here you can specify your output directory too
});
Problem Solution
For your case you can choose both solutions. Pay attention for code comments and specify out directories and name of compiled js file as you wish.
Good Luck!
Resources
Gulp Typescript NPM.
Using TypeScript in Visual Studio Code (MSDN Blog).
Typescript tsconfig.json specification
Using Task Runner in VS Code
I've figured it out. I made a custom Grunt task which is not optimal but does the job.
module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-typescript");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks('grunt-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.initConfig({
typescript: {
base: {
src: ['./client/**/*.ts'],
dest: './temp',
options: {
'module': 'commonjs',
target: 'es5',
sourceMap: true
}
}
},
copy: {
main: {
files: [
{
src: ['./temp/**/*.js', './temp/**/*.js.map'],
dest: './build/',
flatten: true,
expand: true
}
]
}
},
clean: [
'./temp'
],
watch: {
scripts: {
files: ['./client/**/*.ts'],
tasks: ['typescript', 'copy', 'clean']
}
}
});
grunt.registerTask("default", ['typescript', 'copy', 'clean', 'watch']);
};
I think you need to give Gulp or another task runner a look. You will need a few steps to achieve what you are looking for.
Compile Typescript
Concatenate the files
Cleanup the extra files
I use a similar system with CoffeeScript and it works well.
Gulp should work. You can use the flatten plugin:
I would use gulp-flatten:
var flatten = require('gulp-flatten');
gulp.task('compile', function() {
gulp.src('src/**/*.ts')
.pipe(tsc()) //compile them
.pipe(flatten()) //change their relative path to point to one dir
.pipe(gulp.dest('build')); //write them in destination
});
I am using rollup with the typescript plug-in to do this type of work.
npm i rollup
npm i #rollup/plugin-typescript
Then follow the documnets in these respective libraries.

Generating source maps from browserify using grunt

I have followed the instructions here: https://www.npmjs.org/package/grunt-browserify, to try and set up source maps for browserify on grunt. The options for browserify in my gruntfile are :
browserify: {
options: {
bundleOptions : {
debug: true
}
},
dist: {
files: {
"public/client.bundle.js": ["bundle.js"]
}
}
}
The generation of bundle.js happens without any issues, however the source map generation does not happen. Is there anything wrong with my grunt-browserify options.
Thanks for looking.
use browserifyOptions instead of bundleOptions
browserify: {
options: {
browserifyOptions: {
debug: true
}
},
...
}
By default, browserify generates inline source maps as a comment in bundle.js. Browserify's README suggests using exorcist if you want to extract them to a separate file:
$ browserify main.js --debug | exorcist bundle.js.map > bundle.js
And grunt-extract-sourcemap rolls this up in a grunt task

How to concatenate and minify multiple CSS and JavaScript files with Grunt.js (0.3.x)

Note: This question is only relevant for Grunt 0.3.x and has been left for reference. For help with the latest Grunt 1.x release please see my comment below this question.
I'm currently trying to use Grunt.js to setup an automatic build process for first concatenating and then minifying CSS and JavaScript files.
I have been able to successfully concatenate and minify my JavaScript files, although each time I run grunt it seems to just append to the file instead of overwriting them.
As for the minifying or even concatenating CSS, I have been unable to do this as of yet!
In terms of grunt CSS modules I have tried using consolidate-css, grunt-css & cssmin but to no avail. Could not get my head around how to use them!
My directory structure is as follows (being a typical node.js application):
app.js
grunt.js
/public/index.html
/public/css/[various css files]
/public/js/[various javascript files]
Here is what my grunt.js file currently looks like in the root folder of my application:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
concat: {
dist: {
src: 'public/js/*.js',
dest: 'public/js/concat.js'
}
},
min: {
dist: {
src: 'public/js/concat.js',
dest: 'public/js/concat.min.js'
}
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
boss: true,
eqnull: true,
node: true
},
globals: {
exports: true,
module: false
}
},
uglify: {}
});
// Default task.
grunt.registerTask('default', 'concat min');
};
So just to summarise I need help with two questions:
How to concatenate and minify all my css files under the folder /public/css/ into one file, say main.min.css
Why does grunt.js keep on appending to the concatenated and minified javascript files concat.js and concat.min.js under /public/js/ instead of overwriting them each time the command grunt is run?
Updated 5th of July 2016 - Upgrading from Grunt 0.3.x to Grunt 0.4.x or 1.x
Grunt.js has moved to a new structure in Grunt 0.4.x (the file is now called Gruntfile.js). Please see my open source project Grunt.js Skeleton for help with setting up a build process for Grunt 1.x.
Moving from Grunt 0.4.x to Grunt 1.x should not introduce many major changes.
concat.js is being included in the concat task's source files public/js/*.js. You could have a task that removes concat.js (if the file exists) before concatenating again, pass an array to explicitly define which files you want to concatenate and their order, or change the structure of your project.
If doing the latter, you could put all your sources under ./src and your built files under ./dest
src
├── css
│   ├── 1.css
│   ├── 2.css
│   └── 3.css
└── js
├── 1.js
├── 2.js
└── 3.js
Then set up your concat task
concat: {
js: {
src: 'src/js/*.js',
dest: 'dest/js/concat.js'
},
css: {
src: 'src/css/*.css',
dest: 'dest/css/concat.css'
}
},
Your min task
min: {
js: {
src: 'dest/js/concat.js',
dest: 'dest/js/concat.min.js'
}
},
The build-in min task uses UglifyJS, so you need a replacement. I found grunt-css to be pretty good. After installing it, load it into your grunt file
grunt.loadNpmTasks('grunt-css');
And then set it up
cssmin: {
css:{
src: 'dest/css/concat.css',
dest: 'dest/css/concat.min.css'
}
}
Notice that the usage is similar to the built-in min.
Change your default task to
grunt.registerTask('default', 'concat min cssmin');
Now, running grunt will produce the results you want.
dest
├── css
│   ├── concat.css
│   └── concat.min.css
└── js
├── concat.js
└── concat.min.js
I want to mention here a very, VERY, interesting technique that is being used in huge projects like jQuery and Modernizr for concatenate things.
Both of this projects are entirely developed with requirejs modules (you can see that in their github repos) and then they use the requirejs optimizer as a very smart concatenator. The interesting thing is that, as you can see, nor jQuery neither Modernizr needs on requirejs to work, and this happen because they erase the requirejs syntatic ritual in order to get rid of requirejs in their code. So they end up with a standalone library that was developed with requirejs modules! Thanks to this they are able to perform cutsom builds of their libraries, among other advantages.
For all those interested in concatenation with the requirejs optimizer, check out this post
Also there is a small tool that abstracts all the boilerplate of the process: AlbanilJS
I agree with above answer. But here is another way of CSS compression.
You can concat your CSS by using YUI compressor:
module.exports = function(grunt) {
var exec = require('child_process').exec;
grunt.registerTask('cssmin', function() {
var cmd = 'java -jar -Xss2048k '
+ __dirname + '/../yuicompressor-2.4.7.jar --type css '
+ grunt.template.process('/css/style.css') + ' -o '
+ grunt.template.process('/css/style.min.css')
exec(cmd, function(err, stdout, stderr) {
if(err) throw err;
});
});
};
You don't need to add the concat package, you can do this via cssmin like this:
cssmin : {
options: {
keepSpecialComments: 0
},
minify : {
expand : true,
cwd : '/library/css',
src : ['*.css', '!*.min.css'],
dest : '/library/css',
ext : '.min.css'
},
combine : {
files: {
'/library/css/app.combined.min.css': ['/library/css/main.min.css', '/library/css/font-awesome.min.css']
}
}
}
And for js, use uglify like this:
uglify: {
my_target: {
files: {
'/library/js/app.combined.min.js' : ['/app.js', '/controllers/*.js']
}
}
}
I think may be more automatic, grunt task usemin take care to do all this jobs for you, only need some configuration:
https://stackoverflow.com/a/33481683/1897196

Categories

Resources