13af6ab5fSopenharmony_ci/**
23af6ab5fSopenharmony_ci * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License.
53af6ab5fSopenharmony_ci * You may obtain a copy of the License at
63af6ab5fSopenharmony_ci *
73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
83af6ab5fSopenharmony_ci *
93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and
133af6ab5fSopenharmony_ci * limitations under the License.
143af6ab5fSopenharmony_ci */
153af6ab5fSopenharmony_ci
163af6ab5fSopenharmony_ci#include "arktsconfig.h"
173af6ab5fSopenharmony_ci#include "libpandabase/utils/json_builder.h"
183af6ab5fSopenharmony_ci#include "libpandabase/utils/json_parser.h"
193af6ab5fSopenharmony_ci#include "libpandabase/os/filesystem.h"
203af6ab5fSopenharmony_ci#include "util/language.h"
213af6ab5fSopenharmony_ci#include "generated/signatures.h"
223af6ab5fSopenharmony_ci
233af6ab5fSopenharmony_ci#include <fstream>
243af6ab5fSopenharmony_ci#include <regex>
253af6ab5fSopenharmony_ci#include <sstream>
263af6ab5fSopenharmony_ci#include <system_error>
273af6ab5fSopenharmony_ci
283af6ab5fSopenharmony_ci#ifndef ARKTSCONFIG_USE_FILESYSTEM
293af6ab5fSopenharmony_ci#include <dirent.h>
303af6ab5fSopenharmony_ci#include <sys/types.h>
313af6ab5fSopenharmony_ci#include <unistd.h>
323af6ab5fSopenharmony_ci#else
333af6ab5fSopenharmony_ci#if __has_include(<filesystem>)
343af6ab5fSopenharmony_ci#include <filesystem>
353af6ab5fSopenharmony_cinamespace fs = std::filesystem;
363af6ab5fSopenharmony_ci#elif __has_include(<experimental/filesystem>)
373af6ab5fSopenharmony_ci#include <experimental/filesystem>
383af6ab5fSopenharmony_cinamespace fs = std::experimental::filesystem;
393af6ab5fSopenharmony_ci#endif
403af6ab5fSopenharmony_ci#endif
413af6ab5fSopenharmony_ci
423af6ab5fSopenharmony_cinamespace ark::es2panda {
433af6ab5fSopenharmony_ci
443af6ab5fSopenharmony_citemplate <class... Ts>
453af6ab5fSopenharmony_cistatic bool Check(bool cond, const Ts &...msgs)
463af6ab5fSopenharmony_ci{
473af6ab5fSopenharmony_ci    if (!cond) {
483af6ab5fSopenharmony_ci        ((std::cerr << "ArkTsConfig error: ") << ... << msgs);
493af6ab5fSopenharmony_ci        return false;
503af6ab5fSopenharmony_ci    }
513af6ab5fSopenharmony_ci
523af6ab5fSopenharmony_ci    return true;
533af6ab5fSopenharmony_ci}
543af6ab5fSopenharmony_ci
553af6ab5fSopenharmony_cistatic bool IsAbsolute(const std::string &path)
563af6ab5fSopenharmony_ci{
573af6ab5fSopenharmony_ci#ifndef ARKTSCONFIG_USE_FILESYSTEM
583af6ab5fSopenharmony_ci    return !path.empty() && path[0] == '/';
593af6ab5fSopenharmony_ci#else
603af6ab5fSopenharmony_ci    return fs::path(path).is_absolute();
613af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
623af6ab5fSopenharmony_ci}
633af6ab5fSopenharmony_ci
643af6ab5fSopenharmony_cistd::string JoinPaths(const std::string &a, const std::string &b)
653af6ab5fSopenharmony_ci{
663af6ab5fSopenharmony_ci#ifndef ARKTSCONFIG_USE_FILESYSTEM
673af6ab5fSopenharmony_ci    return a + '/' + b;
683af6ab5fSopenharmony_ci#else
693af6ab5fSopenharmony_ci    return (fs::path(a) / b).string();
703af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
713af6ab5fSopenharmony_ci}
723af6ab5fSopenharmony_ci
733af6ab5fSopenharmony_cistd::string ParentPath(const std::string &path)
743af6ab5fSopenharmony_ci{
753af6ab5fSopenharmony_ci#ifndef ARKTSCONFIG_USE_FILESYSTEM
763af6ab5fSopenharmony_ci    auto pos = path.find('/');
773af6ab5fSopenharmony_ci    return pos == std::string::npos ? path : path.substr(0, pos);
783af6ab5fSopenharmony_ci#else
793af6ab5fSopenharmony_ci    return fs::path(path).parent_path().string();
803af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
813af6ab5fSopenharmony_ci}
823af6ab5fSopenharmony_ci
833af6ab5fSopenharmony_cistatic std::string MakeAbsolute(const std::string &path, const std::string &base)
843af6ab5fSopenharmony_ci{
853af6ab5fSopenharmony_ci    return IsAbsolute(path) ? path : JoinPaths(base, path);
863af6ab5fSopenharmony_ci}
873af6ab5fSopenharmony_ci
883af6ab5fSopenharmony_ci#ifdef ARKTSCONFIG_USE_FILESYSTEM
893af6ab5fSopenharmony_ci
903af6ab5fSopenharmony_ciArkTsConfig::Pattern::Pattern(std::string value, std::string base) : value_(std::move(value)), base_(std::move(base))
913af6ab5fSopenharmony_ci{
923af6ab5fSopenharmony_ci    ASSERT(fs::path(base_).is_absolute());
933af6ab5fSopenharmony_ci}
943af6ab5fSopenharmony_ci
953af6ab5fSopenharmony_cibool ArkTsConfig::Pattern::IsPattern() const
963af6ab5fSopenharmony_ci{
973af6ab5fSopenharmony_ci    return (value_.find('*') != std::string::npos) || (value_.find('?') != std::string::npos);
983af6ab5fSopenharmony_ci}
993af6ab5fSopenharmony_ci
1003af6ab5fSopenharmony_cistd::string ArkTsConfig::Pattern::GetSearchRoot() const
1013af6ab5fSopenharmony_ci{
1023af6ab5fSopenharmony_ci    fs::path relative;
1033af6ab5fSopenharmony_ci    if (!IsPattern()) {
1043af6ab5fSopenharmony_ci        relative = value_;
1053af6ab5fSopenharmony_ci    } else {
1063af6ab5fSopenharmony_ci        auto foundStar = value_.find_first_of('*');
1073af6ab5fSopenharmony_ci        auto foundQuestion = value_.find_first_of('?');
1083af6ab5fSopenharmony_ci        relative = value_.substr(0, std::min(foundStar, foundQuestion));
1093af6ab5fSopenharmony_ci        relative = relative.parent_path();
1103af6ab5fSopenharmony_ci    }
1113af6ab5fSopenharmony_ci    return MakeAbsolute(relative.string(), base_);
1123af6ab5fSopenharmony_ci}
1133af6ab5fSopenharmony_ci
1143af6ab5fSopenharmony_cibool ArkTsConfig::Pattern::Match(const std::string &path) const
1153af6ab5fSopenharmony_ci{
1163af6ab5fSopenharmony_ci    ASSERT(fs::path(path).is_absolute());
1173af6ab5fSopenharmony_ci    fs::path value = fs::path(value_);
1183af6ab5fSopenharmony_ci    std::string pattern = value.is_absolute() ? value.string() : (base_ / value).string();
1193af6ab5fSopenharmony_ci
1203af6ab5fSopenharmony_ci    // Replace arktsconfig special symbols with regular expressions
1213af6ab5fSopenharmony_ci    if (IsPattern()) {
1223af6ab5fSopenharmony_ci        // '**' matches any directory nested to any level
1233af6ab5fSopenharmony_ci        pattern = std::regex_replace(pattern, std::regex("\\*\\*/"), ".*");
1243af6ab5fSopenharmony_ci        // '*' matches zero or more characters (excluding directory separators)
1253af6ab5fSopenharmony_ci        pattern = std::regex_replace(pattern, std::regex("([^\\.])\\*"), "$1[^/]*");
1263af6ab5fSopenharmony_ci        // '?' matches any one character (excluding directory separators)
1273af6ab5fSopenharmony_ci        pattern = std::regex_replace(pattern, std::regex("\\?"), "[^/]");
1283af6ab5fSopenharmony_ci    }
1293af6ab5fSopenharmony_ci    if (!value.has_extension()) {
1303af6ab5fSopenharmony_ci        // default extensions to match
1313af6ab5fSopenharmony_ci        pattern += R"(.*(\.ts|\.d\.ts|\.sts)$)";
1323af6ab5fSopenharmony_ci    }
1333af6ab5fSopenharmony_ci    std::smatch m;
1343af6ab5fSopenharmony_ci    auto res = std::regex_match(path, m, std::regex(pattern));
1353af6ab5fSopenharmony_ci    return res;
1363af6ab5fSopenharmony_ci}
1373af6ab5fSopenharmony_ci
1383af6ab5fSopenharmony_cistatic std::string ResolveConfigLocation(const std::string &relPath, const std::string &base)
1393af6ab5fSopenharmony_ci{
1403af6ab5fSopenharmony_ci    auto resolvedPath = MakeAbsolute(relPath, base);
1413af6ab5fSopenharmony_ci    auto newBase = base;
1423af6ab5fSopenharmony_ci    while (!fs::exists(resolvedPath)) {
1433af6ab5fSopenharmony_ci        resolvedPath = MakeAbsolute(relPath, JoinPaths(newBase, "node_modules"));
1443af6ab5fSopenharmony_ci        if (newBase == ParentPath(newBase)) {
1453af6ab5fSopenharmony_ci            return "";
1463af6ab5fSopenharmony_ci        }
1473af6ab5fSopenharmony_ci        newBase = ParentPath(newBase);
1483af6ab5fSopenharmony_ci    }
1493af6ab5fSopenharmony_ci    return resolvedPath;
1503af6ab5fSopenharmony_ci}
1513af6ab5fSopenharmony_ci
1523af6ab5fSopenharmony_cibool ArkTsConfig::ParseExtends(const std::string &extends, const std::string &configDir)
1533af6ab5fSopenharmony_ci{
1543af6ab5fSopenharmony_ci    auto basePath = ResolveConfigLocation(extends, configDir);
1553af6ab5fSopenharmony_ci    if (!Check(!basePath.empty(), "Can't resolve config path: ", extends)) {
1563af6ab5fSopenharmony_ci        return false;
1573af6ab5fSopenharmony_ci    }
1583af6ab5fSopenharmony_ci
1593af6ab5fSopenharmony_ci    auto base = ArkTsConfig(basePath);
1603af6ab5fSopenharmony_ci    if (!Check(base.Parse(), "Failed to parse base config: ", extends)) {
1613af6ab5fSopenharmony_ci        return false;
1623af6ab5fSopenharmony_ci    }
1633af6ab5fSopenharmony_ci
1643af6ab5fSopenharmony_ci    Inherit(base);
1653af6ab5fSopenharmony_ci    return true;
1663af6ab5fSopenharmony_ci}
1673af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
1683af6ab5fSopenharmony_ci
1693af6ab5fSopenharmony_cistatic std::string ValidDynamicLanguages()
1703af6ab5fSopenharmony_ci{
1713af6ab5fSopenharmony_ci    JsonArrayBuilder builder;
1723af6ab5fSopenharmony_ci    for (auto &l : Language::All()) {
1733af6ab5fSopenharmony_ci        if (l.IsDynamic()) {
1743af6ab5fSopenharmony_ci            builder.Add(l.ToString());
1753af6ab5fSopenharmony_ci        }
1763af6ab5fSopenharmony_ci    }
1773af6ab5fSopenharmony_ci    return std::move(builder).Build();
1783af6ab5fSopenharmony_ci}
1793af6ab5fSopenharmony_ci
1803af6ab5fSopenharmony_citemplate <class PathsMap>
1813af6ab5fSopenharmony_cistatic bool ParsePaths(const JsonObject::JsonObjPointer *options, PathsMap &pathsMap, const std::string &baseUrl)
1823af6ab5fSopenharmony_ci{
1833af6ab5fSopenharmony_ci    if (options == nullptr) {
1843af6ab5fSopenharmony_ci        return true;
1853af6ab5fSopenharmony_ci    }
1863af6ab5fSopenharmony_ci
1873af6ab5fSopenharmony_ci    auto paths = options->get()->GetValue<JsonObject::JsonObjPointer>("paths");
1883af6ab5fSopenharmony_ci    if (paths == nullptr) {
1893af6ab5fSopenharmony_ci        return true;
1903af6ab5fSopenharmony_ci    }
1913af6ab5fSopenharmony_ci
1923af6ab5fSopenharmony_ci    for (size_t keyIdx = 0; keyIdx < paths->get()->GetSize(); ++keyIdx) {
1933af6ab5fSopenharmony_ci        auto &key = paths->get()->GetKeyByIndex(keyIdx);
1943af6ab5fSopenharmony_ci        if (pathsMap.count(key) == 0U) {
1953af6ab5fSopenharmony_ci            pathsMap.insert({key, {}});
1963af6ab5fSopenharmony_ci        }
1973af6ab5fSopenharmony_ci
1983af6ab5fSopenharmony_ci        auto values = paths->get()->GetValue<JsonObject::ArrayT>(key);
1993af6ab5fSopenharmony_ci        if (!Check(values, "Invalid value for 'path' with key '", key, "'")) {
2003af6ab5fSopenharmony_ci            return false;
2013af6ab5fSopenharmony_ci        }
2023af6ab5fSopenharmony_ci
2033af6ab5fSopenharmony_ci        if (!Check(!values->empty(), "Substitutions for pattern '", key, "' shouldn't be an empty array")) {
2043af6ab5fSopenharmony_ci            return false;
2053af6ab5fSopenharmony_ci        }
2063af6ab5fSopenharmony_ci
2073af6ab5fSopenharmony_ci        for (auto &v : *values) {
2083af6ab5fSopenharmony_ci            auto p = *v.Get<JsonObject::StringT>();
2093af6ab5fSopenharmony_ci            pathsMap[key].emplace_back(MakeAbsolute(p, baseUrl));
2103af6ab5fSopenharmony_ci        }
2113af6ab5fSopenharmony_ci    }
2123af6ab5fSopenharmony_ci
2133af6ab5fSopenharmony_ci    return true;
2143af6ab5fSopenharmony_ci}
2153af6ab5fSopenharmony_ci
2163af6ab5fSopenharmony_citemplate <class PathsMap>
2173af6ab5fSopenharmony_cistatic bool ParseDynamicPaths(const JsonObject::JsonObjPointer *options, PathsMap &dynamicPathsMap)
2183af6ab5fSopenharmony_ci{
2193af6ab5fSopenharmony_ci    static const std::string LANGUAGE = "language";
2203af6ab5fSopenharmony_ci    static const std::string HAS_DECL = "hasDecl";
2213af6ab5fSopenharmony_ci
2223af6ab5fSopenharmony_ci    if (options == nullptr) {
2233af6ab5fSopenharmony_ci        return true;
2243af6ab5fSopenharmony_ci    }
2253af6ab5fSopenharmony_ci
2263af6ab5fSopenharmony_ci    auto dynamicPaths = options->get()->GetValue<JsonObject::JsonObjPointer>("dynamicPaths");
2273af6ab5fSopenharmony_ci    if (dynamicPaths == nullptr) {
2283af6ab5fSopenharmony_ci        return true;
2293af6ab5fSopenharmony_ci    }
2303af6ab5fSopenharmony_ci
2313af6ab5fSopenharmony_ci    for (size_t keyIdx = 0; keyIdx < dynamicPaths->get()->GetSize(); ++keyIdx) {
2323af6ab5fSopenharmony_ci        auto &key = dynamicPaths->get()->GetKeyByIndex(keyIdx);
2333af6ab5fSopenharmony_ci        auto data = dynamicPaths->get()->GetValue<JsonObject::JsonObjPointer>(key);
2343af6ab5fSopenharmony_ci        if (!Check(data != nullptr, "Invalid value for for dynamic path with key '", key, "'")) {
2353af6ab5fSopenharmony_ci            return false;
2363af6ab5fSopenharmony_ci        }
2373af6ab5fSopenharmony_ci
2383af6ab5fSopenharmony_ci        auto langValue = data->get()->GetValue<JsonObject::StringT>(LANGUAGE);
2393af6ab5fSopenharmony_ci        if (!Check(langValue != nullptr, "Invalid '", LANGUAGE, "' value for dynamic path with key '", key, "'")) {
2403af6ab5fSopenharmony_ci            return false;
2413af6ab5fSopenharmony_ci        }
2423af6ab5fSopenharmony_ci
2433af6ab5fSopenharmony_ci        auto lang = Language::FromString(*langValue);
2443af6ab5fSopenharmony_ci        if (!Check(lang && lang->IsDynamic(), "Invalid '", LANGUAGE, "' value for dynamic path with key '", key,
2453af6ab5fSopenharmony_ci                   "'. Should be one of ", ValidDynamicLanguages())) {
2463af6ab5fSopenharmony_ci            return false;
2473af6ab5fSopenharmony_ci        }
2483af6ab5fSopenharmony_ci
2493af6ab5fSopenharmony_ci        if (!Check(compiler::Signatures::Dynamic::IsSupported(*lang), "Interoperability with language '",
2503af6ab5fSopenharmony_ci                   lang->ToString(), "' is not supported")) {
2513af6ab5fSopenharmony_ci            return false;
2523af6ab5fSopenharmony_ci        }
2533af6ab5fSopenharmony_ci
2543af6ab5fSopenharmony_ci        auto hasDeclValue = data->get()->GetValue<JsonObject::BoolT>(HAS_DECL);
2553af6ab5fSopenharmony_ci        if (!Check(hasDeclValue != nullptr, "Invalid '", HAS_DECL, "' value for dynamic path with key '", key, "'")) {
2563af6ab5fSopenharmony_ci            return false;
2573af6ab5fSopenharmony_ci        }
2583af6ab5fSopenharmony_ci
2593af6ab5fSopenharmony_ci        auto normalizedKey = ark::os::NormalizePath(key);
2603af6ab5fSopenharmony_ci        auto res = dynamicPathsMap.insert({normalizedKey, ArkTsConfig::DynamicImportData(*lang, *hasDeclValue)});
2613af6ab5fSopenharmony_ci        if (!Check(res.second, "Duplicated dynamic path '", key, "' for key '", key, "'")) {
2623af6ab5fSopenharmony_ci            return false;
2633af6ab5fSopenharmony_ci        }
2643af6ab5fSopenharmony_ci    }
2653af6ab5fSopenharmony_ci
2663af6ab5fSopenharmony_ci    return true;
2673af6ab5fSopenharmony_ci}
2683af6ab5fSopenharmony_ci
2693af6ab5fSopenharmony_citemplate <class Collection, class Function>
2703af6ab5fSopenharmony_cistatic bool ParseCollection(const JsonObject *config, Collection &out, const std::string &target,
2713af6ab5fSopenharmony_ci                            Function &&constructor)
2723af6ab5fSopenharmony_ci{
2733af6ab5fSopenharmony_ci    auto arr = config->GetValue<JsonObject::ArrayT>(target);
2743af6ab5fSopenharmony_ci    if (arr != nullptr) {
2753af6ab5fSopenharmony_ci        out = {};
2763af6ab5fSopenharmony_ci        if (!Check(!arr->empty(), "The '", target, "' list in config file is empty")) {
2773af6ab5fSopenharmony_ci            return false;
2783af6ab5fSopenharmony_ci        }
2793af6ab5fSopenharmony_ci
2803af6ab5fSopenharmony_ci        for (auto &i : *arr) {
2813af6ab5fSopenharmony_ci            out.emplace_back(constructor(*i.Get<JsonObject::StringT>()));
2823af6ab5fSopenharmony_ci        }
2833af6ab5fSopenharmony_ci    }
2843af6ab5fSopenharmony_ci
2853af6ab5fSopenharmony_ci    return true;
2863af6ab5fSopenharmony_ci}
2873af6ab5fSopenharmony_ci
2883af6ab5fSopenharmony_cistatic std::optional<std::string> ReadConfig(const std::string &path)
2893af6ab5fSopenharmony_ci{
2903af6ab5fSopenharmony_ci    std::ifstream inputStream(path);
2913af6ab5fSopenharmony_ci    if (!Check(!inputStream.fail(), "Failed to open file: ", path)) {
2923af6ab5fSopenharmony_ci        return {};
2933af6ab5fSopenharmony_ci    }
2943af6ab5fSopenharmony_ci
2953af6ab5fSopenharmony_ci    std::stringstream ss;
2963af6ab5fSopenharmony_ci    ss << inputStream.rdbuf();
2973af6ab5fSopenharmony_ci    return ss.str();
2983af6ab5fSopenharmony_ci}
2993af6ab5fSopenharmony_ci
3003af6ab5fSopenharmony_cistatic void ParseRelDir(std::string &dst, const std::string &key, const JsonObject::JsonObjPointer *options,
3013af6ab5fSopenharmony_ci                        const std::string &configDir)
3023af6ab5fSopenharmony_ci{
3033af6ab5fSopenharmony_ci    if (options != nullptr) {
3043af6ab5fSopenharmony_ci        auto path = options->get()->GetValue<JsonObject::StringT>(key);
3053af6ab5fSopenharmony_ci        dst = ((path != nullptr) ? *path : "");
3063af6ab5fSopenharmony_ci    }
3073af6ab5fSopenharmony_ci
3083af6ab5fSopenharmony_ci    dst = MakeAbsolute(dst, configDir);
3093af6ab5fSopenharmony_ci}
3103af6ab5fSopenharmony_ci
3113af6ab5fSopenharmony_cibool ArkTsConfig::Parse()
3123af6ab5fSopenharmony_ci{
3133af6ab5fSopenharmony_ci    static const std::string BASE_URL = "baseUrl";
3143af6ab5fSopenharmony_ci    static const std::string COMPILER_OPTIONS = "compilerOptions";
3153af6ab5fSopenharmony_ci    static const std::string EXCLUDE = "exclude";
3163af6ab5fSopenharmony_ci    static const std::string EXTENDS = "extends";
3173af6ab5fSopenharmony_ci    static const std::string FILES = "files";
3183af6ab5fSopenharmony_ci    static const std::string INCLUDE = "include";
3193af6ab5fSopenharmony_ci    static const std::string OUT_DIR = "outDir";
3203af6ab5fSopenharmony_ci    static const std::string ROOT_DIR = "rootDir";
3213af6ab5fSopenharmony_ci
3223af6ab5fSopenharmony_ci    ASSERT(!isParsed_);
3233af6ab5fSopenharmony_ci    isParsed_ = true;
3243af6ab5fSopenharmony_ci    auto arktsConfigDir = ParentPath(ark::os::GetAbsolutePath(configPath_));
3253af6ab5fSopenharmony_ci
3263af6ab5fSopenharmony_ci    // Read input
3273af6ab5fSopenharmony_ci    auto tsConfigSource = ReadConfig(configPath_);
3283af6ab5fSopenharmony_ci    if (!tsConfigSource) {
3293af6ab5fSopenharmony_ci        return false;
3303af6ab5fSopenharmony_ci    }
3313af6ab5fSopenharmony_ci
3323af6ab5fSopenharmony_ci    // Parse json
3333af6ab5fSopenharmony_ci    auto arktsConfig = std::make_unique<JsonObject>(*tsConfigSource);
3343af6ab5fSopenharmony_ci    if (!Check(arktsConfig->IsValid(), "ArkTsConfig is not valid json")) {
3353af6ab5fSopenharmony_ci        return false;
3363af6ab5fSopenharmony_ci    }
3373af6ab5fSopenharmony_ci
3383af6ab5fSopenharmony_ci#ifdef ARKTSCONFIG_USE_FILESYSTEM
3393af6ab5fSopenharmony_ci    // Parse "extends"
3403af6ab5fSopenharmony_ci    auto extends = arktsConfig->GetValue<JsonObject::StringT>(EXTENDS);
3413af6ab5fSopenharmony_ci    if (extends != nullptr && !ParseExtends(*extends, arktsConfigDir)) {
3423af6ab5fSopenharmony_ci        return false;
3433af6ab5fSopenharmony_ci    }
3443af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
3453af6ab5fSopenharmony_ci
3463af6ab5fSopenharmony_ci    auto compilerOptions = arktsConfig->GetValue<JsonObject::JsonObjPointer>(COMPILER_OPTIONS);
3473af6ab5fSopenharmony_ci
3483af6ab5fSopenharmony_ci    // Parse "baseUrl", "outDir", "rootDir"
3493af6ab5fSopenharmony_ci    ParseRelDir(baseUrl_, BASE_URL, compilerOptions, arktsConfigDir);
3503af6ab5fSopenharmony_ci    ParseRelDir(outDir_, OUT_DIR, compilerOptions, arktsConfigDir);
3513af6ab5fSopenharmony_ci    ParseRelDir(rootDir_, ROOT_DIR, compilerOptions, arktsConfigDir);
3523af6ab5fSopenharmony_ci
3533af6ab5fSopenharmony_ci    // Parse "paths"
3543af6ab5fSopenharmony_ci    if (!ParsePaths(compilerOptions, paths_, baseUrl_) || !ParseDynamicPaths(compilerOptions, dynamicPaths_)) {
3553af6ab5fSopenharmony_ci        return false;
3563af6ab5fSopenharmony_ci    }
3573af6ab5fSopenharmony_ci
3583af6ab5fSopenharmony_ci    // Parse "files"
3593af6ab5fSopenharmony_ci    auto concatPath = [&arktsConfigDir](const auto &val) { return MakeAbsolute(val, arktsConfigDir); };
3603af6ab5fSopenharmony_ci    if (!ParseCollection(arktsConfig.get(), files_, FILES, concatPath)) {
3613af6ab5fSopenharmony_ci        return false;
3623af6ab5fSopenharmony_ci    }
3633af6ab5fSopenharmony_ci
3643af6ab5fSopenharmony_ci#ifdef ARKTSCONFIG_USE_FILESYSTEM
3653af6ab5fSopenharmony_ci    // Parse "include" and "exclude"
3663af6ab5fSopenharmony_ci    auto consPattern = [&arktsConfigDir](const auto &val) { return Pattern {val, arktsConfigDir}; };
3673af6ab5fSopenharmony_ci    return ParseCollection(arktsConfig.get(), include_, INCLUDE, consPattern) &&
3683af6ab5fSopenharmony_ci           ParseCollection(arktsConfig.get(), exclude_, EXCLUDE, consPattern);
3693af6ab5fSopenharmony_ci#else
3703af6ab5fSopenharmony_ci    return true;
3713af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
3723af6ab5fSopenharmony_ci}
3733af6ab5fSopenharmony_ci
3743af6ab5fSopenharmony_civoid ArkTsConfig::Inherit(const ArkTsConfig &base)
3753af6ab5fSopenharmony_ci{
3763af6ab5fSopenharmony_ci    baseUrl_ = base.baseUrl_;
3773af6ab5fSopenharmony_ci    outDir_ = base.outDir_;
3783af6ab5fSopenharmony_ci    rootDir_ = base.rootDir_;
3793af6ab5fSopenharmony_ci    paths_ = base.paths_;
3803af6ab5fSopenharmony_ci    files_ = base.files_;
3813af6ab5fSopenharmony_ci#ifdef ARKTSCONFIG_USE_FILESYSTEM
3823af6ab5fSopenharmony_ci    include_ = base.include_;
3833af6ab5fSopenharmony_ci    exclude_ = base.exclude_;
3843af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
3853af6ab5fSopenharmony_ci}
3863af6ab5fSopenharmony_ci
3873af6ab5fSopenharmony_ci// Remove '/' and '*' from the end of path
3883af6ab5fSopenharmony_cistatic std::string TrimPath(const std::string &path)
3893af6ab5fSopenharmony_ci{
3903af6ab5fSopenharmony_ci    std::string trimmedPath = path;
3913af6ab5fSopenharmony_ci    while (!trimmedPath.empty() && (trimmedPath.back() == '*' || trimmedPath.back() == '/')) {
3923af6ab5fSopenharmony_ci        trimmedPath.pop_back();
3933af6ab5fSopenharmony_ci    }
3943af6ab5fSopenharmony_ci    return trimmedPath;
3953af6ab5fSopenharmony_ci}
3963af6ab5fSopenharmony_ci
3973af6ab5fSopenharmony_cistd::optional<std::string> ArkTsConfig::ResolvePath(const std::string &path) const
3983af6ab5fSopenharmony_ci{
3993af6ab5fSopenharmony_ci    for (const auto &[alias, paths] : paths_) {
4003af6ab5fSopenharmony_ci        auto trimmedAlias = TrimPath(alias);
4013af6ab5fSopenharmony_ci        size_t pos = path.rfind(trimmedAlias, 0);
4023af6ab5fSopenharmony_ci        if (pos == 0) {
4033af6ab5fSopenharmony_ci            std::string resolved = path;
4043af6ab5fSopenharmony_ci            // NOTE(ivagin): arktsconfig contains array of paths for each prefix, for now just get first one
4053af6ab5fSopenharmony_ci            std::string newPrefix = TrimPath(paths[0]);
4063af6ab5fSopenharmony_ci            resolved.replace(pos, trimmedAlias.length(), newPrefix);
4073af6ab5fSopenharmony_ci            return resolved;
4083af6ab5fSopenharmony_ci        }
4093af6ab5fSopenharmony_ci    }
4103af6ab5fSopenharmony_ci    return std::nullopt;
4113af6ab5fSopenharmony_ci}
4123af6ab5fSopenharmony_ci
4133af6ab5fSopenharmony_ci#ifdef ARKTSCONFIG_USE_FILESYSTEM
4143af6ab5fSopenharmony_cistatic bool MatchExcludes(const fs::path &path, const std::vector<ArkTsConfig::Pattern> &excludes)
4153af6ab5fSopenharmony_ci{
4163af6ab5fSopenharmony_ci    for (auto &e : excludes) {
4173af6ab5fSopenharmony_ci        if (e.Match(path.string())) {
4183af6ab5fSopenharmony_ci            return true;
4193af6ab5fSopenharmony_ci        }
4203af6ab5fSopenharmony_ci    }
4213af6ab5fSopenharmony_ci    return false;
4223af6ab5fSopenharmony_ci}
4233af6ab5fSopenharmony_ci
4243af6ab5fSopenharmony_cistatic std::vector<fs::path> GetSourceList(const std::shared_ptr<ArkTsConfig> &arktsConfig)
4253af6ab5fSopenharmony_ci{
4263af6ab5fSopenharmony_ci    auto includes = arktsConfig->Include();
4273af6ab5fSopenharmony_ci    auto excludes = arktsConfig->Exclude();
4283af6ab5fSopenharmony_ci    auto files = arktsConfig->Files();
4293af6ab5fSopenharmony_ci
4303af6ab5fSopenharmony_ci    // If "files" and "includes" are empty - include everything from tsconfig root
4313af6ab5fSopenharmony_ci    auto configDir = fs::absolute(fs::path(arktsConfig->ConfigPath())).parent_path();
4323af6ab5fSopenharmony_ci    if (files.empty() && includes.empty()) {
4333af6ab5fSopenharmony_ci        includes = {ArkTsConfig::Pattern("**/*", configDir.string())};
4343af6ab5fSopenharmony_ci    }
4353af6ab5fSopenharmony_ci    // If outDir in not default add it into exclude
4363af6ab5fSopenharmony_ci    if (!fs::equivalent(arktsConfig->OutDir(), configDir)) {
4373af6ab5fSopenharmony_ci        excludes.emplace_back("**/*", arktsConfig->OutDir());
4383af6ab5fSopenharmony_ci    }
4393af6ab5fSopenharmony_ci
4403af6ab5fSopenharmony_ci    // Collect "files"
4413af6ab5fSopenharmony_ci    std::vector<fs::path> sourceList;
4423af6ab5fSopenharmony_ci    for (auto &f : files) {
4433af6ab5fSopenharmony_ci        if (!Check(fs::exists(f) && fs::path(f).has_filename(), "No such file: ", f)) {
4443af6ab5fSopenharmony_ci            return {};
4453af6ab5fSopenharmony_ci        }
4463af6ab5fSopenharmony_ci
4473af6ab5fSopenharmony_ci        sourceList.emplace_back(f);
4483af6ab5fSopenharmony_ci    }
4493af6ab5fSopenharmony_ci
4503af6ab5fSopenharmony_ci    // Collect "include"
4513af6ab5fSopenharmony_ci    // TSC traverses folders for sources starting from 'include' rather than from 'rootDir', so we do the same
4523af6ab5fSopenharmony_ci    for (auto &include : includes) {
4533af6ab5fSopenharmony_ci        auto traverseRoot = fs::path(include.GetSearchRoot());
4543af6ab5fSopenharmony_ci        if (!fs::exists(traverseRoot)) {
4553af6ab5fSopenharmony_ci            continue;
4563af6ab5fSopenharmony_ci        }
4573af6ab5fSopenharmony_ci        if (!fs::is_directory(traverseRoot)) {
4583af6ab5fSopenharmony_ci            if (include.Match(traverseRoot.string()) && !MatchExcludes(traverseRoot, excludes)) {
4593af6ab5fSopenharmony_ci                sourceList.emplace_back(traverseRoot);
4603af6ab5fSopenharmony_ci            }
4613af6ab5fSopenharmony_ci            continue;
4623af6ab5fSopenharmony_ci        }
4633af6ab5fSopenharmony_ci        for (const auto &dirEntry : fs::recursive_directory_iterator(traverseRoot)) {
4643af6ab5fSopenharmony_ci            if (include.Match(dirEntry.path().string()) && !MatchExcludes(dirEntry, excludes)) {
4653af6ab5fSopenharmony_ci                sourceList.emplace_back(dirEntry);
4663af6ab5fSopenharmony_ci            }
4673af6ab5fSopenharmony_ci        }
4683af6ab5fSopenharmony_ci    }
4693af6ab5fSopenharmony_ci    return sourceList;
4703af6ab5fSopenharmony_ci}
4713af6ab5fSopenharmony_ci
4723af6ab5fSopenharmony_ci// Analogue of 'std::filesystem::relative()'
4733af6ab5fSopenharmony_ci// Example: Relative("/a/b/c", "/a/b") returns "c"
4743af6ab5fSopenharmony_cistatic fs::path Relative(const fs::path &src, const fs::path &base)
4753af6ab5fSopenharmony_ci{
4763af6ab5fSopenharmony_ci    fs::path tmpPath = src;
4773af6ab5fSopenharmony_ci    fs::path relPath;
4783af6ab5fSopenharmony_ci    while (!fs::equivalent(tmpPath, base)) {
4793af6ab5fSopenharmony_ci        relPath = relPath.empty() ? tmpPath.filename() : tmpPath.filename() / relPath;
4803af6ab5fSopenharmony_ci        if (tmpPath == tmpPath.parent_path()) {
4813af6ab5fSopenharmony_ci            return fs::path();
4823af6ab5fSopenharmony_ci        }
4833af6ab5fSopenharmony_ci        tmpPath = tmpPath.parent_path();
4843af6ab5fSopenharmony_ci    }
4853af6ab5fSopenharmony_ci    return relPath;
4863af6ab5fSopenharmony_ci}
4873af6ab5fSopenharmony_ci
4883af6ab5fSopenharmony_ci// Compute path to destination file and create subfolders
4893af6ab5fSopenharmony_cistatic fs::path ComputeDestination(const fs::path &src, const fs::path &rootDir, const fs::path &outDir)
4903af6ab5fSopenharmony_ci{
4913af6ab5fSopenharmony_ci    auto rel = Relative(src, rootDir);
4923af6ab5fSopenharmony_ci    if (!Check(!rel.empty(), rootDir, " is not root directory for ", src)) {
4933af6ab5fSopenharmony_ci        return {};
4943af6ab5fSopenharmony_ci    }
4953af6ab5fSopenharmony_ci
4963af6ab5fSopenharmony_ci    auto dst = outDir / rel;
4973af6ab5fSopenharmony_ci    fs::create_directories(dst.parent_path());
4983af6ab5fSopenharmony_ci    return dst.replace_extension("abc");
4993af6ab5fSopenharmony_ci}
5003af6ab5fSopenharmony_ci
5013af6ab5fSopenharmony_cistd::vector<std::pair<std::string, std::string>> FindProjectSources(const std::shared_ptr<ArkTsConfig> &arktsConfig)
5023af6ab5fSopenharmony_ci{
5033af6ab5fSopenharmony_ci    auto sourceFiles = GetSourceList(arktsConfig);
5043af6ab5fSopenharmony_ci    std::vector<std::pair<std::string, std::string>> compilationList;
5053af6ab5fSopenharmony_ci    for (auto &src : sourceFiles) {
5063af6ab5fSopenharmony_ci        auto dst = ComputeDestination(src, arktsConfig->RootDir(), arktsConfig->OutDir());
5073af6ab5fSopenharmony_ci        if (!Check(!dst.empty(), "Invalid destination file")) {
5083af6ab5fSopenharmony_ci            return {};
5093af6ab5fSopenharmony_ci        }
5103af6ab5fSopenharmony_ci
5113af6ab5fSopenharmony_ci        compilationList.emplace_back(src.string(), dst.string());
5123af6ab5fSopenharmony_ci    }
5133af6ab5fSopenharmony_ci
5143af6ab5fSopenharmony_ci    return compilationList;
5153af6ab5fSopenharmony_ci}
5163af6ab5fSopenharmony_ci#else
5173af6ab5fSopenharmony_cistd::vector<std::pair<std::string, std::string>> FindProjectSources(
5183af6ab5fSopenharmony_ci    [[maybe_unused]] const std::shared_ptr<ArkTsConfig> &arkts_config)
5193af6ab5fSopenharmony_ci{
5203af6ab5fSopenharmony_ci    ASSERT(false);
5213af6ab5fSopenharmony_ci    return {};
5223af6ab5fSopenharmony_ci}
5233af6ab5fSopenharmony_ci#endif  // ARKTSCONFIG_USE_FILESYSTEM
5243af6ab5fSopenharmony_ci
5253af6ab5fSopenharmony_ci}  // namespace ark::es2panda
526