diff --git a/packages/xarc-create-app/template/README.md b/packages/xarc-create-app/template/README.md index be3194732..6874be5d9 100644 --- a/packages/xarc-create-app/template/README.md +++ b/packages/xarc-create-app/template/README.md @@ -1,6 +1,6 @@ # my-x-app -Welcome to your web application using Electrode X. +Welcome to your React application using Electrode X. ## Development @@ -14,30 +14,28 @@ Some initial things to do and try: - First install dependencies - ``` - npm install - ``` +``` +npm install +``` - - Then to develop your app, do +- Then to develop your app, do - ``` - npm run dev - ``` +``` +npm run dev +``` - - Once App is running, point browser to +- Once App is running, point browser to -3. Try adding some simple text to `home/subapp-home.js` or `demo1/subapp-demo1.js`. +3. Try adding some simple text to `src/home/index.ts` or `src/demo1/index.ts`. 4. Create some React components and add them to the home or demo subapp. -5. Enable some optional features in `archetype/config/index.js`. +5. Enable some optional features in `xclap.ts` when calling `loadXarcDevTasks`. -6. Read up on a quick intro to the [Electrode SubApp architecture](https://github.com/electrode-io/electrode/blob/master/samples/poc-subapp/README.md). +6. Create a repo and push your app to , and update `repository` in `package.json`. -7. Create a repo and push your app to , and update `repository` in `package.json`. - -8. Contribute to the [Electrode Platform](https://github.com/electrode-io/electrode/blob/master/CONTRIBUTING.md). +7. Contribute to the [Electrode Platform](https://github.com/electrode-io/electrode/blob/master/CONTRIBUTING.md). ## Resources -- Check Electrode docs at +- Check Electrode docs at diff --git a/packages/xarc-create-app/template/_package.js b/packages/xarc-create-app/template/_package.js index 60a919b67..d55bd8e51 100644 --- a/packages/xarc-create-app/template/_package.js +++ b/packages/xarc-create-app/template/_package.js @@ -30,22 +30,16 @@ module.exports = (base, merge) => { npm: ">= 6" }, dependencies: { - "@xarc/app": "^8.1.8", + "@xarc/app": "^8.2.0", "@xarc/fastify-server": "^2.0.0", - react: "^16.13.1", - "react-dom": "^16.13.1", - redux: "^4.0.5", - "react-redux": "^7.2.0", - "subapp-react": "^0.0.23", - "subapp-redux": "^1.0.32", - "subapp-server": "^1.3.1" + "@xarc/react": "^0.1.0", + "@xarc/react-redux": "^0.1.0" }, devDependencies: { "@types/node": "^14.14.6", - "@xarc/app-dev": "^8.1.8", + "@xarc/app-dev": "^8.2.0", "ts-node": "^9.0.0", - typescript: "^4.0.3", - xclap: "^0.2.53" + typescript: "^4.0.3" } }; diff --git a/packages/xarc-create-app/template/src/app.tsx b/packages/xarc-create-app/template/src/app.tsx new file mode 100644 index 000000000..b20594a61 --- /dev/null +++ b/packages/xarc-create-app/template/src/app.tsx @@ -0,0 +1,13 @@ +import { declareSubApp, xarcV2 } from "@xarc/react"; + +export const home = declareSubApp({ + name: "home", + getModule: () => import("./home") +}); + +export const Demo2 = declareSubApp({ + name: "demo2", + getModule: () => import("./demo2") +}); + +xarcV2.debug("app.tsx"); diff --git a/packages/xarc-create-app/template/src/demo1/index.tsx b/packages/xarc-create-app/template/src/demo1/index.tsx new file mode 100644 index 000000000..34e9e742a --- /dev/null +++ b/packages/xarc-create-app/template/src/demo1/index.tsx @@ -0,0 +1,17 @@ +import { React, ReactSubApp } from "@xarc/react"; + +const Demo1 = props => { + return ( +
+

subapp demo1

+ props: {JSON.stringify(props)} +

+ Electrode Docs +

