Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs and appPath looking in electron distribution in node_modules #374

Closed
jefffriesen opened this issue Sep 5, 2016 · 40 comments
Closed

fs and appPath looking in electron distribution in node_modules #374

jefffriesen opened this issue Sep 5, 2016 · 40 comments

Comments

@jefffriesen
Copy link
Contributor

I'm unable to load a file using appPath because it's looking inside the Electron.app inside of node_modules:
electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar

screen shot 2016-09-05 at 11 14 42 am

Here is an example: jefffriesen@961fd0e

const remote = require('electron').remote;
const appPath = remote.app.getAppPath();
console.log('appPath: ', appPath);

const irisPath = `/${appPath}/app/data/iris.json`;
const irisData = JSON.parse(fs.readFileSync(irisPath, 'utf8'));
console.log('irisData: ', irisData);

I've compared this with the vanilla electron-quick-start app and it doesn't have this behavior:
jefffriesen/electron-quick-start@8cf2650
screen shot 2016-09-05 at 11 08 38 am

Is there an alternative to appPath that I should be using, or is possibly a configuration problem in this repo?

Thanks

@vinnymac
Copy link
Contributor

vinnymac commented Sep 7, 2016

@jefffriesen I think you are looking for remote.app.getPath('appData') You can then get the path to your json like I do below. Make sure to replace yourappsname with whatever the name of the app is.

import path from 'path'
import { remote } from 'electron'

const irisPath = path.join(remote.app.getPath('appData'), '/yourappsname/iris.json')

@jefffriesen
Copy link
Contributor Author

@vinnymac I'm not sure this is right, at least for my use case. But I may be thinking about this wrong.

I'm trying to bundle and distribute large data files with my app. appData looks inside the user's system. For example, on macOS it's ~/Library/Application Support. (https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname) which wouldn't contain the distributed data.

This code:

import path from 'path';
import { remote } from 'electron';

const irisPath = path.join(remote.app.getPath('appData'), '/electron-react-boilerplate/iris.json');
console.log('irisPath: ', irisPath);

const irisData = JSON.parse(fs.readFileSync(irisPath, 'utf8'));
console.log('irisData: ', irisData);

produces

irisPath:  /Users/jeffers/Library/Application Support/electron-react-boilerplate/iris.json
fs.js:640Uncaught Error: ENOENT: no such file or directory, open '/Users/jeffers/Library/Application Support/electron-react-boilerplate/iris.json'

Which is consistent with their documentation.

Using import to import the JSON files is clunky and caused problems with large files (50MB-100MB).

What's strange is this worked properly on a previous version of this boilerplate. I'm guessing it was a change the Electron 1.0+ API, but I don't know how to fix it.

@vinnymac
Copy link
Contributor

vinnymac commented Sep 7, 2016

@jefffriesen it works for me in my projects with the latest version of electron. I noticed that above you wrote the data path is that where your iris.json is located? Your path might be incorrect.

It looks like you are using OS X, so you can just open finder and type SHIFT+CMD+G and then type the path to your project, such as /Users/jeffers/Library/Application Support/electronreact/.

I think that the reason you got the error you received is because it uses the productName for the folder, and not the name in the package.json. See here.

This might also only occur when you run package.js. As I believe that may be creating the directory. Interestingly it seems that the folder doesn't exist in the development environment. I guess you need to check if the folder exists and create it yourself in that location during development. Kind of odd that you would need to do that, but it would certainly fix the issue, and any other issues that would arise from the folder not existing.

@jefffriesen
Copy link
Contributor Author

jefffriesen commented Sep 7, 2016

@vinnymac et al,
I did have an error in my path. I'm storing data in app/data/iris.json. I fixed that to double check but it didn't work. I also hardcoded appName to be 'thisAppName' just to be sure I was getting it right but that fix it either.

I'm confused though - why would there be any data in /Users/username/Library/Application Support/thisAppName unless I specifically write to that directory with fs? Is there some magic that Electron does to copy it over there?

