"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (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 });
exports.Stylable = exports.validateDefaultConfig = void 0;
const create_stylable_processor_1 = require("./create-stylable-processor");
const diagnostics_1 = require("./diagnostics");
const parser_1 = require("./parser");
const stylable_processor_1 = require("./stylable-processor");
const stylable_resolver_1 = require("./stylable-resolver");
const stylable_transformer_1 = require("./stylable-transformer");
const module_resolver_1 = require("./module-resolver");
const features_1 = require("./features");
const visit_meta_css_dependencies_1 = require("./visit-meta-css-dependencies");
const postcss = __importStar(require("postcss"));
const deprecation_1 = require("./helpers/deprecation");
const feature_1 = require("./features/feature");
// This defines and validates known configs for the defaultConfig in 'stylable.config.js
const globalDefaultSupportedConfigs = new Set([
    'resolveModule',
    'resolveNamespace',
    'requireModule',
    'flags',
    'experimentalSelectorInference',
]);
function validateDefaultConfig(defaultConfigObj) {
    if (typeof defaultConfigObj === 'object') {
        for (const configName of Object.keys(defaultConfigObj)) {
            if (!globalDefaultSupportedConfigs.has(configName)) {
                console.warn(`Caution: loading "${configName}" config is experimental, and may behave unexpectedly`);
            }
        }
    }
}
exports.validateDefaultConfig = validateDefaultConfig;
class Stylable {
    constructor(config) {
        this.stModule = new features_1.STImport.StylablePublicApi(this);
        this.stScope = new features_1.STScope.StylablePublicApi(this);
        this.cssCustomProperty = new features_1.CSSCustomProperty.StylablePublicApi(this);
        this.stVar = new features_1.STVar.StylablePublicApi(this);
        this.stMixin = new features_1.STMixin.StylablePublicApi(this);
        this.cssClass = new features_1.CSSClass.StylablePublicApi(this);
        this.diagnostics = new diagnostics_1.Diagnostics();
        this.experimentalSelectorInference =
            config.experimentalSelectorInference === false ? false : true;
        if (this.experimentalSelectorInference === false) {
            (0, deprecation_1.warnOnce)('Stylable is running in a deprecated mode that will be removed in a future 6.x.x release. Please set experimentalSelectorInference=true to avoid this warning.');
        }
        this.projectRoot = config.projectRoot;
        this.fileSystem = config.fileSystem;
        this.requireModule =
            config.requireModule ||
                (() => {
                    throw new Error('Javascript files are not supported without Stylable `requireModule` option');
                });
        this.onProcess = config.onProcess;
        this.hooks = config.hooks || {};
        this.optimizer = config.optimizer;
        this.mode = config.mode || `production`;
        this.resolveNamespace = config.resolveNamespace;
        this.moduleResolver = this.initModuleResolver(config);
        this.cssParser = config.cssParser || parser_1.cssParse;
        this.resolverCache = config.resolverCache || new Map();
        this.fileProcessorCache = config.fileProcessorCache;
        this.flags = {
            ...feature_1.defaultFeatureFlags,
            ...config.flags,
        };
        this.fileProcessor = (0, create_stylable_processor_1.createStylableFileProcessor)({
            fileSystem: this.fileSystem,
            onProcess: this.onProcess,
            resolveNamespace: this.resolveNamespace,
            cssParser: this.cssParser,
            cache: this.fileProcessorCache,
            flags: this.flags,
        });
        this.resolver = this.createResolver();
    }
    initModuleResolver(config) {
        return typeof config.resolveModule === 'function'
            ? config.resolveModule
            : (0, module_resolver_1.createDefaultResolver)({
                fs: this
                    .fileSystem /* we force to provide resolveModule when using MinimalFS */,
                ...config.resolveModule,
            });
    }
    getDependencies(meta) {
        const dependencies = [];
        for (const dependency of (0, visit_meta_css_dependencies_1.visitMetaCSSDependencies)({ meta, resolver: this.resolver })) {
            dependencies.push(dependency);
        }
        return dependencies;
    }
    initCache({ filter } = {}) {
        if (filter && this.resolverCache) {
            for (const [key, cacheEntity] of this.resolverCache) {
                const keep = filter(key, cacheEntity);
                if (!keep) {
                    this.resolverCache.delete(key);
                }
            }
        }
        else {
            this.resolverCache = new Map();
            this.resolver = this.createResolver();
        }
    }
    createResolver({ requireModule = this.requireModule, resolverCache = this.resolverCache, resolvePath = this.moduleResolver, } = {}) {
        return new stylable_resolver_1.StylableResolver(this.fileProcessor, requireModule, resolvePath, resolverCache);
    }
    createProcessor({ resolveNamespace = this.resolveNamespace, } = {}) {
        return new stylable_processor_1.StylableProcessor(new diagnostics_1.Diagnostics(), resolveNamespace, this.flags);
    }
    createTransformer(options = {}) {
        return new stylable_transformer_1.StylableTransformer({
            moduleResolver: this.moduleResolver,
            diagnostics: new diagnostics_1.Diagnostics(),
            fileProcessor: this.fileProcessor,
            requireModule: this.requireModule,
            postProcessor: this.hooks.postProcessor,
            replaceValueHook: this.hooks.replaceValueHook,
            resolverCache: this.resolverCache,
            mode: this.mode,
            experimentalSelectorInference: this.experimentalSelectorInference,
            ...options,
        });
    }
    transform(pathOrMeta, options = {}) {
        const meta = typeof pathOrMeta === `string` ? this.analyze(pathOrMeta) : pathOrMeta;
        const transformer = this.createTransformer(options);
        return transformer.transform(meta);
    }
    transformSelector(pathOrMeta, selector, options) {
        const meta = typeof pathOrMeta === `string` ? this.analyze(pathOrMeta) : pathOrMeta;
        const transformer = this.createTransformer(options);
        const r = transformer.scopeSelector(meta, selector, undefined, undefined, undefined, true);
        return {
            selector: r.selector,
            resolved: r.elements,
        };
    }
    transformCustomProperty(pathOrMeta, prop) {
        const meta = typeof pathOrMeta === `string` ? this.analyze(pathOrMeta) : pathOrMeta;
        return features_1.CSSCustomProperty.scopeCSSVar(this.resolver, meta, prop);
    }
    transformDecl(pathOrMeta, prop, value, options) {
        const decl = postcss.decl({ prop, value });
        this.transformAST(pathOrMeta, postcss.root({}).append(postcss.rule({ selector: `.x` }).append(decl)), options);
        return { prop: decl.prop, value: decl.value };
    }
    transformAST(pathOrMeta, ast, options) {
        const meta = typeof pathOrMeta === `string` ? this.analyze(pathOrMeta) : pathOrMeta;
        const transformer = this.createTransformer(options);
        transformer.transformAst(ast, meta);
        return ast;
    }
    analyze(fullPath, overrideSrc) {
        return overrideSrc
            ? this.fileProcessor.processContent(overrideSrc, fullPath)
            : this.fileProcessor.process(fullPath);
    }
    resolvePath(directoryPath, request) {
        return this.resolver.resolvePath(directoryPath, request);
    }
}
exports.Stylable = Stylable;
//# sourceMappingURL=stylable.js.map