Skip to content
This repository has been archived by the owner on Mar 27, 2020. It is now read-only.

next #29

Closed
deepsweet opened this issue Mar 16, 2017 · 31 comments
Closed

next #29

deepsweet opened this issue Mar 16, 2017 · 31 comments

Comments

@deepsweet
Copy link
Owner

Hey. I'm about to introduce next major version relatively soon, with simplified tasks API and better API for tasks runner itself. Some details from future migration.md:

Start

Repositories

Start became a monorepo.

Pros:

  • much easier to maintain
  • no start-start-preset
  • no babel-preset-start

Cons:

  • ???

Runner

import Runner from 'start';
import Reporter from 'start-pretty-reporter';

const runner = Runner(Reporter());

export const build = () => runner(
  // ...
);

With this naming our core concept became much more clear: there are tasks and tasks runners. You have to wrap tasks with runner.

"External" input

runner itself is another function in which you can pass an "initial" input:

runner(...tasks)('input!')
  .then(console.log)
  .catch(console.error);

So no more start-input-connector:

export const tasksRunner1 = () => runner(
  function task2(input) {
    console.log('input from previous runner': input);

    return Promise.resolve();
  },
);

export const tasksRunner2 = () => runner(
  function task1() {
    return Promise.resolve('output');
  },
  tasksRunner1()
);

And start-watch became really beautiful:

export const dev = runner(
  clean('lib/'),
  watch('src/**/*.js')(
    runner(
      read(),
      babel(),
      write('lib/')
    )
  )
);

Tasks

Refactoring

Tasks were slightly simplified:

export default (options) => function myTask(input, log, reporter) {
  console.log('input:', input);
  log('hey from My Task!');
  console.log('original reporter:', reporter);

  return Promise.resolve('My Task output');
}

So in the simplest case task is just a single named function which should return a Promise.

Reporters

Default reporter

console.log is no more a default reporter for Runner.

Refactoring

Reporters were splitted into composed functions:

export default (name) => (type, message) => console.log(name, type, message);

@effervescentia @tunnckoCore @laggingreflex @nikolay @eisisig I was thinking about scoped packages like @start/runner, @start/clean and so on. But unfortunately start username is already taken on NPM. So I wrote to the author – radio silence, wrote to NPM support – they can't help even with abandoned empty accounts...

Rename the whole project? Or leave it as is with start- naming convention? I'm ok with both, but I need your opinion.

import Runner from '@please/runner';
import Reporter from '@please/reporter';
yarn please build
yarn please test

🤔

Pros:

  • we can start (damn) everything from scratch with 0.1.0 versions for every package
  • more searchable name? with pleasejs/please as a main repo

Cons:

  • you have to rename and republish your tasks and presets as well – a lot of work (or not?)

What do you think about please? The major changes described above will be implemented regardless this naming issue.

@tunnckoCore
Copy link

Heya! Just not force the things too fast. You should see what's the community and how big it is. Invest some time to research, because this seems like big change.

I'm agree with the monorepo and probrably with the renaming, since most of the packages are at your accont and this org.

What means "no more presets"? And why "console.log is not the default reporter"? What will be the default reporter?

If the rename happen, I suggest to start it as separate project OR release it as Start v6. If there's no impact on the community and actually don't have any let start v5 be last version and start a new project from scratch without touching start.

@deepsweet
Copy link
Owner Author

What means "no more presets"?

It's only about "no start-start-preset", because I just don't need it anymore with monorepo.

And why "console.log is not the default reporter"? What will be the default reporter?

Nothing by default? I mean you have to choose something and pass it as an argument to Runner (simple, pretty, whatever), default console.log was kinda useless anyway imo.

release it as Start v6

Actually that's the decision I tend to. Just not to break literally everything with one move. Make a monorepo, make breaking changes, major bump start to v6 and major bump every task package.

Also I want to rename start-files to start-find because it's more clear and unix-like, and also a verb :) find, read, write.

@tunnckoCore
Copy link

Agree. Sounds good.

@effervescentia
Copy link

Seems like the best option, I also don't know what I would rename re-start to, so for that reason alone I'd stick with start. Technical changes sound good too

@tunnckoCore
Copy link

tunnckoCore commented Mar 17, 2017

Forgot to mention that there is also https://github.com/iamvdo/pleeease (edited)

@deepsweet
Copy link
Owner Author

alright, just got "start" username (so @start scope as well) on NPM 🔥

@tunnckoCore
Copy link

tunnckoCore commented Sep 7, 2017

Super 🎉

As about the tasks. Are they curried? Like all of these are equivalent

export default (options) => function myTask(input, log, reporter) {
  console.log('input:', input);
  log('hey from My Task!');
  console.log('original reporter:', reporter);

  return Promise.resolve('My Task output');
}

second variant

export default (options) => function myTask(input, log) {
  return (reporter) => {
    console.log('input:', input);
    log('hey from My Task!');
    console.log('original reporter:', reporter);

    return Promise.resolve('My Task output');
  }
}

