UNPKG

@maizzle/framework

Version:

Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.

198 lines (197 loc) 3.93 kB
import { componentNameFromPath } from "../utils/componentSources.js"; import { existsSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { glob } from "tinyglobby"; //#region src/server/sfc-utils.ts const __dirname = dirname(fileURLToPath(import.meta.url)); /** * Built-in framework components Maizzle ships internally. Issues raised * against these files are framework-owned, not the user's responsibility, * so the dev UI's Checks tab filters them out. */ const FRAMEWORK_COMPONENTS_DIR = resolve(__dirname, "../components").replace(/\\/g, "/") + "/"; function isFrameworkComponent(file) { return file.replace(/\\/g, "/").startsWith(FRAMEWORK_COMPONENTS_DIR); } function parseSfcBlocks(source) { let template = null; const styles = []; const templateMatch = source.match(/<template\b[^>]*>([\s\S]*)<\/template>/); if (templateMatch) { const contentStart = source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1]); const offset = source.slice(0, contentStart).split("\n").length - 1; template = { content: templateMatch[1], offset }; } const styleRe = /<style\b([^>]*)>([\s\S]*?)<\/style>/g; let m; while ((m = styleRe.exec(source)) !== null) { if (/\blang\s*=\s*["'](?!css)/i.test(m[1])) continue; const contentStart = m.index + m[0].indexOf(m[2]); const offset = source.slice(0, contentStart).split("\n").length - 1; styles.push({ content: m[2], offset }); } return { template, styles }; } /** * Standard HTML elements — anything not in this set is treated as a component. */ const HTML_ELEMENTS = new Set([ "a", "abbr", "address", "area", "article", "aside", "audio", "b", "base", "bdi", "bdo", "blockquote", "body", "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "label", "legend", "li", "link", "main", "map", "mark", "menu", "meta", "meter", "nav", "noscript", "object", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "search", "section", "select", "slot", "small", "source", "span", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "u", "ul", "var", "video", "wbr" ]); function findComponentTags(templateContent) { const tags = /* @__PURE__ */ new Set(); const pascalRe = /<([A-Z][a-zA-Z0-9]*)\b/g; let m; while ((m = pascalRe.exec(templateContent)) !== null) tags.add(m[1]); const kebabRe = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\b/g; while ((m = kebabRe.exec(templateContent)) !== null) if (!HTML_ELEMENTS.has(m[1])) tags.add(m[1]); return [...tags]; } async function buildComponentMap(root, componentDirs) { const map = /* @__PURE__ */ new Map(); const allSources = [...[resolve(__dirname, "../components"), resolve(root, "components")].filter(existsSync).map((path) => ({ path, prefix: void 0, pathPrefix: true })), ...componentDirs.filter((s) => existsSync(s.path))]; for (const source of allSources) { const files = await glob(["**/*.vue"], { cwd: source.path, absolute: true }); for (const file of files) { const name = componentNameFromPath({ filePath: file, dirRoot: source.path, prefix: source.prefix, pathPrefix: source.pathPrefix }); map.set(name.toLowerCase(), file); } } return map; } //#endregion export { HTML_ELEMENTS, buildComponentMap, findComponentTags, isFrameworkComponent, parseSfcBlocks }; //# sourceMappingURL=sfc-utils.js.map