A way to initial our spa project quickly, base on templates [vue, react, angular]. ##### How to use ``` - spa - webpack / vite / rollup - vue / react / angular - coffeescript ? - pug ? - jsex ? - theme support ?
Single-page generator with React frontend and Spring-boot backend supports options to separately generate parts of an application. It uses plop to generate source code from templates. The generator provides boilerplate for creating PWA applications.
This boilerplate provides an array of features that make it different from other frameworks:
Tool is build for easy generation of single-page applications with usage of best practices in web development. Provides better maintenance, testing, cooperation and deployment.
Install yarn.
npm install -g yarn
Install create-sbspa tool.
npm install -g create-sbspa
After installing create-sbspa tool run
create-sbspa
Options:
Application uses gradle automation build and deployment tool. Project modules are defined in settings.gradle and in build.gradle files.
yarn install && yarn build:dev
yarn install && yarn build
Gradle:
gradle build
yarn start:dev
yarn start
Gradle:
gradle runWeb
Available at localhost:3000.
Build application
gradle build
Run application
gradle runApi
Available at localhost:8080.
GraphQL console is available at localhost:8080/graphiql.
Run tests with yarn
yarn run test
Run tests on changed files
yarn run test:changed
Run tests with coverage
yarn run test:coverage
Update tests
yarn run test:update
Run checkstyle (build/reports/checkstyle.html)
gradle checkstyleMain
Run findbugs (build/reports/findbugs.html)
gradle findbugsMain
Check tests for backend and frontend for more information.
Frontend application uses prepush and precommit hooks.
yarn run test:coverage
Coverage threshold can be set in jest.config.js.
yarn run lint-staged
Static analyzer can be modified in tslint.json.
Deploy frontend or backend with provided docker configuration. In root frontend or backend directory run
docker build . -t <name>:<version>
Main application class with Async route definition. Each page (container/component) can be asynchronously loaded with support of Webpack chunks.
Route definition: SamplePage route with root component in "./shared/components/ReplaceMeComponent".
Important: path is made from Router.tsx component.
export const routes = {
samplePage: () =>
// @ts-ignore
lazy(() =>
import(/* webpackChunkName: "SamplePage" */ "./shared/components/ReplaceMeComponent"),
),
};
Routes are evaluated in Router.tsx class (modules/shared/components/Router.tsx).
You can freely change loading component and other properties of the DynamicRouter component.
Generator provides support for redux and sagas with async injection feature.
Store is defined in common/store/store.tsx. It containes store setup with reducers and sagas. Also includes Redux devtools extension for development.
You can add your reducer directly to store or inject it asynchronously.
Create your reducer myReducer.tsx
import {combineActions, handleActions} from "redux-actions";
const defaultState = "";
const reducer = handleActions(
{
["MY_REDUCER_ACTION"]:
(state, {payload: {value}}) => value,
},
defaultState,
);
export default reducer;
Add it to reducerRegistry reducerRegistry.tsx
import myReducer from "./reducers/myReducer"
...
public getReducers() {
return {
myReducer: myReducer,
...this._reducers};
}
...
Reducers and sagas can by injected asynchronously with implemented helper function in common/store/helpers.tsx.
Example usage of async reducer and saga
Create an app container
import {compose} from "redux";
import * as React from "react";
import {connect} from "react-redux";
import reducer from "./reducers/myReducer";
import {withInjectedReducersAndSagas} from "../../common/store/helpers";
import saga from "./sagas/mySaga";
import {myReducerAction} from "./actions/myActions";
const ExampleContainer = ({value, myReducerAction}) =><div onClick={()=>myReducerAction("click")}>{value}My container!</div>
export default compose(
withInjectedReducersAndSagas({
reducers:[
{
key: "MY_REDUCER",
value: reducer
}
],
sagas:[
{
key: "MY_SAGA",
value: saga
}
]
}),
// after async injection u can easily connect to store
connect(
(state)=>({
value: state["MY_REDUCER"].value
}),
(dispatch)=>({
myReducerAction: value => dispatch(myReducerAction(value))
})
)
)(ExampleContainer);
Reducer definition ./reducers/myReducer
import {combineActions, handleActions} from "redux-actions";
const defaultState = "";
const reducer = handleActions(
{
["MY_REDUCER/MY_REDUCER_ACTION"]:
(state, {payload: {value}}) => value,
},
defaultState,
);
export default reducer;
Saga definition ./sagas/mySaga
import {takeEvery} from "redux-saga/effects";
const watchAction = function* watchAction() {
yield;
};
function* rootSaga() {
yield takeEvery("MY_REDUCER/MY_REDUCER_ACTION", watchAction);
}
export default rootSaga;
Actions definition ./actions/myActions
export const {
myReducer: {myReducerAction},
} = createActions({
["MY_REDUCER"]: {
[`MY_REDUCER_ACTION`]: value => ({value}),
},
});
Resolvers are used for postprocessing of model fields. For blog we can acquire data, which is postprocessing of author object.
Each DAO object contains functions called from Query.
Schema definition with supporoted queries.
This project is licensed under the MIT License.
Toreator - Single-page application with GraphQL proxy for searching IP addresses in TOR network.