├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── package.json ├── src ├── cli.js ├── prompts.js ├── types.js └── utils.js ├── test ├── cli.test.js ├── fixtures │ ├── create-react-app │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ ├── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── App.test.js │ │ │ ├── components │ │ │ │ └── Button │ │ │ │ │ ├── Button.css │ │ │ │ │ ├── Button.js │ │ │ │ │ └── Button.test.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ ├── logo.svg │ │ │ └── registerServiceWorker.js │ │ └── yarn.lock │ └── react-static-boilerplate │ │ ├── .editorconfig │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── components │ │ ├── Button │ │ │ ├── Button.js │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── Footer │ │ │ ├── Footer.js │ │ │ └── package.json │ │ ├── Layout │ │ │ ├── Header.css │ │ │ ├── Header.js │ │ │ ├── Layout.css │ │ │ ├── Layout.js │ │ │ ├── Navigation.js │ │ │ └── package.json │ │ └── Link │ │ │ ├── Link.js │ │ │ └── package.json │ │ ├── database.rules.json │ │ ├── docs │ │ ├── README.md │ │ ├── recipes │ │ │ ├── deploy-to-amazon-s3.md │ │ │ ├── deploy-to-github-pages.md │ │ │ ├── how-to-integrate-material-design-lite.md │ │ │ ├── how-to-use-sass.md │ │ │ └── how-to-use-with-bootstrap.md │ │ └── routing-and-navigation.md │ │ ├── firebase.json │ │ ├── package.json │ │ ├── public │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── crossdomain.xml │ │ ├── favicon.ico │ │ ├── humans.txt │ │ ├── index.ejs │ │ ├── robots.txt │ │ ├── sitemap.ejs │ │ ├── tile-wide.png │ │ └── tile.png │ │ ├── src │ │ ├── about │ │ │ ├── index.js │ │ │ ├── index.md │ │ │ └── styles.css │ │ ├── error │ │ │ ├── index.js │ │ │ └── styles.css │ │ ├── history.js │ │ ├── home │ │ │ ├── index.js │ │ │ ├── index.md │ │ │ └── styles.css │ │ ├── main.js │ │ ├── router.js │ │ ├── routes.json │ │ └── store.js │ │ ├── test │ │ ├── .eslintrc │ │ └── spec.js │ │ ├── tools │ │ ├── .eslintrc │ │ ├── README.md │ │ ├── build.js │ │ ├── config.js │ │ ├── markdown-loader.js │ │ ├── postcss.config.js │ │ ├── publish.js │ │ ├── routes-loader.js │ │ ├── run.js │ │ ├── task.js │ │ └── webpack.config.js │ │ └── yarn.lock └── utils.test.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "node": 6 6 | } 7 | }], 8 | "stage-2" 9 | ], 10 | "plugins": ["transform-flow-strip-types"], 11 | "sourceMaps": "inline" 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/fixtures 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "airbnb-base", 5 | "plugin:flowtype/recommended" 6 | ], 7 | "plugins": [ 8 | "flowtype", 9 | "flowtype-errors" 10 | ], 11 | "env": { 12 | "jest": true 13 | }, 14 | "rules": { 15 | "semi": [2, "never"], 16 | "comma-dangle": [2, "always-multiline"], 17 | "flowtype-errors/show-errors": 2 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/dist 3 | .*/coverage 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | dist 5 | *.log 6 | .idea 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v6 4 | script: 5 | - npm run lint && npm test -- --coverage 6 | cache: 7 | - yarn 8 | after_success: 9 | - bash <(curl -s https://codecov.io/bash) 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Diego Haz 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 |

2 | generact 3 |

4 | 5 |

6 | Generated with nod 7 | NPM version 8 | Build Status 9 | Coverage Status 10 |

11 | 12 |
13 | 14 |

15 | Tool for generating React components by replicating your own.
16 | It's intended to work no matter how your file structure is. 17 |

18 | 19 |
20 | 21 |

22 | generact 23 |

24 |

25 | 26 |

27 |  Are you looking for a VS Code extension? Try vscode-generact. 28 |

29 | 30 |
31 | 32 | > It already works with boilerplates such as [create-react-app](https://github.com/facebookincubator/create-react-app) (above example), [react-boilerplate](https://github.com/react-boilerplate/react-boilerplate), [react-starter-kit](https://github.com/kriasoft/react-starter-kit) and [ARc](https://arc.js.org) (ok, I'm self-promoting here 😆). So, most likely this will work for you with your current project. 33 | 34 | ## Install 35 | 36 | ```sh 37 | $ npm install -g generact 38 | ``` 39 | 40 | ## Motivation 41 | 42 |

43 | Facebook poll 44 | Facebook poll 45 |

46 | 47 | I usually work on different projects with different file structures. Whenever I needed to create a new component, the approach I used was to copy and paste a similar or very basic component and start writing the new component from it. Talking with other developers, this seemed like a very common process. 48 | 49 | However, I've never been satisfied with that. It looked like I was doing a robot job. So why not create a robot to do that? 50 | 51 | ## Usage 52 | 53 | ```sh 54 | $ cd ~/my-projects/my-react-project 55 | $ generact 56 | ``` 57 | 58 | That will scan `~/my-projects/my-react-project` for components to replicate. 59 | 60 | ### Specify another root path to find components 61 | 62 | If you want to replicate components from another directory, you can do that by using `root` option: 63 | 64 | ```sh 65 | $ generact --root relative/or/absolute/path/to/any/react/project 66 | ``` 67 | 68 | ### Specify component path to replicate 69 | 70 | `generact` will probably find all component files inside the root path automagically (using [list-react-files](https://github.com/diegohaz/list-react-files)). But, if it doesn't, you can still pass in the component path: 71 | 72 | ```sh 73 | $ generact relative/or/absolute/path/to/component.js 74 | ``` 75 | 76 | ## Contributing 77 | 78 | PRs are welcome. 79 | 80 | Use `npm run watch` while coding. 81 | 82 | ## License 83 | 84 | MIT © [Diego Haz](https://github.com/diegohaz) 85 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | type InquirerFile = { 2 | name: string; 3 | short: string; 4 | value: string; 5 | }; 6 | 7 | /** 8 | * Get the folder of a component at `path`. 9 | * 10 | * @param path Path to a component. 11 | * @return The folder of the component given at `path`. 12 | */ 13 | export declare function getComponentFolder(path: string): string 14 | 15 | /** 16 | * Search for React components in directory `root`. 17 | * 18 | * @param root Directory to use as root. 19 | * @param workingDir (Optional) Directory the command is executed from (used for relative paths). 20 | * @return A promise resolving to an array of objects containing information about found components. 21 | */ 22 | export declare function getComponentFiles( 23 | root: string, 24 | workingDir?: string, 25 | ): Promise 26 | 27 | /** 28 | * Replicate component given by `originialPath` into component with name `answers.name` 29 | * in folder `answers.folder`. 30 | * 31 | * `originalPath` should point to the component file itself. Test, styles, etc. will also 32 | * be copied automatically. 33 | * 34 | * @param originalPath Path of the component to replicate. 35 | * @param answers An object containing `name` and `folder` of the new component. 36 | * @param workingDir (Optional) Current working directory 37 | */ 38 | export declare function replicate( 39 | originalPath: string, 40 | answers: { name: string; folder: string }, 41 | workingDir?: string, 42 | ): Promise 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/utils') 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generact", 3 | "version": "0.4.0", 4 | "description": "Tool for generating React components by replicating your own", 5 | "license": "MIT", 6 | "repository": "diegohaz/generact", 7 | "author": { 8 | "name": "Diego Haz", 9 | "email": "hazdiego@gmail.com", 10 | "url": "https://github.com/diegohaz" 11 | }, 12 | "main": "index.js", 13 | "types": "index.d.ts", 14 | "engines": { 15 | "node": ">=6" 16 | }, 17 | "bin": { 18 | "generact": "dist/cli.js" 19 | }, 20 | "files": [ 21 | "index.js", 22 | "index.d.ts", 23 | "dist" 24 | ], 25 | "scripts": { 26 | "pretest": "npm run build", 27 | "test": "jest --forceExit --runInBand", 28 | "coverage": "npm test -- --coverage", 29 | "postcoverage": "opn coverage/lcov-report/index.html", 30 | "lint": "eslint src test", 31 | "flow": "flow check", 32 | "clean": "rimraf dist", 33 | "prebuild": "npm run clean", 34 | "build": "babel src -d dist", 35 | "watch": "npm-watch", 36 | "patch": "npm version patch && npm publish", 37 | "minor": "npm version minor && npm publish", 38 | "major": "npm version major && npm publish", 39 | "prepublish": "npm run lint && npm test && npm run build", 40 | "postpublish": "git push origin master --follow-tags", 41 | "prepare": "npm run build" 42 | }, 43 | "watch": { 44 | "test": "{src,test}/*.js", 45 | "lint": "{src,test}/*.js", 46 | "build": "src" 47 | }, 48 | "jest": { 49 | "testRegex": "test\\/[^/]+test\\.js$", 50 | "testEnvironment": "node" 51 | }, 52 | "keywords": [ 53 | "generact", 54 | "react", 55 | "generator", 56 | "cli", 57 | "create-react-app" 58 | ], 59 | "dependencies": { 60 | "chalk": "^1.1.3", 61 | "fs-extra": "^3.0.1", 62 | "glob": "^7.1.2", 63 | "inquirer": "^3.0.6", 64 | "inquirer-autocomplete-prompt": "^0.8.0", 65 | "list-react-files": "^0.2.0", 66 | "lodash": "^4.17.4", 67 | "meow": "^3.7.0", 68 | "ora": "^1.2.0" 69 | }, 70 | "devDependencies": { 71 | "babel-cli": "^6.18.0", 72 | "babel-eslint": "^7.1.1", 73 | "babel-jest": "^20.0.0", 74 | "babel-plugin-transform-flow-strip-types": "^6.21.0", 75 | "babel-preset-env": "^1.1.8", 76 | "babel-preset-stage-2": "^6.18.0", 77 | "eslint": "^3.14.0", 78 | "eslint-config-airbnb-base": "^11.0.1", 79 | "eslint-plugin-flowtype": "^2.29.2", 80 | "eslint-plugin-flowtype-errors": "^3.0.0", 81 | "eslint-plugin-import": "^2.2.0", 82 | "flow-bin": "^0.46.0", 83 | "jest-cli": "^20.0.0", 84 | "npm-watch": "^0.1.7", 85 | "opn-cli": "^3.1.0", 86 | "rimraf": "^2.6.1", 87 | "suppose": "^0.6.2" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint-disable no-console */ 3 | import { join, relative, isAbsolute } from 'path' 4 | import { cyan, green, red } from 'chalk' 5 | import meow from 'meow' 6 | import inquirer from 'inquirer' 7 | import autocomplete from 'inquirer-autocomplete-prompt' 8 | import ora from 'ora' 9 | import { component, name, folder } from './prompts' 10 | import { 11 | getComponentName, 12 | getComponentFolder, 13 | getComponentFiles, 14 | replicate, 15 | } from './utils' 16 | 17 | const cli = meow(` 18 | Usage 19 | $ generact [path] 20 | 21 | Options 22 | --root Sets the root path to scan for component files. 23 | 24 | Examples 25 | $ generact 26 | $ generact src/components/Button.js 27 | $ generact --root src/components 28 | `) 29 | 30 | const performReplication = async (path) => { 31 | const originalName = getComponentName(path) 32 | const absolutePath = isAbsolute(path) ? path : join(process.cwd(), path) 33 | const relativePath = relative(process.cwd(), absolutePath) 34 | const originalFolder = getComponentFolder(relativePath) 35 | 36 | const answers = await inquirer.prompt([ 37 | name(originalName), 38 | folder(originalFolder), 39 | ]) 40 | 41 | replicate(path, answers) 42 | } 43 | 44 | const scan = async (root = process.cwd()) => { 45 | const absoluteRoot = isAbsolute(root) ? root : join(process.cwd(), root) 46 | const spinner = ora(`Scanning ${green(absoluteRoot)} for React component files...`).start() 47 | const files = await getComponentFiles(absoluteRoot) 48 | spinner.stop() 49 | 50 | if (!files.length) { 51 | console.log(red.bold('No components found! :(\n')) 52 | console.log(`Make sure you are running ${cyan('generact')} inside a React-like project directory or using ${green('root')} option:\n`) 53 | console.log(` ${cyan('$ generact')} ${green('--root relative/or/absolute/path/to/any/react/project')}\n`) 54 | console.log(`If you are already doing that, it means that ${cyan('generact')} could not find your React component files automagically.`) 55 | console.log('In this case, you can explicitly pass the component path to replicate:\n') 56 | console.log(` ${cyan('$ generact')} ${green('relative/or/absolute/path/to/my/react/component.js')}\n`) 57 | return process.exit(1) 58 | } 59 | 60 | inquirer.registerPrompt('autocomplete', autocomplete) 61 | const answers = await inquirer.prompt([component(files)]) 62 | return answers.component 63 | } 64 | 65 | if (cli.input.length) { 66 | performReplication(cli.input[0]) 67 | } else { 68 | scan(cli.flags.root).then(performReplication) 69 | } 70 | -------------------------------------------------------------------------------- /src/prompts.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { isAbsolute, relative } from 'path' 3 | import type { InquirerFile } from './types' 4 | 5 | export const component = (files: InquirerFile[]): {} => ({ 6 | type: 'autocomplete', 7 | name: 'component', 8 | message: 'Which component do you want to replicate?', 9 | source: (_, input) => 10 | Promise.resolve( 11 | files.filter(file => !input || file.value.toLowerCase().indexOf(input.toLowerCase()) >= 0) 12 | ), 13 | }) 14 | 15 | export const name = (originalName: string): {} => ({ 16 | type: 'input', 17 | name: 'name', 18 | message: `How do you want to name ${originalName} component?`, 19 | default: originalName, 20 | }) 21 | 22 | export const folder = (originalFolder: string): {} => ({ 23 | type: 'input', 24 | name: 'folder', 25 | message: answers => `In which folder do you want to put ${answers.name} component?`, 26 | default: originalFolder, 27 | filter: input => (isAbsolute(input) ? relative(process.cwd(), input) : input), 28 | }) 29 | -------------------------------------------------------------------------------- /src/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type InquirerFile = { 4 | name: string, 5 | short: string, 6 | value: string, 7 | } 8 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { basename, dirname, isAbsolute, join, relative } from 'path' 3 | import { camelCase, upperFirst } from 'lodash' 4 | import { gray } from 'chalk' 5 | import glob from 'glob' 6 | import listReactFiles from 'list-react-files' 7 | import { copy, move, readFileSync, writeFileSync } from 'fs-extra' 8 | import type { InquirerFile } from './types' 9 | 10 | const removeExt = path => path.replace(/\.[^.]+$/, '') 11 | 12 | export const getComponentName = (path: string): string => ( 13 | path.split('/').reduce((name, part) => { 14 | if (/^[A-Z]/.test(part)) { 15 | return removeExt(part) 16 | } else if (/^((?!index).+)\.[^.]+$/.test(part)) { 17 | return upperFirst(camelCase(removeExt(part))) 18 | } 19 | return name 20 | }, '') 21 | ) 22 | 23 | export const getComponentFolder = (path: string): string => { 24 | const name = getComponentName(path) 25 | return dirname(path).split('/').reduce((folder, part) => { 26 | if (removeExt(part) === name) { 27 | return folder 28 | } 29 | return join(folder, part) 30 | }, './') 31 | } 32 | 33 | export const isSingleFile = (path: string): boolean => { 34 | const name = getComponentName(path) 35 | const [dir] = dirname(path).split('/').reverse() 36 | 37 | return dir !== name 38 | } 39 | 40 | export const getFiles = (cwd: string, componentName?: string): string[] => { 41 | const extensions = '{js,ts,jsx,tsx,css,less,scss,sass,sss,json,md,mdx}' 42 | const pattern = componentName ? `**/${componentName}{.,.*.}${extensions}` : `**/*.${extensions}` 43 | return glob.sync(pattern, { cwd, absolute: true, nodir: true }) 44 | } 45 | 46 | export const getComponentFiles = ( 47 | root: string, 48 | workingDir?: string = process.cwd() 49 | ): Promise => ( 50 | listReactFiles(root).then((files: string[]) => 51 | files.map((path: string): InquirerFile => { 52 | const name = getComponentName(path) 53 | const absolutePath = join(root, path) 54 | const relativePath = relative(workingDir, absolutePath) 55 | return { 56 | name: `${name} ${gray(relativePath)}`, 57 | short: name, 58 | value: absolutePath, 59 | } 60 | }) 61 | ) 62 | ) 63 | 64 | export const replaceContents = ( 65 | contents: string, 66 | oldName: string, 67 | newName: string 68 | ): string => contents.replace( 69 | new RegExp(`([^a-zA-Z0-9_$])${oldName}([^a-zA-Z0-9_$]|Container)|(['|"]./[a-zA-Z0-9_$]*?)${oldName}([a-zA-Z0-9_$]*?)`, 'g'), 70 | `$1$3${newName}$2$4` 71 | ) 72 | 73 | export const replicate = async ( 74 | originalPath: string, 75 | answers: { name: string, folder: string }, 76 | workingDir?: string = process.cwd() 77 | ) => { 78 | const originalName = getComponentName(originalPath) 79 | const absolutePath = isAbsolute(originalPath) ? originalPath : join(workingDir, originalPath) 80 | 81 | const promises = [] 82 | 83 | if (isSingleFile(originalPath)) { 84 | const files = getFiles(dirname(absolutePath), originalName) 85 | 86 | files.forEach(async (file) => { 87 | const filename = basename(file).replace(originalName, answers.name) 88 | const destinationPath = join(workingDir, answers.folder, filename) 89 | const promise = copy(file, destinationPath).then(() => { 90 | const contents = readFileSync(destinationPath).toString() 91 | writeFileSync(destinationPath, replaceContents(contents, originalName, answers.name)) 92 | }) 93 | promises.push(promise) 94 | }) 95 | } else { 96 | const destinationPath = join(workingDir, answers.folder, answers.name) 97 | await copy(dirname(absolutePath), destinationPath) 98 | const files = getFiles(destinationPath) 99 | 100 | files.forEach((file) => { 101 | const contents = readFileSync(file).toString() 102 | const renamedPath = join(dirname(file), basename(file).replace(originalName, answers.name)) 103 | writeFileSync(file, replaceContents(contents, originalName, answers.name)) 104 | const promise = move(file, renamedPath) 105 | promises.push(promise) 106 | }) 107 | } 108 | await Promise.all(promises) 109 | } 110 | -------------------------------------------------------------------------------- /test/cli.test.js: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { tmpdir } from 'os' 3 | import { remove, pathExistsSync, readFileSync } from 'fs-extra' 4 | import suppose from 'suppose' 5 | 6 | const moveDown = (times = 1) => { 7 | const downChars = '\x1B\x5B\x42' 8 | let chars = '' 9 | for (let i = 0; i < times; i += 1) chars += downChars 10 | return chars 11 | } 12 | 13 | const tmp = (path = '') => join(tmpdir(), 'generact', path) 14 | 15 | const root = name => ['--root', join(__dirname, 'fixtures', name)] 16 | 17 | const exec = (args, { component, name }) => new Promise((resolve, reject) => { 18 | suppose(join(__dirname, '../dist/cli.js'), args) 19 | .when(/Which component/, `${component}\n`) 20 | .when(/How do you want to name/, `${name}\n`) 21 | .when(/In which folder/, tmp()) 22 | .on('error', reject) 23 | .end(resolve) 24 | }) 25 | 26 | beforeAll(() => remove(tmp())) 27 | afterAll(() => remove(tmp())) 28 | 29 | describe('create-react-app', () => { 30 | describe('src/App.js', () => { 31 | beforeAll(() => exec(root('create-react-app'), { component: '', name: 'MyComponent' })) 32 | 33 | it('created component file properly', () => { 34 | expect(pathExistsSync(tmp('MyComponent.js'))).toBe(true) 35 | expect(pathExistsSync(tmp('MyComponent.css'))).toBe(true) 36 | expect(pathExistsSync(tmp('MyComponent.test.js'))).toBe(true) 37 | }) 38 | 39 | it('modified component contents properly', () => { 40 | const contents = readFileSync(tmp('MyComponent.js')).toString() 41 | 42 | expect(contents).toMatch(/import '.\/MyComponent.css'/) 43 | expect(contents).toMatch(/class MyComponent extends Component/) 44 | expect(contents).toMatch(/className="MyComponent"/) 45 | expect(contents).toMatch(/className="MyComponent-header"/) 46 | expect(contents).toMatch(/className="MyComponent-logo"/) 47 | expect(contents).toMatch(/className="MyComponent-intro"/) 48 | expect(contents).toMatch(/src\/MyComponent.js<\/code>/) 49 | expect(contents).toMatch(/export default MyComponent/) 50 | }) 51 | }) 52 | 53 | describe('src/components/Button', () => { 54 | beforeAll(() => exec(root('create-react-app'), { component: moveDown(), name: 'AnotherButton' })) 55 | 56 | it('created component files properly', () => { 57 | expect(pathExistsSync(tmp('AnotherButton/AnotherButton.js'))).toBe(true) 58 | expect(pathExistsSync(tmp('AnotherButton/AnotherButton.test.js'))).toBe(true) 59 | expect(pathExistsSync(tmp('AnotherButton/AnotherButton.css'))).toBe(true) 60 | }) 61 | }) 62 | }) 63 | 64 | describe('react-static-boilerplate', () => { 65 | describe('components/Button', () => { 66 | beforeAll(() => exec(root('react-static-boilerplate'), { component: '', name: 'MyComponent' })) 67 | 68 | it('created component file properly', () => { 69 | expect(pathExistsSync(tmp('MyComponent/MyComponent.js'))).toBe(true) 70 | expect(pathExistsSync(tmp('MyComponent/package.json'))).toBe(true) 71 | }) 72 | 73 | it('modified package contents properly', () => { 74 | const contents = readFileSync(tmp('MyComponent/package.json')).toString() 75 | const json = JSON.parse(contents) 76 | expect(json.name).toBe('MyComponent') 77 | expect(json.main).toBe('./MyComponent.js') 78 | }) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /test/fixtures/create-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /test/fixtures/create-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4" 8 | }, 9 | "devDependencies": { 10 | "react-scripts": "1.0.7" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } -------------------------------------------------------------------------------- /test/fixtures/create-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegohaz/generact/0ad333c9a6f328f024e00bf53c1197ac5586f260/test/fixtures/create-react-app/public/favicon.ico -------------------------------------------------------------------------------- /test/fixtures/create-react-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/fixtures/create-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/create-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/create-react-app/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 | logo 11 |

Welcome to React

12 |
13 |

14 | To get started, edit src/App.js and save to reload. 15 |

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /test/fixtures/create-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /test/fixtures/create-react-app/src/components/Button/Button.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegohaz/generact/0ad333c9a6f328f024e00bf53c1197ac5586f260/test/fixtures/create-react-app/src/components/Button/Button.css -------------------------------------------------------------------------------- /test/fixtures/create-react-app/src/components/Button/Button.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './Button.css'; 3 | 4 | export const Button = () => 13 | 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | | Prop | Type | Default | Possible Values 20 | | ------------- | -------- | ----------- | --------------------------------------------- 21 | | **component** | | `button` | React component to use, e.g. `a` 22 | | **type** | `string` | `flat` | `raised`, `fab`, `mini-fab`, `icon` 23 | | **to** | `string` | `undefined` | A URL string 24 | | **colored** | `bool` | `false` | `true`, `false` 25 | | **primary** | `bool` | `false` | `true`, `false` 26 | | **accent** | `bool` | `false` | `true`, `false` 27 | | **ripple** | `bool` | `false` | `true`, `false` 28 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Button/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Button", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Button.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React from 'react'; 12 | import Link from '../Link'; 13 | 14 | function Footer() { 15 | return ( 16 | 63 | ); 64 | } 65 | 66 | export default Footer; 67 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Footer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Footer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Footer.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Layout/Header.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | .row { 12 | padding: 40px; 13 | } 14 | 15 | .title { 16 | color: #fff; 17 | text-decoration: none; 18 | } 19 | 20 | @media screen and (max-width: 1024px) { 21 | .header { 22 | display: flex; 23 | } 24 | 25 | .row { 26 | padding: 0 16px; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Layout/Header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React from 'react'; 12 | import Navigation from './Navigation'; 13 | import Link from '../Link'; 14 | import s from './Header.css'; 15 | 16 | class Header extends React.Component { 17 | 18 | componentDidMount() { 19 | window.componentHandler.upgradeElement(this.root); 20 | } 21 | 22 | componentWillUnmount() { 23 | window.componentHandler.downgradeElements(this.root); 24 | } 25 | 26 | render() { 27 | return ( 28 |
(this.root = node)}> 29 |
30 | 31 | React Static Boilerplate 32 | 33 |
34 | 35 |
36 |
37 | ); 38 | } 39 | 40 | } 41 | 42 | export default Header; 43 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Layout/Layout.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | .content { 12 | margin: 0 auto; 13 | max-width: 1000px; 14 | width: 100%; 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Layout/Layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React, { PropTypes } from 'react'; 12 | import cx from 'classnames'; 13 | import Header from './Header'; 14 | import Footer from '../Footer'; 15 | import s from './Layout.css'; 16 | 17 | class Layout extends React.Component { 18 | 19 | static propTypes = { 20 | className: PropTypes.string, 21 | }; 22 | 23 | componentDidMount() { 24 | window.componentHandler.upgradeElement(this.root); 25 | } 26 | 27 | componentWillUnmount() { 28 | window.componentHandler.downgradeElements(this.root); 29 | } 30 | 31 | render() { 32 | return ( 33 |
(this.root = node)}> 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | ); 43 | } 44 | } 45 | 46 | export default Layout; 47 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Layout/Navigation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React from 'react'; 12 | import Link from '../Link'; 13 | 14 | class Navigation extends React.Component { 15 | 16 | componentDidMount() { 17 | window.componentHandler.upgradeElement(this.root); 18 | } 19 | 20 | componentWillUnmount() { 21 | window.componentHandler.downgradeElements(this.root); 22 | } 23 | 24 | render() { 25 | return ( 26 | 30 | ); 31 | } 32 | 33 | } 34 | 35 | export default Navigation; 36 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Layout", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Layout.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Link/Link.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React, { PropTypes } from 'react'; 12 | import history from '../../src/history'; 13 | 14 | class Link extends React.Component { 15 | 16 | static propTypes = { 17 | to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired, 18 | onClick: PropTypes.func, 19 | }; 20 | 21 | handleClick = (event) => { 22 | if (this.props.onClick) { 23 | this.props.onClick(event); 24 | } 25 | 26 | if (event.button !== 0 /* left click */) { 27 | return; 28 | } 29 | 30 | if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) { 31 | return; 32 | } 33 | 34 | if (event.defaultPrevented === true) { 35 | return; 36 | } 37 | 38 | event.preventDefault(); 39 | 40 | if (this.props.to) { 41 | history.push(this.props.to); 42 | } else { 43 | history.push({ 44 | pathname: event.currentTarget.pathname, 45 | search: event.currentTarget.search, 46 | }); 47 | } 48 | }; 49 | 50 | render() { 51 | const { to, ...props } = this.props; // eslint-disable-line no-use-before-define 52 | // eslint-disable-next-line jsx-a11y/anchor-has-content 53 | return ; 54 | } 55 | 56 | } 57 | 58 | export default Link; 59 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/components/Link/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Link", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Link.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | ".read": "auth != null", 4 | ".write": "auth != null" 5 | } 6 | } -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/docs/README.md: -------------------------------------------------------------------------------- 1 | ## Table of Contents 2 | 3 | - [Routing and Navigation](routing-and-navigation.md) 4 | - Recipes 5 | - [How to Publish Website to Amazon S3](recipes/deploy-to-amazon-s3.md) 6 | - [How to Publish Website to GitHub Pages](recipes/deploy-to-github-pages.md) 7 | - [How to Integrate Material Design Lite (MDL)](recipes/how-to-integrate-material-design-lite.md) 8 | - [How to Use Sass/SCSS](recipes/how-to-use-sass.md) 9 | - [How to Use with Bootstrap](recipes/how-to-use-with-bootstrap.md) -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/docs/recipes/deploy-to-amazon-s3.md: -------------------------------------------------------------------------------- 1 | ## How to Publish Website to Amazon S3 2 | 3 | ### Step 1 4 | 5 | Configure S3 bucket for hosting a static site: 6 | 7 | http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html 8 | 9 | Set both index document and error document to `index.html`. This will allow refreshing any route (e.g. /about) without getting 404. 10 | ![S3 hosting settings for kriasoft/react-static-boilerplate](https://cloud.githubusercontent.com/assets/2770290/18042054/a68f0ca2-6e01-11e6-810d-9100e432b2f3.png) 11 | 12 | ### Step 2 13 | 14 | Install [`s3`](https://github.com/andrewrk/node-s3-client) npm module: 15 | 16 | ```sh 17 | $ npm install s3 --save-dev 18 | ``` 19 | 20 | ### Step 3 21 | 22 | Add deployment script to `publish.js`: 23 | 24 | ```js 25 | module.exports = task('publish', () => new Promise((resolve, reject) => { 26 | const client = s3.createClient({ 27 | s3Options: { 28 | region: 'us-east-1', 29 | sslEnabled: true, 30 | }, 31 | }); 32 | const uploader = client.uploadDir({ 33 | localDir: 'public', 34 | deleteRemoved: true, 35 | s3Params: { Bucket: 'artgorithms' }, 36 | }); 37 | uploader.on('error', reject); 38 | uploader.on('end', resolve); 39 | })); 40 | ``` 41 | 42 | Step 4 43 | 44 | Whenever you need to compile and publish your site to Amazon S3 simply run: 45 | 46 | ```sh 47 | $ yarn run publish 48 | ``` 49 | 50 | ![publish](https://koistya.github.io/files/react-static-boilerplate-publish.gif) 51 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/docs/recipes/deploy-to-github-pages.md: -------------------------------------------------------------------------------- 1 | ## How to Publish Website to [GitHub Pages](https://pages.github.com/) 2 | 3 | ### Step 1 4 | 5 | Add deployment script to `run.js`: 6 | 7 | ```js 8 | const path = require('path'); 9 | ``` 10 | 11 | ```js 12 | tasks.set('publish', () => { 13 | const remote = { 14 | url: 'https://github.com//.git', // TODO: Update deployment URL 15 | branch: 'gh-pages', 16 | }; 17 | global.DEBUG = process.argv.includes('--debug') || false; 18 | const spawn = require('child_process').spawn; 19 | const opts = { cwd: path.resolve(__dirname, './public'), stdio: ['ignore', 'inherit', 'inherit'] }; 20 | const git = (...args) => new Promise((resolve, reject) => { 21 | spawn('git', args, opts).on('close', code => { 22 | if (code === 0) { 23 | resolve(); 24 | } else { 25 | reject(new Error(`git ${args.join(' ')} => ${code} (error)`)); 26 | } 27 | }); 28 | }); 29 | 30 | return Promise.resolve() 31 | .then(() => run('clean')) 32 | .then(() => git('init', '--quiet')) 33 | .then(() => git('config', '--get', 'remote.origin.url') 34 | .then(() => git('remote', 'set-url', 'origin', remote.url)) 35 | .catch(() => git('remote', 'add', 'origin', remote.url)) 36 | ) 37 | .then(() => git('ls-remote', '--exit-code', remote.url, 'master') 38 | .then(() => Promise.resolve() 39 | .then(() => git('fetch', 'origin')) 40 | .then(() => git('reset', `origin/${remote.branch}`, '--hard')) 41 | .then(() => git('clean', '--force')) 42 | ) 43 | .catch(() => Promise.resolve()) 44 | ) 45 | .then(() => run('build')) 46 | .then(() => git('add', '.', '--all')) 47 | .then(() => git('commit', '--message', new Date().toUTCString()) 48 | .catch(() => Promise.resolve())) 49 | .then(() => git('push', 'origin', `HEAD:${remote.branch}`, '--force', '--set-upstream')); 50 | }); 51 | ``` 52 | 53 | ### Step 2 54 | 55 | Whenever you need to compile and publish your site to GitHub Pages simply run: 56 | 57 | ```sh 58 | $ node run publish 59 | ``` 60 | 61 | ![publish](https://koistya.github.io/files/react-static-boilerplate-publish.gif) 62 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/docs/recipes/how-to-integrate-material-design-lite.md: -------------------------------------------------------------------------------- 1 | ## How to Integrate Material Design Lite (MDL) 2 | 3 | ### Step 1 4 | 5 | Install [`react-mdl`](http://www.npmjs.com/package/react-mdl) npm package: 6 | 7 | ```sh 8 | $ npm install react-mdl --save 9 | ``` 10 | 11 | Add [Material Design Lite](https://getmdl.io) (MDL) CSS and JavaScript files as entry points 12 | in [`webpack.config.js`](../../webpack.config.js): 13 | 14 | ```js 15 | const config = { 16 | 17 | entry: [ 18 | '!!style!css!react-mdl/extra/material.min.css', // <== 19 | 'react-mdl/extra/material.min.js', // <== 20 | './main.js', 21 | ], 22 | 23 | ... 24 | 25 | }; 26 | ``` 27 | 28 | **Note**: Due to compatibility issues of the Layout component in MDL `v1.1.x` with React, you must use 29 | the the patched version of MDL from `react-mdl` npm package (as opposed to 30 | [`material-design-lite`](https://www.npmjs.com/package/material-design-lite)). This is a [known 31 | issue](https://github.com/google/material-design-lite/pull/1357), which will be fixed in `v2.x`. 32 | 33 | ### Step 2 34 | 35 | Decorate your UI elements with MDL classes, for example: 36 | 37 | #### Badge 38 | 39 | ```jsx 40 | Inbox 41 | ``` 42 | 43 | #### Grid 44 | 45 | ```jsx 46 |
47 |
Content
48 |
goes
49 |
here
50 |
51 | ``` 52 | 53 | ### List 54 | 55 | ```jsx 56 |
    57 |
  • 58 |
  • 59 |
  • 60 |
