Created
February 23, 2021 06:58
-
-
Save sangramthecoder/8648e7ff28f28f6a6ee9d0fe44a79339 to your computer and use it in GitHub Desktop.
Create React Library with Simple zsh script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/zsh | |
function jsonValue() { | |
KEY=$1 | |
num=$2 | |
awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'$KEY'\042/){print $(i+1)}}}' | tr -d '"' | sed -n ${num}p | |
} | |
echo "Creating directory " $1 | |
mkdir $1 | |
cd $1 | |
echo "Initiate NPM Repository"; | |
npm init | |
mkdir example example/public example/src && touch example/.gitignore example/package.json example/README.md example/public/index.html example/public/manifest.json example/public/robots.txt example/src/App.js example/src/index.js example/src/App.test.js example/src/reportWebVitals.js example/src/setupTests.js | |
mkdir src && touch src/index.tsx src/index.test.tsx src/$2.tsx | |
touch README.md rollup.config.js tsconfig.json .npmignore .babelrc .gitignore .eslintrc .eslintignore .editorconfig | |
echo "Creating .editorconfig content"; | |
cat > .editorconfig <<EOF | |
root = true | |
[*] | |
charset = utf-8 | |
indent_style = space | |
indent_size = 2 | |
end_of_line = lf | |
insert_final_newline = true | |
trim_trailing_whitespace = true% | |
EOF | |
echo "Creating .eslintrc content" | |
cat > .eslintrc <<EOF | |
{ | |
"parser": "@typescript-eslint/parser", | |
"extends": [ | |
"standard", | |
"standard-react", | |
"plugin:prettier/recommended", | |
"prettier/standard", | |
"prettier/react", | |
"plugin:@typescript-eslint/eslint-recommended" | |
], | |
"env": { | |
"node": true | |
}, | |
"parserOptions": { | |
"ecmaVersion": 2020, | |
"ecmaFeatures": { | |
"legacyDecorators": true, | |
"jsx": true | |
} | |
}, | |
"settings": { | |
"react": { | |
"version": "17" | |
} | |
}, | |
"rules": { | |
"space-before-function-paren": 0, | |
"react/prop-types": 0, | |
"react/jsx-handler-names": 0, | |
"react/jsx-fragments": 0, | |
"react/no-unused-prop-types": 0, | |
"import/export": 0 | |
} | |
} | |
EOF | |
echo "Creating .eslintignore content" | |
cat > .eslintignore <<EOF | |
dist/ | |
node_modules/ | |
.snapshots/ | |
*.min.js | |
EOF | |
echo "Creating .gitignore content" | |
cat > .gitignore <<EOF | |
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | |
# dependencies | |
node_modules | |
/.pnp | |
.pnp.js | |
# testing | |
coverage | |
/coverage | |
# builds/production | |
build | |
dist | |
.rpt2_cache | |
# misc | |
.DS_Store | |
.env | |
.env.local | |
.env.development.local | |
.env.test.local | |
.env.production.local | |
npm-debug.log* | |
yarn-debug.log* | |
yarn-error.log* | |
EOF | |
echo "Creating .tsconfig.json content" | |
cat > tsconfig.json <<EOF | |
{ | |
"compilerOptions": { | |
"outDir": "dist", | |
"module": "esnext", | |
"target": "es5", | |
"lib": ["es6","dom","es2016","es2017"], | |
"sourceMap": true, | |
"allowJs": true, | |
"jsx": "react-jsx", | |
"declaration":true, | |
"moduleResolution": "node", | |
"forceConsistentCasingInFileNames": true, | |
"skipLibCheck": true, | |
"esModuleInterop": true, | |
"allowSyntheticDefaultImports": true, | |
"strict": true, | |
"noImplicitReturns": true, | |
"noImplicitThis": true, | |
"noImplicitAny": true, | |
"strictNullChecks": true, | |
"noFallthroughCasesInSwitch": true, | |
"resolveJsonModule": true, | |
"isolatedModules": true, | |
"noEmit": true, | |
"noUnusedLocals": true, | |
"noUnusedParameters": true | |
}, | |
"include": ["src"], | |
"exclude": ["node_modules", "dist", "example", "rollup.config.js"] | |
} | |
EOF | |
echo "Creating .babelrc content" | |
cat > .babelrc <<EOF | |
{ | |
"plugins": [], | |
"presets": [ | |
"@babel/preset-react", | |
[ | |
"@babel/preset-env", | |
{ | |
"targets": { | |
"browsers": [ | |
"> 1%", | |
"Chrome >= 48", | |
"Firefox >= 44", | |
"Firefox ESR", | |
"Safari >= 9.1", | |
"Edge >= 12", | |
"ios_saf >= 9.3", | |
"ie 11" | |
] | |
}, | |
"modules": false, | |
"forceAllTransforms": true | |
} | |
] | |
], | |
"env": { | |
"test": { | |
"presets": [ | |
[ | |
"@babel/preset-env", | |
{ | |
"targets": { | |
"node": "current" | |
} | |
} | |
] | |
], | |
"plugins": [ | |
[ | |
"babel-plugin-styled-components", | |
{ | |
"minify": false | |
} | |
] | |
] | |
} | |
} | |
} | |
EOF | |
echo "Creating rollup config js content" | |
cat > rollup.config.js << 'EOF' | |
import babel from '@rollup/plugin-babel'; | |
import commonjs from '@rollup/plugin-commonjs'; | |
import resolve from '@rollup/plugin-node-resolve'; | |
import external from 'rollup-plugin-peer-deps-external'; | |
import { terser } from 'rollup-plugin-terser'; | |
import { uglify } from 'rollup-plugin-uglify'; | |
import typescript from 'rollup-plugin-typescript2'; | |
import pkg from './package.json'; | |
const input = "src/index.tsx" | |
export default [ | |
{ | |
input: input, | |
output: { | |
name: pkg.name, | |
file: `dist/${pkg.name}.dev.js`, | |
format: "cjs", | |
exports: "named", | |
}, | |
plugins: [ | |
resolve({ | |
browser: true, | |
preferBuiltins: true, | |
extensions: [".mjs", ".js", ".jsx",".tsx"], | |
}), | |
external(), | |
babel({ | |
exclude: "node_modules/**" | |
}), | |
commonjs({ | |
include: "node_modules/**", | |
}), | |
typescript({ | |
typescript: require("typescript"), | |
}) | |
], | |
external: ["react", "react-dom"] | |
}, | |
{ | |
input: input, | |
output: { | |
name:pkg.name, | |
file: pkg.main, | |
format: "cjs", | |
exports: "named", | |
}, | |
plugins: [ | |
resolve({ | |
browser: true, | |
preferBuiltins: true, | |
extensions: [".mjs", ".js", ".jsx",".tsx"], | |
}), | |
external(), | |
babel({ | |
exclude: "node_modules/**" | |
}), | |
commonjs({ | |
include: "node_modules/**", | |
}), | |
uglify(), | |
typescript({ | |
typescript: require("typescript"), | |
}) | |
], | |
external: ["react", "react-dom"] | |
}, | |
{ | |
input: input, | |
output: { | |
name:pkg.name, | |
file: pkg.module, | |
format: "es", | |
exports: "named", | |
}, | |
plugins: [ | |
resolve({ | |
browser: true, | |
preferBuiltins: true, | |
extensions: [".mjs", ".js", ".jsx",".tsx"], | |
}), | |
external(), | |
babel({ | |
exclude: "node_modules/**" | |
}), | |
commonjs({ | |
include: "node_modules/**", | |
}), | |
terser(), | |
typescript({ | |
typescript: require("typescript"), | |
}) | |
], | |
external: ["react", "react-dom"] | |
}, | |
{ | |
input: input, | |
output: { | |
name: pkg.name, | |
file: `dist/${pkg.name}.umd.js`, | |
format: "umd", | |
globals: { | |
react: "React", | |
}, | |
exports: "named", | |
}, | |
plugins: [ | |
resolve({ | |
browser: true, | |
preferBuiltins: true, | |
extensions: [".mjs", ".js", ".jsx",".tsx"], | |
}), | |
external(), | |
babel({ | |
exclude: "node_modules/**" | |
}), | |
commonjs({ | |
include: "node_modules/**", | |
}), | |
uglify({}), | |
typescript({ | |
typescript: require("typescript"), | |
}) | |
], | |
external: ["react", "react-dom"] | |
} | |
] | |
EOF | |
echo "Going to install npm packages, this will take a while to complete" | |
npm i -D react react-dom react-scripts web-vitals typescript @types/react @types/react-dom @types/jest @types/node rollup rollup-plugin-typescript2 @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-peer-deps-external rollup-plugin-terser rollup-plugin-uglify @babel/core @babel/runtime rimraf npm-run-all cross-env @testing-library/jest-dom @testing-library/react @testing-library/user-event | |
mainJsonPackage="package.json" | |
node > new_${mainJsonPackage} <<EOF | |
//Read data | |
var data = require("./${mainJsonPackage}"); | |
//Manipulate data | |
data.main = "dist/index.cjs.js"; | |
data.module = "dist/index.es.js"; | |
data.browser = "dist/index.cjs.js"; | |
data.types = "dist/index.d.ts"; | |
data.scripts={ | |
"prebuild": "rimraf dist", | |
"prepublishOnly": "npm run build", | |
"build": "rollup -c", | |
"watch": "rollup -c --watch", | |
"test": "run-s test:unit test:build", | |
"test:build": "run-s build", | |
"test:unit": "cross-env CI=1 react-scripts test --env=jsdom", | |
"test:watch": "react-scripts test --env=jsdom" | |
} | |
data.peerDependencies = { | |
"react": ">=16.8.0", | |
"react-dom": ">=16.8.0" | |
}; | |
data.files = ["dist"]; | |
//Output data | |
console.log(JSON.stringify(data,null,2)); | |
EOF | |
cp new_package.json package.json | |
rm new_package.json | |
packageName=`cat package.json | jsonValue name | xargs`; | |
echo "new package Name " $packageName; | |
cat > src/index.tsx << EOF | |
import $2 from "./$2"; | |
export default $2; | |
EOF | |
cat > src/$2.tsx << EOF | |
const $2 = () => { | |
return ( | |
<div> | |
Your $2 Component | |
</div> | |
) | |
} | |
export default $2; | |
EOF | |
cat > src/index.test.tsx << EOF | |
import $2 from '.' | |
describe("$2", () => { | |
it('is truthy', () => { | |
expect($2).toBeTruthy() | |
}) | |
}); | |
EOF | |
cp .gitignore example/.gitignore | |
cat > example/package.json <<EOF | |
{ | |
"name": "example", | |
"version": "0.1.0", | |
"private": false, | |
"dependencies": { | |
"@testing-library/jest-dom": "file:../node_modules/@testing-library/jest-dom", | |
"@testing-library/react": "file:../node_modules/@testing-library/react", | |
"@testing-library/user-event": "file:../node_modules/@testing-library/user-event", | |
"react": "file:../node_modules/react", | |
"react-dom": "file:../node_modules/react-dom", | |
"react-scripts": "file:../node_modules/react-scripts", | |
"web-vitals": "file:../node_modules/web-vitals", | |
"$packageName": "file:.." | |
}, | |
"scripts": { | |
"start": "react-scripts start", | |
"build": "react-scripts build", | |
"test": "react-scripts test", | |
"eject": "react-scripts eject" | |
}, | |
"eslintConfig": { | |
"extends": [ | |
"react-app", | |
"react-app/jest" | |
] | |
}, | |
"browserslist": { | |
"production": [ | |
">0.2%", | |
"not dead", | |
"not op_mini all" | |
], | |
"development": [ | |
"last 1 chrome version", | |
"last 1 firefox version", | |
"last 1 safari version" | |
] | |
} | |
} | |
EOF | |
cat > example/public/index.html << EOF | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<meta name="theme-color" content="#000000" /> | |
<meta | |
name="description" | |
content="$2 exmaple website" | |
/> | |
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> | |
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> | |
<title>Example $2</title> | |
</head> | |
<body> | |
<noscript>You need to enable JavaScript to run this app.</noscript> | |
<div id="root"></div> | |
</body> | |
</html> | |
EOF | |
curl https://static.thenounproject.com/png/24031-200.png > example/public/favicon.ico | |
curl https://static.thenounproject.com/png/24031-200.png > example/public/logo192.png | |
cat > example/public/manifest.json << EOF | |
{ | |
"short_name": "example", | |
"name": "$2 example app", | |
"icons": [ | |
{ | |
"src": "favicon.ico", | |
"sizes": "64x64 32x32 24x24 16x16", | |
"type": "image/x-icon" | |
}, | |
{ | |
"src": "logo192.png", | |
"type": "image/png", | |
"sizes": "192x192" | |
} | |
], | |
"start_url": ".", | |
"display": "standalone", | |
"theme_color": "#000000", | |
"background_color": "#ffffff" | |
} | |
EOF | |
cat > example/public/robots.txt << EOF | |
# https://www.robotstxt.org/robotstxt.html | |
User-agent: * | |
Disallow: | |
EOF | |
cat > example/src/App.js << EOF | |
import $2 from '$packageName'; | |
import { useEffect, useState } from "react"; | |
function App() { | |
const [count,setCount] = useState(0); | |
useEffect(() => { | |
setCount(prevCount => prevCount + 1); | |
},[]); | |
return ( | |
<div className="App"> | |
<$2 text={'Hola la le lo '} number={count}/> | |
<a | |
className="App-link" | |
href="https://reactjs.org" | |
target="_blank" | |
rel="noopener noreferrer" | |
>Learn React | |
</a> | |
</div> | |
); | |
} | |
export default App; | |
EOF | |
cat > example/src/index.js << EOF | |
import React from "react"; | |
import ReactDOM from "react-dom"; | |
import App from "./App"; | |
import reportWebVitals from "./reportWebVitals"; | |
ReactDOM.render( | |
<React.StrictMode> | |
<App /> | |
</React.StrictMode>, | |
document.getElementById("root") | |
); | |
// If you want to start measuring performance in your app, pass a function | |
// to log results (for example: reportWebVitals(console.log)) | |
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals | |
reportWebVitals(); | |
EOF | |
cat > example/src/App.test.js << EOF | |
import { render, screen } from "@testing-library/react"; | |
import App from "./App"; | |
test("renders learn react link", () => { | |
render(<App />); | |
const linkElement = screen.getByText(/learn react/i); | |
expect(linkElement).toBeInTheDocument(); | |
}); | |
EOF | |
cat > example/src/reportWebVitals.js << EOF | |
const reportWebVitals = onPerfEntry => { | |
if (onPerfEntry && onPerfEntry instanceof Function) { | |
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { | |
getCLS(onPerfEntry); | |
getFID(onPerfEntry); | |
getFCP(onPerfEntry); | |
getLCP(onPerfEntry); | |
getTTFB(onPerfEntry); | |
}); | |
} | |
}; | |
export default reportWebVitals; | |
EOF | |
cat > example/src/setupTests.js << EOF | |
// jest-dom adds custom jest matchers for asserting on DOM nodes. | |
// allows you to do things like: | |
// expect(element).toHaveTextContent(/react/i) | |
// learn more: https://github.com/testing-library/jest-dom | |
import "@testing-library/jest-dom"; | |
EOF | |
cd example | |
npm i |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Create React Library for npm packaging using zsh script.
Simple and robust. it accepts two parameters
Parameter 1: name of the library you want to create.
Parameter 2: name of the base component of your library.
Internally it uses rollup.js for bundling your package and uses typescript as a base for your library. It's important to have typescript because it helps others to know what api's/parameters are exposed for your react component.