├── .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 [](https://travis-ci.org/zzarcon/react-cute)
2 | > Cute JSON's in React
3 |
4 |
5 |

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 | }
--------------------------------------------------------------------------------