dota_ability_transformer
Version:
Adds a tstl transformer to build dota kv abilities from ts syntax.
921 lines (919 loc) • 33.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const ts = __importStar(require("typescript"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const node_modules = require("node_modules-path");
const valve_kv_1 = require("valve-kv");
const transformEnums_1 = require("dota_ability_transformer/transformEnums");
const GENERATED_FILE_NAME = "/generatedAbilities.kv";
const GENERATED_FILE_PATH = "generatedAbilities";
const BASE_NPC_ABILITY_FILE = `
#base "heroes/meepo.kv"
"DOTAAbilities"
{
}`;
const BASE_ABILITY_OBJECT = `
"DOTAAbilities"
{
}`;
// initially set to the default config
let configuration = {
modularization: "folder" /* Folder */,
debug: false,
strict: "warn" /* Warn */,
disable: false,
};
const abilityMap = new Map();
const curAbilities = new Map();
/**
* Is the given number in the string an integer or a float?
* @param num number string to check
* @returns true, if its an integer
*/
function isInt(num) {
const parsedNum = parseFloat(num);
if (parsedNum < 0)
return false;
return Number.isInteger(parsedNum);
}
/**
* Check if the given input is a valid number.
* @param val number or string represented number
* @returns true, if its a valid number
*/
function isNumber(val) {
if (typeof val === "number")
return true;
const num = parseFloat(val);
if (!num)
return false;
if (isNaN(num))
return false;
return isFinite(num);
}
/**
* Check if the given input is an array of valid numbers.
* Its assumed that all elements are the same type.
* @param arr input array
* @returns true, if its a number array
*/
function isNumberArr(arr) {
if (arr.length < 1)
return false;
return isNumber(arr[0]);
}
/**
* Print debug messages
* @param msg message to print
*/
function debugPrint(msg) {
if (configuration.debug)
console.log("> " + msg);
}
/**
* Custom error for this transfomer.
*/
class AbilityTransformerError {
constructor(message) {
const text = `\x1b[91m${message}\x1b[0m`;
const error = Error(text);
Object.defineProperties(error, {
message: {
get() {
return text;
},
},
name: {
get() {
return "AbilityTransformerError";
},
},
});
Error.captureStackTrace(error, AbilityTransformerError);
return error;
}
}
/**
* Get the path to the ability directory inside "scripts/npc"
* @returns ability path
*/
function getAbilityPath() {
const vPath = getTsConfig().output;
const abilityDir = path.resolve(vPath, "../npc");
if (!fs.existsSync(abilityDir))
throw new AbilityTransformerError("NPC script path not found");
const genPath = abilityDir + "/abilities";
if (!fs.existsSync(genPath))
fs.mkdirSync(genPath);
return genPath;
}
/**
* Get the file path of an ability based on the current node.
* @param node current node
* @returns current node file path (relative from "vscripts")
*/
function getCleanedFilePath(node) {
const rootPath = getTsConfig().rootDir;
const absPath = ts.getOriginalNode(node).getSourceFile().fileName;
const cleanedPath = absPath.substring(absPath.indexOf(rootPath) + rootPath.length + 1);
return cleanedPath.replace(".ts", "");
// const match = absPath.match(/.*vscripts[\/\\](.*)\.ts/);
// if (!match) throw new AbilityTransformerError("Invalid File Path: " + absPath);
// return match[1] + ".lua";
}
/**
* Get all currently generated abilities.
* @returns generated abilities object
*/
function getAllGeneratedAbilities() {
const abilityPath = getAbilityPath();
const abilityFilePath = abilityPath + GENERATED_FILE_NAME;
if (!fs.existsSync(abilityFilePath)) {
debugPrint("Failed to find " + GENERATED_FILE_NAME);
return;
}
return valve_kv_1.deserializeFile(abilityFilePath);
}
/**
* Get all currently generated abilities.
* @returns generated abilities object
*/
function getGeneratedAbilities(absPath) {
const abilityFilePath = getAbilityPathName(absPath);
if (!fs.existsSync(abilityFilePath)) {
debugPrint("Failed to find " + abilityFilePath);
return;
}
return valve_kv_1.deserializeFile(abilityFilePath);
}
/**
* Writes the information of this ability object to a file, based on current configuration.
* @param absPath orig file path (to determine modularization)
* @param abilityObject ability object to write
*/
function writeGeneratedAbilities(absPath, abilityObject) {
const abilityStr = valve_kv_1.serialize({ DOTAAbilities: abilityObject });
const abilityFilePath = getAbilityPathName(absPath);
debugPrint("Write to " + abilityFilePath);
fs.writeFileSync(abilityFilePath, abilityStr);
addBase(absPath);
}
/**
* Writes the information of this ability object to a file.
* Never uses modularization.
* @param abilityObject
*/
function writeAllGeneratedAbilities(abilityObject) {
const abilityStr = valve_kv_1.serialize({ DOTAAbilities: abilityObject });
const abilityPath = getAbilityPath() + GENERATED_FILE_NAME;
debugPrint("Write all abilities");
fs.writeFileSync(abilityPath, abilityStr);
}
/**
* Get the name of the module for this path. Only relevant if the modularization is not none.
* @param filePath path to get the module name
* @returns module name
*/
function getModuleName(filePath) {
switch (configuration.modularization) {
case "none" /* None */:
return "";
case "file" /* File */: {
const match = filePath.match(/(^.*[\/\\])?(\w+)(\.lua)?/);
return match ? match[2] : "root";
}
case "folder" /* Folder */: {
const match = filePath.match(/(^.*[\/\\])?(\w+)[\/\\]\w+(\.lua)?/);
return match ? match[2] : "root";
}
}
}
/**
* Get the final ability path to write an ability to.
* @param absPath source path
* @returns final ability path
*/
function getAbilityPathName(absPath) {
const abilityPath = getAbilityPath();
let abilityFilePath;
switch (configuration.modularization) {
case "none" /* None */:
abilityFilePath = abilityPath + GENERATED_FILE_NAME;
break;
case "file" /* File */:
case "folder" /* Folder */:
const moduleName = getModuleName(absPath);
abilityFilePath = path.resolve(abilityPath, GENERATED_FILE_PATH, moduleName + ".kv");
}
return abilityFilePath;
}
/**
* Add a new base to the generatedAbilities.kv file.
* Checks if the base already exists.
* @param absPath abs path of the ability
*/
function addBase(absPath) {
debugPrint("Check bases [Add]");
if (configuration.modularization === "none" /* None */)
return;
const moduleName = getModuleName(absPath);
const curBases = getBases();
if (curBases.includes(moduleName)) {
debugPrint("Base " + moduleName + " already included");
return;
}
writeBases([...curBases, moduleName]);
}
/**
* Remove a base from the generatedAbilities.kv file.
* Checks if the base actually exists.
* @param absPath abs path of the ability
*/
function removeBase(absPath) {
debugPrint("Check bases [Remove]");
if (configuration.modularization === "none" /* None */)
return;
const moduleName = getModuleName(absPath);
const curBases = getBases();
if (!curBases.includes(moduleName)) {
debugPrint("Base " + moduleName + " not included");
return;
}
writeBases(curBases.filter((base) => base !== moduleName));
}
/**
* Checks whether the generated ability kv is already included in the base abilities file.
* If not, it includes it.
*/
function checkAbilityBase() {
const abilityPath = getAbilityPath();
const baseAbilityFilePath = abilityPath + "/../npc_abilities_custom.txt";
debugPrint("Check if " + GENERATED_FILE_NAME + " is already included as base");
if (!fs.existsSync(baseAbilityFilePath)) {
fs.writeFileSync(baseAbilityFilePath, BASE_NPC_ABILITY_FILE);
}
else {
const baseAbilityFile = fs.readFileSync(baseAbilityFilePath).toString();
const regex = /^#base\s+["'](.*)["']/gm;
let match;
const includedFiles = [];
while ((match = regex.exec(baseAbilityFile)) !== null) {
if (!match)
continue;
includedFiles.push(match[1]);
}
if (!includedFiles.includes("abilities/generatedAbilities.kv")) {
fs.writeFileSync(baseAbilityFilePath, `#base "abilities/generatedAbilities.kv"\n${baseAbilityFile}`);
}
}
}
let initialized = false;
/**
* Check what abilities are currently defined already.
*/
function inititialize() {
var _a;
if (initialized === true)
return;
initialized = true;
getConfiguration();
if (configuration.disable === true)
return;
console.log("[Ability Transformer] Initialize...");
checkAbilityBase();
let origfileContent;
try {
origfileContent = getAllGeneratedAbilities();
}
catch (_b) {
debugPrint("Failed to read " + GENERATED_FILE_NAME);
}
let fileContent = {};
if (!origfileContent || Object.keys(origfileContent).length === 0) {
debugPrint(GENERATED_FILE_NAME + " is empty or not found");
const abilityPath = `${getAbilityPath()}${GENERATED_FILE_NAME}`;
fs.writeFileSync(abilityPath, BASE_ABILITY_OBJECT);
if (configuration.modularization !== "none" /* None */) {
const abilityBasePath = `${getAbilityPath()}/${GENERATED_FILE_PATH}`;
if (!fs.existsSync(abilityBasePath))
fs.mkdirSync(abilityBasePath);
}
console.log("\x1b[32m%s\x1b[0m", "[Ability Transformer] Initialization complete!\n");
return;
}
else {
fileContent = origfileContent.DOTAAbilities;
}
for (const [key, value] of Object.entries(fileContent)) {
const fileName = value["ScriptFile"];
if (!fileName)
continue;
let abilitySet = abilityMap.get(fileName);
if (!abilitySet)
abilitySet = new Set();
abilitySet.add(key);
abilityMap.set(fileName, abilitySet);
}
const bases = getBases();
// Adjust the bases to the current configuration
switch (configuration.modularization) {
case "none" /* None */: {
debugPrint("Switch to modularization: " + configuration.modularization);
if (bases.length > 0) {
writeAllGeneratedAbilities(fileContent);
}
const abilityBasePath = `${getAbilityPath()}/${GENERATED_FILE_PATH}`;
if (fs.existsSync(abilityBasePath)) {
fs.rmdirSync(abilityBasePath, { recursive: true });
}
break;
}
case "file" /* File */:
case "folder" /* Folder */:
debugPrint("Switch to modularization: " + configuration.modularization);
const abilityBasePath = `${getAbilityPath()}/${GENERATED_FILE_PATH}`;
if (!fs.existsSync(abilityBasePath)) {
fs.mkdirSync(abilityBasePath);
}
else {
fs.rmdirSync(abilityBasePath, { recursive: true });
fs.mkdirSync(abilityBasePath);
}
const moduleMap = new Map();
for (const [key, value] of Object.entries(fileContent)) {
const fileName = value["ScriptFile"];
if (!fileName)
continue;
const moduleName = getModuleName(fileName);
const curModules = (_a = moduleMap.get(moduleName)) !== null && _a !== void 0 ? _a : {};
curModules[key] = value;
moduleMap.set(moduleName, curModules);
}
debugPrint("Write new modularized ability files...");
const newBases = [];
moduleMap.forEach((value, key) => {
newBases.push(key);
const abilityStr = valve_kv_1.serialize({ DOTAAbilities: value });
const abilityPath = abilityBasePath + `/${key}.kv`;
fs.writeFileSync(abilityPath, abilityStr);
});
writeBases(newBases);
break;
}
console.log("\x1b[32m%s\x1b[0m", "[Ability Transformer] Initialization complete!\n");
}
/**
* Read the current configuration file or create a new one if none exists.
*/
function getConfiguration() {
const filePath = ".abilityTransformerrc.json";
if (!fs.existsSync(filePath)) {
return;
}
else {
const content = fs.readFileSync(filePath, "utf-8");
const newConfig = JSON.parse(content);
for (const [key, val] of Object.entries(newConfig)) {
configuration[key] = val;
}
}
debugPrint("Get current configuration");
}
/**
* Get all current bases for the modularization.
* @returns
*/
function getBases() {
debugPrint("Get current bases");
const abilityPath = getAbilityPath();
const abilityFilePath = abilityPath + GENERATED_FILE_NAME;
if (!fs.existsSync(abilityFilePath))
return [];
const content = fs.readFileSync(abilityFilePath, "utf-8");
const regex = /^#base\s+\".*?\/(.*).kv\"/gm;
let match;
const bases = [];
while ((match = regex.exec(content)) !== null) {
bases.push(match[1]);
}
return bases;
}
/**
* Write the bases for a .kv file.
* @param bases list of bases
*/
function writeBases(bases) {
let basesString = "";
for (const base of bases) {
basesString += `#base "${GENERATED_FILE_PATH}/${base}.kv"\n`;
}
basesString += BASE_ABILITY_OBJECT;
const abilityPath = `${getAbilityPath()}${GENERATED_FILE_NAME}`;
debugPrint("Refresh bases");
fs.writeFileSync(abilityPath, basesString);
}
/**
* Create the ability text from the given information and update the ability kvs.
* @param name name of the ability
* @param scriptFile scripts file path
* @param properties base properties of the the ability
* @param specials ability special values
*/
function writeAbility(ability) {
var _a;
debugPrint("Prepare write of ability");
const formattedSpecials = {};
for (let i = 0; i < ability.specials.length; i++) {
const special = ability.specials[i];
const index = (i + 1).toString().padStart(2, "0");
const formattedValue = Array.isArray(special.value) ? special.value.join(" ") : special.value.toString();
const otherFields = {};
for (const [name, val] of Object.entries(special)) {
if (name === "name" || name === "type" || name === "value")
continue;
otherFields[name] = val;
}
formattedSpecials[index] = Object.assign({ var_type: special.type, [special.name]: formattedValue }, otherFields);
}
const replacedProperties = {};
for (const [name, val] of Object.entries(ability.properties)) {
const replacedName = (_a = transformEnums_1.DifferentlyNamedAbilityKVs[name]) !== null && _a !== void 0 ? _a : name;
replacedProperties[replacedName] = val;
}
const kvAbility = Object.assign(Object.assign({ BaseClass: "ability_lua", ScriptFile: ability.scriptFile }, replacedProperties), ability.customProperties);
if (ability.specials.length > 0) {
kvAbility.AbilitySpecial = Object.assign({}, formattedSpecials);
}
const origfileContent = getGeneratedAbilities(ability.scriptFile);
let fileContent = {};
if (origfileContent) {
fileContent = origfileContent.DOTAAbilities;
}
fileContent[ability.name] = kvAbility;
writeGeneratedAbilities(ability.scriptFile, fileContent);
}
/**
* Remove an ability from the KV ability file.
* @param abilityName name of the ability
*/
function removeAbility(absPath, abilityName, remBase) {
debugPrint("Remove ability: " + abilityName);
const origfileContent = getGeneratedAbilities(absPath);
const abilityFilePath = getAbilityPathName(absPath);
let fileContent = {};
if (origfileContent) {
fileContent = origfileContent.DOTAAbilities;
}
delete fileContent[abilityName];
if (remBase)
removeBase(absPath);
if (Object.keys(fileContent).length === 0 && configuration.modularization !== "none" /* None */) {
fs.unlinkSync(abilityFilePath);
return;
}
const abilityStr = valve_kv_1.serialize({ DOTAAbilities: fileContent });
fs.writeFileSync(abilityFilePath, abilityStr);
}
/**
* Get the name and arguments of a decorator node.
* @param decorator decorator node to check
* @returns name and arguments (if any)
*/
function getDecoratorInfo(decorator) {
const exp = decorator.expression;
if (ts.isCallExpression(exp)) {
const args = exp.arguments;
const name = exp.expression.getText();
return { name, args };
}
return;
}
/**
* Get the name of a node.
* @param node node to check
* @returns name
*/
function getNodeName(node) {
var _a;
const nameNode = node.name;
if (!ts.isIdentifier(nameNode))
return "";
return (_a = nameNode.escapedText) !== null && _a !== void 0 ? _a : "";
}
/**
* Get the expression name of a node.
* @param node node to check
* @returns expression name
*/
function getNodeExpressionName(node) {
var _a;
const nameNode = node.expression;
if (!ts.isIdentifier(nameNode))
return "";
return (_a = nameNode.escapedText) !== null && _a !== void 0 ? _a : "";
}
/**
* Return the content of an object node as an object.
* @param node object node
* @returns object
*/
function getObjectNodeEntries(node) {
let entries = {};
for (const property of node.properties) {
if (!ts.isPropertyAssignment(property))
continue;
const name = getNodeName(property);
let value = "";
if (ts.isNumericLiteral(property.initializer) || ts.isPrefixUnaryExpression(property.initializer)) {
value = property.initializer.getText();
}
else if (ts.isStringLiteral(property.initializer)) {
value = property.initializer.text;
}
else if (ts.isArrayLiteralExpression(property.initializer)) {
value = [];
for (const elem of property.initializer.elements) {
if (ts.isNumericLiteral(elem) || ts.isPrefixUnaryExpression(elem)) {
value.push(elem.getText());
}
else if (ts.isStringLiteral(elem)) {
value.push(elem.text);
}
else if (ts.isIdentifier(elem)) {
value.push(elem.escapedText.toString());
}
}
}
else if (ts.isIdentifier(property.initializer)) {
value = property.initializer.escapedText.toString();
}
else if (property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
value = "1";
}
else if (property.initializer.kind === ts.SyntaxKind.FalseKeyword) {
value = "0";
}
else if (ts.isPropertyAccessExpression(property.initializer)) {
const name = getNodeName(property.initializer);
const expName = getNodeExpressionName(property.initializer);
switch (expName) {
case "LinkedSpecialBonusOperation":
value = transformEnums_1.LinkedSpecialBonusOperationNames[name];
break;
case "SpellImmunityTypes":
value = transformEnums_1.SpellImmunityTypesNames[name];
break;
case "SpellDispellableTypes":
value = transformEnums_1.SpellDispellableTypesNames[name];
break;
case "AbilityCastGestureSlotValue":
value = transformEnums_1.AbilityCastGestureSlotValueNames[name];
break;
}
}
entries[name] = value;
}
return entries;
}
/**
* Get the special values of an ability.
* @param node ability special values declaration node
* @returns ability special values
*/
function getSpecialValues(node) {
const initializer = node.initializer;
if (!initializer)
return [];
if (!ts.isObjectLiteralExpression(initializer))
return [];
const specials = [];
for (const property of initializer.properties) {
if (!ts.isPropertyAssignment(property))
continue;
const name = getNodeName(property);
if (ts.isNumericLiteral(property.initializer) ||
ts.isStringLiteral(property.initializer) ||
ts.isPrefixUnaryExpression(property.initializer)) {
const value = property.initializer.getText();
specials.push({
type: isInt(value) ? "FIELD_INTEGER" /* INTEGER */ : "FIELD_FLOAT" /* FLOAT */,
name,
value,
});
}
else if (ts.isArrayLiteralExpression(property.initializer)) {
const values = [];
let type = "FIELD_INTEGER" /* INTEGER */;
for (const elem of property.initializer.elements) {
if (ts.isNumericLiteral(elem) || ts.isStringLiteral(elem) || ts.isPrefixUnaryExpression(elem)) {
values.push(elem.getText());
if (!isInt(elem.getText()))
type = "FIELD_FLOAT" /* FLOAT */;
}
}
specials.push({
type,
name,
value: values,
});
}
else if (ts.isObjectLiteralExpression(property.initializer)) {
const entries = getObjectNodeEntries(property.initializer);
let value = "";
let type = "FIELD_INTEGER" /* INTEGER */;
if (entries["value"]) {
if (Array.isArray(entries["value"])) {
value = [];
for (const elem of entries["value"]) {
value.push(elem);
if (!isInt(elem))
type = "FIELD_FLOAT" /* FLOAT */;
}
}
else {
value = entries["value"];
if (!isInt(entries["value"]))
type = "FIELD_FLOAT" /* FLOAT */;
}
}
const otherEntries = {};
for (const [name, val] of Object.entries(entries)) {
if (name === "value")
continue;
otherEntries[name] = val;
}
specials.push(Object.assign({ type,
name,
value }, otherEntries));
}
}
return specials;
}
/**
* Get the base properties of an ability.
* @param node base property declaration node
* @returns base properties
*/
function getBaseProperties(node) {
const initializer = node.initializer;
if (!initializer)
return {};
if (!ts.isObjectLiteralExpression(initializer))
return {};
const properties = {};
for (const [name, val] of Object.entries(getObjectNodeEntries(initializer))) {
let value;
if (Array.isArray(val)) {
value = isNumberArr(val) ? val.join(" ") : val.join(" | ");
}
else {
if (val in transformEnums_1.DifferentlyNamesEnums) {
value = transformEnums_1.DifferentlyNamesEnums[val];
}
else if (transformEnums_1.NumericBaseProperties.includes(name) && !isNumber(val)) {
value = `%${val}`;
}
else {
value = val;
}
}
properties[name] = value;
}
return properties;
}
/**
* Get the custom properties of an ability (user added).
* @param node custom property declaration node
* @returns custom properties
*/
function getCustomProperties(node) {
const initializer = node.initializer;
if (!initializer)
return {};
if (!ts.isObjectLiteralExpression(initializer))
return {};
const properties = {};
for (const [name, val] of Object.entries(getObjectNodeEntries(initializer))) {
properties[name] = Array.isArray(val) ? val.join(" ") : val;
}
return properties;
}
/**
* Should this ability be skipped?
* Only do so if its true.
* @param node skip declaration node
* @returns true if ability should be skipped
*/
function getSkipValue(node) {
const initializer = node.initializer;
if (!initializer)
return false;
if (initializer.kind === ts.SyntaxKind.TrueKeyword)
return true;
return false;
}
/**
* Check if a node is an ability class and write it.
* @param node node to check
*/
function checkNode(node) {
if (ts.isClassDeclaration(node)) {
const decorators = node.decorators;
if (!decorators)
return;
for (const deco of decorators) {
const decoInfo = getDecoratorInfo(deco);
if (!decoInfo)
return;
if (decoInfo.name !== "registerAbility")
return;
}
if (!node.name)
return;
const name = node.name.escapedText.toString();
let values;
let props;
let customProps;
let skip = false;
node.forEachChild((child) => {
if (ts.isPropertyDeclaration(child)) {
const name = getNodeName(child);
if (name === transformEnums_1.ProtectedProperties.SpecialValues) {
values = getSpecialValues(child);
}
if (name === transformEnums_1.ProtectedProperties.BaseProperties) {
props = getBaseProperties(child);
}
if (name === transformEnums_1.ProtectedProperties.SkipAbility) {
skip = getSkipValue(child);
}
if (name === transformEnums_1.ProtectedProperties.CustomProperties) {
customProps = getCustomProperties(child);
}
}
});
const filePath = getCleanedFilePath(node);
if (!skip) {
const abilityList = curAbilities.get(filePath);
if (!abilityList)
return;
if (!props) {
if (configuration.strict === "warn" /* Warn */) {
console.log("\x1b[93m%s\x1b[0m", `[Ability Transformer] No properties for '${name}'. Skipping.`);
}
if (configuration.strict === "error" /* Error */) {
throw new AbilityTransformerError(`No properties for '${name}'. Aborting.`);
}
return;
}
abilityList.add({
name,
scriptFile: filePath,
properties: props !== null && props !== void 0 ? props : {},
specials: values !== null && values !== void 0 ? values : [],
customProperties: customProps !== null && customProps !== void 0 ? customProps : {},
});
}
else {
debugPrint("Skipped ability creation for: " + name);
}
}
}
function hasAbility(abilityName, abilities) {
let found = false;
abilities.forEach((ability) => {
if (ability.name === abilityName) {
found = true;
return;
}
});
return found;
}
function getAbilityFileCountByFolder(absPath) {
const curModuleName = getModuleName(absPath);
let count = 0;
for (const [filePath, names] of abilityMap) {
if (filePath === absPath)
continue;
const namesSet = names;
if (namesSet.size === 0)
continue;
const moduleName = getModuleName(filePath);
if (moduleName === curModuleName)
count++;
}
return count;
}
/**
* Check if this node should be deleted.
* @param node node to check
* @returns node. undefined if it has to be removed
*/
const removeNode = (node) => {
if (ts.isPropertyDeclaration(node)) {
const name = getNodeName(node);
if (name in transformEnums_1.ProtectedProperties)
return;
}
return node;
};
let curTSConfig;
/**
* Get the current tsconfig paths
* @returns
*/
function getTsConfig() {
if (!curTSConfig)
throw new AbilityTransformerError("TS configuration not found!");
return curTSConfig;
}
/**
* Read the current tsconfig file and load its paths
* @param program
* @returns
*/
function setTsConfig(program) {
if (curTSConfig)
return;
const configFilePath = program.getCompilerOptions().configFilePath;
const match = configFilePath.match(/(.*)[\/\\]tsconfig\.json/);
if (!match)
throw new AbilityTransformerError("No valid path for tsconfig file: ");
const configFileDir = match[1];
const configFileRaw = fs.readFileSync(configFilePath, "utf-8");
const configFile = JSON.parse(configFileRaw);
const rootDir = configFile.compilerOptions.rootDir
? path.resolve(configFileDir, configFile.compilerOptions.rootDir)
: configFileDir;
const outDir = configFile.compilerOptions.outDir
? path.resolve(configFileDir, configFile.compilerOptions.outDir)
: configFileDir;
curTSConfig = {
rootDir: rootDir.replace(/\\/g, "/"),
output: outDir.replace(/\\/g, "/"),
};
}
/**
* Creates the transformer.
*/
const createDotaTransformer = (program) => (context) => {
setTsConfig(program);
inititialize();
const visit = (node) => {
if (configuration.disable === true)
return node;
checkNode(node);
if (!removeNode(node))
return;
return ts.visitEachChild(node, visit, context);
};
return (file) => {
var _a;
const fileName = getCleanedFilePath(file);
let fileAbilities = (_a = abilityMap.get(fileName)) !== null && _a !== void 0 ? _a : new Set();
curAbilities.set(fileName, new Set());
const res = ts.visitNode(file, visit);
const curFileAbilities = curAbilities.get(fileName);
fileAbilities.forEach((abilityName) => {
if (!hasAbility(abilityName, curFileAbilities)) {
let remBase = false;
if (configuration.modularization !== "none" /* None */) {
remBase = curFileAbilities.size === 0;
}
if (configuration.modularization === "folder" /* Folder */ && remBase) {
const count = getAbilityFileCountByFolder(fileName);
remBase = count === 0;
}
removeAbility(fileName, abilityName, remBase);
}
});
fileAbilities = new Set();
curFileAbilities.forEach((ability) => {
writeAbility(ability);
fileAbilities.add(ability.name);
});
abilityMap.set(fileName, fileAbilities);
return res;
};
};
exports.default = createDotaTransformer;