redwoodjs / redwood

The App Framework for Startups
https://redwoodjs.com
MIT License
17.33k stars 994 forks source link

Preact instead of React? #261

Closed heymartinadams closed 2 years ago

heymartinadams commented 4 years ago

Hi @peterp @thedavidprice and @mojombo, been following redwoodjs with great excitement — also enthusiastically support Preact and Prisma. Might adopt Redwood.

Noticed you support React out of the box, but not Preact. Been using Preact and been achieving a fantastic Lighthouse score as opposed React, which I use in another project; it’s quite bloated.

Would you consider switching to Preact?

mojombo commented 4 years ago

I haven't spent any time with Preact, but certainly its smaller size is appealing. Can you help me understand what the limitations are, and what we'd have to change in Redwood to support it? It's possible we could make it an option, instead of React, if it didn't mean massive changes to the framework.

heymartinadams commented 4 years ago

So far our startup has been developing a PWA using Preact instead of React and we’ve encountered 0 issues, even though our app uses the following React-centric dependencies: @mdi/react, @react-google-maps/api, @react-spring/web (that’s a big one, for animation), @uploadcare/react-widget, among others (I think that’s because the creators/maintainers have been putting in care to make it cross-compatible). We use the latest ES6 and hooks.

The one thing that might make this more difficult is that our app requires a preact.config.js file, since Preact uses Webpack. Here’s some of what’s in our config file:

import CopyWebpackPlugin from 'copy-webpack-plugin'
import envVars from 'preact-cli-plugin-env-vars'

export default (config, env, helpers) => {
    // Copies any files (but not folders) located in the `/assets` folder directly into the build folder; useful for robots.txt
    config.plugins.push(new CopyWebpackPlugin([{ context: `${__dirname}/src/assets`, from: `*.*` }]))
    // environment variables
    envVars(config, env, helpers)
}
heymartinadams commented 4 years ago

Tagging @developit, @marvinhagemeister (Preact creators/maintainers) in case they’d like to comment as well.

marvinhagemeister commented 4 years ago

@heymartinadams Thanks for tagging me :+1:

Looking at the frontend architecture of redwood it seems to be as easy as setting up the proper aliases for Preact. Adding these few lines to redwood's webpack config will do the trick :tada:

// inside webpack.config.js
"resolve": { 
  "alias": { 
     "react": "preact/compat",
     "react-dom/test-utils": "preact/test-utils",
     "react-dom": "preact/compat",
   },
}

Note that Preact the framework is independent from our cli. Preact can run without any build tools right in the browser :100:

thedavidprice commented 4 years ago

Hot off the press we have the capability (and docs!) to extend default Webpack config within a Redwood app: https://github.com/redwoodjs/redwood/blob/master/docs/webpack.md

Could someone take @marvinhagemeister's Preact config for a spin and report back?

Bonus points for an example deployed on Netlify 🚀

mohsen1 commented 4 years ago

Nice! I think just like Next.js this should be done in userland

jonniebigodes commented 4 years ago

@thedavidprice there you go. I have been fiddling with this and managed to get a working Preact redwood app. The code is here and a deployed version is here

What is still not done:

Also this goes a bit on my other issue #288. If a user wanted it to go through this he/she would have the opportunity of getting this example through the cli, like so many others frameworks allow.

jonniebigodes commented 4 years ago

@thedavidprice i circled back to this and it seems that the testing will not be so streamlined, a bit of work might be required to get this working properly.

What i did was the following:

/* import { render, cleanup } from '@testing-library/react' */
import { h } from 'preact'
import { render, cleanup } from '@testing-library/preact'

import HomePage from './HomePage'

describe('HomePage', () => {
  afterEach(() => {
    cleanup()
  })
  it('renders successfully', () => {
    expect(() => {
      render(<HomePage />)
    }).not.toThrow()
  })
})

