@qds.dev/tools 0.11.2 → 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 +204 -1
- package/lib/linter/qds.d.ts +59 -0
- package/lib/linter/qds.unit.d.ts +1 -0
- package/lib/linter/rule-tester.d.ts +23 -1
- package/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 +6 -5
- package/lib/rolldown/as-child.qwik.mjs +52 -91
- package/lib/rolldown/index.d.ts +3 -2
- package/lib/rolldown/index.qwik.mjs +2 -3
- package/lib/rolldown/inject-component-types.qwik.mjs +1 -1
- package/lib/rolldown/inline-asset.qwik.mjs +6 -6
- 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/qds.unit.d.ts +1 -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/rolldown/ui.unit.d.ts +1 -0
- package/lib/utils/icons/transform/mdx.d.ts +3 -11
- package/lib/utils/icons/transform/mdx.qwik.mjs +14 -20
- package/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/utils/transform-dts.qwik.mjs +1 -1
- package/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/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 +8 -7
- package/lib/rolldown/icons.qwik.mjs +0 -107
|
@@ -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 };
|
package/lib/rolldown/index.d.ts
CHANGED
|
@@ -1,9 +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";
|
|
7
5
|
export { injectComponentTypes } from "./inject-component-types";
|
|
8
6
|
export { inlineCssPlugin } from "./inline-css";
|
|
9
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,8 +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";
|
|
4
2
|
import { injectComponentTypes } from "./inject-component-types.qwik.mjs";
|
|
5
3
|
import { inlineCssPlugin } from "./inline-css.qwik.mjs";
|
|
6
4
|
import { qwikRolldown } from "./qwik-rolldown.qwik.mjs";
|
|
5
|
+
import { qds } from "./qds.qwik.mjs";
|
|
7
6
|
|
|
8
|
-
export {
|
|
7
|
+
export { injectComponentTypes, inlineAsset, inlineCssPlugin, qds, qwikRolldown };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getNodeText } from "../utils/ast/core.qwik.mjs";
|
|
2
2
|
import { detectsRenderComponentUsage } from "../utils/ast/qwik.qwik.mjs";
|
|
3
|
+
import { dirname, join, relative } from "node:path";
|
|
3
4
|
import { anyOf, createRegExp } from "magic-regexp";
|
|
4
5
|
import MagicString from "magic-string";
|
|
5
6
|
import { parseSync } from "oxc-parser";
|
|
6
7
|
import { walk } from "oxc-walker";
|
|
7
|
-
import { dirname, join, relative } from "node:path";
|
|
8
8
|
|
|
9
9
|
//#region rolldown/inject-component-types.ts
|
|
10
10
|
const tsFilePattern = createRegExp(anyOf(".tsx", ".ts"));
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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";
|