third

export default (options) => function myTask(input) {
  return (log, reporter) => {
    console.log('input:', input);
    log('hey from My Task!');
    console.log('original reporter:', reporter);

    return Promise.resolve('My Task output');
  }
}

and

export default (options) => function myTask(input) {
  return (log) => {
    return (reporter) => {
      console.log('input:', input);
      log('hey from My Task!');
      console.log('original reporter:', reporter);

      return Promise.resolve('My Task output');
    }
  }
}

@tunnckoCore
Copy link

Looking over the new code: no. But would be good to support it.

@tunnckoCore
Copy link

tunnckoCore commented Sep 7, 2017

@deepsweet one more thing that may be good to add is, don't sure how to name it, the Start() call to return a function. So it may be a bit easier and bit prettier to create tasks. Because i'm using prettier it formats to a bit unclear (that's because i also enforce ESLint's arrow-body-style and prefer-arrow-callback rules)

the eslint rule config

    'arrow-body-style': [
      'error',
      'as-needed',
      { requireReturnForObjectLiteral: false },
    ],

for example your build task

export const build = () => start(
  env('NODE_ENV', 'production'),
  files('build/'),
  clean(),
  files('lib/**/*.js'),
  read(),
  babel(),
  write('build/')
);

to this one

export const build = () =>
  start(
    env('NODE_ENV', 'production'),
    files('build/'),
    clean(),
    files('lib/**/*.js'),
    read(),
    babel(),
    write('build/')
  )

which is a bit uglier for my taste (i hate this new line return, like in Python and Ruby).

All this can be fixed if the start returns a function. I currently can workaround it with that

export const start = Start(reporter())
const _start = (...args) => () => start(...args)

export const build = _start(
  env('NODE_ENV', 'production'),
  files('build/'),
  clean(),
  files('lib/**/*.js'),
  read(),
  babel(),
  write('build/')
)

So probably we can add second argument to the initial Start call, which would be boolean, so

// returns as what currently returns (a function which accept tasks)
const start = Start(reporter())

and if we pass true as second argument it will return a task function which calls the start function

const start = Start(reporter())
const _start = Start(reporter(), true)

export const bar = () => start(
  one,
  two,
  three
)

export const foo = _start(
  one,
  two,
  three
)

@deepsweet
Copy link
Owner Author

deepsweet commented Sep 7, 2017

As about the tasks. Are they curried?

Task internals are kinda not exposed to the user land, that's why I was thinking about to simplify tasks API a bit because it's used only by Start itself. Will post an update here soon, I have a bunch of new ideas since March :)

_start()

These arguments are really useful when they available for the entire scope:

export const makeES = (packageName) => start(
  files(`packages/${packageName}/es/`),
  clean(),
  files(`packages/${packageName}/src/**/*.js?(x)`),
  read(),
  babel(babelConfig),
  rename((file) => file.replace(/\.jsx$/, '.js')),
  write(`packages/${packageName}/es/`)
);

Also it will play very nice with the next version of start-parallel plugin:

const build = (packageName) => start(
  parallel('makeES, makeLib')(packageName)
);
yarn start build my-package

prettier

🙈

@tunnckoCore
Copy link

These arguments are really useful

I know 😆 I wanted them. But in most cases may be not needed.

@deepsweet
Copy link
Owner Author

deepsweet commented Sep 7, 2017

So here is my latest ideas:

task()

import Task from '@start/task';
import reporter from '@start/pretty-reporter';

const task = Task(reporter());

export const build = (packageName) => task('build it!')(
  find(`packages/${packageName}/es/`),
  clean(),
  find(`packages/${packageName}/src/**/*.js`),
  read(),
  babel({}),
  write(`packages/${packageName}/dist/`)
);

And the report, something like:

> build it!
  > find ...
  > clean ...
  > read ...
  > babel ...
  > write ...

Now Start is aware of plugins list in a named "group" in advance so we can build a really beautiful reporters even with animations. Ink!

Nested tasks

export const foo = (sameArg) => task('foo!')(
  plugin3(),
  plugin4()
);
export const bar = (arg) => task('bar!')(
  plugin1(),
  foo(arg),
  plugin2()
);

CLI:

yarn start bar "Hi, this is arg"

Report:

> bar!
  > plugin1 ...
  > foo!
    > plugin3 ...
    > plugin4 ...
  > plugin2 ...

Parallel

Report is not a mess anymore because it contains plugins log grouped under the particular task name.

> build esm!
  > find ...
  > clean ...
  > read ...
  > babel ...
  > write ...

> build cjs!
  > find ...
  > clean ...
  > read ...
  > babel ...
  > write ...

plugin()

Maybe something like this?

import plugin from '@start/plugin';

export default (options) => plugin('Oh My Plugin')((input) => (log, reporter) => {
  log('weee');
  return Promise.resolve(input);
});

