├── .editorConfig
├── .gitattributes
├── .gitignore
├── .npmrc
├── .storybook
├── addons.js
├── config.js
└── webpack.config.ts
├── .travis.yml
├── LICENSE
├── README.md
├── cypress.json
├── cypress
├── fixtures
│ └── example.json
├── integration
│ └── smoke.spec.js
├── plugins
│ └── index.js
└── support
│ ├── commands.js
│ └── index.js
├── package-lock.json
├── package.json
├── preprocessor.js
├── src
├── __tests__
│ └── sample-test-spec.ts
├── components
│ ├── stories
│ │ └── todo-item.stories.tsx
│ ├── todo-item.tsx
│ └── todo-list.tsx
├── index.html
└── index.tsx
├── tsconfig.json
├── tslint.json
└── webpack.config.ts
/.editorConfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | #Unix-style newlines with a newline ending every file
7 | end_of_line = lf
8 | insert_final_newline = true
9 |
10 | [*]
11 | # Indentation style
12 | indent_style = space
13 | indent_size = 2
14 |
15 | # File character encoding
16 | charset = utf-8
17 |
18 | # Denotes whether to trim whitespace at the end of lines
19 | trim_trailing_whitespace = true
20 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.ts linguist-language=TypeScript
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 | *.pid.lock
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # nyc test coverage
19 | .nyc_output
20 |
21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22 | .grunt
23 |
24 | # node-waf configuration
25 | .lock-wscript
26 |
27 | # Compiled binary addons (http://nodejs.org/api/addons.html)
28 | build/Release
29 |
30 | # Dependency directories
31 | node_modules
32 | jspm_packages
33 |
34 | # Optional npm cache directory
35 | .npm
36 |
37 | # Optional eslint cache
38 | .eslintcache
39 |
40 | # Optional REPL history
41 | .node_repl_history
42 |
43 | # Output of 'npm pack'
44 | *.tgz
45 |
46 | # Yarn Integrity file
47 | .yarn-integrity
48 |
49 | dist/
50 |
51 | cypress/screenshots/
52 | cypress/videos/
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | save-exact=true
2 |
--------------------------------------------------------------------------------
/.storybook/addons.js:
--------------------------------------------------------------------------------
1 | import '@storybook/addon-actions/register';
2 | import '@storybook/addon-links/register';
3 |
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from '@storybook/preact';
2 |
3 | // Automatically import all files ending in *.stories.tsx
4 | const req = require.context('../src/components', true, /\.stories\.tsx$/);
5 |
6 | function loadStories() {
7 | req.keys().forEach(filename => req(filename));
8 | }
9 |
10 | configure(loadStories, module);
11 |
--------------------------------------------------------------------------------
/.storybook/webpack.config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This extends the default webpack configuration used in storybook.
3 | */
4 | import { resolve } from 'path';
5 |
6 | module.exports = {
7 | resolve: {
8 | extensions: ['.ts', '.tsx', '.js']
9 | },
10 | module: {
11 | rules: [
12 | {
13 | enforce: 'pre',
14 | test: /\.tsx?$/,
15 | exclude: /node_modules/,
16 | use: [
17 | {
18 | loader: 'tslint-loader',
19 | options: {
20 | configFile: resolve(__dirname, '../tslint.json'),
21 | emitErrors: true,
22 | failOnHint: true,
23 | typeCheck: true
24 | }
25 | }
26 | ]
27 | },
28 | {
29 | test: /\.tsx?$/,
30 | exclude: /node_modules/,
31 | use: [
32 | {
33 | loader: 'ts-loader'
34 | }
35 | ]
36 | }
37 | ]
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | script:
4 | - npm test
5 | - npm run build
6 |
7 | cache:
8 | yarn: true
9 | directories:
10 | - node_modules
11 |
12 | node_js:
13 | - "12"
14 | - "10"
15 | - "8"
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Nick Taylor
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 | # ts-preact-starter
2 |
3 | **This project is no longer maintained as there are better projects for TypeScript and Preact that have popped up since this project was created**
4 |
5 | [](https://travis-ci.org/nickytonline/ts-preact-starter)
6 | [](https://app.netlify.com/sites/fervent-newton-a3b969/deploys)
7 |
8 |
9 |
10 | This is a barebones starter kit for Preact with TypeScript. Click the green "Use this template" button at the top of this page and enter a name and description for your repository.
11 |
12 | To get up and running
13 |
14 | 1. `npm install`
15 | 1. From the command line run `npm start`
16 | 1. Navigate to [http://localhost:3000](http://localhost:3000)
17 |
18 | To run tests:
19 |
20 | 1. `npm test`
21 | 1. To run in watch mode, run `npm run test:watch`
22 | 1. Tests are set up to run out of the `__tests__` folder. I put this by default as this appears to be part of the Jest defaults, but if you prefer to have your tests beside the code you want to test, simply modify the regex in the Jest configuration in `package.json`.
23 |
24 | To run Cypress:
25 |
26 | 1. Run `npm run start` to start the webpack dev server.
27 | 2. Run `npm run e2e:dev` to open the Cypress test runner. For more information on Cypress, see their [official documentation](https://docs.cypress.io)
28 |
29 | To run Storybook:
30 |
31 | 1. `npm run storybook`
32 | 1. Navigate to [http://localhost:6006](http://localhost:6006)
33 | 1. For more information on using Storybook, see the [Storybook for Preact](https://storybook.js.org/docs/guides/guide-preact) guide.
34 |
35 | A deployed demo can be found at [typescript-preact-starter.iamdeveloper.com](https://typescript-preact-starter.iamdeveloper.com).
36 |
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:8080"
3 | }
4 |
--------------------------------------------------------------------------------
/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/cypress/integration/smoke.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('Smoke test site', () => {
4 | it('Should load the TODO app', () => {
5 | cy.visit('/');
6 |
7 | // Ensure we have an input to add a new todo
8 | cy.get('[data-cy="new-todo-input"]').should('be.visible');
9 |
10 | // Ensure Add TODO button is present
11 | cy.get('[data-cy="todo-submit"]').should('be.visible');
12 |
13 | cy.get('[data-cy="todo-list"]').then(([todoList]) => {
14 | // No TODOs when the app initializes.
15 | expect(todoList.children.length).to.equal(0);
16 | })
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | module.exports = (on, config) => {
15 | return config
16 | };
17 |
--------------------------------------------------------------------------------
/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This is will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ts-preact-starter",
3 | "version": "1.0.0",
4 | "description": "Barebones starter project for Preact with TypeScript",
5 | "main": "index.js",
6 | "author": "Nick Taylor ",
7 | "license": "MIT",
8 | "keywords": [
9 | "preact",
10 | "react",
11 | "typescript",
12 | "boilerplate"
13 | ],
14 | "engines": {
15 | "node": ">=8"
16 | },
17 | "scripts": {
18 | "prebuild": "CI=1 npm i cypress",
19 | "build": "webpack --mode=production",
20 | "postbuild": "http-server -p 8080 ./dist & npm run e2e && fkill -f :8080",
21 | "start": "webpack-dev-server --mode=development --progress --config ./webpack.config.ts",
22 | "test": "jest",
23 | "test:watch": "npm run test -- --watch",
24 | "storybook": "start-storybook -p 6006",
25 | "build-storybook": "build-storybook",
26 | "e2e": "[ ! -z \"$DEPLOY_URL\" ] && cypress run || echo 'e2e only runs on Netlify'",
27 | "e2e:dev": "CYPRESS_baseUrl=http://localhost:9000 cypress open"
28 | },
29 | "jest": {
30 | "moduleFileExtensions": [
31 | "ts",
32 | "tsx",
33 | "js"
34 | ],
35 | "transform": {
36 | "^.+\\.(ts|tsx)$": "/preprocessor.js"
37 | },
38 | "testRegex": "/__tests__/.*\\.(ts|tsx|js)$"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "7.8.6",
42 | "@storybook/addon-actions": "5.1.11",
43 | "@storybook/addon-links": "5.1.11",
44 | "@storybook/addons": "5.1.11",
45 | "@storybook/preact": "5.1.11",
46 | "@types/jest": "25.1.3",
47 | "@types/webpack": "4.41.7",
48 | "@types/webpack-dev-server": "3.10.0",
49 | "babel-loader": "8.0.6",
50 | "cypress": "3.8.1",
51 | "fkill-cli": "5.2.0",
52 | "html-webpack-plugin": "3.2.0",
53 | "http-server": "0.12.1",
54 | "jest": "25.1.0",
55 | "preact-compat": "3.19.0",
56 | "ts-loader": "6.2.1",
57 | "ts-node": "8.6.2",
58 | "tslint": "6.0.0",
59 | "tslint-loader": "3.5.4",
60 | "typescript": "3.8.3",
61 | "webpack": "4.41.5",
62 | "webpack-cli": "3.3.10",
63 | "webpack-dev-server": "3.10.2"
64 | },
65 | "dependencies": {
66 | "preact": "8.5.3",
67 | "tslib": "1.11.1"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/preprocessor.js:
--------------------------------------------------------------------------------
1 | const tsc = require('typescript');
2 | const tsConfig = require('./tsconfig.json');
3 |
4 | module.exports = {
5 | process(src, path) {
6 | if (path.endsWith('.ts') || path.endsWith('.tsx')) {
7 | return tsc.transpile(
8 | src,
9 | tsConfig.compilerOptions,
10 | path,
11 | []
12 | );
13 | }
14 | return src;
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/src/__tests__/sample-test-spec.ts:
--------------------------------------------------------------------------------
1 | describe ('WHEN this sample test runs', () => {
2 | it('SHOULD return true', () => {
3 | // Arrange
4 | const testData: string = 'some data';
5 | const expected = true;
6 |
7 | // Act
8 | const actual = testData.includes('a');
9 |
10 | // Assert
11 | expect(expected).toBe(actual);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/components/stories/todo-item.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from '@storybook/preact';
2 | import { h } from 'preact';
3 | import TodoItem from '../todo-item';
4 |
5 | storiesOf('Todo Item', module)
6 | .add('with text', () => {
7 |
8 | return (
9 |
12 | );
13 | });
14 |
--------------------------------------------------------------------------------
/src/components/todo-item.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact';
2 |
3 | interface TodoItemProps {
4 | text: string;
5 | }
6 |
7 | const TodoItem = ({text}: TodoItemProps) => ({text});
8 |
9 | export default TodoItem;
10 |
--------------------------------------------------------------------------------
/src/components/todo-list.tsx:
--------------------------------------------------------------------------------
1 | import { Component, h } from 'preact';
2 | import TodoItem from './todo-item';
3 |
4 | interface TodoListState {
5 | todos: { text: string }[];
6 | text: string;
7 | }
8 |
9 | export default class TodoList extends Component<{}, TodoListState> {
10 | state = { todos: [], text: '' };
11 |
12 | setText = (e: Event) => {
13 | this.setState({
14 | text: (e.target as HTMLInputElement).value
15 | });
16 | }
17 |
18 | addTodo = () => {
19 | const { todos, text } = this.state;
20 |
21 | this.setState({
22 | todos: [...todos, { text }],
23 | text: ''
24 | });
25 | }
26 |
27 | render({}, { todos, text }) {
28 | return (
29 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ts-preact-starter
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { render, h } from 'preact';
2 | import TodoList from './components/todo-list';
3 |
4 | render(
5 | ,
6 | document.querySelector('#root')
7 | );
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "alwaysStrict": true,
4 | "jsx": "react",
5 | "target": "es5",
6 | "module": "commonjs",
7 | "lib": ["dom", "es2015"],
8 | "jsxFactory": "h",
9 | "noEmitHelpers": true,
10 | "importHelpers": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "align": [
4 | true,
5 | "parameters",
6 | "arguments",
7 | "statements"
8 | ],
9 | "ban": false,
10 | "class-name": true,
11 | "comment-format": [
12 | true,
13 | "check-space"
14 | ],
15 | "curly": false,
16 | "eofline": true,
17 | "forin": true,
18 | "indent": [
19 | true,
20 | "spaces"
21 | ],
22 | "interface-name": false,
23 | "jsdoc-format": true,
24 | "label-position": true,
25 | "max-line-length": [
26 | true,
27 | 140
28 | ],
29 | "member-ordering": [
30 | true,
31 | "public-before-private",
32 | "static-before-instance",
33 | "variables-before-functions"
34 | ],
35 | "no-any": false,
36 | "no-arg": true,
37 | "no-bitwise": true,
38 | "no-console": [
39 | true,
40 | "debug",
41 | "info",
42 | "time",
43 | "timeEnd",
44 | "trace"
45 | ],
46 | "no-construct": true,
47 | "no-parameter-properties": false,
48 | "no-debugger": true,
49 | "no-shadowed-variable": true,
50 | "no-duplicate-variable": true,
51 | "no-empty": true,
52 | "no-eval": true,
53 | "no-internal-module": true,
54 | "no-require-imports": true,
55 | "no-string-literal": true,
56 | "no-switch-case-fall-through": true,
57 | "trailing-comma": [
58 | true,
59 | {
60 | "multiline": "never",
61 | "singleline": "never"
62 | }
63 | ],
64 | "no-trailing-whitespace": true,
65 | "no-unused-expression": true,
66 | "no-use-before-declare": true,
67 | "no-var-keyword": true,
68 | "no-var-requires": false,
69 | "one-line": [
70 | true,
71 | "check-open-brace",
72 | "check-catch",
73 | "check-else",
74 | "check-whitespace"
75 | ],
76 | "quotemark": [
77 | true,
78 | "single",
79 | "jsx-double"
80 | ],
81 | "radix": true,
82 | "semicolon": [
83 | true,
84 | "always"
85 | ],
86 | "switch-default": true,
87 | "triple-equals": [
88 | true,
89 | "allow-null-check"
90 | ],
91 | "typedef": [
92 | false,
93 | "call-signature",
94 | "parameter",
95 | "property-declaration",
96 | "member-variable-declaration"
97 | ],
98 | "typedef-whitespace": [
99 | true,
100 | {
101 | "call-signature": "nospace",
102 | "index-signature": "nospace",
103 | "parameter": "nospace",
104 | "property-declaration": "nospace",
105 | "variable-declaration": "nospace"
106 | }
107 | ],
108 | "variable-name": [
109 | true,
110 | "ban-keywords"
111 | ],
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ]
120 | }
121 | }
--------------------------------------------------------------------------------
/webpack.config.ts:
--------------------------------------------------------------------------------
1 | import * as webpack from 'webpack';
2 | import { resolve, join } from 'path';
3 | import * as HtmlWebpackPlugin from 'html-webpack-plugin';
4 |
5 | const { HotModuleReplacementPlugin } = webpack;
6 | const port = 3000;
7 | const context = __dirname + '/src';
8 |
9 | interface WebpackEnvironment {
10 | NODE_ENV: string;
11 | }
12 |
13 | module.exports = (env: WebpackEnvironment, argv: { mode: string }) => {
14 | const appEntryPoints = argv.mode === 'production'
15 | ? ['./index']
16 | : [
17 | `webpack-dev-server/client?http://localhost:${port}`,
18 | 'webpack/hot/only-dev-server',
19 | './index'
20 | ];
21 |
22 | const config: webpack.Configuration = {
23 | name: 'client',
24 | target: 'web',
25 | context,
26 | entry: {
27 | app: appEntryPoints
28 | },
29 | output: {
30 | filename: '[name].js',
31 | path: resolve(__dirname, 'dist')
32 | },
33 | resolve: {
34 | extensions: ['.ts', '.tsx', '.js', 'jsx']
35 | },
36 | devtool: argv.mode === 'production' ? 'source-map' : 'cheap-eval-source-map',
37 | module: {
38 | rules: [
39 | {
40 | enforce: 'pre',
41 | test: /\.tsx?$/,
42 | loader: 'tslint-loader',
43 | exclude: /node_modules/,
44 | options: {
45 | configFile: resolve(__dirname, './tslint.json'),
46 | emitErrors: true,
47 | failOnHint: true,
48 | typeCheck: true
49 | }
50 | },
51 | {
52 | test: /\.tsx?$/,
53 | loader: 'ts-loader',
54 | exclude: /node_modules/
55 | }
56 | ]
57 | },
58 | plugins: [
59 | new HtmlWebpackPlugin({
60 | template: './index.html',
61 | hash: true,
62 | filename: 'index.html',
63 | inject: 'body'
64 | }),
65 | new HotModuleReplacementPlugin()
66 | ]
67 | };
68 |
69 | if (argv.mode === 'development') {
70 | config.devServer = {
71 | contentBase: join(__dirname, 'dist'),
72 | compress: true,
73 | port: 9000
74 | };
75 | }
76 |
77 | return config;
78 | };
79 |
--------------------------------------------------------------------------------