init commit
Some checks are pending
Lint Node.js / build (windows-latest) (push) Waiting to run
Lint Node.js / build (macos-latest) (push) Waiting to run
Lint Node.js / build (ubuntu-latest) (push) Waiting to run
Lint Rust / build (macos-latest) (push) Waiting to run
Lint Rust / build (ubuntu-latest) (push) Waiting to run
Lint Rust / build (windows-latest) (push) Waiting to run
29
.eslintrc.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/strict-type-checked",
|
||||
"plugin:@typescript-eslint/stylistic-type-checked",
|
||||
"next/core-web-vitals"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": ["./tsconfig.json"]
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"eqeqeq": "error",
|
||||
"no-else-return": "error",
|
||||
"no-implicit-coercion": ["error", { "disallowTemplateShorthand": true }],
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-useless-concat": "error",
|
||||
"prefer-arrow-callback": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-rest-params": "error",
|
||||
"prefer-spread": "error",
|
||||
"prefer-template": "error",
|
||||
"radix": ["error", "always"],
|
||||
"react-hooks/exhaustive-deps": "error"
|
||||
}
|
||||
}
|
||||
28
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Enable version updates for Node.js dependencies
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
allow:
|
||||
- dependency-type: "all"
|
||||
groups:
|
||||
all:
|
||||
patterns:
|
||||
- "*"
|
||||
ignore:
|
||||
- dependency-name: "eslint"
|
||||
versions: ">= 9"
|
||||
|
||||
# Enable version updates for rust
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/src-tauri"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
allow:
|
||||
- dependency-type: "all"
|
||||
groups:
|
||||
all:
|
||||
patterns:
|
||||
- "*"
|
||||
34
.github/workflows/dependabot-automerge.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Automatically squashes and merges Dependabot dependency upgrades if tests pass
|
||||
|
||||
name: Dependabot Auto-merge
|
||||
|
||||
on: pull_request_target
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
steps:
|
||||
- name: Fetch Dependabot metadata
|
||||
id: dependabot-metadata
|
||||
uses: dependabot/fetch-metadata@v1.3.3
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Approve Dependabot PR
|
||||
run: gh pr review --approve "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Auto-merge (squash) Dependabot PR
|
||||
if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}
|
||||
run: gh pr merge --auto --squash "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
46
.github/workflows/lint-js.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# Installs Node.js dependencies and pnpm, and checks formatting + linting
|
||||
|
||||
name: Lint Node.js
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "src-tauri/**"
|
||||
- "README.md"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Disable git core.autocrlf on Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: git config --global core.autocrlf false
|
||||
|
||||
- name: Checkout repository code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up pnpm package manager
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Set up Node.js v22
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies from lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run lint step
|
||||
run: pnpm lint
|
||||
55
.github/workflows/lint-rs.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# Installs Rust and checks formatting + linting
|
||||
|
||||
name: Lint Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "src/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "yarn.lock"
|
||||
- "pnpm-lock.yaml"
|
||||
- "README.md"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Disable git core.autocrlf on Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: git config --global core.autocrlf false
|
||||
|
||||
- name: Checkout repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install Linux dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt install libdbus-1-dev libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
|
||||
|
||||
- name: Create empty 'out' directory
|
||||
run: mkdir out
|
||||
|
||||
- name: Run rustfmt check
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: src-tauri
|
||||
|
||||
- name: Run clippy check and deny warnings
|
||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
working-directory: src-tauri
|
||||
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
4
.husky/pre-commit
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
pnpm exec lint-staged
|
||||
13
.lintstagedrc.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const path = require("path")
|
||||
|
||||
const buildEslintCommand = (filenames) =>
|
||||
`next lint --fix --file ${filenames
|
||||
.map((f) => path.relative(process.cwd(), f))
|
||||
.join(" --file ")}`
|
||||
|
||||
module.exports = {
|
||||
"src/**/*.{js,jsx,ts,tsx}": [buildEslintCommand],
|
||||
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
|
||||
"biome check --apply --no-errors-on-unmatched", // Format, sort imports, lint, and apply safe fixes
|
||||
],
|
||||
}
|
||||
1
.node-version
Normal file
@@ -0,0 +1 @@
|
||||
22
|
||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Kevin Xiao
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
216
README.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# Tauri + Next.js Template
|
||||
|
||||

