I'm quite a beginner with Browserify. I tried to integrate it into gulp.js with Watchify (for performance reasons) and Coffeeify. I tried at least five more or less different approaches I found in Google, the last of them being an official recipe from the gulp docs.
Problem now is the transforming part. I want to use Coffeeify to parse my code which is mostly written in CoffeeScript.
Coffeeify successfully parses my entry file app.coffee but when I require a ./foo.coffee from there, that file seems not to be transformed which will naturally result in a parsing error by Browserify about unexpected tokens etc.
Anyone got an idea how to fix that?
Here's the relevant part of my gulpfile, mostly the same as in the link above, just with added transformations.
var gulp = require( 'gulp' );
var gutil = require( 'gulp-util' );
var browserify = require( 'browserify' );
var watchify = require( 'watchify' );
var coffeeify = require( 'coffeeify' );
var source = require( 'vinyl-source-stream' );
var b = watchify( browserify({
entries: [ './coffee/app.coffee' ],
extensions: [ '.coffee' ],
debug: true,
cache: false,
packageCache: false
}) );
b.transform( coffeeify ); // as described in the gulp docs
gulp.task( 'bundle', bundle );
b.on( 'update', bundle );
b.on( 'log', gutil.log );
function bundle() {
return b.bundle()
.on( 'error', gutil.log.bind( gutil, 'Browserify Error' ) )
.pipe( source( 'bundle.js' ) )
// I actually tried to pipe in coffeeify here (gulp-coffeeify as well), didn't help
.pipe( gulp.dest( './js' ) );
};
Okay, this was totally my bad. I abstracted my code a bit too far for this post so the actual problem wasn't reflected here:
app.coffee does not actually require ./foo.coffee directly but requires foo/foo from a node_modules directory, just to make things easier.
Which was exactly the problem: Transforms will not apply to required packages. This is intended behavior.
There seem to be two ways around that:
Solution 1: A browserify option called global-transform.
In the API it's used like b.transform( coffeeify, { global: true } ). This will apply the coffeeify transform to every required module.
Solution 2: Package-wise definition of transforms. That means: Adding something like this to each modules' package.json:
{
"browserify": {
"transform": ["coffeeify"]
}
}
I decided to use the first one because my "packages" actually don't even have a package.json file, they're just in the node_modules directory to be easily accessible by browserify.
Related
I want to use libquassel (https://github.com/magne4000/node-libquassel) in my Angular 2 project. The library is browserified, so in theory it should work, but I'm not really sure how to import it in my project.
I tried adding to my typings.d.ts
declare module 'libquassel';
and then importing the library with
import * as Quassel from 'libquassel';
but I get
EXCEPTION: net.Socket is not a function
when I try to run my code, which I believe is another library that browserify embedded in the client/libquassel.js file.
How can I use this library?
Edit: I'll answer all questions here:
My setup is a plain angular-cli project. No fancy stuff, just ng new proj1 and then npm install libquassel --save.
My index.html doesn't have anything else that ng new hasn't placed in there.
I tried importing the library with import * as Quassel from 'libquassel' and var Quassel = require('quassel') (and permutations of those), without any luck (errors varying from unknown function 'require' to can't find module lib|quassel).
Steps to repro my project:
ng new test
cd test
npm install libquassel --save
ng g s quassel
Then add QuasselService to the providers array in app.module.ts.
This would be a good demo of my problem, which is how to import libquassel in my QuasselService.
Update (fix it, this time for real)
There is a very good reason why it doesn't work in your code: because I lied to you. So here is my real cheat in case you didn't "diffed" it yourself yet.
I still need to require libquassel 2 times but the first time is done not by the call to require which is useless but by adding it to angular-cli.json to the scripts section:
"scripts": [
"../node_modules/libquassel/client/libquassel.js"
],
This is important because "client/libquassel.js" declares its own require but do not explicitly exports it so if you do
require('libquassel/client/libquassel.js');
libquassel's custom require is left stuck inside anonymous namespace and you can't access it. Adding it to the scripts section on the other hand, lets libquassel.js to pollute the global namespace (i.e. window) with its require and thus make it work.
So the actual steps to make it work are:
Add client/libquassel.js to the scripts section of angular-cli.json
Use additional require to actually load Quassel module
let Quassel = window['require']('quassel'); // note that usingg require('quassel') leads to compilation error
let quasselObj = new Quassel(...);
Here is my attempt. It looks like you need to require "quassel" 2 times: first time to load the JS from "../node_modules/libquassel/client/libquassel.js" and second time to let the overridden require from that JS actually resolve "quassel". Here is a trick that seems to work for me in 'main.ts':
require('../node_modules/libquassel/client/libquassel.js');
let Quassel = window['require']('quassel'); // note that using require('quassel') leads to compilation error
let quasselObj = new Quassel(...);
I needed one more trick to not fail with a compilation error: particularly I had to use window['require'] instead of just require for the second call as it is a call for the inner require as defined inside client/libquassel.js and Node/Angular-cli alone can't handle it.
Note that after that setup my application still failed with a runtime error in some AJAX because browsified libquassel.js seem to require a proxy set up on the server-side at URL like /api/vm/net/connect and this is probably what server-side of the net-browsify should do but I didn't try to set it up.
Answer to comments
It looks like you use old version of Angular-CLI and if you upgrade everything should work fine.
Here is what ng --version tells me on my original machine
angular-cli: 1.0.0-beta.28.3
node: 6.10.0
os: win32 x64
#angular/common: 2.4.10
#angular/compiler: 2.4.10
#angular/core: 2.4.10
#angular/forms: 2.4.10
#angular/http: 2.4.10
#angular/platform-browser: 2.4.10
#angular/platform-browser-dynamic: 2.4.10
#angular/router: 3.4.10
#angular/compiler-cli: 2.4.10
When I tried to copy my original project to a different machine I also got a compilation error about require but when I updated Angular-CLI to
#angular/cli: 1.0.0
node: 6.10.0
os: darwin x64
#angular/common: 2.4.10
#angular/compiler: 2.4.10
#angular/core: 2.4.10
#angular/forms: 2.4.10
#angular/http: 2.4.10
#angular/platform-browser: 2.4.10
#angular/platform-browser-dynamic: 2.4.10
#angular/router: 3.4.10
#angular/cli: 1.0.0
#angular/compiler-cli: 2.4.10
it compiled and worked the same. What I did is just followed the instruction
The package "angular-cli" has been deprecated and renamed to "#angular/cli".
Please take the following steps to avoid issues:
"npm uninstall --save-dev angular-cli"
"npm install --save-dev #angular/cli#latest"
and then updated angular-cli.json following the instructions by ng serve
Works like a charm :
declare var require;
const Quassel = require( 'libquassel/lib/libquassel.js' );
console.log( 'Quassel', Quassel );
Looking at the libquassel's folder in node_module, realised that there is no index.js in the root directory.
When there is not index.js , you can't do this :
require( 'libquassel' );
Simply because node doesn't know what to pull in for you, so you'd need to specify the exact path, which is not that bad in this case.
Also , note that it's better to move declare var require; to your typings file located under the src folder, because you might need to declare it again , so it's better be there.
EDIT :
Here is what I found after trying to instantiate the Quassel like bellow :
const Quassel = require( 'libquassel/lib/libquassel.js' );
console.log( 'Quassel', Quassel );
var quassel = new Quassel( "quassel.domain.tld",
4242,
{ backloglimit : 10 },
function ( next ) {
next( "user", "password" );
} );
console.log( 'quassel', quassel );
And here is my console log :
But having said that, I realised that there is a problem inside the libquassel.js , as bellow :
in line 10, they're doing this :
var net = require('net');
And looking their package.json, there is no such a thing as net and there is net-browserify-alt;
So if they change that import to :
var net = require('net-browserify-alt'),
Everything will work.
Having said that, obviously you don't want to edit your node_module, but I'm really surprised of how this really works even outside of angular and webpack , because clearly they've mentioned a wrong node_module which if you google , there is only one package named net which I had a look and it's empty and dummy !!!!
** ********** UPDATE : ********** **
What exactly needs to be done :
1- run ng eject
that will generate a webpack.conf.js inside your root directory.
2- inside that find resolve property and add :
"alias":{
'net':'net-browserify-alt'
},
so your resolve will probably look like :
"resolve": {
"extensions": [
".ts",
".js"
],
"alias":{
'net':'net-browserify-alt'
},
"modules": [
"./node_modules"
]
},
3- import and use it :
declare var require;
const Quassel = require( 'libquassel/lib/libquassel.js' );
console.log( 'Quassel', Quassel );
var quassel = new Quassel( "quassel.domain.tld",
4242,
{ backloglimit : 10 },
function ( next ) {
next( "user", "password" );
} );
console.log( 'quassel', quassel );
NOTE :
Having a look at webpack configuration, seems like webpack likes to override couple of modules :
"node": {
"fs": "empty",
"global": true,
"crypto": "empty",
"tls": "empty",
"net": "empty",
"process": true,
"module": false,
"clearImmediate": false,
"setImmediate": false
}
I don't exactly know why, but this list is in the webpack config and seems to be making net to be undefiend ( empty ) and that's why we had to create an alias.
Angular-cli uses webpack
you will need to add the JS file to apps[0].scripts in your angular-cli.json file, which WebPack then bundles as if it were loaded with a tag.
apps:[
{
...
"scripts": [
"../node_modules/libquassel/client/libquassel.js"
],
If you do that, you can get at it by adding declare var Quassel :any; in your src/typings.d.ts or component.
let quassel = new Quassel("localhost", 4242, {}, function(next) {
next("user", "password");
});
quassel.connect();
The key issue is the browserified libquassel library contains within it a global require function. That conflicts with standard require function.
There are many ways of handling this but one solution would be to copy /libquassel/client/libquassel.js (and the minified version) to your /src/app/ directory.
Then run a search and replace in these files to change ALL occurrences of require to _quasselRequire_
You would do this for both libquassel.js and libquassel.min.js.
To utilise this you would do something like this in quassel.service.ts:
import { Injectable } from '#angular/core';
import * as dummyQuassel1 from './libquassel.js'
var dummyQuassel2 = dummyQuassel1; // something just to force the import
declare var _quasselRequire_ :any;
#Injectable()
export class QuasselService {
constructor() {
// set up in the constructor as a demo
let Quassel = _quasselRequire_( 'quassel' );
console.log('Quassel', Quassel);
var quassel = new Quassel(
"quassel.domain.tld",
4242,
{ backloglimit : 10 },
function ( next ) {
next( "user", "password" );
}
);
console.log('quassel', quassel);
quassel.on('network.init', function(networkId) {
console.log("before net");
var network = quassel.getNetworks().get(networkId);
console.log("after net");
// ...
});
console.log("before connect");
quassel.connect();
console.log("after connect");
}
getQuassel() {
return 'My Quassel service';
}
}
This by itself results in the error:
POST http://localhost:4200/api/vm/net/connect 404 (Not Found)
and
Error: Uncaught, unspecified 'error' event.
at new Error (native)
at Quassel.n.emit (http://localhost:4200/main.bundle.js:570:4210) [angular]
at Socket. (http://localhost:4200/main.bundle.js:810:19931) [angular]
at Socket.EventEmitter.emit (http://localhost:4200/main.bundle.js:573:1079) [angular] ...
But as I understand it this is fairly normal given I haven't Express etc. set up.
Note: the name _quasselRequire_ can be anything that's appropriate to you.
Working on a project that gets its content with ProtoBuff. Made it work once with the loading of the JavaScripts in the HTML. Now refactoring to use requirejs to load the scripts. But when I try to use the scripts, it gives an error telling me the scripts are not loaded.
Require.js is loaded in the index.html
Bower is used to manage dependencies.
I am pretty sure I am missing a (simple) thing here, hope some one can help.
requirejs.config({
long : "long",
ByteBuffer : "ByteBuffer",
ProtoBuf : "ProtoBuf"
});
requirejs([ "long", "ByteBuffer", "ProtoBuf" ],
function( long, ByteBuffer, ProtoBuf ) {
});
the files long.js, ByteBuffer.js and ProtoBuf.js are all in the same map as the App.js, where this is called.
*While this question about requirejs and ByteBuffer looks promising, I think I am missing something here.
This does work, the functions in those files are accessible within the rest of the scope:
requirejs([ "otherPage", "differentPage" ],
function( util ) {
});
You need to make sure you have requirejs hooked up correctly and that you have the relevant proto library loaded.
You can use bower to manage dependencies. Install bower and
bower install long byteBuffer protobuf requirejs-text requirejs-proto
The final code can then look something like this:
require.config({
paths: {
'Long': '../../bower_components/long/dist/Long',
'ByteBuffer': '../../bower_components/byteBuffer/dist/ByteBufferAB',
'ProtoBuf': '../../bower_components/protobuf/dist/ProtoBuf',
'text': '../../bower_components/requirejs-text/text',
'proto': '../../bower_components/requirejs-proto/proto'
},
proto: {
ext: 'proto',
convertFieldsToCamelCase: false,
populateAccessors: true
}
});
require(['proto!test'], function(builder) {
var pack = builder.build('pack');
var Message1 = builder.build('pack.Message1');
});
require(['proto!test::pack.Message1', 'proto!test::pack.Message2'], function(Message1, Message2) {
...
});
some code from https://www.npmjs.com/package/requirejs-proto
I am trying to set up a build system for my front end work though I am running into a problem where it loops processing files over and over again. This is a problem with my js processing since I am not sure how to exclude just the files with .min as a suffix.
The task goes as follows
return gulp.src(["!dev/js/*.min.js", "dev/js/*.js"])
.pipe(plumber())
.pipe(browserify())
.pipe(smaps.init())
.pipe(uglyify({preserveComments: "license"}))
.pipe(smaps.write())
.pipe(rename({suffix: ".min"}))
.pipe(gulp.dest(output_dir));
Though what I have found is that it still targets the .min.js files since they are also seen as .js files. I have messed around with a few different configurations of these wildcards but I keep ending up with the task looping creating example.min.js then example.min.min.js then example.min.min.min.js etc.
So, how can I just process files that do not include the .min prefix?
You can use negated patterns to exclude .min.js files.
gulp.src(['dev/js/*.js', '!dev/js/*.min.js'])
if you want to do it in only one string you can use:
gulp.src(["dev/js/!(*.min)*.js"])
In your gulp command.
const
gulpIgnore = require( 'gulp-ignore' ),
uglify = require( 'gulp-uglify' ),
jshint = require( 'gulp-jshint' ),
condition = './**/*.min.js';
gulp.task( 'task', function() {
gulp.src( './**/*.js' )
.pipe( jshint() )
.pipe( gulpIgnore.exclude( condition ) )
.pipe( uglify() )
.pipe( gulp.dest( './dist/' ) );
} );
In the condition you specify your directory. ../ to go backward, /dir to go forwards
Hope someone can help me figure out this.
I have 3 products that have almost the same interface, lets say Product A, B and C
What I want to do, if possible(through Gulp/Grunt/Other), is to create one source code and when building the final app, it will carry over to the dist folder the part for Produtc A, B C.
Example:
app/
js/
assets/
views/
dist/
ProdA/
ProdB/
ProdC/
Creating the build isn't the hard part, but how can I make my code so I can consume consume different API for each product.
Example:
Product A, B and C have status pages, A have (3 fields), B have all field from A (plus one) and C have fields completely different.
Do you guys have tips/tricks/guides so I can accomplish a similar task and reduce my code re-use?
Some Thoughts and Options
Your question seems to leave it to interpretation. Too many open ended answers possible, and many great answers will be provided. However, based on what I think you are doing, here are some options:
Use an external config file, and use globbing patterns for each, and exclude what you don't want.
Create a gulp task that copies some source files and moves them to your dist/a, dist/b, etc.
Turn your reusable code into a node_module and 'require' it in each apps src.
Turn your reusable code into a git submodule and use it as a dependency for each app. Works well with the previous point.
A Gulp Task to move files and folders
For exactly what you asked, all sharing one repo (I do highly recommend separate repos though to simplify the tasks), here is a gulp task you can use to move files around. It's very simple.
This example / suggestion will have two parts for convenience. The task, and a config section to manage it. You can either use it in the gulpfile, or as an external file which may be easier to manage if you are using the same gulpfile for three projects.
You could also use merge-stream to do the same task to multiple src & dest:
npm install --save-dev merge-stream
gulpfile.js
// =========================================================
// Projects: Project A, Project B, Project C
// info:
//
// =========================================================
// ------------------------------------------------ Requires
var gulp = require('gulp'),
merge = require('merge-stream');
// -------------------------------------------------- Config
var config = {
// These are some examples
reusable: [
'./src/reusableCode/scripts/**/*.js',
'./src/reusableCode/styles/**/*.css
],
productA: {
src: [ ], // Add product A src
opts: { }, // Add product A options for gulp tasks,
dest: './dist/A'
},
productB: {
src: [ ], // Add product B src
opts: { }, // Add product B options for gulp tasks,
dest: './dist/B'
},
productC: {
src: [ ], // Add product C src
opts: { }, // Add product C options for gulp tasks,
dest: './dist/C'
},
}
// --------------------------------------------------- Tasks
// ...
gulp.task('moveSrc', function(){
var prodA = gulp.src( config.reusable )
.pipe( gulp.dest( './dist/A' ) );
var prodB = gulp.src( config.reusable )
.pipe( gulp.dest( './dist/B' ) );
var prodC = gulp.src( config.reusable )
.pipe( gulp.dest( './dist/C' ) );
return merge( prodA, prodB, prodC);
});
// A task to move folders and files without `merge-stream`
// gulp.task( 'moveSingleStream', function() {
// gulp.src takes the src into a stream, and output the stream
// just by using gulp.dest . This moves files and folders around
gulp.src( config.reusable )
.pipe( './dist' );
});
// --------------------------------------------------- Build
gulp.task( 'build', [ 'moveSrc', 'otherTask1', 'otherTask2' ] );
You could also skip the reasuable array in config in this example, and just add what you want to move in each products src array. Whatever works.
Now that being said, I would suggest separate repo's and using that reusable code as node modules, but you can use merge-stream to simplify the process of using the same tasks to do multiple things. I hope this helps.
Assuming I understand your question, what you want is a basic service that has modifications by the product type. If it is the case, you can use angular's service decorator, this enables you to take an existing service and add to it functionality, either by defining new methods or wrapping the behavior of existing methods. If this works for you, have each product decorate the base service according to its needs.
There are many examples of decorations, for example this one.
So here is my hypothetical config object for a hypothetical fooTask that does something (not relevant to question) to a bunch of JS files
grunt.initConfig({
fooTask: {
app1: 'app1/*.js',
app2: 'app2/*.js',
app3: 'app3/*.js'
}
});
As you can see, with this approach, I have to run fooTask 3 times with each app specified as a target:
grunt fooTask:app1
grunt fooTask:app2
grunt fooTask:app3
Needless to say this does not scale as either the number of apps increase or the number of such foo tasks increase as one has to C&P the same code over and over for each app.
So ideally what I would like to define is just one target with the name of the app passed in as a config variable
grunt.initConfig({
fooTask: {
dist: '<%=appName%>/*.js'
}
});
I would then like to call fooTask 3 times, one for each app, with the right app set as appName
var apps = ['app1', 'app2', 'app3'];
apps.forEach(function(app) {
var currAppName = app;
// Run fooTask but how do I specify the new currAppName config?
grunt.task.run('fooTask');
});
As from code above, I know I can run my fooTask using grunt.task.run but how do I set the appName config for my task?
Note that this question is similar to this other one that also does not have the right answer yet - Pass Grunt config options from task.run
Thanks a lot.
EDIT 2:
So nevermind the garbage below the first edit, leaving as example of what doesn't work. In my case it was really important to be able to set the value within a task at run-time so I settled on the file system. Perhaps it suits your needs.
grunt.initConfig({
someTask: {
someKey: fs.readFileSync('file.txt', { encoding: 'utf8' })
}
});
of course you can do the readFile outside of the task if you need a bunch of different app names.
EDIT:
Hmmm. I swear I had this working when I wrote this...but now it is not. Grunt just sees the extra arguments as additional unfound tasks.
I was trying to figure this out myself, finally a "duh" just moment happened - why not parse process.argv before grunt.initConfig?
module.exports = function(grunt) {
var sourcefile = process.argv[2] || 'default.js'; // <- this
grunt.initConfig({
uglify: {
main: {
src: sourcefile, // <- voila :)
dest: sourcefile.substring(0, sourcefile.length-3) + '.min.js'
}
}
});
grunt.loadNpmTasks('uglify');
grunt.registerTask('default', ['uglify']);
};
and use from command line:
grunt mykillerscript.js
I didn't even try to use grunt.option for the same reason that all the examples only showed directing which task is run, but I wouldn't be surprised if there is a more "grunt" way to do this.