spences10 / twitter-bot-bootstrap

Twitter bot bootstrap :boot: using node and twit :bird:
https://medium.freecodecamp.com/easily-set-up-your-own-twitter-bot-4aeed5e61f7f#.dw898leyj
MIT License
231 stars 104 forks source link

export default? #37

Closed spences10 closed 7 years ago

spences10 commented 7 years ago

@MarcL I'm attempting to call a function in bot.js from another module, it's for the search part of the bot.

I can call the function from withing the bot.js module but when I try move it out into it;s own module and then import to the bot I get an error saying it's not a function, here's a quick overview:

Working bot.js

import Twiter from 'twitter'
import config from './config'
import {
  getFunName,
  rando
} from './helpers/helpers'
import params from './helpers/parameters'

const client = new Twiter(config.twitter)

const tweetNow = async (txt) => {
  try {
    await client.post('statuses/update', { status: txt })
    console.log(txt)
  } catch (err) {
    console.log(err)
  }
}

// tweetNow(getFunName())

// setInterval(() => tweetNow(getFunName()), 1000 * 60 * 2)

const searchResult = async () => {
  let res
  try {
    res = await client.get('search/tweets', params)
    // const randoUser = rando(res.data.statuses).user.scree_name
    // console.log('rando user = ', randoUser)
  } catch (err) {
    console.log('error = ', err)
  }

  // console.log(JSON.stringify(res))
  console.log(rando(res.statuses).user.screen_name)
  console.log(rando(res.statuses).text)
  console.log(rando(res.statuses).id_str)
}

searchResult()

setInterval(() => searchResult(), 1000 * 60 * 2)

