Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/expo/app/(public)/markdown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import MarkdownScreen from '@app/core/screens/MarkdownScreen'

export default MarkdownScreen
4 changes: 4 additions & 0 deletions apps/expo/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,9 @@ config.resolver.nodeModulesPaths = [
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
// config.resolver.disableHierarchicalLookup = true

// 4. Add .md & .mdx files to the file extensions Metro will handle
config.resolver.sourceExts.push('md', 'mdx');
config.transformer.babelTransformerPath = require.resolve('./transformer.js');

// Export the modified config
module.exports = config
5 changes: 3 additions & 2 deletions apps/expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
"private": true,
"main": "index.js",
"dependencies": {
"@bacons/mdx": "~0.2.0",
"@expo/metro-runtime": "^3.2.1",
"expo": "^51.0.8",
"expo-constants": "~16.0.1",
"expo-image": "~1.12.9",
"expo-linking": "~6.3.1",
"expo-router": "~3.5.14",
"expo-status-bar": "~1.12.1",
Expand All @@ -15,8 +17,7 @@
"react-native": "0.74.1",
"react-native-safe-area-context": "4.10.1",
"react-native-screens": "~3.31.1",
"react-native-web": "~0.19.11",
"expo-image": "~1.12.9"
"react-native-web": "~0.19.11"
},
"devDependencies": {
"@babel/core": "^7.19.3",
Expand Down
10 changes: 10 additions & 0 deletions apps/expo/transformer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const upstreamTransformer = require('@expo/metro-config/babel-transformer');
const MdxTransformer = require("@bacons/mdx/metro-transformer");

module.exports.transform = async (props) => {
// Then pass it to the upstream transformer.
return upstreamTransformer.transform(
// Transpile MDX first.
await MdxTransformer.transform(props)
);
};
4 changes: 4 additions & 0 deletions apps/next/app/(public)/markdown/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use client'
import MarkdownScreen from '@app/core/screens/MarkdownScreen'

export default MarkdownScreen
9 changes: 7 additions & 2 deletions apps/next/next.config.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
const { withExpo } = require("@expo/next-adapter");
const withMDX = require("@next/mdx")();

/** @type {import('next').NextConfig} */
const nextConfig = withExpo({
const nextConfig = withMDX(withExpo({
reactStrictMode: true,
swcMinify: true,
transpilePackages: [
"react-native",
"react-native-web",
"expo",
"@bacons/mdx",
"@bacons/react-views",
"@expo/html-elements",
// Add more React Native / Expo packages here...
],
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
typescript: {
ignoreBuildErrors: true,
},
Expand All @@ -24,6 +29,6 @@ const nextConfig = withExpo({
}
]
}
});
}));

module.exports = nextConfig;
5 changes: 4 additions & 1 deletion apps/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
"version": "1.0.0",
"private": true,
"dependencies": {
"@mdx-js/loader": "^3.0.1",
"@mdx-js/react": "^3.0.1",
"@next/mdx": "^14.1.4",
"@types/mdx": "^2.0.12",
"next": "~14.0.4"
},
"devDependencies": {},
"scripts": {
"dev": "next dev",
"build": "next build",
Expand Down
74 changes: 74 additions & 0 deletions features/app-core/mdx/MarkdownImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { useState, useEffect } from 'react'
import { StyleSheet, View, Dimensions, Image as RNImage, LayoutChangeEvent } from 'react-native'
import { Image } from '../components/Image'
import './markdown.theme.css' // Duplicate of the React-Native styles from this file
import { UniversalImageProps } from '../components/Image.types'

/* --- <MarkdownImage/> --------------------------------------------------------------------------- */

export const MarkdownImage = (props: UniversalImageProps) => {
// Props
const { src, alt } = props

// State
const [imgDimensions, setImgDimensions] = useState({ width: 0, height: 0 })
const [wrapperWidth, setWrapperWidth] = useState(0)

// Flags
const hasWrapperWidth = !!wrapperWidth

// Vars
const imgRatio = imgDimensions.height / imgDimensions.width
const maxWindowWidth = Dimensions.get('window').width
const widthToUse = Math.min(...[imgDimensions.width, wrapperWidth, maxWindowWidth].filter(Boolean))
const heightToUse = widthToUse * imgRatio
const finalDimensions = imgRatio ? { width: widthToUse, height: heightToUse } : imgDimensions

// -- Handlers --

const handleWrapperLayout = (event: LayoutChangeEvent) => {
if (wrapperWidth < 5) setWrapperWidth(event.nativeEvent.layout.width)
}

// -- Effects --

useEffect(() => {
const checkImageDimensions = async () => {
RNImage.getSize(src as string, (width, height) => {
setImgDimensions({ width, height })
})
}
if (typeof src === 'string') checkImageDimensions()
}, [])

// -- Render --

return (
<View
style={{
...styles.imgWrapper,
...(!hasWrapperWidth ? { minWidth: '100%' } : finalDimensions)
}}
onLayout={!hasWrapperWidth ? handleWrapperLayout : undefined}
>
<Image
alt={alt}
src={src}
style={finalDimensions}
contentFit="contain"
/>
</View>
)
}

/* --- Styles ---------------------------------------------------------------------------------- */
// -i- These styles won't work in Next.js for some reason, duplicate them in markdown.theme.css

const styles = StyleSheet.create({
imgWrapper: {
display: 'flex',
position: 'relative',
flexDirection: 'column',
marginTop: 16,
}
})
97 changes: 97 additions & 0 deletions features/app-core/mdx/MarkdownTheme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from 'react'
import { StyleSheet, View, Text } from 'react-native'
import { MarkdownImage } from './MarkdownImage'
import { MDXStyles, MDXComponents } from '@bacons/mdx'
import { Link } from '../navigation/Link'
import './markdown.theme.css' // Duplicate of the React-Native styles from this file

/* --- Types -------------------------------------------------------------------------------------- */

type MarkdownScreenProps = {
children: React.ReactNode
}

/* --- <MarkdownTheme/> --------------------------------------------------------------------------- */

const MarkdownTheme = ({ children }: MarkdownScreenProps) => {
return (
<View id="markdown-theme">
<MDXStyles
h1={styles.h1}
h2={styles.h2}
p={styles.p}
ul={styles.ul}
li={styles.li}
blockquote={styles.blockquote}
a={styles.link}
img={styles.img}
>
<MDXComponents
h1={(props) => <Text {...props} style={{ ...styles.h1 }} />}
h2={(props) => <Text {...props} style={{ ...styles.h2 }} />}
p={(props) => <Text {...props} style={{ ...styles.p }} />}
ul={(props) => <View {...props} style={{ ...styles.ul }} />}
li={(props) => <Text {...props} style={{ ...styles.li }} />}
blockquote={(props) => <View {...props} style={{ ...styles.blockquote }} />} // prettier-ignore
a={(props) => <Link {...props} style={{ ...styles.link }} />}
img={(props) => <MarkdownImage {...props} />}
>
{children}
</MDXComponents>
</MDXStyles>
</View>
)
}

/* --- Styles ---------------------------------------------------------------------------------- */
// -i- These styles won't work in Next.js for some reason, duplicate them in markdown.theme.css

const styles = StyleSheet.create({
h1: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
h2: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 16,
},
p: {
fontSize: 16,
marginBottom: 16,
lineHeight: 22,
},
ul: {
padding: 0,
},
li: {
fontSize: 16,
marginBottom: 16,
lineHeight: 22,
},
blockquote: {
borderLeftColor: 'lightgray',
borderStyle: 'solid',
borderLeftWidth: 6,
fontSize: 16,
paddingLeft: 16,
paddingTop: 4,
lineHeight: 22,
},
link: {
marginTop: 16,
fontSize: 16,
color: 'blue',
textAlign: 'center',
textDecorationLine: 'underline',
},
img: {
maxWidth: '100%',
marginTop: 16,
}
})

/* --- Exports --------------------------------------------------------------------------------- */

export default MarkdownTheme
66 changes: 66 additions & 0 deletions features/app-core/mdx/markdown.theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

/* --- Resets ---------------------------------------------------------------------------------- */

ul {
padding-inline-start: 0px;
list-style: none;
}

blockquote {
margin: 0;
padding: 0;
}

/* --- Markdown theme -------------------------------------------------------------------------- */
/* -i- duplicate from 'MarkdownTheme.tsx', keep them in sync */

#markdown-theme h1 {
font-size: 24px;
font-weight: bold;
margin-bottom: 16px;
}