+
+ ); +}; + +export const subapp: ReactSubApp = { + Component: Demo1 +}; diff --git a/packages/xarc-create-app/template/src/demo1/subapp-demo1.tsx b/packages/xarc-create-app/template/src/demo1/subapp-demo1.tsx deleted file mode 100644 index dc2308279..000000000 --- a/packages/xarc-create-app/template/src/demo1/subapp-demo1.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { React, loadSubApp } from "subapp-react"; - -const Demo1 = props => { - return ( -
-

subapp demo1

- props: {JSON.stringify(props)} -

- Electrode Docs -

-
- ); -}; - -export default loadSubApp({ - Component: Demo1, - name: "Demo1", - prepare: () => { - return { data: "hello from demo1" }; - } -}); diff --git a/packages/xarc-create-app/template/src/demo2/index.tsx b/packages/xarc-create-app/template/src/demo2/index.tsx new file mode 100644 index 000000000..72af6a81e --- /dev/null +++ b/packages/xarc-create-app/template/src/demo2/index.tsx @@ -0,0 +1,65 @@ +// +// A more complicate demo subapp using Redux +// +// Note: using redux requires top level Redux store initialization so if another +// subapp tries to use this as a dynamic component, then it must also uses redux and +// provides the redux top level store facility. +// + +import { React, ReactSubApp } from "@xarc/react"; +import { reduxFeature, connect } from "@xarc/react-redux"; +export { reduxReducers } from "./reducers"; + +const incNumber = () => { + return { + type: "INC_NUMBER" + }; +}; + +const decNumber = () => { + return { + type: "DEC_NUMBER" + }; +}; + +const Demo2 = props => { + const { value, dispatch } = props; + + return ( +
+
+

subapp demo2 with Redux

+ Redux State Demo: +  {value}  + +
+

© {new Date().getFullYear()} Your (Company) name here

