UNPKG

@wc-toolkit/cem-inheritance

Version:

A tool for mapping inherited content (including class members, attributes, CSS parts, CSS variables, slots, and events) from parent web component classes in the Custom Elements Manifest

488 lines (480 loc) 15.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { cemInheritancePlugin: () => cemInheritancePlugin, generateUpdatedCem: () => generateUpdatedCem, updateCemInheritance: () => updateCemInheritance }); module.exports = __toCommonJS(index_exports); // node_modules/.pnpm/@wc-toolkit+cem-utilities@1.2.0/node_modules/@wc-toolkit/cem-utilities/dist/index.js var definitionExports = /* @__PURE__ */ new Map(); var components = []; var manifest; function getAllComponents(customElementsManifest, exclude = []) { if (areObjectsEqual(customElementsManifest, manifest)) { return components; } resetCache(); manifest = customElementsManifest; getAllDefinitionExports(customElementsManifest); manifest.modules.forEach((module2) => { const ces = module2.declarations?.filter( (d) => d.customElement ); if (ces?.length) { ces.forEach((ce) => { if (exclude?.includes(ce.name)) { return; } ce.modulePath = module2.path; ce.definitionPath = definitionExports.get(ce.name); if ("typeDefinitionPath" in module2 && module2.typeDefinitionPath) { ce.typeDefinitionPath = module2.typeDefinitionPath; } components.push(ce); }); } }); return components; } function resetCache() { components = []; manifest = void 0; definitionExports.clear(); } function getAllMixins(customElementsManifest, exclude = []) { return (customElementsManifest.modules?.map( (mod) => mod.declarations?.filter((d) => d.kind === "mixin")?.flat() )?.flat() || []).filter((x) => x && !exclude?.includes(x.name)); } function getAllDefinitionExports(customElementsManifest) { customElementsManifest.modules.forEach((mod) => { const defExports = mod?.exports?.filter( (e) => e.kind === "custom-element-definition" ); if (defExports?.length) { defExports.forEach((e) => { if (e.declaration.name) { definitionExports.set(e.declaration.name, mod.path); } }); } }); } function areObjectsEqual(obj1, obj2) { if (obj1 === obj2) return true; if (obj1 === null || obj2 === null || typeof obj1 !== "object" || typeof obj2 !== "object") return false; if (Array.isArray(obj1) && Array.isArray(obj2)) { if (obj1.length !== obj2.length) return false; return obj1.every((item, index) => areObjectsEqual(item, obj2[index])); } const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) return false; if (!keys2.every((key) => key in obj1)) return false; return keys1.every((key) => { const val1 = obj1[key]; const val2 = obj2[key]; if (val1 === null && val2 === null) return true; if (val1 === null || val2 === null) return false; if (typeof val1 === "object" && typeof val2 === "object") { return areObjectsEqual(val1, val2); } return val1 === val2; }); } function deepMerge(target, source) { if (typeof target !== "object" || target === null) { return source; } if (typeof source !== "object" || source === null) { return target; } const targetObj = target; const sourceObj = source; for (const key of Object.keys(source)) { if (sourceObj[key] instanceof Array) { if (!targetObj[key]) { targetObj[key] = []; } targetObj[key] = targetObj[key].concat(sourceObj[key]); } else if (sourceObj[key] instanceof Object) { if (!targetObj[key]) { targetObj[key] = {}; } targetObj[key] = deepMerge(targetObj[key], sourceObj[key]); } else { targetObj[key] = sourceObj[key]; } } return targetObj; } // src/logger.ts var Logger = class { #debug; constructor(debug = false) { this.#debug = debug; } log(message, color = "\x1B[30m%s\x1B[0m") { if (!this.#debug) { return; } console.log(color, message); } red(message) { this.log(message, "\x1B[31m%s\x1B[0m"); } green(message) { this.log(message, "\x1B[32m%s\x1B[0m"); } yellow(message) { this.log(message, "\x1B[33m%s\x1B[0m"); } blue(message) { this.log(message, "\x1B[34m%s\x1B[0m"); } magenta(message) { this.log(message, "\x1B[35m%s\x1B[0m"); } cyan(message) { this.log(message, "\x1B[36m%s\x1B[0m"); } }; // src/utilities.ts var import_fs = __toESM(require("fs"), 1); var import_path = __toESM(require("path"), 1); function createOutDir(outDir) { if (outDir !== "./" && !import_fs.default.existsSync(outDir)) { import_fs.default.mkdirSync(outDir, { recursive: true }); } } function saveFile(outDir, fileName, contents) { const outputPath = import_path.default.join(outDir, fileName); import_fs.default.writeFileSync(outputPath, JSON.stringify(contents, null, 2)); return outputPath; } // src/default-values.ts var defaultOmittedProperties = { cssProperties: "omitCssProps", cssParts: "omitCssParts", cssStates: "omitCssStates", methods: "omitMethods", attributes: "omit", properties: "omit", events: "omitEvents", slots: "omitSlots" }; var defaultUserConfig = { fileName: "custom-elements.json", outdir: "./", exclude: [], externalManifests: [], omitByConfig: {}, omitByProperty: defaultOmittedProperties }; // src/inheritance.ts var completedClasses = /* @__PURE__ */ new Set(); var classQueue = []; var cemEntities = []; var externalComponents = []; var externalMixins = []; var updatedCEM = {}; var log; var userConfig = defaultUserConfig; function updateCemInheritance(cem, options = {}) { log = new Logger(options.debug); if (options.skip) { log.yellow("[cem-inheritance] - Skipped"); return; } log.log("[cem-inheritance] - Updating Custom Elements Manifest..."); const newCem = generateUpdatedCem(cem, options); if (!options.usedByPlugin) { createOutDir(userConfig.outdir); saveFile( userConfig.outdir, userConfig.fileName, JSON.stringify(newCem, null, 2) ); } log.green("[cem-inheritance] - Custom Elements Manifest updated."); } function updateOptions(options = {}) { return deepMerge(userConfig, options); } function setExternalManifests(manifests) { if (!manifests?.length) { return; } const combinedManifests = { modules: manifests.flatMap((manifest2) => manifest2.modules) }; externalComponents = getDeclarations(combinedManifests); externalMixins = getAllMixins(combinedManifests); } function createComponentMap(components2) { const map = /* @__PURE__ */ new Map(); components2.forEach((component) => { map.set(component.name, component); }); return map; } function generateUpdatedCem(cem, options = {}) { if (!cem) { throw new Error( "Custom Elements Manifest is required to update inheritance." ); } const originalCEM = structuredClone(cem); updatedCEM = originalCEM; userConfig = options.usedByPlugin ? options : updateOptions(options); setExternalManifests(userConfig.externalManifests); if (userConfig.includeExternalManifests && userConfig.externalManifests?.length) { updatedCEM = mergeExternalManifests(updatedCEM, userConfig.externalManifests); } cemEntities = getDeclarations(originalCEM, userConfig.exclude); const cemMap = createComponentMap(cemEntities); const externalMap = createComponentMap(externalComponents); cemEntities.forEach((component) => { getAncestors(component, cemMap, externalMap); }); processInheritanceQueue(cemMap, externalMap); return updatedCEM; } function getAncestors(component, cemMap, externalMap) { if (!component || completedClasses.has(component.name)) { return; } classQueue.push(component); if (component.superclass?.name && !completedClasses.has(component.superclass.name)) { const parent = cemMap?.get(component.superclass.name) || externalMap?.get(component.superclass.name); getAncestors(parent, cemMap, externalMap); } } function processInheritanceQueue(cemMap, externalMap) { if (classQueue.length === 0) { return; } try { while (classQueue.length > 0) { const component = classQueue.pop(); if (!component) continue; const parent = cemMap?.get(component.superclass?.name || "") || externalMap?.get(component.superclass?.name || ""); if (parent) { Object.keys(defaultUserConfig.omitByProperty).forEach((key) => { const componentApi = key; const omit = getOmittedProperties(component, parent, componentApi); updateApi(component, parent, componentApi, omit); }); } completedClasses.add(component.name); } } catch (error) { console.error( "[cem-inheritance] - Error processing inheritance queue.", error ); } finally { classQueue = []; } } function getOmittedProperties(component, parent, api) { const configOmits = userConfig.omitByConfig?.[component.name]?.[api] || []; const componentOmitProp = userConfig.omitByProperty?.[api] || ""; const allOmits = [ ...configOmits.map((name) => ({ name })), ...component[componentOmitProp] || [], ...parent[componentOmitProp] || [] ]; const uniqueOmits = [...new Set(allOmits.map((omit) => omit.name))]; if (uniqueOmits.length) { component[componentOmitProp] = uniqueOmits.map((name) => ({ name })); } return uniqueOmits; } function updateApi(component, parent, api, omit) { if (api === "properties" || api === "methods") { updateClassMembers(component, parent, api, omit); return; } if (userConfig.ignore?.includes(api)) { return; } if (!component[api]) { component[api] = []; } parent[api]?.forEach((element) => { let apiItem = component[api]?.find( (a) => a.name === element.name ); if (!apiItem) { apiItem = addInheritedFromInfo(element, parent.name); component[api].push(apiItem); } }); component[api] = component[api]?.filter( (a) => !omit.includes(a.name) && a.inheritedFrom ); if (api === "attributes" && component.members?.length && omit.length) { const omittedAttributeFields = component.attributes?.filter((a) => omit.includes(a.name || "")).map((a) => a.fieldName); component.members = component.members.filter( (member) => member.inheritedFrom ? !omittedAttributeFields?.includes(member.name) : true ); } } function updateClassMembers(component, parent, api, omit) { const parentContent = parent.members?.filter( (m) => m.kind === (api === "methods" ? "method" : "field") ); if (userConfig.ignore?.includes(api)) { return; } if (!component.members) { component.members = []; } parentContent?.forEach((member) => { let apiItem = component.members?.find((a) => a.name === member.name); if (!apiItem) { apiItem = addInheritedFromInfo(member, parent.name); component.members.push(apiItem); } }); component.mixins?.forEach((mixin) => { const extMixin = externalMixins.find( (extMixin2) => extMixin2.name === mixin.name ); if (!extMixin) { return; } const mixinApi = extMixin?.members?.filter( (m) => m.kind === (api === "methods" ? "method" : "field") ); mixinApi?.forEach((element) => { let apiItem = component[api]?.find( (a) => a.name === element.name ); if (!apiItem) { apiItem = addInheritedFromInfo(element, extMixin.name); component.members.push(apiItem); } }); }); component.members = component.members?.filter( (a) => !omit.includes(a.name) ); if (api !== "methods" && component.attributes?.length) { component.attributes = component.attributes.filter( (a) => !omit.includes(a.fieldName || "") ); } } function addInheritedFromInfo(member, parentName) { const newMember = { ...member }; if (!member.inheritedFrom) { newMember.inheritedFrom = { name: parentName || "" }; } return newMember; } function getDeclarations(customElementsManifest, exclude) { return getAllComponents(customElementsManifest)?.filter( (c) => !exclude?.includes(c.name) || [] ); } function mergeExternalManifests(mainManifest, externalManifests) { if (!externalManifests || externalManifests.length === 0) { return mainManifest; } const mergedManifest = { ...mainManifest }; if (!mergedManifest.modules) { mergedManifest.modules = []; } for (const externalManifest of externalManifests) { if (externalManifest.modules) { mergedManifest.modules.push(...externalManifest.modules); } } return mergedManifest; } // src/cem-plugin.ts var import_jsdoc_tags = require("@wc-toolkit/jsdoc-tags"); var userOptions = defaultUserConfig; var defaultTags = { "omit": { isArray: true, mappedName: userOptions.omitByProperty?.attributes }, "omit-part": { isArray: true, mappedName: userOptions.omitByProperty?.cssParts }, "omit-cssprop": { isArray: true, mappedName: userOptions.omitByProperty?.cssProperties }, "omit-cssState": { isArray: true, mappedName: userOptions.omitByProperty?.cssStates }, "omit-event": { isArray: true, mappedName: userOptions.omitByProperty?.events }, "omit-slot": { isArray: true, mappedName: userOptions.omitByProperty?.slots }, "omit-method": { isArray: true, mappedName: userOptions.omitByProperty?.methods } }; function cemInheritancePlugin(options = {}) { userOptions = deepMerge(defaultUserConfig, options); userOptions.usedByPlugin = true; return { name: "cem-inheritance", analyzePhase(params) { (0, import_jsdoc_tags.parseJsDocTags)(params, defaultTags); }, packageLinkPhase({ customElementsManifest }) { options.usedByPlugin = true; updateCemInheritance(customElementsManifest, userOptions); } }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { cemInheritancePlugin, generateUpdatedCem, updateCemInheritance });