Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI responsiveness #6028

Merged
merged 28 commits into from
Jul 28, 2022
Merged

CLI responsiveness #6028

merged 28 commits into from
Jul 28, 2022

Conversation

peterp
Copy link
Contributor

@peterp peterp commented Jul 23, 2022

The CLI is a bit slow. It takes ~1150ms to run redwood --help. I believe speed is part of DX and we should try to aim for ~300ms response time in the CLI.

We're also using yargs at Snaplet and the approach we've taken is to separate the handlers (the logic that the CLI runs) from the structure that dictates the commands the CLI exposes. We do this by asynchronously importing the code in the handler.

@jtoar @dac09 I would love some input on the approach I'm taking before fully committing to it.

Closes #6027

@nx-cloud
Copy link

nx-cloud bot commented Jul 23, 2022

☁️ Nx Cloud Report

CI is running/has finished running commands for commit 3b13aba. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this branch


✅ Successfully ran 14 targets

Sent with 💌 from NxCloud.

@netlify
Copy link

netlify bot commented Jul 23, 2022

Deploy Preview for redwoodjs-docs canceled.

Name Link
🔨 Latest commit 3b13aba
🔍 Latest deploy log https://app.netlify.com/sites/redwoodjs-docs/deploys/62e259364bc2460008f84994

@peterp peterp requested review from dac09 and jtoar July 24, 2022 05:58
@jtoar jtoar added topic/cli release:feature This PR introduces a new feature labels Jul 24, 2022
@peterp
Copy link
Contributor Author

peterp commented Jul 24, 2022

hyperfine --warmup 3 'node node_modules/@redwoodjs/cli/dist/index.js --help'
Benchmark 1: node node_modules/@redwoodjs/cli/dist/index.js --help
  Time (mean ± σ):     791.3 ms ±   2.8 ms    [User: 777.2 ms, System: 167.5 ms]
  Range (min … max):   787.6 ms … 795.8 ms    10 runs

The last two biggest candidates are the build and serve command
image

Copy link
Contributor

@jtoar jtoar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peterp love the approach so far! The way you're doing the imports here gives us as much lazy evaluation as we can get out of yargs I think. The command, description, and builder all need to be available right away, but not the handler. (Accordingly, we should make sure the builders are clean. There’s a few commands that import some things. Probably nothing too crazy, but still.)

A somewhat-related concern I have that you've already made a few moves on is all the CLI's dependencies, and what's going on in lib. Writing a simple isTypeScriptProject helper instead of importing @redwoodjs/structure is a solid approach. (I really wish we could bundle the helpers we need into CLI and not list any dependencies at all, but that would require 1) decoupling the way code is written 2) overhauling the build process.) But did you have more thoughts on how you wanted to tackle dependencies and lib?

And my main question is: how can I help more concretely? Are there commands I can take from you? (build or serve? Both, or more?)

Here's a few other things that occur to me. All of these could be handled separately, but just wanted to share:

  • There's some bugs around dotenv loading I wonder if we could fix at the same time. Note that this probably a symptom of what I did to get yarn 3 working
  • We use a few lodash functions, but list the whole package as a dependency. We could shed some weight there
    • Along the same lines, we should make sure prompts isn't too heavy. if it is, we can switch to enquirer
  • I'd be happy to add a test for CLI responsiveness to CI. Also, this package needs way more tests. That should be easier to organize with the handlers separated out
  • I’d be happy to update the README after this is all over. Or I can do it as a part of this PR

One more thing: execa. I think we should try to standardize the way we use it as much as possible. I'm also down to remove it, but it's probably harder to replace than I think.

resolveFile,
} from '@redwoodjs/internal'
} from '@redwoodjs/internal/dist/paths'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind explaining this one? Are you trying to make it so that we just import path functions and not the kitchen sink with internal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jtoar I think what we're trying to achieve is "tree shaking," but we're doing that manually here!

