Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 35 additions & 86 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ An experimental, comprehensive port of React Native to the web.
Native on mobile, all of your react components/app logic are run in web
worker, leaving the main thread to entirely focus on rendering.
- **Same layout behavior as React Native on mobile:** Powered by custom bindings
to Yoga and compiled to WebAssembly, avoid layout inconsistencies between
your native and web projects.
to Yoga and compiled to WebAssembly, avoid layout inconsistencies between your
native and web projects.
- **Built with the same bundler used for existing React Native platforms:**
Build both the "native" main and JS threads with the Metro Bundler along with
all the developer experience features it provides.
- **Ecosystem compatible escape hatch to the DOM:** Using the same native module
bridge, expose DOM-specific APIs in a more generic way that can easily be made
into a cross-platform module.

To see it in action, check out these live demos:
To see it in action, check out these live demos: (TODO: demos are not updated
with latest bundles)

- [Movies Demo](https://rndom-movie-demo.now.sh)
- [RNTester (component playground used for manual testing)](https://rntester.now.sh)
Expand All @@ -32,10 +33,13 @@ For the best introduction to this project, check out

## **WARNING**

This project is still highly experimental and many aspects of it are subject to
This project is in experimental stage and many aspects of it are subject to
breaking changes, continue at your own risk.

---
please note: current version of react-native-dom is compatible only with
~0.62.\* of react-native.

## ex : react-native init [project name] --version 0.62.2

## Getting Started

Expand All @@ -54,34 +58,32 @@ yarn global add react-native-cli
Next, initialize your React Native project.

```
react-native init [project name]
react-native init [project name] --version 0.62.2
```

Then, `cd` into your project and install `rnpm-plugin-dom` into your
`devDependencies`, after which you can initialize your React Native DOM
scaffolding with the `react-native dom` command.
Next, Install react-native-dom-init CLI Globally

```
npm install --save-dev rnpm-plugin-dom
npm install -g react-native-dom-init
# or
yarn add --dev rnpm-plugin-dom

# Add DOM support to your React Native project
react-native dom
yarn global add react-native-dom-init
```

Then, `cd` into your project and run command `react-native-dom-init` after which
react-native project will be initialized to react-native-dom

To run your initialized project in your browser, you can either:

- Start the packager yourself using `react-native start` and navigate open your
browser to `localhost:8081/dom`
- Leverage the built-in rnpm command `react-native run-dom` which will start the
packager and open the browser to the correct URL for you
- Leverage the built-in react-native cli command `react-native run-dom` which
will start the packager and open the browser to the correct URL for you

**NOTE:** After setting up the DOM platform you may need to run
`react-native start` with the `--reset-cache` flag at least once if you recieve
an error message like `Unable to resolve module AccessibilityInfo`.

### Overview of files generated by the RNPM plugin
### Overview of files generated by the react-native-dom-init cli

- `dom/bootstrap.js` - Entry point to the main thread bundle where you can set
runtime configuration options, register custom native modules, or any other JS
Expand All @@ -90,10 +92,6 @@ an error message like `Unable to resolve module AccessibilityInfo`.
importing your App's entry point from the top-level folder of your project.
- `dom/index.html` - HTML file which is what references and loads the JS
bundles.
- (conditionally) `rn-cli.config.js` - Depending on if the project already has
one, the rnpm plugin will either create it with the proper configuration
options to support the DOM platform or will simply add the necessary entries
to your existing one.

---

Expand All @@ -119,41 +117,17 @@ end of your development url (e.g. localhost:8081/dom?devtools)

## Building for Production

A built-in script for performing a production build is still in the backlog but
here is a manual script which does so (assuming the same directory structure
that gets generated from the rnpm plugin).

```shell
# Ensure development-speecific code is stripped from the bundle
export NODE_ENV=production

# Make the dist directory, or the build command below will fail.
mkdir -p ./dom/dist

# Build the main thread bundle
react-native bundle \
--config $(pwd)/rn-cli.config.js \
--dev false \
--platform dom \
--entry-file ./dom/bootstrap.js \
--assets-dest ./dom/dist \
--bundle-output ./dom/dist/bootstrap.bundle

# Build the JS thread bundle
react-native bundle \
--config $(pwd)/rn-cli.config.js \
--dev false \
--entry-file ./dom/entry.js \
--platform dom \
--bundle-output ./dom/dist/entry.bundle \
--assets-dest ./dom/dist

# Copy the index.html file to the build destination
cp dom/index.html dom/dist/index.html
A built-in script for performing a production build is implemented and can be
invoked using

```
npm buildDom
or
yarn buildDom
```

The resulting folder in `dom/dist` will contain static HTML & JS ready to be
deployed to your provider of choice.
deployed to your hosting provider of choice.

---

Expand All @@ -164,55 +138,30 @@ deployed to your provider of choice.
The API for this is going to be overhauled soon with accompanying documentation.
If you want to see what it currently looks like take a look at some of the built
in native modules such as
[AsyncLocalStorage](../master/packages/react-native-dom/ReactDom/modules/RCTAsyncLocalStorage.js)
[AsyncLocalStorage](../master/packages/react-native-dom-renderer/ReactDom/modules/RCTAsyncLocalStorage.js)

---

## Repository Structure

This project is a lerna-managed monorepo with all the projects living in the
`packages` folder.
This project is a workspaces managed monorepo with all the projects living in
the `packages` folder and Library in `react-native-dom` folder.

### Package Overview

- `react-native-dom` - The library itself (this is most likely the package
you're interested in).
- `rnpm-plugin-dom` - RNPM plugin primarily used for bootstrapping DOM support
into a React Native project.
- `react-native-dom-cli` - CLI to build and run React Native for dom apps
- `react-native-dom-init` - CLI to bootstrap the addition of the dom platform to
react-native projects
- `react-native-dom-renderer` - Browser Implementation of react-native-dom
components
- `rndom-*` - Custom web components (built with
[svelte](https://svelte.technology)) used for some of the built-in
widgets/views in `react-native-dom`.

One noticeable omission to the list of packages is the custom build of Yoga
which can be found in
[this separate repo](https://github.com/vincentriemer/yoga-dom). `yoga-dom` is
not included in this monorepo due to requiring a significantly different build
environment than this repo's entirely JS codebase.

---

### Running RNTester/Examples

To run the examples located in the `react-native-dom` source, run the following
commands from the root of the monorepo:

```sh
# be sure to update the git submodules to pull the RNTester code
git submodule update --init

# install dependencies
yarn && yarn compile

# start the react-native packager
yarn run-examples
```

Then navigate to `localhost:8081/Examples` and choose which example you would
like to see.

A live deployment of the RNTester project (used primarily for manually testing
changes) can be found at [rntester.now.sh](https://rntester.now.sh)

## Contributors

Thanks goes to these wonderful people
Expand Down
3 changes: 3 additions & 0 deletions packages/react-native-dom-cli/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["env"]
}
4 changes: 4 additions & 0 deletions packages/react-native-dom-cli/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
.npmrc
build
lib
39 changes: 39 additions & 0 deletions packages/react-native-dom-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "react-native-dom-cli",
"version": "1.0.0",
"description": "CLI for react-native-dom",
"main": "build/index.js",
"homepage": "https://github.com/vincentriemer/react-native-dom",
"bugs": {
"url": "https://github.com/vincentriemer/react-native-dom/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/vincentriemer/react-native-dom.git"
},
"license": "MIT",
"scripts": {
"compile": "babel src -d build",
"watch": "watch \"yarn compile\" src"
},
"files": [
"build"
],
"keywords": [
"react-native",
"react-native-dom",
"react-native-dom-cli"
],
"devDependencies": {
"babel": "^6.23.0",
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.7.0",
"watch": "^1.0.2"
},
"dependencies": {
"chalk": "^4.1.0",
"fs-extra": "^9.0.1",
"opn": "^6.0.0",
"wait-port": "^0.2.9"
}
}
95 changes: 95 additions & 0 deletions packages/react-native-dom-cli/src/buildDom/buildDom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const chalk = require("chalk");

const generateBundles = (loc) => {
const source = path.resolve(loc, "../")
copyFiles(source, loc)
editFiles(loc);
executeBundleCommands(loc);
clearBundleFiles(loc);
}

const cleanDistFolder = (loc) => {
const files = fs.readdirSync(loc);
console.log(`cleaning ${chalk.yellow(loc.substring(loc.lastIndexOf("/")))} folder`)
for (const file of files) {
let stats = fs.statSync(`${loc}/${file}`);
if (stats.isDirectory()) {
cleanDistFolder(`${loc}/${file}`)
} else {
fs.unlinkSync(`${loc}/${file}`);
}
}
}

const copyFiles = (source, dest) => {
console.log(`copying files from ${chalk.yellow("dom")} folder to ${chalk.yellow("dist")} folder`)
const files = fs.readdirSync(source);
for (const file of files) {
if (file !== "dist") {
fs.copyFileSync(`${source}/${file}`, `${dest}/${file}`);
}
}
}

const editFiles = (loc) => {
const files = fs.readdirSync(loc);
console.log(`preparing files for bundling`);
for (const file of files) {
let stats = fs.statSync(`${loc}/${file}`);
if (stats.isDirectory()) {
editFiles(`${loc}/${file}`)
} else {
let data = fs.readFileSync(`${loc}/${file}`, 'utf-8');
data = data.replace(/.bundle/g, ".bundle.js");
fs.writeFileSync(`${loc}/${file}`, data, 'utf-8');
}
}
}

const executeBundleCommands = (loc) => {
const bootstrapEntry = `${loc}/bootstrap.js`;
const bootstrapBundle = `${loc}/bootstrap.bundle.js`;
const appEntry = `${loc}/entry.js`;
const appBundle = `${loc}/entry.bundle.js`;
console.log(`bundling ${chalk.yellow("bootstrap.js")}`);
execSync(`react-native bundle --dev false --platform dom --entry-file ${bootstrapEntry} --bundle-output ${bootstrapBundle} --assets-dest ${loc}`);
console.log(`bundling ${chalk.green("bootstrap.js")} complete`);
console.log(`bundling ${chalk.yellow("entry.js")}`);
execSync(`react-native bundle --dev false --platform dom --entry-file ${appEntry} --bundle-output ${appBundle} --assets-dest ${loc}`);
console.log(`bundling ${chalk.green("entry.js")} complete`);
}

const clearBundleFiles = (loc) => {
console.log(`getting bundle ready for deployment`);
const files = fs.readdirSync(loc);
for (const file of files) {
if (file !== "assets" && !file.match("bundle.js") && !file.match("index.html")) {
fs.unlinkSync(`${loc}/${file}`);
}
}
}
const initiateBundling = () => {
const distLoc = `${process.cwd()}/dom/dist`;
if (!fs.existsSync(distLoc)) {
console.log(`creating dist directory at location ${chalk.green(distLoc)}`);
fs.mkdirSync(distLoc);
} else {
cleanDistFolder(distLoc);
}
generateBundles(distLoc)
console.log(chalk.green(`bundling completed`));
};

function buildDom(){
process.env.NODE_ENV="production";
initiateBundling();
}

module.exports = {
name: "build-dom",
description: "bundles app and dom native code for deployment",
func: buildDom
};
3 changes: 3 additions & 0 deletions packages/react-native-dom-cli/src/generator-dom/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const copyTemplate = () => {

}
26 changes: 26 additions & 0 deletions packages/react-native-dom-cli/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as path from "path";

import * as fse from "fs-extra";

import * as fs from 'fs';

import runDom from './runDom/runDom';

import buildDom from './buildDom/buildDom';

export function generateDom(projectDir, name) {

const templatePath = path.dirname(
require.resolve("react-native-dom/package.json", {
paths: [projectDir]
})
);

fse.copySync(`${templatePath}\\dom`, `${projectDir}\\dom`);
fs.copyFileSync(`${templatePath}/dom/_npmrc`, `${projectDir}/.npmrc`)
}

export const commands = [runDom, buildDom];
// export const dependencyConfig = dependencyConfigDom;
// export const projectConfig = projectConfigDom;

Loading