openxiangda 1.0.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/README.md +58 -0
- package/bin/openxiangda.js +11 -0
- package/lib/cli.js +2423 -0
- package/lib/config.js +121 -0
- package/lib/http.js +47 -0
- package/lib/skills.js +371 -0
- package/lib/utils.js +87 -0
- package/lib/workspace-init.js +139 -0
- package/openxiangda-skills/SKILL.md +128 -0
- package/openxiangda-skills/references/architecture-patterns.md +242 -0
- package/openxiangda-skills/references/automation-v3.md +129 -0
- package/openxiangda-skills/references/component-guide.md +198 -0
- package/openxiangda-skills/references/forms/component-registry.md +53 -0
- package/openxiangda-skills/references/forms/form-schema.md +109 -0
- package/openxiangda-skills/references/forms/layout-and-rules.md +24 -0
- package/openxiangda-skills/references/openxiangda-api.md +466 -0
- package/openxiangda-skills/references/pages/page-sdk.md +13 -0
- package/openxiangda-skills/references/pages/publish-flow.md +36 -0
- package/openxiangda-skills/references/pages/workspace-structure.md +38 -0
- package/openxiangda-skills/references/permissions-settings.md +147 -0
- package/openxiangda-skills/references/platform-data-model.md +305 -0
- package/openxiangda-skills/references/style-system.md +492 -0
- package/openxiangda-skills/references/troubleshooting.md +246 -0
- package/openxiangda-skills/references/workflow-v3.md +105 -0
- package/openxiangda-skills/references/workspace-state.md +45 -0
- package/openxiangda-skills/skills/openxiangda-app/SKILL.md +64 -0
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +143 -0
- package/openxiangda-skills/skills/openxiangda-form/SKILL.md +76 -0
- package/openxiangda-skills/skills/openxiangda-inspect/SKILL.md +40 -0
- package/openxiangda-skills/skills/openxiangda-page/SKILL.md +62 -0
- package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +95 -0
- package/openxiangda-skills/skills/openxiangda-workflow-automation/SKILL.md +97 -0
- package/package.json +126 -0
- package/packages/sdk/bin/lowcode-workspace.mjs +4 -0
- package/packages/sdk/dist/build/index.cjs +33 -0
- package/packages/sdk/dist/build/index.cjs.map +1 -0
- package/packages/sdk/dist/build/index.d.mts +40 -0
- package/packages/sdk/dist/build/index.d.ts +40 -0
- package/packages/sdk/dist/build/index.mjs +8 -0
- package/packages/sdk/dist/build/index.mjs.map +1 -0
- package/packages/sdk/dist/components/index.cjs +18700 -0
- package/packages/sdk/dist/components/index.cjs.map +1 -0
- package/packages/sdk/dist/components/index.d.mts +2094 -0
- package/packages/sdk/dist/components/index.d.ts +2094 -0
- package/packages/sdk/dist/components/index.mjs +18649 -0
- package/packages/sdk/dist/components/index.mjs.map +1 -0
- package/packages/sdk/dist/runtime/index.cjs +1469 -0
- package/packages/sdk/dist/runtime/index.cjs.map +1 -0
- package/packages/sdk/dist/runtime/index.d.mts +831 -0
- package/packages/sdk/dist/runtime/index.d.ts +831 -0
- package/packages/sdk/dist/runtime/index.mjs +1420 -0
- package/packages/sdk/dist/runtime/index.mjs.map +1 -0
- package/packages/sdk/dist/styles/antd-theme.cjs +60 -0
- package/packages/sdk/dist/styles/antd-theme.cjs.map +1 -0
- package/packages/sdk/dist/styles/antd-theme.d.mts +5 -0
- package/packages/sdk/dist/styles/antd-theme.d.ts +5 -0
- package/packages/sdk/dist/styles/antd-theme.mjs +35 -0
- package/packages/sdk/dist/styles/antd-theme.mjs.map +1 -0
- package/packages/sdk/dist/styles/tailwind-preset.cjs +2641 -0
- package/packages/sdk/dist/styles/tailwind-preset.cjs.map +1 -0
- package/packages/sdk/dist/styles/tailwind-preset.d.mts +75 -0
- package/packages/sdk/dist/styles/tailwind-preset.d.ts +75 -0
- package/packages/sdk/dist/styles/tailwind-preset.mjs +2618 -0
- package/packages/sdk/dist/styles/tailwind-preset.mjs.map +1 -0
- package/packages/sdk/dist/styles/tokens.css +73 -0
- package/packages/sdk/src/build-source/README.md +9 -0
- package/packages/sdk/src/build-source/bin/lowcode-workspace.mjs +7 -0
- package/packages/sdk/src/build-source/package.json +34 -0
- package/packages/sdk/src/build-source/scripts/build-forms.mjs +824 -0
- package/packages/sdk/src/build-source/scripts/build-forms.runtime-entry.test.ts +18 -0
- package/packages/sdk/src/build-source/scripts/build-pages.mjs +793 -0
- package/packages/sdk/src/build-source/scripts/build-workspace.mjs +64 -0
- package/packages/sdk/src/build-source/scripts/publish-all.mjs +127 -0
- package/packages/sdk/src/build-source/scripts/publish-oss.mjs +149 -0
- package/packages/sdk/src/build-source/scripts/register-bundle.mjs +1 -0
- package/packages/sdk/src/build-source/scripts/register.mjs +329 -0
- package/packages/sdk/src/build-source/scripts/sync-schema.mjs +301 -0
- package/packages/sdk/src/build-source/scripts/utils/form-api.mjs +639 -0
- package/packages/sdk/src/build-source/scripts/utils/form-api.test.ts +244 -0
- package/packages/sdk/src/build-source/scripts/utils/form-runtime-assets.mjs +57 -0
- package/packages/sdk/src/build-source/scripts/utils/form-runtime-assets.test.ts +135 -0
- package/packages/sdk/src/build-source/scripts/utils/incremental.mjs +210 -0
- package/packages/sdk/src/build-source/scripts/utils/load-config.mjs +257 -0
- package/packages/sdk/src/build-source/scripts/utils/load-config.test.ts +44 -0
- package/packages/sdk/src/build-source/scripts/utils/mime-types.mjs +70 -0
- package/packages/sdk/src/build-source/scripts/utils/namespace-css.mjs +61 -0
- package/packages/sdk/src/build-source/scripts/utils/oss-client.mjs +128 -0
- package/packages/sdk/src/build-source/scripts/utils/pages.mjs +80 -0
- package/packages/sdk/src/build-source/scripts/utils/progress.mjs +57 -0
- package/packages/sdk/src/build-source/scripts/utils/register-payload.mjs +89 -0
- package/packages/sdk/src/build-source/scripts/utils/register-payload.test.ts +76 -0
- package/packages/sdk/src/build-source/scripts/utils/runtime-css-check.mjs +44 -0
- package/packages/sdk/src/build-source/scripts/utils/runtime-css-check.test.ts +54 -0
- package/packages/sdk/src/build-source/scripts/utils/schema-transform.mjs +130 -0
- package/packages/sdk/src/build-source/scripts/utils/schema-transform.test.ts +141 -0
- package/packages/sdk/src/build-source/scripts/utils/tailwind-config.mjs +227 -0
- package/packages/sdk/src/build-source/scripts/utils/tailwind-config.test.ts +187 -0
- package/packages/sdk/src/build-source/src/cli.mjs +679 -0
- package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +34 -0
- package/templates/sy-lowcode-app-workspace/examples/forms/customer/page.tsx +1 -0
- package/templates/sy-lowcode-app-workspace/examples/forms/customer/schema.ts +35 -0
- package/templates/sy-lowcode-app-workspace/index.html +12 -0
- package/templates/sy-lowcode-app-workspace/package.json +49 -0
- package/templates/sy-lowcode-app-workspace/postcss.config.cjs +6 -0
- package/templates/sy-lowcode-app-workspace/scripts/build-js-code.mjs +100 -0
- package/templates/sy-lowcode-app-workspace/src/dev/App.tsx +26 -0
- package/templates/sy-lowcode-app-workspace/src/forms/.gitkeep +1 -0
- package/templates/sy-lowcode-app-workspace/src/forms/README.md +48 -0
- package/templates/sy-lowcode-app-workspace/src/index.css +28 -0
- package/templates/sy-lowcode-app-workspace/src/js-code-nodes/.gitkeep +1 -0
- package/templates/sy-lowcode-app-workspace/src/js-code-nodes/types.d.ts +3 -0
- package/templates/sy-lowcode-app-workspace/src/main.tsx +36 -0
- package/templates/sy-lowcode-app-workspace/src/pages/.gitkeep +1 -0
- package/templates/sy-lowcode-app-workspace/src/shared/form-schema.ts +128 -0
- package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +31 -0
- package/templates/sy-lowcode-app-workspace/tailwind.config.cjs +30 -0
- package/templates/sy-lowcode-app-workspace/tsconfig.app.json +24 -0
- package/templates/sy-lowcode-app-workspace/tsconfig.js-code-nodes.json +15 -0
- package/templates/sy-lowcode-app-workspace/tsconfig.json +7 -0
- package/templates/sy-lowcode-app-workspace/tsconfig.node.json +10 -0
- package/templates/sy-lowcode-app-workspace/vite.config.ts +32 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const managedHeader = `const openxiangdaPath = require("node:path");
|
|
5
|
+
const openxiangdaPresetModule = require("openxiangda/tailwind-preset");
|
|
6
|
+
const openxiangdaPreset =
|
|
7
|
+
openxiangdaPresetModule.default ?? openxiangdaPresetModule;
|
|
8
|
+
|
|
9
|
+
function resolveOpenXiangdaContent() {
|
|
10
|
+
try {
|
|
11
|
+
const packagePath = require.resolve("openxiangda");
|
|
12
|
+
const distDir = openxiangdaPath.dirname(packagePath);
|
|
13
|
+
return [openxiangdaPath.join(distDir, "..", "**/*.{js,mjs,cjs}")];
|
|
14
|
+
} catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const openxiangdaContent = resolveOpenXiangdaContent();
|
|
20
|
+
`;
|
|
21
|
+
const tailwindDirectives = ["base", "components", "utilities"];
|
|
22
|
+
const requiredBlocklistEntries = ['"[-:T]"', '"[-:TZ.]"'];
|
|
23
|
+
const layeredTailwindCss = `@import "openxiangda/styles/tokens.css";
|
|
24
|
+
|
|
25
|
+
@layer tailwind-base {
|
|
26
|
+
@tailwind base;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@tailwind components;
|
|
30
|
+
@tailwind utilities;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
export const canonicalTailwindConfig = `${managedHeader}
|
|
34
|
+
/** @type {import('tailwindcss').Config} */
|
|
35
|
+
module.exports = {
|
|
36
|
+
content: [
|
|
37
|
+
"./index.html",
|
|
38
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
39
|
+
...openxiangdaContent,
|
|
40
|
+
],
|
|
41
|
+
blocklist: ["[-:T]", "[-:TZ.]"],
|
|
42
|
+
presets: [openxiangdaPreset],
|
|
43
|
+
theme: {
|
|
44
|
+
extend: {},
|
|
45
|
+
},
|
|
46
|
+
plugins: [],
|
|
47
|
+
};
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
export function isWorkspaceTailwindConfigCurrent(content) {
|
|
51
|
+
return Boolean(
|
|
52
|
+
content.includes("openxiangda/tailwind-preset") &&
|
|
53
|
+
content.includes("resolveOpenXiangdaContent") &&
|
|
54
|
+
content.includes("openxiangdaContent") &&
|
|
55
|
+
content.includes("openxiangdaPreset") &&
|
|
56
|
+
content.includes("...openxiangdaContent") &&
|
|
57
|
+
hasBlocklistEntry(content, "[-:T]") &&
|
|
58
|
+
hasBlocklistEntry(content, "[-:TZ.]"),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function isWorkspaceTailwindCssCurrent(content) {
|
|
63
|
+
return (
|
|
64
|
+
/@layer\s+tailwind-base\s*\{\s*@tailwind\s+base\s*;\s*\}/s.test(content) &&
|
|
65
|
+
!/@layer\s+tailwind-base\s*,\s*antd\s*;/.test(content) &&
|
|
66
|
+
tailwindDirectives
|
|
67
|
+
.filter((directive) => directive !== "base")
|
|
68
|
+
.every((directive) =>
|
|
69
|
+
new RegExp(`@tailwind\\s+${directive}\\s*;`).test(content),
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function hasBlocklistEntry(content, entry) {
|
|
75
|
+
const singleQuoted = `'${entry.slice(1, -1)}'`;
|
|
76
|
+
return content.includes(entry) || content.includes(singleQuoted);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function insertAfterModuleExportsOpen(content, propertyLine) {
|
|
80
|
+
return content.replace(
|
|
81
|
+
/module\.exports\s*=\s*\{\s*/,
|
|
82
|
+
(match) => `${match}\n ${propertyLine}\n`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function ensureManagedHeader(content) {
|
|
87
|
+
if (content.includes("resolveOpenXiangdaContent")) return content;
|
|
88
|
+
return `${managedHeader}\n${content}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function ensureContentScan(content) {
|
|
92
|
+
if (content.includes("...openxiangdaContent")) {
|
|
93
|
+
return content;
|
|
94
|
+
}
|
|
95
|
+
if (/content\s*:\s*\[/.test(content)) {
|
|
96
|
+
return content.replace(
|
|
97
|
+
/content\s*:\s*\[/,
|
|
98
|
+
"content: [\n ...openxiangdaContent,",
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return insertAfterModuleExportsOpen(
|
|
102
|
+
content,
|
|
103
|
+
'content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}", ...openxiangdaContent],',
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function ensurePreset(content) {
|
|
108
|
+
if (/presets\s*:\s*\[[^\]]*openxiangdaPreset/s.test(content)) {
|
|
109
|
+
return content;
|
|
110
|
+
}
|
|
111
|
+
if (/presets\s*:\s*\[/.test(content)) {
|
|
112
|
+
return content.replace(
|
|
113
|
+
/presets\s*:\s*\[/,
|
|
114
|
+
"presets: [openxiangdaPreset, ",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return insertAfterModuleExportsOpen(
|
|
118
|
+
content,
|
|
119
|
+
"presets: [openxiangdaPreset],",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function normalizeLegacyManagedNames(content) {
|
|
124
|
+
return content
|
|
125
|
+
.replace(
|
|
126
|
+
/const syLowcodePath = require\("node:path"\);[\s\S]*?const syLowcodeFormComponentsContent = resolveSyLowcodeFormComponentsContent\(\);\n*/g,
|
|
127
|
+
"",
|
|
128
|
+
)
|
|
129
|
+
.replace(/syLowcodeFormComponentsPreset/g, "openxiangdaPreset")
|
|
130
|
+
.replace(/syLowcodeFormComponentsContent/g, "openxiangdaContent")
|
|
131
|
+
.replace(/formComponentsPreset/g, "openxiangdaPreset")
|
|
132
|
+
.replace(/formComponentsContent/g, "openxiangdaContent");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function ensureBlocklist(content) {
|
|
136
|
+
if (!/blocklist\s*:\s*\[/.test(content)) {
|
|
137
|
+
return insertAfterModuleExportsOpen(
|
|
138
|
+
content,
|
|
139
|
+
'blocklist: ["[-:T]", "[-:TZ.]"],',
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let nextContent = content;
|
|
144
|
+
for (const entry of [...requiredBlocklistEntries].reverse()) {
|
|
145
|
+
if (hasBlocklistEntry(nextContent, entry)) continue;
|
|
146
|
+
nextContent = nextContent.replace(
|
|
147
|
+
/blocklist\s*:\s*\[/,
|
|
148
|
+
`blocklist: [${entry}, `,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return nextContent;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function patchWorkspaceTailwindConfig(content) {
|
|
155
|
+
if (!content.trim()) return canonicalTailwindConfig;
|
|
156
|
+
content = normalizeLegacyManagedNames(content);
|
|
157
|
+
if (isWorkspaceTailwindConfigCurrent(content)) return content;
|
|
158
|
+
|
|
159
|
+
let nextContent = ensureManagedHeader(content);
|
|
160
|
+
nextContent = ensureContentScan(nextContent);
|
|
161
|
+
nextContent = ensurePreset(nextContent);
|
|
162
|
+
nextContent = ensureBlocklist(nextContent);
|
|
163
|
+
return nextContent;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function patchWorkspaceTailwindCss(content) {
|
|
167
|
+
if (isWorkspaceTailwindCssCurrent(content)) return content;
|
|
168
|
+
|
|
169
|
+
const customCss = content
|
|
170
|
+
.replace(/@import\s+["']openxiangda\/styles\/tokens\.css["'];\s*/g, "")
|
|
171
|
+
.replace(/@layer\s+tailwind-base\s*,\s*antd\s*;\s*/g, "")
|
|
172
|
+
.replace(/@layer\s+tailwind-base\s*\{\s*@tailwind\s+base\s*;\s*\}\s*/gs, "")
|
|
173
|
+
.replace(/@tailwind\s+(?:base|components|utilities)\s*;\s*/g, "")
|
|
174
|
+
.trimStart();
|
|
175
|
+
|
|
176
|
+
return customCss ? `${layeredTailwindCss}\n${customCss}` : layeredTailwindCss;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function ensureWorkspaceTailwindConfig(workspaceRoot) {
|
|
180
|
+
const configPath = path.join(workspaceRoot, "tailwind.config.cjs");
|
|
181
|
+
const current = fs.existsSync(configPath)
|
|
182
|
+
? fs.readFileSync(configPath, "utf-8")
|
|
183
|
+
: "";
|
|
184
|
+
const nextContent = patchWorkspaceTailwindConfig(current);
|
|
185
|
+
if (current !== nextContent) {
|
|
186
|
+
fs.writeFileSync(configPath, nextContent, "utf-8");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const cssPath = path.join(workspaceRoot, "src", "index.css");
|
|
190
|
+
const currentCss = fs.existsSync(cssPath)
|
|
191
|
+
? fs.readFileSync(cssPath, "utf-8")
|
|
192
|
+
: "";
|
|
193
|
+
const nextCss = patchWorkspaceTailwindCss(currentCss);
|
|
194
|
+
if (currentCss !== nextCss) {
|
|
195
|
+
fs.mkdirSync(path.dirname(cssPath), { recursive: true });
|
|
196
|
+
fs.writeFileSync(cssPath, nextCss, "utf-8");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
changed: current !== nextContent || currentCss !== nextCss,
|
|
201
|
+
path: configPath,
|
|
202
|
+
cssPath,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function validateWorkspaceTailwindConfig(workspaceRoot) {
|
|
207
|
+
const configPath = path.join(workspaceRoot, "tailwind.config.cjs");
|
|
208
|
+
if (!fs.existsSync(configPath)) {
|
|
209
|
+
return ["tailwind.config.cjs is missing"];
|
|
210
|
+
}
|
|
211
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
212
|
+
if (!isWorkspaceTailwindConfigCurrent(content)) {
|
|
213
|
+
return [
|
|
214
|
+
"tailwind.config.cjs must include openxiangda/tailwind-preset and scan openxiangda package output",
|
|
215
|
+
];
|
|
216
|
+
}
|
|
217
|
+
const cssPath = path.join(workspaceRoot, "src", "index.css");
|
|
218
|
+
const cssContent = fs.existsSync(cssPath)
|
|
219
|
+
? fs.readFileSync(cssPath, "utf-8")
|
|
220
|
+
: "";
|
|
221
|
+
if (!isWorkspaceTailwindCssCurrent(cssContent)) {
|
|
222
|
+
return [
|
|
223
|
+
"src/index.css must keep Tailwind base in @layer tailwind-base and avoid declaring antd layer",
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
ensureWorkspaceTailwindConfig,
|
|
9
|
+
patchWorkspaceTailwindCss,
|
|
10
|
+
patchWorkspaceTailwindConfig,
|
|
11
|
+
validateWorkspaceTailwindConfig,
|
|
12
|
+
} from "./tailwind-config.mjs";
|
|
13
|
+
|
|
14
|
+
let tempDirs: string[] = [];
|
|
15
|
+
|
|
16
|
+
function createTempDir() {
|
|
17
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "workspace-tailwind-"));
|
|
18
|
+
tempDirs.push(dir);
|
|
19
|
+
return dir;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
for (const dir of tempDirs) {
|
|
24
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
tempDirs = [];
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("workspace Tailwind config helpers", () => {
|
|
30
|
+
it("patches stale app workspace config with openxiangda preset scanning", () => {
|
|
31
|
+
const workspaceRoot = createTempDir();
|
|
32
|
+
const configPath = path.join(workspaceRoot, "tailwind.config.cjs");
|
|
33
|
+
fs.writeFileSync(
|
|
34
|
+
configPath,
|
|
35
|
+
`module.exports = { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { extend: {} }, plugins: [] };\n`,
|
|
36
|
+
"utf-8",
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const result = ensureWorkspaceTailwindConfig(workspaceRoot);
|
|
40
|
+
const nextContent = fs.readFileSync(configPath, "utf-8");
|
|
41
|
+
const cssContent = fs.readFileSync(
|
|
42
|
+
path.join(workspaceRoot, "src", "index.css"),
|
|
43
|
+
"utf-8",
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(result.changed).toBe(true);
|
|
47
|
+
expect(nextContent).toContain(
|
|
48
|
+
'require("openxiangda/tailwind-preset")',
|
|
49
|
+
);
|
|
50
|
+
expect(nextContent).toContain("...openxiangdaContent");
|
|
51
|
+
expect(cssContent).not.toContain("@layer tailwind-base, antd;");
|
|
52
|
+
expect(cssContent).toContain("@layer tailwind-base");
|
|
53
|
+
expect(cssContent).toContain("@tailwind base;");
|
|
54
|
+
expect(cssContent).toContain("@tailwind components;");
|
|
55
|
+
expect(cssContent).toContain("@tailwind utilities;");
|
|
56
|
+
expect(validateWorkspaceTailwindConfig(workspaceRoot)).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("preserves custom Tailwind config while adding runtime requirements", () => {
|
|
60
|
+
const source = `const customPlugin = require("./custom-plugin");
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
darkMode: "class",
|
|
64
|
+
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}", "./features/**/*.{tsx,ts}"],
|
|
65
|
+
safelist: ["custom-safe-class"],
|
|
66
|
+
theme: {
|
|
67
|
+
extend: {
|
|
68
|
+
colors: {
|
|
69
|
+
brand: "#123456",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
plugins: [customPlugin],
|
|
74
|
+
};
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const nextContent = patchWorkspaceTailwindConfig(source);
|
|
78
|
+
|
|
79
|
+
expect(nextContent).toContain("customPlugin");
|
|
80
|
+
expect(nextContent).toContain('"./features/**/*.{tsx,ts}"');
|
|
81
|
+
expect(nextContent).toContain('safelist: ["custom-safe-class"]');
|
|
82
|
+
expect(nextContent).toContain('brand: "#123456"');
|
|
83
|
+
expect(nextContent).toContain("plugins: [customPlugin]");
|
|
84
|
+
expect(nextContent).toContain(
|
|
85
|
+
'require("openxiangda/tailwind-preset")',
|
|
86
|
+
);
|
|
87
|
+
expect(nextContent).toContain("...openxiangdaContent");
|
|
88
|
+
expect(nextContent).toContain("presets: [openxiangdaPreset]");
|
|
89
|
+
expect(nextContent).toContain('"[-:T]"');
|
|
90
|
+
expect(nextContent).toContain('"[-:TZ.]"');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("preserves existing index css while migrating to Tailwind base layer directives", () => {
|
|
94
|
+
const nextContent = patchWorkspaceTailwindCss(`@tailwind base;
|
|
95
|
+
@tailwind components;
|
|
96
|
+
@tailwind utilities;
|
|
97
|
+
|
|
98
|
+
#root {
|
|
99
|
+
min-height: 100vh;
|
|
100
|
+
}
|
|
101
|
+
`);
|
|
102
|
+
|
|
103
|
+
expect(nextContent).not.toContain("@layer tailwind-base, antd;");
|
|
104
|
+
expect(nextContent).toContain("@layer tailwind-base");
|
|
105
|
+
expect(nextContent).toContain("@tailwind base;");
|
|
106
|
+
expect(nextContent).toContain("@tailwind components;");
|
|
107
|
+
expect(nextContent).toContain("@tailwind utilities;");
|
|
108
|
+
expect(nextContent).toContain("#root");
|
|
109
|
+
expect(nextContent.match(/@tailwind\s+base\s*;/g)).toHaveLength(1);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("adds missing layered Tailwind directives without removing custom css", () => {
|
|
113
|
+
const nextContent = patchWorkspaceTailwindCss(`#root {
|
|
114
|
+
min-height: 100vh;
|
|
115
|
+
}
|
|
116
|
+
`);
|
|
117
|
+
|
|
118
|
+
expect(nextContent.startsWith("@layer tailwind-base {")).toBe(true);
|
|
119
|
+
expect(nextContent).toContain("@tailwind components;");
|
|
120
|
+
expect(nextContent).toContain("#root");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("keeps current Tailwind base layer index css unchanged", () => {
|
|
124
|
+
const source = `@layer tailwind-base {
|
|
125
|
+
@tailwind base;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@tailwind components;
|
|
129
|
+
@tailwind utilities;
|
|
130
|
+
|
|
131
|
+
#root {
|
|
132
|
+
min-height: 100vh;
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
expect(patchWorkspaceTailwindCss(source)).toBe(source);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("removes stale antd layer declaration from index css", () => {
|
|
140
|
+
const nextContent = patchWorkspaceTailwindCss(`@layer tailwind-base, antd;
|
|
141
|
+
|
|
142
|
+
@layer tailwind-base {
|
|
143
|
+
@tailwind base;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@tailwind components;
|
|
147
|
+
@tailwind utilities;
|
|
148
|
+
`);
|
|
149
|
+
|
|
150
|
+
expect(nextContent).not.toContain("@layer tailwind-base, antd;");
|
|
151
|
+
expect(nextContent.startsWith("@layer tailwind-base {")).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("reports stale config during update check", () => {
|
|
155
|
+
const workspaceRoot = createTempDir();
|
|
156
|
+
fs.writeFileSync(
|
|
157
|
+
path.join(workspaceRoot, "tailwind.config.cjs"),
|
|
158
|
+
`module.exports = { content: ["./src/**/*.{ts,tsx}"] };\n`,
|
|
159
|
+
"utf-8",
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(validateWorkspaceTailwindConfig(workspaceRoot)).toEqual([
|
|
163
|
+
"tailwind.config.cjs must include openxiangda/tailwind-preset and scan openxiangda package output",
|
|
164
|
+
]);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("reports missing Tailwind directives during update check", () => {
|
|
168
|
+
const workspaceRoot = createTempDir();
|
|
169
|
+
fs.mkdirSync(path.join(workspaceRoot, "src"), { recursive: true });
|
|
170
|
+
fs.writeFileSync(
|
|
171
|
+
path.join(workspaceRoot, "tailwind.config.cjs"),
|
|
172
|
+
patchWorkspaceTailwindConfig(
|
|
173
|
+
`module.exports = { content: ["./src/**/*.{ts,tsx}"] };\n`,
|
|
174
|
+
),
|
|
175
|
+
"utf-8",
|
|
176
|
+
);
|
|
177
|
+
fs.writeFileSync(
|
|
178
|
+
path.join(workspaceRoot, "src", "index.css"),
|
|
179
|
+
"#root { min-height: 100vh; }\n",
|
|
180
|
+
"utf-8",
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
expect(validateWorkspaceTailwindConfig(workspaceRoot)).toEqual([
|
|
184
|
+
"src/index.css must keep Tailwind base in @layer tailwind-base and avoid declaring antd layer",
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
});
|