For a long running process I guess it's fine to pay the upfront cost of importing all this JavaScript, but for something like a CLI it's gonna hurt. I think we should use package.json "exports" to expose a light version of "@redwoodjs/internal" that we can use is middleware and for the builder parts of the CLI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly what I was thinking of doing. I think we should export these individual files, rather than everything from index.js - which is part of the problem with internal (and structure... but that's a harder one)

@peterp
Copy link
Contributor Author

peterp commented Jul 25, 2022

@jtoar I think it would be great if you could help with the build and serve command. Maybe we could merge this approach to yield off merge conflicts? and then I'll create a loom video that explains to the process to discover the "hot paths" and what we're trying to achieve for the other commands.

I think we could also look at tree-shaking or bundling as a possible approach to solve this automatically, but I do not have a bunch of confidence in those.

(I really wish we could bundle the helpers we need into CLI and not list any dependencies at all, but that would require 1)

I think your intuition is totally correct here.

But did you have more thoughts on how you wanted to tackle dependencies and lib?

I was thinking that most of the functionality in lib/index.js should be placed in separate files, so we only import what we need at the time we need it. (mostly getPaths)

@dac09
Copy link
Contributor

dac09 commented Jul 25, 2022

@jtoar I think it would be great if you could help with the build and serve command. Maybe we could merge this approach to yield off merge conflicts? and then I'll create a loom video that explains to the process to discover the "hot paths" and what we're trying to achieve for the other commands.

This would be incredibly helpful @peterp - this is something I've struggled with. On another PR - Tobbe mentioned using yarn nx graph - that gives us a high level overview of how our packages depend on each other.

@jtoar jtoar marked this pull request as ready for review July 27, 2022 02:45
Copy link
Contributor

@jtoar jtoar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! @dac09 I reverted the debugging change we made to the imports back to the way Peter had it (from dist). The problem was just that most of the tests were mocking @redwoodjs/internal, not @redwoodjs/internal/dist/paths. After making that change and mocking the new functions in lib/project, things went back to passing.

If this looks good to you, we can merge this then focus on making legitimate entry points to @redwoodjs/internal, either using "exports" or another style—whatever we all settle on after doing some research and experimentation.

@dac09 dac09 enabled auto-merge (squash) July 27, 2022 09:28
@dac09 dac09 disabled auto-merge July 27, 2022 09:29
Copy link
Contributor

@dac09 dac09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HODL ✋ - found a bug in build - prerender fails with "getTasks" not found. Not sure why CI isn't catching it - @jtoar nx caching perhaps?

Copy link
Contributor

@dac09 dac09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the bug

@jtoar
Copy link
Contributor

jtoar commented Jul 27, 2022

HODL ✋ - found a bug in build - prerender fails with "getTasks" not found. Not sure why CI isn't catching it - @jtoar nx caching perhaps?

Looked into it, and it turns out that CI isn't catching it cause we don't test for it. We run yarn rw build --no-prerender:

- name: Run `rw build` without prerender
run: |
yarn rw build --no-prerender
working-directory: ${{ steps.setup_test_project.outputs.test_project_path }}

getTasks in yarn rw build is only invoked if it prerenders too:

prerender && {
title: 'Prerendering Web...',
task: async () => {
const prerenderRoutes = detectPrerenderRoutes()
if (prerenderRoutes.length === 0) {
return `You have not marked any "prerender" in your ${terminalLink(
'Routes',
'file://' + rwjsPaths.web.routes
)}.`
}
return new Listr(await getPrerenderTasks(), {
renderer: verbose && VerboseRenderer,
concurrent: true, // Re-use prerender tasks, but run them in parallel to speed things up
})
},
},

But why doesn't the yarn rw build unit test fail? Turns out it also doesn't test the lines where getTasks is called:

// Run prerendering task, but expect failure,
// because `detectPrerenderRoutes` is empty.
const x = await Listr.mock.calls[0][0][2].task()
expect(x.startsWith('You have not marked any "prerender" in your Routes'))

@jtoar jtoar enabled auto-merge (squash) July 28, 2022 09:39
@jtoar jtoar merged commit f262bab into main Jul 28, 2022
@jtoar jtoar deleted the pp/cli-performance-improvements branch July 28, 2022 10:03
@redwoodjs-bot redwoodjs-bot bot added this to the next-release milestone Jul 28, 2022
@jtoar jtoar mentioned this pull request Jul 28, 2022
1 task
dac09 added a commit to dthyresson/redwood that referenced this pull request Jul 28, 2022
…onfig-plugins

* 'main' of github.com:redwoodjs/redwood: (78 commits)
  fix(cli): Modify existence check of jest.config to check for both .js and .ts extensions in rw test (redwoodjs#6074)
  Tutorial 6.4: Add type generics to mockGraphQLMutation (redwoodjs#6046)
  Tutorial 6.2: Add generics type hint to CellSuccessProps (redwoodjs#6042)
  fix(deps): update graphql-tools monorepo (redwoodjs#6066)
  fix(deps): update dependency @types/node to v16.11.46 (redwoodjs#6065)
  chore(deps): update dependency @okta/okta-auth-js to v6.7.3 (redwoodjs#6068)
  chore(deps): update dependency @types/react to v17.0.48 (redwoodjs#6069)
  CLI responsiveness (redwoodjs#6028)
  Copy changes from "Make Jest Debugging a No-Brainer" (5296) (redwoodjs#6070)
  fix(deps): update dependency @graphql-codegen/cli to v2.11.0 (redwoodjs#6067)
  Update yarn.lock
  Version docs to 2.2
  Update all contributors
  Update yarn.lock
  v2.2.0
  Dedupe yarn.lock
  Show correct env filename on serverless firstrun deploy (redwoodjs#6023)
  chore(deps): update dependency firebase to v9.9.1 (redwoodjs#6020)
  tailwindcss ui: Add tailwindcss prettier rules (redwoodjs#5812)
  fix(deps): update dependency eslint-plugin-jsx-a11y to v6.6.1 (redwoodjs#6017)
  ...
@jtoar jtoar modified the milestones: next-release, v3.0.0 Sep 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release:feature This PR introduces a new feature topic/cli
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[RFC]: CLI Speed Improvement
3 participants