nparashuram / cordova-plugin-browsersync

BrowserSync Plugin for Cordova
110 stars 69 forks source link

opts.forEach is not a function #5

Closed roblav96 closed 8 years ago

roblav96 commented 8 years ago

Following your video directly and I can't do

cordova run

It says:

ERROR running one or more of the platforms: TypeError: opts.forEach is not a function
You may not have the required environment or OS to run this project

Any ideas?

Cordova CLI: 5.4.0
Gulp version:  CLI version 3.9.0
Gulp local:  
ios-deploy version: 1.8.2 
ios-sim version: 5.0.3 
OS: Mac OS Yosemite
Node Version: v4.2.1
Xcode version: Xcode 7.1 Build version 7B91b 
roblav96 commented 8 years ago

Looks like it's coming from when I execute

cordova prepare

Here's the file:

testApp/plugins/cordova-plugin-browsersync/lib/projectHook.js
var path = require( 'path' );
var glob = require( 'glob' );

var Patcher = require( './utils/Patcher' );
var browserSyncServer = require( './utils/browserSyncServer' );

function parseOptions( opts ) {
    var result = {};
    opts.forEach( function ( opt ) {
        var parts = opt.split( /=/ );
        result[ parts[ 0 ].replace( /^-+/, '' ) ] = parts[ 1 ] || true;
    } );
    return result;
}

module.exports = function ( context ) {
    var options = parseOptions( context.opts.options );

    if ( typeof options[ 'live-reload' ] === 'undefined' ) {
        return;
    }

    // TODO - Add back ignored option
    // TODO - Enable live reload servers

    var platforms = [ 'android', 'ios' ];
    var patcher = new Patcher( context.opts.projectRoot, platforms );

    var bs = browserSyncServer( function ( defaults ) {
        defaults.files.push( {
            match: [ 'www/**/*.*' ],
            fn: function ( event, file ) {
                if ( event === 'change' ) {
                    context.cordova.raw.prepare().then( function () {
                        patcher.addCSP();
                        bs.reload();
                    } );
                }
            }
        } );

        defaults.server = {
            baseDir: platforms.map( patcher.getWWWFolder.bind( patcher ) ),
            routes: {}
        }

        platforms.forEach( function ( platform ) {
            var www = patcher.getWWWFolder( platform );
            defaults.server.routes[ '/' + www ] = path.join( context.opts.projectRoot, www );
        } );

        return defaults;
    }, function ( err, servers ) {
        patcher.patch( {
            servers: servers
        } );
    } );
}

I'm gonna keep trying to figure this out. Any help would be amazing!

roblav96 commented 8 years ago

Here's some more info:

Executing "before_prepare"  hook for all plugins.
Generating config.xml from defaults for platform "android"
Wrote out Android application name to "HelloCordova"
This app does not have launcher icons defined
Wrote out Android package name to "io.cordova.hellocordova"
Generating config.xml from defaults for platform "ios"
Wrote out iOS Bundle Identifier to "io.cordova.hellocordova"
Wrote out iOS Bundle Version to "0.0.1"
iOS Product Name has not changed (still "HelloCordova")
Executing "after_prepare"  hook for all plugins.
Error: TypeError: opts.forEach is not a function
    at parseOptions (/Users/admin/Downloads/testApp/plugins/cordova-plugin-browsersync/lib/pluginHook.js:9:7)
    at module.exports (/Users/admin/Downloads/testApp/plugins/cordova-plugin-browsersync/lib/pluginHook.js:17:16)
    at runScriptViaModuleLoader (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/src/hooks/HooksRunner.js:151:18)
    at runScript (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/src/hooks/HooksRunner.js:129:16)
    at /usr/local/lib/node_modules/cordova/node_modules/cordova-lib/src/hooks/HooksRunner.js:114:20
    at _fulfilled (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:787:54)
    at self.promiseDispatch.done (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:816:30)
    at Promise.promise.promiseDispatch (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:749:13)
    at /usr/local/lib/node_modules/cordova/node_modules/q/q.js:810:14
    at flush (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:108:17)
roblav96 commented 8 years ago

Commenting out these lines got it working:

    // var options = parseOptions( context.opts.options );

    // if ( typeof options[ 'live-reload' ] === 'undefined' ) {
    //  return;
    // }
roblav96 commented 8 years ago

Still broken though after the second reload.

