@qds.dev/tools 0.11.1 → 0.13.0

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.
Files changed (85) hide show
  1. package/lib/linter/qds-internal.d.ts +210 -0
  2. package/lib/linter/qds.d.ts +59 -0
  3. package/{lib-types/tools → lib}/linter/rule-tester.d.ts +23 -1
  4. package/{lib-types/tools → lib}/playground/prop-extraction.d.ts +6 -1
  5. package/lib/playground/prop-extraction.qwik.mjs +68 -9
  6. package/lib/playground/scenario-injection.qwik.mjs +41 -8
  7. package/lib/rolldown/as-child.d.ts +16 -0
  8. package/lib/rolldown/as-child.qwik.mjs +52 -91
  9. package/lib/rolldown/icons.d.ts +21 -0
  10. package/{lib-types/tools → lib}/rolldown/index.d.ts +4 -2
  11. package/lib/rolldown/index.qwik.mjs +3 -3
  12. package/lib/rolldown/inject-component-types.d.ts +2 -0
  13. package/lib/rolldown/inject-component-types.qwik.mjs +138 -0
  14. package/lib/rolldown/inline-asset.qwik.mjs +6 -6
  15. package/lib/rolldown/inline-css.d.ts +2 -0
  16. package/lib/rolldown/inline-css.qwik.mjs +1 -1
  17. package/lib/rolldown/qds-types.d.ts +41 -0
  18. package/lib/rolldown/qds.d.ts +5 -0
  19. package/lib/rolldown/qds.qwik.mjs +147 -0
  20. package/lib/rolldown/qwik-rolldown.d.ts +6 -0
  21. package/lib/rolldown/ui-types.d.ts +42 -0
  22. package/lib/rolldown/ui.d.ts +12 -0
  23. package/lib/rolldown/ui.qwik.mjs +445 -0
  24. package/lib/utils/icons/naming.unit.d.ts +1 -0
  25. package/{lib-types/tools → lib}/utils/icons/transform/mdx.d.ts +3 -11
  26. package/lib/utils/icons/transform/mdx.qwik.mjs +14 -20
  27. package/{lib-types/tools → lib}/utils/icons/transform/tsx.d.ts +3 -12
  28. package/lib/utils/icons/transform/tsx.qwik.mjs +28 -37
  29. package/lib/utils/index.qwik.mjs +5 -5
  30. package/{lib-types/tools → lib}/utils/transform-dts.d.ts +4 -3
  31. package/lib/utils/transform-dts.qwik.mjs +18 -23
  32. package/lib/utils/transform-dts.unit.d.ts +1 -0
  33. package/{lib-types/tools → lib}/vite/index.d.ts +2 -2
  34. package/lib/vite/index.qwik.mjs +2 -3
  35. package/lib/vite/minify-content.qwik.mjs +1 -1
  36. package/lib/vite/minify-content.unit.d.ts +1 -0
  37. package/linter/qds-internal.ts +707 -0
  38. package/linter/qds-internal.unit.ts +399 -0
  39. package/linter/qds.ts +300 -0
  40. package/linter/qds.unit.ts +158 -0
  41. package/linter/rule-tester.ts +395 -0
  42. package/package.json +17 -18
  43. package/lib/rolldown/icons.qwik.mjs +0 -112
  44. package/lib-types/components/src/icons-runtime.d.ts +0 -223
  45. package/lib-types/tools/linter/qds-internal.d.ts +0 -7
  46. package/lib-types/tools/rolldown/as-child.d.ts +0 -24
  47. package/lib-types/tools/rolldown/icons.d.ts +0 -45
  48. package/lib-types/tools/rolldown/inline-css.d.ts +0 -26
  49. package/lib-types/tools/rolldown/qwik-rolldown.d.ts +0 -9
  50. /package/{lib-types/tools → lib}/linter/qds-internal.unit.d.ts +0 -0
  51. /package/{lib-types/tools/playground/generate-metadata.d.ts → lib/linter/qds.unit.d.ts} +0 -0
  52. /package/{lib-types/tools/playground/generate-metadata.unit.d.ts → lib/playground/generate-metadata.d.ts} +0 -0
  53. /package/{lib-types/tools/playground/prop-extraction.unit.d.ts → lib/playground/generate-metadata.unit.d.ts} +0 -0
  54. /package/{lib-types/tools → lib}/playground/index.d.ts +0 -0
  55. /package/{lib-types/tools/playground/scenario-injection.unit.d.ts → lib/playground/prop-extraction.unit.d.ts} +0 -0
  56. /package/{lib-types/tools → lib}/playground/scenario-injection.d.ts +0 -0
  57. /package/{lib-types/tools/rolldown/as-child.unit.d.ts → lib/playground/scenario-injection.unit.d.ts} +0 -0
  58. /package/{lib-types/tools/rolldown/icons.unit.d.ts → lib/rolldown/as-child.unit.d.ts} +0 -0
  59. /package/{lib-types/tools/rolldown/inline-asset.unit.d.ts → lib/rolldown/icons.unit.d.ts} +0 -0
  60. /package/{lib-types/tools/src/generate/icon-types.unit.d.ts → lib/rolldown/inject-component-types.unit.d.ts} +0 -0
  61. /package/{lib-types/tools → lib}/rolldown/inline-asset.d.ts +0 -0
  62. /package/{lib-types/tools/utils/icons/ast/expressions.unit.d.ts → lib/rolldown/inline-asset.unit.d.ts} +0 -0
  63. /package/{lib-types/tools/utils/icons/ast/jsx.unit.d.ts → lib/rolldown/qds.unit.d.ts} +0 -0
  64. /package/{lib-types/tools/utils/icons/naming.unit.d.ts → lib/rolldown/ui.unit.d.ts} +0 -0
  65. /package/{lib-types/tools → lib}/src/generate/icon-types.d.ts +0 -0
  66. /package/{lib-types/tools → lib}/src/index.d.ts +0 -0
  67. /package/{lib-types/tools → lib}/src/vite.d.ts +0 -0
  68. /package/{lib-types/tools → lib}/utils/ast/core.d.ts +0 -0
  69. /package/{lib-types/tools → lib}/utils/ast/imports.d.ts +0 -0
  70. /package/{lib-types/tools → lib}/utils/ast/jsx-helpers.d.ts +0 -0
  71. /package/{lib-types/tools → lib}/utils/ast/qwik.d.ts +0 -0
  72. /package/{lib-types/tools → lib}/utils/fs-mock.d.ts +0 -0
  73. /package/{lib-types/tools → lib}/utils/fs.d.ts +0 -0
  74. /package/{lib-types/tools → lib}/utils/icons/ast/expressions.d.ts +0 -0
  75. /package/{lib-types/tools/utils/transform-dts.unit.d.ts → lib/utils/icons/ast/expressions.unit.d.ts} +0 -0
  76. /package/{lib-types/tools → lib}/utils/icons/ast/jsx.d.ts +0 -0
  77. /package/{lib-types/tools/vite/minify-content.unit.d.ts → lib/utils/icons/ast/jsx.unit.d.ts} +0 -0
  78. /package/{lib-types/tools → lib}/utils/icons/collections/loader.d.ts +0 -0
  79. /package/{lib-types/tools → lib}/utils/icons/import-resolver.d.ts +0 -0
  80. /package/{lib-types/tools → lib}/utils/icons/naming.d.ts +0 -0
  81. /package/{lib-types/tools → lib}/utils/icons/transform/shared.d.ts +0 -0
  82. /package/{lib-types/tools → lib}/utils/icons/types/mdx-ast.d.ts +0 -0
  83. /package/{lib-types/tools → lib}/utils/index.d.ts +0 -0
  84. /package/{lib-types/tools → lib}/utils/package-json.d.ts +0 -0
  85. /package/{lib-types/tools → lib}/vite/minify-content.d.ts +0 -0
