services_tsc_language.js

/**
 * @file core/source.js
 * @copyright @spmhome @_2025
 * @author Scott Meesseman @spmeesseman
 *//** */

const { apply, pick } = require("@spmhome/type-utils");
const { isWpwSourceCodeLanguage } = require("../../utils");
const { findFiles } = require("@spmhome/cmn-utils");


class WpwSourceInfo
{
    /**
     * @private
     * @type {boolean}
     */
    _autoConfig;
    /**
     * @private
     * @type {Pick<IWpwSourceCodeConfig, "ext" | "ide" | "language">}
     */
    _config;
    /**
     * @private
     * @type {WpwSourceCodeInfo}
     */
    _info;
    /**
     * @private
     * @type {WpwSourceCodeExtension}
     */
    _ext;
    /**
     * @private
     * @type {WpwSourceCodeLanguage}
     */
    _lng;
    /**
     * @private
     * @readonly
     * @type {WpwSourceCodeLanguage[]}
     */
    _jsTypes = [
        "javascript", "react.javascript", "expo.javascript", "reactnative"
    ];
    /**
     * @private
     * @readonly
     * @type {WpwSourceCodeLanguage[]}
     */
    _tsTypes = [ "typescript", "react.typescript", "expo.typescript" ];
    /**
     * @private
     * @readonly
     * @type {WpwSourceCodeLanguage[]}
     */
    _jstsTypes = [ "typescript-javascript", "javascript-typescript" ];
    /**
     * @private
     * @readonly
     * @type {WpwSourceCodeLanguage[]}
     */
    _tsjsTypes = [ "typescript-javascript" ];


    /**
     * @param {WpwSourceCodeConfig} config
	 * @param {WpwBuild} build
     */
    constructor(config, build)
    {
        this.build = build;
        this.logger = build.logger;
        this.initialConfig = apply({}, pick(config, "ext", "ide", "language"));
    };


    get autoConfig() { return this._autoConfig === true; }
    /** @private */set autoConfig(v) { this._autoConfig = v; }
    get config() { return this._config; }
    get dotext() { return /** @type {WpwSourceDotExtensionApp} */(`.${this.ext}`); }
    get defaultExclude() { return this._defaultExclude === true; }
    /** @private */set defaultExclude(v) { this._defaultExclude = v; }
    get ext() { return this._ext; };
    get ide() { return this._info.ide; }
    get info() { return this._info; }
    get isJs() { return this._jsTypes.includes(this._lng); }
    get isJsTs() { return this._jstsTypes.includes(this._lng); }
    get isTs() { return this._tsTypes.includes(this._lng); }
    get isTsJs() { return this._tsjsTypes.includes(this._lng); }
    get language() { return this._lng; }


    /**
	 * @param {string} lPad
     */
    async init(lPad)
    {
        const config = this.initialConfig,
              l = this.logger.write("determine language type", 1, lPad);

        if (!this.build.isTranspiled)
        {
            this._ext = "js";
            this._lng =  "none";
        }
        else if (this.build.tsc.compilerOptions.jsx?.startsWith("react") && !this._lng.startsWith("react"))
        {   this._lng = this._info.language = this._lng.includes("typescript") ? "react.typescript" : "react.javascript";
            this._ext = this._info.ext = this._lng === "react.typescript" ? "tsx" : "jsx";
        }
        else if (!config.language || !isWpwSourceCodeLanguage(config.language))
        {
            const src = this.build.getSrcPath();
            const o = {
                cwd: src, maxDepth: 6, nodir: true,
                ignore: [ "**/*.d.ts", "**/model/**", "**/types/**", "**/typings/**/*", "**/node_modules/**" ]
            };

            const [ js, ts, jsx, tsx ] = (
                await Promise.all([
                    findFiles("**/*.js", o), findFiles("**/*.ts", o), findFiles("**/*.jsx", o), findFiles("**/*.tsx", o)
                ])
            ).map((f) => f.length);

            l.value("   root source directory", src, 1, lPad);
            l.value("   # of javascript [.js] files found", js, 2, lPad);
            l.value("   # of typescript [.ts] files found", ts, 2, lPad);
            l.value("   # of javascript [.jsx] files found", jsx, 2, lPad);
            l.value("   # of typescript [.tsx] files found", tsx, 2, lPad);

            if (js > 0)
            {   if (ts === 0 && tsx === 0) {
                    this._ext = "js";
                    this._lng = jsx === 0 ? "javascript" : "react.javascript";
                }
                else {
                    this._ext = "ts";
                    this._lng = js >= ts ? "javascript-typescript" : "typescript-javascript";
                }
            }
            else if (ts > 0)
            {   if (jsx === 0) {
                    this._ext = "ts";
                    this._lng = tsx === 0 ? "typescript" : "react.typescript";
                }
                else {
                    this._ext = "ts";
                    this._lng = "typescript-javascript";
                }
            }
            else if (jsx > tsx)
            {   this._ext = "jsx";
                this._lng = "react.javascript";
            }
            else if (tsx > jsx)
            {   this._ext = "tsx";
                this._lng = "react.typescript";
            }
            else
            {   this._ext = "js";
                this._lng = "javascript";
            }
        }
        else {
            this._lng = config.language;
        }

        if (!this._ext) {
            this._ext = this.isTs || this.isTsJs ? "ts" :
                        (this.isJs || this.isJsTs ? "js" : (this._lng.includes("javascript") ? "jsx": "tsx"));
        }

        l.write(`source language set to ${this._lng } [${this._ext}]`, 1, lPad);
    }


    update()
    {
        if (this.build.tsc.compilerOptions.jsx?.startsWith("react") && !this._lng.startsWith("react"))
        {   this._lng = this._info.language = this._lng.includes("typescript") ? "react.typescript" : "react.javascript";
            this._ext = this._info.ext = this._lng === "react.typescript" ? "tsx" : "jsx";
        }
        if (!this._ext) {
            this._ext = this.isTs || this.isTsJs ? "ts" :
                        (this.isJs || this.isJsTs ? "js" : (this._lng.includes("javascript") ? "jsx": "tsx"));
        }
    }
}


module.exports = WpwSourceInfo;