├── .gitignore ├── .nvmrc ├── .travis.yml ├── LICENSE ├── README.md ├── __tests__ ├── cutify.spec.ts ├── index.tsx └── injectCSS.spec.ts ├── custom-typings ├── .gitkeep └── atlaskit.d.ts ├── demo.png ├── example ├── app.tsx ├── index.html ├── index.tsx └── styled.ts ├── package.json ├── src ├── cute.tsx ├── cutify.ts ├── index.ts ├── injectCSS.ts └── styles.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | dist 63 | publish_dist -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.5.0 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | cache: yarn 5 | script: 6 | - yarn 7 | - yarn test:ci 8 | notifications: 9 | email: false -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Hector Zarco 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 | # react-cute [![Build Status](https://travis-ci.org/zzarcon/react-cute.svg?branch=master)](https://travis-ci.org/zzarcon/react-cute) 2 | > Cute JSON's in React 3 | 4 |
5 | Logo 6 |

7 |
8 | 9 | # Demo 👀 10 | [https://zzarcon.github.io/react-cute](https://zzarcon.github.io/react-cute) 11 | 12 | # Install 🚀 13 | ``` 14 | $ yarn add react-cute 15 | ``` 16 | 17 | # Usage ⛏ 18 | 19 | ```jsx 20 | import Cute from 'react-cute'; 21 | 22 | const json = { 23 | name: 'hector', 24 | twitter: '@zzarcon' 25 | }; 26 | 27 | 28 | ``` 29 | 30 | # Features ✨ 31 | * Out of the box UI 32 | * Different colors based on value type 33 | * Render values not showed by default 34 | * Dependency free + no external CSS 35 | * [3kB size](https://bundlephobia.com/result?p=react-cute) 36 | 37 | # Author 👶 38 | [@zzarcon](https://twitter.com/zzarcon) 39 | -------------------------------------------------------------------------------- /__tests__/cutify.spec.ts: -------------------------------------------------------------------------------- 1 | import { cutify } from "../src/cutify"; 2 | 3 | describe('cutify()', () => { 4 | it('should highlight colons', () => { 5 | expect(cutify({name: 'hector'})).toEqual(` 6 | { 7 | name: hector 8 | } 9 | `.trim()); 10 | }); 11 | }); -------------------------------------------------------------------------------- /__tests__/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {shallow, mount} from 'enzyme'; 3 | import Cute from '../src'; 4 | 5 | describe('Cute', () => { 6 | const setup = () => { 7 | const json = { 8 | name: 'hector', 9 | nick: 'zzarcon', 10 | age: 26, 11 | getName() { 12 | return this.name; 13 | }, 14 | info: { 15 | hobbies: ['coding', 'skate', 'gym'] 16 | } 17 | }; 18 | const component = mount(); 19 | const html = component.find('pre').html(); 20 | 21 | return { 22 | component, 23 | json, 24 | html 25 | }; 26 | }; 27 | 28 | it('should render a cute json', () => { 29 | const {component} = setup(); 30 | 31 | expect(component.find('pre')).toHaveLength(1); 32 | }); 33 | 34 | it.skip('should render all keys', () => { 35 | 36 | }); 37 | 38 | it('should recognize colons', () => { 39 | const {html} = setup(); 40 | 41 | expect(html).toContain('name:'); 42 | expect(html).toContain('nick:'); 43 | }); 44 | 45 | it('should recognize strings', () => { 46 | const {html} = setup(); 47 | 48 | expect(html).toContain('hector'); 49 | expect(html).toContain('zzarcon'); 50 | }); 51 | 52 | it('should recognize numbers', () => { 53 | const {html} = setup(); 54 | 55 | expect(html).toContain('26'); 56 | }); 57 | 58 | it('should recognize functions', () => { 59 | const {html} = setup(); 60 | 61 | expect(html).toContain('getName: [Function]'); 62 | }); 63 | 64 | it.skip('should work with nested objects', () => { 65 | 66 | }); 67 | 68 | it.skip('should work with Arrays', () => { 69 | 70 | }); 71 | 72 | it.skip('should work with Classes', () => { 73 | 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /__tests__/injectCSS.spec.ts: -------------------------------------------------------------------------------- 1 | import { injectCSS } from "../src/injectCSS"; 2 | 3 | describe('injectCSS()', () => { 4 | it('should inject styled on the page
', () => { 5 | const spy = jest.spyOn(document, 'createTextNode'); 6 | injectCSS(`body {background: black;}`); 7 | 8 | expect(document.head.childElementCount).toEqual(1); 9 | expect(spy).toHaveBeenCalledTimes(1); 10 | expect(spy).lastCalledWith(`body {background: black;}`); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /custom-typings/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzarcon/react-cute/b4695f0ef3f3ae59a0c98eda2971a086ff49a88e/custom-typings/.gitkeep -------------------------------------------------------------------------------- /custom-typings/atlaskit.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@atlaskit/*'; -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzarcon/react-cute/b4695f0ef3f3ae59a0c98eda2971a086ff49a88e/demo.png -------------------------------------------------------------------------------- /example/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Component} from 'react'; 3 | import {GHCorner} from 'react-gh-corner'; 4 | import Cute from '../src'; 5 | import {Title, AppWrapper, JsonWrapper, JsonsWrapper} from './styled'; 6 | 7 | export interface AppState { 8 | json: Object; 9 | } 10 | 11 | const repoUrl = 'https://github.com/zzarcon/react-cute'; 12 | const initialJson = { 13 | user: { 14 | name: 'Hector', 15 | lastName: 'Zarco' 16 | }, 17 | isGeek: true, 18 | status: undefined, 19 | foo: null, 20 | age: 26, 21 | getName() { 22 | return this.user.name; 23 | }, 24 | countries: [{ 25 | name: 'Spain', 26 | cities: ['Valencia', 'Barcelona'] 27 | }] 28 | }; 29 | 30 | export default class App extends Component <{}, AppState> { 31 | state: AppState = { 32 | json: initialJson 33 | } 34 | 35 | onChange = (e: any) => { 36 | try { 37 | const text = e.target.textContent; 38 | const json = JSON.parse(text); 39 | 40 | this.setState({json}); 41 | } catch (e) { 42 | 43 | } 44 | } 45 | 46 | renderUglyJson() { 47 | const {json} = this.state; 48 | return ( 49 | 50 |

default

51 |
 52 |           {JSON.stringify(json, undefined, 2)}
 53 |         
54 |
55 | ) 56 | } 57 | 58 | renderCute() { 59 | const {json} = this.state; 60 | 61 | return ( 62 | 63 |

react-cute

64 | 67 |
68 | ) 69 | } 70 | 71 | renderFeatures() { 72 | return ( 73 |
74 |

75 | Features ✨ 76 |

77 |
    78 |
  • 79 | Out of the box UI 80 |
  • 81 |
  • 82 | Different colors based on value type 83 |
  • 84 |
  • 85 | Render values not showed by default 86 |
  • 87 |
  • 88 | Dependency free + no external CSS 89 |
  • 90 |
  • 91 | 92 | 3kB size 93 | 94 |
  • 95 |
96 |

97 | Install 🚀 98 |

99 |
100 |           $ yarn add react-cute
101 |         
102 |
103 | ) 104 | } 105 | 106 | render() { 107 | return ( 108 | 109 | 110 | Render cute JSON in React 111 | 112 | {this.renderUglyJson()} 113 | {this.renderCute()} 114 | 115 | {this.renderFeatures()} 116 | 117 | ) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /example/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import App from './app'; 4 | 5 | ReactDOM.render(, document.getElementById('app')); 6 | -------------------------------------------------------------------------------- /example/styled.ts: -------------------------------------------------------------------------------- 1 | import styled, {injectGlobal} from 'styled-components'; 2 | 3 | injectGlobal` 4 | body { 5 | font-family: Helvetica; 6 | background-color: #D8D1F5; 7 | color: #444; 8 | } 9 | 10 | * { 11 | box-sizing: content-box; 12 | } 13 | `; 14 | 15 | export const AppWrapper = styled.div` 16 | background: white; 17 | padding: 20px; 18 | border-radius: 3px; 19 | width: 600px; 20 | margin: 20px auto; 21 | `; 22 | 23 | export const JsonWrapper = styled.div` 24 | border: 1px solid #ccc; 25 | padding: 15px 30px; 26 | border-radius: 3px; 27 | 28 | h1 { 29 | margin: 0; 30 | text-align: center; 31 | border-bottom: 1px solid #ccc; 32 | font-size: 18px; 33 | } 34 | `; 35 | 36 | export const JsonsWrapper = styled.div` 37 | display: flex; 38 | justify-content: space-around; 39 | `; 40 | 41 | export const Title = styled.div` 42 | font-size: 20px; 43 | margin-bottom: 15px; 44 | border-left: 3px solid #ccc; 45 | padding-left: 15px; 46 | color: #6a737d; 47 | ` 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-cute", 3 | "version": "1.0.2", 4 | "description": "Cute JSON's in React", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "bootstrap": "ts-react-toolbox init", 9 | "dev": "ts-react-toolbox dev", 10 | "test": "ts-react-toolbox test", 11 | "test:ci": "ts-react-toolbox test --runInBand --coverage", 12 | "build": "ts-react-toolbox build", 13 | "release": "ts-react-toolbox release", 14 | "lint": "ts-react-toolbox lint", 15 | "static": "ts-react-toolbox publish", 16 | "format": "ts-react-toolbox format", 17 | "analyze": "ts-react-toolbox analyze" 18 | }, 19 | "engines": { 20 | "node": ">=8.5.0" 21 | }, 22 | "devDependencies": { 23 | "ts-react-toolbox": "^0.1.15" 24 | }, 25 | "peerDependencies": { 26 | "react": "^16.3.0" 27 | }, 28 | "files": [ 29 | "dist" 30 | ], 31 | "keywords": [ 32 | "react", 33 | "json", 34 | "stringify", 35 | "pretty", 36 | "prettify", 37 | "toString", 38 | "cute", 39 | "theme" 40 | ], 41 | "repository": "https://github.com/zzarcon/react-cute" 42 | } 43 | -------------------------------------------------------------------------------- /src/cute.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Component } from 'react'; 3 | import * as styles from './styles'; 4 | import { cutify } from './cutify'; 5 | 6 | export interface CuteProps { 7 | json: Object; 8 | } 9 | 10 | export interface CuteState { 11 | 12 | } 13 | 14 | export class Cute extends Component { 15 | render() { 16 | const { json } = this.props; 17 | const cutifiedJson = cutify(json); 18 | 19 | return ( 20 |
21 |     );
22 |   }
23 | }


