@vuestic/compiler
Version:
Combination of bundling tools focusing on improving development experience when working with Vuestic UI
1,133 lines (1,091 loc) • 35.3 kB
JavaScript
// tsconfig-plugin/plugin.ts
import fs from "fs/promises";
import path from "path";
import { parseJSON5 } from "confbox";
// logger.ts
import { createLogger } from "vite";
var logger = createLogger("info", {
prefix: "[vuestic:compiler]"
});
// shared/color.ts
var white = "\x1B[0m";
var reset = "\x1BRESET";
var red = "\x1B[31m";
var yellow = "\x1B[33m";
var cyan = "\x1B[36m";
var formatString = (str) => {
str = str.replace(/\[([^\]]*)\]/g, (_, p1) => cyan + p1 + reset);
if (str.startsWith("!!! ")) {
str = red + str.slice(4).replaceAll(reset, red) + reset;
}
if (str.startsWith("! ")) {
str = yellow + str.slice(2).replaceAll(reset, yellow) + reset;
}
return str.replaceAll(reset, white);
};
// tsconfig-plugin/plugin.ts
var include = [
"node_modules/.vuestic/**/*",
"node_modules/vuestic-ui/**/*"
];
function fileName(filePath) {
return path.basename(filePath);
}
function hasInclude(array) {
return include.every((item) => {
return array.includes(item);
});
}
var vuesticTsConfig = () => {
return [
{
name: "vuestic:tsconfig",
// On vite start save makeTsConfig to node_modules/.vuestic/tsconfig.json
buildStart: async () => {
const tsconfigAppPath = path.resolve("tsconfig.app.json");
const tsconfigGlobalPath = path.resolve("tsconfig.json");
if (await fs.stat(tsconfigAppPath).catch(() => false)) {
const tsconfigApp = await fs.readFile(tsconfigAppPath, "utf-8");
const tsconfigAppJson = parseJSON5(tsconfigApp);
if (!tsconfigAppJson.include) {
tsconfigAppJson.include = [];
}
if (hasInclude(tsconfigAppJson.include)) {
return;
}
tsconfigAppJson.include.push(...include);
await fs.writeFile(tsconfigAppPath, JSON.stringify(tsconfigAppJson, null, 2), "utf-8");
logger.info(formatString(`tsconfig.app.json found, updated ${fileName(tsconfigAppPath)} with default include.`), { timestamp: true });
} else if (await fs.stat(tsconfigGlobalPath).catch(() => false)) {
const tsconfigGlobal = await fs.readFile(tsconfigGlobalPath, "utf-8");
const tsconfigGlobalJson = parseJSON5(tsconfigGlobal);
if (!tsconfigGlobalJson.include) {
tsconfigGlobalJson.include = [];
}
if (hasInclude(tsconfigGlobalJson.include)) {
return;
}
tsconfigGlobalJson.include.push(...include);
await fs.writeFile(tsconfigGlobalPath, JSON.stringify(tsconfigGlobalJson, null, 2), "utf-8");
logger.info(formatString(`tsconfig.app.json found, updated ${fileName(tsconfigGlobalPath)} with default include.`), { timestamp: true });
} else {
await fs.mkdir(path.dirname(tsconfigGlobalPath), { recursive: true });
await fs.writeFile(tsconfigGlobalPath, JSON.stringify({
"include": [...include]
}, null, 2), "utf-8");
logger.warn(formatString(`!!! No tsconfig.app.json or tsconfig.json found. Created ${fileName(tsconfigGlobalPath)} with default include.`), { timestamp: true });
}
}
}
];
};
// devtools/plugin/plugin.ts
import { createLogger as createLogger2 } from "vite";
import { createFilter } from "@rollup/pluginutils";
// devtools/plugin/compiler.ts
import { parse } from "@vue/compiler-sfc";
import MagicString from "magic-string";
// devtools/shared/CONST.ts
var PREFIX = "va";
var API_PREFIX = "/vuestic-devtools-api";
// devtools/shared/slug.ts
var knownPaths = /* @__PURE__ */ new Map();
var minifyPath = (path2) => {
const minified = `${PREFIX}-${knownPaths.size}`;
knownPaths.set(minified, path2);
return minified;
};
var unminifyPath = (minified) => {
if (knownPaths.has(minified)) {
return knownPaths.get(minified);
}
return null;
};
var replacePath = (minified, path2) => {
knownPaths.set(minified, path2);
return minified;
};
// devtools/shared/file-query.ts
var stringifyFileQuery = (path2, start, end) => `${path2}:${start}:${end}`;
var parseFileQuery = (query) => {
const params = query.split(":");
const end = params.pop();
const start = params.pop();
const path2 = params.join(":");
return { path: path2, start: Number(start), end: Number(end) };
};
// devtools/plugin/compiler.ts
import { readFile } from "fs/promises";
// devtools/plugin/diff-sync.ts
var makeDiffSyncByRow = (original, modified) => {
const originalLines = original.split("\n");
const modifiedLines = modified.split("\n");
let sourceShift = 0;
let outputShift = 0;
const blocks = [];
for (let i = 0; i < Math.max(originalLines.length, modifiedLines.length); i++) {
const sourceLine = (originalLines[i] ?? "") + "\n";
const modifiedLine = (modifiedLines[i] ?? "") + "\n";
if (sourceLine === modifiedLine) {
blocks.push({
type: "equal",
start: { source: sourceShift, output: outputShift },
end: {
source: sourceShift + sourceLine.length,
output: outputShift + sourceLine.length
},
source: sourceLine
});
} else {
const lineDiff = makeDiffSync(sourceLine, modifiedLine);
for (const block of lineDiff) {
blocks.push({
...block,
start: {
source: block.start.source + sourceShift,
output: block.start.output + outputShift
},
end: {
source: block.end.source + sourceShift,
output: block.end.output + outputShift
}
});
}
}
sourceShift += sourceLine.length;
outputShift += modifiedLine.length;
}
return mergeEqualBlocks(blocks);
};
var mergeEqualBlocks = (blocks) => {
return blocks.reduce((acc, block) => {
const last = acc.at(-1);
if (block.type === "equal" && (last == null ? void 0 : last.type) === "equal") {
last.source += block.source;
last.end = block.end;
} else {
acc.push(block);
}
return acc;
}, []);
};
var makeDiffSync = (source, output) => {
let originalShift = 0;
let modifiedShift = 0;
function makeBlock(type, sourceStart, sourceEnd, outputStart, outputEnd) {
const blockSource = type === "insert" ? output.slice(outputStart, outputEnd) : source.slice(sourceStart, sourceEnd);
return {
start: {
source: sourceStart,
output: outputStart
},
end: {
source: sourceEnd,
output: outputEnd
},
type,
source: blockSource
};
}
const blocks = [
makeBlock("equal", 0, 0, 0, 0)
];
function findInsert(originalShift2, modifiedShift2) {
const modifiedShiftStart = modifiedShift2;
while (originalShift2 < source.length && modifiedShift2 < output.length) {
if (output[modifiedShift2] === source[originalShift2]) {
break;
}
modifiedShift2++;
}
if (output.length < modifiedShiftStart) {
return;
}
return makeBlock("insert", originalShift2, originalShift2, modifiedShiftStart, modifiedShift2);
}
function findDelete(originalShift2, modifiedShift2) {
const originalShiftStart = originalShift2;
while (originalShift2 < source.length && modifiedShift2 < output.length) {
if (output[modifiedShift2] === source[originalShift2]) {
return makeBlock("delete", originalShiftStart, originalShift2, modifiedShift2, modifiedShift2);
}
originalShift2++;
}
}
while (originalShift < source.length && modifiedShift < output.length) {
const originalChar = source[originalShift];
const modifiedChar = output[modifiedShift];
const currentBlock = blocks[blocks.length - 1];
if (originalChar === modifiedChar) {
if (currentBlock.type === "equal") {
currentBlock.end.output++;
currentBlock.end.source++;
currentBlock.source = source.slice(currentBlock.start.source, currentBlock.end.source);
} else {
blocks.push(makeBlock("equal", originalShift, originalShift + 1, modifiedShift, modifiedShift + 1));
}
originalShift++;
modifiedShift++;
} else {
const nextChain = findDelete(originalShift, modifiedShift);
const insertBlock = findInsert(originalShift, modifiedShift);
if (nextChain && insertBlock) {
if (output.slice(modifiedShift).includes(nextChain.source)) {
blocks.push(insertBlock);
modifiedShift = insertBlock.end.output;
continue;
}
blocks.push(nextChain);
originalShift = nextChain.end.source;
continue;
}
if (insertBlock) {
blocks.push(insertBlock);
modifiedShift = insertBlock.end.output;
continue;
}
if (nextChain) {
blocks.push(nextChain);
originalShift = nextChain.end.source;
continue;
}
break;
}
}
return blocks;
};
// devtools/plugin/compiler.ts
var walk = (node, cb) => {
cb(node);
if (!("children" in node)) {
return;
}
for (const child of node.children) {
if (typeof child === "string") {
continue;
}
if (typeof child === "symbol") {
continue;
}
if (child.type === 4) {
continue;
}
walk(child, cb);
}
};
var findEndTagIndex = (source) => {
let inQuotes = false;
for (let i = 0; i < source.length; i++) {
if (source[i] === '"') {
inQuotes = !inQuotes;
}
if (source[i] === ">" && !inQuotes) {
return i;
}
}
return -1;
};
var findSefCloseTagIndex = (source) => {
let inQuotes = false;
for (let i = 0; i < source.length; i++) {
if (source[i] === '"') {
inQuotes = !inQuotes;
}
if (source[i] === "/" && source[i + 1] === ">" && !inQuotes) {
return i;
}
}
return -1;
};
var getNodeTagLoc = (source) => {
let selfCloseIndex = findSefCloseTagIndex(source);
let closeIndex = findEndTagIndex(source);
if (selfCloseIndex === -1) {
selfCloseIndex = source.length;
}
if (closeIndex === -1) {
closeIndex = source.length;
}
return {
start: { offset: 0 },
end: { offset: selfCloseIndex < closeIndex ? selfCloseIndex + 2 : closeIndex + 1 },
source: source.slice(0, selfCloseIndex < closeIndex ? selfCloseIndex + 2 : closeIndex + 1),
endSymbol: selfCloseIndex < closeIndex ? "/>" : ">"
};
};
var fromOutputToSourceShift = (source, output) => {
const diff = makeDiffSyncByRow(source, output);
return (start, end) => {
let current = void 0;
let shiftStart = 0;
let shiftEnd = 0;
for (let i = 0; i < diff.length; i++) {
current = diff[i];
if (current.end.output > end) {
break;
}
if (current.type === "equal") {
continue;
} else if (current.type === "insert") {
if (current.start.output < start) {
shiftStart -= current.source.length;
}
if (current.end.output < end) {
shiftEnd -= current.source.length;
}
}
}
if (!current) {
return { start: 0, end: 0 };
}
return { start: shiftStart, end: shiftEnd };
};
};
var transformFile = async (code, id) => {
var _a;
const sourceFile = await readFile(id, "utf-8");
const result = parse(code);
const templateAst = (_a = result.descriptor.template) == null ? void 0 : _a.ast;
if (!templateAst) {
return;
}
let source = new MagicString(code);
if (code !== sourceFile) {
const getShift = fromOutputToSourceShift(sourceFile, code);
walk(templateAst, (node) => {
if (node.type === 1) {
const tagLoc = getNodeTagLoc(node.loc.source);
const shift = getShift(node.loc.start.offset, node.loc.end.offset);
const nodeId = stringifyFileQuery(id, node.loc.start.offset + shift.start, node.loc.end.offset + shift.end);
const withAttribute = ` data-${PREFIX}="" data-${minifyPath(nodeId)}="${node.tag}"`;
source.appendLeft(node.loc.start.offset + tagLoc.end.offset - tagLoc.endSymbol.length, withAttribute);
}
});
} else {
walk(templateAst, (node) => {
if (node.type === 1) {
const tagLoc = getNodeTagLoc(node.loc.source);
const nodeId = stringifyFileQuery(id, node.loc.start.offset, node.loc.end.offset);
const withAttribute = ` data-${PREFIX}="" data-${minifyPath(nodeId)}="${node.tag}"`;
source.appendLeft(node.loc.start.offset + tagLoc.end.offset - tagLoc.endSymbol.length, withAttribute);
}
});
}
return {
code: source.toString(),
map: source.generateMap()
};
};
// devtools/server/utils.ts
var readBody = async (req) => {
return new Promise((resolve3) => {
let body = "";
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
resolve3(body);
});
});
};
// devtools/server/file.ts
import { readFile as readFile2, writeFile } from "fs/promises";
var requestSource = async (path2) => {
const source = await readFile2(path2, "utf-8");
return source.toString();
};
var getIntent = (source, lineStart) => {
let intent = 0;
for (let i = lineStart - 1; i > 0; i--) {
if (source[i] === " ") {
intent++;
} else {
break;
}
}
return intent;
};
var removeIntent = (source, intent) => {
const lines = source.split("\n");
const intentString = " ".repeat(intent);
return lines.map((line) => {
if (line.startsWith(intentString)) {
return line.slice(intentString.length);
}
return line;
}).join("\n");
};
var addIntent = (source, intent) => {
if (source.length === 0) {
return source;
}
const lines = source.split("\n");
const intentString = " ".repeat(intent);
return lines.filter((line) => line.length > 0).map((line, index) => {
if (index === 0) {
return line;
}
return intentString + line;
}).join("\n");
};
var getComponentSource = async (path2, start, end) => {
const fileSource = await requestSource(path2);
const intent = getIntent(fileSource, start);
const componentSource = fileSource.slice(start, end);
return removeIntent(componentSource, intent);
};
var setComponentSource = async (path2, start, end, source) => {
const fileSource = await requestSource(path2);
const intent = getIntent(fileSource, start);
const sourceWithIntent = addIntent(source, intent);
const fileSourceStart = fileSource.slice(0, start);
const fileSourceEnd = fileSource.slice(end);
const newFileContent = fileSourceStart + sourceWithIntent + fileSourceEnd;
await writeFile(path2, newFileContent);
return {
path: path2,
start,
end: start + sourceWithIntent.length
};
};
var deleteComponentSource = async (path2, start, end) => {
const fileSource = await requestSource(path2);
const intent = getIntent(fileSource, start);
if (intent === 0) {
await writeFile(path2, fileSource.slice(0, start) + fileSource.slice(end));
return {
path: path2,
start,
end: start
};
}
const fileSourceStart = fileSource.slice(0, start - intent - "\n".length);
const fileSourceEnd = fileSource.slice(end);
await writeFile(path2, fileSourceStart + fileSourceEnd);
return {
path: path2,
start: start - intent - "\n".length,
end: start - intent - "\n".length
};
};
var getComponentLineAndCol = async (path2, start) => {
const fileSource = await requestSource(path2);
const intent = getIntent(fileSource, start);
const lines = fileSource.slice(0, start).split("\n");
const line = lines.length;
const col = intent;
return { line, col };
};
var getRelativeFilePath = (path2) => {
return "." + path2.replace(process.cwd(), "");
};
// devtools/server/server-middleware.ts
var devtoolsServerMiddleware = () => {
return async (req, res, next) => {
if (!req.url || !req.url.startsWith(API_PREFIX)) {
return next();
}
const url = new URL(req.url, "http://localhost:8088");
const minified = url.searchParams.get("q") ?? "";
const unminified = unminifyPath(minified);
if (!unminified) {
res.writeHead(400);
res.end(`No q provided. Got q="${url.searchParams.get("q")}"`);
return;
}
const { path: path2, start, end } = parseFileQuery(unminified);
if (req.method === "GET" && req.url.startsWith(`${API_PREFIX}/node-source`)) {
res.writeHead(200);
res.end(await getComponentSource(path2, start, end));
return;
}
if (req.method === "PATCH" && req.url.startsWith(`${API_PREFIX}/node-source`)) {
const body = await readBody(req);
if (!(typeof body === "string")) {
throw new Error("Body is required.");
}
const newPath = await setComponentSource(path2, start, end, body);
replacePath(minified, stringifyFileQuery(newPath.path, newPath.start, newPath.end));
res.writeHead(200);
res.end(minified);
return;
}
if (req.method === "DELETE" && req.url.startsWith(`${API_PREFIX}/node-source`)) {
const newPath = await deleteComponentSource(path2, start, end);
replacePath(minified, stringifyFileQuery(newPath.path, newPath.start, newPath.end));
res.writeHead(200);
res.end();
return;
}
if (req.method === "GET" && req.url.startsWith(`${API_PREFIX}/file-name`)) {
res.writeHead(200);
res.end(unminified);
return;
}
if (req.method === "GET" && req.url.startsWith(`${API_PREFIX}/relative-file-path`)) {
res.writeHead(200);
res.end(getRelativeFilePath(path2));
return;
}
if (req.method === "GET" && req.url.startsWith(`${API_PREFIX}/vscode-path`)) {
const { line, col } = await getComponentLineAndCol(path2, Number(start));
res.writeHead(200);
res.end(`${path2}:${line}:${col}`);
return;
}
next();
};
};
// devtools/plugin/plugin.ts
import { fileURLToPath, URL as URL2 } from "url";
// devtools/plugin/add-vue-plugin.ts
import MagicString2 from "magic-string";
var CREATE_APP_TEMPLATE = "createApp(App)";
var addVuePlugin = (code) => {
const ms = new MagicString2(code);
const createAppIndex = code.indexOf(CREATE_APP_TEMPLATE);
if (createAppIndex === -1) {
return null;
}
ms.appendRight(createAppIndex + CREATE_APP_TEMPLATE.length, ".use(createVuesticDevtools())");
ms.appendLeft(0, 'import { createVuesticDevtools } from "@vuestic/compiler/devtools";\n');
return {
code: ms.toString(),
map: ms.generateMap({ hires: true })
};
};
// devtools/plugin/plugin.ts
var ALT_KEY = process.platform === "darwin" ? "Option \u2325" : "Alt";
var logger2 = createLogger2("info", {
prefix: "[vuestic:devtools]"
});
var devtools = (options = {}) => {
const filter = createFilter(
options.include ?? ["**/*.vue"],
options.exclude ?? ["node_modules/**"]
);
let config;
return {
name: "vuestic:devtools",
configureServer(server) {
server.middlewares.use(devtoolsServerMiddleware());
},
configResolved(resolvedConfig) {
config = resolvedConfig;
},
async resolveId(id) {
if (id.startsWith("@vuestic/compiler/devtools")) {
if (config.isProduction) {
throw new Error("VuesticDevtools: devtools must not be imported in production");
}
return fileURLToPath(new URL2("../client/index.ts", import.meta.url));
}
},
async transform(code, id) {
if (config.isProduction) {
return;
}
if (/\/src\/main\.(ts|js|mjs|mts)$/.test(id)) {
const newCode = addVuePlugin(code);
if (newCode) {
logger2.info(formatString(`Vuestic Devtools installed. Open your application and press [${ALT_KEY}] + [F12] to open devtools`), {
timestamp: true
});
} else {
logger2.error(formatString("! Devtools plugin can not find createApp(App) in your main file. This is likely a bug, please, open an issue on GitHub."), {
timestamp: true
});
logger2.error(formatString("[https://github.com/epicmaxco/vuestic-ui/issues/new?assignees=&labels=BUG&projects=&template=bug-template.md]"), {
timestamp: true
});
}
return newCode;
}
if (!filter(id)) {
return;
}
return transformFile(code, id);
}
};
};
// css-layers/plugin.ts
import MagicString3 from "magic-string";
var addLayer = (ms, layer) => {
ms.prepend(`@layer vuestic.styles, vuestic.components;
@layer ${layer} {
`);
ms.append(`
}`);
return {
code: ms.toString(),
map: ms.generateMap()
};
};
var cssLayers = {
name: "vuestic:css-layer",
transform(code, id) {
if (!id.endsWith(".css")) return null;
if (id.includes("vuestic-ui/dist/styles/") || id.includes("vuestic-ui/packages/ui/dist/styles/")) {
return addLayer(new MagicString3(code), "vuestic.styles");
}
if (id.includes("vuestic-ui/dist/es/") || id.includes("vuestic-ui/packages/ui/dist/es/")) {
return addLayer(new MagicString3(code), "vuestic.components");
}
}
};
// vuestic-config/plugins/config-resolver.ts
import { existsSync } from "fs";
import { readFile as readFile3 } from "fs/promises";
import { resolve } from "path";
var VUESTIC_CONFIG_ALIAS = "#vuestic-config";
var resolveVuesticConfigPath = () => {
if (existsSync("./vuestic.config.ts")) {
return "./vuestic.config.ts";
} else if (existsSync("./vuestic.config.js")) {
return "./vuestic.config.js";
} else if (existsSync("./vuestic.config.mjs")) {
return "./vuestic.config.mjs";
} else if (existsSync("./vuestic.config.cjs")) {
return "./vuestic.config.cjs";
} else if (existsSync("./vuestic.config.mts")) {
return "./vuestic.config.mts";
} else {
return void 0;
}
};
var isConfigExists = (configPath) => {
if (!configPath) {
return resolveVuesticConfigPath();
}
return existsSync(configPath);
};
var configResolver = (options = {}) => {
return {
name: "vuestic:config-resolver",
// Resolve vuestic config alias
async resolveId(source, importer) {
if (source === VUESTIC_CONFIG_ALIAS) {
const {
configPath = resolveVuesticConfigPath()
} = options;
if (configPath) {
return resolve(configPath);
}
return `virtual:vuestic-config`;
}
},
async load(id) {
if (id === `virtual:vuestic-config`) {
return "export default {}";
}
}
};
};
// shared/plugin/is-entry-file.ts
var isEntryFile = (id) => {
return /\/src\/main\.(ts|js|mjs|mts)$/.test(id);
};
// shared/plugin/js.ts
import MagicString4 from "magic-string";
// shared/plugin/code.ts
import { parse as parse2 } from "acorn";
var getContentInParenthesis = (content) => {
let text = "";
let bracketCount = 0;
for (let i = 0; i < content.length; i++) {
if (content[i] === "(") {
bracketCount++;
continue;
}
if (content[i] === ")") {
bracketCount--;
}
if (bracketCount === 0) {
break;
}
text += content[i];
}
return text;
};
var isObject = (t) => {
return t.type === "ObjectExpression";
};
var isProperty = (t) => {
return t.type === "Property";
};
var OBJECT_DECLARATION = "const obj = ";
var replaceOrAddConfigPropertyValue = (content, newValue) => {
const code = OBJECT_DECLARATION + content;
try {
const program = parse2(code, { ecmaVersion: 2020 });
const object = program.body[0].declarations[0].init;
let configProperty = null;
if (isObject(object)) {
const properties = object.properties;
for (const property of properties) {
if (isProperty(property)) {
const name = "name" in property.key ? property.key.name : "value" in property.key ? property.key.value : null;
if (name === "config") {
configProperty = property;
break;
}
}
}
if (!configProperty) {
return `{
config: ${newValue},
${properties.map((p) => {
var _a;
return (_a = p.loc) == null ? void 0 : _a.source;
}).join(", ")}
}`;
}
return (code.slice(0, configProperty.start) + `config: ` + newValue + code.slice(configProperty.end)).slice(OBJECT_DECLARATION.length);
}
return content;
} catch (e) {
console.error("Unable to parse code:", code);
console.error(e);
return content;
}
};
var omitComments = (code) => {
return code.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, "").trim();
};
// shared/plugin/js.ts
var CREATE_APP_TEMPLATE2 = "createApp(App)";
var createMagicString = (code) => {
return typeof code === "string" ? new MagicString4(code) : code;
};
var addImport = (originalCode, code) => {
const ms = createMagicString(originalCode);
ms.appendLeft(0, code + "\n");
return ms;
};
var hasImport = (originalCode, from) => {
const ms = createMagicString(originalCode);
if (typeof from === "string") {
const importRegex = new RegExp(`import\\s+.*?\\s+from\\s+['"]${from}['"]`);
return importRegex.test(ms.original);
}
if (typeof from === "object" && from.from && from.named) {
const importRegex = new RegExp(`import\\s+.*?\\s+from\\s+['"]${from.from}['"]\\s*{\\s*${from.named}\\s*}`);
return importRegex.test(ms.original);
}
return false;
};
var hasVuePlugin = (originalCode, pluginName) => {
const ms = createMagicString(originalCode);
const pluginRegex = new RegExp(`\\.use\\(${pluginName}\\(`);
return pluginRegex.test(omitComments(ms.original));
};
var addVuePlugin2 = (originalCode, code) => {
const ms = createMagicString(originalCode);
const createAppIndex = ms.original.indexOf(CREATE_APP_TEMPLATE2);
if (createAppIndex === -1) {
throw new Error("createApp not found");
}
ms.appendRight(createAppIndex + CREATE_APP_TEMPLATE2.length, `.use(${code})`);
return ms;
};
var mergeVuesticPluginConfigOption = (originalCode, pluginName, content = "") => {
const ms = createMagicString(originalCode);
const createPluginCode = `.use(${pluginName}(`;
const existingPluginIndex = ms.original.indexOf(createPluginCode);
if (existingPluginIndex === -1) {
return null;
}
const nextCode = ms.original.slice(existingPluginIndex + createPluginCode.length - 1);
const contentInParenthesis = getContentInParenthesis(nextCode);
let newCode;
if (contentInParenthesis) {
newCode = replaceOrAddConfigPropertyValue(contentInParenthesis, content);
} else {
newCode = `{ config: ${content} }`;
}
return ms.remove(existingPluginIndex, existingPluginIndex + createPluginCode.length + contentInParenthesis.length + 2).appendLeft(existingPluginIndex + createPluginCode.length, `.use(${pluginName}(${newCode}))`);
};
var compileCode = (ms) => {
if (typeof ms === "string") {
return { code: ms };
}
return {
code: ms.toString(),
map: ms.generateMap()
};
};
// vuestic-config/plugins/use-config.ts
var CONFIG_IMPORT_NAME = "vuesticConfig$va1";
var useConfig = (options = {}) => {
return {
name: "vuestic:use-config",
transform(code, id) {
if (!isEntryFile(id)) {
return;
}
if (!isConfigExists(options.configPath)) {
return;
}
let newCode = addImport(code, `import ${CONFIG_IMPORT_NAME} from '#vuestic-config'`);
const createVuestic = mergeVuesticPluginConfigOption(newCode, "createVuestic", CONFIG_IMPORT_NAME);
const createVuesticEssential = mergeVuesticPluginConfigOption(newCode, "createVuesticEssential", CONFIG_IMPORT_NAME);
if (createVuestic) {
return compileCode(createVuestic);
}
if (createVuesticEssential) {
return compileCode(createVuesticEssential);
}
throw new Error("createVuestic or createVuesticEssential not found in entry file. Please ensure you installed createVuestic plugin or used autoImport option for compiler.");
}
};
};
// vuestic-config/plugins/config-types.ts
import { writeFile as writeFile2, mkdir } from "fs/promises";
import { resolve as resolve2 } from "path";
import { existsSync as existsSync2 } from "fs";
var typeModule = (configPath) => {
return `
import config from '${configPath}';
import { PartialGlobalConfig, type IconConfiguration } from 'vuestic-ui';
type ExtractColorNames<T extends PartialGlobalConfig> = T['colors'] extends { variables: infer U } ? keyof U : never
type ExtractIconNames<T extends PartialGlobalConfig> = T['icons'] extends IconConfiguration<infer U>[] ? U : never
type ExtractI18nKeys<T extends PartialGlobalConfig> = keyof T['i18n']
type Config = typeof config
type ColorNames = ExtractColorNames<Config>
declare module 'vuestic-ui' {
export interface CustomColorVariables extends Record<ColorNames, string> {}
}
type IconNames = ExtractIconNames<Config>
declare module 'vuestic-ui' {
export interface CustomIconVariables extends Record<IconNames, string> {}
}
type I18nKeys = ExtractI18nKeys<Config>
declare module 'vuestic-ui' {
export interface CustomI18NKeys extends Record<I18nKeys, string> {}
}
`.trim();
};
var vuesticConfig = () => {
return `
import { defineVuesticConfig } from "vuestic-ui";
export default defineVuesticConfig({
})
`.trim();
};
var configTypes = (options = {}) => {
return {
name: "vuestic:config-types",
async buildStart() {
let configPath = options.configPath || resolveVuesticConfigPath();
if (!configPath) {
return;
}
if (!existsSync2(configPath)) {
await writeFile2(configPath, vuesticConfig(), {
encoding: "utf-8",
flag: "w"
});
logger.info(formatString(`Vuestic config file created at ${configPath}. Please update it with your configuration.`));
}
configPath = resolve2(configPath);
const module = typeModule(configPath);
await mkdir("node_modules/.vuestic", { recursive: true });
await writeFile2("node_modules/.vuestic/config.d.ts", module, {
encoding: "utf-8",
flag: "w"
});
}
};
};
// vuestic-config/plugin.ts
var vuesticConfig2 = (options = {}) => {
return [
configTypes(options),
configResolver(options),
useConfig(options)
];
};
// auto-import/plugin.ts
import Components from "unplugin-vue-components/vite";
import { MagicString as MagicString5 } from "@vue/compiler-sfc";
function installVuesticEssentialPlugin(ms) {
if (!hasImport(ms, { named: "createVuesticEssential", from: "vuestic-ui" })) {
addImport(ms, `import { createVuesticEssential } from 'vuestic-ui'`);
}
if (!hasVuePlugin(ms, "createVuesticEssential")) {
addVuePlugin2(ms, "createVuesticEssential()");
}
return ms;
}
function installVuesticEssentialStyles(ms) {
if (!hasImport(ms, "vuestic-ui/css")) {
addImport(ms, `import 'vuestic-ui/css'`);
}
return ms;
}
var vuesticAutoImport = (options = { prefix: "Va" }) => {
const prefix = options.prefix || "Va";
return [
{
name: "vuestic:auto-import",
enforce: "pre",
transform(code, id) {
if (!isEntryFile(id)) {
return;
}
const ms = new MagicString5(code);
installVuesticEssentialPlugin(ms);
installVuesticEssentialStyles(ms);
return {
code: ms.toString(),
map: ms.generateMap({ hires: true })
};
}
},
Components({
dts: "node_modules/.vuestic/components.d.ts",
resolvers: [
(componentName) => {
if (componentName.startsWith(prefix))
return { name: componentName, from: "vuestic-ui" };
}
]
})
];
};
// shared/merge-deep.ts
var isObject2 = (obj) => obj && typeof obj === "object" && !Array.isArray(obj);
var mergeDeep = (target, source) => {
if (!isObject2(target)) {
target = {};
}
Object.keys(source).forEach((key) => {
const targetValue = target[key];
const sourceValue = source[key];
if (sourceValue instanceof RegExp || sourceValue instanceof Date) {
target[key] = sourceValue;
} else if (isObject2(targetValue) && isObject2(sourceValue)) {
target[key] = mergeDeep(Object.create(
Object.getPrototypeOf(targetValue),
Object.getOwnPropertyDescriptors(targetValue)
), sourceValue);
} else {
target[key] = sourceValue;
}
});
return target;
};
// shared/project-env.ts
import { parseJSON5 as parseJSON52 } from "confbox";
import { existsSync as existsSync3, readFileSync } from "fs";
function readPackageJson() {
try {
const packageJson = readFileSync("package.json", "utf-8");
return parseJSON52(packageJson);
} catch (error) {
return null;
}
}
function checkModuleExists(moduleName) {
var _a, _b;
try {
(_b = (_a = import.meta).resolve) == null ? void 0 : _b.call(_a, moduleName);
return true;
} catch (error) {
return false;
}
}
function getNodeVersion() {
const version = process.versions.node;
return {
major: parseInt(version.split(".")[0], 10),
minor: parseInt(version.split(".")[1], 10),
patch: parseInt(version.split(".")[2], 10),
full: version,
toString: () => version
};
}
function getPackageManager() {
var _a;
const packageJson = readPackageJson();
if (existsSync3("yarn.lock")) {
return "yarn";
}
if (existsSync3("pnpm-lock.yaml")) {
return "pnpm";
}
if (existsSync3("bun.lockb")) {
return "bun";
}
if (existsSync3("package-lock.json")) {
return "npm";
}
return ((_a = packageJson == null ? void 0 : packageJson.packageManager) == null ? void 0 : _a.split("@")[0]) || "npm";
}
var getProjectEnv = () => {
return {
hasVuesticUI: checkModuleExists("vuestic-ui"),
hasTailwindCSS: checkModuleExists("tailwindcss"),
nodeVersion: getNodeVersion(),
packageManager: getPackageManager(),
production: process.env.NODE_ENV === "production" || import.meta.env.PROD
};
};
// vite-plugin/index.ts
var defaultOptions = {
devtools: true,
cssLayers: false,
autoImport: true,
config: {
configPath: "vuestic.config.ts"
}
};
var vuestic = (options = {}) => {
options = mergeDeep(defaultOptions, options);
const extractOptions = (key) => {
return typeof options[key] === "object" ? options[key] : void 0;
};
const env = getProjectEnv();
if (!(env == null ? void 0 : env.hasVuesticUI)) {
logger.error(formatString(`Vuestic UI is not installed in the project. Run [${env.packageManager} install vuestic-ui].`), {
timestamp: true
});
return [];
}
const plugins = [];
plugins.push(...vuesticTsConfig());
if (options.devtools !== false && !env.production) {
logger.info(formatString("Using [vuestic:devtools] plugin."), {
timestamp: true
});
plugins.push(devtools(extractOptions("devtools")));
}
if (options.cssLayers !== false || env.hasTailwindCSS) {
if (env.hasTailwindCSS) {
logger.info(formatString("Using [vuestic:css-layers] plugin, because Tailwind CSS is detected."), {
timestamp: true
});
} else {
logger.info(formatString("Using [vuestic:css-layers] plugin."), {
timestamp: true
});
}
plugins.push(cssLayers);
}
if (Boolean(options.autoImport)) {
logger.info(formatString("Using [vuestic:auto-import] plugin."), {
timestamp: true
});
plugins.push(...vuesticAutoImport());
}
if (Boolean(options.config)) {
logger.info(formatString("Using [vuestic:config] plugin."), {
timestamp: true
});
plugins.push(...vuesticConfig2(extractOptions("config")));
}
return plugins;
};
export {
vuestic
};