I'm developing the app storing data in thisAppName/app/data/xyz.json. (I'm open to other suggestions). So I would think that there would be a way to access the app data where the .app or .exe is stored. For example, at /Application/thisAppName/contents/... if the user put the app in their Applications folder. And that's what used to work previously using a version of this boilerplate as a starting point.

I ran an experiment:

// ran in both old app and new app (unpackaged in dev environment):
 console.log("remote.app.getAppPath(): ", remote.app.getAppPath())
 console.log("remote.app.getPath('appData'): ", remote.app.getPath('appData'))

// latest electron-react-boilerplate
remote.app.getAppPath():  /Users/jeffers/git/forks/electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar
remote.app.getPath('appData'):  /Users/jeffers/Library/Application Support

// older app based on electron-react-boilerplate
remote.app.getAppPath():  /Users/jeffers/git/jefffriesen/oldAppName  // works correctly
remote.app.getPath('appData'):  /Users/jeffers/Library/Application Support

I have 3 apps based off an older version of this repo and all use fs and .getAppPath(). They all work properly in development and as a package both on Windows and Mac.

In the master branch of Electron, the docs state that getAppPath() should:

Returns the current application directory.

(https://github.com/electron/electron/blob/master/docs/api/app.md#appgetapppath)

I searched the release notes of Electron for getAppPath and didn't find anything, so I'm guessing they haven't changed that behavior. Could there be a configuration change in this repo that could be causing this?

Thanks for helping me track this down.

@vinnymac
Copy link
Contributor

vinnymac commented Sep 8, 2016

@jefffriesen You bring up a good point about asking why it is created. I haven't figured out when or why. However, I did some testing of my own to see if I could provide you more information. I noticed that during development I do not see any folders added to the appData directory. However, when I package the app, a folder is consistently created for me. Even with the latest versions of electron-react-boilerplate and electron I see the folders being created upon launch of my packaged applications.

When in development I believe electron prebuilt creates a tiny electron app in ./node_modules/electron-prebuilt/dist/, seeing as dev is so different that might explain why I am not seeing the folder creation, it appears to be something internal to electron. I am sure chromium itself needs a place to store its extensions, local storage, and everything else, so maybe this is basic chromium behavior.

If you package an app with the latest version do you see the folders created?

As an aside, you can probably solve this a different way. I think you are just trying to get access to some json data in electron, you shouldn't even need the path. Using webpack you should be able to just simply require the json file in your directory and then when it builds main or your bundle it will include the json as a module in the final bundle.

Something like

import iris from './data/iris.json'

Unless I misunderstood what it is you were trying to do. If you need to keep writing new json for some reason, then you will probably need a place to store that data, and then we will be back to discussing paths. If all you need is the data, then requiring it should work.

@jefffriesen
Copy link
Contributor Author

@vinnymac You're right - a folder is created in the appData directory (~/Library/Application Support) when launching a packaged app. I learned something new. Thanks.

Unfortunately, importing the data doesn't work because it's too big (the iris.json file was just an example). I'm working with files between 10MB and 100MB (one of the reasons I'm using Electron). So for example, webpack just gives up on a 50MB json file. Importing smaller but still-large json files blocks the whole UI until it's imported. Using fs gives me a lot more control over when that file is loaded and the UI state.

Now that I know Electron builds a set of files on startup in appData, and that maybe it builds an electron app inside the node_modules folder, I can see why looking in electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar may make sense. But something isn't hooked up right.

Looking back on my first post on this thread, it still seems relevant:

  1. Apps I built using a previous version of this boilerplate repo (from around March) worked fine. New apps based on the current master don't work anymore (for this fs problem).
  2. This repo has different behavior than the official electron-quick-start repo. I did an fs experiment there and it worked properly: jefffriesen/electron-quick-start@8cf2650
  3. Applying that same fs test I did on the quick start repo to a clean version of this repo fails: jefffriesen/electron-quick-start@8cf2650

So I'm still stuck but I also think other people are going to run up against this. This seems like it's due to a configuration change that's happened in the app in the last 6 months but I don't know where to test from here.

@vinnymac
Copy link
Contributor

vinnymac commented Sep 14, 2016

