Skip to content

Commit

Permalink
fix: expand on shadowDom support
Browse files Browse the repository at this point in the history
  • Loading branch information
darlanalves authored Aug 29, 2024
1 parent e784d08 commit 4d68df3
Showing 1 changed file with 96 additions and 62 deletions.
158 changes: 96 additions & 62 deletions server/presets.mjs
Original file line number Diff line number Diff line change
@@ -1,70 +1,85 @@
import { readFile, mkdir, writeFile } from 'node:fs/promises';
import { createReadStream, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import tailwind from 'tailwindcss';
import resolveConfig from 'tailwindcss/resolveConfig.js';
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import Yaml from 'yaml';
import { defaultPlugins, allPlugins } from './constants.mjs';
import { readFile, mkdir, writeFile } from "node:fs/promises";
import { createReadStream, existsSync } from "node:fs";
import { join, dirname } from "node:path";
import tailwind from "tailwindcss";
import resolveConfig from "tailwindcss/resolveConfig.js";
import postcss from "postcss";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";
import Yaml from "yaml";
import { defaultPlugins, allPlugins } from "./constants.mjs";

const CWD = process.cwd();
const getPresetPath = (name) => join(CWD, 'systems', name + '.yml');
const getPresetPath = (name) => join(CWD, "systems", name + ".yml");
const each = (v) => (!v ? [] : Object.entries(v));

function generateComponentParts(name, def, separator) {
return !def ? [] : Object.entries(def).map(([part, classes]) => `.${name}${separator}${part} {\n @apply ${classes} ;\n}\n`);
return each(def).map(([part, c]) => [`.${name}${separator}${part}`, c]);
}

function generateShadowComponentParts(name, def) {
return !def ? [] : Object.entries(def).map(([part, classes]) => `${name}::part(${part}) {\n @apply ${classes} ;\n}\n`);
function generateShadowComponentParts(name, parts) {
return each(parts).map(([part, c]) => [`${name}::part(${part})`, c]);
}

function generateShadowComponentStates(name, def) {
return !def ? [] : Object.entries(def).map(([part, classes]) => `${name}::part(component):${part} {\n @apply ${classes} ;\n}\n`);
return each(def).map(([part, c]) => [`${name}::part(component):${part}`, c]);
}

function defineComponent(name, def, useShadowDom) {
if (useShadowDom) {
return [
`${name}::part(component) {\n${(def.apply && ' @apply ' + def.apply + ';\n') || ''}}\n`,
generateShadowComponentParts(name, def.parts),
generateShadowComponentStates(name, def.states),
]
.flat()
.join('');
}
function generateShadowComponentVariants(name, variants) {
return each(variants).map(
([variant, classes]) =>
`.${name}.${name}-${variant}::part(component) {\n @apply ${classes} ;\n}\n`
);
}

return [
`.${name} {\n${(def.apply && ' @apply ' + def.apply + ';\n') || ''}}\n`,
generateComponentParts(name, def.parts, '__'),
generateComponentParts(name, def.modifiers, '--'),
generateComponentParts(name, def.variants, '-'),
generateComponentParts(name, def.states, ':'),
]
.flat()
.join('');
function generateComponent(name, def, prefix = "") {
return !def ? [] : [`${prefix}${name}::part(component)`, def.apply];
}

function defineComponent(name, def, useShadowDom) {
const all = useShadowDom
? [
generateComponent(name, def.apply),
generateShadowComponentParts(name, def.parts),
generateShadowComponentStates(name, def.states),
generateShadowComponentVariants(name, def.variants, "-"),
]
: [
generateComponent(name, def.apply, "."),
generateComponentParts(name, def.parts, "__"),
generateComponentParts(name, def.modifiers, "--"),
generateComponentParts(name, def.variants, "-"),
generateComponentParts(name, def.states, ":"),
];

return all
.flatMap((item) => item[0] + `{\n @apply ${item[1]} ;\n}\n`)
.join("");
}

function generateCssSafelist(presets) {
const classes = [];

presets.forEach(next => {
if (!(next.components && typeof next.components === 'object')) return;
presets.forEach((next) => {
if (!(next.components && typeof next.components === "object")) return;

Object.entries(next.components).forEach(([name, def]) => {
classes.push(name);

if (def.parts) {
classes.push(...Object.keys(def.parts).map(key => name + '__' + key));
classes.push(...Object.keys(def.parts).map((key) => name + "__" + key));
}

if (def.modifiers) {
classes.push(...Object.keys(def.modifiers).map(key => name + '--' + key));
classes.push(
...Object.keys(def.modifiers).map((key) => name + "--" + key)
);
}

if (def.variants) {
classes.push(...Object.keys(def.variants).map(key => name + '-' + key));
classes.push(
...Object.keys(def.variants).map((key) => name + "-" + key)
);
}
});
});
Expand All @@ -84,7 +99,7 @@ function generateCssTemplate(presets, useShadowDom) {
chain.variables = Object.assign({}, chain.variables, next.variables);
}

if (next.components && typeof next.components === 'object') {
if (next.components && typeof next.components === "object") {
Object.assign(chain, next.components);
}

Expand All @@ -99,8 +114,12 @@ function generateCssTemplate(presets, useShadowDom) {
return chain;
}, {});

const components = Object.entries(chain).map(([name, def]) => defineComponent(name, def, useShadowDom)).join('');
const allVariables = Object.entries(variables).map(([key, value]) => `--${key}: ${value};`).join('\n');
const components = Object.entries(chain)
.map(([name, def]) => defineComponent(name, def, useShadowDom))
.join("");
const allVariables = Object.entries(variables)
.map(([key, value]) => `--${key}: ${value};`)
.join("\n");
const css = `:root{
${allVariables}
}
Expand All @@ -112,7 +131,7 @@ function generateCssTemplate(presets, useShadowDom) {
${components}
}
${styles.join('')}
${styles.join("")}
`;

return css;
Expand All @@ -130,7 +149,9 @@ export async function generatePreset(input) {
}

const allPresets = [...presetChain, input];
const pluginChain = allPresets.flatMap(p => transformPlugins(p.corePlugins)).filter(Boolean);
const pluginChain = allPresets
.flatMap((p) => transformPlugins(p.corePlugins))
.filter(Boolean);
const resolvedPlugins = [...new Set(pluginChain)];

if (resolvedPlugins.length) {
Expand All @@ -142,34 +163,44 @@ export async function generatePreset(input) {
if (input.autoPurge) {
tailwindConfig.purge = {
enabled: true,
content: ['*.xyz'],
content: ["*.xyz"],
safelist: generateCssSafelist(allPresets),
};
}

const json = JSON.stringify(tailwindConfig, null, 2);
const cssTemplate = generateCssTemplate(allPresets, (input.shadowDom || input['shadow-dom']));
const plugins = [tailwind(tailwindConfig), autoprefixer(), input.minify && cssnano()].filter(Boolean);
const cssTemplate = generateCssTemplate(
allPresets,
input.shadowDom || input["shadow-dom"]
);
const plugins = [
tailwind(tailwindConfig),
autoprefixer(),
input.minify && cssnano(),
].filter(Boolean);
const processor = postcss(...plugins);

try {
const output = await processor.process(cssTemplate, { from: '/web-design-system.css', to: '/index.css' });
const output = await processor.process(cssTemplate, {
from: "/web-design-system.css",
to: "/index.css",
});
const { css } = output;

return { error: null, css, json };
} catch (error) {
return { error, css: '', json };
return { error, css: "", json };
}
}

export async function readPreset(name) {
const path = getPresetPath(name);

if (!existsSync(path)) {
return '';
return "";
}

return await readFile(path, 'utf-8');
return await readFile(path, "utf-8");
}

/**
Expand All @@ -188,7 +219,7 @@ export async function loadPreset(name) {
export async function loadPresetChain(nameOrPreset, presets = []) {
let preset = nameOrPreset;

if (typeof nameOrPreset === 'string') {
if (typeof nameOrPreset === "string") {
preset = await loadPreset(nameOrPreset);
}

Expand All @@ -197,7 +228,10 @@ export async function loadPresetChain(nameOrPreset, presets = []) {
}

if (preset.extends) {
const extensions = typeof preset.extends === 'string' ? [preset.extends] : preset.extends || [];
const extensions =
typeof preset.extends === "string"
? [preset.extends]
: preset.extends || [];

for (const extension of extensions.reverse()) {
const next = await loadPreset(extension);
Expand All @@ -215,19 +249,19 @@ export async function loadPresetChain(nameOrPreset, presets = []) {
export async function savePreset(name, preset) {
const path = getPresetPath(name);
await ensureFolder(dirname(path));
await writeFile(path, preset, 'utf-8');
await writeFile(path, preset, "utf-8");
}

export async function savePresetAssets(name, preset) {
const { json, css } = preset;
const basePath = join(CWD, 'presets', name);
const basePath = join(CWD, "presets", name);
await ensureFolder(dirname(basePath));
await writeFile(basePath + '.mjs', 'export default ' + json);
await writeFile(basePath + '.css', css);
await writeFile(basePath + ".mjs", "export default " + json);
await writeFile(basePath + ".css", css);
}

export function loadPresetAsset(name) {
const path = join(CWD, 'presets', name);
const path = join(CWD, "presets", name);

if (existsSync(path)) {
return createReadStream(path);
Expand All @@ -240,15 +274,15 @@ export function loadPresetAsset(name) {
* @returns {string[]} plugins after transforming the keywords
*/
export function transformPlugins(plugins) {
if (plugins === 'all') {
if (plugins === "all") {
return allPlugins;
}

if (plugins === 'none') {
if (plugins === "none") {
return [];
}

if (plugins === 'default') {
if (plugins === "default") {
plugins = defaultPlugins;
}

Expand All @@ -257,7 +291,7 @@ export function transformPlugins(plugins) {
}

return plugins.flatMap((next) => {
if (next.endsWith('*')) {
if (next.endsWith("*")) {
const stem = next.slice(0, -1);
return allPlugins.filter((p) => p.startsWith(stem));
}
Expand Down

0 comments on commit 4d68df3

Please sign in to comment.