├── .gitignore ├── ACTUALLYREADME.md ├── README.md ├── example ├── .gitignore ├── README.md ├── package.json ├── postcss.config.js ├── src │ ├── App.tsx │ ├── components │ │ ├── Card.tsx │ │ ├── EmailForm.tsx │ │ └── Hero.tsx │ ├── gitignore │ ├── index.css │ ├── index.html │ └── index.tsx ├── tsconfig.json └── yarn.lock ├── package.json ├── packages └── rincewind │ ├── .circleci │ └── config.yml │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc │ ├── CHANGELOG.md │ ├── README.md │ ├── bin │ ├── run │ └── run.cmd │ ├── package.json │ ├── src │ ├── commands │ │ ├── build.ts │ │ ├── create.ts │ │ └── serve.ts │ ├── index.ts │ ├── normalizeOptions.ts │ └── templates │ │ ├── README.md │ │ └── basic │ │ ├── LICENSE │ │ ├── README.md │ │ ├── _.gitignore │ │ ├── netlify.toml │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src │ │ ├── App.tsx │ │ ├── components │ │ │ ├── Card.tsx │ │ │ ├── EmailForm.tsx │ │ │ └── Hero.tsx │ │ ├── gitignore │ │ ├── index.css │ │ ├── index.html │ │ └── index.tsx │ │ └── tsconfig.json │ ├── tsconfig.json │ └── yarn.lock └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | .nyc_output 4 | dist 5 | lib 6 | package-lock.json 7 | tmp 8 | node_modules 9 | .vscode 10 | .DS_Store -------------------------------------------------------------------------------- /ACTUALLYREADME.md: -------------------------------------------------------------------------------- 1 | # notes to contributors 2 | 3 | dont edit the toplevel readme - the project readme is maintained in `/packages/rincewind/README.md` and copied out to the top level at publish time. 4 | 5 | we do this so that npm.im/rincewind will always have a decent readme available, but when people land on github.com/sw-yx/rincewind they also have a nice readme. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rincewind 2 | 3 | Rincewind: Create React Apps with Parcel + React + PostCSS (with Tailwind) + TypeScript 4 | 5 | [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io) 6 | [![Version](https://img.shields.io/npm/v/rincewind.svg)](https://npmjs.org/package/rincewind) 7 | [![CircleCI](https://circleci.com/gh/sw-yx/rincewind/tree/master.svg?style=shield)](https://circleci.com/gh/sw-yx/rincewind/tree/master) 8 | [![Downloads/week](https://img.shields.io/npm/dw/rincewind.svg)](https://npmjs.org/package/rincewind) 9 | [![License](https://img.shields.io/npm/l/rincewind.svg)](https://github.com/sw-yx/rincewind/blob/master/package.json) 10 | [![Netlify Status](https://api.netlify.com/api/v1/badges/88e02ab4-ab2c-4949-8dbe-1afb7f3b2ae6/deploy-status)](https://app.netlify.com/sites/rincewind/deploys) 11 | [![https://www.netlify.com/img/deploy/button.svg](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/sw-yx/rincewin-demo) 12 | 13 | ![https://humanitysdarkerside.files.wordpress.com/2012/07/rincewind-by-lindsay-c-walker-2.jpg?w=217&h=278](https://humanitysdarkerside.files.wordpress.com/2012/07/rincewind-by-lindsay-c-walker-2.jpg?w=217&h=278) 14 | 15 | ## Usage 16 | 17 | ```sh-session 18 | $ npm install -g rincewind 19 | $ rincewind # or rw 20 | ``` 21 | 22 | By default it creates the new app in a directory called `rincewind-app`. If you want to specify the folder name, add an extra arg: `rw create myapp` 23 | 24 | Once you have a scaffolded app, `cd` into the project and then either run: 25 | 26 | - `rw build` for a production build 27 | - `rw serve` for local development. (aliases for this: `rw dev` or `rw develop`) 28 | 29 | `rw` is just an alias for `rincewind`, you can use `rw` interchangeably everywhere except with npx 30 | 31 | Enjoy! 32 | 33 | ## Commands 34 | 35 | ### `rincewind create` 36 | 37 | _Aliases: `rw create`_ 38 | 39 | Creates a Rincewind app. Only basic template available for now. You can pass an arg or a `-d` flag to specify the name of the directory it creates. 40 | 41 | ```sh-session 42 | $ npm install -g rincewind 43 | $ rincewind # or rw 44 | ``` 45 | 46 | These all also do the same thing 47 | 48 | - `npx rincewind` creates a rincewind app without install 49 | - `rw init` 50 | - `rw create` 51 | - `rw init myapp` 52 | - `rw create myapp` 53 | - `rw init -d myapp` 54 | - `rw create -d myapp` 55 | 56 | ### `rincewind serve` 57 | 58 | _Aliases: `rw dev`, `rw develop`_ 59 | 60 | Locally serve a rincewind app for development. 61 | 62 | ```sh-session 63 | rincewind serve 64 | ``` 65 | 66 | These all also do the same thing 67 | 68 | - `rw serve` 69 | - `rw dev` 70 | - `rw develop` 71 | 72 | ### `rincewind build` 73 | 74 | _Aliases: `rw build`_ 75 | 76 | Build a Rincewind app for production. 77 | 78 | ```sh-session 79 | rincewind build 80 | ``` 81 | 82 | These all also do the same thing 83 | 84 | - `rw build` 85 | 86 | ## Roadmap 87 | 88 | - add basic design system to draw from 89 | - other templates to scaffold from including your own 90 | - caching the node modules so you copy paste 91 | - module/nomodule 92 | - preact aliasing 93 | - react single file components 94 | - other ideas from Proxx: https://www.youtube.com/watch?v=fWc3Zu6A3Ws 95 | - need to be liefie resistant - service workers 96 | - ??? 97 | - Give me your ideas! https://twitter.com/swyx 98 | 99 | ## Name and History 100 | 101 | v0-v2 of this package was a different project, run by https://github.com/mmckegg over from 2013-2016 102 | 103 | https://github.com/mmckegg kindly handed this project over to swyx in Nov 2019, because of his deep love of Terry Pratchett's Discworld series and [its bumbling Wizzard](https://en.wikipedia.org/wiki/Rincewind). 104 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Rincewind 2 | 3 | this is a boilerplate for 4 | 5 | - React 6 | - TailwindCSS 7 | - TypeScript 8 | 9 | ## Demo at https://rincewind.netlify.com/ 10 | 11 | You can see logs at https://app.netlify.com/sites/rincewind/deploys 12 | 13 | [![https://www.netlify.com/img/deploy/button.svg](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/sw-yx/rincewind) 14 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rincewind-example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "license": "MIT", 6 | "dependencies": { 7 | "react": "^16.12.0", 8 | "react-dom": "^16.12.0" 9 | }, 10 | "devDependencies": { 11 | "@fullhuman/postcss-purgecss": "^1.3.0", 12 | "@types/react": "^16.9.11", 13 | "@types/react-dom": "^16.9.4", 14 | "autoprefixer": "^9.7.1", 15 | "postcss-import": "^12.0.1", 16 | "postcss-nested": "^4.2.1", 17 | "postcss-preset-env": "^6.7.0", 18 | "rincewind": "latest", 19 | "tailwindcss": "^1.1.3", 20 | "typescript": "^3.7.2" 21 | }, 22 | "scripts": { 23 | "build": "rincewind build src/index.html", 24 | "start": "rincewind serve src/index.html" 25 | }, 26 | "default": "dist/index.html", 27 | "targets": { 28 | "default": { 29 | "publicUrl": "./" 30 | } 31 | }, 32 | "repository": "https://github.com/sw-yx/rincewind", 33 | "author": "sw-yx " 34 | } 35 | -------------------------------------------------------------------------------- /example/postcss.config.js: -------------------------------------------------------------------------------- 1 | // postcss relies on this to know whether to apply purgecss 2 | const isProduction = !process.env.ROLLUP_WATCH; 3 | 4 | module.exports = { 5 | plugins: [ 6 | require("postcss-preset-env"), 7 | require("postcss-import"), 8 | require("tailwindcss"), 9 | require("autoprefixer"), 10 | require("postcss-nested"), 11 | ...(isProduction 12 | ? [ 13 | require("@fullhuman/postcss-purgecss")({ 14 | // Specify the paths to all of the template files in your project 15 | content: [ 16 | "./index.html", 17 | "./src/**/*.tsx" 18 | // './src/**/*.elm', 19 | //"./src/**/*.html", 20 | //"./src/**/*.vue", 21 | // etc. 22 | ], 23 | 24 | // Include any special characters you're using in this regular expression 25 | defaultExtractor: content => content.match(/[\w-/:]+(? 11 | 12 | {toggle ? : } 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /example/src/components/Card.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Card() { 4 | return ( 5 |
6 |
7 |
8 |
9 | Woman paying for a purchase 14 |
{" "} 15 |
16 |
17 | Marketing 18 |
{" "} 19 | 23 | Finding customers for your new business 24 | {" "} 25 |