|
||||
|
||||
This is a [Tauri](https://tauri.app/) project template using [Next.js](https://nextjs.org/),
|
||||
bootstrapped by combining [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app)
|
||||
and [`create tauri-app`](https://tauri.app/v1/guides/getting-started/setup).
|
||||
|
||||
This template uses [`pnpm`](https://pnpm.io/) as the Node.js dependency
|
||||
manager.
|
||||
|
||||
## Template Features
|
||||
|
||||
- TypeScript frontend using Next.js React framework
|
||||
- [TailwindCSS](https://tailwindcss.com/) as a utility-first atomic CSS framework
|
||||
- The example page in this template app has been updated to use only TailwindCSS
|
||||
- While not included by default, consider using
|
||||
[React Aria components](https://react-spectrum.adobe.com/react-aria/index.html)
|
||||
and/or [HeadlessUI components](https://headlessui.com/) for completely unstyled and
|
||||
fully accessible UI components, which integrate nicely with TailwindCSS
|
||||
- Opinionated formatting and linting already setup and enabled
|
||||
- [ESLint](https://eslint.org/) for pure React + TypeScript linting, and
|
||||
[Biome](https://biomejs.dev/) for a combination of fast formatting, linting, and
|
||||
import sorting of JavaScript and TypeScript code
|
||||
- [clippy](https://github.com/rust-lang/rust-clippy) and
|
||||
[rustfmt](https://github.com/rust-lang/rustfmt) for Rust code
|
||||
- GitHub Actions to check code formatting and linting for both TypeScript and Rust
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Running development server and use Tauri window
|
||||
|
||||
After cloning for the first time, set up git pre-commit hooks:
|
||||
|
||||
```shell
|
||||
pnpm prepare
|
||||
```
|
||||
|
||||
To develop and run the frontend in a Tauri window:
|
||||
|
||||
```shell
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
This will load the Next.js frontend directly in a Tauri webview window, in addition to
|
||||
starting a development server on `localhost:3000`.
|
||||
|
||||
### Building for release
|
||||
|
||||
To export the Next.js frontend via SSG and build the Tauri application for release:
|
||||
|
||||
```shell
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Please remember to change the bundle identifier in
|
||||
`tauri.conf.json > tauri > bundle > identifier`, as the default value will yield an
|
||||
error that prevents you from building the application for release.
|
||||
|
||||
### Source structure
|
||||
|
||||
Next.js frontend source files are located in `src/` and Tauri Rust application source
|
||||
files are located in `src-tauri/`. Please consult the Next.js and Tauri documentation
|
||||
respectively for questions pertaining to either technology.
|
||||
|
||||
## Caveats
|
||||
|
||||
### Static Site Generation / Pre-rendering
|
||||
|
||||
Next.js is a great React frontend framework which supports server-side rendering (SSR)
|
||||
as well as static site generation (SSG or pre-rendering). For the purposes of creating a
|
||||
Tauri frontend, only SSG can be used since SSR requires an active Node.js server.
|
||||
|
||||
Using Next.js and SSG helps to provide a quick and performant single-page-application
|
||||
(SPA) frontend experience. More information regarding this can be found here:
|
||||
https://nextjs.org/docs/basic-features/pages#pre-rendering
|
||||
|
||||
### `next/image`
|
||||
|
||||
The [`next/image` component](https://nextjs.org/docs/basic-features/image-optimization)
|
||||
is an enhancement over the regular `<img>` HTML element with additional optimizations
|
||||
built in. However, because we are not deploying the frontend onto Vercel directly, some
|
||||
optimizations must be disabled to properly build and export the frontend via SSG.
|
||||
As such, the
|
||||
[`unoptimized` property](https://nextjs.org/docs/api-reference/next/image#unoptimized)
|
||||
is set to true for the `next/image` component in the `next.config.js` configuration.
|
||||
This will allow the image to be served as-is from source, without
|
||||
changes to its quality, size, or format.
|
||||
|
||||
### error[E0554]: `#![feature]` may not be used on the stable release channel
|
||||
|
||||
If you are getting this issue when trying to run `pnpm tauri dev`, it may be that you
|
||||
have a newer version of a Rust dependency that uses an unstable feature.
|
||||
`pnpm tauri build` should still work for production builds, but to get the dev command
|
||||
working, either downgrade the dependency or use Rust nightly via
|
||||
`rustup override set nightly`.
|
||||
|
||||
### ReferenceError: navigator is not defined
|
||||
|
||||
If you are using Tauri's `invoke` function or any OS related Tauri function from within
|
||||
JavaScript, you may encounter this error when importing the function in a global,
|
||||
non-browser context. This is due to the nature of Next.js' dev server effectively
|
||||
running a Node.js server for SSR and hot module replacement (HMR), and Node.js does not
|
||||
have a notion of `window` or `navigator`.
|
||||
|
||||
#### Solution 1 - Dependency Injection (may not always work)
|
||||
|
||||
Make sure that you are calling these functions within the browser context, e.g. within a
|
||||
React component inside a `useEffect` hook when the DOM actually exists by then. If you
|
||||
are trying to use a Tauri function in a generalized utility source file, a workaround is
|
||||
to use dependency injection for the function itself to delay the actual importing of the
|
||||
real function (see example below for more info).
|
||||
|
||||
Example using Tauri's `invoke` function:
|
||||
|
||||
`src/lib/some_tauri_functions.ts` (problematic)
|
||||
|
||||
```typescript
|
||||
// Generalized file containing all the invoke functions we need to fetch data from Rust
|
||||
import { invoke } from "@tauri-apps/api/tauri"
|
||||
|
||||
const loadFoo = (): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_foo")
|
||||
}
|
||||
|
||||
const loadBar = (): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_bar")
|
||||
}
|
||||
|
||||
const loadBaz = (): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_baz")
|
||||
}
|
||||
|
||||
// and so on ...
|
||||
```
|
||||
|
||||
`src/lib/some_tauri_functions.ts` (fixed)
|
||||
|
||||
```typescript
|
||||
// Generalized file containing all the invoke functions we need to fetch data from Rust
|
||||
//
|
||||
// We apply the idea of dependency injection to use a supplied invoke function as a
|
||||
// function argument, rather than directly referencing the Tauri invoke function.
|
||||
// Hence, don't import invoke globally in this file.
|
||||
//
|
||||
// import { invoke } from "@tauri-apps/api/tauri" <-- remove this!
|
||||
//
|
||||
|
||||
import { InvokeArgs } from "@tauri-apps/api/tauri"
|
||||
type InvokeFunction = <T>(cmd: string, args?: InvokeArgs | undefined) => Promise<T>
|
||||
|
||||
const loadFoo = (invoke: InvokeFunction): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_foo")
|
||||
}
|
||||
|
||||
const loadBar = (invoke: InvokeFunction): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_bar")
|
||||
}
|
||||
|
||||
const loadBaz = (invoke: InvokeFunction): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_baz")
|
||||
}
|
||||
|
||||
// and so on ...
|
||||
```
|
||||
|
||||
Then, when using `loadFoo`/`loadBar`/`loadBaz` within your React components, import the
|
||||
invoke function from `@tauri-apps/api` and pass `invoke` into the loadXXX function as
|
||||
the `InvokeFunction` argument. This should allow the actual Tauri API to be bundled
|
||||
only within the context of a React component, so it should not be loaded by Next.js upon
|
||||
initial startup until the browser has finished loading the page.
|
||||
|
||||
#### Solution 2: Wrap Tauri API behind dynamic `import()`
|
||||
|
||||
Since the Tauri API needs to read from the browser's `window` and `navigator` object,
|
||||
this data does not exist in a Node.js and hence SSR environment. One can create an
|
||||
exported function that wraps the Tauri API behind a dynamic runtime `import()` call.
|
||||
|
||||
Example: create a `src/lib/tauri.ts` to re-export `invoke`
|
||||
|
||||
```typescript
|
||||
import type { InvokeArgs } from "@tauri-apps/api/tauri"
|
||||
|
||||
const isNode = (): boolean =>
|
||||
Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) ===
|
||||
"[object process]"
|
||||
|
||||
export async function invoke<T>(
|
||||
cmd: string,
|
||||
args?: InvokeArgs | undefined,
|
||||
): Promise<T> {
|
||||
if (isNode()) {
|
||||
// This shouldn't ever happen when React fully loads
|
||||
return Promise.resolve(undefined as unknown as T)
|
||||
}
|
||||
const tauriAppsApi = await import("@tauri-apps/api")
|
||||
const tauriInvoke = tauriAppsApi.invoke
|
||||
return tauriInvoke(cmd, args)
|
||||
}
|
||||
```
|
||||
|
||||
Then, instead of importing `import { invoke } from "@tauri-apps/api/tauri"`, use invoke
|
||||
from `import { invoke } from "@/lib/tauri"`.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and
|
||||
API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
And to learn more about Tauri, take a look at the following resources:
|
||||
|
||||
- [Tauri Documentation - Guides](https://tauri.app/v1/guides/) - learn about the Tauri
|
||||
toolkit.
|
||||
24
biome.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.5.3/schema.json",
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"semicolons": "asNeeded",
|
||||
"lineEnding": "lf",
|
||||
"trailingComma": "all"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
11
next.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
output: "export",
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
59
package.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "tauri-nextjs-template",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"author": {
|
||||
"name": "Your Name Here",
|
||||
"email": "your-email-here@example.com"
|
||||
},
|
||||
"scripts": {
|
||||
"next-start": "cross-env BROWSER=none next dev",
|
||||
"next-build": "next build",
|
||||
"tauri": "tauri",
|
||||
"build": "tauri build",
|
||||
"dev": "tauri dev",
|
||||
"lint": "next lint && biome check src/",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.6.0",
|
||||
"clsx": "^2.1.1",
|
||||
"next": "^14.2.11",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.1",
|
||||
"@tauri-apps/cli": "^1.6.2",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.5.0",
|
||||
"@testing-library/react": "^16.0.1",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/node": "^22.5.5",
|
||||
"@types/react": "^18.3.6",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
||||
"@typescript-eslint/parser": "^8.5.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^7.0.6",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "^14.2.11",
|
||||
"husky": "^9.1.6",
|
||||
"lint-staged": "^15.2.10",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-nesting": "^13.0.0",
|
||||
"tailwindcss": "^3.4.11",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [">0.2%", "not dead", "not op_mini all"],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
5203
pnpm-lock.yaml
generated
Normal file
9
postcss.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
"postcss-import": {},
|
||||
"tailwindcss/nesting": "postcss-nesting",
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}),
|
||||
},
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/tauri-nextjs-template_screenshot.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
21
public/tauri_logo_light.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg width="430" height="134" fill="none" xmlns="http://www.w3.org/2000/svg"><path
|
||||
d="M177.007 104.258c-.629 0-1.195-.219-1.698-.658-.441-.5-.661-1.064-.661-1.69V53.276H157.85c-.629 0-1.195-.22-1.699-.658-.44-.5-.66-1.064-.66-1.69V40.882c0-.688.22-1.252.66-1.69.504-.438 1.07-.657 1.699-.657h50.772c.692 0 1.258.22 1.699.657.44.438.66 1.002.66 1.69v10.046c0 .689-.22 1.252-.66 1.69-.441.439-1.007.658-1.699.658h-16.798v48.634c0 .626-.22 1.19-.661 1.69-.44.439-1.006.658-1.698.658h-12.458Zm32.049 0c-.504 0-.976-.188-1.416-.564-.377-.438-.566-.907-.566-1.408 0-.376.031-.657.094-.845l22.178-60.277a3.406 3.406 0 0 1 1.226-1.878c.629-.5 1.447-.751 2.454-.751h13.967c1.007 0 1.825.25 2.454.751a3.413 3.413 0 0 1 1.227 1.878l22.083 60.277.188.845c0 .501-.22.97-.66 1.408-.378.376-.85.564-1.416.564h-11.608c-1.51 0-2.516-.658-3.02-1.972l-3.68-9.577h-25.197l-3.586 9.577c-.504 1.314-1.542 1.972-3.115 1.972h-11.607Zm39.73-25.069-8.776-24.787-8.777 24.787h17.553Zm57.868 26.008c-8.87 0-15.823-2.16-20.856-6.479-4.97-4.319-7.455-10.828-7.455-19.529V40.882c0-.688.22-1.252.661-1.69.503-.438 1.069-.657 1.698-.657h12.174c.692 0 1.258.22 1.699.657.503.438.755 1.002.755 1.69v38.213c0 4.069.944 7.136 2.831 9.201 1.95 2.003 4.75 3.005 8.399 3.005 3.586 0 6.354-1.033 8.305-3.098 1.95-2.066 2.925-5.102 2.925-9.108V40.882c0-.688.221-1.252.661-1.69.503-.438 1.069-.657 1.699-.657h12.268c.692 0 1.258.22 1.699.657.44.438.66 1.002.66 1.69V79.19c0 8.7-2.516 15.21-7.549 19.53-4.971 4.318-11.828 6.478-20.574 6.478Zm42.997-.939c-.629 0-1.195-.219-1.699-.658-.44-.5-.66-1.064-.66-1.69V40.882c0-.688.22-1.252.66-1.69.504-.438 1.07-.657 1.699-.657h25.669c8.242 0 14.659 1.878 19.252 5.633 4.656 3.693 6.984 8.951 6.984 15.774 0 4.381-1.038 8.106-3.115 11.173-2.076 3.067-4.938 5.414-8.587 7.041l12.928 23.097c.189.376.284.72.284 1.033 0 .501-.221.97-.661 1.408-.377.376-.818.564-1.321.564h-12.552c-1.824 0-3.114-.845-3.869-2.535l-10.758-20.656h-9.626v20.843c0 .689-.252 1.252-.755 1.69-.441.439-1.007.658-1.699.658h-12.174Zm25.481-36.523c2.894 0 5.096-.689 6.606-2.066 1.51-1.44 2.265-3.411 2.265-5.915s-.755-4.475-2.265-5.915c-1.447-1.502-3.649-2.253-6.606-2.253h-10.853v16.149h10.853Zm39.768 36.523c-.629 0-1.195-.219-1.698-.658-.441-.5-.661-1.064-.661-1.69V40.882c0-.688.22-1.252.661-1.69.503-.438 1.069-.657 1.698-.657h12.646c.692 0 1.259.22 1.699.657.503.438.755 1.002.755 1.69v61.028c0 .689-.252 1.252-.755 1.69-.44.439-1.007.658-1.699.658H414.9ZM82.875 48.385c0 6.998-5.703 12.672-12.737 12.672-7.035 0-12.738-5.674-12.738-12.672 0-6.999 5.703-12.672 12.738-12.672 7.034 0 12.737 5.673 12.737 12.672Z"
|
||||
fill="#2F2F2F"
|
||||
/><ellipse
|
||||
cx="48.716"
|
||||
cy="84.673"
|
||||
rx="12.737"
|
||||
ry="12.672"
|
||||
transform="rotate(180 48.716 84.673)"
|
||||
fill="#2F2F2F"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M96.536 89.021a48.773 48.773 0 0 1-16.779 6.793 33.78 33.78 0 0 0 1.878-11.14c0-1.416-.087-2.811-.256-4.181a34.241 34.241 0 0 0 7.309-3.553 34.05 34.05 0 0 0 12.609-14.59 33.843 33.843 0 0 0 2.629-19.064 33.922 33.922 0 0 0-8.192-17.43 34.2 34.2 0 0 0-16.39-10.215 34.343 34.343 0 0 0-19.34.273A34.186 34.186 0 0 0 42.367 28.57a56.962 56.962 0 0 0-18.65 5.389 48.31 48.31 0 0 1 9.098-16.596A48.649 48.649 0 0 1 55.717 2.176a48.873 48.873 0 0 1 27.52-.388 48.67 48.67 0 0 1 23.326 14.537 48.28 48.28 0 0 1 11.658 24.804 48.163 48.163 0 0 1-3.741 27.129A48.46 48.46 0 0 1 96.536 89.02Zm-72.24-46.238 11.922 1.457a33.856 33.856 0 0 1 1.539-6.726 48.79 48.79 0 0 0-13.462 5.27Z"
|
||||
fill="#2F2F2F"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M22.235 44.037a48.773 48.773 0 0 1 16.913-6.82 33.761 33.761 0 0 0-2.094 11.744c0 1.234.066 2.454.195 3.654a34.246 34.246 0 0 0-7.166 3.503 34.05 34.05 0 0 0-12.609 14.59 33.842 33.842 0 0 0-2.63 19.064 33.924 33.924 0 0 0 8.194 17.43 34.202 34.202 0 0 0 16.39 10.215 34.345 34.345 0 0 0 19.339-.272 34.19 34.19 0 0 0 17.645-12.667 56.948 56.948 0 0 0 18.658-5.426 48.315 48.315 0 0 1-9.114 16.643 48.648 48.648 0 0 1-22.902 15.187 48.871 48.871 0 0 1-27.52.388 48.67 48.67 0 0 1-23.325-14.537A48.275 48.275 0 0 1 .549 91.929 48.161 48.161 0 0 1 4.293 64.8a48.455 48.455 0 0 1 17.943-20.763Z"
|
||||
fill="#2F2F2F"
|
||||
/></svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
3
src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
2
src-tauri/.rustfmt.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
tab_spaces = 4
|
||||
max_width = 100
|
||||
5385
src-tauri/Cargo.lock
generated
Normal file
28
src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
license = ""
|
||||
repository = ""
|
||||
default-run = "app"
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.5", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.8", features = ["api-all"] }
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||
default = [ "custom-protocol" ]
|
||||
# this feature is used used for production builds where `devPath` points to the filesystem
|
||||
# DO NOT remove this
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
3
src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
BIN
src-tauri/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src-tauri/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src-tauri/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 974 B |
BIN
src-tauri/icons/Square107x107Logo.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
src-tauri/icons/Square142x142Logo.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src-tauri/icons/Square150x150Logo.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src-tauri/icons/Square284x284Logo.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
src-tauri/icons/Square30x30Logo.png
Normal file
|
After Width: | Height: | Size: 903 B |
BIN
src-tauri/icons/Square310x310Logo.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src-tauri/icons/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src-tauri/icons/Square71x71Logo.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src-tauri/icons/Square89x89Logo.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
src-tauri/icons/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src-tauri/icons/icon.icns
Normal file
BIN
src-tauri/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
src-tauri/icons/icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
23
src-tauri/src/main.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[tauri::command]
|
||||
fn on_button_clicked() -> String {
|
||||
let start = SystemTime::now();
|
||||
let since_the_epoch = start
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Time went backwards")
|
||||
.as_millis();
|
||||
format!("on_button_clicked called from Rust! (timestamp: {since_the_epoch}ms)")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![on_button_clicked])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
66
src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"build": {
|
||||
"beforeBuildCommand": "pnpm next-build",
|
||||
"beforeDevCommand": "pnpm next-start",
|
||||
"devPath": "http://localhost:3000",
|
||||
"distDir": "../out"
|
||||
},
|
||||
"package": {
|
||||
"productName": "tauri-nextjs-template",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": true
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
"copyright": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "com.tauri.dev",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"fullscreen": false,
|
||||
"resizable": true,
|
||||
"title": "Tauri NextJS App",
|
||||
"width": 1280,
|
||||
"height": 800
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
21
src/components/Card.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
interface CardProps {
|
||||
url: string
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export const Card: React.FC<CardProps> = ({
|
||||
url,
|
||||
title,
|
||||
description,
|
||||
}: CardProps) => (
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="m-4 max-w-xs rounded-xl border border-gray-200 p-6 text-left text-inherit transition-colors hover:border-blue-600 hover:text-blue-600 focus:border-blue-600 focus:text-blue-600 active:border-blue-600 active:text-blue-600"
|
||||
>
|
||||
<h2 className="mb-4 text-2xl">{title} →</h2>
|
||||
<p className="m-0 text-xl">{description}</p>
|
||||
</a>
|
||||
)
|
||||
20
src/components/CardButton.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
interface CardButtonProps {
|
||||
onClick: () => void
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export const CardButton: React.FC<CardButtonProps> = ({
|
||||
onClick,
|
||||
title,
|
||||
description,
|
||||
}: CardButtonProps) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="m-4 max-w-xs rounded-xl border border-gray-200 p-6 text-left text-inherit transition-colors hover:border-blue-600 hover:text-blue-600 focus:border-blue-600 focus:text-blue-600 active:border-blue-600 active:text-blue-600"
|
||||
>
|
||||
<h2 className="mb-4 text-2xl">{title} →</h2>
|
||||
<p className="m-0 text-xl">{description}</p>
|
||||
</button>
|
||||
)
|
||||
40
src/hooks/tauri/shortcuts.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
type ShortcutHandler,
|
||||
isRegistered,
|
||||
register,
|
||||
unregister,
|
||||
} from "@tauri-apps/api/globalShortcut"
|
||||
import { useEffect } from "react"
|
||||
|
||||
/**
|
||||
* A React hook to register global shortcuts using Tauri's globalShortcut API.
|
||||
* Internally this uses a useEffect hook, but has proper support for React.StrictMode
|
||||
* via cleaning up the effect hook, so as to maintain idempotency.
|
||||
*
|
||||
* @param shortcut The key combination string for the shortcut
|
||||
* @param shortcutHandler The handler callback when the shortcut is triggered
|
||||
*/
|
||||
export const useGlobalShortcut = (
|
||||
shortcut: string,
|
||||
shortcutHandler: ShortcutHandler,
|
||||
) => {
|
||||
useEffect(() => {
|
||||
let ignore = false
|
||||
|
||||
async function registerShortcut() {
|
||||
const isShortcutRegistered = await isRegistered(shortcut)
|
||||
if (!ignore && !isShortcutRegistered) {
|
||||
await register(shortcut, shortcutHandler)
|
||||
}
|
||||
}
|
||||
|
||||
void registerShortcut().catch((err: unknown) => {
|
||||
console.error(`Failed to register global shortcut '${shortcut}'`, err)
|
||||
})
|
||||
|
||||
return () => {
|
||||
ignore = true
|
||||
void unregister(shortcut)
|
||||
}
|
||||
}, [shortcut, shortcutHandler])
|
||||
}
|
||||
8
src/pages/_app.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import "@/styles/globals.css"
|
||||
import type { AppProps } from "next/app"
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
|
||||
export default MyApp
|
||||
108
src/pages/index.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Card } from "@/components/Card"
|
||||
import { CardButton } from "@/components/CardButton"
|
||||
import { useGlobalShortcut } from "@/hooks/tauri/shortcuts"
|
||||
import { invoke } from "@tauri-apps/api/tauri"
|
||||
import type { NextPage } from "next"
|
||||
import Head from "next/head"
|
||||
import Image from "next/image"
|
||||
import { useCallback, useState } from "react"
|
||||
|
||||
const Home: NextPage = () => {
|
||||
const [buttonDesc, setButtonDesc] = useState<string>(
|
||||
"Waiting to be clicked. This calls 'on_button_clicked' from Rust.",
|
||||
)
|
||||
const onButtonClick = () => {
|
||||
invoke<string>("on_button_clicked")
|
||||
.then((value) => {
|
||||
setButtonDesc(value)
|
||||
})
|
||||
.catch(() => {
|
||||
setButtonDesc("Failed to invoke Rust command 'on_button_clicked'")
|
||||
})
|
||||
}
|
||||
|
||||
const shortcutHandler = useCallback(() => {
|
||||
console.log("Ctrl+P was pressed!")
|
||||
}, [])
|
||||
useGlobalShortcut("CommandOrControl+P", shortcutHandler)
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-white">
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main className="flex flex-1 flex-col items-center justify-center py-8">
|
||||
<h1 className="m-0 text-center text-6xl">
|
||||
Welcome to{" "}
|
||||
<a
|
||||
href="https://nextjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:underline focus:underline active:underline"
|
||||
>
|
||||
Next.js!
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
<p className="my-12 text-center text-2xl leading-9">
|
||||
Get started by editing{" "}
|
||||
<code className="rounded-md bg-gray-200 p-2 font-mono text-xl">
|
||||
src/pages/index.tsx
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<div className="flex max-w-3xl flex-wrap items-center justify-center">
|
||||
<Card
|
||||
url="https://nextjs.org/docs"
|
||||
title="Documentation"
|
||||
description="Find in-depth information about Next.js features and API."
|
||||
/>
|
||||
|
||||
<Card
|
||||
url="https://nextjs.org/learn"
|
||||
title="Learn"
|
||||
description="Learn about Next.js in an interactive course with quizzes!"
|
||||
/>
|
||||
|
||||
<Card
|
||||
url="https://github.com/vercel/next.js/tree/canary/examples"
|
||||
title="Examples"
|
||||
description="Discover and deploy boilerplate example Next.js projects."
|
||||
/>
|
||||
|
||||
<CardButton
|
||||
onClick={onButtonClick}
|
||||
title="Tauri Invoke"
|
||||
description={buttonDesc}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer className="flex flex-1 flex-grow-0 items-center justify-center border-t border-gray-200 py-4">
|
||||
<div>
|
||||
<a
|
||||
href="https://tauri.app/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex flex-grow items-center justify-center p-4"
|
||||
>
|
||||
Powered by{" "}
|
||||
<span className="ml-2 h-6">
|
||||
<Image
|
||||
src="/tauri_logo_light.svg"
|
||||
alt="Vercel Logo"
|
||||
height={24}
|
||||
width={78}
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
20
src/styles/globals.css
Normal file
@@ -0,0 +1,20 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
7
tailwind.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
24
tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "*.js"],
|
||||
"exclude": ["node_modules", "src-tauri"]
|
||||
}
|
||||