I actually never liked that function.name "hack" we used to get a plugin name.

@tunnckoCore
Copy link

tunnckoCore commented Sep 7, 2017

I actually never liked that function.name "hack" we used to get a plugin name.

It's not a hack and is enough in most cases. Otherwise you can use get-fn-name which is a bit better than fn-name by Sindre. Created and exists for reason. So you we can just skip the needless naming. Like

export default (options) => plugin('foo bar plug', (input, log, reporter) => {})

// if not a string, then fallback to `get-fn-name`
export default (options) => plugin((input, log, reporter) => {})

evenmore, this plugin factory should return a function which accepts options. So things would be a more standard and clear

export default plugin('Oh Buble Plugin', (input, options, log, reporter) => {
  if (options.foo) {
    log('woohoo')
  }
})

usage

import bublePlugin from '@start/buble'

export const build = start(
  buble({ foo: 'bar' })
)

where, of course this (input, options, log, reporter) => {} is curried internally, so plugin creators may use it in what way they want.

@tunnckoCore
Copy link

tunnckoCore commented Sep 7, 2017

Even more clean and beauty may be if we enforce conventions, using the destructuring. The destructuring js feature is just freaking awesome and amazing, exactly because these two things:

  • allow teams and projects to enforce conventions
  • lowers the arguments number

So probably it will be the best if our signature is

export default plugin('Oh Buble Plugin', (input, { options, log, reporter }) => {
  if (options.foo) {
    log('woohoo')
  }
})

Haha, and so, no need for currying support 🤣

@deepsweet
Copy link
Owner Author

THIS 🔥

export default plugin('Oh Buble Plugin', ({ input, options, log, reporter })

@tunnckoCore
Copy link

Exactly! Exactly this was typing in the editor, because i'm working on 40 lines version :P
Please don't rush with the new release :D

@tunnckoCore
Copy link

tunnckoCore commented Sep 7, 2017

What about Start to accept emitter instead of reporter function? It will allow better reporting and even if someone think and need: TAP producing reporter ;p

@deepsweet
Copy link
Owner Author

What about Start to accept emitter instead of reporter function

💎

@tunnckoCore
Copy link

tunnckoCore commented Sep 7, 2017

Great! I'll PR with my idea for next :) And we can discuss there instead.

@deepsweet
Copy link
Owner Author

next branch is outdated, please wait until I push the ideas described above :)

@tunnckoCore
Copy link

tunnckoCore commented Sep 7, 2017

Anyway, it's written from scratch, so i don't need a branch :D I'll create new branch anyway ;] Just to see it in files and not in issue codeblocks. :)

@radum
Copy link

radum commented Oct 23, 2017

@deepsweet Looking fwd for the new changes. Need any help?

I was looking for a module that does pretty much what you are doing before I start building my own. Do you have an ETA or need some help?

@deepsweet
Copy link
Owner Author

deepsweet commented Oct 23, 2017

@radum I'm expecting start@next to build itself next week, so all the core utils and few important plugins like @start/babel should be ready. depends on which plugins you want, we have something like 30, and I'll migrate them one by one in some special (but still unclear) priority.

@deepsweet
Copy link
Owner Author

so far current state is something like this and this – not sure that I'm not going to change @start/plugin entirely soon.

@radum
Copy link

radum commented Oct 23, 2017

Plugins are not important for me right now. The fact that I can use functions that return a promise as tasks and start using async await are the key features. Thank you for your quick reply.

@tunnckoCore
Copy link

tunnckoCore commented Oct 23, 2017

Hey @deepsweet start's great to hear! Sorry about the promotion, but someone may be interested in hela too. It's is similar, but takes a bit different path by using just execa and executing the commands, instead of using task's/package's APIs, which requires a lot small boilerplate. You can see hela-config-tunnckocore for complete example.

I invest heavily in it. And everything just started after complete rewrite of Start (again around 30-40 lines). Btw... i have it somewhere locally and it was meant to be landed as PR here to discuss.

edit: (...after few seconds) damn, i reinstalled my machine last night and don't remember if i had it somewhere backedup in the github.

edit2: The next branch looks good too! The reporter to be an emitter is great (we probably discussed that? hm)

@radum
Copy link

radum commented Nov 22, 2017

Hey @deepsweet, did you manage to finish this?

I'm quite keen to see how the final Plugin code looks like.

@deepsweet
Copy link
Owner Author

😿

@radum
Copy link

radum commented Nov 29, 2017

@deepsweet I would gladly help to port the current plugins. For my own personal use and for my company, the concurrent and parallel plugins are the most important ones.

But before I start we need the final version of the next release :P

@deepsweet
Copy link
Owner Author

deepsweet commented Apr 15, 2018

see master branch for current state – it's almost stabilized and all known API experiments are completed. I've been waiting for this since forever as well 🙏

there should be docs/migration.md at some point.

@deepsweet
Copy link
Owner Author

I've just published v0.1.0 of everything.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants