mirror of
https://github.com/enso-org/enso.git
synced 2024-10-05 17:17:50 +03:00
Google Analytics events for opening and closing the app, and opening and closing projects (#9779)
- Closes #9778 - Add `open_app`, `close_app`, `open_workflow`, and `close_workflow` events - Miscellaneous fixes for Google Analytics: - Fix `open_chat` and `close_chat` events firing even when chat is not visible - Add Google Analytics script to GUI2 entrypoint (i.e. the entrypoint used by the desktop app) Unrelated changes: - Add Nix development shell to allow Nix users to build GUI2 and the build script - Java dependencies have *not* been added in this PR to keep things simple # Important Notes None
This commit is contained in:
parent
0d495ffd97
commit
3a53d470eb
7
.envrc
Normal file
7
.envrc
Normal file
@ -0,0 +1,7 @@
|
||||
strict_env
|
||||
|
||||
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
|
||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
|
||||
fi
|
||||
|
||||
use flake
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -162,3 +162,9 @@ test-results
|
||||
*.ir
|
||||
*.meta
|
||||
.enso/
|
||||
|
||||
##################
|
||||
## direnv cache ##
|
||||
##################
|
||||
|
||||
.direnv
|
||||
|
@ -8,6 +8,7 @@
|
||||
"./src/appConfig": "./src/appConfig.js",
|
||||
"./src/buildUtils": "./src/buildUtils.js",
|
||||
"./src/detect": "./src/detect.ts",
|
||||
"./src/gtag": "./src/gtag.ts"
|
||||
"./src/gtag": "./src/gtag.ts",
|
||||
"./src/load": "./src/load.ts"
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,12 @@ import * as fs from 'node:fs/promises'
|
||||
import * as path from 'node:path'
|
||||
import * as url from 'node:url'
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
const ENSO_CLOUD_GOOGLE_ANALYTICS_TAG = 'G-CLTBJ37MDM'
|
||||
|
||||
// ===============================
|
||||
// === readEnvironmentFromFile ===
|
||||
// ===============================
|
||||
@ -35,6 +41,10 @@ export async function readEnvironmentFromFile() {
|
||||
}
|
||||
const variables = Object.fromEntries(entries)
|
||||
Object.assign(process.env, variables)
|
||||
if (!('' in process.env)) {
|
||||
// @ts-expect-error This is the only place where this environment variable is set.
|
||||
process.env.ENSO_CLOUD_GOOGLE_ANALYTICS_TAG = ENSO_CLOUD_GOOGLE_ANALYTICS_TAG
|
||||
}
|
||||
} catch (error) {
|
||||
if (isProduction) {
|
||||
console.warn('Could not load `.env` file; disabling cloud backend.')
|
||||
|
@ -17,15 +17,27 @@ export enum Platform {
|
||||
windows = 'Windows',
|
||||
macOS = 'macOS',
|
||||
linux = 'Linux',
|
||||
windowsPhone = 'Windows Phone',
|
||||
iPhoneOS = 'iPhone OS',
|
||||
android = 'Android',
|
||||
}
|
||||
|
||||
/** Return the platform the app is currently running on.
|
||||
/** The platform the app is currently running on.
|
||||
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts. */
|
||||
export function platform(): Platform {
|
||||
if (isOnWindows()) {
|
||||
export function platform() {
|
||||
if (isOnWindowsPhone()) {
|
||||
// MUST be before Android and Windows.
|
||||
return Platform.windowsPhone
|
||||
} else if (isOnWindows()) {
|
||||
return Platform.windows
|
||||
} else if (isOnIPhoneOS()) {
|
||||
// MUST be before macOS.
|
||||
return Platform.iPhoneOS
|
||||
} else if (isOnMacOS()) {
|
||||
return Platform.macOS
|
||||
} else if (isOnAndroid()) {
|
||||
// MUST be before Linux.
|
||||
return Platform.android
|
||||
} else if (isOnLinux()) {
|
||||
return Platform.linux
|
||||
} else {
|
||||
@ -33,22 +45,37 @@ export function platform(): Platform {
|
||||
}
|
||||
}
|
||||
|
||||
/** Return whether the device is running Windows. */
|
||||
/** Whether the device is running Windows. */
|
||||
export function isOnWindows() {
|
||||
return /windows/i.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/** Return whether the device is running macOS. */
|
||||
/** Whether the device is running macOS. */
|
||||
export function isOnMacOS() {
|
||||
return /mac os/i.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/** Return whether the device is running Linux. */
|
||||
/** Whether the device is running Linux. */
|
||||
export function isOnLinux() {
|
||||
return /linux/i.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/** Return whether the device is running an unknown OS. */
|
||||
/** Whether the device is running Windows Phone. */
|
||||
export function isOnWindowsPhone() {
|
||||
return /windows phone/i.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/** Whether the device is running iPhone OS. */
|
||||
export function isOnIPhoneOS() {
|
||||
return /iPhone/i.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/** Whether the device is running Android. */
|
||||
export function isOnAndroid() {
|
||||
return /android/i.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/** Whether the device is running an unknown OS. */
|
||||
export function isOnUnknownOS() {
|
||||
return platform() === Platform.unknown
|
||||
}
|
||||
@ -126,3 +153,73 @@ export function isOnSafari() {
|
||||
export function isOnUnknownBrowser() {
|
||||
return browser() === Browser.unknown
|
||||
}
|
||||
|
||||
// ====================
|
||||
// === Architecture ===
|
||||
// ====================
|
||||
|
||||
let detectedArchitecture: string | null = null
|
||||
// Only implemented by Chromium.
|
||||
// @ts-expect-error This API exists, but no typings exist for it yet.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
navigator.userAgentData
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
?.getHighEntropyValues(['architecture'])
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
.then((values: unknown) => {
|
||||
if (
|
||||
typeof values === 'object' &&
|
||||
values != null &&
|
||||
'architecture' in values &&
|
||||
typeof values.architecture === 'string'
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
detectedArchitecture = String(values.architecture)
|
||||
}
|
||||
})
|
||||
|
||||
/** Possible processor architectures. */
|
||||
export enum Architecture {
|
||||
intel64 = 'x86_64',
|
||||
arm64 = 'arm64',
|
||||
}
|
||||
|
||||
/** The processor architecture of the current system. */
|
||||
export function architecture() {
|
||||
if (detectedArchitecture != null) {
|
||||
switch (detectedArchitecture) {
|
||||
case 'arm': {
|
||||
return Architecture.arm64
|
||||
}
|
||||
default: {
|
||||
return Architecture.intel64
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (platform()) {
|
||||
case Platform.windows:
|
||||
case Platform.linux:
|
||||
case Platform.unknown: {
|
||||
return Architecture.intel64
|
||||
}
|
||||
case Platform.macOS:
|
||||
case Platform.iPhoneOS:
|
||||
case Platform.android:
|
||||
case Platform.windowsPhone: {
|
||||
// Assume the macOS device is on a M-series CPU.
|
||||
// This is highly unreliable, but operates under the assumption that all
|
||||
// new macOS devices will be ARM64.
|
||||
return Architecture.arm64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether the device has an Intel 64-bit CPU. */
|
||||
export function isIntel64() {
|
||||
return architecture() === Architecture.intel64
|
||||
}
|
||||
|
||||
/** Whether the device has an ARM 64-bit CPU. */
|
||||
export function isArm64() {
|
||||
return architecture() === Architecture.arm64
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
/** @file Google Analytics tag. */
|
||||
import * as load from './load'
|
||||
|
||||
if (process.env.ENSO_CLOUD_GOOGLE_ANALYTICS_TAG != null) {
|
||||
void load.loadScript(
|
||||
`https://www.googletagmanager.com/gtag/js?id=${process.env.ENSO_CLOUD_GOOGLE_ANALYTICS_TAG}`
|
||||
)
|
||||
}
|
||||
|
||||
// @ts-expect-error This is explicitly not given types as it is a mistake to acess this
|
||||
// anywhere else.
|
||||
|
32
app/ide-desktop/lib/common/src/load.ts
Normal file
32
app/ide-desktop/lib/common/src/load.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/** @file Utilities for loading resources. */
|
||||
|
||||
/** Add a script to the DOM. */
|
||||
export function loadScript(url: string) {
|
||||
const script = document.createElement('script')
|
||||
script.crossOrigin = 'anonymous'
|
||||
script.src = url
|
||||
document.head.appendChild(script)
|
||||
return new Promise<HTMLScriptElement>((resolve, reject) => {
|
||||
script.onload = () => {
|
||||
resolve(script)
|
||||
}
|
||||
script.onerror = reject
|
||||
})
|
||||
}
|
||||
|
||||
/** Add a CSS stylesheet to the DOM. */
|
||||
export function loadStyle(url: string) {
|
||||
const style = document.createElement('link')
|
||||
style.crossOrigin = 'anonymous'
|
||||
style.href = url
|
||||
style.rel = 'stylesheet'
|
||||
style.media = 'screen'
|
||||
style.type = 'text/css'
|
||||
document.head.appendChild(style)
|
||||
return new Promise<HTMLLinkElement>((resolve, reject) => {
|
||||
style.onload = () => {
|
||||
resolve(style)
|
||||
}
|
||||
style.onerror = reject
|
||||
})
|
||||
}
|
@ -42,10 +42,5 @@
|
||||
<noscript>
|
||||
This page requires JavaScript to run. Please enable it in your browser.
|
||||
</noscript>
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-CLTBJ37MDM"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -43,10 +43,5 @@
|
||||
<noscript>
|
||||
This page requires JavaScript to run. Please enable it in your browser.
|
||||
</noscript>
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-CLTBJ37MDM"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -70,6 +70,9 @@ const MODIFIER_JSX: Readonly<
|
||||
</aria.Text>
|
||||
),
|
||||
},
|
||||
[detect.Platform.iPhoneOS]: {},
|
||||
[detect.Platform.android]: {},
|
||||
[detect.Platform.windowsPhone]: {},
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,9 @@ import * as gtag from 'enso-common/src/gtag'
|
||||
|
||||
import * as authProvider from '#/providers/AuthProvider'
|
||||
|
||||
// ===================
|
||||
// === useGtag ===
|
||||
// ===================
|
||||
// ====================
|
||||
// === useGtagEvent ===
|
||||
// ====================
|
||||
|
||||
/** A hook that returns a no-op if the user is offline, otherwise it returns
|
||||
* a transparent wrapper around `gtag.event`. */
|
||||
@ -22,3 +22,28 @@ export function useGtagEvent() {
|
||||
[sessionType]
|
||||
)
|
||||
}
|
||||
|
||||
// =============================
|
||||
// === gtagOpenCloseCallback ===
|
||||
// =============================
|
||||
|
||||
/** Send an event indicating that something has been opened, and return a cleanup function
|
||||
* sending an event indicating that it has been closed.
|
||||
*
|
||||
* Also sends the close event when the window is unloaded. */
|
||||
export function gtagOpenCloseCallback(
|
||||
gtagEventRef: React.MutableRefObject<ReturnType<typeof useGtagEvent>>,
|
||||
openEvent: string,
|
||||
closeEvent: string
|
||||
) {
|
||||
const gtagEventCurrent = gtagEventRef.current
|
||||
gtagEventCurrent(openEvent)
|
||||
const onBeforeUnload = () => {
|
||||
gtagEventCurrent(closeEvent)
|
||||
}
|
||||
window.addEventListener('beforeunload', onBeforeUnload)
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', onBeforeUnload)
|
||||
gtagEventCurrent(closeEvent)
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +243,6 @@ interface InternalChatHeaderProps {
|
||||
function ChatHeader(props: InternalChatHeaderProps) {
|
||||
const { threads, setThreads, threadId, threadTitle, setThreadTitle } = props
|
||||
const { switchThread, sendMessage, doClose } = props
|
||||
const gtagEvent = gtagHooks.useGtagEvent()
|
||||
const [isThreadListVisible, setIsThreadListVisible] = React.useState(false)
|
||||
// These will never be `null` as their values are set immediately.
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
@ -258,12 +257,10 @@ function ChatHeader(props: InternalChatHeaderProps) {
|
||||
setIsThreadListVisible(false)
|
||||
}
|
||||
document.addEventListener('click', onClick)
|
||||
gtagEvent('cloud_open_chat')
|
||||
return () => {
|
||||
document.removeEventListener('click', onClick)
|
||||
gtagEvent('cloud_close_chat')
|
||||
}
|
||||
}, [gtagEvent])
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -394,6 +391,17 @@ export default function Chat(props: ChatProps) {
|
||||
}
|
||||
},
|
||||
})
|
||||
const gtagEvent = gtagHooks.useGtagEvent()
|
||||
const gtagEventRef = React.useRef(gtagEvent)
|
||||
gtagEventRef.current = gtagEvent
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isOpen) {
|
||||
return
|
||||
} else {
|
||||
return gtagHooks.gtagOpenCloseCallback(gtagEventRef, 'cloud_open_chat', 'cloud_close_chat')
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
/** This is SAFE, because this component is only rendered when `accessToken` is present.
|
||||
* See `dashboard.tsx` for its sole usage. */
|
||||
|
@ -1,14 +1,15 @@
|
||||
/** @file The container that launches the IDE. */
|
||||
import * as React from 'react'
|
||||
|
||||
import * as load from 'enso-common/src/load'
|
||||
|
||||
import * as appUtils from '#/appUtils'
|
||||
|
||||
import * as gtagHooks from '#/hooks/gtagHooks'
|
||||
import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
|
||||
|
||||
import * as backendModule from '#/services/Backend'
|
||||
|
||||
import * as load from '#/utilities/load'
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
@ -39,6 +40,9 @@ export interface EditorProps {
|
||||
export default function Editor(props: EditorProps) {
|
||||
const { hidden, supportsLocalBackend, projectStartupInfo, appRunner } = props
|
||||
const toastAndLog = toastAndLogHooks.useToastAndLog()
|
||||
const gtagEvent = gtagHooks.useGtagEvent()
|
||||
const gtagEventRef = React.useRef(gtagEvent)
|
||||
gtagEventRef.current = gtagEvent
|
||||
const [initialized, setInitialized] = React.useState(supportsLocalBackend)
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -48,6 +52,14 @@ export default function Editor(props: EditorProps) {
|
||||
}
|
||||
}, [hidden])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hidden) {
|
||||
return
|
||||
} else {
|
||||
return gtagHooks.gtagOpenCloseCallback(gtagEventRef, 'open_workflow', 'close_workflow')
|
||||
}
|
||||
}, [projectStartupInfo, hidden])
|
||||
|
||||
let hasEffectRun = false
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -6,6 +6,8 @@ import type * as stripeTypes from '@stripe/stripe-js'
|
||||
import * as stripe from '@stripe/stripe-js/pure'
|
||||
import * as toast from 'react-toastify'
|
||||
|
||||
import * as load from 'enso-common/src/load'
|
||||
|
||||
import * as appUtils from '#/appUtils'
|
||||
import type * as text from '#/text'
|
||||
|
||||
@ -20,7 +22,6 @@ import UnstyledButton from '#/components/UnstyledButton'
|
||||
|
||||
import * as backendModule from '#/services/Backend'
|
||||
|
||||
import * as load from '#/utilities/load'
|
||||
import * as string from '#/utilities/string'
|
||||
|
||||
// =================
|
||||
|
@ -10,10 +10,13 @@ import isNetworkError from 'is-network-error'
|
||||
import * as router from 'react-router-dom'
|
||||
import * as toast from 'react-toastify'
|
||||
|
||||
import * as detect from 'enso-common/src/detect'
|
||||
import * as gtag from 'enso-common/src/gtag'
|
||||
|
||||
import * as appUtils from '#/appUtils'
|
||||
|
||||
import * as gtagHooks from '#/hooks/gtagHooks'
|
||||
|
||||
import * as backendProvider from '#/providers/BackendProvider'
|
||||
import * as localStorageProvider from '#/providers/LocalStorageProvider'
|
||||
import * as loggerProvider from '#/providers/LoggerProvider'
|
||||
@ -238,6 +241,16 @@ export default function AuthProvider(props: AuthProviderProps) {
|
||||
},
|
||||
[userSession?.type]
|
||||
)
|
||||
const gtagEventRef = React.useRef(gtagEvent)
|
||||
gtagEventRef.current = gtagEvent
|
||||
|
||||
React.useEffect(() => {
|
||||
gtag.gtag('set', {
|
||||
platform: detect.platform(),
|
||||
architecture: detect.architecture(),
|
||||
})
|
||||
return gtagHooks.gtagOpenCloseCallback(gtagEventRef, 'open_app', 'close_app')
|
||||
}, [])
|
||||
|
||||
// This is identical to `hooks.useOnlineCheck`, however it is inline here to avoid any possible
|
||||
// circular dependency.
|
||||
|
@ -1,32 +0,0 @@
|
||||
/** @file Utilities for loading resources. */
|
||||
|
||||
/** Add a script to the DOM. */
|
||||
export function loadScript(url: string) {
|
||||
const script = document.createElement('script')
|
||||
script.crossOrigin = 'anonymous'
|
||||
script.src = url
|
||||
document.head.appendChild(script)
|
||||
return new Promise<HTMLScriptElement>((resolve, reject) => {
|
||||
script.onload = () => {
|
||||
resolve(script)
|
||||
}
|
||||
script.onerror = reject
|
||||
})
|
||||
}
|
||||
|
||||
/** Add a CSS stylesheet to the DOM. */
|
||||
export function loadStyle(url: string) {
|
||||
const style = document.createElement('link')
|
||||
style.crossOrigin = 'anonymous'
|
||||
style.href = url
|
||||
style.rel = 'stylesheet'
|
||||
style.media = 'screen'
|
||||
style.type = 'text/css'
|
||||
document.head.appendChild(style)
|
||||
return new Promise<HTMLLinkElement>((resolve, reject) => {
|
||||
style.onload = () => {
|
||||
resolve(style)
|
||||
}
|
||||
style.onerror = reject
|
||||
})
|
||||
}
|
2
app/ide-desktop/lib/types/globals.d.ts
vendored
2
app/ide-desktop/lib/types/globals.d.ts
vendored
@ -140,6 +140,8 @@ declare global {
|
||||
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
|
||||
readonly ENSO_CLOUD_COGNITO_REGION?: string
|
||||
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
|
||||
readonly ENSO_CLOUD_GOOGLE_ANALYTICS_TAG?: string
|
||||
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
|
||||
readonly ENSO_SUPPORTS_VIBRANCY?: string
|
||||
|
||||
// === Electron watch script variables ===
|
||||
|
64
flake.lock
Normal file
64
flake.lock
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"nodes": {
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": ["nixpkgs"],
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1713939967,
|
||||
"narHash": "sha256-3YQSEYvAIHE40tx5nM9dgeEe0gsHjf15+gurUpyDYNw=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "5c3ff469526a6ca54a887fbda9d67aef4dd4a921",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1713805509,
|
||||
"narHash": "sha256-YgSEan4CcrjivCNO5ZNzhg7/8ViLkZ4CB/GrGBVSudo=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1e1dc66fe68972a76679644a5577828b6a7e8be4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1713801366,
|
||||
"narHash": "sha256-VmzP5s59kb6//mj+ES+hslTLuugjd7OluGIXXcwuyHg=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "e31c9f3fe11148514c3ad254b639b2ed7dbe35de",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rust-lang",
|
||||
"ref": "nightly",
|
||||
"repo": "rust-analyzer",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
46
flake.nix
Normal file
46
flake.nix
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable;
|
||||
fenix.url = github:nix-community/fenix;
|
||||
fenix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
outputs = { self, nixpkgs, fenix }:
|
||||
let
|
||||
forAllSystems = with nixpkgs.lib; f: foldAttrs mergeAttrs { }
|
||||
(map (s: { ${s} = f s; }) systems.flakeExposed);
|
||||
in
|
||||
{
|
||||
devShell = forAllSystems
|
||||
(system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
rust = fenix.packages.${system}.fromToolchainFile {
|
||||
dir = ./.;
|
||||
sha256 = "sha256-o/MRwGYjLPyD1zZQe3LX0dOynwRJpVygfF9+vSnqTOc=";
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
# === TypeScript dependencies ===
|
||||
nodejs_20 # should match the Node.JS version of the lambdas
|
||||
corepack
|
||||
# === Electron ===
|
||||
electron
|
||||
# === node-gyp dependencies ===
|
||||
python3
|
||||
gnumake
|
||||
# === WASM parser dependencies ===
|
||||
rust
|
||||
wasm-pack
|
||||
# Java and SBT omitted for now
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
# `sccache` can be used to speed up compile times for Rust crates.
|
||||
# `~/.cargo/bin/sccache` is provided by `cargo install sccache`.
|
||||
# `~/.cargo/bin` must be in the `PATH` for the binary to be accessible.
|
||||
export PATH=$HOME/.cargo/bin:$PATH
|
||||
'';
|
||||
});
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user