Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a13f7cc
Convert nseventforwarder package to ESM
tobias-jarvelov Jan 5, 2026
8fe7c92
Convert windows-utils package to ESM
tobias-jarvelov Jan 5, 2026
03d75e9
Convert mullvad-vpn package to ESM
tobias-jarvelov Jan 5, 2026
a19ad31
Ensure preload file is output with correct file extension
tobias-jarvelov Jan 5, 2026
0a6be85
Replace __dirname with import.meta.dirname
tobias-jarvelov Jan 5, 2026
4aafb50
Change TS moduleResolution to bundler and update TS output to es2022
tobias-jarvelov Sep 10, 2025
f100dab
Rename CJS files to use .cjs extensions
tobias-jarvelov Jan 5, 2026
eacdbfb
Use file extension in imports in CJS files
tobias-jarvelov Jan 5, 2026
2634182
Update package.json to use .cjs for tasks' file extension
tobias-jarvelov Jan 5, 2026
4d2a15d
Include .cjs files in linting
tobias-jarvelov Jan 5, 2026
0216dd8
Update redux to 5.0.1
tobias-jarvelov Jan 5, 2026
a94c2eb
Update @vitejs/plugin-react to 5.0.2
tobias-jarvelov Sep 10, 2025
88068be
Add react/react-dom to be deduped
tobias-jarvelov Jan 5, 2026
8f762c1
Fix type error by passing enhancer as the third argument to createStore
tobias-jarvelov Jan 5, 2026
59cc270
Sort exports
tobias-jarvelov Jan 9, 2026
884853b
Update tsconfig to allow importing .ts extensions
tobias-jarvelov Jan 9, 2026
24cc1f2
Extract allowed HTML tags to separate constants file
tobias-jarvelov Jan 9, 2026
b8214d9
Import allowed HTML tags from translations constants
tobias-jarvelov Jan 9, 2026
a1eff54
Move allowed void tags to translations constants
tobias-jarvelov Jan 9, 2026
6e735d0
Use explicit type import syntax
tobias-jarvelov Jan 9, 2026
3e67725
Use node instead of ts-node when verifying translations
tobias-jarvelov Jan 9, 2026
3d08282
Remove ts-node
tobias-jarvelov Jan 9, 2026
9ab9240
Update package-lock.json
tobias-jarvelov Jan 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion desktop/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default tseslint.config(
},
},
{
files: ['**/*.{js,mjs,ts,tsx}'],
files: ['**/*.{js,cjs,mjs,ts,tsx}'],
plugins: {
'simple-import-sort': simpleImportSort,
},
Expand Down
874 changes: 309 additions & 565 deletions desktop/package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-simple-import-sort": "^12.1.1",
"prettier": "^3.3.3",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.15.0"
},
Expand Down
21 changes: 11 additions & 10 deletions desktop/packages/mullvad-vpn/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"name": "Mullvad VPN",
"email": "[email protected]"
},
"type": "module",
"repository": "https://github.com/mullvad/mullvadvpn-app",
"license": "GPL-3.0",
"dependencies": {
Expand All @@ -25,7 +26,7 @@
"react-dom": "^19.1.1",
"react-redux": "^9.2.0",
"react-router": "^5.3.4",
"redux": "^4.2.0",
"redux": "^5.0.1",
"simple-plist": "^1.3.1",
"sprintf-js": "^1.1.2",
"styled-components": "^6.1.19",
Expand All @@ -42,7 +43,7 @@
"@types/react-router": "^5.1.19",
"@types/sprintf-js": "^1.1.2",
"@types/topojson-specification": "^1.0.2",
"@vitejs/plugin-react": "^4.7.0",
"@vitejs/plugin-react": "^5.0.2",
"cross-env": "^7.0.3",
"electron": "39.2.6",
"electron-builder": "26.6.0",
Expand All @@ -69,10 +70,10 @@
},
"scripts": {
"preinstall": "test -d node_modules || mkdir node_modules",
"build": "node tasks/build-production.js",
"build": "node tasks/build-production.cjs",
"build:vite": "vite build",
"build:test": "node tasks/build-test.js",
"build:standalone": "node tasks/build-standalone.js",
"build:test": "node tasks/build-test.cjs",
"build:standalone": "node tasks/build-standalone.cjs",
"pack-test-executable": "./scripts/build-test-executable.sh",
"build-test-executable": "npm run pack-test-executable",
"lint": "eslint . --max-warnings 0",
Expand All @@ -83,14 +84,14 @@
"e2e:sequential:no-build": "xvfb-maybe -- playwright test -c test/e2e/installed/playwright.config.ts --workers 1",
"e2e:update-snapshots": "npm run e2e:no-build -- --update-snapshots",
"develop": "npm run develop:pre && npm run develop:vite",
"develop:pre": "node tasks/pre-develop.js",
"develop:pre": "node tasks/pre-develop.cjs",
"develop:vite": "vite",
"test": "cross-env NODE_ENV=test vitest test/unit",
"type-check": "tsc --noEmit",
"update-translations": "node scripts/extract-translations",
"pack:mac": "node tasks/pack-mac.js",
"pack:win": "node tasks/pack-windows.js",
"pack:linux": "node tasks/pack-linux.js"
"update-translations": "node scripts/extract-translations.cjs",
"pack:mac": "node tasks/pack-mac.cjs",
"pack:win": "node tasks/pack-windows.cjs",
"pack:linux": "node tasks/pack-linux.cjs"
},
"volta": {
"extends": "../../package.json"
Expand Down
22 changes: 14 additions & 8 deletions desktop/packages/mullvad-vpn/scripts/verify-translations-format.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import fs from 'fs';
import { GetTextTranslation, po } from 'gettext-parser';
import { type GetTextTranslation, po } from 'gettext-parser';
import path from 'path';

import { ALLOWED_TAGS } from '../src/renderer/lib/html-formatter';
import { ValueOfArray } from '../src/shared/utility-types';
import {
type AllowedTag,
type AllowedVoidTag,
translations,
} from '../src/shared/constants/translations.ts';
const { allowedTags, allowedVoidTags } = translations;

const LOCALES_DIR = path.join('locales');

const ALLOWED_VOID_TAGS = ['br'];

// Make sure to report these strings to crowdin. View this as a temporary escape
// hatch, not a permanent solution.
const IGNORED_STRINGS: Set<string> = new Set([
Expand Down Expand Up @@ -71,8 +73,12 @@ function checkFormatSpecifiers(translation: GetTextTranslation): boolean {
.every((result) => result);
}

function isAllowedTag(tag: string): tag is ValueOfArray<typeof ALLOWED_TAGS> {
return ALLOWED_TAGS.some((allowedTag) => tag === allowedTag);
function isAllowedTag(tag: string): tag is AllowedTag {
return allowedTags.some((allowedTag) => tag === allowedTag);
}

function isAllowedVoidTag(tag: string): tag is AllowedVoidTag {
return allowedVoidTags.some((allowedTag) => tag === allowedTag);
}

function checkHtmlTagsImpl(value: string): { correct: boolean; amount: number } {
Expand All @@ -93,7 +99,7 @@ function checkHtmlTagsImpl(value: string): { correct: boolean; amount: number }
return { correct: false, amount: NaN };
}

if (!ALLOWED_VOID_TAGS.includes(tag)) {
if (!isAllowedVoidTag(tag)) {
if (endTag) {
// End tags require a matching start tag.
if (tag !== tagStack.pop()) {
Expand Down
2 changes: 1 addition & 1 deletion desktop/packages/mullvad-vpn/src/main/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import log from '../shared/logging';
// Reads and parses the changelog file.
export function readChangelog(): IChangelog {
try {
const changelogPath = path.join(__dirname, '..', 'changes.txt');
const changelogPath = path.join(import.meta.dirname, '..', 'changes.txt');
const contents = fs.readFileSync(changelogPath).toString();
return parseChangelog(contents);
} catch (e) {
Expand Down
4 changes: 2 additions & 2 deletions desktop/packages/mullvad-vpn/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const ALLOWED_PERMISSIONS = ['clipboard-sanitized-write'];
const SANDBOX_DISABLED = app.commandLine.hasSwitch('no-sandbox');
const UPDATE_NOTIFICATION_DISABLED = process.env.MULLVAD_DISABLE_UPDATE_NOTIFICATION === '1';

const GEO_DIR = path.resolve(__dirname, 'assets/geo');
const GEO_DIR = path.resolve(import.meta.dirname, 'assets/geo');

class ApplicationMain
implements
Expand Down Expand Up @@ -1018,7 +1018,7 @@ class ApplicationMain
}

private allowFileAccess(url: string): boolean {
const buildDir = path.normalize(path.join(path.resolve(__dirname), '..', '..'));
const buildDir = path.normalize(path.join(path.resolve(import.meta.dirname), '..', '..'));

if (url.startsWith('file:')) {
// Extract the path from the URL
Expand Down
2 changes: 1 addition & 1 deletion desktop/packages/mullvad-vpn/src/main/load-translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import path from 'path';
import log from '../shared/logging';

const SOURCE_LANGUAGE = 'en';
const LOCALES_DIR = path.resolve(__dirname, 'locales');
const LOCALES_DIR = path.resolve(import.meta.dirname, 'locales');

export function loadTranslations(
currentLocale: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default class NotificationController {
}

if (usePngIcon) {
const imagePath = path.join(__dirname, 'assets/images/icon-notification.png');
const imagePath = path.join(import.meta.dirname, 'assets/images/icon-notification.png');
// `nativeImage` is undefined when running tests
this.notificationIcon = nativeImage?.createFromPath(imagePath);
}
Expand Down
3 changes: 2 additions & 1 deletion desktop/packages/mullvad-vpn/src/main/proc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export function resolveBin(binaryName: string) {
function getBasePath(): string {
if (process.env.NODE_ENV === 'development') {
return (
process.env.MULLVAD_PATH || path.resolve(path.join(__dirname, '../../../../target/debug'))
process.env.MULLVAD_PATH ||
path.resolve(path.join(import.meta.dirname, '../../../../target/debug'))
);
} else {
return process.resourcesPath;
Expand Down
2 changes: 1 addition & 1 deletion desktop/packages/mullvad-vpn/src/main/tray-icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export class TrayIcon {
constructor(public fileName?: string) {}

public get basePath() {
const basePath = path.resolve(__dirname, 'assets/images/menubar-icons');
const basePath = path.resolve(import.meta.dirname, 'assets/images/menubar-icons');

return basePath;
}
Expand Down
4 changes: 2 additions & 2 deletions desktop/packages/mullvad-vpn/src/main/user-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export default class UserInterface implements WindowControllerDelegate {
if (process.env.NODE_ENV === 'development' && process.env.VITE_DEV_SERVER_URL) {
await window.loadURL(process.env.VITE_DEV_SERVER_URL);
} else {
await window.loadFile(path.join(__dirname, 'index.html'));
await window.loadFile(path.join(import.meta.dirname, 'index.html'));
}
} catch (e) {
const error = e as Error;
Expand Down Expand Up @@ -285,7 +285,7 @@ export default class UserInterface implements WindowControllerDelegate {
show: false,
frame: unpinnedWindow,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
preload: path.join(import.meta.dirname, 'preload.js'),
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import React, { JSX } from 'react';
import styled from 'styled-components';

import { type ValueOfArray } from '../../shared/utility-types';
import type { AllowedTag } from '../../shared/constants';
import { colors } from './foundations';

const Bold = styled.span({ fontWeight: 700 });
const Emphasis = styled.em({ color: colors.white, fontWeight: 600 });

export const ALLOWED_TAGS = ['b', 'br', 'em', 'a'] as const;
export type AllowedTags = ValueOfArray<typeof ALLOWED_TAGS>;

export type Transformer = (value: string) => React.ReactElement;
export type TransformerMap = Record<AllowedTags, Transformer>;
export type TransformerMap = Record<AllowedTag, Transformer>;

const defaultTransformers: Partial<TransformerMap> = {
b: (value) => <Bold>{value}</Bold>,
Expand Down
2 changes: 1 addition & 1 deletion desktop/packages/mullvad-vpn/src/renderer/redux/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default function configureStore() {

const rootReducer = combineReducers(reducers);

return createStore(rootReducer, composeEnhancers());
return createStore(rootReducer, {}, composeEnhancers());
}

function composeEnhancers(): StoreEnhancer {
Expand Down
3 changes: 2 additions & 1 deletion desktop/packages/mullvad-vpn/src/shared/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './urls';
export * from './strings';
export * from './translations';
export * from './urls';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ValueOfArray } from '../utility-types';

export const translations = {
allowedTags: ['b', 'br', 'em', 'a'],
allowedVoidTags: ['br'],
} as const;

export type AllowedTag = ValueOfArray<typeof translations.allowedTags>;
export type AllowedVoidTag = ValueOfArray<typeof translations.allowedVoidTags>;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { build } = require('./build');
const { setNodeEnvironment } = require('./utils');
const { build } = require('./build.cjs');
const { setNodeEnvironment } = require('./utils.cjs');

async function buildProduction() {
setNodeEnvironment('production');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { BUILD_STANDALONE_DIR } = require('./constants');
const { removeRecursively, runCommand, setNodeEnvironment } = require('./utils');
const { BUILD_STANDALONE_DIR } = require('./constants.cjs');
const { removeRecursively, runCommand, setNodeEnvironment } = require('./utils.cjs');

async function transpileBuildStandalone() {
await runCommand('npx tsc -p tsconfig.standalone.json');
Expand Down
9 changes: 9 additions & 0 deletions desktop/packages/mullvad-vpn/tasks/build-test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { build } = require('./build.cjs');
const { setNodeEnvironment } = require('./utils.cjs');

async function buildTest() {
setNodeEnvironment('test');
await build();
}

buildTest();
9 changes: 0 additions & 9 deletions desktop/packages/mullvad-vpn/tasks/build-test.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { copyAssetsToBuildDirectory, removeRecursively, runNpmScript } = require('./utils');
const { BUILD_DIR } = require('./constants');
const { copyAssetsToBuildDirectory, removeRecursively, runNpmScript } = require('./utils.cjs');
const { BUILD_DIR } = require('./constants.cjs');

async function build() {
await removeRecursively(BUILD_DIR);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { build } = require('./build');
const { packLinux } = require('./distribution');
const { build } = require('./build.cjs');
const { packLinux } = require('./distribution.cjs');

async function buildAndPackage() {
await build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { build } = require('./build');
const { packMac } = require('./distribution');
const { build } = require('./build.cjs');
const { packMac } = require('./distribution.cjs');

async function buildAndPackage() {
await build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { build } = require('./build');
const { packWin } = require('./distribution');
const { build } = require('./build.cjs');
const { packWin } = require('./distribution.cjs');

async function buildAndPackage() {
await build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { copyAssetsToBuildDirectory, setNodeEnvironment } = require('./utils.js');
const { copyAssetsToBuildDirectory, setNodeEnvironment } = require('./utils.cjs');

async function preDevelop() {
setNodeEnvironment('development');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const childProcess = require('child_process');
const fs = require('fs/promises');
const path = require('path');
const { BUILD_DIR, GEO_DIR, LOCALES_DIR, IMAGES_DIR, ICONS_DIR } = require('./constants');
const { BUILD_DIR, GEO_DIR, LOCALES_DIR, IMAGES_DIR, ICONS_DIR } = require('./constants.cjs');

async function copyAssetsToBuildDirectory() {
await Promise.all([
Expand Down
4 changes: 2 additions & 2 deletions desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class ApplicationMain {
frame: true,
webPreferences: {
offscreen: CI_E2E && !TEST_SHOW_WINDOW,
preload: path.join(__dirname, 'preload.js'),
preload: path.join(import.meta.dirname, 'preload.js'),
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
Expand All @@ -109,7 +109,7 @@ class ApplicationMain {

this.registerIpcListeners();

await window.loadFile(path.join(__dirname, 'index.html'));
await window.loadFile(path.join(import.meta.dirname, 'index.html'));

if (process.argv.includes('--show-window')) {
window.show();
Expand Down
9 changes: 6 additions & 3 deletions desktop/packages/mullvad-vpn/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": true,
"noEmit": true,
"moduleResolution": "bundler",
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "commonjs",
"module": "es2022",
"outDir": "build",
"rootDirs": [
"src",
"assets"
],
"skipLibCheck": true,
"target": "es2021",
"target": "es2022",
"lib": [
"es2021",
"es2022",
"dom"
],
"typeRoots": [
Expand Down
10 changes: 10 additions & 0 deletions desktop/packages/mullvad-vpn/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const viteConfig = defineConfig({
build: {
outDir: OUT_DIR,
},
resolve: {
dedupe: ['react', 'react-dom'],
},
plugins: [
electron({
main: {
Expand Down Expand Up @@ -112,6 +115,13 @@ const viteConfig = defineConfig({
vite: {
build: {
outDir: OUT_DIR,
rollupOptions: {
output: {
// We have to specify preload.js here as otherwise it would
// use the '.mjs' file extension.
entryFileNames: 'preload.js',
},
},
},
},
},
Expand Down
Loading
Loading