--------------------------------------------------------------------------------
/src/cutify.ts:
--------------------------------------------------------------------------------
 1 | import {ClassNames} from './styles';
 2 | 
 3 | const regEx = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g;
 4 | const syntaxHighlight = (json: Object): string => {
 5 |   const jsonString = JSON.stringify(json, replacer, 2)
 6 |     .replace(/&/g, '&')
 7 |     .replace(//g, '>');
 9 | 
10 |   return jsonString.replace(regEx, (match) => {
11 |     let className = ClassNames.number;
12 |     let text = match;
13 |     
14 |     if (/^"/.test(match)) {
15 |       if (/:$/.test(match)) {
16 |         className = ClassNames.key;
17 |         text = text.replace(':', `:`);
18 |       } else {
19 |         if (match === '"undefined"') {
20 |           className = ClassNames.undefined;
21 |         } else if (match === '"[Function]"') {
22 |           className = ClassNames.function;
23 |         } else {
24 |           className = ClassNames.string;
25 |         }
26 |       }
27 |     } else if (/true|false/.test(match)) {
28 |       className = ClassNames.boolean;
29 |     } else if (/null/.test(match)) {
30 |       className = ClassNames.null;
31 |     } 
32 | 
33 |     return `${text}`;
34 |   });
35 | }
36 | 
37 | const replacer = (_: string, value: any) => {
38 |   if (typeof value === 'function') {
39 |     return '[Function]';
40 |   }
41 | 
42 |   if (typeof value === 'undefined') {
43 |     return 'undefined';
44 |   }
45 | 
46 |   return value;
47 | }
48 | 
49 | export const cutify = (json: Object): string => {
50 |   return syntaxHighlight(json)
51 |     .replace(/"/g, '');
52 | }


--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './cute';
2 | export {Cute as default} from './cute';


--------------------------------------------------------------------------------
/src/injectCSS.ts:
--------------------------------------------------------------------------------
1 | export const injectCSS = (styles: string) => {
2 |   const style = document.createElement('style');
3 | 
4 |   style.type = 'text/css';
5 |   style.appendChild(document.createTextNode(styles));
6 |   document.head.appendChild(style);
7 | };


--------------------------------------------------------------------------------
/src/styles.ts:
--------------------------------------------------------------------------------
 1 | import { CSSProperties } from "react";
 2 | import {injectCSS} from './injectCSS';
 3 | 
 4 | export enum ClassNames {
 5 |   string = 'cute-string',
 6 |   undefined = 'cute-undefined',
 7 |   function = 'cute-function',
 8 |   number = 'cute-number',
 9 |   boolean = 'cute-boolean',
10 |   null = 'cute-null',
11 |   colon = 'cute-colon',
12 |   key = 'cute-key'
13 | };
14 | 
15 | injectCSS(`
16 |   .${ClassNames.string} {
17 |     color: #f1fa8c; 
18 |   }
19 |   .${ClassNames.number} {
20 |     color: #50fa7b; 
21 |   }
22 |   .${ClassNames.boolean} {
23 |     color: #ff79c6; 
24 |   }
25 |   .${ClassNames.function} {
26 |     color: #bd93f9; 
27 |   }
28 |   .${ClassNames.null} {
29 |     color: #bd93f9; 
30 |   }
31 |   .${ClassNames.undefined} {
32 |     color: #bd93f9;
33 |   }
34 |   .${ClassNames.key} {
35 |     color: #66d9ef;
36 |     margin-right: 5px;
37 |   }
38 |   .${ClassNames.colon} {
39 |     color: #f8f8f2;
40 |     margin-left: 1px;
41 |   }
42 | `);
43 | 
44 | export const pre: CSSProperties = {
45 |   fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial',
46 |   fontSize: 15,
47 |   lineHeight: '20px',
48 |   display: 'inline-block',
49 |   borderRadius: 3,
50 |   padding: '10px 15px',
51 |   background: '#272822',
52 |   color: '#f8f8f2',
53 |   textShadow: '1px 1px black',
54 |   overflow: 'auto'
55 | };


--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "compilerOptions": {
 3 |     "strict": true,
 4 |     "strictNullChecks": true,
 5 |     "strictFunctionTypes": true,
 6 |     "noImplicitThis": true,
 7 |     "alwaysStrict": true,
 8 |     "noUnusedLocals": true,
 9 |     "noUnusedParameters": true,
10 |     "noImplicitReturns": true,
11 |     "noFallthroughCasesInSwitch": true,
12 |     "noImplicitAny": true,
13 |     "removeComments": true,
14 |     "target": "es5",
15 |     "lib": [
16 |       "dom",
17 |       "es5",
18 |       "scripthost",
19 |       "es2015.collection",
20 |       "es2015.symbol",
21 |       "es2015.iterable",
22 |       "es2015.promise"
23 |     ],
24 |     "jsx": "react"
25 |   },
26 |   "files": [
27 |     "./custom-typings/atlaskit.d.ts"
28 |   ]
29 | }


--------------------------------------------------------------------------------