61 | ``` 62 | 63 | ### Step 3 64 | 65 | Create stand-alone React components for MDL elements that rely on JavaScript code to operate (see 66 | MDL [source code](https://github.com/google/material-design-lite/tree/mdl-1.x/src)). After such 67 | component mounts into the DOM, it need to notify MDL runtime that the underlying DOM elements can be 68 | directly manipulated by MDL; likewise right before the React component is being removed from the DOM 69 | it needs to notify MDL so it could do proper clean up. MDL provides `upgradeElement(node)` and 70 | `downgradeElements(nodes)` API methods for that. For example, to implement a [Button](../../components/Button) 71 | component you would write code similar to this: 72 | 73 | #### `components/Button/Button.js` 74 | 75 | ```js 76 | import React, { PropTypes } from 'react'; 77 | import classNames from 'classnames'; 78 | 79 | class Button extends React.Component { 80 | 81 | static propTypes = { 82 | className: PropTypes.string, 83 | primary: PropTypes.bool, 84 | }; 85 | 86 | componentDidMount() { 87 | window.componentHandler.upgradeElement(this.root); // <== 88 | } 89 | 90 | componentWillUnmount() { 91 | window.componentHandler.downgradeElements(this.root); // <== 92 | } 93 | 94 | render() { 95 | const { className, href, primary, children, ...other } = this.props; 96 | return React.createElement( 97 | href ? 'a' : 'button', 98 | { 99 | ref: node => (this.root = node), // <== 100 | className: classNames({ 101 | 'mdl-button mdl-js-button': true, 102 | 'mdl-button--primary': primary, 103 | }), 104 | href, 105 | ...other 106 | }, 107 | children 108 | ); 109 | } 110 | 111 | } 112 | 113 | export default Button; 114 | ``` 115 | 116 | #### Usage Example: 117 | 118 | ```js 119 | import Button from './components/Button'; 120 | 121 | function MyComponent() { 122 | return ( 123 |
124 | 125 | 126 |
127 | ); 128 | } 129 | 130 | export default MyComponent; 131 | ``` 132 | 133 | ### Step 4 134 | 135 | Extend MDL components with your own styles (via [CSS Modules](https://github.com/css-modules/css-modules) 136 | or [inline styles](https://facebook.github.io/react/tips/inline-styles.html)): 137 | 138 | #### `components/Spinner/Spinner.css` 139 | 140 | ```css 141 | .spinner { 142 | border: 1px solid red; 143 | } 144 | ``` 145 | 146 | #### `components/Spinner/Spinner.js` 147 | 148 | ```js 149 | import React, { PropTypes } from 'react'; 150 | import classNames from 'classnames'; 151 | import s from './Spinner.css'; 152 | 153 | class Spinner extends React.Component { 154 | 155 | static propTypes = { 156 | isActive: PropTypes.bool, 157 | }; 158 | 159 | componentDidMount() { 160 | window.componentHandler.upgradeElement(this.root); 161 | } 162 | 163 | componentWillUnmount() { 164 | window.componentHandler.downgradeElements(this.root); 165 | } 166 | 167 | render() { 168 | const { className, isActive, ...other } = this.props; 169 | return ( 170 |
(this.root = node)} 172 | className={classNames({ 173 | 'mdl-spinner mdl-js-spinner': true, 174 | 'is-active': isActive, 175 | s.spinner, 176 | className, 177 | })} 178 | {...other} 179 | /> 180 | ); 181 | } 182 | 183 | } 184 | 185 | export default Spinner; 186 | ``` 187 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/docs/recipes/how-to-use-sass.md: -------------------------------------------------------------------------------- 1 | ## How to Use Sass/SCSS 2 | 3 | > **Note**: Using plain CSS via [PostCSS](http://postcss.org/) is recommended approach because it 4 | reduces the size of the tech stack used in the project, enforces you to learn vanilla CSS syntax 5 | with modern CSS Level 3+ features that allow you doing everything you would normally do with 6 | Sass/SCSS. Also compilation of plain `.css` files should work faster with `postcss` pre-processor 7 | than `node-sass`. 8 | 9 | ### Step 1 10 | 11 | Install [`node-sass`](https://github.com/sass/node-sass) and 12 | [`sass-loader`](https://github.com/jtangelder/sass-loader) modules as dev dependencies: 13 | 14 | ```sh 15 | $ npm install node-sass --save-dev 16 | $ npm install sass-loader --save-dev 17 | ``` 18 | 19 | ### Step 2 20 | 21 | Update [`webpack.config.js`](../../webpack.config.js) file to use `sass-loader` for `.scss` files: 22 | 23 | ```js 24 | const config = { 25 | ... 26 | module: { 27 | loaders: [ 28 | ... 29 | { 30 | test: /\.scss$/, 31 | loaders: [ 32 | 'style-loader', 33 | `css-loader?${JSON.stringify({ sourceMap: isDebug, minimize: !isDebug })}`, 34 | 'postcss-loader?pack=sass', 35 | 'sass-loader', 36 | ], 37 | }, 38 | ... 39 | ] 40 | } 41 | ... 42 | } 43 | ``` 44 | 45 | ### Step 3 46 | 47 | Add one more configuration (pack) for [PostCSS](https://github.com/postcss/postcss) named `sass` to 48 | enable [Autoprefixer](https://github.com/postcss/autoprefixer) for your `.scss` files: 49 | 50 | ```js 51 | const config = { 52 | ... 53 | postcss(bundler) { 54 | return { 55 | defaults: [ 56 | ... 57 | ], 58 | sass: [ 59 | require('autoprefixer')(), 60 | ], 61 | }; 62 | } 63 | ... 64 | } 65 | ``` 66 | 67 | For more information visit https://github.com/jtangelder/sass-loader and https://github.com/sass/node-sass 68 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/docs/recipes/how-to-use-with-bootstrap.md: -------------------------------------------------------------------------------- 1 | 2 | ## How to use with Bootstrap

 3 | 4 | By default all is configured to use Material Design Lite from Google. 5 | 
Bootstrap is another styling framework that many can prefer. 
 6 | Since we use Webpack and CSS modules, just adding Bootstrap's CSS
won't work; 7 | one needs to configure an extra loader.

 8 | 9 | #### 1. Install dependencies:

 10 | 11 | npm install --save jquery bootstrap-sass # v3 
 12 | npm install --save-dev css-loader node-sass resolve-url-loader sass-loader style-loader url-loader
 13 | npm install --save-dev extract-text-webpack-plugin
 14 | npm install --save-dev bootstrap-loader

 15 | 16 | #### 2. Add an entry point to `webpack.config.js`:

 17 | 18 | const config = {
 19 | ...
 20 | entry: [
 21 | 'bootstrap-loader',
 22 | ...
 23 | 'main.js',
 24 | ],
 25 | 26 | #### 3. Add a plugin to `webpack.config.js`: 27 | 28 | 

We need to load jQuery before loading Bootstrap.
 
 29 | 30 | const config = { 
 31 | ...
 32 | plugins: [
 33 | ...
 34 | new webpack.ProvidePlugin({
 35 | jQuery: 'jquery'
 36 | }),
 37 | ],
 38 | 39 | 
 40 | #### 4. Test that it works:

 41 | 42 | Add a paragraph with a Bootstrap theme:

 43 | 44 |

TEST


 
 45 | 46 | It should be green.
 47 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/docs/routing-and-navigation.md: -------------------------------------------------------------------------------- 1 | ## Routing and Navigation 2 | 3 | [React Static Boilerplate](https://github.com/kriasoft/react-static-boilerplate) (RSB) uses a 4 | custom minimalistic (under 100 LOC) declarative routing approach that is easy to customize. It's 5 | comprised of five major parts: 6 | 7 | * **Routes** — the list of application routes in JSON format (see [`src/routes.json`](../src/routes.json)) 8 | * **Routes Loader** — a custom loader for Webpack that converts routes from JSON to JavaScript on 9 | build (see [`tools/routes-loader.js`](../tools/routes-loader.js)) 10 | * **URL Matcher** — a function that checks if a given URI matches to the route's `path` string (see 11 | `matchURI()` method in [`src/router.js`](../src/router.js)) 12 | * **Route Resolver** — a function just resolves a URI string to the first matched route, fetches 13 | all the required data and returns a React component to render (see `resolve()` method in 14 | [`src/router.js`](../src/router.js)) 15 | * **History** — client-side navigation library powered by [`history`](https://github.com/ReactJSTraining/history) 16 | npm module (the same one used in `react-router`) that helps with transitioning between pages 17 | (screens) in the browser without causing full-page refresh (see [`src/history.js`](../src/history.js)) 18 | 19 | The list of routes is just an array where each item contains a `path` - parametrized URL path string 20 | and a `page` field that points to a corresponding UI (page or screen) component within the project's 21 | file structure. For a simple to-do app, this list of routes may look like this (`routes.json`): 22 | 23 | ```json 24 | [ 25 | { 26 | "path": "/", 27 | "page": "./home" 28 | }, 29 | { 30 | "path": "/tasks/:status(pending|completed)?", 31 | "page": "./tasks/list" 32 | }, 33 | { 34 | "path": "/tasks/new", 35 | "page": "./tasks/new" 36 | }, 37 | { 38 | "path": "/tasks/:id", 39 | "page": "./tasks/details" 40 | } 41 | ] 42 | ``` 43 | 44 | This list of routes is referenced inside the main application file (where the React app is being 45 | bootstrapped) by using [`routes-loader`](../utils/routes-loader.js) (see [`src/main.js`](../src/main.js)): 46 | 47 | ```js 48 | import routes from '!!../tools/routes-loader!./routes.json'; 49 | ``` 50 | 51 | If you're new to Webpack's "loader" concept, please refer to https://webpack.js.org/concepts/loaders/ 52 | 53 | The [`routes-loader`](../tools/routes-loader.js) performs three tasks: 54 | 55 | * Converts JSON-based routes into JavaScript 56 | * Converts parametrized URL path strings into regular expressions by using 57 | [`path-to-regexp`](https://github.com/pillarjs/path-to-regexp) 58 | * Wraps page/screen UI components' path strings into Webpack's `require.ensure(..)`. For more 59 | information see [code-splitting](https://webpack.js.org/guides/code-splitting/) in Webpack docs. 60 | 61 | For example, a route like this: 62 | 63 | ```json 64 | { 65 | "path": "/tasks/:id", 66 | "page": "./tasks/details" 67 | } 68 | ``` 69 | 70 | Will become: 71 | 72 | ```js 73 | { 74 | path: '/tasks/:id', 75 | pattern: /^\/tasks\/((?:[^\/]+?))(?:\/(?=$))?$/i, 76 | keys: [{ name: 'id', pattern: '[^\\/]+?', ... }], 77 | page: './tasks/details', 78 | load: () => new Promise(resolve => 79 | require.ensure([], require => resolve(require('./tasks/details')))), 80 | } 81 | ``` 82 | 83 | Given the list of routes you can ask the router to "resolve" the given URI string to a React 84 | component. The code for that may look something like this: 85 | 86 | ```js 87 | router.resolve(routes, { pathname: '/tasks/123' }).then(component => { 88 | ReactDOM.render(component, container); 89 | }); 90 | ``` 91 | 92 | The `resolve(routes, context)` method will find the first route from the list matching to the 93 | `/tasks/123` URI string, execute its `load()` method, and return corresponding React component as a 94 | result wrapped into ES6 Promise (see [`src/router.js`](../src/router.js). 95 | 96 | If a route contains some REST API or GraphQL endpoints as data requirements for the given route, 97 | the `resolve(..)` method can also fetch the required data from these endpoints. For example, a 98 | route that needs to fetch a task by its ID may look like this: 99 | 100 | ```json 101 | { 102 | "path": "/tasks/:id", 103 | "page": "./tasks/details", 104 | "fetch": { 105 | "task": "GET /api/tasks/$id", 106 | } 107 | } 108 | ``` 109 | 110 | Finally, you can hook the router's `resolve(..)` method to be called each time when a user navigates 111 | (transitions) between pages. The code for that may look something like this: 112 | 113 | ```js 114 | function render(location) { 115 | router.resolve(routes, location) 116 | .then(renderComponent) 117 | .catch(error => router.resolve(routes, { ...location, error }).then(renderComponent)); 118 | } 119 | 120 | history.listen(render); 121 | render(history.location); 122 | ``` 123 | 124 | For more information about how the `history` npm module works please visit: 125 | 126 | https://github.com/mjackson/history#usage 127 | 128 | All transitions between pages must be performed by using this module, for example: 129 | 130 | ```js 131 | import React from 'react'; 132 | import history from '../history'; 133 | 134 | class HomePage extends React.Component { 135 | 136 | transition = event => { 137 | event.preventDefault(); 138 | history.push({ pathname: event.currentTarget.pathname }); 139 | }; 140 | 141 | render() { 142 | return ( 143 |
147 | ); 148 | } 149 | 150 | } 151 | ``` 152 | 153 | The `transition(event)` method above cancels default behavior of the `` element that causes 154 | full-page refresh and instead redirects a user to the `/tasks/123` page by using HTML5 History API. 155 | This transition is then handled by `history.listen(render)` listener inside the 156 | [`src/main.js`](../src/main.js) file. 157 | 158 | RSB comes with a helper component that can be used instead of `` elements, see 159 | [`components/Link/Link.js`](../components/Link/Link.js). So, instead of writing `Show task #123` you can have `Show task #123`. 161 | 162 | ### Related Articles 163 | 164 | * [You might not need React Router](https://medium.com/@tarkus/you-might-not-need-react-router-38673620f3d) by Konstantin Tarkus 165 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "rules": "database.rules.json" 4 | }, 5 | "hosting": { 6 | "public": "public", 7 | "rewrites": [ 8 | { 9 | "source": "**", 10 | "destination": "/index.html" 11 | } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "engines": { 6 | "node": ">=6", 7 | "npm": ">=3.8" 8 | }, 9 | "dependencies": { 10 | "babel-polyfill": "^6.23.0", 11 | "classnames": "^2.2.5", 12 | "fastclick": "^1.0.6", 13 | "history": "^4.6.1", 14 | "react": "^15.4.2", 15 | "react-dom": "^15.4.2", 16 | "react-mdl": "^1.9.0", 17 | "react-redux": "^5.0.3", 18 | "redux": "^3.6.0", 19 | "whatwg-fetch": "^2.0.3" 20 | }, 21 | "devDependencies": { 22 | "assets-webpack-plugin": "^3.5.1", 23 | "autoprefixer": "^6.7.7", 24 | "babel-core": "^6.24.0", 25 | "babel-eslint": "^7.1.1", 26 | "babel-loader": "^6.4.1", 27 | "babel-plugin-transform-runtime": "^6.23.0", 28 | "babel-preset-latest": "^6.24.0", 29 | "babel-preset-react": "^6.23.0", 30 | "babel-preset-stage-1": "^6.22.0", 31 | "babel-register": "^6.24.0", 32 | "babel-runtime": "^6.23.0", 33 | "browser-sync": "^2.18.8", 34 | "chai": "^4.0.0-canary.1", 35 | "connect-history-api-fallback": "^1.3.0", 36 | "css-loader": "^0.27.3", 37 | "ejs": "^2.5.6", 38 | "eslint": "^3.18.0", 39 | "eslint-config-airbnb": "^14.1.0", 40 | "eslint-plugin-import": "^2.2.0", 41 | "eslint-plugin-jsx-a11y": "^4.0.0", 42 | "eslint-plugin-react": "^6.10.2", 43 | "file-loader": "^0.10.1", 44 | "firebase-tools": "^3.5.0", 45 | "front-matter": "^2.1.2", 46 | "highlight.js": "^9.10.0", 47 | "json-loader": "^0.5.4", 48 | "markdown-it": "^8.3.1", 49 | "mocha": "^3.2.0", 50 | "path-to-regexp": "^1.7.0", 51 | "pixrem": "^3.0.2", 52 | "pleeease-filters": "^3.0.1", 53 | "postcss": "^5.2.16", 54 | "postcss-calc": "^5.3.1", 55 | "postcss-color-function": "^3.0.0", 56 | "postcss-custom-media": "^5.0.1", 57 | "postcss-custom-properties": "^5.0.2", 58 | "postcss-custom-selectors": "^3.0.0", 59 | "postcss-flexbugs-fixes": "^2.1.0", 60 | "postcss-import": "^9.1.0", 61 | "postcss-loader": "^1.3.3", 62 | "postcss-media-minmax": "^2.1.2", 63 | "postcss-nesting": "^2.3.1", 64 | "postcss-selector-matches": "^2.0.5", 65 | "postcss-selector-not": "^2.0.0", 66 | "react-hot-loader": "^3.0.0-beta.2", 67 | "rimraf": "^2.6.1", 68 | "s3": "^4.4.0", 69 | "style-loader": "^0.14.1", 70 | "stylelint": "^7.9.0", 71 | "stylelint-config-standard": "^16.0.0", 72 | "url-loader": "^0.5.8", 73 | "webpack": "^2.2.1", 74 | "webpack-dev-middleware": "^1.10.1", 75 | "webpack-hot-middleware": "^2.17.1" 76 | }, 77 | "babel": { 78 | "presets": [ 79 | "latest", 80 | "stage-1", 81 | "react" 82 | ], 83 | "plugins": [ 84 | "transform-runtime" 85 | ] 86 | }, 87 | "eslintConfig": { 88 | "parser": "babel-eslint", 89 | "extends": "airbnb", 90 | "rules": { 91 | "react/jsx-filename-extension": "off", 92 | "react/require-default-props": "off", 93 | "import/no-extraneous-dependencies": "off" 94 | }, 95 | "env": { 96 | "browser": true 97 | } 98 | }, 99 | "stylelint": { 100 | "extends": "stylelint-config-standard", 101 | "rules": { 102 | "string-quotes": "single" 103 | } 104 | }, 105 | "scripts": { 106 | "eslint": "eslint components src test tools postcss.config.js webpack.config.js", 107 | "stylelint": "stylelint \"components/**/*.css\" \"src/**/*.css\"", 108 | "lint": "npm run eslint && npm run stylelint", 109 | "test": "mocha --compilers js:babel-register", 110 | "test:watch": "mocha --compilers js:babel-register --reporter min --watch", 111 | "build": "node tools/build.js", 112 | "build:debug": "node tools/build.js --debug", 113 | "publish": "node tools/publish.js", 114 | "publish:debug": "node tools/publish.js --debug", 115 | "start": "node tools/run.js" 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegohaz/generact/0ad333c9a6f328f024e00bf53c1197ac5586f260/test/fixtures/react-static-boilerplate/public/apple-touch-icon.png -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegohaz/generact/0ad333c9a6f328f024e00bf53c1197ac5586f260/test/fixtures/react-static-boilerplate/public/favicon.ico -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | JavaScript, HTML5, CSS3 15 | React, Redux, Material Design Lite (MDL) 16 | Babel, Webpack, Node.js 17 | React Static Boilerplate 18 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= config.title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | <%_ if (!debug && config.trackingID) { _%> 17 | 21 | 22 | <%_ } _%> 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/sitemap.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%_ urls.forEach(url => { _%> 4 | 5 | <%= config.url %><%= url.loc %> 6 | 7 | <%_ }); _%> 8 | 9 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/tile-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegohaz/generact/0ad333c9a6f328f024e00bf53c1197ac5586f260/test/fixtures/react-static-boilerplate/public/tile-wide.png -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/public/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegohaz/generact/0ad333c9a6f328f024e00bf53c1197ac5586f260/test/fixtures/react-static-boilerplate/public/tile.png -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/about/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React from 'react'; 12 | import Layout from '../../components/Layout'; 13 | import s from './styles.css'; 14 | import { title, html } from './index.md'; 15 | 16 | class AboutPage extends React.Component { 17 | 18 | componentDidMount() { 19 | document.title = title; 20 | } 21 | 22 | render() { 23 | return ( 24 | 25 |