tweetNow(getFunName()) and setInterval(() => which are commented out work I'm just concentrating on setting a search working using the same structure so I can them move it out to another module when finished.

Some terminal output from the above code: image

Not working an attempt to make things modular bot.js

import Twiter from 'twitter'
import config from './config'
import {
  getFunName,
} from './helpers/helpers'
import { searchResult } from './helpers/search'

const client = new Twiter(config.twitter)

const tweetNow = async (txt) => {
  try {
    await client.post('statuses/update', { status: txt })
    console.log(txt)
  } catch (err) {
    console.log(err)
  }
}

// tweetNow(getFunName())

// setInterval(() => tweetNow(getFunName()), 1000 * 60 * 2)

searchResult()

setInterval(() => searchResult(), 1000 * 5)

search.js

import Twiter from 'twitter'
import config from '../config'
import {
  rando
} from './helpers'
import params from './parameters'

const client = new Twiter(config.twitter)

const searchResult = async () => {
  let res
  try {
    res = await client.get('search/tweets', params)
    // const randoUser = rando(res.data.statuses).user.scree_name
    // console.log('rando user = ', randoUser)
  } catch (err) {
    console.log('error = ', err)
  }

  // console.log(JSON.stringify(res))
  console.log(rando(res.statuses).user.screen_name)
  console.log(rando(res.statuses).text)
  console.log(rando(res.statuses).id_str)
}

export default searchResult()

Terminal output from the above code: image

So I can use it in bot.js as a function but when I try abstract it out into it's own module it's a no?

Do you have any idea what I'm doing wrong?

MarcL commented 7 years ago

You've defined searchResult as a function:

const searchResult = async () => {

But then you're actually executing the function and returning the result of it:

export default searchResult()

Your function doesn't currently return anything so it will return undefined by default. You're then attempting to call undefined as a function, which it's obviously not.

You should just be exporting the function so that you can call it from outside of the module:

export default searchResult;

^ Note: no () after it.

Shout if it's still not clear.

spences10 commented 7 years ago

Hey @MarcL, I should have mentioned that I also tried to remove the parenthesis in search.js with the same error before posting this.

image

I thought it may have been something to do with Babel being a dev dependency not instead of a dependency, I then added the two require into index.js:

require('babel-register')
require('babel-polyfill')

looking at the error message TypeError: (0 , _search.searchResult) is not a function the underscore part is from Babel transpiling it for ES5 syntax correct?

TypeError: (0 , _search.searchResult) is not a function
    at Object.<anonymous> (C:/Users/spenc/gitrepos/twitter-bot-bootstrap/src/bot.js:48:1)
    at Module._compile (module.js:571:32)
    at loader (C:\Users\spenc\gitrepos\twitter-bot-bootstrap\node_modules\babel-register\lib\node.js:144:5)
    at Object.require.extensions.(anonymous function) [as .js] (C:\Users\spenc\gitrepos\twitter-bot-bootstrap\node_modules\babel-register\lib\node.js:154:7)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (C:/Users/spenc/gitrepos/twitter-bot-bootstrap/src/index.js:3:1)

I'm wondering if I have Babel set up incorrectly I'm confused, because it couldn't possibly be my code, right 😏

MarcL commented 7 years ago

Ok no problem. Just relooked. No, you've not set Babel up incorrectly. You're not quite using import/export correctly.

You've exported searchResult as a default:

export default searchResult; // Definitely without parentheses :)

But then you're importing as a named import:

import {searchResult} from './helpers/search';

But the export means that you're exporting an object with a default property which you'd have to import like this:

import searchResult from './helpers/search'; // No curly brackets

To use the import in the way you currently have it, you'd have to export it named like this:

export {
    searchResult
};

You normally do this when you have multiple functions to export, e.g.

function searchResult() {}
function tweetFunction() {}
function anotherFunction() {}

export {
     searchResult,
     tweetFunction,
     anotherFunction,
};

Make sense?

Imports and export with ES6 are sometimes confusing!

spences10 commented 7 years ago

I was right, it wasn't my code at all

jk! I had Babel pointing to the wrong file!!

My dev script

"dev": "nodemon --exec babel-node src/index.js jest --coverage",

Well actually take a look at the git diff on the package.json

image

Moved all the things!!

Thanks for all your help @MarcL just being able to bounce ideas off of you has helped me a ton

Oh, also, check this out:

search.js image

^^ as a named export, it works but ESLint doesn't like it, so try as default export? Ok lets try that..

Nope!

Ok, what about as default and as a named export??

image

Sure that will work just fine...

image

wtf?

As you can clearly see I really have no idea what I'm doing

spences10 commented 7 years ago

OMG!

Thanks for this @MarcL !! https://github.com/spences10/twitter-bot-bootstrap/issues/37#issuecomment-293812053

Thing is I was just watching the Wes Bos ES6 course on this!!

Ok, changed the import, so it's the default and got rid of the export on searchResult all good now

Thanks again, really appreciate it

MarcL commented 7 years ago

No problem. Happy to help.

I can't tell why the exports don't work without seeing the subsequent import I'm afraid.

Depends on the config but ESLint will probably tell you to use default export if you only export one function. See AirBnB ESLint module rules for some info.

MarcL commented 7 years ago

Ah - didn't realise you hadn't seen my reply after replying to your additional comment.

spences10 commented 7 years ago

I can't tell why the exports don't work without seeing the subsequent import I'm afraid.

It's because I wasn't importing it into bot.js correctly, I removed the curly braces from the import in bot.js as it was a default export and not a named one, then all is good.

This is a pretty big milestone for me @MarcL as I was struggling for weeks on this with that node module testing repo that you kindly had a look at for me all those weeks ago.

All I wanted to do was make my code modular, now I can πŸ˜„

MarcL commented 7 years ago

No problem. Always happy to help.

For testing your understanding of ES6/JS/modules in general, it might be easier to just to create a simple repo with import / export of simple functions to see how it works. I worry that you're getting bogged down with JS toolchains which can confuse the issue. Just export the classic calculator functions (function add(), function subtract() etc). You'll be able to get simple testing working too.

If you run a later version of node (> 6.4 I think) then you'll have 95% of ES6 functionality so you can avoid the transpile step with Babel. I'd also ignore things like Travis and snyk until you really need them. Otherwise you're adding more complication and more potential for pitfalls.

Have you seen this set of egghead.io videos from Trevor Miller? Shows a really basic NPM module set up and I think he works through each step really clearly.

https://egghead.io/courses/publish-javascript-packages-on-npm

spences10 commented 7 years ago

I worry that you're getting bogged down with JS toolchains which can confuse the issue.

@MarcL completely agree with you, I spent a couple of hours just setting up the tooling alone 😲 I think I set up Travis as part of the commitizen setup, gotta love those wizards πŸ˜„

I thought it best that I get familiar with working with all of the tooling needed as a real world example, building this bot, the current production one taught me a lot but I'll take your advice and do the basics. Typical me trying to run before I can walk!

I'll take a look at the videos, many thanks, really like egghead.io as a resource

spences10 commented 7 years ago

Closed with c28e902fbdd9af1929ab1f087a9b6107f1af5ef5