@4399ywkf/core 5.0.12 → 5.0.13
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/dist/index.d.ts +61 -15
- package/dist/index.js +630 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2763,33 +2763,38 @@ import { join as join8 } from "path";
|
|
|
2763
2763
|
var i18nPlugin = createPlugin((options = {}) => ({
|
|
2764
2764
|
name: "@4399ywkf/plugin-i18n",
|
|
2765
2765
|
version: "1.0.0",
|
|
2766
|
-
description: "\u56FD\u9645\u5316 (i18next) \u63D2\u4EF6",
|
|
2766
|
+
description: "\u56FD\u9645\u5316 (i18next) \u63D2\u4EF6 \u2014 TS-first \u5DE5\u4F5C\u6D41",
|
|
2767
2767
|
setup(context) {
|
|
2768
2768
|
const {
|
|
2769
2769
|
defaultLocale = "zh-CN",
|
|
2770
2770
|
locales = ["zh-CN", "en-US"],
|
|
2771
2771
|
localesDir = "locales",
|
|
2772
|
+
sourceDir = "src/locales/default",
|
|
2772
2773
|
defaultNS = ["common"],
|
|
2773
2774
|
autoScaffold = true
|
|
2774
2775
|
} = options;
|
|
2775
|
-
const { cwd, logger } = context;
|
|
2776
|
+
const { cwd, logger, isDev } = context;
|
|
2776
2777
|
if (autoScaffold) {
|
|
2777
|
-
|
|
2778
|
+
scaffoldSourceFiles(cwd, sourceDir, defaultNS, logger);
|
|
2779
|
+
scaffoldResourcesFile(cwd, sourceDir, defaultNS, locales, defaultLocale, logger);
|
|
2780
|
+
scaffoldConvertScript(cwd, sourceDir, localesDir, defaultLocale, logger);
|
|
2781
|
+
scaffoldI18nRc(cwd, localesDir, defaultLocale, locales, logger);
|
|
2782
|
+
scaffoldLocaleJsonDirs(cwd, localesDir, locales, defaultLocale, defaultNS, logger);
|
|
2778
2783
|
}
|
|
2779
2784
|
const hooks = {
|
|
2780
2785
|
modifyRspackConfig(rspackConfig) {
|
|
2781
2786
|
const aliases = rspackConfig.resolve?.alias || {};
|
|
2782
2787
|
if (!aliases["@locales"]) {
|
|
2783
2788
|
aliases["@locales"] = join8(cwd, localesDir);
|
|
2784
|
-
rspackConfig.resolve = { ...rspackConfig.resolve, alias: aliases };
|
|
2785
2789
|
}
|
|
2790
|
+
rspackConfig.resolve = { ...rspackConfig.resolve, alias: aliases };
|
|
2786
2791
|
return rspackConfig;
|
|
2787
2792
|
},
|
|
2788
2793
|
generateFiles(ctx) {
|
|
2789
2794
|
return [
|
|
2790
2795
|
{
|
|
2791
2796
|
path: "i18n.ts",
|
|
2792
|
-
content: generateI18nCode(defaultLocale, locales, localesDir, defaultNS)
|
|
2797
|
+
content: generateI18nCode({ defaultLocale, locales, localesDir, sourceDir, defaultNS, isDev })
|
|
2793
2798
|
}
|
|
2794
2799
|
];
|
|
2795
2800
|
},
|
|
@@ -2799,7 +2804,6 @@ var i18nPlugin = createPlugin((options = {}) => ({
|
|
|
2799
2804
|
`import { initI18n } from "./i18n";`
|
|
2800
2805
|
],
|
|
2801
2806
|
topLevel: [
|
|
2802
|
-
`// \u521D\u59CB\u5316 i18n\uFF08\u5728\u5E94\u7528\u542F\u52A8\u524D\uFF09`,
|
|
2803
2807
|
`await initI18n();`
|
|
2804
2808
|
]
|
|
2805
2809
|
};
|
|
@@ -2808,38 +2812,43 @@ var i18nPlugin = createPlugin((options = {}) => ({
|
|
|
2808
2812
|
return hooks;
|
|
2809
2813
|
}
|
|
2810
2814
|
}));
|
|
2811
|
-
function generateI18nCode(
|
|
2815
|
+
function generateI18nCode(opts) {
|
|
2816
|
+
const { defaultLocale, locales, defaultNS, sourceDir } = opts;
|
|
2817
|
+
const defaultDirAlias = sourceDir.replace(/^src\//, "");
|
|
2818
|
+
const parentDirAlias = defaultDirAlias.replace(/\/[^/]+$/, "");
|
|
2812
2819
|
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-i18n \u81EA\u52A8\u751F\u6210
|
|
2813
2820
|
import i18n from "i18next";
|
|
2814
2821
|
import LanguageDetector from "i18next-browser-languagedetector";
|
|
2815
2822
|
import resourcesToBackend from "i18next-resources-to-backend";
|
|
2816
2823
|
import { initReactI18next } from "react-i18next";
|
|
2824
|
+
import { normalizeLocale, SUPPORTED_LOCALES, DEFAULT_LOCALE } from "@/${parentDirAlias}/resources";
|
|
2817
2825
|
|
|
2818
|
-
export
|
|
2819
|
-
|
|
2820
|
-
export const SUPPORTED_LOCALES = ${JSON.stringify(locales)} as const;
|
|
2826
|
+
export { DEFAULT_LOCALE, SUPPORTED_LOCALES, normalizeLocale };
|
|
2827
|
+
export type { SupportedLocale, NS } from "@/${parentDirAlias}/resources";
|
|
2821
2828
|
|
|
2822
|
-
|
|
2829
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
2823
2830
|
|
|
2824
2831
|
const instance = i18n
|
|
2825
2832
|
.use(initReactI18next)
|
|
2826
2833
|
.use(LanguageDetector)
|
|
2827
2834
|
.use(
|
|
2828
2835
|
resourcesToBackend(async (lng: string, ns: string) => {
|
|
2829
|
-
|
|
2836
|
+
const normalizedLng = normalizeLocale(lng);
|
|
2837
|
+
|
|
2838
|
+
// \u5F00\u53D1\u6A21\u5F0F\u4E0B\uFF0C\u9ED8\u8BA4\u8BED\u8A00\u76F4\u63A5 import TS\uFF08\u5373\u65F6\u751F\u6548\uFF0C\u65E0\u9700\u5148\u751F\u6210 JSON\uFF09
|
|
2839
|
+
if (isDev && normalizedLng === "${defaultLocale}") {
|
|
2840
|
+
return import("@/${defaultDirAlias}/" + ns);
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
// \u5176\u4ED6\u8BED\u8A00 / \u751F\u4EA7\u6A21\u5F0F\uFF1A\u61D2\u52A0\u8F7D JSON
|
|
2844
|
+
return import(
|
|
2845
|
+
/* webpackInclude: /\\.json$/ */
|
|
2846
|
+
/* webpackChunkName: "locales-[request]" */
|
|
2847
|
+
\`@locales/\${normalizedLng}/\${ns}.json\`
|
|
2848
|
+
);
|
|
2830
2849
|
})
|
|
2831
2850
|
);
|
|
2832
2851
|
|
|
2833
|
-
function normalizeLocale(locale?: string): string {
|
|
2834
|
-
if (!locale) return DEFAULT_LOCALE;
|
|
2835
|
-
for (const l of SUPPORTED_LOCALES) {
|
|
2836
|
-
if (l.startsWith(locale) || locale.startsWith(l.split("-")[0])) {
|
|
2837
|
-
return l;
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
return DEFAULT_LOCALE;
|
|
2841
|
-
}
|
|
2842
|
-
|
|
2843
2852
|
let initialized = false;
|
|
2844
2853
|
|
|
2845
2854
|
export async function initI18n(lang?: string): Promise<typeof i18n> {
|
|
@@ -2860,19 +2869,191 @@ export { instance as i18n };
|
|
|
2860
2869
|
export default instance;
|
|
2861
2870
|
`;
|
|
2862
2871
|
}
|
|
2863
|
-
function
|
|
2872
|
+
function scaffoldSourceFiles(cwd, sourceDir, namespaces, logger) {
|
|
2873
|
+
const dir = join8(cwd, sourceDir);
|
|
2874
|
+
if (!existsSync8(dir)) {
|
|
2875
|
+
mkdirSync3(dir, { recursive: true });
|
|
2876
|
+
}
|
|
2877
|
+
for (const ns of namespaces) {
|
|
2878
|
+
const filePath = join8(dir, `${ns}.ts`);
|
|
2879
|
+
if (!existsSync8(filePath)) {
|
|
2880
|
+
writeFileSync4(filePath, buildDefaultNsFile(ns), "utf-8");
|
|
2881
|
+
logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u6E90\u6587\u4EF6: ${sourceDir}/${ns}.ts`);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
const indexPath = join8(dir, "index.ts");
|
|
2885
|
+
if (!existsSync8(indexPath)) {
|
|
2886
|
+
const imports = namespaces.map((ns) => `import ${ns} from "./${ns}";`).join("\n");
|
|
2887
|
+
const entries = namespaces.join(",\n ");
|
|
2888
|
+
const content = `${imports}
|
|
2889
|
+
|
|
2890
|
+
const resources = {
|
|
2891
|
+
${entries},
|
|
2892
|
+
} as const;
|
|
2893
|
+
|
|
2894
|
+
export default resources;
|
|
2895
|
+
`;
|
|
2896
|
+
writeFileSync4(indexPath, content, "utf-8");
|
|
2897
|
+
logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u7D22\u5F15: ${sourceDir}/index.ts`);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
function buildDefaultNsFile(ns) {
|
|
2901
|
+
if (ns === "common") {
|
|
2902
|
+
return `export default {
|
|
2903
|
+
welcome: "\u6B22\u8FCE\u4F7F\u7528",
|
|
2904
|
+
loading: "\u52A0\u8F7D\u4E2D...",
|
|
2905
|
+
confirm: "\u786E\u5B9A",
|
|
2906
|
+
cancel: "\u53D6\u6D88",
|
|
2907
|
+
save: "\u4FDD\u5B58",
|
|
2908
|
+
delete: "\u5220\u9664",
|
|
2909
|
+
edit: "\u7F16\u8F91",
|
|
2910
|
+
back: "\u8FD4\u56DE",
|
|
2911
|
+
} as const;
|
|
2912
|
+
`;
|
|
2913
|
+
}
|
|
2914
|
+
return `export default {
|
|
2915
|
+
title: "${ns}",
|
|
2916
|
+
} as const;
|
|
2917
|
+
`;
|
|
2918
|
+
}
|
|
2919
|
+
function scaffoldResourcesFile(cwd, sourceDir, namespaces, locales, defaultLocale, logger) {
|
|
2920
|
+
const parentDir = join8(cwd, sourceDir, "..");
|
|
2921
|
+
const filePath = join8(parentDir, "resources.ts");
|
|
2922
|
+
if (existsSync8(filePath)) return;
|
|
2923
|
+
const content = `import resources from "./default";
|
|
2924
|
+
|
|
2925
|
+
export const DEFAULT_LOCALE = "${defaultLocale}";
|
|
2926
|
+
|
|
2927
|
+
export const SUPPORTED_LOCALES = ${JSON.stringify(locales)} as const;
|
|
2928
|
+
|
|
2929
|
+
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
|
2930
|
+
|
|
2931
|
+
export type DefaultResources = typeof resources;
|
|
2932
|
+
|
|
2933
|
+
export type NS = keyof DefaultResources;
|
|
2934
|
+
|
|
2935
|
+
export const normalizeLocale = (locale?: string): SupportedLocale => {
|
|
2936
|
+
if (!locale) return DEFAULT_LOCALE as SupportedLocale;
|
|
2937
|
+
|
|
2938
|
+
for (const l of SUPPORTED_LOCALES) {
|
|
2939
|
+
if (l.startsWith(locale) || locale.startsWith(l.split("-")[0])) {
|
|
2940
|
+
return l;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
return DEFAULT_LOCALE as SupportedLocale;
|
|
2945
|
+
};
|
|
2946
|
+
|
|
2947
|
+
export const localeOptions = ${JSON.stringify(
|
|
2948
|
+
locales.map((l) => ({ value: l, label: getLocaleLabel(l) })),
|
|
2949
|
+
null,
|
|
2950
|
+
2
|
|
2951
|
+
)} as const;
|
|
2952
|
+
`;
|
|
2953
|
+
writeFileSync4(filePath, content, "utf-8");
|
|
2954
|
+
logger.info(`\u5DF2\u751F\u6210\u7C7B\u578B\u6587\u4EF6: ${sourceDir}/../resources.ts`);
|
|
2955
|
+
}
|
|
2956
|
+
function getLocaleLabel(locale) {
|
|
2957
|
+
const map = {
|
|
2958
|
+
"zh-CN": "\u7B80\u4F53\u4E2D\u6587",
|
|
2959
|
+
"zh-TW": "\u7E41\u9AD4\u4E2D\u6587",
|
|
2960
|
+
"en-US": "English",
|
|
2961
|
+
"ja-JP": "\u65E5\u672C\u8A9E",
|
|
2962
|
+
"ko-KR": "\uD55C\uAD6D\uC5B4",
|
|
2963
|
+
"de-DE": "Deutsch",
|
|
2964
|
+
"fr-FR": "Fran\xE7ais",
|
|
2965
|
+
"es-ES": "Espa\xF1ol",
|
|
2966
|
+
"pt-BR": "Portugu\xEAs",
|
|
2967
|
+
"ru-RU": "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
|
|
2968
|
+
"it-IT": "Italiano",
|
|
2969
|
+
"vi-VN": "Ti\u1EBFng Vi\u1EC7t",
|
|
2970
|
+
"ar": "\u0627\u0644\u0639\u0631\u0628\u064A\u0629"
|
|
2971
|
+
};
|
|
2972
|
+
return map[locale] || locale;
|
|
2973
|
+
}
|
|
2974
|
+
function scaffoldConvertScript(cwd, sourceDir, localesDir, defaultLocale, logger) {
|
|
2975
|
+
const scriptsDir = join8(cwd, "scripts");
|
|
2976
|
+
const scriptPath = join8(scriptsDir, "i18n-gen.js");
|
|
2977
|
+
if (existsSync8(scriptPath)) return;
|
|
2978
|
+
if (!existsSync8(scriptsDir)) {
|
|
2979
|
+
mkdirSync3(scriptsDir, { recursive: true });
|
|
2980
|
+
}
|
|
2981
|
+
const content = `const fs = require("fs");
|
|
2982
|
+
const path = require("path");
|
|
2983
|
+
|
|
2984
|
+
const sourceDir = path.join(__dirname, "../${sourceDir}");
|
|
2985
|
+
const targetDir = path.join(__dirname, "../${localesDir}/${defaultLocale}");
|
|
2986
|
+
|
|
2987
|
+
const files = fs
|
|
2988
|
+
.readdirSync(sourceDir)
|
|
2989
|
+
.filter((file) => file.endsWith(".ts") && file !== "index.ts")
|
|
2990
|
+
.map((file) => path.basename(file, ".ts"));
|
|
2991
|
+
|
|
2992
|
+
require("ts-node/register");
|
|
2993
|
+
|
|
2994
|
+
if (!fs.existsSync(targetDir)) {
|
|
2995
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
console.log("\u5F00\u59CB\u8F6C\u6362 TypeScript \u6587\u4EF6\u5230 JSON...\\n");
|
|
2999
|
+
|
|
3000
|
+
files.forEach((fileName) => {
|
|
3001
|
+
try {
|
|
3002
|
+
const modulePath = path.join(sourceDir, \`\${fileName}.ts\`);
|
|
3003
|
+
delete require.cache[require.resolve(modulePath)];
|
|
3004
|
+
const moduleContent = require(modulePath).default;
|
|
3005
|
+
const jsonContent = JSON.stringify(moduleContent, null, 2);
|
|
3006
|
+
const targetPath = path.join(targetDir, \`\${fileName}.json\`);
|
|
3007
|
+
fs.writeFileSync(targetPath, jsonContent, "utf8");
|
|
3008
|
+
console.log(\` \${fileName}.ts \u2192 \${fileName}.json\`);
|
|
3009
|
+
} catch (error) {
|
|
3010
|
+
console.error(\` \u8F6C\u6362 \${fileName}.ts \u5931\u8D25:\`, error.message);
|
|
3011
|
+
}
|
|
3012
|
+
});
|
|
3013
|
+
|
|
3014
|
+
console.log("\\n\u8F6C\u6362\u5B8C\u6210\uFF01");
|
|
3015
|
+
`;
|
|
3016
|
+
writeFileSync4(scriptPath, content, "utf-8");
|
|
3017
|
+
logger.info("\u5DF2\u751F\u6210\u8F6C\u6362\u811A\u672C: scripts/i18n-gen.js");
|
|
3018
|
+
}
|
|
3019
|
+
function scaffoldI18nRc(cwd, localesDir, defaultLocale, locales, logger) {
|
|
3020
|
+
const filePath = join8(cwd, ".i18nrc.js");
|
|
3021
|
+
if (existsSync8(filePath)) return;
|
|
3022
|
+
const outputLocales = locales.filter((l) => l !== defaultLocale).map((l) => `"${l}"`).join(", ");
|
|
3023
|
+
const content = `const { defineConfig } = require("@lobehub/i18n-cli");
|
|
3024
|
+
|
|
3025
|
+
module.exports = defineConfig({
|
|
3026
|
+
entry: "${localesDir}/${defaultLocale}",
|
|
3027
|
+
entryLocale: "${defaultLocale}",
|
|
3028
|
+
output: "${localesDir}",
|
|
3029
|
+
outputLocales: [${outputLocales}],
|
|
3030
|
+
});
|
|
3031
|
+
`;
|
|
3032
|
+
writeFileSync4(filePath, content, "utf-8");
|
|
3033
|
+
logger.info("\u5DF2\u751F\u6210\u7FFB\u8BD1\u914D\u7F6E: .i18nrc.js\uFF08@lobehub/i18n-cli\uFF09");
|
|
3034
|
+
}
|
|
3035
|
+
function scaffoldLocaleJsonDirs(cwd, localesDir, locales, defaultLocale, namespaces, logger) {
|
|
2864
3036
|
const baseDir = join8(cwd, localesDir);
|
|
2865
3037
|
for (const locale of locales) {
|
|
2866
3038
|
const localeDir = join8(baseDir, locale);
|
|
2867
3039
|
if (!existsSync8(localeDir)) {
|
|
2868
3040
|
mkdirSync3(localeDir, { recursive: true });
|
|
2869
3041
|
}
|
|
3042
|
+
if (locale === defaultLocale) continue;
|
|
2870
3043
|
for (const ns of namespaces) {
|
|
2871
3044
|
const filePath = join8(localeDir, `${ns}.json`);
|
|
2872
3045
|
if (!existsSync8(filePath)) {
|
|
2873
|
-
const
|
|
2874
|
-
|
|
2875
|
-
|
|
3046
|
+
const placeholder = ns === "common" ? JSON.stringify({
|
|
3047
|
+
welcome: "Welcome",
|
|
3048
|
+
loading: "Loading...",
|
|
3049
|
+
confirm: "OK",
|
|
3050
|
+
cancel: "Cancel",
|
|
3051
|
+
save: "Save",
|
|
3052
|
+
delete: "Delete",
|
|
3053
|
+
edit: "Edit",
|
|
3054
|
+
back: "Back"
|
|
3055
|
+
}, null, 2) : JSON.stringify({ title: ns }, null, 2);
|
|
3056
|
+
writeFileSync4(filePath, placeholder + "\n", "utf-8");
|
|
2876
3057
|
logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u6587\u4EF6: ${localesDir}/${locale}/${ns}.json`);
|
|
2877
3058
|
}
|
|
2878
3059
|
}
|
|
@@ -2880,22 +3061,37 @@ function scaffoldLocaleFiles(cwd, localesDir, locales, namespaces, logger) {
|
|
|
2880
3061
|
}
|
|
2881
3062
|
|
|
2882
3063
|
// src/plugin/builtin/theme.ts
|
|
3064
|
+
import { join as join9 } from "path";
|
|
2883
3065
|
var themePlugin = createPlugin((options = {}) => ({
|
|
2884
3066
|
name: "@4399ywkf/plugin-theme",
|
|
2885
|
-
version: "
|
|
2886
|
-
description: "antd-style \u4E3B\u9898\u7CFB\u7EDF\u63D2\u4EF6",
|
|
3067
|
+
version: "2.0.0",
|
|
3068
|
+
description: "antd-style \u54CD\u5E94\u5F0F\u4E3B\u9898\u7CFB\u7EDF\u63D2\u4EF6",
|
|
2887
3069
|
setup(context) {
|
|
2888
3070
|
const {
|
|
2889
3071
|
darkMode = true,
|
|
2890
|
-
defaultAppearance = "
|
|
2891
|
-
primaryColor = "
|
|
3072
|
+
defaultAppearance = "dark",
|
|
3073
|
+
primaryColor = "#1677ff",
|
|
3074
|
+
neutralColor,
|
|
2892
3075
|
prefixCls = "ant",
|
|
2893
3076
|
cssVar = true,
|
|
2894
|
-
globalReset = true
|
|
3077
|
+
globalReset = true,
|
|
3078
|
+
externalTheme = false
|
|
2895
3079
|
} = options;
|
|
2896
3080
|
const { logger } = context;
|
|
2897
|
-
logger.info(`\u4E3B\u9898\u6A21\u5F0F: ${defaultAppearance}, \u4E3B\u8272: ${primaryColor}`);
|
|
3081
|
+
logger.info(`\u4E3B\u9898\u6A21\u5F0F: ${defaultAppearance}, \u4E3B\u8272: ${primaryColor}, \u54CD\u5E94\u5F0F: \u2713`);
|
|
2898
3082
|
const hooks = {
|
|
3083
|
+
modifyRspackConfig(config, ctx) {
|
|
3084
|
+
const themePath = join9(ctx.cwd, ".ywkf", "theme.tsx");
|
|
3085
|
+
const currentAlias = config.resolve?.alias ?? {};
|
|
3086
|
+
config.resolve = {
|
|
3087
|
+
...config.resolve,
|
|
3088
|
+
alias: {
|
|
3089
|
+
...currentAlias,
|
|
3090
|
+
"@ywkf/theme": themePath
|
|
3091
|
+
}
|
|
3092
|
+
};
|
|
3093
|
+
return config;
|
|
3094
|
+
},
|
|
2899
3095
|
generateFiles(ctx) {
|
|
2900
3096
|
return [
|
|
2901
3097
|
{
|
|
@@ -2904,9 +3100,11 @@ var themePlugin = createPlugin((options = {}) => ({
|
|
|
2904
3100
|
darkMode,
|
|
2905
3101
|
defaultAppearance,
|
|
2906
3102
|
primaryColor,
|
|
3103
|
+
neutralColor,
|
|
2907
3104
|
prefixCls,
|
|
2908
3105
|
cssVar,
|
|
2909
|
-
globalReset
|
|
3106
|
+
globalReset,
|
|
3107
|
+
externalTheme
|
|
2910
3108
|
})
|
|
2911
3109
|
}
|
|
2912
3110
|
];
|
|
@@ -2917,61 +3115,168 @@ var themePlugin = createPlugin((options = {}) => ({
|
|
|
2917
3115
|
`import { ThemeWrapper } from "./theme";`
|
|
2918
3116
|
]
|
|
2919
3117
|
};
|
|
3118
|
+
},
|
|
3119
|
+
modifyBootstrapCode(code) {
|
|
3120
|
+
const providerEntry = ` {
|
|
3121
|
+
component: ThemeWrapper as React.ComponentType<{ children: React.ReactNode }>,
|
|
3122
|
+
props: {},
|
|
3123
|
+
order: 10,
|
|
3124
|
+
}`;
|
|
3125
|
+
if (code.includes("providers: []")) {
|
|
3126
|
+
code = code.replace(
|
|
3127
|
+
"providers: []",
|
|
3128
|
+
`providers: [
|
|
3129
|
+
${providerEntry},
|
|
3130
|
+
]`
|
|
3131
|
+
);
|
|
3132
|
+
} else if (code.includes("providers: [")) {
|
|
3133
|
+
code = code.replace(
|
|
3134
|
+
"providers: [",
|
|
3135
|
+
`providers: [
|
|
3136
|
+
${providerEntry},`
|
|
3137
|
+
);
|
|
3138
|
+
}
|
|
3139
|
+
if (!code.includes("import React")) {
|
|
3140
|
+
code = code.replace(
|
|
3141
|
+
`import { bootstrap`,
|
|
3142
|
+
`import React from "react";
|
|
3143
|
+
import { bootstrap`
|
|
3144
|
+
);
|
|
3145
|
+
}
|
|
3146
|
+
return code;
|
|
2920
3147
|
}
|
|
2921
|
-
// Provider 由生成的 theme.tsx 文件在用户层使用
|
|
2922
|
-
// 用户可以在 src/app.config.ts 中通过 providers 引入 ThemeWrapper
|
|
2923
3148
|
};
|
|
2924
3149
|
return hooks;
|
|
2925
3150
|
}
|
|
2926
3151
|
}));
|
|
2927
3152
|
function generateThemeProvider(opts) {
|
|
2928
|
-
const {
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
3153
|
+
const {
|
|
3154
|
+
darkMode,
|
|
3155
|
+
defaultAppearance,
|
|
3156
|
+
primaryColor,
|
|
3157
|
+
neutralColor,
|
|
3158
|
+
prefixCls,
|
|
3159
|
+
cssVar,
|
|
3160
|
+
globalReset,
|
|
3161
|
+
externalTheme
|
|
3162
|
+
} = opts;
|
|
3163
|
+
const sections = [];
|
|
3164
|
+
sections.push(`// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-theme \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539`);
|
|
3165
|
+
sections.push(buildImports({ globalReset, cssVar }));
|
|
3166
|
+
sections.push(TYPES_CODE);
|
|
3167
|
+
sections.push(buildStoreCode({ defaultAppearance, primaryColor, neutralColor }));
|
|
3168
|
+
sections.push(HOOKS_CODE);
|
|
3169
|
+
if (globalReset) sections.push(GLOBAL_RESET_CODE);
|
|
3170
|
+
if (cssVar) sections.push(CSS_VAR_SYNC_CODE);
|
|
3171
|
+
if (darkMode) sections.push(APPEARANCE_SYNC_CODE);
|
|
3172
|
+
if (externalTheme) sections.push(EXTERNAL_THEME_CODE);
|
|
3173
|
+
sections.push(buildWrapperCode({ darkMode, cssVar, globalReset, externalTheme, prefixCls }));
|
|
3174
|
+
return sections.join("\n");
|
|
3175
|
+
}
|
|
3176
|
+
function buildImports(opts) {
|
|
3177
|
+
const reactImports = [
|
|
3178
|
+
"useCallback",
|
|
3179
|
+
"useEffect",
|
|
3180
|
+
...opts.cssVar ? ["useLayoutEffect", "useRef"] : [],
|
|
3181
|
+
"type ReactNode"
|
|
3182
|
+
];
|
|
3183
|
+
const antdStyleImports = [
|
|
3184
|
+
"ThemeProvider as AntdThemeProvider",
|
|
3185
|
+
"type GetAntdTheme",
|
|
3186
|
+
...opts.globalReset ? ["createGlobalStyle", "css"] : []
|
|
3187
|
+
];
|
|
3188
|
+
return `
|
|
3189
|
+
import React, { ${reactImports.join(", ")} } from "react";
|
|
3190
|
+
import { ${antdStyleImports.join(", ")} } from "antd-style";
|
|
3191
|
+
import { createWithEqualityFn } from "zustand/traditional";
|
|
3192
|
+
import { shallow } from "zustand/shallow";`;
|
|
3193
|
+
}
|
|
3194
|
+
var TYPES_CODE = `
|
|
3195
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Types \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
2932
3196
|
|
|
2933
|
-
|
|
3197
|
+
export type ThemeAppearance = "light" | "dark" | "auto";
|
|
2934
3198
|
|
|
2935
|
-
|
|
3199
|
+
export interface ThemeState {
|
|
3200
|
+
appearance: ThemeAppearance;
|
|
3201
|
+
primaryColor: string;
|
|
3202
|
+
neutralColor?: string;
|
|
3203
|
+
}
|
|
2936
3204
|
|
|
2937
|
-
interface
|
|
2938
|
-
|
|
3205
|
+
export interface ThemeActions {
|
|
3206
|
+
setAppearance: (mode: ThemeAppearance) => void;
|
|
3207
|
+
setPrimaryColor: (color: string) => void;
|
|
3208
|
+
setNeutralColor: (color: string) => void;
|
|
3209
|
+
/** \u6279\u91CF\u66F4\u65B0\u4E3B\u9898\u72B6\u6001 */
|
|
3210
|
+
setTheme: (partial: Partial<ThemeState>) => void;
|
|
2939
3211
|
}
|
|
2940
3212
|
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
3213
|
+
export type ThemeStore = ThemeState & ThemeActions;`;
|
|
3214
|
+
function buildStoreCode(opts) {
|
|
3215
|
+
const neutralLine = opts.neutralColor ? `
|
|
3216
|
+
neutralColor: "${opts.neutralColor}",` : "";
|
|
3217
|
+
return `
|
|
3218
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Theme Store \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
2947
3219
|
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
theme={{
|
|
2953
|
-
cssVar: ${cssVar},
|
|
2954
|
-
token: {
|
|
2955
|
-
colorPrimary: "${primaryColor}",
|
|
2956
|
-
},
|
|
2957
|
-
}}
|
|
2958
|
-
themeMode={DEFAULT_APPEARANCE}
|
|
2959
|
-
>
|
|
2960
|
-
${globalReset ? "<GlobalReset />" : ""}
|
|
2961
|
-
{children}
|
|
2962
|
-
</ThemeProvider>
|
|
2963
|
-
);
|
|
2964
|
-
}
|
|
3220
|
+
const DEFAULT_THEME: ThemeState = {
|
|
3221
|
+
appearance: "${opts.defaultAppearance}",
|
|
3222
|
+
primaryColor: "${opts.primaryColor}",${neutralLine}
|
|
3223
|
+
};
|
|
2965
3224
|
|
|
2966
|
-
export
|
|
2967
|
-
|
|
3225
|
+
export const useThemeStore = createWithEqualityFn<ThemeStore>()(
|
|
3226
|
+
(set) => ({
|
|
3227
|
+
...DEFAULT_THEME,
|
|
3228
|
+
setAppearance: (mode) => set({ appearance: mode }),
|
|
3229
|
+
setPrimaryColor: (color) => set({ primaryColor: color }),
|
|
3230
|
+
setNeutralColor: (color) => set({ neutralColor: color }),
|
|
3231
|
+
setTheme: (partial) => set(partial),
|
|
3232
|
+
}),
|
|
3233
|
+
shallow,
|
|
3234
|
+
);`;
|
|
2968
3235
|
}
|
|
3236
|
+
var HOOKS_CODE = `
|
|
3237
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Convenience Hooks \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
3238
|
+
|
|
3239
|
+
/** \u83B7\u53D6\u5F53\u524D\u4E3B\u9898\u72B6\u6001\uFF08appearance + primaryColor + neutralColor\uFF09 */
|
|
3240
|
+
export const useTheme = () =>
|
|
3241
|
+
useThemeStore(
|
|
3242
|
+
(s) => ({
|
|
3243
|
+
appearance: s.appearance,
|
|
3244
|
+
primaryColor: s.primaryColor,
|
|
3245
|
+
neutralColor: s.neutralColor,
|
|
3246
|
+
}),
|
|
3247
|
+
shallow,
|
|
3248
|
+
);
|
|
3249
|
+
|
|
3250
|
+
/** \u4EC5\u8BA2\u9605 appearance */
|
|
3251
|
+
export const useAppearance = () => useThemeStore((s) => s.appearance);
|
|
3252
|
+
|
|
3253
|
+
/** \u4EC5\u8BA2\u9605 primaryColor */
|
|
3254
|
+
export const usePrimaryColor = () => useThemeStore((s) => s.primaryColor);`;
|
|
2969
3255
|
var GLOBAL_RESET_CODE = `
|
|
2970
|
-
|
|
3256
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Global Style \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
3257
|
+
// \u53C2\u8003 lobe-ui GlobalStyle\uFF1A\u4F7F\u7528 theme tokens \u5B9E\u73B0\u54CD\u5E94\u5F0F\u5168\u5C40\u6837\u5F0F
|
|
3258
|
+
|
|
3259
|
+
const GlobalReset = createGlobalStyle(({ theme }) => css\`
|
|
3260
|
+
:root {
|
|
3261
|
+
--font-settings: "cv01", "tnum", "kern";
|
|
3262
|
+
--font-variations: "opsz" auto, tabular-nums;
|
|
3263
|
+
}
|
|
3264
|
+
|
|
2971
3265
|
*,
|
|
2972
3266
|
*::before,
|
|
2973
3267
|
*::after {
|
|
2974
3268
|
box-sizing: border-box;
|
|
3269
|
+
vertical-align: baseline;
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
* {
|
|
3273
|
+
scrollbar-color: \${theme.colorFill} transparent;
|
|
3274
|
+
scrollbar-width: thin;
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
html {
|
|
3278
|
+
overscroll-behavior: none;
|
|
3279
|
+
color-scheme: \${theme.isDarkMode ? "dark" : "light"};
|
|
2975
3280
|
}
|
|
2976
3281
|
|
|
2977
3282
|
html, body, #root, #app {
|
|
@@ -2981,34 +3286,191 @@ const GlobalReset = createGlobalStyle\`
|
|
|
2981
3286
|
}
|
|
2982
3287
|
|
|
2983
3288
|
body {
|
|
2984
|
-
|
|
2985
|
-
|
|
3289
|
+
overflow: hidden auto;
|
|
3290
|
+
min-height: 100vh;
|
|
3291
|
+
font-family: \${theme.fontFamily};
|
|
3292
|
+
font-size: \${theme.fontSize}px;
|
|
3293
|
+
font-feature-settings: var(--font-settings);
|
|
3294
|
+
font-variation-settings: var(--font-variations);
|
|
3295
|
+
line-height: 1;
|
|
3296
|
+
color: \${theme.colorTextBase};
|
|
3297
|
+
text-size-adjust: none;
|
|
3298
|
+
text-rendering: optimizelegibility;
|
|
3299
|
+
word-wrap: break-word;
|
|
3300
|
+
background-color: \${theme.colorBgLayout};
|
|
2986
3301
|
-webkit-font-smoothing: antialiased;
|
|
2987
3302
|
-moz-osx-font-smoothing: grayscale;
|
|
3303
|
+
-webkit-overflow-scrolling: touch;
|
|
3304
|
+
-webkit-tap-highlight-color: transparent;
|
|
2988
3305
|
}
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
3306
|
+
|
|
3307
|
+
code {
|
|
3308
|
+
font-family: \${theme.fontFamilyCode} !important;
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
::selection {
|
|
3312
|
+
color: #000;
|
|
3313
|
+
-webkit-text-fill-color: unset !important;
|
|
3314
|
+
}
|
|
3315
|
+
\`);`;
|
|
3316
|
+
var CSS_VAR_SYNC_CODE = `
|
|
3317
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 CSS Variable Sync \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
3318
|
+
// \u5C06 antd-style \u6CE8\u5165\u7684 css-var-* class \u540C\u6B65\u5230 <html>\uFF0C
|
|
3319
|
+
// \u4F7F\u5168\u5C40 CSS / Tailwind \u4E5F\u80FD\u6D88\u8D39 antd CSS \u53D8\u91CF
|
|
3320
|
+
|
|
3321
|
+
const CSS_VAR_PREFIX = "css-var-";
|
|
3322
|
+
|
|
3323
|
+
function useCssVarSync(ref: React.RefObject<HTMLDivElement | null>) {
|
|
3324
|
+
useLayoutEffect(() => {
|
|
3325
|
+
const node = ref.current;
|
|
3326
|
+
if (!node) return;
|
|
3327
|
+
|
|
3328
|
+
const htmlEl = document.documentElement;
|
|
3329
|
+
let currentClasses: string[] = [];
|
|
3330
|
+
|
|
3331
|
+
const sync = () => {
|
|
3332
|
+
for (const cls of currentClasses) htmlEl.classList.remove(cls);
|
|
3333
|
+
|
|
3334
|
+
const next: string[] = [];
|
|
3335
|
+
let el: HTMLElement | null = node;
|
|
3336
|
+
while (el && el !== htmlEl) {
|
|
3337
|
+
for (const cls of el.classList) {
|
|
3338
|
+
if (cls.startsWith(CSS_VAR_PREFIX)) next.push(cls);
|
|
3339
|
+
}
|
|
3340
|
+
el = el.parentElement;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
for (const cls of next) htmlEl.classList.add(cls);
|
|
3344
|
+
currentClasses = next;
|
|
3345
|
+
};
|
|
3346
|
+
|
|
3347
|
+
sync();
|
|
3348
|
+
|
|
3349
|
+
const observer = new MutationObserver(sync);
|
|
3350
|
+
let el: HTMLElement | null = node;
|
|
3351
|
+
while (el && el !== htmlEl) {
|
|
3352
|
+
observer.observe(el, { attributeFilter: ["class"] });
|
|
3353
|
+
el = el.parentElement;
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
return () => {
|
|
3357
|
+
observer.disconnect();
|
|
3358
|
+
for (const cls of currentClasses) htmlEl.classList.remove(cls);
|
|
3359
|
+
};
|
|
3360
|
+
}, []);
|
|
3361
|
+
}`;
|
|
3362
|
+
var APPEARANCE_SYNC_CODE = `
|
|
3363
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Appearance Sync \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
3364
|
+
|
|
3365
|
+
function useAppearanceSync(appearance: ThemeAppearance) {
|
|
2992
3366
|
useEffect(() => {
|
|
2993
|
-
if (
|
|
2994
|
-
document.documentElement.dataset.theme =
|
|
3367
|
+
if (appearance !== "auto") {
|
|
3368
|
+
document.documentElement.dataset.theme = appearance;
|
|
2995
3369
|
return;
|
|
2996
3370
|
}
|
|
2997
3371
|
|
|
2998
|
-
const
|
|
2999
|
-
document.documentElement.dataset.theme =
|
|
3372
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
3373
|
+
document.documentElement.dataset.theme = mq.matches ? "dark" : "light";
|
|
3000
3374
|
|
|
3001
3375
|
function handleChange(e: MediaQueryListEvent) {
|
|
3002
3376
|
document.documentElement.dataset.theme = e.matches ? "dark" : "light";
|
|
3003
3377
|
}
|
|
3004
3378
|
|
|
3005
|
-
|
|
3006
|
-
return () =>
|
|
3379
|
+
mq.addEventListener("change", handleChange);
|
|
3380
|
+
return () => mq.removeEventListener("change", handleChange);
|
|
3381
|
+
}, [appearance]);
|
|
3382
|
+
}`;
|
|
3383
|
+
var EXTERNAL_THEME_CODE = `
|
|
3384
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 External Theme Injection \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
3385
|
+
// \u5FAE\u524D\u7AEF\u573A\u666F\uFF1A\u76D1\u542C\u4E3B\u5E94\u7528\u4E0B\u53D1\u7684\u4E3B\u9898\u53D8\u66F4
|
|
3386
|
+
|
|
3387
|
+
function useExternalTheme() {
|
|
3388
|
+
useEffect(() => {
|
|
3389
|
+
const hostTheme = (window as any).__YWKF_THEME__;
|
|
3390
|
+
if (hostTheme && typeof hostTheme === "object") {
|
|
3391
|
+
useThemeStore.getState().setTheme(hostTheme);
|
|
3392
|
+
}
|
|
3393
|
+
|
|
3394
|
+
const handler = (e: Event) => {
|
|
3395
|
+
const detail = (e as CustomEvent<Partial<ThemeState>>).detail;
|
|
3396
|
+
if (detail) useThemeStore.getState().setTheme(detail);
|
|
3397
|
+
};
|
|
3398
|
+
|
|
3399
|
+
window.addEventListener("ywkf:theme-change", handler);
|
|
3400
|
+
return () => window.removeEventListener("ywkf:theme-change", handler);
|
|
3007
3401
|
}, []);
|
|
3402
|
+
}`;
|
|
3403
|
+
function buildWrapperCode(opts) {
|
|
3404
|
+
const { darkMode, cssVar, globalReset, externalTheme, prefixCls } = opts;
|
|
3405
|
+
const refLine = cssVar ? "\n const containerRef = useRef<HTMLDivElement>(null);" : "";
|
|
3406
|
+
const cssVarSyncLine = cssVar ? "\n useCssVarSync(containerRef);" : "";
|
|
3407
|
+
const appearanceSyncLine = darkMode ? "\n useAppearanceSync(appearance);" : "";
|
|
3408
|
+
const externalThemeLine = externalTheme ? "\n useExternalTheme();" : "";
|
|
3409
|
+
const childrenSlot = cssVar ? `<div ref={containerRef} style={{ display: "contents" }}>
|
|
3410
|
+
{children}
|
|
3411
|
+
</div>` : "{children}";
|
|
3412
|
+
return `
|
|
3413
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 ThemeWrapper \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
3414
|
+
|
|
3415
|
+
interface ThemeWrapperProps {
|
|
3416
|
+
children: ReactNode;
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
/**
|
|
3420
|
+
* \u4E3B\u9898\u5305\u88F9\u7EC4\u4EF6
|
|
3421
|
+
*
|
|
3422
|
+
* \u7531 themePlugin \u81EA\u52A8\u6CE8\u5165\u5230\u5E94\u7528\u542F\u52A8\u5C42\uFF0C\u4ECE useThemeStore \u8BFB\u53D6\u4E3B\u9898\u72B6\u6001\u3002
|
|
3423
|
+
* \u4E1A\u52A1\u4FA7\u901A\u8FC7 useThemeStore / useTheme \u6D88\u8D39\u6216\u4FEE\u6539\u4E3B\u9898\u3002
|
|
3424
|
+
*/
|
|
3425
|
+
export function ThemeWrapper({ children }: ThemeWrapperProps) {${refLine}
|
|
3426
|
+
const { appearance, primaryColor } = useThemeStore(
|
|
3427
|
+
(s) => ({ appearance: s.appearance, primaryColor: s.primaryColor }),
|
|
3428
|
+
shallow,
|
|
3429
|
+
);${cssVarSyncLine}${appearanceSyncLine}${externalThemeLine}
|
|
3430
|
+
|
|
3431
|
+
const theme = useCallback<GetAntdTheme>(
|
|
3432
|
+
() => ({${cssVar ? "\n cssVar: true," : ""}
|
|
3433
|
+
token: {
|
|
3434
|
+
colorPrimary: primaryColor,
|
|
3435
|
+
// lobe-ui base tokens
|
|
3436
|
+
borderRadius: 8,
|
|
3437
|
+
borderRadiusLG: 12,
|
|
3438
|
+
borderRadiusSM: 6,
|
|
3439
|
+
borderRadiusXS: 4,
|
|
3440
|
+
controlHeight: 36,
|
|
3441
|
+
fontFamily: [
|
|
3442
|
+
'"HarmonyOS Sans", "Segoe UI", "SF Pro Display"',
|
|
3443
|
+
'-apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial',
|
|
3444
|
+
'"PingFang SC", "Hiragino Sans GB", "Microsoft Yahei", "Noto Sans SC", sans-serif',
|
|
3445
|
+
'"Segoe UI Emoji", "Noto Color Emoji"',
|
|
3446
|
+
].join(", "),
|
|
3447
|
+
fontFamilyCode: [
|
|
3448
|
+
'Hack, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas',
|
|
3449
|
+
'"PingFang SC", "Noto Sans SC", monospace',
|
|
3450
|
+
].join(", "),
|
|
3451
|
+
},
|
|
3452
|
+
}),
|
|
3453
|
+
[primaryColor],
|
|
3454
|
+
);
|
|
3455
|
+
|
|
3456
|
+
return (
|
|
3457
|
+
<AntdThemeProvider
|
|
3458
|
+
prefixCls="${prefixCls}"
|
|
3459
|
+
themeMode={appearance}
|
|
3460
|
+
theme={theme}
|
|
3461
|
+
>
|
|
3462
|
+
${globalReset ? "<GlobalReset />" : ""}
|
|
3463
|
+
${childrenSlot}
|
|
3464
|
+
</AntdThemeProvider>
|
|
3465
|
+
);
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3468
|
+
export default ThemeWrapper;
|
|
3008
3469
|
`;
|
|
3470
|
+
}
|
|
3009
3471
|
|
|
3010
3472
|
// src/plugin/builtin/mock.ts
|
|
3011
|
-
import { resolve as resolve2, join as
|
|
3473
|
+
import { resolve as resolve2, join as join10 } from "path";
|
|
3012
3474
|
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
3013
3475
|
var mockPlugin = createPlugin((options = {}) => ({
|
|
3014
3476
|
name: "@4399ywkf/plugin-mock",
|
|
@@ -3063,7 +3525,7 @@ function scanMockFiles(dir) {
|
|
|
3063
3525
|
}
|
|
3064
3526
|
const entries = readdirSync2(dir);
|
|
3065
3527
|
for (const entry of entries) {
|
|
3066
|
-
const fullPath =
|
|
3528
|
+
const fullPath = join10(dir, entry);
|
|
3067
3529
|
const stat = statSync2(fullPath);
|
|
3068
3530
|
if (stat.isFile() && /\.(ts|js|mjs)$/.test(entry)) {
|
|
3069
3531
|
files.push(fullPath);
|
|
@@ -3232,7 +3694,8 @@ function buildRequestFile(opts) {
|
|
|
3232
3694
|
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-react-query \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
|
|
3233
3695
|
// \u5982\u9700\u5B9A\u5236\u62E6\u622A\u5668\uFF0C\u8BF7\u7F16\u8F91 src/request.ts
|
|
3234
3696
|
import axios from "axios";
|
|
3235
|
-
import
|
|
3697
|
+
import qs from "qs";
|
|
3698
|
+
import type { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
|
|
3236
3699
|
import type { RequestConfig, Result } from "@4399ywkf/core/runtime";
|
|
3237
3700
|
|
|
3238
3701
|
// ============ \u52A0\u8F7D\u7528\u6237\u914D\u7F6E ============
|
|
@@ -3246,9 +3709,38 @@ try {
|
|
|
3246
3709
|
// src/request.ts \u4E0D\u5B58\u5728\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E
|
|
3247
3710
|
}
|
|
3248
3711
|
|
|
3712
|
+
// ============ \u53C2\u6570\u5E8F\u5217\u5316\uFF08GET \u8BF7\u6C42\u4E13\u7528\uFF09============
|
|
3713
|
+
|
|
3714
|
+
/**
|
|
3715
|
+
* GET \u8BF7\u6C42\u53C2\u6570\u5E8F\u5217\u5316\u89C4\u5219\uFF1A
|
|
3716
|
+
* - \u6570\u7EC4\uFF1Arepeat \u6A21\u5F0F\uFF08key=1&key=2\uFF09
|
|
3717
|
+
* - \u5BF9\u8C61\uFF1AJSON.stringify \u540E\u4F5C\u4E3A\u5B57\u7B26\u4E32\u4F20\u9012
|
|
3718
|
+
* - null / undefined\uFF1AskipNulls \u8DF3\u8FC7
|
|
3719
|
+
* - \u57FA\u672C\u7C7B\u578B\uFF1A\u539F\u6837\u4F20\u9012
|
|
3720
|
+
*/
|
|
3721
|
+
export function paramsSerializer(params: Record<string, any>): string {
|
|
3722
|
+
const normalized: Record<string, any> = {};
|
|
3723
|
+
|
|
3724
|
+
Object.keys(params).forEach((key) => {
|
|
3725
|
+
const value = params[key];
|
|
3726
|
+
|
|
3727
|
+
if (value === null || value === undefined) {
|
|
3728
|
+
normalized[key] = value;
|
|
3729
|
+
} else if (Array.isArray(value)) {
|
|
3730
|
+
normalized[key] = value;
|
|
3731
|
+
} else if (typeof value === "object") {
|
|
3732
|
+
normalized[key] = JSON.stringify(value);
|
|
3733
|
+
} else {
|
|
3734
|
+
normalized[key] = value;
|
|
3735
|
+
}
|
|
3736
|
+
});
|
|
3737
|
+
|
|
3738
|
+
return qs.stringify(normalized, { arrayFormat: "repeat", skipNulls: true });
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3249
3741
|
// ============ \u521B\u5EFA Axios \u5B9E\u4F8B ============
|
|
3250
3742
|
|
|
3251
|
-
|
|
3743
|
+
const instance: AxiosInstance = axios.create({
|
|
3252
3744
|
baseURL: userConfig.baseURL ?? ("${baseURL}" || ""),
|
|
3253
3745
|
timeout: userConfig.timeout ?? ${timeout},
|
|
3254
3746
|
withCredentials: userConfig.withCredentials ?? false,
|
|
@@ -3260,7 +3752,7 @@ export const request: AxiosInstance = axios.create({
|
|
|
3260
3752
|
|
|
3261
3753
|
// ============ \u8BF7\u6C42\u62E6\u622A\u5668 ============
|
|
3262
3754
|
|
|
3263
|
-
|
|
3755
|
+
instance.interceptors.request.use(
|
|
3264
3756
|
(config: InternalAxiosRequestConfig) => {
|
|
3265
3757
|
// 1. Token \u6CE8\u5165
|
|
3266
3758
|
if (userConfig.getToken) {
|
|
@@ -3291,7 +3783,7 @@ request.interceptors.request.use(
|
|
|
3291
3783
|
|
|
3292
3784
|
// ============ \u54CD\u5E94\u62E6\u622A\u5668 ============
|
|
3293
3785
|
|
|
3294
|
-
|
|
3786
|
+
instance.interceptors.response.use(
|
|
3295
3787
|
(response: AxiosResponse) => {
|
|
3296
3788
|
// \u7528\u6237\u81EA\u5B9A\u4E49\u54CD\u5E94\u62E6\u622A
|
|
3297
3789
|
if (userConfig.responseInterceptor) {
|
|
@@ -3330,8 +3822,45 @@ request.interceptors.response.use(
|
|
|
3330
3822
|
}
|
|
3331
3823
|
);
|
|
3332
3824
|
|
|
3825
|
+
// ============ \u8BF7\u6C42\u5C01\u88C5\u7C7B ============
|
|
3826
|
+
|
|
3827
|
+
type ExtraConfig = AxiosRequestConfig & { suppressErrorNotification?: boolean };
|
|
3828
|
+
|
|
3829
|
+
class Request {
|
|
3830
|
+
constructor(private readonly http: AxiosInstance) {}
|
|
3831
|
+
|
|
3832
|
+
get<T = any>(url: string, params?: Record<string, any>, config?: ExtraConfig): Promise<Result<T>> {
|
|
3833
|
+
return this.http.get(url, {
|
|
3834
|
+
...config,
|
|
3835
|
+
params,
|
|
3836
|
+
paramsSerializer,
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
post<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
|
|
3841
|
+
return this.http.post(url, data, config);
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3844
|
+
put<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
|
|
3845
|
+
return this.http.put(url, data, config);
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3848
|
+
delete<T = any>(url: string, params?: Record<string, any>, config?: ExtraConfig): Promise<Result<T>> {
|
|
3849
|
+
return this.http.delete(url, {
|
|
3850
|
+
...config,
|
|
3851
|
+
params,
|
|
3852
|
+
paramsSerializer,
|
|
3853
|
+
});
|
|
3854
|
+
}
|
|
3855
|
+
|
|
3856
|
+
patch<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
|
|
3857
|
+
return this.http.patch(url, data, config);
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
|
|
3333
3861
|
// ============ \u5BFC\u51FA ============
|
|
3334
3862
|
|
|
3863
|
+
export const request = new Request(instance);
|
|
3335
3864
|
export type { Result };
|
|
3336
3865
|
export default request;
|
|
3337
3866
|
`;
|
|
@@ -3339,7 +3868,7 @@ export default request;
|
|
|
3339
3868
|
|
|
3340
3869
|
// src/plugin/builtin/zustand.ts
|
|
3341
3870
|
import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
3342
|
-
import { join as
|
|
3871
|
+
import { join as join11 } from "path";
|
|
3343
3872
|
var zustandPlugin = createPlugin((options = {}) => ({
|
|
3344
3873
|
name: "@4399ywkf/plugin-zustand",
|
|
3345
3874
|
version: "1.0.0",
|
|
@@ -3347,7 +3876,7 @@ var zustandPlugin = createPlugin((options = {}) => ({
|
|
|
3347
3876
|
setup(context) {
|
|
3348
3877
|
const { scaffold = true, storeDir = "store" } = options;
|
|
3349
3878
|
const { cwd, logger } = context;
|
|
3350
|
-
const storePath =
|
|
3879
|
+
const storePath = join11(cwd, storeDir);
|
|
3351
3880
|
if (scaffold && !existsSync10(storePath)) {
|
|
3352
3881
|
scaffoldStore(storePath, logger);
|
|
3353
3882
|
}
|
|
@@ -3376,10 +3905,10 @@ var zustandPlugin = createPlugin((options = {}) => ({
|
|
|
3376
3905
|
function scaffoldStore(storePath, logger) {
|
|
3377
3906
|
const dirs = [
|
|
3378
3907
|
storePath,
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3908
|
+
join11(storePath, "middleware"),
|
|
3909
|
+
join11(storePath, "app"),
|
|
3910
|
+
join11(storePath, "app", "slices"),
|
|
3911
|
+
join11(storePath, "app", "slices", "counter")
|
|
3383
3912
|
];
|
|
3384
3913
|
for (const dir of dirs) {
|
|
3385
3914
|
if (!existsSync10(dir)) {
|
|
@@ -3387,36 +3916,36 @@ function scaffoldStore(storePath, logger) {
|
|
|
3387
3916
|
}
|
|
3388
3917
|
}
|
|
3389
3918
|
writeFileSync5(
|
|
3390
|
-
|
|
3919
|
+
join11(storePath, "middleware", "createDevtools.ts"),
|
|
3391
3920
|
TPL_CREATE_DEVTOOLS,
|
|
3392
3921
|
"utf-8"
|
|
3393
3922
|
);
|
|
3394
3923
|
writeFileSync5(
|
|
3395
|
-
|
|
3924
|
+
join11(storePath, "app", "slices", "counter", "initialState.ts"),
|
|
3396
3925
|
TPL_COUNTER_INITIAL_STATE,
|
|
3397
3926
|
"utf-8"
|
|
3398
3927
|
);
|
|
3399
3928
|
writeFileSync5(
|
|
3400
|
-
|
|
3929
|
+
join11(storePath, "app", "slices", "counter", "actions.ts"),
|
|
3401
3930
|
TPL_COUNTER_ACTIONS,
|
|
3402
3931
|
"utf-8"
|
|
3403
3932
|
);
|
|
3404
3933
|
writeFileSync5(
|
|
3405
|
-
|
|
3934
|
+
join11(storePath, "app", "initialState.ts"),
|
|
3406
3935
|
TPL_APP_INITIAL_STATE,
|
|
3407
3936
|
"utf-8"
|
|
3408
3937
|
);
|
|
3409
3938
|
writeFileSync5(
|
|
3410
|
-
|
|
3939
|
+
join11(storePath, "app", "store.ts"),
|
|
3411
3940
|
TPL_APP_STORE,
|
|
3412
3941
|
"utf-8"
|
|
3413
3942
|
);
|
|
3414
3943
|
writeFileSync5(
|
|
3415
|
-
|
|
3944
|
+
join11(storePath, "app", "index.tsx"),
|
|
3416
3945
|
TPL_APP_INDEX,
|
|
3417
3946
|
"utf-8"
|
|
3418
3947
|
);
|
|
3419
|
-
writeFileSync5(
|
|
3948
|
+
writeFileSync5(join11(storePath, "index.ts"), TPL_STORE_INDEX, "utf-8");
|
|
3420
3949
|
logger.info("\u5DF2\u751F\u6210 store/ \u811A\u624B\u67B6\uFF08Agent/Slice \u67B6\u6784\uFF09");
|
|
3421
3950
|
}
|
|
3422
3951
|
var TPL_CREATE_DEVTOOLS = `import type { DevtoolsOptions } from "zustand/middleware";
|
|
@@ -3673,7 +4202,7 @@ import chalk from "chalk";
|
|
|
3673
4202
|
import os from "os";
|
|
3674
4203
|
import { createRequire as createRequire3 } from "module";
|
|
3675
4204
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3676
|
-
import { dirname as dirname2, join as
|
|
4205
|
+
import { dirname as dirname2, join as join12 } from "path";
|
|
3677
4206
|
var __filename = fileURLToPath2(import.meta.url);
|
|
3678
4207
|
var __dirname2 = dirname2(__filename);
|
|
3679
4208
|
var require4 = createRequire3(import.meta.url);
|
|
@@ -3681,7 +4210,7 @@ var _version = null;
|
|
|
3681
4210
|
function getFrameworkVersion() {
|
|
3682
4211
|
if (_version) return _version;
|
|
3683
4212
|
try {
|
|
3684
|
-
const pkgPath =
|
|
4213
|
+
const pkgPath = join12(__dirname2, "../../package.json");
|
|
3685
4214
|
const pkg = require4(pkgPath);
|
|
3686
4215
|
_version = pkg.version || "0.0.0";
|
|
3687
4216
|
} catch {
|