TVke / react-native-tailwindcss

A react-native style system based on TailwindCSS
https://tvke.github.io/react-native-tailwindcss/
MIT License
565 stars 34 forks source link

Static generator for tailwind styles & colors #46

Open davidgovea opened 4 years ago

davidgovea commented 4 years ago

Hi @TVke !

This PR is not ready yet, but I wanted to share the approach to discuss.

Motivations:

  1. TypeScript definitions for non-default tailwind configs (Issue #18)
    • Pre-generation was the only avenue that I could think of here
  2. Eliminate project structure assumptions for requiring tailwind.config.js: We ship react-native-based native SDKs with electrode-native, and in that case, the require('../../../tailwind.config') causes issues (here: https://github.com/TVke/react-native-tailwindcss/blob/master/util/configHandler.js#L6).

Definitely acknowledge that 2) is more of an exotic build setup issue, but 1) is of high importance to all users of this library

Approach:

  1. Use react-native-tailwindcss as a generator, not a runtime dependency:
    • babel-node generate.js --config tailwind.config.js
    • a rn-tailwind.ts file is created
  2. import { t, color } from './rn-tailwind'

Since the generated file is TypeScript, we have full types, even for custom changes to tailwind config!

Design notes:

Further work:

davidgovea commented 4 years ago

This has been working out really well -- Actually caught some type errors, since this goes beyond the styleName: any definitions of the current version.

For example, passing <View style={[t.alignCenter]} /> now causes a type error: textAlignVertical: 'center' is not a valid style for View. I actually intended to use itemsCenter.

I ended up using module-aliasing to trample the "real" react-native-tailwindcss with my static file:

tsconfig.json

{
  "compilerOptions": {
    "paths": {
      "react-native-tailwindcss": ["rn-tailwind.ts"]

babel.config.js

module.exports = {
  plugins: [
    ['module-resolver', {
      root: ['.'],
      alias: {
        'react-native-tailwindcss': ['./rn-tailwind.ts'],
      },
    }],

Then, all my existing imports worked, with the enhanced typing (and my custom keys like t.bgBrand) import { t } from 'react-native-tailwindcss'; import { t, color } from 'react-native-tailwindcss'; I never import { theme }, but should probably handle that too.

davidgovea commented 4 years ago

Started investigating the performance characteristics of static-vs-runtime tailwind.

Created 2 fresh expo apps, one with the current runtime generated tailwind, and one with a single static-file version (using the default tailwind config). Evaluated both with react-native-bundle-visualizer.

Static-generated react-native-tailwindcss Overall bundle size: 923.94 KB

Runtime-generated: Overall bundle size: 945.34 KB

Those numbers don't add up perfectly -- it's not clear to me where the additional bundle size is coming from in the runtime-generated mode.


Learnings:

Thoughts:


It seems to me that pre-generation is the best (only?) way to get TypeScript definitions for custom tailwind configs. So, if we're pre-generating types, why not also pre-generate the style object?

@TVke would be interested in your thoughts here. This would be a significant change to the approach taken by this library.

One way we could keep current behavior, but allow opt-in to pregeneration, is using a postinstall hook:

npx react-native-tailwindcss-generate could consume the project's tailwind.config.js file and trample the "main" node_modules/react-native-tailwindcss/index.js & index.d.ts files.

This could be added to a project's "postinstall" hook to ensure the pregenerated file is always present and up-to-date. No babel/tsconfig path hacks needed.


This is shaping up to suit my needs perfectly. If you agree, I will continue working on this in this repo. Otherwise, I could make a new project that depends on rn-tailwindcss and performs this generation separately. In any case, definitely want to leverage the great work you've done on adapting tailwind styles to RN.

Thanks for reading!

TVke commented 4 years ago

Hi @davidgovea

I would love the autocomplete to work as it should so I think you are heading in a good direction. I was a bit concerned about the need for building but things like that are easily implemented in the build commands 😊

greetings Thomas