vueless 1.2.5-beta.8 → 1.2.5

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.
@@ -1,22 +1,26 @@
1
1
  /* eslint-disable no-console */
2
2
 
3
- import { existsSync } from "node:fs";
4
3
  import path from "node:path";
5
4
  import { cwd } from "node:process";
6
- import { cp, readFile, writeFile, rename } from "node:fs/promises";
5
+ import { existsSync } from "node:fs";
7
6
  import { styleText } from "node:util";
7
+ import { cp, readFile, writeFile, rename } from "node:fs/promises";
8
8
 
9
9
  import { getDirFiles } from "../../utils/node/helper.js";
10
10
  import { replaceRelativeImports } from "../utils/format.js";
11
11
  import { getStorybookId, getStoryMetaKeyIndex } from "../utils/data.js";
12
-
13
- import { SRC_COMPONENTS_PATH, COMPONENTS_PATH } from "../constants.js";
14
- import { COMPONENTS, VUELESS_PACKAGE_DIR, VUELESS_LOCAL_DIR } from "../../constants.js";
15
-
16
- function getSourcePath(componentName) {
17
- return path.join(cwd(), VUELESS_PACKAGE_DIR, COMPONENTS[componentName]);
18
- }
19
-
12
+ import { COMPONENTS, VUELESS_PACKAGE_DIR, VUELESS_USER_COMPONENTS_DIR } from "../../constants.js";
13
+
14
+ /**
15
+ * Copies an existing Vueless component to a new location with a new name.
16
+ * This includes duplicating the source files and updating the component's internal references as needed.
17
+ *
18
+ * @param {Array<string>} options - An array containing two elements:
19
+ * – The name of the component to be copied.
20
+ * – The desired name for the copied component.
21
+ * @return {Promise<void>} A promise that resolves when the component has been successfully copied.
22
+ * If an error occurs, no value is returned, and the operation exits early with a logged message.
23
+ */
20
24
  export async function copyVuelessComponent(options) {
21
25
  const [componentName, newComponentName] = options;
22
26
 
@@ -32,73 +36,120 @@ export async function copyVuelessComponent(options) {
32
36
  return;
33
37
  }
34
38
 
35
- const sourceComponentPath = getSourcePath(componentName);
39
+ if (newComponentName in COMPONENTS) {
40
+ console.log(
41
+ styleText("red", `Component with name '${newComponentName}' already exists in Vueless UI.`),
42
+ );
36
43
 
37
- const isSrcDir = existsSync(path.join(cwd(), VUELESS_LOCAL_DIR));
38
- const destPath = isSrcDir
39
- ? path.join(SRC_COMPONENTS_PATH, newComponentName)
40
- : path.join(COMPONENTS_PATH, newComponentName);
41
- const absoluteDestPath = path.join(cwd(), destPath);
44
+ return;
45
+ }
46
+
47
+ if (!newComponentName.startsWith("U")) {
48
+ console.log(styleText("red", `Component should have 'U' prefix (ex. 'UButtonCustom').`));
49
+
50
+ return;
51
+ }
42
52
 
43
- const isComponentExists = newComponentName in COMPONENTS || existsSync(absoluteDestPath);
53
+ const absoluteSourcePath = path.join(cwd(), VUELESS_PACKAGE_DIR, COMPONENTS[componentName]);
54
+ const absoluteDestPath = path.join(cwd(), VUELESS_USER_COMPONENTS_DIR, newComponentName);
44
55
 
45
- if (isComponentExists) {
46
- console.log(styleText("red", `Component with name ${newComponentName} already exists.`));
56
+ if (existsSync(absoluteDestPath)) {
57
+ console.log(styleText("red", `Component with name '${newComponentName}' already exists.`));
47
58
 
48
59
  return;
49
60
  }
50
61
 
51
- await cp(sourceComponentPath, absoluteDestPath, { recursive: true });
62
+ await cp(absoluteSourcePath, absoluteDestPath, { recursive: true });
52
63
  await modifyCreatedComponent(absoluteDestPath, componentName, newComponentName);
53
64
 
54
- const successMessage = styleText(
55
- "green",
56
- `The '${componentName}' was successfully copied into the '${destPath}' directory.`,
65
+ console.log(
66
+ // eslint-disable-next-line vue/max-len, prettier/prettier
67
+ styleText("green", `The '${componentName}' was successfully copied into the '${VUELESS_USER_COMPONENTS_DIR}/${newComponentName}' directory.`)
57
68
  );
58
-
59
- console.log(successMessage);
60
69
  }
61
70
 
71
+ /**
72
+ * Modifies the files related to a specified component by renaming the component and updating its references
73
+ * across various files in the specified directory.
74
+ *
75
+ * @param {string} destPath - The destination path where the component and its related files are located.
76
+ * @param {string} componentName - The current name of the component to be modified.
77
+ * @param {string} newComponentName - The new name to assign to the component and its references.
78
+ * @return {Promise<void>} A promise that resolves when the modification process is completed.
79
+ */
62
80
  async function modifyCreatedComponent(destPath, componentName, newComponentName) {
63
81
  const destFiles = await getDirFiles(destPath, "");
64
82
  const storybookId = await getStorybookId();
65
83
 
66
- const componentFileName = `${componentName}.vue`;
67
- const newComponentFileName = `${newComponentName}.vue`;
68
-
69
84
  for await (const filePath of destFiles) {
70
85
  const fileContent = await readFile(filePath, "utf-8");
71
-
72
86
  let updatedContent = replaceRelativeImports(newComponentName, filePath, fileContent);
73
- let targetPath = filePath;
74
87
 
88
+ /* Renaming component name in constants */
75
89
  if (filePath.endsWith("constants.ts")) {
76
- updatedContent = updatedContent.replace(componentName, newComponentName);
90
+ updatedContent = updatedContent.replaceAll(componentName, newComponentName);
91
+ }
92
+
93
+ /* Renaming component name in tests */
94
+ if (filePath.endsWith("test.ts")) {
95
+ updatedContent = updatedContent.replaceAll(componentName, newComponentName);
96
+ }
97
+
98
+ /* Renaming component name in types */
99
+ if (filePath.endsWith("types.ts")) {
100
+ updatedContent = updatedContent.replaceAll(componentName, newComponentName);
101
+ }
102
+
103
+ /* Renaming component name in components */
104
+ if (filePath.endsWith(".vue")) {
105
+ let lines = updatedContent.split("\n");
106
+
107
+ for (const [index, line] of lines.entries()) {
108
+ // Add some condition here in future if some edge cases appear
109
+ if (line.includes(componentName)) {
110
+ lines[index] = line.replaceAll(componentName, newComponentName);
111
+ }
112
+ }
113
+
114
+ updatedContent = lines.join("\n");
77
115
  }
78
116
 
117
+ /* Renaming component name in stories */
79
118
  if (filePath.endsWith("stories.ts")) {
80
- const storyLines = updatedContent.split("\n");
119
+ let lines = updatedContent.split("\n");
81
120
 
82
- const storyComponentImportIndex = storyLines.findIndex(
83
- (line) => line.includes(componentName) && line.includes("import"),
84
- );
121
+ // saving indexes
85
122
  const storyIdIndex = getStoryMetaKeyIndex(fileContent, "id");
86
123
  const storyTitleIndex = getStoryMetaKeyIndex(fileContent, "title");
124
+ const componentImportIndex = lines.findIndex(
125
+ (line) => line.includes(componentName) && line.includes("import"),
126
+ );
87
127
 
88
- storyLines[storyIdIndex] = ` id: "${storybookId}",`;
89
- storyLines[storyTitleIndex] = ` title: "Custom / ${newComponentName}",`;
90
- storyLines[storyComponentImportIndex] =
91
- `import ${newComponentName} from "../${newComponentFileName}"`;
128
+ updatedContent = lines.join("\n").replaceAll(componentName, newComponentName);
129
+ lines = updatedContent.split("\n");
92
130
 
93
- updatedContent = storyLines.join("\n").replaceAll(componentName, newComponentName);
131
+ // replacing lines by indexes
132
+ lines[storyIdIndex] = ` id: "${storybookId}",`;
133
+ lines[storyTitleIndex] = ` title: "Custom / ${newComponentName}",`;
134
+ lines[componentImportIndex] = `import ${newComponentName} from "../${newComponentName}.vue";`;
135
+
136
+ updatedContent = lines.join("\n");
94
137
  }
95
138
 
96
- if (targetPath.endsWith(componentFileName)) {
97
- targetPath = targetPath.replace(componentFileName, newComponentFileName);
139
+ /* Renaming file */
140
+ let targetPath = filePath;
141
+ const [fileName] = filePath.split("/").reverse();
142
+
143
+ if (fileName.includes(componentName)) {
144
+ const [targetDir] = filePath.split(fileName);
145
+ const targetFileName = fileName.replace(componentName, newComponentName);
146
+
147
+ targetPath = path.join(targetDir, targetFileName);
98
148
 
99
149
  await rename(filePath, targetPath);
100
150
  }
101
151
 
152
+ /* Update file */
102
153
  await writeFile(targetPath, updatedContent);
103
154
  }
104
155
  }
@@ -1,18 +1,15 @@
1
1
  /* eslint-disable no-console */
2
2
 
3
- import { existsSync } from "node:fs";
4
3
  import path from "node:path";
5
4
  import { cwd } from "node:process";
6
- import { readFile, writeFile, rename, mkdir, readdir, copyFile } from "node:fs/promises";
5
+ import { existsSync } from "node:fs";
7
6
  import { styleText } from "node:util";
7
+ import { readFile, writeFile, rename, mkdir, readdir, copyFile } from "node:fs/promises";
8
8
 
9
+ import { getStorybookId } from "../utils/data.js";
9
10
  import { getDirFiles } from "../../utils/node/helper.js";
10
11
  import { replaceRelativeImports } from "../utils/format.js";
11
- import { getStorybookId } from "../utils/data.js";
12
-
13
- import { SRC_COMPONENTS_PATH, COMPONENTS_PATH } from "../constants.js";
14
-
15
- import { COMPONENTS, VUELESS_PACKAGE_DIR, VUELESS_LOCAL_DIR } from "../../constants.js";
12
+ import { COMPONENTS, VUELESS_PACKAGE_DIR, VUELESS_USER_COMPONENTS_DIR } from "../../constants.js";
16
13
 
17
14
  const BOILERPLATE_NAME = "UBoilerplate";
18
15
  const BOILERPLATE_PATH = path.join(cwd(), VUELESS_PACKAGE_DIR, "ui.boilerplate");
@@ -26,26 +23,34 @@ export async function createVuelessComponent(options) {
26
23
  return;
27
24
  }
28
25
 
29
- const isSrcDir = existsSync(path.join(cwd(), VUELESS_LOCAL_DIR));
30
- const destPath = isSrcDir
31
- ? path.join(SRC_COMPONENTS_PATH, componentName)
32
- : path.join(COMPONENTS_PATH, componentName);
33
- const absoluteDestPath = path.join(cwd(), destPath);
26
+ if (componentName in COMPONENTS) {
27
+ console.log(
28
+ styleText("red", `Component with name '${componentName}' already exists in Vueless UI.`),
29
+ );
30
+
31
+ return;
32
+ }
33
+
34
+ if (!componentName.startsWith("U")) {
35
+ console.log(styleText("red", `Component should have 'U' prefix (ex. 'UButtonCustom').`));
36
+
37
+ return;
38
+ }
34
39
 
35
- const isComponentExists = componentName in COMPONENTS || existsSync(absoluteDestPath);
40
+ const absoluteDestPath = path.join(cwd(), VUELESS_USER_COMPONENTS_DIR, componentName);
36
41
 
37
- if (isComponentExists) {
38
- console.log(styleText("red", `Component with name ${componentName} already exists.`));
42
+ if (existsSync(absoluteDestPath)) {
43
+ console.log(styleText("red", `Component with name '${componentName}' already exists.`));
39
44
 
40
45
  return;
41
46
  }
42
47
 
43
48
  await copyAndRenameFiles(BOILERPLATE_PATH, absoluteDestPath);
44
- await modifyCreatedComponent(absoluteDestPath, componentName);
49
+ await modifyCreatedComponent(componentName, absoluteDestPath);
45
50
 
46
51
  console.log(
47
- // eslint-disable-next-line prettier/prettier
48
- styleText("green", `The '${componentName}' was successfully created in the '${destPath}' directory.`,),
52
+ // eslint-disable-next-line vue/max-len, prettier/prettier
53
+ styleText("green", `The '${componentName}' was successfully created in the '${VUELESS_USER_COMPONENTS_DIR}/${componentName}' directory.`,),
49
54
  );
50
55
  }
51
56
 
@@ -55,11 +60,7 @@ async function copyAndRenameFiles(srcDir, destDir) {
55
60
 
56
61
  for (const entry of entries) {
57
62
  const srcPath = path.join(srcDir, entry.name);
58
-
59
- const renamed = entry.name.replace(".hidden", "");
60
- const destPath = path.join(destDir, renamed);
61
-
62
- console.log(entry.name, renamed);
63
+ const destPath = path.join(destDir, entry.name);
63
64
 
64
65
  entry.isDirectory()
65
66
  ? await copyAndRenameFiles(srcPath, destPath)
@@ -67,32 +68,64 @@ async function copyAndRenameFiles(srcDir, destDir) {
67
68
  }
68
69
  }
69
70
 
70
- async function modifyCreatedComponent(destPath, componentName) {
71
+ async function modifyCreatedComponent(componentName, destPath) {
71
72
  const destFiles = await getDirFiles(destPath, "");
72
73
  const storybookId = await getStorybookId();
73
74
 
74
75
  for await (const filePath of destFiles) {
75
76
  const fileContent = await readFile(filePath, "utf-8");
76
-
77
77
  let updatedContent = replaceRelativeImports(componentName, filePath, fileContent);
78
- let targetPath = filePath;
79
78
 
79
+ /* Renaming component name in constants */
80
80
  if (filePath.endsWith("constants.ts")) {
81
- updatedContent = updatedContent.replace(BOILERPLATE_NAME, componentName);
81
+ updatedContent = updatedContent.replaceAll(BOILERPLATE_NAME, componentName);
82
+ }
83
+
84
+ /* Renaming component name in tests */
85
+ if (filePath.endsWith("test.ts")) {
86
+ updatedContent = updatedContent.replaceAll(BOILERPLATE_NAME, componentName);
87
+ }
88
+
89
+ /* Renaming component name in types */
90
+ if (filePath.endsWith("types.ts")) {
91
+ updatedContent = updatedContent.replaceAll(BOILERPLATE_NAME, componentName);
82
92
  }
83
93
 
94
+ /* Renaming component name in components */
95
+ if (filePath.endsWith(".vue")) {
96
+ let lines = updatedContent.split("\n");
97
+
98
+ for (const [index, line] of lines.entries()) {
99
+ // Add some condition here in future if some edge cases appear
100
+ if (line.includes(componentName)) {
101
+ lines[index] = line.replaceAll(BOILERPLATE_NAME, componentName);
102
+ }
103
+ }
104
+
105
+ updatedContent = lines.join("\n");
106
+ }
107
+
108
+ /* Renaming component name in stories */
84
109
  if (filePath.endsWith("stories.ts")) {
85
110
  updatedContent = updatedContent
86
111
  .replaceAll(BOILERPLATE_NAME, componentName)
87
112
  .replace("{{component_id}}", String(storybookId));
88
113
  }
89
114
 
90
- if (targetPath.endsWith(`${BOILERPLATE_NAME}.vue`)) {
91
- targetPath = targetPath.replace(BOILERPLATE_NAME, componentName);
115
+ /* Renaming file */
116
+ let targetPath = filePath;
117
+ const [fileName] = filePath.split("/").reverse();
118
+
119
+ if (fileName.includes(BOILERPLATE_NAME)) {
120
+ const [targetDir] = filePath.split(fileName);
121
+ const targetFileName = fileName.replace(BOILERPLATE_NAME, componentName);
122
+
123
+ targetPath = path.join(targetDir, targetFileName);
92
124
 
93
125
  await rename(filePath, targetPath);
94
126
  }
95
127
 
128
+ /* Update file */
96
129
  await writeFile(targetPath, updatedContent);
97
130
  }
98
131
  }
@@ -1,3 +1 @@
1
- export const SRC_COMPONENTS_PATH: "/src/components";
2
- export const COMPONENTS_PATH: "/components";
3
1
  export const DEFAULT_VUELESS_CONFIG_CONTENT: "import { componentConfigs } from \"./.vueless\";\n\nexport default {\n /**\n * Global settings.\n */\n primary: \"grayscale\",\n neutral: \"gray\",\n text: 14,\n outline: 2,\n rounding: 8,\n letterSpacing: 0,\n disabledOpacity: 50,\n colorMode: \"auto\",\n\n /**\n * Component settings.\n */\n components: /*tw*/ {\n ...componentConfigs,\n },\n\n /**\n * Directive settings.\n */\n directives: {},\n\n /**\n * Light theme CSS variable settings.\n */\n lightTheme: {\n /* Primary colors */\n \"--vl-primary\": \"--vl-primary-600\",\n \"--vl-primary-lifted\": \"--vl-primary-700\",\n \"--vl-primary-accented\": \"--vl-primary-800\",\n\n /* Secondary colors */\n \"--vl-secondary\": \"--vl-neutral-500\",\n \"--vl-secondary-lifted\": \"--vl-neutral-600\",\n \"--vl-secondary-accented\": \"--vl-neutral-700\",\n\n /* Success colors */\n \"--vl-success\": \"--color-green-600\",\n \"--vl-success-lifted\": \"--color-green-700\",\n \"--vl-success-accented\": \"--color-green-800\",\n\n /* Info colors */\n \"--vl-info\": \"--color-blue-600\",\n \"--vl-info-lifted\": \"--color-blue-700\",\n \"--vl-info-accented\": \"--color-blue-800\",\n\n /* Notice colors */\n \"--vl-notice\": \"--color-violet-600\",\n \"--vl-notice-lifted\": \"--color-violet-700\",\n \"--vl-notice-accented\": \"--color-violet-800\",\n\n /* Warning colors */\n \"--vl-warning\": \"--color-orange-600\",\n \"--vl-warning-lifted\": \"--color-orange-700\",\n \"--vl-warning-accented\": \"--color-orange-800\",\n\n /* Error colors */\n \"--vl-error\": \"--color-red-600\",\n \"--vl-error-lifted\": \"--color-red-700\",\n \"--vl-error-accented\": \"--color-red-800\",\n\n /* Grayscale colors */\n \"--vl-grayscale\": \"--vl-neutral-900\",\n \"--vl-grayscale-lifted\": \"--vl-neutral-800\",\n \"--vl-grayscale-accented\": \"--vl-neutral-700\",\n\n /* Neutral colors */\n \"--vl-neutral\": \"--vl-neutral-500\",\n \"--vl-neutral-lifted\": \"--vl-neutral-600\",\n \"--vl-neutral-accented\": \"--vl-neutral-700\",\n\n /* Text neutral colors */\n \"--vl-text-inverted\": \"--color-white\",\n \"--vl-text-muted\": \"--vl-neutral-400\",\n \"--vl-text-lifted\": \"--vl-neutral-500\",\n \"--vl-text-accented\": \"--vl-neutral-600\",\n \"--vl-text\": \"--vl-neutral-900\",\n\n /* Border neutral colors */\n \"--vl-border-muted\": \"--vl-neutral-200\",\n \"--vl-border\": \"--vl-neutral-300\",\n \"--vl-border-lifted\": \"--vl-neutral-400\",\n \"--vl-border-accented\": \"--vl-neutral-600\",\n\n /* Background neutral colors */\n \"--vl-bg\": \"--color-white\",\n \"--vl-bg-muted\": \"--vl-neutral-50\",\n \"--vl-bg-lifted\": \"--vl-neutral-100\",\n \"--vl-bg-accented\": \"--vl-neutral-200\",\n \"--vl-bg-inverted\": \"--vl-neutral-900\",\n },\n\n /**\n * Dark theme CSS variable settings.\n */\n darkTheme: {\n /* Primary colors */\n \"--vl-primary\": \"--vl-primary-400\",\n \"--vl-primary-lifted\": \"--vl-primary-500\",\n \"--vl-primary-accented\": \"--vl-primary-600\",\n\n /* Secondary colors */\n \"--vl-secondary\": \"--vl-neutral-300\",\n \"--vl-secondary-lifted\": \"--vl-neutral-400\",\n \"--vl-secondary-accented\": \"--vl-neutral-500\",\n\n /* Success colors */\n \"--vl-success\": \"--color-green-400\",\n \"--vl-success-lifted\": \"--color-green-500\",\n \"--vl-success-accented\": \"--color-green-600\",\n\n /* Info colors */\n \"--vl-info\": \"--color-blue-400\",\n \"--vl-info-lifted\": \"--color-blue-500\",\n \"--vl-info-accented\": \"--color-blue-600\",\n\n /* Notice colors */\n \"--vl-notice\": \"--color-violet-400\",\n \"--vl-notice-lifted\": \"--color-violet-500\",\n \"--vl-notice-accented\": \"--color-violet-600\",\n\n /* Warning colors */\n \"--vl-warning\": \"--color-orange-400\",\n \"--vl-warning-lifted\": \"--color-orange-500\",\n \"--vl-warning-accented\": \"--color-orange-600\",\n\n /* Error colors */\n \"--vl-error\": \"--color-red-400\",\n \"--vl-error-lifted\": \"--color-red-500\",\n \"--vl-error-accented\": \"--color-red-600\",\n\n /* Grayscale colors */\n \"--vl-grayscale\": \"--vl-neutral-100\",\n \"--vl-grayscale-lifted\": \"--vl-neutral-200\",\n \"--vl-grayscale-accented\": \"--vl-neutral-300\",\n\n /* Neutral colors */\n \"--vl-neutral\": \"--vl-neutral-300\",\n \"--vl-neutral-lifted\": \"--vl-neutral-400\",\n \"--vl-neutral-accented\": \"--vl-neutral-500\",\n\n /* Text neutral colors */\n \"--vl-text-inverted\": \"--vl-neutral-900\",\n \"--vl-text-muted\": \"--vl-neutral-600\",\n \"--vl-text-lifted\": \"--vl-neutral-400\",\n \"--vl-text-accented\": \"--vl-neutral-300\",\n \"--vl-text\": \"--vl-neutral-100\",\n\n /* Border neutral colors */\n \"--vl-border-muted\": \"--vl-neutral-800\",\n \"--vl-border\": \"--vl-neutral-700\",\n \"--vl-border-lifted\": \"--vl-neutral-600\",\n \"--vl-border-accented\": \"--vl-neutral-400\",\n\n /* Background neutral colors */\n \"--vl-bg\": \"--vl-neutral-900\",\n \"--vl-bg-muted\": \"--vl-neutral-800\",\n \"--vl-bg-lifted\": \"--vl-neutral-800\",\n \"--vl-bg-accented\": \"--vl-neutral-700\",\n \"--vl-bg-inverted\": \"--vl-neutral-100\",\n },\n};\n";
package/bin/constants.js CHANGED
@@ -1,6 +1,3 @@
1
- export const SRC_COMPONENTS_PATH = "/src/components";
2
- export const COMPONENTS_PATH = "/components";
3
-
4
1
  export const DEFAULT_VUELESS_CONFIG_CONTENT = `import { componentConfigs } from "./.vueless";
5
2
 
6
3
  export default {
package/bin/utils/data.js CHANGED
@@ -5,30 +5,26 @@ import { readFile } from "node:fs/promises";
5
5
 
6
6
  import { getDirFiles } from "../../utils/node/helper.js";
7
7
 
8
- import { SRC_COMPONENTS_PATH, COMPONENTS_PATH } from "../constants.js";
9
- import { VUELESS_PACKAGE_DIR } from "../../constants.js";
8
+ import { VUELESS_PACKAGE_DIR, VUELESS_USER_COMPONENTS_DIR } from "../../constants.js";
10
9
 
11
10
  const storiesName = "stories.ts";
12
11
 
12
+ /**
13
+ * Retrieves the next available Storybook ID by scanning story files for the highest existing ID and incrementing it.
14
+ *
15
+ * @return {Promise<number>} A promise that resolves to the next available Storybook ID.
16
+ */
13
17
  export async function getStorybookId() {
14
- const srcComponentsDir = path.join(cwd(), SRC_COMPONENTS_PATH);
15
- const componentsDir = path.join(cwd(), COMPONENTS_PATH);
16
18
  const vuelessPackagePath = path.join(cwd(), VUELESS_PACKAGE_DIR);
17
- const isSrcComponentsDir = existsSync(srcComponentsDir);
18
- const isComponentsDir = existsSync(componentsDir);
19
+ const vuelessUserComponentsPath = path.join(cwd(), VUELESS_USER_COMPONENTS_DIR);
19
20
 
20
21
  const stories = await getDirFiles(vuelessPackagePath, storiesName);
22
+ const hasVuelessUserComponentsDir = existsSync(vuelessUserComponentsPath);
21
23
 
22
- if (isSrcComponentsDir) {
23
- const srcComponentsDirStories = await getDirFiles(srcComponentsDir, storiesName);
24
+ if (hasVuelessUserComponentsDir) {
25
+ const customStories = await getDirFiles(vuelessUserComponentsPath, storiesName);
24
26
 
25
- stories.push(...srcComponentsDirStories);
26
- }
27
-
28
- if (isComponentsDir) {
29
- const componentsDirStories = await getDirFiles(componentsDir, storiesName);
30
-
31
- stories.push(...componentsDirStories);
27
+ stories.push(...customStories);
32
28
  }
33
29
 
34
30
  let id = 200000;
@@ -51,6 +47,14 @@ export async function getStorybookId() {
51
47
  return id + 10;
52
48
  }
53
49
 
50
+ /**
51
+ * Retrieves the line index within a file content where a specific key appears
52
+ * as a top-level property in an exported default object.
53
+ *
54
+ * @param {string} fileContent - The content of the file to search through.
55
+ * @param {string} key - The key to locate within the top-level export block.
56
+ * @return {number} The zero-based index of the line where the key is found, or -1 if the key is not found.
57
+ */
54
58
  export function getStoryMetaKeyIndex(fileContent, key) {
55
59
  const lines = fileContent.split("\n");
56
60
  let insideExportBlock = false;
@@ -2,6 +2,14 @@ import path from "node:path";
2
2
 
3
3
  import { VUELESS_LIBRARY } from "../../constants.js";
4
4
 
5
+ /**
6
+ * Replaces relative import paths in the provided file content based on the given component name and file path.
7
+ *
8
+ * @param {string} componentName - The name of the component, used to determine if the file is at the top level.
9
+ * @param {string} filePath - The file path to check if the file is located at the top level of the component.
10
+ * @param {string} fileContent - The content of the file where the relative import paths need to be replaced.
11
+ * @return {string} The updated file content with the adjusted relative import paths.
12
+ */
5
13
  export function replaceRelativeImports(componentName, filePath, fileContent) {
6
14
  const isTopLevelFile = path.dirname(filePath).endsWith(componentName);
7
15
  const contentLines = fileContent.split("\n");
@@ -9,6 +17,13 @@ export function replaceRelativeImports(componentName, filePath, fileContent) {
9
17
  return contentLines.map((line) => replaceRelativeLineImports(line, isTopLevelFile)).join("\n");
10
18
  }
11
19
 
20
+ /**
21
+ * Replaces relative import paths in a given line of code with a new base path for certain types of imports.
22
+ *
23
+ * @param {string} line - The line of code containing import statements to process.
24
+ * @param {boolean} isTopLevelFile - Indicates if the file is a top-level file within the project structure.
25
+ * @return {string} The modified line of code with adjusted import paths, or the original line if no changes are required.
26
+ */
12
27
  function replaceRelativeLineImports(line, isTopLevelFile) {
13
28
  const importRegex = /import\s+(?:[\w\s{},*]+)\s+from\s+(['"])(\.\.?\/.*?)(\.[tj]s)?\1(?!\?)/g;
14
29
  const multiLineImportRegExp = /from\s+(['"])(\.\.?\/.*?)(\.[tj]s)?\1(?!\?)/g; // Matches import's "from" part
@@ -23,8 +38,8 @@ function replaceRelativeLineImports(line, isTopLevelFile) {
23
38
  }
24
39
 
25
40
  if (line.startsWith("}")) {
26
- return line.replace(multiLineImportRegExp, (match, quote, oldPath, ext) => {
27
- return match.replace(oldPath + (ext || ""), VUELESS_LIBRARY);
41
+ return line.replace(multiLineImportRegExp, (match) => {
42
+ return match.replace(relativePathStartRegExp, `${VUELESS_LIBRARY}/`);
28
43
  });
29
44
  }
30
45
 
package/constants.d.ts CHANGED
@@ -290,14 +290,21 @@ export namespace TAILWIND_MERGE_EXTENSION {
290
290
  }
291
291
  }
292
292
  export namespace DEFAULT_SVGO_CONFIG {
293
- let plugins: {
293
+ let plugins: ({
294
+ name: string;
295
+ params: {
296
+ attrs: string;
297
+ attributes?: undefined;
298
+ };
299
+ } | {
294
300
  name: string;
295
301
  params: {
296
302
  attributes: {
297
303
  fill: string;
298
304
  }[];
305
+ attrs?: undefined;
299
306
  };
300
- }[];
307
+ })[];
301
308
  }
302
309
  export const INTERNAL_ENV: "internal";
303
310
  export const STORYBOOK_ENV: "storybook";
@@ -305,6 +312,7 @@ export const NUXT_MODULE_ENV: "nuxt-module";
305
312
  export const VUELESS_LIBRARY: "vueless";
306
313
  export const INTERNAL_ICONS_LIBRARY: "internal";
307
314
  export const STORYBOOK_ICONS_LIBRARY: "storybook";
315
+ export const SRC_DIR: "src";
308
316
  export const CACHE_DIR: ".cache";
309
317
  export const NODE_MODULES_DIR: "node_modules";
310
318
  export const VUELESS_PACKAGE_DIR: "node_modules/vueless";
@@ -321,6 +329,8 @@ export const VUELESS_MERGED_CONFIGS_CACHED_DIR: "node_modules/.cache/vueless/mer
321
329
  export const VUELESS_CONFIG_FILE_NAME: "vueless.config";
322
330
  export const CONFIG_INDEX_FILE_NAME: "index";
323
331
  export const VUELESS_CONFIG_DIR: ".vueless";
332
+ export const VUELESS_USER_COMPONENTS_DIR: ".vueless/components";
333
+ export const SRC_USER_COMPONENTS_DIR: "src/components";
324
334
  export const DEFAULT_EXIT_CODE: 0;
325
335
  export const FAILURE_CODE: 1;
326
336
  export const PX_IN_REM: 16;
package/constants.js CHANGED
@@ -367,6 +367,12 @@ export const TAILWIND_MERGE_EXTENSION = {
367
367
  /* SVGO config for preparing SVG icons. */
368
368
  export const DEFAULT_SVGO_CONFIG = {
369
369
  plugins: [
370
+ {
371
+ name: "removeAttrs",
372
+ params: {
373
+ attrs: "(fill)",
374
+ },
375
+ },
370
376
  {
371
377
  name: "addAttributesToSVGElement",
372
378
  params: {
@@ -385,11 +391,12 @@ export const VUELESS_LIBRARY = "vueless";
385
391
  export const INTERNAL_ICONS_LIBRARY = "internal";
386
392
  export const STORYBOOK_ICONS_LIBRARY = "storybook";
387
393
 
394
+ export const SRC_DIR = "src";
388
395
  export const CACHE_DIR = ".cache";
389
396
  export const NODE_MODULES_DIR = "node_modules";
390
397
  export const VUELESS_PACKAGE_DIR = `${NODE_MODULES_DIR}/vueless`;
391
398
  export const VUELESS_CACHE_DIR = `${NODE_MODULES_DIR}/${CACHE_DIR}/vueless`;
392
- export const VUELESS_LOCAL_DIR = "src";
399
+ export const VUELESS_LOCAL_DIR = SRC_DIR;
393
400
  export const ICONS_DIR = "icons";
394
401
  export const ICONS_VUELESS_DIR = `${VUELESS_PACKAGE_DIR}/${ICONS_DIR}`;
395
402
  export const ICONS_CACHED_DIR = `${VUELESS_CACHE_DIR}/${ICONS_DIR}`;
@@ -403,6 +410,8 @@ export const VUELESS_MERGED_CONFIGS_CACHED_DIR = `${VUELESS_CACHE_DIR}/mergedCon
403
410
  export const VUELESS_CONFIG_FILE_NAME = "vueless.config";
404
411
  export const CONFIG_INDEX_FILE_NAME = "index";
405
412
  export const VUELESS_CONFIG_DIR = ".vueless";
413
+ export const VUELESS_USER_COMPONENTS_DIR = `${VUELESS_CONFIG_DIR}/components`;
414
+ export const SRC_USER_COMPONENTS_DIR = `${SRC_DIR}/components`;
406
415
 
407
416
  /* System error codes */
408
417
  export const DEFAULT_EXIT_CODE = 0;
package/index.d.ts CHANGED
@@ -9,16 +9,16 @@ export {
9
9
  setTitle,
10
10
  getStored,
11
11
  getRandomId,
12
- createDebounce,
13
- hasSlotContent,
14
12
  getCookie,
15
13
  setCookie,
16
- deleteCookie
14
+ deleteCookie,
15
+ createDebounce,
16
+ hasSlotContent,
17
17
  } from "./utils/helper";
18
18
  export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
19
19
  export { cx, cva, compose, getDefaults, setVuelessConfig, setColor, vuelessConfig } from "./utils/ui";
20
20
  export { getTheme, setTheme, resetTheme, normalizeThemeConfig, cssVar } from "./utils/theme";
21
- export { getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
21
+ export { getArgs, getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
22
22
  /* adapters */
23
23
  export { default as defaultEnLocale } from "./adapter.locale/locales/en";
24
24
  export { createVuelessAdapter } from "./adapter.locale/vueless";
@@ -29,6 +29,8 @@ export { default as useUI } from "./composables/useUI";
29
29
  export { useDarkMode } from "./composables/useDarkMode";
30
30
  export { useLoaderProgress } from "./ui.loader-progress/useLoaderProgress";
31
31
  export { useMutationObserver } from "./composables/useMutationObserver";
32
+ export { Direction, useAutoPosition } from "./composables/useAutoPosition";
33
+ export { useComponentLocaleMessages } from "./composables/useComponentLocaleMassages";
32
34
  /* loaders */
33
35
  export { loaderProgressOn, loaderProgressOff } from "./ui.loader-progress/utilLoaderProgress";
34
36
  export { useLoaderOverlay } from "./ui.loader-overlay/useLoaderOverlay";
package/index.ts CHANGED
@@ -15,16 +15,16 @@ export {
15
15
  setTitle,
16
16
  getStored,
17
17
  getRandomId,
18
- createDebounce,
19
- hasSlotContent,
20
18
  getCookie,
21
19
  setCookie,
22
- deleteCookie
20
+ deleteCookie,
21
+ createDebounce,
22
+ hasSlotContent,
23
23
  } from "./utils/helper";
24
24
  export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
25
25
  export { cx, cva, compose, getDefaults, setVuelessConfig, setColor, vuelessConfig } from "./utils/ui";
26
26
  export { getTheme, setTheme, resetTheme, normalizeThemeConfig, cssVar } from "./utils/theme";
27
- export { getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
27
+ export { getArgs, getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
28
28
  /* adapters */
29
29
  export { default as defaultEnLocale } from "./adapter.locale/locales/en";
30
30
  export { createVuelessAdapter } from "./adapter.locale/vueless";
@@ -35,6 +35,8 @@ export { default as useUI } from "./composables/useUI";
35
35
  export { useDarkMode } from "./composables/useDarkMode";
36
36
  export { useLoaderProgress } from "./ui.loader-progress/useLoaderProgress";
37
37
  export { useMutationObserver } from "./composables/useMutationObserver";
38
+ export { Direction, useAutoPosition } from "./composables/useAutoPosition";
39
+ export { useComponentLocaleMessages } from "./composables/useComponentLocaleMassages";
38
40
  /* loaders */
39
41
  export { loaderProgressOn, loaderProgressOff } from "./ui.loader-progress/utilLoaderProgress";
40
42
  export { useLoaderOverlay } from "./ui.loader-overlay/useLoaderOverlay";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "1.2.5-beta.8",
3
+ "version": "1.2.5",
4
4
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
5
5
  "author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
6
6
  "homepage": "https://vueless.com",
@@ -56,7 +56,7 @@
56
56
  "@vue/eslint-config-typescript": "^14.6.0",
57
57
  "@vue/test-utils": "^2.4.6",
58
58
  "@vue/tsconfig": "^0.7.0",
59
- "@vueless/storybook": "^1.2.2",
59
+ "@vueless/storybook": "^1.2.4",
60
60
  "eslint": "^9.32.0",
61
61
  "eslint-plugin-storybook": "^9.0.18",
62
62
  "eslint-plugin-vue": "^10.3.0",
package/plugin-vite.js CHANGED
@@ -28,17 +28,19 @@ import {
28
28
  autoImportUserConfigs,
29
29
  } from "./utils/node/helper.js";
30
30
  import {
31
+ VUE_EXT,
32
+ JAVASCRIPT_EXT,
33
+ TYPESCRIPT_EXT,
31
34
  INTERNAL_ENV,
32
35
  STORYBOOK_ENV,
33
36
  NUXT_MODULE_ENV,
34
37
  VUELESS_LOCAL_DIR,
35
38
  VUELESS_PACKAGE_DIR,
39
+ SRC_USER_COMPONENTS_DIR,
40
+ VUELESS_USER_COMPONENTS_DIR,
36
41
  ICONS_VIRTUAL_MODULE_ID,
37
42
  RESOLVED_ICONS_VIRTUAL_MODULE_ID,
38
43
  DEFAULT_EXIT_CODE,
39
- JAVASCRIPT_EXT,
40
- TYPESCRIPT_EXT,
41
- VUE_EXT,
42
44
  } from "./constants.js";
43
45
 
44
46
  /* TailwindCSS Vite plugins. */
@@ -49,6 +51,7 @@ export const TailwindCSS = (options) => {
49
51
  /* Automatically importing Vueless components on demand */
50
52
  export const UnpluginComponents = (options) =>
51
53
  UnpluginVueComponents({
54
+ dirs: [VUELESS_USER_COMPONENTS_DIR, SRC_USER_COMPONENTS_DIR],
52
55
  resolvers: [componentResolver, directiveResolver],
53
56
  dts: true,
54
57
  ...options,
@@ -103,6 +106,11 @@ export const Vueless = function (options = {}) {
103
106
  define: {
104
107
  "process.env": {},
105
108
  },
109
+ build: {
110
+ rollupOptions: {
111
+ external: ["node:fs/promises"],
112
+ },
113
+ },
106
114
  optimizeDeps: {
107
115
  include: isInternalEnv
108
116
  ? []
@@ -35,5 +35,5 @@ const { getDataTest, wrapperAttrs } = useUI<Config>(defaultConfig, mutatedProps)
35
35
  </script>
36
36
 
37
37
  <template>
38
- <div v-bind="wrapperAttrs" :data-test="getDataTest()">Boilerplate</div>
38
+ <div v-bind="wrapperAttrs" :data-test="getDataTest()">UBoilerplate</div>
39
39
  </template>
@@ -7,7 +7,7 @@ export function getVueDirs(): string[];
7
7
  export function getVuelessConfigDirs(): string[];
8
8
  export function getMergedComponentConfig(name: any): Promise<any>;
9
9
  export function getDefaultComponentConfig(name: any, configDir: any): Promise<{}>;
10
- export function cacheMergedConfigs(srcDir: any): Promise<void>;
10
+ export function cacheMergedConfigs(basePath: any): Promise<void>;
11
11
  export function buildTSFile(entryPath: any, configOutFile: any): Promise<void>;
12
12
  export function removeFolderIfEmpty(dirPath: any): Promise<void>;
13
13
  export function detectTypeScript(): Promise<boolean>;
@@ -5,7 +5,7 @@ import { pathToFileURL } from "node:url";
5
5
  import { existsSync, statSync } from "node:fs";
6
6
  import { mkdir, readdir, rmdir, readFile, writeFile } from "node:fs/promises";
7
7
 
8
- import { vuelessConfig, getMergedConfig } from "./vuelessConfig.js";
8
+ import { getMergedConfig, getVuelessConfig } from "./vuelessConfig.js";
9
9
 
10
10
  import {
11
11
  COMPONENTS,
@@ -117,11 +117,12 @@ export async function getDefaultComponentConfig(name, configDir) {
117
117
  return config;
118
118
  }
119
119
 
120
- export async function cacheMergedConfigs(srcDir) {
120
+ export async function cacheMergedConfigs(basePath) {
121
+ const vuelessConfig = await getVuelessConfig(basePath);
121
122
  const componentNames = Object.entries(COMPONENTS);
122
123
 
123
124
  for await (const [componentName, componentDir] of componentNames) {
124
- const defaultComponentConfigPath = path.join(srcDir, componentDir, "config.ts");
125
+ const defaultComponentConfigPath = path.join(basePath, componentDir, "config.ts");
125
126
 
126
127
  const defaultConfig = await getDefaultComponentConfig(
127
128
  componentName,
@@ -17,14 +17,6 @@ import {
17
17
 
18
18
  export let vuelessConfig = {};
19
19
 
20
- /**
21
- * Load Vueless config from the project root.
22
- * IIFE is used to prevent top level await issue.
23
- */
24
- (async () => {
25
- vuelessConfig = await getVuelessConfig();
26
- })();
27
-
28
20
  /**
29
21
  * Retrieves the Vueless config from the project root.
30
22
  *
package/utils/ui.ts CHANGED
@@ -49,9 +49,14 @@ if (isSSR) {
49
49
  /* Load Tailwind config from the project root in IIFE (no top-level await). */
50
50
  (async () => {
51
51
  try {
52
+ const { readFile } = await import("node:fs/promises");
53
+
54
+ const path = `${process.cwd()}/${VUELESS_CACHE_DIR}/${VUELESS_CONFIG_FILE_NAME}.mjs`;
55
+ const code = await readFile(path, "utf8");
56
+
52
57
  vuelessConfig = (
53
58
  await import(
54
- /* @vite-ignore */ `${process.cwd()}/${VUELESS_CACHE_DIR}/${VUELESS_CONFIG_FILE_NAME}.mjs`
59
+ /* @vite-ignore */ `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`
55
60
  )
56
61
  ).default;
57
62
  } catch {