vueless 0.0.577 → 0.0.579

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.577",
3
+ "version": "0.0.579",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
package/plugin-vite.js CHANGED
@@ -10,6 +10,7 @@ import { cacheIcons, removeIconsCache, copyIconsCache } from "./utils/node/loade
10
10
  import { createTailwindSafelist, clearTailwindSafelist } from "./utils/node/tailwindSafelist.js";
11
11
  import { getNuxtFiles, getVueFiles } from "./utils/node/helper.js";
12
12
  import { componentResolver, directiveResolver } from "./utils/node/vuelessResolver.js";
13
+ import { setCustomPropTypes, removeCustomPropTypes } from "./utils/node/dynamicProps.js";
13
14
 
14
15
  /* Automatically importing Vueless components on demand */
15
16
  export const VuelessUnpluginComponents = (options) =>
@@ -34,6 +35,8 @@ export const Vueless = function (options = {}) {
34
35
 
35
36
  /* if server stopped by developer (Ctrl+C) */
36
37
  process.on("SIGINT", async () => {
38
+ await removeCustomPropTypes(isVuelessEnv);
39
+
37
40
  /* remove cached icons */
38
41
  await removeIconsCache(mirrorCacheDir, debug);
39
42
 
@@ -82,6 +85,8 @@ export const Vueless = function (options = {}) {
82
85
  await cacheIcons({ mode: "vuelessIcons", env, debug, targetFiles });
83
86
  /* copy vueless cache folder */
84
87
  await copyIconsCache(mirrorCacheDir, debug);
88
+
89
+ await setCustomPropTypes(isVuelessEnv);
85
90
  }
86
91
  },
87
92
 
@@ -12,12 +12,12 @@ import UIcon from "../ui.image-icon/UIcon.vue";
12
12
  import defaultConfig from "./config.ts";
13
13
  import { UButton } from "./constants.ts";
14
14
 
15
- import type { UButtonProps, LoaderSize, IconSize, Config } from "./types.ts";
15
+ import type { Props, LoaderSize, IconSize, Config } from "./types.ts";
16
16
 
17
17
  defineOptions({ inheritAttrs: false });
18
18
 
19
- const props = withDefaults(defineProps<UButtonProps>(), {
20
- ...getDefaults<UButtonProps>(defaultConfig, UButton),
19
+ const props = withDefaults(defineProps<Props>(), {
20
+ ...getDefaults<Props>(defaultConfig, UButton),
21
21
  });
22
22
 
23
23
  const slots = useSlots();
@@ -15,9 +15,9 @@ import UCol from "../../ui.container-col/UCol.vue";
15
15
  import { useDarkMode } from "../../composables/useDarkMode.ts";
16
16
 
17
17
  import type { Meta, StoryFn } from "@storybook/vue3";
18
- import type { UButtonProps } from "../types.ts";
18
+ import type { Props } from "../types.ts";
19
19
 
20
- interface UButtonArgs extends UButtonProps {
20
+ interface UButtonArgs extends Props {
21
21
  slotTemplate?: string;
22
22
  enum: "variant" | "size";
23
23
  }
@@ -6,7 +6,7 @@ export type Config = ComponentConfig<typeof defaultConfig>;
6
6
  export type LoaderSize = "sm" | "md" | "lg";
7
7
  export type IconSize = "2xs" | "xs" | "sm" | "md";
8
8
 
9
- export interface UButtonProps {
9
+ export interface Props {
10
10
  /**
11
11
  * Button variant.
12
12
  */
@@ -0,0 +1,190 @@
1
+ import fs from "fs/promises";
2
+ import { existsSync } from "fs";
3
+ import path from "node:path";
4
+
5
+ import { vuelessConfig } from "./vuelessConfig.js";
6
+
7
+ import { COMPONENTS, VUELESS_DIR, VUELESS_LOCAL_DIR } from "../../constants.js";
8
+
9
+ const OPTIONAL_MARK = "?";
10
+ const CLOSING_BRACKET = "}";
11
+ const IGNORE_PROP = "@ignore";
12
+ const CUSTOM_PROP = "@custom";
13
+
14
+ const VUELESS_SRC = path.join(VUELESS_DIR, VUELESS_LOCAL_DIR);
15
+
16
+ const PROPS_INTERFACE_REG_EXP = /export\s+interface\s+Props\s*{([^}]*)}/s;
17
+ const UNION_SYMBOLS_REG_EXP = /[?|:"|;]/g;
18
+ const WORD_IN_QUOTE_REG_EXP = /"([^"]+)"/g;
19
+
20
+ async function cacheComponentTypes(filePath) {
21
+ const cacheDir = path.join(filePath, ".cache");
22
+ const sourceFile = path.join(filePath, "types.ts");
23
+ const destFile = path.join(cacheDir, "types.ts");
24
+
25
+ if (existsSync(cacheDir)) {
26
+ return;
27
+ }
28
+
29
+ if (existsSync(sourceFile)) {
30
+ await fs.mkdir(cacheDir);
31
+ await fs.cp(sourceFile, destFile);
32
+ }
33
+ }
34
+
35
+ async function clearComponentTypesCache(filePath) {
36
+ await fs.rm(path.join(filePath, ".cache"), { force: true, recursive: true });
37
+ }
38
+
39
+ export async function restoreComponentTypes(filePath) {
40
+ const cacheDir = path.join(filePath, ".cache");
41
+ const sourceFile = path.join(cacheDir, "types.ts");
42
+ const destFile = path.join(filePath, "types.ts");
43
+
44
+ if (existsSync(sourceFile)) {
45
+ await fs.cp(sourceFile, destFile);
46
+ }
47
+ }
48
+
49
+ function getMultiLineUnionValues(lines, propIndex, propEndIndex) {
50
+ return lines
51
+ .slice(propIndex)
52
+ .slice(1, propEndIndex + 1)
53
+ .map((item) => item.replace(UNION_SYMBOLS_REG_EXP, "").trim());
54
+ }
55
+
56
+ function getInlineUnionValues(lines, propIndex, propEndIndex) {
57
+ const types = lines
58
+ .slice(propIndex)
59
+ .slice(0, propEndIndex + 1)
60
+ .at(0)
61
+ .match(WORD_IN_QUOTE_REG_EXP);
62
+
63
+ return types ? types.map((value) => value.replace(/"/g, "")) : [];
64
+ }
65
+
66
+ /**
67
+ * Updates or add a prop types dynamically.
68
+ * @param {string} filePath - The path to the TypeScript file.
69
+ * @param {Array} props - Array of prop objects to add or update.
70
+ */
71
+ async function modifyComponentTypes(filePath, props) {
72
+ try {
73
+ const targetFile = path.join(filePath, "types.ts");
74
+
75
+ /* Read `types.ts` and split it by lines. */
76
+ let fileContent = await fs.readFile(targetFile, "utf-8");
77
+ const propsInterface = fileContent.match(PROPS_INTERFACE_REG_EXP)?.at(0)?.trim();
78
+
79
+ /* Remove props interface and double returns from fileContent */
80
+ fileContent = fileContent.replace(propsInterface, "").replace(/\n\s*\n/g, "\n");
81
+
82
+ const lines = propsInterface.split("\n");
83
+
84
+ for (const prop of props) {
85
+ const { name, type, values = [], description, required, ignore } = prop;
86
+
87
+ if (!name) return;
88
+
89
+ /* Find line with prop. */
90
+ const propRegex = new RegExp(`^\\s*${name}[?:]?\\s*:`);
91
+ const propIndex = lines.findIndex((line) => propRegex.test(line));
92
+ const propEndIndex = lines.slice(propIndex).findIndex((line) => line.endsWith(";"));
93
+ const propTypes = propEndIndex
94
+ ? getMultiLineUnionValues(lines, propIndex, propEndIndex)
95
+ : getInlineUnionValues(lines, propIndex, propEndIndex);
96
+
97
+ const defaultUnionType = propTypes.map((value) => `"${value}"`).join(" | ");
98
+
99
+ /* Prepare prop params. */
100
+ const uniqueValues = [...new Set(values)];
101
+ const isAssignableValue = uniqueValues.every((value) => propTypes.includes(value));
102
+ const unionType = uniqueValues.map((value) => `"${value}"`).join(" | ");
103
+ const userOptionalMark = required ? "" : OPTIONAL_MARK;
104
+ const defaultOptionalMark = lines[propIndex]?.includes(OPTIONAL_MARK) ? OPTIONAL_MARK : "";
105
+ const optionalMark = required === undefined ? defaultOptionalMark : userOptionalMark;
106
+
107
+ const propDescription = description?.replaceAll(/[\n\s]+/g, " ").trim() || "–"; // removes new lines and double spaces.
108
+ const propType = unionType.length ? unionType : type;
109
+
110
+ /* Add ignore JSDoc property. */
111
+ if (ignore) {
112
+ const ignoreDefinition = [` * ${IGNORE_PROP}`, ` */`];
113
+ const deleteLinesCount = lines[propIndex - 2].includes(IGNORE_PROP) ? 2 : 1;
114
+
115
+ lines.splice(propIndex - deleteLinesCount, deleteLinesCount, ...ignoreDefinition);
116
+ }
117
+
118
+ /* Check if the prop type already exists. */
119
+ if (~propIndex) {
120
+ if (unionType.length && isAssignableValue) {
121
+ // Remove multiline union types;
122
+ lines.splice(propIndex + 1, propEndIndex);
123
+
124
+ lines.splice(propIndex, 1, ` ${name}${defaultOptionalMark}: ${propType};`);
125
+ }
126
+
127
+ if (unionType.length && !isAssignableValue) {
128
+ // eslint-disable-next-line no-console
129
+ console.warn(`${unionType} is not assignable to type ${defaultUnionType}.`);
130
+ }
131
+
132
+ const isCustomProp = lines[propIndex - 2].includes(CUSTOM_PROP);
133
+
134
+ if (!isCustomProp && (type || description || required)) {
135
+ // eslint-disable-next-line no-console, prettier/prettier
136
+ console.warn("Changing of prop type, description or required are not allowed for the default props.");
137
+ }
138
+
139
+ continue;
140
+ }
141
+
142
+ /* Add new prop. */
143
+ const closingBracketIndex = lines.findIndex((line) => line.trim() === CLOSING_BRACKET);
144
+ const propDefinition = [
145
+ "",
146
+ ` /**`,
147
+ ` * ${propDescription}`,
148
+ ` * ${CUSTOM_PROP}`,
149
+ ` */`,
150
+ ` ${name}${optionalMark}: ${type};`,
151
+ ];
152
+
153
+ lines.splice(closingBracketIndex, 0, ...propDefinition);
154
+ }
155
+
156
+ lines.unshift(fileContent);
157
+
158
+ /* Update `types.ts` file. */
159
+ await fs.writeFile(targetFile, lines.join("\n"), "utf-8");
160
+ } catch (error) {
161
+ // eslint-disable-next-line no-console
162
+ console.error("Error updating file:", error.message);
163
+ }
164
+ }
165
+
166
+ export async function setCustomPropTypes(isVuelessEnv) {
167
+ const srcDir = isVuelessEnv ? VUELESS_LOCAL_DIR : VUELESS_SRC;
168
+
169
+ for await (const [componentName, componentDir] of Object.entries(COMPONENTS)) {
170
+ const customProps =
171
+ componentName in vuelessConfig.component && vuelessConfig.component[componentName].props;
172
+
173
+ if (customProps) {
174
+ await cacheComponentTypes(path.join(srcDir, componentDir));
175
+ await modifyComponentTypes(
176
+ path.join(srcDir, componentDir),
177
+ vuelessConfig.component[componentName].props,
178
+ );
179
+ }
180
+ }
181
+ }
182
+
183
+ export async function removeCustomPropTypes(isVuelessEnv) {
184
+ const srcDir = isVuelessEnv ? VUELESS_LOCAL_DIR : VUELESS_SRC;
185
+
186
+ for await (const componentDir of Object.values(COMPONENTS)) {
187
+ await restoreComponentTypes(path.join(srcDir, componentDir));
188
+ await clearComponentTypesCache(path.join(srcDir, componentDir));
189
+ }
190
+ }
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "vueless",
4
- "version": "0.0.577",
4
+ "version": "0.0.579",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",