+
+ ); +}; + +const mapStateToProps = state => { + return { value: state.number.value }; +}; + +export const subapp: ReactSubApp = { + Component: connect(mapStateToProps, dispatch => ({ dispatch }))(Demo2), + wantFeatures: [ + reduxFeature({ + React, + shareStore: true, + reducers: true, // true => read the reduxReducers export from this file + prepare: async initialState => { + return { initialState: initialState || { number: { value: 999 } } }; + } + }) + ] +}; diff --git a/packages/xarc-create-app/template/src/demo2/reducers.ts b/packages/xarc-create-app/template/src/demo2/reducers.ts index e9c284a95..1c885bc4a 100644 --- a/packages/xarc-create-app/template/src/demo2/reducers.ts +++ b/packages/xarc-create-app/template/src/demo2/reducers.ts @@ -12,4 +12,6 @@ const number = (store = { value: 0 }, action) => { return store; }; -export default number; +export const reduxReducers = { + number +}; diff --git a/packages/xarc-create-app/template/src/demo2/subapp-demo2.tsx b/packages/xarc-create-app/template/src/demo2/subapp-demo2.tsx deleted file mode 100644 index 0991107cc..000000000 --- a/packages/xarc-create-app/template/src/demo2/subapp-demo2.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { React } from "subapp-react"; -import { connect } from "react-redux"; -import { reduxLoadSubApp } from "subapp-redux"; -import reduxReducers from "./reducers"; - -const incNumber = () => { - return { - type: "INC_NUMBER" - }; -}; - -const decNumber = () => { - return { - type: "DEC_NUMBER" - }; -}; - -const Demo2 = props => { - const { value, dispatch } = props; - - return ( -
-
-

subapp demo2

- Redux State Demo: -  {value}  - -
-

© {new Date().getFullYear()} Your (Company) name here

-
- ); -}; - -const mapStateToProps = state => state; - -export default reduxLoadSubApp({ - Component: connect(mapStateToProps, dispatch => ({ dispatch }))(Demo2), - name: "Demo2", - reduxReducers, - prepare: ({ context, request }) => { - return Promise.resolve({ value: 999 }); - } -}); diff --git a/packages/xarc-create-app/template/src/home/index.tsx b/packages/xarc-create-app/template/src/home/index.tsx new file mode 100644 index 000000000..83f987b44 --- /dev/null +++ b/packages/xarc-create-app/template/src/home/index.tsx @@ -0,0 +1,36 @@ +import { React, ReactSubApp, createDynamicComponent, staticPropsFeature } from "@xarc/react"; +import electrodePng from "../../static/electrode.png"; +import { message } from "./message"; + +export const Demo1 = createDynamicComponent( + { + name: "demo1", + getModule: () => import("../demo1") + }, + { ssr: true } +); + +const Home = props => { + return ( +
+

+ + Electrode + +

+

{message}

+

props: {JSON.stringify(props)}

+

Demo1 subapp as a dynamic component in Home

+ +
+ ); +}; + +export const subapp: ReactSubApp = { + Component: Home, + wantFeatures: [ + staticPropsFeature({ + serverModule: require.resolve("./static-props") + }) + ] +}; diff --git a/packages/xarc-create-app/template/src/home/message.ts b/packages/xarc-create-app/template/src/home/message.ts new file mode 100644 index 000000000..e9b738d27 --- /dev/null +++ b/packages/xarc-create-app/template/src/home/message.ts @@ -0,0 +1 @@ +export const message = "Welcome to xarc React Application"; diff --git a/packages/xarc-create-app/template/src/home/static-props.tsx b/packages/xarc-create-app/template/src/home/static-props.tsx new file mode 100644 index 000000000..71e62c354 --- /dev/null +++ b/packages/xarc-create-app/template/src/home/static-props.tsx @@ -0,0 +1,11 @@ +const maxDelay = 50; + +export async function getStaticProps() { + const delay = Math.floor(Math.random() * maxDelay); + return new Promise(resolve => { + return setTimeout( + () => resolve({ props: { message: "Hello from static props", delay } }), + delay + ); + }); +} diff --git a/packages/xarc-create-app/template/src/home/subapp-home.tsx b/packages/xarc-create-app/template/src/home/subapp-home.tsx deleted file mode 100644 index 847894748..000000000 --- a/packages/xarc-create-app/template/src/home/subapp-home.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { React, loadSubApp } from "subapp-react"; -import electrodePng from "../../static/electrode.png"; - -const Home = () => { - return ( -

- Hello from{" "} - - Electrode - -

- ); -}; - -export default loadSubApp({ Component: Home, name: "Home" }); diff --git a/packages/xarc-create-app/template/src/server/config.ts b/packages/xarc-create-app/template/src/server/config.ts index 2480c0fd4..5c1d6618f 100644 --- a/packages/xarc-create-app/template/src/server/config.ts +++ b/packages/xarc-create-app/template/src/server/config.ts @@ -24,21 +24,7 @@ export const config = { "@xarc/app-dev": { priority: -1, enable: process.env.WEBPACK_DEV === "true" - }, - /** - * Register the server routes plugin for the app - */ - "subapp-server": { - options: { - cdn: { - /** - * Enable CDN in production mode. To try this locally, do: - * 1. npm run build - * 2. NODE_ENV=production clap mock-cloud - */ - enable: process.env.NODE_ENV === "production" - } - } } - } + }, + deferStart: true }; diff --git a/packages/xarc-create-app/template/src/server/index.ts b/packages/xarc-create-app/template/src/server/index.ts index ec5bb800d..b73d7ba22 100644 --- a/packages/xarc-create-app/template/src/server/index.ts +++ b/packages/xarc-create-app/template/src/server/index.ts @@ -1,10 +1,53 @@ -import { config } from "./config"; -const support = require("@xarc/app/support"); +import { PageRenderer } from "@xarc/react"; +import { load } from "@xarc/app/support"; const electrodeServer = require("@xarc/fastify-server"); +import { config } from "./config"; +import { Demo2, home } from "../app"; + async function start() { - await support.load(); - await electrodeServer(config); + await load({ + isomorphicCdnOptions: { + prodOnly: true + } + }); + const server = await electrodeServer(config); + + let homeRenderer: PageRenderer; + + server.route({ + method: "GET", + path: "/", + async handler(request, reply) { + try { + if (!homeRenderer) { + homeRenderer = new PageRenderer({ + pageTitle: "xarc React App demo", + subApps: [ + { name: home.name, ssr: true }, + { name: Demo2.name, ssr: true } + ], + prodAssetData: { + cdnMap: "config/assets.json" + } + }); + } + const context = await homeRenderer.render({ request }); + reply.type("text/html"); + + if (context.user.cspHeader) { + reply.header(`content-security-policy`, context.user.cspHeader); + } + + reply.send(context.result); + } catch (error) { + reply.send(error.stack); + } + } + }); + + server.start(); + return server; } start(); diff --git a/packages/xarc-create-app/template/xclap.ts b/packages/xarc-create-app/template/xclap.ts index 4be381b9f..8d09b0791 100644 --- a/packages/xarc-create-app/template/xclap.ts +++ b/packages/xarc-create-app/template/xclap.ts @@ -1,5 +1,4 @@ -import { loadXarcDevTasks } from "@xarc/app-dev/lib/dev-tasks"; -import xclap from "xclap"; +import { loadXarcDevTasks, xclap } from "@xarc/app-dev/lib/dev-tasks"; xclap.updateEnv( {