Skip to content

Commit

Permalink
Added redux-router from webpack-typescript-builder
Browse files Browse the repository at this point in the history
  • Loading branch information
offbeatful committed Apr 30, 2020
1 parent fbf5348 commit 99c34c8
Show file tree
Hide file tree
Showing 21 changed files with 488 additions and 73 deletions.
17 changes: 11 additions & 6 deletions examples/router-redux/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import { Helmet } from "react-helmet-async";

import { connect } from "react-redux";
import { ApplicationState } from "./store";

interface AppProps {
counter: number;
Expand All @@ -11,7 +12,7 @@ interface AppDispatch {
decrement: () => void;
}

class AppInternal extends React.Component<AppProps & AppDispatch, any> {
class AppInternal extends React.Component<AppProps & AppDispatch, ApplicationState> {
public render(): JSX.Element {
return (
<div>
Expand All @@ -28,13 +29,17 @@ class AppInternal extends React.Component<AppProps & AppDispatch, any> {
}
}

const App = connect<AppProps, AppDispatch, {}, any>(
state => ({
const App = connect<AppProps, AppDispatch, {}, ApplicationState>(
(state) => ({
counter: state.counter,
}),
dispatch => ({
decrement: () => dispatch({ type: "DECREMENT" }),
increment: () => dispatch({ type: "INCREMENT" }),
(dispatch) => ({
decrement: (): void => {
dispatch({ type: "DECREMENT" });
},
increment: (): void => {
dispatch({ type: "INCREMENT" });
},
}),
)(AppInternal);

Expand Down
4 changes: 2 additions & 2 deletions examples/router-redux/store.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { RouterState } from "connected-react-router";
import * as Redux from "redux";

interface ApplicationState {
export interface ApplicationState {
router?: RouterState;
counter: number;
}

const counterReducer = (state = 1, action: Redux.Action) => {
const counterReducer = (state = 1, action: Redux.Action): number => {
switch (action.type) {
case "INCREMENT":
return state + 1;
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/prettier.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
useTabs: false,
printWidth: 80,
printWidth: 90,
tabWidth: 2,
singleQuote: false,
trailingComma: "all",
Expand Down
4 changes: 2 additions & 2 deletions packages/react-app-basic/lib/app.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as React from "react";
import { HelmetWrapper, AppProps } from "@mocoding/react-app-common";

import InjectedAppModule from "injected-hmr-entry";
import HmrProxyEntryModule from "@mocoding/react-app-common/lib/entry";

export class App extends React.Component<AppProps> {
public render(): React.ReactNode {
return (
<HelmetWrapper helmetContext={this.props.context.helmetContext}>
<InjectedAppModule />
<HmrProxyEntryModule />
</HelmetWrapper>
);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/react-app-common/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from "./common"
export * from "./components"
export * from "./common";
export * from "./components";
14 changes: 11 additions & 3 deletions packages/react-app-common/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,29 @@ declare module "*.gif" {
}

declare module "injected-bootstrap-module" {
import { AppProps, ContextFactoryFunc } from "@mocoding/react-app-common";
const createContext: ContextFactoryFunc;
const App: React.ComponentType<AppProps>;
}

// TODO: Once Node 13.2+ is there this can be changed to "injected-bootstrap-module/render"
// I don't like specific assumptions that bootstrap module contains lib folder.
declare module "injected-bootstrap-module/lib/render" {
import { RenderFunc } from "@mocoding/react-app-common";
const render: RenderFunc;
}

declare module "injected-bootstrap-module/lib/middlewares" {
import { RenderFunc } from "@mocoding/react-app-common";
const render: RenderFunc;
}

declare module "injected-app-entry" {
import { AppProps } from "@mocoding/react-app-common";
export const App: React.ComponentType<AppProps>;
}

declare module "injected-hmr-entry" {
const EntryPoint: React.ComponentType<AppProps>;
export = EntryPoint;
declare module "@mocoding/react-app-common/lib/entry" {
const HmrProxyEntryModule: React.ComponentType;
export = HmrProxyEntryModule;
}
20 changes: 20 additions & 0 deletions packages/react-app-router-redux/lib/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ConnectedRouter } from "connected-react-router";
import * as React from "react";
import { Provider } from "react-redux";
import { AppProps, HelmetWrapper } from "@mocoding/react-app-common";

import HmrProxyEntryModule from "@mocoding/react-app-common/lib/entry";

export class App extends React.Component<AppProps> {
public render(): React.ReactNode {
return (
<HelmetWrapper helmetContext={this.props.context.helmetContext}>
<Provider store={this.props.context.store}>
<ConnectedRouter history={this.props.context.history}>
<HmrProxyEntryModule />
</ConnectedRouter>
</Provider>
</HelmetWrapper>
);
}
}
71 changes: 71 additions & 0 deletions packages/react-app-router-redux/lib/createContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { connectRouter, RouterState, LocationChangeAction } from "connected-react-router";
import { routerMiddleware } from "connected-react-router";
import { createBrowserHistory, History } from "history";
import * as Redux from "redux";
import { Context } from "@mocoding/react-app-common";

import { middlewares, reducers } from "injected-app-entry/store";
import devMiddlewares from "@mocoding/react-app-router-redux/lib/middlewares";

export interface ReduxRouterState {
// Allows any extra properties to be defined in an action.
[extraProps: string]: unknown;
router: RouterState<unknown>;
}

function createRootReducer<TState extends ReduxRouterState>(
history: History<unknown>,
appReducers: Redux.Reducer[],
): Redux.Reducer<TState> {
// https://github.com/reduxjs/redux/pull/3679
// Once the issue is fixed - we may remove casting to any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const reducersMap: any = {
...appReducers,
router: connectRouter(history),
};
return Redux.combineReducers<TState, Redux.AnyAction>(reducersMap);
}

function configureStore<TState extends ReduxRouterState>(
history: History<unknown>,
initialState?: Redux.PreloadedState<TState>,
): Redux.Store<TState, Redux.AnyAction> {
// Create reducers from root
const rootReducers = createRootReducer<TState>(history, reducers);

// Adding logger and router to middlewares
const isSsr = typeof window === "undefined";
const defaultMiddlewares: Redux.Middleware[] = !isSsr ? devMiddlewares : [];
defaultMiddlewares.push(routerMiddleware(history));

const pipeline = Redux.applyMiddleware(...defaultMiddlewares, ...middlewares);

// create store
const store = Redux.createStore(rootReducers, initialState, Redux.compose(pipeline));

if (module.hot) {
module.hot.accept(["injected-app-entry/store"], () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newStore = require("injected-app-entry/store");
const nextReducer = createRootReducer<TState>(history, newStore.reducers);

store.replaceReducer(nextReducer);
});
}

return store;
}

export function createContext(abstractHistory?: History): Context {
const initialState =
typeof window === "undefined" ||
typeof (window as any).__PRELOADED_STATE__ === "undefined"
? undefined
: JSON.parse((window as any)?.__PRELOADED_STATE__);
const history = abstractHistory ?? createBrowserHistory();
return {
history,
store: configureStore(history, initialState),
};
}
2 changes: 2 additions & 0 deletions packages/react-app-router-redux/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./createContext";
export * from "./app";
3 changes: 3 additions & 0 deletions packages/react-app-router-redux/lib/middlewares/index.dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { logger } from "redux-logger";

export default [logger];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default [];

This file was deleted.

37 changes: 37 additions & 0 deletions packages/react-app-router-redux/lib/render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createMemoryHistory } from "history";
import * as React from "react";
import { renderToStaticMarkup, renderToString } from "react-dom/server";
import {
Context,
RenderCallback,
RenderFuncProps,
HelmetHtml,
HelmetHtmlProps,
} from "@mocoding/react-app-common";
import { App } from "./app";
import { createContext } from "./createContext";

export function render(callback: RenderCallback, props: RenderFuncProps): void {
try {
const history = createMemoryHistory();
history.replace(props.requestUrl);
const context: Context = createContext(history);
context.helmetContext = {}; // init helmet for ssr
const markup = renderToString(<App context={context} />);

const htmlProps: HelmetHtmlProps = {
assets: props.assets,
context,
inlineScripts: props.inlineScripts,
markup,
};

const html = "<!DOCTYPE html>" + renderToStaticMarkup(<HelmetHtml {...htmlProps} />);

callback(undefined, {
html,
});
} catch (error) {
callback(error);
}
}
16 changes: 14 additions & 2 deletions packages/react-app-router-redux/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"keywords": [],
"author": "Dennis Miasoutov <dennis.miasoutov@mocoding.com>",
"license": "ISC",
"main": "lib/mocoding-app-router-redux.js",
"main": "lib/index.ts",
"directories": {
"lib": "lib",
"test": "__tests__"
Expand All @@ -14,7 +14,19 @@
"lib"
],
"scripts": {
"build": "echo",
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"@mocoding/react-app-common": "*",
"@types/react-redux": "^7.1.7",
"@types/react-router-dom": "^5.1.5",
"@types/redux-logger": "^3.0.7",
"@types/redux": "^3.6.0",
"@types/webpack-env": "^1.15.2",
"connected-react-router": "^6.8.0",
"react-redux": "^7.2.0",
"react-router-dom": "^5.1.2",
"redux-logger": "^3.0.6",
"redux": "^4.0.5"
}
}
9 changes: 9 additions & 0 deletions packages/react-app-router-redux/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"esModuleInterop": true,
"jsx": "preserve",
"noEmit": true,
"strict": true
},
"include": ["lib/**/*", "./typings.d.ts", "../react-app-common/typings.d.ts"]
}
11 changes: 11 additions & 0 deletions packages/react-app-router-redux/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
declare module "@mocoding/react-app-router-redux/lib/middlewares" {
import { Middleware } from "redux";
const middlewares: Middleware[];
export default middlewares;
}

declare module "injected-app-entry/store" {
import { Middleware, Reducer } from "redux";
export const middlewares: Middleware[];
export const reducers: Reducer[];
}
22 changes: 17 additions & 5 deletions packages/react-app/src/cli/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface Options {
outputClientPath: string;
outputServerPath: string;
bootstrapModule: string;
appRoot: string;
appEntry: string;
// devApiUrl: string;
}

Expand All @@ -17,8 +17,20 @@ export function registerCommand(command: string, description: string): program.C
.description(description)
.option("-p, --production", "build for production", false)
.option("-a, --analyze", "enable bundle analyzer", false)
.option("-r, --appRoot [path]", "specify application root", ".")
.option("-m, --bootstrapModule [path]", "sets bootstrap module", "@mocoding/react-app-basic")
.option("-oc, --outputClientPath [path]", "sets output path for client files", "./wwwroot")
.option("-os, --outputServerPath [path]", "sets output path for server files. module", "./wwwroot_node");
.option("-e, --appEntry [path]", "specify application root", ".")
.option(
"-b, --bootstrapModule [path]",
"sets bootstrap module",
"@mocoding/react-app-basic",
)
.option(
"-oc, --outputClientPath [path]",
"sets output path for client files",
"./wwwroot",
)
.option(
"-os, --outputServerPath [path]",
"sets output path for server files. module",
"./wwwroot_node",
);
}
Loading

0 comments on commit 99c34c8

Please sign in to comment.