{title}

26 |
30 | 31 | ); 32 | } 33 | 34 | } 35 | 36 | export default AboutPage; 37 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/about/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About Us 3 | --- 4 | 5 | ## Cadme comitum fecere 6 | 7 | Lorem markdownum velis auras figuram spes solebat spectabat, cum alium, 8 | plenissima aratri visae herbarum in corpore silvas consumpta. Subito virgae nec 9 | paratae flexit et niveae repperit erat paratu cum albis steterat conclamat hic! 10 | 11 | Nocte suae ligat! *Si* nitidum pervia, illa tua, ab minimo pasci dabitur? In 12 | fictus concurreret pennis, illis cum accipe rogavi in et nostro cum lacertis 13 | hostibus ab saxo ne. Genibusque vixque; sine videt terribili lucos ipsum vobis 14 | resque, et suum pietatis fulvis, est velle. Semele oscula ferat frigidus mactata 15 | montes, es me parari, piae. 16 | 17 | ## Inflataque ait leves frigida 18 | 19 | Letum per ipsa nostro animae, mari illuc in levi corpus aestibus excussam 20 | deflentem sic cuius. Venere dedit illa cui in quo senecta artus bella inficit, 21 | Achaica. Videbatur crinem resonantia alto dea umida dicitur igne; meus signa 22 | habet; est. Cognovit coepta: similes fugis: habuissem votivi liquida: ictus visi 23 | nostra me Adoni. 24 | 25 | ## Laedar cum margine quoque 26 | 27 | Quam dato ullis, acer venturi volantes! Tuam non non cursu acta hic, novem 28 | nutrit, in sidera viscera iam fontes tempora, omnes. Saturnius artus inquit, 29 | conatoque erectos lenius, carinae, ora est infamia elige per Medusaei induitur. 30 | Quem quem ab postquam tunc frondescere nodis capiam labique. Voluere luce 31 | Semeles. 32 | 33 | ``` 34 | if (delete(digital, hibernateSoft, dynamicExcelVpn) > io_secondary_led / 35 | 84) { 36 | disk = load; 37 | orientationPci.matrix_laptop(modelSsdTweet); 38 | } else { 39 | kdeEmoticonLed.mebibyte_algorithm_domain(2, 40 | hackerCtr.rom_iso_desktop.scarewarePrimaryBankruptcy(station, 41 | disk_mask_matrix, restore_crt)); 42 | cameraSpyware(4, multitasking(-3, log_dfs_controller)); 43 | menuCisc.swappable -= w(mount_vle_unicode, 5); 44 | } 45 | var optic_spider = newbieFunctionThick(-3, esportsKbpsUnix); 46 | var dvd_ctp_resolution = dithering; 47 | ``` 48 | 49 | ## Usus fixurus illi petunt 50 | 51 | Domosque tune amas mihi adhuc et *alter per* suasque versavitque iners 52 | crescentemque nomen verba nunc. Acervos hinc natus si habet. Et cervix imago 53 | quod! Arduus dolet! 54 | 55 | ``` 56 | cpcDdrCommand.window(moodleAlpha, im, server_alpha.doubleVrmlMonochrome( 57 | iosBar - -2, white_dual, ad(2, 94, 83))); 58 | mbps_typeface_publishing.bit.host_flash_capacity(click(90, 59 | cyberspace_srgb_pup - mpeg, marketing_trackback + 60 | table_plagiarism_domain)); 61 | syn_e = powerExtension * defragmentNntpOsd(alertOutputNode(pop, 62 | pageResponsiveDrive)); 63 | method -= switch_newsgroup_flaming; 64 | ``` 65 | 66 | Aliquid mansura arida altismunera **in illi**. Dignus vir pontum *crimen 67 | versabat* carpunt omnes rotis Canentem erant in Oebalio, et manu senecta 68 | iungere. Prima diurnis! 69 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/about/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/koistya/react-static-boilerplate 4 | * 5 | * Copyright © 2015-2016 Konstantin Tarkus (@koistya) 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | @media screen and (max-width: 1024px) { 12 | .content { 13 | padding: 0 16px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/error/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React from 'react'; 12 | import history from '../history'; 13 | import Link from '../../components/Link'; 14 | import s from './styles.css'; 15 | 16 | class ErrorPage extends React.Component { 17 | 18 | static propTypes = { 19 | error: React.PropTypes.object, // eslint-disable-line react/forbid-prop-types 20 | }; 21 | 22 | componentDidMount() { 23 | document.title = this.props.error && this.props.error.status === 404 ? 24 | 'Page Not Found' : 'Error'; 25 | } 26 | 27 | goBack = (event) => { 28 | event.preventDefault(); 29 | history.goBack(); 30 | }; 31 | 32 | render() { 33 | if (this.props.error) console.error(this.props.error); // eslint-disable-line no-console 34 | 35 | const [code, title] = this.props.error && this.props.error.status === 404 ? 36 | ['404', 'Page not found'] : 37 | ['Error', 'Oups, something went wrong']; 38 | 39 | return ( 40 |
41 |
42 |

