├── .eslintrc.js
├── .gitignore
├── .vscode
└── launch.json
├── README.md
├── apps
├── docs
│ ├── .eslintrc.js
│ ├── README.md
│ ├── app
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── page.module.css
│ │ └── page.tsx
│ ├── base.json
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── next.js
│ ├── nextjs.json
│ ├── package.json
│ ├── public
│ │ ├── circles.svg
│ │ ├── next.svg
│ │ ├── turborepo.svg
│ │ └── vercel.svg
│ └── tsconfig.json
├── expo-router-example
│ ├── .gitignore
│ ├── app.d.ts
│ ├── app.json
│ ├── app
│ │ ├── (tabs)
│ │ │ ├── _layout.tsx
│ │ │ ├── index.tsx
│ │ │ └── two.tsx
│ │ ├── +html.tsx
│ │ ├── +not-found.tsx
│ │ ├── _layout.tsx
│ │ └── modal.tsx
│ ├── assets
│ │ ├── fonts
│ │ │ └── SpaceMono-Regular.ttf
│ │ └── images
│ │ │ ├── adaptive-icon.png
│ │ │ ├── favicon.png
│ │ │ ├── icon.png
│ │ │ └── splash.png
│ ├── babel.config.js
│ ├── components
│ │ ├── EditScreenInfo.tsx
│ │ ├── ExternalLink.tsx
│ │ ├── StyledText.tsx
│ │ ├── Themed.tsx
│ │ ├── __tests__
│ │ │ └── StyledText-test.js
│ │ ├── button
│ │ │ ├── button.expo-web.tsx
│ │ │ └── button.tsx
│ │ ├── useClientOnlyValue.ts
│ │ ├── useClientOnlyValue.web.ts
│ │ ├── useColorScheme.ts
│ │ └── useColorScheme.web.ts
│ ├── constants
│ │ └── Colors.ts
│ ├── global.css
│ ├── metro.config.js
│ ├── package.json
│ ├── tailwind.config.js
│ ├── tsconfig.json
│ └── utils
│ │ ├── font-plugin-expo.js
│ │ ├── font-plugin-expo.web.js
│ │ └── font-plugin.js
├── mobile
│ ├── .gitignore
│ ├── App.tsx
│ ├── app.json
│ ├── assets
│ │ ├── adaptive-icon.png
│ │ ├── favicon.png
│ │ ├── icon.png
│ │ ├── splash.png
│ │ └── test.png
│ ├── babel.config.js
│ ├── base.json
│ ├── expo.json
│ ├── index.js
│ ├── metro.config.js
│ ├── package.json
│ └── tsconfig.json
└── web
│ ├── .eslintrc.js
│ ├── README.md
│ ├── app
│ ├── Second
│ │ └── page.tsx
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ ├── page.module.css
│ ├── page.tsx
│ └── registry.tsx
│ ├── base.json
│ ├── components
│ ├── button.next-web.tsx
│ ├── button.tsx
│ └── ui
│ │ └── gluestack-ui-provider
│ │ ├── config.ts
│ │ ├── index.tsx
│ │ └── index.web.tsx
│ ├── gluestack-ui.config.json
│ ├── nativewind-env.d.ts
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── next.js
│ ├── next.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ ├── assets
│ │ └── images
│ │ │ └── splash.png
│ ├── circles.svg
│ ├── next.svg
│ ├── test.png
│ ├── turborepo.svg
│ └── vercel.svg
│ ├── tailwind.config.js
│ └── tsconfig.json
├── base.json
├── library.js
├── package.json
├── packages
├── babel-plugin-universal-image
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ └── index.js
│ └── turbo
│ │ └── generators
│ │ ├── config.ts
│ │ └── templates
│ │ └── component.hbs
├── fonts-loader
│ ├── .eslintrc.js
│ ├── README.md
│ ├── package.json
│ └── src
│ │ ├── index.js
│ │ └── index.next-web.js
├── fonts-plugin
│ ├── .eslintrc.js
│ ├── README.md
│ ├── helpers
│ │ └── index.js
│ ├── package.json
│ └── src
│ │ ├── expo.js
│ │ └── next.js
├── image-expo
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ └── webpack.config.js
├── image-next
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ └── webpack.config.js
├── image
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ └── webpack.config.js
├── link-expo
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ ├── turbo
│ │ └── generators
│ │ │ ├── config.ts
│ │ │ └── templates
│ │ │ └── component.hbs
│ └── webpack.config.js
├── link-next
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ ├── turbo
│ │ └── generators
│ │ │ ├── config.ts
│ │ │ └── templates
│ │ │ └── component.hbs
│ └── webpack.config.js
├── link
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ ├── turbo
│ │ └── generators
│ │ │ ├── config.ts
│ │ │ └── templates
│ │ │ └── component.hbs
│ └── webpack.config.js
├── metro-config
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ ├── index.ts
│ │ └── resolver.ts
│ ├── tsconfig.json
│ └── tsconfig.lint.json
├── navigation
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ ├── turbo
│ │ └── generators
│ │ │ ├── config.ts
│ │ │ └── templates
│ │ │ └── component.hbs
│ └── webpack.config.js
├── next-adapter
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ ├── index.ts
│ │ └── utils.ts
│ ├── tsconfig.json
│ └── tsconfig.lint.json
├── router-expo
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ ├── turbo
│ │ └── generators
│ │ │ ├── config.ts
│ │ │ └── templates
│ │ │ └── component.hbs
│ └── webpack.config.js
├── router-next
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ ├── turbo
│ │ └── generators
│ │ │ ├── config.ts
│ │ │ └── templates
│ │ │ └── component.hbs
│ └── webpack.config.js
├── router
│ ├── .eslintrc.js
│ ├── README.md
│ ├── base.json
│ ├── package.json
│ ├── react-internal.js
│ ├── react-library.json
│ ├── src
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lint.json
│ ├── turbo
│ │ └── generators
│ │ │ ├── config.ts
│ │ │ └── templates
│ │ │ └── component.hbs
│ └── webpack.config.js
└── utils
│ ├── .npmignore
│ ├── .npmrc
│ ├── README.md
│ ├── package.json
│ ├── src
│ ├── index.ts
│ └── postinstall.js
│ ├── tsconfig.json
│ └── turbo
│ └── generators
│ ├── config.ts
│ └── templates
│ └── component.hbs
├── patches
└── react-native-css-interop+0.0.36.patch
├── tsconfig.json
├── turbo.json
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // This configuration only applies to the package manager root.
2 | /** @type {import("eslint").Linter.Config} */
3 | module.exports = {
4 | ignorePatterns: ["apps/**", "packages/**"],
5 | extends: ["./library.js"],
6 | parser: "@typescript-eslint/parser",
7 | parserOptions: {
8 | project: true,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/.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 | # Local env files
9 | .env
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | # Testing
16 | coverage
17 |
18 | # Turbo
19 | .turbo
20 |
21 | # Vercel
22 | .vercel
23 |
24 | # Build Outputs
25 | .next/
26 | out/
27 | build
28 | dist
29 |
30 |
31 | # Debug
32 | npm-debug.log*
33 | yarn-debug.log*
34 | yarn-error.log*
35 |
36 | # Misc
37 | .DS_Store
38 | *.pem
39 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "react-native-ide",
9 | "request": "launch",
10 | "name": "React Native IDE panel",
11 | "ios": {
12 | "configuration": "Debug"
13 | },
14 | "android": {
15 | "variant": "debug"
16 | },
17 | "appRoot": "apps/expo-router-example/expo-router/entry"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Introduction to Unitools
2 |
3 | Unitools is a group of tooling that aims to simplify the process of code sharing between `Next.js`, `Expo`, and `React Native` projects. It's a required and indispensable tool in the realm of cross-platform development today with more and more apps moving towards universal app development. Unitools provides a unified API for handling `images`, `links`, and `route navigation hooks` across all these platforms.
4 |
5 | ## Documentation
6 |
7 | You can find more details on [documentation website](https://unitools.geekyants.com/overview/introduction/).
8 |
9 |
10 | ## Why?
11 |
12 | The challenge of sharing code for navigation and images across platforms like Next.js and Expo has always been a significant hurdle for developers because of the fragmented APIs. These platforms, although powerful in their own right, have distinct structures and syntax, which creates an added layer of complexity when writing code. Unitools is designed to address this specific problem. By enabling seamless code sharing between these platforms, Unitools eliminates the need for developers to write separate sets of code, thus saving time and effort and allowing for a smoother workflow.
13 |
14 | ## How it is different?
15 |
16 | There are other tools in the market that attempt to solve the same problem, such as Solito. These tools, while offering much more features also adds a layer of complexities and learning curve.
17 |
18 | Unitools is designed to be simple and easy to use. It provides a unified API but only for your DX and rely on the bundler to use the actual platform specific code. This makes it easier to understand and use.
19 |
20 | ## What’s included?
21 |
22 | Each package contains three libraries.
23 |
24 | 1. Base package which exports the interface
25 | 2. Expo package
26 | 3. Next.js package
27 |
28 | Below are the packages we have created so far:
29 |
30 | - Link - A unified API for expo and next link
31 | - Image -A unified API for expo and next image
32 | - Router - A unified API for expo and next router
33 |
--------------------------------------------------------------------------------
/apps/docs/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./next.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: true,
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/apps/docs/README.md:
--------------------------------------------------------------------------------
1 | ## Getting Started
2 |
3 | First, run the development server:
4 |
5 | ```bash
6 | yarn dev
7 | ```
8 |
9 | Open [http://localhost:3001](http://localhost:3001) with your browser to see the result.
10 |
11 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
12 |
13 | To create [API routes](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) add an `api/` directory to the `app/` directory with a `route.ts` file. For individual endpoints, create a subfolder in the `api` directory, like `api/hello/route.ts` would map to [http://localhost:3001/api/hello](http://localhost:3001/api/hello).
14 |
15 | ## Learn More
16 |
17 | To learn more about Next.js, take a look at the following resources:
18 |
19 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
20 | - [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial.
21 |
22 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
23 |
24 | ## Deploy on Vercel
25 |
26 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
27 |
28 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
29 |
--------------------------------------------------------------------------------
/apps/docs/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/docs/app/favicon.ico
--------------------------------------------------------------------------------
/apps/docs/app/globals.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --max-width: 1100px;
3 | --border-radius: 12px;
4 | --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
5 | "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
6 | "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
7 |
8 | --foreground-rgb: 255, 255, 255;
9 | --background-start-rgb: 0, 0, 0;
10 | --background-end-rgb: 0, 0, 0;
11 |
12 | --callout-rgb: 20, 20, 20;
13 | --callout-border-rgb: 108, 108, 108;
14 | --card-rgb: 100, 100, 100;
15 | --card-border-rgb: 200, 200, 200;
16 |
17 | --glow-conic: conic-gradient(
18 | from 180deg at 50% 50%,
19 | #2a8af6 0deg,
20 | #a853ba 180deg,
21 | #e92a67 360deg
22 | );
23 | }
24 |
25 | * {
26 | box-sizing: border-box;
27 | padding: 0;
28 | margin: 0;
29 | }
30 |
31 | html,
32 | body {
33 | max-width: 100vw;
34 | overflow-x: hidden;
35 | }
36 |
37 | body {
38 | color: rgb(var(--foreground-rgb));
39 | background: linear-gradient(
40 | to bottom,
41 | transparent,
42 | rgb(var(--background-end-rgb))
43 | )
44 | rgb(var(--background-start-rgb));
45 | }
46 |
47 | a {
48 | color: inherit;
49 | text-decoration: none;
50 | }
51 |
--------------------------------------------------------------------------------
/apps/docs/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./globals.css";
2 | import type { Metadata } from "next";
3 | // import { Inter } from "next/font/google";
4 |
5 | // const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Turborepo",
9 | description: "Generated by create turbo",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: {
15 | children: React.ReactNode;
16 | }): JSX.Element {
17 | return (
18 |
19 |
{children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/apps/docs/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/docs/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/apps/docs/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | module.exports = {
3 | transpilePackages: [
4 | "@unitools/link",
5 | "@unitools/image",
6 | "@unitools/navigation",
7 | ],
8 | };
9 |
--------------------------------------------------------------------------------
/apps/docs/next.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /** @type {import("eslint").Linter.Config} */
6 | module.exports = {
7 | extends: [
8 | "eslint:recommended",
9 | "prettier",
10 | require.resolve("@vercel/style-guide/eslint/next"),
11 | "eslint-config-turbo",
12 | ],
13 | globals: {
14 | React: true,
15 | JSX: true,
16 | },
17 | env: {
18 | node: true,
19 | browser: true,
20 | },
21 | plugins: ["only-warn"],
22 | settings: {
23 | "import/resolver": {
24 | typescript: {
25 | project,
26 | },
27 | },
28 | },
29 | ignorePatterns: [
30 | // Ignore dotfiles
31 | ".*.js",
32 | "node_modules/",
33 | ],
34 | overrides: [{ files: ["*.js?(x)", "*.ts?(x)"] }],
35 | };
36 |
--------------------------------------------------------------------------------
/apps/docs/nextjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Next.js",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "plugins": [{ "name": "next" }],
7 | "module": "ESNext",
8 | "moduleResolution": "Bundler",
9 | "allowJs": true,
10 | "jsx": "preserve",
11 | "noEmit": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev --port 3001",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "eslint . --max-warnings 0"
10 | },
11 | "dependencies": {
12 | "next": "^14.1.1",
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@next/eslint-plugin-next": "^14.1.1",
18 | "@types/eslint": "^8.56.5",
19 | "@types/node": "^20.11.24",
20 | "@types/react": "^18.2.61",
21 | "@types/react-dom": "^18.2.19",
22 | "eslint": "^8.57.0",
23 | "typescript": "^5.3.3"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/apps/docs/public/circles.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/docs/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/docs/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./nextjs.json",
3 | "compilerOptions": {
4 | "plugins": [
5 | {
6 | "name": "next"
7 | }
8 | ]
9 | },
10 | "include": [
11 | "next-env.d.ts",
12 | "next.config.js",
13 | "**/*.ts",
14 | "**/*.tsx",
15 | ".next/types/**/*.ts"
16 | ],
17 | "exclude": [
18 | "node_modules"
19 | ]
20 | }
--------------------------------------------------------------------------------
/apps/expo-router-example/.gitignore:
--------------------------------------------------------------------------------
1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # Expo
7 | .expo/
8 | dist/
9 | web-build/
10 |
11 | # Native
12 | *.orig.*
13 | *.jks
14 | *.p8
15 | *.p12
16 | *.key
17 | *.mobileprovision
18 |
19 | # Metro
20 | .metro-health-check*
21 |
22 | # debug
23 | npm-debug.*
24 | yarn-debug.*
25 | yarn-error.*
26 |
27 | # macOS
28 | .DS_Store
29 | *.pem
30 |
31 | # local env files
32 | .env*.local
33 |
34 | # typescript
35 | *.tsbuildinfo
36 |
37 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
38 | # The following patterns were generated by expo-cli
39 |
40 | expo-env.d.ts
41 | # @end expo-cli
--------------------------------------------------------------------------------
/apps/expo-router-example/app.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "expo-router-example",
4 | "slug": "expo-router-example",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/images/icon.png",
8 | "scheme": "myapp",
9 | "userInterfaceStyle": "automatic",
10 | "splash": {
11 | "image": "./assets/images/splash.png",
12 | "resizeMode": "contain",
13 | "backgroundColor": "#ffffff"
14 | },
15 | "assetBundlePatterns": [
16 | "**/*"
17 | ],
18 | "ios": {
19 | "supportsTablet": true
20 | },
21 | "android": {
22 | "adaptiveIcon": {
23 | "foregroundImage": "./assets/images/adaptive-icon.png",
24 | "backgroundColor": "#ffffff"
25 | }
26 | },
27 | "web": {
28 | "bundler": "metro",
29 | "output": "static",
30 | "favicon": "./assets/images/favicon.png"
31 | },
32 | "plugins": [
33 | "expo-router",
34 | "expo-font"
35 | ],
36 | "experiments": {
37 | "typedRoutes": true
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app/(tabs)/_layout.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import FontAwesome from "@expo/vector-icons/FontAwesome";
3 | import { Tabs } from "expo-router";
4 | import Link from "@unitools/link";
5 | import { Pressable } from "react-native";
6 |
7 | import Colors from "@/constants/Colors";
8 | import { useColorScheme } from "@/components/useColorScheme";
9 | import { useClientOnlyValue } from "@/components/useClientOnlyValue";
10 | // You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
11 | function TabBarIcon(props: {
12 | name: React.ComponentProps["name"];
13 | color: string;
14 | }) {
15 | return ;
16 | }
17 |
18 | export default function TabLayout() {
19 | const colorScheme = useColorScheme();
20 |
21 | return (
22 |
30 | ,
35 | headerRight: () => (
36 |
37 |
38 | {({ pressed }) => (
39 |
45 | )}
46 |
47 |
48 | ),
49 | }}
50 | />
51 | ,
56 | }}
57 | />
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app/(tabs)/index.tsx:
--------------------------------------------------------------------------------
1 | import { Pressable, StyleSheet } from "react-native";
2 |
3 | import EditScreenInfo from "@/components/EditScreenInfo";
4 | import { Text, View } from "@/components/Themed";
5 | import Link from "@unitools/link";
6 | import { useNavigation } from "expo-router";
7 | import Image from "@unitools/image";
8 | import { Image as ExpoImage } from "expo-image";
9 | import type { ImageProps } from "expo-image";
10 | import { useRouter } from "@unitools/router";
11 | import Button from "@/components/button/button";
12 |
13 | export default function TabOneScreen() {
14 | let a: ImageProps = {};
15 |
16 | const router = useRouter();
17 | const blurhash =
18 | "|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj[";
19 |
20 | return (
21 |
22 |
31 | Go to Tab two
32 |
33 | Tab One
34 |
39 |
40 | Go to tabs
41 |
49 |
50 | {/* */}
58 | {
60 | router.push("/(tabs)/two");
61 | }}
62 | >
63 | Go to Tab two
64 |
65 |
66 | );
67 | }
68 |
69 | const styles = StyleSheet.create({
70 | container: {
71 | flex: 1,
72 | alignItems: "center",
73 | justifyContent: "center",
74 | },
75 | title: {
76 | fontSize: 20,
77 | fontWeight: "bold",
78 | },
79 | separator: {
80 | marginVertical: 30,
81 | height: 1,
82 | width: "80%",
83 | },
84 | });
85 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app/(tabs)/two.tsx:
--------------------------------------------------------------------------------
1 | import { Pressable, StyleSheet } from "react-native";
2 |
3 | import EditScreenInfo from "@/components/EditScreenInfo";
4 | import { Text, View } from "@/components/Themed";
5 | import { useRouter } from "@unitools/router";
6 |
7 | export default function TabTwoScreen() {
8 | const router = useRouter();
9 | return (
10 |
11 | Tab Two
12 |
17 |
18 | {
20 | router.back();
21 | }}
22 | >
23 | Back
24 |
25 |
26 | );
27 | }
28 |
29 | const styles = StyleSheet.create({
30 | container: {
31 | flex: 1,
32 | alignItems: "center",
33 | justifyContent: "center",
34 | },
35 | title: {
36 | fontSize: 20,
37 | fontWeight: "bold",
38 | },
39 | separator: {
40 | marginVertical: 30,
41 | height: 1,
42 | width: "80%",
43 | },
44 | });
45 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app/+html.tsx:
--------------------------------------------------------------------------------
1 | import { ScrollViewStyleReset } from 'expo-router/html';
2 |
3 | // This file is web-only and used to configure the root HTML for every
4 | // web page during static rendering.
5 | // The contents of this function only run in Node.js environments and
6 | // do not have access to the DOM or browser APIs.
7 | export default function Root({ children }: { children: React.ReactNode }) {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 | {/*
16 | Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
17 | However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
18 | */}
19 |
20 |
21 | {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
22 |
23 | {/* Add any additional elements that you want globally available on web... */}
24 |
25 | {children}
26 |
27 | );
28 | }
29 |
30 | const responsiveBackground = `
31 | body {
32 | background-color: #fff;
33 | }
34 | @media (prefers-color-scheme: dark) {
35 | body {
36 | background-color: #000;
37 | }
38 | }`;
39 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app/+not-found.tsx:
--------------------------------------------------------------------------------
1 | import { Link, Stack } from 'expo-router';
2 | import { StyleSheet } from 'react-native';
3 |
4 | import { Text, View } from '@/components/Themed';
5 |
6 | export default function NotFoundScreen() {
7 | return (
8 | <>
9 |
10 |
11 | This screen doesn't exist.
12 |
13 |
14 | Go to home screen!
15 |
16 |
17 | >
18 | );
19 | }
20 |
21 | const styles = StyleSheet.create({
22 | container: {
23 | flex: 1,
24 | alignItems: 'center',
25 | justifyContent: 'center',
26 | padding: 20,
27 | },
28 | title: {
29 | fontSize: 20,
30 | fontWeight: 'bold',
31 | },
32 | link: {
33 | marginTop: 15,
34 | paddingVertical: 15,
35 | },
36 | linkText: {
37 | fontSize: 14,
38 | color: '#2e78b7',
39 | },
40 | });
41 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app/_layout.tsx:
--------------------------------------------------------------------------------
1 | import FontAwesome from "@expo/vector-icons/FontAwesome";
2 | import {
3 | DarkTheme,
4 | DefaultTheme,
5 | ThemeProvider,
6 | } from "@react-navigation/native";
7 | import { useFonts } from "@unitools/fonts-loader";
8 | import { Stack } from "expo-router";
9 | import * as SplashScreen from "expo-splash-screen";
10 | import { useEffect } from "react";
11 |
12 | import { useColorScheme } from "@/components/useColorScheme";
13 | import "../global.css";
14 | export {
15 | // Catch any errors thrown by the Layout component.
16 | ErrorBoundary,
17 | } from "expo-router";
18 |
19 | export const unstable_settings = {
20 | // Ensure that reloading on `/modal` keeps a back button present.
21 | initialRouteName: "(tabs)",
22 | };
23 |
24 | // Prevent the splash screen from auto-hiding before asset loading is complete.
25 | SplashScreen.preventAutoHideAsync();
26 |
27 | export default function RootLayout() {
28 | const { loaded, error } = useFonts();
29 |
30 | // Expo Router uses Error Boundaries to catch errors in the navigation tree.
31 | useEffect(() => {
32 | if (error) throw error;
33 | }, [error]);
34 |
35 | useEffect(() => {
36 | if (loaded) {
37 | SplashScreen.hideAsync();
38 | }
39 | }, [loaded]);
40 |
41 | if (!loaded) {
42 | return null;
43 | }
44 |
45 | return ;
46 | }
47 |
48 | function RootLayoutNav() {
49 | const colorScheme = useColorScheme();
50 |
51 | return (
52 |
53 |
54 |
55 |
56 |
57 |
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/apps/expo-router-example/app/modal.tsx:
--------------------------------------------------------------------------------
1 | import { StatusBar } from 'expo-status-bar';
2 | import { Platform, StyleSheet } from 'react-native';
3 |
4 | import EditScreenInfo from '@/components/EditScreenInfo';
5 | import { Text, View } from '@/components/Themed';
6 |
7 | export default function ModalScreen() {
8 | return (
9 |
10 | Modal
11 |
12 |
13 |
14 | {/* Use a light status bar on iOS to account for the black space above the modal */}
15 |
16 |
17 | );
18 | }
19 |
20 | const styles = StyleSheet.create({
21 | container: {
22 | flex: 1,
23 | alignItems: 'center',
24 | justifyContent: 'center',
25 | },
26 | title: {
27 | fontSize: 20,
28 | fontWeight: 'bold',
29 | },
30 | separator: {
31 | marginVertical: 30,
32 | height: 1,
33 | width: '80%',
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/apps/expo-router-example/assets/fonts/SpaceMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/expo-router-example/assets/fonts/SpaceMono-Regular.ttf
--------------------------------------------------------------------------------
/apps/expo-router-example/assets/images/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/expo-router-example/assets/images/adaptive-icon.png
--------------------------------------------------------------------------------
/apps/expo-router-example/assets/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/expo-router-example/assets/images/favicon.png
--------------------------------------------------------------------------------
/apps/expo-router-example/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/expo-router-example/assets/images/icon.png
--------------------------------------------------------------------------------
/apps/expo-router-example/assets/images/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/expo-router-example/assets/images/splash.png
--------------------------------------------------------------------------------
/apps/expo-router-example/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = function (api) {
4 | api.cache(true);
5 | return {
6 | presets: [
7 | ["babel-preset-expo", { jsxImportSource: "nativewind" }],
8 | "nativewind/babel",
9 | ],
10 | plugins: [
11 | [
12 | "module-resolver",
13 | {
14 | alias: {
15 | "@unitools/link": "@unitools/link-expo",
16 | "@unitools/router": "@unitools/router-expo",
17 | "@unitools/image": "@unitools/image-expo",
18 | "@unitools/font-plugin": "@unitools/font-plugin-expo",
19 | },
20 | },
21 | ],
22 | ],
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/EditScreenInfo.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 |
4 | import { ExternalLink } from './ExternalLink';
5 | import { MonoText } from './StyledText';
6 | import { Text, View } from './Themed';
7 |
8 | import Colors from '@/constants/Colors';
9 |
10 | export default function EditScreenInfo({ path }: { path: string }) {
11 | return (
12 |
13 |
14 |
18 | Open up the code for this screen:
19 |
20 |
21 |
25 | {path}
26 |
27 |
28 |
32 | Change any of the text, save the file, and your app will automatically update.
33 |
34 |
35 |
36 |
37 |
40 |
41 | Tap here if your app doesn't automatically update after making changes
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
49 | const styles = StyleSheet.create({
50 | getStartedContainer: {
51 | alignItems: 'center',
52 | marginHorizontal: 50,
53 | },
54 | homeScreenFilename: {
55 | marginVertical: 7,
56 | },
57 | codeHighlightContainer: {
58 | borderRadius: 3,
59 | paddingHorizontal: 4,
60 | },
61 | getStartedText: {
62 | fontSize: 17,
63 | lineHeight: 24,
64 | textAlign: 'center',
65 | },
66 | helpContainer: {
67 | marginTop: 15,
68 | marginHorizontal: 20,
69 | alignItems: 'center',
70 | },
71 | helpLink: {
72 | paddingVertical: 15,
73 | },
74 | helpLinkText: {
75 | textAlign: 'center',
76 | },
77 | });
78 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/ExternalLink.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'expo-router';
2 | import * as WebBrowser from 'expo-web-browser';
3 | import React from 'react';
4 | import { Platform } from 'react-native';
5 |
6 | export function ExternalLink(
7 | props: Omit, 'href'> & { href: string }
8 | ) {
9 | return (
10 | {
16 | if (Platform.OS !== 'web') {
17 | // Prevent the default behavior of linking to the default browser on native.
18 | e.preventDefault();
19 | // Open the link in an in-app browser.
20 | WebBrowser.openBrowserAsync(props.href as string);
21 | }
22 | }}
23 | />
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/StyledText.tsx:
--------------------------------------------------------------------------------
1 | import { Text, TextProps } from './Themed';
2 |
3 | export function MonoText(props: TextProps) {
4 | return ;
5 | }
6 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/Themed.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Learn more about Light and Dark modes:
3 | * https://docs.expo.io/guides/color-schemes/
4 | */
5 |
6 | import { Text as DefaultText, View as DefaultView } from 'react-native';
7 |
8 | import Colors from '@/constants/Colors';
9 | import { useColorScheme } from './useColorScheme';
10 |
11 | type ThemeProps = {
12 | lightColor?: string;
13 | darkColor?: string;
14 | };
15 |
16 | export type TextProps = ThemeProps & DefaultText['props'];
17 | export type ViewProps = ThemeProps & DefaultView['props'];
18 |
19 | export function useThemeColor(
20 | props: { light?: string; dark?: string },
21 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark
22 | ) {
23 | const theme = useColorScheme() ?? 'light';
24 | const colorFromProps = props[theme];
25 |
26 | if (colorFromProps) {
27 | return colorFromProps;
28 | } else {
29 | return Colors[theme][colorName];
30 | }
31 | }
32 |
33 | export function Text(props: TextProps) {
34 | const { style, lightColor, darkColor, ...otherProps } = props;
35 | const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
36 |
37 | return ;
38 | }
39 |
40 | export function View(props: ViewProps) {
41 | const { style, lightColor, darkColor, ...otherProps } = props;
42 | const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
43 |
44 | return ;
45 | }
46 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/__tests__/StyledText-test.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import { MonoText } from '../StyledText';
5 |
6 | it(`renders correctly`, () => {
7 | const tree = renderer.create(Snapshot test!).toJSON();
8 |
9 | expect(tree).toMatchSnapshot();
10 | });
11 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/button/button.expo-web.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Text } from "react-native";
3 |
4 | const Button = () => {
5 | return Button web expo;
6 | };
7 |
8 | export default Button;
9 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/button/button.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Text } from "react-native";
3 |
4 | const Button = () => {
5 | return Button;
6 | };
7 |
8 | export default Button;
9 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/useClientOnlyValue.ts:
--------------------------------------------------------------------------------
1 | // This function is web-only as native doesn't currently support server (or build-time) rendering.
2 | export function useClientOnlyValue(server: S, client: C): S | C {
3 | return client;
4 | }
5 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/useClientOnlyValue.web.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | // `useEffect` is not invoked during server rendering, meaning
4 | // we can use this to determine if we're on the server or not.
5 | export function useClientOnlyValue(server: S, client: C): S | C {
6 | const [value, setValue] = React.useState(server);
7 | React.useEffect(() => {
8 | setValue(client);
9 | }, [client]);
10 |
11 | return value;
12 | }
13 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/useColorScheme.ts:
--------------------------------------------------------------------------------
1 | export { useColorScheme } from 'react-native';
2 |
--------------------------------------------------------------------------------
/apps/expo-router-example/components/useColorScheme.web.ts:
--------------------------------------------------------------------------------
1 | // NOTE: The default React Native styling doesn't support server rendering.
2 | // Server rendered styles should not change between the first render of the HTML
3 | // and the first render on the client. Typically, web developers will use CSS media queries
4 | // to render different styles on the client and server, these aren't directly supported in React Native
5 | // but can be achieved using a styling library like Nativewind.
6 | export function useColorScheme() {
7 | return 'light';
8 | }
9 |
--------------------------------------------------------------------------------
/apps/expo-router-example/constants/Colors.ts:
--------------------------------------------------------------------------------
1 | const tintColorLight = '#2f95dc';
2 | const tintColorDark = '#fff';
3 |
4 | export default {
5 | light: {
6 | text: '#000',
7 | background: '#fff',
8 | tint: tintColorLight,
9 | tabIconDefault: '#ccc',
10 | tabIconSelected: tintColorLight,
11 | },
12 | dark: {
13 | text: '#fff',
14 | background: '#000',
15 | tint: tintColorDark,
16 | tabIconDefault: '#ccc',
17 | tabIconSelected: tintColorDark,
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/apps/expo-router-example/global.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/apps/expo-router-example/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig } = require("expo/metro-config");
2 | const findYarnWorkspaceRoot = require("find-yarn-workspace-root");
3 | const path = require("path");
4 | const { withUnitools } = require("@unitools/metro-config");
5 | const { withNativeWind } = require("nativewind/metro");
6 |
7 | // Find the project and workspace directories
8 | const projectRoot = __dirname;
9 | // This can be replaced with `find-yarn-workspace-root`
10 | // if you are using Yarn Workspaces
11 | const monorepoRoot = findYarnWorkspaceRoot(projectRoot);
12 | // const monorepoRoot = path.resolve(projectRoot, "../..");
13 |
14 | const config = getDefaultConfig(projectRoot, {});
15 |
16 | config.watchFolders = [monorepoRoot];
17 | // 2. Let Metro know where to resolve packages and in what order
18 | config.resolver.nodeModulesPaths = [
19 | path.resolve(projectRoot, "node_modules"),
20 | path.resolve(monorepoRoot, "node_modules"),
21 | ];
22 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
23 | config.resolver.disableHierarchicalLookup = true;
24 |
25 | module.exports = withNativeWind(withUnitools(config), {
26 | input: "./global.css",
27 | });
28 |
--------------------------------------------------------------------------------
/apps/expo-router-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "expo-router-example",
3 | "main": "expo-router/entry",
4 | "version": "1.0.0",
5 | "scripts": {
6 | "start": "EXPO_USE_METRO_WORKSPACE_ROOT=1 npx expo start",
7 | "android": "EXPO_USE_METRO_WORKSPACE_ROOT=1 npx expo start --android",
8 | "ios": "EXPO_USE_METRO_WORKSPACE_ROOT=1 npx expo start --ios",
9 | "web": "EXPO_USE_METRO_WORKSPACE_ROOT=1 npx expo start --web",
10 | "test": "jest --watchAll"
11 | },
12 | "jest": {
13 | "preset": "jest-expo"
14 | },
15 | "dependencies": {
16 | "@expo/vector-icons": "^14.0.0",
17 | "@react-navigation/native": "^6.0.2",
18 | "expo": "51",
19 | "expo-font": "~12.0.7",
20 | "expo-linking": "~6.3.1",
21 | "expo-router": "~3.5.16",
22 | "expo-splash-screen": "~0.27.5",
23 | "expo-status-bar": "~1.12.1",
24 | "expo-system-ui": "~3.0.6",
25 | "expo-web-browser": "~13.0.3",
26 | "nativewind": "^4.0.1",
27 | "react": "18.2.0",
28 | "react-dom": "18.2.0",
29 | "react-native": "0.74.2",
30 | "react-native-reanimated": "^3.12.1",
31 | "react-native-safe-area-context": "4.10.1",
32 | "react-native-screens": "3.31.1",
33 | "react-native-web": "~0.19.6",
34 | "tailwindcss": "^3.4.4"
35 | },
36 | "devDependencies": {
37 | "@babel/core": "^7.20.0",
38 | "@types/react": "~18.2.79",
39 | "expo-atlas": "^0.3.0",
40 | "jest": "^29.2.1",
41 | "jest-expo": "~51.0.1",
42 | "react-test-renderer": "18.2.0",
43 | "typescript": "~5.3.3"
44 | },
45 | "private": true
46 | }
47 |
--------------------------------------------------------------------------------
/apps/expo-router-example/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 |
3 | module.exports = {
4 | // NOTE: Update this to include the paths to all of your component files.
5 | content: ["./app/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"],
6 | presets: [require("nativewind/preset")],
7 | theme: {
8 | fontFamily: {
9 | body: "SpaceMono",
10 | },
11 | extend: {},
12 | },
13 | plugins: [require("@unitools/font-plugin/expo")],
14 | };
15 |
--------------------------------------------------------------------------------
/apps/expo-router-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "expo/tsconfig.base",
3 | "compilerOptions": {
4 | "strict": true,
5 | "paths": {
6 | "@/*": [
7 | "./*"
8 | ]
9 | }
10 | },
11 | "include": [
12 | "**/*.ts",
13 | "**/*.tsx",
14 | ".expo/types/**/*.ts",
15 | "expo-env.d.ts"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/apps/expo-router-example/utils/font-plugin-expo.js:
--------------------------------------------------------------------------------
1 | const plugin = require("tailwindcss/plugin");
2 |
3 | function splitFontWeight(fontWeight) {
4 | const regex = /^([1-9]00)([a-zA-Z]+)$/;
5 | const match = fontWeight.match(regex);
6 |
7 | if (match) {
8 | return {
9 | numericWeight: match[1],
10 | textWeight: match[2],
11 | };
12 | }
13 | }
14 |
15 | function parseFontString(fontString) {
16 | const regex = /^([^_]+)(?:_([^_]+))?(?:_([^_]+))?$/;
17 | const match = fontString.match(regex);
18 |
19 | if (match) {
20 | let fontConfiguration = {};
21 | if (match[1]) {
22 | fontConfiguration.fontFamily = match[1].toLowerCase();
23 | }
24 | if (match[2]) {
25 | const splittedFontWeight = splitFontWeight(match[2]);
26 | fontConfiguration.fontWeight = splittedFontWeight.numericWeight;
27 | }
28 | if (match[3]) {
29 | fontConfiguration.fontStyle = match[3].toLowerCase();
30 | }
31 | return fontConfiguration;
32 | }
33 | }
34 |
35 | console.log("hahahahahahahahahaha normal");
36 |
37 | module.exports = plugin(function ({ matchUtilities, theme, e }) {
38 | console.log("hahahahahahahahahaha insdie");
39 | matchUtilities(
40 | {
41 | font: (val) => {
42 | const parsedFonts = parseFontString(val);
43 | return parsedFonts;
44 | },
45 | },
46 | {
47 | values: theme("fontFamily"),
48 | }
49 | );
50 | });
51 |
--------------------------------------------------------------------------------
/apps/expo-router-example/utils/font-plugin-expo.web.js:
--------------------------------------------------------------------------------
1 | const plugin = require("tailwindcss/plugin");
2 |
3 | function splitFontWeight(fontWeight) {
4 | const regex = /^([1-9]00)([a-zA-Z]+)$/;
5 | const match = fontWeight.match(regex);
6 |
7 | if (match) {
8 | return {
9 | numericWeight: match[1],
10 | textWeight: match[2],
11 | };
12 | }
13 | }
14 |
15 | function parseFontString(fontString) {
16 | const regex = /^([^_]+)(?:_([^_]+))?(?:_([^_]+))?$/;
17 | const match = fontString.match(regex);
18 |
19 | if (match) {
20 | let fontConfiguration = {};
21 | if (match[1]) {
22 | fontConfiguration.fontFamily = match[1].toLowerCase();
23 | }
24 | if (match[2]) {
25 | const splittedFontWeight = splitFontWeight(match[2]);
26 | fontConfiguration.fontWeight = splittedFontWeight.numericWeight;
27 | }
28 | if (match[3]) {
29 | fontConfiguration.fontStyle = match[3].toLowerCase();
30 | }
31 | return fontConfiguration;
32 | }
33 | }
34 |
35 | module.exports = plugin(function ({ matchUtilities, theme, e }) {
36 | matchUtilities(
37 | {
38 | font: (val) => {
39 | const parsedFonts = parseFontString(val);
40 |
41 | return parsedFonts;
42 | },
43 | },
44 | {
45 | values: theme("fontFamily"),
46 | }
47 | );
48 | });
49 |
--------------------------------------------------------------------------------
/apps/expo-router-example/utils/font-plugin.js:
--------------------------------------------------------------------------------
1 | const plugin = require("tailwindcss/plugin");
2 |
3 | const fff = plugin(function ({ addUtilities, theme, e }) {
4 | const fonts = theme("fontFamily");
5 | const ogFontWeight = theme("fontWeight");
6 |
7 | let modifiedFontFamily = {};
8 |
9 | Object.keys(ogFontWeight).forEach((fw) => {
10 | let updatedFw = fw;
11 |
12 | if (ogFontWeight[fw] === "400") updatedFw = "regular";
13 | modifiedFontFamily[fw] =
14 | `${ogFontWeight[fw]}${updatedFw.charAt(0).toUpperCase()}${updatedFw.slice(1)}`;
15 | });
16 |
17 | const combinedUtilities = {};
18 |
19 | const fontStyles = {
20 | italic: "Italic",
21 | };
22 |
23 | Object.entries(fonts).forEach(([fontName, fontValue]) => {
24 | const capitalizedFontName =
25 | fontValue.charAt(0).toUpperCase() + fontValue.slice(1);
26 | const defaultClassName = `.${e(`font-${fontName}`)}`;
27 | combinedUtilities[defaultClassName] = {
28 | fontFamily: `${capitalizedFontName}_400Regular`,
29 | };
30 | const italicDefaultClassName = `.${e(`font-${fontName}`)}.${e(`font-${fontStyles["italic"]}`)}`;
31 | combinedUtilities[italicDefaultClassName] = {
32 | fontFamily: `${capitalizedFontName}_400Regular_Italic`,
33 | };
34 | Object.entries(modifiedFontFamily).forEach(
35 | ([weightValue, weightSuffix]) => {
36 | Object.entries(fontStyles).forEach(([styleName, styleSuffix]) => {
37 | const className = `.${e(`font-${fontName}`)}.${e(`font-${weightValue}`)}.${e(`font-${styleName}`)}`;
38 | combinedUtilities[className] = {
39 | fontFamily: `${capitalizedFontName}_${weightSuffix}_${styleSuffix}`,
40 | };
41 |
42 | const regularClassName = `.${e(`font-${fontName}`)}.${e(`font-${weightValue}`)}`;
43 | combinedUtilities[regularClassName] = {
44 | fontFamily: `${capitalizedFontName}_${weightSuffix}`,
45 | };
46 | });
47 | }
48 | );
49 | });
50 |
51 | addUtilities(combinedUtilities);
52 | });
53 |
54 | export default fff;
55 |
--------------------------------------------------------------------------------
/apps/mobile/.gitignore:
--------------------------------------------------------------------------------
1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # Expo
7 | .expo/
8 | dist/
9 | web-build/
10 |
11 | # Native
12 | *.orig.*
13 | *.jks
14 | *.p8
15 | *.p12
16 | *.key
17 | *.mobileprovision
18 |
19 | # Metro
20 | .metro-health-check*
21 |
22 | # debug
23 | npm-debug.*
24 | yarn-debug.*
25 | yarn-error.*
26 |
27 | # macOS
28 | .DS_Store
29 | *.pem
30 |
31 | # local env files
32 | .env*.local
33 |
34 | # typescript
35 | *.tsbuildinfo
36 |
--------------------------------------------------------------------------------
/apps/mobile/App.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Pressable, Text, View } from "react-native";
3 |
4 | import { preview } from "react-native-ide";
5 |
6 | preview();
7 |
8 | function MyButton({ name }: any) {
9 | return (
10 |
11 | {name}
12 |
13 | );
14 | }
15 |
16 | const App = () => {
17 | return (
18 |
25 | Open up App.tsx to start working on your app!
26 |
27 | );
28 | };
29 |
30 | export default App;
31 |
--------------------------------------------------------------------------------
/apps/mobile/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "mobile",
4 | "slug": "mobile",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "userInterfaceStyle": "light",
9 | "splash": {
10 | "image": "./assets/splash.png",
11 | "resizeMode": "contain",
12 | "backgroundColor": "#ffffff"
13 | },
14 | "assetBundlePatterns": ["**/*"],
15 | "ios": {
16 | "supportsTablet": true
17 | },
18 | "android": {
19 | "adaptiveIcon": {
20 | "foregroundImage": "./assets/adaptive-icon.png",
21 | "backgroundColor": "#ffffff"
22 | }
23 | },
24 | "web": {
25 | "favicon": "./assets/favicon.png"
26 | },
27 | "scheme": "myapp"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/mobile/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/mobile/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/apps/mobile/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/mobile/assets/favicon.png
--------------------------------------------------------------------------------
/apps/mobile/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/mobile/assets/icon.png
--------------------------------------------------------------------------------
/apps/mobile/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/mobile/assets/splash.png
--------------------------------------------------------------------------------
/apps/mobile/assets/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/mobile/assets/test.png
--------------------------------------------------------------------------------
/apps/mobile/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const myBabel = require("@unitools/babel-plugin-universal-image");
3 | // path.resolve(__dirname, "../../packages/babel-plugin-universal-image/src")
4 | module.exports = function (api) {
5 | api.cache(true);
6 | return {
7 | presets: ["babel-preset-expo"],
8 | plugins: [
9 | // [
10 | // "module-resolver",
11 | // {
12 | // alias: {
13 | // "@gluestack-ui/universal-navigation": path.resolve(
14 | // __dirname,
15 | // "../../packages/navigation/src"
16 | // ),
17 | // // "@gluestack-ui/babel-plugin-universal-image": path.resolve(
18 | // // __dirname,
19 | // // "../../packages/babel-plugin-universal-image/src"
20 | // // ),
21 | // "@gluestack-ui/universal-image": path.resolve(
22 | // __dirname,
23 | // "../../packages/image/src"
24 | // ),
25 | // },
26 | // },
27 | // ],
28 | [
29 | myBabel,
30 | {
31 | assetPath: "assets",
32 | },
33 | ],
34 | ],
35 | };
36 | };
37 |
--------------------------------------------------------------------------------
/apps/mobile/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/mobile/expo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Expo",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "allowJs": true,
7 | "esModuleInterop": true,
8 | "jsx": "react-native",
9 | "lib": ["DOM", "ESNext"],
10 | "moduleResolution": "node",
11 | "noEmit": true,
12 | "resolveJsonModule": true,
13 | "skipLibCheck": true,
14 | "target": "ESNext"
15 | },
16 |
17 | "exclude": [
18 | "node_modules",
19 | "babel.config.js",
20 | "metro.config.js",
21 | "jest.config.js"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/mobile/index.js:
--------------------------------------------------------------------------------
1 | import { registerRootComponent } from "expo";
2 |
3 | import App from "./App";
4 |
5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6 | // It also ensures that whether you load the app in Expo Go or in a native build,
7 | // the environment is set up appropriately
8 | registerRootComponent(App);
9 |
--------------------------------------------------------------------------------
/apps/mobile/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig } = require("expo/metro-config");
2 | const findYarnWorkspaceRoot = require("find-yarn-workspace-root");
3 | const path = require("path");
4 |
5 | // Find the project and workspace directories
6 | const projectRoot = __dirname;
7 | // This can be replaced with `find-yarn-workspace-root`
8 | // if you are using Yarn Workspaces
9 | const monorepoRoot = findYarnWorkspaceRoot(projectRoot);
10 | // const monorepoRoot = path.resolve(projectRoot, "../..");
11 |
12 | const config = getDefaultConfig(projectRoot);
13 |
14 | // 1. Watch all files within the monorepo
15 | config.watchFolders = [monorepoRoot];
16 | // 2. Let Metro know where to resolve packages and in what order
17 | config.resolver.nodeModulesPaths = [
18 | path.resolve(projectRoot, "node_modules"),
19 | path.resolve(monorepoRoot, "node_modules"),
20 | ];
21 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
22 | config.resolver.disableHierarchicalLookup = true;
23 |
24 | module.exports = config;
25 |
--------------------------------------------------------------------------------
/apps/mobile/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobile",
3 | "version": "1.0.0",
4 | "main": "./index.js",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web"
10 | },
11 | "dependencies": {
12 | "@react-navigation/native": "^6.1.17",
13 | "@react-navigation/native-stack": "^6.9.25",
14 | "@unitools/image": "*",
15 | "@unitools/link": "*",
16 | "@unitools/navigation": "*",
17 | "expo": "~50.0.13",
18 | "expo-image": "^1.10.6",
19 | "expo-status-bar": "~1.11.1",
20 | "react": "18.2.0",
21 | "react-native": "0.73.5",
22 | "react-native-ide": "^0.0.2",
23 | "react-native-safe-area-context": "4.8.2",
24 | "react-native-screens": "~3.29.0"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.20.0",
28 | "@types/react": "~18.2.45",
29 | "babel-plugin-module-resolver": "^5.0.0",
30 | "typescript": "^5.1.3"
31 | },
32 | "private": true
33 | }
34 |
--------------------------------------------------------------------------------
/apps/mobile/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./expo.json",
3 | "compilerOptions": {
4 | "strict": true,
5 | "moduleResolution": "NodeNext",
6 | "module": "NodeNext"
7 | }
8 | }
--------------------------------------------------------------------------------
/apps/web/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./next.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: true,
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/apps/web/README.md:
--------------------------------------------------------------------------------
1 | ## Getting Started
2 |
3 | First, run the development server:
4 |
5 | ```bash
6 | yarn dev
7 | ```
8 |
9 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
10 |
11 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
12 |
13 | To create [API routes](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) add an `api/` directory to the `app/` directory with a `route.ts` file. For individual endpoints, create a subfolder in the `api` directory, like `api/hello/route.ts` would map to [http://localhost:3000/api/hello](http://localhost:3000/api/hello).
14 |
15 | ## Learn More
16 |
17 | To learn more about Next.js, take a look at the following resources:
18 |
19 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
20 | - [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial.
21 |
22 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
23 |
24 | ## Deploy on Vercel
25 |
26 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
27 |
28 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
29 |
--------------------------------------------------------------------------------
/apps/web/app/Second/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 |
4 | export default function SecondScreen({ navigation }: any) {
5 | return (
6 |
7 |
Second Screen
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/web/app/favicon.ico
--------------------------------------------------------------------------------
/apps/web/app/globals.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --max-width: 1100px;
3 | --border-radius: 12px;
4 | --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
5 | "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
6 | "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
7 |
8 | --foreground-rgb: 255, 255, 255;
9 | --background-start-rgb: 0, 0, 0;
10 | --background-end-rgb: 0, 0, 0;
11 |
12 | --callout-rgb: 20, 20, 20;
13 | --callout-border-rgb: 108, 108, 108;
14 | --card-rgb: 100, 100, 100;
15 | --card-border-rgb: 200, 200, 200;
16 |
17 | --glow-conic: conic-gradient(
18 | from 180deg at 50% 50%,
19 | #2a8af6 0deg,
20 | #a853ba 180deg,
21 | #e92a67 360deg
22 | );
23 | }
24 |
25 | * {
26 | box-sizing: border-box;
27 | padding: 0;
28 | margin: 0;
29 | }
30 |
31 | html,
32 | body {
33 | max-width: 100vw;
34 | overflow-x: hidden;
35 | }
36 |
37 | body {
38 | color: rgb(var(--foreground-rgb));
39 | background: linear-gradient(
40 | to bottom,
41 | transparent,
42 | rgb(var(--background-end-rgb))
43 | )
44 | rgb(var(--background-start-rgb));
45 | }
46 |
47 | a {
48 | color: inherit;
49 | text-decoration: none;
50 | }
51 |
52 | @tailwind base;
53 | @tailwind components;
54 | @tailwind utilities;
55 |
--------------------------------------------------------------------------------
/apps/web/app/layout.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useFonts } from "@unitools/fonts-loader";
3 | import "./globals.css";
4 | import { GluestackUIProvider } from "@/components/ui/gluestack-ui-provider";
5 |
6 | const { inter } = useFonts();
7 |
8 | import StyledJsxRegistry from "./registry";
9 | export default function RootLayout({
10 | children,
11 | }: Readonly<{
12 | children: React.ReactNode;
13 | }>) {
14 | return (
15 |
16 |
17 |
18 | {children}
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/apps/web/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Link from "@unitools/link";
3 | import React from "react";
4 | import { useRouter } from "@unitools/router";
5 | import Image from "@unitools/image";
6 | import Button from "../components/button";
7 | import { Text } from "react-native";
8 |
9 | const App = () => {
10 | const router = useRouter();
11 | const blurhash = "blur";
12 | return (
13 |
14 |
Helllo text check
15 |
16 |
17 |
18 |
19 |
27 |
28 | );
29 | };
30 |
31 | export default App;
32 |
--------------------------------------------------------------------------------
/apps/web/app/registry.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import React, { useRef, useState } from 'react';
4 | import { useServerInsertedHTML } from 'next/navigation';
5 | import { StyleRegistry, createStyleRegistry } from 'styled-jsx';
6 | import { Main } from 'next/document';
7 | // @ts-ignore
8 | import { AppRegistry } from 'react-native-web';
9 | import { flush } from '@gluestack-ui/nativewind-utils/flush';
10 |
11 | export default function StyledJsxRegistry({
12 | children,
13 | }: {
14 | children: React.ReactNode;
15 | }) {
16 | // Only create stylesheet once with lazy initial state
17 | // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
18 | const [jsxStyleRegistry] = useState(() => createStyleRegistry());
19 | const isServerInserted = useRef(false);
20 |
21 | useServerInsertedHTML(() => {
22 | AppRegistry.registerComponent('Main', () => Main);
23 | const { getStyleElement } = AppRegistry.getApplication('Main');
24 | if (!isServerInserted.current) {
25 | isServerInserted.current = true;
26 | const styles = [getStyleElement(), jsxStyleRegistry.styles(), flush()];
27 | jsxStyleRegistry.flush();
28 | return <>{styles}>;
29 | }
30 | });
31 |
32 | return {children};
33 | }
34 |
--------------------------------------------------------------------------------
/apps/web/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/components/button.next-web.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | console.log("Hello world next web");
4 |
5 | const Button = () => {
6 | return Button Next
;
7 | };
8 |
9 | export default Button;
10 |
--------------------------------------------------------------------------------
/apps/web/components/button.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | console.log("hello world normal");
4 |
5 | const Button = () => {
6 | return Button
;
7 | };
8 |
9 | export default Button;
10 |
--------------------------------------------------------------------------------
/apps/web/components/ui/gluestack-ui-provider/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { config } from "./config";
4 | import { View } from "react-native";
5 | import { OverlayProvider } from "@gluestack-ui/overlay";
6 | import { ToastProvider } from "@gluestack-ui/toast";
7 |
8 | export function GluestackUIProvider({
9 | mode = "light",
10 | ...props
11 | }: {
12 | mode?: "light" | "dark";
13 | children?: any;
14 | }) {
15 | return (
16 |
24 |
25 | {props.children}
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/apps/web/components/ui/gluestack-ui-provider/index.web.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import React, { useEffect } from 'react';
3 | import { config } from './config';
4 | import { OverlayProvider } from '@gluestack-ui/overlay';
5 | import { ToastProvider } from '@gluestack-ui/toast';
6 | import { setFlushStyles } from '@gluestack-ui/nativewind-utils/flush';
7 |
8 | const styleTagId = 'gluestack-ui-nativewind';
9 | const createStyle = (styleTagId: any) => {
10 | let style = document.createElement('style');
11 | style.id = styleTagId;
12 | style.appendChild(document.createTextNode(''));
13 | return style;
14 | };
15 |
16 | export function GluestackUIProvider({
17 | mode = 'light',
18 | ...props
19 | }: {
20 | mode?: 'light' | 'dark';
21 | children?: any;
22 | }) {
23 | const stringcssvars = Object.keys(config[mode]).reduce((acc, cur) => {
24 | acc += `${cur}:${config[mode][cur]};`;
25 | return acc;
26 | }, '');
27 |
28 | setFlushStyles(`:root {${stringcssvars}} `);
29 |
30 | useEffect(() => {
31 | if (config[mode] && typeof document !== 'undefined') {
32 | const element = document.documentElement;
33 | if (element) {
34 | element.classList.add(mode);
35 | element.classList.remove(mode === 'light' ? 'dark' : 'light');
36 | const head = element.querySelector('head');
37 | let style = head?.querySelector(`[id='${styleTagId}']`);
38 | if (!style) {
39 | style = createStyle(styleTagId);
40 | }
41 | style.innerHTML = `:root {${stringcssvars}} `;
42 | if (head) head.appendChild(style);
43 | }
44 | }
45 | // eslint-disable-next-line react-hooks/exhaustive-deps
46 | }, [mode]);
47 |
48 | return (
49 |
50 | {props.children}
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/apps/web/gluestack-ui.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "tailwind": {
3 | "config": "tailwind.config.js",
4 | "css": "app/globals.css"
5 | },
6 | "app": {
7 | "entry": "app/layout.tsx",
8 | "components": "components/ui"
9 | }
10 | }
--------------------------------------------------------------------------------
/apps/web/nativewind-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/web/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/apps/web/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const { withUnitools } = require("@unitools/next-adapter");
3 | const config = withUnitools({
4 | transpilePackages: ["nativewind", "react-native-css-interop"],
5 | });
6 |
7 | module.exports = config;
8 |
--------------------------------------------------------------------------------
/apps/web/next.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /** @type {import("eslint").Linter.Config} */
6 | module.exports = {
7 | extends: [
8 | "eslint:recommended",
9 | "prettier",
10 | require.resolve("@vercel/style-guide/eslint/next"),
11 | "eslint-config-turbo",
12 | ],
13 | globals: {
14 | React: true,
15 | JSX: true,
16 | },
17 | env: {
18 | node: true,
19 | browser: true,
20 | },
21 | plugins: ["only-warn"],
22 | settings: {
23 | "import/resolver": {
24 | typescript: {
25 | project,
26 | },
27 | },
28 | },
29 | ignorePatterns: [
30 | // Ignore dotfiles
31 | ".*.js",
32 | "node_modules/",
33 | ],
34 | overrides: [{ files: ["*.js?(x)", "*.ts?(x)"] }],
35 | };
36 |
--------------------------------------------------------------------------------
/apps/web/next.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Next.js",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "plugins": [{ "name": "next" }],
7 | "module": "ESNext",
8 | "moduleResolution": "Bundler",
9 | "allowJs": true,
10 | "jsx": "preserve",
11 | "noEmit": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "eslint . --max-warnings 0"
10 | },
11 | "dependencies": {
12 | "@react-navigation/native": "^6.1.17",
13 | "@unitools/image": "*",
14 | "@unitools/navigation": "*",
15 | "@unitools/link": "*",
16 | "expo-image": "^1.10.6",
17 | "next": "^14.1.1",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0",
20 | "react-native": "0.73.6",
21 | "react-native-web": "latest",
22 | "postcss": "latest",
23 | "autoprefixer": "latest",
24 | "@gluestack/ui-next-adapter": "latest",
25 | "@types/react-native": "^0.73.0",
26 | "tailwindcss": "3.4.3",
27 | "nativewind": "4.0.36",
28 | "@gluestack-ui/overlay": "latest",
29 | "@gluestack-ui/toast": "latest",
30 | "@gluestack-ui/nativewind-utils": "latest",
31 | "react-native-svg": "13.4.0"
32 | },
33 | "devDependencies": {
34 | "@next/eslint-plugin-next": "^14.1.1",
35 | "@types/eslint": "^8.56.5",
36 | "@types/node": "^20.11.24",
37 | "@types/react": "^18.2.61",
38 | "@types/react-dom": "^18.2.19",
39 | "eslint": "^8.57.0",
40 | "typescript": "^5.3.3",
41 | "jscodeshift": "0.15.2",
42 | "prettier": "^3.3.2"
43 | }
44 | }
--------------------------------------------------------------------------------
/apps/web/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/web/public/assets/images/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/web/public/assets/images/splash.png
--------------------------------------------------------------------------------
/apps/web/public/circles.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/web/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/web/public/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeekyAnts/unitools/476813b80309b02f9e9fbdf78561c973af086b3e/apps/web/public/test.png
--------------------------------------------------------------------------------
/apps/web/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/web/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 |
3 | module.exports = {
4 | // NOTE: Update this to include the paths to all of your component files.
5 | content: ["./app/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"],
6 | presets: [require("nativewind/preset")],
7 | theme: {
8 | fontFamily: {
9 | body: "Inter_800Bold",
10 | },
11 | extend: {},
12 | },
13 | plugins: [require("@unitools/font-plugin/next")],
14 | };
15 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./next.json",
3 | "compilerOptions": {
4 | "jsx": "preserve",
5 | "plugins": [
6 | {
7 | "name": "next"
8 | }
9 | ],
10 | "paths": {
11 | "@/*": ["./*"],
12 | "@/assets/*": ["./public/assets/*"]
13 | },
14 | "jsxImportSource": "nativewind"
15 | },
16 | "include": [
17 | "next-env.d.ts",
18 | "next.config.js",
19 | "**/*.ts",
20 | "**/*.tsx",
21 | ".next/types/**/*.ts"
22 | ],
23 | "exclude": ["node_modules"]
24 | }
25 |
--------------------------------------------------------------------------------
/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/library.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /** @type {import("eslint").Linter.Config} */
6 | module.exports = {
7 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
8 | plugins: ["only-warn"],
9 | globals: {
10 | React: true,
11 | JSX: true,
12 | },
13 | env: {
14 | node: true,
15 | },
16 | settings: {
17 | "import/resolver": {
18 | typescript: {
19 | project,
20 | },
21 | },
22 | },
23 | ignorePatterns: [
24 | // Ignore dotfiles
25 | ".*.js",
26 | "node_modules/",
27 | "dist/",
28 | ],
29 | overrides: [
30 | {
31 | files: ["*.js?(x)", "*.ts?(x)"],
32 | },
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "unitools",
3 | "private": true,
4 | "scripts": {
5 | "build": "turbo build",
6 | "dev": "turbo dev",
7 | "lint": "turbo lint",
8 | "format": "prettier --write \"**/*.{ts,tsx,md}\"",
9 | "postinstall": "patch-package"
10 | },
11 | "devDependencies": {
12 | "prettier": "^3.2.5",
13 | "turbo": "latest"
14 | },
15 | "engines": {
16 | "node": ">=18"
17 | },
18 | "packageManager": "yarn@1.22.21",
19 | "workspaces": [
20 | "apps/*",
21 | "packages/*"
22 | ]
23 | }
--------------------------------------------------------------------------------
/packages/babel-plugin-universal-image/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/babel-plugin-universal-image [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/babel-plugin-universal-image` package. This needs to be used hand-in-hand with the `@unitools/image` package.
4 |
5 | ## Installation
6 |
7 | ### Install all the dependencies
8 |
9 | ```bash
10 | npm install @unitools/babel-plugin-universal-image
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add -D @unitools/babel-plugin-universal-image
17 | ```
18 |
19 | ### For Expo
20 |
21 | Add babel plugin to your `babel.config.js` file.
22 |
23 | ```js
24 | // babel.config.js
25 |
26 | module.exports = {
27 | plugins: [
28 | [
29 | "@unitools/babel-plugin-universal-image",
30 | {
31 | assets: "src/assets", // Path to your assets folder where images are stored
32 | },
33 | ],
34 | ],
35 | };
36 | ```
37 |
38 | ## What this does?
39 |
40 | Image sourcing works differently in Next.js and Expo. This plugin helps you to use the same API for both projects. This plugin will require the image path and it will automatically resolve the path on expo projects dynamically.
41 |
42 | So if you write the following code:
43 |
44 | ```jsx
45 | import Image from "@unitools/image";
46 |
47 | export default function Home() {
48 | return ;
49 | }
50 | ```
51 |
52 | This babel plugin will convert the above code to:
53 |
54 | ```jsx
55 | import Image from "@unitools/image";
56 |
57 | export default function Home() {
58 | return ;
59 | }
60 | ```
61 |
--------------------------------------------------------------------------------
/packages/babel-plugin-universal-image/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/babel-plugin-universal-image",
3 | "version": "1.0.0",
4 | "private": false,
5 | "exports": {
6 | ".": "./dist/index.js"
7 | },
8 | "scripts": {
9 | "lint": "eslint . --max-warnings 0",
10 | "build": "esbuild ./src/index.js --bundle --platform=node --packages=external --outfile=dist/index.js"
11 | },
12 | "devDependencies": {
13 | "@turbo/gen": "^1.12.4",
14 | "@types/eslint": "^8.56.5",
15 | "@types/node": "^20.11.24",
16 | "@types/react": "^18.2.61",
17 | "@types/react-dom": "^18.2.19",
18 | "esbuild": "^0.20.2",
19 | "eslint": "^8.57.0",
20 | "react": "^18.2.0",
21 | "typescript": "^5.3.3"
22 | },
23 | "dependencies": {
24 | "@react-navigation/native": "^6.1.16",
25 | "expo-image": "^1.10.6"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/babel-plugin-universal-image/src/index.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | module.exports = function universalImagePlugin() {
3 | let ImageComponentIdentifier = null;
4 | let assetPath = "";
5 |
6 | const addRequireCall = (srcAttribute, sourcePath) => {
7 | if (!sourcePath.startsWith("https") && !sourcePath.startsWith("file://")) {
8 | let srcString = "";
9 | srcString = path.join(assetPath, sourcePath);
10 | srcAttribute.value = {
11 | type: "JSXExpressionContainer",
12 | expression: {
13 | type: "CallExpression",
14 | callee: {
15 | type: "Identifier",
16 | name: "require",
17 | },
18 | arguments: [
19 | {
20 | type: "StringLiteral",
21 | value: srcString,
22 | },
23 | ],
24 | },
25 | };
26 | }
27 | };
28 |
29 | try {
30 | return {
31 | visitor: {
32 | ImportDeclaration(path) {
33 | if (
34 | path.node.source.value === "@unitools/image" ||
35 | path.node.source.value === "@unitools/image-expo"
36 | ) {
37 | ImageComponentIdentifier = path.node.specifiers[0].local.name;
38 | }
39 | },
40 | JSXOpeningElement(jsxOpeningElementPath, state) {
41 | if (
42 | jsxOpeningElementPath?.node?.name?.name === ImageComponentIdentifier
43 | ) {
44 | const { assetPath: absAssetPath } = state.opts;
45 | assetPath = path.resolve(process.cwd(), absAssetPath);
46 |
47 | const srcAttribute = jsxOpeningElementPath.node.attributes.find(
48 | (attribute) => attribute.name.name === "source"
49 | );
50 |
51 | if (srcAttribute.value.type === "JSXExpressionContainer") {
52 | if (srcAttribute.value.expression.type === "StringLiteral") {
53 | const sourcePath = srcAttribute.value.expression.value;
54 | addRequireCall(srcAttribute, sourcePath);
55 | } else if (srcAttribute.value.expression.type === "Identifier") {
56 | const identifier = srcAttribute.value.expression.name;
57 | const scope = jsxOpeningElementPath.scope;
58 | const scopeVar = scope.getBinding(identifier);
59 | const sourcePath = scopeVar.path.node.init.value;
60 | addRequireCall(srcAttribute, sourcePath);
61 | }
62 | } else if (srcAttribute.value.type === "StringLiteral") {
63 | const sourcePath = srcAttribute.value.value;
64 | addRequireCall(srcAttribute, sourcePath);
65 | }
66 | }
67 | },
68 | },
69 | };
70 | } catch (error) {
71 | console.error("Error in @unitools/babel-plugin-universal-image", error);
72 | }
73 | };
74 |
--------------------------------------------------------------------------------
/packages/babel-plugin-universal-image/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/babel-plugin-universal-image/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/fonts-loader/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/fonts-loader/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/metro-config [ALPHA]
2 |
3 | ### This is the official documentation for the @unitools/metro-config package.
4 |
5 | ## Features
6 |
7 | - Support for .expo-web.{js|ts|jsx|tsx} file extensions.
8 |
9 | ### Installation
10 |
11 | To install `@unitools/metro-config`, use either of the following commands:
12 |
13 | ```bash
14 | npm install @unitools/metro-config
15 | ```
16 |
17 | or
18 |
19 | ```bash
20 | yarn add @unitools/metro-config
21 | ```
22 |
23 | ## Usage
24 |
25 | Add following to your `metro.config.js` file.
26 |
27 | ```jsx
28 | const { getDefaultConfig } = require("expo/metro-config");
29 | const { withUnitools } = require("@unitools/metro-config");
30 |
31 | const projectRoot = __dirname;
32 |
33 | const config = getDefaultConfig(projectRoot, {});
34 |
35 | module.exports = withUnitools(config);
36 | ```
37 |
--------------------------------------------------------------------------------
/packages/fonts-loader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/fonts",
3 | "version": "0.0.1",
4 | "private": false,
5 | "main": "src",
6 | "files": [
7 | "src"
8 | ],
9 | "exports": {
10 | "./next": {
11 | "import": "./src/index.next-web.js",
12 | "require": "./src/index.next-web.js"
13 | },
14 | "./expo": {
15 | "import": "./src/index.js",
16 | "require": "./src/index.js"
17 | }
18 | },
19 | "scripts": {
20 | "build": "tsc"
21 | },
22 | "devDependencies": {
23 | "eslint": "^8.57.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/fonts-loader/src/index.js:
--------------------------------------------------------------------------------
1 | import { useFonts as useExpoFonts } from "expo-font";
2 | export const useFonts = (params) => {
3 | const [loaded, error] = useExpoFonts(params);
4 | const result = []
5 | Object.keys(params).forEach((item) => {
6 | result.push({
7 | [item]: {
8 | className: item,
9 | variable: item,
10 | style: {
11 | fontFamily: item,
12 | }
13 | }
14 | })
15 | })
16 | return {
17 | loaded,
18 | error,
19 | result
20 | };
21 | }
--------------------------------------------------------------------------------
/packages/fonts-loader/src/index.next-web.js:
--------------------------------------------------------------------------------
1 | export const useFonts = (params) => {
2 | let loaded = true;
3 | let error = false
4 | let variableString = "";
5 |
6 | params.forEach((font) => {
7 | variableString = variableString + " " + font?.variable;
8 | });
9 |
10 | return {
11 | loaded,
12 | error,
13 | variables: variableString
14 | };
15 | };
16 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/fonts-plugin/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/fonts-plugin/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/metro-config [ALPHA]
2 |
3 | ### This is the official documentation for the @unitools/metro-config package.
4 |
5 | ## Features
6 |
7 | - Support for .expo-web.{js|ts|jsx|tsx} file extensions.
8 |
9 | ### Installation
10 |
11 | To install `@unitools/metro-config`, use either of the following commands:
12 |
13 | ```bash
14 | npm install @unitools/metro-config
15 | ```
16 |
17 | or
18 |
19 | ```bash
20 | yarn add @unitools/metro-config
21 | ```
22 |
23 | ## Usage
24 |
25 | Add following to your `metro.config.js` file.
26 |
27 | ```jsx
28 | const { getDefaultConfig } = require("expo/metro-config");
29 | const { withUnitools } = require("@unitools/metro-config");
30 |
31 | const projectRoot = __dirname;
32 |
33 | const config = getDefaultConfig(projectRoot, {});
34 |
35 | module.exports = withUnitools(config);
36 | ```
37 |
--------------------------------------------------------------------------------
/packages/fonts-plugin/helpers/index.js:
--------------------------------------------------------------------------------
1 | function splitFontWeight(fontWeight) {
2 | const regex = /^([1-9]00)([a-zA-Z]+)$/;
3 | const match = fontWeight.match(regex);
4 |
5 | if (match) {
6 | return {
7 | numericWeight: match[1],
8 | textWeight: match[2],
9 | };
10 | }
11 | }
12 |
13 | function parseFontExpoString(fontString) {
14 | if (typeof fontString !== "string") return fontString;
15 |
16 | const regex = /^([^_]+)(?:_([^_]+))?(?:_([^_]+))?$/;
17 | const match = fontString.match(regex);
18 |
19 | if (match) {
20 | let fontConfiguration = {};
21 | if (match[1]) {
22 | fontConfiguration.fontFamily = `${match[1].replace(" ", "-").toLowerCase()}`;
23 | }
24 | if (match[2]) {
25 | const splittedFontWeight = splitFontWeight(match[2]);
26 | fontConfiguration.fontWeight = splittedFontWeight.numericWeight;
27 | }
28 | if (match[3]) {
29 | fontConfiguration.fontStyle = match[3].toLowerCase();
30 | }
31 | return fontConfiguration;
32 | }
33 | }
34 | function parseFontNextString(fontString) {
35 | if (typeof fontString !== "string") return fontString;
36 |
37 | const regex = /^([^_]+)(?:_([^_]+))?(?:_([^_]+))?$/;
38 | const match = fontString.match(regex);
39 |
40 | if (match) {
41 | let fontConfiguration = {};
42 | if (match[1]) {
43 | fontConfiguration.fontFamily = `var(--${match[1].replace(" ", "-").toLowerCase()})`;
44 | }
45 | if (match[2]) {
46 | const splittedFontWeight = splitFontWeight(match[2]);
47 | fontConfiguration.fontWeight = splittedFontWeight.numericWeight;
48 | }
49 | if (match[3]) {
50 | fontConfiguration.fontStyle = match[3].toLowerCase();
51 | }
52 | return fontConfiguration;
53 | }
54 | }
55 |
56 | export { parseFontExpoString,parseFontNextString }
--------------------------------------------------------------------------------
/packages/fonts-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/tailwindcss-fonts",
3 | "version": "0.0.1",
4 | "private": false,
5 | "exports": {
6 | "./next": {
7 | "import": "./src/next.js",
8 | "require": "./src/next.js"
9 | },
10 | "./expo": {
11 | "import": "./src/expo.js",
12 | "require": "./src/expo.js"
13 | }
14 | },
15 | "devDependencies": {
16 | "eslint": "^8.57.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/fonts-plugin/src/expo.js:
--------------------------------------------------------------------------------
1 | const plugin = require("tailwindcss/plugin");
2 | const { parseFontExpoString } = require("../helpers");
3 |
4 |
5 | module.exports = plugin(function ({ matchUtilities, theme, e }) {
6 | matchUtilities(
7 | {
8 | font: (val) => {
9 | const parsedFonts = parseFontExpoString(val)
10 | return parsedFonts;
11 | },
12 | },
13 | {
14 | values: theme("fontFamily"),
15 | }
16 | );
17 | });
18 |
--------------------------------------------------------------------------------
/packages/fonts-plugin/src/next.js:
--------------------------------------------------------------------------------
1 | const plugin = require("tailwindcss/plugin");
2 | const { parseFontNextString } = require("../helpers");
3 |
4 |
5 | module.exports = plugin(function ({ matchUtilities, theme, e }) {
6 | matchUtilities(
7 | {
8 | font: (val) => {
9 | const parsedFonts = parseFontNextString(val);
10 | return parsedFonts;
11 | },
12 | },
13 | {
14 | values: theme("fontFamily"),
15 | }
16 | );
17 | });
18 |
--------------------------------------------------------------------------------
/packages/image-expo/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/image-expo/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/image-expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/image-expo",
3 | "version": "0.0.5",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "build": "yarn type-check && tsc",
13 | "prepare": "yarn type-check && tsc",
14 | "lint": "eslint . --max-warnings 0"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-env": "^7.24.3",
19 | "@babel/preset-react": "^7.24.1",
20 | "@babel/preset-typescript": "^7.24.1",
21 | "@turbo/gen": "^1.12.4",
22 | "@types/eslint": "^8.56.5",
23 | "@types/node": "^20.11.24",
24 | "@types/react": "^18.2.61",
25 | "@types/react-dom": "^18.2.19",
26 | "babel-loader": "^9.1.3",
27 | "esbuild": "^0.20.2",
28 | "eslint": "^8.57.0",
29 | "react": "^18.2.0",
30 | "ts-loader": "^9.5.1",
31 | "typescript": "^5.3.3",
32 | "webpack": "^5.91.0",
33 | "webpack-cli": "^5.1.4"
34 | },
35 | "peerDependencies": {
36 | "@unitools/image": "*",
37 | "expo-image": "^1.10.6"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/image-expo/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/image-expo/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/image-expo/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Image as ExpoImage } from "expo-image";
2 | import { forwardRef } from "react";
3 |
4 | const Image = forwardRef((props: any, ref: any) => {
5 | const { height, width, style, ...restProps } = props;
6 |
7 | let updatedStyles = Array.isArray(style) ? [...style] : [{ ...style }];
8 |
9 | if (Array.isArray(updatedStyles)) {
10 | updatedStyles.unshift({ height, width });
11 | } else {
12 | updatedStyles = [{ height, width }, style];
13 | }
14 |
15 | return ;
16 | });
17 |
18 | Image.displayName = "ExpoImage";
19 |
20 | export default Image;
21 |
--------------------------------------------------------------------------------
/packages/image-expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src"
8 | ],
9 | "exclude": [
10 | "node_modules",
11 | "dist"
12 | ]
13 | }
--------------------------------------------------------------------------------
/packages/image-expo/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/image-expo/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | "expo-image": "commonjs expo-image",
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/packages/image-next/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/image-next/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/image-next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/image-next",
3 | "version": "0.0.6",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "build": "yarn type-check && tsc",
13 | "prepare": "yarn type-check && tsc",
14 | "lint": "eslint . --max-warnings 0"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-env": "^7.24.3",
19 | "@babel/preset-react": "^7.24.1",
20 | "@babel/preset-typescript": "^7.24.1",
21 | "@turbo/gen": "^1.12.4",
22 | "@types/eslint": "^8.56.5",
23 | "@types/node": "^20.11.24",
24 | "@types/react": "^18.2.61",
25 | "@types/react-dom": "^18.2.19",
26 | "babel-loader": "^9.1.3",
27 | "esbuild": "^0.20.2",
28 | "eslint": "^8.57.0",
29 | "react": "^18.2.0",
30 | "ts-loader": "^9.5.1",
31 | "typescript": "^5.3.3",
32 | "webpack": "^5.91.0",
33 | "webpack-cli": "^5.1.4"
34 | },
35 | "peerDependencies": {
36 | "expo-image": "^1.10.6"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/image-next/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/image-next/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/image-next/src/index.tsx:
--------------------------------------------------------------------------------
1 | import NextImage from "next/image";
2 | import { forwardRef } from "react";
3 |
4 | const Image = forwardRef((props: any, ref: any) => {
5 | let { source, placeholder, contentFit, contentPosition, ...restProps } =
6 | props;
7 |
8 | let nextImageProps: any = {
9 | src: source,
10 | };
11 |
12 | if (restProps?.height === "100%" && restProps?.width === "100%") {
13 | nextImageProps.fill = true;
14 | delete restProps.height;
15 | delete restProps.width;
16 | }
17 |
18 | if (source?.uri) {
19 | nextImageProps.src = source?.uri;
20 | }
21 |
22 | return (
23 |
34 | );
35 | });
36 |
37 | Image.displayName = "NextImage";
38 |
39 | export default Image;
40 |
--------------------------------------------------------------------------------
/packages/image-next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src"
8 | ],
9 | "exclude": [
10 | "node_modules",
11 | "dist"
12 | ]
13 | }
--------------------------------------------------------------------------------
/packages/image-next/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/image-next/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | "expo-image": "commonjs expo-image",
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/packages/image/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/image/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/image [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/image` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/image
11 | npm install -D @unitools/babel-plugin-image
12 | ```
13 |
14 | or
15 |
16 | ```bash
17 | yarn add @unitools/image
18 | yarn add -D @unitools/babel-plugin-image
19 | ```
20 |
21 | Add module resolver to your `next.config.js` file.
22 |
23 | ```js
24 | // next.config.js
25 |
26 | module.exports = {
27 | webpack(config) {
28 | config.resolve.alias["@unitools/image"] = "next/image";
29 | return config;
30 | },
31 | };
32 | ```
33 |
34 | ## For Expo
35 |
36 | ### Installation
37 |
38 | ```bash
39 | npm install @unitools/image expo-image
40 | npm install -D @unitools/babel-plugin-image
41 | ```
42 |
43 | or
44 |
45 | ```bash
46 | yarn add @unitools/image expo-image
47 | yarn add -D @unitools/babel-plugin-image
48 | ```
49 |
50 | Add babel plugin to your `babel.config.js` file.
51 |
52 | ```js
53 | // babel.config.js
54 |
55 | module.exports = {
56 | plugins: [
57 | [
58 | "@unitools/babel-plugin-image",
59 | {
60 | assets: "src/assets", // Path to your assets folder where images are stored
61 | },
62 | ],
63 | ],
64 | };
65 | ```
66 |
67 | ## Usage
68 |
69 | ```jsx
70 | import Image from "@unitools/image";
71 |
72 | export default function Home() {
73 | return (
74 |
75 |
76 |
77 | );
78 | }
79 | ```
80 |
81 | ## Props
82 |
83 | | Prop | Type | Default | Description | Status | Support Status |
84 | | ----------------- | ------------------------ | ------- | ----------------- | -------- | -------------- |
85 | | src | string \| NextImage Type | | Image source | required | ✅ |
86 | | alt | string | | Image alt text | required | ✅ |
87 | | width | number | | Image width | required | ✅ |
88 | | height | number | | Image height | required | ✅ |
89 | | loader | string | | Image loader | - | ❌ |
90 | | fill | string | | Image fill | - | ❌ |
91 | | sizes | string | | Image sizes | - | ❌ |
92 | | quality | number (integer 1-100) | | Image quality | - | ❌ |
93 | | priority | boolean | | Image priority | - | ✅ |
94 | | placeholder | string | | Image placeholder | - | ✅ |
95 | | style | object | | Image style | - | ✅ |
96 | | onError | function | | Error function | - | ✅ |
97 | | onLoad | function | | Load function | - | ✅ |
98 | | onLoadingComplete | function | | Callback function | - | ❌ |
99 | | loading | string | | Image loading | - | ❌ |
100 | | blurDataURL | string | | Image blur data | - | ❌ |
101 |
--------------------------------------------------------------------------------
/packages/image/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/image/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/image",
3 | "version": "0.0.5",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "build": "yarn type-check && tsc",
13 | "prepare": "yarn type-check && tsc",
14 | "lint": "eslint . --max-warnings 0"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-env": "^7.24.3",
19 | "@babel/preset-react": "^7.24.1",
20 | "@babel/preset-typescript": "^7.24.1",
21 | "@turbo/gen": "^1.12.4",
22 | "@types/eslint": "^8.56.5",
23 | "@types/node": "^20.11.24",
24 | "@types/react": "^18.2.61",
25 | "@types/react-dom": "^18.2.19",
26 | "babel-loader": "^9.1.3",
27 | "eslint": "^8.57.0",
28 | "react": "^18.2.0",
29 | "ts-loader": "^9.5.1",
30 | "typescript": "^5.3.3"
31 | },
32 | "peerDependencies": {
33 | "expo-image": "^1.10.6"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/image/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/image/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/image/src/index.tsx:
--------------------------------------------------------------------------------
1 | import type { ImageStyle, ImageContentFit } from "expo-image";
2 |
3 | export type ImageSource = {
4 | /**
5 | * A string representing the resource identifier for the image,
6 | * which could be an http address, a local file path, or the name of a static image resource.
7 | */
8 | uri: string;
9 | };
10 |
11 | export interface IImageProps {
12 | /**
13 | * An image to display while loading the proper image and no image has been displayed yet or the source is unset.
14 | */
15 | placeholder?: ImageSource | string;
16 | /**
17 | * The text that's read by the screen reader when the user interacts with the image. Sets the the `alt` tag on web which is used for web crawlers and link traversal. Is an alias for `accessibilityLabel`.
18 | *
19 | * @alias accessibilityLabel
20 | * @default undefined
21 | */
22 | alt: string;
23 |
24 | /**
25 | * The image source, either a remote URL, a local file resource or a number that is the result of the `require()` function.
26 | * When provided as an array of sources, the source that fits best into the container size and is closest to the screen scale
27 | * will be chosen. In this case it is important to provide `width`, `height` and `scale` properties.
28 | */
29 | source?: ImageSource | number | string;
30 | height?: number | string;
31 | width?: number | string;
32 | style?: ImageStyle | ImageStyle[] | any;
33 | contentFit?: ImageContentFit;
34 | contentPosition?: string;
35 | onError?: (error: any) => void;
36 | priority?: any;
37 | }
38 |
39 | function Image(_: IImageProps) {
40 | return null;
41 | }
42 |
43 | export default Image;
44 |
--------------------------------------------------------------------------------
/packages/image/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src"
8 | ],
9 | "exclude": [
10 | "node_modules",
11 | "dist"
12 | ]
13 | }
--------------------------------------------------------------------------------
/packages/image/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/image/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | "expo-image": "commonjs expo-image",
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/packages/link-expo/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/link-expo/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/link [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/link` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/link
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add @unitools/link
17 | ```
18 |
19 | Add module resolver to your `next.config.js` file.
20 |
21 | ```js
22 | // next.config.js
23 |
24 | module.exports = {
25 | webpack(config) {
26 | config.resolve.alias["@unitools/link"] = "next/link";
27 | return config;
28 | },
29 | };
30 | ```
31 |
32 | ## For Expo
33 |
34 | ### Installation
35 |
36 | Install `@unitools/link` and the peer dependency `@react-navigation/native`.
37 |
38 | ```bash
39 | npm install @unitools/link @react-navigation/native
40 | ```
41 |
42 | or
43 |
44 | ```bash
45 | yarn add @unitools/link @react-navigation/native
46 | ```
47 |
48 | ## Usage
49 |
50 | ```jsx
51 | import Link from "@unitools/link";
52 |
53 | export default function Home() {
54 | return (
55 |
56 |
57 | About
58 |
59 |
60 | );
61 | }
62 | ```
63 |
64 | ## Props
65 |
66 | | Prop | Type | Default | Description | status | Support Status |
67 | | -------- | ---------------- | ------- | -------------------------------------------------- | -------- | -------------- |
68 | | href | String or Object | | href url | required | ✅ |
69 | | replace | Boolean | | Replace the current history state | - | ✅ |
70 | | scroll | Boolean | | - | - | ❌ |
71 | | prefetch | Boolean | | If true, this will prefetch the url | - | ❌ |
72 | | passHref | Boolean | | Forces Link to send the href property to its child | - | ❌ |
73 | | shallow | Boolean | | - | - | ❌ |
74 | | locale | Boolean | | If true, the active locale is automatically | - | ❌ |
75 |
--------------------------------------------------------------------------------
/packages/link-expo/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/link-expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/link-expo",
3 | "version": "0.0.5",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "webpack",
14 | "build:tsc": "yarn type-check && tsc",
15 | "prepare": "yarn type-check && tsc",
16 | "build-old": "yarn type-check && esbuild src/index.tsx --loader:.tsx=ts --bundle --minify --platform=node --outfile=dist/index.js"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.24.3",
20 | "@babel/preset-env": "^7.24.3",
21 | "@babel/preset-react": "^7.24.1",
22 | "@babel/preset-typescript": "^7.24.1",
23 | "@turbo/gen": "^1.12.4",
24 | "@types/eslint": "^8.56.5",
25 | "@types/node": "^20.12.3",
26 | "@types/react": "^18.2.74",
27 | "@types/react-dom": "^18.2.23",
28 | "babel-loader": "^9.1.3",
29 | "esbuild": "^0.20.2",
30 | "eslint": "^8.57.0",
31 | "expo-router": "^3.4.8",
32 | "react": "^18.2.0",
33 | "ts-loader": "^9.5.1",
34 | "typescript": "^5.4.3",
35 | "webpack": "^5.91.0",
36 | "webpack-cli": "^5.1.4"
37 | },
38 | "peerDependencies": {
39 | "@react-navigation/native": "^6.1.16",
40 | "@unitools/link": "*",
41 | "react": "^18.2.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/link-expo/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/link-expo/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/link-expo/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Link as ExpoLink } from "expo-router";
2 | import React from "react";
3 | import type { LinkProps } from "@unitools/link";
4 | import { forwardRef } from "react";
5 |
6 | // ExpoLink doesn't support have ref prop
7 | export const Link = forwardRef((props: LinkProps, _?: any) => {
8 | return ;
9 | });
10 |
11 | export default Link;
12 |
--------------------------------------------------------------------------------
/packages/link-expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/link-expo/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/link-expo/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/link-expo/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/link-expo/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/link-next/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/link-next/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/link [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/link` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/link
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add @unitools/link
17 | ```
18 |
19 | Add module resolver to your `next.config.js` file.
20 |
21 | ```js
22 | // next.config.js
23 |
24 | module.exports = {
25 | webpack(config) {
26 | config.resolve.alias["@unitools/link"] = "next/link";
27 | return config;
28 | },
29 | };
30 | ```
31 |
32 | ## For Expo
33 |
34 | ### Installation
35 |
36 | Install `@unitools/link` and the peer dependency `@react-navigation/native`.
37 |
38 | ```bash
39 | npm install @unitools/link @react-navigation/native
40 | ```
41 |
42 | or
43 |
44 | ```bash
45 | yarn add @unitools/link @react-navigation/native
46 | ```
47 |
48 | ## Usage
49 |
50 | ```jsx
51 | import Link from "@unitools/link";
52 |
53 | export default function Home() {
54 | return (
55 |
56 |
57 | About
58 |
59 |
60 | );
61 | }
62 | ```
63 |
64 | ## Props
65 |
66 | | Prop | Type | Default | Description | status | Support Status |
67 | | -------- | ---------------- | ------- | -------------------------------------------------- | -------- | -------------- |
68 | | href | String or Object | | href url | required | ✅ |
69 | | replace | Boolean | | Replace the current history state | - | ✅ |
70 | | scroll | Boolean | | - | - | ❌ |
71 | | prefetch | Boolean | | If true, this will prefetch the url | - | ❌ |
72 | | passHref | Boolean | | Forces Link to send the href property to its child | - | ❌ |
73 | | shallow | Boolean | | - | - | ❌ |
74 | | locale | Boolean | | If true, the active locale is automatically | - | ❌ |
75 |
--------------------------------------------------------------------------------
/packages/link-next/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/link-next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/link-next",
3 | "version": "0.0.6",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "webpack",
14 | "build:tsc": "yarn type-check && tsc",
15 | "prepare": "yarn type-check && tsc",
16 | "build-old": "yarn type-check && esbuild src/index.tsx --loader:.tsx=ts --bundle --minify --platform=node --outfile=dist/index.js"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.24.3",
20 | "@babel/preset-env": "^7.24.3",
21 | "@babel/preset-react": "^7.24.1",
22 | "@babel/preset-typescript": "^7.24.1",
23 | "@turbo/gen": "^1.12.4",
24 | "@types/eslint": "^8.56.5",
25 | "@types/node": "^20.12.3",
26 | "@types/react": "^18.2.74",
27 | "@types/react-dom": "^18.2.23",
28 | "babel-loader": "^9.1.3",
29 | "esbuild": "^0.20.2",
30 | "eslint": "^8.57.0",
31 | "react": "^18.2.0",
32 | "ts-loader": "^9.5.1",
33 | "typescript": "^5.4.3",
34 | "webpack": "^5.91.0",
35 | "webpack-cli": "^5.1.4"
36 | },
37 | "peerDependencies": {
38 | "@react-navigation/native": "^6.1.16",
39 | "next": "*",
40 | "react": "^18.2.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/link-next/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/link-next/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/link-next/src/index.tsx:
--------------------------------------------------------------------------------
1 | import NextLink from "next/link";
2 | import React from "react";
3 | import type { LinkProps } from "@unitools/link";
4 | import { forwardRef } from "react";
5 |
6 | export const Link = forwardRef(
7 | ({ asChild, children, ...props }: LinkProps, ref?: any) => {
8 | if (asChild) {
9 | return (
10 |
18 | {children}
19 |
20 | );
21 | }
22 |
23 | return {children};
24 | }
25 | );
26 |
27 | Link.displayName = "NextLink";
28 | export default Link;
29 |
--------------------------------------------------------------------------------
/packages/link-next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/link-next/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/link-next/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/link-next/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/link-next/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/link/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/link/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/link [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/link` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/link
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add @unitools/link
17 | ```
18 |
19 | Add module resolver to your `next.config.js` file.
20 |
21 | ```js
22 | // next.config.js
23 |
24 | module.exports = {
25 | webpack(config) {
26 | config.resolve.alias["@unitools/link"] = "next/link";
27 | return config;
28 | },
29 | };
30 | ```
31 |
32 | ## For Expo
33 |
34 | ### Installation
35 |
36 | Install `@unitools/link` and the peer dependency `@react-navigation/native`.
37 |
38 | ```bash
39 | npm install @unitools/link @react-navigation/native
40 | ```
41 |
42 | or
43 |
44 | ```bash
45 | yarn add @unitools/link @react-navigation/native
46 | ```
47 |
48 | ## Usage
49 |
50 | ```jsx
51 | import Link from "@unitools/link";
52 |
53 | export default function Home() {
54 | return (
55 |
56 |
57 | About
58 |
59 |
60 | );
61 | }
62 | ```
63 |
64 | ## Props
65 |
66 | | Prop | Type | Default | Description | status | Support Status |
67 | | -------- | ---------------- | ------- | -------------------------------------------------- | -------- | -------------- |
68 | | href | String or Object | | href url | required | ✅ |
69 | | replace | Boolean | | Replace the current history state | - | ✅ |
70 | | scroll | Boolean | | - | - | ❌ |
71 | | prefetch | Boolean | | If true, this will prefetch the url | - | ❌ |
72 | | passHref | Boolean | | Forces Link to send the href property to its child | - | ❌ |
73 | | shallow | Boolean | | - | - | ❌ |
74 | | locale | Boolean | | If true, the active locale is automatically | - | ❌ |
75 |
--------------------------------------------------------------------------------
/packages/link/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/link/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/link",
3 | "version": "0.0.4",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "yarn type-check && tsc",
14 | "prepare": "yarn type-check && tsc"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-env": "^7.24.3",
19 | "@babel/preset-react": "^7.24.1",
20 | "@babel/preset-typescript": "^7.24.1",
21 | "@turbo/gen": "^1.12.4",
22 | "@types/eslint": "^8.56.5",
23 | "@types/node": "^20.12.3",
24 | "@types/react": "^18.2.74",
25 | "@types/react-dom": "^18.2.23",
26 | "babel-loader": "^9.1.3",
27 | "esbuild": "^0.20.2",
28 | "eslint": "^8.57.0",
29 | "react": "^18.2.0",
30 | "ts-loader": "^9.5.1",
31 | "typescript": "^5.4.3",
32 | "webpack": "^5.91.0",
33 | "webpack-cli": "^5.1.4"
34 | },
35 | "peerDependencies": {
36 | "react": "^18.2.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/link/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/link/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/link/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Link as RNLink } from "@react-navigation/native";
2 | import React from "react";
3 |
4 | export interface LinkProps {
5 | href: string;
6 | children: React.ReactNode;
7 | push?: boolean;
8 | replace?: boolean;
9 | asChild?: boolean;
10 | target?: string;
11 | }
12 |
13 | export default function Link(_: LinkProps) {
14 | return null;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/link/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/link/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/link/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/link/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/link/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/metro-config/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/metro-config/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/metro-config [ALPHA]
2 |
3 | ### This is the official documentation for the @unitools/metro-config package.
4 |
5 | ## Features
6 |
7 | - Support for .expo-web.{js|ts|jsx|tsx} file extensions.
8 |
9 | ### Installation
10 |
11 | To install `@unitools/metro-config`, use either of the following commands:
12 |
13 | ```bash
14 | npm install @unitools/metro-config
15 | ```
16 |
17 | or
18 |
19 | ```bash
20 | yarn add @unitools/metro-config
21 | ```
22 |
23 | ## Usage
24 |
25 | Add following to your `metro.config.js` file.
26 |
27 | ```jsx
28 | const { getDefaultConfig } = require("expo/metro-config");
29 | const { withUnitools } = require("@unitools/metro-config");
30 |
31 | const projectRoot = __dirname;
32 |
33 | const config = getDefaultConfig(projectRoot, {});
34 |
35 | module.exports = withUnitools(config);
36 | ```
37 |
--------------------------------------------------------------------------------
/packages/metro-config/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/metro-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/metro-config",
3 | "version": "0.0.1",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "yarn type-check && tsc",
14 | "prepare": "yarn type-check && tsc"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-typescript": "^7.24.1",
19 | "@types/eslint": "^8.56.5",
20 | "babel-loader": "^9.1.3",
21 | "eslint": "^8.57.0",
22 | "typescript": "^5.4.3"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/metro-config/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/metro-config/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/metro-config/src/index.ts:
--------------------------------------------------------------------------------
1 | import { resolver } from "./resolver";
2 | import type { MetroConfig } from "metro-config";
3 |
4 | export const withUnitools = (config: MetroConfig): MetroConfig => {
5 | return {
6 | ...config,
7 | resolver: {
8 | ...config?.resolver,
9 | resolveRequest: resolver,
10 | },
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/packages/metro-config/src/resolver.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import fs from "fs";
3 | import { resolve } from "metro-resolver";
4 |
5 | export const resolver = (context: any, moduleName: any, platform: any) => {
6 | try {
7 | if (platform === "web") {
8 | const resolution = resolve(
9 | {
10 | ...context,
11 | originModulePath: context.originModulePath,
12 | },
13 | moduleName,
14 | platform
15 | );
16 |
17 | if (resolution?.type === "sourceFile") {
18 | const filepath = resolution?.filePath;
19 |
20 | if (filepath) {
21 | const directory = path.dirname(filepath);
22 | const filename = path.basename(filepath, path.extname(filepath));
23 | const fileExt = `.expo-web${path.extname(filepath)}`;
24 | const platformResolveFilePath = path.join(
25 | directory,
26 | `${filename}${fileExt}`
27 | );
28 |
29 | if (fs.existsSync(platformResolveFilePath)) {
30 | return context.resolveRequest(
31 | context,
32 | platformResolveFilePath,
33 | platform
34 | );
35 | }
36 | }
37 | }
38 |
39 | return context.resolveRequest(context, moduleName, platform);
40 | }
41 |
42 | return context.resolveRequest(context, moduleName, platform);
43 | } catch (err) {
44 | return context.resolveRequest(context, moduleName, platform);
45 | }
46 | };
47 |
--------------------------------------------------------------------------------
/packages/metro-config/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/metro-config/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/navigation/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/navigation/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/navigation [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/navigation` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/navigation
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add @unitools/navigation
17 | ```
18 |
19 | Add module resolver to your `next.config.js` file.
20 |
21 | ```js
22 | // next.config.js
23 |
24 | module.exports = {
25 | webpack(config) {
26 | config.resolve.alias["@unitools/navigation"] = "next/navigation";
27 | return config;
28 | },
29 | };
30 | ```
31 |
32 | ## For Expo
33 |
34 | ### Installation
35 |
36 | Install `@unitools/navigation` and the peer dependency `@react-navigation/native`.
37 |
38 | ```bash
39 | npm install @unitools/navigation @react-navigation/native
40 | ```
41 |
42 | or
43 |
44 | ```bash
45 | yarn add @unitools/navigation @react-navigation/native
46 | ```
47 |
48 | ## Usage
49 |
50 | ```jsx
51 | import { usePathname, useSearchParams, useRouter } from "@unitools/navigation";
52 |
53 | export default function Home() {
54 | // usePathname
55 | const pathname = usePathname();
56 |
57 | // useSearchParams
58 | const searchParams = useSearchParams();
59 |
60 | const search = searchParams.get("search");
61 |
62 | // useRouter
63 | const router = useRouter();
64 |
65 | return (
66 |
67 | About
68 |
69 | );
70 | }
71 | ```
72 |
--------------------------------------------------------------------------------
/packages/navigation/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/navigation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/navigation",
3 | "version": "0.0.1",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "webpack",
14 | "build-old": "yarn type-check && esbuild src/index.tsx --loader:.tsx=ts --bundle --minify --platform=node --outfile=dist/index.js"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-env": "^7.24.3",
19 | "@babel/preset-react": "^7.24.1",
20 | "@babel/preset-typescript": "^7.24.1",
21 | "@turbo/gen": "^1.12.4",
22 | "@types/eslint": "^8.56.5",
23 | "@types/node": "^20.12.3",
24 | "@types/react": "^18.2.74",
25 | "@types/react-dom": "^18.2.23",
26 | "babel-loader": "^9.1.3",
27 | "esbuild": "^0.20.2",
28 | "eslint": "^8.57.0",
29 | "react": "^18.2.0",
30 | "ts-loader": "^9.5.1",
31 | "typescript": "^5.4.3",
32 | "webpack": "^5.91.0",
33 | "webpack-cli": "^5.1.4"
34 | },
35 | "peerDependencies": {
36 | "@react-navigation/native": "^6.1.16",
37 | "react": "^18.2.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/navigation/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/navigation/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/navigation/src/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | NavigationRouteContext,
3 | useNavigation,
4 | useLinkTo,
5 | StackActions,
6 | } from "@react-navigation/native";
7 | import { useContext, useMemo } from "react";
8 |
9 | // useRouter
10 | function usePathname() {
11 | let route = useContext(NavigationRouteContext);
12 | return route?.path;
13 | }
14 |
15 | function useSearchParams() {
16 | let route = useContext(NavigationRouteContext);
17 | let params = route?.params;
18 | // @ts-ignore
19 | let paramsURLString = new URLSearchParams(params).toString();
20 | return useMemo(
21 | () => params && new URLSearchParams(paramsURLString),
22 | [paramsURLString]
23 | );
24 | }
25 |
26 | function useRouter() {
27 | let navigation = useNavigation();
28 | let navRoute = useContext(NavigationRouteContext);
29 | const to = useLinkTo();
30 | // events, prefetch, beforePopState
31 | let route = {
32 | push: (path: string) => {
33 | to(path);
34 | },
35 | back: navigation.goBack,
36 | // @ts-ignore
37 | reload: () => navigation.navigate(navRoute.name),
38 | // events: navigation.addListener,
39 | prefetch: () => null,
40 | replace: (url: string, as?: string, _options?: any) => {
41 | let href = as || url;
42 | if (href.startsWith("/")) {
43 | href = href.slice(1);
44 | }
45 | // get params from url
46 | let params = {};
47 | let urlParts = href.split("?");
48 | if (urlParts.length > 1) {
49 | const searchParams = new URLSearchParams(urlParts[1]);
50 | searchParams.forEach((value, key) => {
51 | // @ts-ignore
52 | params[key] = value;
53 | });
54 | }
55 | navigation.dispatch(StackActions.replace(href, params));
56 | },
57 | beforePopState: navigation.addListener,
58 | };
59 | return route;
60 | }
61 |
62 | export { usePathname, useSearchParams, useRouter };
63 |
--------------------------------------------------------------------------------
/packages/navigation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/navigation/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/navigation/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/navigation/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/navigation/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/next-adapter/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/next-adapter/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/next-adapter [ALPHA]
2 |
3 | ### This is the official documentation for the @unitools/next-adapter package.
4 |
5 | ## Features
6 |
7 | - Support for .unitools.{js|ts|jsx|tsx} file extensions.
8 | - Automatic aliasing of @unitools/_ packages to @unitools/_-next and react-native to react-native-web.
9 | - Transpilation support for react-native, @unitools/_-next, @expo, and expo-_ packages.
10 |
11 | ### Installation
12 |
13 | To install the package, use either of the following commands:
14 |
15 | ```bash
16 | npm install @unitools/next-adapter
17 | ```
18 |
19 | or
20 |
21 | ```bash
22 | yarn add @unitools/next-adapter
23 | ```
24 |
25 | Then, add the following configuration to your next.config.js file:
26 |
27 | ```js
28 | /** @type {import('next').NextConfig} */
29 | const { withUnitools } = require("@unitools/next-adapter");
30 | const config = withUnitools({});
31 |
32 | module.exports = config;
33 | ```
34 |
--------------------------------------------------------------------------------
/packages/next-adapter/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/next-adapter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/next-adapter",
3 | "version": "0.0.1",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "yarn type-check && tsc",
14 | "prepare": "yarn type-check && tsc"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-typescript": "^7.24.1",
19 | "@types/eslint": "^8.56.5",
20 | "@types/fs-extra": "^11.0.4",
21 | "babel-loader": "^9.1.3",
22 | "eslint": "^8.57.0",
23 | "next": "*",
24 | "typescript": "^5.4.3"
25 | },
26 | "dependencies": {
27 | "fs-extra": "^11.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/next-adapter/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/next-adapter/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/next-adapter/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 | import findWorkspaceRoot from "find-yarn-workspace-root";
3 | import { DefinePlugin } from "webpack";
4 | import {
5 | checkIfWorkspace,
6 | getDependenciesFromNodeModules,
7 | getExactDependenciesFromNodeModules,
8 | } from "./utils";
9 |
10 | const unitoolsDeps = ["@unitools", "@expo", "expo-"];
11 |
12 | const reactNativeDeps = [
13 | "react-native",
14 | "react-native-web",
15 | "react-native-svg",
16 | ];
17 |
18 | export const withUnitools = (nextConfig: NextConfig) => {
19 | const currDir = process.cwd();
20 | let rootDependencyList = [];
21 | try {
22 | rootDependencyList = getDependenciesFromNodeModules(currDir, unitoolsDeps);
23 | } catch (e) {}
24 |
25 | let rootExactDependencyList = [];
26 | try {
27 | rootExactDependencyList = getExactDependenciesFromNodeModules(
28 | currDir,
29 | reactNativeDeps
30 | );
31 | } catch (e) {}
32 |
33 | const workspaceRoot = findWorkspaceRoot(currDir); // Absolute path or null
34 | const metaWorkspace = checkIfWorkspace(currDir);
35 | let parentDependencyList = [];
36 | let parentExactDependencyList = [];
37 |
38 | if (metaWorkspace.isWorkspace) {
39 | try {
40 | parentDependencyList = getDependenciesFromNodeModules(
41 | metaWorkspace.workspacePath,
42 | unitoolsDeps
43 | );
44 | parentExactDependencyList = getExactDependenciesFromNodeModules(
45 | metaWorkspace.workspacePath,
46 | reactNativeDeps
47 | );
48 | } catch (e) {}
49 | }
50 |
51 | if (workspaceRoot) {
52 | try {
53 | parentDependencyList = getDependenciesFromNodeModules(
54 | workspaceRoot,
55 | unitoolsDeps
56 | );
57 | parentExactDependencyList = getExactDependenciesFromNodeModules(
58 | workspaceRoot,
59 | reactNativeDeps
60 | );
61 | } catch (e) {}
62 | }
63 | let unitoolsUITranspileModules = Array.from(
64 | new Set([
65 | ...rootDependencyList,
66 | ...parentDependencyList,
67 | ...rootExactDependencyList,
68 | ...parentExactDependencyList,
69 | ...(nextConfig.transpilePackages || []),
70 | ])
71 | );
72 |
73 | const updatedNextConfig = {
74 | ...nextConfig,
75 | transpilePackages: unitoolsUITranspileModules,
76 | webpack: (config: any, ctx: any) => {
77 | config = nextConfig.webpack ? nextConfig.webpack(config, ctx) : config;
78 |
79 | config.resolve.alias = {
80 | ...(config.resolve.alias || {}),
81 | "react-native$": "react-native-web",
82 | "@unitools/link": "@unitools/link-next",
83 | "@unitools/router": "@unitools/router-next",
84 | "@unitools/image": "@unitools/image-next",
85 | };
86 |
87 | config.resolve.extensions = [
88 | ".next-web.js",
89 | ".next-web.jsx",
90 | ".next-web.ts",
91 | ".next-web.tsx",
92 | ".web.js",
93 | ".web.jsx",
94 | ".web.ts",
95 | ".web.tsx",
96 | ...config.resolve.extensions,
97 | ];
98 |
99 | config.module.rules.push({
100 | test: /\.ttf$/,
101 | loader: "url-loader",
102 | });
103 |
104 | config.plugins.push(
105 | new DefinePlugin({
106 | __DEV__: JSON.stringify(process.env.NODE_ENV !== "production"),
107 | })
108 | );
109 |
110 | return config;
111 | },
112 | };
113 |
114 | return updatedNextConfig;
115 | };
116 |
--------------------------------------------------------------------------------
/packages/next-adapter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/next-adapter/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/router-expo/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/router-expo/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/link [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/link` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/link
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add @unitools/link
17 | ```
18 |
19 | Add module resolver to your `next.config.js` file.
20 |
21 | ```js
22 | // next.config.js
23 |
24 | module.exports = {
25 | webpack(config) {
26 | config.resolve.alias["@unitools/link"] = "next/link";
27 | return config;
28 | },
29 | };
30 | ```
31 |
32 | ## For Expo
33 |
34 | ### Installation
35 |
36 | Install `@unitools/link` and the peer dependency `@react-navigation/native`.
37 |
38 | ```bash
39 | npm install @unitools/link @react-navigation/native
40 | ```
41 |
42 | or
43 |
44 | ```bash
45 | yarn add @unitools/link @react-navigation/native
46 | ```
47 |
48 | ## Usage
49 |
50 | ```jsx
51 | import Link from "@unitools/link";
52 |
53 | export default function Home() {
54 | return (
55 |
56 |
57 | About
58 |
59 |
60 | );
61 | }
62 | ```
63 |
64 | ## Props
65 |
66 | | Prop | Type | Default | Description | status | Support Status |
67 | | -------- | ---------------- | ------- | -------------------------------------------------- | -------- | -------------- |
68 | | href | String or Object | | href url | required | ✅ |
69 | | replace | Boolean | | Replace the current history state | - | ✅ |
70 | | scroll | Boolean | | - | - | ❌ |
71 | | prefetch | Boolean | | If true, this will prefetch the url | - | ❌ |
72 | | passHref | Boolean | | Forces Link to send the href property to its child | - | ❌ |
73 | | shallow | Boolean | | - | - | ❌ |
74 | | locale | Boolean | | If true, the active locale is automatically | - | ❌ |
75 |
--------------------------------------------------------------------------------
/packages/router-expo/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/router-expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/router-expo",
3 | "version": "0.0.5",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "webpack",
14 | "build:tsc": "yarn type-check && tsc",
15 | "prepare": "yarn type-check && tsc",
16 | "build-old": "yarn type-check && esbuild src/index.tsx --loader:.tsx=ts --bundle --minify --platform=node --outfile=dist/index.js"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.24.3",
20 | "@babel/preset-env": "^7.24.3",
21 | "@babel/preset-react": "^7.24.1",
22 | "@babel/preset-typescript": "^7.24.1",
23 | "@turbo/gen": "^1.12.4",
24 | "@types/eslint": "^8.56.5",
25 | "@types/node": "^20.12.3",
26 | "@types/react": "^18.2.74",
27 | "@types/react-dom": "^18.2.23",
28 | "babel-loader": "^9.1.3",
29 | "esbuild": "^0.20.2",
30 | "eslint": "^8.57.0",
31 | "expo-router": "^3.4.8",
32 | "react": "^18.2.0",
33 | "ts-loader": "^9.5.1",
34 | "typescript": "^5.4.3",
35 | "webpack": "^5.91.0",
36 | "webpack-cli": "^5.1.4"
37 | },
38 | "peerDependencies": {
39 | "@react-navigation/native": "^6.1.16",
40 | "@unitools/link": "*",
41 | "react": "^18.2.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/router-expo/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/router-expo/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/router-expo/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { useRouter as useExpoRouter } from "expo-router";
2 | import { usePathname as useExpoPathname } from "expo-router";
3 | import type { RouterProps } from "@unitools/router";
4 |
5 | function useRouter(): RouterProps {
6 | const router = useExpoRouter();
7 |
8 | const routerProps = {
9 | push: router.push,
10 | replace: router.replace,
11 | back: router.back,
12 | navigate: router.navigate,
13 | };
14 |
15 | return routerProps;
16 | }
17 |
18 | function usePathname(): string {
19 | return useExpoPathname();
20 | }
21 |
22 | export { useRouter, usePathname };
23 |
--------------------------------------------------------------------------------
/packages/router-expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/router-expo/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/router-expo/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/router-expo/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/router-expo/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/router-next/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/router-next/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/link [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/link` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/link
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add @unitools/link
17 | ```
18 |
19 | Add module resolver to your `next.config.js` file.
20 |
21 | ```js
22 | // next.config.js
23 |
24 | module.exports = {
25 | webpack(config) {
26 | config.resolve.alias["@unitools/link"] = "next/link";
27 | return config;
28 | },
29 | };
30 | ```
31 |
32 | ## For Expo
33 |
34 | ### Installation
35 |
36 | Install `@unitools/link` and the peer dependency `@react-navigation/native`.
37 |
38 | ```bash
39 | npm install @unitools/link @react-navigation/native
40 | ```
41 |
42 | or
43 |
44 | ```bash
45 | yarn add @unitools/link @react-navigation/native
46 | ```
47 |
48 | ## Usage
49 |
50 | ```jsx
51 | import Link from "@unitools/link";
52 |
53 | export default function Home() {
54 | return (
55 |
56 |
57 | About
58 |
59 |
60 | );
61 | }
62 | ```
63 |
64 | ## Props
65 |
66 | | Prop | Type | Default | Description | status | Support Status |
67 | | -------- | ---------------- | ------- | -------------------------------------------------- | -------- | -------------- |
68 | | href | String or Object | | href url | required | ✅ |
69 | | replace | Boolean | | Replace the current history state | - | ✅ |
70 | | scroll | Boolean | | - | - | ❌ |
71 | | prefetch | Boolean | | If true, this will prefetch the url | - | ❌ |
72 | | passHref | Boolean | | Forces Link to send the href property to its child | - | ❌ |
73 | | shallow | Boolean | | - | - | ❌ |
74 | | locale | Boolean | | If true, the active locale is automatically | - | ❌ |
75 |
--------------------------------------------------------------------------------
/packages/router-next/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/router-next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/router-next",
3 | "version": "0.0.5",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "yarn type-check && tsc",
14 | "prepare": "yarn type-check && tsc"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.24.3",
18 | "@babel/preset-env": "^7.24.3",
19 | "@babel/preset-react": "^7.24.1",
20 | "@babel/preset-typescript": "^7.24.1",
21 | "@turbo/gen": "^1.12.4",
22 | "@types/eslint": "^8.56.5",
23 | "@types/node": "^20.12.3",
24 | "@types/react": "^18.2.74",
25 | "@types/react-dom": "^18.2.23",
26 | "babel-loader": "^9.1.3",
27 | "esbuild": "^0.20.2",
28 | "eslint": "^8.57.0",
29 | "expo-router": "^3.4.8",
30 | "react": "^18.2.0",
31 | "ts-loader": "^9.5.1",
32 | "typescript": "^5.4.3",
33 | "webpack": "^5.91.0",
34 | "webpack-cli": "^5.1.4"
35 | },
36 | "peerDependencies": {
37 | "@unitools/router": "*",
38 | "react": "^18.2.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/router-next/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/router-next/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/router-next/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import type { RouterProps } from "@unitools/router";
3 | import { useRouter as useNextRouter } from "next/navigation";
4 | import { usePathname as useNextPathname } from "next/navigation";
5 |
6 | function useRouter(): RouterProps {
7 | const router = useNextRouter();
8 |
9 | const routerProps = {
10 | push: router.push,
11 | replace: router.replace,
12 | back: router.back,
13 | navigate: router.push,
14 | };
15 |
16 | return routerProps;
17 | }
18 |
19 | function usePathname(): string {
20 | return useNextPathname();
21 | }
22 |
23 | export { useRouter, usePathname };
24 |
--------------------------------------------------------------------------------
/packages/router-next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/router-next/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/router-next/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/router-next/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/router-next/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/router/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | root: true,
4 | extends: ["./react-internal.js"],
5 | parser: "@typescript-eslint/parser",
6 | parserOptions: {
7 | project: "./tsconfig.lint.json",
8 | tsconfigRootDir: __dirname,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/router/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/link [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/link` package.
4 |
5 | ## For Next.js
6 |
7 | ### Installation
8 |
9 | ```bash
10 | npm install @unitools/link
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add @unitools/link
17 | ```
18 |
19 | Add module resolver to your `next.config.js` file.
20 |
21 | ```js
22 | // next.config.js
23 |
24 | module.exports = {
25 | webpack(config) {
26 | config.resolve.alias["@unitools/link"] = "next/link";
27 | return config;
28 | },
29 | };
30 | ```
31 |
32 | ## For Expo
33 |
34 | ### Installation
35 |
36 | Install `@unitools/link` and the peer dependency `@react-navigation/native`.
37 |
38 | ```bash
39 | npm install @unitools/link @react-navigation/native
40 | ```
41 |
42 | or
43 |
44 | ```bash
45 | yarn add @unitools/link @react-navigation/native
46 | ```
47 |
48 | ## Usage
49 |
50 | ```jsx
51 | import Link from "@unitools/link";
52 |
53 | export default function Home() {
54 | return (
55 |
56 |
57 | About
58 |
59 |
60 | );
61 | }
62 | ```
63 |
64 | ## Props
65 |
66 | | Prop | Type | Default | Description | status | Support Status |
67 | | -------- | ---------------- | ------- | -------------------------------------------------- | -------- | -------------- |
68 | | href | String or Object | | href url | required | ✅ |
69 | | replace | Boolean | | Replace the current history state | - | ✅ |
70 | | scroll | Boolean | | - | - | ❌ |
71 | | prefetch | Boolean | | If true, this will prefetch the url | - | ❌ |
72 | | passHref | Boolean | | Forces Link to send the href property to its child | - | ❌ |
73 | | shallow | Boolean | | - | - | ❌ |
74 | | locale | Boolean | | If true, the active locale is automatically | - | ❌ |
75 |
--------------------------------------------------------------------------------
/packages/router/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "declaration": true,
6 | "declarationMap": true,
7 | "esModuleInterop": true,
8 | "incremental": false,
9 | "isolatedModules": true,
10 | "lib": ["es2022", "DOM", "DOM.Iterable"],
11 | "module": "NodeNext",
12 | "moduleDetection": "force",
13 | "moduleResolution": "NodeNext",
14 | "noUncheckedIndexedAccess": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "target": "ES2022"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/router",
3 | "version": "0.0.6",
4 | "private": false,
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js"
9 | },
10 | "scripts": {
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "webpack",
14 | "build:tsc": "yarn type-check && tsc",
15 | "prepare": "yarn type-check && tsc"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.24.3",
19 | "@babel/preset-env": "^7.24.3",
20 | "@babel/preset-react": "^7.24.1",
21 | "@babel/preset-typescript": "^7.24.1",
22 | "@turbo/gen": "^1.12.4",
23 | "@types/eslint": "^8.56.5",
24 | "@types/node": "^20.12.3",
25 | "@types/react": "^18.2.74",
26 | "@types/react-dom": "^18.2.23",
27 | "babel-loader": "^9.1.3",
28 | "esbuild": "^0.20.2",
29 | "eslint": "^8.57.0",
30 | "react": "^18.2.0",
31 | "ts-loader": "^9.5.1",
32 | "typescript": "^5.4.3",
33 | "webpack": "^5.91.0",
34 | "webpack-cli": "^5.1.4"
35 | },
36 | "peerDependencies": {
37 | "react": "^18.2.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/router/react-internal.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require("node:path");
2 |
3 | const project = resolve(process.cwd(), "tsconfig.json");
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | *
10 | * This config extends the Vercel Engineering Style Guide.
11 | * For more information, see https://github.com/vercel/style-guide
12 | *
13 | */
14 |
15 | /** @type {import("eslint").Linter.Config} */
16 | module.exports = {
17 | extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
18 | plugins: ["only-warn"],
19 | globals: {
20 | React: true,
21 | JSX: true,
22 | },
23 | env: {
24 | browser: true,
25 | },
26 | settings: {
27 | "import/resolver": {
28 | typescript: {
29 | project,
30 | },
31 | },
32 | },
33 | ignorePatterns: [
34 | // Ignore dotfiles
35 | ".*.js",
36 | "node_modules/",
37 | "dist/",
38 | ],
39 | overrides: [
40 | // Force ESLint to detect .tsx files
41 | { files: ["*.js?(x)", "*.ts?(x)"] },
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/packages/router/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/router/src/index.tsx:
--------------------------------------------------------------------------------
1 | export interface RouterProps {
2 | push: (path: string) => void;
3 | replace: (path: string) => void;
4 | back: () => void;
5 | navigate: (path: string) => void;
6 | }
7 |
8 | export function useRouter(): RouterProps {
9 | const routerProps = {
10 | push: (path: string) => {},
11 | replace: (path: string) => {},
12 | back: () => {},
13 | navigate: (path: string) => {},
14 | };
15 |
16 | return routerProps;
17 | }
18 |
19 | export function usePathname(): string {
20 | return "";
21 | }
22 |
--------------------------------------------------------------------------------
/packages/router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "NodeNext",
6 | "jsx": "react",
7 | "outDir": "./dist",
8 | },
9 | "include": [
10 | "src"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | "dist"
15 | ]
16 | }
--------------------------------------------------------------------------------
/packages/router/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./react-library.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "turbo"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist"
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/router/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/router/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/packages/router/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/index.tsx",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "index.js",
9 | libraryTarget: "commonjs",
10 | },
11 | resolve: {
12 | extensions: [".ts", ".tsx", ".js", ".json"],
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(ts|tsx)$/,
18 | exclude: /(node_modules|bower_components)/,
19 | use: {
20 | loader: "babel-loader",
21 | options: {
22 | presets: [
23 | "@babel/preset-env",
24 | "@babel/preset-react",
25 | "@babel/preset-typescript",
26 | ],
27 | },
28 | },
29 | },
30 | {
31 | test: /\.(ts|tsx)$/,
32 | exclude: /(node_modules|bower_components)/,
33 | use: {
34 | loader: "ts-loader",
35 | },
36 | },
37 | ],
38 | },
39 | externals: {
40 | react: "commonjs react",
41 | "react-native": "commonjs react-native",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/utils/.npmignore:
--------------------------------------------------------------------------------
1 | !dist
--------------------------------------------------------------------------------
/packages/utils/.npmrc:
--------------------------------------------------------------------------------
1 |
2 | dist
3 | src
4 | package.json
5 | README.md
--------------------------------------------------------------------------------
/packages/utils/README.md:
--------------------------------------------------------------------------------
1 | # @unitools/utils [ALPHA]
2 |
3 | This is the official documentation of the `@unitools/utils` package.
4 |
5 | ## Installation
6 |
7 | ```bash
8 | npm install @unitools/utils
9 | ```
10 |
11 | or
12 |
13 | ```bash
14 | yarn add -D @unitools/utils
15 | ```
16 |
17 | ## Usage
18 |
19 | ```jsx
20 | import { Platform } from "@unitools/utils";
21 |
22 | export default function Home() {
23 | return (
24 |
32 | );
33 | }
34 | ```
35 |
--------------------------------------------------------------------------------
/packages/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@unitools/utils",
3 | "version": "0.0.1-alpha.11",
4 | "private": false,
5 | "module": "dist/index.js",
6 | "main": "dist/index.js",
7 | "typings": "dist/index.d.ts",
8 | "exports": {
9 | ".": "./dist/index.js"
10 | },
11 | "scripts": {
12 | "lint": "eslint . --max-warnings 0",
13 | "build": "esbuild ./src/*.js --bundle --platform=node --packages=external --outdir=dist && esbuild ./src/*.ts --bundle --sourcemap --tsconfig=tsconfig.json --platform=node --loader:.ts=ts --packages=external --outdir=dist && tsc --emitDeclarationOnly --outDir dist"
14 | },
15 | "devDependencies": {
16 | "@turbo/gen": "^1.12.4",
17 | "@types/eslint": "^8.56.5",
18 | "@types/node": "^20.11.24",
19 | "@types/react": "^18.2.61",
20 | "@types/react-dom": "^18.2.19",
21 | "eslint": "^8.57.0",
22 | "react": "^18.2.0",
23 | "typescript": "^5.3.3",
24 | "esbuild": "^0.20.2"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/utils/src/index.ts:
--------------------------------------------------------------------------------
1 | var Platform: "unknown" | "next" | "expo" = "unknown";
2 |
3 | try {
4 | const platform = require("./platform.js");
5 | Platform = platform.value;
6 | } catch (e) {
7 | console.error(e);
8 | }
9 |
10 | export { Platform };
11 |
--------------------------------------------------------------------------------
/packages/utils/src/postinstall.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 | const os = require("os");
4 | // Read the package.json file
5 | const packageJson = JSON.parse(
6 | fs.readFileSync(
7 | path.resolve(process.cwd(), "..", "..", "..", "package.json"),
8 | "utf8"
9 | )
10 | );
11 |
12 | // Get the value of the start script
13 | const startScript = packageJson.scripts.start;
14 |
15 | // Determine the value to set in the index.js file
16 | let valueToSet;
17 | if (startScript.includes("next")) {
18 | valueToSet = "next";
19 | } else if (startScript.includes("expo")) {
20 | valueToSet = "expo";
21 | } else {
22 | // Handle other cases if needed
23 | valueToSet = "unkown";
24 | }
25 |
26 | // Set the value in the node_modules/@unitools/utils/dist/index.js file
27 | fs.writeFileSync(
28 | path.resolve(process.cwd(), "dist", "platform.js"),
29 | `module.exports = {value:"${valueToSet}"};`
30 | );
31 |
--------------------------------------------------------------------------------
/packages/utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "module": "commonjs"
5 | },
6 | "exclude": [
7 | "node_modules"
8 | ],
9 | "include": [
10 | "src"
11 | ]
12 | }
--------------------------------------------------------------------------------
/packages/utils/turbo/generators/config.ts:
--------------------------------------------------------------------------------
1 | import type { PlopTypes } from "@turbo/gen";
2 |
3 | // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
4 |
5 | export default function generator(plop: PlopTypes.NodePlopAPI): void {
6 | // A simple generator to add a new React component to the internal UI library
7 | plop.setGenerator("react-component", {
8 | description: "Adds a new react component",
9 | prompts: [
10 | {
11 | type: "input",
12 | name: "name",
13 | message: "What is the name of the component?",
14 | },
15 | ],
16 | actions: [
17 | {
18 | type: "add",
19 | path: "src/{{kebabCase name}}.tsx",
20 | templateFile: "templates/component.hbs",
21 | },
22 | {
23 | type: "append",
24 | path: "package.json",
25 | pattern: /"exports": {(?)/g,
26 | template: '"./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/utils/turbo/generators/templates/component.hbs:
--------------------------------------------------------------------------------
1 | export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
4 |
{{ pascalCase name }} Component
5 | {children}
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./base.json"
3 | }
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "globalDependencies": ["**/.env.*local"],
4 | "pipeline": {
5 | "build": {
6 | "dependsOn": ["^build"],
7 | "outputs": [".next/**", "!.next/cache/**"]
8 | },
9 | "lint": {
10 | "dependsOn": ["^lint"]
11 | },
12 | "dev": {
13 | "cache": false,
14 | "persistent": true
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------