Universal package.json

roppongi.js #6 Leko

Leko

Node.js, React, React Native, React Native for web

TL;DR

package.jsonにreact-nativeフィールドを定義すると、React Native(metro)でのみ反応するエントリポイントを定義できる。
React Nativeも見据えたUniversalなパッケージ作りに可能性を感じるが、ただし使い所が難しい。

with React Native

import React from 'react'
import styled from 'styled-components'
const StyledView = styled.View`
background-color: papayawhip;
`
const StyledText = styled.Text`
color: palevioletred;
`
const MyReactNativeComponent = () => (
<StyledView>
<StyledText>Hello World!</StyledText>
</StyledView>
)

Require from anywhare...?

package.json

{
"name": "styled-components",
"version": "3.4.6",
"description": "...",
"typings": "...",
"main": "...",
"jsnext:main": "dist/styled-components.esm.js",
"module": "dist/styled-components.esm.js",
"react-native": "dist/styled-components.native.cjs.js",
"browser": {
"...": "...",
},
...
}

What are `react-native` and `browser`?

browser

If your module is meant to be used client-side the browser field should be used instead of the main field.

package.json | npm Documentation

src/ModuleGraph/node-haste/Package.js

function getReplacements(pkg) {
let rn = pkg['react-native'];
let browser = pkg.browser;
if (rn == null) {
return browser;
}
if (browser == null) {
return rn;
}
const main = getMain(pkg);
if (typeof rn !== 'object') {
rn = { [main]: rn };
}
if (typeof browser !== 'object') {
browser = { [main]: browser };
}
// merge with "browser" as default,
// "react-native" as override
return { ...browser, ...rn };
}

metro can treat `react-native` and `browser` field in package.json

package.json

{
"name": "styled-components",
"version": "3.4.6",
"description": "...",
"typings": "...",
"main": "dist/styled-components.cjs.js",
"jsnext:main": "dist/styled-components.esm.js",
"module": "dist/styled-components.esm.js",
"react-native": "dist/styled-components.native.cjs.js",
"browser": {
...
"./dist/styled-components.cjs.js": "./dist/styled-components.browser.cjs.js",
...
},
...
}

What are `react-native` and `browser`?

in webpack

{
...
"main": "./dist/styled-components.browser.cjs.js",
...
}

in React Native (metro)

{
...
"main": "dist/styled-components.native.cjs.js",
...
}

react-native field with react-native-web

react-native-web (RN-web)

"React Native for Web" makes it possible to run React Native components and APIs on the web using React DOM

https://github.com/necolas/react-native-web

How react-native-web works

// webpack.config.js
module.exports = {
// ...
resolve: {
alias: {
'react-native$': 'react-native-web'
}
}
}

RNと同一I/FでimportできるUIコンポーネント集。webpackでbundle

context

  • RNでもRN-webでもで動くコンポーネント作りたい
  • styled-componentsでthemingしたい
  • /nativeと書くのはdeprecatedなので/native外す

in React Native for web

import React from 'react'
import styled from 'styled-components'
const StyledView = styled.View`
background-color: papayawhip;
`
const StyledText = styled.Text`
color: palevioletred;
`
const MyReactNativeComponent = () => (
<StyledView>
<StyledText>Hello World!</StyledText>
</StyledView>
)
> const styled = require('styled-components').default
>
> styled.View
undefined
> styled.Text
undefined
>
> styled.span
{ [Function: templateFunction] ... }
> styled.div
{ [Function: templateFunction] ... }
View, Textdiv, span
RN + (no suffix)🙆🙅
RN + /native🙆🙅
RN-web + (no suffix)😭🙆
RN-web + /native🙆🙅

RN-web + (no suffix)が鬼門。
react-nativeをimportしてほしいが、RN-webは環境的にはwebpackなのでbrowserフィールドが優先される

conclusion

  • browser, react-nativeはmetroにおいてmainより優先される
  • RN⇄browserはNode.js⇄browserかそれ以上に難しい
  • Universalなpackage.jsonにするなら、I/Fも環境によらず一定の方が良さそう

END