Test Suites: 4 failed, 4 total Tests: 0 total Snapshots: 0 total Time: 8.943s Ran all test suites. error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command. [19:04:50] Running 'web' jest tests [failed] [19:04:50] → Command failed with ENOENT: yarn jest --passWithNoTests --config ../node_modules/@redwoodjs/core/config/jest.config.web.js spawn yarn jest ENOENT Command failed with ENOENT: yarn jest --passWithNoTests --config ../node_modules/@redwoodjs/core/config/jest.config.web.js spawn yarn jest ENOENT Done in 12.18s.



- I thought of making the same approach for setting preact by overriding the configuration by setting up a file `jest.config.web.js` with the necessary configuration and see if it worked, it didn't. It yields the same result.
- Did a bit of digging and it seems that part of the problem lies [here](https://github.com/redwoodjs/redwood/blob/master/packages/cli/src/commands/test.js#L25) it's hardcoding the location of the configuration. 

It could be more than this involved, but it seems that this is a good place to start changing. Possibly allow the configuration to be something that the user can pass in as a argument. 

If that argument is not injected then use the default one.

Feel free to provide feedback
thedavidprice commented 4 years ago

Apologies for the delayed reply here. I truly appreciate your work on this and I know a lot of people will be excited to take this for a spin once you have it working.

Jest tests have been an increasingly uphill battle. And we aren't actually able to get them working yet -- the current place we're stuck is on Page imports (Route object), which I believe is similar to what you see above.

The original Tracking Issue for tests is #181 And where we left off is #265

Peter did a substantial amount of Babel/Webpack/ESlint (re)work to even get Jest able to run. I can only hope all of this will work for Preact as is.

re: yarn rw jest config Yes, currently it's hard-coded into the command to use the core config files. But that could be easy to modify as needed and you'll see it's mentioned in the Tracking Issue -- e.g. adding an option to the command for a path to custom config.

As of now, I'd suggest you just work around this by:

Here's the web config you could copy to start with: https://github.com/redwoodjs/redwood/blob/master/packages/core/config/jest.config.web.js

jonniebigodes commented 4 years ago

@thedavidprice i made some progress regarding this. Here are the steps taken and the mixed results i got.

Going to start with the steps, going by your comment.

And:

```js
require('@testing-library/jest-dom')

export default Samplecomponent

And:
```js
import { h } from 'preact'
import { render, cleanup } from '@testing-library/preact'

import Samplecomponent from './Samplecomponent'

describe('Samplecomponent', () => {
  afterEach(() => {
    cleanup()
  })
  it('renders successfully', () => {
    const { getByText } = render(<Samplecomponent />)
    expect(getByText('Oingo Bongo')).toBeInTheDocument()
  })
})

And one page called OnemorePage with the following content and test respectively:

//import { h } from 'preact'
import Samplecomponent from 'src/components/Samplecomponent/Samplecomponent'
const OnemorePage = () => {
  return (
    <div>
      <h1>OnemorePage</h1>
      <Samplecomponent />
    </div>
  )
}

export default OnemorePage

And

import { h } from 'preact'
import { render, cleanup } from '@testing-library/preact'

import OnemorePage from './OnemorePage'

describe('OnemorePage', () => {
  afterEach(() => {
    cleanup()
  })
  it('renders successfully', () => {
    expect(() => {
      render(<OnemorePage />)
    }).not.toThrow()
  })
})

Issuing yarn jest as is yelds the following (i'm just leaving in the component error message, to keep it short).


jest --passWithNoTests --config=config/jest.config.web.js --watch
 FAIL  src/components/Samplecomponent/Samplecomponent.test.js (17.841s)
  ● Samplecomponent › renders successfully

    ReferenceError: h is not defined

      1 | //import { h } from 'preact'
      2 | const Samplecomponent = () => {
    > 3 |   return (
        |   ^
      4 |     <div>
      5 |       <h1>Oingo Bongo</h1>
      6 |     </div>

      at d.parentDom [as constructor] (src/components/Samplecomponent/Samplecomponent.js:3:3)
      at d.Array [as render] (../node_modules/preact/src/render.js:31:47)
      at push (../node_modules/preact/src/diff/index.js:171:7)
      at children (../node_modules/preact/src/diff/children.js:117:11)
      at _ (../node_modules/preact/src/diff/children.js:262:25)
      at key (../node_modules/preact/src/diff/children.js:260:30)
      at k (../node_modules/preact/src/diff/children.js:76:34)
      at childNodes (../node_modules/preact/src/diff/index.js:205:15)
      at E (../node_modules/preact/src/render.js:48:37)
      at resolve (../node_modules/@testing-library/preact/dist/pure.js:79:26)
      at Object.<anonymous>.exports.act (../node_modules/preact/test-utils/src/index.js:78:17)
      at render (../node_modules/@testing-library/preact/dist/pure.js:75:22)
      at Object.<anonymous> (src/components/Samplecomponent/Samplecomponent.test.js:11:27)

Uncommenting out the line import { h } from 'preact' yelds the following:

 PASS  src/pages/OnemorePage/OnemorePage.test.js (6.525s)
 PASS  src/components/Samplecomponent/Samplecomponent.test.js (6.557s)

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        8.941s, estimated 9s
Ran all test suites related to changed files.

At least we're getting some progress. I've checked both issues that you mentioned and tried some stuff around to the extent of trying to mock the entire "tree" (Router/Link/Links) and see if it could work, but alas i'm getting the same error you've described here.

Regarding the import error, probably it could be solved by adding the necessary config for preact in babel.config.js, probably extending your own here and see if it will work.

EDIT: As of a recent commit the import issue i was experiencing is fixed. Testing can be done minus the usage of the redwood router for instance.

Feel free to provide feedback.

thedavidprice commented 4 years ago

@jonniebigodes looks like you're making some great progress here! Hodgepodge of items related to testing:

//import { h } from 'preact' Redwood uses Webpack to handle React auto-importing. For tests, has to be emulated through babel config. Here is the specific setting: https://github.com/redwoodjs/redwood/blob/4f1cc1d26a65b6342245a9d6e3ad7848441a19c2/packages/core/config/babel-preset.js#L108-L115

RW Jest test support There's a lot of current momentum happening thanks to Robert Broersma. Want to make sure you’re following things in progress:

All of this is aligned with the Tracking Issue #181 and last place we got stuck #265

Hopefully, the result of all this being fixed will be config and tooling easily consumable in the case of Preact.

yarn rw test --config Your current workaround creating custom scripts and manually copy+paste config is what I would do as well.

Once tests are (finally!) passing, next up will be making config extensible (or at the very least pass your own config file). Per tracking issue:

if additional config is needed, capability for simple config extension managed from either App root or specific to App sides (e.g. web/, api/, etc.).

Ideally this would automatically include a config file if present at a specific path.

Anything else? Overall, this is looking like it's in a good place and just waiting for more core support from Redwood itself. What's next? What, if anything, is still missing and/or blocking your next steps?

thedavidprice commented 4 years ago

Is there anything keeping you from creating an initial NPM Package, e.g. redwoodjs-preact? At this time it would just be config. But once generator templates are "pluggable", it could include those as well. Along with more over time like specific helpers, extensions, etc.

I'd encourage you to do so if you are interested! Someone's going to eventually do it if not you. And don't worry about it still having rough edges -- there's nothing like getting feedback, help, and momentum from a community excited about what you're trying to do. Just don't overpromise, spend extra time on a good README doc, and make sure the manual setup steps are clear. 😁

🚀

Most of all excited you're excited about Redwood.

jonniebigodes commented 4 years ago

@thedavidprice i thought about what you said in your previous comment and i managed to create a small cli tooling that will help streamline the process of adding preact into redwood. It isn't published yet. But it's already in a repo. The reason i went with this cli for starters is to make the configuration more easy.

Going to expand a bit more on this.

Feel free to provide feedback.

jtoar commented 2 years ago

Thanks for experimenting with us way back when @jonniebigodes! It's been quite a while, and now it seems highly unlikely that we'll focus on adding Preact support. There is quite a lot of discussion around adding other sides though, and that may tie in / indirectly make it possible, though it'd still mostly be up to the user.