@@ -6,101 +6,62 @@ import { parseSync } from "oxc-parser";
6
6
  import { walk } from "oxc-walker";
7
7
 
8
8
  //#region rolldown/as-child.ts
9
- /**
10
- * Rolldown plugin that transforms JSX elements with asChild prop by moving child element props to the parent
11
- * @param options - Plugin configuration options
12
- * @returns Rolldown-compatible plugin object
13
- */ const asChild = (options = {}) => {
14
- const { debug: isDebugMode = false } = options;
15
- /**
16
- * Debug logging function that only outputs when debug mode is enabled
17
- * @param message - Debug message to log
18
- */ const debug = (message) => {
19
- if (!isDebugMode) return;
20
- console.log(message);
21
- };
22
- return {
23
- name: "vite-plugin-as-child",
24
- enforce: "pre",
25
- transform: {
26
- filter: { id: createRegExp(exactly(".").and(anyOf("tsx", "jsx")).at.lineEnd()) },
27
- handler(code, id) {
28
- debug(`🔧 asChild plugin processing: ${id}`);
29
- const parsed = parseSync(id, code);
30
- if (parsed.errors.length > 0) return null;
31
- const ast = parsed.program;
32
- const s = new MagicString(code);
33
- walk(ast, { enter(node) {
34
- if (isJSXElement(node) && hasAsChild(node.openingElement)) processAsChild(node, s, code);
35
- } });
36
- if (s.hasChanged()) return {
37
- code: s.toString(),
38
- map: s.generateMap({ hires: true })
39
- };
40
- return null;
41
- }
9
+ function hasAsChild(opening, debug) {
10
+ const isAsChildProp = opening.attributes.some((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "asChild");
11
+ if (isAsChildProp) debug("🔄 Found asChild element!");
12
+ return isAsChildProp;
13
+ }
14
+ function processAsChild(elem, s, source, debug) {
15
+ const children = elem.children.filter((child) => !isJSXText(child) || child.value.trim() !== "");
16
+ if (children.length === 0) return;
17
+ if (children.length > 1) throw new Error(`asChild elements must have exactly one child at ${elem.start}`);
18
+ const child = children[0];
19
+ let jsxType;
20
+ let movedProps;
21
+ if (isJSXElement(child)) {
22
+ const { type, props } = extractFromElement(child, source);
23
+ jsxType = type;
24
+ movedProps = props;
25
+ const attrs = child.openingElement.attributes;
26
+ if (attrs.length > 0) {
27
+ const nameEnd = child.openingElement.name.end;
28
+ const firstAttrStart = attrs[0].start;
29
+ const lastAttrEnd = attrs[attrs.length - 1].end;
30
+ const startPos = Math.max(nameEnd, firstAttrStart - 10);
31
+ const actualStart = source.slice(startPos, firstAttrStart).search(/\s/) + startPos;
32
+ s.remove(actualStart === startPos - 1 ? firstAttrStart : actualStart, lastAttrEnd);
42
33
  }
43
- };
44
- /**
45
- * Checks if a JSX opening element has an asChild attribute
46
- * @param opening - JSX opening element to check
47
- * @returns True if element has asChild attribute
48
- */ function hasAsChild(opening) {
49
- const isAsChildProp = opening.attributes.some((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "asChild");
50
- if (isAsChildProp) debug("🔄 Found asChild element!");
51
- return isAsChildProp;
52
- }
53
- /**
54
- * Processes a JSX element with asChild prop, moving child props to parent
55
- * @param elem - JSX element with asChild prop
56
- * @param s - MagicString instance for code transformation
57
- * @param source - Original source code
58
- */ function processAsChild(elem, s, source) {
59
- const children = elem.children.filter((child) => !isJSXText(child) || child.value.trim() !== "");
60
- if (children.length === 0) return;
61
- if (children.length > 1) throw new Error(`asChild elements must have exactly one child at ${elem.start}`);
62
- const child = children[0];
63
- let jsxType;
64
- let movedProps;
65
- if (isJSXElement(child)) {
66
- const { type, props } = extractFromElement(child, source);
67
- jsxType = type;
68
- movedProps = props;
69
- const attrs = child.openingElement.attributes;
70
- if (attrs.length > 0) {
71
- const nameEnd = child.openingElement.name.end;
72
- const firstAttrStart = attrs[0].start;
73
- const lastAttrEnd = attrs[attrs.length - 1].end;
74
- const startPos = Math.max(nameEnd, firstAttrStart - 10);
75
- const actualStart = source.slice(startPos, firstAttrStart).search(/\s/) + startPos;
76
- s.remove(actualStart === startPos - 1 ? firstAttrStart : actualStart, lastAttrEnd);
77
- }
78
- if (child.children.length > 0) {
79
- const childrenCode = child.children.map((grandchild) => source.slice(grandchild.start, grandchild.end)).join("");
80
- s.overwrite(child.start, child.end, childrenCode);
81
- } else s.remove(child.start, child.end);
82
- } else if (isJSXExpressionContainer(child)) {
83
- const result = handleExpression(child.expression, source);
84
- if (result) {
85
- jsxType = result.type;
86
- movedProps = result.props;
87
- debug("⚠️ Expression asChild: props not removed from children");
88
- } else {
89
- debug(`⚠️ Skipping unsupported expression type: ${child.expression.type} at line ${getLineNumber(source, elem.start)}`);
90
- return;
91
- }
34
+ if (child.children.length > 0) {
35
+ const childrenCode = child.children.map((grandchild) => source.slice(grandchild.start, grandchild.end)).join("");
36
+ s.overwrite(child.start, child.end, childrenCode);
37
+ } else s.remove(child.start, child.end);
38
+ } else if (isJSXExpressionContainer(child)) {
39
+ const result = handleExpression(child.expression, source);
40
+ if (result) {
41
+ jsxType = result.type;
42
+ movedProps = result.props;
43
+ debug("⚠️ Expression asChild: props not removed from children");
92
44
  } else {
93
- debug(`⚠️ Skipping unsupported child type: ${child.type} at line ${getLineNumber(source, elem.start)}`);
45
+ debug(`⚠️ Skipping unsupported expression type: ${child.expression.type} at line ${getLineNumber(source, elem.start)}`);
94
46
  return;
95
47
  }
96
- const opening = elem.openingElement;
97
- const insertPos = opening.attributes.length > 0 ? opening.attributes[opening.attributes.length - 1].end : opening.name.end;
98
- const typeAttr = jsxType.startsWith("\"") ? ` jsxType=${jsxType}` : ` jsxType={${jsxType}}`;
99
- s.appendLeft(insertPos, typeAttr);
100
- const propsAttr = ` movedProps={${movedProps}}`;
101
- s.appendLeft(insertPos, propsAttr);
48
+ } else {
49
+ debug(`⚠️ Skipping unsupported child type: ${child.type} at line ${getLineNumber(source, elem.start)}`);
50
+ return;
102
51
  }
103
- };
52
+ const opening = elem.openingElement;
53
+ const insertPos = opening.attributes.length > 0 ? opening.attributes[opening.attributes.length - 1].end : opening.name.end;
54
+ const typeAttr = jsxType.startsWith("\"") ? ` jsxType=${jsxType}` : ` jsxType={${jsxType}}`;
55
+ s.appendLeft(insertPos, typeAttr);
56
+ const propsAttr = ` movedProps={${movedProps}}`;
57
+ s.appendLeft(insertPos, propsAttr);
58
+ }
59
+ function asChildTransformCore(ast, s, code, debug) {
60
+ walk(ast, { enter(node) {
61
+ if (isJSXElement(node) && hasAsChild(node.openingElement, debug)) processAsChild(node, s, code, debug);
62
+ } });
63
+ return { changed: s.hasChanged() };
64
+ }
104
65
 
105
66
  //#endregion
106
- export { asChild };
67
+ export { asChildTransformCore };
@@ -0,0 +1,21 @@
1
+ import type { Plugin } from "rolldown";
2
+ export type PacksMap = Record<string, {
3
+ iconifyPrefix: string;
4
+ sanitizeIcon?: (pascal: string) => string;
5
+ }>;
6
+ export type IconsPluginOptions = {
7
+ debug?: boolean;
8
+ /**
9
+ * The sources to scan for imports. By default this includes QDS, you can also add your own when creating library wrappers.
10
+ */
11
+ importSources?: string[];
12
+ packs?: PacksMap;
13
+ };
14
+ /**
15
+ * Rolldown plugin that transforms icon JSX elements to direct <svg /> calls
16
+ * @param options - Plugin configuration options
17
+ * @returns Rolldown-compatible plugin object
18
+ */
19
+ export declare const icons: (options?: IconsPluginOptions) => Plugin & {
20
+ enforce?: "pre" | "post";
21
+ };
@@ -1,8 +1,10 @@
1
1
  export type { AsChildPluginOptions, AsChildTypes } from "./as-child";
2
- export { asChild } from "./as-child";
3
2
  export type { IconsPluginOptions, PacksMap } from "./icons";
4
- export { icons } from "./icons";
5
3
  export type { InlineAssetPluginOptions } from "./inline-asset";
6
4
  export { inlineAsset } from "./inline-asset";
5
+ export { injectComponentTypes } from "./inject-component-types";
7
6
  export { inlineCssPlugin } from "./inline-css";
8
7
  export { qwikRolldown } from "./qwik-rolldown";
8
+ export type { UiPluginOptions } from "./ui";
9
+ export type { QdsOptions, QdsSubPlugin } from "./qds-types";
10
+ export { qds } from "./qds";
@@ -1,7 +1,7 @@
1
- import { asChild } from "./as-child.qwik.mjs";
2
- import { icons } from "./icons.qwik.mjs";
3
1
  import { inlineAsset } from "./inline-asset.qwik.mjs";
2
+ import { injectComponentTypes } from "./inject-component-types.qwik.mjs";
4
3
  import { inlineCssPlugin } from "./inline-css.qwik.mjs";
5
4
  import { qwikRolldown } from "./qwik-rolldown.qwik.mjs";
5
+ import { qds } from "./qds.qwik.mjs";
6
6
 
7
- export { asChild, icons, inlineAsset, inlineCssPlugin, qwikRolldown };
7
+ export { injectComponentTypes, inlineAsset, inlineCssPlugin, qds, qwikRolldown };
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from "rolldown";
2
+ export declare function injectComponentTypes(): Plugin;
@@ -0,0 +1,138 @@
1
+ import { getNodeText } from "../utils/ast/core.qwik.mjs";
2
+ import { detectsRenderComponentUsage } from "../utils/ast/qwik.qwik.mjs";
3
+ import { dirname, join, relative } from "node:path";
4
+ import { anyOf, createRegExp } from "magic-regexp";
5
+ import MagicString from "magic-string";
6
+ import { parseSync } from "oxc-parser";
7
+ import { walk } from "oxc-walker";
8
+
9
+ //#region rolldown/inject-component-types.ts
10
+ const tsFilePattern = createRegExp(anyOf(".tsx", ".ts"));
11
+ const KNOWN_GENERIC_CALLS = {
12
+ createContextId: "ContextId",
13
+ createSignal: "Signal"
14
+ };
15
+ function injectComponentTypes() {
16
+ const asChildTypesModule = join(join(process.cwd(), "src"), "types");
17
+ return {
18
+ name: "rolldown-plugin-inject-component-types",
19
+ transform: {
20
+ order: "pre",
21
+ filter: { id: tsFilePattern },
22
+ handler(code, id) {
23
+ const parsed = parseSync(id, code);
24
+ if (parsed.errors.length > 0) return null;
25
+ const s = new MagicString(code);
26
+ const usesRender = detectsRenderComponentUsage(code);
27
+ let needsComponentImport = false;
28
+ let needsAsChildImport = false;
29
+ const additionalQwikImports = /* @__PURE__ */ new Set();
30
+ walk(parsed.program, { enter(node) {
31
+ if (node.type !== "ExportNamedDeclaration") return;
32
+ if (!("declaration" in node) || !node.declaration) return;
33
+ const declaration = node.declaration;
34
+ if (declaration.type !== "VariableDeclaration") return;
35
+ if (!("declarations" in declaration)) return;
36
+ for (const declarator of declaration.declarations) {
37
+ if (declarator.type !== "VariableDeclarator") continue;
38
+ if (!declarator.id || declarator.id.type !== "Identifier") continue;
39
+ if ("typeAnnotation" in declarator.id && declarator.id.typeAnnotation) continue;
40
+ if (!declarator.init || declarator.init.type !== "CallExpression") continue;
41
+ if (!("callee" in declarator.init)) continue;
42
+ const callee = declarator.init.callee;
43
+ if (!callee || callee.type !== "Identifier") continue;
44
+ const calleeName = getNodeText(callee, code);
45
+ if (calleeName === "component$") {
46
+ const propsType = extractComponentPropsType(declarator.init, code);
47
+ let annotation;
48
+ if (!propsType) annotation = ": Component";
49
+ else if (usesRender) {
50
+ annotation = `: Component<${propsType} & AsChildTypes>`;
51
+ needsAsChildImport = true;
52
+ } else annotation = `: Component<${propsType}>`;
53
+ s.appendLeft(declarator.id.end, annotation);
54
+ needsComponentImport = true;
55
+ continue;
56
+ }
57
+ const returnTypeName = KNOWN_GENERIC_CALLS[calleeName];
58
+ if (returnTypeName) {
59
+ const typeArg = extractCallTypeArgument(declarator.init, code);
60
+ if (typeArg) {
61
+ s.appendLeft(declarator.id.end, `: ${returnTypeName}<${typeArg}>`);
62
+ additionalQwikImports.add(returnTypeName);
63
+ }
64
+ }
65
+ }
66
+ } });
67
+ if (!s.hasChanged()) return null;
68
+ if (needsComponentImport) additionalQwikImports.add("Component");
69
+ if (additionalQwikImports.size > 0) addQwikImports(parsed, code, s, additionalQwikImports);
70
+ if (needsAsChildImport) {
71
+ let relPath = relative(dirname(id), asChildTypesModule);
72
+ if (!relPath.startsWith(".")) relPath = "./" + relPath;
73
+ s.prepend(`import type { AsChildTypes } from "${relPath}";\n`);
74
+ }
75
+ return {
76
+ code: s.toString(),
77
+ map: s.generateMap({ hires: true })
78
+ };
79
+ }
80
+ }
81
+ };
82
+ }
83
+ function extractCallTypeArgument(callExpr, code) {
84
+ if (!("typeArguments" in callExpr) || !callExpr.typeArguments) return null;
85
+ const ta = callExpr.typeArguments;
86
+ if (!ta.params?.length) return null;
87
+ return getNodeText(ta.params[0], code);
88
+ }
89
+ function extractComponentPropsType(callExpr, code) {
90
+ const typeArg = extractCallTypeArgument(callExpr, code);
91
+ if (typeArg) return typeArg;
92
+ if (!("arguments" in callExpr)) return null;
93
+ const args = callExpr.arguments;
94
+ if (args.length === 0) return null;
95
+ const callback = args[0];
96
+ if (callback.type !== "ArrowFunctionExpression" && callback.type !== "FunctionExpression") return null;
97
+ if ("typeParameters" in callback && callback.typeParameters) return null;
98
+ const params = callback.params;
99
+ if (!params || params.length === 0) return null;
100
+ const firstParam = params[0];
101
+ if (firstParam.type === "Identifier" && "typeAnnotation" in firstParam && firstParam.typeAnnotation) {
102
+ const ta = firstParam.typeAnnotation;
103
+ if (ta.typeAnnotation) return getNodeText(ta.typeAnnotation, code);
104
+ }
105
+ if (firstParam.type === "ObjectPattern" && "typeAnnotation" in firstParam) {
106
+ const annotation = firstParam.typeAnnotation;
107
+ if (annotation?.typeAnnotation) return getNodeText(annotation.typeAnnotation, code);
108
+ }
109
+ return null;
110
+ }
111
+ function addQwikImports(parsed, code, s, needed) {
112
+ for (const node of parsed.program.body) {
113
+ if (node.type !== "ImportDeclaration") continue;
114
+ if (!("source" in node)) continue;
115
+ if (code.slice(node.source.start + 1, node.source.end - 1) !== "@qwik.dev/core") continue;
116
+ if (!node.specifiers) continue;
117
+ for (const spec of node.specifiers) {
118
+ if (spec.type !== "ImportSpecifier") continue;
119
+ if (!("imported" in spec)) continue;
120
+ const imported = spec.imported;
121
+ if (imported?.name) needed.delete(imported.name);
122
+ }
123
+ if (needed.size === 0) return;
124
+ const lastSpec = node.specifiers[node.specifiers.length - 1];
125
+ if (lastSpec) {
126
+ const isTypeImport = "importKind" in node && node.importKind === "type";
127
+ const additions = Array.from(needed).map((n) => isTypeImport ? n : `type ${n}`).join(",\n ");
128
+ s.appendLeft(lastSpec.end, `,\n ${additions}`);
129
+ return;
130
+ }
131
+ }
132
+ if (needed.size === 0) return;
133
+ const names = Array.from(needed).join(", ");
134
+ s.prepend(`import type { ${names} } from "@qwik.dev/core";\n`);
135
+ }
136
+
137
+ //#endregion
138
+ export { injectComponentTypes };
@@ -1,8 +1,8 @@
1
- import { anyOf, char, createRegExp, exactly, maybe, oneOrMore } from "magic-regexp";
1
+ import { createHash } from "node:crypto";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
4
  import { basename, dirname, join } from "node:path";
5
- import { createHash } from "node:crypto";
5
+ import { anyOf, char, createRegExp, exactly, maybe, oneOrMore } from "magic-regexp";
6
6
 
7
7
  //#region rolldown/inline-asset.ts
8
8
  const nodeRequire = createRequire(import.meta.url);
@@ -42,7 +42,7 @@ const nodeRequire = createRequire(import.meta.url);
42
42
  resolveId: {
43
43
  order: "pre",
44
44
  filter: { id: combinedQuery },
45
- async handler(id, importer, options) {
45
+ async handler(id, importer, resolveOptions) {
46
46
  const isInlineAsset = inlineAssetQuery.test(id);
47
47
  const isNoTransform = noTransformQuery.test(id);
48
48
  if (!isInlineAsset && !isNoTransform) return;
@@ -57,7 +57,7 @@ const nodeRequire = createRequire(import.meta.url);
57
57
  let resolvedPath = null;
58
58
  try {
59
59
  const resolved = await this.resolve(basePath, importer, {
60
- ...options,
60
+ ...resolveOptions,
61
61
  skipSelf: true
62
62
  });
63
63
  if (resolved) resolvedPath = resolved.id.split("?")[0];
@@ -75,8 +75,8 @@ const nodeRequire = createRequire(import.meta.url);
75
75
  }
76
76
  const subpath = parts.slice(subpathStartIndex).join("/");
77
77
  try {
78
- const basePath = importer ? dirname(importer) : process.cwd();
79
- resolvedPath = join(dirname(nodeRequire.resolve(`${packageName}/package.json`, { paths: [basePath] })), subpath);
78
+ const resolverBasePath = importer ? dirname(importer) : process.cwd();
79
+ resolvedPath = join(dirname(nodeRequire.resolve(`${packageName}/package.json`, { paths: [resolverBasePath] })), subpath);
80
80
  debugInlineAsset("Manually resolved package import:", resolvedPath);
81
81
  } catch (err) {
82
82
  debugInlineAsset("Failed to manually resolve:", err);
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from "rolldown";
2
+ export declare function inlineCssPlugin(): Plugin;
@@ -1,5 +1,5 @@
1
- import { anyOf, charNotIn, createRegExp, exactly, oneOrMore } from "magic-regexp";
2
1
  import { readFileSync } from "node:fs";
2
+ import { anyOf, charNotIn, createRegExp, exactly, oneOrMore } from "magic-regexp";
3
3
 
4
4
  //#region rolldown/inline-css.ts
5
5
  function inlineCssPlugin() {
@@ -0,0 +1,41 @@
1
+ import type { Program } from "@oxc-project/types";
2
+ import type MagicString from "magic-string";
3
+ export type QdsDebugFn = (message: string, ...data: unknown[]) => void;
4
+ export type QdsSubPluginContext = {
5
+ code: string;
6
+ id: string;
7
+ ast: Program | null;
8
+ s: MagicString;
9
+ debug: QdsDebugFn;
10
+ warn?: (message: string) => void;
11
+ };
12
+ export type QdsSubPluginResult = {
13
+ changed: boolean;
14
+ };
15
+ export type QdsSubPlugin = {
16
+ name: string;
17
+ extensions: RegExp;
18
+ transform(ctx: QdsSubPluginContext): QdsSubPluginResult | null;
19
+ resolveId?(source: string): string | null;
20
+ load?(id: string): Promise<{
21
+ code: string;
22
+ } | null> | {
23
+ code: string;
24
+ } | null;
25
+ };
26
+ export type QdsUiOptions = {
27
+ importSources?: string[];
28
+ };
29
+ export type QdsIconsOptions = {
30
+ importSources?: string[];
31
+ packs?: Record<string, {
32
+ iconifyPrefix: string;
33
+ sanitizeIcon?: (pascal: string) => string;
34
+ }>;
35
+ };
36
+ export type QdsOptions = {
37
+ ui?: boolean | QdsUiOptions;
38
+ icons?: boolean | QdsIconsOptions;
39
+ asChild?: boolean;
40
+ debug?: boolean;
41
+ };
@@ -0,0 +1,5 @@
1
+ import type { Plugin } from "rolldown";
2
+ import type { QdsOptions, QdsSubPlugin } from "./qds-types";
3
+ export declare const qds: (options?: QdsOptions, _testSubPlugins?: QdsSubPlugin[]) => Plugin & {
4
+ enforce?: "pre" | "post";
5
+ };
@@ -0,0 +1,147 @@
1
+ import { asChildTransformCore } from "./as-child.qwik.mjs";
2
+ import { uiTransformCore } from "./ui.qwik.mjs";
3
+ import { CollectionLoader } from "../utils/icons/collections/loader.qwik.mjs";
4
+ import { resolveImportAliases } from "../utils/icons/import-resolver.qwik.mjs";
5
+ import { transformTSXFileCore } from "../utils/icons/transform/tsx.qwik.mjs";
6
+ import { transformMDXFileCore } from "../utils/icons/transform/mdx.qwik.mjs";
7
+ import MagicString from "magic-string";
8
+ import { parseSync } from "oxc-parser";
9
+
10
+ //#region rolldown/qds.ts
11
+ const createDebug = (pluginName, isDebugMode) => (message, ...data) => {
12
+ if (!isDebugMode) return;
13
+ console.log(`[qds:${pluginName}] ${message}`, ...data);
14
+ };
15
+ const buildCombinedFilter = (activePlugins) => {
16
+ if (activePlugins.length === 0) return /(?!)/;
17
+ return new RegExp(activePlugins.map((p) => p.extensions.source).join("|"));
18
+ };
19
+ const createUiSubPlugin = (opts, _debug) => {
20
+ const importSources = typeof opts === "object" && opts.importSources ? opts.importSources : ["@qds.dev/ui"];
21
+ return {
22
+ name: "ui",
23
+ extensions: /\.(tsx|jsx)$/,
24
+ transform: (ctx) => {
25
+ if (!ctx.ast) return { changed: false };
26
+ return uiTransformCore(ctx.code, ctx.id, ctx.ast, ctx.s, importSources, ctx.debug, ctx.warn);
27
+ }
28
+ };
29
+ };
30
+ const createIconsSubPlugin = (opts, debug) => {
31
+ const importSources = typeof opts === "object" && opts.importSources ? opts.importSources : ["@qds.dev/ui"];
32
+ const packs = typeof opts === "object" ? opts.packs : void 0;
33
+ const collectionNames = /* @__PURE__ */ new Map();
34
+ const collectionLoader = new CollectionLoader(debug);
35
+ return {
36
+ name: "icons",
37
+ extensions: /\.(tsx|jsx|mdx)$/,
38
+ transform: (ctx) => {
39
+ if (collectionLoader.getAvailableCollections().size === 0) collectionLoader.discoverCollections();
40
+ const availableCollections = collectionLoader.getAvailableCollections();
41
+ if (ctx.id.endsWith(".mdx")) return transformMDXFileCore(ctx.code, ctx.id, ctx.s, importSources, availableCollections, collectionNames, packs, ctx.debug);
42
+ if (!ctx.ast) return { changed: false };
43
+ const aliasToPack = resolveImportAliases(ctx.ast, importSources, availableCollections, collectionNames, packs, ctx.debug);
44
+ if (aliasToPack.size === 0) return { changed: false };
45
+ return transformTSXFileCore(ctx.code, ctx.id, ctx.ast, ctx.s, aliasToPack, collectionNames, availableCollections, packs, ctx.debug);
46
+ },
47
+ resolveId: (source) => {
48
+ if (source.startsWith("virtual:icons/")) return `\0${source}`;
49
+ return null;
50
+ },
51
+ load: async (id) => {
52
+ if (!id.startsWith("\0virtual:icons/")) return null;
53
+ const parts = id.slice(1).split("/");
54
+ const prefix = parts[1];
55
+ const name = parts[2];
56
+ if (!prefix || !name) return null;
57
+ try {
58
+ const iconData = name.includes("-") ? await collectionLoader.loadIconDataLazy(prefix, name) : await collectionLoader.loadIconDataByLowercase(prefix, name);
59
+ if (!iconData) return { code: `export default '<path d="M12 2L2 7l10 5 10-5z"/><path d="M2 17l10 5 10-5M2 12l10 5 10-5"/>';\n` };
60
+ return { code: `export default \`${iconData.body}\`;` };
61
+ } catch {
62
+ return { code: `export default '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>';\n` };
63
+ }
64
+ }
65
+ };
66
+ };
67
+ const createAsChildSubPlugin = (_debug) => ({
68
+ name: "as-child",
69
+ extensions: /\.(tsx|jsx)$/,
70
+ transform: (ctx) => {
71
+ if (!ctx.ast) return { changed: false };
72
+ return asChildTransformCore(ctx.ast, ctx.s, ctx.code, ctx.debug);
73
+ }
74
+ });
75
+ const buildActivePlugins = (options, isDebugMode) => {
76
+ const plugins = [];
77
+ if (options.ui !== false) {
78
+ const uiOpts = typeof options.ui === "object" ? options.ui : true;
79
+ plugins.push(createUiSubPlugin(uiOpts, createDebug("ui", isDebugMode)));
80
+ }
81
+ if (options.icons) {
82
+ const iconsOpts = typeof options.icons === "object" ? options.icons : true;
83
+ plugins.push(createIconsSubPlugin(iconsOpts, createDebug("icons", isDebugMode)));
84
+ }
85
+ if (options.asChild) plugins.push(createAsChildSubPlugin(createDebug("as-child", isDebugMode)));
86
+ return plugins;
87
+ };
88
+ const qds = (options = {}, _testSubPlugins) => {
89
+ const isDebugMode = !!options.debug;
90
+ const activePlugins = _testSubPlugins ?? buildActivePlugins(options, isDebugMode);
91
+ const combinedFilter = buildCombinedFilter(activePlugins);
92
+ const hasResolveId = activePlugins.some((p) => "resolveId" in p);
93
+ const hasLoad = activePlugins.some((p) => "load" in p);
94
+ const plugin = {
95
+ name: "vite-plugin-qds",
96
+ enforce: "pre",
97
+ transform: {
98
+ filter: { id: combinedFilter },
99
+ handler(code, id) {
100
+ const isMDX = id.endsWith(".mdx");
101
+ let ast = null;
102
+ if (!isMDX) {
103
+ const parsed = parseSync(id, code);
104
+ if (parsed.errors.length > 0) return null;
105
+ ast = parsed.program;
106
+ }
107
+ const s = new MagicString(code);
108
+ let changed = false;
109
+ for (const subPlugin of activePlugins) {
110
+ if (!subPlugin.extensions.test(id)) continue;
111
+ if (subPlugin.transform({
112
+ code,
113
+ id,
114
+ ast,
115
+ s,
116
+ debug: createDebug(subPlugin.name, isDebugMode),
117
+ warn: this?.warn?.bind(this)
118
+ })?.changed) changed = true;
119
+ }
120
+ if (!changed) return null;
121
+ return {
122
+ code: s.toString(),
123
+ map: s.generateMap({ hires: true })
124
+ };
125
+ }
126
+ }
127
+ };
128
+ if (hasResolveId) plugin.resolveId = { handler(source) {
129
+ for (const subPlugin of activePlugins) {
130
+ if (!subPlugin.resolveId) continue;
131
+ const result = subPlugin.resolveId(source);
132
+ if (result !== null) return result;
133
+ }
134
+ return null;
135
+ } };
136
+ if (hasLoad) plugin.load = { async handler(id) {
137
+ for (const subPlugin of activePlugins) {
138
+ if (!subPlugin.load) continue;
139
+ return subPlugin.load(id);
140
+ }
141
+ return null;
142
+ } };
143
+ return plugin;
144
+ };
145
+
146
+ //#endregion
147
+ export { qds };
@@ -0,0 +1,6 @@
1
+ import type { Plugin } from "rolldown";
2
+ /**
3
+ * Plugin to ensure .qwik.mjs extension and correct output dir
4
+ * Runs after qwikRollup to override its settings
5
+ */
6
+ export declare function qwikRolldown(): Plugin;
@@ -0,0 +1,42 @@
1
+ import type { JSXExpressionContainer, StaticMemberExpression } from "@oxc-project/types";
2
+ export type UIPluginOptions = {
3
+ debug?: boolean;
4
+ importSources?: string[];
5
+ };
6
+ export type BoundNamespace = {
7
+ localName: string;
8
+ importedName: string;
9
+ };
10
+ export type StateReference = {
11
+ node: StaticMemberExpression;
12
+ namespace: BoundNamespace;
13
+ getterName: string;
14
+ contextField: string;
15
+ start: number;
16
+ end: number;
17
+ };
18
+ export type AliasBinding = {
19
+ localName: string;
20
+ namespace: BoundNamespace;
21
+ getterName: string;
22
+ contextField: string;
23
+ };
24
+ export type TransformTarget = {
25
+ kind: "expression" | "element";
26
+ container?: JSXExpressionContainer;
27
+ stateRefs: StateReference[];
28
+ aliasRefs: AliasBinding[];
29
+ start: number;
30
+ end: number;
31
+ };
32
+ export type HookTarget = {
33
+ hookStart: number;
34
+ hookEnd: number;
35
+ stateRefs: StateReference[];
36
+ aliasRefs: AliasBinding[];
37
+ namespace: BoundNamespace;
38
+ contextField: string;
39
+ rootOpeningEnd: number;
40
+ aliasDeclaratorStart: number;
41
+ aliasDeclaratorEnd: number;
42
+ };
@@ -0,0 +1,12 @@
1
+ import type { Program } from "@oxc-project/types";
2
+ import type { Plugin } from "rolldown";
3
+ import MagicString from "magic-string";
4
+ import type { UIPluginOptions } from "./ui-types";
5
+ export type UiTransformCoreResult = {
6
+ changed: boolean;
7
+ };
8
+ export declare function uiTransformCore(code: string, id: string, ast: Program, s: MagicString, importSources: string[], debug: (message: string, ...data: unknown[]) => void, warn?: (message: string) => void): UiTransformCoreResult;
9
+ export declare const ui: (options?: UIPluginOptions) => Plugin & {
10
+ enforce?: "pre" | "post";
11
+ };
12
+ export type { UIPluginOptions as UiPluginOptions } from "./ui-types";