/**
* @file core/source.js
* @copyright @spmhome @_2025
* @author Scott Meesseman @spmeesseman
*//** */
const WpwBase = require("./base");
const WpwTscService = require("../services/tsc");
const { isWpwSourceCodeLanguage } = require("../utils");
/**
* @class WpwSourceCode
*/
class WpwSourceCode extends WpwBase
{
/**
* @private
* @type {boolean}
*/
_autoConfig;
/**
* @private
* @type {Required<IWpwSourceCodeConfig>}
*/
_config;
/**
* @private
* @type {boolean}
*/
_defaultExclude;
/**
* @private
* @type {WpwTscService}
*/
_tsc;
/**
* @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} options
* @param {WpwBuild} build
*/
constructor(options, build)
{
super(options);
this.build = build;
this.logger = build.logger;
};
get autoConfig() { return this._autoConfig === true; }
/** @private */set autoConfig(v) { this._autoConfig = v; }
get config() { return this._config; }
get configFile() { return this._info.file; }
get compilerOptions() { return this._tsconfig?.compilerOptions || {}; }
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 tsc() { return this._tsc; }
get language() { return this._lng; }
/**
* @override
*/
dispose() {}
/**
* @param {string} lPad
*/
async init(lPad)
{
const b = this.build,
l = this.logger.write("configure sourcecode service", 1, lPad);
await this.setLanguage(lPad);
this._config = this._obj_.apply(this._config || {}, { language: this._lng, ext: this._ext }, this.initialConfig);
this._tsconfig = { compilerOptions: { allowJs: this.isJs || this.isJsTs }, files: [], include: [], exclude: [] };
this._info = {
dir: ".", file: "tsconfig.json", pathRel: "./tsconfig.json",
path: "./tsconfig.json", language: this._lng, ext: this._ext, ide: "visualstudiocode-ms"
};
l.value(" rc.config.configFile", this._config.configFile, 2, lPad);
l.value(" has rc.config.tsconfig", !!this._config.tsconfig, 2, lPad);
l.value(" has rc.config.compilerOptions", !!this._config.compilerOptions, 2, lPad);
if (!b.isTranspiled) {
b.logger.write(` tsc service not required for '${b.type}' build `, 1, lPad);
}
else
{ l.write(" create tsc service", 1, lPad);
this._tsc = new WpwTscService({ build: this.build });
const scCfgInfo = await this._tsc.init(this._config, lPad + " ");
this._obj_.merge(this._info, scCfgInfo);
if (this._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";
}
}
l.value(" source language", this._lng, 1, lPad);
l.value(" source default extension", this.ext, 1, lPad);
l.success("sourcecode service ready", 1, lPad);
}
/**
* @private
* @param {string} lPad
*/
async setLanguage(lPad)
{
const config = this.initialConfig,
l = this.logger.write("determine language type", 1, lPad);
if (!this.build.isTranspiled)
{
this._lng = "none";
}
if (!config.language || !isWpwSourceCodeLanguage(config.language))
{
const src = this.build.getSrcPath();
l.value(" sourcecode root directory", src, 1, lPad);
const o = {
cwd: src, maxDepth: 6, nodir: true,
ignore: [ "**/*.d.ts", "**/model/**", "**/types/**", "**/typings/**/*", "**/node_modules/**" ]
};
const findFiles = this._fs_.findFiles,
[ 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(" # 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(`project language initially set to ${this._lng } [${this._ext}]`, 1, lPad);
}
}
module.exports = WpwSourceCode;