@jefffriesen If you are failing to include the json in webpack I would add the json to the noParse list. It will tell webpack that it is a large dependency and to leave it untouched. Hopefully that will help you see better results with larger files. This way you can include it in the bundle.

If for some reason that fails you, you can try requiring the json. According to what I can find online, including this, node.js appears to be capable of requiring json files for a long time now. You will still need the correct path, but you may be able to use relative ones. You'll have to configure webpack not to include the json though. In order to do that I believe you will have to add it to the list of externals, just like this project recommends doing with other node dependencies like sql. It will end up leaving the require untouched and then node will take over for you.

Getting the right path shouldn't be that bad, using relative require paths should make it easier. If you know the relative paths between the file you are requiring it from and the iris.json you should be able to require it successfully. Although if you don't know the path you might find yourself back in this appData directory issue.

I agree that something changed, and is funky. It would be awesome to figure out what and why, but I am still trying to think of other ways of solving your problem without figuring out what changed. If you'd like me to try to test anything for you let me know, I'd be willing to post my results as well.

@jefffriesen
Copy link
Contributor Author

@vinnymac great idea to tell webpack not to include the json. That would likely allow me to at least load the files. It's not as nice as fs but it may work for now.

I still want to get the path stuff figured out for me and all of us using this repo.

I was looking at other electron boilerplate repos to see how they configured the packaging. Here's one but nothing stands out as the solution: https://github.com/SimulatedGREG/electron-vue/blob/master/template/config.js. Maybe it does to you

@vinnymac
Copy link
Contributor

vinnymac commented Sep 14, 2016

@jefffriesen the first thing that pops out to me is the arguments in ignore.

The docs on electron-package state that those files won't be included in the app bundle. Perhaps you could temporarily ignore nothing, and see how that changes things. Just comment out these lines. If this works, I'd imagine it is as simple as placing your files in a path that is just not ignored.

In any case, attempting to see if any changes have been made around the dir and ignore options might prove useful.

@jefffriesen
Copy link
Contributor Author

@vinnymac your lead on what's included in ignore helped me solve this other ticket #384. Thanks.

I really want to solve this fs path problem. I've narrowed it down:

Data, as before, is stored in app/data/iris.json just like any other app file. I'm calling it like this:

const irisPath = `${appPath}/app/data/iris.json`;
const irisData = JSON.parse(fs.readFileSync(irisPath, 'utf8'));
console.log('appPath: ', appPath);

I tested different start configurations:


npm run package and launch. Works!:

appPath: /Users/jeffers/git/forks/electron-react-boilerplate/release/darwin-x64/thisAppName-darwin-x64/thisAppName.app/Contents/Resources/app

npm run hot-server and npm run start-hot (same as npm run dev): Doesn't work and I can't imagine we would ever want to write into or expect app data to be in the node_modules folder:

appPath: /Users/jeffers/git/forks/electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar

npm run hot-server and npm start. Works!

appPath: /Users/jeffers/git/forks/electron-react-boilerplate

Ok, so that works. The difference between npm start and npm run start-hot is:

 "start": "cross-env NODE_ENV=production electron ./",
 "start-hot": "cross-env HOT=1 NODE_ENV=development electron -r babel-register -r babel-polyfill ./main.development",

Thinking it had something to do with NODE_ENV=development, I tried start-hot with NODE_ENV=production:

npm run hot-server and npm run start-hot, switching to NODE_ENV=production. Doesn't work:

appPath: /Users/jeffers/git/forks/electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar

That leaves the culprit as one or both of these:

  1. babel-register and babel-polyfill
  2. main.js (works) vs. main.development.js (doesn't work)
    main.js is called as the default script from electron ./ from npm start and main.development.js is called from start-hot.

main.development.js seems straightforward. The only thing that looks suspicious is this:

mainWindow.loadURL(`file://${__dirname}/app/app.html`);

But that doesn't seem like it would do it. Maybe it's what's not being set in main.development.js but is in main.js?

Or maybe it's a babel issue, but that seems less likely. Open to suggestions

@vinnymac
Copy link
Contributor

vinnymac commented Sep 14, 2016

Hrmm, well if file://${__dirname}/app/ works for loadURL, maybe file://${__dirname}/app/data/iris.json would work for you? But that is probably for main.js.

When I am near my computer I'll try to do what you did above and post my findings, and see if I get anywhere with it.

npm start simply runs electron using the latest built main.js file. It would be used in the case that you wanted to try out production without packaging it up. Meaning webpack was used to create a new main.js from main.development.js.

EDIT

@jefffriesen I added a couple of additional logs such as __dirname, appData, and userData.

Results from a fresh install of electron-quick-start using npm run hot-server and npm run start-hot
screen shot 2016-09-14 at 4 38 22 pm

Results from a fresh install of your fs-test2 branch using npm run hot-server and npm run start-hot
screen shot 2016-09-14 at 4 48 43 pm

@jefffriesen
Copy link
Contributor Author

Quick followup:

  1. Adding the dataset in externals still does not allow fs to find the data. I wouldn't expect it to because appPath is still looking in the node_modules folder.
  externals: [
    './app/data/iris.json'
  ]
  1. I've also tried process.resourcesPath with no luck. It still looks at the node_modules folder, but it looks a Contents/Resources/ instead of Content/Resources/default_app.asar.

Branch with notes and minimal code are here: https://github.com/jefffriesen/electron-react-boilerplate/compare/master...jefffriesen:fs-test2?diff=unified&name=fs-test2

@vinnymac
Copy link
Contributor

vinnymac commented Sep 14, 2016

When looking at other helpful projects such as electron-builder it seems that they have an option for including extraResources such as .dlls. Since your app isn't creating the json, it doesn't really belong in that cache folder that is created in Application Support, despite the fact that for some odd reason electron-quick-start creates the folder while using npm start.

If you look at the docs for electron-builder you can see the options that they allow. I think that when building electron for production you need to tell it you are including this json as an extra file. If you don't want to require it via webpack and carry it along inside the renderer.js then you need to get it into the binary through some other means. I don't think relying on the path is going to be enough, because the path is pointing to a compressed asar.

I think electron is expected to be packaged with the necessary files, but you aren't passing these necessary files in when starting electron. electron-builder says it will put the files in /Contents/Resources for you if you use the correct arguments. electron-builder uses asar packing just like electron does. You can read more on application packaging here.

For starters, move /data/iris.json out of the app folder, it doesn't belong near any of the renderer.js stuff if you aren't going to be using webpack to retrieve it. Then maybe I'd investigate how to pack using asar. According to the app packaging docs I sent you, it seems you should even be able to require files using fs through the asar itself! So that is really the correct path

List all files under the root of the archive:

const fs = require('fs')
fs.readdirSync('/path/to/example.asar')

EDIT

After running this on the appPath /Users/vinnymac/Sites/electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar this is what I saw logged from reading the list of files. I imagine if you could get your file to get packaged inside of the asar, you'd be able to then require it as you desire.
screen shot 2016-09-14 at 6 04 29 pm

In a production packaged build this is what I see from listing
screen shot 2016-09-14 at 7 58 54 pm

You can also use asar.listPackage if you want to do this on the command line.

In production, these are the paths that were logged. I also added process.cwd().

screen shot 2016-09-14 at 7 59 28 pm

You would think including a json file would be as simple as including an image file or any other dependency of the project. For some reason I am convinced their is an easier way than all of this though. When looking at other apps I have built before using the same setup as this project, the final mac app contains /Contents/Resources/app/ and inside that folder is everything that my project comes with. If only this was also available during development inside of or next to the asar.

@jefffriesen
Copy link
Contributor Author

@vinnymac great research and info. I'm learning a lot.

First off, I tested __dirname and __filename from the renderer and it gave me / and /index.js respectively. Like you said, that is probably just used for main.js.

I did move data/iris.json out into the main application folder and adjusted the path for fs. Same results as last time - works for npm run package but not npm run dev.

I doubled checked to see if the build step was writing to any files inside node_modules/Electron/dist/Electron.app. Like you suggested it could be writing to the asar file. None of the files have been modified since a couple days ago when I fetched upstream changes. So in dev mode, it's looking there but not writing there.

The problem I think has to be in the step going from main.development.js to main.js, which is done by webpack.config.electron.js best as I can tell.

@jhen0409 @chentsulin @krzkaczor First of all thanks for doing so much for this repo. I'm stuck on a problem using fs in the renderer in dev mode (but packaging the app works fine).

You guys worked on this file: https://github.com/chentsulin/electron-react-boilerplate/blame/master/webpack.config.electron.js#L12 which creates main.js from main.development.js when running npm run start-hot

Here is a simple setup on a clean, recent branch of this repo:

const irisPath = `${appPath}/app/data/iris.json`;
const irisData = JSON.parse(fs.readFileSync(irisPath, 'utf8'));
console.log('appPath: ', appPath);

npm run server-hot and npm run start-hot creates a path inside the node_modules Electron app, which doesn't work (and I would think wouldn't be the intention):

