├── .gitignore ├── cover.png ├── icon16.png ├── icon32.png ├── icon48.png ├── icon128.png ├── tsconfig.json ├── pnpm-lock.yaml ├── package.json ├── .prettierrc.js ├── manifest.json ├── README.md ├── .pnpm-debug.log ├── src └── script.ts └── dist └── script.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmirhBeigi/package-installer-commands/HEAD/cover.png -------------------------------------------------------------------------------- /icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmirhBeigi/package-installer-commands/HEAD/icon16.png -------------------------------------------------------------------------------- /icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmirhBeigi/package-installer-commands/HEAD/icon32.png -------------------------------------------------------------------------------- /icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmirhBeigi/package-installer-commands/HEAD/icon48.png -------------------------------------------------------------------------------- /icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmirhBeigi/package-installer-commands/HEAD/icon128.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "outDir": "dist", 6 | "esModuleInterop": true, 7 | "removeComments": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true 11 | }, 12 | "include": ["./"] 13 | } 14 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | typescript: ^4.6.4 5 | 6 | devDependencies: 7 | typescript: 4.6.4 8 | 9 | packages: 10 | 11 | /typescript/4.6.4: 12 | resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} 13 | engines: {node: '>=4.2.0'} 14 | hasBin: true 15 | dev: true 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-extension", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "tsc --watch", 8 | "build": "tsc" 9 | }, 10 | "keywords": [ 11 | "npm", 12 | "extension", 13 | "typescript", 14 | "chrome" 15 | ], 16 | "author": "Amirhbeigi", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "typescript": "^4.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSpacing: true, 4 | endOfLine: 'lf', 5 | htmlWhitespaceSensitivity: 'css', 6 | insertPragma: false, 7 | jsxBracketSameLine: false, 8 | jsxSingleQuote: false, 9 | printWidth: 100, 10 | proseWrap: 'always', 11 | quoteProps: 'consistent', 12 | requirePragma: false, 13 | semi: true, 14 | singleQuote: true, 15 | tabWidth: 4, 16 | trailingComma: 'none', 17 | useTabs: false 18 | }; 19 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Package installer commands", 3 | "icons": { 4 | "16": "icon16.png", 5 | "32": "icon32.png", 6 | "48": "icon48.png", 7 | "128": "icon128.png" 8 | }, 9 | "description": "From now on, it can be installed with all package managers", 10 | "version": "1.0", 11 | "manifest_version": 3, 12 | "content_scripts": [ 13 | { 14 | "matches": ["*://www.npmjs.com/package/*"], 15 | "js": ["dist/script.js"] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Package installer commands 2 | 3 | From now on, it can be installed with all package managers in npmjs.com. 4 | ![package installer commands](https://github.com/AmirhBeigi/package-installer-commands/blob/main/cover.png?raw=true) 5 | 6 | ## Run Locally 7 | 8 | 1. Clone the project 9 | 10 | ```bash 11 | git clone https://github.com/AmirhBeigi/package-installer-commands 12 | ``` 13 | 14 | 2. Open Google Chrome. 15 | 16 | 3. Open the Extension Management page by navigating to `chrome://extensions` 17 | 18 | 4. Enable Developer Mode by clicking the toggle switch next to Developer mode. 19 | 20 | 5. Click the Load unpacked button and select the extension directory. 21 | 22 | ## License 23 | 24 | [MIT](https://choosealicense.com/licenses/mit/) 25 | 26 | ## Contributing 27 | 28 | Contributions are welcome! ❤️ 29 | -------------------------------------------------------------------------------- /.pnpm-debug.log: -------------------------------------------------------------------------------- 1 | { 2 | "0 debug pnpm:scope": { 3 | "selected": 1 4 | }, 5 | "1 error pnpm": { 6 | "errno": 126, 7 | "code": "ELIFECYCLE", 8 | "pkgid": "npm-extension@1.0.0", 9 | "stage": "dev", 10 | "script": "tsc --watch", 11 | "pkgname": "npm-extension", 12 | "err": { 13 | "name": "pnpm", 14 | "message": "npm-extension@1.0.0 dev: `tsc --watch`\nExit status 126", 15 | "code": "ELIFECYCLE", 16 | "stack": "pnpm: npm-extension@1.0.0 dev: `tsc --watch`\nExit status 126\n at EventEmitter. (C:\\Users\\AmirhBeigi\\AppData\\Local\\Volta\\tools\\image\\packages\\pnpm\\node_modules\\pnpm\\dist\\pnpm.cjs:106732:20)\n at EventEmitter.emit (node:events:390:28)\n at ChildProcess. (C:\\Users\\AmirhBeigi\\AppData\\Local\\Volta\\tools\\image\\packages\\pnpm\\node_modules\\pnpm\\dist\\pnpm.cjs:93301:18)\n at ChildProcess.emit (node:events:390:28)\n at maybeClose (node:internal/child_process:1064:16)\n at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5)" 17 | } 18 | }, 19 | "2 warn pnpm:global": " Local package.json exists, but node_modules missing, did you mean to install?" 20 | } -------------------------------------------------------------------------------- /src/script.ts: -------------------------------------------------------------------------------- 1 | const getCss = () => ` 2 | .switch { 3 | position: relative; 4 | display: inline-block; 5 | width: 40px; 6 | height: 25px; 7 | } 8 | 9 | 10 | .toast { 11 | background-color: #000; 12 | color: #fff; 13 | border-radius: 5px; 14 | padding: 1rem 2rem; 15 | margin: 0.5rem; 16 | cursor: pointer; 17 | } 18 | 19 | #toasts { 20 | position: fixed; 21 | bottom: 10px; 22 | right: 10px; 23 | display: flex; 24 | flex-direction: column; 25 | align-items: flex-end; 26 | z-index: 9999; 27 | } 28 | 29 | .switch input { 30 | opacity: 0; 31 | width: 0; 32 | height: 0; 33 | } 34 | .slider { 35 | position: absolute; 36 | cursor: pointer; 37 | top: 0; 38 | left: 0; 39 | right: 0; 40 | bottom: 0; 41 | background-color: #ccc; 42 | -webkit-transition: .4s; 43 | transition: .4s; 44 | border-radius: 34px; 45 | } 46 | 47 | .slider:before { 48 | position: absolute; 49 | content: ""; 50 | height: 17px; 51 | width: 17px; 52 | left: 4px; 53 | bottom: 4px; 54 | background-color: white; 55 | -webkit-transition: .4s; 56 | transition: .4s; 57 | border-radius: 50%; 58 | } 59 | 60 | input:checked + .slider { 61 | background-color: #000; 62 | } 63 | 64 | input:focus + .slider { 65 | box-shadow: 0 0 1px #000; 66 | } 67 | .pic-switch-toggles { 68 | gap: 1rem; 69 | } 70 | .st_toggle { 71 | gap: 0.5rem; 72 | } 73 | 74 | input:checked + .slider:before { 75 | -webkit-transform: translateX(15px); 76 | -ms-transform: translateX(15px); 77 | transform: translateX(15px); 78 | }`; 79 | 80 | document.head.insertAdjacentHTML('beforeend', ``); 81 | 82 | let togglesState: any = { 83 | isDev: false, 84 | isOptional: false 85 | }; 86 | 87 | const packageManagers = { 88 | installers: { 89 | pnpm: 'i', 90 | yarn: 'add', 91 | npm: 'i' 92 | }, 93 | dev: { 94 | pnpm: '-D', 95 | yarn: '-D', 96 | npm: '-D' 97 | }, 98 | optional: { 99 | npm: '-O', 100 | yarn: '-O', 101 | pnpm: '-O' 102 | } 103 | }; 104 | 105 | type Manager = keyof typeof packageManagers.installers; 106 | 107 | const packageManagerNameList = Object.keys(packageManagers.installers) as Manager[]; 108 | 109 | const sidebarEl = document.querySelector( 110 | '#top > div.fdbf4038.w-third-l.mt3.w-100.ph3.ph4-m.pv3.pv0-l' 111 | ); 112 | sidebarEl?.insertAdjacentHTML('afterbegin', `
`); 113 | const packageBoxEl = document.getElementById('package-box'); 114 | const packageName = location.pathname.split('/').splice(2).join('/'); 115 | const toastContainer = document.createElement('div'); 116 | toastContainer.id = 'toasts'; 117 | document.querySelector('#app > div')?.insertAdjacentElement('afterbegin', toastContainer); 118 | 119 | const cleanUp = () => { 120 | document 121 | .querySelector( 122 | '#top > div.fdbf4038.w-third-l.mt3.w-100.ph3.ph4-m.pv3.pv0-l > div.d767adf4.lh-copy.truncate.ph0.mb3.black-80.b5be2af6.f6.flex.flex-row' 123 | ) 124 | ?.remove(); 125 | document 126 | .querySelector('#top > div.fdbf4038.w-third-l.mt3.w-100.ph3.ph4-m.pv3.pv0-l > h3') 127 | ?.remove(); 128 | }; 129 | 130 | cleanUp(); 131 | function generateCommand( 132 | manager: Manager, 133 | options: { isDev: boolean; isOptional: boolean } = { 134 | isDev: togglesState.isDev, 135 | isOptional: togglesState.isOptional 136 | } 137 | ) { 138 | let commands: string[] = [manager, packageManagers.installers[manager]]; 139 | if (options.isDev) commands.push(packageManagers.dev[manager]); 140 | if (options.isOptional) commands.push(packageManagers.optional[manager] || ''); 141 | commands.push(packageName); 142 | return commands.join(' '); 143 | } 144 | const copyToClipboard = (str: string) => { 145 | if (navigator && navigator.clipboard && navigator.clipboard.writeText) 146 | return navigator.clipboard.writeText(str); 147 | const el = document.createElement('textarea'); 148 | el.value = str; 149 | el.setAttribute('readonly', ''); 150 | el.style.position = 'absolute'; 151 | el.style.left = '-9999px'; 152 | document.body.appendChild(el); 153 | el.select(); 154 | document.execCommand('copy'); 155 | document.body.removeChild(el); 156 | }; 157 | 158 | const toast = ({ text }: { text: string }) => { 159 | const toast = document.createElement('div'); 160 | toast.classList.add('toast'); 161 | toast.innerHTML = `
${text}
`; 162 | toastContainer.appendChild(toast); 163 | toast.addEventListener('click', () => { 164 | toast.remove(); 165 | }); 166 | setTimeout(() => { 167 | toast.remove(); 168 | }, 2000); 169 | }; 170 | 171 | const isDevSwitchButton = () => ``; 175 | const isOptionalSwitchButton = () => ``; 179 | 180 | const header = () => `
181 |