{code}

43 |

{title}

44 | {code === '404' && 45 |

46 | The page you're looking for does not exist or an another error occurred. 47 |

48 | } 49 |

50 | Go back 51 | , or head over to the  52 | home page 53 | to choose a new direction. 54 |

55 |
56 |
57 | ); 58 | } 59 | 60 | } 61 | 62 | export default ErrorPage; 63 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/error/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/koistya/react-static-boilerplate 4 | * 5 | * Copyright © 2015-2016 Konstantin Tarkus (@koistya) 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | :root { 12 | --color: #607d8b; 13 | } 14 | 15 | .container { 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | display: flex; 20 | width: 100%; 21 | height: 100%; 22 | text-align: center; 23 | justify-content: center; 24 | align-items: center; 25 | } 26 | 27 | .content { 28 | padding-bottom: 80px; 29 | } 30 | 31 | .code { 32 | margin: 0; 33 | color: var(--color); 34 | letter-spacing: 0.02em; 35 | font-weight: 300; 36 | font-size: 15em; 37 | line-height: 1; 38 | } 39 | 40 | .title { 41 | padding-bottom: 0.5em; 42 | color: var(--color); 43 | letter-spacing: 0.02em; 44 | font-weight: 400; 45 | font-size: 2em; 46 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 47 | line-height: 1em; 48 | } 49 | 50 | .text { 51 | padding-bottom: 0; 52 | color: color(var(--color) alpha(50%)); 53 | font-size: 1.125em; 54 | line-height: 1.5em; 55 | } 56 | 57 | @media only screen and (max-width: 280px) { 58 | .container, 59 | .text { 60 | width: 95%; 61 | } 62 | 63 | .title { 64 | margin: 0 0 0.3em; 65 | font-size: 1.5em; 66 | } 67 | } 68 | 69 | @media screen and (max-width: 1024px) { 70 | .content { 71 | padding: 0 16px; 72 | } 73 | 74 | .code { 75 | font-size: 10em; 76 | } 77 | 78 | .title { 79 | font-size: 1.5em; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/history.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import createHistory from 'history/createBrowserHistory'; 12 | 13 | export default createHistory(); 14 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/home/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React, { PropTypes } from 'react'; 12 | import Layout from '../../components/Layout'; 13 | import s from './styles.css'; 14 | import { title, html } from './index.md'; 15 | 16 | class HomePage extends React.Component { 17 | 18 | static propTypes = { 19 | articles: PropTypes.arrayOf(PropTypes.shape({ 20 | url: PropTypes.string.isRequired, 21 | title: PropTypes.string.isRequired, 22 | author: PropTypes.string.isRequired, 23 | }).isRequired).isRequired, 24 | }; 25 | 26 | componentDidMount() { 27 | document.title = title; 28 | } 29 | 30 | render() { 31 | return ( 32 | 33 |
37 |

Articles

38 |
    39 | {this.props.articles.map(article => 40 |
  • 41 | {article.title} 42 | by {article.author} 43 |
  • , 44 | )} 45 |
46 |

47 |

48 |

49 | 50 | ); 51 | } 52 | 53 | } 54 | 55 | export default HomePage; 56 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/home/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Static Boilerplate 3 | --- 4 | 5 | ## Welcome! 6 | 7 | This is a single-page application powered by React and Material Design Lite (MDL). 8 | 9 | https://github.com/kriasoft/react-static-boilerplate 10 | 11 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/home/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/koistya/react-static-boilerplate 4 | * 5 | * Copyright © 2015-2016 Konstantin Tarkus (@koistya) 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | @media screen and (max-width: 1024px) { 12 | .content { 13 | padding: 0 16px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import 'babel-polyfill'; 12 | import 'whatwg-fetch'; 13 | 14 | import React from 'react'; 15 | import ReactDOM from 'react-dom'; 16 | import FastClick from 'fastclick'; 17 | import { Provider } from 'react-redux'; 18 | 19 | import store from './store'; 20 | import router from './router'; 21 | import history from './history'; 22 | 23 | let routes = require('./routes.json').default; // Loaded with utils/routes-loader.js 24 | 25 | const container = document.getElementById('container'); 26 | 27 | function renderComponent(component) { 28 | ReactDOM.render({component}, container); 29 | } 30 | 31 | // Find and render a web page matching the current URL path, 32 | // if such page is not found then render an error page (see routes.json, core/router.js) 33 | function render(location) { 34 | router.resolve(routes, location) 35 | .then(renderComponent) 36 | .catch(error => router.resolve(routes, { ...location, error }).then(renderComponent)); 37 | } 38 | 39 | // Handle client-side navigation by using HTML5 History API 40 | // For more information visit https://github.com/ReactJSTraining/history/tree/master/docs#readme 41 | history.listen(render); 42 | render(history.location); 43 | 44 | // Eliminates the 300ms delay between a physical tap 45 | // and the firing of a click event on mobile browsers 46 | // https://github.com/ftlabs/fastclick 47 | FastClick.attach(document.body); 48 | 49 | // Enable Hot Module Replacement (HMR) 50 | if (module.hot) { 51 | module.hot.accept('./routes.json', () => { 52 | routes = require('./routes.json').default; // eslint-disable-line global-require 53 | render(history.location); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import React from 'react'; 12 | 13 | function decodeParam(val) { 14 | if (!(typeof val === 'string' || val.length === 0)) { 15 | return val; 16 | } 17 | 18 | try { 19 | return decodeURIComponent(val); 20 | } catch (err) { 21 | if (err instanceof URIError) { 22 | err.message = `Failed to decode param '${val}'`; 23 | err.status = 400; 24 | } 25 | 26 | throw err; 27 | } 28 | } 29 | 30 | // Match the provided URL path pattern to an actual URI string. For example: 31 | // matchURI({ path: '/posts/:id' }, '/dummy') => null 32 | // matchURI({ path: '/posts/:id' }, '/posts/123') => { id: 123 } 33 | function matchURI(route, path) { 34 | const match = route.pattern.exec(path); 35 | 36 | if (!match) { 37 | return null; 38 | } 39 | 40 | const params = Object.create(null); 41 | 42 | for (let i = 1; i < match.length; i += 1) { 43 | params[route.keys[i - 1].name] = match[i] !== undefined ? decodeParam(match[i]) : undefined; 44 | } 45 | 46 | return params; 47 | } 48 | 49 | // Find the route matching the specified location (context), fetch the required data, 50 | // instantiate and return a React component 51 | function resolve(routes, context) { 52 | for (const route of routes) { // eslint-disable-line no-restricted-syntax 53 | const params = matchURI(route, context.error ? '/error' : context.pathname); 54 | 55 | if (!params) { 56 | continue; // eslint-disable-line no-continue 57 | } 58 | 59 | // Check if the route has any data requirements, for example: 60 | // { path: '/tasks/:id', data: { task: 'GET /api/tasks/$id' }, page: './pages/task' } 61 | if (route.data) { 62 | // Load page component and all required data in parallel 63 | const keys = Object.keys(route.data); 64 | return Promise.all([ 65 | route.load(), 66 | ...keys.map((key) => { 67 | const query = route.data[key]; 68 | const method = query.substring(0, query.indexOf(' ')); // GET 69 | let url = query.substr(query.indexOf(' ') + 1); // /api/tasks/$id 70 | // TODO: Optimize 71 | Object.keys(params).forEach((k) => { 72 | url = url.replace(`${k}`, params[k]); 73 | }); 74 | return fetch(url, { method }).then(resp => resp.json()); 75 | }), 76 | ]).then(([Page, ...data]) => { 77 | const props = keys.reduce((result, key, i) => ({ ...result, [key]: data[i] }), {}); 78 | return ; 79 | }); 80 | } 81 | 82 | return route.load().then(Page => ); 83 | } 84 | 85 | const error = new Error('Page not found'); 86 | error.status = 404; 87 | return Promise.reject(error); 88 | } 89 | 90 | export default { resolve }; 91 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/routes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "/", 4 | "page": "./home", 5 | "chunk": "main", 6 | "data": { 7 | "articles": "GET https://gist.githubusercontent.com/koistya/a32919e847531320675764e7308b796a/raw/articles.json" 8 | } 9 | }, 10 | { 11 | "path": "/error", 12 | "page": "./error", 13 | "chunk": "main" 14 | }, 15 | { 16 | "path": "/about", 17 | "page": "./about" 18 | }, 19 | { 20 | "path": "/tasks/:status(pending|completed)?", 21 | "page": "./home" 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/src/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import { createStore } from 'redux'; 12 | 13 | // Centralized application state 14 | // For more information visit http://redux.js.org/ 15 | const initialState = { count: 0 }; 16 | 17 | const store = createStore((state = initialState, action) => { 18 | // TODO: Add action handlers (aka "reducers") 19 | switch (action.type) { 20 | case 'COUNT': 21 | return { ...state, count: (state.count) + 1 }; 22 | default: 23 | return state; 24 | } 25 | }); 26 | 27 | export default store; 28 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "rules": { 6 | "no-console": 0, 7 | "no-unused-expressions": 0, 8 | "padded-blocks": 0 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/test/spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | import { expect } from 'chai'; 12 | 13 | describe('test suite', () => { 14 | 15 | it('test', () => { 16 | expect(true).to.be.true; 17 | }); 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "global-require": "off", 4 | "no-console": "off", 5 | "import/no-extraneous-dependencies": ["error", { "devDependencies": true }] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/README.md: -------------------------------------------------------------------------------- 1 | # Build Configuration and Automation Scripts 2 | 3 | ```bash 4 | ├── build.js # Compiles the app from source code 5 | ├── config.js # General application settings 6 | ├── markdown-loader.js # Webpack loader for .md files 7 | ├── postcss.config.js # PostCSS settings for compiling CSS files 8 | ├── publish.js # Builds and deploys the app to Firebase 9 | ├── routes-loader.js # Webpack loader for parsing src/routes.json 10 | ├── run.js # Compiles the app in watch mode and runs dev server 11 | ├── task.js # A custom minimalistic script/task runner 12 | └── webpack.config.js # Bundling and optimization settings 13 | ``` 14 | 15 | 16 | ### [`build.js`](./build.js) — compilation 17 | 18 | ```bash 19 | node tools/build # Compiles the app for production 20 | node tools/build --debug # Compiles the app in debug (non-optimized) mode 21 | ``` 22 | 23 | 24 | ### [`run.js`](./run.js) — launching for testing/debugging 25 | 26 | ```bash 27 | node tools/run # Compiles the app and opens it in a browser with "live reload" 28 | ``` 29 | 30 | 31 | ### [`publish.js`](./publish.js) — deployment 32 | 33 | ```bash 34 | node tools/publish.js # Compiles the app and deployes it to Firebase 35 | ``` 36 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | /* eslint-disable no-console, global-require */ 12 | 13 | const fs = require('fs'); 14 | const rimraf = require('rimraf'); 15 | const ejs = require('ejs'); 16 | const webpack = require('webpack'); 17 | const task = require('./task'); 18 | const config = require('./config'); 19 | 20 | // Copy ./index.html into the /public folder 21 | const html = task('html', () => { 22 | const webpackConfig = require('./webpack.config'); 23 | const assets = JSON.parse(fs.readFileSync('./public/dist/assets.json', 'utf8')); 24 | const template = fs.readFileSync('./public/index.ejs', 'utf8'); 25 | const render = ejs.compile(template, { filename: './public/index.ejs' }); 26 | const output = render({ debug: webpackConfig.debug, bundle: assets.main.js, config }); 27 | fs.writeFileSync('./public/index.html', output, 'utf8'); 28 | }); 29 | 30 | // Generate sitemap.xml 31 | const sitemap = task('sitemap', () => { 32 | const urls = require('../src/routes.json') 33 | .filter(x => !x.path.includes(':')) 34 | .map(x => ({ loc: x.path })); 35 | const template = fs.readFileSync('./public/sitemap.ejs', 'utf8'); 36 | const render = ejs.compile(template, { filename: './public/sitemap.ejs' }); 37 | const output = render({ config, urls }); 38 | fs.writeFileSync('public/sitemap.xml', output, 'utf8'); 39 | }); 40 | 41 | // Bundle JavaScript, CSS and image files with Webpack 42 | const bundle = task('bundle', () => { 43 | const webpackConfig = require('./webpack.config'); 44 | return new Promise((resolve, reject) => { 45 | webpack(webpackConfig).run((err, stats) => { 46 | if (err) { 47 | reject(err); 48 | } else { 49 | console.log(stats.toString(webpackConfig.stats)); 50 | resolve(); 51 | } 52 | }); 53 | }); 54 | }); 55 | 56 | // 57 | // Build website into a distributable format 58 | // ----------------------------------------------------------------------------- 59 | module.exports = task('build', () => { 60 | global.DEBUG = process.argv.includes('--debug') || false; 61 | rimraf.sync('public/dist/*', { nosort: true, dot: true }); 62 | return Promise.resolve() 63 | .then(bundle) 64 | .then(html) 65 | .then(sitemap); 66 | }); 67 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | module.exports = { 12 | title: 'React Static Boilerplate', // Your website title 13 | url: 'https://rsb.kriasoft.com', // Your website URL 14 | project: 'react-static-boilerplate', // Firebase project. See README.md -> How to Deploy 15 | trackingID: 'UA-XXXXX-Y', // Google Analytics Site's ID 16 | }; 17 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/markdown-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | const MarkdownIt = require('markdown-it'); 12 | const hljs = require('highlight.js'); 13 | const fm = require('front-matter'); 14 | 15 | module.exports = function markdownLoader(source) { 16 | this.cacheable(); 17 | 18 | const md = new MarkdownIt({ 19 | html: true, 20 | linkify: true, 21 | highlight: (str, lang) => { 22 | if (lang && hljs.getLanguage(lang)) { 23 | try { 24 | return hljs.highlight(lang, str).value; 25 | } catch (err) { console.error(err.stack); } // eslint-disable-line no-console 26 | } 27 | 28 | try { 29 | return hljs.highlightAuto(str).value; 30 | } catch (err) { console.error(err.stack); } // eslint-disable-line no-console 31 | 32 | return ''; 33 | }, 34 | }); 35 | 36 | const frontmatter = fm(source); 37 | frontmatter.attributes.html = md.render(frontmatter.body); 38 | 39 | return `module.exports = ${JSON.stringify(frontmatter.attributes)};`; 40 | }; 41 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/postcss.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | /* eslint-disable global-require */ 12 | 13 | module.exports = () => ({ 14 | plugins: [ 15 | // Transfer @import rule by inlining content, e.g. @import 'normalize.css' 16 | // https://github.com/postcss/postcss-import 17 | require('postcss-import')(), 18 | // W3C variables, e.g. :root { --color: red; } div { background: var(--color); } 19 | // https://github.com/postcss/postcss-custom-properties 20 | require('postcss-custom-properties')(), 21 | // W3C CSS Custom Media Queries, e.g. @custom-media --small-viewport (max-width: 30em); 22 | // https://github.com/postcss/postcss-custom-media 23 | require('postcss-custom-media')(), 24 | // CSS4 Media Queries, e.g. @media screen and (width >= 500px) and (width <= 1200px) { } 25 | // https://github.com/postcss/postcss-media-minmax 26 | require('postcss-media-minmax')(), 27 | // W3C CSS Custom Selectors, e.g. @custom-selector :--heading h1, h2, h3, h4, h5, h6; 28 | // https://github.com/postcss/postcss-custom-selectors 29 | require('postcss-custom-selectors')(), 30 | // W3C calc() function, e.g. div { height: calc(100px - 2em); } 31 | // https://github.com/postcss/postcss-calc 32 | require('postcss-calc')(), 33 | // Allows you to nest one style rule inside another 34 | // https://github.com/jonathantneal/postcss-nesting 35 | require('postcss-nesting')(), 36 | // W3C color() function, e.g. div { background: color(red alpha(90%)); } 37 | // https://github.com/postcss/postcss-color-function 38 | require('postcss-color-function')(), 39 | // Convert CSS shorthand filters to SVG equivalent, e.g. .blur { filter: blur(4px); } 40 | // https://github.com/iamvdo/pleeease-filters 41 | require('pleeease-filters')(), 42 | // Generate pixel fallback for "rem" units, e.g. div { margin: 2.5rem 2px 3em 100%; } 43 | // https://github.com/robwierzbowski/node-pixrem 44 | require('pixrem')(), 45 | // W3C CSS Level4 :matches() pseudo class, e.g. p:matches(:first-child, .special) { } 46 | // https://github.com/postcss/postcss-selector-matches 47 | require('postcss-selector-matches')(), 48 | // Transforms :not() W3C CSS Level 4 pseudo class to :not() CSS Level 3 selectors 49 | // https://github.com/postcss/postcss-selector-not 50 | require('postcss-selector-not')(), 51 | // Postcss flexbox bug fixer 52 | // https://github.com/luisrudge/postcss-flexbugs-fixes 53 | require('postcss-flexbugs-fixes')(), 54 | // Add vendor prefixes to CSS rules using values from caniuse.com 55 | // https://github.com/postcss/autoprefixer 56 | require('autoprefixer')(), 57 | ], 58 | }); 59 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/publish.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | const path = require('path'); 12 | const firebase = require('firebase-tools'); 13 | const build = require('./build'); 14 | const task = require('./task'); 15 | const config = require('./config'); 16 | 17 | // Build and deploy the app to Firebase 18 | module.exports = task('deploy', () => Promise.resolve() 19 | .then(() => build()) 20 | .then(() => firebase.login({ nonInteractive: false })) 21 | .then(() => firebase.deploy({ 22 | project: config.project, 23 | cwd: path.resolve(__dirname, '../'), 24 | })) 25 | .then(() => { setTimeout(() => process.exit()); })); 26 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/routes-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | const toRegExp = require('path-to-regexp'); 12 | 13 | function escape(text) { 14 | return text.replace('\'', '\\\'').replace('\\', '\\\\'); 15 | } 16 | 17 | /** 18 | * Converts application routes from JSON to JavaScript. For example, a route like 19 | * 20 | * { 21 | * "path": "/about", 22 | * "page": "./about" 23 | * } 24 | * 25 | * becomes 26 | * 27 | * { 28 | * path: '/about', 29 | * pattern: /^\\/about(?:\/(?=$))?$/i, 30 | * keys: [], 31 | * page: './about', 32 | * load: function () { return new Promise(resolve => require(['./about'], resolve)); } 33 | * } 34 | */ 35 | module.exports = function routesLoader(source) { 36 | this.cacheable(); 37 | 38 | const output = ['[\n']; 39 | const routes = JSON.parse(source); 40 | 41 | for (const route of routes) { // eslint-disable-line no-restricted-syntax 42 | const keys = []; 43 | const pattern = toRegExp(route.path, keys); 44 | const require = route.chunk && route.chunk === 'main' ? 45 | module => `Promise.resolve(require('${escape(module)}').default)` : 46 | module => `new Promise(function (resolve, reject) { 47 | try { 48 | require.ensure(['${escape(module)}'], function (require) { 49 | resolve(require('${escape(module)}').default); 50 | }${typeof route.chunk === 'string' ? `, '${escape(route.chunk)}'` : ''}); 51 | } catch (err) { 52 | reject(err); 53 | } 54 | })`; 55 | output.push(' {\n'); 56 | output.push(` path: '${escape(route.path)}',\n`); 57 | output.push(` pattern: ${pattern.toString()},\n`); 58 | output.push(` keys: ${JSON.stringify(keys)},\n`); 59 | output.push(` page: '${escape(route.page)}',\n`); 60 | if (route.data) { 61 | output.push(` data: ${JSON.stringify(route.data)},\n`); 62 | } 63 | output.push(` load() {\n return ${require(route.page)};\n },\n`); // eslint-disable-line import/no-dynamic-require 64 | output.push(' },\n'); 65 | } 66 | 67 | output.push(']'); 68 | 69 | return `export default ${output.join('')};`; 70 | }; 71 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | const fs = require('fs'); 12 | const ejs = require('ejs'); 13 | const rimraf = require('rimraf'); 14 | const webpack = require('webpack'); 15 | const Browsersync = require('browser-sync'); 16 | const task = require('./task'); 17 | const config = require('./config'); 18 | 19 | global.HMR = !process.argv.includes('--no-hmr'); // Hot Module Replacement (HMR) 20 | 21 | // Build the app and launch it in a browser for testing via Browsersync 22 | module.exports = task('run', () => new Promise((resolve) => { 23 | rimraf.sync('public/dist/*', { nosort: true, dot: true }); 24 | let count = 0; 25 | const bs = Browsersync.create(); 26 | const webpackConfig = require('./webpack.config'); 27 | const compiler = webpack(webpackConfig); 28 | // Node.js middleware that compiles application in watch mode with HMR support 29 | // http://webpack.github.io/docs/webpack-dev-middleware.html 30 | const webpackDevMiddleware = require('webpack-dev-middleware')(compiler, { 31 | publicPath: webpackConfig.output.publicPath, 32 | stats: webpackConfig.stats, 33 | }); 34 | 35 | compiler.plugin('done', (stats) => { 36 | // Generate index.html page 37 | const bundle = stats.compilation.chunks.find(x => x.name === 'main').files[0]; 38 | const template = fs.readFileSync('./public/index.ejs', 'utf8'); 39 | const render = ejs.compile(template, { filename: './public/index.ejs' }); 40 | const output = render({ debug: true, bundle: `/dist/${bundle}`, config }); 41 | fs.writeFileSync('./public/index.html', output, 'utf8'); 42 | 43 | // Launch Browsersync after the initial bundling is complete 44 | // For more information visit https://browsersync.io/docs/options 45 | count += 1; 46 | if (count === 1) { 47 | bs.init({ 48 | port: process.env.PORT || 3000, 49 | ui: { port: Number(process.env.PORT || 3000) + 1 }, 50 | server: { 51 | baseDir: 'public', 52 | middleware: [ 53 | webpackDevMiddleware, 54 | require('webpack-hot-middleware')(compiler), 55 | require('connect-history-api-fallback')(), 56 | ], 57 | }, 58 | }, resolve); 59 | } 60 | }); 61 | })); 62 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/task.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | /* 12 | * Minimalistic script runner. Usage example: 13 | * 14 | * node tools/deploy.js 15 | * Starting 'deploy'... 16 | * Starting 'build'... 17 | * Finished 'build' in 3212ms 18 | * Finished 'deploy' in 582ms 19 | */ 20 | 21 | function run(task, action, ...args) { 22 | const command = process.argv[2]; 23 | const taskName = command && !command.startsWith('-') ? `${task}:${command}` : task; 24 | const start = new Date(); 25 | process.stdout.write(`Starting '${taskName}'...\n`); 26 | return Promise.resolve().then(() => action(...args)).then(() => { 27 | process.stdout.write(`Finished '${taskName}' after ${new Date().getTime() - start.getTime()}ms\n`); 28 | }, err => process.stderr.write(`${err.stack}\n`)); 29 | } 30 | 31 | process.nextTick(() => require.main.exports()); 32 | module.exports = (task, action) => run.bind(undefined, task, action); 33 | -------------------------------------------------------------------------------- /test/fixtures/react-static-boilerplate/tools/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Static Boilerplate 3 | * https://github.com/kriasoft/react-static-boilerplate 4 | * 5 | * Copyright © 2015-present Kriasoft, LLC. All rights reserved. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE.txt file in the root directory of this source tree. 9 | */ 10 | 11 | /* eslint-disable global-require, no-confusing-arrow, max-len */ 12 | 13 | const path = require('path'); 14 | const webpack = require('webpack'); 15 | const AssetsPlugin = require('assets-webpack-plugin'); 16 | const pkg = require('../package.json'); 17 | 18 | const isDebug = global.DEBUG === false ? false : !process.argv.includes('--release'); 19 | const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v'); 20 | const useHMR = !!global.HMR; // Hot Module Replacement (HMR) 21 | const babelConfig = Object.assign({}, pkg.babel, { 22 | babelrc: false, 23 | cacheDirectory: useHMR, 24 | presets: pkg.babel.presets.map(x => x === 'latest' ? ['latest', { es2015: { modules: false } }] : x), 25 | }); 26 | 27 | // Webpack configuration (main.js => public/dist/main.{hash}.js) 28 | // http://webpack.github.io/docs/configuration.html 29 | const config = { 30 | 31 | // The base directory for resolving the entry option 32 | context: path.resolve(__dirname, '../src'), 33 | 34 | // The entry point for the bundle 35 | entry: [ 36 | /* Material Design Lite (https://getmdl.io) */ 37 | '!!style-loader!css-loader!react-mdl/extra/material.min.css', 38 | 'react-mdl/extra/material.min.js', 39 | /* The main entry point of your JavaScript application */ 40 | './main.js', 41 | ], 42 | 43 | // Options affecting the output of the compilation 44 | output: { 45 | path: path.resolve(__dirname, '../public/dist'), 46 | publicPath: isDebug ? `http://localhost:${process.env.PORT || 3000}/dist/` : '/dist/', 47 | filename: isDebug ? '[name].js?[hash]' : '[name].[hash].js', 48 | chunkFilename: isDebug ? '[id].js?[chunkhash]' : '[id].[chunkhash].js', 49 | sourcePrefix: ' ', 50 | }, 51 | 52 | // Developer tool to enhance debugging, source maps 53 | // http://webpack.github.io/docs/configuration.html#devtool 54 | devtool: isDebug ? 'source-map' : false, 55 | 56 | // What information should be printed to the console 57 | stats: { 58 | colors: true, 59 | reasons: isDebug, 60 | hash: isVerbose, 61 | version: isVerbose, 62 | timings: true, 63 | chunks: isVerbose, 64 | chunkModules: isVerbose, 65 | cached: isVerbose, 66 | cachedAssets: isVerbose, 67 | }, 68 | 69 | // The list of plugins for Webpack compiler 70 | plugins: [ 71 | new webpack.DefinePlugin({ 72 | 'process.env.NODE_ENV': isDebug ? '"development"' : '"production"', 73 | __DEV__: isDebug, 74 | }), 75 | // Emit a JSON file with assets paths 76 | // https://github.com/sporto/assets-webpack-plugin#options 77 | new AssetsPlugin({ 78 | path: path.resolve(__dirname, '../public/dist'), 79 | filename: 'assets.json', 80 | prettyPrint: true, 81 | }), 82 | new webpack.LoaderOptionsPlugin({ 83 | debug: isDebug, 84 | minimize: !isDebug, 85 | }), 86 | ], 87 | 88 | // Options affecting the normal modules 89 | module: { 90 | rules: [ 91 | { 92 | test: /\.jsx?$/, 93 | include: [ 94 | path.resolve(__dirname, '../src'), 95 | path.resolve(__dirname, '../components'), 96 | ], 97 | loader: 'babel-loader', 98 | options: babelConfig, 99 | }, 100 | { 101 | test: /\.css/, 102 | use: [ 103 | { 104 | loader: 'style-loader', 105 | }, 106 | { 107 | loader: 'css-loader', 108 | options: { 109 | sourceMap: isDebug, 110 | importLoaders: true, 111 | // CSS Modules https://github.com/css-modules/css-modules 112 | modules: true, 113 | localIdentName: isDebug ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]', 114 | // CSS Nano http://cssnano.co/options/ 115 | minimize: !isDebug, 116 | }, 117 | }, 118 | { 119 | loader: 'postcss-loader', 120 | options: { 121 | config: './tools/postcss.config.js', 122 | }, 123 | }, 124 | ], 125 | }, 126 | { 127 | test: /\.json$/, 128 | exclude: [ 129 | path.resolve(__dirname, '../src/routes.json'), 130 | ], 131 | loader: 'json-loader', 132 | }, 133 | { 134 | test: /\.json$/, 135 | include: [ 136 | path.resolve(__dirname, '../src/routes.json'), 137 | ], 138 | use: [ 139 | { 140 | loader: 'babel-loader', 141 | options: babelConfig, 142 | }, 143 | { 144 | loader: path.resolve(__dirname, './routes-loader.js'), 145 | }, 146 | ], 147 | }, 148 | { 149 | test: /\.md$/, 150 | loader: path.resolve(__dirname, './markdown-loader.js'), 151 | }, 152 | { 153 | test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/, 154 | loader: 'url-loader', 155 | options: { 156 | limit: 10000, 157 | }, 158 | }, 159 | { 160 | test: /\.(eot|ttf|wav|mp3)$/, 161 | loader: 'file-loader', 162 | }, 163 | ], 164 | }, 165 | }; 166 | 167 | // Optimize the bundle in release (production) mode 168 | if (!isDebug) { 169 | config.plugins.push(new webpack.optimize.UglifyJsPlugin({ 170 | sourceMap: true, 171 | compress: { 172 | warnings: isVerbose, 173 | }, 174 | })); 175 | config.plugins.push(new webpack.optimize.AggressiveMergingPlugin()); 176 | } 177 | 178 | // Hot Module Replacement (HMR) + React Hot Reload 179 | if (isDebug && useHMR) { 180 | babelConfig.plugins.unshift('react-hot-loader/babel'); 181 | config.entry.unshift('react-hot-loader/patch', 'webpack-hot-middleware/client'); 182 | config.plugins.push(new webpack.HotModuleReplacementPlugin()); 183 | config.plugins.push(new webpack.NoEmitOnErrorsPlugin()); 184 | } 185 | 186 | module.exports = config; 187 | -------------------------------------------------------------------------------- /test/utils.test.js: -------------------------------------------------------------------------------- 1 | import { join, relative } from 'path' 2 | import { tmpdir } from 'os' 3 | import { remove, pathExistsSync, readFileSync } from 'fs-extra' 4 | import { 5 | getComponentName, 6 | getComponentFolder, 7 | isSingleFile, 8 | getFiles, 9 | getComponentFiles, 10 | replaceContents, 11 | replicate, 12 | } from '../src/utils' 13 | 14 | test('getComponentName', () => { 15 | expect(getComponentName('src/components/Foo/index.js')).toBe('Foo') 16 | expect(getComponentName('src/components/Foo/index.jsx')).toBe('Foo') 17 | expect(getComponentName('src/components/Foo/index.ts')).toBe('Foo') 18 | expect(getComponentName('src/components/Foo/index.tsx')).toBe('Foo') 19 | expect(getComponentName('src/components/Foo.js')).toBe('Foo') 20 | expect(getComponentName('src/components/Foo/Foo.js')).toBe('Foo') 21 | expect(getComponentName('src/components/Foo/Bar.js')).toBe('Bar') 22 | expect(getComponentName('src/components/FooBar.js')).toBe('FooBar') 23 | expect(getComponentName('src/components/Foo/index.jsx')).toBe('Foo') 24 | expect(getComponentName('src/components/foo-bar.jsx')).toBe('FooBar') 25 | expect(getComponentName('src/components/foo-bar.tsx')).toBe('FooBar') 26 | }) 27 | 28 | test('getComponentFolder', () => { 29 | expect(getComponentFolder('src/components/Foo/index.js')).toBe('src/components') 30 | expect(getComponentFolder('src/components/Foo/Foo.js')).toBe('src/components') 31 | expect(getComponentFolder('src/Bar/Foo/Foo.js')).toBe('src/Bar') 32 | expect(getComponentFolder('src/components/foo-bar.jsx')).toBe('src/components') 33 | }) 34 | 35 | test('isSingleFile', () => { 36 | expect(isSingleFile('src/components/Foo/index.js')).toBe(false) 37 | expect(isSingleFile('src/components/Foo/index.jsx')).toBe(false) 38 | expect(isSingleFile('src/components/Foo/Foo.js')).toBe(false) 39 | expect(isSingleFile('src/components/Foo.js')).toBe(true) 40 | expect(isSingleFile('src/components/Foo/Bar.js')).toBe(true) 41 | expect(isSingleFile('src/components/foo-bar.jsx')).toBe(true) 42 | expect(isSingleFile('src/components/FooBar/foo-bar.jsx')).toBe(false) 43 | }) 44 | 45 | describe('getFiles', () => { 46 | test('Button', () => { 47 | const cwd = join(__dirname, 'fixtures/create-react-app/src/components/Button') 48 | expect(getFiles(cwd)).toEqual([ 49 | join(cwd, 'Button.css'), 50 | join(cwd, 'Button.js'), 51 | join(cwd, 'Button.test.js'), 52 | ]) 53 | }) 54 | 55 | test('App', async () => { 56 | const cwd = join(__dirname, 'fixtures/create-react-app/src') 57 | expect(getFiles(cwd, 'App')).toEqual([ 58 | join(cwd, 'App.css'), 59 | join(cwd, 'App.js'), 60 | join(cwd, 'App.test.js'), 61 | ]) 62 | }) 63 | 64 | test('Link', async () => { 65 | const cwd = join(__dirname, 'fixtures/react-static-boilerplate/components/Link') 66 | expect(getFiles(cwd)).toEqual([ 67 | join(cwd, 'Link.js'), 68 | join(cwd, 'package.json'), 69 | ]) 70 | }) 71 | }) 72 | 73 | test('getComponentFiles', async () => { 74 | expect(await getComponentFiles(join(__dirname, 'fixtures/create-react-app'))).toEqual([{ 75 | name: expect.stringMatching(/App/), 76 | short: 'App', 77 | value: join(__dirname, 'fixtures/create-react-app/src/App.js'), 78 | }, { 79 | name: expect.stringMatching(/Button/), 80 | short: 'Button', 81 | value: join(__dirname, 'fixtures/create-react-app/src/components/Button/Button.js'), 82 | }]) 83 | }) 84 | 85 | test('replaceContents', () => { 86 | const contents = ` 87 | import './Button.css' 88 | import ButtonComponent from '../ButtonComponent' 89 | import NodeButtonComponent from 'node-button-component' 90 | import NodeYellowButtonComponent from 'node-component/YellowButton' 91 | import SimpleButton from './SimpleButton' 92 | import { someButtonUtil } from './SimpleButtonUtils' 93 | import { someButtonUtil } from "./SimpleButtonUtils"; 94 | const Button = () =>