Error: listen EADDRINUSE :::3004
    at Object.exports._errnoException (util.js:874:11)
    at exports._exceptionWithHostPort (util.js:897:20)
    at Server._listen2 (net.js:1234:14)
    at listen (net.js:1270:10)
    at Server.listen (net.js:1366:5)
    at module.exports.plugin (/Users/admin/Downloads/syncz/node_modules/browser-sync/lib/server/index.js:24:25)
    at Object.module.exports.startServer [as fn] (/Users/admin/Downloads/syncz/node_modules/browser-sync/lib/async.js:236:52)
    at /Users/admin/Downloads/syncz/node_modules/browser-sync/lib/browser-sync.js:149:14
    at iterate (/Users/admin/Downloads/syncz/node_modules/browser-sync/node_modules/async-each-series/index.js:8:5)
    at /Users/admin/Downloads/syncz/node_modules/browser-sync/node_modules/async-each-series/index.js:16:16

Seems like the entire browsersync server creates another instance when a file is changed.

Sorry if I'm bombarding you with this stuff :X

axemclion commented 8 years ago

Will look at it now...

axemclion commented 8 years ago

Can you help me understand your project setup? Are you adding this as a plugin, or as a hook ?

roblav96 commented 8 years ago

I'm adding this as a plugin. I'm following your video step by step.

roblav96 commented 8 years ago

I'm going to try and play around with adding as a hook.

roblav96 commented 8 years ago

Might another option be setting all this up manually?

roblav96 commented 8 years ago

Are there any global dependentcies I need installed?

roblav96 commented 8 years ago

Would you like to use teamviewer sometime? I'm in Boston, MA USA

axemclion commented 8 years ago

Sure, we could do that sometime today. I am in PST. Weird that the plugin does not work. It should "just work". I will look in to see if there is an error in this code.

roblav96 commented 8 years ago

I'm free whenever you are.

I'll be in this chat room, channel "cordova". you don't need to auth or anything http://webchat.freenode.net/

my unsername is rob__

roblav96 commented 8 years ago

FIXED IT - with improvements

I edited your hook file like so and skipped the whole parsing function, which I never understood why it was there.

    var port = parseInt(context.opts.options.argv)
    console.log( 'port >', port )
    require( 'dns' ).lookup( require( 'os' ).hostname(), function ( err, address, fam ) {
        console.log( 'address >', address )

        var tcpPortUsed = require( 'tcp-port-used' );

        tcpPortUsed.check( port, address ).then( function ( inUse ) {
            console.log( 'inUse >', inUse )
            if ( inUse == false ) {

                var platforms = [ 'android', 'ios' ];
                var patcher = new Patcher( context.opts.projectRoot, platforms );

                var bs = browserSyncServer( function ( defaults ) {
                    defaults.files.push( {
                        match: [ 'www/**/*.*' ],
                        fn: function ( event, file ) {
                            if ( event === 'change' ) {
                                context.cordova.raw.prepare().then( function () {
                                    patcher.addCSP();
                                    bs.reload();
                                } );
                            }
                        }
                    } );

                    defaults.server = {
                        baseDir: platforms.map( patcher.getWWWFolder.bind( patcher ) ),
                        routes: {}
                    }

                    platforms.forEach( function ( platform ) {
                        var www = patcher.getWWWFolder( platform );
                        defaults.server.routes[ '/' + www ] = path.join( context.opts.projectRoot, www );
                    } );

                    return defaults;
                }, function ( err, servers ) {
                    patcher.patch( {
                        servers: servers
                    } );
                } );

            }
        } )
    } )

Then i just do:

cordova run android -d -- 3000

The problem was that every time it would trigger a refresh, a whole new browsersync instance would be created on top of the same port so it would error out and crash. Now I just pass in the port I'd like to use as a parameter so now I can use this on as many devices as I'd like.

I'd like to see if there's a way that I can use one browsersync instance for all devices.

axemclion commented 8 years ago

I fixed this issue here - https://github.com/nparashuram/cordova-plugin-browsersync/commit/4b8f98a5a87e0e8707198b4f92f5cdffec1bec23

Looks like the way I was getting options from the command line was messed up - i thought context.opts.options should work, but look like there is another argv option there. This is needed so that the plugin is activated only when you call it with -- --live-reload (Note the double dashes). The plugin should not be called any other time since it changes the <content src="index.html"> in every platform's config.xml to point to the browsersync server.

We also need to pass options to let a user ignore watching certain files. Also, note that changing any file calls prepare, which invokes this plugin - that is the reason you get the error about Error: listen EADDRINUSE :::3004 when you disabled it. This is the same problem with your code, when you call prepare as a part of the after-prepare hook. You need a way to not call the same hook from when the files are changed.

With the fix above, you should be good. Please let me know if that works.

Btw, I hangout in the Cordova slack channel here - http://cordova.slack.com/. I am axemclion. So feel free to ping me if this is still an issue.

roblav96 commented 8 years ago

Interesting. I'll have to try this out as soon as I get home. Thank you!!!

axemclion commented 8 years ago

@roblav96 Closing this issue as there is no more activity on this required. Please re-open if this issue is still not resolved.

roblav96 commented 8 years ago

Ok thanks man!