@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
JavaScript
;
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
});