This repository was archived by the owner on Mar 30, 2022. It is now read-only.
- Notifications
You must be signed in to change notification settings - Fork1
Example Github Page PWA with NextJs, Redux-toolkit and Sql.js
License
NotificationsYou must be signed in to change notification settings
Project-Setup/github_sql_pwa
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Github Page PWA boilerplate with NextJs, code splitting Redux-Toolkit, Sql.js, Typeorm, Typescript, Eslint, Jest and Emotion.
- Multi-pageReactProgressive Web App
- Installable foroffline use through Chrome on desktop ormobile
- Can be statically hosted onGithub Page for free (or as a regular web app hosted on a custom server)
- Dynamically loadedRedux reducers forcode splitting
- On browserSQL database
- Prefetch security sensitive content at build time
- All inTypescript/Javascript withCSS-in-JS
- Easy testing withJest andEnzyme
- Eslint helps practice standard coding styles
- NextJs v9.4.2
- Redux-Toolkit v1.3.6
- Emotion v10
- Sql.js v1.2.2
- Typeorm v0.2.24
- Typescript v3.9.2
- [Nextjs_Ts_Eslint] NextJs, EmotionJs, Typescript
- [nextjs_redux_toolkit] NextJs, Redux-Toolkit
- [github_pwa] Github page pwa setup with NextJs, code splitting Redux-Toolkit
- setup node env
nvm usenpm install
- remove unwanted files in
public/
,src/
- add
.env
and other .env files - preview dev progress on
http://localhost:3000/
npm run dev
- export to
docs/
for Github Page deploynpm runexport
- readSetup for notes
- install nvm in the os
nvm install nodegit init
- add
.gitignore
node -v> .nvmrc
npm init -y
npm i -S next react react-dom
- add a script to your package.json like this:
{"scripts": {"dev":"next","build":"next build","start":"next start" }}
npm i -D typescript @types/react @types/react-dom @types/node
- create
tsconfig.json
{"compilerOptions": {"allowJs":true,"allowSyntheticDefaultImports":true,"alwaysStrict":true,"esModuleInterop":true,"isolatedModules":true,"jsx":"preserve","lib": ["dom","es2017" ],"module":"esnext","moduleResolution":"node","noEmit":true,"typeRoots": ["./node_modules/@types" ],"noFallthroughCasesInSwitch":true,"noUnusedLocals":true,"noUnusedParameters":true,"resolveJsonModule":true,"removeComments":false,"skipLibCheck":true,"strict":true,"target":"esnext","forceConsistentCasingInFileNames":true,"baseUrl":"./src" },"exclude": ["node_modules","next.config.js" ],"include": ["**/*.ts","**/*.tsx" ]}
- create
src/pages
folder (orpages
) - create
pages.tsx
undersrc/pages/
(i.e.src/pages/index.tsx
for/
route)
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-import-resolver-typescriptnpm i -D eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-import eslint-plugin-react-hooksnpm i -D prettier eslint-config-prettier eslint-plugin-prettier
- create
.eslintrc.js
module.exports={parser:'@typescript-eslint/parser',// Specifies the ESLint parserextends:['plugin:react/recommended',// Uses the recommended rules from@eslint-plugin-react'plugin:@typescript-eslint/recommended',// Uses the recommended rules from@typescript-eslint/eslint-plugin'airbnb',//Uses airbnb recommended rules'prettier/@typescript-eslint',// Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier'plugin:prettier/recommended',// Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.],parserOptions:{ecmaVersion:2018,// Allows for the parsing of modern ECMAScript featuressourceType:'module',// Allows for the use of importsecmaFeatures:{jsx:true,// Allows for the parsing of JSX},},env:{browser:true,node:true},rules:{// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs// e.g. '@typescript-eslint/explicit-function-return-type': 'off','no-unused-vars':'off','@typescript-eslint/no-unused-vars':['error',{'vars':'all','args':'after-used','ignoreRestSiblings':false}],'react/jsx-filename-extension':[1,{'extensions':['.js','.jsx','.ts','.tsx']}],'react/jsx-first-prop-new-line':0,'@typescript-eslint/no-explicit-any':'off','@typescript-eslint/explicit-function-return-type':0,'@typescript-eslint/no-namespace':'off','jsx-a11y/anchor-is-valid':['error',{'components':['Link'],'specialLink':['hrefLeft','hrefRight'],'aspects':['invalidHref','preferButton']}],'react/prop-types':'off','import/extensions':[1,{'extensions':['.js','.jsx','.ts','.tsx']}],'import/no-extraneous-dependencies':['error',{'devDependencies':true}],'comma-dangle':['error',{'arrays':'always-multiline','objects':'always-multiline','imports':'always-multiline','exports':'always-multiline','functions':'never'}],"react-hooks/rules-of-hooks":"error",'react-hooks/exhaustive-deps':'off','no-bitwise':'off'},plugins:['@typescript-eslint/eslint-plugin','react-hooks',],settings:{'import/resolver':{node:{extensions:['.js','.jsx','.ts','.tsx'],},typescript:{},},react:{version:'detect',// Tells eslint-plugin-react to automatically detect the version of React to use},},};
- create
.prettierrc.js
module.exports={semi:true,trailingComma:'es5',singleQuote:true,printWidth:80,tabWidth:2,};
npm i -D jest babel-jest
- add scripts in
package.json
"scripts": {"test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage"},
npm i -D enzyme enzyme-adapter-react-16 enzyme-to-jsonnpm i -D typescript @types/enzyme @types/enzyme-adapter-react-16 @types/jest
- create
jest.config.js
module.exports={moduleFileExtensions:['ts','tsx','js'],testRegex:'(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|js?|tsx?|ts?)$',globals:{NODE_ENV:'test',},snapshotSerializers:['enzyme-to-json/serializer'],transform:{'^.+\\.(j|t)sx?$':'babel-jest',},coveragePathIgnorePatterns:['/node_modules/','jest.setup.js','<rootDir>/configs/','jest.config.js','.json','.snap',],setupFiles:['<rootDir>/jest/jest.setup.js'],coverageReporters:['json','lcov','text','text-summary'],moduleNameMapper:{'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':'<rootDir>/__mocks__/mocks.js','\\.(css|less|scss)$':'<rootDir>/__mocks__/mocks.js',},moduleDirectories:['node_modules','src'],};
- create
babel.config.js
module.exports={presets:['next/babel'],};
- create
jest/jest.setup.js
importEnzymefrom'enzyme';importAdapterfrom'enzyme-adapter-react-16';import{join}from'path';import{loadEnvConfig}from'next/dist/lib/load-env-config';// to load '.env' files in testloadEnvConfig(join(__dirname,'.../'));Enzyme.configure({adapter:newAdapter()});
- change
env
in.eslintrc.js
env:{browser:true,node:true,jest:true},
npm i -S @emotion/corenpm i -D @emotion/babel-preset-css-prop jest-emotion eslint-plugin-emotion
- change
babel.config.js
module.exports={presets:[['next/babel',{'preset-env':{},'preset-react':{},},],'@emotion/babel-preset-css-prop',],};
- add rules and plugins to
.eslintrc.js
module.exports={// ...rules:{// ..."emotion/no-vanilla":"error","emotion/import-from-emotion":"error","emotion/styled-import":"error",},// ...plugins:['emotion',// ...],// ...}
- add
jest/jest.setupAfterEnv.js
import{matchers}from'jest-emotion';expect.extend(matchers);
- add serializers and setup files to
jest/jest.config.js
// ...snapshotSerializers:['enzyme-to-json/serializer','jest-emotion'],// ...setupFilesAfterEnv:['<rootDir>/jest.setupAfterEnv.js'],// ...
(deploy to /docs intead of using gh-pages branch; replace{folder}
with the project name in github repo)
- add
.env.production
NEXT_PUBLIC_LINK_PREFIX=/{folder}
- create
LINK_PREFIX
innext.config.js
constLINK_PREFIX=process.env.NEXT_PUBLIC_LINK_PREFIX||'';module.exports=()=>({assetPrefix:LINK_PREFIX,});
- change
as
prop innext/Link
to addlinkPrefix
, similar tosrc/features/link/Link.tsx
in the example setup - change
scripts
inpackage.json
{"scripts": {"export":"NODE_ENV=production npm run build && next export -o docs && touch docs/.nojekyll" }}
npm i -D @babel/plugin-proposal-nullish-coalescing-operator @babel/plugin-proposal-optional-chaining
- add the plugins to
babel.config.js
module.exports={presets:[// ...],plugins:['@babel/plugin-proposal-optional-chaining','@babel/plugin-proposal-nullish-coalescing-operator',],};
npm i -S react-redux @reduxjs/toolkitnpm i -D @types/react-redux
- either use
next-redux-wrapper
package (npm i -P next-redux-wrapper
) or copy thewithRedux.tsx
from the example setupsrc/utils/redux
- create custom
makeStore
function,_app.tsx
page and other redux setup as examples innext-redux-wrapper
repo shows
- copy
configureStore.ts
,DynamicStoreWrap.tsx
from the example setupsrc/utils/redux
, andobjectAssign.ts
fromsrc/utils/common
- change
src/_app.tsx
similar to the example setup
npm i -S next-pwa
- change
next.config.js
constisProd=process.env.NODE_ENV==='production';constFOLDER=LINK_PREFIX&&LINK_PREFIX.substring(1);// tranfrom precache url for browsers that encode dynamic routes// i.e. "[id].js" => "%5Bid%5D.js"constencodeUriTransform=async(manifestEntries)=>{constmanifest=manifestEntries.map((entry)=>{entry.url=encodeURI(entry.url);returnentry;});return{ manifest,warnings:[]};};module.exports=()=>withManifest(withPWA({// ...// service workerpwa:{disable:!isProd,subdomainPrefix:LINK_PREFIX,dest:'public',navigationPreload:true,},// manifestmanifest:{/* eslint-disable @typescript-eslint/camelcase */output:'public',short_name:FOLDER,name:FOLDER,start_url:`${LINK_PREFIX}/`,background_color:THEME_COLOR,display:'standalone',scope:`${LINK_PREFIX}/`,dir:'ltr',// text direction: left to righttheme_color:THEME_COLOR,icons:[{src:`${LINK_PREFIX}${ICON_192_PATH}`,sizes:'192x192',type:'image/png',},{src:`${LINK_PREFIX}${ICON_512_PATH}`,sizes:'512x512',type:'image/png',},],},}));
- add
public/icons
folder and include corresponding icon files in the folder - copy
ManifestHead.tsx
from the example setupsrc/features/head
- import
ManifestHead
in pages
npm i -S typeorm reflect-metadata sql.js localforage next-transpile-modulesnpm i -D webpack babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
- add to
compilerOptions
intsconfig.json
{"compilerOptions": {"emitDecoratorMetadata":true,"experimentalDecorators":true, }}
- add to the
plugins
inbabel.config.js
// ... plugins:['babel-plugin-transform-typescript-metadata',['@babel/plugin-proposal-decorators',{legacy:true}],['@babel/plugin-proposal-class-properties',{loose:true}],// ...]// ...
- add to the top of
_app.tsx
or whatever the entry fileimport'reflect-metadata';importlocalforagefrom'localforage';// ...declare global{interfaceWindow{localforage?:LocalForage;}}// ...// ...inside the AppuseEffect(()=>{window.localforage=localforage;return()=>{window.localforage=undefined;};},[]);// ...
- add to
next.config.js
// ...constwithTM=require('next-transpile-modules')(['typeorm/browser']);constwebpack=require('webpack');const{ dependencies}=require('./package-lock.json');// ...module.exports=()=>/* ... other wrappers, like withPWA() add withTM wrap innermost */withTM({webpack:(config,{ isServer})=>{config.plugins.push(newwebpack.ProvidePlugin({'window.SQL':'sql.js/dist/sql-wasm.js',}));if(!isServer){config.node={fs:'empty',net:'empty',tls:'empty',};}returnconfig;}// ...other existing configsenv:{NEXT_PUBLIC_SQL_JS_VERSION:dependencies['sql.js'].version||'',}})// ...
- copy
connection.ts
fromsrc/sql/connection
in the example setup and modify thedefaultEntities
- NextJs, next-pwa, workbox are still growing their api, so this project setup will be modified in the future for easier setup.
- There is a known error on the workbox:GoogleChrome/workbox#2178.
- Only direct children in
next/head
will be picked up at build time, so allnext/link
wrapped elements must be inserted (useEffect) after thenext/head
is loaded.
About
Example Github Page PWA with NextJs, Redux-toolkit and Sql.js
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
No releases published
Packages0
No packages published
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.