chore: add prettier for js formatting (#937)

* chore: add prettier for js/ts formatting

* fix lint-staged to object

* test commit

* format all

* lock file bump

* eslint extends prettier

This will let us skip rules in eslint that prettier can control. Prettier for styles, eslint for code errors.

* add prettier config

* roll back to what we had with eslint settings

* skip mutation observer

* add prettier typescript eslint

* run prettier in lint workflow

* format:check script

* turn off space before function in eslint

it is fighting with prettier

* fix dir in workflow

* remove semis

* add api to eslint

* shift eslint ignore comment after prettier format

* ignore errors that currently exist

* build:typevalidators

* replace was broken on typevalidator build

* try pushing up error

* format

* try removing working dir from eslint workflow

* try node 12

* fix indent in action

* bump eslint

* fix supposeded error and try another

* try breaking eslint

* try building in action

* adjust action paths again

* need dot

* remove build

* fix(tauri.js/eslint): escape glob *

* fix(tauri.js): ignore lint error

* Create prettier-taurijs.md

Co-authored-by: Noah Klayman <noahklayman@gmail.com>
This commit is contained in:
Jacob Bolda 2020-08-18 21:36:46 -05:00 committed by GitHub
parent a949e711e4
commit 6a21965ff3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1698 additions and 1280 deletions

View File

@ -0,0 +1,5 @@
---
"tauri.js": patch
---
Format all code with prettier. This technically should only affect code styles, but noting for posterity.

View File

@ -12,9 +12,14 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12'
- name: install deps via yarn
working-directory: ./cli/tauri.js
working-directory: ./cli/tauri.js/
run: yarn
- name: run eslint
working-directory: ./cli/tauri.js
working-directory: ./cli/tauri.js/
run: yarn lint
- name: run prettier
working-directory: ./cli/tauri.js/
run: yarn format:check

1
cli/deno Submodule

@ -0,0 +1 @@
Subproject commit 55fd9104fa4da3b7afb96a16bb1ed8378b028a5a

View File

@ -11,9 +11,11 @@ module.exports = {
extends: [
'standard-with-typescript',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:lodash-template/recommended'
'plugin:lodash-template/recommended',
// TODO: make this work with typescript
// 'plugin:node/recommended'
'prettier',
'prettier/@typescript-eslint'
],
plugins: ['@typescript-eslint', 'node', 'security'],
@ -49,14 +51,6 @@ module.exports = {
'security/detect-pseudoRandomBytes': 'error',
'space-before-function-paren': 'off',
'@typescript-eslint/default-param-last': 'off',
'@typescript-eslint/strict-boolean-expressions': 0,
'@typescript-eslint/space-before-function-paren': [
'error',
{
asyncArrow: 'always',
anonymous: 'never',
named: 'never'
}
]
'@typescript-eslint/strict-boolean-expressions': 0
}
}

View File

@ -0,0 +1,5 @@
module.exports = {
singleQuote: true,
semi: false,
trailingComma: 'none'
}

View File

@ -1,4 +1,3 @@
import 'regenerator-runtime/runtime'
import * as cli from './cli'
import * as dialog from './dialog'
@ -10,14 +9,4 @@ import * as tauri from './tauri'
import * as window from './window'
import * as notification from './notification'
export {
cli,
dialog,
event,
fs,
http,
process,
tauri,
window,
notification
}
export { cli, dialog, event, fs, http, process, tauri, window, notification }

View File

@ -32,6 +32,4 @@ async function getMatches(): Promise<CliMatches> {
})
}
export {
getMatches
}
export { getMatches }

View File

