@qds.dev/tools 0.9.1 → 0.9.7

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": "@qds.dev/tools",
3
- "version": "0.9.1",
3
+ "version": "0.9.7",
4
4
  "private": false,
5
5
  "description": "Tools and utilities for Qwik Design System",
6
6
  "type": "module",
@@ -34,6 +34,12 @@
34
34
  "types": "./lib-types/tools/playground/index.d.ts"
35
35
  }
36
36
  },
37
+ "scripts": {
38
+ "build": "pnpm run build.lib & pnpm run build.types && pnpm generate.icon.types",
39
+ "build.lib": "rolldown -c rolldown.config.ts",
40
+ "build.types": "tsc --emitDeclarationOnly --outDir ./lib-types",
41
+ "generate.icon.types": "node src/generate/icon-types.ts"
42
+ },
37
43
  "devDependencies": {
38
44
  "@iconify/types": "^2",
39
45
  "magic-string": "^0.30.17",
@@ -54,10 +60,8 @@
54
60
  "remark-mdx": "^3.1.1"
55
61
  },
56
62
  "sideEffects": false,
57
- "scripts": {
58
- "build": "pnpm run build.lib & pnpm run build.types && pnpm generate.icon.types",
59
- "build.lib": "rolldown -c rolldown.config.ts",
60
- "build.types": "tsc --emitDeclarationOnly --outDir ./lib-types",
61
- "generate.icon.types": "node src/generate/icon-types.ts"
63
+ "repository": {
64
+ "type": "git",
65
+ "url": "https://github.com/kunai-consulting/qwik-design-system"
62
66
  }
63
- }
67
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Kunai Consulting
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,500 +0,0 @@
1
- import { charIn, charNotIn, createRegExp, exactly, maybe, oneOrMore, whitespace } from "magic-regexp";
2
- import { parseSync } from "oxc-parser";
3
- import { walk } from "oxc-walker";
4
- import { existsSync, statSync } from "node:fs";
5
- import { dirname, join, relative } from "node:path";
6
- import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
7
-
8
- //#region docs/component-props.ts
9
- const JSDOC_START_PATTERN = createRegExp(exactly("/**"));
10
- const JSDOC_END_PATTERN = createRegExp(exactly("*/"));
11
- const JSDOC_PATTERN = createRegExp(exactly("/**"), charNotIn("*").or(exactly("*").notAfter(exactly("/"))).times.any(), exactly("*/"));
12
- const componentProps = (options) => {
13
- const { componentsDir, outputDir, debug: isDebugMode = false } = options;
14
- const debug = (message, ...data) => {
15
- if (!isDebugMode) return;
16
- console.log(`[component-props] ${message}`, ...data);
17
- };
18
- return {
19
- name: "vite-plugin-qds-component-props",
20
- enforce: "pre",
21
- configResolved() {
22
- debug("Component props plugin initialized");
23
- generateAllComponentMetadata(componentsDir, outputDir, debug).catch((error) => {
24
- console.error("[component-props] Error generating metadata:", error);
25
- });
26
- },
27
- async handleHotUpdate(ctx) {
28
- if (!ctx.file.includes(componentsDir) || !ctx.file.endsWith(".tsx")) return;
29
- const componentName = extractComponentName(ctx.file, componentsDir);
30
- if (!componentName) return;
31
- await generateComponentMetadata(componentName, componentsDir, outputDir, debug);
32
- }
33
- };
34
- };
35
- async function generateAllComponentMetadata(componentsDir, outputDir, debug) {
36
- const componentDirs = await findComponentDirs(componentsDir);
37
- await Promise.all(componentDirs.map(async (componentName) => generateComponentMetadata(componentName, componentsDir, outputDir, debug)));
38
- }
39
- async function generateComponentMetadata(componentName, componentsDir, outputDir, debug) {
40
- try {
41
- debug(`Generating metadata for component: ${componentName}`);
42
- const pieces = await parseComponentPieces(componentName, componentsDir, debug);
43
- debug(`Found ${pieces.length} pieces for ${componentName}:`, pieces.map((p) => p.name));
44
- const metadata = {
45
- componentName,
46
- pieces
47
- };
48
- const outputPath = join(outputDir, `${componentName}.json`);
49
- await mkdir(dirname(outputPath), { recursive: true });
50
- await writeFile(outputPath, JSON.stringify(metadata, null, 2), "utf-8");
51
- debug(`Wrote metadata to ${outputPath}`);
52
- } catch (error) {
53
- debug(`Error generating metadata for ${componentName}:`, error);
54
- console.error(`[component-props] Error generating metadata for ${componentName}:`, error);
55
- }
56
- }
57
- async function parseComponentPieces(componentName, componentsDir, debug) {
58
- const componentPath = join(componentsDir, componentName);
59
- debug(`Parsing pieces for ${componentName}`);
60
- if (!existsSync(componentPath)) {
61
- debug(`Component path does not exist: ${componentPath}`);
62
- return [];
63
- }
64
- const files = await readdir(componentPath);
65
- debug(`Found ${files.length} files in directory:`, files);
66
- const pieceFiles = files.filter((file) => isPieceFile(file, debug));
67
- debug(`Filtered to ${pieceFiles.length} piece files:`, pieceFiles);
68
- const piecePromises = pieceFiles.map(async (file) => {
69
- const filePath = join(componentPath, file);
70
- if (!statSync(filePath).isFile()) {
71
- debug(`Skipping ${filePath} (not a file)`);
72
- return null;
73
- }
74
- const pieceName = extractPieceName(file, componentName);
75
- debug(`Parsing piece file: ${file} -> piece name: ${pieceName}`);
76
- const props = await parseComponentFile(filePath, pieceName, debug);
77
- if (props.length === 0) {
78
- debug(`Skipping ${pieceName} (no props)`);
79
- return null;
80
- }
81
- debug(`Found ${props.length} props in ${pieceName}`);
82
- return {
83
- name: pieceName,
84
- props
85
- };
86
- });
87
- const pieces = (await Promise.all(piecePromises)).filter((piece) => piece !== null);
88
- pieces.sort((a, b) => {
89
- if (a.name === "Root") return -1;
90
- if (b.name === "Root") return 1;
91
- return a.name.localeCompare(b.name);
92
- });
93
- debug(`Final pieces for ${componentName}:`, pieces.map((p) => p.name));
94
- return pieces;
95
- }
96
- function isPieceFile(file, debug) {
97
- if (!file.endsWith(".tsx")) {
98
- debug(`Skipping ${file} (not .tsx)`);
99
- return false;
100
- }
101
- if (file.endsWith("-context.tsx")) {
102
- debug(`Skipping ${file} (is -context.tsx)`);
103
- return false;
104
- }
105
- if (file.includes(".browser.")) {
106
- debug(`Skipping ${file} (contains .browser.)`);
107
- return false;
108
- }
109
- debug(`Including ${file} as piece file`);
110
- return true;
111
- }
112
- function extractPieceName(file, componentName) {
113
- return file.replace(`${componentName}-`, "").replace(".tsx", "").split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
114
- }
115
- async function parseComponentFile(filePath, pieceName, debug) {
116
- debug(`parseComponentFile: ${filePath} (piece: ${pieceName})`);
117
- const source = await readFile(filePath, "utf-8");
118
- const ast = parseSync(filePath, source);
119
- if (ast.errors.length > 0) {
120
- debug(`Parse errors in ${filePath}:`, ast.errors);
121
- return [];
122
- }
123
- const typeAliasCache = buildTypeAliasCache(ast.program);
124
- const analysisResult = analyzeProgram(ast.program, debug, typeAliasCache);
125
- if (!analysisResult.propsType) {
126
- debug(`No props type found in ${filePath}`);
127
- return [];
128
- }
129
- debug(`Bindable props:`, Array.from(analysisResult.bindableProps));
130
- const typeLiterals = /* @__PURE__ */ new Set();
131
- const bindableInlineTypes = /* @__PURE__ */ new Set();
132
- collectTypeLiterals(analysisResult.propsType, typeAliasCache, typeLiterals);
133
- collectBindableInlineTypes(analysisResult.propsType, bindableInlineTypes);
134
- for (const bindableType of bindableInlineTypes) typeLiterals.add(bindableType);
135
- for (const bindableType of analysisResult.aliasBindableTypes) {
136
- bindableInlineTypes.add(bindableType);
137
- typeLiterals.add(bindableType);
138
- }
139
- const propsMap = /* @__PURE__ */ new Map();
140
- for (const typeLiteral of typeLiterals) {
141
- const isFromBindable = bindableInlineTypes.has(typeLiteral);
142
- extractPropsFromType(typeLiteral, source, analysisResult.bindableProps, propsMap, typeAliasCache, isFromBindable);
143
- }
144
- if (analysisResult.bindableTypeName) {
145
- const bindableTypeAlias = typeAliasCache.get(analysisResult.bindableTypeName);
146
- if (bindableTypeAlias) {
147
- const typeAnnotation = "typeAnnotation" in bindableTypeAlias ? bindableTypeAlias.typeAnnotation : null;
148
- if (typeAnnotation?.type === "TSTypeLiteral") extractPropsFromType(typeAnnotation, source, analysisResult.bindableProps, propsMap, typeAliasCache, true);
149
- else if (typeAnnotation?.type === "TSTypeReference") {
150
- const resolvedType = resolveTypeAliasCached(typeAnnotation, typeAliasCache);
151
- if (resolvedType?.type === "TSTypeLiteral") extractPropsFromType(resolvedType, source, analysisResult.bindableProps, propsMap, typeAliasCache, true);
152
- }
153
- }
154
- }
155
- const finalProps = Array.from(propsMap.values());
156
- debug(`Extracted ${finalProps.length} props from ${filePath}`);
157
- return finalProps;
158
- }
159
- function buildTypeAliasCache(program) {
160
- const cache = /* @__PURE__ */ new Map();
161
- walk(program, { enter(node) {
162
- if (node.type === "TSTypeAliasDeclaration") {
163
- const typeName = getTypeName(node);
164
- if (typeName) cache.set(typeName, node);
165
- }
166
- } });
167
- return cache;
168
- }
169
- function analyzeProgram(program, debug, typeAliasCache) {
170
- let propsType = null;
171
- const bindableProps = /* @__PURE__ */ new Set();
172
- let bindableTypeName = null;
173
- const aliasBindableTypes = [];
174
- let foundComponentCall = false;
175
- walk(program, { enter(node) {
176
- if (!foundComponentCall && node.type === "CallExpression") {
177
- const callee = "callee" in node ? node.callee : null;
178
- if (callee) {
179
- if (callee.type === "Identifier" && "name" in callee && callee.name === "component$" || callee.type === "MemberExpression" && "property" in callee && callee.property && typeof callee.property === "object" && "name" in callee.property && callee.property.name === "component$") {
180
- foundComponentCall = true;
181
- const firstArg = ("arguments" in node ? node.arguments : [])[0];
182
- if (firstArg && typeof firstArg === "object" && firstArg.type === "ArrowFunctionExpression") {
183
- if ("typeArguments" in node && node.typeArguments) {
184
- const typeArgs = node.typeArguments;
185
- if (Array.isArray(typeArgs.params) && typeArgs.params.length > 0) {
186
- const firstTypeArg = typeArgs.params[0];
187
- if (firstTypeArg && typeof firstTypeArg === "object" && "type" in firstTypeArg) {
188
- debug(`Found component$ call with generic type parameter`);
189
- propsType = firstTypeArg;
190
- return;
191
- }
192
- }
193
- }
194
- const firstParam = ("params" in firstArg ? firstArg.params : [])[0];
195
- if (firstParam && typeof firstParam === "object") {
196
- const typeAnnotation = "typeAnnotation" in firstParam ? firstParam.typeAnnotation : null;
197
- if (typeAnnotation && typeof typeAnnotation === "object") {
198
- const paramType = "typeAnnotation" in typeAnnotation ? typeAnnotation.typeAnnotation : null;
199
- if (paramType && typeof paramType === "object") {
200
- debug(`Found component$ call with props type`);
201
- propsType = paramType;
202
- return;
203
- }
204
- }
205
- }
206
- }
207
- }
208
- }
209
- }
210
- if (node.type === "TSTypeAliasDeclaration") {
211
- const typeName = getTypeName(node);
212
- if (!typeName) return;
213
- const typeAnnotation = "typeAnnotation" in node ? node.typeAnnotation : null;
214
- if (!typeAnnotation) return;
215
- if (!propsType && typeName.endsWith("Props")) {
216
- debug(`Found *Props type: ${typeName}`);
217
- if ("type" in typeAnnotation) propsType = typeAnnotation;
218
- }
219
- if (typeName.endsWith("Props") && typeAnnotation.type === "TSIntersectionType") {
220
- const types = "types" in typeAnnotation ? typeAnnotation.types : [];
221
- for (const type of types) {
222
- if (type.type !== "TSTypeReference") continue;
223
- const typeNameNode = "typeName" in type ? type.typeName : null;
224
- if (!typeNameNode || !("name" in typeNameNode) || typeNameNode.name !== "BindableProps") continue;
225
- const firstParam = getTypeParameters(type)[0];
226
- if (firstParam && typeof firstParam === "object" && firstParam !== null && "type" in firstParam) {
227
- if (firstParam.type === "TSTypeReference") {
228
- const refTypeName = firstParam.typeName?.name;
229
- if (typeof refTypeName === "string") bindableTypeName = refTypeName;
230
- } else if (firstParam.type === "TSTypeLiteral") aliasBindableTypes.push(firstParam);
231
- }
232
- }
233
- }
234
- if (bindableTypeName && typeName === bindableTypeName) {
235
- if (typeAnnotation.type === "TSTypeLiteral") extractPropNames(typeAnnotation, bindableProps);
236
- else if (typeAnnotation.type === "TSTypeReference") {
237
- const resolvedType = resolveTypeAliasCached(typeAnnotation, typeAliasCache);
238
- if (resolvedType?.type === "TSTypeLiteral") extractPropNames(resolvedType, bindableProps);
239
- }
240
- }
241
- }
242
- } });
243
- return {
244
- propsType,
245
- bindableProps,
246
- bindableTypeName,
247
- aliasBindableTypes
248
- };
249
- }
250
- function resolveTypeAliasCached(type, typeAliasCache) {
251
- if (type.type !== "TSTypeReference") return null;
252
- const typeName = getTypeReferenceName(type);
253
- if (!typeName) return null;
254
- const aliasNode = typeAliasCache.get(typeName);
255
- if (!aliasNode) return null;
256
- const typeAnnotation = "typeAnnotation" in aliasNode ? aliasNode.typeAnnotation : null;
257
- if (typeAnnotation && "type" in typeAnnotation && typeAnnotation.type !== "TSTypeAnnotation") return typeAnnotation;
258
- return null;
259
- }
260
- function collectTypeLiterals(type, typeAliasCache, collected) {
261
- if (!type) return;
262
- if (type.type === "TSTypeLiteral") {
263
- collected.add(type);
264
- return;
265
- }
266
- if (type.type === "TSIntersectionType" && "types" in type) {
267
- for (const intersectionType of type.types) collectTypeLiterals(intersectionType, typeAliasCache, collected);
268
- return;
269
- }
270
- if (type.type === "TSTypeReference") {
271
- const resolved = resolveTypeAliasCached(type, typeAliasCache);
272
- if (resolved) collectTypeLiterals(resolved, typeAliasCache, collected);
273
- }
274
- }
275
- function collectBindableInlineTypes(type, collected) {
276
- if (!type) return;
277
- if (type.type === "TSIntersectionType" && "types" in type) for (const intersectionType of type.types) {
278
- if (intersectionType.type === "TSTypeReference") {
279
- if (getTypeReferenceName(intersectionType) === "BindableProps") {
280
- const firstParam = getTypeParameters(intersectionType)[0];
281
- if (firstParam && typeof firstParam === "object" && firstParam !== null && "type" in firstParam && firstParam.type === "TSTypeLiteral") collected.add(firstParam);
282
- }
283
- }
284
- collectBindableInlineTypes(intersectionType, collected);
285
- }
286
- }
287
- function getTypeName(node) {
288
- if (node.type !== "TSTypeAliasDeclaration") return "";
289
- if (!("id" in node) || !("name" in node.id)) return "";
290
- return node.id.name;
291
- }
292
- function getTypeReferenceName(type) {
293
- if (type.type !== "TSTypeReference") return "";
294
- if (!("typeName" in type)) return "";
295
- const typeName = type.typeName;
296
- if (!typeName || !("name" in typeName) || typeof typeName.name !== "string") return "";
297
- return typeName.name;
298
- }
299
- function getTypeParameters(type) {
300
- if (type.type === "TSTypeReference") {
301
- if ("typeArguments" in type && type.typeArguments) {
302
- const typeArgs = type.typeArguments;
303
- if (Array.isArray(typeArgs.params)) return typeArgs.params;
304
- }
305
- return [];
306
- }
307
- if (!("typeParameters" in type) || !type.typeParameters || typeof type.typeParameters !== "object" || !("params" in type.typeParameters) || !Array.isArray(type.typeParameters.params)) return [];
308
- return type.typeParameters.params;
309
- }
310
- function extractPropNames(typeLiteral, props) {
311
- if (!("members" in typeLiteral)) return;
312
- for (const member of typeLiteral.members) {
313
- if (member.type !== "TSPropertySignature") continue;
314
- const key = getPropertyKey(member);
315
- if (key) props.add(key);
316
- }
317
- }
318
- function extractPropsFromType(typeLiteral, source, bindableProps, propsMap, typeAliasCache, forceBindable = false) {
319
- if (!("members" in typeLiteral)) return;
320
- for (const member of typeLiteral.members) {
321
- if (member.type !== "TSPropertySignature") continue;
322
- const prop = extractPropFromSignature(member, source, bindableProps, typeAliasCache, forceBindable);
323
- if (prop) {
324
- const existing = propsMap.get(prop.name);
325
- if (existing) {
326
- if (prop.isBindable) existing.isBindable = true;
327
- if (prop.comment && !existing.comment) existing.comment = prop.comment;
328
- if (prop.type !== "unknown" && existing.type === "unknown") {
329
- existing.type = prop.type;
330
- existing.unionValues = prop.unionValues;
331
- existing.isFunction = prop.isFunction;
332
- existing.initialValue = prop.initialValue;
333
- }
334
- } else propsMap.set(prop.name, prop);
335
- }
336
- }
337
- }
338
- function extractPropFromSignature(signature, source, bindableProps, typeAliasCache, forceBindable = false) {
339
- const key = getPropertyKey(signature);
340
- if (!key || key.startsWith("_")) return null;
341
- const typeAnnotation = "typeAnnotation" in signature ? signature.typeAnnotation : null;
342
- if (!typeAnnotation) return null;
343
- const type = typeAnnotation.typeAnnotation;
344
- const jsdoc = extractJSDoc(signature, source, key);
345
- const propType = parseType(type, source, typeAliasCache);
346
- return {
347
- name: key,
348
- type: propType.type,
349
- unionValues: propType.unionValues,
350
- isBindable: forceBindable || bindableProps.has(key),
351
- isFunction: propType.isFunction,
352
- initialValue: propType.initialValue,
353
- comment: jsdoc.comment,
354
- scenario: jsdoc.scenario
355
- };
356
- }
357
- function getPropertyKey(signature) {
358
- const keyNode = "key" in signature ? signature.key : null;
359
- if (!keyNode || !("name" in keyNode)) return null;
360
- if (typeof keyNode.name === "string") return keyNode.name;
361
- return keyNode.name.name;
362
- }
363
- function extractJSDoc(node, source, propName) {
364
- let commentValue = null;
365
- if ("leadingComments" in node && node.leadingComments && Array.isArray(node.leadingComments)) for (const comment of node.leadingComments) {
366
- if (typeof comment !== "object" || comment === null || !("type" in comment) || !("value" in comment)) continue;
367
- const commentTyped = comment;
368
- if (commentTyped.type !== "CommentBlock") continue;
369
- const value = commentTyped.value;
370
- if (typeof value === "string" && value.startsWith("*")) {
371
- commentValue = value;
372
- break;
373
- }
374
- }
375
- if (!commentValue && propName) {
376
- const propPattern = createRegExp(maybe(whitespace), exactly(propName), maybe(whitespace.times.any()), charIn("?:"));
377
- const propPatternGlobal = new RegExp(propPattern.source, "g");
378
- let match;
379
- while ((match = propPatternGlobal.exec(source)) !== null) {
380
- const propStart = match.index;
381
- const beforeProp = source.slice(Math.max(0, propStart - 500), propStart);
382
- const matches = Array.from(beforeProp.matchAll(new RegExp(JSDOC_PATTERN.source, "g")));
383
- if (matches.length > 0) {
384
- const lastMatch = matches[matches.length - 1];
385
- if (beforeProp.slice(lastMatch.index + lastMatch[0].length).trim() === "") {
386
- commentValue = lastMatch[0].replace(JSDOC_START_PATTERN, "").replace(JSDOC_END_PATTERN, "");
387
- break;
388
- }
389
- }
390
- }
391
- }
392
- if (!commentValue) return {};
393
- const lines = commentValue.split("\n").map((line) => {
394
- return line.replace(/^\s*\*?\s?/, "").trim();
395
- }).filter((line) => line !== "" && line !== "*");
396
- const scenario = lines.find((line) => line.startsWith("@scenario"))?.replace("@scenario", "").trim();
397
- return {
398
- comment: lines.filter((line) => line && !line.startsWith("@")).join(" ").trim() || void 0,
399
- scenario: scenario || void 0
400
- };
401
- }
402
- function parseType(type, _source, typeAliasCache) {
403
- if (type.type === "TSUnionType") return parseUnionType(type, typeAliasCache);
404
- if (type.type === "TSBooleanKeyword") return {
405
- type: "boolean",
406
- initialValue: false
407
- };
408
- if (type.type === "TSStringKeyword") return {
409
- type: "string",
410
- initialValue: ""
411
- };
412
- if (type.type === "TSNumberKeyword") return {
413
- type: "number",
414
- initialValue: 0
415
- };
416
- if (type.type === "TSLiteralType") return parseLiteralType(type);
417
- if (type.type === "TSFunctionType") return {
418
- type: "function",
419
- isFunction: true
420
- };
421
- if (type.type === "TSTypeReference") {
422
- if (getTypeReferenceName(type) === "QRL") return {
423
- type: "function",
424
- isFunction: true
425
- };
426
- const resolvedType = resolveTypeAliasCached(type, typeAliasCache);
427
- if (resolvedType) return parseType(resolvedType, _source, typeAliasCache);
428
- }
429
- return { type: "unknown" };
430
- }
431
- function parseUnionType(type, typeAliasCache) {
432
- if (type.type !== "TSUnionType" || !("types" in type)) return { type: "unknown" };
433
- const unionTypes = type.types;
434
- const stringLiterals = [];
435
- for (let i = 0; i < unionTypes.length; i++) {
436
- let unionType = unionTypes[i];
437
- if (unionType.type === "TSTypeReference") {
438
- const resolvedType = resolveTypeAliasCached(unionType, typeAliasCache);
439
- if (resolvedType) {
440
- if (resolvedType.type === "TSUnionType") {
441
- const nestedResult = parseUnionType(resolvedType, typeAliasCache);
442
- if (nestedResult.type === "union" && nestedResult.unionValues) stringLiterals.push(...nestedResult.unionValues);
443
- continue;
444
- }
445
- unionType = resolvedType;
446
- }
447
- }
448
- if (unionType.type === "TSLiteralType" && "literal" in unionType) {
449
- const literal = unionType.literal;
450
- if (literal && typeof literal === "object" && "type" in literal && "value" in literal) {
451
- const literalValue = literal.value;
452
- if (typeof literalValue === "string") stringLiterals.push(literalValue);
453
- }
454
- }
455
- }
456
- const hasBoolean = unionTypes.some((t) => {
457
- if (t.type === "TSBooleanKeyword") return true;
458
- if (t.type === "TSLiteralType" && "literal" in t) {
459
- const literal = t.literal;
460
- if (literal && typeof literal === "object" && "value" in literal) return typeof literal.value === "boolean";
461
- }
462
- return false;
463
- });
464
- const unionValues = [];
465
- if (hasBoolean) unionValues.push("false", "true");
466
- if (stringLiterals.length > 0) unionValues.push(...stringLiterals);
467
- if (unionValues.length > 0) return {
468
- type: "union",
469
- unionValues
470
- };
471
- return { type: "unknown" };
472
- }
473
- function parseLiteralType(type) {
474
- if (type.type !== "TSLiteralType" || !("literal" in type)) return { type: "unknown" };
475
- const literal = type.literal;
476
- if (!literal || typeof literal !== "object" || !("type" in literal) || !("value" in literal)) return { type: "unknown" };
477
- const literalValue = literal.value;
478
- if (typeof literalValue === "string") return {
479
- type: "union",
480
- unionValues: [literalValue]
481
- };
482
- if (typeof literalValue === "boolean") return {
483
- type: "boolean",
484
- initialValue: literalValue
485
- };
486
- return { type: "unknown" };
487
- }
488
- function extractComponentName(filePath, componentsDir) {
489
- const relativePath = relative(componentsDir, filePath);
490
- const componentNamePattern = createRegExp(oneOrMore(charNotIn("/")).groupedAs("componentName"), exactly("/"));
491
- return relativePath.match(componentNamePattern)?.groups?.componentName ?? null;
492
- }
493
- async function findComponentDirs(componentsDir) {
494
- const components = (await readdir(componentsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
495
- if (components.length === 0) throw new Error(`No component directories found in ${componentsDir}`);
496
- return components;
497
- }
498
-
499
- //#endregion
500
- export { componentProps, extractJSDoc, generateAllComponentMetadata, generateComponentMetadata };
@@ -1,3 +0,0 @@
1
- import { componentProps, extractJSDoc, generateAllComponentMetadata, generateComponentMetadata } from "./component-props.qwik.mjs";
2
-
3
- export { componentProps, extractJSDoc, generateAllComponentMetadata, generateComponentMetadata };
@@ -1,153 +0,0 @@
1
- import MagicString from "magic-string";
2
- import { parseSync } from "oxc-parser";
3
- import { walk } from "oxc-walker";
4
-
5
- //#region playground/generate-jsx.ts
6
- function generateJsx({ metadata, selectedPieceName, propValues, isBindProps, baseSource }) {
7
- const componentName = metadata.componentName.charAt(0).toUpperCase() + metadata.componentName.slice(1);
8
- const getPieceTagName = (pieceName) => pieceName === "Root" ? `${componentName}.Root` : `${componentName}.${pieceName}`;
9
- if (!baseSource) {
10
- const piece = metadata.pieces.find((p) => p.name === selectedPieceName) || metadata.pieces[0];
11
- if (!piece) return "No piece found";
12
- const pieceTagName = getPieceTagName(piece.name);
13
- const values = propValues[piece.name] || {};
14
- const props = [];
15
- for (const prop of piece.props) {
16
- const value = values[prop.name];
17
- if (value === prop.initialValue) continue;
18
- if ((prop.type === "boolean" || prop.type === "union") && (value === false || value === "false") && prop.initialValue === void 0) continue;
19
- if (prop.type === "string" && value === "" && prop.initialValue === void 0) continue;
20
- if (prop.type === "number" && value === 0 && prop.initialValue === void 0) continue;
21
- if (prop.isBindable && isBindProps) props.push(`bind:${prop.name}={signal}`);
22
- else if (value === "true" || value === true) props.push(prop.name);
23
- else if (value === "false" || value === false) props.push(`${prop.name}={false}`);
24
- else if (typeof value === "string") props.push(`${prop.name}="${value}"`);
25
- else if (typeof value === "number") props.push(`${prop.name}={${value}}`);
26
- else if (value !== void 0) props.push(`${prop.name}={${JSON.stringify(value)}}`);
27
- }
28
- const propsString = props.length > 0 ? ` ${props.join(" ")}` : "";
29
- if (piece.name === "Root") return `<${pieceTagName}${propsString}>Click me</${pieceTagName}>`;
30
- return `<${pieceTagName}${propsString} />`;
31
- }
32
- const s = new MagicString(baseSource);
33
- let ast;
34
- try {
35
- ast = parseSync("source.tsx", baseSource);
36
- } catch (e) {
37
- console.warn("generateJsx: Failed to parse", e);
38
- return baseSource;
39
- }
40
- if (ast.errors.length > 0) return baseSource;
41
- let componentBodyStart = -1;
42
- let importsEnd = 0;
43
- let qwikImportEnd = -1;
44
- let existingUseSignal = false;
45
- walk(ast.program, { enter(node) {
46
- if (node.type === "ImportDeclaration") {
47
- if (node.end > importsEnd) importsEnd = node.end;
48
- const source = node.source.value;
49
- if (source.includes("props-playground") || source.includes("ContextId")) s.remove(node.start, node.end);
50
- if (source === "@qwik.dev/core") {
51
- qwikImportEnd = node.end;
52
- if (node.specifiers) {
53
- node.specifiers.forEach((spec) => {
54
- if (spec.local.name === "useSignal") existingUseSignal = true;
55
- });
56
- if (node.specifiers.length > 0) qwikImportEnd = node.specifiers[node.specifiers.length - 1].end;
57
- }
58
- }
59
- }
60
- if (node.type === "JSXElement") {
61
- const opening = node.openingElement;
62
- let tagName = "";
63
- if (opening.name.type === "JSXIdentifier") tagName = opening.name.name;
64
- if (tagName === "PlaygroundState") s.remove(node.start, node.end);
65
- }
66
- if (node.type === "ExportDefaultDeclaration") {
67
- if (node.declaration.type === "CallExpression" && node.declaration.callee.type === "Identifier" && node.declaration.callee.name === "component$") {
68
- const arg = node.declaration.arguments[0];
69
- if (arg && arg.type === "ArrowFunctionExpression" && (arg.body.type === "FunctionBody" || arg.body.type === "BlockStatement")) componentBodyStart = arg.body.start + 1;
70
- }
71
- }
72
- if (node.type === "ExportNamedDeclaration" && node.declaration && node.declaration.type === "VariableDeclaration") {
73
- const decl = node.declaration;
74
- if (decl.declarations[0].id.type === "Identifier" && decl.declarations[0].id.name === "PlaygroundState") s.remove(node.start, node.end);
75
- }
76
- } });
77
- for (const piece of metadata.pieces) {
78
- const pieceTagName = getPieceTagName(piece.name);
79
- const values = propValues[piece.name] || {};
80
- const openTags = [];
81
- walk(ast.program, { enter(node) {
82
- if (node.type === "JSXOpeningElement") {
83
- let name = "";
84
- if (node.name.type === "JSXIdentifier") name = node.name.name;
85
- else if (node.name.type === "JSXMemberExpression") name = `${node.name.object.name}.${node.name.property.name}`;
86
- if (name === pieceTagName) openTags.push(node);
87
- }
88
- } });
89
- for (const openTag of openTags) {
90
- const definedProps = /* @__PURE__ */ new Set();
91
- openTag.attributes.forEach((attr) => {
92
- if (attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier") definedProps.add(attr.name.name);
93
- });
94
- const propsToInject = [];
95
- for (const prop of piece.props) {
96
- const value = values[prop.name];
97
- if (prop.type === "function") {
98
- const isEnabled = value === true || value === "true";
99
- const signalName = prop.name.replace(/\$$/, "").replace(/^on/, "").toLowerCase() + "Count";
100
- if (isEnabled) {
101
- if (!definedProps.has(prop.name)) {
102
- if (!existingUseSignal) {
103
- if (qwikImportEnd !== -1) s.appendLeft(qwikImportEnd, ", useSignal");
104
- else s.prepend("import { useSignal } from \"@qwik.dev/core\";\n");
105
- existingUseSignal = true;
106
- }
107
- if (componentBodyStart !== -1) {
108
- if (!baseSource.includes(`const ${signalName} = useSignal`)) s.appendRight(componentBodyStart, `\n const ${signalName} = useSignal(0);`);
109
- }
110
- propsToInject.push(`${prop.name}={${"() => " + signalName + ".value++"}}`);
111
- if (openTag.selfClosing) {
112
- s.overwrite(openTag.end - 2, openTag.end, ">");
113
- s.appendLeft(openTag.end, `\n <div><p class="text-sm">Change count: {${signalName}.value}</p></div>\n </${pieceTagName}>`);
114
- } else {
115
- let closingStart = -1;
116
- walk(ast.program, { enter(n) {
117
- if (n.type === "JSXElement" && n.openingElement === openTag && n.closingElement) closingStart = n.closingElement.start;
118
- } });
119
- if (closingStart !== -1) s.appendLeft(closingStart, `\n <div><p class="text-sm">Change count: {${signalName}.value}</p></div>\n `);
120
- }
121
- }
122
- } else if (definedProps.has(prop.name)) {
123
- const attr = openTag.attributes.find((a) => a.name?.name === prop.name);
124
- if (attr) s.remove(attr.start, attr.end);
125
- }
126
- continue;
127
- }
128
- if (value === prop.initialValue) continue;
129
- if ((prop.type === "boolean" || prop.type === "union") && (value === false || value === "false") && prop.initialValue === void 0) continue;
130
- if (prop.type === "string" && value === "" && prop.initialValue === void 0) continue;
131
- if (prop.type === "number" && value === 0 && prop.initialValue === void 0) continue;
132
- if (definedProps.has(prop.name)) {
133
- const attr = openTag.attributes.find((a) => a.name?.name === prop.name);
134
- if (attr) s.remove(attr.start, attr.end);
135
- }
136
- if (prop.isBindable && isBindProps) propsToInject.push(`bind:${prop.name}={signal}`);
137
- else if (value === "true" || value === true) propsToInject.push(prop.name);
138
- else if (value === "false" || value === false) propsToInject.push(`${prop.name}={false}`);
139
- else if (typeof value === "string") propsToInject.push(`${prop.name}="${value}"`);
140
- else if (typeof value === "number") propsToInject.push(`${prop.name}={${value}}`);
141
- else if (value !== void 0) propsToInject.push(`${prop.name}={${JSON.stringify(value)}}`);
142
- }
143
- if (propsToInject.length > 0) {
144
- const insertPos = openTag.end - (openTag.selfClosing ? 2 : 1);
145
- s.appendLeft(insertPos, " " + propsToInject.join(" "));
146
- }
147
- }
148
- }
149
- return s.toString().replace(/^\s*[\r\n]/gm, "");
150
- }
151
-
152
- //#endregion
153
- export { generateJsx };
@@ -1,38 +0,0 @@
1
- import { QWIK_PKG_NAME } from "../repl-constants.qwik.mjs";
2
- import { version } from "@qwik.dev/core";
3
- import qWasmMjs from "@qwik.dev/core/bindings/qwik.wasm.mjs?raw-source";
4
- import qWasmBinUrl from "@qwik.dev/core/bindings/qwik_wasm_bg.wasm?raw-source";
5
- import qBuild from "@qwik.dev/core/dist/build/index.d.ts?raw-source";
6
- import qCoreMinMjs from "@qwik.dev/core/dist/core.min.mjs?raw-source";
7
- import qCoreMjs from "@qwik.dev/core/dist/core.mjs?raw-source";
8
- import qCoreDts from "@qwik.dev/core/dist/core-internal.d.ts?raw-source";
9
- import qOptimizerMjs from "@qwik.dev/core/dist/optimizer.mjs?raw-source";
10
- import qPreloaderMjs from "@qwik.dev/core/dist/preloader.mjs?raw-source";
11
- import qQwikLoaderJs from "@qwik.dev/core/dist/qwikloader.debug.js?raw-source";
12
- import qServerDts from "@qwik.dev/core/dist/server.d.ts?raw-source";
13
- import qServerMjs from "@qwik.dev/core/dist/server.mjs?raw-source";
14
- import qHandlersMjs from "@qwik.dev/core/handlers.mjs?raw-source";
15
-
16
- //#region repl/bundler/bundled.ts
17
- const qwikUrls = {
18
- version,
19
- "/dist/build/index.d.ts": qBuild,
20
- "/dist/core.d.ts": qCoreDts,
21
- "/dist/core.min.mjs": qCoreMinMjs,
22
- "/dist/core.mjs": qCoreMjs,
23
- "/dist/optimizer.mjs": qOptimizerMjs,
24
- "/dist/server.mjs": qServerMjs,
25
- "/dist/server.d.ts": qServerDts,
26
- "/dist/preloader.mjs": qPreloaderMjs,
27
- "/dist/qwikloader.js": qQwikLoaderJs,
28
- "/bindings/qwik.wasm.mjs": qWasmMjs,
29
- "/bindings/qwik_wasm_bg.wasm": qWasmBinUrl,
30
- "/handlers.mjs": qHandlersMjs
31
- };
32
- const bundled = { [QWIK_PKG_NAME]: qwikUrls };
33
- const getDeps = () => {
34
- return bundled;
35
- };
36
-
37
- //#endregion
38
- export { getDeps };
@@ -1,89 +0,0 @@
1
- import { getDeps } from "./bundled.qwik.mjs";
2
-
3
- //#region repl/bundler/index.ts
4
- var Bundler = class {
5
- worker = null;
6
- initP = null;
7
- ready = null;
8
- timer = null;
9
- buildPromises = /* @__PURE__ */ new Map();
10
- nextBuildId = 1;
11
- constructor() {
12
- this.initWorker();
13
- this.keepAlive();
14
- }
15
- initWorker() {
16
- this.initP = new Promise((res) => this.ready = res);
17
- this.worker = new Worker(new URL("./repl-bundler-worker.ts", import.meta.url), { type: "module" });
18
- this.worker.addEventListener("message", this.messageHandler);
19
- this.worker.addEventListener("error", (e) => {
20
- console.error(`Bundler worker failed`, e.message);
21
- this.terminateWorker();
22
- });
23
- }
24
- messageHandler = (e) => {
25
- const { type } = e.data;
26
- if (type === "ready") {
27
- const message = {
28
- type: "init",
29
- deps: getDeps()
30
- };
31
- this.worker.postMessage.bind(this.worker)(message);
32
- this.ready();
33
- } else if (type === "result" || type === "error") {
34
- const { buildId } = e.data;
35
- const promise = this.buildPromises.get(buildId);
36
- if (promise) {
37
- this.buildPromises.delete(buildId);
38
- if (type === "result") promise.resolve(e.data.result);
39
- else {
40
- const { error, stack } = e.data;
41
- const err = new Error(error);
42
- if (stack) err.stack = stack;
43
- promise.reject(err);
44
- }
45
- }
46
- }
47
- };
48
- keepAlive() {
49
- clearTimeout(this.timer);
50
- this.timer = setTimeout(() => this.terminateWorker(), 1e3 * 60 * 5);
51
- }
52
- bundle(options) {
53
- if (!this.worker) this.initWorker();
54
- this.keepAlive();
55
- return this.initP.then(() => {
56
- return new Promise((resolve, reject) => {
57
- const buildId = this.nextBuildId++;
58
- this.buildPromises.set(buildId, {
59
- resolve,
60
- reject
61
- });
62
- const message = {
63
- type: "bundle",
64
- buildId,
65
- data: options
66
- };
67
- const postMessage = this.worker?.postMessage.bind(this.worker);
68
- postMessage?.(message);
69
- });
70
- });
71
- }
72
- terminateWorker() {
73
- if (this.worker) {
74
- this.worker.removeEventListener("message", this.messageHandler);
75
- this.worker.terminate();
76
- this.worker = null;
77
- this.buildPromises.forEach((p) => p.reject(/* @__PURE__ */ new Error("Worker terminated")));
78
- this.buildPromises.clear();
79
- }
80
- }
81
- };
82
- let bundler;
83
- const getBundler = () => {
84
- if (!bundler) bundler = new Bundler();
85
- return bundler;
86
- };
87
-
88
- //#endregion
89
- export { getBundler };
@@ -1,21 +0,0 @@
1
- import { getBundler } from "./bundler/index.qwik.mjs";
2
-
3
- //#region repl/index.ts
4
- var ReplCompiler = class {
5
- options;
6
- bundler;
7
- constructor(options) {
8
- this.options = options;
9
- this.bundler = getBundler();
10
- }
11
- async compile(input) {
12
- const finalOptions = {
13
- ...this.options,
14
- ...input
15
- };
16
- return this.bundler.bundle(finalOptions);
17
- }
18
- };
19
-
20
- //#endregion
21
- export { ReplCompiler };
@@ -1,5 +0,0 @@
1
- //#region repl/repl-constants.ts
2
- const QWIK_PKG_NAME = "@qwik.dev/core";
3
-
4
- //#endregion
5
- export { QWIK_PKG_NAME };
@@ -1,67 +0,0 @@
1
- import { createRegExp, exactly } from "magic-regexp";
2
- import MagicString from "magic-string";
3
- import { walk } from "oxc-walker";
4
- import { existsSync, readdirSync, statSync } from "node:fs";
5
- import { basename, dirname, extname, join } from "node:path";
6
- import { remark } from "remark";
7
- import remarkMdx from "remark-mdx";
8
-
9
- //#region rolldown/playground.ts
10
- const playground = () => {
11
- const isMDX = createRegExp(exactly(".").and("mdx").at.lineEnd());
12
- return {
13
- name: "vite-plugin-qds-playground",
14
- enforce: "pre",
15
- async transform(code, id) {
16
- if (!isMDX.test(id)) return;
17
- try {
18
- const mdast = remark().use(remarkMdx).parse(code);
19
- const s = new MagicString(code);
20
- let hasPlayground = false;
21
- const scenariosDir = join(dirname(id), "scenarios");
22
- if (!existsSync(scenariosDir) || !statSync(scenariosDir).isDirectory()) return null;
23
- const scenarioFiles = readdirSync(scenariosDir).filter((file) => file.endsWith(".tsx") || file.endsWith(".ts")).map((file) => {
24
- const name = basename(file, extname(file));
25
- return {
26
- name,
27
- file,
28
- componentVar: `Scenario_${name.replace(/[^a-zA-Z0-9]/g, "_")}`,
29
- sourceVar: `Source_${name.replace(/[^a-zA-Z0-9]/g, "_")}`
30
- };
31
- });
32
- if (scenarioFiles.length === 0) return null;
33
- walk(mdast, { enter(node) {
34
- const mdxNode = node;
35
- if ((mdxNode.type === "mdxJsxFlowElement" || mdxNode.type === "mdxJsxTextElement") && mdxNode.name === "Playground") {
36
- if (mdxNode.position?.start?.offset !== void 0 && mdxNode.position?.end?.offset !== void 0) {
37
- if (!mdxNode.attributes.some((attr) => attr.name === "scenarios")) {
38
- const scenariosArrayString = `[${scenarioFiles.map((s$1) => `{name: "${s$1.name}", component: ${s$1.componentVar}, source: ${s$1.sourceVar}}`).join(", ")}]`;
39
- const insertPos$1 = mdxNode.position.start.offset + 11;
40
- s.appendLeft(insertPos$1, ` scenarios={${scenariosArrayString}}`);
41
- hasPlayground = true;
42
- }
43
- }
44
- }
45
- } });
46
- if (!hasPlayground) return null;
47
- const imports = scenarioFiles.map((file) => `import ${file.componentVar} from "./scenarios/${file.file}";\nimport ${file.sourceVar} from "./scenarios/${file.file}?raw";`).join("\n");
48
- let insertPos = 0;
49
- if (code.startsWith("---")) {
50
- const secondDelimiter = code.indexOf("---", 3);
51
- if (secondDelimiter !== -1) insertPos = secondDelimiter + 3;
52
- }
53
- s.appendLeft(insertPos, `\n${imports}\n`);
54
- return {
55
- code: s.toString(),
56
- map: s.generateMap({ hires: true })
57
- };
58
- } catch (error) {
59
- console.error(`Error transforming Playground in ${id}:`, error);
60
- return null;
61
- }
62
- }
63
- };
64
- };
65
-
66
- //#endregion
67
- export { playground };
@@ -1,32 +0,0 @@
1
- import type { Node } from "@oxc-project/types";
2
- import type { Plugin as VitePlugin } from "vite";
3
- export type PropType = {
4
- name: string;
5
- type: "boolean" | "string" | "number" | "union" | "function" | "unknown";
6
- unionValues?: string[];
7
- isBindable: boolean;
8
- isFunction?: boolean;
9
- initialValue?: unknown;
10
- comment?: string;
11
- scenario?: string;
12
- };
13
- export type ComponentPiece = {
14
- name: string;
15
- props: PropType[];
16
- };
17
- export type ComponentMetadata = {
18
- componentName: string;
19
- pieces: ComponentPiece[];
20
- };
21
- export type ComponentPropsPluginOptions = {
22
- componentsDir: string;
23
- outputDir: string;
24
- debug?: boolean;
25
- };
26
- export declare const componentProps: (options: ComponentPropsPluginOptions) => VitePlugin;
27
- export declare function generateAllComponentMetadata(componentsDir: string, outputDir: string, debug: (message: string, ...data: unknown[]) => void): Promise<void>;
28
- export declare function generateComponentMetadata(componentName: string, componentsDir: string, outputDir: string, debug: (message: string, ...data: unknown[]) => void): Promise<void>;
29
- export declare function extractJSDoc(node: Node, source: string, propName?: string): {
30
- comment?: string;
31
- scenario?: string;
32
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- export * from "./component-props";
2
- export type { ComponentMetadata, ComponentPiece, PropType } from "./component-props";
@@ -1,9 +0,0 @@
1
- import type { ComponentMetadata } from "./prop-extraction";
2
- export type GenerateJsxOptions = {
3
- metadata: ComponentMetadata;
4
- selectedPieceName: string;
5
- propValues: Record<string, Record<string, unknown>>;
6
- isBindProps: boolean;
7
- baseSource?: string;
8
- };
9
- export declare function generateJsx({ metadata, selectedPieceName, propValues, isBindProps, baseSource }: GenerateJsxOptions): string;
@@ -1,8 +0,0 @@
1
- export declare const playground: () => {
2
- name: string;
3
- enforce: "pre";
4
- transform(code: string, id: string): Promise<{
5
- code: string;
6
- map: import("magic-string").SourceMap;
7
- } | null | undefined>;
8
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};