mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
feat(icons): add and test icon generation for tauri (#55)
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -62,3 +62,4 @@ package-lock.json
|
|||||||
|
|
||||||
|
|
||||||
src-tauri
|
src-tauri
|
||||||
|
test/jest/tmp
|
||||||
64
README.md
64
README.md
@@ -1,7 +1,7 @@
|
|||||||
# tauri [WIP]
|
# tauri [WIP]
|
||||||
## A fresh take on creating cross-platform apps.
|
## A fresh take on creating cross-platform apps.
|
||||||
[](https://github.com/quasarframework/quasar/tree/tauri)
|
[](https://github.com/quasarframework/quasar/tree/tauri)
|
||||||
[](https://github.com/tauri-apps/tauri/tree/dev) <img align="right" src="/tauri-logo.png" height="240" width="240">
|
[](https://github.com/tauri-apps/tauri/tree/dev) <img align="right" src="/app-icon.png" height="240" width="240">
|
||||||
|
|
||||||
[](https://discord.gg/SpmNs4S)
|
[](https://discord.gg/SpmNs4S)
|
||||||
[](https://dev.to/tauri)
|
[](https://dev.to/tauri)
|
||||||
@@ -10,64 +10,64 @@
|
|||||||
[](https://opencollective.com/tauri)
|
[](https://opencollective.com/tauri)
|
||||||
|
|
||||||
|
|
||||||
**Tauri** is a tool for building tiny, blazing fast binaries for all
|
**Tauri** is a tool for building tiny, blazing fast binaries for all
|
||||||
major desktop platforms. It was incubated at Quasar Framework.
|
major desktop platforms. It was incubated at Quasar Framework.
|
||||||
|
|
||||||
Whether you are just starting out making apps for your meetup or
|
Whether you are just starting out making apps for your meetup or
|
||||||
regularly crunch terabyte datasets, we are absolutely confident that
|
regularly crunch terabyte datasets, we are absolutely confident that
|
||||||
you will love using Tauri as much as we love making and maintaining it.
|
you will love using Tauri as much as we love making and maintaining it.
|
||||||
|
|
||||||
## Who Tauri is For
|
## Who Tauri is For
|
||||||
Because of the way Tauri has been built and can be extended, developers
|
Because of the way Tauri has been built and can be extended, developers
|
||||||
are able to interface not only with the entire Rust ecosystem, but also
|
are able to interface not only with the entire Rust ecosystem, but also
|
||||||
with many other programming languages. Being freed of the heaviest thing
|
with many other programming languages. Being freed of the heaviest thing
|
||||||
in the universe and the many shortcomings of server-side Javascript
|
in the universe and the many shortcomings of server-side Javascript
|
||||||
suddenly opens up whole new avenues for high-performance, security-focused
|
suddenly opens up whole new avenues for high-performance, security-focused
|
||||||
applications that need the purebred power, agility and community
|
applications that need the purebred power, agility and community
|
||||||
acceptance of a low-level language.
|
acceptance of a low-level language.
|
||||||
|
|
||||||
We expect to witness an entire new class of applications being built with
|
We expect to witness an entire new class of applications being built with
|
||||||
Tauri. From a simple calender to locally crunching massive realtime
|
Tauri. From a simple calender to locally crunching massive realtime
|
||||||
feeds at particle colliders or even mesh-network based distributed message-
|
feeds at particle colliders or even mesh-network based distributed message-
|
||||||
passing ecosystems - the bar has been raised and gauntlet thrown.
|
passing ecosystems - the bar has been raised and gauntlet thrown.
|
||||||
|
|
||||||
What will you make?
|
What will you make?
|
||||||
|
|
||||||
## 5 Reasons to consider Tauri
|
## 5 Reasons to consider Tauri
|
||||||
- **BUNDLE SIZE** of a vanilla Tauri app is less than 3 MB - about 140 MB smaller than what you get with Electron.
|
- **BUNDLE SIZE** of a vanilla Tauri app is less than 3 MB - about 140 MB smaller than what you get with Electron.
|
||||||
- **MEMORY FOOTPRINT** is less than half of the size of an Electron app built from the same codebase.
|
- **MEMORY FOOTPRINT** is less than half of the size of an Electron app built from the same codebase.
|
||||||
- **SECURITY** is Tauri's biggest priority and we take it so seriously that we innovate to keep hackers out of your apps.
|
- **SECURITY** is Tauri's biggest priority and we take it so seriously that we innovate to keep hackers out of your apps.
|
||||||
- **RELIABILITY** of the underlying code base is why critical libraries have been forked and will be perpetually maintained.
|
- **RELIABILITY** of the underlying code base is why critical libraries have been forked and will be perpetually maintained.
|
||||||
- **FLOSS** licensing is regretfully impossible with downstream Chromium consumers, like Electron. Sources: [0](https://lists.gnu.org/archive/html/libreplanet-discuss/2017-01/msg00056.html) [1](https://lists.gnu.org/archive/html/directory-discuss/2017-12/msg00008.html) [2](https://lists.gnu.org/archive/html/libreplanet-discuss/2019-02/msg00001.html)
|
- **FLOSS** licensing is regretfully impossible with downstream Chromium consumers, like Electron. Sources: [0](https://lists.gnu.org/archive/html/libreplanet-discuss/2017-01/msg00056.html) [1](https://lists.gnu.org/archive/html/directory-discuss/2017-12/msg00008.html) [2](https://lists.gnu.org/archive/html/libreplanet-discuss/2019-02/msg00001.html)
|
||||||
|
|
||||||
## Technical Details
|
## Technical Details
|
||||||
The user interface in Tauri apps currently leverages Cocoa/WebKit on macOS,
|
The user interface in Tauri apps currently leverages Cocoa/WebKit on macOS,
|
||||||
gtk-webkit2 on Linux and MSHTML (IE10/11) or Webkit via Edge on Windows.
|
gtk-webkit2 on Linux and MSHTML (IE10/11) or Webkit via Edge on Windows.
|
||||||
**Tauri** is based on the MIT licensed prior work known as
|
**Tauri** is based on the MIT licensed prior work known as
|
||||||
[webview](https://github.com/zserge/webview).
|
[webview](https://github.com/zserge/webview).
|
||||||
|
|
||||||
The default binding to the underlying webview library currently uses Rust,
|
The default binding to the underlying webview library currently uses Rust,
|
||||||
but other languages like Golang or Python (and many others) are possible
|
but other languages like Golang or Python (and many others) are possible
|
||||||
(and only a PR away).
|
(and only a PR away).
|
||||||
|
|
||||||
> Rust is blazingly fast and memory-efficient: with no runtime or garbage
|
> Rust is blazingly fast and memory-efficient: with no runtime or garbage
|
||||||
collector, it can power performance-critical services, run on embedded
|
collector, it can power performance-critical services, run on embedded
|
||||||
devices, and easily integrate with other languages. Rust’s rich type system
|
devices, and easily integrate with other languages. Rust’s rich type system
|
||||||
and ownership model guarantee memory-safety and thread-safety — and enable
|
and ownership model guarantee memory-safety and thread-safety — and enable
|
||||||
you to eliminate many classes of bugs at compile-time. Rust has great
|
you to eliminate many classes of bugs at compile-time. Rust has great
|
||||||
documentation, a friendly compiler with useful error messages, and top-notch
|
documentation, a friendly compiler with useful error messages, and top-notch
|
||||||
tooling — an integrated package manager and build tool, smart multi-editor
|
tooling — an integrated package manager and build tool, smart multi-editor
|
||||||
support with auto-completion and type inspections, an auto-formatter, and
|
support with auto-completion and type inspections, an auto-formatter, and
|
||||||
more. - [https://www.rust-lang.org/](https://www.rust-lang.org/)
|
more. - [https://www.rust-lang.org/](https://www.rust-lang.org/)
|
||||||
|
|
||||||
This combination of power, safety and usability are why we chose Rust to be
|
This combination of power, safety and usability are why we chose Rust to be
|
||||||
the default binding for Tauri. It is our intention to provide the most safe
|
the default binding for Tauri. It is our intention to provide the most safe
|
||||||
and performant native app experience (for devs and app consumers), out of
|
and performant native app experience (for devs and app consumers), out of
|
||||||
the box.
|
the box.
|
||||||
|
|
||||||
To this end, we have spent a great deal of time creating an especially secure
|
To this end, we have spent a great deal of time creating an especially secure
|
||||||
localhost-free backend for the security conscious application-artisans. This
|
localhost-free backend for the security conscious application-artisans. This
|
||||||
means that your app does not use a localhost server, as is generally the case with
|
means that your app does not use a localhost server, as is generally the case with
|
||||||
cordova apps. This also has the positive side effect, that less code is needed
|
cordova apps. This also has the positive side effect, that less code is needed
|
||||||
and the final binaries are smaller.
|
and the final binaries are smaller.
|
||||||
|
|
||||||
@@ -93,12 +93,12 @@ you through the process. Here is a bit of a status report.
|
|||||||
- [ ] Tray (coming soon)
|
- [ ] Tray (coming soon)
|
||||||
- [x] Copy Buffer
|
- [x] Copy Buffer
|
||||||
|
|
||||||
#### API
|
#### API
|
||||||
- [ ] answer - enable rust to direct the UI
|
- [ ] answer - enable rust to direct the UI
|
||||||
- [ ] bridge - enable Quasar Bridge
|
- [ ] bridge - enable Quasar Bridge
|
||||||
- [x] event - enable binding to message
|
- [x] event - enable binding to message
|
||||||
- [x] execute - STDOUT Passthrough with Command Invocation
|
- [x] execute - STDOUT Passthrough with Command Invocation
|
||||||
- [x] listFiles - list files in a directory
|
- [x] listFiles - list files in a directory
|
||||||
- [x] open - open link in a browser
|
- [x] open - open link in a browser
|
||||||
- [x] readBinaryFile - read binary file from local filesystem
|
- [x] readBinaryFile - read binary file from local filesystem
|
||||||
- [x] readTextFile - read text file from local filesystem
|
- [x] readTextFile - read text file from local filesystem
|
||||||
@@ -161,20 +161,20 @@ projects here in this repository, in order to guarantee the security of the
|
|||||||
code and our ability to enhance it with features that may not be needed for
|
code and our ability to enhance it with features that may not be needed for
|
||||||
other consumers.
|
other consumers.
|
||||||
|
|
||||||
We hope that this code is useful, but make no claims to suitability or
|
We hope that this code is useful, but make no claims to suitability or
|
||||||
guarantees that it will work outside of the Quasar ecosystem.
|
guarantees that it will work outside of the Quasar ecosystem.
|
||||||
|
|
||||||
This has been done with our best attempt at due diligence and in
|
This has been done with our best attempt at due diligence and in
|
||||||
respect of the original authors. Thankyou - this project would never have
|
respect of the original authors. Thankyou - this project would never have
|
||||||
been possible without your amazing contribution to open-source and we are
|
been possible without your amazing contribution to open-source and we are
|
||||||
honoured to carry the torch further. Of special note:
|
honoured to carry the torch further. Of special note:
|
||||||
- [zserge](https://github.com/zserge) for the original webview approach and
|
- [zserge](https://github.com/zserge) for the original webview approach and
|
||||||
go bindings
|
go bindings
|
||||||
- [Boscop](https://github.com/Boscop) for the Rust Bindings
|
- [Boscop](https://github.com/Boscop) for the Rust Bindings
|
||||||
- [Burtonago](https://github.com/burtonageo) for the Cargo Bundle prototype
|
- [Burtonago](https://github.com/burtonageo) for the Cargo Bundle prototype
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
Please make sure to read the [Contributing Guide](./.github/CONTRIBUTING.md)
|
Please make sure to read the [Contributing Guide](./.github/CONTRIBUTING.md)
|
||||||
before making a pull request.
|
before making a pull request.
|
||||||
|
|
||||||
Thank you to all the people who already contributed to Tauri!
|
Thank you to all the people who already contributed to Tauri!
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
@@ -11,7 +11,7 @@ module.exports = {
|
|||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
coverageDirectory: '<rootDir>/test/jest/coverage',
|
coverageDirectory: '<rootDir>/test/jest/coverage',
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
'<rootDir>/mode/**/*.js',
|
'<rootDir>/mode/**/*.js'
|
||||||
],
|
],
|
||||||
coverageReporters: ['json-summary', 'text', 'lcov'],
|
coverageReporters: ['json-summary', 'text', 'lcov'],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
@@ -29,7 +29,8 @@ module.exports = {
|
|||||||
moduleFileExtensions: ['js', 'json'],
|
moduleFileExtensions: ['js', 'json'],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^~/(.*)$': '<rootDir>/$1',
|
'^~/(.*)$': '<rootDir>/$1',
|
||||||
'^mode/(.*)$': '<rootDir>/mode/$1'
|
'^mode/(.*)$': '<rootDir>/mode/$1',
|
||||||
|
'^test/(.*)$': '<rootDir>/test/$1'
|
||||||
},
|
},
|
||||||
transform: {}
|
transform: {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
const
|
const parseArgs = require('minimist')
|
||||||
parseArgs = require('minimist')
|
|
||||||
|
|
||||||
const argv = parseArgs(process.argv.slice(2), {
|
const argv = parseArgs(process.argv.slice(2), {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
61
mode/bin/tauri-icon.js
Normal file
61
mode/bin/tauri-icon.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
const parseArgs = require('minimist')
|
||||||
|
const { appDir, tauriDir } = require('../helpers/app-paths')
|
||||||
|
const logger = require('../helpers/logger')
|
||||||
|
const log = logger('app:tauri')
|
||||||
|
const warn = logger('app:tauri (icon)', 'red')
|
||||||
|
const { tauricon } = require('../helpers/tauricon')
|
||||||
|
const { resolve } = require('path')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {object}
|
||||||
|
* @property {boolean} h
|
||||||
|
* @property {boolean} help
|
||||||
|
* @property {string|boolean} f
|
||||||
|
* @property {string|boolean} force
|
||||||
|
* @property {boolean} l
|
||||||
|
* @property {boolean} log
|
||||||
|
* @property {boolean} c
|
||||||
|
* @property {boolean} config
|
||||||
|
* @property {boolean} s
|
||||||
|
* @property {boolean} source
|
||||||
|
* @property {boolean} t
|
||||||
|
* @property {boolean} target
|
||||||
|
*/
|
||||||
|
const argv = parseArgs(process.argv.slice(2), {
|
||||||
|
alias: {
|
||||||
|
h: 'help',
|
||||||
|
l: 'log',
|
||||||
|
c: 'config',
|
||||||
|
s: 'source',
|
||||||
|
t: 'target'
|
||||||
|
},
|
||||||
|
boolean: ['h', 'l']
|
||||||
|
})
|
||||||
|
|
||||||
|
if (argv.help) {
|
||||||
|
console.log(`
|
||||||
|
Description
|
||||||
|
Create all the icons you need for your Tauri app.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
$ tauri icon
|
||||||
|
|
||||||
|
Options
|
||||||
|
--help, -h Displays this message
|
||||||
|
--log, l Logging [boolean]
|
||||||
|
--icon, i Source icon (png, 1240x1240 with transparency)
|
||||||
|
--target, t Target folder (default: 'src-tauri/icons')
|
||||||
|
--compression, c Compression type [pngquant|optipng|zopfli]
|
||||||
|
`)
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
tauricon.make(
|
||||||
|
argv.i || resolve(appDir, 'app-icon.png'),
|
||||||
|
argv.t || resolve(tauriDir, 'icons'),
|
||||||
|
argv.c || 'optipng'
|
||||||
|
).then(() => {
|
||||||
|
log('(tauricon) Completed')
|
||||||
|
}).catch(e => {
|
||||||
|
warn(e)
|
||||||
|
})
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
const
|
const parseArgs = require('minimist')
|
||||||
parseArgs = require('minimist')
|
|
||||||
const appPaths = require('../helpers/app-paths')
|
const appPaths = require('../helpers/app-paths')
|
||||||
const logger = require('../helpers/logger')
|
const logger = require('../helpers/logger')
|
||||||
const log = logger('app:tauri')
|
const log = logger('app:tauri')
|
||||||
@@ -13,6 +12,8 @@ const warn = logger('app:tauri (init)', 'red')
|
|||||||
* @property {string|boolean} force
|
* @property {string|boolean} force
|
||||||
* @property {boolean} l
|
* @property {boolean} l
|
||||||
* @property {boolean} log
|
* @property {boolean} log
|
||||||
|
* @property {boolean} d
|
||||||
|
* @property {boolean} directory
|
||||||
*/
|
*/
|
||||||
const argv = parseArgs(process.argv.slice(2), {
|
const argv = parseArgs(process.argv.slice(2), {
|
||||||
alias: {
|
alias: {
|
||||||
@@ -44,7 +45,7 @@ const { inject } = require('../template')
|
|||||||
|
|
||||||
const target = appPaths.tauriDir
|
const target = appPaths.tauriDir
|
||||||
|
|
||||||
if (inject(target, 'all', argv.f, argv.l, argv.d)) {
|
if (inject(target, 'all', argv.f || null, argv.l || null, argv.d || null)) {
|
||||||
log('tauri init successful')
|
log('tauri init successful')
|
||||||
} else {
|
} else {
|
||||||
warn('tauri init unsuccessful')
|
warn('tauri init unsuccessful')
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const cmds = ['init', 'dev', 'build', 'help']
|
const cmds = ['init', 'dev', 'build', 'help', 'icon']
|
||||||
|
|
||||||
const cmd = process.argv[2]
|
const cmd = process.argv[2]
|
||||||
|
/**
|
||||||
|
* @description This is the bootstrapper that in turn calls subsequent
|
||||||
|
* Tauri Commands
|
||||||
|
*
|
||||||
|
* @param {string|array} command
|
||||||
|
*/
|
||||||
const tauri = function (command) {
|
const tauri = function (command) {
|
||||||
|
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(`
|
console.log(`
|
||||||
Description
|
Description
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
const
|
const { existsSync } = require('fs')
|
||||||
{ existsSync } = require('fs')
|
|
||||||
const { resolve, join, normalize, sep } = require('path')
|
const { resolve, join, normalize, sep } = require('path')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
92
mode/helpers/tauricon.config.js
Normal file
92
mode/helpers/tauricon.config.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
exports.options = {
|
||||||
|
// folder determines in which path to drop the generated file
|
||||||
|
// prefix is the first part of the generated file's name
|
||||||
|
// infix adds e.g. '44x44' based on the size in sizes to the generated file's name
|
||||||
|
// suffix adds a file-ending to the generated file's name
|
||||||
|
// sizes determines the pixel width and height to use
|
||||||
|
background_color: '#000074',
|
||||||
|
theme_color: '#02aa9b',
|
||||||
|
sharp: 'kernel: sharp.kernel.lanczos3', // one of [nearest|cubic|lanczos2|lanczos3]
|
||||||
|
minify: {
|
||||||
|
batch: false,
|
||||||
|
overwrite: true,
|
||||||
|
available: ['pngquant', 'optipng', 'zopfli'],
|
||||||
|
type: 'pngquant',
|
||||||
|
pngcrushOptions: {
|
||||||
|
reduce: true
|
||||||
|
},
|
||||||
|
pngquantOptions: {
|
||||||
|
quality: [0.6, 0.8],
|
||||||
|
floyd: 0.1, // 0.1 - 1
|
||||||
|
speed: 10 // 1 - 10
|
||||||
|
},
|
||||||
|
optipngOptions: {
|
||||||
|
optimizationLevel: 4,
|
||||||
|
bitDepthReduction: true,
|
||||||
|
colorTypeReduction: true,
|
||||||
|
paletteReduction: true
|
||||||
|
},
|
||||||
|
zopfliOptions: {
|
||||||
|
transparent: true,
|
||||||
|
more: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splash_type: 'generate',
|
||||||
|
tauri: {
|
||||||
|
linux: {
|
||||||
|
folder: '.',
|
||||||
|
prefix: '',
|
||||||
|
infix: true,
|
||||||
|
suffix: '.png',
|
||||||
|
sizes: [
|
||||||
|
32, 128
|
||||||
|
]
|
||||||
|
},
|
||||||
|
linux_2x: {
|
||||||
|
folder: '.',
|
||||||
|
prefix: '128x128@2x',
|
||||||
|
infix: false,
|
||||||
|
suffix: '.png',
|
||||||
|
sizes: [
|
||||||
|
256
|
||||||
|
]
|
||||||
|
},
|
||||||
|
defaults: {
|
||||||
|
folder: '.',
|
||||||
|
prefix: 'icon',
|
||||||
|
infix: false,
|
||||||
|
suffix: '.png',
|
||||||
|
sizes: [
|
||||||
|
512
|
||||||
|
]
|
||||||
|
},
|
||||||
|
appx_logo: {
|
||||||
|
folder: '.',
|
||||||
|
prefix: 'StoreLogo',
|
||||||
|
infix: false,
|
||||||
|
suffix: '.png',
|
||||||
|
sizes: [
|
||||||
|
50
|
||||||
|
]
|
||||||
|
},
|
||||||
|
appx_square: {
|
||||||
|
folder: '.',
|
||||||
|
prefix: 'Square',
|
||||||
|
infix: true,
|
||||||
|
suffix: 'Logo.png',
|
||||||
|
sizes: [
|
||||||
|
30,
|
||||||
|
44,
|
||||||
|
71,
|
||||||
|
89,
|
||||||
|
107,
|
||||||
|
142,
|
||||||
|
150,
|
||||||
|
284,
|
||||||
|
310
|
||||||
|
]
|
||||||
|
}
|
||||||
|
// todo: look at capacitor and cordova for insight into what icons
|
||||||
|
// we need for those distribution targets
|
||||||
|
}
|
||||||
|
}
|
||||||
420
mode/helpers/tauricon.js
Normal file
420
mode/helpers/tauricon.js
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a module that takes an original image and resizes
|
||||||
|
* it to common icon sizes and will put them in a folder.
|
||||||
|
* It will retain transparency and can make special file
|
||||||
|
* types. You can control the settings.
|
||||||
|
*
|
||||||
|
* @module tauricon
|
||||||
|
* @exports tauricon
|
||||||
|
* @author Daniel Thompson-Yvetot
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const sharp = require('sharp')
|
||||||
|
const imagemin = require('imagemin')
|
||||||
|
const pngquant = require('imagemin-pngquant')
|
||||||
|
const optipng = require('imagemin-optipng')
|
||||||
|
const zopfli = require('imagemin-zopfli')
|
||||||
|
const png2icons = require('png2icons')
|
||||||
|
const readChunk = require('read-chunk')
|
||||||
|
const isPng = require('is-png')
|
||||||
|
|
||||||
|
const settings = require('./tauricon.config')
|
||||||
|
let image = false
|
||||||
|
|
||||||
|
const {
|
||||||
|
access,
|
||||||
|
writeFileSync,
|
||||||
|
ensureDir,
|
||||||
|
ensureFileSync
|
||||||
|
} = require('fs-extra')
|
||||||
|
|
||||||
|
const exists = async function (file) {
|
||||||
|
try {
|
||||||
|
await access(file)
|
||||||
|
return true
|
||||||
|
} catch (err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the first call that attempts to memoize the sharp(src).
|
||||||
|
* If the source image cannot be found or if it is not a png, it
|
||||||
|
* is a failsafe that will exit or throw.
|
||||||
|
*
|
||||||
|
* @param {string} src - a folder to target
|
||||||
|
* @exits {error} if not a png, if not an image
|
||||||
|
*/
|
||||||
|
const checkSrc = async function (src) {
|
||||||
|
if (image !== false) {
|
||||||
|
return image
|
||||||
|
} else {
|
||||||
|
const srcExists = await exists(src)
|
||||||
|
if (!srcExists) {
|
||||||
|
image = false
|
||||||
|
throw new Error('[ERROR] Source image for tauricon not found')
|
||||||
|
} else {
|
||||||
|
const buffer = await readChunk(src, 0, 8)
|
||||||
|
if (isPng(buffer) === true) {
|
||||||
|
return (image = sharp(src))
|
||||||
|
} else {
|
||||||
|
image = false
|
||||||
|
throw new Error('[ERROR] Source image for tauricon is not a png')
|
||||||
|
// exit because this is BAD!
|
||||||
|
// Developers should catch () { } this as it is
|
||||||
|
// the last chance to stop bad things happening.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the folders in the current job for unique folders.
|
||||||
|
*
|
||||||
|
* @param {object} options - a subset of the settings
|
||||||
|
* @returns {array} folders
|
||||||
|
*/
|
||||||
|
const uniqueFolders = function (options) {
|
||||||
|
let folders = []
|
||||||
|
for (const type in options) {
|
||||||
|
if (options[type].folder) {
|
||||||
|
folders.push(options[type].folder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
folders = folders.sort().filter((x, i, a) => !i || x !== a[i - 1])
|
||||||
|
return folders
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a hex color (like #212342) into r,g,b values
|
||||||
|
*
|
||||||
|
* @param {string} hex - hex colour
|
||||||
|
* @returns {array} r,g,b
|
||||||
|
*/
|
||||||
|
const hexToRgb = function (hex) {
|
||||||
|
// 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 (m, r, g, b) {
|
||||||
|
return r + r + g + g + b + b
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* validate image and directory
|
||||||
|
* @param {string} src
|
||||||
|
* @param {string} target
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const validate = async function (src, target) {
|
||||||
|
if (target !== undefined) {
|
||||||
|
await ensureDir(target)
|
||||||
|
}
|
||||||
|
return checkSrc(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log progress in the command line
|
||||||
|
*
|
||||||
|
* @param {string} msg
|
||||||
|
* @param {boolean} end
|
||||||
|
*/
|
||||||
|
const progress = function (msg) {
|
||||||
|
console.log(msg)
|
||||||
|
// process.stdout.write(` ${msg} \r`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a spinner on the command line
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* const spinnerInterval = spinner()
|
||||||
|
* // later
|
||||||
|
* clearInterval(spinnerInterval)
|
||||||
|
* @returns {function} - the interval object
|
||||||
|
*/
|
||||||
|
const spinner = function () {
|
||||||
|
return setInterval(() => {
|
||||||
|
process.stdout.write('/ \r')
|
||||||
|
setTimeout(() => {
|
||||||
|
process.stdout.write('- \r')
|
||||||
|
setTimeout(() => {
|
||||||
|
process.stdout.write('\\ \r')
|
||||||
|
setTimeout(() => {
|
||||||
|
process.stdout.write('| \r')
|
||||||
|
}, 100)
|
||||||
|
}, 100)
|
||||||
|
}, 100)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tauricon = exports.tauricon = {
|
||||||
|
validate: async function (src, target) {
|
||||||
|
await validate(src, target)
|
||||||
|
return typeof image === 'object'
|
||||||
|
},
|
||||||
|
version: function () {
|
||||||
|
return require('../../package.json').version
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} src
|
||||||
|
* @param {string} target
|
||||||
|
* @param {string} strategy
|
||||||
|
* @param {object} options
|
||||||
|
*/
|
||||||
|
make: async function (src, target, strategy, options) {
|
||||||
|
const spinnerInterval = spinner()
|
||||||
|
options = options || settings.options.tauri
|
||||||
|
await this.validate(src, target)
|
||||||
|
progress('Building Tauri icns and ico')
|
||||||
|
await this.icns(src, target, options, strategy)
|
||||||
|
progress('Building Tauri png icons')
|
||||||
|
await this.build(src, target, options)
|
||||||
|
if (strategy) {
|
||||||
|
progress(`Minifying assets with ${strategy}`)
|
||||||
|
await this.minify(target, options, strategy, 'batch')
|
||||||
|
} else {
|
||||||
|
console.log('no minify strategy')
|
||||||
|
}
|
||||||
|
progress('Tauricon Finished')
|
||||||
|
clearInterval(spinnerInterval)
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a set of images according to the subset of options it knows about.
|
||||||
|
*
|
||||||
|
* @param {string} src - image location
|
||||||
|
* @param {string} target - where to drop the images
|
||||||
|
* @param {object} options - js object that defines path and sizes
|
||||||
|
*/
|
||||||
|
build: async function (src, target, options) {
|
||||||
|
await this.validate(src, target) // creates the image object
|
||||||
|
const buildify2 = async function (pvar) {
|
||||||
|
try {
|
||||||
|
const pngImage = image.resize(pvar[1], pvar[1])
|
||||||
|
if (pvar[2]) {
|
||||||
|
const rgb = hexToRgb(options.background_color)
|
||||||
|
pngImage.flatten({
|
||||||
|
background: { r: rgb.r, g: rgb.g, b: rgb.b, alpha: 1 }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pngImage.png()
|
||||||
|
await pngImage.toFile(pvar[0])
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let output
|
||||||
|
const folders = uniqueFolders(options)
|
||||||
|
for (const n in folders) {
|
||||||
|
// make the folders first
|
||||||
|
ensureDir(`${target}${path.sep}${folders[n]}`)
|
||||||
|
}
|
||||||
|
for (const optionKey in options) {
|
||||||
|
const option = options[optionKey]
|
||||||
|
// chain up the transforms
|
||||||
|
for (const sizeKey in option.sizes) {
|
||||||
|
const size = option.sizes[sizeKey]
|
||||||
|
if (!option.splash) {
|
||||||
|
const dest = `${target}/${option.folder}`
|
||||||
|
if (option.infix === true) {
|
||||||
|
output = `${dest}${path.sep}${option.prefix}${size}x${size}${option.suffix}`
|
||||||
|
} else {
|
||||||
|
output = `${dest}${path.sep}${option.prefix}${option.suffix}`
|
||||||
|
}
|
||||||
|
const pvar = [output, size, option.background]
|
||||||
|
await buildify2(pvar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Creates a set of splash images (COMING SOON!!!)
|
||||||
|
*
|
||||||
|
* @param {string} src - icon location
|
||||||
|
* @param {string} splashSrc - splashscreen location
|
||||||
|
* @param {string} target - where to drop the images
|
||||||
|
* @param {object} options - js object that defines path and sizes
|
||||||
|
*/
|
||||||
|
splash: async function (src, splashSrc, target, options) {
|
||||||
|
let output
|
||||||
|
let block = false
|
||||||
|
const rgb = hexToRgb(options.background_color)
|
||||||
|
|
||||||
|
// three options
|
||||||
|
// options: splashscreen_type [generate | overlay | pure]
|
||||||
|
// - generate (icon + background color) DEFAULT
|
||||||
|
// - overlay (icon + splashscreen)
|
||||||
|
// - pure (only splashscreen)
|
||||||
|
|
||||||
|
let sharpSrc
|
||||||
|
if (splashSrc === src) {
|
||||||
|
// prevent overlay or pure
|
||||||
|
block = true
|
||||||
|
}
|
||||||
|
if (block === true || options.splashscreen_type === 'generate') {
|
||||||
|
await this.validate(src, target)
|
||||||
|
if (!image) {
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
sharpSrc = sharp(src)
|
||||||
|
sharpSrc.extend({
|
||||||
|
top: 726,
|
||||||
|
bottom: 726,
|
||||||
|
left: 726,
|
||||||
|
right: 726,
|
||||||
|
background: {
|
||||||
|
r: rgb.r,
|
||||||
|
g: rgb.g,
|
||||||
|
b: rgb.b,
|
||||||
|
alpha: 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten({ background: { r: rgb.r, g: rgb.g, b: rgb.b, alpha: 1 } })
|
||||||
|
} else if (options.splashscreen_type === 'overlay') {
|
||||||
|
sharpSrc = sharp(splashSrc)
|
||||||
|
.flatten({ background: { r: rgb.r, g: rgb.g, b: rgb.b, alpha: 1 } })
|
||||||
|
.composite([{
|
||||||
|
input: src
|
||||||
|
// blend: 'multiply' <= future work, maybe just a gag
|
||||||
|
}])
|
||||||
|
} else if (options.splashscreen_type === 'pure') {
|
||||||
|
sharpSrc = sharp(splashSrc)
|
||||||
|
.flatten({ background: { r: rgb.r, g: rgb.g, b: rgb.b, alpha: 1 } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await sharpSrc.toBuffer()
|
||||||
|
|
||||||
|
for (const optionKey in options) {
|
||||||
|
const option = options[optionKey]
|
||||||
|
for (const sizeKey in option.sizes) {
|
||||||
|
const size = option.sizes[sizeKey]
|
||||||
|
if (option.splash) {
|
||||||
|
const dest = `${target}${path.sep}${option.folder}`
|
||||||
|
await ensureDir(dest)
|
||||||
|
|
||||||
|
if (option.infix === true) {
|
||||||
|
output = `${dest}${path.sep}${option.prefix}${size}x${size}${option.suffix}`
|
||||||
|
} else {
|
||||||
|
output = `${dest}${path.sep}${option.prefix}${option.suffix}`
|
||||||
|
}
|
||||||
|
// console.log('p1', output, size)
|
||||||
|
const pvar = [output, size]
|
||||||
|
let sharpData = sharp(data)
|
||||||
|
sharpData = sharpData.resize(pvar[1][0], pvar[1][1])
|
||||||
|
await sharpData.toFile(pvar[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minifies a set of images
|
||||||
|
*
|
||||||
|
* @param {string} target - image location
|
||||||
|
* @param {object} options - where to drop the images
|
||||||
|
* @param {string} strategy - which minify strategy to use
|
||||||
|
* @param {string} mode - singlefile or batch
|
||||||
|
*/
|
||||||
|
minify: async function (target, options, strategy, mode) {
|
||||||
|
let cmd
|
||||||
|
const minify = settings.options.minify
|
||||||
|
if (!minify.available.find(x => x === strategy)) {
|
||||||
|
strategy = minify.type
|
||||||
|
}
|
||||||
|
switch (strategy) {
|
||||||
|
case 'pngquant':
|
||||||
|
cmd = pngquant(minify.pngquantOptions)
|
||||||
|
break
|
||||||
|
case 'optipng':
|
||||||
|
cmd = optipng(minify.optipngOptions)
|
||||||
|
break
|
||||||
|
case 'zopfli':
|
||||||
|
cmd = zopfli(minify.zopfliOptions)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const __minifier = async (pvar) => {
|
||||||
|
await imagemin([pvar[0]], {
|
||||||
|
destination: pvar[1],
|
||||||
|
plugins: [cmd]
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
switch (mode) {
|
||||||
|
case 'singlefile':
|
||||||
|
await __minifier([target, path.dirname(target)], cmd)
|
||||||
|
break
|
||||||
|
case 'batch':
|
||||||
|
// eslint-disable-next-line no-case-declarations
|
||||||
|
const folders = uniqueFolders(options)
|
||||||
|
for (const n in folders) {
|
||||||
|
console.log('batch minify:', folders[n])
|
||||||
|
await __minifier([
|
||||||
|
`${target}${path.sep}${folders[n]}${path.sep}*.png`,
|
||||||
|
`${target}${path.sep}${folders[n]}`
|
||||||
|
], cmd)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.error('* [ERROR] Minify mode must be one of [ singlefile | batch]')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
return 'minified'
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates special icns and ico filetypes
|
||||||
|
*
|
||||||
|
* @param {string} src - image location
|
||||||
|
* @param {string} target - where to drop the images
|
||||||
|
* @param {object} options
|
||||||
|
* @param {string} strategy
|
||||||
|
*/
|
||||||
|
icns: async function (src, target, options, strategy) {
|
||||||
|
try {
|
||||||
|
if (!image) {
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
await this.validate(src, target)
|
||||||
|
|
||||||
|
const sharpSrc = sharp(src)
|
||||||
|
const buf = await sharpSrc.toBuffer()
|
||||||
|
|
||||||
|
const out = await png2icons.createICNS(buf, png2icons.BICUBIC, 0)
|
||||||
|
ensureFileSync(path.join(target, '/icon.icns'))
|
||||||
|
writeFileSync(path.join(target, '/icon.icns'), out)
|
||||||
|
|
||||||
|
const out2 = await png2icons.createICO(buf, png2icons.BICUBIC, 0, true)
|
||||||
|
ensureFileSync(path.join(target, '/icon.ico'))
|
||||||
|
writeFileSync(path.join(target, '/icon.ico'), out2)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof exports !== 'undefined') {
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
exports = module.exports = tauricon
|
||||||
|
}
|
||||||
|
exports.tauricon = tauricon
|
||||||
|
}
|
||||||
@@ -36,10 +36,18 @@
|
|||||||
"fast-glob": "^3.0.4",
|
"fast-glob": "^3.0.4",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
"html-webpack-inline-source-plugin": "^0.0.10",
|
"html-webpack-inline-source-plugin": "^0.0.10",
|
||||||
|
"imagemin": "^7.0.1",
|
||||||
|
"imagemin-optipng": "^7.1.0",
|
||||||
|
"imagemin-pngquant": "^8.0.0",
|
||||||
|
"imagemin-zopfli": "^6.0.0",
|
||||||
|
"is-png": "^2.0.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.template": "^4.5.0",
|
"lodash.template": "^4.5.0",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"ms": "^2.1.2",
|
"ms": "^2.1.2",
|
||||||
|
"png2icons": "^2.0.1",
|
||||||
|
"read-chunk": "^3.2.0",
|
||||||
|
"sharp": "^0.23.2",
|
||||||
"webpack-merge": "^4.2.1"
|
"webpack-merge": "^4.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,34 +1,32 @@
|
|||||||
const { tauri } = require('mode/bin/tauri')
|
const { tauri } = require('mode/bin/tauri')
|
||||||
// const mockProcess = require('jest-mock-process')
|
|
||||||
|
|
||||||
|
|
||||||
describe('[CLI] tauri.js', () => {
|
describe('[CLI] tauri.js', () => {
|
||||||
it('displays a help message', async () => {
|
it('displays a help message', async () => {
|
||||||
jest.spyOn(console, 'log')
|
jest.spyOn(console, 'log')
|
||||||
jest.spyOn(process, 'exit').mockImplementation(() => true)
|
jest.spyOn(process, 'exit').mockImplementation(() => true)
|
||||||
let result = tauri('help')
|
tauri('help')
|
||||||
console.log(process.exit.mock.calls[0][0])
|
console.log(process.exit.mock.calls[0][0])
|
||||||
expect(process.exit.mock.calls[0][0]).toBe(0)
|
expect(process.exit.mock.calls[0][0]).toBe(0)
|
||||||
// console.log(console.log.mock.calls[0][0])
|
|
||||||
expect(!!console.log.mock.calls[0][0]).toBe(true)
|
expect(!!console.log.mock.calls[0][0]).toBe(true)
|
||||||
result = tauri('--help')
|
tauri('--help')
|
||||||
// console.log(console.log.mock.calls[2][0])
|
|
||||||
expect(!!console.log.mock.calls[2][0]).toBe(true)
|
expect(!!console.log.mock.calls[2][0]).toBe(true)
|
||||||
result = tauri('-h')
|
tauri('-h')
|
||||||
expect(!!console.log.mock.calls[3][0]).toBe(true)
|
expect(!!console.log.mock.calls[3][0]).toBe(true)
|
||||||
|
tauri(['help'])
|
||||||
|
expect(!!console.log.mock.calls[4][0]).toBe(true)
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('will not run an unavailable command', async () => {
|
it('will not run an unavailable command', async () => {
|
||||||
jest.spyOn(console, 'log')
|
jest.spyOn(console, 'log')
|
||||||
let result = tauri('foo')
|
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()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('will pass on an available command', async () => {
|
it('will pass on an available command', async () => {
|
||||||
jest.spyOn(console, 'log')
|
jest.spyOn(console, 'log')
|
||||||
let result = tauri('init')
|
tauri('init')
|
||||||
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()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|||||||
56
test/jest/__tests__/tauricon.spec.js
Normal file
56
test/jest/__tests__/tauricon.spec.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const { tauricon } = require('mode/helpers/tauricon')
|
||||||
|
const { tauri } = require('mode/bin/tauri')
|
||||||
|
|
||||||
|
describe('[CLI] tauri-icon internals', () => {
|
||||||
|
it('tells you the version', () => {
|
||||||
|
const version = tauricon.version()
|
||||||
|
expect(!!version).toBe(true)
|
||||||
|
})
|
||||||
|
it('gets you help', async () => {
|
||||||
|
jest.spyOn(console, 'log')
|
||||||
|
tauri(['icon', 'help'])
|
||||||
|
expect(!!console.log.mock.calls[0][0]).toBe(true)
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('will not validate a non-file', async () => {
|
||||||
|
try {
|
||||||
|
await tauricon.validate('test/jest/fixtures/doesnotexist.png', 'test/jest/fixtures/')
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe('[ERROR] Source image for tauricon not found')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it('will not validate a non-png', async () => {
|
||||||
|
try {
|
||||||
|
await tauricon.validate('test/jest/fixtures/notAMeme.jpg', 'test/jest/fixtures/')
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe('[ERROR] Source image for tauricon is not a png')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it('can validate an image as PNG', async () => {
|
||||||
|
const valid = await tauricon.validate('test/jest/fixtures/tauri-logo.png', 'test/jest/fixtures/')
|
||||||
|
expect(valid).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test suite takes A LOT of time. Maybe 5 minutes...? You may blame
|
||||||
|
* Zopfli, but don't blame us for trying to help you get the smallest
|
||||||
|
* possible binaries!
|
||||||
|
*/
|
||||||
|
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')
|
||||||
|
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')
|
||||||
|
expect(valid).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('makes a set of icons with zopfli', async () => {
|
||||||
|
const valid = await tauricon.make('test/jest/fixtures/tauri-logo.png', 'test/jest/tmp/zopfli', 'zopfli')
|
||||||
|
expect(valid).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
BIN
test/jest/fixtures/notAMeme.jpg
Normal file
BIN
test/jest/fixtures/notAMeme.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
test/jest/fixtures/tauri-logo.png
Normal file
BIN
test/jest/fixtures/tauri-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
@@ -1,4 +1,4 @@
|
|||||||
jest.setTimeout(1000)
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
global.Promise = require('promise')
|
global.Promise = require('promise')
|
||||||
|
|
||||||
@@ -6,5 +6,4 @@ setTimeout(() => {
|
|||||||
// do nothing
|
// do nothing
|
||||||
}, 1)
|
}, 1)
|
||||||
|
|
||||||
|
|
||||||
require('dotenv').config({ path: '.env.jest' })
|
require('dotenv').config({ path: '.env.jest' })
|
||||||
|
|||||||
Reference in New Issue
Block a user