ソースを参照

feat(tauri.js) validate tauri.conf.json schema on runtime (#756)

Lucas Fernandes Nogueira 5 年 前
コミット
960ad58c12

+ 5 - 0
.changes/conf-validation.md

@@ -0,0 +1,5 @@
+---
+"tauri.js": patch
+---
+
+Adds tauri.conf.json schema validation to the CLI.

+ 1 - 1
cli/tauri.js/.eslintignore

@@ -1 +1 @@
-/src-tauri
+/src/types/config.validator.ts

+ 1 - 0
cli/tauri.js/.npmignore

@@ -4,3 +4,4 @@ node_modules
 .github
 .idea
 SECURITY.md
+build

+ 16 - 0
cli/tauri.js/build/type-validators.js

@@ -0,0 +1,16 @@
+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';`))
+  }
+})

+ 4 - 1
cli/tauri.js/package.json

@@ -11,7 +11,8 @@
   },
   "scripts": {
     "build": "yarn build:api && yarn build:typedefs && yarn build:webpack",
-    "build:webpack": "rimraf ./dist && webpack --progress",
+    "build:webpack": "rimraf ./dist && yarn build:typevalidators && webpack --progress",
+    "build:typevalidators": "node ./build/type-validators",
     "build:api": "rimraf ./api && rollup -c --silent && yarn build:typedefs",
     "build:typedefs": "yarn tsc ./api-src/index.ts --declaration --emitDeclarationOnly --outDir api",
     "build-release": "yarn build --display none --progress false",
@@ -94,6 +95,7 @@
     "@typescript-eslint/eslint-plugin": "3.5.0",
     "@typescript-eslint/parser": "3.5.0",
     "babel-jest": "26.1.0",
+    "copy-webpack-plugin": "^6.0.3",
     "dotenv": "8.2.0",
     "eslint": "7.3.1",
     "eslint-config-standard-with-typescript": "18.0.2",
@@ -119,6 +121,7 @@
     "ts-loader": "7.0.5",
     "tslib": "2.0.0",
     "typescript": "^3.9.6",
+    "typescript-json-validator": "^2.4.2",
     "webpack": "4.43.0",
     "webpack-cli": "3.3.12",
     "webpack-node-externals": "1.7.2"

+ 29 - 24
cli/tauri.js/src/helpers/tauri-config.ts

@@ -5,6 +5,7 @@ import logger from '../helpers/logger'
 import * as appPaths from './app-paths'
 import nonWebpackRequire from '../helpers/non-webpack-require'
 import chalk from 'chalk'
+import { isTauriConfig, ajv } from '../types/config.validator'
 
 const error = logger('ERROR:', chalk.red)
 
@@ -65,8 +66,20 @@ const getTauriConfig = (cfg: Partial<TauriConfig>): TauriConfig => {
     cfg as any
   ) as TauriConfig
 
-  if (!config.build.devPath || !config.build.distDir) {
-    error('Missing required build configuration in your tauri.conf.json file. Please make sure to add the proper path configuration as described at https://github.com/tauri-apps/tauri/wiki/05.-Tauri-Integration#src-tauritauriconfjson.')
+  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')
+
+    for (const message of messages) {
+      error(message)
+    }
     process.exit(1)
   }
 
@@ -80,32 +93,24 @@ const getTauriConfig = (cfg: Partial<TauriConfig>): TauriConfig => {
     process.env.TAURI_DIST_DIR = config.build.distDir
   }
 
-  // bundle configuration
-  if (config.tauri.bundle) {
-    // OSX
-    if (config.tauri.bundle.osx) {
-      const license = config.tauri.bundle.osx.license
-      if (typeof license === 'string') {
-        config.tauri.bundle.osx.license = appPaths.resolve.tauri(license)
-      } else if (license !== null) {
-        const licensePath = appPaths.resolve.app('LICENSE')
-        if (existsSync(licensePath)) {
-          config.tauri.bundle.osx.license = licensePath
-        }
-      }
-    }
-
-    // targets
-    if (Array.isArray(config.tauri.bundle.targets)) {
-      if (process.platform !== 'win32') {
-        config.tauri.bundle.targets = config.tauri.bundle.targets.filter(t => t !== 'msi')
+  // OSX bundle config
+  if (config.tauri.bundle.osx) {
+    const license = config.tauri.bundle.osx.license
+    if (typeof license === 'string') {
+      config.tauri.bundle.osx.license = appPaths.resolve.tauri(license)
+    } else if (license !== null) {
+      const licensePath = appPaths.resolve.app('LICENSE')
+      if (existsSync(licensePath)) {
+        config.tauri.bundle.osx.license = licensePath
       }
     }
   }
 
-  if (!process.env.TAURI_DIST_DIR) {
-    error("Couldn't resolve the dist dir. Make sure you have `devPath` or `distDir` under tauri.conf.json > build")
-    process.exit(1)
+  // 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')
+    }
   }
 
   process.env.TAURI_DIR = appPaths.tauriDir

+ 489 - 0
cli/tauri.js/src/types/config.schema.json

@@ -0,0 +1,489 @@
+{
+  "$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"
+        },
+        "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"
+        },
+        "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"
+        },
+        "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"
+        },
+        "multipleOccurrences": {
+          "description": "specifies that the argument may appear more than once.",
+          "type": "boolean"
+        },
+        "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"
+          },
+          "type": "array"
+        },
+        "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"
+        },
+        "requiredIf": {
+          "additionalItems": {
+            "anyOf": [
+              {
+                "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": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "string"
+            }
+          ],
+          "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"
+        },
+        "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"
+        },
+        "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"
+        },
+        "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"
+          },
+          "type": "array"
+        },
+        "requiresIf": {
+          "additionalItems": {
+            "anyOf": [
+              {
+                "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": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "string"
+            }
+          ],
+          "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"
+        },
+        "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"
+    },
+    "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"
+          },
+          "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"
+        },
+        "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"
+        },
+        "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"
+        }
+      },
+      "type": "object"
+    }
+  },
+  "description": "Tauri configuration",
+  "properties": {
+    "build": {
+      "additionalProperties": false,
+      "defaultProperties": [
+      ],
+      "description": "build/dev configuration",
+      "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"
+        },
+        "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"
+        },
+        "withGlobalTauri": {
+          "type": "boolean"
+        }
+      },
+      "required": [
+        "devPath",
+        "distDir"
+      ],
+      "type": "object"
+    },
+    "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"
+        },
+        "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"
+        },
+        "target": {
+          "description": "the target of the compilation (see `rustup target list`)",
+          "type": "string"
+        }
+      },
+      "type": "object"
+    },
+    "tauri": {
+      "additionalProperties": false,
+      "defaultProperties": [
+      ],
+      "description": "tauri root configuration object",
+      "properties": {
+        "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"
+            },
+            "copyright": {
+              "type": "string"
+            },
+            "deb": {
+              "additionalProperties": false,
+              "defaultProperties": [
+              ],
+              "properties": {
+                "depends": {
+                  "items": {
+                    "type": "string"
+                  },
+                  "type": "array"
+                },
+                "useBootstrapper": {
+                  "type": "boolean"
+                }
+              },
+              "type": "object"
+            },
+            "exceptionDomain": {
+              "type": "string"
+            },
+            "externalBin": {
+              "items": {
+                "type": "string"
+              },
+              "type": "array"
+            },
+            "icon": {
+              "description": "the app's icons",
+              "items": {
+                "type": "string"
+              },
+              "type": "array"
+            },
+            "identifier": {
+              "description": "the app's identifier",
+              "type": "string"
+            },
+            "longDescription": {
+              "type": "string"
+            },
+            "osx": {
+              "additionalProperties": false,
+              "defaultProperties": [
+              ],
+              "properties": {
+                "frameworks": {
+                  "items": {
+                    "type": "string"
+                  },
+                  "type": "array"
+                },
+                "license": {
+                  "type": "string"
+                },
+                "minimumSystemVersion": {
+                  "type": "string"
+                },
+                "useBootstrapper": {
+                  "type": "boolean"
+                }
+              },
+              "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"
+              },
+              "type": "array"
+            },
+            "shortDescription": {
+              "type": "string"
+            },
+            "targets": {
+              "anyOf": [
+                {
+                  "items": {
+                    "type": "string"
+                  },
+                  "type": "array"
+                },
+                {
+                  "type": "string"
+                }
+              ],
+              "description": "the bundle targets, currently supports [\"deb\", \"osx\", \"msi\", \"appimage\", \"dmg\"] or \"all\""
+            }
+          },
+          "required": [
+            "icon",
+            "identifier"
+          ],
+          "type": "object"
+        },
+        "cli": {
+          "$ref": "#/definitions/CliConfig",
+          "description": "app's CLI definition"
+        },
+        "edge": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "active": {
+              "type": "boolean"
+            }
+          },
+          "type": "object"
+        },
+        "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"
+            }
+          },
+          "type": "object"
+        },
+        "inliner": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "active": {
+              "type": "boolean"
+            }
+          },
+          "type": "object"
+        },
+        "security": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "csp": {
+              "type": "string"
+            }
+          },
+          "type": "object"
+        },
+        "whitelist": {
+          "additionalProperties": {
+            "type": "boolean"
+          },
+          "defaultProperties": [
+          ],
+          "properties": {
+            "all": {
+              "type": "boolean"
+            }
+          },
+          "required": [
+            "all"
+          ],
+          "type": "object"
+        },
+        "window": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "fullscreen": {
+              "type": "boolean"
+            },
+            "height": {
+              "type": "number"
+            },
+            "resizable": {
+              "type": "boolean"
+            },
+            "title": {
+              "type": "string"
+            },
+            "width": {
+              "type": "number"
+            }
+          },
+          "required": [
+            "title"
+          ],
+          "type": "object"
+        }
+      },
+      "required": [
+        "bundle",
+        "edge",
+        "embeddedServer",
+        "inliner",
+        "security",
+        "whitelist",
+        "window"
+      ],
+      "type": "object"
+    }
+  },
+  "required": [
+    "build",
+    "ctx",
+    "tauri"
+  ],
+  "type": "object"
+}

