├── .gitignore ├── LICENSE ├── README.md ├── example ├── .npmignore ├── index.html ├── index.tsx ├── package.json └── tsconfig.json ├── package-lock.json ├── package.json ├── src └── index.tsx ├── test ├── __snapshots__ │ └── render.test.tsx.snap ├── render.test.tsx └── utils │ └── test-components.tsx └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | .rts2_cache_cjs 6 | .rts2_cache_esm 7 | .rts2_cache_umd 8 | .rts2_cache_system 9 | dist 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Horus Lugo 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Component Pack · [![npm version](https://img.shields.io/npm/v/react-component-pack.svg)](https://www.npmjs.com/package/react-component-pack) [![license](https://img.shields.io/npm/l/react-component-pack.svg?color=blue)](./LICENSE) 2 | 3 | Say goodbye to provider hell with react-component-pack, a utility that allows you to group multiple components into a single one 4 | 5 | ```bash 6 | npm install react-component-pack 7 | ``` 8 | 9 | ## Usage 10 | 11 | With `react-component-pack` you can go from this: 12 | 13 | ```jsx 14 | function App() { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | } 31 | ``` 32 | 33 | To this: 34 | 35 | ```jsx 36 | import { createPack } from 'react-component-pack'; 37 | 38 | const ProviderPack = createPack( 39 | AuthProvider, 40 | DataProvider, 41 | AnotherDataProvider, 42 | WtfProvider, 43 | ThisIsGettingReallyBigProvider, 44 | OhMyGodTheresMoreProvider 45 | ); 46 | 47 | function App() { 48 | return ( 49 | 50 | 51 | 52 | ); 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /example/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | dist -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Playground 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/index.tsx: -------------------------------------------------------------------------------- 1 | import 'react-app-polyfill/ie11'; 2 | import * as React from 'react'; 3 | import * as ReactDOM from 'react-dom'; 4 | import { createPack } from '../.'; 5 | 6 | const ExamplePack = createPack( 7 | "div", 8 | "h1" 9 | ) 10 | 11 | const App = () => { 12 | return ( 13 |
14 | 15 | Div and H1 packed! 16 | 17 |
18 | ); 19 | }; 20 | 21 | ReactDOM.render(, document.getElementById('root')); 22 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "parcel index.html", 8 | "build": "parcel build index.html" 9 | }, 10 | "dependencies": { 11 | "react-app-polyfill": "^1.0.0" 12 | }, 13 | "alias": { 14 | "react": "../node_modules/react", 15 | "react-dom": "../node_modules/react-dom/profiling", 16 | "scheduler/tracing": "../node_modules/scheduler/tracing-profiling" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^16.9.11", 20 | "@types/react-dom": "^16.8.4", 21 | "parcel": "^1.12.3", 22 | "typescript": "^3.4.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": false, 4 | "target": "es5", 5 | "module": "commonjs", 6 | "jsx": "react", 7 | "moduleResolution": "node", 8 | "noImplicitAny": false, 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "removeComments": true, 12 | "strictNullChecks": true, 13 | "preserveConstEnums": true, 14 | "sourceMap": true, 15 | "lib": ["es2015", "es2016", "dom"], 16 | "baseUrl": ".", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-component-pack", 3 | "version": "1.0.1", 4 | "license": "MIT", 5 | "author": "Horus Lugo ", 6 | "description": "Say goodbye to provider hell with react-component-pack, a utility that allows you to group multiple components into a single one", 7 | "keywords": [ 8 | "react", 9 | "pack", 10 | "context hell", 11 | "component pack", 12 | "tree", 13 | "provider hell" 14 | ], 15 | "homepage": "https://github.com/HorusGoul/react-component-pack#readme", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/HorusGoul/react-component-pack.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/HorusGoul/react-component-pack/issues" 22 | }, 23 | "main": "dist/index.js", 24 | "module": "dist/react-component-pack.esm.js", 25 | "typings": "dist/index.d.ts", 26 | "files": [ 27 | "dist" 28 | ], 29 | "scripts": { 30 | "start": "tsdx watch", 31 | "build": "tsdx build", 32 | "test": "tsdx test --env=jsdom", 33 | "lint": "tsdx lint", 34 | "release": "npm run test && npm run build && npm publish" 35 | }, 36 | "peerDependencies": { 37 | "react": ">=16" 38 | }, 39 | "husky": { 40 | "hooks": { 41 | "pre-commit": "tsdx lint" 42 | } 43 | }, 44 | "prettier": { 45 | "printWidth": 80, 46 | "semi": true, 47 | "singleQuote": true, 48 | "trailingComma": "es5" 49 | }, 50 | "devDependencies": { 51 | "@types/jest": "^24.0.23", 52 | "@types/react": "^16.9.11", 53 | "@types/react-dom": "^16.9.4", 54 | "@types/react-test-renderer": "^16.9.1", 55 | "husky": "^3.0.9", 56 | "react": "^16.12.0", 57 | "react-dom": "^16.12.0", 58 | "react-test-renderer": "^16.12.0", 59 | "tsdx": "^0.11.0", 60 | "tslib": "^1.10.0", 61 | "typescript": "^3.7.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export interface WithChildrenProps { 4 | children: React.ReactNode; 5 | } 6 | 7 | export type PackComponentType< 8 | T extends WithChildrenProps = WithChildrenProps 9 | > = React.ElementType; 10 | 11 | export function pack( 12 | children: React.ReactNode = null, 13 | ...components: PackComponentType[] 14 | ) { 15 | if (!components.length) { 16 | return children as JSX.Element; 17 | } 18 | 19 | const [Component, ...rest] = components; 20 | 21 | return {pack(children, ...rest)}; 22 | } 23 | 24 | export function createPack(...components: PackComponentType[]) { 25 | return function PackComponent({ children }: WithChildrenProps) { 26 | return pack(children, ...components); 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /test/__snapshots__/render.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`it renders without crashing a pack of one component 1`] = ` 4 |
5 | Test 6 |
7 | `; 8 | 9 | exports[`it renders without crashing a pack of three components 1`] = ` 10 |
11 |
12 |
13 | Test 14 |
15 |
16 |
17 | `; 18 | -------------------------------------------------------------------------------- /test/render.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createPack } from '../src'; 3 | import { WithChildrenComponent } from './utils/test-components'; 4 | import * as renderer from 'react-test-renderer'; 5 | 6 | describe('it', () => { 7 | it('renders without crashing a pack of one component', () => { 8 | const Pack = createPack(WithChildrenComponent); 9 | 10 | const tree = renderer.create(Test).toJSON(); 11 | 12 | expect(tree).toMatchSnapshot(); 13 | }); 14 | 15 | it('renders without crashing a pack of three components', () => { 16 | const Pack = createPack( 17 | WithChildrenComponent, 18 | WithChildrenComponent, 19 | WithChildrenComponent 20 | ); 21 | 22 | const tree = renderer.create(Test).toJSON(); 23 | 24 | expect(tree).toMatchSnapshot(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/utils/test-components.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export function WithChildrenComponent({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) { 8 | return
{children}
; 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types", "test"], 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "rootDir": "./", 11 | "strict": true, 12 | "noImplicitAny": true, 13 | "strictNullChecks": true, 14 | "strictFunctionTypes": true, 15 | "strictPropertyInitialization": true, 16 | "noImplicitThis": true, 17 | "alwaysStrict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noImplicitReturns": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "moduleResolution": "node", 23 | "baseUrl": "./", 24 | "paths": { 25 | "*": ["src/*", "node_modules/*"] 26 | }, 27 | "jsx": "react", 28 | "esModuleInterop": true 29 | } 30 | } 31 | --------------------------------------------------------------------------------