├── .env ├── .github ├── node_modules │ ├── data-uri-to-buffer │ │ ├── README.md │ │ ├── dist │ │ │ └── src │ │ │ │ ├── index.d.ts │ │ │ │ ├── index.js │ │ │ │ └── index.js.map │ │ └── package.json │ ├── fetch-blob │ │ ├── LICENSE │ │ ├── README.md │ │ ├── file.d.ts │ │ ├── file.js │ │ ├── from.d.ts │ │ ├── from.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ └── streams.cjs │ ├── node-fetch │ │ ├── @types │ │ │ └── index.d.ts │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── package.json │ │ └── src │ │ │ ├── body.js │ │ │ ├── errors │ │ │ ├── abort-error.js │ │ │ ├── base.js │ │ │ └── fetch-error.js │ │ │ ├── headers.js │ │ │ ├── index.js │ │ │ ├── request.js │ │ │ ├── response.js │ │ │ └── utils │ │ │ ├── form-data.js │ │ │ ├── get-search.js │ │ │ ├── is-redirect.js │ │ │ └── is.js │ └── web-streams-polyfill │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── dist │ │ ├── polyfill.es2018.js │ │ ├── polyfill.es2018.js.map │ │ ├── polyfill.es2018.min.js │ │ ├── polyfill.es2018.min.js.map │ │ ├── polyfill.es2018.mjs │ │ ├── polyfill.es2018.mjs.map │ │ ├── polyfill.es6.js │ │ ├── polyfill.es6.js.map │ │ ├── polyfill.es6.min.js │ │ ├── polyfill.es6.min.js.map │ │ ├── polyfill.es6.mjs │ │ ├── polyfill.es6.mjs.map │ │ ├── polyfill.js │ │ ├── polyfill.js.map │ │ ├── polyfill.min.js │ │ ├── polyfill.min.js.map │ │ ├── polyfill.mjs │ │ ├── polyfill.mjs.map │ │ ├── ponyfill.es2018.js │ │ ├── ponyfill.es2018.js.map │ │ ├── ponyfill.es2018.mjs │ │ ├── ponyfill.es2018.mjs.map │ │ ├── ponyfill.es6.js │ │ ├── ponyfill.es6.js.map │ │ ├── ponyfill.es6.mjs │ │ ├── ponyfill.es6.mjs.map │ │ ├── ponyfill.js │ │ ├── ponyfill.js.map │ │ ├── ponyfill.mjs │ │ ├── ponyfill.mjs.map │ │ └── types │ │ │ ├── polyfill.d.ts │ │ │ ├── ts3.6 │ │ │ └── polyfill.d.ts │ │ │ └── tsdoc-metadata.json │ │ ├── es2018 │ │ └── package.json │ │ ├── es6 │ │ └── package.json │ │ ├── package.json │ │ └── ponyfill │ │ ├── es2018 │ │ └── package.json │ │ ├── es6 │ │ └── package.json │ │ └── package.json ├── update-sponsors │ ├── fetch-sponsors.mjs │ └── index.mjs └── workflows │ └── update-sponsors.yml ├── .gitignore ├── .gitpod.yml ├── .prettierrc ├── data ├── demos │ ├── code.mdx │ ├── comment-annotations.mdx │ ├── custom-annotations.mdx │ ├── custom-annotations │ │ └── MyThing.jsx │ ├── filenames.mdx │ ├── index.json │ ├── meta-annotations.mdx │ ├── old-show.mdx │ ├── scrollycoding-preview.mdx │ ├── scrollycoding.mdx │ ├── sections.mdx │ ├── show.mdx │ ├── slideshow-preview.mdx │ ├── slideshow.mdx │ ├── spotlight-preview.mdx │ └── spotlight.mdx └── sponsors.json ├── docs ├── annotations.mdx ├── ch-code.mdx ├── ch-scrollycoding.mdx ├── ch-section.mdx ├── ch-slideshow.mdx ├── ch-spotlight.mdx ├── codeblocks.mdx ├── configuration.mdx ├── configuration │ ├── line-numbers.mdx │ └── theme.mdx ├── installation-astro.mdx ├── installation-contentlayer.mdx ├── installation-cra.mdx ├── installation-docspage.mdx ├── installation-docusaurus.mdx ├── installation-eleventy.mdx ├── installation-gatsby.mdx ├── installation-mdx-bundler.mdx ├── installation-motif.mdx ├── installation-next-mdx-remote.mdx ├── installation-nextjs.mdx ├── installation-nextra.mdx ├── installation-parcel.mdx ├── installation-remix.mdx ├── installation-vite.mdx ├── installation.mdx ├── introduction.mdx ├── preview │ ├── ch-code-1.mdx │ ├── ch-code-2.mdx │ ├── ch-code-3.mdx │ ├── ch-code-4.mdx │ ├── ch-code-5.mdx │ ├── ch-code-6.mdx │ ├── ch-section-1.mdx │ ├── ch-section-2.mdx │ ├── ch-section-3.mdx │ ├── codeblocks-1.mdx │ ├── codeblocks-2.mdx │ ├── installation-1.mdx │ ├── mark-1.mdx │ ├── mark-2.mdx │ ├── mark-3.mdx │ └── with-class.mdx ├── styling.mdx ├── themes.mdx └── troubleshooting.mdx ├── global.css ├── next.config.js ├── package.json ├── pages ├── _app.js ├── _document.js ├── api │ ├── auth │ │ └── [...nextauth].js │ └── update-sponsors.js ├── card.js ├── demo │ └── [...slug].js ├── docs │ ├── [slug].js │ ├── configuration.js │ ├── installation │ │ └── [fwk].js │ ├── preview │ │ └── [preview].js │ └── themes.js ├── index.js ├── logo.js └── show.js ├── postcss.config.js ├── public ├── card.png ├── cards │ ├── card.png │ ├── contentlayer.png │ ├── docusaurus.png │ ├── gatsby.png │ └── nextjs.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── logo │ ├── astro.svg │ ├── contentlayer.svg │ ├── cra.svg │ ├── docspage.svg │ ├── docusaurus.svg │ ├── eleventy.svg │ ├── gatsby.svg │ ├── github.svg │ ├── mdx-bundler.svg │ ├── motif.svg │ ├── next-mdx-remote.svg │ ├── nextjs.svg │ ├── nextra.svg │ ├── parcel.svg │ ├── remix.svg │ ├── vite.svg │ └── webpack.svg ├── not-stripe.png ├── not-tailwind.png └── show1.mp4 ├── readme.md ├── src ├── ch-theme.js ├── collapsable.js ├── demo-grid.js ├── docs-layout.js ├── docs-mdx.js ├── footer.js ├── frameworks.js ├── home-demo-server.js ├── home-demo.js ├── logo.js └── seo.js ├── tailwind.config.js ├── themes.css └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | GITHUB_TOKEN= used to get the list of sponsors (deprecated, this is done by a GH action now) 2 | GITHUB_ID= used to authenticate website visitors 3 | GITHUB_SECRET= used to authenticate website visitors 4 | TRIGGER_ACTION_PAT= GH PAT used to trigger update sponsors workflow on ch-site 5 | NEXTAUTH_URL= NextAuth.js url (https://next-auth.js.org/warnings#nextauth_url) 6 | JWT_SIGNING_PRIVATE_KEY= NextAuth.js JWT key (https://next-auth.js.org/warnings#jwt_auto_generated_signing_key) -------------------------------------------------------------------------------- /.github/node_modules/data-uri-to-buffer/README.md: -------------------------------------------------------------------------------- 1 | data-uri-to-buffer 2 | ================== 3 | ### Generate a Buffer instance from a [Data URI][rfc] string 4 | [![Build Status](https://travis-ci.org/TooTallNate/node-data-uri-to-buffer.svg?branch=master)](https://travis-ci.org/TooTallNate/node-data-uri-to-buffer) 5 | 6 | This module accepts a ["data" URI][rfc] String of data, and returns a 7 | node.js `Buffer` instance with the decoded data. 8 | 9 | 10 | Installation 11 | ------------ 12 | 13 | Install with `npm`: 14 | 15 | ``` bash 16 | $ npm install data-uri-to-buffer 17 | ``` 18 | 19 | 20 | Example 21 | ------- 22 | 23 | ``` js 24 | var dataUriToBuffer = require('data-uri-to-buffer'); 25 | 26 | // plain-text data is supported 27 | var uri = 'data:,Hello%2C%20World!'; 28 | var decoded = dataUriToBuffer(uri); 29 | console.log(decoded.toString()); 30 | // 'Hello, World!' 31 | 32 | // base64-encoded data is supported 33 | uri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D'; 34 | decoded = dataUriToBuffer(uri); 35 | console.log(decoded.toString()); 36 | // 'Hello, World!' 37 | ``` 38 | 39 | 40 | API 41 | --- 42 | 43 | ### dataUriToBuffer(String uri) → Buffer 44 | 45 | The `type` property on the Buffer instance gets set to the main type portion of 46 | the "mediatype" portion of the "data" URI, or defaults to `"text/plain"` if not 47 | specified. 48 | 49 | The `typeFull` property on the Buffer instance gets set to the entire 50 | "mediatype" portion of the "data" URI (including all parameters), or defaults 51 | to `"text/plain;charset=US-ASCII"` if not specified. 52 | 53 | The `charset` property on the Buffer instance gets set to the Charset portion of 54 | the "mediatype" portion of the "data" URI, or defaults to `"US-ASCII"` if the 55 | entire type is not specified, or defaults to `""` otherwise. 56 | 57 | *Note*: If the only the main type is specified but not the charset, e.g. 58 | `"data:text/plain,abc"`, the charset is set to the empty string. The spec only 59 | defaults to US-ASCII as charset if the entire type is not specified. 60 | 61 | 62 | License 63 | ------- 64 | 65 | (The MIT License) 66 | 67 | Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net> 68 | 69 | Permission is hereby granted, free of charge, to any person obtaining 70 | a copy of this software and associated documentation files (the 71 | 'Software'), to deal in the Software without restriction, including 72 | without limitation the rights to use, copy, modify, merge, publish, 73 | distribute, sublicense, and/or sell copies of the Software, and to 74 | permit persons to whom the Software is furnished to do so, subject to 75 | the following conditions: 76 | 77 | The above copyright notice and this permission notice shall be 78 | included in all copies or substantial portions of the Software. 79 | 80 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 81 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 82 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 83 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 84 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 85 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 86 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 87 | 88 | [rfc]: http://tools.ietf.org/html/rfc2397 89 | -------------------------------------------------------------------------------- /.github/node_modules/data-uri-to-buffer/dist/src/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns a `Buffer` instance from the given data URI `uri`. 3 | * 4 | * @param {String} uri Data URI to turn into a Buffer instance 5 | * @return {Buffer} Buffer instance from Data URI 6 | * @api public 7 | */ 8 | /// 9 | declare function dataUriToBuffer(uri: string): dataUriToBuffer.MimeBuffer; 10 | declare namespace dataUriToBuffer { 11 | interface MimeBuffer extends Buffer { 12 | type: string; 13 | typeFull: string; 14 | charset: string; 15 | } 16 | } 17 | export = dataUriToBuffer; 18 | -------------------------------------------------------------------------------- /.github/node_modules/data-uri-to-buffer/dist/src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Returns a `Buffer` instance from the given data URI `uri`. 4 | * 5 | * @param {String} uri Data URI to turn into a Buffer instance 6 | * @return {Buffer} Buffer instance from Data URI 7 | * @api public 8 | */ 9 | function dataUriToBuffer(uri) { 10 | if (!/^data:/i.test(uri)) { 11 | throw new TypeError('`uri` does not appear to be a Data URI (must begin with "data:")'); 12 | } 13 | // strip newlines 14 | uri = uri.replace(/\r?\n/g, ''); 15 | // split the URI up into the "metadata" and the "data" portions 16 | const firstComma = uri.indexOf(','); 17 | if (firstComma === -1 || firstComma <= 4) { 18 | throw new TypeError('malformed data: URI'); 19 | } 20 | // remove the "data:" scheme and parse the metadata 21 | const meta = uri.substring(5, firstComma).split(';'); 22 | let charset = ''; 23 | let base64 = false; 24 | const type = meta[0] || 'text/plain'; 25 | let typeFull = type; 26 | for (let i = 1; i < meta.length; i++) { 27 | if (meta[i] === 'base64') { 28 | base64 = true; 29 | } 30 | else { 31 | typeFull += `;${meta[i]}`; 32 | if (meta[i].indexOf('charset=') === 0) { 33 | charset = meta[i].substring(8); 34 | } 35 | } 36 | } 37 | // defaults to US-ASCII only if type is not provided 38 | if (!meta[0] && !charset.length) { 39 | typeFull += ';charset=US-ASCII'; 40 | charset = 'US-ASCII'; 41 | } 42 | // get the encoded data portion and decode URI-encoded chars 43 | const encoding = base64 ? 'base64' : 'ascii'; 44 | const data = unescape(uri.substring(firstComma + 1)); 45 | const buffer = Buffer.from(data, encoding); 46 | // set `.type` and `.typeFull` properties to MIME type 47 | buffer.type = type; 48 | buffer.typeFull = typeFull; 49 | // set the `.charset` property 50 | buffer.charset = charset; 51 | return buffer; 52 | } 53 | module.exports = dataUriToBuffer; 54 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /.github/node_modules/data-uri-to-buffer/dist/src/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;AAEH,SAAS,eAAe,CAAC,GAAW;IACnC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QACzB,MAAM,IAAI,SAAS,CAClB,kEAAkE,CAClE,CAAC;KACF;IAED,iBAAiB;IACjB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEhC,+DAA+D;IAC/D,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,UAAU,KAAK,CAAC,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE;QACzC,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;KAC3C;IAED,mDAAmD;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAErD,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC;IACrC,IAAI,QAAQ,GAAG,IAAI,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE;YACzB,MAAM,GAAG,IAAI,CAAC;SACd;aAAM;YACN,QAAQ,IAAI,IAAM,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;gBACtC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;aAC/B;SACD;KACD;IACD,oDAAoD;IACpD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;QAChC,QAAQ,IAAI,mBAAmB,CAAC;QAChC,OAAO,GAAG,UAAU,CAAC;KACrB;IAED,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;IAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAA+B,CAAC;IAEzE,sDAAsD;IACtD,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAE3B,8BAA8B;IAC9B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IAEzB,OAAO,MAAM,CAAC;AACf,CAAC;AAUD,iBAAS,eAAe,CAAC"} -------------------------------------------------------------------------------- /.github/node_modules/data-uri-to-buffer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "data-uri-to-buffer@^3.0.1", 3 | "_id": "data-uri-to-buffer@3.0.1", 4 | "_inBundle": false, 5 | "_integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", 6 | "_location": "/data-uri-to-buffer", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "range", 10 | "registry": true, 11 | "raw": "data-uri-to-buffer@^3.0.1", 12 | "name": "data-uri-to-buffer", 13 | "escapedName": "data-uri-to-buffer", 14 | "rawSpec": "^3.0.1", 15 | "saveSpec": null, 16 | "fetchSpec": "^3.0.1" 17 | }, 18 | "_requiredBy": [ 19 | "/node-fetch" 20 | ], 21 | "_resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", 22 | "_shasum": "594b8973938c5bc2c33046535785341abc4f3636", 23 | "_spec": "data-uri-to-buffer@^3.0.1", 24 | "_where": "/home/pomber/p/temp/x/node_modules/node-fetch", 25 | "author": { 26 | "name": "Nathan Rajlich", 27 | "email": "nathan@tootallnate.net", 28 | "url": "http://n8.io/" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/TooTallNate/node-data-uri-to-buffer/issues" 32 | }, 33 | "bundleDependencies": false, 34 | "deprecated": false, 35 | "description": "Generate a Buffer instance from a Data URI string", 36 | "devDependencies": { 37 | "@types/es6-promisify": "^5.0.0", 38 | "@types/mocha": "^5.2.7", 39 | "@types/node": "^10.5.3", 40 | "@typescript-eslint/eslint-plugin": "1.6.0", 41 | "@typescript-eslint/parser": "1.1.0", 42 | "eslint": "5.16.0", 43 | "eslint-config-airbnb": "17.1.0", 44 | "eslint-config-prettier": "4.1.0", 45 | "eslint-import-resolver-typescript": "1.1.1", 46 | "eslint-plugin-import": "2.16.0", 47 | "eslint-plugin-jsx-a11y": "6.2.1", 48 | "eslint-plugin-react": "7.12.4", 49 | "mocha": "^6.2.0", 50 | "typescript": "^3.5.3" 51 | }, 52 | "engines": { 53 | "node": ">= 6" 54 | }, 55 | "files": [ 56 | "dist/src" 57 | ], 58 | "homepage": "https://github.com/TooTallNate/node-data-uri-to-buffer", 59 | "keywords": [ 60 | "data", 61 | "uri", 62 | "datauri", 63 | "data-uri", 64 | "buffer", 65 | "convert", 66 | "rfc2397", 67 | "2397" 68 | ], 69 | "license": "MIT", 70 | "main": "dist/src/index.js", 71 | "name": "data-uri-to-buffer", 72 | "repository": { 73 | "type": "git", 74 | "url": "git://github.com/TooTallNate/node-data-uri-to-buffer.git" 75 | }, 76 | "scripts": { 77 | "build": "tsc", 78 | "prepublishOnly": "npm run build", 79 | "test": "mocha --reporter spec dist/test/*.js", 80 | "test-lint": "eslint src --ext .js,.ts" 81 | }, 82 | "types": "dist/src/index.d.ts", 83 | "version": "3.0.1" 84 | } 85 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 David Frank 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/README.md: -------------------------------------------------------------------------------- 1 | # fetch-blob 2 | 3 | [![npm version][npm-image]][npm-url] 4 | [![build status][ci-image]][ci-url] 5 | [![coverage status][codecov-image]][codecov-url] 6 | [![install size][install-size-image]][install-size-url] 7 | 8 | A Blob implementation in Node.js, originally from [node-fetch](https://github.com/node-fetch/node-fetch). 9 | 10 | ## Installation 11 | 12 | ```sh 13 | npm install fetch-blob 14 | ``` 15 | 16 |
17 | Upgrading from 2x to 3x 18 | 19 | Updating from 2 to 3 should be a breeze since there is not many changes to the blob specification. 20 | The major cause of a major release is coding standards. 21 | - internal WeakMaps was replaced with private fields 22 | - internal Buffer.from was replaced with TextEncoder/Decoder 23 | - internal buffers was replaced with Uint8Arrays 24 | - CommonJS was replaced with ESM 25 | - The node stream returned by calling `blob.stream()` was replaced with whatwg streams 26 | - (Read "Differences from other blobs" for more info.) 27 | 28 |
29 | 30 |
31 | Differences from other Blobs 32 | 33 | - Unlike NodeJS `buffer.Blob` (Added in: v15.7.0) and browser native Blob this polyfilled version can't be sent via PostMessage 34 | - This blob version is more arbitrary, it can be constructed with blob parts that isn't a instance of itself 35 | it has to look and behave as a blob to be accepted as a blob part. 36 | - The benefit of this is that you can create other types of blobs that don't contain any internal data that has to be read in other ways, such as the `BlobDataItem` created in `from.js` that wraps a file path into a blob-like item and read lazily (nodejs plans to [implement this][fs-blobs] as well) 37 | - The `blob.stream()` is the most noticeable differences. It returns a WHATWG stream now. to keep it as a node stream you would have to do: 38 | 39 | ```js 40 | import {Readable} from 'stream' 41 | const stream = Readable.from(blob.stream()) 42 | ``` 43 |
44 | 45 | ## Usage 46 | 47 | ```js 48 | // Ways to import 49 | // (PS it's dependency free ESM package so regular http-import from CDN works too) 50 | import Blob from 'fetch-blob' 51 | import File from 'fetch-blob/file.js' 52 | 53 | import {Blob} from 'fetch-blob' 54 | import {File} from 'fetch-blob/file.js' 55 | 56 | const {Blob} = await import('fetch-blob') 57 | 58 | 59 | // Ways to read the blob: 60 | const blob = new Blob(['hello, world']) 61 | 62 | await blob.text() 63 | await blob.arrayBuffer() 64 | for await (let chunk of blob.stream()) { ... } 65 | blob.stream().getReader().read() 66 | blob.stream().getReader({mode: 'byob'}).read(view) 67 | ``` 68 | 69 | ### Blob part backed up by filesystem 70 | 71 | `fetch-blob/from.js` comes packed with tools to convert any filepath into either a Blob or a File 72 | It will not read the content into memory. It will only stat the file for last modified date and file size. 73 | 74 | ```js 75 | // The default export is sync and use fs.stat to retrieve size & last modified as a blob 76 | import blobFromSync from 'fetch-blob/from.js' 77 | import {File, Blob, blobFrom, blobFromSync, fileFrom, fileFromSync} from 'fetch-blob/from.js' 78 | 79 | const fsFile = fileFromSync('./2-GiB-file.bin', 'application/octet-stream') 80 | const fsBlob = await blobFrom('./2-GiB-file.mp4') 81 | 82 | // Not a 4 GiB memory snapshot, just holds references 83 | // points to where data is located on the disk 84 | const blob = new Blob([fsFile, fsBlob, 'memory', new Uint8Array(10)]) 85 | console.log(blob.size) // ~4 GiB 86 | ``` 87 | 88 | `blobFrom|blobFromSync|fileFrom|fileFromSync(path, [mimetype])` 89 | 90 | ### Creating Blobs backed up by other async sources 91 | Our Blob & File class are more generic then any other polyfills in the way that it can accept any blob look-a-like item 92 | An example of this is that our blob implementation can be constructed with parts coming from [BlobDataItem](https://github.com/node-fetch/fetch-blob/blob/8ef89adad40d255a3bbd55cf38b88597c1cd5480/from.js#L32) (aka a filepath) or from [buffer.Blob](https://nodejs.org/api/buffer.html#buffer_new_buffer_blob_sources_options), It dose not have to implement all the methods - just enough that it can be read/understood by our Blob implementation. The minium requirements is that it has `Symbol.toStringTag`, `size`, `slice()` and either a `stream()` or a `arrayBuffer()` method. If you then wrap it in our Blob or File `new Blob([blobDataItem])` then you get all of the other methods that should be implemented in a blob or file 93 | 94 | An example of this could be to create a file or blob like item coming from a remote HTTP request. Or from a DataBase 95 | 96 | See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [tests](https://github.com/node-fetch/fetch-blob/blob/master/test.js) for more details of how to use the Blob. 97 | 98 | [npm-image]: https://flat.badgen.net/npm/v/fetch-blob 99 | [npm-url]: https://www.npmjs.com/package/fetch-blob 100 | [ci-image]: https://github.com/node-fetch/fetch-blob/workflows/CI/badge.svg 101 | [ci-url]: https://github.com/node-fetch/fetch-blob/actions 102 | [codecov-image]: https://flat.badgen.net/codecov/c/github/node-fetch/fetch-blob/master 103 | [codecov-url]: https://codecov.io/gh/node-fetch/fetch-blob 104 | [install-size-image]: https://flat.badgen.net/packagephobia/install/fetch-blob 105 | [install-size-url]: https://packagephobia.now.sh/result?p=fetch-blob 106 | [fs-blobs]: https://github.com/nodejs/node/issues/37340 107 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/file.d.ts: -------------------------------------------------------------------------------- 1 | /** @type {typeof globalThis.File} */ export const File: typeof globalThis.File; 2 | export default File; 3 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/file.js: -------------------------------------------------------------------------------- 1 | import Blob from './index.js' 2 | 3 | const _File = class File extends Blob { 4 | #lastModified = 0 5 | #name = '' 6 | 7 | /** 8 | * @param {*[]} fileBits 9 | * @param {string} fileName 10 | * @param {{lastModified?: number, type?: string}} options 11 | */// @ts-ignore 12 | constructor (fileBits, fileName, options = {}) { 13 | if (arguments.length < 2) { 14 | throw new TypeError(`Failed to construct 'File': 2 arguments required, but only ${arguments.length} present.`) 15 | } 16 | super(fileBits, options) 17 | 18 | if (options === null) options = {} 19 | 20 | // Simulate WebIDL type casting for NaN value in lastModified option. 21 | const lastModified = options.lastModified === undefined ? Date.now() : Number(options.lastModified) 22 | if (!Number.isNaN(lastModified)) { 23 | this.#lastModified = lastModified 24 | } 25 | 26 | this.#name = String(fileName) 27 | } 28 | 29 | get name () { 30 | return this.#name 31 | } 32 | 33 | get lastModified () { 34 | return this.#lastModified 35 | } 36 | 37 | get [Symbol.toStringTag] () { 38 | return 'File' 39 | } 40 | } 41 | 42 | /** @type {typeof globalThis.File} */// @ts-ignore 43 | export const File = _File 44 | export default File 45 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/from.d.ts: -------------------------------------------------------------------------------- 1 | export default blobFromSync; 2 | /** 3 | * @param {string} path filepath on the disk 4 | * @param {string} [type] mimetype to use 5 | */ 6 | export function blobFromSync(path: string, type?: string): Blob; 7 | import File from "./file.js"; 8 | import Blob from "./index.js"; 9 | /** 10 | * @param {string} path filepath on the disk 11 | * @param {string} [type] mimetype to use 12 | */ 13 | export function blobFrom(path: string, type?: string): any; 14 | /** 15 | * @param {string} path filepath on the disk 16 | * @param {string} [type] mimetype to use 17 | */ 18 | export function fileFrom(path: string, type?: string): any; 19 | /** 20 | * @param {string} path filepath on the disk 21 | * @param {string} [type] mimetype to use 22 | */ 23 | export function fileFromSync(path: string, type?: string): File; 24 | export { File, Blob }; 25 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/from.js: -------------------------------------------------------------------------------- 1 | import { statSync, createReadStream, promises as fs } from 'node:fs' 2 | import { basename } from 'node:path' 3 | import { MessageChannel } from 'node:worker_threads' 4 | 5 | import File from './file.js' 6 | import Blob from './index.js' 7 | 8 | const { stat } = fs 9 | 10 | const DOMException = globalThis.DOMException || (() => { 11 | const port = new MessageChannel().port1 12 | const ab = new ArrayBuffer(0) 13 | try { port.postMessage(ab, [ab, ab]) } catch (err) { return err.constructor } 14 | })() 15 | 16 | /** 17 | * @param {string} path filepath on the disk 18 | * @param {string} [type] mimetype to use 19 | */ 20 | const blobFromSync = (path, type) => fromBlob(statSync(path), path, type) 21 | 22 | /** 23 | * @param {string} path filepath on the disk 24 | * @param {string} [type] mimetype to use 25 | */ 26 | const blobFrom = (path, type) => stat(path).then(stat => fromBlob(stat, path, type)) 27 | 28 | /** 29 | * @param {string} path filepath on the disk 30 | * @param {string} [type] mimetype to use 31 | */ 32 | const fileFrom = (path, type) => stat(path).then(stat => fromFile(stat, path, type)) 33 | 34 | /** 35 | * @param {string} path filepath on the disk 36 | * @param {string} [type] mimetype to use 37 | */ 38 | const fileFromSync = (path, type) => fromFile(statSync(path), path, type) 39 | 40 | // @ts-ignore 41 | const fromBlob = (stat, path, type = '') => new Blob([new BlobDataItem({ 42 | path, 43 | size: stat.size, 44 | lastModified: stat.mtimeMs, 45 | start: 0 46 | })], { type }) 47 | 48 | // @ts-ignore 49 | const fromFile = (stat, path, type = '') => new File([new BlobDataItem({ 50 | path, 51 | size: stat.size, 52 | lastModified: stat.mtimeMs, 53 | start: 0 54 | })], basename(path), { type, lastModified: stat.mtimeMs }) 55 | 56 | /** 57 | * This is a blob backed up by a file on the disk 58 | * with minium requirement. Its wrapped around a Blob as a blobPart 59 | * so you have no direct access to this. 60 | * 61 | * @private 62 | */ 63 | class BlobDataItem { 64 | #path 65 | #start 66 | 67 | constructor (options) { 68 | this.#path = options.path 69 | this.#start = options.start 70 | this.size = options.size 71 | this.lastModified = options.lastModified 72 | } 73 | 74 | /** 75 | * Slicing arguments is first validated and formatted 76 | * to not be out of range by Blob.prototype.slice 77 | */ 78 | slice (start, end) { 79 | return new BlobDataItem({ 80 | path: this.#path, 81 | lastModified: this.lastModified, 82 | size: end - start, 83 | start 84 | }) 85 | } 86 | 87 | async * stream () { 88 | const { mtimeMs } = await stat(this.#path) 89 | if (mtimeMs > this.lastModified) { 90 | throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError') 91 | } 92 | yield * createReadStream(this.#path, { 93 | start: this.#start, 94 | end: this.#start + this.size - 1 95 | }) 96 | } 97 | 98 | get [Symbol.toStringTag] () { 99 | return 'Blob' 100 | } 101 | } 102 | 103 | export default blobFromSync 104 | export { File, Blob, blobFrom, blobFromSync, fileFrom, fileFromSync } 105 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/index.d.ts: -------------------------------------------------------------------------------- 1 | /** @type {typeof globalThis.Blob} */ 2 | export const Blob: typeof globalThis.Blob; 3 | export default Blob; 4 | /** 5 | * } 6 | */ 7 | export type NodeBlob = import('buffer').Blob; 8 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "fetch-blob@^3.1.2", 3 | "_id": "fetch-blob@3.1.3", 4 | "_inBundle": false, 5 | "_integrity": "sha512-ax1Y5I9w+9+JiM+wdHkhBoxew+zG4AJ2SvAD1v1szpddUIiPERVGBxrMcB2ZqW0Y3PP8bOWYv2zqQq1Jp2kqUQ==", 6 | "_location": "/fetch-blob", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "range", 10 | "registry": true, 11 | "raw": "fetch-blob@^3.1.2", 12 | "name": "fetch-blob", 13 | "escapedName": "fetch-blob", 14 | "rawSpec": "^3.1.2", 15 | "saveSpec": null, 16 | "fetchSpec": "^3.1.2" 17 | }, 18 | "_requiredBy": [ 19 | "/node-fetch" 20 | ], 21 | "_resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.3.tgz", 22 | "_shasum": "a7dca4855e39d3e3c5a1da62d4ee335c37d26012", 23 | "_spec": "fetch-blob@^3.1.2", 24 | "_where": "/home/pomber/p/temp/x/node_modules/node-fetch", 25 | "author": { 26 | "name": "Jimmy Wärting", 27 | "email": "jimmy@warting.se", 28 | "url": "https://jimmy.warting.se" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/node-fetch/fetch-blob/issues" 32 | }, 33 | "bundleDependencies": false, 34 | "dependencies": { 35 | "web-streams-polyfill": "^3.0.3" 36 | }, 37 | "deprecated": false, 38 | "description": "Blob & File implementation in Node.js, originally from node-fetch.", 39 | "devDependencies": { 40 | "ava": "^3.15.0", 41 | "c8": "^7.7.2", 42 | "codecov": "^3.8.2", 43 | "node-fetch": "^3.0.0-beta.9", 44 | "typescript": "^4.3.2" 45 | }, 46 | "engines": { 47 | "node": "^12.20 || >= 14.13" 48 | }, 49 | "files": [ 50 | "from.js", 51 | "file.js", 52 | "file.d.ts", 53 | "index.js", 54 | "index.d.ts", 55 | "from.d.ts", 56 | "streams.cjs" 57 | ], 58 | "funding": [ 59 | { 60 | "type": "github", 61 | "url": "https://github.com/sponsors/jimmywarting" 62 | }, 63 | { 64 | "type": "paypal", 65 | "url": "https://paypal.me/jimmywarting" 66 | } 67 | ], 68 | "homepage": "https://github.com/node-fetch/fetch-blob#readme", 69 | "keywords": [ 70 | "blob", 71 | "file", 72 | "node-fetch" 73 | ], 74 | "license": "MIT", 75 | "main": "index.js", 76 | "name": "fetch-blob", 77 | "repository": { 78 | "type": "git", 79 | "url": "git+https://github.com/node-fetch/fetch-blob.git" 80 | }, 81 | "scripts": { 82 | "coverage": "c8 --reporter json --reporter text ava test.js && codecov -f coverage/coverage-final.json", 83 | "prepublishOnly": "tsc --declaration --emitDeclarationOnly --allowJs index.js from.js", 84 | "report": "c8 --reporter json --reporter text ava test.js", 85 | "test": "ava test.js", 86 | "test-wpt": "node --experimental-loader ./test/http-loader.js ./test/test-wpt-in-node.js" 87 | }, 88 | "type": "module", 89 | "version": "3.1.3" 90 | } 91 | -------------------------------------------------------------------------------- /.github/node_modules/fetch-blob/streams.cjs: -------------------------------------------------------------------------------- 1 | /* c8 ignore start */ 2 | // 64 KiB (same size chrome slice theirs blob into Uint8array's) 3 | const POOL_SIZE = 65536 4 | 5 | if (!globalThis.ReadableStream) { 6 | // `node:stream/web` got introduced in v16.5.0 as experimental 7 | // and it's preferred over the polyfilled version. So we also 8 | // suppress the warning that gets emitted by NodeJS for using it. 9 | try { 10 | const process = require('node:process') 11 | const { emitWarning } = process 12 | try { 13 | process.emitWarning = () => {} 14 | Object.assign(globalThis, require('node:stream/web')) 15 | process.emitWarning = emitWarning 16 | } catch (error) { 17 | process.emitWarning = emitWarning 18 | throw error 19 | } 20 | } catch (error) { 21 | // fallback to polyfill implementation 22 | Object.assign(globalThis, require('web-streams-polyfill/dist/ponyfill.es2018.js')) 23 | } 24 | } 25 | 26 | try { 27 | // Don't use node: prefix for this, require+node: is not supported until node v14.14 28 | // Only `import()` can use prefix in 12.20 and later 29 | const { Blob } = require('buffer') 30 | if (Blob && !Blob.prototype.stream) { 31 | Blob.prototype.stream = function name (params) { 32 | let position = 0 33 | const blob = this 34 | 35 | return new ReadableStream({ 36 | type: 'bytes', 37 | async pull (ctrl) { 38 | const chunk = blob.slice(position, Math.min(blob.size, position + POOL_SIZE)) 39 | const buffer = await chunk.arrayBuffer() 40 | position += buffer.byteLength 41 | ctrl.enqueue(new Uint8Array(buffer)) 42 | 43 | if (position === blob.size) { 44 | ctrl.close() 45 | } 46 | } 47 | }) 48 | } 49 | } 50 | } catch (error) {} 51 | /* c8 ignore end */ 52 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/@types/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import {Agent} from 'http'; 5 | 6 | type AbortSignal = { 7 | readonly aborted: boolean; 8 | 9 | addEventListener: (type: 'abort', listener: (this: AbortSignal) => void) => void; 10 | removeEventListener: (type: 'abort', listener: (this: AbortSignal) => void) => void; 11 | }; 12 | 13 | export type HeadersInit = Headers | Record | Iterable | Iterable>; 14 | 15 | /** 16 | * This Fetch API interface allows you to perform various actions on HTTP request and response headers. 17 | * These actions include retrieving, setting, adding to, and removing. 18 | * A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs. 19 | * You can add to this using methods like append() (see Examples.) 20 | * In all methods of this interface, header names are matched by case-insensitive byte sequence. 21 | * */ 22 | export class Headers { 23 | constructor(init?: HeadersInit); 24 | 25 | append(name: string, value: string): void; 26 | delete(name: string): void; 27 | get(name: string): string | null; 28 | has(name: string): boolean; 29 | set(name: string, value: string): void; 30 | forEach( 31 | callbackfn: (value: string, key: string, parent: Headers) => void, 32 | thisArg?: any 33 | ): void; 34 | 35 | [Symbol.iterator](): IterableIterator<[string, string]>; 36 | /** 37 | * Returns an iterator allowing to go through all key/value pairs contained in this object. 38 | */ 39 | entries(): IterableIterator<[string, string]>; 40 | /** 41 | * Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. 42 | */ 43 | keys(): IterableIterator; 44 | /** 45 | * Returns an iterator allowing to go through all values of the key/value pairs contained in this object. 46 | */ 47 | values(): IterableIterator; 48 | 49 | /** Node-fetch extension */ 50 | raw(): Record; 51 | } 52 | 53 | export interface RequestInit { 54 | /** 55 | * A BodyInit object or null to set request's body. 56 | */ 57 | body?: BodyInit | null; 58 | /** 59 | * A Headers object, an object literal, or an array of two-item arrays to set request's headers. 60 | */ 61 | headers?: HeadersInit; 62 | /** 63 | * A string to set request's method. 64 | */ 65 | method?: string; 66 | /** 67 | * A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. 68 | */ 69 | redirect?: RequestRedirect; 70 | /** 71 | * An AbortSignal to set request's signal. 72 | */ 73 | signal?: AbortSignal | null; 74 | 75 | // Node-fetch extensions to the whatwg/fetch spec 76 | agent?: Agent | ((parsedUrl: URL) => Agent); 77 | compress?: boolean; 78 | counter?: number; 79 | follow?: number; 80 | hostname?: string; 81 | port?: number; 82 | protocol?: string; 83 | size?: number; 84 | highWaterMark?: number; 85 | insecureHTTPParser?: boolean; 86 | } 87 | 88 | export interface ResponseInit { 89 | headers?: HeadersInit; 90 | status?: number; 91 | statusText?: string; 92 | } 93 | 94 | export type BodyInit = 95 | | Blob 96 | | Buffer 97 | | URLSearchParams 98 | | NodeJS.ReadableStream 99 | | string; 100 | declare class BodyMixin { 101 | constructor(body?: BodyInit, options?: {size?: number}); 102 | 103 | readonly body: NodeJS.ReadableStream | null; 104 | readonly bodyUsed: boolean; 105 | readonly size: number; 106 | 107 | buffer(): Promise; 108 | arrayBuffer(): Promise; 109 | blob(): Promise; 110 | json(): Promise; 111 | text(): Promise; 112 | } 113 | 114 | // `Body` must not be exported as a class since it's not exported from the JavaScript code. 115 | export interface Body extends Pick {} 116 | 117 | export type RequestRedirect = 'error' | 'follow' | 'manual'; 118 | export type RequestInfo = string | Request; 119 | export class Request extends BodyMixin { 120 | constructor(input: RequestInfo, init?: RequestInit); 121 | 122 | /** 123 | * Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. 124 | */ 125 | readonly headers: Headers; 126 | /** 127 | * Returns request's HTTP method, which is "GET" by default. 128 | */ 129 | readonly method: string; 130 | /** 131 | * Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. 132 | */ 133 | readonly redirect: RequestRedirect; 134 | /** 135 | * Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. 136 | */ 137 | readonly signal: AbortSignal; 138 | /** 139 | * Returns the URL of request as a string. 140 | */ 141 | readonly url: string; 142 | clone(): Request; 143 | } 144 | 145 | type ResponseType = 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect'; 146 | 147 | export class Response extends BodyMixin { 148 | constructor(body?: BodyInit | null, init?: ResponseInit); 149 | 150 | readonly headers: Headers; 151 | readonly ok: boolean; 152 | readonly redirected: boolean; 153 | readonly status: number; 154 | readonly statusText: string; 155 | readonly type: ResponseType; 156 | readonly url: string; 157 | clone(): Response; 158 | 159 | static error(): Response; 160 | } 161 | 162 | export class FetchError extends Error { 163 | constructor(message: string, type: string, systemError?: Record); 164 | 165 | name: 'FetchError'; 166 | [Symbol.toStringTag]: 'FetchError'; 167 | type: string; 168 | code?: string; 169 | errno?: string; 170 | } 171 | 172 | export class AbortError extends Error { 173 | type: string; 174 | name: 'AbortError'; 175 | [Symbol.toStringTag]: 'AbortError'; 176 | } 177 | 178 | export function isRedirect(code: number): boolean; 179 | export default function fetch(url: RequestInfo, init?: RequestInit): Promise; 180 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 - 2020 Node Fetch Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "node-fetch", 3 | "_id": "node-fetch@3.0.0", 4 | "_inBundle": false, 5 | "_integrity": "sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==", 6 | "_location": "/node-fetch", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "node-fetch", 12 | "name": "node-fetch", 13 | "escapedName": "node-fetch", 14 | "rawSpec": "", 15 | "saveSpec": null, 16 | "fetchSpec": "latest" 17 | }, 18 | "_requiredBy": [ 19 | "#USER", 20 | "/" 21 | ], 22 | "_resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz", 23 | "_shasum": "79da7146a520036f2c5f644e4a26095f17e411ea", 24 | "_spec": "node-fetch", 25 | "_where": "/home/pomber/p/temp/x", 26 | "author": { 27 | "name": "David Frank" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/node-fetch/node-fetch/issues" 31 | }, 32 | "bundleDependencies": false, 33 | "dependencies": { 34 | "data-uri-to-buffer": "^3.0.1", 35 | "fetch-blob": "^3.1.2" 36 | }, 37 | "deprecated": false, 38 | "description": "A light-weight module that brings Fetch API to node.js", 39 | "devDependencies": { 40 | "abort-controller": "^3.0.0", 41 | "abortcontroller-polyfill": "^1.7.1", 42 | "busboy": "^0.3.1", 43 | "c8": "^7.7.2", 44 | "chai": "^4.3.4", 45 | "chai-as-promised": "^7.1.1", 46 | "chai-iterator": "^3.0.2", 47 | "chai-string": "^1.5.0", 48 | "coveralls": "^3.1.0", 49 | "delay": "^5.0.0", 50 | "form-data": "^4.0.0", 51 | "formdata-node": "^3.5.4", 52 | "mocha": "^8.3.2", 53 | "p-timeout": "^5.0.0", 54 | "tsd": "^0.14.0", 55 | "xo": "^0.39.1" 56 | }, 57 | "engines": { 58 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 59 | }, 60 | "files": [ 61 | "src", 62 | "@types/index.d.ts" 63 | ], 64 | "funding": { 65 | "type": "opencollective", 66 | "url": "https://opencollective.com/node-fetch" 67 | }, 68 | "homepage": "https://github.com/node-fetch/node-fetch", 69 | "keywords": [ 70 | "fetch", 71 | "http", 72 | "promise", 73 | "request", 74 | "curl", 75 | "wget", 76 | "xhr", 77 | "whatwg" 78 | ], 79 | "license": "MIT", 80 | "main": "./src/index.js", 81 | "name": "node-fetch", 82 | "repository": { 83 | "type": "git", 84 | "url": "git+https://github.com/node-fetch/node-fetch.git" 85 | }, 86 | "runkitExampleFilename": "example.js", 87 | "scripts": { 88 | "coverage": "c8 report --reporter=text-lcov | coveralls", 89 | "lint": "xo", 90 | "test": "mocha", 91 | "test-types": "tsd" 92 | }, 93 | "sideEffects": false, 94 | "tsd": { 95 | "cwd": "@types", 96 | "compilerOptions": { 97 | "esModuleInterop": true 98 | } 99 | }, 100 | "type": "module", 101 | "types": "./@types/index.d.ts", 102 | "version": "3.0.0", 103 | "xo": { 104 | "envs": [ 105 | "node", 106 | "browser" 107 | ], 108 | "ignores": [ 109 | "example.js" 110 | ], 111 | "rules": { 112 | "complexity": 0, 113 | "import/extensions": 0, 114 | "import/no-useless-path-segments": 0, 115 | "import/no-anonymous-default-export": 0, 116 | "import/no-named-as-default": 0, 117 | "unicorn/import-index": 0, 118 | "unicorn/no-array-reduce": 0, 119 | "unicorn/prefer-node-protocol": 0, 120 | "unicorn/numeric-separators-style": 0, 121 | "unicorn/explicit-length-check": 0, 122 | "capitalized-comments": 0, 123 | "@typescript-eslint/member-ordering": 0 124 | }, 125 | "overrides": [ 126 | { 127 | "files": "test/**/*.js", 128 | "envs": [ 129 | "node", 130 | "mocha" 131 | ], 132 | "rules": { 133 | "max-nested-callbacks": 0, 134 | "no-unused-expressions": 0, 135 | "no-warning-comments": 0, 136 | "new-cap": 0, 137 | "guard-for-in": 0, 138 | "unicorn/no-array-for-each": 0, 139 | "unicorn/prevent-abbreviations": 0, 140 | "promise/prefer-await-to-then": 0, 141 | "ava/no-import-test-files": 0 142 | } 143 | } 144 | ] 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/errors/abort-error.js: -------------------------------------------------------------------------------- 1 | import {FetchBaseError} from './base.js'; 2 | 3 | /** 4 | * AbortError interface for cancelled requests 5 | */ 6 | export class AbortError extends FetchBaseError { 7 | constructor(message, type = 'aborted') { 8 | super(message, type); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/errors/base.js: -------------------------------------------------------------------------------- 1 | export class FetchBaseError extends Error { 2 | constructor(message, type) { 3 | super(message); 4 | // Hide custom error implementation details from end-users 5 | Error.captureStackTrace(this, this.constructor); 6 | 7 | this.type = type; 8 | } 9 | 10 | get name() { 11 | return this.constructor.name; 12 | } 13 | 14 | get [Symbol.toStringTag]() { 15 | return this.constructor.name; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/errors/fetch-error.js: -------------------------------------------------------------------------------- 1 | 2 | import {FetchBaseError} from './base.js'; 3 | 4 | /** 5 | * @typedef {{ address?: string, code: string, dest?: string, errno: number, info?: object, message: string, path?: string, port?: number, syscall: string}} SystemError 6 | */ 7 | 8 | /** 9 | * FetchError interface for operational errors 10 | */ 11 | export class FetchError extends FetchBaseError { 12 | /** 13 | * @param {string} message - Error message for human 14 | * @param {string} [type] - Error type for machine 15 | * @param {SystemError} [systemError] - For Node.js system error 16 | */ 17 | constructor(message, type, systemError) { 18 | super(message, type); 19 | // When err.type is `system`, err.erroredSysCall contains system error and err.code contains system error code 20 | if (systemError) { 21 | // eslint-disable-next-line no-multi-assign 22 | this.code = this.errno = systemError.code; 23 | this.erroredSysCall = systemError.syscall; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/response.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Response.js 3 | * 4 | * Response class provides content decoding 5 | */ 6 | 7 | import Headers from './headers.js'; 8 | import Body, {clone, extractContentType} from './body.js'; 9 | import {isRedirect} from './utils/is-redirect.js'; 10 | 11 | const INTERNALS = Symbol('Response internals'); 12 | 13 | /** 14 | * Response class 15 | * 16 | * Ref: https://fetch.spec.whatwg.org/#response-class 17 | * 18 | * @param Stream body Readable stream 19 | * @param Object opts Response options 20 | * @return Void 21 | */ 22 | export default class Response extends Body { 23 | constructor(body = null, options = {}) { 24 | super(body, options); 25 | 26 | // eslint-disable-next-line no-eq-null, eqeqeq, no-negated-condition 27 | const status = options.status != null ? options.status : 200; 28 | 29 | const headers = new Headers(options.headers); 30 | 31 | if (body !== null && !headers.has('Content-Type')) { 32 | const contentType = extractContentType(body); 33 | if (contentType) { 34 | headers.append('Content-Type', contentType); 35 | } 36 | } 37 | 38 | this[INTERNALS] = { 39 | type: 'default', 40 | url: options.url, 41 | status, 42 | statusText: options.statusText || '', 43 | headers, 44 | counter: options.counter, 45 | highWaterMark: options.highWaterMark 46 | }; 47 | } 48 | 49 | get type() { 50 | return this[INTERNALS].type; 51 | } 52 | 53 | get url() { 54 | return this[INTERNALS].url || ''; 55 | } 56 | 57 | get status() { 58 | return this[INTERNALS].status; 59 | } 60 | 61 | /** 62 | * Convenience property representing if the request ended normally 63 | */ 64 | get ok() { 65 | return this[INTERNALS].status >= 200 && this[INTERNALS].status < 300; 66 | } 67 | 68 | get redirected() { 69 | return this[INTERNALS].counter > 0; 70 | } 71 | 72 | get statusText() { 73 | return this[INTERNALS].statusText; 74 | } 75 | 76 | get headers() { 77 | return this[INTERNALS].headers; 78 | } 79 | 80 | get highWaterMark() { 81 | return this[INTERNALS].highWaterMark; 82 | } 83 | 84 | /** 85 | * Clone this response 86 | * 87 | * @return Response 88 | */ 89 | clone() { 90 | return new Response(clone(this, this.highWaterMark), { 91 | type: this.type, 92 | url: this.url, 93 | status: this.status, 94 | statusText: this.statusText, 95 | headers: this.headers, 96 | ok: this.ok, 97 | redirected: this.redirected, 98 | size: this.size 99 | }); 100 | } 101 | 102 | /** 103 | * @param {string} url The URL that the new response is to originate from. 104 | * @param {number} status An optional status code for the response (e.g., 302.) 105 | * @returns {Response} A Response object. 106 | */ 107 | static redirect(url, status = 302) { 108 | if (!isRedirect(status)) { 109 | throw new RangeError('Failed to execute "redirect" on "response": Invalid status code'); 110 | } 111 | 112 | return new Response(null, { 113 | headers: { 114 | location: new URL(url).toString() 115 | }, 116 | status 117 | }); 118 | } 119 | 120 | static error() { 121 | const response = new Response(null, {status: 0, statusText: ''}); 122 | response[INTERNALS].type = 'error'; 123 | return response; 124 | } 125 | 126 | get [Symbol.toStringTag]() { 127 | return 'Response'; 128 | } 129 | } 130 | 131 | Object.defineProperties(Response.prototype, { 132 | type: {enumerable: true}, 133 | url: {enumerable: true}, 134 | status: {enumerable: true}, 135 | ok: {enumerable: true}, 136 | redirected: {enumerable: true}, 137 | statusText: {enumerable: true}, 138 | headers: {enumerable: true}, 139 | clone: {enumerable: true} 140 | }); 141 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/utils/form-data.js: -------------------------------------------------------------------------------- 1 | import {randomBytes} from 'crypto'; 2 | 3 | import {isBlob} from './is.js'; 4 | 5 | const carriage = '\r\n'; 6 | const dashes = '-'.repeat(2); 7 | const carriageLength = Buffer.byteLength(carriage); 8 | 9 | /** 10 | * @param {string} boundary 11 | */ 12 | const getFooter = boundary => `${dashes}${boundary}${dashes}${carriage.repeat(2)}`; 13 | 14 | /** 15 | * @param {string} boundary 16 | * @param {string} name 17 | * @param {*} field 18 | * 19 | * @return {string} 20 | */ 21 | function getHeader(boundary, name, field) { 22 | let header = ''; 23 | 24 | header += `${dashes}${boundary}${carriage}`; 25 | header += `Content-Disposition: form-data; name="${name}"`; 26 | 27 | if (isBlob(field)) { 28 | header += `; filename="${field.name}"${carriage}`; 29 | header += `Content-Type: ${field.type || 'application/octet-stream'}`; 30 | } 31 | 32 | return `${header}${carriage.repeat(2)}`; 33 | } 34 | 35 | /** 36 | * @return {string} 37 | */ 38 | export const getBoundary = () => randomBytes(8).toString('hex'); 39 | 40 | /** 41 | * @param {FormData} form 42 | * @param {string} boundary 43 | */ 44 | export async function * formDataIterator(form, boundary) { 45 | for (const [name, value] of form) { 46 | yield getHeader(boundary, name, value); 47 | 48 | if (isBlob(value)) { 49 | yield * value.stream(); 50 | } else { 51 | yield value; 52 | } 53 | 54 | yield carriage; 55 | } 56 | 57 | yield getFooter(boundary); 58 | } 59 | 60 | /** 61 | * @param {FormData} form 62 | * @param {string} boundary 63 | */ 64 | export function getFormDataLength(form, boundary) { 65 | let length = 0; 66 | 67 | for (const [name, value] of form) { 68 | length += Buffer.byteLength(getHeader(boundary, name, value)); 69 | 70 | length += isBlob(value) ? value.size : Buffer.byteLength(String(value)); 71 | 72 | length += carriageLength; 73 | } 74 | 75 | length += Buffer.byteLength(getFooter(boundary)); 76 | 77 | return length; 78 | } 79 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/utils/get-search.js: -------------------------------------------------------------------------------- 1 | export const getSearch = parsedURL => { 2 | if (parsedURL.search) { 3 | return parsedURL.search; 4 | } 5 | 6 | const lastOffset = parsedURL.href.length - 1; 7 | const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : ''); 8 | return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : ''; 9 | }; 10 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/utils/is-redirect.js: -------------------------------------------------------------------------------- 1 | const redirectStatus = new Set([301, 302, 303, 307, 308]); 2 | 3 | /** 4 | * Redirect code matching 5 | * 6 | * @param {number} code - Status code 7 | * @return {boolean} 8 | */ 9 | export const isRedirect = code => { 10 | return redirectStatus.has(code); 11 | }; 12 | -------------------------------------------------------------------------------- /.github/node_modules/node-fetch/src/utils/is.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Is.js 3 | * 4 | * Object type checks. 5 | */ 6 | 7 | const NAME = Symbol.toStringTag; 8 | 9 | /** 10 | * Check if `obj` is a URLSearchParams object 11 | * ref: https://github.com/node-fetch/node-fetch/issues/296#issuecomment-307598143 12 | * 13 | * @param {*} obj 14 | * @return {boolean} 15 | */ 16 | export const isURLSearchParameters = object => { 17 | return ( 18 | typeof object === 'object' && 19 | typeof object.append === 'function' && 20 | typeof object.delete === 'function' && 21 | typeof object.get === 'function' && 22 | typeof object.getAll === 'function' && 23 | typeof object.has === 'function' && 24 | typeof object.set === 'function' && 25 | typeof object.sort === 'function' && 26 | object[NAME] === 'URLSearchParams' 27 | ); 28 | }; 29 | 30 | /** 31 | * Check if `object` is a W3C `Blob` object (which `File` inherits from) 32 | * 33 | * @param {*} obj 34 | * @return {boolean} 35 | */ 36 | export const isBlob = object => { 37 | return ( 38 | typeof object === 'object' && 39 | typeof object.arrayBuffer === 'function' && 40 | typeof object.type === 'string' && 41 | typeof object.stream === 'function' && 42 | typeof object.constructor === 'function' && 43 | /^(Blob|File)$/.test(object[NAME]) 44 | ); 45 | }; 46 | 47 | /** 48 | * Check if `obj` is a spec-compliant `FormData` object 49 | * 50 | * @param {*} object 51 | * @return {boolean} 52 | */ 53 | export function isFormData(object) { 54 | return ( 55 | typeof object === 'object' && 56 | typeof object.append === 'function' && 57 | typeof object.set === 'function' && 58 | typeof object.get === 'function' && 59 | typeof object.getAll === 'function' && 60 | typeof object.delete === 'function' && 61 | typeof object.keys === 'function' && 62 | typeof object.values === 'function' && 63 | typeof object.entries === 'function' && 64 | typeof object.constructor === 'function' && 65 | object[NAME] === 'FormData' 66 | ); 67 | } 68 | 69 | /** 70 | * Check if `obj` is an instance of AbortSignal. 71 | * 72 | * @param {*} obj 73 | * @return {boolean} 74 | */ 75 | export const isAbortSignal = object => { 76 | return ( 77 | typeof object === 'object' && ( 78 | object[NAME] === 'AbortSignal' || 79 | object[NAME] === 'EventTarget' 80 | ) 81 | ); 82 | }; 83 | 84 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Mattias Buelens 4 | Copyright (c) 2016 Diwank Singh Tomer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/dist/types/tsdoc-metadata.json: -------------------------------------------------------------------------------- 1 | // This file is read by tools that parse documentation comments conforming to the TSDoc standard. 2 | // It should be published with your NPM package. It should not be tracked by Git. 3 | { 4 | "tsdocVersion": "0.12", 5 | "toolPackages": [ 6 | { 7 | "packageName": "@microsoft/api-extractor", 8 | "packageVersion": "7.13.4" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/es2018/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-streams-polyfill-es2018", 3 | "main": "../dist/polyfill.es2018", 4 | "browser": "../dist/polyfill.es2018.min.js", 5 | "module": "../dist/polyfill.es2018.mjs", 6 | "types": "../dist/types/polyfill.d.ts" 7 | } 8 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/es6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-streams-polyfill-es6", 3 | "main": "../dist/polyfill.es6", 4 | "browser": "../dist/polyfill.es6.min.js", 5 | "module": "../dist/polyfill.es6.mjs", 6 | "types": "../dist/types/polyfill.d.ts" 7 | } 8 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "web-streams-polyfill@^3.0.3", 3 | "_id": "web-streams-polyfill@3.1.1", 4 | "_inBundle": false, 5 | "_integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q==", 6 | "_location": "/web-streams-polyfill", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "range", 10 | "registry": true, 11 | "raw": "web-streams-polyfill@^3.0.3", 12 | "name": "web-streams-polyfill", 13 | "escapedName": "web-streams-polyfill", 14 | "rawSpec": "^3.0.3", 15 | "saveSpec": null, 16 | "fetchSpec": "^3.0.3" 17 | }, 18 | "_requiredBy": [ 19 | "/fetch-blob" 20 | ], 21 | "_resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz", 22 | "_shasum": "1516f2d4ea8f1bdbfed15eb65cb2df87098c8364", 23 | "_spec": "web-streams-polyfill@^3.0.3", 24 | "_where": "/home/pomber/p/temp/x/node_modules/fetch-blob", 25 | "author": { 26 | "name": "Mattias Buelens", 27 | "email": "mattias@buelens.com" 28 | }, 29 | "browser": "dist/polyfill.min.js", 30 | "bugs": { 31 | "url": "https://github.com/MattiasBuelens/web-streams-polyfill/issues" 32 | }, 33 | "bundleDependencies": false, 34 | "contributors": [ 35 | { 36 | "name": "Diwank Singh", 37 | "email": "diwank.singh@gmail.com" 38 | } 39 | ], 40 | "deprecated": false, 41 | "description": "Web Streams, based on the WHATWG spec reference implementation", 42 | "devDependencies": { 43 | "@microsoft/api-extractor": "^7.13.4", 44 | "@rollup/plugin-inject": "^4.0.2", 45 | "@rollup/plugin-replace": "^2.4.2", 46 | "@rollup/plugin-strip": "^2.0.0", 47 | "@rollup/plugin-typescript": "^8.2.1", 48 | "@types/node": "^14.14.37", 49 | "@typescript-eslint/eslint-plugin": "^4.21.0", 50 | "@typescript-eslint/parser": "^4.21.0", 51 | "@ungap/promise-all-settled": "^1.1.2", 52 | "eslint": "^7.23.0", 53 | "jasmine": "^3.7.0", 54 | "micromatch": "^4.0.2", 55 | "rollup": "^2.44.0", 56 | "rollup-plugin-terser": "^7.0.2", 57 | "ts-morph": "^10.0.2", 58 | "tslib": "^2.2.0", 59 | "typescript": "^4.2.4", 60 | "wpt-runner": "^3.2.1" 61 | }, 62 | "engines": { 63 | "node": ">= 8" 64 | }, 65 | "files": [ 66 | "dist", 67 | "es6", 68 | "es2018", 69 | "ponyfill" 70 | ], 71 | "homepage": "https://github.com/MattiasBuelens/web-streams-polyfill#readme", 72 | "keywords": [ 73 | "streams", 74 | "whatwg", 75 | "polyfill" 76 | ], 77 | "license": "MIT", 78 | "main": "dist/polyfill", 79 | "module": "dist/polyfill.mjs", 80 | "name": "web-streams-polyfill", 81 | "repository": { 82 | "type": "git", 83 | "url": "git+https://github.com/MattiasBuelens/web-streams-polyfill.git" 84 | }, 85 | "scripts": { 86 | "accept:types": "tsc --project . --emitDeclarationOnly --declarationDir ./lib && api-extractor run --local && node ./build/downlevel-dts.js", 87 | "build": "npm run build:bundle && npm run build:types", 88 | "build:bundle": "rollup -c", 89 | "build:types": "tsc --project . --emitDeclarationOnly --declarationDir ./lib && api-extractor run && node ./build/downlevel-dts.js", 90 | "lint": "eslint \"src/**/*.ts\"", 91 | "prepare": "npm run build", 92 | "pretest:wpt": "git submodule update --init --recursive", 93 | "test": "npm run test:types && npm run test:unit && npm run test:wpt", 94 | "test:types": "tsc -p ./test/types/tsconfig.json", 95 | "test:unit": "jasmine --config=test/unit/jasmine.json", 96 | "test:wpt": "node --expose_gc ./test/run-web-platform-tests.js" 97 | }, 98 | "types": "dist/types/polyfill.d.ts", 99 | "typesVersions": { 100 | ">=3.6": { 101 | "dist/types/*": [ 102 | "dist/types/ts3.6/*" 103 | ] 104 | } 105 | }, 106 | "version": "3.1.1" 107 | } 108 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/ponyfill/es2018/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-streams-ponyfill-es2018", 3 | "main": "../../dist/ponyfill.es2018", 4 | "module": "../../dist/ponyfill.es2018.mjs", 5 | "types": "../../dist/types/polyfill.d.ts" 6 | } 7 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/ponyfill/es6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-streams-ponyfill-es6", 3 | "main": "../../dist/ponyfill.es6", 4 | "module": "../../dist/ponyfill.es6.mjs", 5 | "types": "../../dist/types/polyfill.d.ts" 6 | } 7 | -------------------------------------------------------------------------------- /.github/node_modules/web-streams-polyfill/ponyfill/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-streams-ponyfill", 3 | "main": "../dist/ponyfill", 4 | "module": "../dist/ponyfill.mjs", 5 | "types": "../dist/types/polyfill.d.ts" 6 | } 7 | -------------------------------------------------------------------------------- /.github/update-sponsors/fetch-sponsors.mjs: -------------------------------------------------------------------------------- 1 | import fetch from "node-fetch"; 2 | 3 | export async function fetchSponsors() { 4 | const githubUrl = "https://api.github.com/graphql"; 5 | const token = process.env.GITHUB_TOKEN; 6 | const oauth = { Authorization: "bearer " + token }; 7 | 8 | if (!token) { 9 | console.log("Missing process.env.GITHUB_TOKEN"); 10 | return []; 11 | } 12 | 13 | return await fetch(githubUrl, { 14 | method: "POST", 15 | body: JSON.stringify({ query }), 16 | headers: oauth, 17 | }) 18 | .then(function (response) { 19 | return response.json(); 20 | }) 21 | .then(({ data, errors }) => { 22 | if (errors) { 23 | console.error(JSON.stringify(errors)); 24 | return; 25 | } 26 | // console.log(JSON.stringify(data.organization, null, 2)); 27 | const ghSponsors = data.organization.sponsorshipsAsMaintainer.nodes 28 | .filter((node) => node.tier.monthlyPriceInDollars >= 9) 29 | .map((node) => { 30 | const { __typename, ...sponsor } = node.sponsorEntity; 31 | return { 32 | ...sponsor, 33 | isOrg: __typename === "Organization", 34 | }; 35 | }); 36 | return [...otherSponsors, ...ghSponsors.reverse()]; 37 | }) 38 | .catch((error) => { 39 | console.error("Error fetching sponsors", error); 40 | }); 41 | } 42 | 43 | const query = ` 44 | { 45 | organization(login: "code-hike") { 46 | sponsorshipsAsMaintainer(last: 100, orderBy: {field: CREATED_AT, direction: DESC}) { 47 | nodes { 48 | tier { 49 | monthlyPriceInDollars 50 | isCustomAmount 51 | isOneTime 52 | } 53 | sponsorEntity { 54 | __typename 55 | ... on User { 56 | name 57 | login 58 | avatarUrl(size: 128) 59 | location 60 | url 61 | } 62 | ... on Organization { 63 | avatarUrl(size: 128) 64 | login 65 | name 66 | location 67 | url 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | `; 75 | 76 | const otherSponsors = [ 77 | { 78 | name: "Meta", 79 | login: "facebook", 80 | avatarUrl: "https://avatars.githubusercontent.com/u/69631?v=4", 81 | location: "Menlo Park, California", 82 | url: "https://github.com/facebook", 83 | isOrg: true, 84 | }, 85 | { 86 | name: "Outerbounds", 87 | login: "outerbounds", 88 | avatarUrl: "https://avatars.githubusercontent.com/u/82194325?v=4", 89 | location: "", 90 | url: "https://github.com/outerbounds", 91 | isOrg: true, 92 | }, 93 | { 94 | name: "Fran Méndez", 95 | login: "fmvilas", 96 | avatarUrl: "https://avatars.githubusercontent.com/u/242119?s=128&v=4", 97 | location: "Spain", 98 | url: "https://github.com/fmvilas", 99 | isOrg: false, 100 | }, 101 | { 102 | name: "Matthias Zepper", 103 | login: "MatthiasZepper", 104 | avatarUrl: "https://avatars.githubusercontent.com/u/6963520?s=128&v=4", 105 | location: "Germany", 106 | url: "https://github.com/matthiaszepper", 107 | isOrg: false, 108 | }, 109 | ]; 110 | -------------------------------------------------------------------------------- /.github/update-sponsors/index.mjs: -------------------------------------------------------------------------------- 1 | import { fetchSponsors } from "./fetch-sponsors.mjs"; 2 | import { promises as fs } from "fs"; 3 | const sponsorsFilePath = "./data/sponsors.json"; 4 | const demosFilePath = "./data/demos/index.json"; 5 | 6 | async function main() { 7 | const sponsors = await fetchSponsors(); 8 | 9 | updateSponsors(sponsors); 10 | updateDemos(sponsors); 11 | } 12 | 13 | main(); 14 | 15 | process.on("unhandledRejection", (up) => { 16 | throw up; 17 | }); 18 | 19 | async function updateSponsors(sponsors) { 20 | const sponsorsData = JSON.parse(await fs.readFile(sponsorsFilePath, "utf8")); 21 | sponsorsData.sponsors = sponsors; 22 | console.table(sponsors.map(({ url, avatarUrl, ...x }) => x)); 23 | await fs.writeFile(sponsorsFilePath, JSON.stringify(sponsorsData, null, 2)); 24 | } 25 | 26 | async function updateDemos(sponsors) { 27 | const demos = JSON.parse(await fs.readFile(demosFilePath, "utf8")); 28 | const logins = sponsors.map(({ login }) => login); 29 | 30 | demos.forEach((demo) => { 31 | demo.sponsors.forEach((sponsor) => { 32 | if (!logins.includes(sponsor)) { 33 | // remove old sponsor for demo sponsors 34 | removeItem(demo.sponsors, sponsor); 35 | } else { 36 | // remove used sponsors from login list 37 | removeItem(logins, sponsor); 38 | } 39 | }); 40 | }); 41 | 42 | // add new logins 43 | demos.forEach((demo) => { 44 | while (demo.sponsors.length < 5 && logins.length > 0) { 45 | demo.sponsors.push(logins.pop()); 46 | } 47 | demo.locked = demo.sponsors.length < 5; 48 | }); 49 | 50 | await fs.writeFile(demosFilePath, JSON.stringify(demos, null, 2)); 51 | } 52 | 53 | function removeItem(list, item) { 54 | const index = list.indexOf(item); 55 | if (index > -1) { 56 | list.splice(index, 1); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /.github/workflows/update-sponsors.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | name: Update Sponsors 4 | jobs: 5 | render: 6 | name: Update Sponsors 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@main 10 | - uses: actions/setup-node@main 11 | - name: Update sponsors files 12 | run: node .github/update-sponsors/index.mjs 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.CH_GITHUB_TOKEN }} 15 | - name: Commit files 16 | run: | 17 | git config --local user.email "action@github.com" 18 | git config --local user.name "GitHub Action" 19 | git add . 20 | git diff-index --quiet HEAD || git commit -m "Update sponsors" -a 21 | - name: Push changes 22 | uses: ad-m/github-push-action@v0.6.0 23 | with: 24 | github_token: ${{ secrets.GITHUB_TOKEN }} 25 | branch: ${{ github.ref }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | todo.md 37 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | tasks: 6 | - init: yarn install 7 | command: yarn run dev 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": ["*.mdx"], 5 | "options": { 6 | "semi": false, 7 | "printWidth": 40 8 | } 9 | }, 10 | { 11 | "files": ["show.mdx", "old-show.mdx"], 12 | "options": { 13 | "printWidth": 54, 14 | "semi": false 15 | } 16 | }, 17 | { 18 | "files": ["slideshow-preview.mdx"], 19 | "options": { 20 | "printWidth": 60, 21 | "semi": false 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /data/demos/code.mdx: -------------------------------------------------------------------------------- 1 | # Code and Focus 2 | 3 | Code Hike will apply syntax highlighting to any code block 4 | 5 | ```js 6 | function lorem(ipsum, dolor = 1) { 7 | const sit = ipsum == null ? 0 : ipsum.sit; 8 | dolor = sit - amet(dolor); 9 | return sit ? consectetur(ipsum) : []; 10 | } 11 | ``` 12 | 13 | It works by default with many languages 14 | 15 | ```groovy 16 | def f() { 17 | return [2, 3] 18 | } 19 | 20 | (a, b) = f() 21 | println a 22 | println b 23 | ``` 24 | 25 | Use `focus` to show the code that's important to the reader 26 | 27 | ```js focus=4 28 | function lorem(ipsum, dolor = 1) { 29 | const sit = ipsum == null ? 0 : ipsum.sit; 30 | dolor = sit - amet(dolor); 31 | return sit ? consectetur(ipsum) : []; 32 | } 33 | ``` 34 | 35 | You can specify a list of line numbers 36 | 37 | ```js focus=1,3:4 38 | function lorem(ipsum, dolor = 1) { 39 | const sit = ipsum == null ? 0 : ipsum.sit; 40 | dolor = sit - amet(dolor); 41 | return sit ? consectetur(ipsum) : []; 42 | } 43 | ``` 44 | 45 | And also columns 46 | 47 | ```js focus=2,4[10:13] 48 | function lorem(ipsum, dolor = 1) { 49 | const sit = ipsum == null ? 0 : ipsum.sit; 50 | dolor = sit - amet(dolor); 51 | return sit ? consectetur(ipsum) : []; 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /data/demos/comment-annotations.mdx: -------------------------------------------------------------------------------- 1 | # Comment annotations 2 | 3 | You can use comments inside the code to make the focus relative. 4 | 5 | ```js 6 | function lorem(ipsum, dolor = 1) { 7 | const sit = ipsum == null && 0 8 | dolor = sit - amet(dolor) 9 | // focus 10 | return sit ? consectetur(ipsum) : [] 11 | } 12 | 13 | // focus(1:4) 14 | function adipiscing(...elit) { 15 | console.log(elit) 16 | return elit.map((ipsum) => ipsum.sit) 17 | } 18 | 19 | // focus[13:17] 20 | console.log("hey") 21 | ``` 22 | 23 | Same with other annotations like `mark`. 24 | 25 | ```js 26 | function foo() { 27 | // mark 28 | console.log("lorem ipsum") 29 | return 8 30 | } 31 | ``` 32 | 33 | You can pass a string parameter to comment annotations 34 | 35 | ```js index.js 36 | function lorem(ipsum, dolor = 1) { 37 | // mark(1:3) line-through 38 | const sit = ipsum == null && 0 39 | dolor = sit - amet(dolor) 40 | return sit ? consectetur(ipsum) : [] 41 | } 42 | 43 | function adipiscing(...elit) { 44 | console.log(elit) 45 | // withClass[19:38] line-through 46 | return elit.map((ipsum) => ipsum.sit) 47 | } 48 | ``` 49 | 50 | ## Links and labels 51 | 52 | And now we introduce two more annotations: `link` and `label` 53 | 54 | ```js focus=4,8 55 | function lorem(ipsum, dolor = 1) { 56 | const sit = ipsum == null && 0 57 | dolor = sit - amet(dolor) 58 | // link[16:26] https://github.com/code-hike/codehike 59 | return sit ? consectetur(ipsum) : [] 60 | } 61 | 62 | function adipiscing(...elit) { 63 | // label something something 64 | console.log("hover me") 65 | return elit.map((ipsum) => ipsum.sit) 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /data/demos/custom-annotations.mdx: -------------------------------------------------------------------------------- 1 | import MyThing from "./MyThing"; 2 | 3 | # Custom Annotations 4 | 5 | ```js app.js 6 | function lorem(ipsum, dolor = 1) { 7 | const sit = ipsum == null && 0; 8 | dolor = sit - amet(dolor); 9 | return sit ? consectetur(ipsum) : []; 10 | } 11 | 12 | function adipiscing(...elit) { 13 | const sit = ipsum == null && 0; 14 | dolor = sit - amet(dolor); 15 | console.log(2); 16 | return elit.map((ipsum) => ipsum.sit); 17 | } 18 | ``` 19 | 20 | 21 | 22 | ### Something 23 | 24 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 25 | 26 | 27 | -------------------------------------------------------------------------------- /data/demos/custom-annotations/MyThing.jsx: -------------------------------------------------------------------------------- 1 | import * as HoverCard from "@radix-ui/react-hover-card"; 2 | 3 | export default function MyTooltipAnnotation({ children, data, theme }) { 4 | const border = 5 | typeof data === "string" 6 | ? data 7 | : theme.tokenColors.find((tc) => tc.scope?.includes("string"))?.settings 8 | ?.foreground || "yellow"; 9 | 10 | return ( 11 | 12 | 13 | {children} 14 | 15 | 19 | 20 | {data?.children || "Hey"} 21 | 22 | 23 | ); 24 | return; 25 | } 26 | -------------------------------------------------------------------------------- /data/demos/filenames.mdx: -------------------------------------------------------------------------------- 1 | # Filenames 2 | 3 | Adding a filename to the codeblock 4 | 5 | ```js app.js 6 | function lorem(ipsum, dolor = 1) { 7 | const sit = ipsum == null ? 0 : ipsum.sit; 8 | dolor = sit - amet(dolor); 9 | return sit ? consectetur(ipsum) : []; 10 | } 11 | ``` 12 | 13 | ## Tabs 14 | 15 | To add more tabs, wrap multiple codeblocks with ``: 16 | 17 | 18 | 19 | ```js app.js 20 | function lorem(ipsum, dolor = 1) { 21 | const sit = ipsum == null ? 0 : ipsum.sit; 22 | dolor = sit - amet(dolor); 23 | return sit ? consectetur(ipsum) : []; 24 | } 25 | ``` 26 | 27 | ```css styles.css 28 | .lorem { 29 | color: #fff; 30 | padding: 10px; 31 | background: #000; 32 | } 33 | ``` 34 | 35 | 36 | 37 | ## Panels 38 | 39 | The _editor_ can be splitted vertically in two panels using `---` 40 | 41 | 42 | 43 | ```js app.js 44 | function lorem(ipsum, dolor = 1) { 45 | const sit = ipsum == null ? 0 : ipsum.sit; 46 | dolor = sit - amet(dolor); 47 | return sit ? consectetur(ipsum) : []; 48 | } 49 | ``` 50 | 51 | --- 52 | 53 | ```css styles.css 54 | .lorem { 55 | color: #fff; 56 | padding: 10px; 57 | background: #000; 58 | } 59 | ``` 60 | 61 | 62 | -------------------------------------------------------------------------------- /data/demos/index.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "code", 4 | "title": "Code and focus", 5 | "sponsors": [ 6 | "facebook", 7 | "fmvilas", 8 | "MatthiasZepper", 9 | "speakeasybot", 10 | "kickstartDS" 11 | ], 12 | "locked": false 13 | }, 14 | { 15 | "id": "filenames", 16 | "title": "Filenames and tabs", 17 | "sponsors": [ 18 | "yacinelakel", 19 | "molebox", 20 | "codecrafters-io", 21 | "kiliman", 22 | "zmzlois" 23 | ], 24 | "locked": false 25 | }, 26 | { 27 | "id": "meta-annotations", 28 | "title": "Annotations", 29 | "sponsors": [ 30 | "codeSTACKr", 31 | "hunterbecton", 32 | "n0lawz" 33 | ], 34 | "locked": true 35 | }, 36 | { 37 | "id": "comment-annotations", 38 | "title": "Comment annotations", 39 | "sponsors": [], 40 | "locked": true 41 | }, 42 | { 43 | "id": "sections", 44 | "title": "Code links", 45 | "sponsors": [ 46 | "traviscooper", 47 | "arosenkranz", 48 | "Naturalclar", 49 | "outerbounds", 50 | "ndimares" 51 | ], 52 | "locked": false 53 | }, 54 | { 55 | "id": "spotlight", 56 | "title": "Spotlight", 57 | "sponsors": [ 58 | "obgibson" 59 | ], 60 | "locked": true 61 | }, 62 | { 63 | "id": "spotlight-preview", 64 | "title": "Spotlight & preview", 65 | "sponsors": [ 66 | "brianespinosa", 67 | "drivly", 68 | "nathanclevenger" 69 | ], 70 | "locked": true 71 | }, 72 | { 73 | "id": "scrollycoding", 74 | "title": "Scrollycoding", 75 | "sponsors": [], 76 | "locked": true 77 | }, 78 | { 79 | "id": "scrollycoding-preview", 80 | "title": "Scrollycoding & preview", 81 | "sponsors": [], 82 | "locked": true 83 | }, 84 | { 85 | "id": "slideshow", 86 | "title": "Slideshow", 87 | "sponsors": [], 88 | "locked": true 89 | }, 90 | { 91 | "id": "slideshow-preview", 92 | "title": "Slideshow & preview", 93 | "sponsors": [], 94 | "locked": true 95 | } 96 | ] -------------------------------------------------------------------------------- /data/demos/meta-annotations.mdx: -------------------------------------------------------------------------------- 1 | # Annotations 2 | 3 | There are a few more annotations that can be used to highlight code. 4 | 5 | ### `mark` annotation 6 | 7 | ```js mark=1[10:14] 8 | function lorem(ipsum, dolor = 1) { 9 | const sit = 10 | ipsum == null ? 0 : ipsum.sit 11 | dolor = sit - amet(dolor) 12 | return dolor 13 | } 14 | ``` 15 | 16 | ```js mark=2:4 17 | function lorem(ipsum, dolor = 1) { 18 | const sit = 19 | ipsum == null ? 0 : ipsum.sit 20 | dolor = sit - amet(dolor) 21 | return dolor 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /data/demos/scrollycoding.mdx: -------------------------------------------------------------------------------- 1 | # Scrollycoding 2 | 3 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. 4 | 5 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. 6 | 7 | 8 | 9 | ## Step 1 10 | 11 | Lorem ipsum dolor sit amet, consectetur adipiscing something about points, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 12 | 13 | > Nova in illis at dabat legi harundine non, ova miratur? _Quid in_ sole aer 14 | > ad diffusa illis voluisti fidensque coniugiale laniata curam. Aras rivus 15 | > eripuit, qua fistula haec partus; serpens, negat. 16 | 17 | Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. 18 | 19 | ```js app.js focus=3:10 20 | const { lorem, ipsum } = dolor({ 21 | sit: { 22 | amet: 1, 23 | consectetur: 2, 24 | adipiscing: (elit) => ({ 25 | sed: elit, 26 | }), 27 | eiusmod: (tempor) => ({ 28 | incididunt: tempor, 29 | }), 30 | ut: (labore) => ({ 31 | et: labore, 32 | dolore: labore + 1, 33 | }), 34 | magna: (aliqua) => ({ 35 | ut: aliqua, 36 | }), 37 | nostrud: (elit) => ({ 38 | exercitation: elit, 39 | ullamco: elit, 40 | }), 41 | laboris: (elit) => ({ 42 | nisi: elit, 43 | }), 44 | }, 45 | }); 46 | ``` 47 | 48 | --- 49 | 50 | ## Step 2 51 | 52 | Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. 53 | 54 | Praesent elementum facilisis leo vel fringilla est ullamcorper eget. 55 | 56 | Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Nibh cras pulvinar mattis nunc sed. Luctus accumsan tortor posuere ac ut consequat semper viverra. Fringilla ut morbi tincidunt augue interdum velit euismod. 57 | 58 | Morbi quis commodo. 59 | 60 | ```js app.js focus=11:17 61 | 62 | ``` 63 | 64 | --- 65 | 66 | ## Step 3 67 | 68 | Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. 69 | 70 | - Nisi lacus sed viverra tellus in 71 | - Nibh cras pulvinar mattis nunc sed 72 | - Luctus accumsan tortor posuere ac 73 | 74 | Ut consequat semper viverra. Fringilla ut morbi tincidunt augue interdum velit euismod. 75 | 76 | ```js app.js focus=11:14 77 | const { lorem, ipsum } = dolor({ 78 | sit: { 79 | amet: 1, 80 | consectetur: 2, 81 | adipiscing: (elit) => ({ 82 | sed: elit, 83 | }), 84 | eiusmod: (tempor) => ({ 85 | incididunt: tempor, 86 | }), 87 | ut: (labore) => ({ 88 | et: lorem(labore * ipsum), 89 | dolore: lorem(labore + 1), 90 | }), 91 | nostrud: (elit) => ({ 92 | exercitation: elit, 93 | ullamco: elit, 94 | }), 95 | laboris: (elit) => ({ 96 | nisi: elit, 97 | }), 98 | }, 99 | }); 100 | ``` 101 | 102 | --- 103 | 104 | ## Step 4 105 | 106 | Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Venenatis cras sed felis eget velit. Consectetur libero id faucibus nisl tincidunt. 107 | 108 | Sed blandit libero volutpat sed cras. 109 | 110 | - Nisi lacus sed viverra tellus in 111 | - Nibh cras pulvinar mattis nunc sed 112 | 113 | Gravida in fermentum et sollicitudin ac orci phasellus egestas tellus. Volutpat consequat mauris nunc congue nisi vitae. 114 | 115 | ```js app.js focus=15:21 116 | 117 | ``` 118 | 119 | --- 120 | 121 | ## Step 5 122 | 123 | Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. 124 | 125 | Praesent elementum facilisis leo vel fringilla est ullamcorper eget. 126 | 127 | Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. 128 | 129 | Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Nibh cras pulvinar mattis nunc sed. Luctus accumsan tortor posuere ac ut consequat semper viverra. 130 | 131 | - Fringilla ut morbi tincidunt augue interdum velit euismod. 132 | - Luctus accumsan tortor posuere ac ut consequat semper viverra. 133 | 134 | Morbi quis commodo. 135 | 136 | ```js app.js 137 | 138 | ``` 139 | 140 | 141 | 142 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. 143 | 144 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. 145 | -------------------------------------------------------------------------------- /data/demos/sections.mdx: -------------------------------------------------------------------------------- 1 | # Code Links 2 | 3 | Lorem ipsum dolor sit amet. 4 | 5 | 6 | 7 | Consectetur adipiscing elit, sed do eiusmod tempor [incididunt](focus://4:7) ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. 8 | 9 | ```js 10 | function lorem(ipsum, dolor) { 11 | const sit = "lorem ipsum"; 12 | dolor = elit(dolor, 3); 13 | while (++consectetur < amet) { 14 | sit.eiusmod(150); 15 | tempor(ipsum, adipiscing); 16 | } 17 | } 18 | ``` 19 | 20 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum [dolore](focus://1[23:27],3) eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident. 21 | 22 | 23 | 24 | ## With multiple files 25 | 26 | 27 | 28 | Lorem dolor sit amet, [javascript](focus://index.js#2:3) adipiscing elit, sed do eiusmod [styles](focus://styles.css#2:3) incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 29 | 30 | 31 | 32 | ```js index.js 33 | function lorem(ipsum, dolor) { 34 | const sit = ipsum - amet(dolor); 35 | return sit + "lorem ipsum"; 36 | } 37 | ``` 38 | 39 | ```css styles.css 40 | .lorem-ipsum > .dolor pre { 41 | background-color: var(--color-bg); 42 | padding: 1em 0px 10vh 300px; 43 | } 44 | ``` 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /data/demos/slideshow.mdx: -------------------------------------------------------------------------------- 1 | # Slideshow 2 | 3 | This is how to use the `` component. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quia! Quidem, quisquam. 4 | 5 | 6 | 7 | ```rust 8 | pub fn add(a: i32, b: i32) -> i32 { 9 | a + b 10 | } 11 | 12 | #[allow(dead_code)] 13 | fn bad_add(a: i32, b: i32) -> i32 { 14 | a - b 15 | } 16 | ``` 17 | 18 | --- 19 | 20 | ```rust 21 | pub fn add(a: i32, b: i32) -> i32 { 22 | a + b 23 | } 24 | 25 | #[allow(dead_code)] 26 | fn bad_add(a: i32, b: i32) -> i32 { 27 | a - b 28 | } 29 | 30 | // focus(1:4) 31 | #[cfg(test)] 32 | mod tests { 33 | use super::*; 34 | } 35 | ``` 36 | 37 | --- 38 | 39 | ```rust 40 | // focus(1:3) 41 | pub fn add(a: i32, b: i32) -> i32 { 42 | a + b 43 | } 44 | 45 | #[allow(dead_code)] 46 | fn bad_add(a: i32, b: i32) -> i32 { 47 | a - b 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use super::*; 53 | // focus(1:4) 54 | #[test] 55 | fn test_add() { 56 | assert_eq!(add(1, 2), 3); 57 | } 58 | } 59 | ``` 60 | 61 | --- 62 | 63 | ```rust 64 | pub fn add(a: i32, b: i32) -> i32 { 65 | a + b 66 | } 67 | 68 | // focus(1:4) 69 | #[allow(dead_code)] 70 | fn bad_add(a: i32, b: i32) -> i32 { 71 | a - b 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | #[test] 78 | fn test_add() { 79 | assert_eq!(add(1, 2), 3); 80 | } 81 | // focus(1:4) 82 | #[test] 83 | fn test_bad_add() { 84 | assert_eq!(bad_add(1, 2), 3); 85 | } 86 | } 87 | ``` 88 | 89 | 90 | -------------------------------------------------------------------------------- /data/demos/spotlight-preview.mdx: -------------------------------------------------------------------------------- 1 | # Spotlight with preview 2 | 3 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. 4 | 5 | 6 | 7 | ```jsx app.js 8 | import Circle from "./circle"; 9 | const red = "hsl(0 85% 60%)"; 10 | export default function App() { 11 | return ( 12 |
13 | 14 |
15 | ); 16 | } 17 | ``` 18 | 19 | --- 20 | 21 | ## Lorem 22 | 23 | Lorem ipsum dolor sit amet, consectetur adipiscing something about points, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 24 | 25 | ```jsx app.js 26 | 27 | ``` 28 | 29 | --- 30 | 31 | ## Ipsum 32 | 33 | Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. 34 | 35 | ```jsx app.js focus=5:9 36 | import Circle from "./circle"; 37 | const red = "hsl(0 85% 60%)"; 38 | export default function App() { 39 | return ( 40 |
41 | 42 | 43 | 44 |
45 | ); 46 | } 47 | ``` 48 | 49 | --- 50 | 51 | ## Dolor sit 52 | 53 | Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. 54 | 55 | ```jsx app.js focus=2,6:8 56 | import Circle from "./circle"; 57 | const red = "hsl(0 85% 60%)"; 58 | export default function App() { 59 | return ( 60 |
61 | 62 | 63 | 64 |
65 | ); 66 | } 67 | ``` 68 | 69 | --- 70 | 71 | ## Amet 72 | 73 | Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Venenatis cras sed felis eget velit. Consectetur libero id faucibus nisl tincidunt. 74 | 75 | ```jsx app.js focus=8:10 76 | import Circle from "./circle"; 77 | const red = "hsl(0 85% 60%)"; 78 | const blue = "hsl(240 50% 60%)"; 79 | const gold = "hsl(60 60% 60%)"; 80 | export default function App() { 81 | return ( 82 |
83 | 84 | 85 | 86 |
87 | ); 88 | } 89 | ``` 90 | 91 |
92 | 93 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. 94 | 95 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. 96 | -------------------------------------------------------------------------------- /data/demos/spotlight.mdx: -------------------------------------------------------------------------------- 1 | # Spotlight 2 | 3 | This is how to use the `` component. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quia! Quidem, quisquam. 4 | 5 | 6 | 7 | ```js app.js 8 | function lorem(ipsum, dolor = 1) { 9 | const sit = ipsum == null && 0; 10 | dolor = sit - amet(dolor); 11 | return sit ? consectetur(ipsum) : []; 12 | } 13 | ``` 14 | 15 | --- 16 | 17 | Change focus 18 | 19 | ```js app.js focus=2:4 20 | 21 | ``` 22 | 23 | --- 24 | 25 | Or change the code 26 | 27 | ```js app.js focus=6:10 28 | function lorem(ipsum, dolor = 1) { 29 | const sit = ipsum == null && 0; 30 | dolor = sit - amet(dolor); 31 | return sit ? consectetur(ipsum) : []; 32 | } 33 | 34 | function adipiscing(...elit) { 35 | console.log(elit); 36 | return elit.map((ipsum) => ipsum.sit); 37 | } 38 | ``` 39 | 40 | --- 41 | 42 | Or change the file 43 | 44 | 45 | 46 | ```js app.js focus=1:4 47 | function adipiscing(...elit) { 48 | console.log(elit); 49 | return elit.map((ipsum) => ipsum.sit); 50 | } 51 | ``` 52 | 53 | --- 54 | 55 | ```css styles.css 56 | .lorem { 57 | color: #fff; 58 | padding: 10px; 59 | background: #000; 60 | } 61 | ``` 62 | 63 | 64 | 65 | --- 66 | 67 | ### By the way 68 | 69 | - you can 70 | - put any 71 | - markdown 72 | - here 73 | 74 | 👍 75 | 76 | ```js app.js 77 | 78 | ``` 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/ch-code.mdx: -------------------------------------------------------------------------------- 1 | With _``_ you can show code files in [tabs](#tabs) or [panels](#panels). You can also pass props to [override configuration](#override-configuration) or [customize styling](#styling-props). 2 | 3 | ## Tabs 4 | 5 | When you include more than one code blocks inside a _``_, Code Hike will show them as tabs. 6 | 7 | 8 | 9 | ```mdx your.mdx 10 | // from ../docs/preview/ch-code-1.mdx 11 | ``` 12 | 13 | 20 | 21 | 22 | 23 | ## Panels 24 | 25 | You can also show two files at the same time in two panels. Use a divider _`---`_ to separate the top tabs from the bottom tabs. 26 | 27 | 28 | 29 | ```mdx your.mdx focus=1,7,13 30 | // from ../docs/preview/ch-code-2.mdx 31 | ``` 32 | 33 | 40 | 41 | 42 | 43 | ## Override configuration 44 | 45 | You can override some of the [configuration](configuration) options by passing props to _``_. 46 | 47 | 48 | 49 | ```mdx your.mdx focus=1,9 mark=1[10:27] 50 | // from ../docs/preview/ch-code-3.mdx 51 | ``` 52 | 53 | 60 | 61 | 62 | 63 | ## Styling props 64 | 65 | Then there are the _`style`_ and _`className`_ props. You can use them to customize the styles of the component. 66 | 67 | 68 | 69 | ```mdx your.mdx focus=1,9 mark=1[10:30] 70 | // from ../docs/preview/ch-code-4.mdx 71 | ``` 72 | 73 | 80 | 81 | 82 | 83 | ## Height 84 | 85 | 86 | 87 | By default, `` will take the height needed to fit all the lines of the given code. As we saw in the previous example, you can use CSS to set a fixed height. 88 | 89 | Instead of setting the height with CSS, you can also use the _`rows`_ prop. You pass the number of lines you want to show, and Code Hike will set the height accordingly. 90 | 91 | 92 | 93 | ```mdx your.mdx focus=1 mark=1[10:17] 94 | // from ../docs/preview/ch-code-5.mdx 95 | ``` 96 | 97 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | In addition to the number of lines, you can also pass _`"focus"`_, then the height of the code will match the height of the focused lines. 112 | 113 | 114 | 115 | ```mdx your.mdx focus=1 mark=1[10:21] 116 | // from ../docs/preview/ch-code-6.mdx 117 | ``` 118 | 119 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/ch-scrollycoding.mdx: -------------------------------------------------------------------------------- 1 | **This feature is experimental.** 2 | 3 | For a sneak peek, see the demos: 4 | 5 | - [Scrollycoding demo](/demo/scrollycoding) 6 | - [Scrollycoding with preview demo](/demo/scrollycoding-preview) 7 | -------------------------------------------------------------------------------- /docs/ch-section.mdx: -------------------------------------------------------------------------------- 1 | Sometimes it is useful to reference code from a section of text. Since the code may be below or above the text, we need to wrap the text and the code inside an element: _``_. 2 | 3 | With the `` component you can reference code in two ways: 4 | 5 | - [Inline code](#inline-code) 6 | - [Code mentions](#code-mentions) 7 | 8 | ## Inline code 9 | 10 | With Code Hike, you have two options to render inline code: 11 | 12 | - You can use the standard markdown syntax where you wrap the code in backticks: `` `var x = 10` ``, which won't be syntax-highlighted. 13 | - Or use a special syntax where you wrap the code with underscores and backticks: _`` _`var x = 10`_ ``_, which will be syntax-highlighted as _`var x = 10`_. 14 | 15 | Usually, the syntax depends on the context and the language. If you use the special inline code inside a `` component, Code Hike will copy the highlighting from the code to the text: 16 | 17 | 18 | 19 | ```mdx your.mdx mark=8[11:30] 20 | // from ../docs/preview/ch-section-1.mdx 21 | ``` 22 | 23 | 30 | 31 | 32 | 33 | ## Code mentions 34 | 35 | Code mentions are a way to link code and text. We borrow the link syntax from markdown to create hoverable links to the code. It's easier to show than to explain: 36 | 37 | 38 | 39 | ```mdx your.mdx focus=1,8[8:34],10 40 | // from ../docs/preview/ch-section-2.mdx 41 | ``` 42 | 43 | 50 | 51 | 52 | 53 | The syntax is the same as a markdown link, but instead of an URL, you use `focus://` as the protocol, and then pass a [focus string](annotations#metastring-syntax). 54 | 55 | Code mentions are useful when you have more than one file. To specify the file, you prepend the name before the focus string and separate them with a `#`: 56 | 57 | 58 | 59 | ```mdx your.mdx focus=1,3,4,21 mark=3[20:34],4[20:33] 60 | // from ../docs/preview/ch-section-3.mdx 61 | ``` 62 | 63 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /docs/ch-slideshow.mdx: -------------------------------------------------------------------------------- 1 | **This feature is experimental.** 2 | 3 | For a sneak peek, see the demos: 4 | 5 | - [Slideshow demo](/demo/slideshow) 6 | - [Slideshow with preview demo](/demo/slideshow-preview) 7 | -------------------------------------------------------------------------------- /docs/ch-spotlight.mdx: -------------------------------------------------------------------------------- 1 | **This feature is experimental.** 2 | 3 | For a sneak peek, see the demos: 4 | 5 | - [Spotlight demo](/demo/spotlight) 6 | - [Spotlight with preview demo](/demo/spotlight-preview) 7 | -------------------------------------------------------------------------------- /docs/codeblocks.mdx: -------------------------------------------------------------------------------- 1 | The fundamental building block of Code Hike is the code block. There are several ways to write code blocks inside markdown. The most common way is to wrap the code using triple backticks: 2 | 3 | {/* prettier-ignore */} 4 | ````mdx lorem.mdx focus=3:5 5 | ## Lorem ipsum 6 | 7 | ``` 8 | print("Hello, world!") 9 | ``` 10 | 11 | dolor **sit** amet 12 | ```` 13 | 14 | ### Syntax highlighting 15 | 16 | If you specify a language after the ``` Code Hike will apply syntax highlighting to the code. 17 | 18 | 19 | 20 | {/* prettier-ignore */} 21 | ````mdx lorem.mdx focus=3:5 mark=3[4:9] 22 | ### Lorem ipsum 23 | 24 | ```python 25 | print("Hello, world!") 26 | ``` 27 | 28 | dolor **sit** amet 29 | ```` 30 | 31 | 39 | 40 | 41 | 42 | ### Languages 43 | 44 | 45 | 46 | ### Filename 47 | 48 | If you add a title after the language, Code Hike will show a different UI featuring the filename: 49 | 50 | 51 | 52 | {/* prettier-ignore */} 53 | ````mdx lorem.mdx focus=3:5 mark=3[11:18] 54 | ### Lorem ipsum 55 | 56 | ```python hello.py 57 | print("Hello, world!") 58 | ``` 59 | 60 | dolor **sit** amet 61 | ```` 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /docs/configuration.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | Somewhere in your code, you'll have a _`remarkPlugins`_ array, there you'll find Code Hike's [config object](focus://9:13). If you haven't already set Code Hike up, see the [installation docs](installation) first. 4 | 5 | {/* prettier-ignore */} 6 | ```js your.config.js 7 | const remarkSomething = require("remark-something") 8 | // focus 9 | const { remarkCodeHike } = require("@code-hike/mdx") 10 | 11 | mdxOptions = { 12 | remarkPlugins: [ 13 | // focus(1:12) 14 | [ 15 | remarkCodeHike, 16 | { 17 | // link[9:19] #line-numbers 18 | lineNumbers: false, 19 | // link[9:22] #copy-button 20 | showCopyButton: false, 21 | // link[9:13] #theme 22 | theme: "dark-plus", 23 | // link[9:21] #skip-languages 24 | skipLanguages: ["mermaid"], 25 | // link[9:24] #static-components 26 | staticMediaQuery: "not screen, (max-width: 768px)", 27 | // link[9:18] #auto-import 28 | autoImport: true, 29 | // link[9:16] #auto-link 30 | autoLink: false, 31 | }, 32 | ], 33 | remarkSomething, 34 | remarkSomethingElse, 35 | ], 36 | } 37 | ``` 38 | 39 | 40 | 41 | ## Line Numbers 42 | 43 | 44 | 45 | ## Copy Button 46 | 47 | To add a copy button to your code, set _`showCopyButton`_ to _`true`_. 48 | 49 | 50 | 51 | ```js your.config.js 52 | mdxOptions = { 53 | remarkPlugins: [ 54 | [ 55 | remarkCodeHike, 56 | { 57 | theme: someTheme, 58 | // focus 59 | // mark[25:28] 60 | showCopyButton: true, 61 | }, 62 | ], 63 | ], 64 | } 65 | ``` 66 | 67 | 68 | 69 | A common pattern is to have the copy button hidden by default, but show it when the user hovers over the code. You can do it with this CSS: 70 | 71 | 72 | 73 | ```css 74 | .ch-codeblock .ch-code-button { 75 | display: none; 76 | } 77 | 78 | .ch-codeblock:hover .ch-code-button { 79 | display: block; 80 | } 81 | ``` 82 | 83 | 84 | 85 | ## Theme 86 | 87 | See the [themes docs](themes) for more info. 88 | 89 | ## Skip Languages 90 | 91 | 92 | 93 | If you want Code Hike to skip certain languages, you can set _`skipLanguages`_. This is useful when you have other plugins that handle those languages, like [mermaid](https://github.com/mermaid-js/mermaid). 94 | 95 | ```js 96 | mdxOptions = { 97 | remarkPlugins: [ 98 | [ 99 | remarkCodeHike, 100 | { 101 | theme: someTheme, 102 | // focus 103 | skipLanguages: ["", "mermaid"], 104 | }, 105 | ], 106 | ], 107 | } 108 | ``` 109 | 110 | 111 | 112 | ## Static Components 113 | 114 | Some components, like `` have static versions more suitable for small screens or printing. You can choose the media query that triggers the static version. 115 | 116 | {/* prettier-ignore */} 117 | ```js 118 | mdxOptions = { 119 | remarkPlugins: [ 120 | [ 121 | remarkCodeHike, 122 | { 123 | theme: someTheme, 124 | // focus 125 | // mark[28:57] 126 | staticMediaQuery: "not screen, (max-width: 768px)", 127 | }, 128 | ], 129 | ], 130 | } 131 | ``` 132 | 133 | ## Auto Import 134 | 135 | 136 | 137 | By default, any Code Hike component used in your mdx files will be automatically imported. But some tools don't work well with imports in mdx files, so you can disable this feature by setting _`autoImport: false`_. 138 | 139 | ```js 140 | mdxOptions = { 141 | remarkPlugins: [ 142 | [ 143 | remarkCodeHike, 144 | { 145 | theme: someTheme, 146 | // focus 147 | // mark[21:25] 148 | autoImport: false, 149 | }, 150 | ], 151 | ], 152 | } 153 | ``` 154 | 155 | 156 | 157 | Then you'll need to pass Code Hike components as a prop like this: 158 | 159 | ```js 160 | import Example from "./example.mdx" 161 | import { CH } from "@code-hike/mdx/components" 162 | 163 | function App() { 164 | // mark[19:37] 165 | return 166 | } 167 | ``` 168 | 169 | ## Auto Link 170 | 171 | 172 | 173 | You can set _`autoLink: true`_ to automatically turn any URL in your code into a link. 174 | 175 | ```js 176 | mdxOptions = { 177 | remarkPlugins: [ 178 | [ 179 | remarkCodeHike, 180 | { 181 | // focus 182 | // mark[19:22] 183 | autoLink: true, 184 | }, 185 | ], 186 | ], 187 | } 188 | ``` 189 | 190 | 191 | -------------------------------------------------------------------------------- /docs/configuration/line-numbers.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | Line numbers are turned off by default (I'm not a fan of line numbers). You can turn them on by setting _`lineNumbers: true`_. 4 | 5 | ```js 6 | mdxOptions = { 7 | remarkPlugins: [ 8 | [ 9 | remarkCodeHike, 10 | { 11 | theme: someTheme, 12 | // focus 13 | // mark[22:25] 14 | // label but do you really need line numbers? 15 | lineNumbers: true, 16 | }, 17 | ], 18 | ], 19 | } 20 | ``` 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/configuration/theme.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js lorem.js 4 | // lorem ipsum dolor sit 5 | function lorem(ipsum, dolor = 1) { 6 | const sit = ipsum == null 7 | // mark 8 | dolor = sit - amet(dolor) 9 | return "consectetur" 10 | } 11 | ``` 12 | 13 | ```css styles/ipsum.css 14 | html, 15 | body { 16 | margin: 0; 17 | font-size: 16px; 18 | font-family: sans-serif; 19 | } 20 | ``` 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/installation-astro.mdx: -------------------------------------------------------------------------------- 1 | Not sure what's the state of MDX v2 support with Astro. But I guess it's possible to [build a custom renderer](https://docs.astro.build/en/reference/renderer-reference/#building-your-own-renderer) for MDX v2. Contributions to [the examples repo](https://github.com/code-hike/examples) are welcome. 2 | -------------------------------------------------------------------------------- /docs/installation-cra.mdx: -------------------------------------------------------------------------------- 1 | Not working. See this [pull request](https://github.com/code-hike/examples/pull/2). 2 | -------------------------------------------------------------------------------- /docs/installation-docspage.mdx: -------------------------------------------------------------------------------- 1 | ## docs.page + Code Hike 2 | 3 | [Docs.page](https://docs.page/) generates docs directly from your GitHub repository. 4 | 5 | You can [use it together with Code Hike](https://twitter.com/pomber/status/1501972442432122880) using an [experimental flag](https://github.com/pomber/docs-page-demo/blob/main/docs.json#L7). 6 | 7 | Guide coming soon. 8 | -------------------------------------------------------------------------------- /docs/installation-eleventy.mdx: -------------------------------------------------------------------------------- 1 | See this [pull request](https://github.com/code-hike/examples/pull/1). 2 | -------------------------------------------------------------------------------- /docs/installation-gatsby.mdx: -------------------------------------------------------------------------------- 1 | ## Gatsby + Code Hike 2 | 3 | See [this example](https://github.com/code-hike/codehike/tree/next/examples/gatsby) or try it on [CodeSandbox](https://codesandbox.io/s/github/code-hike/codehike/tree/main/examples/gatsby?file=/src/pages/index.mdx). 4 | 5 | Gatsby + MDX docs: [https://www.gatsbyjs.com/docs/how-to/routing/mdx/](https://www.gatsbyjs.com/docs/how-to/routing/mdx/) 6 | -------------------------------------------------------------------------------- /docs/installation-mdx-bundler.mdx: -------------------------------------------------------------------------------- 1 | ## Next.js + MDX Bundler + Code Hike 2 | 3 | See this [example](https://github.com/code-hike/codehike/tree/next/examples/mdx-bundler). Guide coming soon. 4 | -------------------------------------------------------------------------------- /docs/installation-motif.mdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/docs/installation-motif.mdx -------------------------------------------------------------------------------- /docs/installation-next-mdx-remote.mdx: -------------------------------------------------------------------------------- 1 | ## Next MDX Remote + Code Hike 2 | 3 | _Based on [Next.js official docs](https://nextjs.org/docs/advanced-features/using-mdx) and [next-mdx-remote](https://github.com/hashicorp/next-mdx-remote)._ 4 | 5 | Start by installing next and react on an empty directory: 6 | 7 | 8 | 9 | ```bash 10 | npm install next react react-dom 11 | ``` 12 | 13 | 14 | 15 | 16 | 17 | Then also install the [next-mdx-remote](focus://1[7:21]) plugin. 18 | 19 | 20 | 21 | ```bash 22 | npm i next-mdx-remote 23 | ``` 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | First, you need to create a `pages/_app.js` file if you don't have one. 34 | 35 | The _`MyApp`_ component is where you put global stuff that applies to all pages. 36 | 37 | You can find more information about the `_app.js` file in the [Next.js official docs](https://nextjs.org/docs/advanced-features/custom-app). 38 | 39 | {/* prettier-ignore */} 40 | ```js pages/_app.js 41 | function MyApp({ Component, pageProps }) { 42 | return 43 | } 44 | 45 | export default MyApp 46 | ``` 47 | 48 | --- 49 | 50 | We use `pages/_app.js` file to import Code Hike's stylesheet. 51 | 52 | If you want to customize Code Hike's styles with a global stylesheet make sure to import it after this import to avoid specificity issues. 53 | 54 | You can learn more about customizing Code Hike styles in the [styling docs](/docs/styling). 55 | 56 | {/* prettier-ignore */} 57 | ```js pages/_app.js focus=1 58 | import "@code-hike/mdx/dist/index.css" 59 | 60 | function MyApp({ Component, pageProps }) { 61 | return 62 | } 63 | 64 | export default MyApp 65 | ``` 66 | 67 | --- 68 | 69 | Next, create a page for rendering your MDX content. 70 | 71 | You can learn more about parsing MDX content from remote locations in the [next-mdx-remote docs](https://github.com/hashicorp/next-mdx-remote); 72 | 73 | ```js pages/content.js 74 | import { serialize } from "next-mdx-remote/serialize" 75 | import { MDXRemote } from "next-mdx-remote" 76 | 77 | import Test from "../components/test" 78 | 79 | const components = {} 80 | 81 | export default function TestPage({ 82 | source, 83 | }) { 84 | return ( 85 |
86 | 90 |
91 | ) 92 | } 93 | 94 | export async function getStaticProps() { 95 | // MDX text - can be from a local file, database, anywhere 96 | const source = 97 | "Some **mdx** text,

using an HTML element

" 98 | const mdxSource = await serialize( 99 | source 100 | ) 101 | return { 102 | props: { source: mdxSource }, 103 | } 104 | } 105 | ``` 106 | 107 | --- 108 | 109 | To set up Code Hike you need to import the ` @code-hike/mdx`` plugin, and add it to the `remarkPlugins``array in the`serialize` function. 110 | 111 | Next to the plugin you can pass [a config object](focus://10[24:32]). Almost always you'll want to pass a `theme` there. For more information about themes, see the [themes docs](/docs/themes). 112 | 113 | You can also pass more options, like `lineNumbers` for example. See the [configuration docs](/docs/configuration) for more information. 114 | 115 | ```js pages/content.js focus=3:4,6,19:24 116 | import { serialize } from "next-mdx-remote/serialize" 117 | import { MDXRemote } from "next-mdx-remote" 118 | import { remarkCodeHike } from "@code-hike/mdx" 119 | import { CH } from "@code-hike/mdx/components" 120 | 121 | const components = { CH } 122 | 123 | export default function TestPage({ 124 | source, 125 | }) { 126 | return ( 127 |
128 | 132 |
133 | ) 134 | } 135 | 136 | export async function getStaticProps() { 137 | // MDX text - can be from a local file, database, anywhere 138 | const source = 139 | "Some **mdx** text,

using an HTML element

" 140 | const mdxSource = await serialize( 141 | source, 142 | { 143 | mdxOptions: { 144 | remarkPlugins: [ 145 | [ 146 | remarkCodeHike, 147 | { 148 | autoImport: false, 149 | theme: "material-default", 150 | }, 151 | ], 152 | ], 153 | useDynamicImport: true, 154 | }, 155 | } 156 | ) 157 | return { 158 | props: { source: mdxSource }, 159 | } 160 | } 161 | ``` 162 | 163 | --- 164 | 165 | And now you can import mdx files from anywhere. 166 | 167 | For examples on importing files from your local file system or a database, refer to the `next-mdx-remote` [docs](https://github.com/hashicorp/next-mdx-remote). 168 | 169 |
170 | 171 |
172 | 173 | A demo of Code Hike + NextJS is available on [GitHub](https://github.com/code-hike/codehike/tree/next/examples/next-mdx-remote). You can also try it out from your browser on [StackBlitz](https://github.com/code-hike/codehike/tree/next/examples/next-mdx-remote?file=pages%2Findex.mdx). 174 | 175 |
176 | -------------------------------------------------------------------------------- /docs/installation-nextjs.mdx: -------------------------------------------------------------------------------- 1 | ## Next.js + Code Hike 2 | 3 | _Based on [Next.js official docs](https://nextjs.org/docs/advanced-features/using-mdx)._ 4 | 5 | Start by installing next and react on an empty directory: 6 | 7 | 8 | 9 | ```bash 10 | npm install next react react-dom 11 | ``` 12 | 13 | 14 | 15 | 16 | 17 | Then also install the [mdx plugin for next](focus://1[13:21]), the [mdx loader](focus://1[23:36]), and [Code Hike](focus://1[38:56]). 18 | 19 | 20 | 21 | ```bash 22 | npm install @next/mdx @mdx-js/loader @code-hike/mdx 23 | ``` 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | Create a `next.config.js` file at the root of your project. 34 | 35 | We use the _`@next/mdx`_ plugin to [set up MDX imports](focus://1:6,8,13). 36 | 37 | Inside the _`withMDX`_ function we pass the Next JS config. Make sure to [include `"md"` and `"mdx"`](focus://9,11[12:22],12) on the _`pageExtensions`_ setting if you want to write MDX files directly in your `pages` directory. 38 | 39 | After this step, you can use MDX files in your project, but you can't use Code Hike yet. 40 | 41 | {/* prettier-ignore */} 42 | ```js next.config.js 43 | const withMDX = require("@next/mdx")({ 44 | extension: /\.mdx?$/, 45 | options: { 46 | remarkPlugins: [], 47 | }, 48 | }) 49 | 50 | module.exports = withMDX({ 51 | pageExtensions: [ 52 | "ts", "tsx", "js", 53 | "jsx", "md", "mdx" 54 | ], 55 | }) 56 | ``` 57 | 58 | --- 59 | 60 | To set up Code Hike you need to import the _`@code-hike/mdx`_ plugin, and add it to the _`remarkPlugins`_ array in the `next.config.js` file. 61 | 62 | Next to the plugin you can pass [a config object](focus://10[24:32]). Almost always you'll want to pass a `theme` there. For more information about themes, see the [themes docs](/docs/themes). 63 | 64 | You can also pass more options, like `lineNumbers` for example. See the [configuration docs](/docs/configuration) for more information. 65 | 66 | {/* prettier-ignore */} 67 | ```js next.config.js focus=1:3,8:10 68 | const { 69 | remarkCodeHike, 70 | } = require("@code-hike/mdx") 71 | 72 | const withMDX = require("@next/mdx")({ 73 | extension: /\.mdx?$/, 74 | options: { 75 | remarkPlugins: [ 76 | [remarkCodeHike, { theme: "nord" }] 77 | ], 78 | }, 79 | }) 80 | 81 | module.exports = withMDX({ 82 | pageExtensions: [ 83 | "ts", "tsx", "js", 84 | "jsx", "md", "mdx" 85 | ], 86 | }) 87 | ``` 88 | 89 | --- 90 | 91 | Then you need to create a `pages/_app.js` file if you don't have one. 92 | 93 | The _`MyApp`_ component is where you put global stuff that applies to all pages. 94 | 95 | You can find more information about the `_app.js` file in the [Next.js official docs](https://nextjs.org/docs/advanced-features/custom-app). 96 | 97 | {/* prettier-ignore */} 98 | ```js pages/_app.js 99 | function MyApp({ Component, pageProps }) { 100 | return 101 | } 102 | 103 | export default MyApp 104 | ``` 105 | 106 | --- 107 | 108 | We use `pages/_app.js` file to import Code Hike's stylesheet. 109 | 110 | If you want to customize Code Hike's styles with a global stylesheet make sure to import it after this import to avoid specificity issues. 111 | 112 | You can learn more about customizing Code Hike styles in the [styling docs](/docs/styling). 113 | 114 | {/* prettier-ignore */} 115 | ```js pages/_app.js focus=1 116 | import "@code-hike/mdx/dist/index.css" 117 | 118 | function MyApp({ Component, pageProps }) { 119 | return 120 | } 121 | 122 | export default MyApp 123 | ``` 124 | 125 | --- 126 | 127 | And now you should be able to use Code Hike inside your mdx files. 128 | 129 | For example, create an `index.mdx` file in your `pages` directory, and run Next with `npx next`. 130 | 131 | If you open _`localhost:3000`_ on your browser you should see Code Hike's syntax highlighting. 132 | 133 | Markdown (.md) files should also work. 134 | 135 | {/* prettier-ignore */} 136 | ~~~md pages/index.mdx 137 | # Hello 138 | 139 | Lorem ipsum dolor sit amet. 140 | 141 | ```python hello.py 142 | print("Rendered with Code Hike") 143 | ``` 144 | 145 | Lorem ipsum dolor sit amet. 146 | ~~~ 147 | 148 | 149 | 150 |
151 | 152 | A demo of Code Hike + NextJS is available on [GitHub](https://github.com/code-hike/codehike/tree/main/examples/nextjs). You can also try it out from your browser on [StackBlitz](https://stackblitz.com/github/code-hike/codehike/tree/main/examples/nextjs?file=pages%2Findex.mdx). 153 | 154 |
155 | -------------------------------------------------------------------------------- /docs/installation-nextra.mdx: -------------------------------------------------------------------------------- 1 | ## Nextra + Code Hike 2 | 3 | See this [example](https://github.com/code-hike/codehike/tree/next/examples/nextra). Guide coming soon. 4 | -------------------------------------------------------------------------------- /docs/installation-parcel.mdx: -------------------------------------------------------------------------------- 1 | See this [pull request](https://github.com/code-hike/examples/pull/3). 2 | -------------------------------------------------------------------------------- /docs/installation-remix.mdx: -------------------------------------------------------------------------------- 1 | ## Remix + Code Hike 2 | 3 | See this [example](https://github.com/code-hike/codehike/tree/next/examples/remix). Guide coming soon. 4 | -------------------------------------------------------------------------------- /docs/installation-vite.mdx: -------------------------------------------------------------------------------- 1 | ## Vite + Code Hike 2 | 3 | See this [example](https://github.com/code-hike/codehike/tree/next/examples/vite). Guide coming soon. 4 | -------------------------------------------------------------------------------- /docs/installation.mdx: -------------------------------------------------------------------------------- 1 | Code Hike is a remark plugin for [MDX v2](https://mdxjs.com/). The specific set up will depend on [your stack](#frameworks), it usually involves five steps: 2 | 3 | 4 | 5 | **1.** Set up MDX v2 6 | 7 | See the [MDX v2 documentation](https://mdxjs.com/docs/getting-started/). 8 | 9 | 10 | 11 | 12 | **2.** Install the Code Hike plugin 13 | 14 | 15 | 16 | ```bash 17 | npm install @code-hike/mdx 18 | ``` 19 | 20 | 21 | 22 | or 23 | 24 | 25 | 26 | ```bash 27 | yarn add @code-hike/mdx 28 | ``` 29 | 30 | 31 | 32 | 33 | 34 | 35 | **3.** Include Code Hike's CSS 36 | 37 | Depending on your stack, it could be: 38 | 39 | 40 | 41 | ```js your.js 42 | import "@code-hike/mdx/dist/index.css" 43 | ``` 44 | 45 | 46 | 47 | 48 | 49 | 50 | **4.** Add the remark plugin to your MDX configuration 51 | 52 | Also depends on your stack. Find the right place to pass `remarkPlugins`. 53 | 54 | {/* prettier-ignore */} 55 | ```js your.config.js focus=2,5,6,9 56 | const remarkSomething = require("remark-something"); 57 | const { remarkCodeHike } = require("@code-hike/mdx"); 58 | 59 | mdxOptions = { 60 | remarkPlugins: [ 61 | [remarkCodeHike, { theme: "github-dark", lineNumbers: false }], 62 | remarkSomething, 63 | remarkSomethingElse, 64 | ], 65 | }; 66 | ``` 67 | 68 | 69 | 70 | 71 | **5.** Copy and try one of the [demos](https://codehike.org/#demos) to test the set up. 72 | 73 | For example: 74 | 75 | {/* prettier-ignore */} 76 | ````mdx your.mdx 77 | # Hello, world! 78 | 79 | 80 | 81 | ```js app.js focus=2:4 82 | function lorem(ipsum, dolor = 1) { 83 | const sit = ipsum == null ? 0 : ipsum.sit; 84 | dolor = sit - amet(dolor); 85 | return sit ? consectetur(ipsum) : []; 86 | } 87 | ``` 88 | 89 | ```python hello.py 90 | print("Hello, world!") 91 | ``` 92 | 93 | --- 94 | 95 | ```css styles.css 96 | .lorem { 97 | color: #fff; 98 | padding: 10px; 99 | background: #000; 100 | } 101 | ``` 102 | 103 | 104 | ```` 105 | 106 | 107 | 108 | ## Frameworks 109 | 110 | Installation guides for specific frameworks. Pick one: 111 | -------------------------------------------------------------------------------- /docs/introduction.mdx: -------------------------------------------------------------------------------- 1 | Code Hike is a remark plugin for MDX. It will help you display code on your websites. 2 | 3 | - Display code blocks, using **VS Code themes** for syntax highlighting and support for [ programming languages](codeblocks#languages). 4 | - Use Code Hike **components and annotations** to enhance the **code-reading experience** and embrace the web as the interactive medium it is. 5 | - Good-looking **default styles**. Customizable using CSS. 6 | - And always keeping the MDX syntax as close to markdown as possible for a better **authoring experience**. 7 | 8 | ## Limitations 9 | 10 | - It only works with MDX v2. 11 | - Even when you can use MDX with frameworks like Vue or Svelte, Code Hike can only be used with React. 12 | - Code Hike components are meant to be used from MDX files. It's a bad idea to import them directly from React code. 13 | -------------------------------------------------------------------------------- /docs/preview/ch-code-1.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python one.py 4 | print("Hello, one!") 5 | ``` 6 | 7 | ```python two.py 8 | print("Hello, two!") 9 | ``` 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/preview/ch-code-2.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python one.py 4 | print("Hello, one!") 5 | ``` 6 | 7 | --- 8 | 9 | ```python two.py 10 | print("Hello, two!") 11 | ``` 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/preview/ch-code-3.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python one.py 4 | print("Hello, world!") 5 | print("Hello, world!") 6 | print("Hello, world!") 7 | ``` 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/preview/ch-code-4.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python one.py 4 | print("Hello, world!") 5 | print("Hello, world!") 6 | print("Hello, world!") 7 | ``` 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/preview/ch-code-5.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python one.py focus=2 4 | print("Hello, world 1!") 5 | print("Hello, world 2!") 6 | print("Hello, world 3!") 7 | print("Hello, world 4!") 8 | print("Hello, world 5!") 9 | ``` 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/preview/ch-code-6.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python one.py focus=2:3 4 | print("Hello, world 1!") 5 | print("Hello, world 2!") 6 | print("Hello, world 3!") 7 | print("Hello, world 4!") 8 | print("Hello, world 5!") 9 | ``` 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/preview/ch-section-1.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python 4 | def lorem(ipsum): 5 | ipsum + 1 6 | ``` 7 | 8 | Something _`def lorem(ipsum)`_ 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/preview/ch-section-2.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```python 4 | def lorem(ipsum): 5 | ipsum + 1 6 | ``` 7 | 8 | Hello, [hover me](focus://1[5:16]) 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/preview/ch-section-3.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | [lorem js](focus://two.js#1[10:21]) 4 | [lorem py](focus://one.py#1[5:16]) 5 | 6 | 7 | 8 | ```python one.py 9 | def lorem(ipsum): 10 | ipsum + 1 11 | ``` 12 | 13 | ```js two.js 14 | function lorem(ipsum) { 15 | return ipsum + 1 16 | } 17 | ``` 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/preview/codeblocks-1.mdx: -------------------------------------------------------------------------------- 1 | ### Lorem ipsum 2 | 3 | ```python 4 | print("Hello, world!") 5 | ``` 6 | 7 | dolor **sit** amet 8 | -------------------------------------------------------------------------------- /docs/preview/codeblocks-2.mdx: -------------------------------------------------------------------------------- 1 | ### Lorem ipsum 2 | 3 | ```python hello.py 4 | print("Hello, world!") 5 | ``` 6 | 7 | dolor **sit** amet 8 | -------------------------------------------------------------------------------- /docs/preview/installation-1.mdx: -------------------------------------------------------------------------------- 1 | ### Lorem ipsum 2 | 3 | ```python hello.py 4 | # mark[16:24] 5 | print("This is Code Hike") 6 | ``` 7 | 8 | Lorem ipsum dolor sit amet. 9 | -------------------------------------------------------------------------------- /docs/preview/mark-1.mdx: -------------------------------------------------------------------------------- 1 | ```py 2 | print("This is line 1") 3 | # mark 4 | print("This is line 2") 5 | print("This is line 3") 6 | ``` 7 | -------------------------------------------------------------------------------- /docs/preview/mark-2.mdx: -------------------------------------------------------------------------------- 1 | ```py 2 | # mark[7:11] 3 | class Lorem: 4 | def dolor(self): 5 | # mark[5:10] 6 | return "sit" 7 | ``` 8 | -------------------------------------------------------------------------------- /docs/preview/mark-3.mdx: -------------------------------------------------------------------------------- 1 | ```py 2 | # mark[16:24] my-colors 3 | print("This is Code Hike") 4 | ``` 5 | -------------------------------------------------------------------------------- /docs/preview/with-class.mdx: -------------------------------------------------------------------------------- 1 | ```py 2 | # withClass[7:16] my-class 3 | print("hover me") 4 | ``` 5 | -------------------------------------------------------------------------------- /docs/styling.mdx: -------------------------------------------------------------------------------- 1 | Styling docs are coming soon. 2 | 3 | In the meantime, here are two examples of styling Code Hike components to look very different: 4 | 5 | 6 | 7 | [![Stripe Docs Clone](/not-stripe.png)](https://not-stripe.vercel.app/) 8 | 9 |
10 | 11 | ### Stripe docs clone 12 | 13 | - [Demo](https://not-stripe.vercel.app/) 14 | - [Code](https://github.com/code-hike/not-stripe) 15 | - [Video](https://www.youtube.com/watch?v=W2rJ7p-G63c) 16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | [![Tailwind's Home Page Clone](/not-tailwind.png)](https://not-tailwind.vercel.app/) 24 | 25 |
26 | 27 | ### Tailwind's home page clone 28 | 29 | - [Demo](https://not-tailwind.vercel.app/) 30 | - [Code](https://github.com/code-hike/not-tailwind) 31 | - [Video](https://www.youtube.com/watch?v=sPDvKlmUkC4) 32 | 33 |
34 | 35 |
36 | -------------------------------------------------------------------------------- /docs/themes.mdx: -------------------------------------------------------------------------------- 1 | 2 | 3 | The `theme` option accepts a `string` for built-in themes or an `object` for custom themes. 4 | 5 | {/* prettier-ignore */} 6 | ```js config.js 7 | import { remarkCodeHike } from "@code-hike/mdx" 8 | 9 | // focus 10 | // config depends on the framework you're using 11 | mdxOptions = { 12 | remarkPlugins: [ 13 | // focus[22:43] 14 | // mark[32:40] 15 | [remarkCodeHike, { theme: "dark-plus" }], 16 | anotherPlugin, 17 | ], 18 | } 19 | ``` 20 | 21 | 22 | 23 | ## Built in themes 24 | 25 | 26 | 27 | ## Light/Dark mode 28 | 29 | There are also two built-in themes that support light/dark mode using CSS: 30 | 31 | 32 | 33 | To use them you need to include the colors as CSS variables. You can copy the CSS from here: 34 | 35 | - [`github-from-css` colors](https://github.com/code-hike/lighter/blob/main/lib/themes/github-from-css.css) 36 | - [`material-from-css` colors](https://github.com/code-hike/lighter/blob/main/lib/themes/material-from-css.css) 37 | 38 | and adapt it to your needs by changing the CSS selector. 39 | 40 | ## Custom themes and VS Code themes 41 | 42 | You can use the [Theme Editor](https://themes.codehike.org/editor) to customize any of the built-in themes or any theme from the VS Code marketplace. 43 | 44 | Once you have your theme, import it in your config file: 45 | 46 | {/* prettier-ignore */} 47 | ```js config.js 48 | import { remarkCodeHike } from "@code-hike/mdx" 49 | // focus 50 | import myTheme from "./my-theme" 51 | 52 | mdxOptions = { 53 | remarkPlugins: [ 54 | // focus[22:39] 55 | [remarkCodeHike, { theme: myTheme }], 56 | anotherPlugin, 57 | ], 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/troubleshooting.mdx: -------------------------------------------------------------------------------- 1 | ### Can I have different themes for dark and light mode? 2 | 3 | Yes. See [themes](/docs/themes). 4 | 5 | ### Do you have questions or issues with Code Hike? 6 | 7 | If you need help or have any questions, go to [Code Hike's discussions](https://github.com/code-hike/codehike/discussions) or [Discord](http://discord.codehike.org). 8 | -------------------------------------------------------------------------------- /global.css: -------------------------------------------------------------------------------- 1 | /* docs frameworks */ 2 | 3 | .docs .frameworks { 4 | --ch-scrollycoding-sticker-width: 390px; 5 | --ch-scrollycoding-code-min-height: 460px; 6 | } 7 | 8 | .docs .frameworks .ch-scrollycoding-step-content { 9 | margin-left: -1rem; 10 | padding-left: 1rem; 11 | padding-right: 1rem; 12 | } 13 | 14 | .docs .frameworks .ch-scrollycoding-sticker { 15 | margin-right: -1rem; 16 | } 17 | 18 | .docs .configuration-themes { 19 | display: grid; 20 | grid-template-columns: repeat(2, 1fr); 21 | grid-column-gap: 48px; 22 | /* grid-row-gap: 16px; */ 23 | } 24 | .docs .configuration-themes > .ch-codegroup { 25 | margin-top: 0px !important; 26 | margin-bottom: 0px !important; 27 | } 28 | 29 | .line-through * { 30 | text-decoration: line-through; 31 | } 32 | 33 | .hide-copy .ch-code-button { 34 | display: none; 35 | } 36 | 37 | .hide-copy:hover .ch-code-button { 38 | display: block; 39 | } 40 | 41 | /* docs collapsable */ 42 | 43 | @keyframes open-collapsable { 44 | from { 45 | height: 0; 46 | opacity: 0; 47 | overflow: hidden; 48 | } 49 | 50 | to { 51 | height: var(--radix-collapsible-content-height); 52 | overflow: hidden; 53 | opacity: 1; 54 | } 55 | } 56 | 57 | @keyframes close-collapsable { 58 | from { 59 | opacity: 1; 60 | height: var(--radix-collapsible-content-height); 61 | overflow: hidden; 62 | } 63 | 64 | to { 65 | height: 0; 66 | opacity: 0; 67 | overflow: hidden; 68 | } 69 | } 70 | 71 | .docs .collapsable-content[data-state="open"] { 72 | margin: -1px -2rem; 73 | padding: 1px 2rem; 74 | animation: open-collapsable 300ms ease-out; 75 | } 76 | .docs .collapsable-content[data-state="closed"] { 77 | overflow: hidden; 78 | margin: -1px -2rem; 79 | padding: 1px 2rem; 80 | animation: close-collapsable 300ms ease-out; 81 | } 82 | 83 | /* docs */ 84 | 85 | .docs main > :not(.configuration-themes, .theme-list) .ch-frame-button { 86 | border-color: #5c5c5c !important; 87 | background-color: #535353 !important; 88 | } 89 | 90 | .docs .ch-codeblock, 91 | .docs .ch-codegroup { 92 | margin-left: -16px !important; 93 | margin-right: -16px !important; 94 | } 95 | 96 | .docs :is(.ch-spotlight, .ch-scrollycoding) :is(.ch-codeblock, .ch-codegroup) { 97 | margin-left: 0 !important; 98 | margin-right: 0 !important; 99 | } 100 | 101 | .docs 102 | *:is(.ch-cols, .ch-spotlight, .ch-scrollycoding) 103 | :is(.ch-codeblock, .ch-codegroup, .ch-preview) { 104 | margin-top: 0 !important; 105 | margin-bottom: 0 !important; 106 | } 107 | 108 | .docs .ch-spotlight-sticker { 109 | width: 280px; 110 | } 111 | 112 | :target:before { 113 | content: ""; 114 | display: block; 115 | height: 4rem; 116 | margin: -4rem 0 0; 117 | pointer-events: none; 118 | } 119 | 120 | .prose .anchor { 121 | visibility: hidden; 122 | position: absolute; 123 | margin-left: 0.4em; 124 | cursor: pointer; 125 | opacity: 0; 126 | transition: opacity 0.2s ease-in-out; 127 | } 128 | 129 | .prose :is(h1, h2, h3, h4, h5, h6):hover .anchor { 130 | visibility: visible; 131 | text-decoration: none; 132 | opacity: 0.8; 133 | } 134 | 135 | /* for rehype-autolink-headings */ 136 | .prose .anchor .icon-link:after { 137 | content: "#"; 138 | } 139 | 140 | /* home */ 141 | 142 | body { 143 | overflow: overlay; 144 | } 145 | 146 | .source pre { 147 | white-space: pre-wrap; 148 | } 149 | 150 | .home-demo .ch-frame-button { 151 | background: #d1d5db; 152 | border-color: #d1d5db; 153 | } 154 | 155 | .home-demo .ch-editor-frame, 156 | .home-demo .ch-frame { 157 | @apply shadow-lg; 158 | border-radius: 6px; 159 | overflow: hidden; 160 | } 161 | 162 | .home-demo .ch-frame-title-bar { 163 | background: #f6f6f6; 164 | } 165 | 166 | .ch-mini-browser a { 167 | color: unset !important; 168 | } 169 | 170 | .prose > style:first-child + h3 { 171 | margin-top: 0; 172 | } 173 | /* unreset */ 174 | 175 | .unreset a { 176 | @apply text-blue-700 underline; 177 | } 178 | .unreset p { 179 | @apply my-4; 180 | } 181 | 182 | .unreset hr { 183 | @apply border; 184 | } 185 | 186 | .unreset h1 { 187 | @apply text-4xl font-bold my-2; 188 | } 189 | 190 | .unreset h2 { 191 | @apply text-2xl font-bold my-3; 192 | } 193 | 194 | .unreset h3 { 195 | @apply text-lg font-bold my-4; 196 | } 197 | 198 | .unreset h4 { 199 | @apply text-base font-bold my-5; 200 | } 201 | 202 | .unreset h5 { 203 | @apply text-sm font-bold my-6; 204 | } 205 | 206 | .unreset h6 { 207 | @apply text-xs font-bold my-10; 208 | } 209 | 210 | .unreset ol { 211 | @apply list-decimal my-4 pl-10; 212 | } 213 | 214 | .unreset dd { 215 | @apply pl-10; 216 | } 217 | 218 | .unreset dl { 219 | @apply my-4; 220 | } 221 | 222 | .unreset legend { 223 | @apply py-0 px-1; 224 | } 225 | 226 | .unreset fieldset { 227 | @apply my-0 mx-1 pt-0 px-1 pb-2; 228 | } 229 | 230 | .unreset pre { 231 | @apply my-4; 232 | } 233 | 234 | /* .unreset code { 235 | font-family: monospace; 236 | } */ 237 | 238 | .unreset ul, 239 | .unreset ol { 240 | list-style-type: circle; 241 | } 242 | 243 | .unreset ul, 244 | .unreset menu { 245 | @apply list-disc my-1 pl-10; 246 | } 247 | .unreset blockquote, 248 | .unreset figure { 249 | @apply my-4 mx-10; 250 | } 251 | 252 | .unreset b, 253 | .unreset strong { 254 | font-weight: bold; 255 | } 256 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const { remarkCodeHike } = require("@code-hike/mdx"); 2 | const theme = require("./src/ch-theme"); 3 | 4 | module.exports = { 5 | experimental: { esmExternals: true }, 6 | webpack(config, options) { 7 | config.module.rules.push({ 8 | test: /\.mdx?$/, 9 | use: [ 10 | // The default `babel-loader` used by Next: 11 | options.defaultLoaders.babel, 12 | { 13 | loader: "@mdx-js/loader", 14 | /** @type {import('@mdx-js/loader').Options} */ 15 | options: { 16 | remarkPlugins: [[remarkCodeHike, { theme }]], 17 | }, 18 | }, 19 | ], 20 | }); 21 | return config; 22 | }, 23 | async redirects() { 24 | return [ 25 | { 26 | source: "/docs", 27 | destination: "/docs/introduction", 28 | permanent: true, 29 | }, 30 | { 31 | source: "/docs/installation", 32 | destination: "/docs/installation/nextjs", 33 | permanent: true, 34 | }, 35 | ]; 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@code-hike/mdx": "0.9.0", 10 | "@mdx-js/loader": "^2.0.0", 11 | "@radix-ui/react-collapsible": "^0.1.1", 12 | "@radix-ui/react-dialog": "^0.1.1", 13 | "@radix-ui/react-dropdown-menu": "^0.1.1", 14 | "@radix-ui/react-hover-card": "^0.1.1", 15 | "@radix-ui/react-icons": "^1.0.3", 16 | "@radix-ui/react-id": "^0.1.1", 17 | "@radix-ui/react-select": "^0.1.0", 18 | "@vercel/analytics": "^1.0.1", 19 | "esbuild": "^0.13.10", 20 | "mdx-bundler": "^6.0.2", 21 | "next": "^12.1.5", 22 | "next-auth": "4.0.0-beta.4", 23 | "react": "^17.0.2", 24 | "react-dom": "^17.0.2", 25 | "rehype-autolink-headings": "^6.1.1", 26 | "rehype-slug": "^5.0.1", 27 | "shiki": "0.10.1" 28 | }, 29 | "devDependencies": { 30 | "@tailwindcss/typography": "^0.5.0", 31 | "autoprefixer": "^10.4.2", 32 | "postcss": "^8.4.5", 33 | "tailwindcss": "^3.0.15" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import "@code-hike/mdx/dist/index.css"; 2 | import "tailwindcss/tailwind.css"; 3 | import "../global.css"; 4 | import "../themes.css"; 5 | import { SessionProvider } from "next-auth/react"; 6 | import { Analytics } from "@vercel/analytics/react"; 7 | 8 | export default function App({ 9 | Component, 10 | pageProps: { session, ...pageProps }, 11 | }) { 12 | return ( 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | class MyDocument extends Document { 4 | static async getInitialProps(ctx) { 5 | const initialProps = await Document.getInitialProps(ctx); 6 | return { ...initialProps }; 7 | } 8 | 9 | render() { 10 | return ( 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | ); 19 | } 20 | } 21 | 22 | export default MyDocument; 23 | -------------------------------------------------------------------------------- /pages/api/auth/[...nextauth].js: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | import GithubProvider from "next-auth/providers/github"; 3 | 4 | export default NextAuth({ 5 | providers: [ 6 | GithubProvider({ 7 | clientId: process.env.GITHUB_ID, 8 | clientSecret: process.env.GITHUB_SECRET, 9 | authorization: "https://github.com/login/oauth/authorize?scope=", // needed to remove scope added by next-auth 10 | 11 | profile: async (p) => { 12 | const orgs = await fetchUserOrgs(p.login); 13 | // console.log({ p }); 14 | return { 15 | id: p.id, 16 | name: p.login, 17 | email: "", 18 | image: p.avatar_url, 19 | orgs, 20 | }; 21 | }, 22 | }), 23 | ], 24 | jwt: { 25 | signingKey: process.env.JWT_SIGNING_PRIVATE_KEY, 26 | }, 27 | callbacks: { 28 | async jwt({ token, account, user, profile }) { 29 | if (user && user.orgs && !token.orgs) { 30 | token.orgs = user.orgs; 31 | } 32 | return token; 33 | }, 34 | async session({ session, token, user }) { 35 | session.orgs = token.orgs; 36 | return session; 37 | }, 38 | }, 39 | }); 40 | 41 | async function fetchUserOrgs(login) { 42 | const res = await fetch(`https://api.github.com/users/${login}/orgs`); 43 | const orgs = await res.json(); 44 | return orgs.map((o) => o.login); 45 | } 46 | -------------------------------------------------------------------------------- /pages/api/update-sponsors.js: -------------------------------------------------------------------------------- 1 | const nodeBtoa = (b) => Buffer.from(b).toString("base64"); 2 | 3 | export default async function triggerSponsorsWorkflow(req, res) { 4 | if (req.method !== "POST") { 5 | res.status(405).send("Method not allowed"); 6 | return; 7 | } 8 | 9 | await fetch( 10 | "https://api.github.com/repos/pomber/code-hike-site/actions/workflows/update-sponsors.yml/dispatches", 11 | { 12 | method: "POST", 13 | headers: { 14 | Accept: "application/vnd.github.everest-preview+json", 15 | "Content-Type": "application/json", 16 | Authorization: 17 | "Basic " + nodeBtoa("pomber:" + process.env.TRIGGER_ACTION_PAT), 18 | }, 19 | body: JSON.stringify({ ref: "main" }), 20 | } 21 | ); 22 | res.status(200).send("OK"); 23 | } 24 | -------------------------------------------------------------------------------- /pages/card.js: -------------------------------------------------------------------------------- 1 | import { CodeHikeLogo } from "../src/logo"; 2 | import React from "react"; 3 | 4 | export default function Home({}) { 5 | return ( 6 |
7 | 8 |

9 | Not just a{" "} 10 | syntax highlighter. 11 |

12 |
13 | ); 14 | } 15 | 16 | function CodeHike() { 17 | return ( 18 |
19 | 20 |

Code Hike

21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /pages/docs/[slug].js: -------------------------------------------------------------------------------- 1 | import { Collapsable } from "../../src/collapsable"; 2 | 3 | import React, { useMemo } from "react"; 4 | import { LANG_NAMES } from "@code-hike/lighter"; 5 | import { DocsLayout, sidebar } from "../../src/docs-layout"; 6 | import { mdxToCode } from "../../src/docs-mdx"; 7 | import { getMDXComponent } from "mdx-bundler/client"; 8 | 9 | export async function getStaticPaths() { 10 | return { 11 | paths: sidebar 12 | .filter( 13 | ([title, slug]) => 14 | !slug.startsWith("installation") && 15 | !slug.startsWith("configuration") && 16 | !slug.startsWith("themes") 17 | ) 18 | .map(([title, slug]) => ({ 19 | params: { slug, title }, 20 | })), 21 | fallback: false, 22 | }; 23 | } 24 | 25 | export async function getStaticProps(context) { 26 | const filename = context.params.slug || "introduction"; 27 | const title = sidebar.find(([, item]) => item === filename)[0]; 28 | const code = await mdxToCode(filename); 29 | 30 | return { 31 | props: { 32 | previewSource: code, 33 | slug: filename, 34 | title: title || null, 35 | }, 36 | }; 37 | } 38 | 39 | export default function Page({ slug, previewSource, title }) { 40 | return ( 41 | 42 | 51 | 52 | ); 53 | } 54 | 55 | function MDXComponent({ code, components }) { 56 | const Component = useMemo(() => getMDXComponent(code), [code]); 57 | return Component({ components }); 58 | } 59 | 60 | function LangCount() { 61 | return LANG_NAMES.length; 62 | } 63 | 64 | function LanguageList() { 65 | const languages = LANG_NAMES; 66 | const lastLanguage = languages[languages.length - 1]; 67 | const headLanguages = languages.slice(0, languages.length - 1); 68 | 69 | return ( 70 |

71 | Code Hike handles syntax highlighting for{" "} 72 | {LANG_NAMES.length} languages:{" "} 73 | {headLanguages.map((id) => ( 74 | 75 | 76 | {id} 77 | 78 | {", "} 79 | 80 | ))} 81 | {"and "} 82 | 86 | {lastLanguage} 87 | 88 | . 89 |

90 | ); 91 | } 92 | 93 | function SideBySide({ children }) { 94 | // split children into two lists 95 | const [left, right] = React.Children.toArray(children); 96 | return ( 97 |
98 |
{left}
99 |
{right}
100 |
101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /pages/docs/configuration.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { DocsLayout, sidebar } from "../../src/docs-layout"; 3 | import { mdxToCode } from "../../src/docs-mdx"; 4 | import { getMDXComponent } from "mdx-bundler/client"; 5 | 6 | export async function getStaticProps() { 7 | const filename = "configuration"; 8 | const title = sidebar.find(([, item]) => item === filename)[0]; 9 | const code = await mdxToCode(filename); 10 | const lineNumbersCode = await mdxToCode("configuration/line-numbers", { 11 | lineNumbers: true, 12 | }); 13 | 14 | return { 15 | props: { 16 | previewSource: code, 17 | slug: filename, 18 | title: title || null, 19 | lineNumbersCode, 20 | }, 21 | }; 22 | } 23 | 24 | export default function Page({ slug, previewSource, title, lineNumbersCode }) { 25 | const LineNumbersSection = useMemo( 26 | () => getMDXComponent(lineNumbersCode), 27 | [lineNumbersCode] 28 | ); 29 | 30 | return ( 31 | 32 | 33 | 34 | ); 35 | } 36 | 37 | function MDXComponent({ code, components }) { 38 | const Component = useMemo(() => getMDXComponent(code), [code]); 39 | return Component({ components }); 40 | } 41 | -------------------------------------------------------------------------------- /pages/docs/installation/[fwk].js: -------------------------------------------------------------------------------- 1 | import { Collapsable } from "../../../src/collapsable"; 2 | import { Frameworks } from "../../../src/frameworks"; 3 | 4 | import { getMDXComponent } from "mdx-bundler/client"; 5 | import React, { useMemo } from "react"; 6 | 7 | import { DocsLayout } from "../../../src/docs-layout"; 8 | import { mdxToCode } from "../../../src/docs-mdx"; 9 | 10 | const all = [ 11 | { 12 | id: "nextjs", // should match mdx file and logo 13 | name: "Next.js", // used in the dropdown menu text 14 | cardId: "nextjs", // if it has a custom card inside /public/cards/{cardId}.png 15 | title: "How to use Code Hike with Next.js", // used in the SEO title 16 | description: 17 | "Use Code Hike syntax highlighting and components in your Next.js pages", // used in the SEO description 18 | }, 19 | { 20 | id: "docusaurus", 21 | name: "Docusaurus", 22 | cardId: "docusaurus", 23 | title: "How to use Code Hike with Docusaurus", 24 | description: 25 | "Add Code Hike syntax highlighting and components in your Docusaurus mdx", 26 | }, 27 | { 28 | id: "contentlayer", 29 | name: "Next.js + Contentlayer", 30 | cardId: "contentlayer", 31 | title: "How to use Code Hike with Contentlayer", 32 | description: 33 | "Use Code Hike syntax highlighting and components with Contentlayer", 34 | }, 35 | { 36 | id: "nextra", 37 | name: "Nextra", 38 | }, 39 | { 40 | id: "gatsby", 41 | name: "Gatsby", 42 | cardId: "gatsby", 43 | title: "How to use Code Hike with Gatsby", 44 | description: "Use Code Hike syntax highlighting and components in Gatsby", 45 | }, 46 | { 47 | id: "remix", 48 | name: "Remix", 49 | }, 50 | { 51 | id: "vite", 52 | name: "Vite", 53 | }, 54 | { 55 | id: "mdx-bundler", 56 | name: "Next.js + MDX Bundler", 57 | }, 58 | { 59 | id: "next-mdx-remote", 60 | name: "Next MDX Remote", 61 | }, 62 | { 63 | id: "docspage", 64 | name: "docs.page", 65 | }, 66 | { 67 | id: "astro", 68 | name: "Astro", 69 | soon: true, 70 | }, 71 | { 72 | id: "parcel", 73 | name: "Parcel", 74 | soon: true, 75 | }, 76 | { 77 | id: "cra", 78 | name: "Create React App", 79 | soon: true, 80 | }, 81 | { 82 | id: "eleventy", 83 | name: "Eleventy", 84 | soon: true, 85 | }, 86 | // { 87 | // id: 'motif', 88 | // name: 'Motif', 89 | // soon: true, 90 | // }, 91 | ]; 92 | 93 | export async function getStaticPaths() { 94 | return { 95 | paths: all.map(({ id }) => ({ 96 | params: { fwk: id }, 97 | })), 98 | fallback: false, 99 | }; 100 | } 101 | 102 | export async function getStaticProps(context) { 103 | const filename = "installation"; 104 | const fwk = context.params.fwk; 105 | 106 | const introCode = await mdxToCode(filename); 107 | const fwkCode = await mdxToCode("installation-" + fwk); 108 | 109 | return { 110 | props: { 111 | introCode, 112 | fwkCode, 113 | slug: filename, 114 | title: "Installation", 115 | fwk, 116 | }, 117 | }; 118 | } 119 | 120 | export default function Page({ slug, introCode, title, fwkCode, fwk }) { 121 | const option = useMemo(() => all.find(({ id }) => id === fwk), [fwk]); 122 | return ( 123 | 130 | 131 | 132 |
133 | 134 |
135 |
136 | ); 137 | } 138 | 139 | function MDXComponent({ code, components }) { 140 | const Component = useMemo(() => getMDXComponent(code), [code]); 141 | return Component({ components }); 142 | } 143 | 144 | function SideBySide({ children }) { 145 | // split children into two lists 146 | const [left, right] = React.Children.toArray(children); 147 | return ( 148 |
149 |
{left}
150 |
{right}
151 |
152 | ); 153 | } 154 | -------------------------------------------------------------------------------- /pages/docs/preview/[preview].js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { remarkCodeHike } from "@code-hike/mdx"; 3 | import { getMDXComponent } from "mdx-bundler/client"; 4 | import { bundleMDX } from "mdx-bundler"; 5 | import React, { useMemo } from "react"; 6 | 7 | export async function getStaticPaths() { 8 | const filenames = await fs.promises.readdir("docs/preview"); 9 | 10 | return { 11 | paths: filenames.map((filename) => ({ 12 | params: { preview: filename.slice(0, -4) }, 13 | })), 14 | fallback: false, 15 | }; 16 | } 17 | 18 | export async function getStaticProps(context) { 19 | const filename = context.params.preview; 20 | const mdxSource = await fs.promises.readFile( 21 | `./docs/preview/${filename}.mdx`, 22 | "utf8" 23 | ); 24 | 25 | const previewSource = await bundleMDX(mdxSource, { 26 | esbuildOptions(options) { 27 | options.platform = "node"; 28 | return options; 29 | }, 30 | xdmOptions(options) { 31 | options.remarkPlugins = [[remarkCodeHike, { theme: "dark-plus" }]]; 32 | return options; 33 | }, 34 | }); 35 | 36 | return { 37 | props: { 38 | previewSource: previewSource.code, 39 | }, 40 | }; 41 | } 42 | 43 | export default function Page({ previewSource }) { 44 | return ( 45 |
46 | 65 | 66 |
67 | ); 68 | } 69 | 70 | function MDXComponent({ code }) { 71 | const Component = useMemo(() => getMDXComponent(code), [code]); 72 | return ; 73 | } 74 | -------------------------------------------------------------------------------- /pages/docs/themes.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { DocsLayout, sidebar } from "../../src/docs-layout"; 3 | import { mdxToCode } from "../../src/docs-mdx"; 4 | import { getMDXComponent } from "mdx-bundler/client"; 5 | 6 | import { THEME_NAMES } from "@code-hike/lighter"; 7 | 8 | export async function getStaticProps() { 9 | const filename = "themes"; 10 | const title = sidebar.find(([, item]) => item === filename)[0]; 11 | const code = await mdxToCode(filename); 12 | 13 | const promises = THEME_NAMES.map(async (themeName) => { 14 | const code = await mdxToCode("configuration/theme", { 15 | theme: themeName, 16 | lineNumbers: true, 17 | showCopyButton: true, 18 | }); 19 | return { themeName, code }; 20 | }); 21 | 22 | const themeCodes = await Promise.all(promises); 23 | 24 | return { 25 | props: { 26 | previewSource: code, 27 | slug: filename, 28 | title: title || null, 29 | themeCodes, 30 | }, 31 | }; 32 | } 33 | 34 | function getCodeSection(themeCodes, showModes) { 35 | const [selected, setSelected] = React.useState(0); 36 | const [isDark, setIsDark] = React.useState(false); 37 | const Component = getMDXComponent(themeCodes[selected].code); 38 | return () => ( 39 |
40 |
41 | {themeCodes.map(({ themeName }, i) => { 42 | const isSelected = i === selected; 43 | return ( 44 | 52 | ); 53 | })} 54 |
55 | {showModes && ( 56 |
57 | {" "} 63 | |{" "} 64 | 70 |
71 | )} 72 | 73 | {`{ theme: "${themeCodes[selected].themeName}" }`} 74 |
75 | ); 76 | } 77 | 78 | export default function Page({ slug, previewSource, title, themeCodes }) { 79 | const BuitInThemes = getCodeSection( 80 | themeCodes.filter(({ themeName }) => !themeName.endsWith("from-css")) 81 | ); 82 | const FromCSSThemes = getCodeSection( 83 | themeCodes.filter(({ themeName }) => themeName.endsWith("from-css")), 84 | true 85 | ); 86 | 87 | return ( 88 | 89 | 96 | 97 | ); 98 | } 99 | 100 | function MDXComponent({ code, components }) { 101 | const Component = useMemo(() => getMDXComponent(code), [code]); 102 | return Component({ components }); 103 | } 104 | -------------------------------------------------------------------------------- /pages/logo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Page() { 4 | const [config, setConfig] = React.useState({ 5 | y0: -60, 6 | x1: 57, 7 | y1: 27, 8 | y3: 70, 9 | c: "#2563eb", 10 | s: 30, 11 | }); 12 | 13 | return ( 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | ); 30 | } 31 | 32 | function ShowLogo({ config, circle, dark, favicon, ...rest }) { 33 | const points = getPoints(config); 34 | 35 | const border = "#222"; 36 | const bg = dark ? "#222" : "#fafafa"; 37 | return ( 38 |
47 | 48 |
49 | ); 50 | } 51 | function ShowTitle({ config, ...rest }) { 52 | const points = getPoints(config); 53 | 54 | return ( 55 |
56 |
63 | 64 |
65 |

Code Hike

66 |
67 | ); 68 | } 69 | function Editor({ config, setConfig }) { 70 | return ( 71 |
72 | setConfig({ ...config, c: e.currentTarget.value })} 76 | /> 77 | 78 | 79 | 80 | 81 | 82 |
83 | ); 84 | } 85 | 86 | function Range({ config, setConfig, v, style, min = 1, max = 100 }) { 87 | function setter(newValue) { 88 | setConfig({ ...config, [v]: newValue }); 89 | } 90 | return ( 91 | 103 | ); 104 | } 105 | 106 | function Logo({ points, color, circle, axis, paths }) { 107 | const { x0, y0, x1, y1, x2, y2, x3, y3, s } = points; 108 | return ( 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | {circle && } 117 | 118 | {paths ? ( 119 | 120 | ) : ( 121 | 129 | )} 130 | {axis && ( 131 | <> 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | )} 140 | 141 | 142 | ); 143 | } 144 | 145 | function Paths({ points, color }) { 146 | const { x0, y0, x1, y1, x2, y2, x3, y3, s } = points; 147 | return ( 148 | <> 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | ); 157 | } 158 | 159 | function Polygon({ d, s, color }) { 160 | const [x0, y0, x1, y1] = d; 161 | return ( 162 | 168 | ); 169 | } 170 | 171 | function getPoints({ y0, x1, y1, y3, s }) { 172 | const x0 = 100 - s / 2; 173 | const x3 = 0; 174 | const p = (x0 - x1) / (y1 - y0); 175 | const r0 = x1 - p * y1; 176 | const r1 = p * y3; 177 | const x2 = (r0 + r1) / 2; 178 | const y2 = (x2 - r0) / p; 179 | return { x0, y0: -y0, x1, y1: -y1, x2, y2: -y2, x3, y3: -y3, s, r0, r1 }; 180 | } 181 | -------------------------------------------------------------------------------- /pages/show.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { remarkCodeHike } from "@code-hike/mdx"; 3 | import { getMDXComponent } from "mdx-bundler/client"; 4 | import { bundleMDX } from "mdx-bundler"; 5 | import React from "react"; 6 | import { steps } from "../src/home-demo"; 7 | 8 | export async function getStaticProps() { 9 | const mdxSource = await fs.promises.readFile(`./data/demos/show.mdx`, "utf8"); 10 | 11 | const previewSource = await bundleMDX(mdxSource, { 12 | esbuildOptions(options) { 13 | options.platform = "node"; 14 | return options; 15 | }, 16 | xdmOptions(options) { 17 | options.remarkPlugins = [ 18 | [remarkCodeHike, { theme: "material-palenight" }], 19 | ]; 20 | return options; 21 | }, 22 | }); 23 | 24 | return { props: { code: previewSource.code } }; 25 | } 26 | 27 | export default function Home({ code }) { 28 | const Component = React.useMemo( 29 | () => getMDXComponent(code, { react: React }), 30 | [code] 31 | ); 32 | return ( 33 |
37 | 50 | 51 | 67 |
68 | ); 69 | } 70 | 71 | function tour() { 72 | document.getElementById("start-button").style.display = "none"; 73 | window.scrollTo({ 74 | top: steps[0].top, 75 | }); 76 | let t0 = performance.now(); 77 | let i = 0; 78 | setInterval(() => { 79 | const currentStep = steps[i]; 80 | const nextStep = steps[i + 1]; 81 | if (!currentStep) return; 82 | 83 | const delta = (performance.now() - t0) / 1000; 84 | if (delta > currentStep.delay) { 85 | t0 += currentStep.delay * 1000; 86 | if (nextStep) { 87 | window.scrollTo({ 88 | top: nextStep.top, 89 | behavior: "smooth", 90 | }); 91 | } else { 92 | document.getElementById("start-button").style.display = "block"; 93 | } 94 | i++; 95 | } 96 | }, 30); 97 | } 98 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /public/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/card.png -------------------------------------------------------------------------------- /public/cards/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/cards/card.png -------------------------------------------------------------------------------- /public/cards/contentlayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/cards/contentlayer.png -------------------------------------------------------------------------------- /public/cards/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/cards/docusaurus.png -------------------------------------------------------------------------------- /public/cards/gatsby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/cards/gatsby.png -------------------------------------------------------------------------------- /public/cards/nextjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/cards/nextjs.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/favicon.ico -------------------------------------------------------------------------------- /public/logo/astro.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/logo/contentlayer.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/logo/cra.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/logo/docspage.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /public/logo/eleventy.svg: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /public/logo/gatsby.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/logo/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /public/logo/mdx-bundler.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/logo/motif.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/logo/next-mdx-remote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/logo/nextjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/logo/nextra.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/logo/parcel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /public/logo/remix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /public/logo/vite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/logo/webpack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/not-stripe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/not-stripe.png -------------------------------------------------------------------------------- /public/not-tailwind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/not-tailwind.png -------------------------------------------------------------------------------- /public/show1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/code-hike-site/eb211f21fb0aecab2596eb78247b26dd026e038a/public/show1.mp4 -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | The website for https://v0.codehike.org 2 | -------------------------------------------------------------------------------- /src/collapsable.js: -------------------------------------------------------------------------------- 1 | import * as Collapsible from "@radix-ui/react-collapsible"; 2 | import { ChevronDownIcon } from "@radix-ui/react-icons"; 3 | import React from "react"; 4 | 5 | export function Collapsable({ 6 | children, 7 | openText = "Show", 8 | closeText = "Hide", 9 | }) { 10 | const [isOpen, setOpen] = React.useState(false); 11 | const [header, ...content] = React.Children.toArray(children); 12 | return ( 13 | setOpen(open)}> 14 |
15 |
{header?.props?.children}
16 | 17 | {isOpen ? closeText : openText} 18 | 26 | 27 |
28 | 29 | {content} 30 | 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/demo-grid.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "next/link"; 3 | import demos from "../data/demos/index.json"; 4 | 5 | export function DemoGrid() { 6 | return ( 7 | <> 8 |
9 | {demos.map((demo) => ( 10 | 11 | 12 | {demo.title} 13 | {demo.locked && ( 14 | 15 | )} 16 | 17 | 18 | ))} 19 |
20 |

21 | Everyone can see all the demos, but only sponsors can see the code for 22 | demos marked with . 23 | Locked demos are unlocked for everyone after being sponsored by five 24 | sponsors. 25 |

26 | 27 | ); 28 | } 29 | 30 | function LockIcon(props) { 31 | return ( 32 | 42 | Locked 43 | 44 | 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/docs-layout.js: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { IdProvider } from "@radix-ui/react-id"; 3 | import { 4 | CodeHikeLogo, 5 | DiscordLink, 6 | GitHubLink, 7 | MenuIcon, 8 | TwitterLink, 9 | } from "./logo"; 10 | import * as Dialog from "@radix-ui/react-dialog"; 11 | import { useRouter } from "next/router"; 12 | import { SEO } from "./seo"; 13 | 14 | export const sidebar = [ 15 | ["Introduction", "introduction"], 16 | ["Installation", "installation/nextjs"], 17 | ["Configuration", "configuration"], 18 | ["Themes", "themes"], 19 | ["Code Blocks", "codeblocks"], 20 | ["Annotations", "annotations"], 21 | ["", "ch-code"], 22 | ["", "ch-section"], 23 | ["", "ch-scrollycoding"], 24 | ["", "ch-spotlight"], 25 | ["", "ch-slideshow"], 26 | // ["Roadmap", "roadmap"], 27 | // ["Changelog", "changelog"], 28 | ["Styling", "styling"], 29 | ["Troubleshooting", "troubleshooting"], 30 | ]; 31 | 32 | const stable = [ 33 | "introduction", 34 | "installation/nextjs", 35 | "configuration", 36 | "themes", 37 | "codeblocks", 38 | "annotations", 39 | "ch-code", 40 | "ch-section", 41 | "troubleshooting", 42 | ]; 43 | function isExperimental(slug) { 44 | return !stable.includes(slug); 45 | } 46 | 47 | export function DocsLayout({ title, h1, slug, children, cardId, description }) { 48 | return ( 49 | <> 50 |
51 | These are the old docs. Code Hike v1.0 is out! Read{" "} 52 | 53 | the announcement 54 | 55 | . 56 |
57 |
58 | 59 | 60 |
61 | 81 |
82 |
83 | 86 | 87 |
88 |
92 |

{h1}

93 | {children} 94 |
95 |
96 |
97 |
98 |
99 | 100 | ); 101 | } 102 | 103 | function Sidebar({ current }) { 104 | const currentSlug = current.split("/")[0]; 105 | return ( 106 | 126 | ); 127 | } 128 | 129 | function ExperimentalIcon() { 130 | return ( 131 | 138 | Experimental 139 | 145 | 146 | ); 147 | } 148 | 149 | function MobileMenu({ current }) { 150 | const router = useRouter(); 151 | 152 | return ( 153 |
154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 |
163 | ); 164 | } 165 | -------------------------------------------------------------------------------- /src/docs-mdx.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { bundleMDX } from "mdx-bundler"; 3 | import { remarkCodeHike } from "@code-hike/mdx"; 4 | import theme from "./ch-theme"; 5 | import rehypeSlug from "rehype-slug"; 6 | import rehypeAutolinkHeadings from "rehype-autolink-headings"; 7 | 8 | export async function mdxToCode(filename, config = {}) { 9 | const mdxSource = await fs.promises.readFile( 10 | `./docs/${filename}.mdx`, 11 | "utf8" 12 | ); 13 | 14 | const { code } = await bundleMDX(mdxSource, { 15 | esbuildOptions(options) { 16 | options.platform = "node"; 17 | return options; 18 | }, 19 | xdmOptions(options) { 20 | options.rehypePlugins = [ 21 | rehypeSlug, 22 | rehypeAutolinkHeadings, 23 | [ 24 | rehypeAutolinkHeadings, 25 | { 26 | behavior: "append", 27 | properties: { 28 | className: ["anchor"], 29 | }, 30 | }, 31 | ], 32 | ]; 33 | options.remarkPlugins = [[remarkCodeHike, { theme, ...config }]]; 34 | return options; 35 | }, 36 | }); 37 | 38 | return code; 39 | } 40 | -------------------------------------------------------------------------------- /src/frameworks.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "next/link"; 3 | 4 | function findOption(id, options) { 5 | return options.find((option) => option.id === id); 6 | } 7 | 8 | export function Frameworks({ loadId, options }) { 9 | const selectedOption = findOption(loadId, options); 10 | 11 | const now = options.filter(({ soon }) => !soon); 12 | // const soon = options.filter(({ soon }) => soon); 13 | return ( 14 |
15 | {now.map((option) => ( 16 | 21 | ))} 22 |
23 | ); 24 | } 25 | function Framework({ option, className, isSelected }) { 26 | return ( 27 | 28 | 33 |
34 | {option.name} 39 |
40 | {option.name} 41 |
42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /src/home-demo-server.js: -------------------------------------------------------------------------------- 1 | import { highlight } from "@code-hike/mdx"; 2 | import fs from "fs"; 3 | 4 | export async function getHomeDemoProps() { 5 | const mdx = fs.readFileSync("./data/demos/old-show.mdx", "utf8"); 6 | 7 | const code = await highlight({ 8 | code: mdx, 9 | lang: "mdx", 10 | theme: "github-light", 11 | }); 12 | 13 | return { code }; 14 | } 15 | -------------------------------------------------------------------------------- /src/logo.js: -------------------------------------------------------------------------------- 1 | const color = "#3574fd"; 2 | export function CodeHikeLogo(props) { 3 | return ( 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export function TwitterLink(props) { 16 | return ( 17 | 23 | 31 | 35 | 36 | 37 | ); 38 | } 39 | 40 | export function GitHubLink(props) { 41 | return ( 42 | 48 | 49 | 53 | 54 | 55 | ); 56 | } 57 | export function DiscordLink(props) { 58 | return ( 59 | 65 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ); 83 | } 84 | 85 | export function MenuIcon(props) { 86 | return ( 87 | 101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /src/seo.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | 3 | const baseUrl = `https://${ 4 | process.env.NEXT_PUBLIC_VERCEL_URL || "codehike.org" 5 | }`; 6 | 7 | export function SEO({ 8 | children, 9 | cardId = "card", 10 | title = "Code Hike", 11 | description = "Marvellous code walkthroughs", 12 | }) { 13 | const imageUrl = `${baseUrl}/cards/${cardId}.png`; 14 | return ( 15 | 16 | {title} 17 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {children} 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: "jit", 3 | purge: ["./pages/**/*.{js,ts,jsx,tsx}", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: { 6 | typography: { 7 | DEFAULT: { 8 | css: { 9 | a: { 10 | color: "#374151", 11 | }, 12 | "a:hover": { 13 | color: "rgb(37 99 235)", 14 | }, 15 | "code::before": false, 16 | "code::after": false, 17 | code: { 18 | "font-weight": "unset", 19 | }, 20 | "blockquote p:first-of-type::before": false, 21 | "blockquote p:first-of-type::after": false, 22 | blockquote: { "font-weight": "unset" }, 23 | }, 24 | }, 25 | }, 26 | }, 27 | screens: { 28 | md: "830px", 29 | "2cols": "982px", 30 | "3cols": "1255px", 31 | }, 32 | }, 33 | plugins: [require("@tailwindcss/typography")], 34 | }; 35 | -------------------------------------------------------------------------------- /themes.css: -------------------------------------------------------------------------------- 1 | [data-ch-theme="github-from-css"] { 2 | --ch-0: dark; 3 | --ch-1: #8b949e; 4 | --ch-2: #79c0ff; 5 | --ch-3: #ffa657; 6 | --ch-4: #c9d1d9; 7 | --ch-5: #d2a8ff; 8 | --ch-6: #7ee787; 9 | --ch-7: #ff7b72; 10 | --ch-8: #a5d6ff; 11 | --ch-9: #ffa198; 12 | --ch-10: #f0f6fc; 13 | --ch-11: #490202; 14 | --ch-12: #04260f; 15 | --ch-13: #5a1e02; 16 | --ch-14: #161b22; 17 | --ch-15: #8b949e; 18 | --ch-16: #0d1117; 19 | --ch-17: #6e76811a; 20 | --ch-18: #ffffff0b; 21 | --ch-19: #3794ff; 22 | --ch-20: #264f78; 23 | --ch-21: #1f6feb; 24 | --ch-22: #010409; 25 | --ch-23: #30363d; 26 | --ch-24: #6e7681; 27 | --ch-25: #6e768166; 28 | --ch-26: #0d1117e6; 29 | } 30 | 31 | [data-theme="light"] [data-ch-theme="github-from-css"] { 32 | --ch-0: light; 33 | --ch-1: #6e7781; 34 | --ch-2: #0550ae; 35 | --ch-3: #953800; 36 | --ch-4: #24292f; 37 | --ch-5: #8250df; 38 | --ch-6: #116329; 39 | --ch-7: #cf222e; 40 | --ch-8: #0a3069; 41 | --ch-9: #82071e; 42 | --ch-10: #f6f8fa; 43 | --ch-11: #ffebe9; 44 | --ch-12: #dafbe1; 45 | --ch-13: #ffd8b5; 46 | --ch-14: #eaeef2; 47 | --ch-15: #57606a; 48 | --ch-16: #ffffff; 49 | --ch-17: #eaeef280; 50 | --ch-18: #fdff0033; 51 | --ch-19: #1a85ff; 52 | --ch-20: #add6ff; 53 | --ch-21: #0969da; 54 | --ch-22: #f6f8fa; 55 | --ch-23: #d0d7de; 56 | --ch-24: #8c959f; 57 | --ch-25: #afb8c133; 58 | --ch-26: #ffffffe6; 59 | } 60 | 61 | [data-ch-theme="material-from-css"] { 62 | --ch-0: dark; 63 | --ch-1: #212121; 64 | --ch-2: #eeffff; 65 | --ch-3: #c3e88d; 66 | --ch-4: #89ddff; 67 | --ch-5: #ff9cac; 68 | --ch-6: #f78c6c; 69 | --ch-7: #82aaff; 70 | --ch-8: #c792ea; 71 | --ch-9: #f07178; 72 | --ch-10: #ffcb6b; 73 | --ch-11: #545454; 74 | --ch-12: #b2ccd6; 75 | --ch-13: #eeffff90; 76 | --ch-14: #00000050; 77 | --ch-15: #3794ff; 78 | --ch-16: #61616150; 79 | --ch-17: #ffffff; 80 | --ch-18: #616161; 81 | --ch-19: #00000030; 82 | --ch-20: #424242; 83 | --ch-21: #2b2b2b; 84 | --ch-22: #ffffff10; 85 | --ch-23: #c5c5c5; 86 | --ch-24: #21212160; 87 | --ch-25: #ffffff; 88 | --ch-26: #212121e6; 89 | } 90 | 91 | [data-theme="light"] [data-ch-theme="material-from-css"] { 92 | --ch-0: light; 93 | --ch-1: #fafafa; 94 | --ch-2: #90a4ae; 95 | --ch-3: #91b859; 96 | --ch-4: #39adb5; 97 | --ch-5: #ff5370; 98 | --ch-6: #f76d47; 99 | --ch-7: #6182b8; 100 | --ch-8: #9c3eda; 101 | --ch-9: #e53935; 102 | --ch-10: #e2931d; 103 | --ch-11: #90a4ae; 104 | --ch-12: #8796b0; 105 | --ch-13: #90a4ae90; 106 | --ch-14: #ccd7da50; 107 | --ch-15: #1a85ff; 108 | --ch-16: #80cbc440; 109 | --ch-17: #000000; 110 | --ch-18: #7e939e; 111 | --ch-19: #00000020; 112 | --ch-20: #cfd8dc; 113 | --ch-21: #eeeeee; 114 | --ch-22: #00000010; 115 | --ch-23: #424242; 116 | --ch-24: #fafafa60; 117 | --ch-25: #b1c7d3; 118 | --ch-26: #fafafae6; 119 | } 120 | --------------------------------------------------------------------------------