mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-02-06 13:37:09 +00:00
refactor(tauri): support for building without environmental variables (#850)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
bffbf7d242
commit
e02c9419cb
8
.changes/config-refactor.md
Normal file
8
.changes/config-refactor.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
"tauri-utils": minor
|
||||
"tauri-api": minor
|
||||
"tauri": minor
|
||||
---
|
||||
|
||||
The Tauri files are now read on the app space instead of the `tauri` create.
|
||||
Also, the `AppBuilder` `build` function now returns a Result.
|
||||
@ -178,6 +178,11 @@
|
||||
"manager": "rust",
|
||||
"dependencies": ["tauri-utils"]
|
||||
},
|
||||
"tauri-macros": {
|
||||
"path": "./tauri-macros",
|
||||
"manager": "rust",
|
||||
"dependencies": ["tauri-utils"]
|
||||
},
|
||||
"tauri-updater": {
|
||||
"path": "./tauri-updater",
|
||||
"manager": "rust",
|
||||
@ -186,7 +191,7 @@
|
||||
"tauri": {
|
||||
"path": "./tauri",
|
||||
"manager": "rust",
|
||||
"dependencies": ["api", "tauri-api", "tauri-updater"]
|
||||
"dependencies": ["api", "tauri-api", "tauri-macros", "tauri-updater"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,3 @@
|
||||
---
|
||||
|
||||
Added `async` support to the Tauri Rust core on commit [#a169b67](https://github.com/tauri-apps/tauri/commit/a169b67ef0277b958bdac97e33c6e4c41b6844c3).
|
||||
This is a breaking change:
|
||||
- Change `.setup(|webview, source| {` to `.setup(|webview, _source| async move {`.
|
||||
- Change `.invoke_handler(|_webview, arg| {` to `.invoke_handler(|_webview, arg| async move {`.
|
||||
- Add `.await` after `tauri::execute_promise()` calls.
|
||||
|
||||
@ -3,5 +3,3 @@
|
||||
---
|
||||
|
||||
The Tauri Node.js CLI package is now `@tauri-apps/cli`.
|
||||
To use the new CLI, delete the old `tauri` from your `package.json` and install the new package:
|
||||
`$ yarn remove tauri && yarn add --dev @tauri-apps/cli` or `$ npm uninstall tauri && npm install --save-dev @tauri-apps/cli`.
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
members = [
|
||||
"tauri",
|
||||
"tauri-api",
|
||||
"tauri-macros",
|
||||
"tauri-utils",
|
||||
]
|
||||
exclude = [
|
||||
|
||||
@ -3,54 +3,54 @@ module.exports = {
|
||||
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
jest: true
|
||||
},
|
||||
|
||||
parser: "@typescript-eslint/parser",
|
||||
parser: '@typescript-eslint/parser',
|
||||
|
||||
extends: [
|
||||
"standard-with-typescript",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:lodash-template/recommended",
|
||||
'standard-with-typescript',
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
'plugin:lodash-template/recommended',
|
||||
// TODO: make this work with typescript
|
||||
// 'plugin:node/recommended'
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint",
|
||||
'prettier',
|
||||
'prettier/@typescript-eslint'
|
||||
],
|
||||
|
||||
plugins: ["@typescript-eslint", "node", "security"],
|
||||
plugins: ['@typescript-eslint', 'node', 'security'],
|
||||
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: "./tsconfig.json",
|
||||
project: './tsconfig.json'
|
||||
},
|
||||
|
||||
globals: {
|
||||
__statics: true,
|
||||
process: true,
|
||||
process: true
|
||||
},
|
||||
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
// allow console.log during development only
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
// allow debugger during development only
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
"no-process-exit": "off",
|
||||
"security/detect-non-literal-fs-filename": "warn",
|
||||
"security/detect-unsafe-regex": "error",
|
||||
"security/detect-buffer-noassert": "error",
|
||||
"security/detect-child-process": "warn",
|
||||
"security/detect-disable-mustache-escape": "error",
|
||||
"security/detect-eval-with-expression": "error",
|
||||
"security/detect-no-csrf-before-method-override": "error",
|
||||
"security/detect-non-literal-regexp": "error",
|
||||
"security/detect-non-literal-require": "warn",
|
||||
"security/detect-object-injection": "warn",
|
||||
"security/detect-possible-timing-attacks": "error",
|
||||
"security/detect-pseudoRandomBytes": "error",
|
||||
"space-before-function-paren": "off",
|
||||
"@typescript-eslint/default-param-last": "off",
|
||||
"@typescript-eslint/strict-boolean-expressions": 0,
|
||||
},
|
||||
};
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-process-exit': 'off',
|
||||
'security/detect-non-literal-fs-filename': 'warn',
|
||||
'security/detect-unsafe-regex': 'error',
|
||||
'security/detect-buffer-noassert': 'error',
|
||||
'security/detect-child-process': 'warn',
|
||||
'security/detect-disable-mustache-escape': 'error',
|
||||
'security/detect-eval-with-expression': 'error',
|
||||
'security/detect-no-csrf-before-method-override': 'error',
|
||||
'security/detect-non-literal-regexp': 'error',
|
||||
'security/detect-non-literal-require': 'warn',
|
||||
'security/detect-object-injection': 'warn',
|
||||
'security/detect-possible-timing-attacks': 'error',
|
||||
'security/detect-pseudoRandomBytes': 'error',
|
||||
'space-before-function-paren': 'off',
|
||||
'@typescript-eslint/default-param-last': 'off',
|
||||
'@typescript-eslint/strict-boolean-expressions': 0
|
||||
}
|
||||
}
|
||||
|
||||
5
api/.prettierrc.js
Normal file
5
api/.prettierrc.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
trailingComma: 'none'
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: "current",
|
||||
node: 'current'
|
||||
},
|
||||
modules: "commonjs",
|
||||
},
|
||||
modules: 'commonjs'
|
||||
}
|
||||
],
|
||||
"@babel/preset-typescript",
|
||||
],
|
||||
};
|
||||
'@babel/preset-typescript'
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,107 +1,107 @@
|
||||
// rollup.config.js
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import sucrase from "@rollup/plugin-sucrase";
|
||||
import babel, { getBabelOutputPlugin } from "@rollup/plugin-babel";
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
import pkg from "./package.json";
|
||||
import { terser } from 'rollup-plugin-terser'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import sucrase from '@rollup/plugin-sucrase'
|
||||
import babel, { getBabelOutputPlugin } from '@rollup/plugin-babel'
|
||||
import typescript from '@rollup/plugin-typescript'
|
||||
import pkg from './package.json'
|
||||
|
||||
export default [
|
||||
{
|
||||
input: {
|
||||
fs: "./src/fs.ts",
|
||||
path: "./src/path.ts",
|
||||
dialog: "./src/dialog.ts",
|
||||
event: "./src/event.ts",
|
||||
http: "./src/http.ts",
|
||||
index: "./src/index.ts",
|
||||
process: "./src/process.ts",
|
||||
tauri: "./src/tauri.ts",
|
||||
window: "./src/window.ts",
|
||||
cli: "./src/cli.ts",
|
||||
notification: "./src/notification.ts",
|
||||
fs: './src/fs.ts',
|
||||
path: './src/path.ts',
|
||||
dialog: './src/dialog.ts',
|
||||
event: './src/event.ts',
|
||||
http: './src/http.ts',
|
||||
index: './src/index.ts',
|
||||
process: './src/process.ts',
|
||||
tauri: './src/tauri.ts',
|
||||
window: './src/window.ts',
|
||||
cli: './src/cli.ts',
|
||||
notification: './src/notification.ts'
|
||||
},
|
||||
treeshake: true,
|
||||
perf: true,
|
||||
output: [
|
||||
{
|
||||
dir: "dist/",
|
||||
entryFileNames: "[name].js",
|
||||
format: "cjs",
|
||||
exports: "named",
|
||||
globals: {},
|
||||
dir: 'dist/',
|
||||
entryFileNames: '[name].js',
|
||||
format: 'cjs',
|
||||
exports: 'named',
|
||||
globals: {}
|
||||
},
|
||||
{
|
||||
dir: "dist/",
|
||||
entryFileNames: "[name].mjs",
|
||||
format: "esm",
|
||||
exports: "named",
|
||||
globals: {},
|
||||
},
|
||||
dir: 'dist/',
|
||||
entryFileNames: '[name].mjs',
|
||||
format: 'esm',
|
||||
exports: 'named',
|
||||
globals: {}
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
commonjs({}),
|
||||
resolve({
|
||||
// pass custom options to the resolve plugin
|
||||
customResolveOptions: {
|
||||
moduleDirectory: "node_modules",
|
||||
},
|
||||
moduleDirectory: 'node_modules'
|
||||
}
|
||||
}),
|
||||
typescript({
|
||||
tsconfig: "./tsconfig.json",
|
||||
tsconfig: './tsconfig.json'
|
||||
}),
|
||||
babel({
|
||||
configFile: false,
|
||||
presets: [["@babel/preset-env"], ["@babel/preset-typescript"]],
|
||||
presets: [['@babel/preset-env'], ['@babel/preset-typescript']]
|
||||
}),
|
||||
terser(),
|
||||
terser()
|
||||
],
|
||||
external: [
|
||||
...Object.keys(pkg.dependencies || {}),
|
||||
...Object.keys(pkg.peerDependencies || {}),
|
||||
...Object.keys(pkg.peerDependencies || {})
|
||||
],
|
||||
watch: {
|
||||
chokidar: true,
|
||||
include: "src/**",
|
||||
exclude: "node_modules/**",
|
||||
},
|
||||
include: 'src/**',
|
||||
exclude: 'node_modules/**'
|
||||
}
|
||||
},
|
||||
{
|
||||
input: {
|
||||
bundle: "./src/bundle.ts",
|
||||
bundle: './src/bundle.ts'
|
||||
},
|
||||
output: [
|
||||
{
|
||||
name: "__TAURI__",
|
||||
dir: "dist/", // if it needs to run in the browser
|
||||
entryFileNames: "tauri.bundle.umd.js",
|
||||
format: "umd",
|
||||
name: '__TAURI__',
|
||||
dir: 'dist/', // if it needs to run in the browser
|
||||
entryFileNames: 'tauri.bundle.umd.js',
|
||||
format: 'umd',
|
||||
plugins: [
|
||||
getBabelOutputPlugin({
|
||||
presets: [["@babel/preset-env", { modules: "umd" }]],
|
||||
allowAllFormats: true,
|
||||
presets: [['@babel/preset-env', { modules: 'umd' }]],
|
||||
allowAllFormats: true
|
||||
}),
|
||||
terser(),
|
||||
terser()
|
||||
],
|
||||
globals: {},
|
||||
},
|
||||
globals: {}
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
sucrase({
|
||||
exclude: ["node_modules"],
|
||||
transforms: ["typescript"],
|
||||
exclude: ['node_modules'],
|
||||
transforms: ['typescript']
|
||||
}),
|
||||
resolve({
|
||||
// pass custom options to the resolve plugin
|
||||
customResolveOptions: {
|
||||
moduleDirectory: "node_modules",
|
||||
},
|
||||
}),
|
||||
moduleDirectory: 'node_modules'
|
||||
}
|
||||
})
|
||||
],
|
||||
external: [
|
||||
...Object.keys(pkg.dependencies || {}),
|
||||
...Object.keys(pkg.peerDependencies || {}),
|
||||
],
|
||||
},
|
||||
];
|
||||
...Object.keys(pkg.peerDependencies || {})
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import "regenerator-runtime/runtime";
|
||||
import * as cli from "./cli";
|
||||
import * as dialog from "./dialog";
|
||||
import * as event from "./event";
|
||||
import * as fs from "./fs";
|
||||
import * as path from "./path";
|
||||
import http from "./http";
|
||||
import * as process from "./process";
|
||||
import * as tauri from "./tauri";
|
||||
import * as window from "./window";
|
||||
import * as notification from "./notification";
|
||||
import 'regenerator-runtime/runtime'
|
||||
import * as cli from './cli'
|
||||
import * as dialog from './dialog'
|
||||
import * as event from './event'
|
||||
import * as fs from './fs'
|
||||
import * as path from './path'
|
||||
import http from './http'
|
||||
import * as process from './process'
|
||||
import * as tauri from './tauri'
|
||||
import * as window from './window'
|
||||
import * as notification from './notification'
|
||||
|
||||
export {
|
||||
cli,
|
||||
@ -20,5 +20,5 @@ export {
|
||||
process,
|
||||
tauri,
|
||||
window,
|
||||
notification,
|
||||
};
|
||||
notification
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { promisified } from "./tauri";
|
||||
import { promisified } from './tauri'
|
||||
|
||||
export interface ArgMatch {
|
||||
/**
|
||||
@ -6,21 +6,21 @@ export interface ArgMatch {
|
||||
* boolean if flag
|
||||
* string[] or null if takes multiple values
|
||||
*/
|
||||
value: string | boolean | string[] | null;
|
||||
value: string | boolean | string[] | null
|
||||
/**
|
||||
* number of occurrences
|
||||
*/
|
||||
occurrences: number;
|
||||
occurrences: number
|
||||
}
|
||||
|
||||
export interface SubcommandMatch {
|
||||
name: string;
|
||||
matches: CliMatches;
|
||||
name: string
|
||||
matches: CliMatches
|
||||
}
|
||||
|
||||
export interface CliMatches {
|
||||
args: { [name: string]: ArgMatch };
|
||||
subcommand: SubcommandMatch | null;
|
||||
args: { [name: string]: ArgMatch }
|
||||
subcommand: SubcommandMatch | null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -28,8 +28,8 @@ export interface CliMatches {
|
||||
*/
|
||||
async function getMatches(): Promise<CliMatches> {
|
||||
return await promisified<CliMatches>({
|
||||
cmd: "cliMatches",
|
||||
});
|
||||
cmd: 'cliMatches'
|
||||
})
|
||||
}
|
||||
|
||||
export { getMatches };
|
||||
export { getMatches }
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { promisified } from "./tauri";
|
||||
import { promisified } from './tauri'
|
||||
|
||||
export interface OpenDialogOptions {
|
||||
filter?: string;
|
||||
defaultPath?: string;
|
||||
multiple?: boolean;
|
||||
directory?: boolean;
|
||||
filter?: string
|
||||
defaultPath?: string
|
||||
multiple?: boolean
|
||||
directory?: boolean
|
||||
}
|
||||
|
||||
export type SaveDialogOptions = Pick<
|
||||
OpenDialogOptions,
|
||||
"filter" | "defaultPath"
|
||||
>;
|
||||
'filter' | 'defaultPath'
|
||||
>
|
||||
|
||||
/**
|
||||
* @name openDialog
|
||||
@ -25,14 +25,14 @@ export type SaveDialogOptions = Pick<
|
||||
async function open(
|
||||
options: OpenDialogOptions = {}
|
||||
): Promise<string | string[]> {
|
||||
if (typeof options === "object") {
|
||||
Object.freeze(options);
|
||||
if (typeof options === 'object') {
|
||||
Object.freeze(options)
|
||||
}
|
||||
|
||||
return await promisified({
|
||||
cmd: "openDialog",
|
||||
options,
|
||||
});
|
||||
cmd: 'openDialog',
|
||||
options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,14 +44,14 @@ async function open(
|
||||
* @returns {Promise<string>} Promise resolving to the select path
|
||||
*/
|
||||
async function save(options: SaveDialogOptions = {}): Promise<string> {
|
||||
if (typeof options === "object") {
|
||||
Object.freeze(options);
|
||||
if (typeof options === 'object') {
|
||||
Object.freeze(options)
|
||||
}
|
||||
|
||||
return await promisified({
|
||||
cmd: "saveDialog",
|
||||
options,
|
||||
});
|
||||
cmd: 'saveDialog',
|
||||
options
|
||||
})
|
||||
}
|
||||
|
||||
export { open, save };
|
||||
export { open, save }
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { invoke, transformCallback } from "./tauri";
|
||||
import { invoke, transformCallback } from './tauri'
|
||||
|
||||
export interface Event<T> {
|
||||
type: string;
|
||||
payload: T;
|
||||
type: string
|
||||
payload: T
|
||||
}
|
||||
|
||||
export type EventCallback<T> = (event: Event<T>) => void;
|
||||
export type EventCallback<T> = (event: Event<T>) => void
|
||||
|
||||
/**
|
||||
* listen to an event from the backend
|
||||
@ -19,11 +19,11 @@ function listen<T>(
|
||||
once = false
|
||||
): void {
|
||||
invoke({
|
||||
cmd: "listen",
|
||||
cmd: 'listen',
|
||||
event,
|
||||
handler: transformCallback(handler, once),
|
||||
once,
|
||||
});
|
||||
once
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,10 +34,10 @@ function listen<T>(
|
||||
*/
|
||||
function emit(event: string, payload?: string): void {
|
||||
invoke({
|
||||
cmd: "emit",
|
||||
cmd: 'emit',
|
||||
event,
|
||||
payload,
|
||||
});
|
||||
payload
|
||||
})
|
||||
}
|
||||
|
||||
export { listen, emit };
|
||||
export { listen, emit }
|
||||
|
||||
@ -1,47 +1,47 @@
|
||||
import { promisified } from "./tauri";
|
||||
import { promisified } from './tauri'
|
||||
|
||||
export enum ResponseType {
|
||||
JSON = 1,
|
||||
Text = 2,
|
||||
Binary = 3,
|
||||
Binary = 3
|
||||
}
|
||||
|
||||
export enum BodyType {
|
||||
Form = 1,
|
||||
File = 2,
|
||||
Auto = 3,
|
||||
Auto = 3
|
||||
}
|
||||
|
||||
export type Body = object | string | BinaryType;
|
||||
export type Body = object | string | BinaryType
|
||||
|
||||
export type HttpVerb =
|
||||
| "GET"
|
||||
| "POST"
|
||||
| "PUT"
|
||||
| "DELETE"
|
||||
| "PATCH"
|
||||
| "HEAD"
|
||||
| "OPTIONS"
|
||||
| "CONNECT"
|
||||
| "TRACE";
|
||||
| 'GET'
|
||||
| 'POST'
|
||||
| 'PUT'
|
||||
| 'DELETE'
|
||||
| 'PATCH'
|
||||
| 'HEAD'
|
||||
| 'OPTIONS'
|
||||
| 'CONNECT'
|
||||
| 'TRACE'
|
||||
|
||||
export interface HttpOptions {
|
||||
method: HttpVerb;
|
||||
url: string;
|
||||
headers?: Record<string, any>;
|
||||
params?: Record<string, any>;
|
||||
body?: Body;
|
||||
followRedirects: boolean;
|
||||
maxRedirections: boolean;
|
||||
connectTimeout: number;
|
||||
readTimeout: number;
|
||||
timeout: number;
|
||||
allowCompression: boolean;
|
||||
responseType?: ResponseType;
|
||||
bodyType: BodyType;
|
||||
method: HttpVerb
|
||||
url: string
|
||||
headers?: Record<string, any>
|
||||
params?: Record<string, any>
|
||||
body?: Body
|
||||
followRedirects: boolean
|
||||
maxRedirections: boolean
|
||||
connectTimeout: number
|
||||
readTimeout: number
|
||||
timeout: number
|
||||
allowCompression: boolean
|
||||
responseType?: ResponseType
|
||||
bodyType: BodyType
|
||||
}
|
||||
|
||||
export type PartialOptions = Omit<HttpOptions, "method" | "url">;
|
||||
export type PartialOptions = Omit<HttpOptions, 'method' | 'url'>
|
||||
|
||||
/**
|
||||
* makes a HTTP request
|
||||
@ -52,9 +52,9 @@ export type PartialOptions = Omit<HttpOptions, "method" | "url">;
|
||||
*/
|
||||
async function request<T>(options: HttpOptions): Promise<T> {
|
||||
return await promisified({
|
||||
cmd: "httpRequest",
|
||||
options: options,
|
||||
});
|
||||
cmd: 'httpRequest',
|
||||
options: options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,10 +67,10 @@ async function request<T>(options: HttpOptions): Promise<T> {
|
||||
*/
|
||||
async function get<T>(url: string, options: PartialOptions): Promise<T> {
|
||||
return await request({
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
url,
|
||||
...options,
|
||||
});
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,11 +88,11 @@ async function post<T>(
|
||||
options: PartialOptions
|
||||
): Promise<T> {
|
||||
return await request({
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
url,
|
||||
body,
|
||||
...options,
|
||||
});
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,11 +110,11 @@ async function put<T>(
|
||||
options: PartialOptions
|
||||
): Promise<T> {
|
||||
return await request({
|
||||
method: "PUT",
|
||||
method: 'PUT',
|
||||
url,
|
||||
body,
|
||||
...options,
|
||||
});
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,10 +127,10 @@ async function put<T>(
|
||||
*/
|
||||
async function patch<T>(url: string, options: PartialOptions): Promise<T> {
|
||||
return await request({
|
||||
method: "PATCH",
|
||||
method: 'PATCH',
|
||||
url,
|
||||
...options,
|
||||
});
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,10 +146,10 @@ async function deleteRequest<T>(
|
||||
options: PartialOptions
|
||||
): Promise<T> {
|
||||
return await request({
|
||||
method: "DELETE",
|
||||
method: 'DELETE',
|
||||
url,
|
||||
...options,
|
||||
});
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
@ -160,5 +160,5 @@ export default {
|
||||
patch,
|
||||
delete: deleteRequest,
|
||||
ResponseType,
|
||||
BodyType,
|
||||
};
|
||||
BodyType
|
||||
}
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
import * as api from "./bundle";
|
||||
export default api;
|
||||
import * as api from './bundle'
|
||||
export default api
|
||||
|
||||
@ -1,35 +1,35 @@
|
||||
import { promisified } from "./tauri";
|
||||
import { promisified } from './tauri'
|
||||
|
||||
export interface Options {
|
||||
title: string;
|
||||
body?: string;
|
||||
icon?: string;
|
||||
title: string
|
||||
body?: string
|
||||
icon?: string
|
||||
}
|
||||
|
||||
export type PartialOptions = Omit<Options, "title">;
|
||||
export type Permission = "granted" | "denied" | "default";
|
||||
export type PartialOptions = Omit<Options, 'title'>
|
||||
export type Permission = 'granted' | 'denied' | 'default'
|
||||
|
||||
async function isPermissionGranted(): Promise<boolean | null> {
|
||||
if (window.Notification.permission !== "default") {
|
||||
return await Promise.resolve(window.Notification.permission === "granted");
|
||||
if (window.Notification.permission !== 'default') {
|
||||
return await Promise.resolve(window.Notification.permission === 'granted')
|
||||
}
|
||||
return await promisified({
|
||||
cmd: "isNotificationPermissionGranted",
|
||||
});
|
||||
cmd: 'isNotificationPermissionGranted'
|
||||
})
|
||||
}
|
||||
|
||||
async function requestPermission(): Promise<Permission> {
|
||||
return await window.Notification.requestPermission();
|
||||
return await window.Notification.requestPermission()
|
||||
}
|
||||
|
||||
function sendNotification(options: Options | string): void {
|
||||
if (typeof options === "string") {
|
||||
if (typeof options === 'string') {
|
||||
// eslint-disable-next-line no-new
|
||||
new window.Notification(options);
|
||||
new window.Notification(options)
|
||||
} else {
|
||||
// eslint-disable-next-line no-new
|
||||
new window.Notification(options.title, options);
|
||||
new window.Notification(options.title, options)
|
||||
}
|
||||
}
|
||||
|
||||
export { sendNotification, requestPermission, isPermissionGranted };
|
||||
export { sendNotification, requestPermission, isPermissionGranted }
|
||||
|
||||
158
api/src/path.ts
158
api/src/path.ts
@ -1,5 +1,5 @@
|
||||
import { promisified } from "./tauri";
|
||||
import { BaseDirectory } from "./fs";
|
||||
import { promisified } from './tauri'
|
||||
import { BaseDirectory } from './fs'
|
||||
|
||||
/**
|
||||
* @name appDir
|
||||
@ -8,10 +8,10 @@ import { BaseDirectory } from "./fs";
|
||||
*/
|
||||
async function appDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.App,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.App
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -21,10 +21,10 @@ async function appDir(): Promise<string> {
|
||||
*/
|
||||
async function audioDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Audio,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Audio
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,10 +34,10 @@ async function audioDir(): Promise<string> {
|
||||
*/
|
||||
async function cacheDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Cache,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Cache
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,10 +47,10 @@ async function cacheDir(): Promise<string> {
|
||||
*/
|
||||
async function configDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Config,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Config
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,10 +60,10 @@ async function configDir(): Promise<string> {
|
||||
*/
|
||||
async function dataDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Data,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,10 +73,10 @@ async function dataDir(): Promise<string> {
|
||||
*/
|
||||
async function desktopDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Desktop,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Desktop
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,10 +86,10 @@ async function desktopDir(): Promise<string> {
|
||||
*/
|
||||
async function documentDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Document,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Document
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,10 +99,10 @@ async function documentDir(): Promise<string> {
|
||||
*/
|
||||
async function downloadDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Download,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Download
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,10 +112,10 @@ async function downloadDir(): Promise<string> {
|
||||
*/
|
||||
async function executableDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Executable,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Executable
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,10 +125,10 @@ async function executableDir(): Promise<string> {
|
||||
*/
|
||||
async function fontDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Font,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Font
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,10 +138,10 @@ async function fontDir(): Promise<string> {
|
||||
*/
|
||||
async function homeDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Home,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Home
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,10 +151,10 @@ async function homeDir(): Promise<string> {
|
||||
*/
|
||||
async function localDataDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.LocalData,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.LocalData
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,10 +164,10 @@ async function localDataDir(): Promise<string> {
|
||||
*/
|
||||
async function pictureDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Picture,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Picture
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,10 +177,10 @@ async function pictureDir(): Promise<string> {
|
||||
*/
|
||||
async function publicDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Public,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Public
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,10 +190,10 @@ async function publicDir(): Promise<string> {
|
||||
*/
|
||||
async function resourceDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Resource,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Resource
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -203,10 +203,10 @@ async function resourceDir(): Promise<string> {
|
||||
*/
|
||||
async function runtimeDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Runtime,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Runtime
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,10 +216,10 @@ async function runtimeDir(): Promise<string> {
|
||||
*/
|
||||
async function templateDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Template,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Template
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,10 +229,10 @@ async function templateDir(): Promise<string> {
|
||||
*/
|
||||
async function videoDir(): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
path: "",
|
||||
directory: BaseDirectory.Video,
|
||||
});
|
||||
cmd: 'resolvePath',
|
||||
path: '',
|
||||
directory: BaseDirectory.Video
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,10 +245,10 @@ async function resolvePath(
|
||||
directory: BaseDirectory
|
||||
): Promise<string> {
|
||||
return await promisified<string>({
|
||||
cmd: "resolvePath",
|
||||
cmd: 'resolvePath',
|
||||
path,
|
||||
directory,
|
||||
});
|
||||
directory
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
@ -270,5 +270,5 @@ export {
|
||||
runtimeDir,
|
||||
templateDir,
|
||||
videoDir,
|
||||
resolvePath,
|
||||
};
|
||||
resolvePath
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { promisified } from "./tauri";
|
||||
import { promisified } from './tauri'
|
||||
|
||||
/**
|
||||
* spawns a process
|
||||
@ -11,15 +11,15 @@ async function execute(
|
||||
command: string,
|
||||
args?: string | string[]
|
||||
): Promise<string> {
|
||||
if (typeof args === "object") {
|
||||
Object.freeze(args);
|
||||
if (typeof args === 'object') {
|
||||
Object.freeze(args)
|
||||
}
|
||||
|
||||
return await promisified({
|
||||
cmd: "execute",
|
||||
cmd: 'execute',
|
||||
command,
|
||||
args: typeof args === "string" ? [args] : args,
|
||||
});
|
||||
args: typeof args === 'string' ? [args] : args
|
||||
})
|
||||
}
|
||||
|
||||
export { execute };
|
||||
export { execute }
|
||||
|
||||
@ -1,31 +1,31 @@
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Window {
|
||||
__TAURI_INVOKE_HANDLER__: (command: string) => void;
|
||||
__TAURI_INVOKE_HANDLER__: (command: string) => void
|
||||
}
|
||||
}
|
||||
|
||||
function s4(): string {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
.substring(1)
|
||||
}
|
||||
|
||||
function uid(): string {
|
||||
return (
|
||||
s4() +
|
||||
s4() +
|
||||
"-" +
|
||||
'-' +
|
||||
s4() +
|
||||
"-" +
|
||||
'-' +
|
||||
s4() +
|
||||
"-" +
|
||||
'-' +
|
||||
s4() +
|
||||
"-" +
|
||||
'-' +
|
||||
s4() +
|
||||
s4() +
|
||||
s4()
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,28 +34,28 @@ function uid(): string {
|
||||
* @param args
|
||||
*/
|
||||
function invoke(args: any): void {
|
||||
window.__TAURI_INVOKE_HANDLER__(JSON.stringify(args));
|
||||
window.__TAURI_INVOKE_HANDLER__(JSON.stringify(args))
|
||||
}
|
||||
|
||||
function transformCallback(
|
||||
callback?: (response: any) => void,
|
||||
once = false
|
||||
): string {
|
||||
const identifier = uid();
|
||||
const identifier = uid()
|
||||
|
||||
Object.defineProperty(window, identifier, {
|
||||
value: (result: any) => {
|
||||
if (once) {
|
||||
Reflect.deleteProperty(window, identifier);
|
||||
Reflect.deleteProperty(window, identifier)
|
||||
}
|
||||
|
||||
return callback?.(result);
|
||||
return callback?.(result)
|
||||
},
|
||||
writable: false,
|
||||
configurable: true,
|
||||
});
|
||||
configurable: true
|
||||
})
|
||||
|
||||
return identifier;
|
||||
return identifier
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,20 +68,20 @@ function transformCallback(
|
||||
async function promisified<T>(args: any): Promise<T> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const callback = transformCallback((e) => {
|
||||
resolve(e);
|
||||
Reflect.deleteProperty(window, error);
|
||||
}, true);
|
||||
resolve(e)
|
||||
Reflect.deleteProperty(window, error)
|
||||
}, true)
|
||||
const error = transformCallback((e) => {
|
||||
reject(e);
|
||||
Reflect.deleteProperty(window, callback);
|
||||
}, true);
|
||||
reject(e)
|
||||
Reflect.deleteProperty(window, callback)
|
||||
}, true)
|
||||
|
||||
invoke({
|
||||
callback,
|
||||
error,
|
||||
...args,
|
||||
});
|
||||
});
|
||||
...args
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export { invoke, transformCallback, promisified };
|
||||
export { invoke, transformCallback, promisified }
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { invoke } from "./tauri";
|
||||
import { invoke } from './tauri'
|
||||
|
||||
/**
|
||||
* sets the window title
|
||||
@ -7,9 +7,9 @@ import { invoke } from "./tauri";
|
||||
*/
|
||||
function setTitle(title: string): void {
|
||||
invoke({
|
||||
cmd: "setTitle",
|
||||
title,
|
||||
});
|
||||
cmd: 'setTitle',
|
||||
title
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -19,9 +19,9 @@ function setTitle(title: string): void {
|
||||
*/
|
||||
function open(url: string): void {
|
||||
invoke({
|
||||
cmd: "open",
|
||||
uri: url,
|
||||
});
|
||||
cmd: 'open',
|
||||
uri: url
|
||||
})
|
||||
}
|
||||
|
||||
export { setTitle, open };
|
||||
export { setTitle, open }
|
||||
|
||||
@ -130,4 +130,4 @@
|
||||
- Create UMD, ESM and CJS artifacts for the JavaScript API entry point from TS source using rollup.
|
||||
- Renaming window.tauri to window.\_\_TAURI\_\_, closing #435.
|
||||
The **Tauri** object now follows the TypeScript API structure (e.g. window.tauri.readTextFile is now window.\_\_TAURI\_\_.fs.readTextFile).
|
||||
If you want to keep the `window.tauri` object for a while, you can add a [mapping object](https://gist.github.com/lucasfernog/8f7b29cadd91d92ee2cf816a20c2ef01) to your code.
|
||||
If you want to keep the `window.tauri` object for a while, you can add a [mapping object](https://gist.github.com/lucasfernog/8f7b29cadd91d92ee2cf816a20c2ef01) to your code.
|
||||
@ -9,8 +9,7 @@ function runCliCommand(
|
||||
args: Args
|
||||
): { pid: number; promise: Promise<void> } {
|
||||
const argsArray = []
|
||||
for (const argName in args) {
|
||||
const argValue = args[argName]
|
||||
for (const [argName, argValue] of Object.entries(args)) {
|
||||
if (argValue === false) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -406,6 +406,10 @@
|
||||
}
|
||||
],
|
||||
"description": "the embedded server port number or the 'random' string to generate one at runtime"
|
||||
},
|
||||
"publicPath": {
|
||||
"description": "The base path for all the assets within your application",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
||||
@ -229,6 +229,10 @@ export interface TauriConfig {
|
||||
* the embedded server port number or the 'random' string to generate one at runtime
|
||||
*/
|
||||
port?: number | 'random' | undefined
|
||||
/**
|
||||
* The base path for all the assets within your application
|
||||
*/
|
||||
publicPath?: string
|
||||
}
|
||||
/**
|
||||
* tauri bundler configuration
|
||||
|
||||
@ -458,6 +458,11 @@ export const TauriConfigSchema = {
|
||||
],
|
||||
description:
|
||||
"the embedded server port number or the 'random' string to generate one at runtime"
|
||||
},
|
||||
publicPath: {
|
||||
description:
|
||||
'The base path for all the assets within your application.',
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
type: 'object'
|
||||
|
||||
@ -5,8 +5,11 @@
|
||||
|
||||
mod cmd;
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
struct Context;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::<tauri::flavors::Wry>::new()
|
||||
tauri::AppBuilder::<tauri::flavors::Wry, Context>::new()
|
||||
.invoke_handler(|_webview, arg| async move {
|
||||
use cmd::Cmd::*;
|
||||
match serde_json::from_str(&arg) {
|
||||
@ -24,5 +27,6 @@ fn main() {
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.unwrap()
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -24,8 +24,6 @@ icon = [
|
||||
serde_json = "1.0.61"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
phf = "0.8.0"
|
||||
includedir = "0.6.0"
|
||||
tauri = { path = "../../../../../../../tauri", features =["all-api"]}
|
||||
|
||||
[features]
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#[derive(Deserialize)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||
pub enum Cmd {
|
||||
// your custom commands
|
||||
// multiple arguments are allowed
|
||||
// note that rename_all = "camelCase": you need to use "myCustomCommand" on JS
|
||||
Exit { },
|
||||
Exit {},
|
||||
}
|
||||
|
||||
@ -1,26 +1,25 @@
|
||||
mod cmd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
use tauri::ApplicationDispatcherExt;
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
struct Context;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::new()
|
||||
.setup(|webview, _| async move {
|
||||
let mut webview_ = webview.as_mut();
|
||||
tauri::AppBuilder::<tauri::flavors::Wry, Context>::new()
|
||||
.setup(|dispatcher, _| async move {
|
||||
let mut dispatcher_ = dispatcher.clone();
|
||||
tauri::event::listen(String::from("hello"), move |_| {
|
||||
tauri::event::emit(
|
||||
&mut webview_,
|
||||
&mut dispatcher_,
|
||||
String::from("reply"),
|
||||
Some("{ msg: 'TEST' }".to_string()),
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
let _ = webview.dispatch(|w| {
|
||||
w.eval("window.onTauriInit && window.onTauriInit()");
|
||||
});
|
||||
dispatcher.eval("window.onTauriInit && window.onTauriInit()");
|
||||
})
|
||||
.invoke_handler(|webview, arg| async move {
|
||||
.invoke_handler(|dispatcher, arg| async move {
|
||||
use cmd::Cmd::*;
|
||||
match serde_json::from_str(&arg) {
|
||||
Err(e) => Err(e.to_string()),
|
||||
@ -28,7 +27,7 @@ fn main() {
|
||||
match command {
|
||||
// definitions for your custom commands from Cmd here
|
||||
Exit {} => {
|
||||
webview.terminate();
|
||||
// TODO dispatcher.terminate();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -36,5 +35,6 @@ fn main() {
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.unwrap()
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -24,8 +24,8 @@ semver = "0.11"
|
||||
tempfile = "3"
|
||||
either = "1.6.1"
|
||||
tar = "0.4"
|
||||
flate2 = "1"
|
||||
anyhow = "1.0.38"
|
||||
flate2 = "1.0"
|
||||
thiserror = "1.0.23"
|
||||
rand = "0.8"
|
||||
nfd = "0.0.4"
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
use std::{
|
||||
env,
|
||||
error::Error,
|
||||
fs::{read_to_string, File},
|
||||
io::{BufWriter, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
let out_dir = env::var("OUT_DIR")?;
|
||||
|
||||
let dest_config_path = Path::new(&out_dir).join("tauri.conf.json");
|
||||
let mut config_file = BufWriter::new(File::create(&dest_config_path)?);
|
||||
|
||||
match env::var_os("TAURI_CONFIG") {
|
||||
Some(tauri_config) => {
|
||||
println!("cargo:rerun-if-env-changed=TAURI_CONFIG");
|
||||
let tauri_config_string = tauri_config.into_string().unwrap();
|
||||
write!(config_file, "{}", tauri_config_string)?;
|
||||
}
|
||||
None => match env::var_os("TAURI_DIR") {
|
||||
Some(tauri_dir) => {
|
||||
let tauri_dir_string = tauri_dir.into_string().unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed={}", tauri_dir_string);
|
||||
|
||||
let original_config_path = Path::new(&tauri_dir_string).join("tauri.conf.json");
|
||||
let original_config = read_to_string(original_config_path)?;
|
||||
|
||||
write!(config_file, "{}", original_config)?;
|
||||
}
|
||||
None => {
|
||||
write!(config_file, "{{}}")?;
|
||||
println!("Build error: Couldn't find ENV: TAURI_CONFIG or TAURI_DIR");
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::config::{get as get_config, CliArg, CliConfig};
|
||||
use crate::config::{CliArg, CliConfig, Config};
|
||||
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use serde::Serialize;
|
||||
@ -53,8 +53,7 @@ impl Matches {
|
||||
}
|
||||
|
||||
/// Gets the arg matches of the CLI definition.
|
||||
pub fn get_matches() -> crate::Result<Matches> {
|
||||
let config = get_config()?;
|
||||
pub fn get_matches(config: &Config) -> crate::Result<Matches> {
|
||||
let cli = config
|
||||
.tauri
|
||||
.cli
|
||||
|
||||
@ -3,8 +3,6 @@
|
||||
|
||||
/// The Command API module allows you to manage child processes.
|
||||
pub mod command;
|
||||
/// The Config module allows you to read the configuration from `tauri.conf.json`.
|
||||
pub mod config;
|
||||
/// The Dialog API module allows you to show messages and prompt for file paths.
|
||||
pub mod dialog;
|
||||
/// The Dir module is a helper for file system directory management.
|
||||
@ -22,6 +20,9 @@ pub mod tcp;
|
||||
/// The semver API.
|
||||
pub mod version;
|
||||
|
||||
/// The Tauri config definition.
|
||||
pub use tauri_utils::config;
|
||||
|
||||
/// The CLI args interface.
|
||||
#[cfg(feature = "cli")]
|
||||
pub mod cli;
|
||||
@ -61,3 +62,15 @@ pub enum Error {
|
||||
#[error("Network Error:{0}")]
|
||||
Network(attohttpc::StatusCode),
|
||||
}
|
||||
|
||||
// Not public API
|
||||
#[doc(hidden)]
|
||||
pub mod private {
|
||||
pub trait AsTauriContext {
|
||||
fn config_path() -> &'static std::path::Path;
|
||||
fn raw_config() -> &'static str;
|
||||
fn assets() -> &'static crate::assets::Assets;
|
||||
fn raw_index() -> &'static str;
|
||||
fn raw_tauri_script() -> &'static str;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
#[cfg(windows)]
|
||||
use crate::config::get as get_config;
|
||||
#[cfg(windows)]
|
||||
use std::path::MAIN_SEPARATOR;
|
||||
|
||||
/// The Notification definition.
|
||||
@ -10,7 +8,7 @@ use std::path::MAIN_SEPARATOR;
|
||||
/// ```
|
||||
/// use tauri_api::notification::Notification;
|
||||
/// // shows a notification with the given title and body
|
||||
/// Notification::new()
|
||||
/// Notification::new("studio.tauri.example")
|
||||
/// .title("New message")
|
||||
/// .body("You've got a new message.")
|
||||
/// .show();
|
||||
@ -24,12 +22,17 @@ pub struct Notification {
|
||||
title: Option<String>,
|
||||
/// The notification icon.
|
||||
icon: Option<String>,
|
||||
/// The notification identifier
|
||||
identifier: String,
|
||||
}
|
||||
|
||||
impl Notification {
|
||||
/// Initializes a instance of a Notification.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
pub fn new(identifier: impl Into<String>) -> Self {
|
||||
Self {
|
||||
identifier: identifier.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the notification body.
|
||||
@ -71,9 +74,7 @@ impl Notification {
|
||||
if !(curr_dir.ends_with(format!("{S}target{S}debug", S = MAIN_SEPARATOR).as_str())
|
||||
|| curr_dir.ends_with(format!("{S}target{S}release", S = MAIN_SEPARATOR).as_str()))
|
||||
{
|
||||
let config = get_config()?;
|
||||
let identifier = config.tauri.bundle.identifier.clone();
|
||||
notification.app_id(&identifier);
|
||||
notification.app_id(&self.identifier);
|
||||
}
|
||||
}
|
||||
notification
|
||||
|
||||
23
tauri-macros/Cargo.toml
Normal file
23
tauri-macros/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "tauri-macros"
|
||||
version = "0.1.0"
|
||||
authors = [ "Tauri Community" ]
|
||||
categories = [ "gui", "os", "filesystem", "web-programming" ]
|
||||
license = "MIT"
|
||||
homepage = "https://tauri.studio"
|
||||
repository = "https://github.com/tauri-apps/tauri"
|
||||
description = "Macros for the tauri crate."
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
flate2 = "1"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
syn = { version = "1", features = ["extra-traits"] }
|
||||
tauri-utils = { version = "0.5", path = "../tauri-utils" }
|
||||
walkdir = "2"
|
||||
75
tauri-macros/src/error.rs
Normal file
75
tauri-macros/src/error.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
use std::io::Error as IoError;
|
||||
use std::path::PathBuf;
|
||||
use Error::*;
|
||||
|
||||
pub(crate) enum Error {
|
||||
EnvOutDir,
|
||||
EnvCargoManifestDir,
|
||||
IncludeDirPrefix,
|
||||
IncludeDirCacheDir,
|
||||
IncludeDirEmptyFilename,
|
||||
ConfigDir,
|
||||
Serde(PathBuf, serde_json::Error),
|
||||
Io(PathBuf, IoError),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Output a compiler error to the ast being transformed
|
||||
pub(crate) fn into_compile_error(self, struct_: &Ident) -> TokenStream {
|
||||
let error: String = match self {
|
||||
EnvOutDir => "Unable to find OUT_DIR environmental variable from tauri-macros".into(),
|
||||
EnvCargoManifestDir => {
|
||||
"Unable to find CARGO_MANIFEST_DIR environmental variable from tauri-macros".into()
|
||||
}
|
||||
IncludeDirPrefix => "Invalid directory prefix encountered while including assets".into(),
|
||||
IncludeDirCacheDir => {
|
||||
"Unable to find cache directory to compress assets into during tauri-macros".into()
|
||||
}
|
||||
IncludeDirEmptyFilename => "Asset included during tauri-macros has empty filename".into(),
|
||||
ConfigDir => {
|
||||
"Unable to get the directory the config file was found in during tauri-macros".into()
|
||||
}
|
||||
Serde(path, error) => format!(
|
||||
"{:?} encountered for {} during tauri-macros",
|
||||
error,
|
||||
path.display()
|
||||
),
|
||||
Io(path, error) => format!(
|
||||
"{:?} encountered for {} during tauri-macros",
|
||||
error.kind(),
|
||||
path.display()
|
||||
),
|
||||
};
|
||||
|
||||
quote! {
|
||||
compile_error!(#error);
|
||||
|
||||
impl ::tauri::api::private::AsTauriContext for #struct_ {
|
||||
fn config_path() -> &'static std::path::Path {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Make the file a dependency for the compiler
|
||||
fn raw_config() -> &'static str {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn assets() -> &'static ::tauri::api::assets::Assets {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Make the index.tauri.html a dependency for the compiler
|
||||
fn raw_index() -> &'static str {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Make the __tauri.js a dependency for the compiler
|
||||
fn raw_tauri_script() -> &'static str {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
120
tauri-macros/src/expand.rs
Normal file
120
tauri-macros/src/expand.rs
Normal file
@ -0,0 +1,120 @@
|
||||
use crate::error::Error;
|
||||
use crate::include_dir::IncludeDir;
|
||||
use crate::DEFAULT_CONFIG_FILE;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::collections::HashSet;
|
||||
use std::env::var;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use syn::{DeriveInput, Lit::Str, Meta::NameValue, MetaNameValue};
|
||||
use tauri_utils::{assets::AssetCompression, config::Config};
|
||||
|
||||
pub(crate) fn load_context(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
let name = input.ident;
|
||||
|
||||
// quick way of parsing #[config_path = "path_goes_here"]
|
||||
let mut config_file_path = DEFAULT_CONFIG_FILE.into();
|
||||
let config_path_attr = input
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|attr| attr.path.is_ident("config_path"));
|
||||
if let Some(attr) = config_path_attr {
|
||||
if let Ok(meta) = attr.parse_meta() {
|
||||
if let NameValue(MetaNameValue { lit: Str(path), .. }) = meta {
|
||||
config_file_path = path.value()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// grab the manifest of the application the macro is in
|
||||
let manifest = var("CARGO_MANIFEST_DIR")
|
||||
.map(PathBuf::from)
|
||||
.map_err(|_| Error::EnvCargoManifestDir)?;
|
||||
|
||||
let full_config_path = Path::new(&manifest).join(config_file_path);
|
||||
let config = get_config(&full_config_path)?;
|
||||
let config_dir = full_config_path.parent().ok_or(Error::ConfigDir)?;
|
||||
let dist_dir = config_dir.join(config.build.dist_dir);
|
||||
|
||||
// generate the assets into a perfect hash function
|
||||
let assets = generate_asset_map(&dist_dir)?;
|
||||
|
||||
// should be possible to do the index.tauri.hmtl manipulations during this macro too in the future
|
||||
let tauri_index_html_path = dist_dir.join("index.tauri.html");
|
||||
let tauri_script_path = dist_dir.join("__tauri.js");
|
||||
|
||||
// format paths into a string to use them in quote!
|
||||
let tauri_config_path = full_config_path.display().to_string();
|
||||
let tauri_index_html_path = tauri_index_html_path.display().to_string();
|
||||
let tauri_script_path = tauri_script_path.display().to_string();
|
||||
|
||||
Ok(quote! {
|
||||
impl ::tauri::api::private::AsTauriContext for #name {
|
||||
fn config_path() -> &'static std::path::Path {
|
||||
std::path::Path::new(#tauri_config_path)
|
||||
}
|
||||
|
||||
/// Make the file a dependency for the compiler
|
||||
fn raw_config() -> &'static str {
|
||||
include_str!(#tauri_config_path)
|
||||
}
|
||||
|
||||
fn assets() -> &'static ::tauri::api::assets::Assets {
|
||||
use ::tauri::api::assets::{Assets, AssetCompression, phf, phf::phf_map};
|
||||
static ASSETS: Assets = Assets::new(#assets);
|
||||
&ASSETS
|
||||
}
|
||||
|
||||
/// Make the index.tauri.html a dependency for the compiler
|
||||
fn raw_index() -> &'static str {
|
||||
include_str!(#tauri_index_html_path)
|
||||
}
|
||||
|
||||
/// Make the __tauri.js a dependency for the compiler
|
||||
fn raw_tauri_script() -> &'static str {
|
||||
include_str!(#tauri_script_path)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_config(path: &Path) -> Result<Config, Error> {
|
||||
match var("TAURI_CONFIG") {
|
||||
Ok(custom_config) => {
|
||||
serde_json::from_str(&custom_config).map_err(|e| Error::Serde("TAURI_CONFIG".into(), e))
|
||||
}
|
||||
Err(_) => {
|
||||
let file = File::open(&path).map_err(|e| Error::Io(path.into(), e))?;
|
||||
let reader = BufReader::new(file);
|
||||
serde_json::from_reader(reader).map_err(|e| Error::Serde(path.into(), e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a perfect hash function from `phf` of the assets in dist directory
|
||||
///
|
||||
/// The `TokenStream` produced by this function expects to have `phf` and
|
||||
/// `phf_map` paths available. Make sure to `use` these so the macro has access to them.
|
||||
/// It also expects `AssetCompression` to be in path.
|
||||
fn generate_asset_map(dist: &Path) -> Result<TokenStream, Error> {
|
||||
let mut inline_assets = HashSet::new();
|
||||
if let Ok(assets) = std::env::var("TAURI_INLINED_ASSETS") {
|
||||
assets
|
||||
.split('|')
|
||||
.filter(|&s| !s.trim().is_empty())
|
||||
.map(PathBuf::from)
|
||||
.for_each(|path| {
|
||||
inline_assets.insert(path);
|
||||
})
|
||||
}
|
||||
|
||||
// the index.html is parsed so we always ignore it
|
||||
inline_assets.insert("/index.html".into());
|
||||
|
||||
IncludeDir::new(&dist)
|
||||
.dir(&dist, AssetCompression::Gzip)?
|
||||
.set_filter(inline_assets)?
|
||||
.build()
|
||||
}
|
||||
164
tauri-macros/src/include_dir.rs
Normal file
164
tauri-macros/src/include_dir.rs
Normal file
@ -0,0 +1,164 @@
|
||||
use crate::error::Error;
|
||||
use flate2::bufread::GzEncoder;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::TokenStreamExt;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::env::var;
|
||||
use std::fs::{canonicalize, create_dir_all, File};
|
||||
use std::io::{BufReader, BufWriter};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tauri_utils::assets::{AssetCompression, Assets};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
enum Asset {
|
||||
Identity(PathBuf),
|
||||
Compressed(PathBuf, PathBuf),
|
||||
}
|
||||
|
||||
pub(crate) struct IncludeDir {
|
||||
assets: HashMap<String, Asset>,
|
||||
filter: HashSet<String>,
|
||||
prefix: PathBuf,
|
||||
}
|
||||
|
||||
impl IncludeDir {
|
||||
pub fn new(prefix: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
assets: HashMap::new(),
|
||||
filter: HashSet::new(),
|
||||
prefix: prefix.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// get a relative path based on the `IncludeDir`'s prefix
|
||||
fn relative<'p>(&self, path: &'p Path) -> Result<&'p Path, Error> {
|
||||
path
|
||||
.strip_prefix(&self.prefix)
|
||||
.map_err(|_| Error::IncludeDirPrefix)
|
||||
}
|
||||
|
||||
pub fn file(mut self, path: impl Into<PathBuf>, comp: AssetCompression) -> Result<Self, Error> {
|
||||
let path = path.into();
|
||||
let relative = self.relative(&path)?;
|
||||
let key = Assets::format_key(&relative);
|
||||
|
||||
let asset = match comp {
|
||||
AssetCompression::None => Asset::Identity(path),
|
||||
AssetCompression::Gzip => {
|
||||
let cache = var("OUT_DIR")
|
||||
.map_err(|_| Error::EnvOutDir)
|
||||
.and_then(|out| canonicalize(&out).map_err(|e| Error::Io(PathBuf::from(out), e)))
|
||||
.map(|out| out.join(".tauri-assets"))?;
|
||||
|
||||
// normalize path separators
|
||||
let relative: PathBuf = relative.components().collect();
|
||||
let cache = cache.join(relative);
|
||||
|
||||
// append .br extension to filename
|
||||
let filename = cache.file_name().ok_or(Error::IncludeDirEmptyFilename)?;
|
||||
let filename = format!("{}.br", filename.to_string_lossy());
|
||||
|
||||
// remove filename from cache
|
||||
let cache = cache.parent().ok_or(Error::IncludeDirCacheDir)?;
|
||||
|
||||
// append the filename to the canonical path
|
||||
let cache_file = cache.join(filename);
|
||||
|
||||
// make sure the cache directory is created
|
||||
create_dir_all(&cache).map_err(|e| Error::Io(cache.to_path_buf(), e))?;
|
||||
|
||||
// open original asset path
|
||||
let reader = File::open(&path).map_err(|e| Error::Io(path.to_path_buf(), e))?;
|
||||
let reader = BufReader::new(reader);
|
||||
let mut reader = GzEncoder::new(reader, flate2::Compression::best());
|
||||
|
||||
// open cache path
|
||||
let writer =
|
||||
File::create(&cache_file).map_err(|e| Error::Io(cache_file.to_path_buf(), e))?;
|
||||
let mut writer = BufWriter::new(writer);
|
||||
|
||||
std::io::copy(&mut reader, &mut writer).map_err(|e| Error::Io(path.to_path_buf(), e))?;
|
||||
|
||||
Asset::Compressed(path, cache_file)
|
||||
}
|
||||
};
|
||||
|
||||
self.assets.insert(key, asset);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn dir(mut self, path: impl AsRef<Path>, comp: AssetCompression) -> Result<Self, Error> {
|
||||
let path = path.as_ref();
|
||||
let walker = WalkDir::new(&path).follow_links(true);
|
||||
for entry in walker.into_iter() {
|
||||
match entry {
|
||||
Ok(e) => {
|
||||
if !e.file_type().is_dir() {
|
||||
self = self.file(e.path(), comp)?
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(Error::Io(path.into(), e.into())),
|
||||
}
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Set list of files to not embed. Paths should be relative to the dist dir
|
||||
pub fn set_filter(mut self, filter: HashSet<PathBuf>) -> Result<Self, Error> {
|
||||
self.filter = filter
|
||||
.iter()
|
||||
.map(|path| {
|
||||
let path = if path.starts_with(&self.prefix) {
|
||||
self.relative(path)?
|
||||
} else {
|
||||
&path
|
||||
};
|
||||
Ok(Assets::format_key(path))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<TokenStream, Error> {
|
||||
let mut matches = TokenStream::new();
|
||||
for (key, asset) in self.assets {
|
||||
if self.filter.contains(&key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = match asset {
|
||||
Asset::Identity(path) => {
|
||||
let path = path.display().to_string();
|
||||
quote! {
|
||||
(AssetCompression::None, include_bytes!(#path))
|
||||
}
|
||||
}
|
||||
Asset::Compressed(path, cache) => {
|
||||
let path = path.display().to_string();
|
||||
let cache = cache.display().to_string();
|
||||
quote! {
|
||||
{
|
||||
// make compiler check asset file for re-run.
|
||||
// rely on dead code elimination to remove it from target binary
|
||||
const _: &[u8] = include_bytes!(#path);
|
||||
|
||||
(AssetCompression::Gzip, include_bytes!(#cache))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
matches.append_all(quote! {
|
||||
#key => #value,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
phf_map! {
|
||||
#matches
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
19
tauri-macros/src/lib.rs
Normal file
19
tauri-macros/src/lib.rs
Normal file
@ -0,0 +1,19 @@
|
||||
extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
mod error;
|
||||
mod expand;
|
||||
mod include_dir;
|
||||
|
||||
const DEFAULT_CONFIG_FILE: &str = "tauri.conf.json";
|
||||
|
||||
#[proc_macro_derive(FromTauriContext, attributes(config_path))]
|
||||
pub fn load_context(ast: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(ast as DeriveInput);
|
||||
let name = input.ident.clone();
|
||||
|
||||
expand::load_context(input)
|
||||
.unwrap_or_else(|e| e.into_compile_error(&name))
|
||||
.into()
|
||||
}
|
||||
@ -8,8 +8,11 @@ repository = "https://github.com/tauri-apps/tauri"
|
||||
description = "Utilities for Tauri"
|
||||
edition = "2018"
|
||||
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
sysinfo = "0.10"
|
||||
anyhow = "1.0.31"
|
||||
thiserror = "1.0.19"
|
||||
thiserror = "1.0.19"
|
||||
phf = { version = "0.8", features = ["macros"] }
|
||||
flate2 = "1"
|
||||
|
||||
108
tauri-utils/src/assets.rs
Normal file
108
tauri-utils/src/assets.rs
Normal file
@ -0,0 +1,108 @@
|
||||
//! Assets handled by Tauri during compile time and runtime.
|
||||
|
||||
use flate2::read::{GzDecoder, GzEncoder};
|
||||
pub use phf;
|
||||
use std::io::Read;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
/// Type of compression applied to an asset
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum AssetCompression {
|
||||
/// No compression applied
|
||||
None,
|
||||
|
||||
/// Compressed with (gzip)[https://crates.io/crates/flate2]
|
||||
Gzip,
|
||||
}
|
||||
|
||||
/// How the embedded asset should be fetched from `Assets`
|
||||
pub enum AssetFetch {
|
||||
/// Do not modify the compression
|
||||
Identity,
|
||||
|
||||
/// Ensure asset is decompressed
|
||||
Decompress,
|
||||
|
||||
/// Ensure asset is compressed
|
||||
Compress,
|
||||
}
|
||||
|
||||
/// Runtime access to the included files
|
||||
pub struct Assets {
|
||||
inner: phf::Map<&'static str, (AssetCompression, &'static [u8])>,
|
||||
}
|
||||
|
||||
impl Assets {
|
||||
/// Create `Assets` container from `phf::Map`
|
||||
pub const fn new(map: phf::Map<&'static str, (AssetCompression, &'static [u8])>) -> Self {
|
||||
Self { inner: map }
|
||||
}
|
||||
|
||||
/// Format a key used to identify a file embedded in `Assets`.
|
||||
///
|
||||
/// Output should use unix path separators and have a root directory to mimic
|
||||
/// server urls.
|
||||
pub fn format_key(path: impl Into<PathBuf>) -> String {
|
||||
let path = path.into();
|
||||
|
||||
// add in root to mimic how it is used from a server url
|
||||
let path = if path.has_root() {
|
||||
path
|
||||
} else {
|
||||
Path::new(&Component::RootDir).join(path)
|
||||
};
|
||||
|
||||
if cfg!(windows) {
|
||||
let mut buf = String::new();
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::RootDir => buf.push('/'),
|
||||
Component::CurDir => buf.push_str("./"),
|
||||
Component::ParentDir => buf.push_str("../"),
|
||||
Component::Prefix(prefix) => buf.push_str(&prefix.as_os_str().to_string_lossy()),
|
||||
Component::Normal(s) => {
|
||||
buf.push_str(&s.to_string_lossy());
|
||||
buf.push('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove the last slash
|
||||
if buf != "/" {
|
||||
buf.pop();
|
||||
}
|
||||
|
||||
buf
|
||||
} else {
|
||||
path.to_string_lossy().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get embedded asset, automatically handling compression.
|
||||
pub fn get(
|
||||
&self,
|
||||
path: impl Into<PathBuf>,
|
||||
fetch: AssetFetch,
|
||||
) -> Option<(Box<dyn Read>, AssetCompression)> {
|
||||
use self::{AssetCompression::*, AssetFetch::*};
|
||||
|
||||
let key = Self::format_key(path);
|
||||
let &(compression, content) = self.inner.get(&*key)?;
|
||||
Some(match (compression, fetch) {
|
||||
// content is already in compression format expected
|
||||
(_, Identity) | (None, Decompress) | (Gzip, Compress) => (Box::new(content), compression),
|
||||
|
||||
// content is uncompressed, but fetched with compression
|
||||
(None, Compress) => {
|
||||
let compressor = GzEncoder::new(content, flate2::Compression::new(6));
|
||||
(Box::new(compressor), Gzip)
|
||||
}
|
||||
|
||||
// content is compressed, but fetched with decompression
|
||||
(Gzip, Decompress) => {
|
||||
let decompressor = GzDecoder::new(content);
|
||||
(Box::new(decompressor), None)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,8 @@ use serde::de::{Deserializer, Error as DeError, Visitor};
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
static CONFIG: OnceCell<Config> = OnceCell::new();
|
||||
|
||||
/// The window configuration object.
|
||||
#[derive(PartialEq, Deserialize, Debug)]
|
||||
#[serde(tag = "window", rename_all = "camelCase")]
|
||||
@ -44,13 +41,15 @@ fn default_title() -> String {
|
||||
"Tauri App".to_string()
|
||||
}
|
||||
|
||||
fn default_window() -> WindowConfig {
|
||||
WindowConfig {
|
||||
width: default_width(),
|
||||
height: default_height(),
|
||||
resizable: default_resizable(),
|
||||
title: default_title(),
|
||||
fullscreen: false,
|
||||
impl Default for WindowConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: default_width(),
|
||||
height: default_height(),
|
||||
resizable: default_resizable(),
|
||||
title: default_title(),
|
||||
fullscreen: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,12 +73,38 @@ pub struct EmbeddedServerConfig {
|
||||
/// If it's `random`, we'll generate one at runtime.
|
||||
#[serde(default = "default_port", deserialize_with = "port_deserializer")]
|
||||
pub port: Port,
|
||||
|
||||
/// The base path of the embedded server.
|
||||
/// The path should always start and end in a forward slash, which the deserializer will ensure
|
||||
#[serde(
|
||||
default = "default_public_path",
|
||||
deserialize_with = "public_path_deserializer"
|
||||
)]
|
||||
pub public_path: String,
|
||||
}
|
||||
|
||||
fn default_host() -> String {
|
||||
"http://127.0.0.1".to_string()
|
||||
}
|
||||
|
||||
fn default_port() -> Port {
|
||||
Port::Random
|
||||
}
|
||||
|
||||
fn default_public_path() -> String {
|
||||
"/".to_string()
|
||||
}
|
||||
|
||||
impl Default for EmbeddedServerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
host: default_host(),
|
||||
port: default_port(),
|
||||
public_path: default_public_path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn port_deserializer<'de, D>(deserializer: D) -> Result<Port, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
@ -116,15 +141,45 @@ where
|
||||
deserializer.deserialize_any(PortDeserializer {})
|
||||
}
|
||||
|
||||
fn default_port() -> Port {
|
||||
Port::Random
|
||||
}
|
||||
fn public_path_deserializer<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct PublicPathDeserializer;
|
||||
|
||||
fn default_embedded_server() -> EmbeddedServerConfig {
|
||||
EmbeddedServerConfig {
|
||||
host: default_host(),
|
||||
port: default_port(),
|
||||
impl<'de> Visitor<'de> for PublicPathDeserializer {
|
||||
type Value = String;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
formatter.write_str("a string starting and ending in a forward slash /")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: DeError,
|
||||
{
|
||||
match value.len() {
|
||||
0 => return Ok("/".into()),
|
||||
1 if value == "/" => return Ok("/".into()),
|
||||
1 => return Ok(format!("/{}/", value)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// we know there are at least 2 characters in the string
|
||||
let mut chars = value.chars();
|
||||
let first = chars.next().unwrap();
|
||||
let last = chars.last().unwrap();
|
||||
|
||||
match (first == '/', last == '/') {
|
||||
(true, true) => Ok(value.into()),
|
||||
(true, false) => Ok(format!("{}/", value)),
|
||||
(false, true) => Ok(format!("/{}", value)),
|
||||
_ => Ok(format!("/{}/", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(PublicPathDeserializer {})
|
||||
}
|
||||
|
||||
/// A CLI argument definition
|
||||
@ -270,9 +325,11 @@ pub struct BundleConfig {
|
||||
pub identifier: String,
|
||||
}
|
||||
|
||||
fn default_bundle() -> BundleConfig {
|
||||
BundleConfig {
|
||||
identifier: String::from(""),
|
||||
impl Default for BundleConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
identifier: String::from(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,19 +338,30 @@ fn default_bundle() -> BundleConfig {
|
||||
#[serde(tag = "tauri", rename_all = "camelCase")]
|
||||
pub struct TauriConfig {
|
||||
/// The window configuration.
|
||||
#[serde(default = "default_window")]
|
||||
#[serde(default)]
|
||||
pub window: WindowConfig,
|
||||
/// The embeddedServer configuration.
|
||||
#[serde(default = "default_embedded_server")]
|
||||
#[serde(default)]
|
||||
pub embedded_server: EmbeddedServerConfig,
|
||||
/// The CLI configuration.
|
||||
#[serde(default)]
|
||||
pub cli: Option<CliConfig>,
|
||||
/// The bundler configuration.
|
||||
#[serde(default = "default_bundle")]
|
||||
#[serde(default)]
|
||||
pub bundle: BundleConfig,
|
||||
}
|
||||
|
||||
impl Default for TauriConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
window: WindowConfig::default(),
|
||||
embedded_server: EmbeddedServerConfig::default(),
|
||||
cli: None,
|
||||
bundle: BundleConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The Build configuration object.
|
||||
#[derive(PartialEq, Deserialize, Debug)]
|
||||
#[serde(tag = "build", rename_all = "camelCase")]
|
||||
@ -301,11 +369,26 @@ pub struct BuildConfig {
|
||||
/// the devPath config.
|
||||
#[serde(default = "default_dev_path")]
|
||||
pub dev_path: String,
|
||||
/// the dist config.
|
||||
#[serde(default = "default_dist_path")]
|
||||
pub dist_dir: String,
|
||||
}
|
||||
|
||||
fn default_dev_path() -> String {
|
||||
"".to_string()
|
||||
}
|
||||
fn default_dist_path() -> String {
|
||||
"../dist".to_string()
|
||||
}
|
||||
|
||||
impl Default for BuildConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dev_path: default_dev_path(),
|
||||
dist_dir: default_dist_path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type JsonObject = HashMap<String, JsonValue>;
|
||||
|
||||
@ -314,10 +397,10 @@ type JsonObject = HashMap<String, JsonValue>;
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Config {
|
||||
/// The Tauri configuration.
|
||||
#[serde(default = "default_tauri")]
|
||||
#[serde(default)]
|
||||
pub tauri: TauriConfig,
|
||||
/// The build configuration.
|
||||
#[serde(default = "default_build")]
|
||||
#[serde(default)]
|
||||
pub build: BuildConfig,
|
||||
/// The plugins config.
|
||||
#[serde(default)]
|
||||
@ -331,160 +414,29 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn default_tauri() -> TauriConfig {
|
||||
TauriConfig {
|
||||
window: default_window(),
|
||||
embedded_server: default_embedded_server(),
|
||||
cli: None,
|
||||
bundle: default_bundle(),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_build() -> BuildConfig {
|
||||
BuildConfig {
|
||||
dev_path: default_dev_path(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the static parsed config from `tauri.conf.json`.
|
||||
pub fn get() -> crate::Result<&'static Config> {
|
||||
if let Some(config) = CONFIG.get() {
|
||||
return Ok(config);
|
||||
}
|
||||
let config: Config = match option_env!("TAURI_CONFIG") {
|
||||
Some(config) => serde_json::from_str(config).expect("failed to parse TAURI_CONFIG env"),
|
||||
None => {
|
||||
let config = include_str!(concat!(env!("OUT_DIR"), "/tauri.conf.json"));
|
||||
serde_json::from_str(&config).expect("failed to read tauri.conf.json")
|
||||
}
|
||||
};
|
||||
|
||||
CONFIG
|
||||
.set(config)
|
||||
.map_err(|_| anyhow::anyhow!("failed to set CONFIG"))?;
|
||||
|
||||
let config = CONFIG.get().unwrap();
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
// generate a test_config based on the test fixture
|
||||
fn create_test_config() -> Config {
|
||||
let mut subcommands = std::collections::HashMap::new();
|
||||
subcommands.insert(
|
||||
"update".to_string(),
|
||||
CliConfig {
|
||||
description: Some("Updates the app".to_string()),
|
||||
long_description: None,
|
||||
before_help: None,
|
||||
after_help: None,
|
||||
args: Some(vec![CliArg {
|
||||
short: Some('b'),
|
||||
name: "background".to_string(),
|
||||
description: Some("Update in background".to_string()),
|
||||
..Default::default()
|
||||
}]),
|
||||
subcommands: None,
|
||||
},
|
||||
);
|
||||
Config {
|
||||
tauri: TauriConfig {
|
||||
window: WindowConfig {
|
||||
width: 800,
|
||||
height: 600,
|
||||
resizable: true,
|
||||
title: String::from("Tauri API Validation"),
|
||||
fullscreen: false,
|
||||
},
|
||||
embedded_server: EmbeddedServerConfig {
|
||||
host: String::from("http://127.0.0.1"),
|
||||
port: Port::Random,
|
||||
},
|
||||
bundle: BundleConfig {
|
||||
identifier: String::from("com.tauri.communication"),
|
||||
},
|
||||
cli: Some(CliConfig {
|
||||
description: Some("Tauri communication example".to_string()),
|
||||
long_description: None,
|
||||
before_help: None,
|
||||
after_help: None,
|
||||
args: Some(vec![
|
||||
CliArg {
|
||||
short: Some('c'),
|
||||
name: "config".to_string(),
|
||||
takes_value: Some(true),
|
||||
description: Some("Config path".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
CliArg {
|
||||
short: Some('t'),
|
||||
name: "theme".to_string(),
|
||||
takes_value: Some(true),
|
||||
description: Some("App theme".to_string()),
|
||||
possible_values: Some(vec![
|
||||
"light".to_string(),
|
||||
"dark".to_string(),
|
||||
"system".to_string(),
|
||||
]),
|
||||
..Default::default()
|
||||
},
|
||||
CliArg {
|
||||
short: Some('v'),
|
||||
name: "verbose".to_string(),
|
||||
multiple_occurrences: Some(true),
|
||||
description: Some("Verbosity level".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
]),
|
||||
subcommands: Some(subcommands),
|
||||
}),
|
||||
},
|
||||
build: BuildConfig {
|
||||
dev_path: String::from("../dist"),
|
||||
},
|
||||
plugins: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// test the get function. Will only resolve to true if the TAURI_CONFIG variable is set properly to the fixture.
|
||||
fn test_get() {
|
||||
// get test_config
|
||||
let test_config = create_test_config();
|
||||
|
||||
// call get();
|
||||
let config = get();
|
||||
|
||||
// check to see if there is an OK or Err, on Err fail test.
|
||||
match config {
|
||||
// On Ok, check that the config is the same as the test config.
|
||||
Ok(c) => {
|
||||
println!("{:?}", c);
|
||||
assert_eq!(c, &test_config)
|
||||
}
|
||||
Err(e) => panic!("get config failed: {:?}", e.to_string()),
|
||||
}
|
||||
}
|
||||
// TODO: create a test that compares a config to a json config
|
||||
|
||||
#[test]
|
||||
// test all of the default functions
|
||||
fn test_defaults() {
|
||||
// get default tauri config
|
||||
let t_config = default_tauri();
|
||||
let t_config = TauriConfig::default();
|
||||
// get default build config
|
||||
let b_config = default_build();
|
||||
let b_config = BuildConfig::default();
|
||||
// get default dev path
|
||||
let d_path = default_dev_path();
|
||||
// get default embedded server
|
||||
let de_server = default_embedded_server();
|
||||
let de_server = EmbeddedServerConfig::default();
|
||||
// get default window
|
||||
let d_window = default_window();
|
||||
let d_window = WindowConfig::default();
|
||||
// get default title
|
||||
let d_title = default_title();
|
||||
// get default bundle
|
||||
let d_bundle = default_bundle();
|
||||
let d_bundle = BundleConfig::default();
|
||||
|
||||
// create a tauri config.
|
||||
let tauri = TauriConfig {
|
||||
@ -498,6 +450,7 @@ mod test {
|
||||
embedded_server: EmbeddedServerConfig {
|
||||
host: String::from("http://127.0.0.1"),
|
||||
port: Port::Random,
|
||||
public_path: "/".into(),
|
||||
},
|
||||
bundle: BundleConfig {
|
||||
identifier: String::from(""),
|
||||
@ -508,6 +461,7 @@ mod test {
|
||||
// create a build config
|
||||
let build = BuildConfig {
|
||||
dev_path: String::from(""),
|
||||
dist_dir: String::from("../dist"),
|
||||
};
|
||||
|
||||
// test the configs
|
||||
@ -1,6 +1,10 @@
|
||||
//! Tauri utility helpers
|
||||
#![warn(missing_docs, rust_2018_idioms)]
|
||||
|
||||
/// The Assets module allows you to read files that have been bundled by tauri
|
||||
pub mod assets;
|
||||
/// Tauri config definition.
|
||||
pub mod config;
|
||||
/// Platform helpers
|
||||
pub mod platform;
|
||||
/// Process helpers
|
||||
|
||||
@ -20,8 +20,6 @@ features = [ "all-api" ]
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
tauri_includedir = "0.6.0"
|
||||
phf = "0.8.0"
|
||||
base64 = "0.13.0"
|
||||
webbrowser = "0.5.5"
|
||||
lazy_static = "1.4.0"
|
||||
@ -34,6 +32,7 @@ anyhow = "1.0.38"
|
||||
thiserror = "1.0.23"
|
||||
once_cell = "1.5.2"
|
||||
tauri-api = { version = "0.7.5", path = "../tauri-api" }
|
||||
tauri-macros = { version = "0.1", path = "../tauri-macros" }
|
||||
urlencoding = "1.1.1"
|
||||
wry = { git = "https://github.com/tauri-apps/wry", rev = "42f4f2133f7921ed5adc47908787094da8abeac5" }
|
||||
|
||||
@ -41,7 +40,6 @@ wry = { git = "https://github.com/tauri-apps/wry", rev = "42f4f2133f7921ed5adc47
|
||||
runas = "0.2"
|
||||
|
||||
[build-dependencies]
|
||||
tauri_includedir_codegen = "0.6.2"
|
||||
cfg_aliases = "0.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
122
tauri/build.rs
122
tauri/build.rs
@ -1,126 +1,6 @@
|
||||
use cfg_aliases::cfg_aliases;
|
||||
|
||||
use std::{
|
||||
env,
|
||||
error::Error,
|
||||
fs::{read_to_string, File},
|
||||
io::{BufWriter, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "embedded-server", feature = "no-server"))]
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
shared()?;
|
||||
|
||||
let out_dir = env::var("OUT_DIR")?;
|
||||
|
||||
let dest_index_html_path = Path::new(&out_dir).join("index.tauri.html");
|
||||
let mut index_html_file = BufWriter::new(File::create(&dest_index_html_path)?);
|
||||
|
||||
match env::var_os("TAURI_DIST_DIR") {
|
||||
Some(dist_path) => {
|
||||
let dist_path_string = dist_path.into_string().unwrap();
|
||||
let dist_path = Path::new(&dist_path_string);
|
||||
|
||||
println!("cargo:rerun-if-changed={}", dist_path_string);
|
||||
|
||||
let mut inlined_assets = match std::env::var_os("TAURI_INLINED_ASSETS") {
|
||||
Some(assets) => assets
|
||||
.into_string()
|
||||
.unwrap()
|
||||
.split('|')
|
||||
.map(|s| s.to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
|
||||
// the index.html is parsed so we always ignore it
|
||||
inlined_assets.push(
|
||||
dist_path
|
||||
.join("index.html")
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("failed to convert dist path to string"),
|
||||
);
|
||||
if cfg!(feature = "no-server") {
|
||||
// on no-server we include_str() the index.tauri.html on the runner
|
||||
inlined_assets.push(
|
||||
dist_path
|
||||
.join("index.tauri.html")
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("failed to convert dist path to string"),
|
||||
);
|
||||
}
|
||||
|
||||
// include assets
|
||||
tauri_includedir_codegen::start("ASSETS")
|
||||
.dir(
|
||||
dist_path_string.clone(),
|
||||
tauri_includedir_codegen::Compression::None,
|
||||
)
|
||||
.build("data.rs", inlined_assets)
|
||||
.expect("failed to build data.rs");
|
||||
|
||||
write!(
|
||||
index_html_file,
|
||||
"{}",
|
||||
read_to_string(dist_path.join("index.tauri.html"))?
|
||||
)?;
|
||||
}
|
||||
None => {
|
||||
// dummy assets
|
||||
tauri_includedir_codegen::start("ASSETS")
|
||||
.dir("".to_string(), tauri_includedir_codegen::Compression::None)
|
||||
.build("data.rs", vec![])
|
||||
.expect("failed to build data.rs");
|
||||
write!(
|
||||
index_html_file,
|
||||
"<html><body>Build error: Couldn't find ENV: TAURI_DIST_DIR</body></html>"
|
||||
)?;
|
||||
println!("Build error: Couldn't find ENV: TAURI_DIST_DIR");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "embedded-server", feature = "no-server")))]
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
shared()
|
||||
}
|
||||
|
||||
fn shared() -> Result<(), Box<dyn Error>> {
|
||||
let out_dir = env::var("OUT_DIR")?;
|
||||
let dest_tauri_script_path = Path::new(&out_dir).join("__tauri.js");
|
||||
let mut tauri_script_file = BufWriter::new(File::create(&dest_tauri_script_path)?);
|
||||
|
||||
match env::var_os("TAURI_DIST_DIR") {
|
||||
Some(dist_path) => {
|
||||
let dist_path_string = dist_path.into_string().unwrap();
|
||||
let dist_path = Path::new(&dist_path_string);
|
||||
|
||||
write!(
|
||||
tauri_script_file,
|
||||
"{}",
|
||||
read_to_string(dist_path.join("__tauri.js"))?
|
||||
)?;
|
||||
}
|
||||
None => {
|
||||
write!(
|
||||
tauri_script_file,
|
||||
r#"console.warning("Couldn't find ENV: TAURI_DIST_DIR, the Tauri API won't work. Please rebuild with the Tauri CLI.")"#,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
setup_env_aliases();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn setup_env_aliases() {
|
||||
fn main() {
|
||||
cfg_aliases! {
|
||||
embedded_server: { feature = "embedded-server" },
|
||||
no_server: { feature = "no-server" },
|
||||
|
||||
66
tauri/examples/api/src-tauri/Cargo.lock
generated
66
tauri/examples/api/src-tauri/Cargo.lock
generated
@ -1717,17 +1717,9 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1740,6 +1732,20 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote 1.0.8",
|
||||
"syn 1.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -2375,13 +2381,11 @@ dependencies = [
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"phf",
|
||||
"runas",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-api",
|
||||
"tauri_includedir",
|
||||
"tauri_includedir_codegen",
|
||||
"tauri-macros",
|
||||
"thiserror",
|
||||
"tiny_http",
|
||||
"tokio",
|
||||
@ -2405,6 +2409,7 @@ dependencies = [
|
||||
"nfd",
|
||||
"notify-rust",
|
||||
"once_cell",
|
||||
"phf",
|
||||
"rand 0.8.3",
|
||||
"semver",
|
||||
"serde",
|
||||
@ -2437,6 +2442,20 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"proc-macro2",
|
||||
"quote 1.0.8",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 1.0.60",
|
||||
"tauri-api",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "0.5.1"
|
||||
@ -2446,27 +2465,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri_includedir"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7a3644510b11203f42cf6e6384d5c46e0d20e426aa6e05b9110f5e4583a1032"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri_includedir_codegen"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afccce834cf6062a1a19d2d0018fd2a5a2d13d75f547ed0dac2cc3d9813b6ae7"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"phf_codegen",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.2.0"
|
||||
|
||||
@ -12,21 +12,24 @@ struct Reply {
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
struct Context;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::<tauri::flavors::Wry>::new()
|
||||
.setup(|webview, _source| async move {
|
||||
let mut webview = webview.clone();
|
||||
tauri::AppBuilder::<tauri::flavors::Wry, Context>::new()
|
||||
.setup(|dispatcher, _source| async move {
|
||||
let mut dispatcher = dispatcher.clone();
|
||||
tauri::event::listen(String::from("js-event"), move |msg| {
|
||||
println!("got js-event with message '{:?}'", msg);
|
||||
let reply = Reply {
|
||||
data: "something else".to_string(),
|
||||
};
|
||||
|
||||
tauri::event::emit(&mut webview, String::from("rust-event"), Some(reply))
|
||||
tauri::event::emit(&mut dispatcher, String::from("rust-event"), Some(reply))
|
||||
.expect("failed to emit");
|
||||
});
|
||||
})
|
||||
.invoke_handler(|mut webview, arg| async move {
|
||||
.invoke_handler(|mut dispatcher, arg| async move {
|
||||
use cmd::Cmd::*;
|
||||
match serde_json::from_str(&arg) {
|
||||
Err(e) => Err(e.to_string()),
|
||||
@ -64,5 +67,6 @@ fn main() {
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.unwrap()
|
||||
.run();
|
||||
}
|
||||
|
||||
66
tauri/examples/communication/src-tauri/Cargo.lock
generated
66
tauri/examples/communication/src-tauri/Cargo.lock
generated
@ -1717,17 +1717,9 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1740,6 +1732,20 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote 1.0.8",
|
||||
"syn 1.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -2375,13 +2381,11 @@ dependencies = [
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"phf",
|
||||
"runas",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-api",
|
||||
"tauri_includedir",
|
||||
"tauri_includedir_codegen",
|
||||
"tauri-macros",
|
||||
"thiserror",
|
||||
"tiny_http",
|
||||
"tokio",
|
||||
@ -2405,6 +2409,7 @@ dependencies = [
|
||||
"nfd",
|
||||
"notify-rust",
|
||||
"once_cell",
|
||||
"phf",
|
||||
"rand 0.8.3",
|
||||
"semver",
|
||||
"serde",
|
||||
@ -2437,6 +2442,20 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"proc-macro2",
|
||||
"quote 1.0.8",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 1.0.60",
|
||||
"tauri-api",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "0.5.1"
|
||||
@ -2446,27 +2465,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri_includedir"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7a3644510b11203f42cf6e6384d5c46e0d20e426aa6e05b9110f5e4583a1032"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri_includedir_codegen"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afccce834cf6062a1a19d2d0018fd2a5a2d13d75f547ed0dac2cc3d9813b6ae7"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"phf_codegen",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.2.0"
|
||||
|
||||
@ -12,21 +12,25 @@ struct Reply {
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[derive(tauri::FromTauriContext)]
|
||||
#[config_path = "examples/communication/src-tauri/tauri.conf.json"]
|
||||
struct Context;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::<tauri::flavors::Wry>::new()
|
||||
.setup(|webview, _source| async move {
|
||||
let mut webview = webview.clone();
|
||||
tauri::AppBuilder::<tauri::flavors::Wry, Context>::new()
|
||||
.setup(|dispatcher, _source| async move {
|
||||
let mut dispatcher = dispatcher.clone();
|
||||
tauri::event::listen(String::from("js-event"), move |msg| {
|
||||
println!("got js-event with message '{:?}'", msg);
|
||||
let reply = Reply {
|
||||
data: "something else".to_string(),
|
||||
};
|
||||
|
||||
tauri::event::emit(&mut webview, String::from("rust-event"), Some(reply))
|
||||
tauri::event::emit(&mut dispatcher, String::from("rust-event"), Some(reply))
|
||||
.expect("failed to emit");
|
||||
});
|
||||
})
|
||||
.invoke_handler(|mut webview, arg| async move {
|
||||
.invoke_handler(|mut dispatcher, arg| async move {
|
||||
use cmd::Cmd::*;
|
||||
match serde_json::from_str(&arg) {
|
||||
Err(e) => Err(e.to_string()),
|
||||
@ -44,7 +48,7 @@ fn main() {
|
||||
// tauri::execute_promise is a helper for APIs that uses the tauri.promisified JS function
|
||||
// so you can easily communicate between JS and Rust with promises
|
||||
tauri::execute_promise(
|
||||
&mut webview,
|
||||
&mut dispatcher,
|
||||
async move {
|
||||
println!("{} {:?}", endpoint, body);
|
||||
// perform an async operation here
|
||||
@ -64,5 +68,6 @@ fn main() {
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.unwrap()
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -1,11 +1,38 @@
|
||||
use crate::ApplicationExt;
|
||||
use futures::future::BoxFuture;
|
||||
use std::marker::PhantomData;
|
||||
use tauri_api::config::Config;
|
||||
use tauri_api::private::AsTauriContext;
|
||||
|
||||
mod runner;
|
||||
|
||||
type InvokeHandler<W> = dyn Fn(W, String) -> BoxFuture<'static, Result<(), String>> + Send + Sync;
|
||||
type Setup<W> = dyn Fn(W, String) -> BoxFuture<'static, ()> + Send + Sync;
|
||||
|
||||
/// `App` runtime information.
|
||||
pub struct Context {
|
||||
pub(crate) config: Config,
|
||||
pub(crate) tauri_script: &'static str,
|
||||
#[cfg(assets)]
|
||||
pub(crate) assets: &'static tauri_api::assets::Assets,
|
||||
#[cfg(any(dev, no_server))]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) index: &'static str,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub(crate) fn new<Context: AsTauriContext>() -> crate::Result<Self> {
|
||||
Ok(Self {
|
||||
config: serde_json::from_str(Context::raw_config())?,
|
||||
tauri_script: Context::raw_tauri_script(),
|
||||
#[cfg(assets)]
|
||||
assets: Context::assets(),
|
||||
#[cfg(any(dev, no_server))]
|
||||
index: Context::raw_index(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The application runner.
|
||||
pub struct App<A: ApplicationExt> {
|
||||
/// The JS message handler.
|
||||
@ -14,6 +41,8 @@ pub struct App<A: ApplicationExt> {
|
||||
setup: Option<Box<Setup<A::Dispatcher>>>,
|
||||
/// The HTML of the splashscreen to render.
|
||||
splashscreen_html: Option<String>,
|
||||
/// The context the App was created with
|
||||
pub(crate) context: Context,
|
||||
}
|
||||
|
||||
impl<A: ApplicationExt + 'static> App<A> {
|
||||
@ -54,22 +83,25 @@ impl<A: ApplicationExt + 'static> App<A> {
|
||||
|
||||
/// The App builder.
|
||||
#[derive(Default)]
|
||||
pub struct AppBuilder<A: ApplicationExt> {
|
||||
pub struct AppBuilder<A: ApplicationExt, C: AsTauriContext> {
|
||||
/// The JS message handler.
|
||||
invoke_handler: Option<Box<InvokeHandler<A::Dispatcher>>>,
|
||||
/// The setup callback, invoked when the webview is ready.
|
||||
setup: Option<Box<Setup<A::Dispatcher>>>,
|
||||
/// The HTML of the splashscreen to render.
|
||||
splashscreen_html: Option<String>,
|
||||
/// The configuration used
|
||||
config: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<A: ApplicationExt + 'static> AppBuilder<A> {
|
||||
impl<A: ApplicationExt + 'static, C: AsTauriContext> AppBuilder<A, C> {
|
||||
/// Creates a new App builder.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
invoke_handler: None,
|
||||
setup: None,
|
||||
splashscreen_html: None,
|
||||
config: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,11 +149,12 @@ impl<A: ApplicationExt + 'static> AppBuilder<A> {
|
||||
}
|
||||
|
||||
/// Builds the App.
|
||||
pub fn build(self) -> App<A> {
|
||||
App {
|
||||
pub fn build(self) -> crate::Result<App<A>> {
|
||||
Ok(App {
|
||||
invoke_handler: self.invoke_handler,
|
||||
setup: self.setup,
|
||||
splashscreen_html: self.splashscreen_html,
|
||||
}
|
||||
context: Context::new::<C>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::{ApplicationDispatcherExt, ApplicationExt, WebviewBuilderExt, WindowB
|
||||
use super::App;
|
||||
#[cfg(embedded_server)]
|
||||
use crate::api::tcp::{get_available_port, port_is_available};
|
||||
use tauri_api::config::get;
|
||||
use crate::app::Context;
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Content<T> {
|
||||
@ -19,17 +19,21 @@ enum Content<T> {
|
||||
/// Main entry point for running the Webview
|
||||
pub(crate) fn run<A: ApplicationExt + 'static>(application: App<A>) -> crate::Result<()> {
|
||||
// setup the content using the config struct depending on the compile target
|
||||
let main_content = setup_content()?;
|
||||
let main_content = setup_content(&application.context)?;
|
||||
|
||||
// setup the server url for the embedded-server
|
||||
#[cfg(embedded_server)]
|
||||
let server_url = {
|
||||
if let Content::Url(ref url) = &main_content {
|
||||
{
|
||||
// setup the server url for the embedded-server
|
||||
let server_url = if let Content::Url(url) = &main_content {
|
||||
String::from(url)
|
||||
} else {
|
||||
String::from("")
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// spawn the embedded server on our server url
|
||||
#[cfg(embedded_server)]
|
||||
spawn_server(server_url, &application.context)?;
|
||||
}
|
||||
|
||||
let splashscreen_content = if application.splashscreen_html().is_some() {
|
||||
Some(Content::Html(
|
||||
@ -50,10 +54,6 @@ pub(crate) fn run<A: ApplicationExt + 'static>(application: App<A>) -> crate::Re
|
||||
crate::plugin::created(A::plugin_store(), &mut dispatcher).await
|
||||
});
|
||||
|
||||
// spawn the embedded server on our server url
|
||||
#[cfg(embedded_server)]
|
||||
spawn_server(server_url);
|
||||
|
||||
// spin up the updater process
|
||||
#[cfg(feature = "updater")]
|
||||
spawn_updater();
|
||||
@ -65,14 +65,14 @@ pub(crate) fn run<A: ApplicationExt + 'static>(application: App<A>) -> crate::Re
|
||||
}
|
||||
|
||||
#[cfg(all(embedded_server, no_server))]
|
||||
fn setup_content() -> crate::Result<Content<String>> {
|
||||
fn setup_content(_: &Context) -> crate::Result<Content<String>> {
|
||||
panic!("only one of `embedded-server` and `no-server` is allowed")
|
||||
}
|
||||
|
||||
// setup content for dev-server
|
||||
#[cfg(dev)]
|
||||
fn setup_content() -> crate::Result<Content<String>> {
|
||||
let config = get()?;
|
||||
fn setup_content(context: &Context) -> crate::Result<Content<String>> {
|
||||
let config = &context.config;
|
||||
if config.build.dev_path.starts_with("http") {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
@ -99,27 +99,19 @@ fn setup_content() -> crate::Result<Content<String>> {
|
||||
}
|
||||
Ok(Content::Url(config.build.dev_path.clone()))
|
||||
} else {
|
||||
let dev_dir = &config.build.dev_path;
|
||||
let dev_path = std::path::Path::new(dev_dir).join("index.tauri.html");
|
||||
if !dev_path.exists() {
|
||||
panic!(
|
||||
"Couldn't find 'index.tauri.html' inside {}; did you forget to run 'tauri dev'?",
|
||||
dev_dir
|
||||
);
|
||||
}
|
||||
Ok(Content::Html(format!(
|
||||
"data:text/html,{}",
|
||||
urlencoding::encode(&std::fs::read_to_string(dev_path)?)
|
||||
urlencoding::encode(context.index)
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// setup content for embedded server
|
||||
#[cfg(all(embedded_server, not(no_server)))]
|
||||
fn setup_content() -> crate::Result<Content<String>> {
|
||||
let (port, valid) = setup_port()?;
|
||||
fn setup_content(context: &Context) -> crate::Result<Content<String>> {
|
||||
let (port, valid) = setup_port(&context)?;
|
||||
let url = (if valid {
|
||||
setup_server_url(port)
|
||||
setup_server_url(port, &context)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("invalid port"))
|
||||
})
|
||||
@ -130,19 +122,18 @@ fn setup_content() -> crate::Result<Content<String>> {
|
||||
|
||||
// setup content for no-server
|
||||
#[cfg(all(no_server, not(embedded_server)))]
|
||||
fn setup_content() -> crate::Result<Content<String>> {
|
||||
let html = include_str!(concat!(env!("OUT_DIR"), "/index.tauri.html"));
|
||||
fn setup_content(context: &Context) -> crate::Result<Content<String>> {
|
||||
Ok(Content::Html(format!(
|
||||
"data:text/html,{}",
|
||||
urlencoding::encode(html)
|
||||
urlencoding::encode(context.index)
|
||||
)))
|
||||
}
|
||||
|
||||
// get the port for the embedded server
|
||||
#[cfg(embedded_server)]
|
||||
#[allow(dead_code)]
|
||||
fn setup_port() -> crate::Result<(String, bool)> {
|
||||
let config = get()?;
|
||||
fn setup_port(context: &Context) -> crate::Result<(String, bool)> {
|
||||
let config = &context.config;
|
||||
match config.tauri.embedded_server.port {
|
||||
tauri_api::config::Port::Random => match get_available_port() {
|
||||
Some(available_port) => Ok((available_port.to_string(), true)),
|
||||
@ -158,8 +149,8 @@ fn setup_port() -> crate::Result<(String, bool)> {
|
||||
// setup the server url for embedded server
|
||||
#[cfg(embedded_server)]
|
||||
#[allow(dead_code)]
|
||||
fn setup_server_url(port: String) -> crate::Result<String> {
|
||||
let config = get()?;
|
||||
fn setup_server_url(port: String, context: &Context) -> crate::Result<String> {
|
||||
let config = &context.config;
|
||||
let mut url = format!("{}:{}", config.tauri.embedded_server.host, port);
|
||||
if !url.starts_with("http") {
|
||||
url = format!("http://{}", url);
|
||||
@ -169,21 +160,35 @@ fn setup_server_url(port: String) -> crate::Result<String> {
|
||||
|
||||
// spawn the embedded server
|
||||
#[cfg(embedded_server)]
|
||||
fn spawn_server(server_url: String) {
|
||||
fn spawn_server(server_url: String, context: &Context) -> crate::Result<()> {
|
||||
let assets = context.assets;
|
||||
let public_path = context.config.tauri.embedded_server.public_path.clone();
|
||||
std::thread::spawn(move || {
|
||||
let server = tiny_http::Server::http(server_url.replace("http://", "").replace("https://", ""))
|
||||
.expect("Unable to spawn server");
|
||||
for request in server.incoming_requests() {
|
||||
let url = match request.url() {
|
||||
let url = request.url().replace(&server_url, "");
|
||||
let url = match url.as_str() {
|
||||
"/" => "/index.tauri.html",
|
||||
url => url,
|
||||
url => {
|
||||
if url.starts_with(&public_path) {
|
||||
&url[public_path.len() - 1..]
|
||||
} else {
|
||||
eprintln!(
|
||||
"found url not matching public path.\nurl: {}\npublic path: {}",
|
||||
url, public_path
|
||||
);
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_string();
|
||||
request
|
||||
.respond(crate::server::asset_response(&url))
|
||||
.respond(crate::server::asset_response(&url, assets))
|
||||
.expect("unable to setup response");
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// spawn an updater process.
|
||||
@ -242,7 +247,7 @@ fn build_webview<A: ApplicationExt + 'static>(
|
||||
content: Content<String>,
|
||||
splashscreen_content: Option<Content<String>>,
|
||||
) -> crate::Result<(A, A::Dispatcher)> {
|
||||
let config = get()?;
|
||||
let config = &application.context.config;
|
||||
// TODO let debug = cfg!(debug_assertions);
|
||||
// get properties from config struct
|
||||
// TODO let width = config.tauri.window.width;
|
||||
@ -276,7 +281,7 @@ fn build_webview<A: ApplicationExt + 'static>(
|
||||
}}
|
||||
{plugin_init}
|
||||
"#,
|
||||
tauri_init = include_str!(concat!(env!("OUT_DIR"), "/__tauri.js")),
|
||||
tauri_init = application.context.tauri_script,
|
||||
event_init = init(),
|
||||
plugin_init = crate::async_runtime::block_on(crate::plugin::init_script(A::plugin_store()))
|
||||
);
|
||||
@ -316,9 +321,10 @@ fn build_webview<A: ApplicationExt + 'static>(
|
||||
} else if arg == r#"{"cmd":"closeSplashscreen"}"# {
|
||||
dispatcher.eval(&format!(r#"window.location.href = "{}""#, content_url));
|
||||
} else {
|
||||
let mut endpoint_handle = crate::endpoints::handle(&mut dispatcher, &arg)
|
||||
.await
|
||||
.map_err(|e| e.to_string());
|
||||
let mut endpoint_handle =
|
||||
crate::endpoints::handle(&mut dispatcher, &arg, &application.context)
|
||||
.await
|
||||
.map_err(|e| e.to_string());
|
||||
if let Err(ref tauri_handle_error) = endpoint_handle {
|
||||
if tauri_handle_error.contains("unknown variant") {
|
||||
let error = match application.run_invoke_handler(&mut dispatcher, &arg).await {
|
||||
@ -388,21 +394,18 @@ fn get_api_error_message(arg: &str, handler_error_message: String) -> String {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Content;
|
||||
use crate::Context;
|
||||
use crate::FromTauriContext;
|
||||
use proptest::prelude::*;
|
||||
use std::env;
|
||||
|
||||
#[derive(FromTauriContext)]
|
||||
#[config_path = "test/fixture/src-tauri/tauri.conf.json"]
|
||||
struct TauriContext;
|
||||
|
||||
#[test]
|
||||
fn check_setup_content() {
|
||||
let tauri_dir = match option_env!("TAURI_DIR") {
|
||||
Some(d) => d.to_string(),
|
||||
None => env::current_dir()
|
||||
.unwrap()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("Unable to convert to normal String"),
|
||||
};
|
||||
env::set_current_dir(tauri_dir).expect("failed to change cwd");
|
||||
let res = super::setup_content();
|
||||
let context = Context::new::<TauriContext>().unwrap();
|
||||
let res = super::setup_content(&context);
|
||||
|
||||
#[cfg(embedded_server)]
|
||||
match res {
|
||||
@ -413,23 +416,9 @@ mod test {
|
||||
#[cfg(no_server)]
|
||||
match res {
|
||||
Ok(Content::Html(s)) => {
|
||||
let dist_dir = match option_env!("TAURI_DIST_DIR") {
|
||||
Some(d) => d.to_string(),
|
||||
None => env::current_dir()
|
||||
.unwrap()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("Unable to convert to normal String"),
|
||||
};
|
||||
assert_eq!(
|
||||
s,
|
||||
format!(
|
||||
"data:text/html,{}",
|
||||
urlencoding::encode(
|
||||
&std::fs::read_to_string(std::path::Path::new(&dist_dir).join("index.tauri.html"))
|
||||
.unwrap()
|
||||
)
|
||||
)
|
||||
format!("data:text/html,{}", urlencoding::encode(context.index))
|
||||
);
|
||||
}
|
||||
_ => panic!("setup content failed"),
|
||||
@ -437,20 +426,13 @@ mod test {
|
||||
|
||||
#[cfg(dev)]
|
||||
{
|
||||
let config = tauri_api::config::get().expect("unable to setup default config");
|
||||
let config = &context.config;
|
||||
match res {
|
||||
Ok(Content::Url(dp)) => assert_eq!(dp, config.build.dev_path),
|
||||
Ok(Content::Html(s)) => {
|
||||
let dev_dir = &config.build.dev_path;
|
||||
let dev_path = std::path::Path::new(dev_dir).join("index.tauri.html");
|
||||
assert_eq!(
|
||||
s,
|
||||
format!(
|
||||
"data:text/html,{}",
|
||||
urlencoding::encode(
|
||||
&std::fs::read_to_string(dev_path).expect("failed to read dev path")
|
||||
)
|
||||
)
|
||||
format!("data:text/html,{}", urlencoding::encode(context.index))
|
||||
);
|
||||
}
|
||||
_ => panic!("setup content failed"),
|
||||
@ -461,7 +443,8 @@ mod test {
|
||||
#[cfg(embedded_server)]
|
||||
#[test]
|
||||
fn check_setup_port() {
|
||||
let res = super::setup_port();
|
||||
let context = Context::new::<TauriContext>().unwrap();
|
||||
let res = super::setup_port(&context);
|
||||
match res {
|
||||
Ok((_s, _b)) => {}
|
||||
_ => panic!("setup port failed"),
|
||||
@ -474,8 +457,9 @@ mod test {
|
||||
#[test]
|
||||
fn check_server_url(port in (any::<u32>().prop_map(|v| v.to_string()))) {
|
||||
let p = port.clone();
|
||||
let context = Context::new::<TauriContext>().unwrap();
|
||||
|
||||
let res = super::setup_server_url(port);
|
||||
let res = super::setup_server_url(port, &context);
|
||||
|
||||
match res {
|
||||
Ok(url) => assert!(url.contains(&p)),
|
||||
|
||||
@ -1 +0,0 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/data.rs"));
|
||||
@ -1,9 +0,0 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use tauri_api::cli::Matches;
|
||||
|
||||
/// Gets the CLI arg matches.
|
||||
pub fn get_matches() -> &'static Option<Matches> {
|
||||
static MATCHES: Lazy<Option<Matches>> = Lazy::new(|| tauri_api::cli::get_matches().ok());
|
||||
|
||||
&MATCHES
|
||||
}
|
||||
@ -16,12 +16,13 @@ mod http;
|
||||
#[cfg(notification)]
|
||||
mod notification;
|
||||
|
||||
use crate::{ApplicationDispatcherExt, Event};
|
||||
use crate::{app::Context, ApplicationDispatcherExt, Event};
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) async fn handle<D: ApplicationDispatcherExt + 'static>(
|
||||
dispatcher: &mut D,
|
||||
arg: &str,
|
||||
context: &Context,
|
||||
) -> crate::Result<()> {
|
||||
use cmd::Cmd::*;
|
||||
match serde_json::from_str(arg) {
|
||||
@ -277,22 +278,14 @@ pub(crate) async fn handle<D: ApplicationDispatcherExt + 'static>(
|
||||
callback,
|
||||
error,
|
||||
} => {
|
||||
asset::load(dispatcher, asset, asset_type, callback, error).await;
|
||||
asset::load(dispatcher, asset, asset_type, callback, error, &context).await;
|
||||
}
|
||||
CliMatches { callback, error } => {
|
||||
#[cfg(cli)]
|
||||
crate::execute_promise(
|
||||
dispatcher,
|
||||
async move {
|
||||
match crate::cli::get_matches() {
|
||||
Some(matches) => Ok(matches),
|
||||
None => Err(anyhow::anyhow!(r#""failed to get matches""#)),
|
||||
}
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
)
|
||||
.await;
|
||||
{
|
||||
let matches = tauri_api::cli::get_matches(&context.config);
|
||||
crate::execute_promise(dispatcher, async move { matches }, callback, error).await;
|
||||
}
|
||||
#[cfg(not(cli))]
|
||||
api_error(
|
||||
dispatcher,
|
||||
@ -306,7 +299,7 @@ pub(crate) async fn handle<D: ApplicationDispatcherExt + 'static>(
|
||||
error,
|
||||
} => {
|
||||
#[cfg(notification)]
|
||||
notification::send(dispatcher, options, callback, error).await;
|
||||
notification::send(dispatcher, options, callback, error, &context.config).await;
|
||||
#[cfg(not(notification))]
|
||||
allowlist_error(dispatcher, error, "notification");
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::ApplicationDispatcherExt;
|
||||
use std::path::PathBuf;
|
||||
use crate::{ApplicationDispatcherExt, Context};
|
||||
use std::io::Read;
|
||||
use tauri_api::assets::{AssetFetch, Assets};
|
||||
|
||||
#[allow(clippy::option_env_unwrap)]
|
||||
pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
@ -8,42 +9,43 @@ pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
asset_type: String,
|
||||
callback: String,
|
||||
error: String,
|
||||
ctx: &Context,
|
||||
) {
|
||||
let mut dispatcher_ = dispatcher.clone();
|
||||
let assets = ctx.assets;
|
||||
let public_path = ctx.config.tauri.embedded_server.public_path.clone();
|
||||
crate::execute_promise(
|
||||
dispatcher,
|
||||
async move {
|
||||
let mut path = PathBuf::from(if asset.starts_with('/') {
|
||||
asset.replacen("/", "", 1)
|
||||
// strip "about:" uri scheme if it exists
|
||||
let asset = if asset.starts_with("about:") {
|
||||
&asset[6..]
|
||||
} else {
|
||||
asset.clone()
|
||||
});
|
||||
let mut read_asset;
|
||||
loop {
|
||||
read_asset = crate::assets::ASSETS.get(&format!(
|
||||
"{}/{}",
|
||||
option_env!("TAURI_DIST_DIR")
|
||||
.expect("tauri apps should be built with the TAURI_DIST_DIR environment variable"),
|
||||
path.to_string_lossy()
|
||||
));
|
||||
if read_asset.is_err() {
|
||||
match path.iter().next() {
|
||||
Some(component) => {
|
||||
let first_component = component.to_str().expect("failed to read path component");
|
||||
path = PathBuf::from(path.to_string_lossy().replacen(
|
||||
format!("{}/", first_component).as_str(),
|
||||
"",
|
||||
1,
|
||||
));
|
||||
}
|
||||
None => {
|
||||
return Err(anyhow::anyhow!("Asset '{}' not found", asset));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
&asset
|
||||
};
|
||||
|
||||
// handle public path setting from tauri.conf > tauri > embeddedServer > publicPath
|
||||
let asset = if asset.starts_with(&public_path) {
|
||||
&asset[public_path.len() - 1..]
|
||||
} else {
|
||||
eprintln!(
|
||||
"found url not matching public path.\nasset url: {}\npublic path: {}",
|
||||
asset, public_path
|
||||
);
|
||||
asset
|
||||
}
|
||||
.to_string();
|
||||
|
||||
// how should that condition be handled now?
|
||||
let asset_bytes = assets
|
||||
.get(&Assets::format_key(&asset), AssetFetch::Decompress)
|
||||
.ok_or_else(|| anyhow::anyhow!("Asset '{}' not found", asset))
|
||||
.and_then(|(read, _)| {
|
||||
read
|
||||
.bytes()
|
||||
.collect::<Result<Vec<u8>, _>>()
|
||||
.map_err(Into::into)
|
||||
})?;
|
||||
|
||||
if asset_type == "image" {
|
||||
let mime_type = if asset.ends_with("gif") {
|
||||
@ -62,12 +64,11 @@ pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
"jpeg"
|
||||
};
|
||||
Ok(format!(
|
||||
r#"data:image/{};base64,{}"#,
|
||||
r#""data:image/{};base64,{}""#,
|
||||
mime_type,
|
||||
base64::encode(&read_asset.expect("Failed to read asset type").into_owned())
|
||||
base64::encode(&asset_bytes)
|
||||
))
|
||||
} else {
|
||||
let asset_bytes = read_asset.expect("Failed to read asset type");
|
||||
let asset_str =
|
||||
std::str::from_utf8(&asset_bytes).expect("failed to convert asset bytes to u8 slice");
|
||||
if asset_type == "stylesheet" {
|
||||
|
||||
@ -1,17 +1,22 @@
|
||||
use super::cmd::NotificationOptions;
|
||||
use crate::ApplicationDispatcherExt;
|
||||
use serde_json::Value as JsonValue;
|
||||
use tauri_api::config::Config;
|
||||
use tauri_api::notification::Notification;
|
||||
|
||||
pub async fn send<D: ApplicationDispatcherExt>(
|
||||
dispatcher: &mut D,
|
||||
options: NotificationOptions,
|
||||
callback: String,
|
||||
error: String,
|
||||
config: &Config,
|
||||
) {
|
||||
let identifier = config.tauri.bundle.identifier.clone();
|
||||
|
||||
crate::execute_promise(
|
||||
dispatcher,
|
||||
async move {
|
||||
let mut notification = tauri_api::notification::Notification::new().title(options.title);
|
||||
let mut notification = Notification::new(identifier).title(options.title);
|
||||
if let Some(body) = options.body {
|
||||
notification = notification.body(body);
|
||||
}
|
||||
|
||||
@ -6,9 +6,6 @@
|
||||
//! Tauri uses (and contributes to) the MIT licensed project that you can find at [webview](https://github.com/webview/webview).
|
||||
#![warn(missing_docs, rust_2018_idioms)]
|
||||
|
||||
/// The asset management module.
|
||||
#[cfg(assets)]
|
||||
pub mod assets;
|
||||
/// The event system module.
|
||||
pub mod event;
|
||||
/// The embedded server helpers.
|
||||
@ -17,10 +14,6 @@ pub mod server;
|
||||
/// The Tauri-specific settings for your app e.g. notification permission status.
|
||||
pub mod settings;
|
||||
|
||||
/// The CLI args interface.
|
||||
#[cfg(cli)]
|
||||
pub mod cli;
|
||||
|
||||
/// The webview application entry.
|
||||
mod app;
|
||||
/// The Tauri API endpoints.
|
||||
@ -41,6 +34,7 @@ pub type SyncTask = Box<dyn FnOnce() + Send>;
|
||||
pub use anyhow::Result;
|
||||
pub use app::*;
|
||||
pub use tauri_api as api;
|
||||
pub use tauri_macros::FromTauriContext;
|
||||
pub use webview::{
|
||||
ApplicationDispatcherExt, ApplicationExt, Callback, Event, WebviewBuilderExt, WindowBuilderExt,
|
||||
};
|
||||
|
||||
@ -1,39 +1,39 @@
|
||||
use tiny_http::{Header, Response};
|
||||
use std::io::Read;
|
||||
use tauri_api::assets::{AssetFetch, Assets};
|
||||
use tiny_http::{Response, StatusCode};
|
||||
|
||||
/// Returns the HTTP response of the given asset path.
|
||||
#[allow(clippy::option_env_unwrap)]
|
||||
pub fn asset_response(path: &str) -> Response<std::io::Cursor<Vec<u8>>> {
|
||||
let asset_path = &format!(
|
||||
"{}{}",
|
||||
option_env!("TAURI_DIST_DIR")
|
||||
.expect("tauri apps should be built with the TAURI_DIST_DIR environment variable"),
|
||||
path
|
||||
);
|
||||
let asset = crate::assets::ASSETS
|
||||
.get(asset_path)
|
||||
.unwrap_or_else(|_| panic!("Could not read asset {}", asset_path))
|
||||
.into_owned();
|
||||
let mut response = Response::from_data(asset);
|
||||
let header;
|
||||
pub fn asset_response(path: &str, assets: &'static Assets) -> Response<impl Read> {
|
||||
let (asset, _) = assets
|
||||
.get(path, AssetFetch::Compress)
|
||||
.unwrap_or_else(|| panic!("Could not read asset {}", path));
|
||||
|
||||
if path.ends_with(".svg") {
|
||||
header = Header::from_bytes(&b"Content-Type"[..], &b"image/svg+xml"[..])
|
||||
.expect("Could not add svg+xml header");
|
||||
let mut headers = Vec::new();
|
||||
|
||||
// Content-Encoding
|
||||
const CONTENT_ENCODING: &str = "Content-Encoding: gzip";
|
||||
let content_encoding = CONTENT_ENCODING
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("Could not add {} header", CONTENT_ENCODING));
|
||||
headers.push(content_encoding);
|
||||
|
||||
// Content-Type
|
||||
let mime = if path.ends_with(".svg") {
|
||||
"Content-Type: image/svg+xml"
|
||||
} else if path.ends_with(".css") {
|
||||
header =
|
||||
Header::from_bytes(&b"Content-Type"[..], &b"text/css"[..]).expect("Could not add css header");
|
||||
"Content-Type: text/css"
|
||||
} else if path.ends_with(".html") {
|
||||
header = Header::from_bytes(&b"Content-Type"[..], &b"text/html"[..])
|
||||
.expect("Could not add html header");
|
||||
"Content-Type: text/html"
|
||||
} else if path.ends_with(".js") {
|
||||
header = Header::from_bytes(&b"Content-Type"[..], &b"text/javascript"[..])
|
||||
.expect("Could not add Javascript header");
|
||||
"Content-Type: text/javascript"
|
||||
} else {
|
||||
header = Header::from_bytes(&b"Content-Type"[..], &b"application/octet-stream"[..])
|
||||
.expect("Could not add octet-stream header");
|
||||
}
|
||||
"Content-Type: application/octet-stream"
|
||||
};
|
||||
|
||||
response.add_header(header);
|
||||
let content_type = mime
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("Could not add {} header", mime));
|
||||
headers.push(content_type);
|
||||
|
||||
response
|
||||
Response::new(StatusCode(200), headers, asset, None, None)
|
||||
}
|
||||
|
||||
0
tauri/test/fixture/dist/__tauri.js
vendored
Normal file
0
tauri/test/fixture/dist/__tauri.js
vendored
Normal file
@ -9,6 +9,7 @@
|
||||
"active": true
|
||||
},
|
||||
"bundle": {
|
||||
"identifier": "studio.tauri.example",
|
||||
"active": true
|
||||
},
|
||||
"allowlist": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user