26 | Getting a new business off the ground is a lot of hard work. Here 27 | are five ideas you can use to find your first customers. 28 |

29 |
30 |
31 |
32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /example/src/components/EmailForm.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function EmailForm() { 4 | //
5 | return ( 6 |
7 |
8 |
9 | 12 | 19 |
20 |
21 | 24 | 31 |
32 |
33 | 39 |
40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /example/src/components/Hero.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Hero({ 4 | setToggle 5 | }: { 6 | setToggle: (updaterFn: (x: boolean) => boolean) => void; 7 | }) { 8 | return ( 9 |
10 |

11 | Rincewind: A fast boilerplate for{" "} 12 | 13 | React + TypeScript + Tailwind apps. 14 | 15 |

16 |

17 | Tailwind CSS is a highly customizable, low-level CSS framework that 18 | gives you all of the building blocks you need to build bespoke designs 19 | without any annoying opinionated styles you have to fight to override. 20 |

21 |
22 | 28 | 32 | Why Tailwind? 33 | 34 |
35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /example/src/gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | /.nyc_output 4 | /dist 5 | /lib 6 | /package-lock.json 7 | /tmp 8 | node_modules -------------------------------------------------------------------------------- /example/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | 7 | /* Strong opionion on position:relative */ 8 | *, 9 | *::after, 10 | *::before { 11 | position: relative; 12 | } 13 | /* 14 | * 15 | * Other CSS Resets 16 | * Tailwind includes its reset, Preflight, by default: https://unpkg.com/tailwindcss/dist/base.css 17 | * Others to reference: https://hankchizljaw.com/wrote/a-modern-css-reset/ 18 | * Another one you could use: https://bitsofco.de/my-css-reset-base/ 19 | * 20 | */ 21 | -------------------------------------------------------------------------------- /example/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rollup React 6 | 7 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | 6 | ReactDOM.render(, document.getElementById("app")); 7 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "outDir": "build/lib", 5 | "target": "es5", 6 | "module": "esnext", 7 | "lib": ["dom", "esnext"], 8 | "sourceMap": true, 9 | "importHelpers": true, 10 | "declaration": true, 11 | "rootDir": "src", 12 | "strict": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "allowJs": false, 19 | "jsx": "react", 20 | "moduleResolution": "node", 21 | "baseUrl": "src", 22 | "forceConsistentCasingInFileNames": true, 23 | "esModuleInterop": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "allowSyntheticDefaultImports": true, 26 | "experimentalDecorators": true 27 | }, 28 | "include": ["src/**/*"], 29 | "exclude": ["node_modules", "build", "scripts"] 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*", 5 | "example" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/rincewind/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | jobs: 4 | node-latest: &test 5 | docker: 6 | - image: node:latest 7 | working_directory: ~/cli 8 | steps: 9 | - checkout 10 | - restore_cache: &restore_cache 11 | keys: 12 | - v1-npm-{{checksum ".circleci/config.yml"}}-{{checksum "yarn.lock"}} 13 | - v1-npm-{{checksum ".circleci/config.yml"}} 14 | - run: 15 | name: Install dependencies 16 | command: yarn 17 | - run: ./bin/run --version 18 | - run: ./bin/run --help 19 | - run: 20 | name: Testing 21 | command: yarn test 22 | node-12: 23 | <<: *test 24 | docker: 25 | - image: node:12 26 | node-10: 27 | <<: *test 28 | docker: 29 | - image: node:10 30 | cache: 31 | <<: *test 32 | steps: 33 | - checkout 34 | - run: 35 | name: Install dependencies 36 | command: yarn 37 | - save_cache: 38 | key: v1-npm-{{checksum ".circleci/config.yml"}}-{{checksum "yarn.lock"}} 39 | paths: 40 | - ~/cli/node_modules 41 | - /usr/local/share/.cache/yarn 42 | - /usr/local/share/.config/yarn 43 | 44 | workflows: 45 | version: 2 46 | "rincewind": 47 | jobs: 48 | - node-latest 49 | - node-12 50 | - node-10 51 | - cache: 52 | filters: 53 | tags: 54 | only: /^v.*/ 55 | branches: 56 | ignore: /.*/ 57 | -------------------------------------------------------------------------------- /packages/rincewind/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /packages/rincewind/.eslintignore: -------------------------------------------------------------------------------- 1 | /lib 2 | -------------------------------------------------------------------------------- /packages/rincewind/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["oclif", "oclif-typescript"], 3 | "rules": { 4 | "quotes": [ 5 | 2, 6 | "single", 7 | { 8 | "avoidEscape": true 9 | } 10 | ], 11 | "node/no-extraneous-require": "off" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/rincewind/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). 9 | 10 | ## [v3.0.12](https://github.com/sw-yx/rincewind/compare/v3.0.11...v3.0.12) - 2020-03-03 11 | 12 | ### Commits 13 | 14 | - bump parcel version to nightly131 to solve CI issue [`19137aa`](https://github.com/sw-yx/rincewind/commit/19137aaa49cb62c013db7f2bb2a4be9b8767eb5c) 15 | - updates [`0191606`](https://github.com/sw-yx/rincewind/commit/0191606fa4cebb3fac779e3d0ee8747c1319a4be) 16 | 17 | ## [v3.0.11](https://github.com/sw-yx/rincewind/compare/v3.0.9...v3.0.11) - 2019-12-09 18 | 19 | ### Merged 20 | 21 | - fixes `no such file` error for windows users [`#3`](https://github.com/sw-yx/rincewind/pull/3) 22 | 23 | ### Commits 24 | 25 | - readme [`a8d6be8`](https://github.com/sw-yx/rincewind/commit/a8d6be8f5d327a408f6d8f3dfd232cba051c166f) 26 | - Update README.md [`20885d4`](https://github.com/sw-yx/rincewind/commit/20885d4d3d71ffad1f79afb2411ec668317c35a8) 27 | 28 | ## [v3.0.9](https://github.com/sw-yx/rincewind/compare/v3.0.8...v3.0.9) - 2019-12-01 29 | 30 | ### Commits 31 | 32 | - readme and cahngelog [`3deb37d`](https://github.com/sw-yx/rincewind/commit/3deb37dceb0daf7e2b2ae692f354b5c1f24124e0) 33 | - fix argv bug [`c5cd448`](https://github.com/sw-yx/rincewind/commit/c5cd4480bfc07c49a0d26666f87ec3b6303ac69f) 34 | 35 | ## [v3.0.8](https://github.com/sw-yx/rincewind/compare/v3.0.5...v3.0.8) - 2019-12-01 36 | 37 | ### Commits 38 | 39 | - fill out readme a bit [`9abf26a`](https://github.com/sw-yx/rincewind/commit/9abf26a4fa5f813eabdba668d8ce15dcf2ddbaf6) 40 | - refix the readme to TOC better [`ee11c99`](https://github.com/sw-yx/rincewind/commit/ee11c99e14d446bc5184fa9ab2cafa721459ea51) 41 | - fix readme again [`2a8de02`](https://github.com/sw-yx/rincewind/commit/2a8de025d535b76fcdd8c8937326051c545251a9) 42 | 43 | ## [v3.0.5](https://github.com/sw-yx/rincewind/compare/v3.0.4...v3.0.5) - 2019-12-01 44 | 45 | ### Commits 46 | 47 | - ensure cache directory exists [`7bf1f36`](https://github.com/sw-yx/rincewind/commit/7bf1f36060cf790b83d9849e925e282143912ef2) 48 | 49 | ## [v3.0.4](https://github.com/sw-yx/rincewind/compare/v3.0.3...v3.0.4) - 2019-11-30 50 | 51 | ### Commits 52 | 53 | - minor tweaks [`d18ae07`](https://github.com/sw-yx/rincewind/commit/d18ae07a922c5282e253d53f2468cc7d47269c91) 54 | 55 | ## v3.0.3 - 2019-11-30 56 | 57 | ### Commits 58 | 59 | - switch to yarn workspace [`0770afb`](https://github.com/sw-yx/rincewind/commit/0770afbf226e522294b179de68b125f8a4d70f45) 60 | - minor tweaks [`dfe8625`](https://github.com/sw-yx/rincewind/commit/dfe8625c673021430249cae547fe2f628891e727) 61 | - readme [`ebd1bec`](https://github.com/sw-yx/rincewind/commit/ebd1beceef9c5ed6613696b8ff07d3c22c641fde) 62 | -------------------------------------------------------------------------------- /packages/rincewind/README.md: -------------------------------------------------------------------------------- 1 | # rincewind 2 | 3 | Rincewind: Create React Apps with Parcel + React + PostCSS (with Tailwind) + TypeScript 4 | 5 | [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io) 6 | [![Version](https://img.shields.io/npm/v/rincewind.svg)](https://npmjs.org/package/rincewind) 7 | [![CircleCI](https://circleci.com/gh/sw-yx/rincewind/tree/master.svg?style=shield)](https://circleci.com/gh/sw-yx/rincewind/tree/master) 8 | [![Downloads/week](https://img.shields.io/npm/dw/rincewind.svg)](https://npmjs.org/package/rincewind) 9 | [![License](https://img.shields.io/npm/l/rincewind.svg)](https://github.com/sw-yx/rincewind/blob/master/package.json) 10 | [![Netlify Status](https://api.netlify.com/api/v1/badges/88e02ab4-ab2c-4949-8dbe-1afb7f3b2ae6/deploy-status)](https://app.netlify.com/sites/rincewind/deploys) 11 | [![https://www.netlify.com/img/deploy/button.svg](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/sw-yx/rincewin-demo) 12 | 13 | ![https://humanitysdarkerside.files.wordpress.com/2012/07/rincewind-by-lindsay-c-walker-2.jpg?w=217&h=278](https://humanitysdarkerside.files.wordpress.com/2012/07/rincewind-by-lindsay-c-walker-2.jpg?w=217&h=278) 14 | 15 | ## Usage 16 | 17 | ```sh-session 18 | $ npm install -g rincewind 19 | $ rincewind # or rw 20 | ``` 21 | 22 | By default it creates the new app in a directory called `rincewind-app`. If you want to specify the folder name, add an extra arg: `rw create myapp` 23 | 24 | Once you have a scaffolded app, `cd` into the project and then either run: 25 | 26 | - `rw build` for a production build 27 | - `rw serve` for local development. (aliases for this: `rw dev` or `rw develop`) 28 | 29 | `rw` is just an alias for `rincewind`, you can use `rw` interchangeably everywhere except with npx 30 | 31 | Enjoy! 32 | 33 | ## Commands 34 | 35 | ### `rincewind create` 36 | 37 | _Aliases: `rw create`_ 38 | 39 | Creates a Rincewind app. Only basic template available for now. You can pass an arg or a `-d` flag to specify the name of the directory it creates. 40 | 41 | ```sh-session 42 | $ npm install -g rincewind 43 | $ rincewind # or rw 44 | ``` 45 | 46 | These all also do the same thing 47 | 48 | - `npx rincewind` creates a rincewind app without install 49 | - `rw init` 50 | - `rw create` 51 | - `rw init myapp` 52 | - `rw create myapp` 53 | - `rw init -d myapp` 54 | - `rw create -d myapp` 55 | 56 | ### `rincewind serve` 57 | 58 | _Aliases: `rw dev`, `rw develop`_ 59 | 60 | Locally serve a rincewind app for development. 61 | 62 | ```sh-session 63 | rincewind serve 64 | ``` 65 | 66 | These all also do the same thing 67 | 68 | - `rw serve` 69 | - `rw dev` 70 | - `rw develop` 71 | 72 | ### `rincewind build` 73 | 74 | _Aliases: `rw build`_ 75 | 76 | Build a Rincewind app for production. 77 | 78 | ```sh-session 79 | rincewind build 80 | ``` 81 | 82 | These all also do the same thing 83 | 84 | - `rw build` 85 | 86 | ## Roadmap 87 | 88 | - add basic design system to draw from 89 | - other templates to scaffold from including your own 90 | - caching the node modules so you copy paste 91 | - module/nomodule 92 | - preact aliasing 93 | - react single file components 94 | - other ideas from Proxx: https://www.youtube.com/watch?v=fWc3Zu6A3Ws 95 | - need to be liefie resistant - service workers 96 | - ??? 97 | - Give me your ideas! https://twitter.com/swyx 98 | 99 | ## Name and History 100 | 101 | v0-v2 of this package was a different project, run by https://github.com/mmckegg over from 2013-2016 102 | 103 | https://github.com/mmckegg kindly handed this project over to swyx in Nov 2019, because of his deep love of Terry Pratchett's Discworld series and [its bumbling Wizzard](https://en.wikipedia.org/wiki/Rincewind). 104 | -------------------------------------------------------------------------------- /packages/rincewind/bin/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs') 4 | const path = require('path') 5 | const project = path.join(__dirname, '../tsconfig.json') 6 | const dev = fs.existsSync(project) 7 | const recognizedCommands = [ 8 | 'create', 9 | 'init', 10 | 'serve', 11 | 'build', 12 | 'dev', 13 | 'develop', 14 | ] 15 | if (process.argv.length > 2 && recognizedCommands.includes(process.argv[2])) { 16 | // // multi command 17 | // [ '/Users/swyx/.nvm/versions/node/v10.17.0/bin/node', 18 | // '/Users/swyx/Work/rincewind/node_modules/.bin/rincewind', 19 | // 'create' ] } 20 | require('@oclif/command') 21 | .run() 22 | .then(require('@oclif/command/flush')) 23 | .catch(require('@oclif/errors/handle')) 24 | } else { 25 | // eslint-disable-next-line no-console 26 | console.log( 27 | 'Creating a basic Rincewind (https://github.com/sw-yx/rincewind) app' 28 | ) 29 | // // single command 30 | // [ '/Users/swyx/.nvm/versions/node/v10.17.0/bin/node', 31 | // '/Users/swyx/Work/rincewind/node_modules/.bin/rincewind' ] } 32 | if (dev) { 33 | // eslint-disable-next-line node/no-unpublished-require 34 | require('ts-node').register({ project }) 35 | } 36 | 37 | require(`../${dev ? 'src' : 'lib'}/commands/create`) 38 | .default.run() 39 | .catch(require('@oclif/errors/handle')) 40 | } 41 | -------------------------------------------------------------------------------- /packages/rincewind/bin/run.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\run" %* 4 | -------------------------------------------------------------------------------- /packages/rincewind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rincewind", 3 | "description": "Rincewind: A boilerplate for React + PostCSS (with Tailwind) + TypeScript", 4 | "version": "3.0.12", 5 | "author": "swyx @sw-yx", 6 | "bin": { 7 | "rincewind": "./bin/run", 8 | "rw": "./bin/run" 9 | }, 10 | "bugs": "https://github.com/sw-yx/rincewind/issues", 11 | "dependencies": { 12 | "@oclif/command": "^1", 13 | "@oclif/config": "^1", 14 | "@oclif/plugin-help": "^2", 15 | "@parcel/config-default": "^2.0.0-nightly.131", 16 | "@parcel/core": "^2.0.0-nightly.131", 17 | "@parcel/fs": "^2.0.0-nightly.131", 18 | "@parcel/package-manager": "^2.0.0-nightly.131", 19 | "chalk": "^3.0.0", 20 | "copy-template-dir": "^1.4.0", 21 | "env-paths": "^2.2.0", 22 | "get-port": "^5.0.0", 23 | "git-user-name": "^2.0.0", 24 | "progress-estimator": "^0.2.2", 25 | "tslib": "^1", 26 | "yarn-or-npm": "^3.0.1" 27 | }, 28 | "devDependencies": { 29 | "@oclif/dev-cli": "^1", 30 | "@types/node": "^10", 31 | "@types/react": "^16.9.13", 32 | "@types/react-dom": "^16.9.4", 33 | "auto-changelog": "^1.16.2", 34 | "eslint": "^5.13", 35 | "eslint-config-oclif": "^3.1", 36 | "eslint-config-oclif-typescript": "^0.1", 37 | "gh-release": "^3.5.0", 38 | "globby": "^10", 39 | "react": "^16.12.0", 40 | "react-dom": "^16.12.0", 41 | "ts-node": "^8", 42 | "typescript": "^3.3" 43 | }, 44 | "engines": { 45 | "node": ">=8.0.0" 46 | }, 47 | "files": [ 48 | "/bin", 49 | "/lib", 50 | "/npm-shrinkwrap.json", 51 | "/oclif.manifest.json" 52 | ], 53 | "homepage": "https://github.com/sw-yx/rincewind", 54 | "keywords": [ 55 | "oclif" 56 | ], 57 | "license": "MIT", 58 | "main": "lib/index.js", 59 | "oclif": { 60 | "commands": "./lib/commands", 61 | "bin": "rincewind", 62 | "plugins": [ 63 | "@oclif/plugin-help" 64 | ] 65 | }, 66 | "repository": "sw-yx/rincewind", 67 | "scripts": { 68 | "postpack": "rm -f oclif.manifest.json", 69 | "posttest": "eslint . --ext .ts --config .eslintrc", 70 | "build": "tsc -b && cp -R src/templates lib", 71 | "prepack": "rm -rf lib && yarn build && oclif-dev manifest && oclif-dev readme", 72 | "test": "echo NO TESTS", 73 | "preversion": "oclif-dev readme && git add README.md && yarn copyREADME", 74 | "version": "auto-changelog -p --template keepachangelog && git add CHANGELOG.md", 75 | "prepublishOnly": "git push && git push --tags && gh-release", 76 | "copyREADME": "cp README.md ../../README.md" 77 | }, 78 | "types": "lib/index.d.ts" 79 | } 80 | -------------------------------------------------------------------------------- /packages/rincewind/src/commands/build.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | const { BuildError } = require("@parcel/core"); 3 | const { NodePackageManager } = require("@parcel/package-manager"); 4 | const { NodeFS } = require("@parcel/fs"); 5 | const chalk = require("chalk"); 6 | import { normalizeOptions } from "../normalizeOptions"; 7 | 8 | export default class Build extends Command { 9 | static description = "describe the command here"; 10 | 11 | static examples = [`$ rincewind build # production build of a rincewind app`]; 12 | 13 | static flags = { 14 | help: flags.help({ char: "h" }), 15 | "no-cache": flags.boolean({ default: false }), 16 | "no-sourceMaps": flags.boolean({ default: false }), 17 | "no-autoinstall": flags.boolean({ default: false }), 18 | "no-profile": flags.boolean({ default: false }), 19 | "no-minify": flags.boolean({ default: false }), 20 | "no-scope-hoisting": flags.boolean({ default: false }) 21 | }; 22 | 23 | static args = [{ name: "file" }]; // todo: support multiple entries 24 | 25 | async run() { 26 | const { args, flags } = this.parse(Build); 27 | 28 | let Parcel = require("@parcel/core").default; 29 | let packageManager = new NodePackageManager(new NodeFS()); 30 | let defaultConfig = await packageManager.require( 31 | "@parcel/config-default", 32 | __filename 33 | ); 34 | let parcel = new Parcel({ 35 | entries: args.file, 36 | packageManager, 37 | defaultConfig: { 38 | ...defaultConfig, 39 | filePath: ( 40 | await packageManager.resolve("@parcel/config-default", __filename) 41 | ).resolved 42 | }, 43 | patchConsole: false, 44 | 45 | ...(await normalizeOptions({ 46 | isProd: true, 47 | cache: !flags["no-cache"], 48 | minify: !flags["no-minify"], 49 | sourceMaps: !flags["no-sourceMaps"], 50 | scopeHoist: !flags["no-scope-hoisting"], 51 | autoinstall: !flags["no-autoinstall"], 52 | profile: !flags["no-profile"] 53 | // TODO: offer https, minify, sourceMaps etc options 54 | })) // todo: see what is in there we need 55 | }); 56 | 57 | try { 58 | await parcel.run(); 59 | console.log(`Build Done!`); 60 | console.log(`Either serve locally: `); 61 | console.log(` ${chalk.cyan(`serve dist`)}`); 62 | console.log(`Or deploy to Netlify: `); 63 | console.log(` ${chalk.cyan(`ntl deploy --prod`)}`); 64 | } catch (e) { 65 | // If an exception is thrown during Parcel.build, it is given to reporters in a 66 | // buildFailure event, and has been shown to the user. 67 | if (!(e instanceof BuildError)) console.error(e); 68 | process.exit(1); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/rincewind/src/commands/create.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | const copy = require("copy-template-dir"); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | const chalk = require("chalk"); 6 | const yarnOrNpm = require("yarn-or-npm"); 7 | const spawn = yarnOrNpm.spawn; 8 | const ghUserName = require("git-user-name"); 9 | const createLogger = require("progress-estimator"); 10 | const envPaths = require("env-paths"); 11 | // All configuration keys are optional, but it's recommended to specify a storage location. 12 | // Learn more about configuration options below. 13 | const logger = initProgressEstimator(); 14 | 15 | export default class Create extends Command { 16 | static description = "describe the command here"; 17 | 18 | static examples = [`$ rw create # creates a rincewind app`]; 19 | static aliases = ["init"]; 20 | static flags = { 21 | help: flags.help({ char: "h" }), 22 | dir: flags.string({ 23 | char: "d", 24 | description: "directory to create", 25 | default: findDefaultDir() 26 | }) 27 | }; 28 | 29 | static args = [{ name: "file" }]; 30 | 31 | async run() { 32 | const { args, flags } = this.parse(Create); 33 | const createDir = args.file || flags.dir; 34 | const vars = { 35 | githubUsername: ghUserName() || "TODO_WRITE_THIS", 36 | currentYear: new Date().toISOString().slice(0, 4) 37 | }; 38 | const inDir = path.join(__dirname, "../templates/basic"); 39 | const prettifycreateDir = path.relative(process.cwd(), createDir); 40 | copy(inDir, createDir, vars, async () => { 41 | this.log(`Scaffolded app to ${chalk.cyan(prettifycreateDir)}`); 42 | // renameGitIgnoreFile(createDir); 43 | process.chdir(prettifycreateDir); 44 | 45 | await logger( 46 | new Promise((yay, nay) => { 47 | const childproc = spawn(["install"], { 48 | stdio: ["ignore", "ignore", "inherit"] 49 | }); 50 | childproc.on("exit", function(code: number /*signal*/) { 51 | if (code > 0) { 52 | // not good 53 | console.error("something bad happened!"); // todo: think about how to handle this 54 | nay(); 55 | } else { 56 | yay(); 57 | } 58 | }); 59 | }), 60 | "Installing dependencies...", 61 | { 62 | estimate: 40000 63 | } 64 | ); 65 | this.log(`Scaffolding Done!`); 66 | this.log(`Please run: `); 67 | this.log(` ${chalk.cyan(`cd ${prettifycreateDir}`)}`); 68 | this.log(` ${chalk.cyan(`${yarnOrNpm()} start`)}`); 69 | }); 70 | } 71 | } 72 | 73 | /** 74 | * 75 | * utils 76 | * 77 | */ 78 | 79 | function findDefaultDir() { 80 | const basedir = path.join(process.cwd(), "rincewind-app"); 81 | let dir = basedir; 82 | let idx = 0; 83 | while (fs.existsSync(dir)) { 84 | idx += 1; 85 | dir = basedir + "_" + idx; 86 | } 87 | return dir; 88 | } 89 | 90 | function initProgressEstimator() { 91 | if (!fs.existsSync(envPaths("rincewind").cache)) 92 | fs.mkdirSync(envPaths("rincewind").cache, { recursive: true }); 93 | const storagePath = path.join( 94 | envPaths("rincewind").cache, 95 | ".progress-estimator" 96 | ); 97 | return createLogger({ storagePath }); 98 | } 99 | -------------------------------------------------------------------------------- /packages/rincewind/src/commands/serve.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | const { NodePackageManager } = require("@parcel/package-manager"); 3 | const { NodeFS } = require("@parcel/fs"); 4 | // const chalk = require("chalk"); 5 | import { normalizeOptions } from "../normalizeOptions"; 6 | 7 | export default class Serve extends Command { 8 | static description = "describe the command here"; 9 | 10 | static examples = [`$ rincewind build # production build of a rincewind app`]; 11 | static aliases = ["develop", "dev"]; 12 | 13 | static flags = { 14 | help: flags.help({ char: "h" }), 15 | "no-minify": flags.boolean({ default: false }), 16 | "no-scope-hoisting": flags.boolean({ default: false }) 17 | }; 18 | 19 | static args = [{ name: "file" }]; // todo: support multiple entries 20 | 21 | async run() { 22 | const { args, flags } = this.parse(Serve); 23 | 24 | let Parcel = require("@parcel/core").default; 25 | let packageManager = new NodePackageManager(new NodeFS()); 26 | let defaultConfig = await packageManager.require( 27 | "@parcel/config-default", 28 | __filename 29 | ); 30 | let parcel = new Parcel({ 31 | entries: args.file, 32 | packageManager, 33 | defaultConfig: { 34 | ...defaultConfig, 35 | filePath: ( 36 | await packageManager.resolve("@parcel/config-default", __filename) 37 | ).resolved 38 | }, 39 | patchConsole: false, 40 | ...(await normalizeOptions({ 41 | isServe: true 42 | // // idk how this works! 43 | // hmr: true, 44 | // hmrPort: 1234, 45 | // hmrHost: "localhost" 46 | })) // todo: see what is in there we need 47 | }); 48 | 49 | let { unsubscribe } = await parcel.watch((err: Error) => { 50 | if (err) { 51 | throw err; 52 | } 53 | }); 54 | 55 | let isExiting: boolean; 56 | const exit = async () => { 57 | if (isExiting) return; 58 | isExiting = true; 59 | await unsubscribe(); 60 | process.exit(); 61 | }; 62 | 63 | // Detect the ctrl+c key, and gracefully exit after writing the asset graph to the cache. 64 | // This is mostly for tools that wrap Parcel as a child process like yarn and npm. 65 | // 66 | // Setting raw mode prevents SIGINT from being sent in response to ctrl-c: 67 | // https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode 68 | // 69 | // We don't use the SIGINT event for this because when run inside yarn, the parent 70 | // yarn process ends before Parcel and it appears that Parcel has ended while it may still 71 | // be cleaning up. Handling events from stdin prevents this impression. 72 | if (process.stdin.isTTY) { 73 | process.stdin.setRawMode && process.stdin.setRawMode(true); 74 | require("readline").emitKeypressEvents(process.stdin); 75 | 76 | process.stdin.on("keypress", async (char, key) => { 77 | if (key.ctrl && key.name === "c") { 78 | await exit(); 79 | } 80 | }); 81 | } 82 | 83 | // In non-tty cases, respond to SIGINT by cleaning up. 84 | process.on("SIGINT", exit); 85 | process.on("SIGTERM", exit); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packages/rincewind/src/index.ts: -------------------------------------------------------------------------------- 1 | export { run } from "@oclif/command"; 2 | import { Command, flags } from "@oclif/command"; 3 | 4 | class Potato extends Command { 5 | static description = "describe the command here"; 6 | 7 | static flags = { 8 | // add --version flag to show CLI version 9 | version: flags.version({ char: "v" }), 10 | help: flags.help({ char: "h" }), 11 | // flag with a value (-n, --name=VALUE) 12 | name: flags.string({ char: "n", description: "name to print" }), 13 | // flag with no value (-f, --force) 14 | force: flags.boolean({ char: "f" }) 15 | }; 16 | 17 | static args = [{ name: "file" }]; 18 | 19 | async run() { 20 | const { args, flags } = this.parse(Potato); 21 | 22 | const name = flags.name || "world"; 23 | this.log(`hello ${name} from ./src/index.ts`); 24 | if (args.file && flags.force) { 25 | this.log(`you input --force and --file: ${args.file}`); 26 | } 27 | } 28 | } 29 | 30 | export default Potato; 31 | -------------------------------------------------------------------------------- /packages/rincewind/src/normalizeOptions.ts: -------------------------------------------------------------------------------- 1 | import getPort from "get-port"; 2 | const chalk = require("chalk"); 3 | 4 | /** 5 | * porting the normalizeOptions function in parcel core over to here 6 | * InitialParcelOptions is found in 7 | * https://github.com/parcel-bundler/parcel/blob/23ba2cc54f3286f60e2cab63eb358660c916139d/packages/core/types/index.js 8 | * 9 | */ 10 | // export async function normalizeOptions(NormOpts): Promise { 11 | 12 | export type ServerOptions = { 13 | host?: string; 14 | port: number; 15 | https?: HTTPSOptions | boolean; 16 | publicUrl?: string; 17 | }; 18 | type HTTPSOptions = { cert: string; key: string }; 19 | type HMROptions = { 20 | hmr?: boolean; 21 | port?: number; 22 | host?: string; 23 | https?: string; 24 | }; 25 | type NormOpts = { 26 | isProd?: boolean; 27 | /** 28 | * if HTTPS needed, must specify cert and key 29 | */ 30 | https?: HTTPSOptions; 31 | cert?: string; 32 | key?: string; 33 | /** 34 | * if serve mode, must have host and publicUrl? 35 | */ 36 | isServe?: boolean; 37 | port?: number; 38 | host?: string; 39 | publicUrl?: string; 40 | /** 41 | * if hmr needed 42 | */ 43 | hmr?: boolean; 44 | hmrPort?: number; 45 | hmrHost?: string; 46 | /** 47 | * misc 48 | */ 49 | cache?: boolean; 50 | cacheDir?: string; 51 | minify?: boolean; 52 | sourceMaps?: boolean; 53 | scopeHoist?: boolean; 54 | autoinstall?: boolean; 55 | logLevel?: "none" | "error" | "warn" | "info" | "verbose"; 56 | profile?: boolean; 57 | target?: any; // https://github.com/parcel-bundler/parcel/blob/23ba2cc54f3286f60e2cab63eb358660c916139d/packages/core/types/index.js#L163 58 | }; 59 | export async function normalizeOptions(opts: NormOpts) { 60 | if (opts.isProd === true) { 61 | process.env.NODE_ENV = process.env.NODE_ENV || "production"; 62 | } else { 63 | process.env.NODE_ENV = process.env.NODE_ENV || "development"; 64 | } 65 | 66 | // let https = !!opts.https as HTTPSOptions | false; // also used as string | undefined in the hmr section! bug? 67 | let https = !!opts.https as any; 68 | if (opts.cert && opts.key) { 69 | https = { 70 | cert: opts.cert, 71 | key: opts.key 72 | }; 73 | } 74 | 75 | let serve = false as ServerOptions | false; 76 | if (opts.isServe) { 77 | let { port = 1234, host, publicUrl } = opts; 78 | port = await getPort({ port, host }); 79 | 80 | if (opts.port && port !== opts.port) { 81 | // Parcel logger is not set up at this point, so just use native console. 82 | console.warn( 83 | chalk.bold.yellowBright(`⚠️ Port ${opts.port} could not be used.`) 84 | ); 85 | } 86 | serve = { 87 | https, 88 | port, 89 | host, 90 | publicUrl 91 | }; 92 | } 93 | 94 | let hmr = false as HMROptions | false; 95 | if (opts.isServe && opts.hmr !== false) { 96 | let port = opts.hmrPort || 12345; 97 | let host = opts.hmrHost || opts.host; 98 | port = await getPort({ port, host }); 99 | 100 | process.env.HMR_HOSTNAME = host || ""; 101 | process.env.HMR_PORT = String(port); 102 | 103 | hmr = { 104 | https, 105 | port, 106 | host 107 | }; 108 | } 109 | 110 | let mode = opts.isProd ? "production" : "development"; 111 | return { 112 | disableCache: opts.cache === false, 113 | cacheDir: opts.cacheDir, 114 | mode, 115 | minify: opts.minify != null ? opts.minify : mode === "production", 116 | sourceMaps: opts.sourceMaps ?? true, 117 | scopeHoist: opts.scopeHoist, 118 | hot: hmr, 119 | serve, 120 | targets: opts.target?.length > 0 ? opts.target : null, 121 | autoinstall: opts.autoinstall ?? true, 122 | logLevel: opts.logLevel, 123 | profile: opts.profile 124 | }; 125 | } 126 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/README.md: -------------------------------------------------------------------------------- 1 | # templates 2 | 3 | place your templates here. 4 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) {{currentYear}} {{githubUsername}} 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 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/README.md: -------------------------------------------------------------------------------- 1 | # Rincewind 2 | 3 | this is a boilerplate for 4 | 5 | - React 6 | - TailwindCSS 7 | - TypeScript 8 | 9 | You can scaffold this with `npx rincewind`. 10 | 11 | ## Demo at https://rincewind.netlify.com/ 12 | 13 | You can see logs at https://app.netlify.com/sites/rincewind/deploys 14 | 15 | [![https://www.netlify.com/img/deploy/button.svg](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/sw-yx/rincewind) 16 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/_.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | .nyc_output 4 | dist 5 | lib 6 | package-lock.json 7 | tmp 8 | node_modules 9 | .vscode 10 | .DS_Store -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # https://docs.netlify.com/configure-builds/file-based-configuration/#sample-file 3 | 4 | # Directory (relative to root of your repo) that contains the deploy-ready 5 | # HTML files and assets generated by the build. If a base directory has 6 | # been specified, include it in the publish directory path. 7 | publish = "dist" 8 | 9 | # Default build command. 10 | command = "yarn build" -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rincewind-app", 3 | "version": "0.0.1", 4 | "private": true, 5 | "license": "MIT", 6 | "dependencies": { 7 | "react": "^16.12.0", 8 | "react-dom": "^16.12.0" 9 | }, 10 | "devDependencies": { 11 | "@fullhuman/postcss-purgecss": "^1.3.0", 12 | "@types/react": "^16.9.11", 13 | "@types/react-dom": "^16.9.4", 14 | "autoprefixer": "^9.7.1", 15 | "postcss-import": "^12.0.1", 16 | "postcss-nested": "^4.2.1", 17 | "postcss-preset-env": "^6.7.0", 18 | "rincewind": "latest", 19 | "tailwindcss": "^1.1.3", 20 | "typescript": "^3.7.2" 21 | }, 22 | "scripts": { 23 | "build": "rincewind build src/index.html", 24 | "start": "rincewind serve src/index.html" 25 | }, 26 | "default": "dist/index.html", 27 | "targets": { 28 | "default": { 29 | "publicUrl": "./" 30 | } 31 | }, 32 | "repository": "https://github.com/{{githubUsername}}/rincewind-app", 33 | "author": "{{githubUsername}} " 34 | } 35 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/postcss.config.js: -------------------------------------------------------------------------------- 1 | // postcss relies on this to know whether to apply purgecss 2 | const isProduction = !process.env.ROLLUP_WATCH; 3 | 4 | module.exports = { 5 | plugins: [ 6 | require("postcss-preset-env"), 7 | require("postcss-import"), 8 | require("tailwindcss"), 9 | require("autoprefixer"), 10 | require("postcss-nested"), 11 | ...(isProduction 12 | ? [ 13 | require("@fullhuman/postcss-purgecss")({ 14 | // Specify the paths to all of the template files in your project 15 | content: [ 16 | "./index.html", 17 | "./src/**/*.tsx" 18 | // './src/**/*.elm', 19 | //"./src/**/*.html", 20 | //"./src/**/*.vue", 21 | // etc. 22 | ], 23 | 24 | // Include any special characters you're using in this regular expression 25 | defaultExtractor: content => content.match(/[\w-/:]+(? 11 | 12 | {toggle ? : } 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/src/components/Card.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Card() { 4 | return ( 5 |
6 |
7 |
8 |
9 | Woman paying for a purchase 14 |
{" "} 15 |
16 |
17 | Marketing 18 |
{" "} 19 | 23 | Finding customers for your new business 24 | {" "} 25 |

26 | Getting a new business off the ground is a lot of hard work. Here 27 | are five ideas you can use to find your first customers. 28 |

29 |
30 |
31 |
32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/src/components/EmailForm.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function EmailForm() { 4 | //
5 | return ( 6 |
7 |
8 |
9 | 12 | 19 |
20 |
21 | 24 | 31 |
32 |
33 | 39 |
40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/src/components/Hero.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Hero({ 4 | setToggle 5 | }: { 6 | setToggle: (updaterFn: (x: boolean) => boolean) => void; 7 | }) { 8 | return ( 9 |
10 |

11 | Rincewind: A fast boilerplate for{" "} 12 | 13 | React + TypeScript + Tailwind apps. 14 | 15 |

16 |

17 | Tailwind CSS is a highly customizable, low-level CSS framework that 18 | gives you all of the building blocks you need to build bespoke designs 19 | without any annoying opinionated styles you have to fight to override. 20 |

21 |
22 | 28 | 32 | Why Tailwind? 33 | 34 |
35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/src/gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | /.nyc_output 4 | /dist 5 | /lib 6 | /package-lock.json 7 | /tmp 8 | node_modules -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | 7 | /* Strong opionion on position:relative */ 8 | *, 9 | *::after, 10 | *::before { 11 | position: relative; 12 | } 13 | /* 14 | * 15 | * Other CSS Resets 16 | * Tailwind includes its reset, Preflight, by default: https://unpkg.com/tailwindcss/dist/base.css 17 | * Others to reference: https://hankchizljaw.com/wrote/a-modern-css-reset/ 18 | * Another one you could use: https://bitsofco.de/my-css-reset-base/ 19 | * 20 | */ 21 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rollup React 6 | 7 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | 6 | ReactDOM.render(, document.getElementById("app")); 7 | -------------------------------------------------------------------------------- /packages/rincewind/src/templates/basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "outDir": "build/lib", 5 | "target": "es5", 6 | "module": "esnext", 7 | "lib": ["dom", "esnext"], 8 | "sourceMap": true, 9 | "importHelpers": true, 10 | "declaration": true, 11 | "rootDir": "src", 12 | "strict": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "allowJs": false, 19 | "jsx": "react", 20 | "moduleResolution": "node", 21 | "baseUrl": "src", 22 | "forceConsistentCasingInFileNames": true, 23 | "esModuleInterop": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "allowSyntheticDefaultImports": true, 26 | "experimentalDecorators": true 27 | }, 28 | "include": ["src/**/*"], 29 | "exclude": ["node_modules", "build", "scripts"] 30 | } 31 | -------------------------------------------------------------------------------- /packages/rincewind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "importHelpers": true, 5 | "module": "commonjs", 6 | "outDir": "lib", 7 | "rootDir": "src", 8 | "strict": true, 9 | "target": "es2017", 10 | "jsx": "react", 11 | "esModuleInterop": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["src/templates/**/*"] 15 | } 16 | --------------------------------------------------------------------------------