diff --git a/src/build/prepareElectronApp.test.ts b/src/build/prepareElectronApp.test.ts new file mode 100644 index 0000000..2dca8e6 --- /dev/null +++ b/src/build/prepareElectronApp.test.ts @@ -0,0 +1,11 @@ +import { normalizeAppName } from './prepareElectronApp'; + +describe('normalizeAppName', () => { + test('it is stable', () => { + // Non-determinism / unstability would cause using a different appName + // at each app regen, thus a different appData folder, which would cause + // losing user state, including login state through cookies. + const normalizedTrello = normalizeAppName('Trello', 'https://trello.com'); + expect(normalizedTrello).toBe('trello-nativefier-679e8e'); + }); +}); diff --git a/src/build/prepareElectronApp.ts b/src/build/prepareElectronApp.ts index 84fe9a3..a679327 100644 --- a/src/build/prepareElectronApp.ts +++ b/src/build/prepareElectronApp.ts @@ -1,3 +1,4 @@ +import * as crypto from 'crypto'; import * as fs from 'fs'; import * as path from 'path'; import { promisify } from 'util'; @@ -127,9 +128,16 @@ async function maybeCopyScripts(srcs: string[], dest: string): Promise { } } -function normalizeAppName(appName: string): string { - // use a simple random string to prevent collisions - const postFixHash = generateRandomSuffix(); +/** + * Use a basic 6-character hash to prevent collisions. The hash is deterministic url & name, + * so that an upgrade (same URL) of an app keeps using the same appData folder. + * Warning! Changing this normalizing & hashing will change the way appNames are generated, + * changing appData folder, and users will get logged out of their apps after an upgrade. + */ +export function normalizeAppName(appName: string, url: string): string { + const hash = crypto.createHash('md5'); + hash.update(url); + const postFixHash = hash.digest('hex').substring(0, 6); const normalized = appName .toLowerCase() .replace(/[,:.]/g, '') @@ -137,10 +145,14 @@ function normalizeAppName(appName: string): string { return `${normalized}-nativefier-${postFixHash}`; } -function changeAppPackageJsonName(appPath: string, name: string): void { +function changeAppPackageJsonName( + appPath: string, + name: string, + url: string, +): void { const packageJsonPath = path.join(appPath, '/package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); - const normalizedAppName = normalizeAppName(name); + const normalizedAppName = normalizeAppName(name, url); packageJson.name = normalizedAppName; log.debug(`Updating ${packageJsonPath} 'name' field to ${normalizedAppName}`); @@ -184,5 +196,9 @@ export async function prepareElectronApp( } catch (err) { log.error('Error copying injection files.', err); } - changeAppPackageJsonName(dest, options.packager.name); + changeAppPackageJsonName( + dest, + options.packager.name, + options.packager.targetUrl, + ); }