tutanota/buildSrc/DevBuild.js

205 lines
6.9 KiB
JavaScript
Raw Normal View History

import path from "path"
import fs from "fs-extra"
2022-12-27 15:37:40 +01:00
import { build as esbuild } from "esbuild"
import { getTutanotaAppVersion, runStep, sh, writeFile } from "./buildUtils.js"
import { $ } from "zx"
import "zx/globals"
2022-05-02 17:17:33 +02:00
import * as env from "./env.js"
2022-12-27 15:37:40 +01: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"
2022-12-27 15:37:40 +01:00
import { checkOfflineDatabaseMigrations } from "./checkOfflineDbMigratons.js"
2022-12-27 15:37:40 +01:00
export async function runDevBuild({ stage, host, desktop, clean, ignoreMigrations }) {
if (clean) {
2022-05-02 17:17:33 +02:00
await runStep("Clean", async () => {
await fs.emptyDir("build")
})
}
2022-06-07 09:57:44 +02:00
await runStep("Validate", () => {
if (ignoreMigrations) {
console.warn("CAUTION: Offline migrations are not being validated.")
} else {
checkOfflineDatabaseMigrations()
}
})
2022-05-02 17:17:33 +02:00
await runStep("Packages", async () => {
await $`npm run build-runtime-packages`
})
2022-05-02 17:17:33 +02:00
const version = getTutanotaAppVersion()
2022-05-02 17:17:33 +02:00
await runStep("Types", async () => {
await sh`npx tsc --incremental ${true} --noEmit true`
2022-05-02 17:17:33 +02:00
})
2022-05-02 17:17:33 +02:00
const mode = desktop ? "Desktop" : "Browser"
2022-12-27 15:37:40 +01:00
await buildWebPart({ stage, host, version, mode })
2022-05-02 17:17:33 +02:00
if (desktop) {
2022-12-27 15:37:40 +01:00
await buildDesktopPart({ version })
}
2022-05-02 17:17:33 +02:00
}
2022-12-27 15:37:40 +01:00
async function buildWebPart({ stage, host, version }) {
2022-05-02 17:17:33 +02:00
await runStep("Web: Assets", async () => {
await prepareAssets(stage, host, version)
2022-12-27 15:37:40 +01:00
await fs.promises.writeFile(
"build/worker-bootstrap.js",
`importScripts("./polyfill.js")
importScripts("./worker.js")
2022-12-27 15:37:40 +01:00
`,
)
2022-05-02 17:17:33 +02:00
})
await runStep("Web: Esbuild", async () => {
2022-05-11 11:43:28 +02:00
await esbuild({
// Using named entry points so that it outputs build/worker.js and not build/api/worker/worker.js
2022-12-27 15:37:40 +01:00
entryPoints: { app: "src/app.ts", worker: "src/api/worker/worker.ts" },
2022-05-02 17:17:33 +02:00
outdir: "./build/",
// 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,
2022-12-27 15:37:40 +01:00
format: "esm",
// "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
2022-12-27 15:37:40 +01:00
NO_THREAD_ASSERTIONS: "true",
2022-05-02 17:17:33 +02:00
},
2022-12-27 15:37:40 +01:00
plugins: [libDeps(), externalTranslationsPlugin()],
2022-05-02 17:17:33 +02:00
})
})
}
2022-12-27 15:37:40 +01:00
async function buildDesktopPart({ version }) {
2022-05-02 17:17:33 +02:00
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",
// 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,
2022-12-27 15:37:40 +01:00
format: "cjs",
2022-05-02 17:17:33 +02:00
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",
2022-12-27 15:37:40 +01:00
nativeBindingPath: "./keytar.node",
2022-05-02 17:17:33 +02:00
platform: process.platform,
}),
2022-12-27 15:37:40 +01:00
preludeEnvPlugin(env.create({ staticUrl: null, version, mode: "Desktop", dist: false })),
externalTranslationsPlugin(),
2022-05-02 17:17:33 +02:00
],
})
})
2022-05-02 17:17:33 +02:00
await runStep("Desktop: assets", async () => {
const desktopIconsPath = "./resources/desktop-icons"
2022-12-27 15:37:40 +01:00
await fs.copy(desktopIconsPath, "./build/desktop/resources/icons", { overwrite: true })
const templateGenerator = (await import("./electron-package-json-template.js")).default
2022-05-10 16:36:19 +02:00
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"),
2022-12-27 15:37:40 +01:00
sign: false,
linux: process.platform === "linux",
2022-05-02 17:17:33 +02:00
})
const content = JSON.stringify(packageJSON, null, 2)
await fs.createFile("./build/package.json")
2022-12-27 15:37:40 +01:00
await fs.writeFile("./build/package.json", content, "utf-8")
2022-05-02 17:17:33 +02:00
2022-12-27 15:37:40 +01:00
await fs.mkdir("build/desktop", { recursive: true })
2022-05-02 17:17:33 +02:00
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"
}
2022-12-27 15:37:40 +01:00
const imports = [{ src: "polyfill.js" }, { src: jsFileName }]
2022-05-10 12:07:23 +02:00
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
2022-12-27 15:37:40 +01:00
} else if (stage === "test") {
return "https://test.tutanota.com"
} else if (stage === "prod") {
return "https://mail.tutanota.com"
} else if (stage === "local") {
2022-05-10 12:07:23 +02:00
return "http://" + os.hostname() + ":9000"
2022-12-27 15:37:40 +01:00
} else {
// host
2022-05-10 12:07:23 +02:00
return host
}
}
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")),
2022-12-27 15:37:40 +01:00
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, "/resources/wordlibrary.json"), path.join(root, "build/wordlibrary.json")),
fs.copy(path.join(root, "/src/braintree.html"), path.join(root, "build/braintree.html")),
2022-05-10 12:07:23 +02:00
])
// write empty file
await fs.writeFile("build/polyfill.js", "")
for (const mode of ["Browser", "App", "Desktop"]) {
2022-12-27 15:37:40 +01:00
await createBootstrap(env.create({ staticUrl: getStaticUrl(stage, mode, host), version, mode, dist: false }))
}
2022-12-27 15:37:40 +01:00
}