appPath: /Users/jeffers/git/forks/electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar

npm run server-hot and npm start creates the correct path inside the package (this works)

appPath: /Users/jeffers/git/forks/electron-react-boilerplate

Do you all have any idea why appPath is looking in the wrong place during dev mode?

Here is a compare of my fork and master: master...jefffriesen:fs-test2

@vinnymac
Copy link
Contributor

vinnymac commented Sep 15, 2016

@jefffriesen I decided to investigate this issue from another point of view. This time I took the repo we had that was working, quick-start, and then I took your branch fs-test2. I made your branch more and more like quick-start until it worked as you intend it to. I believe I have discovered the cause.

First I replaced the index.html with the one from quick-start, then I created a renderer.js so that the require would succeed, and I added your logging from fs-test2. Next I replaced the main.development.js with the one from quick-start. At this point the app was loading successfully using npm run start-hot, but the issue was still occurring. I still saw the asar path. So I then simplified npm run start-hot to just be electron ./main.development.

This also did not fix the issue.

Finally, I created a main.js manually from the main.js on quick-start, same as the code in main.development.js. I also changed npm run start-hot to electron ..
screen shot 2016-09-14 at 9 00 10 pm

It worked. So apparently this is because electron needs to see your package.json for the name of the app. I tried other file names and paths such as ./index.js, ./main.js, and main.js. They all had the same asar path we have seen. You can even rename main.js to index.js and it will still load, as long as you use electron . in your script it appears to work without any issue.

Trying this helped me understand it.

  1. Create a folder named example in the root directory.
  2. Copy and paste index.html, renderer.js, main.js, and package.json to the new folder.
  3. In the root directories package.json use electron ./example for npm run start-hot.

The result should be the same path as before, but this time with /example appended.

One more reason to use the two separate package.json configuration that I use in some of my other projects, kind of a shame that it can't be done with one.

Relevant Issues electron/electron#3204
Relevant StackOverflow

@jefffriesen
Copy link
Contributor Author

