const swc = require("@swc/core");

function makeLoader() {
    return function (source, inputSourceMap) {
        // Make the loader async
        const callback = this.async();
        const filename = this.resourcePath;

        let loaderOptions =
            (typeof this.getOptions === "function"
                ? this.getOptions()
                : require("loader-utils").getOptions(this)) || {};

        // Standardize on 'sourceMaps' as the key passed through to Webpack, so that
        // users may safely use either one alongside our default use of
        // 'this.sourceMap' below without getting error about conflicting aliases.
        if (
            Object.prototype.hasOwnProperty.call(loaderOptions, "sourceMap") &&
            !Object.prototype.hasOwnProperty.call(loaderOptions, "sourceMaps")
        ) {
            loaderOptions = Object.assign({}, loaderOptions, {
                sourceMaps: loaderOptions.sourceMap,
            });
            delete loaderOptions.sourceMap;
        }

        if (inputSourceMap && typeof inputSourceMap === "object") {
            inputSourceMap = JSON.stringify(inputSourceMap);
        }

        const programmaticOptions = Object.assign({}, loaderOptions, {
            filename,
            inputSourceMap: inputSourceMap || undefined,

            // Set the default sourcemap behavior based on Webpack's mapping flag,
            // but allow users to override if they want.
            sourceMaps:
                loaderOptions.sourceMaps === undefined
                    ? this.sourceMap
                    : loaderOptions.sourceMaps,

            // Ensure that Webpack will get a full absolute path in the sourcemap
            // so that it can properly map the module back to its internal cached
            // modules.
            sourceFileName: filename,
        });
        if (!programmaticOptions.inputSourceMap) {
            delete programmaticOptions.inputSourceMap;
        }

        const sync = programmaticOptions.sync;
        const parseMap = programmaticOptions.parseMap;

        // Remove loader related options
        delete programmaticOptions.sync;
        delete programmaticOptions.parseMap;
        delete programmaticOptions.customize;
        delete programmaticOptions.cacheDirectory;
        delete programmaticOptions.cacheIdentifier;
        delete programmaticOptions.cacheCompression;
        delete programmaticOptions.metadataSubscribers;

        // auto detect development mode
        if (
            this.mode &&
            programmaticOptions.jsc &&
            programmaticOptions.jsc.transform &&
            programmaticOptions.jsc.transform.react &&
            !Object.prototype.hasOwnProperty.call(
                programmaticOptions.jsc.transform.react,
                "development"
            )
        ) {
            programmaticOptions.jsc.transform.react.development =
                this.mode === "development";
        }

        if (programmaticOptions.sourceMaps === "inline") {
            // Babel has this weird behavior where if you set "inline", we
            // inline the sourcemap, and set 'result.map = null'. This results
            // in bad behavior from Babel since the maps get put into the code,
            // which Webpack does not expect, and because the map we return to
            // Webpack is null, which is also bad. To avoid that, we override the
            // behavior here so "inline" just behaves like 'true'.
            programmaticOptions.sourceMaps = true;
        }

        try {
            if (sync) {
                const output = swc.transformSync(source, programmaticOptions);
                callback(
                    null,
                    output.code,
                    parseMap ? JSON.parse(output.map) : output.map
                );
            } else {
                swc.transform(source, programmaticOptions).then(
                    (output) => {
                        callback(
                            null,
                            output.code,
                            parseMap ? JSON.parse(output.map) : output.map
                        );
                    },
                    (err) => {
                        callback(err);
                    }
                );
            }
        } catch (e) {
            callback(e);
        }
    };
}

module.exports = makeLoader();
module.exports.custom = makeLoader;
