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

Saving the same file over and over again quickly causes problems #262

Open
tankorsmash opened this issue Nov 4, 2021 · 2 comments
Open

Comments

@tankorsmash
Copy link

tankorsmash commented Nov 4, 2021

In my day-to-day editing of an Elm file, fairly often the js fails to reload properly so the page crashes. I can reproduce it in my medium sized project pretty consistently by spamming CTRL-S in my editor.

It seems like the websocket gets cut off early instead of sending the whole reloaded js file or something. Occasionally the JS error is 'unexpected end of input in main.js', but sometimes it's 'Elm is undefined' or something along those lines.

I hate to give a screenshot of code, but this demonstrates one way the the main.js file ends up incomplete (where the file ends is inconsistent, so particular line of code in the screenshot doesn't matter):

image

Just reloading the page resolves the issue, but ideally I'd be able to make code changes without worrying about hitting Save too often in my editor.

I'm running elm-live 4.0.2 on Windows 10 through Powershell with the following command:

elm-live .\src\Main.elm -u --verbose "--" --output="main.js" --debug

but the issue happens with --hot too, and just tested that 4.0.1 has the same issue: image

I've tried making a minimal repro case but haven't been able to reproduce the issue on a project a few dozen lines long.

@tankorsmash
Copy link
Author

tankorsmash commented Nov 4, 2021

Hacked a workaround until this is resolved. In my index.html, instead of putting the main.js in a <script>, I create it dynamically, and then in script.onload, I check to make sure Elm is defined, otherwise I change the scripts src to main.js?tries=1 etc. The downside is this requires a hack to change elm-live's start.js's parseUrl to ignore querystrings for the target:

//index.html, in a <script> tag, where callback is whatever logic for initializing elm you use

let tries = 0;
const mainName = "/main.js";

function loadScript(url, callback)
{
	var head = document.head;
	var script = document.createElement('script');
	script.type = 'text/javascript';
	script.src = url;

	const try_again = () => {
		script.remove();
		let new_url = mainName + "?____tries___="+tries;
		console.log("trying again for", new_url)
		loadScript(new_url, callback);
		tries += 1;
	}

	script.onload = (args, second_args) => {
		if (typeof Elm === "undefined") { 
			try_again();
		} else {
			return callback()
		}
	};

	// Fire the loading
	head.appendChild(script);
}
//elm-live/lib/src/start.js
function parseUrl (pathname, model) {
	if (pathname.startsWith(model.target)) {
		console.log("overriting pathname to ", model.target);
		pathname = model.target;
	}
   ...
}

Maybe there's a better way of getting elm-live to see that main.js?anythingatall is still a request for main.js, but this works for now, even if it only stops the partial file errors, and not the errors about the file length mismatch.

tankorsmash added a commit to tankorsmash/elm_fooling that referenced this issue Nov 4, 2021
…g and reloading the script

made an issue on elm-live wking-io/elm-live#262 to track this
@tankorsmash
Copy link
Author

tankorsmash commented Nov 28, 2021

I've made some changes to elm-live's build.js to hack around this issue. It seems to be some sort of issue with either elm fighting with another sub process of elm make in runElm, so a solution I'm using is setting a global variable to true when its building, and false when its done.

const GLOBALS = {};
GLOBALS['isBuilding'] = false;
const setIsBuilding = () => { GLOBALS['isBuilding'] = true; }
const setIsNoLongerBuilding = () => { GLOBALS['isBuilding'] = false; }

then runElm turns into

const runElm = model => Async((reject, resolve) => {
	const wasBuilding = isItBuilding();

	setIsBuilding();
  
        ...
        if (!wasBuilding ) {
	  proc = spawn(model.elm, ['make', '--report=json', ...model.elmArgs])
	} else {
          reject(`already ${chalk.bold('building')}, so just exiting instead.`);
	  return;
	}
        ...

         proc.on('error', function (error) {
	   setIsNoLongerBuilding();
           if (error.code === 'ENOENT') {
           ...
         }

         proc.on('exit', function (status) {
           setIsNoLongerBuilding();
           ...
         }
   ...
   return wasBuilding; //not sure if this is necessary, since its in a callback as it is
}

This effectively just kills elm-live when it tries to build while elm-live's already in the process of building once.

A better solution would either:

  • put the error message I reject with into messages.js
  • or, add some action in init.js and resolve() instead of reject()ing, and then handling that action down the pipe, so elm-live wouldn't crash. Everything I tried to hack together still ended up with a JS error in Chrome with main.js being half-written to, or a HTTP error with a length mismatch (probably a symptom of the same issue, just sooner up the chain)

For me, I just run elm-live in a powershell loop and its good enough for now (appears to work with hot-reloading too, which is a nice bonus)

while ($true) { elm-live elmliveargs "--" elmmakeargs  }

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

1 participant