2021-11-05 17:11:35 +01:00
|
|
|
import path from "path"
|
|
|
|
|
import fs from "fs-extra"
|
2022-05-11 11:43:28 +02:00
|
|
|
import {build as esbuild} from "esbuild"
|
2022-05-10 12:07:23 +02:00
|
|
|
import {getTutanotaAppVersion, runStep, writeFile} from "./buildUtils.js"
|
2022-05-02 17:17:33 +02:00
|
|
|
import {$} from "zx"
|
2022-05-10 10:58:11 +02:00
|
|
|
import "zx/globals"
|
2022-05-02 17:17:33 +02:00
|
|
|
import * as env from "./env.js"
|
2022-05-10 12:07:23 +02:00
|
|
|
import {externalTranslationsPlugin, keytarNativePlugin, libDeps, preludeEnvPlugin, sqliteNativePlugin} from "./esbuildUtils.js"
|
|
|
|
|
import {fileURLToPath} from "url"
|
2022-05-10 16:36:19 +02:00
|
|
|
import * as LaunchHtml from "./LaunchHtml.js"
|
2022-05-10 12:07:23 +02:00
|
|
|
import os from "os"
|
2021-11-05 17:11:35 +01:00
|
|
|
|
2022-05-10 10:58:11 +02:00
|
|
|
export async function runDevBuild({stage, host, desktop, clean}) {
|
2021-11-05 17:11:35 +01:00
|
|
|
if (clean) {
|
2022-05-02 17:17:33 +02:00
|
|
|
await runStep("Clean", async () => {
|
|
|
|
|
await fs.emptyDir("build")
|
|
|
|
|
})
|
2021-11-05 17:11:35 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-02 17:17:33 +02:00
|
|
|
await runStep("Packages", async () => {
|
|
|
|
|
await $`npm run build-runtime-packages`
|
|
|
|
|
})
|
2022-02-14 10:19:20 +01:00
|
|
|
|
2022-05-02 17:17:33 +02:00
|
|
|
const version = getTutanotaAppVersion()
|
2022-02-18 09:12:05 +01:00
|
|
|
|
2022-05-02 17:17:33 +02:00
|
|
|
await runStep("Types", async () => {
|
|
|
|
|
await $`npx tsc --incremental true --noEmit true`
|
|
|
|
|
})
|
2021-11-10 13:35:54 +01:00
|
|
|
|
2021-11-05 17:11:35 +01:00
|
|
|
|
2022-05-02 17:17:33 +02:00
|
|
|
const mode = desktop ? "Desktop" : "Browser"
|
|
|
|
|
await buildWebPart({stage, host, version, mode})
|
2022-01-07 15:58:30 +01:00
|
|
|
|
2022-05-02 17:17:33 +02:00
|
|
|
if (desktop) {
|
|
|
|
|
await buildDesktopPart({version})
|
2021-11-05 17:11:35 +01:00
|
|
|
}
|
2022-05-02 17:17:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function buildWebPart({stage, host, version}) {
|
|
|
|
|
await runStep("Web: Assets", async () => {
|
|
|
|
|
await prepareAssets(stage, host, version)
|
|
|
|
|
await fs.promises.writeFile("build/worker-bootstrap.js", `importScripts("./polyfill.js")
|
2022-05-10 10:58:11 +02:00
|
|
|
importScripts("./worker.js")
|
2022-05-02 17:17:33 +02:00
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await runStep("Web: Esbuild", async () => {
|
2022-05-11 11:43:28 +02:00
|
|
|
await esbuild({
|
2022-05-10 10:58:11 +02:00
|
|
|
// Using named entry points so that it outputs build/worker.js and not build/api/worker/worker.js
|
|
|
|
|
entryPoints: {app: "src/app.ts", worker: "src/api/worker/worker.ts"},
|
2022-05-02 17:17:33 +02:00
|
|
|
outdir: "./build/",
|
2022-05-10 10:58:11 +02:00
|
|
|
// Why bundle at the moment:
|
|
|
|
|
// - We need to include all the imports: everything in src + libs. We could use wildcard in the future.
|
|
|
|
|
// - We can't have imports or dynamic imports in the worker because we can't start it as a module because of Firefox.
|
|
|
|
|
// (see https://bugzilla.mozilla.org/show_bug.cgi?id=1247687)
|
|
|
|
|
// We can theoretically compile it separately but it will be slower and more confusing.
|
2022-05-02 17:17:33 +02:00
|
|
|
bundle: true,
|
|
|
|
|
format: 'esm',
|
2022-05-17 16:05:40 +02:00
|
|
|
// "both" is the most reliable as in Worker or on Android linked source maps don't work
|
|
|
|
|
sourcemap: "both",
|
2022-05-02 17:17:33 +02:00
|
|
|
define: {
|
|
|
|
|
// See Env.ts for explanation
|
|
|
|
|
"NO_THREAD_ASSERTIONS": 'true',
|
|
|
|
|
},
|
|
|
|
|
plugins: [
|
|
|
|
|
libDeps(),
|
2022-05-10 10:58:11 +02:00
|
|
|
externalTranslationsPlugin(),
|
2022-05-02 17:17:33 +02:00
|
|
|
],
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function buildDesktopPart({version}) {
|
|
|
|
|
await runStep("Desktop: Esbuild", async () => {
|
2022-05-11 11:43:28 +02:00
|
|
|
await esbuild({
|
2022-05-02 17:17:33 +02:00
|
|
|
entryPoints: ["src/desktop/DesktopMain.ts"],
|
|
|
|
|
outdir: "./build/desktop",
|
2022-05-10 10:58:11 +02:00
|
|
|
// Why we bundle at the moment:
|
|
|
|
|
// - We need to include all the imports: we currently use some node_modules directly, without pre-bundling them like rest of libs we can't avoid it
|
2022-05-02 17:17:33 +02:00
|
|
|
bundle: true,
|
|
|
|
|
format: 'cjs',
|
|
|
|
|
sourcemap: "linked",
|
|
|
|
|
platform: "node",
|
|
|
|
|
external: ["electron"],
|
|
|
|
|
plugins: [
|
|
|
|
|
libDeps(),
|
|
|
|
|
sqliteNativePlugin({
|
|
|
|
|
environment: "electron",
|
|
|
|
|
dstPath: "./build/desktop/better_sqlite3.node",
|
|
|
|
|
platform: process.platform,
|
|
|
|
|
nativeBindingPath: "./better_sqlite3.node",
|
|
|
|
|
}),
|
|
|
|
|
keytarNativePlugin({
|
|
|
|
|
environment: "electron",
|
|
|
|
|
dstPath: "./build/desktop/keytar.node",
|
|
|
|
|
nativeBindingPath: './keytar.node',
|
|
|
|
|
platform: process.platform,
|
|
|
|
|
}),
|
2022-05-10 10:58:11 +02:00
|
|
|
preludeEnvPlugin(env.create({staticUrl: null, version, mode: "Desktop", dist: false})),
|
|
|
|
|
externalTranslationsPlugin(),
|
2022-05-02 17:17:33 +02:00
|
|
|
],
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-11-05 17:11:35 +01:00
|
|
|
|
2022-05-02 17:17:33 +02:00
|
|
|
await runStep("Desktop: assets", async () => {
|
|
|
|
|
const desktopIconsPath = "./resources/desktop-icons"
|
|
|
|
|
await fs.copy(desktopIconsPath, "./build/desktop/resources/icons", {overwrite: true})
|
2022-05-10 16:36:19 +02:00
|
|
|
const templateGenerator = (await import('./electron-package-json-template.js')).default
|
|
|
|
|
const packageJSON = await templateGenerator({
|
2022-05-02 17:17:33 +02:00
|
|
|
nameSuffix: "-debug",
|
|
|
|
|
version,
|
|
|
|
|
updateUrl: "http://localhost:9000/client/build",
|
|
|
|
|
iconPath: path.join(desktopIconsPath, "logo-solo-red.png"),
|
|
|
|
|
sign: false
|
|
|
|
|
})
|
|
|
|
|
const content = JSON.stringify(packageJSON, null, 2)
|
|
|
|
|
|
|
|
|
|
await fs.createFile("./build/package.json")
|
|
|
|
|
await fs.writeFile("./build/package.json", content, 'utf-8')
|
|
|
|
|
|
|
|
|
|
await fs.mkdir("build/desktop", {recursive: true})
|
|
|
|
|
await fs.copyFile("src/desktop/preload.js", "build/desktop/preload.js")
|
|
|
|
|
await fs.copyFile("src/desktop/preload-webdialog.js", "build/desktop/preload-webdialog.js")
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 12:07:23 +02:00
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
|
|
|
const root = __dirname.split(path.sep).slice(0, -1).join(path.sep)
|
|
|
|
|
|
|
|
|
|
async function createBootstrap(env) {
|
|
|
|
|
let jsFileName
|
|
|
|
|
let htmlFileName
|
|
|
|
|
switch (env.mode) {
|
|
|
|
|
case "App":
|
|
|
|
|
jsFileName = "index-app.js"
|
|
|
|
|
htmlFileName = "index-app.html"
|
|
|
|
|
break
|
|
|
|
|
case "Browser":
|
|
|
|
|
jsFileName = "index.js"
|
|
|
|
|
htmlFileName = "index.html"
|
|
|
|
|
break
|
|
|
|
|
case "Desktop":
|
|
|
|
|
jsFileName = "index-desktop.js"
|
|
|
|
|
htmlFileName = "index-desktop.html"
|
|
|
|
|
}
|
|
|
|
|
const imports = [{src: 'polyfill.js'}, {src: jsFileName}]
|
|
|
|
|
|
|
|
|
|
const template = `window.whitelabelCustomizations = null
|
|
|
|
|
window.env = ${JSON.stringify(env, null, 2)}
|
|
|
|
|
if (env.staticUrl == null && window.tutaoDefaultApiUrl) {
|
|
|
|
|
// overriden by js dev server
|
|
|
|
|
window.env.staticUrl = window.tutaoDefaultApiUrl
|
|
|
|
|
}
|
|
|
|
|
import('./app.js')`
|
|
|
|
|
await writeFile(`./build/${jsFileName}`, template)
|
|
|
|
|
const html = await LaunchHtml.renderHtml(imports, env)
|
|
|
|
|
await writeFile(`./build/${htmlFileName}`, html)
|
2022-05-02 17:17:33 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-10 12:07:23 +02:00
|
|
|
function getStaticUrl(stage, mode, host) {
|
|
|
|
|
if (stage === "local" && mode === "Browser") {
|
|
|
|
|
// We would like to use web app build for both JS server and actual server. For that we should avoid hardcoding URL as server
|
|
|
|
|
// might be running as one of testing HTTPS domains. So instead we override URL when the app is served from JS server
|
|
|
|
|
// (see DevServer).
|
|
|
|
|
// This is only relevant for browser environment.
|
|
|
|
|
return null
|
|
|
|
|
} else if (stage === 'test') {
|
|
|
|
|
return 'https://test.tutanota.com'
|
|
|
|
|
} else if (stage === 'prod') {
|
|
|
|
|
return 'https://mail.tutanota.com'
|
|
|
|
|
} else if (stage === 'local') {
|
|
|
|
|
return "http://" + os.hostname() + ":9000"
|
|
|
|
|
} else { // host
|
|
|
|
|
return host
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
2022-05-10 10:58:11 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-10 12:07:23 +02:00
|
|
|
export async function prepareAssets(stage, host, version) {
|
|
|
|
|
await Promise.all([
|
|
|
|
|
await fs.emptyDir(path.join(root, "build/images")),
|
|
|
|
|
fs.copy(path.join(root, '/resources/favicon'), path.join(root, '/build/images')),
|
|
|
|
|
fs.copy(path.join(root, '/resources/images/'), path.join(root, '/build/images')),
|
|
|
|
|
fs.copy(path.join(root, '/resources/desktop-icons'), path.join(root, '/build/icons')),
|
|
|
|
|
fs.copy(path.join(root, '/src/braintree.html'), path.join(root, 'build/braintree.html'))
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// write empty file
|
|
|
|
|
await fs.writeFile("build/polyfill.js", "")
|
|
|
|
|
|
|
|
|
|
for (const mode of ["Browser", "App", "Desktop"]) {
|
|
|
|
|
await createBootstrap(env.create({staticUrl: getStaticUrl(stage, mode, host), version, mode, dist: false}))
|
2022-05-10 10:58:11 +02:00
|
|
|
}
|
2022-05-09 18:41:10 +02:00
|
|
|
}
|