sindresorhus / default-browser

Get the default browser
MIT License
54 stars 9 forks source link

reduce bundle size by 90% #8

Closed milahu closed 4 years ago

milahu commented 4 years ago

the current solution for darwin is horrible cos we need a 50 KB bundle to solve a problem that can be solved in like 1 KB

when on darwin, we can use platform-dependent child_process.spawnSync calls

#!/bin/bash
# https://stackoverflow.com/a/32465364/10440128

x=~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist; \
plutil -convert xml1 $x; \
grep 'https' -b3 $x | awk 'NR==2 {split($2, arr, "[><]"); print arr[3]}'; \
plutil -convert binary1 $x

this is just one sample solution

at first sight, the file in $x should not be converted back-and-forth between xml and binary format only once from binary to xml or something readable like JSON - yes, plutil can translate to json also avoid using temp-files

here is a draft version, i have no darwin machine to test = no warranty

// darwin os: get default browser name
// use the `plutil` tool https://www.manpagez.com/man/1/plutil/
const child_process = require('child_process');
const lsf = '~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist';
const lso = JSON.parse(child_process.spawnSync(
  'plutil', ['-convert', 'json', '-o', '-', lsf],
  {maxBuffer: Infinity, encoding: 'utf-8'}).stdout);
// find value in lso
// ....
// print lso
console.log(JSON.stringify(lso, null, 2));

but this is still a bad solution, the convert step should go

it should be even easier with

// darwin os: get default browser name
// use the `defaults` tool https://ss64.com/osx/defaults.html
// defaults accepts short names for lsf
// https://superuser.com/a/20857/951886
const child_process = require('child_process');
const lsf = 'com.apple.LaunchServices';
const keyOfDefaultBrowser = 'TODO_key_path_here';
const val = child_process.spawnSync(
  'defaults', ['read', lsf, keyOfDefaultBrowser]).stdout;

the standard API function is LSCopyDefaultApplicationURLForURL (this is used by electron to get default apps) but its closed source, so we cant use that to get the key path

whos got a darwin machine? what is the structure of com.apple.LaunchServices?

edit: here is a sample output from defaults read output format is NeXTSTEP plist format

$ defaults read ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist
{
    LSHandlers =     (
                {
            LSHandlerContentType = "org.7-zip.7-zip-archive";
            LSHandlerPreferredVersions =             {
                LSHandlerRoleViewer = "-";
            };
            LSHandlerRoleViewer = "com.aone.keka";
        },
                {
            LSHandlerContentType = "public.html";
            LSHandlerPreferredVersions =             {
                LSHandlerRoleAll = "-";
            };
            LSHandlerRoleAll = "com.apple.safari";
        }
    );
}

ugly! and expensive to parse

so back to plutil -convert json -o - com.apple.LaunchServices then the minimal solution should be (using the logic from default-browser-id)

// darwin os: get default browser name
// use the `plutil` tool https://www.manpagez.com/man/1/plutil/
const child_process = require('child_process');
const getDefaultBrowserOnDarwin = () => (
    JSON.parse(child_process.execSync(
        "plutil -convert json -o - ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist",
        {maxBuffer: 1/0, encoding: "utf-8"}
    )).LSHandlers.filter(h => h.LSHandlerURLScheme == "http")
    .map(h => h.LSHandlerRoleAll).filter(Boolean)[0]
    || 'com.apple.Safari'
);

that is 414 bytes of code : P

told ya, we can do this in a 1 KB bundle

milahu commented 4 years ago

done. reduced bundle size by 95% : D

https://github.com/milahu/default-browser-name

/*
default-browser-name.js
size is 1380 byte
license is CC0-1.0
author is milahu
usage is
const
    f = require('./default-browser-name.js'),
    b = f(),
    r = f({raw:1});
*/

const s=require('os'),
c=(s)=>require('child_process').execSync(
    s,{maxBuffer:1/0, encoding:'utf-8'});

String.prototype.m1=function(e,f=''){
    return[this.match(e)].map(r=>r?r[1]:f)[0]};

module.exports=(a={})=>([[
[/(linux|freebsd)/,()=>(
    [c('xdg-mime query default text/html')]
    .map(r=>a.raw?r:r.m1(/^.+?-(.+)-.+?/))[0])],
[/darwin/,()=> ( [
    JSON.parse(c('plutil -convert json -o - '+
        '~/Library/Preferences/com.apple.LaunchServices/'+
        'com.apple.launchservices.secure.plist'))
    .LSHandlers.filter(h => h.LSHandlerURLScheme == 'http')
    .map(h => h.LSHandlerRoleAll).filter(Boolean)[0]
    || 'com.apple.Safari'].map(r=>a.raw?r:r.m1(/.+\.(.+?)$/)))],
[/win32/,()=>(
    [s.release().slice(0,3)=='10.'
    ? ['HKEY_CURRENT_USER/SOFTWARE/Microsoft/Windows/Shell/'+
        'Associations/URLAssociations/http/UserChoice',
        'ProgId',/.+ {4}(.+?)\r/,/^(.+)(\.HTTP|URL|HTML)$/,'edge']
    : ['HKCU/Software/Clients/StartMenuInternet',
        'REG_SZ',/REG_SZ  "(.+?)"/,/.+\\(.+?)(\.[^.]+)?$/,'ie']
    ].map(([p,k,e,f,d])=>c(
        'reg query '+p.replace(/\//g,'\\')+' | findstr '+k).m1(e))
    .map(r=>a.raw?r:r.m1(f,d))[0]
    )],
].find(x=>s.platform().match(x[0]))
].map(r=>r?r[1]().trim():'')
.map(r=>a.raw?r:r.toLowerCase())[0]
)
sindresorhus commented 4 years ago

Yeah, it could probably be improved. However, the priority was performance, not size. Spawning is very slow.