@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.
- package/lib/linter/qds-internal.d.ts +210 -0
- package/lib/linter/qds.d.ts +59 -0
- package/{lib-types/tools → lib}/linter/rule-tester.d.ts +23 -1
- package/{lib-types/tools → lib}/playground/prop-extraction.d.ts +6 -1
- package/lib/playground/prop-extraction.qwik.mjs +68 -9
- package/lib/playground/scenario-injection.qwik.mjs +41 -8
- package/lib/rolldown/as-child.d.ts +16 -0
- package/lib/rolldown/as-child.qwik.mjs +52 -91
- package/lib/rolldown/icons.d.ts +21 -0
- package/{lib-types/tools → lib}/rolldown/index.d.ts +4 -2
- package/lib/rolldown/index.qwik.mjs +3 -3
- package/lib/rolldown/inject-component-types.d.ts +2 -0
- package/lib/rolldown/inject-component-types.qwik.mjs +138 -0
- package/lib/rolldown/inline-asset.qwik.mjs +6 -6
- package/lib/rolldown/inline-css.d.ts +2 -0
- package/lib/rolldown/inline-css.qwik.mjs +1 -1
- package/lib/rolldown/qds-types.d.ts +41 -0
- package/lib/rolldown/qds.d.ts +5 -0
- package/lib/rolldown/qds.qwik.mjs +147 -0
- package/lib/rolldown/qwik-rolldown.d.ts +6 -0
- package/lib/rolldown/ui-types.d.ts +42 -0
- package/lib/rolldown/ui.d.ts +12 -0
- package/lib/rolldown/ui.qwik.mjs +445 -0
- package/lib/utils/icons/naming.unit.d.ts +1 -0
- package/{lib-types/tools → lib}/utils/icons/transform/mdx.d.ts +3 -11
- package/lib/utils/icons/transform/mdx.qwik.mjs +14 -20
- package/{lib-types/tools → lib}/utils/icons/transform/tsx.d.ts +3 -12
- package/lib/utils/icons/transform/tsx.qwik.mjs +28 -37
- package/lib/utils/index.qwik.mjs +5 -5
- package/{lib-types/tools → lib}/utils/transform-dts.d.ts +4 -3
- package/lib/utils/transform-dts.qwik.mjs +18 -23
- package/lib/utils/transform-dts.unit.d.ts +1 -0
- package/{lib-types/tools → lib}/vite/index.d.ts +2 -2
- package/lib/vite/index.qwik.mjs +2 -3
- package/lib/vite/minify-content.qwik.mjs +1 -1
- package/lib/vite/minify-content.unit.d.ts +1 -0
- package/linter/qds-internal.ts +707 -0
- package/linter/qds-internal.unit.ts +399 -0
- package/linter/qds.ts +300 -0
- package/linter/qds.unit.ts +158 -0
- package/linter/rule-tester.ts +395 -0
- package/package.json +17 -18
- package/lib/rolldown/icons.qwik.mjs +0 -112
- package/lib-types/components/src/icons-runtime.d.ts +0 -223
- package/lib-types/tools/linter/qds-internal.d.ts +0 -7
- package/lib-types/tools/rolldown/as-child.d.ts +0 -24
- package/lib-types/tools/rolldown/icons.d.ts +0 -45
- package/lib-types/tools/rolldown/inline-css.d.ts +0 -26
- package/lib-types/tools/rolldown/qwik-rolldown.d.ts +0 -9
- /package/{lib-types/tools → lib}/linter/qds-internal.unit.d.ts +0 -0
- /package/{lib-types/tools/playground/generate-metadata.d.ts → lib/linter/qds.unit.d.ts} +0 -0
- /package/{lib-types/tools/playground/generate-metadata.unit.d.ts → lib/playground/generate-metadata.d.ts} +0 -0
- /package/{lib-types/tools/playground/prop-extraction.unit.d.ts → lib/playground/generate-metadata.unit.d.ts} +0 -0
- /package/{lib-types/tools → lib}/playground/index.d.ts +0 -0
- /package/{lib-types/tools/playground/scenario-injection.unit.d.ts → lib/playground/prop-extraction.unit.d.ts} +0 -0
- /package/{lib-types/tools → lib}/playground/scenario-injection.d.ts +0 -0
- /package/{lib-types/tools/rolldown/as-child.unit.d.ts → lib/playground/scenario-injection.unit.d.ts} +0 -0
- /package/{lib-types/tools/rolldown/icons.unit.d.ts → lib/rolldown/as-child.unit.d.ts} +0 -0
- /package/{lib-types/tools/rolldown/inline-asset.unit.d.ts → lib/rolldown/icons.unit.d.ts} +0 -0
- /package/{lib-types/tools/src/generate/icon-types.unit.d.ts → lib/rolldown/inject-component-types.unit.d.ts} +0 -0
- /package/{lib-types/tools → lib}/rolldown/inline-asset.d.ts +0 -0
- /package/{lib-types/tools/utils/icons/ast/expressions.unit.d.ts → lib/rolldown/inline-asset.unit.d.ts} +0 -0
- /package/{lib-types/tools/utils/icons/ast/jsx.unit.d.ts → lib/rolldown/qds.unit.d.ts} +0 -0
- /package/{lib-types/tools/utils/icons/naming.unit.d.ts → lib/rolldown/ui.unit.d.ts} +0 -0
- /package/{lib-types/tools → lib}/src/generate/icon-types.d.ts +0 -0
- /package/{lib-types/tools → lib}/src/index.d.ts +0 -0
- /package/{lib-types/tools → lib}/src/vite.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/ast/core.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/ast/imports.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/ast/jsx-helpers.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/ast/qwik.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/fs-mock.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/fs.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/icons/ast/expressions.d.ts +0 -0
- /package/{lib-types/tools/utils/transform-dts.unit.d.ts → lib/utils/icons/ast/expressions.unit.d.ts} +0 -0
- /package/{lib-types/tools → lib}/utils/icons/ast/jsx.d.ts +0 -0
- /package/{lib-types/tools/vite/minify-content.unit.d.ts → lib/utils/icons/ast/jsx.unit.d.ts} +0 -0
- /package/{lib-types/tools → lib}/utils/icons/collections/loader.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/icons/import-resolver.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/icons/naming.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/icons/transform/shared.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/icons/types/mdx-ast.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/index.d.ts +0 -0
- /package/{lib-types/tools → lib}/utils/package-json.d.ts +0 -0
- /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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
45
|
+
debug(`⚠️ Skipping unsupported expression type: ${child.expression.type} at line ${getLineNumber(source, elem.start)}`);
|
|
94
46
|
return;
|
|
95
47
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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 {
|
|
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 {
|
|
7
|
+
export { injectComponentTypes, inlineAsset, inlineCssPlugin, qds, qwikRolldown };
|
|
@@ -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 {
|
|
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 {
|
|
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,
|
|
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
|
-
...
|
|
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
|
|
79
|
-
resolvedPath = join(dirname(nodeRequire.resolve(`${packageName}/package.json`, { paths: [
|
|
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);
|
|
@@ -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,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,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";
|