electron-react-boilerplate icon indicating copy to clipboard operation
electron-react-boilerplate copied to clipboard

Custom Protocol for Deep Link no working in Development

Open tilto0822 opened this issue 2 years ago • 23 comments

Hello, I'm created custom protocol for deep link. code is like bottom.

src/main/main.ts

app.removeAsDefaultProtocolClient('customtest');

if (process.defaultApp) {
  console.log(process.execPath);
  console.log(process.argv);
  if (process.env.NODE_ENV === 'development' && process.platform === 'win32') {
    app.setAsDefaultProtocolClient('customtest', process.execPath, [
      process.argv[1],
      path.resolve(process.argv[2]),
      process.argv[3],
      path.join(
        __dirname,
        '..',
        '..',
        'node_modules',
        'ts-node',
        'register',
        'transpile-only.js'
      ),
      path.resolve(process.argv[5]),
    ]);
  } else if (process.platform === 'win32') {
    app.setAsDefaultProtocolClient('customtest', process.execPath, []);
  } else {
    app.setAsDefaultProtocolClient('customtest');
  }
} else {
  app.setAsDefaultProtocolClient('pixellauncher', process.execPath);
}

const gotTheLock = app.requestSingleInstanceLock();

(skip)

app.on('open-url', (event, url) => {
    dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`);
});

image When I package the app, it's working well. but run development with npm run start, then it cause the error "unable to parse" like this.

[
  'C:\\Users\\tilto\\Documents\\git-projects\\***\\node_modules\\electron\\dist\\electron.exe',
  '--require',
  'C:\\Users\\tilto\\Documents\\git-projects\\***\\node_modules\\electronmon\\src\\hook.js',
  '-r',
  'ts-node/register/transpile-only',
  '.'
]

I checked process argument like this. so this is protocol set in development.

app.setAsDefaultProtocolClient('customtest', process.execPath, [
      process.argv[1],
      path.resolve(process.argv[2]),
      process.argv[3],
      path.join(
        __dirname,
        '..',
        '..',
        'node_modules',
        'ts-node',
        'register',
        'transpile-only.js'
      ),
      path.resolve(process.argv[5]),
    ]);

How can I get Custom Protocol for Deep Link to work in Development in Electron React Boilerplate?

tilto0822 avatar Jun 21 '22 01:06 tilto0822

same question, It's work in package.

I don't find more explanation about deep link.

What do these two sentences mean?

doc path string (optional) Windows - The path to the Electron executable. Defaults to process.execPath args string[] (optional) Windows - Arguments passed to the executable. Defaults to an empty array

colining avatar Jun 23 '22 03:06 colining

Was having the same issue in development mode. For me the following approach worked:

app.setAsDefaultProtocolClient('customtest', process.execPath, [
    '-r',
    'ts-node/register/transpile-only',
    '.',
]);

In my case process.execPath was pointing to 'C:\\Users\\***\\node_modules\\electron\\dist\\electron.exe'. Depending on your setup you may need to resolve '.' to an absolute path.

Hope this helps you as well 🤞

IanStorm avatar Sep 28 '22 13:09 IanStorm

Was having the same issue in development mode. For me the following approach worked:

app.setAsDefaultProtocolClient('customtest', process.execPath, [
    '-r',
    'ts-node/register/transpile-only',
    '.',
]);

In my case process.execPath was pointing to 'C:\\Users\\***\\node_modules\\electron\\dist\\electron.exe'. Depending on your setup you may need to resolve '.' to an absolute path.

Hope this helps you as well 🤞

Hey, regarding your workaround, what am I looking to point it to? When I paste your code it says it cannot find the module "ts-node/register/transpile-only/ so I don't even hink it is at the point where its looking at the relative or absolute path yet. I've been trying to make this work for 3 weeks i'm really losing my mind on this.......

Sabin-DC avatar Oct 27 '22 13:10 Sabin-DC

@Sabin-DC ,

if ( process.env.NODE_ENV === 'development' && process.platform === 'win32') { app.setAsDefaultProtocolClient(APP_PROTOCOL, process.execPath, [ '-r', path.resolve(__dirname,'..','..','node_modules','ts-node/register/transpile-only'), path.resolve(__dirname,'..','..'), ]); } else { app.setAsDefaultProtocolClient(APP_PROTOCOL); }

Depending on your folder path you need to adjust the depth or not. There seems to be a problem with global ts-node when given the argument so you must explicitely set both paths.

veemex avatar Nov 01 '22 15:11 veemex

@Sabin-DC ,

if ( process.env.NODE_ENV === 'development' && process.platform === 'win32') { app.setAsDefaultProtocolClient(APP_PROTOCOL, process.execPath, [ '-r', path.resolve(__dirname,'..','..','node_modules','ts-node/register/transpile-only'), path.resolve(__dirname,'..','..'), ]); } else { app.setAsDefaultProtocolClient(APP_PROTOCOL); }

Depending on your folder path you need to adjust the depth or not. There seems to be a problem with global ts-node when given the argument so you must explicitely set both paths.

The error i'm getting right now is that it says it "Cannot find module 'ts-node/register/transpile-only'. Is this because I don't have ts-node installed globally? Or perhaps the path for my app?

Sabin-DC avatar Nov 01 '22 17:11 Sabin-DC

@Sabin-DC , yes. It throws that error because "path.resolve(__dirname,'..','..','node_modules','ts-node/register/transpile-only')" is not pointing correctly. Can you try to debug this by looking at your folder paths ? The thing I did fixed it for me, thanks to @IanStorm . Also you can try debugging by getting the "process.execPath" and then running in the terminal the command until it works, by replacing the variables with the actual locations.

veemex avatar Nov 01 '22 22:11 veemex

@Sabin-DC , yes. It throws that error because "path.resolve(__dirname,'..','..','node_modules','ts-node/register/transpile-only')" is not pointing correctly. Can you try to debug this by looking at your folder paths ? The thing I did fixed it for me, thanks to @IanStorm . Also you can try debugging by getting the "process.execPath" and then running in the terminal the command until it works, by replacing the variables with the actual locations.

When I console log process.execPath its giving me this:

"F:\Dev_Projects\React Projects\dummy2\node_modules\electron\dist\electron.exe".

This does exist and now that I copy & pasted your code and replaced APP_PROTOCOL with a custom protocol, if I call it it will open a second main window once with a white screen, so not focusing the existing one (I have to event handle that I suppose). However if I call the deeplink a second time within that same session then I get this error:

"Unable to find electron app at C:\WINDOWS\system32\testdeep:"

Not sure if by opening one window it's re-assigning the paths on this second pass and now it's just incorrectly assigned... I might just have to do an event handler properly for the first time I call to focus the main and not open a 2nd instance.

Sabin-DC avatar Nov 01 '22 23:11 Sabin-DC

@Sabin-DC , yes. It throws that error because "path.resolve(__dirname,'..','..','node_modules','ts-node/register/transpile-only')" is not pointing correctly. Can you try to debug this by looking at your folder paths ? The thing I did fixed it for me, thanks to @IanStorm . Also you can try debugging by getting the "process.execPath" and then running in the terminal the command until it works, by replacing the variables with the actual locations.

Alright well some progress is made I think (at least in my understanding of it), now i'm pointing the app towards the /src/main/main.ts but it's still opening a new window with nothing inside so i'm not sure what exactly is going on. Is it not transpiling the typescript but still running it? This is so confusing without having any errors...

Sabin-DC avatar Nov 02 '22 16:11 Sabin-DC

@Sabin-DC , well the second browserWindow exists there only for you to get the path, you need this in order to handle it properly https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app . You need to implement that "gotTheLock" in your app.

veemex avatar Nov 30 '22 16:11 veemex

I solved this problem, but I used an alias to make it impossible to compile。 The plugin I use is: typescript-transform-paths

MiniOcean404 avatar Dec 06 '22 09:12 MiniOcean404

If you use the tsconfig-paths plugin ,write a script

const path = require('path');
const old = process.cwd;

const basePath = path.resolve('D:\soft-dev\code\web\frame\React\electron-printer')

process.cwd = () => {
return basePath;
};
ts_node.register({
transpileOnly: true,
pretty:true,

files: true,
cwd: basePath,
dir: basePath,

projectSearchDir:basePath,
project:path.join(basePath,'tsconfig.json'),
require:[path.join(basePath,'node_modules','tsconfig-paths/register')],
});
process.cwd = old;

exec app.setAsDefaultProtocolClient('customtest', process.execPath, [ '-r', 'script', process.cwd() ]);

This idea messed with me all day and all night!!!!!!!!!!!!!

MiniOcean404 avatar Dec 06 '22 17:12 MiniOcean404

Has anyone figured out a solution to this?

harismuha123 avatar Dec 21 '22 16:12 harismuha123

@amilajack do you perhaps have a solution for how this should be handled?

harismuha123 avatar Dec 23 '22 13:12 harismuha123

I don't unfortunately, I might find the time to repro this and put together a solution

amilajack avatar Feb 05 '23 20:02 amilajack

I've been battling with this so far as well, but no luck. Just for a note, this is relevant when you're using electronmon + typescript! This issue is relevant only to Windows!

ifree92 avatar Mar 11 '23 12:03 ifree92

@ifree92 @amilajack I managed to resolve it eventually by doing it like this:

// remove so we can register each time as we run the app.
app.removeAsDefaultProtocolClient(PROTOCOL_ID);

// If we are running a non-packaged version of the app && on windows
if (process.argv.length >= 2) {
  app.setAsDefaultProtocolClient(PROTOCOL_ID, process.execPath, [
    '-r',
    path.join(
      __dirname,
      '..',
      '..',
      'node_modules',
      'ts-node/register/transpile-only'
    ),
    path.join(__dirname, '..', '..'),
  ]);
} else {
  app.setAsDefaultProtocolClient(PROTOCOL_ID);
}

Also very IMPORTANT: Don't run the process as an Administrator on Windows, the deep links never work that way in development.

harismuha123 avatar Mar 13 '23 09:03 harismuha123

@harismuha123 thank you for the example. Looks like it still doesn't work. It looks like it is trying to open the second instance of the app.

But I will play around, will see what I can do.

ifree92 avatar Mar 15 '23 19:03 ifree92

@harismuha123 thank you for the example. Looks like it still doesn't work. It looks like it is trying to open the second instance of the app.

But I will play around, will see what I can do.

You need to implement the single instance locking mechanism from the Electron documentation on deep links in Windows. Otherwise it will always try opening a second instance.

harismuha123 avatar Mar 15 '23 19:03 harismuha123

@harismuha123 Using your example I faced another problem in that my tsconfig.json file has not been read as expected. I did not find a way to specify over -r ts-node/register the specific tsconfig.json file.

No, actually, there was a way to do that using TS_NODE_PROJECT env variable, but no way to specify this variable while registering the protocol.

So I found another workaround.

Just create the file, e.g.: your-electron-project/dev-main.js with the following code:

const path = require('path');

require('ts-node').register({
  project: path.resolve(__dirname, 'tsconfig.custom.json'),
  transpileOnly: true,
});
require('./src/main/main.ts');

So the your-electron-project/src/main/main.ts file would look like:

// remove so we can register each time as we run the app.
app.removeAsDefaultProtocolClient(PROTOCOL_ID);

// If we are running a non-packaged version of the app && on windows
if (process.argv.length >= 2) {
  app.setAsDefaultProtocolClient(PROTOCOL_ID, process.execPath, [path.join(__dirname, '..', '..', 'dev-main.js')]);
} else {
  app.setAsDefaultProtocolClient(PROTOCOL_ID);
}

Run your project using the following script:

{
  "scripts": {
    "start": "electronmon dev-main.js"
  }
}

Now it works for me.

ifree92 avatar Mar 15 '23 22:03 ifree92

@ifree92 @amilajack I managed to resolve it eventually by doing it like this:

// remove so we can register each time as we run the app.
app.removeAsDefaultProtocolClient(PROTOCOL_ID);

// If we are running a non-packaged version of the app && on windows
if (process.argv.length >= 2) {
  app.setAsDefaultProtocolClient(PROTOCOL_ID, process.execPath, [
    '-r',
    path.join(
      __dirname,
      '..',
      '..',
      'node_modules',
      'ts-node/register/transpile-only'
    ),
    path.join(__dirname, '..', '..'),
  ]);
} else {
  app.setAsDefaultProtocolClient(PROTOCOL_ID);
}

Also very IMPORTANT: Don't run the process as an Administrator on Windows, the deep links never work that way in development.

This solution worked for me as well. I don't have to change anything elsewhere.

Thanks for the help.

awaisali88 avatar Sep 14 '23 00:09 awaisali88

What's important for calling app.setAsDefaultProtocolClient on Windows is that the first item in the args parameter array is the path to the directory where the package.json for your Electron app is located.

Consider the following example. Let's say you have a CLI argument --app-data-path that you can pass to your Electron app like this:

$ npx electron --user-data-path={./foo} .

In this scenario, calling app.setAsDefaultProtocolClient('customtest', process.execPath, [process.argv[1], ... ]) won't work as process.argv[1] is --user-data-path={./foo}, and then you'll be getting the error "Error launching app".

One option to fix this would be to find the path in process.argv, put in front when passing to app.setAsDefaultProtocolClient, and then pass the rest of process.argv.

mkarp avatar Mar 28 '24 10:03 mkarp