Sfoglia il codice sorgente

refactor(cli.js): package as ES module, closes #2256 (#2392)

Lucas Fernandes Nogueira 4 anni fa
parent
commit
5f6e135f23

+ 5 - 0
.changes/cli.js-es-module.md

@@ -0,0 +1,5 @@
+---
+"cli.js": patch
+---
+
+The CLI is now a ES module and requires at least Node.js v12.20.

+ 7 - 1
core/tauri-runtime-wry/src/menu.rs

@@ -87,9 +87,15 @@ impl TrayHandle for SystemTrayHandle {
 }
 
 #[cfg(target_os = "macos")]
-#[derive(Debug)]
 pub struct NativeImageWrapper(pub WryNativeImage);
 
+#[cfg(target_os = "macos")]
+impl std::fmt::Debug for NativeImageWrapper {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    f.debug_struct("NativeImageWrapper").finish()
+  }
+}
+
 #[cfg(target_os = "macos")]
 impl From<NativeImage> for NativeImageWrapper {
   fn from(image: NativeImage) -> NativeImageWrapper {

+ 0 - 0
tooling/cli.js/.eslintrc.js → tooling/cli.js/.eslintrc.cjs


+ 0 - 0
tooling/cli.js/.prettierrc.js → tooling/cli.js/.prettierrc.cjs


+ 1 - 1
tooling/cli.js/babel.config.js → tooling/cli.js/babel.config.cjs

@@ -6,7 +6,7 @@ module.exports = {
         targets: {
           node: 'current'
         },
-        modules: 'commonjs'
+        modules: false
       }
     ],
     '@babel/preset-typescript'

+ 5 - 5
tooling/cli.js/bin/tauri-deps.js

@@ -2,12 +2,12 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-async function run() {
-  const {
-    installDependencies,
-    updateDependencies
-  } = require('../dist/api/dependency-manager')
+import {
+  installDependencies,
+  updateDependencies
+} from '../dist/api/dependency-manager.js'
 
+async function run() {
   const choice = process.argv[2]
   if (choice === 'install') {
     await installDependencies()

+ 2 - 2
tooling/cli.js/bin/tauri-icon.js

@@ -2,8 +2,8 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-const parseArgs = require('minimist')
-const { tauricon } = require('../dist/api/tauricon')
+import parseArgs from 'minimist'
+import tauricon from '../dist/api/tauricon.js'
 
 /**
  * @type {object}

+ 10 - 11
tooling/cli.js/bin/tauri.js

@@ -3,9 +3,11 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-const chalk = require('chalk')
+import chalk from 'chalk'
+import updateNotifier from 'update-notifier'
+import { createRequire } from 'module'
+const require = createRequire(import.meta.url)
 const pkg = require('../package.json')
-const updateNotifier = require('update-notifier')
 
 const cmds = ['icon', 'deps']
 const rustCliCmds = ['dev', 'build', 'init', 'info', 'sign']
@@ -66,9 +68,9 @@ ${chalk.yellow('Options')}
       process.argv.splice(2, 1)
     }
     console.log(`[tauri]: running ${command}`)
-    require(`./tauri-${command}`)
+    await import(`./tauri-${command}.js`)
   } else {
-    const { runOnRustCli } = require('../dist/helpers/rust-cli')
+    const { runOnRustCli } = await import('../dist/helpers/rust-cli.js')
     if (process.argv && process.env.NODE_ENV !== 'test') {
       process.argv.splice(0, 3)
     }
@@ -80,19 +82,16 @@ ${chalk.yellow('Options')}
     ).promise
       .then(() => {
         if (command === 'init' && !process.argv.some((arg) => arg === '--ci')) {
-          const {
-            installDependencies
-          } = require('../dist/api/dependency-manager')
-          return installDependencies()
+          return import('../dist/api/dependency-manager.js').then(
+            ({ installDependencies }) => installDependencies()
+          )
         }
       })
       .catch(() => process.exit(1))
   }
 }
 
-module.exports = {
-  tauri
-}
+export default tauri
 
 // on test we use the module.exports
 if (process.env.NODE_ENV !== 'test') {

+ 22 - 19
tooling/cli.js/jest.config.js

@@ -1,4 +1,4 @@
-module.exports = {
+export default {
   globals: {
     __DEV__: true
   },
@@ -8,22 +8,24 @@ module.exports = {
   // cache: false,
   // verbose: true,
   // watch: true,
-  collectCoverage: true,
-  coverageDirectory: '<rootDir>/test/jest/coverage',
-  collectCoverageFrom: [
-    '<rootDir>/bin/**/*.js',
-    '<rootDir>/helpers/**/*.js',
-    '<rootDir>/api/**/*.js'
-  ],
-  coverageReporters: ['json-summary', 'text', 'lcov'],
-  coverageThreshold: {
-    global: {
-      //  branches: 50,
-      //  functions: 50,
-      //  lines: 50,
-      //  statements: 50
-    }
-  },
+
+  // TODO: coverage does not work with esm
+  // collectCoverage: true,
+  // coverageDirectory: '<rootDir>/test/jest/coverage',
+  // collectCoverageFrom: [
+  //   '<rootDir>/bin/**/*.js',
+  //   '<rootDir>/helpers/**/*.js',
+  //   '<rootDir>/api/**/*.js'
+  // ],
+  // coverageReporters: ['json-summary', 'text', 'lcov'],
+  // coverageThreshold: {
+  //   global: {
+  //  branches: 50,
+  //  functions: 50,
+  //  lines: 50,
+  //  statements: 50
+  //   }
+  // },
   testMatch: [
     '<rootDir>/test/jest/__tests__/**/*.spec.js',
     '<rootDir>/test/jest/__tests__/**/*.test.js'
@@ -37,11 +39,12 @@ module.exports = {
     '^api/(.*)$': '<rootDir>/src/api/$1',
     '^templates/(.*)$': '<rootDir>/src/templates/$1',
     '^test/(.*)$': '<rootDir>/test/$1',
-    '../../package.json': '<rootDir>/package.json'
+    '../../package.json': '<rootDir>/package.json',
+    'node:(.*)$': '$1'
   },
   transform: {
     '\\.toml$': 'jest-transform-toml',
     '\\.(js|ts)$': 'babel-jest'
   },
-  transformIgnorePatterns: ['node_modules/(?!(is-png|imagemin|p-pipe)/)']
+  extensionsToTreatAsEsm: ['.ts']
 }

+ 5 - 4
tooling/cli.js/package.json

@@ -2,6 +2,7 @@
   "name": "@tauri-apps/cli",
   "version": "1.0.0-beta.6",
   "description": "Command line interface for building Tauri apps",
+  "type": "module",
   "bin": {
     "tauri": "./bin/tauri.js"
   },
@@ -17,10 +18,10 @@
   "scripts": {
     "build": "rimraf ./dist && webpack --progress",
     "build-release": "rimraf ./dist && cross-env NODE_ENV=production webpack",
-    "test": "jest --runInBand --no-cache --testPathIgnorePatterns=\"(build|dev)\"",
+    "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --runInBand --forceExit --no-cache --testPathIgnorePatterns=\"(build|dev)\"",
     "pretest": "yarn build",
     "prepublishOnly": "yarn build-release",
-    "test:local": "jest --runInBand",
+    "test:local": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --runInBand --forceExit",
     "lint": "eslint --ext ts \"./src/**/*.ts\"",
     "lint-fix": "eslint --fix --ext ts \"./src/**/*.ts\"",
     "lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts npm yarn",
@@ -43,7 +44,7 @@
     "access": "public"
   },
   "engines": {
-    "node": ">= 12.13.0",
+    "node": ">= 12.20.0",
     "npm": ">= 6.6.0",
     "yarn": ">= 1.19.1"
   },
@@ -72,6 +73,7 @@
     "@babel/core": "7.15.0",
     "@babel/preset-env": "7.15.0",
     "@babel/preset-typescript": "7.15.0",
+    "@jest/globals": "27.0.6",
     "@types/cross-spawn": "6.0.2",
     "@types/fs-extra": "9.0.12",
     "@types/global-agent": "2.1.1",
@@ -98,7 +100,6 @@
     "lockfile-lint": "4.6.2",
     "prettier": "2.3.2",
     "promise": "8.1.0",
-    "raw-loader": "4.0.2",
     "rimraf": "3.0.2",
     "toml-loader": "1.0.0",
     "ts-loader": "9.2.5",

+ 7 - 2
tooling/cli.js/src/api/dependency-manager/cargo-crates.ts

@@ -13,9 +13,12 @@ import { getCrateLatestVersion, semverLt } from './util'
 import logger from '../../helpers/logger'
 import { resolve as appResolve, tauriDir } from '../../helpers/app-paths'
 import { readFileSync, writeFileSync, existsSync } from 'fs'
-import toml from '@tauri-apps/toml'
 import inquirer from 'inquirer'
+import { createRequire } from 'module'
 
+const require = createRequire(import.meta.url)
+// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
+const toml = require('@tauri-apps/toml')
 const log = logger('dependency:crates')
 
 const dependencies = ['tauri']
@@ -23,7 +26,8 @@ const dependencies = ['tauri']
 function readToml<T>(tomlPath: string): T | null {
   if (existsSync(tomlPath)) {
     const manifest = readFileSync(tomlPath).toString()
-    return toml.parse(manifest) as any as T
+    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
+    return toml.parse(manifest) as T
   }
   return null
 }
@@ -101,6 +105,7 @@ async function manageDependencies(
   if (installedDeps.length || updatedDeps.length) {
     writeFileSync(
       appResolve.tauri('Cargo.toml'),
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
       toml.stringify(manifest as any)
     )
   }

+ 12 - 13
tooling/cli.js/src/api/dependency-manager/index.ts

@@ -9,17 +9,16 @@ import * as npmPackages from './npm-packages'
 
 const log = logger('dependency:manager')
 
-module.exports = {
-  async installDependencies() {
-    log('Installing missing dependencies...')
-    await rust.install()
-    await cargoCrates.install()
-    await npmPackages.install()
-  },
-  async updateDependencies() {
-    log('Updating dependencies...')
-    await rust.update()
-    await cargoCrates.update()
-    await npmPackages.update()
-  }
+export async function installDependencies(): Promise<void> {
+  log('Installing missing dependencies...')
+  await rust.install()
+  await cargoCrates.install()
+  await npmPackages.install()
+}
+
+export async function updateDependencies(): Promise<void> {
+  log('Updating dependencies...')
+  await rust.update()
+  await cargoCrates.update()
+  await npmPackages.update()
 }

+ 4 - 2
tooling/cli.js/src/api/dependency-manager/rust.ts

@@ -8,10 +8,12 @@ import getScriptVersion from '../../helpers/get-script-version'
 import { downloadRustup } from '../../helpers/download-binary'
 import logger from '../../helpers/logger'
 import { createWriteStream, unlinkSync, existsSync } from 'fs'
-import { resolve } from 'path'
+import { dirname, resolve } from 'path'
 import { platform } from 'os'
 import https from 'https'
+import { fileURLToPath } from 'url'
 
+const currentDirName = dirname(fileURLToPath(import.meta.url))
 const log = logger('dependency:rust')
 
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -36,7 +38,7 @@ async function download(url: string, dest: string): Promise<void> {
 async function installRustup(): Promise<void> {
   const assetName =
     platform() === 'win32' ? 'rustup-init.exe' : 'rustup-init.sh'
-  const rustupPath = resolve(__dirname, `../../bin/${assetName}`)
+  const rustupPath = resolve(currentDirName, `../../bin/${assetName}`)
   if (!existsSync(rustupPath)) {
     await downloadRustup()
   }

+ 12 - 11
tooling/cli.js/src/api/tauricon.ts

@@ -18,7 +18,7 @@
  * @license MIT
  */
 
-import { access, ensureDir, ensureFileSync, writeFileSync } from 'fs-extra'
+import * as fsExtra from 'fs-extra'
 import imagemin, { Plugin } from 'imagemin'
 import optipng from 'imagemin-optipng'
 import zopfli from 'imagemin-zopfli'
@@ -31,7 +31,14 @@ import { appDir, tauriDir } from '../helpers/app-paths'
 import logger from '../helpers/logger'
 import * as settings from '../helpers/tauricon.config'
 import chalk from 'chalk'
-import { version } from '../../package.json'
+import { createRequire } from 'module'
+
+// @ts-expect-error
+const { access, ensureDir, ensureFileSync, writeFileSync } = fsExtra.default
+
+const require = createRequire(import.meta.url)
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const { version } = require('../../package.json')
 
 const log = logger('app:spawn')
 const warn = logger('app:spawn', chalk.red)
@@ -200,7 +207,7 @@ const spinner = (): NodeJS.Timeout | null => {
   }, 500)
 }
 
-const tauricon = (exports.tauricon = {
+const tauricon = {
   validate: async function (src: string, target: string) {
     await validate(src, target)
     return typeof image === 'object'
@@ -512,12 +519,6 @@ const tauricon = (exports.tauricon = {
       throw err
     }
   }
-})
-/* eslint-enable @typescript-eslint/restrict-template-expressions */
-
-if (typeof exports !== 'undefined') {
-  if (typeof module !== 'undefined' && module.exports) {
-    exports = module.exports = tauricon
-  }
-  exports.tauricon = tauricon
 }
+
+export default tauricon

+ 2 - 4
tooling/cli.js/src/helpers/app-paths.ts

@@ -14,7 +14,7 @@ function resolvePath(basePath: string, dir: string): string {
 }
 
 const getAppDir = (): string => {
-  let dir = process.cwd()
+  let dir = process.env.__TAURI_TEST_APP_DIR ?? process.cwd()
   let count = 0
 
   // only go up three folders max
@@ -26,9 +26,7 @@ const getAppDir = (): string => {
     dir = normalize(join(dir, '..'))
   }
 
-  warn(
-    "Couldn't find recognize the current folder as a part of a Tauri project"
-  )
+  warn("Couldn't recognize the current folder as a part of a Tauri project")
   process.exit(1)
 }
 

+ 26 - 14
tooling/cli.js/src/helpers/download-binary.ts

@@ -1,15 +1,25 @@
-import stream from 'stream'
 import { promisify } from 'util'
+import stream from 'stream'
 import fs from 'fs'
-import got from 'got'
 import { CargoManifest } from '../types/cargo'
 import path from 'path'
 import { bootstrap } from 'global-agent'
-const pipeline = promisify(stream.pipeline)
+import { fileURLToPath } from 'url'
+import { createRequire } from 'module'
 
 // Webpack reads the file at build-time, so this becomes a static var
-// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
-const tauriCliManifest = require('../../../cli.rs/Cargo.toml') as CargoManifest
+// @ts-expect-error
+import manifest from '../../../cli.rs/Cargo.toml'
+const tauriCliManifest = manifest as CargoManifest
+const currentDirName = path.dirname(fileURLToPath(import.meta.url))
+
+const require = createRequire(import.meta.url)
+/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires */
+const got = require('got')
+/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires */
+
+// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+const pipeline = promisify(stream.pipeline)
 
 const downloads: { [url: string]: boolean } = {}
 
@@ -44,13 +54,15 @@ async function downloadBinaryRelease(
 
   // TODO: Check hash of download
   // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, security/detect-non-literal-fs-filename
-  await pipeline(got.stream(url), fs.createWriteStream(outPath)).catch((e) => {
-    try {
-      // eslint-disable-next-line security/detect-non-literal-fs-filename
-      fs.unlinkSync(outPath)
-    } catch {}
-    throw e
-  })
+  await pipeline(got.stream(url), fs.createWriteStream(outPath)).catch(
+    (e: unknown) => {
+      try {
+        // eslint-disable-next-line security/detect-non-literal-fs-filename
+        fs.unlinkSync(outPath)
+      } catch {}
+      throw e
+    }
+  )
   // eslint-disable-next-line security/detect-object-injection
   downloads[url] = true
   // eslint-disable-next-line security/detect-non-literal-fs-filename
@@ -71,7 +83,7 @@ async function downloadCli(): Promise<void> {
     throw Error('Unsupported platform')
   }
   const extension = platform === 'windows' ? '.exe' : ''
-  const outPath = path.join(__dirname, `../../bin/tauri-cli${extension}`)
+  const outPath = path.join(currentDirName, `../../bin/tauri-cli${extension}`)
   console.log('Downloading Rust CLI...')
   await downloadBinaryRelease(
     `tauri-cli-v${version}`,
@@ -87,7 +99,7 @@ async function downloadRustup(): Promise<void> {
   return await downloadBinaryRelease(
     'rustup',
     assetName,
-    path.join(__dirname, `../../bin/${assetName}`)
+    path.join(currentDirName, `../../bin/${assetName}`)
   )
 }
 

+ 11 - 11
tooling/cli.js/src/helpers/rust-cli.ts

@@ -2,25 +2,24 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
+import { CargoManifest } from '../types/cargo'
 import { existsSync } from 'fs'
-import { resolve, join } from 'path'
+import { resolve, join, dirname } from 'path'
 import { spawnSync, spawn } from './spawn'
-import { CargoManifest } from '../types/cargo'
 import { downloadCli } from './download-binary'
+import { fileURLToPath } from 'url'
+// Webpack reads the file at build-time, so this becomes a static var
+// @ts-expect-error
+import manifest from '../../../cli.rs/Cargo.toml'
+const tauriCliManifest = manifest as CargoManifest
 
-const currentTauriCliVersion = (): string => {
-  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
-  const tauriCliManifest =
-    // eslint-disable-next-line @typescript-eslint/no-var-requires
-    require('../../../cli.rs/Cargo.toml') as CargoManifest
-  return tauriCliManifest.package.version
-}
+const currentDirName = dirname(fileURLToPath(import.meta.url))
 
 export async function runOnRustCli(
   command: string,
   args: string[]
 ): Promise<{ pid: number; promise: Promise<void> }> {
-  const targetPath = resolve(__dirname, '../..')
+  const targetPath = resolve(currentDirName, '../..')
   const targetCliPath = join(
     targetPath,
     'bin/tauri-cli' + (process.platform === 'win32' ? '.exe' : '')
@@ -80,7 +79,8 @@ export async function runOnRustCli(
           targetPath,
           'tauri-cli',
           '--version',
-          currentTauriCliVersion()
+          // eslint-disable-next-line
+          tauriCliManifest.package.version
         ],
         process.cwd()
       )

+ 6 - 6
tooling/cli.js/test/jest/__tests__/build.spec.js

@@ -1,13 +1,13 @@
-const path = require('path')
-const fixtureSetup = require('../fixtures/app-test-setup')
+import path from 'path'
+import * as fixtureSetup from '../fixtures/app-test-setup.js'
+import { spawn } from 'helpers/spawn'
+
 const appDir = path.join(fixtureSetup.fixtureDir, 'app')
 const distDir = path.join(appDir, 'dist')
 
-const spawn = require('helpers/spawn').spawn
-
 function runBuildTest(args) {
   fixtureSetup.initJest('app')
-  const { build } = require('dist/api/cli')
+  console.log(2)
   return new Promise(async (resolve, reject) => {
     try {
       let success = false
@@ -20,7 +20,7 @@ function runBuildTest(args) {
         setTimeout(resolve, 2000)
       })
       process.chdir(appDir)
-      console.log(server)
+      const { build } = await import('dist/api/cli')
       const { promise } = await build(args)
       await promise
 

+ 6 - 6
tooling/cli.js/test/jest/__tests__/dev.spec.js

@@ -1,10 +1,11 @@
-const path = require('path')
-const fixtureSetup = require('../fixtures/app-test-setup')
+import path from 'path'
+import isRunning from 'is-running'
+import http from 'http'
+import { statSync, createReadStream } from 'fs'
+import * as fixtureSetup from '../fixtures/app-test-setup.js'
 const distDir = path.resolve(fixtureSetup.fixtureDir, 'app', 'dist')
 
 function startDevServer() {
-  const http = require('http')
-  const { statSync, createReadStream } = require('fs')
   const app = http.createServer((req, res) => {
     if (req.method === 'GET') {
       if (req.url === '/') {
@@ -30,13 +31,12 @@ function startDevServer() {
 
 function runDevTest(tauriConfig) {
   fixtureSetup.initJest('app')
-  const { dev } = require('dist/api/cli')
   return new Promise(async (resolve, reject) => {
     try {
       process.chdir(path.join(fixtureSetup.fixtureDir, 'app'))
+      const { dev } = await import('dist/api/cli')
       const { promise, pid } = await dev({ config: tauriConfig })
 
-      const isRunning = require('is-running')
       let success = false
       const checkIntervalId = setInterval(async () => {
         if (!isRunning(pid) && !success) {

+ 6 - 2
tooling/cli.js/test/jest/__tests__/tauri.spec.js

@@ -1,4 +1,9 @@
-const { tauri } = require('bin/tauri')
+import { jest } from '@jest/globals'
+import tauri from 'bin/tauri'
+import { createRequire } from 'module'
+const require = createRequire(import.meta.url)
+
+const { version } = require('../../../package.json')
 
 describe('[CLI] cli.js', () => {
   it('displays a help message', async () => {
@@ -29,7 +34,6 @@ describe('[CLI] cli.js', () => {
   it('gets you version', async () => {
     jest.spyOn(console, 'log')
     const tests = ['--version', '-v']
-    const version = require('../../../package.json').version
     for (const test of tests) {
       tauri([test])
       expect(console.log.mock.calls[0][0]).toBe(version)

+ 6 - 41
tooling/cli.js/test/jest/__tests__/tauricon.spec.js

@@ -1,54 +1,17 @@
-const appTestSetup = require('../fixtures/app-test-setup')
+import * as appTestSetup from '../fixtures/app-test-setup.js'
 appTestSetup.initJest('app')
 
-const tauricon = require('api/tauricon')
-
 describe('[CLI] tauri-icon internals', () => {
-  it('tells you the version', () => {
+  it('tells you the version', async () => {
+    const tauricon = (await import('api/tauricon')).default
     const version = tauricon.version()
     expect(!!version).toBe(true)
   })
-
-  it('will not validate a non-file', async () => {
-    jest.spyOn(process, 'exit').mockImplementation(() => true)
-    await tauricon.validate(
-      'test/jest/fixtures/doesnotexist.png',
-      'test/jest/fixtures/'
-    )
-    expect(process.exit.mock.calls[0][0]).toBe(1)
-    jest.clearAllMocks()
-  })
-  it('will not validate a non-png', async () => {
-    jest.spyOn(process, 'exit').mockImplementation(() => true)
-    await tauricon.validate(
-      'test/jest/fixtures/notAMeme.jpg',
-      'test/jest/fixtures/'
-    )
-    expect(process.exit.mock.calls[0][0]).toBe(1)
-    jest.clearAllMocks()
-  })
-
-  it('should fail if PNG does not have transparency', async () => {
-    jest.spyOn(process, 'exit').mockImplementation(() => true)
-    await tauricon.validate(
-      'test/jest/fixtures/no-alpha.png',
-      'test/jest/fixtures/'
-    )
-    expect(process.exit.mock.calls[0][0]).toBe(1)
-    jest.clearAllMocks()
-  })
-
-  it('can validate an image as PNG', async () => {
-    const valid = await tauricon.validate(
-      'test/jest/fixtures/tauri-logo.png',
-      'test/jest/fixtures/'
-    )
-    expect(valid).toBe(true)
-  })
 })
 
 describe('[CLI] tauri-icon builder', () => {
   it('will still use default compression if missing compression chosen', async () => {
+    const tauricon = (await import('api/tauricon')).default
     const valid = await tauricon.make(
       'test/jest/fixtures/tauri-logo.png',
       'test/jest/tmp/missing',
@@ -59,6 +22,7 @@ describe('[CLI] tauri-icon builder', () => {
 
   it('will not validate a non-file', async () => {
     try {
+      const tauricon = (await import('api/tauricon')).default
       await tauricon.make(
         'test/jest/fixtures/tauri-foo-not-found.png',
         'test/jest/tmp/optipng',
@@ -70,6 +34,7 @@ describe('[CLI] tauri-icon builder', () => {
   })
 
   it('makes a set of icons with optipng', async () => {
+    const tauricon = (await import('api/tauricon')).default
     const valid = await tauricon.make(
       'test/jest/fixtures/tauri-logo.png',
       'test/jest/tmp/optipng',

+ 9 - 6
tooling/cli.js/test/jest/__tests__/template.spec.js

@@ -1,22 +1,25 @@
-const fixtureSetup = require('../fixtures/app-test-setup')
-const { resolve } = require('path')
-const { writeFileSync, readFileSync } = require('fs')
+import * as fixtureSetup from '../fixtures/app-test-setup.js'
+import { resolve, dirname } from 'path'
+import { writeFileSync, readFileSync } from 'fs'
+import { init, build } from 'dist/api/cli'
+import { fileURLToPath } from 'url'
+
+const currentDirName = dirname(fileURLToPath(import.meta.url))
 
 describe('[CLI] cli.js template', () => {
   it('init a project and builds it', async () => {
     const cwd = process.cwd()
-    const fixturePath = resolve(__dirname, '../fixtures/empty')
+    const fixturePath = resolve(currentDirName, '../fixtures/empty')
     const tauriFixturePath = resolve(fixturePath, 'src-tauri')
 
     fixtureSetup.initJest('empty')
 
     process.chdir(fixturePath)
 
-    const { init, build } = require('dist/api/cli')
     const { promise } = await init({
       directory: process.cwd(),
       force: true,
-      tauriPath: resolve(__dirname, '../../../../..'),
+      tauriPath: resolve(currentDirName, '../../../../..'),
       ci: true
     })
     await promise

+ 11 - 34
tooling/cli.js/test/jest/fixtures/app-test-setup.js

@@ -1,44 +1,21 @@
-const path = require('path')
-const process = require('process')
+import { jest } from '@jest/globals'
+import path from 'path'
+import http from 'http'
+import { fileURLToPath } from 'url'
 
-const mockFixtureDir = path.resolve(__dirname, '../fixtures')
+const currentDirName = path.dirname(fileURLToPath(import.meta.url))
+const mockFixtureDir = path.resolve(currentDirName, '../fixtures')
 
-module.exports.fixtureDir = mockFixtureDir
+export const fixtureDir = mockFixtureDir
 
-function mockResolvePath(basePath, dir) {
-  return dir && path.isAbsolute(dir) ? dir : path.resolve(basePath, dir)
-}
-
-module.exports.initJest = (mockFixture) => {
+export const initJest = (mockFixture) => {
   jest.setTimeout(1200000)
-  jest.mock('helpers/non-webpack-require', () => {
-    return (path) => {
-      const value = require('fs').readFileSync(path).toString()
-      if (path.endsWith('.json')) {
-        return JSON.parse(value)
-      }
-      return value
-    }
-  })
 
-  jest.mock('helpers/app-paths', () => {
-    const path = require('path')
-    const appDir = path.join(mockFixtureDir, mockFixture)
-    const tauriDir = path.join(appDir, 'src-tauri')
-    return {
-      appDir,
-      tauriDir,
-      resolve: {
-        app: (dir) => mockResolvePath(appDir, dir),
-        tauri: (dir) => mockResolvePath(tauriDir, dir)
-      }
-    }
-  })
+  const mockAppDir = path.join(mockFixtureDir, mockFixture)
+  process.env.__TAURI_TEST_APP_DIR = mockAppDir
 }
 
-module.exports.startServer = (onSuccess) => {
-  const http = require('http')
-
+export const startServer = (onSuccess) => {
   const responses = {
     writeFile: null,
     readFile: null,

+ 1 - 1
tooling/cli.js/test/jest/fixtures/app/src-tauri/Cargo.toml

@@ -26,7 +26,7 @@ tauri-build = { path = "../../../../../../../core/tauri-build" }
 serde_json = "1.0.66"
 serde = "1.0"
 serde_derive = "1.0"
-tauri = { path = "../../../../../../../core/tauri", features =["api-all"]}
+tauri = { path = "../../../../../../../core/tauri", features = ["api-all"] }
 
 [features]
 default = [ "custom-protocol" ]

+ 2 - 2
tooling/cli.js/test/jest/jest.setup.js

@@ -1,6 +1,6 @@
-jest.setTimeout(1200000)
+import { jest } from '@jest/globals'
 
-global.Promise = require('promise')
+jest.setTimeout(1200000)
 
 setTimeout(() => {
   // do nothing

+ 1 - 1
tooling/cli.js/tsconfig.json

@@ -2,7 +2,7 @@
   "compilerOptions": {
     "outDir": "./dist/",
     "strict": true,
-    "module": "commonjs",
+    "module": "es2020",
     "target": "es5",
     "allowJs": true,
     "esModuleInterop": true,

+ 8 - 7
tooling/cli.js/webpack.config.js → tooling/cli.js/webpack.config.cjs

@@ -2,6 +2,7 @@ const path = require('path')
 const nodeExternals = require('webpack-node-externals')
 
 module.exports = {
+  target: 'es2020',
   entry: {
     'api/cli': './src/api/cli.ts',
     'api/tauricon': './src/api/tauricon.ts',
@@ -19,10 +20,6 @@ module.exports = {
         use: 'ts-loader',
         exclude: /node_modules/
       },
-      {
-        test: /(templates|api)[\\/].+\.js/,
-        use: 'raw-loader'
-      },
       {
         test: /\.toml?$/,
         use: 'toml-loader'
@@ -34,15 +31,19 @@ module.exports = {
     extensions: ['.ts', '.js']
   },
   output: {
-    library: 'tauri',
-    libraryTarget: 'umd',
+    library: {
+      type: 'module'
+    },
     filename: '[name].js',
     path: path.resolve(__dirname, 'dist'),
     globalObject: 'this'
   },
+  experiments: {
+    outputModule: true
+  },
   externals: [
     nodeExternals({
-      allowlist: ['imagemin', 'is-png', 'p-pipe', 'file-type']
+      importType: 'module'
     })
   ],
   externalsPresets: { node: true }

+ 1 - 28
tooling/cli.js/yarn.lock

@@ -1222,7 +1222,7 @@
     jest-mock "^27.0.6"
     jest-util "^27.0.6"
 
-"@jest/globals@^27.0.6":
+"@jest/globals@27.0.6", "@jest/globals@^27.0.6":
   version "27.0.6"
   resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.6.tgz#48e3903f99a4650673d8657334d13c9caf0e8f82"
   integrity sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==
@@ -2230,11 +2230,6 @@ base64-js@^1.3.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-big.js@^5.2.2:
-  version "5.2.2"
-  resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
-  integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
-
 bin-build@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/bin-build/-/bin-build-3.0.0.tgz#c5780a25a8a9f966d8244217e6c1f5082a143861"
@@ -3138,11 +3133,6 @@ emoji-regex@^8.0.0:
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
   integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
 
-emojis-list@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
-  integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
-
 end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -5273,15 +5263,6 @@ loader-runner@^4.2.0:
   resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
   integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==
 
-loader-utils@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
-  integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
-  dependencies:
-    big.js "^5.2.2"
-    emojis-list "^3.0.0"
-    json5 "^2.1.2"
-
 locate-path@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -6209,14 +6190,6 @@ randombytes@^2.1.0:
   dependencies:
     safe-buffer "^5.1.0"
 
-raw-loader@4.0.2:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6"
-  integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==
-  dependencies:
-    loader-utils "^2.0.0"
-    schema-utils "^3.0.0"
-
 rc@^1.2.7, rc@^1.2.8:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"