@vinnymac Great detective work. This is the money quote from that stack overflow link (which is what you're saying too):

I believe the issue is that you're pointing Electron to your script directly, meaning Electron is ignoring the existence of your package.json file entirely. Thus it does not know the name of your app.

Doesn't that sound like a poor design choice by Electron? I know lots of people are working on Electron and using it in huge apps, so maybe there is a really good reason for it. It doesn't smell right and it's really subtle.

So what's the next step for this? Is there a PR that would be appropriate here? Or will this be made irrelevant by this PR: #310

@vinnymac
Copy link
Contributor

@jefffriesen I am not sure what the next step here is. Obviously electron-builder makes this problem easier to handle, albeit with a more complex configuration. In the PR you posted, I see that some others have already implemented it and are a good reference for moving forward. However, using the existing code I think the only thing to do would be to use a hacky solution such as this

const remote = require('electron').remote;
const appPath = process.env.NODE_ENV === 'production' ? remote.app.getAppPath() : __dirname

This way development mode falls back to being the root directory where the main.development.js is being executed. In production it will continue to use the correct location in /Content/Resources/.

I'd love to see someone come up with a better solution, as I am not a big fan of this one.

@jefffriesen
Copy link
Contributor Author

I've been thinking along the lines of the NODE_ENV conditional as well. You're right though it's a little hacky. It's fine for me personally for now since I know the issues. I hope other people don't get stuck on this.

It sounds like electron-builder is a better packager anyway and that PR seems fairly close. Should we keep this ticket open while we wait for that?

@amilajack
Copy link
Member

Was this issue resolved?

@jefffriesen
Copy link
Contributor Author

No. We currently have a hacky workaround:

const remote = require('electron').remote;
const appPath = process.env.NODE_ENV === 'production' ? remote.app.getAppPath() : __dirname

But something in main.development.js is breaking where getAppPath() is looking for files

@amilajack
Copy link
Member

What happens when you npm run dev?

@jefffriesen
Copy link
Contributor Author

These don't work:

npm run hot-server && npm run start-hot
npm run dev

This works:

npm run hot-server && npm start

You can see the tests I ran higher in the comments: #374 (comment)

I think the key finding from @vinnymac is that when you point Electron at a script directly, it ignores package.json and doesn't know the name of the app. If you point Electron to a directory that contains a package.json it works because it reads the package.json.

You can see that with these scripts:

"start-hot": "cross-env HOT=1 NODE_ENV=development electron -r babel-register -r babel-polyfill ./main.development",

"start": "cross-env NODE_ENV=production electron ./",

You can see his comment here: #374 (comment)

@jefffriesen
Copy link
Contributor Author

I wonder if it makes sense to use 2 package.json files like @vinnymac suggested. Here is an electron boilerplate repo that does it:
https://github.com/szwacz/electron-boilerplate#declaring-dependencies
This is their reasoning:

OMG, but seriously why there are two package.json?

  1. Native npm modules (those written in C, not JavaScript) need to be compiled, and here we have two different compilation targets for them. Those used in application need to be compiled against electron runtime, and all devDependencies need to be compiled against your locally installed node.js. Thanks to having two files this is trivial.
  2. When you package the app for distribution there is no need to add up to size of the app with your devDependencies. Here those are always not included (reside outside the app directory).

But for us it would also solve our getAppPath problem (which maybe they didn't run into). I can't help but think this should be coordinated with the PR that is pending that will use electron-builder to build the app: #310 that @dustintownsend and @xwartz are working on.

@amilajack
Copy link
Member

electron-builder support is coming in a few days or so.

@jefffriesen
Copy link
Contributor Author

Looking forward to it

On Oct 8, 2016, at 7:23 PM, Amila Welihinda notifications@github.com wrote:

electron-builder support is coming in a few days or so.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@amilajack
Copy link
Member

@jefffriesen does this issue persist in the latest release?

@jefffriesen
Copy link
Contributor Author

I'll look into it. Thanks

@jefffriesen
Copy link
Contributor Author

@amilajack Unfortunately it's still not fixed even with the dual package.json setup.

I've merged upstream and reinstalled all node modules detailed here (#400)

npm run dev
Uncaught Error: ENOENT, data/iris.json not found in /Users/jeff/git/forks/electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar

It's still looking in the node_modules folder for the assets.

You can see the diff compared to master here: jefffriesen/electron-react-boilerplate@master...jefffriesen:fs-test2

Packaging the app (on Mac) now gives a new error

npm run package
Uncaught Error: ENOENT, data/iris.json not found in /Users/jeff/git/forks/electron-react-boilerplate/release/mac/electron-react-boilerplate.app/Contents/Resources/app.asar

@amilajack
Copy link
Member

@jefffriesen can you point to the source of this project?

@jefffriesen
Copy link
Contributor Author

Here is the diff of the branch vs master on my fork of the repo: jefffriesen/electron-react-boilerplate@master...jefffriesen:fs-test2

@amilajack
Copy link
Member

@jefffriesen was this resolved?

@jefffriesen
Copy link
Contributor Author

jefffriesen commented Dec 9, 2016 via email

@jefffriesen
Copy link
Contributor Author

@amilajack this isn't fixed yet. I'm kind of surprised no one else is running into problems using fs since that is a nice feature of Electron. Or if they are using fs they are using it differently. I would love to know what it is.

Here is a simple example of it breaking, based off the latest master:
master...jefffriesen:fs-test3

It's basically this:

const remote = require('electron').remote;
const appPath = remote.app.getAppPath();
const irisPath = `${appPath}/data/iris.json`;
const irisData = JSON.parse(fs.readFileSync(irisPath, 'utf8'));
console.log('irisData: ', irisData);

The error I get with npm run dev are:

ELECTRON_ASAR.js:115Uncaught Error: ENOENT, data/iris.json not found in /Users/jeffers/git/forks/electron-react-boilerplate/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar

I don't think it should be looking in the default_app.asar file. The error I get after packaging it on a mac with npm run package is:

Uncaught Error: ENOENT, data/iris.json not found in /Users/jeffers/git/forks/electron-react-boilerplate/release/mac/ElectronReact.app/Contents/Resources/app.asar

@vinnymac
Copy link
Contributor

@jefffriesen if I were you I would open an issue in the electron or electron-builder repositories. This issue seems to be specific to starting electron from a specific path where a package.json is, and is not particular to electron-react-boilerplate. I would link back to this discussion for education on the problem.

@JackieLs
Copy link

JackieLs commented Jun 9, 2017

How about process.cwd() ?

@vinnymac
Copy link
Contributor

vinnymac commented Jun 9, 2017

@JackieLs process.cwd() will work fine in development, as will __dirname but in production process.cwd() will not work.

Running the following before we use fs
console.log('process.cwd', process.cwd())

Produces the following for me
development - process.cwd /Users/vinnymac/Sites/electron-react-boilerplate
production - process.cwd /

Production will throw an error when we use fs because / is obviously the incorrect location for this data.

Same reason why the fix we came up with above works, because it checks whether or not you are using production or development first. I think this issue needs to be brought up with electron or electron-builder if we want to fix it, as it is not specific to this project necessarily. It is strictly related to how electron can be used on the CLI with a package.json.

@mtompkins
Copy link

I believe this issue needs to be revisited as there is really a 3rd "state" that has not been addressed. Specifically, I am having the exact problem listed above and have used the logic of "production" and "development" for file access.

Here is my example:

const appPath = process.env.NODE_ENV === 'production' ? app.getAppPath() : path.join(__dirname, '..', '..');
export const cKey = fs.readFileSync(path.join(appPath, 'main/certs/client-key.pem'));
export const cCert = fs.readFileSync(path.join(appPath, 'main/certs/client-cert.pem'));
export const cCA = fs.readFileSync(path.join(appPath, 'main/certs/ca-cert.pem'));

This allows me to call either yarn run dev or yarn start successfully.
Once packaged into an asar however, this all breaks down.

So there really are 3 states: development, production, and packaged (the last of which would only be relevant to production).

In its current form, you have to sacrifice one of the production states unless I've missed something.

@vinnymac
Copy link
Contributor

@mtompkins do you have a reproduction? The above tests were run with yarn run dev and yarn run package for development and production builds.

@mtompkins
Copy link

@vinnymac I'll strip up an example as soon as I can. Thanks for the reply.

@mtompkins
Copy link

Ok - so after referencing much of the above and a bit more research...
For my use case I have a few cert (.pem) files that I need loaded in the main thread for DB connectivity.

Path looks like:
app/main/certs/*.pem

Following down the references to asar and how it may be leveraged directly as a virtual folder, I added to package.json as follows:

...
"files": [
      "dist/",
      "main/certs/",          // my add
      "node_modules/",
      "app.html",
      "main.prod.js",
      "main.prod.js.map",
      "package.json"
    ],
...

this causes the directory to be copied including preserving the path in to the asar

That can be checked by a asar list app.asar in the releases/*-unpacked/resources directory

This then allowed me to use references that were consistent across dev, prod, and package.

HTH someone else

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants