├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── by-feature.js ├── by-provider.js ├── index.js ├── package.json ├── recipes ├── by-feature │ ├── debug-css.js │ ├── emails.js │ ├── embed.js │ ├── excerpt.js │ ├── favicon.js │ ├── full-screenshot.js │ ├── headings.js │ ├── healthcheck.js │ ├── html.js │ ├── images.js │ ├── json-ld.js │ ├── lighthouse.js │ ├── pdf.js │ ├── screenshot.js │ ├── technology-stack.js │ ├── text.js │ ├── uris.js │ └── youtube-dl.js └── by-provider │ ├── amazon.js │ ├── assc.js │ ├── balenciaga.js │ ├── betalist.js │ ├── canopy.js │ ├── codepen.js │ ├── fca.js │ ├── github.js │ ├── google.js │ ├── hacker-news.js │ ├── imdb.js │ ├── instagram.js │ ├── meetup.js │ ├── parcel.js │ ├── producthunt.js │ ├── reddit.js │ ├── ripndip.js │ ├── soundcloud.js │ ├── spotify.js │ ├── telegram.js │ ├── tiktok.js │ ├── twitter.js │ ├── wipoid.js │ ├── youtube.js │ └── zalando.js └── test ├── by-feature.js └── by-provider.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | max_line_length = 80 13 | indent_brace_style = 1TBS 14 | spaces_around_operators = true 15 | quote_type = auto 16 | 17 | [package.json] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | # Check for updates to GitHub Actions every weekday 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | - name: Setup Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: lts/* 21 | - name: Install 22 | run: npm install --no-package-lock 23 | - name: Test 24 | run: npm test 25 | - name: Report 26 | run: mkdir -p coverage && npx c8 report --reporter=text-lcov > coverage/lcov.info 27 | - name: Coverage 28 | uses: coverallsapp/github-action@main 29 | with: 30 | github-token: ${{ secrets.GITHUB_TOKEN }} 31 | - name: Release 32 | if: ${{ github.ref == 'refs/heads/master' && !contains(github.event.head_commit.message, 'release') && !contains(github.event.head_commit.message, 'docs') }} 33 | env: 34 | CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{ secrets.GH_TOKEN }} 35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 36 | run: | 37 | git config --global user.email ${{ secrets.GIT_EMAIL }} 38 | git config --global user.name ${{ secrets.GIT_USERNAME }} 39 | npm run release 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # npm 3 | ############################ 4 | node_modules 5 | npm-debug.log 6 | .node_history 7 | yarn.lock 8 | package-lock.json 9 | 10 | ############################ 11 | # tmp, editor & OS files 12 | ############################ 13 | .tmp 14 | *.swo 15 | *.swp 16 | *.swn 17 | *.swm 18 | .DS_Store 19 | *# 20 | *~ 21 | .idea 22 | *sublime* 23 | nbproject 24 | 25 | ############################ 26 | # Tests 27 | ############################ 28 | testApp 29 | coverage 30 | .nyc_output 31 | 32 | ############################ 33 | # Other 34 | ############################ 35 | .env 36 | .envrc 37 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-prefix=~ 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.8.1](https://github.com/microlinkhq/recipes/compare/v1.8.0...v1.8.1) (2023-09-06) 6 | 7 | ## [1.8.0](https://github.com/microlinkhq/recipes/compare/v1.7.0...v1.8.0) (2023-06-06) 8 | 9 | 10 | ### Features 11 | 12 | * add text recipe ([#18](https://github.com/microlinkhq/recipes/issues/18)) ([9293f68](https://github.com/microlinkhq/recipes/commit/9293f68cba4f8001d5bfa4b8ac5aeb13e097ed42)) 13 | 14 | ## [1.7.0](https://github.com/microlinkhq/recipes/compare/v1.6.2...v1.7.0) (2023-06-03) 15 | 16 | 17 | ### Features 18 | 19 | * add URIs recipe ([feec888](https://github.com/microlinkhq/recipes/commit/feec8880489823f565b90a96cdaef34762aa5555)) 20 | 21 | ### [1.6.2](https://github.com/microlinkhq/recipes/compare/v1.6.1...v1.6.2) (2023-04-04) 22 | 23 | ### [1.6.1](https://github.com/microlinkhq/recipes/compare/v1.6.0...v1.6.1) (2023-01-25) 24 | 25 | ## [1.6.0](https://github.com/microlinkhq/recipes/compare/v1.5.0...v1.6.0) (2022-08-25) 26 | 27 | 28 | ### Features 29 | 30 | * add youtube-dl recipe ([9e45c5b](https://github.com/microlinkhq/recipes/commit/9e45c5bf2aa18bb11ffc6eb39b83ae440d1552cb)) 31 | 32 | ## [1.5.0](https://github.com/microlinkhq/recipes/compare/v1.4.0...v1.5.0) (2022-07-06) 33 | 34 | 35 | ### Features 36 | 37 | * **provider:** add parcel ([4bcd866](https://github.com/microlinkhq/recipes/commit/4bcd86611455c197955c7705862df940ac573a50)) 38 | 39 | ## [1.4.0](https://github.com/microlinkhq/recipes/compare/v1.3.6...v1.4.0) (2022-02-22) 40 | 41 | 42 | ### Features 43 | 44 | * add `tiktok` recipe ([1506f96](https://github.com/microlinkhq/recipes/commit/1506f9630399e9c80ce3c59c8d976ca793870c16)) 45 | 46 | ### [1.3.6](https://github.com/microlinkhq/recipes/compare/v1.3.5...v1.3.6) (2022-02-06) 47 | 48 | ### [1.3.5](https://github.com/microlinkhq/recipes/compare/v1.3.4...v1.3.5) (2022-02-06) 49 | 50 | ### [1.3.4](https://github.com/microlinkhq/recipes/compare/v1.3.3...v1.3.4) (2022-01-24) 51 | 52 | ### [1.3.3](https://github.com/microlinkhq/recipes/compare/v1.3.2...v1.3.3) (2022-01-19) 53 | 54 | 55 | ### Bug Fixes 56 | 57 | * github recipe `stars` attribute ([2cdc547](https://github.com/microlinkhq/recipes/commit/2cdc5473c0909b7eb6d498dd9016f2b3e81399c7)) 58 | 59 | ### [1.3.2](https://github.com/microlinkhq/recipes/compare/v1.3.1...v1.3.2) (2022-01-08) 60 | 61 | 62 | ### Bug Fixes 63 | 64 | * wait twitter data fetch ([a2e6cf7](https://github.com/microlinkhq/recipes/commit/a2e6cf73520e78036258cb84868478fafeac05ff)) 65 | 66 | ### [1.3.1](https://github.com/microlinkhq/recipes/compare/v1.3.0...v1.3.1) (2021-12-20) 67 | 68 | ## [1.3.0](https://github.com/microlinkhq/recipes/compare/v1.2.8...v1.3.0) (2021-12-12) 69 | 70 | 71 | ### Features 72 | 73 | * add amazon recipe ([27cb618](https://github.com/microlinkhq/recipes/commit/27cb6184c92b6037169db6cc6f205ca9ceb15dc3)) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * linter for amazon recipe ([db6b055](https://github.com/microlinkhq/recipes/commit/db6b055238eaf23201ce9341da8ec90a9756f099)) 79 | 80 | ### [1.2.8](https://github.com/microlinkhq/recipes/compare/v1.2.7...v1.2.8) (2021-12-01) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * add url protocol ([923d681](https://github.com/microlinkhq/recipes/commit/923d681c7b43c4b35aa24ec2b1d73822d8110b94)) 86 | 87 | ### [1.2.7](https://github.com/microlinkhq/recipes/compare/v1.2.6...v1.2.7) (2021-12-01) 88 | 89 | ### [1.2.6](https://github.com/microlinkhq/recipes/compare/v1.2.5...v1.2.6) (2021-11-21) 90 | 91 | ### [1.2.5](https://github.com/microlinkhq/recipes/compare/v1.2.4...v1.2.5) (2021-11-16) 92 | 93 | 94 | ### Bug Fixes 95 | 96 | * get html from function parameters ([9f1f4ea](https://github.com/microlinkhq/recipes/commit/9f1f4ea68487cf5cea23d8b2ca0c51e17e1cff03)) 97 | 98 | ### [1.2.4](https://github.com/microlinkhq/recipes/compare/v1.2.3...v1.2.4) (2021-10-31) 99 | 100 | 101 | ### Bug Fixes 102 | 103 | * add if statement ([8d89b6c](https://github.com/microlinkhq/recipes/commit/8d89b6c449e6ec04adf58744231f9577dcdafa20)) 104 | * update coolmod example url ([5c7a5e6](https://github.com/microlinkhq/recipes/commit/5c7a5e60d43cea925dcf7004d9b8971ecee6789f)) 105 | 106 | ### [1.2.3](https://github.com/microlinkhq/recipes/compare/v1.2.2...v1.2.3) (2021-10-30) 107 | 108 | ### [1.2.2](https://github.com/microlinkhq/recipes/compare/v1.2.1...v1.2.2) (2021-10-30) 109 | 110 | ### [1.2.1](https://github.com/microlinkhq/recipes/compare/v1.2.0...v1.2.1) (2021-10-24) 111 | 112 | 113 | ### Bug Fixes 114 | 115 | * define function as text ([46c99a5](https://github.com/microlinkhq/recipes/commit/46c99a54d48fae10c2bb9b485b2f5c019e674c9d)) 116 | 117 | ## [1.2.0](https://github.com/microlinkhq/recipes/compare/v1.1.9...v1.2.0) (2021-10-12) 118 | 119 | 120 | ### Features 121 | 122 | * add get excerpts and emails ([ccce589](https://github.com/microlinkhq/recipes/commit/ccce58972309051a4fb07d62f8317c445ca000d4)) 123 | 124 | ### [1.1.9](https://github.com/microlinkhq/recipes/compare/v1.1.8...v1.1.9) (2021-09-30) 125 | 126 | ### [1.1.8](https://github.com/microlinkhq/recipes/compare/v1.1.7...v1.1.8) (2021-09-28) 127 | 128 | 129 | ### Bug Fixes 130 | 131 | * codepen returns a screenshot url ([2ec6260](https://github.com/microlinkhq/recipes/commit/2ec626061455b8ab2a92f904902a943a41d82d5f)) 132 | 133 | ### [1.1.7](https://github.com/microlinkhq/recipes/compare/v1.1.6...v1.1.7) (2021-09-19) 134 | 135 | ### [1.1.6](https://github.com/microlinkhq/recipes/compare/v1.1.5...v1.1.6) (2021-09-05) 136 | 137 | ### [1.1.5](https://github.com/microlinkhq/recipes/compare/v1.1.4...v1.1.5) (2021-09-05) 138 | 139 | ### [1.1.4](https://github.com/microlinkhq/recipes/compare/v1.1.3...v1.1.4) (2021-09-05) 140 | 141 | ### [1.1.3](https://github.com/microlinkhq/recipes/compare/v1.1.2...v1.1.3) (2021-09-05) 142 | 143 | ### 1.1.2 (2021-07-18) 144 | 145 | ### [1.1.1](https://github.com/microlinkhq/recipes/compare/v1.1.0...v1.1.1) (2021-07-12) 146 | 147 | ## [1.1.0](https://github.com/microlinkhq/recipes/compare/v1.0.3...v1.1.0) (2021-07-08) 148 | 149 | 150 | ### Features 151 | 152 | * add telegram recipe ([bef7497](https://github.com/microlinkhq/recipes/commit/bef749799a47a5f004e1e2bca592916cce04fa08)) 153 | 154 | ### [1.0.3](https://github.com/microlinkhq/recipes/compare/v1.0.2...v1.0.3) (2021-06-28) 155 | 156 | 157 | ### Bug Fixes 158 | 159 | * avoid to force prerendering ([4fc03c6](https://github.com/microlinkhq/recipes/commit/4fc03c6c19bf97a8eaafeb6c1d0d4ffb42723c46)) 160 | 161 | ### [1.0.2](https://github.com/microlinkhq/recipes/compare/v1.0.1...v1.0.2) (2021-06-28) 162 | 163 | ### [1.0.1](https://github.com/microlinkhq/recipes/compare/v1.0.0...v1.0.1) (2021-06-25) 164 | 165 | ## [1.0.0](https://github.com/microlinkhq/recipes/compare/v0.0.32...v1.0.0) (2021-04-24) 166 | 167 | 168 | ### Bug Fixes 169 | 170 | * debug ([207283c](https://github.com/microlinkhq/recipes/commit/207283cc1bdbc5aa26f222fee2af6899eab76fde)) 171 | * test ([be86ba5](https://github.com/microlinkhq/recipes/commit/be86ba548c3b386ef7e6a2f4f5ec4f49faf3e2a0)) 172 | 173 | ### [0.0.32](https://github.com/microlinkhq/recipes/compare/v0.0.31...v0.0.32) (2021-03-11) 174 | 175 | ### [0.0.31](https://github.com/microlinkhq/recipes/compare/v0.0.30...v0.0.31) (2020-11-30) 176 | 177 | ### [0.0.30](https://github.com/microlinkhq/recipes/compare/v0.0.29...v0.0.30) (2020-11-30) 178 | 179 | ### [0.0.29](https://github.com/microlinkhq/recipes/compare/v0.0.28...v0.0.29) (2020-11-30) 180 | 181 | ### [0.0.28](https://github.com/microlinkhq/recipes/compare/v0.0.27...v0.0.28) (2020-11-10) 182 | 183 | ### [0.0.27](https://github.com/microlinkhq/recipes/compare/v0.0.26...v0.0.27) (2020-10-23) 184 | 185 | ### [0.0.26](https://github.com/microlinkhq/recipes/compare/v0.0.25...v0.0.26) (2020-10-22) 186 | 187 | ### [0.0.25](https://github.com/microlinkhq/recipes/compare/v0.0.24...v0.0.25) (2020-10-22) 188 | 189 | ### [0.0.24](https://github.com/microlinkhq/recipes/compare/v0.0.23...v0.0.24) (2020-09-26) 190 | 191 | ### [0.0.23](https://github.com/microlinkhq/recipes/compare/v0.0.22...v0.0.23) (2020-09-22) 192 | 193 | ### [0.0.22](https://github.com/microlinkhq/recipes/compare/v0.0.21...v0.0.22) (2020-09-16) 194 | 195 | 196 | ### Bug Fixes 197 | 198 | * add missing dependency ([68dbd80](https://github.com/microlinkhq/recipes/commit/68dbd80335a5aa3736b630256d23f85dedbabad1)) 199 | 200 | ### [0.0.21](https://github.com/microlinkhq/recipes/compare/v0.0.20...v0.0.21) (2020-08-21) 201 | 202 | 203 | ### Bug Fixes 204 | 205 | * path ([83147ac](https://github.com/microlinkhq/recipes/commit/83147ac9a2af6d8f7ab4c273ec7e8d78d326aa23)) 206 | 207 | ### [0.0.20](https://github.com/microlinkhq/recipes/compare/v0.0.19...v0.0.20) (2020-08-21) 208 | 209 | ### [0.0.19](https://github.com/microlinkhq/recipes/compare/v0.0.18...v0.0.19) (2020-08-21) 210 | 211 | ### [0.0.18](https://github.com/microlinkhq/recipes/compare/v0.0.17...v0.0.18) (2020-08-09) 212 | 213 | ### [0.0.17](https://github.com/microlinkhq/recipes/compare/v0.0.16...v0.0.17) (2020-08-05) 214 | 215 | 216 | ### Bug Fixes 217 | 218 | * pitfalls ([e79cef9](https://github.com/microlinkhq/recipes/commit/e79cef952f83a13440391194d16d6d44c8841e8e)) 219 | 220 | ### [0.0.16](https://github.com/microlinkhq/recipes/compare/v0.0.15...v0.0.16) (2020-07-13) 221 | 222 | ### [0.0.15](https://github.com/microlinkhq/recipes/compare/v0.0.14...v0.0.15) (2020-07-13) 223 | 224 | 225 | ### Bug Fixes 226 | 227 | * files meta ([e5983ee](https://github.com/microlinkhq/recipes/commit/e5983eeede35e2029274d9b32cd606efe73bd511)) 228 | * meta ([7669e14](https://github.com/microlinkhq/recipes/commit/7669e14748f7632cd69c90f23071bf76bb1b817a)) 229 | 230 | ### [0.0.14](http://github.com///compare/v0.0.13...v0.0.14) (2020-07-13) 231 | 232 | ### [0.0.13](http://github.com///compare/v0.0.12...v0.0.13) (2020-07-09) 233 | 234 | ### [0.0.12](http://github.com///compare/v0.0.11...v0.0.12) (2020-07-08) 235 | 236 | ### [0.0.11](http://github.com///compare/v0.0.10...v0.0.11) (2020-07-08) 237 | 238 | ### [0.0.10](http://github.com///compare/v0.0.9...v0.0.10) (2020-07-08) 239 | 240 | 241 | ### Bug Fixes 242 | 243 | * import ([7e354d8](http://github.com///commit/7e354d8f7eeef2c63201c0dbe129a8f4f654ffbd)) 244 | 245 | ### [0.0.9](http://github.com///compare/v0.0.8...v0.0.9) (2020-07-08) 246 | 247 | ### [0.0.8](http://github.com///compare/v0.0.7...v0.0.8) (2020-07-06) 248 | 249 | ### [0.0.7](http://github.com///compare/v0.0.6...v0.0.7) (2020-07-06) 250 | 251 | ### [0.0.6](http://github.com///compare/v0.0.5...v0.0.6) (2020-07-05) 252 | 253 | ### [0.0.5](http://github.com///compare/v0.0.4...v0.0.5) (2020-07-01) 254 | 255 | ### [0.0.4](http://github.com///compare/v0.0.3...v0.0.4) (2020-07-01) 256 | 257 | ### [0.0.3](http://github.com///compare/v0.0.2...v0.0.3) (2020-06-30) 258 | 259 | ### [0.0.2](http://github.com///compare/v0.0.1...v0.0.2) (2020-06-30) 260 | 261 | ### 0.0.1 (2020-06-30) 262 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2020 microlink.io (microlink.io) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | microlink logo 3 | microlink logo 4 |
5 |
6 |
7 | 8 | ![Last version](https://img.shields.io/github/tag/microlinkhq/recipes.svg?style=flat-square) 9 | [![Coverage Status](https://img.shields.io/coveralls/microlinkhq/recipes.svg?style=flat-square)](https://coveralls.io/github/microlinkhq/recipes) 10 | [![NPM Status](https://img.shields.io/npm/dm/recipes.svg?style=flat-square)](https://www.npmjs.org/package/recipes) 11 | 12 | ## License 13 | 14 | **@microlink/recipes** © [Microlink](https://microlink.io), released under the [MIT](https://github.com/microlinkhq/recipes/blob/master/LICENSE.md) License.
15 | Authored and maintained by [Kiko Beats](https://kikobeats.com) with help from [contributors](https://github.com/microlinkhq/recipes/contributors). 16 | 17 | > [microlink.io](https://microlink.io) · GitHub [microlinkhq](https://github.com/microlinkhq) · Twitter [@microlinkhq](https://twitter.com/microlinkhq) 18 | -------------------------------------------------------------------------------- /by-feature.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | debugCss: require('./recipes/by-feature/debug-css'), 3 | emails: require('./recipes/by-feature/emails'), 4 | embed: require('./recipes/by-feature/embed'), 5 | excerpt: require('./recipes/by-feature/excerpt'), 6 | favicon: require('./recipes/by-feature/favicon'), 7 | fullScreenshot: require('./recipes/by-feature/full-screenshot'), 8 | headings: require('./recipes/by-feature/headings'), 9 | healthcheck: require('./recipes/by-feature/healthcheck'), 10 | html: require('./recipes/by-feature/html'), 11 | images: require('./recipes/by-feature/images'), 12 | jsonLd: require('./recipes/by-feature/json-ld'), 13 | lighthouse: require('./recipes/by-feature/lighthouse'), 14 | pdf: require('./recipes/by-feature/pdf'), 15 | screenshot: require('./recipes/by-feature/screenshot'), 16 | technologyStack: require('./recipes/by-feature/technology-stack'), 17 | text: require('./recipes/by-feature/text'), 18 | uris: require('./recipes/by-feature/uris'), 19 | youtubeDl: require('./recipes/by-feature/youtube-dl') 20 | } 21 | -------------------------------------------------------------------------------- /by-provider.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | amazon: require('./recipes/by-provider/amazon'), 3 | assc: require('./recipes/by-provider/assc'), 4 | balenciaga: require('./recipes/by-provider/balenciaga'), 5 | betalist: require('./recipes/by-provider/betalist'), 6 | canopy: require('./recipes/by-provider/canopy'), 7 | codepen: require('./recipes/by-provider/codepen'), 8 | fca: require('./recipes/by-provider/fca'), 9 | github: require('./recipes/by-provider/github'), 10 | google: require('./recipes/by-provider/google'), 11 | hackerNews: require('./recipes/by-provider/hacker-news'), 12 | imdb: require('./recipes/by-provider/imdb'), 13 | instagram: require('./recipes/by-provider/instagram'), 14 | meetup: require('./recipes/by-provider/meetup'), 15 | parcel: require('./recipes/by-provider/parcel'), 16 | producthunt: require('./recipes/by-provider/producthunt'), 17 | reddit: require('./recipes/by-provider/reddit'), 18 | ripndip: require('./recipes/by-provider/ripndip'), 19 | soundcloud: require('./recipes/by-provider/soundcloud'), 20 | spotify: require('./recipes/by-provider/spotify'), 21 | telegram: require('./recipes/by-provider/telegram'), 22 | tiktok: require('./recipes/by-provider/tiktok'), 23 | twitter: require('./recipes/by-provider/twitter'), 24 | wipoid: require('./recipes/by-provider/wipoid'), 25 | zalando: require('./recipes/by-provider/zalando'), 26 | youtube: require('./recipes/by-provider/youtube') 27 | } 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { ...require('./by-provider'), ...require('./by-feature') } 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@microlink/recipes", 3 | "description": "Build something with just a few of lines", 4 | "homepage": "https://microlink.io/recipes", 5 | "version": "1.8.1", 6 | "main": "index.js", 7 | "author": { 8 | "email": "hello@microlink.io", 9 | "name": "microlink.io", 10 | "url": "https://microlink.io" 11 | }, 12 | "contributors": [ 13 | { 14 | "name": "Kiko Beats", 15 | "email": "josefrancisco.verdu@gmail.com" 16 | }, 17 | { 18 | "name": "Brad Adams", 19 | "email": "hi@breadadams.com" 20 | } 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/microlinkhq/recipes.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/microlinkhq/recipes/issues" 28 | }, 29 | "keywords": [ 30 | "microlink", 31 | "recipes" 32 | ], 33 | "dependencies": { 34 | "@microlink/demo-links": "latest", 35 | "@microlink/function": "latest", 36 | "@microlink/mql": "latest" 37 | }, 38 | "devDependencies": { 39 | "@commitlint/cli": "latest", 40 | "@commitlint/config-conventional": "latest", 41 | "@ksmithut/prettier-standard": "latest", 42 | "ava": "latest", 43 | "c8": "latest", 44 | "ci-publish": "latest", 45 | "conventional-github-releaser": "latest", 46 | "finepack": "latest", 47 | "git-authors-cli": "latest", 48 | "nano-staged": "latest", 49 | "npm-check-updates": "latest", 50 | "ow": "~0.28.2", 51 | "simple-git-hooks": "latest", 52 | "standard": "latest", 53 | "standard-markdown": "latest", 54 | "standard-version": "latest" 55 | }, 56 | "engines": { 57 | "node": ">= 10" 58 | }, 59 | "files": [ 60 | "by-feature.js", 61 | "by-provider.js", 62 | "index.js", 63 | "recipes" 64 | ], 65 | "scripts": { 66 | "clean": "rm -rf node_modules", 67 | "contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true", 68 | "coverage": "nyc report --reporter=text-lcov | coveralls", 69 | "lint": "standard-markdown README.md && standard", 70 | "postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)", 71 | "prerelease": "npm run update:check && npm run contributors", 72 | "pretest": "npm run lint", 73 | "release": "standard-version -a", 74 | "release:github": "conventional-github-releaser -p angular", 75 | "release:tags": "git push --follow-tags origin HEAD:master", 76 | "test": "c8 ava", 77 | "update": "ncu -u", 78 | "update:check": "ncu -- --error-level 2" 79 | }, 80 | "license": "MIT", 81 | "commitlint": { 82 | "extends": [ 83 | "@commitlint/config-conventional" 84 | ] 85 | }, 86 | "nano-staged": { 87 | "*.js,!*.min.js,": [ 88 | "prettier-standard", 89 | "standard --fix" 90 | ], 91 | "*.md": [ 92 | "standard-markdown" 93 | ], 94 | "package.json": [ 95 | "finepack" 96 | ] 97 | }, 98 | "simple-git-hooks": { 99 | "commit-msg": "npx commitlint --edit", 100 | "pre-commit": "npx nano-staged" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /recipes/by-feature/debug-css.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts = {}) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | screenshot: true, 9 | ...opts, 10 | scripts: [ 11 | ...(opts.scripts || []), 12 | '[].forEach.call(document.querySelectorAll("*"),function(a){a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)})' 13 | ] 14 | }) 15 | 16 | return data.screenshot 17 | } 18 | 19 | module.exports.meta = { 20 | name: 'Debug CSS', 21 | description: 'A CSS debugger visualization for any screenshot.', 22 | examples: ['https://microlink.io'] 23 | } 24 | -------------------------------------------------------------------------------- /recipes/by-feature/emails.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | data: { 9 | emails: { 10 | selector: 'body', 11 | type: 'email' 12 | } 13 | }, 14 | ...opts 15 | }) 16 | 17 | return data.emails 18 | } 19 | 20 | module.exports.meta = { 21 | name: 'Emails', 22 | description: 'Find any email inside a website', 23 | examples: ['https://www.raycast.com'] 24 | } 25 | -------------------------------------------------------------------------------- /recipes/by-feature/embed.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const demoLinks = require('@microlink/demo-links') 4 | const mql = require('@microlink/mql') 5 | 6 | module.exports = async (url, opts) => { 7 | const { data } = await mql(url, { 8 | meta: false, 9 | iframe: { maxWidth: 350 }, 10 | ...opts 11 | }) 12 | 13 | return data.iframe 14 | } 15 | 16 | module.exports.meta = { 17 | name: 'Universal Embed', 18 | description: 'Display embedded content, in a unified way', 19 | examples: [ 20 | demoLinks.YouTube.url, 21 | // demoLinks.Vimeo.url 22 | demoLinks.SoundCloud.url, 23 | demoLinks.Twitter.url 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /recipes/by-feature/excerpt.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const microlink = require('@microlink/function') 4 | 5 | const code = `async ({ url, html }) => { 6 | const { Readability } = require('@mozilla/readability') 7 | const { JSDOM, VirtualConsole } = require('jsdom') 8 | 9 | const dom = new JSDOM(html, { 10 | url, 11 | virtualConsole: new VirtualConsole() 12 | }) 13 | 14 | const reader = new Readability(dom.window.document) 15 | return reader.parse().excerpt 16 | }` 17 | 18 | module.exports = microlink(code) 19 | module.exports.code = code 20 | module.exports.meta = { 21 | name: 'Excerpt', 22 | description: 'Get the article description, or short excerpt from the content', 23 | examples: ['https://developer.redis.com'] 24 | } 25 | -------------------------------------------------------------------------------- /recipes/by-feature/favicon.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | data: { 9 | favicon: [ 10 | { 11 | selector: 'link[href*="favicon.ico"]', 12 | attr: 'href', 13 | type: 'image' 14 | }, 15 | { 16 | selector: 'link[type="image/x-icon"]', 17 | attr: 'href', 18 | type: 'image' 19 | } 20 | ] 21 | }, 22 | ...opts 23 | }) 24 | 25 | return data.favicon 26 | } 27 | 28 | module.exports.meta = { 29 | name: 'Favicon', 30 | description: 'Get the favicon for any web', 31 | examples: ['https://microlink.io'] 32 | } 33 | -------------------------------------------------------------------------------- /recipes/by-feature/full-screenshot.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | screenshot: true, 9 | fullPage: true, 10 | ...opts 11 | }) 12 | 13 | return data.screenshot 14 | } 15 | 16 | module.exports.meta = { 17 | name: 'Full Page Screenshot', 18 | description: 'Generate a full page screenshot over the target URL', 19 | examples: ['https://eosrei.github.io/emojione-color-font/full-demo.html'] 20 | } 21 | -------------------------------------------------------------------------------- /recipes/by-feature/headings.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | data: { 9 | h1: { 10 | selectorAll: 'h1', 11 | attr: 'text', 12 | type: 'title' 13 | }, 14 | h2: { 15 | selectorAll: 'h2', 16 | attr: 'text', 17 | type: 'title' 18 | }, 19 | h3: { 20 | selectorAll: 'h3', 21 | attr: 'text', 22 | type: 'title' 23 | }, 24 | h4: { 25 | selectorAll: 'h4', 26 | attr: 'text', 27 | type: 'title' 28 | }, 29 | h5: { 30 | selectorAll: 'h5', 31 | attr: 'text', 32 | type: 'title' 33 | }, 34 | h6: { 35 | selectorAll: 'h6', 36 | attr: 'text', 37 | type: 'title' 38 | } 39 | }, 40 | ...opts 41 | }) 42 | 43 | return data 44 | } 45 | 46 | module.exports.meta = { 47 | name: 'Headings', 48 | description: 'Retrieve all the headings of the target URL', 49 | examples: ['https://microlink.io/'] 50 | } 51 | -------------------------------------------------------------------------------- /recipes/by-feature/healthcheck.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const microlink = require('@microlink/function') 4 | 5 | const code = `async ({ query, page, response }) => ({ 6 | url: response && response.url(), 7 | statusCode: response && response.status(), 8 | headers: response && response.headers(), 9 | html: await page.content(), 10 | statusCode: response && response.status() 11 | })` 12 | 13 | module.exports = microlink(code) 14 | module.exports.code = code 15 | module.exports.meta = { 16 | name: 'Healthcheck', 17 | description: 'A simple way to check if an URL is reachable', 18 | examples: ['https://deno.com'] 19 | } 20 | -------------------------------------------------------------------------------- /recipes/by-feature/html.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | data: { 9 | html: { 10 | selector: 'html' 11 | } 12 | }, 13 | ...opts 14 | }) 15 | 16 | return data.html 17 | } 18 | 19 | module.exports.meta = { 20 | name: 'HTML', 21 | description: 'Get the rendered HTML markup of the target URL', 22 | examples: ['https://example.com'] 23 | } 24 | -------------------------------------------------------------------------------- /recipes/by-feature/images.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | data: { 9 | images: { 10 | selectorAll: 'img', 11 | attr: 'src', 12 | type: 'image' 13 | } 14 | }, 15 | ...opts 16 | }) 17 | 18 | return data.images 19 | } 20 | 21 | module.exports.meta = { 22 | name: 'Images', 23 | description: 'Retrieve all the images of the target URL', 24 | examples: ['https://microlink.io'] 25 | } 26 | -------------------------------------------------------------------------------- /recipes/by-feature/json-ld.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | data: { 9 | jsonLd: { 10 | selectorAll: 'script[type="application/ld+json"]' 11 | } 12 | }, 13 | ...opts 14 | }) 15 | 16 | return data.jsonLd 17 | } 18 | 19 | module.exports.meta = { 20 | name: 'JSON+LD', 21 | description: 'Retrieve all JSON+LD elements present on the website', 22 | examples: ['https://microlink.io'] 23 | } 24 | -------------------------------------------------------------------------------- /recipes/by-feature/lighthouse.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | technologies: false, 9 | insights: { 10 | lighthouse: { 11 | preset: 'lr-mobile' 12 | } 13 | }, 14 | ...opts 15 | }) 16 | 17 | return data.insights.lighthouse 18 | } 19 | 20 | module.exports.meta = { 21 | name: 'Lighthouse', 22 | logo: 'https://cdn.microlink.io/logos/lighthouse.png', 23 | description: 'Run Lighthouse on demand', 24 | examples: ['https://example.com'] 25 | } 26 | -------------------------------------------------------------------------------- /recipes/by-feature/pdf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | pdf: true, 9 | ...opts 10 | }) 11 | 12 | return data.pdf 13 | } 14 | 15 | module.exports.meta = { 16 | name: 'PDF', 17 | description: 'Generate a PDF over the target URL', 18 | examples: ['https://rauchg.com/2014/7-principles-of-rich-web-applications'] 19 | } 20 | -------------------------------------------------------------------------------- /recipes/by-feature/screenshot.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | screenshot: true, 9 | ...opts 10 | }) 11 | 12 | return data.screenshot 13 | } 14 | 15 | module.exports.meta = { 16 | name: 'Screenshot', 17 | description: 'Generate a screenshot over the target URL', 18 | examples: ['https://vercel.com'] 19 | } 20 | -------------------------------------------------------------------------------- /recipes/by-feature/technology-stack.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | insights: { 9 | lighthouse: false, 10 | technologies: true 11 | }, 12 | ...opts 13 | }) 14 | 15 | return data.insights.technologies 16 | } 17 | 18 | module.exports.meta = { 19 | name: 'Technology Stack', 20 | logo: 'https://cdn.microlink.io/logos/wappalyzer.png', 21 | description: 'Detect web technologies behind a site', 22 | examples: ['https://example.com'] 23 | } 24 | -------------------------------------------------------------------------------- /recipes/by-feature/text.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const microlink = require('@microlink/function') 4 | 5 | const code = `async ({ url, html }) => { 6 | const condenseWhitespace = require('condense-whitespace') 7 | const { Readability } = require('@mozilla/readability') 8 | const { JSDOM, VirtualConsole } = require('jsdom') 9 | 10 | const dom = new JSDOM(html, { 11 | url, 12 | virtualConsole: new VirtualConsole() 13 | }) 14 | 15 | const reader = new Readability(dom.window.document) 16 | const { textContent } = reader.parse() 17 | return condenseWhitespace(textContent) 18 | }` 19 | 20 | module.exports = microlink(code) 21 | module.exports.code = code 22 | module.exports.meta = { 23 | name: 'Text', 24 | description: 'Get the text content of the target URL', 25 | examples: [ 26 | 'https://geohot.github.io/blog/jekyll/update/2023/04/26/a-person-of-compute.html' 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /recipes/by-feature/uris.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const microlink = require('@microlink/function') 4 | 5 | const code = `({ url, html }) => { 6 | const htmlUrls = require('html-urls') 7 | return htmlUrls({ html, url }).map(({ url }) => url) 8 | }` 9 | 10 | module.exports = microlink(code) 11 | module.exports.code = code 12 | module.exports.meta = { 13 | name: 'URIs', 14 | description: 'Retrieve all the URIs over the target URL', 15 | examples: ['https://rauchg.com/'] 16 | } 17 | -------------------------------------------------------------------------------- /recipes/by-feature/youtube-dl.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const microlink = require('@microlink/function') 4 | 5 | const code = `async ({ url, html }) => { 6 | const youtubedl = require('youtube-dl-exec') 7 | 8 | return youtubedl(url, { 9 | dumpSingleJson: true, 10 | noCheckCertificates: true, 11 | noWarnings: true, 12 | preferFreeFormats: true 13 | }) 14 | }` 15 | 16 | module.exports = microlink(code) 17 | module.exports.code = code 18 | module.exports.meta = { 19 | name: 'youtube-dl', 20 | description: 21 | 'Get metadata for any video URL. Over 700 online video services supported.', 22 | examples: ['https://imrane.substack.com/p/la-guerre-de-troie-pour-ecrire-un'] 23 | } 24 | -------------------------------------------------------------------------------- /recipes/by-provider/amazon.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = (url, opts) => 6 | mql(url, { 7 | data: { 8 | price: { 9 | selector: '#attach-base-product-price', 10 | attr: 'val', 11 | type: 'number' 12 | }, 13 | currency: { 14 | selector: '#attach-base-product-currency-symbol', 15 | attr: 'val' 16 | } 17 | }, 18 | ...opts 19 | }) 20 | 21 | module.exports.meta = { 22 | name: 'Amazon', 23 | examples: ['https://www.amazon.com/dp/B08FC6C75Y'] 24 | } 25 | -------------------------------------------------------------------------------- /recipes/by-provider/assc.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | data: { 8 | price: { 9 | selector: '.money' 10 | } 11 | }, 12 | ...opts 13 | }) 14 | 15 | return result 16 | } 17 | 18 | module.exports.meta = { 19 | name: 'ASSC', 20 | examples: [ 21 | 'https://www.antisocialsocialclub.com/products/assc-x-case-study-flag-black-hoodie?variant=41157788532934' 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /recipes/by-provider/balenciaga.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | prerender: true, 8 | data: { 9 | currency: { 10 | selector: '[itemprop="priceCurrency"]', 11 | attr: 'content' 12 | }, 13 | price: { 14 | selector: '[itemprop="price"]', 15 | attr: 'content' 16 | // type: 'number' 17 | } 18 | }, 19 | ...opts 20 | }) 21 | 22 | return result 23 | } 24 | 25 | module.exports.meta = { 26 | name: 'Balenciaga', 27 | examples: [ 28 | 'https://www.balenciaga.com/en-us/speed-recycled-sneaker-black-white-645056W2DBQ1015.html' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /recipes/by-provider/betalist.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = (url, opts) => 6 | mql(url, { 7 | meta: false, 8 | prerender: true, 9 | data: { 10 | startups: { 11 | selectorAll: '.startupCard', 12 | attr: { 13 | name: { 14 | selector: '.startupCard__details__name', 15 | type: 'text' 16 | }, 17 | url: { 18 | selector: '.startupCard__visual', 19 | attr: 'href' 20 | }, 21 | description: { 22 | selector: '.startupCard__details__pitch', 23 | type: 'text' 24 | }, 25 | image: { 26 | selector: '.startupCard__visual__image', 27 | attr: ['src', 'poster'], 28 | type: 'url' 29 | } 30 | } 31 | } 32 | }, 33 | ...opts 34 | }) 35 | 36 | module.exports.meta = { 37 | name: 'BetaList', 38 | examples: ['https://betalist.com/markets/developer-tools?page=1'] 39 | } 40 | -------------------------------------------------------------------------------- /recipes/by-provider/canopy.js: -------------------------------------------------------------------------------- 1 | const mql = require('@microlink/mql') 2 | 3 | module.exports = (url, opts) => 4 | mql(url, { 5 | prerender: true, 6 | data: { 7 | products: { 8 | selectorAll: '.product-card', 9 | attr: { 10 | title: { 11 | type: 'title', 12 | selector: '.product-details-name', 13 | attr: 'text' 14 | }, 15 | url: { 16 | type: 'url', 17 | selector: '.product-link-tracking', 18 | attr: 'href' 19 | }, 20 | amazonUrl: { 21 | type: 'url', 22 | selector: '.card-buy-button', 23 | attr: 'href' 24 | }, 25 | image: { 26 | type: 'img', 27 | selector: '.product-card-image img', 28 | attr: 'src' 29 | }, 30 | brand: { 31 | type: 'title', 32 | selector: '.product-details-brand', 33 | attr: 'text' 34 | } 35 | } 36 | } 37 | }, 38 | ...opts 39 | }) 40 | 41 | module.exports.meta = { 42 | name: 'Canocopy', 43 | examples: ['https://canopy.co/shop/everyday-carry'] 44 | } 45 | -------------------------------------------------------------------------------- /recipes/by-provider/codepen.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url, { 7 | meta: false, 8 | screenshot: true, 9 | styles: ['.main-header, .oldie-header {display: none}'], 10 | ...opts 11 | }) 12 | 13 | return data.screenshot.url 14 | } 15 | 16 | module.exports.meta = { 17 | name: 'CodePen', 18 | examples: ['https://codepen.io/fossheim/full/oNjxrZa'] 19 | } 20 | -------------------------------------------------------------------------------- /recipes/by-provider/fca.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | waitForSelector: '#profile-header', 8 | meta: false, 9 | prerender: true, 10 | data: { 11 | name: { 12 | selector: 'h1' 13 | }, 14 | updatedAt: { 15 | attr: 'text', 16 | selector: 17 | '#who-is-this-details-content > div.stack.stack--direct.stack--medium > div.accordion__text > div > p:nth-child(2)' 18 | }, 19 | address: { 20 | selector: 21 | '#who-is-this-details-content > div.stack.stack--direct.stack--medium > div.slds-grid.slds-wrap.gutters-large.gutters-medium_none > div.slds-col.slds-size_1-of-1.slds-medium-size_6-of-12.slds-p-around_none.slds-p-right_small > div > div > div:nth-child(1) > p', 22 | attr: 'text' 23 | }, 24 | firmReferenceNumber: { 25 | type: 'number', 26 | selector: 27 | '#who-is-this-details-content > div.stack.stack--direct.stack--medium > div.slds-grid.slds-wrap.gutters-large.gutters-medium_none > div:nth-child(3) > div > div > div:nth-child(1) > p' 28 | }, 29 | registeredCompanyNumber: { 30 | selector: '.hide-href-print' 31 | }, 32 | email: { 33 | selector: 34 | '#who-is-this-details-content > div.stack.stack--direct.stack--medium > div.slds-grid.slds-wrap.gutters-large.gutters-medium_none > div.slds-col.slds-size_1-of-1.slds-medium-size_6-of-12.slds-p-around_none.slds-p-right_small > div > div > div:nth-child(3) > p' 35 | }, 36 | phone: { 37 | type: 'text', 38 | selector: 39 | '#who-is-this-details-content > div.stack.stack--direct.stack--medium > div.slds-grid.slds-wrap.gutters-large.gutters-medium_none > div.slds-col.slds-size_1-of-1.slds-medium-size_6-of-12.slds-p-around_none.slds-p-right_small > div > div > div:nth-child(2) > p' 40 | }, 41 | website: { 42 | type: 'url', 43 | selector: 44 | '#who-is-this-details-content > div.stack.stack--direct.stack--medium > div.slds-grid.slds-wrap.gutters-large.gutters-medium_none > div.slds-col.slds-size_1-of-1.slds-medium-size_6-of-12.slds-p-around_none.slds-p-right_small > div > div > div:nth-child(3) > a > span:nth-child(1)' 45 | } 46 | }, 47 | ...opts 48 | }) 49 | 50 | if (result.data.registeredCompanyNumber) { 51 | result.data.registeredCompanyNumber = Number( 52 | result.data.registeredCompanyNumber.split('<')[0] 53 | ) 54 | } 55 | 56 | if (result.data.updatedAt) { 57 | result.data.updatedAt = result.data.updatedAt.split('firm details')[1].trim() 58 | } 59 | 60 | return result 61 | } 62 | 63 | module.exports.meta = { 64 | name: 'FCA', 65 | examples: ['https://register.fca.org.uk/s/firm?id=001b000000NMdXdAAL'] 66 | } 67 | -------------------------------------------------------------------------------- /recipes/by-provider/github.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = (url, opts) => 6 | mql(url, { 7 | data: { 8 | stats: { 9 | selector: '.application-main', 10 | attr: { 11 | followers: { 12 | selector: '.js-profile-editable-area a[href*="tab=followers"] span', 13 | type: 'number' 14 | }, 15 | following: { 16 | selector: '.js-profile-editable-area a[href*="tab=following"] span', 17 | type: 'number' 18 | }, 19 | stars: { 20 | selector: '.js-responsive-underlinenav a[data-tab-item="stars"] span', 21 | type: 'number' 22 | } 23 | } 24 | } 25 | }, 26 | ...opts 27 | }) 28 | 29 | module.exports.meta = { 30 | name: 'GitHub', 31 | examples: ['https://github.com/kikobeats'] 32 | } 33 | -------------------------------------------------------------------------------- /recipes/by-provider/google.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | data: { 8 | results: { 9 | selectorAll: '.g', 10 | attr: { 11 | title: { 12 | selector: 'h3', 13 | attr: 'text' 14 | }, 15 | description: { 16 | selector: 'span', 17 | attr: 'text' 18 | }, 19 | breadcumb: { 20 | selector: 'cite', 21 | attr: 'text', 22 | type: 'text' 23 | }, 24 | link: { 25 | selector: 'a', 26 | attr: 'href', 27 | type: 'url' 28 | } 29 | } 30 | } 31 | }, 32 | ...opts 33 | }) 34 | 35 | if (result.data.results) { 36 | result.data.results = result.data.results.filter( 37 | ({ title, description }) => !!title && !!description 38 | ) 39 | } 40 | 41 | return result 42 | } 43 | 44 | module.exports.meta = { 45 | name: 'Google', 46 | examples: ['https://www.google.com/search?q=microlink'] 47 | } 48 | -------------------------------------------------------------------------------- /recipes/by-provider/hacker-news.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = opts => 6 | mql('https://news.ycombinator.com', { 7 | data: { 8 | posts: { 9 | selectorAll: '.athing', 10 | attr: { 11 | title: { 12 | type: 'title', 13 | selector: '.titlelink', 14 | attr: 'text' 15 | }, 16 | url: { 17 | type: 'url', 18 | selector: '.titlelink', 19 | attr: 'href' 20 | } 21 | } 22 | } 23 | }, 24 | ...opts 25 | }) 26 | 27 | module.exports.meta = { 28 | name: 'Hacker News', 29 | examples: ['https://news.ycombinator.com'] 30 | } 31 | -------------------------------------------------------------------------------- /recipes/by-provider/imdb.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | data: { 8 | director: { 9 | selector: '.ipc-metadata-list__item:nth-child(1) a', 10 | type: 'text' 11 | }, 12 | writer: { 13 | selector: '.ipc-metadata-list__item:nth-child(2) a', 14 | type: 'text' 15 | }, 16 | duration: { 17 | selector: '.ipc-inline-list__item[role="presentation"]:nth-child(3)', 18 | type: 'text' 19 | }, 20 | year: { 21 | selector: 22 | '.ipc-inline-list__item[role="presentation"]:nth-child(1) span', 23 | type: 'number' 24 | }, 25 | rating: { 26 | selector: '.rating-bar__base-button .ipc-button__text span', 27 | type: 'text' 28 | }, 29 | ratingCount: { 30 | selector: '.rating-bar__base-button .ipc-button__text div:nth-child(3)', 31 | type: 'text' 32 | } 33 | }, 34 | ...opts 35 | }) 36 | 37 | if (result.data.rating) result.data.rating += ' of 10' 38 | if (result.data.duration) result.data.duration = result.data.duration.trim() 39 | return result 40 | } 41 | 42 | module.exports.meta = { 43 | name: 'IMDb', 44 | examples: ['https://www.imdb.com/title/tt1853728/'] 45 | } 46 | -------------------------------------------------------------------------------- /recipes/by-provider/instagram.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | data: { 8 | avatar: { 9 | selector: 'meta[property="og:image"]', 10 | attr: 'content', 11 | type: 'image' 12 | }, 13 | stats: { 14 | selector: 'meta[property="og:description"]', 15 | attr: 'content' 16 | } 17 | }, 18 | ...opts 19 | }) 20 | 21 | if (result.data.stats) { 22 | result.data.stats = result.data.stats.split(' - ')[0] 23 | result.data.stats = result.data.stats.split(', ') 24 | result.data.stats = result.data.stats.reduce((acc, info) => { 25 | const [value, key] = info.split(' ') 26 | return { ...acc, [key.toLowerCase()]: value } 27 | }, {}) 28 | } 29 | 30 | return result 31 | } 32 | 33 | module.exports.meta = { 34 | name: 'Instagram', 35 | examples: ['https://www.instagram.com/willsmith'] 36 | } 37 | -------------------------------------------------------------------------------- /recipes/by-provider/meetup.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => 6 | mql(url, { 7 | data: { 8 | members: { 9 | selector: '.groupHomeHeaderInfo-memberLink span', 10 | type: 'text' 11 | }, 12 | pastEvents: { 13 | selectorAll: '.eventCard', 14 | attr: { 15 | title: { 16 | selector: '.eventCardHead--title' 17 | }, 18 | link: { 19 | selector: '.eventCard--link', 20 | attr: 'href', 21 | type: 'url' 22 | }, 23 | attendees: { 24 | selector: '.avatarRow--attendingCount span' 25 | } 26 | } 27 | } 28 | }, 29 | ...opts 30 | }) 31 | 32 | module.exports.meta = { 33 | name: 'Meetup', 34 | examples: ['https://www.meetup.com/Alicante-Frontend/'] 35 | } 36 | -------------------------------------------------------------------------------- /recipes/by-provider/parcel.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const { data } = await mql(url.endsWith('/raw') ? url : `${url}/raw`, { 7 | meta: false, 8 | screenshot: true, 9 | device: 'iPhone X', 10 | ...opts 11 | }) 12 | 13 | return data.screenshot.url 14 | } 15 | 16 | module.exports.meta = { 17 | name: 'Parcel', 18 | examples: ['https://parcel.io/e/bfb95111-317b-4615-8b46-9e9bb6d9d365'] 19 | } 20 | -------------------------------------------------------------------------------- /recipes/by-provider/producthunt.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = (url, opts) => 6 | mql(url, { 7 | data: { 8 | reviews: { 9 | selector: 'div div div div div div a[href$="reviews"]' 10 | } 11 | }, 12 | ...opts 13 | }) 14 | 15 | module.exports.meta = { 16 | name: 'Product Hunt', 17 | examples: ['https://www.producthunt.com/products/microlink-io'] 18 | } 19 | -------------------------------------------------------------------------------- /recipes/by-provider/reddit.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = (url, opts) => 6 | mql(url, { 7 | data: { 8 | karma: { 9 | selector: '#profile--id-card--highlight-tooltip--karma' 10 | }, 11 | birthday: { 12 | selector: '#profile--id-card--highlight-tooltip--cakeday' 13 | }, 14 | avatar: { 15 | selector: 'img[alt="User avatar"]', 16 | attr: 'src', 17 | type: 'url' 18 | } 19 | }, 20 | ...opts 21 | }) 22 | 23 | module.exports.meta = { 24 | name: 'Reddit', 25 | examples: ['https://www.reddit.com/user/kikobeats'] 26 | } 27 | -------------------------------------------------------------------------------- /recipes/by-provider/ripndip.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | data: { 8 | price: { 9 | selector: '#ProductPrice' 10 | } 11 | }, 12 | ...opts 13 | }) 14 | 15 | return result 16 | } 17 | 18 | module.exports.meta = { 19 | name: 'ripndip', 20 | examples: [ 21 | 'https://www.ripndipclothing.com/collections/shirts/products/lord-nermal-black' 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /recipes/by-provider/soundcloud.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | prerender: true, 8 | audio: true, 9 | data: { 10 | plays: { 11 | selector: '.sc-ministats-plays .sc-visuallyhidden', 12 | type: 'number' 13 | } 14 | }, 15 | ...opts 16 | }) 17 | 18 | return result 19 | } 20 | 21 | module.exports.meta = { 22 | name: 'SoundCloud', 23 | examples: ['https://soundcloud.com/beautybrainsp/beauty-brain-swag-bandicoot'] 24 | } 25 | -------------------------------------------------------------------------------- /recipes/by-provider/spotify.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | audio: true, 8 | ...opts 9 | }) 10 | 11 | return result 12 | } 13 | 14 | module.exports.meta = { 15 | name: 'Spotify', 16 | examples: ['https://open.spotify.com/track/5kn2FMZoBVClbA9CV7w3k5'] 17 | } 18 | -------------------------------------------------------------------------------- /recipes/by-provider/telegram.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | data: { 8 | subscribers: { 9 | selector: '.tgme_channel_info_counter:nth-child(1) .counter_value' 10 | }, 11 | photos: { 12 | selector: '.tgme_channel_info_counter:nth-child(2) .counter_value' 13 | }, 14 | links: { 15 | selector: '.tgme_channel_info_counter:nth-child(3) .counter_value' 16 | } 17 | }, 18 | ...opts 19 | }) 20 | 21 | return result 22 | } 23 | 24 | module.exports.meta = { 25 | name: 'Telegram', 26 | examples: ['https://telegram.me/s/teslahunt'] 27 | } 28 | -------------------------------------------------------------------------------- /recipes/by-provider/tiktok.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = (url, opts) => 6 | mql(url, { 7 | data: { 8 | song: { 9 | selector: 'h4[data-e2e="browse-music"]', 10 | attr: 'text', 11 | type: 'string' 12 | }, 13 | likeCount: { 14 | selector: 'strong[data-e2e="like-count"]', 15 | attr: 'text', 16 | type: 'string' 17 | }, 18 | commentCount: { 19 | selector: 'strong[data-e2e="comment-count"]', 20 | attr: 'text', 21 | type: 'string' 22 | }, 23 | shareCount: { 24 | selector: 'strong[data-e2e="share-count"]', 25 | attr: 'text', 26 | type: 'string' 27 | } 28 | }, 29 | ...opts 30 | }) 31 | 32 | module.exports.meta = { 33 | name: 'TikTok', 34 | examples: ['https://www.tiktok.com/@willsmith/video/7064624682766503214'] 35 | } 36 | -------------------------------------------------------------------------------- /recipes/by-provider/twitter.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | data: { 8 | banner: { 9 | selector: 'main a[href$="header_photo"] img', 10 | attr: 'src', 11 | type: 'image' 12 | }, 13 | stats: { 14 | selector: 'main', 15 | attr: { 16 | retweets: { 17 | selector: 'a[href*="retweets"] span span span:nth(0)', 18 | attr: 'text' 19 | }, 20 | quoteRetweets: { 21 | selector: 'a[href*="retweets"] span span span:nth(1)', 22 | attr: 'text' 23 | }, 24 | likes: { 25 | selector: 'a[href*="likes"] span span span', 26 | attr: 'text' 27 | }, 28 | tweetDate: { 29 | selector: 'a[href*="status"] time', 30 | type: 'text' 31 | }, 32 | tweets: { 33 | selector: 'div > div > div > div h2 + div' 34 | }, 35 | followings: { 36 | selector: 'a[href*="following"] span span' 37 | }, 38 | followers: { 39 | selector: 'a[href*="followers"] span span' 40 | } 41 | } 42 | }, 43 | latestTweets: { 44 | selectorAll: 'main article', 45 | attr: { 46 | content: { 47 | selector: 'div[lang]', 48 | attr: 'text' 49 | }, 50 | link: { 51 | selector: 'a', 52 | attr: 'href' 53 | } 54 | } 55 | } 56 | }, 57 | ...opts 58 | }) 59 | 60 | if (result.data.stats.tweets) { 61 | result.data.stats.tweets = Number( 62 | result.data.stats.tweets.replace(' Tweets', '') 63 | ) 64 | } 65 | 66 | return result 67 | } 68 | 69 | module.exports.meta = { 70 | name: 'Twitter', 71 | examples: [ 72 | 'https://twitter.com/microlinkhq', 73 | 'https://twitter.com/AREdotNA/status/1424776632695414786' 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /recipes/by-provider/wipoid.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = (url, opts) => 6 | mql(url, { 7 | data: { 8 | price: { 9 | selector: '#our_price_display', 10 | attr: 'content', 11 | type: 'number' 12 | } 13 | }, 14 | ...opts 15 | }) 16 | 17 | module.exports.meta = { 18 | name: 'Wipoid', 19 | examples: ['https://www.wipoid.com/msi-mag-b550m-mortar-wifi.html'] 20 | } 21 | -------------------------------------------------------------------------------- /recipes/by-provider/youtube.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mql = require('@microlink/mql') 4 | 5 | module.exports = async (url, opts) => { 6 | const result = await mql(url, { 7 | prerender: true, 8 | video: true, 9 | audio: true, 10 | data: { 11 | views: { 12 | selector: '.view-count', 13 | type: 'number' 14 | } 15 | }, 16 | ...opts 17 | }) 18 | 19 | return result 20 | } 21 | 22 | module.exports.meta = { 23 | name: 'YouTube', 24 | examples: ['https://www.youtube.com/watch?v=dQw4w9WgXcQ'] 25 | } 26 | -------------------------------------------------------------------------------- /recipes/by-provider/zalando.js: -------------------------------------------------------------------------------- 1 | const mql = require('@microlink/mql') 2 | 3 | module.exports = (url, opts) => 4 | mql(url, { 5 | data: { 6 | price: { 7 | selector: 'meta[name="twitter:data1"]', 8 | attr: 'content' 9 | }, 10 | color: { 11 | selector: 'meta[name="twitter:data2"]', 12 | attr: 'content' 13 | } 14 | }, 15 | ...opts 16 | }) 17 | 18 | module.exports.meta = { 19 | name: 'Zalando', 20 | examples: [ 21 | 'https://www.zalando.es/jordan-air-jordan-1-mid-zapatillas-altas-joc12n001-g11.html' 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /test/by-feature.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('ava') 4 | const { default: ow } = require('ow') 5 | 6 | const { 7 | debugCss, 8 | emails, 9 | embed, 10 | excerpt, 11 | favicon, 12 | fullScreenshot, 13 | headings, 14 | healthcheck, 15 | html, 16 | images, 17 | jsonLd, 18 | lighthouse, 19 | pdf, 20 | screenshot, 21 | technologyStack, 22 | text, 23 | uris, 24 | youtubeDl 25 | } = require('../by-feature') 26 | 27 | const apiKey = process.env.MICROLINK_API_KEY 28 | 29 | for (const url of embed.meta.examples) { 30 | test(`.embed for ${url}`, async t => { 31 | const iframe = await embed(url, { apiKey }) 32 | t.true(ow.isValid(iframe, ow.object.not.empty)) 33 | t.true(ow.isValid(iframe.html, ow.string.not.empty)) 34 | t.true(iframe.html.includes('width="350"'), url) 35 | }) 36 | } 37 | 38 | test('.technologyStack', async t => { 39 | const technologies = await technologyStack(technologyStack.meta.examples[0], { 40 | apiKey 41 | }) 42 | t.true(ow.isValid(technologies, ow.object.not.empty)) 43 | }) 44 | 45 | test('.lighthouse', async t => { 46 | const report = await lighthouse(lighthouse.meta.examples[0], { 47 | apiKey 48 | }) 49 | t.true(ow.isValid(report, ow.object.not.empty)) 50 | }) 51 | 52 | test('.html', async t => { 53 | const renderedHtml = await html(html.meta.examples[0], { apiKey }) 54 | t.true(renderedHtml.startsWith(' { 58 | const { value } = await excerpt(excerpt.meta.examples[0], { apiKey }) 59 | t.true(ow.isValid(value, ow.string.not.empty)) 60 | }) 61 | 62 | test('.emails', async t => { 63 | const allEmails = await emails(emails.meta.examples[0], { apiKey }) 64 | t.true(ow.isValid(allEmails, ow.array.not.empty)) 65 | }) 66 | 67 | test('.favicon', async t => { 68 | const faviconUrl = await favicon(favicon.meta.examples[0], { apiKey }) 69 | t.true(!!faviconUrl) 70 | }) 71 | 72 | test('.images', async t => { 73 | const allImages = await images(images.meta.examples[0], { apiKey }) 74 | 75 | allImages.forEach(image => { 76 | t.true(ow.isValid(image.url, ow.string.not.empty)) 77 | t.true(ow.isValid(image.type, ow.string.not.empty)) 78 | t.true(ow.isValid(image.size, ow.number.finite)) 79 | t.true(ow.isValid(image.size_pretty, ow.string.not.empty)) 80 | }) 81 | }) 82 | 83 | test('.headings', async t => { 84 | const { url, ...allHeaders } = await headings(headings.meta.examples[0], { 85 | apiKey 86 | }) 87 | 88 | Object.entries(allHeaders) 89 | .filter(([, values]) => Array.isArray(values)) 90 | .forEach(([, values]) => { 91 | t.true(ow.isValid(values, ow.array.not.empty)) 92 | }) 93 | }) 94 | 95 | test('.pdf', async t => { 96 | const asset = await pdf(pdf.meta.examples[0], { 97 | apiKey 98 | }) 99 | t.true(ow.isValid(asset, ow.object.not.empty)) 100 | }) 101 | 102 | test('.screenshot', async t => { 103 | const image = await screenshot(screenshot.meta.examples[0], { 104 | apiKey 105 | }) 106 | t.true(ow.isValid(image, ow.object.not.empty)) 107 | }) 108 | 109 | test('.fullScreenshot', async t => { 110 | const image = await fullScreenshot(fullScreenshot.meta.examples[0], { 111 | apiKey 112 | }) 113 | t.true(ow.isValid(image, ow.object.not.empty)) 114 | }) 115 | 116 | test('.debugCss', async t => { 117 | const screenshot = await debugCss(debugCss.meta.examples[0], { apiKey }) 118 | t.true(ow.isValid(screenshot, ow.object.not.empty)) 119 | }) 120 | 121 | test('.jsonLd', async t => { 122 | const data = await jsonLd(jsonLd.meta.examples[0], { apiKey }) 123 | t.true(ow.isValid(data, ow.array.not.empty)) 124 | }) 125 | 126 | test('.healthcheck', async t => { 127 | const { value } = await healthcheck(healthcheck.meta.examples[0], { apiKey }) 128 | t.true(ow.isValid(value.url, ow.string.not.empty)) 129 | t.true(ow.isValid(value.statusCode, ow.number.finite)) 130 | t.true(ow.isValid(value.headers, ow.object.not.empty)) 131 | t.true(ow.isValid(value.html, ow.string.not.empty)) 132 | }) 133 | 134 | test('.youtubeDl', async t => { 135 | const { value } = await youtubeDl(youtubeDl.meta.examples[0], { apiKey }) 136 | t.true(ow.isValid(value, ow.object.not.empty)) 137 | }) 138 | 139 | test('.uris', async t => { 140 | const { value } = await uris(uris.meta.examples[0], { apiKey }) 141 | t.true(ow.isValid(value, ow.array.not.empty)) 142 | }) 143 | 144 | test('.text', async t => { 145 | const { value } = await text(text.meta.examples[0], { apiKey }) 146 | t.true(ow.isValid(value, ow.string.not.empty)) 147 | }) 148 | -------------------------------------------------------------------------------- /test/by-provider.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { default: ow } = require('ow') 4 | const test = require('ava') 5 | 6 | const { 7 | amazon, 8 | assc, 9 | balenciaga, 10 | betalist, 11 | canopy, 12 | codepen, 13 | fca, 14 | github, 15 | google, 16 | hackerNews, 17 | imdb, 18 | instagram, 19 | meetup, 20 | parcel, 21 | producthunt, 22 | reddit, 23 | ripndip, 24 | soundcloud, 25 | spotify, 26 | telegram, 27 | tiktok, 28 | twitter, 29 | wipoid, 30 | youtube, 31 | zalando 32 | } = require('../by-provider') 33 | 34 | const apiKey = process.env.MICROLINK_API_KEY 35 | 36 | test('betalist', async t => { 37 | const { data } = await betalist(betalist.meta.examples[0], { apiKey }) 38 | 39 | data.startups.forEach(startup => { 40 | t.true(ow.isValid(startup.name, ow.string.not.empty)) 41 | t.true(ow.isValid(startup.url, ow.string.not.empty)) 42 | t.true(ow.isValid(startup.description, ow.string.not.empty)) 43 | t.true(ow.isValid(startup.image, ow.string.not.empty)) 44 | }) 45 | }) 46 | 47 | test('twitter', async t => { 48 | { 49 | const { data } = await twitter(twitter.meta.examples[1], { apiKey }) 50 | 51 | data.latestTweets.forEach(tweet => { 52 | t.true(ow.isValid(tweet.content, ow.string.not.empty)) 53 | t.true(ow.isValid(tweet.link, ow.string.not.empty)) 54 | }) 55 | 56 | t.true(ow.isValid(data.stats.followers, ow.number.finite)) 57 | t.true(ow.isValid(data.stats.followings, ow.number.finite)) 58 | t.true(ow.isValid(data.stats.tweets, ow.number.finite)) 59 | } 60 | { 61 | const { data } = await twitter(twitter.meta.examples[1], { apiKey }) 62 | 63 | t.true(ow.isValid(data.stats.retweets, ow.number.finite)) 64 | t.true(ow.isValid(data.stats.quoteRetweets, ow.number.finite)) 65 | t.true(ow.isValid(data.stats.likes, ow.number.finite)) 66 | t.true(ow.isValid(data.stats.tweetDate, ow.string.not.empty)) 67 | } 68 | }) 69 | 70 | test('producthunt', async t => { 71 | const { data } = await producthunt(producthunt.meta.examples[0], { apiKey }) 72 | 73 | t.true(ow.isValid(data.name, ow.string.not.empty)) 74 | t.true(ow.isValid(data.upvotes, ow.string.not.empty)) 75 | }) 76 | 77 | test('wipoid', async t => { 78 | const { data } = await wipoid(wipoid.meta.examples[0], { apiKey }) 79 | 80 | t.true(ow.isValid(data.price, ow.number.finite)) 81 | }) 82 | 83 | test('meetup', async t => { 84 | const { data } = await meetup(meetup.meta.examples[0], { apiKey }) 85 | t.true(ow.isValid(data.members, ow.string.not.empty)) 86 | 87 | data.pastEvents.forEach(post => { 88 | t.true(ow.isValid(post.attendees, ow.string.not.empty)) 89 | t.true(ow.isValid(post.link, ow.string.not.empty)) 90 | t.true(ow.isValid(post.title, ow.string.not.empty)) 91 | }) 92 | }) 93 | 94 | test('instagram', async t => { 95 | const { data } = await instagram(instagram.meta.examples[0], { apiKey }) 96 | t.true(ow.isValid(data.avatar, ow.object.not.empty)) 97 | t.true(ow.isValid(data.stats, ow.object.not.empty)) 98 | }) 99 | 100 | test('hacker-news', async t => { 101 | const { data } = await hackerNews({ apiKey }) 102 | 103 | data.posts.forEach(post => { 104 | t.true(ow.isValid(post.title, ow.string.not.empty)) 105 | t.true(ow.isValid(post.url, ow.string.not.empty)) 106 | }) 107 | }) 108 | 109 | test('github', async t => { 110 | const { data } = await github(github.meta.examples[0], { apiKey }) 111 | 112 | t.true(ow.isValid(data.stats.followers, ow.number.finite)) 113 | t.true(ow.isValid(data.stats.following, ow.number.finite)) 114 | t.true(ow.isValid(data.stats.stars, ow.number.finite)) 115 | }) 116 | 117 | test('google', async t => { 118 | const { data } = await google(google.meta.examples[0], { apiKey }) 119 | 120 | data.results.forEach(result => { 121 | t.true(ow.isValid(result.title, ow.string.not.empty)) 122 | t.true(ow.isValid(result.description, ow.string.not.empty)) 123 | t.true(ow.isValid(result.breadcumb, ow.string.not.empty)) 124 | t.true(ow.isValid(result.link, ow.string.not.empty)) 125 | }) 126 | }) 127 | 128 | test('imdb', async t => { 129 | const { data } = await imdb(imdb.meta.examples[0], { apiKey }) 130 | 131 | t.true(ow.isValid(data.director, ow.string.not.empty)) 132 | t.true(ow.isValid(data.writer, ow.string.not.empty)) 133 | t.true(ow.isValid(data.duration, ow.string.not.empty)) 134 | t.true(ow.isValid(data.year, ow.number.finite)) 135 | t.true(ow.isValid(data.rating, ow.string.not.empty)) 136 | t.true(ow.isValid(data.ratingCount, ow.string.not.empty)) 137 | }) 138 | 139 | test('fca', async t => { 140 | const { data } = await fca(fca.meta.examples[0], { apiKey }) 141 | 142 | t.true(ow.isValid(data.url, ow.string.not.empty)) 143 | t.true(ow.isValid(data.name, ow.string.not.empty)) 144 | t.true(ow.isValid(data.updatedAt, ow.string.not.empty)) 145 | t.true(ow.isValid(data.address, ow.string.not.empty)) 146 | t.true(ow.isValid(data.firmReferenceNumber, ow.number.finite)) 147 | t.true( 148 | ow.isValid(data.firmReferenceNumber.toString(), ow.string.minLength(6)) 149 | ) 150 | t.true(ow.isValid(data.registeredCompanyNumber, ow.number.finite)) 151 | t.true( 152 | ow.isValid(data.registeredCompanyNumber.toString(), ow.string.minLength(6)) 153 | ) 154 | t.true(ow.isValid(data.email, ow.string.not.empty)) 155 | t.true(ow.isValid(data.phone, ow.string.not.empty)) 156 | }) 157 | 158 | test('canopy', async t => { 159 | const { data } = await canopy(canopy.meta.examples[0], { apiKey }) 160 | 161 | data.products.forEach(result => { 162 | t.true(ow.isValid(result.title, ow.string.not.empty)) 163 | t.true(ow.isValid(result.url, ow.string.not.empty)) 164 | t.true(ow.isValid(result.amazonUrl, ow.string.not.empty)) 165 | t.true(ow.isValid(result.image, ow.string.not.empty)) 166 | }) 167 | }) 168 | 169 | test('codepen', async t => { 170 | const screenshotUrl = await codepen(codepen.meta.examples[0], { apiKey }) 171 | t.true(ow.isValid(screenshotUrl, ow.string.not.empty)) 172 | }) 173 | 174 | test('telegram', async t => { 175 | const { data } = await telegram(telegram.meta.examples[0], { apiKey }) 176 | t.true(ow.isValid(data.subscribers, ow.number.finite)) 177 | t.true(ow.isValid(data.photos, ow.number.finite)) 178 | t.true(ow.isValid(data.links, ow.string.not.empty)) 179 | }) 180 | 181 | test('reddit', async t => { 182 | const { data } = await reddit(reddit.meta.examples[0], { apiKey }) 183 | t.true(ow.isValid(data.karma, ow.number.finite)) 184 | t.true(ow.isValid(data.birthday, ow.string.not.empty)) 185 | t.true(ow.isValid(data.avatar, ow.object.not.empty)) 186 | }) 187 | 188 | test('youtube', async t => { 189 | const { data } = await youtube(youtube.meta.examples[0], { apiKey }) 190 | t.true(ow.isValid(data.views, ow.number.finite)) 191 | t.true(ow.isValid(data.video, ow.object.not.empty)) 192 | t.true(ow.isValid(data.audio, ow.object.not.empty)) 193 | }) 194 | 195 | test('soundcloud', async t => { 196 | const { data } = await soundcloud(soundcloud.meta.examples[0], { apiKey }) 197 | t.true(ow.isValid(data.plays, ow.number.finite)) 198 | t.true(ow.isValid(data.audio, ow.object.not.empty)) 199 | }) 200 | 201 | test('spotify', async t => { 202 | const { data } = await spotify(spotify.meta.examples[0], { apiKey }) 203 | t.true(ow.isValid(data.audio, ow.object.not.empty)) 204 | }) 205 | 206 | test('balenciaga', async t => { 207 | const { data } = await balenciaga(balenciaga.meta.examples[0], { apiKey }) 208 | t.true(ow.isValid(data.price, ow.number.finite)) 209 | t.true(ow.isValid(data.currency, ow.string.not.empty)) 210 | }) 211 | 212 | test('assc', async t => { 213 | const { data } = await assc(assc.meta.examples[0], { apiKey }) 214 | t.true(ow.isValid(data.price, ow.string.not.empty)) 215 | }) 216 | 217 | test('ripndip', async t => { 218 | const { data } = await ripndip(ripndip.meta.examples[0], { apiKey }) 219 | t.true(ow.isValid(data.price, ow.string.not.empty)) 220 | }) 221 | 222 | test('zalando', async t => { 223 | const { data } = await zalando(zalando.meta.examples[0], { apiKey }) 224 | t.true(ow.isValid(data.price, ow.string.not.empty)) 225 | t.true(ow.isValid(data.color, ow.string.not.empty)) 226 | }) 227 | 228 | test('amazon', async t => { 229 | const { data } = await amazon(amazon.meta.examples[0], { apiKey }) 230 | t.true(ow.isValid(data.price, ow.number.finite)) 231 | t.true(ow.isValid(data.currency, ow.string.not.empty)) 232 | }) 233 | 234 | test('tiktok', async t => { 235 | const { data } = await tiktok(tiktok.meta.examples[0], { apiKey }) 236 | t.true(ow.isValid(data.song, ow.string.not.empty)) 237 | t.true(ow.isValid(data.likeCount, ow.string.not.empty)) 238 | t.true(ow.isValid(data.commentCount, ow.string.not.empty)) 239 | t.true(ow.isValid(data.shareCount, ow.string.not.empty)) 240 | }) 241 | 242 | test('parcel', async t => { 243 | const screenshotUrl = await parcel(parcel.meta.examples[0], { apiKey }) 244 | t.true(ow.isValid(screenshotUrl, ow.string.not.empty)) 245 | }) 246 | --------------------------------------------------------------------------------