@vueless/storybook 0.0.53 → 0.0.55

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/storybook",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "description": "Simplifies Storybook configuration for Vueless UI library.",
5
5
  "homepage": "https://vueless.com",
6
6
  "author": "Johnny Grid",
@@ -35,9 +35,15 @@
35
35
  "@storybook/theming": "^8.4.7",
36
36
  "@storybook/vue3": "^8.4.7",
37
37
  "@storybook/vue3-vite": "^8.4.7",
38
+ "chokidar": "^4.0.3",
39
+ "esbuild": "^0.24.2",
40
+ "globby": "^14.0.2",
41
+ "minimist": "^1.2.8",
42
+ "mkdirp": "^3.0.1",
38
43
  "prettier2": "npm:prettier@2.8.8",
39
44
  "storybook": "^8.4.7",
40
- "storybook-dark-mode": "^4.0.2"
45
+ "storybook-dark-mode": "^4.0.2",
46
+ "vue-docgen-api": "^4.79.2"
41
47
  },
42
48
  "devDependencies": {
43
49
  "@release-it/bumper": "^6.0.1",
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import minimist from "minimist";
3
+ import { extractConfig } from "./lib/config.js";
4
+ import build from "./lib/build.js";
5
+
6
+ const {
7
+ _: pathArray,
8
+ configFile,
9
+ watch,
10
+ cwd,
11
+ } = minimist(process.argv.slice(2), {
12
+ alias: { c: "configFile", w: "watch" },
13
+ });
14
+
15
+ export default async function buildWebTypes(vuelessConfig) {
16
+ const conf = await extractConfig(cwd || process.cwd(), watch, configFile, pathArray);
17
+
18
+ build(conf, vuelessConfig);
19
+ }
@@ -0,0 +1,242 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { readFile } from "node:fs/promises";
4
+ import { mkdirp } from "mkdirp";
5
+ import chokidar from "chokidar";
6
+ import { globbySync } from "globby";
7
+ import { parse } from "vue-docgen-api";
8
+ import _ from "lodash-es";
9
+
10
+ export default async function build(config, vuelessConfig) {
11
+ config.componentsRoot = path.resolve(config.cwd, config.componentsRoot);
12
+ config.outFile = path.resolve(config.cwd, config.outFile);
13
+
14
+ const { watcher, componentFiles } = getSources(config.components, config.componentsRoot);
15
+
16
+ const cache = {};
17
+ const buildWebTypesBound = rebuild.bind(null, config, componentFiles, cache, vuelessConfig);
18
+
19
+ try {
20
+ await buildWebTypesBound();
21
+ } catch (e) {
22
+ // eslint-disable-next-line no-console
23
+ console.error("Error building web-types: " + e.message);
24
+ await watcher.close();
25
+
26
+ return;
27
+ }
28
+
29
+ if (config.watch) {
30
+ watcher
31
+ .on("add", buildWebTypesBound)
32
+ .on("change", buildWebTypesBound)
33
+ .on("unlink", async (filePath) => {
34
+ // eslint-disable-next-line no-console
35
+ console.log("Rebuilding on file removal " + filePath);
36
+
37
+ delete cache[filePath];
38
+ await writeDownWebTypesFile(config, Object.values(cache), config.outFile);
39
+ });
40
+ } else {
41
+ await watcher.close();
42
+ }
43
+ }
44
+
45
+ function getSources(components, cwd) {
46
+ const watcher = chokidar.watch(components, { cwd });
47
+ const allComponentFiles = globbySync(components, { cwd });
48
+
49
+ return { watcher, componentFiles: allComponentFiles };
50
+ }
51
+
52
+ async function rebuild(config, files, cachedContent, vuelessConfig) {
53
+ const cacheWebTypesContent = async (filePath) => {
54
+ cachedContent[filePath.replace(/\\/g, "/")] = await extractInformation(
55
+ filePath,
56
+ config,
57
+ vuelessConfig,
58
+ );
59
+
60
+ return true;
61
+ };
62
+
63
+ try {
64
+ // if we are initializing the current file, parse all components
65
+ await Promise.all(files.map(cacheWebTypesContent));
66
+ } catch (e) {
67
+ throw new Error(`Error building file ${config.outFile}: ${e.message}`);
68
+ }
69
+
70
+ // and finally, save all concatenated values to the Markdown file
71
+ await writeDownWebTypesFile(config, Object.values(cachedContent), config.outFile);
72
+ }
73
+
74
+ async function writeDownWebTypesFile(config, definitions, destFilePath) {
75
+ const destFolder = path.dirname(destFilePath);
76
+
77
+ await mkdirp(destFolder);
78
+ let writeStream = fs.createWriteStream(destFilePath);
79
+
80
+ const contents = {
81
+ framework: "vue",
82
+ name: config.packageName,
83
+ version: config.packageVersion,
84
+ contributions: {
85
+ html: {
86
+ "description-markup": config.descriptionMarkup,
87
+ "types-syntax": config.typesSyntax,
88
+ tags: _(definitions)
89
+ .flatMap((d) => d.tags || [])
90
+ .orderBy("name", "asc")
91
+ .value(),
92
+ attributes: _(definitions)
93
+ .flatMap((d) => d.attributes || [])
94
+ .orderBy("name", "asc")
95
+ .value(),
96
+ "vue-filters": _(definitions)
97
+ .flatMap((d) => d["vue-filters"] || [])
98
+ .orderBy("name", "asc")
99
+ .value(),
100
+ },
101
+ },
102
+ };
103
+
104
+ const html = contents.contributions.html;
105
+
106
+ if (!html.tags?.length) html.tags = undefined;
107
+ if (!html.attributes?.length) html.attributes = undefined;
108
+ if (!html["vue-filters"]?.length) html["vue-filters"] = undefined;
109
+
110
+ writeStream.write(JSON.stringify(contents, null, 2));
111
+ writeStream.close();
112
+ }
113
+
114
+ function ensureRelative(path) {
115
+ // The .replace() is a fix for paths that end up like "./src\\components\\General\\VerticalButton.vue" on windows machines.
116
+ return (path.startsWith("./") || path.startsWith("../") ? path : "./" + path).replace(/\\/g, "/");
117
+ }
118
+
119
+ export function getDefaultConfigJson(fileContents) {
120
+ const objectStartIndex = fileContents.indexOf("{");
121
+ const objectString = fileContents.substring(objectStartIndex).replace("};", "}");
122
+
123
+ // indirect eval
124
+ return (0, eval)("(" + objectString + ")"); // Converting into JS object
125
+ }
126
+
127
+ function getDefaultConfigFileName(folderPath) {
128
+ const folder = fs.readdirSync(path.dirname(folderPath));
129
+
130
+ return folder.find((file) => file === "config.js" || file === "config.ts") || "";
131
+ }
132
+
133
+ function getEnum(prop) {
134
+ let values = null;
135
+
136
+ if (prop.type?.elements) {
137
+ values = prop.type.elements.map((item) => item.name.replaceAll('"', ""));
138
+ }
139
+
140
+ if (prop.values) {
141
+ values = prop.values;
142
+ }
143
+
144
+ return values ? { enum: values } : {};
145
+ }
146
+
147
+ function getType(prop) {
148
+ return prop.type?.name ?? "any";
149
+ }
150
+
151
+ async function extractInformation(absolutePath, config, vuelessConfig) {
152
+ const doc = await parse(absolutePath, config.apiOptions);
153
+ const name = doc.name || doc.displayName;
154
+ let description = doc.description?.trim() ?? "";
155
+
156
+ const defaultConfigFileName = getDefaultConfigFileName(absolutePath);
157
+ let defaultConfig = {};
158
+
159
+ if (defaultConfigFileName) {
160
+ const defaultConfigPath = path.join(path.dirname(absolutePath), defaultConfigFileName);
161
+ const defaultConfigContent = await readFile(defaultConfigPath, { encoding: "utf-8" });
162
+
163
+ defaultConfig = getDefaultConfigJson(defaultConfigContent);
164
+ }
165
+
166
+ const globalConfigComponents = vuelessConfig?.component || {};
167
+
168
+ const defaults = _.merge(
169
+ defaultConfig?.defaults || {},
170
+ globalConfigComponents[name]?.defaults || {},
171
+ );
172
+
173
+ doc.docsBlocks?.forEach((block) => {
174
+ if (description.length > 0) {
175
+ if (config.descriptionMarkup === "html") {
176
+ description += "<br/><br/>";
177
+ } else {
178
+ description += "\n\n";
179
+ }
180
+ }
181
+
182
+ description += block;
183
+ });
184
+
185
+ const componentPath = ensureRelative(path.relative(config.cwd, absolutePath));
186
+ // Prevent "Chose declaration" duplication issue in Intellij
187
+ const source = !componentPath.includes("vueless")
188
+ ? { source: { module: componentPath, symbol: doc.exportName } }
189
+ : {};
190
+
191
+ return {
192
+ tags: [
193
+ {
194
+ name,
195
+ description,
196
+ attributes: doc.props?.map((prop) => ({
197
+ name: prop.name,
198
+ required: prop.required,
199
+ description: prop.tags?.ignore ? "@ignore: " + prop.description : prop.description,
200
+ ...getEnum(prop),
201
+ value: {
202
+ kind: "expression",
203
+ type: getType(prop),
204
+ },
205
+ default:
206
+ defaults && prop.name in defaults
207
+ ? defaults[prop.name]?.toString()
208
+ : prop.defaultValue?.value?.toString(),
209
+ })),
210
+ events: doc.events?.map((event) => ({
211
+ name: event.name,
212
+ description: event.description,
213
+ properties: event.properties?.map((property) => ({
214
+ type: property.type?.names,
215
+ name: property.name,
216
+ description: property.description,
217
+ })),
218
+ })),
219
+ slots: doc.slots?.map((slot) => ({
220
+ name: slot.name,
221
+ scoped: slot.scoped,
222
+ description: slot.description,
223
+ bindings: slot.bindings?.map((binding) => ({
224
+ type: binding.type?.name,
225
+ name: binding.name,
226
+ description: binding.description,
227
+ })),
228
+ })),
229
+ exposes: doc.expose?.map((expose) => ({
230
+ name: expose.name,
231
+ description: expose.description,
232
+ properties: expose.tags?.map((property) => ({
233
+ type: property.type?.name,
234
+ name: property.name,
235
+ description: property.description,
236
+ })),
237
+ })),
238
+ ...source,
239
+ },
240
+ ],
241
+ };
242
+ }
@@ -0,0 +1,69 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { readFile } from "fs/promises";
4
+ import esbuild from "esbuild";
5
+
6
+ const CACHE_PATH = "./node_modules/.cache/vueless";
7
+ const WEB_TYPES_CONFIG_FILE_NAME = "web-types.config";
8
+
9
+ export async function extractConfig(cwd, watch = false, configFileFromCmd) {
10
+ const fileContent = await readFile(path.join(cwd, "package.json"), "utf-8");
11
+ const packageJson = JSON.parse(fileContent);
12
+
13
+ const config = await getConfig(configFileFromCmd);
14
+
15
+ const components = config?.isVuelessEnv
16
+ ? [`${process.cwd()}/src/**/*.vue`]
17
+ : [
18
+ `${process.cwd()}/node_modules/vueless/**/*.vue`,
19
+ `${process.cwd()}/src/components/**/*.vue`,
20
+ ];
21
+
22
+ return {
23
+ cwd,
24
+ watch,
25
+ components,
26
+ componentsRoot: cwd,
27
+ outFile: `${CACHE_PATH}/web-types.json`,
28
+ packageName: packageJson["name"],
29
+ packageVersion: packageJson["version"],
30
+ descriptionMarkup: "markdown",
31
+ typesSyntax: "typescript",
32
+ ...config,
33
+ };
34
+ }
35
+
36
+ async function getConfig(configFromCmd) {
37
+ const configPathFromCmd = configFromCmd && path.resolve(process.cwd(), configFromCmd);
38
+ const configPathJs = path.resolve(process.cwd(), `${WEB_TYPES_CONFIG_FILE_NAME}.js`);
39
+ const configPathTs = path.resolve(process.cwd(), `${WEB_TYPES_CONFIG_FILE_NAME}.ts`);
40
+ const configOutPath = path.join(process.cwd(), `${CACHE_PATH}/${WEB_TYPES_CONFIG_FILE_NAME}.mjs`);
41
+
42
+ let config = {};
43
+
44
+ if (!fs.existsSync(configPathJs) && !fs.existsSync(configPathTs)) {
45
+ return config;
46
+ }
47
+
48
+ fs.existsSync(configPathJs) && (await buildConfig(configPathJs, configOutPath));
49
+ fs.existsSync(configPathTs) && (await buildConfig(configPathTs, configOutPath));
50
+ fs.existsSync(configPathFromCmd) && (await buildConfig(configPathFromCmd, configPathFromCmd));
51
+
52
+ if (fs.existsSync(configOutPath)) {
53
+ config = (await import(configOutPath)).default;
54
+ }
55
+
56
+ return config;
57
+ }
58
+
59
+ async function buildConfig(entryPath, configOutFile) {
60
+ await esbuild.build({
61
+ entryPoints: [entryPath],
62
+ outfile: configOutFile,
63
+ bundle: true,
64
+ platform: "node",
65
+ format: "esm",
66
+ target: "ESNext",
67
+ loader: { ".ts": "ts" },
68
+ });
69
+ }