Install

182 |
183 | is Dev${isDevSwitchButton()} 184 | is Optional${isOptionalSwitchButton()} 185 |
186 |
`; 187 | 188 | const renderInstallPackageBox = (manager: Manager) => { 189 | const command = generateCommand(manager); 190 | 191 | return `
192 | 193 |

194 | ${command} 195 | 196 |

197 |
`; 198 | }; 199 | 200 | const installPackageBoxes = () => { 201 | packageManagerNameList.map((manager: Manager) => { 202 | document.getElementById(`${manager}-box`)?.remove(); 203 | 204 | packageBoxEl?.insertAdjacentHTML('afterbegin', renderInstallPackageBox(manager)); 205 | const command = generateCommand(manager); 206 | return document.getElementById(`${manager}-box`)?.addEventListener('click', () => 207 | copyToClipboard(command)?.then(() => 208 | toast({ 209 | text: `Copied '${command}' to clipboard` 210 | }) 211 | ) 212 | ); 213 | }); 214 | }; 215 | 216 | installPackageBoxes(); 217 | sidebarEl?.insertAdjacentHTML('afterbegin', header()); 218 | 219 | type Toggle = keyof typeof toggles; 220 | const toggles: any = { 221 | isDev: 'devDependenciesToggleButton', 222 | isOptional: 'optionalToggleButton' 223 | }; 224 | Object.entries(toggles).forEach(([toggle, element]: any) => { 225 | document.getElementById(element)?.addEventListener('change', (e: any) => { 226 | togglesState[toggle] = e.target.checked; 227 | installPackageBoxes(); 228 | }); 229 | }); 230 | -------------------------------------------------------------------------------- /dist/script.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var _a; 3 | const getCss = () => ` 4 | .switch { 5 | position: relative; 6 | display: inline-block; 7 | width: 40px; 8 | height: 25px; 9 | } 10 | 11 | 12 | .toast { 13 | background-color: #000; 14 | color: #fff; 15 | border-radius: 5px; 16 | padding: 1rem 2rem; 17 | margin: 0.5rem; 18 | cursor: pointer; 19 | } 20 | 21 | #toasts { 22 | position: fixed; 23 | bottom: 10px; 24 | right: 10px; 25 | display: flex; 26 | flex-direction: column; 27 | align-items: flex-end; 28 | z-index: 9999; 29 | } 30 | 31 | .switch input { 32 | opacity: 0; 33 | width: 0; 34 | height: 0; 35 | } 36 | .slider { 37 | position: absolute; 38 | cursor: pointer; 39 | top: 0; 40 | left: 0; 41 | right: 0; 42 | bottom: 0; 43 | background-color: #ccc; 44 | -webkit-transition: .4s; 45 | transition: .4s; 46 | border-radius: 34px; 47 | } 48 | 49 | .slider:before { 50 | position: absolute; 51 | content: ""; 52 | height: 17px; 53 | width: 17px; 54 | left: 4px; 55 | bottom: 4px; 56 | background-color: white; 57 | -webkit-transition: .4s; 58 | transition: .4s; 59 | border-radius: 50%; 60 | } 61 | 62 | input:checked + .slider { 63 | background-color: #000; 64 | } 65 | 66 | input:focus + .slider { 67 | box-shadow: 0 0 1px #000; 68 | } 69 | .pic-switch-toggles { 70 | gap: 1rem; 71 | } 72 | .st_toggle { 73 | gap: 0.5rem; 74 | } 75 | 76 | input:checked + .slider:before { 77 | -webkit-transform: translateX(15px); 78 | -ms-transform: translateX(15px); 79 | transform: translateX(15px); 80 | }`; 81 | document.head.insertAdjacentHTML('beforeend', ``); 82 | let togglesState = { 83 | isDev: false, 84 | isOptional: false 85 | }; 86 | const packageManagers = { 87 | installers: { 88 | pnpm: 'i', 89 | yarn: 'add', 90 | npm: 'i' 91 | }, 92 | dev: { 93 | pnpm: '-D', 94 | yarn: '-D', 95 | npm: '-D' 96 | }, 97 | optional: { 98 | npm: '-O', 99 | yarn: '-O', 100 | pnpm: '-O' 101 | } 102 | }; 103 | const packageManagerNameList = Object.keys(packageManagers.installers); 104 | const sidebarEl = document.querySelector('#top > div.fdbf4038.w-third-l.mt3.w-100.ph3.ph4-m.pv3.pv0-l'); 105 | sidebarEl === null || sidebarEl === void 0 ? void 0 : sidebarEl.insertAdjacentHTML('afterbegin', `
`); 106 | const packageBoxEl = document.getElementById('package-box'); 107 | const packageName = location.pathname.split('/').splice(2).join('/'); 108 | const toastContainer = document.createElement('div'); 109 | toastContainer.id = 'toasts'; 110 | (_a = document.querySelector('#app > div')) === null || _a === void 0 ? void 0 : _a.insertAdjacentElement('afterbegin', toastContainer); 111 | const cleanUp = () => { 112 | var _a, _b; 113 | (_a = document 114 | .querySelector('#top > div.fdbf4038.w-third-l.mt3.w-100.ph3.ph4-m.pv3.pv0-l > div.d767adf4.lh-copy.truncate.ph0.mb3.black-80.b5be2af6.f6.flex.flex-row')) === null || _a === void 0 ? void 0 : _a.remove(); 115 | (_b = document 116 | .querySelector('#top > div.fdbf4038.w-third-l.mt3.w-100.ph3.ph4-m.pv3.pv0-l > h3')) === null || _b === void 0 ? void 0 : _b.remove(); 117 | }; 118 | cleanUp(); 119 | function generateCommand(manager, options = { 120 | isDev: togglesState.isDev, 121 | isOptional: togglesState.isOptional 122 | }) { 123 | let commands = [manager, packageManagers.installers[manager]]; 124 | if (options.isDev) 125 | commands.push(packageManagers.dev[manager]); 126 | if (options.isOptional) 127 | commands.push(packageManagers.optional[manager] || ''); 128 | commands.push(packageName); 129 | return commands.join(' '); 130 | } 131 | const copyToClipboard = (str) => { 132 | if (navigator && navigator.clipboard && navigator.clipboard.writeText) 133 | return navigator.clipboard.writeText(str); 134 | const el = document.createElement('textarea'); 135 | el.value = str; 136 | el.setAttribute('readonly', ''); 137 | el.style.position = 'absolute'; 138 | el.style.left = '-9999px'; 139 | document.body.appendChild(el); 140 | el.select(); 141 | document.execCommand('copy'); 142 | document.body.removeChild(el); 143 | }; 144 | const toast = ({ text }) => { 145 | const toast = document.createElement('div'); 146 | toast.classList.add('toast'); 147 | toast.innerHTML = `
${text}
`; 148 | toastContainer.appendChild(toast); 149 | toast.addEventListener('click', () => { 150 | toast.remove(); 151 | }); 152 | setTimeout(() => { 153 | toast.remove(); 154 | }, 2000); 155 | }; 156 | const isDevSwitchButton = () => ``; 160 | const isOptionalSwitchButton = () => ``; 164 | const header = () => `
165 |

Install

166 |
167 | is Dev${isDevSwitchButton()} 168 | is Optional${isOptionalSwitchButton()} 169 |
170 |
`; 171 | const renderInstallPackageBox = (manager) => { 172 | const command = generateCommand(manager); 173 | return `
174 | 175 |

176 | ${command} 177 | 178 |

179 |
`; 180 | }; 181 | const installPackageBoxes = () => { 182 | packageManagerNameList.map((manager) => { 183 | var _a, _b; 184 | (_a = document.getElementById(`${manager}-box`)) === null || _a === void 0 ? void 0 : _a.remove(); 185 | packageBoxEl === null || packageBoxEl === void 0 ? void 0 : packageBoxEl.insertAdjacentHTML('afterbegin', renderInstallPackageBox(manager)); 186 | const command = generateCommand(manager); 187 | return (_b = document.getElementById(`${manager}-box`)) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => { 188 | var _a; 189 | return (_a = copyToClipboard(command)) === null || _a === void 0 ? void 0 : _a.then(() => toast({ 190 | text: `Copied '${command}' to clipboard` 191 | })); 192 | }); 193 | }); 194 | }; 195 | installPackageBoxes(); 196 | sidebarEl === null || sidebarEl === void 0 ? void 0 : sidebarEl.insertAdjacentHTML('afterbegin', header()); 197 | const toggles = { 198 | isDev: 'devDependenciesToggleButton', 199 | isOptional: 'optionalToggleButton' 200 | }; 201 | Object.entries(toggles).forEach(([toggle, element]) => { 202 | var _a; 203 | (_a = document.getElementById(element)) === null || _a === void 0 ? void 0 : _a.addEventListener('change', (e) => { 204 | togglesState[toggle] = e.target.checked; 205 | installPackageBoxes(); 206 | }); 207 | }); 208 | --------------------------------------------------------------------------------