@ -7,7 +7,10 @@ export interface OpenDialogOptions {
directory?: boolean
}
export type SaveDialogOptions = Pick<OpenDialogOptions, 'filter' | 'defaultPath'>
export type SaveDialogOptions = Pick<
OpenDialogOptions,
'filter' | 'defaultPath'
>
/**
* @name openDialog
@ -51,7 +54,4 @@ async function save(options: SaveDialogOptions = {}): Promise<string> {
})
}
export {
open,
save
}
export { open, save }

View File

@ -13,7 +13,11 @@ export type EventCallback<T> = (event: Event<T>) => void
* @param event the event name
* @param handler the event handler callback
*/
function listen<T>(event: string, handler: EventCallback<T>, once = false): void {
function listen<T>(
event: string,
handler: EventCallback<T>,
once = false
): void {
invoke({
cmd: 'listen',
event,
@ -36,7 +40,4 @@ function emit(event: string, payload?: string): void {
})
}
export {
listen,
emit
}
export { listen, emit }

View File

@ -18,7 +18,7 @@ export enum BaseDirectory {
Template,
Video,
Resource,
App,
App
}
export interface FsOptions {
@ -92,7 +92,10 @@ async function readBinaryFile(
* @param [options.dir] base directory
* @return
*/
async function writeFile(file: FsTextFileOption, options: FsOptions = {}): Promise<void> {
async function writeFile(
file: FsTextFileOption,
options: FsOptions = {}
): Promise<void> {
if (typeof options === 'object') {
Object.freeze(options)
}
@ -151,7 +154,10 @@ function arrayBufferToBase64(buffer: ArrayBuffer): string {
* @param [options.dir] base directory
* @return
*/
async function writeBinaryFile(file: FsBinaryFileOption, options: FsOptions = {}): Promise<void> {
async function writeBinaryFile(
file: FsBinaryFileOption,
options: FsOptions = {}
): Promise<void> {
if (typeof options === 'object') {
Object.freeze(options)
}
@ -176,7 +182,10 @@ async function writeBinaryFile(file: FsBinaryFileOption, options: FsOptions = {}
* @param [options.dir] base directory
* @return
*/
async function readDir(dir: string, options: FsOptions = {}): Promise<FileEntry[]> {
async function readDir(
dir: string,
options: FsOptions = {}
): Promise<FileEntry[]> {
return await promisified({
cmd: 'readDir',
path: dir,
@ -230,7 +239,11 @@ async function removeDir(dir: string, options: FsOptions = {}): Promise<void> {
* @param [options.dir] base directory
* @return
*/
async function copyFile(source: string, destination: string, options: FsOptions = {}): Promise<void> {
async function copyFile(
source: string,
destination: string,
options: FsOptions = {}
): Promise<void> {
return await promisified({
cmd: 'copyFile',
source,
@ -247,7 +260,10 @@ async function copyFile(source: string, destination: string, options: FsOptions
* @param [options.dir] base directory
* @return
*/
async function removeFile(file: string, options: FsOptions = {}): Promise<void> {
async function removeFile(
file: string,
options: FsOptions = {}
): Promise<void> {
return await promisified({
cmd: 'removeFile',
path: file,
@ -264,7 +280,11 @@ async function removeFile(file: string, options: FsOptions = {}): Promise<void>
* @param [options.dir] base directory
* @return
*/
async function renameFile(oldPath: string, newPath: string, options: FsOptions = {}): Promise<void> {
async function renameFile(
oldPath: string,
newPath: string,
options: FsOptions = {}
): Promise<void> {
return await promisified({
cmd: 'renameFile',
oldPath,

View File

@ -14,7 +14,16 @@ export enum BodyType {
export type Body = object | string | BinaryType
export type HttpVerb = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE'
export type HttpVerb =
| 'GET'
| 'POST'
| 'PUT'
| 'DELETE'
| 'PATCH'
| 'HEAD'
| 'OPTIONS'
| 'CONNECT'
| 'TRACE'
export interface HttpOptions {
method: HttpVerb
@ -73,7 +82,11 @@ async function get<T>(url: string, options: PartialOptions): Promise<T> {
*
* @return promise resolving to the response
*/
async function post<T>(url: string, body: Body, options: PartialOptions): Promise<T> {
async function post<T>(
url: string,
body: Body,
options: PartialOptions
): Promise<T> {
return await request({
method: 'POST',
url,
@ -91,7 +104,11 @@ async function post<T>(url: string, body: Body, options: PartialOptions): Promis
*
* @return promise resolving to the response
*/
async function put<T>(url: string, body: Body, options: PartialOptions): Promise<T> {
async function put<T>(
url: string,
body: Body,
options: PartialOptions
): Promise<T> {
return await request({
method: 'PUT',
url,
@ -124,7 +141,10 @@ async function patch<T>(url: string, options: PartialOptions): Promise<T> {
*
* @return promise resolving to the response
*/
async function deleteRequest<T>(url: string, options: PartialOptions): Promise<T> {
async function deleteRequest<T>(
url: string,
options: PartialOptions
): Promise<T> {
return await request({
method: 'DELETE',
url,

View File

@ -32,8 +32,4 @@ function sendNotification(options: Options | string): void {
}
}
export {
sendNotification,
requestPermission,
isPermissionGranted
}
export { sendNotification, requestPermission, isPermissionGranted }

View File

@ -7,7 +7,10 @@ import { promisified } from './tauri'
* @param [args] command args
* @return promise resolving to the stdout text
*/
async function execute(command: string, args?: string | string[]): Promise<string> {
async function execute(
command: string,
args?: string | string[]
): Promise<string> {
if (typeof args === 'object') {
Object.freeze(args)
}
@ -19,6 +22,4 @@ async function execute(command: string, args?: string | string[]): Promise<strin
})
}
export {
execute
}
export { execute }

View File

@ -11,8 +11,20 @@ function s4(): string {
}
function uid(): string {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
return (
s4() +
s4() +
'-' +
s4() +
'-' +
s4() +
'-' +
s4() +
'-' +
s4() +
s4() +
s4()
)
}
/**
@ -24,7 +36,10 @@ function invoke(args: any): void {
window.__TAURI_INVOKE_HANDLER__(args)
}
function transformCallback(callback?: (response: any) => void, once = false): string {
function transformCallback(
callback?: (response: any) => void,
once = false
): string {
const identifier = uid()
Object.defineProperty(window, identifier, {
@ -50,11 +65,11 @@ function transformCallback(callback?: (response: any) => void, once = false): st
*/
async function promisified<T>(args: any): Promise<T> {
return await new Promise((resolve, reject) => {
const callback = transformCallback(e => {
const callback = transformCallback((e) => {
resolve(e)
Reflect.deleteProperty(window, error)
}, true)
const error = transformCallback(e => {
const error = transformCallback((e) => {
reject(e)
Reflect.deleteProperty(window, callback)
}, true)
@ -67,8 +82,4 @@ async function promisified<T>(args: any): Promise<T> {
})
}
export {
invoke,
transformCallback,
promisified
}
export { invoke, transformCallback, promisified }

View File

@ -24,7 +24,4 @@ function open(url: string): void {
})
}
export {
setTitle,
open
}
export { setTitle, open }

View File

@ -1,11 +1,14 @@
module.exports = {
presets: [
['@babel/preset-env', {
targets: {
node: 'current'
},
modules: 'commonjs'
}],
[
'@babel/preset-env',
{
targets: {
node: 'current'
},
modules: 'commonjs'
}
],
'@babel/preset-typescript'
]
};
}

View File

@ -25,7 +25,7 @@ if (argv.help) {
process.exit(0)
}
async function run () {
async function run() {
const build = require('../dist/api/build')
await build({

View File

@ -2,11 +2,11 @@ const parseArgs = require('minimist')
const inquirer = require('inquirer')
const { resolve } = require('path')
const { merge } = require('lodash')
const {
recipeShortNames,
recipeDescriptiveNames,
recipeByDescriptiveName,
recipeByShortName
const {
recipeShortNames,
recipeDescriptiveNames,
recipeByDescriptiveName,
recipeByShortName
} = require('../dist/api/recipes')
/**
@ -47,7 +47,7 @@ function main(cliArgs) {
if (argv.ci) {
runInit(argv)
} else {
getOptionsInteractive(argv).then(responses => runInit(argv, responses))
getOptionsInteractive(argv).then((responses) => runInit(argv, responses))
}
}
@ -78,18 +78,22 @@ const getOptionsInteractive = (argv) => {
let defaultAppName = argv.A
if (!defaultAppName) {
try {
const packageJson = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json')).toString())
const packageJson = JSON.parse(
readFileSync(resolve(process.cwd(), 'package.json')).toString()
)
defaultAppName = packageJson.displayName || packageJson.name
} catch {}
}
return inquirer
.prompt([{
.prompt([
{
type: 'input',
name: 'appName',
message: 'What is your app name?',
default: defaultAppName
}, {
},
{
type: 'input',
name: 'tauri.window.title',
message: 'What should the window title be?',
@ -105,7 +109,7 @@ const getOptionsInteractive = (argv) => {
when: () => !argv.r
}
])
.then(answers =>
.then((answers) =>
inquirer
.prompt([
{
@ -113,19 +117,24 @@ const getOptionsInteractive = (argv) => {
name: 'build.devPath',
message: 'What is the url of your dev server?',
default: 'http://localhost:4000',
when: () => !argv.P && !argv.p && answers.recipeName === 'No recipe' || argv.r === 'none'
when: () =>
(!argv.P && !argv.p && answers.recipeName === 'No recipe') ||
argv.r === 'none'
},
{
type: 'input',
name: 'build.distDir',
message: 'Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri" folder that will be created?',
message:
'Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri" folder that will be created?',
default: '../dist',
when: () => !argv.D && answers.recipeName === 'No recipe' || argv.r === 'none'
when: () =>
(!argv.D && answers.recipeName === 'No recipe') ||
argv.r === 'none'
}
])
.then(answers2 => ({...answers, ...answers2}))
.then((answers2) => ({ ...answers, ...answers2 }))
)
.catch(error => {
.catch((error) => {
if (error.isTtyError) {
// Prompt couldn't be rendered in the current environment
console.log(
@ -139,16 +148,11 @@ const getOptionsInteractive = (argv) => {
})
}
async function runInit(argv, config = {}) {
const {
appName,
recipeName,
...configOptions
} = config
const { appName, recipeName, ...configOptions } = config
const init = require('../dist/api/init')
let recipe;
let recipe
let recipeSelection = 'none'
if (recipeName !== undefined) {
@ -168,7 +172,7 @@ async function runInit(argv, config = {}) {
}
const directory = argv.d || process.cwd()
init({
directory,
force: argv.f || null,
@ -184,18 +188,16 @@ async function runInit(argv, config = {}) {
}
})
})
const {
installDependencies
} = require('../dist/api/dependency-manager')
const { installDependencies } = require('../dist/api/dependency-manager')
await installDependencies()
if (recipe !== undefined) {
const {
installRecipeDependencies,
runRecipePostConfig
} = require('../dist/api/recipes/install')
await installRecipeDependencies(recipe, directory)
await runRecipePostConfig(recipe, directory)
}

View File

@ -20,7 +20,7 @@ if (argv.help) {
process.exit(0)
}
async function run () {
async function run() {
const dev = require('../dist/api/dev')
await dev({

View File

@ -45,14 +45,13 @@ if (argv.help) {
process.exit(0)
}
tauricon.make(
argv.i,
argv.t,
argv.c || 'optipng'
).then(() => {
// TODO: use logger module for prettier output
console.log('app:tauri (tauricon) Completed')
}).catch(e => {
// TODO: use logger module for prettier output
console.error('app:tauri (icon)', e)
})
tauricon
.make(argv.i, argv.t, argv.c || 'optipng')
.then(() => {
// TODO: use logger module for prettier output
console.log('app:tauri (tauricon) Completed')
})
.catch((e) => {
// TODO: use logger module for prettier output
console.error('app:tauri (icon)', e)
})

View File

@ -2,9 +2,9 @@ const parseArgs = require('minimist')
const tauriCreate = require('./tauri-create')
/**
* init is an alias for create -r none, same as
* init is an alias for create -r none, same as
* creating a fresh tauri project with no UI recipe applied.
*
*
* @type {object}
* @property {boolean} h
* @property {boolean} help
@ -18,7 +18,7 @@ const tauriCreate = require('./tauri-create')
function main(cliArgs) {
const argv = parseArgs(cliArgs, {
alias: {
h: 'help',
h: 'help'
},
boolean: ['h']
})
@ -32,7 +32,6 @@ function main(cliArgs) {
tauriCreate([...cliArgs, '-r', 'none'])
}
function printUsage() {
console.log(`
Description

View File

@ -10,11 +10,17 @@ const cmd = process.argv[2]
* @param {string|array} command
*/
const tauri = function (command) {
if (typeof command === 'object') { // technically we just care about an array
if (typeof command === 'object') {
// technically we just care about an array
command = command[0]
}
if (!command || command === '-h' || command === '--help' || command === 'help') {
if (
!command ||
command === '-h' ||
command === '--help' ||
command === 'help'
) {
console.log(`
Description
This is the Tauri CLI.

View File

@ -1,16 +1,28 @@
const { exec } = require("child_process")
const { exec } = require('child_process')
const { readFileSync, writeFileSync } = require('fs')
const { resolve } = require('path')
const sourcePath = resolve(__dirname, '../src/types/config.ts')
exec(`typescript-json-validator --noExtraProps ${sourcePath} TauriConfig`, error => {
if (error) {
console.error(error.message)
process.exit(error.code || 1)
} else {
const configValidatorPath = resolve(__dirname, '../src/types/config.validator.ts')
const configValidator = readFileSync(configValidatorPath).toString()
writeFileSync(configValidatorPath, configValidator.replace(`import Ajv = require('ajv');`, `import Ajv from 'ajv';`))
exec(
`typescript-json-validator --noExtraProps ${sourcePath} TauriConfig`,
(error) => {
if (error) {
console.error(error.message)
process.exit(error.code || 1)
} else {
const configValidatorPath = resolve(
__dirname,
'../src/types/config.validator.ts'
)
const configValidator = readFileSync(configValidatorPath).toString()
writeFileSync(
configValidatorPath,
configValidator.replace(
`import Ajv = require('ajv')`,
`import Ajv from 'ajv'`
)
)
}
}
})
)

View File

@ -38,8 +38,9 @@ module.exports = {
'^test/(.*)$': '<rootDir>/test/$1',
'../../package.json': '<rootDir>/package.json'
},
"transform": {
"templates[\\\\/](tauri|mutation-observer)\.js": "./test/jest/raw-loader-transformer.js",
"\\.(js|ts)$": "babel-jest"
transform: {
'templates[\\\\/](tauri|mutation-observer).js':
'./test/jest/raw-loader-transformer.js',
'\\.(js|ts)$': 'babel-jest'
}
}

View File

@ -19,9 +19,11 @@
"pretest": "yarn build",
"prepublishOnly": "yarn build-release",
"test:local": "jest --runInBand",
"lint": "eslint --ext ts ./src/**/*.ts ./api-src/**/*.ts",
"lint-fix": "eslint --fix --ext ts ./src/**/*.ts",
"lint": "eslint --ext ts \"./src/**/*.ts\" \"./api-src/**/*.ts\"",
"lint-fix": "eslint --fix --ext ts \"./src/**/*.ts\" \"./api-src/**/*.ts\"",
"lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts npm yarn",
"format": "prettier --write --end-of-line=auto !./**/mutation-observer.js !./**/config.validator.js \"./**/*.{js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
"format:check": "prettier --check --end-of-line=auto !./**/mutation-observer.js \"./**/*.{js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
"build:tauri[rust]": "cd ../tauri && TAURI_DIST_DIR=../../test/fixture/dist TAURI_DIR=../test/fixture cargo publish --dry-run --allow-dirty"
},
"repository": {
@ -96,12 +98,13 @@
"@types/semver": "7.3.1",
"@types/sharp": "0.25.1",
"@types/webpack-merge": "4.1.5",
"@typescript-eslint/eslint-plugin": "3.7.0",
"@typescript-eslint/parser": "3.7.0",
"@typescript-eslint/eslint-plugin": "3.9.1",
"@typescript-eslint/parser": "3.9.1",
"babel-jest": "26.1.0",
"copy-webpack-plugin": "6.0.3",
"dotenv": "8.2.0",
"eslint": "7.5.0",
"eslint": "7.7.0",
"eslint-config-prettier": "6.11.0",
"eslint-config-standard-with-typescript": "18.0.2",
"eslint-plugin-import": "2.22.0",
"eslint-plugin-lodash-template": "0.19.0",
@ -115,6 +118,7 @@
"jest-mock-process": "1.4.0",
"lint-staged": "10.2.11",
"lockfile-lint": "4.3.7",
"prettier": "2.0.5",
"promise": "8.1.0",
"raw-loader": "4.0.1",
"rimraf": "3.0.2",
@ -138,7 +142,8 @@
"pre-commit": "lint-staged"
}
},
"lint-staged": [
"eslint --fix"
]
"lint-staged": {
"*.{js,jsx,ts,tsx,md,html,css,json}": "prettier --write --end-of-line=auto !./**/mutation-observer.js !./**/config.validator.js \"./**/*.{js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
"*.{ts,tsx}": "eslint --fix --ext ts ./src/**/*.ts ./api-src/**/*.ts"
}
}

View File

@ -7,100 +7,100 @@ import babel, { getBabelOutputPlugin } from '@rollup/plugin-babel'
import typescript from '@rollup/plugin-typescript'
import pkg from './package.json'
export default [{
input: {
'fs': './api-src/fs.ts',
'dialog': './api-src/dialog.ts',
'event': './api-src/event.ts',
'http': './api-src/http.ts',
'index': './api-src/index.ts',
'process': './api-src/process.ts',
'tauri': './api-src/tauri.ts',
'window': './api-src/window.ts',
'cli': './api-src/cli.ts',
'notification': './api-src/notification.ts',
},
treeshake: true,
perf: true,
output: [
{
dir: 'api/',
entryFileNames: '[name].js',
format: 'cjs',
exports: 'named',
globals: {}
export default [
{
input: {
fs: './api-src/fs.ts',
dialog: './api-src/dialog.ts',
event: './api-src/event.ts',
http: './api-src/http.ts',
index: './api-src/index.ts',
process: './api-src/process.ts',
tauri: './api-src/tauri.ts',
window: './api-src/window.ts',
cli: './api-src/cli.ts',
notification: './api-src/notification.ts'
},
{
dir: 'api/',
entryFileNames: '[name].mjs',
format: 'esm',
exports: 'named',
globals: {}
}
],
plugins: [
commonjs({}),
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
treeshake: true,
perf: true,
output: [
{
dir: 'api/',
entryFileNames: '[name].js',
format: 'cjs',
exports: 'named',
globals: {}
},
{
dir: 'api/',
entryFileNames: '[name].mjs',
format: 'esm',
exports: 'named',
globals: {}
}
}),
typescript({
tsconfig: './tsconfig.api.json'
}),
babel({
configFile: false,
presets: [
['@babel/preset-env'],
['@babel/preset-typescript']
]
}),
terser()
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
],
watch: {
chokidar: true,
include: 'api-src/**',
exclude: 'node_modules/**'
}
},
{
input: {
'bundle': './api-src/bundle.ts'
},
output: [{
name: '__TAURI__',
dir: 'api/', // 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
commonjs({}),
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
}
}),
typescript({
tsconfig: './tsconfig.api.json'
}),
babel({
configFile: false,
presets: [['@babel/preset-env'], ['@babel/preset-typescript']]
}),
terser()
],
globals: {
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
],
watch: {
chokidar: true,
include: 'api-src/**',
exclude: 'node_modules/**'
}
}],
plugins: [
sucrase({
exclude: ['node_modules'],
transforms: ['typescript']
}),
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
},
{
input: {
bundle: './api-src/bundle.ts'
},
output: [
{
name: '__TAURI__',
dir: 'api/', // 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
}),
terser()
],
globals: {}
}
})
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
]
}]
],
plugins: [
sucrase({
exclude: ['node_modules'],
transforms: ['typescript']
}),
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
}
})
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
]
}
]

View File

@ -9,7 +9,9 @@ const log = logger('dependency:cargo-commands')
const dependencies = ['tauri-bundler']
async function manageDependencies(managementType: ManagementType): Promise<Result> {
async function manageDependencies(
managementType: ManagementType
): Promise<Result> {
const installedDeps = []
const updatedDeps = []
@ -22,12 +24,16 @@ async function manageDependencies(managementType: ManagementType): Promise<Resul
} else if (managementType === ManagementType.Update) {
const latestVersion = await getCrateLatestVersion(dependency)
if (semverLt(currentVersion, latestVersion)) {
const inquired = await inquirer.prompt([{
type: 'confirm',
name: 'answer',
message: `[CARGO COMMANDS] "${dependency}" latest version is ${latestVersion}. Do you want to update?`,
default: false
}])
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const inquired = await inquirer.prompt([
{
type: 'confirm',
name: 'answer',
message: `[CARGO COMMANDS] "${dependency}" latest version is ${latestVersion}. Do you want to update?`,
default: false
}
])
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (inquired.answer) {
spawnSync('cargo', ['install', dependency, '--force'])
updatedDeps.push(dependency)
@ -55,7 +61,4 @@ async function update(): Promise<Result> {
return await manageDependencies(ManagementType.Update)
}
export {
install,
update
}
export { install, update }

View File

@ -1,5 +1,9 @@
import { spawnSync } from './../../helpers/spawn'
import { CargoManifest, CargoManifestDependency, CargoLock } from './../../types/cargo'
import {
CargoManifest,
CargoManifestDependency,
CargoLock
} from './../../types/cargo'
import { ManagementType, Result } from './types'
import { getCrateLatestVersion, semverLt } from './util'
import logger from '../../helpers/logger'
@ -15,7 +19,7 @@ const dependencies = ['tauri']
function readToml<T>(tomlPath: string): T | null {
if (existsSync(tomlPath)) {
const manifest = readFileSync(tomlPath).toString()
return toml.parse(manifest) as any as T
return (toml.parse(manifest) as any) as T
}
return null
}
@ -24,7 +28,9 @@ function dependencyDefinition(version: string): CargoManifestDependency {
return { version: version.substring(0, version.lastIndexOf('.')) }
}
async function manageDependencies(managementType: ManagementType): Promise<Result> {
async function manageDependencies(
managementType: ManagementType
): Promise<Result> {
const installedDeps = []
const updatedDeps = []
const result: Result = new Map<ManagementType, string[]>()
@ -43,12 +49,17 @@ async function manageDependencies(managementType: ManagementType): Promise<Resul
const lock = readToml<CargoLock>(lockPath)
for (const dependency of dependencies) {
const lockPackages = lock ? lock.package.filter(pkg => pkg.name === dependency) : []
const lockPackages = lock
? lock.package.filter((pkg) => pkg.name === dependency)
: []
// eslint-disable-next-line security/detect-object-injection
const manifestDep = manifest.dependencies[dependency]
const currentVersion = lockPackages.length === 1
? lockPackages[0].version
: (typeof manifestDep === 'string' ? manifestDep : manifestDep?.version)
const currentVersion =
lockPackages.length === 1
? lockPackages[0].version
: typeof manifestDep === 'string'
? manifestDep
: manifestDep?.version
if (currentVersion === undefined) {
log(`Installing ${dependency}...`)
const latestVersion = await getCrateLatestVersion(dependency)
@ -58,16 +69,22 @@ async function manageDependencies(managementType: ManagementType): Promise<Resul
} else if (managementType === ManagementType.Update) {
const latestVersion = await getCrateLatestVersion(dependency)
if (semverLt(currentVersion, latestVersion)) {
const inquired = await inquirer.prompt([{
type: 'confirm',
name: 'answer',
message: `[CRATES] "${dependency}" latest version is ${latestVersion}. Do you want to update?`,
default: false
}])
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
const inquired = await inquirer.prompt([
{
type: 'confirm',
name: 'answer',
message: `[CRATES] "${dependency}" latest version is ${latestVersion}. Do you want to update?`,
default: false
}
])
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
if (inquired.answer) {
log(`Updating ${dependency}...`)
// eslint-disable-next-line security/detect-object-injection
manifest.dependencies[dependency] = dependencyDefinition(latestVersion)
manifest.dependencies[dependency] = dependencyDefinition(
latestVersion
)
updatedDeps.push(dependency)
}
} else {
@ -79,13 +96,27 @@ async function manageDependencies(managementType: ManagementType): Promise<Resul
}
if (installedDeps.length || updatedDeps.length) {
writeFileSync(appResolve.tauri('Cargo.toml'), toml.stringify(manifest as any))
writeFileSync(
appResolve.tauri('Cargo.toml'),
toml.stringify(manifest as any)
)
}
if (updatedDeps.length) {
if (!existsSync(appResolve.tauri('Cargo.lock'))) {
spawnSync('cargo', ['generate-lockfile'], tauriDir)
}
spawnSync('cargo', ['update', '--aggressive', ...updatedDeps.reduce<string[]>((initialValue, dep) => [...initialValue, '-p', dep], [])], tauriDir)
spawnSync(
'cargo',
[
'update',
'--aggressive',
...updatedDeps.reduce<string[]>(
(initialValue, dep) => [...initialValue, '-p', dep],
[]
)
],
tauriDir
)
}
result.set(ManagementType.Install, installedDeps)
@ -102,7 +133,4 @@ async function update(): Promise<Result> {
return await manageDependencies(ManagementType.Update)
}
export {
install,
update
}
export { install, update }

View File

@ -14,7 +14,10 @@ import { existsSync } from 'fs'
const log = logger('dependency:npm-packages')
async function manageDependencies(managementType: ManagementType, dependencies: string[]): Promise<Result> {
async function manageDependencies(
managementType: ManagementType,
dependencies: string[]
): Promise<Result> {
const installedDeps = []
const updatedDeps = []
@ -32,12 +35,16 @@ async function manageDependencies(managementType: ManagementType, dependencies:
} else if (managementType === ManagementType.Update) {
const latestVersion = getNpmLatestVersion(dependency)
if (semverLt(currentVersion, latestVersion)) {
const inquired = await inquirer.prompt([{
type: 'confirm',
name: 'answer',
message: `[NPM]: "${dependency}" latest version is ${latestVersion}. Do you want to update?`,
default: false
}])
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
const inquired = await inquirer.prompt([
{
type: 'confirm',
name: 'answer',
message: `[NPM]: "${dependency}" latest version is ${latestVersion}. Do you want to update?`,
default: false
}
])
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
if (inquired.answer) {
log(`Updating ${dependency}...`)
updateNpmPackage(dependency)
@ -77,9 +84,4 @@ async function update(): Promise<Result> {
return await manageDependencies(ManagementType.Update, dependencies)
}
export {
install,
installThese,
installTheseDev,
update
}
export { install, installThese, installTheseDev, update }

View File

@ -9,27 +9,38 @@ import https from 'https'
const log = logger('dependency:rust')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function download(url: string, dest: string): Promise<void> {
const file = createWriteStream(dest)
return await new Promise((resolve, reject) => {
https.get(url, response => {
response.pipe(file)
file.on('finish', function() {
file.close()
resolve()
https
.get(url, (response) => {
response.pipe(file)
file.on('finish', function () {
file.close()
resolve()
})
})
.on('error', function (err) {
unlinkSync(dest)
reject(err.message)
})
}).on('error', function(err) {
unlinkSync(dest)
reject(err.message)
})
})
};
}
function installRustup(): void {
if (platform() === 'win32') {
return spawnSync('powershell', [resolve(__dirname, '../../scripts/rustup-init.exe')], process.cwd())
return spawnSync(
'powershell',
[resolve(__dirname, '../../scripts/rustup-init.exe')],
process.cwd()
)
}
return spawnSync('/bin/sh', [resolve(__dirname, '../../scripts/rustup-init.sh')], process.cwd())
return spawnSync(
'/bin/sh',
[resolve(__dirname, '../../scripts/rustup-init.sh')],
process.cwd()
)
}
function manageDependencies(managementType: ManagementType): void {
@ -51,7 +62,4 @@ function update(): void {
return manageDependencies(ManagementType.Update)
}
export {
install,
update
}
export { install, update }

View File

@ -23,13 +23,21 @@ async function getCrateLatestVersion(crateName: string): Promise<string> {
}
function getNpmLatestVersion(packageName: string): string {
const child = crossSpawnSync('npm', ['show', packageName, 'version'], { cwd: appDir })
const child = crossSpawnSync('npm', ['show', packageName, 'version'], {
cwd: appDir
})
return String(child.output[1]).replace('\n', '')
}
async function getNpmPackageVersion(packageName: string): Promise<string | null> {
async function getNpmPackageVersion(
packageName: string
): Promise<string | null> {
return await new Promise((resolve) => {
const child = crossSpawnSync('npm', ['list', packageName, 'version', '--depth', '0'], { cwd: appDir })
const child = crossSpawnSync(
'npm',
['list', packageName, 'version', '--depth', '0'],
{ cwd: appDir }
)
const output = String(child.output[1])
// eslint-disable-next-line security/detect-non-literal-regexp
const matches = new RegExp(packageName + '@(\\S+)', 'g').exec(output)

View File

@ -17,10 +17,14 @@ interface DevResult {
module.exports = (config: TauriConfig): DevResult => {
if (platform() === 'win32') {
const child = spawnSync('powershell', [resolve(__dirname, '../../scripts/is-admin.ps1')])
const child = spawnSync('powershell', [
resolve(__dirname, '../../scripts/is-admin.ps1')
])
const response = String(child.output[1]).replace('\n', '').trim()
if (response === 'True') {
error('Administrator privileges detected. Tauri doesn\'t work when running as admin, see https://github.com/Boscop/web-view/issues/96')
error(
"Administrator privileges detected. Tauri doesn't work when running as admin, see https://github.com/Boscop/web-view/issues/96"
)
process.exit(1)
}
}

View File

@ -1,4 +1,3 @@
import toml from '@tauri-apps/toml'
import chalk from 'chalk'
import fs from 'fs'
@ -11,7 +10,11 @@ import { CargoLock, CargoManifest } from '../types/cargo'
import nonWebpackRequire from '../helpers/non-webpack-require'
import packageJson from '../../package.json'
import getScriptVersion from '../helpers/get-script-version'
import { semverLt, getNpmLatestVersion, getCrateLatestVersion } from './dependency-manager/util'
import {
semverLt,
getNpmLatestVersion,
getCrateLatestVersion
} from './dependency-manager/util'
async function crateLatestVersion(name: string): Promise<string | undefined> {
try {
@ -39,7 +42,7 @@ function dirTree(filename: string, recurse = true): DirInfo {
if (stats.isDirectory()) {
info.type = 'folder'
if (recurse) {
info.children = fs.readdirSync(filename).map(function(child: string) {
info.children = fs.readdirSync(filename).map(function (child: string) {
return dirTree(filename + '/' + child, false)
})
}
@ -74,7 +77,7 @@ function printInfo(info: Info): void {
const suffix = info.suffix ? ` ${info.suffix}` : ''
console.log(
`${info.section ? '\n' : ''}${info.key}${
info.value === undefined ? '' : ' - ' + info.value
info.value === undefined ? '' : ' - ' + info.value
}${suffix}`
)
}
@ -87,18 +90,28 @@ interface Version {
}
function printVersion(info: Version): void {
const outdated = info.version && info.targetVersion && semverLt(info.version, info.targetVersion)
const outdated =
info.version &&
info.targetVersion &&
semverLt(info.version, info.targetVersion)
console.log(
`${info.section ? '\n' : ''}${info.key}${
info.version ? ' - ' + chalk.green(info.version) : chalk.red('Not installed')
}` + (outdated && info.targetVersion ? ` (${chalk.red('outdated, latest: ' + info.targetVersion)})` : '')
info.version
? ' - ' + chalk.green(info.version)
: chalk.red('Not installed')
}` +
(outdated && info.targetVersion
? ` (${chalk.red('outdated, latest: ' + info.targetVersion)})`
: '')
)
}
function readTomlFile<T extends CargoLock | CargoManifest>(filepath: string): T | null {
function readTomlFile<T extends CargoLock | CargoManifest>(
filepath: string
): T | null {
try {
const file = fs.readFileSync(filepath).toString()
return toml.parse(file) as unknown as T
return (toml.parse(file) as unknown) as T
} catch (_) {
return null
}
@ -109,7 +122,9 @@ async function printAppInfo(tauriDir: string): Promise<void> {
const lockPath = path.join(tauriDir, 'Cargo.lock')
const lock = readTomlFile<CargoLock>(lockPath)
const lockPackages = lock ? lock.package.filter(pkg => pkg.name === 'tauri') : []
const lockPackages = lock
? lock.package.filter((pkg) => pkg.name === 'tauri')
: []
const manifestPath = path.join(tauriDir, 'Cargo.toml')
const manifest = readTomlFile<CargoManifest>(manifestPath)
@ -123,7 +138,9 @@ async function printAppInfo(tauriDir: string): Promise<void> {
} else if (lock && lockPackages.length === 1) {
// good lockfile, but no manifest - will cause problems building
foundTauriVersions.push(lockPackages[0].version)
tauriVersion = `${chalk.green(lockPackages[0].version)} (${chalk.red('no manifest')})`
tauriVersion = `${chalk.green(lockPackages[0].version)} (${chalk.red(
'no manifest'
)})`
} else {
// we found multiple/none `tauri` packages in the lockfile, or
// no manifest. in both cases we want more info on the manifest
@ -140,7 +157,9 @@ async function printAppInfo(tauriDir: string): Promise<void> {
const manifestPath = path.resolve(tauriDir, tauri.path, 'Cargo.toml')
const manifestContent = readTomlFile<CargoManifest>(manifestPath)
let pathVersion = manifestContent?.package.version
pathVersion = pathVersion ? chalk.yellow(pathVersion) : chalk.red(pathVersion)
pathVersion = pathVersion
? chalk.yellow(pathVersion)
: chalk.red(pathVersion)
return `path:${tauri.path} [${pathVersion}]`
}
} else {
@ -151,7 +170,7 @@ async function printAppInfo(tauriDir: string): Promise<void> {
let lockVersion
if (lock && lockPackages.length > 0) {
lockVersion = chalk.yellow(lockPackages.map(p => p.version).join(', '))
lockVersion = chalk.yellow(lockPackages.map((p) => p.version).join(', '))
} else if (lock && lockPackages.length === 0) {
lockVersion = chalk.red('unknown lockfile')
} else {
@ -161,14 +180,20 @@ async function printAppInfo(tauriDir: string): Promise<void> {
tauriVersion = `${manifestVersion()} (${chalk.yellow(lockVersion)})`
}
const tauriVersionString = foundTauriVersions.reduce((old, current) => semverLt(old, current) ? current : old, '0.0.0')
const tauriVersionString = foundTauriVersions.reduce(
(old, current) => (semverLt(old, current) ? current : old),
'0.0.0'
)
const latestTauriCore = await crateLatestVersion('tauri')
printInfo({
key: ' tauri.rs',
value: tauriVersion,
suffix: tauriVersionString !== '0.0.0' && latestTauriCore && semverLt(tauriVersionString, latestTauriCore)
? `(${chalk.red('outdated, latest: ' + latestTauriCore)})`
: undefined
suffix:
tauriVersionString !== '0.0.0' &&
latestTauriCore &&
semverLt(tauriVersionString, latestTauriCore)
? `(${chalk.red('outdated, latest: ' + latestTauriCore)})`
: undefined
})
try {
@ -185,8 +210,7 @@ async function printAppInfo(tauriDir: string): Promise<void> {
printInfo({ key: ' mode', value: tauriMode(config) })
printInfo({
key: ' build-type',
value:
config.tauri.bundle?.active ? 'bundle' : 'build'
value: config.tauri.bundle?.active ? 'bundle' : 'build'
})
printInfo({
key: ' CSP',
@ -204,7 +228,7 @@ async function printAppInfo(tauriDir: string): Promise<void> {
? chalk.green(config.build.devPath)
: chalk.red('unset')
})
} catch (_) { }
} catch (_) {}
}
module.exports = async () => {
@ -216,16 +240,28 @@ module.exports = async () => {
section: true
})
if (os.platform() === 'win32') {
const { stdout } = spawn('REG', ['QUERY', 'HKEY_CLASSES_root\\AppX3xxs313wwkfjhythsb8q46xdsq8d2cvv\\Application', '/v', 'ApplicationName'])
const { stdout } = spawn('REG', [
'QUERY',
'HKEY_CLASSES_root\\AppX3xxs313wwkfjhythsb8q46xdsq8d2cvv\\Application',
'/v',
'ApplicationName'
])
const match = /{(\S+)}/g.exec(stdout.toString())
if (match) {
const edgeString = match[1]
printInfo({ key: 'Microsoft Edge', value: edgeString.split('?')[0].replace('Microsoft.MicrosoftEdge_', '') })
printInfo({
key: 'Microsoft Edge',
value: edgeString.split('?')[0].replace('Microsoft.MicrosoftEdge_', '')
})
}
}
printInfo({ key: 'Node.js environment', section: true })
printVersion({ key: ' Node.js', version: process.version.slice(1), targetVersion: packageJson.engines.node.replace('>= ', '') })
printVersion({
key: ' Node.js',
version: process.version.slice(1),
targetVersion: packageJson.engines.node.replace('>= ', '')
})
printVersion({
key: ' tauri.js',
version: packageJson.version,
@ -235,11 +271,11 @@ module.exports = async () => {
printInfo({ key: 'Rust environment', section: true })
printInfo({
key: ' rustc',
value: getVersion('rustc', [], output => output.split(' ')[1])
value: getVersion('rustc', [], (output) => output.split(' ')[1])
})
printInfo({
key: ' cargo',
value: getVersion('cargo', [], output => output.split(' ')[1])
value: getVersion('cargo', [], (output) => output.split(' ')[1])
})
printVersion({
key: ' tauri-bundler',

View File

@ -26,14 +26,19 @@ module.exports = (args: {
)
if (args.appName) {
const manifestPath = resolve(args.directory, 'src-tauri/Cargo.toml')
const cargoManifest = toml.parse(readFileSync(manifestPath).toString()) as unknown as CargoManifest
const cargoManifest = (toml.parse(
readFileSync(manifestPath).toString()
) as unknown) as CargoManifest
const binName = kebabCase(args.appName)
cargoManifest.package.name = binName
cargoManifest.package['default-run'] = binName
if (cargoManifest.bin?.length) {
cargoManifest.bin[0].name = binName
}
writeFileSync(manifestPath, toml.stringify(cargoManifest as unknown as JsonMap))
writeFileSync(
manifestPath,
toml.stringify((cargoManifest as unknown) as JsonMap)
)
}
return injectResult
}

View File

@ -20,14 +20,12 @@ const none = {
postConfiguration: (cwd: string) => {}
}
export const allRecipes: Recipe[] = [
none,
reactjs,
reactts
]
export const allRecipes: Recipe[] = [none, reactjs, reactts]
export const recipeNames: Array<[string, string]> =
map(allRecipes, (r: Recipe) => [r.shortName, r.descriptiveName])
export const recipeNames: Array<[string, string]> = map(
allRecipes,
(r: Recipe) => [r.shortName, r.descriptiveName]
)
export const recipeByShortName = (name: string): Recipe | undefined =>
find(allRecipes, (r: Recipe) => r.shortName === name)
@ -35,6 +33,12 @@ export const recipeByShortName = (name: string): Recipe | undefined =>
export const recipeByDescriptiveName = (name: string): Recipe | undefined =>
find(allRecipes, (r: Recipe) => r.descriptiveName === name)
export const recipeShortNames: string[] = map(allRecipes, (r: Recipe) => r.shortName)
export const recipeShortNames: string[] = map(
allRecipes,
(r: Recipe) => r.shortName
)
export const recipeDescriptiveNames: string[] = map(allRecipes, (r: Recipe) => r.descriptiveName)
export const recipeDescriptiveNames: string[] = map(
allRecipes,
(r: Recipe) => r.descriptiveName
)

View File

@ -1,20 +1,33 @@
import { installThese, installTheseDev } from '../dependency-manager/npm-packages'
import {
installThese,
installTheseDev
} from '../dependency-manager/npm-packages'
import { Recipe } from '.'
import { Result } from '../dependency-manager/types'
import logger from '../../helpers/logger'
export async function installRecipeDependencies(recipe: Recipe): Promise<Result> {
export async function installRecipeDependencies(
recipe: Recipe
): Promise<Result> {
const log = logger('recipe:install')
log(`Installing dependencies for ${recipe.descriptiveName}`)
return await installThese(recipe.extraNpmDependencies).then(async (results) =>
await installTheseDev(recipe.extraNpmDevDependencies).then((results2) =>
new Map([...Array.from(results.entries()), ...Array.from(results2.entries())])
)
return await installThese(recipe.extraNpmDependencies).then(
async (results) =>
await installTheseDev(recipe.extraNpmDevDependencies).then(
(results2) =>
new Map([
...Array.from(results.entries()),
...Array.from(results2.entries())
])
)
)
}
export async function runRecipePostConfig(recipe: Recipe, cwd: string): Promise<void> {
export async function runRecipePostConfig(
recipe: Recipe,
cwd: string
): Promise<void> {
const log = logger('recipe:postconfig')
log(`Running post configuration for ${recipe.descriptiveName}`)

View File

@ -45,14 +45,21 @@ const reactts: Recipe = {
...reactjs,
descriptiveName: 'React with Typescript',
shortName: 'reactts',
extraNpmDependencies: ['typescript', '@types/node', '@types/react', '@types/react-dom', '@types/jest'],
extraNpmDependencies: [
'typescript',
'@types/node',
'@types/react',
'@types/react-dom',
'@types/jest'
],
postConfiguration: (cwd: string) => {
spawnSync('yarn', ['create-react-app', '--template', 'typescript', uiAppDir], cwd)
spawnSync(
'yarn',
['create-react-app', '--template', 'typescript', uiAppDir],
cwd
)
afterCra()
}
}
export {
reactjs,
reactts
}
export { reactjs, reactts }

View File

@ -36,7 +36,7 @@ const warn = logger('app:spawn', chalk.red)
let image: boolean | sharp.Sharp = false
const spinnerInterval = false
const exists = async function(file: string | Buffer): Promise<boolean> {
const exists = async function (file: string | Buffer): Promise<boolean> {
try {
await access(file)
return true
@ -106,11 +106,11 @@ const uniqueFolders = (options: { [index: string]: any }): any[] => {
*/
const hexToRgb = (
hex: string
): { r: number, g: number, b: number } | undefined => {
): { r: number; g: number; b: number } | undefined => {
// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
hex = hex.replace(shorthandRegex, function(
hex = hex.replace(shorthandRegex, function (
m: string,
r: string,
g: string,
@ -122,10 +122,10 @@ const hexToRgb = (
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
}
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
}
: undefined
}
@ -178,14 +178,14 @@ const spinner = (): NodeJS.Timeout => {
}
const tauricon = (exports.tauricon = {
validate: async function(src: string, target: string) {
validate: async function (src: string, target: string) {
await validate(src, target)
return typeof image === 'object'
},
version: function() {
version: function () {
return version
},
make: async function(
make: async function (
src: string = path.resolve(appDir, 'app-icon.png'),
target: string = path.resolve(tauriDir, 'icons'),
strategy: string,
@ -217,7 +217,7 @@ const tauricon = (exports.tauricon = {
* @param {string} target - where to drop the images
* @param {object} options - js object that defines path and sizes
*/
build: async function(
build: async function (
src: string,
target: string,
// TODO: proper type for options
@ -225,7 +225,7 @@ const tauricon = (exports.tauricon = {
) {
await this.validate(src, target)
const sharpSrc = sharp(src) // creates the image object
const buildify2 = async function(
const buildify2 = async function (
pvar: [string, number, number]
): Promise<void> {
try {
@ -288,7 +288,7 @@ const tauricon = (exports.tauricon = {
* @param {string} target - where to drop the images
* @param {object} options - js object that defines path and sizes
*/
splash: async function(
splash: async function (
src: string,
splashSrc: string,
target: string,
@ -349,7 +349,9 @@ const tauricon = (exports.tauricon = {
background: { r: rgb.r, g: rgb.g, b: rgb.b, alpha: 1 }
})
} else {
throw new Error(`unknown options.splashscreen_type: ${options.splashscreen_type}`)
throw new Error(
`unknown options.splashscreen_type: ${options.splashscreen_type}`
)
}
const data = await sharpSrc.toBuffer()
@ -383,7 +385,7 @@ const tauricon = (exports.tauricon = {
* @param {string} strategy - which minify strategy to use
* @param {string} mode - singlefile or batch
*/
minify: async function(
minify: async function (
target: string,
// TODO: proper type for options
options: { [index: string]: any },
@ -392,13 +394,13 @@ const tauricon = (exports.tauricon = {
) {
let cmd: Plugin
const minify = settings.options.minify
if (!minify.available.find(x => x === strategy)) {
if (!minify.available.find((x) => x === strategy)) {
strategy = minify.type
}
switch (strategy) {
case 'pngquant':
// TODO: is minify.pngquantOptions the proper format?
cmd = pngquant(minify.pngquantOptions as any as PngQuantOptions)
cmd = pngquant((minify.pngquantOptions as any) as PngQuantOptions)
break
case 'optipng':
cmd = optipng(minify.optipngOptions)
@ -414,7 +416,7 @@ const tauricon = (exports.tauricon = {
await imagemin([pvar[0]], {
destination: pvar[1],
plugins: [cmd]
}).catch(err => {
}).catch((err) => {
warn(err)
})
}
@ -454,7 +456,7 @@ const tauricon = (exports.tauricon = {
* @param {object} options
* @param {string} strategy
*/
icns: async function(
icns: async function (
src: string,
target: string,
// TODO: proper type for options

View File

@ -6,9 +6,7 @@ import chalk from 'chalk'
const warn = logger('tauri', chalk.red)
function resolvePath(basePath: string, dir: string): string {
return dir && isAbsolute(dir)
? dir
: resolve(basePath, dir)
return dir && isAbsolute(dir) ? dir : resolve(basePath, dir)
}
const getAppDir = (): string => {
@ -24,7 +22,9 @@ const getAppDir = (): string => {
dir = normalize(join(dir, '..'))
}
warn('Couldn\'t find recognize the current folder as a part of a Tauri project')
warn(
"Couldn't find recognize the current folder as a part of a Tauri project"
)
process.exit(1)
}

View File

@ -21,7 +21,7 @@ const copyTemplates = ({
for (const rawPath of files) {
const targetRelativePath = rawPath
.split('/')
.map(name => {
.map((name) => {
// dotfiles are ignored when published to npm, therefore in templates
// we need to use underscore instead (e.g. "_gitignore")
if (name.startsWith('_') && name.charAt(1) !== '_') {

View File

@ -2,22 +2,25 @@
import net from 'net'
async function findClosestOpenPort(port: number, host: string): Promise<number> {
return await isPortAvailable(port, host)
.then(isAvailable => {
if (isAvailable) {
return port
} else if (port < 65535) {
return findClosestOpenPort(port + 1, host)
} else {
throw new Error('ERROR_NETWORK_PORT_NOT_AVAIL')
}
})
async function findClosestOpenPort(
port: number,
host: string
): Promise<number> {
return await isPortAvailable(port, host).then((isAvailable) => {
if (isAvailable) {
return port
} else if (port < 65535) {
return findClosestOpenPort(port + 1, host)
} else {
throw new Error('ERROR_NETWORK_PORT_NOT_AVAIL')
}
})
}
async function isPortAvailable(port: number, host: string): Promise<boolean> {
return await new Promise((resolve, reject) => {
const tester = net.createServer()
const tester = net
.createServer()
.once('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EADDRNOTAVAIL') {
reject(new Error('ERROR_NETWORK_ADDRESS_NOT_AVAIL'))
@ -28,9 +31,10 @@ async function isPortAvailable(port: number, host: string): Promise<boolean> {
}
})
.once('listening', () => {
tester.once('close', () => {
resolve(true) // found available host/port
})
tester
.once('close', () => {
resolve(true) // found available host/port
})
.close()
})
.on('error', (err: any) => {
@ -40,7 +44,4 @@ async function isPortAvailable(port: number, host: string): Promise<boolean> {
})
}
export {
findClosestOpenPort,
isPortAvailable
}
export { findClosestOpenPort, isPortAvailable }

View File

@ -24,7 +24,7 @@ export const spawn = (
env: process.env
})
runner.on('close', code => {
runner.on('close', (code) => {
log()
if (code) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions

View File

@ -18,8 +18,12 @@ const getTauriConfig = (cfg: Partial<TauriConfig>): TauriConfig => {
)
process.exit(1)
}
const tauriConf = JSON.parse(readFileSync(tauriConfPath).toString()) as TauriConfig
const pkg = existsSync(pkgPath) ? nonWebpackRequire(pkgPath) as { productName: string } : null
const tauriConf = JSON.parse(
readFileSync(tauriConfPath).toString()
) as TauriConfig
const pkg = existsSync(pkgPath)
? (nonWebpackRequire(pkgPath) as { productName: string })
: null
const config = merge(
{
@ -48,7 +52,8 @@ const getTauriConfig = (cfg: Partial<TauriConfig>): TauriConfig => {
title: pkg?.productName ?? 'Tauri App'
},
security: {
csp: "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'"
csp:
"default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'"
},
inliner: {
active: true
@ -60,15 +65,24 @@ const getTauriConfig = (cfg: Partial<TauriConfig>): TauriConfig => {
) as TauriConfig
if (!isTauriConfig(config)) {
const messages = ajv.errorsText(
isTauriConfig.errors?.filter(e => e.keyword !== 'if').map(e => {
e.dataPath = e.dataPath.replace(/\./g, ' > ')
if (e.keyword === 'additionalProperties' && typeof e.message === 'string' && 'additionalProperty' in e.params) {
e.message = `has unknown property ${e.params.additionalProperty}`
}
return e
}), { dataVar: 'tauri.conf.json', separator: '\n' }
).split('\n')
const messages = ajv
.errorsText(
isTauriConfig.errors
?.filter((e) => e.keyword !== 'if')
.map((e) => {
e.dataPath = e.dataPath.replace(/\./g, ' > ')
if (
e.keyword === 'additionalProperties' &&
typeof e.message === 'string' &&
'additionalProperty' in e.params
) {
e.message = `has unknown property ${e.params.additionalProperty}`
}
return e
}),
{ dataVar: 'tauri.conf.json', separator: '\n' }
)
.split('\n')
for (const message of messages) {
error(message)
@ -102,7 +116,9 @@ const getTauriConfig = (cfg: Partial<TauriConfig>): TauriConfig => {
// bundle targets
if (Array.isArray(config.tauri.bundle.targets)) {
if (process.platform !== 'win32') {
config.tauri.bundle.targets = config.tauri.bundle.targets.filter(t => t !== 'msi')
config.tauri.bundle.targets = config.tauri.bundle.targets.filter(
(t) => t !== 'msi'
)
}
}

View File

@ -39,7 +39,7 @@ class Runner {
this.pid = 0
this.tauriWatcher = undefined
onShutdown(() => {
this.stop().catch(e => {
this.stop().catch((e) => {
throw e
})
})
@ -56,14 +56,18 @@ class Runner {
if (!this.beforeDevProcess && cfg.build.beforeDevCommand) {
log('Running `' + cfg.build.beforeDevCommand + '`')
const ls = exec(cfg.build.beforeDevCommand, {
cwd: appDir,
env: process.env
}, error => {
if (error) {
process.exit(1)
const ls = exec(
cfg.build.beforeDevCommand,
{
cwd: appDir,
env: process.env
},
(error) => {
if (error) {
process.exit(1)
}
}
})
)
ls.stderr?.pipe(process.stderr)
ls.stdout?.pipe(process.stdout)
@ -73,19 +77,23 @@ class Runner {
const devTryTimeout = 3000
while (!(await isReachable(devPath))) {
log('Waiting for your dev server to start...')
await new Promise(resolve => setTimeout(resolve, devTryTimeout))
await new Promise((resolve) => setTimeout(resolve, devTryTimeout))
devTryCount++
if (devTryCount === 10) {
warn(`Couldn't connect to ${devPath} after ${devTryTimeout * devTryCount / 1000}s. Please make sure that's the URL to your dev server.`)
warn(
`Couldn't connect to ${devPath} after ${
(devTryTimeout * devTryCount) / 1000
}s. Please make sure that's the URL to your dev server.`
)
process.exit(1)
}
}
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const cargoManifest = this.__getManifest() as any as CargoManifest
const cargoManifest = (this.__getManifest() as any) as CargoManifest
this.__allowlistApi(cfg, cargoManifest)
this.__rewriteManifest(cargoManifest as unknown as toml.JsonMap)
this.__rewriteManifest((cargoManifest as unknown) as toml.JsonMap)
const runningDevServer = devPath.startsWith('http')
@ -104,51 +112,67 @@ class Runner {
selfHandleResponse: true
})
proxy.on('proxyRes', (proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => {
if (req.url === '/') {
const body: Uint8Array[] = []
proxyRes.on('data', (chunk: Uint8Array) => {
body.push(chunk)
})
proxyRes.on('end', () => {
const bodyStr = body.join('')
const indexDir = os.tmpdir()
writeFileSync(path.join(indexDir, 'index.html'), bodyStr)
self.__parseHtml(cfg, indexDir, false)
.then(({ html }) => {
const headers: { [key: string]: string } = {}
if (proxyRes.headers['content-type']) {
headers['content-type'] = proxyRes.headers['content-type']
} else {
const charsetMatch = /charset="(\S+)"/g.exec(bodyStr)
if (charsetMatch) {
headers['content-type'] = `'text/html; charset=${charsetMatch[1]}`
proxy.on(
'proxyRes',
(
proxyRes: http.IncomingMessage,
req: http.IncomingMessage,
res: http.ServerResponse
) => {
if (req.url === '/') {
const body: Uint8Array[] = []
proxyRes.on('data', (chunk: Uint8Array) => {
body.push(chunk)
})
proxyRes.on('end', () => {
const bodyStr = body.join('')
const indexDir = os.tmpdir()
writeFileSync(path.join(indexDir, 'index.html'), bodyStr)
self
.__parseHtml(cfg, indexDir, false)
.then(({ html }) => {
const headers: { [key: string]: string } = {}
if (proxyRes.headers['content-type']) {
headers['content-type'] = proxyRes.headers['content-type']
} else {
const charsetMatch = /charset="(\S+)"/g.exec(bodyStr)
if (charsetMatch) {
headers[
'content-type'
] = `'text/html; charset=${charsetMatch[1]}`
}
}
}
res.writeHead(200, headers)
res.end(html)
}).catch(err => {
res.writeHead(500, JSON.stringify(err))
res.end()
})
})
} else {
if (proxyRes.statusCode) {
res = res.writeHead(proxyRes.statusCode, proxyRes.headers)
res.writeHead(200, headers)
res.end(html)
})
.catch((err) => {
res.writeHead(500, JSON.stringify(err))
res.end()
})
})
} else {
if (proxyRes.statusCode) {
res = res.writeHead(proxyRes.statusCode, proxyRes.headers)
}
proxyRes.pipe(res)
}
proxyRes.pipe(res)
}
})
)
proxy.on('error', (error: Error, _: http.IncomingMessage, res: http.ServerResponse) => {
if (error.message?.includes('ECONNREFUSED')) {
warn(`Connection refused to ${devUrl.protocol}//${devUrl.host}. Did you start your dev server? Usually that's done with a \`dev\` or \`serve\` NPM script.`)
} else {
console.error(error)
proxy.on(
'error',
(error: Error, _: http.IncomingMessage, res: http.ServerResponse) => {
if (error.message?.includes('ECONNREFUSED')) {
warn(
`Connection refused to ${devUrl.protocol}//${devUrl.host}. Did you start your dev server? Usually that's done with a \`dev\` or \`serve\` NPM script.`
)
} else {
console.error(error)
}
res.writeHead(500, error.message)
}
res.writeHead(500, error.message)
})
)
const proxyServer = http.createServer((req, res) => {
delete req.headers['accept-encoding']
@ -158,7 +182,10 @@ class Runner {
proxy.ws(req, socket, head)
})
const port = await findClosestOpenPort(parseInt(devUrl.port) + 1, devUrl.hostname)
const port = await findClosestOpenPort(
parseInt(devUrl.port) + 1,
devUrl.hostname
)
const devServer = proxyServer.listen(port)
this.devServer = devServer
devPath = `${devUrl.protocol}//localhost:${port}`
@ -184,8 +211,14 @@ class Runner {
// eslint-disable-next-line security/detect-non-literal-fs-filename
let tauriPaths: string[] = []
if (typeof cargoManifest.dependencies.tauri !== 'string' && cargoManifest.dependencies.tauri.path) {
const tauriPath = path.resolve(tauriDir, cargoManifest.dependencies.tauri.path)
if (
typeof cargoManifest.dependencies.tauri !== 'string' &&
cargoManifest.dependencies.tauri.path
) {
const tauriPath = path.resolve(
tauriDir,
cargoManifest.dependencies.tauri.path
)
tauriPaths = [
tauriPath,
`${tauriPath}-api`,
@ -207,30 +240,36 @@ class Runner {
].concat(runningDevServer ? [] : [devPath]),
{
ignoreInitial: true,
ignored: [runningDevServer ? null : path.join(devPath, 'index.tauri.html')].concat(path.join(tauriDir, 'target'))
ignored: [
runningDevServer ? null : path.join(devPath, 'index.tauri.html')
].concat(path.join(tauriDir, 'target'))
}
)
.on(
'change',
debounce((changedPath: string) => {
if (this.rewritingToml && changedPath.startsWith(path.join(tauriDir, 'Cargo.toml'))) {
if (
this.rewritingToml &&
changedPath.startsWith(path.join(tauriDir, 'Cargo.toml'))
) {
return
}
(this.pid ? this.__stopCargo() : Promise.resolve())
;(this.pid ? this.__stopCargo() : Promise.resolve())
.then(() => {
const shouldTriggerRun = changedPath.includes('tauri.conf.json') ||
const shouldTriggerRun =
changedPath.includes('tauri.conf.json') ||
changedPath.startsWith(devPath)
if (shouldTriggerRun) {
this.run(getTauriConfig({ ctx: cfg.ctx })).catch(e => {
this.run(getTauriConfig({ ctx: cfg.ctx })).catch((e) => {
throw e
})
} else {
startDevTauri().catch(e => {
startDevTauri().catch((e) => {
throw e
})
}
})
.catch(err => {
.catch((err) => {
warn(err)
process.exit(1)
})
@ -248,10 +287,11 @@ class Runner {
}
const cargoManifest = this.__getManifest()
this.__allowlistApi(cfg, cargoManifest as unknown as CargoManifest)
this.__allowlistApi(cfg, (cargoManifest as unknown) as CargoManifest)
this.__rewriteManifest(cargoManifest)
const inlinedAssets = (await this.__parseHtml(cfg, cfg.build.distDir)).inlinedAssets
const inlinedAssets = (await this.__parseHtml(cfg, cfg.build.distDir))
.inlinedAssets
process.env.TAURI_INLINED_ASSETS = inlinedAssets.join('|')
const features = [
@ -264,11 +304,11 @@ class Runner {
cfg.tauri.bundle.active ? 'tauri-bundler' : 'build',
'--features',
...features,
...(
cfg.tauri.bundle.active && Array.isArray(cfg.tauri.bundle.targets) && cfg.tauri.bundle.targets.length
? ['--format'].concat(cfg.tauri.bundle.targets)
: []
)
...(cfg.tauri.bundle.active &&
Array.isArray(cfg.tauri.bundle.targets) &&
cfg.tauri.bundle.targets.length
? ['--format'].concat(cfg.tauri.bundle.targets)
: [])
]
.concat(cfg.ctx.debug ? [] : ['--release'])
.concat(cfg.verbose ? ['--verbose'] : [])
@ -288,7 +328,11 @@ class Runner {
}
}
async __parseHtml(cfg: TauriConfig, indexDir: string, inlinerEnabled = cfg.tauri.inliner.active): Promise<{ inlinedAssets: string[], html: string }> {
async __parseHtml(
cfg: TauriConfig,
indexDir: string,
inlinerEnabled = cfg.tauri.inliner.active
): Promise<{ inlinedAssets: string[]; html: string }> {
const inlinedAssets: string[] = []
return await new Promise((resolve, reject) => {
@ -301,17 +345,29 @@ class Runner {
}
const originalHtml = readFileSync(indexPath).toString()
const rewriteHtml = (html: string, interceptor?: (dom: JSDOM) => void): string => {
const rewriteHtml = (
html: string,
interceptor?: (dom: JSDOM) => void
): string => {
const dom = new JSDOM(html)
const document = dom.window.document
if (interceptor !== undefined) {
interceptor(dom)
}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (!((cfg.ctx.dev && cfg.build.devPath.startsWith('http')) || cfg.tauri.embeddedServer.active)) {
if (
!(
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
(
(cfg.ctx.dev && cfg.build.devPath.startsWith('http')) ||
cfg.tauri.embeddedServer.active
)
/* eslint-enable */
)
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
const mutationObserverTemplate = require('../templates/mutation-observer').default
const mutationObserverTemplate = require('../templates/mutation-observer')
.default
const compiledMutationObserver = template(mutationObserverTemplate)
const bodyMutationObserverScript = document.createElement('script')
@ -319,14 +375,20 @@ class Runner {
target: 'body',
inlinedAssets: JSON.stringify(inlinedAssets)
})
document.body.insertBefore(bodyMutationObserverScript, document.body.firstChild)
document.body.insertBefore(
bodyMutationObserverScript,
document.body.firstChild
)
const headMutationObserverScript = document.createElement('script')
headMutationObserverScript.text = compiledMutationObserver({
target: 'head',
inlinedAssets: JSON.stringify(inlinedAssets)
})
document.head.insertBefore(headMutationObserverScript, document.head.firstChild)
document.head.insertBefore(
headMutationObserverScript,
document.head.firstChild
)
}
const tauriScript = document.createElement('script')
@ -350,40 +412,47 @@ class Runner {
}
const newHtml = dom.serialize()
writeFileSync(
path.join(indexDir, 'index.tauri.html'),
newHtml
)
writeFileSync(path.join(indexDir, 'index.tauri.html'), newHtml)
return newHtml
}
const domInterceptor = cfg.tauri.embeddedServer.active ? undefined : (dom: JSDOM) => {
const document = dom.window.document
if (!cfg.ctx.dev) {
document.querySelectorAll('link').forEach((link: HTMLLinkElement) => {
link.removeAttribute('rel')
link.removeAttribute('as')
})
}
}
const domInterceptor = cfg.tauri.embeddedServer.active
? undefined
: (dom: JSDOM) => {
const document = dom.window.document
if (!cfg.ctx.dev) {
document
.querySelectorAll('link')
.forEach((link: HTMLLinkElement) => {
link.removeAttribute('rel')
link.removeAttribute('as')
})
}
}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if ((!cfg.ctx.dev && cfg.tauri.embeddedServer.active) || !inlinerEnabled) {
if (
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(!cfg.ctx.dev && cfg.tauri.embeddedServer.active) ||
!inlinerEnabled
) {
const html = rewriteHtml(originalHtml, domInterceptor)
resolve({ inlinedAssets, html })
} else {
const cwd = process.cwd()
process.chdir(indexDir) // the inliner requires this to properly work
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
const inliner = new Inliner({ source: originalHtml }, (err: Error, html: string) => {
process.chdir(cwd) // reset CWD
if (err) {
reject(err)
} else {
const rewrittenHtml = rewriteHtml(html, domInterceptor)
resolve({ inlinedAssets, html: rewrittenHtml })
const inliner = new Inliner(
{ source: originalHtml },
(err: Error, html: string) => {
process.chdir(cwd) // reset CWD
if (err) {
reject(err)
} else {
const rewrittenHtml = rewriteHtml(html, domInterceptor)
resolve({ inlinedAssets, html: rewrittenHtml })
}
}
})
)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
inliner.on('progress', (event: string) => {
const match = event.match(/([\S\d]+)\.([\S\d]+)/g)
@ -397,9 +466,7 @@ class Runner {
return await new Promise((resolve, reject) => {
this.devServer?.close()
this.tauriWatcher?.close().catch(reject)
this.__stopCargo()
.then(resolve)
.catch(reject)
this.__stopCargo().then(resolve).catch(reject)
})
}
@ -445,7 +512,9 @@ class Runner {
warn()
warn('⚠️ [FAIL] Cargo CLI has failed')
warn()
reject(new Error('Cargo failed with status code ' + code.toString()))
reject(
new Error('Cargo failed with status code ' + code.toString())
)
process.exit(1)
} else if (!dev) {
resolve()
@ -508,22 +577,20 @@ class Runner {
}, WATCHER_INTERVAL * 2)
}
__allowlistApi(
cfg: TauriConfig,
manifest: CargoManifest
): void {
__allowlistApi(cfg: TauriConfig, manifest: CargoManifest): void {
const tomlFeatures = []
if (cfg.tauri.allowlist.all) {
tomlFeatures.push('all-api')
} else {
const toKebabCase = (value: string): string => {
return value.replace(/([a-z])([A-Z])/g, '$1-$2')
return value
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/\s+/g, '-')
.toLowerCase()
}
const allowlist = Object.keys(cfg.tauri.allowlist).filter(
w => cfg.tauri.allowlist[String(w)]
(w) => cfg.tauri.allowlist[String(w)]
)
tomlFeatures.push(...allowlist.map(toKebabCase))
}

View File

@ -14,7 +14,13 @@ export default {
active: true,
targets: 'all', // or an array of targets
identifier: 'com.tauri.dev',
icon: ['icons/32x32.png', 'icons/128x128.png', 'icons/128x128@2x.png', 'icons/icon.icns', 'icons/icon.ico'],
icon: [
'icons/32x32.png',
'icons/128x128.png',
'icons/128x128@2x.png',
'icons/icon.icns',
'icons/icon.ico'
],
resources: [],
externalBin: [],
copyright: '',
@ -43,7 +49,8 @@ export default {
fullscreen: false
},
security: {
csp: "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'"
csp:
"default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'"
},
inliner: {
active: true

View File

@ -34,7 +34,7 @@ const injectConfFile = (
if (!force) return false
} else {
removeSync(path)
Object.keys(defaultConfig).forEach(key => {
Object.keys(defaultConfig).forEach((key) => {
// Options marked `null` should be removed
/* eslint-disable security/detect-object-injection */
if ((customConfig as UnknownObject)[key] === null) {
@ -45,7 +45,10 @@ const injectConfFile = (
}
/* eslint-enable security/detect-object-injection */
})
const finalConf = merge(defaultConfig as any, customConfig as any) as UnknownObject
const finalConf = merge(
defaultConfig as any,
customConfig as any
) as UnknownObject
writeFileSync(path, JSON.stringify(finalConf, undefined, 2))
if (logging) log('Successfully wrote tauri.conf.json')
@ -64,9 +67,10 @@ Run \`tauri init --force template\` to overwrite.`)
}
const resolveTauriPath = (tauriPath: string): string => {
const resolvedPath = tauriPath.startsWith('/') || /^\S:/g.test(tauriPath)
? join(tauriPath, 'tauri') // we received a full path as argument
: join('..', tauriPath, 'tauri') // we received a relative path
const resolvedPath =
tauriPath.startsWith('/') || /^\S:/g.test(tauriPath)
? join(tauriPath, 'tauri') // we received a full path as argument
: join('..', tauriPath, 'tauri') // we received a relative path
return resolvedPath.replace(/\\/g, '/')
}
@ -106,7 +110,11 @@ const inject = (
injectTemplate(injectPath, { force, logging, tauriPath })
}
if (type === 'conf' || type === 'all') {
injectConfFile(join(injectPath, 'src-tauri'), { force, logging }, customConfig)
injectConfFile(
join(injectPath, 'src-tauri'),
{ force, logging },
customConfig
)
}
return true
}

View File

@ -1,6 +1,6 @@
export interface CargoManifest {
dependencies: { [k: string]: string | CargoManifestDependency }
package: { version: string, name: string, 'default-run': string }
package: { version: string; name: string; 'default-run': string }
bin: Array<{
name: string
path: string

View File

@ -1,13 +1,11 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"definitions": {
"CliArg": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"description": "A CLI argument definition",
"properties": {
"conflictsWith": {
@ -149,15 +147,12 @@
"type": "boolean"
}
},
"required": [
"name"
],
"required": ["name"],
"type": "object"
},
"CliConfig": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"description": "describes a CLI configuration",
"properties": {
"afterHelp": {
@ -187,8 +182,7 @@
"additionalProperties": {
"$ref": "#/definitions/CliConfig"
},
"defaultProperties": [
],
"defaultProperties": [],
"description": "list of subcommands of this command\n\nsubcommands are effectively sub-apps, because they can contain their own arguments, subcommands, usage, etc.\nthey also function just like the app command, in that they get their own auto generated help and usage",
"type": "object"
}
@ -197,8 +191,7 @@
},
"TauriBuildConfig": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"properties": {
"beforeBuildCommand": {
"description": "a shell command to run before `tauri build` kicks in",
@ -220,10 +213,7 @@
"type": "boolean"
}
},
"required": [
"devPath",
"distDir"
],
"required": ["devPath", "distDir"],
"type": "object"
}
},
@ -235,8 +225,7 @@
},
"ctx": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"description": "the context of the current `tauri dev` or `tauri build`",
"properties": {
"debug": {
@ -264,42 +253,34 @@
},
"plugins": {
"additionalProperties": {
"additionalProperties": {
},
"defaultProperties": [
],
"additionalProperties": {},
"defaultProperties": [],
"type": "object"
},
"defaultProperties": [
],
"defaultProperties": [],
"type": "object"
},
"tauri": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"description": "tauri root configuration object",
"properties": {
"allowlist": {
"additionalProperties": {
"type": "boolean"
},
"defaultProperties": [
],
"defaultProperties": [],
"properties": {
"all": {
"type": "boolean"
}
},
"required": [
"all"
],
"required": ["all"],
"type": "object"
},
"bundle": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"description": "tauri bundler configuration",
"properties": {
"active": {
@ -314,8 +295,7 @@
},
"deb": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"properties": {
"depends": {
"items": {
@ -354,8 +334,7 @@
},
"osx": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"properties": {
"frameworks": {
"items": {
@ -400,10 +379,7 @@
"description": "the bundle targets, currently supports [\"deb\", \"osx\", \"msi\", \"appimage\", \"dmg\"] or \"all\""
}
},
"required": [
"icon",
"identifier"
],
"required": ["icon", "identifier"],
"type": "object"
},
"cli": {
@ -412,8 +388,7 @@
},
"embeddedServer": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"description": "the embedded server configuration",
"properties": {
"active": {
@ -423,9 +398,7 @@
"port": {
"anyOf": [
{
"enum": [
"random"
],
"enum": ["random"],
"type": "string"
},
{
@ -439,8 +412,7 @@
},
"inliner": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"properties": {
"active": {
"type": "boolean"
@ -450,8 +422,7 @@
},
"security": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"properties": {
"csp": {
"type": "string"
@ -461,8 +432,7 @@
},
"window": {
"additionalProperties": false,
"defaultProperties": [
],
"defaultProperties": [],
"properties": {
"fullscreen": {
"type": "boolean"
@ -480,9 +450,7 @@
"type": "number"
}
},
"required": [
"title"
],
"required": ["title"],
"type": "object"
}
},
@ -501,10 +469,6 @@
"type": "boolean"
}
},
"required": [
"build",
"ctx",
"tauri"
],
"required": ["build", "ctx", "tauri"],
"type": "object"
}
}

View File

@ -1,533 +1,545 @@
/* tslint:disable */
// generated by typescript-json-validator
import {inspect} from 'util';
import Ajv from 'ajv';
import TauriConfig from './config';
export const ajv = new Ajv({"allErrors":true,"coerceTypes":false,"format":"fast","nullable":true,"unicode":true,"uniqueItems":true,"useDefaults":true});
import { inspect } from 'util'
import Ajv from 'ajv'
import TauriConfig from './config'
export const ajv = new Ajv({
allErrors: true,
coerceTypes: false,
format: 'fast',
nullable: true,
unicode: true,
uniqueItems: true,
useDefaults: true
})
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'))
export {TauriConfig};
export { TauriConfig }
export const TauriConfigSchema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"defaultProperties": [
],
"definitions": {
"CliArg": {
"additionalProperties": false,
"defaultProperties": [
],
"description": "A CLI argument definition",
"properties": {
"conflictsWith": {
"description": "sets a conflicting argument by name\ni.e. when using this argument, the following argument can't be present and vice versa",
"type": "string"
$schema: 'http://json-schema.org/draft-07/schema#',
additionalProperties: false,
defaultProperties: [],
definitions: {
CliArg: {
additionalProperties: false,
defaultProperties: [],
description: 'A CLI argument definition',
properties: {
conflictsWith: {
description:
"sets a conflicting argument by name\ni.e. when using this argument, the following argument can't be present and vice versa",
type: 'string'
},
"conflictsWithAll": {
"description": "the same as conflictsWith but allows specifying multiple two-way conflicts per argument",
"type": "string"
conflictsWithAll: {
description:
'the same as conflictsWith but allows specifying multiple two-way conflicts per argument',
type: 'string'
},
"description": {
"description": "the argument description which will be shown on the help information\ntypically, this is a short (one line) description of the arg",
"type": "string"
description: {
description:
'the argument description which will be shown on the help information\ntypically, this is a short (one line) description of the arg',
type: 'string'
},
"index": {
"description": "The positional argument index, starting at 1.\n\nThe index refers to position according to other positional argument.\nIt does not define position in the argument list as a whole. When utilized with multiple=true,\nonly the last positional argument may be defined as multiple (i.e. the one with the highest index).",
"type": "number"
index: {
description:
'The positional argument index, starting at 1.\n\nThe index refers to position according to other positional argument.\nIt does not define position in the argument list as a whole. When utilized with multiple=true,\nonly the last positional argument may be defined as multiple (i.e. the one with the highest index).',
type: 'number'
},
"longDescription": {
"description": "the argument long description which will be shown on the help information\ntypically this a more detailed (multi-line) message that describes the argument",
"type": "string"
longDescription: {
description:
'the argument long description which will be shown on the help information\ntypically this a more detailed (multi-line) message that describes the argument',
type: 'string'
},
"maxValues": {
"description": "specifies the maximum number of values are for this argument.\nfor example, if you had a -f <file> argument where you wanted up to 3 'files' you would set .max_values(3), and this argument would be satisfied if the user provided, 1, 2, or 3 values.",
"type": "number"
maxValues: {
description:
"specifies the maximum number of values are for this argument.\nfor example, if you had a -f <file> argument where you wanted up to 3 'files' you would set .max_values(3), and this argument would be satisfied if the user provided, 1, 2, or 3 values.",
type: 'number'
},
"minValues": {
"description": "specifies the minimum number of values for this argument.\nfor example, if you had a -f <file> argument where you wanted at least 2 'files' you would set `minValues: 2`, and this argument would be satisfied if the user provided, 2 or more values.",
"type": "number"
minValues: {
description:
"specifies the minimum number of values for this argument.\nfor example, if you had a -f <file> argument where you wanted at least 2 'files' you would set `minValues: 2`, and this argument would be satisfied if the user provided, 2 or more values.",
type: 'number'
},
"multiple": {
"description": "specifies that the argument may appear more than once.\nfor flags, this results in the number of occurrences of the flag being recorded. For example -ddd or -d -d -d would count as three occurrences.\nfor options there is a distinct difference in multiple occurrences vs multiple values. For example, --opt val1 val2 is one occurrence, but two values. Whereas --opt val1 --opt val2 is two occurrences.",
"type": "boolean"
multiple: {
description:
'specifies that the argument may appear more than once.\nfor flags, this results in the number of occurrences of the flag being recorded. For example -ddd or -d -d -d would count as three occurrences.\nfor options there is a distinct difference in multiple occurrences vs multiple values. For example, --opt val1 val2 is one occurrence, but two values. Whereas --opt val1 --opt val2 is two occurrences.',
type: 'boolean'
},
"multipleOccurrences": {
"description": "specifies that the argument may appear more than once.",
"type": "boolean"
multipleOccurrences: {
description: 'specifies that the argument may appear more than once.',
type: 'boolean'
},
"name": {
"description": "the unique argument name",
"type": "string"
name: {
description: 'the unique argument name',
type: 'string'
},
"possibleValues": {
"description": "specifies a list of possible values for this argument. At runtime, the CLI verifies that only one of the specified values was used, or fails with an error message.",
"items": {
"type": "string"
possibleValues: {
description:
'specifies a list of possible values for this argument. At runtime, the CLI verifies that only one of the specified values was used, or fails with an error message.',
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"requireEquals": {
"description": "requires that options use the --option=val syntax\ni.e. an equals between the option and associated value",
"type": "boolean"
requireEquals: {
description:
'requires that options use the --option=val syntax\ni.e. an equals between the option and associated value',
type: 'boolean'
},
"required": {
"description": "sets whether or not the argument is required by default\nrequired by default means it is required, when no other conflicting rules have been evaluated\nconflicting rules take precedence over being required.",
"type": "boolean"
required: {
description:
'sets whether or not the argument is required by default\nrequired by default means it is required, when no other conflicting rules have been evaluated\nconflicting rules take precedence over being required.',
type: 'boolean'
},
"requiredIf": {
"additionalItems": {
"anyOf": [
requiredIf: {
additionalItems: {
anyOf: [
{
"type": "string"
type: 'string'
},
{
"type": "string"
type: 'string'
}
]
},
"description": "allows specifying that an argument is required conditionally with the signature [arg: string, value: string]\nthe requirement will only become valid if the `arg`'s value equals `${value}`.",
"items": [
description:
"allows specifying that an argument is required conditionally with the signature [arg: string, value: string]\nthe requirement will only become valid if the `arg`'s value equals `${value}`.",
items: [
{
"type": "string"
type: 'string'
},
{
"type": "string"
type: 'string'
}
],
"minItems": 2,
"type": "array"
minItems: 2,
type: 'array'
},
"requiredUnless": {
"description": "sets an arg that override this arg's required setting\ni.e. this arg will be required unless this other argument is present",
"type": "string"
requiredUnless: {
description:
"sets an arg that override this arg's required setting\ni.e. this arg will be required unless this other argument is present",
type: 'string'
},
"requiredUnlessAll": {
"description": "sets args that override this arg's required setting\ni.e. this arg will be required unless all these other arguments are present",
"items": {
"type": "string"
requiredUnlessAll: {
description:
"sets args that override this arg's required setting\ni.e. this arg will be required unless all these other arguments are present",
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"requiredUnlessOne": {
"description": "sets args that override this arg's required setting\ni.e. this arg will be required unless at least one of these other arguments are present",
"items": {
"type": "string"
requiredUnlessOne: {
description:
"sets args that override this arg's required setting\ni.e. this arg will be required unless at least one of these other arguments are present",
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"requires": {
"description": "sets an argument by name that is required when this one is present\ni.e. when using this argument, the following argument must be present",
"type": "string"
requires: {
description:
'sets an argument by name that is required when this one is present\ni.e. when using this argument, the following argument must be present',
type: 'string'
},
"requiresAll": {
"description": "sets multiple arguments by names that are required when this one is present\ni.e. when using this argument, the following arguments must be present",
"items": {
"type": "string"
requiresAll: {
description:
'sets multiple arguments by names that are required when this one is present\ni.e. when using this argument, the following arguments must be present',
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"requiresIf": {
"additionalItems": {
"anyOf": [
requiresIf: {
additionalItems: {
anyOf: [
{
"type": "string"
type: 'string'
},
{
"type": "string"
type: 'string'
}
]
},
"description": "allows a conditional requirement with the signature [arg: string, value: string]\nthe requirement will only become valid if `arg`'s value equals `${value}`",
"items": [
description:
"allows a conditional requirement with the signature [arg: string, value: string]\nthe requirement will only become valid if `arg`'s value equals `${value}`",
items: [
{
"type": "string"
type: 'string'
},
{
"type": "string"
type: 'string'
}
],
"minItems": 2,
"type": "array"
minItems: 2,
type: 'array'
},
"short": {
"description": "the short version of the argument, without the preceding -\nNOTE: Any leading - characters will be stripped, and only the first non - character will be used as the short version",
"type": "string"
short: {
description:
'the short version of the argument, without the preceding -\nNOTE: Any leading - characters will be stripped, and only the first non - character will be used as the short version',
type: 'string'
},
"takesValue": {
"description": "specifies that the argument takes a value at run time.\nNOTE: values for arguments may be specified in any of the following methods\n- Using a space such as -o value or --option value\n- Using an equals and no space such as -o=value or --option=value\n- Use a short and no space such as -ovalue",
"type": "boolean"
takesValue: {
description:
'specifies that the argument takes a value at run time.\nNOTE: values for arguments may be specified in any of the following methods\n- Using a space such as -o value or --option value\n- Using an equals and no space such as -o=value or --option=value\n- Use a short and no space such as -ovalue',
type: 'boolean'
}
},
"required": [
"name"
],
"type": "object"
required: ['name'],
type: 'object'
},
"CliConfig": {
"additionalProperties": false,
"defaultProperties": [
],
"description": "describes a CLI configuration",
"properties": {
"afterHelp": {
"description": "adds additional help information to be displayed in addition to auto-generated help\nthis information is displayed after the auto-generated help information\nthis is often used to describe how to use the arguments, or caveats to be noted.",
"type": "string"
CliConfig: {
additionalProperties: false,
defaultProperties: [],
description: 'describes a CLI configuration',
properties: {
afterHelp: {
description:
'adds additional help information to be displayed in addition to auto-generated help\nthis information is displayed after the auto-generated help information\nthis is often used to describe how to use the arguments, or caveats to be noted.',
type: 'string'
},
"args": {
"description": "list of args for the command",
"items": {
"$ref": "#/definitions/CliArg"
args: {
description: 'list of args for the command',
items: {
$ref: '#/definitions/CliArg'
},
"type": "array"
type: 'array'
},
"beforeHelp": {
"description": "adds additional help information to be displayed in addition to auto-generated help\nthis information is displayed before the auto-generated help information.\nthis is often used for header information",
"type": "string"
beforeHelp: {
description:
'adds additional help information to be displayed in addition to auto-generated help\nthis information is displayed before the auto-generated help information.\nthis is often used for header information',
type: 'string'
},
"description": {
"description": "command description which will be shown on the help information",
"type": "string"
description: {
description:
'command description which will be shown on the help information',
type: 'string'
},
"longDescription": {
"description": "command long description which will be shown on the help information",
"type": "string"
longDescription: {
description:
'command long description which will be shown on the help information',
type: 'string'
},
"subcommands": {
"additionalProperties": {
"$ref": "#/definitions/CliConfig"
subcommands: {
additionalProperties: {
$ref: '#/definitions/CliConfig'
},
"defaultProperties": [
],
"description": "list of subcommands of this command\n\nsubcommands are effectively sub-apps, because they can contain their own arguments, subcommands, usage, etc.\nthey also function just like the app command, in that they get their own auto generated help and usage",
"type": "object"
defaultProperties: [],
description:
'list of subcommands of this command\n\nsubcommands are effectively sub-apps, because they can contain their own arguments, subcommands, usage, etc.\nthey also function just like the app command, in that they get their own auto generated help and usage',
type: 'object'
}
},
"type": "object"
type: 'object'
},
"TauriBuildConfig": {
"additionalProperties": false,
"defaultProperties": [
],
"properties": {
"beforeBuildCommand": {
"description": "a shell command to run before `tauri build` kicks in",
"type": "string"
TauriBuildConfig: {
additionalProperties: false,
defaultProperties: [],
properties: {
beforeBuildCommand: {
description: 'a shell command to run before `tauri build` kicks in',
type: 'string'
},
"beforeDevCommand": {
"description": "a shell command to run before `tauri dev` kicks in",
"type": "string"
beforeDevCommand: {
description: 'a shell command to run before `tauri dev` kicks in',
type: 'string'
},
"devPath": {
"description": "the app's dev server URL, or the path to the directory containing an index.html to open",
"type": "string"
devPath: {
description:
"the app's dev server URL, or the path to the directory containing an index.html to open",
type: 'string'
},
"distDir": {
"description": "the path to the app's dist dir\nthis path must contain your index.html file",
"type": "string"
distDir: {
description:
"the path to the app's dist dir\nthis path must contain your index.html file",
type: 'string'
},
"withGlobalTauri": {
"type": "boolean"
withGlobalTauri: {
type: 'boolean'
}
},
"required": [
"devPath",
"distDir"
],
"type": "object"
required: ['devPath', 'distDir'],
type: 'object'
}
},
"description": "Tauri configuration",
"properties": {
"build": {
"$ref": "#/definitions/TauriBuildConfig",
"description": "build/dev configuration"
description: 'Tauri configuration',
properties: {
build: {
$ref: '#/definitions/TauriBuildConfig',
description: 'build/dev configuration'
},
"ctx": {
"additionalProperties": false,
"defaultProperties": [
],
"description": "the context of the current `tauri dev` or `tauri build`",
"properties": {
"debug": {
"description": "whether the app should be built on debug mode or not",
"type": "boolean"
ctx: {
additionalProperties: false,
defaultProperties: [],
description: 'the context of the current `tauri dev` or `tauri build`',
properties: {
debug: {
description: 'whether the app should be built on debug mode or not',
type: 'boolean'
},
"dev": {
"description": "whether we're running on the dev environment or not",
"type": "boolean"
dev: {
description: "whether we're running on the dev environment or not",
type: 'boolean'
},
"exitOnPanic": {
"description": "defines we should exit the `tauri dev` process if a Rust code error is found",
"type": "boolean"
exitOnPanic: {
description:
'defines we should exit the `tauri dev` process if a Rust code error is found',
type: 'boolean'
},
"prod": {
"description": "whether we're building for production or not",
"type": "boolean"
prod: {
description: "whether we're building for production or not",
type: 'boolean'
},
"target": {
"description": "the target of the compilation (see `rustup target list`)",
"type": "string"
target: {
description:
'the target of the compilation (see `rustup target list`)',
type: 'string'
}
},
"type": "object"
type: 'object'
},
"plugins": {
"additionalProperties": {
"additionalProperties": {
},
"defaultProperties": [
],
"type": "object"
plugins: {
additionalProperties: {
additionalProperties: {},
defaultProperties: [],
type: 'object'
},
"defaultProperties": [
],
"type": "object"
defaultProperties: [],
type: 'object'
},
"tauri": {
"additionalProperties": false,
"defaultProperties": [
],
"description": "tauri root configuration object",
"properties": {
"allowlist": {
"additionalProperties": {
"type": "boolean"
tauri: {
additionalProperties: false,
defaultProperties: [],
description: 'tauri root configuration object',
properties: {
allowlist: {
additionalProperties: {
type: 'boolean'
},
"defaultProperties": [
],
"properties": {
"all": {
"type": "boolean"
defaultProperties: [],
properties: {
all: {
type: 'boolean'
}
},
"required": [
"all"
],
"type": "object"
required: ['all'],
type: 'object'
},
"bundle": {
"additionalProperties": false,
"defaultProperties": [
],
"description": "tauri bundler configuration",
"properties": {
"active": {
"description": "whether we should build your app with tauri-bundler or plain `cargo build`",
"type": "boolean"
bundle: {
additionalProperties: false,
defaultProperties: [],
description: 'tauri bundler configuration',
properties: {
active: {
description:
'whether we should build your app with tauri-bundler or plain `cargo build`',
type: 'boolean'
},
"category": {
"type": "string"
category: {
type: 'string'
},
"copyright": {
"type": "string"
copyright: {
type: 'string'
},
"deb": {
"additionalProperties": false,
"defaultProperties": [
],
"properties": {
"depends": {
"items": {
"type": "string"
deb: {
additionalProperties: false,
defaultProperties: [],
properties: {
depends: {
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"useBootstrapper": {
"type": "boolean"
useBootstrapper: {
type: 'boolean'
}
},
"type": "object"
type: 'object'
},
"exceptionDomain": {
"type": "string"
exceptionDomain: {
type: 'string'
},
"externalBin": {
"items": {
"type": "string"
externalBin: {
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"icon": {
"description": "the app's icons",
"items": {
"type": "string"
icon: {
description: "the app's icons",
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"identifier": {
"description": "the app's identifier",
"type": "string"
identifier: {
description: "the app's identifier",
type: 'string'
},
"longDescription": {
"type": "string"
longDescription: {
type: 'string'
},
"osx": {
"additionalProperties": false,
"defaultProperties": [
],
"properties": {
"frameworks": {
"items": {
"type": "string"
osx: {
additionalProperties: false,
defaultProperties: [],
properties: {
frameworks: {
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"license": {
"type": "string"
license: {
type: 'string'
},
"minimumSystemVersion": {
"type": "string"
minimumSystemVersion: {
type: 'string'
},
"useBootstrapper": {
"type": "boolean"
useBootstrapper: {
type: 'boolean'
}
},
"type": "object"
type: 'object'
},
"resources": {
"description": "app resources to bundle\neach resource is a path to a file or directory\nglob patterns are supported",
"items": {
"type": "string"
resources: {
description:
'app resources to bundle\neach resource is a path to a file or directory\nglob patterns are supported',
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
"shortDescription": {
"type": "string"
shortDescription: {
type: 'string'
},
"targets": {
"anyOf": [
targets: {
anyOf: [
{
"items": {
"type": "string"
items: {
type: 'string'
},
"type": "array"
type: 'array'
},
{
"type": "string"
type: 'string'
}
],
"description": "the bundle targets, currently supports [\"deb\", \"osx\", \"msi\", \"appimage\", \"dmg\"] or \"all\""
description:
'the bundle targets, currently supports ["deb", "osx", "msi", "appimage", "dmg"] or "all"'
}
},
"required": [
"icon",
"identifier"
],
"type": "object"
required: ['icon', 'identifier'],
type: 'object'
},
"cli": {
"$ref": "#/definitions/CliConfig",
"description": "app's CLI definition"
cli: {
$ref: '#/definitions/CliConfig',
description: "app's CLI definition"
},
"embeddedServer": {
"additionalProperties": false,
"defaultProperties": [
],
"description": "the embedded server configuration",
"properties": {
"active": {
"description": "whether we should use the embedded-server or the no-server mode",
"type": "boolean"
embeddedServer: {
additionalProperties: false,
defaultProperties: [],
description: 'the embedded server configuration',
properties: {
active: {
description:
'whether we should use the embedded-server or the no-server mode',
type: 'boolean'
},
"port": {
"anyOf": [
port: {
anyOf: [
{
"enum": [
"random"
],
"type": "string"
enum: ['random'],
type: 'string'
},
{
"type": "number"
type: 'number'
}
],
"description": "the embedded server port number or the 'random' string to generate one at runtime"
description:
"the embedded server port number or the 'random' string to generate one at runtime"
}
},
"type": "object"
type: 'object'
},
"inliner": {
"additionalProperties": false,
"defaultProperties": [
],
"properties": {
"active": {
"type": "boolean"
inliner: {
additionalProperties: false,
defaultProperties: [],
properties: {
active: {
type: 'boolean'
}
},
"type": "object"
type: 'object'
},
"security": {
"additionalProperties": false,
"defaultProperties": [
],
"properties": {
"csp": {
"type": "string"
security: {
additionalProperties: false,
defaultProperties: [],
properties: {
csp: {
type: 'string'
}
},
"type": "object"
type: 'object'
},
"window": {
"additionalProperties": false,
"defaultProperties": [
],
"properties": {
"fullscreen": {
"type": "boolean"
window: {
additionalProperties: false,
defaultProperties: [],
properties: {
fullscreen: {
type: 'boolean'
},
"height": {
"type": "number"
height: {
type: 'number'
},
"resizable": {
"type": "boolean"
resizable: {
type: 'boolean'
},
"title": {
"type": "string"
title: {
type: 'string'
},
"width": {
"type": "number"
width: {
type: 'number'
}
},
"required": [
"title"
],
"type": "object"
required: ['title'],
type: 'object'
}
},
"required": [
"allowlist",
"bundle",
"embeddedServer",
"inliner",
"security",
"window"
required: [
'allowlist',
'bundle',
'embeddedServer',
'inliner',
'security',
'window'
],
"type": "object"
type: 'object'
},
"verbose": {
"description": "Whether or not to enable verbose logging",
"type": "boolean"
verbose: {
description: 'Whether or not to enable verbose logging',
type: 'boolean'
}
},
"required": [
"build",
"ctx",
"tauri"
],
"type": "object"
};
export type ValidateFunction<T> = ((data: unknown) => data is T) & Pick<Ajv.ValidateFunction, 'errors'>
export const isTauriConfig = ajv.compile(TauriConfigSchema) as ValidateFunction<TauriConfig>;
required: ['build', 'ctx', 'tauri'],
type: 'object'
}
export type ValidateFunction<T> = ((data: unknown) => data is T) &
Pick<Ajv.ValidateFunction, 'errors'>
export const isTauriConfig = ajv.compile(TauriConfigSchema) as ValidateFunction<
TauriConfig
>
export default function validate(value: unknown): TauriConfig {
if (isTauriConfig(value)) {
return value;
return value
} else {
throw new Error(
ajv.errorsText(isTauriConfig.errors!.filter((e: any) => e.keyword !== 'if'), {dataVar: 'TauriConfig'}) +
'\n\n' +
inspect(value),
);
ajv.errorsText(
isTauriConfig.errors!.filter((e: any) => e.keyword !== 'if'),
{ dataVar: 'TauriConfig' }
) +
'\n\n' +
inspect(value)
)
}
}

View File

@ -1,8 +1,8 @@
import React from 'react';
import logo from './logo.svg';
import tauriCircles from './tauri.svg';
import tauriWord from './wordmark.svg';
import './App.css';
import React from 'react'
import logo from './logo.svg'
import tauriCircles from './tauri.svg'
import tauriWord from './wordmark.svg'
import './App.css'
function App() {
return (
@ -17,7 +17,7 @@ function App() {
href="https://tauri.studio"
target="_blank"
rel="noopener noreferrer"
>
>
Learn Tauri
</a>
<img src={logo} className="App-logo rotate" alt="logo" />
@ -34,7 +34,7 @@ function App() {
</p>
</header>
</div>
);
)
}
export default App;
export default App

View File

@ -6,7 +6,7 @@ if (!String.prototype.startsWith) {
}
}
(function () {
;(function () {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
@ -14,38 +14,58 @@ if (!String.prototype.startsWith) {
}
var uid = function () {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
return (
s4() +
s4() +
'-' +
s4() +
'-' +
s4() +
'-' +
s4() +
'-' +
s4() +
s4() +
s4()
)
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
var keys = Object.keys(object)
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
var symbols = Object.getOwnPropertySymbols(object)
if (enumerableOnly)
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable
})
keys.push.apply(keys, symbols)
}
return keys;
return keys
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var source = arguments[i] != null ? arguments[i] : {}
if (i % 2) {
ownKeys(source, true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
_defineProperty(target, key, source[key])
})
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source)
)
} else {
ownKeys(source).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
Object.defineProperty(
target,
key,
Object.getOwnPropertyDescriptor(source, key)
)
})
}
}
return target;
return target
}
function _defineProperty(obj, key, value) {
@ -55,18 +75,21 @@ if (!String.prototype.startsWith) {
enumerable: true,
configurable: true,
writable: true
});
})
} else {
obj[key] = value;
obj[key] = value
}
return obj;
return obj
}
if (!window.__TAURI__) {
window.__TAURI__ = {}
}
window.__TAURI__.transformCallback = function transformCallback(callback, once) {
window.__TAURI__.transformCallback = function transformCallback(
callback,
once
) {
var identifier = uid()
window[identifier] = function (result) {
@ -77,7 +100,7 @@ if (!String.prototype.startsWith) {
return callback && callback(result)
}
return identifier;
return identifier
}
window.__TAURI__.promisified = function promisified(args) {
@ -93,10 +116,15 @@ if (!String.prototype.startsWith) {
delete window[callback]
}, true)
window.__TAURI_INVOKE_HANDLER__(_objectSpread({
callback: callback,
error: error
}, args))
window.__TAURI_INVOKE_HANDLER__(
_objectSpread(
{
callback: callback,
error: error
},
args
)
)
})
}
@ -108,46 +136,70 @@ if (!String.prototype.startsWith) {
})
}
document.addEventListener('error', function (e) {
var target = e.target
while (target != null) {
if (target.matches ? target.matches('img') : target.msMatchesSelector('img')) {
window.__TAURI__.loadAsset(target.src, 'image')
.then(function (img) {
target.src = img
})
break
}
target = target.parentElement
}
}, true)
// open <a href="..."> links with the Tauri API
function __openLinks() {
document.querySelector('body').addEventListener('click', function (e) {
document.addEventListener(
'error',
function (e) {
var target = e.target
while (target != null) {
if (target.matches ? target.matches('a') : target.msMatchesSelector('a')) {
if (target.href && target.href.startsWith('http') && target.target === '_blank') {
window.__TAURI_INVOKE_HANDLER__({
cmd: 'open',
uri: target.href
})
e.preventDefault()
}
if (
target.matches
? target.matches('img')
: target.msMatchesSelector('img')
) {
window.__TAURI__.loadAsset(target.src, 'image').then(function (img) {
target.src = img
})
break
}
target = target.parentElement
}
}, true)
},
true
)
// open <a href="..."> links with the Tauri API
function __openLinks() {
document.querySelector('body').addEventListener(
'click',
function (e) {
var target = e.target
while (target != null) {
if (
target.matches ? target.matches('a') : target.msMatchesSelector('a')
) {
if (
target.href &&
target.href.startsWith('http') &&
target.target === '_blank'
) {
window.__TAURI_INVOKE_HANDLER__({
cmd: 'open',
uri: target.href
})
e.preventDefault()
}
break
}
target = target.parentElement
}
},
true
)
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
if (
document.readyState === 'complete' ||
document.readyState === 'interactive'
) {
__openLinks()
} else {
window.addEventListener('DOMContentLoaded', function () {
__openLinks()
}, true)
window.addEventListener(
'DOMContentLoaded',
function () {
__openLinks()
},
true
)
}
let permissionSettable = false
@ -169,12 +221,14 @@ if (!String.prototype.startsWith) {
}
function requestPermission() {
return window.__TAURI__.promisified({
cmd: 'requestNotificationPermission'
}).then(function (permission) {
setNotificationPermission(permission)
return permission
})
return window.__TAURI__
.promisified({
cmd: 'requestNotificationPermission'
})
.then(function (permission) {
setNotificationPermission(permission)
return permission
})
}
function sendNotification(options) {
@ -182,24 +236,28 @@ if (!String.prototype.startsWith) {
Object.freeze(options)
}
isPermissionGranted()
.then(function (permission) {
if (permission) {
return window.__TAURI__.promisified({
cmd: 'notification',
options: typeof options === 'string' ? {
title: options
} : options
})
}
})
isPermissionGranted().then(function (permission) {
if (permission) {
return window.__TAURI__.promisified({
cmd: 'notification',
options:
typeof options === 'string'
? {
title: options
}
: options
})
}
})
}
window.Notification = function (title, options) {
var opts = options || {}
sendNotification(Object.assign(opts, {
title: title
}))
sendNotification(
Object.assign(opts, {
title: title
})
)
}
window.Notification.requestPermission = requestPermission
@ -217,14 +275,13 @@ if (!String.prototype.startsWith) {
}
})
isPermissionGranted()
.then(function (response) {
if (response === null) {
setNotificationPermission('default')
} else {
setNotificationPermission(response ? 'granted' : 'denied')
}
})
isPermissionGranted().then(function (response) {
if (response === null) {
setNotificationPermission('default')
} else {
setNotificationPermission(response ? 'granted' : 'denied')
}
})
window.alert = function (message) {
window.__TAURI_INVOKE_HANDLER__({

View File

@ -23,10 +23,18 @@ function runBuildTest(tauriConfig) {
await result.promise
const artifactFolder = tauriConfig.ctx.debug ? 'debug' : 'release'
const artifactPath = path.resolve(appDir, `src-tauri/target/${artifactFolder}/app`)
const artifactPath = path.resolve(
appDir,
`src-tauri/target/${artifactFolder}/app`
)
const appPid = spawn(
process.platform === 'win32' ? `${artifactPath}.exe` : artifactPath.replace(`${artifactFolder}/app`, `${artifactFolder}/./app`),
process.platform === 'win32'
? `${artifactPath}.exe`
: artifactPath.replace(
`${artifactFolder}/app`,
`${artifactFolder}/./app`
),
[],
null
)
@ -37,7 +45,9 @@ function runBuildTest(tauriConfig) {
try {
process.kill(appPid)
} catch {}
const failedCommands = Object.keys(responses).filter(k => responses[k] === null).join(', ')
const failedCommands = Object.keys(responses)
.filter((k) => responses[k] === null)
.join(', ')
reject("App didn't reply to " + failedCommands)
})
}
@ -55,11 +65,11 @@ describe('Tauri Build', () => {
}
it.each`
mode | flag
${'embedded-server'} | ${'debug'}
${'embedded-server'} | ${'release'}
${'no-server'} | ${'debug'}
${'no-server'} | ${'release'}
mode | flag
${'embedded-server'} | ${'debug'}
${'embedded-server'} | ${'release'}
${'no-server'} | ${'debug'}
${'no-server'} | ${'release'}
`('works with the $mode $flag mode', ({ mode, flag }) => {
return runBuildTest({
build,

View File

@ -3,29 +3,29 @@ const fixtureSetup = require('../fixtures/app-test-setup')
const distDir = path.resolve(fixtureSetup.fixtureDir, 'app', 'dist')
function startDevServer() {
const http = require('http')
const { statSync, createReadStream } = require('fs')
const app = http.createServer((req, res) => {
if (req.method === 'GET') {
if (req.url === '/') {
const indexPath = path.join(distDir, 'index.html')
const stat = statSync(indexPath)
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Length': stat.size
})
createReadStream(indexPath).pipe(res)
}
const http = require('http')
const { statSync, createReadStream } = require('fs')
const app = http.createServer((req, res) => {
if (req.method === 'GET') {
if (req.url === '/') {
const indexPath = path.join(distDir, 'index.html')
const stat = statSync(indexPath)
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Length': stat.size
})
createReadStream(indexPath).pipe(res)
}
})
const port = 7001
const server = app.listen(port)
return {
server,
url: `http://localhost:${port}`
}
})
const port = 7001
const server = app.listen(port)
return {
server,
url: `http://localhost:${port}`
}
}
function runDevTest(tauriConfig) {
@ -39,7 +39,9 @@ function runDevTest(tauriConfig) {
let success = false
const checkIntervalId = setInterval(async () => {
if (!isRunning(runner.pid) && !success) {
const failedCommands = Object.keys(responses).filter(k => responses[k] === null).join(', ')
const failedCommands = Object.keys(responses)
.filter((k) => responses[k] === null)
.join(', ')
server.close(() => reject("App didn't reply to " + failedCommands))
}
}, 2000)

View File

@ -20,7 +20,9 @@ describe('[CLI] tauri.js', () => {
it('will not run an unavailable command', async () => {
jest.spyOn(console, 'log')
tauri('foo')
expect(console.log.mock.calls[0][0].split('.')[0]).toBe('Invalid command foo')
expect(console.log.mock.calls[0][0].split('.')[0]).toBe(
'Invalid command foo'
)
jest.clearAllMocks()
})
@ -28,9 +30,11 @@ describe('[CLI] tauri.js', () => {
jest.spyOn(console, 'log')
jest.mock('fs')
try {
tauri('init')
tauri('init')
} catch {}
expect(console.log.mock.calls[0][0].split('.')[0]).toBe('[tauri]: running init')
expect(console.log.mock.calls[0][0].split('.')[0]).toBe(
'[tauri]: running init'
)
jest.clearAllMocks()
})
it('gets you help', async () => {

View File

@ -11,25 +11,38 @@ describe('[CLI] tauri-icon internals', () => {
it('will not validate a non-file', async () => {
jest.spyOn(process, 'exit').mockImplementation(() => true)
await tauricon.validate('test/jest/fixtures/doesnotexist.png', 'test/jest/fixtures/')
await tauricon.validate(
'test/jest/fixtures/doesnotexist.png',
'test/jest/fixtures/'
)
expect(process.exit.mock.calls[0][0]).toBe(1)
jest.clearAllMocks()
})
it('will not validate a non-png', async () => {
jest.spyOn(process, 'exit').mockImplementation(() => true)
await tauricon.validate('test/jest/fixtures/notAMeme.jpg', 'test/jest/fixtures/')
await tauricon.validate(
'test/jest/fixtures/notAMeme.jpg',
'test/jest/fixtures/'
)
expect(process.exit.mock.calls[0][0]).toBe(1)
jest.clearAllMocks()
})
it('can validate an image as PNG', async () => {
const valid = await tauricon.validate('test/jest/fixtures/tauri-logo.png', 'test/jest/fixtures/')
const valid = await tauricon.validate(
'test/jest/fixtures/tauri-logo.png',
'test/jest/fixtures/'
)
expect(valid).toBe(true)
})
})
describe('[CLI] tauri-icon builder', () => {
it('will still use default compression if missing compression chosen', async () => {
const valid = await tauricon.make('test/jest/fixtures/tauri-logo.png', 'test/jest/tmp/missing', 'missing')
const valid = await tauricon.make(
'test/jest/fixtures/tauri-logo.png',
'test/jest/tmp/missing',
'missing'
)
expect(valid).toBe(true)
})
})
@ -37,7 +50,11 @@ describe('[CLI] tauri-icon builder', () => {
describe('[CLI] tauri-icon builder', () => {
it('will not validate a non-file', async () => {
try {
await tauricon.make('test/jest/fixtures/tauri-foo-not-found.png', 'test/jest/tmp/pngquant', 'pngquant')
await tauricon.make(
'test/jest/fixtures/tauri-foo-not-found.png',
'test/jest/tmp/pngquant',
'pngquant'
)
} catch (e) {
expect(e.message).toBe('Input file is missing')
}
@ -46,12 +63,20 @@ describe('[CLI] tauri-icon builder', () => {
describe('[CLI] tauri-icon builder', () => {
it('makes a set of icons with pngquant', async () => {
const valid = await tauricon.make('test/jest/fixtures/tauri-logo.png', 'test/jest/tmp/pngquant', 'pngquant')
const valid = await tauricon.make(
'test/jest/fixtures/tauri-logo.png',
'test/jest/tmp/pngquant',
'pngquant'
)
expect(valid).toBe(true)
})
it('makes a set of icons with optipng', async () => {
const valid = await tauricon.make('test/jest/fixtures/tauri-logo.png', 'test/jest/tmp/optipng', 'optipng')
const valid = await tauricon.make(
'test/jest/fixtures/tauri-logo.png',
'test/jest/tmp/optipng',
'optipng'
)
expect(valid).toBe(true)
})

View File

@ -1,11 +1,6 @@
const fixtureSetup = require('../fixtures/app-test-setup')
const {
resolve
} = require('path')
const {
writeFileSync,
readFileSync
} = require('fs')
const { resolve } = require('path')
const { writeFileSync, readFileSync } = require('fs')
describe('[CLI] tauri.js template', () => {
it('init a project and builds it', async () => {

View File

@ -6,15 +6,13 @@ const mockFixtureDir = path.resolve(__dirname, '../fixtures')
module.exports.fixtureDir = mockFixtureDir
function mockResolvePath(basePath, dir) {
return dir && path.isAbsolute(dir) ?
dir :
path.resolve(basePath, dir)
return dir && path.isAbsolute(dir) ? dir : path.resolve(basePath, dir)
}
module.exports.initJest = (mockFixture) => {
jest.setTimeout(720000)
jest.mock('helpers/non-webpack-require', () => {
return path => {
return (path) => {
const value = require('fs').readFileSync(path).toString()
if (path.endsWith('.json')) {
return JSON.parse(value)
@ -31,8 +29,8 @@ module.exports.initJest = (mockFixture) => {
appDir,
tauriDir,
resolve: {
app: dir => mockResolvePath(appDir, dir),
tauri: dir => mockResolvePath(tauriDir, dir)
app: (dir) => mockResolvePath(appDir, dir),
tauri: (dir) => mockResolvePath(tauriDir, dir)
}
}
})
@ -63,7 +61,7 @@ module.exports.startServer = (onSuccess) => {
function addResponse(response) {
responses[response.cmd] = true
if (!Object.values(responses).some(c => c === null)) {
if (!Object.values(responses).some((c) => c === null)) {
server.close(onSuccess)
}
}
@ -83,7 +81,7 @@ module.exports.startServer = (onSuccess) => {
if (req.method === 'POST') {
let body = ''
req.on('data', chunk => {
req.on('data', (chunk) => {
body += chunk.toString()
})
if (req.url === '/reply') {

View File

@ -1,117 +1,145 @@
<!DOCTYPE html>
<html>
<body>
<script>
function callBackEnd(route, args) {
console.log(route)
console.log(args)
var xhr = new XMLHttpRequest()
xhr.onload = function () {
console.log(route, args, 'done', xhr.status)
}
xhr.onerror = function () {
console.log(route, args, 'error with ' + xhr.status)
}
xhr.open('POST', 'http://localhost:7000/' + route)
xhr.setRequestHeader('Content-type', 'application/json')
xhr.send(JSON.stringify(args))
}
function reply(args) {
return callBackEnd('reply', args)
}
function sendError(error) {
return callBackEnd('error', error)
}
function testFs(dir) {
var contents = 'TAURI E2E TEST FILE'
var commandSuffix = (dir ? 'WithDir' : '')
var options = {
dir: dir || null
<body>
<script>
function callBackEnd(route, args) {
console.log(route)
console.log(args)
var xhr = new XMLHttpRequest()
xhr.onload = function () {
console.log(route, args, 'done', xhr.status)
}
xhr.onerror = function () {
console.log(route, args, 'error with ' + xhr.status)
}
xhr.open('POST', 'http://localhost:7000/' + route)
xhr.setRequestHeader('Content-type', 'application/json')
xhr.send(JSON.stringify(args))
}
return window.__TAURI__.fs.writeFile({
file: 'tauri-test.txt',
contents: contents
}, options).then(function (res) {
reply({
cmd: 'writeFile' + commandSuffix
})
return window.__TAURI__.fs.readTextFile('tauri-test.txt', options).then(function (res) {
if (res === contents) {
function reply(args) {
return callBackEnd('reply', args)
}
function sendError(error) {
return callBackEnd('error', error)
}
function testFs(dir) {
var contents = 'TAURI E2E TEST FILE'
var commandSuffix = dir ? 'WithDir' : ''
var options = {
dir: dir || null
}
return window.__TAURI__.fs
.writeFile(
{
file: 'tauri-test.txt',
contents: contents
},
options
)
.then(function (res) {
reply({
cmd: 'readFile' + commandSuffix
cmd: 'writeFile' + commandSuffix
})
return window.__TAURI__.fs.readDir('.', options).then(res => {
reply({
cmd: 'readDir' + commandSuffix
})
return window.__TAURI__.fs.copyFile('tauri-test.txt', 'tauri-test-copy.txt', options)
.then(function (res) {
return window.__TAURI__.fs
.readTextFile('tauri-test.txt', options)
.then(function (res) {
if (res === contents) {
reply({
cmd: 'copyFile' + commandSuffix
cmd: 'readFile' + commandSuffix
})
return window.__TAURI__.fs.createDir('tauri-test-dir', options).then(function (res) {
reply({
cmd: 'createDir' + commandSuffix
})
return window.__TAURI__.fs.removeDir('tauri-test-dir', options).then(function (res) {
return window.__TAURI__.fs
.readDir('.', options)
.then((res) => {
reply({
cmd: 'removeDir' + commandSuffix
cmd: 'readDir' + commandSuffix
})
})
}).then(function (res) {
return window.__TAURI__.fs.renameFile('tauri-test.txt', 'tauri.testt.txt', options)
.then(function (res) {
reply({
cmd: 'renameFile' + commandSuffix
})
return Promise.all([
window.__TAURI__.fs.removeFile('tauri.testt.txt', options),
window.__TAURI__.fs.removeFile('tauri-test-copy.txt', options)
]).then(function (res) {
return window.__TAURI__.fs
.copyFile(
'tauri-test.txt',
'tauri-test-copy.txt',
options
)
.then(function (res) {
reply({
cmd: 'removeFile' + commandSuffix
cmd: 'copyFile' + commandSuffix
})
return window.__TAURI__.fs
.createDir('tauri-test-dir', options)
.then(function (res) {
reply({
cmd: 'createDir' + commandSuffix
})
return window.__TAURI__.fs
.removeDir('tauri-test-dir', options)
.then(function (res) {
reply({
cmd: 'removeDir' + commandSuffix
})
})
})
.then(function (res) {
return window.__TAURI__.fs
.renameFile(
'tauri-test.txt',
'tauri.testt.txt',
options
)
.then(function (res) {
reply({
cmd: 'renameFile' + commandSuffix
})
return Promise.all([
window.__TAURI__.fs.removeFile(
'tauri.testt.txt',
options
),
window.__TAURI__.fs.removeFile(
'tauri-test-copy.txt',
options
)
]).then(function (res) {
reply({
cmd: 'removeFile' + commandSuffix
})
})
})
})
})
})
})
})
})
} else {
sendError('expected "' + contents + '" found "' + res + '"')
}
})
} else {
sendError('expected "' + contents + '" found "' + res + '"')
}
})
})
.catch(sendError)
}
window.__TAURI__.event.listen('reply', function (res) {
reply({
cmd: 'listen'
})
}).catch(sendError)
}
window.__TAURI__.event.listen('reply', function (res) {
reply({
cmd: 'listen'
})
})
window.onTauriInit = function () {
window.__TAURI__.event.emit('hello')
}
window.onTauriInit = function () {
window.__TAURI__.event.emit('hello')
}
testFs(null).then(function () {
testFs(window.__TAURI__.fs.Dir.Config)
})
setTimeout(function () {
window.__TAURI__.invoke({
cmd: 'exit'
testFs(null).then(function () {
testFs(window.__TAURI__.fs.Dir.Config)
})
}, 15000)
</script>
</body>
setTimeout(function () {
window.__TAURI__.invoke({
cmd: 'exit'
})
}, 15000)
</script>
</body>
</html>

View File

@ -14,9 +14,11 @@ app.post('/reply', (req, res) => {
exit(0)
})
const server = app.listen(port, () => console.log(`Test listening on port ${port}!`))
const server = app.listen(port, () =>
console.log(`Test listening on port ${port}!`)
)
const exit = code => {
const exit = (code) => {
server.close()
process.kill(appPid)
process.exit(code)
@ -42,7 +44,9 @@ build({
const spawn = require('../cli/tauri.js/dist/helpers/spawn').spawn
const artifactPath = path.resolve(__dirname, 'src-tauri/target/debug/app')
appPid = spawn(
process.platform === 'win32' ? `${artifactPath}.exe` : artifactPath.replace('debug/app', 'debug/./app'),
process.platform === 'win32'
? `${artifactPath}.exe`
: artifactPath.replace('debug/app', 'debug/./app'),
[],
null
)

View File

@ -1,22 +1,22 @@
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"tauri:prod": "tauri",
"tauri:source": "node ../../../../bin/tauri",
"tauri:source:init": "yarn tauri:source init --tauriPath ..",
"tauri:prod:init": "yarn tauri:prod init",
"tauri:source:dev": "yarn tauri:source dev",
"tauri:prod:dev": "yarn tauri:prod dev",
"tauri:source:build": "yarn tauri:source build",
"tauri:prod:build": "yarn tauri:prod build"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1"
}
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"tauri:prod": "tauri",
"tauri:source": "node ../../../../bin/tauri",
"tauri:source:init": "yarn tauri:source init --tauriPath ..",
"tauri:prod:init": "yarn tauri:prod init",
"tauri:source:dev": "yarn tauri:source dev",
"tauri:prod:dev": "yarn tauri:prod dev",
"tauri:source:build": "yarn tauri:source build",
"tauri:prod:build": "yarn tauri:prod build"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1"
}
}

View File

@ -8,7 +8,13 @@
"all": true
},
"bundle": {
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"identifier": "fixture.app"
},
"window": {

View File

@ -1,3 +1,3 @@
module.exports = {
process: content => `module.exports = {default: ${JSON.stringify(content)}}`
process: (content) => `module.exports = {default: ${JSON.stringify(content)}}`
}

View File

@ -11,7 +11,7 @@
"baseUrl": ".",
"paths": {
"types": ["src/types"]
},
}
},
"include": ["src", "api-src"]
}

View File

@ -18,7 +18,8 @@ module.exports = {
mode: process.env.NODE_ENV || 'development',
devtool: 'source-map',
module: {
rules: [{
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
@ -47,13 +48,15 @@ module.exports = {
target: 'node',
plugins: [
new CopyWebpackPlugin({
patterns: [{
from: './src/types/config.validator.ts',
to: '../src/types/config.schema.json',
transform(content) {
return schemaParser('TauriConfigSchema', content.toString())
patterns: [
{
from: './src/types/config.validator.ts',
to: '../src/types/config.schema.json',
transform(content) {
return schemaParser('TauriConfigSchema', content.toString())
}
}
}]
]
})
]
}
@ -74,5 +77,5 @@ function schemaParser(schemaName, content) {
}
}
return output.join("\n")
return output.join('\n')
}

View File

@ -1562,12 +1562,12 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.7.0.tgz#0f91aa3c83d019591719e597fbdb73a59595a263"
integrity sha512-4OEcPON3QIx0ntsuiuFP/TkldmBGXf0uKxPQlGtS/W2F3ndYm8Vgdpj/woPJkzUc65gd3iR+qi3K8SDQP/obFg==
"@typescript-eslint/eslint-plugin@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.9.1.tgz#8cf27b6227d12d66dd8dc1f1a4b04d1daad51c2e"
integrity sha512-XIr+Mfv7i4paEdBf0JFdIl9/tVxyj+rlilWIfZ97Be0lZ7hPvUbS5iHt9Glc8kRI53dsr0PcAEudbf8rO2wGgg==
dependencies:
"@typescript-eslint/experimental-utils" "3.7.0"
"@typescript-eslint/experimental-utils" "3.9.1"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
@ -1585,26 +1585,26 @@
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/experimental-utils@3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.7.0.tgz#0ee21f6c48b2b30c63211da23827725078d5169a"
integrity sha512-xpfXXAfZqhhqs5RPQBfAFrWDHoNxD5+sVB5A46TF58Bq1hRfVROrWHcQHHUM9aCBdy9+cwATcvCbRg8aIRbaHQ==
"@typescript-eslint/experimental-utils@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.9.1.tgz#b140b2dc7a7554a44f8a86fb6fe7cbfe57ca059e"
integrity sha512-lkiZ8iBBaYoyEKhCkkw4SAeatXyBq9Ece5bZXdLe1LWBUwTszGbmbiqmQbwWA8cSYDnjWXp9eDbXpf9Sn0hLAg==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/types" "3.7.0"
"@typescript-eslint/typescript-estree" "3.7.0"
"@typescript-eslint/types" "3.9.1"
"@typescript-eslint/typescript-estree" "3.9.1"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/parser@3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.7.0.tgz#3e9cd9df9ea644536feb6e5acdb8279ecff96ce9"
integrity sha512-2LZauVUt7jAWkcIW7djUc3kyW+fSarNEuM3RF2JdLHR9BfX/nDEnyA4/uWz0wseoWVZbDXDF7iF9Jc342flNqQ==
"@typescript-eslint/parser@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.9.1.tgz#ab7983abaea0ae138ff5671c7c7739d8a191b181"
integrity sha512-y5QvPFUn4Vl4qM40lI+pNWhTcOWtpZAJ8pOEQ21fTTW4xTJkRplMjMRje7LYTXqVKKX9GJhcyweMz2+W1J5bMg==
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "3.7.0"
"@typescript-eslint/types" "3.7.0"
"@typescript-eslint/typescript-estree" "3.7.0"
"@typescript-eslint/experimental-utils" "3.9.1"
"@typescript-eslint/types" "3.9.1"
"@typescript-eslint/typescript-estree" "3.9.1"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/parser@^3.0.1":
@ -1623,10 +1623,10 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.1.tgz#87600fe79a1874235d3cc1cf5c7e1a12eea69eee"
integrity sha512-NPxd5yXG63gx57WDTW1rp0cF3XlNuuFFB5G+Kc48zZ+51ZnQn9yjDEsjTPQ+aWM+V+Z0I4kuTFKjKvgcT1F7xQ==
"@typescript-eslint/types@3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.7.0.tgz#09897fab0cb95479c01166b10b2c03c224821077"
integrity sha512-reCaK+hyKkKF+itoylAnLzFeNYAEktB0XVfSQvf0gcVgpz1l49Lt6Vo9x4MVCCxiDydA0iLAjTF/ODH0pbfnpg==
"@typescript-eslint/types@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.9.1.tgz#b2a6eaac843cf2f2777b3f2464fb1fbce5111416"
integrity sha512-15JcTlNQE1BsYy5NBhctnEhEoctjXOjOK+Q+rk8ugC+WXU9rAcS2BYhoh6X4rOaXJEpIYDl+p7ix+A5U0BqPTw==
"@typescript-eslint/typescript-estree@3.6.1":
version "3.6.1"
@ -1642,13 +1642,13 @@
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/typescript-estree@3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.7.0.tgz#66872e6da120caa4b64e6b4ca5c8702afc74738d"
integrity sha512-xr5oobkYRebejlACGr1TJ0Z/r0a2/HUf0SXqPvlgUMwiMqOCu/J+/Dr9U3T0IxpE5oLFSkqMx1FE/dKaZ8KsOQ==
"@typescript-eslint/typescript-estree@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.9.1.tgz#fd81cada74bc8a7f3a2345b00897acb087935779"
integrity sha512-IqM0gfGxOmIKPhiHW/iyAEXwSVqMmR2wJ9uXHNdFpqVvPaQ3dWg302vW127sBpAiqM9SfHhyS40NKLsoMpN2KA==
dependencies:
"@typescript-eslint/types" "3.7.0"
"@typescript-eslint/visitor-keys" "3.7.0"
"@typescript-eslint/types" "3.9.1"
"@typescript-eslint/visitor-keys" "3.9.1"
debug "^4.1.1"
glob "^7.1.6"
is-glob "^4.0.1"
@ -1663,10 +1663,10 @@
dependencies:
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/visitor-keys@3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.7.0.tgz#ac0417d382a136e4571a0b0dcfe52088cb628177"
integrity sha512-k5PiZdB4vklUpUX4NBncn5RBKty8G3ihTY+hqJsCdMuD0v4jofI5xuqwnVcWxfv6iTm2P/dfEa2wMUnsUY8ODw==
"@typescript-eslint/visitor-keys@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.9.1.tgz#92af3747cdb71509199a8f7a4f00b41d636551d1"
integrity sha512-zxdtUjeoSh+prCpogswMwVUJfEFmCOjdzK9rpNjNBfm6EyPt99x3RrJoBOGZO23FCt0WPKUCOL5mb/9D5LjdwQ==
dependencies:
eslint-visitor-keys "^1.1.0"
@ -3835,6 +3835,13 @@ escodegen@^1.14.1:
optionalDependencies:
source-map "~0.6.1"
eslint-config-prettier@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1"
integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==
dependencies:
get-stdin "^6.0.0"
eslint-config-standard-with-typescript@18.0.2:
version "18.0.2"
resolved "https://registry.yarnpkg.com/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-18.0.2.tgz#eb02d5358b17fe083c6f993ff829492c8f96b18f"
@ -3956,10 +3963,10 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
eslint@7.5.0:
version "7.5.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.5.0.tgz#9ecbfad62216d223b82ac9ffea7ef3444671d135"
integrity sha512-vlUP10xse9sWt9SGRtcr1LAC67BENcQMFeV+w5EvLEoFe3xJ8cF1Skd0msziRx/VMC+72B4DxreCE+OR12OA6Q==
eslint@7.7.0:
version "7.7.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073"
integrity sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==
dependencies:
"@babel/code-frame" "^7.0.0"
ajv "^6.10.0"
@ -4620,6 +4627,11 @@ get-stdin@^4.0.1:
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
get-stdin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@ -7698,6 +7710,11 @@ prepend-http@^3.0.1:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-3.0.1.tgz#3e724d58fd5867465b300bb9615009fa2f8ee3b6"
integrity sha512-BLxfZh+m6UiAiCPZFJ4+vYoL7NrRs5XgCTRrjseATAggXhdZKKxn+JUNmuVYWY23bDHgaEHodxw8mnmtVEDtHw==
prettier@2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
pretty-format@^26.1.0:
version "26.1.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.1.0.tgz#272b9cd1f1a924ab5d443dc224899d7a65cb96ec"

View File

@ -8,7 +8,11 @@
"url": "https://github.com/tauri-apps/tauri.git",
"directory": "cli/tauri.js"
},
"scripts": {
"format": "prettier --write --end-of-line=auto \"./**/*.{js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore"
},
"devDependencies": {
"covector": "^0.2.6"
"covector": "^0.2.6",
"prettier": "^2.0.5"
}
}