forked fromProject-Setup/github_sql_pwa
- Notifications
You must be signed in to change notification settings - Fork0
Example Github Page PWA with NextJs, Redux-toolkit and Sql.js
License
NotificationsYou must be signed in to change notification settings
joelesilva/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
.envand 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> .nvmrcnpm 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/pagesfolder (orpages) - create
pages.tsxundersrc/pages/(i.e.src/pages/index.tsxfor/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.jsmodule.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.jsmodule.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.jsmodule.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.jsmodule.exports={presets:['next/babel'],};
- create
jest/jest.setup.jsimportEnzymefrom'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
envin.eslintrc.jsenv:{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.jsmodule.exports={presets:[['next/babel',{'preset-env':{},'preset-react':{},},],'@emotion/babel-preset-css-prop',],};
- add rules and plugins to
.eslintrc.jsmodule.exports={// ...rules:{// ..."emotion/no-vanilla":"error","emotion/import-from-emotion":"error","emotion/styled-import":"error",},// ...plugins:['emotion',// ...],// ...}
- add
jest/jest.setupAfterEnv.jsimport{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_PREFIXinnext.config.jsconstLINK_PREFIX=process.env.NEXT_PUBLIC_LINK_PREFIX||'';module.exports=()=>({assetPrefix:LINK_PREFIX,});
- change
asprop innext/Linkto addlinkPrefix, similar tosrc/features/link/Link.tsxin the example setup - change
scriptsinpackage.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.jsmodule.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-wrapperpackage (npm i -P next-redux-wrapper) or copy thewithRedux.tsxfrom the example setupsrc/utils/redux - create custom
makeStorefunction,_app.tsxpage and other redux setup as examples innext-redux-wrapperrepo shows
- copy
configureStore.ts,DynamicStoreWrap.tsxfrom the example setupsrc/utils/redux, andobjectAssign.tsfromsrc/utils/common - change
src/_app.tsxsimilar to the example setup
npm i -S next-pwa
- change
next.config.jsconstisProd=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/iconsfolder and include corresponding icon files in the folder - copy
ManifestHead.tsxfrom the example setupsrc/features/head - import
ManifestHeadin 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
compilerOptionsintsconfig.json{"compilerOptions": {"emitDecoratorMetadata":true,"experimentalDecorators":true, }} - add to the
pluginsinbabel.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.tsxor 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.tsfromsrc/sql/connectionin 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/headwill be picked up at build time, so allnext/linkwrapped elements must be inserted (useEffect) after thenext/headis loaded.
About
Example Github Page PWA with NextJs, Redux-toolkit and Sql.js
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
Languages
- TypeScript82.5%
- JavaScript17.5%