#markdown-theme h2 {
font-size: 20px;
font-weight: bold;
margin-bottom: 16px;
}

#markdown-theme p {
font-size: 16px;
margin-bottom: 16px;
line-height: 22px;
}

#markdown-theme ul {
padding: 0;
}

#markdown-theme li {
font-size: 16px;
margin-bottom: 16px;
line-height: 22px;
}

#markdown-theme blockquote {
border-width: 0;
border-style: solid;
border-left-color: lightgray;
border-left-width: 6px;
font-size: 16px;
padding-left: 16px;
padding-top: 4px;
line-height: 22px;
}

#markdown-theme a {
margin-top: 16px;
font-size: 16px;
color: blue;
text-align: center;
text-decoration: underline;
}

#markdown-theme img {
max-width: 100%;
}
23 changes: 23 additions & 0 deletions features/app-core/mdx/readme.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import View from 'react-native'
import { Image } from '../components/Image'

<Image
src={require('../assets/aetherspaceLogo.png')}
style={{ marginBottom: 20 }}
width={60}
height={60}
/>

# Universal Expo + Next.js App Router Starter, with MDX

A minimal starter for a **universal Expo + Next.js app** with their respective app routers.

It's a good starting point if you want to:

- ✅ make use of _app-dir file based routing_ in expo and next.js
- ✅ have a _minimal monorepo setup_ with Typescript but no monorepo tool yet
- ✅ leave *all other tech choices* for e.g. styling, dbs, component libs, etc. *up to you*

> Need a more robust, Fully-Stacked, Full-Product, Universal App Setup? Check out **[FullProduct.dev](https://fullproduct.dev) ⚡️**

[![Screenshot of FullProduct.dev](https://github.com/user-attachments/assets/a2eecfd2-7889-4079-944b-1b5af6cf5ddf)](https://fullproduct.dev)
1 change: 1 addition & 0 deletions features/app-core/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const HomeScreen = () => {
<Text style={styles.subtitle}>Open HomeScreen.tsx in features/app-core/screens to start working on your app</Text>
<Link href="/subpages/aetherspace" style={styles.link}>Test navigation</Link>
<Link href="/images" style={styles.link}>Test images</Link>
<Link href="/markdown" style={styles.link}>Test markdown</Link>
<Link href="https://universal-base-starter-docs.vercel.app/" target="_blank" style={styles.link}>Docs</Link>
</View>
)
Expand Down
Loading