+ 6 - 0
cli/tauri.js/src/types/config.ts

@@ -35,6 +35,10 @@ export interface CliArg {
    * for 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.
    */
   multiple?: boolean
+  /**
+   * specifies that the argument may appear more than once.
+   */
+  multipleOccurrences?: boolean
   /**
    * 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.
    */
@@ -285,3 +289,5 @@ export interface TauriConfig {
     }
   }
 }
+
+export default TauriConfig

+ 512 - 0
cli/tauri.js/src/types/config.validator.ts

@@ -0,0 +1,512 @@
+/* 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});
+
+ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
+
+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"
+        },
+        "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"
+        },
+        "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"
+        },
+        "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"
+        },
+        "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"
+        },
+        "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"
+          },
+          "type": "array"
+        },
+        "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"
+        },
+        "requiredIf": {
+          "additionalItems": {
+            "anyOf": [
+              {
+                "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": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "string"
+            }
+          ],
+          "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"
+        },
+        "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"
+        },
+        "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"
+        },
+        "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"
+          },
+          "type": "array"
+        },
+        "requiresIf": {
+          "additionalItems": {
+            "anyOf": [
+              {
+                "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": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "string"
+            }
+          ],
+          "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"
+        },
+        "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"
+    },
+    "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"
+          },
+          "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"
+        },
+        "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"
+        },
+        "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"
+        }
+      },
+      "type": "object"
+    }
+  },
+  "description": "Tauri configuration",
+  "properties": {
+    "build": {
+      "additionalProperties": false,
+      "defaultProperties": [
+      ],
+      "description": "build/dev configuration",
+      "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"
+        },
+        "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"
+        },
+        "withGlobalTauri": {
+          "type": "boolean"
+        }
+      },
+      "required": [
+        "devPath",
+        "distDir"
+      ],
+      "type": "object"
+    },
+    "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"
+        },
+        "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"
+        },
+        "target": {
+          "description": "the target of the compilation (see `rustup target list`)",
+          "type": "string"
+        }
+      },
+      "type": "object"
+    },
+    "tauri": {
+      "additionalProperties": false,
+      "defaultProperties": [
+      ],
+      "description": "tauri root configuration object",
+      "properties": {
+        "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"
+            },
+            "copyright": {
+              "type": "string"
+            },
+            "deb": {
+              "additionalProperties": false,
+              "defaultProperties": [
+              ],
+              "properties": {
+                "depends": {
+                  "items": {
+                    "type": "string"
+                  },
+                  "type": "array"
+                },
+                "useBootstrapper": {
+                  "type": "boolean"
+                }
+              },
+              "type": "object"
+            },
+            "exceptionDomain": {
+              "type": "string"
+            },
+            "externalBin": {
+              "items": {
+                "type": "string"
+              },
+              "type": "array"
+            },
+            "icon": {
+              "description": "the app's icons",
+              "items": {
+                "type": "string"
+              },
+              "type": "array"
+            },
+            "identifier": {
+              "description": "the app's identifier",
+              "type": "string"
+            },
+            "longDescription": {
+              "type": "string"
+            },
+            "osx": {
+              "additionalProperties": false,
+              "defaultProperties": [
+              ],
+              "properties": {
+                "frameworks": {
+                  "items": {
+                    "type": "string"
+                  },
+                  "type": "array"
+                },
+                "license": {
+                  "type": "string"
+                },
+                "minimumSystemVersion": {
+                  "type": "string"
+                },
+                "useBootstrapper": {
+                  "type": "boolean"
+                }
+              },
+              "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"
+              },
+              "type": "array"
+            },
+            "shortDescription": {
+              "type": "string"
+            },
+            "targets": {
+              "anyOf": [
+                {
+                  "items": {
+                    "type": "string"
+                  },
+                  "type": "array"
+                },
+                {
+                  "type": "string"
+                }
+              ],
+              "description": "the bundle targets, currently supports [\"deb\", \"osx\", \"msi\", \"appimage\", \"dmg\"] or \"all\""
+            }
+          },
+          "required": [
+            "icon",
+            "identifier"
+          ],
+          "type": "object"
+        },
+        "cli": {
+          "$ref": "#/definitions/CliConfig",
+          "description": "app's CLI definition"
+        },
+        "edge": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "active": {
+              "type": "boolean"
+            }
+          },
+          "type": "object"
+        },
+        "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"
+            }
+          },
+          "type": "object"
+        },
+        "inliner": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "active": {
+              "type": "boolean"
+            }
+          },
+          "type": "object"
+        },
+        "security": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "csp": {
+              "type": "string"
+            }
+          },
+          "type": "object"
+        },
+        "whitelist": {
+          "additionalProperties": {
+            "type": "boolean"
+          },
+          "defaultProperties": [
+          ],
+          "properties": {
+            "all": {
+              "type": "boolean"
+            }
+          },
+          "required": [
+            "all"
+          ],
+          "type": "object"
+        },
+        "window": {
+          "additionalProperties": false,
+          "defaultProperties": [
+          ],
+          "properties": {
+            "fullscreen": {
+              "type": "boolean"
+            },
+            "height": {
+              "type": "number"
+            },
+            "resizable": {
+              "type": "boolean"
+            },
+            "title": {
+              "type": "string"
+            },
+            "width": {
+              "type": "number"
+            }
+          },
+          "required": [
+            "title"
+          ],
+          "type": "object"
+        }
+      },
+      "required": [
+        "bundle",
+        "edge",
+        "embeddedServer",
+        "inliner",
+        "security",
+        "whitelist",
+        "window"
+      ],
+      "type": "object"
+    }
+  },
+  "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;
+  } else {
+    throw new Error(
+      ajv.errorsText(isTauriConfig.errors!.filter((e: any) => e.keyword !== 'if'), {dataVar: 'TauriConfig'}) +
+      '\n\n' +
+      inspect(value),
+    );
+  }
+}

+ 32 - 1
cli/tauri.js/webpack.config.js

@@ -1,5 +1,6 @@
 const path = require('path')
 const nodeExternals = require('webpack-node-externals')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
 
 module.exports = {
   entry: {
@@ -40,5 +41,35 @@ module.exports = {
     path: path.resolve(__dirname, 'dist')
   },
   externals: [nodeExternals()],
-  target: 'node'
+  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())
+        }
+      }]
+    })
+  ]
+}
+
+function schemaParser(schemaName, content) {
+  const lines = content.split('\n')
+  const output = []
+
+  for (const line of lines) {
+    if (line === `export const ${schemaName} = {`) {
+      output.push('{')
+    } else if (output.length) {
+      if (line === '};') {
+        output.push('}')
+        break
+      }
+      output.push(line)
+    }
+  }
+
+  return output.join("\n")
 }

+ 192 - 14
cli/tauri.js/yarn.lock

@@ -1276,6 +1276,13 @@
     "@nodelib/fs.scandir" "2.1.3"
     fastq "^1.6.0"
 
+"@npmcli/move-file@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
+  integrity sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==
+  dependencies:
+    mkdirp "^1.0.4"
+
 "@rollup/plugin-babel@5.0.4":
   version "5.0.4"
   resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.0.4.tgz#dbfcf86a0561e399579ceb1a534a83c970e76645"
@@ -1401,6 +1408,13 @@
   resolved "https://registry.yarnpkg.com/@tauri-apps/toml/-/toml-2.2.4.tgz#2b4f637aded7fc3a7302724605682c8fa3ac7505"
   integrity sha512-NJV/pdgJObDlDWi5+MTHZ2qyNvdL0dlHqQ72nzQYXWbW1LHMPXgCJYl0pLqL1XxxLtxtInYbtVCGVAcwhGxdkw==
 
+"@types/ajv@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@types/ajv/-/ajv-1.0.0.tgz#4fb2440742f2f6c30e7fb0797b839fc6f696682a"
+  integrity sha1-T7JEB0Ly9sMOf7B5e4OfxvaWaCo=
+  dependencies:
+    ajv "*"
+
 "@types/anymatch@*":
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
@@ -1455,7 +1469,7 @@
   resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
   integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
 
-"@types/cross-spawn@6.0.2":
+"@types/cross-spawn@6.0.2", "@types/cross-spawn@^6.0.0":
   version "6.0.2"
   resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.2.tgz#168309de311cd30a2b8ae720de6475c2fbf33ac7"
   integrity sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==
@@ -1560,6 +1574,11 @@
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
   integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
 
+"@types/json-stable-stringify@^1.0.32":
+  version "1.0.32"
+  resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz#121f6917c4389db3923640b2e68de5fa64dda88e"
+  integrity sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw==
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -1570,7 +1589,7 @@
   resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.157.tgz#fdac1c52448861dfde1a2e1515dbc46e54926dc8"
   integrity sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==
 
-"@types/minimatch@*":
+"@types/minimatch@*", "@types/minimatch@^3.0.3":
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
   integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
@@ -1995,6 +2014,16 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
   integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
 
+ajv@*:
+  version "6.12.3"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706"
+  integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
 ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5:
   version "6.12.2"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
@@ -2660,6 +2689,29 @@ cacache@^12.0.2:
     unique-filename "^1.1.1"
     y18n "^4.0.0"
 
+cacache@^15.0.4:
+  version "15.0.4"
+  resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.4.tgz#b2c23cf4ac4f5ead004fb15a0efb0a20340741f1"
+  integrity sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw==
+  dependencies:
+    "@npmcli/move-file" "^1.0.1"
+    chownr "^2.0.0"
+    fs-minipass "^2.0.0"
+    glob "^7.1.4"
+    infer-owner "^1.0.4"
+    lru-cache "^5.1.1"
+    minipass "^3.1.1"
+    minipass-collect "^1.0.2"
+    minipass-flush "^1.0.5"
+    minipass-pipeline "^1.2.2"
+    mkdirp "^1.0.3"
+    p-map "^4.0.0"
+    promise-inflight "^1.0.1"
+    rimraf "^3.0.2"
+    ssri "^8.0.0"
+    tar "^6.0.2"
+    unique-filename "^1.1.1"
+
 cache-base@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -3163,6 +3215,23 @@ copy-descriptor@^0.1.0:
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
   integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
 
+copy-webpack-plugin@^6.0.3:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz#2b3d2bfc6861b96432a65f0149720adbd902040b"
+  integrity sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA==
+  dependencies:
+    cacache "^15.0.4"
+    fast-glob "^3.2.4"
+    find-cache-dir "^3.3.1"
+    glob-parent "^5.1.1"
+    globby "^11.0.1"
+    loader-utils "^2.0.0"
+    normalize-path "^3.0.0"
+    p-limit "^3.0.1"
+    schema-utils "^2.7.0"
+    serialize-javascript "^4.0.0"
+    webpack-sources "^1.4.3"
+
 core-js-compat@^3.6.2:
   version "3.6.5"
   resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c"
@@ -4300,7 +4369,7 @@ fast-deep-equal@^3.1.1:
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
   integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
 
-fast-glob@3.2.4:
+fast-glob@3.2.4, fast-glob@^3.1.1, fast-glob@^3.2.4:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
   integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
@@ -4743,14 +4812,14 @@ glob-parent@^3.1.0:
     is-glob "^3.1.0"
     path-dirname "^1.0.0"
 
-glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
+glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
   integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
   dependencies:
     is-glob "^4.0.1"
 
-glob@7.1.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+glob@7.1.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.4:
   version "7.1.6"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
   integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -4831,6 +4900,18 @@ globby@^10.0.0:
     merge2 "^1.2.3"
     slash "^3.0.0"
 
+globby@^11.0.1:
+  version "11.0.1"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357"
+  integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==
+  dependencies:
+    array-union "^2.1.0"
+    dir-glob "^3.0.1"
+    fast-glob "^3.1.1"
+    ignore "^5.1.4"
+    merge2 "^1.3.0"
+    slash "^3.0.0"
+
 got@^7.0.0:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
@@ -5151,6 +5232,11 @@ ignore@^5.1.1:
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.6.tgz#643194ad4bf2712f37852e386b6998eff0db2106"
   integrity sha512-cgXgkypZBcCnOgSihyeqbo6gjIaIyDqPQB7Ra4vhE9m6kigdGoQDMHjviFhRZo3IMlRy6yElosoviMs5YxZXUA==
 
+ignore@^5.1.4:
+  version "5.1.8"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
+  integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
+
 imagemin-optipng@8.0.0:
   version "8.0.0"
   resolved "https://registry.yarnpkg.com/imagemin-optipng/-/imagemin-optipng-8.0.0.tgz#b88e5cf6da25cc8479e07cdf38c3ae0479df7ef2"
@@ -5244,7 +5330,7 @@ indent-string@^4.0.0:
   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
   integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
 
-infer-owner@^1.0.3:
+infer-owner@^1.0.3, infer-owner@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
   integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
@@ -6153,6 +6239,13 @@ json-stable-stringify-without-jsonify@^1.0.1:
   resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
   integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
 
+json-stable-stringify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+  integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
+  dependencies:
+    jsonify "~0.0.0"
+
 json-stringify-safe@~5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -6165,7 +6258,7 @@ json5@^1.0.1:
   dependencies:
     minimist "^1.2.0"
 
-json5@^2.1.2:
+json5@^2.1.1, json5@^2.1.2:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
   integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
@@ -6188,6 +6281,11 @@ jsonfile@^6.0.1:
   optionalDependencies:
     graceful-fs "^4.1.6"
 
+jsonify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+  integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
+
 jsprim@^1.2.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -6770,7 +6868,28 @@ minimist@1.2.5, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
   integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
 
-minipass@^3.0.0:
+minipass-collect@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
+  integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
+  dependencies:
+    minipass "^3.0.0"
+
+minipass-flush@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
+  integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
+  dependencies:
+    minipass "^3.0.0"
+
+minipass-pipeline@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz#55f7839307d74859d6e8ada9c3ebe72cec216a34"
+  integrity sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ==
+  dependencies:
+    minipass "^3.0.0"
+
+minipass@^3.0.0, minipass@^3.1.1:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
   integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==
@@ -6821,7 +6940,7 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
-mkdirp@^1.0.3:
+mkdirp@^1.0.3, mkdirp@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
@@ -7262,6 +7381,13 @@ p-limit@^2.0.0, p-limit@^2.2.0:
   dependencies:
     p-try "^2.0.0"
 
+p-limit@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.1.tgz#584784ac0722d1aed09f19f90ed2999af6ce2839"
+  integrity sha512-mw/p92EyOzl2MhauKodw54Rx5ZK4624rNfgNaBguFZkHzyUG9WsDzFF5/yQVEJinbJDdP4jEfMN+uBquiGnaLg==
+  dependencies:
+    p-try "^2.0.0"
+
 p-locate@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -8154,7 +8280,7 @@ resolve@1.15.1:
   dependencies:
     path-parse "^1.0.6"
 
-resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.13.1, resolve@^1.14.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.3.2:
+resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.13.1, resolve@^1.14.1, resolve@^1.14.2, resolve@^1.15.1, resolve@^1.17.0, resolve@^1.3.2:
   version "1.17.0"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
   integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
@@ -8325,7 +8451,7 @@ schema-utils@^1.0.0:
     ajv-errors "^1.0.0"
     ajv-keywords "^3.1.0"
 
-schema-utils@^2.6.5:
+schema-utils@^2.6.5, schema-utils@^2.7.0:
   version "2.7.0"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
   integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
@@ -8397,6 +8523,13 @@ serialize-javascript@^3.0.0:
   dependencies:
     randombytes "^2.1.0"
 
+serialize-javascript@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
+  integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
+  dependencies:
+    randombytes "^2.1.0"
+
 set-blocking@^2.0.0, set-blocking@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -8710,6 +8843,13 @@ ssri@^6.0.1:
   dependencies:
     figgy-pudding "^3.5.1"
 
+ssri@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
+  integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
+  dependencies:
+    minipass "^3.1.1"
+
 stable@^0.1.8:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
@@ -9339,6 +9479,16 @@ ts-loader@7.0.5:
     micromatch "^4.0.0"
     semver "^6.0.0"
 
+tsconfig-loader@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/tsconfig-loader/-/tsconfig-loader-1.1.0.tgz#90f60a569a65569c1096719c112b48b3f13f9fc1"
+  integrity sha512-KrFF45RYo/JHpoAp1Lf68NupYNyRmh7BwSh1AmAQ3fdCMl8laOyZSLO5iByQR2VTkVdt454HS3c5kfVeYWq7iQ==
+  dependencies:
+    deepmerge "^4.2.2"
+    json5 "^2.1.1"
+    resolve "^1.15.1"
+    strip-bom "^4.0.0"
+
 tsconfig-paths@^3.9.0:
   version "3.9.0"
   resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
@@ -9434,7 +9584,35 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
-typescript@^3.9.6:
+typescript-json-schema@^0.38.3:
+  version "0.38.3"
+  resolved "https://registry.yarnpkg.com/typescript-json-schema/-/typescript-json-schema-0.38.3.tgz#90faca22860a656680ebcde102c8510b6b349c65"
+  integrity sha512-+13qUoBUQwOXqxUoYQWtLA9PEM7ojfv8r+hYc2ebeqqVwVM4+yI5JSlsYRBlJKKewc9q1FHqrMR6L6d9TNX9Dw==
+  dependencies:
+    glob "~7.1.4"
+    json-stable-stringify "^1.0.1"
+    typescript "^3.5.1"
+    yargs "^13.2.4"
+
+typescript-json-validator@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/typescript-json-validator/-/typescript-json-validator-2.4.2.tgz#8056746eaf77f364b5ca4b0dabdfcfd51e3e8af4"
+  integrity sha512-4oliZJGo8jwRAWxssz1n7KiNo21AwN/XqXm8l66k1sH3emqrulR2EGjsNfLV95/JD07C1YIkFlvClOlNANghag==
+  dependencies:
+    "@types/ajv" "^1.0.0"
+    "@types/cross-spawn" "^6.0.0"
+    "@types/glob" "^7.1.1"
+    "@types/json-stable-stringify" "^1.0.32"
+    "@types/minimatch" "^3.0.3"
+    cross-spawn "^6.0.5"
+    glob "^7.1.3"
+    json-stable-stringify "^1.0.1"
+    minimatch "^3.0.4"
+    tsconfig-loader "^1.1.0"
+    typescript-json-schema "^0.38.3"
+    yargs "^13.2.4"
+
+typescript@^3.5.1, typescript@^3.9.6:
   version "3.9.6"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
   integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
@@ -9762,7 +9940,7 @@ webpack-shell-plugin@0.5.0:
   resolved "https://registry.yarnpkg.com/webpack-shell-plugin/-/webpack-shell-plugin-0.5.0.tgz#29b8a1d80ddeae0ddb10e729667f728653c2c742"
   integrity sha1-Kbih2A3erg3bEOcpZn9yhlPCx0I=
 
-webpack-sources@^1.4.0, webpack-sources@^1.4.1:
+webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
   integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
@@ -9985,7 +10163,7 @@ yargs-parser@^18.1.1:
     camelcase "^5.0.0"
     decamelize "^1.2.0"
 
-yargs@^13.3.2:
+yargs@^13.2.4, yargs@^13.3.2:
   version "13.3.2"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
   integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==

+ 1 - 8
tauri/examples/communication/src-tauri/tauri.conf.json

@@ -8,9 +8,6 @@
   "tauri": {
     "cli": {
       "description": "Tauri communication example",
-      "longDescription": null,
-      "beforeHelp": null,
-      "afterHelp": null,
       "args": [{
         "short": "c",
         "name": "config",
@@ -31,15 +28,11 @@
       "subcommands": {
         "update": {
           "description": "Updates the app",
-          "longDescription": null,
-          "beforeHelp": null,
-          "afterHelp": null,
           "args": [{
             "short": "b",
             "name": "background",
             "description": "Update in background"
-          }],
-          "subcommands": null
+          }]
         }
       }
     },