├── .gitignore ├── LICENSE ├── README.md └── template ├── .gitignore ├── README.md ├── package.json ├── src ├── assets │ ├── favicon.ico │ ├── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── mstile-150x150.png │ ├── preact-logo-inverse.svg │ └── preact-logo.svg ├── components │ ├── app.js │ └── header │ │ ├── index.js │ │ └── style.css ├── index.js ├── manifest.json ├── routes │ ├── home │ │ ├── index.js │ │ └── style.css │ └── profile │ │ └── index.js └── style │ └── index.css └── tests ├── __mocks__ ├── browserMocks.js ├── fileMocks.js └── setupTests.js └── header.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *.lock 4 | *.log 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Preact CLI Templates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # preact-default-boilerplate 2 | 3 | ![Lighthouse-Badge](https://img.shields.io/badge/lighthouse-100%2F100-brightgreen.svg) 4 | 5 | ## Documentation 6 | - This is the default template for [preact-cli](https://github.com/developit/preact-cli). 7 | - [For Preact](https://preactjs.com/): General information about how to work with Preact, not specific to this template 8 | 9 | ## Usage 10 | 11 | ```bash 12 | $ npx preact-cli create default my-project 13 | $ cd my-project 14 | $ npm install 15 | $ npm run dev 16 | ``` 17 | 18 | Development server runs on port `8080`. If the default port is already in use on your machine it will start the development server on a random port. 19 | 20 | ## Commands 21 | 22 | - `npm run dev`: Run a development, HMR server 23 | 24 | - `npm run serve`: Run a production-like server 25 | 26 | - `npm run build`: Production-ready build 27 | 28 | - `npm run lint`: Pass JavaScript files using ESLint 29 | 30 | - `npm run test`: Run Jest and Enzyme with [`enzyme-adapter-preact-pure`](https://github.com/preactjs/enzyme-adapter-preact-pure) for your tests 31 | 32 | ### How to Test 33 | 34 | The `default` template provides a basic test setup with Jest, Enzyme, and [`enzyme-adapter-preact-pure`](https://github.com/preactjs/enzyme-adapter-preact-pure). You are free to change Enzyme with any other testing library (eg. [Preact Testing Library](https://testing-library.com/docs/preact-testing-library/intro)). 35 | 36 | You can run all additional Jest CLI commands with the `npm run test` command as described in the [Jest docs](https://facebook.github.io/jest/docs/en/cli.html#using-with-npm-scripts). For example, running jest in watch mode would be : 37 | 38 | - `npm run test -- --watch` instead of `jest --watch ` 39 | 40 | ### Fork It And Make Your Own 41 | 42 | You can fork this repo to create your own boilerplate, and use it with `preact-cli`: 43 | 44 | ``` bash 45 | preact create username/repo my-project 46 | ``` 47 | -------------------------------------------------------------------------------- /template/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /*.log 4 | -------------------------------------------------------------------------------- /template/README.md: -------------------------------------------------------------------------------- 1 | # {{ name }} 2 | 3 | ## CLI Commands 4 | 5 | ```bash 6 | # install dependencies 7 | npm install 8 | 9 | # serve with hot reload at localhost:8080 10 | npm run dev 11 | 12 | # build for production with minification 13 | npm run build 14 | 15 | # test the production build locally 16 | npm run serve 17 | 18 | # run tests with jest and enzyme 19 | npm run test 20 | ``` 21 | 22 | For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md). 23 | -------------------------------------------------------------------------------- /template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "{{ name }}", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "preact build", 8 | "serve": "sirv build --port 8080 --cors --single", 9 | "dev": "preact watch", 10 | "lint": "eslint src", 11 | "test": "jest" 12 | }, 13 | "eslintConfig": { 14 | "extends": "preact", 15 | "ignorePatterns": [ 16 | "build/" 17 | ] 18 | }, 19 | "devDependencies": { 20 | "enzyme": "^3.11.0", 21 | "enzyme-adapter-preact-pure": "^4.0.0", 22 | "eslint": "^8.30.0", 23 | "eslint-config-preact": "^1.3.0", 24 | "jest": "^27.0.0", 25 | "jest-preset-preact": "^4.0.5", 26 | "preact-cli": "^3.4.2", 27 | "sirv-cli": "^2.0.2" 28 | }, 29 | "dependencies": { 30 | "preact": "^10.11.3", 31 | "preact-render-to-string": "^5.2.6", 32 | "preact-router": "^3.2.1" 33 | }, 34 | "jest": { 35 | "preset": "jest-preset-preact", 36 | "setupFiles": [ 37 | "/tests/__mocks__/browserMocks.js", 38 | "/tests/__mocks__/setupTests.js" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /template/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preactjs-templates/default/747620707c2b0286f7efa86ebe50a768422779f9/template/src/assets/favicon.ico -------------------------------------------------------------------------------- /template/src/assets/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preactjs-templates/default/747620707c2b0286f7efa86ebe50a768422779f9/template/src/assets/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /template/src/assets/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preactjs-templates/default/747620707c2b0286f7efa86ebe50a768422779f9/template/src/assets/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /template/src/assets/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preactjs-templates/default/747620707c2b0286f7efa86ebe50a768422779f9/template/src/assets/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /template/src/assets/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preactjs-templates/default/747620707c2b0286f7efa86ebe50a768422779f9/template/src/assets/icons/favicon-16x16.png -------------------------------------------------------------------------------- /template/src/assets/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preactjs-templates/default/747620707c2b0286f7efa86ebe50a768422779f9/template/src/assets/icons/favicon-32x32.png -------------------------------------------------------------------------------- /template/src/assets/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preactjs-templates/default/747620707c2b0286f7efa86ebe50a768422779f9/template/src/assets/icons/mstile-150x150.png -------------------------------------------------------------------------------- /template/src/assets/preact-logo-inverse.svg: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /template/src/assets/preact-logo.svg: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /template/src/components/app.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { Router } from 'preact-router'; 3 | 4 | import Header from './header'; 5 | 6 | // Code-splitting is automated for `routes` directory 7 | import Home from '../routes/home'; 8 | import Profile from '../routes/profile'; 9 | 10 | const App = () => ( 11 |
12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | ); 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /template/src/components/header/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { Link } from 'preact-router/match'; 3 | import style from './style.css'; 4 | 5 | const Header = () => ( 6 |
7 | 8 | Preact Logo 9 |

Preact CLI

10 |
11 | 22 |
23 | ); 24 | 25 | export default Header; 26 | -------------------------------------------------------------------------------- /template/src/components/header/style.css: -------------------------------------------------------------------------------- 1 | .header { 2 | position: fixed; 3 | left: 0; 4 | top: 0; 5 | 6 | display: flex; 7 | justify-content: space-between; 8 | 9 | width: 100%; 10 | height: 3.5rem; 11 | 12 | background: #673ab8; 13 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); 14 | z-index: 50; 15 | } 16 | 17 | .header a { 18 | display: inline-block; 19 | padding: 0 1rem; 20 | color: #fff; 21 | text-decoration: none; 22 | line-height: 3.5rem; 23 | } 24 | 25 | .header a:hover, 26 | .header a:active { 27 | background: rgba(0, 0, 0, 0.2); 28 | } 29 | 30 | .header a.logo { 31 | display: flex; 32 | align-items: center; 33 | padding: 0.5rem 1rem; 34 | } 35 | 36 | .logo h1 { 37 | padding: 0 0.5rem; 38 | font-size: 1.5rem; 39 | line-height: 2rem; 40 | font-weight: 400; 41 | } 42 | 43 | @media (max-width: 639px) { 44 | .logo h1 { 45 | display: none; 46 | } 47 | } 48 | 49 | .header nav a.active { 50 | background: rgba(0, 0, 0, 0.4); 51 | } 52 | -------------------------------------------------------------------------------- /template/src/index.js: -------------------------------------------------------------------------------- 1 | import './style'; 2 | import App from './components/app'; 3 | 4 | export default App; 5 | -------------------------------------------------------------------------------- /template/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{ name }}", 3 | "short_name": "{{ name }}", 4 | "start_url": "/", 5 | "display": "standalone", 6 | "orientation": "portrait", 7 | "background_color": "#fff", 8 | "theme_color": "#673ab8", 9 | "icons": [ 10 | { 11 | "src": "/assets/icons/android-chrome-192x192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "/assets/icons/android-chrome-512x512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /template/src/routes/home/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import style from './style.css'; 3 | 4 | const Home = () => { 5 | return ( 6 |
7 | 8 | Preact Logo 9 | 10 |

Get Started Building PWAs with Preact-CLI

11 |
12 | 17 | 22 | 27 |
28 |
29 | ); 30 | }; 31 | 32 | const Resource = props => { 33 | return ( 34 | 35 |

{props.title}

36 |

{props.description}

37 |
38 | ); 39 | }; 40 | 41 | export default Home; 42 | -------------------------------------------------------------------------------- /template/src/routes/home/style.css: -------------------------------------------------------------------------------- 1 | .home { 2 | text-align: center; 3 | } 4 | 5 | .home img { 6 | padding: 1.5rem; 7 | } 8 | 9 | .home img:hover { 10 | filter: drop-shadow(0 0 3rem #673ab888); 11 | } 12 | 13 | .home section { 14 | margin-top: 10rem; 15 | display: grid; 16 | grid-template-columns: 1fr 1fr 1fr; 17 | column-gap: 1.5rem; 18 | } 19 | 20 | @media (max-width: 639px) { 21 | .home section { 22 | margin-top: 5rem; 23 | grid-template-columns: 1fr; 24 | row-gap: 1rem; 25 | } 26 | } 27 | 28 | .resource { 29 | padding: 0.75rem 1.5rem; 30 | border-radius: 0.5rem; 31 | text-align: left; 32 | text-decoration: none; 33 | color: #000; 34 | background-color: #f1f1f1; 35 | border: 1px solid transparent; 36 | } 37 | 38 | .resource:hover { 39 | border: 1px solid #000; 40 | box-shadow: 0 25px 50px -12px #673ab888; 41 | } 42 | 43 | @media (prefers-color-scheme: dark) { 44 | .resource { 45 | color: #fff; 46 | background-color: #181818; 47 | } 48 | .resource:hover { 49 | border: 1px solid #bbb; 50 | box-shadow: 0 25px 50px -12px #673ab888; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /template/src/routes/profile/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { useEffect, useState } from 'preact/hooks'; 3 | 4 | // Note: `user` comes from the URL, courtesy of our router 5 | const Profile = ({ user }) => { 6 | const [time, setTime] = useState(Date.now()); 7 | const [count, setCount] = useState(10); 8 | 9 | useEffect(() => { 10 | let timer = setInterval(() => setTime(Date.now()), 1000); 11 | return () => clearInterval(timer); 12 | }, []); 13 | 14 | return ( 15 |
16 |

Profile: {user}

17 |

This is the user profile for a user named {user}.

18 | 19 |
Current time: {new Date(time).toLocaleString()}
20 | 21 |

22 | {' '} 23 | Clicked {count} times. 24 |

25 |
26 | ); 27 | }; 28 | 29 | export default Profile; 30 | -------------------------------------------------------------------------------- /template/src/style/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: 'Helvetica Neue', arial, sans-serif; 3 | font-weight: 400; 4 | -webkit-font-smoothing: antialiased; 5 | -moz-osx-font-smoothing: grayscale; 6 | 7 | color-scheme: light dark; 8 | color: #444; 9 | background: #fafafa; 10 | } 11 | 12 | @media (prefers-color-scheme: dark) { 13 | :root { 14 | color: #fff; 15 | background: #1c1c1c; 16 | } 17 | } 18 | 19 | body { 20 | margin: 0; 21 | padding: 0; 22 | min-height: 100vh; 23 | } 24 | 25 | #app > main { 26 | display: flex; 27 | padding-top: 3.5rem; 28 | margin: 0 auto; 29 | min-height: calc(100vh - 3.5rem); 30 | max-width: 1280px; 31 | align-items: center; 32 | justify-content: center; 33 | } 34 | 35 | @media (max-width: 639px) { 36 | #app > main { 37 | margin: 0 2rem; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /template/tests/__mocks__/browserMocks.js: -------------------------------------------------------------------------------- 1 | // Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage 2 | /** 3 | * An example how to mock localStorage is given below 👇 4 | */ 5 | 6 | /* 7 | // Mocks localStorage 8 | const localStorageMock = (function() { 9 | let store = {}; 10 | 11 | return { 12 | getItem: (key) => store[key] || null, 13 | setItem: (key, value) => store[key] = value.toString(), 14 | clear: () => store = {} 15 | }; 16 | 17 | })(); 18 | 19 | Object.defineProperty(window, 'localStorage', { 20 | value: localStorageMock 21 | }); */ 22 | -------------------------------------------------------------------------------- /template/tests/__mocks__/fileMocks.js: -------------------------------------------------------------------------------- 1 | // This fixed an error related to the CSS and loading gif breaking my Jest test 2 | // See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets 3 | module.exports = 'test-file-stub'; 4 | -------------------------------------------------------------------------------- /template/tests/__mocks__/setupTests.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-preact-pure'; 3 | 4 | configure({ 5 | adapter: new Adapter(), 6 | }); 7 | -------------------------------------------------------------------------------- /template/tests/header.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import Header from '../src/components/header'; 3 | // See: https://github.com/preactjs/enzyme-adapter-preact-pure 4 | import { shallow } from 'enzyme'; 5 | 6 | describe('Initial Test of the Header', () => { 7 | test('Header renders 3 nav items', () => { 8 | const context = shallow(
); 9 | expect(context.find('h1').text()).toBe('Preact App'); 10 | expect(context.find('Link').length).toBe(3); 11 | }); 12 | }); 13 | --------------------------------------------------------------------------------