diff --git a/astro.config.ts b/astro.config.ts
index 8023ecdc..c88a7435 100644
--- a/astro.config.ts
+++ b/astro.config.ts
@@ -32,6 +32,7 @@ import mdx from "@astrojs/mdx";
import symlink from "symlink-dir";
import * as path from "path";
import svgr from "vite-plugin-svgr";
+import { rehypeFileTree } from "utils/markdown/file-tree/rehype-file-tree";
await symlink(path.resolve("content"), path.resolve("public/content"));
@@ -108,6 +109,7 @@ export default defineConfig({
},
},
],
+ rehypeFileTree,
rehypeHeaderText,
/**
* Insert custom HTML generation code here
diff --git a/content/blog/setup-a-react-native-monorepo/index.md b/content/blog/setup-a-react-native-monorepo/index.md
index ae5861ef..aaef5c47 100644
--- a/content/blog/setup-a-react-native-monorepo/index.md
+++ b/content/blog/setup-a-react-native-monorepo/index.md
@@ -71,6 +71,53 @@ We now have a basic demo application that we can extend by adding it to our mono
# Maintain Multiple Package Roots with Yarn Berry {#yarn-berry}
+While the `react-native init` command is great for single apps, it doesn't do much to help us scaffold our monorepo.
+
+Currently, with the newly created React Native project, our filesystem looks something like this:
+
+
+- `/`
+ - `App.tsx`
+ - `android/`
+ - `app.json`
+ - `babel.config.js`
+ - `index.js`
+ - `ios/`
+ - `metro.config.js`
+ - `node_modules`
+ - `package.json`
+ - `tsconfig.json`
+ - `yarn.lock`
+
+
+In a monorepo, however, we might have multiple apps and packages that we want to keep in the same repository. To do this, our filesystem should look something akin to this structure:
+
+
+- `/`
+ - `apps/`
+ - `chat-app-mobile/`
+ - `src`
+ - `App.tsx`
+ - `components/`
+ - `hooks/`
+ - `utils/`
+ - `types/`
+ - `android/`
+ - `app.json`
+ - `babel.config.js`
+ - `index.js`
+ - `ios/`
+ - `metro.config.js`
+ - `node_modules`
+ - `package.json`
+ - `tsconfig.json`
+ - `yarn.lock`
+
+
+
+
+
+
https://twitter.com/larixer/status/1570459837498290178
diff --git a/src/utils/markdown/file-tree/file-tree-icons.ts b/src/utils/markdown/file-tree/file-tree-icons.ts
new file mode 100644
index 00000000..8dbdaf5f
--- /dev/null
+++ b/src/utils/markdown/file-tree/file-tree-icons.ts
@@ -0,0 +1,756 @@
+/**
+ * Based on https://github.com/elviswolcott/seti-icons which
+ * is derived from https://github.com/jesseweed/seti-ui/
+ *
+ * Copyright (c) 2014 Jesse Weed
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+const rawDefinitions = {
+ files: {
+ COMMIT_EDITMSG: ["git", "ignore"],
+ MERGE_MSG: ["git", "ignore"],
+ "karma.conf.js": ["karma", "green"],
+ "karma.conf.coffee": ["karma", "green"],
+ "README.md": ["info", "blue"],
+ "README.txt": ["info", "blue"],
+ README: ["info", "blue"],
+ "CHANGELOG.md": ["clock", "blue"],
+ "CHANGELOG.txt": ["clock", "blue"],
+ CHANGELOG: ["clock", "blue"],
+ "CHANGES.md": ["clock", "blue"],
+ "CHANGES.txt": ["clock", "blue"],
+ CHANGES: ["clock", "blue"],
+ "VERSION.md": ["clock", "blue"],
+ "VERSION.txt": ["clock", "blue"],
+ VERSION: ["clock", "blue"],
+ mvnw: ["maven", "red"],
+ "tsconfig.json": ["tsconfig", "blue"],
+ "swagger.json": ["json", "green"],
+ "swagger.yml": ["json", "green"],
+ "swagger.yaml": ["json", "green"],
+ "mime.types": ["config", "grey-light"],
+ Jenkinsfile: ["jenkins", "red"],
+ "babel.config.js": ["babel", "yellow"],
+ "babel.config.json": ["babel", "yellow"],
+ "babel.config.cjs": ["babel", "yellow"],
+ BUILD: ["bazel", "green"],
+ "BUILD.bazel": ["bazel", "green"],
+ WORKSPACE: ["bazel", "green"],
+ "WORKSPACE.bazel": ["bazel", "green"],
+ "bower.json": ["bower", "orange"],
+ "Bower.json": ["bower", "orange"],
+ "firebase.json": ["firebase", "orange"],
+ geckodriver: ["firefox", "orange"],
+ "Gruntfile.js": ["grunt", "orange"],
+ "gruntfile.babel.js": ["grunt", "orange"],
+ "Gruntfile.babel.js": ["grunt", "orange"],
+ "gruntfile.js": ["grunt", "orange"],
+ "Gruntfile.coffee": ["grunt", "orange"],
+ "gruntfile.coffee": ["grunt", "orange"],
+ "ionic.config.json": ["ionic", "blue"],
+ "Ionic.config.json": ["ionic", "blue"],
+ "ionic.project": ["ionic", "blue"],
+ "Ionic.project": ["ionic", "blue"],
+ "platformio.ini": ["platformio", "orange"],
+ "rollup.config.js": ["rollup", "red"],
+ "sass-lint.yml": ["sass", "pink"],
+ "stylelint.config.js": ["stylelint", "white"],
+ "stylelint.config.cjs": ["stylelint", "white"],
+ "yarn.clean": ["yarn", "blue"],
+ "yarn.lock": ["yarn", "blue"],
+ "webpack.config.js": ["webpack", "blue"],
+ "webpack.config.cjs": ["webpack", "blue"],
+ "webpack.config.ts": ["webpack", "blue"],
+ "webpack.config.build.js": ["webpack", "blue"],
+ "webpack.config.build.cjs": ["webpack", "blue"],
+ "webpack.config.build.ts": ["webpack", "blue"],
+ "webpack.common.js": ["webpack", "blue"],
+ "webpack.common.cjs": ["webpack", "blue"],
+ "webpack.common.ts": ["webpack", "blue"],
+ "webpack.dev.js": ["webpack", "blue"],
+ "webpack.dev.cjs": ["webpack", "blue"],
+ "webpack.dev.ts": ["webpack", "blue"],
+ "webpack.prod.js": ["webpack", "blue"],
+ "webpack.prod.cjs": ["webpack", "blue"],
+ "webpack.prod.ts": ["webpack", "blue"],
+ "npm-debug.log": ["npm_ignored", "ignore"],
+ },
+ extensions: {
+ ".astro": ["astro", "red"],
+ ".bsl": ["bsl", "red"],
+ ".mdo": ["mdo", "red"],
+ ".cls": ["salesforce", "blue"],
+ ".apex": ["salesforce", "blue"],
+ ".asm": ["asm", "red"],
+ ".s": ["asm", "red"],
+ ".bicep": ["bicep", "blue"],
+ ".bzl": ["bazel", "green"],
+ ".bazel": ["bazel", "green"],
+ ".BUILD": ["bazel", "green"],
+ ".WORKSPACE": ["bazel", "green"],
+ ".bazelignore": ["bazel", "green"],
+ ".bazelversion": ["bazel", "green"],
+ ".c": ["c", "blue"],
+ ".h": ["c", "purple"],
+ ".m": ["c", "yellow"],
+ ".cs": ["c-sharp", "blue"],
+ ".cshtml": ["html", "blue"],
+ ".aspx": ["html", "blue"],
+ ".ascx": ["html", "green"],
+ ".asax": ["html", "yellow"],
+ ".master": ["html", "yellow"],
+ ".cc": ["cpp", "blue"],
+ ".cpp": ["cpp", "blue"],
+ ".cxx": ["cpp", "blue"],
+ ".c++": ["cpp", "blue"],
+ ".hh": ["cpp", "purple"],
+ ".hpp": ["cpp", "purple"],
+ ".hxx": ["cpp", "purple"],
+ ".h++": ["cpp", "purple"],
+ ".mm": ["cpp", "yellow"],
+ ".clj": ["clojure", "green"],
+ ".cljs": ["clojure", "green"],
+ ".cljc": ["clojure", "green"],
+ ".edn": ["clojure", "blue"],
+ ".cfc": ["coldfusion", "blue"],
+ ".cfm": ["coldfusion", "blue"],
+ ".coffee": ["coffee", "yellow"],
+ ".litcoffee": ["coffee", "yellow"],
+ ".config": ["config", "grey-light"],
+ ".cfg": ["config", "grey-light"],
+ ".conf": ["config", "grey-light"],
+ ".cr": ["crystal", "white"],
+ ".ecr": ["crystal_embedded", "white"],
+ ".slang": ["crystal_embedded", "white"],
+ ".cson": ["json", "yellow"],
+ ".css": ["css", "blue"],
+ ".css.map": ["css", "blue"],
+ ".sss": ["css", "blue"],
+ ".csv": ["csv", "green"],
+ ".xls": ["xls", "green"],
+ ".xlsx": ["xls", "green"],
+ ".cu": ["cu", "green"],
+ ".cuh": ["cu", "purple"],
+ ".hu": ["cu", "purple"],
+ ".cake": ["cake", "red"],
+ ".ctp": ["cake_php", "red"],
+ ".d": ["d", "red"],
+ ".doc": ["word", "blue"],
+ ".docx": ["word", "blue"],
+ ".ejs": ["ejs", "yellow"],
+ ".ex": ["elixir", "purple"],
+ ".exs": ["elixir_script", "purple"],
+ ".elm": ["elm", "blue"],
+ ".ico": ["favicon", "yellow"],
+ ".fs": ["f-sharp", "blue"],
+ ".fsx": ["f-sharp", "blue"],
+ ".gitignore": ["git", "ignore"],
+ ".gitconfig": ["git", "ignore"],
+ ".gitkeep": ["git", "ignore"],
+ ".gitattributes": ["git", "ignore"],
+ ".gitmodules": ["git", "ignore"],
+ ".go": ["go2", "blue"],
+ ".slide": ["go", "blue"],
+ ".article": ["go", "blue"],
+ ".gd": ["godot", "blue"],
+ ".godot": ["godot", "red"],
+ ".tres": ["godot", "yellow"],
+ ".tscn": ["godot", "purple"],
+ ".gradle": ["gradle", "blue"],
+ ".groovy": ["grails", "green"],
+ ".gsp": ["grails", "green"],
+ ".gql": ["graphql", "pink"],
+ ".graphql": ["graphql", "pink"],
+ ".graphqls": ["graphql", "pink"],
+ ".hack": ["hacklang", "orange"],
+ ".haml": ["haml", "red"],
+ ".handlebars": ["mustache", "orange"],
+ ".hbs": ["mustache", "orange"],
+ ".hjs": ["mustache", "orange"],
+ ".hs": ["haskell", "purple"],
+ ".lhs": ["haskell", "purple"],
+ ".hx": ["haxe", "orange"],
+ ".hxs": ["haxe", "yellow"],
+ ".hxp": ["haxe", "blue"],
+ ".hxml": ["haxe", "purple"],
+ ".html": ["html", "orange"],
+ ".jade": ["jade", "red"],
+ ".java": ["java", "red"],
+ ".class": ["java", "blue"],
+ ".classpath": ["java", "red"],
+ ".properties": ["java", "red"],
+ ".js": ["javascript", "yellow"],
+ ".js.map": ["javascript", "yellow"],
+ ".spec.js": ["javascript", "orange"],
+ ".test.js": ["javascript", "orange"],
+ ".es": ["javascript", "yellow"],
+ ".es5": ["javascript", "yellow"],
+ ".es6": ["javascript", "yellow"],
+ ".es7": ["javascript", "yellow"],
+ ".cjs": ["javascript", "yellow"],
+ ".mjs": ["javascript", "yellow"],
+ ".jinja": ["jinja", "red"],
+ ".jinja2": ["jinja", "red"],
+ ".json": ["json", "yellow"],
+ ".jl": ["julia", "purple"],
+ ".kt": ["kotlin", "orange"],
+ ".kts": ["kotlin", "orange"],
+ ".dart": ["dart", "blue"],
+ ".less": ["less", "blue"],
+ ".liquid": ["liquid", "green"],
+ ".ls": ["livescript", "blue"],
+ ".lua": ["lua", "blue"],
+ ".markdown": ["markdown", "blue"],
+ ".md": ["markdown", "blue"],
+ ".mdx": ["markdown", "blue"],
+ ".argdown": ["argdown", "blue"],
+ ".ad": ["argdown", "blue"],
+ ".mustache": ["mustache", "orange"],
+ ".stache": ["mustache", "orange"],
+ ".nim": ["nim", "yellow"],
+ ".nims": ["nim", "yellow"],
+ ".github-issues": ["github", "white"],
+ ".ipynb": ["notebook", "blue"],
+ ".njk": ["nunjucks", "green"],
+ ".nunjucks": ["nunjucks", "green"],
+ ".nunjs": ["nunjucks", "green"],
+ ".nunj": ["nunjucks", "green"],
+ ".njs": ["nunjucks", "green"],
+ ".nj": ["nunjucks", "green"],
+ ".npm-debug.log": ["npm", "ignore"],
+ ".npmignore": ["npm", "red"],
+ ".npmrc": ["npm", "red"],
+ ".ml": ["ocaml", "orange"],
+ ".mli": ["ocaml", "orange"],
+ ".cmx": ["ocaml", "orange"],
+ ".cmxa": ["ocaml", "orange"],
+ ".odata": ["odata", "orange"],
+ ".pl": ["perl", "blue"],
+ ".php": ["php", "purple"],
+ ".php.inc": ["php", "purple"],
+ ".pipeline": ["pipeline", "orange"],
+ ".pddl": ["pddl", "purple"],
+ ".plan": ["plan", "green"],
+ ".happenings": ["happenings", "blue"],
+ ".ps1": ["powershell", "blue"],
+ ".psd1": ["powershell", "blue"],
+ ".psm1": ["powershell", "blue"],
+ ".prisma": ["prisma", "blue"],
+ ".pug": ["pug", "red"],
+ ".pp": ["puppet", "yellow"],
+ ".epp": ["puppet", "yellow"],
+ ".purs": ["purescript", "white"],
+ ".py": ["python", "blue"],
+ ".jsx": ["react", "blue"],
+ ".spec.jsx": ["react", "orange"],
+ ".test.jsx": ["react", "orange"],
+ ".cjsx": ["react", "blue"],
+ ".spec.tsx": ["react", "orange"],
+ ".test.tsx": ["react", "orange"],
+ ".re": ["reasonml", "red"],
+ ".res": ["rescript", "red"],
+ ".resi": ["rescript", "pink"],
+ ".R": ["R", "blue"],
+ ".rmd": ["R", "blue"],
+ ".rb": ["ruby", "red"],
+ ".erb": ["html_erb", "red"],
+ ".erb.html": ["html_erb", "red"],
+ ".html.erb": ["html_erb", "red"],
+ ".rs": ["rust", "grey-light"],
+ ".sass": ["sass", "pink"],
+ ".scss": ["sass", "pink"],
+ ".springBeans": ["spring", "green"],
+ ".slim": ["slim", "orange"],
+ ".smarty.tpl": ["smarty", "yellow"],
+ ".tpl": ["smarty", "yellow"],
+ ".sbt": ["sbt", "blue"],
+ ".scala": ["scala", "red"],
+ ".sol": ["ethereum", "blue"],
+ ".styl": ["stylus", "green"],
+ ".svelte": ["svelte", "red"],
+ ".swift": ["swift", "orange"],
+ ".sql": ["db", "pink"],
+ ".soql": ["db", "blue"],
+ ".tf": ["terraform", "purple"],
+ ".tf.json": ["terraform", "purple"],
+ ".tfvars": ["terraform", "purple"],
+ ".tfvars.json": ["terraform", "purple"],
+ ".tex": ["tex", "blue"],
+ ".sty": ["tex", "yellow"],
+ ".dtx": ["tex", "orange"],
+ ".ins": ["tex", "white"],
+ ".txt": ["default", "white"],
+ ".toml": ["config", "grey-light"],
+ ".twig": ["twig", "green"],
+ ".ts": ["typescript", "blue"],
+ ".tsx": ["typescript", "blue"],
+ ".spec.ts": ["typescript", "orange"],
+ ".test.ts": ["typescript", "orange"],
+ ".vala": ["vala", "grey-light"],
+ ".vapi": ["vala", "grey-light"],
+ ".component": ["html", "orange"],
+ ".vue": ["vue", "green"],
+ ".wasm": ["wasm", "purple"],
+ ".wat": ["wat", "purple"],
+ ".xml": ["xml", "orange"],
+ ".yml": ["yml", "purple"],
+ ".yaml": ["yml", "purple"],
+ ".pro": ["prolog", "orange"],
+ ".zig": ["zig", "orange"],
+ ".jar": ["zip", "red"],
+ ".zip": ["zip", "grey-light"],
+ ".wgt": ["wgt", "blue"],
+ ".ai": ["illustrator", "yellow"],
+ ".psd": ["photoshop", "blue"],
+ ".pdf": ["pdf", "red"],
+ ".eot": ["font", "red"],
+ ".ttf": ["font", "red"],
+ ".woff": ["font", "red"],
+ ".woff2": ["font", "red"],
+ ".avif": ["image", "purple"],
+ ".gif": ["image", "purple"],
+ ".jpg": ["image", "purple"],
+ ".jpeg": ["image", "purple"],
+ ".png": ["image", "purple"],
+ ".pxm": ["image", "purple"],
+ ".svg": ["svg", "purple"],
+ ".svgx": ["image", "purple"],
+ ".tiff": ["image", "purple"],
+ ".webp": ["image", "purple"],
+ ".sublime-project": ["sublime", "orange"],
+ ".sublime-workspace": ["sublime", "orange"],
+ ".code-search": ["code-search", "purple"],
+ ".sh": ["shell", "green"],
+ ".zsh": ["shell", "green"],
+ ".fish": ["shell", "green"],
+ ".zshrc": ["shell", "green"],
+ ".bashrc": ["shell", "green"],
+ ".mov": ["video", "pink"],
+ ".ogv": ["video", "pink"],
+ ".webm": ["video", "pink"],
+ ".avi": ["video", "pink"],
+ ".mpg": ["video", "pink"],
+ ".mp4": ["video", "pink"],
+ ".mp3": ["audio", "purple"],
+ ".ogg": ["audio", "purple"],
+ ".wav": ["audio", "purple"],
+ ".flac": ["audio", "purple"],
+ ".3ds": ["svg", "blue"],
+ ".3dm": ["svg", "blue"],
+ ".stl": ["svg", "blue"],
+ ".obj": ["svg", "blue"],
+ ".dae": ["svg", "blue"],
+ ".bat": ["windows", "blue"],
+ ".cmd": ["windows", "blue"],
+ ".babelrc": ["babel", "yellow"],
+ ".babelrc.js": ["babel", "yellow"],
+ ".babelrc.cjs": ["babel", "yellow"],
+ ".bazelrc": ["bazel", "grey"],
+ ".bowerrc": ["bower", "orange"],
+ ".codeclimate.yml": ["code-climate", "green"],
+ ".eslintrc": ["eslint", "purple"],
+ ".eslintrc.js": ["eslint", "purple"],
+ ".eslintrc.cjs": ["eslint", "purple"],
+ ".eslintrc.yaml": ["eslint", "purple"],
+ ".eslintrc.yml": ["eslint", "purple"],
+ ".eslintrc.json": ["eslint", "purple"],
+ ".eslintignore": ["eslint", "grey"],
+ ".firebaserc": ["firebase", "orange"],
+ ".gitlab-ci.yml": ["gitlab", "orange"],
+ ".jshintrc": ["javascript", "blue"],
+ ".jscsrc": ["javascript", "blue"],
+ ".stylelintrc": ["stylelint", "white"],
+ ".stylelintrc.json": ["stylelint", "white"],
+ ".stylelintrc.yaml": ["stylelint", "white"],
+ ".stylelintrc.yml": ["stylelint", "white"],
+ ".stylelintrc.js": ["stylelint", "white"],
+ ".stylelintignore": ["stylelint", "grey"],
+ ".direnv": ["config", "grey-light"],
+ ".env": ["config", "grey-light"],
+ ".static": ["config", "grey-light"],
+ ".editorconfig": ["config", "grey-light"],
+ ".slugignore": ["config", "grey-light"],
+ ".tmp": ["clock", "grey-light"],
+ ".htaccess": ["config", "grey-light"],
+ ".key": ["lock", "green"],
+ ".cert": ["lock", "green"],
+ ".cer": ["lock", "green"],
+ ".crt": ["lock", "green"],
+ ".pem": ["lock", "green"],
+ ".DS_Store": ["ignored", "ignore"],
+ },
+ partials: [
+ ["TODO.md", ["todo", "blue"]],
+ ["TODO.txt", ["todo", "blue"]],
+ ["TODO", ["todo", "blue"]],
+ ["Procfile", ["heroku", "purple"]],
+ ["cmakelists.txt", ["makefile", "blue"]],
+ ["CMakeLists.txt", ["makefile", "blue"]],
+ ["CMAKELISTS.txt", ["makefile", "blue"]],
+ ["CMAKELISTS.TXT", ["makefile", "blue"]],
+ ["omakefile", ["makefile", "grey-light"]],
+ ["OMakefile", ["makefile", "grey-light"]],
+ ["OMAKEFILE", ["makefile", "grey-light"]],
+ ["qmakefile", ["makefile", "purple"]],
+ ["QMakefile", ["makefile", "purple"]],
+ ["QMAKEFILE", ["makefile", "purple"]],
+ ["makefile", ["makefile", "orange"]],
+ ["Makefile", ["makefile", "orange"]],
+ ["MAKEFILE", ["makefile", "orange"]],
+ ["CONTRIBUTING.md", ["license", "red"]],
+ ["CONTRIBUTING.txt", ["license", "red"]],
+ ["CONTRIBUTING", ["license", "red"]],
+ ["COMPILING.md", ["license", "orange"]],
+ ["COMPILING.txt", ["license", "orange"]],
+ ["COMPILING", ["license", "orange"]],
+ ["COPYING.md", ["license", "yellow"]],
+ ["COPYING.txt", ["license", "yellow"]],
+ ["COPYING", ["license", "yellow"]],
+ ["LICENCE.md", ["license", "yellow"]],
+ ["LICENSE.md", ["license", "yellow"]],
+ ["LICENCE.txt", ["license", "yellow"]],
+ ["LICENSE.txt", ["license", "yellow"]],
+ ["LICENCE", ["license", "yellow"]],
+ ["LICENSE", ["license", "yellow"]],
+ ["gulpfile.js", ["gulp", "red"]],
+ ["gulpfile", ["gulp", "red"]],
+ ["Gulpfile", ["gulp", "red"]],
+ ["GULPFILE", ["gulp", "red"]],
+ ["docker-compose.override.yaml", ["docker", "pink"]],
+ ["docker-compose.override.yml", ["docker", "pink"]],
+ ["docker-compose.yaml", ["docker", "pink"]],
+ ["docker-compose.yml", ["docker", "pink"]],
+ ["docker-healthcheck", ["docker", "green"]],
+ [".dockerignore", ["docker", "grey"]],
+ ["DOCKERFILE", ["docker", "blue"]],
+ ["Dockerfile", ["docker", "blue"]],
+ ["dockerfile", ["docker", "blue"]],
+ ["gemfile", ["ruby", "red"]],
+ ["Gemfile", ["ruby", "red"]],
+ ["mix", ["hex", "red"]],
+ ],
+ default: ["default", "white"],
+};
+
+const rawIcons = {
+ astro:
+ '',
+ bsl: '',
+ mdo: '',
+ salesforce:
+ '',
+ asm: '',
+ bicep:
+ '',
+ bazel:
+ '',
+ c: '',
+ "c-sharp":
+ '',
+ html: '',
+ cpp: '',
+ clojure:
+ '',
+ coldfusion:
+ '',
+ coffee:
+ '',
+ config:
+ '',
+ crystal:
+ '',
+ crystal_embedded:
+ '',
+ json: '',
+ css: '',
+ csv: '',
+ xls: '',
+ cu: '',
+ cake: '',
+ cake_php:
+ '',
+ d: '',
+ word: '',
+ ejs: '',
+ elixir:
+ '',
+ elixir_script:
+ '',
+ hex: '',
+ elm: '',
+ favicon:
+ '',
+ "f-sharp":
+ '',
+ git: '',
+ go2: '',
+ go: '',
+ godot:
+ '',
+ gradle:
+ '',
+ grails:
+ '',
+ graphql:
+ '',
+ hacklang:
+ '',
+ haml: '',
+ mustache:
+ '',
+ haskell:
+ '',
+ haxe: '',
+ jade: '',
+ java: '',
+ javascript:
+ '',
+ jinja:
+ '',
+ julia:
+ '',
+ karma:
+ '',
+ kotlin:
+ '',
+ dart: '',
+ less: '',
+ liquid:
+ '',
+ livescript:
+ '',
+ lua: '',
+ markdown:
+ '',
+ argdown:
+ '',
+ info: '',
+ clock:
+ '',
+ maven:
+ '',
+ nim: '',
+ github:
+ '',
+ notebook:
+ '',
+ nunjucks:
+ '',
+ npm: '',
+ ocaml:
+ '',
+ odata:
+ '',
+ perl: '',
+ php: '',
+ pipeline:
+ '',
+ pddl: '',
+ plan: '',
+ happenings:
+ '',
+ powershell:
+ '',
+ prisma:
+ '',
+ pug: '',
+ puppet:
+ '',
+ purescript:
+ '',
+ python:
+ '',
+ react:
+ '',
+ reasonml:
+ '',
+ rescript:
+ '',
+ R: '',
+ ruby: '',
+ html_erb:
+ '',
+ rust: '',
+ sass: '',
+ spring:
+ '',
+ slim: '',
+ smarty:
+ '',
+ sbt: '',
+ scala:
+ '',
+ ethereum:
+ '',
+ stylus:
+ '',
+ svelte:
+ '',
+ swift:
+ '',
+ db: '',
+ terraform:
+ '',
+ tex: '',
+ default:
+ '',
+ twig: '',
+ typescript:
+ '',
+ tsconfig:
+ '',
+ vala: '',
+ vue: '',
+ wasm: '',
+ wat: '',
+ xml: '',
+ yml: '',
+ prolog:
+ '',
+ zig: '',
+ zip: '',
+ wgt: '',
+ illustrator:
+ '',
+ photoshop:
+ '',
+ pdf: '',
+ font: '',
+ image:
+ '',
+ svg: '',
+ sublime:
+ '',
+ "code-search":
+ '',
+ shell:
+ '',
+ video:
+ '',
+ audio:
+ '',
+ windows:
+ '',
+ jenkins:
+ '',
+ babel:
+ '',
+ bower:
+ '',
+ docker:
+ '',
+ "code-climate":
+ '',
+ eslint:
+ '',
+ firebase:
+ '',
+ firefox:
+ '',
+ gitlab:
+ '',
+ grunt:
+ '',
+ gulp: '',
+ ionic:
+ '',
+ platformio:
+ '',
+ rollup:
+ '',
+ stylelint:
+ '',
+ yarn: '',
+ webpack:
+ '',
+ lock: '',
+ license:
+ '',
+ makefile:
+ '',
+ heroku:
+ '',
+ todo: '',
+ npm_ignored:
+ '',
+ ignored:
+ '',
+};
+
+type IconDetails = [string, string];
+
+interface SetiTheme {
+ blue: string;
+ grey: string;
+ "grey-light": string;
+ green: string;
+ orange: string;
+ pink: string;
+ purple: string;
+ red: string;
+ white: string;
+ yellow: string;
+ ignore: string;
+}
+
+type Color = keyof SetiTheme;
+
+interface Icon {
+ svg: string;
+ color: Color;
+}
+
+const definitions = rawDefinitions as unknown as {
+ default: IconDetails;
+ extensions: { [extension: string]: IconDetails };
+ files: { [file: string]: IconDetails };
+ partials: [string, IconDetails][];
+};
+const icons = rawIcons as unknown as {
+ [icon: string]: string;
+};
+
+const getDetails = (fileName: string): IconDetails => {
+ if (definitions.files[fileName]) {
+ return definitions.files[fileName];
+ }
+ let extension = fileName.slice(fileName.indexOf("."));
+ while (extension !== "") {
+ if (definitions.extensions[extension]) {
+ return definitions.extensions[extension];
+ }
+ // look for next "."
+ extension = extension.slice(1);
+ extension = extension.slice(extension.indexOf("."));
+ }
+ for (const partial of definitions.partials) {
+ if (fileName.indexOf(partial[0]) > -1) {
+ return partial[1];
+ }
+ }
+ return definitions.default;
+};
+
+export const getIcon = (fileName: string): Icon => {
+ const [icon, color] = getDetails(fileName);
+ return { svg: icons[icon], color } as Icon;
+};
diff --git a/src/utils/markdown/file-tree/rehype-file-tree.ts b/src/utils/markdown/file-tree/rehype-file-tree.ts
new file mode 100644
index 00000000..74e5caa3
--- /dev/null
+++ b/src/utils/markdown/file-tree/rehype-file-tree.ts
@@ -0,0 +1,161 @@
+/**
+ * This was taken from Astro docs:
+ * https://github.com/withastro/docs/blob/83e4e7946933b468f857c76f8d4f9861e37d7059/src/components/internal/rehype-file-tree.ts
+ *
+ * Then modified to work with HTML comments :)
+ */
+import { fromHtml } from "hast-util-from-html";
+import { toString } from "hast-util-to-string";
+import { h } from "hastscript";
+import type { Element, HChild } from "hastscript/lib/core";
+import { CONTINUE, SKIP, visit } from "unist-util-visit";
+import { getIcon } from "./file-tree-icons";
+import replaceAllBetween from "unist-util-replace-all-between";
+import { Node } from "unist";
+import { Root } from "hast";
+
+/** Make a text node with the pass string as its contents. */
+const Text = (value = ""): { type: "text"; value: string } => ({
+ type: "text",
+ value,
+});
+
+/** Convert an HTML string containing an SVG into a HAST element node. */
+const makeSVGIcon = (svgString: string) => {
+ const root = fromHtml(svgString, { fragment: true });
+ const svg = root.children[0] as Element;
+ svg.properties = {
+ ...svg.properties,
+ width: 16,
+ height: 16,
+ class: "tree-icon",
+ "aria-hidden": "true",
+ };
+ return svg;
+};
+
+const FileIcon = (filename: string) => {
+ const { svg } = getIcon(filename);
+ return makeSVGIcon(svg);
+};
+
+const FolderIcon = makeSVGIcon(
+ ''
+);
+
+export const rehypeFileTree = () => {
+ return (tree) => {
+ function replaceFiletreeNodes(nodes: Node[]) {
+ const root = { type: "root", children: nodes } as Root;
+ visit(root, "element", (node) => {
+ // Strip nodes that only contain newlines
+ node.children = node.children.filter(
+ (child) =>
+ child.type === "comment" ||
+ child.type !== "text" ||
+ !/^\n+$/.test(child.value)
+ );
+
+ if (node.tagName !== "li") return CONTINUE;
+
+ // Ensure node has properties so we can assign classes later.
+ if (!node.properties) node.properties = {};
+
+ const [firstChild, ...otherChildren] = node.children;
+
+ const comment: HChild[] = [];
+ if (firstChild.type === "text") {
+ const [filename, ...fragments] = firstChild.value.split(" ");
+ firstChild.value = filename;
+ comment.push(fragments.join(" "));
+ }
+ const subTreeIndex = otherChildren.findIndex(
+ (child) => child.type === "element" && child.tagName === "ul"
+ );
+ const commentNodes =
+ subTreeIndex > -1
+ ? otherChildren.slice(0, subTreeIndex)
+ : [...otherChildren];
+ otherChildren.splice(
+ 0,
+ subTreeIndex > -1 ? subTreeIndex : otherChildren.length
+ );
+ comment.push(...commentNodes);
+
+ const firstChildTextContent = toString(firstChild);
+
+ // Decide a node is a directory if it ends in a `/` or contains another list.
+ const isDirectory =
+ /\/\s*$/.test(firstChildTextContent) ||
+ otherChildren.some(
+ (child) => child.type === "element" && child.tagName === "ul"
+ );
+ const isPlaceholder = /^\s*(\.{3}|…)\s*$/.test(firstChildTextContent);
+ const isHighlighted =
+ firstChild.type === "element" && firstChild.tagName === "strong";
+ const hasContents = otherChildren.length > 0;
+
+ const fileExtension = isDirectory
+ ? "dir"
+ : firstChildTextContent.trim().split(".").pop() || "";
+
+ const icon = h(
+ "span",
+ isDirectory ? FolderIcon : FileIcon(firstChildTextContent)
+ );
+ if (!icon.properties) icon.properties = {};
+ if (isDirectory) {
+ icon.properties["aria-label"] = "Directory";
+ }
+
+ node.properties.class = isDirectory ? "directory" : "file";
+ if (isPlaceholder) node.properties.class += " empty";
+ node.properties["data-filetype"] = fileExtension;
+
+ const treeEntry = h(
+ "span",
+ { class: "tree-entry" },
+ h("span", { class: isHighlighted ? "highlight" : "" }, [
+ isPlaceholder ? null : icon,
+ firstChild,
+ ]),
+ Text(comment.length > 0 ? " " : ""),
+ comment.length > 0
+ ? h("span", { class: "comment" }, ...comment)
+ : Text()
+ );
+
+ if (isDirectory) {
+ node.children = [
+ h("details", { open: hasContents }, [
+ h("summary", treeEntry),
+ ...(hasContents ? otherChildren : [h("ul", h("li", "…"))]),
+ ]),
+ ];
+ // Continue down the tree.
+ return CONTINUE;
+ }
+
+ node.children = [treeEntry, ...otherChildren];
+
+ // Files can’t contain further files or directories, so skip iterating children.
+ return SKIP;
+ });
+
+ return root.children;
+ }
+
+ replaceAllBetween(
+ tree,
+ { type: "raw", value: "" } as never,
+ { type: "raw", value: "" } as never,
+ replaceFiletreeNodes
+ );
+ replaceAllBetween(
+ tree,
+ { type: "comment", value: " filetree:start " } as never,
+ { type: "comment", value: " filetree:end " } as never,
+ replaceFiletreeNodes
+ );
+ };
+};