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

Unstable behavior across builds #1125

Closed
freiondrej opened this issue Jun 1, 2020 · 12 comments
Closed

Unstable behavior across builds #1125

freiondrej opened this issue Jun 1, 2020 · 12 comments
Labels

Comments

@freiondrej
Copy link

Expected Behaviour

Several consecutive builds without any code changes should produce consistent results - either success or failure.

Actual Behaviour

For some builds, I'm getting ts-related failures (usage of any etc). I'm using multiple tsconfigs in the project, each has its own webpack entrypoint. I thought the problem might be related to what was solved in #1104, but it did not solve the issue. What confuses me the most is that without any code changes, the results are different (sometimes the build succeeds just fine, same with launching webpack-dev-server). Is there anything like a cache involved in ts-loader internally, what could cause this to happen?

Steps to Reproduce the Problem

I'm using ts-loader 7.0.4 with webpack 4.42.0, webpack-cli 3.3.11 and webpack-dev-server 3.10.3

Location of a Minimal Repository that Demonstrates the Issue.

Because I don't know what and when causes the issue, I'm unable to create a reproduction for it.

@freiondrej
Copy link
Author

I tried some more debugging; neither using webpack --no-cache neither specifying cache: false in webpack config helped. An interesting observation: every first time after editing the configuration seemed to succeed, while second and subsequent failed (mostly - sometimes I got 2 successful attempts, then failure - never 3 successful attempts). Could there maybe be something like a cache bound to e.g. a hash of the configuration?

@freiondrej
Copy link
Author

OK, it's driving me crazy, so I did some more digging and I now know when the issue will happen, although I don't know why. I logged rootFileNames in method successfulTypeScriptInstance and although they did not differ, their order did - I have three entrypoints using ts-loader, let's call them A,B and C. I found out that order ABC builds successfully, order CBA as well, but order CAB breaks. The orders are random (= when the build starts, I cannot predict which order it will use) - that explains why those errors occurred randomly for me.

Could anyone help me with debugging why the order matters? Or at least how I can always use the same order - that would effectively solve my problem, because I could always use ABC which succeeds every time.

@freiondrej
Copy link
Author

I ended up solving this by writing a custom webpack loader which makes sure the order in which ts-loader is invoked is always the same. That effectively solved the issue for me, but the weirdness of ts-loader's failure with "bad" order remains. I'll leave the issue here for reference (if someone stumbles upon a similar problem), if the maintainers desire to close it, no problem.

@johnnyreilly
Copy link
Member

Thanks for reporting your findings - that's super helpful. If you're able to create a minimal reproducible repo (even if it is flaky) that will give someone a chance to potentially work on a permanent fix. Please do consider having a go yourself if you get a chance 🤗

@freiondrej
Copy link
Author

@johnnyreilly unfortunately by getting stuck on this I got two days behind my schedule, so I'm afraid I won't be able to get back to this anytime soon :( If there's a time window, I might give it a go, but cannot promise anything.

@johnnyreilly
Copy link
Member

That's cool - if you can help we'd appreciate it. No worries if not!

@connorjsmith
Copy link

@freiondrej Can you share your custom loader or the approach you took to ensure a deterministic ts-loader build? I think I'm running into the same problem

@freiondrej
Copy link
Author

@connorjsmith Hello, I created the following loader:

let currentHighestOrder = 1;
let queuedCallbacks = {};

module.exports = function loader(contents) {
    const callback = this.async();
    const options = this.query; // getOptions() from 'loader-utils';

    const order = +options.order;

    if (order <= currentHighestOrder) {
        // execute right away
        callback(null, contents);

        const fireableQueuedCallbacksOrder = currentHighestOrder + 1;

        if (queuedCallbacks.hasOwnProperty(fireableQueuedCallbacksOrder) && queuedCallbacks[fireableQueuedCallbacksOrder].length) {
            while (queuedCallbacks[fireableQueuedCallbacksOrder].length) {
                let cb = queuedCallbacks[fireableQueuedCallbacksOrder].shift();
                cb();
            }

            queuedCallbacks[fireableQueuedCallbacksOrder] = [];
        }

        if (order === currentHighestOrder) {
            currentHighestOrder++;
        }
    } else {
        if (false === queuedCallbacks.hasOwnProperty(order)) {
            queuedCallbacks[order] = [];
        }

        queuedCallbacks[order].push(() => {
            callback(null, contents);
        });
    }
};

It definitely is not perfect, I've never created a loader before neither do I understand the problematics - I simply needed the simplest working solution.

In webpack.common.js I have a dedicated rule for each ts config - the rule uses include to restrict the files. Then, the use config of the rule looks like this:

                "use": [
                    {
                        "loader": "ts-loader",
                        "options": {
                            "configFile": path.resolve(__dirname, 'src/typescript/tsconfig.json')
                        }
                    },
                    {
                        "loader": path.resolve('./waiting-loader.js'),
                        "options": {
                            "order": 2
                        }
                    }
                ]

Each of the rules specifies different options.order for the waiting-loader. That's how I've been able to always ensure the same order and prevent random failures :) I hope I my explanation is not too confusing.

@stale
Copy link

stale bot commented Sep 17, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Sep 17, 2020
@stale
Copy link

stale bot commented Sep 27, 2020

Closing as stale. Please reopen if you'd like to work on this further.

@stale stale bot closed this as completed Sep 27, 2020
@Ptival
Copy link

Ptival commented Oct 8, 2021

I also ran into this! :-(

I feel like it'd be nice to document this known issue in the main README, to avoid people having to re-discover it over and over until they end up on this issue.

@johnnyreilly
Copy link
Member

If you're game to put up a docs PR I'll take a look

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

No branches or pull requests

4 participants